@ -23,8 +23,64 @@ static SQLite3DB * debugdb_disk = NULL;
sqlite3_stmt * statement1 = NULL ;
static unsigned int debug_output = 1 ;
# define DEBUG_MSG_MAXSIZE 1024
struct DebugLogEntry {
unsigned long long time ;
unsigned long long lapse ;
int thr ;
std : : string file ;
int line ;
std : : string funct ;
int module ;
std : : string modname ;
int verbosity ;
std : : string message ;
std : : string backtrace ;
} ;
static const size_t limitSize = 30 ;
static std : : vector < DebugLogEntry > log_buffer = { } ;
/**
* @ brief Synchronizes the log buffer to the SQLite database .
*
* This function writes the contents of the shared log buffer to the SQLite database
* in a single transaction . It assumes that the log buffer is protected by a mutex
* and that the prepared statement ` statement1 ` has already been prepared .
* This happens in proxy_debug_func ( )
*
* @ param db A pointer to the SQLite3DB object .
*/
void sync_log_buffer_to_disk ( SQLite3DB * db ) {
int rc ;
db - > execute ( " BEGIN TRANSACTION " ) ;
for ( const auto & entry : log_buffer ) {
rc = ( * proxy_sqlite3_bind_int64 ) 64 ( statement1 , 1 , entry . time ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_int64 ) 64 ( statement1 , 2 , entry . lapse ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_int64 ) ( statement1 , 3 , entry . thr ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_text ) ( statement1 , 4 , entry . file . c_str ( ) , - 1 , SQLITE_TRANSIENT ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_int64 ) ( statement1 , 5 , entry . line ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_text ) ( statement1 , 6 , entry . funct . c_str ( ) , - 1 , SQLITE_TRANSIENT ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_int64 ) ( statement1 , 7 , entry . module ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_text ) ( statement1 , 8 , entry . modname . c_str ( ) , - 1 , SQLITE_TRANSIENT ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_int64 ) ( statement1 , 9 , entry . verbosity ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_text ) ( statement1 , 10 , entry . message . c_str ( ) , - 1 , SQLITE_TRANSIENT ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_text ) ( statement1 , 11 , entry . backtrace . c_str ( ) , - 1 , SQLITE_TRANSIENT ) ; ASSERT_SQLITE_OK ( rc , db ) ;
SAFE_SQLITE3_STEP2 ( statement1 ) ;
rc = ( * proxy_sqlite3_clear_bindings ) ( statement1 ) ; ASSERT_SQLITE_OK ( rc , db ) ;
// Note: no assert() in proxy_debug_func() after sqlite3_reset() because it is possible that we are in shutdown
rc = ( * proxy_sqlite3_reset ) ( statement1 ) ; // ASSERT_SQLITE_OK(rc, db);
}
db - > execute ( " COMMIT " ) ;
log_buffer . clear ( ) ;
}
/**
* @ brief Contains all filters related to debug .
* @ details The convention for key value is ` filename : line : function ` . This key structure also applies also
@ -210,21 +266,28 @@ void proxy_debug_func(
}
}
if ( write_to_disk = = true ) {
rc = ( * proxy_sqlite3_bind_int64 ) ( statement1 , 1 , curtime ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_int64 ) ( statement1 , 2 , curtime - pretime ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_int64 ) ( statement1 , 3 , thr ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_text ) ( statement1 , 4 , __file , - 1 , SQLITE_TRANSIENT ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_int64 ) ( statement1 , 5 , __line ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_text ) ( statement1 , 6 , __func , - 1 , SQLITE_TRANSIENT ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_int64 ) ( statement1 , 7 , module ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_text ) ( statement1 , 8 , GloVars . global . gdbg_lvl [ module ] . name , - 1 , SQLITE_TRANSIENT ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_int64 ) ( statement1 , 9 , verbosity ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_text ) ( statement1 , 10 , origdebugbuff , - 1 , SQLITE_TRANSIENT ) ; ASSERT_SQLITE_OK ( rc , db ) ;
rc = ( * proxy_sqlite3_bind_text ) ( statement1 , 11 , longdebugbuff2 , - 1 , SQLITE_TRANSIENT ) ; ASSERT_SQLITE_OK ( rc , db ) ;
SAFE_SQLITE3_STEP2 ( statement1 ) ;
rc = ( * proxy_sqlite3_clear_bindings ) ( statement1 ) ; ASSERT_SQLITE_OK ( rc , db ) ;
// Note: no assert() in proxy_debug_func() after sqlite3_reset() because it is possible that we are in shutdown
rc = ( * proxy_sqlite3_reset ) ( statement1 ) ; // ASSERT_SQLITE_OK(rc, db);
// Create a DebugLogEntry
LogEntry entry ;
entry . time = curtime ;
entry . lapse = curtime - pretime ;
entry . thr = thr ;
entry . file = __file ;
entry . line = __line ;
entry . funct = __func ;
entry . module = module ;
entry . modname = GloVars . global . gdbg_lvl [ module ] . name ;
entry . verbosity = verbosity ;
entry . message = origdebugbuff ;
entry . backtrace = longdebugbuff2 ;
log_buffer . push_back ( entry ) ;
// we now batch writes
// note1: in case of crash, the database will have some missing entries,
// but the entries can be read in `log_buffer` in the core dump
// note2: also in case of shutdown , `log_buffer` will have entries that won't be saved.
// if we really want *all* entries, we could just call sync_log_buffer_to_disk() on shutdown
if ( log_buffer . size ( ) > limitSize ) {
sync_log_buffer_to_disk ( db ) ;
}
}
}
pthread_mutex_unlock ( & debug_mutex ) ;