1 | //===-- NativeThreadNetBSD.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 "NativeThreadNetBSD.h" |
10 | #include "NativeRegisterContextNetBSD.h" |
11 | |
12 | #include "NativeProcessNetBSD.h" |
13 | |
14 | #include "Plugins/Process/POSIX/CrashReason.h" |
15 | #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" |
16 | #include "lldb/Utility/LLDBAssert.h" |
17 | #include "lldb/Utility/RegisterValue.h" |
18 | #include "lldb/Utility/State.h" |
19 | #include "llvm/Support/Errno.h" |
20 | |
21 | // clang-format off |
22 | #include <sys/types.h> |
23 | #include <sys/ptrace.h> |
24 | // clang-format on |
25 | |
26 | #include <sstream> |
27 | |
28 | // clang-format off |
29 | #include <sys/types.h> |
30 | #include <sys/sysctl.h> |
31 | // clang-format on |
32 | |
33 | using namespace lldb; |
34 | using namespace lldb_private; |
35 | using namespace lldb_private::process_netbsd; |
36 | |
37 | NativeThreadNetBSD::NativeThreadNetBSD(NativeProcessNetBSD &process, |
38 | lldb::tid_t tid) |
39 | : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid), |
40 | m_stop_info(), m_reg_context_up( |
41 | NativeRegisterContextNetBSD::CreateHostNativeRegisterContextNetBSD(target_arch: process.GetArchitecture(), native_thread&: *this) |
42 | ), m_stop_description() {} |
43 | |
44 | Status NativeThreadNetBSD::Resume() { |
45 | Status ret = NativeProcessNetBSD::PtraceWrapper(req: PT_RESUME, pid: m_process.GetID(), |
46 | addr: nullptr, data: GetID()); |
47 | if (!ret.Success()) |
48 | return ret; |
49 | ret = NativeProcessNetBSD::PtraceWrapper(req: PT_CLEARSTEP, pid: m_process.GetID(), |
50 | addr: nullptr, data: GetID()); |
51 | if (ret.Success()) |
52 | SetRunning(); |
53 | return ret; |
54 | } |
55 | |
56 | Status NativeThreadNetBSD::SingleStep() { |
57 | Status ret = NativeProcessNetBSD::PtraceWrapper(req: PT_RESUME, pid: m_process.GetID(), |
58 | addr: nullptr, data: GetID()); |
59 | if (!ret.Success()) |
60 | return ret; |
61 | ret = NativeProcessNetBSD::PtraceWrapper(req: PT_SETSTEP, pid: m_process.GetID(), |
62 | addr: nullptr, data: GetID()); |
63 | if (ret.Success()) |
64 | SetStepping(); |
65 | return ret; |
66 | } |
67 | |
68 | Status NativeThreadNetBSD::Suspend() { |
69 | Status ret = NativeProcessNetBSD::PtraceWrapper(req: PT_SUSPEND, pid: m_process.GetID(), |
70 | addr: nullptr, data: GetID()); |
71 | if (ret.Success()) |
72 | SetStopped(); |
73 | return ret; |
74 | } |
75 | |
76 | void NativeThreadNetBSD::SetStoppedBySignal(uint32_t signo, |
77 | const siginfo_t *info) { |
78 | Log *log = GetLog(mask: POSIXLog::Thread); |
79 | LLDB_LOG(log, "tid = {0} in called with signal {1}" , GetID(), signo); |
80 | |
81 | SetStopped(); |
82 | |
83 | m_stop_info.reason = StopReason::eStopReasonSignal; |
84 | m_stop_info.signo = signo; |
85 | |
86 | m_stop_description.clear(); |
87 | if (info) { |
88 | switch (signo) { |
89 | case SIGSEGV: |
90 | case SIGBUS: |
91 | case SIGFPE: |
92 | case SIGILL: |
93 | m_stop_description = GetCrashReasonString(info: *info); |
94 | break; |
95 | } |
96 | } |
97 | } |
98 | |
99 | void NativeThreadNetBSD::SetStoppedByBreakpoint() { |
100 | SetStopped(); |
101 | m_stop_info.reason = StopReason::eStopReasonBreakpoint; |
102 | m_stop_info.signo = SIGTRAP; |
103 | } |
104 | |
105 | void NativeThreadNetBSD::SetStoppedByTrace() { |
106 | SetStopped(); |
107 | m_stop_info.reason = StopReason::eStopReasonTrace; |
108 | m_stop_info.signo = SIGTRAP; |
109 | } |
110 | |
111 | void NativeThreadNetBSD::SetStoppedByExec() { |
112 | SetStopped(); |
113 | m_stop_info.reason = StopReason::eStopReasonExec; |
114 | m_stop_info.signo = SIGTRAP; |
115 | } |
116 | |
117 | void NativeThreadNetBSD::SetStoppedByWatchpoint(uint32_t wp_index) { |
118 | lldbassert(wp_index != LLDB_INVALID_INDEX32 && "wp_index cannot be invalid" ); |
119 | |
120 | std::ostringstream ostr; |
121 | ostr << GetRegisterContext().GetWatchpointAddress(wp_index) << " " ; |
122 | ostr << wp_index; |
123 | |
124 | ostr << " " << GetRegisterContext().GetWatchpointHitAddress(wp_index); |
125 | |
126 | SetStopped(); |
127 | m_stop_description = ostr.str(); |
128 | m_stop_info.reason = StopReason::eStopReasonWatchpoint; |
129 | m_stop_info.signo = SIGTRAP; |
130 | } |
131 | |
132 | void NativeThreadNetBSD::SetStoppedByFork(lldb::pid_t child_pid, |
133 | lldb::tid_t child_tid) { |
134 | SetStopped(); |
135 | |
136 | m_stop_info.reason = StopReason::eStopReasonFork; |
137 | m_stop_info.signo = SIGTRAP; |
138 | m_stop_info.details.fork.child_pid = child_pid; |
139 | m_stop_info.details.fork.child_tid = child_tid; |
140 | } |
141 | |
142 | void NativeThreadNetBSD::SetStoppedByVFork(lldb::pid_t child_pid, |
143 | lldb::tid_t child_tid) { |
144 | SetStopped(); |
145 | |
146 | m_stop_info.reason = StopReason::eStopReasonVFork; |
147 | m_stop_info.signo = SIGTRAP; |
148 | m_stop_info.details.fork.child_pid = child_pid; |
149 | m_stop_info.details.fork.child_tid = child_tid; |
150 | } |
151 | |
152 | void NativeThreadNetBSD::SetStoppedByVForkDone() { |
153 | SetStopped(); |
154 | |
155 | m_stop_info.reason = StopReason::eStopReasonVForkDone; |
156 | m_stop_info.signo = SIGTRAP; |
157 | } |
158 | |
159 | void NativeThreadNetBSD::SetStoppedWithNoReason() { |
160 | SetStopped(); |
161 | |
162 | m_stop_info.reason = StopReason::eStopReasonNone; |
163 | m_stop_info.signo = 0; |
164 | } |
165 | |
166 | void NativeThreadNetBSD::SetStopped() { |
167 | const StateType new_state = StateType::eStateStopped; |
168 | m_state = new_state; |
169 | m_stop_description.clear(); |
170 | } |
171 | |
172 | void NativeThreadNetBSD::SetRunning() { |
173 | m_state = StateType::eStateRunning; |
174 | m_stop_info.reason = StopReason::eStopReasonNone; |
175 | } |
176 | |
177 | void NativeThreadNetBSD::SetStepping() { |
178 | m_state = StateType::eStateStepping; |
179 | m_stop_info.reason = StopReason::eStopReasonNone; |
180 | } |
181 | |
182 | std::string NativeThreadNetBSD::GetName() { |
183 | Log *log = GetLog(mask: POSIXLog::Thread); |
184 | |
185 | #ifdef PT_LWPSTATUS |
186 | struct ptrace_lwpstatus info = {}; |
187 | info.pl_lwpid = m_tid; |
188 | Status error = NativeProcessNetBSD::PtraceWrapper( |
189 | PT_LWPSTATUS, static_cast<int>(m_process.GetID()), &info, sizeof(info)); |
190 | if (error.Fail()) { |
191 | return "" ; |
192 | } |
193 | return info.pl_name; |
194 | #else |
195 | std::vector<struct kinfo_lwp> infos; |
196 | int mib[5] = {CTL_KERN, KERN_LWP, static_cast<int>(m_process.GetID()), |
197 | sizeof(struct kinfo_lwp), 0}; |
198 | size_t size; |
199 | |
200 | if (::sysctl(mib, 5, nullptr, &size, nullptr, 0) == -1 || size == 0) { |
201 | LLDB_LOG(log, "sysctl() for LWP info size failed: {0}" , |
202 | llvm::sys::StrError()); |
203 | return "" ; |
204 | } |
205 | |
206 | mib[4] = size / sizeof(size_t); |
207 | infos.resize(size / sizeof(struct kinfo_lwp)); |
208 | |
209 | if (sysctl(mib, 5, infos.data(), &size, NULL, 0) == -1 || size == 0) { |
210 | LLDB_LOG(log, "sysctl() for LWP info failed: {0}" , llvm::sys::StrError()); |
211 | return "" ; |
212 | } |
213 | |
214 | size_t nlwps = size / sizeof(struct kinfo_lwp); |
215 | for (size_t i = 0; i < nlwps; i++) { |
216 | if (static_cast<lldb::tid_t>(infos[i].l_lid) == m_tid) { |
217 | return infos[i].l_name; |
218 | } |
219 | } |
220 | |
221 | LLDB_LOG(log, "unable to find lwp {0} in LWP infos" , m_tid); |
222 | return "" ; |
223 | #endif |
224 | } |
225 | |
226 | lldb::StateType NativeThreadNetBSD::GetState() { return m_state; } |
227 | |
228 | bool NativeThreadNetBSD::GetStopReason(ThreadStopInfo &stop_info, |
229 | std::string &description) { |
230 | Log *log = GetLog(mask: POSIXLog::Thread); |
231 | description.clear(); |
232 | |
233 | switch (m_state) { |
234 | case eStateStopped: |
235 | case eStateCrashed: |
236 | case eStateExited: |
237 | case eStateSuspended: |
238 | case eStateUnloaded: |
239 | stop_info = m_stop_info; |
240 | description = m_stop_description; |
241 | |
242 | return true; |
243 | |
244 | case eStateInvalid: |
245 | case eStateConnected: |
246 | case eStateAttaching: |
247 | case eStateLaunching: |
248 | case eStateRunning: |
249 | case eStateStepping: |
250 | case eStateDetached: |
251 | LLDB_LOG(log, "tid = {0} in state {1} cannot answer stop reason" , GetID(), |
252 | StateAsCString(m_state)); |
253 | return false; |
254 | } |
255 | llvm_unreachable("unhandled StateType!" ); |
256 | } |
257 | |
258 | NativeRegisterContextNetBSD &NativeThreadNetBSD::GetRegisterContext() { |
259 | assert(m_reg_context_up); |
260 | return *m_reg_context_up; |
261 | } |
262 | |
263 | Status NativeThreadNetBSD::SetWatchpoint(lldb::addr_t addr, size_t size, |
264 | uint32_t watch_flags, bool hardware) { |
265 | assert(m_state == eStateStopped); |
266 | if (!hardware) |
267 | return Status("not implemented" ); |
268 | Status error = RemoveWatchpoint(addr); |
269 | if (error.Fail()) |
270 | return error; |
271 | uint32_t wp_index = |
272 | GetRegisterContext().SetHardwareWatchpoint(addr, size, watch_flags); |
273 | if (wp_index == LLDB_INVALID_INDEX32) |
274 | return Status("Setting hardware watchpoint failed." ); |
275 | m_watchpoint_index_map.insert(x: {addr, wp_index}); |
276 | return Status(); |
277 | } |
278 | |
279 | Status NativeThreadNetBSD::RemoveWatchpoint(lldb::addr_t addr) { |
280 | auto wp = m_watchpoint_index_map.find(x: addr); |
281 | if (wp == m_watchpoint_index_map.end()) |
282 | return Status(); |
283 | uint32_t wp_index = wp->second; |
284 | m_watchpoint_index_map.erase(position: wp); |
285 | if (GetRegisterContext().ClearHardwareWatchpoint(hw_index: wp_index)) |
286 | return Status(); |
287 | return Status("Clearing hardware watchpoint failed." ); |
288 | } |
289 | |
290 | Status NativeThreadNetBSD::SetHardwareBreakpoint(lldb::addr_t addr, |
291 | size_t size) { |
292 | assert(m_state == eStateStopped); |
293 | Status error = RemoveHardwareBreakpoint(addr); |
294 | if (error.Fail()) |
295 | return error; |
296 | |
297 | uint32_t bp_index = GetRegisterContext().SetHardwareBreakpoint(addr, size); |
298 | |
299 | if (bp_index == LLDB_INVALID_INDEX32) |
300 | return Status("Setting hardware breakpoint failed." ); |
301 | |
302 | m_hw_break_index_map.insert(x: {addr, bp_index}); |
303 | return Status(); |
304 | } |
305 | |
306 | Status NativeThreadNetBSD::RemoveHardwareBreakpoint(lldb::addr_t addr) { |
307 | auto bp = m_hw_break_index_map.find(x: addr); |
308 | if (bp == m_hw_break_index_map.end()) |
309 | return Status(); |
310 | |
311 | uint32_t bp_index = bp->second; |
312 | if (GetRegisterContext().ClearHardwareBreakpoint(hw_idx: bp_index)) { |
313 | m_hw_break_index_map.erase(position: bp); |
314 | return Status(); |
315 | } |
316 | |
317 | return Status("Clearing hardware breakpoint failed." ); |
318 | } |
319 | |
320 | llvm::Error |
321 | NativeThreadNetBSD::CopyWatchpointsFrom(NativeThreadNetBSD &source) { |
322 | llvm::Error s = GetRegisterContext().CopyHardwareWatchpointsFrom( |
323 | source&: source.GetRegisterContext()); |
324 | if (!s) { |
325 | m_watchpoint_index_map = source.m_watchpoint_index_map; |
326 | m_hw_break_index_map = source.m_hw_break_index_map; |
327 | } |
328 | return s; |
329 | } |
330 | |