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