╭───────────────────────────────────────────╮
│Aliceserver │
│ ┌───────────────────────────────────────┐ │
│ │ Server │ │
│ ├───────────┬───┬──────────┬─┬──────────┤ │
│ │ │ │ Adapter │ │ Adapter │ │
│ │ Transport │ ├──────────┤ ├──────────┤ │
│ │ │ │ Debugger │ │ Proxy │ │
╰─┴─────▲─────┴───┴────▲─────┴─┴────▲─────┴─╯
│ │ │
╔═════▼═════╗ ╔════▼═════╗ ╔════▼═════╗
║ Debugger ║ ║ Remote ║ ║ Remote ║
║ Client ║ ║ Process ║ ║ Server ║
╚═══════════╝ ╚══════════╝ ╚══════════╝
Aliceserver is implemented using an Object-Oriented Programming model.
The debugger server provides types and structures that the adapters and debuggers must interpret.
Transports handle the transport of data from and to clients.
The medium can include streams, sockets, anything really.
Each transport class inherits transport.ITransport.
Available transports:
StdioTransport: Implemented using standard streams.SocketTransport: Implemented using Socket (for TCP and UNIX sockets).NamedPipeTransport: Implemented using Windows NamedPipes.
File: source/transports/stdio.d
StdioTransport uses the standard Phobos stream handles.
Nothing special. Uses poll.2 and PeekNamedPipe to perform peeking.
File: source/transports/socket.d
SocketTransport uses Phobos Socket (std.socket) for TCP and UNIX socket transports.
Socket.select is used to know if the socket has data.
Path resolution is done by the server. When --pipe= argument starts with /,
it is assumed a full path. Otherwise, XDG_RUNTIME_DIR is checked and used as
prefix if it exists. If not, /tmp/ is used as prefix.
File: source/transports/pipe.d
NamedPipeTransport uses the Win32 API.
Duplex Named Pipes are created with PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS
(byte-oriented, blocking, and refusing remote connections).
Uses PeekNamedPipe for peeking data.
Path resolution is done by the server. When --pipe= argument starts with \\,
it is assumed a full path. Otherwise, \\.\pipe\ is prefixed.
Adapters have the responsibility of handling the behavior of the given transport and the debugger instances.
Using the transport instance, adapters need to parse requests and send formatted replies and events back to the client via the transport instance.
Using the debugger instance, adapters need to interpret commands and handle debugger events.
After setting up instances, the server calls IAdapter.loop(ITransport, IDebugger).
Each adapter inherits adapter.IAdapter.
Available adapters:
DAPAdapter: Implements Debug Adapter Protocol as an adapter.MIAdapter: Implements GDB's Machine Interface as an adapter.
Used to interface a debugger that manipulates processes.
Each debugger class inherits debugger.IDebugger.
Right now, only AlicedbgDebugger is available as a debugger.
File: source/adapters/dap.d
Debugger Adapter Protocol (DAP) is a protocol using JSON-RPC that was introduced in vscode-debugadapter-node and was readapted as a standalone protocol for debugging various processes and runtimes in Visual Studio Code.
This chapter reuses terminology from DAP, such as Integer meaning, strictly
speaking, a 32-bit integer number (int), and Number meaning a 64-bit
double-precision floating-point number (IEEE 754 double).
DAP has two connection models: Single-session and multi-session.
- Single-session: Using standard I/O (stdio), a single adapter instance is started.
- Multi-session: Using TCP/IP, every new connection initiates a new debug session.
Messages are encoded as HTTP messages using the UTF-8 encoding and JSON payloads.
Currently, there is only one header field, Content-Length, that determines the
length of the message (payload). This field is read as an Integer.
A typical request may look like this:
Content-Length: 82\r\n
\r\n
{"seq":1,"type":"request","command":"initialize","arguments":{"adapterId":"test"}}
And a typical response may look like this:
Content-Length: 81\r\n
\r\n
{"command":"initialize","request_seq":1,"seq":1,"success":true,"type":"response"}
Both client and server maintain their own sequence number, starting at 1.
NOTE: lldb-vscode starts their seq number at 0, while not as per specification, it poses no difference to its usage.
Implementation-specific details:
launchrequest:arguments:path: (Required) [String] File path.
attachrequest:arguments:pid: (Required) [Integer] Process ID.
Command support:
| Command | Supported? | Comments |
|---|---|---|
attach |
✔️ | __restart argument not supported. |
breakpointLocations |
❌ | |
completions |
❌ | |
configurationDone |
✔️ | |
continue |
✔️ | |
dataBreakpointInfo |
❌ | |
disassemble |
❌ | |
disconnect |
✔️ | |
evaluate |
❌ | |
exceptionInfo |
❌ | |
goto |
❌ | |
gotoTargets |
❌ | |
initialize |
✔️ | Locale is not supported. |
launch |
✔️ | noDebug and __restart are not supported. |
loadedSources |
❌ | |
modules |
❌ | |
next |
❌ | |
pause |
❌ | |
readMemory |
❌ | |
restart |
❌ | |
restartFrame |
❌ | |
reverseContinue |
❌ | |
scopes |
❌ | |
setBreakpoints |
❌ | |
setDataBreakpoints |
❌ | |
setExceptionBreakpoints |
❌ | |
setExpression |
❌ | |
setFunctionBreakpoints |
❌ | |
setInstructionBreakpoints |
❌ | |
setVariable |
❌ | |
source |
❌ | |
stackTrace |
❌ | |
stepBack |
❌ | |
stepIn |
❌ | |
stepInTargets |
❌ | |
stepOut |
❌ | |
terminate |
✔️ | |
terminateThreads |
❌ | |
threads |
❌ | |
variables |
❌ | |
writeMemory |
❌ |
| Event | Supported? | Comments |
|---|---|---|
breakpoint |
❌ | |
capabilities |
❌ | |
continued |
❌ | |
exited |
✔️ | |
initialized |
❌ | |
invalidated |
❌ | |
loadedSource |
❌ | |
memory |
❌ | |
module |
❌ | |
output |
||
process |
❌ | |
progressEnd |
❌ | |
progressStart |
❌ | |
progressUpdate |
❌ | |
stopped |
||
terminated |
❌ | |
thread |
❌ |
File: source/adapters/mi.d
Machine Interface is a line-oriented protocol introduced in GDB 5.1.
In a typical setting, MI uses the standard streams to communicate with the child process.
Once the server starts running, it may already emit console streams, until
(gdb)\n is printed, indicating that the server is ready to receive commands.
Commands are almost the same as you would use on GDB:
attach 12345\n
Replies to commands start with a ^ character:
^done\n
Or on error (note: \\n and \\" denote c-string formatting):
^error,msg="Example text.\\n\\nValue: \\"Test\\""\n
Events, console streams, logs, start with a significant unique character.
For example, command input (e.g., test\n) will be replied as &"test\\n"\n
using c-string formatting.
| Reply/Event | Character | Description |
|---|---|---|
| Result | ^ |
Used to reply to a command, if successful or erroneous. |
| Exec | * |
Async execution state changed. |
| Notify | = |
Async notification related to the debugger. |
| Status | + |
Async status change. |
| Console Stream | ~ |
Console messages intended to be printed. |
| Target Stream | @ |
Program output when truly asynchronous, for remote targets. |
| Log Stream | & |
Internal debugger messages. |
Some commands may start with -.
NOTE: Command focus is on GDB, lldb-mi commands may work.
| Request | Commands | Supported? | Comments |
|---|---|---|---|
| Attach | target-attach, attach |
✔️ | |
| Launch | -exec-run, run, target exec, -file-exec-and-symbols |
✔️ | |
| Set arguments | -exec-arguments |
✔️ | |
| Continue | -exec-continue, continue |
✔️ | |
| Pause | -exec-interrupt, pause |
✔️ | |
| Terminate | -exec-abort |
✔️ | |
| Detach | -target-detach, -gdb-detach, detach |
✔️ | |
| Disconnect | -target-disconnect |
✔️ | |
| Set working directory | -environment-cd |
Stub, not fully implemented | |
| Thread info | -thread-info |
✔️ | |
| Show | show |
Only show version supported |
|
| Command info | -info-gdb-mi-command |
✔️ | |
| List features | -list-features |
✔️ | |
| Settings | -gdb-set, -inferior-tty-set |
Stubs required by clients | |
| Quit | -gdb-exit, quit, q |
✔️ |
| Request | Details | Supported? | Comments |
|---|---|---|---|
| Continued | ✔️ | ||
| Exited | Reasons: exited, exited-normally |
✔️ | |
| Output | ❌ | ||
| Stopped | Reasons: breakpoint-hit, signal-received, end-stepping-range |
✔️ |