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

Reply via email to