blob: cdd4feb00099f3184e5c9089578811a64355de25 [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- M E M R O O T --
-- --
-- B o d y --
-- --
-- Copyright (C) 1997-2005, AdaCore --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
-- ware Foundation; either version 2, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
-- for more details. You should have received a copy of the GNU General --
-- Public License distributed with GNAT; see file COPYING. If not, write --
-- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
-- Boston, MA 02110-1301, USA. --
-- --
-- GNAT was originally developed by the GNAT team at New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc. --
-- --
------------------------------------------------------------------------------
with GNAT.Table;
with GNAT.HTable; use GNAT.HTable;
with Ada.Text_IO; use Ada.Text_IO;
package body Memroot is
Main_Name_Id : Name_Id;
-- The constant "main" where we should stop the backtraces
-------------
-- Name_Id --
-------------
package Chars is new GNAT.Table (
Table_Component_Type => Character,
Table_Index_Type => Integer,
Table_Low_Bound => 1,
Table_Initial => 10_000,
Table_Increment => 100);
-- The actual character container for names
type Name is record
First, Last : Integer;
end record;
package Names is new GNAT.Table (
Table_Component_Type => Name,
Table_Index_Type => Name_Id,
Table_Low_Bound => 0,
Table_Initial => 400,
Table_Increment => 100);
type Name_Range is range 1 .. 1023;
function Name_Eq (N1, N2 : Name) return Boolean;
-- compare 2 names
function H (N : Name) return Name_Range;
package Name_HTable is new GNAT.HTable.Simple_HTable (
Header_Num => Name_Range,
Element => Name_Id,
No_Element => No_Name_Id,
Key => Name,
Hash => H,
Equal => Name_Eq);
--------------
-- Frame_Id --
--------------
type Frame is record
Name, File, Line : Name_Id;
end record;
function Image
(F : Frame_Id;
Max_Fil : Integer;
Max_Lin : Integer;
Short : Boolean := False) return String;
-- Returns an image for F containing the file name, the Line number,
-- and if 'Short' is not true, the subprogram name. When possible, spaces
-- are inserted between the line number and the subprogram name in order
-- to align images of the same frame. Alignement is cimputed with Max_Fil
-- & Max_Lin representing the max number of character in a filename or
-- length in a given frame.
package Frames is new GNAT.Table (
Table_Component_Type => Frame,
Table_Index_Type => Frame_Id,
Table_Low_Bound => 1,
Table_Initial => 400,
Table_Increment => 100);
type Frame_Range is range 1 .. 10000;
function H (N : Integer_Address) return Frame_Range;
package Frame_HTable is new GNAT.HTable.Simple_HTable (
Header_Num => Frame_Range,
Element => Frame_Id,
No_Element => No_Frame_Id,
Key => Integer_Address,
Hash => H,
Equal => "=");
-------------
-- Root_Id --
-------------
type Root is record
First, Last : Integer;
Nb_Alloc : Integer;
Alloc_Size : Storage_Count;
High_Water_Mark : Storage_Count;
end record;
package Frames_In_Root is new GNAT.Table (
Table_Component_Type => Frame_Id,
Table_Index_Type => Integer,
Table_Low_Bound => 1,
Table_Initial => 400,
Table_Increment => 100);
package Roots is new GNAT.Table (
Table_Component_Type => Root,
Table_Index_Type => Root_Id,
Table_Low_Bound => 1,
Table_Initial => 200,
Table_Increment => 100);
type Root_Range is range 1 .. 513;
function Root_Eq (N1, N2 : Root) return Boolean;
function H (B : Root) return Root_Range;
package Root_HTable is new GNAT.HTable.Simple_HTable (
Header_Num => Root_Range,
Element => Root_Id,
No_Element => No_Root_Id,
Key => Root,
Hash => H,
Equal => Root_Eq);
----------------
-- Alloc_Size --
----------------
function Alloc_Size (B : Root_Id) return Storage_Count is
begin
return Roots.Table (B).Alloc_Size;
end Alloc_Size;
-----------------
-- Enter_Frame --
-----------------
function Enter_Frame
(Addr : System.Address;
Name : Name_Id;
File : Name_Id;
Line : Name_Id)
return Frame_Id
is
begin
Frames.Increment_Last;
Frames.Table (Frames.Last) := Frame'(Name, File, Line);
Frame_HTable.Set (To_Integer (Addr), Frames.Last);
return Frames.Last;
end Enter_Frame;
----------------
-- Enter_Name --
----------------
function Enter_Name (S : String) return Name_Id is
Old_L : constant Integer := Chars.Last;
Len : constant Integer := S'Length;
F : constant Integer := Chars.Allocate (Len);
Res : Name_Id;
begin
Chars.Table (F .. F + Len - 1) := Chars.Table_Type (S);
Names.Increment_Last;
Names.Table (Names.Last) := Name'(F, F + Len - 1);
Res := Name_HTable.Get (Names.Table (Names.Last));
if Res /= No_Name_Id then
Names.Decrement_Last;
Chars.Set_Last (Old_L);
return Res;
else
Name_HTable.Set (Names.Table (Names.Last), Names.Last);
return Names.Last;
end if;
end Enter_Name;
----------------
-- Enter_Root --
----------------
function Enter_Root (Fr : Frame_Array) return Root_Id is
Old_L : constant Integer := Frames_In_Root.Last;
Len : constant Integer := Fr'Length;
F : constant Integer := Frames_In_Root.Allocate (Len);
Res : Root_Id;
begin
Frames_In_Root.Table (F .. F + Len - 1) :=
Frames_In_Root.Table_Type (Fr);
Roots.Increment_Last;
Roots.Table (Roots.Last) := Root'(F, F + Len - 1, 0, 0, 0);
Res := Root_HTable.Get (Roots.Table (Roots.Last));
if Res /= No_Root_Id then
Frames_In_Root.Set_Last (Old_L);
Roots.Decrement_Last;
return Res;
else
Root_HTable.Set (Roots.Table (Roots.Last), Roots.Last);
return Roots.Last;
end if;
end Enter_Root;
---------------
-- Frames_Of --
---------------
function Frames_Of (B : Root_Id) return Frame_Array is
begin
return Frame_Array (
Frames_In_Root.Table (Roots.Table (B).First .. Roots.Table (B).Last));
end Frames_Of;
---------------
-- Get_First --
---------------
function Get_First return Root_Id is
begin
return Root_HTable.Get_First;
end Get_First;
--------------
-- Get_Next --
--------------
function Get_Next return Root_Id is
begin
return Root_HTable.Get_Next;
end Get_Next;
-------
-- H --
-------
function H (B : Root) return Root_Range is
type Uns is mod 2 ** 32;
function Rotate_Left (Value : Uns; Amount : Natural) return Uns;
pragma Import (Intrinsic, Rotate_Left);
Tmp : Uns := 0;
begin
for J in B.First .. B.Last loop
Tmp := Rotate_Left (Tmp, 1) + Uns (Frames_In_Root.Table (J));
end loop;
return Root_Range'First
+ Root_Range'Base (Tmp mod Root_Range'Range_Length);
end H;
function H (N : Name) return Name_Range is
function H is new Hash (Name_Range);
begin
return H (String (Chars.Table (N.First .. N.Last)));
end H;
function H (N : Integer_Address) return Frame_Range is
begin
return Frame_Range (1 + N mod Frame_Range'Range_Length);
end H;
---------------------
-- High_Water_Mark --
---------------------
function High_Water_Mark (B : Root_Id) return Storage_Count is
begin
return Roots.Table (B).High_Water_Mark;
end High_Water_Mark;
-----------
-- Image --
-----------
function Image (N : Name_Id) return String is
Nam : Name renames Names.Table (N);
begin
return String (Chars.Table (Nam.First .. Nam.Last));
end Image;
function Image
(F : Frame_Id;
Max_Fil : Integer;
Max_Lin : Integer;
Short : Boolean := False) return String
is
Fram : Frame renames Frames.Table (F);
Fil : Name renames Names.Table (Fram.File);
Lin : Name renames Names.Table (Fram.Line);
Nam : Name renames Names.Table (Fram.Name);
Fil_Len : constant Integer := Fil.Last - Fil.First + 1;
Lin_Len : constant Integer := Lin.Last - Lin.First + 1;
use type Chars.Table_Type;
Spaces : constant String (1 .. 80) := (1 .. 80 => ' ');
Result : constant String :=
String (Chars.Table (Fil.First .. Fil.Last))
& ':'
& String (Chars.Table (Lin.First .. Lin.Last));
begin
if Short then
return Result;
else
return Result
& Spaces (1 .. 1 + Max_Fil - Fil_Len + Max_Lin - Lin_Len)
& String (Chars.Table (Nam.First .. Nam.Last));
end if;
end Image;
-------------
-- Name_Eq --
-------------
function Name_Eq (N1, N2 : Name) return Boolean is
use type Chars.Table_Type;
begin
return
Chars.Table (N1.First .. N1.Last) = Chars.Table (N2.First .. N2.Last);
end Name_Eq;
--------------
-- Nb_Alloc --
--------------
function Nb_Alloc (B : Root_Id) return Integer is
begin
return Roots.Table (B).Nb_Alloc;
end Nb_Alloc;
--------------
-- Print_BT --
--------------
procedure Print_BT (B : Root_Id; Short : Boolean := False) is
Max_Col_Width : constant := 35;
-- Largest filename length for which backtraces will be
-- properly aligned. Frames containing longer names won't be
-- truncated but they won't be properly aligned either.
F : constant Frame_Array := Frames_Of (B);
Max_Fil : Integer;
Max_Lin : Integer;
begin
Max_Fil := 0;
Max_Lin := 0;
for J in F'Range loop
declare
Fram : Frame renames Frames.Table (F (J));
Fil : Name renames Names.Table (Fram.File);
Lin : Name renames Names.Table (Fram.Line);
begin
Max_Fil := Integer'Max (Max_Fil, Fil.Last - Fil.First + 1);
Max_Lin := Integer'Max (Max_Lin, Lin.Last - Lin.First + 1);
end;
end loop;
Max_Fil := Integer'Min (Max_Fil, Max_Col_Width);
for J in F'Range loop
Put (" ");
Put_Line (Image (F (J), Max_Fil, Max_Lin, Short));
end loop;
end Print_BT;
-------------
-- Read_BT --
-------------
function Read_BT (BT_Depth : Integer) return Root_Id is
Max_Line : constant Integer := 500;
Curs1 : Integer;
Curs2 : Integer;
Line : String (1 .. Max_Line);
Last : Integer := 0;
Frames : Frame_Array (1 .. BT_Depth);
F : Integer := Frames'First;
Nam : Name_Id;
Fil : Name_Id;
Lin : Name_Id;
Add : System.Address;
Int_Add : Integer_Address;
Fr : Frame_Id;
Main_Found : Boolean := False;
pragma Warnings (Off, Line);
procedure Find_File;
pragma Inline (Find_File);
-- Position Curs1 and Curs2 so that Line (Curs1 .. Curs2) contains
-- the file name. The file name may not be on the current line since
-- a frame may be printed on more than one line when there is a lot
-- of parameters or names are long, so this subprogram can read new
-- lines of input.
procedure Find_Line;
pragma Inline (Find_Line);
-- Position Curs1 and Curs2 so that Line (Curs1 .. Curs2) contains
-- the line number.
procedure Find_Name;
pragma Inline (Find_Name);
-- Position Curs1 and Curs2 so that Line (Curs1 .. Curs2) contains
-- the subprogram name.
function Skip_To_Space (Pos : Integer) return Integer;
pragma Inline (Skip_To_Space);
-- Scans Line starting with position Pos, returning the position
-- immediately before the first space, or the value of Last if no
-- spaces were found
---------------
-- Find_File --
---------------
procedure Find_File is
begin
-- Skip " at "
Curs1 := Curs2 + 5;
Curs2 := Last;
-- Scan backwards from end of line until ':' is encountered
for J in reverse Curs1 .. Last loop
if Line (J) = ':' then
Curs2 := J - 1;
end if;
end loop;
end Find_File;
---------------
-- Find_Line --
---------------
procedure Find_Line is
begin
Curs1 := Curs2 + 2;
Curs2 := Last;
-- Check for Curs1 too large. Should never happen with non-corrupt
-- output. If it does happen, just reset it to the highest value.
if Curs1 > Last then
Curs1 := Last;
end if;
end Find_Line;
---------------
-- Find_Name --
---------------
procedure Find_Name is
begin
-- Skip the address value and " in "
Curs1 := Skip_To_Space (1) + 5;
Curs2 := Skip_To_Space (Curs1);
end Find_Name;
-------------------
-- Skip_To_Space --
-------------------
function Skip_To_Space (Pos : Integer) return Integer is
begin
for Cur in Pos .. Last loop
if Line (Cur) = ' ' then
return Cur - 1;
end if;
end loop;
return Last;
end Skip_To_Space;
procedure Gmem_Read_Next_Frame (Addr : out System.Address);
pragma Import (C, Gmem_Read_Next_Frame, "__gnat_gmem_read_next_frame");
-- Read the next frame in the current traceback. Addr is set to 0 if
-- there are no more addresses in this traceback. The pointer is moved
-- to the next frame.
procedure Gmem_Symbolic
(Addr : System.Address; Buf : String; Last : out Natural);
pragma Import (C, Gmem_Symbolic, "__gnat_gmem_symbolic");
-- Get the symbolic traceback for Addr. Note: we cannot use
-- GNAT.Tracebacks.Symbolic, since the latter will only work with the
-- current executable.
--
-- "__gnat_gmem_symbolic" will work with the executable whose name is
-- given in gnat_argv[0], as initialized by Gnatmem.Gmem_A21_Initialize.
-- Start of processing for Read_BT
begin
while F <= BT_Depth and then not Main_Found loop
Gmem_Read_Next_Frame (Add);
Int_Add := To_Integer (Add);
exit when Int_Add = 0;
Fr := Frame_HTable.Get (Int_Add);
if Fr = No_Frame_Id then
Gmem_Symbolic (Add, Line, Last);
Last := Last - 1; -- get rid of the trailing line-feed
Find_Name;
-- Skip the __gnat_malloc frame itself
if Line (Curs1 .. Curs2) /= "<__gnat_malloc>" then
Nam := Enter_Name (Line (Curs1 .. Curs2));
Main_Found := (Nam = Main_Name_Id);
Find_File;
Fil := Enter_Name (Line (Curs1 .. Curs2));
Find_Line;
Lin := Enter_Name (Line (Curs1 .. Curs2));
Frames (F) := Enter_Frame (Add, Nam, Fil, Lin);
F := F + 1;
end if;
else
Frames (F) := Fr;
Main_Found := (Memroot.Frames.Table (Fr).Name = Main_Name_Id);
F := F + 1;
end if;
end loop;
return Enter_Root (Frames (1 .. F - 1));
end Read_BT;
-------------
-- Root_Eq --
-------------
function Root_Eq (N1, N2 : Root) return Boolean is
use type Frames_In_Root.Table_Type;
begin
return
Frames_In_Root.Table (N1.First .. N1.Last)
= Frames_In_Root.Table (N2.First .. N2.Last);
end Root_Eq;
--------------------
-- Set_Alloc_Size --
--------------------
procedure Set_Alloc_Size (B : Root_Id; V : Storage_Count) is
begin
Roots.Table (B).Alloc_Size := V;
end Set_Alloc_Size;
-------------------------
-- Set_High_Water_Mark --
-------------------------
procedure Set_High_Water_Mark (B : Root_Id; V : Storage_Count) is
begin
Roots.Table (B).High_Water_Mark := V;
end Set_High_Water_Mark;
------------------
-- Set_Nb_Alloc --
------------------
procedure Set_Nb_Alloc (B : Root_Id; V : Integer) is
begin
Roots.Table (B).Nb_Alloc := V;
end Set_Nb_Alloc;
begin
-- Initialize name for No_Name_ID
Names.Increment_Last;
Names.Table (Names.Last) := Name'(1, 0);
Main_Name_Id := Enter_Name ("main");
end Memroot;