adrift in the sea of experience

Monday, July 16, 2007

Using System.Diagnostics.StackTrace to leave debugging clues.

When I am debugging, I often find myself wishing I could step back to a certain point, in order to discover at which point some piece of data becomes corrupted. Most of the time, it is possible to set a conditional breakpoint which will break the program at the point where something unexpected happens, and reproduce the bug again.

However, this does not always work. Let's say for example that some piece of code is calling Dispose on an instance which it doesn't own, and therefore shouldn't dispose. Things will go haywire when the instance is used after being disposed. At that point, it may be hard to determine the culprit who called Dispose. And because of the number of instances being created and disposed, it may very well be impractical to set a breakpoint in Dispose and inspect each call to it!

What we need here is a way to see who called Dispose in the past.
The way I do this in .NET is by setting a field to a new System.Diagnostics.StackTrace in Dispose just for debugging. Then when things go haywire and the debugger breaks, I simply read the stacktrace to discover the culprit. This trick can be used in any programming language or environment which supports the runtime generation and inspection of stacktraces.

In visual studio 2005, you could also avoid modifying your code by inserting a tracepoint with the following expression:
Instance with hashcode {GetHashCode()} was disposed. Call stack: $CALLSTACK
Then when things go haywire, you can inspect the hashcode of the corrupted instance and search for it in the trace log.

Monday, May 28, 2007

20 kilometers of Brussels

Like last year, I participated in the 20 kilometers of Brussels yesterday along with some of my coworkers. I had my sights set on finihsing within 1h40m.

Up to the 17th kilometer I was perfectly on schedule for that time. Unfortunately I got recurring cramps in my calf muscles during the last 3 kilometers, which almost halved my speed :( I still finished in 1h46m15s. That's an improvement of 2 minutes over last year, so the result was not a complete disappointement.

Wednesday, May 16, 2007

Don't document that you throw ArgumentException or InvalidOperationException

When programming by contract (which should always be the case, at least implicitly), each method has pre-conditions that the caller should adhere to and post-conditions which the callee guarantees if the pre-conditions were satisfied; if they weren't, all bets are off.

Let's look at an example in the .NET framework: System.XML.XmlReader.ReadElementContentAsString. This method has the precondition that the xml reader should be positioned on an element. However, Microsoft has chosen to actually document this as a postcondition: if the xml reader is not positioned on an xml element, InvalidOperationException is thrown. This is the normal documentation style for the .NET framework, but it has never sat right with me for two reasons.

The first reason is that you shouldn't encourage programmers to rely on avoidable exceptions. I feel it would be better if this was simply documented as a precondition which must never be violated. What happens when the caller violates the contract doesn't need to be documented, so the guarantee of an InvalidOperationException could be omitted. It would of course still be thrown, but as an indication of a programming error. It shouldn't be relied upon.

Another reason is inheritance. One of the great things of programming by contract is that it provides rules for what you can do in an overriding method. Any method must implement the contract of the method it overrides, because the caller may not be aware of the exact type of the callee. However, the overriding method may loosen the pre-conditions. It must merely accept anything that the overridden method would have accepted. It may accept more. Unfortunately, if the preconditions are documented as exceptions, they are now post-conditions which restrict the overriding method without good reason.

Conclusion: don't document ArgumentException and InvalidOperationException if it is sufficient to document pre-conditions.