gdb debugger support - threads, stack, variables

This commit is contained in:
Vadim Lopatin 2015-12-17 14:54:06 +03:00
parent 17195a81f5
commit 253cb1f7b4
4 changed files with 204 additions and 13 deletions

View File

@ -28,9 +28,11 @@ class LocationBase {
int line;
}
class DebugLocation : LocationBase {
class DebugFrame : LocationBase {
ulong address;
string func;
int level;
DebugVariableList locals;
void fillMissingFields(LocationBase v) {
if (file.empty)
file = v.file;
@ -63,6 +65,46 @@ class Breakpoint : LocationBase {
}
}
class DebugThread {
ulong id;
string name;
DebugFrame frame;
DebuggingState state;
DebugStack stack;
}
class DebugThreadList {
DebugThread[] threads;
ulong currentThreadId;
@property DebugThread currentThread() {
return findThread(currentThreadId);
}
DebugThread findThread(ulong id) {
foreach(t; threads)
if (t.id == id)
return t;
return null;
}
@property int length() { return threads.length; }
DebugThread opIndex(int index) { return threads[index]; }
}
class DebugStack {
DebugFrame[] frames;
@property int length() { return frames.length; }
DebugFrame opIndex(int index) { return frames[index]; }
}
class DebugVariable {
string name;
string type;
string value;
}
class DebugVariableList {
DebugVariable[] variables;
}
static __gshared _nextBreakpointId = 1;
interface Debugger : ProgramExecution {
@ -98,7 +140,7 @@ interface DebuggerCallback : ProgramExecutionStatusListener {
void onProgramLoaded(bool successful, bool debugInfoLoaded);
/// state changed: running / paused / stopped
void onDebugState(DebuggingState state, StateChangeReason reason, DebugLocation location, Breakpoint bp);
void onDebugState(DebuggingState state, StateChangeReason reason, DebugFrame location, Breakpoint bp);
void onResponse(ResponseCode code, string msg);
}
@ -178,7 +220,7 @@ class DebuggerProxy : Debugger, DebuggerCallback {
}
/// state changed: running / paused / stopped
void onDebugState(DebuggingState state, StateChangeReason reason, DebugLocation location, Breakpoint bp) {
void onDebugState(DebuggingState state, StateChangeReason reason, DebugFrame location, Breakpoint bp) {
_callbackDelegate( delegate() { _callback.onDebugState(state, reason, location, bp); } );
}

View File

@ -451,20 +451,22 @@ class GDBInterface : ConsoleDebuggerInterface {
_callback.onDebugState(DebuggingState.running, StateChangeReason.unknown, null, null);
} else if (msgId == AsyncClass.stopped) {
StateChangeReason reasonId = StateChangeReason.unknown;
DebugLocation location = parseFrame(params["frame"]);
DebugFrame location = parseFrame(params["frame"]);
string threadId = params.getString("thread-id");
string stoppedThreads = params.getString("all");
Breakpoint bp = null;
if (reason.equal("end-stepping-range")) {
updateState();
_callback.onDebugState(DebuggingState.paused, StateChangeReason.endSteppingRange, location, bp);
} else if (reason.equal("breakpoint-hit")) {
if (GDBBreakpoint gdbbp = findBreakpointByNumber(params.getString("bkptno"))) {
bp = gdbbp.bp;
if (!location && bp) {
location = new DebugLocation();
location = new DebugFrame();
location.fillMissingFields(bp);
}
}
updateState();
_callback.onDebugState(DebuggingState.paused, StateChangeReason.breakpointHit, location, bp);
} else {
_callback.onDebugState(DebuggingState.stopped, StateChangeReason.exited, null, null);
@ -472,6 +474,16 @@ class GDBInterface : ConsoleDebuggerInterface {
}
}
int _threadInfoRequest;
int _stackListFramesRequest;
int _stackListLocalsRequest;
DebugThreadList _currentState;
void updateState() {
_currentState = null;
_threadInfoRequest = sendCommand("-thread-info");
_stackListFramesRequest = sendCommand("-stack-list-frames");
}
// +asyncclass,result
void handleStatusAsyncMessage(uint token, string s) {
string msgType = parseIdentAndSkipComma(s);
@ -503,6 +515,60 @@ class GDBInterface : ConsoleDebuggerInterface {
// result of breakpoint creation operation
handleBreakpointRequestResult(gdbbp, msgId, params);
return;
} else if (token == _threadInfoRequest) {
handleThreadInfoRequestResult(msgId, params);
} else if (token == _stackListFramesRequest) {
handleStackListFramesRequest(msgId, params);
} else if (token == _stackListLocalsRequest) {
handleLocalVariableListRequestResult(msgId, params);
}
}
void handleStackListFramesRequest(ResultClass msgId, MIValue params) {
if (msgId == ResultClass.done) {
DebugStack stack = parseStack(params);
if (stack) {
// TODO
Log.d("Stack frames list is parsed: " ~ to!string(stack));
if (_currentState) {
if (DebugThread currentThread = _currentState.currentThread) {
currentThread.stack = stack;
Log.d("Setting stack frames for current thread");
}
}
}
} else {
}
}
void handleThreadInfoRequestResult(ResultClass msgId, MIValue params) {
if (msgId == ResultClass.done) {
_currentState = parseThreadList(params);
if (_currentState) {
// TODO
Log.d("Thread list is parsed");
_stackListLocalsRequest = sendCommand("-stack-list-locals --thread " ~ to!string(_currentState.currentThreadId) ~ " --frame 0 --all-values");
}
} else {
}
}
void handleLocalVariableListRequestResult(ResultClass msgId, MIValue params) {
if (msgId == ResultClass.done) {
DebugVariableList variables = parseVariableList(params);
if (variables) {
// TODO
Log.d("Variable list is parsed: " ~ to!string(variables));
if (_currentState) {
if (DebugThread currentThread = _currentState.currentThread) {
if (currentThread.stack.length > 0) {
currentThread.stack[0].locals = variables;
Log.d("Setting variables for current thread top frame");
}
}
}
}
} else {
}
}

View File

@ -63,20 +63,96 @@ MIValue parseMI(string s) {
line = "8"
},
*/
DebugLocation parseFrame(MIValue frame) {
DebugFrame parseFrame(MIValue frame) {
import std.path;
if (!frame)
return null;
DebugLocation location = new DebugLocation();
DebugFrame location = new DebugFrame();
location.file = baseName(toNativeDelimiters(frame.getString("file")));
location.projectFilePath = toNativeDelimiters(frame.getString("file"));
location.fullFilePath = toNativeDelimiters(frame.getString("fullname"));
location.line = frame.getInt("line");
location.func = frame.getString("func");
location.address = frame.getUlong("addr");
location.level = frame.getInt("level");
return location;
}
DebugVariableList parseVariableList(MIValue params) {
if (!params)
return null;
DebugVariableList res = new DebugVariableList();
MIValue list = params["locals"];
if (list && list.isList) {
for(int i = 0; i < list.length; i++) {
if (DebugVariable t = parseVariable(list[i]))
res.variables ~= t;
}
}
return res;
}
DebugVariable parseVariable(MIValue params) {
if (!params)
return null;
DebugVariable res = new DebugVariable();
res.name = params.getString("name");
res.value = params.getString("value");
res.type = params.getString("type");
return res;
}
DebugThreadList parseThreadList(MIValue params) {
if (!params)
return null;
DebugThreadList res = new DebugThreadList();
res.currentThreadId = params.getUlong("current-thread-id");
MIValue threads = params["threads"];
if (threads && threads.isList) {
for(int i = 0; i < threads.length; i++) {
if (DebugThread t = parseThread(threads[i]))
res.threads ~= t;
}
}
return res;
}
DebugThread parseThread(MIValue params) {
if (!params)
return null;
DebugThread res = new DebugThread();
res.id = params.getUlong("id");
res.name = params.getString("target-id");
string stateName = params.getString("state");
if (stateName == "stopped")
res.state = DebuggingState.paused;
else if (stateName == "running")
res.state = DebuggingState.running;
else
res.state = DebuggingState.stopped;
res.frame = parseFrame(params["frame"]);
return res;
}
DebugStack parseStack(MIValue params) {
if (!params)
return null;
MIValue stack = params["stack"];
if (!stack)
return null;
DebugStack res = new DebugStack();
for (int i = 0; i < stack.length; i++) {
MIValue item = stack[i];
if (item && item.isKeyValue && item.key.equal("frame")) {
DebugFrame location = parseFrame(item.value);
if (location) {
res.frames ~= location;
}
}
}
return res;
}
string toNativeDelimiters(string s) {
version(Windows) {
char[] buf;
@ -338,9 +414,16 @@ class MIValue {
this.type = type;
}
@property string str() { return null; }
@property int length() { return 1; }
@property int length() { return 0; }
MIValue opIndex(int index) { return null; }
MIValue opIndex(string key) { return null; }
@property bool isIdent() { return type == MIValueType.list; }
@property bool isString() { return type == MIValueType.str; }
@property bool isKeyValue() { return type == MIValueType.keyValue; }
@property bool isMap() { return type == MIValueType.map; }
@property bool isList() { return type == MIValueType.list; }
@property string key() { return str; }
@property MIValue value() { return this; }
string getString(string name) {
MIValue v = opIndex(name);
@ -395,9 +478,9 @@ class MIKeyValue : MIValue {
_key = key;
_value = value;
}
@property string key() { return _key; }
override @property string key() { return _key; }
override @property MIValue value() { return _value; }
override @property string str() { return _key; }
@property MIValue value() { return _value; }
override void dump(ref char[] buf, int level) {
//dumpLevel(buf, level);
buf ~= _key;

View File

@ -17,7 +17,7 @@ class DebuggerUIHandler : DebuggerCallback {
IDEFrame _ide;
Debugger _debugger;
DebuggingState _state = DebuggingState.loaded;
DebugLocation _location;
DebugFrame _location;
this(IDEFrame ide, Debugger debugger) {
_ide = ide;
@ -53,7 +53,7 @@ class DebuggerUIHandler : DebuggerCallback {
_debugger.execStart();
}
void updateLocation(DebugLocation location) {
void updateLocation(DebugFrame location) {
_location = location;
ProjectSourceFile sourceFile = location ? currentWorkspace.findSourceFile(location.projectFilePath, location.fullFilePath) : null;
if (location) {
@ -73,7 +73,7 @@ class DebuggerUIHandler : DebuggerCallback {
}
/// state changed: running / paused / stopped
void onDebugState(DebuggingState state, StateChangeReason reason, DebugLocation location, Breakpoint bp) {
void onDebugState(DebuggingState state, StateChangeReason reason, DebugFrame location, Breakpoint bp) {
Log.d("onDebugState: ", state, " reason=", reason);
_state = state;
if (state == DebuggingState.stopped) {