1 | //===-- NativeThreadFreeBSD.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 "NativeThreadFreeBSD.h" |
10 | #include "NativeRegisterContextFreeBSD.h" |
11 | |
12 | #include "NativeProcessFreeBSD.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 | #include <sys/sysctl.h> |
25 | #include <sys/user.h> |
26 | // clang-format on |
27 | |
28 | #include <sstream> |
29 | #include <vector> |
30 | |
31 | using namespace lldb; |
32 | using namespace lldb_private; |
33 | using namespace lldb_private::process_freebsd; |
34 | |
35 | NativeThreadFreeBSD::NativeThreadFreeBSD(NativeProcessFreeBSD &process, |
36 | lldb::tid_t tid) |
37 | : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid), |
38 | m_stop_info(), |
39 | m_reg_context_up( |
40 | NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD( |
41 | target_arch: process.GetArchitecture(), native_thread&: *this)), |
42 | m_stop_description() {} |
43 | |
44 | Status NativeThreadFreeBSD::Resume() { |
45 | Status ret = NativeProcessFreeBSD::PtraceWrapper(req: PT_RESUME, pid: GetID()); |
46 | if (!ret.Success()) |
47 | return ret; |
48 | ret = NativeProcessFreeBSD::PtraceWrapper(req: PT_CLEARSTEP, pid: GetID()); |
49 | // we can get EINVAL if the architecture in question does not support |
50 | // hardware single-stepping -- that's fine, we have nothing to clear |
51 | // then |
52 | if (ret.GetError() == EINVAL) |
53 | ret.Clear(); |
54 | if (ret.Success()) |
55 | SetRunning(); |
56 | return ret; |
57 | } |
58 | |
59 | Status NativeThreadFreeBSD::SingleStep() { |
60 | Status ret = NativeProcessFreeBSD::PtraceWrapper(req: PT_RESUME, pid: GetID()); |
61 | if (!ret.Success()) |
62 | return ret; |
63 | ret = NativeProcessFreeBSD::PtraceWrapper(req: PT_SETSTEP, pid: GetID()); |
64 | if (ret.Success()) |
65 | SetStepping(); |
66 | return ret; |
67 | } |
68 | |
69 | Status NativeThreadFreeBSD::Suspend() { |
70 | Status ret = NativeProcessFreeBSD::PtraceWrapper(req: PT_SUSPEND, pid: GetID()); |
71 | if (ret.Success()) |
72 | SetStopped(); |
73 | return ret; |
74 | } |
75 | |
76 | void NativeThreadFreeBSD::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 NativeThreadFreeBSD::SetStoppedByBreakpoint() { |
100 | SetStopped(); |
101 | m_stop_info.reason = StopReason::eStopReasonBreakpoint; |
102 | m_stop_info.signo = SIGTRAP; |
103 | } |
104 | |
105 | void NativeThreadFreeBSD::SetStoppedByTrace() { |
106 | SetStopped(); |
107 | m_stop_info.reason = StopReason::eStopReasonTrace; |
108 | m_stop_info.signo = SIGTRAP; |
109 | } |
110 | |
111 | void NativeThreadFreeBSD::SetStoppedByExec() { |
112 | SetStopped(); |
113 | m_stop_info.reason = StopReason::eStopReasonExec; |
114 | m_stop_info.signo = SIGTRAP; |
115 | } |
116 | |
117 | void NativeThreadFreeBSD::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 NativeThreadFreeBSD::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 NativeThreadFreeBSD::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 NativeThreadFreeBSD::SetStoppedByVForkDone() { |
153 | SetStopped(); |
154 | |
155 | m_stop_info.reason = StopReason::eStopReasonVForkDone; |
156 | m_stop_info.signo = SIGTRAP; |
157 | } |
158 | |
159 | void NativeThreadFreeBSD::SetStoppedWithNoReason() { |
160 | SetStopped(); |
161 | |
162 | m_stop_info.reason = StopReason::eStopReasonNone; |
163 | m_stop_info.signo = 0; |
164 | } |
165 | |
166 | void NativeThreadFreeBSD::SetStopped() { |
167 | const StateType new_state = StateType::eStateStopped; |
168 | m_state = new_state; |
169 | m_stop_description.clear(); |
170 | } |
171 | |
172 | void NativeThreadFreeBSD::SetRunning() { |
173 | m_state = StateType::eStateRunning; |
174 | m_stop_info.reason = StopReason::eStopReasonNone; |
175 | } |
176 | |
177 | void NativeThreadFreeBSD::SetStepping() { |
178 | m_state = StateType::eStateStepping; |
179 | m_stop_info.reason = StopReason::eStopReasonNone; |
180 | } |
181 | |
182 | std::string NativeThreadFreeBSD::GetName() { |
183 | Log *log = GetLog(mask: POSIXLog::Thread); |
184 | |
185 | std::vector<struct kinfo_proc> kp; |
186 | int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_INC_THREAD, |
187 | static_cast<int>(GetProcess().GetID())}; |
188 | |
189 | while (1) { |
190 | size_t len = kp.size() * sizeof(struct kinfo_proc); |
191 | void *ptr = len == 0 ? nullptr : kp.data(); |
192 | int error = ::sysctl(mib, 4, ptr, &len, nullptr, 0); |
193 | if (ptr == nullptr || (error != 0 && errno == ENOMEM)) { |
194 | kp.resize(len / sizeof(struct kinfo_proc)); |
195 | continue; |
196 | } |
197 | if (error != 0) { |
198 | len = 0; |
199 | LLDB_LOG(log, "tid = {0} in state {1} failed to get thread name: {2}" , |
200 | GetID(), m_state, strerror(errno)); |
201 | } |
202 | kp.resize(len / sizeof(struct kinfo_proc)); |
203 | break; |
204 | } |
205 | |
206 | for (auto &procinfo : kp) { |
207 | if (procinfo.ki_tid == static_cast<lwpid_t>(GetID())) |
208 | return procinfo.ki_tdname; |
209 | } |
210 | |
211 | return "" ; |
212 | } |
213 | |
214 | lldb::StateType NativeThreadFreeBSD::GetState() { return m_state; } |
215 | |
216 | bool NativeThreadFreeBSD::GetStopReason(ThreadStopInfo &stop_info, |
217 | std::string &description) { |
218 | Log *log = GetLog(mask: POSIXLog::Thread); |
219 | description.clear(); |
220 | |
221 | switch (m_state) { |
222 | case eStateStopped: |
223 | case eStateCrashed: |
224 | case eStateExited: |
225 | case eStateSuspended: |
226 | case eStateUnloaded: |
227 | stop_info = m_stop_info; |
228 | description = m_stop_description; |
229 | |
230 | return true; |
231 | |
232 | case eStateInvalid: |
233 | case eStateConnected: |
234 | case eStateAttaching: |
235 | case eStateLaunching: |
236 | case eStateRunning: |
237 | case eStateStepping: |
238 | case eStateDetached: |
239 | LLDB_LOG(log, "tid = {0} in state {1} cannot answer stop reason" , GetID(), |
240 | StateAsCString(m_state)); |
241 | return false; |
242 | } |
243 | llvm_unreachable("unhandled StateType!" ); |
244 | } |
245 | |
246 | NativeRegisterContextFreeBSD &NativeThreadFreeBSD::GetRegisterContext() { |
247 | assert(m_reg_context_up); |
248 | return *m_reg_context_up; |
249 | } |
250 | |
251 | Status NativeThreadFreeBSD::SetWatchpoint(lldb::addr_t addr, size_t size, |
252 | uint32_t watch_flags, bool hardware) { |
253 | assert(m_state == eStateStopped); |
254 | if (!hardware) |
255 | return Status("not implemented" ); |
256 | Status error = RemoveWatchpoint(addr); |
257 | if (error.Fail()) |
258 | return error; |
259 | uint32_t wp_index = |
260 | GetRegisterContext().SetHardwareWatchpoint(addr, size, watch_flags); |
261 | if (wp_index == LLDB_INVALID_INDEX32) |
262 | return Status("Setting hardware watchpoint failed." ); |
263 | m_watchpoint_index_map.insert(x: {addr, wp_index}); |
264 | return Status(); |
265 | } |
266 | |
267 | Status NativeThreadFreeBSD::RemoveWatchpoint(lldb::addr_t addr) { |
268 | auto wp = m_watchpoint_index_map.find(x: addr); |
269 | if (wp == m_watchpoint_index_map.end()) |
270 | return Status(); |
271 | uint32_t wp_index = wp->second; |
272 | m_watchpoint_index_map.erase(position: wp); |
273 | if (GetRegisterContext().ClearHardwareWatchpoint(hw_index: wp_index)) |
274 | return Status(); |
275 | return Status("Clearing hardware watchpoint failed." ); |
276 | } |
277 | |
278 | Status NativeThreadFreeBSD::SetHardwareBreakpoint(lldb::addr_t addr, |
279 | size_t size) { |
280 | assert(m_state == eStateStopped); |
281 | Status error = RemoveHardwareBreakpoint(addr); |
282 | if (error.Fail()) |
283 | return error; |
284 | |
285 | uint32_t bp_index = GetRegisterContext().SetHardwareBreakpoint(addr, size); |
286 | |
287 | if (bp_index == LLDB_INVALID_INDEX32) |
288 | return Status("Setting hardware breakpoint failed." ); |
289 | |
290 | m_hw_break_index_map.insert(x: {addr, bp_index}); |
291 | return Status(); |
292 | } |
293 | |
294 | Status NativeThreadFreeBSD::RemoveHardwareBreakpoint(lldb::addr_t addr) { |
295 | auto bp = m_hw_break_index_map.find(x: addr); |
296 | if (bp == m_hw_break_index_map.end()) |
297 | return Status(); |
298 | |
299 | uint32_t bp_index = bp->second; |
300 | if (GetRegisterContext().ClearHardwareBreakpoint(hw_idx: bp_index)) { |
301 | m_hw_break_index_map.erase(position: bp); |
302 | return Status(); |
303 | } |
304 | |
305 | return Status("Clearing hardware breakpoint failed." ); |
306 | } |
307 | |
308 | llvm::Error |
309 | NativeThreadFreeBSD::CopyWatchpointsFrom(NativeThreadFreeBSD &source) { |
310 | llvm::Error s = GetRegisterContext().CopyHardwareWatchpointsFrom( |
311 | source&: source.GetRegisterContext()); |
312 | if (!s) { |
313 | m_watchpoint_index_map = source.m_watchpoint_index_map; |
314 | m_hw_break_index_map = source.m_hw_break_index_map; |
315 | } |
316 | return s; |
317 | } |
318 | |
319 | llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> |
320 | NativeThreadFreeBSD::GetSiginfo() const { |
321 | Log *log = GetLog(mask: POSIXLog::Process); |
322 | |
323 | struct ptrace_lwpinfo info; |
324 | const auto siginfo_err = NativeProcessFreeBSD::PtraceWrapper( |
325 | PT_LWPINFO, GetID(), &info, sizeof(info)); |
326 | if (siginfo_err.Fail()) { |
327 | LLDB_LOG(log, "PT_LWPINFO failed {0}" , siginfo_err); |
328 | return siginfo_err.ToError(); |
329 | } |
330 | |
331 | if (info.pl_event != PL_EVENT_SIGNAL) |
332 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
333 | Msg: "Thread not signaled" ); |
334 | if (!(info.pl_flags & PL_FLAG_SI)) |
335 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
336 | Msg: "No siginfo for thread" ); |
337 | |
338 | return llvm::MemoryBuffer::getMemBufferCopy( |
339 | InputData: llvm::StringRef(reinterpret_cast<const char *>(&info.pl_siginfo), |
340 | sizeof(info.pl_siginfo))); |
341 | } |
342 | |