16#include <ascii-chat/asciichat_errno.h>
17#include <ascii-chat/util/path.h>
18#include <ascii-chat/util/time.h>
19#include <ascii-chat/util/string.h>
20#include <ascii-chat/platform/system.h>
21#include <ascii-chat/platform/errno.h>
22#include <ascii-chat/platform/init.h>
23#include <ascii-chat/common.h>
24#include <ascii-chat/log/logging.h>
35 .context_message = NULL,
39 .backtrace_symbols = NULL,
41 .has_system_error =
false};
46static bool g_suppress_error_context =
false;
53static asciichat_error_stats_t error_stats = {0};
54static bool stats_initialized =
false;
55static static_mutex_t g_error_stats_mutex = STATIC_MUTEX_INIT;
62#define MAX_THREAD_ERRORS 64
74static void capture_backtrace(
void **backtrace,
char ***backtrace_symbols,
int *stack_depth) {
80 (void)backtrace_symbols;
85static bool skip_backtrace_frame(
const char *frame) {
86 return (strstr(frame,
"BaseThreadInitThunk") != NULL || strstr(frame,
"RtlUserThreadStart") != NULL ||
87 strstr(frame,
"__scrt_common_main_seh") != NULL || strstr(frame,
"capture_backtrace") != NULL ||
88 strstr(frame,
"safe_backtrace") != NULL || strstr(frame,
"platform_backtrace") != NULL ||
89 strstr(frame,
"asciichat_set_errno") != NULL || strstr(frame,
"asciichat_set_errno_with_message") != NULL ||
90 strstr(frame,
"SET_ERRNO") != NULL || strstr(frame,
"asciichat_fatal_with_context") != NULL ||
91 strstr(frame,
"asciichat_print_error_context") != NULL);
94void log_labeled(
const char *label, log_color_t color,
const char *message, ...) {
96 va_start(
args, message);
102 log_file(
"%s: %s", label, formatted_message);
104 SAFE_FREE(formatted_message);
113 const char *context_message) {
115 if (g_suppress_error_context) {
137 if (context_message == NULL) {
138 log_error(
"context_message is NULL");
139 const char *fallback =
"No context message (this is invalid - set a context message)";
140 size_t len = strlen(fallback) + 1;
145 log_error(
"SAFE_MALLOC failed for fallback context_message");
148 size_t len = strlen(context_message) + 1;
153 log_error(
"SAFE_MALLOC failed for context_message");
170 const char *format, ...) {
172 va_start(
args, format);
177 if (context_message) {
178 SAFE_FREE(context_message);
192 const char *function,
int sys_errno,
const char *format, ...) {
194 va_start(
args, format);
201 if (context_message) {
202 SAFE_FREE(context_message);
268 const char *format, ...) {
274 asciichat_error_context_t err_ctx;
275 if (HAS_ERRNO(&err_ctx)) {
276 log_labeled(
"\nasciichat_errno: libary code error context", LOG_COLOR_ERROR,
"");
283 log_labeled(
"FATAL ERROR", LOG_COLOR_FATAL,
"exit code %d (%s)", (
int)code, asciichat_error_string(code));
286 log_plain(
" Location: %s:%d in %s()", relative_file, line, function);
291 va_start(
args, format);
293 log_plain(
" Error message: %s", formatted_message);
294 SAFE_FREE(formatted_message);
320 if (!context || context->code == ASCIICHAT_OK) {
324 if (context->file && context->line && context->function) {
328 log_plain(
" Location: unknown (set by system code)");
331 if (context->context_message) {
333 log_file(
" Context: %s", context->context_message);
336 if (context->has_system_error) {
337 log_plain(
" System error: %s (code: %d, meaning: %s)", SAFE_STRERROR(context->system_errno), context->system_errno,
338 SAFE_STRERROR(context->system_errno));
342 if (context->timestamp > 0) {
343 time_t sec = (time_t)(context->timestamp / NS_PER_MS_INT);
344 long usec = (long)(context->timestamp % NS_PER_MS_INT);
348 (void)strftime(time_str,
sizeof(time_str),
"%Y-%m-%d %H:%M:%S", &tm_info);
349 log_plain(
" Timestamp: %s.%06ld", time_str, usec);
354 if (context->stack_depth > 0 && context->backtrace_symbols) {
356 0, 0, skip_backtrace_frame);
366 static_mutex_lock(&g_error_stats_mutex);
368 if (!stats_initialized) {
369 memset(&error_stats, 0,
sizeof(error_stats));
370 stats_initialized =
true;
373 static_mutex_unlock(&g_error_stats_mutex);
377 static_mutex_lock(&g_error_stats_mutex);
379 if (!stats_initialized) {
380 memset(&error_stats, 0,
sizeof(error_stats));
381 stats_initialized =
true;
384 if (code >= 0 && code < 256) {
385 error_stats.error_counts[code]++;
387 error_stats.total_errors++;
389 error_stats.last_error_code = code;
391 static_mutex_unlock(&g_error_stats_mutex);
395 static_mutex_lock(&g_error_stats_mutex);
397 if (!stats_initialized || error_stats.total_errors == 0) {
398 static_mutex_unlock(&g_error_stats_mutex);
399 log_plain(
"No errors recorded.\n");
404 asciichat_error_stats_t local_stats = error_stats;
405 static_mutex_unlock(&g_error_stats_mutex);
407 log_plain(
"\n=== ascii-chat Error Statistics ===\n");
408 log_plain(
"Total errors: %llu\n", (
unsigned long long)local_stats.total_errors);
410 if (local_stats.last_error_time > 0) {
411 time_t sec = (time_t)(local_stats.last_error_time / NS_PER_MS_INT);
415 strftime(time_str,
sizeof(time_str),
"%Y-%m-%d %H:%M:%S", &tm_info);
416 log_plain(
"Last error: %s (code %d)\n", time_str, (
int)local_stats.last_error_code);
420 log_plain(
"\nError breakdown:\n");
421 for (
int i = 0; i < 256; i++) {
422 if (local_stats.error_counts[i] > 0) {
423 log_plain(
" %3d (%s): %llu\n", i, asciichat_error_string((asciichat_error_t)i),
424 (
unsigned long long)local_stats.error_counts[i]);
431 static_mutex_lock(&g_error_stats_mutex);
432 memset(&error_stats, 0,
sizeof(error_stats));
433 static_mutex_unlock(&g_error_stats_mutex);
437 static_mutex_lock(&g_error_stats_mutex);
439 if (!stats_initialized) {
440 memset(&error_stats, 0,
sizeof(error_stats));
441 stats_initialized =
true;
444 asciichat_error_stats_t result = error_stats;
445 static_mutex_unlock(&g_error_stats_mutex);
458 return thread_errors[i].error_code;
472 if (!thread_errors[i].
valid && slot == -1) {
478 thread_errors[slot].thread_id =
thread_id;
479 thread_errors[slot].error_code = code;
480 thread_errors[slot].valid =
true;
487 thread_errors[i].valid =
false;
499 g_suppress_error_context = suppress;
518 g_suppress_error_context =
true;
asciichat_error_t error_code
void asciichat_set_errno(asciichat_error_t code, const char *file, int line, const char *function, const char *context_message)
bool asciichat_has_wsa_error(void)
#define MAX_THREAD_ERRORS
void asciichat_clear_thread_error(int thread_id)
void asciichat_fatal_with_context(asciichat_error_t code, const char *file, int line, const char *function, const char *format,...)
void asciichat_errno_suppress(bool suppress)
void asciichat_set_errno_with_system_error(asciichat_error_t code, const char *file, int line, const char *function, int sys_errno)
__thread asciichat_error_t asciichat_errno
void asciichat_error_stats_print(void)
bool asciichat_has_errno(asciichat_error_context_t *context)
void asciichat_clear_errno(void)
asciichat_error_t asciichat_get_thread_error(int thread_id)
asciichat_error_t asciichat_get_errno(void)
void asciichat_set_errno_with_wsa_error(asciichat_error_t code, const char *file, int line, const char *function, int wsa_error)
asciichat_error_stats_t asciichat_error_stats_get(void)
void asciichat_error_stats_init(void)
void log_labeled(const char *label, log_color_t color, const char *message,...)
void asciichat_set_errno_with_message(asciichat_error_t code, const char *file, int line, const char *function, const char *format,...)
void asciichat_errno_destroy(void)
void asciichat_error_stats_reset(void)
__thread asciichat_error_context_t asciichat_errno_context
void asciichat_set_thread_error(int thread_id, asciichat_error_t code)
void asciichat_print_error_context(const asciichat_error_context_t *context)
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,...)
void asciichat_error_stats_record(asciichat_error_t code)
char * format_message(const char *format, va_list args)
const char * extract_project_relative_path(const char *file)
int safe_fprintf(FILE *stream, const char *format,...)
Safe formatted output to file stream.
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 colored terminal output and plain log file output.
const char * colored_string(log_color_t color, const char *text)
uint64_t time_get_realtime_ns(void)
int platform_backtrace(void **buffer, int size)
void platform_clear_error_state(void)
void platform_backtrace_symbols_destroy(char **symbols)
asciichat_error_t platform_localtime(const time_t *timer, struct tm *result)
char ** platform_backtrace_symbols(void *const *buffer, int size)