Non-Symbolic Traceback (GNAT User’s Guide for Native Platforms)

#### 6.1.14.1 Non-Symbolic Traceback ¶

Note: this feature is not supported on all platforms. See `GNAT.Traceback` spec in `g-traceb.ads` for a complete list of supported platforms.

#### Tracebacks From an Unhandled Exception ¶

A runtime non-symbolic traceback is a list of addresses of call instructions. To enable this feature you must use the `-E` `gnatbind` switch. With this switch, a stack traceback is stored at runtime 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’s 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 can see, the traceback lists a sequence of addresses for the unhandled exception `CONSTRAINT_ERROR` raised in procedure P1. It’s easy to see that this exception come from procedure P1. To translate these addresses into the source lines where the calls appear, you need to invoke the `addr2line` tool 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. `b~std.adb` is the binder file, which contains the call to the main program; Running gnatbind. The remaining entries are assorted runtime routines. The output will vary from platform to platform.

You can also 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. The default mode of `gnatsymbolize` is equivalent to that of `addr2line` with the above switches, so none of them are 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 ???

#### Tracebacks From Exception Occurrences ¶

Non-symbolic tracebacks are obtained by using the `-E` binder switch. The stack traceback is attached to the exception information string and you can retrieve it in an exception handler within the Ada program by means of the Ada facilities defined in `Ada.Exceptions`. Here’s 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

#### Tracebacks From Anywhere in a Program ¶

You can also 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. You don’t have to use the `-E` `gnatbind` switch 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 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 get even more 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’).