1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
11 -- Copyright (C) 1997-2001 Ada Core Technologies, Inc. --
13 -- GNAT is free software; you can redistribute it and/or modify it under --
14 -- terms of the GNU General Public License as published by the Free Soft- --
15 -- ware Foundation; either version 2, or (at your option) any later ver- --
16 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
17 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
18 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
19 -- for more details. You should have received a copy of the GNU General --
20 -- Public License distributed with GNAT; see file COPYING. If not, write --
21 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
22 -- MA 02111-1307, USA. --
24 -- GNAT was originally developed by the GNAT team at New York University. --
25 -- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). --
27 ------------------------------------------------------------------------------
30 with GNAT.HTable; use GNAT.HTable;
31 with Ada.Text_IO; use Ada.Text_IO;
33 package body Memroot is
39 package Chars is new GNAT.Table (
40 Table_Component_Type => Character,
41 Table_Index_Type => Integer,
43 Table_Initial => 10_000,
44 Table_Increment => 100);
45 -- The actual character container for names
48 First, Last : Integer;
51 package Names is new GNAT.Table (
52 Table_Component_Type => Name,
53 Table_Index_Type => Name_Id,
56 Table_Increment => 100);
58 type Name_Range is range 1 .. 1023;
60 function Name_Eq (N1, N2 : Name) return Boolean;
63 function H (N : Name) return Name_Range;
65 package Name_HTable is new GNAT.HTable.Simple_HTable (
66 Header_Num => Name_Range,
68 No_Element => No_Name_Id,
78 Name, File, Line : Name_Id;
86 -- Returns an image for F containing the file name, the Line number,
87 -- and the subprogram name. When possible, spaces are inserted between
88 -- the line number and the subprogram name in order to align images of the
89 -- same frame. Alignement is cimputed with Max_Fil & Max_Lin representing
90 -- the max number of character in a filename or length in a given frame.
92 package Frames is new GNAT.Table (
93 Table_Component_Type => Frame,
94 Table_Index_Type => Frame_Id,
97 Table_Increment => 100);
99 type Frame_Range is range 1 .. 513;
100 function H (N : Frame) return Frame_Range;
102 package Frame_HTable is new GNAT.HTable.Simple_HTable (
103 Header_Num => Frame_Range,
105 No_Element => No_Frame_Id,
115 First, Last : Integer;
117 Alloc_Size : Storage_Count;
118 High_Water_Mark : Storage_Count;
121 package Frames_In_Root is new GNAT.Table (
122 Table_Component_Type => Frame_Id,
123 Table_Index_Type => Integer,
124 Table_Low_Bound => 1,
125 Table_Initial => 400,
126 Table_Increment => 100);
128 package Roots is new GNAT.Table (
129 Table_Component_Type => Root,
130 Table_Index_Type => Root_Id,
131 Table_Low_Bound => 1,
132 Table_Initial => 200,
133 Table_Increment => 100);
134 type Root_Range is range 1 .. 513;
136 function Root_Eq (N1, N2 : Root) return Boolean;
137 function H (B : Root) return Root_Range;
139 package Root_HTable is new GNAT.HTable.Simple_HTable (
140 Header_Num => Root_Range,
142 No_Element => No_Root_Id,
151 function Alloc_Size (B : Root_Id) return Storage_Count is
153 return Roots.Table (B).Alloc_Size;
160 function Enter_Frame (Name, File, Line : Name_Id) return Frame_Id is
164 Frames.Increment_Last;
165 Frames.Table (Frames.Last) := Frame'(Name, File, Line);
166 Res := Frame_HTable.Get (Frames.Table (Frames.Last));
168 if Res /= No_Frame_Id then
169 Frames.Decrement_Last;
173 Frame_HTable.Set (Frames.Table (Frames.Last), Frames.Last);
182 function Enter_Name (S : String) return Name_Id is
183 Old_L : constant Integer := Chars.Last;
184 Len : constant Integer := S'Length;
185 F : constant Integer := Chars.Allocate (Len);
189 Chars.Table (F .. F + Len - 1) := Chars.Table_Type (S);
190 Names.Increment_Last;
191 Names.Table (Names.Last) := Name'(F, F + Len - 1);
192 Res := Name_HTable.Get (Names.Table (Names.Last));
194 if Res /= No_Name_Id then
195 Names.Decrement_Last;
196 Chars.Set_Last (Old_L);
200 Name_HTable.Set (Names.Table (Names.Last), Names.Last);
209 function Enter_Root (Fr : Frame_Array) return Root_Id is
210 Old_L : constant Integer := Frames_In_Root.Last;
211 Len : constant Integer := Fr'Length;
212 F : constant Integer := Frames_In_Root.Allocate (Len);
216 Frames_In_Root.Table (F .. F + Len - 1) :=
217 Frames_In_Root.Table_Type (Fr);
218 Roots.Increment_Last;
219 Roots.Table (Roots.Last) := Root'(F, F + Len - 1, 0, 0, 0);
220 Res := Root_HTable.Get (Roots.Table (Roots.Last));
222 if Res /= No_Root_Id then
223 Frames_In_Root.Set_Last (Old_L);
224 Roots.Decrement_Last;
228 Root_HTable.Set (Roots.Table (Roots.Last), Roots.Last);
237 function Frames_Of (B : Root_Id) return Frame_Array is
240 Frames_In_Root.Table (Roots.Table (B).First .. Roots.Table (B).Last));
247 function Get_First return Root_Id is
249 return Root_HTable.Get_First;
256 function Get_Next return Root_Id is
258 return Root_HTable.Get_Next;
265 function H (B : Root) return Root_Range is
267 type Uns is mod 2 ** 32;
269 function Rotate_Left (Value : Uns; Amount : Natural) return Uns;
270 pragma Import (Intrinsic, Rotate_Left);
275 for J in B.First .. B.Last loop
276 Tmp := Rotate_Left (Tmp, 1) + Uns (Frames_In_Root.Table (J));
279 return Root_Range'First
280 + Root_Range'Base (Tmp mod Root_Range'Range_Length);
283 function H (N : Name) return Name_Range is
284 function H is new Hash (Name_Range);
287 return H (String (Chars.Table (N.First .. N.Last)));
290 function H (N : Frame) return Frame_Range is
292 return Frame_Range (1 + (7 * N.Name + 13 * N.File + 17 * N.Line)
293 mod Frame_Range'Range_Length);
296 ---------------------
297 -- High_Water_Mark --
298 ---------------------
300 function High_Water_Mark (B : Root_Id) return Storage_Count is
302 return Roots.Table (B).High_Water_Mark;
309 function Image (N : Name_Id) return String is
310 Nam : Name renames Names.Table (N);
313 return String (Chars.Table (Nam.First .. Nam.Last));
322 Fram : Frame renames Frames.Table (F);
323 Fil : Name renames Names.Table (Fram.File);
324 Lin : Name renames Names.Table (Fram.Line);
325 Nam : Name renames Names.Table (Fram.Name);
327 Fil_Len : constant Integer := Fil.Last - Fil.First + 1;
328 Lin_Len : constant Integer := Lin.Last - Lin.First + 1;
330 use type Chars.Table_Type;
332 Spaces : constant String (1 .. 80) := (1 .. 80 => ' ');
335 return String (Chars.Table (Fil.First .. Fil.Last))
337 & String (Chars.Table (Lin.First .. Lin.Last))
338 & Spaces (1 .. 1 + Max_Fil - Fil_Len + Max_Lin - Lin_Len)
339 & String (Chars.Table (Nam.First .. Nam.Last));
346 function Name_Eq (N1, N2 : Name) return Boolean is
347 use type Chars.Table_Type;
350 Chars.Table (N1.First .. N1.Last) = Chars.Table (N2.First .. N2.Last);
357 function Nb_Alloc (B : Root_Id) return Integer is
359 return Roots.Table (B).Nb_Alloc;
366 procedure Print_BT (B : Root_Id) is
367 Max_Col_Width : constant := 35;
368 -- Largest filename length for which backtraces will be
369 -- properly aligned. Frames containing longer names won't be
370 -- truncated but they won't be properly aligned either.
372 F : constant Frame_Array := Frames_Of (B);
381 for J in F'Range loop
383 Fram : Frame renames Frames.Table (F (J));
384 Fil : Name renames Names.Table (Fram.File);
385 Lin : Name renames Names.Table (Fram.Line);
388 Max_Fil := Integer'Max (Max_Fil, Fil.Last - Fil.First + 1);
389 Max_Lin := Integer'Max (Max_Lin, Lin.Last - Lin.First + 1);
393 Max_Fil := Integer'Min (Max_Fil, Max_Col_Width);
395 for J in F'Range loop
397 Put_Line (Image (F (J), Max_Fil, Max_Lin));
405 function Read_BT (BT_Depth : Integer; FT : File_Type) return Root_Id is
406 Max_Line : constant Integer := 500;
409 Line : String (1 .. Max_Line);
411 Frames : Frame_Array (1 .. BT_Depth);
412 F : Integer := Frames'First;
417 No_File : Boolean := False;
418 Main_Found : Boolean := False;
421 -- Position Curs1 and Curs2 so that Line (Curs1 .. Curs2) contains
422 -- the file name. The file name may not be on the current line since
423 -- a frame may be printed on more than one line when there is a lot
424 -- of parameters or names are long, so this subprogram can read new
428 -- Position Curs1 and Curs2 so that Line (Curs1 .. Curs2) contains
432 -- Position Curs1 and Curs2 so that Line (Curs1 .. Curs2) contains
433 -- the subprogram name.
435 procedure Gmem_Read_BT_Frame (Buf : out String; Last : out Natural);
436 -- GMEM functionality binding
442 procedure Find_File is
443 Match_Parent : Integer;
450 while Curs1 <= Last loop
451 if Line (Curs1) = '(' then
452 Match_Parent := Match_Parent + 1;
453 elsif Line (Curs1) = ')' then
454 Match_Parent := Match_Parent - 1;
455 exit when Match_Parent = 0;
465 if Curs1 >= Last then
467 -- Maybe the file reference is on one of the next lines
470 Get_Line (FT, Line, Last);
472 -- If we have another Frame or if the backtrace is finished
473 -- the file reference was just missing
475 if Last <= 1 or else Line (1) = '#' then
482 while Curs1 <= Last - 2 loop
483 if Line (Curs1) = '(' then
484 Match_Parent := Match_Parent + 1;
485 elsif Line (Curs1) = ')' then
486 Match_Parent := Match_Parent - 1;
490 and then Line (Curs1 .. Curs1 + 1) = "at"
502 -- Let's assume that the filename length is greater than 1
503 -- it simplifies dealing with the potential drive ':' on
507 while Line (Curs2 + 1) /= ':' loop Curs2 := Curs2 + 1; end loop;
514 procedure Find_Line is
518 if Curs2 - Curs1 > 5 then
519 raise Constraint_Error;
527 procedure Find_Name is
533 while Line (Curs1) /= ' ' loop Curs1 := Curs1 + 1; end loop;
537 while Line (Curs1) = ' ' loop Curs1 := Curs1 + 1; end loop;
540 while Line (Curs2 + 1) /= ' ' loop Curs2 := Curs2 + 1; end loop;
543 ------------------------
544 -- Gmem_Read_BT_Frame --
545 ------------------------
547 procedure Gmem_Read_BT_Frame (Buf : out String; Last : out Natural) is
548 procedure Read_BT_Frame (buf : System.Address);
549 pragma Import (C, Read_BT_Frame, "__gnat_gmem_read_bt_frame");
551 function Strlen (chars : System.Address) return Natural;
552 pragma Import (C, Strlen, "strlen");
554 S : String (1 .. 1000);
556 Read_BT_Frame (S'Address);
557 Last := Strlen (S'Address);
558 Buf (1 .. Last) := S (1 .. Last);
559 end Gmem_Read_BT_Frame;
561 -- Start of processing for Read_BT
566 Gmem_Read_BT_Frame (Line, Last);
569 while Line (1) /= '#' loop
570 Get_Line (FT, Line, Last);
574 while Last >= 1 and then Line (1) = '#' and then not Main_Found loop
575 if F <= BT_Depth then
577 Nam := Enter_Name (Line (Curs1 .. Curs2));
578 Main_Found := Line (Curs1 .. Curs2) = "main";
586 Fil := Enter_Name (Line (Curs1 .. Curs2));
589 Lin := Enter_Name (Line (Curs1 .. Curs2));
592 Frames (F) := Enter_Frame (Nam, Fil, Lin);
598 -- If no file reference was found, the next line has already
599 -- been read because, it may sometimes be found on the next
606 Gmem_Read_BT_Frame (Line, Last);
608 Get_Line (FT, Line, Last);
609 exit when End_Of_File (FT);
615 return Enter_Root (Frames (1 .. F - 1));
622 function Root_Eq (N1, N2 : Root) return Boolean is
623 use type Frames_In_Root.Table_Type;
627 Frames_In_Root.Table (N1.First .. N1.Last)
628 = Frames_In_Root.Table (N2.First .. N2.Last);
635 procedure Set_Alloc_Size (B : Root_Id; V : Storage_Count) is
637 Roots.Table (B).Alloc_Size := V;
640 -------------------------
641 -- Set_High_Water_Mark --
642 -------------------------
644 procedure Set_High_Water_Mark (B : Root_Id; V : Storage_Count) is
646 Roots.Table (B).High_Water_Mark := V;
647 end Set_High_Water_Mark;
653 procedure Set_Nb_Alloc (B : Root_Id; V : Integer) is
655 Roots.Table (B).Nb_Alloc := V;
659 -- Initialize name for No_Name_ID
661 Names.Increment_Last;
662 Names.Table (Names.Last) := Name'(1, 0);