2010-06-08 16:52:24 +00:00
//===-- ProcessGDBRemote.cpp ------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// C Includes
# include <errno.h>
# include <spawn.h>
# include <sys/types.h>
# include <sys/stat.h>
// C++ Includes
# include <algorithm>
# include <map>
// Other libraries and framework includes
# include "lldb/Breakpoint/WatchpointLocation.h"
2010-06-15 19:49:27 +00:00
# include "lldb/Interpreter/Args.h"
2010-06-08 16:52:24 +00:00
# include "lldb/Core/ArchSpec.h"
# include "lldb/Core/Debugger.h"
# include "lldb/Core/ConnectionFileDescriptor.h"
# include "lldb/Core/FileSpec.h"
# include "lldb/Core/InputReader.h"
# include "lldb/Core/Module.h"
# include "lldb/Core/PluginManager.h"
# include "lldb/Core/State.h"
# include "lldb/Core/StreamString.h"
# include "lldb/Core/Timer.h"
# include "lldb/Host/TimeValue.h"
# include "lldb/Symbol/ObjectFile.h"
# include "lldb/Target/DynamicLoader.h"
# include "lldb/Target/Target.h"
# include "lldb/Target/TargetList.h"
2010-06-09 21:28:42 +00:00
# include "lldb/Utility/PseudoTerminal.h"
2010-06-08 16:52:24 +00:00
// Project includes
# include "lldb/Host/Host.h"
2010-07-09 20:39:50 +00:00
# include "Utility/StringExtractorGDBRemote.h"
2010-06-08 16:52:24 +00:00
# include "GDBRemoteRegisterContext.h"
# include "ProcessGDBRemote.h"
# include "ProcessGDBRemoteLog.h"
# include "ThreadGDBRemote.h"
# include "MacOSXLibunwindCallbacks.h"
2010-08-04 01:40:35 +00:00
# include "StopInfoMachException.h"
2010-06-08 16:52:24 +00:00
# define DEBUGSERVER_BASENAME "debugserver"
using namespace lldb ;
using namespace lldb_private ;
static inline uint16_t
get_random_port ( )
{
return ( arc4random ( ) % ( UINT16_MAX - 1000u ) ) + 1000u ;
}
const char *
ProcessGDBRemote : : GetPluginNameStatic ( )
{
return " process.gdb-remote " ;
}
const char *
ProcessGDBRemote : : GetPluginDescriptionStatic ( )
{
return " GDB Remote protocol based debugging plug-in. " ;
}
void
ProcessGDBRemote : : Terminate ( )
{
PluginManager : : UnregisterPlugin ( ProcessGDBRemote : : CreateInstance ) ;
}
Process *
ProcessGDBRemote : : CreateInstance ( Target & target , Listener & listener )
{
return new ProcessGDBRemote ( target , listener ) ;
}
bool
ProcessGDBRemote : : CanDebug ( Target & target )
{
// For now we are just making sure the file exists for a given module
ModuleSP exe_module_sp ( target . GetExecutableModule ( ) ) ;
if ( exe_module_sp . get ( ) )
return exe_module_sp - > GetFileSpec ( ) . Exists ( ) ;
2010-08-09 23:31:02 +00:00
// However, if there is no executable module, we return true since we might be preparing to attach.
return true ;
2010-06-08 16:52:24 +00:00
}
//----------------------------------------------------------------------
// ProcessGDBRemote constructor
//----------------------------------------------------------------------
ProcessGDBRemote : : ProcessGDBRemote ( Target & target , Listener & listener ) :
Process ( target , listener ) ,
m_dynamic_loader_ap ( ) ,
m_flags ( 0 ) ,
m_stdio_communication ( " gdb-remote.stdio " ) ,
m_stdio_mutex ( Mutex : : eMutexTypeRecursive ) ,
m_stdout_data ( ) ,
2010-07-09 20:39:50 +00:00
m_byte_order ( eByteOrderHost ) ,
2010-06-08 16:52:24 +00:00
m_gdb_comm ( ) ,
m_debugserver_pid ( LLDB_INVALID_PROCESS_ID ) ,
2010-08-21 02:22:51 +00:00
m_debugserver_thread ( LLDB_INVALID_HOST_THREAD ) ,
2010-07-09 20:39:50 +00:00
m_last_stop_packet ( ) ,
2010-06-08 16:52:24 +00:00
m_register_info ( ) ,
m_async_broadcaster ( " lldb.process.gdb-remote.async-broadcaster " ) ,
m_async_thread ( LLDB_INVALID_HOST_THREAD ) ,
2010-07-09 20:39:50 +00:00
m_curr_tid ( LLDB_INVALID_THREAD_ID ) ,
m_curr_tid_run ( LLDB_INVALID_THREAD_ID ) ,
2010-06-08 16:52:24 +00:00
m_z0_supported ( 1 ) ,
m_continue_packet ( ) ,
m_dispatch_queue_offsets_addr ( LLDB_INVALID_ADDRESS ) ,
2010-07-09 20:39:50 +00:00
m_packet_timeout ( 1 ) ,
m_max_memory_size ( 512 ) ,
2010-06-08 16:52:24 +00:00
m_libunwind_target_type ( UNW_TARGET_UNSPECIFIED ) ,
m_libunwind_addr_space ( NULL ) ,
2010-08-09 23:31:02 +00:00
m_waiting_for_attach ( false ) ,
m_local_debugserver ( true )
2010-06-08 16:52:24 +00:00
{
}
//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
ProcessGDBRemote : : ~ ProcessGDBRemote ( )
{
2010-08-21 02:22:51 +00:00
if ( m_debugserver_thread ! = LLDB_INVALID_HOST_THREAD )
{
Host : : ThreadCancel ( m_debugserver_thread , NULL ) ;
thread_result_t thread_result ;
Host : : ThreadJoin ( m_debugserver_thread , & thread_result , NULL ) ;
m_debugserver_thread = LLDB_INVALID_HOST_THREAD ;
}
2010-06-08 16:52:24 +00:00
// m_mach_process.UnregisterNotificationCallbacks (this);
Clear ( ) ;
}
//----------------------------------------------------------------------
// PluginInterface
//----------------------------------------------------------------------
const char *
ProcessGDBRemote : : GetPluginName ( )
{
return " Process debugging plug-in that uses the GDB remote protocol " ;
}
const char *
ProcessGDBRemote : : GetShortPluginName ( )
{
return GetPluginNameStatic ( ) ;
}
uint32_t
ProcessGDBRemote : : GetPluginVersion ( )
{
return 1 ;
}
void
ProcessGDBRemote : : GetPluginCommandHelp ( const char * command , Stream * strm )
{
strm - > Printf ( " TODO: fill this in \n " ) ;
}
Error
ProcessGDBRemote : : ExecutePluginCommand ( Args & command , Stream * strm )
{
Error error ;
error . SetErrorString ( " No plug-in commands are currently supported. " ) ;
return error ;
}
Log *
ProcessGDBRemote : : EnablePluginLogging ( Stream * strm , Args & command )
{
return NULL ;
}
void
ProcessGDBRemote : : BuildDynamicRegisterInfo ( )
{
char register_info_command [ 64 ] ;
m_register_info . Clear ( ) ;
StringExtractorGDBRemote : : Type packet_type = StringExtractorGDBRemote : : eResponse ;
uint32_t reg_offset = 0 ;
uint32_t reg_num = 0 ;
for ( ; packet_type = = StringExtractorGDBRemote : : eResponse ; + + reg_num )
{
: : snprintf ( register_info_command , sizeof ( register_info_command ) , " qRegisterInfo%x " , reg_num ) ;
StringExtractorGDBRemote response ;
if ( m_gdb_comm . SendPacketAndWaitForResponse ( register_info_command , response , 2 , false ) )
{
packet_type = response . GetType ( ) ;
if ( packet_type = = StringExtractorGDBRemote : : eResponse )
{
std : : string name ;
std : : string value ;
ConstString reg_name ;
ConstString alt_name ;
ConstString set_name ;
RegisterInfo reg_info = { NULL , // Name
NULL , // Alt name
0 , // byte size
reg_offset , // offset
eEncodingUint , // encoding
eFormatHex , // formate
reg_num , // native register number
{
LLDB_INVALID_REGNUM , // GCC reg num
LLDB_INVALID_REGNUM , // DWARF reg num
LLDB_INVALID_REGNUM , // generic reg num
reg_num // GDB reg num
}
} ;
while ( response . GetNameColonValue ( name , value ) )
{
if ( name . compare ( " name " ) = = 0 )
{
reg_name . SetCString ( value . c_str ( ) ) ;
}
else if ( name . compare ( " alt-name " ) = = 0 )
{
alt_name . SetCString ( value . c_str ( ) ) ;
}
else if ( name . compare ( " bitsize " ) = = 0 )
{
reg_info . byte_size = Args : : StringToUInt32 ( value . c_str ( ) , 0 , 0 ) / CHAR_BIT ;
}
else if ( name . compare ( " offset " ) = = 0 )
{
uint32_t offset = Args : : StringToUInt32 ( value . c_str ( ) , UINT32_MAX , 0 ) ;
2010-06-11 23:44:18 +00:00
if ( reg_offset ! = offset )
2010-06-08 16:52:24 +00:00
{
reg_offset = offset ;
}
}
else if ( name . compare ( " encoding " ) = = 0 )
{
if ( value . compare ( " uint " ) = = 0 )
reg_info . encoding = eEncodingUint ;
else if ( value . compare ( " sint " ) = = 0 )
reg_info . encoding = eEncodingSint ;
else if ( value . compare ( " ieee754 " ) = = 0 )
reg_info . encoding = eEncodingIEEE754 ;
else if ( value . compare ( " vector " ) = = 0 )
reg_info . encoding = eEncodingVector ;
}
else if ( name . compare ( " format " ) = = 0 )
{
if ( value . compare ( " binary " ) = = 0 )
reg_info . format = eFormatBinary ;
else if ( value . compare ( " decimal " ) = = 0 )
reg_info . format = eFormatDecimal ;
else if ( value . compare ( " hex " ) = = 0 )
reg_info . format = eFormatHex ;
else if ( value . compare ( " float " ) = = 0 )
reg_info . format = eFormatFloat ;
else if ( value . compare ( " vector-sint8 " ) = = 0 )
reg_info . format = eFormatVectorOfSInt8 ;
else if ( value . compare ( " vector-uint8 " ) = = 0 )
reg_info . format = eFormatVectorOfUInt8 ;
else if ( value . compare ( " vector-sint16 " ) = = 0 )
reg_info . format = eFormatVectorOfSInt16 ;
else if ( value . compare ( " vector-uint16 " ) = = 0 )
reg_info . format = eFormatVectorOfUInt16 ;
else if ( value . compare ( " vector-sint32 " ) = = 0 )
reg_info . format = eFormatVectorOfSInt32 ;
else if ( value . compare ( " vector-uint32 " ) = = 0 )
reg_info . format = eFormatVectorOfUInt32 ;
else if ( value . compare ( " vector-float32 " ) = = 0 )
reg_info . format = eFormatVectorOfFloat32 ;
else if ( value . compare ( " vector-uint128 " ) = = 0 )
reg_info . format = eFormatVectorOfUInt128 ;
}
else if ( name . compare ( " set " ) = = 0 )
{
set_name . SetCString ( value . c_str ( ) ) ;
}
else if ( name . compare ( " gcc " ) = = 0 )
{
reg_info . kinds [ eRegisterKindGCC ] = Args : : StringToUInt32 ( value . c_str ( ) , LLDB_INVALID_REGNUM , 0 ) ;
}
else if ( name . compare ( " dwarf " ) = = 0 )
{
reg_info . kinds [ eRegisterKindDWARF ] = Args : : StringToUInt32 ( value . c_str ( ) , LLDB_INVALID_REGNUM , 0 ) ;
}
else if ( name . compare ( " generic " ) = = 0 )
{
if ( value . compare ( " pc " ) = = 0 )
reg_info . kinds [ eRegisterKindGeneric ] = LLDB_REGNUM_GENERIC_PC ;
else if ( value . compare ( " sp " ) = = 0 )
reg_info . kinds [ eRegisterKindGeneric ] = LLDB_REGNUM_GENERIC_SP ;
else if ( value . compare ( " fp " ) = = 0 )
reg_info . kinds [ eRegisterKindGeneric ] = LLDB_REGNUM_GENERIC_FP ;
else if ( value . compare ( " ra " ) = = 0 )
reg_info . kinds [ eRegisterKindGeneric ] = LLDB_REGNUM_GENERIC_RA ;
else if ( value . compare ( " flags " ) = = 0 )
reg_info . kinds [ eRegisterKindGeneric ] = LLDB_REGNUM_GENERIC_FLAGS ;
}
}
2010-06-11 23:44:18 +00:00
reg_info . byte_offset = reg_offset ;
2010-06-08 16:52:24 +00:00
assert ( reg_info . byte_size ! = 0 ) ;
reg_offset + = reg_info . byte_size ;
m_register_info . AddRegister ( reg_info , reg_name , alt_name , set_name ) ;
}
}
else
{
packet_type = StringExtractorGDBRemote : : eError ;
}
}
if ( reg_num = = 0 )
{
// We didn't get anything. See if we are debugging ARM and fill with
// a hard coded register set until we can get an updated debugserver
// down on the devices.
ArchSpec arm_arch ( " arm " ) ;
if ( GetTarget ( ) . GetArchitecture ( ) = = arm_arch )
m_register_info . HardcodeARMRegisters ( ) ;
}
m_register_info . Finalize ( ) ;
}
Error
ProcessGDBRemote : : WillLaunch ( Module * module )
{
return WillLaunchOrAttach ( ) ;
}
Error
ProcessGDBRemote : : WillAttach ( lldb : : pid_t pid )
{
return WillLaunchOrAttach ( ) ;
}
Error
ProcessGDBRemote : : WillAttach ( const char * process_name , bool wait_for_launch )
{
return WillLaunchOrAttach ( ) ;
}
Error
ProcessGDBRemote : : WillLaunchOrAttach ( )
{
Error error ;
// TODO: this is hardcoded for macosx right now. We need this to be more dynamic
m_dynamic_loader_ap . reset ( DynamicLoader : : FindPlugin ( this , " dynamic-loader.macosx-dyld " ) ) ;
if ( m_dynamic_loader_ap . get ( ) = = NULL )
error . SetErrorString ( " unable to find the dynamic loader named 'dynamic-loader.macosx-dyld' " ) ;
m_stdio_communication . Clear ( ) ;
return error ;
}
//----------------------------------------------------------------------
// Process Control
//----------------------------------------------------------------------
Error
ProcessGDBRemote : : DoLaunch
(
Module * module ,
char const * argv [ ] ,
char const * envp [ ] ,
2010-08-31 18:35:14 +00:00
uint32_t launch_flags ,
2010-06-08 16:52:24 +00:00
const char * stdin_path ,
const char * stdout_path ,
const char * stderr_path
)
{
// ::LogSetBitMask (GDBR_LOG_DEFAULT);
// ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD);
// ::LogSetLogFile ("/dev/stdout");
Error error ;
ObjectFile * object_file = module - > GetObjectFile ( ) ;
if ( object_file )
{
ArchSpec inferior_arch ( module - > GetArchitecture ( ) ) ;
char host_port [ 128 ] ;
snprintf ( host_port , sizeof ( host_port ) , " localhost:%u " , get_random_port ( ) ) ;
bool start_debugserver_with_inferior_args = false ;
if ( start_debugserver_with_inferior_args )
{
// We want to launch debugserver with the inferior program and its
// arguments on the command line. We should only do this if we
// the GDB server we are talking to doesn't support the 'A' packet.
error = StartDebugserverProcess ( host_port ,
argv ,
envp ,
NULL , //stdin_path,
LLDB_INVALID_PROCESS_ID ,
NULL , false ,
2010-09-03 18:20:07 +00:00
( launch_flags & eLaunchFlagDisableASLR ) ! = 0 ,
2010-06-08 16:52:24 +00:00
inferior_arch ) ;
if ( error . Fail ( ) )
return error ;
error = ConnectToDebugserver ( host_port ) ;
if ( error . Success ( ) )
{
SetID ( m_gdb_comm . GetCurrentProcessID ( m_packet_timeout ) ) ;
}
}
else
{
error = StartDebugserverProcess ( host_port ,
NULL ,
NULL ,
NULL , //stdin_path,
LLDB_INVALID_PROCESS_ID ,
NULL , false ,
2010-09-03 18:20:07 +00:00
( launch_flags & eLaunchFlagDisableASLR ) ! = 0 ,
2010-06-08 16:52:24 +00:00
inferior_arch ) ;
if ( error . Fail ( ) )
return error ;
error = ConnectToDebugserver ( host_port ) ;
if ( error . Success ( ) )
{
// Send the environment and the program + arguments after we connect
if ( envp )
{
const char * env_entry ;
for ( int i = 0 ; ( env_entry = envp [ i ] ) ; + + i )
{
if ( m_gdb_comm . SendEnvironmentPacket ( env_entry , m_packet_timeout ) ! = 0 )
break ;
}
}
2010-08-03 00:35:52 +00:00
// FIXME: convert this to use the new set/show variables when they are available
#if 0
if ( : : getenv ( " LLDB_DEBUG_DEBUGSERVER " ) )
{
const uint32_t attach_debugserver_secs = 10 ;
: : printf ( " attach to debugserver (pid = %i) \n " , m_debugserver_pid ) ;
for ( uint32_t i = 0 ; i < attach_debugserver_secs ; + + i )
{
printf ( " %i \n " , attach_debugserver_secs - i ) ;
sleep ( 1 ) ;
}
}
# endif
2010-06-08 16:52:24 +00:00
const uint32_t arg_timeout_seconds = 10 ;
int arg_packet_err = m_gdb_comm . SendArgumentsPacket ( argv , arg_timeout_seconds ) ;
if ( arg_packet_err = = 0 )
{
std : : string error_str ;
if ( m_gdb_comm . GetLaunchSuccess ( m_packet_timeout , error_str ) )
{
SetID ( m_gdb_comm . GetCurrentProcessID ( m_packet_timeout ) ) ;
}
else
{
error . SetErrorString ( error_str . c_str ( ) ) ;
}
}
else
{
error . SetErrorStringWithFormat ( " 'A' packet returned an error: %i. \n " , arg_packet_err ) ;
}
SetID ( m_gdb_comm . GetCurrentProcessID ( m_packet_timeout ) ) ;
}
}
if ( GetID ( ) = = LLDB_INVALID_PROCESS_ID )
{
KillDebugserverProcess ( ) ;
return error ;
}
StringExtractorGDBRemote response ;
if ( m_gdb_comm . SendPacketAndWaitForResponse ( " ? " , 1 , response , m_packet_timeout , false ) )
SetPrivateState ( SetThreadStopInfo ( response ) ) ;
}
else
{
// Set our user ID to an invalid process ID.
SetID ( LLDB_INVALID_PROCESS_ID ) ;
error . SetErrorStringWithFormat ( " Failed to get object file from '%s' for arch %s. \n " , module - > GetFileSpec ( ) . GetFilename ( ) . AsCString ( ) , module - > GetArchitecture ( ) . AsCString ( ) ) ;
}
// Return the process ID we have
return error ;
}
Error
ProcessGDBRemote : : ConnectToDebugserver ( const char * host_port )
{
Error error ;
// Sleep and wait a bit for debugserver to start to listen...
std : : auto_ptr < ConnectionFileDescriptor > conn_ap ( new ConnectionFileDescriptor ( ) ) ;
if ( conn_ap . get ( ) )
{
std : : string connect_url ( " connect:// " ) ;
connect_url . append ( host_port ) ;
const uint32_t max_retry_count = 50 ;
uint32_t retry_count = 0 ;
while ( ! m_gdb_comm . IsConnected ( ) )
{
if ( conn_ap - > Connect ( connect_url . c_str ( ) , & error ) = = eConnectionStatusSuccess )
{
m_gdb_comm . SetConnection ( conn_ap . release ( ) ) ;
break ;
}
retry_count + + ;
if ( retry_count > = max_retry_count )
break ;
usleep ( 100000 ) ;
}
}
if ( ! m_gdb_comm . IsConnected ( ) )
{
if ( error . Success ( ) )
error . SetErrorString ( " not connected to remote gdb server " ) ;
return error ;
}
m_gdb_comm . SetAckMode ( true ) ;
if ( m_gdb_comm . StartReadThread ( & error ) )
{
// Send an initial ack
m_gdb_comm . SendAck ( ' + ' ) ;
if ( m_debugserver_pid ! = LLDB_INVALID_PROCESS_ID )
2010-08-21 02:22:51 +00:00
m_debugserver_thread = Host : : StartMonitoringChildProcess ( MonitorDebugserverProcess ,
this ,
m_debugserver_pid ,
false ) ;
2010-06-08 16:52:24 +00:00
StringExtractorGDBRemote response ;
if ( m_gdb_comm . SendPacketAndWaitForResponse ( " QStartNoAckMode " , response , 1 , false ) )
{
if ( response . IsOKPacket ( ) )
m_gdb_comm . SetAckMode ( false ) ;
}
BuildDynamicRegisterInfo ( ) ;
}
return error ;
}
void
ProcessGDBRemote : : DidLaunchOrAttach ( )
{
ProcessGDBRemoteLog : : LogIf ( GDBR_LOG_PROCESS , " ProcessGDBRemote::DidLaunch() " ) ;
if ( GetID ( ) = = LLDB_INVALID_PROCESS_ID )
{
m_dynamic_loader_ap . reset ( ) ;
}
else
{
m_dispatch_queue_offsets_addr = LLDB_INVALID_ADDRESS ;
2010-08-09 23:31:02 +00:00
Module * exe_module = GetTarget ( ) . GetExecutableModule ( ) . get ( ) ;
2010-06-08 16:52:24 +00:00
assert ( exe_module ) ;
ObjectFile * exe_objfile = exe_module - > GetObjectFile ( ) ;
assert ( exe_objfile ) ;
m_byte_order = exe_objfile - > GetByteOrder ( ) ;
assert ( m_byte_order ! = eByteOrderInvalid ) ;
StreamString strm ;
ArchSpec inferior_arch ;
// See if the GDB server supports the qHostInfo information
const char * vendor = m_gdb_comm . GetVendorString ( ) . AsCString ( ) ;
const char * os_type = m_gdb_comm . GetOSString ( ) . AsCString ( ) ;
2010-08-09 23:31:02 +00:00
ArchSpec arch_spec = GetTarget ( ) . GetArchitecture ( ) ;
2010-06-08 16:52:24 +00:00
2010-08-09 23:31:02 +00:00
if ( arch_spec . IsValid ( ) & & arch_spec = = ArchSpec ( " arm " ) )
2010-06-08 16:52:24 +00:00
{
// For ARM we can't trust the arch of the process as it could
// have an armv6 object file, but be running on armv7 kernel.
inferior_arch = m_gdb_comm . GetHostArchitecture ( ) ;
}
if ( ! inferior_arch . IsValid ( ) )
2010-08-09 23:31:02 +00:00
inferior_arch = arch_spec ;
2010-06-08 16:52:24 +00:00
if ( vendor = = NULL )
vendor = Host : : GetVendorString ( ) . AsCString ( " apple " ) ;
if ( os_type = = NULL )
os_type = Host : : GetOSString ( ) . AsCString ( " darwin " ) ;
strm . Printf ( " %s-%s-%s " , inferior_arch . AsCString ( ) , vendor , os_type ) ;
std : : transform ( strm . GetString ( ) . begin ( ) ,
strm . GetString ( ) . end ( ) ,
strm . GetString ( ) . begin ( ) ,
: : tolower ) ;
m_target_triple . SetCString ( strm . GetString ( ) . c_str ( ) ) ;
}
}
void
ProcessGDBRemote : : DidLaunch ( )
{
DidLaunchOrAttach ( ) ;
if ( m_dynamic_loader_ap . get ( ) )
m_dynamic_loader_ap - > DidLaunch ( ) ;
}
Error
2010-07-09 20:39:50 +00:00
ProcessGDBRemote : : DoAttachToProcessWithID ( lldb : : pid_t attach_pid )
2010-06-08 16:52:24 +00:00
{
Error error ;
// Clear out and clean up from any current state
Clear ( ) ;
2010-08-09 23:31:02 +00:00
ArchSpec arch_spec = GetTarget ( ) . GetArchitecture ( ) ;
2010-06-08 16:52:24 +00:00
//Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS);
2010-08-09 23:31:02 +00:00
2010-06-08 16:52:24 +00:00
if ( attach_pid ! = LLDB_INVALID_PROCESS_ID )
{
SetPrivateState ( eStateAttaching ) ;
char host_port [ 128 ] ;
snprintf ( host_port , sizeof ( host_port ) , " localhost:%u " , get_random_port ( ) ) ;
2010-08-31 18:35:14 +00:00
error = StartDebugserverProcess ( host_port , // debugserver_url
NULL , // inferior_argv
NULL , // inferior_envp
NULL , // stdin_path
LLDB_INVALID_PROCESS_ID , // attach_pid
NULL , // attach_pid_name
false , // wait_for_launch
false , // disable_aslr
2010-08-09 23:31:02 +00:00
arch_spec ) ;
2010-06-08 16:52:24 +00:00
if ( error . Fail ( ) )
{
const char * error_string = error . AsCString ( ) ;
if ( error_string = = NULL )
error_string = " unable to launch " DEBUGSERVER_BASENAME ;
SetExitStatus ( - 1 , error_string ) ;
}
else
{
error = ConnectToDebugserver ( host_port ) ;
if ( error . Success ( ) )
{
char packet [ 64 ] ;
const int packet_len = : : snprintf ( packet , sizeof ( packet ) , " vAttach;%x " , attach_pid ) ;
StringExtractorGDBRemote response ;
StateType stop_state = m_gdb_comm . SendContinuePacketAndWaitForResponse ( this ,
packet ,
packet_len ,
response ) ;
switch ( stop_state )
{
case eStateStopped :
case eStateCrashed :
case eStateSuspended :
SetID ( attach_pid ) ;
m_last_stop_packet = response ;
m_last_stop_packet . SetFilePos ( 0 ) ;
SetPrivateState ( stop_state ) ;
break ;
case eStateExited :
m_last_stop_packet = response ;
m_last_stop_packet . SetFilePos ( 0 ) ;
response . SetFilePos ( 1 ) ;
SetExitStatus ( response . GetHexU8 ( ) , NULL ) ;
break ;
default :
SetExitStatus ( - 1 , " unable to attach to process " ) ;
break ;
}
}
}
}
lldb : : pid_t pid = GetID ( ) ;
if ( pid = = LLDB_INVALID_PROCESS_ID )
{
KillDebugserverProcess ( ) ;
}
return error ;
}
size_t
ProcessGDBRemote : : AttachInputReaderCallback
(
void * baton ,
InputReader * reader ,
lldb : : InputReaderAction notification ,
const char * bytes ,
size_t bytes_len
)
{
if ( notification = = eInputReaderGotToken )
{
ProcessGDBRemote * gdb_process = ( ProcessGDBRemote * ) baton ;
if ( gdb_process - > m_waiting_for_attach )
gdb_process - > m_waiting_for_attach = false ;
reader - > SetIsDone ( true ) ;
return 1 ;
}
return 0 ;
}
Error
2010-07-09 20:39:50 +00:00
ProcessGDBRemote : : DoAttachToProcessWithName ( const char * process_name , bool wait_for_launch )
2010-06-08 16:52:24 +00:00
{
Error error ;
// Clear out and clean up from any current state
Clear ( ) ;
// HACK: require arch be set correctly at the target level until we can
// figure out a good way to determine the arch of what we are attaching to
//Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS);
if ( process_name & & process_name [ 0 ] )
{
SetPrivateState ( eStateAttaching ) ;
char host_port [ 128 ] ;
2010-08-09 23:31:02 +00:00
ArchSpec arch_spec = GetTarget ( ) . GetArchitecture ( ) ;
2010-06-08 16:52:24 +00:00
snprintf ( host_port , sizeof ( host_port ) , " localhost:%u " , get_random_port ( ) ) ;
2010-08-31 18:35:14 +00:00
error = StartDebugserverProcess ( host_port , // debugserver_url
NULL , // inferior_argv
NULL , // inferior_envp
NULL , // stdin_path
LLDB_INVALID_PROCESS_ID , // attach_pid
NULL , // attach_pid_name
false , // wait_for_launch
false , // disable_aslr
2010-08-09 23:31:02 +00:00
arch_spec ) ;
2010-06-08 16:52:24 +00:00
if ( error . Fail ( ) )
{
const char * error_string = error . AsCString ( ) ;
if ( error_string = = NULL )
error_string = " unable to launch " DEBUGSERVER_BASENAME ;
SetExitStatus ( - 1 , error_string ) ;
}
else
{
error = ConnectToDebugserver ( host_port ) ;
if ( error . Success ( ) )
{
StreamString packet ;
packet . PutCString ( " vAttach " ) ;
if ( wait_for_launch )
packet . PutCString ( " Wait " ) ;
packet . PutChar ( ' ; ' ) ;
packet . PutBytesAsRawHex8 ( process_name , strlen ( process_name ) , eByteOrderHost , eByteOrderHost ) ;
StringExtractorGDBRemote response ;
StateType stop_state = m_gdb_comm . SendContinuePacketAndWaitForResponse ( this ,
packet . GetData ( ) ,
packet . GetSize ( ) ,
response ) ;
switch ( stop_state )
{
case eStateStopped :
case eStateCrashed :
case eStateSuspended :
SetID ( m_gdb_comm . GetCurrentProcessID ( m_packet_timeout ) ) ;
m_last_stop_packet = response ;
m_last_stop_packet . SetFilePos ( 0 ) ;
SetPrivateState ( stop_state ) ;
break ;
case eStateExited :
m_last_stop_packet = response ;
m_last_stop_packet . SetFilePos ( 0 ) ;
response . SetFilePos ( 1 ) ;
SetExitStatus ( response . GetHexU8 ( ) , NULL ) ;
break ;
default :
SetExitStatus ( - 1 , " unable to attach to process " ) ;
break ;
}
}
}
}
lldb : : pid_t pid = GetID ( ) ;
if ( pid = = LLDB_INVALID_PROCESS_ID )
{
KillDebugserverProcess ( ) ;
}
return error ;
}
//
// if (wait_for_launch)
// {
// InputReaderSP reader_sp (new InputReader());
// StreamString instructions;
// instructions.Printf("Hit any key to cancel waiting for '%s' to launch...", process_name);
// error = reader_sp->Initialize (AttachInputReaderCallback, // callback
// this, // baton
// eInputReaderGranularityByte,
// NULL, // End token
// false);
//
// StringExtractorGDBRemote response;
// m_waiting_for_attach = true;
// FILE *reader_out_fh = reader_sp->GetOutputFileHandle();
// while (m_waiting_for_attach)
// {
// // Wait for one second for the stop reply packet
// if (m_gdb_comm.WaitForPacket(response, 1))
// {
// // Got some sort of packet, see if it is the stop reply packet?
// char ch = response.GetChar(0);
// if (ch == 'T')
// {
// m_waiting_for_attach = false;
// }
// }
// else
// {
// // Put a period character every second
// fputc('.', reader_out_fh);
// }
// }
// }
// }
// return GetID();
//}
void
ProcessGDBRemote : : DidAttach ( )
{
2010-08-09 23:31:02 +00:00
// If we haven't got an executable module yet, then we should make a dynamic loader, and
// see if it can find the executable module for us. If we do have an executable module,
// make sure it matches the process we've just attached to.
ModuleSP exe_module_sp = GetTarget ( ) . GetExecutableModule ( ) ;
if ( ! m_dynamic_loader_ap . get ( ) )
{
m_dynamic_loader_ap . reset ( DynamicLoader : : FindPlugin ( this , " dynamic-loader.macosx-dyld " ) ) ;
}
2010-06-08 16:52:24 +00:00
if ( m_dynamic_loader_ap . get ( ) )
m_dynamic_loader_ap - > DidAttach ( ) ;
2010-08-09 23:31:02 +00:00
Module * new_exe_module = GetTarget ( ) . GetExecutableModule ( ) . get ( ) ;
if ( new_exe_module = = NULL )
{
}
DidLaunchOrAttach ( ) ;
2010-06-08 16:52:24 +00:00
}
Error
ProcessGDBRemote : : WillResume ( )
{
m_continue_packet . Clear ( ) ;
// Start the continue packet we will use to run the target. Each thread
// will append what it is supposed to be doing to this packet when the
// ThreadList::WillResume() is called. If a thread it supposed
// to stay stopped, then don't append anything to this string.
m_continue_packet . Printf ( " vCont " ) ;
return Error ( ) ;
}
Error
ProcessGDBRemote : : DoResume ( )
{
ProcessGDBRemoteLog : : LogIf ( GDBR_LOG_PROCESS , " ProcessGDBRemote::Resume() " ) ;
m_async_broadcaster . BroadcastEvent ( eBroadcastBitAsyncContinue , new EventDataBytes ( m_continue_packet . GetData ( ) , m_continue_packet . GetSize ( ) ) ) ;
return Error ( ) ;
}
size_t
ProcessGDBRemote : : GetSoftwareBreakpointTrapOpcode ( BreakpointSite * bp_site )
{
const uint8_t * trap_opcode = NULL ;
uint32_t trap_opcode_size = 0 ;
static const uint8_t g_arm_breakpoint_opcode [ ] = { 0xFE , 0xDE , 0xFF , 0xE7 } ;
//static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE };
static const uint8_t g_ppc_breakpoint_opcode [ ] = { 0x7F , 0xC0 , 0x00 , 0x08 } ;
static const uint8_t g_i386_breakpoint_opcode [ ] = { 0xCC } ;
2010-08-09 23:31:02 +00:00
ArchSpec : : CPU arch_cpu = GetTarget ( ) . GetArchitecture ( ) . GetGenericCPUType ( ) ;
2010-06-11 03:25:34 +00:00
switch ( arch_cpu )
2010-06-08 16:52:24 +00:00
{
2010-06-11 03:25:34 +00:00
case ArchSpec : : eCPU_i386 :
case ArchSpec : : eCPU_x86_64 :
trap_opcode = g_i386_breakpoint_opcode ;
trap_opcode_size = sizeof ( g_i386_breakpoint_opcode ) ;
break ;
case ArchSpec : : eCPU_arm :
// TODO: fill this in for ARM. We need to dig up the symbol for
// the address in the breakpoint locaiton and figure out if it is
// an ARM or Thumb breakpoint.
trap_opcode = g_arm_breakpoint_opcode ;
trap_opcode_size = sizeof ( g_arm_breakpoint_opcode ) ;
break ;
case ArchSpec : : eCPU_ppc :
case ArchSpec : : eCPU_ppc64 :
trap_opcode = g_ppc_breakpoint_opcode ;
trap_opcode_size = sizeof ( g_ppc_breakpoint_opcode ) ;
break ;
default :
assert ( ! " Unhandled architecture in ProcessMacOSX::GetSoftwareBreakpointTrapOpcode() " ) ;
break ;
2010-06-08 16:52:24 +00:00
}
if ( trap_opcode & & trap_opcode_size )
{
if ( bp_site - > SetTrapOpcode ( trap_opcode , trap_opcode_size ) )
return trap_opcode_size ;
}
return 0 ;
}
uint32_t
ProcessGDBRemote : : UpdateThreadListIfNeeded ( )
{
// locker will keep a mutex locked until it goes out of scope
Log * log = ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_THREAD ) ;
if ( log & & log - > GetMask ( ) . IsSet ( GDBR_LOG_VERBOSE ) )
log - > Printf ( " ProcessGDBRemote::%s (pid = %i) " , __FUNCTION__ , GetID ( ) ) ;
2010-09-03 17:10:42 +00:00
Mutex : : Locker locker ( m_thread_list . GetMutex ( ) ) ;
2010-06-08 16:52:24 +00:00
const uint32_t stop_id = GetStopID ( ) ;
if ( m_thread_list . GetSize ( false ) = = 0 | | stop_id ! = m_thread_list . GetStopID ( ) )
{
// Update the thread list's stop id immediately so we don't recurse into this function.
ThreadList curr_thread_list ( this ) ;
curr_thread_list . SetStopID ( stop_id ) ;
Error err ;
StringExtractorGDBRemote response ;
for ( m_gdb_comm . SendPacketAndWaitForResponse ( " qfThreadInfo " , response , 1 , false ) ;
response . IsNormalPacket ( ) ;
m_gdb_comm . SendPacketAndWaitForResponse ( " qsThreadInfo " , response , 1 , false ) )
{
char ch = response . GetChar ( ) ;
if ( ch = = ' l ' )
break ;
if ( ch = = ' m ' )
{
do
{
tid_t tid = response . GetHexMaxU32 ( false , LLDB_INVALID_THREAD_ID ) ;
if ( tid ! = LLDB_INVALID_THREAD_ID )
{
ThreadSP thread_sp ( GetThreadList ( ) . FindThreadByID ( tid , false ) ) ;
if ( thread_sp )
thread_sp - > GetRegisterContext ( ) - > Invalidate ( ) ;
else
thread_sp . reset ( new ThreadGDBRemote ( * this , tid ) ) ;
curr_thread_list . AddThread ( thread_sp ) ;
}
ch = response . GetChar ( ) ;
} while ( ch = = ' , ' ) ;
}
}
m_thread_list = curr_thread_list ;
SetThreadStopInfo ( m_last_stop_packet ) ;
}
return GetThreadList ( ) . GetSize ( false ) ;
}
StateType
ProcessGDBRemote : : SetThreadStopInfo ( StringExtractor & stop_packet )
{
const char stop_type = stop_packet . GetChar ( ) ;
switch ( stop_type )
{
case ' T ' :
case ' S ' :
{
// Stop with signal and thread info
const uint8_t signo = stop_packet . GetHexU8 ( ) ;
std : : string name ;
std : : string value ;
std : : string thread_name ;
uint32_t exc_type = 0 ;
2010-07-23 16:45:51 +00:00
std : : vector < addr_t > exc_data ;
2010-06-08 16:52:24 +00:00
uint32_t tid = LLDB_INVALID_THREAD_ID ;
addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS ;
uint32_t exc_data_count = 0 ;
while ( stop_packet . GetNameColonValue ( name , value ) )
{
if ( name . compare ( " metype " ) = = 0 )
{
// exception type in big endian hex
exc_type = Args : : StringToUInt32 ( value . c_str ( ) , 0 , 16 ) ;
}
else if ( name . compare ( " mecount " ) = = 0 )
{
// exception count in big endian hex
exc_data_count = Args : : StringToUInt32 ( value . c_str ( ) , 0 , 16 ) ;
}
else if ( name . compare ( " medata " ) = = 0 )
{
// exception data in big endian hex
exc_data . push_back ( Args : : StringToUInt64 ( value . c_str ( ) , 0 , 16 ) ) ;
}
else if ( name . compare ( " thread " ) = = 0 )
{
// thread in big endian hex
tid = Args : : StringToUInt32 ( value . c_str ( ) , 0 , 16 ) ;
}
else if ( name . compare ( " name " ) = = 0 )
{
thread_name . swap ( value ) ;
}
else if ( name . compare ( " dispatchqaddr " ) = = 0 )
{
thread_dispatch_qaddr = Args : : StringToUInt64 ( value . c_str ( ) , 0 , 16 ) ;
}
}
ThreadSP thread_sp ( m_thread_list . FindThreadByID ( tid , false ) ) ;
if ( thread_sp )
{
ThreadGDBRemote * gdb_thread = static_cast < ThreadGDBRemote * > ( thread_sp . get ( ) ) ;
gdb_thread - > SetThreadDispatchQAddr ( thread_dispatch_qaddr ) ;
gdb_thread - > SetName ( thread_name . empty ( ) ? thread_name . c_str ( ) : NULL ) ;
if ( exc_type ! = 0 )
{
2010-08-04 01:40:35 +00:00
const size_t exc_data_count = exc_data . size ( ) ;
gdb_thread - > SetStopInfo ( StopInfoMachException : : CreateStopReasonWithMachException ( * thread_sp ,
exc_type ,
exc_data_count ,
exc_data_count > = 1 ? exc_data [ 0 ] : 0 ,
exc_data_count > = 2 ? exc_data [ 1 ] : 0 ) ) ;
2010-06-08 16:52:24 +00:00
}
else if ( signo )
{
2010-08-04 01:40:35 +00:00
gdb_thread - > SetStopInfo ( StopInfo : : CreateStopReasonWithSignal ( * thread_sp , signo ) ) ;
2010-06-08 16:52:24 +00:00
}
else
{
2010-08-04 01:40:35 +00:00
StopInfoSP invalid_stop_info_sp ;
gdb_thread - > SetStopInfo ( invalid_stop_info_sp ) ;
2010-06-08 16:52:24 +00:00
}
}
return eStateStopped ;
}
break ;
case ' W ' :
// process exited
return eStateExited ;
default :
break ;
}
return eStateInvalid ;
}
void
ProcessGDBRemote : : RefreshStateAfterStop ( )
{
2010-08-09 23:31:02 +00:00
// FIXME - add a variable to tell that we're in the middle of attaching if we
// need to know that.
2010-06-08 16:52:24 +00:00
// We must be attaching if we don't already have a valid architecture
2010-08-09 23:31:02 +00:00
// if (!GetTarget().GetArchitecture().IsValid())
// {
// Module *exe_module = GetTarget().GetExecutableModule().get();
// if (exe_module)
// m_arch_spec = exe_module->GetArchitecture();
// }
2010-06-08 16:52:24 +00:00
// Let all threads recover from stopping and do any clean up based
// on the previous thread state (if any).
m_thread_list . RefreshStateAfterStop ( ) ;
// Discover new threads:
UpdateThreadListIfNeeded ( ) ;
}
Error
ProcessGDBRemote : : DoHalt ( )
{
Error error ;
if ( m_gdb_comm . IsRunning ( ) )
{
bool timed_out = false ;
2010-09-03 19:15:43 +00:00
Mutex : : Locker locker ;
if ( ! m_gdb_comm . SendInterrupt ( locker , 2 , & timed_out ) )
2010-06-08 16:52:24 +00:00
{
if ( timed_out )
error . SetErrorString ( " timed out sending interrupt packet " ) ;
else
error . SetErrorString ( " unknown error sending interrupt packet " ) ;
}
}
return error ;
}
Error
ProcessGDBRemote : : WillDetach ( )
{
Error error ;
const StateType state = m_private_state . GetValue ( ) ;
if ( IsRunning ( state ) )
error . SetErrorString ( " Process must be stopped in order to detach. " ) ;
return error ;
}
Error
ProcessGDBRemote : : DoDestroy ( )
{
Error error ;
Log * log = ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ;
if ( log )
log - > Printf ( " ProcessGDBRemote::DoDestroy() " ) ;
// Interrupt if our inferior is running...
2010-09-03 19:15:43 +00:00
Mutex : : Locker locker ;
m_gdb_comm . SendInterrupt ( locker , 1 ) ;
2010-06-08 16:52:24 +00:00
DisableAllBreakpointSites ( ) ;
SetExitStatus ( - 1 , " process killed " ) ;
StringExtractorGDBRemote response ;
if ( m_gdb_comm . SendPacketAndWaitForResponse ( " k " , response , 2 , false ) )
{
if ( log )
{
if ( response . IsOKPacket ( ) )
log - > Printf ( " ProcessGDBRemote::DoDestroy() kill was successful " ) ;
else
log - > Printf ( " ProcessGDBRemote::DoDestroy() kill failed: %s " , response . GetStringRef ( ) . c_str ( ) ) ;
}
}
StopAsyncThread ( ) ;
m_gdb_comm . StopReadThread ( ) ;
KillDebugserverProcess ( ) ;
return error ;
}
ByteOrder
ProcessGDBRemote : : GetByteOrder ( ) const
{
return m_byte_order ;
}
//------------------------------------------------------------------
// Process Queries
//------------------------------------------------------------------
bool
ProcessGDBRemote : : IsAlive ( )
{
return m_gdb_comm . IsConnected ( ) ;
}
addr_t
ProcessGDBRemote : : GetImageInfoAddress ( )
{
if ( ! m_gdb_comm . IsRunning ( ) )
{
StringExtractorGDBRemote response ;
if ( m_gdb_comm . SendPacketAndWaitForResponse ( " qShlibInfoAddr " , : : strlen ( " qShlibInfoAddr " ) , response , 2 , false ) )
{
if ( response . IsNormalPacket ( ) )
return response . GetHexMaxU64 ( false , LLDB_INVALID_ADDRESS ) ;
}
}
return LLDB_INVALID_ADDRESS ;
}
DynamicLoader *
ProcessGDBRemote : : GetDynamicLoader ( )
{
return m_dynamic_loader_ap . get ( ) ;
}
//------------------------------------------------------------------
// Process Memory
//------------------------------------------------------------------
size_t
ProcessGDBRemote : : DoReadMemory ( addr_t addr , void * buf , size_t size , Error & error )
{
if ( size > m_max_memory_size )
{
// Keep memory read sizes down to a sane limit. This function will be
// called multiple times in order to complete the task by
// lldb_private::Process so it is ok to do this.
size = m_max_memory_size ;
}
char packet [ 64 ] ;
const int packet_len = : : snprintf ( packet , sizeof ( packet ) , " m%llx,%zx " , ( uint64_t ) addr , size ) ;
assert ( packet_len + 1 < sizeof ( packet ) ) ;
StringExtractorGDBRemote response ;
if ( m_gdb_comm . SendPacketAndWaitForResponse ( packet , packet_len , response , 2 , true ) )
{
if ( response . IsNormalPacket ( ) )
{
error . Clear ( ) ;
return response . GetHexBytes ( buf , size , ' \xdd ' ) ;
}
else if ( response . IsErrorPacket ( ) )
error . SetErrorStringWithFormat ( " gdb remote returned an error: %s " , response . GetStringRef ( ) . c_str ( ) ) ;
else if ( response . IsUnsupportedPacket ( ) )
error . SetErrorStringWithFormat ( " '%s' packet unsupported " , packet ) ;
else
error . SetErrorStringWithFormat ( " unexpected response to '%s': '%s' " , packet , response . GetStringRef ( ) . c_str ( ) ) ;
}
else
{
error . SetErrorStringWithFormat ( " failed to sent packet: '%s' " , packet ) ;
}
return 0 ;
}
size_t
ProcessGDBRemote : : DoWriteMemory ( addr_t addr , const void * buf , size_t size , Error & error )
{
StreamString packet ;
packet . Printf ( " M%llx,%zx: " , addr , size ) ;
packet . PutBytesAsRawHex8 ( buf , size , eByteOrderHost , eByteOrderHost ) ;
StringExtractorGDBRemote response ;
if ( m_gdb_comm . SendPacketAndWaitForResponse ( packet . GetData ( ) , packet . GetSize ( ) , response , 2 , true ) )
{
if ( response . IsOKPacket ( ) )
{
error . Clear ( ) ;
return size ;
}
else if ( response . IsErrorPacket ( ) )
error . SetErrorStringWithFormat ( " gdb remote returned an error: %s " , response . GetStringRef ( ) . c_str ( ) ) ;
else if ( response . IsUnsupportedPacket ( ) )
error . SetErrorStringWithFormat ( " '%s' packet unsupported " , packet . GetString ( ) . c_str ( ) ) ;
else
error . SetErrorStringWithFormat ( " unexpected response to '%s': '%s' " , packet . GetString ( ) . c_str ( ) , response . GetStringRef ( ) . c_str ( ) ) ;
}
else
{
error . SetErrorStringWithFormat ( " failed to sent packet: '%s' " , packet . GetString ( ) . c_str ( ) ) ;
}
return 0 ;
}
lldb : : addr_t
ProcessGDBRemote : : DoAllocateMemory ( size_t size , uint32_t permissions , Error & error )
{
addr_t allocated_addr = m_gdb_comm . AllocateMemory ( size , permissions , m_packet_timeout ) ;
if ( allocated_addr = = LLDB_INVALID_ADDRESS )
error . SetErrorStringWithFormat ( " unable to allocate %zu bytes of memory with permissions %u " , size , permissions ) ;
else
error . Clear ( ) ;
return allocated_addr ;
}
Error
ProcessGDBRemote : : DoDeallocateMemory ( lldb : : addr_t addr )
{
Error error ;
if ( ! m_gdb_comm . DeallocateMemory ( addr , m_packet_timeout ) )
error . SetErrorStringWithFormat ( " unable to deallocate memory at 0x%llx " , addr ) ;
return error ;
}
//------------------------------------------------------------------
// Process STDIO
//------------------------------------------------------------------
size_t
ProcessGDBRemote : : GetSTDOUT ( char * buf , size_t buf_size , Error & error )
{
Mutex : : Locker locker ( m_stdio_mutex ) ;
size_t bytes_available = m_stdout_data . size ( ) ;
if ( bytes_available > 0 )
{
ProcessGDBRemoteLog : : LogIf ( GDBR_LOG_PROCESS , " ProcessGDBRemote::%s (&%p[%u]) ... " , __FUNCTION__ , buf , buf_size ) ;
if ( bytes_available > buf_size )
{
2010-07-20 22:52:08 +00:00
memcpy ( buf , m_stdout_data . c_str ( ) , buf_size ) ;
2010-06-08 16:52:24 +00:00
m_stdout_data . erase ( 0 , buf_size ) ;
bytes_available = buf_size ;
}
else
{
2010-07-20 22:52:08 +00:00
memcpy ( buf , m_stdout_data . c_str ( ) , bytes_available ) ;
2010-06-08 16:52:24 +00:00
m_stdout_data . clear ( ) ;
//ResetEventBits(eBroadcastBitSTDOUT);
}
}
return bytes_available ;
}
size_t
ProcessGDBRemote : : GetSTDERR ( char * buf , size_t buf_size , Error & error )
{
// Can we get STDERR through the remote protocol?
return 0 ;
}
size_t
ProcessGDBRemote : : PutSTDIN ( const char * src , size_t src_len , Error & error )
{
if ( m_stdio_communication . IsConnected ( ) )
{
ConnectionStatus status ;
m_stdio_communication . Write ( src , src_len , status , NULL ) ;
}
return 0 ;
}
Error
ProcessGDBRemote : : EnableBreakpoint ( BreakpointSite * bp_site )
{
Error error ;
assert ( bp_site ! = NULL ) ;
Log * log = ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_BREAKPOINTS ) ;
user_id_t site_id = bp_site - > GetID ( ) ;
const addr_t addr = bp_site - > GetLoadAddress ( ) ;
if ( log )
log - > Printf ( " ProcessGDBRemote::EnableBreakpoint (size_id = %d) address = 0x%llx " , site_id , ( uint64_t ) addr ) ;
if ( bp_site - > IsEnabled ( ) )
{
if ( log )
log - > Printf ( " ProcessGDBRemote::EnableBreakpoint (size_id = %d) address = 0x%llx -- SUCCESS (already enabled) " , site_id , ( uint64_t ) addr ) ;
return error ;
}
else
{
const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode ( bp_site ) ;
if ( bp_site - > HardwarePreferred ( ) )
{
// Try and set hardware breakpoint, and if that fails, fall through
// and set a software breakpoint?
}
if ( m_z0_supported )
{
char packet [ 64 ] ;
const int packet_len = : : snprintf ( packet , sizeof ( packet ) , " Z0,%llx,%zx " , addr , bp_op_size ) ;
assert ( packet_len + 1 < sizeof ( packet ) ) ;
StringExtractorGDBRemote response ;
if ( m_gdb_comm . SendPacketAndWaitForResponse ( packet , packet_len , response , 2 , true ) )
{
if ( response . IsUnsupportedPacket ( ) )
{
// Disable z packet support and try again
m_z0_supported = 0 ;
return EnableBreakpoint ( bp_site ) ;
}
else if ( response . IsOKPacket ( ) )
{
bp_site - > SetEnabled ( true ) ;
bp_site - > SetType ( BreakpointSite : : eExternal ) ;
return error ;
}
else
{
uint8_t error_byte = response . GetError ( ) ;
if ( error_byte )
error . SetErrorStringWithFormat ( " %x packet failed with error: %i (0x%2.2x). \n " , packet , error_byte , error_byte ) ;
}
}
}
else
{
return EnableSoftwareBreakpoint ( bp_site ) ;
}
}
if ( log )
{
const char * err_string = error . AsCString ( ) ;
log - > Printf ( " ProcessGDBRemote::EnableBreakpoint() error for breakpoint at 0x%8.8llx: %s " ,
bp_site - > GetLoadAddress ( ) ,
err_string ? err_string : " NULL " ) ;
}
// We shouldn't reach here on a successful breakpoint enable...
if ( error . Success ( ) )
error . SetErrorToGenericError ( ) ;
return error ;
}
Error
ProcessGDBRemote : : DisableBreakpoint ( BreakpointSite * bp_site )
{
Error error ;
assert ( bp_site ! = NULL ) ;
addr_t addr = bp_site - > GetLoadAddress ( ) ;
user_id_t site_id = bp_site - > GetID ( ) ;
Log * log = ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_BREAKPOINTS ) ;
if ( log )
log - > Printf ( " ProcessGDBRemote::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx " , site_id , ( uint64_t ) addr ) ;
if ( bp_site - > IsEnabled ( ) )
{
const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode ( bp_site ) ;
if ( bp_site - > IsHardware ( ) )
{
// TODO: disable hardware breakpoint...
}
else
{
if ( m_z0_supported )
{
char packet [ 64 ] ;
const int packet_len = : : snprintf ( packet , sizeof ( packet ) , " z0,%llx,%zx " , addr , bp_op_size ) ;
assert ( packet_len + 1 < sizeof ( packet ) ) ;
StringExtractorGDBRemote response ;
if ( m_gdb_comm . SendPacketAndWaitForResponse ( packet , packet_len , response , 2 , true ) )
{
if ( response . IsUnsupportedPacket ( ) )
{
error . SetErrorString ( " Breakpoint site was set with Z packet, yet remote debugserver states z packets are not supported. " ) ;
}
else if ( response . IsOKPacket ( ) )
{
if ( log )
log - > Printf ( " ProcessGDBRemote::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx -- SUCCESS " , site_id , ( uint64_t ) addr ) ;
bp_site - > SetEnabled ( false ) ;
return error ;
}
else
{
uint8_t error_byte = response . GetError ( ) ;
if ( error_byte )
error . SetErrorStringWithFormat ( " %x packet failed with error: %i (0x%2.2x). \n " , packet , error_byte , error_byte ) ;
}
}
}
else
{
return DisableSoftwareBreakpoint ( bp_site ) ;
}
}
}
else
{
if ( log )
log - > Printf ( " ProcessGDBRemote::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx -- SUCCESS (already disabled) " , site_id , ( uint64_t ) addr ) ;
return error ;
}
if ( error . Success ( ) )
error . SetErrorToGenericError ( ) ;
return error ;
}
Error
ProcessGDBRemote : : EnableWatchpoint ( WatchpointLocation * wp )
{
Error error ;
if ( wp )
{
user_id_t watchID = wp - > GetID ( ) ;
addr_t addr = wp - > GetLoadAddress ( ) ;
Log * log = ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_WATCHPOINTS ) ;
if ( log )
log - > Printf ( " ProcessGDBRemote::EnableWatchpoint(watchID = %d) " , watchID ) ;
if ( wp - > IsEnabled ( ) )
{
if ( log )
log - > Printf ( " ProcessGDBRemote::EnableWatchpoint(watchID = %d) addr = 0x%8.8llx: watchpoint already enabled. " , watchID , ( uint64_t ) addr ) ;
return error ;
}
else
{
// Pass down an appropriate z/Z packet...
error . SetErrorString ( " watchpoints not supported " ) ;
}
}
else
{
error . SetErrorString ( " Watchpoint location argument was NULL. " ) ;
}
if ( error . Success ( ) )
error . SetErrorToGenericError ( ) ;
return error ;
}
Error
ProcessGDBRemote : : DisableWatchpoint ( WatchpointLocation * wp )
{
Error error ;
if ( wp )
{
user_id_t watchID = wp - > GetID ( ) ;
Log * log = ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_WATCHPOINTS ) ;
addr_t addr = wp - > GetLoadAddress ( ) ;
if ( log )
log - > Printf ( " ProcessGDBRemote::DisableWatchpoint (watchID = %d) addr = 0x%8.8llx " , watchID , ( uint64_t ) addr ) ;
if ( wp - > IsHardware ( ) )
{
// Pass down an appropriate z/Z packet...
error . SetErrorString ( " watchpoints not supported " ) ;
}
// TODO: clear software watchpoints if we implement them
}
else
{
error . SetErrorString ( " Watchpoint location argument was NULL. " ) ;
}
if ( error . Success ( ) )
error . SetErrorToGenericError ( ) ;
return error ;
}
void
ProcessGDBRemote : : Clear ( )
{
m_flags = 0 ;
m_thread_list . Clear ( ) ;
{
Mutex : : Locker locker ( m_stdio_mutex ) ;
m_stdout_data . clear ( ) ;
}
DestoryLibUnwindAddressSpace ( ) ;
}
Error
ProcessGDBRemote : : DoSignal ( int signo )
{
Error error ;
Log * log = ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ;
if ( log )
log - > Printf ( " ProcessGDBRemote::DoSignal (signal = %d) " , signo ) ;
if ( ! m_gdb_comm . SendAsyncSignal ( signo ) )
error . SetErrorStringWithFormat ( " failed to send signal %i " , signo ) ;
return error ;
}
Error
ProcessGDBRemote : : DoDetach ( )
{
Error error ;
Log * log = ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ;
if ( log )
log - > Printf ( " ProcessGDBRemote::DoDetach() " ) ;
// if (DoSIGSTOP (true))
// {
// CloseChildFileDescriptors ();
//
// // Scope for "locker" so we can reply to all of our exceptions (the SIGSTOP
// // exception).
// {
// Mutex::Locker locker(m_exception_messages_mutex);
// ReplyToAllExceptions();
// }
//
// // Shut down the exception thread and cleanup our exception remappings
// Task().ShutDownExceptionThread();
//
// pid_t pid = GetID();
//
// // Detach from our process while we are stopped.
// errno = 0;
//
// // Detach from our process
// ::ptrace (PT_DETACH, pid, (caddr_t)1, 0);
//
// error.SetErrorToErrno();
//
// if (log || error.Fail())
// error.PutToLog(log, "::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid);
//
// // Resume our task
// Task().Resume();
//
// // NULL our task out as we have already retored all exception ports
// Task().Clear();
//
// // Clear out any notion of the process we once were
// Clear();
//
// SetPrivateState (eStateDetached);
// return true;
// }
return error ;
}
void
ProcessGDBRemote : : STDIOReadThreadBytesReceived ( void * baton , const void * src , size_t src_len )
{
ProcessGDBRemote * process = ( ProcessGDBRemote * ) baton ;
process - > AppendSTDOUT ( static_cast < const char * > ( src ) , src_len ) ;
}
void
ProcessGDBRemote : : AppendSTDOUT ( const char * s , size_t len )
{
ProcessGDBRemoteLog : : LogIf ( GDBR_LOG_PROCESS , " ProcessGDBRemote::%s (<%d> %s) ... " , __FUNCTION__ , len , s ) ;
Mutex : : Locker locker ( m_stdio_mutex ) ;
m_stdout_data . append ( s , len ) ;
// FIXME: Make a real data object for this and put it out.
BroadcastEventIfUnique ( eBroadcastBitSTDOUT ) ;
}
Error
ProcessGDBRemote : : StartDebugserverProcess
(
const char * debugserver_url , // The connection string to use in the spawned debugserver ("localhost:1234" or "/dev/tty...")
char const * inferior_argv [ ] , // Arguments for the inferior program including the path to the inferior itself as the first argument
char const * inferior_envp [ ] , // Environment to pass along to the inferior program
char const * stdio_path ,
lldb : : pid_t attach_pid , // If inferior inferior_argv == NULL, and attach_pid != LLDB_INVALID_PROCESS_ID then attach to this attach_pid
const char * attach_name , // Wait for the next process to launch whose basename matches "attach_name"
bool wait_for_launch , // Wait for the process named "attach_name" to launch
2010-08-31 18:35:14 +00:00
bool disable_aslr , // Disable ASLR
2010-06-08 16:52:24 +00:00
ArchSpec & inferior_arch // The arch of the inferior that we will launch
)
{
Error error ;
if ( m_debugserver_pid = = LLDB_INVALID_PROCESS_ID )
{
// If we locate debugserver, keep that located version around
static FileSpec g_debugserver_file_spec ;
FileSpec debugserver_file_spec ;
char debugserver_path [ PATH_MAX ] ;
// Always check to see if we have an environment override for the path
// to the debugserver to use and use it if we do.
const char * env_debugserver_path = getenv ( " LLDB_DEBUGSERVER_PATH " ) ;
if ( env_debugserver_path )
debugserver_file_spec . SetFile ( env_debugserver_path ) ;
else
debugserver_file_spec = g_debugserver_file_spec ;
bool debugserver_exists = debugserver_file_spec . Exists ( ) ;
if ( ! debugserver_exists )
{
// The debugserver binary is in the LLDB.framework/Resources
// directory.
FileSpec framework_file_spec ( Host : : GetModuleFileSpecForHostAddress ( ( void * ) lldb_private : : Initialize ) ) ;
const char * framework_dir = framework_file_spec . GetDirectory ( ) . AsCString ( ) ;
const char * lldb_framework = : : strstr ( framework_dir , " /LLDB.framework " ) ;
if ( lldb_framework )
{
int len = lldb_framework - framework_dir + strlen ( " /LLDB.framework " ) ;
: : snprintf ( debugserver_path ,
sizeof ( debugserver_path ) ,
" %.*s/Resources/%s " ,
len ,
framework_dir ,
DEBUGSERVER_BASENAME ) ;
debugserver_file_spec . SetFile ( debugserver_path ) ;
debugserver_exists = debugserver_file_spec . Exists ( ) ;
}
if ( debugserver_exists )
{
g_debugserver_file_spec = debugserver_file_spec ;
}
else
{
g_debugserver_file_spec . Clear ( ) ;
debugserver_file_spec . Clear ( ) ;
}
}
if ( debugserver_exists )
{
debugserver_file_spec . GetPath ( debugserver_path , sizeof ( debugserver_path ) ) ;
m_stdio_communication . Clear ( ) ;
posix_spawnattr_t attr ;
Log * log = ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ;
Error local_err ; // Errors that don't affect the spawning.
if ( log )
log - > Printf ( " %s ( path='%s', argv=%p, envp=%p, arch=%s ) " , __FUNCTION__ , debugserver_path , inferior_argv , inferior_envp , inferior_arch . AsCString ( ) ) ;
error . SetError ( : : posix_spawnattr_init ( & attr ) , eErrorTypePOSIX ) ;
if ( error . Fail ( ) | | log )
error . PutToLog ( log , " ::posix_spawnattr_init ( &attr ) " ) ;
if ( error . Fail ( ) )
return error ; ;
# if !defined (__arm__)
// We don't need to do this for ARM, and we really shouldn't now that we
// have multiple CPU subtypes and no posix_spawnattr call that allows us
// to set which CPU subtype to launch...
2010-06-11 03:25:34 +00:00
if ( inferior_arch . GetType ( ) = = eArchTypeMachO )
2010-06-08 16:52:24 +00:00
{
2010-06-11 03:25:34 +00:00
cpu_type_t cpu = inferior_arch . GetCPUType ( ) ;
if ( cpu ! = 0 & & cpu ! = UINT32_MAX & & cpu ! = LLDB_INVALID_CPUTYPE )
{
size_t ocount = 0 ;
error . SetError ( : : posix_spawnattr_setbinpref_np ( & attr , 1 , & cpu , & ocount ) , eErrorTypePOSIX ) ;
if ( error . Fail ( ) | | log )
error . PutToLog ( log , " ::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %zu ) " , cpu , ocount ) ;
2010-06-08 16:52:24 +00:00
2010-06-11 03:25:34 +00:00
if ( error . Fail ( ) ! = 0 | | ocount ! = 1 )
return error ;
}
2010-06-08 16:52:24 +00:00
}
# endif
Args debugserver_args ;
char arg_cstr [ PATH_MAX ] ;
bool launch_process = true ;
if ( inferior_argv = = NULL & & attach_pid ! = LLDB_INVALID_PROCESS_ID )
launch_process = false ;
else if ( attach_name )
launch_process = false ; // Wait for a process whose basename matches that in inferior_argv[0]
bool pass_stdio_path_to_debugserver = true ;
lldb_utility : : PseudoTerminal pty ;
if ( stdio_path = = NULL )
{
pass_stdio_path_to_debugserver = false ;
if ( pty . OpenFirstAvailableMaster ( O_RDWR | O_NOCTTY , NULL , 0 ) )
{
struct termios stdin_termios ;
if ( : : tcgetattr ( pty . GetMasterFileDescriptor ( ) , & stdin_termios ) = = 0 )
{
stdin_termios . c_lflag & = ~ ECHO ; // Turn off echoing
stdin_termios . c_lflag & = ~ ICANON ; // Get one char at a time
: : tcsetattr ( pty . GetMasterFileDescriptor ( ) , TCSANOW , & stdin_termios ) ;
}
stdio_path = pty . GetSlaveName ( NULL , 0 ) ;
}
}
// Start args with "debugserver /file/path -r --"
debugserver_args . AppendArgument ( debugserver_path ) ;
debugserver_args . AppendArgument ( debugserver_url ) ;
debugserver_args . AppendArgument ( " --native-regs " ) ; // use native registers, not the GDB registers
debugserver_args . AppendArgument ( " --setsid " ) ; // make debugserver run in its own session so
// signals generated by special terminal key
// sequences (^C) don't affect debugserver
2010-08-31 18:35:14 +00:00
if ( disable_aslr )
debugserver_args . AppendArguments ( " --disable-aslr " ) ;
2010-06-08 16:52:24 +00:00
// Only set the inferior
if ( launch_process )
{
if ( stdio_path & & pass_stdio_path_to_debugserver )
{
debugserver_args . AppendArgument ( " -s " ) ; // short for --stdio-path
StreamString strm ;
strm . Printf ( " '%s' " , stdio_path ) ;
debugserver_args . AppendArgument ( strm . GetData ( ) ) ; // path to file to have inferior open as it's STDIO
}
}
const char * env_debugserver_log_file = getenv ( " LLDB_DEBUGSERVER_LOG_FILE " ) ;
if ( env_debugserver_log_file )
{
: : snprintf ( arg_cstr , sizeof ( arg_cstr ) , " --log-file=%s " , env_debugserver_log_file ) ;
debugserver_args . AppendArgument ( arg_cstr ) ;
}
const char * env_debugserver_log_flags = getenv ( " LLDB_DEBUGSERVER_LOG_FLAGS " ) ;
if ( env_debugserver_log_flags )
{
: : snprintf ( arg_cstr , sizeof ( arg_cstr ) , " --log-flags=%s " , env_debugserver_log_flags ) ;
debugserver_args . AppendArgument ( arg_cstr ) ;
}
// debugserver_args.AppendArgument("--log-file=/tmp/debugserver.txt");
// debugserver_args.AppendArgument("--log-flags=0x800e0e");
// Now append the program arguments
if ( launch_process )
{
if ( inferior_argv )
{
// Terminate the debugserver args so we can now append the inferior args
debugserver_args . AppendArgument ( " -- " ) ;
for ( int i = 0 ; inferior_argv [ i ] ! = NULL ; + + i )
debugserver_args . AppendArgument ( inferior_argv [ i ] ) ;
}
else
{
// Will send environment entries with the 'QEnvironment:' packet
// Will send arguments with the 'A' packet
}
}
else if ( attach_pid ! = LLDB_INVALID_PROCESS_ID )
{
: : snprintf ( arg_cstr , sizeof ( arg_cstr ) , " --attach=%u " , attach_pid ) ;
debugserver_args . AppendArgument ( arg_cstr ) ;
}
else if ( attach_name & & attach_name [ 0 ] )
{
if ( wait_for_launch )
debugserver_args . AppendArgument ( " --waitfor " ) ;
else
debugserver_args . AppendArgument ( " --attach " ) ;
debugserver_args . AppendArgument ( attach_name ) ;
}
Error file_actions_err ;
posix_spawn_file_actions_t file_actions ;
# if DONT_CLOSE_DEBUGSERVER_STDIO
file_actions_err . SetErrorString ( " Remove this after uncommenting the code block below. " ) ;
# else
file_actions_err . SetError ( : : posix_spawn_file_actions_init ( & file_actions ) , eErrorTypePOSIX ) ;
if ( file_actions_err . Success ( ) )
{
: : posix_spawn_file_actions_addclose ( & file_actions , STDIN_FILENO ) ;
: : posix_spawn_file_actions_addclose ( & file_actions , STDOUT_FILENO ) ;
: : posix_spawn_file_actions_addclose ( & file_actions , STDERR_FILENO ) ;
}
# endif
if ( log )
{
StreamString strm ;
debugserver_args . Dump ( & strm ) ;
log - > Printf ( " %s arguments: \n %s " , debugserver_args . GetArgumentAtIndex ( 0 ) , strm . GetData ( ) ) ;
}
error . SetError ( : : posix_spawnp ( & m_debugserver_pid ,
debugserver_path ,
file_actions_err . Success ( ) ? & file_actions : NULL ,
& attr ,
debugserver_args . GetArgumentVector ( ) ,
( char * const * ) inferior_envp ) ,
eErrorTypePOSIX ) ;
2010-07-02 01:29:13 +00:00
: : posix_spawnattr_destroy ( & attr ) ;
2010-06-08 16:52:24 +00:00
if ( file_actions_err . Success ( ) )
: : posix_spawn_file_actions_destroy ( & file_actions ) ;
// We have seen some cases where posix_spawnp was returning a valid
// looking pid even when an error was returned, so clear it out
if ( error . Fail ( ) )
m_debugserver_pid = LLDB_INVALID_PROCESS_ID ;
if ( error . Fail ( ) | | log )
error . PutToLog ( log , " ::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p ) " , m_debugserver_pid , debugserver_path , NULL , & attr , inferior_argv , inferior_envp ) ;
// if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID)
// {
// std::auto_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor (pty.ReleaseMasterFileDescriptor(), true));
// if (conn_ap.get())
// {
// m_stdio_communication.SetConnection(conn_ap.release());
// if (m_stdio_communication.IsConnected())
// {
// m_stdio_communication.SetReadThreadBytesReceivedCallback (STDIOReadThreadBytesReceived, this);
// m_stdio_communication.StartReadThread();
// }
// }
// }
}
else
{
error . SetErrorStringWithFormat ( " Unable to locate " DEBUGSERVER_BASENAME " . \n " ) ;
}
if ( m_debugserver_pid ! = LLDB_INVALID_PROCESS_ID )
StartAsyncThread ( ) ;
}
return error ;
}
bool
ProcessGDBRemote : : MonitorDebugserverProcess
(
void * callback_baton ,
lldb : : pid_t debugserver_pid ,
int signo , // Zero for no signal
int exit_status // Exit value of process if signal is zero
)
{
// We pass in the ProcessGDBRemote inferior process it and name it
// "gdb_remote_pid". The process ID is passed in the "callback_baton"
// pointer value itself, thus we need the double cast...
// "debugserver_pid" argument passed in is the process ID for
// debugserver that we are tracking...
2010-08-21 02:22:51 +00:00
ProcessGDBRemote * process = ( ProcessGDBRemote * ) callback_baton ;
if ( process )
2010-06-08 16:52:24 +00:00
{
2010-08-21 02:22:51 +00:00
// Sleep for a half a second to make sure our inferior process has
// time to set its exit status before we set it incorrectly when
// both the debugserver and the inferior process shut down.
usleep ( 500000 ) ;
// If our process hasn't yet exited, debugserver might have died.
// If the process did exit, the we are reaping it.
if ( process - > GetState ( ) ! = eStateExited )
2010-06-08 16:52:24 +00:00
{
2010-08-21 02:22:51 +00:00
char error_str [ 1024 ] ;
if ( signo )
2010-06-08 16:52:24 +00:00
{
2010-08-21 02:22:51 +00:00
const char * signal_cstr = process - > GetUnixSignals ( ) . GetSignalAsCString ( signo ) ;
if ( signal_cstr )
: : snprintf ( error_str , sizeof ( error_str ) , DEBUGSERVER_BASENAME " died with signal %s " , signal_cstr ) ;
2010-06-08 16:52:24 +00:00
else
2010-08-21 02:22:51 +00:00
: : snprintf ( error_str , sizeof ( error_str ) , DEBUGSERVER_BASENAME " died with signal %i " , signo ) ;
2010-06-08 16:52:24 +00:00
}
else
{
2010-08-21 02:22:51 +00:00
: : snprintf ( error_str , sizeof ( error_str ) , DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x " , exit_status ) ;
2010-06-08 16:52:24 +00:00
}
2010-08-21 02:22:51 +00:00
process - > SetExitStatus ( - 1 , error_str ) ;
}
else
{
// Debugserver has exited we need to let our ProcessGDBRemote
// know that it no longer has a debugserver instance
process - > m_debugserver_pid = LLDB_INVALID_PROCESS_ID ;
// We are returning true to this function below, so we can
// forget about the monitor handle.
process - > m_debugserver_thread = LLDB_INVALID_HOST_THREAD ;
2010-06-08 16:52:24 +00:00
}
}
return true ;
}
void
ProcessGDBRemote : : KillDebugserverProcess ( )
{
if ( m_debugserver_pid ! = LLDB_INVALID_PROCESS_ID )
{
: : kill ( m_debugserver_pid , SIGINT ) ;
m_debugserver_pid = LLDB_INVALID_PROCESS_ID ;
}
}
void
ProcessGDBRemote : : Initialize ( )
{
static bool g_initialized = false ;
if ( g_initialized = = false )
{
g_initialized = true ;
PluginManager : : RegisterPlugin ( GetPluginNameStatic ( ) ,
GetPluginDescriptionStatic ( ) ,
CreateInstance ) ;
Log : : Callbacks log_callbacks = {
ProcessGDBRemoteLog : : DisableLog ,
ProcessGDBRemoteLog : : EnableLog ,
ProcessGDBRemoteLog : : ListLogCategories
} ;
Log : : RegisterLogChannel ( ProcessGDBRemote : : GetPluginNameStatic ( ) , log_callbacks ) ;
}
}
bool
ProcessGDBRemote : : SetCurrentGDBRemoteThread ( int tid )
{
if ( m_curr_tid = = tid )
return true ;
char packet [ 32 ] ;
const int packet_len = : : snprintf ( packet , sizeof ( packet ) , " Hg%x " , tid ) ;
assert ( packet_len + 1 < sizeof ( packet ) ) ;
StringExtractorGDBRemote response ;
if ( m_gdb_comm . SendPacketAndWaitForResponse ( packet , packet_len , response , 2 , false ) )
{
if ( response . IsOKPacket ( ) )
{
m_curr_tid = tid ;
return true ;
}
}
return false ;
}
bool
ProcessGDBRemote : : SetCurrentGDBRemoteThreadForRun ( int tid )
{
if ( m_curr_tid_run = = tid )
return true ;
char packet [ 32 ] ;
const int packet_len = : : snprintf ( packet , sizeof ( packet ) , " Hg%x " , tid ) ;
assert ( packet_len + 1 < sizeof ( packet ) ) ;
StringExtractorGDBRemote response ;
if ( m_gdb_comm . SendPacketAndWaitForResponse ( packet , packet_len , response , 2 , false ) )
{
if ( response . IsOKPacket ( ) )
{
m_curr_tid_run = tid ;
return true ;
}
}
return false ;
}
void
ProcessGDBRemote : : ResetGDBRemoteState ( )
{
// Reset and GDB remote state
m_curr_tid = LLDB_INVALID_THREAD_ID ;
m_curr_tid_run = LLDB_INVALID_THREAD_ID ;
m_z0_supported = 1 ;
}
bool
ProcessGDBRemote : : StartAsyncThread ( )
{
ResetGDBRemoteState ( ) ;
Log * log = ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ;
if ( log )
log - > Printf ( " ProcessGDBRemote::%s () " , __FUNCTION__ ) ;
// Create a thread that watches our internal state and controls which
// events make it to clients (into the DCProcess event queue).
m_async_thread = Host : : ThreadCreate ( " <lldb.process.gdb-remote.async> " , ProcessGDBRemote : : AsyncThread , this , NULL ) ;
return m_async_thread ! = LLDB_INVALID_HOST_THREAD ;
}
void
ProcessGDBRemote : : StopAsyncThread ( )
{
Log * log = ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ;
if ( log )
log - > Printf ( " ProcessGDBRemote::%s () " , __FUNCTION__ ) ;
m_async_broadcaster . BroadcastEvent ( eBroadcastBitAsyncThreadShouldExit ) ;
// Stop the stdio thread
if ( m_async_thread ! = LLDB_INVALID_HOST_THREAD )
{
Host : : ThreadJoin ( m_async_thread , NULL , NULL ) ;
}
}
void *
ProcessGDBRemote : : AsyncThread ( void * arg )
{
ProcessGDBRemote * process = ( ProcessGDBRemote * ) arg ;
Log * log = ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ;
if ( log )
log - > Printf ( " ProcessGDBRemote::%s (arg = %p, pid = %i) thread starting... " , __FUNCTION__ , arg , process - > GetID ( ) ) ;
Listener listener ( " ProcessGDBRemote::AsyncThread " ) ;
EventSP event_sp ;
const uint32_t desired_event_mask = eBroadcastBitAsyncContinue |
eBroadcastBitAsyncThreadShouldExit ;
if ( listener . StartListeningForEvents ( & process - > m_async_broadcaster , desired_event_mask ) = = desired_event_mask )
{
bool done = false ;
while ( ! done )
{
if ( log )
log - > Printf ( " ProcessGDBRemote::%s (arg = %p, pid = %i) listener.WaitForEvent (NULL, event_sp)... " , __FUNCTION__ , arg , process - > GetID ( ) ) ;
if ( listener . WaitForEvent ( NULL , event_sp ) )
{
const uint32_t event_type = event_sp - > GetType ( ) ;
switch ( event_type )
{
case eBroadcastBitAsyncContinue :
{
const EventDataBytes * continue_packet = EventDataBytes : : GetEventDataFromEvent ( event_sp . get ( ) ) ;
if ( continue_packet )
{
const char * continue_cstr = ( const char * ) continue_packet - > GetBytes ( ) ;
const size_t continue_cstr_len = continue_packet - > GetByteSize ( ) ;
if ( log )
log - > Printf ( " ProcessGDBRemote::%s (arg = %p, pid = %i) got eBroadcastBitAsyncContinue: %s " , __FUNCTION__ , arg , process - > GetID ( ) , continue_cstr ) ;
process - > SetPrivateState ( eStateRunning ) ;
StringExtractorGDBRemote response ;
StateType stop_state = process - > GetGDBRemote ( ) . SendContinuePacketAndWaitForResponse ( process , continue_cstr , continue_cstr_len , response ) ;
switch ( stop_state )
{
case eStateStopped :
case eStateCrashed :
case eStateSuspended :
process - > m_last_stop_packet = response ;
process - > m_last_stop_packet . SetFilePos ( 0 ) ;
process - > SetPrivateState ( stop_state ) ;
break ;
case eStateExited :
process - > m_last_stop_packet = response ;
process - > m_last_stop_packet . SetFilePos ( 0 ) ;
response . SetFilePos ( 1 ) ;
process - > SetExitStatus ( response . GetHexU8 ( ) , NULL ) ;
done = true ;
break ;
case eStateInvalid :
break ;
default :
process - > SetPrivateState ( stop_state ) ;
break ;
}
}
}
break ;
case eBroadcastBitAsyncThreadShouldExit :
if ( log )
log - > Printf ( " ProcessGDBRemote::%s (arg = %p, pid = %i) got eBroadcastBitAsyncThreadShouldExit... " , __FUNCTION__ , arg , process - > GetID ( ) ) ;
done = true ;
break ;
default :
if ( log )
log - > Printf ( " ProcessGDBRemote::%s (arg = %p, pid = %i) got unknown event 0x%8.8x " , __FUNCTION__ , arg , process - > GetID ( ) , event_type ) ;
done = true ;
break ;
}
}
else
{
if ( log )
log - > Printf ( " ProcessGDBRemote::%s (arg = %p, pid = %i) listener.WaitForEvent (NULL, event_sp) => false " , __FUNCTION__ , arg , process - > GetID ( ) ) ;
done = true ;
}
}
}
if ( log )
log - > Printf ( " ProcessGDBRemote::%s (arg = %p, pid = %i) thread exiting... " , __FUNCTION__ , arg , process - > GetID ( ) ) ;
process - > m_async_thread = LLDB_INVALID_HOST_THREAD ;
return NULL ;
}
lldb_private : : unw_addr_space_t
ProcessGDBRemote : : GetLibUnwindAddressSpace ( )
{
unw_targettype_t target_type = UNW_TARGET_UNSPECIFIED ;
2010-06-11 03:25:34 +00:00
ArchSpec : : CPU arch_cpu = m_target . GetArchitecture ( ) . GetGenericCPUType ( ) ;
if ( arch_cpu = = ArchSpec : : eCPU_i386 )
2010-06-08 16:52:24 +00:00
target_type = UNW_TARGET_I386 ;
2010-06-11 03:25:34 +00:00
else if ( arch_cpu = = ArchSpec : : eCPU_x86_64 )
2010-06-08 16:52:24 +00:00
target_type = UNW_TARGET_X86_64 ;
if ( m_libunwind_addr_space )
{
if ( m_libunwind_target_type ! = target_type )
DestoryLibUnwindAddressSpace ( ) ;
else
return m_libunwind_addr_space ;
}
unw_accessors_t callbacks = get_macosx_libunwind_callbacks ( ) ;
m_libunwind_addr_space = unw_create_addr_space ( & callbacks , target_type ) ;
if ( m_libunwind_addr_space )
m_libunwind_target_type = target_type ;
else
m_libunwind_target_type = UNW_TARGET_UNSPECIFIED ;
return m_libunwind_addr_space ;
}
void
ProcessGDBRemote : : DestoryLibUnwindAddressSpace ( )
{
if ( m_libunwind_addr_space )
{
unw_destroy_addr_space ( m_libunwind_addr_space ) ;
m_libunwind_addr_space = NULL ;
}
m_libunwind_target_type = UNW_TARGET_UNSPECIFIED ;
}
const char *
ProcessGDBRemote : : GetDispatchQueueNameForThread
(
addr_t thread_dispatch_qaddr ,
std : : string & dispatch_queue_name
)
{
dispatch_queue_name . clear ( ) ;
if ( thread_dispatch_qaddr ! = 0 & & thread_dispatch_qaddr ! = LLDB_INVALID_ADDRESS )
{
// Cache the dispatch_queue_offsets_addr value so we don't always have
// to look it up
if ( m_dispatch_queue_offsets_addr = = LLDB_INVALID_ADDRESS )
{
ModuleSP module_sp ( GetTarget ( ) . GetImages ( ) . FindFirstModuleForFileSpec ( FileSpec ( " libSystem.B.dylib " ) ) ) ;
if ( module_sp . get ( ) = = NULL )
return NULL ;
const Symbol * dispatch_queue_offsets_symbol = module_sp - > FindFirstSymbolWithNameAndType ( ConstString ( " dispatch_queue_offsets " ) , eSymbolTypeData ) ;
if ( dispatch_queue_offsets_symbol )
m_dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol - > GetValue ( ) . GetLoadAddress ( this ) ;
if ( m_dispatch_queue_offsets_addr = = LLDB_INVALID_ADDRESS )
return NULL ;
}
uint8_t memory_buffer [ 8 ] ;
DataExtractor data ( memory_buffer , sizeof ( memory_buffer ) , GetByteOrder ( ) , GetAddressByteSize ( ) ) ;
// Excerpt from src/queue_private.h
struct dispatch_queue_offsets_s
{
uint16_t dqo_version ;
uint16_t dqo_label ;
uint16_t dqo_label_size ;
} dispatch_queue_offsets ;
Error error ;
if ( ReadMemory ( m_dispatch_queue_offsets_addr , memory_buffer , sizeof ( dispatch_queue_offsets ) , error ) = = sizeof ( dispatch_queue_offsets ) )
{
uint32_t data_offset = 0 ;
if ( data . GetU16 ( & data_offset , & dispatch_queue_offsets . dqo_version , sizeof ( dispatch_queue_offsets ) / sizeof ( uint16_t ) ) )
{
if ( ReadMemory ( thread_dispatch_qaddr , & memory_buffer , data . GetAddressByteSize ( ) , error ) = = data . GetAddressByteSize ( ) )
{
data_offset = 0 ;
lldb : : addr_t queue_addr = data . GetAddress ( & data_offset ) ;
lldb : : addr_t label_addr = queue_addr + dispatch_queue_offsets . dqo_label ;
dispatch_queue_name . resize ( dispatch_queue_offsets . dqo_label_size , ' \0 ' ) ;
size_t bytes_read = ReadMemory ( label_addr , & dispatch_queue_name [ 0 ] , dispatch_queue_offsets . dqo_label_size , error ) ;
if ( bytes_read < dispatch_queue_offsets . dqo_label_size )
dispatch_queue_name . erase ( bytes_read ) ;
}
}
}
}
if ( dispatch_queue_name . empty ( ) )
return NULL ;
return dispatch_queue_name . c_str ( ) ;
}
2010-08-09 23:31:02 +00:00
uint32_t
ProcessGDBRemote : : ListProcessesMatchingName ( const char * name , StringList & matches , std : : vector < lldb : : pid_t > & pids )
{
// If we are planning to launch the debugserver remotely, then we need to fire up a debugserver
// process and ask it for the list of processes. But if we are local, we can let the Host do it.
if ( m_local_debugserver )
{
return Host : : ListProcessesMatchingName ( name , matches , pids ) ;
}
else
{
// FIXME: Implement talking to the remote debugserver.
return 0 ;
}
}