mirror of https://github.com/buggins/dlangide.git
debugger UI - stack trace, locals
This commit is contained in:
parent
87ca1f8ddd
commit
9dc84ab7c7
2
dub.json
2
dub.json
|
@ -14,7 +14,7 @@
|
||||||
"copyFiles-windows": ["lib/win32/dcd-server.exe", "lib/win32/dcd-client.exe"],
|
"copyFiles-windows": ["lib/win32/dcd-server.exe", "lib/win32/dcd-client.exe"],
|
||||||
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dlangui": "~>0.7.24",
|
"dlangui": "~>0.7.25",
|
||||||
"libdparse": "==0.2.0"
|
"libdparse": "==0.2.0"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -177,6 +177,14 @@ class DebugVariableList {
|
||||||
foreach(t; v.variables)
|
foreach(t; v.variables)
|
||||||
variables ~= new DebugVariable(t);
|
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;
|
static __gshared _nextBreakpointId = 1;
|
||||||
|
@ -204,6 +212,9 @@ interface Debugger : ProgramExecution {
|
||||||
|
|
||||||
/// update list of breakpoints
|
/// update list of breakpoints
|
||||||
void setBreakpoints(Breakpoint[] bp);
|
void setBreakpoints(Breakpoint[] bp);
|
||||||
|
|
||||||
|
/// request stack trace and local vars for thread and frame
|
||||||
|
void requestDebugContextInfo(ulong threadId, int frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DebuggerCallback : ProgramExecutionStatusListener {
|
interface DebuggerCallback : ProgramExecutionStatusListener {
|
||||||
|
@ -219,7 +230,7 @@ interface DebuggerCallback : ProgramExecutionStatusListener {
|
||||||
void onResponse(ResponseCode code, string msg);
|
void onResponse(ResponseCode code, string msg);
|
||||||
|
|
||||||
/// send debug context (threads, stack frames, local vars...)
|
/// send debug context (threads, stack frames, local vars...)
|
||||||
void onDebugContextInfo(DebugThreadList info);
|
void onDebugContextInfo(DebugThreadList info, ulong threadId, int frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ResponseCode : int {
|
enum ResponseCode : int {
|
||||||
|
@ -302,8 +313,8 @@ class DebuggerProxy : Debugger, DebuggerCallback {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// send debug context (threads, stack frames, local vars...)
|
/// send debug context (threads, stack frames, local vars...)
|
||||||
void onDebugContextInfo(DebugThreadList info) {
|
void onDebugContextInfo(DebugThreadList info, ulong threadId, int frame) {
|
||||||
_callbackDelegate( delegate() { _callback.onDebugContextInfo(info); } );
|
_callbackDelegate( delegate() { _callback.onDebugContextInfo(info, threadId, frame); } );
|
||||||
}
|
}
|
||||||
|
|
||||||
void onResponse(ResponseCode code, string msg) {
|
void onResponse(ResponseCode code, string msg) {
|
||||||
|
@ -359,6 +370,10 @@ class DebuggerProxy : Debugger, DebuggerCallback {
|
||||||
void execRestart() {
|
void execRestart() {
|
||||||
_debugger.postRequest(delegate() { _debugger.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
|
/// update list of breakpoints
|
||||||
void setBreakpoints(Breakpoint[] breakpoints) {
|
void setBreakpoints(Breakpoint[] breakpoints) {
|
||||||
Breakpoint[] cloned;
|
Breakpoint[] cloned;
|
||||||
|
|
|
@ -481,7 +481,7 @@ class GDBInterface : ConsoleDebuggerInterface, TextCommandTarget {
|
||||||
DebugThreadList _currentState;
|
DebugThreadList _currentState;
|
||||||
void updateState() {
|
void updateState() {
|
||||||
_currentState = null;
|
_currentState = null;
|
||||||
submitRequest(new ThreadInfoRequest(), new StackListFramesRequest());
|
submitRequest(new ThreadInfoRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
// +asyncclass,result
|
// +asyncclass,result
|
||||||
|
@ -526,6 +526,12 @@ class GDBInterface : ConsoleDebuggerInterface, TextCommandTarget {
|
||||||
_requests.submit(requests[0]);
|
_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 {
|
class ThreadInfoRequest : GDBRequest {
|
||||||
this() { command = "-thread-info"; }
|
this() { command = "-thread-info"; }
|
||||||
override void onResult() {
|
override void onResult() {
|
||||||
|
@ -533,30 +539,43 @@ class GDBInterface : ConsoleDebuggerInterface, TextCommandTarget {
|
||||||
if (_currentState) {
|
if (_currentState) {
|
||||||
// TODO
|
// TODO
|
||||||
Log.d("Thread list is parsed");
|
Log.d("Thread list is parsed");
|
||||||
|
submitRequest(new StackListFramesRequest(_currentState.currentThreadId, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StackListFramesRequest : GDBRequest {
|
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() {
|
override void onResult() {
|
||||||
DebugStack stack = parseStack(params);
|
DebugStack stack = parseStack(params);
|
||||||
if (stack) {
|
if (stack) {
|
||||||
// TODO
|
// TODO
|
||||||
Log.d("Stack frames list is parsed: " ~ to!string(stack));
|
Log.d("Stack frames list is parsed: " ~ to!string(stack));
|
||||||
if (_currentState) {
|
if (_currentState) {
|
||||||
if (DebugThread currentThread = _currentState.currentThread) {
|
if (DebugThread currentThread = _currentState.findThread(_threadId)) {
|
||||||
currentThread.stack = stack;
|
currentThread.stack = stack;
|
||||||
Log.d("Setting stack frames for current thread");
|
Log.d("Setting stack frames for current thread");
|
||||||
}
|
}
|
||||||
submitRequest(new LocalVariableListRequest(_currentState.currentThreadId, 0));
|
submitRequest(new LocalVariableListRequest(_threadId, _frameId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalVariableListRequest : GDBRequest {
|
class LocalVariableListRequest : GDBRequest {
|
||||||
|
ulong _threadId;
|
||||||
|
int _frameId;
|
||||||
this(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";
|
command = "-stack-list-locals --thread " ~ to!string(threadId) ~ " --frame " ~ to!string(frameId) ~ " --simple-values";
|
||||||
}
|
}
|
||||||
override void onResult() {
|
override void onResult() {
|
||||||
|
@ -569,7 +588,7 @@ class GDBInterface : ConsoleDebuggerInterface, TextCommandTarget {
|
||||||
if (currentThread.length > 0) {
|
if (currentThread.length > 0) {
|
||||||
currentThread[0].locals = variables;
|
currentThread[0].locals = variables;
|
||||||
Log.d("Setting variables for current thread top frame");
|
Log.d("Setting variables for current thread top frame");
|
||||||
_callback.onDebugContextInfo(_currentState.clone());
|
_callback.onDebugContextInfo(_currentState.clone(), _threadId, _frameId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import dlangide.ui.watchpanel;
|
||||||
import ddebug.common.execution;
|
import ddebug.common.execution;
|
||||||
import ddebug.common.debugger;
|
import ddebug.common.debugger;
|
||||||
|
|
||||||
class DebuggerUIHandler : DebuggerCallback {
|
class DebuggerUIHandler : DebuggerCallback, StackFrameSelectedHandler {
|
||||||
|
|
||||||
private IDEFrame _ide;
|
private IDEFrame _ide;
|
||||||
private Debugger _debugger;
|
private Debugger _debugger;
|
||||||
|
@ -21,6 +21,7 @@ class DebuggerUIHandler : DebuggerCallback {
|
||||||
private DebugFrame _location;
|
private DebugFrame _location;
|
||||||
private WatchPanel _watchPanel;
|
private WatchPanel _watchPanel;
|
||||||
private StackPanel _stackPanel;
|
private StackPanel _stackPanel;
|
||||||
|
private DebugThreadList _debugInfo;
|
||||||
|
|
||||||
this(IDEFrame ide, Debugger debugger) {
|
this(IDEFrame ide, Debugger debugger) {
|
||||||
_ide = ide;
|
_ide = ide;
|
||||||
|
@ -38,10 +39,22 @@ class DebuggerUIHandler : DebuggerCallback {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// send debug context (threads, stack frames, local vars...)
|
/// 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");
|
Log.d("Debugger context received");
|
||||||
_stackPanel.updateDebugInfo(info, info.currentThreadId, 0);
|
_debugInfo = info;
|
||||||
_watchPanel.updateDebugInfo(info, info.currentThreadId, 0);
|
_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) {
|
void onResponse(ResponseCode code, string msg) {
|
||||||
|
@ -186,6 +199,7 @@ class DebuggerUIHandler : DebuggerCallback {
|
||||||
_stackPanel = new StackPanel("stack");
|
_stackPanel = new StackPanel("stack");
|
||||||
_stackPanel.dockAlignment = DockAlignment.Right;
|
_stackPanel.dockAlignment = DockAlignment.Right;
|
||||||
_ide.dockHost.addDockedWindow(_stackPanel);
|
_ide.dockHost.addDockedWindow(_stackPanel);
|
||||||
|
_stackPanel.stackFrameSelected = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void switchToDevelopPerspective() {
|
void switchToDevelopPerspective() {
|
||||||
|
|
|
@ -5,7 +5,13 @@ import dlangui;
|
||||||
import std.string : format;
|
import std.string : format;
|
||||||
import ddebug.common.debugger;
|
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) {
|
this(string id) {
|
||||||
super(id);
|
super(id);
|
||||||
|
@ -21,15 +27,16 @@ class StackPanel : DockWindow {
|
||||||
_comboBox = new ComboBox("threadComboBox", ["Thread1"d]);
|
_comboBox = new ComboBox("threadComboBox", ["Thread1"d]);
|
||||||
_comboBox.layoutWidth = FILL_PARENT;
|
_comboBox.layoutWidth = FILL_PARENT;
|
||||||
_comboBox.selectedItemIndex = 0;
|
_comboBox.selectedItemIndex = 0;
|
||||||
|
_comboBox.itemClick = this;
|
||||||
_grid = new StringGridWidget("stackGrid");
|
_grid = new StringGridWidget("stackGrid");
|
||||||
_grid.resize(2, 20);
|
_grid.cellActivated = this;
|
||||||
|
_grid.resize(2, 0);
|
||||||
_grid.showColHeaders = true;
|
_grid.showColHeaders = true;
|
||||||
_grid.showRowHeaders = false;
|
_grid.showRowHeaders = false;
|
||||||
_grid.layoutHeight = FILL_PARENT;
|
_grid.layoutHeight = FILL_PARENT;
|
||||||
_grid.layoutWidth = FILL_PARENT;
|
_grid.layoutWidth = FILL_PARENT;
|
||||||
_grid.setColTitle(0, "Function"d);
|
_grid.setColTitle(0, "Function"d);
|
||||||
_grid.setColTitle(1, "Address"d);
|
_grid.setColTitle(1, "Address"d);
|
||||||
_grid.setCellText(0, 0, "main()"d);
|
|
||||||
_grid.layoutWidth = FILL_PARENT;
|
_grid.layoutWidth = FILL_PARENT;
|
||||||
_grid.layoutHeight = FILL_PARENT;
|
_grid.layoutHeight = FILL_PARENT;
|
||||||
root.addChild(_comboBox);
|
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) {
|
protected void onPopupMenuItem(MenuItem item) {
|
||||||
if (item.action)
|
if (item.action)
|
||||||
handleAction(item.action);
|
handleAction(item.action);
|
||||||
|
@ -91,5 +114,18 @@ class StackPanel : DockWindow {
|
||||||
override bool handleAction(const Action a) {
|
override bool handleAction(const Action a) {
|
||||||
return super.handleAction(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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,10 @@ import std.string : format;
|
||||||
import ddebug.common.debugger;
|
import ddebug.common.debugger;
|
||||||
|
|
||||||
class VariablesWindow : StringGridWidget {
|
class VariablesWindow : StringGridWidget {
|
||||||
|
DebugFrame _frame;
|
||||||
this(string ID = null) {
|
this(string ID = null) {
|
||||||
super(ID);
|
super(ID);
|
||||||
resize(3, 20);
|
resize(3, 0);
|
||||||
showColHeaders = true;
|
showColHeaders = true;
|
||||||
showRowHeaders = false;
|
showRowHeaders = false;
|
||||||
layoutHeight = FILL_PARENT;
|
layoutHeight = FILL_PARENT;
|
||||||
|
@ -16,12 +17,23 @@ class VariablesWindow : StringGridWidget {
|
||||||
setColTitle(0, "Variable"d);
|
setColTitle(0, "Variable"d);
|
||||||
setColTitle(1, "Value"d);
|
setColTitle(1, "Value"d);
|
||||||
setColTitle(2, "Type"d);
|
setColTitle(2, "Type"d);
|
||||||
setCellText(0, 0, "a"d);
|
autoFit();
|
||||||
setCellText(1, 0, "1"d);
|
}
|
||||||
setCellText(2, 0, "int"d);
|
void setFrame(DebugFrame frame) {
|
||||||
setCellText(0, 1, "b"d);
|
_frame = frame;
|
||||||
setCellText(1, 1, "42"d);
|
if (frame && frame.locals) {
|
||||||
setCellText(2, 1, "ulong"d);
|
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;
|
protected VariablesWindow _autos;
|
||||||
|
|
||||||
override protected Widget createBodyWidget() {
|
override protected Widget createBodyWidget() {
|
||||||
|
layoutWidth = FILL_PARENT;
|
||||||
|
layoutHeight = FILL_PARENT;
|
||||||
_tabs = new TabWidget("WatchPanelTabs", Align.Bottom);
|
_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.setStyles(null, STYLE_TAB_DOWN_DARK, STYLE_TAB_DOWN_BUTTON_DARK, STYLE_TAB_UP_BUTTON_DARK_TEXT);
|
||||||
_tabs.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
|
_tabs.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
|
||||||
|
@ -98,8 +112,9 @@ class WatchPanel : DockWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
}
|
}
|
||||||
|
_locals.setFrame(_frame);
|
||||||
|
_autos.setFrame(_frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue