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