xref: /freebsd-src/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp (revision 04eeddc0aa8e0a417a16eaf9d7d095207f4a8623)
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 
130eae32dcSDimitry Andric #include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.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,
350eae32dcSDimitry Andric                           fvc_t *fvc);
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,
530eae32dcSDimitry Andric                           kvm_t *fvc);
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,
700eae32dcSDimitry Andric                                            ListenerSP listener_sp)
710eae32dcSDimitry Andric     : PostMortemProcess(target_sp, listener_sp) {}
720eae32dcSDimitry Andric 
730eae32dcSDimitry Andric lldb::ProcessSP ProcessFreeBSDKernel::CreateInstance(lldb::TargetSP target_sp,
740eae32dcSDimitry Andric                                                      ListenerSP listener_sp,
750eae32dcSDimitry Andric                                                      const FileSpec *crash_file,
760eae32dcSDimitry Andric                                                      bool can_connect) {
770eae32dcSDimitry Andric   ModuleSP executable = target_sp->GetExecutableModule();
780eae32dcSDimitry Andric   if (crash_file && !can_connect && executable) {
790eae32dcSDimitry Andric #if LLDB_ENABLE_FBSDVMCORE
800eae32dcSDimitry Andric     fvc_t *fvc =
810eae32dcSDimitry Andric         fvc_open(executable->GetFileSpec().GetPath().c_str(),
820eae32dcSDimitry Andric                  crash_file->GetPath().c_str(), nullptr, nullptr, nullptr);
830eae32dcSDimitry Andric     if (fvc)
840eae32dcSDimitry Andric       return std::make_shared<ProcessFreeBSDKernelFVC>(target_sp, listener_sp,
850eae32dcSDimitry Andric                                                        fvc);
860eae32dcSDimitry Andric #endif
870eae32dcSDimitry Andric 
880eae32dcSDimitry Andric #if defined(__FreeBSD__)
890eae32dcSDimitry Andric     kvm_t *kvm =
900eae32dcSDimitry Andric         kvm_open2(executable->GetFileSpec().GetPath().c_str(),
910eae32dcSDimitry Andric                   crash_file->GetPath().c_str(), O_RDONLY, nullptr, nullptr);
920eae32dcSDimitry Andric     if (kvm)
930eae32dcSDimitry Andric       return std::make_shared<ProcessFreeBSDKernelKVM>(target_sp, listener_sp,
940eae32dcSDimitry Andric                                                        kvm);
950eae32dcSDimitry Andric #endif
960eae32dcSDimitry Andric   }
970eae32dcSDimitry Andric   return nullptr;
980eae32dcSDimitry Andric }
990eae32dcSDimitry Andric 
1000eae32dcSDimitry Andric void ProcessFreeBSDKernel::Initialize() {
1010eae32dcSDimitry Andric   static llvm::once_flag g_once_flag;
1020eae32dcSDimitry Andric 
1030eae32dcSDimitry Andric   llvm::call_once(g_once_flag, []() {
1040eae32dcSDimitry Andric     PluginManager::RegisterPlugin(GetPluginNameStatic(),
1050eae32dcSDimitry Andric                                   GetPluginDescriptionStatic(), CreateInstance);
1060eae32dcSDimitry Andric   });
1070eae32dcSDimitry Andric }
1080eae32dcSDimitry Andric 
1090eae32dcSDimitry Andric void ProcessFreeBSDKernel::Terminate() {
1100eae32dcSDimitry Andric   PluginManager::UnregisterPlugin(ProcessFreeBSDKernel::CreateInstance);
1110eae32dcSDimitry Andric }
1120eae32dcSDimitry Andric 
1130eae32dcSDimitry Andric Status ProcessFreeBSDKernel::DoDestroy() { return Status(); }
1140eae32dcSDimitry Andric 
1150eae32dcSDimitry Andric bool ProcessFreeBSDKernel::CanDebug(lldb::TargetSP target_sp,
1160eae32dcSDimitry Andric                                     bool plugin_specified_by_name) {
1170eae32dcSDimitry Andric   return true;
1180eae32dcSDimitry Andric }
1190eae32dcSDimitry Andric 
1200eae32dcSDimitry Andric void ProcessFreeBSDKernel::RefreshStateAfterStop() {}
1210eae32dcSDimitry Andric 
1220eae32dcSDimitry Andric bool ProcessFreeBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list,
1230eae32dcSDimitry Andric                                               ThreadList &new_thread_list) {
1240eae32dcSDimitry Andric   if (old_thread_list.GetSize(false) == 0) {
1250eae32dcSDimitry Andric     // Make up the thread the first time this is called so we can set our one
1260eae32dcSDimitry Andric     // and only core thread state up.
1270eae32dcSDimitry Andric 
1280eae32dcSDimitry Andric     // We cannot construct a thread without a register context as that crashes
1290eae32dcSDimitry Andric     // LLDB but we can construct a process without threads to provide minimal
1300eae32dcSDimitry Andric     // memory reading support.
1310eae32dcSDimitry Andric     switch (GetTarget().GetArchitecture().GetMachine()) {
1320eae32dcSDimitry Andric     case llvm::Triple::aarch64:
1330eae32dcSDimitry Andric     case llvm::Triple::x86:
1340eae32dcSDimitry Andric     case llvm::Triple::x86_64:
1350eae32dcSDimitry Andric       break;
1360eae32dcSDimitry Andric     default:
1370eae32dcSDimitry Andric       return false;
1380eae32dcSDimitry Andric     }
1390eae32dcSDimitry Andric 
140*04eeddc0SDimitry Andric     Status error;
141*04eeddc0SDimitry Andric 
142*04eeddc0SDimitry Andric     // struct field offsets are written as symbols so that we don't have
143*04eeddc0SDimitry Andric     // to figure them out ourselves
144*04eeddc0SDimitry Andric     int32_t offset_p_list = ReadSignedIntegerFromMemory(
145*04eeddc0SDimitry Andric         FindSymbol("proc_off_p_list"), 4, -1, error);
146*04eeddc0SDimitry Andric     int32_t offset_p_pid =
147*04eeddc0SDimitry Andric         ReadSignedIntegerFromMemory(FindSymbol("proc_off_p_pid"), 4, -1, error);
148*04eeddc0SDimitry Andric     int32_t offset_p_threads = ReadSignedIntegerFromMemory(
149*04eeddc0SDimitry Andric         FindSymbol("proc_off_p_threads"), 4, -1, error);
150*04eeddc0SDimitry Andric     int32_t offset_p_comm = ReadSignedIntegerFromMemory(
151*04eeddc0SDimitry Andric         FindSymbol("proc_off_p_comm"), 4, -1, error);
152*04eeddc0SDimitry Andric 
153*04eeddc0SDimitry Andric     int32_t offset_td_tid = ReadSignedIntegerFromMemory(
154*04eeddc0SDimitry Andric         FindSymbol("thread_off_td_tid"), 4, -1, error);
155*04eeddc0SDimitry Andric     int32_t offset_td_plist = ReadSignedIntegerFromMemory(
156*04eeddc0SDimitry Andric         FindSymbol("thread_off_td_plist"), 4, -1, error);
157*04eeddc0SDimitry Andric     int32_t offset_td_pcb = ReadSignedIntegerFromMemory(
158*04eeddc0SDimitry Andric         FindSymbol("thread_off_td_pcb"), 4, -1, error);
159*04eeddc0SDimitry Andric     int32_t offset_td_oncpu = ReadSignedIntegerFromMemory(
160*04eeddc0SDimitry Andric         FindSymbol("thread_off_td_oncpu"), 4, -1, error);
161*04eeddc0SDimitry Andric     int32_t offset_td_name = ReadSignedIntegerFromMemory(
162*04eeddc0SDimitry Andric         FindSymbol("thread_off_td_name"), 4, -1, error);
163*04eeddc0SDimitry Andric 
164*04eeddc0SDimitry Andric     // fail if we were not able to read any of the offsets
165*04eeddc0SDimitry Andric     if (offset_p_list == -1 || offset_p_pid == -1 || offset_p_threads == -1 ||
166*04eeddc0SDimitry Andric         offset_p_comm == -1 || offset_td_tid == -1 || offset_td_plist == -1 ||
167*04eeddc0SDimitry Andric         offset_td_pcb == -1 || offset_td_oncpu == -1 || offset_td_name == -1)
168*04eeddc0SDimitry Andric       return false;
169*04eeddc0SDimitry Andric 
170*04eeddc0SDimitry Andric     // dumptid contains the thread-id of the crashing thread
171*04eeddc0SDimitry Andric     // dumppcb contains its PCB
172*04eeddc0SDimitry Andric     int32_t dumptid =
173*04eeddc0SDimitry Andric         ReadSignedIntegerFromMemory(FindSymbol("dumptid"), 4, -1, error);
174*04eeddc0SDimitry Andric     lldb::addr_t dumppcb = FindSymbol("dumppcb");
175*04eeddc0SDimitry Andric 
176*04eeddc0SDimitry Andric     // stoppcbs is an array of PCBs on all CPUs
177*04eeddc0SDimitry Andric     // each element is of size pcb_size
178*04eeddc0SDimitry Andric     int32_t pcbsize =
179*04eeddc0SDimitry Andric         ReadSignedIntegerFromMemory(FindSymbol("pcb_size"), 4, -1, error);
180*04eeddc0SDimitry Andric     lldb::addr_t stoppcbs = FindSymbol("stoppcbs");
181*04eeddc0SDimitry Andric 
182*04eeddc0SDimitry Andric     // from FreeBSD sys/param.h
183*04eeddc0SDimitry Andric     constexpr size_t fbsd_maxcomlen = 19;
184*04eeddc0SDimitry Andric 
185*04eeddc0SDimitry Andric     // iterate through a linked list of all processes
186*04eeddc0SDimitry Andric     // allproc is a pointer to the first list element, p_list field
187*04eeddc0SDimitry Andric     // (found at offset_p_list) specifies the next element
188*04eeddc0SDimitry Andric     for (lldb::addr_t proc =
189*04eeddc0SDimitry Andric              ReadPointerFromMemory(FindSymbol("allproc"), error);
190*04eeddc0SDimitry Andric          proc != 0 && proc != LLDB_INVALID_ADDRESS;
191*04eeddc0SDimitry Andric          proc = ReadPointerFromMemory(proc + offset_p_list, error)) {
192*04eeddc0SDimitry Andric       int32_t pid =
193*04eeddc0SDimitry Andric           ReadSignedIntegerFromMemory(proc + offset_p_pid, 4, -1, error);
194*04eeddc0SDimitry Andric       // process' command-line string
195*04eeddc0SDimitry Andric       char comm[fbsd_maxcomlen + 1];
196*04eeddc0SDimitry Andric       ReadCStringFromMemory(proc + offset_p_comm, comm, sizeof(comm), error);
197*04eeddc0SDimitry Andric 
198*04eeddc0SDimitry Andric       // iterate through a linked list of all process' threads
199*04eeddc0SDimitry Andric       // the initial thread is found in process' p_threads, subsequent
200*04eeddc0SDimitry Andric       // elements are linked via td_plist field
201*04eeddc0SDimitry Andric       for (lldb::addr_t td =
202*04eeddc0SDimitry Andric                ReadPointerFromMemory(proc + offset_p_threads, error);
203*04eeddc0SDimitry Andric            td != 0; td = ReadPointerFromMemory(td + offset_td_plist, error)) {
204*04eeddc0SDimitry Andric         int32_t tid =
205*04eeddc0SDimitry Andric             ReadSignedIntegerFromMemory(td + offset_td_tid, 4, -1, error);
206*04eeddc0SDimitry Andric         lldb::addr_t pcb_addr =
207*04eeddc0SDimitry Andric             ReadPointerFromMemory(td + offset_td_pcb, error);
208*04eeddc0SDimitry Andric         // whether process was on CPU (-1 if not, otherwise CPU number)
209*04eeddc0SDimitry Andric         int32_t oncpu =
210*04eeddc0SDimitry Andric             ReadSignedIntegerFromMemory(td + offset_td_oncpu, 4, -2, error);
211*04eeddc0SDimitry Andric         // thread name
212*04eeddc0SDimitry Andric         char thread_name[fbsd_maxcomlen + 1];
213*04eeddc0SDimitry Andric         ReadCStringFromMemory(td + offset_td_name, thread_name,
214*04eeddc0SDimitry Andric                               sizeof(thread_name), error);
215*04eeddc0SDimitry Andric 
216*04eeddc0SDimitry Andric         // if we failed to read TID, ignore this thread
217*04eeddc0SDimitry Andric         if (tid == -1)
218*04eeddc0SDimitry Andric           continue;
219*04eeddc0SDimitry Andric 
220*04eeddc0SDimitry Andric         std::string thread_desc = llvm::formatv("(pid {0}) {1}", pid, comm);
221*04eeddc0SDimitry Andric         if (*thread_name && strcmp(thread_name, comm)) {
222*04eeddc0SDimitry Andric           thread_desc += '/';
223*04eeddc0SDimitry Andric           thread_desc += thread_name;
224*04eeddc0SDimitry Andric         }
225*04eeddc0SDimitry Andric 
226*04eeddc0SDimitry Andric         // roughly:
227*04eeddc0SDimitry Andric         // 1. if the thread crashed, its PCB is going to be at "dumppcb"
228*04eeddc0SDimitry Andric         // 2. if the thread was on CPU, its PCB is going to be on the CPU
229*04eeddc0SDimitry Andric         // 3. otherwise, its PCB is in the thread struct
230*04eeddc0SDimitry Andric         if (tid == dumptid) {
231*04eeddc0SDimitry Andric           // NB: dumppcb can be LLDB_INVALID_ADDRESS if reading it failed
232*04eeddc0SDimitry Andric           pcb_addr = dumppcb;
233*04eeddc0SDimitry Andric           thread_desc += " (crashed)";
234*04eeddc0SDimitry Andric         } else if (oncpu != -1) {
235*04eeddc0SDimitry Andric           // if we managed to read stoppcbs and pcb_size, use them to find
236*04eeddc0SDimitry Andric           // the correct PCB
237*04eeddc0SDimitry Andric           if (stoppcbs != LLDB_INVALID_ADDRESS && pcbsize > 0)
238*04eeddc0SDimitry Andric             pcb_addr = stoppcbs + oncpu * pcbsize;
239*04eeddc0SDimitry Andric           else
240*04eeddc0SDimitry Andric             pcb_addr = LLDB_INVALID_ADDRESS;
241*04eeddc0SDimitry Andric           thread_desc += llvm::formatv(" (on CPU {0})", oncpu);
242*04eeddc0SDimitry Andric         }
243*04eeddc0SDimitry Andric 
244*04eeddc0SDimitry Andric         ThreadSP thread_sp{
245*04eeddc0SDimitry Andric             new ThreadFreeBSDKernel(*this, tid, pcb_addr, thread_desc)};
2460eae32dcSDimitry Andric         new_thread_list.AddThread(thread_sp);
247*04eeddc0SDimitry Andric       }
248*04eeddc0SDimitry Andric     }
2490eae32dcSDimitry Andric   } else {
2500eae32dcSDimitry Andric     const uint32_t num_threads = old_thread_list.GetSize(false);
2510eae32dcSDimitry Andric     for (uint32_t i = 0; i < num_threads; ++i)
2520eae32dcSDimitry Andric       new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false));
2530eae32dcSDimitry Andric   }
2540eae32dcSDimitry Andric   return new_thread_list.GetSize(false) > 0;
2550eae32dcSDimitry Andric }
2560eae32dcSDimitry Andric 
2570eae32dcSDimitry Andric Status ProcessFreeBSDKernel::DoLoadCore() {
2580eae32dcSDimitry Andric   // The core is already loaded by CreateInstance().
2590eae32dcSDimitry Andric   return Status();
2600eae32dcSDimitry Andric }
2610eae32dcSDimitry Andric 
2620eae32dcSDimitry Andric DynamicLoader *ProcessFreeBSDKernel::GetDynamicLoader() {
2630eae32dcSDimitry Andric   if (m_dyld_up.get() == nullptr)
2640eae32dcSDimitry Andric     m_dyld_up.reset(DynamicLoader::FindPlugin(
2650eae32dcSDimitry Andric         this, DynamicLoaderStatic::GetPluginNameStatic()));
2660eae32dcSDimitry Andric   return m_dyld_up.get();
2670eae32dcSDimitry Andric }
2680eae32dcSDimitry Andric 
269*04eeddc0SDimitry Andric lldb::addr_t ProcessFreeBSDKernel::FindSymbol(const char *name) {
270*04eeddc0SDimitry Andric   ModuleSP mod_sp = GetTarget().GetExecutableModule();
271*04eeddc0SDimitry Andric   const Symbol *sym = mod_sp->FindFirstSymbolWithNameAndType(ConstString(name));
272*04eeddc0SDimitry Andric   return sym ? sym->GetLoadAddress(&GetTarget()) : LLDB_INVALID_ADDRESS;
273*04eeddc0SDimitry Andric }
274*04eeddc0SDimitry Andric 
2750eae32dcSDimitry Andric #if LLDB_ENABLE_FBSDVMCORE
2760eae32dcSDimitry Andric 
2770eae32dcSDimitry Andric ProcessFreeBSDKernelFVC::ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp,
2780eae32dcSDimitry Andric                                                  ListenerSP listener_sp,
2790eae32dcSDimitry Andric                                                  fvc_t *fvc)
2800eae32dcSDimitry Andric     : ProcessFreeBSDKernel(target_sp, listener_sp), m_fvc(fvc) {}
2810eae32dcSDimitry Andric 
2820eae32dcSDimitry Andric ProcessFreeBSDKernelFVC::~ProcessFreeBSDKernelFVC() {
2830eae32dcSDimitry Andric   if (m_fvc)
2840eae32dcSDimitry Andric     fvc_close(m_fvc);
2850eae32dcSDimitry Andric }
2860eae32dcSDimitry Andric 
2870eae32dcSDimitry Andric size_t ProcessFreeBSDKernelFVC::DoReadMemory(lldb::addr_t addr, void *buf,
2880eae32dcSDimitry Andric                                              size_t size, Status &error) {
2890eae32dcSDimitry Andric   ssize_t rd = 0;
2900eae32dcSDimitry Andric   rd = fvc_read(m_fvc, addr, buf, size);
2910eae32dcSDimitry Andric   if (rd < 0 || static_cast<size_t>(rd) != size) {
2920eae32dcSDimitry Andric     error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());
2930eae32dcSDimitry Andric     return rd > 0 ? rd : 0;
2940eae32dcSDimitry Andric   }
2950eae32dcSDimitry Andric   return rd;
2960eae32dcSDimitry Andric }
2970eae32dcSDimitry Andric 
2980eae32dcSDimitry Andric const char *ProcessFreeBSDKernelFVC::GetError() { return fvc_geterr(m_fvc); }
2990eae32dcSDimitry Andric 
3000eae32dcSDimitry Andric #endif // LLDB_ENABLE_FBSDVMCORE
3010eae32dcSDimitry Andric 
3020eae32dcSDimitry Andric #if defined(__FreeBSD__)
3030eae32dcSDimitry Andric 
3040eae32dcSDimitry Andric ProcessFreeBSDKernelKVM::ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp,
3050eae32dcSDimitry Andric                                                  ListenerSP listener_sp,
3060eae32dcSDimitry Andric                                                  kvm_t *fvc)
3070eae32dcSDimitry Andric     : ProcessFreeBSDKernel(target_sp, listener_sp), m_kvm(fvc) {}
3080eae32dcSDimitry Andric 
3090eae32dcSDimitry Andric ProcessFreeBSDKernelKVM::~ProcessFreeBSDKernelKVM() {
3100eae32dcSDimitry Andric   if (m_kvm)
3110eae32dcSDimitry Andric     kvm_close(m_kvm);
3120eae32dcSDimitry Andric }
3130eae32dcSDimitry Andric 
3140eae32dcSDimitry Andric size_t ProcessFreeBSDKernelKVM::DoReadMemory(lldb::addr_t addr, void *buf,
3150eae32dcSDimitry Andric                                              size_t size, Status &error) {
3160eae32dcSDimitry Andric   ssize_t rd = 0;
3170eae32dcSDimitry Andric   rd = kvm_read2(m_kvm, addr, buf, size);
3180eae32dcSDimitry Andric   if (rd < 0 || static_cast<size_t>(rd) != size) {
3190eae32dcSDimitry Andric     error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());
3200eae32dcSDimitry Andric     return rd > 0 ? rd : 0;
3210eae32dcSDimitry Andric   }
3220eae32dcSDimitry Andric   return rd;
3230eae32dcSDimitry Andric }
3240eae32dcSDimitry Andric 
3250eae32dcSDimitry Andric const char *ProcessFreeBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); }
3260eae32dcSDimitry Andric 
3270eae32dcSDimitry Andric #endif // defined(__FreeBSD__)
328