ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
stats.c
Go to the documentation of this file.
1
133#include <ascii-chat/atomic.h>
134#include <stdio.h>
135#include <string.h>
136#include <time.h>
137#include "main.h"
138#include "stats.h"
139#include "client.h"
140#include "render.h"
141#include <ascii-chat/common.h>
148
164
173
183
188int stats_init(void) {
189 if (!lifecycle_init(&g_stats_lc, "stats")) {
190 return 0; // Already initialized
191 }
192 return 0;
193}
194
201void stats_cleanup(void) {
202 lifecycle_shutdown(&g_stats_lc);
203}
204
205/* ============================================================================
206 * Statistics Collection and Reporting Thread
207 * ============================================================================
208 */
209
327void *stats_logger_thread(void *arg) {
328 (void)arg;
329
331 // Log buffer pool statistics every 10 seconds with fast exit checking (10ms intervals)
332 for (int i = 0; i < 1000 && !atomic_load_bool(&g_should_exit); i++) {
333 platform_sleep_us(10 * US_PER_MS_INT); // 10ms sleep
334 }
335
336 // Check exit condition before proceeding with statistics logging
337 // Check multiple times to avoid accessing freed resources during shutdown.
339 break;
340 }
341
342 // Collect all statistics data first
343#ifndef NDEBUG
344 char lock_debug_info[BUFFER_SIZE_MEDIUM] = {0};
345 // Check exit condition again before accessing lock_debug.
346 // lock_debug might be destroyed during shutdown
348 uint64_t total_acquired = 0, total_released = 0;
349 uint32_t currently_held = 0;
350 debug_sync_get_stats(&total_acquired, &total_released, &currently_held);
351
352 safe_snprintf(lock_debug_info, sizeof(lock_debug_info),
353 "Historical Mutex/RWLock Statistics:\n"
354 " Total locks acquired: %llu\n"
355 " Total locks released: %llu\n"
356 " Currently held: %u\n"
357 " Net locks (acquired - released): %lld",
358 (unsigned long long)total_acquired, (unsigned long long)total_released, currently_held,
359 (long long)total_acquired - (long long)total_released);
360 }
361#endif
362
363 // Collect client statistics
364 // Check exit condition again before accessing rwlock.
365 // rwlock might be destroyed during shutdown.
367 break;
368 }
369
371 int active_clients = 0;
372 int clients_with_audio = 0;
373 int clients_with_video = 0;
374 char client_details[BUFFER_SIZE_XLARGE] = {0};
375 int client_details_len = 0;
376
377 for (int i = 0; i < MAX_CLIENTS; i++) {
379 active_clients++;
381 clients_with_audio++;
382 }
384 clients_with_video++;
385 }
386 }
387 }
388
389 // Collect per-client details
390 for (int i = 0; i < MAX_CLIENTS; i++) {
392 bool is_active = atomic_load_bool(&client->active);
393 const char *client_id_snapshot = client->client_id;
394
395 if (is_active && client_id_snapshot != 0) {
396 // Check audio queue stats
397 if (client->audio_queue) {
398 uint64_t enqueued, dequeued, dropped;
399 packet_queue_get_stats(client->audio_queue, &enqueued, &dequeued, &dropped);
400 if (enqueued > 0 || dequeued > 0 || dropped > 0) {
401 int len =
402 safe_snprintf(client_details + client_details_len, sizeof(client_details) - client_details_len,
403 " Client %u audio queue: %llu enqueued, %llu dequeued, %llu dropped", client_id_snapshot,
404 (unsigned long long)enqueued, (unsigned long long)dequeued, (unsigned long long)dropped);
405 if (len > 0 && client_details_len + len < (int)sizeof(client_details)) {
406 client_details_len += len;
407 }
408 }
409 }
410 // Check video buffer stats
411 if (client->outgoing_video_buffer) {
414 if (stats.total_frames > 0) {
415 int len = safe_snprintf(client_details + client_details_len, sizeof(client_details) - client_details_len,
416 " Client %u video buffer: %llu frames, %llu dropped (%.1f%% drop rate)",
417 client_id_snapshot, (unsigned long long)stats.total_frames,
418 (unsigned long long)stats.dropped_frames, stats.drop_rate * 100.0f);
419 if (len > 0 && client_details_len + len < (int)sizeof(client_details)) {
420 client_details_len += len;
421 }
422 }
423 }
424 }
425 }
427
428 // Single comprehensive log statement
429 if (client_details_len > 0) {
430 log_info("Stats: Clients: %d, Audio: %d, Video: %d\n%s", active_clients, clients_with_audio, clients_with_video,
431 client_details);
432 }
433 }
434
436
438
439 // Clean up thread-local error context before exit
441
442 return NULL;
443}
444
474 // Aggregate statistics from all active clients
475 uint64_t total_frames_sent = 0;
476 uint64_t total_frames_dropped = 0;
477
478 // Read-lock to safely iterate over all clients
480
481 for (int i = 0; i < MAX_CLIENTS; i++) {
483 if (client->client_id[0] != '\0' && atomic_load_bool(&client->active)) {
484 // Aggregate frames sent to all clients
485 total_frames_sent += client->frames_sent;
486
487 // Get dropped frame statistics from outgoing video buffer
488 if (client->outgoing_video_buffer) {
491 total_frames_dropped += stats.dropped_frames;
492 }
493 }
494 }
495
497
498 // Update global statistics atomically
500 g_stats.frames_sent = total_frames_sent;
501 g_stats.frames_dropped = total_frames_dropped;
502
503 // Calculate frames_captured from frames_sent and frames_dropped
504 // frames_captured = frames_sent + frames_dropped (total output frames)
505 if (total_frames_sent > 0 || total_frames_dropped > 0) {
506 g_stats.frames_captured = total_frames_sent + total_frames_dropped;
507 }
508
510}
511
555 // Check if stats mutex is initialized (safe in debug builds where stats_init() may not be called)
556 if (!lifecycle_is_initialized(&g_stats_lc)) {
557 return; // Mutex not initialized, skip logging
558 }
559
560 // Now safe to use the mutex
562 log_info("Server Statistics:\n"
563 " frames_captured=%llu\n"
564 " frames_sent=%llu\n"
565 " frames_dropped=%llu\n"
566 " Average FPS: capture=%.2f, send=%.2f",
567 (unsigned long long)g_stats.frames_captured, (unsigned long long)g_stats.frames_sent,
570}
asciichat_error_stats_t stats
bool atomic_load_bool(atomic_t *a)
Atomically load a boolean value.
Definition atomic.c:169
⚛️ Atomic operations abstraction layer with debug tracking
🗃️ Lock-Free Unified Memory Buffer Pool with Lazy Allocation
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_XLARGE
Extra large buffer size (2048 bytes)
#define BUFFER_SIZE_MEDIUM
Medium buffer size (512 bytes)
unsigned int uint32_t
Definition common.h:58
unsigned long long uint64_t
Definition common.h:59
void debug_sync_get_stats(uint64_t *total_acquired, uint64_t *total_released, uint32_t *currently_held)
Get synchronization statistics.
Definition sync.c:722
void asciichat_error_stats_print(void)
Print error statistics to stderr.
void asciichat_errno_destroy(void)
Cleanup error system resources.
#define MAX_CLIENTS
Maximum possible clients (static array size) - actual runtime limit set by –max-clients (1-32)
Definition limits.h:26
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
#define US_PER_MS_INT
Definition time.h:160
void packet_queue_get_stats(packet_queue_t *queue, uint64_t *enqueued, uint64_t *dequeued, uint64_t *dropped)
Get queue statistics.
Definition queue.c:637
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:148
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
#define rwlock_rdlock(lock)
Acquire a read lock (with debug tracking in debug builds)
Definition rwlock.h:294
bool debug_sync_is_initialized(void)
Definition sync.c:820
void platform_sleep_us(unsigned int us)
High-precision sleep function with microsecond precision.
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)
#define rwlock_rdunlock(lock)
Release a read lock (with debug tracking in debug builds)
Definition rwlock.h:331
void video_frame_get_stats(video_frame_buffer_t *vfb, video_frame_stats_t *stats)
Get frame statistics for quality monitoring.
Platform initialization and static synchronization helpers.
bool lifecycle_is_initialized(const lifecycle_t *lc)
Definition lifecycle.c:155
bool lifecycle_init(lifecycle_t *lc, const char *name)
Definition lifecycle.c:26
bool lifecycle_shutdown(lifecycle_t *lc)
Definition lifecycle.c:108
Lock-free module lifecycle state machine using pure stdatomic.
#define LIFECYCLE_INIT_MUTEX(m)
Static initializer for lifecycle with mutex.
Definition lifecycle.h:74
ascii-chat Server Mode Entry Point Header
Per-client rendering threads with rate limiting.
rwlock_t g_client_manager_rwlock
Reader-writer lock protecting the global client manager.
client_manager_t g_client_manager
Global client manager singleton - central coordination point.
Client-side H.265 media capture and encoding pipeline.
server_stats_t g_stats
Global server statistics structure.
Definition stats.c:163
void log_server_stats(void)
Log comprehensive server statistics summary.
Definition stats.c:554
void * stats_logger_thread(void *arg)
Main statistics collection and reporting thread function.
Definition stats.c:327
int stats_init(void)
Initialize the stats mutex.
Definition stats.c:188
mutex_t g_stats_mutex
Mutex protecting global server statistics.
Definition stats.c:172
void stats_cleanup(void)
Cleanup the stats mutex.
Definition stats.c:201
void update_server_stats(void)
Update global server statistics (placeholder)
Definition stats.c:473
Server performance statistics tracking.
Per-client state structure for server-side client management.
video_frame_buffer_t * outgoing_video_buffer
char client_id[MAX_CLIENT_ID_LEN]
client_info_t clients[MAX_CLIENTS]
Array of client_info_t structures (backing storage)
Mutex type (POSIX: pthread_mutex_t with debug tracking)
Server performance statistics structure.
Definition stats.h:47
uint64_t frames_dropped
Definition stats.h:50
double avg_capture_fps
Definition stats.h:52
uint64_t frames_captured
Definition stats.h:48
double avg_send_fps
Definition stats.h:53
uint64_t frames_sent
Definition stats.h:49
Frame statistics structure.
🔒 Synchronization primitive debugging (mutexes, rwlocks, condition variables)
⏱️ High-precision timing utilities using sokol_time.h and uthash