#include "Log.h" #include "arch/CPU.h" #include "arch/Serial.h" #include "arch/Timer.h" #include "video/TextConsole.h" #include #include #include 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]); auto* time = Timer::monotonic_clock(); 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 { 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 { 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(); } 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(); }