A little while ago, someone asked me how Eclipse developers go about debugging the platform. It’s a tricky question to answer because :
- I’m not a contributor to any of the “standard” platform projects
- Even when debugging the platform, it’s usually only one component (help system, jdt, p2) that you’re ever concerned with at any given time.
Normally I’d probably debug/test changes to a platform bundle just like any other Eclipse plugin :
Launch a child Eclipse instance making sure that the necessary bundles in the workspace are enabled (overriding the ones provided by the platform). However, in this case I’ve decided to craft an example where this will simply not work.
This problem is loosely based on an actual issue that has occured in Fedora’s RPM-packaged Eclipse.
We’ll launch our eclipse instance. You’re probably expecting it to load, with some minor problems.
Nope. Basically, most of the “standard” plugins are not loaded, and so we pretty much have a broken platform. There’s no JDT or PDE either, so good luck launching a child Eclipse instance.
What we can do is launch our broken Eclipse (from within the VM) in remote debug mode, and connect to it from our host machine. We’ll also need to have the right sources checked out. Since the problem seems to be related to the loading of bundles a good place to start is with the simpleconfigurator bundle (org.eclipse.equinox.simpleconfigurator). The last bit of preparation involves checking out the sources that match the bundles we’re using. Most projects
will match tag names in their SCM to the version/build qualifier of the released product.
We’ll also open up port 8000 on our VM so that we may connect to it from our host machine. (If security really isn’t an issue, then you can always just bring down iptables)
$ iptables -I INPUT -p TCP -i eth0 --dport 8000 -j ACCEPT
$ eclipse -vmargs -Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000
The vmargs flag passes the arguments that follow it, to the JVM.
Basically we’re waiting for a remote client to connect on port 8000 before launching in debug mode.
Now it’s time to set our breakpoints. The simpleconfigurator is a good place to start, so we’ll set a breakpoint when the bundle gets started.
We go back to our host machine and create our java remote debugging configuration. (Run -> Debug Configurations). From here we scroll down to the “Remote Java Application” and create a new one.
I’ve filled in the VM’s IP, and specified port 8000 as the port to which we will be connecting. The last thing to do is to include the actual source as part of the configuration.
As you can see, the simpleconfigurator bundle has already been included (since the project had focus when we created the configuration) so we don’t need to add anything else. We could always add more sources by clicking on “Add”. Now we just hit “Apply”, and then “Debug”.
If you didn’t open up the port correctly, you’d see something like this.
In the Activator’s start() method, the SimpleConfiguratorImpl constructor doesn’t seem to do too much, but applyConfiguration() seems worth exploring, so we’ll step into this function.
The first thing we see is getConfigurationURL() and it’s worth a quick look . Inside, we see that it parses a String representation of a URL. In fact, by just stepping over most of the conditions, we can see that we’re dealing with a file URL, that has a relative path. More specifically we have file URLs for both a user, and shared configuration, and these configurations hold a list of bundles along with various attributes (name, version, location, etc.). Seems like we’re in the right place.
We can now see that these two configurations (user, and shared) are read, and this is where things get interesting.
This return statement says it all :
return (userBundles.containsAll(sharedBundles)) ? userConfigURL : sharedConfigURL;
If there’s any bundle from the shared configuration, that isn’t present in the user configuration, then the configuration returned is just the shared one. If we copy that expression into the expressions view, we can see it evaluates to false, so we now know this is exactly what’s happening. The Eclipse platform is in fact being properly loaded, but additional installed plug-ins that aren’t part of the shared configuration (like the JDT, PDE, etc.) do not get loaded.
So the question is, which bundle in the shared configuration is not present in the user configuration?
This can be figured out pretty easily. We’ll just add some code to print out which bundle isn’t in the user configuration, and add a new breakpoint on the return call just below. This will allow us to skip to it instead of having to step over all those lines. Now We’ll save the file, and this will restart our debugger from the top of getConfigurationURL().
We resume the program (F8) and it will hit the return call’s breakpoint
So foo.bar.fake is in the shared configuration, but not in the user configuration. Of course there might be many different reasons for why something like this would happen, but for this demonstration, it simply shouldn’t be there, so removing it from the configuration will fix our problem. If we didn’t know the location of the user, or shared configurations, we could easily find them by looking through the defined variables.
We’ll delete this bundle from the shared configuration, and start Eclipse.
Everything is back to normal.
Note that the ugly red background around the minimize/maximize buttons is visible only when using Eclipse from a VM through VNC.