@ -1,15 +1,16 @@
# include "debug.h"
# include "debug.h"
# include <thread>
# include <lldb/API/SBTarget.h>
# include <lldb/API/SBProcess.h>
# include "lldb/API/SBTarget.h"
# include <lldb/API/SBEvent.h>
# include "lldb/API/SBProcess.h"
# include <lldb/API/SBBreakpoint.h>
# include "lldb/API/SBListener.h"
# include <lldb/API/SBThread.h>
# include "lldb/API/SBEvent.h"
# include <lldb/API/SBStream.h>
# include "lldb/API/SBBreakpoint.h"
# include <lldb/API/SBDeclaration.h>
# include "lldb/API/SBThread.h"
# include <lldb/API/SBCommandInterpreter.h>
# include "lldb/API/SBStream.h"
# include <lldb/API/SBCommandReturnObject.h>
# include "lldb/API/SBDeclaration.h"
# include "terminal.h"
# include <iostream> //TODO: remove
# include <iostream> //TODO: remove
using namespace std ;
using namespace std ;
@ -18,7 +19,7 @@ void log(const char *msg, void *) {
cout < < " debugger log: " < < msg < < endl ;
cout < < " debugger log: " < < msg < < endl ;
}
}
Debug : : Debug ( ) : stopped ( false ) {
Debug : : Debug ( ) : listener ( " juCi++ lldb listener " ) , state ( lldb : : StateType : : eStateInvalid ) , buffer_size ( 131072 ) {
lldb : : SBDebugger : : Initialize ( ) ;
lldb : : SBDebugger : : Initialize ( ) ;
debugger = lldb : : SBDebugger : : Create ( true , log , nullptr ) ;
debugger = lldb : : SBDebugger : : Create ( true , log , nullptr ) ;
@ -27,157 +28,167 @@ Debug::Debug(): stopped(false) {
void Debug : : start ( std : : shared_ptr < std : : vector < std : : pair < boost : : filesystem : : path , int > > > breakpoints , const boost : : filesystem : : path & executable ,
void Debug : : start ( std : : shared_ptr < std : : vector < std : : pair < boost : : filesystem : : path , int > > > breakpoints , const boost : : filesystem : : path & executable ,
const boost : : filesystem : : path & path , std : : function < void ( int exit_status ) > callback ,
const boost : : filesystem : : path & path , std : : function < void ( int exit_status ) > callback ,
std : : function < void ( const std : : string & status ) > status_callback ,
std : : function < void ( const std : : string & status ) > status_callback ,
std : : function < void ( const boost : : filesystem : : path & file , int line ) > stop_callback ) {
std : : function < void ( const boost : : filesystem : : path & file_path , int line_nr ) > stop_callback ) {
std : : thread debug_thread ( [ this , breakpoints , executable , path , callback , status_callback , stop_callback ] ( ) {
auto target = debugger . CreateTarget ( executable . string ( ) . c_str ( ) ) ;
auto target = debugger . CreateTarget ( executable . string ( ) . c_str ( ) ) ;
auto listener = lldb : : SBListener ( " juCi++ lldb listener " ) ;
if ( ! target . IsValid ( ) ) {
cerr < < " Error: Could not create debug target to: " < < executable < < endl ; //TODO: output to terminal instead
return ;
}
for ( auto & breakpoint : * breakpoints ) {
if ( ! target . IsValid ( ) ) {
if ( ! ( target . BreakpointCreateByLocation ( breakpoint . first . string ( ) . c_str ( ) , breakpoint . second ) ) . IsValid ( ) ) {
Terminal : : get ( ) . async_print ( " Error (debug): Could not create debug target to: " + executable . string ( ) + ' \n ' , true ) ;
cerr < < " Error: Could not create breakpoint at: " < < breakpoint . first < < " : " < < breakpoint . second < < endl ; //TODO: output to terminal instead
return ;
return ;
}
}
}
lldb : : SBError error ;
process = std : : unique_ptr < lldb : : SBProcess > ( new lldb : : SBProcess ( target . Launch ( listener , nullptr , nullptr , nullptr , nullptr , nullptr , path . string ( ) . c_str ( ) , lldb : : eLaunchFlagNone , false , error ) ) ) ;
if ( error . Fail ( ) ) {
for ( auto & breakpoint : * breakpoints ) {
cerr < < " Error (debug): " < < error . GetCString ( ) < < endl ; //TODO: output to terminal instead
if ( ! ( target . BreakpointCreateByLocation ( breakpoint . first . string ( ) . c_str ( ) , breakpoint . second ) ) . IsValid ( ) ) {
Terminal : : get ( ) . async_print ( " Error (debug): Could not create breakpoint at: " + breakpoint . first . string ( ) + " : " + std : : to_string ( breakpoint . second ) + ' \n ' , true ) ;
return ;
return ;
}
}
}
lldb : : SBError error ;
process = std : : unique_ptr < lldb : : SBProcess > ( new lldb : : SBProcess ( target . Launch ( listener , nullptr , nullptr , nullptr , nullptr , nullptr , path . string ( ) . c_str ( ) , lldb : : eLaunchFlagNone , false , error ) ) ) ;
if ( error . Fail ( ) ) {
Terminal : : get ( ) . async_print ( std : : string ( " Error (debug): " ) + error . GetCString ( ) + ' \n ' , true ) ;
return ;
}
if ( debug_thread . joinable ( ) )
debug_thread . join ( ) ;
debug_thread = std : : thread ( [ this , breakpoints , executable , path , callback , status_callback , stop_callback ] ( ) {
lldb : : SBEvent event ;
lldb : : SBEvent event ;
while ( true ) {
while ( true ) {
if ( listener . WaitForEvent ( 3 , event ) ) {
event_mutex . lock ( ) ;
auto state = process - > GetStateFromEvent ( event ) ;
if ( listener . WaitForEvent ( 1 , event ) ) {
if ( ( event . GetType ( ) & lldb : : SBProcess : : eBroadcastBitStateChanged ) > 0 ) {
//Update debug status
auto state = process - > GetStateFromEvent ( event ) ;
lldb : : SBStream stream ;
this - > state = state ;
event . GetDescription ( stream ) ;
std : : string event_desc = stream . GetData ( ) ;
//Update debug status
event_desc . pop_back ( ) ;
lldb : : SBStream stream ;
auto pos = event_desc . rfind ( " = " ) ;
event . GetDescription ( stream ) ;
if ( status_callback & & pos ! = std : : string : : npos )
std : : string event_desc = stream . GetData ( ) ;
status_callback ( event_desc . substr ( pos + 3 ) ) ;
event_desc . pop_back ( ) ;
auto pos = event_desc . rfind ( " = " ) ;
bool expected = false ;
if ( status_callback & & pos ! = std : : string : : npos )
if ( state = = lldb : : StateType : : eStateStopped & & stopped . compare_exchange_strong ( expected , true ) ) {
status_callback ( event_desc . substr ( pos + 3 ) ) ;
auto line_entry = process - > GetSelectedThread ( ) . GetSelectedFrame ( ) . GetLineEntry ( ) ;
if ( stop_callback ) {
if ( state = = lldb : : StateType : : eStateStopped ) {
lldb : : SBStream stream ;
auto line_entry = process - > GetSelectedThread ( ) . GetSelectedFrame ( ) . GetLineEntry ( ) ;
line_entry . GetFileSpec ( ) . GetDescription ( stream ) ;
if ( stop_callback ) {
stop_callback ( stream . GetData ( ) , line_entry . GetLine ( ) ) ;
lldb : : SBStream stream ;
line_entry . GetFileSpec ( ) . GetDescription ( stream ) ;
stop_callback ( stream . GetData ( ) , line_entry . GetLine ( ) ) ;
}
}
}
/*lldb::SBStream stream;
else if ( state = = lldb : : StateType : : eStateExited ) {
process - > GetSelectedThread ( ) . GetDescription ( stream ) ;
auto exit_status = process - > GetExitStatus ( ) ;
cout < < stream . GetData ( ) < < endl ; */
if ( callback )
for ( uint32_t thread_index = 0 ; thread_index < process - > GetNumThreads ( ) ; thread_index + + ) {
callback ( exit_status ) ;
auto thread = process - > GetThreadAtIndex ( thread_index ) ;
if ( status_callback )
for ( uint32_t frame_index = 0 ; frame_index < thread . GetNumFrames ( ) ; frame_index + + ) {
status_callback ( " " ) ;
auto frame = thread . GetFrameAtIndex ( frame_index ) ;
if ( stop_callback )
auto values = frame . GetVariables ( false , true , true , false ) ;
stop_callback ( " " , 0 ) ;
for ( uint32_t value_index = 0 ; value_index < values . GetSize ( ) ; value_index + + ) {
process . reset ( ) ;
/*cout << thread_index << ", " << frame_index << endl;
this - > state = lldb : : StateType : : eStateInvalid ;
lldb : : SBStream stream ;
event_mutex . unlock ( ) ;
process - > GetDescription ( stream ) ;
return ;
cout < < stream . GetData ( ) < < endl ;
stream . Clear ( ) ;
process - > GetSelectedThread ( ) . GetSelectedFrame ( ) . GetLineEntry ( ) . GetDescription ( stream ) ;
cout < < stream . GetData ( ) < < endl ; */
/*lldb::SBStream stream;
auto value = values . GetValueAtIndex ( value_index ) ;
cout < < value . GetFrame ( ) . GetSymbol ( ) . GetName ( ) < < endl ;
auto declaration = value . GetDeclaration ( ) ;
if ( declaration . IsValid ( ) )
cout < < declaration . GetFileSpec ( ) . GetFilename ( ) < < " : " < < declaration . GetLine ( ) < < " : " < < declaration . GetColumn ( ) < < endl ;
value . GetDescription ( stream ) ;
cout < < " " < < stream . GetData ( ) < < endl ;
stream . Clear ( ) ;
value . GetData ( ) . GetDescription ( stream ) ;
cout < < " " < < stream . GetData ( ) < < endl ; */
}
}
}
}
}
else if ( state = = lldb : : StateType : : eStateExited ) {
else if ( state = = lldb : : StateType : : eStateCrashed ) {
auto exit_status = process - > GetExitStatus ( ) ;
if ( callback )
if ( callback )
callback ( - 1 ) ;
callback ( exit_status ) ;
if ( status_callback )
if ( status_callback )
status_callback ( " " ) ;
status_callback ( " " ) ;
if ( stop_callback )
if ( stop_callback )
stop_callback ( " " , 0 ) ;
stop_callback ( " " , 0 ) ;
process . reset ( ) ;
process . reset ( ) ;
this - > state = lldb : : StateType : : eStateInvalid ;
stopped = false ;
event_mutex . unlock ( ) ;
return ;
return ;
}
}
}
if ( ( event . GetType ( ) & lldb : : SBProcess : : eBroadcastBitSTDOUT ) > 0 ) {
else if ( state = = lldb : : StateType : : eStateCrashed ) {
char buffer [ buffer_size ] ;
if ( callback )
size_t n ;
callback ( - 1 ) ;
while ( ( n = process - > GetSTDOUT ( buffer , buffer_size ) ) ! = 0 )
if ( status_callback )
Terminal : : get ( ) . async_print ( std : : string ( buffer , n ) ) ;
status_callback ( " " ) ;
}
if ( stop_callback )
//TODO: for some reason stderr is redirected to stdout
stop_callback ( " " , 0 ) ;
if ( ( event . GetType ( ) & lldb : : SBProcess : : eBroadcastBitSTDERR ) > 0 ) {
process . reset ( ) ;
char buffer [ buffer_size ] ;
stopped = false ;
size_t n ;
return ;
while ( ( n = process - > GetSTDERR ( buffer , buffer_size ) ) ! = 0 )
Terminal : : get ( ) . async_print ( std : : string ( buffer , n ) , true ) ;
}
}
}
}
event_mutex . unlock ( ) ;
this_thread : : sleep_for ( std : : chrono : : milliseconds ( 200 ) ) ;
this_thread : : sleep_for ( std : : chrono : : milliseconds ( 200 ) ) ;
}
}
} ) ;
} ) ;
debug_thread . detach ( ) ;
}
}
void Debug : : continue_debug ( ) {
void Debug : : continue_debug ( ) {
bool expected = true ;
event_mutex . lock ( ) ;
if ( stopped . compare_exchange_strong ( expected , false ) )
if ( state = = lldb : : StateType : : eStateStopped )
process - > Continue ( ) ;
process - > Continue ( ) ;
event_mutex . unlock ( ) ;
}
}
void Debug : : stop ( ) {
void Debug : : stop ( ) {
auto error = process - > Stop ( ) ;
event_mutex . lock ( ) ;
if ( error . Fail ( ) ) {
if ( state = = lldb : : StateType : : eStateRunning ) {
cerr < < " Error (debug): " < < error . GetCString ( ) < < endl ; //TODO: output to terminal instead
auto error = process - > Stop ( ) ;
return ;
if ( error . Fail ( ) )
Terminal : : get ( ) . async_print ( std : : string ( " Error (debug): " ) + error . GetCString ( ) + ' \n ' , true ) ;
}
}
event_mutex . unlock ( ) ;
}
}
void Debug : : kill ( ) {
void Debug : : kill ( ) {
auto error = process - > Kill ( ) ;
event_mutex . lock ( ) ;
if ( error . Fail ( ) ) {
if ( process ) {
cerr < < " Error (debug): " < < error . GetCString ( ) < < endl ; //TODO: output to terminal instead
auto error = process - > Kill ( ) ;
return ;
if ( error . Fail ( ) )
Terminal : : get ( ) . async_print ( std : : string ( " Error (debug): " ) + error . GetCString ( ) + ' \n ' , true ) ;
}
}
event_mutex . unlock ( ) ;
}
std : : pair < std : : string , std : : string > Debug : : run_command ( const std : : string & command ) {
std : : pair < std : : string , std : : string > command_return ;
event_mutex . lock ( ) ;
if ( state = = lldb : : StateType : : eStateStopped ) {
lldb : : SBCommandReturnObject command_return_object ;
debugger . GetCommandInterpreter ( ) . HandleCommand ( command . c_str ( ) , command_return_object , true ) ;
command_return . first = command_return_object . GetOutput ( ) ;
command_return . second = command_return_object . GetError ( ) ;
}
event_mutex . unlock ( ) ;
return command_return ;
}
void Debug : : delete_debug ( ) {
kill ( ) ;
if ( debug_thread . joinable ( ) )
debug_thread . join ( ) ;
}
}
std : : string Debug : : get_value ( const std : : string & variable ) {
std : : string Debug : : get_value ( const std : : string & variable ) {
if ( stopped ) {
std : : string variable_value ;
event_mutex . lock ( ) ;
if ( state = = lldb : : StateType : : eStateStopped ) {
auto frame = process - > GetSelectedThread ( ) . GetSelectedFrame ( ) ;
auto frame = process - > GetSelectedThread ( ) . GetSelectedFrame ( ) ;
auto values = frame . GetVariables ( false , true , false , false ) ;
auto values = frame . GetVariables ( tru e, true , true , tru e) ;
for ( uint32_t value_index = 0 ; value_index < values . GetSize ( ) ; value_index + + ) {
for ( uint32_t value_index = 0 ; value_index < values . GetSize ( ) ; value_index + + ) {
lldb : : SBStream stream ;
lldb : : SBStream stream ;
auto value = values . GetValueAtIndex ( value_index ) ;
auto value = values . GetValueAtIndex ( value_index ) ;
if ( value . GetName ( ) = = variable ) {
if ( value . GetName ( ) = = variable ) {
value . GetDescription ( stream ) ;
value . GetDescription ( stream ) ;
return stream . GetData ( ) ;
variable_value = stream . GetData ( ) ;
break ;
}
}
}
}
}
}
return std : : string ( ) ;
event_mutex . unlock ( ) ;
return variable_value ;
}
}