1 | //===-- NativeProcessFreeBSD.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 "NativeProcessFreeBSD.h" |
10 | |
11 | // clang-format off |
12 | #include <sys/types.h> |
13 | #include <sys/ptrace.h> |
14 | #include <sys/sysctl.h> |
15 | #include <sys/user.h> |
16 | #include <sys/wait.h> |
17 | #include <machine/elf.h> |
18 | // clang-format on |
19 | |
20 | #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" |
21 | #include "lldb/Host/HostProcess.h" |
22 | #include "lldb/Host/posix/ProcessLauncherPosixFork.h" |
23 | #include "lldb/Target/Process.h" |
24 | #include "lldb/Utility/State.h" |
25 | #include "llvm/Support/Errno.h" |
26 | |
27 | using namespace lldb; |
28 | using namespace lldb_private; |
29 | using namespace lldb_private::process_freebsd; |
30 | using namespace llvm; |
31 | |
32 | // Simple helper function to ensure flags are enabled on the given file |
33 | // descriptor. |
34 | static Status EnsureFDFlags(int fd, int flags) { |
35 | Status error; |
36 | |
37 | int status = fcntl(fd: fd, F_GETFL); |
38 | if (status == -1) { |
39 | error = Status::FromErrno(); |
40 | return error; |
41 | } |
42 | |
43 | if (fcntl(fd: fd, F_SETFL, status | flags) == -1) { |
44 | error = Status::FromErrno(); |
45 | return error; |
46 | } |
47 | |
48 | return error; |
49 | } |
50 | |
51 | static Status CanTrace() { |
52 | int proc_debug, ret; |
53 | size_t len = sizeof(proc_debug); |
54 | ret = ::sysctlbyname("security.bsd.unprivileged_proc_debug", &proc_debug, |
55 | &len, nullptr, 0); |
56 | if (ret != 0) |
57 | return Status::FromErrorString( |
58 | str: "sysctlbyname() security.bsd.unprivileged_proc_debug failed"); |
59 | |
60 | if (proc_debug < 1) |
61 | return Status::FromErrorString( |
62 | str: "process debug disabled by security.bsd.unprivileged_proc_debug oid"); |
63 | |
64 | return {}; |
65 | } |
66 | |
67 | // Public Static Methods |
68 | |
69 | llvm::Expected<std::unique_ptr<NativeProcessProtocol>> |
70 | NativeProcessFreeBSD::Manager::Launch(ProcessLaunchInfo &launch_info, |
71 | NativeDelegate &native_delegate) { |
72 | Log *log = GetLog(mask: POSIXLog::Process); |
73 | Status status; |
74 | |
75 | ::pid_t pid = ProcessLauncherPosixFork() |
76 | .LaunchProcess(launch_info, error&: status) |
77 | .GetProcessId(); |
78 | LLDB_LOG(log, "pid = {0:x}", pid); |
79 | if (status.Fail()) { |
80 | LLDB_LOG(log, "failed to launch process: {0}", status); |
81 | auto error = CanTrace(); |
82 | if (error.Fail()) |
83 | return error.ToError(); |
84 | return status.ToError(); |
85 | } |
86 | |
87 | // Wait for the child process to trap on its call to execve. |
88 | int wstatus; |
89 | ::pid_t wpid = llvm::sys::RetryAfterSignal(Fail: -1, F&: ::waitpid, As: pid, As: &wstatus, As: 0); |
90 | assert(wpid == pid); |
91 | UNUSED_IF_ASSERT_DISABLED(wpid); |
92 | if (!WIFSTOPPED(wstatus)) { |
93 | LLDB_LOG(log, "Could not sync with inferior process: wstatus={1}", |
94 | WaitStatus::Decode(wstatus)); |
95 | return llvm::make_error<StringError>(Args: "Could not sync with inferior process", |
96 | Args: llvm::inconvertibleErrorCode()); |
97 | } |
98 | LLDB_LOG(log, "inferior started, now in stopped state"); |
99 | |
100 | ProcessInstanceInfo Info; |
101 | if (!Host::GetProcessInfo(pid, proc_info&: Info)) { |
102 | return llvm::make_error<StringError>(Args: "Cannot get process architecture", |
103 | Args: llvm::inconvertibleErrorCode()); |
104 | } |
105 | |
106 | // Set the architecture to the exe architecture. |
107 | LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid, |
108 | Info.GetArchitecture().GetArchitectureName()); |
109 | |
110 | std::unique_ptr<NativeProcessFreeBSD> process_up(new NativeProcessFreeBSD( |
111 | pid, launch_info.GetPTY().ReleasePrimaryFileDescriptor(), native_delegate, |
112 | Info.GetArchitecture(), m_mainloop)); |
113 | |
114 | status = process_up->SetupTrace(); |
115 | if (status.Fail()) |
116 | return status.ToError(); |
117 | |
118 | for (const auto &thread : process_up->m_threads) |
119 | static_cast<NativeThreadFreeBSD &>(*thread).SetStoppedBySignal(SIGSTOP); |
120 | process_up->SetState(state: StateType::eStateStopped, notify_delegates: false); |
121 | |
122 | return std::move(process_up); |
123 | } |
124 | |
125 | llvm::Expected<std::unique_ptr<NativeProcessProtocol>> |
126 | NativeProcessFreeBSD::Manager::Attach( |
127 | lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate) { |
128 | Log *log = GetLog(mask: POSIXLog::Process); |
129 | LLDB_LOG(log, "pid = {0:x}", pid); |
130 | |
131 | // Retrieve the architecture for the running process. |
132 | ProcessInstanceInfo Info; |
133 | if (!Host::GetProcessInfo(pid, proc_info&: Info)) { |
134 | return llvm::make_error<StringError>(Args: "Cannot get process architecture", |
135 | Args: llvm::inconvertibleErrorCode()); |
136 | } |
137 | |
138 | std::unique_ptr<NativeProcessFreeBSD> process_up(new NativeProcessFreeBSD( |
139 | pid, -1, native_delegate, Info.GetArchitecture(), m_mainloop)); |
140 | |
141 | Status status = process_up->Attach(); |
142 | if (!status.Success()) |
143 | return status.ToError(); |
144 | |
145 | return std::move(process_up); |
146 | } |
147 | |
148 | NativeProcessFreeBSD::Extension |
149 | NativeProcessFreeBSD::Manager::GetSupportedExtensions() const { |
150 | return |
151 | #if defined(PT_COREDUMP) |
152 | Extension::savecore | |
153 | #endif |
154 | Extension::multiprocess | Extension::fork | Extension::vfork | |
155 | Extension::pass_signals | Extension::auxv | Extension::libraries_svr4 | |
156 | Extension::siginfo_read; |
157 | } |
158 | |
159 | // Public Instance Methods |
160 | |
161 | NativeProcessFreeBSD::NativeProcessFreeBSD(::pid_t pid, int terminal_fd, |
162 | NativeDelegate &delegate, |
163 | const ArchSpec &arch, |
164 | MainLoop &mainloop) |
165 | : NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch), |
166 | m_main_loop(mainloop) { |
167 | if (m_terminal_fd != -1) { |
168 | Status status = EnsureFDFlags(fd: m_terminal_fd, O_NONBLOCK); |
169 | assert(status.Success()); |
170 | } |
171 | |
172 | Status status; |
173 | m_sigchld_handle = mainloop.RegisterSignal( |
174 | SIGCHLD, callback: [this](MainLoopBase &) { SigchldHandler(); }, error&: status); |
175 | assert(m_sigchld_handle && status.Success()); |
176 | } |
177 | |
178 | // Handles all waitpid events from the inferior process. |
179 | void NativeProcessFreeBSD::MonitorCallback(lldb::pid_t pid, int signal) { |
180 | switch (signal) { |
181 | case SIGTRAP: |
182 | return MonitorSIGTRAP(pid); |
183 | case SIGSTOP: |
184 | return MonitorSIGSTOP(pid); |
185 | default: |
186 | return MonitorSignal(pid, signal); |
187 | } |
188 | } |
189 | |
190 | void NativeProcessFreeBSD::MonitorExited(lldb::pid_t pid, WaitStatus status) { |
191 | Log *log = GetLog(mask: POSIXLog::Process); |
192 | |
193 | LLDB_LOG(log, "got exit signal({0}) , pid = {1}", status, pid); |
194 | |
195 | /* Stop Tracking All Threads attached to Process */ |
196 | m_threads.clear(); |
197 | |
198 | SetExitStatus(status, bNotifyStateChange: true); |
199 | |
200 | // Notify delegate that our process has exited. |
201 | SetState(state: StateType::eStateExited, notify_delegates: true); |
202 | } |
203 | |
204 | void NativeProcessFreeBSD::MonitorSIGSTOP(lldb::pid_t pid) { |
205 | /* Stop all Threads attached to Process */ |
206 | for (const auto &thread : m_threads) { |
207 | static_cast<NativeThreadFreeBSD &>(*thread).SetStoppedBySignal(SIGSTOP, |
208 | info: nullptr); |
209 | } |
210 | SetState(state: StateType::eStateStopped, notify_delegates: true); |
211 | } |
212 | |
213 | void NativeProcessFreeBSD::MonitorSIGTRAP(lldb::pid_t pid) { |
214 | Log *log = GetLog(mask: POSIXLog::Process); |
215 | struct ptrace_lwpinfo info; |
216 | |
217 | const auto siginfo_err = PtraceWrapper(PT_LWPINFO, pid, &info, sizeof(info)); |
218 | if (siginfo_err.Fail()) { |
219 | LLDB_LOG(log, "PT_LWPINFO failed {0}", siginfo_err); |
220 | return; |
221 | } |
222 | assert(info.pl_event == PL_EVENT_SIGNAL); |
223 | |
224 | LLDB_LOG(log, "got SIGTRAP, pid = {0}, lwpid = {1}, flags = {2:x}", pid, |
225 | info.pl_lwpid, info.pl_flags); |
226 | NativeThreadFreeBSD *thread = nullptr; |
227 | |
228 | if (info.pl_flags & (PL_FLAG_BORN | PL_FLAG_EXITED)) { |
229 | if (info.pl_flags & PL_FLAG_BORN) { |
230 | LLDB_LOG(log, "monitoring new thread, tid = {0}", info.pl_lwpid); |
231 | NativeThreadFreeBSD &t = AddThread(thread_id: info.pl_lwpid); |
232 | |
233 | // Technically, the FreeBSD kernel copies the debug registers to new |
234 | // threads. However, there is a non-negligible delay between acquiring |
235 | // the DR values and reporting the new thread during which the user may |
236 | // establish a new watchpoint. In order to ensure that watchpoints |
237 | // established during this period are propagated to new threads, |
238 | // explicitly copy the DR value at the time the new thread is reported. |
239 | // |
240 | // See also: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=250954 |
241 | |
242 | llvm::Error error = t.CopyWatchpointsFrom( |
243 | source&: static_cast<NativeThreadFreeBSD &>(*GetCurrentThread())); |
244 | if (error) { |
245 | LLDB_LOG_ERROR(log, std::move(error), |
246 | "failed to copy watchpoints to new thread {1}: {0}", |
247 | info.pl_lwpid); |
248 | SetState(state: StateType::eStateInvalid); |
249 | return; |
250 | } |
251 | } else /*if (info.pl_flags & PL_FLAG_EXITED)*/ { |
252 | LLDB_LOG(log, "thread exited, tid = {0}", info.pl_lwpid); |
253 | RemoveThread(thread_id: info.pl_lwpid); |
254 | } |
255 | |
256 | Status error = |
257 | PtraceWrapper(PT_CONTINUE, pid, addr: reinterpret_cast<void *>(1), data: 0); |
258 | if (error.Fail()) |
259 | SetState(state: StateType::eStateInvalid); |
260 | return; |
261 | } |
262 | |
263 | if (info.pl_flags & PL_FLAG_EXEC) { |
264 | Status error = ReinitializeThreads(); |
265 | if (error.Fail()) { |
266 | SetState(state: StateType::eStateInvalid); |
267 | return; |
268 | } |
269 | |
270 | // Let our delegate know we have just exec'd. |
271 | NotifyDidExec(); |
272 | |
273 | for (const auto &thread : m_threads) |
274 | static_cast<NativeThreadFreeBSD &>(*thread).SetStoppedByExec(); |
275 | SetCurrentThreadID(m_threads.front()->GetID()); |
276 | SetState(state: StateType::eStateStopped, notify_delegates: true); |
277 | return; |
278 | } |
279 | |
280 | if (info.pl_lwpid > 0) { |
281 | for (const auto &t : m_threads) { |
282 | if (t->GetID() == static_cast<lldb::tid_t>(info.pl_lwpid)) |
283 | thread = static_cast<NativeThreadFreeBSD *>(t.get()); |
284 | static_cast<NativeThreadFreeBSD *>(t.get())->SetStoppedWithNoReason(); |
285 | } |
286 | if (!thread) |
287 | LLDB_LOG(log, "thread not found in m_threads, pid = {0}, LWP = {1}", pid, |
288 | info.pl_lwpid); |
289 | } |
290 | |
291 | if (info.pl_flags & PL_FLAG_FORKED) { |
292 | assert(thread); |
293 | MonitorClone(child_pid: info.pl_child_pid, is_vfork: info.pl_flags & PL_FLAG_VFORKED, parent_thread&: *thread); |
294 | return; |
295 | } |
296 | |
297 | if (info.pl_flags & PL_FLAG_VFORK_DONE) { |
298 | assert(thread); |
299 | if ((m_enabled_extensions & Extension::vfork) == Extension::vfork) { |
300 | thread->SetStoppedByVForkDone(); |
301 | SetState(state: StateType::eStateStopped, notify_delegates: true); |
302 | } else { |
303 | Status error = |
304 | PtraceWrapper(PT_CONTINUE, pid, addr: reinterpret_cast<void *>(1), data: 0); |
305 | if (error.Fail()) |
306 | SetState(state: StateType::eStateInvalid); |
307 | } |
308 | return; |
309 | } |
310 | |
311 | if (info.pl_flags & PL_FLAG_SI) { |
312 | assert(info.pl_siginfo.si_signo == SIGTRAP); |
313 | LLDB_LOG(log, "SIGTRAP siginfo: si_code = {0}, pid = {1}", |
314 | info.pl_siginfo.si_code, info.pl_siginfo.si_pid); |
315 | |
316 | switch (info.pl_siginfo.si_code) { |
317 | case TRAP_BRKPT: |
318 | LLDB_LOG(log, "SIGTRAP/TRAP_BRKPT: si_addr: {0}", |
319 | info.pl_siginfo.si_addr); |
320 | |
321 | if (thread) { |
322 | auto ®ctx = static_cast<NativeRegisterContextFreeBSD &>( |
323 | thread->GetRegisterContext()); |
324 | auto thread_info = |
325 | m_threads_stepping_with_breakpoint.find(x: thread->GetID()); |
326 | if (thread_info != m_threads_stepping_with_breakpoint.end() && |
327 | thread_info->second == regctx.GetPC()) { |
328 | thread->SetStoppedByTrace(); |
329 | Status brkpt_error = RemoveBreakpoint(addr: thread_info->second); |
330 | if (brkpt_error.Fail()) |
331 | LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}", |
332 | thread_info->first, brkpt_error); |
333 | m_threads_stepping_with_breakpoint.erase(position: thread_info); |
334 | } else |
335 | thread->SetStoppedByBreakpoint(); |
336 | FixupBreakpointPCAsNeeded(thread&: *thread); |
337 | SetCurrentThreadID(thread->GetID()); |
338 | } |
339 | SetState(state: StateType::eStateStopped, notify_delegates: true); |
340 | return; |
341 | case TRAP_TRACE: |
342 | LLDB_LOG(log, "SIGTRAP/TRAP_TRACE: si_addr: {0}", |
343 | info.pl_siginfo.si_addr); |
344 | |
345 | if (thread) { |
346 | auto ®ctx = static_cast<NativeRegisterContextFreeBSD &>( |
347 | thread->GetRegisterContext()); |
348 | uint32_t wp_index = LLDB_INVALID_INDEX32; |
349 | Status error = regctx.GetWatchpointHitIndex( |
350 | wp_index, trap_addr: reinterpret_cast<uintptr_t>(info.pl_siginfo.si_addr)); |
351 | if (error.Fail()) |
352 | LLDB_LOG(log, |
353 | "received error while checking for watchpoint hits, pid = " |
354 | "{0}, LWP = {1}, error = {2}", |
355 | pid, info.pl_lwpid, error); |
356 | if (wp_index != LLDB_INVALID_INDEX32) { |
357 | regctx.ClearWatchpointHit(hw_index: wp_index); |
358 | thread->SetStoppedByWatchpoint(wp_index); |
359 | SetCurrentThreadID(thread->GetID()); |
360 | SetState(state: StateType::eStateStopped, notify_delegates: true); |
361 | break; |
362 | } |
363 | |
364 | thread->SetStoppedByTrace(); |
365 | SetCurrentThreadID(thread->GetID()); |
366 | } |
367 | |
368 | SetState(state: StateType::eStateStopped, notify_delegates: true); |
369 | return; |
370 | } |
371 | } |
372 | |
373 | // Either user-generated SIGTRAP or an unknown event that would |
374 | // otherwise leave the debugger hanging. |
375 | LLDB_LOG(log, "unknown SIGTRAP, passing to generic handler"); |
376 | MonitorSignal(pid, SIGTRAP); |
377 | } |
378 | |
379 | void NativeProcessFreeBSD::MonitorSignal(lldb::pid_t pid, int signal) { |
380 | Log *log = GetLog(mask: POSIXLog::Process); |
381 | struct ptrace_lwpinfo info; |
382 | |
383 | const auto siginfo_err = PtraceWrapper(PT_LWPINFO, pid, &info, sizeof(info)); |
384 | if (siginfo_err.Fail()) { |
385 | LLDB_LOG(log, "PT_LWPINFO failed {0}", siginfo_err); |
386 | return; |
387 | } |
388 | assert(info.pl_event == PL_EVENT_SIGNAL); |
389 | // TODO: do we need to handle !PL_FLAG_SI? |
390 | assert(info.pl_flags & PL_FLAG_SI); |
391 | assert(info.pl_siginfo.si_signo == signal); |
392 | |
393 | for (const auto &abs_thread : m_threads) { |
394 | NativeThreadFreeBSD &thread = |
395 | static_cast<NativeThreadFreeBSD &>(*abs_thread); |
396 | assert(info.pl_lwpid >= 0); |
397 | if (info.pl_lwpid == 0 || |
398 | static_cast<lldb::tid_t>(info.pl_lwpid) == thread.GetID()) { |
399 | thread.SetStoppedBySignal(signo: info.pl_siginfo.si_signo, info: &info.pl_siginfo); |
400 | SetCurrentThreadID(thread.GetID()); |
401 | } else |
402 | thread.SetStoppedWithNoReason(); |
403 | } |
404 | SetState(state: StateType::eStateStopped, notify_delegates: true); |
405 | } |
406 | |
407 | Status NativeProcessFreeBSD::PtraceWrapper(int req, lldb::pid_t pid, void *addr, |
408 | int data, int *result) { |
409 | Log *log = GetLog(mask: POSIXLog::Ptrace); |
410 | Status error; |
411 | int ret; |
412 | |
413 | errno = 0; |
414 | ret = |
415 | ptrace(req, static_cast<::pid_t>(pid), static_cast<caddr_t>(addr), data); |
416 | |
417 | if (ret == -1) { |
418 | error = CanTrace(); |
419 | if (error.Success()) |
420 | error = Status::FromErrno(); |
421 | } |
422 | |
423 | if (result) |
424 | *result = ret; |
425 | |
426 | LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3})={4:x}", req, pid, addr, data, ret); |
427 | |
428 | if (error.Fail()) |
429 | LLDB_LOG(log, "ptrace() failed: {0}", error); |
430 | |
431 | return error; |
432 | } |
433 | |
434 | llvm::Expected<llvm::ArrayRef<uint8_t>> |
435 | NativeProcessFreeBSD::GetSoftwareBreakpointTrapOpcode(size_t size_hint) { |
436 | static const uint8_t g_arm_opcode[] = {0xfe, 0xde, 0xff, 0xe7}; |
437 | static const uint8_t g_thumb_opcode[] = {0x01, 0xde}; |
438 | |
439 | switch (GetArchitecture().GetMachine()) { |
440 | case llvm::Triple::arm: |
441 | switch (size_hint) { |
442 | case 2: |
443 | return llvm::ArrayRef(g_thumb_opcode); |
444 | case 4: |
445 | return llvm::ArrayRef(g_arm_opcode); |
446 | default: |
447 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
448 | S: "Unrecognised trap opcode size hint!"); |
449 | } |
450 | default: |
451 | return NativeProcessProtocol::GetSoftwareBreakpointTrapOpcode(size_hint); |
452 | } |
453 | } |
454 | |
455 | Status NativeProcessFreeBSD::Resume(const ResumeActionList &resume_actions) { |
456 | Log *log = GetLog(mask: POSIXLog::Process); |
457 | LLDB_LOG(log, "pid {0}", GetID()); |
458 | |
459 | Status ret; |
460 | |
461 | int signal = 0; |
462 | for (const auto &abs_thread : m_threads) { |
463 | assert(abs_thread && "thread list should not contain NULL threads"); |
464 | NativeThreadFreeBSD &thread = |
465 | static_cast<NativeThreadFreeBSD &>(*abs_thread); |
466 | |
467 | const ResumeAction *action = |
468 | resume_actions.GetActionForThread(tid: thread.GetID(), default_ok: true); |
469 | // we need to explicit issue suspend requests, so it is simpler to map it |
470 | // into proper action |
471 | ResumeAction suspend_action{.tid: thread.GetID(), .state: eStateSuspended, |
472 | LLDB_INVALID_SIGNAL_NUMBER}; |
473 | |
474 | if (action == nullptr) { |
475 | LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(), |
476 | thread.GetID()); |
477 | action = &suspend_action; |
478 | } |
479 | |
480 | LLDB_LOG( |
481 | log, |
482 | "processing resume action state {0} signal {1} for pid {2} tid {3}", |
483 | action->state, action->signal, GetID(), thread.GetID()); |
484 | |
485 | switch (action->state) { |
486 | case eStateRunning: |
487 | ret = thread.Resume(); |
488 | break; |
489 | case eStateStepping: |
490 | ret = thread.SingleStep(); |
491 | break; |
492 | case eStateSuspended: |
493 | case eStateStopped: |
494 | if (action->signal != LLDB_INVALID_SIGNAL_NUMBER) |
495 | return Status::FromErrorString( |
496 | str: "Passing signal to suspended thread unsupported"); |
497 | |
498 | ret = thread.Suspend(); |
499 | break; |
500 | |
501 | default: |
502 | return Status::FromErrorStringWithFormat( |
503 | format: "NativeProcessFreeBSD::%s (): unexpected state %s specified " |
504 | "for pid %"PRIu64 ", tid %"PRIu64, |
505 | __FUNCTION__, StateAsCString(state: action->state), GetID(), thread.GetID()); |
506 | } |
507 | |
508 | if (!ret.Success()) |
509 | return ret; |
510 | if (action->signal != LLDB_INVALID_SIGNAL_NUMBER) |
511 | signal = action->signal; |
512 | } |
513 | |
514 | ret = |
515 | PtraceWrapper(PT_CONTINUE, pid: GetID(), addr: reinterpret_cast<void *>(1), data: signal); |
516 | if (ret.Success()) |
517 | SetState(state: eStateRunning, notify_delegates: true); |
518 | return ret; |
519 | } |
520 | |
521 | Status NativeProcessFreeBSD::Halt() { |
522 | Status error; |
523 | |
524 | // Do not try to stop a process that's already stopped, this may cause |
525 | // the SIGSTOP to get queued and stop the process again once resumed. |
526 | if (StateIsStoppedState(state: m_state, must_exist: false)) |
527 | return error; |
528 | if (kill(pid: GetID(), SIGSTOP) != 0) |
529 | error = Status::FromErrno(); |
530 | return error; |
531 | } |
532 | |
533 | Status NativeProcessFreeBSD::Detach() { |
534 | Status error; |
535 | |
536 | // Stop monitoring the inferior. |
537 | m_sigchld_handle.reset(); |
538 | |
539 | // Tell ptrace to detach from the process. |
540 | if (GetID() == LLDB_INVALID_PROCESS_ID) |
541 | return error; |
542 | |
543 | return PtraceWrapper(PT_DETACH, pid: GetID()); |
544 | } |
545 | |
546 | Status NativeProcessFreeBSD::Signal(int signo) { |
547 | Status error; |
548 | |
549 | if (kill(pid: GetID(), sig: signo)) |
550 | error = Status::FromErrno(); |
551 | |
552 | return error; |
553 | } |
554 | |
555 | Status NativeProcessFreeBSD::Interrupt() { return Halt(); } |
556 | |
557 | Status NativeProcessFreeBSD::Kill() { |
558 | Log *log = GetLog(mask: POSIXLog::Process); |
559 | LLDB_LOG(log, "pid {0}", GetID()); |
560 | |
561 | Status error; |
562 | |
563 | switch (m_state) { |
564 | case StateType::eStateInvalid: |
565 | case StateType::eStateExited: |
566 | case StateType::eStateCrashed: |
567 | case StateType::eStateDetached: |
568 | case StateType::eStateUnloaded: |
569 | // Nothing to do - the process is already dead. |
570 | LLDB_LOG(log, "ignored for PID {0} due to current state: {1}", GetID(), |
571 | StateAsCString(m_state)); |
572 | return error; |
573 | |
574 | case StateType::eStateConnected: |
575 | case StateType::eStateAttaching: |
576 | case StateType::eStateLaunching: |
577 | case StateType::eStateStopped: |
578 | case StateType::eStateRunning: |
579 | case StateType::eStateStepping: |
580 | case StateType::eStateSuspended: |
581 | // We can try to kill a process in these states. |
582 | break; |
583 | } |
584 | |
585 | return PtraceWrapper(PT_KILL, pid: m_pid); |
586 | } |
587 | |
588 | Status NativeProcessFreeBSD::GetMemoryRegionInfo(lldb::addr_t load_addr, |
589 | MemoryRegionInfo &range_info) { |
590 | |
591 | if (m_supports_mem_region == LazyBool::eLazyBoolNo) { |
592 | // We're done. |
593 | return Status::FromErrorString(str: "unsupported"); |
594 | } |
595 | |
596 | Status error = PopulateMemoryRegionCache(); |
597 | if (error.Fail()) { |
598 | return error; |
599 | } |
600 | |
601 | lldb::addr_t prev_base_address = 0; |
602 | // FIXME start by finding the last region that is <= target address using |
603 | // binary search. Data is sorted. |
604 | // There can be a ton of regions on pthreads apps with lots of threads. |
605 | for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end(); |
606 | ++it) { |
607 | MemoryRegionInfo &proc_entry_info = it->first; |
608 | // Sanity check assumption that memory map entries are ascending. |
609 | assert((proc_entry_info.GetRange().GetRangeBase() >= prev_base_address) && |
610 | "descending memory map entries detected, unexpected"); |
611 | prev_base_address = proc_entry_info.GetRange().GetRangeBase(); |
612 | UNUSED_IF_ASSERT_DISABLED(prev_base_address); |
613 | // If the target address comes before this entry, indicate distance to next |
614 | // region. |
615 | if (load_addr < proc_entry_info.GetRange().GetRangeBase()) { |
616 | range_info.GetRange().SetRangeBase(load_addr); |
617 | range_info.GetRange().SetByteSize( |
618 | proc_entry_info.GetRange().GetRangeBase() - load_addr); |
619 | range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); |
620 | range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); |
621 | range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); |
622 | range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); |
623 | return error; |
624 | } else if (proc_entry_info.GetRange().Contains(r: load_addr)) { |
625 | // The target address is within the memory region we're processing here. |
626 | range_info = proc_entry_info; |
627 | return error; |
628 | } |
629 | // The target memory address comes somewhere after the region we just |
630 | // parsed. |
631 | } |
632 | // If we made it here, we didn't find an entry that contained the given |
633 | // address. Return the load_addr as start and the amount of bytes betwwen |
634 | // load address and the end of the memory as size. |
635 | range_info.GetRange().SetRangeBase(load_addr); |
636 | range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); |
637 | range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); |
638 | range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); |
639 | range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); |
640 | range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); |
641 | return error; |
642 | } |
643 | |
644 | Status NativeProcessFreeBSD::PopulateMemoryRegionCache() { |
645 | Log *log = GetLog(mask: POSIXLog::Process); |
646 | // If our cache is empty, pull the latest. There should always be at least |
647 | // one memory region if memory region handling is supported. |
648 | if (!m_mem_region_cache.empty()) { |
649 | LLDB_LOG(log, "reusing {0} cached memory region entries", |
650 | m_mem_region_cache.size()); |
651 | return Status(); |
652 | } |
653 | |
654 | int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, static_cast<int>(m_pid)}; |
655 | int ret; |
656 | size_t len; |
657 | |
658 | ret = ::sysctl(mib, 4, nullptr, &len, nullptr, 0); |
659 | if (ret != 0) { |
660 | m_supports_mem_region = LazyBool::eLazyBoolNo; |
661 | return Status::FromErrorString(str: "sysctl() for KERN_PROC_VMMAP failed"); |
662 | } |
663 | |
664 | std::unique_ptr<WritableMemoryBuffer> buf = |
665 | llvm::WritableMemoryBuffer::getNewMemBuffer(Size: len); |
666 | ret = ::sysctl(mib, 4, buf->getBufferStart(), &len, nullptr, 0); |
667 | if (ret != 0) { |
668 | m_supports_mem_region = LazyBool::eLazyBoolNo; |
669 | return Status::FromErrorString(str: "sysctl() for KERN_PROC_VMMAP failed"); |
670 | } |
671 | |
672 | char *bp = buf->getBufferStart(); |
673 | char *end = bp + len; |
674 | while (bp < end) { |
675 | auto *kv = reinterpret_cast<struct kinfo_vmentry *>(bp); |
676 | if (kv->kve_structsize == 0) |
677 | break; |
678 | bp += kv->kve_structsize; |
679 | |
680 | MemoryRegionInfo info; |
681 | info.Clear(); |
682 | info.GetRange().SetRangeBase(kv->kve_start); |
683 | info.GetRange().SetRangeEnd(kv->kve_end); |
684 | info.SetMapped(MemoryRegionInfo::OptionalBool::eYes); |
685 | |
686 | if (kv->kve_protection & VM_PROT_READ) |
687 | info.SetReadable(MemoryRegionInfo::OptionalBool::eYes); |
688 | else |
689 | info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); |
690 | |
691 | if (kv->kve_protection & VM_PROT_WRITE) |
692 | info.SetWritable(MemoryRegionInfo::OptionalBool::eYes); |
693 | else |
694 | info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); |
695 | |
696 | if (kv->kve_protection & VM_PROT_EXECUTE) |
697 | info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes); |
698 | else |
699 | info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); |
700 | |
701 | if (kv->kve_path[0]) |
702 | info.SetName(kv->kve_path); |
703 | |
704 | m_mem_region_cache.emplace_back(args&: info, |
705 | args: FileSpec(info.GetName().GetCString())); |
706 | } |
707 | |
708 | if (m_mem_region_cache.empty()) { |
709 | // No entries after attempting to read them. This shouldn't happen. Assume |
710 | // we don't support map entries. |
711 | LLDB_LOG(log, "failed to find any vmmap entries, assuming no support " |
712 | "for memory region metadata retrieval"); |
713 | m_supports_mem_region = LazyBool::eLazyBoolNo; |
714 | return Status::FromErrorString(str: "not supported"); |
715 | } |
716 | LLDB_LOG(log, "read {0} memory region entries from process {1}", |
717 | m_mem_region_cache.size(), GetID()); |
718 | // We support memory retrieval, remember that. |
719 | m_supports_mem_region = LazyBool::eLazyBoolYes; |
720 | |
721 | return Status(); |
722 | } |
723 | |
724 | size_t NativeProcessFreeBSD::UpdateThreads() { return m_threads.size(); } |
725 | |
726 | Status NativeProcessFreeBSD::SetBreakpoint(lldb::addr_t addr, uint32_t size, |
727 | bool hardware) { |
728 | if (hardware) |
729 | return SetHardwareBreakpoint(addr, size); |
730 | return SetSoftwareBreakpoint(addr, size_hint: size); |
731 | } |
732 | |
733 | Status NativeProcessFreeBSD::GetLoadedModuleFileSpec(const char *module_path, |
734 | FileSpec &file_spec) { |
735 | Status error = PopulateMemoryRegionCache(); |
736 | if (error.Fail()) { |
737 | auto status = CanTrace(); |
738 | if (status.Fail()) |
739 | return status; |
740 | return error; |
741 | } |
742 | |
743 | FileSpec module_file_spec(module_path); |
744 | FileSystem::Instance().Resolve(file_spec&: module_file_spec); |
745 | |
746 | file_spec.Clear(); |
747 | for (const auto &it : m_mem_region_cache) { |
748 | if (it.second.GetFilename() == module_file_spec.GetFilename()) { |
749 | file_spec = it.second; |
750 | return Status(); |
751 | } |
752 | } |
753 | return Status::FromErrorStringWithFormat( |
754 | format: "Module file (%s) not found in process' memory map!", |
755 | module_file_spec.GetFilename().AsCString()); |
756 | } |
757 | |
758 | Status |
759 | NativeProcessFreeBSD::GetFileLoadAddress(const llvm::StringRef &file_name, |
760 | lldb::addr_t &load_addr) { |
761 | load_addr = LLDB_INVALID_ADDRESS; |
762 | Status error = PopulateMemoryRegionCache(); |
763 | if (error.Fail()) { |
764 | auto status = CanTrace(); |
765 | if (status.Fail()) |
766 | return status; |
767 | return error; |
768 | } |
769 | |
770 | FileSpec file(file_name); |
771 | for (const auto &it : m_mem_region_cache) { |
772 | if (it.second == file) { |
773 | load_addr = it.first.GetRange().GetRangeBase(); |
774 | return Status(); |
775 | } |
776 | } |
777 | return Status::FromErrorStringWithFormat(format: "No load address found for file %s.", |
778 | file_name.str().c_str()); |
779 | } |
780 | |
781 | void NativeProcessFreeBSD::SigchldHandler() { |
782 | Log *log = GetLog(mask: POSIXLog::Process); |
783 | int status; |
784 | ::pid_t wait_pid = |
785 | llvm::sys::RetryAfterSignal(Fail: -1, F&: waitpid, As: GetID(), As: &status, WNOHANG); |
786 | |
787 | if (wait_pid == 0) |
788 | return; |
789 | |
790 | if (wait_pid == -1) { |
791 | Status error(errno, eErrorTypePOSIX); |
792 | LLDB_LOG(log, "waitpid ({0}, &status, _) failed: {1}", GetID(), error); |
793 | return; |
794 | } |
795 | |
796 | WaitStatus wait_status = WaitStatus::Decode(wstatus: status); |
797 | bool exited = wait_status.type == WaitStatus::Exit || |
798 | (wait_status.type == WaitStatus::Signal && |
799 | wait_pid == static_cast<::pid_t>(GetID())); |
800 | |
801 | LLDB_LOG(log, |
802 | "waitpid ({0}, &status, _) => pid = {1}, status = {2}, exited = {3}", |
803 | GetID(), wait_pid, status, exited); |
804 | |
805 | if (exited) |
806 | MonitorExited(pid: wait_pid, status: wait_status); |
807 | else { |
808 | assert(wait_status.type == WaitStatus::Stop); |
809 | MonitorCallback(pid: wait_pid, signal: wait_status.status); |
810 | } |
811 | } |
812 | |
813 | bool NativeProcessFreeBSD::HasThreadNoLock(lldb::tid_t thread_id) { |
814 | for (const auto &thread : m_threads) { |
815 | assert(thread && "thread list should not contain NULL threads"); |
816 | if (thread->GetID() == thread_id) { |
817 | // We have this thread. |
818 | return true; |
819 | } |
820 | } |
821 | |
822 | // We don't have this thread. |
823 | return false; |
824 | } |
825 | |
826 | NativeThreadFreeBSD &NativeProcessFreeBSD::AddThread(lldb::tid_t thread_id) { |
827 | Log *log = GetLog(mask: POSIXLog::Thread); |
828 | LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id); |
829 | |
830 | assert(thread_id > 0); |
831 | assert(!HasThreadNoLock(thread_id) && |
832 | "attempted to add a thread by id that already exists"); |
833 | |
834 | // If this is the first thread, save it as the current thread |
835 | if (m_threads.empty()) |
836 | SetCurrentThreadID(thread_id); |
837 | |
838 | m_threads.push_back(x: std::make_unique<NativeThreadFreeBSD>(args&: *this, args&: thread_id)); |
839 | return static_cast<NativeThreadFreeBSD &>(*m_threads.back()); |
840 | } |
841 | |
842 | void NativeProcessFreeBSD::RemoveThread(lldb::tid_t thread_id) { |
843 | Log *log = GetLog(mask: POSIXLog::Thread); |
844 | LLDB_LOG(log, "pid {0} removing thread with tid {1}", GetID(), thread_id); |
845 | |
846 | assert(thread_id > 0); |
847 | assert(HasThreadNoLock(thread_id) && |
848 | "attempted to remove a thread that does not exist"); |
849 | |
850 | for (auto it = m_threads.begin(); it != m_threads.end(); ++it) { |
851 | if ((*it)->GetID() == thread_id) { |
852 | m_threads.erase(position: it); |
853 | break; |
854 | } |
855 | } |
856 | |
857 | if (GetCurrentThreadID() == thread_id) |
858 | SetCurrentThreadID(m_threads.front()->GetID()); |
859 | } |
860 | |
861 | Status NativeProcessFreeBSD::Attach() { |
862 | // Attach to the requested process. |
863 | // An attach will cause the thread to stop with a SIGSTOP. |
864 | Status status = PtraceWrapper(PT_ATTACH, pid: m_pid); |
865 | if (status.Fail()) |
866 | return status; |
867 | |
868 | int wstatus; |
869 | // Need to use WALLSIG otherwise we receive an error with errno=ECHLD At this |
870 | // point we should have a thread stopped if waitpid succeeds. |
871 | if ((wstatus = llvm::sys::RetryAfterSignal(Fail: -1, F&: waitpid, As: m_pid, As: nullptr, As: 0)) < |
872 | 0) |
873 | return Status(errno, eErrorTypePOSIX); |
874 | |
875 | // Initialize threads and tracing status |
876 | // NB: this needs to be called before we set thread state |
877 | status = SetupTrace(); |
878 | if (status.Fail()) |
879 | return status; |
880 | |
881 | for (const auto &thread : m_threads) |
882 | static_cast<NativeThreadFreeBSD &>(*thread).SetStoppedBySignal(SIGSTOP); |
883 | |
884 | // Let our process instance know the thread has stopped. |
885 | SetCurrentThreadID(m_threads.front()->GetID()); |
886 | SetState(state: StateType::eStateStopped, notify_delegates: false); |
887 | return Status(); |
888 | } |
889 | |
890 | Status NativeProcessFreeBSD::ReadMemory(lldb::addr_t addr, void *buf, |
891 | size_t size, size_t &bytes_read) { |
892 | unsigned char *dst = static_cast<unsigned char *>(buf); |
893 | struct ptrace_io_desc io; |
894 | |
895 | Log *log = GetLog(mask: POSIXLog::Memory); |
896 | LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); |
897 | |
898 | bytes_read = 0; |
899 | io.piod_op = PIOD_READ_D; |
900 | io.piod_len = size; |
901 | |
902 | do { |
903 | io.piod_offs = (void *)(addr + bytes_read); |
904 | io.piod_addr = dst + bytes_read; |
905 | |
906 | Status error = NativeProcessFreeBSD::PtraceWrapper(req: PT_IO, pid: GetID(), addr: &io); |
907 | if (error.Fail() || io.piod_len == 0) |
908 | return error; |
909 | |
910 | bytes_read += io.piod_len; |
911 | io.piod_len = size - bytes_read; |
912 | } while (bytes_read < size); |
913 | |
914 | return Status(); |
915 | } |
916 | |
917 | Status NativeProcessFreeBSD::WriteMemory(lldb::addr_t addr, const void *buf, |
918 | size_t size, size_t &bytes_written) { |
919 | const unsigned char *src = static_cast<const unsigned char *>(buf); |
920 | Status error; |
921 | struct ptrace_io_desc io; |
922 | |
923 | Log *log = GetLog(mask: POSIXLog::Memory); |
924 | LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); |
925 | |
926 | bytes_written = 0; |
927 | io.piod_op = PIOD_WRITE_D; |
928 | io.piod_len = size; |
929 | |
930 | do { |
931 | io.piod_addr = |
932 | const_cast<void *>(static_cast<const void *>(src + bytes_written)); |
933 | io.piod_offs = (void *)(addr + bytes_written); |
934 | |
935 | Status error = NativeProcessFreeBSD::PtraceWrapper(req: PT_IO, pid: GetID(), addr: &io); |
936 | if (error.Fail() || io.piod_len == 0) |
937 | return error; |
938 | |
939 | bytes_written += io.piod_len; |
940 | io.piod_len = size - bytes_written; |
941 | } while (bytes_written < size); |
942 | |
943 | return error; |
944 | } |
945 | |
946 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> |
947 | NativeProcessFreeBSD::GetAuxvData() const { |
948 | int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_AUXV, static_cast<int>(GetID())}; |
949 | size_t auxv_size = AT_COUNT * sizeof(Elf_Auxinfo); |
950 | std::unique_ptr<WritableMemoryBuffer> buf = |
951 | llvm::WritableMemoryBuffer::getNewMemBuffer(Size: auxv_size); |
952 | |
953 | if (::sysctl(mib, 4, buf->getBufferStart(), &auxv_size, nullptr, 0) != 0) |
954 | return std::error_code(errno, std::generic_category()); |
955 | |
956 | return buf; |
957 | } |
958 | |
959 | Status NativeProcessFreeBSD::SetupTrace() { |
960 | // Enable event reporting |
961 | int events; |
962 | Status status = |
963 | PtraceWrapper(req: PT_GET_EVENT_MASK, pid: GetID(), addr: &events, data: sizeof(events)); |
964 | if (status.Fail()) |
965 | return status; |
966 | events |= PTRACE_LWP | PTRACE_FORK | PTRACE_VFORK; |
967 | status = PtraceWrapper(req: PT_SET_EVENT_MASK, pid: GetID(), addr: &events, data: sizeof(events)); |
968 | if (status.Fail()) |
969 | return status; |
970 | |
971 | return ReinitializeThreads(); |
972 | } |
973 | |
974 | Status NativeProcessFreeBSD::ReinitializeThreads() { |
975 | // Clear old threads |
976 | m_threads.clear(); |
977 | |
978 | int num_lwps; |
979 | Status error = PtraceWrapper(req: PT_GETNUMLWPS, pid: GetID(), addr: nullptr, data: 0, result: &num_lwps); |
980 | if (error.Fail()) |
981 | return error; |
982 | |
983 | std::vector<lwpid_t> lwp_ids; |
984 | lwp_ids.resize(new_size: num_lwps); |
985 | error = PtraceWrapper(req: PT_GETLWPLIST, pid: GetID(), addr: lwp_ids.data(), |
986 | data: lwp_ids.size() * sizeof(lwpid_t), result: &num_lwps); |
987 | if (error.Fail()) |
988 | return error; |
989 | |
990 | // Reinitialize from scratch threads and register them in process |
991 | for (lwpid_t lwp : lwp_ids) |
992 | AddThread(thread_id: lwp); |
993 | |
994 | return error; |
995 | } |
996 | |
997 | bool NativeProcessFreeBSD::SupportHardwareSingleStepping() const { |
998 | return !m_arch.IsMIPS(); |
999 | } |
1000 | |
1001 | void NativeProcessFreeBSD::MonitorClone(::pid_t child_pid, bool is_vfork, |
1002 | NativeThreadFreeBSD &parent_thread) { |
1003 | Log *log = GetLog(mask: POSIXLog::Process); |
1004 | LLDB_LOG(log, "fork, child_pid={0}", child_pid); |
1005 | |
1006 | int status; |
1007 | ::pid_t wait_pid = |
1008 | llvm::sys::RetryAfterSignal(Fail: -1, F&: ::waitpid, As: child_pid, As: &status, As: 0); |
1009 | if (wait_pid != child_pid) { |
1010 | LLDB_LOG(log, |
1011 | "waiting for pid {0} failed. Assuming the pid has " |
1012 | "disappeared in the meantime", |
1013 | child_pid); |
1014 | return; |
1015 | } |
1016 | if (WIFEXITED(status)) { |
1017 | LLDB_LOG(log, |
1018 | "waiting for pid {0} returned an 'exited' event. Not " |
1019 | "tracking it.", |
1020 | child_pid); |
1021 | return; |
1022 | } |
1023 | |
1024 | struct ptrace_lwpinfo info; |
1025 | const auto siginfo_err = PtraceWrapper(PT_LWPINFO, child_pid, &info, sizeof(info)); |
1026 | if (siginfo_err.Fail()) { |
1027 | LLDB_LOG(log, "PT_LWPINFO failed {0}", siginfo_err); |
1028 | return; |
1029 | } |
1030 | assert(info.pl_event == PL_EVENT_SIGNAL); |
1031 | lldb::tid_t child_tid = info.pl_lwpid; |
1032 | |
1033 | std::unique_ptr<NativeProcessFreeBSD> child_process{ |
1034 | new NativeProcessFreeBSD(static_cast<::pid_t>(child_pid), m_terminal_fd, |
1035 | m_delegate, m_arch, m_main_loop)}; |
1036 | if (!is_vfork) |
1037 | child_process->m_software_breakpoints = m_software_breakpoints; |
1038 | |
1039 | Extension expected_ext = is_vfork ? Extension::vfork : Extension::fork; |
1040 | if ((m_enabled_extensions & expected_ext) == expected_ext) { |
1041 | child_process->SetupTrace(); |
1042 | for (const auto &thread : child_process->m_threads) |
1043 | static_cast<NativeThreadFreeBSD &>(*thread).SetStoppedBySignal(SIGSTOP); |
1044 | child_process->SetState(state: StateType::eStateStopped, notify_delegates: false); |
1045 | |
1046 | m_delegate.NewSubprocess(parent_process: this, child_process: std::move(child_process)); |
1047 | if (is_vfork) |
1048 | parent_thread.SetStoppedByVFork(child_pid, child_tid); |
1049 | else |
1050 | parent_thread.SetStoppedByFork(child_pid, child_tid); |
1051 | SetState(state: StateType::eStateStopped, notify_delegates: true); |
1052 | } else { |
1053 | child_process->Detach(); |
1054 | Status pt_error = |
1055 | PtraceWrapper(PT_CONTINUE, pid: GetID(), addr: reinterpret_cast<void *>(1), data: 0); |
1056 | if (pt_error.Fail()) { |
1057 | LLDB_LOG_ERROR(log, pt_error.ToError(), |
1058 | "unable to resume parent process {1}: {0}", GetID()); |
1059 | SetState(state: StateType::eStateInvalid); |
1060 | } |
1061 | } |
1062 | } |
1063 | |
1064 | llvm::Expected<std::string> |
1065 | NativeProcessFreeBSD::SaveCore(llvm::StringRef path_hint) { |
1066 | #if defined(PT_COREDUMP) |
1067 | using namespace llvm::sys::fs; |
1068 | |
1069 | llvm::SmallString<128> path{path_hint}; |
1070 | Status error; |
1071 | struct ptrace_coredump pc = {}; |
1072 | |
1073 | // Try with the suggested path first. If there is no suggested path or it |
1074 | // failed to open, use a temporary file. |
1075 | if (path.empty() || |
1076 | openFile(path, pc.pc_fd, CD_CreateNew, FA_Write, OF_None)) { |
1077 | if (std::error_code errc = |
1078 | createTemporaryFile("lldb", "core", pc.pc_fd, path)) |
1079 | return llvm::createStringError(errc, "Unable to create a temporary file"); |
1080 | } |
1081 | error = PtraceWrapper(PT_COREDUMP, GetID(), &pc, sizeof(pc)); |
1082 | |
1083 | std::error_code close_err = closeFile(pc.pc_fd); |
1084 | if (error.Fail()) |
1085 | return error.ToError(); |
1086 | if (close_err) |
1087 | return llvm::createStringError( |
1088 | close_err, "Unable to close the core dump after writing"); |
1089 | return path.str().str(); |
1090 | #else // !defined(PT_COREDUMP) |
1091 | return llvm::createStringError( |
1092 | EC: llvm::inconvertibleErrorCode(), |
1093 | S: "PT_COREDUMP not supported in the FreeBSD version used to build LLDB"); |
1094 | #endif |
1095 | } |
1096 |
Definitions
- EnsureFDFlags
- CanTrace
- Launch
- Attach
- GetSupportedExtensions
- NativeProcessFreeBSD
- MonitorCallback
- MonitorExited
- MonitorSIGSTOP
- MonitorSIGTRAP
- MonitorSignal
- PtraceWrapper
- GetSoftwareBreakpointTrapOpcode
- Resume
- Halt
- Detach
- Signal
- Interrupt
- Kill
- GetMemoryRegionInfo
- PopulateMemoryRegionCache
- UpdateThreads
- SetBreakpoint
- GetLoadedModuleFileSpec
- GetFileLoadAddress
- SigchldHandler
- HasThreadNoLock
- AddThread
- RemoveThread
- Attach
- ReadMemory
- WriteMemory
- GetAuxvData
- SetupTrace
- ReinitializeThreads
- SupportHardwareSingleStepping
- MonitorClone
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more