diff options
author | Holger Hans Peter Freyther <zecke@selfish.org> | 2009-12-22 22:32:51 +0100 |
---|---|---|
committer | Harald Welte <laforge@netfilter.org> | 2009-12-22 22:32:51 +0100 |
commit | b61e3b21580afc6381a6c72618d51697c7ce9771 (patch) | |
tree | 2a88eb434a28d4064f7017fd1202f10e88f341f7 /openbsc/src/debug.c | |
parent | 0f9141384bc338cdf8db805fbfe502aed622f816 (diff) |
Import the new logging architecture
This is the new logging architecture, including
* support for multiuple logging targets like stderr and vty
* log levels in addition to categories/subsystems
* filtering based on imsi, i.e. only see events for one subscriber
* dynamically change log level for each category for each vty
Diffstat (limited to 'openbsc/src/debug.c')
-rw-r--r-- | openbsc/src/debug.c | 296 |
1 files changed, 255 insertions, 41 deletions
diff --git a/openbsc/src/debug.c b/openbsc/src/debug.c index 6e99d4144..d3d58f3fb 100644 --- a/openbsc/src/debug.c +++ b/openbsc/src/debug.c @@ -27,21 +27,53 @@ #include <time.h> #include <openbsc/debug.h> +#include <openbsc/talloc.h> +#include <openbsc/gsm_data.h> +#include <openbsc/gsm_subscriber.h> -unsigned int debug_mask = 0xffffffff & ~(DMI|DMIB|DMEAS); +/* default categories */ +static struct debug_category default_categories[Debug_LastEntry] = { + [DRLL] = { .enabled = 1, .loglevel = 0}, + [DCC] = { .enabled = 1, .loglevel = 0}, + [DMM] = { .enabled = 1, .loglevel = 0}, + [DRR] = { .enabled = 1, .loglevel = 0}, + [DRSL] = { .enabled = 1, .loglevel = 0}, + [DMM] = { .enabled = 1, .loglevel = 0}, + [DMNCC] = { .enabled = 1, .loglevel = 0}, + [DSMS] = { .enabled = 1, .loglevel = 0}, + [DPAG] = { .enabled = 1, .loglevel = 0}, + [DMEAS] = { .enabled = 0, .loglevel = 0}, + [DMI] = { .enabled = 0, .loglevel = 0}, + [DMIB] = { .enabled = 0, .loglevel = 0}, + [DMUX] = { .enabled = 1, .loglevel = 0}, + [DINP] = { .enabled = 1, .loglevel = 0}, + [DSCCP] = { .enabled = 1, .loglevel = 0}, + [DMSC] = { .enabled = 1, .loglevel = 0}, + [DMGCP] = { .enabled = 1, .loglevel = 0}, + [DHO] = { .enabled = 1, .loglevel = 0}, +}; struct debug_info { const char *name; const char *color; const char *description; int number; + int position; +}; + +struct debug_context { + struct gsm_lchan *lchan; + struct gsm_subscriber *subscr; + struct gsm_bts *bts; }; +static struct debug_context debug_context; +static void *tall_dbg_ctx = NULL; +static LLIST_HEAD(target_list); + #define DEBUG_CATEGORY(NUMBER, NAME, COLOR, DESCRIPTION) \ { .name = NAME, .color = COLOR, .description = DESCRIPTION, .number = NUMBER }, -#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) - static const struct debug_info debug_info[] = { DEBUG_CATEGORY(DRLL, "DRLL", "\033[1;31m", "") DEBUG_CATEGORY(DCC, "DCC", "\033[1;32m", "") @@ -63,50 +95,51 @@ static const struct debug_info debug_info[] = { DEBUG_CATEGORY(DHO, "DHO", "", "") }; -static int use_color = 1; - -void debug_use_color(int color) -{ - use_color = color; -} - -static int print_timestamp = 0; - -void debug_timestamp(int enable) -{ - print_timestamp = enable; -} - - /* * Parse the category mask. - * category1:category2:category3 + * The format can be this: category1:category2:category3 + * or category1,2:category2,3:... */ -void debug_parse_category_mask(const char *_mask) +void debug_parse_category_mask(struct debug_target* target, const char *_mask) { - unsigned int new_mask = 0; int i = 0; char *mask = strdup(_mask); char *category_token = NULL; + /* Disable everything to enable it afterwards */ + for (i = 0; i < ARRAY_SIZE(target->categories); ++i) + target->categories[i].enabled = 0; + category_token = strtok(mask, ":"); do { for (i = 0; i < ARRAY_SIZE(debug_info); ++i) { - if (strcasecmp(debug_info[i].name, category_token) == 0) - new_mask |= debug_info[i].number; + char* colon = strstr(category_token, ","); + int length = strlen(category_token); + + if (colon) + length = colon - category_token; + + if (strncasecmp(debug_info[i].name, category_token, length) == 0) { + int number = debug_info[i].number; + int level = 0; + + if (colon) + level = atoi(colon+1); + + target->categories[number].enabled = 1; + target->categories[number].loglevel = level; + } } } while ((category_token = strtok(NULL, ":"))); - free(mask); - debug_mask = new_mask; } -const char* color(int subsys) +static const char* color(int subsys) { int i = 0; - for (i = 0; use_color && i < ARRAY_SIZE(debug_info); ++i) { + for (i = 0; i < ARRAY_SIZE(debug_info); ++i) { if (debug_info[i].number == subsys) return debug_info[i].color; } @@ -114,35 +147,97 @@ const char* color(int subsys) return ""; } -void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) +static void _output(struct debug_target *target, unsigned int subsys, char *file, int line, + int cont, const char *format, va_list ap) { - va_list ap; - FILE *outfd = stderr; + char col[30]; + char sub[30]; + char tim[30]; + char buf[4096]; + char final[4096]; - if (!(debug_mask & subsys)) - return; - - va_start(ap, format); + /* prepare the data */ + col[0] = '\0'; + sub[0] = '\0'; + tim[0] = '\0'; + buf[0] = '\0'; - fprintf(outfd, "%s", color(subsys)); + /* are we using color */ + if (target->use_color) + snprintf(col, sizeof(col), "%s", color(subsys)); + vsnprintf(buf, sizeof(buf), format, ap); if (!cont) { - if (print_timestamp) { + if (target->print_timestamp) { char *timestr; time_t tm; tm = time(NULL); timestr = ctime(&tm); timestr[strlen(timestr)-1] = '\0'; - fprintf(outfd, "%s ", timestr); + snprintf(tim, sizeof(tim), "%s ", timestr); } - fprintf(outfd, "<%4.4x> %s:%d ", subsys, file, line); + snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line); } - vfprintf(outfd, format, ap); - fprintf(outfd, "\033[0;m"); + snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf); + target->output(target, final); +} + + +static void _debugp(unsigned int subsys, int level, char *file, int line, + int cont, const char *format, va_list ap) +{ + struct debug_target *tar; + + llist_for_each_entry(tar, &target_list, entry) { + struct debug_category *category; + int output = 0; + + category = &tar->categories[subsys]; + /* subsystem is not supposed to be debugged */ + if (!category->enabled) + continue; + + /* Check the global log level */ + if (tar->loglevel != 0 && level < tar->loglevel) + continue; + + /* Check the category log level */ + if (category->loglevel != 0 && level < category->loglevel) + continue; + + /* + * Apply filters here... if that becomes messy we will need to put + * filters in a list and each filter will say stop, continue, output + */ + if ((tar->filter_map & DEBUG_FILTER_ALL) != 0) { + output = 1; + } else if ((tar->filter_map & DEBUG_FILTER_IMSI) != 0 + && debug_context.subscr && strcmp(debug_context.subscr->imsi, tar->imsi_filter) == 0) { + output = 1; + } + + if (output) + _output(tar, subsys, file, line, cont, format, ap); + } +} + +void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + _debugp(subsys, LOGL_DEBUG, file, line, cont, format, ap); va_end(ap); +} + +void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...) +{ + va_list ap; - fflush(outfd); + va_start(ap, format); + _debugp(subsys, level, file, line, cont, format, ap); + va_end(ap); } static char hexd_buff[4096]; @@ -164,3 +259,122 @@ char *hexdump(const unsigned char *buf, int len) return hexd_buff; } + + +void debug_add_target(struct debug_target *target) +{ + llist_add_tail(&target->entry, &target_list); +} + +void debug_del_target(struct debug_target *target) +{ + llist_del(&target->entry); +} + +void debug_reset_context(void) +{ + memset(&debug_context, 0, sizeof(debug_context)); +} + +/* currently we are not reffing these */ +void debug_set_context(int ctx, void *value) +{ + switch (ctx) { + case BSC_CTX_LCHAN: + debug_context.lchan = (struct gsm_lchan *) value; + break; + case BSC_CTX_SUBSCR: + debug_context.subscr = (struct gsm_subscriber *) value; + break; + case BSC_CTX_BTS: + debug_context.bts = (struct gsm_bts *) value; + break; + case BSC_CTX_SCCP: + break; + default: + break; + } +} + +void debug_set_imsi_filter(struct debug_target *target, const char *imsi) +{ + if (imsi) { + target->filter_map |= DEBUG_FILTER_IMSI; + target->imsi_filter = talloc_strdup(target, imsi); + } else if (target->imsi_filter) { + target->filter_map &= ~DEBUG_FILTER_IMSI; + talloc_free(target->imsi_filter); + target->imsi_filter = NULL; + } +} + +void debug_set_all_filter(struct debug_target *target, int all) +{ + if (all) + target->filter_map |= DEBUG_FILTER_ALL; + else + target->filter_map &= ~DEBUG_FILTER_ALL; +} + +void debug_set_use_color(struct debug_target *target, int use_color) +{ + target->use_color = use_color; +} + +void debug_set_print_timestamp(struct debug_target *target, int print_timestamp) +{ + target->print_timestamp = print_timestamp; +} + +void debug_set_log_level(struct debug_target *target, int log_level) +{ + target->loglevel = log_level; +} + +void debug_set_category_filter(struct debug_target *target, int category, int enable, int level) +{ + if (category >= Debug_LastEntry) + return; + target->categories[category].enabled = !!enable; + target->categories[category].loglevel = level; +} + +static void _stderr_output(struct debug_target *target, const char *log) +{ + fprintf(target->tgt_stdout.out, "%s", log); + fflush(target->tgt_stdout.out); +} + +struct debug_target *debug_target_create(void) +{ + struct debug_target *target; + + target = talloc_zero(tall_dbg_ctx, struct debug_target); + if (!target) + return NULL; + + INIT_LLIST_HEAD(&target->entry); + memcpy(target->categories, default_categories, sizeof(default_categories)); + target->use_color = 1; + target->print_timestamp = 0; + target->loglevel = 0; + return target; +} + +struct debug_target *debug_target_create_stderr(void) +{ + struct debug_target *target; + + target = debug_target_create(); + if (!target) + return NULL; + + target->tgt_stdout.out = stderr; + target->output = _stderr_output; + return target; +} + +void debug_init(void) +{ + tall_dbg_ctx = talloc_named_const(NULL, 1, "debug"); +} |