diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/pda/breakpoints_1.dia b/plugins/org.eclipse.dd.doc.dsf/docs/pda/breakpoints_1.dia new file mode 100644 index 00000000000..e07bbeef391 Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/pda/breakpoints_1.dia differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/pda/breakpoints_1.png b/plugins/org.eclipse.dd.doc.dsf/docs/pda/breakpoints_1.png new file mode 100644 index 00000000000..90a94ebb2a9 Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/pda/breakpoints_1.png differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/pda/connecting_1.dia b/plugins/org.eclipse.dd.doc.dsf/docs/pda/connecting_1.dia new file mode 100644 index 00000000000..a1fb4d2376c Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/pda/connecting_1.dia differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/pda/connecting_1.png b/plugins/org.eclipse.dd.doc.dsf/docs/pda/connecting_1.png new file mode 100644 index 00000000000..b07c13701a9 Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/pda/connecting_1.png differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/pda/launch_1.png b/plugins/org.eclipse.dd.doc.dsf/docs/pda/launch_1.png new file mode 100644 index 00000000000..acf5dd0d8e6 Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/pda/launch_1.png differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/pda/launch_2.dia b/plugins/org.eclipse.dd.doc.dsf/docs/pda/launch_2.dia new file mode 100644 index 00000000000..f0d4b4876b2 Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/pda/launch_2.dia differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/pda/pda.gif b/plugins/org.eclipse.dd.doc.dsf/docs/pda/pda.gif new file mode 100644 index 00000000000..9a6adbfc87e Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/pda/pda.gif differ diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/pda/pda_tutorial_outline.html b/plugins/org.eclipse.dd.doc.dsf/docs/pda/pda_tutorial_outline.html index d85019cd9b3..ff01755a0c8 100644 --- a/plugins/org.eclipse.dd.doc.dsf/docs/pda/pda_tutorial_outline.html +++ b/plugins/org.eclipse.dd.doc.dsf/docs/pda/pda_tutorial_outline.html @@ -4,15 +4,213 @@ How to write a DSF-based debugger -

How to write a DSF-based debugger
-

+

How to write a DSF-based debugger
+

Summary

DSF

-

Debug Services

+

Push Down Automata (PDA)
+

+The Push Down Automata (PDA) debugger example is used as the basis for +this tutorial.  Before starting the tutorial it is best to +familiarize with the features of the debugger.
+

Running the Example

+Launch the PDA debugger with these twelve "easy" steps:
+
    +
  1. Download and install Eclipse development environment, either the Eclipse Classic 3.4 or Eclipse IDE for C/C++ Developers
    +
  2. +
  3. Install the DSF SDK feature to build against, by performing +either:
    +
  4. +
      +
    1. Using update manager, install the Debugger Services Framework end-user and +extender SDK, found in the Ganymede +Discovery Site under Remote +Access and Device Development.
    2. +
    3. Check out org.eclipse.dd.dsf +and org.eclipse.dd.dsf.ui plugins, found in the /cvsroot/dsdp repository under org.eclipse.dd.dsf/plugins +directory.
    4. +
    +
  5. Check out the org.eclipse.dd.examples.pda +and org.eclipse.dd.examples.pda.ui plugins, +found /cvsroot/dsdp in the org.eclipse.dd.dsf/plugins +directory.
  6. +
  7. Build the PDA plugins.
  8. +
  9. Create an instance of an Eclipse +Application launch configuration and launch it in Debug mode.
    +
  10. +
  11. Switch to the new Eclipse IDE Application
  12. +
  13. Create a new empty project:
    +
  14. +
      +
    1. Select the File->New->Project... +action
    2. +
    3. Select General->Project +in the New Project dialog.
    4. +
    5. Enter a name for the new project (e.g. "PDA")
    6. +
    +
  15. Link in the folder with PDA examples from the org.eclipse.dd.examples.pda plugin.
  16. +
      +
    1. Right-click on the new Project and select New->Folder
      +
    2. +
    3. Click on the Advanced +>> button at the bottom of the New Folder dialog.
    4. +
    5. Select the Link to folder in +the filesystem check box.
    6. +
    7. Type in or browse to the samples +directory found in the org.eclipse.dd.examples.pda +plugin.
    8. +
    +
  17. Open the PDA editor by double-clicking on the PDA file (e.g. fibonacci.pda).  See note +below.
    +
  18. +
  19. Set a breakpoint in the program by double-clicking in the editor +gutter.
  20. +
  21. Launch the PDA debugger
  22. +
      +
    1. Set the dsfPerlDebugger +variable to point to the Perl executable in your system.  +Variables can be set in the Windows->Preferences->Run/Debug->String +Substitution preference page.
    2. +
    3. Create a new launch configuration of type DSF PDA Application
    4. +
    5. In the Main tab, enter the workspace path to the program name +(e.g. /PDA/samples/fibonacci.pda).
    6. +
    7. Select Debug to +launch the PDA debugger.
    8. +
    +
  23. Step, select stack frames, debug...
  24. +
+ + + + + + +
Note: If the Debug Platform example +plugins (org.eclipse.debug.examples.*) were previously installed in the +same workspace as the DSF PDA example, the two examples will both have +an editor registered for the ".pda" file type.  To ensure that the +right editor is opened, right click on the PDA file in the Navigator, +and select Open With->PDA(DSF) Editor.  The editor that is +opened should have the DSF-PDA icon:
+
+ + + + + + +
Note: A Perl +interpreter is required for PDA. Linux®™ comes with Perl. For +Microsoft® +Windows®, we use either ActivePerl (http://www.activeperl.com/) or +Indigo Perl (http://www.indigostar.com/).
+
+

Language

+

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:

+ +

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):

+
+
+ + + + + + + + + + +
samples/fibonacci.pda
+

+
+
push 6
call Fibonacci function call with one argument on the data stack
output print result to stdout
halt
#
# f(n) = f(n-1) + f(n-2)
# f(0) = 1
# f(1) = 1
#
:fibonacci
var n define variable n on control stack
pop $n get n from data stack
push $n
branch_not_zero gt0
push 1 f(0) = 1

return return with one value on data stack
:gt0
push $n
dec
branch_not_zero gt1
push 1 f(1) = 1
return return with one value on data stack
:gt1
push $n stack: n
dec stack: n-1
call fibonacci stack: f(n-1)

push $n stack: f(n-1) n
dec stack: f(n-1) n-1
dec stack: f(n-1) n-2
call Fibonacci stack: f(n-1) f(n-2)
add stack: f(n-1)+f(n-2)
return return with one value on data stack
+
+
+

Debug Protocol

+

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.  A detailed description of +the protocol can be found in org.eclipse.dd.examples.pda/pdavm/docs/protocol.html, +but the lists below show a quick overview.
+

+

The commands include:

+ +

The debug events that are reported asynchronously to the second +socket include:

+ +

Debug Services

+

TODO: remove this sesion?

Use of services is intended to allow for maximum level of extendability and customization.  To achieve this, service interfaces should encapsulate functionality that logically belongs together and at the @@ -63,86 +261,1970 @@ for reading stack information.
Service for reading symbol information about the loaded modules.
-


-

+

Step 1 - Launching
+

+The first task in integrating a debugger in Eclipse is creating and +managing the debugger process.  The Eclipse Platform provides an +extensive API for this purpose, which is nicely presented in the We +Have Lift-off: The Launching Framework in Eclipse article.  +This section (as this tutorial) concentrates on the DSF-specific tasks +of launching the PDA debugger.
+

Launch Delegate

+At first glance, there's nothing unusual about the PDA debugger launch +delegate.  Just like the Debug Platform version it:
+
    +
  1. finds the Perl executable,
  2. +
  3. finds free socket ports for debugger communication,
  4. +
  5. finds the PDA program
  6. +
  7. launches Perl to run the interpreter
  8. +
+The major difference is that it does not create an instance of the +standard debug model IDebugTerget +object.  Instead it implements the getLaunch() method in the ILaunchConfigurationDelegate2 +extension interface, in order to create a custom launch object:

+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.launch.PDALaunchDelegate +- getLaunch()

+
+
 51:  public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException {

52: // Need to configure the source locator before creating the launch
53: // because once the launch is created and added to launch manager,
54: // the adapters will be created for the whole session, including
55: // the source lookup adapter.
56: ISourceLocator locator = getSourceLocator(configuration);

58: return new PDALaunch(configuration, mode, locator);
59: }
+
+
+ +

PDALaunch

+The PDALaunch object plays two main roles:
+
    +
  1. Serve as the root element of the PDA View Model hierarchy
  2. +
  3. Manage the lifecycle of the DSF session, its services, and the +executor that belongs to the session. 
    +
  4. +
+The first task will be described in the View Model section, the second +task is described here. 

-

Push Down Automata (PDA)
+Even though the PDALaunch constructor is called +long before the debugging services are created, the session and the +executor need to be available to the UI clients that present the launch +object in the Debug view.
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.launch.PDALaunch +- <<constructor>>

+
+
 65:     public PDALaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) {
66: super(launchConfiguration, mode, locator);

68: // Create the dispatch queue to be used by debugger control and services
69: // that belong to this launch
70: final DefaultDsfExecutor dsfExecutor = new DefaultDsfExecutor(PDAPlugin.ID_PDA_DEBUG_MODEL);
71: dsfExecutor.prestartCoreThread();
72: fExecutor = dsfExecutor;
73: fSession = DsfSession.startSession(fExecutor, PDAPlugin.ID_PDA_DEBUG_MODEL);
74: }
+
+
+ +As the last step of the launch process, after the Perl process is +started, the launch delegate calls the launch to initialize the DSF +services.  There is an expected race condition between +initializeServices() and shutdownServices() routines in that the PDA +process may run to completion and exit while the initialize services +routine is still running.  Also, the user may terminate the +program while the initialization sequene is still running.  The +use of fInitializationSequence variable and other flags protects deals +with this race condition.
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.launch.PDALaunch +- intializeServices()

+
+
 90:     @ConfinedToDsfExecutor("getSession().getExecutor()")
91: public void initializeServices(String program, int requestPort, int eventPort, final RequestMonitor rm)
92: {
93: // Double-check that we're being called in the correct thread.
94: assert fExecutor.isInExecutorThread();

96: // Check if shutdownServices() was called already, which would be
97: // highly unusual, but if so we don't need to do anything except set
98: // the initialized flag.
99: synchronized(this) {
100: if (fShutDown) {
101: fInitialized = true;
102: return;
103: }
104: }

106: // Register the launch as listener for services events.
107: fSession.addServiceEventListener(PDALaunch.this, null);

109: // The initialization sequence is stored in a field to allow it to be
110: // canceled if shutdownServices() is called before the sequence
111: // completes.
112: fInitializationSequence = new PDAServicesInitSequence(
113: getSession(), program, requestPort, eventPort,
114: new RequestMonitor(ImmediateExecutor.getInstance(), rm) {
115: @Override
116: protected void handleCompleted() {
117: // Set the initialized flag and check whether the
118: // shutdown flag is set. Access the flags in a
119: // synchronized section as these flags can be accessed
120: // on any thread.
121: boolean doShutdown = false;
122: synchronized (this) {
123: fInitialized = true;
124: fInitializationSequence = null;
125: if (fShutDown) {
126: doShutdown = true;
127: }
128: }

130: if (doShutdown) {
131: // If shutdownServices() was already called, start the
132: // shutdown sequence now.
133: doShutdown(rm);
134: } else {
135: // If there was an error in the startup sequence,
136: // report the error to the client.
137: if (getStatus().getSeverity() == IStatus.ERROR) {
138: rm.setStatus(getStatus());
139: }
140: rm.done();
141: }
142: fireChanged();
143: }
144: });

146: // Finally, execute the sequence.
147: getSession().getExecutor().execute(fInitializationSequence);
148: }
+
+
+ +Due to race conditions between debugger events and user commands, the shutdownServices() routine may be +invoked more than once.  The shutdown logic must protect against +these race conditions.
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.launch.PDALaunch +- shutdownServices()
+
202:     @ConfinedToDsfExecutor("getSession().getExecutor()")
203: public void shutdownServices(final RequestMonitor rm) {
204: // Check initialize and shutdown flags to determine if the shutdown
205: // sequence can be called yet.
206: boolean doShutdown = false;
207: synchronized (this) {
208: if (!fInitialized && fInitializationSequence != null) {
209: // Launch has not yet initialized, try to cancel the
210: // shutdown sequence.
211: fInitializationSequence.cancel(false);
212: } else {
213: doShutdown = !fShutDown && fInitialized;
214: }
215: fShutDown = true;
216: }

218: if (doShutdown) {
219: doShutdown(rm);
220: } else {
221: rm.done();
222: }
223: }

225: @ConfinedToDsfExecutor("getSession().getExecutor()")
226: private void doShutdown(final RequestMonitor rm) {
227: fExecutor.execute( new PDAServicesShutdownSequence(
228: fExecutor, fSession.getId(),
229: new RequestMonitor(fSession.getExecutor(), rm) {
230: @Override
231: public void handleCompleted() {
232: fSession.removeServiceEventListener(PDALaunch.this);
233: if (!getStatus().isOK()) {
234: PDAPlugin.getDefault().getLog().log(new MultiStatus(
235: PDAPlugin.PLUGIN_ID, -1, new IStatus[]{getStatus()}, "Session shutdown failed", null)); //$NON-NLS-1$
236: }
237: // Last order of business, shutdown the dispatch queue.
238: DsfSession.endSession(fSession);
239: // endSession takes a full dispatch to distribute the
240: // session-ended event, finish step only after the dispatch.
241: fExecutor.shutdown();
242: fireTerminate();

244: rm.setStatus(getStatus());
245: rm.done();
246: }
247: }) );
248: }

+

+
+
+

Launch/Shutdown Sequence

+The actual task of calling the asynchronous IDsfService's initialize() and shutdown() methods is implemented +using the Sequence +object.  The following listing shows part of the declaration of +the +Sequence.Step objects which +perform the service initialization:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.launch.PDAServicesInitSequence +- fSteps

+
+
 38:     Step[] fSteps = new Step[] {
39: new Step()
40: {
41: @Override
42: public void execute(RequestMonitor requestMonitor) {
43: // Create the connection to PDA debugger.
44: fCommandControl = new PDACommandControl(fSession, fProgram, fRequestPort, fEventPort);
45: fCommandControl.initialize(requestMonitor);
46: }
47: },
48: new Step() {
49: @Override
50: public void execute(RequestMonitor requestMonitor) {
51: // Start the run control service.
52: fRunControl = new PDARunControl(fSession);
53: fRunControl.initialize(requestMonitor);
54: }
55: },
56: new Step() {
57: @Override
58: public void execute(RequestMonitor requestMonitor) {
59: // Start the service to manage step actions.
60: new StepQueueManager(fSession).initialize(requestMonitor);
61: }
62: },
...
+
+
+

Step 2 - Connecting 

+With the launch framework in place, the debugger back end is running +and the DSF session and executor are started.  The next step is to +create the first service and to connect to the debugger.  DSF +defines a debug interface: ICommandControl +which abstracts a debugger connection as a facility that processes +commands and generates events.  The ICommandControl method allow +for three major functions:
+
    +
  1. Queue/Remove/Cancel +commands - It is assumed that the command control service uses a +queue to hold commands that are to be sent to the debugger.  As +long as commands are still in the queue, clients can remove the +commands so they are never sent.  Even after the commands are +sent, the clients may request to cancel a running command, although +there is no guarantee that the debugger supports that.
  2. +
  3. Listening to Commands +Queued/Sent/Completed - Clients can listen to all command +traffic in order to implement custom processing.
  4. +
  5. Listening to Events +- Events are messages from the debugger which are not direct responses +to any commands.  Many clients need to listen to events such as +target state change events.
    +
  6. +
+ + + + + + + + + + +

+
Image 1: PDA Command Control Diagram
+
+

Synchronization

+Since there are several threads being used by the PDA Command Control +protecting state data becomes very important.
+
+Most of the state data in the command control service is protected +using the session thread, i.e. they can only be accessed while +executing in the session executor's thread:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.service.PDACommandControl +- members declaration
+
 56:     // Parameters that the command control is created with.
57: final private String fProgram;
58: final private int fRequestPort;
59: final private int fEventPort;

61: // Queue of commands waiting to be sent to the debugger. As long as commands
62: // are in this queue, they can still be removed by clients.
63: private final List<CommandHandle> fCommandQueue = new LinkedList<CommandHandle>();
64:
65: // Queue of commands that are being sent to the debugger. This queue is read
66: // by the send job, so as soon as commands are inserted into this queue, they can
67: // be considered as sent.
68: @ThreadSafe
69: private final BlockingQueue<CommandHandle> fTxCommands = new LinkedBlockingQueue<CommandHandle>();
70:
71: // Flag indicating that the PDA debugger started
72: private boolean fStarted = false;
73:
74: // Flag indicating that the PDA debugger has been disconnected
75: @ThreadSafe
76: private boolean fTerminated = false;
77:
78: // Data Model context of this command control.
79: private PDAProgramDMContext fDMContext;

81: // Synchronous listeners for commands and events.
82: private final List<ICommandListener> fCommandListeners = new ArrayList<ICommandListener>();
83: private final List<IEventListener> fEventListeners = new ArrayList<IEventListener>();
84:
85: // Sockets for communicating with PDA debugger
86: @ThreadSafe
87: private Socket fRequestSocket;
88: @ThreadSafe
89: private PrintWriter fRequestWriter;
90: @ThreadSafe
91: private BufferedReader fRequestReader;
92: @ThreadSafe
93: private Socket fEventSocket;
94: @ThreadSafe
95: private BufferedReader fEventReader;

97: // Jobs servicing the sockets.
98: private EventDispatchJob fEventDispatchJob;
99: private CommandSendJob fCommandSendJob;
+

+
+
+
    +
+ +Following is an example of how the access to session-thread protected +variables is implemented.
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.service.PDACommandControl.EventDispatchJob +- run()

+
+
300:         protected IStatus run(IProgressMonitor monitor) {
301: while (!isTerminated()) {
302: try {
303: // Wait for an event.
304: final String event = fEventReader.readLine();
305: if (event != null) {
306: try {
307: // Process the event in executor thread.
308: getExecutor().execute(new DsfRunnable() {
309: public void run() {
310: processEventReceived(event);
311: }
312: });
313: } catch (RejectedExecutionException e) {}
314: } else {
315: break;
316: }
317: } catch (IOException e) {
318: break;
319: }
320: }
321: if (!isTerminated()) {
322: // Exception from the event socket is an indicator that the PDA debugger
323: // has exited. Call setTerminated() in executor thread.
324: try {
325: getExecutor().execute(new DsfRunnable() {
326: public void run() {
327: setTerminated();
328: }
329: });
330: } catch (RejectedExecutionException e) {}
331: }
332: return Status.OK_STATUS;
333: }
334:
335: }
+
+ +
+

Command/Event Listeners

+As mentioned before there are two types of listeners that can be +registered with the comands control: event +listners and command listeners.  The most important +feature of these listeners, is that they are called by the command +control synchronously.  +As a result of this, the command listeners can expect to see the state +of the command queue that is consistent with the event they just +received.  However, if clients need to modify the queue as a +result of the event, they should only do it in a separate runnable, +otherwise other command listeners may encounter the command control in +an inconsistent state.
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.service.PDACommandControl +- queueCommand()
+
337:     public <V extends ICommandResult> void queueCommand(ICommand<V> command, DataRequestMonitor<V> rm) {
338: if (command instanceof AbstractPDACommand<?>) {
339: // Cast from command with "<V extends ICommandResult>" to a more concrete
340: // type to use internally in the command control.
341: @SuppressWarnings("unchecked")
342: AbstractPDACommand<PDACommandResult> pdaCommand = (AbstractPDACommand<PDACommandResult>)command;
343:
344: // Similarly, cast the request monitor to a more concrete type.
345: @SuppressWarnings("unchecked")
346: DataRequestMonitor<PDACommandResult> pdaRM = (DataRequestMonitor<PDACommandResult>)rm;

348: // Add the command to the queue and notify command listeners.
349: fCommandQueue.add( new CommandHandle(pdaCommand, pdaRM) );
350: for (ICommandListener listener : fCommandListeners) {
351: listener.commandQueued(command);
352: }
353:
354: // In a separate dispatch cycle. This allows command listeners to repond to the
355: // command queued event.
356: getExecutor().execute(new DsfRunnable() {
357: public void run() {
358: processQueues();
359: }
360: });
361:
362: } else {
363: PDAPlugin.failRequest(rm, INTERNAL_ERROR, "Unrecognized command: " + command);
364: }
365: }
+

+
+
+ +

PDAProgramDMContext

+Finally the command control also declares a Data Model context, which +is a parent to all other contexts for a given PDA debugger +session.  Each command used with the command control has to +implement the ICommand.getContext() method, which returns the context +that the command is acting on.  In PDA debugger, this context is +always the PDAProgramDMContext +instance returned by PDACommandControl.getProgramDMContext(). +However in other debuggers this context can have two other functions:
+
    +
  1. To identify the command +control instnace - In debugger sessions that connect to multiple +back ends, the context can be used to identify which command control +should process a given command.
  2. +
  3. To help control +debugger command protocol state - The PDA debug protocol is +stateless, which means that any command acts independently of any +commands that came before it.  For debuggers which do have +protocol state, e.g. GDB/MI, the command control needs to check the +context of each command and set the protocol by preceeding the command +being processed with other commands.
    +
  4. +
+ +

PDA Commands

+To increase type safetly and make the code more readable the plain text +PDA commands are abstracted using specific command objects.  Below +is an example of a command class:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.service.commands.PDADataCommand +

+
+
 15: /**
16: * Retrieves data stack information
17: *
18: * <pre>
19: * C: data
20: * R: {value 1}|{value 2}|{value 3}|...|
21: * </pre>
22: */
23: @Immutable
24:
public class PDADataCommand extends AbstractPDACommand<PDADataCommandResult> {
25:
26: public PDADataCommand(PDAProgramDMContext context) {
27: super(context, "data");
28: }
29:
30: @Override
31: public PDADataCommandResult createResult(String resultText) {
32: return new PDADataCommandResult(resultText);
33: }
34: }
+
+
+ +Here is the corresponding data result class:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.service.commands.PDADataCommandResult +

+
+
 20: @Immutable
21:
public class PDADataCommandResult extends PDACommandResult {
22:
23: final public String[] fValues;
24:
25: PDADataCommandResult(String response) {
26: super(response);
27: StringTokenizer st = new StringTokenizer(response, "|");
28: List<String> valuesList = new ArrayList<String>();
29:
30: while (st.hasMoreTokens()) {
31: String token = st.nextToken();
32: if (token.length() != 0) {
33: valuesList.add(st.nextToken());
34: }
35: }
36:
37: fValues = new String[valuesList.size()];
38: for (int i = 0; i < valuesList.size(); i++) {
39: fValues[i] = valuesList.get(i);
40: }
41: }
42: }
+
+
+ +
+ + + + + + +
Note: Command and command results +can be stored in a cache as keys and values.  Making them +immutable helps guard the integrity of these caches.
+

+

Step 3 - View Model

+

Adapter Glue

+ + + + + + +
The adapter mechanism is something +like the glue of Eclipse APIs, since it allows object to be associated +with each without having any +explicit dependencies.  Just like glue it works best when the +mating +parts are clean and closely fitted together, where just a little glue +does the job.  If too much glue is used to put together many +odd +parts, the whole thing can turn into a big sticky mess that falls apart +at the lightest touch.
+
+After connecting to the debugger, the next step is to get something to +display in the debugger views.  Flexible Hierarchy viewers depend +heavily on the adapter mechanism to associate the presentation classes +with the objects being presented.  The first step to connect the +DSF +View Model for the debugger views is to register an adapter factory for +the custom PDALaunch object:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.ui/plugin.xml +- PDA adapter factory declaration
+
   <extension
point="org.eclipse.core.runtime.adapters">
<factory
class="org.eclipse.dd.examples.pda.ui.PDAAdapterFactory"
adaptableType="org.eclipse.dd.examples.pda.launch.PDALaunch">
<adapter type="org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider"/>
<adapter type="org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory"/>
</factory>
</extension>
+

+
+
+
+The adapter factory has two jobs:
+
    +
  1. Return the PDAVMAdapter instance as the content provider for the +PDALaunch element.
  2. +
  3. Register and manage the life-cycle of all other adapters required +for a functioning debugger.
  4. +
+The first job is performed by the IAdapterFactory.getAdapter() method +listed below:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.ui.PDAAdapterFactory +- getAdapter()

+
+
157:     public Object getAdapter(Object adaptableObject, Class adapterType) {
158: if (!(adaptableObject instanceof PDALaunch)) return null;

160: PDALaunch launch = (PDALaunch)adaptableObject;

162: // Find the correct set of adapters based on the launch. If not found
163: // it means that we have a new launch, and we have to create a
164: // new set of adapters.
165: LaunchAdapterSet adapterSet;
166: synchronized(fLaunchAdapterSets) {
167: adapterSet = fLaunchAdapterSets.get(launch);
168: if (adapterSet == null) {
169: adapterSet = new LaunchAdapterSet(launch);
170: fLaunchAdapterSets.put(launch, adapterSet);
171: }
172: }
173:
174: // Returns the adapter type for the launch object.
175: if (adapterType.equals(IElementContentProvider.class)) return adapterSet.fViewModelAdapter;
176: else if (adapterType.equals(IModelProxyFactory.class)) return adapterSet.fViewModelAdapter;
177: else return null;
178: }
+
+
-

PDA debugger
+The LaunchAdapterSet constructor is responsible for creating and +registering all other adapters for the new debug session.  +However, instead of using the platform adapter factory mechanism, the +adapters are registered with the DSF session object and are returned as +adapters to the Data Model's IDMContext +object.
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.ui.PDAAdapterFactory.LaunchAdapterSet +- <<constructor>>
+
 85:         LaunchAdapterSet(PDALaunch launch) {
86: // Initialize launch and session.
87: fLaunch = launch;
88: DsfSession session = launch.getSession();
89:
90: // Initialize VM
91: fViewModelAdapter = new PDAVMAdapter(session);

93: // Initialize source lookup
94: fSourceDisplayAdapter = new MISourceDisplayAdapter(session, (ISourceLookupDirector)launch.getSourceLocator());
95: session.registerModelAdapter(ISourceDisplay.class, fSourceDisplayAdapter);
96:
97: // Initialize retargetable command handler.
98: fStepIntoCommand = new DsfStepIntoCommand(session);
99: fStepOverCommand = new DsfStepOverCommand(session);
100: fStepReturnCommand = new DsfStepReturnCommand(session);
101: fSuspendCommand = new DsfSuspendCommand(session);
102: fResumeCommand = new DsfResumeCommand(session);
103: fTerminateCommand = new PDATerminateCommand(session);
104: session.registerModelAdapter(IStepIntoHandler.class, fStepIntoCommand);
105: session.registerModelAdapter(IStepOverHandler.class, fStepOverCommand);
106: session.registerModelAdapter(IStepReturnHandler.class, fStepReturnCommand);
107: session.registerModelAdapter(ISuspendHandler.class, fSuspendCommand);
108: session.registerModelAdapter(IResumeHandler.class, fResumeCommand);
109: session.registerModelAdapter(ITerminateHandler.class, fTerminateCommand);

111: // Initialize debug model provider
112: fDebugModelProvider = new IDebugModelProvider() {
113: public String[] getModelIdentifiers() {
114: return new String[] { PDAPlugin.ID_PDA_DEBUG_MODEL };
115: }
116: };
117: session.registerModelAdapter(IDebugModelProvider.class, fDebugModelProvider);
118:
119: // Register the launch as an adapter This ensures that the launch,
120: // and debug model ID will be associated with all DMContexts from this
121: // session.
122: session.registerModelAdapter(ILaunch.class, fLaunch);
123: }
+

+
+
+ + +Each adapter set is created new for each PDA debug session.  They +also need to be disposed when they are no longer needed.  It could +be expected that the adapters should be disposed when the PDA debugger +is terminated.  However, the terminated debug session still +appears in the Debug view, and this requires the adapters to be +present.  Instead, the adapters are removed when the corresponding +PDA launch is removed:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.ui.PDAAdapterFactory +- launchesRemoved()

+
+
185:     public void launchesRemoved(ILaunch[] launches) {
186: // Dispose the set of adapters for a launch only after the launch is
187: // removed from the view. If the launch is terminated, the adapters
188: // are still needed to populate the contents of the view.
189: for (ILaunch launch : launches) {
190: if (launch instanceof PDALaunch) {
191: PDALaunch pdaLaunch = (PDALaunch)launch;
192: synchronized(fLaunchAdapterSets) {
193: if ( fLaunchAdapterSets.containsKey(pdaLaunch) ) {
194: fLaunchAdapterSets.remove(pdaLaunch).dispose();
195: }
196: }
197: }
198: }
199: }
+
+
+

PDA View Model

+The PDAVMAdapter creates the VM Providers on demand for each debugger +view it supports:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.ui.viewmodel.PDAVMAdapter +- createViewModelProvider()

+
+
 43:     protected AbstractDMVMProvider createViewModelProvider(IPresentationContext context) {
44: if ( IDebugUIConstants.ID_DEBUG_VIEW.equals(context.getId()) ) {
45: return new PDALaunchVMProvider(this, context, getSession());
46: } else if (IDebugUIConstants.ID_VARIABLE_VIEW.equals(context.getId()) ) {
47: return new VariableVMProvider(this, context, getSession());
48: } else if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(context.getId()) ) {
49: return new ExpressionVMProvider(this, context, getSession());
50: }
51: return null;
52: }
+
+
+ +

Launch VM Provider

+"Launch" actually refers to the internal name of the Debug view.  +The PDA debugger has a somewhat simpler presentation in Debug view than +most debuggers because it does not support multiple threads so it has +the thread node shown directly below the launch node.
+
+ + + + + + + + + +

+
Image 2: Debug view screen capture
+
+The PDALaunchVMProvider constructor creates the VM Nodes and arranges +them in a hierarchy that mirrors the screen-shot in Image 2.
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.ui.viewmodel.launch.PDALaunchVMProvider +- <<constructor>>

+
+
 50:     public PDALaunchVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext, DsfSession session) 
51: {
52: super(adapter, presentationContext, session);
53:
54: IRootVMNode launchNode = new LaunchRootVMNode(this);
55: setRootNode(launchNode);

57: // Launch node is a parent to the processes and program nodes.
58: IVMNode pdaProgramNode = new PDAProgramVMNode(this, getSession());
59: IVMNode processesNode = new StandardProcessVMNode(this);
60: addChildNodes(launchNode, new IVMNode[] { pdaProgramNode, processesNode});
61:
62: // Stack frames node is under the PDA program node.
63: IVMNode stackFramesNode = new StackFramesVMNode(this, getSession());
64: addChildNodes(pdaProgramNode, new IVMNode[] { stackFramesNode });

66: // Register the LaunchVM provider as a listener to debug and launch
67: // events. These events are used by the launch and processes nodes.
68: DebugPlugin.getDefault().addDebugEventListener(this);
69: DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this);
70: }
+
+
+ +

PDA Program VM Node

+ +

Elements

+The PDA Program Node is the most complex component of the PDA View +Model.  It supplies an element representing the PDA program and it +operates in three modes:
+
    +
  1. Not Initialized +- After the PDALaunch object +has been created, but the services have not yet been initialized.  +In this mode the PDA Program is not shown in Debug view.
    +
  2. +
  3. Running - After +the PDALaunch and PDACommandControl are initialized, the PDAProgramDMContext object is used +as the underlying Data Model element shown in Debug view. 
    +
  4. +
  5. Shut down - +After a program has terminated, it is still shown in the Debug view, as +is consistent with the established workflow.  However, at this +point the PDACommandControl +service is shut down and the PDAProgramDMContext +is not available.  Hence, a separate TerminatedProgramVMContext +wrapper element is used which does not have an underlying Data Model +element.
  6. +
+The following two methods implement the logic of supplying the elements +of the above modes:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.ui.viewmodel.launch.PDALaunchVMNode +- update(IChildrenUpdate[])
+
119:     @Override
120: public void update(IChildrenUpdate[] updates) {
121: for (IChildrenUpdate update : updates) {
122: PDALaunch launch = findLaunchInPath(update.getElementPath());
123: if (launch != null && launch.isInitialized() && launch.isShutDown()) {
124: // If the debug session has been shut down, add a dummy
125: // VM context representing the PDA thread.
126: update.setChild(new TerminatedProgramVMContext(getVMProvider().getVMAdapter(), this), 0);
127: update.done();
128: } else {
129: super.update(new IChildrenUpdate[] { update });
130: }
131: }
132: }
133:
134: @Override
135: protected void updateElementsInSessionThread(final IChildrenUpdate update) {
136: // Get the instance of the service. Note that there is no race condition
137: // in getting the service since this method is called only in the
138: // service executor thread.
139: final PDACommandControl commandControl = getServicesTracker().getService(PDACommandControl.class);

141: // Check if the service is available. If it is not, no elements are
142: // updated.
143: if (commandControl == null) {
144: handleFailedUpdate(update);
145: return;
146: }
147:
148: update.setChild(createVMContext(commandControl.getProgramDMContext()), 0);
149: update.done();
150: }
+

+
+
+
+ +

Label

+Calculating the label for the PDA program element is also split into +two parts depending on whether the program is terminated.  +Similarly as when calculating the element, if the program is +terminated, the label is calculated in the View Model thread, if the +program is running, the execution is switched to the session executor +thread.  This is accomplished in the update(ILabelUpdate[]) +implementation:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.ui.viewmodel.launch.PDALaunchVMNode +- update(ILabelUpdate[]) +

+
+
152:     public void update(final ILabelUpdate[] updates) {
153: for (final ILabelUpdate update : updates) {
154: if (update.getElement() instanceof TerminatedProgramVMContext) {
155: // If the element is a terminated program, update the label
156: // in the View Model thread.
157: updateTerminatedThreadLabel(update);
158: } else {
159: // If the element is the PDA Program context, try to switch
160: // to the DSF session thread before updating the label.
161: try {
162: getSession().getExecutor().execute(new DsfRunnable() {
163: public void run() {
164: updateProgramLabelInSessionThread(update);
165: }});
166: } catch (RejectedExecutionException e) {
167: // Acceptable race condition: DSF session terminated.
168: handleFailedUpdate(update);
169: }
170: }
171: }
172: }
+
+
+ +The updateProgramLabelInSessionThread() +is rather long, but it is useful to look at it in detail because it is +representative of what all label providers must do.
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.ui.viewmodel.launch.PDALaunchVMNode +- updateProgramLabelInSessionThread()

+
+
174:     @ConfinedToDsfExecutor("getSession().getExecutor()")
175: private void updateProgramLabelInSessionThread(final ILabelUpdate update) {
176: // Get a reference to the run control service.
177: final IRunControl runControl = getServicesTracker().getService(IRunControl.class);
178: if (runControl == null) {
179: handleFailedUpdate(update);
180: return;
181: }
182:
183: // Find the PDA program context.
184: final PDAProgramDMContext programCtx =
185: findDmcInPath(update.getViewerInput(), update.getElementPath(), PDAProgramDMContext.class);

187: // Call service to get current program state
188: final boolean isSuspended = runControl.isSuspended(programCtx);

190: // Set the program icon based on the running state of the program.
191: String imageKey = null;
192: if (isSuspended) {
193: imageKey = IDebugUIConstants.IMG_OBJS_THREAD_SUSPENDED;
194: } else {
195: imageKey = IDebugUIConstants.IMG_OBJS_THREAD_RUNNING;
196: }
197: update.setImageDescriptor(DebugUITools.getImageDescriptor(imageKey), 0);

199: // Retrieve the last state chagne reason
200: runControl.getExecutionData(
201: programCtx,
202: new DataRequestMonitor<IExecutionDMData>(ImmediateExecutor.getInstance(), null)
203: {
204: @Override
205: public void handleCompleted(){
206: // If the request failed, fail the udpate.
207: if (!getStatus().isOK()) {
208: handleFailedUpdate(update);
209: return;
210: }
211:
212: // Compose the thread name string.
213: final StringBuilder builder = new StringBuilder();
214:
215: builder.append("PDA [");
216: builder.append(programCtx.getProgram());
217: builder.append("]");
218:
219: if(isSuspended) {
220: builder.append(" (Suspended");
221: } else {
222: builder.append(" (Running");
223: }
224: // Reason will be null before ContainerSuspendEvent is fired
225: if(getData().getStateChangeReason() != null) {
226: builder.append(" : ");
227: builder.append(getData().getStateChangeReason());
228: }
229: builder.append(")");
230: update.setLabel(builder.toString(), 0);
231: update.done();
232: }
233: });
234: }
+
+
+ +

Delta

+Translating the Data Model events into IModelDelta objects that can be +processed by the Flehible Hierarchy views, is the most complicated task +performed by the View Model infrastructure.  The model deltas +require that a path be formed by the IModelDelta objects which matches +the hierarchy elements found in the view, including such details as +indexes of elements and their number at each level.  Normally this +requires that the model event handler should know the full hierarchy of +the elements in a given view.  However, with the DSF View Model, +this logic is split into two parts:
+
    +
  1. The event handler in the VM Provider
  2. +
  3. The build delta methods in the VM Nodes.
  4. +
+The debug model event handler below is an example of the event handler +implementation in the VM Provider.  The event handler for Data +Model events is already implemented in the AbstractDMVMProvider base class:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.ui.viewmodel.launch.PDALaunchVMProvider +- handleDebugEvents()
+
 73:     public void handleDebugEvents(final DebugEvent[] events) {
74: if (isDisposed()) return;
75:
76: // This method may be called on any thread. Switch to the
77: // view model executor thread before processing.
78: try {
79: getExecutor().execute(new Runnable() {
80: public void run() {
81: if (isDisposed()) return;
82:
83: for (final DebugEvent event : events) {
84: handleEvent(event);
85: }
86: }});
87: } catch (RejectedExecutionException e) {
88: // Ignore. This exception could be thrown if the view model is being
89: // shut down.
90: }
91: }
+

+
+
+ +The PDA program node implements methods to add the delta nodes for its +elements:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.ui.viewmodel.launch.PDALaunchVMNode +- buildDelta()

+
+
265:     public int getDeltaFlags(Object e) {
266: if(e instanceof IResumedDMEvent || e instanceof ISuspendedDMEvent) {
267: return IModelDelta.STATE;
268: }
269: if (e instanceof PDAStartedEvent) {
270: return IModelDelta.EXPAND | IModelDelta.SELECT;
271: }
272: return IModelDelta.NO_CHANGE;
273: }
274:
275: public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor rm) {
276: if(e instanceof IResumedDMEvent || e instanceof ISuspendedDMEvent) {
277: // If a suspended/resumed event is received, just update the
278: // state of the program. StackFramesVMNode will take care of
279: // refreshing the stack frames.
280: parentDelta.addNode(createVMContext(((IDMEvent<?>)e).getDMContext()), IModelDelta.STATE);
281: }
282: if (e instanceof PDAStartedEvent) {
283: // When debug session is started expand and select the program.
284: // If the program hits a breakpoint, the top stack frame will then
285: // be selected.
286: parentDelta.addNode(createVMContext(((PDAStartedEvent)e).getDMContext()), IModelDelta.EXPAND | IModelDelta.SELECT);
287: }
288: rm.done();
289: }
290: }
+
+
+ + + + + + + +
Note: In theory, each VM Node +should only generate delta flags that only affect its own +elements.  In this way, the layout of the VM Nodes in a view could +be costomized as needed.  In practice, testing and fine-tuning of +the view requires adjustments in the flags returned by the various VM +Nodes in a given view.
+

Step 4 - Run Control

+Up to this point most of the work in creating the new PDA debugger has +gone into infrastructure.  Now it is time to start adding +functionality to do some actual debugging.  The work needed to get +run control functionality implemented is all encapsulated in the PDARunControl service.
+

State Tracking

+The primary function of the run control system is to track the current +execution state of the program and to issue the corresponding events to +the run control service clients.  Both of these tasks are +accomplished by the ICommandControl's IEventListner.eventReceived() +implementation in combination with service event handlers for +IRunControl.IResumedDMEvent and IRunControl.ISuspendedDMEvent.  It +may seem odd that the run control service is listening to its own +events in order to change its internal state, but doing so guarantees +that the execution state reported by the service is consistent with the +events it sends out.
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.service.PDARunControl +- eventReceived()

+
+
181:     public void eventReceived(Object output) {
182: if (!(output instanceof String)) return;
183: String event = (String)output;
184:
185: // Handle PDA debugger suspended/resumed events and issue the
186: // corresponding Data Model events. Do not update the state
187: // information until we start dispatching the service events.
188: if (event.startsWith("suspended")) {
189: IDMEvent<?> dmEvent = new SuspendedEvent(fCommandControl.getProgramDMContext(), event);
190: getSession().dispatchEvent(dmEvent, getProperties());
191: } else if (event.startsWith("resumed")) {
192: IDMEvent<?> dmEvent = new ResumedEvent(fCommandControl.getProgramDMContext(), event);
193: getSession().dispatchEvent(dmEvent, getProperties());
194: }
195: }
196:
197:
198: @DsfServiceEventHandler
199: public void eventDispatched(ResumedEvent e) {
200: // This service should be the first to receive the ResumedEvent,
201: // (before any other listeners are called). Here, update the
202: // service state information based on the the resumed event.
203: fSuspended = false;
204: fResumePending = false;
205: fStateChangeReason = e.getReason();
206: fStepping = e.getReason().equals(StateChangeReason.STEP);
207: }
208:
209:
210: @DsfServiceEventHandler
211: public void eventDispatched(SuspendedEvent e) {
212: // This service should be the first to receive the SuspendedEvent also,
213: // (before any other listeners are called). Here, update the
214: // service state information based on the the suspended event.
215: fStateChangeReason = e.getReason();
216: fResumePending = false;
217: fSuspended = true;
218: fStepping = false;
219: }
+
+
-

Launching
-

+One remarkable aspect of the state tracking logic is the use of the fResumePending flag.  This +flag is set to true when a resume or step command is sent to the PDA +debugger.  It is then used by the canResume() method to disallow +sending another resume command.  This kind of predictive state +logic an improve the efficiency of the IDE commands.
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.service.PDARunControl +- canResume()

+
+
222:     public boolean canResume(IExecutionDMContext context) {
223: return isSuspended(context) && !fResumePending;
224: }
+
+
+

Commands

+The command control commands all follow the same patter shown below by +example of the resume() command:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.service.PDARunControl +- resume()

+
+
238:     public void resume(IExecutionDMContext context, final RequestMonitor rm) {
239: assert context != null;
240:
241: if (canResume(context)) {
242: fResumePending = true;
243: fCommandControl.queueCommand(
244: new PDAResumeCommand(fCommandControl.getProgramDMContext()),
245: new DataRequestMonitor<PDACommandResult>(getExecutor(), rm) {
246: @Override
247: protected void handleErrorOrCancel() {
248: // If the resume command failed, we no longer
249: // expect to receive a resumed event.
250: fResumePending = false;
251: super.handleErrorOrCancel();
252: }
253: }
254: );
255: } else {
256: PDAPlugin.failRequest(rm, INVALID_STATE, "Given context: " + context + ", is already running.");
257: }
258: }
+
+
-
 51:     public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException {

52: // Need to configure the source locator before creating the launch
53: // because once the launch is created and added to launch manager,
54: // the adapters will be created for the whole session, including
55: // the source lookup adapter.
56: ISourceLocator locator = getSourceLocator(configuration);

58: return new PDALaunch(configuration, mode, locator);
59: }
- -

Connecting
-

- -

View Model

- -

Run Control

-

Breakpoints

+

IExecutionDMData

+There is very little data that the run control servie interface returns +for a given execution context.  The intention behind this is to +allow the service to remain very generic and thus applicable to any +debugger.  In specific debuggers, additional data about processes, +threads, cores, etc, should be retrieved from other services or service +extensions.  IExpressionDMData is the only object which is +retrieved asynchronously from the run control service, and it only +contains the state change reason for the last debugger state +change. 
+

Step 5 - Breakpoints

+Managing breakpoints is one of the complicated tasks that need to be +implemented by Eclipse debuggers.  The source of this complexity +is the fact that Eclipse breakpoints (IDE breakpoints) are managed +independently of breakpoints that are installed in the debugger +(target-side breakpoints).  The Eclipse debugger integration has +to keep these two sets of breakpoints synchronized. 
+

IDE Breakpoints

+Eclipse breakpoints are based on markers, which are special tags in the +Eclispe resource system and are associated with files and +folders.  By using markers, breakpoints gain the benefit of the +resource system synchronization mechanism and the automatic +persistence.  Also some other cool features such as adjusting the +breakpoint line number when the source file is edited, are also +automatically gained by using markers. 
+

An eclipse breakpoint declaration comes in three parts:
+

+
    +
  1. org.eclipse.core.resources.markers extension - A debugger must +use this extension to declare a marker type for the new +breakpoint.  Markers are hierarchical and a breakpoint should have +one of the platform breakpoint objects as a super type.
  2. +
  3. org.eclipse.debug.core.breakpoints extension - A breakpoint must +be declared using this extension point, which requires a valid marker +type.
  4. +
  5. org.eclipse.debug.core.model.IBreakpoint implementation - A +breakpoint object must implement this interface.
  6. +
+Finally, in order to have breakpoints to appear in the Breakpoints +view, they need to be registered with the breakpoint manager, which is +represented by the org.eclipse.debug.core.IBreakpointManager +interface.  The breakpoint manager also relays events for +notifying of breakpoint changes and for a debugger it is the central +object used to find the IDE breakpoints that need to be installed on +target.
+
+Code listings are omitted here since implementing Eclipse breakpoints +is described in fine detail the How +to write an Eclipse debugger article.
+

+

Target-Side Breakpoints

+DSF defines the IBreakpoints interface for a service which the fuctions +of managing breakpoints installed on the target.  These functions +include:
+ +

Breakpoint Context
+

+It is expected that primary client of this service is going to be +another service which reads IDE Breakpoints, however under certain +circumstances such as launching or testing, this interface could be +used by other clients, therefore it is desirable that the interface +itself should not have any dependencies on the IDE Breakpoint +API.  To remain generic, the attributes for breakpoints to be +created or update are specified using a simple property bag of type: Map<String, Object>.  +After the target-side breakpoint is created, it is represented using an +opaque Data Model context of type IBreakpointDMContext.  +As an example the PDA line breakpoint context implementation is the +following:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.service.PDABreakpoints +- BreakpointDMContext

+
+
 50:     private static class BreakpointDMContext extends AbstractDMContext implements IBreakpointDMContext {
51:
52: final Integer fLine;
53:
54: public BreakpointDMContext(String sessionId, PDAProgramDMContext commandControlCtx, Integer line) {
55: super(sessionId, new IDMContext[] { commandControlCtx });
56: fLine = line;
57: }
58:
59: @Override
60: public boolean equals(Object obj) {
61: return baseEquals(obj) && (fLine.equals(((BreakpointDMContext) obj).fLine));
62: }
63:
64: @Override
65: public int hashCode() {
66: return baseHashCode() + fLine.hashCode();
67: }
68:
69: @Override
70: public String toString() {
71: return baseToString() + ".breakpoint(" + fLine + ")"; //$NON-NLS-1$//$NON-NLS-2$*/
72: }
73: }

+
+
+ +It is also important to note that the IBreakpoints interface is +designed to work with debuggers that can track separate breakpoint sets +for different cores/processes/threads.  Each context which +represents a "breakpoint space" must implement an IBreakpointsTargetDMContext +interface, which is then used as a parent context of the IBreakpointDMContext object +instances.
+

Inserting a Breakpoint

+PDA debugger supports two types of breakpoints.  The +IBreakpoints.insertBreakpoint() implementation performs the task of +what type of breakpoint should be installed and delegates to the proper +subroutine:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.service.PDABreakpoints +- insertBreakpoint()

+
+
180:         public void insertBreakpoint(IBreakpointsTargetDMContext context, Map<String, Object> attributes, 
181: DataRequestMonitor<IBreakpointDMContext> rm)
182: {
183: Boolean enabled = (Boolean)attributes.get(IBreakpoint.ENABLED);
184: if (enabled != null && !enabled.booleanValue()) {
185: // If the breakpoint is disabled, just fail the request.
186: PDAPlugin.failRequest(rm, REQUEST_FAILED, "Breakpoint is disabled");
187: } else {
188: String type = (String) attributes.get(ATTR_BREAKPOINT_TYPE);
189:
190: if (PDA_LINE_BREAKPOINT.equals(type)) {
191: // Retrieve the PDA program context from the context given in the
192: // argument. This service is typically only called by the
193: // breakpoints mediator, which was called with the program context
194: // in the services initialization sequence. So checking if
195: // programCtx != null is mostly a formality.
196: PDAProgramDMContext programCtx = DMContexts.getAncestorOfType(context, PDAProgramDMContext.class);
197: if (programCtx != null) {
198: doInsertBreakpoint(programCtx, attributes, rm);
199: } else {
200: PDAPlugin.failRequest(rm, INVALID_HANDLE, "Unknown breakpoint type");
201: }
202: }
203: else if (PDA_WATCHPOINT.equals(type)) {
204: doInsertWatchpoint(attributes, rm);
205: }
206: else {
207: PDAPlugin.failRequest(rm, REQUEST_FAILED, "Unknown breakpoint type");
208: }
209: }
210: }
+
+
+ +The doInserBreakpoint() subroutine is listed next:
+
+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.service.PDABreakpoints +- doInsertBreakpoint()

+
+
212:     private void doInsertBreakpoint(PDAProgramDMContext programCtx, final Map<String, Object> attributes, final DataRequestMonitor<IBreakpointDMContext> rm) 
213: {
214: // Compare the program path in the breakpoint with the path in the PDA
215: // program context. Only insert the breakpoint if the program matches.
216: String program = (String)attributes.get(ATTR_PROGRAM_PATH);
217: if (!programCtx.getProgram().equals(program)) {
218: PDAPlugin.failRequest(rm, REQUEST_FAILED, "Invalid file name");
219: return;
220: }

222: // Retrieve the line.
223: Integer line = (Integer)attributes.get(IMarker.LINE_NUMBER);
224: if (line == null) {
225: PDAPlugin.failRequest(rm, REQUEST_FAILED, "No breakpoint line specified");
226: return;
227: }

229: // Create a new breakpoint context object and check that it's not
230: // installed already. PDA can only track a single breakpoint at a
231: // given line, attempting to set the second breakpoint should fail.
232: final BreakpointDMContext breakpointCtx =
233: new BreakpointDMContext(getSession().getId(), fCommandControl.getProgramDMContext(), line);
234: if (fBreakpoints.contains(breakpointCtx)) {
235: PDAPlugin.failRequest(rm, REQUEST_FAILED, "Breakpoint already set");
236: return;
237: }

239: // Add the new breakpoint context to the list of known breakpoints.
240: // Adding it here, before the set command is completed will prevent
241: // a possibility of a second breakpoint being installed in the same
242: // location while this breakpoint is being processed. It will also
243: // allow the breakpoint to be removed or updated even while it is
244: // still being processed here.
245: fBreakpoints.add(breakpointCtx);
246: fCommandControl.queueCommand(
247: new PDASetBreakpointCommand(fCommandControl.getProgramDMContext(), line),
248: new DataRequestMonitor<PDACommandResult>(getExecutor(), rm) {
249: @Override
250: protected void handleOK() {
251: rm.setData(breakpointCtx);
252: rm.done();
253: }

255: @Override
256: protected void handleErrorOrCancel() {
257: // If inserting of the breakpoint failed, remove it from
258: // the set of installed breakpoints.
259: fBreakpoints.remove(breakpointCtx);
260: super.handleErrorOrCancel();
261: }
262: });
263: }

+
+
+

Removing a Breakpoint

+The PDABreakpoints.removeBreakpoint() command takes the +IBreakpointDMContext as an argument, but otherwise follows the same +general logic as the insertBreakpoint() implementation.
+

Updating a Breakpoint

+Updating a breakpoint involves motifying some of the attributes of an +existing breakpoint.  Not all debuggers may support this +functionality and for debuggers that do, not all types of breakpoints +and not all attributes may be updated.  The IBreakpoints interface +does not provide a way for the clients to discover what breakpoints and +what attributes may be updated.  It is up to the client to know +this information in advance. 
+

The PDA debugger allows watchpoints to be updated, but only with +respect to what operations may trigger the watchpoint:
+

+
+ + + + + + + + + + +
org.eclipse.dd.examples.pda.service.PDABreakpoints +- updateBreakpoint()

+
+
367:     public void updateBreakpoint(final IBreakpointDMContext bpCtx, Map<String, Object> attributes, final RequestMonitor rm) {
368: if (!fBreakpoints.contains(bpCtx)) {
369: PDAPlugin.failRequest(rm, REQUEST_FAILED, "Breakpoint not installed");
370: return;
371: }

373: if (bpCtx instanceof BreakpointDMContext) {
374: PDAPlugin.failRequest(rm, NOT_SUPPORTED, "Modifying PDA breakpoints is not supported");
375: } else if (bpCtx instanceof WatchpointDMContext) {
376: WatchpointDMContext wpCtx = (WatchpointDMContext)bpCtx;
377: if (!wpCtx.fFunction.equals(attributes.get(PDAWatchpoint.FUNCTION_NAME)) ||
378: !wpCtx.fVariable.equals(attributes.get(PDAWatchpoint.VAR_NAME)) )
379: {
380: PDAPlugin.failRequest(rm, REQUEST_FAILED, "Cannot modify watchpoint function or variable");
381: return;
382: }
383:
384: // PDA debugger can only track one watchpoint in the same location,
385: // so we can simply remove the existing context from the set and
386: // call insert again.
387: fBreakpoints.remove(bpCtx);
388: doInsertWatchpoint(
389: attributes,
390: new DataRequestMonitor<IBreakpointDMContext>(getExecutor(), rm) {
391: @Override
392: protected void handleOK() {
393: // The inserted watchpoint context will equal the
394: // current context.
395: assert bpCtx.equals(getData());
396: rm.done();
397: }
398: });
399: } else {
400: PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid breakpoint");
401: }
402: }

+
+
+ -

Stack

+

Step 6 - Stack

-

Source Display

+

Step 7 - Source Display

-

Variables

+

Step 8 - Variables

+
+ + + + + + + + + + +
org.eclipse.dd.examples.dsf.timers.TimersVMNode +

+

+
+