Getting the Bugs Out: A Conversation With Bug Fixer Brian Harry

   
By Janice J. Heiss, April 2007    

Interviews Index

  Bio: Iowa developer Brian Harry, also known as "leouser" on java.net, is renowned for the bug fixes he contributed to Java SE 6, which number well into the hundreds and won him a Duke's Choice award for outstanding platform contributions in 2006. His method was simple: He scanned Sun's openly available bug database for intriguing bugs, primarily in the Swing user interface (UI) code, printed them out, and put the bug reports on a stack beside his computer. Then, he fixed them one by one, submitting them through the standard JDK Community contribution process. A Sun Certified Java Developer (SCJD) and independent consultant, Harry describes himself as "a Midwesterner who believes that when the Big 10 wins the NCAA basketball tournament, the world will be a better place."

Java Sun (JSC): How many bugs did you fix in Java SE 6?

Harry: I actually don't know -- I've probably submitted a multiple of a hundred. What's important is not how many you fix but getting into the code. I can't see someone being motivated by thinking, "I'm going to do 500 bug patches."

Wanting to devour the code is a much stronger motivator. I like to get inside things and see how they work. You can read the code and learn about it that way, but if you want to make it instinctual, you need to work with it.

JSC: Did you also spend time finding bugs?

"Bug fixing is a great way to understand how the code works. Reading the code is fine, but working with it is better. Even if you don't find a solution, you will learn and become a more powerful developer."
 
 
Brian Harry
Java Technology Consultant
 

Harry: Yes, I would start with a simple question, such as "Do any classes return internals and violate encapsulation?" Then I'd write a test case testing each Swing widget. Sometimes things would fly. Other times I'd find problems.

Also, I look at the bug database for a matching bug report as a natural part of doing any request for enhancement (RFE) or bug fix. When you find something wrong, it's important to see if there's a matching bug report. While researching a MDI (Multi-Document Interface) RFE, I had serialization problems during testing. I found a bug report that seemed to match what I saw, so I wrote a fix for it and tested the code with the fix in place and voilà, things worked.

JSC: Lots of people find bugs, but only a few seem willing to dig in and fix them. What motivates you to fix these bugs?

Harry: Bug fixing is a great way to understand how the code works. Reading the code is fine, but working with it is better. Even if you don't find a solution, you'll learn and become a more powerful developer.

Just think of times when you were faced with an API that made sense, but you had trouble getting it to work. If you cross the Javadoc boundary into the source code, your chances of success increase. For example, I helped someone on java.net find an acceptable solution to the " JInternalFrame can't play with Heavyweights" problem. The Sun articles and tutorials typically say that you can't do it. But since I was familiar with the code, I was able to help the developer. Of course, I wasn't 100 percent happy with the solution -- the frames flickered when resizing -- but having something to use is better than having nothing.

Bug Tips
"First, always acquire the test that's attached to the bug report.... Next, ask yourself if it's really a bug.... And consider writing different solutions."
 
 
Brian Harry
Java Technology Consultant
 

JSC: Are there any tips you can share?

Harry: First, always acquire the test that's attached to the bug report. The state of the bug database is a mixed affair. Sometimes the test is visible, sometimes not. If you're impatient and decide to build a fix based on the report because the test isn't visible, you may waste your time trying to recreate the problem. In several cases, after I reviewed the test, it became apparent that the report gave the wrong description of the problem.

Next, ask yourself if it's really a bug. In some cases, the reporter would have been better off getting help from a Java forum. Moreover, sometimes the test case was buggy. Once you confirm that it's a bug, you dig in. The best way to be effective is by being familiar with the source.

Also, writing down my thoughts as I seek a solution helps clarify the problem and documents the history if I need to revisit it. Including your reasoning processes in your patch helps communicate with whoever evaluates the patch.

And consider writing multiple solutions. Your solution may work, but it may not be optimal. In a similar vein, be prepared to iterate on your solution. It may seem to be a good solution, but unanticipated issues can trip you up.

As to writing unit tests, look at what the patched code interacts with. This may entail viewing a lot of code, but it's necessary to test the different paths to the patch.

Swing Tips

JSC: Do you have any Swing tips?

Harry: Yes, try to be comprehensive in testing the patch with different looks and feels (LAFs). Much of Swing's behavior depends on the LAFs, so ignoring any of them because you never use them amounts to ignoring the central clients of Swing. What good is a patch for BasicButtonUI if the GTK variant is going to explode on you?

Also, make sure that the test case is running on the event dispatch thread (EDT). If it isn't, you may not have a bug, but instead just have bad programming.

There are exceptions to this rule, for example, on a non-EDT thread:

DefaultStyledDocument dsd = new DefaultStyledDocument();
dsd.setCharacterAttributes(0, 10, sas, true);
 

The only reason I'd do something like this is because the Javadoc explicitly states: "Is Thread Safe." In this particular case, I've used this with asynchronous colorizers. If it wasn't thread safe, you'd risk ending up with a deadlocked Swing application and a negative perception of Swing. Understanding threading is necessary for Swing programming. Though the application framework might make this easier, currently, to do anything serious, you need to be able to think with threads.

JSC: Why did you fix so many bugs in Swing?

Harry: I gradually evolved into doing it. I started scouting the Swing forums for help, which in turn led me to helping other people.

During a discussion about making AWTKeyStroke different, an AWT engineer suggested I write the patch for the change. It made sense, so I did. I suppose if I were helping folks out with a different set of functionality, it would have been a different package. But since I was already helping developers on the forums, I thought, "Why not write a patch for the JDK?"

Instead of helping one developer, leverage your work and help everyone.

JSC: What makes a good bug fixer?

Harry: Someone who's a researcher and doesn't quit when things initially go wrong. Here's an example. I was flummoxed by this byte-code sequence:

1 goto 8
2 set i to 100
3 set i to 100 --> start protected block
4 do stuff with i
5 return --> end protected block
6 use i to print out error message --> start exception handler
7 return
8 jump to 2 or 3
 

It seems simple, setting a local variable to the integer 100. But the verifier blew up because i may have been uninitialized. How's that likely to happen? It's possible that an out of memory error could be tossed in the exception handler, which led to the problem. Looking at the VM (virtual machine) specification, the data flow analyzer section says that it must "ensure that the specified local variable contains a value of the appropriate type." I thought, "Hmm, that seems to map what's going on here."

I never truly appreciated how much the verifier does for the Java developer until I started working with byte codes. If you're new to it, you quickly learn what a strict taskmaster it is. It may have driven me mad for a while, but I was able to sit down with the VM spec and come up with a rationalization of the problem and a subsequent restructuring that worked.

Likewise, if you're doing a Java code fix, don't just look at the current version of the platform. I've routinely tested out bug tests on 4.0, 5.0, and 6 to investigate when a problem started and stopped. See if you can find why something started. My "uninitialized" problem has roots in the verifier spec. JDK bugs will have roots in some historical moment. Getting a bigger picture may provide clues about the design factors that led to the bug.

Swing Developments in Java SE 6

JSC: How do you feel about Swing developments in Java SE 6?

Harry: There are some cool things in Java SE 6: better native LAF fidelity, SwingWorker becoming official, and so on.

Java SE 7 looks much more exciting. The application framework could change how future Swing apps are written. If I can just do this:

@Action
public void imAMethod(ActionEvent e){ ... }
 

and have it produce an Action to use without having to mess around defining a class, now that's exciting. I have simpler code that focuses solely on what's important: the isAMethod code. I've typically used a powerful code organizer like Leo to achieve this focus, but that may not be needed for actions anymore. I wouldn't mind having a @Listener annotation that generated the listener instance for me. But maybe that's out of scope for the framework?

Anyway, if the trend is to get rid of the boilerplate, it seems good. Why should you as a developer always end up defining a new class when all you care about are the guts? If at some point you need to reuse something, just whip out the lovely Java refactoring "Extract Class From Annotation," and you're set.

A Fun Bug Fix
"The Solaris DTrace facility was great at providing more detailed information about the crash. It was my first time using it, and I was very pleased with it."
 
 
Brian Harry
Java Technology Consultant
 

JSC: Can you give us an example of a bug fix you enjoyed?

Harry: The ultimate fix wasn't exciting, but I enjoyed finding and writing a fix for a Java crasher that was located in the GTK code. In a nutshell, if you changed the GTK Theme, your Java instance would crash. So the journey started out in the GTK LAF, went into its native code, and eventually deposited into the GTK widgets that are used by Swing to do its nifty rendering.

The Solaris DTrace facility was great at providing more detailed information about the crash. It was my first time using it, and I was very pleased with it. Without DTrace, all I got was an error message. The GTK text widget's cursor started blinking because of the theme change. The widget's code is designed to abort or toss an assertion if it's blinking, and it didn't have the focus. So the fix was: Don't let it blink. Odd but true. I'm glad Swing doesn't do such things. Swing lets you be weird. It was a simple fix but an interesting journey down atypical paths.

JSC: You're probably very familiar with the Java source code at this point. What advice do you have for those who are just starting to look at the source code?

Harry: For Swing, map out the relationship between the Swing class and its UI. To be an effective Swing hacker, you need to know what's coming from what. As to robustness, java.util seems tough from a developer's perspective. I just take it for granted that my ArrayList is going to work and don't think too deeply about it. Isn't that a sign of robustness?

Testing, Testing, Testing

JSC: How do you go about testing your fixes?

Harry: I like tests to do two different things. I'll have a non-GUI method that's a set of assert statements. Then I try to have a GUI part where I can fiddle with the fix and see if I'm missing something in the assert methods. Adding some randomness to the process doesn't equate to what the real world will throw at it, but it helps to do some things without thinking and see what goes wrong. Always having a unit test helps the process out. I've even written unit tests to prove that a Javadoc change was correct.

Surf the Bug Database

JSC: Is there a book or article on bug fixing that you recommend?

Harry: The book I'd recommend is Code Complete . It's convinced me of the limitations of unit tests, which does not mean you shouldn't do them. But you need to complement them with code reading and other things.

I'd recommend surfing the bug database, especially for the subject area that interests you. This may not hold true for areas such as the VM, but it's indispensable to become better acquainted with what has been bugging Swing. It may also help you gain a more general sense of a particular problem.

Look at the "Swing models need pre and post listeners" bug. It probably has one vote for it. But in reality, it manifests itself over and over again in other little bugs. I've written patches for bugs that have fallen into this category with the recommendation that you either put the patch in or close the bug and reference the "pre post" bug report. This problem has acted as a force in the Swing library as well.

Take a look at TreeExpansionListener and TreeWillExpandListener. I'm sure TreeWillExpandListener was introduced because of a need to know that the JTree is going to expand but hasn't happened yet. If there was a simple way for the user to order their listeners so the "before it happens" code is executed, wouldn't it be possible for the TreeWillExpandListener to simply go away?

Of course not, but it's fun to dream.

When Not to Fix

JSC: Are there some bugs you shouldn't mess with?

Harry: Sometimes nontechnical factors cause problems. I dealt with a bug for mixed enabled widgets and the cursor, and I worked out a fix on the UNIX side. Since this was in the secret AWT code, I had to look at what was on the Windows side. The Windows code looked remarkably similar to the UNIX code but was in C++. Since I didn't have a $1000 Microsoft Visual Studio to build Java on Windows, I didn't submit a patch. I believe the compiler requirements have changed, so I might be able to revisit this someday.

Another showstopper is when an external component is buggy. For example, there's a bug I would describe as "GTK, JButton, and Crux Theme: Large buttons causes Java to crash." The problem has nothing to do with Java code but is due to a stack/memory problem in C, so there's not much one can do.

I also haven't fixed text/caret bugs that mix right to left and left to right text. Usually when I start working on a bug, I snip out any evaluation by the engineer and start from scratch to see if I can reach some common ground. After considerable analysis, I've concluded that the problem is due to two significant pieces with conflicting Bidi information. The text widget's document has one set of data, and the views have another.

I found one method that appeared to do the wrong thing, so altering that repaired some problems. But ultimately, it didn't solve the problem because there was still conflicting Bidi data affecting the caret. I still benefited from the process because I learned about some classes like Bidi and TextLayout that I now use in my programming arsenal.

My Pet Bug

JSC: Do you have a favorite bug story?

Harry: I don't recall the particular bug, but someone on java.net complained about a problem and put out a bug report. I put a fix together and submitted it. Then, rather quickly, a Swing engineer folded it into the code base. Though the technical aspects aren't exciting, I feel gratified that different members of the Java community can work together and accomplish things. With open sourcing of the Java language, perhaps this will become a trend. To empower yourself, you need to get involved.

The Potential of Open Source

JSC: How do you feel about open source? Are you involved with the OpenJDK project and JDK 7?

Harry: Open source has significant potential for Java developers, depending on community involvement. Regular users who aren't that involved with the language itself may prefer to regard the Java language as a building block only. Yet a part of the community actively called for it to be open sourced. It's happened, so now what? Are these folks going to contribute and collaborate? Time will tell.

I look forward to seeing what the Java SE 7 application framework can do. Looking at the source, reflection appears to be one of its tools for getting work done. Maybe rather than reflection, runtime class generation could be utilized instead? It would be more efficient -- though is it necessary? -- and may lend itself to making the framework do more than it's capable of now.

I've been playing around with class generation for the last couple of months, so I'm curious to see if I can fuse the Swing/byte code so that they're spinning together.

JSC: What enhancements would you like to see?

Harry: I'm excited about superpackages and method references at the language level. Superpackages hold the promise of eliminating some crude hacks to get cross-package interaction working without breaking encapsulation. We'll have to see about method references. I like the freedom that passing methods around could introduce, but I don't like the idea of sacrificing clarity to do this. Will it be implemented as a new construct, or will it be one of these sneaky compiler tricks? I don't know.

At the VM level, I'm excited about invokedynamic. We have invokestatic, invokevirtual, invokeinterface, and invokespecial today, each of which requires a lot of information to execute.

This is great if you're engaged in Java programming, but what if you're running a language on the VM that's a little less smart? Take Jython, for example. In its implementation, the PyObject, which is central to Jython's running, has something like 150 methods. Why are these methods there? Probably because it would be nonperformant to look up each one of these when you need to execute.

Maybe invokedynamic will reduce the need for these down to zero. If the problem area was purely on the code compiler side, it would seem feasible. Invokedynamic is supposedly going to be able to handle the problem when a method doesn't exist on an object. Yet Java code in the implementation is expecting these methods as well.

Hopefully in the future, a limited form of multiple inheritance will exist. I don't particularly care about being able to express this at the Java language level, but it would be powerful if I could generate a class at runtime and have it inherit a chunk of code in a very constrained manner. I'm not too hip on introducing inheritance diamonds into the system, but there are days when the "implement an interface and delegate to a common implementation" design doesn't scale very well.

Gosling: "Perhaps You're Looking in the Wrong Place"

JSC: On his blog, James Gosling, after struggling to fix a bug, describes one of the most important rules of debugging: "If you don't see the bug where you're looking, perhaps you're looking in the wrong place." Any comment?

Harry: That makes sense. I'd add my own rule: "The place where you find bugs may not be the right place to put a fix in."

Why is this method being fed -1 and bombing? You could insert a guard to protect against it and think that's good enough.

I wouldn't do that -- I'd do some digging and find the root cause. If you simply patch it in a specific place, it doesn't prevent -1 from flowing into another method, such as an override, and creating havoc.

Solving One Problem and Creating Another

JSC: So solving some problems can create others?

Harry: Yes, a BufferedInputStream bug seemed to be causing the stream to read too much and lock up, which, I believe, runs counter to its spec. I worked out a patch that relied on the return value of the available method. This solved that problem but introduced a severe performance problem for another class of streams.

Look at InflaterInputStream's available method's Javadoc: "Returns 0 after EOF has been reached, otherwise always return 1. Programs should not count on this method to return the actual number of bytes that could be read without blocking." For these streams, reading as much as possible is preferred since relying on its available method will result in one slow stream. The only way to fix both problems is to allow the user to configure the BufferedInputStream's behavior to choose between different reading strategies. I'm not sure if it's worth adding this complexity to the class for this result.

A common theme in bug reports is that different people want different things. Take Swing's setEnabled method, which just turns off the current widget. Although that's great for some users, it's generated bug reports when the behavior is different. There are bug reports for the others who want all the widgets to be turned off as well. Who's right? How do you decide? At what point do you say configuration is not the solution, it's this way or the highway?

JSC: Any closing thoughts?

Harry: Tell your boss you'd like to spend some of that training time fixing JDK bugs. Not only will you fix that annoying bug, but you will become more of a JDK expert, which is the point of training, isn't it?

See Also

The OpenJDK Project
Contribute to the OpenJDK Project
How to Write a Helpful Bug Report
Report a Bug or Request a Feature
Bug Database
Java.net
The Jython Project
Code Complete
Sun Certification Program in Java Technology

Rate and Review
Tell us what you think of the content of this page.
Excellent   Good   Fair   Poor  
Comments:
Your email address (no reply is possible without an address):
Sun Privacy Policy

Note: We are not able to respond to all submitted comments.