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