]> git.ipfire.org Git - thirdparty/squid.git/blob - src/debug.cc
Merge Coverity Fixes
[thirdparty/squid.git] / src / debug.cc
1 /*
2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /* DEBUG: section 00 Debug Routines */
10
11 #include "squid.h"
12 #include "Debug.h"
13 #include "ipc/Kids.h"
14 #include "SquidTime.h"
15 #include "util.h"
16
17 /* for shutting_down flag in xassert() */
18 #include "globals.h"
19
20 char *Debug::debugOptions = NULL;
21 int Debug::override_X = 0;
22 int Debug::log_stderr = -1;
23 bool Debug::log_syslog = false;
24 int Debug::Levels[MAX_DEBUG_SECTIONS];
25 int Debug::level;
26 int Debug::sectionLevel;
27 char *Debug::cache_log = NULL;
28 int Debug::rotateNumber = -1;
29 FILE *debug_log = NULL;
30 static char *debug_log_file = NULL;
31 static int Ctx_Lock = 0;
32 static const char *debugLogTime(void);
33 static const char *debugLogKid(void);
34 static void ctx_print(void);
35 #if HAVE_SYSLOG
36 #ifdef LOG_LOCAL4
37 static int syslog_facility = 0;
38 #endif
39 static void _db_print_syslog(const char *format, va_list args);
40 #endif
41 static void _db_print_stderr(const char *format, va_list args);
42 static void _db_print_file(const char *format, va_list args);
43
44 #if _SQUID_WINDOWS_
45 extern LPCRITICAL_SECTION dbg_mutex;
46 typedef BOOL (WINAPI * PFInitializeCriticalSectionAndSpinCount) (LPCRITICAL_SECTION, DWORD);
47 #endif
48
49 void
50 _db_print(const char *format,...)
51 {
52 char f[BUFSIZ];
53 f[0]='\0';
54 va_list args1;
55 va_list args2;
56 va_list args3;
57
58 #if _SQUID_WINDOWS_
59 /* Multiple WIN32 threads may call this simultaneously */
60
61 if (!dbg_mutex) {
62 HMODULE krnl_lib = GetModuleHandle("Kernel32");
63 PFInitializeCriticalSectionAndSpinCount InitializeCriticalSectionAndSpinCount = NULL;
64
65 if (krnl_lib)
66 InitializeCriticalSectionAndSpinCount =
67 (PFInitializeCriticalSectionAndSpinCount) GetProcAddress(krnl_lib,
68 "InitializeCriticalSectionAndSpinCount");
69
70 dbg_mutex = static_cast<CRITICAL_SECTION*>(xcalloc(1, sizeof(CRITICAL_SECTION)));
71
72 if (InitializeCriticalSectionAndSpinCount) {
73 /* let multiprocessor systems EnterCriticalSection() fast */
74
75 if (!InitializeCriticalSectionAndSpinCount(dbg_mutex, 4000)) {
76 if (debug_log) {
77 fprintf(debug_log, "FATAL: _db_print: can't initialize critical section\n");
78 fflush(debug_log);
79 }
80
81 fprintf(stderr, "FATAL: _db_print: can't initialize critical section\n");
82 abort();
83 } else
84 InitializeCriticalSection(dbg_mutex);
85 }
86 }
87
88 EnterCriticalSection(dbg_mutex);
89 #endif
90
91 /* give a chance to context-based debugging to print current context */
92 if (!Ctx_Lock)
93 ctx_print();
94
95 va_start(args1, format);
96 va_start(args2, format);
97 va_start(args3, format);
98
99 snprintf(f, BUFSIZ, "%s%s| %s",
100 debugLogTime(),
101 debugLogKid(),
102 format);
103
104 _db_print_file(f, args1);
105 _db_print_stderr(f, args2);
106
107 #if HAVE_SYSLOG
108 _db_print_syslog(format, args3);
109 #endif
110
111 #if _SQUID_WINDOWS_
112 LeaveCriticalSection(dbg_mutex);
113 #endif
114
115 va_end(args1);
116 va_end(args2);
117 va_end(args3);
118 }
119
120 static void
121 _db_print_file(const char *format, va_list args)
122 {
123 if (debug_log == NULL)
124 return;
125
126 /* give a chance to context-based debugging to print current context */
127 if (!Ctx_Lock)
128 ctx_print();
129
130 vfprintf(debug_log, format, args);
131 fflush(debug_log);
132 }
133
134 static void
135 _db_print_stderr(const char *format, va_list args)
136 {
137 if (Debug::log_stderr < Debug::level)
138 return;
139
140 if (debug_log == stderr)
141 return;
142
143 vfprintf(stderr, format, args);
144 }
145
146 #if HAVE_SYSLOG
147 static void
148 _db_print_syslog(const char *format, va_list args)
149 {
150 /* level 0,1 go to syslog */
151
152 if (Debug::level > 1)
153 return;
154
155 if (!Debug::log_syslog)
156 return;
157
158 char tmpbuf[BUFSIZ];
159 tmpbuf[0] = '\0';
160
161 vsnprintf(tmpbuf, BUFSIZ, format, args);
162
163 tmpbuf[BUFSIZ - 1] = '\0';
164
165 syslog(Debug::level == 0 ? LOG_WARNING : LOG_NOTICE, "%s", tmpbuf);
166 }
167 #endif /* HAVE_SYSLOG */
168
169 static void
170 debugArg(const char *arg)
171 {
172 int s = 0;
173 int l = 0;
174 int i;
175
176 if (!strncasecmp(arg, "rotate=", 7)) {
177 arg += 7;
178 Debug::rotateNumber = atoi(arg);
179 return;
180 } else if (!strncasecmp(arg, "ALL", 3)) {
181 s = -1;
182 arg += 4;
183 } else {
184 s = atoi(arg);
185 while (*arg && *arg++ != ',');
186 }
187
188 l = atoi(arg);
189 assert(s >= -1);
190
191 if (s >= MAX_DEBUG_SECTIONS)
192 s = MAX_DEBUG_SECTIONS-1;
193
194 if (l < 0)
195 l = 0;
196
197 if (l > 10)
198 l = 10;
199
200 if (s >= 0) {
201 Debug::Levels[s] = l;
202 return;
203 }
204
205 for (i = 0; i < MAX_DEBUG_SECTIONS; ++i)
206 Debug::Levels[i] = l;
207 }
208
209 static void
210 debugOpenLog(const char *logfile)
211 {
212 if (logfile == NULL) {
213 debug_log = stderr;
214 return;
215 }
216
217 if (debug_log_file)
218 xfree(debug_log_file);
219
220 debug_log_file = xstrdup(logfile); /* keep a static copy */
221
222 if (debug_log && debug_log != stderr)
223 fclose(debug_log);
224
225 debug_log = fopen(logfile, "a+");
226
227 if (!debug_log) {
228 fprintf(stderr, "WARNING: Cannot write log file: %s\n", logfile);
229 perror(logfile);
230 fprintf(stderr, " messages will be sent to 'stderr'.\n");
231 fflush(stderr);
232 debug_log = stderr;
233 }
234
235 #if _SQUID_WINDOWS_
236 setmode(fileno(debug_log), O_TEXT);
237 #endif
238 }
239
240 #if HAVE_SYSLOG
241 #ifdef LOG_LOCAL4
242
243 static struct syslog_facility_name {
244 const char *name;
245 int facility;
246 }
247
248 syslog_facility_names[] = {
249
250 #ifdef LOG_AUTH
251 {
252 "auth", LOG_AUTH
253 },
254 #endif
255 #ifdef LOG_AUTHPRIV
256 {
257 "authpriv", LOG_AUTHPRIV
258 },
259 #endif
260 #ifdef LOG_CRON
261 {
262 "cron", LOG_CRON
263 },
264 #endif
265 #ifdef LOG_DAEMON
266 {
267 "daemon", LOG_DAEMON
268 },
269 #endif
270 #ifdef LOG_FTP
271 {
272 "ftp", LOG_FTP
273 },
274 #endif
275 #ifdef LOG_KERN
276 {
277 "kern", LOG_KERN
278 },
279 #endif
280 #ifdef LOG_LPR
281 {
282 "lpr", LOG_LPR
283 },
284 #endif
285 #ifdef LOG_MAIL
286 {
287 "mail", LOG_MAIL
288 },
289 #endif
290 #ifdef LOG_NEWS
291 {
292 "news", LOG_NEWS
293 },
294 #endif
295 #ifdef LOG_SYSLOG
296 {
297 "syslog", LOG_SYSLOG
298 },
299 #endif
300 #ifdef LOG_USER
301 {
302 "user", LOG_USER
303 },
304 #endif
305 #ifdef LOG_UUCP
306 {
307 "uucp", LOG_UUCP
308 },
309 #endif
310 #ifdef LOG_LOCAL0
311 {
312 "local0", LOG_LOCAL0
313 },
314 #endif
315 #ifdef LOG_LOCAL1
316 {
317 "local1", LOG_LOCAL1
318 },
319 #endif
320 #ifdef LOG_LOCAL2
321 {
322 "local2", LOG_LOCAL2
323 },
324 #endif
325 #ifdef LOG_LOCAL3
326 {
327 "local3", LOG_LOCAL3
328 },
329 #endif
330 #ifdef LOG_LOCAL4
331 {
332 "local4", LOG_LOCAL4
333 },
334 #endif
335 #ifdef LOG_LOCAL5
336 {
337 "local5", LOG_LOCAL5
338 },
339 #endif
340 #ifdef LOG_LOCAL6
341 {
342 "local6", LOG_LOCAL6
343 },
344 #endif
345 #ifdef LOG_LOCAL7
346 {
347 "local7", LOG_LOCAL7
348 },
349 #endif
350 {
351 NULL, 0
352 }
353 };
354
355 #endif
356
357 void
358 _db_set_syslog(const char *facility)
359 {
360 Debug::log_syslog = true;
361
362 #ifdef LOG_LOCAL4
363 #ifdef LOG_DAEMON
364
365 syslog_facility = LOG_DAEMON;
366 #else
367
368 syslog_facility = LOG_LOCAL4;
369 #endif /* LOG_DAEMON */
370
371 if (facility) {
372
373 struct syslog_facility_name *n;
374
375 for (n = syslog_facility_names; n->name; ++n) {
376 if (strcmp(n->name, facility) == 0) {
377 syslog_facility = n->facility;
378 return;
379 }
380 }
381
382 fprintf(stderr, "unknown syslog facility '%s'\n", facility);
383 exit(1);
384 }
385
386 #else
387 if (facility)
388 fprintf(stderr, "syslog facility type not supported on your system\n");
389
390 #endif /* LOG_LOCAL4 */
391 }
392
393 #endif
394
395 void
396 Debug::parseOptions(char const *options)
397 {
398 int i;
399 char *p = NULL;
400 char *s = NULL;
401
402 if (override_X) {
403 debugs(0, 9, "command-line -X overrides: " << options);
404 return;
405 }
406
407 for (i = 0; i < MAX_DEBUG_SECTIONS; ++i)
408 Debug::Levels[i] = 0;
409
410 if (options) {
411 p = xstrdup(options);
412
413 for (s = strtok(p, w_space); s; s = strtok(NULL, w_space))
414 debugArg(s);
415
416 xfree(p);
417 }
418 }
419
420 void
421 _db_init(const char *logfile, const char *options)
422 {
423 Debug::parseOptions(options);
424
425 debugOpenLog(logfile);
426
427 #if HAVE_SYSLOG && defined(LOG_LOCAL4)
428
429 if (Debug::log_syslog)
430 openlog(APP_SHORTNAME, LOG_PID | LOG_NDELAY | LOG_CONS, syslog_facility);
431
432 #endif /* HAVE_SYSLOG */
433
434 /* Pre-Init TZ env, see bug #2656 */
435 tzset();
436 }
437
438 void
439 _db_rotate_log(void)
440 {
441 if (debug_log_file == NULL)
442 return;
443
444 #ifdef S_ISREG
445 struct stat sb;
446 if (stat(debug_log_file, &sb) == 0)
447 if (S_ISREG(sb.st_mode) == 0)
448 return;
449 #endif
450
451 char from[MAXPATHLEN];
452 from[0] = '\0';
453
454 char to[MAXPATHLEN];
455 to[0] = '\0';
456
457 /*
458 * NOTE: we cannot use xrename here without having it in a
459 * separate file -- tools.c has too many dependencies to be
460 * used everywhere debug.c is used.
461 */
462 /* Rotate numbers 0 through N up one */
463 for (int i = Debug::rotateNumber; i > 1;) {
464 --i;
465 snprintf(from, MAXPATHLEN, "%s.%d", debug_log_file, i - 1);
466 snprintf(to, MAXPATHLEN, "%s.%d", debug_log_file, i);
467 #if _SQUID_WINDOWS_
468 remove
469 (to);
470 #endif
471 errno = 0;
472 if (rename(from, to) == -1) {
473 const auto saved_errno = errno;
474 debugs(0, DBG_IMPORTANT, "log rotation failed: " << xstrerr(saved_errno));
475 }
476 }
477
478 /*
479 * You can't rename open files on Microsoft "operating systems"
480 * so we close before renaming.
481 */
482 #if _SQUID_WINDOWS_
483 if (debug_log != stderr)
484 fclose(debug_log);
485 #endif
486 /* Rotate the current log to .0 */
487 if (Debug::rotateNumber > 0) {
488 snprintf(to, MAXPATHLEN, "%s.%d", debug_log_file, 0);
489 #if _SQUID_WINDOWS_
490 errno = 0;
491 if (remove(to) == -1) {
492 const auto saved_errno = errno;
493 debugs(0, DBG_IMPORTANT, "removal of log file " << to << " failed: " << xstrerr(saved_errno));
494 }
495 #endif
496 errno = 0;
497 if (rename(debug_log_file, to) == -1) {
498 const auto saved_errno = errno;
499 debugs(0, DBG_IMPORTANT, "renaming file " << debug_log_file << " to "
500 << to << "failed: " << xstrerr(saved_errno));
501 }
502 }
503
504 /* Close and reopen the log. It may have been renamed "manually"
505 * before HUP'ing us. */
506 if (debug_log != stderr)
507 debugOpenLog(Debug::cache_log);
508 }
509
510 static const char *
511 debugLogTime(void)
512 {
513
514 time_t t = getCurrentTime();
515
516 struct tm *tm;
517 static char buf[128];
518 static time_t last_t = 0;
519
520 if (Debug::level > 1) {
521 char buf2[128];
522 tm = localtime(&t);
523 strftime(buf2, 127, "%Y/%m/%d %H:%M:%S", tm);
524 buf2[127] = '\0';
525 snprintf(buf, 127, "%s.%03d", buf2, (int) current_time.tv_usec / 1000);
526 last_t = t;
527 } else if (t != last_t) {
528 tm = localtime(&t);
529 strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
530 last_t = t;
531 }
532
533 buf[127] = '\0';
534 return buf;
535 }
536
537 static const char *
538 debugLogKid(void)
539 {
540 if (KidIdentifier != 0) {
541 static char buf[16];
542 if (!*buf) // optimization: fill only once after KidIdentifier is set
543 snprintf(buf, sizeof(buf), " kid%d", KidIdentifier);
544 return buf;
545 }
546
547 return "";
548 }
549
550 void
551 xassert(const char *msg, const char *file, int line)
552 {
553 debugs(0, DBG_CRITICAL, "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 Ctx
653 ctx_enter(const char *descr)
654 {
655 ++Ctx_Current_Level;
656
657 if (Ctx_Current_Level <= CTX_MAX_LEVEL)
658 Ctx_Descrs[Ctx_Current_Level] = descr;
659
660 if (Ctx_Current_Level == Ctx_Warn_Level) {
661 debugs(0, DBG_CRITICAL, "# ctx: suspiciously deep (" << Ctx_Warn_Level << ") nesting:");
662 Ctx_Warn_Level *= 2;
663 }
664
665 return Ctx_Current_Level;
666 }
667
668 void
669 ctx_exit(Ctx ctx)
670 {
671 assert(ctx >= 0);
672 Ctx_Current_Level = (ctx >= 0) ? ctx - 1 : -1;
673
674 if (Ctx_Valid_Level > Ctx_Current_Level)
675 Ctx_Valid_Level = Ctx_Current_Level;
676 }
677
678 /*
679 * the idea id to print each context description at most once but provide enough
680 * info for deducing the current execution stack
681 */
682 static void
683 ctx_print(void)
684 {
685 /* lock so _db_print will not call us recursively */
686 ++Ctx_Lock;
687 /* ok, user saw [0,Ctx_Reported_Level] descriptions */
688 /* first inform about entries popped since user saw them */
689
690 if (Ctx_Valid_Level < Ctx_Reported_Level) {
691 if (Ctx_Reported_Level != Ctx_Valid_Level + 1)
692 _db_print("ctx: exit levels from %2d down to %2d\n",
693 Ctx_Reported_Level, Ctx_Valid_Level + 1);
694 else
695 _db_print("ctx: exit level %2d\n", Ctx_Reported_Level);
696
697 Ctx_Reported_Level = Ctx_Valid_Level;
698 }
699
700 /* report new contexts that were pushed since last report */
701 while (Ctx_Reported_Level < Ctx_Current_Level) {
702 ++Ctx_Reported_Level;
703 ++Ctx_Valid_Level;
704 _db_print("ctx: enter level %2d: '%s'\n", Ctx_Reported_Level,
705 ctx_get_descr(Ctx_Reported_Level));
706 }
707
708 /* unlock */
709 --Ctx_Lock;
710 }
711
712 /* checks for nulls and overflows */
713 static const char *
714 ctx_get_descr(Ctx ctx)
715 {
716 if (ctx < 0 || ctx > CTX_MAX_LEVEL)
717 return "<lost>";
718
719 return Ctx_Descrs[ctx] ? Ctx_Descrs[ctx] : "<null>";
720 }
721
722 int Debug::TheDepth = 0;
723
724 Debug::OutStream *Debug::CurrentDebug(NULL);
725
726 std::ostream &
727 Debug::getDebugOut()
728 {
729 assert(TheDepth >= 0);
730 ++TheDepth;
731 if (TheDepth > 1) {
732 assert(CurrentDebug);
733 *CurrentDebug << std::endl << "reentrant debuging " << TheDepth << "-{";
734 } else {
735 assert(!CurrentDebug);
736 CurrentDebug = new Debug::OutStream;
737 // set default formatting flags
738 CurrentDebug->setf(std::ios::fixed);
739 CurrentDebug->precision(2);
740 }
741 return *CurrentDebug;
742 }
743
744 void
745 Debug::finishDebug()
746 {
747 assert(TheDepth >= 0);
748 assert(CurrentDebug);
749 if (TheDepth > 1) {
750 *CurrentDebug << "}-" << TheDepth << std::endl;
751 } else {
752 assert(TheDepth == 1);
753 _db_print("%s\n", CurrentDebug->str().c_str());
754 delete CurrentDebug;
755 CurrentDebug = NULL;
756 }
757 --TheDepth;
758 }
759
760 // Hack: replaces global ::xassert() to debug debugging assertions
761 // Relies on assert macro calling xassert() without a specific scope.
762 void
763 Debug::xassert(const char *msg, const char *file, int line)
764 {
765
766 if (CurrentDebug) {
767 *CurrentDebug << "assertion failed: " << file << ":" << line <<
768 ": \"" << msg << "\"";
769 }
770 abort();
771 }
772
773 size_t
774 BuildPrefixInit()
775 {
776 // XXX: This must be kept in sync with the actual debug.cc location
777 const char *ThisFileNameTail = "src/debug.cc";
778
779 const char *file=__FILE__;
780
781 // Disable heuristic if it does not work.
782 if (!strstr(file, ThisFileNameTail))
783 return 0;
784
785 return strlen(file)-strlen(ThisFileNameTail);
786 }
787
788 const char*
789 SkipBuildPrefix(const char* path)
790 {
791 static const size_t BuildPrefixLength = BuildPrefixInit();
792
793 return path+BuildPrefixLength;
794 }
795
796 std::ostream &
797 Raw::print(std::ostream &os) const
798 {
799 if (label_)
800 os << ' ' << label_ << '[' << size_ << ']';
801
802 if (!size_)
803 return os;
804
805 // finalize debugging level if no level was set explicitly via minLevel()
806 const int finalLevel = (level >= 0) ? level :
807 (size_ > 40 ? DBG_DATA : Debug::sectionLevel);
808 if (finalLevel <= Debug::sectionLevel) {
809 os << (label_ ? '=' : ' ');
810 if (data_)
811 os.write(data_, size_);
812 else
813 os << "[null]";
814 }
815
816 return os;
817 }
818