Note: this feature is not supported on all platforms. See
GNAT.Traceback
spec in g-traceb.ads
for a complete list of supported platforms.
A runtime non-symbolic traceback is a list of addresses of call instructions.
To enable this feature you must use the -E
gnatbind
option. With
this option a stack traceback is stored as part of exception information.
You can translate this information using the addr2line
tool, provided that
the program is compiled with debugging options (see Compiler Switches)
and linked at a fixed position with -no-pie
.
Here is a simple example with gnatmake
:
procedure STB is procedure P1 is begin raise Constraint_Error; end P1; procedure P2 is begin P1; end P2; begin P2; end STB;$ gnatmake stb -g -bargs -E -largs -no-pie $ stb Execution of stb terminated by unhandled exception raised CONSTRAINT_ERROR : stb.adb:5 explicit raise Load address: 0x400000 Call stack traceback locations: 0x401373 0x40138b 0x40139c 0x401335 0x4011c4 0x4011f1 0x77e892a4
As we see the traceback lists a sequence of addresses for the unhandled
exception CONSTRAINT_ERROR
raised in procedure P1. It is easy to
guess that this exception come from procedure P1. To translate these
addresses into the source lines where the calls appear, the addr2line
tool needs to be invoked like this:
$ addr2line -e stb 0x401373 0x40138b 0x40139c 0x401335 0x4011c4 0x4011f1 0x77e892a4 d:/stb/stb.adb:5 d:/stb/stb.adb:10 d:/stb/stb.adb:14 d:/stb/b~stb.adb:197 crtexe.c:? crtexe.c:? ??:0
The addr2line
tool has several other useful options:
-a --addresses
to show the addresses alongside the line numbers -f --functions
to get the function name corresponding to a location -p --pretty-print
to print all the information on a single line --demangle=gnat
to use the GNAT decoding mode for the function names $ addr2line -e stb -a -f -p --demangle=gnat 0x401373 0x40138b 0x40139c 0x401335 0x4011c4 0x4011f1 0x77e892a4 0x00401373: stb.p1 at d:/stb/stb.adb:5 0x0040138B: stb.p2 at d:/stb/stb.adb:10 0x0040139C: stb at d:/stb/stb.adb:14 0x00401335: main at d:/stb/b~stb.adb:197 0x004011c4: ?? at crtexe.c:? 0x004011f1: ?? at crtexe.c:? 0x77e892a4: ?? ??:0
From this traceback we can see that the exception was raised in stb.adb
at line 5, which was reached from a procedure call in stb.adb
at line
10, and so on. The b~std.adb
is the binder file, which contains the
call to the main program. Running gnatbind. The remaining entries are
assorted runtime routines and the output will vary from platform to platform.
It is also possible to use GDB
with these traceback addresses to debug
the program. For example, we can break at a given code location, as reported
in the stack traceback:
$ gdb -nw stb (gdb) break *0x401373 Breakpoint 1 at 0x401373: file stb.adb, line 5.
It is important to note that the stack traceback addresses do not change when debug information is included. This is particularly useful because it makes it possible to release software without debug information (to minimize object size), get a field report that includes a stack traceback whenever an internal bug occurs, and then be able to retrieve the sequence of calls with the same program compiled with debug information.
However the addr2line
tool does not work with Position-Independent Code
(PIC), the historical example being Linux dynamic libraries and Windows DLLs,
which nowadays encompasse Position-Independent Executables (PIE) on recent
Linux and Windows versions.
In order to translate addresses the source lines with Position-Independent
Executables on recent Linux and Windows versions, in other words without
using the switch -no-pie
during linking, you need to use the
gnatsymbolize
tool with --load
instead of the addr2line
tool. The main difference is that you need to copy the Load Address output
in the traceback ahead of the sequence of addresses. And the default mode
of gnatsymbolize
is equivalent to that of addr2line
with the above
switches, so none of them is needed:
$ gnatmake stb -g -bargs -E $ stb Execution of stb terminated by unhandled exception raised CONSTRAINT_ERROR : stb.adb:5 explicit raise Load address: 0x400000 Call stack traceback locations: 0x401373 0x40138b 0x40139c 0x401335 0x4011c4 0x4011f1 0x77e892a4 $ gnatsymbolize --load stb 0x400000 0x401373 0x40138b 0x40139c 0x401335 \ 0x4011c4 0x4011f1 0x77e892a4 0x00401373 Stb.P1 at stb.adb:5 0x0040138B Stb.P2 at stb.adb:10 0x0040139C Stb at stb.adb:14 0x00401335 Main at b~stb.adb:197 0x004011c4 __tmainCRTStartup at ??? 0x004011f1 mainCRTStartup at ??? 0x77e892a4 ??? at ???
Non-symbolic tracebacks are obtained by using the -E
binder argument.
The stack traceback is attached to the exception information string, and can
be retrieved in an exception handler within the Ada program, by means of the
Ada facilities defined in Ada.Exceptions
. Here is a simple example:
with Ada.Text_IO; with Ada.Exceptions; procedure STB is use Ada; use Ada.Exceptions; procedure P1 is K : Positive := 1; begin K := K - 1; exception when E : others => Text_IO.Put_Line (Exception_Information (E)); end P1; procedure P2 is begin P1; end P2; begin P2; end STB;$ gnatmake stb -g -bargs -E -largs -no-pie $ stb raised CONSTRAINT_ERROR : stb.adb:12 range check failed Load address: 0x400000 Call stack traceback locations: 0x4015e4 0x401633 0x401644 0x401461 0x4011c4 0x4011f1 0x77e892a4
It is also possible to retrieve a stack traceback from anywhere in a program.
For this you need to use the GNAT.Traceback
API. This package includes a
procedure called Call_Chain
that computes a complete stack traceback, as
well as useful display procedures described below. It is not necessary to use
the -E
gnatbind
option in this case, because the stack traceback
mechanism is invoked explicitly.
In the following example we compute a traceback at a specific location in the
program, and we display it using GNAT.Debug_Utilities.Image
to convert
addresses to strings:
with Ada.Text_IO; with GNAT.Traceback; with GNAT.Debug_Utilities; with System; procedure STB is use Ada; use Ada.Text_IO; use GNAT; use GNAT.Traceback; use System; LA : constant Address := Executable_Load_Address; procedure P1 is TB : Tracebacks_Array (1 .. 10); -- We are asking for a maximum of 10 stack frames. Len : Natural; -- Len will receive the actual number of stack frames returned. begin Call_Chain (TB, Len); Put ("In STB.P1 : "); for K in 1 .. Len loop Put (Debug_Utilities.Image_C (TB (K))); Put (' '); end loop; New_Line; end P1; procedure P2 is begin P1; end P2; begin if LA /= Null_Address then Put_Line ("Load address: " & Debug_Utilities.Image_C (LA)); end if; P2; end STB;$ gnatmake stb -g $ stb Load address: 0x400000 In STB.P1 : 0x40F1E4 0x4014F2 0x40170B 0x40171C 0x401461 0x4011C4 \ 0x4011F1 0x77E892A4
You can then get further information by invoking the addr2line
tool or
the gnatsymbolize
tool as described earlier (note that the hexadecimal
addresses need to be specified in C format, with a leading ‘0x’).