From 253cb1f7b484ac3efda5bfc07e198cf9c723d68d Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Thu, 17 Dec 2015 14:54:06 +0300 Subject: [PATCH] gdb debugger support - threads, stack, variables --- src/ddebug/common/debugger.d | 48 ++++++++++++++++-- src/ddebug/gdb/gdbinterface.d | 70 +++++++++++++++++++++++++- src/ddebug/gdb/gdbmiparser.d | 93 +++++++++++++++++++++++++++++++++-- src/dlangide/ui/debuggerui.d | 6 +-- 4 files changed, 204 insertions(+), 13 deletions(-) diff --git a/src/ddebug/common/debugger.d b/src/ddebug/common/debugger.d index 4e5810f..472660e 100644 --- a/src/ddebug/common/debugger.d +++ b/src/ddebug/common/debugger.d @@ -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); } ); } diff --git a/src/ddebug/gdb/gdbinterface.d b/src/ddebug/gdb/gdbinterface.d index db86ba9..cddaeba 100644 --- a/src/ddebug/gdb/gdbinterface.d +++ b/src/ddebug/gdb/gdbinterface.d @@ -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 { } } diff --git a/src/ddebug/gdb/gdbmiparser.d b/src/ddebug/gdb/gdbmiparser.d index 148c9b4..fa0a834 100644 --- a/src/ddebug/gdb/gdbmiparser.d +++ b/src/ddebug/gdb/gdbmiparser.d @@ -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; diff --git a/src/dlangide/ui/debuggerui.d b/src/dlangide/ui/debuggerui.d index 9720ead..fce7bf7 100644 --- a/src/dlangide/ui/debuggerui.d +++ b/src/dlangide/ui/debuggerui.d @@ -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) {