| //===-- NativeThreadFreeBSD.cpp -------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "NativeThreadFreeBSD.h" | |
| #include "NativeRegisterContextFreeBSD.h" | |
| #include "NativeProcessFreeBSD.h" | |
| #include "Plugins/Process/POSIX/CrashReason.h" | |
| #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" | |
| #include "lldb/Utility/LLDBAssert.h" | |
| #include "lldb/Utility/RegisterValue.h" | |
| #include "lldb/Utility/State.h" | |
| #include "llvm/Support/Errno.h" | |
| // clang-format off | |
| #include <sys/types.h> | |
| #include <sys/ptrace.h> | |
| #include <sys/sysctl.h> | |
| #include <sys/user.h> | |
| // clang-format on | |
| #include <sstream> | |
| #include <vector> | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| using namespace lldb_private::process_freebsd; | |
| NativeThreadFreeBSD::NativeThreadFreeBSD(NativeProcessFreeBSD &process, | |
| lldb::tid_t tid) | |
| : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid), | |
| m_reg_context_up( | |
| NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD( | |
| process.GetArchitecture(), *this)) {} | |
| Status NativeThreadFreeBSD::Resume() { | |
| Status ret = NativeProcessFreeBSD::PtraceWrapper(PT_RESUME, GetID()); | |
| if (!ret.Success()) | |
| return ret; | |
| ret = NativeProcessFreeBSD::PtraceWrapper(PT_CLEARSTEP, GetID()); | |
| // we can get EINVAL if the architecture in question does not support | |
| // hardware single-stepping -- that's fine, we have nothing to clear | |
| // then | |
| if (ret.GetError() == EINVAL) | |
| ret.Clear(); | |
| if (ret.Success()) | |
| SetRunning(); | |
| return ret; | |
| } | |
| Status NativeThreadFreeBSD::SingleStep() { | |
| Status ret = NativeProcessFreeBSD::PtraceWrapper(PT_RESUME, GetID()); | |
| if (!ret.Success()) | |
| return ret; | |
| ret = NativeProcessFreeBSD::PtraceWrapper(PT_SETSTEP, GetID()); | |
| if (ret.Success()) | |
| SetStepping(); | |
| return ret; | |
| } | |
| Status NativeThreadFreeBSD::Suspend() { | |
| Status ret = NativeProcessFreeBSD::PtraceWrapper(PT_SUSPEND, GetID()); | |
| if (ret.Success()) | |
| SetStopped(); | |
| return ret; | |
| } | |
| void NativeThreadFreeBSD::SetStoppedBySignal(uint32_t signo, | |
| const siginfo_t *info) { | |
| Log *log = GetLog(POSIXLog::Thread); | |
| LLDB_LOG(log, "tid = {0} in called with signal {1}", GetID(), signo); | |
| SetStopped(); | |
| m_stop_info.reason = StopReason::eStopReasonSignal; | |
| m_stop_info.signo = signo; | |
| m_stop_description.clear(); | |
| if (info) { | |
| switch (signo) { | |
| case SIGSEGV: | |
| case SIGBUS: | |
| case SIGFPE: | |
| case SIGILL: | |
| m_stop_description = GetCrashReasonString(*info); | |
| break; | |
| } | |
| } | |
| } | |
| void NativeThreadFreeBSD::SetStoppedByBreakpoint() { | |
| SetStopped(); | |
| m_stop_info.reason = StopReason::eStopReasonBreakpoint; | |
| m_stop_info.signo = SIGTRAP; | |
| } | |
| void NativeThreadFreeBSD::SetStoppedByTrace() { | |
| SetStopped(); | |
| m_stop_info.reason = StopReason::eStopReasonTrace; | |
| m_stop_info.signo = SIGTRAP; | |
| } | |
| void NativeThreadFreeBSD::SetStoppedByExec() { | |
| SetStopped(); | |
| m_stop_info.reason = StopReason::eStopReasonExec; | |
| m_stop_info.signo = SIGTRAP; | |
| } | |
| void NativeThreadFreeBSD::SetStoppedByWatchpoint(uint32_t wp_index) { | |
| lldbassert(wp_index != LLDB_INVALID_INDEX32 && "wp_index cannot be invalid"); | |
| std::ostringstream ostr; | |
| ostr << GetRegisterContext().GetWatchpointAddress(wp_index) << " "; | |
| ostr << wp_index; | |
| ostr << " " << GetRegisterContext().GetWatchpointHitAddress(wp_index); | |
| SetStopped(); | |
| m_stop_description = ostr.str(); | |
| m_stop_info.reason = StopReason::eStopReasonWatchpoint; | |
| m_stop_info.signo = SIGTRAP; | |
| } | |
| void NativeThreadFreeBSD::SetStoppedByFork(lldb::pid_t child_pid, | |
| lldb::tid_t child_tid) { | |
| SetStopped(); | |
| m_stop_info.reason = StopReason::eStopReasonFork; | |
| m_stop_info.signo = SIGTRAP; | |
| m_stop_info.details.fork.child_pid = child_pid; | |
| m_stop_info.details.fork.child_tid = child_tid; | |
| } | |
| void NativeThreadFreeBSD::SetStoppedByVFork(lldb::pid_t child_pid, | |
| lldb::tid_t child_tid) { | |
| SetStopped(); | |
| m_stop_info.reason = StopReason::eStopReasonVFork; | |
| m_stop_info.signo = SIGTRAP; | |
| m_stop_info.details.fork.child_pid = child_pid; | |
| m_stop_info.details.fork.child_tid = child_tid; | |
| } | |
| void NativeThreadFreeBSD::SetStoppedByVForkDone() { | |
| SetStopped(); | |
| m_stop_info.reason = StopReason::eStopReasonVForkDone; | |
| m_stop_info.signo = SIGTRAP; | |
| } | |
| void NativeThreadFreeBSD::SetStoppedWithNoReason() { | |
| SetStopped(); | |
| m_stop_info.reason = StopReason::eStopReasonNone; | |
| m_stop_info.signo = 0; | |
| } | |
| void NativeThreadFreeBSD::SetStopped() { | |
| const StateType new_state = StateType::eStateStopped; | |
| m_state = new_state; | |
| m_stop_description.clear(); | |
| } | |
| void NativeThreadFreeBSD::SetRunning() { | |
| m_state = StateType::eStateRunning; | |
| ClearStopInfo(); | |
| } | |
| void NativeThreadFreeBSD::SetStepping() { | |
| m_state = StateType::eStateStepping; | |
| ClearStopInfo(); | |
| } | |
| std::string NativeThreadFreeBSD::GetName() { | |
| Log *log = GetLog(POSIXLog::Thread); | |
| std::vector<struct kinfo_proc> kp; | |
| int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_INC_THREAD, | |
| static_cast<int>(GetProcess().GetID())}; | |
| while (1) { | |
| size_t len = kp.size() * sizeof(struct kinfo_proc); | |
| void *ptr = len == 0 ? nullptr : kp.data(); | |
| int error = ::sysctl(mib, 4, ptr, &len, nullptr, 0); | |
| if (ptr == nullptr || (error != 0 && errno == ENOMEM)) { | |
| kp.resize(len / sizeof(struct kinfo_proc)); | |
| continue; | |
| } | |
| if (error != 0) { | |
| len = 0; | |
| LLDB_LOG(log, "tid = {0} in state {1} failed to get thread name: {2}", | |
| GetID(), m_state, strerror(errno)); | |
| } | |
| kp.resize(len / sizeof(struct kinfo_proc)); | |
| break; | |
| } | |
| for (auto &procinfo : kp) { | |
| if (procinfo.ki_tid == static_cast<lwpid_t>(GetID())) | |
| return procinfo.ki_tdname; | |
| } | |
| return ""; | |
| } | |
| lldb::StateType NativeThreadFreeBSD::GetState() { return m_state; } | |
| bool NativeThreadFreeBSD::GetStopReason(ThreadStopInfo &stop_info, | |
| std::string &description) { | |
| Log *log = GetLog(POSIXLog::Thread); | |
| description.clear(); | |
| switch (m_state) { | |
| case eStateStopped: | |
| case eStateCrashed: | |
| case eStateExited: | |
| case eStateSuspended: | |
| case eStateUnloaded: | |
| stop_info = m_stop_info; | |
| description = m_stop_description; | |
| return true; | |
| case eStateInvalid: | |
| case eStateConnected: | |
| case eStateAttaching: | |
| case eStateLaunching: | |
| case eStateRunning: | |
| case eStateStepping: | |
| case eStateDetached: | |
| LLDB_LOG(log, "tid = {0} in state {1} cannot answer stop reason", GetID(), | |
| StateAsCString(m_state)); | |
| return false; | |
| } | |
| llvm_unreachable("unhandled StateType!"); | |
| } | |
| NativeRegisterContextFreeBSD &NativeThreadFreeBSD::GetRegisterContext() { | |
| assert(m_reg_context_up); | |
| return *m_reg_context_up; | |
| } | |
| Status NativeThreadFreeBSD::SetWatchpoint(lldb::addr_t addr, size_t size, | |
| uint32_t watch_flags, bool hardware) { | |
| assert(m_state == eStateStopped); | |
| if (!hardware) | |
| return Status::FromErrorString("not implemented"); | |
| Status error = RemoveWatchpoint(addr); | |
| if (error.Fail()) | |
| return error; | |
| uint32_t wp_index = | |
| GetRegisterContext().SetHardwareWatchpoint(addr, size, watch_flags); | |
| if (wp_index == LLDB_INVALID_INDEX32) | |
| return Status::FromErrorString("Setting hardware watchpoint failed."); | |
| m_watchpoint_index_map.insert({addr, wp_index}); | |
| return Status(); | |
| } | |
| Status NativeThreadFreeBSD::RemoveWatchpoint(lldb::addr_t addr) { | |
| auto wp = m_watchpoint_index_map.find(addr); | |
| if (wp == m_watchpoint_index_map.end()) | |
| return Status(); | |
| uint32_t wp_index = wp->second; | |
| m_watchpoint_index_map.erase(wp); | |
| if (GetRegisterContext().ClearHardwareWatchpoint(wp_index)) | |
| return Status(); | |
| return Status::FromErrorString("Clearing hardware watchpoint failed."); | |
| } | |
| Status NativeThreadFreeBSD::SetHardwareBreakpoint(lldb::addr_t addr, | |
| size_t size) { | |
| assert(m_state == eStateStopped); | |
| Status error = RemoveHardwareBreakpoint(addr); | |
| if (error.Fail()) | |
| return error; | |
| uint32_t bp_index = GetRegisterContext().SetHardwareBreakpoint(addr, size); | |
| if (bp_index == LLDB_INVALID_INDEX32) | |
| return Status::FromErrorString("Setting hardware breakpoint failed."); | |
| m_hw_break_index_map.insert({addr, bp_index}); | |
| return Status(); | |
| } | |
| Status NativeThreadFreeBSD::RemoveHardwareBreakpoint(lldb::addr_t addr) { | |
| auto bp = m_hw_break_index_map.find(addr); | |
| if (bp == m_hw_break_index_map.end()) | |
| return Status(); | |
| uint32_t bp_index = bp->second; | |
| if (GetRegisterContext().ClearHardwareBreakpoint(bp_index)) { | |
| m_hw_break_index_map.erase(bp); | |
| return Status(); | |
| } | |
| return Status::FromErrorString("Clearing hardware breakpoint failed."); | |
| } | |
| llvm::Error | |
| NativeThreadFreeBSD::CopyWatchpointsFrom(NativeThreadFreeBSD &source) { | |
| llvm::Error s = GetRegisterContext().CopyHardwareWatchpointsFrom( | |
| source.GetRegisterContext()); | |
| if (!s) { | |
| m_watchpoint_index_map = source.m_watchpoint_index_map; | |
| m_hw_break_index_map = source.m_hw_break_index_map; | |
| } | |
| return s; | |
| } | |
| NativeProcessFreeBSD &NativeThreadFreeBSD::GetProcess() { | |
| return static_cast<NativeProcessFreeBSD &>(m_process); | |
| } | |
| llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> | |
| NativeThreadFreeBSD::GetSiginfo() const { | |
| Log *log = GetLog(POSIXLog::Process); | |
| struct ptrace_lwpinfo info; | |
| const auto siginfo_err = NativeProcessFreeBSD::PtraceWrapper( | |
| PT_LWPINFO, GetID(), &info, sizeof(info)); | |
| if (siginfo_err.Fail()) { | |
| LLDB_LOG(log, "PT_LWPINFO failed {0}", siginfo_err); | |
| return siginfo_err.ToError(); | |
| } | |
| if (info.pl_event != PL_EVENT_SIGNAL) | |
| return llvm::createStringError(llvm::inconvertibleErrorCode(), | |
| "Thread not signaled"); | |
| if (!(info.pl_flags & PL_FLAG_SI)) | |
| return llvm::createStringError(llvm::inconvertibleErrorCode(), | |
| "No siginfo for thread"); | |
| return llvm::MemoryBuffer::getMemBufferCopy( | |
| llvm::StringRef(reinterpret_cast<const char *>(&info.pl_siginfo), | |
| sizeof(info.pl_siginfo))); | |
| } |