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: Adding breakpoint support to the model
Debuggers generally should suspend when a new source file is loaded. Then the model can attach breakpoints to the debugger and resume operation afterwards.
Additionally we need to listen for breakpoint changes to add new breakpoints to the debugger or to remove them when they get disabled or removed by the user.
Lets start with the listener implementation:
public class TextDebugTarget extends TextDebugElement implements IDebugTarget, IEventProcessor { @Override public boolean supportsBreakpoint(final IBreakpoint breakpoint) { if (fFile.equals(breakpoint.getMarker().getResource())) { // breakpoint on our source file return true; } return false; } private boolean isEnabledBreakpoint(IBreakpoint breakpoint) { try { return breakpoint.isEnabled() && DebugPlugin.getDefault().getBreakpointManager().isEnabled(); } catch (CoreException e) { // ignore invalid breakpoint } return false; } // ************************************************************ // IBreakpointListener // ************************************************************ @Override public void breakpointAdded(final IBreakpoint breakpoint) { if ((supportsBreakpoint(breakpoint)) && (isEnabledBreakpoint(breakpoint))) fireModelEvent(new BreakpointRequest(breakpoint, BreakpointRequest.ADDED)); } @Override public void breakpointRemoved(final IBreakpoint breakpoint, final IMarkerDelta delta) { if (supportsBreakpoint(breakpoint)) fireModelEvent(new BreakpointRequest(breakpoint, BreakpointRequest.REMOVED)); } @Override public void breakpointChanged(final IBreakpoint breakpoint, final IMarkerDelta delta) { breakpointRemoved(breakpoint, delta); breakpointAdded(breakpoint); } }As breakpoints might be targeting non relevant source files or even other target languages we need to verify that a breakpoint is related to our source file. If yes, we send an adequate event to our debugger.
While there are no further checks for a breakpoint removal, we only add active breakpoints. There exist two separate enablements we need to verify: the breakpoint itself can be disabled and/or the BreakpointManager can be disabled globally.
Next we need to take care that we track breakpoint changes accordingly by subscribing to the BreakpointManager.
public class TextDebugTarget extends TextDebugElement implements IDebugTarget, IEventProcessor { @Override public void handleEvent(final IDebugEvent event) { if (!isDisconnected()) { if (event instanceof DebuggerStartedEvent) { // create debug thread TextThread thread = new TextThread(this); fThreads.add(thread); thread.fireCreationEvent(); // debugger got started and waits in suspended mode setState(State.SUSPENDED); // add breakpoint listener DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(this); // attach deferred breakpoints to debugger IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(getModelIdentifier()); for (IBreakpoint breakpoint : breakpoints) breakpointAdded(breakpoint); // inform eclipse of suspended state fireSuspendEvent(DebugEvent.CLIENT_REQUEST); } else if (event instanceof TerminatedEvent) { // debugger is terminated setState(State.TERMINATED); // unregister breakpoint listener DebugPlugin.getDefault().getBreakpointManager().removeBreakpointListener(this); // we do not need our dispatcher anymore fDispatcher.terminate(); // inform eclipse of terminated state fireTerminateEvent(); } } } }On a DebuggerStartedEvent we need to set all relevant breakpoints. If we stay suspended or immediately call resume() is a matter of taste.
Step 2: Adding breakpoint support to the debugger
The debugger needs to cache breakpoints and check them on a line change event. In our case we will simply track line numbers where we want to suspend:
public class TextDebugger implements IDebugger, IEventProcessor { private final Collection<Integer> fBreakpoints = new HashSet<>(); @Override public boolean isBreakpoint(final int lineNumber) { if (fBreakpoints.contains(lineNumber)) return true; return fIsStepping; } @Override public void handleEvent(final IDebugEvent event) { [...] } else if (event instanceof BreakpointRequest) { int line = ((BreakpointRequest) event).getBreakpoint().getMarker().getAttribute(IMarker.LINE_NUMBER, -1); if (line != -1) { if (((BreakpointRequest) event).getType() == BreakpointRequest.ADDED) fBreakpoints.add(line); else if (((BreakpointRequest) event).getType() == BreakpointRequest.REMOVED) fBreakpoints.remove(line); } } } }
Breakpoints are stored for a dedicated IDebugModelPresentation. We introduced one in the previous tutorial. Now we need to let TextDebugElement.getModelIdentifier() refer to this ID. If you do not set this identifier, you will not be able to find any breakpoints.
The mechanisms for setting and caching breakpoints might get a bit more complicated for a real world solution as you might support different kinds of breakpoints like function breakpoints or watchpoints.
No comments:
Post a Comment