LLDB mainline
ProcessFreeBSDKernelCore.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "lldb/Core/Module.h"
12#include "lldb/Symbol/Type.h"
15#include "lldb/Utility/Log.h"
17
21
22using namespace lldb;
23using namespace lldb_private;
24
26
27namespace {
28
29#define LLDB_PROPERTIES_processfreebsdkernelcore
30#include "ProcessFreeBSDKernelCoreProperties.inc"
31
32enum {
33#define LLDB_PROPERTIES_processfreebsdkernelcore
34#include "ProcessFreeBSDKernelCorePropertiesEnum.inc"
35};
36
37class PluginProperties : public Properties {
38public:
39 static llvm::StringRef GetSettingName() {
41 }
42
43 PluginProperties() : Properties() {
44 m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
45 m_collection_sp->Initialize(g_processfreebsdkernelcore_properties_def);
46 }
47
48 ~PluginProperties() override = default;
49
50 bool GetReadOnly() const {
51 const uint32_t idx = ePropertyReadOnly;
52 return GetPropertyAtIndexAs<bool>(idx, true);
53 }
54};
55
56} // namespace
57
58static PluginProperties &GetGlobalPluginProperties() {
59 static PluginProperties g_settings;
60 return g_settings;
61}
62
64 ListenerSP listener_sp,
65 kvm_t *kvm,
66 const FileSpec &core_file)
67 : PostMortemProcess(target_sp, listener_sp, core_file), m_kvm(kvm) {}
68
73
75 lldb::TargetSP target_sp, ListenerSP listener_sp,
76 const FileSpec *crash_file, bool can_connect) {
77 ModuleSP executable = target_sp->GetExecutableModule();
78 if (crash_file && !can_connect && executable) {
79 kvm_t *kvm =
80 kvm_open2(executable->GetFileSpec().GetPath().c_str(),
81 crash_file->GetPath().c_str(), O_RDONLY, nullptr, nullptr);
82 if (kvm)
83 return std::make_shared<ProcessFreeBSDKernelCore>(target_sp, listener_sp,
84 kvm, *crash_file);
85 }
86 return nullptr;
87}
88
90 static llvm::once_flag g_once_flag;
91
92 llvm::call_once(g_once_flag, []() {
96 });
97}
98
101 debugger, PluginProperties::GetSettingName())) {
102 const bool is_global_setting = true;
105 "Properties for the freebsd-kernel process plug-in.",
106 is_global_setting);
107 }
108}
109
113
115 bool plugin_specified_by_name) {
116 return true;
117}
118
120 // The core is already loaded by CreateInstance().
121 return Status();
122}
123
130
132
139
141 const void *buf, size_t size,
142 Status &error) {
143 if (GetGlobalPluginProperties().GetReadOnly()) {
145 "Memory writes are disabled in read-only mode. Disable with 'settings "
146 "set plugin.process.freebsd-kernel-core.read-only false'");
147 return 0;
148 }
149
150 ssize_t rd = 0;
151 rd = kvm_write(m_kvm, addr, buf, size);
152 if (rd < 0 || static_cast<size_t>(rd) != size) {
153 error = Status::FromErrorStringWithFormat("Writing memory failed: %s",
154 GetError());
155 return rd > 0 ? rd : 0;
156 }
157 return rd;
158}
159
161 ThreadList &new_thread_list) {
162 if (old_thread_list.GetSize(false) == 0) {
163 // Make up the thread the first time this is called so we can set our one
164 // and only core thread state up.
165
166 // We cannot construct a thread without a register context as that crashes
167 // LLDB but we can construct a process without threads to provide minimal
168 // memory reading support.
169 switch (GetTarget().GetArchitecture().GetMachine()) {
170 case llvm::Triple::aarch64:
171 case llvm::Triple::x86:
172 case llvm::Triple::x86_64:
173 break;
174 default:
175 return false;
176 }
177
179
180 // struct field offsets are written as symbols so that we don't have
181 // to figure them out ourselves
182 int32_t offset_p_list = ReadSignedIntegerFromMemory(
183 FindSymbol("proc_off_p_list"), 4, -1, error);
184 int32_t offset_p_pid =
185 ReadSignedIntegerFromMemory(FindSymbol("proc_off_p_pid"), 4, -1, error);
186 int32_t offset_p_threads = ReadSignedIntegerFromMemory(
187 FindSymbol("proc_off_p_threads"), 4, -1, error);
188 int32_t offset_p_comm = ReadSignedIntegerFromMemory(
189 FindSymbol("proc_off_p_comm"), 4, -1, error);
190
191 int32_t offset_td_tid = ReadSignedIntegerFromMemory(
192 FindSymbol("thread_off_td_tid"), 4, -1, error);
193 int32_t offset_td_plist = ReadSignedIntegerFromMemory(
194 FindSymbol("thread_off_td_plist"), 4, -1, error);
195 int32_t offset_td_pcb = ReadSignedIntegerFromMemory(
196 FindSymbol("thread_off_td_pcb"), 4, -1, error);
197 int32_t offset_td_oncpu = ReadSignedIntegerFromMemory(
198 FindSymbol("thread_off_td_oncpu"), 4, -1, error);
199 int32_t offset_td_name = ReadSignedIntegerFromMemory(
200 FindSymbol("thread_off_td_name"), 4, -1, error);
201
202 // Fail if we were not able to read any of the offsets.
203 if (offset_p_list == -1 || offset_p_pid == -1 || offset_p_threads == -1 ||
204 offset_p_comm == -1 || offset_td_tid == -1 || offset_td_plist == -1 ||
205 offset_td_pcb == -1 || offset_td_oncpu == -1 || offset_td_name == -1)
206 return false;
207
208 // dumptid contains the thread-id of the crashing thread
209 // dumppcb contains its PCB
210 int32_t dumptid =
211 ReadSignedIntegerFromMemory(FindSymbol("dumptid"), 4, -1, error);
212 lldb::addr_t dumppcb = FindSymbol("dumppcb");
213
214 // stoppcbs is an array of PCBs on all CPUs.
215 // Each element is of size pcb_size.
216 int32_t pcbsize =
217 ReadSignedIntegerFromMemory(FindSymbol("pcb_size"), 4, -1, error);
218 lldb::addr_t stoppcbs = FindSymbol("stoppcbs");
219
220 // from FreeBSD sys/param.h
221 constexpr size_t fbsd_maxcomlen = 19;
222
223 // Iterate through a linked list of all processes. New processes are added
224 // to the head of this list. Which means that earlier PIDs are actually at
225 // the end of the list, so we have to walk it backwards. First collect all
226 // the processes in the list order.
227 std::vector<lldb::addr_t> process_addrs;
228 for (lldb::addr_t proc =
230 proc != 0 && proc != LLDB_INVALID_ADDRESS;
231 proc = ReadPointerFromMemory(proc + offset_p_list, error)) {
232 process_addrs.push_back(proc);
233 }
234
235 // Processes are in the linked list in descending PID order, so we must walk
236 // them in reverse to get ascending PID order.
237 for (auto proc_it = process_addrs.rbegin(); proc_it != process_addrs.rend();
238 ++proc_it) {
239 lldb::addr_t proc = *proc_it;
240 int32_t pid =
241 ReadSignedIntegerFromMemory(proc + offset_p_pid, 4, -1, error);
242 // process' command-line string
243 char comm[fbsd_maxcomlen + 1];
244 ReadCStringFromMemory(proc + offset_p_comm, comm, sizeof(comm), error);
245
246 // Iterate through a linked list of all process' threads
247 // the initial thread is found in process' p_threads, subsequent
248 // elements are linked via td_plist field
249 for (lldb::addr_t td =
250 ReadPointerFromMemory(proc + offset_p_threads, error);
251 td != 0; td = ReadPointerFromMemory(td + offset_td_plist, error)) {
252 int32_t tid =
253 ReadSignedIntegerFromMemory(td + offset_td_tid, 4, -1, error);
254 lldb::addr_t pcb_addr =
255 ReadPointerFromMemory(td + offset_td_pcb, error);
256 // whether process was on CPU (-1 if not, otherwise CPU number)
257 int32_t oncpu =
258 ReadSignedIntegerFromMemory(td + offset_td_oncpu, 4, -2, error);
259 // thread name
260 char thread_name[fbsd_maxcomlen + 1];
261 ReadCStringFromMemory(td + offset_td_name, thread_name,
262 sizeof(thread_name), error);
263
264 // If we failed to read TID, ignore this thread.
265 if (tid == -1)
266 continue;
267
268 std::string thread_desc = llvm::formatv("(pid {0}) {1}", pid, comm);
269 if (*thread_name && strcmp(thread_name, comm)) {
270 thread_desc += '/';
271 thread_desc += thread_name;
272 }
273
274 // Roughly:
275 // 1. if the thread crashed, its PCB is going to be at "dumppcb"
276 // 2. if the thread was on CPU, its PCB is going to be on the CPU
277 // 3. otherwise, its PCB is in the thread struct
278 if (tid == dumptid) {
279 // NB: dumppcb can be LLDB_INVALID_ADDRESS if reading it failed
280 pcb_addr = dumppcb;
281 thread_desc += " (crashed)";
282 } else if (oncpu != -1) {
283 // If we managed to read stoppcbs and pcb_size, use them to find
284 // the correct PCB.
285 if (stoppcbs != LLDB_INVALID_ADDRESS && pcbsize > 0)
286 pcb_addr = stoppcbs + oncpu * pcbsize;
287 else
288 pcb_addr = LLDB_INVALID_ADDRESS;
289 thread_desc += llvm::formatv(" (on CPU {0})", oncpu);
290 }
291
292 auto thread =
293 new ThreadFreeBSDKernelCore(*this, tid, pcb_addr, thread_desc);
294
295 if (tid == dumptid)
296 thread->SetIsCrashedThread(true);
297
298 new_thread_list.AddThread(static_cast<ThreadSP>(thread));
299 }
300 }
301 } else {
302 const uint32_t num_threads = old_thread_list.GetSize(false);
303 for (uint32_t i = 0; i < num_threads; ++i)
304 new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false));
305 }
306 return new_thread_list.GetSize(false) > 0;
307}
308
310 size_t size, Status &error) {
311 ssize_t rd = 0;
312 rd = kvm_read2(m_kvm, addr, buf, size);
313 if (rd < 0 || static_cast<size_t>(rd) != size) {
314 error = Status::FromErrorStringWithFormat("Reading memory failed: %s",
315 GetError());
316 return rd > 0 ? rd : 0;
317 }
318 return rd;
319}
320
323 const Symbol *sym = mod_sp->FindFirstSymbolWithNameAndType(ConstString(name));
324 return sym ? sym->GetLoadAddress(&GetTarget()) : LLDB_INVALID_ADDRESS;
325}
326
328 Target &target = GetTarget();
329 Debugger &debugger = target.GetDebugger();
330
331 if (!debugger.GetCommandInterpreter().IsInteractive())
332 return;
333
335
336 // Find msgbufp symbol (pointer to message buffer)
337 lldb::addr_t msgbufp_addr = FindSymbol("msgbufp");
338 if (msgbufp_addr == LLDB_INVALID_ADDRESS)
339 return;
340
341 // Read the pointer value
342 lldb::addr_t msgbufp = ReadPointerFromMemory(msgbufp_addr, error);
343 if (!error.Success() || msgbufp == LLDB_INVALID_ADDRESS)
344 return;
345
346 // Get the type information for struct msgbuf from DWARF
347 TypeQuery query("msgbuf");
348 TypeResults results;
349 target.GetImages().FindTypes(nullptr, query, results);
350
351 uint64_t offset_msg_ptr = 0;
352 uint64_t offset_msg_size = 0;
353 uint64_t offset_msg_wseq = 0;
354 uint64_t offset_msg_rseq = 0;
355
356 if (results.GetTypeMap().GetSize() > 0) {
357 // Found type info - use it to get field offsets
358 CompilerType msgbuf_type =
359 results.GetTypeMap().GetTypeAtIndex(0)->GetForwardCompilerType();
360
361 uint32_t num_fields = msgbuf_type.GetNumFields();
362 int field_found = 0;
363 for (uint32_t i = 0; i < num_fields; i++) {
364 std::string field_name;
365 uint64_t field_offset = 0;
366
367 msgbuf_type.GetFieldAtIndex(i, field_name, &field_offset, nullptr,
368 nullptr);
369
370 if (field_name == "msg_ptr") {
371 offset_msg_ptr = field_offset / 8; // Convert bits to bytes
372 field_found++;
373 } else if (field_name == "msg_size") {
374 offset_msg_size = field_offset / 8;
375 field_found++;
376 } else if (field_name == "msg_wseq") {
377 offset_msg_wseq = field_offset / 8;
378 field_found++;
379 } else if (field_name == "msg_rseq") {
380 offset_msg_rseq = field_offset / 8;
381 field_found++;
382 }
383 }
384
385 if (field_found != 4) {
386 LLDB_LOGF(
388 "FreeBSD-Kernel-Core: Could not find all required fields for msgbuf");
389 return;
390 }
391 } else {
392 // Fallback: use hardcoded offsets based on struct layout
393 // struct msgbuf layout (from sys/sys/msgbuf.h):
394 // char *msg_ptr; - offset 0
395 // u_int msg_magic; - offset ptr_size
396 // u_int msg_size; - offset ptr_size + 4
397 // u_int msg_wseq; - offset ptr_size + 8
398 // u_int msg_rseq; - offset ptr_size + 12
399 uint32_t ptr_size = GetAddressByteSize();
400 offset_msg_ptr = 0;
401 offset_msg_size = ptr_size + 4;
402 offset_msg_wseq = ptr_size + 8;
403 offset_msg_rseq = ptr_size + 12;
404 }
405
406 // Read struct msgbuf fields
407 lldb::addr_t bufp = ReadPointerFromMemory(msgbufp + offset_msg_ptr, error);
408 if (!error.Success() || bufp == LLDB_INVALID_ADDRESS)
409 return;
410
411 uint32_t size =
412 ReadUnsignedIntegerFromMemory(msgbufp + offset_msg_size, 4, 0, error);
413 if (!error.Success() || size == 0)
414 return;
415
416 uint32_t wseq =
417 ReadUnsignedIntegerFromMemory(msgbufp + offset_msg_wseq, 4, 0, error);
418 if (!error.Success())
419 return;
420
421 uint32_t rseq =
422 ReadUnsignedIntegerFromMemory(msgbufp + offset_msg_rseq, 4, 0, error);
423 if (!error.Success())
424 return;
425
426 // Convert sequences to positions
427 // MSGBUF_SEQ_TO_POS macro in FreeBSD: ((seq) % (size))
428 uint32_t rseq_pos = rseq % size;
429 uint32_t wseq_pos = wseq % size;
430
431 if (rseq_pos == wseq_pos)
432 return;
433
434 // Print crash info at once using stream
435 lldb::StreamSP stream_sp = debugger.GetAsyncOutputStream();
436 if (!stream_sp)
437 return;
438
439 stream_sp->PutCString("\nUnread portion of the kernel message buffer:\n");
440
441 // Read ring buffer in at most two chunks
442 if (rseq_pos < wseq_pos) {
443 // No wrap: read from rseq_pos to wseq_pos
444 size_t len = wseq_pos - rseq_pos;
445 std::string buf(len, '\0');
446 size_t bytes_read = ReadMemory(bufp + rseq_pos, &buf[0], len, error);
447 if (error.Success() && bytes_read > 0) {
448 buf.resize(bytes_read);
449 *stream_sp << buf;
450 }
451 } else {
452 // Wrap around: read from rseq_pos to end, then from start to wseq_pos
453 size_t len1 = size - rseq_pos;
454 std::string buf1(len1, '\0');
455 size_t bytes_read1 = ReadMemory(bufp + rseq_pos, &buf1[0], len1, error);
456 if (error.Success() && bytes_read1 > 0) {
457 buf1.resize(bytes_read1);
458 *stream_sp << buf1;
459 }
460
461 if (wseq_pos > 0) {
462 std::string buf2(wseq_pos, '\0');
463 size_t bytes_read2 = ReadMemory(bufp, &buf2[0], wseq_pos, error);
464 if (error.Success() && bytes_read2 > 0) {
465 buf2.resize(bytes_read2);
466 *stream_sp << buf2;
467 }
468 }
469 }
470
471 stream_sp->PutChar('\n');
472 stream_sp->Flush();
473}
474
475const char *ProcessFreeBSDKernelCore::GetError() { return kvm_geterr(m_kvm); }
static llvm::raw_ostream & error(Stream &strm)
static PluginProperties & GetGlobalPluginProperties()
#define LLDB_LOGF(log,...)
Definition Log.h:376
#define LLDB_PLUGIN_DEFINE(PluginName)
static PluginProperties & GetGlobalPluginProperties()
static llvm::StringRef GetPluginNameStatic()
lldb_private::Status DoDestroy() override
static llvm::StringRef GetPluginNameStatic()
lldb::addr_t FindSymbol(const char *name)
size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, lldb_private::Status &error) override
Actually do the reading of memory from a process.
static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener, const lldb_private::FileSpec *crash_file_path, bool can_connect)
void RefreshStateAfterStop() override
Currently called as part of ShouldStop.
static void DebuggerInitialize(lldb_private::Debugger &debugger)
static llvm::StringRef GetPluginDescriptionStatic()
lldb_private::Status DoLoadCore() override
lldb_private::DynamicLoader * GetDynamicLoader() override
Get the dynamic loader plug-in for this process.
ProcessFreeBSDKernelCore(lldb::TargetSP target_sp, lldb::ListenerSP listener, kvm_t *kvm, const lldb_private::FileSpec &core_file)
size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size, lldb_private::Status &error) override
Actually do the writing of memory to a process.
bool DoUpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list) override
Update the thread list following process plug-in's specific logic.
bool CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) override
Check if a plug-in instance can debug the file in module.
Generic representation of a type in a programming language.
CompilerType GetFieldAtIndex(size_t idx, std::string &name, uint64_t *bit_offset_ptr, uint32_t *bitfield_bit_size_ptr, bool *is_bitfield_ptr) const
uint32_t GetNumFields() const
A uniqued constant string class.
Definition ConstString.h:40
CommandInterpreter & GetCommandInterpreter()
Definition Debugger.h:173
lldb::StreamUP GetAsyncOutputStream()
static DynamicLoader * FindPlugin(Process *process, llvm::StringRef plugin_name)
Find a dynamic loader plugin for a given process.
A file utility class.
Definition FileSpec.h:57
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition FileSpec.cpp:374
void FindTypes(Module *search_first, const TypeQuery &query, lldb_private::TypeResults &results) const
Find types using a type-matching object that contains all search parameters.
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, ABICreateInstance create_callback)
static bool CreateSettingForProcessPlugin(Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp, llvm::StringRef description, bool is_global_property)
static lldb::OptionValuePropertiesSP GetSettingForProcessPlugin(Debugger &debugger, llvm::StringRef setting_name)
static bool UnregisterPlugin(ABICreateInstance create_callback)
PostMortemProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const FileSpec &core_file)
int64_t ReadSignedIntegerFromMemory(lldb::addr_t load_addr, size_t byte_size, int64_t fail_value, Status &error)
Definition Process.cpp:2356
size_t ReadCStringFromMemory(lldb::addr_t vm_addr, char *cstr, size_t cstr_max_len, Status &error)
Read a NULL terminated C string from memory.
Definition Process.cpp:2226
lldb::DynamicLoaderUP m_dyld_up
Definition Process.h:3424
virtual size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error)
Read of memory from a process.
Definition Process.cpp:1940
uint64_t ReadUnsignedIntegerFromMemory(lldb::addr_t load_addr, size_t byte_size, uint64_t fail_value, Status &error)
Reads an unsigned integer of the specified byte size from process memory.
Definition Process.cpp:2345
lldb::addr_t ReadPointerFromMemory(lldb::addr_t vm_addr, Status &error)
Definition Process.cpp:2367
friend class Target
Definition Process.h:360
uint32_t GetAddressByteSize() const
Definition Process.cpp:3758
friend class DynamicLoader
Definition Process.h:357
friend class Debugger
Definition Process.h:356
friend class ThreadList
Definition Process.h:361
Target & GetTarget()
Get the target object pointer for this module.
Definition Process.h:1267
lldb::OptionValuePropertiesSP GetValueProperties() const
An error handling class.
Definition Status.h:118
static Status FromErrorStringWithFormat(const char *format,...) __attribute__((format(printf
Definition Status.cpp:106
static Status FromErrorString(const char *str)
Definition Status.h:141
lldb::addr_t GetLoadAddress(Target *target) const
Definition Symbol.cpp:504
Debugger & GetDebugger() const
Definition Target.h:1224
lldb::ModuleSP GetExecutableModule()
Gets the module for the main executable.
Definition Target.cpp:1525
const ModuleList & GetImages() const
Get accessor for the images for this process.
Definition Target.h:1141
void AddThread(const lldb::ThreadSP &thread_sp)
uint32_t GetSize(bool can_update=true)
lldb::ThreadSP GetThreadAtIndex(uint32_t idx, bool can_update=true)
uint32_t GetSize() const
Definition TypeMap.cpp:75
lldb::TypeSP GetTypeAtIndex(uint32_t idx)
Definition TypeMap.cpp:83
A class that contains all state required for type lookups.
Definition Type.h:104
This class tracks the state and results of a TypeQuery.
Definition Type.h:344
TypeMap & GetTypeMap()
Definition Type.h:386
#define LLDB_INVALID_ADDRESS
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:332
std::shared_ptr< lldb_private::Thread > ThreadSP
std::shared_ptr< lldb_private::Stream > StreamSP
std::shared_ptr< lldb_private::Process > ProcessSP
std::shared_ptr< lldb_private::Listener > ListenerSP
uint64_t addr_t
Definition lldb-types.h:80
std::shared_ptr< lldb_private::Target > TargetSP
std::shared_ptr< lldb_private::Module > ModuleSP