Bug 334747 - Need a way to inject an additional service into a stock dsf-gdb session
Summary: Need a way to inject an additional service into a stock dsf-gdb session
Status: NEW
Alias: None
Product: CDT
Classification: Tools
Component: cdt-debug-dsf-gdb (show other bugs)
Version: 8.0   Edit
Hardware: PC Windows 7
: P3 enhancement (vote)
Target Milestone: ---   Edit
Assignee: Project Inbox CLA
QA Contact: Jonah Graham CLA
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-01-19 04:49 EST by Derek Morris CLA
Modified: 2020-09-04 15:17 EDT (History)
5 users (show)

See Also:


Attachments
example plugin showing how to add and access a DSF service into an arbitrary DSF session (7.71 KB, application/x-zip-compressed)
2011-01-20 11:18 EST, John Cortell CLA
no flags Details
example plugin showing how to add and access a DSF service into an arbitrary DSF session (7.78 KB, application/x-zip-compressed)
2011-01-20 11:46 EST, John Cortell CLA
no flags Details
my extend DFS-GDB program (18.57 KB, application/x-itunes-itlp)
2011-08-26 10:07 EDT, tony burton CLA
no flags Details
An example DSF-GDB command (30.95 KB, application/zip)
2011-10-24 08:23 EDT, tony burton CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Derek Morris CLA 2011-01-19 04:49:37 EST
Build Identifier: Helios SR1

I am trying to add a plugin that adds a new service/model/view to a standard DSF-GDB debugger. However, there is no way for a plugin to do this.

This prevents me (and other third parties) from enhancing DSF functionality - something that I could do with CDI based debuggers.

Note that https://bugs.eclipse.org/bugs/show_bug.cgi?id=326951 does not help, as this enables a custom debugger some added flexibility. I am not trying to add a custom debugger - just extend the existing one!


Reproducible: Always

Steps to Reproduce:
Quote from John Cortell in the cdt-dev list:

"Each specific DSF debugger implementation dictates which DSF services it will use. None of the stock DSF debuggers (dsf-gdb, edc) allow someone to inject an arbitrary service into the mix.

Off the top of my head, I don't know why we couldn't make the debuggers discover (through an extension point) additional services to involve. I think that would solve your problem. You may want to take a crack at adding that support and contributing it."

Sorry - I am not up to fixing this in the CDT sources.
Comment 1 Marc Khouzam CLA 2011-01-19 15:45:53 EST
(In reply to comment #0)
> Build Identifier: Helios SR1
> 
> I am trying to add a plugin that adds a new service/model/view to a standard
> DSF-GDB debugger. However, there is no way for a plugin to do this.
> 
> This prevents me (and other third parties) from enhancing DSF functionality -
> something that I could do with CDI based debuggers.
> 
> Note that https://bugs.eclipse.org/bugs/show_bug.cgi?id=326951 does not help,
> as this enables a custom debugger some added flexibility. I am not trying to
> add a custom debugger - just extend the existing one!

Is it that your plugin only wants to contribute extensions?
You don't want to have code in your plugin?  Is that the issue?
Comment 2 Derek Morris CLA 2011-01-19 16:36:34 EST
I'm not sure what you mean. I'm happy to put whatever code is necessary in my plugin, but I don't have the time/capability to start patching CDT sources. I need my plugin to b a 'pure' plugin and not have to ship bits of CDT with it.
Comment 3 Marc Khouzam CLA 2011-01-19 21:26:27 EST
(In reply to comment #2)
> I'm not sure what you mean. I'm happy to put whatever code is necessary in my
> plugin, but I don't have the time/capability to start patching CDT sources. I
> need my plugin to b a 'pure' plugin and not have to ship bits of CDT with it.

Ok, but CDT will be installed right?  You can still extend CDT with a stand-alone plugin.  So, using the fix to bug 326951, you could simply override one or two proper classes (we can point you to the right ones if that is not clear), and add your own service.

I'm not sure about the view part of this effort, but Mikhail may have a solution for that.
Comment 4 John Cortell CLA 2011-01-19 22:17:40 EST
(In reply to comment #3)
> Ok, but CDT will be installed right?  You can still extend CDT with a
> stand-alone plugin.  So, using the fix to bug 326951, you could simply override
> one or two proper classes (we can point you to the right ones if that is not
> clear), and add your own service.

But that requires, effectively, a custom debugger. It's not the effort of one or two classes, which is minimal. It's the fact that a user would not how to choose a custom debugger in order to get the functionality in the custom view. I think the point is to get the custom view to work with a stock dsf-gdb session. Again, I could be wrong. That's my interpretation of the requirement.
Comment 5 John Cortell CLA 2011-01-19 22:19:56 EST
Take 2:

It's the fact that a user would have to choose a custom debugger in order to get the functionality in the custom view.
Comment 6 Derek Morris CLA 2011-01-20 03:12:02 EST
(In reply to comment #5)
> Take 2:
> 
> It's the fact that a user would have to choose a custom debugger in order to
> get the functionality in the custom view.

Please let me know what is not clear about my request, I am struggling here because to me, this seems like an obvious request. Let me try again.

I *need* to be able to supply my plugin without having to supply a custom debugger. I *must* be able to 'inject' my plugin into a completely standard CDT distribution *and* one with a 'custom' debugger (provided it is GDB based). Shipping my own custom debugger is not a viable option.

I view the inability to do this as a very serious regression from CDI. I have a plugin that I ship on CDI that does everything I need to do. It plugs in to a standard CDT distribution *and* one with a customised CDI interface.

From the history trail, it seems that John Cortell fully understands what I want to do, and why, but Marc Khouzam doesn't...
Comment 7 Marc Khouzam CLA 2011-01-20 05:24:21 EST
(In reply to comment #5)
> Take 2:
> 
> It's the fact that a user would have to choose a custom debugger in order to
> get the functionality in the custom view.

Ok, now I see, the part I was missing is that you would need to create a custom launch configuration to be able to choose this new service/view.
Comment 8 Marc Khouzam CLA 2011-01-20 05:25:11 EST
(In reply to comment #6)

> I *need* to be able to supply my plugin without having to supply a custom
> debugger. I *must* be able to 'inject' my plugin into a completely standard CDT
> distribution *and* one with a 'custom' debugger (provided it is GDB based).
> Shipping my own custom debugger is not a viable option.
> 
> I view the inability to do this as a very serious regression from CDI. I have a
> plugin that I ship on CDI that does everything I need to do. It plugs in to a
> standard CDT distribution *and* one with a customised CDI interface.

CDI is still available in CDT, if this is a blocking issue for you.
Comment 9 Derek Morris CLA 2011-01-20 05:35:57 EST
(In reply to comment #8)
> (In reply to comment #6)
> 
> > I *need* to be able to supply my plugin without having to supply a custom
> > debugger. I *must* be able to 'inject' my plugin into a completely standard CDT
> > distribution *and* one with a 'custom' debugger (provided it is GDB based).
> > Shipping my own custom debugger is not a viable option.
> > 
> > I view the inability to do this as a very serious regression from CDI. I have a
> > plugin that I ship on CDI that does everything I need to do. It plugs in to a
> > standard CDT distribution *and* one with a customised CDI interface.
> 
> CDI is still available in CDT, if this is a blocking issue for you.

The existing plugin is working fine in Helios, using CDI, but we need to move to DSF.. But the customers want to be able to use some of the new features available in DSF debugging - and have bought into the fact that DSF is the future. 

To me, the ability to be able to add some new plugin to an existing debugger implementation is an 'obvious' requirement. GDB provides all the facilities I need (basically, to be able to read memory), but I need to be able to add a new debug view to display it. Having to supply a custom debugger, just to add a new view seems crazy...
Comment 10 Anton Leherbauer CLA 2011-01-20 06:26:39 EST
Maybe I am missing something, but why is it impossible to add a service to a DSF session?

MyService svc = new MyService(session);
svc.initialize(new RequestMonitor(){...});
svc.register(...);
Comment 11 Marc Khouzam CLA 2011-01-20 10:14:24 EST
(In reply to comment #10)
> Maybe I am missing something, but why is it impossible to add a service to a
> DSF session?
> 
> MyService svc = new MyService(session);
> svc.initialize(new RequestMonitor(){...});
> svc.register(...);

!!!
Could it be that easy?  Does look like it.
Comment 12 Derek Morris CLA 2011-01-20 11:14:18 EST
(In reply to comment #10)
> Maybe I am missing something, but why is it impossible to add a service to a
> DSF session?
> 
> MyService svc = new MyService(session);
> svc.initialize(new RequestMonitor(){...});
> svc.register(...);

That looks easy, but how do I get access to some of the other services that are running, such as the commandfactory for the session I am running in? I need to be able to post commands to GDB. I also need to be 'informed' when the selected item changes (e.g. user selects a different function on the call stack). 

Come to that, where do I get "session" from?

Sorry but none of this is documented - at least I can't find any of it!
Comment 13 John Cortell CLA 2011-01-20 11:16:56 EST
(In reply to comment #10)
> Maybe I am missing something, but why is it impossible to add a service to a
> DSF session?
> 
> MyService svc = new MyService(session);
> svc.initialize(new RequestMonitor(){...});
> svc.register(...);

Nice! That does seems to work. I created a little project that tests your
theory and attached it.

Some caveats:

1. The example makes no attempt to shutdown the service. To do this, one would
use DsfSession.addSessionEndedListener() and then shutdown the service from the
listener, assuming it's not too late at that point.

2. To get services from the DsfSession, the plugin needs to access the core dsd
plugin's bundle context. This example does that using an internal class. Not
sure if there's a cleaner way to do that.
Comment 14 John Cortell CLA 2011-01-20 11:18:56 EST
Created attachment 187206 [details]
example plugin showing how to add and access a DSF service into an arbitrary DSF session

This plugin adds a IMyService top-level menu. Launch a dsf-gdb session, then use the actions.
Comment 15 John Cortell CLA 2011-01-20 11:24:01 EST
(In reply to comment #13)
> 1. The example makes no attempt to shutdown the service. To do this, one would
> use DsfSession.addSessionEndedListener() and then shutdown the service from the
> listener, assuming it's not too late at that point.
> 
> 2. To get services from the DsfSession, the plugin needs to access the core dsd
> plugin's bundle context. This example does that using an internal class. Not
> sure if there's a cleaner way to do that.

Another caveat:

3. This example just uses the first active DSF session. A production plugin would likely want to find the session associated with the active debug context. This is left as an exercise for the reader :-)
Comment 16 John Cortell CLA 2011-01-20 11:46:49 EST
Created attachment 187211 [details]
example plugin showing how to add and access a DSF service into an arbitrary DSF session

Updated to avoid tracker leak (dispose() wasn't being called). Also, avoid NPE when user invokes Use Service before Add Service
Comment 17 Marc Khouzam CLA 2011-01-21 13:35:07 EST
(In reply to comment #16)
> Created attachment 187211 [details]
> example plugin showing how to add and access a DSF service into an arbitrary
> DSF session
> 
> Updated to avoid tracker leak (dispose() wasn't being called). Also, avoid NPE
> when user invokes Use Service before Add Service

This is really cool!
We should commit it in some examples plugin.  Probably in DSF.

To properly address the two below issues:
 
> This plugin adds a IMyService top-level menu. Launch a dsf-gdb session, then
> use the actions.

> 3. This example just uses the first active DSF session. A production plugin
> would likely want to find the session associated with the active debug context.

I suggest we do:

Instead of using an action to trigger the new service, we should use a ILaunchesListener which would look for a new launch that is an instance of DsfLaunch (for any DSF debugger), GdbLaunch for DSF-GDB.  Once such a launch appears, the session number can be fetched from the launch and the new service added.
Comment 18 John Cortell CLA 2011-01-21 15:31:23 EST
(In reply to comment #17)
> I suggest we do:
> 
> Instead of using an action to trigger the new service, we should use a
> ILaunchesListener which would look for a new launch that is an instance of
> DsfLaunch (for any DSF debugger), GdbLaunch for DSF-GDB.  Once such a launch
> appears, the session number can be fetched from the launch and the new service
> added.

Definitely. The auto-registration of the service came to mind when I realized a session listener was needed to shutdown the service. But at that point, I felt I'd already invested too much time in the example and guilt forced me to stop and return to my real work :-) Also, it occurred to me that a custom view may want to register the service on the fly long after the session started--e.g., when the user opens the view. Both are valid use cases.

Note I'm assuming you meant 'register' instead of 'trigger'.
Comment 19 Marc Khouzam CLA 2011-01-21 15:35:09 EST
(In reply to comment #18)
> I felt I'd already invested too much time in the example and guilt forced
> me to stop and return to my real work :-)

I get that.  But what you already provided was valuable.
Maybe Derek can post snippets of his solution once he gets it to work.

> Also, it occurred to me that a custom view may
> want to register the service on the fly long after the session started--e.g.,
> when the user opens the view. Both are valid use cases.

That is a good point.

> Note I'm assuming you meant 'register' instead of 'trigger'.

Right.
Comment 20 tony burton CLA 2011-08-25 10:08:37 EDT
I am trying to do the same thing as Derek Morris and so far I've done the following (documented in http://wiki.eclipse.org/CDT/cdt-debug-dsf-gdb-extensibility):

   1.  sub-class any MI command class and its output class to add your changes. These are found in org.eclipse.cdt.dsf.mi.service.command.commands and org.eclipse.cdt.dsf.mi.service.command.output
   2. sub-class org.eclipse.cdt.dsf.mi.service.command.CommandFactory and override/add your necessary changes.
   3. sub-class org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate and override GdbLaunchDelegate#newServiceFactory().
   4. sub-class org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactory and override GdbDebugServicesFactory#createCommandControl() to pass in your own command factory.

Could you please tell what to do now?

Derek did you solve this one and if so could you post an example?

Thanks in advance
Comment 21 Marc Khouzam CLA 2011-08-25 10:45:41 EDT
(In reply to comment #20)
> I am trying to do the same thing as Derek Morris and so far I've done the
> following (documented in
> http://wiki.eclipse.org/CDT/cdt-debug-dsf-gdb-extensibility):
> 
>    1.  sub-class any MI command class and its output class to add your changes.
> These are found in org.eclipse.cdt.dsf.mi.service.command.commands and
> org.eclipse.cdt.dsf.mi.service.command.output
>    2. sub-class org.eclipse.cdt.dsf.mi.service.command.CommandFactory and
> override/add your necessary changes.
>    3. sub-class org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate and
> override GdbLaunchDelegate#newServiceFactory().
>    4. sub-class org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactory and
> override GdbDebugServicesFactory#createCommandControl() to pass in your own
> command factory.
> 
> Could you please tell what to do now?

What are you trying to achieve?  Do you want to modify services or add new ones but re-use the same launch configuration that is provided in CDT?
Comment 22 tony burton CLA 2011-08-25 11:06:43 EDT
(In reply to comment #21)
> (In reply to comment #20)
> > I am trying to do the same thing as Derek Morris and so far I've done the
> > following (documented in
> > http://wiki.eclipse.org/CDT/cdt-debug-dsf-gdb-extensibility):
> > 
> >    1.  sub-class any MI command class and its output class to add your changes.
> > These are found in org.eclipse.cdt.dsf.mi.service.command.commands and
> > org.eclipse.cdt.dsf.mi.service.command.output
> >    2. sub-class org.eclipse.cdt.dsf.mi.service.command.CommandFactory and
> > override/add your necessary changes.
> >    3. sub-class org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate and
> > override GdbLaunchDelegate#newServiceFactory().
> >    4. sub-class org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactory and
> > override GdbDebugServicesFactory#createCommandControl() to pass in your own
> > command factory.
> > 
> > Could you please tell what to do now?
> 
> What are you trying to achieve?  Do you want to modify services or add new ones

Thanks for your reply. I am trying to add a new command to send to gdb. We have written a remote server similar to gdbserver and we want to send a custom command using the GDB Monitor command. Although I have subclassed the classes as documented for extending dsf-gdb, I don't know how to attach to the dsf service, call my new methods and send the command.
Comment 23 Marc Khouzam CLA 2011-08-25 13:18:31 EDT
> Thanks for your reply. I am trying to add a new command to send to gdb. We have
> written a remote server similar to gdbserver and we want to send a custom
> command using the GDB Monitor command.

Ok, makes sense.

> Although I have subclassed the classes
> as documented for extending dsf-gdb, I don't know how to attach to the dsf
> service, call my new methods and send the command.

You need some way to trigger your new functionality, the way for the user to send that command from the UI.  That should call the DSF service that will send the new MI command.  The DSF service will either be a sub-class or an existing service or a new service, depending on what you have done.

Do you have all that?
Comment 24 tony burton CLA 2011-08-26 10:07:14 EDT
Created attachment 202233 [details]
my extend DFS-GDB program
Comment 25 tony burton CLA 2011-08-26 10:12:10 EDT
(In reply to comment #24)
> Created attachment 202233 [details]
> my extend DFS-GDB program

sorry my comments were lost concerning the attachment and answer to your question:

> You need some way to trigger your new functionality, the way for the user to
> send that command from the UI.  That should call the DSF service that will send
> the new MI command.  The DSF service will either be a sub-class or an existing
> service or a new service, depending on what you have done.

> Do you have all that?

Yes I think so. (Please bare with me, I'm new to the fascinating world of Java, Eclipse and Eclipse plug-ins!).
I have attached my program with comments showing where I'm lost.

Thanks again for your help.
Comment 26 Marc Khouzam CLA 2011-08-26 15:57:40 EDT
(In reply to comment #25)

> I have attached my program with comments showing where I'm lost.
> 
> Thanks again for your help.

What you can do is look at how another MI command is used.  For example MIExecContinue.  You can see how the GDBRunControl_7_0 (MIRunControl) sends that command to GDB.

That is the patter to follow.  The UI wants to do something, so it calls a DSF service method.  That service sends the proper MI command to GDB and returns the result to the UI.

So, in your case you'll need to have some service call CommandFactory.createMIAimBreak(), and have your UI command call that service.

The last missing step will be to use MyGdbLaunchDelagate instead of GdbLaunchDelagate, to do that, you'll need to create a launch configuration for that delegate.  Look at the plugin.xml file that references GdbLaunchDelagate and do the same thing.
Comment 27 tony burton CLA 2011-08-30 04:04:19 EDT
(In reply to comment #26)
> (In reply to comment #25)
> 
> > I have attached my program with comments showing where I'm lost.
> > 
> > Thanks again for your help.
> 
> What you can do is look at how another MI command is used.  For example
> MIExecContinue.  You can see how the GDBRunControl_7_0 (MIRunControl) sends
> that command to GDB.
> 
> That is the patter to follow.  The UI wants to do something, so it calls a DSF
> service method.  That service sends the proper MI command to GDB and returns
> the result to the UI.
> 
> So, in your case you'll need to have some service call
> CommandFactory.createMIAimBreak(), and have your UI command call that service.
> 
> The last missing step will be to use MyGdbLaunchDelagate instead of
> GdbLaunchDelagate, to do that, you'll need to create a launch configuration for
> that delegate.  Look at the plugin.xml file that references GdbLaunchDelagate
> and do the same thing.

Thank you very much, Marc, for your suggestions. I have been away a few days so today I'll try those suggestions out and inform you of my progress.
Comment 28 tony burton CLA 2011-08-31 05:26:23 EDT
(In reply to comment #27)
> (In reply to comment #26)
> > (In reply to comment #25)
> > 
> > > I have attached my program with comments showing where I'm lost.
> > > 
> > > Thanks again for your help.
> > 
> > What you can do is look at how another MI command is used.  For example
> > MIExecContinue.  You can see how the GDBRunControl_7_0 (MIRunControl) sends
> > that command to GDB.
> > 
> > That is the patter to follow.  The UI wants to do something, so it calls a DSF
> > service method.  That service sends the proper MI command to GDB and returns
> > the result to the UI.
> > 
> > So, in your case you'll need to have some service call
> > CommandFactory.createMIAimBreak(), and have your UI command call that service.
> > 
> > The last missing step will be to use MyGdbLaunchDelagate instead of
> > GdbLaunchDelagate, to do that, you'll need to create a launch configuration for
> > that delegate.  Look at the plugin.xml file that references GdbLaunchDelagate
> > and do the same thing.
> 
> Thank you very much, Marc, for your suggestions. I have been away a few days so
> today I'll try those suggestions out and inform you of my progress.

I am trying to subclass GdbLaunchDelegate with MyGdbLaunchDelegate to override the newServiceFactory() method. I have put the following in the plugin.xml file:

<extension
  	name="DSF Launch Types"
         point="org.eclipse.debug.core.launchConfigurationTypes">
      <launchConfigurationType
            delegate="com.test.commands.MyGdbLaunchDelegate"
            id="com.test.commands.MyGdbLaunchDelegate"
            modes="run,debug"
            name="%launchConfigurationType.name"
            public="true"
            sourceLocatorId="org.eclipse.cdt.debug.core.sourceLocator"
            sourcePathComputerId="org.eclipse.cdt.debug.core.sourcePathComputer">
      </launchConfigurationType>
      <launchConfigurationType
            delegate="com.test.commands.MyGdbLaunchDelegate"
            id="com.test.commands.MyRemoteGdbLaunchDelegate"
            modes="run,debug"
            name="%launchConfigurationType.name.0"
            public="true"
            sourceLocatorId="org.eclipse.cdt.debug.core.sourceLocator"
            sourcePathComputerId="org.eclipse.cdt.debug.core.sourcePathComputer">
      </launchConfigurationType>
   </extension>

What more should I do? Should I add a service (as in the dsfInjector example), then instantiate MyGdbLaunchDelegate using that service? Sorry but I am a bit confused. If there were a simple example it would be easier for me.
Thanks again
Comment 29 Marc Khouzam CLA 2011-08-31 12:22:41 EDT
(In reply to comment #28)

> <extension
>       name="DSF Launch Types"
>          point="org.eclipse.debug.core.launchConfigurationTypes">

You also need to add an extension to
"org.eclipse.debug.core.launchDelegates"
Something like:
    <extension point="org.eclipse.debug.core.launchDelegates">
      <launchDelegate
            id="org.eclipse.cdt.dsf.gdb.launch.localCLaunch"
            type="org.eclipse.cdt.launch.applicationLaunchType"
            modes="debug"
            delegate="com.test.commands.MyGdbLaunchDelegate"
            name="New name you want"
            delegateDescription="your description"
            sourceLocatorId="org.eclipse.cdt.debug.core.sourceLocator"
            sourcePathComputerId="org.eclipse.cdt.debug.core.sourcePathComputer">
      </launchDelegate>


> What more should I do? Should I add a service (as in the dsfInjector example),
> then instantiate MyGdbLaunchDelegate using that service? 

MyGdbLaunchDelegate will be created by the extension of "org.eclipse.debug.core.launchDelegates".  Then, when you go to Run->Debug configurations... you should have a new choice along the C/C++ Application one and the others.  I should have the name that maps to %launchConfigurationType.name.0
Comment 30 tony burton CLA 2011-09-02 10:18:23 EDT
(In reply to comment #29)
> (In reply to comment #28)
> 
> > <extension
> >       name="DSF Launch Types"
> >          point="org.eclipse.debug.core.launchConfigurationTypes">
> 
> You also need to add an extension to
> "org.eclipse.debug.core.launchDelegates"
> Something like:
>     <extension point="org.eclipse.debug.core.launchDelegates">
>       <launchDelegate
>             id="org.eclipse.cdt.dsf.gdb.launch.localCLaunch"
>             type="org.eclipse.cdt.launch.applicationLaunchType"
>             modes="debug"
>             delegate="com.test.commands.MyGdbLaunchDelegate"
>             name="New name you want"
>             delegateDescription="your description"
>             sourceLocatorId="org.eclipse.cdt.debug.core.sourceLocator"
>            
> sourcePathComputerId="org.eclipse.cdt.debug.core.sourcePathComputer">
>       </launchDelegate>
> 
> 
> > What more should I do? Should I add a service (as in the dsfInjector example),
> > then instantiate MyGdbLaunchDelegate using that service? 
> 
> MyGdbLaunchDelegate will be created by the extension of
> "org.eclipse.debug.core.launchDelegates".  Then, when you go to Run->Debug
> configurations... you should have a new choice along the C/C++ Application one
> and the others.  I should have the name that maps to
> %launchConfigurationType.name.0

Great! I now have a new launch config and the overidden newServiceFactory() method is called in MyGdbLaunchDelegate which in turn instantiates the class MyGdbLaunchDelegate which in turn instantiates the class MyGdbDebugServicesFactory. The createCommandControl method is overidden and it returns an instance of GDBControl_7_2 containing MyCommandFactory. But I'm still a bit confused. How do I access this later? Should I create a new service or find the reference to an existing one, and then how is the command factory connected to the service? 
I suppose I'll have an instruction like this:

service.createMIAimBreak(IDMContext, 5, 0x81956);

But where do I obtain the IDMContext?

Thanks again, I think I'm not too far from success!
Comment 31 Marc Khouzam CLA 2011-09-02 11:03:10 EDT
(In reply to comment #30)

> Great! I now have a new launch config and the overidden newServiceFactory()
> method is called in MyGdbLaunchDelegate which in turn instantiates the class
> MyGdbDebugServicesFactory. The createCommandControl method is overidden and it
> returns an instance of GDBControl_7_2 containing MyCommandFactory. 

Perfect.

> But I'm
> still a bit confused. How do I access this later? Should I create a new service
> or find the reference to an existing one, and then how is the command factory
> connected to the service? 

A service provides a higher-level interface, like createBreakpoint(), which in turn does what it needs, like call commandFactory.createMIinsertBreakpoint().

> I suppose I'll have an instruction like this:
> 
> service.createMIAimBreak(IDMContext, 5, 0x81956);

Note exactly.
Look at how we use other MI commands.
Comment 32 tony burton CLA 2011-09-06 10:51:00 EDT
(In reply to comment #31)
> (In reply to comment #30)
> 
> > Great! I now have a new launch config and the overidden newServiceFactory()
> > method is called in MyGdbLaunchDelegate which in turn instantiates the class
> > MyGdbDebugServicesFactory. The createCommandControl method is overidden and it
> > returns an instance of GDBControl_7_2 containing MyCommandFactory. 
> 
> Perfect.
> 
> > But I'm
> > still a bit confused. How do I access this later? Should I create a new service
> > or find the reference to an existing one, and then how is the command factory
> > connected to the service? 
> 
> A service provides a higher-level interface, like createBreakpoint(), which in
> turn does what it needs, like call commandFactory.createMIinsertBreakpoint().
> 
> > I suppose I'll have an instruction like this:
> > 
> > service.createMIAimBreak(IDMContext, 5, 0x81956);
> 
> Note exactly.
> Look at how we use other MI commands.

Hi, have been debugging the MIExecContinue() command. I see how GDBRunControl calls:

fConnection.queueCommand(fCommandFactory.createMIExecContinue(context), new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
			@Override
			protected void handleFailure() {
				threadState.fResumePending = false;
				super.handleFailure();
			}
		});
		
I suppose I must subclass GDBRunControl and add a similar method to call my MIAimBreak() command, is this correct?

Also when I set a breakpoint on the MIExecContinue() command, the debug trace only takes me a few calls back, then I fall into classes without source code. Is there a way of setting a breakpoint on the command handler that the resume button on the debug toolbar calls? In that way I could debug from there onwards.

Thanks for your help.
Comment 33 Marc Khouzam CLA 2011-09-06 12:58:43 EDT
(In reply to comment #32)

> Hi, have been debugging the MIExecContinue() command. I see how GDBRunControl
> calls:
> 
> fConnection.queueCommand(fCommandFactory.createMIExecContinue(context), new
> DataRequestMonitor<MIInfo>(getExecutor(), rm) {
>             @Override
>             protected void handleFailure() {
>                 threadState.fResumePending = false;
>                 super.handleFailure();
>             }
>         });
> 
> I suppose I must subclass GDBRunControl and add a similar method to call my
> MIAimBreak() command, is this correct?

You need to choose the service that best fit the functionality you will be providing with MIAimBreak().  It may be RunControl or whichever one you choose.  Or you may decide to create a new service altogether, if you find it would make more sense.

 
> Also when I set a breakpoint on the MIExecContinue() command, the debug trace
> only takes me a few calls back, then I fall into classes without source code.
> Is there a way of setting a breakpoint on the command handler that the resume
> button on the debug toolbar calls? In that way I could debug from there
> onwards.

Because of DSF's asynchronous model, you can't see the entire stack trace at once.  Code is being invoked asynchronously.  What you can do is go to the last level of the stack that still makes sense and set a breakpoint there.  Then run again.
Comment 34 tony burton CLA 2011-09-07 06:08:02 EDT
(In reply to comment #33)
> (In reply to comment #32)
> 
> > Hi, have been debugging the MIExecContinue() command. I see how GDBRunControl
> > calls:
> > 
> > fConnection.queueCommand(fCommandFactory.createMIExecContinue(context), new
> > DataRequestMonitor<MIInfo>(getExecutor(), rm) {
> >             @Override
> >             protected void handleFailure() {
> >                 threadState.fResumePending = false;
> >                 super.handleFailure();
> >             }
> >         });
> > 
> > I suppose I must subclass GDBRunControl and add a similar method to call my
> > MIAimBreak() command, is this correct?
> 
> You need to choose the service that best fit the functionality you will be
> providing with MIAimBreak().  It may be RunControl or whichever one you choose.
>  Or you may decide to create a new service altogether, if you find it would
> make more sense.
> 
> 
> > Also when I set a breakpoint on the MIExecContinue() command, the debug trace
> > only takes me a few calls back, then I fall into classes without source code.
> > Is there a way of setting a breakpoint on the command handler that the resume
> > button on the debug toolbar calls? In that way I could debug from there
> > onwards.
> 
> Because of DSF's asynchronous model, you can't see the entire stack trace at
> once.  Code is being invoked asynchronously.  What you can do is go to the last
> level of the stack that still makes sense and set a breakpoint there.  Then run
> again.

OK, thanks. I'll try and use an existing service and see if that works. Yes, I had forgotten that DSF runs several threads asynchronously, I'll try and find the plugin.xml file for the debug toolbar plug-in and find the command handler from there...
Comment 35 Marc Khouzam CLA 2011-09-07 09:28:37 EDT
(In reply to comment #34)

> OK, thanks. I'll try and use an existing service and see if that works. Yes, I
> had forgotten that DSF runs several threads asynchronously, I'll try and find
> the plugin.xml file for the debug toolbar plug-in and find the command handler
> from there...

Actually, DSF runs all the services on a single thread: the DSF Executor.  However, to avoid locking that thread, the calls are asynchronous using the RequestMonitor class.  You can see examples all over the place.
Comment 36 tony burton CLA 2011-09-09 11:58:05 EDT
(In reply to comment #35)
> (In reply to comment #34)
> 
> > OK, thanks. I'll try and use an existing service and see if that works. Yes, I
> > had forgotten that DSF runs several threads asynchronously, I'll try and find
> > the plugin.xml file for the debug toolbar plug-in and find the command handler
> > from there...
> 
> Actually, DSF runs all the services on a single thread: the DSF Executor. 
> However, to avoid locking that thread, the calls are asynchronous using the
> RequestMonitor class.  You can see examples all over the place.

Thanks for correcting me. I have been reading about DSF and request monitors. Have also looked at the PDA example, but it's all a little bit difficult for me to understand - I'm beginning to get the picture, but still need a few pointers if you have the time.

I have tried to do the following:


public class MyGdbRunControl extends GDBRunControl_7_0_NS {

	private ICommandControlService fConnection;
	private CommandFactory fCommandFactory;
	
	public MyGdbRunControl(DsfSession session) {
		super(session);
		// TODO Auto-generated constructor stub
	}
	
	private void sendAIMBreak(IMIExecutionDMContext context, final RequestMonitor rm) {
		fConnection = getServicesTracker().getService(ICommandControlService.class);
		fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory();
		
		fConnection.queueCommand(fCommandFactory.createMIAimBreak((IDMContext)DsfPlugin.getBundleContext(), 5, 0x81956), new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
			@Override
			protected void handleFailure() {
				super.handleFailure();
			}
		});
	}
}

...but I get an error: "The method createMIAimBreak(IDMContext, int, int) is undefined for the type CommandFactory" although I have subclassed commandFactory:

public class MyCommandFactory extends CommandFactory {

	public ICommand<MIInfo> createMIAimBreak(IDMContext ctx, int thread, int addr) {
		return new MIAimBreak<MIInfo>(ctx, thread, addr, 0, 0, 1, 0);
	}
	
	public ICommand<MIInfo> createMIAimBreak(IDMContext ctx, int thread, int addr, int count) {
		return new MIAimBreak<MIInfo>(ctx, thread, addr, 0, 0, count, 0);
	}
	
	public ICommand<MIInfo> createMIAimBreak(IDMContext ctx, int thread, long addr, int addrVar, int data) {
		return new MIAimBreak<MIInfo>(ctx, thread, addr, addrVar, data, 1, 0);
	}
	
	public ICommand<MIInfo> createMIAimBreak(IDMContext ctx, int thread, long addr, int addrVar, int data, int count) {
		return new MIAimBreak<MIInfo>(ctx, thread, addr, addrVar, data, count, 0);
	}
	
	public ICommand<MIInfo> createMIBreakEnable(IBreakpointsTargetDMContext ctx, int[] array) {
		return new MIBreakEnable(ctx, array);
	}
}

...and GdbDebugServicesFactory:


public class MyGdbDebugServicesFactory extends GdbDebugServicesFactory {
	public MyGdbDebugServicesFactory(String version) {
		super(version);
	}
	
	protected ICommandControl createCommandControl(DsfSession session, ILaunchConfiguration config) {
		return new GDBControl_7_0(session,config, new MyCommandFactory());
	}
	
	protected IRunControl createRunControlService(DsfSession session) {
        return new MyGdbRunControl(session); 
    }
}

What am I doing wrong? Thanks again and have a good weekend.
Comment 37 Marc Khouzam CLA 2011-09-09 13:05:15 EDT
(In reply to comment #36)

fConnection.queueCommand(fCommandFactory.createMIAimBreak((IDMContext)DsfPlugin.getBundleContext(),

> What am I doing wrong? Thanks again and have a good weekend.

DsfPlugin.getBundleContext() is not an IDMContext.
You should use the parameter 'context' passed to sendAIMBreak()
Comment 38 tony burton CLA 2011-09-12 12:28:30 EDT
(In reply to comment #37)
> (In reply to comment #36)
> 
> fConnection.queueCommand(fCommandFactory.createMIAimBreak((IDMContext)DsfPlugin.getBundleContext(),
> 
> > What am I doing wrong? Thanks again and have a good weekend.
> 
> DsfPlugin.getBundleContext() is not an IDMContext.
> You should use the parameter 'context' passed to sendAIMBreak()

Hi, thanks for your answer, there were other problems too which I have corrected. But now I am trying to send my command to gdb in MyGdbLaunchDelegate. I have overriden the launch method as follows:

public void launch( ILaunchConfiguration config, String mode, ILaunch launch, IProgressMonitor monitor ) throws CoreException {
		super.launch(config, mode, launch, monitor);
		
		// get the active debug sessions...
		DsfSession[] sessions = DsfSession.getActiveSessions();
		// check that one has been created...
		if (sessions.length < 1)
			// TODO: treat error
			return; 
		// We always have only one session...
		final DsfSession session = sessions[0];

		// get executor for this session...
        final DsfExecutor executor = session.getExecutor();
        
        // send command...
		executor.execute(new Runnable(){
			public void run() {
		        MyGdbRunControl mgrc = new MyGdbRunControl(session);
		        RequestMonitor rm = new RequestMonitor(executor, null);
		        mgrc.sendAIMBreak(rm);
			}
		});
	}

I would like to use the GDBRunControl service and have subclassed GDBRunControl_7_0_NS:

public class MyGdbRunControl extends GDBRunControl_7_0_NS {

	private ICommandControlService fConnection;
	private MyCommandFactory fCommandFactory;
	
	public MyGdbRunControl(DsfSession session) {
		super(session);
		// TODO Auto-generated constructor stub
	}
	
	public void sendAIMBreak(final RequestMonitor rm) {
		fConnection = getServicesTracker().getService(ICommandControlService.class);
		fCommandFactory = (MyCommandFactory) getServicesTracker().getService(IMICommandControl.class).getCommandFactory();
		
		fConnection.queueCommand(fCommandFactory.createMIAimBreak(fConnection.getContext(), 5, 0x81956), new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
			@Override
			protected void handleFailure() {
				super.handleFailure();
			}
		});
	}
}

When I get to :
fConnection = getServicesTracker().getService(ICommandControlService.class);

and try to step over, I don't reach the next instruction, can you tell me why? (I got this line from the GdbRunControl Initialize method)

Could you please tell me if I am doing all this right.

Thanks again.
Comment 39 Marc Khouzam CLA 2011-09-12 12:59:35 EDT
(In reply to comment #38)
> public void launch( ILaunchConfiguration config, String mode, ILaunch launch,
> IProgressMonitor monitor ) throws CoreException {
>         super.launch(config, mode, launch, monitor);
> 
>         // get the active debug sessions...
>         DsfSession[] sessions = DsfSession.getActiveSessions();
>         // check that one has been created...
>         if (sessions.length < 1)
>             // TODO: treat error
>             return; 
>         // We always have only one session...
>         final DsfSession session = sessions[0];

You could have more than one session if you launch more than one at the same time.

>         // get executor for this session...
>         final DsfExecutor executor = session.getExecutor();
> 
>         // send command...
>         executor.execute(new Runnable(){
>             public void run() {
>                 MyGdbRunControl mgrc = new MyGdbRunControl(session);

This is wrong, your service should already exist because it was launched by the ServicesLaunchSequence class.

>                 RequestMonitor rm = new RequestMonitor(executor, null);
>                 mgrc.sendAIMBreak(rm);
>             }
>         });
>     }
> 
> I would like to use the GDBRunControl service and have subclassed
> GDBRunControl_7_0_NS:
> 
> public class MyGdbRunControl extends GDBRunControl_7_0_NS {
> 
>     private ICommandControlService fConnection;
>     private MyCommandFactory fCommandFactory;
> 
>     public MyGdbRunControl(DsfSession session) {
>         super(session);
>         // TODO Auto-generated constructor stub
>     }
> 
>     public void sendAIMBreak(final RequestMonitor rm) {
>         fConnection =
> getServicesTracker().getService(ICommandControlService.class);
>         fCommandFactory = (MyCommandFactory)
> getServicesTracker().getService(IMICommandControl.class).getCommandFactory();
> 
>        
> fConnection.queueCommand(fCommandFactory.createMIAimBreak(fConnection.getContext(),
> 5, 0x81956), new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
>             @Override
>             protected void handleFailure() {
>                 super.handleFailure();
>             }
>         });
>     }
> }
> 
> When I get to :
> fConnection = getServicesTracker().getService(ICommandControlService.class);
> 
> and try to step over, I don't reach the next instruction, can you tell me why?
> (I got this line from the GdbRunControl Initialize method)

I don't know, it should work.
Step in the getService method and see what is happening.
Comment 40 tony burton CLA 2011-09-13 09:41:48 EDT
> >         final DsfSession session = sessions[0];
> 
> You could have more than one session if you launch more than one at the same
> time.

I meant that for our needs we will only have one session

> >                 MyGdbRunControl mgrc = new MyGdbRunControl(session);
> 
> This is wrong, your service should already exist because it was launched by the
> ServicesLaunchSequence class.

Correct, It was created in the method createRunControlService() in the class MyGdbDebugServicesFactory. So I modified the code as follows:


public class UseService extends AbstractHandler {
	public Object execute(ExecutionEvent event) throws ExecutionException {
		// get the active debug sessions...
		DsfSession[] sessions = DsfSession.getActiveSessions();
		// check that one has been created...
		if (sessions.length < 1)
			// TODO: treat error
			return null; 
		// We always have only one session...
		final DsfSession session = sessions[0];
		
		// get executor for this session...
	    final DsfExecutor executor = session.getExecutor();
	    
	    final IRunControl rc = MyGdbDebugServicesFactory.getRunControlService();
	    
	    // send command...
		executor.execute(new Runnable(){
			public void run() {
		        RequestMonitor rm = new RequestMonitor(executor, null);
		        MyGdbRunControl test = (MyGdbRunControl)rc;
		        test.sendAIMBreak(rm);
			}
		});
		return null;
	}
}

...and in MyGdbDebugServicesFactory which instantiates MyGdbRunControl I save the IRunControl in an attribute which I can obtain later with getRunControlService():

public class MyGdbDebugServicesFactory extends GdbDebugServicesFactory {
	private static IRunControl fRunControl;
	
	public MyGdbDebugServicesFactory(String version) {
		super(version);
	}
	
	protected ICommandControl createCommandControl(DsfSession session, ILaunchConfiguration config) {
		return new GDBControl_7_0(session, config, new MyCommandFactory());
	}
	
	protected IRunControl createRunControlService(DsfSession session) {
		fRunControl = new MyGdbRunControl(session);
        return fRunControl; 
    }
	
	public static IRunControl getRunControlService() {
		return fRunControl; 
	}
}
...and now it works!!! Of course I'm not finished yet, cos I have to read the reply, but I'm very happy to have made this much work. Thanks a lot for your help. Once I have a fully working example I will post it here.
Comment 41 tony burton CLA 2011-09-16 09:16:38 EDT
Hi Marc. Have successfully created other gdb commands and have created dialogs to enter parameters for those commands but am stuck trying to find out how to read the results GDB sends back.

One of the things I would like to do is verify if a program is running in the target. I thought of doing this with a thread-info command:

// Try to select thread 2. As thread 1 is the inferior (AimGdbServer), if an application is running, 
// the thread 2 will be selected, if not the reply will be: "Thread ID 2 not known."

DsfSession[] sessions = DsfSession.getActiveSessions();
		final DsfSession session = sessions[0];
	    final DsfExecutor executor = session.getExecutor();
	    
	    final IRunControl rc = MyGdbDebugServicesFactory.getRunControlService();
		executor.execute(new Runnable(){
			public void run() {
		        RequestMonitor rm = new RequestMonitor(executor, null);
		        MyGdbRunControl test = (MyGdbRunControl)rc;
		        test.sendThreadSelect(rm, 2);
			}
		});

(sendThreadSelect is declared in MyGdbRunControl:


What class should I subclass
Comment 42 tony burton CLA 2011-09-16 09:28:44 EDT
Hi. Have successfully created other gdb commands and have created dialogs to enter parameters for those commands but am stuck trying to find out how to read the results GDB sends back.

One of the things I would like to do is verify if a program is running in the target. I thought of doing this with a thread-select command:

// Try to select thread 2. As thread 1 is the inferior (AimGdbServer), if an application is running, 
// the thread 2 will be selected, if not the reply will be: "Thread ID 2 not known."

DsfSession[] sessions = DsfSession.getActiveSessions();
		final DsfSession session = sessions[0];
	    final DsfExecutor executor = session.getExecutor();
	    
	    final IRunControl rc = MyGdbDebugServicesFactory.getRunControlService();
		executor.execute(new Runnable(){
			public void run() {
		        RequestMonitor rm = new RequestMonitor(executor, null);
		        MyGdbRunControl test = (MyGdbRunControl)rc;
		        test.sendThreadSelect(rm, 2);
			}
		});

sendThreadSelect is declared in MyGdbRunControl:

public void sendAIMThreadSelect(final RequestMonitor rm, int thread) {
		fConnection = getServicesTracker().getService(ICommandControlService.class);
		fCommandFactory = (MyCommandFactory) getServicesTracker().getService(IMICommandControl.class).getCommandFactory();
		fConnection.queueCommand(fCommandFactory.createMIThreadSelect(fConnection.getContext(), thread), new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
			@Override
			protected void handleFailure() {
				super.handleFailure();
			}
		});
	}

What class in org.eclipse.cdt.dsf.mi.service.command.output should I subclass to read the reply? How do I then obtain the result?

I have searched the internet but there is no documentation for all this and debugging is difficult due to the aynchronous nature of dsf, so some more hints would be most appreciated! Thanks - Antony
Comment 43 Marc Khouzam CLA 2011-09-16 10:48:26 EDT
(In reply to comment #42)
> 
>         final IRunControl rc =
> MyGdbDebugServicesFactory.getRunControlService();
>         executor.execute(new Runnable(){
>             public void run() {
>                 RequestMonitor rm = new RequestMonitor(executor, null);
>                 MyGdbRunControl test = (MyGdbRunControl)rc;
>                 test.sendThreadSelect(rm, 2);
>             }
>         });

To get a reference to a service, you should use the DsfServicesTracker.  You can create new instance of DsfServicesTracker anywhere, ask it for the service you want, and then call dispose() on DsfServicesTracker.  Look for examples in CDT, we use it all over.

> sendThreadSelect is declared in MyGdbRunControl:
> 
> public void sendAIMThreadSelect(final RequestMonitor rm, int thread) {
>         fConnection =
> getServicesTracker().getService(ICommandControlService.class);
>         fCommandFactory = (MyCommandFactory)
> getServicesTracker().getService(IMICommandControl.class).getCommandFactory();
>        
> fConnection.queueCommand(fCommandFactory.createMIThreadSelect(fConnection.getContext(),
> thread), new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
>             @Override
>             protected void handleFailure() {
>                 super.handleFailure();
>             }
>         });
>     }
> 
> What class in org.eclipse.cdt.dsf.mi.service.command.output should I subclass
> to read the reply? How do I then obtain the result?

-thread-select does not return anything as output so MIInfo is the right class for the result.  If -thread-select fails, the handleFailure() method will be called and you can use getStatus() to get details on the failure.  If -target-select works, handleSuccess() will be called.
Comment 44 tony burton CLA 2011-09-16 11:22:31 EDT
> To get a reference to a service, you should use the DsfServicesTracker.  You
> can create new instance of DsfServicesTracker anywhere, ask it for the service
> you want, and then call dispose() on DsfServicesTracker.  Look for examples in
> CDT, we use it all over.
> 

OK, I've changed my code to:

 DsfServicesTracker tracker = new DsfServicesTracker(DsfPlugin.getBundleContext(), session.getId());
	    final IRunControl rc = tracker.getService(IRunControl.class);
(but it worked with the method I used. Could this have caused errors?)

> 
> -thread-select does not return anything as output so MIInfo is the right class
> for the result.  If -thread-select fails, the handleFailure() method will be
> called and you can use getStatus() to get details on the failure.  If
> -target-select works, handleSuccess() will be called.

When you say "-thread-select does not return anything as output" what do you mean? (I can see results displayed in the console when I launch this command). What is a command that returns something as output? In that case, what class would I subclass?
Comment 45 Marc Khouzam CLA 2011-09-16 11:28:28 EDT
(In reply to comment #44)
 
>  DsfServicesTracker tracker = new
> DsfServicesTracker(DsfPlugin.getBundleContext(), session.getId());
>         final IRunControl rc = tracker.getService(IRunControl.class);
> (but it worked with the method I used. Could this have caused errors?)

I recommend doing 
MyGdbRunControl rc = tracker.getService(MyGdbRunControl.class);
to avoid casting.
Do a check that rc != null just in case your service does not exist.

I don't know if your way would cause errors...

> > -thread-select does not return anything as output so MIInfo is the right class
> > for the result.  If -thread-select fails, the handleFailure() method will be
> > called and you can use getStatus() to get details on the failure.  If
> > -target-select works, handleSuccess() will be called.
> 
> When you say "-thread-select does not return anything as output" what do you
> mean? (I can see results displayed in the console when I launch this command).
> What is a command that returns something as output? In that case, what class
> would I subclass?

Sorry I just checked and you are right, -thread-select does return an output.  I guess we never used that output so we didn't bother code a result class.  What you need to do is to extend MIInfo.

You can mimic what is done for MIBreakInsert
Comment 46 tony burton CLA 2011-09-16 11:48:52 EDT
> What you need to do is to extend MIInfo.
> 
> You can mimic what is done for MIBreakInsert

Ok thanks, will try that. Have a good weekend (I am in France so it's time to go).
Comment 47 tony burton CLA 2011-10-24 08:23:36 EDT
Created attachment 205805 [details]
An example DSF-GDB command
Comment 48 tony burton CLA 2011-10-24 08:26:05 EDT
As promised I have put together a simple example of adding a new GDB command.

The example is the GDB “info line” command, which returns information on the line of a source file, for example, you would type in a console where GDB is running and connected to a remote target running gdbserver:

line info main.c:142

and GDB will return something like:

Line 142 of "../SRC/main.c" starts at address 0x80197be <MySubRoutine+78> and ends at 0x80197d6 <MySubRoutine+102>

To try it out you must have a target running gdbserver. Run the plugin in debug mode so you can see what's happening. When you do this, Eclipse creates a workspace called  “runtime-EclipseApplication” in the same folder as the workspace your plugin is in. First import a C project to debug, open the Debug Configurations... dialogue and you will see a new launch configuration type - “My Launcher”. Right click it and select New. Fill in the different parameters: in the Main tab, the application you want to debug and the project name and in the debugger tab, (sub tab Main) the path to your GDB debugger and the .gdbinit file and (sub tab Connection) your connection parameters.
	Click the Debug button and MyGdbLaunchDelegate (which extends GdbLaunchDelegate and overrides newServiceFactory() ) will be run. This will instantiate MyCommandFactory which adds the new GDB command.
	In the launch method, a handle to the dsf session is obtained, an executor for the DataRequestMonitor and a handle to the DSF service (In this example I have extended GDBRunControl_7_0_NS to add sendInfoLine()).
	sendInfoLine() queues the command and runs another DataRequestMonitor whose return type is MIInfoLineInfo. Once the command terminates and all went well, the handleSuccess() callback is invoked which recuperates the results using the getResults() method defined in MIInfoLineInfo.java. The calling DataRequestMonitor's data is set with rm.setData(fResult) and is informed the command has terminated with rm.done().
	Back in MyGdbLaunchDelegate, the handleCompleted() callback is invoked and the results from GDB are read using the DataRequestMonitor's method getData(). The address is extracted from the result string and displayed in a dialogue.

Marc, thanks again for helping me understand how this works.