]> git.ipfire.org Git - thirdparty/squid.git/blame - src/debug/debug.cc
Maintenance: Removed most NULLs using modernize-use-nullptr (#1075)
[thirdparty/squid.git] / src / debug / debug.cc
CommitLineData
30a4f2a8 1/*
bf95c10a 2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
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.
30a4f2a8 7 */
5b6f3fe5 8
bbc27441
AJ
9/* DEBUG: section 00 Debug Routines */
10
f7f3304a 11#include "squid.h"
61be1d8e 12#include "base/Optional.h"
8b082ed9 13#include "base/TextException.h"
675b8408 14#include "debug/Stream.h"
d7ca82e6 15#include "fd.h"
602d9612 16#include "ipc/Kids.h"
98cacedb 17#include "time/gadgets.h"
62493678 18#include "util.h"
c772f001 19
6821c276 20#include <algorithm>
61be1d8e
AR
21#include <deque>
22#include <functional>
23#include <memory>
6821c276 24
aee3523a 25char *Debug::debugOptions = nullptr;
62493678 26int Debug::override_X = 0;
62493678 27bool Debug::log_syslog = false;
c772f001 28int Debug::Levels[MAX_DEBUG_SECTIONS];
aee3523a 29char *Debug::cache_log = nullptr;
47df1aa7 30int Debug::rotateNumber = -1;
61be1d8e
AR
31
32/// a counter related to the number of debugs() calls
33using DebugRecordCount = uint64_t;
34
35class DebugModule;
36
37/// Debugging module singleton.
38static DebugModule *Module_ = nullptr;
39
40/// Explicitly configured maximum level for debugs() messages written to stderr.
41/// debugs() messages with this (or lower) level will be written to stderr (and
42/// possibly other channels).
43static Optional<int> ExplicitStderrLevel;
44
45/// ExplicitStderrLevel preference or default: Just like with
46/// ExplicitStderrLevel, debugs() messages with this (or lower) level will be
47/// written to stderr (and possibly other channels), but this setting is ignored
48/// when ExplicitStderrLevel is set. This setting is also ignored after major
49/// problems that prevent logging of important debugs() messages (e.g., failing
50/// to open cache_log or assertions).
51static int DefaultStderrLevel = -1;
52
53/// early debugs() with higher level are not buffered and, hence, may be lost
54static constexpr int EarlyMessagesLevel = DBG_IMPORTANT;
55
879c39f7
AR
56/// pre-formatted name of the current process for debugs() messages (or empty)
57static std::string ProcessLabel;
58
61be1d8e 59static const char *debugLogTime(time_t t);
879c39f7 60
811ef305 61#if HAVE_SYSLOG
5e6d4736 62#ifdef LOG_LOCAL4
63static int syslog_facility = 0;
64#endif
811ef305 65#endif
24382924 66
7aa9bb3e 67#if _SQUID_WINDOWS_
d9c252f2 68extern LPCRITICAL_SECTION dbg_mutex;
1bc874d7 69typedef BOOL (WINAPI * PFInitializeCriticalSectionAndSpinCount) (LPCRITICAL_SECTION, DWORD);
1bc874d7 70#endif
71
61be1d8e
AR
72static void ResetSections(const int level = DBG_IMPORTANT);
73
879c39f7
AR
74/// Whether ResetSections() has been called already. We need to keep track of
75/// this state because external code may trigger ResetSections() before the
76/// DebugModule constructor has a chance to ResetSections() to their defaults.
77/// TODO: Find a way to static-initialize Debug::Levels instead.
78static bool DidResetSections = false;
79
61be1d8e 80/// a named FILE with very-early/late usage safety mechanisms
0c7e529e
AR
81class DebugFile
82{
83public:
84 DebugFile() {}
85 ~DebugFile() { clear(); }
86 DebugFile(DebugFile &&) = delete; // no copying or moving of any kind
87
88 /// switches to the new pair, absorbing FILE and duping the name
89 void reset(FILE *newFile, const char *newName);
90
91 /// go back to the initial state
92 void clear() { reset(nullptr, nullptr); }
93
61be1d8e
AR
94 /// an opened cache_log stream or nil
95 FILE *file() { return file_; }
0c7e529e
AR
96
97 char *name = nullptr;
98
99private:
100 friend void ResyncDebugLog(FILE *newFile);
101
102 FILE *file_ = nullptr; ///< opened "real" file or nil; never stderr
103};
104
61be1d8e
AR
105/// meta-information of a Finish()ed debugs() message
106class DebugMessageHeader
107{
108public:
109 DebugMessageHeader(const DebugRecordCount aRecordNumber, const Debug::Context &);
110
111 DebugRecordCount recordNumber; ///< LogMessage() calls before this message
112 time_t timestamp; ///< approximate debugs() call time
113 int section; ///< debugs() section
114 int level; ///< debugs() level
115 bool forceAlert; ///< debugs() forceAlert flag
116};
117
118// Avoid SBuf for CompiledDebugMessageBody:
119// * SBuf's own debugging may create a lot of reentrant debugging noise.
120// * Debug::Context::buf is an std::string-based STL ostream. Converting its
121// buf() result to a different kind of string may increase complexity/cost.
122// TODO: Consider switching to a simple fixed-size buffer and a matching stream!
123/// The processed "content" (i.e. the last parameter) part of a debugs() call.
124using CompiledDebugMessageBody = std::string;
125
126/// a fully processed debugs(), ready to be logged
127class CompiledDebugMessage
128{
129public:
130 using Header = DebugMessageHeader;
131 using Body = CompiledDebugMessageBody;
132
133 CompiledDebugMessage(const Header &, const Body &);
134
135 Header header; ///< debugs() meta-information; reflected in log line prefix
136 Body body; ///< the log line after the prefix (without the newline)
137};
138
139// We avoid PoolingAllocator for CompiledDebugMessages to minimize reentrant
140// debugging noise. This noise reduction has negligible performance overhead
141// because it only applied to early messages, and there are few of them.
142/// debugs() messages captured in LogMessage() call order
143using CompiledDebugMessages = std::deque<CompiledDebugMessage>;
144
145/// a receiver of debugs() messages (e.g., stderr or cache.log)
146class DebugChannel
147{
148public:
149 using EarlyMessages = std::unique_ptr<CompiledDebugMessages>;
150
151 explicit DebugChannel(const char *aName);
152 virtual ~DebugChannel() = default;
153
154 // no copying or moving or any kind (for simplicity sake and to prevent accidental copies)
155 DebugChannel(DebugChannel &&) = delete;
156
157 /// whether we are still expecting (and buffering) early messages
158 bool collectingEarlyMessages() const { return bool(earlyMessages); }
159
160 /// end early message buffering, logging any saved messages
161 void stopEarlyMessageCollection();
162
163 /// end early message buffering, without logging any saved messages
164 /// \returns (a possibly empty container with) saved messages or nil
165 EarlyMessages releaseEarlyMessages() { return EarlyMessages(earlyMessages.release()); }
166
167 /// Write the message to the channel if the channel accepts (such) messages.
168 /// This writing may be delayed until the channel configuration is settled.
169 void log(const DebugMessageHeader &, const CompiledDebugMessageBody &);
170
171protected:
172 /// output iterator for writing CompiledDebugMessages to a given channel
173 class Logger
174 {
175 public:
176 using difference_type = void;
177 using value_type = void;
178 using pointer = void;
179 using reference = void;
180 using iterator_category = std::output_iterator_tag;
181
182 explicit Logger(DebugChannel &ch): channel(ch) {}
183
184 Logger &operator=(const CompiledDebugMessage &message)
185 {
186 if (Debug::Enabled(message.header.section, message.header.level))
187 channel.get().log(message.header, message.body);
188 return *this;
189 }
190
191 // These no-op operators are provided to satisfy LegacyOutputIterator requirements,
192 // as is customary for similar STL output iterators like std::ostream_iterator.
193 Logger &operator*() { return *this; }
194 Logger &operator++() { return *this; }
195 Logger &operator++(int) { return *this; }
196
197 private:
198 // wrap: output iterators must be CopyAssignable; raw references are not
199 std::reference_wrapper<DebugChannel> channel; ///< output destination
200 };
201
202 /// whether we should write() the corresponding debugs() message now
203 /// (assumes some higher-level code applied cache.log section/level filter)
204 virtual bool shouldWrite(const DebugMessageHeader &) const = 0;
205
206 /// write the corresponding debugs() message into the channel
207 virtual void write(const DebugMessageHeader &, const CompiledDebugMessageBody &) = 0;
208
209 /// stores the given early message (if possible) or forgets it (otherwise)
210 void saveMessage(const DebugMessageHeader &, const CompiledDebugMessageBody &);
211
212 /// stop saving and log() any "early" messages, in recordNumber order
213 static void StopSavingAndLog(DebugChannel &, DebugChannel * = nullptr);
214
215 /// Formats a validated debugs() record and writes it to the given FILE.
216 void writeToStream(FILE &, const DebugMessageHeader &, const CompiledDebugMessageBody &);
217
218 /// reacts to a written a debugs() message
219 void noteWritten(const DebugMessageHeader &);
220
221protected:
222 const char * const name = nullptr; ///< unique channel label for debugging
223
224 /// the number of messages sent to the underlying channel so far
225 DebugRecordCount written = 0;
226
227 /// DebugMessageHeader::recordNumber of the last message we wrote
228 DebugRecordCount lastWrittenRecordNumber = 0;
229
230 /// debugs() messages waiting for the channel configuration to settle (and
231 /// the channel to open) so that their eligibility for logging can be
232 /// determined (and the messages can be actually written somewhere)
233 EarlyMessages earlyMessages;
234};
235
236/// DebugChannel managing messages destined for the configured cache_log file
237class CacheLogChannel: public DebugChannel
238{
239public:
240 CacheLogChannel(): DebugChannel("cache_log") {}
241
242protected:
243 /* DebugChannel API */
244 virtual bool shouldWrite(const DebugMessageHeader &) const final;
245 virtual void write(const DebugMessageHeader &, const CompiledDebugMessageBody &) final;
246};
247
248/// DebugChannel managing messages destined for "standard error stream" (stderr)
249class StderrChannel: public DebugChannel
250{
251public:
252 StderrChannel(): DebugChannel("stderr") {}
253
254 /// start to take care of past/saved and future cacheLovirtual gChannel messages
255 void takeOver(CacheLogChannel &);
256
257 /// stop providing a cache_log replacement (if we were providing it)
258 void stopCoveringForCacheLog();
259
260 /// \copydoc DebugChannel::shouldWrite()
261 bool enabled(const int messageDebugLevel) const;
262
263protected:
264 /* DebugChannel API */
265 virtual bool shouldWrite(const DebugMessageHeader &) const final;
266 virtual void write(const DebugMessageHeader &, const CompiledDebugMessageBody &) final;
267
268private:
269 /// whether we are the last resort for logging debugs() messages
270 bool coveringForCacheLog = false;
271};
272
273/// syslog DebugChannel
274class SyslogChannel: public DebugChannel
275{
276public:
277 SyslogChannel(): DebugChannel("syslog") {}
278
279 void markOpened() { opened = true; }
280
281protected:
282 /* DebugChannel API */
283 virtual bool shouldWrite(const DebugMessageHeader &) const final;
284 virtual void write(const DebugMessageHeader &, const CompiledDebugMessageBody &) final;
285
286private:
287 bool opened = false; ///< whether openlog() was called
288};
289
290/// Manages private module state that must be available during program startup
291/// and (especially) termination. Any non-trivial state objects must be
292/// encapsulated here because debugs() may be called before dynamic
293/// initialization or after the destruction of static objects in debug.cc.
294class DebugModule
295{
296public:
297 DebugModule();
298
299 // we provide debugging services for the entire duration of the program
300 ~DebugModule() = delete;
301
302 /// \copydoc Debug::PrepareToDie()
303 void prepareToDie();
304
305 /// Log the given debugs() message to appropriate channel(s) (eventually).
306 /// Assumes the message has passed the global section/level filter.
307 void log(const DebugMessageHeader &, const CompiledDebugMessageBody &);
308
309 /// Start using an open cache_log file as the primary debugs() destination.
310 /// Stop using stderr as a cache_log replacement (if we were doing that).
311 void useCacheLog();
312
313 /// Start using stderr as the primary debugs() destination.
314 /// Stop waiting for an open cache_log file (if we were doing that).
315 void banCacheLogUse();
316
317public:
318 CacheLogChannel cacheLogChannel;
319 StderrChannel stderrChannel;
320 SyslogChannel syslogChannel;
321};
322
323/// Maintains the number of code paths on the current call stack that need
324/// protection from new debugs() calls. Squid cannot _block_ re-entrant debugs()
325/// calls, but the high-level debugs() handling code queues re-entrant logging
326/// attempts when Busy() instead of letting them through to sensitive code.
327class LoggingSectionGuard
328{
329public:
330 LoggingSectionGuard();
331 ~LoggingSectionGuard();
332
333 /// whether new debugs() messages must be queued
334 static bool Busy() { return LoggingConcurrencyLevel; }
335
336private:
337 /// the current number of protected callers
338 static size_t LoggingConcurrencyLevel;
339};
340
341size_t LoggingSectionGuard::LoggingConcurrencyLevel = 0;
342
343/// debugs() messages postponed due to LoggingSectionGuard::Busy(). This is the
344/// head of the invasive Context::upper FIFO list of such messages.
345static Debug::Context *WaitingForIdle = nullptr;
346
347/// cache_log file
0c7e529e 348/// safe during static initialization, even if it has not been constructed yet
61be1d8e 349/// safe during program termination, even if it has been destructed already
0c7e529e
AR
350static DebugFile TheLog;
351
352FILE *
353DebugStream() {
61be1d8e
AR
354 return TheLog.file() ? TheLog.file() : stderr;
355}
356
357/// used for the side effect: fills Debug::Levels with the given level
358static void
359ResetSections(const int level)
360{
879c39f7 361 DidResetSections = true;
61be1d8e
AR
362 for (auto &sectionLevel: Debug::Levels)
363 sectionLevel = level;
364}
365
879c39f7
AR
366/// optimization: formats ProcessLabel once for frequent debugs() reuse
367static void
368LabelThisProcess(const char * const name, const Optional<int> id = Optional<int>())
369{
370 assert(name);
371 assert(strlen(name));
372 std::stringstream os;
373 os << ' ' << name;
374 if (id.has_value()) {
375 assert(id.value() >= 0);
376 os << id.value();
377 }
378 ProcessLabel = os.str();
379}
380
381void
382Debug::NameThisHelper(const char * const name)
383{
384 LabelThisProcess(name);
385
386 if (const auto parentProcessDebugOptions = getenv("SQUID_DEBUG")) {
387 assert(!debugOptions);
388 debugOptions = xstrdup(parentProcessDebugOptions);
389 }
390
391 // do not restrict helper (i.e. stderr) logging beyond debug_options
392 EnsureDefaultStderrLevel(DBG_DATA);
393
394 // helpers do not write to cache.log directly; instead, ipcCreate()
395 // diverts helper stderr output to cache.log of the parent process
396 BanCacheLogUse();
397
398 SettleStderr();
399 SettleSyslog();
0efecd32
AR
400
401 debugs(84, 2, "starting " << name << " with PID " << getpid());
879c39f7
AR
402}
403
404void
405Debug::NameThisKid(const int kidIdentifier)
406{
407 // to reduce noise and for backward compatibility, do not label kid messages
408 // in non-SMP mode
409 if (kidIdentifier)
410 LabelThisProcess("kid", Optional<int>(kidIdentifier));
411 else
412 ProcessLabel.clear(); // probably already empty
413}
414
61be1d8e
AR
415/* LoggingSectionGuard */
416
417LoggingSectionGuard::LoggingSectionGuard()
418{
419 ++LoggingConcurrencyLevel;
420}
421
422LoggingSectionGuard::~LoggingSectionGuard()
423{
424 if (--LoggingConcurrencyLevel == 0)
425 Debug::LogWaitingForIdle();
426}
427
428/* DebugModule */
429
430// Depending on DBG_CRITICAL activity and command line options, this code may
431// run as early as static initialization during program startup or as late as
432// the first debugs(DBG_CRITICAL) call from the main loop.
433DebugModule::DebugModule()
434{
435 // explicit initialization before any use by debugs() calls; see bug #2656
436 tzset();
437
438 (void)std::atexit(&Debug::PrepareToDie);
439
879c39f7 440 if (!DidResetSections)
61be1d8e
AR
441 ResetSections();
442}
443
444void
445DebugModule::log(const DebugMessageHeader &header, const CompiledDebugMessageBody &body)
446{
447 cacheLogChannel.log(header, body);
448 stderrChannel.log(header, body);
449 syslogChannel.log(header, body);
450}
451
452void
453DebugModule::prepareToDie()
454{
455 const LoggingSectionGuard sectionGuard;
456
457 // Switch to stderr to improve our chances to log _early_ debugs(). However,
458 // use existing cache_log and/or stderr levels for post-open/close ones.
459 if (cacheLogChannel.collectingEarlyMessages() && !TheLog.file())
460 banCacheLogUse();
461
462 cacheLogChannel.stopEarlyMessageCollection();
463 stderrChannel.stopEarlyMessageCollection();
464 syslogChannel.stopEarlyMessageCollection();
465
466 // Explicit last-resort call because we want to dump any pending messages
467 // (possibly including an assertion) even if another call, higher in the
468 // call stack, is currently in the sensitive section. Squid is dying, and
469 // that other caller (if any) will not get control back and, hence, will not
470 // trigger a Debug::LogWaitingForIdle() check. In most cases, we will log
471 // any pending messages successfully here. In the remaining few cases, we
472 // will lose them just like we would lose them without this call. The
473 // (small) risk here is that we might abort() or crash trying.
474 Debug::LogWaitingForIdle();
475
476 // Do not close/destroy channels: While the Debug module is not _guaranteed_
477 // to get control after prepareToDie(), debugs() calls are still very much
478 // _possible_, and we want to support/log them for as long as we can.
0c7e529e
AR
479}
480
481void
61be1d8e 482DebugModule::useCacheLog()
0c7e529e 483{
61be1d8e
AR
484 assert(TheLog.file());
485 stderrChannel.stopCoveringForCacheLog(); // in case it was covering
486 cacheLogChannel.stopEarlyMessageCollection(); // in case it was collecting
487}
488
489void
490DebugModule::banCacheLogUse()
491{
492 assert(!TheLog.file());
493 stderrChannel.takeOver(cacheLogChannel);
494}
495
496/// safe access to the debugging module
497static
498DebugModule &
499Module()
500{
501 if (!Module_) {
502 Module_ = new DebugModule();
503#if !HAVE_SYSLOG
504 // Optimization: Do not wait for others to tell us what we already know.
505 Debug::SettleSyslog();
506#endif
507 }
508
509 return *Module_;
0c7e529e
AR
510}
511
512void
513ResyncDebugLog(FILE *newFile)
514{
515 TheLog.file_ = newFile;
516}
517
61be1d8e
AR
518/* DebugChannel */
519
520DebugChannel::DebugChannel(const char * const aName):
521 name(aName),
522 earlyMessages(new CompiledDebugMessages())
523{
524}
525
526void
527DebugChannel::stopEarlyMessageCollection()
528{
529 if (earlyMessages)
530 StopSavingAndLog(*this);
531 // else already stopped
532}
533
534void
535DebugChannel::log(const DebugMessageHeader &header, const CompiledDebugMessageBody &body)
536{
537 if (header.recordNumber <= lastWrittenRecordNumber)
538 return;
539
540 if (!shouldWrite(header))
541 return saveMessage(header, body);
542
543 // We only save messages until we learn whether the channel is going to be
544 // used. We now know that it will be used. Also logs saved early messages
545 // (if they became eligible now) before lastWrittenRecordNumber blocks them.
546 stopEarlyMessageCollection();
547
548 write(header, body);
549}
550
551void
552Debug::ForgetSaved()
553{
554 auto &module = Module();
555 (void)module.cacheLogChannel.releaseEarlyMessages();
556 (void)module.stderrChannel.releaseEarlyMessages();
557 (void)module.syslogChannel.releaseEarlyMessages();
558}
559
560void
561Debug::PrepareToDie()
562{
563 Module().prepareToDie();
564}
565
566void
567DebugChannel::StopSavingAndLog(DebugChannel &channelA, DebugChannel *channelBOrNil)
568{
569 const LoggingSectionGuard sectionGuard;
570
571 assert(&channelA != channelBOrNil);
572 const auto asOrNil = channelA.releaseEarlyMessages();
573 const auto bsOrNil = channelBOrNil ? channelBOrNil->releaseEarlyMessages() : nullptr;
574 const auto &as = asOrNil ? *asOrNil : CompiledDebugMessages();
575 const auto &bs = bsOrNil ? *bsOrNil : CompiledDebugMessages();
576
577 const auto writtenEarlier = channelA.written;
578
579 std::merge(as.begin(), as.end(), bs.begin(), bs.end(), Logger(channelA),
580 [](const CompiledDebugMessage &mA, const CompiledDebugMessage &mB) {
581 return mA.header.recordNumber < mB.header.recordNumber;
582 });
583
584 const auto writtenNow = channelA.written - writtenEarlier;
585 if (const auto totalCount = as.size() + bs.size()) {
586 debugs(0, 5, "wrote " << writtenNow << " out of " << totalCount << '=' <<
587 as.size() << '+' << bs.size() << " early messages to " << channelA.name);
588 }
589}
590
591void
592DebugChannel::saveMessage(const DebugMessageHeader &header, const CompiledDebugMessageBody &body)
593{
594 if (!earlyMessages)
595 return; // we have stopped saving early messages
596
597 if (header.level > EarlyMessagesLevel)
598 return; // this message is not important enough to save
599
600 // Given small EarlyMessagesLevel, only a Squid bug can cause so many
601 // earlyMessages. Saving/dumping excessive messages correctly is not only
602 // difficult but is more likely to complicate triage than help: It is the
603 // first earlyMessages that are going to be the most valuable. Our assert()
604 // will dump them if at all possible.
605 assert(earlyMessages->size() < 1000);
606
607 earlyMessages->emplace_back(header, body);
608}
609
610void
611DebugChannel::writeToStream(FILE &destination, const DebugMessageHeader &header, const CompiledDebugMessageBody &body)
612{
613 fprintf(&destination, "%s%s| %s\n",
614 debugLogTime(header.timestamp),
879c39f7 615 ProcessLabel.c_str(),
61be1d8e
AR
616 body.c_str());
617 noteWritten(header);
618}
619
620void
621DebugChannel::noteWritten(const DebugMessageHeader &header)
622{
623 ++written;
624 lastWrittenRecordNumber = header.recordNumber;
625}
626
627/* CacheLogChannel */
628
629bool
630CacheLogChannel::shouldWrite(const DebugMessageHeader &) const
631{
632 return TheLog.file();
633}
634
635void
636CacheLogChannel::write(const DebugMessageHeader &header, const CompiledDebugMessageBody &body)
637{
638 writeToStream(*TheLog.file(), header, body);
639 fflush(TheLog.file());
640}
641
642/* StderrChannel */
643
644bool
645StderrChannel::enabled(const int level) const
646{
647 if (!stderr)
648 return false; // nowhere to write
649
650 if (ExplicitStderrLevel.has_value()) // explicit admin restrictions (-d)
651 return level <= ExplicitStderrLevel.value();
652
653 // whether the given level is allowed by emergency handling circumstances
654 // (coveringForCacheLog) or configuration aspects (e.g., -k or -z)
655 return coveringForCacheLog || level <= DefaultStderrLevel;
656}
657
658bool
659StderrChannel::shouldWrite(const DebugMessageHeader &header) const
660{
661 return enabled(header.level);
662}
663
664void
665StderrChannel::write(const DebugMessageHeader &header, const CompiledDebugMessageBody &body)
666{
667 writeToStream(*stderr, header, body);
668}
669
670void
671StderrChannel::takeOver(CacheLogChannel &cacheLogChannel)
672{
673 if (coveringForCacheLog)
674 return;
675 coveringForCacheLog = true;
676
677 StopSavingAndLog(*this, &cacheLogChannel);
678}
679
680void
681StderrChannel::stopCoveringForCacheLog()
682{
683 if (!coveringForCacheLog)
684 return;
685
686 coveringForCacheLog = false;
687 debugs(0, DBG_IMPORTANT, "Resuming logging to cache_log");
688}
689
690void
691Debug::EnsureDefaultStderrLevel(const int maxDefault)
692{
693 if (DefaultStderrLevel < maxDefault)
694 DefaultStderrLevel = maxDefault; // may set or increase
695 // else: somebody has already requested a more permissive maximum
696}
697
698void
699Debug::ResetStderrLevel(const int maxLevel)
700{
701 ExplicitStderrLevel = maxLevel; // may set, increase, or decrease
702}
703
704void
705Debug::SettleStderr()
706{
707 auto &stderrChannel = Module().stderrChannel;
708
709 stderrChannel.stopEarlyMessageCollection();
710
711 if (override_X) {
712 // Some users might expect -X to force -d9. Tell them what is happening.
713 const auto outcome =
714 stderrChannel.enabled(DBG_DATA) ? "; stderr will see all messages":
715 stderrChannel.enabled(DBG_CRITICAL) ? "; stderr will not see some messages":
716 "; stderr will see no messages";
717 if (ExplicitStderrLevel)
718 debugs(0, DBG_CRITICAL, "Using -X and -d" << ExplicitStderrLevel.value() << outcome);
719 else
720 debugs(0, DBG_CRITICAL, "Using -X without -d" << outcome);
721 }
722}
723
724bool
725Debug::StderrEnabled()
726{
727 return Module().stderrChannel.enabled(DBG_CRITICAL);
728}
729
730/* DebugMessageHeader */
731
732DebugMessageHeader::DebugMessageHeader(const DebugRecordCount aRecordNumber, const Debug::Context &context):
733 recordNumber(aRecordNumber),
734 timestamp(getCurrentTime()),
735 section(context.section),
736 level(context.level),
737 forceAlert(context.forceAlert)
738{
739}
740
741/* CompiledDebugMessage */
742
743CompiledDebugMessage::CompiledDebugMessage(const Header &aHeader, const Body &aBody):
744 header(aHeader),
745 body(aBody)
746{
747}
748
749/* DebugFile */
750
0c7e529e
AR
751void
752DebugFile::reset(FILE *newFile, const char *newName)
753{
754 // callers must use nullptr instead of the used-as-the-last-resort stderr
755 assert(newFile != stderr || !stderr);
756
d7ca82e6
EB
757 if (file_) {
758 fd_close(fileno(file_));
0c7e529e 759 fclose(file_);
d7ca82e6 760 }
0c7e529e
AR
761 file_ = newFile; // may be nil
762
d7ca82e6
EB
763 if (file_)
764 fd_open(fileno(file_), FD_LOG, Debug::cache_log);
765
0c7e529e
AR
766 xfree(name);
767 name = newName ? xstrdup(newName) : nullptr;
768
769 // all open files must have a name
770 // all cleared files must not have a name
771 assert(!file_ == !name);
772}
773
61be1d8e 774/// broadcasts debugs() message to the logging channels
b8d8561b 775void
61be1d8e 776Debug::LogMessage(const Context &context)
090089c4 777{
7aa9bb3e 778#if _SQUID_WINDOWS_
1bc874d7 779 /* Multiple WIN32 threads may call this simultaneously */
780
26ac0430 781 if (!dbg_mutex) {
1bc874d7 782 HMODULE krnl_lib = GetModuleHandle("Kernel32");
783 PFInitializeCriticalSectionAndSpinCount InitializeCriticalSectionAndSpinCount = NULL;
784
785 if (krnl_lib)
786 InitializeCriticalSectionAndSpinCount =
787 (PFInitializeCriticalSectionAndSpinCount) GetProcAddress(krnl_lib,
788 "InitializeCriticalSectionAndSpinCount");
789
790 dbg_mutex = static_cast<CRITICAL_SECTION*>(xcalloc(1, sizeof(CRITICAL_SECTION)));
791
792 if (InitializeCriticalSectionAndSpinCount) {
793 /* let multiprocessor systems EnterCriticalSection() fast */
794
795 if (!InitializeCriticalSectionAndSpinCount(dbg_mutex, 4000)) {
61be1d8e
AR
796 if (const auto logFile = TheLog.file()) {
797 fprintf(logFile, "FATAL: %s: can't initialize critical section\n", __FUNCTION__);
798 fflush(logFile);
1bc874d7 799 }
800
61be1d8e 801 fprintf(stderr, "FATAL: %s: can't initialize critical section\n", __FUNCTION__);
1bc874d7 802 abort();
803 } else
804 InitializeCriticalSection(dbg_mutex);
805 }
806 }
807
808 EnterCriticalSection(dbg_mutex);
380de6f3 809#endif
62e76326 810
61be1d8e
AR
811 static DebugRecordCount LogMessageCalls = 0;
812 const DebugMessageHeader header(++LogMessageCalls, context);
813 Module().log(header, context.buf.str());
1bc874d7 814
7aa9bb3e 815#if _SQUID_WINDOWS_
1bc874d7 816 LeaveCriticalSection(dbg_mutex);
1bc874d7 817#endif
090089c4 818}
819
b8d8561b 820static void
af6a12ee
AJ
821debugArg(const char *arg)
822{
c9f06944 823 int s = 0;
824 int l = 0;
62e76326 825
62493678
AJ
826 if (!strncasecmp(arg, "rotate=", 7)) {
827 arg += 7;
828 Debug::rotateNumber = atoi(arg);
829 return;
830 } else if (!strncasecmp(arg, "ALL", 3)) {
62e76326 831 s = -1;
832 arg += 4;
c9f06944 833 } else {
62e76326 834 s = atoi(arg);
3d0ac046 835 while (*arg && *arg++ != ',');
c9f06944 836 }
62e76326 837
c9f06944 838 l = atoi(arg);
b6a2f15e 839 assert(s >= -1);
e879923e 840
26ac0430 841 if (s >= MAX_DEBUG_SECTIONS)
e879923e 842 s = MAX_DEBUG_SECTIONS-1;
62e76326 843
b6a2f15e 844 if (l < 0)
62e76326 845 l = 0;
846
b6a2f15e 847 if (l > 10)
62e76326 848 l = 10;
849
c9f06944 850 if (s >= 0) {
62e76326 851 Debug::Levels[s] = l;
852 return;
c9f06944 853 }
62e76326 854
61be1d8e 855 ResetSections(l);
12b9e9b1 856}
857
b8d8561b 858static void
af6a12ee
AJ
859debugOpenLog(const char *logfile)
860{
61be1d8e 861 assert(logfile);
62e76326 862
98ad5991
AJ
863 // Bug 4423: ignore the stdio: logging module name if present
864 const char *logfilename;
865 if (strncmp(logfile, "stdio:",6) == 0)
866 logfilename = logfile + 6;
867 else
868 logfilename = logfile;
869
0c7e529e
AR
870 if (auto log = fopen(logfilename, "a+")) {
871#if _SQUID_WINDOWS_
872 setmode(fileno(log), O_TEXT);
873#endif
874 TheLog.reset(log, logfilename);
61be1d8e 875 Module().useCacheLog();
0c7e529e 876 } else {
61be1d8e 877 const auto xerrno = errno;
0c7e529e 878 TheLog.clear();
61be1d8e
AR
879 Module().banCacheLogUse();
880
881 // report the problem after banCacheLogUse() to improve our chances of
882 // reporting earlier debugs() messages (that cannot be written after us)
883 debugs(0, DBG_CRITICAL, "ERROR: Cannot open cache_log (" << logfilename << ") for writing;" <<
884 Debug::Extra << "fopen(3) error: " << xstrerr(xerrno));
ccff9601 885 }
886}
887
5e6d4736 888#if HAVE_SYSLOG
889#ifdef LOG_LOCAL4
890
891static struct syslog_facility_name {
892 const char *name;
893 int facility;
894}
895
896syslog_facility_names[] = {
897
898#ifdef LOG_AUTH
26ac0430
AJ
899 {
900 "auth", LOG_AUTH
901 },
5e6d4736 902#endif
903#ifdef LOG_AUTHPRIV
26ac0430
AJ
904 {
905 "authpriv", LOG_AUTHPRIV
906 },
5e6d4736 907#endif
908#ifdef LOG_CRON
26ac0430
AJ
909 {
910 "cron", LOG_CRON
911 },
5e6d4736 912#endif
913#ifdef LOG_DAEMON
26ac0430
AJ
914 {
915 "daemon", LOG_DAEMON
916 },
5e6d4736 917#endif
918#ifdef LOG_FTP
26ac0430
AJ
919 {
920 "ftp", LOG_FTP
921 },
5e6d4736 922#endif
923#ifdef LOG_KERN
26ac0430
AJ
924 {
925 "kern", LOG_KERN
926 },
5e6d4736 927#endif
928#ifdef LOG_LPR
26ac0430
AJ
929 {
930 "lpr", LOG_LPR
931 },
5e6d4736 932#endif
933#ifdef LOG_MAIL
26ac0430
AJ
934 {
935 "mail", LOG_MAIL
936 },
5e6d4736 937#endif
938#ifdef LOG_NEWS
26ac0430
AJ
939 {
940 "news", LOG_NEWS
941 },
5e6d4736 942#endif
943#ifdef LOG_SYSLOG
26ac0430
AJ
944 {
945 "syslog", LOG_SYSLOG
946 },
5e6d4736 947#endif
948#ifdef LOG_USER
26ac0430
AJ
949 {
950 "user", LOG_USER
951 },
5e6d4736 952#endif
953#ifdef LOG_UUCP
26ac0430
AJ
954 {
955 "uucp", LOG_UUCP
956 },
5e6d4736 957#endif
958#ifdef LOG_LOCAL0
26ac0430
AJ
959 {
960 "local0", LOG_LOCAL0
961 },
5e6d4736 962#endif
963#ifdef LOG_LOCAL1
26ac0430
AJ
964 {
965 "local1", LOG_LOCAL1
966 },
5e6d4736 967#endif
968#ifdef LOG_LOCAL2
26ac0430
AJ
969 {
970 "local2", LOG_LOCAL2
971 },
5e6d4736 972#endif
973#ifdef LOG_LOCAL3
26ac0430
AJ
974 {
975 "local3", LOG_LOCAL3
976 },
5e6d4736 977#endif
978#ifdef LOG_LOCAL4
26ac0430
AJ
979 {
980 "local4", LOG_LOCAL4
981 },
5e6d4736 982#endif
983#ifdef LOG_LOCAL5
26ac0430
AJ
984 {
985 "local5", LOG_LOCAL5
986 },
5e6d4736 987#endif
988#ifdef LOG_LOCAL6
26ac0430
AJ
989 {
990 "local6", LOG_LOCAL6
991 },
5e6d4736 992#endif
993#ifdef LOG_LOCAL7
26ac0430
AJ
994 {
995 "local7", LOG_LOCAL7
996 },
5e6d4736 997#endif
26ac0430 998 {
aee3523a 999 nullptr, 0
26ac0430
AJ
1000 }
1001};
5e6d4736 1002
1003#endif
1004
61be1d8e 1005static void
af6a12ee
AJ
1006_db_set_syslog(const char *facility)
1007{
62493678
AJ
1008 Debug::log_syslog = true;
1009
5e6d4736 1010#ifdef LOG_LOCAL4
1011#ifdef LOG_DAEMON
1012
1013 syslog_facility = LOG_DAEMON;
1014#else
1015
1016 syslog_facility = LOG_LOCAL4;
62493678 1017#endif /* LOG_DAEMON */
5e6d4736 1018
1019 if (facility) {
1020
1021 struct syslog_facility_name *n;
1022
95dc7ff4 1023 for (n = syslog_facility_names; n->name; ++n) {
5e6d4736 1024 if (strcmp(n->name, facility) == 0) {
1025 syslog_facility = n->facility;
1026 return;
1027 }
1028 }
1029
1030 fprintf(stderr, "unknown syslog facility '%s'\n", facility);
24885773 1031 exit(EXIT_FAILURE);
5e6d4736 1032 }
1033
1034#else
1035 if (facility)
1036 fprintf(stderr, "syslog facility type not supported on your system\n");
1037
62493678 1038#endif /* LOG_LOCAL4 */
5e6d4736 1039}
1040
61be1d8e
AR
1041/* SyslogChannel */
1042
1043static int
1044SyslogPriority(const DebugMessageHeader &header)
1045{
1046 return header.forceAlert ? LOG_ALERT :
1047 (header.level == 0 ? LOG_WARNING : LOG_NOTICE);
1048}
8b082ed9
FC
1049
1050void
61be1d8e
AR
1051SyslogChannel::write(const DebugMessageHeader &header, const CompiledDebugMessageBody &body)
1052{
1053 syslog(SyslogPriority(header), "%s", body.c_str());
1054 noteWritten(header);
1055}
1056
1057#else
1058
1059void
1060SyslogChannel::write(const DebugMessageHeader &, const CompiledDebugMessageBody &)
1061{
1062 assert(!"unreachable code because opened, shouldWrite() are always false");
1063}
1064
1065#endif /* HAVE_SYSLOG */
1066
1067bool
1068SyslogChannel::shouldWrite(const DebugMessageHeader &header) const
8b082ed9 1069{
61be1d8e
AR
1070 if (!opened)
1071 return false;
1072
1073 assert(Debug::log_syslog);
1074 return header.forceAlert || header.level <= DBG_IMPORTANT;
8b082ed9
FC
1075}
1076
61be1d8e
AR
1077void
1078Debug::ConfigureSyslog(const char *facility)
1079{
1080#if HAVE_SYSLOG
1081 _db_set_syslog(facility);
1082#else
1083 (void)facility;
1084 // TODO: Throw.
1085 fatalf("Logging to syslog not available on this platform");
5e6d4736 1086#endif
61be1d8e 1087}
5e6d4736 1088
b8d8561b 1089void
af6a12ee
AJ
1090Debug::parseOptions(char const *options)
1091{
aee3523a
AR
1092 char *p = nullptr;
1093 char *s = nullptr;
090089c4 1094
62493678 1095 if (override_X) {
37296f4c 1096 debugs(0, 9, "command-line -X overrides: " << options);
1097 return;
1098 }
1099
61be1d8e 1100 ResetSections();
090089c4 1101
30a4f2a8 1102 if (options) {
62e76326 1103 p = xstrdup(options);
1104
aee3523a 1105 for (s = strtok(p, w_space); s; s = strtok(nullptr, w_space))
62e76326 1106 debugArg(s);
1107
1108 xfree(p);
12b9e9b1 1109 }
d9e04dc7 1110}
1111
1112void
61be1d8e 1113Debug::BanCacheLogUse()
af6a12ee 1114{
61be1d8e
AR
1115 Debug::parseOptions(debugOptions);
1116 Module().banCacheLogUse();
1117}
62e76326 1118
61be1d8e
AR
1119void
1120Debug::UseCacheLog()
1121{
1122 Debug::parseOptions(debugOptions);
1123 debugOpenLog(cache_log);
1124}
090089c4 1125
61be1d8e
AR
1126void
1127Debug::StopCacheLogUse()
1128{
1129 if (TheLog.file()) {
1130 // UseCacheLog() was successful.
1131 Module().cacheLogChannel.stopEarlyMessageCollection(); // paranoid
1132 TheLog.clear();
1133 } else {
1134 // UseCacheLog() was not called at all or failed to open cache_log.
1135 Module().banCacheLogUse(); // may already be banned
1136 }
1137}
1138
1139void
1140Debug::SettleSyslog()
1141{
30a4f2a8 1142#if HAVE_SYSLOG && defined(LOG_LOCAL4)
62e76326 1143
61be1d8e 1144 if (Debug::log_syslog) {
7dbca7a4 1145 openlog(APP_SHORTNAME, LOG_PID | LOG_NDELAY | LOG_CONS, syslog_facility);
61be1d8e
AR
1146 Module().syslogChannel.markOpened();
1147 }
62e76326 1148
db40ae20 1149#endif /* HAVE_SYSLOG */
1150
61be1d8e 1151 Module().syslogChannel.stopEarlyMessageCollection();
090089c4 1152}
1153
b8d8561b 1154void
af6a12ee
AJ
1155_db_rotate_log(void)
1156{
0c7e529e 1157 if (!TheLog.name)
62e76326 1158 return;
1159
71d95578 1160#ifdef S_ISREG
62493678 1161 struct stat sb;
0c7e529e 1162 if (stat(TheLog.name, &sb) == 0)
62e76326 1163 if (S_ISREG(sb.st_mode) == 0)
1164 return;
71d95578 1165#endif
090089c4 1166
62493678
AJ
1167 char from[MAXPATHLEN];
1168 from[0] = '\0';
1169
1170 char to[MAXPATHLEN];
1171 to[0] = '\0';
1172
1f38f50a 1173 /*
1174 * NOTE: we cannot use xrename here without having it in a
1175 * separate file -- tools.c has too many dependencies to be
1176 * used everywhere debug.c is used.
1177 */
090089c4 1178 /* Rotate numbers 0 through N up one */
62493678 1179 for (int i = Debug::rotateNumber; i > 1;) {
5e263176 1180 --i;
0c7e529e
AR
1181 snprintf(from, MAXPATHLEN, "%s.%d", TheLog.name, i - 1);
1182 snprintf(to, MAXPATHLEN, "%s.%d", TheLog.name, i);
7aa9bb3e 1183#if _SQUID_WINDOWS_
3211fedb
FC
1184 remove
1185 (to);
0ef0f1de 1186#endif
595d1637
FC
1187 errno = 0;
1188 if (rename(from, to) == -1) {
a95a43a6 1189 const auto saved_errno = errno;
d816f28d 1190 debugs(0, DBG_IMPORTANT, "ERROR: log rotation failed: " << xstrerr(saved_errno));
595d1637 1191 }
090089c4 1192 }
62e76326 1193
090089c4 1194 /* Rotate the current log to .0 */
62493678 1195 if (Debug::rotateNumber > 0) {
0c7e529e
AR
1196 // form file names before we may clear TheLog below
1197 snprintf(from, MAXPATHLEN, "%s", TheLog.name);
1198 snprintf(to, MAXPATHLEN, "%s.%d", TheLog.name, 0);
1199
7aa9bb3e 1200#if _SQUID_WINDOWS_
f4c818af
FC
1201 errno = 0;
1202 if (remove(to) == -1) {
1203 const auto saved_errno = errno;
d816f28d 1204 debugs(0, DBG_IMPORTANT, "ERROR: removal of log file " << to << " failed: " << xstrerr(saved_errno));
f4c818af 1205 }
0c7e529e 1206 TheLog.clear(); // Windows cannot rename() open files
0ef0f1de 1207#endif
f4c818af 1208 errno = 0;
0c7e529e 1209 if (rename(from, to) == -1) {
f4c818af 1210 const auto saved_errno = errno;
d816f28d 1211 debugs(0, DBG_IMPORTANT, "ERROR: renaming file " << from << " to "
203fdebd 1212 << to << "failed: " << xstrerr(saved_errno));
f4c818af 1213 }
090089c4 1214 }
62e76326 1215
0c7e529e
AR
1216 // Close (if we have not already) and reopen the log because
1217 // it may have been renamed "manually" before HUP'ing us.
1218 debugOpenLog(Debug::cache_log);
090089c4 1219}
4873d3a4 1220
1221static const char *
61be1d8e 1222debugLogTime(const time_t t)
af6a12ee 1223{
4873d3a4 1224 struct tm *tm;
b56b37cf 1225 static char buf[128]; // arbitrary size, big enough for the below timestamp strings.
4873d3a4 1226 static time_t last_t = 0;
62e76326 1227
014adac1 1228 if (Debug::Level() > 1) {
b56b37cf
AJ
1229 // 4 bytes smaller than buf to ensure .NNN catenation by snprintf()
1230 // is safe and works even if strftime() fills its buffer.
1231 char buf2[sizeof(buf)-4];
12858c3a 1232 tm = localtime(&t);
b56b37cf
AJ
1233 strftime(buf2, sizeof(buf2), "%Y/%m/%d %H:%M:%S", tm);
1234 buf2[sizeof(buf2)-1] = '\0';
1235 const int sz = snprintf(buf, sizeof(buf), "%s.%03d", buf2, static_cast<int>(current_time.tv_usec / 1000));
1236 assert(0 < sz && sz < static_cast<int>(sizeof(buf)));
12858c3a 1237 last_t = t;
1238 } else if (t != last_t) {
62e76326 1239 tm = localtime(&t);
b56b37cf
AJ
1240 const int sz = strftime(buf, sizeof(buf), "%Y/%m/%d %H:%M:%S", tm);
1241 assert(0 < sz && sz <= static_cast<int>(sizeof(buf)));
62e76326 1242 last_t = t;
4873d3a4 1243 }
62e76326 1244
b56b37cf 1245 buf[sizeof(buf)-1] = '\0';
4873d3a4 1246 return buf;
1247}
1248
61be1d8e
AR
1249/// Whether there are any xassert() calls in the call stack. Treat as private to
1250/// xassert(): It is moved out only to simplify the asserting code path.
1251static auto Asserting_ = false;
1252
bb71ea1f 1253void
af6a12ee
AJ
1254xassert(const char *msg, const char *file, int line)
1255{
61be1d8e
AR
1256 // if the non-trivial code below has itself asserted, then simplify instead
1257 // of running out of stack and complicating triage
1258 if (Asserting_)
1259 abort();
1260
1261 Asserting_ = true;
1262
d816f28d 1263 debugs(0, DBG_CRITICAL, "FATAL: assertion failed: " << file << ":" << line << ": \"" << msg << "\"");
62e76326 1264
c799ae55
AR
1265 Debug::PrepareToDie();
1266 abort();
bb71ea1f 1267}
1268
014adac1 1269Debug::Context *Debug::Current = nullptr;
9eab365d 1270
014adac1 1271Debug::Context::Context(const int aSection, const int aLevel):
61be1d8e 1272 section(aSection),
014adac1
AR
1273 level(aLevel),
1274 sectionLevel(Levels[aSection]),
e863656d 1275 upper(Current),
61be1d8e
AR
1276 forceAlert(false),
1277 waitingForIdle(false)
014adac1
AR
1278{
1279 formatStream();
1280}
bfa09779 1281
014adac1
AR
1282/// Optimization: avoids new Context creation for every debugs().
1283void
1284Debug::Context::rewind(const int aSection, const int aLevel)
af6a12ee 1285{
61be1d8e 1286 section = aSection;
014adac1
AR
1287 level = aLevel;
1288 sectionLevel = Levels[aSection];
1289 assert(upper == Current);
61be1d8e 1290 assert(!waitingForIdle);
014adac1 1291
61be1d8e 1292 buf.str(CompiledDebugMessageBody());
014adac1
AR
1293 buf.clear();
1294 // debugs() users are supposed to preserve format, but
1295 // some do not, so we have to waste cycles resetting it for all.
1296 formatStream();
f95fe6ed 1297}
1298
014adac1 1299/// configures default formatting for the debugging stream
f95fe6ed 1300void
014adac1
AR
1301Debug::Context::formatStream()
1302{
1303 const static std::ostringstream cleanStream;
1304 buf.flags(cleanStream.flags() | std::ios::fixed);
1305 buf.width(cleanStream.width());
1306 buf.precision(2);
1307 buf.fill(' ');
1308 // If this is not enough, use copyfmt(cleanStream) which is ~10% slower.
1309}
1310
61be1d8e
AR
1311void
1312Debug::LogWaitingForIdle()
1313{
1314 if (!WaitingForIdle)
1315 return; // do not lock in vain because unlocking would calls us
1316
1317 const LoggingSectionGuard sectionGuard;
1318 while (const auto current = WaitingForIdle) {
1319 assert(current->waitingForIdle);
1320 LogMessage(*current);
1321 WaitingForIdle = current->upper;
1322 delete current;
1323 }
1324}
1325
014adac1
AR
1326std::ostringstream &
1327Debug::Start(const int section, const int level)
af6a12ee 1328{
014adac1
AR
1329 Context *future = nullptr;
1330
61be1d8e
AR
1331 if (LoggingSectionGuard::Busy()) {
1332 // a very rare reentrant debugs() call that originated during Finish() and such
1333 future = new Context(section, level);
1334 future->waitingForIdle = true;
1335 } else if (Current) {
1336 // a rare reentrant debugs() call that originated between Start() and Finish()
014adac1 1337 future = new Context(section, level);
9eab365d 1338 } else {
014adac1
AR
1339 // Optimization: Nearly all debugs() calls get here; avoid allocations
1340 static Context *topContext = new Context(1, 1);
1341 topContext->rewind(section, level);
1342 future = topContext;
9eab365d 1343 }
014adac1
AR
1344
1345 Current = future;
1346
1347 return future->buf;
9eab365d 1348}
1349
9eab365d 1350void
014adac1 1351Debug::Finish()
af6a12ee 1352{
61be1d8e
AR
1353 const LoggingSectionGuard sectionGuard;
1354
ccfbe8f4
AR
1355 // TODO: #include "base/CodeContext.h" instead if doing so works well.
1356 extern std::ostream &CurrentCodeContextDetail(std::ostream &os);
1357 if (Current->level <= DBG_IMPORTANT)
1358 Current->buf << CurrentCodeContextDetail;
1359
61be1d8e
AR
1360 if (Current->waitingForIdle) {
1361 const auto past = Current;
1362 Current = past->upper;
1363 past->upper = nullptr;
1364 // do not delete `past` because we store it in WaitingForIdle below
1365
1366 // waitingForIdle messages are queued here instead of Start() because
1367 // their correct order is determined by the Finish() call timing/order.
1368 // Linear search, but this list ought to be very short (usually empty).
1369 auto *last = &WaitingForIdle;
1370 while (*last)
1371 last = &(*last)->upper;
1372 *last = past;
1373
1374 return;
1375 }
1376
1377 LogMessage(*Current);
e863656d 1378 Current->forceAlert = false;
014adac1
AR
1379
1380 Context *past = Current;
1381 Current = past->upper;
1382 if (Current)
1383 delete past;
1384 // else it was a static topContext from Debug::Start()
f95fe6ed 1385}
1386
e863656d
CT
1387void
1388Debug::ForceAlert()
1389{
1390 // the ForceAlert(ostream) manipulator should only be used inside debugs()
1391 if (Current)
1392 Current->forceAlert = true;
1393}
1394
1395std::ostream&
1396ForceAlert(std::ostream& s)
1397{
1398 Debug::ForceAlert();
1399 return s;
1400}
1401