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