Java Debug Builds

- Damodar Chetty

- Aug 02, 2009

 

One of the major conveniences of a modern IDE is the ability to debug your application by stepping through code. Also, one of the major benefits of open source software is the ability to view code for libraries and frameworks that were otherwise hidden.

It is therefore not rare to find oneself in a situation where one has traced past an application's logic, right into code that belongs to either the Java platform or some other third party library or framework.

Unfortunately, as soon as you follow the debugger’s instruction pointer into such code (for example, a class in the javax.namingpackage), you’ll immediately notice something amiss. It's like you’re in the middle of a downtown location in a strange city, and the guide you’ve grown to rely on, has abruptly taken off for parts unknown.

There’s the usual instruction pointer, the source code is still present, and you can even step through it. But suddenly there’s no helpful advice on your local variables, you no longer can execute expressions, and all you’re left with is some strange looking arguments named arg0, arg1, and so on.

What's going on?

When a class is compiled using the javac compiler, the default action is to only generate line number and source file information. This is what allows stack trace information to contain line numbers, and why Eclipse lets you single step through this code.

Unfortunately, local variable debugging information must be specifically requested by specifying the –g command line option.

The developer at Sun wisely decided for us that we mere developers don't need this information. As a result, they built the rt.jar file without the –g flag. To be fair, adding this option does increase the size of the rt.jar file. However, it's hard to understand why this is not even available as a option. And, in any case, while optimizing for size is reasonable for the JRE, it's hard to fathom the reason for doing this in a JDK.

Whatever the reasons, when you need this information, you need it. And, there’s nothing else that can substitute for this level of detail.

To see the frustration this has caused, visit: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4652184

 

So, how do you get a version of the JDK that contains a rt.jar compiled with all debug information? Well, unfortunately this is not as easy as downloading a file from the Sun web site. Not that downloading a file from Sun's site is easy, mind you. At least not without wading through numerous "bundle" versions, the recent account registration requirement, and what have you.

 

The Steps

Fortunately, you can put together your own rt.jar file by recompiling the sources that are included in src.zip, as detailed in this post. Unfortunately, this is not for the faint of heart – so I’d recommend thinking very carefully before you attempt to use it.

Note: please see http://www.javalobby.org/java/forums/t19866.html for the batch file that makes this happen.

The basic premise is simple – you extract the contents of the src.zip file (found in JAVA_HOME, the Java install folder) to some location; and then recompile the source files but this time using the –g option. You then repackage these files into a new rt.jar file, and you're done.

Obviously, nothing in life is as easy as all that. So, the rest of the article walks you through these steps.

Step 1:  Locate the JDK source

First navigate to the JDK install directory, and locate the src.zip file. This file contains the JDK sources – and is absolutely invaluable for the rest of this process.

Next, unzip this folder to some location, such as c:\jdk1.6.0_14-src.

Step 2: List all the source files to be compiled

Generate a list of all .java files in the unzipped folder, out to a separate file:
dir /B /S /X c:\jdk1.6.0_14\*.java > fileList-jdk1.6.0_14-src.txt

Step 3: Compile the source

Compile the source files named in this file, using the –g option.

"C:\java\jdk1.6.0_14\bin\javac"

-verbose

-nowarn

-g

-source 1.6 -target 1.6

-J-Xms512m -J-Xmx1024m

-bootclasspath "C:\java\jdk1.6.0_14\jre\lib\rt.jar";C:\java\jdk1.6.0_14\jre\lib\jce.jar;C:\java\jdk1.6.0_14\jre\lib\jsse.jar;C:\java\jdk1.6.0_14\jre\lib\resources.jar;C:\java\jdk1.6.0_14\jre\lib\charsets.jar;C:\java\jdk1.6.0_14\jre\lib\deploy.jar

-sourcepath jdk1.6.0_14-src

-classpath jdk1.6.0_14-src

-d compiledClassesDirectory-jdk1.6.0_14-src

@fileList-jdk1.6.0_14-src.txt

 

Note the presence of the –bootclasspath flag which makes the stated JARs available to the compiler. This is absolutely critical when trying to build the source distribution of JDK 6.

Step 4: Extract rt.jar

Extract the original rt.jar file, that is found in JAVA_HOME\jre\lib, into a temporary folder.

Step 5:  Generate a composite build

Copy the newly compiled .class files from our compiledClassesDirectory-jdk1.6.0_14-src over the folder where the rt.jar file was expanded. This ensures that the final set has old classes overwritten by newer classes with debug information, while still retaining class files that we couldn't compile.

Step 6: Regenerate rt.jar

Finally, recompress all the files from the composite folder into a new rt.jar file, and overwrite the original rt.jar file with this new one.

 

That's it! It’s a lot of work, but the payoff is immediate. You can now fire up your debugger, and detailed information begins to reappear no matter where you travel within the Java sources.