2016-01-06 15:12:03 -05:00
//===-- ProcessKDP.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 <stdlib.h>
// C++ Includes
# include <mutex>
// Other libraries and framework includes
# include "lldb/Core/Debugger.h"
# include "lldb/Core/PluginManager.h"
# include "lldb/Core/Module.h"
# include "lldb/Core/ModuleSpec.h"
# include "lldb/Core/State.h"
# include "lldb/Core/UUID.h"
# include "lldb/Host/ConnectionFileDescriptor.h"
# include "lldb/Host/Host.h"
# include "lldb/Host/Symbols.h"
# include "lldb/Host/ThreadLauncher.h"
# include "lldb/Host/common/TCPSocket.h"
# include "lldb/Interpreter/CommandInterpreter.h"
# include "lldb/Interpreter/CommandObject.h"
# include "lldb/Interpreter/CommandObjectMultiword.h"
# include "lldb/Interpreter/CommandReturnObject.h"
# include "lldb/Interpreter/OptionGroupString.h"
# include "lldb/Interpreter/OptionGroupUInt64.h"
# include "lldb/Interpreter/OptionValueProperties.h"
# include "lldb/Symbol/ObjectFile.h"
# include "lldb/Target/RegisterContext.h"
# include "lldb/Target/Target.h"
# include "lldb/Target/Thread.h"
# include "lldb/Utility/StringExtractor.h"
# define USEC_PER_SEC 1000000
// Project includes
# include "ProcessKDP.h"
# include "ProcessKDPLog.h"
# include "ThreadKDP.h"
# include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h"
# include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h"
using namespace lldb ;
using namespace lldb_private ;
namespace {
static PropertyDefinition
g_properties [ ] =
{
{ " packet-timeout " , OptionValue : : eTypeUInt64 , true , 5 , NULL , NULL , " Specify the default packet timeout in seconds. " } ,
{ NULL , OptionValue : : eTypeInvalid , false , 0 , NULL , NULL , NULL }
} ;
enum
{
ePropertyPacketTimeout
} ;
class PluginProperties : public Properties
{
public :
static ConstString
GetSettingName ( )
{
return ProcessKDP : : GetPluginNameStatic ( ) ;
}
PluginProperties ( ) :
Properties ( )
{
m_collection_sp . reset ( new OptionValueProperties ( GetSettingName ( ) ) ) ;
m_collection_sp - > Initialize ( g_properties ) ;
}
virtual
~ PluginProperties ( )
{
}
uint64_t
GetPacketTimeout ( )
{
const uint32_t idx = ePropertyPacketTimeout ;
return m_collection_sp - > GetPropertyAtIndexAsUInt64 ( NULL , idx , g_properties [ idx ] . default_uint_value ) ;
}
} ;
typedef std : : shared_ptr < PluginProperties > ProcessKDPPropertiesSP ;
static const ProcessKDPPropertiesSP &
GetGlobalPluginProperties ( )
{
static ProcessKDPPropertiesSP g_settings_sp ;
if ( ! g_settings_sp )
g_settings_sp . reset ( new PluginProperties ( ) ) ;
return g_settings_sp ;
}
} // anonymous namespace end
static const lldb : : tid_t g_kernel_tid = 1 ;
ConstString
ProcessKDP : : GetPluginNameStatic ( )
{
static ConstString g_name ( " kdp-remote " ) ;
return g_name ;
}
const char *
ProcessKDP : : GetPluginDescriptionStatic ( )
{
return " KDP Remote protocol based debugging plug-in for darwin kernel debugging. " ;
}
void
ProcessKDP : : Terminate ( )
{
PluginManager : : UnregisterPlugin ( ProcessKDP : : CreateInstance ) ;
}
lldb : : ProcessSP
ProcessKDP : : CreateInstance ( TargetSP target_sp ,
2016-07-23 16:50:09 -04:00
ListenerSP listener_sp ,
2016-01-06 15:12:03 -05:00
const FileSpec * crash_file_path )
{
lldb : : ProcessSP process_sp ;
if ( crash_file_path = = NULL )
2016-07-23 16:50:09 -04:00
process_sp . reset ( new ProcessKDP ( target_sp , listener_sp ) ) ;
2016-01-06 15:12:03 -05:00
return process_sp ;
}
bool
ProcessKDP : : CanDebug ( TargetSP target_sp , bool plugin_specified_by_name )
{
if ( plugin_specified_by_name )
return true ;
// For now we are just making sure the file exists for a given module
Module * exe_module = target_sp - > GetExecutableModulePointer ( ) ;
if ( exe_module )
{
const llvm : : Triple & triple_ref = target_sp - > GetArchitecture ( ) . GetTriple ( ) ;
switch ( triple_ref . getOS ( ) )
{
case llvm : : Triple : : Darwin : // Should use "macosx" for desktop and "ios" for iOS, but accept darwin just in case
case llvm : : Triple : : MacOSX : // For desktop targets
case llvm : : Triple : : IOS : // For arm targets
case llvm : : Triple : : TvOS :
case llvm : : Triple : : WatchOS :
if ( triple_ref . getVendor ( ) = = llvm : : Triple : : Apple )
{
ObjectFile * exe_objfile = exe_module - > GetObjectFile ( ) ;
if ( exe_objfile - > GetType ( ) = = ObjectFile : : eTypeExecutable & &
exe_objfile - > GetStrata ( ) = = ObjectFile : : eStrataKernel )
return true ;
}
break ;
default :
break ;
}
}
return false ;
}
//----------------------------------------------------------------------
// ProcessKDP constructor
//----------------------------------------------------------------------
2016-07-23 16:50:09 -04:00
ProcessKDP : : ProcessKDP ( TargetSP target_sp , ListenerSP listener_sp ) :
Process ( target_sp , listener_sp ) ,
2016-01-06 15:12:03 -05:00
m_comm ( " lldb.process.kdp-remote.communication " ) ,
m_async_broadcaster ( NULL , " lldb.process.kdp-remote.async-broadcaster " ) ,
m_dyld_plugin_name ( ) ,
m_kernel_load_addr ( LLDB_INVALID_ADDRESS ) ,
m_command_sp ( ) ,
m_kernel_thread_wp ( )
{
m_async_broadcaster . SetEventName ( eBroadcastBitAsyncThreadShouldExit , " async thread should exit " ) ;
m_async_broadcaster . SetEventName ( eBroadcastBitAsyncContinue , " async thread continue " ) ;
const uint64_t timeout_seconds = GetGlobalPluginProperties ( ) - > GetPacketTimeout ( ) ;
if ( timeout_seconds > 0 )
m_comm . SetPacketTimeout ( timeout_seconds ) ;
}
//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
ProcessKDP : : ~ ProcessKDP ( )
{
Clear ( ) ;
// 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 ( ) ;
}
//----------------------------------------------------------------------
// PluginInterface
//----------------------------------------------------------------------
lldb_private : : ConstString
ProcessKDP : : GetPluginName ( )
{
return GetPluginNameStatic ( ) ;
}
uint32_t
ProcessKDP : : GetPluginVersion ( )
{
return 1 ;
}
Error
ProcessKDP : : WillLaunch ( Module * module )
{
Error error ;
error . SetErrorString ( " launching not supported in kdp-remote plug-in " ) ;
return error ;
}
Error
ProcessKDP : : WillAttachToProcessWithID ( lldb : : pid_t pid )
{
Error error ;
error . SetErrorString ( " attaching to a by process ID not supported in kdp-remote plug-in " ) ;
return error ;
}
Error
ProcessKDP : : WillAttachToProcessWithName ( const char * process_name , bool wait_for_launch )
{
Error error ;
error . SetErrorString ( " attaching to a by process name not supported in kdp-remote plug-in " ) ;
return error ;
}
bool
ProcessKDP : : GetHostArchitecture ( ArchSpec & arch )
{
uint32_t cpu = m_comm . GetCPUType ( ) ;
if ( cpu )
{
uint32_t sub = m_comm . GetCPUSubtype ( ) ;
arch . SetArchitecture ( eArchTypeMachO , cpu , sub ) ;
// Leave architecture vendor as unspecified unknown
arch . GetTriple ( ) . setVendor ( llvm : : Triple : : UnknownVendor ) ;
arch . GetTriple ( ) . setVendorName ( llvm : : StringRef ( ) ) ;
return true ;
}
arch . Clear ( ) ;
return false ;
}
Error
ProcessKDP : : DoConnectRemote ( Stream * strm , const char * remote_url )
{
Error error ;
// Don't let any JIT happen when doing KDP as we can't allocate
// memory and we don't want to be mucking with threads that might
// already be handling exceptions
SetCanJIT ( false ) ;
if ( remote_url = = NULL | | remote_url [ 0 ] = = ' \0 ' )
{
error . SetErrorStringWithFormat ( " invalid connection URL '%s' " , remote_url ) ;
return error ;
}
std : : unique_ptr < ConnectionFileDescriptor > conn_ap ( new ConnectionFileDescriptor ( ) ) ;
if ( conn_ap . get ( ) )
{
// Only try once for now.
// TODO: check if we should be retrying?
const uint32_t max_retry_count = 1 ;
for ( uint32_t retry_count = 0 ; retry_count < max_retry_count ; + + retry_count )
{
if ( conn_ap - > Connect ( remote_url , & error ) = = eConnectionStatusSuccess )
break ;
usleep ( 100000 ) ;
}
}
if ( conn_ap - > IsConnected ( ) )
{
const TCPSocket & socket = static_cast < const TCPSocket & > ( * conn_ap - > GetReadObject ( ) ) ;
const uint16_t reply_port = socket . GetLocalPortNumber ( ) ;
if ( reply_port ! = 0 )
{
m_comm . SetConnection ( conn_ap . release ( ) ) ;
if ( m_comm . SendRequestReattach ( reply_port ) )
{
if ( m_comm . SendRequestConnect ( reply_port , reply_port , " Greetings from LLDB... " ) )
{
m_comm . GetVersion ( ) ;
Target & target = GetTarget ( ) ;
ArchSpec kernel_arch ;
// The host architecture
GetHostArchitecture ( kernel_arch ) ;
ArchSpec target_arch = target . GetArchitecture ( ) ;
// Merge in any unspecified stuff into the target architecture in
// case the target arch isn't set at all or incompletely.
target_arch . MergeFrom ( kernel_arch ) ;
target . SetArchitecture ( target_arch ) ;
/* Get the kernel's UUID and load address via KDP_KERNELVERSION packet. */
/* An EFI kdp session has neither UUID nor load address. */
UUID kernel_uuid = m_comm . GetUUID ( ) ;
addr_t kernel_load_addr = m_comm . GetLoadAddress ( ) ;
if ( m_comm . RemoteIsEFI ( ) )
{
// Select an invalid plugin name for the dynamic loader so one doesn't get used
// since EFI does its own manual loading via python scripting
static ConstString g_none_dynamic_loader ( " none " ) ;
m_dyld_plugin_name = g_none_dynamic_loader ;
if ( kernel_uuid . IsValid ( ) ) {
// If EFI passed in a UUID= try to lookup UUID
// The slide will not be provided. But the UUID
// lookup will be used to launch EFI debug scripts
// from the dSYM, that can load all of the symbols.
ModuleSpec module_spec ;
module_spec . GetUUID ( ) = kernel_uuid ;
module_spec . GetArchitecture ( ) = target . GetArchitecture ( ) ;
// Lookup UUID locally, before attempting dsymForUUID like action
module_spec . GetSymbolFileSpec ( ) = Symbols : : LocateExecutableSymbolFile ( module_spec ) ;
if ( module_spec . GetSymbolFileSpec ( ) )
{
ModuleSpec executable_module_spec = Symbols : : LocateExecutableObjectFile ( module_spec ) ;
if ( executable_module_spec . GetFileSpec ( ) . Exists ( ) )
{
module_spec . GetFileSpec ( ) = executable_module_spec . GetFileSpec ( ) ;
}
}
if ( ! module_spec . GetSymbolFileSpec ( ) | | ! module_spec . GetSymbolFileSpec ( ) )
Symbols : : DownloadObjectAndSymbolFile ( module_spec , true ) ;
if ( module_spec . GetFileSpec ( ) . Exists ( ) )
{
ModuleSP module_sp ( new Module ( module_spec ) ) ;
if ( module_sp . get ( ) & & module_sp - > GetObjectFile ( ) )
{
// Get the current target executable
ModuleSP exe_module_sp ( target . GetExecutableModule ( ) ) ;
// Make sure you don't already have the right module loaded and they will be uniqued
if ( exe_module_sp . get ( ) ! = module_sp . get ( ) )
target . SetExecutableModule ( module_sp , false ) ;
}
}
}
}
else if ( m_comm . RemoteIsDarwinKernel ( ) )
{
m_dyld_plugin_name = DynamicLoaderDarwinKernel : : GetPluginNameStatic ( ) ;
if ( kernel_load_addr ! = LLDB_INVALID_ADDRESS )
{
m_kernel_load_addr = kernel_load_addr ;
}
}
// Set the thread ID
UpdateThreadListIfNeeded ( ) ;
SetID ( 1 ) ;
GetThreadList ( ) ;
SetPrivateState ( eStateStopped ) ;
StreamSP async_strm_sp ( target . GetDebugger ( ) . GetAsyncOutputStream ( ) ) ;
if ( async_strm_sp )
{
const char * cstr ;
if ( ( cstr = m_comm . GetKernelVersion ( ) ) ! = NULL )
{
async_strm_sp - > Printf ( " Version: %s \n " , cstr ) ;
async_strm_sp - > Flush ( ) ;
}
// if ((cstr = m_comm.GetImagePath ()) != NULL)
// {
// async_strm_sp->Printf ("Image Path: %s\n", cstr);
// async_strm_sp->Flush();
// }
}
}
else
{
error . SetErrorString ( " KDP_REATTACH failed " ) ;
}
}
else
{
error . SetErrorString ( " KDP_REATTACH failed " ) ;
}
}
else
{
error . SetErrorString ( " invalid reply port from UDP connection " ) ;
}
}
else
{
if ( error . Success ( ) )
error . SetErrorStringWithFormat ( " failed to connect to '%s' " , remote_url ) ;
}
if ( error . Fail ( ) )
m_comm . Disconnect ( ) ;
return error ;
}
//----------------------------------------------------------------------
// Process Control
//----------------------------------------------------------------------
Error
ProcessKDP : : DoLaunch ( Module * exe_module ,
ProcessLaunchInfo & launch_info )
{
Error error ;
error . SetErrorString ( " launching not supported in kdp-remote plug-in " ) ;
return error ;
}
Error
ProcessKDP : : DoAttachToProcessWithID ( lldb : : pid_t attach_pid , const ProcessAttachInfo & attach_info )
{
Error error ;
error . SetErrorString ( " attach to process by ID is not suppported in kdp remote debugging " ) ;
return error ;
}
Error
ProcessKDP : : DoAttachToProcessWithName ( const char * process_name , const ProcessAttachInfo & attach_info )
{
Error error ;
error . SetErrorString ( " attach to process by name is not suppported in kdp remote debugging " ) ;
return error ;
}
void
ProcessKDP : : DidAttach ( ArchSpec & process_arch )
{
Process : : DidAttach ( process_arch ) ;
Log * log ( ProcessKDPLog : : GetLogIfAllCategoriesSet ( KDP_LOG_PROCESS ) ) ;
if ( log )
log - > Printf ( " ProcessKDP::DidAttach() " ) ;
if ( GetID ( ) ! = LLDB_INVALID_PROCESS_ID )
{
GetHostArchitecture ( process_arch ) ;
}
}
addr_t
ProcessKDP : : GetImageInfoAddress ( )
{
return m_kernel_load_addr ;
}
lldb_private : : DynamicLoader *
ProcessKDP : : GetDynamicLoader ( )
{
if ( m_dyld_ap . get ( ) = = NULL )
m_dyld_ap . reset ( DynamicLoader : : FindPlugin ( this , m_dyld_plugin_name . IsEmpty ( ) ? NULL : m_dyld_plugin_name . GetCString ( ) ) ) ;
return m_dyld_ap . get ( ) ;
}
Error
ProcessKDP : : WillResume ( )
{
return Error ( ) ;
}
Error
ProcessKDP : : DoResume ( )
{
Error error ;
Log * log ( ProcessKDPLog : : GetLogIfAllCategoriesSet ( KDP_LOG_PROCESS ) ) ;
// Only start the async thread if we try to do any process control
if ( ! m_async_thread . IsJoinable ( ) )
StartAsyncThread ( ) ;
bool resume = false ;
// With KDP there is only one thread we can tell what to do
ThreadSP kernel_thread_sp ( m_thread_list . FindThreadByProtocolID ( g_kernel_tid ) ) ;
if ( kernel_thread_sp )
{
const StateType thread_resume_state = kernel_thread_sp - > GetTemporaryResumeState ( ) ;
if ( log )
log - > Printf ( " ProcessKDP::DoResume() thread_resume_state = %s " , StateAsCString ( thread_resume_state ) ) ;
switch ( thread_resume_state )
{
case eStateSuspended :
// Nothing to do here when a thread will stay suspended
// we just leave the CPU mask bit set to zero for the thread
if ( log )
log - > Printf ( " ProcessKDP::DoResume() = suspended??? " ) ;
break ;
case eStateStepping :
{
lldb : : RegisterContextSP reg_ctx_sp ( kernel_thread_sp - > GetRegisterContext ( ) ) ;
if ( reg_ctx_sp )
{
if ( log )
log - > Printf ( " ProcessKDP::DoResume () reg_ctx_sp->HardwareSingleStep (true); " ) ;
reg_ctx_sp - > HardwareSingleStep ( true ) ;
resume = true ;
}
else
{
error . SetErrorStringWithFormat ( " KDP thread 0x%llx has no register context " , kernel_thread_sp - > GetID ( ) ) ;
}
}
break ;
case eStateRunning :
{
lldb : : RegisterContextSP reg_ctx_sp ( kernel_thread_sp - > GetRegisterContext ( ) ) ;
if ( reg_ctx_sp )
{
if ( log )
log - > Printf ( " ProcessKDP::DoResume () reg_ctx_sp->HardwareSingleStep (false); " ) ;
reg_ctx_sp - > HardwareSingleStep ( false ) ;
resume = true ;
}
else
{
error . SetErrorStringWithFormat ( " KDP thread 0x%llx has no register context " , kernel_thread_sp - > GetID ( ) ) ;
}
}
break ;
default :
// The only valid thread resume states are listed above
assert ( ! " invalid thread resume state " ) ;
break ;
}
}
if ( resume )
{
if ( log )
log - > Printf ( " ProcessKDP::DoResume () sending resume " ) ;
if ( m_comm . SendRequestResume ( ) )
{
m_async_broadcaster . BroadcastEvent ( eBroadcastBitAsyncContinue ) ;
SetPrivateState ( eStateRunning ) ;
}
else
error . SetErrorString ( " KDP resume failed " ) ;
}
else
{
error . SetErrorString ( " kernel thread is suspended " ) ;
}
return error ;
}
lldb : : ThreadSP
ProcessKDP : : GetKernelThread ( )
{
// KDP only tells us about one thread/core. Any other threads will usually
// be the ones that are read from memory by the OS plug-ins.
ThreadSP thread_sp ( m_kernel_thread_wp . lock ( ) ) ;
if ( ! thread_sp )
{
thread_sp . reset ( new ThreadKDP ( * this , g_kernel_tid ) ) ;
m_kernel_thread_wp = thread_sp ;
}
return thread_sp ;
}
bool
ProcessKDP : : UpdateThreadList ( ThreadList & old_thread_list , ThreadList & new_thread_list )
{
// locker will keep a mutex locked until it goes out of scope
Log * log ( ProcessKDPLog : : GetLogIfAllCategoriesSet ( KDP_LOG_THREAD ) ) ;
if ( log & & log - > GetMask ( ) . Test ( KDP_LOG_VERBOSE ) )
log - > Printf ( " ProcessKDP::%s (pid = % " PRIu64 " ) " , __FUNCTION__ , GetID ( ) ) ;
// Even though there is a CPU mask, it doesn't mean we can see each CPU
// individually, there is really only one. Lets call this thread 1.
ThreadSP thread_sp ( old_thread_list . FindThreadByProtocolID ( g_kernel_tid , false ) ) ;
if ( ! thread_sp )
thread_sp = GetKernelThread ( ) ;
new_thread_list . AddThread ( thread_sp ) ;
return new_thread_list . GetSize ( false ) > 0 ;
}
void
ProcessKDP : : RefreshStateAfterStop ( )
{
// Let all threads recover from stopping and do any clean up based
// on the previous thread state (if any).
m_thread_list . RefreshStateAfterStop ( ) ;
}
Error
ProcessKDP : : DoHalt ( bool & caused_stop )
{
Error error ;
if ( m_comm . IsRunning ( ) )
{
if ( m_destroy_in_process )
{
// If we are attemping to destroy, we need to not return an error to
// Halt or DoDestroy won't get called.
// We are also currently running, so send a process stopped event
SetPrivateState ( eStateStopped ) ;
}
else
{
error . SetErrorString ( " KDP cannot interrupt a running kernel " ) ;
}
}
return error ;
}
Error
ProcessKDP : : DoDetach ( bool keep_stopped )
{
Error error ;
Log * log ( ProcessKDPLog : : GetLogIfAllCategoriesSet ( KDP_LOG_PROCESS ) ) ;
if ( log )
log - > Printf ( " ProcessKDP::DoDetach(keep_stopped = %i) " , keep_stopped ) ;
if ( m_comm . IsRunning ( ) )
{
// We are running and we can't interrupt a running kernel, so we need
// to just close the connection to the kernel and hope for the best
}
else
{
// If we are going to keep the target stopped, then don't send the disconnect message.
if ( ! keep_stopped & & m_comm . IsConnected ( ) )
{
const bool success = m_comm . SendRequestDisconnect ( ) ;
if ( log )
{
if ( success )
log - > PutCString ( " ProcessKDP::DoDetach() detach packet sent successfully " ) ;
else
log - > PutCString ( " ProcessKDP::DoDetach() connection channel shutdown failed " ) ;
}
m_comm . Disconnect ( ) ;
}
}
StopAsyncThread ( ) ;
m_comm . Clear ( ) ;
SetPrivateState ( eStateDetached ) ;
ResumePrivateStateThread ( ) ;
//KillDebugserverProcess ();
return error ;
}
Error
ProcessKDP : : DoDestroy ( )
{
// For KDP there really is no difference between destroy and detach
bool keep_stopped = false ;
return DoDetach ( keep_stopped ) ;
}
//------------------------------------------------------------------
// Process Queries
//------------------------------------------------------------------
bool
ProcessKDP : : IsAlive ( )
{
return m_comm . IsConnected ( ) & & Process : : IsAlive ( ) ;
}
//------------------------------------------------------------------
// Process Memory
//------------------------------------------------------------------
size_t
ProcessKDP : : DoReadMemory ( addr_t addr , void * buf , size_t size , Error & error )
{
uint8_t * data_buffer = ( uint8_t * ) buf ;
if ( m_comm . IsConnected ( ) )
{
const size_t max_read_size = 512 ;
size_t total_bytes_read = 0 ;
// Read the requested amount of memory in 512 byte chunks
while ( total_bytes_read < size )
{
size_t bytes_to_read_this_request = size - total_bytes_read ;
if ( bytes_to_read_this_request > max_read_size )
{
bytes_to_read_this_request = max_read_size ;
}
size_t bytes_read = m_comm . SendRequestReadMemory ( addr + total_bytes_read ,
data_buffer + total_bytes_read ,
bytes_to_read_this_request , error ) ;
total_bytes_read + = bytes_read ;
if ( error . Fail ( ) | | bytes_read = = 0 )
{
return total_bytes_read ;
}
}
return total_bytes_read ;
}
error . SetErrorString ( " not connected " ) ;
return 0 ;
}
size_t
ProcessKDP : : DoWriteMemory ( addr_t addr , const void * buf , size_t size , Error & error )
{
if ( m_comm . IsConnected ( ) )
return m_comm . SendRequestWriteMemory ( addr , buf , size , error ) ;
error . SetErrorString ( " not connected " ) ;
return 0 ;
}
lldb : : addr_t
ProcessKDP : : DoAllocateMemory ( size_t size , uint32_t permissions , Error & error )
{
error . SetErrorString ( " memory allocation not suppported in kdp remote debugging " ) ;
return LLDB_INVALID_ADDRESS ;
}
Error
ProcessKDP : : DoDeallocateMemory ( lldb : : addr_t addr )
{
Error error ;
error . SetErrorString ( " memory deallocation not suppported in kdp remote debugging " ) ;
return error ;
}
Error
ProcessKDP : : EnableBreakpointSite ( BreakpointSite * bp_site )
{
if ( m_comm . LocalBreakpointsAreSupported ( ) )
{
Error error ;
if ( ! bp_site - > IsEnabled ( ) )
{
if ( m_comm . SendRequestBreakpoint ( true , bp_site - > GetLoadAddress ( ) ) )
{
bp_site - > SetEnabled ( true ) ;
bp_site - > SetType ( BreakpointSite : : eExternal ) ;
}
else
{
error . SetErrorString ( " KDP set breakpoint failed " ) ;
}
}
return error ;
}
return EnableSoftwareBreakpoint ( bp_site ) ;
}
Error
ProcessKDP : : DisableBreakpointSite ( BreakpointSite * bp_site )
{
if ( m_comm . LocalBreakpointsAreSupported ( ) )
{
Error error ;
if ( bp_site - > IsEnabled ( ) )
{
BreakpointSite : : Type bp_type = bp_site - > GetType ( ) ;
if ( bp_type = = BreakpointSite : : eExternal )
{
if ( m_destroy_in_process & & m_comm . IsRunning ( ) )
{
// We are trying to destroy our connection and we are running
bp_site - > SetEnabled ( false ) ;
}
else
{
if ( m_comm . SendRequestBreakpoint ( false , bp_site - > GetLoadAddress ( ) ) )
bp_site - > SetEnabled ( false ) ;
else
error . SetErrorString ( " KDP remove breakpoint failed " ) ;
}
}
else
{
error = DisableSoftwareBreakpoint ( bp_site ) ;
}
}
return error ;
}
return DisableSoftwareBreakpoint ( bp_site ) ;
}
Error
ProcessKDP : : EnableWatchpoint ( Watchpoint * wp , bool notify )
{
Error error ;
error . SetErrorString ( " watchpoints are not suppported in kdp remote debugging " ) ;
return error ;
}
Error
ProcessKDP : : DisableWatchpoint ( Watchpoint * wp , bool notify )
{
Error error ;
error . SetErrorString ( " watchpoints are not suppported in kdp remote debugging " ) ;
return error ;
}
void
ProcessKDP : : Clear ( )
{
m_thread_list . Clear ( ) ;
}
Error
ProcessKDP : : DoSignal ( int signo )
{
Error error ;
error . SetErrorString ( " sending signals is not suppported in kdp remote debugging " ) ;
return error ;
}
void
ProcessKDP : : Initialize ( )
{
static std : : once_flag g_once_flag ;
std : : call_once ( g_once_flag , [ ] ( )
{
PluginManager : : RegisterPlugin ( GetPluginNameStatic ( ) ,
GetPluginDescriptionStatic ( ) ,
CreateInstance ,
DebuggerInitialize ) ;
Log : : Callbacks log_callbacks = {
ProcessKDPLog : : DisableLog ,
ProcessKDPLog : : EnableLog ,
ProcessKDPLog : : ListLogCategories
} ;
Log : : RegisterLogChannel ( ProcessKDP : : GetPluginNameStatic ( ) , log_callbacks ) ;
} ) ;
}
void
ProcessKDP : : DebuggerInitialize ( lldb_private : : Debugger & debugger )
{
if ( ! PluginManager : : GetSettingForProcessPlugin ( debugger , PluginProperties : : GetSettingName ( ) ) )
{
const bool is_global_setting = true ;
PluginManager : : CreateSettingForProcessPlugin ( debugger ,
GetGlobalPluginProperties ( ) - > GetValueProperties ( ) ,
ConstString ( " Properties for the kdp-remote process plug-in. " ) ,
is_global_setting ) ;
}
}
bool
ProcessKDP : : StartAsyncThread ( )
{
Log * log ( ProcessKDPLog : : GetLogIfAllCategoriesSet ( KDP_LOG_PROCESS ) ) ;
if ( log )
log - > Printf ( " ProcessKDP::StartAsyncThread () " ) ;
if ( m_async_thread . IsJoinable ( ) )
return true ;
m_async_thread = ThreadLauncher : : LaunchThread ( " <lldb.process.kdp-remote.async> " , ProcessKDP : : AsyncThread , this , NULL ) ;
return m_async_thread . IsJoinable ( ) ;
}
void
ProcessKDP : : StopAsyncThread ( )
{
Log * log ( ProcessKDPLog : : GetLogIfAllCategoriesSet ( KDP_LOG_PROCESS ) ) ;
if ( log )
log - > Printf ( " ProcessKDP::StopAsyncThread () " ) ;
m_async_broadcaster . BroadcastEvent ( eBroadcastBitAsyncThreadShouldExit ) ;
// Stop the stdio thread
if ( m_async_thread . IsJoinable ( ) )
m_async_thread . Join ( nullptr ) ;
}
void *
ProcessKDP : : AsyncThread ( void * arg )
{
ProcessKDP * process = ( ProcessKDP * ) arg ;
const lldb : : pid_t pid = process - > GetID ( ) ;
Log * log ( ProcessKDPLog : : GetLogIfAllCategoriesSet ( KDP_LOG_PROCESS ) ) ;
if ( log )
log - > Printf ( " ProcessKDP::AsyncThread (arg = %p, pid = % " PRIu64 " ) thread starting... " , arg , pid ) ;
2016-07-23 16:50:09 -04:00
ListenerSP listener_sp ( Listener : : MakeListener ( " ProcessKDP::AsyncThread " ) ) ;
2016-01-06 15:12:03 -05:00
EventSP event_sp ;
const uint32_t desired_event_mask = eBroadcastBitAsyncContinue |
eBroadcastBitAsyncThreadShouldExit ;
2016-07-23 16:50:09 -04:00
if ( listener_sp - > StartListeningForEvents ( & process - > m_async_broadcaster , desired_event_mask ) = = desired_event_mask )
2016-01-06 15:12:03 -05:00
{
bool done = false ;
while ( ! done )
{
if ( log )
log - > Printf ( " ProcessKDP::AsyncThread (pid = % " PRIu64 " ) listener.WaitForEvent (NULL, event_sp)... " ,
pid ) ;
2016-07-23 16:50:09 -04:00
if ( listener_sp - > WaitForEvent ( NULL , event_sp ) )
2016-01-06 15:12:03 -05:00
{
uint32_t event_type = event_sp - > GetType ( ) ;
if ( log )
log - > Printf ( " ProcessKDP::AsyncThread (pid = % " PRIu64 " ) Got an event of type: %d... " ,
pid ,
event_type ) ;
// When we are running, poll for 1 second to try and get an exception
// to indicate the process has stopped. If we don't get one, check to
// make sure no one asked us to exit
bool is_running = false ;
DataExtractor exc_reply_packet ;
do
{
switch ( event_type )
{
case eBroadcastBitAsyncContinue :
{
is_running = true ;
if ( process - > m_comm . WaitForPacketWithTimeoutMicroSeconds ( exc_reply_packet , 1 * USEC_PER_SEC ) )
{
ThreadSP thread_sp ( process - > GetKernelThread ( ) ) ;
if ( thread_sp )
{
lldb : : RegisterContextSP reg_ctx_sp ( thread_sp - > GetRegisterContext ( ) ) ;
if ( reg_ctx_sp )
reg_ctx_sp - > InvalidateAllRegisters ( ) ;
static_cast < ThreadKDP * > ( thread_sp . get ( ) ) - > SetStopInfoFrom_KDP_EXCEPTION ( exc_reply_packet ) ;
}
// TODO: parse the stop reply packet
is_running = false ;
process - > SetPrivateState ( eStateStopped ) ;
}
else
{
// Check to see if we are supposed to exit. There is no way to
// interrupt a running kernel, so all we can do is wait for an
// exception or detach...
2016-07-23 16:50:09 -04:00
if ( listener_sp - > GetNextEvent ( event_sp ) )
2016-01-06 15:12:03 -05:00
{
// We got an event, go through the loop again
event_type = event_sp - > GetType ( ) ;
}
}
}
break ;
case eBroadcastBitAsyncThreadShouldExit :
if ( log )
log - > Printf ( " ProcessKDP::AsyncThread (pid = % " PRIu64 " ) got eBroadcastBitAsyncThreadShouldExit... " ,
pid ) ;
done = true ;
is_running = false ;
break ;
default :
if ( log )
log - > Printf ( " ProcessKDP::AsyncThread (pid = % " PRIu64 " ) got unknown event 0x%8.8x " ,
pid ,
event_type ) ;
done = true ;
is_running = false ;
break ;
}
} while ( is_running ) ;
}
else
{
if ( log )
log - > Printf ( " ProcessKDP::AsyncThread (pid = % " PRIu64 " ) listener.WaitForEvent (NULL, event_sp) => false " ,
pid ) ;
done = true ;
}
}
}
if ( log )
log - > Printf ( " ProcessKDP::AsyncThread (arg = %p, pid = % " PRIu64 " ) thread exiting... " ,
arg ,
pid ) ;
process - > m_async_thread . Reset ( ) ;
return NULL ;
}
class CommandObjectProcessKDPPacketSend : public CommandObjectParsed
{
private :
OptionGroupOptions m_option_group ;
OptionGroupUInt64 m_command_byte ;
OptionGroupString m_packet_data ;
virtual Options *
GetOptions ( )
{
return & m_option_group ;
}
public :
CommandObjectProcessKDPPacketSend ( CommandInterpreter & interpreter ) :
CommandObjectParsed ( interpreter ,
" process plugin packet send " ,
" Send a custom packet through the KDP protocol by specifying the command byte and the packet payload data. A packet will be sent with a correct header and payload, and the raw result bytes will be displayed as a string value. " ,
NULL ) ,
m_option_group ( interpreter ) ,
m_command_byte ( LLDB_OPT_SET_1 , true , " command " , ' c ' , 0 , eArgTypeNone , " Specify the command byte to use when sending the KDP request packet. " , 0 ) ,
m_packet_data ( LLDB_OPT_SET_1 , false , " payload " , ' p ' , 0 , eArgTypeNone , " Specify packet payload bytes as a hex ASCII string with no spaces or hex prefixes. " , NULL )
{
m_option_group . Append ( & m_command_byte , LLDB_OPT_SET_ALL , LLDB_OPT_SET_1 ) ;
m_option_group . Append ( & m_packet_data , LLDB_OPT_SET_ALL , LLDB_OPT_SET_1 ) ;
m_option_group . Finalize ( ) ;
}
~ CommandObjectProcessKDPPacketSend ( )
{
}
bool
DoExecute ( Args & command , CommandReturnObject & result )
{
const size_t argc = command . GetArgumentCount ( ) ;
if ( argc = = 0 )
{
if ( ! m_command_byte . GetOptionValue ( ) . OptionWasSet ( ) )
{
result . AppendError ( " the --command option must be set to a valid command byte " ) ;
result . SetStatus ( eReturnStatusFailed ) ;
}
else
{
const uint64_t command_byte = m_command_byte . GetOptionValue ( ) . GetUInt64Value ( 0 ) ;
if ( command_byte > 0 & & command_byte < = UINT8_MAX )
{
ProcessKDP * process = ( ProcessKDP * ) m_interpreter . GetExecutionContext ( ) . GetProcessPtr ( ) ;
if ( process )
{
const StateType state = process - > GetState ( ) ;
if ( StateIsStoppedState ( state , true ) )
{
std : : vector < uint8_t > payload_bytes ;
const char * ascii_hex_bytes_cstr = m_packet_data . GetOptionValue ( ) . GetCurrentValue ( ) ;
if ( ascii_hex_bytes_cstr & & ascii_hex_bytes_cstr [ 0 ] )
{
StringExtractor extractor ( ascii_hex_bytes_cstr ) ;
const size_t ascii_hex_bytes_cstr_len = extractor . GetStringRef ( ) . size ( ) ;
if ( ascii_hex_bytes_cstr_len & 1 )
{
result . AppendErrorWithFormat ( " payload data must contain an even number of ASCII hex characters: '%s' " , ascii_hex_bytes_cstr ) ;
result . SetStatus ( eReturnStatusFailed ) ;
return false ;
}
payload_bytes . resize ( ascii_hex_bytes_cstr_len / 2 ) ;
if ( extractor . GetHexBytes ( & payload_bytes [ 0 ] , payload_bytes . size ( ) , ' \xdd ' ) ! = payload_bytes . size ( ) )
{
result . AppendErrorWithFormat ( " payload data must only contain ASCII hex characters (no spaces or hex prefixes): '%s' " , ascii_hex_bytes_cstr ) ;
result . SetStatus ( eReturnStatusFailed ) ;
return false ;
}
}
Error error ;
DataExtractor reply ;
process - > GetCommunication ( ) . SendRawRequest ( command_byte ,
payload_bytes . empty ( ) ? NULL : payload_bytes . data ( ) ,
payload_bytes . size ( ) ,
reply ,
error ) ;
if ( error . Success ( ) )
{
// Copy the binary bytes into a hex ASCII string for the result
StreamString packet ;
packet . PutBytesAsRawHex8 ( reply . GetDataStart ( ) ,
reply . GetByteSize ( ) ,
endian : : InlHostByteOrder ( ) ,
endian : : InlHostByteOrder ( ) ) ;
result . AppendMessage ( packet . GetString ( ) . c_str ( ) ) ;
result . SetStatus ( eReturnStatusSuccessFinishResult ) ;
return true ;
}
else
{
const char * error_cstr = error . AsCString ( ) ;
if ( error_cstr & & error_cstr [ 0 ] )
result . AppendError ( error_cstr ) ;
else
result . AppendErrorWithFormat ( " unknown error 0x%8.8x " , error . GetError ( ) ) ;
result . SetStatus ( eReturnStatusFailed ) ;
return false ;
}
}
else
{
result . AppendErrorWithFormat ( " process must be stopped in order to send KDP packets, state is %s " , StateAsCString ( state ) ) ;
result . SetStatus ( eReturnStatusFailed ) ;
}
}
else
{
result . AppendError ( " invalid process " ) ;
result . SetStatus ( eReturnStatusFailed ) ;
}
}
else
{
result . AppendErrorWithFormat ( " invalid command byte 0x% " PRIx64 " , valid values are 1 - 255 " , command_byte ) ;
result . SetStatus ( eReturnStatusFailed ) ;
}
}
}
else
{
result . AppendErrorWithFormat ( " '%s' takes no arguments, only options. " , m_cmd_name . c_str ( ) ) ;
result . SetStatus ( eReturnStatusFailed ) ;
}
return false ;
}
} ;
class CommandObjectProcessKDPPacket : public CommandObjectMultiword
{
private :
public :
CommandObjectProcessKDPPacket ( CommandInterpreter & interpreter ) :
CommandObjectMultiword ( interpreter ,
" process plugin packet " ,
" Commands that deal with KDP remote packets. " ,
NULL )
{
LoadSubCommand ( " send " , CommandObjectSP ( new CommandObjectProcessKDPPacketSend ( interpreter ) ) ) ;
}
~ CommandObjectProcessKDPPacket ( )
{
}
} ;
class CommandObjectMultiwordProcessKDP : public CommandObjectMultiword
{
public :
2016-07-23 16:50:09 -04:00
CommandObjectMultiwordProcessKDP ( CommandInterpreter & interpreter )
: CommandObjectMultiword ( interpreter , " process plugin " , " Commands for operating on a ProcessKDP process. " ,
" process plugin <subcommand> [<subcommand-options>] " )
2016-01-06 15:12:03 -05:00
{
LoadSubCommand ( " packet " , CommandObjectSP ( new CommandObjectProcessKDPPacket ( interpreter ) ) ) ;
}
~ CommandObjectMultiwordProcessKDP ( )
{
}
} ;
CommandObject *
ProcessKDP : : GetPluginCommandObject ( )
{
if ( ! m_command_sp )
m_command_sp . reset ( new CommandObjectMultiwordProcessKDP ( GetTarget ( ) . GetDebugger ( ) . GetCommandInterpreter ( ) ) ) ;
return m_command_sp . get ( ) ;
}