xref: /llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp (revision 0642cd768b80665585c8500bed2933a3b99123dc)
1 //===-- ProcessFreeBSDKernel.cpp ------------------------------------------===//
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 "lldb/Core/Module.h"
10 #include "lldb/Core/PluginManager.h"
11 #include "lldb/Target/DynamicLoader.h"
12 
13 #include "Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.h"
14 #include "ProcessFreeBSDKernel.h"
15 #include "ThreadFreeBSDKernel.h"
16 
17 #if LLDB_ENABLE_FBSDVMCORE
18 #include <fvc.h>
19 #endif
20 #if defined(__FreeBSD__)
21 #include <kvm.h>
22 #endif
23 
24 using namespace lldb;
25 using namespace lldb_private;
26 
27 LLDB_PLUGIN_DEFINE(ProcessFreeBSDKernel)
28 
29 namespace {
30 
31 #if LLDB_ENABLE_FBSDVMCORE
32 class ProcessFreeBSDKernelFVC : public ProcessFreeBSDKernel {
33 public:
34   ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp, lldb::ListenerSP listener,
35                           fvc_t *fvc, const FileSpec &core_file);
36 
37   ~ProcessFreeBSDKernelFVC();
38 
39   size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
40                       lldb_private::Status &error) override;
41 
42 private:
43   fvc_t *m_fvc;
44 
45   const char *GetError();
46 };
47 #endif // LLDB_ENABLE_FBSDVMCORE
48 
49 #if defined(__FreeBSD__)
50 class ProcessFreeBSDKernelKVM : public ProcessFreeBSDKernel {
51 public:
52   ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp, lldb::ListenerSP listener,
53                           kvm_t *fvc, const FileSpec &core_file);
54 
55   ~ProcessFreeBSDKernelKVM();
56 
57   size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
58                       lldb_private::Status &error) override;
59 
60 private:
61   kvm_t *m_kvm;
62 
63   const char *GetError();
64 };
65 #endif // defined(__FreeBSD__)
66 
67 } // namespace
68 
69 ProcessFreeBSDKernel::ProcessFreeBSDKernel(lldb::TargetSP target_sp,
70                                            ListenerSP listener_sp,
71                                            const FileSpec &core_file)
72     : PostMortemProcess(target_sp, listener_sp, core_file) {}
73 
74 lldb::ProcessSP ProcessFreeBSDKernel::CreateInstance(lldb::TargetSP target_sp,
75                                                      ListenerSP listener_sp,
76                                                      const FileSpec *crash_file,
77                                                      bool can_connect) {
78   ModuleSP executable = target_sp->GetExecutableModule();
79   if (crash_file && !can_connect && executable) {
80 #if LLDB_ENABLE_FBSDVMCORE
81     fvc_t *fvc =
82         fvc_open(executable->GetFileSpec().GetPath().c_str(),
83                  crash_file->GetPath().c_str(), nullptr, nullptr, nullptr);
84     if (fvc)
85       return std::make_shared<ProcessFreeBSDKernelFVC>(target_sp, listener_sp,
86                                                        fvc, *crash_file);
87 #endif
88 
89 #if defined(__FreeBSD__)
90     kvm_t *kvm =
91         kvm_open2(executable->GetFileSpec().GetPath().c_str(),
92                   crash_file->GetPath().c_str(), O_RDONLY, nullptr, nullptr);
93     if (kvm)
94       return std::make_shared<ProcessFreeBSDKernelKVM>(target_sp, listener_sp,
95                                                        kvm, *crash_file);
96 #endif
97   }
98   return nullptr;
99 }
100 
101 void ProcessFreeBSDKernel::Initialize() {
102   static llvm::once_flag g_once_flag;
103 
104   llvm::call_once(g_once_flag, []() {
105     PluginManager::RegisterPlugin(GetPluginNameStatic(),
106                                   GetPluginDescriptionStatic(), CreateInstance);
107   });
108 }
109 
110 void ProcessFreeBSDKernel::Terminate() {
111   PluginManager::UnregisterPlugin(ProcessFreeBSDKernel::CreateInstance);
112 }
113 
114 Status ProcessFreeBSDKernel::DoDestroy() { return Status(); }
115 
116 bool ProcessFreeBSDKernel::CanDebug(lldb::TargetSP target_sp,
117                                     bool plugin_specified_by_name) {
118   return true;
119 }
120 
121 void ProcessFreeBSDKernel::RefreshStateAfterStop() {}
122 
123 bool ProcessFreeBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list,
124                                               ThreadList &new_thread_list) {
125   if (old_thread_list.GetSize(false) == 0) {
126     // Make up the thread the first time this is called so we can set our one
127     // and only core thread state up.
128 
129     // We cannot construct a thread without a register context as that crashes
130     // LLDB but we can construct a process without threads to provide minimal
131     // memory reading support.
132     switch (GetTarget().GetArchitecture().GetMachine()) {
133     case llvm::Triple::aarch64:
134     case llvm::Triple::x86:
135     case llvm::Triple::x86_64:
136       break;
137     default:
138       return false;
139     }
140 
141     Status error;
142 
143     // struct field offsets are written as symbols so that we don't have
144     // to figure them out ourselves
145     int32_t offset_p_list = ReadSignedIntegerFromMemory(
146         FindSymbol("proc_off_p_list"), 4, -1, error);
147     int32_t offset_p_pid =
148         ReadSignedIntegerFromMemory(FindSymbol("proc_off_p_pid"), 4, -1, error);
149     int32_t offset_p_threads = ReadSignedIntegerFromMemory(
150         FindSymbol("proc_off_p_threads"), 4, -1, error);
151     int32_t offset_p_comm = ReadSignedIntegerFromMemory(
152         FindSymbol("proc_off_p_comm"), 4, -1, error);
153 
154     int32_t offset_td_tid = ReadSignedIntegerFromMemory(
155         FindSymbol("thread_off_td_tid"), 4, -1, error);
156     int32_t offset_td_plist = ReadSignedIntegerFromMemory(
157         FindSymbol("thread_off_td_plist"), 4, -1, error);
158     int32_t offset_td_pcb = ReadSignedIntegerFromMemory(
159         FindSymbol("thread_off_td_pcb"), 4, -1, error);
160     int32_t offset_td_oncpu = ReadSignedIntegerFromMemory(
161         FindSymbol("thread_off_td_oncpu"), 4, -1, error);
162     int32_t offset_td_name = ReadSignedIntegerFromMemory(
163         FindSymbol("thread_off_td_name"), 4, -1, error);
164 
165     // fail if we were not able to read any of the offsets
166     if (offset_p_list == -1 || offset_p_pid == -1 || offset_p_threads == -1 ||
167         offset_p_comm == -1 || offset_td_tid == -1 || offset_td_plist == -1 ||
168         offset_td_pcb == -1 || offset_td_oncpu == -1 || offset_td_name == -1)
169       return false;
170 
171     // dumptid contains the thread-id of the crashing thread
172     // dumppcb contains its PCB
173     int32_t dumptid =
174         ReadSignedIntegerFromMemory(FindSymbol("dumptid"), 4, -1, error);
175     lldb::addr_t dumppcb = FindSymbol("dumppcb");
176 
177     // stoppcbs is an array of PCBs on all CPUs
178     // each element is of size pcb_size
179     int32_t pcbsize =
180         ReadSignedIntegerFromMemory(FindSymbol("pcb_size"), 4, -1, error);
181     lldb::addr_t stoppcbs = FindSymbol("stoppcbs");
182 
183     // from FreeBSD sys/param.h
184     constexpr size_t fbsd_maxcomlen = 19;
185 
186     // iterate through a linked list of all processes
187     // allproc is a pointer to the first list element, p_list field
188     // (found at offset_p_list) specifies the next element
189     for (lldb::addr_t proc =
190              ReadPointerFromMemory(FindSymbol("allproc"), error);
191          proc != 0 && proc != LLDB_INVALID_ADDRESS;
192          proc = ReadPointerFromMemory(proc + offset_p_list, error)) {
193       int32_t pid =
194           ReadSignedIntegerFromMemory(proc + offset_p_pid, 4, -1, error);
195       // process' command-line string
196       char comm[fbsd_maxcomlen + 1];
197       ReadCStringFromMemory(proc + offset_p_comm, comm, sizeof(comm), error);
198 
199       // iterate through a linked list of all process' threads
200       // the initial thread is found in process' p_threads, subsequent
201       // elements are linked via td_plist field
202       for (lldb::addr_t td =
203                ReadPointerFromMemory(proc + offset_p_threads, error);
204            td != 0; td = ReadPointerFromMemory(td + offset_td_plist, error)) {
205         int32_t tid =
206             ReadSignedIntegerFromMemory(td + offset_td_tid, 4, -1, error);
207         lldb::addr_t pcb_addr =
208             ReadPointerFromMemory(td + offset_td_pcb, error);
209         // whether process was on CPU (-1 if not, otherwise CPU number)
210         int32_t oncpu =
211             ReadSignedIntegerFromMemory(td + offset_td_oncpu, 4, -2, error);
212         // thread name
213         char thread_name[fbsd_maxcomlen + 1];
214         ReadCStringFromMemory(td + offset_td_name, thread_name,
215                               sizeof(thread_name), error);
216 
217         // if we failed to read TID, ignore this thread
218         if (tid == -1)
219           continue;
220 
221         std::string thread_desc = llvm::formatv("(pid {0}) {1}", pid, comm);
222         if (*thread_name && strcmp(thread_name, comm)) {
223           thread_desc += '/';
224           thread_desc += thread_name;
225         }
226 
227         // roughly:
228         // 1. if the thread crashed, its PCB is going to be at "dumppcb"
229         // 2. if the thread was on CPU, its PCB is going to be on the CPU
230         // 3. otherwise, its PCB is in the thread struct
231         if (tid == dumptid) {
232           // NB: dumppcb can be LLDB_INVALID_ADDRESS if reading it failed
233           pcb_addr = dumppcb;
234           thread_desc += " (crashed)";
235         } else if (oncpu != -1) {
236           // if we managed to read stoppcbs and pcb_size, use them to find
237           // the correct PCB
238           if (stoppcbs != LLDB_INVALID_ADDRESS && pcbsize > 0)
239             pcb_addr = stoppcbs + oncpu * pcbsize;
240           else
241             pcb_addr = LLDB_INVALID_ADDRESS;
242           thread_desc += llvm::formatv(" (on CPU {0})", oncpu);
243         }
244 
245         ThreadSP thread_sp{
246             new ThreadFreeBSDKernel(*this, tid, pcb_addr, thread_desc)};
247         new_thread_list.AddThread(thread_sp);
248       }
249     }
250   } else {
251     const uint32_t num_threads = old_thread_list.GetSize(false);
252     for (uint32_t i = 0; i < num_threads; ++i)
253       new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false));
254   }
255   return new_thread_list.GetSize(false) > 0;
256 }
257 
258 Status ProcessFreeBSDKernel::DoLoadCore() {
259   // The core is already loaded by CreateInstance().
260   return Status();
261 }
262 
263 DynamicLoader *ProcessFreeBSDKernel::GetDynamicLoader() {
264   if (m_dyld_up.get() == nullptr)
265     m_dyld_up.reset(DynamicLoader::FindPlugin(
266         this, DynamicLoaderFreeBSDKernel::GetPluginNameStatic()));
267   return m_dyld_up.get();
268 }
269 
270 lldb::addr_t ProcessFreeBSDKernel::FindSymbol(const char *name) {
271   ModuleSP mod_sp = GetTarget().GetExecutableModule();
272   const Symbol *sym = mod_sp->FindFirstSymbolWithNameAndType(ConstString(name));
273   return sym ? sym->GetLoadAddress(&GetTarget()) : LLDB_INVALID_ADDRESS;
274 }
275 
276 #if LLDB_ENABLE_FBSDVMCORE
277 
278 ProcessFreeBSDKernelFVC::ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp,
279                                                  ListenerSP listener_sp,
280                                                  fvc_t *fvc,
281                                                  const FileSpec &core_file)
282     : ProcessFreeBSDKernel(target_sp, listener_sp, crash_file), m_fvc(fvc) {}
283 
284 ProcessFreeBSDKernelFVC::~ProcessFreeBSDKernelFVC() {
285   if (m_fvc)
286     fvc_close(m_fvc);
287 }
288 
289 size_t ProcessFreeBSDKernelFVC::DoReadMemory(lldb::addr_t addr, void *buf,
290                                              size_t size, Status &error) {
291   ssize_t rd = 0;
292   rd = fvc_read(m_fvc, addr, buf, size);
293   if (rd < 0 || static_cast<size_t>(rd) != size) {
294     error = Status::FromErrorStringWithFormat("Reading memory failed: %s",
295                                               GetError());
296     return rd > 0 ? rd : 0;
297   }
298   return rd;
299 }
300 
301 const char *ProcessFreeBSDKernelFVC::GetError() { return fvc_geterr(m_fvc); }
302 
303 #endif // LLDB_ENABLE_FBSDVMCORE
304 
305 #if defined(__FreeBSD__)
306 
307 ProcessFreeBSDKernelKVM::ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp,
308                                                  ListenerSP listener_sp,
309                                                  kvm_t *fvc,
310                                                  const FileSpec &core_file)
311     : ProcessFreeBSDKernel(target_sp, listener_sp, core_file), m_kvm(fvc) {}
312 
313 ProcessFreeBSDKernelKVM::~ProcessFreeBSDKernelKVM() {
314   if (m_kvm)
315     kvm_close(m_kvm);
316 }
317 
318 size_t ProcessFreeBSDKernelKVM::DoReadMemory(lldb::addr_t addr, void *buf,
319                                              size_t size, Status &error) {
320   ssize_t rd = 0;
321   rd = kvm_read2(m_kvm, addr, buf, size);
322   if (rd < 0 || static_cast<size_t>(rd) != size) {
323     error = Status::FromErrorStringWithFormat("Reading memory failed: %s",
324                                               GetError());
325     return rd > 0 ? rd : 0;
326   }
327   return rd;
328 }
329 
330 const char *ProcessFreeBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); }
331 
332 #endif // defined(__FreeBSD__)
333