| //===-- 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_stop_info(), |
| m_reg_context_up( |
| NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD( |
| process.GetArchitecture(), *this)), |
| m_stop_description() {} |
| |
| Status NativeThreadFreeBSD::Resume() { |
| Status ret = NativeProcessFreeBSD::PtraceWrapper(PT_RESUME, GetID()); |
| if (!ret.Success()) |
| return ret; |
| ret = NativeProcessFreeBSD::PtraceWrapper(PT_CLEARSTEP, GetID()); |
| 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(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); |
| LLDB_LOG(log, "tid = {0} in called with signal {1}", GetID(), signo); |
| |
| SetStopped(); |
| |
| m_stop_info.reason = StopReason::eStopReasonSignal; |
| m_stop_info.details.signal.signo = signo; |
| |
| m_stop_description.clear(); |
| if (info) { |
| switch (signo) { |
| case SIGSEGV: |
| case SIGBUS: |
| case SIGFPE: |
| case SIGILL: |
| const auto reason = GetCrashReason(*info); |
| m_stop_description = GetCrashReasonString(reason, *info); |
| break; |
| } |
| } |
| } |
| |
| void NativeThreadFreeBSD::SetStoppedByBreakpoint() { |
| SetStopped(); |
| m_stop_info.reason = StopReason::eStopReasonBreakpoint; |
| m_stop_info.details.signal.signo = SIGTRAP; |
| } |
| |
| void NativeThreadFreeBSD::SetStoppedByTrace() { |
| SetStopped(); |
| m_stop_info.reason = StopReason::eStopReasonTrace; |
| m_stop_info.details.signal.signo = SIGTRAP; |
| } |
| |
| void NativeThreadFreeBSD::SetStoppedByExec() { |
| SetStopped(); |
| m_stop_info.reason = StopReason::eStopReasonExec; |
| m_stop_info.details.signal.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.details.signal.signo = SIGTRAP; |
| } |
| |
| void NativeThreadFreeBSD::SetStoppedWithNoReason() { |
| SetStopped(); |
| |
| m_stop_info.reason = StopReason::eStopReasonNone; |
| m_stop_info.details.signal.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; |
| m_stop_info.reason = StopReason::eStopReasonNone; |
| } |
| |
| void NativeThreadFreeBSD::SetStepping() { |
| m_state = StateType::eStateStepping; |
| m_stop_info.reason = StopReason::eStopReasonNone; |
| } |
| |
| std::string NativeThreadFreeBSD::GetName() { |
| Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_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(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_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("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("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("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("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("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; |
| } |