Debug Framework Tutorials
For a list of all debug related tutorials see Debug Framework Tutorials Overview.
Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online.
Step 1: Implementing variable classes
Variables are attached to StackFrames and will only be queried when the debugger is in suspended state. To implement them, we need a variable description stored in TextVariable:
public class TextVariable extends TextDebugElement implements IVariable { private final String fName; private IValue fValue; protected TextVariable(IDebugTarget target, String name, String value) { super(target); fName = name; setValue(value); } @Override public void setValue(String expression) { fValue = new TextValue(getDebugTarget(), expression); } @Override public boolean supportsValueModification() { return false; } @Override public IValue getValue() { return fValue; } @Override public String getName() { return fName; } @Override public String getReferenceTypeName() throws DebugException { return "text type"; } }The implementation is straight forward and does not allow to modify the variable content.
An actual value is stored in the TextValue class:
public class TextValue extends TextDebugElement implements IValue { private final String fValue; public TextValue(IDebugTarget target, String value) { super(target); fValue = value; } @Override public String getReferenceTypeName() throws DebugException { return "text type"; } @Override public String getValueString() throws DebugException { return fValue; } @Override public IVariable[] getVariables() throws DebugException { return new IVariable[0]; } @Override public boolean hasVariables() throws DebugException { return false; } }To display hierarchical structures a value might hold child variables (eg. fields of a class, elements of a collection, ...). In that case you need to implement hasVariables() and getVariables() too.
You might have noticed that both classes define a method getReferenceTypeName(). While TextVariable.getReferenceTypeName() denotes the declared variable type, TextValue.getReferenceTypeName() denotes the actual type. Looking at a java example
IValue value = new TextValue();The declared type would be IValue, while the actual type would be TextValue. You may enable these columns in the debug view by opening the Variables view menu and selecting Layout / Select Columns...
Step 2: Updating variables
Now for the big question: "how and when to update variables".
First we notice that variables are queried by the debug framework when a StackFrame element is selected that is in suspended mode. As we should do all our debugger queries in asynchronous mode we have two options:
- populate all stack frames automatically with variables on a suspend event in case our user might query them
- first deliver outdated/no data and update them as fast as possible
public class TextStackFrame extends TextDebugElement implements IStackFrame { private final List<TextVariable> fVariables = new ArrayList<TextVariable>(); private boolean fDirtyVariables = true; @Override public synchronized IVariable[] getVariables() { if (fDirtyVariables) { fDirtyVariables = false; getDebugTarget().fireModelEvent(new FetchVariablesRequest()); } return fVariables.toArray(new IVariable[fVariables.size()]); } public synchronized void setVariables(Map<String, String> variables) { for (String name : variables.keySet()) { boolean processed = false; // try to find existing variable for (TextVariable variable : fVariables) { if (variable.getName().equals(name)) { variable.setValue(variables.get(name)); variable.fireChangeEvent(DebugEvent.CONTENT); processed = true; break; } } if (!processed) { // not found, create new variable TextVariable textVariable = new TextVariable(getDebugTarget(), name, variables.get(name)); fVariables.add(textVariable); textVariable.fireCreationEvent(); } } } @Override public synchronized void fireChangeEvent(int detail) { fDirtyVariables = true; super.fireChangeEvent(detail); } @Override public boolean hasVariables() { return getVariables().length > 0; } }
The TextDebugger sends its list of variables as a new event to the TextDebugTarget, which simply delegates the call to the topmost StackFrame within its handleEvent() method. During the update we send either change events or creation events for the updated variables. Of course a real implementation should be more intelligent than the implemented version above and use intelligent comparisons and caching.
If you want changed contents to be highlighted you should implement IVariable.hasValueChanged(). An example implementation is provided in the source code on github.
UI problems
There is one behavior, I could not fix yet: Sometimes the Variables view remains empty when the debugger suspends. Pushing the view to back and bringing it to front again solves this issue. Not sure if this is a problem in my code or the debugger framework. See bug 528120 for details.
No comments:
Post a Comment