diff --git a/dub.json b/dub.json index 7ddd292..07a3a52 100644 --- a/dub.json +++ b/dub.json @@ -14,7 +14,7 @@ "copyFiles-windows": ["lib/win32/dcd-server.exe", "lib/win32/dcd-client.exe"], "dependencies": { - "dlangui": "~>0.7.24", + "dlangui": "~>0.7.25", "libdparse": "==0.2.0" }, diff --git a/src/ddebug/common/debugger.d b/src/ddebug/common/debugger.d index 0db24da..2de3bc3 100644 --- a/src/ddebug/common/debugger.d +++ b/src/ddebug/common/debugger.d @@ -177,6 +177,14 @@ class DebugVariableList { foreach(t; v.variables) variables ~= new DebugVariable(t); } + + @property int length() { return variables ? cast(int)variables.length : 0; } + + DebugVariable opIndex(int index) { + if (!variables || index < 0 || index > variables.length) + return null; + return variables[index]; + } } static __gshared _nextBreakpointId = 1; @@ -204,6 +212,9 @@ interface Debugger : ProgramExecution { /// update list of breakpoints void setBreakpoints(Breakpoint[] bp); + + /// request stack trace and local vars for thread and frame + void requestDebugContextInfo(ulong threadId, int frame); } interface DebuggerCallback : ProgramExecutionStatusListener { @@ -219,7 +230,7 @@ interface DebuggerCallback : ProgramExecutionStatusListener { void onResponse(ResponseCode code, string msg); /// send debug context (threads, stack frames, local vars...) - void onDebugContextInfo(DebugThreadList info); + void onDebugContextInfo(DebugThreadList info, ulong threadId, int frame); } enum ResponseCode : int { @@ -302,8 +313,8 @@ class DebuggerProxy : Debugger, DebuggerCallback { } /// send debug context (threads, stack frames, local vars...) - void onDebugContextInfo(DebugThreadList info) { - _callbackDelegate( delegate() { _callback.onDebugContextInfo(info); } ); + void onDebugContextInfo(DebugThreadList info, ulong threadId, int frame) { + _callbackDelegate( delegate() { _callback.onDebugContextInfo(info, threadId, frame); } ); } void onResponse(ResponseCode code, string msg) { @@ -359,6 +370,10 @@ class DebuggerProxy : Debugger, DebuggerCallback { void execRestart() { _debugger.postRequest(delegate() { _debugger.execRestart(); }); } + /// request stack trace and local vars for thread and frame + void requestDebugContextInfo(ulong threadId, int frame) { + _debugger.postRequest(delegate() { _debugger.requestDebugContextInfo(threadId, frame); }); + } /// update list of breakpoints void setBreakpoints(Breakpoint[] breakpoints) { Breakpoint[] cloned; diff --git a/src/ddebug/gdb/gdbinterface.d b/src/ddebug/gdb/gdbinterface.d index bd2693a..30cdeef 100644 --- a/src/ddebug/gdb/gdbinterface.d +++ b/src/ddebug/gdb/gdbinterface.d @@ -481,7 +481,7 @@ class GDBInterface : ConsoleDebuggerInterface, TextCommandTarget { DebugThreadList _currentState; void updateState() { _currentState = null; - submitRequest(new ThreadInfoRequest(), new StackListFramesRequest()); + submitRequest(new ThreadInfoRequest()); } // +asyncclass,result @@ -526,6 +526,12 @@ class GDBInterface : ConsoleDebuggerInterface, TextCommandTarget { _requests.submit(requests[0]); } + /// request stack trace and local vars for thread and frame + void requestDebugContextInfo(ulong threadId, int frame) { + Log.d("requestDebugContextInfo threadId=", threadId, " frame=", frame); + submitRequest(new StackListFramesRequest(threadId, frame)); + } + class ThreadInfoRequest : GDBRequest { this() { command = "-thread-info"; } override void onResult() { @@ -533,30 +539,43 @@ class GDBInterface : ConsoleDebuggerInterface, TextCommandTarget { if (_currentState) { // TODO Log.d("Thread list is parsed"); + submitRequest(new StackListFramesRequest(_currentState.currentThreadId, 0)); } } } class StackListFramesRequest : GDBRequest { - this() { command = "-stack-list-frames"; } + private ulong _threadId; + private int _frameId; + this(ulong threadId, int frameId) { + _threadId = threadId; + _frameId = frameId; + if (!_threadId) + _threadId = _currentState ? _currentState.currentThreadId : 0; + command = "-stack-list-frames --thread " ~ to!string(_threadId); + } override void onResult() { DebugStack stack = parseStack(params); if (stack) { // TODO Log.d("Stack frames list is parsed: " ~ to!string(stack)); if (_currentState) { - if (DebugThread currentThread = _currentState.currentThread) { + if (DebugThread currentThread = _currentState.findThread(_threadId)) { currentThread.stack = stack; Log.d("Setting stack frames for current thread"); } - submitRequest(new LocalVariableListRequest(_currentState.currentThreadId, 0)); + submitRequest(new LocalVariableListRequest(_threadId, _frameId)); } } } } class LocalVariableListRequest : GDBRequest { + ulong _threadId; + int _frameId; this(ulong threadId, int frameId) { + _threadId = threadId; + _frameId = frameId; command = "-stack-list-locals --thread " ~ to!string(threadId) ~ " --frame " ~ to!string(frameId) ~ " --simple-values"; } override void onResult() { @@ -569,7 +588,7 @@ class GDBInterface : ConsoleDebuggerInterface, TextCommandTarget { if (currentThread.length > 0) { currentThread[0].locals = variables; Log.d("Setting variables for current thread top frame"); - _callback.onDebugContextInfo(_currentState.clone()); + _callback.onDebugContextInfo(_currentState.clone(), _threadId, _frameId); } } } diff --git a/src/dlangide/ui/debuggerui.d b/src/dlangide/ui/debuggerui.d index 2e5738f..55d865b 100644 --- a/src/dlangide/ui/debuggerui.d +++ b/src/dlangide/ui/debuggerui.d @@ -13,7 +13,7 @@ import dlangide.ui.watchpanel; import ddebug.common.execution; import ddebug.common.debugger; -class DebuggerUIHandler : DebuggerCallback { +class DebuggerUIHandler : DebuggerCallback, StackFrameSelectedHandler { private IDEFrame _ide; private Debugger _debugger; @@ -21,6 +21,7 @@ class DebuggerUIHandler : DebuggerCallback { private DebugFrame _location; private WatchPanel _watchPanel; private StackPanel _stackPanel; + private DebugThreadList _debugInfo; this(IDEFrame ide, Debugger debugger) { _ide = ide; @@ -38,10 +39,22 @@ class DebuggerUIHandler : DebuggerCallback { } /// send debug context (threads, stack frames, local vars...) - void onDebugContextInfo(DebugThreadList info) { + void onDebugContextInfo(DebugThreadList info, ulong threadId, int frameId) { Log.d("Debugger context received"); - _stackPanel.updateDebugInfo(info, info.currentThreadId, 0); - _watchPanel.updateDebugInfo(info, info.currentThreadId, 0); + _debugInfo = info; + _stackPanel.updateDebugInfo(info, threadId, frameId); + _watchPanel.updateDebugInfo(info, threadId, frameId); + } + + void onStackFrameSelected(ulong threadId, int frame) { + if (_debugInfo) { + if (DebugThread t = _debugInfo.findThread(threadId)) { + if (frame < t.length) + updateLocation(t[frame]); + else + updateLocation(t.frame); + } + } } void onResponse(ResponseCode code, string msg) { @@ -186,6 +199,7 @@ class DebuggerUIHandler : DebuggerCallback { _stackPanel = new StackPanel("stack"); _stackPanel.dockAlignment = DockAlignment.Right; _ide.dockHost.addDockedWindow(_stackPanel); + _stackPanel.stackFrameSelected = this; } void switchToDevelopPerspective() { diff --git a/src/dlangide/ui/stackpanel.d b/src/dlangide/ui/stackpanel.d index 7daef90..23938ac 100644 --- a/src/dlangide/ui/stackpanel.d +++ b/src/dlangide/ui/stackpanel.d @@ -5,7 +5,13 @@ import dlangui; import std.string : format; import ddebug.common.debugger; -class StackPanel : DockWindow { +interface StackFrameSelectedHandler { + void onStackFrameSelected(ulong threadId, int frame); +} + +class StackPanel : DockWindow, OnItemSelectedHandler, CellActivatedHandler { + + Signal!StackFrameSelectedHandler stackFrameSelected; this(string id) { super(id); @@ -21,15 +27,16 @@ class StackPanel : DockWindow { _comboBox = new ComboBox("threadComboBox", ["Thread1"d]); _comboBox.layoutWidth = FILL_PARENT; _comboBox.selectedItemIndex = 0; + _comboBox.itemClick = this; _grid = new StringGridWidget("stackGrid"); - _grid.resize(2, 20); + _grid.cellActivated = this; + _grid.resize(2, 0); _grid.showColHeaders = true; _grid.showRowHeaders = false; _grid.layoutHeight = FILL_PARENT; _grid.layoutWidth = FILL_PARENT; _grid.setColTitle(0, "Function"d); _grid.setColTitle(1, "Address"d); - _grid.setCellText(0, 0, "main()"d); _grid.layoutWidth = FILL_PARENT; _grid.layoutHeight = FILL_PARENT; root.addChild(_comboBox); @@ -82,6 +89,22 @@ class StackPanel : DockWindow { } } + void onCellActivated(GridWidgetBase source, int col, int row) { + if (_debugInfo && _selectedThread && row < _selectedThread.length) { + if (stackFrameSelected.assigned) + stackFrameSelected(_currentThreadId, row); + } + } + + bool onItemSelected(Widget source, int itemIndex) { + if (_debugInfo && itemIndex < _debugInfo.length && _currentThreadId != _debugInfo[itemIndex].id) { + _grid.selectCell(0, 0, true, null, false); + if (stackFrameSelected.assigned) + stackFrameSelected(_debugInfo[itemIndex].id, 0); + } + return true; + } + protected void onPopupMenuItem(MenuItem item) { if (item.action) handleAction(item.action); @@ -91,5 +114,18 @@ class StackPanel : DockWindow { override bool handleAction(const Action a) { return super.handleAction(a); } + + override void layout(Rect rc) { + if (visibility == Visibility.Gone) { + return; + } + super.layout(rc); + _grid.autoFitColumnWidth(2); + int w = _grid.clientRect.width - _grid.colWidth(2); + if (w < _grid.clientRect.width * 2 / 3) + w = _grid.clientRect.width * 2 / 3; + _grid.setColWidth(1, w); + _grid.layout(_grid.pos); + } } diff --git a/src/dlangide/ui/watchpanel.d b/src/dlangide/ui/watchpanel.d index 90a3969..b3311d3 100644 --- a/src/dlangide/ui/watchpanel.d +++ b/src/dlangide/ui/watchpanel.d @@ -6,9 +6,10 @@ import std.string : format; import ddebug.common.debugger; class VariablesWindow : StringGridWidget { + DebugFrame _frame; this(string ID = null) { super(ID); - resize(3, 20); + resize(3, 0); showColHeaders = true; showRowHeaders = false; layoutHeight = FILL_PARENT; @@ -16,12 +17,23 @@ class VariablesWindow : StringGridWidget { setColTitle(0, "Variable"d); setColTitle(1, "Value"d); setColTitle(2, "Type"d); - setCellText(0, 0, "a"d); - setCellText(1, 0, "1"d); - setCellText(2, 0, "int"d); - setCellText(0, 1, "b"d); - setCellText(1, 1, "42"d); - setCellText(2, 1, "ulong"d); + autoFit(); + } + void setFrame(DebugFrame frame) { + _frame = frame; + if (frame && frame.locals) { + resize(3, frame.locals.length); + for (int i = 0; i < frame.locals.length; i++) { + DebugVariable var = frame.locals[i]; + setCellText(0, i, var.name.toUTF32); + setCellText(1, i, var.value.toUTF32); + setCellText(2, i, var.type.toUTF32); + } + autoFit(); + } else { + resize(3, 0); + autoFit(); + } } } @@ -38,6 +50,8 @@ class WatchPanel : DockWindow { protected VariablesWindow _autos; override protected Widget createBodyWidget() { + layoutWidth = FILL_PARENT; + layoutHeight = FILL_PARENT; _tabs = new TabWidget("WatchPanelTabs", Align.Bottom); _tabs.setStyles(null, STYLE_TAB_DOWN_DARK, STYLE_TAB_DOWN_BUTTON_DARK, STYLE_TAB_UP_BUTTON_DARK_TEXT); _tabs.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT); @@ -98,8 +112,9 @@ class WatchPanel : DockWindow { } } } - } else { } + _locals.setFrame(_frame); + _autos.setFrame(_frame); } }