]> git.ipfire.org Git - thirdparty/squid.git/blame - src/debug.cc
Windows port: fix another missing USE_SELECT_WIN32 in squid.h
[thirdparty/squid.git] / src / debug.cc
CommitLineData
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
41int Debug::Levels[MAX_DEBUG_SECTIONS];
42int Debug::level;
090089c4 43
090089c4 44static char *debug_log_file = NULL;
123abbe1 45static int Ctx_Lock = 0;
12858c3a 46static const char *debugLogTime(void);
2d72d4fd 47static void ctx_print(void);
811ef305 48#if HAVE_SYSLOG
5e6d4736 49#ifdef LOG_LOCAL4
50static int syslog_facility = 0;
51#endif
811ef305 52static void _db_print_syslog(const char *format, va_list args);
53#endif
54static void _db_print_stderr(const char *format, va_list args);
55static void _db_print_file(const char *format, va_list args);
24382924 56
b8d8561b 57void
811ef305 58#if STDC_HEADERS
a3d5953d 59_db_print(const char *format,...)
090089c4 60{
090089c4 61#else
b8d8561b 62_db_print(va_alist)
62e76326 63va_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 122static 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 137static 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
149static 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 171static void
62e76326 172debugArg(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 207static void
62e76326 208debugOpenLog(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
241static struct syslog_facility_name {
242 const char *name;
243 int facility;
244}
245
246syslog_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
355void
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 391void
d9e04dc7 392Debug::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
410void
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 425void
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
494static const char *
12858c3a 495debugLogTime(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 520void
62e76326 521xassert(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 */
609static int Ctx_Warn_Level = 32;
123abbe1 610/* all descriptions has been printed up to this level */
a3f9588e 611static int Ctx_Reported_Level = -1;
123abbe1 612/* descriptions are still valid or active up to this level */
a3f9588e 613static int Ctx_Valid_Level = -1;
123abbe1 614/* current level, the number of nested ctx_enter() calls */
a3f9588e 615static int Ctx_Current_Level = -1;
123abbe1 616/* saved descriptions (stack) */
2ac76861 617static const char *Ctx_Descrs[CTX_MAX_LEVEL + 1];
b70d1c58 618/* "safe" get secription */
619static const char *ctx_get_descr(Ctx ctx);
123abbe1 620
621
622Ctx
62e76326 623ctx_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
637void
62e76326 638ctx_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 */
650static void
62e76326 651ctx_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 */
680static const char *
62e76326 681ctx_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
688std::ostream &
689Debug::getDebugOut() {
4ecaa0f0 690 assert (CurrentDebug == NULL);
691 CurrentDebug = new std::ostringstream();
692 return *CurrentDebug;
f95fe6ed 693}
694
695void
696Debug::finishDebug() {
4ecaa0f0 697 _db_print("%s\n", CurrentDebug->str().c_str());
698 delete CurrentDebug;
699 CurrentDebug = NULL;
f95fe6ed 700}
701
bf5113eb 702std::ostringstream (*Debug::CurrentDebug)(NULL);