ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
lib/debug/stats.c
Go to the documentation of this file.
5#include <libplatform/thread.h>
6#include <libplatform/cond.h>
7#include <libplatform/atomic.h>
11
12static atomic_t g_debug_stats_signal = {0};
13static atomic_t g_debug_stats_memory_signal = {0};
14static atomic_t g_debug_stats_exiting = {0};
15static atomic_t g_debug_stats_cleaning = {0};
16static platform_thread_t g_debug_stats_thread;
17static uint64_t g_debug_stats_main_thread;
18static mutex_t g_debug_stats_mutex;
19static cond_t g_debug_stats_condition;
20static bool g_debug_stats_initialized;
21static bool g_debug_stats_started;
22static uint64_t g_debug_stats_memory_deadline;
23static uint64_t g_debug_stats_memory_interval;
24
26 g_debug_stats_main_thread = thread_current_id();
27}
29 return g_debug_stats_main_thread;
30}
32 if (g_debug_stats_initialized)
33 return ASCIICHAT_OK;
34 if (mutex_init(&g_debug_stats_mutex, "debug_stats") != 0)
35 return SET_ERRNO(ERROR_THREAD, "Cannot initialize diagnostics mutex");
36 if (cond_init(&g_debug_stats_condition, "debug_stats") != 0) {
37 mutex_destroy(&g_debug_stats_mutex);
38 return SET_ERRNO(ERROR_THREAD, "Cannot initialize diagnostics condition");
39 }
40 g_debug_stats_initialized = true;
41 atomic_store_bool_impl(&g_debug_stats_cleaning, false);
42 return ASCIICHAT_OK;
43}
45 log_info("Diagnostic statistics:");
47}
49 if (!g_debug_stats_initialized)
50 return;
51 mutex_lock(&g_debug_stats_mutex);
52 g_debug_stats_memory_interval = interval;
53 g_debug_stats_memory_deadline = interval ? debug_report_deadline_after(time_get_ns(), interval) : 0;
54 cond_signal(&g_debug_stats_condition);
55 mutex_unlock(&g_debug_stats_mutex);
56}
58 // Do not signal a condition variable or invoke diagnostic hooks in a signal
59 // handler. The worker checks this flag at most 100ms after its next wakeup.
60 atomic_store_bool_impl(&g_debug_stats_signal, true);
61}
63 // Signal handlers may only set the request; the worker performs the report.
64 atomic_store_bool_impl(&g_debug_stats_memory_signal, true);
65}
67 return atomic_load_bool_impl(&g_debug_stats_cleaning);
68}
69void debug_stats_poll(void) {
70 if (!g_debug_stats_initialized)
71 return;
72 uint64_t now = time_get_ns();
73 bool all = atomic_exchange_bool_impl(&g_debug_stats_signal, false);
74 bool requested_memory = atomic_exchange_bool_impl(&g_debug_stats_memory_signal, false);
75 mutex_lock(&g_debug_stats_mutex);
76 bool memory = g_debug_stats_memory_deadline && now >= g_debug_stats_memory_deadline;
77 if (memory)
78 g_debug_stats_memory_deadline = debug_report_deadline_after(now, g_debug_stats_memory_interval);
79 mutex_unlock(&g_debug_stats_mutex);
80 if (all)
82#if defined(DEBUG_MEMORY) && !defined(NDEBUG)
83 if (memory || requested_memory)
84 debug_memory_report(GET_OPTION(color));
85#else
86 (void)memory;
87#endif
88}
89#ifndef EMSCRIPTEN_BUILD
90static void *debug_stats_worker(void *unused) {
91 (void)unused;
92 while (!atomic_load_bool_impl(&g_debug_stats_exiting)) {
94 mutex_lock(&g_debug_stats_mutex);
95 uint64_t now = time_get_ns();
96 uint64_t wait = 100 * NS_PER_MS_INT;
97 uint64_t deadlines[] = {g_debug_stats_memory_deadline};
98 for (size_t i = 0; i < sizeof(deadlines) / sizeof(deadlines[0]); ++i)
99 if (deadlines[i]) {
100 uint64_t remaining = deadlines[i] > now ? deadlines[i] - now : 1;
101 if (remaining < wait)
102 wait = remaining;
103 }
104 if (!atomic_load_bool_impl(&g_debug_stats_exiting))
105 cond_timedwait(&g_debug_stats_condition, &g_debug_stats_mutex, wait);
106 mutex_unlock(&g_debug_stats_mutex);
107 }
108 return NULL;
109}
110#endif
112 if (g_debug_stats_started)
113 return ASCIICHAT_OK;
115 if (result != ASCIICHAT_OK)
116 return result;
117 atomic_store_bool_impl(&g_debug_stats_exiting, false);
118 atomic_store_bool_impl(&g_debug_stats_cleaning, false);
119 options_t *opts = options_get();
120 if (opts) {
121 if (opts->debug_memory_report_interval > 0)
123 }
124#ifndef EMSCRIPTEN_BUILD
125 if (platform_thread_create(&g_debug_stats_thread, "debug_stats", debug_stats_worker, NULL) != 0)
126 return SET_ERRNO(ERROR_THREAD, "Cannot start diagnostics worker");
127#endif
128 g_debug_stats_started = true;
129 return ASCIICHAT_OK;
130}
132 if (!g_debug_stats_started)
133 return;
134 atomic_store_bool_impl(&g_debug_stats_cleaning, true);
135 mutex_lock(&g_debug_stats_mutex);
136 atomic_store_bool_impl(&g_debug_stats_exiting, true);
137 cond_signal(&g_debug_stats_condition);
138 mutex_unlock(&g_debug_stats_mutex);
139#ifndef EMSCRIPTEN_BUILD
140 // Join fully: diagnostic data must not be destroyed while a reader is alive.
141 if (thread_join(&g_debug_stats_thread, NULL) != 0)
142 FATAL(ERROR_THREAD, "Cannot join diagnostics worker");
143#endif
144 g_debug_stats_started = false;
145}
148 if (g_debug_stats_initialized) {
149 cond_destroy(&g_debug_stats_condition);
150 mutex_destroy(&g_debug_stats_mutex);
151 g_debug_stats_initialized = false;
152 atomic_store_bool_impl(&g_debug_stats_signal, false);
153 atomic_store_bool_impl(&g_debug_stats_memory_signal, false);
154 g_debug_stats_memory_deadline = g_debug_stats_memory_interval = 0;
155 }
156}
void asciichat_error_stats_print(void)
Synchronized per-thread pending error stacks and value snapshots. SET_ERRNO pushes,...
#define SET_ERRNO(code,...)
uint64_t debug_report_deadline_after(uint64_t now, uint64_t delay_ns)
Shared deadline and scheduling helpers for diagnostic reports.
#define FATAL(code,...)
Exit with error code and custom message, with stack trace in debug builds.
Definition common.h:160
unsigned long long uint64_t
Definition common.h:63
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_THREAD
#define log_info(...)
Log an INFO message.
Definition log/log.h:608
#define NS_PER_SEC_INT
Definition time.h:169
#define NS_PER_MS_INT
Definition time.h:168
#define GET_OPTION(field)
Safely get a specific option field (lock-free read).
Definition options.h:1336
struct options_state options_t
Immutable, RCU-published snapshot of the application's runtime configuration.
const options_t * options_get(void)
Get pointer to current options struct (lock-free, thread-safe).
Definition rcu.c:511
Statistics worker for error totals and memory reports. Signal handlers may only request a report.
void debug_stats_cleanup_thread(void)
void debug_stats_set_main_thread_id(void)
void debug_stats_destroy(void)
void debug_stats_print(void)
asciichat_error_t debug_stats_init(void)
bool debug_stats_is_cleanup_in_progress(void)
void debug_stats_trigger_print(void)
void debug_stats_trigger_memory_report(void)
uint64_t debug_stats_get_main_thread_id(void)
void debug_stats_poll(void)
void debug_stats_set_memory_report_interval(uint64_t interval)
asciichat_error_t debug_stats_start_thread(void)
⚙️ Unified options parsing system for ascii-chat with builder pattern and lock-free access
double debug_memory_report_interval
Interval in seconds for periodic memory reports.
Definition options.h:1252
⏱️ High-precision timing utilities using sokol_time.h and uthash