1------------------------------------------------------------------------------ 2-- -- 3-- GNAT RUN-TIME COMPONENTS -- 4-- -- 5-- S Y S T E M . T R A C E B A C K . S Y M B O L I C -- 6-- -- 7-- B o d y -- 8-- -- 9-- Copyright (C) 1999-2018, AdaCore -- 10-- -- 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. -- 17-- -- 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. -- 21-- -- 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/>. -- 26-- -- 27-- GNAT was originally developed by the GNAT team at New York University. -- 28-- Extensive contributions were provided by Ada Core Technologies Inc. -- 29-- -- 30------------------------------------------------------------------------------ 31 32-- Run-time symbolic traceback support for targets using DWARF debug data 33 34pragma Polling (Off); 35-- We must turn polling off for this unit, because otherwise we can get 36-- elaboration circularities when polling is turned on. 37 38with Ada.Unchecked_Deallocation; 39 40with Ada.Exceptions.Traceback; use Ada.Exceptions.Traceback; 41with Ada.Containers.Generic_Array_Sort; 42 43with System.Address_To_Access_Conversions; 44with System.Soft_Links; 45with System.CRTL; 46with System.Dwarf_Lines; 47with System.Exception_Traces; 48with System.Standard_Library; 49with System.Traceback_Entries; 50with System.Strings; 51with System.Bounded_Strings; 52 53package body System.Traceback.Symbolic is 54 55 use System.Bounded_Strings; 56 use System.Dwarf_Lines; 57 58 subtype Big_String is String (Positive); 59 -- To deal with C strings 60 61 package Big_String_Conv is new System.Address_To_Access_Conversions 62 (Big_String); 63 64 type Module_Cache; 65 type Module_Cache_Acc is access all Module_Cache; 66 67 type Module_Cache is record 68 Name : Strings.String_Access; 69 -- Name of the module 70 71 C : Dwarf_Context (In_Exception => True); 72 -- Context to symbolize an address within this module 73 74 Chain : Module_Cache_Acc; 75 end record; 76 77 procedure Free is new Ada.Unchecked_Deallocation 78 (Module_Cache, 79 Module_Cache_Acc); 80 81 Cache_Chain : Module_Cache_Acc; 82 -- Simply linked list of modules 83 84 type Module_Array is array (Natural range <>) of Module_Cache_Acc; 85 type Module_Array_Acc is access Module_Array; 86 87 Modules_Cache : Module_Array_Acc; 88 -- Sorted array of cached modules (if not null) 89 90 Exec_Module : aliased Module_Cache; 91 -- Context for the executable 92 93 type Init_State is (Uninitialized, Initialized, Failed); 94 Exec_Module_State : Init_State := Uninitialized; 95 -- How Exec_Module is initialized 96 97 procedure Init_Exec_Module; 98 -- Initialize Exec_Module if not already initialized 99 100 function Symbolic_Traceback 101 (Traceback : System.Traceback_Entries.Tracebacks_Array; 102 Suppress_Hex : Boolean) return String; 103 function Symbolic_Traceback 104 (E : Ada.Exceptions.Exception_Occurrence; 105 Suppress_Hex : Boolean) return String; 106 -- Suppress_Hex means do not print any hexadecimal addresses, even if the 107 -- symbol is not available. 108 109 function Lt (Left, Right : Module_Cache_Acc) return Boolean; 110 -- Sort function for Module_Cache 111 112 procedure Init_Module 113 (Module : out Module_Cache; 114 Success : out Boolean; 115 Module_Name : String; 116 Load_Address : Address := Null_Address); 117 -- Initialize Module 118 119 procedure Close_Module (Module : in out Module_Cache); 120 -- Finalize Module 121 122 function Value (Item : System.Address) return String; 123 -- Return the String contained in Item, up until the first NUL character 124 125 pragma Warnings (Off, "*Add_Module_To_Cache*"); 126 procedure Add_Module_To_Cache (Module_Name : String); 127 -- To be called by Build_Cache_For_All_Modules to add a new module to the 128 -- list. May not be referenced. 129 130 package Module_Name is 131 132 procedure Build_Cache_For_All_Modules; 133 -- Create the cache for all current modules 134 135 function Get (Addr : System.Address; 136 Load_Addr : access System.Address) return String; 137 -- Returns the module name for the given address Addr, or an empty 138 -- string for the main executable. Load_Addr is set to the shared 139 -- library load address if this information is available, or to 140 -- System.Null_Address otherwise. 141 142 function Is_Supported return Boolean; 143 pragma Inline (Is_Supported); 144 -- Returns True if Module_Name is supported, so if the traceback is 145 -- supported for shared libraries. 146 147 end Module_Name; 148 149 package body Module_Name is separate; 150 151 function Executable_Name return String; 152 -- Returns the executable name as reported by argv[0]. If gnat_argv not 153 -- initialized or if argv[0] executable not found in path, function returns 154 -- an empty string. 155 156 function Get_Executable_Load_Address return System.Address; 157 pragma Import 158 (C, 159 Get_Executable_Load_Address, 160 "__gnat_get_executable_load_address"); 161 -- Get the load address of the executable, or Null_Address if not known 162 163 procedure Hexa_Traceback 164 (Traceback : Tracebacks_Array; 165 Suppress_Hex : Boolean; 166 Res : in out Bounded_String); 167 -- Non-symbolic traceback (simply write addresses in hexa) 168 169 procedure Symbolic_Traceback_No_Lock 170 (Traceback : Tracebacks_Array; 171 Suppress_Hex : Boolean; 172 Res : in out Bounded_String); 173 -- Like the public Symbolic_Traceback_No_Lock except there is no provision 174 -- against concurrent accesses. 175 176 procedure Module_Symbolic_Traceback 177 (Traceback : Tracebacks_Array; 178 Module : Module_Cache; 179 Suppress_Hex : Boolean; 180 Res : in out Bounded_String); 181 -- Returns the Traceback for a given module 182 183 procedure Multi_Module_Symbolic_Traceback 184 (Traceback : Tracebacks_Array; 185 Suppress_Hex : Boolean; 186 Res : in out Bounded_String); 187 -- Build string containing symbolic traceback for the given call chain 188 189 procedure Multi_Module_Symbolic_Traceback 190 (Traceback : Tracebacks_Array; 191 Module : Module_Cache; 192 Suppress_Hex : Boolean; 193 Res : in out Bounded_String); 194 -- Likewise but using Module 195 196 Max_String_Length : constant := 4096; 197 -- Arbitrary limit on Bounded_Str length 198 199 ----------- 200 -- Value -- 201 ----------- 202 203 function Value (Item : System.Address) return String is 204 begin 205 if Item /= Null_Address then 206 for J in Big_String'Range loop 207 if Big_String_Conv.To_Pointer (Item) (J) = ASCII.NUL then 208 return Big_String_Conv.To_Pointer (Item) (1 .. J - 1); 209 end if; 210 end loop; 211 end if; 212 213 return ""; 214 end Value; 215 216 ------------------------- 217 -- Add_Module_To_Cache -- 218 ------------------------- 219 220 procedure Add_Module_To_Cache (Module_Name : String) is 221 Module : Module_Cache_Acc; 222 Success : Boolean; 223 begin 224 Module := new Module_Cache; 225 Init_Module (Module.all, Success, Module_Name); 226 if not Success then 227 Free (Module); 228 return; 229 end if; 230 Module.Chain := Cache_Chain; 231 Cache_Chain := Module; 232 end Add_Module_To_Cache; 233 234 ---------------------- 235 -- Init_Exec_Module -- 236 ---------------------- 237 238 procedure Init_Exec_Module is 239 begin 240 if Exec_Module_State = Uninitialized then 241 declare 242 Exec_Path : constant String := Executable_Name; 243 Exec_Load : constant Address := Get_Executable_Load_Address; 244 Success : Boolean; 245 begin 246 Init_Module (Exec_Module, Success, Exec_Path, Exec_Load); 247 248 if Success then 249 Exec_Module_State := Initialized; 250 else 251 Exec_Module_State := Failed; 252 end if; 253 end; 254 end if; 255 end Init_Exec_Module; 256 257 -------- 258 -- Lt -- 259 -------- 260 261 function Lt (Left, Right : Module_Cache_Acc) return Boolean is 262 begin 263 return Low (Left.C) < Low (Right.C); 264 end Lt; 265 266 ----------------------------- 267 -- Module_Cache_Array_Sort -- 268 ----------------------------- 269 270 procedure Module_Cache_Array_Sort is new Ada.Containers.Generic_Array_Sort 271 (Natural, 272 Module_Cache_Acc, 273 Module_Array, 274 Lt); 275 276 ------------------ 277 -- Enable_Cache -- 278 ------------------ 279 280 procedure Enable_Cache (Include_Modules : Boolean := False) is 281 begin 282 -- Can be called at most once 283 if Cache_Chain /= null then 284 return; 285 end if; 286 287 -- Add all modules 288 Init_Exec_Module; 289 Cache_Chain := Exec_Module'Access; 290 291 if Include_Modules then 292 Module_Name.Build_Cache_For_All_Modules; 293 end if; 294 295 -- Build and fill the array of modules 296 declare 297 Count : Natural; 298 Module : Module_Cache_Acc; 299 begin 300 for Phase in 1 .. 2 loop 301 Count := 0; 302 Module := Cache_Chain; 303 while Module /= null loop 304 Count := Count + 1; 305 306 if Phase = 1 then 307 Enable_Cache (Module.C); 308 else 309 Modules_Cache (Count) := Module; 310 end if; 311 Module := Module.Chain; 312 end loop; 313 314 if Phase = 1 then 315 Modules_Cache := new Module_Array (1 .. Count); 316 end if; 317 end loop; 318 end; 319 320 -- Sort the array 321 Module_Cache_Array_Sort (Modules_Cache.all); 322 end Enable_Cache; 323 324 --------------------- 325 -- Executable_Name -- 326 --------------------- 327 328 function Executable_Name return String is 329 -- We have to import gnat_argv as an Address to match the type of 330 -- gnat_argv in the binder generated file. Otherwise, we get spurious 331 -- warnings about type mismatch when LTO is turned on. 332 333 Gnat_Argv : System.Address; 334 pragma Import (C, Gnat_Argv, "gnat_argv"); 335 336 type Argv_Array is array (0 .. 0) of System.Address; 337 package Conv is new System.Address_To_Access_Conversions (Argv_Array); 338 339 function locate_exec_on_path (A : System.Address) return System.Address; 340 pragma Import (C, locate_exec_on_path, "__gnat_locate_exec_on_path"); 341 342 begin 343 if Gnat_Argv = Null_Address then 344 return ""; 345 end if; 346 347 declare 348 Addr : constant System.Address := 349 locate_exec_on_path (Conv.To_Pointer (Gnat_Argv) (0)); 350 Result : constant String := Value (Addr); 351 352 begin 353 -- The buffer returned by locate_exec_on_path was allocated using 354 -- malloc, so we should use free to release the memory. 355 356 if Addr /= Null_Address then 357 System.CRTL.free (Addr); 358 end if; 359 360 return Result; 361 end; 362 end Executable_Name; 363 364 ------------------ 365 -- Close_Module -- 366 ------------------ 367 368 procedure Close_Module (Module : in out Module_Cache) is 369 begin 370 Close (Module.C); 371 Strings.Free (Module.Name); 372 end Close_Module; 373 374 ----------------- 375 -- Init_Module -- 376 ----------------- 377 378 procedure Init_Module 379 (Module : out Module_Cache; 380 Success : out Boolean; 381 Module_Name : String; 382 Load_Address : Address := Null_Address) 383 is 384 begin 385 -- Early return if the module is not known 386 387 if Module_Name = "" then 388 Success := False; 389 return; 390 end if; 391 392 Open (Module_Name, Module.C, Success); 393 394 -- If a module can't be opened just return now, we just cannot give more 395 -- information in this case. 396 397 if not Success then 398 return; 399 end if; 400 401 Set_Load_Address (Module.C, Load_Address); 402 403 Module.Name := new String'(Module_Name); 404 end Init_Module; 405 406 ------------------------------- 407 -- Module_Symbolic_Traceback -- 408 ------------------------------- 409 410 procedure Module_Symbolic_Traceback 411 (Traceback : Tracebacks_Array; 412 Module : Module_Cache; 413 Suppress_Hex : Boolean; 414 Res : in out Bounded_String) 415 is 416 Success : Boolean := False; 417 begin 418 if Symbolic.Module_Name.Is_Supported then 419 Append (Res, '['); 420 Append (Res, Module.Name.all); 421 Append (Res, ']' & ASCII.LF); 422 end if; 423 424 Dwarf_Lines.Symbolic_Traceback 425 (Module.C, 426 Traceback, 427 Suppress_Hex, 428 Success, 429 Res); 430 431 if not Success then 432 Hexa_Traceback (Traceback, Suppress_Hex, Res); 433 end if; 434 435 -- We must not allow an unhandled exception here, since this function 436 -- may be installed as a decorator for all automatic exceptions. 437 438 exception 439 when others => 440 return; 441 end Module_Symbolic_Traceback; 442 443 ------------------------------------- 444 -- Multi_Module_Symbolic_Traceback -- 445 ------------------------------------- 446 447 procedure Multi_Module_Symbolic_Traceback 448 (Traceback : Tracebacks_Array; 449 Suppress_Hex : Boolean; 450 Res : in out Bounded_String) 451 is 452 F : constant Natural := Traceback'First; 453 begin 454 if Traceback'Length = 0 or else Is_Full (Res) then 455 return; 456 end if; 457 458 if Modules_Cache /= null then 459 -- Search in the cache 460 461 declare 462 Addr : constant Address := Traceback (F); 463 Hi, Lo, Mid : Natural; 464 begin 465 Lo := Modules_Cache'First; 466 Hi := Modules_Cache'Last; 467 while Lo <= Hi loop 468 Mid := (Lo + Hi) / 2; 469 if Addr < Low (Modules_Cache (Mid).C) then 470 Hi := Mid - 1; 471 elsif Is_Inside (Modules_Cache (Mid).C, Addr) then 472 Multi_Module_Symbolic_Traceback 473 (Traceback, 474 Modules_Cache (Mid).all, 475 Suppress_Hex, 476 Res); 477 return; 478 else 479 Lo := Mid + 1; 480 end if; 481 end loop; 482 483 -- Not found 484 Hexa_Traceback (Traceback (F .. F), Suppress_Hex, Res); 485 Multi_Module_Symbolic_Traceback 486 (Traceback (F + 1 .. Traceback'Last), 487 Suppress_Hex, 488 Res); 489 end; 490 else 491 492 -- First try the executable 493 if Is_Inside (Exec_Module.C, Traceback (F)) then 494 Multi_Module_Symbolic_Traceback 495 (Traceback, 496 Exec_Module, 497 Suppress_Hex, 498 Res); 499 return; 500 end if; 501 502 -- Otherwise, try a shared library 503 declare 504 Load_Addr : aliased System.Address; 505 M_Name : constant String := 506 Module_Name.Get (Addr => Traceback (F), 507 Load_Addr => Load_Addr'Access); 508 Module : Module_Cache; 509 Success : Boolean; 510 begin 511 Init_Module (Module, Success, M_Name, Load_Addr); 512 if Success then 513 Multi_Module_Symbolic_Traceback 514 (Traceback, 515 Module, 516 Suppress_Hex, 517 Res); 518 Close_Module (Module); 519 else 520 -- Module not found 521 Hexa_Traceback (Traceback (F .. F), Suppress_Hex, Res); 522 Multi_Module_Symbolic_Traceback 523 (Traceback (F + 1 .. Traceback'Last), 524 Suppress_Hex, 525 Res); 526 end if; 527 end; 528 end if; 529 end Multi_Module_Symbolic_Traceback; 530 531 procedure Multi_Module_Symbolic_Traceback 532 (Traceback : Tracebacks_Array; 533 Module : Module_Cache; 534 Suppress_Hex : Boolean; 535 Res : in out Bounded_String) 536 is 537 Pos : Positive; 538 begin 539 -- Will symbolize the first address... 540 541 Pos := Traceback'First + 1; 542 543 -- ... and all addresses in the same module 544 545 Same_Module : 546 loop 547 exit Same_Module when Pos > Traceback'Last; 548 549 -- Get address to check for corresponding module name 550 551 exit Same_Module when not Is_Inside (Module.C, Traceback (Pos)); 552 553 Pos := Pos + 1; 554 end loop Same_Module; 555 556 Module_Symbolic_Traceback 557 (Traceback (Traceback'First .. Pos - 1), 558 Module, 559 Suppress_Hex, 560 Res); 561 Multi_Module_Symbolic_Traceback 562 (Traceback (Pos .. Traceback'Last), 563 Suppress_Hex, 564 Res); 565 end Multi_Module_Symbolic_Traceback; 566 567 -------------------- 568 -- Hexa_Traceback -- 569 -------------------- 570 571 procedure Hexa_Traceback 572 (Traceback : Tracebacks_Array; 573 Suppress_Hex : Boolean; 574 Res : in out Bounded_String) 575 is 576 use System.Traceback_Entries; 577 begin 578 if Suppress_Hex then 579 Append (Res, "..."); 580 Append (Res, ASCII.LF); 581 else 582 for J in Traceback'Range loop 583 Append_Address (Res, PC_For (Traceback (J))); 584 Append (Res, ASCII.LF); 585 end loop; 586 end if; 587 end Hexa_Traceback; 588 589 -------------------------------- 590 -- Symbolic_Traceback_No_Lock -- 591 -------------------------------- 592 593 procedure Symbolic_Traceback_No_Lock 594 (Traceback : Tracebacks_Array; 595 Suppress_Hex : Boolean; 596 Res : in out Bounded_String) 597 is 598 begin 599 if Symbolic.Module_Name.Is_Supported then 600 Multi_Module_Symbolic_Traceback (Traceback, Suppress_Hex, Res); 601 else 602 if Exec_Module_State = Failed then 603 Append (Res, "Call stack traceback locations:" & ASCII.LF); 604 Hexa_Traceback (Traceback, Suppress_Hex, Res); 605 else 606 Module_Symbolic_Traceback 607 (Traceback, 608 Exec_Module, 609 Suppress_Hex, 610 Res); 611 end if; 612 end if; 613 end Symbolic_Traceback_No_Lock; 614 615 ------------------------ 616 -- Symbolic_Traceback -- 617 ------------------------ 618 619 function Symbolic_Traceback 620 (Traceback : Tracebacks_Array; 621 Suppress_Hex : Boolean) return String 622 is 623 Res : Bounded_String (Max_Length => Max_String_Length); 624 begin 625 System.Soft_Links.Lock_Task.all; 626 Init_Exec_Module; 627 Symbolic_Traceback_No_Lock (Traceback, Suppress_Hex, Res); 628 System.Soft_Links.Unlock_Task.all; 629 630 return To_String (Res); 631 632 exception 633 when others => 634 System.Soft_Links.Unlock_Task.all; 635 raise; 636 end Symbolic_Traceback; 637 638 function Symbolic_Traceback 639 (Traceback : System.Traceback_Entries.Tracebacks_Array) return String is 640 begin 641 return Symbolic_Traceback (Traceback, Suppress_Hex => False); 642 end Symbolic_Traceback; 643 644 function Symbolic_Traceback_No_Hex 645 (Traceback : System.Traceback_Entries.Tracebacks_Array) return String is 646 begin 647 return Symbolic_Traceback (Traceback, Suppress_Hex => True); 648 end Symbolic_Traceback_No_Hex; 649 650 function Symbolic_Traceback 651 (E : Ada.Exceptions.Exception_Occurrence; 652 Suppress_Hex : Boolean) return String 653 is 654 begin 655 return Symbolic_Traceback 656 (Ada.Exceptions.Traceback.Tracebacks (E), 657 Suppress_Hex); 658 end Symbolic_Traceback; 659 660 function Symbolic_Traceback 661 (E : Ada.Exceptions.Exception_Occurrence) return String 662 is 663 begin 664 return Symbolic_Traceback (E, Suppress_Hex => False); 665 end Symbolic_Traceback; 666 667 function Symbolic_Traceback_No_Hex 668 (E : Ada.Exceptions.Exception_Occurrence) return String is 669 begin 670 return Symbolic_Traceback (E, Suppress_Hex => True); 671 end Symbolic_Traceback_No_Hex; 672 673 Exception_Tracebacks_Symbolic : Integer; 674 pragma Import 675 (C, 676 Exception_Tracebacks_Symbolic, 677 "__gl_exception_tracebacks_symbolic"); 678 -- Boolean indicating whether symbolic tracebacks should be generated. 679 680 use Standard_Library; 681begin 682 -- If this version of this package is available, and the binder switch -Es 683 -- was given, then we want to use this as the decorator by default, and we 684 -- want to turn on tracing for Unhandled_Raise_In_Main. Note that the user 685 -- cannot have already set Exception_Trace, because the runtime library is 686 -- elaborated before user-defined code. 687 688 if Exception_Tracebacks_Symbolic /= 0 then 689 Exception_Traces.Set_Trace_Decorator (Symbolic_Traceback'Access); 690 pragma Assert (Exception_Trace = RM_Convention); 691 Exception_Trace := Unhandled_Raise_In_Main; 692 end if; 693end System.Traceback.Symbolic; 694