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