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>
2011-03-25 18:16:28 +00:00
# include <stdlib.h>
2011-05-14 01:50:35 +00:00
# include <sys/mman.h> // for mmap
2010-06-08 16:52:24 +00:00
# include <sys/stat.h>
2011-05-14 01:50:35 +00:00
# include <sys/types.h>
2011-03-30 00:12:40 +00:00
# include <time.h>
2010-06-08 16:52:24 +00:00
// C++ Includes
# include <algorithm>
# include <map>
// Other libraries and framework includes
2011-10-14 00:42:25 +00:00
# include "lldb/Breakpoint/Watchpoint.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"
2011-02-08 05:05:52 +00:00
# include "lldb/Host/FileSpec.h"
2010-06-08 16:52:24 +00:00
# include "lldb/Core/InputReader.h"
# include "lldb/Core/Module.h"
# include "lldb/Core/PluginManager.h"
# include "lldb/Core/State.h"
2012-04-13 21:24:18 +00:00
# include "lldb/Core/StreamFile.h"
2010-06-08 16:52:24 +00:00
# include "lldb/Core/StreamString.h"
# include "lldb/Core/Timer.h"
2011-05-15 01:25:55 +00:00
# include "lldb/Core/Value.h"
2010-06-08 16:52:24 +00:00
# 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"
2011-05-14 01:50:35 +00:00
# include "lldb/Target/ThreadPlanCallFunction.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"
2011-06-03 20:40:38 +00:00
# include "Plugins/Process/Utility/InferiorCallPOSIX.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"
2010-08-04 01:40:35 +00:00
# include "StopInfoMachException.h"
2012-04-09 22:46:21 +00:00
namespace lldb
{
// Provide a function that can easily dump the packet history if we know a
// ProcessGDBRemote * value (which we can get from logs or from debugging).
// We need the function in the lldb namespace so it makes it into the final
// executable since the LLDB shared library only exports stuff in the lldb
// namespace. This allows you to attach with a debugger and call this
// function and get the packet history dumped to a file.
void
DumpProcessGDBRemotePacketHistory ( void * p , const char * path )
{
2012-04-13 21:24:18 +00:00
lldb_private : : StreamFile strm ;
lldb_private : : Error error ( strm . GetFile ( ) . Open ( path , lldb_private : : File : : eOpenOptionWrite | lldb_private : : File : : eOpenOptionCanCreate ) ) ;
if ( error . Success ( ) )
( ( ProcessGDBRemote * ) p ) - > GetGDBRemote ( ) . DumpHistory ( strm ) ;
2012-04-09 22:46:21 +00:00
}
} ;
2010-06-08 16:52:24 +00:00
# define DEBUGSERVER_BASENAME "debugserver"
using namespace lldb ;
using namespace lldb_private ;
2011-03-29 21:45:47 +00:00
static bool rand_initialized = false ;
2010-06-08 16:52:24 +00:00
static inline uint16_t
get_random_port ( )
{
2011-03-29 21:45:47 +00:00
if ( ! rand_initialized )
{
2011-03-30 00:12:40 +00:00
time_t seed = time ( NULL ) ;
2011-03-29 21:45:47 +00:00
rand_initialized = true ;
2011-03-30 00:12:40 +00:00
srand ( seed ) ;
2011-03-29 21:45:47 +00:00
}
2011-03-25 18:16:28 +00:00
return ( rand ( ) % ( UINT16_MAX - 1000u ) ) + 1000u ;
2010-06-08 16:52:24 +00:00
}
const char *
ProcessGDBRemote : : GetPluginNameStatic ( )
{
2011-03-19 01:12:21 +00:00
return " gdb-remote " ;
2010-06-08 16:52:24 +00:00
}
const char *
ProcessGDBRemote : : GetPluginDescriptionStatic ( )
{
return " GDB Remote protocol based debugging plug-in. " ;
}
void
ProcessGDBRemote : : Terminate ( )
{
PluginManager : : UnregisterPlugin ( ProcessGDBRemote : : CreateInstance ) ;
}
2012-02-09 06:16:32 +00:00
lldb : : ProcessSP
ProcessGDBRemote : : CreateInstance ( Target & target , Listener & listener , const FileSpec * crash_file_path )
2010-06-08 16:52:24 +00:00
{
2012-02-09 06:16:32 +00:00
lldb : : ProcessSP process_sp ;
if ( crash_file_path = = NULL )
process_sp . reset ( new ProcessGDBRemote ( target , listener ) ) ;
return process_sp ;
2010-06-08 16:52:24 +00:00
}
bool
2011-07-17 20:36:25 +00:00
ProcessGDBRemote : : CanDebug ( Target & target , bool plugin_specified_by_name )
2010-06-08 16:52:24 +00:00
{
2011-10-21 21:41:45 +00:00
if ( plugin_specified_by_name )
return true ;
2010-06-08 16:52:24 +00:00
// For now we are just making sure the file exists for a given module
2011-08-11 02:48:45 +00:00
Module * exe_module = target . GetExecutableModulePointer ( ) ;
if ( exe_module )
2012-02-09 06:16:32 +00:00
{
ObjectFile * exe_objfile = exe_module - > GetObjectFile ( ) ;
// We can't debug core files...
switch ( exe_objfile - > GetType ( ) )
{
case ObjectFile : : eTypeInvalid :
case ObjectFile : : eTypeCoreFile :
case ObjectFile : : eTypeDebugInfo :
case ObjectFile : : eTypeObjectFile :
case ObjectFile : : eTypeSharedLibrary :
case ObjectFile : : eTypeStubLibrary :
return false ;
case ObjectFile : : eTypeExecutable :
case ObjectFile : : eTypeDynamicLinker :
case ObjectFile : : eTypeUnknown :
break ;
}
2011-08-11 02:48:45 +00:00
return exe_module - > GetFileSpec ( ) . Exists ( ) ;
2012-02-09 06:16:32 +00:00
}
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_flags ( 0 ) ,
2011-04-12 05:54:46 +00:00
m_gdb_comm ( false ) ,
2010-06-08 16:52:24 +00:00
m_debugserver_pid ( LLDB_INVALID_PROCESS_ID ) ,
2010-07-09 20:39:50 +00:00
m_last_stop_packet ( ) ,
2011-12-06 04:51:14 +00:00
m_last_stop_packet_mutex ( Mutex : : eMutexTypeNormal ) ,
2010-06-08 16:52:24 +00:00
m_register_info ( ) ,
2012-02-16 06:50:00 +00:00
m_async_broadcaster ( NULL , " lldb.process.gdb-remote.async-broadcaster " ) ,
2010-06-08 16:52:24 +00:00
m_async_thread ( LLDB_INVALID_HOST_THREAD ) ,
2012-04-10 02:25:43 +00:00
m_thread_ids ( ) ,
2011-02-12 06:28:37 +00:00
m_continue_c_tids ( ) ,
m_continue_C_tids ( ) ,
m_continue_s_tids ( ) ,
m_continue_S_tids ( ) ,
2010-06-08 16:52:24 +00:00
m_dispatch_queue_offsets_addr ( LLDB_INVALID_ADDRESS ) ,
2010-07-09 20:39:50 +00:00
m_max_memory_size ( 512 ) ,
2010-08-09 23:31:02 +00:00
m_waiting_for_attach ( false ) ,
2011-01-22 01:33:44 +00:00
m_thread_observation_bps ( )
2010-06-08 16:52:24 +00:00
{
2011-04-01 00:29:43 +00:00
m_async_broadcaster . SetEventName ( eBroadcastBitAsyncThreadShouldExit , " async thread should exit " ) ;
m_async_broadcaster . SetEventName ( eBroadcastBitAsyncContinue , " async thread continue " ) ;
2012-04-12 18:49:31 +00:00
m_async_broadcaster . SetEventName ( eBroadcastBitAsyncThreadDidExit , " async thread did exit " ) ;
2010-06-08 16:52:24 +00:00
}
//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
ProcessGDBRemote : : ~ ProcessGDBRemote ( )
{
// m_mach_process.UnregisterNotificationCallbacks (this);
Clear ( ) ;
2011-10-01 00:45:15 +00:00
// We need to call finalize on the process before destroying ourselves
// to make sure all of the broadcaster cleanup goes as planned. If we
// destruct this class, then Process::~Process() might have problems
// trying to fully destroy the broadcaster.
Finalize ( ) ;
2010-06-08 16:52:24 +00:00
}
//----------------------------------------------------------------------
// 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
2011-01-29 07:10:55 +00:00
ProcessGDBRemote : : BuildDynamicRegisterInfo ( bool force )
2010-06-08 16:52:24 +00:00
{
2011-01-29 07:10:55 +00:00
if ( ! force & & m_register_info . GetNumRegisters ( ) > 0 )
return ;
char packet [ 128 ] ;
2010-06-08 16:52:24 +00:00
m_register_info . Clear ( ) ;
uint32_t reg_offset = 0 ;
uint32_t reg_num = 0 ;
2011-03-22 04:00:09 +00:00
StringExtractorGDBRemote : : ResponseType response_type ;
for ( response_type = StringExtractorGDBRemote : : eResponse ;
response_type = = StringExtractorGDBRemote : : eResponse ;
+ + reg_num )
2010-06-08 16:52:24 +00:00
{
2011-01-29 07:10:55 +00:00
const int packet_len = : : snprintf ( packet , sizeof ( packet ) , " qRegisterInfo%x " , reg_num ) ;
assert ( packet_len < sizeof ( packet ) ) ;
2010-06-08 16:52:24 +00:00
StringExtractorGDBRemote response ;
2011-03-10 02:26:48 +00:00
if ( m_gdb_comm . SendPacketAndWaitForResponse ( packet , packet_len , response , false ) )
2010-06-08 16:52:24 +00:00
{
2011-03-22 04:00:09 +00:00
response_type = response . GetResponseType ( ) ;
if ( response_type = = StringExtractorGDBRemote : : eResponse )
2010-06-08 16:52:24 +00:00
{
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
{
LLDB_INVALID_REGNUM , // GCC reg num
LLDB_INVALID_REGNUM , // DWARF reg num
LLDB_INVALID_REGNUM , // generic reg num
2010-09-10 07:49:16 +00:00
reg_num , // GDB reg num
reg_num // native register number
2012-02-29 19:27:27 +00:00
} ,
NULL ,
NULL
2010-06-08 16:52:24 +00:00
} ;
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 ;
2011-05-22 04:32:55 +00:00
else if ( value . find ( " arg " ) = = 0 )
{
if ( value . size ( ) = = 4 )
{
switch ( value [ 3 ] )
{
case ' 1 ' : reg_info . kinds [ eRegisterKindGeneric ] = LLDB_REGNUM_GENERIC_ARG1 ; break ;
case ' 2 ' : reg_info . kinds [ eRegisterKindGeneric ] = LLDB_REGNUM_GENERIC_ARG2 ; break ;
case ' 3 ' : reg_info . kinds [ eRegisterKindGeneric ] = LLDB_REGNUM_GENERIC_ARG3 ; break ;
case ' 4 ' : reg_info . kinds [ eRegisterKindGeneric ] = LLDB_REGNUM_GENERIC_ARG4 ; break ;
case ' 5 ' : reg_info . kinds [ eRegisterKindGeneric ] = LLDB_REGNUM_GENERIC_ARG5 ; break ;
case ' 6 ' : reg_info . kinds [ eRegisterKindGeneric ] = LLDB_REGNUM_GENERIC_ARG6 ; break ;
case ' 7 ' : reg_info . kinds [ eRegisterKindGeneric ] = LLDB_REGNUM_GENERIC_ARG7 ; break ;
case ' 8 ' : reg_info . kinds [ eRegisterKindGeneric ] = LLDB_REGNUM_GENERIC_ARG8 ; break ;
}
}
}
2010-06-08 16:52:24 +00:00
}
}
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
{
2011-03-22 04:00:09 +00:00
response_type = StringExtractorGDBRemote : : eError ;
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-30 18:16:51 +00:00
break ;
2010-06-08 16:52:24 +00:00
}
}
2012-05-14 18:44:23 +00:00
// We didn't get anything if the accumulated reg_num is zero. 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.
// On the other hand, if the accumulated reg_num is positive, see if we can
// add composite registers to the existing primordial ones.
bool from_scratch = ( reg_num = = 0 ) ;
const ArchSpec & target_arch = GetTarget ( ) . GetArchitecture ( ) ;
const ArchSpec & remote_arch = m_gdb_comm . GetHostArchitecture ( ) ;
if ( ! target_arch . IsValid ( ) )
2010-06-08 16:52:24 +00:00
{
2012-05-14 18:44:23 +00:00
if ( remote_arch . IsValid ( )
& & remote_arch . GetMachine ( ) = = llvm : : Triple : : arm
& & remote_arch . GetTriple ( ) . getVendor ( ) = = llvm : : Triple : : Apple )
m_register_info . HardcodeARMRegisters ( from_scratch ) ;
2010-06-08 16:52:24 +00:00
}
2012-05-14 18:44:23 +00:00
else if ( target_arch . GetMachine ( ) = = llvm : : Triple : : arm )
{
m_register_info . HardcodeARMRegisters ( from_scratch ) ;
}
// At this point, we can finalize our register info.
2010-06-08 16:52:24 +00:00
m_register_info . Finalize ( ) ;
}
Error
ProcessGDBRemote : : WillLaunch ( Module * module )
{
return WillLaunchOrAttach ( ) ;
}
Error
2010-11-18 05:57:03 +00:00
ProcessGDBRemote : : WillAttachToProcessWithID ( lldb : : pid_t pid )
2010-06-08 16:52:24 +00:00
{
return WillLaunchOrAttach ( ) ;
}
Error
2010-11-18 05:57:03 +00:00
ProcessGDBRemote : : WillAttachToProcessWithName ( const char * process_name , bool wait_for_launch )
2010-06-08 16:52:24 +00:00
{
return WillLaunchOrAttach ( ) ;
}
2011-02-04 01:58:07 +00:00
Error
ProcessGDBRemote : : DoConnectRemote ( const char * remote_url )
{
Error error ( WillLaunchOrAttach ( ) ) ;
if ( error . Fail ( ) )
return error ;
2011-04-30 01:09:13 +00:00
error = ConnectToDebugserver ( remote_url ) ;
2011-02-04 01:58:07 +00:00
if ( error . Fail ( ) )
return error ;
StartAsyncThread ( ) ;
2011-03-10 02:26:48 +00:00
lldb : : pid_t pid = m_gdb_comm . GetCurrentProcessID ( ) ;
2011-02-04 01:58:07 +00:00
if ( pid = = LLDB_INVALID_PROCESS_ID )
{
// We don't have a valid process ID, so note that we are connected
// and could now request to launch or attach, or get remote process
// listings...
SetPrivateState ( eStateConnected ) ;
}
else
{
// We have a valid process
SetID ( pid ) ;
2011-08-22 02:49:39 +00:00
GetThreadList ( ) ;
2011-06-02 22:22:38 +00:00
if ( m_gdb_comm . SendPacketAndWaitForResponse ( " ? " , 1 , m_last_stop_packet , false ) )
2011-02-04 01:58:07 +00:00
{
2011-06-02 22:22:38 +00:00
const StateType state = SetThreadStopInfo ( m_last_stop_packet ) ;
2011-02-04 01:58:07 +00:00
if ( state = = eStateStopped )
{
SetPrivateState ( state ) ;
}
else
2011-12-01 23:28:38 +00:00
error . SetErrorStringWithFormat ( " Process %llu was reported after connecting to '%s', but state was not stopped: %s " , pid , remote_url , StateAsCString ( state ) ) ;
2011-02-04 01:58:07 +00:00
}
else
2011-12-01 23:28:38 +00:00
error . SetErrorStringWithFormat ( " Process %llu was reported after connecting to '%s', but no stop reply packet was received " , pid , remote_url ) ;
2011-02-04 01:58:07 +00:00
}
2012-05-03 22:37:30 +00:00
if ( error . Success ( )
& & ! GetTarget ( ) . GetArchitecture ( ) . IsValid ( )
& & m_gdb_comm . GetHostArchitecture ( ) . IsValid ( ) )
{
GetTarget ( ) . SetArchitecture ( m_gdb_comm . GetHostArchitecture ( ) ) ;
}
2011-02-04 01:58:07 +00:00
return error ;
}
2010-06-08 16:52:24 +00:00
Error
ProcessGDBRemote : : WillLaunchOrAttach ( )
{
Error error ;
m_stdio_communication . Clear ( ) ;
return error ;
}
//----------------------------------------------------------------------
// Process Control
//----------------------------------------------------------------------
Error
2011-11-03 21:22:33 +00:00
ProcessGDBRemote : : DoLaunch ( Module * exe_module , const ProcessLaunchInfo & launch_info )
2010-06-08 16:52:24 +00:00
{
2010-09-30 21:49:03 +00:00
Error error ;
2011-11-03 21:22:33 +00:00
uint32_t launch_flags = launch_info . GetFlags ( ) . Get ( ) ;
const char * stdin_path = NULL ;
const char * stdout_path = NULL ;
const char * stderr_path = NULL ;
const char * working_dir = launch_info . GetWorkingDirectory ( ) ;
const ProcessLaunchInfo : : FileAction * file_action ;
file_action = launch_info . GetFileActionForFD ( STDIN_FILENO ) ;
if ( file_action )
{
if ( file_action - > GetAction ( ) = = ProcessLaunchInfo : : FileAction : : eFileActionOpen )
stdin_path = file_action - > GetPath ( ) ;
}
file_action = launch_info . GetFileActionForFD ( STDOUT_FILENO ) ;
if ( file_action )
{
if ( file_action - > GetAction ( ) = = ProcessLaunchInfo : : FileAction : : eFileActionOpen )
stdout_path = file_action - > GetPath ( ) ;
}
file_action = launch_info . GetFileActionForFD ( STDERR_FILENO ) ;
if ( file_action )
{
if ( file_action - > GetAction ( ) = = ProcessLaunchInfo : : FileAction : : eFileActionOpen )
stderr_path = file_action - > GetPath ( ) ;
}
2010-06-08 16:52:24 +00:00
// ::LogSetBitMask (GDBR_LOG_DEFAULT);
// ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD);
// ::LogSetLogFile ("/dev/stdout");
2011-08-09 05:20:29 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ) ;
2010-06-08 16:52:24 +00:00
2011-11-03 21:22:33 +00:00
ObjectFile * object_file = exe_module - > GetObjectFile ( ) ;
2010-06-08 16:52:24 +00:00
if ( object_file )
{
char host_port [ 128 ] ;
snprintf ( host_port , sizeof ( host_port ) , " localhost:%u " , get_random_port ( ) ) ;
2011-02-04 01:58:07 +00:00
char connect_url [ 128 ] ;
snprintf ( connect_url , sizeof ( connect_url ) , " connect://%s " , host_port ) ;
2010-06-08 16:52:24 +00:00
2011-02-24 22:24:29 +00:00
// Make sure we aren't already connected?
if ( ! m_gdb_comm . IsConnected ( ) )
2010-06-08 16:52:24 +00:00
{
2012-02-25 01:07:38 +00:00
error = StartDebugserverProcess ( host_port , launch_info ) ;
2010-06-08 16:52:24 +00:00
if ( error . Fail ( ) )
2011-08-09 05:20:29 +00:00
{
2011-08-09 18:56:45 +00:00
if ( log )
log - > Printf ( " failed to start debugserver process: %s " , error . AsCString ( ) ) ;
2010-06-08 16:52:24 +00:00
return error ;
2011-08-09 05:20:29 +00:00
}
2010-06-08 16:52:24 +00:00
2011-02-04 01:58:07 +00:00
error = ConnectToDebugserver ( connect_url ) ;
2011-02-24 22:24:29 +00:00
}
if ( error . Success ( ) )
{
lldb_utility : : PseudoTerminal pty ;
const bool disable_stdio = ( launch_flags & eLaunchFlagDisableSTDIO ) ! = 0 ;
2011-03-02 21:34:46 +00:00
// If the debugserver is local and we aren't disabling STDIO, lets use
// a pseudo terminal to instead of relying on the 'O' packets for stdio
// since 'O' packets can really slow down debugging if the inferior
// does a lot of output.
2011-06-24 22:32:10 +00:00
PlatformSP platform_sp ( m_target . GetPlatform ( ) ) ;
if ( platform_sp & & platform_sp - > IsHost ( ) & & ! disable_stdio )
2010-06-08 16:52:24 +00:00
{
2011-02-24 22:24:29 +00:00
const char * slave_name = NULL ;
if ( stdin_path = = NULL | | stdout_path = = NULL | | stderr_path = = NULL )
2010-06-08 16:52:24 +00:00
{
2011-02-24 22:24:29 +00:00
if ( pty . OpenFirstAvailableMaster ( O_RDWR | O_NOCTTY , NULL , 0 ) )
slave_name = pty . GetSlaveName ( NULL , 0 ) ;
2010-06-08 16:52:24 +00:00
}
2011-02-24 22:24:29 +00:00
if ( stdin_path = = NULL )
stdin_path = slave_name ;
if ( stdout_path = = NULL )
stdout_path = slave_name ;
2010-06-08 16:52:24 +00:00
2011-02-24 22:24:29 +00:00
if ( stderr_path = = NULL )
stderr_path = slave_name ;
}
2011-03-02 21:34:46 +00:00
// Set STDIN to /dev/null if we want STDIO disabled or if either
// STDOUT or STDERR have been set to something and STDIN hasn't
if ( disable_stdio | | ( stdin_path = = NULL & & ( stdout_path | | stderr_path ) ) )
2011-02-24 22:24:29 +00:00
stdin_path = " /dev/null " ;
2011-03-02 21:34:46 +00:00
// Set STDOUT to /dev/null if we want STDIO disabled or if either
// STDIN or STDERR have been set to something and STDOUT hasn't
if ( disable_stdio | | ( stdout_path = = NULL & & ( stdin_path | | stderr_path ) ) )
2011-02-24 22:24:29 +00:00
stdout_path = " /dev/null " ;
2011-03-02 21:34:46 +00:00
// Set STDERR to /dev/null if we want STDIO disabled or if either
// STDIN or STDOUT have been set to something and STDERR hasn't
if ( disable_stdio | | ( stderr_path = = NULL & & ( stdin_path | | stdout_path ) ) )
2011-02-24 22:24:29 +00:00
stderr_path = " /dev/null " ;
if ( stdin_path )
m_gdb_comm . SetSTDIN ( stdin_path ) ;
if ( stdout_path )
m_gdb_comm . SetSTDOUT ( stdout_path ) ;
if ( stderr_path )
m_gdb_comm . SetSTDERR ( stderr_path ) ;
m_gdb_comm . SetDisableASLR ( launch_flags & eLaunchFlagDisableASLR ) ;
2011-05-08 04:53:50 +00:00
m_gdb_comm . SendLaunchArchPacket ( m_target . GetArchitecture ( ) . GetArchitectureName ( ) ) ;
2011-02-24 22:24:29 +00:00
if ( working_dir & & working_dir [ 0 ] )
{
m_gdb_comm . SetWorkingDir ( working_dir ) ;
}
// Send the environment and the program + arguments after we connect
2011-11-03 21:22:33 +00:00
const Args & environment = launch_info . GetEnvironmentEntries ( ) ;
if ( environment . GetArgumentCount ( ) )
2011-02-24 22:24:29 +00:00
{
2011-11-03 21:22:33 +00:00
size_t num_environment_entries = environment . GetArgumentCount ( ) ;
for ( size_t i = 0 ; i < num_environment_entries ; + + i )
2010-08-03 00:35:52 +00:00
{
2011-11-03 21:22:33 +00:00
const char * env_entry = environment . GetArgumentAtIndex ( i ) ;
if ( env_entry = = NULL | | m_gdb_comm . SendEnvironmentPacket ( env_entry ) ! = 0 )
2011-02-24 22:24:29 +00:00
break ;
2010-08-03 00:35:52 +00:00
}
2011-02-24 22:24:29 +00:00
}
2010-08-03 00:35:52 +00:00
2011-03-10 02:26:48 +00:00
const uint32_t old_packet_timeout = m_gdb_comm . SetPacketTimeout ( 10 ) ;
2011-11-03 21:22:33 +00:00
int arg_packet_err = m_gdb_comm . SendArgumentsPacket ( launch_info . GetArguments ( ) . GetConstArgumentVector ( ) ) ;
2011-02-24 22:24:29 +00:00
if ( arg_packet_err = = 0 )
{
std : : string error_str ;
2011-03-10 02:26:48 +00:00
if ( m_gdb_comm . GetLaunchSuccess ( error_str ) )
2010-06-08 16:52:24 +00:00
{
2011-03-10 02:26:48 +00:00
SetID ( m_gdb_comm . GetCurrentProcessID ( ) ) ;
2010-06-08 16:52:24 +00:00
}
else
{
2011-02-24 22:24:29 +00:00
error . SetErrorString ( error_str . c_str ( ) ) ;
2010-06-08 16:52:24 +00:00
}
2011-02-24 22:24:29 +00:00
}
else
{
2011-10-26 00:56:27 +00:00
error . SetErrorStringWithFormat ( " 'A' packet returned an error: %i " , arg_packet_err ) ;
2011-02-24 22:24:29 +00:00
}
2011-08-10 22:05:39 +00:00
m_gdb_comm . SetPacketTimeout ( old_packet_timeout ) ;
2010-06-08 16:52:24 +00:00
2011-02-24 22:24:29 +00:00
if ( GetID ( ) = = LLDB_INVALID_PROCESS_ID )
{
2011-08-09 18:56:45 +00:00
if ( log )
log - > Printf ( " failed to connect to debugserver: %s " , error . AsCString ( ) ) ;
2011-02-24 22:24:29 +00:00
KillDebugserverProcess ( ) ;
return error ;
2010-06-08 16:52:24 +00:00
}
2011-06-02 22:22:38 +00:00
if ( m_gdb_comm . SendPacketAndWaitForResponse ( " ? " , 1 , m_last_stop_packet , false ) )
2011-02-24 22:24:29 +00:00
{
2011-06-02 22:22:38 +00:00
SetPrivateState ( SetThreadStopInfo ( m_last_stop_packet ) ) ;
2011-02-24 22:24:29 +00:00
if ( ! disable_stdio )
{
if ( pty . GetMasterFileDescriptor ( ) ! = lldb_utility : : PseudoTerminal : : invalid_fd )
2011-11-17 22:14:31 +00:00
SetSTDIOFileDescriptor ( pty . ReleaseMasterFileDescriptor ( ) ) ;
2011-02-24 22:24:29 +00:00
}
}
2010-06-08 16:52:24 +00:00
}
2011-08-09 05:20:29 +00:00
else
{
2011-08-09 18:56:45 +00:00
if ( log )
log - > Printf ( " failed to connect to debugserver: %s " , error . AsCString ( ) ) ;
2011-08-09 05:20:29 +00:00
}
2010-06-08 16:52:24 +00:00
}
else
{
// Set our user ID to an invalid process ID.
SetID ( LLDB_INVALID_PROCESS_ID ) ;
2011-11-03 21:22:33 +00:00
error . SetErrorStringWithFormat ( " failed to get object file from '%s' for arch %s " ,
exe_module - > GetFileSpec ( ) . GetFilename ( ) . AsCString ( ) ,
exe_module - > GetArchitecture ( ) . GetArchitectureName ( ) ) ;
2010-06-08 16:52:24 +00:00
}
return error ;
2010-09-30 21:49:03 +00:00
2010-06-08 16:52:24 +00:00
}
Error
2011-02-04 01:58:07 +00:00
ProcessGDBRemote : : ConnectToDebugserver ( const char * connect_url )
2010-06-08 16:52:24 +00:00
{
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 ( ) )
{
const uint32_t max_retry_count = 50 ;
uint32_t retry_count = 0 ;
while ( ! m_gdb_comm . IsConnected ( ) )
{
2011-02-04 01:58:07 +00:00
if ( conn_ap - > Connect ( connect_url , & error ) = = eConnectionStatusSuccess )
2010-06-08 16:52:24 +00:00
{
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 ;
}
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-30 18:16:51 +00:00
// We always seem to be able to open a connection to a local port
// so we need to make sure we can then send data to it. If we can't
// then we aren't actually connected to anything, so try and do the
// handshake with the remote GDB server and make sure that goes
// alright.
if ( ! m_gdb_comm . HandshakeWithServer ( NULL ) )
2010-06-08 16:52:24 +00:00
{
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-30 18:16:51 +00:00
m_gdb_comm . Disconnect ( ) ;
if ( error . Success ( ) )
error . SetErrorString ( " not connected to remote gdb server " ) ;
return error ;
2010-06-08 16:52:24 +00:00
}
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-30 18:16:51 +00:00
m_gdb_comm . ResetDiscoverableSettings ( ) ;
m_gdb_comm . QueryNoAckModeSupported ( ) ;
m_gdb_comm . GetThreadSuffixSupported ( ) ;
2012-04-10 03:22:03 +00:00
m_gdb_comm . GetListThreadsInStopReplySupported ( ) ;
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-30 18:16:51 +00:00
m_gdb_comm . GetHostInfo ( ) ;
m_gdb_comm . GetVContSupported ( ' c ' ) ;
2010-06-08 16:52:24 +00:00
return error ;
}
void
ProcessGDBRemote : : DidLaunchOrAttach ( )
{
2011-02-05 02:25:06 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ) ;
if ( log )
log - > Printf ( " ProcessGDBRemote::DidLaunch() " ) ;
2011-02-16 04:46:07 +00:00
if ( GetID ( ) ! = LLDB_INVALID_PROCESS_ID )
2010-06-08 16:52:24 +00:00
{
m_dispatch_queue_offsets_addr = LLDB_INVALID_ADDRESS ;
2011-01-29 07:10:55 +00:00
BuildDynamicRegisterInfo ( false ) ;
2010-11-18 05:57:03 +00:00
2010-06-08 16:52:24 +00:00
// See if the GDB server supports the qHostInfo information
2011-03-23 00:09:55 +00:00
const ArchSpec & gdb_remote_arch = m_gdb_comm . GetHostArchitecture ( ) ;
if ( gdb_remote_arch . IsValid ( ) )
2010-06-08 16:52:24 +00:00
{
2011-03-23 00:09:55 +00:00
ArchSpec & target_arch = GetTarget ( ) . GetArchitecture ( ) ;
if ( target_arch . IsValid ( ) )
{
// If the remote host is ARM and we have apple as the vendor, then
// ARM executables and shared libraries can have mixed ARM architectures.
// You can have an armv6 executable, and if the host is armv7, then the
// system will load the best possible architecture for all shared libraries
// it has, so we really need to take the remote host architecture as our
// defacto architecture in this case.
if ( gdb_remote_arch . GetMachine ( ) = = llvm : : Triple : : arm & &
gdb_remote_arch . GetTriple ( ) . getVendor ( ) = = llvm : : Triple : : Apple )
{
target_arch = gdb_remote_arch ;
}
else
{
// Fill in what is missing in the triple
const llvm : : Triple & remote_triple = gdb_remote_arch . GetTriple ( ) ;
llvm : : Triple & target_triple = target_arch . GetTriple ( ) ;
2011-05-15 01:25:55 +00:00
if ( target_triple . getVendorName ( ) . size ( ) = = 0 )
{
2011-03-23 00:09:55 +00:00
target_triple . setVendor ( remote_triple . getVendor ( ) ) ;
2011-05-15 01:25:55 +00:00
if ( target_triple . getOSName ( ) . size ( ) = = 0 )
{
target_triple . setOS ( remote_triple . getOS ( ) ) ;
2011-03-23 00:09:55 +00:00
2011-05-15 01:25:55 +00:00
if ( target_triple . getEnvironmentName ( ) . size ( ) = = 0 )
target_triple . setEnvironment ( remote_triple . getEnvironment ( ) ) ;
}
}
2011-03-23 00:09:55 +00:00
}
}
else
{
// The target doesn't have a valid architecture yet, set it from
// the architecture we got from the remote GDB server
target_arch = gdb_remote_arch ;
}
2010-06-08 16:52:24 +00:00
}
}
}
void
ProcessGDBRemote : : DidLaunch ( )
{
DidLaunchOrAttach ( ) ;
}
Error
2010-07-09 20:39:50 +00:00
ProcessGDBRemote : : DoAttachToProcessWithID ( lldb : : pid_t attach_pid )
2012-02-25 01:07:38 +00:00
{
ProcessAttachInfo attach_info ;
return DoAttachToProcessWithID ( attach_pid , attach_info ) ;
}
Error
ProcessGDBRemote : : DoAttachToProcessWithID ( lldb : : pid_t attach_pid , const ProcessAttachInfo & attach_info )
2010-06-08 16:52:24 +00:00
{
Error error ;
// Clear out and clean up from any current state
Clear ( ) ;
if ( attach_pid ! = LLDB_INVALID_PROCESS_ID )
{
2011-02-24 22:24:29 +00:00
// Make sure we aren't already connected?
if ( ! m_gdb_comm . IsConnected ( ) )
2010-06-08 16:52:24 +00:00
{
2011-02-24 22:24:29 +00:00
char host_port [ 128 ] ;
snprintf ( host_port , sizeof ( host_port ) , " localhost:%u " , get_random_port ( ) ) ;
char connect_url [ 128 ] ;
snprintf ( connect_url , sizeof ( connect_url ) , " connect://%s " , host_port ) ;
2012-02-25 01:07:38 +00:00
error = StartDebugserverProcess ( host_port , attach_info ) ;
2011-02-24 22:24:29 +00:00
if ( error . Fail ( ) )
{
const char * error_string = error . AsCString ( ) ;
if ( error_string = = NULL )
error_string = " unable to launch " DEBUGSERVER_BASENAME ;
2010-06-08 16:52:24 +00:00
2011-02-24 22:24:29 +00:00
SetExitStatus ( - 1 , error_string ) ;
}
else
2010-06-08 16:52:24 +00:00
{
2011-02-24 22:24:29 +00:00
error = ConnectToDebugserver ( connect_url ) ;
2010-06-08 16:52:24 +00:00
}
}
2011-02-24 22:24:29 +00:00
if ( error . Success ( ) )
{
char packet [ 64 ] ;
2011-12-01 23:28:38 +00:00
const int packet_len = : : snprintf ( packet , sizeof ( packet ) , " vAttach;%llx " , attach_pid ) ;
2011-11-19 02:11:30 +00:00
SetID ( attach_pid ) ;
2011-02-24 22:24:29 +00:00
m_async_broadcaster . BroadcastEvent ( eBroadcastBitAsyncContinue , new EventDataBytes ( packet , packet_len ) ) ;
}
2010-06-08 16:52:24 +00:00
}
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
2012-02-25 01:07:38 +00:00
ProcessGDBRemote : : DoAttachToProcessWithName ( const char * process_name , bool wait_for_launch , const ProcessAttachInfo & attach_info )
2010-06-08 16:52:24 +00:00
{
Error error ;
// Clear out and clean up from any current state
Clear ( ) ;
if ( process_name & & process_name [ 0 ] )
{
2011-02-24 22:24:29 +00:00
// Make sure we aren't already connected?
if ( ! m_gdb_comm . IsConnected ( ) )
{
char host_port [ 128 ] ;
snprintf ( host_port , sizeof ( host_port ) , " localhost:%u " , get_random_port ( ) ) ;
char connect_url [ 128 ] ;
snprintf ( connect_url , sizeof ( connect_url ) , " connect://%s " , host_port ) ;
2010-06-08 16:52:24 +00:00
2012-02-25 01:07:38 +00:00
error = StartDebugserverProcess ( host_port , attach_info ) ;
2011-02-24 22:24:29 +00:00
if ( error . Fail ( ) )
2010-06-08 16:52:24 +00:00
{
2011-02-24 22:24:29 +00:00
const char * error_string = error . AsCString ( ) ;
if ( error_string = = NULL )
error_string = " unable to launch " DEBUGSERVER_BASENAME ;
2010-06-08 16:52:24 +00:00
2011-02-24 22:24:29 +00:00
SetExitStatus ( - 1 , error_string ) ;
}
else
{
error = ConnectToDebugserver ( connect_url ) ;
2010-06-08 16:52:24 +00:00
}
}
2011-02-24 22:24:29 +00:00
if ( error . Success ( ) )
{
StreamString packet ;
if ( wait_for_launch )
packet . PutCString ( " vAttachWait " ) ;
else
packet . PutCString ( " vAttachName " ) ;
packet . PutChar ( ' ; ' ) ;
packet . PutBytesAsRawHex8 ( process_name , strlen ( process_name ) , lldb : : endian : : InlHostByteOrder ( ) , lldb : : endian : : InlHostByteOrder ( ) ) ;
m_async_broadcaster . BroadcastEvent ( eBroadcastBitAsyncContinue , new EventDataBytes ( packet . GetData ( ) , packet . GetSize ( ) ) ) ;
}
2010-06-08 16:52:24 +00:00
}
return error ;
}
void
ProcessGDBRemote : : DidAttach ( )
{
2011-02-04 01:58:07 +00:00
DidLaunchOrAttach ( ) ;
2010-06-08 16:52:24 +00:00
}
Error
ProcessGDBRemote : : WillResume ( )
{
2011-02-12 06:28:37 +00:00
m_continue_c_tids . clear ( ) ;
m_continue_C_tids . clear ( ) ;
m_continue_s_tids . clear ( ) ;
m_continue_S_tids . clear ( ) ;
2010-06-08 16:52:24 +00:00
return Error ( ) ;
}
Error
ProcessGDBRemote : : DoResume ( )
{
2010-11-17 02:32:00 +00:00
Error error ;
2011-02-05 02:25:06 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ) ;
if ( log )
log - > Printf ( " ProcessGDBRemote::Resume() " ) ;
2010-12-03 06:02:24 +00:00
Listener listener ( " gdb-remote.resume-packet-sent " ) ;
if ( listener . StartListeningForEvents ( & m_gdb_comm , GDBRemoteCommunication : : eBroadcastBitRunPacketSent ) )
{
2012-04-12 18:49:31 +00:00
listener . StartListeningForEvents ( & m_async_broadcaster , ProcessGDBRemote : : eBroadcastBitAsyncThreadDidExit ) ;
2011-02-12 06:28:37 +00:00
StreamString continue_packet ;
bool continue_packet_error = false ;
if ( m_gdb_comm . HasAnyVContSupport ( ) )
{
continue_packet . PutCString ( " vCont " ) ;
if ( ! m_continue_c_tids . empty ( ) )
{
if ( m_gdb_comm . GetVContSupported ( ' c ' ) )
{
for ( tid_collection : : const_iterator t_pos = m_continue_c_tids . begin ( ) , t_end = m_continue_c_tids . end ( ) ; t_pos ! = t_end ; + + t_pos )
2011-12-01 23:28:38 +00:00
continue_packet . Printf ( " ;c:%4.4llx " , * t_pos ) ;
2011-02-12 06:28:37 +00:00
}
else
continue_packet_error = true ;
}
if ( ! continue_packet_error & & ! m_continue_C_tids . empty ( ) )
{
if ( m_gdb_comm . GetVContSupported ( ' C ' ) )
{
for ( tid_sig_collection : : const_iterator s_pos = m_continue_C_tids . begin ( ) , s_end = m_continue_C_tids . end ( ) ; s_pos ! = s_end ; + + s_pos )
2011-12-01 23:28:38 +00:00
continue_packet . Printf ( " ;C%2.2x:%4.4llx " , s_pos - > second , s_pos - > first ) ;
2011-02-12 06:28:37 +00:00
}
else
continue_packet_error = true ;
}
if ( ! continue_packet_error & & ! m_continue_s_tids . empty ( ) )
{
if ( m_gdb_comm . GetVContSupported ( ' s ' ) )
{
for ( tid_collection : : const_iterator t_pos = m_continue_s_tids . begin ( ) , t_end = m_continue_s_tids . end ( ) ; t_pos ! = t_end ; + + t_pos )
2011-12-01 23:28:38 +00:00
continue_packet . Printf ( " ;s:%4.4llx " , * t_pos ) ;
2011-02-12 06:28:37 +00:00
}
else
continue_packet_error = true ;
}
if ( ! continue_packet_error & & ! m_continue_S_tids . empty ( ) )
{
if ( m_gdb_comm . GetVContSupported ( ' S ' ) )
{
for ( tid_sig_collection : : const_iterator s_pos = m_continue_S_tids . begin ( ) , s_end = m_continue_S_tids . end ( ) ; s_pos ! = s_end ; + + s_pos )
2011-12-01 23:28:38 +00:00
continue_packet . Printf ( " ;S%2.2x:%4.4llx " , s_pos - > second , s_pos - > first ) ;
2011-02-12 06:28:37 +00:00
}
else
continue_packet_error = true ;
}
if ( continue_packet_error )
continue_packet . GetString ( ) . clear ( ) ;
}
else
continue_packet_error = true ;
if ( continue_packet_error )
{
// Either no vCont support, or we tried to use part of the vCont
// packet that wasn't supported by the remote GDB server.
// We need to try and make a simple packet that can do our continue
const size_t num_threads = GetThreadList ( ) . GetSize ( ) ;
const size_t num_continue_c_tids = m_continue_c_tids . size ( ) ;
const size_t num_continue_C_tids = m_continue_C_tids . size ( ) ;
const size_t num_continue_s_tids = m_continue_s_tids . size ( ) ;
const size_t num_continue_S_tids = m_continue_S_tids . size ( ) ;
if ( num_continue_c_tids > 0 )
{
if ( num_continue_c_tids = = num_threads )
{
// All threads are resuming...
2011-04-12 05:54:46 +00:00
m_gdb_comm . SetCurrentThreadForRun ( - 1 ) ;
2011-06-24 03:21:43 +00:00
continue_packet . PutChar ( ' c ' ) ;
continue_packet_error = false ;
2011-02-12 06:28:37 +00:00
}
else if ( num_continue_c_tids = = 1 & &
num_continue_C_tids = = 0 & &
num_continue_s_tids = = 0 & &
num_continue_S_tids = = 0 )
{
// Only one thread is continuing
2011-04-12 05:54:46 +00:00
m_gdb_comm . SetCurrentThreadForRun ( m_continue_c_tids . front ( ) ) ;
2011-02-12 06:28:37 +00:00
continue_packet . PutChar ( ' c ' ) ;
2011-06-24 03:21:43 +00:00
continue_packet_error = false ;
2011-02-12 06:28:37 +00:00
}
}
2011-06-24 03:21:43 +00:00
if ( continue_packet_error & & num_continue_C_tids > 0 )
2011-02-12 06:28:37 +00:00
{
2011-06-24 03:21:43 +00:00
if ( ( num_continue_C_tids + num_continue_c_tids ) = = num_threads & &
num_continue_C_tids > 0 & &
num_continue_s_tids = = 0 & &
num_continue_S_tids = = 0 )
2011-02-12 06:28:37 +00:00
{
const int continue_signo = m_continue_C_tids . front ( ) . second ;
2011-06-24 03:21:43 +00:00
// Only one thread is continuing
2011-02-12 06:28:37 +00:00
if ( num_continue_C_tids > 1 )
{
2011-06-24 03:21:43 +00:00
// More that one thread with a signal, yet we don't have
// vCont support and we are being asked to resume each
// thread with a signal, we need to make sure they are
// all the same signal, or we can't issue the continue
// accurately with the current support...
if ( num_continue_C_tids > 1 )
2011-02-12 06:28:37 +00:00
{
2011-06-24 03:21:43 +00:00
continue_packet_error = false ;
for ( size_t i = 1 ; i < m_continue_C_tids . size ( ) ; + + i )
{
if ( m_continue_C_tids [ i ] . second ! = continue_signo )
continue_packet_error = true ;
}
2011-02-12 06:28:37 +00:00
}
2011-06-24 03:21:43 +00:00
if ( ! continue_packet_error )
m_gdb_comm . SetCurrentThreadForRun ( - 1 ) ;
}
else
{
// Set the continue thread ID
continue_packet_error = false ;
m_gdb_comm . SetCurrentThreadForRun ( m_continue_C_tids . front ( ) . first ) ;
2011-02-12 06:28:37 +00:00
}
if ( ! continue_packet_error )
{
// Add threads continuing with the same signo...
continue_packet . Printf ( " C%2.2x " , continue_signo ) ;
}
}
}
2011-06-24 03:21:43 +00:00
if ( continue_packet_error & & num_continue_s_tids > 0 )
2011-02-12 06:28:37 +00:00
{
if ( num_continue_s_tids = = num_threads )
{
// All threads are resuming...
2011-04-12 05:54:46 +00:00
m_gdb_comm . SetCurrentThreadForRun ( - 1 ) ;
2011-06-24 03:21:43 +00:00
continue_packet . PutChar ( ' s ' ) ;
continue_packet_error = false ;
2011-02-12 06:28:37 +00:00
}
else if ( num_continue_c_tids = = 0 & &
num_continue_C_tids = = 0 & &
num_continue_s_tids = = 1 & &
num_continue_S_tids = = 0 )
{
// Only one thread is stepping
2011-04-12 05:54:46 +00:00
m_gdb_comm . SetCurrentThreadForRun ( m_continue_s_tids . front ( ) ) ;
2011-02-12 06:28:37 +00:00
continue_packet . PutChar ( ' s ' ) ;
2011-06-24 03:21:43 +00:00
continue_packet_error = false ;
2011-02-12 06:28:37 +00:00
}
}
if ( ! continue_packet_error & & num_continue_S_tids > 0 )
{
if ( num_continue_S_tids = = num_threads )
{
const int step_signo = m_continue_S_tids . front ( ) . second ;
// Are all threads trying to step with the same signal?
2011-06-24 03:21:43 +00:00
continue_packet_error = false ;
2011-02-12 06:28:37 +00:00
if ( num_continue_S_tids > 1 )
{
for ( size_t i = 1 ; i < num_threads ; + + i )
{
if ( m_continue_S_tids [ i ] . second ! = step_signo )
continue_packet_error = true ;
}
}
if ( ! continue_packet_error )
{
// Add threads stepping with the same signo...
2011-04-12 05:54:46 +00:00
m_gdb_comm . SetCurrentThreadForRun ( - 1 ) ;
2011-02-12 06:28:37 +00:00
continue_packet . Printf ( " S%2.2x " , step_signo ) ;
}
}
else if ( num_continue_c_tids = = 0 & &
num_continue_C_tids = = 0 & &
num_continue_s_tids = = 0 & &
num_continue_S_tids = = 1 )
{
// Only one thread is stepping with signal
2011-04-12 05:54:46 +00:00
m_gdb_comm . SetCurrentThreadForRun ( m_continue_S_tids . front ( ) . first ) ;
2011-02-12 06:28:37 +00:00
continue_packet . Printf ( " S%2.2x " , m_continue_S_tids . front ( ) . second ) ;
2011-06-24 03:21:43 +00:00
continue_packet_error = false ;
2011-02-12 06:28:37 +00:00
}
}
}
if ( continue_packet_error )
{
error . SetErrorString ( " can't make continue packet for this resume " ) ;
}
else
{
EventSP event_sp ;
TimeValue timeout ;
timeout = TimeValue : : Now ( ) ;
timeout . OffsetWithSeconds ( 5 ) ;
2012-04-12 18:49:31 +00:00
if ( ! IS_VALID_LLDB_HOST_THREAD ( m_async_thread ) )
{
error . SetErrorString ( " Trying to resume but the async thread is dead. " ) ;
if ( log )
log - > Printf ( " ProcessGDBRemote::DoResume: Trying to resume but the async thread is dead. " ) ;
return error ;
}
2011-02-12 06:28:37 +00:00
m_async_broadcaster . BroadcastEvent ( eBroadcastBitAsyncContinue , new EventDataBytes ( continue_packet . GetData ( ) , continue_packet . GetSize ( ) ) ) ;
if ( listener . WaitForEvent ( & timeout , event_sp ) = = false )
2012-04-12 18:49:31 +00:00
{
2011-02-12 06:28:37 +00:00
error . SetErrorString ( " Resume timed out. " ) ;
2012-04-12 18:49:31 +00:00
if ( log )
log - > Printf ( " ProcessGDBRemote::DoResume: Resume timed out. " ) ;
}
else if ( event_sp - > BroadcasterIs ( & m_async_broadcaster ) )
{
error . SetErrorString ( " Broadcast continue, but the async thread was killed before we got an ack back. " ) ;
if ( log )
log - > Printf ( " ProcessGDBRemote::DoResume: Broadcast continue, but the async thread was killed before we got an ack back. " ) ;
return error ;
}
2011-02-12 06:28:37 +00:00
}
2010-12-03 06:02:24 +00:00
}
2010-11-17 02:32:00 +00:00
return error ;
2010-06-08 16:52:24 +00:00
}
2012-04-10 02:25:43 +00:00
void
ProcessGDBRemote : : ClearThreadIDList ( )
{
2012-04-13 02:11:32 +00:00
Mutex : : Locker locker ( m_thread_list . GetMutex ( ) ) ;
2012-04-10 02:25:43 +00:00
m_thread_ids . clear ( ) ;
}
bool
ProcessGDBRemote : : UpdateThreadIDList ( )
{
2012-04-13 02:11:32 +00:00
Mutex : : Locker locker ( m_thread_list . GetMutex ( ) ) ;
2012-04-10 02:25:43 +00:00
bool sequence_mutex_unavailable = false ;
m_gdb_comm . GetCurrentThreadIDs ( m_thread_ids , sequence_mutex_unavailable ) ;
if ( sequence_mutex_unavailable )
{
# if defined (LLDB_CONFIGURATION_DEBUG)
assert ( ! " ProcessGDBRemote::UpdateThreadList() failed due to not getting the sequence mutex " ) ;
# endif
return false ; // We just didn't get the list
}
return true ;
}
2012-04-10 00:18:59 +00:00
bool
2011-08-22 02:49:39 +00:00
ProcessGDBRemote : : UpdateThreadList ( ThreadList & old_thread_list , ThreadList & new_thread_list )
2010-06-08 16:52:24 +00:00
{
// locker will keep a mutex locked until it goes out of scope
2010-11-06 01:53:30 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_THREAD ) ) ;
2010-10-27 03:32:59 +00:00
if ( log & & log - > GetMask ( ) . Test ( GDBR_LOG_VERBOSE ) )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::%s (pid = %llu) " , __FUNCTION__ , GetID ( ) ) ;
2012-04-10 02:25:43 +00:00
size_t num_thread_ids = m_thread_ids . size ( ) ;
// The "m_thread_ids" thread ID list should always be updated after each stop
// reply packet, but in case it isn't, update it here.
if ( num_thread_ids = = 0 )
{
if ( ! UpdateThreadIDList ( ) )
return false ;
num_thread_ids = m_thread_ids . size ( ) ;
}
2010-06-08 16:52:24 +00:00
2011-08-22 02:49:39 +00:00
if ( num_thread_ids > 0 )
2010-06-08 16:52:24 +00:00
{
2011-08-22 02:49:39 +00:00
for ( size_t i = 0 ; i < num_thread_ids ; + + i )
2010-06-08 16:52:24 +00:00
{
2012-04-10 02:25:43 +00:00
tid_t tid = m_thread_ids [ i ] ;
2011-08-22 02:49:39 +00:00
ThreadSP thread_sp ( old_thread_list . FindThreadByID ( tid , false ) ) ;
if ( ! thread_sp )
2012-02-21 00:09:25 +00:00
thread_sp . reset ( new ThreadGDBRemote ( shared_from_this ( ) , tid ) ) ;
2011-08-22 02:49:39 +00:00
new_thread_list . AddThread ( thread_sp ) ;
2011-05-20 23:38:13 +00:00
}
2010-06-08 16:52:24 +00:00
}
2011-08-22 02:49:39 +00:00
2012-04-10 00:18:59 +00:00
return true ;
2010-06-08 16:52:24 +00:00
}
StateType
ProcessGDBRemote : : SetThreadStopInfo ( StringExtractor & stop_packet )
{
2011-06-02 22:22:38 +00:00
stop_packet . SetFilePos ( 0 ) ;
2010-06-08 16:52:24 +00:00
const char stop_type = stop_packet . GetChar ( ) ;
switch ( stop_type )
{
case ' T ' :
case ' S ' :
{
2011-02-15 00:19:15 +00:00
if ( GetStopID ( ) = = 0 )
{
// Our first stop, make sure we have a process ID, and also make
// sure we know about our registers
if ( GetID ( ) = = LLDB_INVALID_PROCESS_ID )
{
2011-03-10 02:26:48 +00:00
lldb : : pid_t pid = m_gdb_comm . GetCurrentProcessID ( ) ;
2011-02-15 00:19:15 +00:00
if ( pid ! = LLDB_INVALID_PROCESS_ID )
SetID ( pid ) ;
}
BuildDynamicRegisterInfo ( true ) ;
}
2010-06-08 16:52:24 +00:00
// Stop with signal and thread info
const uint8_t signo = stop_packet . GetHexU8 ( ) ;
std : : string name ;
std : : string value ;
std : : string thread_name ;
2011-06-04 01:26:29 +00:00
std : : string reason ;
std : : string description ;
2010-06-08 16:52:24 +00:00
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
addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS ;
uint32_t exc_data_count = 0 ;
2011-01-09 21:07:35 +00:00
ThreadSP thread_sp ;
2010-06-08 16:52:24 +00:00
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
2012-04-10 02:25:43 +00:00
lldb : : tid_t tid = Args : : StringToUInt64 ( value . c_str ( ) , LLDB_INVALID_THREAD_ID , 16 ) ;
2011-11-17 04:46:02 +00:00
// m_thread_list does have its own mutex, but we need to
// hold onto the mutex between the call to m_thread_list.FindThreadByID(...)
// and the m_thread_list.AddThread(...) so it doesn't change on us
2011-02-15 00:19:15 +00:00
Mutex : : Locker locker ( m_thread_list . GetMutex ( ) ) ;
2011-01-09 21:07:35 +00:00
thread_sp = m_thread_list . FindThreadByID ( tid , false ) ;
2011-02-15 00:19:15 +00:00
if ( ! thread_sp )
{
// Create the thread if we need to
2012-02-21 00:09:25 +00:00
thread_sp . reset ( new ThreadGDBRemote ( shared_from_this ( ) , tid ) ) ;
2011-02-15 00:19:15 +00:00
m_thread_list . AddThread ( thread_sp ) ;
}
2010-06-08 16:52:24 +00:00
}
2012-04-10 02:25:43 +00:00
else if ( name . compare ( " threads " ) = = 0 )
{
2012-04-13 02:11:32 +00:00
Mutex : : Locker locker ( m_thread_list . GetMutex ( ) ) ;
2012-04-10 02:25:43 +00:00
m_thread_ids . clear ( ) ;
2012-04-10 03:22:03 +00:00
// A comma separated list of all threads in the current
// process that includes the thread for this stop reply
// packet
2012-04-10 02:25:43 +00:00
size_t comma_pos ;
lldb : : tid_t tid ;
while ( ( comma_pos = value . find ( ' , ' ) ) ! = std : : string : : npos )
{
value [ comma_pos ] = ' \0 ' ;
// thread in big endian hex
tid = Args : : StringToUInt64 ( value . c_str ( ) , LLDB_INVALID_THREAD_ID , 16 ) ;
if ( tid ! = LLDB_INVALID_THREAD_ID )
m_thread_ids . push_back ( tid ) ;
value . erase ( 0 , comma_pos + 1 ) ;
}
tid = Args : : StringToUInt64 ( value . c_str ( ) , LLDB_INVALID_THREAD_ID , 16 ) ;
if ( tid ! = LLDB_INVALID_THREAD_ID )
m_thread_ids . push_back ( tid ) ;
}
2011-01-08 03:17:57 +00:00
else if ( name . compare ( " hexname " ) = = 0 )
{
StringExtractor name_extractor ;
// Swap "value" over into "name_extractor"
name_extractor . GetStringRef ( ) . swap ( value ) ;
// Now convert the HEX bytes into a string value
name_extractor . GetHexByteString ( value ) ;
thread_name . swap ( value ) ;
}
2010-06-08 16:52:24 +00:00
else if ( name . compare ( " name " ) = = 0 )
{
thread_name . swap ( value ) ;
}
2010-09-09 06:32:46 +00:00
else if ( name . compare ( " qaddr " ) = = 0 )
2010-06-08 16:52:24 +00:00
{
thread_dispatch_qaddr = Args : : StringToUInt64 ( value . c_str ( ) , 0 , 16 ) ;
}
2011-06-04 01:26:29 +00:00
else if ( name . compare ( " reason " ) = = 0 )
{
reason . swap ( value ) ;
}
else if ( name . compare ( " description " ) = = 0 )
{
StringExtractor desc_extractor ;
// Swap "value" over into "name_extractor"
desc_extractor . GetStringRef ( ) . swap ( value ) ;
// Now convert the HEX bytes into a string value
desc_extractor . GetHexByteString ( thread_name ) ;
}
2011-01-09 21:07:35 +00:00
else if ( name . size ( ) = = 2 & & : : isxdigit ( name [ 0 ] ) & & : : isxdigit ( name [ 1 ] ) )
{
// We have a register number that contains an expedited
// register value. Lets supply this register to our thread
// so it won't have to go and read it.
if ( thread_sp )
{
uint32_t reg = Args : : StringToUInt32 ( name . c_str ( ) , UINT32_MAX , 16 ) ;
if ( reg ! = UINT32_MAX )
{
StringExtractor reg_value_extractor ;
// Swap "value" over into "reg_value_extractor"
reg_value_extractor . GetStringRef ( ) . swap ( value ) ;
2011-02-15 00:19:15 +00:00
if ( ! static_cast < ThreadGDBRemote * > ( thread_sp . get ( ) ) - > PrivateSetRegisterValue ( reg , reg_value_extractor ) )
{
Host : : SetCrashDescriptionWithFormat ( " Setting thread register '%s' (decoded to %u (0x%x)) with value '%s' for stop packet: '%s' " ,
name . c_str ( ) ,
reg ,
reg ,
reg_value_extractor . GetStringRef ( ) . c_str ( ) ,
stop_packet . GetStringRef ( ) . c_str ( ) ) ;
}
2011-01-09 21:07:35 +00:00
}
}
}
2010-06-08 16:52:24 +00:00
}
if ( thread_sp )
{
ThreadGDBRemote * gdb_thread = static_cast < ThreadGDBRemote * > ( thread_sp . get ( ) ) ;
gdb_thread - > SetThreadDispatchQAddr ( thread_dispatch_qaddr ) ;
2011-01-28 02:23:12 +00:00
gdb_thread - > SetName ( thread_name . empty ( ) ? NULL : thread_name . c_str ( ) ) ;
2010-06-08 16:52:24 +00:00
if ( exc_type ! = 0 )
{
2011-01-25 23:55:37 +00:00
const size_t exc_data_size = exc_data . size ( ) ;
2010-08-04 01:40:35 +00:00
gdb_thread - > SetStopInfo ( StopInfoMachException : : CreateStopReasonWithMachException ( * thread_sp ,
exc_type ,
2011-01-25 23:55:37 +00:00
exc_data_size ,
exc_data_size > = 1 ? exc_data [ 0 ] : 0 ,
2011-09-17 01:05:03 +00:00
exc_data_size > = 2 ? exc_data [ 1 ] : 0 ,
exc_data_size > = 3 ? exc_data [ 2 ] : 0 ) ) ;
2010-06-08 16:52:24 +00:00
}
2011-06-04 01:26:29 +00:00
else
2010-06-08 16:52:24 +00:00
{
2011-06-04 01:26:29 +00:00
bool handled = false ;
if ( ! reason . empty ( ) )
{
if ( reason . compare ( " trace " ) = = 0 )
{
gdb_thread - > SetStopInfo ( StopInfo : : CreateStopReasonToTrace ( * thread_sp ) ) ;
handled = true ;
}
else if ( reason . compare ( " breakpoint " ) = = 0 )
{
addr_t pc = gdb_thread - > GetRegisterContext ( ) - > GetPC ( ) ;
2012-02-21 00:09:25 +00:00
lldb : : BreakpointSiteSP bp_site_sp = gdb_thread - > GetProcess ( ) - > GetBreakpointSiteList ( ) . FindByAddress ( pc ) ;
2011-06-04 01:26:29 +00:00
if ( bp_site_sp )
{
// If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread,
// we can just report no reason. We don't need to worry about stepping over the breakpoint here, that
// will be taken care of when the thread resumes and notices that there's a breakpoint under the pc.
if ( bp_site_sp - > ValidForThisThread ( gdb_thread ) )
{
gdb_thread - > SetStopInfo ( StopInfo : : CreateStopReasonWithBreakpointSiteID ( * thread_sp , bp_site_sp - > GetID ( ) ) ) ;
handled = true ;
}
}
if ( ! handled )
{
gdb_thread - > SetStopInfo ( StopInfo : : CreateStopReasonToTrace ( * thread_sp ) ) ;
}
}
else if ( reason . compare ( " trap " ) = = 0 )
{
// Let the trap just use the standard signal stop reason below...
}
else if ( reason . compare ( " watchpoint " ) = = 0 )
{
break_id_t watch_id = LLDB_INVALID_WATCH_ID ;
// TODO: locate the watchpoint somehow...
gdb_thread - > SetStopInfo ( StopInfo : : CreateStopReasonWithWatchpointID ( * thread_sp , watch_id ) ) ;
handled = true ;
}
else if ( reason . compare ( " exception " ) = = 0 )
{
gdb_thread - > SetStopInfo ( StopInfo : : CreateStopReasonWithException ( * thread_sp , description . c_str ( ) ) ) ;
handled = true ;
}
}
if ( signo )
{
if ( signo = = SIGTRAP )
{
// Currently we are going to assume SIGTRAP means we are either
// hitting a breakpoint or hardware single stepping.
addr_t pc = gdb_thread - > GetRegisterContext ( ) - > GetPC ( ) ;
2012-02-21 00:09:25 +00:00
lldb : : BreakpointSiteSP bp_site_sp = gdb_thread - > GetProcess ( ) - > GetBreakpointSiteList ( ) . FindByAddress ( pc ) ;
2011-06-04 01:26:29 +00:00
if ( bp_site_sp )
{
// If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread,
// we can just report no reason. We don't need to worry about stepping over the breakpoint here, that
// will be taken care of when the thread resumes and notices that there's a breakpoint under the pc.
if ( bp_site_sp - > ValidForThisThread ( gdb_thread ) )
{
gdb_thread - > SetStopInfo ( StopInfo : : CreateStopReasonWithBreakpointSiteID ( * thread_sp , bp_site_sp - > GetID ( ) ) ) ;
handled = true ;
}
}
if ( ! handled )
{
// TODO: check for breakpoint or trap opcode in case there is a hard
// coded software trap
gdb_thread - > SetStopInfo ( StopInfo : : CreateStopReasonToTrace ( * thread_sp ) ) ;
handled = true ;
}
}
if ( ! handled )
2011-08-22 02:49:39 +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
}
2011-06-04 01:26:29 +00:00
if ( ! description . empty ( ) )
{
lldb : : StopInfoSP stop_info_sp ( gdb_thread - > GetStopInfo ( ) ) ;
if ( stop_info_sp )
{
stop_info_sp - > SetDescription ( description . c_str ( ) ) ;
2011-08-10 02:10:13 +00:00
}
2011-06-04 01:26:29 +00:00
else
{
gdb_thread - > SetStopInfo ( StopInfo : : CreateStopReasonWithException ( * thread_sp , description . c_str ( ) ) ) ;
}
}
}
2010-06-08 16:52:24 +00:00
}
return eStateStopped ;
}
break ;
case ' W ' :
// process exited
return eStateExited ;
default :
break ;
}
return eStateInvalid ;
}
void
ProcessGDBRemote : : RefreshStateAfterStop ( )
{
2012-04-13 02:11:32 +00:00
Mutex : : Locker locker ( m_thread_list . GetMutex ( ) ) ;
2012-04-10 02:25:43 +00:00
m_thread_ids . clear ( ) ;
// Set the thread stop info. It might have a "threads" key whose value is
// a list of all thread IDs in the current process, so m_thread_ids might
// get set.
SetThreadStopInfo ( m_last_stop_packet ) ;
// Check to see if SetThreadStopInfo() filled in m_thread_ids?
if ( m_thread_ids . empty ( ) )
{
// No, we need to fetch the thread list manually
UpdateThreadIDList ( ) ;
}
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 ( ) ;
2012-04-10 02:25:43 +00:00
2010-06-08 16:52:24 +00:00
}
Error
2010-11-17 02:32:00 +00:00
ProcessGDBRemote : : DoHalt ( bool & caused_stop )
2010-06-08 16:52:24 +00:00
{
Error error ;
2010-11-17 02:32:00 +00:00
2011-01-22 07:12:45 +00:00
bool timed_out = false ;
Mutex : : Locker locker ;
2011-01-29 07:10:55 +00:00
if ( m_public_state . GetValue ( ) = = eStateAttaching )
2010-11-18 05:57:03 +00:00
{
2011-01-29 07:10:55 +00:00
// We are being asked to halt during an attach. We need to just close
// our file handle and debugserver will go away, and we can be done...
m_gdb_comm . Disconnect ( ) ;
}
else
{
2012-03-29 01:55:41 +00:00
if ( ! m_gdb_comm . SendInterrupt ( locker , 2 , timed_out ) )
2011-01-29 07:10:55 +00:00
{
if ( timed_out )
error . SetErrorString ( " timed out sending interrupt packet " ) ;
else
error . SetErrorString ( " unknown error sending interrupt packet " ) ;
}
2012-03-29 01:55:41 +00:00
caused_stop = m_gdb_comm . GetInterruptWasSent ( ) ;
2010-11-18 05:57:03 +00:00
}
2010-06-08 16:52:24 +00:00
return error ;
}
Error
2011-01-22 23:43:18 +00:00
ProcessGDBRemote : : InterruptIfRunning
(
bool discard_thread_plans ,
bool catch_stop_event ,
EventSP & stop_event_sp
)
2010-06-08 16:52:24 +00:00
{
Error error ;
2011-01-23 19:58:49 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ) ;
2011-01-25 02:58:48 +00:00
bool paused_private_state_thread = false ;
2011-01-23 19:58:49 +00:00
const bool is_running = m_gdb_comm . IsRunning ( ) ;
if ( log )
2011-01-25 02:58:48 +00:00
log - > Printf ( " ProcessGDBRemote::InterruptIfRunning(discard_thread_plans=%i, catch_stop_event=%i) is_running=%i " ,
2011-01-23 19:58:49 +00:00
discard_thread_plans ,
2011-01-25 02:58:48 +00:00
catch_stop_event ,
2011-01-23 19:58:49 +00:00
is_running ) ;
if ( discard_thread_plans )
{
if ( log )
log - > Printf ( " ProcessGDBRemote::InterruptIfRunning() discarding all thread plans " ) ;
m_thread_list . DiscardThreadPlans ( ) ;
}
if ( is_running )
2010-09-27 21:07:38 +00:00
{
2011-01-25 02:58:48 +00:00
if ( catch_stop_event )
{
if ( log )
log - > Printf ( " ProcessGDBRemote::InterruptIfRunning() pausing private state thread " ) ;
PausePrivateStateThread ( ) ;
paused_private_state_thread = true ;
}
2010-09-27 21:07:38 +00:00
bool timed_out = false ;
Mutex : : Locker locker ;
2011-01-22 23:43:18 +00:00
2012-03-29 01:55:41 +00:00
if ( ! m_gdb_comm . SendInterrupt ( locker , 1 , timed_out ) )
2010-09-27 21:07:38 +00:00
{
if ( timed_out )
error . SetErrorString ( " timed out sending interrupt packet " ) ;
else
error . SetErrorString ( " unknown error sending interrupt packet " ) ;
2011-01-25 02:58:48 +00:00
if ( paused_private_state_thread )
2011-01-22 23:43:18 +00:00
ResumePrivateStateThread ( ) ;
return error ;
2010-09-27 21:07:38 +00:00
}
2011-01-22 23:43:18 +00:00
if ( catch_stop_event )
{
2011-01-25 02:58:48 +00:00
// LISTEN HERE
2011-01-22 23:43:18 +00:00
TimeValue timeout_time ;
timeout_time = TimeValue : : Now ( ) ;
2011-01-25 02:58:48 +00:00
timeout_time . OffsetWithSeconds ( 5 ) ;
StateType state = WaitForStateChangedEventsPrivate ( & timeout_time , stop_event_sp ) ;
2011-01-23 19:58:49 +00:00
2011-01-25 23:55:37 +00:00
timed_out = state = = eStateInvalid ;
2011-01-23 19:58:49 +00:00
if ( log )
log - > Printf ( " ProcessGDBRemote::InterruptIfRunning() catch stop event: state = %s, timed-out=%i " , StateAsCString ( state ) , timed_out ) ;
2010-10-18 04:14:23 +00:00
2011-01-23 19:58:49 +00:00
if ( timed_out )
2011-01-22 23:43:18 +00:00
error . SetErrorString ( " unable to verify target stopped " ) ;
}
2011-01-25 02:58:48 +00:00
if ( paused_private_state_thread )
2011-01-23 19:58:49 +00:00
{
if ( log )
log - > Printf ( " ProcessGDBRemote::InterruptIfRunning() resuming private state thread " ) ;
2011-01-22 23:43:18 +00:00
ResumePrivateStateThread ( ) ;
2011-01-23 19:58:49 +00:00
}
2010-09-27 21:07:38 +00:00
}
2010-06-08 16:52:24 +00:00
return error ;
}
2011-01-22 23:43:18 +00:00
Error
ProcessGDBRemote : : WillDetach ( )
{
2011-01-23 19:58:49 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ) ;
if ( log )
log - > Printf ( " ProcessGDBRemote::WillDetach() " ) ;
2011-01-22 23:43:18 +00:00
bool discard_thread_plans = true ;
bool catch_stop_event = true ;
EventSP event_sp ;
2011-01-25 02:58:48 +00:00
return InterruptIfRunning ( discard_thread_plans , catch_stop_event , event_sp ) ;
2011-01-22 23:43:18 +00:00
}
2010-09-27 21:07:38 +00:00
Error
ProcessGDBRemote : : DoDetach ( )
{
Error error ;
2010-11-06 01:53:30 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ) ;
2010-09-27 21:07:38 +00:00
if ( log )
log - > Printf ( " ProcessGDBRemote::DoDetach() " ) ;
DisableAllBreakpointSites ( ) ;
2010-10-18 04:14:23 +00:00
m_thread_list . DiscardThreadPlans ( ) ;
2010-09-27 21:07:38 +00:00
2012-04-11 00:24:49 +00:00
bool success = m_gdb_comm . Detach ( ) ;
2010-10-18 04:14:23 +00:00
if ( log )
2010-09-27 21:07:38 +00:00
{
2012-04-11 00:24:49 +00:00
if ( success )
2010-10-18 04:14:23 +00:00
log - > PutCString ( " ProcessGDBRemote::DoDetach() detach packet sent successfully " ) ;
else
log - > PutCString ( " ProcessGDBRemote::DoDetach() detach packet send failed " ) ;
2010-09-27 21:07:38 +00:00
}
2010-10-18 04:14:23 +00:00
// Sleep for one second to let the process get all detached...
2010-09-27 21:07:38 +00:00
StopAsyncThread ( ) ;
2010-10-18 04:14:23 +00:00
SetPrivateState ( eStateDetached ) ;
ResumePrivateStateThread ( ) ;
//KillDebugserverProcess ();
2010-09-27 21:07:38 +00:00
return error ;
}
2010-06-08 16:52:24 +00:00
Error
ProcessGDBRemote : : DoDestroy ( )
{
Error error ;
2010-11-06 01:53:30 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ) ;
2010-06-08 16:52:24 +00:00
if ( log )
log - > Printf ( " ProcessGDBRemote::DoDestroy() " ) ;
// Interrupt if our inferior is running...
2011-01-22 07:12:45 +00:00
if ( m_gdb_comm . IsConnected ( ) )
2010-06-08 16:52:24 +00:00
{
2011-10-28 01:11:35 +00:00
if ( m_public_state . GetValue ( ) ! = eStateAttaching )
2011-01-22 23:43:18 +00:00
{
2011-01-29 07:10:55 +00:00
StringExtractorGDBRemote response ;
bool send_async = true ;
2011-03-10 02:26:48 +00:00
if ( m_gdb_comm . SendPacketAndWaitForResponse ( " k " , 1 , response , send_async ) )
2011-01-29 07:10:55 +00:00
{
char packet_cmd = response . GetChar ( 0 ) ;
if ( packet_cmd = = ' W ' | | packet_cmd = = ' X ' )
{
2011-12-06 04:51:14 +00:00
SetLastStopPacket ( response ) ;
2012-04-10 02:25:43 +00:00
ClearThreadIDList ( ) ;
2011-01-29 07:10:55 +00:00
SetExitStatus ( response . GetHexU8 ( ) , NULL ) ;
}
}
else
{
SetExitStatus ( SIGABRT , NULL ) ;
//error.SetErrorString("kill packet failed");
}
2011-01-22 23:43:18 +00:00
}
2010-06-08 16:52:24 +00:00
}
StopAsyncThread ( ) ;
KillDebugserverProcess ( ) ;
return error ;
}
//------------------------------------------------------------------
// Process Queries
//------------------------------------------------------------------
bool
ProcessGDBRemote : : IsAlive ( )
{
2010-12-08 05:08:21 +00:00
return m_gdb_comm . IsConnected ( ) & & m_private_state . GetValue ( ) ! = eStateExited ;
2010-06-08 16:52:24 +00:00
}
addr_t
ProcessGDBRemote : : GetImageInfoAddress ( )
{
2012-04-11 00:24:49 +00:00
return m_gdb_comm . GetShlibInfoAddr ( ) ;
2010-06-08 16:52:24 +00:00
}
//------------------------------------------------------------------
// 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 ;
2011-03-10 02:26:48 +00:00
if ( m_gdb_comm . SendPacketAndWaitForResponse ( packet , packet_len , response , true ) )
2010-06-08 16:52:24 +00:00
{
2011-03-22 04:00:09 +00:00
if ( response . IsNormalResponse ( ) )
2010-06-08 16:52:24 +00:00
{
error . Clear ( ) ;
return response . GetHexBytes ( buf , size , ' \xdd ' ) ;
}
2011-03-22 04:00:09 +00:00
else if ( response . IsErrorResponse ( ) )
2010-06-08 16:52:24 +00:00
error . SetErrorStringWithFormat ( " gdb remote returned an error: %s " , response . GetStringRef ( ) . c_str ( ) ) ;
2011-03-22 04:00:09 +00:00
else if ( response . IsUnsupportedResponse ( ) )
2010-06-08 16:52:24 +00:00
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 )
{
2011-05-16 02:35:02 +00:00
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 ;
}
2010-06-08 16:52:24 +00:00
StreamString packet ;
packet . Printf ( " M%llx,%zx: " , addr , size ) ;
2011-02-01 01:31:41 +00:00
packet . PutBytesAsRawHex8 ( buf , size , lldb : : endian : : InlHostByteOrder ( ) , lldb : : endian : : InlHostByteOrder ( ) ) ;
2010-06-08 16:52:24 +00:00
StringExtractorGDBRemote response ;
2011-03-10 02:26:48 +00:00
if ( m_gdb_comm . SendPacketAndWaitForResponse ( packet . GetData ( ) , packet . GetSize ( ) , response , true ) )
2010-06-08 16:52:24 +00:00
{
2011-03-22 04:00:09 +00:00
if ( response . IsOKResponse ( ) )
2010-06-08 16:52:24 +00:00
{
error . Clear ( ) ;
return size ;
}
2011-03-22 04:00:09 +00:00
else if ( response . IsErrorResponse ( ) )
2010-06-08 16:52:24 +00:00
error . SetErrorStringWithFormat ( " gdb remote returned an error: %s " , response . GetStringRef ( ) . c_str ( ) ) ;
2011-03-22 04:00:09 +00:00
else if ( response . IsUnsupportedResponse ( ) )
2010-06-08 16:52:24 +00:00
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 )
{
2011-05-14 01:50:35 +00:00
addr_t allocated_addr = LLDB_INVALID_ADDRESS ;
2011-05-15 01:25:55 +00:00
LazyBool supported = m_gdb_comm . SupportsAllocDeallocMemory ( ) ;
2011-05-14 01:50:35 +00:00
switch ( supported )
{
case eLazyBoolCalculate :
case eLazyBoolYes :
allocated_addr = m_gdb_comm . AllocateMemory ( size , permissions ) ;
if ( allocated_addr ! = LLDB_INVALID_ADDRESS | | supported = = eLazyBoolYes )
return allocated_addr ;
case eLazyBoolNo :
2011-06-03 20:40:38 +00:00
// Call mmap() to create memory in the inferior..
unsigned prot = 0 ;
if ( permissions & lldb : : ePermissionsReadable )
prot | = eMmapProtRead ;
if ( permissions & lldb : : ePermissionsWritable )
prot | = eMmapProtWrite ;
if ( permissions & lldb : : ePermissionsExecutable )
prot | = eMmapProtExec ;
if ( InferiorCallMmap ( this , allocated_addr , 0 , size , prot ,
eMmapFlagsAnon | eMmapFlagsPrivate , - 1 , 0 ) )
m_addr_to_mmap_size [ allocated_addr ] = size ;
else
allocated_addr = LLDB_INVALID_ADDRESS ;
2011-05-14 01:50:35 +00:00
break ;
}
2010-06-08 16:52:24 +00:00
if ( allocated_addr = = LLDB_INVALID_ADDRESS )
2011-05-17 03:37:42 +00:00
error . SetErrorStringWithFormat ( " unable to allocate %zu bytes of memory with permissions %s " , size , GetPermissionsAsCString ( permissions ) ) ;
2010-06-08 16:52:24 +00:00
else
error . Clear ( ) ;
return allocated_addr ;
}
2011-11-18 07:03:08 +00:00
Error
ProcessGDBRemote : : GetMemoryRegionInfo ( addr_t load_addr ,
MemoryRegionInfo & region_info )
{
Error error ( m_gdb_comm . GetMemoryRegionInfo ( load_addr , region_info ) ) ;
return error ;
}
2010-06-08 16:52:24 +00:00
Error
ProcessGDBRemote : : DoDeallocateMemory ( lldb : : addr_t addr )
{
Error error ;
2011-05-15 01:25:55 +00:00
LazyBool supported = m_gdb_comm . SupportsAllocDeallocMemory ( ) ;
switch ( supported )
{
case eLazyBoolCalculate :
// We should never be deallocating memory without allocating memory
// first so we should never get eLazyBoolCalculate
error . SetErrorString ( " tried to deallocate memory without ever allocating memory " ) ;
break ;
case eLazyBoolYes :
if ( ! m_gdb_comm . DeallocateMemory ( addr ) )
error . SetErrorStringWithFormat ( " unable to deallocate memory at 0x%llx " , addr ) ;
break ;
case eLazyBoolNo :
2011-06-03 20:40:38 +00:00
// Call munmap() to deallocate memory in the inferior..
2011-05-15 01:25:55 +00:00
{
MMapMap : : iterator pos = m_addr_to_mmap_size . find ( addr ) ;
2011-06-03 20:40:38 +00:00
if ( pos ! = m_addr_to_mmap_size . end ( ) & &
InferiorCallMunmap ( this , addr , pos - > second ) )
m_addr_to_mmap_size . erase ( pos ) ;
else
error . SetErrorStringWithFormat ( " unable to deallocate memory at 0x%llx " , addr ) ;
2011-05-15 01:25:55 +00:00
}
break ;
}
2010-06-08 16:52:24 +00:00
return error ;
}
//------------------------------------------------------------------
// Process STDIO
//------------------------------------------------------------------
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 ) ;
2010-11-06 01:53:30 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_BREAKPOINTS ) ) ;
2010-06-08 16:52:24 +00:00
user_id_t site_id = bp_site - > GetID ( ) ;
const addr_t addr = bp_site - > GetLoadAddress ( ) ;
if ( log )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::EnableBreakpoint (size_id = %llu) address = 0x%llx " , site_id , ( uint64_t ) addr ) ;
2010-06-08 16:52:24 +00:00
if ( bp_site - > IsEnabled ( ) )
{
if ( log )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::EnableBreakpoint (size_id = %llu) address = 0x%llx -- SUCCESS (already enabled) " , site_id , ( uint64_t ) addr ) ;
2010-06-08 16:52:24 +00:00
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?
2011-04-12 05:54:46 +00:00
if ( m_gdb_comm . SupportsGDBStoppointPacket ( eBreakpointHardware ) )
2010-06-08 16:52:24 +00:00
{
2011-04-12 05:54:46 +00:00
if ( m_gdb_comm . SendGDBStoppointTypePacket ( eBreakpointHardware , true , addr , bp_op_size ) = = 0 )
2010-06-08 16:52:24 +00:00
{
bp_site - > SetEnabled ( true ) ;
2011-04-12 05:54:46 +00:00
bp_site - > SetType ( BreakpointSite : : eHardware ) ;
2010-06-08 16:52:24 +00:00
return error ;
}
}
}
2011-04-12 05:54:46 +00:00
if ( m_gdb_comm . SupportsGDBStoppointPacket ( eBreakpointSoftware ) )
2010-06-08 16:52:24 +00:00
{
2011-04-12 05:54:46 +00:00
if ( m_gdb_comm . SendGDBStoppointTypePacket ( eBreakpointSoftware , true , addr , bp_op_size ) = = 0 )
{
bp_site - > SetEnabled ( true ) ;
bp_site - > SetType ( BreakpointSite : : eExternal ) ;
return error ;
}
2010-06-08 16:52:24 +00:00
}
2011-04-12 05:54:46 +00:00
return EnableSoftwareBreakpoint ( bp_site ) ;
2010-06-08 16:52:24 +00:00
}
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 ( ) ;
2010-11-06 01:53:30 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_BREAKPOINTS ) ) ;
2010-06-08 16:52:24 +00:00
if ( log )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::DisableBreakpoint (site_id = %llu) addr = 0x%8.8llx " , site_id , ( uint64_t ) addr ) ;
2010-06-08 16:52:24 +00:00
if ( bp_site - > IsEnabled ( ) )
{
const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode ( bp_site ) ;
2011-04-12 05:54:46 +00:00
BreakpointSite : : Type bp_type = bp_site - > GetType ( ) ;
switch ( bp_type )
2010-06-08 16:52:24 +00:00
{
2011-04-12 05:54:46 +00:00
case BreakpointSite : : eSoftware :
error = DisableSoftwareBreakpoint ( bp_site ) ;
break ;
case BreakpointSite : : eHardware :
if ( m_gdb_comm . SendGDBStoppointTypePacket ( eBreakpointSoftware , false , addr , bp_op_size ) )
error . SetErrorToGenericError ( ) ;
break ;
case BreakpointSite : : eExternal :
if ( m_gdb_comm . SendGDBStoppointTypePacket ( eBreakpointSoftware , false , addr , bp_op_size ) )
error . SetErrorToGenericError ( ) ;
break ;
2010-06-08 16:52:24 +00:00
}
2011-04-12 05:54:46 +00:00
if ( error . Success ( ) )
bp_site - > SetEnabled ( false ) ;
2010-06-08 16:52:24 +00:00
}
else
{
if ( log )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::DisableBreakpoint (site_id = %llu) addr = 0x%8.8llx -- SUCCESS (already disabled) " , site_id , ( uint64_t ) addr ) ;
2010-06-08 16:52:24 +00:00
return error ;
}
if ( error . Success ( ) )
error . SetErrorToGenericError ( ) ;
return error ;
}
2011-09-06 22:38:36 +00:00
// Pre-requisite: wp != NULL.
static GDBStoppointType
2011-10-14 00:42:25 +00:00
GetGDBStoppointType ( Watchpoint * wp )
2011-09-06 22:38:36 +00:00
{
assert ( wp ) ;
bool watch_read = wp - > WatchpointRead ( ) ;
bool watch_write = wp - > WatchpointWrite ( ) ;
// watch_read and watch_write cannot both be false.
assert ( watch_read | | watch_write ) ;
if ( watch_read & & watch_write )
return eWatchpointReadWrite ;
2011-09-09 20:35:15 +00:00
else if ( watch_read )
2011-09-06 22:38:36 +00:00
return eWatchpointRead ;
2011-09-09 20:35:15 +00:00
else // Must be watch_write, then.
2011-09-06 22:38:36 +00:00
return eWatchpointWrite ;
}
2010-06-08 16:52:24 +00:00
Error
2011-10-14 00:42:25 +00:00
ProcessGDBRemote : : EnableWatchpoint ( Watchpoint * wp )
2010-06-08 16:52:24 +00:00
{
Error error ;
if ( wp )
{
user_id_t watchID = wp - > GetID ( ) ;
addr_t addr = wp - > GetLoadAddress ( ) ;
2010-11-06 01:53:30 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_WATCHPOINTS ) ) ;
2010-06-08 16:52:24 +00:00
if ( log )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::EnableWatchpoint(watchID = %llu) " , watchID ) ;
2010-06-08 16:52:24 +00:00
if ( wp - > IsEnabled ( ) )
{
if ( log )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::EnableWatchpoint(watchID = %llu) addr = 0x%8.8llx: watchpoint already enabled. " , watchID , ( uint64_t ) addr ) ;
2010-06-08 16:52:24 +00:00
return error ;
}
2011-09-06 22:38:36 +00:00
GDBStoppointType type = GetGDBStoppointType ( wp ) ;
// Pass down an appropriate z/Z packet...
if ( m_gdb_comm . SupportsGDBStoppointPacket ( type ) )
2010-06-08 16:52:24 +00:00
{
2011-09-06 22:38:36 +00:00
if ( m_gdb_comm . SendGDBStoppointTypePacket ( type , true , addr , wp - > GetByteSize ( ) ) = = 0 )
{
wp - > SetEnabled ( true ) ;
return error ;
}
else
error . SetErrorString ( " sending gdb watchpoint packet failed " ) ;
2010-06-08 16:52:24 +00:00
}
2011-09-06 22:38:36 +00:00
else
error . SetErrorString ( " watchpoints not supported " ) ;
2010-06-08 16:52:24 +00:00
}
else
{
2011-10-14 00:42:25 +00:00
error . SetErrorString ( " Watchpoint argument was NULL. " ) ;
2010-06-08 16:52:24 +00:00
}
if ( error . Success ( ) )
error . SetErrorToGenericError ( ) ;
return error ;
}
Error
2011-10-14 00:42:25 +00:00
ProcessGDBRemote : : DisableWatchpoint ( Watchpoint * wp )
2010-06-08 16:52:24 +00:00
{
Error error ;
if ( wp )
{
user_id_t watchID = wp - > GetID ( ) ;
2010-11-06 01:53:30 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_WATCHPOINTS ) ) ;
2010-06-08 16:52:24 +00:00
addr_t addr = wp - > GetLoadAddress ( ) ;
if ( log )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::DisableWatchpoint (watchID = %llu) addr = 0x%8.8llx " , watchID , ( uint64_t ) addr ) ;
2010-06-08 16:52:24 +00:00
2011-09-06 22:38:36 +00:00
if ( ! wp - > IsEnabled ( ) )
{
if ( log )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::DisableWatchpoint (watchID = %llu) addr = 0x%8.8llx -- SUCCESS (already disabled) " , watchID , ( uint64_t ) addr ) ;
2011-09-06 22:38:36 +00:00
return error ;
}
2010-06-08 16:52:24 +00:00
if ( wp - > IsHardware ( ) )
{
2011-09-06 22:38:36 +00:00
GDBStoppointType type = GetGDBStoppointType ( wp ) ;
2010-06-08 16:52:24 +00:00
// Pass down an appropriate z/Z packet...
2011-09-06 22:38:36 +00:00
if ( m_gdb_comm . SendGDBStoppointTypePacket ( type , false , addr , wp - > GetByteSize ( ) ) = = 0 )
{
wp - > SetEnabled ( false ) ;
return error ;
}
else
error . SetErrorString ( " sending gdb watchpoint packet failed " ) ;
2010-06-08 16:52:24 +00:00
}
// TODO: clear software watchpoints if we implement them
}
else
{
2011-10-14 00:42:25 +00:00
error . SetErrorString ( " Watchpoint argument was NULL. " ) ;
2010-06-08 16:52:24 +00:00
}
if ( error . Success ( ) )
error . SetErrorToGenericError ( ) ;
return error ;
}
void
ProcessGDBRemote : : Clear ( )
{
m_flags = 0 ;
m_thread_list . Clear ( ) ;
}
Error
ProcessGDBRemote : : DoSignal ( int signo )
{
Error error ;
2010-11-06 01:53:30 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ) ;
2010-06-08 16:52:24 +00:00
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
2012-02-25 01:07:38 +00:00
ProcessGDBRemote : : StartDebugserverProcess ( const char * debugserver_url )
{
ProcessLaunchInfo launch_info ;
return StartDebugserverProcess ( debugserver_url , launch_info ) ;
}
Error
ProcessGDBRemote : : StartDebugserverProcess ( const char * debugserver_url , const ProcessInfo & process_info ) // The connection string to use in the spawned debugserver ("localhost:1234" or "/dev/tty...")
2010-06-08 16:52:24 +00:00
{
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 ;
2012-02-25 01:07:38 +00:00
ProcessLaunchInfo debugserver_launch_info ;
2010-06-08 16:52:24 +00:00
char debugserver_path [ PATH_MAX ] ;
2012-02-25 01:07:38 +00:00
FileSpec & debugserver_file_spec = debugserver_launch_info . GetExecutableFile ( ) ;
2010-06-08 16:52:24 +00:00
// 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 )
2010-10-20 20:54:39 +00:00
debugserver_file_spec . SetFile ( env_debugserver_path , false ) ;
2010-06-08 16:52:24 +00:00
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.
Added a new Host call to find LLDB related paths:
static bool
Host::GetLLDBPath (lldb::PathType path_type, FileSpec &file_spec);
This will fill in "file_spec" with an appropriate path that is appropriate
for the current Host OS. MacOSX will return paths within the LLDB.framework,
and other unixes will return the paths they want. The current PathType
enums are:
typedef enum PathType
{
ePathTypeLLDBShlibDir, // The directory where the lldb.so (unix) or LLDB mach-o file in LLDB.framework (MacOSX) exists
ePathTypeSupportExecutableDir, // Find LLDB support executable directory (debugserver, etc)
ePathTypeHeaderDir, // Find LLDB header file directory
ePathTypePythonDir // Find Python modules (PYTHONPATH) directory
} PathType;
All places that were finding executables are and python paths are now updated
to use this Host call.
Added another new host call to launch the inferior in a terminal. This ability
will be very host specific and doesn't need to be supported on all systems.
MacOSX currently will create a new .command file and tell Terminal.app to open
the .command file. It also uses the new "darwin-debug" app which is a small
app that uses posix to exec (no fork) and stop at the entry point of the
program. The GDB remote plug-in is almost able launch a process and attach to
it, it currently will spawn the process, but it won't attach to it just yet.
This will let LLDB not have to share the terminal with another process and a
new terminal window will pop up when you launch. This won't get hooked up
until we work out all of the kinks. The new Host function is:
static lldb::pid_t
Host::LaunchInNewTerminal (
const char **argv, // argv[0] is executable
const char **envp,
const ArchSpec *arch_spec,
bool stop_at_entry,
bool disable_aslr);
Cleaned up FileSpec::GetPath to not use strncpy() as it was always zero
filling the entire path buffer.
Fixed an issue with the dynamic checker function where I missed a '$' prefix
that should have been added.
llvm-svn: 116690
2010-10-17 22:03:32 +00:00
if ( Host : : GetLLDBPath ( ePathTypeSupportExecutableDir , debugserver_file_spec ) )
2010-06-08 16:52:24 +00:00
{
Added a new Host call to find LLDB related paths:
static bool
Host::GetLLDBPath (lldb::PathType path_type, FileSpec &file_spec);
This will fill in "file_spec" with an appropriate path that is appropriate
for the current Host OS. MacOSX will return paths within the LLDB.framework,
and other unixes will return the paths they want. The current PathType
enums are:
typedef enum PathType
{
ePathTypeLLDBShlibDir, // The directory where the lldb.so (unix) or LLDB mach-o file in LLDB.framework (MacOSX) exists
ePathTypeSupportExecutableDir, // Find LLDB support executable directory (debugserver, etc)
ePathTypeHeaderDir, // Find LLDB header file directory
ePathTypePythonDir // Find Python modules (PYTHONPATH) directory
} PathType;
All places that were finding executables are and python paths are now updated
to use this Host call.
Added another new host call to launch the inferior in a terminal. This ability
will be very host specific and doesn't need to be supported on all systems.
MacOSX currently will create a new .command file and tell Terminal.app to open
the .command file. It also uses the new "darwin-debug" app which is a small
app that uses posix to exec (no fork) and stop at the entry point of the
program. The GDB remote plug-in is almost able launch a process and attach to
it, it currently will spawn the process, but it won't attach to it just yet.
This will let LLDB not have to share the terminal with another process and a
new terminal window will pop up when you launch. This won't get hooked up
until we work out all of the kinks. The new Host function is:
static lldb::pid_t
Host::LaunchInNewTerminal (
const char **argv, // argv[0] is executable
const char **envp,
const ArchSpec *arch_spec,
bool stop_at_entry,
bool disable_aslr);
Cleaned up FileSpec::GetPath to not use strncpy() as it was always zero
filling the entire path buffer.
Fixed an issue with the dynamic checker function where I missed a '$' prefix
that should have been added.
llvm-svn: 116690
2010-10-17 22:03:32 +00:00
debugserver_file_spec . GetFilename ( ) . SetCString ( DEBUGSERVER_BASENAME ) ;
2010-06-08 16:52:24 +00:00
debugserver_exists = debugserver_file_spec . Exists ( ) ;
Added a new Host call to find LLDB related paths:
static bool
Host::GetLLDBPath (lldb::PathType path_type, FileSpec &file_spec);
This will fill in "file_spec" with an appropriate path that is appropriate
for the current Host OS. MacOSX will return paths within the LLDB.framework,
and other unixes will return the paths they want. The current PathType
enums are:
typedef enum PathType
{
ePathTypeLLDBShlibDir, // The directory where the lldb.so (unix) or LLDB mach-o file in LLDB.framework (MacOSX) exists
ePathTypeSupportExecutableDir, // Find LLDB support executable directory (debugserver, etc)
ePathTypeHeaderDir, // Find LLDB header file directory
ePathTypePythonDir // Find Python modules (PYTHONPATH) directory
} PathType;
All places that were finding executables are and python paths are now updated
to use this Host call.
Added another new host call to launch the inferior in a terminal. This ability
will be very host specific and doesn't need to be supported on all systems.
MacOSX currently will create a new .command file and tell Terminal.app to open
the .command file. It also uses the new "darwin-debug" app which is a small
app that uses posix to exec (no fork) and stop at the entry point of the
program. The GDB remote plug-in is almost able launch a process and attach to
it, it currently will spawn the process, but it won't attach to it just yet.
This will let LLDB not have to share the terminal with another process and a
new terminal window will pop up when you launch. This won't get hooked up
until we work out all of the kinks. The new Host function is:
static lldb::pid_t
Host::LaunchInNewTerminal (
const char **argv, // argv[0] is executable
const char **envp,
const ArchSpec *arch_spec,
bool stop_at_entry,
bool disable_aslr);
Cleaned up FileSpec::GetPath to not use strncpy() as it was always zero
filling the entire path buffer.
Fixed an issue with the dynamic checker function where I missed a '$' prefix
that should have been added.
llvm-svn: 116690
2010-10-17 22:03:32 +00:00
if ( debugserver_exists )
{
g_debugserver_file_spec = debugserver_file_spec ;
}
else
{
g_debugserver_file_spec . Clear ( ) ;
debugserver_file_spec . Clear ( ) ;
}
2010-06-08 16:52:24 +00:00
}
}
if ( debugserver_exists )
{
debugserver_file_spec . GetPath ( debugserver_path , sizeof ( debugserver_path ) ) ;
m_stdio_communication . Clear ( ) ;
2010-11-06 01:53:30 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ) ;
2010-06-08 16:52:24 +00:00
2012-02-25 01:07:38 +00:00
Args & debugserver_args = debugserver_launch_info . GetArguments ( ) ;
2010-06-08 16:52:24 +00:00
char arg_cstr [ PATH_MAX ] ;
// Start args with "debugserver /file/path -r --"
debugserver_args . AppendArgument ( debugserver_path ) ;
debugserver_args . AppendArgument ( debugserver_url ) ;
Added a new Host call to find LLDB related paths:
static bool
Host::GetLLDBPath (lldb::PathType path_type, FileSpec &file_spec);
This will fill in "file_spec" with an appropriate path that is appropriate
for the current Host OS. MacOSX will return paths within the LLDB.framework,
and other unixes will return the paths they want. The current PathType
enums are:
typedef enum PathType
{
ePathTypeLLDBShlibDir, // The directory where the lldb.so (unix) or LLDB mach-o file in LLDB.framework (MacOSX) exists
ePathTypeSupportExecutableDir, // Find LLDB support executable directory (debugserver, etc)
ePathTypeHeaderDir, // Find LLDB header file directory
ePathTypePythonDir // Find Python modules (PYTHONPATH) directory
} PathType;
All places that were finding executables are and python paths are now updated
to use this Host call.
Added another new host call to launch the inferior in a terminal. This ability
will be very host specific and doesn't need to be supported on all systems.
MacOSX currently will create a new .command file and tell Terminal.app to open
the .command file. It also uses the new "darwin-debug" app which is a small
app that uses posix to exec (no fork) and stop at the entry point of the
program. The GDB remote plug-in is almost able launch a process and attach to
it, it currently will spawn the process, but it won't attach to it just yet.
This will let LLDB not have to share the terminal with another process and a
new terminal window will pop up when you launch. This won't get hooked up
until we work out all of the kinks. The new Host function is:
static lldb::pid_t
Host::LaunchInNewTerminal (
const char **argv, // argv[0] is executable
const char **envp,
const ArchSpec *arch_spec,
bool stop_at_entry,
bool disable_aslr);
Cleaned up FileSpec::GetPath to not use strncpy() as it was always zero
filling the entire path buffer.
Fixed an issue with the dynamic checker function where I missed a '$' prefix
that should have been added.
llvm-svn: 116690
2010-10-17 22:03:32 +00:00
// use native registers, not the GDB registers
debugserver_args . AppendArgument ( " --native-regs " ) ;
// make debugserver run in its own session so signals generated by
// special terminal key sequences (^C) don't affect debugserver
debugserver_args . AppendArgument ( " --setsid " ) ;
2010-06-08 16:52:24 +00:00
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 ) ;
}
2011-01-25 06:55:13 +00:00
// debugserver_args.AppendArgument("--log-file=/tmp/debugserver.txt");
2011-01-29 07:10:55 +00:00
// debugserver_args.AppendArgument("--log-flags=0x802e0e");
2010-06-08 16:52:24 +00:00
2011-04-12 05:54:46 +00:00
// We currently send down all arguments, attach pids, or attach
// process names in dedicated GDB server packets, so we don't need
// to pass them as arguments. This is currently because of all the
// things we need to setup prior to launching: the environment,
// current working dir, file actions, etc.
#if 0
2010-06-08 16:52:24 +00:00
// Now append the program arguments
2011-02-24 22:24:29 +00:00
if ( inferior_argv )
2010-06-08 16:52:24 +00:00
{
2011-02-24 22:24:29 +00:00
// Terminate the debugserver args so we can now append the inferior args
debugserver_args . AppendArgument ( " -- " ) ;
2010-06-08 16:52:24 +00:00
2011-02-24 22:24:29 +00:00
for ( int i = 0 ; inferior_argv [ i ] ! = NULL ; + + i )
debugserver_args . AppendArgument ( inferior_argv [ i ] ) ;
2010-06-08 16:52:24 +00:00
}
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 ) ;
}
# endif
2011-04-12 05:54:46 +00:00
ProcessLaunchInfo : : FileAction file_action ;
// Close STDIN, STDOUT and STDERR. We might need to redirect them
// to "/dev/null" if we run into any problems.
file_action . Close ( STDIN_FILENO ) ;
2012-02-25 01:07:38 +00:00
debugserver_launch_info . AppendFileAction ( file_action ) ;
2011-04-12 05:54:46 +00:00
file_action . Close ( STDOUT_FILENO ) ;
2012-02-25 01:07:38 +00:00
debugserver_launch_info . AppendFileAction ( file_action ) ;
2011-04-12 05:54:46 +00:00
file_action . Close ( STDERR_FILENO ) ;
2012-02-25 01:07:38 +00:00
debugserver_launch_info . AppendFileAction ( file_action ) ;
2010-06-08 16:52:24 +00:00
if ( log )
{
StreamString strm ;
debugserver_args . Dump ( & strm ) ;
log - > Printf ( " %s arguments: \n %s " , debugserver_args . GetArgumentAtIndex ( 0 ) , strm . GetData ( ) ) ;
}
2012-02-25 01:07:38 +00:00
debugserver_launch_info . SetMonitorProcessCallback ( MonitorDebugserverProcess , this , false ) ;
debugserver_launch_info . SetUserID ( process_info . GetUserID ( ) ) ;
2011-11-16 05:37:56 +00:00
2012-02-25 01:07:38 +00:00
error = Host : : LaunchProcess ( debugserver_launch_info ) ;
2010-07-02 01:29:13 +00:00
2011-04-12 05:54:46 +00:00
if ( error . Success ( ) )
2012-02-25 01:07:38 +00:00
m_debugserver_pid = debugserver_launch_info . GetProcessID ( ) ;
2011-04-12 05:54:46 +00:00
else
2010-06-08 16:52:24 +00:00
m_debugserver_pid = LLDB_INVALID_PROCESS_ID ;
if ( error . Fail ( ) | | log )
2011-12-01 23:28:38 +00:00
error . PutToLog ( log . get ( ) , " Host::LaunchProcess (launch_info) => pid=%llu, path='%s' " , m_debugserver_pid , debugserver_path ) ;
2010-06-08 16:52:24 +00:00
}
else
{
2011-10-26 00:56:27 +00:00
error . SetErrorStringWithFormat ( " unable to locate " DEBUGSERVER_BASENAME ) ;
2010-06-08 16:52:24 +00:00
}
if ( m_debugserver_pid ! = LLDB_INVALID_PROCESS_ID )
StartAsyncThread ( ) ;
}
return error ;
}
bool
ProcessGDBRemote : : MonitorDebugserverProcess
(
void * callback_baton ,
lldb : : pid_t debugserver_pid ,
2011-11-16 05:37:56 +00:00
bool exited , // True if the process did exit
2010-06-08 16:52:24 +00:00
int signo , // Zero for no signal
int exit_status // Exit value of process if signal is zero
)
{
2011-11-16 05:37:56 +00:00
// The baton is a "ProcessGDBRemote *". Now this class might be gone
// and might not exist anymore, so we need to carefully try to get the
// target for this process first since we have a race condition when
// we are done running between getting the notice that the inferior
// process has died and the debugserver that was debugging this process.
// In our test suite, we are also continually running process after
// process, so we must be very careful to make sure:
// 1 - process object hasn't been deleted already
// 2 - that a new process object hasn't been recreated in its place
2010-06-08 16:52:24 +00:00
// "debugserver_pid" argument passed in is the process ID for
// debugserver that we are tracking...
2011-11-16 05:37:56 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ) ;
2010-06-08 16:52:24 +00:00
2010-08-21 02:22:51 +00:00
ProcessGDBRemote * process = ( ProcessGDBRemote * ) callback_baton ;
2011-01-22 23:43:18 +00:00
2011-11-16 05:37:56 +00:00
// Get a shared pointer to the target that has a matching process pointer.
// This target could be gone, or the target could already have a new process
// object inside of it
TargetSP target_sp ( Debugger : : FindTargetWithProcess ( process ) ) ;
2011-01-22 23:43:18 +00:00
if ( log )
2011-12-01 23:28:38 +00:00
log - > Printf ( " ProcessGDBRemote::MonitorDebugserverProcess (baton=%p, pid=%llu, signo=%i (0x%x), exit_status=%i) " , callback_baton , debugserver_pid , signo , signo , exit_status ) ;
2011-01-22 23:43:18 +00:00
2011-11-16 05:37:56 +00:00
if ( target_sp )
2010-06-08 16:52:24 +00:00
{
2011-11-16 05:37:56 +00:00
// We found a process in a target that matches, but another thread
// might be in the process of launching a new process that will
// soon replace it, so get a shared pointer to the process so we
// can keep it alive.
ProcessSP process_sp ( target_sp - > GetProcessSP ( ) ) ;
// Now we have a shared pointer to the process that can't go away on us
// so we now make sure it was the same as the one passed in, and also make
// sure that our previous "process *" didn't get deleted and have a new
// "process *" created in its place with the same pointer. To verify this
// we make sure the process has our debugserver process ID. If we pass all
// of these tests, then we are sure that this process is the one we were
// looking for.
if ( process_sp & & process = = process_sp . get ( ) & & process - > m_debugserver_pid = = debugserver_pid )
2010-06-08 16:52:24 +00:00
{
2011-11-16 05:37:56 +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.
const StateType state = process - > GetState ( ) ;
if ( process - > m_debugserver_pid ! = LLDB_INVALID_PROCESS_ID & &
state ! = eStateInvalid & &
state ! = eStateUnloaded & &
state ! = eStateExited & &
state ! = eStateDetached )
2010-06-08 16:52:24 +00:00
{
2011-11-16 05:37:56 +00:00
char error_str [ 1024 ] ;
if ( signo )
{
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 ) ;
else
: : snprintf ( error_str , sizeof ( error_str ) , DEBUGSERVER_BASENAME " died with signal %i " , signo ) ;
}
2010-06-08 16:52:24 +00:00
else
2011-11-16 05:37:56 +00:00
{
: : snprintf ( error_str , sizeof ( error_str ) , DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x " , exit_status ) ;
}
2010-08-21 02:22:51 +00:00
2011-11-16 05:37:56 +00:00
process - > SetExitStatus ( - 1 , error_str ) ;
}
// 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 ;
2010-08-21 02:22:51 +00:00
}
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 : : StartAsyncThread ( )
{
2010-11-06 01:53:30 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ) ;
2010-06-08 16:52:24 +00:00
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 ) ;
2011-02-08 01:34:25 +00:00
return IS_VALID_LLDB_HOST_THREAD ( m_async_thread ) ;
2010-06-08 16:52:24 +00:00
}
void
ProcessGDBRemote : : StopAsyncThread ( )
{
2010-11-06 01:53:30 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ) ;
2010-06-08 16:52:24 +00:00
if ( log )
log - > Printf ( " ProcessGDBRemote::%s () " , __FUNCTION__ ) ;
m_async_broadcaster . BroadcastEvent ( eBroadcastBitAsyncThreadShouldExit ) ;
2011-10-28 01:11:35 +00:00
// This will shut down the async thread.
m_gdb_comm . Disconnect ( ) ; // Disconnect from the debug server.
2010-06-08 16:52:24 +00:00
// Stop the stdio thread
2011-02-08 01:34:25 +00:00
if ( IS_VALID_LLDB_HOST_THREAD ( m_async_thread ) )
2010-06-08 16:52:24 +00:00
{
Host : : ThreadJoin ( m_async_thread , NULL , NULL ) ;
}
}
void *
ProcessGDBRemote : : AsyncThread ( void * arg )
{
ProcessGDBRemote * process = ( ProcessGDBRemote * ) arg ;
2010-11-06 01:53:30 +00:00
LogSP log ( ProcessGDBRemoteLog : : GetLogIfAllCategoriesSet ( GDBR_LOG_PROCESS ) ) ;
2010-06-08 16:52:24 +00:00
if ( log )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::%s (arg = %p, pid = %llu) thread starting... " , __FUNCTION__ , arg , process - > GetID ( ) ) ;
2010-06-08 16:52:24 +00:00
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 )
{
2011-02-24 22:24:29 +00:00
listener . StartListeningForEvents ( & process - > m_gdb_comm , Communication : : eBroadcastBitReadThreadDidExit ) ;
2010-06-08 16:52:24 +00:00
bool done = false ;
while ( ! done )
{
if ( log )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::%s (arg = %p, pid = %llu) listener.WaitForEvent (NULL, event_sp)... " , __FUNCTION__ , arg , process - > GetID ( ) ) ;
2010-06-08 16:52:24 +00:00
if ( listener . WaitForEvent ( NULL , event_sp ) )
{
const uint32_t event_type = event_sp - > GetType ( ) ;
2011-02-24 22:24:29 +00:00
if ( event_sp - > BroadcasterIs ( & process - > m_async_broadcaster ) )
2010-06-08 16:52:24 +00:00
{
2011-02-24 22:24:29 +00:00
if ( log )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::%s (arg = %p, pid = %llu) Got an event of type: %d... " , __FUNCTION__ , arg , process - > GetID ( ) , event_type ) ;
2010-06-08 16:52:24 +00:00
2011-02-24 22:24:29 +00:00
switch ( event_type )
{
case eBroadcastBitAsyncContinue :
2010-06-08 16:52:24 +00:00
{
2011-02-24 22:24:29 +00:00
const EventDataBytes * continue_packet = EventDataBytes : : GetEventDataFromEvent ( event_sp . get ( ) ) ;
2010-06-08 16:52:24 +00:00
2011-02-24 22:24:29 +00:00
if ( continue_packet )
2010-06-08 16:52:24 +00:00
{
2011-02-24 22:24:29 +00:00
const char * continue_cstr = ( const char * ) continue_packet - > GetBytes ( ) ;
const size_t continue_cstr_len = continue_packet - > GetByteSize ( ) ;
if ( log )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::%s (arg = %p, pid = %llu) got eBroadcastBitAsyncContinue: %s " , __FUNCTION__ , arg , process - > GetID ( ) , continue_cstr ) ;
2011-02-24 22:24:29 +00:00
if ( : : strstr ( continue_cstr , " vAttach " ) = = NULL )
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 :
2011-12-06 04:51:14 +00:00
process - > SetLastStopPacket ( response ) ;
2011-02-24 22:24:29 +00:00
process - > SetPrivateState ( stop_state ) ;
break ;
case eStateExited :
2011-12-06 04:51:14 +00:00
process - > SetLastStopPacket ( response ) ;
2012-04-10 02:25:43 +00:00
process - > ClearThreadIDList ( ) ;
2011-02-24 22:24:29 +00:00
response . SetFilePos ( 1 ) ;
process - > SetExitStatus ( response . GetHexU8 ( ) , NULL ) ;
done = true ;
break ;
case eStateInvalid :
process - > SetExitStatus ( - 1 , " lost connection " ) ;
break ;
default :
process - > SetPrivateState ( stop_state ) ;
break ;
}
2010-06-08 16:52:24 +00:00
}
}
2011-02-24 22:24:29 +00:00
break ;
2010-06-08 16:52:24 +00:00
2011-02-24 22:24:29 +00:00
case eBroadcastBitAsyncThreadShouldExit :
if ( log )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::%s (arg = %p, pid = %llu) got eBroadcastBitAsyncThreadShouldExit... " , __FUNCTION__ , arg , process - > GetID ( ) ) ;
2011-02-24 22:24:29 +00:00
done = true ;
break ;
2010-06-08 16:52:24 +00:00
2011-02-24 22:24:29 +00:00
default :
if ( log )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::%s (arg = %p, pid = %llu) got unknown event 0x%8.8x " , __FUNCTION__ , arg , process - > GetID ( ) , event_type ) ;
2011-02-24 22:24:29 +00:00
done = true ;
break ;
}
}
else if ( event_sp - > BroadcasterIs ( & process - > m_gdb_comm ) )
{
if ( event_type & Communication : : eBroadcastBitReadThreadDidExit )
{
process - > SetExitStatus ( - 1 , " lost connection " ) ;
2010-06-08 16:52:24 +00:00
done = true ;
2011-02-24 22:24:29 +00:00
}
2010-06-08 16:52:24 +00:00
}
}
else
{
if ( log )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::%s (arg = %p, pid = %llu) listener.WaitForEvent (NULL, event_sp) => false " , __FUNCTION__ , arg , process - > GetID ( ) ) ;
2010-06-08 16:52:24 +00:00
done = true ;
}
}
}
if ( log )
2011-10-19 18:09:39 +00:00
log - > Printf ( " ProcessGDBRemote::%s (arg = %p, pid = %llu) thread exiting... " , __FUNCTION__ , arg , process - > GetID ( ) ) ;
2010-06-08 16:52:24 +00:00
process - > m_async_thread = LLDB_INVALID_HOST_THREAD ;
return NULL ;
}
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 )
{
2010-10-12 17:33:06 +00:00
static ConstString g_dispatch_queue_offsets_symbol_name ( " dispatch_queue_offsets " ) ;
const Symbol * dispatch_queue_offsets_symbol = NULL ;
2012-02-26 05:51:37 +00:00
ModuleSpec libSystem_module_spec ( FileSpec ( " libSystem.B.dylib " , false ) ) ;
ModuleSP module_sp ( GetTarget ( ) . GetImages ( ) . FindFirstModule ( libSystem_module_spec ) ) ;
2010-10-12 17:33:06 +00:00
if ( module_sp )
dispatch_queue_offsets_symbol = module_sp - > FindFirstSymbolWithNameAndType ( g_dispatch_queue_offsets_symbol_name , eSymbolTypeData ) ;
if ( dispatch_queue_offsets_symbol = = NULL )
{
2012-02-26 05:51:37 +00:00
ModuleSpec libdispatch_module_spec ( FileSpec ( " libdispatch.dylib " , false ) ) ;
module_sp = GetTarget ( ) . GetImages ( ) . FindFirstModule ( libdispatch_module_spec ) ;
2010-10-12 17:33:06 +00:00
if ( module_sp )
dispatch_queue_offsets_symbol = module_sp - > FindFirstSymbolWithNameAndType ( g_dispatch_queue_offsets_symbol_name , eSymbolTypeData ) ;
}
2010-06-08 16:52:24 +00:00
if ( dispatch_queue_offsets_symbol )
2012-03-07 21:03:09 +00:00
m_dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol - > GetAddress ( ) . GetLoadAddress ( & m_target ) ;
2010-06-08 16:52:24 +00:00
if ( m_dispatch_queue_offsets_addr = = LLDB_INVALID_ADDRESS )
return NULL ;
}
uint8_t memory_buffer [ 8 ] ;
2011-02-15 21:59:32 +00:00
DataExtractor data ( memory_buffer ,
sizeof ( memory_buffer ) ,
m_target . GetArchitecture ( ) . GetByteOrder ( ) ,
m_target . GetArchitecture ( ) . GetAddressByteSize ( ) ) ;
2010-06-08 16:52:24 +00:00
// 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 ( ) ;
}
2011-03-08 22:40:15 +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;
// }
//
//}
//
2011-01-22 01:33:44 +00:00
bool
ProcessGDBRemote : : NewThreadNotifyBreakpointHit ( void * baton ,
lldb_private : : StoppointCallbackContext * context ,
lldb : : user_id_t break_id ,
lldb : : user_id_t break_loc_id )
{
// I don't think I have to do anything here, just make sure I notice the new thread when it starts to
// run so I can stop it if that's what I want to do.
LogSP log ( lldb_private : : GetLogIfAllCategoriesSet ( LIBLLDB_LOG_STEP ) ) ;
if ( log )
log - > Printf ( " Hit New Thread Notification breakpoint. " ) ;
return false ;
}
bool
ProcessGDBRemote : : StartNoticingNewThreads ( )
{
static const char * bp_names [ ] =
{
" start_wqthread " ,
2011-02-08 05:19:01 +00:00
" _pthread_wqthread " ,
2011-01-22 01:33:44 +00:00
" _pthread_start " ,
NULL
} ;
LogSP log ( lldb_private : : GetLogIfAllCategoriesSet ( LIBLLDB_LOG_STEP ) ) ;
size_t num_bps = m_thread_observation_bps . size ( ) ;
if ( num_bps ! = 0 )
{
for ( int i = 0 ; i < num_bps ; i + + )
{
lldb : : BreakpointSP break_sp = m_target . GetBreakpointByID ( m_thread_observation_bps [ i ] ) ;
if ( break_sp )
{
2011-10-15 00:21:37 +00:00
if ( log & & log - > GetVerbose ( ) )
2011-01-22 01:33:44 +00:00
log - > Printf ( " Enabled noticing new thread breakpoint. " ) ;
break_sp - > SetEnabled ( true ) ;
}
}
}
else
{
for ( int i = 0 ; bp_names [ i ] ! = NULL ; i + + )
{
2011-09-23 00:54:11 +00:00
Breakpoint * breakpoint = m_target . CreateBreakpoint ( NULL , NULL , bp_names [ i ] , eFunctionNameTypeFull , true ) . get ( ) ;
2011-01-22 01:33:44 +00:00
if ( breakpoint )
{
2011-10-15 00:21:37 +00:00
if ( log & & log - > GetVerbose ( ) )
2011-01-22 01:33:44 +00:00
log - > Printf ( " Successfully created new thread notification breakpoint at \" %s \" . " , bp_names [ i ] ) ;
m_thread_observation_bps . push_back ( breakpoint - > GetID ( ) ) ;
breakpoint - > SetCallback ( ProcessGDBRemote : : NewThreadNotifyBreakpointHit , this , true ) ;
}
else
{
if ( log )
log - > Printf ( " Failed to create new thread notification breakpoint. " ) ;
return false ;
}
}
}
return true ;
}
bool
ProcessGDBRemote : : StopNoticingNewThreads ( )
{
2011-02-08 05:19:01 +00:00
LogSP log ( lldb_private : : GetLogIfAllCategoriesSet ( LIBLLDB_LOG_STEP ) ) ;
2011-10-15 00:21:37 +00:00
if ( log & & log - > GetVerbose ( ) )
2011-02-08 05:19:01 +00:00
log - > Printf ( " Disabling new thread notification breakpoint. " ) ;
2011-01-22 01:33:44 +00:00
size_t num_bps = m_thread_observation_bps . size ( ) ;
if ( num_bps ! = 0 )
{
for ( int i = 0 ; i < num_bps ; i + + )
{
lldb : : BreakpointSP break_sp = m_target . GetBreakpointByID ( m_thread_observation_bps [ i ] ) ;
if ( break_sp )
{
break_sp - > SetEnabled ( false ) ;
}
}
}
return true ;
}