Debugging Ada exceptions
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.