1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
10 -- Copyright (C) 1997-2002 Ada Core Technologies, Inc. --
12 -- GNAT is free software; you can redistribute it and/or modify it under --
13 -- terms of the GNU General Public License as published by the Free Soft- --
14 -- ware Foundation; either version 2, or (at your option) any later ver- --
15 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
16 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
17 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
18 -- for more details. You should have received a copy of the GNU General --
19 -- Public License distributed with GNAT; see file COPYING. If not, write --
20 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
21 -- MA 02111-1307, USA. --
23 -- GNAT was originally developed by the GNAT team at New York University. --
24 -- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). --
26 ------------------------------------------------------------------------------
29 with GNAT
.HTable
; use GNAT
.HTable
;
30 with Ada
.Text_IO
; use Ada
.Text_IO
;
32 package body Memroot
is
38 package Chars
is new GNAT
.Table
(
39 Table_Component_Type
=> Character,
40 Table_Index_Type
=> Integer,
42 Table_Initial
=> 10_000
,
43 Table_Increment
=> 100);
44 -- The actual character container for names
47 First
, Last
: Integer;
50 package Names
is new GNAT
.Table
(
51 Table_Component_Type
=> Name
,
52 Table_Index_Type
=> Name_Id
,
55 Table_Increment
=> 100);
57 type Name_Range
is range 1 .. 1023;
59 function Name_Eq
(N1
, N2
: Name
) return Boolean;
62 function H
(N
: Name
) return Name_Range
;
64 package Name_HTable
is new GNAT
.HTable
.Simple_HTable
(
65 Header_Num
=> Name_Range
,
67 No_Element
=> No_Name_Id
,
77 Name
, File
, Line
: Name_Id
;
85 -- Returns an image for F containing the file name, the Line number,
86 -- and the subprogram name. When possible, spaces are inserted between
87 -- the line number and the subprogram name in order to align images of the
88 -- same frame. Alignement is cimputed with Max_Fil & Max_Lin representing
89 -- the max number of character in a filename or length in a given frame.
91 package Frames
is new GNAT
.Table
(
92 Table_Component_Type
=> Frame
,
93 Table_Index_Type
=> Frame_Id
,
96 Table_Increment
=> 100);
98 type Frame_Range
is range 1 .. 513;
99 function H
(N
: Frame
) return Frame_Range
;
101 package Frame_HTable
is new GNAT
.HTable
.Simple_HTable
(
102 Header_Num
=> Frame_Range
,
104 No_Element
=> No_Frame_Id
,
114 First
, Last
: Integer;
116 Alloc_Size
: Storage_Count
;
117 High_Water_Mark
: Storage_Count
;
120 package Frames_In_Root
is new GNAT
.Table
(
121 Table_Component_Type
=> Frame_Id
,
122 Table_Index_Type
=> Integer,
123 Table_Low_Bound
=> 1,
124 Table_Initial
=> 400,
125 Table_Increment
=> 100);
127 package Roots
is new GNAT
.Table
(
128 Table_Component_Type
=> Root
,
129 Table_Index_Type
=> Root_Id
,
130 Table_Low_Bound
=> 1,
131 Table_Initial
=> 200,
132 Table_Increment
=> 100);
133 type Root_Range
is range 1 .. 513;
135 function Root_Eq
(N1
, N2
: Root
) return Boolean;
136 function H
(B
: Root
) return Root_Range
;
138 package Root_HTable
is new GNAT
.HTable
.Simple_HTable
(
139 Header_Num
=> Root_Range
,
141 No_Element
=> No_Root_Id
,
150 function Alloc_Size
(B
: Root_Id
) return Storage_Count
is
152 return Roots
.Table
(B
).Alloc_Size
;
159 function Enter_Frame
(Name
, File
, Line
: Name_Id
) return Frame_Id
is
163 Frames
.Increment_Last
;
164 Frames
.Table
(Frames
.Last
) := Frame
'(Name, File, Line);
165 Res := Frame_HTable.Get (Frames.Table (Frames.Last));
167 if Res /= No_Frame_Id then
168 Frames.Decrement_Last;
172 Frame_HTable.Set (Frames.Table (Frames.Last), Frames.Last);
181 function Enter_Name (S : String) return Name_Id is
182 Old_L : constant Integer := Chars.Last;
183 Len : constant Integer := S'Length;
184 F : constant Integer := Chars.Allocate (Len);
188 Chars.Table (F .. F + Len - 1) := Chars.Table_Type (S);
189 Names.Increment_Last;
190 Names.Table (Names.Last) := Name'(F
, F
+ Len
- 1);
191 Res
:= Name_HTable
.Get
(Names
.Table
(Names
.Last
));
193 if Res
/= No_Name_Id
then
194 Names
.Decrement_Last
;
195 Chars
.Set_Last
(Old_L
);
199 Name_HTable
.Set
(Names
.Table
(Names
.Last
), Names
.Last
);
208 function Enter_Root
(Fr
: Frame_Array
) return Root_Id
is
209 Old_L
: constant Integer := Frames_In_Root
.Last
;
210 Len
: constant Integer := Fr
'Length;
211 F
: constant Integer := Frames_In_Root
.Allocate
(Len
);
215 Frames_In_Root
.Table
(F
.. F
+ Len
- 1) :=
216 Frames_In_Root
.Table_Type
(Fr
);
217 Roots
.Increment_Last
;
218 Roots
.Table
(Roots
.Last
) := Root
'(F, F + Len - 1, 0, 0, 0);
219 Res := Root_HTable.Get (Roots.Table (Roots.Last));
221 if Res /= No_Root_Id then
222 Frames_In_Root.Set_Last (Old_L);
223 Roots.Decrement_Last;
227 Root_HTable.Set (Roots.Table (Roots.Last), Roots.Last);
236 function Frames_Of (B : Root_Id) return Frame_Array is
239 Frames_In_Root.Table (Roots.Table (B).First .. Roots.Table (B).Last));
246 function Get_First return Root_Id is
248 return Root_HTable.Get_First;
255 function Get_Next return Root_Id is
257 return Root_HTable.Get_Next;
264 function H (B : Root) return Root_Range is
266 type Uns is mod 2 ** 32;
268 function Rotate_Left (Value : Uns; Amount : Natural) return Uns;
269 pragma Import (Intrinsic, Rotate_Left);
274 for J in B.First .. B.Last loop
275 Tmp := Rotate_Left (Tmp, 1) + Uns (Frames_In_Root.Table (J));
278 return Root_Range'First
279 + Root_Range'Base (Tmp mod Root_Range'Range_Length);
282 function H (N : Name) return Name_Range is
283 function H is new Hash (Name_Range);
286 return H (String (Chars.Table (N.First .. N.Last)));
289 function H (N : Frame) return Frame_Range is
291 return Frame_Range (1 + (7 * N.Name + 13 * N.File + 17 * N.Line)
292 mod Frame_Range'Range_Length);
295 ---------------------
296 -- High_Water_Mark --
297 ---------------------
299 function High_Water_Mark (B : Root_Id) return Storage_Count is
301 return Roots.Table (B).High_Water_Mark;
308 function Image (N : Name_Id) return String is
309 Nam : Name renames Names.Table (N);
312 return String (Chars.Table (Nam.First .. Nam.Last));
321 Fram : Frame renames Frames.Table (F);
322 Fil : Name renames Names.Table (Fram.File);
323 Lin : Name renames Names.Table (Fram.Line);
324 Nam : Name renames Names.Table (Fram.Name);
326 Fil_Len : constant Integer := Fil.Last - Fil.First + 1;
327 Lin_Len : constant Integer := Lin.Last - Lin.First + 1;
329 use type Chars.Table_Type;
331 Spaces : constant String (1 .. 80) := (1 .. 80 => ' ');
334 return String (Chars.Table (Fil.First .. Fil.Last))
336 & String (Chars.Table (Lin.First .. Lin.Last))
337 & Spaces (1 .. 1 + Max_Fil - Fil_Len + Max_Lin - Lin_Len)
338 & String (Chars.Table (Nam.First .. Nam.Last));
345 function Name_Eq (N1, N2 : Name) return Boolean is
346 use type Chars.Table_Type;
349 Chars.Table (N1.First .. N1.Last) = Chars.Table (N2.First .. N2.Last);
356 function Nb_Alloc (B : Root_Id) return Integer is
358 return Roots.Table (B).Nb_Alloc;
365 procedure Print_BT (B : Root_Id) is
366 Max_Col_Width : constant := 35;
367 -- Largest filename length for which backtraces will be
368 -- properly aligned. Frames containing longer names won't be
369 -- truncated but they won't be properly aligned either.
371 F : constant Frame_Array := Frames_Of (B);
380 for J in F'Range loop
382 Fram : Frame renames Frames.Table (F (J));
383 Fil : Name renames Names.Table (Fram.File);
384 Lin : Name renames Names.Table (Fram.Line);
387 Max_Fil := Integer'Max (Max_Fil, Fil.Last - Fil.First + 1);
388 Max_Lin := Integer'Max (Max_Lin, Lin.Last - Lin.First + 1);
392 Max_Fil := Integer'Min (Max_Fil, Max_Col_Width);
394 for J in F'Range loop
396 Put_Line (Image (F (J), Max_Fil, Max_Lin));
404 function Read_BT (BT_Depth : Integer; FT : File_Type) return Root_Id is
405 Max_Line : constant Integer := 500;
408 Line : String (1 .. Max_Line);
410 Frames : Frame_Array (1 .. BT_Depth);
411 F : Integer := Frames'First;
416 No_File : Boolean := False;
417 Main_Found : Boolean := False;
420 -- Position Curs1 and Curs2 so that Line (Curs1 .. Curs2) contains
421 -- the file name. The file name may not be on the current line since
422 -- a frame may be printed on more than one line when there is a lot
423 -- of parameters or names are long, so this subprogram can read new
427 -- Position Curs1 and Curs2 so that Line (Curs1 .. Curs2) contains
431 -- Position Curs1 and Curs2 so that Line (Curs1 .. Curs2) contains
432 -- the subprogram name.
434 procedure Gmem_Read_BT_Frame (Buf : out String; Last : out Natural);
435 -- GMEM functionality binding
441 procedure Find_File is
442 Match_Parent : Integer;
449 while Curs1 <= Last loop
450 if Line (Curs1) = '(' then
451 Match_Parent := Match_Parent + 1;
452 elsif Line (Curs1) = ')' then
453 Match_Parent := Match_Parent - 1;
454 exit when Match_Parent = 0;
464 if Curs1 >= Last then
466 -- Maybe the file reference is on one of the next lines
469 Get_Line (FT, Line, Last);
471 -- If we have another Frame or if the backtrace is finished
472 -- the file reference was just missing
474 if Last <= 1 or else Line (1) = '#
' then
481 while Curs1 <= Last - 2 loop
482 if Line (Curs1) = '(' then
483 Match_Parent := Match_Parent + 1;
484 elsif Line (Curs1) = ')' then
485 Match_Parent := Match_Parent - 1;
489 and then Line (Curs1 .. Curs1 + 1) = "at"
501 -- Let's assume that the filename length is greater than 1
502 -- it simplifies dealing with the potential drive ':' on
506 while Line (Curs2 + 1) /= ':' loop Curs2 := Curs2 + 1; end loop;
513 procedure Find_Line is
517 if Curs2 - Curs1 > 5 then
518 raise Constraint_Error;
526 procedure Find_Name is
532 while Line (Curs1) /= ' ' loop Curs1 := Curs1 + 1; end loop;
536 while Line (Curs1) = ' ' loop Curs1 := Curs1 + 1; end loop;
539 while Line (Curs2 + 1) /= ' ' loop Curs2 := Curs2 + 1; end loop;
542 ------------------------
543 -- Gmem_Read_BT_Frame --
544 ------------------------
546 procedure Gmem_Read_BT_Frame (Buf : out String; Last : out Natural) is
547 procedure Read_BT_Frame (buf : System.Address);
548 pragma Import (C, Read_BT_Frame, "__gnat_gmem_read_bt_frame");
550 function Strlen (chars : System.Address) return Natural;
551 pragma Import (C, Strlen, "strlen");
553 S : String (1 .. 1000);
555 Read_BT_Frame (S'Address);
556 Last := Strlen (S'Address);
557 Buf (1 .. Last) := S (1 .. Last);
558 end Gmem_Read_BT_Frame;
560 -- Start of processing for Read_BT
565 Gmem_Read_BT_Frame (Line, Last);
568 while Line (1) /= '#
' loop
569 Get_Line (FT, Line, Last);
573 while Last >= 1 and then Line (1) = '#
' and then not Main_Found loop
574 if F <= BT_Depth then
576 -- Skip the __gnat_malloc frame itself
577 if Line (Curs1 .. Curs2) /= "<__gnat_malloc>" then
578 Nam := Enter_Name (Line (Curs1 .. Curs2));
579 Main_Found := Line (Curs1 .. Curs2) = "main";
587 Fil := Enter_Name (Line (Curs1 .. Curs2));
590 Lin := Enter_Name (Line (Curs1 .. Curs2));
593 Frames (F) := Enter_Frame (Nam, Fil, Lin);
600 -- If no file reference was found, the next line has already
601 -- been read because, it may sometimes be found on the next
608 Gmem_Read_BT_Frame (Line, Last);
610 Get_Line (FT, Line, Last);
611 exit when End_Of_File (FT);
617 return Enter_Root (Frames (1 .. F - 1));
624 function Root_Eq (N1, N2 : Root) return Boolean is
625 use type Frames_In_Root.Table_Type;
629 Frames_In_Root.Table (N1.First .. N1.Last)
630 = Frames_In_Root.Table (N2.First .. N2.Last);
637 procedure Set_Alloc_Size (B : Root_Id; V : Storage_Count) is
639 Roots.Table (B).Alloc_Size := V;
642 -------------------------
643 -- Set_High_Water_Mark --
644 -------------------------
646 procedure Set_High_Water_Mark (B : Root_Id; V : Storage_Count) is
648 Roots.Table (B).High_Water_Mark := V;
649 end Set_High_Water_Mark;
655 procedure Set_Nb_Alloc (B : Root_Id; V : Integer) is
657 Roots.Table (B).Nb_Alloc := V;
661 -- Initialize name for No_Name_ID
663 Names.Increment_Last;
664 Names.Table (Names.Last) := Name'(1, 0);