2016-01-06 15:12:03 -05:00
//===-- 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"
2016-07-23 16:50:09 -04:00
# include "SingleStepCheck.h"
2016-01-06 15:12:03 -05:00
# include "lldb/Core/Log.h"
# include "lldb/Core/State.h"
# include "lldb/Host/HostNativeThread.h"
2016-07-23 16:50:09 -04:00
# include "lldb/Host/linux/Ptrace.h"
2016-01-06 15:12:03 -05:00
# 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) \
2016-08-17 15:37:50 -04:00
syscall ( __NR_tgkill , static_cast < : : pid_t > ( pid ) , static_cast < : : pid_t > ( tid ) , sig )
2016-01-06 15:12:03 -05:00
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. " ) ;
}
2016-07-23 16:50:09 -04:00
Error
NativeThreadLinux : : Resume ( uint32_t signo )
2016-01-06 15:12:03 -05:00
{
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 ( ) )
{
2016-07-23 16:50:09 -04:00
NativeProcessLinux & process = GetProcess ( ) ;
const auto & watchpoint_map = process . GetWatchpointMap ( ) ;
GetRegisterContext ( ) - > ClearAllHardwareWatchpoints ( ) ;
for ( const auto & pair : watchpoint_map )
2016-01-06 15:12:03 -05:00
{
2016-07-23 16:50:09 -04:00
const auto & wp = pair . second ;
SetWatchpoint ( wp . m_addr , wp . m_size , wp . m_watch_flags , wp . m_hardware ) ;
2016-01-06 15:12:03 -05:00
}
}
2016-07-23 16:50:09 -04:00
intptr_t data = 0 ;
if ( signo ! = LLDB_INVALID_SIGNAL_NUMBER )
data = signo ;
return NativeProcessLinux : : PtraceWrapper ( PTRACE_CONT , GetID ( ) , nullptr , reinterpret_cast < void * > ( data ) ) ;
2016-01-06 15:12:03 -05:00
}
void
2016-07-23 16:50:09 -04:00
NativeThreadLinux : : MaybePrepareSingleStepWorkaround ( )
{
if ( ! SingleStepWorkaroundNeeded ( ) )
return ;
Log * log ( GetLogIfAllCategoriesSet ( LIBLLDB_LOG_THREAD ) ) ;
if ( sched_getaffinity ( static_cast < : : pid_t > ( m_tid ) , sizeof m_original_cpu_set , & m_original_cpu_set ) ! = 0 )
{
// This should really not fail. But, just in case...
if ( log )
{
Error error ( errno , eErrorTypePOSIX ) ;
log - > Printf ( " NativeThreadLinux::%s Unable to get cpu affinity for thread % " PRIx64 " : %s " , __FUNCTION__ ,
m_tid , error . AsCString ( ) ) ;
}
return ;
}
cpu_set_t set ;
CPU_ZERO ( & set ) ;
CPU_SET ( 0 , & set ) ;
if ( sched_setaffinity ( static_cast < : : pid_t > ( m_tid ) , sizeof set , & set ) ! = 0 & & log )
{
// This may fail in very locked down systems, if the thread is not allowed to run on
// cpu 0. If that happens, only thing we can do is it log it and continue...
Error error ( errno , eErrorTypePOSIX ) ;
log - > Printf ( " NativeThreadLinux::%s Unable to set cpu affinity for thread % " PRIx64 " : %s " , __FUNCTION__ , m_tid ,
error . AsCString ( ) ) ;
}
}
void
NativeThreadLinux : : MaybeCleanupSingleStepWorkaround ( )
{
if ( ! SingleStepWorkaroundNeeded ( ) )
return ;
if ( sched_setaffinity ( static_cast < : : pid_t > ( m_tid ) , sizeof m_original_cpu_set , & m_original_cpu_set ) ! = 0 )
{
Error error ( errno , eErrorTypePOSIX ) ;
Log * log ( GetLogIfAllCategoriesSet ( LIBLLDB_LOG_THREAD ) ) ;
log - > Printf ( " NativeThreadLinux::%s Unable to reset cpu affinity for thread % " PRIx64 " : %s " , __FUNCTION__ ,
m_tid , error . AsCString ( ) ) ;
}
}
Error
NativeThreadLinux : : SingleStep ( uint32_t signo )
2016-01-06 15:12:03 -05:00
{
const StateType new_state = StateType : : eStateStepping ;
MaybeLogStateChange ( new_state ) ;
m_state = new_state ;
m_stop_info . reason = StopReason : : eStopReasonNone ;
2016-07-23 16:50:09 -04:00
MaybePrepareSingleStepWorkaround ( ) ;
intptr_t data = 0 ;
if ( signo ! = LLDB_INVALID_SIGNAL_NUMBER )
data = signo ;
// 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 ) ) ;
2016-01-06 15:12:03 -05:00
}
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 ) ;
2016-07-23 16:50:09 -04:00
SetStopped ( ) ;
2016-01-06 15:12:03 -05:00
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 ;
}
2016-07-23 16:50:09 -04:00
void
NativeThreadLinux : : SetStopped ( )
{
if ( m_state = = StateType : : eStateStepping )
MaybeCleanupSingleStepWorkaround ( ) ;
const StateType new_state = StateType : : eStateStopped ;
MaybeLogStateChange ( new_state ) ;
m_state = new_state ;
m_stop_description . clear ( ) ;
}
2016-01-06 15:12:03 -05:00
void
NativeThreadLinux : : SetStoppedByExec ( )
{
Log * log ( GetLogIfAllCategoriesSet ( LIBLLDB_LOG_THREAD ) ) ;
if ( log )
log - > Printf ( " NativeThreadLinux::%s() " , __FUNCTION__ ) ;
2016-07-23 16:50:09 -04:00
SetStopped ( ) ;
2016-01-06 15:12:03 -05:00
m_stop_info . reason = StopReason : : eStopReasonExec ;
m_stop_info . details . signal . signo = SIGSTOP ;
}
void
NativeThreadLinux : : SetStoppedByBreakpoint ( )
{
2016-07-23 16:50:09 -04:00
SetStopped ( ) ;
2016-01-06 15:12:03 -05:00
m_stop_info . reason = StopReason : : eStopReasonBreakpoint ;
m_stop_info . details . signal . signo = SIGTRAP ;
m_stop_description . clear ( ) ;
}
void
NativeThreadLinux : : SetStoppedByWatchpoint ( uint32_t wp_index )
{
2016-07-23 16:50:09 -04:00
SetStopped ( ) ;
2016-01-06 15:12:03 -05:00
lldbassert ( wp_index ! = LLDB_INVALID_INDEX32 & &
" wp_index cannot be invalid " ) ;
std : : ostringstream ostr ;
ostr < < GetRegisterContext ( ) - > GetWatchpointAddress ( wp_index ) < < " " ;
ostr < < wp_index ;
/*
* MIPS : Last 3 bits 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 ( )
{
2016-07-23 16:50:09 -04:00
SetStopped ( ) ;
2016-01-06 15:12:03 -05:00
m_stop_info . reason = StopReason : : eStopReasonTrace ;
m_stop_info . details . signal . signo = SIGTRAP ;
}
void
NativeThreadLinux : : SetStoppedWithNoReason ( )
{
2016-07-23 16:50:09 -04:00
SetStopped ( ) ;
2016-01-06 15:12:03 -05:00
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 ) ) ;
2016-07-23 16:50:09 -04:00
NativeProcessLinux & process = GetProcess ( ) ;
2016-01-06 15:12:03 -05:00
2016-07-23 16:50:09 -04:00
lldb : : pid_t pid = process . GetID ( ) ;
2016-01-06 15:12:03 -05:00
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 ) ) ;
}
2016-07-23 16:50:09 -04:00
NativeProcessLinux &
NativeThreadLinux : : GetProcess ( )
{
auto process_sp = std : : static_pointer_cast < NativeProcessLinux > ( NativeThreadProtocol : : GetProcess ( ) ) ;
assert ( process_sp ) ;
return * process_sp ;
}