Trapping Asserts

The basic problem is to maintain context after an assert is thrown. If the assert is thrown as an exception then the default behavior is to catch it in the main program with no context for the original error. There are two workarounds for this, one using the debugger and a second using an alternative form of the assert macro.

I hope that the prose on this page can remain neutral. Documentation should not reveal the author's approval or disapproval of particular methods of trapping asserts.


Default behaviour

By default, Cloudy will throw a C++ exception when an assert fails.

There are genuine science reasons for preferring to use exceptions -- it means that a blown assert at one point doesn't kill an entire grid run, and allows context information for the failure to be obtained as the call stack unwinds.

To resolve problems in the code, it is often useful to have a debugger window open in the context of the assertion failure, before the stack unwinds. Many debuggers (including gdb and Visual Studio) allow you to trap all exceptions which are thrown. Unfortunately, this facility appears to be available in the NetBeans debugger only for Java code.

We will now describe how to trap the throwing of an exception by the ASSERT macro. We start with a description for some commonly used debuggers and then list some fail-safe means which should work on all debuggers.

Trapping within gdb

In gdb, you need to issue the 'catch throw' command before running the program, see edited highlights below.

[user@localhost source]$ gdb ./cloudy.exe
GNU gdb Red Hat Linux (6.6-35.fc8rh)
(gdb) catch throw
Catchpoint 1 (throw)
(gdb) run
Starting program: /home/user/cloudy/trunk/source/cloudy.exe
test
crash assert
[...etc...]

If the next line says "I am still alive - the assert macro is not
working ...." then there are problems.

Catchpoint 1 (exception thrown)
0x4d1c6795 in __cxa_throw () from /usr/lib/libstdc++.so.6
(gdb) where
#0  0x4d1c6795 in __cxa_throw () from /usr/lib/libstdc++.so.6
#1  0x0810f8ba in ParseCrashDo (p=@0xbf9c3c70) at parse_crashdo.cpp:140
#2  0x081083ed in ParseCommands () at parser.h:183
#3  0x0805c1c9 in cloudy () at cloudy.cpp:85
#4  0x0804dc5a in cdDrive () at cddrive.cpp:126
#5  0x0804b503 in cdMain (argc=1, argv=0xbf9c4234) at maincl.cpp:330
#6  0x0804cd82 in main (argc=137173456, argv=0x82eaaf2) at maincl.cpp:109
(gdb) up
#1  0x0810f8ba in ParseCrashDo (p=@0xbf9c3c70) at parse_crashdo.cpp:140
140                     ASSERT( DBL_MAX <  ZeroNum );
(gdb)

Trapping in the Visual Studio debugger

go to

Debug -> Exceptions...

under Visual Studio, you can set it to break when a C++ exception is thrown which should mean you retain context.

If we're developing under C++, it seems like a good thing to enable -- you'd probably want to know about any exception thrown, and it appears to remain set between sessions.

Placing a breakpoint in the badassert constructor

A standard breakpoint may be placed in the constructor for the badassert class (i.e. the function badassert::badassert()) in the file cddefines.cpp. This should act like any other code breakpoint: the routine which triggers the bad assert will be one level higher up in the call stack when the debugger starts.

The -a flag

This causes the code to issue an abort() at the failure point rather than throw an exception. The effect of this is that the debugger window opens at the point of failure. Using the -a flag means that the usual diagnostic information produced by the code is suppressed in all cases, so this flag should be avoided in runs outside a debugger. Note that this will cause Cloudy to abort on every failed assert, even the ones that are caught and recovered from. If the first failed assert is not the one you want to debug, then using the -a flag will make it impossible to reach the bug!

The OLD_ASSERT macro

The enables C-style assert behavior. Define the macro OLD_ASSERT during the compile step by including the option -DOLD_ASSERT when the compiler is invoked. A tripped assert will call routine MyAssert. Breakpoints should be set within MyAssert to break within the debugger. The call stack is preserved. This method has the same disadvantage as described in the section "The -a flag".


Return to DeveloperPages

Return to main wiki page