Anyway, the long wait is over. It is now solution time.
#5: Only guests are welcome - 2008, Javascript
The night before I did some refactoring. I had two methods, let's call them f1() and f2(), both taking two parameters and doing almost the same thing. So I consolidated them into one method, f(). I then searched all call sites of f1() and f2() and changed them to call f(). One of the f2() calls passed null as the second parameter, however f() was built mainly on the f1() code so it didn't know how to handle nulls.
This was frustrating because there were very few f2() calls and I went over them manually. Actually, at some point the null was just there in front of my eyes. But at that moment I didn't think that f() cannot digest nulls. I didn't bother to write unit tests for the Javascript part of the project, so I had no way to verify my refactoring. #Lazy
#4: The unimaginably slow load - 2005, Java
When I wrote the code that saves the data structure into a file (last step of the import) I chose the simplest thing that could possibly work: serialization. Later, I added a publish/subscribe functionality to the data structure, thereby allowing GUI objects to respond to changes in the data.
Guess what happens when you serialize a data structure that has a reference to a listener that is an inner class of the app's Top-level JFrame object: you serialize every piece of runtime data your program holds. Everything. Instead of the saving a minimalistic storage-oriented data structure you suddenly save caches, in-memory indexing tables, the works.
Unfortunately, all these objects were all Serializable so I didn't get any exceptions in the process, just a very slow save/load process.
#3: Modal smiles, Modeless cries - 2001, C
This is a C-specific issue. The error messages were stored in an array of structs where each struct represents a single error. There was an upper limit on the number of errors so I declared an array of 1,000 structs (which was well beyond the limit) . In order to avoid the hassle of dynamic memory management this array was actually a local, stack stored, variable.
After filling the array the code pops-up the Modal window that shows the errors by issuing a DialogBox() call. This call will return only after the window is closed. Thus, as long as the window was open execution was still sitting there at my function, and my array was safe on stack.
A Modeless window is different. The call that creates a modeless window, CreateDialog(), returns immediately while the window is still open. Execution then continues, reaches the end of my function and returns to the caller. At this point, the stack is unwound and the array vanishes into thin air. When the user clicks on an error the event handler tries to access the array (via a pointer) but the array is no longer there. It is an ex-array.
#2: The out-of-nowhere crash - 2002, C++
Again, we are in the no-garbage collection territory. In C++ you constantly write destructors that take care of releasing the resources that the object acquired. This is a safe way to avoid leaks.
What happens when you have a bug in a destructor (e.g.: null pointer error)? Right. the program crashes.
When are destructors of local variables called? At the end of the scope. Literally. At the curly brace character closing the scope.
So there you have it: the program crashes when it reaches the curly braces after the "b = true". The assignment itself went fine. The destructor was the problem. My IDE back then was not smart enough to treat closing braces as a statement. It treated it as part of the "b = true" assignment thereby misleading me to think the assignment failed.
#1: The memory monster - 2004, C#
Actually, this bug is very simple once you see the code. It is my #1 due to the long time it took to track it down. Let's take another look at the code:
for(int i = 0; i < rows.Length; ++i)
if(rows[i].isOutOfView)
rows.Remove(i);
If the isOutOfView property is true we remove the current element from the collection. Let's play with i=4. The if() is true, we call rows.Remove(4), we increase i's value to 5 and start the next pass. Alas, the rows.Remove(4) call moved every element one position towards the beginning: the element at position 5 is now in position 4. At the next pass when i=5 we examine the element that used to be at position 6.
Net result: we never checked the element at position 5. Thus, some of the elements that were needed to be removed from the collection were not removed. Later, the isOutOfView property of these elements were reset back to false. Overtime they accumulated until the heap collapsed.
That's it. Hope you enjoyed this account. Other bug stories will be happily acknowledged.
0 comments :: Top Five Bugs of My Career - Solved
Post a Comment