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"
43#define LOG_MAX_DEDUPE_MESSAGES 20
46static constexpr bool LogTimestampsInNanos =
false;
49EXPORTED_PUBLIC BootProgressUpdateFn g_BootProgressUpdate = 0;
50EXPORTED_PUBLIC
size_t g_BootProgressTotal = 0;
51EXPORTED_PUBLIC
size_t g_BootProgressCurrent = 0;
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",
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,
78static const size_t g_NumRepeatedStrings = 20;
80#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
81Log::CallbackPinHook Log::m_CallbackPinHook =
nullptr;
82Log::EntrySnapshotHook Log::m_EntrySnapshotHook =
nullptr;
89 m_StaticEntryStart(0),
91 m_EchoToSerial(LOG_TO_SERIAL),
94 m_ActiveCallbackPins(nullptr),
95 m_nOutputCallbacks(0),
97 m_LastEntrySeverity(Fatal),
98 m_HashMatchedCount(0),
102 m_OutputCallbacks[i].callback =
nullptr;
103 m_OutputCallbacks[i].inFlight = 0;
104 m_OutputCallbacks[i].removers = 0;
105 m_OutputCallbacks[i].enabled =
false;
113 entry <<
NoTimestamp << Notice <<
"-- Log Terminating --";
122 char* cmdline = g_pBootstrapInfo->getCommandLine();
125 for (
auto it = cmds.begin(); it != cmds.end(); it++) {
127 if (
cmd ==
String(
"--disable-log-to-serial")) {
130 }
else if (
cmd ==
String(
"--enable-log-to-serial")) {
141 installSerialLogger();
152 size_t remaining = 0;
160 if (m_OutputCallbacks[i].callback == pCallback) {
166 if (!m_OutputCallbacks[i].callback) {
167 slot = &m_OutputCallbacks[i];
168 slot->callback = pCallback;
171 slot->enabled =
true;
172 ++m_nOutputCallbacks;
181 entry = m_StaticEntryStart;
190 while (remaining--) {
193 bool timestamps =
false;
199 timestamp = getTimestamp();
203 if (backlogEntry.
str.length()) {
205 msg.append(
"(backlog) ", 10);
208 msg.append(severity, severity.length());
209 if (timestamps && timestamp.length()) {
210 msg.append(timestamp, timestamp.length());
212 msg.append(backlogEntry.
str, backlogEntry.
str.length());
214 bool locked = !backlogEntry.
lockfree;
219 dispatchCallback(slot, msg, locked);
238 const bool canYield =
false;
241 bool removerRegistered =
false;
247 if (m_OutputCallbacks[i].callback == pCallback) {
248 slot = &m_OutputCallbacks[i];
259 slot->enabled =
false;
260 if (m_nOutputCallbacks) {
261 --m_nOutputCallbacks;
265 if (!slot->inFlight) {
266 if (removerRegistered && slot->removers) {
269 if (!slot->removers) {
275 if (isCallbackContext(currentCallbackOwner())) {
285 if (!removerRegistered) {
287 removerRegistered =
true;
291 const WaitQueue::WakeReason reason = guard.waitForCompletion(
292 WaitQueue::Channel(slot), Thread::CallbackDrain,
reinterpret_cast<uintptr_t
>(pCallback));
300void* Log::currentCallbackOwner() {
303 static thread_local char owner;
307 Thread* thread = information.getCurrentThread();
308 return thread ?
static_cast<void*
>(thread) : static_cast<void*>(&information);
314bool Log::isCallbackContext(
void* owner) {
315 for (CallbackPin* pin = m_ActiveCallbackPins; pin; pin = pin->next) {
316 if (pin->owner == owner) {
323void Log::clearCallback(CallbackSlot* slot) {
324 slot->callback =
nullptr;
327 slot->enabled =
false;
332 void* owner = currentCallbackOwner();
335 if (isCallbackContext(owner)) {
340 CallbackSlot* slot = &m_OutputCallbacks[i];
341 if (!slot->callback || !slot->enabled) {
345 CallbackPin& pin = pins[count++];
347 pin.callback = slot->callback;
349 pin.next = m_ActiveCallbackPins;
350 m_ActiveCallbackPins = &pin;
356bool Log::pinCallback(CallbackSlot* slot, CallbackPin& pin) {
358 void* owner = currentCallbackOwner();
359 if (!slot->callback || !slot->enabled || isCallbackContext(owner)) {
364 pin.callback = slot->callback;
366 pin.next = m_ActiveCallbackPins;
367 m_ActiveCallbackPins = &pin;
372bool Log::callbackEnabled(
const CallbackPin& pin) {
374 return pin.slot->enabled && pin.slot->callback == pin.callback;
377void Log::releaseCallback(CallbackPin& pin) {
380 CallbackPin** cursor = &m_ActiveCallbackPins;
381 while (*cursor && *cursor != &pin) {
382 cursor = &((*cursor)->next);
384 if (*cursor == &pin) {
388 CallbackSlot* slot = pin.slot;
389 if (slot->inFlight) {
393 if (!slot->enabled && !slot->inFlight) {
394 if (slot->removers) {
400 pin.callback =
nullptr;
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);
416 pin.callback->callback(
message, locked);
418 releaseCallback(pin);
422void Log::dispatchCallback(CallbackSlot* slot,
const LogCord&
message,
bool locked) {
423 CallbackPin pin = {};
424 if (!pinCallback(slot, pin)) {
428#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
429 CallbackPinHook hook = __atomic_load_n(&m_CallbackPinHook, __ATOMIC_ACQUIRE);
434 pin.callback->callback(
message, locked);
435 releaseCallback(pin);
464 size_t skip = length > capacity ? length - capacity : 0;
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];
477 const size_t bytes = lengths[part] - skip;
478 MemoryCopy(buffer + copied, parts[part] + skip, bytes);
486bool Log::echoToSerial() {
504 str.appendBytes(s.cstr(), s.length());
509 str.appendBytes(s.str(), s.length());
514 for (
auto it = c.segbegin(); it != c.segend(); ++it) {
515 str.appendBytes(it.ptr(), it.length());
521 str.appendBytes(s, s.length());
526 str.appendBytes(s, s.length());
531 str.appendBytes(s, s.length());
536 str.appendBytes(s, s.length());
541 return (*
this) << (
reinterpret_cast<const char*
>(append_str));
546 return *
this <<
"true";
548 return *
this <<
"false";
554 if (numberType ==
Hex) {
557 }
else if (numberType ==
Oct) {
561 str.append(n, radix);
579 Machine& machine = Machine::instance();
580 if (machine.isInitialised() ==
true && machine.getTimer() != 0) {
581 Timer& timer = *machine.getTimer();
594 showTimestamp =
false;
613 static bool handlingFatal =
false;
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;
633 m_StaticEntryStart = (m_StaticEntryStart + 1) %
LOG_ENTRIES;
638 m_StaticEntryEnd = (m_StaticEntryEnd + 1) %
LOG_ENTRIES;
640 outputCallbackCount = snapshotCallbacks(callbackPins);
641 if (outputCallbackCount) {
643 entry.str.allowHashing(
true);
644 uint64_t currentHash = entry.str.hash();
645 entry.str.disableHashing();
651 suppressOutput =
true;
656 if (!suppressOutput) {
667 timestamp = getTimestamp();
672 if (!suppressOutput && !handlingFatal && entry.severity == Fatal) {
673 handlingFatal =
true;
680#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
681 EntrySnapshotHook snapshotHook = __atomic_load_n(&m_EntrySnapshotHook, __ATOMIC_ACQUIRE);
687 if (outputCallbackCount && !suppressOutput) {
690 if (repeatedTimes < g_NumRepeatedStrings) {
691 msg.append(g_RepeatedStrings[repeatedTimes], g_RepeatedLengths[repeatedTimes]);
693 repeated.append(repeatedTimes);
694 msg.append(repeated, repeated.length());
696 msg.append(m_DedupeTail, m_DedupeTail.length());
701 msg.append(severity, severity.length());
702 if (showTimestamp && timestamp.length()) {
703 msg.append(timestamp, timestamp.length());
705 msg.append(entry.str, entry.str.length());
707 dispatchCallbacks(callbackPins, outputCallbackCount, msg, !entry.lockfree);
709 for (
size_t i = 0; i < outputCallbackCount; ++i) {
710 releaseCallback(callbackPins[i]);
716 const char* panicstr =
static_cast<const char*
>(entry.str);
721 if (Machine::instance().getNumSerial()) {
724 serial->write_str(
"\nFATAL before debugger: ");
725 serial->write_str(panicstr);
726 serial->write_str(
"\n");
739void Log::enableTimestamps() {
744void Log::disableTimestamps() {
749#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
750void Log::setCallbackPinHook(CallbackPinHook hook) {
751 __atomic_store_n(&m_CallbackPinHook, hook, __ATOMIC_RELEASE);
754void Log::setEntrySnapshotHook(EntrySnapshotHook hook) {
755 __atomic_store_n(&m_EntrySnapshotHook, hook, __ATOMIC_RELEASE);
760 Time::Timestamp tn = Time::getTimeNanoseconds();
761 Time::Timestamp ts = Time::getTime();
763 EMIT_IF(LogTimestampsInNanos) {
791 return m_NoticeSeverityString;
793 return m_WarningSeverityString;
795 return m_ErrorSeverityString;
797 return m_FatalSeverityString;
801Log::LogCallback::~LogCallback() =
default;
StaticLogEntry m_StaticLog[LOG_ENTRIES]
size_t getStaticEntryCount() const
size_t m_HashMatchedCount
NormalStaticString m_CachedTimestamp
static TinyStaticString m_DebugSeverityString
SeverityLevel m_LastEntrySeverity
EXPORTED_PUBLIC bool removeCallback(LogCallback *pCallback)
EXPORTED_PUBLIC void addEntry(const LogEntry &entry, bool lock=true)
const StaticLogEntry & getStaticEntry(size_t n) const
static TinyStaticString m_LineEnding
static NormalStaticString m_DedupeHead
size_t getDynamicEntryCount() const
Time::Timestamp m_LastTime
static EXPORTED_PUBLIC Log & instance()
EXPORTED_PUBLIC bool installCallback(LogCallback *pCallback, bool bSkipBacklog=false)
const DynamicLogEntry & getDynamicEntry(size_t n) const
EXPORTED_PUBLIC size_t copyText(char *buffer, size_t capacity)
WaitQueue m_CallbackWaiters
static EXPORTED_PUBLIC Log m_Instance
virtual Serial * getSerial(size_t n)=0
static bool getInterrupts()
static ProcessorInformation & information()
bool acquire(bool recurse=false, bool safe=true)
virtual uint64_t getTickCount()=0
A vector / dynamic array.
#define LOG_CALLBACK_COUNT
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
LogEntry & operator<<(const char *)
StaticString< LOG_LENGTH > str