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