diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp')
-rw-r--r-- | gnu/llvm/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/gnu/llvm/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp new file mode 100644 index 00000000000..b2730e60129 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp @@ -0,0 +1,455 @@ +//===-- NativeThreadLinux.cpp --------------------------------- -*- C++ -*-===// +// +// 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 "NativeThreadLinux.h" + +#include <signal.h> +#include <sstream> + +#include "NativeProcessLinux.h" +#include "NativeRegisterContextLinux.h" +#include "SingleStepCheck.h" + +#include "lldb/Host/HostNativeThread.h" +#include "lldb/Host/linux/Ptrace.h" +#include "lldb/Host/linux/Support.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/lldb-enumerations.h" + +#include "llvm/ADT/SmallString.h" + +#include "Plugins/Process/POSIX/CrashReason.h" + +#include <sys/syscall.h> +// Try to define a macro to encapsulate the tgkill syscall +#define tgkill(pid, tid, sig) \ + syscall(__NR_tgkill, static_cast<::pid_t>(pid), static_cast<::pid_t>(tid), \ + sig) + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_linux; + +namespace { +void LogThreadStopInfo(Log &log, const ThreadStopInfo &stop_info, + const char *const header) { + switch (stop_info.reason) { + case eStopReasonNone: + log.Printf("%s: %s no stop reason", __FUNCTION__, header); + return; + case eStopReasonTrace: + log.Printf("%s: %s trace, stopping signal 0x%" PRIx32, __FUNCTION__, header, + stop_info.details.signal.signo); + return; + case eStopReasonBreakpoint: + log.Printf("%s: %s breakpoint, stopping signal 0x%" PRIx32, __FUNCTION__, + header, stop_info.details.signal.signo); + return; + case eStopReasonWatchpoint: + log.Printf("%s: %s watchpoint, stopping signal 0x%" PRIx32, __FUNCTION__, + header, stop_info.details.signal.signo); + return; + case eStopReasonSignal: + log.Printf("%s: %s signal 0x%02" PRIx32, __FUNCTION__, header, + stop_info.details.signal.signo); + return; + case eStopReasonException: + log.Printf("%s: %s exception type 0x%02" PRIx64, __FUNCTION__, header, + stop_info.details.exception.type); + return; + case eStopReasonExec: + log.Printf("%s: %s exec, stopping signal 0x%" PRIx32, __FUNCTION__, header, + stop_info.details.signal.signo); + return; + case eStopReasonPlanComplete: + log.Printf("%s: %s plan complete", __FUNCTION__, header); + return; + case eStopReasonThreadExiting: + log.Printf("%s: %s thread exiting", __FUNCTION__, header); + return; + case eStopReasonInstrumentation: + log.Printf("%s: %s instrumentation", __FUNCTION__, header); + return; + default: + log.Printf("%s: %s invalid stop reason %" PRIu32, __FUNCTION__, header, + static_cast<uint32_t>(stop_info.reason)); + } +} +} + +NativeThreadLinux::NativeThreadLinux(NativeProcessLinux &process, + lldb::tid_t tid) + : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid), + m_stop_info(), + m_reg_context_up( + NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux( + process.GetArchitecture(), *this)), + m_stop_description() {} + +std::string NativeThreadLinux::GetName() { + NativeProcessLinux &process = GetProcess(); + + auto BufferOrError = getProcFile(process.GetID(), GetID(), "comm"); + if (!BufferOrError) + return ""; + return BufferOrError.get()->getBuffer().rtrim('\n'); +} + +lldb::StateType NativeThreadLinux::GetState() { return m_state; } + +bool NativeThreadLinux::GetStopReason(ThreadStopInfo &stop_info, + std::string &description) { + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + + description.clear(); + + switch (m_state) { + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + case eStateUnloaded: + if (log) + LogThreadStopInfo(*log, m_stop_info, "m_stop_info in thread:"); + stop_info = m_stop_info; + description = m_stop_description; + if (log) + LogThreadStopInfo(*log, stop_info, "returned stop_info:"); + + return true; + + case eStateInvalid: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + if (log) { + LLDB_LOGF(log, + "NativeThreadLinux::%s tid %" PRIu64 + " in state %s cannot answer stop reason", + __FUNCTION__, GetID(), StateAsCString(m_state)); + } + return false; + } + llvm_unreachable("unhandled StateType!"); +} + +Status NativeThreadLinux::SetWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags, bool hardware) { + if (!hardware) + return Status("not implemented"); + if (m_state == eStateLaunching) + return Status(); + Status error = RemoveWatchpoint(addr); + if (error.Fail()) + return error; + uint32_t wp_index = + m_reg_context_up->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 NativeThreadLinux::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 (m_reg_context_up->ClearHardwareWatchpoint(wp_index)) + return Status(); + return Status("Clearing hardware watchpoint failed."); +} + +Status NativeThreadLinux::SetHardwareBreakpoint(lldb::addr_t addr, + size_t size) { + if (m_state == eStateLaunching) + return Status(); + + Status error = RemoveHardwareBreakpoint(addr); + if (error.Fail()) + return error; + + uint32_t bp_index = m_reg_context_up->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 NativeThreadLinux::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 (m_reg_context_up->ClearHardwareBreakpoint(bp_index)) { + m_hw_break_index_map.erase(bp); + return Status(); + } + + return Status("Clearing hardware breakpoint failed."); +} + +Status NativeThreadLinux::Resume(uint32_t signo) { + const StateType new_state = StateType::eStateRunning; + MaybeLogStateChange(new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonNone; + m_stop_description.clear(); + + // If watchpoints have been set, but none on this thread, then this is a new + // thread. So set all existing watchpoints. + if (m_watchpoint_index_map.empty()) { + NativeProcessLinux &process = GetProcess(); + + const auto &watchpoint_map = process.GetWatchpointMap(); + m_reg_context_up->ClearAllHardwareWatchpoints(); + for (const auto &pair : watchpoint_map) { + const auto &wp = pair.second; + SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, wp.m_hardware); + } + } + + // Set all active hardware breakpoint on all threads. + if (m_hw_break_index_map.empty()) { + NativeProcessLinux &process = GetProcess(); + + const auto &hw_breakpoint_map = process.GetHardwareBreakpointMap(); + m_reg_context_up->ClearAllHardwareBreakpoints(); + for (const auto &pair : hw_breakpoint_map) { + const auto &bp = pair.second; + SetHardwareBreakpoint(bp.m_addr, bp.m_size); + } + } + + intptr_t data = 0; + + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + data = signo; + + // Before thread resumes, clear any cached register data structures + GetRegisterContext().InvalidateAllRegisters(); + + return NativeProcessLinux::PtraceWrapper(PTRACE_CONT, GetID(), nullptr, + reinterpret_cast<void *>(data)); +} + +Status NativeThreadLinux::SingleStep(uint32_t signo) { + const StateType new_state = StateType::eStateStepping; + MaybeLogStateChange(new_state); + m_state = new_state; + m_stop_info.reason = StopReason::eStopReasonNone; + + if(!m_step_workaround) { + // If we already hava a workaround inplace, don't reset it. Otherwise, the + // destructor of the existing instance will run after the new instance has + // fetched the cpu mask, and the thread will end up with the wrong mask. + m_step_workaround = SingleStepWorkaround::Get(m_tid); + } + + intptr_t data = 0; + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + data = signo; + + // Before thread resumes, clear any cached register data structures + GetRegisterContext().InvalidateAllRegisters(); + + // If hardware single-stepping is not supported, we just do a continue. The + // breakpoint on the next instruction has been setup in + // NativeProcessLinux::Resume. + return NativeProcessLinux::PtraceWrapper( + GetProcess().SupportHardwareSingleStepping() ? PTRACE_SINGLESTEP + : PTRACE_CONT, + m_tid, nullptr, reinterpret_cast<void *>(data)); +} + +void NativeThreadLinux::SetStoppedBySignal(uint32_t signo, + const siginfo_t *info) { + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + LLDB_LOGF(log, "NativeThreadLinux::%s called with signal 0x%02" PRIx32, + __FUNCTION__, 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: + // In case of MIPS64 target, SI_KERNEL is generated for invalid 64bit + // address. + const auto reason = + (info->si_signo == SIGBUS && info->si_code == SI_KERNEL) + ? CrashReason::eInvalidAddress + : GetCrashReason(*info); + m_stop_description = GetCrashReasonString(reason, *info); + break; + } + } +} + +bool NativeThreadLinux::IsStopped(int *signo) { + if (!StateIsStoppedState(m_state, false)) + return false; + + // If we are stopped by a signal, return the signo. + if (signo && m_state == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonSignal) { + *signo = m_stop_info.details.signal.signo; + } + + // Regardless, we are stopped. + return true; +} + +void NativeThreadLinux::SetStopped() { + if (m_state == StateType::eStateStepping) + m_step_workaround.reset(); + + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange(new_state); + m_state = new_state; + m_stop_description.clear(); +} + +void NativeThreadLinux::SetStoppedByExec() { + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + LLDB_LOGF(log, "NativeThreadLinux::%s()", __FUNCTION__); + + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonExec; + m_stop_info.details.signal.signo = SIGSTOP; +} + +void NativeThreadLinux::SetStoppedByBreakpoint() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonBreakpoint; + m_stop_info.details.signal.signo = SIGTRAP; + m_stop_description.clear(); +} + +void NativeThreadLinux::SetStoppedByWatchpoint(uint32_t wp_index) { + SetStopped(); + + lldbassert(wp_index != LLDB_INVALID_INDEX32 && "wp_index cannot be invalid"); + + std::ostringstream ostr; + ostr << m_reg_context_up->GetWatchpointAddress(wp_index) << " "; + ostr << wp_index; + + /* + * MIPS: Last 3bits of the watchpoint address are masked by the kernel. For + * example: + * 'n' is at 0x120010d00 and 'm' is 0x120010d04. When a watchpoint is set at + * 'm', then + * watch exception is generated even when 'n' is read/written. To handle this + * case, + * find the base address of the load/store instruction and append it in the + * stop-info + * packet. + */ + ostr << " " << m_reg_context_up->GetWatchpointHitAddress(wp_index); + + m_stop_description = ostr.str(); + + m_stop_info.reason = StopReason::eStopReasonWatchpoint; + m_stop_info.details.signal.signo = SIGTRAP; +} + +bool NativeThreadLinux::IsStoppedAtBreakpoint() { + return GetState() == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonBreakpoint; +} + +bool NativeThreadLinux::IsStoppedAtWatchpoint() { + return GetState() == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonWatchpoint; +} + +void NativeThreadLinux::SetStoppedByTrace() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonTrace; + m_stop_info.details.signal.signo = SIGTRAP; +} + +void NativeThreadLinux::SetStoppedWithNoReason() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonNone; + m_stop_info.details.signal.signo = 0; +} + +void NativeThreadLinux::SetExited() { + const StateType new_state = StateType::eStateExited; + MaybeLogStateChange(new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonThreadExiting; +} + +Status NativeThreadLinux::RequestStop() { + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + + NativeProcessLinux &process = GetProcess(); + + lldb::pid_t pid = process.GetID(); + lldb::tid_t tid = GetID(); + + LLDB_LOGF(log, + "NativeThreadLinux::%s requesting thread stop(pid: %" PRIu64 + ", tid: %" PRIu64 ")", + __FUNCTION__, pid, tid); + + Status err; + errno = 0; + if (::tgkill(pid, tid, SIGSTOP) != 0) { + err.SetErrorToErrno(); + LLDB_LOGF(log, + "NativeThreadLinux::%s tgkill(%" PRIu64 ", %" PRIu64 + ", SIGSTOP) failed: %s", + __FUNCTION__, pid, tid, err.AsCString()); + } + + return err; +} + +void NativeThreadLinux::MaybeLogStateChange(lldb::StateType new_state) { + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + // If we're not logging, we're done. + if (!log) + return; + + // If this is a state change to the same state, we're done. + lldb::StateType old_state = m_state; + if (new_state == old_state) + return; + + LLDB_LOG(log, "pid={0}, tid={1}: changing from state {2} to {3}", + m_process.GetID(), GetID(), old_state, new_state); +} + +NativeProcessLinux &NativeThreadLinux::GetProcess() { + return static_cast<NativeProcessLinux &>(m_process); +} |