1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- S Y S T E M . T R A C E B A C K . S Y M B O L I C --
9 -- Copyright (C) 1999-2023, AdaCore --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 -- Run-time symbolic traceback support for targets using DWARF debug data
34 with Ada
.Unchecked_Deallocation
;
36 with Ada
.Exceptions
.Traceback
; use Ada
.Exceptions
.Traceback
;
37 with Ada
.Containers
.Generic_Array_Sort
;
39 with System
.Address_To_Access_Conversions
;
40 with System
.Soft_Links
;
42 with System
.Dwarf_Lines
;
43 with System
.Exception_Traces
;
44 with System
.Standard_Library
;
45 with System
.Traceback_Entries
;
47 with System
.Bounded_Strings
;
49 package body System
.Traceback
.Symbolic
is
51 use System
.Bounded_Strings
;
52 use System
.Dwarf_Lines
;
54 subtype Big_String
is String (Positive);
55 -- To deal with C strings
57 package Big_String_Conv
is new System
.Address_To_Access_Conversions
61 type Module_Cache_Acc
is access all Module_Cache
;
63 type Module_Cache
is record
64 Name
: Strings
.String_Access
;
67 C
: Dwarf_Context
(In_Exception
=> True);
68 -- Context to symbolize an address within this module
70 Chain
: Module_Cache_Acc
;
73 procedure Free
is new Ada
.Unchecked_Deallocation
77 Cache_Chain
: Module_Cache_Acc
;
78 -- Simply linked list of modules
80 type Module_Array
is array (Natural range <>) of Module_Cache_Acc
;
81 type Module_Array_Acc
is access Module_Array
;
83 Modules_Cache
: Module_Array_Acc
;
84 -- Sorted array of cached modules (if not null)
86 Exec_Module
: aliased Module_Cache
;
87 -- Context for the executable
89 type Init_State
is (Uninitialized
, Initialized
, Failed
);
90 Exec_Module_State
: Init_State
:= Uninitialized
;
91 -- How Exec_Module is initialized
93 procedure Init_Exec_Module
;
94 -- Initialize Exec_Module if not already initialized
96 function Symbolic_Traceback
97 (Traceback
: System
.Traceback_Entries
.Tracebacks_Array
;
98 Suppress_Hex
: Boolean) return String;
99 function Symbolic_Traceback
100 (E
: Ada
.Exceptions
.Exception_Occurrence
;
101 Suppress_Hex
: Boolean) return String;
102 -- Suppress_Hex means do not print any hexadecimal addresses, even if the
103 -- symbol is not available.
105 function Lt
(Left
, Right
: Module_Cache_Acc
) return Boolean;
106 -- Sort function for Module_Cache
108 procedure Init_Module
109 (Module
: out Module_Cache
;
110 Success
: out Boolean;
111 Module_Name
: String;
112 Load_Address
: Address
:= Null_Address
);
115 procedure Close_Module
(Module
: in out Module_Cache
);
118 function Value
(Item
: System
.Address
) return String;
119 -- Return the String contained in Item, up until the first NUL character
121 pragma Warnings
(Off
, "*Add_Module_To_Cache*");
122 procedure Add_Module_To_Cache
(Module_Name
: String;
123 Load_Address
: System
.Address
);
124 -- To be called by Build_Cache_For_All_Modules to add a new module to the
125 -- list. May not be referenced.
127 package Module_Name
is
129 procedure Build_Cache_For_All_Modules
;
130 -- Create the cache for all current modules
132 function Get
(Addr
: System
.Address
;
133 Load_Addr
: access System
.Address
) return String;
134 -- Returns the module name for the given address Addr, or an empty
135 -- string for the main executable. Load_Addr is set to the shared
136 -- library load address if this information is available, or to
137 -- System.Null_Address otherwise.
139 function Is_Supported
return Boolean;
140 pragma Inline
(Is_Supported
);
141 -- Returns True if Module_Name is supported, so if the traceback is
142 -- supported for shared libraries.
146 package body Module_Name
is separate;
148 function Executable_Name
return String;
149 -- Returns the executable name as reported by argv[0]. If gnat_argv not
150 -- initialized, return an empty string. If the argv[0] executable is not
151 -- found in the PATH, return it unresolved.
153 function Get_Executable_Load_Address
return System
.Address
;
156 Get_Executable_Load_Address
,
157 "__gnat_get_executable_load_address");
158 -- Get the load address of the executable, or Null_Address if not known
160 procedure Hexa_Traceback
161 (Traceback
: Tracebacks_Array
;
162 Suppress_Hex
: Boolean;
163 Res
: in out Bounded_String
);
164 -- Non-symbolic traceback (simply write addresses in hexa)
166 procedure Symbolic_Traceback_No_Lock
167 (Traceback
: Tracebacks_Array
;
168 Suppress_Hex
: Boolean;
169 Res
: in out Bounded_String
);
170 -- Like the public Symbolic_Traceback_No_Lock except there is no provision
171 -- against concurrent accesses.
173 procedure Module_Symbolic_Traceback
174 (Traceback
: Tracebacks_Array
;
175 Module
: Module_Cache
;
176 Suppress_Hex
: Boolean;
177 Res
: in out Bounded_String
);
178 -- Returns the Traceback for a given module
180 procedure Multi_Module_Symbolic_Traceback
181 (Traceback
: Tracebacks_Array
;
182 Suppress_Hex
: Boolean;
183 Res
: in out Bounded_String
);
184 -- Build string containing symbolic traceback for the given call chain
186 procedure Multi_Module_Symbolic_Traceback
187 (Traceback
: Tracebacks_Array
;
188 Module
: Module_Cache
;
189 Suppress_Hex
: Boolean;
190 Res
: in out Bounded_String
);
191 -- Likewise but using Module
193 Max_String_Length
: constant := 4096;
194 -- Arbitrary limit on Bounded_Str length
200 function Value
(Item
: System
.Address
) return String is
202 if Item
/= Null_Address
then
203 for J
in Big_String
'Range loop
204 if Big_String_Conv
.To_Pointer
(Item
) (J
) = ASCII
.NUL
then
205 return Big_String_Conv
.To_Pointer
(Item
) (1 .. J
- 1);
213 -------------------------
214 -- Add_Module_To_Cache --
215 -------------------------
217 procedure Add_Module_To_Cache
(Module_Name
: String;
218 Load_Address
: System
.Address
)
220 Module
: Module_Cache_Acc
;
223 Module
:= new Module_Cache
;
224 Init_Module
(Module
.all, Success
, Module_Name
, Load_Address
);
229 Module
.Chain
:= Cache_Chain
;
230 Cache_Chain
:= Module
;
231 end Add_Module_To_Cache
;
233 ----------------------
234 -- Init_Exec_Module --
235 ----------------------
237 procedure Init_Exec_Module
is
239 if Exec_Module_State
= Uninitialized
then
241 Exec_Path
: constant String := Executable_Name
;
242 Exec_Load
: constant Address
:= Get_Executable_Load_Address
;
245 Init_Module
(Exec_Module
, Success
, Exec_Path
, Exec_Load
);
248 Exec_Module_State
:= Initialized
;
250 Exec_Module_State
:= Failed
;
254 end Init_Exec_Module
;
260 function Lt
(Left
, Right
: Module_Cache_Acc
) return Boolean is
262 return Low_Address
(Left
.C
) < Low_Address
(Right
.C
);
265 -----------------------------
266 -- Module_Cache_Array_Sort --
267 -----------------------------
269 procedure Module_Cache_Array_Sort
is new Ada
.Containers
.Generic_Array_Sort
279 procedure Enable_Cache
(Include_Modules
: Boolean := False) is
281 -- Can be called at most once
282 if Cache_Chain
/= null then
289 if Exec_Module_State
= Failed
then
290 raise Program_Error
with
291 "cannot enable cache, executable state initialization failed.";
294 Cache_Chain
:= Exec_Module
'Access;
296 if Include_Modules
then
297 Module_Name
.Build_Cache_For_All_Modules
;
300 -- Build and fill the array of modules
303 Module
: Module_Cache_Acc
;
305 for Phase
in 1 .. 2 loop
307 Module
:= Cache_Chain
;
308 while Module
/= null loop
312 Enable_Cache
(Module
.C
);
314 Modules_Cache
(Count
) := Module
;
316 Module
:= Module
.Chain
;
320 Modules_Cache
:= new Module_Array
(1 .. Count
);
326 Module_Cache_Array_Sort
(Modules_Cache
.all);
329 ---------------------
330 -- Executable_Name --
331 ---------------------
333 function Executable_Name
return String is
334 -- We have to import gnat_argv as an Address to match the type of
335 -- gnat_argv in the binder generated file. Otherwise, we get spurious
336 -- warnings about type mismatch when LTO is turned on.
338 Gnat_Argv
: System
.Address
;
339 pragma Import
(C
, Gnat_Argv
, "gnat_argv");
341 type Argv_Array
is array (0 .. 0) of System
.Address
;
342 package Conv
is new System
.Address_To_Access_Conversions
(Argv_Array
);
344 function locate_exec_on_path
(A
: System
.Address
) return System
.Address
;
345 pragma Import
(C
, locate_exec_on_path
, "__gnat_locate_exec_on_path");
348 if Gnat_Argv
= Null_Address
then
352 -- See if we can resolve argv[0] to a full path (to a file that we will
353 -- be able to open). If the resolution fails, we were probably spawned
354 -- by an imprecise exec call, typically passing a mere file name as
355 -- argv[0] for a program in the current directory with '.' not on PATH.
356 -- Best we can do is fallback to argv[0] unchanged in this case. If we
357 -- fail opening that downstream, we'll just bail out.
360 Argv0
: constant System
.Address
361 := Conv
.To_Pointer
(Gnat_Argv
) (0);
363 Resolved_Argv0
: constant System
.Address
364 := locate_exec_on_path
(Argv0
);
366 Exe_Argv
: constant System
.Address
367 := (if Resolved_Argv0
/= System
.Null_Address
371 Result
: constant String := Value
(Exe_Argv
);
374 -- The buffer returned by locate_exec_on_path was allocated using
375 -- malloc and we should release this memory.
377 if Resolved_Argv0
/= Null_Address
then
378 System
.CRTL
.free
(Resolved_Argv0
);
389 procedure Close_Module
(Module
: in out Module_Cache
) is
392 Strings
.Free
(Module
.Name
);
399 procedure Init_Module
400 (Module
: out Module_Cache
;
401 Success
: out Boolean;
402 Module_Name
: String;
403 Load_Address
: Address
:= Null_Address
)
406 -- Early return if the module is not known
408 if Module_Name
= "" then
413 Open
(Module_Name
, Module
.C
, Success
);
415 -- If a module can't be opened just return now, we just cannot give more
416 -- information in this case.
422 Set_Load_Address
(Module
.C
, Load_Address
);
424 Module
.Name
:= new String'(Module_Name);
427 -------------------------------
428 -- Module_Symbolic_Traceback --
429 -------------------------------
431 procedure Module_Symbolic_Traceback
432 (Traceback : Tracebacks_Array;
433 Module : Module_Cache;
434 Suppress_Hex : Boolean;
435 Res : in out Bounded_String)
439 if Symbolic.Module_Name.Is_Supported then
441 Append (Res, Module.Name.all);
442 Append (Res, ']' & ASCII.LF);
445 Dwarf_Lines.Symbolic_Traceback
453 Hexa_Traceback (Traceback, Suppress_Hex, Res);
456 -- We must not allow an unhandled exception here, since this function
457 -- may be installed as a decorator for all automatic exceptions.
462 end Module_Symbolic_Traceback;
464 -------------------------------------
465 -- Multi_Module_Symbolic_Traceback --
466 -------------------------------------
468 procedure Multi_Module_Symbolic_Traceback
469 (Traceback : Tracebacks_Array;
470 Suppress_Hex : Boolean;
471 Res : in out Bounded_String)
473 F : constant Natural := Traceback'First;
475 if Traceback'Length = 0 or else Is_Full (Res) then
479 if Modules_Cache /= null then
480 -- Search in the cache
483 Addr : constant Address := Traceback (F);
484 Hi, Lo, Mid : Natural;
486 Lo := Modules_Cache'First;
487 Hi := Modules_Cache'Last;
489 Mid := (Lo + Hi) / 2;
490 if Addr < Low_Address (Modules_Cache (Mid).C) then
492 elsif Is_Inside (Modules_Cache (Mid).C, Addr) then
493 Multi_Module_Symbolic_Traceback
495 Modules_Cache (Mid).all,
505 Hexa_Traceback (Traceback (F .. F), Suppress_Hex, Res);
506 Multi_Module_Symbolic_Traceback
507 (Traceback (F + 1 .. Traceback'Last),
513 -- First try the executable
514 if Is_Inside (Exec_Module.C, Traceback (F)) then
515 Multi_Module_Symbolic_Traceback
523 -- Otherwise, try a shared library
525 Load_Addr : aliased System.Address;
526 M_Name : constant String :=
527 Module_Name.Get (Addr => Traceback (F),
528 Load_Addr => Load_Addr'Access);
529 Module : Module_Cache;
532 Init_Module (Module, Success, M_Name, Load_Addr);
534 Multi_Module_Symbolic_Traceback
539 Close_Module (Module);
542 Hexa_Traceback (Traceback (F .. F), Suppress_Hex, Res);
543 Multi_Module_Symbolic_Traceback
544 (Traceback (F + 1 .. Traceback'Last),
550 end Multi_Module_Symbolic_Traceback;
552 procedure Multi_Module_Symbolic_Traceback
553 (Traceback : Tracebacks_Array;
554 Module : Module_Cache;
555 Suppress_Hex : Boolean;
556 Res : in out Bounded_String)
560 -- Will symbolize the first address...
562 Pos := Traceback'First + 1;
564 -- ... and all addresses in the same module
568 exit Same_Module when Pos > Traceback'Last;
570 -- Get address to check for corresponding module name
572 exit Same_Module when not Is_Inside (Module.C, Traceback (Pos));
575 end loop Same_Module;
577 Module_Symbolic_Traceback
578 (Traceback (Traceback'First .. Pos - 1),
582 Multi_Module_Symbolic_Traceback
583 (Traceback (Pos .. Traceback'Last),
586 end Multi_Module_Symbolic_Traceback;
592 procedure Hexa_Traceback
593 (Traceback : Tracebacks_Array;
594 Suppress_Hex : Boolean;
595 Res : in out Bounded_String)
597 use System.Traceback_Entries;
601 Append (Res, ASCII.LF);
603 for J in Traceback'Range loop
604 Append_Address (Res, PC_For (Traceback (J)));
605 Append (Res, ASCII.LF);
610 --------------------------------
611 -- Symbolic_Traceback_No_Lock --
612 --------------------------------
614 procedure Symbolic_Traceback_No_Lock
615 (Traceback : Tracebacks_Array;
616 Suppress_Hex : Boolean;
617 Res : in out Bounded_String)
620 if Symbolic.Module_Name.Is_Supported then
621 Multi_Module_Symbolic_Traceback (Traceback, Suppress_Hex, Res);
623 if Exec_Module_State = Failed then
624 Append (Res, "Call stack traceback locations:" & ASCII.LF);
625 Hexa_Traceback (Traceback, Suppress_Hex, Res);
627 Module_Symbolic_Traceback
634 end Symbolic_Traceback_No_Lock;
636 ------------------------
637 -- Symbolic_Traceback --
638 ------------------------
640 function Symbolic_Traceback
641 (Traceback : Tracebacks_Array;
642 Suppress_Hex : Boolean) return String
644 Res : Bounded_String (Max_Length => Max_String_Length);
646 System.Soft_Links.Lock_Task.all;
648 Symbolic_Traceback_No_Lock (Traceback, Suppress_Hex, Res);
649 System.Soft_Links.Unlock_Task.all;
651 return To_String (Res);
655 System.Soft_Links.Unlock_Task.all;
657 end Symbolic_Traceback;
659 function Symbolic_Traceback
660 (Traceback : System.Traceback_Entries.Tracebacks_Array) return String is
662 return Symbolic_Traceback (Traceback, Suppress_Hex => False);
663 end Symbolic_Traceback;
665 function Symbolic_Traceback_No_Hex
666 (Traceback : System.Traceback_Entries.Tracebacks_Array) return String is
668 return Symbolic_Traceback (Traceback, Suppress_Hex => True);
669 end Symbolic_Traceback_No_Hex;
671 function Symbolic_Traceback
672 (E : Ada.Exceptions.Exception_Occurrence;
673 Suppress_Hex : Boolean) return String
676 return Symbolic_Traceback
677 (Ada.Exceptions.Traceback.Tracebacks (E),
679 end Symbolic_Traceback;
681 function Symbolic_Traceback
682 (E : Ada.Exceptions.Exception_Occurrence) return String
685 return Symbolic_Traceback (E, Suppress_Hex => False);
686 end Symbolic_Traceback;
688 function Symbolic_Traceback_No_Hex
689 (E : Ada.Exceptions.Exception_Occurrence) return String is
691 return Symbolic_Traceback (E, Suppress_Hex => True);
692 end Symbolic_Traceback_No_Hex;
694 Exception_Tracebacks_Symbolic : constant Integer;
697 Exception_Tracebacks_Symbolic,
698 "__gl_exception_tracebacks_symbolic");
699 -- Boolean indicating whether symbolic tracebacks should be generated.
701 use Standard_Library;
703 -- If this version of this package is available, and the binder switch -Es
704 -- was given, then we want to use this as the decorator by default, and we
705 -- want to turn on tracing for Unhandled_Raise_In_Main. Note that the user
706 -- cannot have already set Exception_Trace, because the runtime library is
707 -- elaborated before user-defined code.
709 if Exception_Tracebacks_Symbolic /= 0 then
710 Exception_Traces.Set_Trace_Decorator (Symbolic_Traceback'Access);
711 pragma Assert (Exception_Trace = RM_Convention);
712 Exception_Trace := Unhandled_Raise_In_Main;
714 end System.Traceback.Symbolic;