Main Menu

Exceptional Debugging

Exceptions (and their stacktraces) are one of the features that first attracted me to Java back in early 1995; C++ obviously had exceptions, but I never worked with any C++ projects that used them as effectively as the Java core classes did. I find well-designed exceptions to be a significant productivity booster.

My first project on the JDK team was developing the debugger API, and I think it was the first debugger where you could set a breakpoint that tripped whenever a specified exception type was created (please correct me if you know of earlier systems). The justification for exception breakpoints was simple: exceptions (should) indicate abnormal control flow in a program, and since we spend most of our time in debuggers figuring out why our programs are acting abnormally, there should be an easy way to halt whenever the program is indicating something worth investigating.

But few engineers I've met use this feature, and it puzzles me a little. Most recently, I filed a NullPointerException problem which didn't log a stacktrace, and the assigned engineer first added logging code so he could get a stacktrace to see where the problem was, which he then spent a lot of time reading various source files to determine what could have caused it. If he had instead set an NullPointerException breakpoint, the debugger would have stopped at the offending statement with all of its local variable and stack information immediately available. This is much, much faster than running until a stacktrace prints, editing the code to add debug statements if the cause isn't obvious, and looping until the problem is figured out. Isn't impatience supposed to be one of the attributes of a good programmer?

Exception breakpoints can be useful even when you aren't debugging exception failures, too. In NetBeans, I always have breakpoints set for NullPointerException and AssertionError (running with the -ea flag), to catch these conditions as soon as they rear their ugly heads.

I have heard complaints that exception breakpoints are "too noisy" because they can trip in places you don't expect. I think this indicates a couple of potential design flaws:

Overly general exceptions: although you don't need a separate type for all possible exception cases, it is often very useful to do more than "throw new InternalError()" when a problem arises. The java.io package derives FileNotFoundException and EOFException from IOException, since for many apps these are much more common recoverable error conditions than, say, a SyncFailedException. A custom exception is a great documentation aid if you have a public API, and exception classes are simple to create and maintain.

Using exceptions for flow control: as Josh Bloch argued in Effective Java, "Use exceptions only for exceptional conditions" (item 39). In the Solaris JDK a few years back, for example, system font files were discovered by trying to open all possible font names and catching all the FileNotFoundExceptions. This was much slower and more memory intensive than using File.exists(), due to all the exception object creation and collection. If your program is querying a class by poking it and testing for exceptions, add a method to that class to get the information directly.

Another complaint is that exception breakpoints catch exceptions in OPC ("other people's code", the source of all developers' woes :-). For example, Swing uses NullPointerException for flow-control in a couple of places, so I filed bugs when they tripped (should be fixed in Mustang) and now just hit continue. I strongly recommend filing such bugs as you find them (along with a recommended patch), so as to eliminate these nits and improve the Java platform as a whole. After all, the more successful it is, the more successful we Java developers will be.