Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp
39642 views
//===-- ProcessFreeBSDKernel.cpp ------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Core/Module.h"9#include "lldb/Core/PluginManager.h"10#include "lldb/Target/DynamicLoader.h"1112#include "Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.h"13#include "ProcessFreeBSDKernel.h"14#include "ThreadFreeBSDKernel.h"1516#if LLDB_ENABLE_FBSDVMCORE17#include <fvc.h>18#endif19#if defined(__FreeBSD__)20#include <kvm.h>21#endif2223using namespace lldb;24using namespace lldb_private;2526LLDB_PLUGIN_DEFINE(ProcessFreeBSDKernel)2728namespace {2930#if LLDB_ENABLE_FBSDVMCORE31class ProcessFreeBSDKernelFVC : public ProcessFreeBSDKernel {32public:33ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp, lldb::ListenerSP listener,34fvc_t *fvc, const FileSpec &core_file);3536~ProcessFreeBSDKernelFVC();3738size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,39lldb_private::Status &error) override;4041private:42fvc_t *m_fvc;4344const char *GetError();45};46#endif // LLDB_ENABLE_FBSDVMCORE4748#if defined(__FreeBSD__)49class ProcessFreeBSDKernelKVM : public ProcessFreeBSDKernel {50public:51ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp, lldb::ListenerSP listener,52kvm_t *fvc, const FileSpec &core_file);5354~ProcessFreeBSDKernelKVM();5556size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,57lldb_private::Status &error) override;5859private:60kvm_t *m_kvm;6162const char *GetError();63};64#endif // defined(__FreeBSD__)6566} // namespace6768ProcessFreeBSDKernel::ProcessFreeBSDKernel(lldb::TargetSP target_sp,69ListenerSP listener_sp,70const FileSpec &core_file)71: PostMortemProcess(target_sp, listener_sp, core_file) {}7273lldb::ProcessSP ProcessFreeBSDKernel::CreateInstance(lldb::TargetSP target_sp,74ListenerSP listener_sp,75const FileSpec *crash_file,76bool can_connect) {77ModuleSP executable = target_sp->GetExecutableModule();78if (crash_file && !can_connect && executable) {79#if LLDB_ENABLE_FBSDVMCORE80fvc_t *fvc =81fvc_open(executable->GetFileSpec().GetPath().c_str(),82crash_file->GetPath().c_str(), nullptr, nullptr, nullptr);83if (fvc)84return std::make_shared<ProcessFreeBSDKernelFVC>(target_sp, listener_sp,85fvc, *crash_file);86#endif8788#if defined(__FreeBSD__)89kvm_t *kvm =90kvm_open2(executable->GetFileSpec().GetPath().c_str(),91crash_file->GetPath().c_str(), O_RDONLY, nullptr, nullptr);92if (kvm)93return std::make_shared<ProcessFreeBSDKernelKVM>(target_sp, listener_sp,94kvm, *crash_file);95#endif96}97return nullptr;98}99100void ProcessFreeBSDKernel::Initialize() {101static llvm::once_flag g_once_flag;102103llvm::call_once(g_once_flag, []() {104PluginManager::RegisterPlugin(GetPluginNameStatic(),105GetPluginDescriptionStatic(), CreateInstance);106});107}108109void ProcessFreeBSDKernel::Terminate() {110PluginManager::UnregisterPlugin(ProcessFreeBSDKernel::CreateInstance);111}112113Status ProcessFreeBSDKernel::DoDestroy() { return Status(); }114115bool ProcessFreeBSDKernel::CanDebug(lldb::TargetSP target_sp,116bool plugin_specified_by_name) {117return true;118}119120void ProcessFreeBSDKernel::RefreshStateAfterStop() {}121122bool ProcessFreeBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list,123ThreadList &new_thread_list) {124if (old_thread_list.GetSize(false) == 0) {125// Make up the thread the first time this is called so we can set our one126// and only core thread state up.127128// We cannot construct a thread without a register context as that crashes129// LLDB but we can construct a process without threads to provide minimal130// memory reading support.131switch (GetTarget().GetArchitecture().GetMachine()) {132case llvm::Triple::aarch64:133case llvm::Triple::x86:134case llvm::Triple::x86_64:135break;136default:137return false;138}139140Status error;141142// struct field offsets are written as symbols so that we don't have143// to figure them out ourselves144int32_t offset_p_list = ReadSignedIntegerFromMemory(145FindSymbol("proc_off_p_list"), 4, -1, error);146int32_t offset_p_pid =147ReadSignedIntegerFromMemory(FindSymbol("proc_off_p_pid"), 4, -1, error);148int32_t offset_p_threads = ReadSignedIntegerFromMemory(149FindSymbol("proc_off_p_threads"), 4, -1, error);150int32_t offset_p_comm = ReadSignedIntegerFromMemory(151FindSymbol("proc_off_p_comm"), 4, -1, error);152153int32_t offset_td_tid = ReadSignedIntegerFromMemory(154FindSymbol("thread_off_td_tid"), 4, -1, error);155int32_t offset_td_plist = ReadSignedIntegerFromMemory(156FindSymbol("thread_off_td_plist"), 4, -1, error);157int32_t offset_td_pcb = ReadSignedIntegerFromMemory(158FindSymbol("thread_off_td_pcb"), 4, -1, error);159int32_t offset_td_oncpu = ReadSignedIntegerFromMemory(160FindSymbol("thread_off_td_oncpu"), 4, -1, error);161int32_t offset_td_name = ReadSignedIntegerFromMemory(162FindSymbol("thread_off_td_name"), 4, -1, error);163164// fail if we were not able to read any of the offsets165if (offset_p_list == -1 || offset_p_pid == -1 || offset_p_threads == -1 ||166offset_p_comm == -1 || offset_td_tid == -1 || offset_td_plist == -1 ||167offset_td_pcb == -1 || offset_td_oncpu == -1 || offset_td_name == -1)168return false;169170// dumptid contains the thread-id of the crashing thread171// dumppcb contains its PCB172int32_t dumptid =173ReadSignedIntegerFromMemory(FindSymbol("dumptid"), 4, -1, error);174lldb::addr_t dumppcb = FindSymbol("dumppcb");175176// stoppcbs is an array of PCBs on all CPUs177// each element is of size pcb_size178int32_t pcbsize =179ReadSignedIntegerFromMemory(FindSymbol("pcb_size"), 4, -1, error);180lldb::addr_t stoppcbs = FindSymbol("stoppcbs");181// In later FreeBSD versions stoppcbs is a pointer to the array.182int32_t osreldate =183ReadSignedIntegerFromMemory(FindSymbol("osreldate"), 4, -1, error);184if (stoppcbs != LLDB_INVALID_ADDRESS && osreldate >= 1400089)185stoppcbs = ReadPointerFromMemory(stoppcbs, error);186187// from FreeBSD sys/param.h188constexpr size_t fbsd_maxcomlen = 19;189190// iterate through a linked list of all processes191// allproc is a pointer to the first list element, p_list field192// (found at offset_p_list) specifies the next element193for (lldb::addr_t proc =194ReadPointerFromMemory(FindSymbol("allproc"), error);195proc != 0 && proc != LLDB_INVALID_ADDRESS;196proc = ReadPointerFromMemory(proc + offset_p_list, error)) {197int32_t pid =198ReadSignedIntegerFromMemory(proc + offset_p_pid, 4, -1, error);199// process' command-line string200char comm[fbsd_maxcomlen + 1];201ReadCStringFromMemory(proc + offset_p_comm, comm, sizeof(comm), error);202203// iterate through a linked list of all process' threads204// the initial thread is found in process' p_threads, subsequent205// elements are linked via td_plist field206for (lldb::addr_t td =207ReadPointerFromMemory(proc + offset_p_threads, error);208td != 0; td = ReadPointerFromMemory(td + offset_td_plist, error)) {209int32_t tid =210ReadSignedIntegerFromMemory(td + offset_td_tid, 4, -1, error);211lldb::addr_t pcb_addr =212ReadPointerFromMemory(td + offset_td_pcb, error);213// whether process was on CPU (-1 if not, otherwise CPU number)214int32_t oncpu =215ReadSignedIntegerFromMemory(td + offset_td_oncpu, 4, -2, error);216// thread name217char thread_name[fbsd_maxcomlen + 1];218ReadCStringFromMemory(td + offset_td_name, thread_name,219sizeof(thread_name), error);220221// if we failed to read TID, ignore this thread222if (tid == -1)223continue;224225std::string thread_desc = llvm::formatv("(pid {0}) {1}", pid, comm);226if (*thread_name && strcmp(thread_name, comm)) {227thread_desc += '/';228thread_desc += thread_name;229}230231// roughly:232// 1. if the thread crashed, its PCB is going to be at "dumppcb"233// 2. if the thread was on CPU, its PCB is going to be on the CPU234// 3. otherwise, its PCB is in the thread struct235if (tid == dumptid) {236// NB: dumppcb can be LLDB_INVALID_ADDRESS if reading it failed237pcb_addr = dumppcb;238thread_desc += " (crashed)";239} else if (oncpu != -1) {240// if we managed to read stoppcbs and pcb_size, use them to find241// the correct PCB242if (stoppcbs != LLDB_INVALID_ADDRESS && pcbsize > 0)243pcb_addr = stoppcbs + oncpu * pcbsize;244else245pcb_addr = LLDB_INVALID_ADDRESS;246thread_desc += llvm::formatv(" (on CPU {0})", oncpu);247}248249ThreadSP thread_sp{250new ThreadFreeBSDKernel(*this, tid, pcb_addr, thread_desc)};251new_thread_list.AddThread(thread_sp);252}253}254} else {255const uint32_t num_threads = old_thread_list.GetSize(false);256for (uint32_t i = 0; i < num_threads; ++i)257new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false));258}259return new_thread_list.GetSize(false) > 0;260}261262Status ProcessFreeBSDKernel::DoLoadCore() {263// The core is already loaded by CreateInstance().264return Status();265}266267DynamicLoader *ProcessFreeBSDKernel::GetDynamicLoader() {268if (m_dyld_up.get() == nullptr)269m_dyld_up.reset(DynamicLoader::FindPlugin(270this, DynamicLoaderFreeBSDKernel::GetPluginNameStatic()));271return m_dyld_up.get();272}273274lldb::addr_t ProcessFreeBSDKernel::FindSymbol(const char *name) {275ModuleSP mod_sp = GetTarget().GetExecutableModule();276const Symbol *sym = mod_sp->FindFirstSymbolWithNameAndType(ConstString(name));277return sym ? sym->GetLoadAddress(&GetTarget()) : LLDB_INVALID_ADDRESS;278}279280#if LLDB_ENABLE_FBSDVMCORE281282ProcessFreeBSDKernelFVC::ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp,283ListenerSP listener_sp,284fvc_t *fvc,285const FileSpec &core_file)286: ProcessFreeBSDKernel(target_sp, listener_sp, crash_file), m_fvc(fvc) {}287288ProcessFreeBSDKernelFVC::~ProcessFreeBSDKernelFVC() {289if (m_fvc)290fvc_close(m_fvc);291}292293size_t ProcessFreeBSDKernelFVC::DoReadMemory(lldb::addr_t addr, void *buf,294size_t size, Status &error) {295ssize_t rd = 0;296rd = fvc_read(m_fvc, addr, buf, size);297if (rd < 0 || static_cast<size_t>(rd) != size) {298error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());299return rd > 0 ? rd : 0;300}301return rd;302}303304const char *ProcessFreeBSDKernelFVC::GetError() { return fvc_geterr(m_fvc); }305306#endif // LLDB_ENABLE_FBSDVMCORE307308#if defined(__FreeBSD__)309310ProcessFreeBSDKernelKVM::ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp,311ListenerSP listener_sp,312kvm_t *fvc,313const FileSpec &core_file)314: ProcessFreeBSDKernel(target_sp, listener_sp, core_file), m_kvm(fvc) {}315316ProcessFreeBSDKernelKVM::~ProcessFreeBSDKernelKVM() {317if (m_kvm)318kvm_close(m_kvm);319}320321size_t ProcessFreeBSDKernelKVM::DoReadMemory(lldb::addr_t addr, void *buf,322size_t size, Status &error) {323ssize_t rd = 0;324rd = kvm_read2(m_kvm, addr, buf, size);325if (rd < 0 || static_cast<size_t>(rd) != size) {326error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());327return rd > 0 ? rd : 0;328}329return rd;330}331332const char *ProcessFreeBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); }333334#endif // defined(__FreeBSD__)335336337