Skip to content

Commit

Permalink
mi: Minimal implementation of stopped event
Browse files Browse the repository at this point in the history
  • Loading branch information
dd86k committed Nov 3, 2024
1 parent c9f2bd4 commit c9edab3
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 14 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ Use cases:

Aliceserver is implemented using an Object-Oriented Programming model.

The debugger server provides types and structures that the adapters and
debuggers must interpret.

## Transports

Each transport classes inherit `transport.base.ITransport`.
Expand Down
61 changes: 56 additions & 5 deletions source/adapter/mi.d
Original file line number Diff line number Diff line change
Expand Up @@ -480,9 +480,9 @@ final class MIAdapter : Adapter
}

override
void event(AdapterEvent msg)
void event(AdapterEvent event)
{
switch (msg.type) with (AdapterEventType) {
switch (event.type) with (AdapterEventType) {
// - ~"Starting program: example.exe \n"
// - =library-loaded,id="C:\\WINDOWS\\SYSTEM32\\ntdll.dll",
// target-name="C:\\WINDOWS\\SYSTEM32\\ntdll.dll",
Expand All @@ -495,6 +495,7 @@ final class MIAdapter : Adapter
break;*/
// - *running,thread-id="all"
case continued:
send("*running\n");
break;
// - *stopped,reason="breakpoint-hit",disp="keep",bkptno="1",thread-id="0",
// frame={addr="0x08048564",func="main",
Expand All @@ -505,18 +506,31 @@ final class MIAdapter : Adapter
// signal-meaning="Segmentation fault",frame={addr="0x0000000000000000",
// func="??",args=[],arch="i386:x86-64"},thread-id="1",stopped-threads="all"
case stopped:
MIValue frame;
frame["addr"] = format("%#x", event.stopped.frame.arch);
frame["func"] = event.stopped.frame.func is null ? "??" : event.stopped.frame.func;
frame["args"] = event.stopped.frame.args;
frame["arch"] = toMIArch(event.stopped.frame.arch);
MIValue root;
root["reason"] = toMIStoppedReason(event.stopped.reason);
//root["signal-name"] = "SIGSEGV";
//root["signal-meaning"] = "Segmentation fault";
root["frame"] = frame;
root["thread-id"] = event.stopped.threadId;
root["stopped-threads"] = "all";
send(toMessage("*stopped", root));
break;
// - *stopped,reason="exited-normally"
// - *stopped,reason="exited",exit-code="01"
// - *stopped,reason="exited-signalled",signal-name="SIGINT",signal-meaning="Interrupt"
case exited:
if (msg.exited.code)
send(format("*stopped,reason=\"exited\",exit-code=\"%d\"\n", msg.exited.code));
if (event.exited.code)
send(format("*stopped,reason=\"exited\",exit-code=\"%d\"\n", event.exited.code));
else
send("*stopped,reason=\"exited-normally\"\n");
break;
default:
logWarn("Unimplemented event type: %s", msg.type);
logWarn("Unimplemented event type: %s", event.type);
}
}

Expand Down Expand Up @@ -560,6 +574,43 @@ unittest
assert(miVersion(AdapterType.dap) == 0);
}

private
string toMessage(string prefix, MIValue miobj)
{
return prefix~","~miobj.toString()~"\n";
}

private
string toMIArch(MachineArchitecture arch)
{
final switch (arch) {
case MachineArchitecture.i386: return "i386";
case MachineArchitecture.x86_64: return "i386:x86_64";
case MachineArchitecture.AArch32: return "??";
case MachineArchitecture.AArch64: return "??";
}
}

private
string toMIStoppedReason(AdapterEventStoppedReason reason)
{
final switch (reason) with (AdapterEventStoppedReason) {
case step:
return "step";
case breakpoint:
return "breakpoint-hit";
case exception:
return "signal-received";
case pause:
case entry:
case goto_:
case functionBreakpoint:
case dataBreakpoint:
case instructionBreakpoint:
return "unknown";
}
}

private
struct MIRequest
{
Expand Down
18 changes: 18 additions & 0 deletions source/adapter/types.d
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
/// License: BSD-3-Clause-Clear
module adapter.types;

enum MachineArchitecture
{
i386,
x86_64,
AArch32,
AArch64,
}

//
// Request types
//
Expand Down Expand Up @@ -182,6 +190,14 @@ enum EventMessageType

}

struct AdapterFrame
{
ulong address;
string func;
string[] args;
MachineArchitecture arch;
}

struct AdapterEvent
{
AdapterEventType type;
Expand All @@ -208,6 +224,8 @@ struct AdapterEvent
/// Additional information. E.g. If reason is `exception`,
/// text contains the exception name. This string is shown in the UI.
string text;
/// MI: Describes the frame where the stop event happened.
AdapterFrame frame;
}
AdapterEventStopped stopped;

Expand Down
34 changes: 34 additions & 0 deletions source/debugger/alicedbg.d
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import debugger.base;
import adapter.types;
import adbg.debugger;
import adbg.process.exception;
import adbg.process.frame;
import adbg.process.thread;
import adbg.machines;
import adbg.error;

// TODO: Could be possible to make a "AlicedbgRemote" class for remote sessions
Expand Down Expand Up @@ -144,16 +147,47 @@ AdapterEventStoppedReason adbgExceptionReason(adbg_exception_t *ex) {
}
}

// Translate AdbgMachine to MachineArchitcture
MachineArchitecture adbgMachine(AdbgMachine mach)
{
switch (mach) {
case AdbgMachine.i386: return MachineArchitecture.i386;
case AdbgMachine.amd64: return MachineArchitecture.x86_64;
default:
// TODO: machine architecture as default
}
return cast(MachineArchitecture)-1;
}

// Handle exceptions
extern (C)
void adbgEventException(adbg_process_t *proc, void *udata, adbg_exception_t *exception)
{
AdapterEvent *event = cast(AdapterEvent*)udata;

event.type = AdapterEventType.stopped;
event.stopped.reason = adbgExceptionReason(exception);
event.stopped.text = adbgExceptionName(exception);
event.stopped.description = "Exception";
event.stopped.threadId = cast(int)adbg_exception_tid(exception);

event.stopped.frame = AdapterFrame.init;
event.stopped.frame.arch = adbgMachine(adbg_process_machine(proc));

adbg_thread_t *thread = adbg_thread_new(adbg_exception_tid(exception));
if (thread == null)
return;
scope(exit) adbg_thread_close(thread);

void *frames = adbg_frame_list(proc, thread);
if (frames == null)
return;
scope(exit) adbg_frame_list_close(frames);

adbg_stackframe_t *frame0 = adbg_frame_list_at(frames, 0);
if (frame0 == null)
return;
event.stopped.frame.address = frame0.address;
}

// Handle continuations
Expand Down
21 changes: 21 additions & 0 deletions source/server.d
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,27 @@ import ddlogger;
// This could be a request type, or server simply launching new ones with
// matching sequence IDs (as DAP goes).

// TODO: Debugger server request queue
//
// Currently, there are no ways for manage multiple requests, for example,
// if the adapter bursts more than one request for fine-grained control.
// This would be nice to have before multi-session handling
//
// Ideas:
// - Make adapters return a dynamic array of requests?
// - Use message passing? One concurrent thread per adapter?

// TODO: Adapter-driven debugger requests
//
// Right now, the server server handles all requests sequencially with
// the intent to reply to the client, but there are times when the adapter
// simply wants additional information that wouldn't be relevant to another
// adapter. (e.g., MI protocol wants frame info on a stop event, DAP doesn't)
//
// Ideas:
// - Attach debugger instance to a Session class with its adapter?
// - Abstract class can setup the event thread, etc.

// NOTE: Structure
//
// The server can ultimately handle one adapter protocol, and if the
Expand Down
45 changes: 36 additions & 9 deletions source/util/mi.d
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ struct MIValue
return store.boolean = v;
}

long integer()
{
if (type != MIType.integer)
throw new Exception(text("Not an integer, it is ", type));
return store.integer;
}
long integer(long v)
{
type = MIType.integer;
return store.integer = v;
}

// Get value by index
ref typeof(this) opIndex(return scope string key)
{
Expand Down Expand Up @@ -212,15 +224,30 @@ private:
unittest
{
// Type testing
MIValue mistring;
mistring["key"] = "value";
assert(mistring["key"].str == "value");
assert(mistring.toString() == `key="value"`);

MIValue mibool;
mibool["boolean"] = true;
assert(mibool["boolean"].boolean == true);
assert(mibool.toString() == `boolean="true"`);
{
MIValue mistring;
mistring["key"] = "value";
assert(mistring["key"].str == "value");
assert(mistring.toString() == `key="value"`);
}
{
MIValue mibool;
mibool["boolean"] = true;
assert(mibool["boolean"].boolean == true);
assert(mibool.toString() == `boolean="true"`);
}
{
MIValue miint;
miint["int"] = 2;
assert(miint["int"].integer == 2);
assert(miint.toString() == `int="2"`);
}
{
MIValue miint;
miint["int"] = 2;
assert(miint["int"].integer == 2);
assert(miint.toString() == `int="2"`);
}

/*
^done,threads=[
Expand Down

0 comments on commit c9edab3

Please sign in to comment.