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 <optional>
10
11#include "lldb/Core/Module.h"
18#include "lldb/Symbol/Type.h"
21#include "lldb/Utility/Log.h"
23
24#include "llvm/Support/Error.h"
25#include "llvm/Support/SaveAndRestore.h"
26
31
32using namespace lldb;
33using namespace lldb_private;
34
36
37namespace {
38
39#define LLDB_PROPERTIES_processfreebsdkernelcore
40#include "ProcessFreeBSDKernelCoreProperties.inc"
41
42enum {
43#define LLDB_PROPERTIES_processfreebsdkernelcore
44#include "ProcessFreeBSDKernelCorePropertiesEnum.inc"
45};
46
47class PluginProperties : public Properties {
48public:
49 static llvm::StringRef GetSettingName() {
51 }
52
53 PluginProperties() : Properties() {
54 m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
55 m_collection_sp->Initialize(g_processfreebsdkernelcore_properties_def);
56 }
57
58 ~PluginProperties() override = default;
59
60 bool GetReadOnly() const {
61 const uint32_t idx = ePropertyReadOnly;
62 return GetPropertyAtIndexAs<bool>(idx, true);
63 }
64};
65
66} // namespace
67
68static PluginProperties &GetGlobalPluginProperties() {
69 static PluginProperties g_settings;
70 return g_settings;
71}
72
73static bool SetKernelDebugFile(const ModuleSP &module) {
74 if (!module)
75 return false;
76
77 FileSpec object_file = module->GetFileSpec();
78 FileSystem::Instance().Resolve(object_file);
79
80 FileSpec symbol_file = module->GetSymbolFileFileSpec();
81 if (symbol_file)
82 FileSystem::Instance().Resolve(symbol_file);
83 if (symbol_file && symbol_file != object_file)
84 return false;
85
86 auto *elf = llvm::dyn_cast_or_null<ObjectFileELF>(module->GetObjectFile());
87 if (!elf)
88 return false;
89
90 // GetDebugLink() returns information collected while parsing section
91 // headers, which might not have happened when a PT_NOTE supplied the UUID.
92 (void)elf->GetSectionList();
93 std::optional<FileSpec> debug_link = elf->GetDebugLink();
94 if (!debug_link || debug_link->GetFilename().empty())
95 return false;
96
97 // The kernel's linker_files list may require symbols from kernel.debug to
98 // locate. Infer an extracted filesystem's root from the local kernel path
99 // and load its FreeBSD-installed debug file before the dynamic loader runs.
100 std::string object_path = object_file.GetPath();
101 FileSpec sysroot = object_file;
102 while (sysroot.RemoveLastPathComponent()) {
103 std::string sysroot_path = sysroot.GetPath();
104 llvm::StringRef relative_path(object_path);
105 if (!relative_path.consume_front(sysroot_path))
106 continue;
107 relative_path = relative_path.ltrim("/\\");
108
109 FileSpec relative_file(relative_path);
110 FileSpec debug_file = sysroot;
111 debug_file.AppendPathComponent("usr/lib/debug");
112 if (!relative_file.GetDirectory().empty())
113 debug_file.AppendPathComponent(relative_file.GetDirectory());
114 debug_file.AppendPathComponent(debug_link->GetFilename());
115 if (!FileSystem::Instance().Exists(debug_file))
116 continue;
117
118 module->SetSymbolFileFileSpec(debug_file);
119 return true;
120 }
121
122 return false;
123}
124
126 : public CommandObjectParsed {
127public:
129 CommandInterpreter &interpreter)
131 interpreter, "process plugin refresh-threads",
132 "Refresh the thread list from the FreeBSD kernel core. The thread "
133 "list and related data structures may be being read from live "
134 "memory (/dev/mem), which may have changed since the last refresh. "
135 "This command clears LLDB's thread list and memory cache then "
136 "re-reads the kernel's allproc/zombie lists to rebuild the thread "
137 "list from scratch.",
138 "process plugin refresh-threads",
139 eCommandRequiresProcess | eCommandTryTargetAPILock) {}
140
142
143protected:
144 void DoExecute(Args &command, CommandReturnObject &result) override {
145 // TODO: Return early for elf-core based implementation.
146
147 auto process = static_cast<ProcessFreeBSDKernelCore *>(
148 m_interpreter.GetExecutionContext().GetProcessPtr());
149
150 // Clear the memory cache so DoUpdateThreadList() will re-read allproc,
151 // zombproc, and all thread/proc structures fresh from the core dump instead
152 // of getting stale cached values.
153 process->m_memory_cache.Clear();
154
155 // Clear both thread lists to guarantee that UpdateThreadListIfNeeded() sees
156 // size == 0 and enters the rebuild path regardless of stop-ID state.
157 // UpdateThreadListIfNeeded() passes m_thread_list_real as old_thread_list
158 // to DoUpdateThreadList(), and DoUpdateThreadList() only rebuilds from
159 // scratch when old_thread_list is empty. m_thread_list is the public copy
160 // that is sync'd from m_thread_list_real afterwards.
161 process->m_thread_list_real.Clear();
162 process->m_thread_list.Clear();
163
164 // This calls UpdateThreadListIfNeeded() to rebuild the process thread list.
165 const uint32_t num_threads =
166 process->GetThreadList().GetSize(/*can_update=*/true);
168 "Thread list refreshed, {0} thread{1} found.", num_threads,
169 num_threads == 1 ? "" : "s");
171 }
172};
173
175 ListenerSP listener_sp,
176 const FileSpec &core_file)
177 : PostMortemProcess(target_sp, listener_sp, core_file) {}
178
180 m_thread_list.Clear();
181
182 // We need to call finalize on the process before destroying ourselves to
183 // make sure all of the broadcaster cleanup goes as planned. If we destruct
184 // this class, then Process::~Process() might have problems trying to fully
185 // destroy the broadcaster.
186 Finalize(/*destructing=*/true);
187}
188
190 lldb::TargetSP target_sp, ListenerSP listener_sp,
191 const FileSpec *crash_file, bool can_connect) {
192 ModuleSP executable = target_sp->GetExecutableModule();
193 if (crash_file && !can_connect && executable) {
194 char errbuf[_POSIX2_LINE_MAX];
195 kvm_t *kvm = OpenKVM(executable, *crash_file, O_RDONLY, errbuf);
196 if (kvm) {
197 kvm_close(kvm);
198 return std::make_shared<ProcessFreeBSDKernelCore>(target_sp, listener_sp,
199 *crash_file);
200 }
201 LLDB_LOGF(GetLog(LLDBLog::Process), "FreeBSD-Kernel-Core: %s", errbuf);
202 }
203 return nullptr;
204}
205
211
214 debugger, PluginProperties::GetSettingName())) {
215 const bool is_global_setting = true;
218 "Properties for the freebsd-kernel process plug-in.",
219 is_global_setting);
220 }
221}
222
226
228 bool plugin_specified_by_name) {
229 return true;
230}
231
233 if (!m_command_sp) {
234 CommandInterpreter &interp =
236 m_command_sp = std::make_unique<CommandObjectMultiword>(
237 interp, "process plugin",
238 "Commands for the FreeBSD kernel process plug-in.",
239 "process plugin <subcommand> [<subcommand-options>]");
240 m_command_sp->LoadSubCommand(
241 "refresh-threads",
244 }
245 return m_command_sp.get();
246}
247
249 ModuleSP executable = GetTarget().GetExecutableModule();
250 if (!executable)
252 "ProcessFreeBSDKernelCore: no executable module set on target");
253
254 bool symbols_changed = SetKernelDebugFile(executable);
255
256 char errbuf[_POSIX2_LINE_MAX];
257 const int flags =
258 GetGlobalPluginProperties().GetReadOnly() ? O_RDONLY : O_RDWR;
259 m_kvm = OpenKVM(executable, GetCoreFile(), flags, errbuf);
260
261 if (!m_kvm) {
262 LLDB_LOGF(GetLog(LLDBLog::Process), "FreeBSD-Kernel-Core: %s", errbuf);
264 "ProcessFreeBSDKernelCore: kvm_open2 failed for core '%s' "
265 "with kernel '%s'",
266 GetCoreFile().GetPath().c_str(),
267 executable->GetFileSpec().GetPath().c_str());
268 }
269
271
272 if (symbols_changed) {
273 ModuleList modules;
274 modules.Append(executable);
275 GetTarget().SymbolsDidLoad(modules);
276 }
277
278 return Status();
279}
280
287
289 if (!m_kvm)
290 return Status::FromErrorString("kvm file descriptor is not set.");
291
292 kvm_close(m_kvm);
293 return Status();
294}
295
302
304 const void *buf, size_t size,
305 Status &error) {
306 if (GetGlobalPluginProperties().GetReadOnly()) {
308 "Memory writes are currently disabled. You can enable them with "
309 "`settings set plugin.process.freebsd-kernel-core.read-only false`.");
310 return 0;
311 }
312
313 ssize_t rd = 0;
314 rd = kvm_write(m_kvm, addr, buf, size);
315 if (rd < 0 || static_cast<size_t>(rd) != size) {
316 error = Status::FromErrorStringWithFormat("Writing memory failed: %s",
317 GetError());
318 return rd > 0 ? rd : 0;
319 }
320 return rd;
321}
322
324 ThreadList &new_thread_list) {
325 if (old_thread_list.GetSize(false) == 0) {
326 // Make up the thread the first time this is called so we can set our one
327 // and only core thread state up.
328
329 // We cannot construct a thread without a register context as that crashes
330 // LLDB but we can construct a process without threads to provide minimal
331 // memory reading support.
332 switch (GetTarget().GetArchitecture().GetMachine()) {
333 case llvm::Triple::arm:
334 case llvm::Triple::aarch64:
335 case llvm::Triple::ppc64le:
336 case llvm::Triple::riscv64:
337 case llvm::Triple::x86:
338 case llvm::Triple::x86_64:
339 break;
340 default:
341 return false;
342 }
343
345
346 // struct field offsets are written as symbols so that we don't have
347 // to figure them out ourselves
348 // Process-related offsets:
349 int32_t offset_p_list = ReadSignedIntegerFromMemory(
350 FindSymbol("proc_off_p_list"), 4, -1, error);
351 if (error.Fail())
352 return false;
353
354 int32_t offset_p_pid =
355 ReadSignedIntegerFromMemory(FindSymbol("proc_off_p_pid"), 4, -1, error);
356 if (error.Fail())
357 return false;
358
359 int32_t offset_p_threads = ReadSignedIntegerFromMemory(
360 FindSymbol("proc_off_p_threads"), 4, -1, error);
361 if (error.Fail())
362 return false;
363
364 int32_t offset_p_comm = ReadSignedIntegerFromMemory(
365 FindSymbol("proc_off_p_comm"), 4, -1, error);
366 if (error.Fail())
367 return false;
368
369 // Thread-related offsets:
370 int32_t offset_td_tid = ReadSignedIntegerFromMemory(
371 FindSymbol("thread_off_td_tid"), 4, -1, error);
372 if (error.Fail())
373 return false;
374
375 int32_t offset_td_plist = ReadSignedIntegerFromMemory(
376 FindSymbol("thread_off_td_plist"), 4, -1, error);
377 if (error.Fail())
378 return false;
379
380 int32_t offset_td_pcb = ReadSignedIntegerFromMemory(
381 FindSymbol("thread_off_td_pcb"), 4, -1, error);
382 if (error.Fail())
383 return false;
384
385 int32_t offset_td_oncpu = ReadSignedIntegerFromMemory(
386 FindSymbol("thread_off_td_oncpu"), 4, -1, error);
387 if (error.Fail())
388 return false;
389
390 int32_t offset_td_name = ReadSignedIntegerFromMemory(
391 FindSymbol("thread_off_td_name"), 4, -1, error);
392 if (error.Fail())
393 return false;
394
395 // Fail if we were not able to read any of the offsets.
396 if (offset_p_list == -1 || offset_p_pid == -1 || offset_p_threads == -1 ||
397 offset_p_comm == -1 || offset_td_tid == -1 || offset_td_plist == -1 ||
398 offset_td_pcb == -1 || offset_td_oncpu == -1 || offset_td_name == -1)
399 return false;
400
401 // dumptid contains the thread-id of the crashing thread
402 // dumppcb contains its PCB
403 int32_t dumptid =
404 ReadSignedIntegerFromMemory(FindSymbol("dumptid"), 4, -1, error);
405 if (error.Fail())
406 return false;
407
408 lldb::addr_t dumppcb = FindSymbol("dumppcb");
409
410 // stoppcbs is an array of PCBs on all CPUs.
411 // Each element is of size pcb_size.
412 int32_t pcbsize =
413 ReadSignedIntegerFromMemory(FindSymbol("pcb_size"), 4, -1, error);
414 if (error.Fail())
415 return false;
416
417 lldb::addr_t stoppcbs = FindSymbol("stoppcbs");
418 // In later FreeBSD versions stoppcbs is a pointer to the array.
419 int32_t osreldate =
420 ReadSignedIntegerFromMemory(FindSymbol("osreldate"), 4, -1, error);
421 if (stoppcbs != LLDB_INVALID_ADDRESS && osreldate >= 1400089) {
422 llvm::Expected<lldb::addr_t> stoppcbs_or_err =
423 ReadPointerFromMemory(stoppcbs);
424 if (!stoppcbs_or_err || *stoppcbs_or_err == 0) {
426 "FreeBSD-Kernel-Core: Could not find stoppcbs");
427 return false;
428 }
429
430 stoppcbs = *stoppcbs_or_err;
431 }
432
433 // Read stopped_cpus bitmask and mp_maxid for CPU validation.
434 lldb::addr_t stopped_cpus = FindSymbol("stopped_cpus");
435 uint32_t mp_maxid = 0;
436
437 if (stopped_cpus != LLDB_INVALID_ADDRESS) {
438 // https://cgit.freebsd.org/src/tree/sys/kern/subr_smp.c
439 mp_maxid =
440 ReadSignedIntegerFromMemory(FindSymbol("mp_maxid"), 4, 0, error);
441 if (error.Fail())
442 stopped_cpus = LLDB_INVALID_ADDRESS;
443 }
444
445 uint32_t long_size_bytes = GetAddressByteSize();
446 uint32_t long_bit = long_size_bytes * 8;
447
448 if (auto type_system_or_err =
449 GetTarget().GetScratchTypeSystemForLanguage(eLanguageTypeC)) {
450 CompilerType long_type =
451 (*type_system_or_err)->GetBasicTypeFromAST(eBasicTypeLong);
452 if (long_type.IsValid())
453 if (auto size = long_type.GetByteSize(nullptr))
454 long_size_bytes = *size;
455 long_bit = long_size_bytes * 8;
456 } else
457 llvm::consumeError(type_system_or_err.takeError());
458
459 // https://cgit.freebsd.org/src/tree/sys/sys/param.h
460 constexpr size_t fbsd_maxcomlen = 19;
461
462 // Iterate through a linked list of all processes then order incrementally
463 // by pid. Though new processes are added to the head of this list, process
464 // ids may be reused as well. So we cannot rely on it being in a particular
465 // order.
466 const lldb::addr_t allproc_addr = FindSymbol("allproc");
467 if (allproc_addr == LLDB_INVALID_ADDRESS)
468 return false;
469
470 std::vector<std::pair<lldb::addr_t, int32_t>> process_addrs;
471 llvm::Expected<lldb::addr_t> proc_or_err =
472 ReadPointerFromMemory(allproc_addr);
473 for (; proc_or_err && *proc_or_err != 0;
474 proc_or_err = ReadPointerFromMemory(*proc_or_err + offset_p_list)) {
475 lldb::addr_t proc = *proc_or_err;
476 int32_t pid =
477 ReadSignedIntegerFromMemory(proc + offset_p_pid, 4, -1, error);
478 if (error.Fail())
479 return false;
480 process_addrs.emplace_back(proc, pid);
481 }
482
483 if (!proc_or_err) {
484 llvm::consumeError(proc_or_err.takeError());
485 return false;
486 }
487
488 std::sort(process_addrs.begin(), process_addrs.end(),
489 [](const auto &a, const auto &b) { return a.second < b.second; });
490
491 for (auto [proc, pid] : process_addrs) {
492 // process' command-line string
493 char comm[fbsd_maxcomlen + 1];
494 ReadCStringFromMemory(proc + offset_p_comm, comm, sizeof(comm), error);
495 if (error.Fail())
496 continue;
497
498 // Iterate through a linked list of all process' threads
499 // the initial thread is found in process' p_threads, subsequent
500 // elements are linked via td_plist field.
501 // If reading memory fails, skip to the next thread.
502 llvm::Expected<lldb::addr_t> td_or_err =
503 ReadPointerFromMemory(proc + offset_p_threads);
504 for (; td_or_err && *td_or_err != 0;
505 td_or_err = ReadPointerFromMemory(*td_or_err + offset_td_plist)) {
506 lldb::addr_t td = *td_or_err;
507 int32_t tid =
508 ReadSignedIntegerFromMemory(td + offset_td_tid, 4, -1, error);
509 if (error.Fail())
510 continue;
511
512 llvm::Expected<lldb::addr_t> pcb_addr_or_err =
513 ReadPointerFromMemory(td + offset_td_pcb);
514 if (!pcb_addr_or_err) {
515 llvm::consumeError(pcb_addr_or_err.takeError());
516 continue;
517 }
518 lldb::addr_t pcb_addr = *pcb_addr_or_err;
519
520 // whether process was on CPU (-1 if not, otherwise CPU number)
521 int32_t oncpu =
522 ReadSignedIntegerFromMemory(td + offset_td_oncpu, 4, -2, error);
523 if (error.Fail())
524 continue;
525
526 // thread name
527 char thread_name[fbsd_maxcomlen + 1];
528 ReadCStringFromMemory(td + offset_td_name, thread_name,
529 sizeof(thread_name), error);
530 if (error.Fail())
531 continue;
532
533 // If we failed to read TID, ignore this thread.
534 if (tid == -1)
535 continue;
536
537 std::string thread_desc = llvm::formatv("(pid {0}) {1}", pid, comm);
538 if (*thread_name && strcmp(thread_name, comm)) {
539 thread_desc += '/';
540 thread_desc += thread_name;
541 }
542
543 // Roughly:
544 // 1. if the thread crashed, its PCB is going to be at "dumppcb"
545 // 2. if the thread was on CPU, its PCB is going to be on the CPU
546 // 3. otherwise, its PCB is in the thread struct
547 if (tid == dumptid) {
548 // NB: dumppcb can be LLDB_INVALID_ADDRESS if reading it failed
549 pcb_addr = dumppcb;
550 thread_desc += " (crashed)";
551 } else if (oncpu != -1) {
552 // Verify the CPU is actually in the stopped set before using
553 // its stoppcbs entry.
554 bool is_stopped = false;
555 if (oncpu >= 0 && static_cast<uint32_t>(oncpu) <= mp_maxid &&
556 stopped_cpus != LLDB_INVALID_ADDRESS) {
557 uint32_t bit = oncpu % long_bit;
558 uint32_t word = oncpu / long_bit;
559 lldb::addr_t mask_addr = stopped_cpus + word * long_size_bytes;
560 uint64_t mask = ReadUnsignedIntegerFromMemory(
561 mask_addr, long_size_bytes, 0, error);
562 if (error.Success())
563 is_stopped = (mask & (1ULL << bit)) != 0;
564 }
565
566 // If we managed to read stoppcbs and pcb_size and the cpu is marked
567 // as stopped, use them to find the correct PCB.
568 if (is_stopped && stoppcbs != LLDB_INVALID_ADDRESS && pcbsize > 0) {
569 pcb_addr = stoppcbs + oncpu * pcbsize;
570 } else {
571 pcb_addr = LLDB_INVALID_ADDRESS;
572 }
573 thread_desc += llvm::formatv(" (on CPU {0})", oncpu);
574 }
575
576 auto thread =
577 new ThreadFreeBSDKernelCore(*this, tid, pcb_addr, thread_desc);
578
579 if (tid == dumptid)
580 thread->SetIsCrashedThread(true);
581
582 new_thread_list.AddThread(static_cast<ThreadSP>(thread));
583 }
584
585 // If reading thread list has failed, return with false.
586 if (!td_or_err) {
587 llvm::consumeError(td_or_err.takeError());
588 return false;
589 }
590 }
591 } else {
592 const uint32_t num_threads = old_thread_list.GetSize(false);
593 for (uint32_t i = 0; i < num_threads; ++i)
594 new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false));
595 }
596 return new_thread_list.GetSize(false) > 0;
597}
598
599size_t
601 void *buf, size_t size, Status &error) {
602 lldb::addr_t addr = process_addr.GetValue();
603 ssize_t rd = 0;
604 rd = kvm_read2(m_kvm, addr, buf, size);
605 if (rd < 0 || static_cast<size_t>(rd) != size) {
606 error = Status::FromErrorStringWithFormat("Reading memory failed: %s",
607 GetError());
608 return rd > 0 ? rd : 0;
609 }
610 return rd;
611}
612
615 const Symbol *sym = mod_sp->FindFirstSymbolWithNameAndType(ConstString(name));
616 return sym ? sym->GetLoadAddress(&GetTarget()) : LLDB_INVALID_ADDRESS;
617}
618
620 kvaddr_t *value) {
622 return 1;
623
624 const Symbol *symbol =
625 g_kvm_kernel_module->FindFirstSymbolWithNameAndType(ConstString(name));
626 if (!symbol)
627 return 1;
628
629 lldb::addr_t address = symbol->GetFileAddress();
630 if (address == LLDB_INVALID_ADDRESS)
631 return 1;
632
633 *value = address;
634 return 0;
635}
636
637kvm_t *ProcessFreeBSDKernelCore::OpenKVM(const ModuleSP &kernel_module,
638 const FileSpec &core_file, int flags,
639 char *errbuf) {
640 llvm::SaveAndRestore resolver_module(g_kvm_kernel_module,
641 kernel_module.get());
642 return kvm_open2(kernel_module->GetFileSpec().GetPath().c_str(),
643 core_file.GetPath().c_str(), flags, errbuf,
645}
646
648 kssize_t displacement = kvm_kerndisp(m_kvm);
649
650 Target &target = GetTarget();
651 lldb::ModuleSP kernel_module_sp = target.GetExecutableModule();
652 if (!kernel_module_sp)
653 return;
654
655 bool changed = false;
656 kernel_module_sp->SetLoadAddress(target,
657 static_cast<lldb::addr_t>(displacement),
658 /*value_is_offset=*/true, changed);
659
660 if (changed) {
661 ModuleList loaded_module_list;
662 loaded_module_list.Append(kernel_module_sp);
663 target.ModulesDidLoad(loaded_module_list);
664 }
665}
666
668 Target &target = GetTarget();
669 Debugger &debugger = target.GetDebugger();
670
672
673 // Find msgbufp symbol (pointer to message buffer)
674 lldb::addr_t msgbufp_addr = FindSymbol("msgbufp");
675 if (msgbufp_addr == LLDB_INVALID_ADDRESS)
676 return;
677
678 // Read the pointer value
679 llvm::Expected<lldb::addr_t> msgbufp_or_err =
680 ReadPointerFromMemory(msgbufp_addr);
681 if (!msgbufp_or_err) {
682 llvm::consumeError(msgbufp_or_err.takeError());
683 return;
684 }
685 lldb::addr_t msgbufp = *msgbufp_or_err;
686
687 // Get the type information for struct msgbuf from DWARF
688 TypeQuery query("msgbuf");
689 TypeResults results;
690 target.GetImages().FindTypes(nullptr, query, results);
691
692 uint64_t offset_msg_ptr = 0;
693 uint64_t offset_msg_size = 0;
694 uint64_t offset_msg_wseq = 0;
695 uint64_t offset_msg_rseq = 0;
696
697 if (results.GetTypeMap().GetSize() > 0) {
698 // Found type info - use it to get field offsets
699 CompilerType msgbuf_type =
700 results.GetTypeMap().GetTypeAtIndex(0)->GetForwardCompilerType();
701
702 uint32_t num_fields = msgbuf_type.GetNumFields();
703 int field_found = 0;
704 for (uint32_t i = 0; i < num_fields; i++) {
705 std::string field_name;
706 uint64_t field_offset = 0;
707
708 msgbuf_type.GetFieldAtIndex(i, field_name, &field_offset, nullptr,
709 nullptr);
710
711 if (field_name == "msg_ptr") {
712 offset_msg_ptr = field_offset / 8; // Convert bits to bytes
713 field_found++;
714 } else if (field_name == "msg_size") {
715 offset_msg_size = field_offset / 8;
716 field_found++;
717 } else if (field_name == "msg_wseq") {
718 offset_msg_wseq = field_offset / 8;
719 field_found++;
720 } else if (field_name == "msg_rseq") {
721 offset_msg_rseq = field_offset / 8;
722 field_found++;
723 }
724 }
725
726 if (field_found != 4) {
727 LLDB_LOGF(
729 "FreeBSD-Kernel-Core: Could not find all required fields for msgbuf");
730 return;
731 }
732 } else {
733 // Fallback: use hardcoded offsets based on struct layout
734 // struct msgbuf layout (from sys/sys/msgbuf.h):
735 // char *msg_ptr; - offset 0
736 // u_int msg_magic; - offset ptr_size
737 // u_int msg_size; - offset ptr_size + 4
738 // u_int msg_wseq; - offset ptr_size + 8
739 // u_int msg_rseq; - offset ptr_size + 12
740 uint32_t ptr_size = GetAddressByteSize();
741 offset_msg_ptr = 0;
742 offset_msg_size = ptr_size + 4;
743 offset_msg_wseq = ptr_size + 8;
744 offset_msg_rseq = ptr_size + 12;
745 }
746
747 // Read struct msgbuf fields
748 llvm::Expected<lldb::addr_t> bufp_or_err =
749 ReadPointerFromMemory(msgbufp + offset_msg_ptr);
750 if (!bufp_or_err) {
751 llvm::consumeError(bufp_or_err.takeError());
752 return;
753 }
754 lldb::addr_t bufp = *bufp_or_err;
755
756 uint32_t size =
757 ReadUnsignedIntegerFromMemory(msgbufp + offset_msg_size, 4, 0, error);
758 if (error.Fail() || size == 0)
759 return;
760
761 uint32_t wseq =
762 ReadUnsignedIntegerFromMemory(msgbufp + offset_msg_wseq, 4, 0, error);
763 if (error.Fail())
764 return;
765
766 uint32_t rseq =
767 ReadUnsignedIntegerFromMemory(msgbufp + offset_msg_rseq, 4, 0, error);
768 if (error.Fail())
769 return;
770
771 // Convert sequences to positions
772 // MSGBUF_SEQ_TO_POS macro in FreeBSD: ((seq) % (size))
773 uint32_t rseq_pos = rseq % size;
774 uint32_t wseq_pos = wseq % size;
775
776 if (rseq_pos == wseq_pos)
777 return;
778
779 // Print crash info at once using stream
780 lldb::StreamSP stream_sp = debugger.GetAsyncOutputStream();
781 if (!stream_sp)
782 return;
783
784 stream_sp->PutCString("\nUnread portion of the kernel message buffer:\n");
785
786 // Read ring buffer in at most two chunks
787 if (rseq_pos < wseq_pos) {
788 // No wrap: read from rseq_pos to wseq_pos
789 size_t len = wseq_pos - rseq_pos;
790 std::string buf(len, '\0');
791 size_t bytes_read = ReadMemory(bufp + rseq_pos, &buf[0], len, error);
792 if (error.Success() && bytes_read > 0) {
793 buf.resize(bytes_read);
794 *stream_sp << buf;
795 }
796 } else {
797 // Wrap around: read from rseq_pos to end, then from start to wseq_pos
798 size_t len1 = size - rseq_pos;
799 std::string buf1(len1, '\0');
800 size_t bytes_read1 = ReadMemory(bufp + rseq_pos, &buf1[0], len1, error);
801 if (error.Success() && bytes_read1 > 0) {
802 buf1.resize(bytes_read1);
803 *stream_sp << buf1;
804 }
805
806 if (wseq_pos > 0) {
807 std::string buf2(wseq_pos, '\0');
808 size_t bytes_read2 = ReadMemory(bufp, &buf2[0], wseq_pos, error);
809 if (error.Success() && bytes_read2 > 0) {
810 buf2.resize(bytes_read2);
811 *stream_sp << buf2;
812 }
813 }
814 }
815
816 stream_sp->PutChar('\n');
817 stream_sp->Flush();
818}
819
820const char *ProcessFreeBSDKernelCore::GetError() { return kvm_geterr(m_kvm); }
static llvm::raw_ostream & error(Stream &strm)
#define bit
static PluginProperties & GetGlobalPluginProperties()
#define LLDB_LOGF(log,...)
Definition Log.h:389
#define LLDB_PLUGIN_DEFINE(PluginName)
static PluginProperties & GetGlobalPluginProperties()
static bool SetKernelDebugFile(const ModuleSP &module)
void DoExecute(Args &command, CommandReturnObject &result) override
CommandObjectProcessFreeBSDKernelCoreRefreshThreads(CommandInterpreter &interpreter)
~CommandObjectProcessFreeBSDKernelCoreRefreshThreads() override=default
static llvm::StringRef GetPluginNameStatic()
lldb_private::Status DoDestroy() override
static llvm::StringRef GetPluginNameStatic()
ProcessFreeBSDKernelCore(lldb::TargetSP target_sp, lldb::ListenerSP listener, const lldb_private::FileSpec &core_file)
lldb::addr_t FindSymbol(const char *name)
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
static int ResolveKVMSymbol(const char *name, kvaddr_t *value)
lldb_private::CommandObject * GetPluginCommandObject() override
Return a multi-word command object that can be used to expose plug-in specific commands.
static thread_local lldb_private::Module * g_kvm_kernel_module
friend class CommandObjectProcessFreeBSDKernelCoreRefreshThreads
lldb_private::DynamicLoader * GetDynamicLoader() override
Get the dynamic loader plug-in for this process.
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.
static kvm_t * OpenKVM(const lldb::ModuleSP &kernel_module, const lldb_private::FileSpec &core_file, int flags, char *errbuf)
size_t DoReadMemory(const lldb_private::ProcessAddress &addr, void *buf, size_t size, lldb_private::Status &error) override
Actually do the reading of memory from a process.
bool CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) override
Check if a plug-in instance can debug the file in module.
std::unique_ptr< lldb_private::CommandObjectMultiword > m_command_sp
A command line argument class.
Definition Args.h:33
CommandObjectParsed(CommandInterpreter &interpreter, const char *name, const char *help=nullptr, const char *syntax=nullptr, uint32_t flags=0)
CommandInterpreter & m_interpreter
void SetStatus(lldb::ReturnStatus status)
void void AppendMessageWithFormatv(const char *format, Args &&...args)
Generic representation of a type in a programming language.
CompilerType GetBasicTypeFromAST(lldb::BasicType basic_type) const
Create related types using the current type's AST.
CompilerType GetFieldAtIndex(size_t idx, std::string &name, uint64_t *bit_offset_ptr, uint32_t *bitfield_bit_size_ptr, bool *is_bitfield_ptr) const
llvm::Expected< uint64_t > GetByteSize(ExecutionContextScope *exe_scope) const
Return the size of the type in bytes.
uint32_t GetNumFields() const
A uniqued constant string class.
Definition ConstString.h:40
CommandInterpreter & GetCommandInterpreter()
Definition Debugger.h:183
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:56
void AppendPathComponent(llvm::StringRef component)
Definition FileSpec.cpp:454
bool RemoveLastPathComponent()
Removes the last path component by replacing the current path with its parent.
Definition FileSpec.cpp:465
llvm::StringRef GetDirectory() const
Directory string const get accessor.
Definition FileSpec.h:233
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition FileSpec.cpp:380
static FileSystem & Instance()
void Resolve(llvm::SmallVectorImpl< char > &path, bool force_make_absolute=false)
Resolve path to make it canonical.
void Clear(bool clear_invalid_ranges=false)
Definition Memory.cpp:123
A collection class for Module objects.
Definition ModuleList.h:128
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.
void Append(const lldb::ModuleSP &module_sp, bool notify=true)
Append a module to the module list.
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)
FileSpec GetCoreFile() const override
Provide a way to retrieve the core dump file that is loaded for debugging.
An address in a process, qualified by an address space.
lldb::addr_t GetValue() const
int64_t ReadSignedIntegerFromMemory(lldb::addr_t load_addr, size_t byte_size, int64_t fail_value, Status &error)
Definition Process.cpp:2539
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:2414
virtual size_t ReadMemory(const ProcessAddress &process_addr, void *buf, size_t size, Status &error)
Read of memory from a process.
Definition Process.cpp:2114
lldb::DynamicLoaderUP m_dyld_up
Definition Process.h:3531
llvm::Expected< lldb::addr_t > ReadPointerFromMemory(lldb::addr_t vm_addr)
Definition Process.cpp:2550
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:2489
friend class Target
Definition Process.h:375
MemoryCache m_memory_cache
Definition Process.h:3557
uint32_t GetAddressByteSize() const
Definition Process.cpp:3953
virtual void Finalize(bool destructing)
This object is about to be destroyed, do any necessary cleanup.
Definition Process.cpp:600
ThreadList m_thread_list
The threads for this process as the user will see them.
Definition Process.h:3501
friend class DynamicLoader
Definition Process.h:372
friend class Debugger
Definition Process.h:371
friend class ThreadList
Definition Process.h:376
Target & GetTarget()
Get the target object pointer for this module.
Definition Process.h:1270
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:605
lldb::addr_t GetFileAddress() const
Definition Symbol.cpp:598
void ModulesDidLoad(ModuleList &module_list)
This call may preload module symbols, and may do so in parallel depending on the following target set...
Definition Target.cpp:1961
void SymbolsDidLoad(ModuleList &module_list)
Definition Target.cpp:1989
Debugger & GetDebugger() const
Definition Target.h:1362
lldb::ModuleSP GetExecutableModule()
Gets the module for the main executable.
Definition Target.cpp:1630
const ModuleList & GetImages() const
Get accessor for the images for this process.
Definition Target.h:1279
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:51
lldb::TypeSP GetTypeAtIndex(uint32_t idx)
Definition TypeMap.cpp:59
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:338
std::shared_ptr< lldb_private::Thread > ThreadSP
std::shared_ptr< lldb_private::CommandObject > CommandObjectSP
@ eLanguageTypeC
Non-standardized C, such as K&R.
std::shared_ptr< lldb_private::Stream > StreamSP
std::shared_ptr< lldb_private::Process > ProcessSP
@ eReturnStatusSuccessFinishResult
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