Skip to content

Commit

Permalink
More DAP WIP.
Browse files Browse the repository at this point in the history
  • Loading branch information
SpartanJ committed Dec 17, 2024
1 parent 75490bf commit e4ee5f1
Show file tree
Hide file tree
Showing 6 changed files with 400 additions and 13 deletions.
25 changes: 25 additions & 0 deletions src/tools/ecode/plugins/debugger/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#include <nlohmann/json.hpp>

using namespace std::literals;

using json = nlohmann::json;

using namespace EE;
Expand All @@ -33,6 +35,10 @@ struct BusSettings {
bool hasConnection() const;
};

static const auto REQUEST = "request"sv;
static const auto REDIRECT_STDERR = "redirectStderr"sv;
static const auto REDIRECT_STDOUT = "redirectStdout"sv;

struct ProtocolSettings {
bool linesStartAt1;
bool columnsStartAt1;
Expand All @@ -42,6 +48,25 @@ struct ProtocolSettings {
bool supportsSourceRequest;
json launchRequest;
std::string locale;

ProtocolSettings() :
linesStartAt1( true ),
columnsStartAt1( true ),
pathFormatURI( false ),
redirectStderr( false ),
redirectStdout( false ),
supportsSourceRequest( true ),
locale( "en-US" ) {}

ProtocolSettings( const nlohmann::json& configuration ) :
linesStartAt1( true ),
columnsStartAt1( true ),
pathFormatURI( false ),
redirectStderr( configuration.value( REDIRECT_STDERR, false ) ),
redirectStdout( configuration.value( REDIRECT_STDOUT, false ) ),
supportsSourceRequest( configuration.value( "supportsSourceRequest", true ) ),
launchRequest( configuration[REQUEST] ),
locale( "en-US" ) {}
};

} // namespace ecode
216 changes: 207 additions & 9 deletions src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,33 @@ namespace ecode::dap {

constexpr int MAX_HEADER_SIZE = 1 << 16;

template <typename T>
inline DebuggerClientDap::ResponseHandler
makeResponseHandler( void ( T::*member )( const Response& response, const nlohmann::json& request ),
T* object ) {
return [object, member]( const Response& response, const nlohmann::json& request ) {
return ( object->*member )( response, request );
};
}

DebuggerClientDap::DebuggerClientDap( std::unique_ptr<Bus>&& bus ) : mBus( std::move( bus ) ) {}

void DebuggerClientDap::makeRequest( const std::string& command, const nlohmann::json& arguments,
std::function<void()> onFinish ) {
nlohmann::json json_cmd = {
void DebuggerClientDap::makeRequest( const std::string_view& command,
const nlohmann::json& arguments, ResponseHandler onFinish ) {
nlohmann::json jsonCmd = {
{ "seq", mIdx.load() },
{ "type", "request" },
{ "command", command },
{ "arguments", arguments.empty() ? nlohmann::json::object() : arguments } };

std::string cmd = json_cmd.dump();
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 ) );
mBus->write( msg.data(), msg.size() );

if ( onFinish )
mCommandQueue[mIdx] = onFinish;
mRequests[mIdx] = { std::string{ command }, arguments, onFinish };

mIdx.fetch_add( 1, std::memory_order_relaxed );
}
Expand All @@ -49,9 +57,86 @@ bool DebuggerClientDap::start() {
if ( started )
mBus->startAsyncRead( [this]( const char* bytes, size_t n ) { asyncRead( bytes, n ); } );
mStarted = started;

return started;
}

void DebuggerClientDap::processResponseInitialize( const Response& response,
const nlohmann::json& ) {
if ( mState != State::Initializing ) {
Log::warning(
"DebuggerClientDap::processResponseInitialize: unexpected initialize response" );
setState( State::None );
return;
}

if ( !response.success && response.isCancelled() ) {
Log::warning( "DebuggerClientDap::processResponseInitialize: InitializeResponse error: %s",
response.message );
if ( response.errorBody ) {
Log::warning( "DebuggerClientDap::processResponseInitialize: error %ld %s",
response.errorBody->id, response.errorBody->format );
}
setState( State::None );
return;
}

// get server capabilities
mAdapterCapabilities = Capabilities( response.body );
if ( mObserver )
mObserver->capabilitiesReceived( mAdapterCapabilities );

requestLaunchCommand();
}

void DebuggerClientDap::processResponseLaunch( const Response& response, const nlohmann::json& ) {
if ( response.success ) {
mLaunched = true;
if ( mObserver )
mObserver->launched();
checkRunning();
} else {
setState( State::Failed );
}
}

void DebuggerClientDap::requestLaunchCommand() {

if ( mState != State::Initializing ) {
Log::warning(
"DebuggerClientDap::requestLaunchCommand: trying to launch in an unexpected state" );
return;
}

if ( mLaunchCommand.empty() )
return;

makeRequest( mLaunchCommand, mProtocol.launchRequest,
makeResponseHandler( &DebuggerClientDap::processResponseLaunch, this ) );
}

void DebuggerClientDap::requestInitialize() {
const nlohmann::json capabilities{
{ DAP_CLIENT_ID, "ecode-dap" },
{ DAP_CLIENT_NAME, "ecode dap" },
{ "locale", mProtocol.locale },
{ DAP_ADAPTER_ID, "qdap" },
{ DAP_LINES_START_AT1, mProtocol.linesStartAt1 },
{ DAP_COLUMNS_START_AT2, mProtocol.columnsStartAt1 },
{ DAP_PATH, ( mProtocol.pathFormatURI ? DAP_URI : DAP_PATH ) },
{ DAP_SUPPORTS_VARIABLE_TYPE, true },
{ DAP_SUPPORTS_VARIABLE_PAGING, false },
{ DAP_SUPPORTS_RUN_IN_TERMINAL_REQUEST, false },
{ DAP_SUPPORTS_MEMORY_REFERENCES, false },
{ DAP_SUPPORTS_PROGRESS_REPORTING, false },
{ DAP_SUPPORTS_INVALIDATED_EVENT, false },
{ DAP_SUPPORTS_MEMORY_EVENT, false } };

setState( State::Initializing );
makeRequest( DAP_INITIALIZE, capabilities,
makeResponseHandler( &DebuggerClientDap::processResponseInitialize, this ) );
}

void DebuggerClientDap::asyncRead( const char* bytes, size_t n ) {
std::string_view read( bytes, n );
mBuffer += read;
Expand Down Expand Up @@ -96,9 +181,122 @@ void DebuggerClientDap::processProtocolMessage( const nlohmann::json& msg ) {
}
}

void DebuggerClientDap::processResponse( const nlohmann::json& msg ) {}
void DebuggerClientDap::processResponse( const nlohmann::json& msg ) {
const Response response( msg );

// check sequence
if ( ( response.request_seq < 0 ) || 0 == mRequests.count( response.request_seq ) ) {
Log::error( "DebuggerClientDap::processResponse: unexpected requested seq in response" );
return;
}

const auto request = mRequests.extract( response.request_seq ).mapped();

// check response
if ( response.command != request.command ) {
Log::error(
"DebuggerClientDap::processResponse: unexpected command in response: %s (expected: %s)",
response.command, request.command );
}

if ( response.isCancelled() )
Log::debug( "DebuggerClientDap::processResponse: request cancelled: %s", response.command );

if ( !response.success )
return errorResponse( response.message, response.errorBody );

if ( request.handler ) {
request.handler( response, request.arguments );
}
}

void DebuggerClientDap::errorResponse( const std::string& summary,
const std::optional<Message>& message ) {
if ( mObserver )
mObserver->errorResponse( summary, message );
}

void DebuggerClientDap::processEvent( const nlohmann::json& msg ) {
const std::string event = msg.value( DAP_EVENT, "" );
const auto body = msg[DAP_BODY];

if ( "initialized"sv == event ) {
processEventInitialized();
} else if ( "terminated"sv == event ) {
processEventTerminated();
} else if ( "exited"sv == event ) {
processEventExited( body );
} else if ( DAP_OUTPUT == event ) {
processEventOutput( body );
} else if ( "process"sv == event ) {
processEventProcess( body );
} else if ( "thread"sv == event ) {
processEventThread( body );
} else if ( "stopped"sv == event ) {
processEventStopped( body );
} else if ( "module"sv == event ) {
processEventModule( body );
} else if ( "continued"sv == event ) {
processEventContinued( body );
} else if ( DAP_BREAKPOINT == event ) {
processEventBreakpoint( body );
} else {
Log::info( "DebuggerClientDap::processEvent: unsupported event: %s", event );
}
}

void DebuggerClientDap::processEventInitialized() {
if ( ( mState != State::Initializing ) ) {
Log::error( "DebuggerClientDap::processEventInitialized: unexpected initialized event" );
return;
}
setState( State::Initialized );
}

void DebuggerClientDap::processEventTerminated() {
setState( State::Terminated );
}

void DebuggerClientDap::processEventExited( const nlohmann::json& body ) {
const int exitCode = body.value( "exitCode", -1 );
if ( mObserver )
mObserver->debuggeeExited( exitCode );
}

void DebuggerClientDap::processEventOutput( const nlohmann::json& body ) {
if ( mObserver )
mObserver->outputProduced( Output( body ) );
}

void DebuggerClientDap::processEventProcess( const nlohmann::json& body ) {
if ( mObserver )
mObserver->debuggingProcess( ProcessInfo( body ) );
}

void DebuggerClientDap::processEventThread( const nlohmann::json& body ) {
if ( mObserver )
mObserver->threadChanged( ThreadEvent( body ) );
}

void DebuggerClientDap::processEvent( const nlohmann::json& msg ) {}
void DebuggerClientDap::processEventStopped( const nlohmann::json& body ) {
if ( mObserver )
mObserver->debuggeeStopped( StoppedEvent( body ) );
}

void DebuggerClientDap::processEventModule( const nlohmann::json& body ) {
if ( mObserver )
mObserver->moduleChanged( ModuleEvent( body ) );
}

void DebuggerClientDap::processEventContinued( const nlohmann::json& body ) {
if ( mObserver )
mObserver->debuggeeContinued( ContinuedEvent( body ) );
}

void DebuggerClientDap::processEventBreakpoint( const nlohmann::json& body ) {
if ( mObserver )
mObserver->breakpointChanged( BreakpointEvent( body ) );
}

std::optional<DebuggerClientDap::HeaderInfo> DebuggerClientDap::readHeader() {
Uint64 length = std::string::npos;
Expand Down Expand Up @@ -159,7 +357,7 @@ std::optional<DebuggerClientDap::HeaderInfo> DebuggerClientDap::readHeader() {
if ( length < 0 )
return std::nullopt;

return HeaderInfo{ .payloadStart = end, .payloadLength = length };
return HeaderInfo{ end, length };
}

bool DebuggerClientDap::attach() {
Expand Down
48 changes: 45 additions & 3 deletions src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "../bus.hpp"
#include "../debuggerclient.hpp"
#include "protocol.hpp"
#include <atomic>
#include <eepp/config.hpp>

Expand All @@ -11,6 +12,8 @@ namespace ecode::dap {

class DebuggerClientDap : public DebuggerClient {
public:
typedef std::function<void( const Response&, const nlohmann::json& )> ResponseHandler;

DebuggerClientDap( std::unique_ptr<Bus>&& bus );

bool hasBreakpoint( const std::string& path, size_t line );
Expand Down Expand Up @@ -54,11 +57,19 @@ class DebuggerClientDap : public DebuggerClient {
Uint64 mThreadId{ 1 };
bool mDebug{ true };
bool mStarted{ false };
UnorderedMap<Uint64, std::function<void()>> mCommandQueue;
struct Request {
std::string command;
nlohmann::json arguments;
ResponseHandler handler;
};
std::unordered_map<Uint64, Request> mRequests;
std::string mBuffer;
ProtocolSettings mProtocol;
Capabilities mAdapterCapabilities;
std::string mLaunchCommand;

void makeRequest( const std::string& command, const nlohmann::json& arguments,
std::function<void()> onFinish = nullptr );
void makeRequest( const std::string_view& command, const nlohmann::json& arguments,
ResponseHandler onFinish = nullptr );

void asyncRead( const char* bytes, size_t n );

Expand All @@ -72,7 +83,38 @@ class DebuggerClientDap : public DebuggerClient {
Uint64 payloadStart;
Uint64 payloadLength;
};

std::optional<HeaderInfo> readHeader();

void errorResponse( const std::string& summary, const std::optional<Message>& message );

void processEventInitialized();

void processEventTerminated();

void processEventExited( const nlohmann::json& body );

void processEventOutput( const nlohmann::json& body );

void processEventProcess( const nlohmann::json& body );

void processEventThread( const nlohmann::json& body );

void processEventStopped( const nlohmann::json& body );

void processEventModule( const nlohmann::json& body );

void processEventContinued( const nlohmann::json& body );

void processEventBreakpoint( const nlohmann::json& body );

void requestInitialize();

void requestLaunchCommand();

void processResponseInitialize( const Response& response, const nlohmann::json& );

void processResponseLaunch( const Response& response, const nlohmann::json& );
};

} // namespace ecode::dap
2 changes: 2 additions & 0 deletions src/tools/ecode/plugins/debugger/dap/messages.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ static const auto DAP_ARGUMENTS = "arguments"sv;
static const auto DAP_BODY = "body"sv;

// capabilities
static const auto DAP_CLIENT_ID = "clientID"sv;
static const auto DAP_CLIENT_NAME = "clientName"sv;
static const auto DAP_ADAPTER_ID = "adapterID"sv;
static const auto DAP_LINES_START_AT1 = "linesStartAt1"sv;
static const auto DAP_COLUMNS_START_AT2 = "columnsStartAt1"sv;
Expand Down
Loading

0 comments on commit e4ee5f1

Please sign in to comment.