More WIP, debugger starting correctly. Tested several commands and all working.

This commit is contained in:
Martín Lucas Golini
2024-12-28 20:31:33 -03:00
parent d07bb2572c
commit bbfcc74977
16 changed files with 349 additions and 161 deletions

View File

@@ -15,7 +15,8 @@
"args": "${args}",
"cwd": "${cwd}",
"env": "${env}",
"stopOnEntry": "${stopOnEntry}"
"stopOnEntry": "${stopOnEntry}",
"stopAtBeginningOfMainSubprogram": false
}
}
]

View File

@@ -44,6 +44,8 @@ class EE_API Process {
typedef std::function<void( const char* bytes, size_t n )> ReadFn;
static std::vector<std::string> parseArgs( const std::string& str );
Process();
/** @brief Create a process.

View File

@@ -42,7 +42,7 @@ static bool isFlatpakEnv() {
#define PROCESS_PTR ( static_cast<struct subprocess_s*>( mProcess ) )
static std::vector<std::string> parseArgs( const std::string& str ) {
std::vector<std::string> Process::parseArgs( const std::string& str ) {
bool inquote = false;
char quoteChar = 0;
std::vector<std::string> res;

View File

@@ -396,6 +396,10 @@ void UIConsole::setMaxLogLines( const Uint32& maxLogLines ) {
void UIConsole::privPushText( String&& str ) {
Lock l( mMutex );
if ( str.find_first_of( '\r' ) != String::InvalidPos )
String::replaceAll( str, "\r", "" );
if ( str.empty() )
return;
mCmdLog.push_back( { std::move( str ), String::hash( str ) } );
if ( mVisible )
invalidateDraw();

View File

@@ -20,6 +20,8 @@ class Bus {
virtual size_t write( const char* buffer, const size_t& size ) = 0;
virtual ~Bus() {}
protected:
void setState( State state );

View File

@@ -22,6 +22,10 @@ DebuggerClientDap::DebuggerClientDap( const ProtocolSettings& protocolSettings,
std::unique_ptr<Bus>&& bus ) :
mBus( std::move( bus ) ), mProtocol( protocolSettings ) {}
DebuggerClientDap::~DebuggerClientDap() {
mBus.reset();
}
void DebuggerClientDap::makeRequest( const std::string_view& command,
const nlohmann::json& arguments, ResponseHandler onFinish ) {
nlohmann::json jsonCmd = {
@@ -31,10 +35,12 @@ void DebuggerClientDap::makeRequest( const std::string_view& command,
{ "arguments", arguments.empty() ? nlohmann::json::object() : arguments } };
std::string cmd = jsonCmd.dump();
Log::instance()->writel( mDebug ? LogLevel::Info : LogLevel::Debug, cmd );
std::string msg( String::format( "Content-Length: %zu\r\n\r\n%s", cmd.size(), cmd ) );
Log::instance()->writel( mDebug ? LogLevel::Info : LogLevel::Debug,
"DebuggerClientDap::makeRequest:" );
Log::instance()->writel( mDebug ? LogLevel::Info : LogLevel::Debug, msg );
mBus->write( msg.data(), msg.size() );
mRequests[mIdx] = { std::string{ command }, arguments, onFinish };
@@ -113,9 +119,9 @@ void DebuggerClientDap::requestLaunchCommand() {
[this]( const Response& response, const auto& ) {
if ( response.success ) {
mLaunched = true;
checkRunning();
for ( auto listener : mListeners )
listener->launched();
checkRunning();
} else {
if ( response.errorBody ) {
Log::warning( "DebuggerClientDap::requestLaunchCommand: error %ld %s",
@@ -131,7 +137,7 @@ void DebuggerClientDap::requestInitialize() {
{ DAP_CLIENT_ID, "ecode-dap" },
{ DAP_CLIENT_NAME, "ecode dap" },
{ "locale", mProtocol.locale },
{ DAP_ADAPTER_ID, "qdap" },
{ DAP_ADAPTER_ID, "ecode-dap" },
{ DAP_LINES_START_AT1, mProtocol.linesStartAt1 },
{ DAP_COLUMNS_START_AT2, mProtocol.columnsStartAt1 },
{ DAP_PATH, ( mProtocol.pathFormatURI ? DAP_URI : DAP_PATH ) },
@@ -166,8 +172,10 @@ void DebuggerClientDap::asyncRead( const char* bytes, size_t n ) {
#endif
auto message = json::parse( data );
if ( mDebug )
if ( mDebug ) {
Log::debug( "DebuggerClientDap::asyncRead:" );
Log::debug( message.dump() );
}
processProtocolMessage( message );
#ifndef EE_DEBUG
@@ -229,7 +237,7 @@ void DebuggerClientDap::errorResponse( const std::string& summary,
void DebuggerClientDap::processEvent( const nlohmann::json& msg ) {
const std::string event = msg.value( DAP_EVENT, "" );
const auto body = msg[DAP_BODY];
const auto body = msg.contains( DAP_BODY ) ? msg[DAP_BODY] : nlohmann::json{};
if ( "initialized"sv == event ) {
processEventInitialized();
@@ -262,6 +270,8 @@ void DebuggerClientDap::processEventInitialized() {
return;
}
setState( State::Initialized );
configurationDone();
}
void DebuggerClientDap::processEventTerminated() {
@@ -337,7 +347,8 @@ std::optional<DebuggerClientDap::HeaderInfo> DebuggerClientDap::readHeader() {
}
const auto header = mBuffer.substr( start, end - start );
end += DAP_SEP_SIZE;
while ( std::string_view{ mBuffer }.substr( end, 2 ) == DAP_SEP )
end += DAP_SEP_SIZE;
// header block separator
if ( header.size() == 0 ) {
@@ -362,18 +373,19 @@ std::optional<DebuggerClientDap::HeaderInfo> DebuggerClientDap::readHeader() {
if ( String::startsWith( header, DAP_CONTENT_LENGTH ) ) {
std::string lengthStr( header.substr( sep + 1, header.size() - sep ) );
String::trimInPlace( lengthStr );
Uint64 length;
if ( !String::fromString( length, lengthStr ) ) {
Log::error( "DebuggerClientDap::readHeader invalid value: ", header );
discardExploredBuffer();
continue; // CONTINUE HEADER
} else {
break;
}
}
start = end;
}
if ( length < 0 )
if ( length < 0 || length == std::string::npos )
return std::nullopt;
return HeaderInfo{ end, length };
@@ -683,4 +695,30 @@ bool DebuggerClientDap::watch( const std::string& expression, std::optional<int>
return evaluate( expression, "watch", frameId );
}
bool DebuggerClientDap::configurationDone() {
if ( mState != State::Initialized ) {
Log::warning( "DebuggerClientDap::requestConfigurationDone: trying to configure in an "
"unexpected status" );
return false;
}
if ( !mAdapterCapabilities.supportsConfigurationDoneRequest ) {
for ( auto listener : mListeners )
listener->configured();
return true;
}
makeRequest( "configurationDone", nlohmann::json{},
[this]( const auto& response, const auto& ) {
if ( response.success ) {
mConfigured = true;
checkRunning();
for ( auto listener : mListeners )
listener->configured();
}
} );
return true;
}
} // namespace ecode::dap

View File

@@ -18,6 +18,8 @@ class DebuggerClientDap : public DebuggerClient {
DebuggerClientDap( const ProtocolSettings& protocolSettings, std::unique_ptr<Bus>&& bus );
virtual ~DebuggerClientDap();
bool start() override;
bool resume( int threadId, bool singleThread = false ) override;
@@ -38,11 +40,12 @@ class DebuggerClientDap : public DebuggerClient {
bool threads() override;
bool stackTrace( int threadId, int startFrame, int levels ) override;
bool stackTrace( int threadId, int startFrame = 0, int levels = 0 ) override;
bool scopes( int frameId ) override;
bool variables( int variablesReference, Variable::Type filter, int start, int count ) override;
bool variables( int variablesReference, Variable::Type filter = Variable::Type::Both,
int start = 0, int count = 0 ) override;
bool modules( int start, int count ) override;
@@ -69,6 +72,8 @@ class DebuggerClientDap : public DebuggerClient {
bool watch( const std::string& expression, std::optional<int> frameId ) override;
bool configurationDone() override;
protected:
std::unique_ptr<Bus> mBus;
UnorderedMap<std::string, UnorderedSet<int>> mBreakpoints;
@@ -131,6 +136,7 @@ class DebuggerClientDap : public DebuggerClient {
void requestLaunchCommand();
void processResponseInitialize( const Response& response, const nlohmann::json& );
void processResponseNext( const Response& response, const nlohmann::json& request );
};

View File

@@ -1,5 +1,5 @@
#include "messages.hpp"
#include "protocol.hpp"
#include "messages.hpp"
#include <eepp/core/core.hpp>
#include <eepp/system/fileinfo.hpp>
#include <eepp/system/filesystem.hpp>
@@ -7,40 +7,46 @@
using namespace EE;
using namespace EE::System;
std::optional<int> parseOptionalInt( const json& value ) {
if ( value.is_null() || value.empty() || !value.is_number() ) {
std::optional<int> parseOptionalInt( const json& value, const std::string_view& name ) {
if ( !value.contains( name ) || value[name].is_null() || value[name].empty() ||
!value[name].is_number() ) {
return std::nullopt;
}
return value.get<int>();
return value[name].get<int>();
}
std::optional<bool> parseOptionalBool( const json& value ) {
if ( value.is_null() || value.empty() || !value.is_boolean() ) {
std::optional<bool> parseOptionalBool( const json& value, const std::string_view& name ) {
if ( !value.contains( name ) || value[name].is_null() || value[name].empty() ||
!value[name].is_boolean() ) {
return std::nullopt;
}
return value.get<bool>();
return value[name].get<bool>();
}
std::optional<std::string> parseOptionalString( const json& value ) {
if ( value.is_null() || value.empty() || !value.is_string() ) {
std::optional<std::string> parseOptionalString( const json& value, const std::string_view& name ) {
if ( !value.contains( name ) || value[name].is_null() || value[name].empty() ||
!value[name].is_string() ) {
return std::nullopt;
}
return value.get<std::string>();
return value[name].get<std::string>();
}
template <typename T> std::optional<T> parseOptionalObject( const json& value ) {
if ( value.is_null() || value.empty() || !value.is_object() ) {
template <typename T>
std::optional<T> parseOptionalObject( const json& value, const std::string_view& name ) {
if ( !value.contains( name ) || value[name].is_null() || value[name].empty() ||
!value[name].is_object() ) {
return std::nullopt;
}
return T( value );
return T( value[name] );
}
std::optional<std::unordered_map<std::string, std::string>>
parseOptionalStringMap( const json& value ) {
if ( value.is_null() || value.empty() || !value.is_object() ) {
parseOptionalStringMap( const json& value, const std::string_view& name ) {
if ( !value.contains( name ) || value[name].is_null() || value[name].empty() ||
!value[name].is_object() ) {
return std::nullopt;
}
const auto& dict = value;
const auto& dict = value[name];
std::unordered_map<std::string, std::string> map;
for ( auto it = dict.begin(); it != dict.end(); ++it ) {
map[it.key()] = it.value().get<std::string>();
@@ -56,12 +62,14 @@ template <typename T> std::vector<T> parseObjectList( const json& array ) {
return out;
}
std::optional<std::vector<int>> parseOptionalIntList( const json& value ) {
if ( value.is_null() || value.empty() || !value.is_array() ) {
std::optional<std::vector<int>> parseOptionalIntList( const json& value,
const std::string_view& name ) {
if ( !value.contains( name ) || value[name].is_null() || value[name].empty() ||
!value[name].is_array() ) {
return std::nullopt;
}
std::vector<int> values;
for ( const auto& item : value ) {
for ( const auto& item : value[name] ) {
values.push_back( item.get<int>() );
}
return values;
@@ -80,19 +88,19 @@ namespace ecode::dap {
Message::Message( const json& body ) :
id( body[DAP_ID].get<int>() ),
format( body["format"].get<std::string>() ),
variables( parseOptionalStringMap( body["variables"] ) ),
sendTelemetry( parseOptionalBool( body["sendTelemetry"] ) ),
showUser( parseOptionalBool( body["showUser"] ) ),
url( parseOptionalString( body["url"] ) ),
urlLabel( parseOptionalString( body["urlLabel"] ) ) {}
variables( parseOptionalStringMap( body, "variables" ) ),
sendTelemetry( parseOptionalBool( body, "sendTelemetry" ) ),
showUser( parseOptionalBool( body, "showUser" ) ),
url( parseOptionalString( body, "url" ) ),
urlLabel( parseOptionalString( body, "urlLabel" ) ) {}
Response::Response( const json& msg ) :
request_seq( msg.value( "request_seq", -1 ) ),
success( msg.value( "success", false ) ),
command( msg.value( DAP_COMMAND, "" ) ),
message( msg.value( "message", "" ) ),
body( msg[DAP_BODY] ),
errorBody( success ? std::nullopt : parseOptionalObject<Message>( body["error"] ) ) {}
body( msg.contains( DAP_BODY ) ? msg[DAP_BODY] : nlohmann::json{} ),
errorBody( success ? std::nullopt : parseOptionalObject<Message>( body, "error" ) ) {}
bool Response::isCancelled() const {
return message == "cancelled";
@@ -100,20 +108,20 @@ bool Response::isCancelled() const {
ProcessInfo::ProcessInfo( const json& body ) :
name( body.value( DAP_NAME, "" ) ),
systemProcessId( parseOptionalInt( body[DAP_SYSTEM_PROCESS_ID] ) ),
isLocalProcess( parseOptionalBool( body[DAP_IS_LOCAL_PROCESS] ) ),
startMethod( parseOptionalString( body[DAP_START_METHOD] ) ),
pointerSize( parseOptionalInt( body[DAP_POINTER_SIZE] ) ) {}
systemProcessId( parseOptionalInt( body, DAP_SYSTEM_PROCESS_ID ) ),
isLocalProcess( parseOptionalBool( body, DAP_IS_LOCAL_PROCESS ) ),
startMethod( parseOptionalString( body, DAP_START_METHOD ) ),
pointerSize( parseOptionalInt( body, DAP_POINTER_SIZE ) ) {}
Output::Output( const json& body ) :
category( Category::Unknown ),
output( body.value( DAP_OUTPUT, "" ) ),
group( std::nullopt ),
variablesReference( parseOptionalInt( body[DAP_VARIABLES_REFERENCE] ) ),
source( parseOptionalObject<Source>( DAP_SOURCE ) ),
line( parseOptionalInt( body[DAP_LINE] ) ),
column( parseOptionalInt( body[DAP_COLUMN] ) ),
data( body[DAP_DATA] ) {
variablesReference( parseOptionalInt( body, DAP_VARIABLES_REFERENCE ) ),
source( parseOptionalObject<Source>( body, DAP_SOURCE ) ),
line( parseOptionalInt( body, DAP_LINE ) ),
column( parseOptionalInt( body, DAP_COLUMN ) ),
data( body.contains( DAP_DATA ) ? body[DAP_DATA] : nlohmann::json{} ) {
if ( body.contains( DAP_GROUP ) ) {
const auto value = body[DAP_GROUP].get<std::string>();
if ( DAP_START == value ) {
@@ -159,12 +167,12 @@ std::string Source::getUnifiedId( const std::string& path, std::optional<int> so
}
Source::Source( const json& body ) :
name( body[DAP_NAME].get<std::string>() ),
path( body[DAP_PATH].get<std::string>() ),
sourceReference( parseOptionalInt( body[DAP_SOURCE_REFERENCE] ) ),
presentationHint( parseOptionalString( body[DAP_PRESENTATION_HINT] ) ),
origin( body[DAP_ORIGIN].get<std::string>() ),
adapterData( body[DAP_ADAPTER_DATA] ) {
name( body.value( DAP_NAME, "" ) ),
path( body.value( DAP_PATH, "" ) ),
sourceReference( parseOptionalInt( body, DAP_SOURCE_REFERENCE ) ),
presentationHint( parseOptionalString( body, DAP_PRESENTATION_HINT ) ),
origin( body.value( DAP_ORIGIN, "" ) ),
adapterData( body.contains( DAP_ADAPTER_DATA ) ? body[DAP_ADAPTER_DATA] : nlohmann::json{} ) {
// sources
if ( body.contains( DAP_SOURCES ) ) {
const auto values = body[DAP_SOURCES];
@@ -225,27 +233,27 @@ json Checksum::toJson() const {
}
Capabilities::Capabilities( const json& body ) :
supportsConfigurationDoneRequest( body["supportsConfigurationDoneRequest"].get<bool>() ),
supportsFunctionBreakpoints( body["supportsFunctionBreakpoints"].get<bool>() ),
supportsConditionalBreakpoints( body["supportsConditionalBreakpoints"].get<bool>() ),
supportsHitConditionalBreakpoints( body["supportsHitConditionalBreakpoints"].get<bool>() ),
supportsLogPoints( body["supportsLogPoints"].get<bool>() ),
supportsModulesRequest( body["supportsModulesRequest"].get<bool>() ),
supportsTerminateRequest( body["supportsTerminateRequest"].get<bool>() ),
supportTerminateDebuggee( body["supportTerminateDebuggee"].get<bool>() ),
supportsGotoTargetsRequest( body["supportsGotoTargetsRequest"].get<bool>() ) {}
supportsConfigurationDoneRequest( body.value( "supportsConfigurationDoneRequest", false ) ),
supportsFunctionBreakpoints( body.value( "supportsFunctionBreakpoints", false ) ),
supportsConditionalBreakpoints( body.value( "supportsConditionalBreakpoints", false ) ),
supportsHitConditionalBreakpoints( body.value( "supportsHitConditionalBreakpoints", false ) ),
supportsLogPoints( body.value( "supportsLogPoints", false ) ),
supportsModulesRequest( body.value( "supportsModulesRequest", false ) ),
supportsTerminateRequest( body.value( "supportsTerminateRequest", false ) ),
supportTerminateDebuggee( body.value( "supportTerminateDebuggee", false ) ),
supportsGotoTargetsRequest( body.value( "supportsGotoTargetsRequest", false ) ) {}
ThreadEvent::ThreadEvent( const json& body ) :
reason( body[DAP_REASON].get<std::string>() ), threadId( body[DAP_THREAD_ID].get<int>() ) {}
StoppedEvent::StoppedEvent( const json& body ) :
reason( body[DAP_REASON].get<std::string>() ),
description( parseOptionalString( body["description"] ) ),
description( parseOptionalString( body, "description" ) ),
threadId( body[DAP_THREAD_ID].get<int>() ),
preserveFocusHint( parseOptionalBool( body["preserveFocusHint"] ) ),
text( parseOptionalString( body["text"] ) ),
allThreadsStopped( parseOptionalBool( body["allThreadsStopped"] ) ),
hitBreakpointsIds( parseOptionalIntList( body["hitBreakpointsIds"] ) ) {}
preserveFocusHint( parseOptionalBool( body, "preserveFocusHint" ) ),
text( parseOptionalString( body, "text" ) ),
allThreadsStopped( parseOptionalBool( body, "allThreadsStopped" ) ),
hitBreakpointsIds( parseOptionalIntList( body, "hitBreakpointsIds" ) ) {}
Thread::Thread( const json& body ) :
id( body[DAP_ID].get<int>() ), name( body[DAP_NAME].get<std::string>() ) {}
@@ -259,48 +267,48 @@ std::vector<Thread> Thread::parseList( const json& threads ) {
StackFrame::StackFrame( const json& body ) :
id( body[DAP_ID].get<int>() ),
name( body[DAP_NAME].get<std::string>() ),
source( parseOptionalObject<Source>( body[DAP_SOURCE] ) ),
source( parseOptionalObject<Source>( body, DAP_SOURCE ) ),
line( body[DAP_LINE].get<int>() ),
column( body[DAP_COLUMN].get<int>() ),
endLine( parseOptionalInt( body["endLine"] ) ),
canRestart( parseOptionalBool( ( body["canRestart"] ) ) ),
instructionPointerReference( parseOptionalString( body["instructionPointerReference"] ) ),
moduleId_int( parseOptionalInt( body[DAP_MODULE_ID] ) ),
moduleId_str( parseOptionalString( body[DAP_MODULE_ID] ) ),
presentationHint( parseOptionalString( body[DAP_PRESENTATION_HINT] ) ) {}
endLine( parseOptionalInt( body, "endLine" ) ),
canRestart( parseOptionalBool( body, "canRestart" ) ),
instructionPointerReference( parseOptionalString( body, "instructionPointerReference" ) ),
moduleId_int( parseOptionalInt( body, DAP_MODULE_ID ) ),
moduleId_str( parseOptionalString( body, DAP_MODULE_ID ) ),
presentationHint( parseOptionalString( body, DAP_PRESENTATION_HINT ) ) {}
StackTraceInfo::StackTraceInfo( const json& body ) :
stackFrames( parseObjectList<StackFrame>( body["stackFrames"] ) ),
totalFrames( parseOptionalInt( body["totalFrames"] ) ) {}
totalFrames( parseOptionalInt( body, "totalFrames" ) ) {}
Module::Module( const json& body ) :
id_int( parseOptionalInt( body[DAP_ID] ) ),
id_str( parseOptionalString( body[DAP_ID] ) ),
id_int( parseOptionalInt( body, DAP_ID ) ),
id_str( parseOptionalString( body, DAP_ID ) ),
name( body[DAP_NAME].get<std::string>() ),
path( parseOptionalString( body[DAP_PATH] ) ),
isOptimized( parseOptionalBool( body["isOptimized"] ) ),
isUserCode( parseOptionalBool( body["isUserCode"] ) ),
version( parseOptionalString( body["version"] ) ),
symbolStatus( parseOptionalString( body["symbolStatus"] ) ),
symbolFilePath( parseOptionalString( body["symbolFilePath"] ) ),
dateTimeStamp( parseOptionalString( body["dateTimeStamp"] ) ),
addressRange( parseOptionalString( body["addressRange"] ) ) {}
path( parseOptionalString( body, DAP_PATH ) ),
isOptimized( parseOptionalBool( body, "isOptimized" ) ),
isUserCode( parseOptionalBool( body, "isUserCode" ) ),
version( parseOptionalString( body, "version" ) ),
symbolStatus( parseOptionalString( body, "symbolStatus" ) ),
symbolFilePath( parseOptionalString( body, "symbolFilePath" ) ),
dateTimeStamp( parseOptionalString( body, "dateTimeStamp" ) ),
addressRange( parseOptionalString( body, "addressRange" ) ) {}
ModuleEvent::ModuleEvent( const json& body ) :
reason( body[DAP_REASON].get<std::string>() ), module( Module( body["module"] ) ) {}
Scope::Scope( const json& body ) :
name( body[DAP_NAME].get<std::string>() ),
presentationHint( parseOptionalString( body[DAP_PRESENTATION_HINT] ) ),
presentationHint( parseOptionalString( body, DAP_PRESENTATION_HINT ) ),
variablesReference( body[DAP_VARIABLES_REFERENCE].get<int>() ),
namedVariables( parseOptionalInt( body["namedVariables"] ) ),
indexedVariables( parseOptionalInt( body["indexedVariables"] ) ),
expensive( parseOptionalBool( body["expensive"] ) ),
source( parseOptionalObject<Source>( body["source"] ) ),
line( parseOptionalInt( body["line"] ) ),
column( parseOptionalInt( body["column"] ) ),
endLine( parseOptionalInt( body["endLine"] ) ),
endColumn( parseOptionalInt( body["endColumn"] ) ) {}
namedVariables( parseOptionalInt( body, "namedVariables" ) ),
indexedVariables( parseOptionalInt( body, "indexedVariables" ) ),
expensive( parseOptionalBool( body, "expensive" ) ),
source( parseOptionalObject<Source>( body, "source" ) ),
line( parseOptionalInt( body, "line" ) ),
column( parseOptionalInt( body, "column" ) ),
endLine( parseOptionalInt( body, "endLine" ) ),
endColumn( parseOptionalInt( body, "endColumn" ) ) {}
Scope::Scope( int variablesReference, std::string name ) :
name( name ), variablesReference( variablesReference ) {}
@@ -312,12 +320,12 @@ std::vector<Scope> Scope::parseList( const json& scopes ) {
Variable::Variable( const json& body ) :
name( body[DAP_NAME].get<std::string>() ),
value( body["value"].get<std::string>() ),
type( parseOptionalString( body[DAP_TYPE].get<std::string>() ) ),
evaluateName( parseOptionalString( body["evaluateName"].get<std::string>() ) ),
type( parseOptionalString( body, DAP_TYPE ) ),
evaluateName( parseOptionalString( body, "evaluateName" ) ),
variablesReference( body[DAP_VARIABLES_REFERENCE].get<int>() ),
namedVariables( parseOptionalInt( body["namedVariables"] ) ),
indexedVariables( parseOptionalInt( body["indexedVariables"] ) ),
memoryReference( parseOptionalString( body["memoryReference"] ) ) {}
namedVariables( parseOptionalInt( body, "namedVariables" ) ),
indexedVariables( parseOptionalInt( body, "indexedVariables" ) ),
memoryReference( parseOptionalString( body, "memoryReference" ) ) {}
Variable::Variable( const std::string& name, const std::string& value, const int reference ) :
name( name ), value( value ), variablesReference( reference ) {}
@@ -328,18 +336,18 @@ std::vector<Variable> Variable::parseList( const json& variables ) {
ModulesInfo::ModulesInfo( const json& body ) :
modules( parseObjectList<Module>( body[DAP_MODULES] ) ),
totalModules( parseOptionalInt( body["totalModules"] ) ) {}
totalModules( parseOptionalInt( body, "totalModules" ) ) {}
ContinuedEvent::ContinuedEvent( const json& body ) :
threadId( body[DAP_THREAD_ID].get<int>() ),
allThreadsContinued( parseOptionalBool( body[DAP_ALL_THREADS_CONTINUED] ) ) {}
allThreadsContinued( parseOptionalBool( body, DAP_ALL_THREADS_CONTINUED ) ) {}
ContinuedEvent::ContinuedEvent( int threadId, bool allThreadsContinued ) :
threadId( threadId ), allThreadsContinued( allThreadsContinued ) {}
SourceContent::SourceContent( const json& body ) :
content( body["content"].get<std::string>() ),
mimeType( parseOptionalString( body["mimeType"] ) ) {}
mimeType( parseOptionalString( body, "mimeType" ) ) {}
SourceContent::SourceContent( const std::string& path ) {
const FileInfo file( path );
@@ -351,10 +359,10 @@ SourceContent::SourceContent( const std::string& path ) {
SourceBreakpoint::SourceBreakpoint( const json& body ) :
line( body[DAP_LINE].get<int>() ),
column( parseOptionalInt( body[DAP_COLUMN] ) ),
condition( parseOptionalString( body[DAP_CONDITION] ) ),
hitCondition( parseOptionalString( body[DAP_HIT_CONDITION] ) ),
logMessage( parseOptionalString( body["logMessage"] ) ) {}
column( parseOptionalInt( body, DAP_COLUMN ) ),
condition( parseOptionalString( body, DAP_CONDITION ) ),
hitCondition( parseOptionalString( body, DAP_HIT_CONDITION ) ),
logMessage( parseOptionalString( body, "logMessage" ) ) {}
SourceBreakpoint::SourceBreakpoint( const int line ) : line( line ) {}
@@ -377,16 +385,16 @@ json SourceBreakpoint::toJson() const {
}
Breakpoint::Breakpoint( const json& body ) :
id( parseOptionalInt( body[DAP_ID] ) ),
verified( body["verified"].get<bool>() ),
message( parseOptionalString( body["message"] ) ),
source( parseOptionalObject<Source>( body[DAP_SOURCE] ) ),
line( parseOptionalInt( body[DAP_LINE] ) ),
column( parseOptionalInt( body[DAP_COLUMN] ) ),
endLine( parseOptionalInt( body[DAP_END_LINE] ) ),
endColumn( parseOptionalInt( body[DAP_END_COLUMN] ) ),
instructionReference( parseOptionalString( body["instructionReference"] ) ),
offset( parseOptionalInt( body["offset"] ) ) {}
id( parseOptionalInt( body, DAP_ID ) ),
verified( body.value( "verified", false ) ),
message( parseOptionalString( body, "message" ) ),
source( parseOptionalObject<Source>( body, DAP_SOURCE ) ),
line( parseOptionalInt( body, DAP_LINE ) ),
column( parseOptionalInt( body, DAP_COLUMN ) ),
endLine( parseOptionalInt( body, DAP_END_LINE ) ),
endColumn( parseOptionalInt( body, DAP_END_COLUMN ) ),
instructionReference( parseOptionalString( body, "instructionReference" ) ),
offset( parseOptionalInt( body, "offset" ) ) {}
Breakpoint::Breakpoint( const int line ) : line( line ) {}
@@ -396,20 +404,20 @@ BreakpointEvent::BreakpointEvent( const json& body ) :
EvaluateInfo::EvaluateInfo( const json& body ) :
result( body[DAP_RESULT].get<std::string>() ),
type( parseOptionalString( body[DAP_TYPE] ) ),
type( parseOptionalString( body, DAP_TYPE ) ),
variablesReference( body[DAP_VARIABLES_REFERENCE].get<int>() ),
namedVariables( parseOptionalInt( body["namedVariables"] ) ),
indexedVariables( parseOptionalInt( body["indexedVariables"] ) ),
memoryReference( parseOptionalString( body["memoryReference"] ) ) {}
namedVariables( parseOptionalInt( body, "namedVariables" ) ),
indexedVariables( parseOptionalInt( body, "indexedVariables" ) ),
memoryReference( parseOptionalString( body, "memoryReference" ) ) {}
GotoTarget::GotoTarget( const json& body ) :
id( body[DAP_ID].get<int>() ),
label( body["label"].get<std::string>() ),
line( body[DAP_LINE].get<int>() ),
column( parseOptionalInt( body[DAP_COLUMN] ) ),
endLine( parseOptionalInt( body[DAP_END_LINE] ) ),
endColumn( parseOptionalInt( body[DAP_END_COLUMN] ) ),
instructionPointerReference( parseOptionalString( body["instructionPointerReference"] ) ) {}
column( parseOptionalInt( body, DAP_COLUMN ) ),
endLine( parseOptionalInt( body, DAP_END_LINE ) ),
endColumn( parseOptionalInt( body, DAP_END_COLUMN ) ),
instructionPointerReference( parseOptionalString( body, "instructionPointerReference" ) ) {}
std::vector<GotoTarget> GotoTarget::parseList( const json& variables ) {
return parseObjectList<GotoTarget>( variables );

View File

@@ -19,7 +19,7 @@ void DebuggerClient::debuggeeRunning() {
void DebuggerClient::debuggeeTerminated() {
for ( auto listener : mListeners )
listener->debuggeeRunning();
listener->debuggeeTerminated();
}
void DebuggerClient::failed() {

View File

@@ -15,6 +15,7 @@ class DebuggerClient {
virtual void stateChanged( State ) = 0;
virtual void initialized() = 0;
virtual void launched() = 0;
virtual void configured() = 0;
virtual void failed() = 0;
virtual void debuggeeRunning() = 0;
virtual void debuggeeTerminated() = 0;
@@ -69,14 +70,14 @@ class DebuggerClient {
virtual bool threads() = 0;
virtual bool stackTrace( int threadId, int startFrame, int levels ) = 0;
virtual bool stackTrace( int threadId, int startFrame = 0, int levels = 0 ) = 0;
virtual bool scopes( int frameId ) = 0;
virtual bool modules( int start, int count ) = 0;
virtual bool variables( int variablesReference, Variable::Type filter, int start,
int count ) = 0;
virtual bool variables( int variablesReference, Variable::Type filter = Variable::Type::Both,
int start = 0, int count = 0 ) = 0;
virtual bool evaluate( const std::string& expression, const std::string& context,
std::optional<int> frameId ) = 0;
@@ -101,10 +102,14 @@ class DebuggerClient {
virtual bool watch( const std::string& expression, std::optional<int> frameId ) = 0;
virtual bool configurationDone() = 0;
void addListener( Listener* listener );
void removeListener( Listener* listener );
virtual ~DebuggerClient() {}
protected:
void setState( const State& state );

View File

@@ -1,24 +1,42 @@
#include "debuggerclientlistener.hpp"
#include "debuggerplugin.hpp"
namespace ecode {
DebuggerClientListener::DebuggerClientListener( DebuggerClient* client, DebuggerPlugin* plugin ) :
mClient( client ), mPlugin( plugin ) {
eeASSERT( mClient && mPlugin );
}
void DebuggerClientListener::stateChanged( DebuggerClient::State ) {}
void DebuggerClientListener::initialized() {}
void DebuggerClientListener::initialized() {
// mClient->setBreakpoints( "/home/programming/eepp/src/tools/ecode/ecode.cpp",
// { SourceBreakpoint( 4116 ) } );
}
void DebuggerClientListener::launched() {}
void DebuggerClientListener::configured() {}
void DebuggerClientListener::failed() {}
void DebuggerClientListener::debuggeeRunning() {}
void DebuggerClientListener::debuggeeTerminated() {}
void DebuggerClientListener::capabilitiesReceived( const Capabilities& capabilities ) {}
void DebuggerClientListener::capabilitiesReceived( const Capabilities& /*capabilities*/ ) {}
void DebuggerClientListener::debuggeeExited( int exitCode ) {}
void DebuggerClientListener::debuggeeExited( int /*exitCode*/ ) {
mPlugin->exitDebugger();
}
void DebuggerClientListener::debuggeeStopped( const StoppedEvent& ) {}
void DebuggerClientListener::debuggeeStopped( const StoppedEvent& event ) {
Log::warning( "DebuggerClientListener::debuggeeStopped: reason %s", event.reason );
// if ( event.threadId ) {
// mClient->stackTrace( *event.threadId );
// }
}
void DebuggerClientListener::debuggeeContinued( const ContinuedEvent& ) {}
@@ -26,39 +44,50 @@ void DebuggerClientListener::outputProduced( const Output& ) {}
void DebuggerClientListener::debuggingProcess( const ProcessInfo& ) {}
void DebuggerClientListener::errorResponse( const std::string& summary,
const std::optional<Message>& message ) {}
void DebuggerClientListener::errorResponse( const std::string& /*summary*/,
const std::optional<Message>& /*message*/ ) {}
void DebuggerClientListener::threadChanged( const ThreadEvent& ) {}
void DebuggerClientListener::moduleChanged( const ModuleEvent& ) {}
void DebuggerClientListener::threads( const std::vector<Thread>& ) {}
void DebuggerClientListener::threads( const std::vector<Thread>& /*threads*/ ) {}
void DebuggerClientListener::stackTrace( const int threadId, const StackTraceInfo& ) {}
void DebuggerClientListener::stackTrace( const int /*threadId*/, const StackTraceInfo& stack ) {
// if ( !stack.stackFrames.empty() ) {
// mClient->scopes( stack.stackFrames[0].id );
// }
}
void DebuggerClientListener::scopes( const int frameId, const std::vector<Scope>& ) {}
void DebuggerClientListener::scopes( const int /*frameId**/, const std::vector<Scope>& scopes ) {
// if ( !scopes.empty() ) {
// mClient->variables( scopes[0].variablesReference );
// }
}
void DebuggerClientListener::variables( const int variablesReference,
const std::vector<Variable>& ) {}
void DebuggerClientListener::variables( const int /*variablesReference*/,
const std::vector<Variable>& vars ) {
// if ( !vars.empty() ) {
// mClient->resume( 1 );
// }
}
void DebuggerClientListener::modules( const ModulesInfo& ) {}
void DebuggerClientListener::serverDisconnected() {}
void DebuggerClientListener::sourceContent( const std::string& path, int reference,
const SourceContent& content ) {}
void DebuggerClientListener::sourceContent( const std::string& /*path*/, int /*reference*/,
const SourceContent& /*content*/ ) {}
void DebuggerClientListener::sourceBreakpoints(
const std::string& path, int reference,
const std::optional<std::vector<Breakpoint>>& breakpoints ) {}
const std::string& /*path*/, int /*reference*/,
const std::optional<std::vector<Breakpoint>>& /*breakpoints*/ ) {}
void DebuggerClientListener::breakpointChanged( const BreakpointEvent& ) {}
void DebuggerClientListener::expressionEvaluated( const std::string& expression,
void DebuggerClientListener::expressionEvaluated( const std::string& /*expression*/,
const std::optional<EvaluateInfo>& ) {}
void DebuggerClientListener::gotoTargets( const Source& source, const int line,
const std::vector<GotoTarget>& targets ) {}
void DebuggerClientListener::gotoTargets( const Source& /*source*/, const int /*line*/,
const std::vector<GotoTarget>& /*targets*/ ) {}
} // namespace ecode

View File

@@ -3,11 +3,16 @@
namespace ecode {
class DebuggerPlugin;
class DebuggerClientListener : public DebuggerClient::Listener {
public:
DebuggerClientListener( DebuggerClient* client, DebuggerPlugin* plugin );
void stateChanged( DebuggerClient::State );
void initialized();
void launched();
void configured();
void failed();
void debuggeeRunning();
void debuggeeTerminated();
@@ -27,13 +32,18 @@ class DebuggerClientListener : public DebuggerClient::Listener {
void variables( const int variablesReference, const std::vector<Variable>& );
void modules( const ModulesInfo& );
void serverDisconnected();
void sourceContent( const std::string& path, int reference, const SourceContent& content = SourceContent() );
void sourceBreakpoints( const std::string& path, int reference, const std::optional<std::vector<Breakpoint>>& breakpoints );
void sourceContent( const std::string& path, int reference,
const SourceContent& content = SourceContent() );
void sourceBreakpoints( const std::string& path, int reference,
const std::optional<std::vector<Breakpoint>>& breakpoints );
void breakpointChanged( const BreakpointEvent& );
void expressionEvaluated( const std::string& expression, const std::optional<EvaluateInfo>& );
void gotoTargets( const Source& source, const int line, const std::vector<GotoTarget>& targets );
void gotoTargets( const Source& source, const int line,
const std::vector<GotoTarget>& targets );
protected:
DebuggerClient* mClient{ nullptr };
DebuggerPlugin* mPlugin{ nullptr };
};
} // namespace ecode

View File

@@ -1,4 +1,5 @@
#include "debuggerplugin.hpp"
#include "../../projectbuild.hpp"
#include "busprocess.hpp"
#include "dap/debuggerclientdap.hpp"
#include <eepp/system/filesystem.hpp>
@@ -48,6 +49,9 @@ DebuggerPlugin::~DebuggerPlugin() {
if ( mSidePanel && mTab )
mSidePanel->removeTab( mTab );
mDebugger.reset();
mListener.reset();
}
void DebuggerPlugin::load( PluginManager* pluginManager ) {
@@ -256,10 +260,10 @@ void DebuggerPlugin::updateSidePanelTab() {
updateDebuggerConfigurationList();
UIPushButton* runButton = mTabContents->find<UIPushButton>( "run_button" );
mRunButton = mTabContents->find<UIPushButton>( "run_button" );
if ( !runButton->hasEventsOfType( Event::MouseClick ) ) {
runButton->onClick( [this]( auto ) {
if ( !mRunButton->hasEventsOfType( Event::MouseClick ) ) {
mRunButton->onClick( [this]( auto ) {
runConfig( mUIDebuggerList->getListBox()->getItemSelectedText().toUtf8(),
mUIDebuggerConfList->getListBox()->getItemSelectedText().toUtf8() );
} );
@@ -293,6 +297,38 @@ void DebuggerPlugin::updateDebuggerConfigurationList() {
mUIDebuggerConfList->getListBox()->setSelected( 0L );
}
void DebuggerPlugin::replaceKeysInJson( nlohmann::json& json ) {
static constexpr auto KEY_FILE = "${file}";
static constexpr auto KEY_ARGS = "${args}";
static constexpr auto KEY_CWD = "${cwd}";
static constexpr auto KEY_ENV = "${env}";
static constexpr auto KEY_STOPONENTRY = "${stopOnEntry}";
auto runConfig = getManager()->getProjectBuildManager()->getCurrentRunConfig();
for ( auto& j : json ) {
if ( j.is_object() ) {
replaceKeysInJson( j );
} else if ( j.is_string() ) {
std::string val( j.get<std::string>() );
if ( runConfig && !runConfig->cmd.empty() && val == KEY_FILE ) {
j = runConfig->cmd;
} else if ( runConfig && !runConfig->args.empty() && val == KEY_ARGS ) {
auto argsArr = nlohmann::json::array();
auto args = Process::parseArgs( runConfig->args );
for ( const auto& arg : args )
argsArr.push_back( arg );
j = argsArr;
} else if ( runConfig && val == KEY_CWD ) {
j = runConfig->workingDir;
} else if ( runConfig && val == KEY_ENV ) {
j = nlohmann::json{};
} else if ( val == KEY_STOPONENTRY ) {
j = false;
}
}
}
}
void DebuggerPlugin::runConfig( const std::string& debugger, const std::string& configuration ) {
auto debuggerIt = std::find_if( mDaps.begin(), mDaps.end(), [&debugger]( const DapTool& dap ) {
return dap.name == debugger;
@@ -312,17 +348,41 @@ void DebuggerPlugin::runConfig( const std::string& debugger, const std::string&
ProtocolSettings protocolSettings;
protocolSettings.launchCommand = configIt->command;
protocolSettings.launchRequest = configIt->args;
auto args = configIt->args;
replaceKeysInJson( args );
protocolSettings.launchRequest = args;
Command cmd;
cmd.command = debuggerIt->run.command;
cmd.arguments = debuggerIt->run.args;
auto bus = std::make_unique<BusProcess>( cmd );
mDebugger = std::make_unique<DebuggerClientDap>( protocolSettings, std::move( bus ) );
mListener = std::make_unique<DebuggerClientListener>();
mListener = std::make_unique<DebuggerClientListener>( mDebugger.get(), this );
mDebugger->addListener( mListener.get() );
mDebugger->start();
mRunButton->setEnabled( false );
mThreadPool->run(
[this] { mDebugger->start(); },
[this]( const Uint64& ) {
if ( !mDebugger || mDebugger->state() != DebuggerClient::State::Running ) {
mRunButton->runOnMainThread( [this] { mRunButton->setEnabled( false ); } );
}
} );
}
void DebuggerPlugin::exitDebugger() {
if ( mDebugger && mListener )
mDebugger->removeListener( mListener.get() );
mThreadPool->run( [this] {
mDebugger.reset();
mListener.reset();
} );
if ( getUISceneNode() ) {
getUISceneNode()->runOnMainThread( [this] { mRunButton->setEnabled( true ); } );
}
}
void DebuggerPlugin::hideSidePanel() {

View File

@@ -38,7 +38,7 @@ class DebuggerPlugin : public PluginBase {
static Plugin* NewSync( PluginManager* pluginManager );
~DebuggerPlugin();
virtual ~DebuggerPlugin();
std::string getId() override { return Definition().id; }
@@ -47,6 +47,8 @@ class DebuggerPlugin : public PluginBase {
std::string getDescription() override { return Definition().description; }
protected:
friend class DebuggerClientListener;
std::vector<DapTool> mDaps;
std::unique_ptr<DebuggerClient> mDebugger;
std::unique_ptr<DebuggerClientListener> mListener;
@@ -57,6 +59,7 @@ class DebuggerPlugin : public PluginBase {
UIWidget* mTabContents{ nullptr };
UIDropDownList* mUIDebuggerList{ nullptr };
UIDropDownList* mUIDebuggerConfList{ nullptr };
UIPushButton* mRunButton{ nullptr };
DebuggerPlugin( PluginManager* pluginManager, bool sync );
@@ -77,6 +80,10 @@ class DebuggerPlugin : public PluginBase {
void loadDAPConfig( const std::string& path, bool updateConfigFile );
void runConfig( const std::string& debugger, const std::string& configuration );
void exitDebugger();
void replaceKeysInJson( nlohmann::json& json );
};
} // namespace ecode

View File

@@ -767,7 +767,7 @@ void ProjectBuildManager::runCurrentConfig( StatusAppOutputController* saoc, boo
}
}
bool ProjectBuildManager::hasBuildConfig() {
bool ProjectBuildManager::hasBuildConfig() const {
return !getBuilds().empty() && !mConfig.buildName.empty();
}
@@ -779,6 +779,20 @@ bool ProjectBuildManager::hasRunConfig() {
return false;
}
std::optional<ProjectBuildStep> ProjectBuildManager::getCurrentRunConfig() {
if ( hasBuildConfig() ) {
auto build = getBuild( mConfig.buildName );
if ( build != nullptr && build->hasRun() ) {
for ( const auto& crun : build->mRun ) {
if ( crun->name == mConfig.runName || mConfig.runName.empty() ) {
return build->replaceVars( *crun.get() );
}
}
}
}
return {};
}
void ProjectBuildManager::runConfig( StatusAppOutputController* saoc ) {
if ( !isRunningApp() && !getBuilds().empty() ) {
BoolScopedOp op( mRunning, true );

View File

@@ -333,10 +333,12 @@ class ProjectBuildManager {
bool hasRunConfig();
bool hasBuildConfig();
bool hasBuildConfig() const;
void selectTab();
std::optional<ProjectBuildStep> getCurrentRunConfig();
protected:
std::string mProjectRoot;
std::string mProjectFile;