Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp
39642 views
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
// In later FreeBSD versions stoppcbs is a pointer to the array.
183
int32_t osreldate =
184
ReadSignedIntegerFromMemory(FindSymbol("osreldate"), 4, -1, error);
185
if (stoppcbs != LLDB_INVALID_ADDRESS && osreldate >= 1400089)
186
stoppcbs = ReadPointerFromMemory(stoppcbs, error);
187
188
// from FreeBSD sys/param.h
189
constexpr size_t fbsd_maxcomlen = 19;
190
191
// iterate through a linked list of all processes
192
// allproc is a pointer to the first list element, p_list field
193
// (found at offset_p_list) specifies the next element
194
for (lldb::addr_t proc =
195
ReadPointerFromMemory(FindSymbol("allproc"), error);
196
proc != 0 && proc != LLDB_INVALID_ADDRESS;
197
proc = ReadPointerFromMemory(proc + offset_p_list, error)) {
198
int32_t pid =
199
ReadSignedIntegerFromMemory(proc + offset_p_pid, 4, -1, error);
200
// process' command-line string
201
char comm[fbsd_maxcomlen + 1];
202
ReadCStringFromMemory(proc + offset_p_comm, comm, sizeof(comm), error);
203
204
// iterate through a linked list of all process' threads
205
// the initial thread is found in process' p_threads, subsequent
206
// elements are linked via td_plist field
207
for (lldb::addr_t td =
208
ReadPointerFromMemory(proc + offset_p_threads, error);
209
td != 0; td = ReadPointerFromMemory(td + offset_td_plist, error)) {
210
int32_t tid =
211
ReadSignedIntegerFromMemory(td + offset_td_tid, 4, -1, error);
212
lldb::addr_t pcb_addr =
213
ReadPointerFromMemory(td + offset_td_pcb, error);
214
// whether process was on CPU (-1 if not, otherwise CPU number)
215
int32_t oncpu =
216
ReadSignedIntegerFromMemory(td + offset_td_oncpu, 4, -2, error);
217
// thread name
218
char thread_name[fbsd_maxcomlen + 1];
219
ReadCStringFromMemory(td + offset_td_name, thread_name,
220
sizeof(thread_name), error);
221
222
// if we failed to read TID, ignore this thread
223
if (tid == -1)
224
continue;
225
226
std::string thread_desc = llvm::formatv("(pid {0}) {1}", pid, comm);
227
if (*thread_name && strcmp(thread_name, comm)) {
228
thread_desc += '/';
229
thread_desc += thread_name;
230
}
231
232
// roughly:
233
// 1. if the thread crashed, its PCB is going to be at "dumppcb"
234
// 2. if the thread was on CPU, its PCB is going to be on the CPU
235
// 3. otherwise, its PCB is in the thread struct
236
if (tid == dumptid) {
237
// NB: dumppcb can be LLDB_INVALID_ADDRESS if reading it failed
238
pcb_addr = dumppcb;
239
thread_desc += " (crashed)";
240
} else if (oncpu != -1) {
241
// if we managed to read stoppcbs and pcb_size, use them to find
242
// the correct PCB
243
if (stoppcbs != LLDB_INVALID_ADDRESS && pcbsize > 0)
244
pcb_addr = stoppcbs + oncpu * pcbsize;
245
else
246
pcb_addr = LLDB_INVALID_ADDRESS;
247
thread_desc += llvm::formatv(" (on CPU {0})", oncpu);
248
}
249
250
ThreadSP thread_sp{
251
new ThreadFreeBSDKernel(*this, tid, pcb_addr, thread_desc)};
252
new_thread_list.AddThread(thread_sp);
253
}
254
}
255
} else {
256
const uint32_t num_threads = old_thread_list.GetSize(false);
257
for (uint32_t i = 0; i < num_threads; ++i)
258
new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false));
259
}
260
return new_thread_list.GetSize(false) > 0;
261
}
262
263
Status ProcessFreeBSDKernel::DoLoadCore() {
264
// The core is already loaded by CreateInstance().
265
return Status();
266
}
267
268
DynamicLoader *ProcessFreeBSDKernel::GetDynamicLoader() {
269
if (m_dyld_up.get() == nullptr)
270
m_dyld_up.reset(DynamicLoader::FindPlugin(
271
this, DynamicLoaderFreeBSDKernel::GetPluginNameStatic()));
272
return m_dyld_up.get();
273
}
274
275
lldb::addr_t ProcessFreeBSDKernel::FindSymbol(const char *name) {
276
ModuleSP mod_sp = GetTarget().GetExecutableModule();
277
const Symbol *sym = mod_sp->FindFirstSymbolWithNameAndType(ConstString(name));
278
return sym ? sym->GetLoadAddress(&GetTarget()) : LLDB_INVALID_ADDRESS;
279
}
280
281
#if LLDB_ENABLE_FBSDVMCORE
282
283
ProcessFreeBSDKernelFVC::ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp,
284
ListenerSP listener_sp,
285
fvc_t *fvc,
286
const FileSpec &core_file)
287
: ProcessFreeBSDKernel(target_sp, listener_sp, crash_file), m_fvc(fvc) {}
288
289
ProcessFreeBSDKernelFVC::~ProcessFreeBSDKernelFVC() {
290
if (m_fvc)
291
fvc_close(m_fvc);
292
}
293
294
size_t ProcessFreeBSDKernelFVC::DoReadMemory(lldb::addr_t addr, void *buf,
295
size_t size, Status &error) {
296
ssize_t rd = 0;
297
rd = fvc_read(m_fvc, addr, buf, size);
298
if (rd < 0 || static_cast<size_t>(rd) != size) {
299
error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());
300
return rd > 0 ? rd : 0;
301
}
302
return rd;
303
}
304
305
const char *ProcessFreeBSDKernelFVC::GetError() { return fvc_geterr(m_fvc); }
306
307
#endif // LLDB_ENABLE_FBSDVMCORE
308
309
#if defined(__FreeBSD__)
310
311
ProcessFreeBSDKernelKVM::ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp,
312
ListenerSP listener_sp,
313
kvm_t *fvc,
314
const FileSpec &core_file)
315
: ProcessFreeBSDKernel(target_sp, listener_sp, core_file), m_kvm(fvc) {}
316
317
ProcessFreeBSDKernelKVM::~ProcessFreeBSDKernelKVM() {
318
if (m_kvm)
319
kvm_close(m_kvm);
320
}
321
322
size_t ProcessFreeBSDKernelKVM::DoReadMemory(lldb::addr_t addr, void *buf,
323
size_t size, Status &error) {
324
ssize_t rd = 0;
325
rd = kvm_read2(m_kvm, addr, buf, size);
326
if (rd < 0 || static_cast<size_t>(rd) != size) {
327
error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());
328
return rd > 0 ? rd : 0;
329
}
330
return rd;
331
}
332
333
const char *ProcessFreeBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); }
334
335
#endif // defined(__FreeBSD__)
336
337