xwords/xwords4/common/stats.c
2024-12-03 21:21:24 -08:00

248 lines
6.4 KiB
C

/*
* Copyright 2024 by Eric House (xwords@eehouse.org). All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "stats.h"
#include "device.h"
#include "xwstream.h"
#include "strutils.h"
#include "dbgutil.h"
#include "timers.h"
#include "xwmutex.h"
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 void loadCountsLocked( XW_DUtilCtxt* dutil, XWEnv xwe );
static void storeCountsLocked( XW_DUtilCtxt* dutil, XWEnv xwe );
static void setStoreTimerLocked( XW_DUtilCtxt* dutil, XWEnv xwe );
void
sts_init( XW_DUtilCtxt* dutil )
{
StatsState* ss = XP_CALLOC( dutil->mpool, sizeof(*ss) );
MUTEX_INIT( &ss->mutex, XP_TRUE );
dutil->statsState = ss;
}
void
sts_cleanup( XW_DUtilCtxt* dutil, XWEnv XP_UNUSED(xwe) )
{
StatsState* ss = dutil->statsState;
XP_ASSERT( !!ss );
MUTEX_DESTROY( &ss->mutex );
XP_FREEP( dutil->mpool, &ss->statsVals );
XP_FREEP( dutil->mpool, &ss );
}
void
sts_increment( XW_DUtilCtxt* dutil, XWEnv xwe, STAT stat )
{
if ( STAT_NONE < stat && stat < STAT_NSTATS ) {
StatsState* ss = dutil->statsState;
XP_ASSERT( !!ss );
WITH_MUTEX( &ss->mutex );
if ( !ss->statsVals ) {
loadCountsLocked( dutil, xwe );
}
++ss->statsVals[stat];
setStoreTimerLocked( dutil, xwe );
END_WITH_MUTEX();
}
}
cJSON*
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 ) {
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( stats, nam, val );
}
}
cJSON_AddNumberToObject( result, "since", ss->startTime );
END_WITH_MUTEX();
return result;
}
void
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 = statsVals;
ss->startTime = startTime;
storeCountsLocked( dutil, xwe );
END_WITH_MUTEX();
}
static const XP_UCHAR*
STATtoStr(STAT stat)
{
const XP_UCHAR* result = NULL;
#define CASESTR(s) case (s): result = #s; break
switch (stat) {
CASESTR(STAT_MQTT_RCVD);
CASESTR(STAT_MQTT_SENT);
CASESTR(STAT_REG_NOROOM);
CASESTR(STAT_NEW_SOLO);
CASESTR(STAT_NEW_TWO);
CASESTR(STAT_NEW_THREE);
CASESTR(STAT_NEW_FOUR);
CASESTR(STAT_NEW_REMATCH);
CASESTR(STAT_NBS_SENT);
CASESTR(STAT_NBS_RCVD);
CASESTR(STAT_BT_SENT);
CASESTR(STAT_BT_RCVD);
default:
XP_ASSERT(0);
}
#undef CASESTR
result += 5;
return result;
}
static void
storeCountsLocked( XW_DUtilCtxt* dutil, XWEnv xwe )
{
StatsState* ss = dutil->statsState;
XP_ASSERT( !!ss );
XWStreamCtxt* stream = mkStream( dutil );
stream_putU8( stream, VERSION_1 );
stream_putU32( stream, ss->startTime );
if ( !!ss->statsVals ) {
for ( int ii = 0; ii < STAT_NSTATS; ++ii ) {
XP_U32 val = ss->statsVals[ii];
if ( 0 != val ) {
stream_putU8( stream, ii );
stream_putU32VL( stream, val );
}
}
}
dutil_storeStream( dutil, xwe, STATS_KEY, stream );
stream_destroy( stream );
}
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 );
dutil_loadStream( dutil, xwe, STATS_KEY, stream );
XP_U8 version;
if ( stream_gotU8( stream, &version ) ) {
if ( VERSION_1 <= version ) {
ss->startTime = stream_getU32(stream);
}
XP_U8 stat;
while ( stream_gotU8( stream, &stat ) ) {
XP_U32 value = stream_getU32VL( stream );
statsVals[stat] = value;
}
}
stream_destroy( stream );
if ( 0 == ss->startTime ) {
ss->startTime = dutil_getCurSeconds( dutil, xwe );
setStoreTimerLocked( dutil, xwe ); /* something to save */
}
}
#ifdef DUTIL_TIMERS
static void
onStoreTimer( void* closure, XWEnv xwe, XP_Bool XP_UNUSED_DBG(fired) )
{
XP_LOGFF( "(fired: %s)", boolToStr(fired) );
XW_DUtilCtxt* dutil = (XW_DUtilCtxt*)closure;
StatsState* ss = dutil->statsState;
XP_ASSERT( !!ss );
WITH_MUTEX( &ss->mutex );
storeCountsLocked( dutil, xwe );
ss->timerSet = XP_FALSE;
END_WITH_MUTEX();
LOG_RETURN_VOID();
}
#endif
static void
setStoreTimerLocked( XW_DUtilCtxt* dutil, XWEnv xwe )
{
#ifdef DUTIL_TIMERS
StatsState* ss = dutil->statsState;
XP_ASSERT( !!ss );
if ( !ss->timerSet ) {
ss->timerSet = XP_TRUE;
XP_U32 inWhenMS = 5 * 1000;
#ifdef DEBUG
TimerKey key =
#endif
tmr_set( dutil, xwe, inWhenMS, onStoreTimer, dutil );
XP_LOGFF( "tmr_set() => %d", key );
} else {
// XP_LOGFF( "timer already set" );
}
#else
XP_USE(dutil);
XP_USE(xwe);
#endif
}