LLDB mainline
ProcessFreeBSDKernel.cpp
Go to the documentation of this file.
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"
12
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
24using namespace lldb;
25using namespace lldb_private;
26
28
29namespace {
30
31#if LLDB_ENABLE_FBSDVMCORE
32class ProcessFreeBSDKernelFVC : public ProcessFreeBSDKernel {
33public:
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
42private:
43 fvc_t *m_fvc;
44
45 const char *GetError();
46};
47#endif // LLDB_ENABLE_FBSDVMCORE
48
49#if defined(__FreeBSD__)
50class ProcessFreeBSDKernelKVM : public ProcessFreeBSDKernel {
51public:
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
60private:
61 kvm_t *m_kvm;
62
63 const char *GetError();
64};
65#endif // defined(__FreeBSD__)
66
67} // namespace
68
70 ListenerSP listener_sp,
71 const FileSpec &core_file)
72 : PostMortemProcess(target_sp, listener_sp, core_file) {}
73
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
102 static llvm::once_flag g_once_flag;
103
104 llvm::call_once(g_once_flag, []() {
107 });
108}
109
112}
113
115
117 bool plugin_specified_by_name) {
118 return true;
119}
120
122
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
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 =
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
259 // The core is already loaded by CreateInstance().
260 return Status();
261}
262
264 if (m_dyld_up.get() == nullptr)
267 return m_dyld_up.get();
268}
269
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
278ProcessFreeBSDKernelFVC::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
284ProcessFreeBSDKernelFVC::~ProcessFreeBSDKernelFVC() {
285 if (m_fvc)
286 fvc_close(m_fvc);
287}
288
289size_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.SetErrorStringWithFormat("Reading memory failed: %s", GetError());
295 return rd > 0 ? rd : 0;
296 }
297 return rd;
298}
299
300const char *ProcessFreeBSDKernelFVC::GetError() { return fvc_geterr(m_fvc); }
301
302#endif // LLDB_ENABLE_FBSDVMCORE
303
304#if defined(__FreeBSD__)
305
306ProcessFreeBSDKernelKVM::ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp,
307 ListenerSP listener_sp,
308 kvm_t *fvc,
309 const FileSpec &core_file)
310 : ProcessFreeBSDKernel(target_sp, listener_sp, core_file), m_kvm(fvc) {}
311
312ProcessFreeBSDKernelKVM::~ProcessFreeBSDKernelKVM() {
313 if (m_kvm)
314 kvm_close(m_kvm);
315}
316
317size_t ProcessFreeBSDKernelKVM::DoReadMemory(lldb::addr_t addr, void *buf,
318 size_t size, Status &error) {
319 ssize_t rd = 0;
320 rd = kvm_read2(m_kvm, addr, buf, size);
321 if (rd < 0 || static_cast<size_t>(rd) != size) {
322 error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());
323 return rd > 0 ? rd : 0;
324 }
325 return rd;
326}
327
328const char *ProcessFreeBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); }
329
330#endif // defined(__FreeBSD__)
static llvm::raw_ostream & error(Stream &strm)
#define LLDB_PLUGIN_DEFINE(PluginName)
Definition: PluginManager.h:31
static llvm::StringRef GetPluginNameStatic()
lldb_private::Status DoLoadCore() override
bool DoUpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list) override
Update the thread list following process plug-in's specific logic.
bool CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) override
Check if a plug-in instance can debug the file in module.
void RefreshStateAfterStop() override
Currently called as part of ShouldStop.
lldb::addr_t FindSymbol(const char *name)
lldb_private::DynamicLoader * GetDynamicLoader() override
Get the dynamic loader plug-in for this process.
ProcessFreeBSDKernel(lldb::TargetSP target_sp, lldb::ListenerSP listener, const lldb_private::FileSpec &core_file)
static llvm::StringRef GetPluginNameStatic()
lldb_private::Status DoDestroy() override
static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener, const lldb_private::FileSpec *crash_file_path, bool can_connect)
static llvm::StringRef GetPluginDescriptionStatic()
A uniqued constant string class.
Definition: ConstString.h:40
A plug-in interface definition class for dynamic loaders.
Definition: DynamicLoader.h:52
static DynamicLoader * FindPlugin(Process *process, llvm::StringRef plugin_name)
Find a dynamic loader plugin for a given process.
A file utility class.
Definition: FileSpec.h:56
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:367
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, ABICreateInstance create_callback)
static bool UnregisterPlugin(ABICreateInstance create_callback)
Base class for all processes that don't represent a live process, such as coredumps or processes trac...
int64_t ReadSignedIntegerFromMemory(lldb::addr_t load_addr, size_t byte_size, int64_t fail_value, Status &error)
Definition: Process.cpp:2091
size_t ReadCStringFromMemory(lldb::addr_t vm_addr, char *cstr, size_t cstr_max_len, Status &error)
Read a NULL terminated C string from memory.
Definition: Process.cpp:2005
lldb::DynamicLoaderUP m_dyld_up
Definition: Process.h:3065
lldb::addr_t ReadPointerFromMemory(lldb::addr_t vm_addr, Status &error)
Definition: Process.cpp:2102
virtual size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error)=0
Actually do the reading of memory from a process.
Target & GetTarget()
Get the target object pointer for this module.
Definition: Process.h:1277
An error handling class.
Definition: Status.h:44
lldb::addr_t GetLoadAddress(Target *target) const
Definition: Symbol.cpp:545
lldb::ModuleSP GetExecutableModule()
Gets the module for the main executable.
Definition: Target.cpp:1422
void AddThread(const lldb::ThreadSP &thread_sp)
uint32_t GetSize(bool can_update=true)
Definition: ThreadList.cpp:83
lldb::ThreadSP GetThreadAtIndex(uint32_t idx, bool can_update=true)
Definition: ThreadList.cpp:91
#define LLDB_INVALID_ADDRESS
Definition: lldb-defines.h:82
A class that represents a running process on the host machine.
Definition: SBAttachInfo.h:14
Definition: SBAddress.h:15
std::shared_ptr< lldb_private::Thread > ThreadSP
Definition: lldb-forward.h:438
std::shared_ptr< lldb_private::Process > ProcessSP
Definition: lldb-forward.h:381
std::shared_ptr< lldb_private::Listener > ListenerSP
Definition: lldb-forward.h:360
uint64_t addr_t
Definition: lldb-types.h:79
std::shared_ptr< lldb_private::Target > TargetSP
Definition: lldb-forward.h:436
std::shared_ptr< lldb_private::Module > ModuleSP
Definition: lldb-forward.h:365