You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

435 lines
13 KiB

/**
* Debug configuration implementation
* Runtime debug configuration system for flexible debug output control
*/
#include "debug_config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <time.h>
#include <strings.h>
/* Rate limiting */
static size_t output_count = 0;
static time_t last_output_time = 0;
/* ANSI color codes */
static const char* color_red = "\033[31m";
static const char* color_yellow = "\033[33m";
static const char* color_green = "\033[32m";
static const char* color_blue = "\033[34m";
static const char* color_magenta = "\033[35m";
static const char* color_cyan = "\033[36m";
static const char* color_reset = "\033[0m";
/* Get category by name */
static debug_category_t get_category_by_name(const char* name) {
struct {
const char* n;
debug_category_t c;
} map[] = {
{"none", DEBUG_CATEGORY_NONE},
{"uasync", DEBUG_CATEGORY_UASYNC},
{"ll_queue", DEBUG_CATEGORY_LL_QUEUE},
{"connection", DEBUG_CATEGORY_CONNECTION},
{"etcp", DEBUG_CATEGORY_ETCP},
{"crypto", DEBUG_CATEGORY_CRYPTO},
{"memory", DEBUG_CATEGORY_MEMORY},
{"timing", DEBUG_CATEGORY_TIMING},
{"config", DEBUG_CATEGORY_CONFIG},
{"tun", DEBUG_CATEGORY_TUN},
{"routing", DEBUG_CATEGORY_ROUTING},
{"timers", DEBUG_CATEGORY_TIMERS},
{"all", DEBUG_CATEGORY_ALL},
{NULL, 0}
};
for (int i = 0; map[i].n; i++) {
if (strcasecmp(map[i].n, name) == 0) {
return map[i].c;
}
}
return DEBUG_CATEGORY_NONE;
}
/* Get level by name */
static debug_level_t get_level_by_name(const char* name) {
struct {
const char* n;
debug_level_t l;
} map[] = {
{"none", DEBUG_LEVEL_NONE},
{"error", DEBUG_LEVEL_ERROR},
{"warn", DEBUG_LEVEL_WARN},
{"info", DEBUG_LEVEL_INFO},
{"debug", DEBUG_LEVEL_DEBUG},
{"trace", DEBUG_LEVEL_TRACE},
{NULL, 0}
};
for (int i = 0; map[i].n; i++) {
if (strcasecmp(map[i].n, name) == 0) {
return map[i].l;
}
}
return DEBUG_LEVEL_NONE;
}
/* Global debug configuration */
debug_config_t g_debug_config;
FILE* debug_output_file = NULL;
/* Forward declaration for global_config structure */
struct global_config {
char log_file[256];
char debug_level[32];
uint32_t debug_categories;
int enable_timestamp;
int enable_function_names;
int enable_file_lines;
int enable_colors;
};
/* Apply debug settings from configuration if not overridden by CLI */
void debug_apply_config(const struct global_config *config, int cli_override_level, int cli_override_categories) {
if (!config) return;
// Apply log file if specified and not overridden
if (config->log_file[0] != '\0') {
debug_set_output_file(config->log_file);
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Set log file to: %s", config->log_file);
}
// Apply debug level from config if not overridden by CLI
if (!cli_override_level && config->debug_level[0] != '\0') {
debug_level_t level = DEBUG_LEVEL_INFO; // default
if (strcasecmp(config->debug_level, "error") == 0) level = DEBUG_LEVEL_ERROR;
else if (strcasecmp(config->debug_level, "warn") == 0) level = DEBUG_LEVEL_WARN;
else if (strcasecmp(config->debug_level, "info") == 0) level = DEBUG_LEVEL_INFO;
else if (strcasecmp(config->debug_level, "debug") == 0) level = DEBUG_LEVEL_DEBUG;
else if (strcasecmp(config->debug_level, "trace") == 0) level = DEBUG_LEVEL_TRACE;
debug_set_level(level);
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Set debug level from config: %s", config->debug_level);
}
// Apply debug categories from config if not overridden by CLI
if (!cli_override_categories && config->debug_categories != 0) {
debug_set_categories(config->debug_categories);
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Set debug categories from config: 0x%X", config->debug_categories);
}
// Apply output options from config
debug_enable_timestamp(config->enable_timestamp);
debug_enable_function_name(config->enable_function_names);
debug_enable_file_line(config->enable_file_lines);
debug_enable_color(config->enable_colors);
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Applied debug configuration from file");
}
/* Initialize debug system with default settings */
void debug_config_init(void) {
g_debug_config.default_level = DEBUG_LEVEL_ERROR;
g_debug_config.categories = DEBUG_CATEGORY_ALL;
memset(g_debug_config.category_levels, 0, sizeof(g_debug_config.category_levels));
g_debug_config.timestamp_enabled = 1;
g_debug_config.function_name_enabled = 1;
g_debug_config.file_line_enabled = 1;
g_debug_config.color_enabled = 1;
g_debug_config.max_output_per_second = 0;
g_debug_config.output_file = NULL;
if (debug_output_file && debug_output_file != stdout && debug_output_file != stderr) {
fclose(debug_output_file);
}
debug_output_file = stdout;
output_count = 0;
last_output_time = 0;
}
/* Set debug level */
void debug_set_level(debug_level_t level) {
g_debug_config.default_level = level;
}
/* Enable/disable specific categories */
void debug_enable_category(debug_category_t category) {
g_debug_config.categories |= category;
}
void debug_disable_category(debug_category_t category) {
g_debug_config.categories &= ~category;
}
void debug_set_categories(uint32_t categories) {
g_debug_config.categories = categories;
}
/* Configure output options */
void debug_enable_timestamp(int enable) {
g_debug_config.timestamp_enabled = enable;
}
void debug_enable_function_name(int enable) {
g_debug_config.function_name_enabled = enable;
}
void debug_enable_file_line(int enable) {
g_debug_config.file_line_enabled = enable;
}
void debug_enable_color(int enable) {
g_debug_config.color_enabled = enable;
}
void debug_set_rate_limit(size_t max_per_second) {
g_debug_config.max_output_per_second = max_per_second;
}
void debug_set_output_file(const char* file_path) {
if (file_path == NULL) {
if (debug_output_file && debug_output_file != stdout && debug_output_file != stderr) {
fclose(debug_output_file);
}
debug_output_file = stdout;
g_debug_config.output_file = NULL;
} else {
FILE* new_file = fopen(file_path, "a");
if (new_file) {
if (debug_output_file && debug_output_file != stdout && debug_output_file != stderr) {
fclose(debug_output_file);
}
debug_output_file = new_file;
g_debug_config.output_file = file_path;
}
}
}
/* Check if debug output should be shown for given level and category */
int debug_should_output(debug_level_t level, debug_category_t category) {
/* Check if category is enabled */
if (!(g_debug_config.categories & category)) {
return 0;
}
/* Check if level is sufficient */
debug_level_t eff_level = debug_get_effective_level(category);
if (level > eff_level) {
return 0;
}
/* Rate limiting check */
if (g_debug_config.max_output_per_second > 0) {
time_t current_time = time(NULL);
if (current_time != last_output_time) {
output_count = 0;
last_output_time = current_time;
}
if (output_count >= g_debug_config.max_output_per_second) {
return 0;
}
output_count++;
}
return 1;
}
/* Get current debug level for a category */
debug_level_t debug_get_effective_level(debug_category_t category) {
if (category == DEBUG_CATEGORY_ALL || category == DEBUG_CATEGORY_NONE) {
return g_debug_config.default_level;
}
/* Assume single category bit */
uint32_t cat = category;
if (__builtin_popcount(cat) != 1) {
return DEBUG_LEVEL_NONE; /* Not supported for multiple */
}
int index = __builtin_ctz(cat);
if (index >= NUM_DEBUG_CATEGORIES) {
return DEBUG_LEVEL_NONE;
}
debug_level_t lev = g_debug_config.category_levels[index];
return (lev == DEBUG_LEVEL_NONE) ? g_debug_config.default_level : lev;
}
/* Get color for debug level */
static const char* get_level_color(debug_level_t level) {
if (!g_debug_config.color_enabled) {
return "";
}
switch (level) {
case DEBUG_LEVEL_ERROR: return color_red;
case DEBUG_LEVEL_WARN: return color_yellow;
case DEBUG_LEVEL_INFO: return color_green;
case DEBUG_LEVEL_DEBUG: return color_blue;
case DEBUG_LEVEL_TRACE: return color_magenta;
default: return "";
}
}
/* Get level name */
static const char* get_level_name(debug_level_t level) {
switch (level) {
case DEBUG_LEVEL_ERROR: return "ERROR";
case DEBUG_LEVEL_WARN: return "WARN";
case DEBUG_LEVEL_INFO: return "INFO";
case DEBUG_LEVEL_DEBUG: return "DEBUG";
case DEBUG_LEVEL_TRACE: return "TRACE";
default: return "UNKNOWN";
}
}
/* Format and output debug message */
void debug_output(debug_level_t level, debug_category_t category,
const char* function, const char* file, int line,
const char* format, ...) {
#define BUFFER_SIZE 4096
char buffer[BUFFER_SIZE];
int offset = 0;
size_t remaining = BUFFER_SIZE;
va_list args;
va_start(args, format);
FILE* output = debug_output_file ? debug_output_file : stdout;
/* Add timestamp if enabled */
if (g_debug_config.timestamp_enabled) {
time_t now = time(NULL);
struct tm* tm_info = localtime(&now);
char time_str[32];
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);
offset += snprintf(buffer + offset, remaining, "[%s] ", time_str);
remaining = BUFFER_SIZE - offset;
}
/* Add level and color */
const char* color = get_level_color(level);
const char* level_name = get_level_name(level);
if (g_debug_config.color_enabled) {
offset += snprintf(buffer + offset, remaining, "%s[%s]%s ", color, level_name, color_reset);
} else {
offset += snprintf(buffer + offset, remaining, "[%s] ", level_name);
}
remaining = BUFFER_SIZE - offset;
/* Add category */
offset += snprintf(buffer + offset, remaining, "[%d] ", category);
remaining = BUFFER_SIZE - offset;
/* Add function name if enabled */
if (g_debug_config.function_name_enabled && function) {
offset += snprintf(buffer + offset, remaining, "%s() ", function);
remaining = BUFFER_SIZE - offset;
}
/* Add file:line if enabled */
if (g_debug_config.file_line_enabled && file) {
offset += snprintf(buffer + offset, remaining, "(%s:%d) ", file, line);
remaining = BUFFER_SIZE - offset;
}
/* Add the actual message */
offset += vsnprintf(buffer + offset, remaining, format, args);
remaining = BUFFER_SIZE - offset;
/* Add newline */
if (remaining > 1) {
buffer[offset] = '\n';
buffer[offset + 1] = '\0';
} else {
buffer[BUFFER_SIZE - 2] = '\n';
buffer[BUFFER_SIZE - 1] = '\0';
}
va_end(args);
/* Output the entire line at once */
fprintf(output, "%s", buffer);
fflush(output);
#undef BUFFER_SIZE
}
/* Parse debug configuration from string */
int debug_parse_config(const char* config_string) {
if (!config_string || !*config_string) {
return 0;
}
char* str = strdup(config_string);
if (!str) {
return -1;
}
char* token = strtok(str, ",");
while (token) {
/* Trim leading spaces */
while (isspace(*token)) {
token++;
}
char* colon = strchr(token, ':');
if (!colon) {
free(str);
return -1;
}
*colon = '\0';
char* cat_name = token;
char* level_name = colon + 1;
/* Trim leading spaces in level_name */
while (isspace(*level_name)) {
level_name++;
}
/* Find category */
debug_category_t cat = get_category_by_name(cat_name);
if (cat == DEBUG_CATEGORY_NONE) {
free(str);
return -1;
}
/* Find level */
debug_level_t lev = get_level_by_name(level_name);
if (lev == DEBUG_LEVEL_NONE) {
free(str);
return -1;
}
if (cat == DEBUG_CATEGORY_ALL) {
g_debug_config.default_level = lev;
} else {
g_debug_config.categories |= cat;
int index = __builtin_ctz(cat);
if (index < NUM_DEBUG_CATEGORIES) {
g_debug_config.category_levels[index] = lev;
}
}
token = strtok(NULL, ",");
}
free(str);
return 0;
}
/* Apply debug settings from configuration string values */
void debug_apply_config_values(const char *log_file, const char *debug_level_str, uint32_t debug_categories,
int enable_timestamp, int enable_function_names, int enable_file_lines, int enable_colors,
int cli_override_level, int cli_override_categories) {
// This function would be implemented to apply individual config values
// For now, it's a placeholder that could be expanded based on specific needs
}