19c7fbc3fSMichał Górny //===-- ProcessFreeBSDKernel.cpp ------------------------------------------===//
29c7fbc3fSMichał Górny //
39c7fbc3fSMichał Górny // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
49c7fbc3fSMichał Górny // See https://llvm.org/LICENSE.txt for license information.
59c7fbc3fSMichał Górny // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
69c7fbc3fSMichał Górny //
79c7fbc3fSMichał Górny //===----------------------------------------------------------------------===//
89c7fbc3fSMichał Górny 
99c7fbc3fSMichał Górny #include "lldb/Core/Module.h"
109c7fbc3fSMichał Górny #include "lldb/Core/PluginManager.h"
119c7fbc3fSMichał Górny #include "lldb/Target/DynamicLoader.h"
129c7fbc3fSMichał Górny 
13fb785877SMichał Górny #include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h"
149c7fbc3fSMichał Górny #include "ProcessFreeBSDKernel.h"
159c7fbc3fSMichał Górny #include "ThreadFreeBSDKernel.h"
169c7fbc3fSMichał Górny 
17fb785877SMichał Górny #if LLDB_ENABLE_FBSDVMCORE
189c7fbc3fSMichał Górny #include <fvc.h>
19fb785877SMichał Górny #endif
20fb785877SMichał Górny #if defined(__FreeBSD__)
21fb785877SMichał Górny #include <kvm.h>
22fb785877SMichał Górny #endif
239c7fbc3fSMichał Górny 
249c7fbc3fSMichał Górny using namespace lldb;
259c7fbc3fSMichał Górny using namespace lldb_private;
269c7fbc3fSMichał Górny 
279c7fbc3fSMichał Górny LLDB_PLUGIN_DEFINE(ProcessFreeBSDKernel)
289c7fbc3fSMichał Górny 
29fb785877SMichał Górny namespace {
309c7fbc3fSMichał Górny 
31fb785877SMichał Górny #if LLDB_ENABLE_FBSDVMCORE
32fb785877SMichał Górny class ProcessFreeBSDKernelFVC : public ProcessFreeBSDKernel {
33fb785877SMichał Górny public:
34fb785877SMichał Górny   ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp, lldb::ListenerSP listener,
35fb785877SMichał Górny                           fvc_t *fvc);
36fb785877SMichał Górny 
37fb785877SMichał Górny   ~ProcessFreeBSDKernelFVC();
38fb785877SMichał Górny 
39fb785877SMichał Górny   size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
40fb785877SMichał Górny                       lldb_private::Status &error) override;
41fb785877SMichał Górny 
42fb785877SMichał Górny private:
43fb785877SMichał Górny   fvc_t *m_fvc;
44fb785877SMichał Górny 
45fb785877SMichał Górny   const char *GetError();
46fb785877SMichał Górny };
47fb785877SMichał Górny #endif // LLDB_ENABLE_FBSDVMCORE
48fb785877SMichał Górny 
49fb785877SMichał Górny #if defined(__FreeBSD__)
50fb785877SMichał Górny class ProcessFreeBSDKernelKVM : public ProcessFreeBSDKernel {
51fb785877SMichał Górny public:
52fb785877SMichał Górny   ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp, lldb::ListenerSP listener,
53fb785877SMichał Górny                           kvm_t *fvc);
54fb785877SMichał Górny 
55fb785877SMichał Górny   ~ProcessFreeBSDKernelKVM();
56fb785877SMichał Górny 
57fb785877SMichał Górny   size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
58fb785877SMichał Górny                       lldb_private::Status &error) override;
59fb785877SMichał Górny 
60fb785877SMichał Górny private:
61fb785877SMichał Górny   kvm_t *m_kvm;
62fb785877SMichał Górny 
63fb785877SMichał Górny   const char *GetError();
64fb785877SMichał Górny };
65fb785877SMichał Górny #endif // defined(__FreeBSD__)
66fb785877SMichał Górny 
67fb785877SMichał Górny } // namespace
68fb785877SMichał Górny 
ProcessFreeBSDKernel(lldb::TargetSP target_sp,ListenerSP listener_sp)69fb785877SMichał Górny ProcessFreeBSDKernel::ProcessFreeBSDKernel(lldb::TargetSP target_sp,
70fb785877SMichał Górny                                            ListenerSP listener_sp)
71fb785877SMichał Górny     : PostMortemProcess(target_sp, listener_sp) {}
729c7fbc3fSMichał Górny 
CreateInstance(lldb::TargetSP target_sp,ListenerSP listener_sp,const FileSpec * crash_file,bool can_connect)739c7fbc3fSMichał Górny lldb::ProcessSP ProcessFreeBSDKernel::CreateInstance(lldb::TargetSP target_sp,
749c7fbc3fSMichał Górny                                                      ListenerSP listener_sp,
759c7fbc3fSMichał Górny                                                      const FileSpec *crash_file,
769c7fbc3fSMichał Górny                                                      bool can_connect) {
779c7fbc3fSMichał Górny   ModuleSP executable = target_sp->GetExecutableModule();
789c7fbc3fSMichał Górny   if (crash_file && !can_connect && executable) {
79fb785877SMichał Górny #if LLDB_ENABLE_FBSDVMCORE
80fb785877SMichał Górny     fvc_t *fvc =
81fb785877SMichał Górny         fvc_open(executable->GetFileSpec().GetPath().c_str(),
829c7fbc3fSMichał Górny                  crash_file->GetPath().c_str(), nullptr, nullptr, nullptr);
839c7fbc3fSMichał Górny     if (fvc)
84fb785877SMichał Górny       return std::make_shared<ProcessFreeBSDKernelFVC>(target_sp, listener_sp,
85fb785877SMichał Górny                                                        fvc);
86fb785877SMichał Górny #endif
87fb785877SMichał Górny 
88fb785877SMichał Górny #if defined(__FreeBSD__)
89fb785877SMichał Górny     kvm_t *kvm =
90fb785877SMichał Górny         kvm_open2(executable->GetFileSpec().GetPath().c_str(),
91fb785877SMichał Górny                   crash_file->GetPath().c_str(), O_RDONLY, nullptr, nullptr);
92fb785877SMichał Górny     if (kvm)
93fb785877SMichał Górny       return std::make_shared<ProcessFreeBSDKernelKVM>(target_sp, listener_sp,
94fb785877SMichał Górny                                                        kvm);
95fb785877SMichał Górny #endif
969c7fbc3fSMichał Górny   }
97fb785877SMichał Górny   return nullptr;
989c7fbc3fSMichał Górny }
999c7fbc3fSMichał Górny 
Initialize()1009c7fbc3fSMichał Górny void ProcessFreeBSDKernel::Initialize() {
1019c7fbc3fSMichał Górny   static llvm::once_flag g_once_flag;
1029c7fbc3fSMichał Górny 
1039c7fbc3fSMichał Górny   llvm::call_once(g_once_flag, []() {
1049c7fbc3fSMichał Górny     PluginManager::RegisterPlugin(GetPluginNameStatic(),
1059c7fbc3fSMichał Górny                                   GetPluginDescriptionStatic(), CreateInstance);
1069c7fbc3fSMichał Górny   });
1079c7fbc3fSMichał Górny }
1089c7fbc3fSMichał Górny 
Terminate()1099c7fbc3fSMichał Górny void ProcessFreeBSDKernel::Terminate() {
1109c7fbc3fSMichał Górny   PluginManager::UnregisterPlugin(ProcessFreeBSDKernel::CreateInstance);
1119c7fbc3fSMichał Górny }
1129c7fbc3fSMichał Górny 
DoDestroy()1139c7fbc3fSMichał Górny Status ProcessFreeBSDKernel::DoDestroy() { return Status(); }
1149c7fbc3fSMichał Górny 
CanDebug(lldb::TargetSP target_sp,bool plugin_specified_by_name)1159c7fbc3fSMichał Górny bool ProcessFreeBSDKernel::CanDebug(lldb::TargetSP target_sp,
1169c7fbc3fSMichał Górny                                     bool plugin_specified_by_name) {
1179c7fbc3fSMichał Górny   return true;
1189c7fbc3fSMichał Górny }
1199c7fbc3fSMichał Górny 
RefreshStateAfterStop()1209c7fbc3fSMichał Górny void ProcessFreeBSDKernel::RefreshStateAfterStop() {}
1219c7fbc3fSMichał Górny 
DoUpdateThreadList(ThreadList & old_thread_list,ThreadList & new_thread_list)1229c7fbc3fSMichał Górny bool ProcessFreeBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list,
1239c7fbc3fSMichał Górny                                               ThreadList &new_thread_list) {
1249c7fbc3fSMichał Górny   if (old_thread_list.GetSize(false) == 0) {
1259c7fbc3fSMichał Górny     // Make up the thread the first time this is called so we can set our one
1269c7fbc3fSMichał Górny     // and only core thread state up.
1279c7fbc3fSMichał Górny 
1289c7fbc3fSMichał Górny     // We cannot construct a thread without a register context as that crashes
1299c7fbc3fSMichał Górny     // LLDB but we can construct a process without threads to provide minimal
1309c7fbc3fSMichał Górny     // memory reading support.
1319c7fbc3fSMichał Górny     switch (GetTarget().GetArchitecture().GetMachine()) {
1329c7fbc3fSMichał Górny     case llvm::Triple::aarch64:
1339c7fbc3fSMichał Górny     case llvm::Triple::x86:
1349c7fbc3fSMichał Górny     case llvm::Triple::x86_64:
1359c7fbc3fSMichał Górny       break;
1369c7fbc3fSMichał Górny     default:
1379c7fbc3fSMichał Górny       return false;
1389c7fbc3fSMichał Górny     }
1399c7fbc3fSMichał Górny 
140*9b1d27b2SMichał Górny     Status error;
141*9b1d27b2SMichał Górny 
142*9b1d27b2SMichał Górny     // struct field offsets are written as symbols so that we don't have
143*9b1d27b2SMichał Górny     // to figure them out ourselves
144*9b1d27b2SMichał Górny     int32_t offset_p_list = ReadSignedIntegerFromMemory(
145*9b1d27b2SMichał Górny         FindSymbol("proc_off_p_list"), 4, -1, error);
146*9b1d27b2SMichał Górny     int32_t offset_p_pid =
147*9b1d27b2SMichał Górny         ReadSignedIntegerFromMemory(FindSymbol("proc_off_p_pid"), 4, -1, error);
148*9b1d27b2SMichał Górny     int32_t offset_p_threads = ReadSignedIntegerFromMemory(
149*9b1d27b2SMichał Górny         FindSymbol("proc_off_p_threads"), 4, -1, error);
150*9b1d27b2SMichał Górny     int32_t offset_p_comm = ReadSignedIntegerFromMemory(
151*9b1d27b2SMichał Górny         FindSymbol("proc_off_p_comm"), 4, -1, error);
152*9b1d27b2SMichał Górny 
153*9b1d27b2SMichał Górny     int32_t offset_td_tid = ReadSignedIntegerFromMemory(
154*9b1d27b2SMichał Górny         FindSymbol("thread_off_td_tid"), 4, -1, error);
155*9b1d27b2SMichał Górny     int32_t offset_td_plist = ReadSignedIntegerFromMemory(
156*9b1d27b2SMichał Górny         FindSymbol("thread_off_td_plist"), 4, -1, error);
157*9b1d27b2SMichał Górny     int32_t offset_td_pcb = ReadSignedIntegerFromMemory(
158*9b1d27b2SMichał Górny         FindSymbol("thread_off_td_pcb"), 4, -1, error);
159*9b1d27b2SMichał Górny     int32_t offset_td_oncpu = ReadSignedIntegerFromMemory(
160*9b1d27b2SMichał Górny         FindSymbol("thread_off_td_oncpu"), 4, -1, error);
161*9b1d27b2SMichał Górny     int32_t offset_td_name = ReadSignedIntegerFromMemory(
162*9b1d27b2SMichał Górny         FindSymbol("thread_off_td_name"), 4, -1, error);
163*9b1d27b2SMichał Górny 
164*9b1d27b2SMichał Górny     // fail if we were not able to read any of the offsets
165*9b1d27b2SMichał Górny     if (offset_p_list == -1 || offset_p_pid == -1 || offset_p_threads == -1 ||
166*9b1d27b2SMichał Górny         offset_p_comm == -1 || offset_td_tid == -1 || offset_td_plist == -1 ||
167*9b1d27b2SMichał Górny         offset_td_pcb == -1 || offset_td_oncpu == -1 || offset_td_name == -1)
168*9b1d27b2SMichał Górny       return false;
169*9b1d27b2SMichał Górny 
170*9b1d27b2SMichał Górny     // dumptid contains the thread-id of the crashing thread
171*9b1d27b2SMichał Górny     // dumppcb contains its PCB
172*9b1d27b2SMichał Górny     int32_t dumptid =
173*9b1d27b2SMichał Górny         ReadSignedIntegerFromMemory(FindSymbol("dumptid"), 4, -1, error);
174*9b1d27b2SMichał Górny     lldb::addr_t dumppcb = FindSymbol("dumppcb");
175*9b1d27b2SMichał Górny 
176*9b1d27b2SMichał Górny     // stoppcbs is an array of PCBs on all CPUs
177*9b1d27b2SMichał Górny     // each element is of size pcb_size
178*9b1d27b2SMichał Górny     int32_t pcbsize =
179*9b1d27b2SMichał Górny         ReadSignedIntegerFromMemory(FindSymbol("pcb_size"), 4, -1, error);
180*9b1d27b2SMichał Górny     lldb::addr_t stoppcbs = FindSymbol("stoppcbs");
181*9b1d27b2SMichał Górny 
182*9b1d27b2SMichał Górny     // from FreeBSD sys/param.h
183*9b1d27b2SMichał Górny     constexpr size_t fbsd_maxcomlen = 19;
184*9b1d27b2SMichał Górny 
185*9b1d27b2SMichał Górny     // iterate through a linked list of all processes
186*9b1d27b2SMichał Górny     // allproc is a pointer to the first list element, p_list field
187*9b1d27b2SMichał Górny     // (found at offset_p_list) specifies the next element
188*9b1d27b2SMichał Górny     for (lldb::addr_t proc =
189*9b1d27b2SMichał Górny              ReadPointerFromMemory(FindSymbol("allproc"), error);
190*9b1d27b2SMichał Górny          proc != 0 && proc != LLDB_INVALID_ADDRESS;
191*9b1d27b2SMichał Górny          proc = ReadPointerFromMemory(proc + offset_p_list, error)) {
192*9b1d27b2SMichał Górny       int32_t pid =
193*9b1d27b2SMichał Górny           ReadSignedIntegerFromMemory(proc + offset_p_pid, 4, -1, error);
194*9b1d27b2SMichał Górny       // process' command-line string
195*9b1d27b2SMichał Górny       char comm[fbsd_maxcomlen + 1];
196*9b1d27b2SMichał Górny       ReadCStringFromMemory(proc + offset_p_comm, comm, sizeof(comm), error);
197*9b1d27b2SMichał Górny 
198*9b1d27b2SMichał Górny       // iterate through a linked list of all process' threads
199*9b1d27b2SMichał Górny       // the initial thread is found in process' p_threads, subsequent
200*9b1d27b2SMichał Górny       // elements are linked via td_plist field
201*9b1d27b2SMichał Górny       for (lldb::addr_t td =
202*9b1d27b2SMichał Górny                ReadPointerFromMemory(proc + offset_p_threads, error);
203*9b1d27b2SMichał Górny            td != 0; td = ReadPointerFromMemory(td + offset_td_plist, error)) {
204*9b1d27b2SMichał Górny         int32_t tid =
205*9b1d27b2SMichał Górny             ReadSignedIntegerFromMemory(td + offset_td_tid, 4, -1, error);
206*9b1d27b2SMichał Górny         lldb::addr_t pcb_addr =
207*9b1d27b2SMichał Górny             ReadPointerFromMemory(td + offset_td_pcb, error);
208*9b1d27b2SMichał Górny         // whether process was on CPU (-1 if not, otherwise CPU number)
209*9b1d27b2SMichał Górny         int32_t oncpu =
210*9b1d27b2SMichał Górny             ReadSignedIntegerFromMemory(td + offset_td_oncpu, 4, -2, error);
211*9b1d27b2SMichał Górny         // thread name
212*9b1d27b2SMichał Górny         char thread_name[fbsd_maxcomlen + 1];
213*9b1d27b2SMichał Górny         ReadCStringFromMemory(td + offset_td_name, thread_name,
214*9b1d27b2SMichał Górny                               sizeof(thread_name), error);
215*9b1d27b2SMichał Górny 
216*9b1d27b2SMichał Górny         // if we failed to read TID, ignore this thread
217*9b1d27b2SMichał Górny         if (tid == -1)
218*9b1d27b2SMichał Górny           continue;
219*9b1d27b2SMichał Górny 
220*9b1d27b2SMichał Górny         std::string thread_desc = llvm::formatv("(pid {0}) {1}", pid, comm);
221*9b1d27b2SMichał Górny         if (*thread_name && strcmp(thread_name, comm)) {
222*9b1d27b2SMichał Górny           thread_desc += '/';
223*9b1d27b2SMichał Górny           thread_desc += thread_name;
224*9b1d27b2SMichał Górny         }
225*9b1d27b2SMichał Górny 
226*9b1d27b2SMichał Górny         // roughly:
227*9b1d27b2SMichał Górny         // 1. if the thread crashed, its PCB is going to be at "dumppcb"
228*9b1d27b2SMichał Górny         // 2. if the thread was on CPU, its PCB is going to be on the CPU
229*9b1d27b2SMichał Górny         // 3. otherwise, its PCB is in the thread struct
230*9b1d27b2SMichał Górny         if (tid == dumptid) {
231*9b1d27b2SMichał Górny           // NB: dumppcb can be LLDB_INVALID_ADDRESS if reading it failed
232*9b1d27b2SMichał Górny           pcb_addr = dumppcb;
233*9b1d27b2SMichał Górny           thread_desc += " (crashed)";
234*9b1d27b2SMichał Górny         } else if (oncpu != -1) {
235*9b1d27b2SMichał Górny           // if we managed to read stoppcbs and pcb_size, use them to find
236*9b1d27b2SMichał Górny           // the correct PCB
237*9b1d27b2SMichał Górny           if (stoppcbs != LLDB_INVALID_ADDRESS && pcbsize > 0)
238*9b1d27b2SMichał Górny             pcb_addr = stoppcbs + oncpu * pcbsize;
239*9b1d27b2SMichał Górny           else
240*9b1d27b2SMichał Górny             pcb_addr = LLDB_INVALID_ADDRESS;
241*9b1d27b2SMichał Górny           thread_desc += llvm::formatv(" (on CPU {0})", oncpu);
242*9b1d27b2SMichał Górny         }
243*9b1d27b2SMichał Górny 
244*9b1d27b2SMichał Górny         ThreadSP thread_sp{
245*9b1d27b2SMichał Górny             new ThreadFreeBSDKernel(*this, tid, pcb_addr, thread_desc)};
2469c7fbc3fSMichał Górny         new_thread_list.AddThread(thread_sp);
247*9b1d27b2SMichał Górny       }
248*9b1d27b2SMichał Górny     }
2499c7fbc3fSMichał Górny   } else {
2509c7fbc3fSMichał Górny     const uint32_t num_threads = old_thread_list.GetSize(false);
2519c7fbc3fSMichał Górny     for (uint32_t i = 0; i < num_threads; ++i)
2529c7fbc3fSMichał Górny       new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false));
2539c7fbc3fSMichał Górny   }
2549c7fbc3fSMichał Górny   return new_thread_list.GetSize(false) > 0;
2559c7fbc3fSMichał Górny }
2569c7fbc3fSMichał Górny 
DoLoadCore()2579c7fbc3fSMichał Górny Status ProcessFreeBSDKernel::DoLoadCore() {
2589c7fbc3fSMichał Górny   // The core is already loaded by CreateInstance().
2599c7fbc3fSMichał Górny   return Status();
2609c7fbc3fSMichał Górny }
2619c7fbc3fSMichał Górny 
GetDynamicLoader()2629c7fbc3fSMichał Górny DynamicLoader *ProcessFreeBSDKernel::GetDynamicLoader() {
2639c7fbc3fSMichał Górny   if (m_dyld_up.get() == nullptr)
2649c7fbc3fSMichał Górny     m_dyld_up.reset(DynamicLoader::FindPlugin(
2659c7fbc3fSMichał Górny         this, DynamicLoaderStatic::GetPluginNameStatic()));
2669c7fbc3fSMichał Górny   return m_dyld_up.get();
2679c7fbc3fSMichał Górny }
268fb785877SMichał Górny 
FindSymbol(const char * name)269*9b1d27b2SMichał Górny lldb::addr_t ProcessFreeBSDKernel::FindSymbol(const char *name) {
270*9b1d27b2SMichał Górny   ModuleSP mod_sp = GetTarget().GetExecutableModule();
271*9b1d27b2SMichał Górny   const Symbol *sym = mod_sp->FindFirstSymbolWithNameAndType(ConstString(name));
272*9b1d27b2SMichał Górny   return sym ? sym->GetLoadAddress(&GetTarget()) : LLDB_INVALID_ADDRESS;
273*9b1d27b2SMichał Górny }
274*9b1d27b2SMichał Górny 
275fb785877SMichał Górny #if LLDB_ENABLE_FBSDVMCORE
276fb785877SMichał Górny 
ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp,ListenerSP listener_sp,fvc_t * fvc)277fb785877SMichał Górny ProcessFreeBSDKernelFVC::ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp,
278fb785877SMichał Górny                                                  ListenerSP listener_sp,
279fb785877SMichał Górny                                                  fvc_t *fvc)
280fb785877SMichał Górny     : ProcessFreeBSDKernel(target_sp, listener_sp), m_fvc(fvc) {}
281fb785877SMichał Górny 
~ProcessFreeBSDKernelFVC()282fb785877SMichał Górny ProcessFreeBSDKernelFVC::~ProcessFreeBSDKernelFVC() {
283fb785877SMichał Górny   if (m_fvc)
284fb785877SMichał Górny     fvc_close(m_fvc);
285fb785877SMichał Górny }
286fb785877SMichał Górny 
DoReadMemory(lldb::addr_t addr,void * buf,size_t size,Status & error)287fb785877SMichał Górny size_t ProcessFreeBSDKernelFVC::DoReadMemory(lldb::addr_t addr, void *buf,
288fb785877SMichał Górny                                              size_t size, Status &error) {
289fb785877SMichał Górny   ssize_t rd = 0;
290fb785877SMichał Górny   rd = fvc_read(m_fvc, addr, buf, size);
291fb785877SMichał Górny   if (rd < 0 || static_cast<size_t>(rd) != size) {
292fb785877SMichał Górny     error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());
293fb785877SMichał Górny     return rd > 0 ? rd : 0;
294fb785877SMichał Górny   }
295fb785877SMichał Górny   return rd;
296fb785877SMichał Górny }
297fb785877SMichał Górny 
GetError()298fb785877SMichał Górny const char *ProcessFreeBSDKernelFVC::GetError() { return fvc_geterr(m_fvc); }
299fb785877SMichał Górny 
300fb785877SMichał Górny #endif // LLDB_ENABLE_FBSDVMCORE
301fb785877SMichał Górny 
302fb785877SMichał Górny #if defined(__FreeBSD__)
303fb785877SMichał Górny 
ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp,ListenerSP listener_sp,kvm_t * fvc)304fb785877SMichał Górny ProcessFreeBSDKernelKVM::ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp,
305fb785877SMichał Górny                                                  ListenerSP listener_sp,
306fb785877SMichał Górny                                                  kvm_t *fvc)
307fb785877SMichał Górny     : ProcessFreeBSDKernel(target_sp, listener_sp), m_kvm(fvc) {}
308fb785877SMichał Górny 
~ProcessFreeBSDKernelKVM()309fb785877SMichał Górny ProcessFreeBSDKernelKVM::~ProcessFreeBSDKernelKVM() {
310fb785877SMichał Górny   if (m_kvm)
311fb785877SMichał Górny     kvm_close(m_kvm);
312fb785877SMichał Górny }
313fb785877SMichał Górny 
DoReadMemory(lldb::addr_t addr,void * buf,size_t size,Status & error)314fb785877SMichał Górny size_t ProcessFreeBSDKernelKVM::DoReadMemory(lldb::addr_t addr, void *buf,
315fb785877SMichał Górny                                              size_t size, Status &error) {
316fb785877SMichał Górny   ssize_t rd = 0;
317fb785877SMichał Górny   rd = kvm_read2(m_kvm, addr, buf, size);
318fb785877SMichał Górny   if (rd < 0 || static_cast<size_t>(rd) != size) {
319fb785877SMichał Górny     error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());
320fb785877SMichał Górny     return rd > 0 ? rd : 0;
321fb785877SMichał Górny   }
322fb785877SMichał Górny   return rd;
323fb785877SMichał Górny }
324fb785877SMichał Górny 
GetError()325fb785877SMichał Górny const char *ProcessFreeBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); }
326fb785877SMichał Górny 
327fb785877SMichał Górny #endif // defined(__FreeBSD__)
328