diff options
Diffstat (limited to 'source/Plugins/Process/Linux/NativeThreadLinux.cpp')
-rw-r--r-- | source/Plugins/Process/Linux/NativeThreadLinux.cpp | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/source/Plugins/Process/Linux/NativeThreadLinux.cpp new file mode 100644 index 000000000000..cbf82885e23a --- /dev/null +++ b/source/Plugins/Process/Linux/NativeThreadLinux.cpp @@ -0,0 +1,440 @@ +//===-- NativeThreadLinux.cpp --------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NativeThreadLinux.h" + +#include <signal.h> +#include <sstream> + +#include "NativeProcessLinux.h" +#include "NativeRegisterContextLinux.h" + +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Host/HostNativeThread.h" +#include "lldb/Utility/LLDBAssert.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(SYS_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_sp (), + m_stop_description () +{ +} + +std::string +NativeThreadLinux::GetName() +{ + NativeProcessProtocolSP process_sp = m_process_wp.lock (); + if (!process_sp) + return "<unknown: no process>"; + + // const NativeProcessLinux *const process = reinterpret_cast<NativeProcessLinux*> (process_sp->get ()); + llvm::SmallString<32> thread_name; + HostNativeThread::GetName(GetID(), thread_name); + return thread_name.c_str(); +} + +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) + { + log->Printf ("NativeThreadLinux::%s tid %" PRIu64 " in state %s cannot answer stop reason", + __FUNCTION__, GetID (), StateAsCString (m_state)); + } + return false; + } + llvm_unreachable("unhandled StateType!"); +} + +NativeRegisterContextSP +NativeThreadLinux::GetRegisterContext () +{ + // Return the register context if we already created it. + if (m_reg_context_sp) + return m_reg_context_sp; + + NativeProcessProtocolSP m_process_sp = m_process_wp.lock (); + if (!m_process_sp) + return NativeRegisterContextSP (); + + ArchSpec target_arch; + if (!m_process_sp->GetArchitecture (target_arch)) + return NativeRegisterContextSP (); + + const uint32_t concrete_frame_idx = 0; + m_reg_context_sp.reset (NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(target_arch, + *this, + concrete_frame_idx)); + + return m_reg_context_sp; +} + +Error +NativeThreadLinux::SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) +{ + if (!hardware) + return Error ("not implemented"); + if (m_state == eStateLaunching) + return Error (); + Error error = RemoveWatchpoint(addr); + if (error.Fail()) return error; + NativeRegisterContextSP reg_ctx = GetRegisterContext (); + uint32_t wp_index = + reg_ctx->SetHardwareWatchpoint (addr, size, watch_flags); + if (wp_index == LLDB_INVALID_INDEX32) + return Error ("Setting hardware watchpoint failed."); + m_watchpoint_index_map.insert({addr, wp_index}); + return Error (); +} + +Error +NativeThreadLinux::RemoveWatchpoint (lldb::addr_t addr) +{ + auto wp = m_watchpoint_index_map.find(addr); + if (wp == m_watchpoint_index_map.end()) + return Error (); + uint32_t wp_index = wp->second; + m_watchpoint_index_map.erase(wp); + if (GetRegisterContext()->ClearHardwareWatchpoint(wp_index)) + return Error (); + return Error ("Clearing hardware watchpoint failed."); +} + +void +NativeThreadLinux::SetRunning () +{ + 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()) + { + const auto process_sp = GetProcess(); + if (process_sp) + { + const auto &watchpoint_map = process_sp->GetWatchpointMap(); + if (watchpoint_map.empty()) return; + GetRegisterContext()->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); + } + } + } +} + +void +NativeThreadLinux::SetStepping () +{ + const StateType new_state = StateType::eStateStepping; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonNone; +} + +void +NativeThreadLinux::SetStoppedBySignal(uint32_t signo, const siginfo_t *info) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("NativeThreadLinux::%s called with signal 0x%02" PRIx32, __FUNCTION__, signo); + + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + 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, reinterpret_cast<uintptr_t>(info->si_addr)); + 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::SetStoppedByExec () +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("NativeThreadLinux::%s()", __FUNCTION__); + + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonExec; + m_stop_info.details.signal.signo = SIGSTOP; +} + +void +NativeThreadLinux::SetStoppedByBreakpoint () +{ + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonBreakpoint; + m_stop_info.details.signal.signo = SIGTRAP; + m_stop_description.clear(); +} + +void +NativeThreadLinux::SetStoppedByWatchpoint (uint32_t wp_index) +{ + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + m_stop_description.clear (); + + lldbassert(wp_index != LLDB_INVALID_INDEX32 && + "wp_index cannot be invalid"); + + std::ostringstream ostr; + ostr << GetRegisterContext()->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 << " " << GetRegisterContext()->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 () +{ + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonTrace; + m_stop_info.details.signal.signo = SIGTRAP; +} + +void +NativeThreadLinux::SetStoppedWithNoReason () +{ + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + 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; +} + +Error +NativeThreadLinux::RequestStop () +{ + Log* log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + + const auto process_sp = GetProcess(); + if (! process_sp) + return Error("Process is null."); + + lldb::pid_t pid = process_sp->GetID(); + lldb::tid_t tid = GetID(); + + if (log) + log->Printf ("NativeThreadLinux::%s requesting thread stop(pid: %" PRIu64 ", tid: %" PRIu64 ")", __FUNCTION__, pid, tid); + + Error err; + errno = 0; + if (::tgkill (pid, tid, SIGSTOP) != 0) + { + err.SetErrorToErrno (); + if (log) + log->Printf ("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; + + NativeProcessProtocolSP m_process_sp = m_process_wp.lock (); + lldb::pid_t pid = m_process_sp ? m_process_sp->GetID () : LLDB_INVALID_PROCESS_ID; + + // Log it. + log->Printf ("NativeThreadLinux: thread (pid=%" PRIu64 ", tid=%" PRIu64 ") changing from state %s to %s", pid, GetID (), StateAsCString (old_state), StateAsCString (new_state)); +} |