Thursday, June 28, 2007

Debug.Assert + Preserved Breakpoints = Safer Adventure

During today's adventure I stumbled over one of the many great new features in Visual Studio. I have always known that VS05 persists my breakpoints from one day to the next but today I experienced just how great this can be.

After reading chapter 3 of John Robbins' Debugging Microsoft .NET 2.0 Applications I started to implement debug.assert statements throughout my code. Knowing that debug.assert statements are great while I am developing they are useless at runtime in a release compile I decided, "Boy, everywhere that I have a debug.assert statement I need a trace statement." This way I can write trace output to a trace log in a release compiled object when debug.assert does nothing for me. I am smart enough to know better than to run around to all my Debug.Assert statements and place a Trace.Write right under it. So I wrote a separate method that accomplished both. It looked something like this:

#Const TRACE = True
Imports System.Diagnostics
Public Module DebuggingUtils
  Public Sub Assert(ByVal message As String)
    Debug.Assert(False, message)
    Const TRACE_SOURCE As String = "tsMain"
    Dim tracer As New TraceSource(TRACE_SOURCE)
    Trace.AutoFlush = True 'Trace is correct, NOT tracer
    tracer.TraceData(TraceEventType.Error, 0, message)
    tracer.Flush() 'make sure the output goes to the logs.
    tracer.Close() 'explicitly close this object.
    tracer = Nothing
  End Sub
End Module

That method worked fine but here is the cool part. I placed a breakpoint on the Debug.Assert line so now every time I get into a situation where the debug dialog box would be displayed I will hit this breakpoint instead of that nasty dialog box that scares me every time I see it. My adventures in .Net are scary enough without that nasty dialog.

Now, you might be wondering about the wisdom of having my Debug.Assert() statement pass false as the first parameter (which means that the assertion will fail every time causing the debug dialog to display) but here is my reasoning. I am trying to follow the advice of those that have travelled these parts before me (in this case John Robbins) and he recommends that I validate every parameter of every public method. So, the first thing each of my methods do is validate each parameter with 1 or many if/then statements and if that variable is invalid the assert method will be called right before the error gets thrown. Here is a sample:

Private Sub DoSomething(ByVal someNumber As Integer)
  If someNumber <= 0 Then

    DebuggingUtils.Assert("someNumber <= 0")
    Throw New ArgumentOutOfRangeException("someNumber")

  End If
End Sub

Additional Notes:
You will notice that the first first statement in my DebuggingUtils module is
#Const TRACE = True
I found that regardless of any trace settings in web.config all of my trace statements were ignored at runtime. I couldn't even set breakpoints on them in design time! Once I defined the TRACE compiler constant within my file everything was fine. Perhaps someone will know of a better solution but I am not too worried however because I only have trace statements within this single file so I do not have to run around and maintain this constant in each file.

Additional Resources:
There is a lot more to know about tracing. Read more about tracing by reading p.197+ in Francesco Balena's superb book Programming Microsoft Visual Basic 2005: The Language. This is one of my 2 favorite .Net books & authors. I disclosed my other favorite author in a prior post. I'll tell you the other book in a future post. That can be a little adventure for you!

Thanks for sharing this adventure with me.

No comments: