OSDN Git Service

optimize
[pf3gnuchains/gcc-fork.git] / gcc / ada / memroot.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT COMPILER COMPONENTS                         --
4 --                                                                          --
5 --                              M E M R O O T                               --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --            Copyright (C) 1997-2003 Ada Core Technologies, 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 2,  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.  See the GNU General Public License --
17 -- for  more details.  You should have  received  a copy of the GNU General --
18 -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
19 -- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
20 -- MA 02111-1307, USA.                                                      --
21 --                                                                          --
22 -- GNAT was originally developed  by the GNAT team at  New York University. --
23 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
24 --                                                                          --
25 ------------------------------------------------------------------------------
26
27 with GNAT.Table;
28 with GNAT.HTable; use GNAT.HTable;
29 with Ada.Text_IO; use Ada.Text_IO;
30 with System.Storage_Elements; use System.Storage_Elements;
31
32 package body Memroot is
33
34    Main_Name_Id : Name_Id;
35    --  The constant "main" where we should stop the backtraces
36
37    -------------
38    -- Name_Id --
39    -------------
40
41    package Chars is new GNAT.Table (
42      Table_Component_Type => Character,
43      Table_Index_Type     => Integer,
44      Table_Low_Bound      => 1,
45      Table_Initial        => 10_000,
46      Table_Increment      => 100);
47    --  The actual character container for names
48
49    type Name is  record
50       First, Last : Integer;
51    end record;
52
53    package Names is new GNAT.Table (
54      Table_Component_Type => Name,
55      Table_Index_Type     => Name_Id,
56      Table_Low_Bound      => 0,
57      Table_Initial        => 400,
58      Table_Increment      => 100);
59
60    type Name_Range is range 1 .. 1023;
61
62    function Name_Eq (N1, N2 : Name) return Boolean;
63    --  compare 2 names
64
65    function H (N : Name) return Name_Range;
66
67    package Name_HTable is new GNAT.HTable.Simple_HTable (
68      Header_Num => Name_Range,
69      Element    => Name_Id,
70      No_Element => No_Name_Id,
71      Key        => Name,
72      Hash       => H,
73      Equal      => Name_Eq);
74
75    --------------
76    -- Frame_Id --
77    --------------
78
79    type Frame is record
80       Name, File, Line : Name_Id;
81    end record;
82
83    function Image
84      (F       : Frame_Id;
85       Max_Fil : Integer;
86       Max_Lin : Integer;
87       Short   : Boolean := False) return String;
88    --  Returns an image for F containing the file name, the Line number,
89    --  and if 'Short' is not true, the subprogram name. When possible, spaces
90    --  are inserted between the line number and the subprogram name in order
91    --  to align images of the same frame. Alignement is cimputed with Max_Fil
92    --  & Max_Lin representing the max number of character in a filename or
93    --  length in a given frame.
94
95    package Frames is new GNAT.Table (
96      Table_Component_Type => Frame,
97      Table_Index_Type     => Frame_Id,
98      Table_Low_Bound      => 1,
99      Table_Initial        => 400,
100      Table_Increment      => 100);
101
102    type Frame_Range is range 1 .. 10000;
103    function H (N : Integer_Address) return Frame_Range;
104
105    package Frame_HTable is new GNAT.HTable.Simple_HTable (
106      Header_Num => Frame_Range,
107      Element    => Frame_Id,
108      No_Element => No_Frame_Id,
109      Key        => Integer_Address,
110      Hash       => H,
111      Equal      => "=");
112
113    -------------
114    -- Root_Id --
115    -------------
116
117    type Root is  record
118      First, Last     : Integer;
119      Nb_Alloc        : Integer;
120      Alloc_Size      : Storage_Count;
121      High_Water_Mark : Storage_Count;
122    end record;
123
124    package Frames_In_Root is new GNAT.Table (
125      Table_Component_Type => Frame_Id,
126      Table_Index_Type     => Integer,
127      Table_Low_Bound      => 1,
128      Table_Initial        => 400,
129      Table_Increment      => 100);
130
131    package Roots is new GNAT.Table (
132      Table_Component_Type => Root,
133      Table_Index_Type     => Root_Id,
134      Table_Low_Bound      => 1,
135      Table_Initial        => 200,
136      Table_Increment      => 100);
137    type Root_Range is range 1 .. 513;
138
139    function Root_Eq (N1, N2 : Root) return Boolean;
140    function H     (B : Root)     return Root_Range;
141
142    package Root_HTable is new GNAT.HTable.Simple_HTable (
143      Header_Num => Root_Range,
144      Element    => Root_Id,
145      No_Element => No_Root_Id,
146      Key        => Root,
147      Hash       => H,
148      Equal      => Root_Eq);
149
150    ----------------
151    -- Alloc_Size --
152    ----------------
153
154    function Alloc_Size (B : Root_Id) return Storage_Count is
155    begin
156       return Roots.Table (B).Alloc_Size;
157    end Alloc_Size;
158
159    -----------------
160    -- Enter_Frame --
161    -----------------
162
163    function Enter_Frame
164      (Addr : System.Address;
165       Name : Name_Id;
166       File : Name_Id;
167       Line : Name_Id)
168       return Frame_Id
169    is
170    begin
171       Frames.Increment_Last;
172       Frames.Table (Frames.Last) := Frame'(Name, File, Line);
173
174       Frame_HTable.Set (To_Integer (Addr), Frames.Last);
175       return Frames.Last;
176    end Enter_Frame;
177
178    ----------------
179    -- Enter_Name --
180    ----------------
181
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);
186       Res   : Name_Id;
187
188    begin
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));
193
194       if Res /= No_Name_Id then
195          Names.Decrement_Last;
196          Chars.Set_Last (Old_L);
197          return Res;
198
199       else
200          Name_HTable.Set (Names.Table (Names.Last), Names.Last);
201          return Names.Last;
202       end if;
203    end Enter_Name;
204
205    ----------------
206    -- Enter_Root --
207    ----------------
208
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);
213       Res   : Root_Id;
214
215    begin
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));
221
222       if Res /= No_Root_Id then
223          Frames_In_Root.Set_Last (Old_L);
224          Roots.Decrement_Last;
225          return Res;
226
227       else
228          Root_HTable.Set (Roots.Table (Roots.Last), Roots.Last);
229          return Roots.Last;
230       end if;
231    end Enter_Root;
232
233    ---------------
234    -- Frames_Of --
235    ---------------
236
237    function Frames_Of (B : Root_Id) return Frame_Array is
238    begin
239       return Frame_Array (
240         Frames_In_Root.Table (Roots.Table (B).First .. Roots.Table (B).Last));
241    end Frames_Of;
242
243    ---------------
244    -- Get_First --
245    ---------------
246
247    function Get_First return Root_Id is
248    begin
249       return  Root_HTable.Get_First;
250    end Get_First;
251
252    --------------
253    -- Get_Next --
254    --------------
255
256    function Get_Next return Root_Id is
257    begin
258       return Root_HTable.Get_Next;
259    end Get_Next;
260
261    -------
262    -- H --
263    -------
264
265    function H (B : Root) return Root_Range is
266
267       type Uns is mod 2 ** 32;
268
269       function Rotate_Left (Value : Uns; Amount : Natural) return Uns;
270       pragma Import (Intrinsic, Rotate_Left);
271
272       Tmp : Uns := 0;
273
274    begin
275       for J in B.First .. B.Last loop
276          Tmp := Rotate_Left (Tmp, 1) + Uns (Frames_In_Root.Table (J));
277       end loop;
278
279       return Root_Range'First
280         + Root_Range'Base (Tmp mod Root_Range'Range_Length);
281    end H;
282
283    function H (N : Name) return Name_Range is
284       function H is new Hash (Name_Range);
285
286    begin
287       return H (String (Chars.Table (N.First .. N.Last)));
288    end H;
289
290    function H (N : Integer_Address) return Frame_Range is
291    begin
292       return Frame_Range (1 + N mod Frame_Range'Range_Length);
293    end H;
294
295    ---------------------
296    -- High_Water_Mark --
297    ---------------------
298
299    function High_Water_Mark (B : Root_Id) return Storage_Count is
300    begin
301       return Roots.Table (B).High_Water_Mark;
302    end High_Water_Mark;
303
304    -----------
305    -- Image --
306    -----------
307
308    function Image (N : Name_Id) return String is
309       Nam : Name renames Names.Table (N);
310
311    begin
312       return String (Chars.Table (Nam.First .. Nam.Last));
313    end Image;
314
315    function Image
316      (F       : Frame_Id;
317       Max_Fil : Integer;
318       Max_Lin : Integer;
319       Short   : Boolean := False) return String
320    is
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);
325
326       Fil_Len  : constant Integer := Fil.Last - Fil.First + 1;
327       Lin_Len  : constant Integer := Lin.Last - Lin.First + 1;
328
329       use type Chars.Table_Type;
330
331       Spaces : constant String (1 .. 80) := (1 .. 80 => ' ');
332
333       Result : constant String :=
334         String (Chars.Table (Fil.First .. Fil.Last))
335         & ':'
336         & String (Chars.Table (Lin.First .. Lin.Last));
337    begin
338       if Short then
339          return Result;
340       else
341          return Result
342            & Spaces (1 .. 1 + Max_Fil - Fil_Len + Max_Lin - Lin_Len)
343            & String (Chars.Table (Nam.First .. Nam.Last));
344       end if;
345    end Image;
346
347    -------------
348    -- Name_Eq --
349    -------------
350
351    function Name_Eq (N1, N2 : Name) return Boolean is
352       use type Chars.Table_Type;
353    begin
354       return
355         Chars.Table (N1.First .. N1.Last) = Chars.Table (N2.First .. N2.Last);
356    end Name_Eq;
357
358    --------------
359    -- Nb_Alloc --
360    --------------
361
362    function Nb_Alloc (B : Root_Id) return Integer is
363    begin
364       return Roots.Table (B).Nb_Alloc;
365    end Nb_Alloc;
366
367    --------------
368    -- Print_BT --
369    --------------
370
371    procedure Print_BT (B  : Root_Id; Short : Boolean := False) is
372       Max_Col_Width : constant := 35;
373       --  Largest filename length for which backtraces will be
374       --  properly aligned. Frames containing longer names won't be
375       --  truncated but they won't be properly aligned either.
376
377       F : constant Frame_Array := Frames_Of (B);
378
379       Max_Fil : Integer;
380       Max_Lin : Integer;
381
382    begin
383       Max_Fil := 0;
384       Max_Lin := 0;
385
386       for J in F'Range loop
387          declare
388             Fram : Frame renames Frames.Table (F (J));
389             Fil  : Name renames Names.Table (Fram.File);
390             Lin  : Name renames Names.Table (Fram.Line);
391
392          begin
393             Max_Fil := Integer'Max (Max_Fil, Fil.Last - Fil.First + 1);
394             Max_Lin := Integer'Max (Max_Lin, Lin.Last - Lin.First + 1);
395          end;
396       end loop;
397
398       Max_Fil := Integer'Min (Max_Fil, Max_Col_Width);
399
400       for J in F'Range loop
401          Put ("   ");
402          Put_Line (Image (F (J), Max_Fil, Max_Lin, Short));
403       end loop;
404    end Print_BT;
405
406    -------------
407    -- Read_BT --
408    -------------
409
410    function Read_BT (BT_Depth : Integer) return Root_Id is
411       Max_Line : constant Integer := 500;
412       Curs1    : Integer;
413       Curs2    : Integer;
414       Line     : String (1 .. Max_Line);
415       Last     : Integer := 0;
416       Frames   : Frame_Array (1 .. BT_Depth);
417       F        : Integer := Frames'First;
418       Nam      : Name_Id;
419       Fil      : Name_Id;
420       Lin      : Name_Id;
421       Add      : System.Address;
422       Int_Add  : Integer_Address;
423       Fr       : Frame_Id;
424       Main_Found : Boolean := False;
425       pragma Warnings (Off, Line);
426
427       procedure Find_File;
428       --  Position Curs1 and Curs2 so that Line (Curs1 .. Curs2) contains
429       --  the file name. The file name may not be on the current line since
430       --  a frame may be printed on more than one line when there is a lot
431       --  of parameters or names are long, so this subprogram can read new
432       --  lines of input.
433
434       procedure Find_Line;
435       --  Position Curs1 and Curs2 so that Line (Curs1 .. Curs2) contains
436       --  the line number.
437
438       procedure Find_Name;
439       --  Position Curs1 and Curs2 so that Line (Curs1 .. Curs2) contains
440       --  the subprogram name.
441
442       function Skip_To_Space (Pos : Integer) return Integer;
443       --  Scans Line starting with position Pos, returning the position
444       --  immediately before the first space, or the value of Last if no
445       --  spaces were found
446
447
448       pragma Inline (Find_File, Find_Line, Find_Name, Skip_To_Space);
449
450       ---------------
451       -- Find_File --
452       ---------------
453
454       procedure Find_File is
455       begin
456          --  Skip " at "
457
458          Curs1 := Curs2 + 5;
459          Curs2 := Last;
460
461          --  Scan backwards from end of line until ':' is encountered
462
463          for J in reverse Curs1 .. Last loop
464             if Line (J) = ':' then
465                Curs2 := J - 1;
466             end if;
467          end loop;
468       end Find_File;
469
470       ---------------
471       -- Find_Line --
472       ---------------
473
474       procedure Find_Line is
475       begin
476          Curs1 := Curs2 + 2;
477          Curs2 := Last;
478
479          --  Check for Curs1 too large. Should never happen with non-corrupt
480          --  output. If it does happen, just reset it to the highest value.
481
482          if Curs1 > Last then
483             Curs1 := Last;
484          end if;
485       end Find_Line;
486
487       ---------------
488       -- Find_Name --
489       ---------------
490
491       procedure Find_Name is
492       begin
493          --  Skip the address value and " in "
494
495          Curs1 := Skip_To_Space (1) + 5;
496          Curs2 := Skip_To_Space (Curs1);
497       end Find_Name;
498
499       -------------------
500       -- Skip_To_Space --
501       -------------------
502
503       function Skip_To_Space (Pos : Integer) return Integer is
504       begin
505          for Cur in Pos .. Last loop
506             if Line (Cur) = ' ' then
507                return Cur - 1;
508             end if;
509          end loop;
510
511          return Last;
512       end Skip_To_Space;
513
514       procedure Gmem_Read_Next_Frame (Addr : out System.Address);
515       pragma Import (C, Gmem_Read_Next_Frame, "__gnat_gmem_read_next_frame");
516       --  Read the next frame in the current traceback. Addr is set to 0 if
517       --  there are no more addresses in this traceback. The pointer is moved
518       --  to the next frame.
519
520       procedure Gmem_Symbolic
521         (Addr : System.Address; Buf : String; Last : out Natural);
522       pragma Import (C, Gmem_Symbolic, "__gnat_gmem_symbolic");
523       --  Get the symbolic traceback for Addr. Note: we cannot use
524       --  GNAT.Tracebacks.Symbolic, since the latter will only work with the
525       --  current executable.
526       --
527       --  "__gnat_gmem_symbolic" will work with the executable whose name is
528       --  given in gnat_argv[0], as initialized by Gnatmem.Gmem_A21_Initialize.
529
530    --  Start of processing for Read_BT
531
532    begin
533       while F <= BT_Depth and then not Main_Found loop
534          Gmem_Read_Next_Frame (Add);
535          Int_Add := To_Integer (Add);
536          exit when Int_Add = 0;
537
538          Fr := Frame_HTable.Get (Int_Add);
539
540          if Fr = No_Frame_Id then
541             Gmem_Symbolic (Add, Line, Last);
542             Last := Last - 1; -- get rid of the trailing line-feed
543             Find_Name;
544
545             --  Skip the __gnat_malloc frame itself
546
547             if Line (Curs1 .. Curs2) /= "<__gnat_malloc>" then
548                Nam := Enter_Name (Line (Curs1 .. Curs2));
549                Main_Found := (Nam = Main_Name_Id);
550
551                Find_File;
552                Fil := Enter_Name (Line (Curs1 .. Curs2));
553                Find_Line;
554                Lin := Enter_Name (Line (Curs1 .. Curs2));
555
556                Frames (F) := Enter_Frame (Add, Nam, Fil, Lin);
557                F := F + 1;
558             end if;
559
560          else
561             Frames (F) := Fr;
562             Main_Found := (Memroot.Frames.Table (Fr).Name = Main_Name_Id);
563             F := F + 1;
564          end if;
565       end loop;
566
567       return Enter_Root (Frames (1 .. F - 1));
568    end Read_BT;
569
570    -------------
571    -- Root_Eq --
572    -------------
573
574    function Root_Eq (N1, N2 : Root) return Boolean is
575       use type Frames_In_Root.Table_Type;
576
577    begin
578       return
579         Frames_In_Root.Table (N1.First .. N1.Last)
580           = Frames_In_Root.Table (N2.First .. N2.Last);
581    end Root_Eq;
582
583    --------------------
584    -- Set_Alloc_Size --
585    --------------------
586
587    procedure Set_Alloc_Size (B : Root_Id; V : Storage_Count) is
588    begin
589       Roots.Table (B).Alloc_Size := V;
590    end Set_Alloc_Size;
591
592    -------------------------
593    -- Set_High_Water_Mark --
594    -------------------------
595
596    procedure Set_High_Water_Mark (B : Root_Id; V : Storage_Count) is
597    begin
598       Roots.Table (B).High_Water_Mark := V;
599    end Set_High_Water_Mark;
600
601    ------------------
602    -- Set_Nb_Alloc --
603    ------------------
604
605    procedure Set_Nb_Alloc (B : Root_Id; V : Integer) is
606    begin
607       Roots.Table (B).Nb_Alloc := V;
608    end Set_Nb_Alloc;
609
610 begin
611    --  Initialize name for No_Name_ID
612
613    Names.Increment_Last;
614    Names.Table (Names.Last) := Name'(1, 0);
615    Main_Name_Id := Enter_Name ("main");
616 end Memroot;