From 28edeb18774da5a0c1f639531e909fcc488259a1 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sat, 24 Aug 2024 09:41:22 -0700 Subject: [PATCH] add creation time to stats Useful, especially when it's getting reset mysteriously -- which was probably the Android timers bug --- .../org/eehouse/android/xw4/DlgDelegate.kt | 1 + .../eehouse/android/xw4/GamesListDelegate.kt | 19 +++++-- .../app/src/main/res/values/tmpstrings.xml | 2 + xwords4/common/stats.c | 55 ++++++++++++++----- 4 files changed, 58 insertions(+), 19 deletions(-) diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegate.kt b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegate.kt index f8e2fd20a..00fabe166 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegate.kt +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegate.kt @@ -65,6 +65,7 @@ class DlgDelegate( CLEAR_INT_STATS, // debug only RESTART, NOTIFY_PERMS, + CLEAR_STATS, // BoardDelegate UNDO_LAST_ACTION, diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.kt b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.kt index 1d59ecbb9..4080e1bd6 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.kt +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.kt @@ -49,6 +49,7 @@ import com.jakewharton.processphoenix.ProcessPhoenix import java.io.File import java.io.Serializable +import java.util.Date import org.eehouse.android.xw4.DBUtils.DBChangeListener import org.eehouse.android.xw4.DBUtils.GameChangeType @@ -1353,8 +1354,8 @@ class GamesListDelegate(delegator: Delegator) : DictBrowseDelegate.launch(getDelegator(), (params[0] as String)) Action.LAUNCH_AFTER_DEL -> deleteGames( longArrayOf((params[1] as Long)), - false - ) + false ) + Action.CLEAR_STATS -> XwJNI.sts_clearAll() else -> handled = super.onPosButton(action, *params) } @@ -1765,16 +1766,22 @@ class GamesListDelegate(delegator: Delegator) : Action.WRITE_LOG_DB) R.id.games_menu_statsShow -> { - val stats = XwJNI.sts_export() + val obj = XwJNI.sts_export() + val stats = obj.getJSONObject("stats") + val startTime = Date(obj.getLong("since")*1000) val pairs = ArrayList() stats.keys().forEach { key -> val value = stats.getLong(key) pairs.add("$key: $value") } - makeOkOnlyBuilder(TextUtils.join("\n", pairs)) - .show() + val txt = "Since: $startTime\n" + + TextUtils.join("\n", pairs) + makeOkOnlyBuilder(txt).show() } - R.id.games_menu_statsClear -> XwJNI.sts_clearAll() + R.id.games_menu_statsClear -> + makeConfirmThenBuilder(DlgDelegate.Action.CLEAR_STATS, + R.string.statsClearConfirm) + .show() else -> handled = (handleSelGamesItem(itemID, selRowIDs) || handleSelGroupsItem(itemID, selGroupIDs)) diff --git a/xwords4/android/app/src/main/res/values/tmpstrings.xml b/xwords4/android/app/src/main/res/values/tmpstrings.xml index f9d5ab0f2..fd7c8ce6a 100644 --- a/xwords4/android/app/src/main/res/values/tmpstrings.xml +++ b/xwords4/android/app/src/main/res/values/tmpstrings.xml @@ -45,4 +45,6 @@ Show Clear + Are you sure you want to permanently delete stats? + diff --git a/xwords4/common/stats.c b/xwords4/common/stats.c index 577742d66..b1c04e811 100644 --- a/xwords4/common/stats.c +++ b/xwords4/common/stats.c @@ -28,12 +28,17 @@ typedef struct StatsState { XP_U32* statsVals; + XP_U32 startTime; MutexState mutex; XP_Bool timerSet; } StatsState; +enum { VERSION_0, + VERSION_1, /* adds timestamp at start */ +}; + static const XP_UCHAR* STATtoStr(STAT stat); -static XP_U32* loadCounts( XW_DUtilCtxt* dutil, XWEnv xwe ); +static void loadCountsLocked( XW_DUtilCtxt* dutil, XWEnv xwe ); static void storeCountsLocked( XW_DUtilCtxt* dutil, XWEnv xwe ); static void setStoreTimerLocked( XW_DUtilCtxt* dutil, XWEnv xwe ); @@ -63,7 +68,7 @@ sts_increment( XW_DUtilCtxt* dutil, XWEnv xwe, STAT stat ) XP_ASSERT( !!ss ); WITH_MUTEX( &ss->mutex ); if ( !ss->statsVals ) { - ss->statsVals = loadCounts( dutil, xwe ); + loadCountsLocked( dutil, xwe ); } ++ss->statsVals[stat]; @@ -78,18 +83,24 @@ sts_export( XW_DUtilCtxt* dutil, XWEnv xwe ) StatsState* ss = dutil->statsState; XP_ASSERT( !!ss ); cJSON* result = cJSON_CreateObject(); + cJSON* stats = cJSON_CreateObject(); + cJSON_AddItemToObject(result, "stats", stats ); WITH_MUTEX( &ss->mutex ); if ( !ss->statsVals ) { - ss->statsVals = loadCounts( dutil, xwe ); + loadCountsLocked( dutil, xwe ); } + for ( int ii = 0; ii < STAT_NSTATS; ++ii ) { XP_U32 val = ss->statsVals[ii]; if ( 0 != val ) { const XP_UCHAR* nam = STATtoStr(ii); - cJSON_AddNumberToObject( result, nam, val ); + cJSON_AddNumberToObject( stats, nam, val ); } } + + cJSON_AddNumberToObject( result, "since", ss->startTime ); + END_WITH_MUTEX(); return result; } @@ -100,11 +111,17 @@ sts_clearAll( XW_DUtilCtxt* dutil, XWEnv xwe ) StatsState* ss = dutil->statsState; XP_ASSERT( !!ss ); + /* grab outside the mutex */ + XP_U32 startTime = dutil_getCurSeconds( dutil, xwe ); + XP_U32* statsVals = XP_CALLOC( dutil->mpool, + sizeof(*ss->statsVals) * STAT_NSTATS ); + + WITH_MUTEX( &ss->mutex ); XP_FREEP( dutil->mpool, &ss->statsVals ); - ss->statsVals - = XP_CALLOC( dutil->mpool, sizeof(*ss->statsVals) * STAT_NSTATS ); + ss->statsVals = statsVals; + ss->startTime = startTime; storeCountsLocked( dutil, xwe ); END_WITH_MUTEX(); } @@ -140,7 +157,8 @@ storeCountsLocked( XW_DUtilCtxt* dutil, XWEnv xwe ) XP_ASSERT( !!ss ); XWStreamCtxt* stream = mkStream( dutil ); - stream_putU8( stream, 0 ); /* version */ + stream_putU8( stream, VERSION_1 ); + stream_putU32( stream, ss->startTime ); if ( !!ss->statsVals ) { for ( int ii = 0; ii < STAT_NSTATS; ++ii ) { @@ -157,18 +175,30 @@ storeCountsLocked( XW_DUtilCtxt* dutil, XWEnv xwe ) stream_destroy( stream ); } -static XP_U32* -loadCounts( XW_DUtilCtxt* dutil, XWEnv xwe ) +static void +loadCountsLocked( XW_DUtilCtxt* dutil, XWEnv xwe ) { + StatsState* ss = dutil->statsState; + XP_ASSERT( !!ss ); + XP_U32* statsVals + = XP_CALLOC( dutil->mpool, sizeof(*statsVals) * STAT_NSTATS ); + ss->statsVals = statsVals; + XWStreamCtxt* stream = mkStream( dutil ); const XP_UCHAR* keys[] = { STATS_KEY, NULL }; dutil_loadStream( dutil, xwe, keys, stream ); - XP_U32* statsVals - = XP_CALLOC( dutil->mpool, sizeof(*statsVals) * STAT_NSTATS ); - XP_U8 version; if ( stream_gotU8( stream, &version ) ) { + XP_U32 startTime = 0; + if ( VERSION_1 <= version ) { + startTime = stream_getU32(stream); + } else { + startTime = dutil_getCurSeconds( dutil, xwe ); + setStoreTimerLocked( dutil, xwe ); /* something to save */ + } + ss->startTime = startTime; + XP_U8 stat; while ( stream_gotU8( stream, &stat ) ) { XP_U32 value = stream_getU32VL( stream ); @@ -176,7 +206,6 @@ loadCounts( XW_DUtilCtxt* dutil, XWEnv xwe ) } } stream_destroy( stream ); - return statsVals; } #ifdef DUTIL_TIMERS