Debugging Ada exceptions

Posted on 2011-05-24

Each time i have to code in Ada i just hate the language… it makes “clean” coding a pain in the ass (easy workarounds would include using pointers everywhere).

This time i need to use protected objects; protected objects have some associated code blocks (entries, procedures and functions), which can access the private data part; but only one thread can enter such block for one object at a time.
But all these entry points are public to everyone who sees the protected type; you can’t have private entry points which would be only accessible from your internal package.
So i ended up hiding the protected type in the private part of my package, and using a private record as wrapper around it… (i need this to get a pointer to the protected object too; i can’t get a pointer to the protected object in the entry points – there is no “this” or “self” variable).

The real fun started when I ran my test program and it crashed; it would show me the exception cause, but not the location and no backtrace. Attaching with gdb didn’t help either; there is no automatic breakpoint on unhandled exceptions, and the program doesn’t call abort() (which gdb would break on, and i think this is the way C++ handles it).
Google came up with various different methods to break on unhandled exceptions in ada; only one was a recognized command:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ gdb ./main
[...]
(gdb) catch exception unhandled
Unable to insert catchpoint. Try to start the program first.
(gdb) start
Temporary breakpoint 1 at 0x416d9e: file .../main.adb, line 53.
Starting program: .../main 
[Thread debugging using libthread_db enabled]

Temporary breakpoint 1, main () at ..../main.adb:53
53         Test;
(gdb) catch exception unhandled
Cannot insert catchpoints in this configuration.

And it didn’t work. I searched for the gdb code, and found this (http://gcc.gnu.org/bugzilla/attachment.cgi?id=21797):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
static const struct exception_support_info default_exception_support_info =
{
  "__gnat_debug_raise_exception",
  "__gnat_unhandled_exception",
  "__gnat_debug_raise_assert_failure",
  ada_unhandled_exception_name_addr
};
static const struct exception_support_info exception_support_info_fallback =
{
  "__gnat_raise_nodefer_with_msg",
  "__gnat_unhandled_exception",
  "system__assertions__raise_assert_failure",
  ada_unhandled_exception_name_addr_from_raise
};

So i just tried:

14
15
16
17
(gdb) break __gnat_unhandled_exception
Breakpoint 2 at 0x7ffff78bdb1e
(gdb) continue
[...]

It breaks on the next unhandled exception, showing a useful backtrace and so on. (On my system the second set of function names was the “good” one – break on “__gnat_raise_nodefer_with_msg” to see all exceptions, break on “system__assertions__raise_assert_failure” to see all failed asserts).

PS: To get useful backtraces (i.e. including formal parameters and local values) you have to disable optimisations; gdb can not track the value of variables if they are only stored in registers.

Generated using nanoc and bootstrap - Last content change: 2013-08-16 14:47