Wednesday, August 17, 2011

Ok who did it?

Like solving a tough mystery, debugging software can be challenging too, especially if you plan on targeting multiple platforms and even then you have multiple versions of multiple platforms (i.e. XP, Vista, Snow Leopard, Lion, Ubuntu, KUbuntu, etc.). Real Studio makes this task easier as you can run your app and debug it locally on each platform or with our remote debugger. However, there comes a time when you release your app to the masses, and you are faced with the possibility of some users who do things that you don't expect and crash your app. For simplicity sake we'll consider these two types of crashes:
  • a RealBasic exception (stack overflow, accessing a Nil Object, etc.)
  • an OS exception (memory corruption, buffer overflows, invalid operations, etc.)
Luckily a RealBasic exception is something that we generate and can be caught easily. An OS exception however, is more fatal and can usually be attributed to poorly written declares, invalid pointer manipulation, plug-ins, performing GUI operations in a separate thread, or something in our framework.

In most cases when a user encounters a RealBasic exception you can catch this and continue with your program execution. However, if the user encounters a RealBasic exception that you're not expecting, you can create a stack trace (also referred to as the call stack) to help you narrow this down. I'm not going to go into details about catching exceptions as Geoff's blog post explains it very well, but being able to receive a stack trace from your user(s) can help you locate the problem.

So what is a stack trace? In short, it's a stack of method calls. All apps begin at a main entry point and starting from there it calls other methods.
[   etc.  ]
[ Method2 ]
[ Method1 ]
[   Main  ]

Knowing in which method the exception occurred will help you narrow down the issue. Great, so how do I get this stack information? Those familiar with compilers and using debug symbols know that this information must be stored in the built app. Consider that a method call is really just a pointer to some executable memory location, we need a way to map that address to a human readable method name. To be able to generate a readable stack trace you will need to enable App.IncludeFunctionNames, which defaults to off. With this enabled, RuntimeException.Stack will now contain a readable stack trace.

Since the exception isn't handled by your app, you'll probably want to record the stack information in App.UnhandledException. Consider this less than real world example:

Sub Window.Open() Call Test End Sub Sub Test() // This will cause a NilObjectException Dim f As FolderItem f.Visible = True End Sub Function App.UnhandledException(error As RuntimeException) As Boolean MsgBox Join( error.Stack, EndofLine ) End Function

When you build and run this app, assuming you enabled App.IncludeFunctionNames, you should see something like this appear:
Window1.Window1.test%%o<Window1.Window1> Window1.Window1.Event_Open%%o<Window1.Window1> : [additional stack trace snipped for brevity] : %main

As you can see, the output is a bit mangled, but being able to record the stack trace can help you track down the issue when debugging isn't possible.

1 comment:

Anonymous said...

To anyone wanting a more sophisticated solution based on this suggestion:

My open source tool OpenLingua at "" contains a module named "Exception" which provides an Assert function as well as gloval error handling function to be called from App.UnhandledException that both report the problem to the user, only once per type of problem, and also log it to a file.