]> git.ipfire.org Git - thirdparty/squid.git/blob - src/debug.cc
Make debug_options rotate=N option back-compatible.
[thirdparty/squid.git] / src / debug.cc
1 /*
2 * $Id$
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
35 #include "config.h"
36 #include "Debug.h"
37 #include "SquidTime.h"
38 #include "util.h"
39
40 /* for shutting_down flag in xassert() */
41 #include "globals.h"
42
43 /* cope with no squid.h */
44 #ifndef MAXPATHLEN
45 #define MAXPATHLEN 256
46 #endif
47
48 char *Debug::debugOptions = NULL;
49 int Debug::override_X = 0;
50 int Debug::log_stderr = -1;
51 bool Debug::log_syslog = false;
52 int Debug::Levels[MAX_DEBUG_SECTIONS];
53 int Debug::level;
54 char *Debug::cache_log = NULL;
55 int Debug::rotateNumber = -1;
56 FILE *debug_log = NULL;
57 static char *debug_log_file = NULL;
58 static int Ctx_Lock = 0;
59 static const char *debugLogTime(void);
60 static void ctx_print(void);
61 #if HAVE_SYSLOG
62 #ifdef LOG_LOCAL4
63 static int syslog_facility = 0;
64 #endif
65 static void _db_print_syslog(const char *format, va_list args);
66 #endif
67 static void _db_print_stderr(const char *format, va_list args);
68 static void _db_print_file(const char *format, va_list args);
69
70 #ifdef _SQUID_MSWIN_
71 SQUIDCEXTERN LPCRITICAL_SECTION dbg_mutex;
72 typedef BOOL (WINAPI * PFInitializeCriticalSectionAndSpinCount) (LPCRITICAL_SECTION, DWORD);
73 #endif
74
75 void
76 _db_print(const char *format,...)
77 {
78 char f[BUFSIZ]; 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);
773
774 const size_t
775 BuildPrefixInit()
776 {
777 // XXX: This must be kept in sync with the actual debug.cc location
778 const char *ThisFileNameTail = "src/debug.cc";
779
780 const char *file=__FILE__;
781
782 // Disable heuristic if it does not work.
783 if (!strstr(file, ThisFileNameTail))
784 return 0;
785
786 return strlen(file)-strlen(ThisFileNameTail);
787 }
788
789 const char*
790 SkipBuildPrefix(const char* path)
791 {
792 static const size_t BuildPrefixLength = BuildPrefixInit();
793
794 return path+BuildPrefixLength;
795 }