ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
src/server/stats.c
Go to the documentation of this file.
133
134#include <libplatform/atomic.h>
135#include <stdio.h>
136#include <string.h>
137#include <time.h>
138#include "main.h"
139#include "stats.h"
140#include "client.h"
141#include "render.h"
142#include <ascii-chat/common.h>
144#include <ascii-chat/buffer.h>
148#include <libplatform/lifecycle.h>
149
164server_stats_t g_stats = {0};
165
173mutex_t g_stats_mutex = {0};
174
183static lifecycle_t g_stats_lc = LIFECYCLE_INIT_MUTEX(&g_stats_mutex);
184
189int stats_init(void) {
190 if (!lifecycle_init(&g_stats_lc, "stats")) {
191 return 0; // Already initialized
192 }
193 return 0;
194}
195
202void stats_cleanup(void) {
203 lifecycle_shutdown(&g_stats_lc);
204}
205
206/* ============================================================================
207 * Statistics Collection and Reporting Thread
208 * ============================================================================
209 */
210
327
328void *stats_logger_thread(void *arg) {
329 (void)arg;
330
331 while (!atomic_load_bool(&g_should_exit)) {
332 // Log buffer domain statistics every 10 seconds with fast exit checking (10ms intervals)
333 for (int i = 0; i < 1000 && !atomic_load_bool(&g_should_exit); i++) {
334 platform_sleep_us(10 * US_PER_MS_INT); // 10ms sleep
335 }
336
337 // Check exit condition before proceeding with statistics logging
338 // Check multiple times to avoid accessing freed resources during shutdown.
339 if (atomic_load_bool(&g_should_exit)) {
340 break;
341 }
342
347
348 // Collect all statistics data first
349#ifndef NDEBUG
350 char lock_debug_info[BUFFER_SIZE_MEDIUM] = {0};
351 // Check exit condition again before accessing lock_debug.
352 // lock_debug might be destroyed during shutdown
353 if (!atomic_load_bool(&g_should_exit) && debug_sync_is_initialized()) {
354 uint64_t total_acquired = 0, total_released = 0;
355 uint32_t currently_held = 0;
356 debug_sync_get_stats(&total_acquired, &total_released, &currently_held);
357
358 safe_snprintf(lock_debug_info, sizeof(lock_debug_info),
359 "Historical Mutex/RWLock Statistics:\n"
360 " Total locks acquired: %llu\n"
361 " Total locks released: %llu\n"
362 " Currently held: %u\n"
363 " Net locks (acquired - released): %lld",
364 (unsigned long long)total_acquired, (unsigned long long)total_released, currently_held,
365 (long long)total_acquired - (long long)total_released);
366 }
367#endif
368
369 // Collect client statistics
370 // Check exit condition again before accessing rwlock.
371 // rwlock might be destroyed during shutdown.
372 if (atomic_load_bool(&g_should_exit)) {
373 break;
374 }
375
377 int active_clients = 0;
378 int clients_with_audio = 0;
379 int clients_with_video = 0;
380
381 for (int i = 0; i < MAX_CLIENTS; i++) {
382 if (atomic_load_bool(&g_client_manager.clients[i].active)) {
383 active_clients++;
384 if (atomic_load_bool(&g_client_manager.clients[i].is_sending_audio)) {
385 clients_with_audio++;
386 }
387 if (atomic_load_bool(&g_client_manager.clients[i].is_sending_video)) {
388 clients_with_video++;
389 }
390 }
391 }
392
394
395 log_info("Stats: Clients: %d, Audio: %d, Video: %d", active_clients, clients_with_audio, clients_with_video);
396 }
397
399
401
402 // Clean up thread-local error context before exit
403
404 return NULL;
405}
406
409 stats_snapshot_t snapshot;
410 if (!lifecycle_is_initialized(&g_stats_lc) || stats_scope_snapshot(stats_runtime_scope(), &snapshot) != ASCIICHAT_OK)
411 return;
412 mutex_lock(&g_stats_mutex);
413 g_stats.frames_captured = snapshot.counters[STATS_COUNTER_FRAMES_RECEIVED];
414 g_stats.frames_sent = snapshot.counters[STATS_COUNTER_FRAMES_SENT];
415 g_stats.frames_dropped = snapshot.counters[STATS_COUNTER_FRAMES_DROPPED];
416 double seconds = (double)(snapshot.sampled_ns - snapshot.started_ns) / 1e9;
417 g_stats.avg_capture_fps = seconds > 0 ? (double)g_stats.frames_captured / seconds : 0;
418 g_stats.avg_send_fps = seconds > 0 ? (double)g_stats.frames_sent / seconds : 0;
419 mutex_unlock(&g_stats_mutex);
420}
421
465 // Check if stats mutex is initialized (safe in debug builds where stats_init() may not be called)
466 if (!lifecycle_is_initialized(&g_stats_lc)) {
467 return; // Mutex not initialized, skip logging
468 }
469
470 // Now safe to use the mutex
471 mutex_lock(&g_stats_mutex);
472 log_info("Server Statistics:\n"
473 " frames_captured=%llu\n"
474 " frames_sent=%llu\n"
475 " frames_dropped=%llu\n"
476 " Average FPS: capture=%.2f, send=%.2f",
477 (unsigned long long)g_stats.frames_captured, (unsigned long long)g_stats.frames_sent,
478 (unsigned long long)g_stats.frames_dropped, g_stats.avg_capture_fps, g_stats.avg_send_fps);
479 mutex_unlock(&g_stats_mutex);
480}
void asciichat_error_stats_print(void)
void buffer_domain_log_stats(buffer_domain_t *domain, const char *name)
Definition buffer.c:392
buffer_domain_t * buffer_default_domain(void)
Definition buffer.c:423
void buffer_domain_trim(buffer_domain_t *domain)
Definition buffer.c:170
buffer_domain_t * buffer_control_domain(void)
Definition buffer.c:436
Common buffer size definitions.
⚙️ Common definitions, error codes, macros, and types shared throughout the application
atomic_t g_should_exit
Global application exit flag (shared across all modes).
Definition globals.c:34
#define BUFFER_SIZE_MEDIUM
Medium buffer size (512 bytes).
unsigned int uint32_t
Definition common.h:62
unsigned long long uint64_t
Definition common.h:63
@ ASCIICHAT_OK
Definition error_codes.h:46
#define MAX_CLIENTS
Maximum possible clients (static array size) - actual runtime limit set by –max-clients (1-128).
#define log_info(...)
Log an INFO message.
Definition log/log.h:608
#define US_PER_MS_INT
Definition time.h:172
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Platform-safe snprintf wrapper.
#define rwlock_rdlock(lock)
Acquire a read lock (with debug tracking in debug builds).
Definition rwlock.h:299
bool debug_sync_is_initialized(void)
#define rwlock_rdunlock(lock)
Release a read lock (with debug tracking in debug builds).
Definition rwlock.h:338
asciichat_error_t stats_scope_snapshot(const stats_scope_t *scope, stats_snapshot_t *out)
stats sampler config keeps the values that move through the subsystem: window_ns, resolution_ns....
stats_scope_t * stats_runtime_scope(void)
Definition runtime.c:80
rwlock_t g_client_manager_rwlock
Reader-writer lock protecting the global client manager.
client_manager_t g_client_manager
Hash table entry for client packet dispatch.
Per-client state management and lifecycle orchestration.
Per-client rendering threads with rate limiting.
void log_server_stats(void)
Log comprehensive server statistics summary.
void * stats_logger_thread(void *arg)
Main statistics collection and reporting thread function.
int stats_init(void)
Initialize the stats mutex.
mutex_t g_stats_mutex
Mutex protecting global server statistics.
void stats_cleanup(void)
Cleanup the stats mutex.
void update_server_stats(void)
Server performance statistics tracking.
🔤 String Manipulation and Shell Escaping Utilities
Counters and rates describing the server's media pipeline.
uint64_t counters[STATS_COUNTER_COUNT]
⏱️ High-precision timing utilities using sokol_time.h and uthash