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.
 
 
 
 
 
 

404 lines
12 KiB

/**
* Debug module
*/
#include "debug_config.h"
#include "platform_compat.h"
#include "mem.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <time.h>
void log_dump(const char* prefix, const uint8_t* data, size_t len) {
char hex_buf[513] = {'\0'};
size_t hex_len = 0;
size_t show_len = (len > 128) ? 128 : len;
for (size_t i = 0; i < show_len && hex_len < 512 - 3; i++) {
hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, "%02x", data[i]);
if (i < show_len - 1 && (i + 1) % 32 == 0) { // Add space every 32 bytes
hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, " ");
}
}
if (len > 128) {
hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, "...");
}
// Single-line debug output with packet info
DEBUG_INFO(DEBUG_CATEGORY_DUMP, "%s: len=%zu hex=%s", prefix, len, hex_buf);
// Additional debug info for first few bytes
// if (len >= 2) {
// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "%s: first_bytes=%02x%02x last_bytes=%02x%02x",
// prefix, data[0], data[1], data[len-2], data[len-1]);
// }
}
ip_str_t ip_to_str(const void *addr, int family) {
ip_str_t result;
result.str[0] = '\0';
if (!addr) {
return result;
}
if (family == AF_INET) {
inet_ntop(AF_INET, addr, result.str, 16);
} else if (family == AF_INET6) {
inet_ntop(AF_INET6, addr, result.str, 48);
}
return result;
}
/* 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},
{"normalizer", DEBUG_CATEGORY_NORMALIZER},
{"bgp", DEBUG_CATEGORY_BGP},
{"socket", DEBUG_CATEGORY_SOCKET},
{"control", DEBUG_CATEGORY_CONTROL},
{"dump", DEBUG_CATEGORY_DUMP},
{"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;
}
/* Global debug configuration */
debug_config_t g_debug_config;
/* Initialize debug system with default settings */
void debug_config_init(void) {
g_debug_config.level = DEBUG_LEVEL_ERROR; // Global level: errors only by default
g_debug_config.categories = DEBUG_CATEGORY_ALL;
for (int i = 0; i < DEBUG_CATEGORY_COUNT; i++) {
g_debug_config.category_levels[i] = DEBUG_LEVEL_NONE; // 0 = disabled per category
}
g_debug_config.timestamp_enabled = 1;
g_debug_config.function_name_enabled = 1;
g_debug_config.file_line_enabled = 1;
g_debug_config.output_file = NULL;
// Dual output defaults
g_debug_config.file_output = NULL; // No file by default
g_debug_config.file_level = DEBUG_LEVEL_TRACE; // File gets everything
g_debug_config.console_level = DEBUG_LEVEL_INFO; // Console gets INFO+
g_debug_config.console_enabled = 1; // Console enabled
}
/* Set debug level */
void debug_set_level(debug_level_t level) {
g_debug_config.level = level;
g_debug_config.console_level = level;
}
/* Set debug level for specific category (0 = disabled, otherwise uses that level) */
void debug_set_category_level(debug_category_t category, debug_level_t level) {
if (category == DEBUG_CATEGORY_NONE) return;
int idx = 0;
while (!(category & (1ULL << idx)) && idx < DEBUG_CATEGORY_COUNT) {
idx++;
}
if (idx < DEBUG_CATEGORY_COUNT) {
g_debug_config.category_levels[idx] = 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(debug_category_t categories) {
g_debug_config.categories = categories;
}
/* Set masks directly */
void debug_set_masks(debug_category_t categories, debug_level_t level) {
g_debug_config.categories = categories;
g_debug_config.level = level;
}
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_set_output_file(const char* file_path) {
if (g_debug_config.file_output) return;// уже установлен, кто первый того и тапки
FILE* new_file = fopen(file_path, "a");
if (new_file) {
g_debug_config.file_output = new_file;
g_debug_config.output_file = file_path;
}
}
/* Set console log level */
void debug_set_console_level(debug_level_t level) {
g_debug_config.console_level = level;
}
/* Set file log level */
void debug_set_file_level(debug_level_t level) {
g_debug_config.file_level = level;
}
/* Enable/disable console output */
void debug_enable_console(int enable) {
g_debug_config.console_enabled = enable;
}
/* Enable file output with optional truncation */
void debug_enable_file_output(const char* file_path, int truncate) {
if (!file_path) return;
// Close existing file if open
if (g_debug_config.file_output) {
fclose(g_debug_config.file_output);
g_debug_config.file_output = NULL;
}
// Open new file
const char* mode = truncate ? "w" : "a";
FILE* new_file = fopen(file_path, mode);
if (new_file) {
g_debug_config.file_output = new_file;
g_debug_config.output_file = file_path;
g_debug_config.console_enabled = 0;
}
}
/* Disable file output */
void debug_disable_file_output(void) {
if (g_debug_config.file_output) {
fclose(g_debug_config.file_output);
g_debug_config.file_output = NULL;
g_debug_config.output_file = NULL;
}
}
/* 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;
}
/* Determine effective level: max(global, category) */
int idx = 0;
while (!(category & (1ULL << idx)) && idx < DEBUG_CATEGORY_COUNT) {
idx++;
}
debug_level_t cat_level = (idx < DEBUG_CATEGORY_COUNT) ? g_debug_config.category_levels[idx] : DEBUG_LEVEL_NONE;
debug_level_t effective = (cat_level > g_debug_config.level) ? cat_level : g_debug_config.level;
/* Check if level is sufficient */
if (level > effective) {
return 0;
}
return 1;
}
/* Get current debug level */
debug_level_t debug_get_level(void) {
return g_debug_config.level;
}
/* 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);
/* Add timestamp with microseconds: hh:mm:ss-xxx.yyy */
struct timeval tv;
utun_gettimeofday(&tv, NULL);
time_t tv_sec = (time_t)tv.tv_sec;
struct tm* tm_info = localtime(&tv_sec);
char time_str[32];
strftime(time_str, sizeof(time_str), "%H:%M:%S", tm_info);
offset += snprintf(buffer + offset, remaining, "[%s-%03ld.%03ld] ", time_str, tv.tv_usec / 1000, tv.tv_usec % 1000);
remaining = BUFFER_SIZE - offset;
/* Add level */
const char* level_name = get_level_name(level);
offset += snprintf(buffer + offset, remaining, "[%s] ", level_name);
remaining = BUFFER_SIZE - offset;
/* Add category */
offset += snprintf(buffer + offset, remaining, "[%llu] ", (unsigned long long)category);
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 function name if enabled */
if (g_debug_config.function_name_enabled && function) {
offset += snprintf(buffer + offset, remaining, "%s() ", function);
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 to console if enabled and level is sufficient */
if (g_debug_config.console_enabled && level <= g_debug_config.console_level) {
fprintf(stdout, "%s", buffer);
fflush(stdout);
}
/* Output to file if open and level is sufficient */
if (g_debug_config.file_output && level <= g_debug_config.file_level) {
fprintf(g_debug_config.file_output, "%s", buffer);
fflush(g_debug_config.file_output);
}
#undef BUFFER_SIZE
}
/* Parse debug configuration from string (e.g., "ll_queue:debug,uasync:info,memory:warn,etcp:error") */
int debug_parse_config(const char* config_string) {
if (!config_string || !*config_string) {
return 0;
}
char* str = u_strdup(config_string);
if (!str) {
return -1;
}
debug_category_t categories = 0;
debug_level_t level = DEBUG_LEVEL_NONE;
char* token = strtok(str, ",");
while (token) {
/* Trim leading spaces */
while (isspace(*token)) {
token++;
}
char* colon = strchr(token, ':');
if (!colon) {
u_free(str);
return -1;
}
*colon = '\0';
char* category_name = token;
char* level_name = colon + 1;
/* Trim leading spaces in level_name */
while (isspace(*level_name)) {
level_name++;
}
debug_category_t cat = get_category_by_name(category_name);
if (cat == DEBUG_CATEGORY_NONE) {
u_free(str);
return -1;
}
debug_level_t lev = DEBUG_LEVEL_NONE;
if (strcasecmp(level_name, "none") == 0) {
lev = DEBUG_LEVEL_NONE;
} else if (strcasecmp(level_name, "error") == 0) {
lev = DEBUG_LEVEL_ERROR;
} else if (strcasecmp(level_name, "warn") == 0) {
lev = DEBUG_LEVEL_WARN;
} else if (strcasecmp(level_name, "info") == 0) {
lev = DEBUG_LEVEL_INFO;
} else if (strcasecmp(level_name, "debug") == 0) {
lev = DEBUG_LEVEL_DEBUG;
} else if (strcasecmp(level_name, "trace") == 0) {
lev = DEBUG_LEVEL_TRACE;
} else {
u_free(str);
return -1;
}
/* Set per-category level */
int idx = 0;
while (!(cat & (1ULL << idx)) && idx < DEBUG_CATEGORY_COUNT) {
idx++;
}
if (idx < DEBUG_CATEGORY_COUNT) {
g_debug_config.category_levels[idx] = lev;
}
token = strtok(NULL, ",");
}
u_free(str);
return 0;
}