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 0000000000000..cbf82885e23a6 --- /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)); +}  | 
