mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-02-08 20:46:12 +01:00
first cut at persisting logged messages
Works, with support for writing them to file. BUT does not handle messages coming in from multiple threads at once so drops a lot of them.
This commit is contained in:
parent
36bf92e8e5
commit
cfa9b73833
10 changed files with 400 additions and 21 deletions
|
@ -51,6 +51,7 @@ public class DlgDelegate {
|
||||||
DWNLD_LOC_DICT,
|
DWNLD_LOC_DICT,
|
||||||
NEW_GAME_DFLT_NAME,
|
NEW_GAME_DFLT_NAME,
|
||||||
SEND_EMAIL,
|
SEND_EMAIL,
|
||||||
|
CLEAR_LOG_DB,
|
||||||
|
|
||||||
// BoardDelegate
|
// BoardDelegate
|
||||||
UNDO_LAST_ACTION,
|
UNDO_LAST_ACTION,
|
||||||
|
|
|
@ -62,6 +62,7 @@ import org.eehouse.android.xw4.jni.GameSummary;
|
||||||
import org.eehouse.android.xw4.jni.LastMoveInfo;
|
import org.eehouse.android.xw4.jni.LastMoveInfo;
|
||||||
import org.eehouse.android.xw4.loc.LocUtils;
|
import org.eehouse.android.xw4.loc.LocUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -574,6 +575,10 @@ public class GamesListDelegate extends ListDelegateBase
|
||||||
// R.id.games_menu_loaddb,
|
// R.id.games_menu_loaddb,
|
||||||
R.id.games_menu_storedb,
|
R.id.games_menu_storedb,
|
||||||
R.id.games_menu_writegit,
|
R.id.games_menu_writegit,
|
||||||
|
R.id.games_menu_enableLogStorage,
|
||||||
|
R.id.games_menu_disableLogStorage,
|
||||||
|
R.id.games_menu_clearLogStorage,
|
||||||
|
R.id.games_menu_dumpLogStorage,
|
||||||
};
|
};
|
||||||
private static final int[] NOSEL_ITEMS = {
|
private static final int[] NOSEL_ITEMS = {
|
||||||
R.id.games_menu_newgroup,
|
R.id.games_menu_newgroup,
|
||||||
|
@ -1380,6 +1385,11 @@ public class GamesListDelegate extends ListDelegateBase
|
||||||
Utils.emailAuthor( m_activity );
|
Utils.emailAuthor( m_activity );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CLEAR_LOG_DB:
|
||||||
|
int nDumped = Log.clearStored();
|
||||||
|
Utils.showToast( m_activity, R.string.logstore_cleared_fmt, nDumped );
|
||||||
|
break;
|
||||||
|
|
||||||
case ASKED_PHONE_STATE:
|
case ASKED_PHONE_STATE:
|
||||||
rematchWithNameAndPerm( true, params );
|
rematchWithNameAndPerm( true, params );
|
||||||
break;
|
break;
|
||||||
|
@ -1664,6 +1674,28 @@ public class GamesListDelegate extends ListDelegateBase
|
||||||
Utils.gitInfoToClip( m_activity );
|
Utils.gitInfoToClip( m_activity );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case R.id.games_menu_enableLogStorage:
|
||||||
|
Log.setStoreLogs( true );
|
||||||
|
break;
|
||||||
|
case R.id.games_menu_disableLogStorage:
|
||||||
|
Log.setStoreLogs( false );
|
||||||
|
break;
|
||||||
|
case R.id.games_menu_clearLogStorage:
|
||||||
|
makeConfirmThenBuilder( R.string.logstore_clear_confirm,
|
||||||
|
Action.CLEAR_LOG_DB )
|
||||||
|
.setPosButton( R.string.button_delete )
|
||||||
|
.show();
|
||||||
|
break;
|
||||||
|
case R.id.games_menu_dumpLogStorage:
|
||||||
|
File logLoc = Log.dumpStored();
|
||||||
|
if ( null != logLoc ) {
|
||||||
|
String dumpMsg = LocUtils
|
||||||
|
.getString( m_activity, R.string.logstore_dumped_fmt,
|
||||||
|
logLoc.getPath() );
|
||||||
|
makeOkOnlyBuilder( dumpMsg ).show();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
handled = handleSelGamesItem( itemID, selRowIDs )
|
handled = handleSelGamesItem( itemID, selRowIDs )
|
||||||
|| handleSelGroupsItem( itemID, getSelGroupIDs() );
|
|| handleSelGroupsItem( itemID, getSelGroupIDs() );
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* -*- compile-command: "find-and-gradle.sh inXw4dDeb"; -*- */
|
/* -*- compile-command: "find-and-gradle.sh inXw4dDeb"; -*- */
|
||||||
/*
|
/*
|
||||||
* Copyright 2009 - 2017 by Eric House (xwords@eehouse.org). All rights
|
* Copyright 2009 - 2020 by Eric House (xwords@eehouse.org). All rights
|
||||||
* reserved.
|
* reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -20,22 +20,87 @@
|
||||||
|
|
||||||
package org.eehouse.android.xw4;
|
package org.eehouse.android.xw4;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
import android.os.Environment;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Formatter;
|
import java.util.Formatter;
|
||||||
|
|
||||||
public class Log {
|
public class Log {
|
||||||
|
private static final String TAG = Log.class.getSimpleName();
|
||||||
private static final String PRE_TAG = BuildConfig.FLAVOR + "-";
|
private static final String PRE_TAG = BuildConfig.FLAVOR + "-";
|
||||||
|
private static final String KEY_USE_DB = TAG + "/useDB";
|
||||||
private static final boolean LOGGING_ENABLED
|
private static final boolean LOGGING_ENABLED
|
||||||
= BuildConfig.DEBUG || !BuildConfig.IS_TAGGED_BUILD;
|
= BuildConfig.DEBUG || !BuildConfig.IS_TAGGED_BUILD;
|
||||||
private static final boolean ERROR_LOGGING_ENABLED = true;
|
private static final boolean ERROR_LOGGING_ENABLED = true;
|
||||||
|
private static final String LOGS_DB_NAME = "logs_db";
|
||||||
|
private static final String LOGS_TABLE_NAME = "logs";
|
||||||
|
private static final String COL_ENTRY = "entry";
|
||||||
|
private static final String COL_ROWID = "rowid";
|
||||||
|
private static final String COL_TAG = "tag";
|
||||||
|
private static final String COL_LEVEL = "level";
|
||||||
|
|
||||||
|
private static final int DB_VERSION = 1;
|
||||||
private static boolean sEnabled = BuildConfig.DEBUG;
|
private static boolean sEnabled = BuildConfig.DEBUG;
|
||||||
|
private static boolean sUseDB;
|
||||||
|
private static WeakReference<Context> sContextRef;
|
||||||
|
|
||||||
|
private static enum LOG_LEVEL {
|
||||||
|
INFO,
|
||||||
|
ERROR,
|
||||||
|
WARN,
|
||||||
|
DEBUG,
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void init( Context context )
|
||||||
|
{
|
||||||
|
sContextRef = new WeakReference<>( context );
|
||||||
|
sUseDB = DBUtils.getBoolFor( context, KEY_USE_DB, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setStoreLogs( boolean enable )
|
||||||
|
{
|
||||||
|
Context context = sContextRef.get();
|
||||||
|
if ( null != context ) {
|
||||||
|
DBUtils.setBoolFor( context, KEY_USE_DB, enable );
|
||||||
|
}
|
||||||
|
sUseDB = enable;
|
||||||
|
}
|
||||||
|
|
||||||
public static void enable( boolean newVal )
|
public static void enable( boolean newVal )
|
||||||
{
|
{
|
||||||
sEnabled = newVal;
|
sEnabled = newVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int clearStored()
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
LogDBHelper helper = initDB();
|
||||||
|
if ( null != helper ) {
|
||||||
|
result = helper.clear();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File dumpStored()
|
||||||
|
{
|
||||||
|
File result = null;
|
||||||
|
LogDBHelper helper = initDB();
|
||||||
|
if ( null != helper ) {
|
||||||
|
result = helper.dumpToFile();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public static void enable( Context context )
|
public static void enable( Context context )
|
||||||
{
|
{
|
||||||
boolean on = LOGGING_ENABLED ||
|
boolean on = LOGGING_ENABLED ||
|
||||||
|
@ -47,35 +112,54 @@ public class Log {
|
||||||
public static void d( String tag, String fmt, Object... args )
|
public static void d( String tag, String fmt, Object... args )
|
||||||
{
|
{
|
||||||
if ( sEnabled ) {
|
if ( sEnabled ) {
|
||||||
String str = new Formatter().format( fmt, args ).toString();
|
dolog( LOG_LEVEL.DEBUG, tag, fmt, args );
|
||||||
android.util.Log.d( PRE_TAG + tag, str );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void w( String tag, String fmt, Object... args )
|
public static void w( String tag, String fmt, Object... args )
|
||||||
{
|
{
|
||||||
if ( sEnabled ) {
|
if ( sEnabled ) {
|
||||||
String str = new Formatter().format( fmt, args ).toString();
|
dolog( LOG_LEVEL.WARN, tag, fmt, args );
|
||||||
android.util.Log.w( PRE_TAG + tag, str );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void e( String tag, String fmt, Object... args )
|
public static void e( String tag, String fmt, Object... args )
|
||||||
{
|
{
|
||||||
if ( ERROR_LOGGING_ENABLED ) {
|
if ( ERROR_LOGGING_ENABLED ) {
|
||||||
String str = new Formatter().format( fmt, args ).toString();
|
dolog( LOG_LEVEL.ERROR, tag, fmt, args );
|
||||||
android.util.Log.e( PRE_TAG + tag, str );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void i( String tag, String fmt, Object... args )
|
public static void i( String tag, String fmt, Object... args )
|
||||||
{
|
{
|
||||||
if ( sEnabled ) {
|
if ( sEnabled ) {
|
||||||
String str = new Formatter().format( fmt, args ).toString();
|
dolog( LOG_LEVEL.INFO, tag, fmt, args );
|
||||||
android.util.Log.i( PRE_TAG + tag, str );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void dolog( LOG_LEVEL level, String tag, String fmt, Object[] args )
|
||||||
|
{
|
||||||
|
String str = new Formatter().format( fmt, args ).toString();
|
||||||
|
String fullTag = PRE_TAG + tag;
|
||||||
|
switch ( level ) {
|
||||||
|
case DEBUG:
|
||||||
|
android.util.Log.d( fullTag, str );
|
||||||
|
break;
|
||||||
|
case ERROR:
|
||||||
|
android.util.Log.e( fullTag, str );
|
||||||
|
break;
|
||||||
|
case WARN:
|
||||||
|
android.util.Log.w( fullTag, str );
|
||||||
|
break;
|
||||||
|
case INFO:
|
||||||
|
android.util.Log.e( fullTag, str );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Assert.failDbg();
|
||||||
|
}
|
||||||
|
store( level, fullTag, str );
|
||||||
|
}
|
||||||
|
|
||||||
public static void ex( String tag, Exception exception )
|
public static void ex( String tag, Exception exception )
|
||||||
{
|
{
|
||||||
if ( sEnabled ) {
|
if ( sEnabled ) {
|
||||||
|
@ -83,4 +167,123 @@ public class Log {
|
||||||
DbgUtils.printStack( tag, exception.getStackTrace() );
|
DbgUtils.printStack( tag, exception.getStackTrace() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void llog( String fmt, Object... args )
|
||||||
|
{
|
||||||
|
String str = new Formatter().format( fmt, args ).toString();
|
||||||
|
android.util.Log.d( TAG, str );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LogDBHelper s_dbHelper;
|
||||||
|
private synchronized static LogDBHelper initDB()
|
||||||
|
{
|
||||||
|
if ( null == s_dbHelper ) {
|
||||||
|
Context context = sContextRef.get();
|
||||||
|
if ( null != context ) {
|
||||||
|
s_dbHelper = new LogDBHelper( context );
|
||||||
|
// force any upgrade
|
||||||
|
s_dbHelper.getWritableDatabase().close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s_dbHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called from jni
|
||||||
|
public static void store( String tag, String msg )
|
||||||
|
{
|
||||||
|
llog( "store(%s) called from jni", msg );
|
||||||
|
store( LOG_LEVEL.DEBUG, tag, msg );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void store( LOG_LEVEL level, String tag, String msg )
|
||||||
|
{
|
||||||
|
if ( sUseDB ) {
|
||||||
|
LogDBHelper helper = initDB();
|
||||||
|
if ( null != helper ) {
|
||||||
|
helper.store( level, tag, msg );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LogDBHelper extends SQLiteOpenHelper {
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
LogDBHelper( Context context )
|
||||||
|
{
|
||||||
|
super( context, LOGS_DB_NAME, null, DB_VERSION );
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate( SQLiteDatabase db )
|
||||||
|
{
|
||||||
|
String query = "CREATE TABLE " + LOGS_TABLE_NAME + "("
|
||||||
|
+ COL_ROWID + " INTEGER PRIMARY KEY AUTOINCREMENT"
|
||||||
|
+ "," + COL_ENTRY + " TEXT"
|
||||||
|
+ "," + COL_TAG + " TEXT"
|
||||||
|
+ "," + COL_LEVEL + " INTEGER(2)"
|
||||||
|
+ ");";
|
||||||
|
|
||||||
|
db.execSQL( query );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("fallthrough")
|
||||||
|
public void onUpgrade( SQLiteDatabase db, int oldVersion, int newVersion )
|
||||||
|
{
|
||||||
|
String msg = String.format("onUpgrade(%s): old: %d; new: %d", db, oldVersion, newVersion );
|
||||||
|
android.util.Log.i( TAG, msg );
|
||||||
|
Assert.failDbg();
|
||||||
|
}
|
||||||
|
|
||||||
|
void store( LOG_LEVEL level, String tag, String msg )
|
||||||
|
{
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put( COL_ENTRY, msg );
|
||||||
|
values.put( COL_TAG, tag );
|
||||||
|
values.put( COL_LEVEL, level.ordinal() );
|
||||||
|
long res = getWritableDatabase().insert( LOGS_TABLE_NAME, null, values );
|
||||||
|
}
|
||||||
|
|
||||||
|
File dumpToFile()
|
||||||
|
{
|
||||||
|
File storage = Environment.getExternalStorageDirectory();
|
||||||
|
File db = new File( storage, LOGS_DB_NAME );
|
||||||
|
|
||||||
|
try {
|
||||||
|
OutputStream os = new FileOutputStream( db );
|
||||||
|
OutputStreamWriter osw = new OutputStreamWriter(os);
|
||||||
|
|
||||||
|
String[] columns = { COL_ENTRY, COL_TAG };
|
||||||
|
String selection = null;
|
||||||
|
String orderBy = COL_ROWID;
|
||||||
|
Cursor cursor = getReadableDatabase().query( LOGS_TABLE_NAME, columns,
|
||||||
|
selection, null, null, null,
|
||||||
|
orderBy );
|
||||||
|
llog( "dumpToFile(): got %d results", cursor.getCount() );
|
||||||
|
int indx0 = cursor.getColumnIndex( columns[0] );
|
||||||
|
int indx1 = cursor.getColumnIndex( columns[1] );
|
||||||
|
while ( cursor.moveToNext() ) {
|
||||||
|
String data = cursor.getString(indx0);
|
||||||
|
String tag = cursor.getString(indx1);
|
||||||
|
osw.write( tag );
|
||||||
|
osw.write( ":" );
|
||||||
|
osw.write(data);
|
||||||
|
osw.write( "\n" );
|
||||||
|
}
|
||||||
|
osw.close();
|
||||||
|
} catch ( IOException ioe ) {
|
||||||
|
llog( "dumpToFile(): ioe: %s", ioe );
|
||||||
|
}
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the number of rows
|
||||||
|
int clear()
|
||||||
|
{
|
||||||
|
int result = getWritableDatabase()
|
||||||
|
.delete( LOGS_TABLE_NAME, "1", null );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
|
import java.util.Formatter;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -174,9 +175,10 @@ public class Utils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void showToast( Context context, int id )
|
public static void showToast( Context context, int id, Object... args )
|
||||||
{
|
{
|
||||||
String msg = LocUtils.getString( context, id );
|
String msg = LocUtils.getString( context, id );
|
||||||
|
msg = new Formatter().format( msg, args ).toString();
|
||||||
showToast( context, msg );
|
showToast( context, msg );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,8 @@ public class XWApp extends Application
|
||||||
Assert.assertTrue( s_context == s_context.getApplicationContext() );
|
Assert.assertTrue( s_context == s_context.getApplicationContext() );
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
|
Log.init( this );
|
||||||
|
|
||||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
||||||
|
|
||||||
android.util.Log.i( TAG, "onCreate(); git_rev="
|
android.util.Log.i( TAG, "onCreate(); git_rev="
|
||||||
|
|
|
@ -122,4 +122,16 @@
|
||||||
android:title="@string/gamel_menu_writegit"
|
android:title="@string/gamel_menu_writegit"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<item android:id="@+id/games_menu_enableLogStorage"
|
||||||
|
android:title="@string/gamel_menu_logstore_on"
|
||||||
|
/>
|
||||||
|
<item android:id="@+id/games_menu_disableLogStorage"
|
||||||
|
android:title="@string/gamel_menu_logstore_off"
|
||||||
|
/>
|
||||||
|
<item android:id="@+id/games_menu_dumpLogStorage"
|
||||||
|
android:title="@string/gamel_menu_logstore_dump"
|
||||||
|
/>
|
||||||
|
<item android:id="@+id/games_menu_clearLogStorage"
|
||||||
|
android:title="@string/gamel_menu_logstore_clear"
|
||||||
|
/>
|
||||||
</menu>
|
</menu>
|
||||||
|
|
|
@ -2519,4 +2519,20 @@
|
||||||
<string name="history_pause_fmt">Paused by: %1$s.</string>
|
<string name="history_pause_fmt">Paused by: %1$s.</string>
|
||||||
<string name="history_msg_fmt"> Message: %1$s.</string>
|
<string name="history_msg_fmt"> Message: %1$s.</string>
|
||||||
<string name="history_autopause">Auto-paused.</string>
|
<string name="history_autopause">Auto-paused.</string>
|
||||||
|
|
||||||
|
<!-- Debug-build-only menu to turn on saving of logs -->
|
||||||
|
<string name="gamel_menu_logstore_on">Enable log storage</string>
|
||||||
|
<!-- Debug-build-only menu to turn off saving of logs -->
|
||||||
|
<string name="gamel_menu_logstore_off">Diable log storage</string>
|
||||||
|
<!-- Debug-build-only menu to clear/delete saved logs -->
|
||||||
|
<string name="gamel_menu_logstore_clear">Clear stored logs</string>
|
||||||
|
<!-- Debug-build-only menu to write saved logs to a world-readable file -->
|
||||||
|
<string name="gamel_menu_logstore_dump">Write stored logs to file</string>
|
||||||
|
<!-- Debug-build-only status message shown after logs successfully written to file -->
|
||||||
|
<string name="logstore_dumped_fmt">Logs written to file %1$s</string>
|
||||||
|
<!-- Debug-build-only status message shown after logs successfully cleared -->
|
||||||
|
<string name="logstore_cleared_fmt">%1$d log entries deleted</string>
|
||||||
|
<!-- Debug-build-only question asked to confirm deletion of saved logs -->
|
||||||
|
<string name="logstore_clear_confirm">Are you sure you want to
|
||||||
|
erase your debug logs? This action cannot be undone.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
void
|
void
|
||||||
and_assert( const char* test, int line, const char* file, const char* func )
|
and_assert( const char* test, int line, const char* file, const char* func )
|
||||||
{
|
{
|
||||||
|
RAW_LOG( "assertion \"%s\" failed: line %d in %s() in %s",
|
||||||
|
test, line, func, file );
|
||||||
XP_LOGF( "assertion \"%s\" failed: line %d in %s() in %s",
|
XP_LOGF( "assertion \"%s\" failed: line %d in %s() in %s",
|
||||||
test, line, func, file );
|
test, line, func, file );
|
||||||
__android_log_assert( test, "ASSERT", "line %d in %s() in %s",
|
__android_log_assert( test, "ASSERT", "line %d in %s() in %s",
|
||||||
|
@ -766,6 +768,46 @@ deleteLocalRefs( JNIEnv* env, ... )
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
static int g_log_count = 0;
|
||||||
|
static pthread_mutex_t g_log_count_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
/* A bunch of threads are generating log statements. */
|
||||||
|
static void
|
||||||
|
passToJava( const char* tag, const char* msg )
|
||||||
|
{
|
||||||
|
if ( 1 ) {
|
||||||
|
pthread_mutex_lock( &g_log_count_lock );
|
||||||
|
int lockCount = ++g_log_count;
|
||||||
|
RAW_LOG( "lockCount: %d", lockCount );
|
||||||
|
pthread_mutex_unlock( &g_log_count_lock );
|
||||||
|
|
||||||
|
/* Now pass into Log.java for possible writing to DB */
|
||||||
|
if ( 1 == lockCount ) {
|
||||||
|
JNIEnv* env = waitEnvFromGlobals();
|
||||||
|
if ( !!env ) {
|
||||||
|
jstring jtag = (*env)->NewStringUTF( env, tag );
|
||||||
|
jstring jbuf = (*env)->NewStringUTF( env, msg );
|
||||||
|
jclass clazz = (*env)->FindClass( env, PKG_PATH("Log") );
|
||||||
|
XP_ASSERT( !!clazz );
|
||||||
|
jmethodID mid = (*env)->GetStaticMethodID( env, clazz, "store",
|
||||||
|
"(Ljava/lang/String;Ljava/lang/String;)V" );
|
||||||
|
(*env)->CallStaticVoidMethod( env, clazz, mid, jtag, jbuf );
|
||||||
|
deleteLocalRefs( env, clazz, jtag, jbuf, DELETE_NO_REF );
|
||||||
|
|
||||||
|
releaseEnvFromGlobals( env );
|
||||||
|
} else {
|
||||||
|
RAW_LOG( "env is NULL; dropping" );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RAW_LOG( "recursing! Skipping msg %s", msg );
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock( &g_log_count_lock );
|
||||||
|
--g_log_count;
|
||||||
|
pthread_mutex_unlock( &g_log_count_lock );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
debugf( const char* format, va_list ap )
|
debugf( const char* format, va_list ap )
|
||||||
{
|
{
|
||||||
|
@ -784,7 +826,7 @@ debugf( const char* format, va_list ap )
|
||||||
vsnprintf( buf + len, sizeof(buf)-len, format, ap );
|
vsnprintf( buf + len, sizeof(buf)-len, format, ap );
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)__android_log_write( ANDROID_LOG_DEBUG,
|
const char* tag =
|
||||||
# if defined VARIANT_xw4NoSMS || defined VARIANT_xw4fdroid || defined VARIANT_xw4SMS
|
# if defined VARIANT_xw4NoSMS || defined VARIANT_xw4fdroid || defined VARIANT_xw4SMS
|
||||||
"xw4"
|
"xw4"
|
||||||
# elif defined VARIANT_xw4d || defined VARIANT_xw4dNoSMS
|
# elif defined VARIANT_xw4d || defined VARIANT_xw4dNoSMS
|
||||||
|
@ -792,7 +834,27 @@ debugf( const char* format, va_list ap )
|
||||||
# elif defined VARIANT_xw4dup || defined VARIANT_xw4dupNoSMS
|
# elif defined VARIANT_xw4dup || defined VARIANT_xw4dupNoSMS
|
||||||
"x4du"
|
"x4du"
|
||||||
# endif
|
# endif
|
||||||
, buf );
|
;
|
||||||
|
|
||||||
|
(void)__android_log_write( ANDROID_LOG_DEBUG, tag, buf );
|
||||||
|
|
||||||
|
passToJava( tag, buf );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
raw_log( const char* func, const char* fmt, ... )
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
int len = snprintf( buf, VSIZE(buf) - 1, "in %s(): %s", func, fmt );
|
||||||
|
buf[len] = '\0';
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start( ap, fmt );
|
||||||
|
char buf2[1024];
|
||||||
|
len = vsnprintf( buf2, VSIZE(buf2) - 1, buf, ap );
|
||||||
|
va_end( ap );
|
||||||
|
|
||||||
|
(void)__android_log_write( ANDROID_LOG_DEBUG, "raw", buf2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -108,5 +108,12 @@ XP_U32 getCurSeconds( JNIEnv* env );
|
||||||
|
|
||||||
void deleteLocalRef( JNIEnv* env, jobject jobj );
|
void deleteLocalRef( JNIEnv* env, jobject jobj );
|
||||||
void deleteLocalRefs( JNIEnv* env, ... );
|
void deleteLocalRefs( JNIEnv* env, ... );
|
||||||
|
|
||||||
|
JNIEnv* waitEnvFromGlobals();
|
||||||
|
void releaseEnvFromGlobals( JNIEnv* env );
|
||||||
|
|
||||||
|
void raw_log( const char* func, const char* fmt, ... );
|
||||||
|
#define RAW_LOG(...) raw_log( __func__, __VA_ARGS__ )
|
||||||
|
|
||||||
# define DELETE_NO_REF ((jobject)-1) /* terminates above varargs list */
|
# define DELETE_NO_REF ((jobject)-1) /* terminates above varargs list */
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -118,7 +118,7 @@ typedef long GamePtrType;
|
||||||
#ifdef LOG_MAPPING
|
#ifdef LOG_MAPPING
|
||||||
# ifdef DEBUG
|
# ifdef DEBUG
|
||||||
static int
|
static int
|
||||||
countUsed(const EnvThreadInfo* ti)
|
countUsed( const EnvThreadInfo* ti )
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for ( int ii = 0; ii < ti->nEntries; ++ii ) {
|
for ( int ii = 0; ii < ti->nEntries; ++ii ) {
|
||||||
|
@ -157,8 +157,8 @@ map_thread_prv( EnvThreadInfo* ti, JNIEnv* env, const char* caller )
|
||||||
found = true;
|
found = true;
|
||||||
if ( env != entry->env ) {
|
if ( env != entry->env ) {
|
||||||
/* this DOES happen!!! */
|
/* this DOES happen!!! */
|
||||||
XP_LOGF( "%s (ti=%p): replacing env %p with env %p for thread %x",
|
RAW_LOG( "(ti=%p): replacing env %p with env %p for thread %x",
|
||||||
__func__, ti, entry->env, env, (int)self );
|
ti, entry->env, env, (int)self );
|
||||||
entry->env = env;
|
entry->env = env;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,7 @@ map_thread_prv( EnvThreadInfo* ti, JNIEnv* env, const char* caller )
|
||||||
ti->entries = entries;
|
ti->entries = entries;
|
||||||
ti->nEntries = nEntries;
|
ti->nEntries = nEntries;
|
||||||
#ifdef LOG_MAPPING
|
#ifdef LOG_MAPPING
|
||||||
XP_LOGF( "%s: num env entries now %d", __func__, nEntries );
|
RAW_LOG( "num env entries now %d", nEntries );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,9 +191,9 @@ map_thread_prv( EnvThreadInfo* ti, JNIEnv* env, const char* caller )
|
||||||
++firstEmpty->refcount;
|
++firstEmpty->refcount;
|
||||||
#ifdef LOG_MAPPING
|
#ifdef LOG_MAPPING
|
||||||
firstEmpty->ownerFunc = caller;
|
firstEmpty->ownerFunc = caller;
|
||||||
XP_LOGF( "%s: entry %zu: mapped env %p to thread %x", __func__,
|
RAW_LOG( "entry %zu: mapped env %p to thread %x",
|
||||||
firstEmpty - ti->entries, env, (int)self );
|
firstEmpty - ti->entries, env, (int)self );
|
||||||
XP_LOGF( "%s: num entries USED now %d", __func__, countUsed(ti) );
|
RAW_LOG( "num entries USED now %d", countUsed(ti) );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,9 +221,9 @@ map_remove_prv( EnvThreadInfo* ti, JNIEnv* env, const char* func )
|
||||||
if ( found ) {
|
if ( found ) {
|
||||||
XP_ASSERT( pthread_self() == entry->owner );
|
XP_ASSERT( pthread_self() == entry->owner );
|
||||||
#ifdef LOG_MAPPING
|
#ifdef LOG_MAPPING
|
||||||
XP_LOGF( "%s: UNMAPPED env %p to thread %x (from %s; mapped by %s)", __func__,
|
RAW_LOG( "UNMAPPED env %p to thread %x (from %s; mapped by %s)",
|
||||||
entry->env, (int)entry->owner, func, entry->ownerFunc );
|
entry->env, (int)entry->owner, func, entry->ownerFunc );
|
||||||
XP_LOGF( "%s: %d entries left", __func__, countUsed( ti ) );
|
RAW_LOG( "%d entries left", countUsed( ti ) );
|
||||||
entry->ownerFunc = NULL;
|
entry->ownerFunc = NULL;
|
||||||
#endif
|
#endif
|
||||||
XP_ASSERT( 1 == entry->refcount );
|
XP_ASSERT( 1 == entry->refcount );
|
||||||
|
@ -258,6 +258,46 @@ prvEnvForMe( EnvThreadInfo* ti )
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
static pthread_mutex_t g_globalStateLock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static JNIGlobalState* g_globalState = NULL;
|
||||||
|
|
||||||
|
void setGlobalState( JNIGlobalState* state )
|
||||||
|
{
|
||||||
|
pthread_mutex_lock( &g_globalStateLock );
|
||||||
|
g_globalState = state;
|
||||||
|
pthread_mutex_unlock( &g_globalStateLock );
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEnv*
|
||||||
|
waitEnvFromGlobals() /* hanging */
|
||||||
|
{
|
||||||
|
JNIEnv* result = NULL;
|
||||||
|
pthread_mutex_lock( &g_globalStateLock );
|
||||||
|
JNIGlobalState* state = g_globalState;
|
||||||
|
if ( !!state ) {
|
||||||
|
result = prvEnvForMe( &state->ti );
|
||||||
|
}
|
||||||
|
if ( !result ) {
|
||||||
|
pthread_mutex_unlock( &g_globalStateLock );
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
releaseEnvFromGlobals( JNIEnv* env )
|
||||||
|
{
|
||||||
|
XP_ASSERT( !!env );
|
||||||
|
JNIGlobalState* state = g_globalState;
|
||||||
|
XP_ASSERT( !!state );
|
||||||
|
XP_ASSERT( env == prvEnvForMe( &state->ti ) );
|
||||||
|
pthread_mutex_unlock( &g_globalStateLock );
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
# define setGlobalState(s)
|
||||||
|
#endif
|
||||||
|
|
||||||
JNIEnv*
|
JNIEnv*
|
||||||
envForMe( EnvThreadInfo* ti, const char* caller )
|
envForMe( EnvThreadInfo* ti, const char* caller )
|
||||||
{
|
{
|
||||||
|
@ -325,6 +365,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_initGlobals
|
||||||
globalState->dictMgr = dmgr_make( MPPARM_NOCOMMA( mpool ) );
|
globalState->dictMgr = dmgr_make( MPPARM_NOCOMMA( mpool ) );
|
||||||
globalState->smsProto = smsproto_init( MPPARM( mpool ) globalState->dutil );
|
globalState->smsProto = smsproto_init( MPPARM( mpool ) globalState->dutil );
|
||||||
MPASSIGN( globalState->mpool, mpool );
|
MPASSIGN( globalState->mpool, mpool );
|
||||||
|
setGlobalState( globalState );
|
||||||
// LOG_RETURNF( "%p", globalState );
|
// LOG_RETURNF( "%p", globalState );
|
||||||
return (jlong)globalState;
|
return (jlong)globalState;
|
||||||
}
|
}
|
||||||
|
@ -335,6 +376,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_cleanGlobals
|
||||||
{
|
{
|
||||||
// LOG_FUNC();
|
// LOG_FUNC();
|
||||||
if ( 0 != jniGlobalPtr ) {
|
if ( 0 != jniGlobalPtr ) {
|
||||||
|
setGlobalState( NULL );
|
||||||
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr;
|
||||||
#ifdef MEM_DEBUG
|
#ifdef MEM_DEBUG
|
||||||
MemPoolCtx* mpool = GETMPOOL( globalState );
|
MemPoolCtx* mpool = GETMPOOL( globalState );
|
||||||
|
|
Loading…
Add table
Reference in a new issue