Extra information in the message string of exceptions.

221 views
Skip to first unread message

Rod Kay

unread,
Jun 6, 2022, 9:02:25 AM6/6/22
to
Hi all,

Any thoughts on pro's/con's of having the Ada standard packages
runtime provide extra information in the message string of exceptions ?

For instance, a Constraint_Error message might provide details on
the variable name, the legal range and the erroneous value which caused
the exception.


Regards.

Fabien Chouteau

unread,
Jun 6, 2022, 9:40:38 AM6/6/22
to
On Monday, June 6, 2022 at 3:02:25 PM UTC+2, roda...@gmail.com wrote:
> For instance, a Constraint_Error message might provide details on
> the variable name, the legal range and the erroneous value which caused
> the exception.

That's a really good idea I would say. Extremely useful during development where I often start the debugger just to know what value triggered a Constraint_Error.

I guess the drawback is performance as formatting string with extra information will have a cost.
But this feature could be activated with a switch only when needed.

Gautier write-only address

unread,
Jun 6, 2022, 10:31:09 AM6/6/22
to
> Any thoughts on pro's/con's of having the Ada standard packages
> runtime provide extra information in the message string of exceptions ?

It depends on the compiler. And of course it is advantage to provide more details.
For instance HAC issues for...

procedure CE_2 is
i : Positive;
j : Integer := 1;
begin
i := -j;
end CE_2;

the message:
hac ce_2.adb
HAC VM: raised Constraint_Error
Out of range: pos, -1, is below (sub)type's lower bound, 1
Trace-back: approximate location
ce_2.adb: CE_2 at line 5

Of for...

procedure CE_3 is
a : array (1 .. 5) of Integer;
begin
a (7) := 2;
end CE_3;

hac ce_3.adb
HAC VM: raised Constraint_Error
Out of range: index (pos: 7) is above upper bound (pos: 5)
Trace-back: approximate location
ce_3.adb: CE_3 at line 4

G.B.

unread,
Jun 6, 2022, 2:41:00 PM6/6/22
to
Will it help to have a standard object taking just the data?

Dmitry A. Kazakov

unread,
Jun 6, 2022, 3:24:32 PM6/6/22
to
Besides problems with finalization and touching memory pools, an
exception from a rendezvous is propagated in two tasks. Will the object
be shared, copied?

------------------
Though you can always serialize an object into a string stream and use
the result. One could consider some syntax sugar like:

raise E with X;

would mean

declare
S : aliased String_Stream (200);
begin
T'Output (S'Access, X); -- T is the subtype of X
raise E with S.Get;
end;

and

function Stream (X : Exception_Occurrence)
return not null access Root_Stream_Type'Class;

So, in the handler:

when Error : E =>
declare
X : T'Class := T'Class'Input (Error'Stream);
begin
...
end;

without the sugar:

when Error : E =>
declare
S : aliased String_Stream (200);
begin
S.Set (Exception_Message (S));
declare
X : T'Class := T'Class'Input (S'Access);
begin
...
end;
end;

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

Luke A. Guest

unread,
Jun 6, 2022, 4:15:55 PM6/6/22
to
This is something which could be added to either the language or the
compiler. GNAT does have a dump backtrace call you could use.

Luke.


DrPi

unread,
Jun 6, 2022, 4:49:08 PM6/6/22
to
+1

>
> Regards.

Jerry

unread,
Jun 6, 2022, 7:17:39 PM6/6/22
to
This is not responsive to the OP but here is a bit of code that I find extremely useful to get a symbolic traceback for unhandled exceptions. Here is an example:

with Common; use Common;
with Ada.Exceptions;

procedure CE_2 is
i : Positive;
j : Integer := 1;

procedure A_Subroutine is
begin
i := -j;
end A_Subroutine;

begin
A_Subroutine;

-- Print a traceback for all unhandled exceptions.
-- See http://www.adacore.com/adaanswers/gems/gem-142-exceptions/.
exception
when Error : others =>
Common.Print_Traceback_For_Unhandled_Exception(Error);
end CE_2;

It provides this output:

==========================================================
Exception name: CONSTRAINT_ERROR
Message: ce_2.adb:10 range check failed
Load address: 0x100000000
Call stack traceback locations:
0x1000013ac 0x1000013cd 0x100001c36
<<<<<< Symbolic Traceback >>>>>>
If incomplete, compile with -O0.
ce_2__a_subroutine.3035 (in run) (ce_2.adb:10)
_ada_ce_2 (in run) (ce_2.adb:14)
main (in run) (b__ce_2.adb:354)
==========================================================

I can post the code that does this if anyone is interested. The gist is calling Ada.Exceptions.Exception_Information, massaging that information, and calling atos. (Is atos macOS-specific?)

Jerry

Gautier write-only address

unread,
Jun 6, 2022, 9:53:32 PM6/6/22
to
GNAT provides an integrated symbolic trace-back through the -Es switch. Doesn't it work on your Mac setup?
Anyway, it's much trouble for a feature which should be activated by default...

Randy Brukardt

unread,
Jun 6, 2022, 10:33:48 PM6/6/22
to
"Gautier write-only address" <gautier...@hotmail.com> wrote in message
news:1353f387-bb5a-4dc5...@googlegroups.com...
>> Any thoughts on pro's/con's of having the Ada standard packages
>> runtime provide extra information in the message string of exceptions ?
>
> It depends on the compiler. And of course it is advantage to provide more
> details.
> For instance HAC issues for...
>
> procedure CE_2 is
> i : Positive;
> j : Integer := 1;
> begin
> i := -j;
> end CE_2;
>
> the message:
> hac ce_2.adb
> HAC VM: raised Constraint_Error
> Out of range: pos, -1, is below (sub)type's lower bound, 1
> Trace-back: approximate location
> ce_2.adb: CE_2 at line 5

Janus/Ada has *always* done this (going all the way back to the original
Z-80 CP/M compiler). This particular case is not very expensive, since one
can use the fact that the out-of-range value is certainly in a register to
be tested in order to construct the message. And no real program cares about
the performance of raising Constraint_Error -- the main issue is one of
space (making the program larger).

For example, from a recent bug report:

** Unhandled CONSTRAINT_ERROR
Index or Subtype out of bounds - Pos of Error Value = 197807
On Line Number 37 In XCB_MAIN.DETERMINE_FILE_SIZE
Called from line number 40 In XCB_MAIN

It's possible to turn this off, but there hasn't been much need to do so
this century.

Randy.

P.S. I'd quibble with the OP, in that the vast majority of Constraint_Errors
have nothing to do with the standard library; their checks are compiled
directly into the code of your application; the only use of a library is to
raise an exception. We use special library calls for specific checks (like
the discrete range error discussed here); no strings are created in the user
code. An implementation would certainly have to do that as the alternative
would be to duplicate lots of complex code for each failed check -- but in
the absence of such information, the implementation probably uses the same
library call to raise all exceptions. Ergo, such a change probably isn't
easy for any implementation that doesn't do something like it already.


Randy Brukardt

unread,
Jun 6, 2022, 10:35:49 PM6/6/22
to
"Gautier write-only address" <gautier...@hotmail.com> wrote in message
news:cd0452bc-1330-4754...@googlegroups.com...
And I think it is in every other Ada compiler I've ever used. Ergo, don't
blame the language, blame the implementation. ::-)

Randy.


Simon Wright

unread,
Jun 7, 2022, 4:07:22 AM6/7/22
to
Gautier write-only address <gautier...@hotmail.com> writes:

> GNAT provides an integrated symbolic trace-back through the -Es
> switch. Doesn't it work on your Mac setup?

No. atos is a macOS utility.

Rod Kay

unread,
Jun 7, 2022, 7:29:45 AM6/7/22
to
On 6/6/22 23:40, Fabien Chouteau wrote:
>
> That's a really good idea I would say. Extremely useful during development where I often start the debugger just to know what value triggered a Constraint_Error.

Starting gdb and adding 'put_Line's often is what prompted the
thought :) .


> I guess the drawback is performance as formatting string with extra information will have a cost.
> But this feature could be activated with a switch only when needed.

A compiler switch to be used during development and debugging sounds
ideal.

Rod Kay

unread,
Jun 7, 2022, 7:33:00 AM6/7/22
to
On 7/6/22 00:31, Gautier write-only address wrote:

> hac ce_2.adb
> HAC VM: raised Constraint_Error
> Out of range: pos, -1, is below (sub)type's lower bound, 1
> Trace-back: approximate location
> ce_2.adb: CE_2 at line 5
>
> Of for...
>

... and ...

> hac ce_3.adb
> HAC VM: raised Constraint_Error
> Out of range: index (pos: 7) is above upper bound (pos: 5)
> Trace-back: approximate location
> ce_3.adb: CE_3 at line 4


... are very much what i'd like to see.

Rod Kay

unread,
Jun 7, 2022, 7:50:48 AM6/7/22
to
On 7/6/22 12:33, Randy Brukardt wrote:
>
> Janus/Ada has *always* done this (going all the way back to the original
> Z-80 CP/M compiler).

Very nice !

This particular case is not very expensive, since one
> can use the fact that the out-of-range value is certainly in a register to
> be tested in order to construct the message. And no real program cares about
> the performance of raising Constraint_Error

I didn't think that there would be too much impact on performance.


-- the main issue is one of
> space (making the program larger).
>
I hadn't really considered the extra space required. For a
desktop/workstation/server, I expect the extra space would not be a huge
consideration. For embedded systems and such, I guess it might be,
though if the feature could be switched off for production releases
perhaps not such a big problem.



> P.S. I'd quibble with the OP, in that the vast majority of Constraint_Errors
> have nothing to do with the standard library; their checks are compiled
> directly into the code of your application.

Quite right. I should simply have said the standard exceptions,
wherever they are raised.

Rod Kay

unread,
Jun 7, 2022, 7:54:41 AM6/7/22
to
On 7/6/22 06:14, Luke A. Guest wrote:
>
> This is something which could be added to either the language or the
> compiler.

I'd prefer to see it in the standard (for the usual reasons).


> GNAT does have a dump backtrace call you could use.
>

Ah, does it show more info than just the call stack ?

Fabien Chouteau

unread,
Jun 7, 2022, 11:56:00 AM6/7/22
to
On Monday, June 6, 2022 at 3:40:38 PM UTC+2, Fabien Chouteau wrote:
> But this feature could be activated with a switch only when needed.

Well, it turns our this is already implemented in GNAT ^^

Example here: https://godbolt.org/z/fcTEaq3xP

Anh Vo

unread,
Jun 7, 2022, 12:41:07 PM6/7/22
to
Very nice. I love some thing like this.

Luke A. Guest

unread,
Jun 7, 2022, 5:07:35 PM6/7/22
to

On 07/06/2022 12:51, Rod Kay wrote:
> On 7/6/22 06:14, Luke A. Guest wrote:
>>
>> This is something which could be added to either the language or the
>> compiler.
>
>    I'd prefer to see it in the standard (for the usual reasons).

Yeah, fair enough, just saying it could be added no problems (probably).

>> GNAT does have a dump backtrace call you could use.
>>
>
>    Ah, does it show more info than just the call stack ?

That I'm not sure of, been a while since I looked at it.

Jerry

unread,
Jun 8, 2022, 3:14:56 AM6/8/22
to
On Monday, June 6, 2022 at 6:53:32 PM UTC-7, wrote:
> GNAT provides an integrated symbolic trace-back through the -Es switch. Doesn't it work on your Mac setup?
> Anyway, it's much trouble for a feature which should be activated by default...
It does not work. My compiler reports "unrecognized command line option '-Es'". Maybe it's a new feature. My GNAT is a few years old.

Jerry

unread,
Jun 8, 2022, 3:31:14 AM6/8/22
to
On Tuesday, June 7, 2022 at 8:56:00 AM UTC-7, Fabien Chouteau wrote:
Indeed. The switch is -GNATeE. It provides additional information for the program at that page,

procedure Main is
procedure Test (A : Integer) is
type T is range 0 .. 42;
begin
Ada.Text_IO.Put_Line (T (A)'Img);
end;
begin
Test (-1);
end Main;

But has no effect on this program;

procedure CE_2 is
i : Positive;
j : Integer := 1;
begin
i := -j;
end CE_2;

Jerry

Dmitry A. Kazakov

unread,
Jun 8, 2022, 4:04:32 AM6/8/22
to
The switches are -E for binder and -g for everything else. Try this:

--- test_ce.gpr ----------------------------------------
project Test_CE is
for Source_Files use ("test_ce.adb");
for Main use ("test_ce.adb");

package Binder is
for Default_Switches ("ada") use ("-E");
end Binder;

package Builder is
for Default_Switches ("ada") use ("-g");
end Builder;

package Compiler is
for Default_Switches ("ada") use ("-g");
end Compiler;

package Linker is
for Default_Switches ("ada") use ("-g");
end Linker;

end Test_CE;
--- test_ce.adb ----------------------------------------
with System.Exception_Traces; use System.Exception_Traces;
with System.Traceback.Symbolic;

procedure Test_CE is
i : Positive;
j : Integer := 1;
begin
Trace_On (System.Exception_Traces.Unhandled_Raise);
Set_Trace_Decorator
( System.Traceback.Symbolic.Symbolic_Traceback'Access
);
i := -j;
end Test_CE;

Notes:

1. You can just use Symbolic_Traceback whenever you want. E.g. you can
set an exception handler and dump traceback at the raising point. It is
slow as hell, of course.

2. There are problems getting symbolic traceback working on ARM.

3. This becomes pretty much useless with relocated libraries. I do not
know how to make it work with them.

4. Under Windows you will not get traceback from non-GCC compiled stuff
because it does not understand *.pdb files.

Dmitry A. Kazakov

unread,
Jun 8, 2022, 5:08:46 AM6/8/22
to
On 2022-06-08 09:31, Jerry wrote:

> But has no effect on this program;
>
> procedure CE_2 is
> i : Positive;
> j : Integer := 1;
> begin
> i := -j;
> end CE_2;

I noticed that it requires a user-defined [sub]type AND nested subprogram:

procedure Test (A : Integer) is
subtype T is Integer range 0 .. 42;
i : T;
begin
i := A; -- This will work
end;
begin
Test (-1);

Jerry

unread,
Jun 8, 2022, 10:39:18 PM6/8/22
to
Hmm.... I get the same "Message" i.e. no extra information and only hex traceback info. Docs say -E is the same as -Ea meaning hex traceback, plus my set-up rejects -Ea (and -Es) as illegal switches. (Not sure at this point if we're chasing symbolic traceback or extra compiler information.)

Robin Vowels

unread,
Jun 9, 2022, 4:21:31 AM6/9/22
to
.
PL/I provides a description of an error, e.g. mentioning the condition
that has been raised, e.g., SUBSCRIPTRANGE, ZERODIVIDE.
The user can then take control and print the value of a variable or values of any
group of variables. E.g., for a SUBSCRIPTRANGE error, the user can also print the
upper and lower bounds of the array, though this is usually not done,
it being sufficient to print the name of the variable(s) and value(s).

Rod Kay

unread,
Oct 27, 2022, 7:57:08 AM10/27/22
to
On 8/6/22 19:08, Dmitry A. Kazakov wrote:
> On 2022-06-08 09:31, Jerry wrote:
>
>> But has no effect on this program;
>>
>> procedure CE_2 is
>>      i : Positive;
>>      j : Integer := 1;
>> begin
>>      i := -j;
>> end CE_2;
>
> I noticed that it requires a user-defined [sub]type AND nested subprogram:
>
>    procedure Test (A : Integer) is
>       subtype T is Integer range 0 .. 42;
>       i : T;
>    begin
>       i := A; -- This will work
>    end;
> begin
>    Test (-1);
>

Are you sure about this ?

I tried ...


procedure Example
is
X : Positive := 5;
begin
loop
X := X - 1;
end loop;
end Example;


... with the -gnateE and saw this result ...


[rod@orth example]$ ./example

raised CONSTRAINT_ERROR : example.adb:6:14 range check failed
value 0 not in 1..2147483647
[./example]
0x55916975377f Example at example.adb:6
0x559169753b74 Main at b__example.adb:193
[/usr/lib/libc.so.6]
0x7f9230be728e
0x7f9230be7348
[./example]
0x559169753623 _start at start.S:115
0xfffffffffffffffe


Jerry

unread,
Oct 28, 2022, 8:35:20 PM10/28/22
to
So -gnateE works part of the time?
Jerry

Rod Kay

unread,
Oct 28, 2022, 11:10:01 PM10/28/22
to
I've not tested this extensively but assume the extra info would be
shown in most cases.

One exception is the following ...


procedure Example
is
X : Positive := 0 with Unreferenced;
begin
null;
end Example;


... which shows a compile time warning ...


gprbuild -d -P/home/rod/Desktop/sandbox/ada/example/example.gpr
-XLace_Build_Mode=debug -XLace_Restrictions=xgc -XLace_OS=Linux
-Xrestrictions=xgc
Compile
[Ada] example.adb
example.adb:3:20: warning: value not in range of type
"Standard.Positive" [enabled by default]
example.adb:3:20: warning: Constraint_Error will be raised at run time
[enabled by default]
Bind
[gprbind] example.bexch
[Ada] example.ali
Link
[link] example.adb
[2022-10-29 13:56:36] process terminated successfully, elapsed time: 00.60s


... and when run ...


[rod@orth example]$ ./example

raised CONSTRAINT_ERROR : example.adb:3 range check failed
[./example]
0x560439f23710 Example at example.adb:3
0x560439f23b00 Main at b__example.adb:193
[/usr/lib/libc.so.6]
0x7f746d52928e
0x7f746d529348
[./example]
0x560439f23623 _start at start.S:115
0xfffffffffffffffe


I'm a little surprised that the compiler shows a warning rather than
an error but I suppose there must be some reason for that.

Gautier write-only address

unread,
Oct 29, 2022, 2:30:03 AM10/29/22
to
> One exception is the following ...
>
> procedure Example
> is
> X : Positive := 0 with Unreferenced;
> begin
> null;
> end Example;
>
> ... which shows a compile time warning ...
> [...]
> I'm a little surprised that the compiler shows a warning rather than
> an error but I suppose there must be some reason for that.

HAC (and perhaps other compilers) is stricter on that point:
procedure Example is
X : Positive := 0;
begin
null;
end Example;

example.adb: 2:20-21: error in range constraint: value of expression (0) is out the destination's range, 1 .. 9223372036854775807
E

G.B.

unread,
Oct 29, 2022, 4:59:55 AM10/29/22
to
On 29.10.22 05:10, Rod Kay wrote:

>    I'm a little surprised that the compiler shows a warning rather than an error but I suppose there must be some reason for that.


Formal language reasons aside, a program's author may have wanted
the program to raise an exception. (For example: "There is
a TODO here, so make sure the program never runs past this point.
Assign out of range.")

GNAT just emits a warning for the following program, albeit not the
same warning.
BTW, what would SPARK do here?

procedure Excpt
is
Works_As_Expected: exception;

procedure Test is
begin
if 1 = 1 then
raise Works_As_Expected;
else
raise Program_Error;
end if;
end Test;
begin
Test;
end Excpt;

Jerry

unread,
Oct 31, 2022, 8:05:06 PM10/31/22
to
I simply do not get the robust results others are reporting, and as I noted earlier in this thread, some of the suggested switches are flagged as illegal on macOS

From the docs at https://gcc.gnu.org/onlinedocs/gcc-4.9.4/gnat_ugn_unw/Symbolic-Traceback.html#Symbolic-Traceback:
"Note that this feature is not supported on all platforms. See GNAT.Traceback.Symbolic spec in g-trasym.ads for a complete list of currently supported platforms."

I'm using an older version of GNAT (2017) on Mac and the list of supported platforms is actually in s-trasym.ads and reads as such:
-- The full capability is currently supported on the following targets:

-- HP-UX ia64
-- GNU/Linux x86, x86_64, ia64
-- FreeBSD x86, x86_64
-- Solaris sparc and x86
-- Windows

Maybe newer versions of GNAT are more Mac-friendly. So in order to get both extra information and symbolic tracebacks I've had to write my own garbage-y handler that redirects text output to a file, calls system stuff such as tail and atos, bla bla bla. I hope atos is still supported when I eventually move to ARM. atos I think is Apple's thing that works like addr2line.

BTW, for others attempting this on macOS, turn of address randomization with -no_pie. That is, in my gpr, I have this:
package Linker is
for Default_Switches ("Ada") use ("-Wl,-no_pie");
end Linker;
Reply all
Reply to author
Forward
0 new messages