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.