Author: Minsoo Choo Date: 2026-02-21T18:46:17-05:00 New Revision: 9d9c7fc00bdddd72e78b19e9fbb6af9d07c2218b
URL: https://github.com/llvm/llvm-project/commit/9d9c7fc00bdddd72e78b19e9fbb6af9d07c2218b DIFF: https://github.com/llvm/llvm-project/commit/9d9c7fc00bdddd72e78b19e9fbb6af9d07c2218b.diff LOG: [lldb][Process/FreeBSDKernel] Print unread message buffer on start (#178027) This is equivalent of kgdb_dmesg() in fbsd-kvm.c in FreeBSD kgdb(1) port. Unread kernel messages is only printed in interactive mode (i.e. not in batch mode) to mimic KGDB's behaviour. Example output: ``` ➜ sudo ./build/bin/lldb /boot/kernel/kernel -c /var/crash/vmcore.last (lldb) target create "/boot/kernel/kernel" --core "/var/crash/vmcore.last" Unread portion of the kernel message buffer: panic: kdb_sysctl_panic cpuid = 1 time = 1769364579 KDB: stack backtrace: db_trace_self_wrapper() at db_trace_self_wrapper+0x2b/frame 0xfffffe01b435fa20 vpanic() at vpanic+0x136/frame 0xfffffe01b435fb50 panic() at panic+0x43/frame 0xfffffe01b435fbb0 kdb_sysctl_panic() at kdb_sysctl_panic+0x63/frame 0xfffffe01b435fbe0 sysctl_root_handler_locked() at sysctl_root_handler_locked+0x9c/frame 0xfffffe01b435fc30 sysctl_root() at sysctl_root+0x22f/frame 0xfffffe01b435fcb0 userland_sysctl() at userland_sysctl+0x196/frame 0xfffffe01b435fd50 sys___sysctl() at sys___sysctl+0x65/frame 0xfffffe01b435fe00 amd64_syscall() at amd64_syscall+0x169/frame 0xfffffe01b435ff30 fast_syscall_common() at fast_syscall_common+0xf8/frame 0xfffffe01b435ff30 --- syscall (202, FreeBSD ELF64, __sysctl), rip = 0x3f67cad1c8da, rsp = 0x3f67c80261d8, rbp = 0x3f67c8026220 --- KDB: enter: panic Core file '/var/crash/vmcore.last' (x86_64) was loaded. (lldb) ``` --------- Signed-off-by: Minsoo Choo <[email protected]> Added: Modified: lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.h llvm/docs/ReleaseNotes.md Removed: ################################################################################ diff --git a/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp b/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp index beb7e52adf49e..31b5c05805ed0 100644 --- a/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp +++ b/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp @@ -6,9 +6,15 @@ // //===----------------------------------------------------------------------===// +#include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/Type.h" #include "lldb/Target/DynamicLoader.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" #include "Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.h" #include "ProcessFreeBSDKernel.h" @@ -65,7 +71,12 @@ bool ProcessFreeBSDKernel::CanDebug(lldb::TargetSP target_sp, return true; } -void ProcessFreeBSDKernel::RefreshStateAfterStop() {} +void ProcessFreeBSDKernel::RefreshStateAfterStop() { + if (!m_printed_unread_message) { + PrintUnreadMessage(); + m_printed_unread_message = true; + } +} bool ProcessFreeBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) { @@ -234,6 +245,153 @@ lldb::addr_t ProcessFreeBSDKernel::FindSymbol(const char *name) { return sym ? sym->GetLoadAddress(&GetTarget()) : LLDB_INVALID_ADDRESS; } +void ProcessFreeBSDKernel::PrintUnreadMessage() { + Target &target = GetTarget(); + Debugger &debugger = target.GetDebugger(); + + if (!debugger.GetCommandInterpreter().IsInteractive()) + return; + + Status error; + + // Find msgbufp symbol (pointer to message buffer) + lldb::addr_t msgbufp_addr = FindSymbol("msgbufp"); + if (msgbufp_addr == LLDB_INVALID_ADDRESS) + return; + + // Read the pointer value + lldb::addr_t msgbufp = ReadPointerFromMemory(msgbufp_addr, error); + if (!error.Success() || msgbufp == LLDB_INVALID_ADDRESS) + return; + + // Get the type information for struct msgbuf from DWARF + TypeQuery query("msgbuf"); + TypeResults results; + target.GetImages().FindTypes(nullptr, query, results); + + uint64_t offset_msg_ptr = 0; + uint64_t offset_msg_size = 0; + uint64_t offset_msg_wseq = 0; + uint64_t offset_msg_rseq = 0; + + if (results.GetTypeMap().GetSize() > 0) { + // Found type info - use it to get field offsets + CompilerType msgbuf_type = + results.GetTypeMap().GetTypeAtIndex(0)->GetForwardCompilerType(); + + uint32_t num_fields = msgbuf_type.GetNumFields(); + int field_found = 0; + for (uint32_t i = 0; i < num_fields; i++) { + std::string field_name; + uint64_t field_offset = 0; + + msgbuf_type.GetFieldAtIndex(i, field_name, &field_offset, nullptr, + nullptr); + + if (field_name == "msg_ptr") { + offset_msg_ptr = field_offset / 8; // Convert bits to bytes + field_found++; + } else if (field_name == "msg_size") { + offset_msg_size = field_offset / 8; + field_found++; + } else if (field_name == "msg_wseq") { + offset_msg_wseq = field_offset / 8; + field_found++; + } else if (field_name == "msg_rseq") { + offset_msg_rseq = field_offset / 8; + field_found++; + } + } + + if (field_found != 4) { + LLDB_LOGF(GetLog(LLDBLog::Object), + "FreeBSDKernel: Could not find all required fields for msgbuf"); + return; + } + } else { + // Fallback: use hardcoded offsets based on struct layout + // struct msgbuf layout (from sys/sys/msgbuf.h): + // char *msg_ptr; - offset 0 + // u_int msg_magic; - offset ptr_size + // u_int msg_size; - offset ptr_size + 4 + // u_int msg_wseq; - offset ptr_size + 8 + // u_int msg_rseq; - offset ptr_size + 12 + uint32_t ptr_size = GetAddressByteSize(); + offset_msg_ptr = 0; + offset_msg_size = ptr_size + 4; + offset_msg_wseq = ptr_size + 8; + offset_msg_rseq = ptr_size + 12; + } + + // Read struct msgbuf fields + lldb::addr_t bufp = ReadPointerFromMemory(msgbufp + offset_msg_ptr, error); + if (!error.Success() || bufp == LLDB_INVALID_ADDRESS) + return; + + uint32_t size = + ReadUnsignedIntegerFromMemory(msgbufp + offset_msg_size, 4, 0, error); + if (!error.Success() || size == 0) + return; + + uint32_t wseq = + ReadUnsignedIntegerFromMemory(msgbufp + offset_msg_wseq, 4, 0, error); + if (!error.Success()) + return; + + uint32_t rseq = + ReadUnsignedIntegerFromMemory(msgbufp + offset_msg_rseq, 4, 0, error); + if (!error.Success()) + return; + + // Convert sequences to positions + // MSGBUF_SEQ_TO_POS macro in FreeBSD: ((seq) % (size)) + uint32_t rseq_pos = rseq % size; + uint32_t wseq_pos = wseq % size; + + if (rseq_pos == wseq_pos) + return; + + // Print crash info at once using stream + lldb::StreamSP stream_sp = debugger.GetAsyncOutputStream(); + if (!stream_sp) + return; + + stream_sp->PutCString("\nUnread portion of the kernel message buffer:\n"); + + // Read ring buffer in at most two chunks + if (rseq_pos < wseq_pos) { + // No wrap: read from rseq_pos to wseq_pos + size_t len = wseq_pos - rseq_pos; + std::string buf(len, '\0'); + size_t bytes_read = ReadMemory(bufp + rseq_pos, &buf[0], len, error); + if (error.Success() && bytes_read > 0) { + buf.resize(bytes_read); + *stream_sp << buf; + } + } else { + // Wrap around: read from rseq_pos to end, then from start to wseq_pos + size_t len1 = size - rseq_pos; + std::string buf1(len1, '\0'); + size_t bytes_read1 = ReadMemory(bufp + rseq_pos, &buf1[0], len1, error); + if (error.Success() && bytes_read1 > 0) { + buf1.resize(bytes_read1); + *stream_sp << buf1; + } + + if (wseq_pos > 0) { + std::string buf2(wseq_pos, '\0'); + size_t bytes_read2 = ReadMemory(bufp, &buf2[0], wseq_pos, error); + if (error.Success() && bytes_read2 > 0) { + buf2.resize(bytes_read2); + *stream_sp << buf2; + } + } + } + + stream_sp->PutChar('\n'); + stream_sp->Flush(); +} + size_t ProcessFreeBSDKernel::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Status &error) { ssize_t rd = 0; diff --git a/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.h b/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.h index d933f7bc219f2..f6b5260e37991 100644 --- a/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.h +++ b/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.h @@ -58,9 +58,13 @@ class ProcessFreeBSDKernel : public lldb_private::PostMortemProcess { lldb::addr_t FindSymbol(const char *name); private: - kvm_t *m_kvm; + void PrintUnreadMessage(); const char *GetError(); + + bool m_printed_unread_message = false; + + kvm_t *m_kvm; }; #endif // LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_PROCESSFREEBSDKERNEL_H diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md index c92ea9bba065f..77990611f6085 100644 --- a/llvm/docs/ReleaseNotes.md +++ b/llvm/docs/ReleaseNotes.md @@ -216,6 +216,8 @@ Changes to LLDB from any platform. * The crashed thread is now automatically selected on start. * Threads are listed in incrmental order by pid then by tid. +* Unread kernel messages saved in msgbufp are now printed when lldb starts. This information is printed only + when lldb is in the interactive mode (i.e. not in batch mode). Changes to BOLT --------------- _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
