blob: acfcba30d8e5993986aa1bc6a0efdf997b12238c [file] [log] [blame]
Holger Freyther32636e82008-12-27 11:07:15 +00001/* Debugging/Logging support code */
2/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
Holger Freytherd546e312008-12-27 12:03:07 +00003 * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
Holger Freyther32636e82008-12-27 11:07:15 +00004 * 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>
Holger Freytherd546e312008-12-27 12:03:07 +000023#include <stdlib.h>
Holger Freyther32636e82008-12-27 11:07:15 +000024#include <stdio.h>
25#include <string.h>
Holger Freytherd546e312008-12-27 12:03:07 +000026#include <strings.h>
Holger Freyther32636e82008-12-27 11:07:15 +000027#include <time.h>
Harald Welte (local)b79bdd92009-12-26 19:45:22 +010028#include <errno.h>
Holger Freyther32636e82008-12-27 11:07:15 +000029
30#include <openbsc/debug.h>
Holger Hans Peter Freytherb61e3b22009-12-22 22:32:51 +010031#include <openbsc/talloc.h>
32#include <openbsc/gsm_data.h>
33#include <openbsc/gsm_subscriber.h>
Holger Freyther32636e82008-12-27 11:07:15 +000034
Holger Hans Peter Freytherb61e3b22009-12-22 22:32:51 +010035/* default categories */
36static struct debug_category default_categories[Debug_LastEntry] = {
Harald Welte84105972009-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 Freytherb61e3b22009-12-22 22:32:51 +010057};
Holger Freyther32636e82008-12-27 11:07:15 +000058
Holger Freytherd546e312008-12-27 12:03:07 +000059struct debug_info {
60 const char *name;
61 const char *color;
62 const char *description;
63 int number;
Holger Hans Peter Freytherb61e3b22009-12-22 22:32:51 +010064 int position;
Holger Freytherd546e312008-12-27 12:03:07 +000065};
66
Holger Hans Peter Freytherb61e3b22009-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
Holger Freytherd546e312008-12-27 12:03:07 +000077#define DEBUG_CATEGORY(NUMBER, NAME, COLOR, DESCRIPTION) \
78 { .name = NAME, .color = COLOR, .description = DESCRIPTION, .number = NUMBER },
79
Holger Freytherd546e312008-12-27 12:03:07 +000080static const struct debug_info debug_info[] = {
Holger Freyther7c03e4c2008-12-27 12:46:48 +000081 DEBUG_CATEGORY(DRLL, "DRLL", "\033[1;31m", "")
82 DEBUG_CATEGORY(DCC, "DCC", "\033[1;32m", "")
Holger Freyther3770b762009-06-02 02:35:12 +000083 DEBUG_CATEGORY(DMM, "DMM", "\033[1;33m", "")
Holger Freyther7c03e4c2008-12-27 12:46:48 +000084 DEBUG_CATEGORY(DRR, "DRR", "\033[1;34m", "")
Harald Weltec1d2aae2009-05-23 06:43:35 +000085 DEBUG_CATEGORY(DRSL, "DRSL", "\033[1;35m", "")
Holger Freyther7c03e4c2008-12-27 12:46:48 +000086 DEBUG_CATEGORY(DNM, "DNM", "\033[1;36m", "")
Daniel Willmann10d06f62008-12-28 21:38:25 +000087 DEBUG_CATEGORY(DSMS, "DSMS", "\033[1;37m", "")
Harald Welted35b6a72008-12-29 04:06:41 +000088 DEBUG_CATEGORY(DPAG, "DPAG", "\033[1;38m", "")
Harald Weltec125a682009-05-23 06:42:38 +000089 DEBUG_CATEGORY(DMNCC, "DMNCC","\033[1;39m", "")
Harald Welteedb37782009-05-01 14:59:07 +000090 DEBUG_CATEGORY(DINP, "DINP", "", "")
Harald Welteb60fa592009-02-06 12:02:53 +000091 DEBUG_CATEGORY(DMI, "DMI", "", "")
92 DEBUG_CATEGORY(DMIB, "DMIB", "", "")
Harald Welteba59baf2009-02-23 00:04:04 +000093 DEBUG_CATEGORY(DMUX, "DMUX", "", "")
Harald Welte10d0e672009-06-27 02:53:10 +020094 DEBUG_CATEGORY(DMEAS, "DMEAS", "", "")
Holger Hans Peter Freythered0a47b2009-08-01 16:54:45 +020095 DEBUG_CATEGORY(DSCCP, "DSCCP", "", "")
Holger Hans Peter Freyther32201c52009-08-18 12:54:50 +020096 DEBUG_CATEGORY(DMSC, "DMSC", "", "")
Holger Hans Peter Freytherff5fa4e2009-11-20 13:05:48 +010097 DEBUG_CATEGORY(DMGCP, "DMGCP", "", "")
Harald Welte8d77b952009-12-17 00:31:10 +010098 DEBUG_CATEGORY(DHO, "DHO", "", "")
Harald Welteae1f1592009-12-24 11:39:14 +010099 DEBUG_CATEGORY(DDB, "DDB", "", "")
Harald Welted0c19142009-12-24 11:46:44 +0100100 DEBUG_CATEGORY(DDB, "DREF", "", "")
Holger Freytherd546e312008-12-27 12:03:07 +0000101};
102
Harald Welte (local)b79bdd92009-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
Holger Freytherd546e312008-12-27 12:03:07 +0000130/*
131 * Parse the category mask.
Holger Hans Peter Freytherb61e3b22009-12-22 22:32:51 +0100132 * The format can be this: category1:category2:category3
133 * or category1,2:category2,3:...
Holger Freytherd546e312008-12-27 12:03:07 +0000134 */
Holger Hans Peter Freytherb61e3b22009-12-22 22:32:51 +0100135void debug_parse_category_mask(struct debug_target* target, const char *_mask)
Holger Freytherd546e312008-12-27 12:03:07 +0000136{
Holger Freytherd546e312008-12-27 12:03:07 +0000137 int i = 0;
138 char *mask = strdup(_mask);
139 char *category_token = NULL;
140
Holger Hans Peter Freytherb61e3b22009-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
Holger Freytherd546e312008-12-27 12:03:07 +0000145 category_token = strtok(mask, ":");
146 do {
147 for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
Holger Hans Peter Freytherb61e3b22009-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 }
Holger Freytherd546e312008-12-27 12:03:07 +0000164 }
Holger Freytherca362a62009-01-04 21:05:01 +0000165 } while ((category_token = strtok(NULL, ":")));
Holger Freytherd546e312008-12-27 12:03:07 +0000166
Holger Freytherd546e312008-12-27 12:03:07 +0000167 free(mask);
Holger Freytherd546e312008-12-27 12:03:07 +0000168}
169
Holger Hans Peter Freytherb61e3b22009-12-22 22:32:51 +0100170static const char* color(int subsys)
Holger Freyther7c03e4c2008-12-27 12:46:48 +0000171{
172 int i = 0;
173
Holger Hans Peter Freytherb61e3b22009-12-22 22:32:51 +0100174 for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
Holger Freyther7c03e4c2008-12-27 12:46:48 +0000175 if (debug_info[i].number == subsys)
176 return debug_info[i].color;
177 }
178
179 return "";
180}
181
Holger Hans Peter Freytherb61e3b22009-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)
Holger Freyther32636e82008-12-27 11:07:15 +0000184{
Holger Hans Peter Freytherb61e3b22009-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];
Holger Freyther32636e82008-12-27 11:07:15 +0000190
Holger Hans Peter Freytherb61e3b22009-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';
Holger Freyther32636e82008-12-27 11:07:15 +0000196
Holger Hans Peter Freytherb61e3b22009-12-22 22:32:51 +0100197 /* are we using color */
Harald Welteaa6c9ca2009-12-24 11:11:54 +0100198 if (target->use_color) {
Holger Hans Peter Freytherb61e3b22009-12-22 22:32:51 +0100199 snprintf(col, sizeof(col), "%s", color(subsys));
Harald Welteaa6c9ca2009-12-24 11:11:54 +0100200 col[sizeof(col)-1] = '\0';
201 }
Holger Hans Peter Freytherb61e3b22009-12-22 22:32:51 +0100202 vsnprintf(buf, sizeof(buf), format, ap);
Harald Welteaa6c9ca2009-12-24 11:11:54 +0100203 buf[sizeof(buf)-1] = '\0';
Harald Welte6ddd1682009-02-06 12:38:29 +0000204
205 if (!cont) {
Holger Hans Peter Freytherb61e3b22009-12-22 22:32:51 +0100206 if (target->print_timestamp) {
Harald Welted3ff51d2009-06-09 20:21:57 +0000207 char *timestr;
208 time_t tm;
209 tm = time(NULL);
210 timestr = ctime(&tm);
211 timestr[strlen(timestr)-1] = '\0';
Holger Hans Peter Freytherb61e3b22009-12-22 22:32:51 +0100212 snprintf(tim, sizeof(tim), "%s ", timestr);
Harald Welteaa6c9ca2009-12-24 11:11:54 +0100213 tim[sizeof(tim)-1] = '\0';
Harald Welted3ff51d2009-06-09 20:21:57 +0000214 }
Holger Hans Peter Freytherb61e3b22009-12-22 22:32:51 +0100215 snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
Harald Welteaa6c9ca2009-12-24 11:11:54 +0100216 sub[sizeof(sub)-1] = '\0';
Harald Welte6ddd1682009-02-06 12:38:29 +0000217 }
Holger Freyther32636e82008-12-27 11:07:15 +0000218
Holger Hans Peter Freytherb61e3b22009-12-22 22:32:51 +0100219 snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf);
Harald Welteaa6c9ca2009-12-24 11:11:54 +0100220 final[sizeof(final)-1] = '\0';
Holger Hans Peter Freytherb61e3b22009-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 Welteaa8989c2009-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 Welte7ed25292009-12-24 11:14:03 +0100266 va_end(bp);
Harald Welteaa8989c2009-12-24 11:12:11 +0100267 }
Holger Hans Peter Freytherb61e3b22009-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);
Holger Freyther32636e82008-12-27 11:07:15 +0000277 va_end(ap);
Holger Hans Peter Freytherb61e3b22009-12-22 22:32:51 +0100278}
Holger Freyther32636e82008-12-27 11:07:15 +0000279
Holger Hans Peter Freytherb61e3b22009-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);
Holger Freyther32636e82008-12-27 11:07:15 +0000287}
288
Harald Welte3cc4bf52009-02-28 13:08:01 +0000289static char hexd_buff[4096];
290
Holger Hans Peter Freythere78074e2009-08-20 13:16:26 +0200291char *hexdump(const unsigned char *buf, int len)
Holger Freytherca362a62009-01-04 21:05:01 +0000292{
293 int i;
Harald Welte3cc4bf52009-02-28 13:08:01 +0000294 char *cur = hexd_buff;
295
296 hexd_buff[0] = 0;
Holger Freytherca362a62009-01-04 21:05:01 +0000297 for (i = 0; i < len; i++) {
Harald Welte3cc4bf52009-02-28 13:08:01 +0000298 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;
Holger Freytherca362a62009-01-04 21:05:01 +0000303 }
Harald Welte3cc4bf52009-02-28 13:08:01 +0000304 hexd_buff[sizeof(hexd_buff)-1] = 0;
305 return hexd_buff;
Holger Freytherca362a62009-01-04 21:05:01 +0000306}
307
Holger Hans Peter Freytherb61e3b22009-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}