How to run Eclipse so that you can attach an external debugger
To be able to connect a debugger to Eclipse, it has to be started in debug mode. With a Sun VM, the following magic command line does the trick:
-vmargs -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
Note how we use suspend=n to let Eclipse start without waiting for a debugger to connect. For issues during startup, you want to use suspend=y so that the VM waits for you to connect a debugger before it starts running Eclipse. If you start Eclipse by running the executable directly, you can put the options in your eclipse.ini file instead like this:
-consoleLog -vmargs -Xms40m -Xmx256m -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
To attach an external debugger, start another instance of Eclipse and follow these steps:
1. Pick a project containing the Eclipse code you want to debug. We will be debugging what is called a "Remote Java Application". The debugger needs a project when creating the launch configuration because otherwise it would not be able to look up classes and their source code. Make sure that the code in the project matches the exact version of your running Eclipse. If you want to play it safe, I recommend creating a project as follows: Open the Plug-ins view, select all, and pick "Add to Java Search" from the context menu. This will create a project called "External Plug-in Libraries" that matches your currently running Eclipse instance. If the Eclipse instance used for debugging is set up differently, you may have to change your target platform (Window > Preferences > Plug-in Development > Target Platform) before opening the Plug-ins view.
2. Create a launch configuration for a "Remote Java Application": Select the project from step one. Open the debug launch configuration dialog using Run->Debug..., select "Remote Java Application", and click the button for creating a new launch configuration. The default values should be good, with your project from step one, and the port number matching the one from the command line or eclipse.ini.
Adding System.out.println to code that is not checked out
Sometimes you would like to understand the dynamics of code that is not checked out as a project in your workspace. The obvious way to do this is to use breakpoints, but sometimes (especially when debugging UI event processing code) you don't want to interrupt the control flow. It turns out that you can have arbitrary code executed by the debugger by using conditional breakpoints. Create a breakpoint, then right-click on the breakpoint in the left sidebar and select "Breakpoint Properties...". Check "Enable Condition" and put something like the following in the text box below the check box:
System.out.println("Ran some code in the context of " + this); Thread.dumpStack(); return false;
The "return false" is key - the debugger will never actually stop the program execution at this breakpoint, but it will still execute whatever code you put before the "return false".
For information about thread dumps and how you can create them please see the excellent wiki page for How to report a deadlock. Now if only some other Ninja could explain how best to "see" a deadlock in a thread dump that would be great.
Debug "Widget is disposed" exceptions
The problem with debugging "Widget is disposed" exceptions is that at the time the exception happens, it is already too late. To get to the bottom of issues like this, you need to record information about what happened earlier. Here is one useful technique: Get SWT from HEAD and add a field "creationException" to Image.java (or whatever resource has the widget disposed problem), initialized in the constructor as follows:
creationException = new RuntimeException();
Then, you set up an exception breakpoint for SWTException and reproduce the problem. In the debugger, you can then select the frame with the image and execute "creationException.printStackTrace()" from within the debugger.
Similarly, you can capture information about when the resource was disposed by adding a field "disposalException" and setting it in the "destroy" or "releaseWidget" method.
Debug cases where Eclipse does not prompt for a workspace and always starts with the default
First, make sure that you've not unchecked "Prompt for workspace on startup" on the General>Startup and Shutdown>Workspaces preference page. If this checkbox is checked, but you are not asked for the workspace location on startup, the likely cause is that a bundle is activated very early and causes the "instance location" to be set to its default value before the IDE application can prompt.
The following is pretty advanced stuff. Ask in the forums, or on the #eclipse IRC channel, if you need help.
To find out which bundle or class causes this, you will need two Eclipse instances. One is going to be your debug target (the one that does not prompt), and the other will be used for remote debugging as described under 1. above.
Add the following to the eclipse.ini file of your target Eclipse, after the -vmargs line:
-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=y
Note the "suspend=y" which causes the target Eclipse to wait on startup until you've attached a remote debugger. Don't start your debug target yet. Using your other Eclipse instance, set a breakpoint in org.eclipse.core.runtime.adaptor.LocationManager.initializeLocations() on line 121 (the one that starts with "instanceLocation = buildLocation(PROP_INSTANCE_AREA, defaultLocation"). Now, create a launch configuration as described above under "How to run Eclipse so that you can attach an external debugger". A good project to use is org.eclipse.ui.ide.application. Start your target Eclipse as you normally would, by clicking on eclipse.exe for example. You will notice that it doesn't really start yet, it is waiting for the external debugger. Now "launch" your debug configuration and wait until the breakpoint is hit.
Use F5 to step into the buildLocation method, then F6 until you are on a line that creates a BasicLocation, then F5 to step into the constructor. Now create a breakpoint on the first line of BasicLocation.set(URL, boolean, String), and right-click on the "this" of type BasicLocation in the Variables view. Select "Instance Breakpoints..." and then restrict the breakpoint in BasicLocation.set that you just created to the current instance.
Now press F8 to resume execution. The instance breakpoint should be hit soon. You can use the stack to find out what caused BasicLocation.set to be called before the IDEApplication got to the point where it would prompt. Usually, you will find that a bundle is activated eagerly which has a dependency on the org.eclipse.resources plugin. This bundle's activator, ResourcesPlugin, will initialize itself based on the current instance location, which, if not set explicitly, will be set to a default value (usually a directory "workspace" in the current user's home directory). To fix the problem, you will want to find the lowest stack frame that sets off a chain of dependencies ending up in BasicLocation.set - for example, an OSGI declarative service that is created eagerly instead of on demand.