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 = Status::FromErrno(); |
45 | return error; |
46 | } |
47 | |
48 | if (fcntl(fd: fd, F_SETFL, status | flags) == -1) { |
49 | error = Status::FromErrno(); |
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 = Status::FromErrno(); |
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 = Status::FromErrno(); |
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::FromErrorString( |
451 | str: "NetBSD does not support passing multiple signals " |
452 | "simultaneously") |
453 | .ToError(); |
454 | signal = action->signal; |
455 | signaled_lwp = thread->GetID(); |
456 | } |
457 | } |
458 | } |
459 | } |
460 | |
461 | if (signaled_threads == 0) { |
462 | ptrace_siginfo_t siginfo; |
463 | siginfo.psi_siginfo.si_signo = LLDB_INVALID_SIGNAL_NUMBER; |
464 | return siginfo; |
465 | } |
466 | |
467 | if (signaled_threads > 1 && signaled_threads < threads.size()) |
468 | return Status::FromErrorString( |
469 | str: "NetBSD does not support passing signal to 1<i<all threads") |
470 | .ToError(); |
471 | |
472 | ptrace_siginfo_t siginfo; |
473 | siginfo.psi_siginfo.si_signo = signal; |
474 | siginfo.psi_siginfo.si_code = SI_USER; |
475 | siginfo.psi_siginfo.si_pid = getpid(); |
476 | siginfo.psi_siginfo.si_uid = getuid(); |
477 | if (signaled_threads == 1) |
478 | siginfo.psi_lwpid = signaled_lwp; |
479 | else // signal for the whole process |
480 | siginfo.psi_lwpid = 0; |
481 | return siginfo; |
482 | } |
483 | |
484 | Status NativeProcessNetBSD::Resume(const ResumeActionList &resume_actions) { |
485 | Log *log = GetLog(mask: POSIXLog::Process); |
486 | LLDB_LOG(log, "pid {0}", GetID()); |
487 | |
488 | Status ret; |
489 | |
490 | Expected<ptrace_siginfo_t> siginfo = |
491 | ComputeSignalInfo(m_threads, resume_actions); |
492 | if (!siginfo) |
493 | return Status(siginfo.takeError()); |
494 | |
495 | for (const auto &abs_thread : m_threads) { |
496 | assert(abs_thread && "thread list should not contain NULL threads"); |
497 | NativeThreadNetBSD &thread = static_cast<NativeThreadNetBSD &>(*abs_thread); |
498 | |
499 | const ResumeAction *action = |
500 | resume_actions.GetActionForThread(tid: thread.GetID(), default_ok: true); |
501 | // we need to explicit issue suspend requests, so it is simpler to map it |
502 | // into proper action |
503 | ResumeAction suspend_action{.tid: thread.GetID(), .state: eStateSuspended, |
504 | LLDB_INVALID_SIGNAL_NUMBER}; |
505 | |
506 | if (action == nullptr) { |
507 | LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(), |
508 | thread.GetID()); |
509 | action = &suspend_action; |
510 | } |
511 | |
512 | LLDB_LOG( |
513 | log, |
514 | "processing resume action state {0} signal {1} for pid {2} tid {3}", |
515 | action->state, action->signal, GetID(), thread.GetID()); |
516 | |
517 | switch (action->state) { |
518 | case eStateRunning: |
519 | ret = thread.Resume(); |
520 | break; |
521 | case eStateStepping: |
522 | ret = thread.SingleStep(); |
523 | break; |
524 | case eStateSuspended: |
525 | case eStateStopped: |
526 | if (action->signal != LLDB_INVALID_SIGNAL_NUMBER) |
527 | return Status::FromErrorString( |
528 | str: "Passing signal to suspended thread unsupported"); |
529 | |
530 | ret = thread.Suspend(); |
531 | break; |
532 | |
533 | default: |
534 | return Status::FromErrorStringWithFormat( |
535 | format: "NativeProcessNetBSD::%s (): unexpected state %s specified " |
536 | "for pid %"PRIu64 ", tid %"PRIu64, |
537 | __FUNCTION__, StateAsCString(state: action->state), GetID(), thread.GetID()); |
538 | } |
539 | |
540 | if (!ret.Success()) |
541 | return ret; |
542 | } |
543 | |
544 | int signal = 0; |
545 | if (siginfo->psi_siginfo.si_signo != LLDB_INVALID_SIGNAL_NUMBER) { |
546 | ret = PtraceWrapper(req: PT_SET_SIGINFO, pid: GetID(), addr: &siginfo.get(), |
547 | data: sizeof(*siginfo)); |
548 | if (!ret.Success()) |
549 | return ret; |
550 | signal = siginfo->psi_siginfo.si_signo; |
551 | } |
552 | |
553 | ret = |
554 | PtraceWrapper(PT_CONTINUE, pid: GetID(), addr: reinterpret_cast<void *>(1), data: signal); |
555 | if (ret.Success()) |
556 | SetState(state: eStateRunning, notify_delegates: true); |
557 | return ret; |
558 | } |
559 | |
560 | Status NativeProcessNetBSD::Halt() { return StopProcess(pid: GetID()); } |
561 | |
562 | Status NativeProcessNetBSD::Detach() { |
563 | Status error; |
564 | |
565 | // Stop monitoring the inferior. |
566 | m_sigchld_handle.reset(); |
567 | |
568 | // Tell ptrace to detach from the process. |
569 | if (GetID() == LLDB_INVALID_PROCESS_ID) |
570 | return error; |
571 | |
572 | return PtraceWrapper(PT_DETACH, pid: GetID(), addr: reinterpret_cast<void *>(1)); |
573 | } |
574 | |
575 | Status NativeProcessNetBSD::Signal(int signo) { |
576 | Status error; |
577 | |
578 | if (kill(pid: GetID(), sig: signo)) |
579 | error = Status::FromErrno(); |
580 | |
581 | return error; |
582 | } |
583 | |
584 | Status NativeProcessNetBSD::Interrupt() { return StopProcess(pid: GetID()); } |
585 | |
586 | Status NativeProcessNetBSD::Kill() { |
587 | Log *log = GetLog(mask: POSIXLog::Process); |
588 | LLDB_LOG(log, "pid {0}", GetID()); |
589 | |
590 | Status error; |
591 | |
592 | switch (m_state) { |
593 | case StateType::eStateInvalid: |
594 | case StateType::eStateExited: |
595 | case StateType::eStateCrashed: |
596 | case StateType::eStateDetached: |
597 | case StateType::eStateUnloaded: |
598 | // Nothing to do - the process is already dead. |
599 | LLDB_LOG(log, "ignored for PID {0} due to current state: {1}", GetID(), |
600 | StateAsCString(m_state)); |
601 | return error; |
602 | |
603 | case StateType::eStateConnected: |
604 | case StateType::eStateAttaching: |
605 | case StateType::eStateLaunching: |
606 | case StateType::eStateStopped: |
607 | case StateType::eStateRunning: |
608 | case StateType::eStateStepping: |
609 | case StateType::eStateSuspended: |
610 | // We can try to kill a process in these states. |
611 | break; |
612 | } |
613 | |
614 | if (kill(pid: GetID(), SIGKILL) != 0) { |
615 | error = Status::FromErrno(); |
616 | return error; |
617 | } |
618 | |
619 | return error; |
620 | } |
621 | |
622 | Status NativeProcessNetBSD::GetMemoryRegionInfo(lldb::addr_t load_addr, |
623 | MemoryRegionInfo &range_info) { |
624 | |
625 | if (m_supports_mem_region == LazyBool::eLazyBoolNo) { |
626 | // We're done. |
627 | return Status::FromErrorString(str: "unsupported"); |
628 | } |
629 | |
630 | Status error = PopulateMemoryRegionCache(); |
631 | if (error.Fail()) { |
632 | return error; |
633 | } |
634 | |
635 | lldb::addr_t prev_base_address = 0; |
636 | // FIXME start by finding the last region that is <= target address using |
637 | // binary search. Data is sorted. |
638 | // There can be a ton of regions on pthreads apps with lots of threads. |
639 | for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end(); |
640 | ++it) { |
641 | MemoryRegionInfo &proc_entry_info = it->first; |
642 | // Sanity check assumption that memory map entries are ascending. |
643 | assert((proc_entry_info.GetRange().GetRangeBase() >= prev_base_address) && |
644 | "descending memory map entries detected, unexpected"); |
645 | prev_base_address = proc_entry_info.GetRange().GetRangeBase(); |
646 | UNUSED_IF_ASSERT_DISABLED(prev_base_address); |
647 | // If the target address comes before this entry, indicate distance to next |
648 | // region. |
649 | if (load_addr < proc_entry_info.GetRange().GetRangeBase()) { |
650 | range_info.GetRange().SetRangeBase(load_addr); |
651 | range_info.GetRange().SetByteSize( |
652 | proc_entry_info.GetRange().GetRangeBase() - load_addr); |
653 | range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); |
654 | range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); |
655 | range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); |
656 | range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); |
657 | return error; |
658 | } else if (proc_entry_info.GetRange().Contains(r: load_addr)) { |
659 | // The target address is within the memory region we're processing here. |
660 | range_info = proc_entry_info; |
661 | return error; |
662 | } |
663 | // The target memory address comes somewhere after the region we just |
664 | // parsed. |
665 | } |
666 | // If we made it here, we didn't find an entry that contained the given |
667 | // address. Return the load_addr as start and the amount of bytes betwwen |
668 | // load address and the end of the memory as size. |
669 | range_info.GetRange().SetRangeBase(load_addr); |
670 | range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); |
671 | range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); |
672 | range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); |
673 | range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); |
674 | range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); |
675 | return error; |
676 | } |
677 | |
678 | Status NativeProcessNetBSD::PopulateMemoryRegionCache() { |
679 | Log *log = GetLog(mask: POSIXLog::Process); |
680 | // If our cache is empty, pull the latest. There should always be at least |
681 | // one memory region if memory region handling is supported. |
682 | if (!m_mem_region_cache.empty()) { |
683 | LLDB_LOG(log, "reusing {0} cached memory region entries", |
684 | m_mem_region_cache.size()); |
685 | return Status(); |
686 | } |
687 | |
688 | struct kinfo_vmentry *vm; |
689 | size_t count, i; |
690 | vm = kinfo_getvmmap(GetID(), &count); |
691 | if (vm == NULL) { |
692 | m_supports_mem_region = LazyBool::eLazyBoolNo; |
693 | Status error; |
694 | error = Status::FromErrorString(str: "not supported"); |
695 | return error; |
696 | } |
697 | for (i = 0; i < count; i++) { |
698 | MemoryRegionInfo info; |
699 | info.Clear(); |
700 | info.GetRange().SetRangeBase(vm[i].kve_start); |
701 | info.GetRange().SetRangeEnd(vm[i].kve_end); |
702 | info.SetMapped(MemoryRegionInfo::OptionalBool::eYes); |
703 | |
704 | if (vm[i].kve_protection & VM_PROT_READ) |
705 | info.SetReadable(MemoryRegionInfo::OptionalBool::eYes); |
706 | else |
707 | info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); |
708 | |
709 | if (vm[i].kve_protection & VM_PROT_WRITE) |
710 | info.SetWritable(MemoryRegionInfo::OptionalBool::eYes); |
711 | else |
712 | info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); |
713 | |
714 | if (vm[i].kve_protection & VM_PROT_EXECUTE) |
715 | info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes); |
716 | else |
717 | info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); |
718 | |
719 | if (vm[i].kve_path[0]) |
720 | info.SetName(vm[i].kve_path); |
721 | |
722 | m_mem_region_cache.emplace_back(args&: info, |
723 | args: FileSpec(info.GetName().GetCString())); |
724 | } |
725 | free(ptr: vm); |
726 | |
727 | if (m_mem_region_cache.empty()) { |
728 | // No entries after attempting to read them. This shouldn't happen. Assume |
729 | // we don't support map entries. |
730 | LLDB_LOG(log, "failed to find any vmmap entries, assuming no support " |
731 | "for memory region metadata retrieval"); |
732 | m_supports_mem_region = LazyBool::eLazyBoolNo; |
733 | Status error; |
734 | error = Status::FromErrorString(str: "not supported"); |
735 | return error; |
736 | } |
737 | LLDB_LOG(log, "read {0} memory region entries from process {1}", |
738 | m_mem_region_cache.size(), GetID()); |
739 | // We support memory retrieval, remember that. |
740 | m_supports_mem_region = LazyBool::eLazyBoolYes; |
741 | return Status(); |
742 | } |
743 | |
744 | lldb::addr_t NativeProcessNetBSD::GetSharedLibraryInfoAddress() { |
745 | // punt on this for now |
746 | return LLDB_INVALID_ADDRESS; |
747 | } |
748 | |
749 | size_t NativeProcessNetBSD::UpdateThreads() { return m_threads.size(); } |
750 | |
751 | Status NativeProcessNetBSD::SetBreakpoint(lldb::addr_t addr, uint32_t size, |
752 | bool hardware) { |
753 | if (hardware) |
754 | return Status::FromErrorString( |
755 | str: "NativeProcessNetBSD does not support hardware breakpoints"); |
756 | else |
757 | return SetSoftwareBreakpoint(addr, size_hint: size); |
758 | } |
759 | |
760 | Status NativeProcessNetBSD::GetLoadedModuleFileSpec(const char *module_path, |
761 | FileSpec &file_spec) { |
762 | Status error = PopulateMemoryRegionCache(); |
763 | if (error.Fail()) |
764 | return error; |
765 | |
766 | FileSpec module_file_spec(module_path); |
767 | FileSystem::Instance().Resolve(file_spec&: module_file_spec); |
768 | |
769 | file_spec.Clear(); |
770 | for (const auto &it : m_mem_region_cache) { |
771 | if (it.second.GetFilename() == module_file_spec.GetFilename()) { |
772 | file_spec = it.second; |
773 | return Status(); |
774 | } |
775 | } |
776 | return Status::FromErrorStringWithFormat( |
777 | format: "Module file (%s) not found in process' memory map!", |
778 | module_file_spec.GetFilename().AsCString()); |
779 | } |
780 | |
781 | Status NativeProcessNetBSD::GetFileLoadAddress(const llvm::StringRef &file_name, |
782 | lldb::addr_t &load_addr) { |
783 | load_addr = LLDB_INVALID_ADDRESS; |
784 | Status error = PopulateMemoryRegionCache(); |
785 | if (error.Fail()) |
786 | return error; |
787 | |
788 | FileSpec file(file_name); |
789 | for (const auto &it : m_mem_region_cache) { |
790 | if (it.second == file) { |
791 | load_addr = it.first.GetRange().GetRangeBase(); |
792 | return Status(); |
793 | } |
794 | } |
795 | return Status::FromErrorStringWithFormat(format: "No load address found for file %s.", |
796 | file_name.str().c_str()); |
797 | } |
798 | |
799 | void NativeProcessNetBSD::SigchldHandler() { |
800 | Log *log = GetLog(mask: POSIXLog::Process); |
801 | int status; |
802 | ::pid_t wait_pid = llvm::sys::RetryAfterSignal(-1, waitpid, GetID(), &status, |
803 | WALLSIG | WNOHANG); |
804 | |
805 | if (wait_pid == 0) |
806 | return; |
807 | |
808 | if (wait_pid == -1) { |
809 | Status error(errno, eErrorTypePOSIX); |
810 | LLDB_LOG(log, "waitpid ({0}, &status, _) failed: {1}", GetID(), error); |
811 | return; |
812 | } |
813 | |
814 | WaitStatus wait_status = WaitStatus::Decode(wstatus: status); |
815 | bool exited = wait_status.type == WaitStatus::Exit || |
816 | (wait_status.type == WaitStatus::Signal && |
817 | wait_pid == static_cast<::pid_t>(GetID())); |
818 | |
819 | LLDB_LOG(log, |
820 | "waitpid ({0}, &status, _) => pid = {1}, status = {2}, exited = {3}", |
821 | GetID(), wait_pid, status, exited); |
822 | |
823 | if (exited) |
824 | MonitorExited(pid: wait_pid, status: wait_status); |
825 | else { |
826 | assert(wait_status.type == WaitStatus::Stop); |
827 | MonitorCallback(pid: wait_pid, signal: wait_status.status); |
828 | } |
829 | } |
830 | |
831 | bool NativeProcessNetBSD::HasThreadNoLock(lldb::tid_t thread_id) { |
832 | for (const auto &thread : m_threads) { |
833 | assert(thread && "thread list should not contain NULL threads"); |
834 | if (thread->GetID() == thread_id) { |
835 | // We have this thread. |
836 | return true; |
837 | } |
838 | } |
839 | |
840 | // We don't have this thread. |
841 | return false; |
842 | } |
843 | |
844 | NativeThreadNetBSD &NativeProcessNetBSD::AddThread(lldb::tid_t thread_id) { |
845 | Log *log = GetLog(mask: POSIXLog::Thread); |
846 | LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id); |
847 | |
848 | assert(thread_id > 0); |
849 | assert(!HasThreadNoLock(thread_id) && |
850 | "attempted to add a thread by id that already exists"); |
851 | |
852 | // If this is the first thread, save it as the current thread |
853 | if (m_threads.empty()) |
854 | SetCurrentThreadID(thread_id); |
855 | |
856 | m_threads.push_back(x: std::make_unique<NativeThreadNetBSD>(args&: *this, args&: thread_id)); |
857 | return static_cast<NativeThreadNetBSD &>(*m_threads.back()); |
858 | } |
859 | |
860 | void NativeProcessNetBSD::RemoveThread(lldb::tid_t thread_id) { |
861 | Log *log = GetLog(mask: POSIXLog::Thread); |
862 | LLDB_LOG(log, "pid {0} removing thread with tid {1}", GetID(), thread_id); |
863 | |
864 | assert(thread_id > 0); |
865 | assert(HasThreadNoLock(thread_id) && |
866 | "attempted to remove a thread that does not exist"); |
867 | |
868 | for (auto it = m_threads.begin(); it != m_threads.end(); ++it) { |
869 | if ((*it)->GetID() == thread_id) { |
870 | m_threads.erase(position: it); |
871 | break; |
872 | } |
873 | } |
874 | } |
875 | |
876 | Status NativeProcessNetBSD::Attach() { |
877 | // Attach to the requested process. |
878 | // An attach will cause the thread to stop with a SIGSTOP. |
879 | Status status = PtraceWrapper(PT_ATTACH, pid: m_pid); |
880 | if (status.Fail()) |
881 | return status; |
882 | |
883 | int wstatus; |
884 | // Need to use WALLSIG otherwise we receive an error with errno=ECHLD At this |
885 | // point we should have a thread stopped if waitpid succeeds. |
886 | if ((wstatus = llvm::sys::RetryAfterSignal(-1, waitpid, m_pid, nullptr, |
887 | WALLSIG)) < 0) |
888 | return Status(errno, eErrorTypePOSIX); |
889 | |
890 | // Initialize threads and tracing status |
891 | // NB: this needs to be called before we set thread state |
892 | status = SetupTrace(); |
893 | if (status.Fail()) |
894 | return status; |
895 | |
896 | for (const auto &thread : m_threads) |
897 | static_cast<NativeThreadNetBSD &>(*thread).SetStoppedBySignal(SIGSTOP); |
898 | |
899 | // Let our process instance know the thread has stopped. |
900 | SetCurrentThreadID(m_threads.front()->GetID()); |
901 | SetState(state: StateType::eStateStopped, notify_delegates: false); |
902 | return Status(); |
903 | } |
904 | |
905 | Status NativeProcessNetBSD::ReadMemory(lldb::addr_t addr, void *buf, |
906 | size_t size, size_t &bytes_read) { |
907 | unsigned char *dst = static_cast<unsigned char *>(buf); |
908 | struct ptrace_io_desc io; |
909 | |
910 | Log *log = GetLog(mask: POSIXLog::Memory); |
911 | LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); |
912 | |
913 | bytes_read = 0; |
914 | io.piod_op = PIOD_READ_D; |
915 | io.piod_len = size; |
916 | |
917 | do { |
918 | io.piod_offs = (void *)(addr + bytes_read); |
919 | io.piod_addr = dst + bytes_read; |
920 | |
921 | Status error = NativeProcessNetBSD::PtraceWrapper(req: PT_IO, pid: GetID(), addr: &io); |
922 | if (error.Fail() || io.piod_len == 0) |
923 | return error; |
924 | |
925 | bytes_read += io.piod_len; |
926 | io.piod_len = size - bytes_read; |
927 | } while (bytes_read < size); |
928 | |
929 | return Status(); |
930 | } |
931 | |
932 | Status NativeProcessNetBSD::WriteMemory(lldb::addr_t addr, const void *buf, |
933 | size_t size, size_t &bytes_written) { |
934 | const unsigned char *src = static_cast<const unsigned char *>(buf); |
935 | Status error; |
936 | struct ptrace_io_desc io; |
937 | |
938 | Log *log = GetLog(mask: POSIXLog::Memory); |
939 | LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); |
940 | |
941 | bytes_written = 0; |
942 | io.piod_op = PIOD_WRITE_D; |
943 | io.piod_len = size; |
944 | |
945 | do { |
946 | io.piod_addr = |
947 | const_cast<void *>(static_cast<const void *>(src + bytes_written)); |
948 | io.piod_offs = (void *)(addr + bytes_written); |
949 | |
950 | Status error = NativeProcessNetBSD::PtraceWrapper(req: PT_IO, pid: GetID(), addr: &io); |
951 | if (error.Fail() || io.piod_len == 0) |
952 | return error; |
953 | |
954 | bytes_written += io.piod_len; |
955 | io.piod_len = size - bytes_written; |
956 | } while (bytes_written < size); |
957 | |
958 | return error; |
959 | } |
960 | |
961 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> |
962 | NativeProcessNetBSD::GetAuxvData() const { |
963 | /* |
964 | * ELF_AUX_ENTRIES is currently restricted to kernel |
965 | * (<sys/exec_elf.h> r. 1.155 specifies 15) |
966 | * |
967 | * ptrace(2) returns the whole AUXV including extra fiels after AT_NULL this |
968 | * information isn't needed. |
969 | */ |
970 | size_t auxv_size = 100 * sizeof(AuxInfo); |
971 | |
972 | ErrorOr<std::unique_ptr<WritableMemoryBuffer>> buf = |
973 | llvm::WritableMemoryBuffer::getNewMemBuffer(Size: auxv_size); |
974 | |
975 | struct ptrace_io_desc io; |
976 | io.piod_op = PIOD_READ_AUXV; |
977 | io.piod_offs = 0; |
978 | io.piod_addr = static_cast<void *>(buf.get()->getBufferStart()); |
979 | io.piod_len = auxv_size; |
980 | |
981 | Status error = NativeProcessNetBSD::PtraceWrapper(req: PT_IO, pid: GetID(), addr: &io); |
982 | |
983 | if (error.Fail()) |
984 | return std::error_code(error.GetError(), std::generic_category()); |
985 | |
986 | if (io.piod_len < 1) |
987 | return std::error_code(ECANCELED, std::generic_category()); |
988 | |
989 | return std::move(buf); |
990 | } |
991 | |
992 | Status NativeProcessNetBSD::SetupTrace() { |
993 | // Enable event reporting |
994 | ptrace_event_t events; |
995 | Status status = |
996 | PtraceWrapper(req: PT_GET_EVENT_MASK, pid: GetID(), addr: &events, data: sizeof(events)); |
997 | if (status.Fail()) |
998 | return status; |
999 | // TODO: PTRACE_POSIX_SPAWN? |
1000 | events.pe_set_event |= PTRACE_LWP_CREATE | PTRACE_LWP_EXIT | PTRACE_FORK | |
1001 | PTRACE_VFORK | PTRACE_VFORK_DONE; |
1002 | status = PtraceWrapper(req: PT_SET_EVENT_MASK, pid: GetID(), addr: &events, data: sizeof(events)); |
1003 | if (status.Fail()) |
1004 | return status; |
1005 | |
1006 | return ReinitializeThreads(); |
1007 | } |
1008 | |
1009 | Status NativeProcessNetBSD::ReinitializeThreads() { |
1010 | // Clear old threads |
1011 | m_threads.clear(); |
1012 | |
1013 | // Initialize new thread |
1014 | #ifdef PT_LWPSTATUS |
1015 | struct ptrace_lwpstatus info = {}; |
1016 | int op = PT_LWPNEXT; |
1017 | #else |
1018 | struct ptrace_lwpinfo info = {}; |
1019 | int op = PT_LWPINFO; |
1020 | #endif |
1021 | |
1022 | Status error = PtraceWrapper(req: op, pid: GetID(), addr: &info, data: sizeof(info)); |
1023 | |
1024 | if (error.Fail()) { |
1025 | return error; |
1026 | } |
1027 | // Reinitialize from scratch threads and register them in process |
1028 | while (info.pl_lwpid != 0) { |
1029 | AddThread(thread_id: info.pl_lwpid); |
1030 | error = PtraceWrapper(req: op, pid: GetID(), addr: &info, data: sizeof(info)); |
1031 | if (error.Fail()) { |
1032 | return error; |
1033 | } |
1034 | } |
1035 | |
1036 | return error; |
1037 | } |
1038 | |
1039 | void NativeProcessNetBSD::MonitorClone(::pid_t child_pid, bool is_vfork, |
1040 | NativeThreadNetBSD &parent_thread) { |
1041 | Log *log = GetLog(mask: POSIXLog::Process); |
1042 | LLDB_LOG(log, "clone, child_pid={0}", child_pid); |
1043 | |
1044 | int status; |
1045 | ::pid_t wait_pid = |
1046 | llvm::sys::RetryAfterSignal(Fail: -1, F&: ::waitpid, As: child_pid, As: &status, As: 0); |
1047 | if (wait_pid != child_pid) { |
1048 | LLDB_LOG(log, |
1049 | "waiting for pid {0} failed. Assuming the pid has " |
1050 | "disappeared in the meantime", |
1051 | child_pid); |
1052 | return; |
1053 | } |
1054 | if (WIFEXITED(status)) { |
1055 | LLDB_LOG(log, |
1056 | "waiting for pid {0} returned an 'exited' event. Not " |
1057 | "tracking it.", |
1058 | child_pid); |
1059 | return; |
1060 | } |
1061 | |
1062 | ptrace_siginfo_t info; |
1063 | const auto siginfo_err = |
1064 | PtraceWrapper(req: PT_GET_SIGINFO, pid: child_pid, addr: &info, data: sizeof(info)); |
1065 | if (siginfo_err.Fail()) { |
1066 | LLDB_LOG(log, "PT_GET_SIGINFO failed {0}", siginfo_err); |
1067 | return; |
1068 | } |
1069 | assert(info.psi_lwpid >= 0); |
1070 | lldb::tid_t child_tid = info.psi_lwpid; |
1071 | |
1072 | std::unique_ptr<NativeProcessNetBSD> child_process{ |
1073 | new NativeProcessNetBSD(static_cast<::pid_t>(child_pid), m_terminal_fd, |
1074 | m_delegate, m_arch, m_main_loop)}; |
1075 | if (!is_vfork) |
1076 | child_process->m_software_breakpoints = m_software_breakpoints; |
1077 | |
1078 | Extension expected_ext = is_vfork ? Extension::vfork : Extension::fork; |
1079 | if ((m_enabled_extensions & expected_ext) == expected_ext) { |
1080 | child_process->SetupTrace(); |
1081 | for (const auto &thread : child_process->m_threads) |
1082 | static_cast<NativeThreadNetBSD &>(*thread).SetStoppedBySignal(SIGSTOP); |
1083 | child_process->SetState(state: StateType::eStateStopped, notify_delegates: false); |
1084 | |
1085 | m_delegate.NewSubprocess(parent_process: this, child_process: std::move(child_process)); |
1086 | if (is_vfork) |
1087 | parent_thread.SetStoppedByVFork(child_pid, child_tid); |
1088 | else |
1089 | parent_thread.SetStoppedByFork(child_pid, child_tid); |
1090 | SetState(state: StateType::eStateStopped, notify_delegates: true); |
1091 | } else { |
1092 | child_process->Detach(); |
1093 | Status pt_error = |
1094 | PtraceWrapper(PT_CONTINUE, pid: GetID(), addr: reinterpret_cast<void *>(1), data: 0); |
1095 | if (pt_error.Fail()) { |
1096 | LLDB_LOG_ERROR(log, std::move(pt_error.ToError()), |
1097 | "unable to resume parent process {1}: {0}", GetID()); |
1098 | SetState(state: StateType::eStateInvalid); |
1099 | } |
1100 | } |
1101 | } |
1102 | |
1103 | llvm::Expected<std::string> |
1104 | NativeProcessNetBSD::SaveCore(llvm::StringRef path_hint) { |
1105 | llvm::SmallString<128> path{path_hint}; |
1106 | Status error; |
1107 | |
1108 | // Try with the suggested path first. |
1109 | if (!path.empty()) { |
1110 | error = PtraceWrapper(req: PT_DUMPCORE, pid: GetID(), addr: path.data(), data: path.size()); |
1111 | if (!error.Fail()) |
1112 | return path.str().str(); |
1113 | |
1114 | // If the request errored, fall back to a generic temporary file. |
1115 | } |
1116 | |
1117 | if (std::error_code errc = |
1118 | llvm::sys::fs::createTemporaryFile(Prefix: "lldb", Suffix: "core", ResultPath&: path)) |
1119 | return llvm::createStringError(EC: errc, S: "Unable to create a temporary file"); |
1120 | |
1121 | error = PtraceWrapper(PT_DUMPCORE, GetID(), path.data(), path.size()); |
1122 | if (error.Fail()) |
1123 | return error.ToError(); |
1124 | return path.str().str(); |
1125 | } |
1126 |
Definitions
- EnsureFDFlags
- Launch
- Attach
- GetSupportedExtensions
- NativeProcessNetBSD
- MonitorCallback
- MonitorExited
- MonitorSIGSTOP
- MonitorSIGTRAP
- MonitorSignal
- StopProcess
- PtraceWrapper
- ComputeSignalInfo
- Resume
- Halt
- Detach
- Signal
- Interrupt
- Kill
- GetMemoryRegionInfo
- PopulateMemoryRegionCache
- GetSharedLibraryInfoAddress
- UpdateThreads
- SetBreakpoint
- GetLoadedModuleFileSpec
- GetFileLoadAddress
- SigchldHandler
- HasThreadNoLock
- AddThread
- RemoveThread
- Attach
- ReadMemory
- WriteMemory
- GetAuxvData
- SetupTrace
- ReinitializeThreads
- MonitorClone
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more