Handy Debugging Technique

| | Comments (4)

So I just ran into this while working on an assertion for the REALbasic IDE, and I figured I'd share this with you. I'm assuming that everyone has their own mechanism for dealing with invariants in their code, such as assertions. But if not, here's a little refresher course:

It's never a bad idea to double-check your assumptions in code. If you're assuming that Foo = Bar, then check it. The way most people do this is they make a global method called "Assert" which checks to see if an expression evaluates to false. If it does, then it throws some sort of dialog at the user. The thought is that you should never see assertions since you're asserting that some construct is always true (hence, it's invariant).

For example, let's say you're working with some secure data transmission. The invariant you want to check is that the user is working in secure mode. So you might write your assertion like this:[rbcode]
if Assert( SecurityLevel > 3, "The user's not in secure mode, but we're trying to do secure things." ) then return
[/rbcode]
So this code checks to see if the property SecurityLevel is greater than 3, and if it's not, then it displays a dialog to the user and bails out of the code. It's an easy way to ensure that your assumptions are correct and can save you a lot of headaches.

(Thing to note: assertions are almost always bugs in your code.)

But one thing that's hard to do is track down why the invariant is failing. What is calling that function at the incorrect time or with the incorrect parameters, etc? Here's where my handy tip comes in. You have information available to you which can tell you what code path was used to enter this method: RuntimeException.Stack. Put this code in your Assert method so that the message you display to the user is altered slightly (this code assumes you get a parameter called message as String which is then displayed to the user):
[rbcode]
..
try
raise new RuntimeException
catch err as RuntimeException
message = message + EndOfLine + Join( err.Stack, EndOfLine )
end try
..[/rbcode]
Bingo -- now any time an assertion is thrown, you'll be given information about the call stack which you can then use to help you debug the issue.

Neat and simple, eh?

4 Comments

Is this anything like my floating pallette dissappearing on my second monitor kinda bug?

I use this function in my PDF Classes:

Sub Assert(condition as boolean, message as string)
if not condition then _
raise new PDFFailedAssertionException( message )
End Sub

This has a few advantages:
- You have to deal with the assertion since it's raised as an exception.
- It bails out of code for you.
- You get a stack trace.
- Calling syntax is simple and clear.
- The message can be displayed on an application-specific basis; this is essential for frameworks like my PDF Classes that are used in various applications, most of which are not mine.

It probably has some disadvantages, but I like it :-)

Aaron - Have you tried this in something like an event handler ?
In an effort to make this work as far back as I own I wrote

dim tmp as string

tmp = message

if not condition then

#if RBVersion >= 2006
try
raise new RuntimeException
catch err as RuntimeException
tmp = message + EndOfLine + Join( err.Stack, EndOfLine )
end try
#endif

msgbox "Assertion failed : " + tmp

end if

I put a popupmenu on the windows and agve it an initial of 0 1 2 3 4 5 6
I aded a pushbutton to the window
Then in the pushbutton's action event a wndow I wrote

dim SecurityLevel as integer
SecurityLevel = val(PopupMenu1.text)

if assert(SecurityLevel > 3, "The user's not in secure mode, but we're trying to do secure things." ) then return

Run this with the popup set to 0 and then step in to the assert method

no message and tmp is set to something well .... ick

should I report this ?

@Norman -- I'm unable to reproduce any issue on Windows. You're welcome to report it though (just be sure to double-check you're on 2006 or higher).

Leave a comment

Disclaimer

I'm currently an employee of REAL Software. My blog is mine. The opinions represented in this blog are mine as well and may not represent my employer's opinions. All original material is copyrighted and property of the author.

REALbasic® is a registered trademark of REAL Software, Inc. REAL SQL Server™ and Lingua™ are pending trademarks of REAL Software, Inc. All rights reserved.