blob: acfcba30d8e5993986aa1bc6a0efdf997b12238c [file] [log] [blame]
Harald Welte59b04682009-06-10 05:40:52 +08001/* Debugging/Logging support code */
2/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
3 * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
4 * All Rights Reserved
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 */
21
22#include <stdarg.h>
23#include <stdlib.h>
24#include <stdio.h>
25#include <string.h>
26#include <strings.h>
27#include <time.h>
Harald Welte (local)0bb88162009-12-26 19:45:22 +010028#include <errno.h>
Harald Welte59b04682009-06-10 05:40:52 +080029
30#include <openbsc/debug.h>
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +010031#include <openbsc/talloc.h>
32#include <openbsc/gsm_data.h>
33#include <openbsc/gsm_subscriber.h>
Harald Welte59b04682009-06-10 05:40:52 +080034
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +010035/* default categories */
36static struct debug_category default_categories[Debug_LastEntry] = {
Harald Welte779b65e2009-12-24 13:48:33 +010037 [DRLL] = { .enabled = 1, .loglevel = LOGL_NOTICE },
38 [DCC] = { .enabled = 1, .loglevel = LOGL_NOTICE },
39 [DNM] = { .enabled = 1, .loglevel = LOGL_NOTICE },
40 [DRR] = { .enabled = 1, .loglevel = LOGL_NOTICE },
41 [DRSL] = { .enabled = 1, .loglevel = LOGL_NOTICE },
42 [DMM] = { .enabled = 1, .loglevel = LOGL_INFO },
43 [DMNCC] = { .enabled = 1, .loglevel = LOGL_NOTICE },
44 [DSMS] = { .enabled = 1, .loglevel = LOGL_NOTICE },
45 [DPAG] = { .enabled = 1, .loglevel = LOGL_NOTICE },
46 [DMEAS] = { .enabled = 0, .loglevel = LOGL_NOTICE },
47 [DMI] = { .enabled = 0, .loglevel = LOGL_NOTICE },
48 [DMIB] = { .enabled = 0, .loglevel = LOGL_NOTICE },
49 [DMUX] = { .enabled = 1, .loglevel = LOGL_NOTICE },
50 [DINP] = { .enabled = 1, .loglevel = LOGL_NOTICE },
51 [DSCCP] = { .enabled = 1, .loglevel = LOGL_NOTICE },
52 [DMSC] = { .enabled = 1, .loglevel = LOGL_NOTICE },
53 [DMGCP] = { .enabled = 1, .loglevel = LOGL_NOTICE },
54 [DHO] = { .enabled = 1, .loglevel = LOGL_NOTICE },
55 [DDB] = { .enabled = 1, .loglevel = LOGL_NOTICE },
56 [DREF] = { .enabled = 0, .loglevel = LOGL_NOTICE },
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +010057};
Harald Welte59b04682009-06-10 05:40:52 +080058
59struct debug_info {
60 const char *name;
61 const char *color;
62 const char *description;
63 int number;
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +010064 int position;
Harald Welte59b04682009-06-10 05:40:52 +080065};
66
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +010067struct debug_context {
68 struct gsm_lchan *lchan;
69 struct gsm_subscriber *subscr;
70 struct gsm_bts *bts;
71};
72
73static struct debug_context debug_context;
74static void *tall_dbg_ctx = NULL;
75static LLIST_HEAD(target_list);
76
Harald Welte59b04682009-06-10 05:40:52 +080077#define DEBUG_CATEGORY(NUMBER, NAME, COLOR, DESCRIPTION) \
78 { .name = NAME, .color = COLOR, .description = DESCRIPTION, .number = NUMBER },
79
Harald Welte59b04682009-06-10 05:40:52 +080080static const struct debug_info debug_info[] = {
81 DEBUG_CATEGORY(DRLL, "DRLL", "\033[1;31m", "")
82 DEBUG_CATEGORY(DCC, "DCC", "\033[1;32m", "")
83 DEBUG_CATEGORY(DMM, "DMM", "\033[1;33m", "")
84 DEBUG_CATEGORY(DRR, "DRR", "\033[1;34m", "")
85 DEBUG_CATEGORY(DRSL, "DRSL", "\033[1;35m", "")
86 DEBUG_CATEGORY(DNM, "DNM", "\033[1;36m", "")
87 DEBUG_CATEGORY(DSMS, "DSMS", "\033[1;37m", "")
88 DEBUG_CATEGORY(DPAG, "DPAG", "\033[1;38m", "")
89 DEBUG_CATEGORY(DMNCC, "DMNCC","\033[1;39m", "")
90 DEBUG_CATEGORY(DINP, "DINP", "", "")
91 DEBUG_CATEGORY(DMI, "DMI", "", "")
92 DEBUG_CATEGORY(DMIB, "DMIB", "", "")
93 DEBUG_CATEGORY(DMUX, "DMUX", "", "")
Harald Welte02993682009-06-27 02:53:10 +020094 DEBUG_CATEGORY(DMEAS, "DMEAS", "", "")
Holger Hans Peter Freytherb9cf7812009-08-01 16:54:45 +020095 DEBUG_CATEGORY(DSCCP, "DSCCP", "", "")
Holger Hans Peter Freytheracf289e2009-08-18 12:54:50 +020096 DEBUG_CATEGORY(DMSC, "DMSC", "", "")
Holger Hans Peter Freyther66f78e62009-11-20 13:05:48 +010097 DEBUG_CATEGORY(DMGCP, "DMGCP", "", "")
Harald Welteb90d7bd2009-12-17 00:31:10 +010098 DEBUG_CATEGORY(DHO, "DHO", "", "")
Harald Welte273fdfd2009-12-24 11:39:14 +010099 DEBUG_CATEGORY(DDB, "DDB", "", "")
Harald Welte57fde552009-12-24 11:46:44 +0100100 DEBUG_CATEGORY(DDB, "DREF", "", "")
Harald Welte59b04682009-06-10 05:40:52 +0800101};
102
Harald Welte (local)0bb88162009-12-26 19:45:22 +0100103static const struct value_string loglevel_strs[] = {
104 { 0, "EVERYTHING" },
105 { 1, "DEBUG" },
106 { 3, "INFO" },
107 { 5, "NOTICE" },
108 { 7, "ERROR" },
109 { 8, "FATAL" },
110 { 0, NULL },
111};
112
113int debug_parse_level(const char *lvl)
114{
115 return get_string_value(loglevel_strs, lvl);
116}
117
118int debug_parse_category(const char *category)
119{
120 int i;
121
122 for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
123 if (!strcasecmp(debug_info[i].name+1, category))
124 return debug_info[i].number;
125 }
126
127 return -EINVAL;
128}
129
Harald Welte59b04682009-06-10 05:40:52 +0800130/*
131 * Parse the category mask.
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100132 * The format can be this: category1:category2:category3
133 * or category1,2:category2,3:...
Harald Welte59b04682009-06-10 05:40:52 +0800134 */
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100135void debug_parse_category_mask(struct debug_target* target, const char *_mask)
Harald Welte59b04682009-06-10 05:40:52 +0800136{
Harald Welte59b04682009-06-10 05:40:52 +0800137 int i = 0;
138 char *mask = strdup(_mask);
139 char *category_token = NULL;
140
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100141 /* Disable everything to enable it afterwards */
142 for (i = 0; i < ARRAY_SIZE(target->categories); ++i)
143 target->categories[i].enabled = 0;
144
Harald Welte59b04682009-06-10 05:40:52 +0800145 category_token = strtok(mask, ":");
146 do {
147 for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100148 char* colon = strstr(category_token, ",");
149 int length = strlen(category_token);
150
151 if (colon)
152 length = colon - category_token;
153
154 if (strncasecmp(debug_info[i].name, category_token, length) == 0) {
155 int number = debug_info[i].number;
156 int level = 0;
157
158 if (colon)
159 level = atoi(colon+1);
160
161 target->categories[number].enabled = 1;
162 target->categories[number].loglevel = level;
163 }
Harald Welte59b04682009-06-10 05:40:52 +0800164 }
165 } while ((category_token = strtok(NULL, ":")));
166
Harald Welte59b04682009-06-10 05:40:52 +0800167 free(mask);
Harald Welte59b04682009-06-10 05:40:52 +0800168}
169
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100170static const char* color(int subsys)
Harald Welte59b04682009-06-10 05:40:52 +0800171{
172 int i = 0;
173
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100174 for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
Harald Welte59b04682009-06-10 05:40:52 +0800175 if (debug_info[i].number == subsys)
176 return debug_info[i].color;
177 }
178
179 return "";
180}
181
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100182static void _output(struct debug_target *target, unsigned int subsys, char *file, int line,
183 int cont, const char *format, va_list ap)
Harald Welte59b04682009-06-10 05:40:52 +0800184{
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100185 char col[30];
186 char sub[30];
187 char tim[30];
188 char buf[4096];
189 char final[4096];
Harald Welte59b04682009-06-10 05:40:52 +0800190
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100191 /* prepare the data */
192 col[0] = '\0';
193 sub[0] = '\0';
194 tim[0] = '\0';
195 buf[0] = '\0';
Harald Welte59b04682009-06-10 05:40:52 +0800196
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100197 /* are we using color */
Harald Welte0bbd4802009-12-24 11:11:54 +0100198 if (target->use_color) {
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100199 snprintf(col, sizeof(col), "%s", color(subsys));
Harald Welte0bbd4802009-12-24 11:11:54 +0100200 col[sizeof(col)-1] = '\0';
201 }
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100202 vsnprintf(buf, sizeof(buf), format, ap);
Harald Welte0bbd4802009-12-24 11:11:54 +0100203 buf[sizeof(buf)-1] = '\0';
Harald Welte59b04682009-06-10 05:40:52 +0800204
205 if (!cont) {
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100206 if (target->print_timestamp) {
Harald Welte59b04682009-06-10 05:40:52 +0800207 char *timestr;
208 time_t tm;
209 tm = time(NULL);
210 timestr = ctime(&tm);
211 timestr[strlen(timestr)-1] = '\0';
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100212 snprintf(tim, sizeof(tim), "%s ", timestr);
Harald Welte0bbd4802009-12-24 11:11:54 +0100213 tim[sizeof(tim)-1] = '\0';
Harald Welte59b04682009-06-10 05:40:52 +0800214 }
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100215 snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
Harald Welte0bbd4802009-12-24 11:11:54 +0100216 sub[sizeof(sub)-1] = '\0';
Harald Welte59b04682009-06-10 05:40:52 +0800217 }
Harald Welte59b04682009-06-10 05:40:52 +0800218
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100219 snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf);
Harald Welte0bbd4802009-12-24 11:11:54 +0100220 final[sizeof(final)-1] = '\0';
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100221 target->output(target, final);
222}
223
224
225static void _debugp(unsigned int subsys, int level, char *file, int line,
226 int cont, const char *format, va_list ap)
227{
228 struct debug_target *tar;
229
230 llist_for_each_entry(tar, &target_list, entry) {
231 struct debug_category *category;
232 int output = 0;
233
234 category = &tar->categories[subsys];
235 /* subsystem is not supposed to be debugged */
236 if (!category->enabled)
237 continue;
238
239 /* Check the global log level */
240 if (tar->loglevel != 0 && level < tar->loglevel)
241 continue;
242
243 /* Check the category log level */
244 if (category->loglevel != 0 && level < category->loglevel)
245 continue;
246
247 /*
248 * Apply filters here... if that becomes messy we will need to put
249 * filters in a list and each filter will say stop, continue, output
250 */
251 if ((tar->filter_map & DEBUG_FILTER_ALL) != 0) {
252 output = 1;
253 } else if ((tar->filter_map & DEBUG_FILTER_IMSI) != 0
254 && debug_context.subscr && strcmp(debug_context.subscr->imsi, tar->imsi_filter) == 0) {
255 output = 1;
256 }
257
Harald Welte82ca5792009-12-24 11:12:11 +0100258 if (output) {
259 /* FIXME: copying the va_list is an ugly workaround against a bug
260 * hidden somewhere in _output. If we do not copy here, the first
261 * call to _output() will corrupt the va_list contents, and any
262 * further _output() calls with the same va_list will segfault */
263 va_list bp;
264 va_copy(bp, ap);
265 _output(tar, subsys, file, line, cont, format, bp);
Harald Welte4ddb7c52009-12-24 11:14:03 +0100266 va_end(bp);
Harald Welte82ca5792009-12-24 11:12:11 +0100267 }
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100268 }
269}
270
271void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...)
272{
273 va_list ap;
274
275 va_start(ap, format);
276 _debugp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
Harald Welte59b04682009-06-10 05:40:52 +0800277 va_end(ap);
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100278}
Harald Welte59b04682009-06-10 05:40:52 +0800279
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100280void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
281{
282 va_list ap;
283
284 va_start(ap, format);
285 _debugp(subsys, level, file, line, cont, format, ap);
286 va_end(ap);
Harald Welte59b04682009-06-10 05:40:52 +0800287}
288
289static char hexd_buff[4096];
290
Holger Hans Peter Freyther26d15d12009-08-20 13:16:26 +0200291char *hexdump(const unsigned char *buf, int len)
Harald Welte59b04682009-06-10 05:40:52 +0800292{
293 int i;
294 char *cur = hexd_buff;
295
296 hexd_buff[0] = 0;
297 for (i = 0; i < len; i++) {
298 int len_remain = sizeof(hexd_buff) - (cur - hexd_buff);
299 int rc = snprintf(cur, len_remain, "%02x ", buf[i]);
300 if (rc <= 0)
301 break;
302 cur += rc;
303 }
304 hexd_buff[sizeof(hexd_buff)-1] = 0;
305 return hexd_buff;
306}
307
Holger Hans Peter Freytherc8d862e2009-12-22 22:32:51 +0100308
309
310void debug_add_target(struct debug_target *target)
311{
312 llist_add_tail(&target->entry, &target_list);
313}
314
315void debug_del_target(struct debug_target *target)
316{
317 llist_del(&target->entry);
318}
319
320void debug_reset_context(void)
321{
322 memset(&debug_context, 0, sizeof(debug_context));
323}
324
325/* currently we are not reffing these */
326void debug_set_context(int ctx, void *value)
327{
328 switch (ctx) {
329 case BSC_CTX_LCHAN:
330 debug_context.lchan = (struct gsm_lchan *) value;
331 break;
332 case BSC_CTX_SUBSCR:
333 debug_context.subscr = (struct gsm_subscriber *) value;
334 break;
335 case BSC_CTX_BTS:
336 debug_context.bts = (struct gsm_bts *) value;
337 break;
338 case BSC_CTX_SCCP:
339 break;
340 default:
341 break;
342 }
343}
344
345void debug_set_imsi_filter(struct debug_target *target, const char *imsi)
346{
347 if (imsi) {
348 target->filter_map |= DEBUG_FILTER_IMSI;
349 target->imsi_filter = talloc_strdup(target, imsi);
350 } else if (target->imsi_filter) {
351 target->filter_map &= ~DEBUG_FILTER_IMSI;
352 talloc_free(target->imsi_filter);
353 target->imsi_filter = NULL;
354 }
355}
356
357void debug_set_all_filter(struct debug_target *target, int all)
358{
359 if (all)
360 target->filter_map |= DEBUG_FILTER_ALL;
361 else
362 target->filter_map &= ~DEBUG_FILTER_ALL;
363}
364
365void debug_set_use_color(struct debug_target *target, int use_color)
366{
367 target->use_color = use_color;
368}
369
370void debug_set_print_timestamp(struct debug_target *target, int print_timestamp)
371{
372 target->print_timestamp = print_timestamp;
373}
374
375void debug_set_log_level(struct debug_target *target, int log_level)
376{
377 target->loglevel = log_level;
378}
379
380void debug_set_category_filter(struct debug_target *target, int category, int enable, int level)
381{
382 if (category >= Debug_LastEntry)
383 return;
384 target->categories[category].enabled = !!enable;
385 target->categories[category].loglevel = level;
386}
387
388static void _stderr_output(struct debug_target *target, const char *log)
389{
390 fprintf(target->tgt_stdout.out, "%s", log);
391 fflush(target->tgt_stdout.out);
392}
393
394struct debug_target *debug_target_create(void)
395{
396 struct debug_target *target;
397
398 target = talloc_zero(tall_dbg_ctx, struct debug_target);
399 if (!target)
400 return NULL;
401
402 INIT_LLIST_HEAD(&target->entry);
403 memcpy(target->categories, default_categories, sizeof(default_categories));
404 target->use_color = 1;
405 target->print_timestamp = 0;
406 target->loglevel = 0;
407 return target;
408}
409
410struct debug_target *debug_target_create_stderr(void)
411{
412 struct debug_target *target;
413
414 target = debug_target_create();
415 if (!target)
416 return NULL;
417
418 target->tgt_stdout.out = stderr;
419 target->output = _stderr_output;
420 return target;
421}
422
423void debug_init(void)
424{
425 tall_dbg_ctx = talloc_named_const(NULL, 1, "debug");
426}