]>
Commit | Line | Data |
---|---|---|
38d04788 | 1 | |
30a4f2a8 | 2 | /* |
985c86bc | 3 | * $Id: debug.cc,v 1.98 2006/05/08 23:38:33 robertc Exp $ |
30a4f2a8 | 4 | * |
5 | * DEBUG: section 0 Debug Routines | |
6 | * AUTHOR: Harvest Derived | |
7 | * | |
2b6662ba | 8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 9 | * ---------------------------------------------------------- |
30a4f2a8 | 10 | * |
2b6662ba | 11 | * Squid is the result of efforts by numerous individuals from |
12 | * the Internet community; see the CONTRIBUTORS file for full | |
13 | * details. Many organizations have provided support for Squid's | |
14 | * development; see the SPONSORS file for full details. Squid is | |
15 | * Copyrighted (C) 2001 by the Regents of the University of | |
16 | * California; see the COPYRIGHT file for full details. Squid | |
17 | * incorporates software developed and/or copyrighted by other | |
18 | * sources; see the CREDITS file for full details. | |
30a4f2a8 | 19 | * |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
24 | * | |
25 | * This program is distributed in the hope that it will be useful, | |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
29 | * | |
30 | * You should have received a copy of the GNU General Public License | |
31 | * along with this program; if not, write to the Free Software | |
cbdec147 | 32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 33 | * |
30a4f2a8 | 34 | */ |
ed43818f | 35 | |
44a47c6e | 36 | #include "squid.h" |
c772f001 | 37 | #include "Debug.h" |
985c86bc | 38 | #include "SquidTime.h" |
f95fe6ed | 39 | #include <sstream> |
c772f001 | 40 | |
41 | int Debug::Levels[MAX_DEBUG_SECTIONS]; | |
42 | int Debug::level; | |
090089c4 | 43 | |
090089c4 | 44 | static char *debug_log_file = NULL; |
123abbe1 | 45 | static int Ctx_Lock = 0; |
12858c3a | 46 | static const char *debugLogTime(void); |
2d72d4fd | 47 | static void ctx_print(void); |
811ef305 | 48 | #if HAVE_SYSLOG |
5e6d4736 | 49 | #ifdef LOG_LOCAL4 |
50 | static int syslog_facility = 0; | |
51 | #endif | |
811ef305 | 52 | static void _db_print_syslog(const char *format, va_list args); |
53 | #endif | |
54 | static void _db_print_stderr(const char *format, va_list args); | |
55 | static void _db_print_file(const char *format, va_list args); | |
24382924 | 56 | |
b8d8561b | 57 | void |
811ef305 | 58 | #if STDC_HEADERS |
a3d5953d | 59 | _db_print(const char *format,...) |
090089c4 | 60 | { |
090089c4 | 61 | #else |
b8d8561b | 62 | _db_print(va_alist) |
62e76326 | 63 | va_dcl |
090089c4 | 64 | { |
0ee4272b | 65 | const char *format = NULL; |
97f61b08 | 66 | #endif |
62e76326 | 67 | |
95d659f0 | 68 | LOCAL_ARRAY(char, f, BUFSIZ); |
cd11dce6 | 69 | va_list args1; |
6de2df60 | 70 | #if STDC_HEADERS |
62e76326 | 71 | |
cd11dce6 | 72 | va_list args2; |
73 | va_list args3; | |
380de6f3 | 74 | #else |
75 | #define args2 args1 | |
76 | #define args3 args1 | |
77 | #endif | |
78 | /* give a chance to context-based debugging to print current context */ | |
62e76326 | 79 | |
380de6f3 | 80 | if (!Ctx_Lock) |
62e76326 | 81 | ctx_print(); |
82 | ||
380de6f3 | 83 | #if STDC_HEADERS |
62e76326 | 84 | |
cd11dce6 | 85 | va_start(args1, format); |
62e76326 | 86 | |
cd11dce6 | 87 | va_start(args2, format); |
62e76326 | 88 | |
cd11dce6 | 89 | va_start(args3, format); |
62e76326 | 90 | |
090089c4 | 91 | #else |
62e76326 | 92 | |
cd11dce6 | 93 | format = va_arg(args1, const char *); |
62e76326 | 94 | |
74946a0f | 95 | #endif |
62e76326 | 96 | |
811ef305 | 97 | snprintf(f, BUFSIZ, "%s| %s", |
12858c3a | 98 | debugLogTime(), |
62e76326 | 99 | format); |
100 | ||
cd11dce6 | 101 | _db_print_file(f, args1); |
62e76326 | 102 | |
cd11dce6 | 103 | _db_print_stderr(f, args2); |
62e76326 | 104 | |
811ef305 | 105 | #if HAVE_SYSLOG |
62e76326 | 106 | |
cd11dce6 | 107 | _db_print_syslog(format, args3); |
62e76326 | 108 | |
cd11dce6 | 109 | #endif |
62e76326 | 110 | |
cd11dce6 | 111 | va_end(args1); |
62e76326 | 112 | |
cd11dce6 | 113 | #if STDC_HEADERS |
62e76326 | 114 | |
cd11dce6 | 115 | va_end(args2); |
62e76326 | 116 | |
cd11dce6 | 117 | va_end(args3); |
62e76326 | 118 | |
811ef305 | 119 | #endif |
811ef305 | 120 | } |
74946a0f | 121 | |
811ef305 | 122 | static void |
62e76326 | 123 | _db_print_file(const char *format, va_list args) { |
74946a0f | 124 | if (debug_log == NULL) |
62e76326 | 125 | return; |
126 | ||
123abbe1 | 127 | /* give a chance to context-based debugging to print current context */ |
128 | if (!Ctx_Lock) | |
62e76326 | 129 | ctx_print(); |
130 | ||
811ef305 | 131 | vfprintf(debug_log, format, args); |
62e76326 | 132 | |
17a0a4ee | 133 | if (!Config.onoff.buffered_logs) |
62e76326 | 134 | fflush(debug_log); |
090089c4 | 135 | } |
136 | ||
811ef305 | 137 | static void |
62e76326 | 138 | _db_print_stderr(const char *format, va_list args) { |
c772f001 | 139 | if (opt_debug_stderr < Debug::level) |
62e76326 | 140 | return; |
141 | ||
811ef305 | 142 | if (debug_log == stderr) |
62e76326 | 143 | return; |
144 | ||
811ef305 | 145 | vfprintf(stderr, format, args); |
146 | } | |
147 | ||
148 | #if HAVE_SYSLOG | |
149 | static void | |
62e76326 | 150 | _db_print_syslog(const char *format, va_list args) { |
811ef305 | 151 | LOCAL_ARRAY(char, tmpbuf, BUFSIZ); |
152 | /* level 0,1 go to syslog */ | |
62e76326 | 153 | |
c772f001 | 154 | if (Debug::level > 1) |
62e76326 | 155 | return; |
156 | ||
811ef305 | 157 | if (0 == opt_syslog_enable) |
62e76326 | 158 | return; |
159 | ||
811ef305 | 160 | tmpbuf[0] = '\0'; |
62e76326 | 161 | |
811ef305 | 162 | vsnprintf(tmpbuf, BUFSIZ, format, args); |
62e76326 | 163 | |
811ef305 | 164 | tmpbuf[BUFSIZ - 1] = '\0'; |
62e76326 | 165 | |
c772f001 | 166 | syslog(Debug::level == 0 ? LOG_WARNING : LOG_NOTICE, "%s", tmpbuf); |
811ef305 | 167 | } |
62e76326 | 168 | |
811ef305 | 169 | #endif /* HAVE_SYSLOG */ |
170 | ||
b8d8561b | 171 | static void |
62e76326 | 172 | debugArg(const char *arg) { |
c9f06944 | 173 | int s = 0; |
174 | int l = 0; | |
175 | int i; | |
62e76326 | 176 | |
c9f06944 | 177 | if (!strncasecmp(arg, "ALL", 3)) { |
62e76326 | 178 | s = -1; |
179 | arg += 4; | |
c9f06944 | 180 | } else { |
62e76326 | 181 | s = atoi(arg); |
182 | ||
183 | while (*arg && *arg++ != ',') | |
184 | ||
185 | ; | |
c9f06944 | 186 | } |
62e76326 | 187 | |
c9f06944 | 188 | l = atoi(arg); |
b6a2f15e | 189 | assert(s >= -1); |
190 | assert(s < MAX_DEBUG_SECTIONS); | |
62e76326 | 191 | |
b6a2f15e | 192 | if (l < 0) |
62e76326 | 193 | l = 0; |
194 | ||
b6a2f15e | 195 | if (l > 10) |
62e76326 | 196 | l = 10; |
197 | ||
c9f06944 | 198 | if (s >= 0) { |
62e76326 | 199 | Debug::Levels[s] = l; |
200 | return; | |
c9f06944 | 201 | } |
62e76326 | 202 | |
c9f06944 | 203 | for (i = 0; i < MAX_DEBUG_SECTIONS; i++) |
62e76326 | 204 | Debug::Levels[i] = l; |
12b9e9b1 | 205 | } |
206 | ||
b8d8561b | 207 | static void |
62e76326 | 208 | debugOpenLog(const char *logfile) { |
ccff9601 | 209 | if (logfile == NULL) { |
62e76326 | 210 | debug_log = stderr; |
211 | return; | |
ccff9601 | 212 | } |
62e76326 | 213 | |
ccff9601 | 214 | if (debug_log_file) |
62e76326 | 215 | xfree(debug_log_file); |
216 | ||
ccff9601 | 217 | debug_log_file = xstrdup(logfile); /* keep a static copy */ |
62e76326 | 218 | |
f2cc1af5 | 219 | if (debug_log && debug_log != stderr) |
62e76326 | 220 | fclose(debug_log); |
221 | ||
ccff9601 | 222 | debug_log = fopen(logfile, "a+"); |
62e76326 | 223 | |
ccff9601 | 224 | if (!debug_log) { |
62e76326 | 225 | fprintf(stderr, "WARNING: Cannot write log file: %s\n", logfile); |
226 | perror(logfile); | |
227 | fprintf(stderr, " messages will be sent to 'stderr'.\n"); | |
228 | fflush(stderr); | |
229 | debug_log = stderr; | |
ccff9601 | 230 | } |
62e76326 | 231 | |
ec4daaa5 | 232 | #ifdef _SQUID_WIN32_ |
c4aefe96 | 233 | setmode(fileno(debug_log), O_TEXT); |
62e76326 | 234 | |
c4aefe96 | 235 | #endif |
ccff9601 | 236 | } |
237 | ||
5e6d4736 | 238 | #if HAVE_SYSLOG |
239 | #ifdef LOG_LOCAL4 | |
240 | ||
241 | static struct syslog_facility_name { | |
242 | const char *name; | |
243 | int facility; | |
244 | } | |
245 | ||
246 | syslog_facility_names[] = { | |
247 | ||
248 | #ifdef LOG_AUTH | |
249 | { | |
250 | "auth", LOG_AUTH | |
251 | }, | |
252 | #endif | |
253 | #ifdef LOG_AUTHPRIV | |
254 | { | |
255 | "authpriv", LOG_AUTHPRIV | |
256 | }, | |
257 | #endif | |
258 | #ifdef LOG_CRON | |
259 | { | |
260 | "cron", LOG_CRON | |
261 | }, | |
262 | #endif | |
263 | #ifdef LOG_DAEMON | |
264 | { | |
265 | "daemon", LOG_DAEMON | |
266 | }, | |
267 | #endif | |
268 | #ifdef LOG_FTP | |
269 | { | |
270 | "ftp", LOG_FTP | |
271 | }, | |
272 | #endif | |
273 | #ifdef LOG_KERN | |
274 | { | |
275 | "kern", LOG_KERN | |
276 | }, | |
277 | #endif | |
278 | #ifdef LOG_LPR | |
279 | { | |
280 | "lpr", LOG_LPR | |
281 | }, | |
282 | #endif | |
283 | #ifdef LOG_MAIL | |
284 | { | |
285 | "mail", LOG_MAIL | |
286 | }, | |
287 | #endif | |
288 | #ifdef LOG_NEWS | |
289 | { | |
290 | "news", LOG_NEWS | |
291 | }, | |
292 | #endif | |
293 | #ifdef LOG_SYSLOG | |
294 | { | |
295 | "syslog", LOG_SYSLOG | |
296 | }, | |
297 | #endif | |
298 | #ifdef LOG_USER | |
299 | { | |
300 | "user", LOG_USER | |
301 | }, | |
302 | #endif | |
303 | #ifdef LOG_UUCP | |
304 | { | |
305 | "uucp", LOG_UUCP | |
306 | }, | |
307 | #endif | |
308 | #ifdef LOG_LOCAL0 | |
309 | { | |
310 | "local0", LOG_LOCAL0 | |
311 | }, | |
312 | #endif | |
313 | #ifdef LOG_LOCAL1 | |
314 | { | |
315 | "local1", LOG_LOCAL1 | |
316 | }, | |
317 | #endif | |
318 | #ifdef LOG_LOCAL2 | |
319 | { | |
320 | "local2", LOG_LOCAL2 | |
321 | }, | |
322 | #endif | |
323 | #ifdef LOG_LOCAL3 | |
324 | { | |
325 | "local3", LOG_LOCAL3 | |
326 | }, | |
327 | #endif | |
328 | #ifdef LOG_LOCAL4 | |
329 | { | |
330 | "local4", LOG_LOCAL4 | |
331 | }, | |
332 | #endif | |
333 | #ifdef LOG_LOCAL5 | |
334 | { | |
335 | "local5", LOG_LOCAL5 | |
336 | }, | |
337 | #endif | |
338 | #ifdef LOG_LOCAL6 | |
339 | { | |
340 | "local6", LOG_LOCAL6 | |
341 | }, | |
342 | #endif | |
343 | #ifdef LOG_LOCAL7 | |
344 | { | |
345 | "local7", LOG_LOCAL7 | |
346 | }, | |
347 | #endif | |
348 | { | |
349 | NULL, 0 | |
350 | } | |
351 | }; | |
352 | ||
353 | #endif | |
354 | ||
355 | void | |
356 | _db_set_syslog(const char *facility) { | |
357 | opt_syslog_enable = 1; | |
358 | #ifdef LOG_LOCAL4 | |
359 | #ifdef LOG_DAEMON | |
360 | ||
361 | syslog_facility = LOG_DAEMON; | |
362 | #else | |
363 | ||
364 | syslog_facility = LOG_LOCAL4; | |
365 | #endif | |
366 | ||
367 | if (facility) { | |
368 | ||
369 | struct syslog_facility_name *n; | |
370 | ||
371 | for (n = syslog_facility_names; n->name; n++) { | |
372 | if (strcmp(n->name, facility) == 0) { | |
373 | syslog_facility = n->facility; | |
374 | return; | |
375 | } | |
376 | } | |
377 | ||
378 | fprintf(stderr, "unknown syslog facility '%s'\n", facility); | |
379 | exit(1); | |
380 | } | |
381 | ||
382 | #else | |
383 | if (facility) | |
384 | fprintf(stderr, "syslog facility type not supported on your system\n"); | |
385 | ||
386 | #endif | |
387 | } | |
388 | ||
389 | #endif | |
390 | ||
b8d8561b | 391 | void |
d9e04dc7 | 392 | Debug::parseOptions(char const *options) { |
12b9e9b1 | 393 | int i; |
394 | char *p = NULL; | |
395 | char *s = NULL; | |
090089c4 | 396 | |
12b9e9b1 | 397 | for (i = 0; i < MAX_DEBUG_SECTIONS; i++) |
62e76326 | 398 | Debug::Levels[i] = -1; |
090089c4 | 399 | |
30a4f2a8 | 400 | if (options) { |
62e76326 | 401 | p = xstrdup(options); |
402 | ||
403 | for (s = strtok(p, w_space); s; s = strtok(NULL, w_space)) | |
404 | debugArg(s); | |
405 | ||
406 | xfree(p); | |
12b9e9b1 | 407 | } |
d9e04dc7 | 408 | } |
409 | ||
410 | void | |
411 | _db_init(const char *logfile, const char *options) { | |
412 | Debug::parseOptions(options); | |
62e76326 | 413 | |
ccff9601 | 414 | debugOpenLog(logfile); |
090089c4 | 415 | |
30a4f2a8 | 416 | #if HAVE_SYSLOG && defined(LOG_LOCAL4) |
62e76326 | 417 | |
6e40f263 | 418 | if (opt_syslog_enable) |
5e6d4736 | 419 | openlog(appname, LOG_PID | LOG_NDELAY | LOG_CONS, syslog_facility); |
62e76326 | 420 | |
db40ae20 | 421 | #endif /* HAVE_SYSLOG */ |
422 | ||
090089c4 | 423 | } |
424 | ||
b8d8561b | 425 | void |
62e76326 | 426 | _db_rotate_log(void) { |
090089c4 | 427 | int i; |
95d659f0 | 428 | LOCAL_ARRAY(char, from, MAXPATHLEN); |
429 | LOCAL_ARRAY(char, to, MAXPATHLEN); | |
88738790 | 430 | #ifdef S_ISREG |
62e76326 | 431 | |
2edc2504 | 432 | struct stat sb; |
88738790 | 433 | #endif |
090089c4 | 434 | |
435 | if (debug_log_file == NULL) | |
62e76326 | 436 | return; |
437 | ||
71d95578 | 438 | #ifdef S_ISREG |
62e76326 | 439 | |
2edc2504 | 440 | if (stat(debug_log_file, &sb) == 0) |
62e76326 | 441 | if (S_ISREG(sb.st_mode) == 0) |
442 | return; | |
443 | ||
71d95578 | 444 | #endif |
090089c4 | 445 | |
1f38f50a | 446 | /* |
447 | * NOTE: we cannot use xrename here without having it in a | |
448 | * separate file -- tools.c has too many dependencies to be | |
449 | * used everywhere debug.c is used. | |
450 | */ | |
090089c4 | 451 | /* Rotate numbers 0 through N up one */ |
b6f794d6 | 452 | for (i = Config.Log.rotateNumber; i > 1;) { |
62e76326 | 453 | i--; |
454 | snprintf(from, MAXPATHLEN, "%s.%d", debug_log_file, i - 1); | |
455 | snprintf(to, MAXPATHLEN, "%s.%d", debug_log_file, i); | |
0ef0f1de | 456 | #ifdef _SQUID_MSWIN_ |
62e76326 | 457 | |
458 | remove | |
459 | (to); | |
460 | ||
0ef0f1de | 461 | #endif |
62e76326 | 462 | |
463 | rename(from, to); | |
090089c4 | 464 | } |
62e76326 | 465 | |
466 | /* | |
467 | * You can't rename open files on Microsoft "operating systems" | |
468 | * so we close before renaming. | |
469 | */ | |
e24070c5 | 470 | #ifdef _SQUID_MSWIN_ |
471 | if (debug_log != stderr) | |
62e76326 | 472 | fclose(debug_log); |
473 | ||
e24070c5 | 474 | #endif |
090089c4 | 475 | /* Rotate the current log to .0 */ |
b6f794d6 | 476 | if (Config.Log.rotateNumber > 0) { |
62e76326 | 477 | snprintf(to, MAXPATHLEN, "%s.%d", debug_log_file, 0); |
0ef0f1de | 478 | #ifdef _SQUID_MSWIN_ |
62e76326 | 479 | |
480 | remove | |
481 | (to); | |
482 | ||
0ef0f1de | 483 | #endif |
62e76326 | 484 | |
485 | rename(debug_log_file, to); | |
090089c4 | 486 | } |
62e76326 | 487 | |
090089c4 | 488 | /* Close and reopen the log. It may have been renamed "manually" |
489 | * before HUP'ing us. */ | |
0a8020d1 | 490 | if (debug_log != stderr) |
62e76326 | 491 | debugOpenLog(Config.Log.log); |
090089c4 | 492 | } |
4873d3a4 | 493 | |
494 | static const char * | |
12858c3a | 495 | debugLogTime(void) { |
496 | ||
497 | time_t t = getCurrentTime(); | |
62e76326 | 498 | |
4873d3a4 | 499 | struct tm *tm; |
500 | static char buf[128]; | |
501 | static time_t last_t = 0; | |
62e76326 | 502 | |
12858c3a | 503 | if (Debug::level > 1) { |
504 | char buf2[128]; | |
505 | tm = localtime(&t); | |
506 | strftime(buf2, 127, "%Y/%m/%d %H:%M:%S", tm); | |
507 | buf2[127] = '\0'; | |
508 | snprintf(buf, 127, "%s.%03d", buf2, (int) current_time.tv_usec / 1000); | |
509 | last_t = t; | |
510 | } else if (t != last_t) { | |
62e76326 | 511 | tm = localtime(&t); |
512 | strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm); | |
513 | last_t = t; | |
4873d3a4 | 514 | } |
62e76326 | 515 | |
12858c3a | 516 | buf[127] = '\0'; |
4873d3a4 | 517 | return buf; |
518 | } | |
519 | ||
bb71ea1f | 520 | void |
62e76326 | 521 | xassert(const char *msg, const char *file, int line) { |
bb71ea1f | 522 | debug(0, 0) ("assertion failed: %s:%d: \"%s\"\n", file, line, msg); |
62e76326 | 523 | |
bb71ea1f | 524 | if (!shutting_down) |
62e76326 | 525 | abort(); |
bb71ea1f | 526 | } |
527 | ||
123abbe1 | 528 | /* |
529 | * Context-based Debugging | |
26970499 | 530 | * |
531 | * Rationale | |
532 | * --------- | |
533 | * | |
534 | * When you have a long nested processing sequence, it is often impossible | |
535 | * for low level routines to know in what larger context they operate. If a | |
536 | * routine coredumps, one can restore the context using debugger trace. | |
537 | * However, in many case you do not want to coredump, but just want to report | |
538 | * a potential problem. A report maybe useless out of problem context. | |
539 | * | |
540 | * To solve this potential problem, use the following approach: | |
541 | * | |
542 | * int | |
543 | * top_level_foo(const char *url) | |
544 | * { | |
545 | * // define current context | |
546 | * // note: we stack but do not dup ctx descriptions! | |
547 | * Ctx ctx = ctx_enter(url); | |
548 | * ... | |
549 | * // go down; middle_level_bar will eventually call bottom_level_boo | |
550 | * middle_level_bar(method, protocol); | |
551 | * ... | |
552 | * // exit, clean after yourself | |
553 | * ctx_exit(ctx); | |
554 | * } | |
555 | * | |
556 | * void | |
557 | * bottom_level_boo(int status, void *data) | |
558 | * { | |
559 | * // detect exceptional condition, and simply report it, the context | |
560 | * // information will be available somewhere close in the log file | |
561 | * if (status == STRANGE_STATUS) | |
562 | * debug(13, 6) ("DOS attack detected, data: %p\n", data); | |
563 | * ... | |
564 | * } | |
565 | * | |
566 | * Current implementation is extremely simple but still very handy. It has a | |
567 | * negligible overhead (descriptions are not duplicated). | |
568 | * | |
569 | * When the _first_ debug message for a given context is printed, it is | |
570 | * prepended with the current context description. Context is printed with | |
571 | * the same debugging level as the original message. | |
572 | * | |
573 | * Note that we do not print context every type you do ctx_enter(). This | |
574 | * approach would produce too many useless messages. For the same reason, a | |
575 | * context description is printed at most _once_ even if you have 10 | |
576 | * debugging messages within one context. | |
577 | * | |
578 | * Contexts can be nested, of course. You must use ctx_enter() to enter a | |
579 | * context (push it onto stack). It is probably safe to exit several nested | |
580 | * contexts at _once_ by calling ctx_exit() at the top level (this will pop | |
581 | * all context till current one). However, as in any stack, you cannot start | |
582 | * in the middle. | |
583 | * | |
584 | * Analysis: | |
585 | * i) locate debugging message, | |
586 | * ii) locate current context by going _upstream_ in your log file, | |
587 | * iii) hack away. | |
588 | * | |
589 | * | |
123abbe1 | 590 | * To-Do: |
26970499 | 591 | * ----- |
592 | * | |
123abbe1 | 593 | * decide if we want to dup() descriptions (adds overhead) but allows to |
594 | * add printf()-style interface | |
26970499 | 595 | * |
2ac76861 | 596 | * implementation: |
26970499 | 597 | * --------------- |
2ac76861 | 598 | * |
123abbe1 | 599 | * descriptions for contexts over CTX_MAX_LEVEL limit are ignored, you probably |
600 | * have a bug if your nesting goes that deep. | |
601 | */ | |
2ac76861 | 602 | |
123abbe1 | 603 | #define CTX_MAX_LEVEL 255 |
2ac76861 | 604 | |
605 | /* | |
606 | * produce a warning when nesting reaches this level and then double | |
607 | * the level | |
608 | */ | |
609 | static int Ctx_Warn_Level = 32; | |
123abbe1 | 610 | /* all descriptions has been printed up to this level */ |
a3f9588e | 611 | static int Ctx_Reported_Level = -1; |
123abbe1 | 612 | /* descriptions are still valid or active up to this level */ |
a3f9588e | 613 | static int Ctx_Valid_Level = -1; |
123abbe1 | 614 | /* current level, the number of nested ctx_enter() calls */ |
a3f9588e | 615 | static int Ctx_Current_Level = -1; |
123abbe1 | 616 | /* saved descriptions (stack) */ |
2ac76861 | 617 | static const char *Ctx_Descrs[CTX_MAX_LEVEL + 1]; |
b70d1c58 | 618 | /* "safe" get secription */ |
619 | static const char *ctx_get_descr(Ctx ctx); | |
123abbe1 | 620 | |
621 | ||
622 | Ctx | |
62e76326 | 623 | ctx_enter(const char *descr) { |
123abbe1 | 624 | Ctx_Current_Level++; |
625 | ||
626 | if (Ctx_Current_Level <= CTX_MAX_LEVEL) | |
62e76326 | 627 | Ctx_Descrs[Ctx_Current_Level] = descr; |
123abbe1 | 628 | |
629 | if (Ctx_Current_Level == Ctx_Warn_Level) { | |
62e76326 | 630 | debug(0, 0) ("# ctx: suspiciously deep (%d) nesting:\n", Ctx_Warn_Level); |
631 | Ctx_Warn_Level *= 2; | |
123abbe1 | 632 | } |
62e76326 | 633 | |
123abbe1 | 634 | return Ctx_Current_Level; |
635 | } | |
636 | ||
637 | void | |
62e76326 | 638 | ctx_exit(Ctx ctx) { |
123abbe1 | 639 | assert(ctx >= 0); |
2ac76861 | 640 | Ctx_Current_Level = (ctx >= 0) ? ctx - 1 : -1; |
62e76326 | 641 | |
123abbe1 | 642 | if (Ctx_Valid_Level > Ctx_Current_Level) |
62e76326 | 643 | Ctx_Valid_Level = Ctx_Current_Level; |
123abbe1 | 644 | } |
645 | ||
646 | /* | |
647 | * the idea id to print each context description at most once but provide enough | |
648 | * info for deducing the current execution stack | |
649 | */ | |
650 | static void | |
62e76326 | 651 | ctx_print(void) { |
123abbe1 | 652 | /* lock so _db_print will not call us recursively */ |
653 | Ctx_Lock++; | |
654 | /* ok, user saw [0,Ctx_Reported_Level] descriptions */ | |
655 | /* first inform about entries popped since user saw them */ | |
62e76326 | 656 | |
123abbe1 | 657 | if (Ctx_Valid_Level < Ctx_Reported_Level) { |
62e76326 | 658 | if (Ctx_Reported_Level != Ctx_Valid_Level + 1) |
659 | _db_print("ctx: exit levels from %2d down to %2d\n", | |
660 | Ctx_Reported_Level, Ctx_Valid_Level + 1); | |
661 | else | |
662 | _db_print("ctx: exit level %2d\n", Ctx_Reported_Level); | |
663 | ||
664 | Ctx_Reported_Level = Ctx_Valid_Level; | |
123abbe1 | 665 | } |
62e76326 | 666 | |
123abbe1 | 667 | /* report new contexts that were pushed since last report */ |
668 | while (Ctx_Reported_Level < Ctx_Current_Level) { | |
62e76326 | 669 | Ctx_Reported_Level++; |
670 | Ctx_Valid_Level++; | |
671 | _db_print("ctx: enter level %2d: '%s'\n", Ctx_Reported_Level, | |
672 | ctx_get_descr(Ctx_Reported_Level)); | |
123abbe1 | 673 | } |
62e76326 | 674 | |
123abbe1 | 675 | /* unlock */ |
676 | Ctx_Lock--; | |
677 | } | |
b70d1c58 | 678 | |
679 | /* checks for nulls and overflows */ | |
680 | static const char * | |
62e76326 | 681 | ctx_get_descr(Ctx ctx) { |
2ac76861 | 682 | if (ctx < 0 || ctx > CTX_MAX_LEVEL) |
62e76326 | 683 | return "<lost>"; |
684 | ||
b70d1c58 | 685 | return Ctx_Descrs[ctx] ? Ctx_Descrs[ctx] : "<null>"; |
686 | } | |
f95fe6ed | 687 | |
688 | std::ostream & | |
689 | Debug::getDebugOut() { | |
4ecaa0f0 | 690 | assert (CurrentDebug == NULL); |
691 | CurrentDebug = new std::ostringstream(); | |
692 | return *CurrentDebug; | |
f95fe6ed | 693 | } |
694 | ||
695 | void | |
696 | Debug::finishDebug() { | |
4ecaa0f0 | 697 | _db_print("%s\n", CurrentDebug->str().c_str()); |
698 | delete CurrentDebug; | |
699 | CurrentDebug = NULL; | |
f95fe6ed | 700 | } |
701 | ||
bf5113eb | 702 | std::ostringstream (*Debug::CurrentDebug)(NULL); |