The Pedigree Project 0.1
Log.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/BootstrapInfo.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/machine/Machine.h"
24#include "pedigree/kernel/machine/Serial.h"
25#include "pedigree/kernel/machine/Timer.h"
26#include "pedigree/kernel/panic.h"
27#include "pedigree/kernel/process/Scheduler.h"
28#include "pedigree/kernel/process/TerminationDeferral.h"
29#include "pedigree/kernel/process/Thread.h"
30#include "pedigree/kernel/processor/Processor.h"
31#include "pedigree/kernel/processor/ProcessorInformation.h"
32#include "pedigree/kernel/time/Time.h"
33#include "pedigree/kernel/utilities/Cord.h"
34#include "pedigree/kernel/utilities/StaticCord.h"
35#include "pedigree/kernel/utilities/String.h"
36#include "pedigree/kernel/utilities/StringView.h"
37#include "pedigree/kernel/utilities/Vector.h"
38#include "pedigree/kernel/utilities/utility.h"
39
40extern BootstrapStruct_t* g_pBootstrapInfo;
41
43#define LOG_MAX_DEDUPE_MESSAGES 20
44
46static constexpr bool LogTimestampsInNanos = false;
47
49EXPORTED_PUBLIC BootProgressUpdateFn g_BootProgressUpdate = 0;
50EXPORTED_PUBLIC size_t g_BootProgressTotal = 0;
51EXPORTED_PUBLIC size_t g_BootProgressCurrent = 0;
52
54TinyStaticString Log::m_NoticeSeverityString("(NN) ");
55TinyStaticString Log::m_WarningSeverityString("(WW) ");
56TinyStaticString Log::m_ErrorSeverityString("(EE) ");
57TinyStaticString Log::m_FatalSeverityString("(FF) ");
58
59#if !SERIAL_IS_FILE
61#else
63#endif
64
65NormalStaticString Log::m_DedupeHead("(last message+severity repeated ");
66TinyStaticString Log::m_DedupeTail(" times)");
67
68// Lookup tables to not do int->str conversions every repeated log message
69static const char* g_RepeatedStrings[] = {
70 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
71 "11", "12", "13", "14", "15", "16", "17", "18", "19", "20",
72};
73
74static size_t g_RepeatedLengths[] = {
75 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
76};
77
78static const size_t g_NumRepeatedStrings = 20;
79
80#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
81Log::CallbackPinHook Log::m_CallbackPinHook = nullptr;
82Log::EntrySnapshotHook Log::m_EntrySnapshotHook = nullptr;
83#endif
84
86 : m_Lock(),
87 m_StaticLog(),
88 m_StaticEntries(0),
89 m_StaticEntryStart(0),
90 m_StaticEntryEnd(0),
91 m_EchoToSerial(LOG_TO_SERIAL),
92 m_CallbackWaiters(),
93 m_OutputCallbacks(),
94 m_ActiveCallbackPins(nullptr),
95 m_nOutputCallbacks(0),
96 m_LastEntryHash(0),
97 m_LastEntrySeverity(Fatal),
98 m_HashMatchedCount(0),
99 m_Timestamps(true),
100 m_LastTime(0) {
101 for (size_t i = 0; i < LOG_CALLBACK_COUNT; ++i) {
102 m_OutputCallbacks[i].callback = nullptr;
103 m_OutputCallbacks[i].inFlight = 0;
104 m_OutputCallbacks[i].removers = 0;
105 m_OutputCallbacks[i].enabled = false;
106 }
107}
108
110 // can't render a timestamp, by the time we're shutting down here the
111 // machine instance is gone
112 LogEntry entry;
113 entry << NoTimestamp << Notice << "-- Log Terminating --";
114 addEntry(entry);
115}
116
118 return m_Instance;
119}
120
122 char* cmdline = g_pBootstrapInfo->getCommandLine();
123 if (cmdline) {
124 Vector<String> cmds = String(cmdline).tokenise(' ');
125 for (auto it = cmds.begin(); it != cmds.end(); it++) {
126 auto& cmd = *it;
127 if (cmd == String("--disable-log-to-serial")) {
128 m_EchoToSerial = false;
129 break;
130 } else if (cmd == String("--enable-log-to-serial")) {
131 m_EchoToSerial = true;
132 break;
133 }
134 }
135 }
136}
137
139 EMIT_IF(LOG_TO_SERIAL) {
140 if (m_EchoToSerial)
141 installSerialLogger();
142 }
143}
144
145bool Log::installCallback(LogCallback* pCallback, bool bSkipBacklog) {
146 if (!pCallback) {
147 return false;
148 }
149
150 CallbackSlot* slot = nullptr;
151 size_t entry = 0;
152 size_t remaining = 0;
153 {
154 // Registration and its backlog boundary are one transaction with
155 // entry publication. An entry is therefore delivered either through
156 // the captured backlog or through normal dispatch, never neither.
157 LockGuard<Spinlock> logGuard(m_Lock);
158 auto callbackGuard = m_CallbackWaiters.acquire();
159 for (size_t i = 0; i < LOG_CALLBACK_COUNT; ++i) {
160 if (m_OutputCallbacks[i].callback == pCallback) {
161 return false;
162 }
163 }
164
165 for (size_t i = 0; i < LOG_CALLBACK_COUNT; ++i) {
166 if (!m_OutputCallbacks[i].callback) {
167 slot = &m_OutputCallbacks[i];
168 slot->callback = pCallback;
169 slot->inFlight = 0;
170 slot->removers = 0;
171 slot->enabled = true;
172 ++m_nOutputCallbacks;
173 break;
174 }
175 }
176
177 if (!slot) {
178 return false;
179 }
180
181 entry = m_StaticEntryStart;
182 remaining = m_StaticEntries;
183 }
184
185 // Some callbacks want to skip a (potentially) massive backlog
186 if (bSkipBacklog)
187 return true;
188
189 // Call the callback for the existing, flushed, log entries
190 while (remaining--) {
191 StaticLogEntry backlogEntry;
192 NormalStaticString timestamp;
193 bool timestamps = false;
194 {
196 backlogEntry = m_StaticLog[entry];
197 timestamps = m_Timestamps;
198 if (timestamps) {
199 timestamp = getTimestamp();
200 }
201 }
202
203 if (backlogEntry.str.length()) {
204 LogCord msg;
205 msg.append("(backlog) ", 10);
206
207 const TinyStaticString& severity = severityToString(backlogEntry.severity);
208 msg.append(severity, severity.length());
209 if (timestamps && timestamp.length()) {
210 msg.append(timestamp, timestamp.length());
211 }
212 msg.append(backlogEntry.str, backlogEntry.str.length());
213 msg.append(m_LineEnding, m_LineEnding.length());
214 bool locked = !backlogEntry.lockfree;
215
219 dispatchCallback(slot, msg, locked);
220 }
221
222 entry = (entry + 1) % LOG_ENTRIES;
223 }
224
225 return true;
226}
227
229 if (!pCallback) {
230 return true;
231 }
232
233 TerminationDeferral terminationDeferral;
234#if THREADS
235 Thread* current = Processor::information().getCurrentThread();
236 const bool canYield = current && Processor::getInterrupts();
237#else
238 const bool canYield = false;
239#endif
240 CallbackSlot* slot = nullptr;
241 bool removerRegistered = false;
242
243 while (true) {
244 auto guard = m_CallbackWaiters.acquire();
245 if (!slot) {
246 for (size_t i = 0; i < LOG_CALLBACK_COUNT; ++i) {
247 if (m_OutputCallbacks[i].callback == pCallback) {
248 slot = &m_OutputCallbacks[i];
249 break;
250 }
251 }
252
253 if (!slot) {
254 return true;
255 }
256 }
257
258 if (slot->enabled) {
259 slot->enabled = false;
260 if (m_nOutputCallbacks) {
261 --m_nOutputCallbacks;
262 }
263 }
264
265 if (!slot->inFlight) {
266 if (removerRegistered && slot->removers) {
267 --slot->removers;
268 }
269 if (!slot->removers) {
270 clearCallback(slot);
271 }
272 return true;
273 }
274
275 if (isCallbackContext(currentCallbackOwner())) {
276 // Snapshot dispatch can pin callbacks which have not run yet. Treat any
277 // live target as peer-owned here so false always preserves retry state.
278 return false;
279 }
280
281 if (!canYield) {
282 return false;
283 }
284
285 if (!removerRegistered) {
286 ++slot->removers;
287 removerRegistered = true;
288 }
289
290#if THREADS
291 const WaitQueue::WakeReason reason = guard.waitForCompletion(
292 WaitQueue::Channel(slot), Thread::CallbackDrain, reinterpret_cast<uintptr_t>(pCallback));
293 (void)reason;
294#else
295 return false;
296#endif
297 }
298}
299
300void* Log::currentCallbackOwner() {
301#if UTILITY_LINUX
302 // Standalone tools have host threads even with kernel threading disabled.
303 static thread_local char owner;
304 return &owner;
305#elif THREADS
306 ProcessorInformation& information = Processor::information();
307 Thread* thread = information.getCurrentThread();
308 return thread ? static_cast<void*>(thread) : static_cast<void*>(&information);
309#else
310 return nullptr;
311#endif
312}
313
314bool Log::isCallbackContext(void* owner) {
315 for (CallbackPin* pin = m_ActiveCallbackPins; pin; pin = pin->next) {
316 if (pin->owner == owner) {
317 return true;
318 }
319 }
320 return false;
321}
322
323void Log::clearCallback(CallbackSlot* slot) {
324 slot->callback = nullptr;
325 slot->inFlight = 0;
326 slot->removers = 0;
327 slot->enabled = false;
328}
329
330size_t Log::snapshotCallbacks(CallbackPin pins[LOG_CALLBACK_COUNT]) {
331 auto guard = m_CallbackWaiters.acquire();
332 void* owner = currentCallbackOwner();
333 // Rendering can itself log an error. Keep that entry in the ring without
334 // recursively entering renderers which may already hold their own locks.
335 if (isCallbackContext(owner)) {
336 return 0;
337 }
338 size_t count = 0;
339 for (size_t i = 0; i < LOG_CALLBACK_COUNT; ++i) {
340 CallbackSlot* slot = &m_OutputCallbacks[i];
341 if (!slot->callback || !slot->enabled) {
342 continue;
343 }
344
345 CallbackPin& pin = pins[count++];
346 pin.slot = slot;
347 pin.callback = slot->callback;
348 pin.owner = owner;
349 pin.next = m_ActiveCallbackPins;
350 m_ActiveCallbackPins = &pin;
351 ++slot->inFlight;
352 }
353 return count;
354}
355
356bool Log::pinCallback(CallbackSlot* slot, CallbackPin& pin) {
357 auto guard = m_CallbackWaiters.acquire();
358 void* owner = currentCallbackOwner();
359 if (!slot->callback || !slot->enabled || isCallbackContext(owner)) {
360 return false;
361 }
362
363 pin.slot = slot;
364 pin.callback = slot->callback;
365 pin.owner = owner;
366 pin.next = m_ActiveCallbackPins;
367 m_ActiveCallbackPins = &pin;
368 ++slot->inFlight;
369 return true;
370}
371
372bool Log::callbackEnabled(const CallbackPin& pin) {
373 auto guard = m_CallbackWaiters.acquire();
374 return pin.slot->enabled && pin.slot->callback == pin.callback;
375}
376
377void Log::releaseCallback(CallbackPin& pin) {
378 auto guard = m_CallbackWaiters.acquire();
379
380 CallbackPin** cursor = &m_ActiveCallbackPins;
381 while (*cursor && *cursor != &pin) {
382 cursor = &((*cursor)->next);
383 }
384 if (*cursor == &pin) {
385 *cursor = pin.next;
386 }
387
388 CallbackSlot* slot = pin.slot;
389 if (slot->inFlight) {
390 --slot->inFlight;
391 }
392
393 if (!slot->enabled && !slot->inFlight) {
394 if (slot->removers) {
395 guard.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(slot));
396 }
397 }
398
399 pin.slot = nullptr;
400 pin.callback = nullptr;
401 pin.owner = nullptr;
402 pin.next = nullptr;
403}
404
405void Log::dispatchCallbacks(CallbackPin pins[LOG_CALLBACK_COUNT], size_t count,
406 const LogCord& message, bool locked) {
407 for (size_t i = 0; i < count; ++i) {
408 CallbackPin& pin = pins[i];
409 if (callbackEnabled(pin)) {
410#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
411 CallbackPinHook hook = __atomic_load_n(&m_CallbackPinHook, __ATOMIC_ACQUIRE);
412 if (hook) {
413 hook(pin.callback);
414 }
415#endif
416 pin.callback->callback(message, locked);
417 }
418 releaseCallback(pin);
419 }
420}
421
422void Log::dispatchCallback(CallbackSlot* slot, const LogCord& message, bool locked) {
423 CallbackPin pin = {};
424 if (!pinCallback(slot, pin)) {
425 return;
426 }
427
428#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
429 CallbackPinHook hook = __atomic_load_n(&m_CallbackPinHook, __ATOMIC_ACQUIRE);
430 if (hook) {
431 hook(pin.callback);
432 }
433#endif
434 pin.callback->callback(message, locked);
435 releaseCallback(pin);
436}
437
439 return m_StaticEntries;
440}
441
443 return 0;
444}
445
447 return m_StaticLog[(m_StaticEntryStart + n) % LOG_ENTRIES];
448}
451 return m_StaticLog[0];
452}
453
454size_t Log::copyText(char* buffer, size_t capacity) {
455 if (!capacity)
456 return 0;
457
459 size_t length = 0;
460 for (size_t i = 0; i < m_StaticEntries; ++i)
461 length += getStaticEntry(i).str.length() + 4;
462
463 // Linux READ_ALL keeps the newest bytes when the caller's buffer is short.
464 size_t skip = length > capacity ? length - capacity : 0;
465 size_t copied = 0;
466 for (size_t i = 0; i < m_StaticEntries; ++i) {
467 const auto& entry = getStaticEntry(i);
468 static const char levels[] = {'7', '5', '4', '3', '2'};
469 const char prefix[] = {'<', levels[entry.severity], '>'};
470 const char* parts[] = {prefix, static_cast<const char*>(entry.str), "\n"};
471 const size_t lengths[] = {sizeof(prefix), entry.str.length(), 1};
472 for (size_t part = 0; part < 3; ++part) {
473 if (skip >= lengths[part]) {
474 skip -= lengths[part];
475 continue;
476 }
477 const size_t bytes = lengths[part] - skip;
478 MemoryCopy(buffer + copied, parts[part] + skip, bytes);
479 copied += bytes;
480 skip = 0;
481 }
482 }
483 return copied;
484}
485
486bool Log::echoToSerial() {
487 return m_EchoToSerial;
488}
489
490const Log::LogEntry& Log::getLatestEntry() const {
491 return m_StaticLog[m_StaticEntries - 1];
492}
493
494Log::LogEntry::LogEntry() : timestamp(), severity(), str(), numberType(Dec) {
496}
497
499 str.append(s);
500 return *this;
501}
502
504 str.appendBytes(s.cstr(), s.length());
505 return *this;
506}
507
509 str.appendBytes(s.str(), s.length());
510 return *this;
511}
512
514 for (auto it = c.segbegin(); it != c.segend(); ++it) {
515 str.appendBytes(it.ptr(), it.length());
516 }
517 return *this;
518}
519
521 str.appendBytes(s, s.length());
522 return *this;
523}
524
526 str.appendBytes(s, s.length());
527 return *this;
528}
529
531 str.appendBytes(s, s.length());
532 return *this;
533}
534
536 str.appendBytes(s, s.length());
537 return *this;
538}
539
541 return (*this) << (reinterpret_cast<const char*>(append_str));
542}
543
545 if (b)
546 return *this << "true";
547
548 return *this << "false";
549}
550
551template <class T>
553 size_t radix = 10;
554 if (numberType == Hex) {
555 radix = 16;
556 str.append("0x");
557 } else if (numberType == Oct) {
558 radix = 8;
559 str.append("0");
560 }
561 str.append(n, radix);
562 return *this;
563}
564
566 numberType = type;
567 return *this;
568}
569
571 // Zero the buffer.
572 str.clear();
573 severity = level;
574
575 timestamp = 0;
576
577 EMIT_IF(!UTILITY_LINUX) {
578 if (showTimestamp) {
579 Machine& machine = Machine::instance();
580 if (machine.isInitialised() == true && machine.getTimer() != 0) {
581 Timer& timer = *machine.getTimer();
582 timestamp = timer.getTickCount();
583 }
584 }
585 }
586
587 return *this;
588}
589
591 if (modifier == Unlocked) {
592 lockfree = true;
593 } else if (modifier == NoTimestamp) {
594 showTimestamp = false;
595 }
596 return *this;
597}
598
599// NOTE: Make sure that the templated << operator gets only instantiated for
600// integer types.
602template Log::LogEntry& Log::LogEntry::operator<<(unsigned char);
604template Log::LogEntry& Log::LogEntry::operator<<(unsigned short);
606template Log::LogEntry& Log::LogEntry::operator<<(unsigned int);
608template Log::LogEntry& Log::LogEntry::operator<<(unsigned long);
609template Log::LogEntry& Log::LogEntry::operator<<(long long);
610template Log::LogEntry& Log::LogEntry::operator<<(unsigned long long);
611
612void Log::addEntry(const LogEntry& source, bool lock) {
613 static bool handlingFatal = false;
614
615 StaticLogEntry entry = source;
616 LogCord msg;
617 TinyStaticString repeated;
618 NormalStaticString timestamp;
619 CallbackPin callbackPins[LOG_CALLBACK_COUNT] = {};
620 msg.clear();
621
622 size_t outputCallbackCount = 0;
623 bool suppressOutput = false;
624 bool wasRepeated = false;
625 uint64_t repeatedTimes = 0;
626 bool showTimestamp = false;
627 bool shouldPanic = false;
628
629 if (lock)
630 m_Lock.acquire();
631
633 m_StaticEntryStart = (m_StaticEntryStart + 1) % LOG_ENTRIES;
634 } else
636
637 m_StaticLog[m_StaticEntryEnd] = entry;
638 m_StaticEntryEnd = (m_StaticEntryEnd + 1) % LOG_ENTRIES;
639
640 outputCallbackCount = snapshotCallbacks(callbackPins);
641 if (outputCallbackCount) {
642 // Have we seen this message before?
643 entry.str.allowHashing(true);
644 uint64_t currentHash = entry.str.hash();
645 entry.str.disableHashing();
646 if (currentHash == m_LastEntryHash) {
647 if (m_LastEntrySeverity == entry.severity) {
649
650 if (m_HashMatchedCount < LOG_MAX_DEDUPE_MESSAGES) {
651 suppressOutput = true;
652 }
653 }
654 }
655
656 if (!suppressOutput) {
657 if (m_HashMatchedCount) {
658 wasRepeated = true;
659 repeatedTimes = m_HashMatchedCount;
661 }
662
663 m_LastEntryHash = currentHash;
664 m_LastEntrySeverity = entry.severity;
665 showTimestamp = m_Timestamps;
666 if (showTimestamp) {
667 timestamp = getTimestamp();
668 }
669 }
670 }
671
672 if (!suppressOutput && !handlingFatal && entry.severity == Fatal) {
673 handlingFatal = true;
674 shouldPanic = true;
675 }
676
677 if (lock)
678 m_Lock.release();
679
680#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
681 EntrySnapshotHook snapshotHook = __atomic_load_n(&m_EntrySnapshotHook, __ATOMIC_ACQUIRE);
682 if (snapshotHook) {
683 snapshotHook(entry);
684 }
685#endif
686
687 if (outputCallbackCount && !suppressOutput) {
688 if (wasRepeated) {
689 msg.append(m_DedupeHead, m_DedupeHead.length());
690 if (repeatedTimes < g_NumRepeatedStrings) {
691 msg.append(g_RepeatedStrings[repeatedTimes], g_RepeatedLengths[repeatedTimes]);
692 } else {
693 repeated.append(repeatedTimes);
694 msg.append(repeated, repeated.length());
695 }
696 msg.append(m_DedupeTail, m_DedupeTail.length());
697 msg.append(m_LineEnding, m_LineEnding.length());
698 }
699
700 const TinyStaticString& severity = severityToString(entry.severity);
701 msg.append(severity, severity.length());
702 if (showTimestamp && timestamp.length()) {
703 msg.append(timestamp, timestamp.length());
704 }
705 msg.append(entry.str, entry.str.length());
706 msg.append(m_LineEnding, m_LineEnding.length());
707 dispatchCallbacks(callbackPins, outputCallbackCount, msg, !entry.lockfree);
708 } else {
709 for (size_t i = 0; i < outputCallbackCount; ++i) {
710 releaseCallback(callbackPins[i]);
711 }
712 }
713
714 // Panic if that was a fatal error.
715 if (shouldPanic) {
716 const char* panicstr = static_cast<const char*>(entry.str);
717
718 // Fatal errors must remain visible even when normal serial logging is off
719 // and the debugger cannot quiesce the other processors.
720 EMIT_IF(!UTILITY_LINUX) {
721 if (Machine::instance().getNumSerial()) {
722 Serial* serial = Machine::instance().getSerial(0);
723 if (serial) {
724 serial->write_str("\nFATAL before debugger: ");
725 serial->write_str(panicstr);
726 serial->write_str("\n");
727 }
728 }
729 }
730
731 // Attempt to trap to debugger, panic if that fails.
732 EMIT_IF(DEBUGGER) {
734 }
735 panic(panicstr);
736 }
737}
738
739void Log::enableTimestamps() {
741 m_Timestamps = true;
742}
743
744void Log::disableTimestamps() {
746 m_Timestamps = false;
747}
748
749#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
750void Log::setCallbackPinHook(CallbackPinHook hook) {
751 __atomic_store_n(&m_CallbackPinHook, hook, __ATOMIC_RELEASE);
752}
753
754void Log::setEntrySnapshotHook(EntrySnapshotHook hook) {
755 __atomic_store_n(&m_EntrySnapshotHook, hook, __ATOMIC_RELEASE);
756}
757#endif
758
759const NormalStaticString& Log::getTimestamp() {
760 Time::Timestamp tn = Time::getTimeNanoseconds();
761 Time::Timestamp ts = Time::getTime();
762 Time::Timestamp t;
763 EMIT_IF(LogTimestampsInNanos) {
764 t = tn;
765 }
766 else {
767 t = ts;
768 }
769 if (t == m_LastTime) {
770 return m_CachedTimestamp;
771 }
772
773 m_LastTime = t;
774
776 r += "[";
777 r.append(t);
778 r += ".";
779 r.append(Processor::id());
780 r += "] ";
781
783 return m_CachedTimestamp;
784}
785
786const TinyStaticString& Log::severityToString(SeverityLevel level) const {
787 switch (level) {
788 case Debug:
790 case Notice:
791 return m_NoticeSeverityString;
792 case Warning:
793 return m_WarningSeverityString;
794 case Error:
795 return m_ErrorSeverityString;
796 default:
797 return m_FatalSeverityString;
798 }
799}
800
801Log::LogCallback::~LogCallback() = default;
Definition Cord.h:33
the kernel's log
Definition Log.h:155
StaticLogEntry m_StaticLog[LOG_ENTRIES]
Definition Log.h:356
~Log()
Definition Log.cc:109
Spinlock m_Lock
Definition Log.h:175
size_t getStaticEntryCount() const
Definition Log.cc:438
size_t m_HashMatchedCount
Definition Log.h:383
void initialise1()
Definition Log.cc:121
bool m_EchoToSerial
Definition Log.h:365
NormalStaticString m_CachedTimestamp
Definition Log.h:392
static TinyStaticString m_DebugSeverityString
Definition Log.h:395
SeverityLevel m_LastEntrySeverity
Definition Log.h:380
void initialise2()
Definition Log.cc:138
EXPORTED_PUBLIC bool removeCallback(LogCallback *pCallback)
Definition Log.cc:228
EXPORTED_PUBLIC void addEntry(const LogEntry &entry, bool lock=true)
Definition Log.cc:612
const StaticLogEntry & getStaticEntry(size_t n) const
Definition Log.cc:446
static TinyStaticString m_LineEnding
Definition Log.h:402
static NormalStaticString m_DedupeHead
Definition Log.h:405
size_t getDynamicEntryCount() const
Definition Log.cc:442
Time::Timestamp m_LastTime
Definition Log.h:389
static EXPORTED_PUBLIC Log & instance()
Definition Log.cc:117
SeverityLevel
Definition Log.h:170
uint64_t m_LastEntryHash
Definition Log.h:377
EXPORTED_PUBLIC bool installCallback(LogCallback *pCallback, bool bSkipBacklog=false)
Definition Log.cc:145
size_t m_StaticEntries
Definition Log.h:360
bool m_Timestamps
Definition Log.h:386
const DynamicLogEntry & getDynamicEntry(size_t n) const
Definition Log.cc:450
EXPORTED_PUBLIC size_t copyText(char *buffer, size_t capacity)
Definition Log.cc:454
WaitQueue m_CallbackWaiters
Definition Log.h:368
Log()
Definition Log.cc:85
static EXPORTED_PUBLIC Log m_Instance
Definition Log.h:374
virtual Serial * getSerial(size_t n)=0
static bool getInterrupts()
static ProcessorId id()
static ProcessorInformation & information()
static void breakpoint()
void release()
Definition Spinlock.cc:168
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:36
void disableHashing()
virtual uint64_t getTickCount()=0
A vector / dynamic array.
Definition Vector.h:33
#define LOG_ENTRIES
Definition Log.h:117
LogEntryModifier
Definition Log.h:132
#define LOG_CALLBACK_COUNT
Definition Log.h:119
NumberType
Definition Log.h:122
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118
@ Unlocked
Definition Log.h:134
@ NoTimestamp
Definition Log.h:136
@ Oct
Definition Log.h:128
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124
LogEntry & operator<<(const char *)
Definition Log.cc:498
bool lockfree
Definition Log.h:230
SeverityLevel severity
Definition Log.h:224
StaticString< LOG_LENGTH > str
Definition Log.h:226
Definition cmd.h:30