10eae32dcSDimitry Andric //===-- ProcessFreeBSDKernel.cpp ------------------------------------------===// 20eae32dcSDimitry Andric // 30eae32dcSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40eae32dcSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50eae32dcSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60eae32dcSDimitry Andric // 70eae32dcSDimitry Andric //===----------------------------------------------------------------------===// 80eae32dcSDimitry Andric 90eae32dcSDimitry Andric #include "lldb/Core/Module.h" 100eae32dcSDimitry Andric #include "lldb/Core/PluginManager.h" 110eae32dcSDimitry Andric #include "lldb/Target/DynamicLoader.h" 120eae32dcSDimitry Andric 135f757f3fSDimitry Andric #include "Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.h" 140eae32dcSDimitry Andric #include "ProcessFreeBSDKernel.h" 150eae32dcSDimitry Andric #include "ThreadFreeBSDKernel.h" 160eae32dcSDimitry Andric 170eae32dcSDimitry Andric #if LLDB_ENABLE_FBSDVMCORE 180eae32dcSDimitry Andric #include <fvc.h> 190eae32dcSDimitry Andric #endif 200eae32dcSDimitry Andric #if defined(__FreeBSD__) 210eae32dcSDimitry Andric #include <kvm.h> 220eae32dcSDimitry Andric #endif 230eae32dcSDimitry Andric 240eae32dcSDimitry Andric using namespace lldb; 250eae32dcSDimitry Andric using namespace lldb_private; 260eae32dcSDimitry Andric 270eae32dcSDimitry Andric LLDB_PLUGIN_DEFINE(ProcessFreeBSDKernel) 280eae32dcSDimitry Andric 290eae32dcSDimitry Andric namespace { 300eae32dcSDimitry Andric 310eae32dcSDimitry Andric #if LLDB_ENABLE_FBSDVMCORE 320eae32dcSDimitry Andric class ProcessFreeBSDKernelFVC : public ProcessFreeBSDKernel { 330eae32dcSDimitry Andric public: 340eae32dcSDimitry Andric ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp, lldb::ListenerSP listener, 35*0fca6ea1SDimitry Andric fvc_t *fvc, const FileSpec &core_file); 360eae32dcSDimitry Andric 370eae32dcSDimitry Andric ~ProcessFreeBSDKernelFVC(); 380eae32dcSDimitry Andric 390eae32dcSDimitry Andric size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, 400eae32dcSDimitry Andric lldb_private::Status &error) override; 410eae32dcSDimitry Andric 420eae32dcSDimitry Andric private: 430eae32dcSDimitry Andric fvc_t *m_fvc; 440eae32dcSDimitry Andric 450eae32dcSDimitry Andric const char *GetError(); 460eae32dcSDimitry Andric }; 470eae32dcSDimitry Andric #endif // LLDB_ENABLE_FBSDVMCORE 480eae32dcSDimitry Andric 490eae32dcSDimitry Andric #if defined(__FreeBSD__) 500eae32dcSDimitry Andric class ProcessFreeBSDKernelKVM : public ProcessFreeBSDKernel { 510eae32dcSDimitry Andric public: 520eae32dcSDimitry Andric ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp, lldb::ListenerSP listener, 53*0fca6ea1SDimitry Andric kvm_t *fvc, const FileSpec &core_file); 540eae32dcSDimitry Andric 550eae32dcSDimitry Andric ~ProcessFreeBSDKernelKVM(); 560eae32dcSDimitry Andric 570eae32dcSDimitry Andric size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, 580eae32dcSDimitry Andric lldb_private::Status &error) override; 590eae32dcSDimitry Andric 600eae32dcSDimitry Andric private: 610eae32dcSDimitry Andric kvm_t *m_kvm; 620eae32dcSDimitry Andric 630eae32dcSDimitry Andric const char *GetError(); 640eae32dcSDimitry Andric }; 650eae32dcSDimitry Andric #endif // defined(__FreeBSD__) 660eae32dcSDimitry Andric 670eae32dcSDimitry Andric } // namespace 680eae32dcSDimitry Andric 690eae32dcSDimitry Andric ProcessFreeBSDKernel::ProcessFreeBSDKernel(lldb::TargetSP target_sp, 70*0fca6ea1SDimitry Andric ListenerSP listener_sp, 71*0fca6ea1SDimitry Andric const FileSpec &core_file) 72*0fca6ea1SDimitry Andric : PostMortemProcess(target_sp, listener_sp, core_file) {} 730eae32dcSDimitry Andric 740eae32dcSDimitry Andric lldb::ProcessSP ProcessFreeBSDKernel::CreateInstance(lldb::TargetSP target_sp, 750eae32dcSDimitry Andric ListenerSP listener_sp, 760eae32dcSDimitry Andric const FileSpec *crash_file, 770eae32dcSDimitry Andric bool can_connect) { 780eae32dcSDimitry Andric ModuleSP executable = target_sp->GetExecutableModule(); 790eae32dcSDimitry Andric if (crash_file && !can_connect && executable) { 800eae32dcSDimitry Andric #if LLDB_ENABLE_FBSDVMCORE 810eae32dcSDimitry Andric fvc_t *fvc = 820eae32dcSDimitry Andric fvc_open(executable->GetFileSpec().GetPath().c_str(), 830eae32dcSDimitry Andric crash_file->GetPath().c_str(), nullptr, nullptr, nullptr); 840eae32dcSDimitry Andric if (fvc) 850eae32dcSDimitry Andric return std::make_shared<ProcessFreeBSDKernelFVC>(target_sp, listener_sp, 86*0fca6ea1SDimitry Andric fvc, *crash_file); 870eae32dcSDimitry Andric #endif 880eae32dcSDimitry Andric 890eae32dcSDimitry Andric #if defined(__FreeBSD__) 900eae32dcSDimitry Andric kvm_t *kvm = 910eae32dcSDimitry Andric kvm_open2(executable->GetFileSpec().GetPath().c_str(), 920eae32dcSDimitry Andric crash_file->GetPath().c_str(), O_RDONLY, nullptr, nullptr); 930eae32dcSDimitry Andric if (kvm) 940eae32dcSDimitry Andric return std::make_shared<ProcessFreeBSDKernelKVM>(target_sp, listener_sp, 95*0fca6ea1SDimitry Andric kvm, *crash_file); 960eae32dcSDimitry Andric #endif 970eae32dcSDimitry Andric } 980eae32dcSDimitry Andric return nullptr; 990eae32dcSDimitry Andric } 1000eae32dcSDimitry Andric 1010eae32dcSDimitry Andric void ProcessFreeBSDKernel::Initialize() { 1020eae32dcSDimitry Andric static llvm::once_flag g_once_flag; 1030eae32dcSDimitry Andric 1040eae32dcSDimitry Andric llvm::call_once(g_once_flag, []() { 1050eae32dcSDimitry Andric PluginManager::RegisterPlugin(GetPluginNameStatic(), 1060eae32dcSDimitry Andric GetPluginDescriptionStatic(), CreateInstance); 1070eae32dcSDimitry Andric }); 1080eae32dcSDimitry Andric } 1090eae32dcSDimitry Andric 1100eae32dcSDimitry Andric void ProcessFreeBSDKernel::Terminate() { 1110eae32dcSDimitry Andric PluginManager::UnregisterPlugin(ProcessFreeBSDKernel::CreateInstance); 1120eae32dcSDimitry Andric } 1130eae32dcSDimitry Andric 1140eae32dcSDimitry Andric Status ProcessFreeBSDKernel::DoDestroy() { return Status(); } 1150eae32dcSDimitry Andric 1160eae32dcSDimitry Andric bool ProcessFreeBSDKernel::CanDebug(lldb::TargetSP target_sp, 1170eae32dcSDimitry Andric bool plugin_specified_by_name) { 1180eae32dcSDimitry Andric return true; 1190eae32dcSDimitry Andric } 1200eae32dcSDimitry Andric 1210eae32dcSDimitry Andric void ProcessFreeBSDKernel::RefreshStateAfterStop() {} 1220eae32dcSDimitry Andric 1230eae32dcSDimitry Andric bool ProcessFreeBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list, 1240eae32dcSDimitry Andric ThreadList &new_thread_list) { 1250eae32dcSDimitry Andric if (old_thread_list.GetSize(false) == 0) { 1260eae32dcSDimitry Andric // Make up the thread the first time this is called so we can set our one 1270eae32dcSDimitry Andric // and only core thread state up. 1280eae32dcSDimitry Andric 1290eae32dcSDimitry Andric // We cannot construct a thread without a register context as that crashes 1300eae32dcSDimitry Andric // LLDB but we can construct a process without threads to provide minimal 1310eae32dcSDimitry Andric // memory reading support. 1320eae32dcSDimitry Andric switch (GetTarget().GetArchitecture().GetMachine()) { 1330eae32dcSDimitry Andric case llvm::Triple::aarch64: 1340eae32dcSDimitry Andric case llvm::Triple::x86: 1350eae32dcSDimitry Andric case llvm::Triple::x86_64: 1360eae32dcSDimitry Andric break; 1370eae32dcSDimitry Andric default: 1380eae32dcSDimitry Andric return false; 1390eae32dcSDimitry Andric } 1400eae32dcSDimitry Andric 14104eeddc0SDimitry Andric Status error; 14204eeddc0SDimitry Andric 14304eeddc0SDimitry Andric // struct field offsets are written as symbols so that we don't have 14404eeddc0SDimitry Andric // to figure them out ourselves 14504eeddc0SDimitry Andric int32_t offset_p_list = ReadSignedIntegerFromMemory( 14604eeddc0SDimitry Andric FindSymbol("proc_off_p_list"), 4, -1, error); 14704eeddc0SDimitry Andric int32_t offset_p_pid = 14804eeddc0SDimitry Andric ReadSignedIntegerFromMemory(FindSymbol("proc_off_p_pid"), 4, -1, error); 14904eeddc0SDimitry Andric int32_t offset_p_threads = ReadSignedIntegerFromMemory( 15004eeddc0SDimitry Andric FindSymbol("proc_off_p_threads"), 4, -1, error); 15104eeddc0SDimitry Andric int32_t offset_p_comm = ReadSignedIntegerFromMemory( 15204eeddc0SDimitry Andric FindSymbol("proc_off_p_comm"), 4, -1, error); 15304eeddc0SDimitry Andric 15404eeddc0SDimitry Andric int32_t offset_td_tid = ReadSignedIntegerFromMemory( 15504eeddc0SDimitry Andric FindSymbol("thread_off_td_tid"), 4, -1, error); 15604eeddc0SDimitry Andric int32_t offset_td_plist = ReadSignedIntegerFromMemory( 15704eeddc0SDimitry Andric FindSymbol("thread_off_td_plist"), 4, -1, error); 15804eeddc0SDimitry Andric int32_t offset_td_pcb = ReadSignedIntegerFromMemory( 15904eeddc0SDimitry Andric FindSymbol("thread_off_td_pcb"), 4, -1, error); 16004eeddc0SDimitry Andric int32_t offset_td_oncpu = ReadSignedIntegerFromMemory( 16104eeddc0SDimitry Andric FindSymbol("thread_off_td_oncpu"), 4, -1, error); 16204eeddc0SDimitry Andric int32_t offset_td_name = ReadSignedIntegerFromMemory( 16304eeddc0SDimitry Andric FindSymbol("thread_off_td_name"), 4, -1, error); 16404eeddc0SDimitry Andric 16504eeddc0SDimitry Andric // fail if we were not able to read any of the offsets 16604eeddc0SDimitry Andric if (offset_p_list == -1 || offset_p_pid == -1 || offset_p_threads == -1 || 16704eeddc0SDimitry Andric offset_p_comm == -1 || offset_td_tid == -1 || offset_td_plist == -1 || 16804eeddc0SDimitry Andric offset_td_pcb == -1 || offset_td_oncpu == -1 || offset_td_name == -1) 16904eeddc0SDimitry Andric return false; 17004eeddc0SDimitry Andric 17104eeddc0SDimitry Andric // dumptid contains the thread-id of the crashing thread 17204eeddc0SDimitry Andric // dumppcb contains its PCB 17304eeddc0SDimitry Andric int32_t dumptid = 17404eeddc0SDimitry Andric ReadSignedIntegerFromMemory(FindSymbol("dumptid"), 4, -1, error); 17504eeddc0SDimitry Andric lldb::addr_t dumppcb = FindSymbol("dumppcb"); 17604eeddc0SDimitry Andric 17704eeddc0SDimitry Andric // stoppcbs is an array of PCBs on all CPUs 17804eeddc0SDimitry Andric // each element is of size pcb_size 17904eeddc0SDimitry Andric int32_t pcbsize = 18004eeddc0SDimitry Andric ReadSignedIntegerFromMemory(FindSymbol("pcb_size"), 4, -1, error); 18104eeddc0SDimitry Andric lldb::addr_t stoppcbs = FindSymbol("stoppcbs"); 1820f2bb40bSEd Maste // In later FreeBSD versions stoppcbs is a pointer to the array. 1830f2bb40bSEd Maste int32_t osreldate = 1840f2bb40bSEd Maste ReadSignedIntegerFromMemory(FindSymbol("osreldate"), 4, -1, error); 1850f2bb40bSEd Maste if (stoppcbs != LLDB_INVALID_ADDRESS && osreldate >= 1400089) 1860f2bb40bSEd Maste stoppcbs = ReadPointerFromMemory(stoppcbs, error); 18704eeddc0SDimitry Andric 18804eeddc0SDimitry Andric // from FreeBSD sys/param.h 18904eeddc0SDimitry Andric constexpr size_t fbsd_maxcomlen = 19; 19004eeddc0SDimitry Andric 19104eeddc0SDimitry Andric // iterate through a linked list of all processes 19204eeddc0SDimitry Andric // allproc is a pointer to the first list element, p_list field 19304eeddc0SDimitry Andric // (found at offset_p_list) specifies the next element 19404eeddc0SDimitry Andric for (lldb::addr_t proc = 19504eeddc0SDimitry Andric ReadPointerFromMemory(FindSymbol("allproc"), error); 19604eeddc0SDimitry Andric proc != 0 && proc != LLDB_INVALID_ADDRESS; 19704eeddc0SDimitry Andric proc = ReadPointerFromMemory(proc + offset_p_list, error)) { 19804eeddc0SDimitry Andric int32_t pid = 19904eeddc0SDimitry Andric ReadSignedIntegerFromMemory(proc + offset_p_pid, 4, -1, error); 20004eeddc0SDimitry Andric // process' command-line string 20104eeddc0SDimitry Andric char comm[fbsd_maxcomlen + 1]; 20204eeddc0SDimitry Andric ReadCStringFromMemory(proc + offset_p_comm, comm, sizeof(comm), error); 20304eeddc0SDimitry Andric 20404eeddc0SDimitry Andric // iterate through a linked list of all process' threads 20504eeddc0SDimitry Andric // the initial thread is found in process' p_threads, subsequent 20604eeddc0SDimitry Andric // elements are linked via td_plist field 20704eeddc0SDimitry Andric for (lldb::addr_t td = 20804eeddc0SDimitry Andric ReadPointerFromMemory(proc + offset_p_threads, error); 20904eeddc0SDimitry Andric td != 0; td = ReadPointerFromMemory(td + offset_td_plist, error)) { 21004eeddc0SDimitry Andric int32_t tid = 21104eeddc0SDimitry Andric ReadSignedIntegerFromMemory(td + offset_td_tid, 4, -1, error); 21204eeddc0SDimitry Andric lldb::addr_t pcb_addr = 21304eeddc0SDimitry Andric ReadPointerFromMemory(td + offset_td_pcb, error); 21404eeddc0SDimitry Andric // whether process was on CPU (-1 if not, otherwise CPU number) 21504eeddc0SDimitry Andric int32_t oncpu = 21604eeddc0SDimitry Andric ReadSignedIntegerFromMemory(td + offset_td_oncpu, 4, -2, error); 21704eeddc0SDimitry Andric // thread name 21804eeddc0SDimitry Andric char thread_name[fbsd_maxcomlen + 1]; 21904eeddc0SDimitry Andric ReadCStringFromMemory(td + offset_td_name, thread_name, 22004eeddc0SDimitry Andric sizeof(thread_name), error); 22104eeddc0SDimitry Andric 22204eeddc0SDimitry Andric // if we failed to read TID, ignore this thread 22304eeddc0SDimitry Andric if (tid == -1) 22404eeddc0SDimitry Andric continue; 22504eeddc0SDimitry Andric 22604eeddc0SDimitry Andric std::string thread_desc = llvm::formatv("(pid {0}) {1}", pid, comm); 22704eeddc0SDimitry Andric if (*thread_name && strcmp(thread_name, comm)) { 22804eeddc0SDimitry Andric thread_desc += '/'; 22904eeddc0SDimitry Andric thread_desc += thread_name; 23004eeddc0SDimitry Andric } 23104eeddc0SDimitry Andric 23204eeddc0SDimitry Andric // roughly: 23304eeddc0SDimitry Andric // 1. if the thread crashed, its PCB is going to be at "dumppcb" 23404eeddc0SDimitry Andric // 2. if the thread was on CPU, its PCB is going to be on the CPU 23504eeddc0SDimitry Andric // 3. otherwise, its PCB is in the thread struct 23604eeddc0SDimitry Andric if (tid == dumptid) { 23704eeddc0SDimitry Andric // NB: dumppcb can be LLDB_INVALID_ADDRESS if reading it failed 23804eeddc0SDimitry Andric pcb_addr = dumppcb; 23904eeddc0SDimitry Andric thread_desc += " (crashed)"; 24004eeddc0SDimitry Andric } else if (oncpu != -1) { 24104eeddc0SDimitry Andric // if we managed to read stoppcbs and pcb_size, use them to find 24204eeddc0SDimitry Andric // the correct PCB 24304eeddc0SDimitry Andric if (stoppcbs != LLDB_INVALID_ADDRESS && pcbsize > 0) 24404eeddc0SDimitry Andric pcb_addr = stoppcbs + oncpu * pcbsize; 24504eeddc0SDimitry Andric else 24604eeddc0SDimitry Andric pcb_addr = LLDB_INVALID_ADDRESS; 24704eeddc0SDimitry Andric thread_desc += llvm::formatv(" (on CPU {0})", oncpu); 24804eeddc0SDimitry Andric } 24904eeddc0SDimitry Andric 25004eeddc0SDimitry Andric ThreadSP thread_sp{ 25104eeddc0SDimitry Andric new ThreadFreeBSDKernel(*this, tid, pcb_addr, thread_desc)}; 2520eae32dcSDimitry Andric new_thread_list.AddThread(thread_sp); 25304eeddc0SDimitry Andric } 25404eeddc0SDimitry Andric } 2550eae32dcSDimitry Andric } else { 2560eae32dcSDimitry Andric const uint32_t num_threads = old_thread_list.GetSize(false); 2570eae32dcSDimitry Andric for (uint32_t i = 0; i < num_threads; ++i) 2580eae32dcSDimitry Andric new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false)); 2590eae32dcSDimitry Andric } 2600eae32dcSDimitry Andric return new_thread_list.GetSize(false) > 0; 2610eae32dcSDimitry Andric } 2620eae32dcSDimitry Andric 2630eae32dcSDimitry Andric Status ProcessFreeBSDKernel::DoLoadCore() { 2640eae32dcSDimitry Andric // The core is already loaded by CreateInstance(). 2650eae32dcSDimitry Andric return Status(); 2660eae32dcSDimitry Andric } 2670eae32dcSDimitry Andric 2680eae32dcSDimitry Andric DynamicLoader *ProcessFreeBSDKernel::GetDynamicLoader() { 2690eae32dcSDimitry Andric if (m_dyld_up.get() == nullptr) 2700eae32dcSDimitry Andric m_dyld_up.reset(DynamicLoader::FindPlugin( 2715f757f3fSDimitry Andric this, DynamicLoaderFreeBSDKernel::GetPluginNameStatic())); 2720eae32dcSDimitry Andric return m_dyld_up.get(); 2730eae32dcSDimitry Andric } 2740eae32dcSDimitry Andric 27504eeddc0SDimitry Andric lldb::addr_t ProcessFreeBSDKernel::FindSymbol(const char *name) { 27604eeddc0SDimitry Andric ModuleSP mod_sp = GetTarget().GetExecutableModule(); 27704eeddc0SDimitry Andric const Symbol *sym = mod_sp->FindFirstSymbolWithNameAndType(ConstString(name)); 27804eeddc0SDimitry Andric return sym ? sym->GetLoadAddress(&GetTarget()) : LLDB_INVALID_ADDRESS; 27904eeddc0SDimitry Andric } 28004eeddc0SDimitry Andric 2810eae32dcSDimitry Andric #if LLDB_ENABLE_FBSDVMCORE 2820eae32dcSDimitry Andric 2830eae32dcSDimitry Andric ProcessFreeBSDKernelFVC::ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp, 2840eae32dcSDimitry Andric ListenerSP listener_sp, 285*0fca6ea1SDimitry Andric fvc_t *fvc, 286*0fca6ea1SDimitry Andric const FileSpec &core_file) 287*0fca6ea1SDimitry Andric : ProcessFreeBSDKernel(target_sp, listener_sp, crash_file), m_fvc(fvc) {} 2880eae32dcSDimitry Andric 2890eae32dcSDimitry Andric ProcessFreeBSDKernelFVC::~ProcessFreeBSDKernelFVC() { 2900eae32dcSDimitry Andric if (m_fvc) 2910eae32dcSDimitry Andric fvc_close(m_fvc); 2920eae32dcSDimitry Andric } 2930eae32dcSDimitry Andric 2940eae32dcSDimitry Andric size_t ProcessFreeBSDKernelFVC::DoReadMemory(lldb::addr_t addr, void *buf, 2950eae32dcSDimitry Andric size_t size, Status &error) { 2960eae32dcSDimitry Andric ssize_t rd = 0; 2970eae32dcSDimitry Andric rd = fvc_read(m_fvc, addr, buf, size); 2980eae32dcSDimitry Andric if (rd < 0 || static_cast<size_t>(rd) != size) { 2990eae32dcSDimitry Andric error.SetErrorStringWithFormat("Reading memory failed: %s", GetError()); 3000eae32dcSDimitry Andric return rd > 0 ? rd : 0; 3010eae32dcSDimitry Andric } 3020eae32dcSDimitry Andric return rd; 3030eae32dcSDimitry Andric } 3040eae32dcSDimitry Andric 3050eae32dcSDimitry Andric const char *ProcessFreeBSDKernelFVC::GetError() { return fvc_geterr(m_fvc); } 3060eae32dcSDimitry Andric 3070eae32dcSDimitry Andric #endif // LLDB_ENABLE_FBSDVMCORE 3080eae32dcSDimitry Andric 3090eae32dcSDimitry Andric #if defined(__FreeBSD__) 3100eae32dcSDimitry Andric 3110eae32dcSDimitry Andric ProcessFreeBSDKernelKVM::ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp, 3120eae32dcSDimitry Andric ListenerSP listener_sp, 313*0fca6ea1SDimitry Andric kvm_t *fvc, 314*0fca6ea1SDimitry Andric const FileSpec &core_file) 315*0fca6ea1SDimitry Andric : ProcessFreeBSDKernel(target_sp, listener_sp, core_file), m_kvm(fvc) {} 3160eae32dcSDimitry Andric 3170eae32dcSDimitry Andric ProcessFreeBSDKernelKVM::~ProcessFreeBSDKernelKVM() { 3180eae32dcSDimitry Andric if (m_kvm) 3190eae32dcSDimitry Andric kvm_close(m_kvm); 3200eae32dcSDimitry Andric } 3210eae32dcSDimitry Andric 3220eae32dcSDimitry Andric size_t ProcessFreeBSDKernelKVM::DoReadMemory(lldb::addr_t addr, void *buf, 3230eae32dcSDimitry Andric size_t size, Status &error) { 3240eae32dcSDimitry Andric ssize_t rd = 0; 3250eae32dcSDimitry Andric rd = kvm_read2(m_kvm, addr, buf, size); 3260eae32dcSDimitry Andric if (rd < 0 || static_cast<size_t>(rd) != size) { 3270eae32dcSDimitry Andric error.SetErrorStringWithFormat("Reading memory failed: %s", GetError()); 3280eae32dcSDimitry Andric return rd > 0 ? rd : 0; 3290eae32dcSDimitry Andric } 3300eae32dcSDimitry Andric return rd; 3310eae32dcSDimitry Andric } 3320eae32dcSDimitry Andric 3330eae32dcSDimitry Andric const char *ProcessFreeBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); } 3340eae32dcSDimitry Andric 3350eae32dcSDimitry Andric #endif // defined(__FreeBSD__) 336