Bug 469763 - [services] Provide top class for DSF-GDB services to allow better extensibility
Summary: [services] Provide top class for DSF-GDB services to allow better extensibility
Status: RESOLVED FIXED
Alias: None
Product: CDT
Classification: Tools
Component: cdt-debug-dsf-gdb (show other bugs)
Version: 8.7.0   Edit
Hardware: PC Linux
: P3 normal (vote)
Target Milestone: 8.8.0   Edit
Assignee: Marc Khouzam CLA
QA Contact: Marc Khouzam CLA
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-06-09 14:06 EDT by Marc Khouzam CLA
Modified: 2015-11-25 09:16 EST (History)
5 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Marc Khouzam CLA 2015-06-09 14:06:12 EDT
Example of current situation:

DSF-GDB provides: 
   GDBRunControl_7_2_NS extends GDBRunControl_7_0_NS

someone specializes this by creating
   MyPrivateGDBRunControl extends GDBRunControl_7_2_NS

The day that CDT adds a new version like
   GDBRunControl_7_9_NS extends GDBRunControl_7_2_NS

the extending class MyPrivateGDBRunControl will not use GDBRunControl_7_9_NS unless the developer notices this new version. Both Mikhail and William confirmed they regularly have this issue.

We propose to insert an empty class that will always be at the top.
Something like
   GDBRunControl_NS_Head extends GDBRunControl_7_2_NS

Extenders would use
   MyPrivateGDBRunControl extends GDBRunControl_NS_Head

When CDT wants to introduce GDBRunControl_7_9_NS, we would do it like this:
   GDBRunControl_NS_Head extends GDBRunControl_7_9_NS extends 
                                             GDBRunControl_7_2_NS

 which will allow MyPrivateGDBRunControl to automatically inherit from the new version.

This change need not break the API and could be done easily anytime.
Comment 1 Eclipse Genie CLA 2015-07-22 10:19:05 EDT
New Gerrit change created: https://git.eclipse.org/r/52358
Comment 2 Marc Khouzam CLA 2015-07-22 15:19:07 EDT
(In reply to Eclipse Genie from comment #1)
> New Gerrit change created: https://git.eclipse.org/r/52358

I've posted a solution.  It also includes a bit of code making use of it part of o.e.cdt.examples.dsf.gdb.

What I realized when using it in the example is that the suggested pattern of extending GDB<service>_HEAD will help whenever a new GDB version comes out with a corresponding new service version, but will affect negatively the extender's support of the previous GDB version for that service.

I don't think we can help both situations; either we help extenders automatically support the newest GDB version at the expense of the old one, or the extender will (automatically) continue to properly support the old version, at the expense of the new one.

Having the different GDB<service>_HEAD available does not force extenders to use them, but gives them the opportunity to do so.  It can then be up to the extender to choose if they want to focus on the latest GDB version or not.

I'd love to hear from extenders if this can help their situation.
Comment 3 Marc Khouzam CLA 2015-07-22 15:25:01 EDT
If this pattern is useful, we could also use it for other versioned classes such as:

StartOrRestartProcessSequence_7_3
MIRunControlEventProcessor_7_0
FinalLaunchSequence_7_7
DebugNewProcessSequence_7_2
CLIEventProcessor_7_7

although I'm not sure how often these cause problems to extenders, as they don't change nearly as often.
Comment 4 Nobody - feel free to take it CLA 2015-07-23 14:38:17 EDT
(In reply to Marc Khouzam from comment #2)
> (In reply to Eclipse Genie from comment #1)
> > New Gerrit change created: https://git.eclipse.org/r/52358
> 
> I've posted a solution.  It also includes a bit of code making use of it
> part of o.e.cdt.examples.dsf.gdb.
> 
> What I realized when using it in the example is that the suggested pattern
> of extending GDB<service>_HEAD will help whenever a new GDB version comes
> out with a corresponding new service version, but will affect negatively the
> extender's support of the previous GDB version for that service.
> 
> I don't think we can help both situations; either we help extenders
> automatically support the newest GDB version at the expense of the old one,
> or the extender will (automatically) continue to properly support the old
> version, at the expense of the new one.
> 
> Having the different GDB<service>_HEAD available does not force extenders to
> use them, but gives them the opportunity to do so.  It can then be up to the
> extender to choose if they want to focus on the latest GDB version or not.
> 
> I'd love to hear from extenders if this can help their situation.

I can't help with this problem, we always use the latest GDB. IMO, the flexibility provided by this solution is sufficient to cover both cases.
Comment 5 Marc Khouzam CLA 2015-07-23 15:22:37 EDT
(In reply to Mikhail Khodjaiants from comment #4)

> I can't help with this problem, we always use the latest GDB. IMO, the
> flexibility provided by this solution is sufficient to cover both cases.

Ok, as long as it helps I think it is worth it.

I'll wait until early next week to see if maybe William as a comment.
Comment 6 William Riley CLA 2015-07-24 08:54:05 EDT
(In reply to Marc Khouzam from comment #5)
> (In reply to Mikhail Khodjaiants from comment #4)
> 
> > I can't help with this problem, we always use the latest GDB. IMO, the
> > flexibility provided by this solution is sufficient to cover both cases.
> 
> Ok, as long as it helps I think it is worth it.
> 
> I'll wait until early next week to see if maybe William as a comment.

I was thinking about this, as we do not always use the latest versions of GDB. It don't think it is a major problem, extenders who need to be able to support older GDB versions (like us) do not need to use the new pattern of “HEAD”, but extenders who don’t no longer need to do as much work. It is probably worth a comment in the example plugin documenting the limitations of "HEAD".

I did think of a few ways to make the “HEAD” pattern more tolerant of that, but I’m not sure they really add much other than extra complexity, as it cannot work in all cases without the extender doing the same work they do now. 

Adding a way for "HEAD" to specify the minimum GDB version required, which could then be checked in the ServicesFactory, would at least allow a cleaner fallback if the extender wants.

If not then I think at least an error needs to be logged if "HEAD" is used with an old GDB version. So it is clear what is happening if this causes debugging issues.
Comment 7 Eclipse Genie CLA 2015-07-24 16:33:53 EDT
WARNING: this patchset contains 1274 new lines of code and may require a Contribution Questionnaire (CQ) if the author is not a committer on the project. Please see:https://wiki.eclipse.org/Project_Management_Infrastructure/Creating_A_Contribution_Questionnaire
Comment 8 Marc Khouzam CLA 2015-07-24 16:41:44 EDT
(In reply to William Riley from comment #6)

> I was thinking about this, as we do not always use the latest versions of
> GDB. It don't think it is a major problem, extenders who need to be able to
> support older GDB versions (like us) do not need to use the new pattern of
> “HEAD”, but extenders who don’t no longer need to do as much work. It is
> probably worth a comment in the example plugin documenting the limitations
> of "HEAD".

I have added a NOTE to every *HEAD file which attempts and pointing this out.  I've also put a comment in the example.  This is in patchset 2.

> Adding a way for "HEAD" to specify the minimum GDB version required, which
> could then be checked in the ServicesFactory, would at least allow a cleaner
> fallback if the extender wants.
> 
> If not then I think at least an error needs to be logged if "HEAD" is used
> with an old GDB version. So it is clear what is happening if this causes
> debugging issues.

I've tried to do that in patchset 3.  It adds some duplicated code to each *_HEAD class which prints to the log when finding an old GDB with a new service version.

I've a grown a little hesitant about the real value of this enhancement.  Will this really help extenders, or could it be a poisoned gift?  You guys have a better appreciation of the reality for extenders, so I'll go with your recommendations.  Do you think this is worth going forward with at all?
Comment 9 Marc Khouzam CLA 2015-07-31 11:31:54 EDT
I've update with patchset 5 which reduces code duplication.  Also, to address my concerns about older GDBs it:

1- prints a warning in the log if an older GDB runs with a *HEAD that is newer
2- is asserts in that same case, which should allow extenders to notice this early if they test with older GDBs, while not bother users

I've tested this with the DSF-GDB example plugin and it works well.

I'm more comfortable with the change as it stands now.

I'll commit next week if Mikhail or William don't express concerns.
Comment 11 Marc Khouzam CLA 2015-08-10 11:02:03 EDT
Merged to master.

Thanks everyone for the feedback.

I've updated
https://wiki.eclipse.org/CDT/cdt-debug-dsf-gdb-extensibility
and put an note in the N&N:
https://wiki.eclipse.org/CDT/User/NewIn88#DSF-GDB_service_extensibility_improvement

I hope this helps extenders.
Comment 12 Bruno Medeiros CLA 2015-11-19 12:03:23 EST
I'm may be late to this, but I've only been able to look into this change now.

I don't see how this *_HEAD change is of any significant help. Sure, MyPrivateGDBRunControl will automatically inherit from a new version that gets added to the GDBRunControl_HEAD hierarchy (say 7.9 for example), but the extender IDE still has to customize the code that *creates* MyPrivateGDBRunControl, and that code is still totally sensitive to GDB version changes. 


For example, if we want to customize GDBControl_7_7 with MyGDBControl, we still have to write code to check against the GDB version, see for example here:

https://github.com/RustDT/RustDT/blob/master/plugin_ide.debug/src-lang/melnorme/lang/ide/debug/core/services/LangDebugServicesExtensions.java#L84

In that code, all I want to do is be able to customize the FinalLaunchSequence for GDB versions 7.7 or above. But I have to jump through a lot of hoops to be able to do that, and even so, the way I did things doesn't work if a GDBControl_7_9 is introduced (as in, my customization won't be applied to a 7.9 GDB) .


If I try to inherit from GDBControl_HEAD, I still have this line that I have to customize: 

  if(GdbDebugServicesFactory.GDB_7_7_VERSION.compareTo(gdbSvcFactory.getVersion()) <= 0)

which in fact will break if a GDBControl_7_9 is introduced, because now GDBControl_HEAD (which now inherits from GDBControl_7_9) will be applied to a debug session using GDB 7.7!


Maybe I'm missing something, but I don't see how this solution helps at all, in fact it seems to be begging for errors to be made.
Comment 13 Marc Khouzam CLA 2015-11-24 16:05:02 EST
(In reply to Bruno Medeiros from comment #12)
> [...]
> Maybe I'm missing something, but I don't see how this solution helps at all,
> in fact it seems to be begging for errors to be made.

If you look at comment #2, it mentions your findings.  In the end using the *_HEAD pattern helps extenders that target the latest GDB all the time.  It is not as good for extenders that want to support older GDB versions.

So the suggested approach is that if you want to keep a service moving upwards with the GDB version, then extend *_HEAD, but for the services that target a specific version, they should not use it.

If we can tweak or extend the solution to cover your case, that would be great, but I don't have a good suggestion right now.
Comment 14 Bruno Medeiros CLA 2015-11-25 08:01:27 EST
It seems to be the whole concept of using class hierarchies to customize the behavior of each GDB service according to the GDB version, is broken. As in, there shouldn't be multiple GDBRunControl/GDBControl/etc for each GDB version, rather there should be just one of each GDBRunControl/GDBControl/etc class, whose *objects* would be configured differently at runtime, according to the actual GDB version. Then, extenders would be able to do stuff like:

if( gdbVersion.major == 7 && gdbVersion.minor == 5 ) {
  // Customize this way for 7.5
} else if( gdbVersion.major == 7 && gdbVersion.minor > 7 ) {
  // Customize this way for 7.7 or above
}

And it would work seamlessly when new GDB versions are introduced.
Is there any particular reason why things aren't done this way?


As it stands, with every new CDT minor version I have to check the GDB support classes of CDT to see if anything new is added. It's not a major problem at the moment for me, but I worry it could become trickier in the future.
Comment 15 Marc Khouzam CLA 2015-11-25 09:16:48 EST
(In reply to Bruno Medeiros from comment #14)

> there shouldn't be multiple GDBRunControl/GDBControl/etc for each GDB
> version, rather there should be just one of each
> GDBRunControl/GDBControl/etc class, whose *objects* would be configured
> differently at runtime, 
>
> Is there any particular reason why things aren't done this way?

When we first started DSF-GDB we made the decision not to go with using versioning -within- a class.  The rationale was that adding support for a new version of GDB would then create risk for the support of an older version.  By using the class hierarchy as we do, when we create a new service class, it has no impact on the existing base services, thus guaranteeing that we don't break older support.  It also comes with its drawbacks, but in the end, I believe it has helped us reduce the maintenance burden for older GDBs, which we naturally tend not to test as much as the most recent one.