1------------------------------------------------------------------------------ 2-- -- 3-- GNAT COMPILER COMPONENTS -- 4-- -- 5-- ADA.EXCEPTIONS.EXCEPTION_DATA -- 6-- -- 7-- B o d y -- 8-- -- 9-- Copyright (C) 1992-2019, Free Software Foundation, Inc. -- 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 32with System.Storage_Elements; use System.Storage_Elements; 33 34separate (Ada.Exceptions) 35package body Exception_Data is 36 37 -- This unit implements the Exception_Information related services for 38 -- both the Ada standard requirements and the GNAT.Exception_Traces 39 -- facility. This is also used by the implementation of the stream 40 -- attributes of types Exception_Id and Exception_Occurrence. 41 42 -- There are common parts between the contents of Exception_Information 43 -- (the regular Ada interface) and Untailored_Exception_Information (used 44 -- for streaming, and when there is no symbolic traceback available) The 45 -- overall structure is sketched below: 46 47 -- 48 -- Untailored_Exception_Information 49 -- | 50 -- +-------+--------+ 51 -- | | 52 -- Basic_Exc_Info & Untailored_Exc_Tback 53 -- (B_E_I) (U_E_TB) 54 55 -- o-- 56 -- (B_E_I) | Exception_Name: <exception name> (as in Exception_Name) 57 -- | Message: <message> (or a null line if no message) 58 -- | PID=nnnn (if nonzero) 59 -- o-- 60 -- (U_E_TB) | Call stack traceback locations: 61 -- | <0xyyyyyyyy 0xyyyyyyyy ...> 62 -- o-- 63 64 -- Exception_Information 65 -- | 66 -- +----------+----------+ 67 -- | | 68 -- Basic_Exc_Info & traceback 69 -- | 70 -- +-----------+------------+ 71 -- | | 72 -- Untailored_Exc_Tback Or Tback_Decorator 73 -- if no decorator set otherwise 74 75 -- Functions returning String imply secondary stack use, which is a heavy 76 -- mechanism requiring run-time support. Besides, some of the routines we 77 -- provide here are to be used by the default Last_Chance_Handler, at the 78 -- critical point where the runtime is about to be finalized. Since most 79 -- of the items we have at hand are of bounded length, we also provide a 80 -- procedural interface able to incrementally append the necessary bits to 81 -- a preallocated buffer or output them straight to stderr. 82 83 -- The procedural interface is composed of two major sections: a neutral 84 -- section for basic types like Address, Character, Natural or String, and 85 -- an exception oriented section for the exception names, messages, and 86 -- information. This is the Append_Info family of procedures below. 87 88 -- Output to stderr is commanded by passing an empty buffer to update, and 89 -- care is taken not to overflow otherwise. 90 91 -------------------------------------------- 92 -- Procedural Interface - Neutral section -- 93 -------------------------------------------- 94 95 procedure Append_Info_Address 96 (A : Address; 97 Info : in out String; 98 Ptr : in out Natural); 99 100 procedure Append_Info_Character 101 (C : Character; 102 Info : in out String; 103 Ptr : in out Natural); 104 105 procedure Append_Info_Nat 106 (N : Natural; 107 Info : in out String; 108 Ptr : in out Natural); 109 110 procedure Append_Info_NL 111 (Info : in out String; 112 Ptr : in out Natural); 113 pragma Inline (Append_Info_NL); 114 115 procedure Append_Info_String 116 (S : String; 117 Info : in out String; 118 Ptr : in out Natural); 119 120 ------------------------------------------------------- 121 -- Procedural Interface - Exception oriented section -- 122 ------------------------------------------------------- 123 124 procedure Append_Info_Exception_Name 125 (Id : Exception_Id; 126 Info : in out String; 127 Ptr : in out Natural); 128 129 procedure Append_Info_Exception_Name 130 (X : Exception_Occurrence; 131 Info : in out String; 132 Ptr : in out Natural); 133 134 procedure Append_Info_Exception_Message 135 (X : Exception_Occurrence; 136 Info : in out String; 137 Ptr : in out Natural); 138 139 procedure Append_Info_Basic_Exception_Information 140 (X : Exception_Occurrence; 141 Info : in out String; 142 Ptr : in out Natural); 143 144 procedure Append_Info_Untailored_Exception_Traceback 145 (X : Exception_Occurrence; 146 Info : in out String; 147 Ptr : in out Natural); 148 149 procedure Append_Info_Untailored_Exception_Information 150 (X : Exception_Occurrence; 151 Info : in out String; 152 Ptr : in out Natural); 153 154 -- The "functional" interface to the exception information not involving 155 -- a traceback decorator uses preallocated intermediate buffers to avoid 156 -- the use of secondary stack. Preallocation requires preliminary length 157 -- computation, for which a series of functions are introduced: 158 159 --------------------------------- 160 -- Length evaluation utilities -- 161 --------------------------------- 162 163 function Basic_Exception_Info_Maxlength 164 (X : Exception_Occurrence) return Natural; 165 166 function Untailored_Exception_Traceback_Maxlength 167 (X : Exception_Occurrence) return Natural; 168 169 function Exception_Info_Maxlength 170 (X : Exception_Occurrence) return Natural; 171 172 function Exception_Name_Length 173 (Id : Exception_Id) return Natural; 174 175 function Exception_Name_Length 176 (X : Exception_Occurrence) return Natural; 177 178 function Exception_Message_Length 179 (X : Exception_Occurrence) return Natural; 180 181 -------------------------- 182 -- Functional Interface -- 183 -------------------------- 184 185 function Untailored_Exception_Traceback 186 (X : Exception_Occurrence) return String; 187 -- Returns an image of the complete call chain associated with an 188 -- exception occurrence in its most basic form, that is as a raw sequence 189 -- of hexadecimal addresses. 190 191 function Tailored_Exception_Traceback 192 (X : Exception_Occurrence) return String; 193 -- Returns an image of the complete call chain associated with an 194 -- exception occurrence, either in its basic form if no decorator is 195 -- in place, or as formatted by the decorator otherwise. 196 197 ----------------------------------------------------------------------- 198 -- Services for the default Last_Chance_Handler and the task wrapper -- 199 ----------------------------------------------------------------------- 200 201 pragma Export 202 (Ada, Append_Info_Exception_Message, "__gnat_append_info_e_msg"); 203 204 pragma Export 205 (Ada, Append_Info_Untailored_Exception_Information, 206 "__gnat_append_info_u_e_info"); 207 208 pragma Export 209 (Ada, Exception_Message_Length, "__gnat_exception_msg_len"); 210 211 function Get_Executable_Load_Address return System.Address; 212 pragma Import (C, Get_Executable_Load_Address, 213 "__gnat_get_executable_load_address"); 214 -- Get the load address of the executable, or Null_Address if not known 215 216 ------------------------- 217 -- Append_Info_Address -- 218 ------------------------- 219 220 procedure Append_Info_Address 221 (A : Address; 222 Info : in out String; 223 Ptr : in out Natural) 224 is 225 S : String (1 .. 18); 226 P : Natural; 227 N : Integer_Address; 228 229 H : constant array (Integer range 0 .. 15) of Character := 230 "0123456789abcdef"; 231 begin 232 P := S'Last; 233 N := To_Integer (A); 234 loop 235 S (P) := H (Integer (N mod 16)); 236 P := P - 1; 237 N := N / 16; 238 exit when N = 0; 239 end loop; 240 241 S (P - 1) := '0'; 242 S (P) := 'x'; 243 244 Append_Info_String (S (P - 1 .. S'Last), Info, Ptr); 245 end Append_Info_Address; 246 247 --------------------------------------------- 248 -- Append_Info_Basic_Exception_Information -- 249 --------------------------------------------- 250 251 -- To ease the maximum length computation, we define and pull out some 252 -- string constants: 253 254 BEI_Name_Header : constant String := "raised "; 255 BEI_Msg_Header : constant String := " : "; 256 BEI_PID_Header : constant String := "PID: "; 257 258 procedure Append_Info_Basic_Exception_Information 259 (X : Exception_Occurrence; 260 Info : in out String; 261 Ptr : in out Natural) 262 is 263 Name : String (1 .. Exception_Name_Length (X)); 264 -- Buffer in which to fetch the exception name, in order to check 265 -- whether this is an internal _ABORT_SIGNAL or a regular occurrence. 266 267 Name_Ptr : Natural := Name'First - 1; 268 269 begin 270 -- Output exception name and message except for _ABORT_SIGNAL, where 271 -- these two lines are omitted. 272 273 Append_Info_Exception_Name (X, Name, Name_Ptr); 274 275 if Name (Name'First) /= '_' then 276 Append_Info_String (BEI_Name_Header, Info, Ptr); 277 Append_Info_String (Name, Info, Ptr); 278 279 if Exception_Message_Length (X) /= 0 then 280 Append_Info_String (BEI_Msg_Header, Info, Ptr); 281 Append_Info_Exception_Message (X, Info, Ptr); 282 end if; 283 284 Append_Info_NL (Info, Ptr); 285 end if; 286 287 -- Output PID line if nonzero 288 289 if X.Pid /= 0 then 290 Append_Info_String (BEI_PID_Header, Info, Ptr); 291 Append_Info_Nat (X.Pid, Info, Ptr); 292 Append_Info_NL (Info, Ptr); 293 end if; 294 end Append_Info_Basic_Exception_Information; 295 296 --------------------------- 297 -- Append_Info_Character -- 298 --------------------------- 299 300 procedure Append_Info_Character 301 (C : Character; 302 Info : in out String; 303 Ptr : in out Natural) 304 is 305 begin 306 if Info'Length = 0 then 307 To_Stderr (C); 308 elsif Ptr < Info'Last then 309 Ptr := Ptr + 1; 310 Info (Ptr) := C; 311 end if; 312 end Append_Info_Character; 313 314 ----------------------------------- 315 -- Append_Info_Exception_Message -- 316 ----------------------------------- 317 318 procedure Append_Info_Exception_Message 319 (X : Exception_Occurrence; 320 Info : in out String; 321 Ptr : in out Natural) 322 is 323 begin 324 if X.Id = Null_Id then 325 raise Constraint_Error; 326 end if; 327 328 declare 329 Len : constant Natural := Exception_Message_Length (X); 330 Msg : constant String (1 .. Len) := X.Msg (1 .. Len); 331 begin 332 Append_Info_String (Msg, Info, Ptr); 333 end; 334 end Append_Info_Exception_Message; 335 336 -------------------------------- 337 -- Append_Info_Exception_Name -- 338 -------------------------------- 339 340 procedure Append_Info_Exception_Name 341 (Id : Exception_Id; 342 Info : in out String; 343 Ptr : in out Natural) 344 is 345 begin 346 if Id = Null_Id then 347 raise Constraint_Error; 348 end if; 349 350 declare 351 Len : constant Natural := Exception_Name_Length (Id); 352 Name : constant String (1 .. Len) := To_Ptr (Id.Full_Name) (1 .. Len); 353 begin 354 Append_Info_String (Name, Info, Ptr); 355 end; 356 end Append_Info_Exception_Name; 357 358 procedure Append_Info_Exception_Name 359 (X : Exception_Occurrence; 360 Info : in out String; 361 Ptr : in out Natural) 362 is 363 begin 364 Append_Info_Exception_Name (X.Id, Info, Ptr); 365 end Append_Info_Exception_Name; 366 367 ------------------------------ 368 -- Exception_Info_Maxlength -- 369 ------------------------------ 370 371 function Exception_Info_Maxlength 372 (X : Exception_Occurrence) return Natural 373 is 374 begin 375 return 376 Basic_Exception_Info_Maxlength (X) 377 + Untailored_Exception_Traceback_Maxlength (X); 378 end Exception_Info_Maxlength; 379 380 --------------------- 381 -- Append_Info_Nat -- 382 --------------------- 383 384 procedure Append_Info_Nat 385 (N : Natural; 386 Info : in out String; 387 Ptr : in out Natural) 388 is 389 begin 390 if N > 9 then 391 Append_Info_Nat (N / 10, Info, Ptr); 392 end if; 393 394 Append_Info_Character 395 (Character'Val (Character'Pos ('0') + N mod 10), Info, Ptr); 396 end Append_Info_Nat; 397 398 -------------------- 399 -- Append_Info_NL -- 400 -------------------- 401 402 procedure Append_Info_NL 403 (Info : in out String; 404 Ptr : in out Natural) 405 is 406 begin 407 Append_Info_Character (ASCII.LF, Info, Ptr); 408 end Append_Info_NL; 409 410 ------------------------ 411 -- Append_Info_String -- 412 ------------------------ 413 414 procedure Append_Info_String 415 (S : String; 416 Info : in out String; 417 Ptr : in out Natural) 418 is 419 begin 420 if Info'Length = 0 then 421 To_Stderr (S); 422 else 423 declare 424 Last : constant Natural := 425 Integer'Min (Ptr + S'Length, Info'Last); 426 begin 427 Info (Ptr + 1 .. Last) := S; 428 Ptr := Last; 429 end; 430 end if; 431 end Append_Info_String; 432 433 -------------------------------------------------- 434 -- Append_Info_Untailored_Exception_Information -- 435 -------------------------------------------------- 436 437 procedure Append_Info_Untailored_Exception_Information 438 (X : Exception_Occurrence; 439 Info : in out String; 440 Ptr : in out Natural) 441 is 442 begin 443 Append_Info_Basic_Exception_Information (X, Info, Ptr); 444 Append_Info_Untailored_Exception_Traceback (X, Info, Ptr); 445 end Append_Info_Untailored_Exception_Information; 446 447 ------------------------------------------------ 448 -- Append_Info_Untailored_Exception_Traceback -- 449 ------------------------------------------------ 450 451 -- As for Basic_Exception_Information: 452 453 BETB_Header : constant String := "Call stack traceback locations:"; 454 LDAD_Header : constant String := "Load address: "; 455 456 procedure Append_Info_Untailored_Exception_Traceback 457 (X : Exception_Occurrence; 458 Info : in out String; 459 Ptr : in out Natural) 460 is 461 Load_Address : Address; 462 463 begin 464 if X.Num_Tracebacks = 0 then 465 return; 466 end if; 467 468 -- The executable load address line 469 470 Load_Address := Get_Executable_Load_Address; 471 472 if Load_Address /= Null_Address then 473 Append_Info_String (LDAD_Header, Info, Ptr); 474 Append_Info_Address (Load_Address, Info, Ptr); 475 Append_Info_NL (Info, Ptr); 476 end if; 477 478 -- The traceback lines 479 480 Append_Info_String (BETB_Header, Info, Ptr); 481 Append_Info_NL (Info, Ptr); 482 483 for J in 1 .. X.Num_Tracebacks loop 484 Append_Info_Address (TBE.PC_For (X.Tracebacks (J)), Info, Ptr); 485 exit when J = X.Num_Tracebacks; 486 Append_Info_Character (' ', Info, Ptr); 487 end loop; 488 489 Append_Info_NL (Info, Ptr); 490 end Append_Info_Untailored_Exception_Traceback; 491 492 ------------------------------------ 493 -- Basic_Exception_Info_Maxlength -- 494 ------------------------------------ 495 496 function Basic_Exception_Info_Maxlength 497 (X : Exception_Occurrence) return Natural 498 is 499 begin 500 return 501 BEI_Name_Header'Length + Exception_Name_Length (X) 502 + BEI_Msg_Header'Length + Exception_Message_Length (X) + 1 503 + BEI_PID_Header'Length + 15; 504 end Basic_Exception_Info_Maxlength; 505 506 --------------------------- 507 -- Exception_Information -- 508 --------------------------- 509 510 function Exception_Information (X : Exception_Occurrence) return String is 511 -- The tailored exception information is the basic information 512 -- associated with the tailored call chain backtrace. 513 514 Tback_Info : constant String := Tailored_Exception_Traceback (X); 515 Tback_Len : constant Natural := Tback_Info'Length; 516 517 Info : String (1 .. Basic_Exception_Info_Maxlength (X) + Tback_Len); 518 Ptr : Natural := Info'First - 1; 519 520 begin 521 Append_Info_Basic_Exception_Information (X, Info, Ptr); 522 Append_Info_String (Tback_Info, Info, Ptr); 523 return Info (Info'First .. Ptr); 524 end Exception_Information; 525 526 ------------------------------ 527 -- Exception_Message_Length -- 528 ------------------------------ 529 530 function Exception_Message_Length 531 (X : Exception_Occurrence) return Natural 532 is 533 begin 534 return X.Msg_Length; 535 end Exception_Message_Length; 536 537 --------------------------- 538 -- Exception_Name_Length -- 539 --------------------------- 540 541 function Exception_Name_Length (Id : Exception_Id) return Natural is 542 begin 543 -- What is stored in the internal Name buffer includes a terminating 544 -- null character that we never care about. 545 546 return Id.Name_Length - 1; 547 end Exception_Name_Length; 548 549 function Exception_Name_Length (X : Exception_Occurrence) return Natural is 550 begin 551 return Exception_Name_Length (X.Id); 552 end Exception_Name_Length; 553 554 ------------------------------- 555 -- Untailored_Exception_Traceback -- 556 ------------------------------- 557 558 function Untailored_Exception_Traceback 559 (X : Exception_Occurrence) return String 560 is 561 Info : aliased String 562 (1 .. Untailored_Exception_Traceback_Maxlength (X)); 563 Ptr : Natural := Info'First - 1; 564 begin 565 Append_Info_Untailored_Exception_Traceback (X, Info, Ptr); 566 return Info (Info'First .. Ptr); 567 end Untailored_Exception_Traceback; 568 569 -------------------------------------- 570 -- Untailored_Exception_Information -- 571 -------------------------------------- 572 573 function Untailored_Exception_Information 574 (X : Exception_Occurrence) return String 575 is 576 Info : String (1 .. Exception_Info_Maxlength (X)); 577 Ptr : Natural := Info'First - 1; 578 begin 579 Append_Info_Untailored_Exception_Information (X, Info, Ptr); 580 return Info (Info'First .. Ptr); 581 end Untailored_Exception_Information; 582 583 ------------------------- 584 -- Set_Exception_C_Msg -- 585 ------------------------- 586 587 procedure Set_Exception_C_Msg 588 (Excep : EOA; 589 Id : Exception_Id; 590 Msg1 : System.Address; 591 Line : Integer := 0; 592 Column : Integer := 0; 593 Msg2 : System.Address := System.Null_Address) 594 is 595 Remind : Integer; 596 Ptr : Natural; 597 598 procedure Append_Number (Number : Integer); 599 -- Append given number to Excep.Msg 600 601 ------------------- 602 -- Append_Number -- 603 ------------------- 604 605 procedure Append_Number (Number : Integer) is 606 Val : Integer; 607 Size : Integer; 608 609 begin 610 if Number <= 0 then 611 return; 612 end if; 613 614 -- Compute the number of needed characters 615 616 Size := 1; 617 Val := Number; 618 while Val > 0 loop 619 Val := Val / 10; 620 Size := Size + 1; 621 end loop; 622 623 -- If enough characters are available, put the line number 624 625 if Excep.Msg_Length <= Exception_Msg_Max_Length - Size then 626 Excep.Msg (Excep.Msg_Length + 1) := ':'; 627 Excep.Msg_Length := Excep.Msg_Length + Size; 628 629 Val := Number; 630 Size := 0; 631 while Val > 0 loop 632 Remind := Val rem 10; 633 Val := Val / 10; 634 Excep.Msg (Excep.Msg_Length - Size) := 635 Character'Val (Remind + Character'Pos ('0')); 636 Size := Size + 1; 637 end loop; 638 end if; 639 end Append_Number; 640 641 -- Start of processing for Set_Exception_C_Msg 642 643 begin 644 Excep.Exception_Raised := False; 645 Excep.Id := Id; 646 Excep.Num_Tracebacks := 0; 647 Excep.Pid := Local_Partition_ID; 648 Excep.Msg_Length := 0; 649 650 while To_Ptr (Msg1) (Excep.Msg_Length + 1) /= ASCII.NUL 651 and then Excep.Msg_Length < Exception_Msg_Max_Length 652 loop 653 Excep.Msg_Length := Excep.Msg_Length + 1; 654 Excep.Msg (Excep.Msg_Length) := To_Ptr (Msg1) (Excep.Msg_Length); 655 end loop; 656 657 Append_Number (Line); 658 Append_Number (Column); 659 660 -- Append second message if present 661 662 if Msg2 /= System.Null_Address 663 and then Excep.Msg_Length + 1 < Exception_Msg_Max_Length 664 then 665 Excep.Msg_Length := Excep.Msg_Length + 1; 666 Excep.Msg (Excep.Msg_Length) := ' '; 667 668 Ptr := 1; 669 while To_Ptr (Msg2) (Ptr) /= ASCII.NUL 670 and then Excep.Msg_Length < Exception_Msg_Max_Length 671 loop 672 Excep.Msg_Length := Excep.Msg_Length + 1; 673 Excep.Msg (Excep.Msg_Length) := To_Ptr (Msg2) (Ptr); 674 Ptr := Ptr + 1; 675 end loop; 676 end if; 677 end Set_Exception_C_Msg; 678 679 ----------------------- 680 -- Set_Exception_Msg -- 681 ----------------------- 682 683 procedure Set_Exception_Msg 684 (Excep : EOA; 685 Id : Exception_Id; 686 Message : String) 687 is 688 Len : constant Natural := 689 Natural'Min (Message'Length, Exception_Msg_Max_Length); 690 First : constant Integer := Message'First; 691 begin 692 Excep.Exception_Raised := False; 693 Excep.Msg_Length := Len; 694 Excep.Msg (1 .. Len) := Message (First .. First + Len - 1); 695 Excep.Id := Id; 696 Excep.Num_Tracebacks := 0; 697 Excep.Pid := Local_Partition_ID; 698 end Set_Exception_Msg; 699 700 ---------------------------------- 701 -- Tailored_Exception_Traceback -- 702 ---------------------------------- 703 704 function Tailored_Exception_Traceback 705 (X : Exception_Occurrence) return String 706 is 707 -- We reference the decorator *wrapper* here and not the decorator 708 -- itself. The purpose of the local variable Wrapper is to prevent a 709 -- potential race condition in the code below. The atomicity of this 710 -- assignment is enforced by pragma Atomic in System.Soft_Links. 711 712 -- The potential race condition here, if no local variable was used, 713 -- relates to the test upon the wrapper's value and the call, which 714 -- are not performed atomically. With the local variable, potential 715 -- changes of the wrapper's global value between the test and the 716 -- call become inoffensive. 717 718 Wrapper : constant Traceback_Decorator_Wrapper_Call := 719 Traceback_Decorator_Wrapper; 720 721 begin 722 if Wrapper = null then 723 return Untailored_Exception_Traceback (X); 724 else 725 return Wrapper.all (X.Tracebacks'Address, X.Num_Tracebacks); 726 end if; 727 end Tailored_Exception_Traceback; 728 729 ---------------------------------------------- 730 -- Untailored_Exception_Traceback_Maxlength -- 731 ---------------------------------------------- 732 733 function Untailored_Exception_Traceback_Maxlength 734 (X : Exception_Occurrence) return Natural 735 is 736 Space_Per_Address : constant := 2 + 16 + 1; 737 -- Space for "0x" + HHHHHHHHHHHHHHHH + " " 738 begin 739 return 740 LDAD_Header'Length + Space_Per_Address + BETB_Header'Length + 1 + 741 X.Num_Tracebacks * Space_Per_Address + 1; 742 end Untailored_Exception_Traceback_Maxlength; 743 744end Exception_Data; 745