From 8540ec2446d1fda22904ace548570b73d91f4726 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sun, 19 Jan 2014 10:46:00 +0100 Subject: [PATCH] Save previous session's debug syslog during boot Add boot loader debug menu option "Save syslog from previous session during boot". If enabled (defaults to true), the previous session's debug syslog data is copy to a separate buffer and passed to the kernel, which writes it back to the file /var/log/previous_syslog. As long as Haiku still boots, this should now be the most convenient way to retrieve the output from a kernel crash. --- headers/private/kernel/boot/kernel_args.h | 13 +++++ src/system/boot/loader/main.cpp | 2 + src/system/boot/loader/menu.cpp | 26 +++++++-- src/system/boot/platform/bios_ia32/debug.cpp | 17 ++++++ src/system/kernel/debug/debug.cpp | 56 ++++++++++++++++---- 5 files changed, 102 insertions(+), 12 deletions(-) diff --git a/headers/private/kernel/boot/kernel_args.h b/headers/private/kernel/boot/kernel_args.h index 1ebd5c1ef4..6f608641b3 100644 --- a/headers/private/kernel/boot/kernel_args.h +++ b/headers/private/kernel/boot/kernel_args.h @@ -82,8 +82,21 @@ typedef struct kernel_args { FixedWidthPointer edid_info; FixedWidthPointer debug_output; + // If keep_debug_output_buffer, points to a ring_buffer, else to a + // simple flat buffer. In either case it stores the debug output from + // the boot loader. + FixedWidthPointer previous_debug_output; + // A flat pointer to a buffer containing the debug output from the + // previous session. May be NULL. uint32 debug_size; + // If keep_debug_output_buffer, the size of the ring buffer, otherwise + // the size of the flat buffer debug_output points to. + uint32 previous_debug_size; + // The size of the buffer previous_debug_output points to. Used as a + // boolean indicator whether to save the previous session's debug output + // until initialized for the kernel. bool keep_debug_output_buffer; + // If true, debug_output is a ring buffer, otherwise a flat buffer. platform_kernel_args platform_args; arch_kernel_args arch_args; diff --git a/src/system/boot/loader/main.cpp b/src/system/boot/loader/main.cpp index c0b8f31490..e0555dcdc4 100644 --- a/src/system/boot/loader/main.cpp +++ b/src/system/boot/loader/main.cpp @@ -39,6 +39,8 @@ main(stage2_args *args) // set debug syslog default #if KDEBUG_ENABLE_DEBUG_SYSLOG gKernelArgs.keep_debug_output_buffer = true; + gKernelArgs.previous_debug_size = true; + // used as a boolean indicator until initialized for the kernel #endif add_stage2_driver_settings(args); diff --git a/src/system/boot/loader/menu.cpp b/src/system/boot/loader/menu.cpp index 8f35f19ce6..9f91397f73 100644 --- a/src/system/boot/loader/menu.cpp +++ b/src/system/boot/loader/menu.cpp @@ -1026,6 +1026,14 @@ debug_menu_toggle_debug_syslog(Menu* menu, MenuItem* item) } +static bool +debug_menu_toggle_previous_debug_syslog(Menu* menu, MenuItem* item) +{ + gKernelArgs.previous_debug_size = item->IsMarked(); + return true; +} + + static bool debug_menu_save_previous_syslog(Menu* menu, MenuItem* item) { @@ -1280,6 +1288,20 @@ add_debug_menu() item->SetHelpText("Enables a special in-memory syslog buffer for this " "session that the boot loader will be able to access after rebooting."); + ring_buffer* syslogBuffer + = (ring_buffer*)gKernelArgs.debug_output.Pointer(); + bool hasPreviousSyslog + = syslogBuffer != NULL && ring_buffer_readable(syslogBuffer) > 0; + if (hasPreviousSyslog) { + menu->AddItem(item = new(nothrow) MenuItem( + "Save syslog from previous session during boot")); + item->SetType(MENU_ITEM_MARKABLE); + item->SetMarked(gKernelArgs.previous_debug_size); + item->SetTarget(&debug_menu_toggle_previous_debug_syslog); + item->SetHelpText("Saves the syslog from the previous Haiku session to " + "/var/log/previous_syslog when booting."); + } + bool currentLogItemVisible = platform_debug_get_log_buffer(NULL) != NULL; if (currentLogItemVisible) { menu->AddSeparatorItem(); @@ -1291,9 +1313,7 @@ add_debug_menu() "Displays the debug info the boot loader has logged."); } - ring_buffer* syslogBuffer - = (ring_buffer*)gKernelArgs.debug_output.Pointer(); - if (syslogBuffer != NULL && ring_buffer_readable(syslogBuffer) > 0) { + if (hasPreviousSyslog) { if (!currentLogItemVisible) menu->AddSeparatorItem(); diff --git a/src/system/boot/platform/bios_ia32/debug.cpp b/src/system/boot/platform/bios_ia32/debug.cpp index 39cfc1f94b..9bbc6dc708 100644 --- a/src/system/boot/platform/bios_ia32/debug.cpp +++ b/src/system/boot/platform/bios_ia32/debug.cpp @@ -172,6 +172,23 @@ void debug_cleanup(void) { if (sDebugSyslogBuffer != NULL) { + // If desired, store the debug syslog data from the previous session for + // the kernel. + size_t bytesReadable = 0; + if (gKernelArgs.previous_debug_size != 0) { + bytesReadable = ring_buffer_readable(sDebugSyslogBuffer); + gKernelArgs.previous_debug_size = bytesReadable; + } + + if (bytesReadable != 0) { + if (uint8* buffer = (uint8*)kernel_args_malloc(bytesReadable)) { + ring_buffer_read(sDebugSyslogBuffer, buffer, bytesReadable); + gKernelArgs.previous_debug_output = buffer; + } else + gKernelArgs.previous_debug_size = 0; + } + + // Prepare the debug syslog buffer for this session. size_t signatureLength = strlen(kDebugSyslogSignature); void* buffer = (void*)ROUNDDOWN((addr_t)sDebugSyslogBuffer, B_PAGE_SIZE); diff --git a/src/system/kernel/debug/debug.cpp b/src/system/kernel/debug/debug.cpp index da74437805..7e9266dd39 100644 --- a/src/system/kernel/debug/debug.cpp +++ b/src/system/kernel/debug/debug.cpp @@ -13,8 +13,17 @@ #include "blue_screen.h" +#include +#include +#include +#include +#include +#include +#include + #include +#include #include #include #include @@ -39,13 +48,6 @@ #include -#include -#include -#include -#include -#include -#include - #include "debug_builtin_commands.h" #include "debug_commands.h" #include "debug_output_filter.h" @@ -106,6 +108,9 @@ static size_t sSyslogDebuggerOffset = 0; // (relative) buffer offset of the kernel debugger messages of the current // KDL session +static void* sPreviousSessionSyslogBuffer = NULL; +static size_t sPreviousSessionSyslogBufferSize = 0; + static const char* sCurrentKernelDebuggerMessagePrefix; static const char* sCurrentKernelDebuggerMessage; static va_list sCurrentKernelDebuggerMessageArgs; @@ -1406,6 +1411,17 @@ syslog_init_post_vm(struct kernel_args* args) args->debug_size, false); } + // Allocate memory for the previous session's debug syslog output. In + // syslog_init_post_modules() we'll write it back to disk and free it. + if (args->previous_debug_output != NULL) { + sPreviousSessionSyslogBuffer = malloc(args->previous_debug_size); + if (sPreviousSessionSyslogBuffer != NULL) { + sPreviousSessionSyslogBufferSize = args->previous_debug_size; + memcpy(sPreviousSessionSyslogBuffer, args->previous_debug_output, + sPreviousSessionSyslogBufferSize); + } + } + char revisionBuffer[64]; length = snprintf(revisionBuffer, sizeof(revisionBuffer), "Welcome to syslog debug output!\nHaiku revision: %s\n", @@ -1429,6 +1445,28 @@ err1: return status; } +static void +syslog_init_post_modules() +{ + if (sPreviousSessionSyslogBuffer == NULL) + return; + + void* buffer = sPreviousSessionSyslogBuffer; + size_t bufferSize = sPreviousSessionSyslogBufferSize; + sPreviousSessionSyslogBuffer = NULL; + sPreviousSessionSyslogBufferSize = 0; + MemoryDeleter bufferDeleter(buffer); + + int fd = open("/var/log/previous_syslog", O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) { + dprintf("Failed to open previous syslog file: %s\n", strerror(errno)); + return; + } + + write(fd, buffer, bufferSize); + close(fd); +} static status_t syslog_init(struct kernel_args* args) @@ -1713,7 +1751,7 @@ debug_init_post_settings(struct kernel_args* args) void debug_init_post_modules(struct kernel_args* args) { - void* cookie; + syslog_init_post_modules(); // check for dupped lines every 10/10 second register_kernel_daemon(check_pending_repeats, NULL, 10); @@ -1724,7 +1762,7 @@ debug_init_post_modules(struct kernel_args* args) static const char* kDemanglePrefix = "debugger/demangle/"; - cookie = open_module_list("debugger"); + void* cookie = open_module_list("debugger"); uint32 count = 0; while (count < kMaxDebuggerModules) { char name[B_FILE_NAME_LENGTH];