2014-07-01 05:05:18 +08: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>
2015-02-03 09:51:47 +08:00
# include <sstream>
2014-07-01 05:05:18 +08:00
# include "NativeProcessLinux.h"
2014-07-01 07:51:35 +08:00
# include "NativeRegisterContextLinux_x86_64.h"
2014-07-01 05:05:18 +08:00
# include "lldb/Core/Log.h"
# include "lldb/Core/State.h"
# include "lldb/Host/Host.h"
2014-08-21 00:42:51 +08:00
# include "lldb/Host/HostInfo.h"
2014-09-10 04:54:56 +08:00
# include "lldb/Host/HostNativeThread.h"
2014-07-01 05:05:18 +08:00
# include "lldb/lldb-enumerations.h"
# include "lldb/lldb-private-log.h"
2014-09-10 04:54:56 +08:00
# include "llvm/ADT/SmallString.h"
2015-02-03 09:51:25 +08:00
# include "Plugins/Process/POSIX/CrashReason.h"
2014-08-30 00:01:35 +08:00
# include "Plugins/Process/Utility/RegisterContextLinux_arm64.h"
2014-07-01 05:05:18 +08:00
# 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 :
2015-02-03 09:50:46 +08:00
log . Printf ( " %s: %s signal 0x%02 " PRIx32 , __FUNCTION__ , header , stop_info . details . signal . signo ) ;
2014-07-01 05:05:18 +08:00
return ;
case eStopReasonException :
2015-02-03 09:50:46 +08:00
log . Printf ( " %s: %s exception type 0x%02 " PRIx64 , __FUNCTION__ , header , stop_info . details . exception . type ) ;
2014-08-28 23:46:54 +08:00
return ;
case eStopReasonExec :
log . Printf ( " %s: %s exec, stopping signal 0x% " PRIx32 , __FUNCTION__ , header , stop_info . details . signal . signo ) ;
2014-07-01 05:05:18 +08:00
return ;
default :
2014-08-28 23:46:54 +08:00
log . Printf ( " %s: %s invalid stop reason % " PRIu32 , __FUNCTION__ , header , static_cast < uint32_t > ( stop_info . reason ) ) ;
2014-07-01 05:05:18 +08:00
}
}
}
NativeThreadLinux : : NativeThreadLinux ( NativeProcessLinux * process , lldb : : tid_t tid ) :
NativeThreadProtocol ( process , tid ) ,
m_state ( StateType : : eStateInvalid ) ,
m_stop_info ( ) ,
2015-02-03 09:51:25 +08:00
m_reg_context_sp ( ) ,
m_stop_description ( )
2014-07-01 05:05:18 +08:00
{
}
2014-09-13 06:51:49 +08:00
std : : string
2014-07-01 05:05:18 +08:00
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 ());
2014-09-10 04:54:56 +08:00
llvm : : SmallString < 32 > thread_name ;
HostNativeThread : : GetName ( GetID ( ) , thread_name ) ;
return thread_name . c_str ( ) ;
2014-07-01 05:05:18 +08:00
}
lldb : : StateType
NativeThreadLinux : : GetState ( )
{
return m_state ;
}
bool
2015-02-03 09:51:25 +08:00
NativeThreadLinux : : GetStopReason ( ThreadStopInfo & stop_info , std : : string & description )
2014-07-01 05:05:18 +08:00
{
Log * log ( GetLogIfAllCategoriesSet ( LIBLLDB_LOG_THREAD ) ) ;
2015-02-03 09:51:25 +08:00
description . clear ( ) ;
2014-07-01 05:05:18 +08:00
switch ( m_state )
{
case eStateStopped :
case eStateCrashed :
case eStateExited :
case eStateSuspended :
case eStateUnloaded :
if ( log )
2014-08-28 23:46:54 +08:00
LogThreadStopInfo ( * log , m_stop_info , " m_stop_info in thread: " ) ;
2014-07-01 05:05:18 +08:00
stop_info = m_stop_info ;
2015-02-03 09:51:47 +08:00
switch ( m_stop_info . reason )
{
case StopReason : : eStopReasonException :
case StopReason : : eStopReasonBreakpoint :
case StopReason : : eStopReasonWatchpoint :
description = m_stop_description ;
default :
break ;
}
2014-07-01 05:05:18 +08:00
if ( log )
2014-08-28 23:46:54 +08:00
LogThreadStopInfo ( * log , stop_info , " returned stop_info: " ) ;
2015-02-03 09:51:25 +08:00
2014-07-01 05:05:18 +08:00
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 ;
}
2014-09-16 14:34:29 +08:00
llvm_unreachable ( " unhandled StateType! " ) ;
2014-07-01 05:05:18 +08:00
}
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 ( ) )
{
2014-08-30 00:01:35 +08:00
case llvm : : Triple : : aarch64 :
assert ( ( HostInfo : : GetArchitecture ( ) . GetAddressByteSize ( ) = = 8 ) & & " Register setting path assumes this is a 64-bit host " ) ;
reg_interface = static_cast < RegisterInfoInterface * > ( new RegisterContextLinux_arm64 ( target_arch ) ) ;
break ;
2014-07-01 05:05:18 +08:00
case llvm : : Triple : : x86 :
case llvm : : Triple : : x86_64 :
2014-08-21 00:42:51 +08:00
if ( HostInfo : : GetArchitecture ( ) . GetAddressByteSize ( ) = = 4 )
2014-07-01 05:05:18 +08:00
{
// 32-bit hosts run with a RegisterContextLinux_i386 context.
reg_interface = static_cast < RegisterInfoInterface * > ( new RegisterContextLinux_i386 ( target_arch ) ) ;
}
else
{
2014-08-21 00:42:51 +08:00
assert ( ( HostInfo : : GetArchitecture ( ) . GetAddressByteSize ( ) = = 8 ) & &
" Register setting path assumes this is a 64-bit host " ) ;
2014-07-01 05:05:18 +08:00
// 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
2015-02-16 18:34:30 +08:00
2014-07-01 05:05:18 +08:00
case llvm : : Triple : : x86 :
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 )
{
2015-02-03 09:51:47 +08:00
if ( ! hardware )
return Error ( " not implemented " ) ;
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 ( ) ;
2014-07-01 05:05:18 +08:00
}
Error
NativeThreadLinux : : RemoveWatchpoint ( lldb : : addr_t addr )
{
2015-02-03 09:51:47 +08:00
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. " ) ;
2014-07-01 05:05:18 +08:00
}
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 ;
2015-02-03 09:51:25 +08:00
m_stop_description . clear ( ) ;
2015-02-03 09:51:47 +08:00
// 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 ( ) )
{
2015-02-20 01:58:04 +08:00
const auto process_sp = GetProcess ( ) ;
if ( process_sp )
2015-02-03 09:51:47 +08:00
{
2015-02-20 01:58:04 +08:00
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 ) ;
}
2015-02-03 09:51:47 +08:00
}
}
2014-07-01 05:05:18 +08:00
}
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 )
2015-02-03 09:50:51 +08:00
log - > Printf ( " NativeThreadLinux::%s called with signal 0x%02 " PRIx32 , __FUNCTION__ , signo ) ;
2014-07-01 05:05:18 +08:00
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 ;
}
2014-09-12 07:29:14 +08:00
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 ;
}
2014-08-28 23:46:54 +08:00
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 ;
}
2014-07-01 05:05:18 +08:00
void
NativeThreadLinux : : SetStoppedByBreakpoint ( )
{
const StateType new_state = StateType : : eStateStopped ;
MaybeLogStateChange ( new_state ) ;
m_state = new_state ;
2015-02-03 09:51:25 +08:00
m_stop_info . reason = StopReason : : eStopReasonBreakpoint ;
2014-07-01 05:05:18 +08:00
m_stop_info . details . signal . signo = SIGTRAP ;
2015-02-03 09:51:47 +08:00
m_stop_description . clear ( ) ;
}
void
NativeThreadLinux : : SetStoppedByWatchpoint ( )
{
const StateType new_state = StateType : : eStateStopped ;
MaybeLogStateChange ( new_state ) ;
m_state = new_state ;
m_stop_info . reason = StopReason : : eStopReasonWatchpoint ;
m_stop_info . details . signal . signo = SIGTRAP ;
NativeRegisterContextLinux_x86_64 * reg_ctx =
reinterpret_cast < NativeRegisterContextLinux_x86_64 * > ( GetRegisterContext ( ) . get ( ) ) ;
const uint32_t num_hw_watchpoints =
reg_ctx - > NumSupportedHardwareWatchpoints ( ) ;
m_stop_description . clear ( ) ;
for ( uint32_t wp_index = 0 ; wp_index < num_hw_watchpoints ; + + wp_index )
if ( reg_ctx - > IsWatchpointHit ( wp_index ) . Success ( ) )
{
std : : ostringstream ostr ;
ostr < < reg_ctx - > GetWatchpointAddress ( wp_index ) < < " " < < wp_index ;
m_stop_description = ostr . str ( ) ;
return ;
}
Log * log ( GetLogIfAllCategoriesSet ( LIBLLDB_LOG_THREAD ) ) ;
if ( log )
{
NativeProcessProtocolSP m_process_sp = m_process_wp . lock ( ) ;
lldb : : pid_t pid = m_process_sp ? m_process_sp - > GetID ( ) : LLDB_INVALID_PROCESS_ID ;
log - > Printf ( " NativeThreadLinux: thread (pid=% " PRIu64 " , tid=% " PRIu64 " ) "
" stopped by a watchpoint, but failed to find it " ,
pid , GetID ( ) ) ;
}
2014-07-01 05:05:18 +08:00
}
bool
NativeThreadLinux : : IsStoppedAtBreakpoint ( )
{
2015-02-03 09:51:47 +08:00
return GetState ( ) = = StateType : : eStateStopped & &
m_stop_info . reason = = StopReason : : eStopReasonBreakpoint ;
}
2014-07-01 05:05:18 +08:00
2015-02-03 09:51:47 +08:00
bool
NativeThreadLinux : : IsStoppedAtWatchpoint ( )
{
return GetState ( ) = = StateType : : eStateStopped & &
m_stop_info . reason = = StopReason : : eStopReasonWatchpoint ;
2014-07-01 05:05:18 +08:00
}
void
2015-02-03 09:51:25 +08:00
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 : : SetCrashedWithException ( const siginfo_t & info )
2014-07-01 05:05:18 +08:00
{
const StateType new_state = StateType : : eStateCrashed ;
MaybeLogStateChange ( new_state ) ;
m_state = new_state ;
m_stop_info . reason = StopReason : : eStopReasonException ;
2015-02-03 09:51:25 +08:00
m_stop_info . details . signal . signo = info . si_signo ;
2014-07-01 05:05:18 +08:00
2015-02-03 09:51:25 +08:00
const auto reason = GetCrashReason ( info ) ;
m_stop_description = GetCrashReasonString ( reason , reinterpret_cast < lldb : : addr_t > ( info . si_addr ) ) ;
}
2014-07-01 05:05:18 +08:00
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 ) ) ;
}