~yolanda.robla/+junk/squid3_fresh_branch

« back to all changes in this revision

Viewing changes to src/debug.cc

  • Committer: yolanda.robla at canonical
  • Date: 2013-05-27 10:52:43 UTC
  • Revision ID: yolanda.robla@canonical.com-20130527105243-mcd0nscika17sl4w
first branch

Show diffs side-by-side

added added

removed removed

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