GDB support, refactoring

This commit is contained in:
Vadim Lopatin 2015-12-17 16:35:07 +03:00
parent 253cb1f7b4
commit dd9163a5fb
2 changed files with 154 additions and 62 deletions

View File

@ -71,6 +71,8 @@ class DebugThread {
DebugFrame frame; DebugFrame frame;
DebuggingState state; DebuggingState state;
DebugStack stack; DebugStack stack;
@property int length() { return stack ? stack.length : 0; }
DebugFrame opIndex(int index) { return stack ? stack[index] : null; }
} }
class DebugThreadList { class DebugThreadList {
@ -85,13 +87,13 @@ class DebugThreadList {
return t; return t;
return null; return null;
} }
@property int length() { return threads.length; } @property int length() { return cast(int)threads.length; }
DebugThread opIndex(int index) { return threads[index]; } DebugThread opIndex(int index) { return threads[index]; }
} }
class DebugStack { class DebugStack {
DebugFrame[] frames; DebugFrame[] frames;
@property int length() { return frames.length; } @property int length() { return cast(int)frames.length; }
DebugFrame opIndex(int index) { return frames[index]; } DebugFrame opIndex(int index) { return frames[index]; }
} }
@ -381,3 +383,29 @@ abstract class DebuggerBase : Thread, Debugger {
} }
} }
/// helper for removing class array item by ref
T removeItem(T)(ref T[]array, T item) {
for (int i = cast(int)array.length - 1; i >= 0; i--) {
if (array[i] is item) {
for (int j = i; j < array.length - 1; j++)
array[j] = array[j + 1];
array.length = array.length - 1;
return item;
}
}
return null;
}
/// helper for removing array item by index
T removeItem(T)(ref T[]array, ulong index) {
if (index >= 0 && index < array.length) {
T res = array[index];
for (int j = cast(int)index; j < array.length - 1; j++)
array[j] = array[j + 1];
array.length = array.length - 1;
return res;
}
return null;
}

View File

@ -76,8 +76,17 @@ abstract class ConsoleDebuggerInterface : DebuggerBase, TextWriter {
} }
interface TextCommandTarget {
/// send command as a text string
int sendCommand(string text);
}
import std.process; import std.process;
class GDBInterface : ConsoleDebuggerInterface { class GDBInterface : ConsoleDebuggerInterface, TextCommandTarget {
this() {
_requests.setTarget(this);
}
protected int commandId; protected int commandId;
@ -315,15 +324,7 @@ class GDBInterface : ConsoleDebuggerInterface {
} }
return null; return null;
} }
private GDBBreakpoint findBreakpointByRequestToken(int token) {
if (token == 0)
return null;
foreach(gdbbp; _breakpoints) {
if (gdbbp.createRequestId == token)
return gdbbp;
}
return null;
}
private GDBBreakpoint findBreakpointByNumber(string number) { private GDBBreakpoint findBreakpointByNumber(string number) {
if (number.empty) if (number.empty)
return null; return null;
@ -333,8 +334,24 @@ class GDBInterface : ConsoleDebuggerInterface {
} }
return null; return null;
} }
void handleBreakpointRequestResult(GDBBreakpoint gdbbp, ResultClass resType, MIValue params) {
if (resType == ResultClass.done) { class AddBreakpointRequest : GDBRequest {
GDBBreakpoint gdbbp;
this(Breakpoint bp) {
gdbbp = new GDBBreakpoint();
gdbbp.bp = bp;
char[] cmd;
cmd ~= "-break-insert ";
if (!bp.enabled)
cmd ~= "-d "; // create disabled
cmd ~= bp.fullFilePath;
cmd ~= ":";
cmd ~= to!string(bp.line);
command = cmd.dup;
_breakpoints ~= gdbbp;
}
override void onResult() {
if (MIValue bkpt = params["bkpt"]) { if (MIValue bkpt = params["bkpt"]) {
string number = bkpt.getString("number"); string number = bkpt.getString("number");
gdbbp.number = number; gdbbp.number = number;
@ -343,20 +360,6 @@ class GDBInterface : ConsoleDebuggerInterface {
} }
} }
private void addBreakpoint(Breakpoint bp) {
GDBBreakpoint gdbbp = new GDBBreakpoint();
gdbbp.bp = bp;
char[] cmd;
cmd ~= "-break-insert ";
if (!bp.enabled)
cmd ~= "-d "; // create disabled
cmd ~= bp.fullFilePath;
cmd ~= ":";
cmd ~= to!string(bp.line);
gdbbp.createRequestId = sendCommand(cmd.dup);
_breakpoints ~= gdbbp;
}
/// update list of breakpoints /// update list of breakpoints
void setBreakpoints(Breakpoint[] breakpoints) { void setBreakpoints(Breakpoint[] breakpoints) {
char[] breakpointsToDelete; char[] breakpointsToDelete;
@ -383,7 +386,7 @@ class GDBInterface : ConsoleDebuggerInterface {
foreach(bp; breakpoints) { foreach(bp; breakpoints) {
GDBBreakpoint existing = findBreakpoint(bp); GDBBreakpoint existing = findBreakpoint(bp);
if (!existing) { if (!existing) {
addBreakpoint(bp); submitRequest(new AddBreakpointRequest(bp));
} else { } else {
if (bp.enabled && !existing.bp.enabled) { if (bp.enabled && !existing.bp.enabled) {
if (breakpointsToEnable.length) if (breakpointsToEnable.length)
@ -474,14 +477,11 @@ class GDBInterface : ConsoleDebuggerInterface {
} }
} }
int _threadInfoRequest;
int _stackListFramesRequest;
int _stackListLocalsRequest; int _stackListLocalsRequest;
DebugThreadList _currentState; DebugThreadList _currentState;
void updateState() { void updateState() {
_currentState = null; _currentState = null;
_threadInfoRequest = sendCommand("-thread-info"); submitRequest(new ThreadInfoRequest(), new StackListFramesRequest());
_stackListFramesRequest = sendCommand("-stack-list-frames");
} }
// +asyncclass,result // +asyncclass,result
@ -511,21 +511,35 @@ class GDBInterface : ConsoleDebuggerInterface {
Log.d("GDB WARN unknown result class type: ", msgType); Log.d("GDB WARN unknown result class type: ", msgType);
MIValue params = parseMI(s); MIValue params = parseMI(s);
Log.v("GDB result ^[", token, "] ", msgType, " params: ", (params ? params.toString : "unparsed: " ~ s)); Log.v("GDB result ^[", token, "] ", msgType, " params: ", (params ? params.toString : "unparsed: " ~ s));
if (GDBBreakpoint gdbbp = findBreakpointByRequestToken(token)) { if (_requests.handleResult(token, msgId, params)) {
// result of breakpoint creation operation // handled using result list
handleBreakpointRequestResult(gdbbp, msgId, params); } else {
return; Log.w("received results for unknown request");
} 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) { GDBRequestList _requests;
if (msgId == ResultClass.done) { /// submit single request or request chain
void submitRequest(GDBRequest[] requests ... ) {
for (int i = 0; i + 1 < requests.length; i++)
requests[i].chain(requests[i + 1]);
_requests.submit(requests[0]);
}
class ThreadInfoRequest : GDBRequest {
this() { command = "-thread-info"; }
override void onResult() {
_currentState = parseThreadList(params);
if (_currentState) {
// TODO
Log.d("Thread list is parsed");
}
}
}
class StackListFramesRequest : GDBRequest {
this() { command = "-stack-list-frames"; }
override void onResult() {
DebugStack stack = parseStack(params); DebugStack stack = parseStack(params);
if (stack) { if (stack) {
// TODO // TODO
@ -535,40 +549,30 @@ class GDBInterface : ConsoleDebuggerInterface {
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));
} }
} }
} else {
} }
} }
void handleThreadInfoRequestResult(ResultClass msgId, MIValue params) { class LocalVariableListRequest : GDBRequest {
if (msgId == ResultClass.done) { this(ulong threadId, int frameId) {
_currentState = parseThreadList(params); command = "-stack-list-locals --thread " ~ to!string(threadId) ~ " --frame " ~ to!string(frameId) ~ " --simple-values";
if (_currentState) {
// TODO
Log.d("Thread list is parsed");
_stackListLocalsRequest = sendCommand("-stack-list-locals --thread " ~ to!string(_currentState.currentThreadId) ~ " --frame 0 --all-values");
}
} else {
} }
} override void onResult() {
void handleLocalVariableListRequestResult(ResultClass msgId, MIValue params) {
if (msgId == ResultClass.done) {
DebugVariableList variables = parseVariableList(params); DebugVariableList variables = parseVariableList(params);
if (variables) { if (variables) {
// TODO // TODO
Log.d("Variable list is parsed: " ~ to!string(variables)); Log.d("Variable list is parsed: " ~ to!string(variables));
if (_currentState) { if (_currentState) {
if (DebugThread currentThread = _currentState.currentThread) { if (DebugThread currentThread = _currentState.currentThread) {
if (currentThread.stack.length > 0) { if (currentThread.length > 0) {
currentThread.stack[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");
} }
} }
} }
} }
} else {
} }
} }
@ -631,3 +635,63 @@ class GDBInterface : ConsoleDebuggerInterface {
} }
class GDBRequest {
int id;
string command;
ResultClass resultClass;
MIValue params;
GDBRequest next;
/// called if resultClass is done
void onResult() {
}
/// called if resultClass is error
void onError() {
}
/// called on other result types
void onOtherResult() {
}
/// chain additional request, for case when previous finished ok
GDBRequest chain(GDBRequest next) {
this.next = next;
return this;
}
}
struct GDBRequestList {
private TextCommandTarget _target;
private GDBRequest[int] _activeRequests;
void setTarget(TextCommandTarget target) {
_target = target;
}
void submit(GDBRequest request) {
request.id = _target.sendCommand(request.command);
if (request.id)
_activeRequests[request.id] = request;
}
bool handleResult(int token, ResultClass resultClass, MIValue params) {
if (token in _activeRequests) {
GDBRequest r = _activeRequests[token];
_activeRequests.remove(token);
r.resultClass = resultClass;
r.params = params;
if (resultClass == ResultClass.done) {
r.onResult();
if (r.next)
submit(r.next);
} else if (resultClass == ResultClass.error) {
r.onError();
} else {
r.onOtherResult();
}
return true;
}
return false;
}
}