ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
asciichat_errno.c
Go to the documentation of this file.
1
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <stdarg.h>
11#include <time.h>
12#ifndef _WIN32
13#include <errno.h>
14#endif
15
16#include "asciichat_errno.h"
17#include "util/path.h"
18#include "platform/system.h"
19#include "common.h"
20#include "log/logging.h"
21
22/* ============================================================================
23 * Thread-Local Storage
24 * ============================================================================
25 */
26
28 .file = NULL,
29 .line = 0,
30 .function = NULL,
31 .context_message = NULL,
32 .timestamp = 0,
33 .system_errno = 0,
34 .backtrace = {0},
35 .backtrace_symbols = NULL,
36 .stack_depth = 0,
37 .has_system_error = false};
38
40
41// Suppression flag to prevent error context allocation during cleanup
42static bool g_suppress_error_context = false;
43
44/* ============================================================================
45 * Error Statistics
46 * ============================================================================
47 */
48
49static asciichat_error_stats_t error_stats = {0};
50static bool stats_initialized = false;
51
52/* ============================================================================
53 * Thread-Safe Error Storage
54 * ============================================================================
55 */
56
57#define MAX_THREAD_ERRORS 64
58static struct {
61 bool valid;
62} thread_errors[MAX_THREAD_ERRORS] = {0};
63
64/* ============================================================================
65 * Internal Helper Functions
66 * ============================================================================
67 */
68
69static uint64_t get_timestamp_microseconds(void) {
70 struct timespec ts;
71 if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
72 // Convert seconds and nanoseconds to microseconds
73 return (uint64_t)ts.tv_sec * 1000000ULL + (uint64_t)(ts.tv_nsec / 1000);
74 }
75 return 0;
76}
77
78static void capture_backtrace(void **backtrace, char ***backtrace_symbols, int *stack_depth) {
79#ifndef NDEBUG // Capture in Debug and Dev modes
80 *stack_depth = platform_backtrace(backtrace, 32);
81 *backtrace_symbols = platform_backtrace_symbols(backtrace, *stack_depth);
82#else
83 (void)backtrace;
84 (void)backtrace_symbols;
85 *stack_depth = 0;
86#endif
87}
88
89static bool skip_backtrace_frame(const char *frame) {
90 return (strstr(frame, "BaseThreadInitThunk") != NULL || strstr(frame, "RtlUserThreadStart") != NULL ||
91 strstr(frame, "__scrt_common_main_seh") != NULL || strstr(frame, "capture_backtrace") != NULL ||
92 strstr(frame, "safe_backtrace") != NULL || strstr(frame, "platform_backtrace") != NULL ||
93 strstr(frame, "asciichat_set_errno") != NULL || strstr(frame, "asciichat_set_errno_with_message") != NULL ||
94 strstr(frame, "SET_ERRNO") != NULL || strstr(frame, "asciichat_fatal_with_context") != NULL ||
95 strstr(frame, "asciichat_print_error_context") != NULL);
96}
97
98void log_labeled(const char *label, log_color_t color, const char *message, ...) {
99 va_list args;
100 va_start(args, message);
101 char *formatted_message = format_message(message, args);
102 va_end(args);
103
104 safe_fprintf(stderr, "%s%s%s: %s\n", log_level_color(color), label, log_level_color(LOG_COLOR_RESET),
105 formatted_message);
106
107 log_file("%s: %s", label, formatted_message);
108
109 SAFE_FREE(formatted_message);
110}
111
112/* ============================================================================
113 * Core Error Setting Functions
114 * ============================================================================
115 */
116
117void asciichat_set_errno(asciichat_error_t code, const char *file, int line, const char *function,
118 const char *context_message) {
119 // Suppress error context allocation during cleanup to prevent leaks
120 if (g_suppress_error_context) {
121 return;
122 }
123
124 // Clear any existing context message
128 }
129
130 // Set the error context
135 asciichat_errno_context.timestamp = get_timestamp_microseconds();
137
138 // Set the simple error code variable
139 asciichat_errno = code;
140
141 // Copy context message if provided
142 if (context_message == NULL) {
143 log_error("context_message is NULL");
144 const char *fallback = "No context message (this is invalid - set a context message)";
145 size_t len = strlen(fallback) + 1;
149 } else {
150 log_error("SAFE_MALLOC failed for fallback context_message");
151 }
152 } else {
153 size_t len = strlen(context_message) + 1;
157 } else {
158 log_error("SAFE_MALLOC failed for context_message");
159 }
160 }
161
162 // Capture stack trace in debug builds
166 }
169
170 // Record in statistics
172}
173
174void asciichat_set_errno_with_message(asciichat_error_t code, const char *file, int line, const char *function,
175 const char *format, ...) {
176 va_list args;
177 va_start(args, format);
178
179 char *context_message = format_message(format, args);
180 asciichat_set_errno(code, file, line, function, context_message);
181
182 if (context_message) {
183 SAFE_FREE(context_message);
184 }
185
186 va_end(args);
187}
188
189void asciichat_set_errno_with_system_error(asciichat_error_t code, const char *file, int line, const char *function,
190 int sys_errno) {
191 asciichat_set_errno(code, file, line, function, NULL);
194}
195
197 const char *function, int sys_errno, const char *format, ...) {
198 va_list args;
199 va_start(args, format);
200
201 char *context_message = format_message(format, args);
202 asciichat_set_errno(code, file, line, function, context_message);
205
206 if (context_message) {
207 SAFE_FREE(context_message);
208 }
209
210 va_end(args);
211}
212
213void asciichat_set_errno_with_wsa_error(asciichat_error_t code, const char *file, int line, const char *function,
214 int wsa_error) {
215 asciichat_set_errno(code, file, line, function, NULL);
218}
219
223
224/* ============================================================================
225 * Error Checking and Clearing Functions
226 * ============================================================================
227 */
228
231 return false;
232 }
233
234 if (context) {
235 *context = asciichat_errno_context;
236 }
237
238 return true;
239}
240
265
269
270/* ============================================================================
271 * Enhanced FATAL Functions
272 * ============================================================================
273 */
274
275void asciichat_fatal_with_context(asciichat_error_t code, const char *file, int line, const char *function,
276 const char *format, ...) {
277 (void)file;
278 (void)line;
279 (void)function;
280
281 // Print library error context if available
283 if (HAS_ERRNO(&err_ctx)) {
284 log_labeled("\nasciichat_errno: libary code error context", LOG_COLOR_ERROR, "");
286 } else {
287 log_plain("WARNING: No error context found (asciichat_errno_context.code=%d)", asciichat_errno_context.code);
288 }
289
290 safe_fprintf(stderr, "\n");
291 log_labeled("FATAL ERROR", LOG_COLOR_FATAL, "exit code %d (%s)", (int)code, asciichat_error_string(code));
292#ifndef NDEBUG
293 const char *relative_file = extract_project_relative_path(file);
294 log_plain(" Location: %s:%d in %s()", relative_file, line, function);
295#endif
296
297 if (format) {
298 va_list args;
299 va_start(args, format);
300 char *formatted_message = format_message(format, args);
301 log_plain(" Error message: %s", formatted_message);
302 SAFE_FREE(formatted_message);
303 va_end(args);
304 }
305
306#ifndef NDEBUG
307 // Always print platform backtrace in debug/dev builds
308 void *buffer[32];
309 int size = platform_backtrace(buffer, 32);
310 if (size > 0) {
311 char **symbols = platform_backtrace_symbols(buffer, size);
312 if (symbols) {
313 platform_print_backtrace_symbols("\nFATAL BACKTRACE", symbols, size, 0, 0, skip_backtrace_frame);
315 }
316 }
317#endif
318
319 exit(code);
320}
321
322/* ============================================================================
323 * Error Context Printing Functions
324 * ============================================================================
325 */
326
328 if (!context || context->code == ASCIICHAT_OK) {
329 return;
330 }
331
332 if (context->file && context->line && context->function) {
333 log_plain(" Location: %s:%d in %s()", extract_project_relative_path(context->file), context->line,
334 context->function);
335 } else {
336 log_plain(" Location: unknown (set by system code)");
337 }
338
339 if (context->context_message) {
341 context->context_message);
342 log_file(" Context: %s", context->context_message);
343 }
344
345 if (context->has_system_error) {
346 log_plain(" System error: %s (code: %d, meaning: %s)", SAFE_STRERROR(context->system_errno), context->system_errno,
347 SAFE_STRERROR(context->system_errno));
348 }
349
350 // Print timestamp
351 if (context->timestamp > 0) {
352 time_t sec = (time_t)(context->timestamp / 1000000);
353 long usec = (long)(context->timestamp % 1000000);
354 struct tm tm_info;
355 if (platform_localtime(&sec, &tm_info) == ASCIICHAT_OK) {
356 char time_str[64];
357 (void)strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", &tm_info);
358 log_plain(" Timestamp: %s.%06ld", time_str, usec);
359 }
360 }
361
362 // Print stack trace from library error
363 if (context->stack_depth > 0 && context->backtrace_symbols) {
364 platform_print_backtrace_symbols("\nBacktrace from library error", context->backtrace_symbols, context->stack_depth,
365 0, 0, skip_backtrace_frame);
366 }
367}
368
369/* ============================================================================
370 * Error Statistics Functions
371 * ============================================================================
372 */
373
375 if (!stats_initialized) {
376 memset(&error_stats, 0, sizeof(error_stats));
377 stats_initialized = true;
378 }
379}
380
382 if (!stats_initialized) {
384 }
385
386 if (code >= 0 && code < 256) {
387 error_stats.error_counts[code]++;
388 }
389 error_stats.total_errors++;
390 error_stats.last_error_time = get_timestamp_microseconds();
391 error_stats.last_error_code = code;
392}
393
395 if (!stats_initialized || error_stats.total_errors == 0) {
396 log_plain("No errors recorded.\n");
397 return;
398 }
399
400 log_plain("\n=== ascii-chat Error Statistics ===\n");
401 log_plain("Total errors: %llu\n", (unsigned long long)error_stats.total_errors);
402
403 if (error_stats.last_error_time > 0) {
404 time_t sec = (time_t)(error_stats.last_error_time / 1000000);
405 struct tm tm_info;
406 if (platform_localtime(&sec, &tm_info) == ASCIICHAT_OK) {
407 char time_str[64];
408 strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", &tm_info);
409 log_plain("Last error: %s (code %d)\n", time_str, (int)error_stats.last_error_code);
410 }
411 }
412
413 log_plain("\nError breakdown:\n");
414 for (int i = 0; i < 256; i++) {
415 if (error_stats.error_counts[i] > 0) {
416 log_plain(" %3d (%s): %llu\n", i, asciichat_error_string((asciichat_error_t)i),
417 (unsigned long long)error_stats.error_counts[i]);
418 }
419 }
420 log_plain("\n");
421}
422
424 memset(&error_stats, 0, sizeof(error_stats));
425}
426
428 if (!stats_initialized) {
430 }
431 return error_stats;
432}
433
434/* ============================================================================
435 * Thread-Safe Error Propagation Functions
436 * ============================================================================
437 */
438
440 for (int i = 0; i < MAX_THREAD_ERRORS; i++) {
441 if (thread_errors[i].valid && thread_errors[i].thread_id == thread_id) {
442 return thread_errors[i].error_code;
443 }
444 }
445 return ASCIICHAT_OK;
446}
447
449 // Find existing entry or empty slot
450 int slot = -1;
451 for (int i = 0; i < MAX_THREAD_ERRORS; i++) {
452 if (thread_errors[i].valid && thread_errors[i].thread_id == thread_id) {
453 slot = i;
454 break;
455 }
456 if (!thread_errors[i].valid && slot == -1) {
457 slot = i;
458 }
459 }
460
461 if (slot >= 0) {
462 thread_errors[slot].thread_id = thread_id;
463 thread_errors[slot].error_code = code;
464 thread_errors[slot].valid = true;
465 }
466}
467
469 for (int i = 0; i < MAX_THREAD_ERRORS; i++) {
470 if (thread_errors[i].valid && thread_errors[i].thread_id == thread_id) {
471 thread_errors[i].valid = false;
472 break;
473 }
474 }
475}
476
477/* ============================================================================
478 * Cleanup
479 * ============================================================================
480 */
481
482void asciichat_errno_suppress(bool suppress) {
483 g_suppress_error_context = suppress;
484}
485
490 }
491
494 }
495
496 // Reset the context to a clean state
499
500 // Suppress any further error context allocation to prevent cleanup-phase leaks
501 // This prevents other atexit() functions from allocating new contexts after we've cleaned up
502 g_suppress_error_context = true;
503}
asciichat_error_t error_code
#define MAX_THREAD_ERRORS
bool valid
int thread_id
⚠️‼️ Error and/or exit() when things go bad.
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:358
#define SAFE_FREE(ptr)
Definition common.h:320
#define SAFE_MALLOC(size, cast)
Definition common.h:208
#define SAFE_STRERROR(errnum)
Definition common.h:385
unsigned long long uint64_t
Definition common.h:59
void asciichat_set_errno(asciichat_error_t code, const char *file, int line, const char *function, const char *context_message)
Set error code with basic context.
bool asciichat_has_wsa_error(void)
Check if current error has WSA error code.
void asciichat_clear_thread_error(int thread_id)
Clear error code for a specific thread.
void asciichat_fatal_with_context(asciichat_error_t code, const char *file, int line, const char *function, const char *format,...)
Exit with error code and context (used by FATAL macro)
void asciichat_errno_suppress(bool suppress)
Suppress error logging and reporting.
void asciichat_set_errno_with_system_error(asciichat_error_t code, const char *file, int line, const char *function, int sys_errno)
Set error code with system error (errno)
#define HAS_ERRNO(var)
Check if an error occurred and get full context.
__thread asciichat_error_t asciichat_errno
Thread-local current error code.
void asciichat_error_stats_print(void)
Print error statistics to stderr.
bool asciichat_has_errno(asciichat_error_context_t *context)
Check if error occurred and get full context.
void asciichat_clear_errno(void)
Clear the current error state.
asciichat_error_t asciichat_get_thread_error(int thread_id)
Get error code for a specific thread.
asciichat_error_t asciichat_get_errno(void)
Get current error code.
void asciichat_set_errno_with_wsa_error(asciichat_error_t code, const char *file, int line, const char *function, int wsa_error)
Set error code with Windows socket error (WSA error)
asciichat_error_stats_t asciichat_error_stats_get(void)
Get current error statistics.
void asciichat_error_stats_init(void)
Initialize error statistics system.
void asciichat_errno_cleanup(void)
Cleanup error system resources.
void asciichat_set_errno_with_message(asciichat_error_t code, const char *file, int line, const char *function, const char *format,...)
Set error code with formatted message.
void asciichat_error_stats_reset(void)
Reset all error statistics to zero.
__thread asciichat_error_context_t asciichat_errno_context
Thread-local error context storage.
void asciichat_set_thread_error(int thread_id, asciichat_error_t code)
Set error code for a specific thread.
void asciichat_print_error_context(const asciichat_error_context_t *context)
Print full error context to stderr.
void asciichat_set_errno_with_system_error_and_message(asciichat_error_t code, const char *file, int line, const char *function, int sys_errno, const char *format,...)
Set error code with system error and formatted message.
void asciichat_error_stats_record(asciichat_error_t code)
Record an error in statistics.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ASCIICHAT_OK
Definition error_codes.h:48
const char * log_level_color(log_color_t color)
Get color string for a given color enum.
char * format_message(const char *format, va_list args)
Format a message using va_list.
log_color_t
Color enum for logging - indexes into color arrays.
#define log_error(...)
Log an ERROR message.
void log_labeled(const char *label, log_color_t color, const char *message,...)
Print a labeled message with color.
#define log_file(...)
File-only logging - writes to log file only, no stderr output.
#define log_plain(...)
Plain logging - writes to both log file and stderr without timestamps or log levels.
@ LOG_COLOR_RESET
@ LOG_COLOR_FATAL
@ LOG_COLOR_ERROR
@ LOG_COLOR_WARN
int safe_fprintf(FILE *stream, const char *format,...)
Safe version of fprintf.
int platform_backtrace(void **buffer, int size)
Get a backtrace of the current call stack.
void platform_print_backtrace_symbols(const char *label, char **symbols, int count, int skip_frames, int max_frames, backtrace_frame_filter_t filter)
Print pre-resolved backtrace symbols with consistent formatting.
asciichat_error_t platform_localtime(const time_t *timer, struct tm *result)
Platform-safe localtime wrapper.
void platform_backtrace_symbols_free(char **strings)
Free symbol array returned by platform_backtrace_symbols()
char ** platform_backtrace_symbols(void *const *buffer, int size)
Convert backtrace addresses to symbol names.
int errno
const char * extract_project_relative_path(const char *file)
Extract relative path from an absolute path.
Definition path.c:127
📝 Logging API with multiple log levels and terminal output control
📂 Path Manipulation Utilities
Error context structure.
int wsa_error
Windows socket error code (if applicable, 0 otherwise)
int system_errno
System errno value (if applicable, 0 otherwise)
const char * function
Function name where error occurred (NULL in release builds)
char * context_message
Optional custom message (dynamically allocated, owned by system)
uint64_t timestamp
Timestamp when error occurred (microseconds since epoch)
char ** backtrace_symbols
Stack trace symbol strings (debug builds only)
bool has_system_error
True if system_errno is valid.
const char * file
Source file where error occurred (NULL in release builds)
int stack_depth
Number of stack frames captured (0 if not captured)
int line
Line number where error occurred (0 in release builds)
bool has_wsa_error
True if wsa_error is valid.
asciichat_error_t code
Error code (asciichat_error_t enum value)
void * backtrace[32]
Stack trace addresses (debug builds only)
Error statistics structure.
uint64_t last_error_time
Timestamp of last error (microseconds since epoch)
uint64_t error_counts[256]
Count for each error code (256 possible error codes)
asciichat_error_t last_error_code
Error code of last error recorded.
uint64_t total_errors
Total number of errors recorded (sum of all error_counts)
Cross-platform system functions interface for ascii-chat.
⏱️ High-precision timing utilities using sokol_time.h and uthash
Common SIMD utilities and structures.