[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 <minsoochoo0122@proton.me>
This commit is contained in:
Minsoo Choo 2026-02-21 18:46:17 -05:00 committed by GitHub
parent fdce869d72
commit 9d9c7fc00b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 166 additions and 2 deletions

View File

@ -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;

View File

@ -58,9 +58,13 @@ protected:
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

View File

@ -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
---------------