diff options
author | Todd Fiala <todd.fiala@gmail.com> | 2014-06-30 21:05:18 +0000 |
---|---|---|
committer | Todd Fiala <todd.fiala@gmail.com> | 2014-06-30 21:05:18 +0000 |
commit | af245d115bfab4eb0ca1b969b2aae6f84eb90706 (patch) | |
tree | b38feb475a7ec16b112ce528e3a976b8111976fd /lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp | |
parent | b201cf8883a8669c78f20533a2c47b6a92142a78 (diff) | |
download | bcm5719-llvm-af245d115bfab4eb0ca1b969b2aae6f84eb90706.tar.gz bcm5719-llvm-af245d115bfab4eb0ca1b969b2aae6f84eb90706.zip |
Add lldb-gdbserver support for Linux x86_64.
This change brings in lldb-gdbserver (llgs) specifically for Linux x86_64.
(More architectures coming soon).
Not every debugserver option is covered yet. Currently
the lldb-gdbserver command line can start unattached,
start attached to a pid (process-name attach not supported yet),
or accept lldb attaching and launching a process or connecting
by process id.
The history of this large change can be found here:
https://github.com/tfiala/lldb/tree/dev-tfiala-native-protocol-linux-x86_64
Until mid/late April, I was not sharing the work and continued
to rebase it off of head (developed via id tfiala@google.com). I switched over to
user todd.fiala@gmail.com in the middle, and once I went to github, I did
merges rather than rebasing so I could share with others.
llvm-svn: 212069
Diffstat (limited to 'lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp')
-rw-r--r-- | lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp new file mode 100644 index 00000000000..d9cd18357e7 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp @@ -0,0 +1,363 @@ +//===-- 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 "NativeProcessLinux.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Host/Host.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-private-log.h" +#include "Plugins/Process/Utility/NativeRegisterContextLinux_x86_64.h" +#include "Plugins/Process/Utility/RegisterContextLinux_i386.h" +#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" +#include "Plugins/Process/Utility/RegisterInfoInterface.h" + +using namespace lldb; +using namespace lldb_private; + +namespace +{ + void LogThreadStopInfo (Log &log, const ThreadStopInfo &stop_info, const char *const header) + { + switch (stop_info.reason) + { + case eStopReasonSignal: + log.Printf ("%s: %s: signal 0x%" PRIx32, __FUNCTION__, header, stop_info.details.signal.signo); + return; + case eStopReasonException: + log.Printf ("%s: %s: exception type 0x%" PRIx64, __FUNCTION__, header, stop_info.details.exception.type); + 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 () +{ +} + +const char * +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 ()); + return Host::GetThreadName (process_sp->GetID (), GetID ()).c_str (); +} + +lldb::StateType +NativeThreadLinux::GetState () +{ + return m_state; +} + + +bool +NativeThreadLinux::GetStopReason (ThreadStopInfo &stop_info) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + 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; + 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: + default: + if (log) + { + log->Printf ("NativeThreadLinux::%s tid %" PRIu64 " in state %s cannot answer stop reason", + __FUNCTION__, GetID (), StateAsCString (m_state)); + } + return false; + } +} + +lldb_private::NativeRegisterContextSP +NativeThreadLinux::GetRegisterContext () +{ + // Return the register context if we already created it. + if (m_reg_context_sp) + return m_reg_context_sp; + + // First select the appropriate RegisterInfoInterface. + RegisterInfoInterface *reg_interface = nullptr; + 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 (); + + switch (target_arch.GetTriple().getOS()) + { + case llvm::Triple::Linux: + switch (target_arch.GetMachine()) + { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + if (Host::GetArchitecture().GetAddressByteSize() == 4) + { + // 32-bit hosts run with a RegisterContextLinux_i386 context. + reg_interface = static_cast<RegisterInfoInterface*>(new RegisterContextLinux_i386(target_arch)); + } + else + { + assert((Host::GetArchitecture ().GetAddressByteSize () == 8) && "Register setting path assumes this is a 64-bit host"); + // X86_64 hosts know how to work with 64-bit and 32-bit EXEs using the x86_64 register context. + reg_interface = static_cast<RegisterInfoInterface*> (new RegisterContextLinux_x86_64 (target_arch)); + } + break; + default: + break; + } + break; + default: + break; + } + + assert(reg_interface && "OS or CPU not supported!"); + if (!reg_interface) + return NativeRegisterContextSP (); + + // Now create the register context. + switch (target_arch.GetMachine()) + { +#if 0 + case llvm::Triple::mips64: + { + RegisterContextPOSIXProcessMonitor_mips64 *reg_ctx = new RegisterContextPOSIXProcessMonitor_mips64(*this, 0, reg_interface); + m_posix_thread = reg_ctx; + m_reg_context_sp.reset(reg_ctx); + break; + } +#endif +#if 0 + case llvm::Triple::x86: +#endif + case llvm::Triple::x86_64: + { + const uint32_t concrete_frame_idx = 0; + m_reg_context_sp.reset (new NativeRegisterContextLinux_x86_64(*this, concrete_frame_idx, reg_interface)); + break; + } + default: + break; + } + + return m_reg_context_sp; +} + +Error +NativeThreadLinux::SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) +{ + // TODO implement + return Error ("not implemented"); +} + +Error +NativeThreadLinux::RemoveWatchpoint (lldb::addr_t addr) +{ + // TODO implement + return Error ("not implemented"); +} + +void +NativeThreadLinux::SetLaunching () +{ + const StateType new_state = StateType::eStateLaunching; + MaybeLogStateChange (new_state); + m_state = new_state; + + // Also mark it as stopped since launching temporarily stops the newly created thread + // in the ptrace machinery. + m_stop_info.reason = StopReason::eStopReasonSignal; + m_stop_info.details.signal.signo = SIGSTOP; +} + + +void +NativeThreadLinux::SetRunning () +{ + const StateType new_state = StateType::eStateRunning; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonNone; +} + +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) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("NativeThreadLinux::%s called with signal 0x%" 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; +} + +void +NativeThreadLinux::SetStoppedByBreakpoint () +{ + 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 = SIGTRAP; +} + +bool +NativeThreadLinux::IsStoppedAtBreakpoint () +{ + // Are we stopped? If not, this can't be a breakpoint. + if (GetState () != StateType::eStateStopped) + return false; + + // Was the stop reason a signal with signal number SIGTRAP? If not, not a breakpoint. + return (m_stop_info.reason == StopReason::eStopReasonSignal) && + (m_stop_info.details.signal.signo == SIGTRAP); +} + +void +NativeThreadLinux::SetCrashedWithException (uint64_t exception_type, lldb::addr_t exception_addr) +{ + const StateType new_state = StateType::eStateCrashed; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonException; + m_stop_info.details.exception.type = exception_type; + m_stop_info.details.exception.data_count = 1; + m_stop_info.details.exception.data[0] = exception_addr; +} + + +void +NativeThreadLinux::SetSuspended () +{ + const StateType new_state = StateType::eStateSuspended; + MaybeLogStateChange (new_state); + m_state = new_state; + + // FIXME what makes sense here? Do we need a suspended StopReason? + m_stop_info.reason = StopReason::eStopReasonNone; +} + +void +NativeThreadLinux::SetExited () +{ + const StateType new_state = StateType::eStateExited; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonThreadExiting; +} + +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)); +} + +static +uint32_t MaybeTranslateHostSignoToGdbSigno (uint32_t host_signo) +{ + switch (host_signo) + { + case SIGSEGV: return eGdbSignalBadAccess; + case SIGILL: return eGdbSignalBadInstruction; + case SIGFPE: return eGdbSignalArithmetic; + // NOTE: debugserver sends SIGTRAP through unmodified. Do the same here. + // case SIGTRAP: return eGdbSignalBreakpoint; + + // Nothing for eGdbSignalSoftware (0x95). + // Nothing for eGdbSignalEmulation (0x94). + + default: + // No translations. + return host_signo; + } +} + +uint32_t +NativeThreadLinux::TranslateStopInfoToGdbSignal (const ThreadStopInfo &stop_info) const +{ + switch (stop_info.reason) + { + case eStopReasonSignal: + return MaybeTranslateHostSignoToGdbSigno (stop_info.details.signal.signo); + break; + + case eStopReasonException: + // FIXME verify how we handle exception type. + return MaybeTranslateHostSignoToGdbSigno (static_cast<uint32_t> (stop_info.details.exception.type)); + break; + + default: + assert (0 && "unexpected stop_info.reason found"); + return 0; + } +} + |