wiki:AssertTrapping

Trapping Asserts

The basic problem is to retain the context after an assert fails. Since the assert throws a C++ exception, the default behavior is to catch it in the main program. Stopping the program there means that the context for the original error is already lost. The workaround for this problem is to teach your debugger to catch the exception when it is thrown so that the call stack is still intact and you can inspect the code that failed.

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, lldb, 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 a C++ exception. We start with a description for some commonly used debuggers and at the end list a 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)

Getting Homebrew gdb to work on a Mac requires some setup to be done before the debugger is allowed to modify running programs. This is done for security reasons. The procedure is explained on this page.

Trapping within lldb

This is the debugger that comes with the LLVM compiler and it is the default debugger on Mac systems. Enabling catching a thrown assert works much the same way as described above for gdb, except that the actual command is different: break set -E C++ instead of catch throw. Some more details can be found here.

Trapping in Xcode

The procedure to catch a thrown assert in Xcode is described here.

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 bad_assert constructor

A standard breakpoint may be placed in the constructor for the bad_assert class (i.e. the function bad_assert::bad_assert()) 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.


Return to DeveloperPages

Return to main wiki page

Last modified 7 months ago Last modified on 2018-12-05T14:44:42Z