| <html> |
| |
| <head> |
| <meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> |
| <title>How to Write an Eclipse Debugger</title> |
| <link rel="stylesheet" href="../default_style.css"> |
| </head> |
| |
| <body LINK="#0000ff" VLINK="#800080"> |
| <div align="right"> <font face="Times New Roman, Times, serif" size="2">© |
| Copyright International Business Machines Corporation, 2004. All rights reserved</font> |
| <br> |
| <font face="Times New Roman, Times, serif" size="2">© Copyright Bjorn Freeman-Benson, |
| 2004. All rights reserved</font> |
| <table border=0 cellspacing=0 cellpadding=2 width="100%"> |
| <tr> |
| <td align=LEFT valign=TOP colspan="2" bgcolor="#0080C0"><b><font face="Arial,Helvetica"><font color="#FFFFFF"> Eclipse |
| Corner Article</font></font></b></td> |
| </tr> |
| </table> |
| </div> |
| <div align="left"> |
| <h1><img src="images/Idea.jpg" align=CENTER width="120" height="86"></h1> |
| </div> |
| <h1 ALIGN="CENTER"><br> |
| How to write an Eclipse debugger</h1> |
| |
| <blockquote> |
| <b>Summary</b> |
| |
| <br> |
| One of the major tasks of adding a new language to an Eclipse-based IDE is debugging |
| support. A debugger needs to start and stop the program being debugged, suspend |
| and resume, single-step, manage breakpoints and watch points, and so on. This |
| article explains the Eclipse Platform debug framework and steps through a simple, |
| yet illustrative, example of adding debug support for a new language. |
| <p><b> By Darin Wright, IBM Rational Software Group<br> |
| Bjorn Freeman-Benson, Predictable Software</b><br> |
| <font size="-1">August 27, 2004</font> </p> |
| </blockquote> |
| |
| <hr width="100%"> |
| <h2>Language support</h2> |
| <p>No matter how simple we want to make the topic, there is no getting around |
| the fact that adding a new language to an Eclipse IDE is not a trivial task. |
| This paper, together with its two companion papers, focuses on the |
| launching-running-debugging side of the process. Other aspects of adding new |
| language support include: editors, code assist, refactorings, new views, |
| builders, and other tools. These features are outside the scope of these papers.</p> |
| <p>The first paper in this series, <i><a href="../Article-Launch-Framework/launch.html">We |
| Have Lift-off: The Launching Framework in Eclipse</a></i>, describes the launching |
| framework through an applet launcher example. In this paper, we describe the |
| basic debugging framework using a small assembly language example. And in our |
| third paper (not yet published), <i>Enhancing the Eclipse Debugger</i>, we describe |
| how to enhance the UI for our assembly language debugger by adding all the little |
| bells and whistles that one expects in modern IDEs.</p> |
| <p>The Eclipse SDK provides a framework for building and integrating debuggers, |
| collectively known as the debug platform. The debug platform defines a set of |
| Java™ interfaces modeling a set of artifacts and actions common to many |
| debuggers, known as the debug model. For example, some common debug artifacts |
| are threads, stack frames, variables, and breakpoints; and some common actions |
| are suspending, stepping, resuming, and terminating. The platform does not provide |
| an implementation of a debugger that is the job of language tool developers. |
| However, the platform does provide a basic debugger user interface (that is, |
| the debug perspective) that can be enhanced with features specific to a particular |
| debugger. The base user interface operates against the debug model interfaces, |
| providing views for a programs call stack, variables, breakpoints, watch items, |
| and console I/O, and allows a user to step through source code. The debug platform |
| also provides a framework for launching applications from within the Eclipse |
| IDE, and a framework to perform source lookup.</p> |
| <p>In order to keep this paper to the stated goal of how to write a debugger, |
| and away from the larger problem of how to add a support for a new language, |
| we make the following assumptions:</p> |
| <ul> |
| <li>The new language has an existing execution engine or interface or VM. This |
| paper does not describe how to write an interpreter, virtual machine, or compiler |
| run-time. For our example, the interpreter is implemented as a Perl program |
| (pdavm/pda.pl in the example code). The language and the debugger interface |
| are described in the next section. </li> |
| <li>An Eclipse launcher and launch configuration already exists for the new |
| language. At this point, the launcher needs to create a run configuration |
| this paper will describe how to add a debug configuration. (For completeness, |
| here are the parts that comprise the example launcher support: |
| <ul> |
| <li>org.eclipse.debug.examples.core contributes an org.eclipse.debug.core.launchConfigurationTypes |
| named org.eclipse.debug.examples.core.launchConfigurationType.pda, as |
| well as the two class implementation of the launch configuration (IPDAConstants |
| and PDALaunchDelegate)</li> |
| <li>org.eclipse.debug.examples.core contributes an org.eclipse.core.variables.valueVariables |
| that points to the Perl executable</li> |
| <li>org.eclipse.debug.examples.ui contributes an org.eclipse.debug.ui.launchConfigurationTabGroups |
| for that launch configuration type, as well as the two-class implementation |
| of the tab groups (PDATabGroup and PDAMainTab).</li> |
| </ul> |
| </li> |
| <li>The source for the new language is text files that are displayed in a text |
| editor. The text editor might be a specialized editor with code coloring, |
| formatting, extra menu items, etc., or it might be just the standard Eclipse |
| text editor.</li> |
| </ul> |
| <h2>Our language and its interpreter</h2> |
| <p>To demonstrate how to write a debugger for Eclipse, we need a language and |
| a run time to debug. For this example, we chose an enhanced push down automata |
| (PDA) assembly language and a simple interpreter implemented in Perl. Each line |
| contains a single operation and any number of arguments. Our language differs |
| from a standard PDA in two major ways:</p> |
| <ul> |
| <li>Our language has a control stack and thus has call-return subroutines.</li> |
| <li>Our language allows data to be stored either on the data stack or in named |
| variables on the control stack.</li> |
| </ul> |
| <blockquote>In order to actually run this example, you will need a Perl interpreter. |
| Linux®™ comes with Perl. For Microsoft® Windows®, we use either |
| ActivePerl (<a href="http://www.activeperl.com/">http://www.activeperl.com/</a>) |
| or Indigo Perl (<a href="http://www.indigostar.com/">http://www.indigostar.com/</a>). |
| You also have to set the string substitution variable named perlExecutable |
| to the complete path to your Perl interpreter. (For example, ours was C:\perl\bin\perl.exe) |
| To set a string substitution variable, use the Windows > Preferences > |
| Run/Debug > String Substitution preferences page.</blockquote> |
| <p>Here is an annotated example of the Fibonacci computation (note that the |
| annotations themselves are not valid syntax in this language in this language, |
| all comments must start at column 1 and be the entire line):</p> |
| <pre>push 6 |
| call Fibonacci <i>function call with one argument on the data stack</i> |
| output <i>print result to stdout</i> |
| halt |
| # |
| # f(n) = f(n-1) + f(n-2) |
| # f(0) = 1 |
| # f(1) = 1 |
| # |
| :fibonacci |
| var n <i>define variable n on control stack</i> |
| pop $n <i>get n from data stack</i> |
| push $n |
| branch_not_zero gt0 |
| push 1 <i>f(0) = 1</i> |
| return <i>return with one value on data stack</i> |
| :gt0 |
| push $n |
| dec |
| branch_not_zero gt1 |
| push 1 <i>f(1) = 1</i> |
| return <i>return with one value on data stack</i> |
| :gt1 |
| push $n <i>stack: n</i> |
| dec <i>stack: n-1</i> |
| call fibonacci <i>stack: f(n-1)</i> |
| push $n <i>stack: f(n-1) n</i> |
| dec <i>stack: f(n-1) n-1</i> |
| dec <i>stack: f(n-1) n-2</i> |
| call Fibonacci <i>stack: f(n-1) f(n-2)</i> |
| add <i>stack: f(n-1)+f(n-2)</i> |
| return <i>return with one value on data stack</i></pre> |
| <h3><a name="Interpreter Debug Interface">Interpreter debug interface</a></h3> |
| <p>Our PDA assembly language interpreter can be started in either run mode or |
| debug mode. When started in debug mode, the interpreter listens for debug |
| commands on a specified local TCP socket and sends debug events to a separate |
| local TCP socket. The commands include:</p> |
| <ul> |
| <li><samp>clear N</samp> clear the breakpoint on line N</li> |
| <li><samp>data</samp> return the contents of the data stack; the data is |
| returned from oldest to newest as a single string value|value|value|
|value|</li> |
| <li><samp>exit</samp> end the interpreter</li> |
| <li><samp>resume</samp> resume full speed execution of the program</li> |
| <li><samp>set N</samp> set a breakpoint on line N</li> |
| <li><samp>stack</samp> return the contents of the control stack (program |
| counters, function and variable names); the stack is returned from oldest to |
| newest as a single string frame#frame#frame#
#frame. Each frame is a string |
| filename|pc|function name|variable name|variable name|
|variable name</li> |
| <li><samp>step</samp> single step forward</li> |
| <li><samp>suspend</samp> end full speed execution and listen for debug |
| commands</li> |
| <li><samp>var N M</samp> return the contents of a variable M from the |
| control stack frame N (stack frames are indexed from 0).</li> |
| </ul> |
| <p>The debug events that are reported asynchronously to the second socket |
| include:</p> |
| <ul> |
| <li><samp>started</samp> the interpreter has started (guaranteed to be the |
| first event sent)</li> |
| <li><samp>terminated</samp> the interpreter has terminated (guaranteed to |
| be the last event sent)</li> |
| <li><samp>suspended X</samp> the interpreter has suspended and entered debug |
| mode; X is the cause of the suspension, either step or client or breakpoint |
| N</li> |
| <li><samp>resumed X</samp> the interpreter has resumed execution in run mode; |
| X is the cause of the resume, either step or client</li> |
| <li><samp>unimplemented instruction X</samp> an unimplemented instruction |
| was encountered</li> |
| <li><samp>no such label X</samp> a branch or call to an unknown label was |
| encountered </li> |
| </ul> |
| <h2>Adding debugger support to the launch delegate</h2> |
| <blockquote><i>First Notation Note: </i>The class and object diagrams in this article use a |
| mostly UML syntax. It is only mostly UML because, unfortunately, UML does not |
| provide a way to model extension points and extensions. Therefore, we decided to |
| use a dashed box to indicate an extension in the plugin.xml file. We also use a |
| few non-standard, but labeled, lines with arrows to indicate certain semantics.<p> |
| <i>Second Notation Note:</i> The Eclipse design guidelines include separating the |
| code for the model from the code for the user interface. We abide by that design |
| principle in our example code resulting in two plug-ins. The extensions and code |
| discussed in this article come from both plug-ins.</p> |
| <p><i>Third Notation Note:</i> Each code and plugin.xml fragment in this article |
| includes a header describing the plug-in, package, class, and other details of |
| its location in the complete example code. In order to fit all this on one line, |
| the header abbreviates the org.eclipse.debug.examples.pda.core plug-in as |
| core, the org.eclipse.debug.examples.core.pda.model package as pda.model, |
| etc.</blockquote> |
| <p>Using the previous assumptions, we already have a launch configuration type |
| (org.eclipse.debug.examples.core.launchConfigurationType.pda) and a launch configuration |
| delegate (PDALaunchDelegate). To add debugger support, we modify the delegate |
| by adding code for DEBUG_MODE that performs the following actions:</p> |
| <ul> |
| <li> <img src="images/tag_1.gif" align=CENTER width="24" height="13"> Modifies |
| the process command line to include the debug parameters. For details on what |
| the debug parameters for our interpreter are, see the previous section.</li> |
| <li> <img src="images/tag_2.gif" align=CENTER width="24" height="13"> Creates |
| and registers the PDADebugTarget object for this launch. For details on where |
| the debug target fits into the model, see the next section.</li> |
| </ul> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1">Package:</font> |
| <font color="blue">pda.launching</font>, <font size="-1">Class:</font> <font color="blue">PDALaunchDelegate</font>, |
| <font size="-1">Method:</font> <font color="blue">launch</font></u> |
| <pre> commandList.add(file.getLocation().toOSString()); |
| <img src="images/tag_1.gif" align=CENTER width="24" height="13"> int requestPort = -1; |
| <font color="#0000FF">.</font> int eventPort = -1; |
| <font color="#0000FF">.</font> if<b> </b>(mode.equals(ILaunchManager.DEBUG_MODE)) { |
| <font color="#0000FF">.</font> requestPort = findFreePort(); |
| <font color="#0000FF">.</font> eventPort = findFreePort(); |
| <font color="#0000FF">.</font> if (requestPort == -1 || eventPort == -1) { |
| <font color="#0000FF">.</font> abort("Unable to find free port", null); |
| <font color="#0000FF">.</font> } |
| <font color="#0000FF">.</font> commandList.add("-debug"); |
| <font color="#0000FF">.</font> commandList.add("" + requestPort ); |
| <font color="#0000FF">.</font> commandList.add("" + eventPort ); |
| <font color="#0000FF">.</font> } |
| String[] commandLine = (String[]) |
| commandList.toArray(new String[commandList.size()]); |
| Process process = DebugPlugin.exec(commandLine, null); |
| IProcess p = DebugPlugin.newProcess(launch, process, path); |
| <img src="images/tag_2.gif" align=CENTER width="24" height="13"> if (mode.equals(ILaunchManager.DEBUG_MODE)) { |
| <font color="#0000FF">.</font> IDebugTarget target = new PDADebugTarget(launch,p,requestPort,eventPort ); |
| <font color="#0000FF">.</font> launch.addDebugTarget(target); |
| <font color="#0000FF">.</font> }</pre> |
| <h2>The debug model</h2> |
| <p>The Eclipse debug model is documented in the <i><cite>Platform Plug-in Developer |
| Guide</cite></i> under <b>Programmers Guide > Program debug and launch support> |
| Debugging a Program> Platform debug model</b>. Being more visually oriented, |
| we prefer to look at pictures, so here is a mostly UML diagram of the Eclipse |
| platform debug model:</p> |
| <p align="center"> |
| <img border="0" src="images/debug_model.gif" width="408" height="608"></p> |
| <p>To implement our debugger (the PDA debugger), we have to provide an |
| implementation of each of these debug model interfaces. Most of the |
| implementations are very straightforward except, perhaps, these small items:</p> |
| <ul> |
| <li>The PDADebugElement class includes fireEvent, fireCreationEvent, fireSuspendEvent, |
| and other methods as a convenience to avoid code duplication in the subclasses.</li> |
| <li>The PDADebugTarget is the object that communicates with our interpreter |
| and is complex enough that we describe it in a following section of its own.</li> |
| <li>There is only one thread in a PDA program, so the PDAThread class delegates |
| all its operations (suspend, resume, isSuspended, getStackFrames, and so on) |
| to the debug target. This allows us to encapsulate all communication with |
| the interpreter in our single PDADebugTarget object.</li> |
| <li>The only thing the PDAStackFrame does that is mildly interesting is to parse |
| and cache the stack frame reply message from the interpreter (the string described |
| in the previous section). Other than that, the implementation of getLineNumber |
| returns the previously cached line number; the implementation of stepOver |
| delegates to the thread; and so on.</li> |
| <li>PDAVariable is just a wrapper for the variable name; it delegates the |
| fetching of the value to the debug target.</li> |
| <li>PDAValue is even simpler because the PDA language has only one type: a |
| value. Strings and numbers are stored identically in the interpreter and thus |
| the only thing the PDAValue object does is display the value.</li> |
| <li>The PDALineBreakpoint class is the second most complex class in our model |
| (behind the debug target class), but the Eclipse debug framework provides |
| an excellent abstract implementation of line-oriented breakpoints named LineBreakpoint. |
| Thus PDALineBreakpoint subclasses LineBreakpoint and adds its model identifier |
| and its own constructor. The constructor creates and associates a marker with |
| the breakpoint so that they show up in the annotation ruler. (See <a href="#Breakpoints">section |
| below</a> on breakpoints for more details about this code and the marker ID.)</li> |
| </ul> |
| <pre> public PDALineBreakpoint(IResource resource, int lineNumber) throws CoreException { |
| IMarker marker = resource.createMarker( |
| "org.eclipse.debug.examples.core.pda.lineBreakpoint.marker"); |
| setMarker(marker); |
| setEnabled(true); |
| ensureMarker().setAttribute(IMarker.LINE_NUMBER, lineNumber); |
| ensureMarker().setAttribute(IBreakpoint.ID, IPDAConstants.ID_PDA_DEBUG_MODEL); |
| }</pre> |
| <p>There are two main issues to keep in mind when implementing the debug model |
| classes:</p> |
| <ul> |
| <li>The Eclipse platform is inherently multi-threaded so it is important that |
| all the classes are thread safe, including those that must be single threaded. |
| For example, the debug socket channel to our PDA interpreter is single threaded: |
| the interpreter can only receive one command or request at a time. Thus the |
| methods in our model that communicate with the interpreter must be synchronized |
| to prevent overlapping requests.</li> |
| <li>The user interface actions, for example the step command, are issued from |
| Eclipses user interface thread. Thus these actions must be non-blocking. |
| We use step as the example here, because it might seem that step should block, |
| that it should send the step debug command and wait for the interpreter to |
| do the step, but that behavior would be wrong. As we will see below, the interpreter |
| communicates with the debug model by firing events rather than returning result |
| codes from commands.</li> |
| </ul> |
| <p><a name="Blocking commands">The incorrect blocking command</a> would block the entire Eclipse |
| user interface |
| until the interpreter had finished stepping. In the case of infinite loops and |
| other client program defects, the worst case is that the interpreter might never |
| return thus leaving the Eclipse user interface completely frozen.</p> |
| <p align="center"> |
| <img border="0" src="images/incorrect.gif" width="327" height="336"></p> |
| <p>By sending the step command asynchronously, the Eclipse user interface |
| remains responsive while the interpreter performs the command. (For those |
| readers who are not 100% familiar with UML, notice the difference in the arrows: |
| a synchronous call has a full arrow head and an asynchronous message has a half |
| arrow head.)</p> |
| <p align="center"> <img border="0" src="images/correct.gif" width="327" height="334"></p> |
| <h3>Code highlights of our debug target</h3> |
| <p>In our debug model, the PDADebugTarget is where most of the action happens |
| because the debug target is where we centralize the communication with the PDA |
| interpreter. For example, when the variable view asks a variable for its value, |
| the variable asks the debug target to ask the interpreter for the value.</p> |
| <p align="center"> |
| <img border="0" src="images/get_variable_value.gif" width="451" height="180"></p> |
| <h4>Constructor and initialization</h4> |
| <p>As described <a href="#Interpreter Debug Interface">above</a>, the debug |
| interface to our interpreter consists of two sockets: one for debug commands and |
| one for debug events. Thus when we create our debug target, |
| <img src="images/tag_1.gif" align=CENTER width="24" height="13"> first we initialize some instance |
| variables (our launch, our process, our one thread, etc), then |
| <img src="images/tag_2.gif" align=CENTER width="24" height="13"> we open two debug sockets with readers |
| and writers, then <img src="images/tag_3.gif" align=CENTER width="24" height="13"> we start a thread to |
| listen on the event socket (we are using the Eclipse background process |
| mechanism), and lastly |
| <img src="images/tag_4.gif" align=CENTER width="24" height="13"> we register to listen for breakpoint |
| changes in order to send the set and clear commands to the interpreter.</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1">Package:</font> |
| <font color="blue"> pda.</font><font color="#0000FF">model</font>, <font size="-1">Class:</font> |
| <font color="#0000FF">PDADebugTarget</font></u> |
| <pre> public PDADebugTarget(ILaunch launch, IProcess process, |
| int requestPort, int eventPort) throws CoreException { |
| super(null); |
| <img src="images/tag_1.gif" align=CENTER width="24" height="13"> fLaunch = launch; |
| <font color="#0000FF">.</font> fTarget = this; |
| <font color="#0000FF">.</font> fProcess = process; |
| <font color="#0000FF">.</font> fThread = new PDAThread(this); |
| <font color="#0000FF">.</font> fThreads = new IThread[] {fThread}; |
| <img src="images/tag_2.gif" align=CENTER width="24" height="13"> try { |
| <font color="#0000FF">.</font> fRequestSocket = new Socket("localhost", requestPort); |
| <font color="#0000FF">.</font> fRequestWriter = new PrintWriter(fRequestSocket.getOutputStream()); |
| <font color="#0000FF">.</font> fRequestReader = new BufferedReader(new InputStreamReader( |
| <font color="#0000FF">.</font> fRequestSocket.getInputStream())); |
| <font color="#0000FF">.</font> fEventSocket = new Socket("localhost", eventPort); |
| <font color="#0000FF">.</font> fEventReader = new BufferedReader(new InputStreamReader( |
| <font color="#0000FF">.</font> fEventSocket.getInputStream())); |
| <font color="#0000FF">.</font> } catch (UnknownHostException e) { |
| <font color="#0000FF">.</font> abort("Unable to connect to PDA VM", e); |
| <font color="#0000FF">.</font> } catch (IOException e) { |
| <font color="#0000FF">.</font> abort("Unable to connect to PDA VM", e); |
| <font color="#0000FF">.</font> } |
| <img src="images/tag_3.gif" align=CENTER width="24" height="13"> fEventDispatch = new EventDispatchJob(); |
| <font color="#0000FF">.</font> fEventDispatch.schedule(); |
| <img src="images/tag_4.gif" align=CENTER width="24" height="13"> DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(this); |
| }</pre> |
| <h4>Communicate with interpreter</h4> |
| <p>Now that communications with the debug sockets on the interpreter are |
| initialized, the debug target methods can send the appropriate commands. For |
| example, step and getVariableValue:</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1">Package:</font> |
| <font color="blue"> pda.</font><font color="#0000FF">model</font>, <font size="-1">Class:</font> |
| <font color="#0000FF">PDADebugTarget</font></u> |
| <pre> public void step() throws DebugException { |
| sendRequest("step"); |
| } |
| private void sendRequest(String request) throws DebugException { |
| synchronized (fRequestSocket) { |
| fRequestWriter.println(request); |
| fRequestWriter.flush(); |
| try { |
| // wait for "ok" |
| String response = fRequestReader.readLine(); |
| } catch (IOException e) { |
| abort("Request failed: " + request, e); |
| } |
| } |
| } |
| |
| protected IValue getVariableValue(PDAVariable variable) throws DebugException { |
| synchronized (fRequestSocket) { |
| fRequestWriter.println("var " |
| + variable.getStackFrame().getIdentifier() |
| + " " + variable.getName()); |
| fRequestWriter.flush(); |
| try { |
| String value = fRequestReader.readLine(); |
| return new PDAValue(this, value); |
| } catch (IOException e) { |
| abort(MessageFormat.format("Unable to retrieve value for variable {0}", |
| new String[]{variable.getName()}), e); |
| } |
| } |
| return null; |
| }</pre> |
| <p>There are many more "communicate with the interpreter" methods. Notice |
| that all communication with the interpreter is serialized using synchronized |
| blocks as mentioned previously, this is necessary because the Eclipse platform |
| is inherently multi-threaded but our PDA interpreter is not.</p> |
| <p>Note that our getVariableValue method is synchronous which goes against our |
| <a href="#Blocking commands">earlier caveat</a> to avoid using blocking communication |
| with the target due to the risk of freezing the Eclipse user interface. We chose |
| the synchronous "send the request and wait for reply" rather than |
| an asychronous "send command and later receive the response in an event" |
| for its simplicity in explaining the essential concepts of communication.</p> |
| <h4>Breakpoints are slightly more interesting</h4> |
| <p>The add-a-breakpoint method is a little more interesting because it has to |
| check if |
| <img src="images/tag_1.gif" align=CENTER width="24" height="13"> the breakpoint is valid and |
| <img src="images/tag_2.gif" align=CENTER width="24" height="13"> active, but otherwise it has the |
| same communicate with the interpreter style. The key to determining if the |
| breakpoint is valid is realizing that breakpoint listeners (like this one) get |
| notified of all breakpoint changes, thus we filter for breakpoints |
| <img src="images/tag_3.gif" align=CENTER width="24" height="13"> supported by our debug model and for |
| <img src="images/tag_4.gif" align=CENTER width="24" height="13"> our PDA program:</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1">Package:</font> |
| <font color="blue"> pda.</font><font color="#0000FF">model</font>, <font size="-1">Class:</font> |
| <font color="#0000FF">PDADebugTarget</font></u> |
| <pre> public void breakpointAdded(IBreakpoint breakpoint) { |
| <img src="images/tag_1.gif" align=CENTER width="24" height="13"> if (supportsBreakpoint(breakpoint)) { |
| try { |
| <img src="images/tag_2.gif" align=CENTER width="24" height="13"> if (breakpoint.isEnabled()) { |
| synchronized (fRequestSocket) { |
| try { |
| sendRequest("set " |
| + (((ILineBreakpoint)breakpoint).getLineNumber() - 1)); |
| } catch (CoreException e) { |
| } |
| } |
| } |
| } catch (CoreException e) { |
| } |
| } |
| } |
| public boolean supportsBreakpoint(IBreakpoint breakpoint) { |
| <img src="images/tag_3.gif" align=CENTER width="24" height="13"> if (breakpoint.getModelIdentifier().equals(IPDAConstants.ID_PDA_DEBUG_MODEL)) { |
| try { |
| String program = getLaunch().getLaunchConfiguration() |
| .getAttribute(IPDAConstants.ATTR_PDA_PROGRAM, |
| (String)null); |
| if (program != null) { |
| IMarker marker = breakpoint.getMarker(); |
| if (marker != null) { |
| IPath p = new Path(program); |
| <img src="images/tag_4.gif" align=CENTER width="24" height="13"> return marker.getResource().getFullPath().equals(p); |
| } |
| } |
| } catch (CoreException e) { |
| } |
| } |
| return false; |
| }</pre> |
| <h4>Synchronizing with the interpreter at startup</h4> |
| <p>The other interesting piece of our debug target is the startup code. When the |
| interpreter starts up, it has no breakpoints. Users, having set breakpoints in |
| the code using the Eclipse user interface, expect those breakpoints to work. |
| Thus, after the interpreter starts up, but before it processes any instructions, |
| the debug target has to reach in and set the initial breakpoints. These initial |
| breakpoints are known as <i>deferred breakpoints</i>.</p> |
| <p>The standard way this initial setup is accomplished is by having the |
| interpreter suspend on startup, <img src="images/tag_2.gif" align=CENTER width="24" height="13"> wait |
| for the debug target to set all the breakpoints, and then |
| <img src="images/tag_3.gif" align=CENTER width="24" height="13"> have the debug target resume the |
| interpreter. (Our apologies to the reader here, but weve gotten a little out of |
| order in describing things. The |
| <img src="images/tag_1.gif" align=CENTER width="24" height="13"> started method is called by our debug |
| event handler right after the interpreter starts. But at this point in the |
| paper, we have not described events, so youll just have to trust us on this |
| one.)</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1">Package:</font> |
| <font color="blue">pda.launching</font>, <font size="-1">Class:</font> <font color="#0000FF">PDADebugTarget</font></u> |
| <pre><img src="images/tag_1.gif" align=CENTER width="24" height="13"> private void started() { |
| fireCreationEvent(); |
| <img src="images/tag_2.gif" align=CENTER width="24" height="13"> installDeferredBreakpoints(); |
| try { |
| <img src="images/tag_3.gif" align=CENTER width="24" height="13"> resume(); |
| } catch( DebugException x ) { |
| } |
| } |
| private void installDeferredBreakpoints() { |
| <font color="#0000FF"> </font> IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager() |
| <font color="#0000FF"> </font> .getBreakpoints(IPDAConstants.ID_PDA_DEBUG_MODEL); |
| <font color="#0000FF"> </font> for (int i = 0; i < breakpoints.length; i++) { |
| <font color="#0000FF"> </font> breakpointAdded(breakpoints[i]); |
| <font color="#0000FF"> </font> } |
| <font color="#0000FF"> </font> }</pre> |
| <p>Heres a sequence diagram showing how the debug target, the interpreter, and |
| the events interact:</p> |
| <p align="center"><img border="0" src="images/synchronize_w_interpreter.gif" width="432" height="390"></p> |
| <p>Note that because the interpreter events are delivered over a TCP socket to |
| the PDADebugTarget, even if the interpreter starts up and sends the "started" |
| event before the PDATarget is instantiated, the "started" event will |
| be queued in the socket. The potential race condition between the interpreter |
| and debug target is eliminated by having the interpreter suspend on startup.</p> |
| <h2>The debug events</h2> |
| <p>The Eclipse debug model uses debug events (DebugEvent) to describe events that |
| occur as a program is being debugged. Each element in the debug model has a |
| specific set of events that is supports all of this is documented in the Javadoc |
| of DebugEvent and in the <i><cite>Platform Plug-in Developer Guide</cite></i>.</p> |
| <h3>Code highlights of our debug events</h3> |
| <p>As described <a href="#Interpreter Debug Interface">previously</a>, our example |
| PDA interpreter uses two sockets to communicate with its debugger: a command |
| channel and an event channel. Our debugger uses synchronous RPC over the command |
| channel (send command, block, receive reply), but the events can arrive asynchronously |
| so the debugger must use a separate thread to listen on the event channel.</p> |
| <p>We create the separate event listener thread when we create the debug target |
| (see previous). The main routine of the thread is a dispatch loop that reads |
| from the event channel and dispatches to the appropriate event behavior:</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1">Package:</font> |
| <font color="blue"> pda.model</font>, <font size="-1">Class:</font> <font color="#0000FF">PDADebugTarget.EventDispatchJob</font></u> |
| <pre> <font color="#0000FF"> </font> protected IStatus run(IProgressMonitor monitor) { |
| <font color="#0000FF"> </font> String event = ""; |
| <font color="#0000FF"> </font> while (!isTerminated() && event != null) { |
| <font color="#0000FF"> </font> try { |
| <font color="#0000FF"> </font> event = fEventReader.readLine(); |
| <font color="#0000FF"> </font> if (event != null) { |
| <font color="#0000FF"> </font> fThread.setBreakpoints(null); |
| <font color="#0000FF"> </font> fThread.setStepping(false); |
| <font color="#0000FF"> </font>
dispatch to the event behavior method
|
| <font color="#0000FF"> </font> } |
| <font color="#0000FF"> </font> } catch (IOException e) { |
| <font color="#0000FF"> </font> terminated(); |
| <font color="#0000FF"> </font> } |
| <font color="#0000FF"> </font> } |
| <font color="#0000FF"> </font> return Status.OK_STATUS; |
| <font color="#0000FF"> </font> }</pre> |
| <p>Our interpreter sends six kinds of events (see <a href="#Interpreter Debug Interface">previous</a>), |
| but we only handle four kinds with five subtypes. We simply (and safely) ignore |
| the events that we do not yet handle.</p> |
| <pre> if (event.equals("started")) { |
| started(); |
| } else if (event.equals("terminated")) { |
| terminated(); |
| } else if (event.startsWith("resumed")) { |
| if (event.endsWith("step")) { |
| fThread.setStepping(true); |
| resumed(DebugEvent.STEP_OVER); |
| } else if (event.endsWith("client")) { |
| resumed(DebugEvent.CLIENT_REQUEST); |
| } |
| } else if (event.startsWith("suspended")) { |
| if (event.endsWith("client")) { |
| suspended(DebugEvent.CLIENT_REQUEST); |
| } else if (event.endsWith("step")) { |
| suspended(DebugEvent.STEP_END); |
| } else if (event.indexOf("breakpoint") >= 0) { |
| breakpointHit(event); |
| } |
| }</pre> |
| <p>The event handlers are also simple: they turn around and fire the appropriate |
| DebugEvent to all the listeners. As implementers of the debug model, we dont |
| have to know who those listeners are, but curiosity gets the better of us and we |
| want to know. The answer is the debug views. The debug views listen for events |
| and use those events to update the user interface to show the current state of |
| the debugged program.</p> |
| <p>In the debug target and the debug element:</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1">Package:</font> |
| <font color="blue"> pda.model</font>, <font size="-1">Class:</font> <font color="#0000FF">PDADebugTarget</font></u> |
| <pre> <font color="#0000FF"> </font> private void resumed(int detail) { |
| <font color="#0000FF"> </font> fSuspended = false; |
| <font color="#0000FF"> </font> fThread.fireResumeEvent(detail); |
| <font color="#0000FF"> </font> }</pre> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1">Package:</font> |
| <font color="blue"> pda.model</font>, <font size="-1">Class:</font> <font color="#0000FF">PDADebugElement</font></u> |
| <pre> public void fireResumeEvent(int detail) { |
| fireEvent(new DebugEvent(this, DebugEvent.RESUME, detail)); |
| }</pre> |
| <p>Handling the suspended-due-to-breakpoint event in the debug target is a |
| little more involved because the IThread object keeps track of which breakpoint |
| was hit. The IThread object keeps track of the breakpoint that caused the |
| suspension in case the user interface wants to use that information in labels or |
| icons or some other view. To gather this information, we |
| <img src="images/tag_1.gif" align=CENTER width="24" height="13"> extract the breakpoint number from the |
| PDA interpreter event message, |
| <img src="images/tag_2.gif" align=CENTER width="24" height="13"> find the corresponding breakpoint |
| object, and then |
| <img src="images/tag_3.gif" align=CENTER width="24" height="13"> annotate the current thread object |
| with that breakpoint.</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1">Package:</font> |
| <font color="blue"> pda.model</font>, <font size="-1">Class:</font> <font color="#0000FF">PDADebugTarget</font></u> |
| <pre> private void breakpointHit(String event) { |
| int lastSpace = event.lastIndexOf(' '); |
| if (lastSpace > 0) { |
| String line = event.substring(lastSpace + 1); |
| <img src="images/tag_1.gif" align=CENTER width="24" height="13"> int lineNumber = Integer.parseInt(line); |
| <img src="images/tag_2.gif" align=CENTER width="24" height="13"> IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager() |
| <font color="#0000FF">.</font> .getBreakpoints(IPDAConstants.ID_PDA_DEBUG_MODEL); |
| <font color="#0000FF">.</font> for (int i = 0; i < breakpoints.length; i++) { |
| <font color="#0000FF">.</font> IBreakpoint breakpoint = breakpoints[i]; |
| <font color="#0000FF">.</font> if (supportsBreakpoint(breakpoint)) { |
| <font color="#0000FF">.</font> if (breakpoint instanceof ILineBreakpoint) { |
| <font color="#0000FF">.</font> ILineBreakpoint lineBreakpoint = (ILineBreakpoint) breakpoint; |
| <font color="#0000FF">.</font> try { |
| <font color="#0000FF">.</font> if (lineBreakpoint.getLineNumber() == lineNumber) { |
| <img src="images/tag_3.gif" align=CENTER width="24" height="13"> fThread.setBreakpoints(new IBreakpoint[]{breakpoint}); |
| break; |
| } |
| } catch (CoreException e) { |
| } |
| } |
| } |
| } |
| } |
| suspended(DebugEvent.BREAKPOINT); |
| }</pre> |
| <h3>Pretty pictures of how our debug events work</h3> |
| <p>Here are two sequences showing how the events arrive asynchronously and are |
| processed by the Eclipse debug framework. First, here is how a step action from |
| the user interface gets sent to the interpreter as a step command on the command |
| channel. The interpreter operates asynchronously, sending events (resume and |
| then suspend) back to the debugger by way of the event channel.</p> |
| <p align="center"><img border="0" src="images/single_step.gif" width="444" height="435"></p> |
| <p>Very similarly, here is the sequence for a resume action, followed by the |
| interpreter running for a while and then hitting a breakpoint:</p> |
| <p align="center"><img border="0" src="images/run_until_breakpoint.gif" width="467" height="435"></p> |
| <p>The astute reader will notice that the sequence diagram is exactly the same |
| (except for the slightly different command and different subevents). This regularity |
| to the structure and behavior is a deliberate design decision in the Eclipse |
| debug framework.</p> |
| <h2>Source lookup</h2> |
| <p>Once our debug model and event handler are in place, the Eclipse debugger |
| will work with our interpreter, but its user interface will be quite generic and |
| uninteresting. For example, the generic debugger does not show or highlight |
| source code. </p> |
| <p>Highlighting the current source code line or statement is de rigueur in |
| modern debuggers, so our next step is to enable what is known as source code |
| lookup in our debugger. To do so, we add three new pieces:</p> |
| <ol> |
| <li>Our launch object needs a source locator.</li> |
| <li>The source locator object translates stack frames into source elements. In |
| our case, the source elements are IFile objects.</li> |
| <li>Finally, our debug model presentation object maps the source elements (IFiles) |
| to editor ids and inputs. Our example uses the default Eclipse text editor.</li> |
| </ol> |
| <p>To see how this works, lets start by examining the Eclipse framework for |
| displaying source code of stack frames.</p> |
| <h3>The most general case</h3> |
| <p>In the most general case, the launch object has a source locator object. When |
| the source code for a stack frame needs to be displayed, the stack frame is |
| passed to the source locator method to retrieve the source element. The stack |
| frames model identifier is used to find the debug model presentation object |
| (by way of the corresponding extension). The debug model presentation maps the |
| source element to an editor id and editor input, which are then used by the |
| workbench to open an editor to display the source code.</p> |
| <p align="center"><img border="0" src="images/scl_most_general_case.gif" width="620" height="283"></p> |
| <h3>Extension defined source locator</h3> |
| <p>If the launch object does not have a source locator object (that is, a source |
| locator is not assigned by the launch delegate), the debug framework uses the |
| sourceLocatorId attribute of the launchConfigurationType extension to instantiate |
| an ISourceLocator object and store it in the launch object. Thereafter, the |
| rest of the code works as previously described.</p> |
| <p align="center"><img border="0" src="images/scl_extension_defined.gif" width="576" height="67"></p> |
| <h3>Standard kind of source locator</h3> |
| <p>If your source lookup mechanism is standard, that is, files in directories, |
| you can use the Eclipse debug frameworks standard source lookup. The ISourceLookupDirector |
| mechanism looks up source files in directories (and zips and jars) along a path |
| (known as the source lookup path). The framework has three pieces:</p> |
| <ul> |
| <li>participants (ISourceLookupParticipant) objects that map an IStackFrame |
| into a filename</li> |
| <li>containers (ISourceContainer) objects that find files by filename in directories, |
| zips, jars, etc.</li> |
| <li>source lookup tab (SourceContainerLookupTab) an ILaunchConfigurationTab |
| that provides a user interface for configuring and modifying a source lookup |
| path.</li> |
| </ul> |
| <p>The ISourceLookupDirector is an ISourceLocator, so once you have implemented |
| an ISourceLookupDirector, you place the class name in the sourceLocator |
| extension.</p> |
| <p align="center"><img border="0" src="images/scl_standard_kind.gif" width="599" height="259"></p> |
| <h3>Standard implementation of standard kind of source locator</h3> |
| <p>Although ISourceLookupDirector is available as an interface for |
| reimplementation, most debuggers use the default implementation provided by the |
| Eclipse debug framework. The AbstractSourceLookupDirector provides the algorithm |
| for looking source files up along a source path. The only unimplemented method |
| is initializeParticipants, which creates the collection of |
| ISourceLookupParticipants for mapping the stack frames to filenames.</p> |
| <p align="center"><img border="0" src="images/scl_standard_implementation.gif" width="352" height="352"> </p> |
| <h3>Default source path in the standard implementation</h3> |
| <p>The AbstractSourceLookupDirector has one more trick up its sleeve: if the user |
| has not specified the source path (that is, the source containers) either programmatically |
| or by way of the SourceContainerLookupTab, the AbstractSourceLookupDirector |
| will compute the default source path. Heres how it works:</p> |
| <p>When initialized, the AbstractSourceLookupDirector has a default source path |
| of a single container: a DefaultSourceContainer. The DefaultSourceContainer uses |
| an ISourcePathComputer (which in turn uses an ISourcePathComputerDelegate) to |
| compute the source path. The source path computer is reused to recompute the |
| default source path until such time as the source containers in the source |
| lookup director are explicitly set to something other than the default.</p> |
| <p>The source path computer is defined for each launch configuration type using |
| the sourcePathComputer extension. The source path computer delegate has one |
| method: computeSourceContainers.</p> |
| <p align="center"><img border="0" src="images/scl_default_source_path.gif" width="477" height="277"></p> |
| <h3>Code highlights of our simple solution to source code lookup</h3> |
| <p>The simplest solution to source code lookup is to reuse that wonderful |
| framework the Eclipse debug framework provides. Being good developers, we seek |
| to reuse rather than reinvent, so our example uses an |
| AbstractSourceLookupDirector to lookup source files, and a source path computer |
| to compute the default source lookup path. </p> |
| <blockquote><i>Notation note: </i> |
| The six steps below are strongly interconnected and thus the code references |
| <img src="images/tag_1.gif" align=CENTER width="24" height="13"> |
| <img src="images/tag_2.gif" align=CENTER width="24" height="13"> |
| <img src="images/tag_3.gif" align=CENTER width="24" height="13"> |
| <img src="images/tag_4.gif" align=CENTER width="24" height="13"> |
| <img src="images/tag_5.gif" align=CENTER width="24" height="13"> ... are uniform across the six |
| steps. In other words, the |
| <img src="images/tag_1.gif" align=CENTER width="24" height="13"> in step 1 is the same as the |
| <img src="images/tag_1.gif" align=CENTER width="24" height="13"> in step 3.</blockquote> |
| <h4>Step 1. source locator</h4> |
| <p>For additional simplicity, we chose to use the extension defined source |
| locator described above rather than having our launch delegate create and |
| assign the source locator to the launch object. Thus we start by adding |
| <img src="images/tag_1.gif" align=CENTER width="24" height="13"> a source locator id to our launch |
| configuration type. Then we added an extension for our source locator id pointing to |
| <img src="images/tag_2.gif" align=CENTER width="24" height="13"> our source lookup director class.</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1"> |
| Extension:</font> <font color="blue"> org.eclipse.debug.core.launchConfigurationTypes</font></u> |
| <pre> <launchConfigurationType |
|
other attributes
|
| <img src="images/tag_1.gif" align=CENTER width="24" height="13"> sourceLocatorId="org.eclipse.debug.examples.core.sourceLookupDirector.pda" |
| </launchConfigurationType></pre> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1"> |
| Extension: </font></u><img src="images/tag_1.gif" align=center width="24" height="13"><u> |
| <font color="blue">org.eclipse.debug.core.sourceLocators</font></u> |
| <pre> <sourceLocator |
| name="PDA Source Lookup Director" |
| <img src="images/tag_2.gif" align=CENTER width="24" height="13"> class="org.eclipse.debug.examples.core.pda.launching.PDASourceLookupDirector" |
| <img src="images/tag_1.gif" align=center width="24" height="13"> id="org.eclipse.debug.examples.core.sourceLookupDirector.pda"> |
| </sourceLocator></pre> |
| <h4>Step 2. source lookup director</h4> |
| <p>The abstract class does most of the work, but our PDASourceLocatorDirector |
| subclass of AbstractSourceLookupDirector still has to initialize |
| <img src="images/tag_3.gif" align=CENTER width="24" height="13"> the set of participants. In our case, |
| the set of participants is the singleton PDASourceLookupParticipant. Our |
| participant is trivial because in our debug model, |
| <img src="images/tag_4.gif" align=CENTER width="24" height="13"> the stack frames keep track of their |
| source filenames.</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1">Package:</font> |
| <font color="blue"> pda.launching</font>, <font size="-1">Class: </font></u> |
| <img src="images/tag_2.gif" align=center width="24" height="13"><u> <font color="#0000FF">PDASourceLocatorDirector</font></u> |
| <pre> public void initializeParticipants() { |
| <img src="images/tag_3.gif" align=CENTER width="24" height="13"> addParticipants(new ISourceLookupParticipant[]{new PDASourceLookupParticipant()}); |
| }</pre> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1">Package:</font> |
| <font color="blue"> pda.launching</font>, <font size="-1">Class: </font></u> |
| <img src="images/tag_3.gif" align=center width="24" height="13"><u> <font color="#0000FF">PDASourceLocatorParticipant</font></u> |
| <pre> public String getSourceName(Object object) throws CoreException { |
| if (object instanceof PDAStackFrame) { |
| <img src="images/tag_4.gif" align=CENTER width="24" height="13"> return ((PDAStackFrame)object).getSourceName(); |
| } |
| return null; |
| }</pre> |
| <p>And thats all it takes to configure, instantiate, and use the frameworks |
| source path lookup director.</p> |
| <h4>Step 3. source path computer extension</h4> |
| <p>Next we have to deal with the default source lookup path issue. Again, being |
| efficient, we use the framework provided source path computer extension as |
| explained above. We again |
| <img src="images/tag_5.gif" align=CENTER width="24" height="13"> add to our launch configuration |
| followed by augmenting the corresponding extension to point to our source path |
| computer class.</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1"> |
| Extension:</font> <font color="blue"> org.eclipse.debug.core.launchConfigurationTypes</font></u> |
| <pre> <launchConfigurationType |
|
other attributes
|
| <img src="images/tag_1.gif" align=CENTER width="24" height="13"> sourceLocatorId="org.eclipse.debug.examples.core.sourceLookupDirector.pda" |
| <img src="images/tag_5.gif" align=CENTER width="24" height="13"> sourcePathComputerId="org.eclipse.debug.examples.core.sourcePathComputer.pda" |
| </launchConfigurationType></pre> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1"> |
| Extension: </font></u> <img src="images/tag_5.gif" align=center width="24" height="13"><u> |
| <font color="blue">org.eclipse.debug.core.sourcePathComputers</font></u> |
| <pre> <sourcePathComputer |
| <img src="images/tag_6.gif" align=CENTER width="24" height="13"> class="org.eclipse.debug.examples.core.pda.launching.PDASourcePathComputerDelegate" |
| <img src="images/tag_5.gif" align=center width="24" height="13"> id="org.eclipse.debug.examples.core.sourcePathComputer.pda"> |
| </sourcePathComputer></pre> |
| <h4>Step 4. source path computer implementation</h4> |
| <p>Our source path computer returns a one-element source path where the one |
| element is the ISourceContainer that contains the source file. This is the |
| longest method we have to write to get source lookup to work.</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1">Package:</font> |
| <font color="blue"> pda.launching</font>, <font size="-1">Class: </font></u> |
| <img src="images/tag_6.gif" align=center width="24" height="13"><u> <font color="#0000FF">PDASourcePathComputerDelegate</font></u> |
| <pre> public ISourceContainer[] computeSourceContainers( |
| ILaunchConfiguration configuration, |
| IProgressMonitor monitor) throws CoreException { |
| String path = configuration.getAttribute( |
| IPDAConstants.ATTR_PDA_PROGRAM, (String)null); |
| ISourceContainer sourceContainer = null; |
| if (path != null) { |
| IResource resource = ResourcesPlugin.getWorkspace().getRoot() |
| .findMember(new Path(path)); |
| if (resource != null) { |
| IContainer container = resource.getParent(); |
| if (container.getType() == IResource.PROJECT) { |
| sourceContainer = new ProjectSourceContainer( |
| (IProject)container, false); |
| } else if (container.getType() == IResource.FOLDER) { |
| sourceContainer = new FolderSourceContainer( |
| container, false); |
| } |
| } |
| } |
| if (sourceContainer == null) { |
| sourceContainer = new WorkspaceSourceContainer(); |
| } |
| return new ISourceContainer[]{sourceContainer}; |
| }</pre> |
| <h4>Step 5. debug model presentation</h4> |
| <p>As a penultimate step, we add the debugModelPresentation extension that will |
| map our source element (an IFile) to an editor for the workbench to display.</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="#0000FF">ui</font>, <font size="-1"> |
| Extension:</font> <font color="blue"> org.eclipse.debug.ui.debugModelPresentations</font></u> |
| <pre> <debugModelPresentation |
| <img src="images/tag_7.gif" align=CENTER width="24" height="13"> class="org.eclipse.debug.examples.ui.pda.launching.PDAModelPresentation" |
| <img src="images/tag_8.gif" align=center width="24" height="13"> id="org.eclipse.debug.examples.pda"> |
| </debugModelPresentation></pre> |
| <p>The debug model is linked to the debug model presentation in the usual way |
| that Eclipse user interfaces are linked to Eclipse models: through the extension |
| id. For the debugger, the IDebugElement knows which debug model it is a part of.</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="blue">core</font>, <font size="-1">Package:</font> |
| <font color="blue"> pda.model</font>, <font size="-1">Class:</font> <font color="#0000FF"> |
| PDADebugElement</font></u> |
| <pre> public String getModelIdentifier() { |
| <img src="images/tag_8.gif" align=center width="24" height="13"> return IPDAConstants.ID_PDA_DEBUG_MODEL; |
| }</pre> |
| <p>The model presentation class has many methods, but for now the only two |
| interesting methods are those that translate source elements into editor ids and |
| inputs.</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="#0000FF">ui</font>, <font size="-1">Package:</font> |
| <font color="blue"> pda.model</font>, <font size="-1">Class: </font></u> <img src="images/tag_7.gif" align=center width="24" height="13"><u> |
| <font color="#0000FF">PDAModelPresentation</font></u> |
| <pre> public IEditorInput getEditorInput(Object element) { |
| if (element instanceof IFile) |
| return new FileEditorInput((IFile)element); |
| if (element instanceof ILineBreakpoint) |
| return new FileEditorInput((IFile)((ILineBreakpoint)element).getMarker().getResource()); |
| return null; |
| } |
| public String getEditorId(IEditorInput input, Object element) { |
| if (element instanceof IFile || element instanceof ILineBreakpoint) |
| return "org.eclipse.ui.DefaultTextEditor"; |
| return null; |
| }</pre> |
| <h4>Step 6. source path tab</h4> |
| <p>The final step is to add the source path tab to our launch configuration dialog |
| box. This is <img src="images/tag_9.gif" align=center width="24" height="13"> |
| a simple one-line addition to our existing tab group class (you will recall |
| that we had created the tab group class as part of our original launching code |
| back when we read the first article of this series)</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="#0000FF">ui</font>, <font size="-1">Package:</font> |
| <font color="blue"> pda.launching</font>, <font size="-1">Class: </font><font color="#0000FF"> |
| PDATabGroup</font></u> |
| <pre> public void createTabs(ILaunchConfigurationDialog dialog, String mode) { |
| setTabs(new ILaunchConfigurationTab[] { |
| new PDAMainTab(), |
| <img src="images/tag_9.gif" align=center width="24" height="13"> new SourceContainerLookupTab(), |
| new CommonTab() |
| }); |
| }</pre> |
| <h2><a name="Breakpoints">Breakpoints</a></h2> |
| <p>Breakpoints are the final major component of a modern debugger that our debugger |
| is still missing. The Eclipse debug framework support for breakpoints is documented |
| in the <cite><i>Platform Plug-in Developer Guide</i></cite> under <b>Programmers |
| Guide > Program debug and launch support> Debugging a program> Breakpoints</b>. |
| Our implementation follows that design.</p> |
| <p>Because our assembly language is line-oriented, our breakpoints are line-oriented, |
| that is, breakpoints are associated with lines and there can be at most one |
| breakpoint per line. Other more complex languages have statement or expression-oriented |
| breakpoints and multiple statements or expressions on a single line. We have |
| left discussion of those to the third paper of this series <i> Enhancing the |
| Eclipse Debugger</i>.</p> |
| <blockquote><i>Notation note: </i> We use the same "code references are uniform |
| across the three steps" in this section that we used in the previous section.</blockquote> |
| <h4>Step 1. breakpoint objects</h4> |
| <p>The first step to implementing our line-oriented breakpoints is to define our |
| breakpoint data structure using the breakpoint and the resource marker. As |
| described in the Platform Plug-in Developer Guide, the resource marker is the |
| mechanism by which the breakpoint is persisted between Eclipse sessions. We |
| conveniently reuse code by |
| <img src="images/tag_1.gif" align=CENTER width="24" height="13"> creating the marker for our line-oriented breakpoints |
| as a subtype of the Eclipse framework line-oriented breakpoint marker. Our |
| marker inherits all the attributes of the Eclipse marker and we do not need, |
| thus we do not define, any additional attributes.</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="#0000FF">core</font>, <font size="-1"> |
| Extension:</font> <font color="blue"> org.eclipse.debug.core.breakpoints</font></u> |
| <pre> <breakpoint |
| <img src="images/tag_1.gif" align=center width="24" height="13"> markerType="org.eclipse.debug.examples.core.pda.lineBreakpoint.marker" |
| <img src="images/tag_2.gif" align=CENTER width="24" height="13"> class="org.eclipse.debug.examples.core.pda.model.PDALineBreakpoint" |
| id="org.eclipse.debug.examples.core.pda.lineBreakpoint"> |
| </breakpoint></pre> |
| <p><u> <font size="-1">Plug-in:</font> <font color="#0000FF">core</font></u> |
| <pre> <extension |
| <img src="images/tag_1.gif" align=CENTER width="24" height="13"> id="pda.lineBreakpoint.marker" |
| point="org.eclipse.core.resources.markers"> |
| <super type="org.eclipse.debug.core.lineBreakpointMarker"/> |
| <persistent value="true"/> |
| </extension></pre> |
| <p>Note that the org.eclipse.core.resources.markers extension id is "pda.lineBreakpoint.marker" |
| rather than "org.eclipse.debug.examples.core.pda.lineBreakpoint.marker" because |
| the id is automatically prefixed with plug-in id, i.e., "org.eclipse.debug.examples.core".</p> |
| <h4>Step 2. toggle breakpoint menu item</h4> |
| <p>The toggle breakpoint menu item in the Eclipse debug perspective uses a re-targetable |
| action provided by the debug platform to toggle the breakpoints using the appropriate |
| debug model. The retargetable action asks the active part (editor, view, and |
| so on), for its toggle breakpoint adapter. When available, the adapter is used |
| as a delegate to toggle breakpoints. We use plugin.xml to register the factory |
| for the particular editor we are using, then we define <img src="images/tag_3.gif" align=CENTER width="24" height="13"> |
| the factory and <img src="images/tag_4.gif" align=center width="24" height="13"> |
| the adapter. The adapter has to be an IToggleBreakpointsTarget, but other than |
| that, this is standard Eclipse platform code.</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="#0000FF">ui</font>, <font size="-1"> |
| Extension:</font> <font color="blue"> org.eclipse.core.runtime.adapters</font></u> |
| <pre> <factory |
| <img src="images/tag_3.gif" align=CENTER width="24" height="13"> class="org.eclipse.debug.examples.ui.pda.model.PDABreakpointAdapterFactory" |
| adaptableType="org.eclipse.ui.texteditor.ITextEditor"> |
| <adapter type="org.eclipse.debug.ui.actions.IToggleBreakpointsTarget"/> |
| </factory></pre> |
| <p><u> <font size="-1">Plug-in:</font> <font color="#0000FF">ui</font>, <font size="-1">Package:</font> |
| <font color="blue"> pda.model</font>, <font size="-1">Class: </font></u> <img src="images/tag_3.gif" align=center width="24" height="13"><u><font size="-1"> |
| </font><font color="#0000FF"> PDABreakpointAdapterFactory</font></u> |
| <pre> public Object getAdapter(Object adaptableObject, Class adapterType) { |
| if (adaptableObject instanceof ITextEditor) { |
| ITextEditor editorPart = (ITextEditor) adaptableObject; |
| IResource resource = (IResource) editorPart.getEditorInput().getAdapter(IResource.class); |
| if (resource != null) { |
| String extension = resource.getFileExtension(); |
| if (extension != null && extension.equals("pda")) { |
| <img src="images/tag_4.gif" align=center width="24" height="13"> return new PDALineBreakpointAdapter(); |
| } |
| } |
| } |
| return null; |
| }</pre> |
| <p>Note that we use the plugin.xml to register the adapter rather than |
| programmatically registering the adapter. Using plugin.xml allows our adapter |
| (and thus our toggle breakpoints) to be active before our lazily loaded plug-in |
| |
| is active.</p> |
| <p>Also note that this isnt a completely elegant solution because we are |
| registering an adapter factory for the text editor. Which means that if other |
| plug-ins register a toggle breakpoints adapter for the text editor, one of the |
| plug-ins will work and the others will not, but there is no way to know which |
| one will be "the winner". What we, |
| as language IDE developers, should really do is supply our own source code |
| editor and then adapt that editor rather than the generic text editor. However, |
| source code editing is out of the scope of this article and thus we use the |
| simple text editor for demonstrative purposes.</p> |
| <h4>Step 3. toggle breakpoint action</h4> |
| <p>Toggling a breakpoint is as expected: look for an existing breakpoint on the |
| line; |
| <img src="images/tag_5.gif" align=center width="24" height="13"> if there is a breakpoint, remove it; |
| <img src="images/tag_6.gif" align=center width="24" height="13"> if there is not a breakpoint, create |
| one (of the class we |
| <img src="images/tag_2.gif" align=CENTER width="24" height="13"> registered in plugin.xml).</p> |
| <p><u> <font size="-1">Plug-in:</font> <font color="#0000FF">ui</font>, <font size="-1">Package:</font> |
| <font color="blue"> pda.model</font>, <font size="-1">Class: </font></u> <img src="images/tag_4.gif" align=center width="24" height="13"><u><font size="-1"> |
| </font><font color="#0000FF"> PDALineBreakpointAdapter</font></u> |
| <pre> public void toggleLineBreakpoints(IWorkbenchPart part, ISelection selection) |
| throws CoreException { |
| ITextEditor textEditor = getEditor(part); |
| if (textEditor != null) { |
| IResource resource = (IResource) textEditor.getEditorInput() |
| .getAdapter(IResource.class); |
| ITextSelection textSelection = (ITextSelection) selection; |
| int lineNumber = textSelection.getStartLine(); |
| IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager() |
| .getBreakpoints(IPDAConstants.ID_PDA_DEBUG_MODEL); |
| for (int i = 0; i < breakpoints.length; i++) { |
| IBreakpoint breakpoint = breakpoints[i]; |
| if (resource.equals(breakpoint.getMarker().getResource())) { |
| if (((ILineBreakpoint)breakpoint).getLineNumber() == (lineNumber + 1)) { |
| <img src="images/tag_5.gif" align=center width="24" height="13"> breakpoint.delete(); |
| return; |
| } |
| } |
| } |
| <img src="images/tag_2.gif" align=CENTER width="24" height="13"><img src="images/tag_6.gif" align=center width="24" height="13"> PDALineBreakpoint lineBreakpoint = new PDALineBreakpoint(resource, lineNumber + 1); |
| DebugPlugin.getDefault().getBreakpointManager().addBreakpoint(lineBreakpoint); |
| } |
| }</pre> |
| <p>The only mildly interesting twist in our code is that our breakpoints store |
| the user view of line numbers (the first line of a file is line 1) rather than |
| the implementation view of line numbers (the first line of the file is line 0). |
| But you, our dear reader, have vast experience dealing with off-by-one errors |
| and have already figured this out.</p> |
| <p>The generic Eclipse text editor does not support double clicking in the ruler |
| to create breakpoints the way the more specialized source code editors do, but |
| again that is a feature of the way we chose to implement (or not implement) |
| our source code editor rather than a feature of the Eclipse debug frameworks |
| support for breakpoints.</p> |
| <h2>Summary and further information</h2> |
| <p>The Eclipse platform provides a very powerful framework that enables developers |
| to add support for a new language quickly and easily. Having read this far, |
| you now realize that adding debug support for a new language is a relatively |
| simple task. There are a small number of classes to implement (eighteen in our |
| example), and an even smaller number of extensions to specify (ten in our example). |
| For more details on any aspect of the framework, we refer you to the API documentation |
| of the Debug Platform and, for a very complete example, of the Java Debugger.</p> |
| <p>Our journey, however, is not ended yet. The launch and debug facilities we |
| have described and implemented in these first two papers are useful, but like |
| a light read, will ultimately not be satisfying to most language implementers. |
| We have described launching, debug target control (step/resume/suspend), basic |
| debug element display, source lookup, and breakpoints. Most language implementers |
| will also want watch points, more descriptive labels, informative icons, custom |
| views and language specific actions. For details on these enhancements to the |
| debug support, we refer you to the third paper of this trilogy, <i>Enhancing |
| the Eclipse Debugger</i>, which is still under construction.</p> |
| <h2>Acknowledgements</h2> |
| <p>Thanks to Darin Swanson and Cameron Stevens for their careful reviews and |
| critiques that have improved this paper.</p> |
| <h2>Source code</h2> |
| <p><img src="images/tryit.gif" width="61" height="13"> To examine the entire source |
| code, or to run the example, download the <a href="pda-plugins.zip">zip file</a> |
| and unzip it into your Eclipse directory. Both plug-ins come with complete source |
| code, although you will need your own installation of Perl to run the PDA interpreter. |
| This article describes, and the included code requires, features that are available |
| in Eclipse 3.0 or a later version.</p> |
| <p><b>Trademarks</b></p> |
| <p>IBM and Rational are registered trademarks of International Business Machines |
| Corporation in the United States, other countries, or both.</p> |
| <p>Linux is a trademark of Linus Torvalds in the United States, other countries, |
| or both.</p> |
| <p>Microsoft and Windows are trademarks of Microsoft Corporation in the United |
| States, other countries, or both.</p> |
| <p>Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. |
| in the United States, other countries, or both.</p> |
| <p>Other company, product, and service names may be trademarks or service marks |
| of others.</p> |
| <p></p> |
| </body> |
| </html> |