Luna/kernel/src/Log.cpp
apio 59713279a0
All checks were successful
Build and test / build (push) Successful in 1m33s
kernel: Add a hexdump() method to log binary data for debugging
2024-03-29 12:12:56 +01:00

198 lines
5.2 KiB
C++

#include "Log.h"
#include "arch/CPU.h"
#include "arch/Serial.h"
#include "thread/Clock.h"
#include "video/TextConsole.h"
#include <luna/Format.h>
#include <luna/SourceLocation.h>
#include <luna/Spinlock.h>
#include <luna/StringBuilder.h>
static bool g_debug_enabled = true;
static bool g_serial_enabled = true;
static bool g_text_console_enabled = false;
static bool g_text_console_initialized = false;
static constexpr u32 BLACK = 0xff000000;
static constexpr u32 WHITE = 0xffffffff;
static constexpr u32 YELLOW = 0xffffff00;
static constexpr u32 RED = 0xffff0000;
static char log_level_letters[] = { 'D', 'I', 'W', 'E' }; // D for debug, I for info, W for warning, E for error
static const char* ansi_color_codes_per_log_level[] = { "37", "37", "33", "31" }; // 37 is white, 33 yellow, 31 red
Spinlock g_serial_lock;
static void log_serial(LogLevel level, const char* format, va_list origin)
{
va_list ap;
va_copy(ap, origin);
/*const SafeScopeLock lock { g_serial_lock };
if (!lock.did_succeed()) return;*/
Serial::printf("\x1b[%sm"
"%c"
"\x1b[0m ",
ansi_color_codes_per_log_level[(int)level], log_level_letters[(int)level]);
struct timespec time;
g_monotonic_clock.get_time(time);
Serial::printf("%4zu.%.3zu ", time.tv_sec, time.tv_nsec / 1'000'000);
// NOTE: We do this manually because of a lack of vprintf() in both Serial and TextConsole.
cstyle_format(
format,
[](char c, void*) -> Result<void> {
Serial::putchar((u8)c);
return {};
},
nullptr, ap)
.value();
Serial::putchar('\n');
va_end(ap);
}
Spinlock g_console_lock;
static void log_text_console(LogLevel level, const char* format, va_list origin)
{
va_list ap;
va_copy(ap, origin);
/*const ScopeLock lock { g_console_lock };*/
const u32 original_foreground = TextConsole::foreground();
const u32 original_background = TextConsole::background();
TextConsole::set_background(BLACK);
if (level == LogLevel::Warn) TextConsole::set_foreground(YELLOW);
else if (level == LogLevel::Error)
TextConsole::set_foreground(RED);
else
TextConsole::set_foreground(WHITE);
// NOTE: Same as above.
auto rc = cstyle_format(
format, [](char c, void*) -> Result<void> { return TextConsole::putchar(c); }, nullptr, ap);
if (rc.has_error()) { TextConsole::wprint(L"Invalid UTF-8 in log message"); }
TextConsole::putwchar(L'\n');
TextConsole::set_background(original_background);
TextConsole::set_foreground(original_foreground);
va_end(ap);
}
void vlog(LogLevel level, const char* format, va_list ap)
{
if (!g_debug_enabled && level == LogLevel::Debug) return;
if (g_serial_enabled) log_serial(level, format, ap);
if (g_text_console_enabled) log_text_console(level, format, ap);
}
void log(LogLevel level, const char* format, ...)
{
va_list ap;
va_start(ap, format);
vlog(level, format, ap);
va_end(ap);
}
// for luna/DebugLog.h
void debug_log_impl(const char* format, va_list ap)
{
vlog(LogLevel::Debug, format, ap);
}
void setup_log(bool enable_debug, bool enable_serial, bool enable_text_console)
{
g_debug_enabled = enable_debug;
g_serial_enabled = enable_serial;
g_text_console_enabled = enable_text_console;
if (enable_text_console) g_text_console_initialized = true;
}
bool log_debug_enabled()
{
return g_debug_enabled;
}
bool log_serial_enabled()
{
return g_serial_enabled;
}
bool log_text_console_enabled()
{
return g_text_console_enabled;
}
void set_text_console_initialized()
{
g_text_console_initialized = true;
}
static bool g_check_already_failed = false;
[[noreturn]] bool __check_failed(SourceLocation location, const char* expr)
{
CPU::disable_interrupts();
if (g_text_console_initialized) g_text_console_enabled = true;
if (!g_check_already_failed)
{ // Avoid endlessly failing when trying to report a failed check.
g_check_already_failed = true;
kerrorln("-- KERNEL PANIC: Check failed at %s:%d, in %s: %s --", location.file(), location.line(),
location.function(), expr);
CPU::print_stack_trace();
kerrorln("-- END KERNEL PANIC --");
}
CPU::efficient_halt();
}
[[noreturn]] void __critical_error_handler(SourceLocation location, const char* expr, const char* failmsg,
const char* errmsg)
{
CPU::disable_interrupts();
if (g_text_console_initialized) g_text_console_enabled = true;
if (!g_check_already_failed)
{ // Avoid endlessly failing when trying to report a failed check.
g_check_already_failed = true;
kerrorln("-- KERNEL PANIC: Critical routine \"%s\" failed in %s: %s (%s) --", expr, location.file(), failmsg,
errmsg);
CPU::print_stack_trace();
kerrorln("-- END KERNEL PANIC --");
}
CPU::efficient_halt();
}
Result<void> hexdump(void* data, usize size)
{
StringBuilder sb;
u8* ptr = (u8*)data;
while (size)
{
TRY(sb.format("%#2x ", *ptr));
ptr++;
size--;
}
auto message = TRY(sb.string());
kdbgln("hexdump: %s", message.chars());
return {};
}