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:
```console linenums=1 $ 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>):
```c
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:
console linenums=14
(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.