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