OSDN Git Service

* tree-chrec.c (avoid_arithmetics_in_type_p): New.
[pf3gnuchains/gcc-fork.git] / gcc / ada / g-debpoo.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT COMPILER COMPONENTS                         --
4 --                                                                          --
5 --                       G N A T . D E B U G _ P O O L S                    --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --          Copyright (C) 1992-2005, 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 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,  51  Franklin  Street,  Fifth  Floor, --
20 -- Boston, MA 02110-1301, USA.                                              --
21 --                                                                          --
22 -- As a special exception,  if other files  instantiate  generics from this --
23 -- unit, or you link  this unit with other files  to produce an executable, --
24 -- this  unit  does not  by itself cause  the resulting  executable  to  be --
25 -- covered  by the  GNU  General  Public  License.  This exception does not --
26 -- however invalidate  any other reasons why  the executable file  might be --
27 -- covered by the  GNU Public License.                                      --
28 --                                                                          --
29 -- GNAT was originally developed  by the GNAT team at  New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
31 --                                                                          --
32 ------------------------------------------------------------------------------
33
34 with Ada.Exceptions.Traceback;
35 with GNAT.IO; use GNAT.IO;
36
37 with System.Address_Image;
38 with System.Memory;     use System.Memory;
39 with System.Soft_Links; use System.Soft_Links;
40
41 with System.Traceback_Entries; use System.Traceback_Entries;
42
43 with GNAT.HTable;
44 with GNAT.Traceback; use GNAT.Traceback;
45
46 with Ada.Unchecked_Conversion;
47
48 package body GNAT.Debug_Pools is
49
50    Default_Alignment : constant := Standard'Maximum_Alignment;
51    --  Alignment used for the memory chunks returned by Allocate. Using this
52    --  value garantees that this alignment will be compatible with all types
53    --  and at the same time makes it easy to find the location of the extra
54    --  header allocated for each chunk.
55
56    Initial_Memory_Size : constant Storage_Offset := 2 ** 26; --  64 Mb
57    --  Initial size of memory that the debug pool can handle. This is used to
58    --  compute the size of the htable used to monitor the blocks, but this is
59    --  dynamic and will grow as needed. Having a bigger size here means a
60    --  longer setup time, but less time spent later on to grow the array.
61
62    Max_Ignored_Levels : constant Natural := 10;
63    --  Maximum number of levels that will be ignored in backtraces. This is so
64    --  that we still have enough significant levels in the tracebacks returned
65    --  to the user.
66    --
67    --  The value 10 is chosen as being greater than the maximum callgraph
68    --  in this package. Its actual value is not really relevant, as long as it
69    --  is high enough to make sure we still have enough frames to return to
70    --  the user after we have hidden the frames internal to this package.
71
72    ---------------------------
73    -- Back Trace Hash Table --
74    ---------------------------
75
76    --  This package needs to store one set of tracebacks for each allocation
77    --  point (when was it allocated or deallocated). This would use too much
78    --  memory,  so the tracebacks are actually stored in a hash table, and
79    --  we reference elements in this hash table instead.
80
81    --  This hash-table will remain empty if the discriminant Stack_Trace_Depth
82    --  for the pools is set to 0.
83
84    --  This table is a global table, that can be shared among all debug pools
85    --  with no problems.
86
87    type Header is range 1 .. 1023;
88    --  Number of elements in the hash-table
89
90    type Tracebacks_Array_Access
91       is access GNAT.Traceback.Tracebacks_Array;
92
93    type Traceback_Kind is (Alloc, Dealloc, Indirect_Alloc, Indirect_Dealloc);
94
95    type Traceback_Htable_Elem;
96    type Traceback_Htable_Elem_Ptr
97       is access Traceback_Htable_Elem;
98
99    type Traceback_Htable_Elem is record
100       Traceback : Tracebacks_Array_Access;
101       Kind      : Traceback_Kind;
102       Count     : Natural;
103       Total     : Byte_Count;
104       Next      : Traceback_Htable_Elem_Ptr;
105    end record;
106
107    --  Subprograms used for the Backtrace_Htable instantiation
108
109    procedure Set_Next
110      (E    : Traceback_Htable_Elem_Ptr;
111       Next : Traceback_Htable_Elem_Ptr);
112    pragma Inline (Set_Next);
113
114    function Next
115      (E : Traceback_Htable_Elem_Ptr) return Traceback_Htable_Elem_Ptr;
116    pragma Inline (Next);
117
118    function Get_Key
119      (E : Traceback_Htable_Elem_Ptr) return Tracebacks_Array_Access;
120    pragma Inline (Get_Key);
121
122    function Hash (T : Tracebacks_Array_Access) return Header;
123    pragma Inline (Hash);
124
125    function Equal (K1, K2 : Tracebacks_Array_Access) return Boolean;
126    --  Why is this not inlined???
127
128    --  The hash table for back traces
129
130    package Backtrace_Htable is new GNAT.HTable.Static_HTable
131      (Header_Num => Header,
132       Element    => Traceback_Htable_Elem,
133       Elmt_Ptr   => Traceback_Htable_Elem_Ptr,
134       Null_Ptr   => null,
135       Set_Next   => Set_Next,
136       Next       => Next,
137       Key        => Tracebacks_Array_Access,
138       Get_Key    => Get_Key,
139       Hash       => Hash,
140       Equal      => Equal);
141
142    -----------------------
143    -- Allocations table --
144    -----------------------
145
146    type Allocation_Header;
147    type Allocation_Header_Access is access Allocation_Header;
148
149    type Traceback_Ptr_Or_Address is new System.Address;
150    --  A type that acts as a C union, and is either a System.Address or a
151    --  Traceback_Htable_Elem_Ptr.
152
153    --  The following record stores extra information that needs to be
154    --  memorized for each block allocated with the special debug pool.
155
156    type Allocation_Header is record
157       Allocation_Address : System.Address;
158       --  Address of the block returned by malloc, possibly unaligned
159
160       Block_Size : Storage_Offset;
161       --  Needed only for advanced freeing algorithms (traverse all allocated
162       --  blocks for potential references). This value is negated when the
163       --  chunk of memory has been logically freed by the application. This
164       --  chunk has not been physically released yet.
165
166       Alloc_Traceback : Traceback_Htable_Elem_Ptr;
167       --  ??? comment required
168
169       Dealloc_Traceback : Traceback_Ptr_Or_Address;
170       --  Pointer to the traceback for the allocation (if the memory chunk is
171       --  still valid), or to the first deallocation otherwise. Make sure this
172       --  is a thin pointer to save space.
173       --
174       --  Dealloc_Traceback is also for blocks that are still allocated to
175       --  point to the previous block in the list. This saves space in this
176       --  header, and make manipulation of the lists of allocated pointers
177       --  faster.
178
179       Next : System.Address;
180       --  Point to the next block of the same type (either allocated or
181       --  logically freed) in memory. This points to the beginning of the user
182       --  data, and does not include the header of that block.
183    end record;
184
185    function Header_Of (Address : System.Address)
186       return Allocation_Header_Access;
187    pragma Inline (Header_Of);
188    --  Return the header corresponding to a previously allocated address
189
190    function To_Address is new Ada.Unchecked_Conversion
191      (Traceback_Ptr_Or_Address, System.Address);
192
193    function To_Address is new Ada.Unchecked_Conversion
194      (System.Address, Traceback_Ptr_Or_Address);
195
196    function To_Traceback is new Ada.Unchecked_Conversion
197      (Traceback_Ptr_Or_Address, Traceback_Htable_Elem_Ptr);
198
199    function To_Traceback is new Ada.Unchecked_Conversion
200      (Traceback_Htable_Elem_Ptr, Traceback_Ptr_Or_Address);
201
202    Header_Offset : constant Storage_Count :=
203                      Default_Alignment *
204                        ((Allocation_Header'Size / System.Storage_Unit
205                           + Default_Alignment - 1) / Default_Alignment);
206    --  Offset of user data after allocation header
207
208    Minimum_Allocation : constant Storage_Count :=
209                           Default_Alignment - 1 + Header_Offset;
210    --  Minimal allocation: size of allocation_header rounded up to next
211    --  multiple of default alignment + worst-case padding.
212
213    -----------------------
214    -- Allocations table --
215    -----------------------
216
217    --  This table is indexed on addresses modulo Default_Alignment, and for
218    --  each index it indicates whether that memory block is valid. Its behavior
219    --  is similar to GNAT.Table, except that we need to pack the table to save
220    --  space, so we cannot reuse GNAT.Table as is.
221
222    --  This table is the reason why all alignments have to be forced to common
223    --  value (Default_Alignment), so that this table can be kept to a
224    --  reasonnable size.
225
226    type Byte is mod 2 ** System.Storage_Unit;
227
228    Big_Table_Size : constant Storage_Offset :=
229                       (Storage_Offset'Last - 1) / Default_Alignment;
230    type Big_Table is array (0 .. Big_Table_Size) of Byte;
231    --  A simple, flat-array type used to access memory bytes (see the comment
232    --  for Valid_Blocks below).
233    --
234    --  It would be cleaner to represent this as a packed array of Boolean.
235    --  However, we cannot specify pragma Pack for such an array, since the
236    --  total size on a 64 bit machine would be too big (> Integer'Last).
237    --
238    --  Given an address, we know if it is under control of the debug pool if
239    --  the byte at index:
240    --       ((Address - Edata'Address) / Default_Alignment)
241    --        / Storage_unit
242    --  has the bit
243    --       ((Address - Edata'Address) / Default_Alignment)
244    --        mod Storage_Unit
245    --  set to 1.
246    --
247    --  See the subprograms Is_Valid and Set_Valid for proper manipulation of
248    --  this array.
249
250    type Table_Ptr is access Big_Table;
251    function To_Pointer is new Ada.Unchecked_Conversion
252      (System.Address, Table_Ptr);
253
254    Valid_Blocks      : Table_Ptr      := null;
255    Valid_Blocks_Size : Storage_Offset := 0;
256    --  These two variables represents a mapping of the currently allocated
257    --  memory. Every time the pool works on an address, we first check that the
258    --  index Address / Default_Alignment is True. If not, this means that this
259    --  address is not under control of the debug pool and thus this is probably
260    --  an invalid memory access (it could also be a general access type).
261    --
262    --  Note that in fact we never allocate the full size of Big_Table, only a
263    --  slice big enough to manage the currently allocated memory.
264
265    Edata : System.Address := System.Null_Address;
266    --  Address in memory that matches the index 0 in Valid_Blocks. It is named
267    --  after the symbol _edata, which, on most systems, indicate the lowest
268    --  possible address returned by malloc. Unfortunately, this symbol doesn't
269    --  exist on windows, so we cannot use it instead of this variable.
270
271    -----------------------
272    -- Local subprograms --
273    -----------------------
274
275    function Find_Or_Create_Traceback
276      (Pool                : Debug_Pool;
277       Kind                : Traceback_Kind;
278       Size                : Storage_Count;
279       Ignored_Frame_Start : System.Address;
280       Ignored_Frame_End   : System.Address) return Traceback_Htable_Elem_Ptr;
281    --  Return an element matching the current traceback (omitting the frames
282    --  that are in the current package). If this traceback already existed in
283    --  the htable, a pointer to this is returned to spare memory. Null is
284    --  returned if the pool is set not to store tracebacks. If the traceback
285    --  already existed in the table, the count is incremented so that
286    --  Dump_Tracebacks returns useful results. All addresses up to, and
287    --  including, an address between Ignored_Frame_Start .. Ignored_Frame_End
288    --  are ignored.
289
290    procedure Put_Line
291      (Depth               : Natural;
292       Traceback           : Tracebacks_Array_Access;
293       Ignored_Frame_Start : System.Address := System.Null_Address;
294       Ignored_Frame_End   : System.Address := System.Null_Address);
295    --  Print Traceback to Standard_Output. If Traceback is null, print the
296    --  call_chain at the current location, up to Depth levels, ignoring all
297    --  addresses up to the first one in the range
298    --  Ignored_Frame_Start .. Ignored_Frame_End
299
300    function Is_Valid (Storage : System.Address) return Boolean;
301    pragma Inline (Is_Valid);
302    --  Return True if Storage is an address that the debug pool has under its
303    --  control.
304
305    procedure Set_Valid (Storage : System.Address; Value : Boolean);
306    pragma Inline (Set_Valid);
307    --  Mark the address Storage as being under control of the memory pool (if
308    --  Value is True), or not (if Value is False). This procedure will
309    --  reallocate the table Valid_Blocks as needed.
310
311    procedure Set_Dead_Beef
312      (Storage_Address          : System.Address;
313       Size_In_Storage_Elements : Storage_Count);
314    --  Set the contents of the memory block pointed to by Storage_Address to
315    --  the 16#DEADBEEF# pattern. If Size_In_Storage_Elements is not a multiple
316    --  of the length of this pattern, the last instance may be partial.
317
318    procedure Free_Physically (Pool : in out Debug_Pool);
319    --  Start to physically release some memory to the system, until the amount
320    --  of logically (but not physically) freed memory is lower than the
321    --  expected amount in Pool.
322
323    procedure Allocate_End;
324    procedure Deallocate_End;
325    procedure Dereference_End;
326    --  These procedures are used as markers when computing the stacktraces,
327    --  so that addresses in the debug pool itself are not reported to the user.
328
329    Code_Address_For_Allocate_End    : System.Address;
330    Code_Address_For_Deallocate_End  : System.Address;
331    Code_Address_For_Dereference_End : System.Address;
332    --  Taking the address of the above procedures will not work on some
333    --  architectures (HPUX and VMS for instance). Thus we do the same thing
334    --  that is done in a-except.adb, and get the address of labels instead
335
336    procedure Skip_Levels
337      (Depth               : Natural;
338       Trace               : Tracebacks_Array;
339       Start               : out Natural;
340       Len                 : in out Natural;
341       Ignored_Frame_Start : System.Address;
342       Ignored_Frame_End   : System.Address);
343    --  Set Start .. Len to the range of values from Trace that should be output
344    --  to the user. This range of values exludes any address prior to the first
345    --  one in Ignored_Frame_Start .. Ignored_Frame_End (basically addresses
346    --  internal to this package). Depth is the number of levels that the user
347    --  is interested in.
348
349    ---------------
350    -- Header_Of --
351    ---------------
352
353    function Header_Of (Address : System.Address)
354       return Allocation_Header_Access
355    is
356       function Convert is new Ada.Unchecked_Conversion
357         (System.Address, Allocation_Header_Access);
358    begin
359       return Convert (Address - Header_Offset);
360    end Header_Of;
361
362    --------------
363    -- Set_Next --
364    --------------
365
366    procedure Set_Next
367      (E    : Traceback_Htable_Elem_Ptr;
368       Next : Traceback_Htable_Elem_Ptr)
369    is
370    begin
371       E.Next := Next;
372    end Set_Next;
373
374    ----------
375    -- Next --
376    ----------
377
378    function Next
379      (E : Traceback_Htable_Elem_Ptr) return Traceback_Htable_Elem_Ptr is
380    begin
381       return E.Next;
382    end Next;
383
384    -----------
385    -- Equal --
386    -----------
387
388    function Equal (K1, K2 : Tracebacks_Array_Access) return Boolean is
389       use Ada.Exceptions.Traceback;
390    begin
391       return K1.all = K2.all;
392    end Equal;
393
394    -------------
395    -- Get_Key --
396    -------------
397
398    function Get_Key
399      (E : Traceback_Htable_Elem_Ptr) return Tracebacks_Array_Access
400    is
401    begin
402       return E.Traceback;
403    end Get_Key;
404
405    ----------
406    -- Hash --
407    ----------
408
409    function Hash (T : Tracebacks_Array_Access) return Header is
410       Result : Integer_Address := 0;
411
412    begin
413       for X in T'Range loop
414          Result := Result + To_Integer (PC_For (T (X)));
415       end loop;
416
417       return Header (1 + Result mod Integer_Address (Header'Last));
418    end Hash;
419
420    --------------
421    -- Put_Line --
422    --------------
423
424    procedure Put_Line
425      (Depth               : Natural;
426       Traceback           : Tracebacks_Array_Access;
427       Ignored_Frame_Start : System.Address := System.Null_Address;
428       Ignored_Frame_End   : System.Address := System.Null_Address)
429    is
430       procedure Print (Tr : Tracebacks_Array);
431       --  Print the traceback to standard_output
432
433       -----------
434       -- Print --
435       -----------
436
437       procedure Print (Tr : Tracebacks_Array) is
438       begin
439          for J in Tr'Range loop
440             Put ("0x" & Address_Image (PC_For (Tr (J))) & ' ');
441          end loop;
442          Put (ASCII.LF);
443       end Print;
444
445    --  Start of processing for Put_Line
446
447    begin
448       if Traceback = null then
449          declare
450             Tr  : aliased Tracebacks_Array (1 .. Depth + Max_Ignored_Levels);
451             Start, Len : Natural;
452
453          begin
454             Call_Chain (Tr, Len);
455             Skip_Levels (Depth, Tr, Start, Len,
456                          Ignored_Frame_Start, Ignored_Frame_End);
457             Print (Tr (Start .. Len));
458          end;
459
460       else
461          Print (Traceback.all);
462       end if;
463    end Put_Line;
464
465    -----------------
466    -- Skip_Levels --
467    -----------------
468
469    procedure Skip_Levels
470      (Depth               : Natural;
471       Trace               : Tracebacks_Array;
472       Start               : out Natural;
473       Len                 : in out Natural;
474       Ignored_Frame_Start : System.Address;
475       Ignored_Frame_End   : System.Address)
476    is
477    begin
478       Start := Trace'First;
479
480       while Start <= Len
481         and then (PC_For (Trace (Start)) < Ignored_Frame_Start
482                     or else PC_For (Trace (Start)) > Ignored_Frame_End)
483       loop
484          Start := Start + 1;
485       end loop;
486
487       Start := Start + 1;
488
489       --  Just in case: make sure we have a traceback even if Ignore_Till
490       --  wasn't found.
491
492       if Start > Len then
493          Start := 1;
494       end if;
495
496       if Len - Start + 1 > Depth then
497          Len := Depth + Start - 1;
498       end if;
499    end Skip_Levels;
500
501    ------------------------------
502    -- Find_Or_Create_Traceback --
503    ------------------------------
504
505    function Find_Or_Create_Traceback
506      (Pool                : Debug_Pool;
507       Kind                : Traceback_Kind;
508       Size                : Storage_Count;
509       Ignored_Frame_Start : System.Address;
510       Ignored_Frame_End   : System.Address) return Traceback_Htable_Elem_Ptr
511    is
512    begin
513       if Pool.Stack_Trace_Depth = 0 then
514          return null;
515       end if;
516
517       declare
518          Trace : aliased Tracebacks_Array
519                   (1 .. Integer (Pool.Stack_Trace_Depth) + Max_Ignored_Levels);
520          Len, Start   : Natural;
521          Elem  : Traceback_Htable_Elem_Ptr;
522
523       begin
524          Call_Chain (Trace, Len);
525          Skip_Levels (Pool.Stack_Trace_Depth, Trace, Start, Len,
526                       Ignored_Frame_Start, Ignored_Frame_End);
527
528          --  Check if the traceback is already in the table
529
530          Elem :=
531            Backtrace_Htable.Get (Trace (Start .. Len)'Unrestricted_Access);
532
533          --  If not, insert it
534
535          if Elem = null then
536             Elem := new Traceback_Htable_Elem'
537               (Traceback => new Tracebacks_Array'(Trace (Start .. Len)),
538                Count     => 1,
539                Kind      => Kind,
540                Total     => Byte_Count (Size),
541                Next      => null);
542             Backtrace_Htable.Set (Elem);
543
544          else
545             Elem.Count := Elem.Count + 1;
546             Elem.Total := Elem.Total + Byte_Count (Size);
547          end if;
548
549          return Elem;
550       end;
551    end Find_Or_Create_Traceback;
552
553    --------------
554    -- Is_Valid --
555    --------------
556
557    function Is_Valid (Storage : System.Address) return Boolean is
558       Offset : constant Storage_Offset :=
559                  (Storage - Edata) / Default_Alignment;
560       Bit : constant Byte := 2 ** Natural (Offset mod System.Storage_Unit);
561    begin
562       return (Storage mod Default_Alignment) = 0
563         and then Offset >= 0
564         and then Offset < Valid_Blocks_Size * Storage_Unit
565         and then (Valid_Blocks (Offset / Storage_Unit) and Bit) /= 0;
566    end Is_Valid;
567
568    ---------------
569    -- Set_Valid --
570    ---------------
571
572    procedure Set_Valid (Storage : System.Address; Value : Boolean) is
573       Offset : Storage_Offset;
574       Bit    : Byte;
575       Bytes  : Storage_Offset;
576       Tmp    : constant Table_Ptr := Valid_Blocks;
577
578       Edata_Align : constant Storage_Offset :=
579                       Default_Alignment * Storage_Unit;
580
581       procedure Memset (A : Address; C : Integer; N : size_t);
582       pragma Import (C, Memset, "memset");
583
584       procedure Memmove (Dest, Src : Address; N : size_t);
585       pragma Import (C, Memmove, "memmove");
586
587    begin
588       --  Allocate, or reallocate, the valid blocks table as needed. We start
589       --  with a size big enough to handle Initial_Memory_Size bytes of memory,
590       --  to avoid too many reallocations. The table will typically be around
591       --  16Mb in that case, which is still small enough.
592
593       if Valid_Blocks_Size = 0 then
594          Valid_Blocks_Size := (Initial_Memory_Size / Default_Alignment)
595                                                       / Storage_Unit;
596          Valid_Blocks := To_Pointer (Alloc (size_t (Valid_Blocks_Size)));
597          Edata := Storage;
598
599          --  Reset the memory using memset, which is much faster than the
600          --  standard Ada code with "when others"
601
602          Memset (Valid_Blocks.all'Address, 0, size_t (Valid_Blocks_Size));
603       end if;
604
605       --  First case : the new address is outside of the current scope of
606       --  Valid_Blocks, before the current start address. We need to reallocate
607       --  the table accordingly. This should be a rare occurence, since in most
608       --  cases, the first allocation will also have the lowest address. But
609       --  there is no garantee...
610
611       if Storage < Edata then
612
613          --  The difference between the new Edata and the current one must be
614          --  a multiple of Default_Alignment * Storage_Unit, so that the bit
615          --  representing an address in Valid_Blocks are kept the same.
616
617          Offset := ((Edata - Storage) / Edata_Align + 1) * Edata_Align;
618          Offset := Offset / Default_Alignment;
619          Bytes  := Offset / Storage_Unit;
620          Valid_Blocks :=
621            To_Pointer (Alloc (Size => size_t (Valid_Blocks_Size + Bytes)));
622          Memmove (Dest => Valid_Blocks.all'Address + Bytes,
623                   Src  => Tmp.all'Address,
624                   N    => size_t (Valid_Blocks_Size));
625          Memset (A => Valid_Blocks.all'Address,
626                  C => 0,
627                  N => size_t (Bytes));
628          Free (Tmp.all'Address);
629          Valid_Blocks_Size := Valid_Blocks_Size + Bytes;
630
631          --  Take into the account the new start address
632
633          Edata := Storage - Edata_Align + (Edata - Storage) mod Edata_Align;
634       end if;
635
636       --  Second case : the new address is outside of the current scope of
637       --  Valid_Blocks, so we have to grow the table as appropriate.
638
639       --  Note: it might seem more natural for the following statement to
640       --  be written:
641
642       --      Offset := (Storage - Edata) / Default_Alignment;
643
644       --  but that won't work since Storage_Offset is signed, and it is
645       --  possible to subtract a small address from a large address and
646       --  get a negative value. This may seem strange, but it is quite
647       --  specifically allowed in the RM, and is what most implementations
648       --  including GNAT actually do. Hence the conversion to Integer_Address
649       --  which is a full range modular type, not subject to this glitch.
650
651       Offset := Storage_Offset ((To_Integer (Storage) - To_Integer (Edata)) /
652                                               Default_Alignment);
653
654       if Offset >= Valid_Blocks_Size * System.Storage_Unit then
655          Bytes := Valid_Blocks_Size;
656          loop
657             Bytes := 2 * Bytes;
658             exit when Offset <= Bytes * System.Storage_Unit;
659          end loop;
660
661          Valid_Blocks := To_Pointer
662            (Realloc (Ptr  => Valid_Blocks.all'Address,
663                      Size => size_t (Bytes)));
664          Memset
665            (Valid_Blocks.all'Address + Valid_Blocks_Size,
666             0,
667             size_t (Bytes - Valid_Blocks_Size));
668          Valid_Blocks_Size := Bytes;
669       end if;
670
671       Bit    := 2 ** Natural (Offset mod System.Storage_Unit);
672       Bytes  := Offset / Storage_Unit;
673
674       --  Then set the value as valid
675
676       if Value then
677          Valid_Blocks (Bytes) := Valid_Blocks (Bytes) or Bit;
678       else
679          Valid_Blocks (Bytes) := Valid_Blocks (Bytes) and (not Bit);
680       end if;
681    end Set_Valid;
682
683    --------------
684    -- Allocate --
685    --------------
686
687    procedure Allocate
688      (Pool                     : in out Debug_Pool;
689       Storage_Address          : out Address;
690       Size_In_Storage_Elements : Storage_Count;
691       Alignment                : Storage_Count)
692    is
693       pragma Unreferenced (Alignment);
694       --  Ignored, we always force 'Default_Alignment
695
696       type Local_Storage_Array is new Storage_Array
697         (1 .. Size_In_Storage_Elements + Minimum_Allocation);
698
699       type Ptr is access Local_Storage_Array;
700       --  On some systems, we might want to physically protect pages
701       --  against writing when they have been freed (of course, this is
702       --  expensive in terms of wasted memory). To do that, all we should
703       --  have to do it to set the size of this array to the page size.
704       --  See mprotect().
705
706       P : Ptr;
707
708       Current : Byte_Count;
709       Trace   : Traceback_Htable_Elem_Ptr;
710
711    begin
712       <<Allocate_Label>>
713       Lock_Task.all;
714
715       --  If necessary, start physically releasing memory. The reason this is
716       --  done here, although Pool.Logically_Deallocated has not changed above,
717       --  is so that we do this only after a series of deallocations (e.g a
718       --  loop that deallocates a big array). If we were doing that in
719       --  Deallocate, we might be physically freeing memory several times
720       --  during the loop, which is expensive.
721
722       if Pool.Logically_Deallocated >
723         Byte_Count (Pool.Maximum_Logically_Freed_Memory)
724       then
725          Free_Physically (Pool);
726       end if;
727
728       --  Use standard (ie through malloc) allocations. This automatically
729       --  raises Storage_Error if needed. We also try once more to physically
730       --  release memory, so that even marked blocks, in the advanced scanning,
731       --  are freed.
732
733       begin
734          P := new Local_Storage_Array;
735
736       exception
737          when Storage_Error =>
738             Free_Physically (Pool);
739             P := new Local_Storage_Array;
740       end;
741
742       Storage_Address :=
743         System.Null_Address + Default_Alignment
744           * (((P.all'Address + Default_Alignment - 1) - System.Null_Address)
745              / Default_Alignment)
746         + Header_Offset;
747
748       pragma Assert ((Storage_Address - System.Null_Address)
749                      mod Default_Alignment = 0);
750       pragma Assert (Storage_Address + Size_In_Storage_Elements
751                      <= P.all'Address + P'Length);
752
753       Trace := Find_Or_Create_Traceback
754         (Pool, Alloc, Size_In_Storage_Elements,
755          Allocate_Label'Address, Code_Address_For_Allocate_End);
756
757       pragma Warnings (Off);
758       --  Turn warning on alignment for convert call off. We know that in
759       --  fact this conversion is safe since P itself is always aligned on
760       --  Default_Alignment.
761
762       Header_Of (Storage_Address).all :=
763         (Allocation_Address => P.all'Address,
764          Alloc_Traceback    => Trace,
765          Dealloc_Traceback  => To_Traceback (null),
766          Next               => Pool.First_Used_Block,
767          Block_Size         => Size_In_Storage_Elements);
768
769       pragma Warnings (On);
770
771       --  Link this block in the list of used blocks. This will be used to list
772       --  memory leaks in Print_Info, and for the advanced schemes of
773       --  Physical_Free, where we want to traverse all allocated blocks and
774       --  search for possible references.
775
776       --  We insert in front, since most likely we'll be freeing the most
777       --  recently allocated blocks first (the older one might stay allocated
778       --  for the whole life of the application).
779
780       if Pool.First_Used_Block /= System.Null_Address then
781          Header_Of (Pool.First_Used_Block).Dealloc_Traceback :=
782            To_Address (Storage_Address);
783       end if;
784
785       Pool.First_Used_Block := Storage_Address;
786
787       --  Mark the new address as valid
788
789       Set_Valid (Storage_Address, True);
790
791       --  Update internal data
792
793       Pool.Allocated :=
794         Pool.Allocated + Byte_Count (Size_In_Storage_Elements);
795
796       Current := Pool.Allocated -
797                    Pool.Logically_Deallocated -
798                      Pool.Physically_Deallocated;
799
800       if Current > Pool.High_Water then
801          Pool.High_Water := Current;
802       end if;
803
804       Unlock_Task.all;
805
806    exception
807       when others =>
808          Unlock_Task.all;
809          raise;
810    end Allocate;
811
812    ------------------
813    -- Allocate_End --
814    ------------------
815
816    --  DO NOT MOVE, this must be right after Allocate. This is similar to
817    --  what is done in a-except, so that we can hide the traceback frames
818    --  internal to this package
819
820    procedure Allocate_End is
821    begin
822       <<Allocate_End_Label>>
823       Code_Address_For_Allocate_End := Allocate_End_Label'Address;
824    end Allocate_End;
825
826    -------------------
827    -- Set_Dead_Beef --
828    -------------------
829
830    procedure Set_Dead_Beef
831      (Storage_Address          : System.Address;
832       Size_In_Storage_Elements : Storage_Count)
833    is
834       Dead_Bytes : constant := 4;
835
836       type Data is mod 2 ** (Dead_Bytes * 8);
837       for Data'Size use Dead_Bytes * 8;
838
839       Dead : constant Data := 16#DEAD_BEEF#;
840
841       type Dead_Memory is array
842         (1 .. Size_In_Storage_Elements / Dead_Bytes) of Data;
843       type Mem_Ptr is access Dead_Memory;
844
845       type Byte is mod 2 ** 8;
846       for Byte'Size use 8;
847
848       type Dead_Memory_Bytes is array (0 .. 2) of Byte;
849       type Dead_Memory_Bytes_Ptr is access Dead_Memory_Bytes;
850
851       function From_Ptr is new Ada.Unchecked_Conversion
852         (System.Address, Mem_Ptr);
853
854       function From_Ptr is new Ada.Unchecked_Conversion
855         (System.Address, Dead_Memory_Bytes_Ptr);
856
857       M      : constant Mem_Ptr := From_Ptr (Storage_Address);
858       M2     : Dead_Memory_Bytes_Ptr;
859       Modulo : constant Storage_Count :=
860                  Size_In_Storage_Elements mod Dead_Bytes;
861    begin
862       M.all := (others => Dead);
863
864       --  Any bytes left (up to three of them)
865
866       if Modulo /= 0 then
867          M2 := From_Ptr (Storage_Address + M'Length * Dead_Bytes);
868
869          M2 (0) := 16#DE#;
870          if Modulo >= 2 then
871             M2 (1) := 16#AD#;
872
873             if Modulo >= 3 then
874                M2 (2) := 16#BE#;
875             end if;
876          end if;
877       end if;
878    end Set_Dead_Beef;
879
880    ---------------------
881    -- Free_Physically --
882    ---------------------
883
884    procedure Free_Physically (Pool : in out Debug_Pool) is
885       type Byte is mod 256;
886       type Byte_Access is access Byte;
887
888       function To_Byte is new Ada.Unchecked_Conversion
889         (System.Address, Byte_Access);
890
891       type Address_Access is access System.Address;
892
893       function To_Address_Access is new Ada.Unchecked_Conversion
894         (System.Address, Address_Access);
895
896       In_Use_Mark : constant Byte := 16#D#;
897       Free_Mark   : constant Byte := 16#F#;
898
899       Total_Freed : Storage_Count := 0;
900
901       procedure Reset_Marks;
902       --  Unmark all the logically freed blocks, so that they are considered
903       --  for physical deallocation
904
905       procedure Mark
906         (H : Allocation_Header_Access; A : System.Address; In_Use : Boolean);
907       --  Mark the user data block starting at A. For a block of size zero,
908       --  nothing is done. For a block with a different size, the first byte
909       --  is set to either "D" (in use) or "F" (free).
910
911       function Marked (A : System.Address) return Boolean;
912       --  Return true if the user data block starting at A might be in use
913       --  somewhere else
914
915       procedure Mark_Blocks;
916       --  Traverse all allocated blocks, and search for possible references
917       --  to logically freed blocks. Mark them appropriately
918
919       procedure Free_Blocks (Ignore_Marks : Boolean);
920       --  Physically release blocks. Only the blocks that haven't been marked
921       --  will be released, unless Ignore_Marks is true.
922
923       -----------------
924       -- Free_Blocks --
925       -----------------
926
927       procedure Free_Blocks (Ignore_Marks : Boolean) is
928          Header   : Allocation_Header_Access;
929          Tmp      : System.Address := Pool.First_Free_Block;
930          Next     : System.Address;
931          Previous : System.Address := System.Null_Address;
932
933       begin
934          while Tmp /= System.Null_Address
935            and then Total_Freed < Pool.Minimum_To_Free
936          loop
937             Header := Header_Of (Tmp);
938
939             --  If we know, or at least assume, the block is no longer
940             --  reference anywhere, we can free it physically.
941
942             if Ignore_Marks or else not Marked (Tmp) then
943
944                declare
945                   pragma Suppress (All_Checks);
946                   --  Suppress the checks on this section. If they are overflow
947                   --  errors, it isn't critical, and we'd rather avoid a
948                   --  Constraint_Error in that case.
949                begin
950                   --  Note that block_size < zero for freed blocks
951
952                   Pool.Physically_Deallocated :=
953                     Pool.Physically_Deallocated -
954                       Byte_Count (Header.Block_Size);
955
956                   Pool.Logically_Deallocated :=
957                     Pool.Logically_Deallocated +
958                       Byte_Count (Header.Block_Size);
959
960                   Total_Freed := Total_Freed - Header.Block_Size;
961                end;
962
963                Next := Header.Next;
964                System.Memory.Free (Header.Allocation_Address);
965                Set_Valid (Tmp, False);
966
967                --  Remove this block from the list
968
969                if Previous = System.Null_Address then
970                   Pool.First_Free_Block := Next;
971                else
972                   Header_Of (Previous).Next := Next;
973                end if;
974
975                Tmp  := Next;
976
977             else
978                Previous := Tmp;
979                Tmp := Header.Next;
980             end if;
981          end loop;
982       end Free_Blocks;
983
984       ----------
985       -- Mark --
986       ----------
987
988       procedure Mark
989         (H      : Allocation_Header_Access;
990          A      : System.Address;
991          In_Use : Boolean)
992       is
993       begin
994          if H.Block_Size /= 0 then
995             if In_Use then
996                To_Byte (A).all := In_Use_Mark;
997             else
998                To_Byte (A).all := Free_Mark;
999             end if;
1000          end if;
1001       end Mark;
1002
1003       -----------------
1004       -- Mark_Blocks --
1005       -----------------
1006
1007       procedure Mark_Blocks is
1008          Tmp      : System.Address := Pool.First_Used_Block;
1009          Previous : System.Address;
1010          Last     : System.Address;
1011          Pointed  : System.Address;
1012          Header   : Allocation_Header_Access;
1013
1014       begin
1015          --  For each allocated block, check its contents. Things that look
1016          --  like a possible address are used to mark the blocks so that we try
1017          --  and keep them, for better detection in case of invalid access.
1018          --  This mechanism is far from being fool-proof: it doesn't check the
1019          --  stacks of the threads, doesn't check possible memory allocated not
1020          --  under control of this debug pool. But it should allow us to catch
1021          --  more cases.
1022
1023          while Tmp /= System.Null_Address loop
1024             Previous := Tmp;
1025             Last     := Tmp + Header_Of (Tmp).Block_Size;
1026             while Previous < Last loop
1027                --  ??? Should we move byte-per-byte, or consider that addresses
1028                --  are always aligned on 4-bytes boundaries ? Let's use the
1029                --  fastest for now.
1030
1031                Pointed := To_Address_Access (Previous).all;
1032                if Is_Valid (Pointed) then
1033                   Header := Header_Of (Pointed);
1034
1035                   --  Do not even attempt to mark blocks in use. That would
1036                   --  screw up the whole application, of course.
1037                   if Header.Block_Size < 0 then
1038                      Mark (Header, Pointed, In_Use => True);
1039                   end if;
1040                end if;
1041
1042                Previous := Previous + System.Address'Size;
1043             end loop;
1044
1045             Tmp := Header_Of (Tmp).Next;
1046          end loop;
1047       end Mark_Blocks;
1048
1049       ------------
1050       -- Marked --
1051       ------------
1052
1053       function Marked (A : System.Address) return Boolean is
1054       begin
1055          return To_Byte (A).all = In_Use_Mark;
1056       end Marked;
1057
1058       -----------------
1059       -- Reset_Marks --
1060       -----------------
1061
1062       procedure Reset_Marks is
1063          Current : System.Address := Pool.First_Free_Block;
1064          Header  : Allocation_Header_Access;
1065       begin
1066          while Current /= System.Null_Address loop
1067             Header := Header_Of (Current);
1068             Mark (Header, Current, False);
1069             Current := Header.Next;
1070          end loop;
1071       end Reset_Marks;
1072
1073    --  Start of processing for Free_Physically
1074
1075    begin
1076       Lock_Task.all;
1077
1078       if Pool.Advanced_Scanning then
1079          Reset_Marks; --  Reset the mark for each freed block
1080          Mark_Blocks;
1081       end if;
1082
1083       Free_Blocks (Ignore_Marks => not Pool.Advanced_Scanning);
1084
1085       --  The contract is that we need to free at least Minimum_To_Free bytes,
1086       --  even if this means freeing marked blocks in the advanced scheme
1087
1088       if Total_Freed < Pool.Minimum_To_Free
1089         and then Pool.Advanced_Scanning
1090       then
1091          Pool.Marked_Blocks_Deallocated := True;
1092          Free_Blocks (Ignore_Marks => True);
1093       end if;
1094
1095       Unlock_Task.all;
1096
1097    exception
1098       when others =>
1099          Unlock_Task.all;
1100          raise;
1101    end Free_Physically;
1102
1103    ----------------
1104    -- Deallocate --
1105    ----------------
1106
1107    procedure Deallocate
1108      (Pool                     : in out Debug_Pool;
1109       Storage_Address          : Address;
1110       Size_In_Storage_Elements : Storage_Count;
1111       Alignment                : Storage_Count)
1112    is
1113       pragma Unreferenced (Alignment);
1114
1115       Header   : constant Allocation_Header_Access :=
1116         Header_Of (Storage_Address);
1117       Valid    : Boolean;
1118       Previous : System.Address;
1119
1120    begin
1121       <<Deallocate_Label>>
1122       Lock_Task.all;
1123       Valid := Is_Valid (Storage_Address);
1124
1125       if not Valid then
1126          Unlock_Task.all;
1127          if Pool.Raise_Exceptions then
1128             raise Freeing_Not_Allocated_Storage;
1129          else
1130             Put ("error: Freeing not allocated storage, at ");
1131             Put_Line (Pool.Stack_Trace_Depth, null,
1132                       Deallocate_Label'Address,
1133                       Code_Address_For_Deallocate_End);
1134          end if;
1135
1136       elsif Header.Block_Size < 0 then
1137          Unlock_Task.all;
1138          if Pool.Raise_Exceptions then
1139             raise Freeing_Deallocated_Storage;
1140          else
1141             Put ("error: Freeing already deallocated storage, at ");
1142             Put_Line (Pool.Stack_Trace_Depth, null,
1143                       Deallocate_Label'Address,
1144                       Code_Address_For_Deallocate_End);
1145             Put ("   Memory already deallocated at ");
1146             Put_Line (0, To_Traceback (Header.Dealloc_Traceback).Traceback);
1147             Put ("   Memory was allocated at ");
1148             Put_Line (0, Header.Alloc_Traceback.Traceback);
1149          end if;
1150
1151       else
1152          --  Remove this block from the list of used blocks
1153
1154          Previous :=
1155            To_Address (Header_Of (Storage_Address).Dealloc_Traceback);
1156
1157          if Previous = System.Null_Address then
1158             Pool.First_Used_Block := Header_Of (Pool.First_Used_Block).Next;
1159
1160             if Pool.First_Used_Block /= System.Null_Address then
1161                Header_Of (Pool.First_Used_Block).Dealloc_Traceback :=
1162                  To_Traceback (null);
1163             end if;
1164
1165          else
1166             Header_Of (Previous).Next := Header_Of (Storage_Address).Next;
1167
1168             if Header_Of (Storage_Address).Next /= System.Null_Address then
1169                Header_Of
1170                  (Header_Of (Storage_Address).Next).Dealloc_Traceback :=
1171                     To_Address (Previous);
1172             end if;
1173          end if;
1174
1175          --  Update the header
1176
1177          Header.all :=
1178            (Allocation_Address => Header.Allocation_Address,
1179             Alloc_Traceback    => Header.Alloc_Traceback,
1180             Dealloc_Traceback  => To_Traceback
1181                                     (Find_Or_Create_Traceback
1182                                        (Pool, Dealloc,
1183                                         Size_In_Storage_Elements,
1184                                         Deallocate_Label'Address,
1185                                         Code_Address_For_Deallocate_End)),
1186             Next               => System.Null_Address,
1187             Block_Size         => -Size_In_Storage_Elements);
1188
1189          if Pool.Reset_Content_On_Free then
1190             Set_Dead_Beef (Storage_Address, Size_In_Storage_Elements);
1191          end if;
1192
1193          Pool.Logically_Deallocated :=
1194            Pool.Logically_Deallocated +
1195              Byte_Count (Size_In_Storage_Elements);
1196
1197          --  Link this free block with the others (at the end of the list, so
1198          --  that we can start releasing the older blocks first later on).
1199
1200          if Pool.First_Free_Block = System.Null_Address then
1201             Pool.First_Free_Block := Storage_Address;
1202             Pool.Last_Free_Block := Storage_Address;
1203
1204          else
1205             Header_Of (Pool.Last_Free_Block).Next := Storage_Address;
1206             Pool.Last_Free_Block := Storage_Address;
1207          end if;
1208
1209          --  Do not physically release the memory here, but in Alloc.
1210          --  See comment there for details.
1211
1212          Unlock_Task.all;
1213       end if;
1214
1215    exception
1216       when others =>
1217          Unlock_Task.all;
1218          raise;
1219    end Deallocate;
1220
1221    --------------------
1222    -- Deallocate_End --
1223    --------------------
1224
1225    --  DO NOT MOVE, this must be right after Deallocate
1226    --  See Allocate_End
1227
1228    procedure Deallocate_End is
1229    begin
1230       <<Deallocate_End_Label>>
1231       Code_Address_For_Deallocate_End := Deallocate_End_Label'Address;
1232    end Deallocate_End;
1233
1234    -----------------
1235    -- Dereference --
1236    -----------------
1237
1238    procedure Dereference
1239      (Pool                     : in out Debug_Pool;
1240       Storage_Address          : Address;
1241       Size_In_Storage_Elements : Storage_Count;
1242       Alignment                : Storage_Count)
1243    is
1244       pragma Unreferenced (Alignment, Size_In_Storage_Elements);
1245
1246       Valid   : constant Boolean := Is_Valid (Storage_Address);
1247       Header  : Allocation_Header_Access;
1248
1249    begin
1250       --  Locking policy: we do not do any locking in this procedure. The
1251       --  tables are only read, not written to, and although a problem might
1252       --  appear if someone else is modifying the tables at the same time, this
1253       --  race condition is not intended to be detected by this storage_pool (a
1254       --  now invalid pointer would appear as valid). Instead, we prefer
1255       --  optimum performance for dereferences.
1256
1257       <<Dereference_Label>>
1258
1259       if not Valid then
1260          if Pool.Raise_Exceptions then
1261             raise Accessing_Not_Allocated_Storage;
1262          else
1263             Put ("error: Accessing not allocated storage, at ");
1264             Put_Line (Pool.Stack_Trace_Depth, null,
1265                       Dereference_Label'Address,
1266                       Code_Address_For_Dereference_End);
1267          end if;
1268
1269       else
1270          Header := Header_Of (Storage_Address);
1271
1272          if Header.Block_Size < 0 then
1273             if Pool.Raise_Exceptions then
1274                raise Accessing_Deallocated_Storage;
1275             else
1276                Put ("error: Accessing deallocated storage, at ");
1277                Put_Line
1278                  (Pool.Stack_Trace_Depth, null,
1279                   Dereference_Label'Address,
1280                   Code_Address_For_Dereference_End);
1281                Put ("  First deallocation at ");
1282                Put_Line (0, To_Traceback (Header.Dealloc_Traceback).Traceback);
1283                Put ("  Initial allocation at ");
1284                Put_Line (0, Header.Alloc_Traceback.Traceback);
1285             end if;
1286          end if;
1287       end if;
1288    end Dereference;
1289
1290    ---------------------
1291    -- Dereference_End --
1292    ---------------------
1293
1294    --  DO NOT MOVE: this must be right after Dereference
1295    --  See Allocate_End
1296
1297    procedure Dereference_End is
1298    begin
1299       <<Dereference_End_Label>>
1300       Code_Address_For_Dereference_End := Dereference_End_Label'Address;
1301    end Dereference_End;
1302
1303    ----------------
1304    -- Print_Info --
1305    ----------------
1306
1307    procedure Print_Info
1308      (Pool          : Debug_Pool;
1309       Cumulate      : Boolean := False;
1310       Display_Slots : Boolean := False;
1311       Display_Leaks : Boolean := False)
1312    is
1313
1314       package Backtrace_Htable_Cumulate is new GNAT.HTable.Static_HTable
1315         (Header_Num => Header,
1316          Element    => Traceback_Htable_Elem,
1317          Elmt_Ptr   => Traceback_Htable_Elem_Ptr,
1318          Null_Ptr   => null,
1319          Set_Next   => Set_Next,
1320          Next       => Next,
1321          Key        => Tracebacks_Array_Access,
1322          Get_Key    => Get_Key,
1323          Hash       => Hash,
1324          Equal      => Equal);
1325       --  This needs a comment ??? probably some of the ones below do too???
1326
1327       Data    : Traceback_Htable_Elem_Ptr;
1328       Elem    : Traceback_Htable_Elem_Ptr;
1329       Current : System.Address;
1330       Header  : Allocation_Header_Access;
1331       K       : Traceback_Kind;
1332
1333    begin
1334       Put_Line
1335         ("Total allocated bytes : " &
1336          Byte_Count'Image (Pool.Allocated));
1337
1338       Put_Line
1339         ("Total logically deallocated bytes : " &
1340          Byte_Count'Image (Pool.Logically_Deallocated));
1341
1342       Put_Line
1343         ("Total physically deallocated bytes : " &
1344          Byte_Count'Image (Pool.Physically_Deallocated));
1345
1346       if Pool.Marked_Blocks_Deallocated then
1347          Put_Line ("Marked blocks were physically deallocated. This is");
1348          Put_Line ("potentially dangereous, and you might want to run");
1349          Put_Line ("again with a lower value of Minimum_To_Free");
1350       end if;
1351
1352       Put_Line
1353         ("Current Water Mark: " &
1354          Byte_Count'Image
1355           (Pool.Allocated - Pool.Logically_Deallocated
1356                                    - Pool.Physically_Deallocated));
1357
1358       Put_Line
1359         ("High Water Mark: " &
1360           Byte_Count'Image (Pool.High_Water));
1361
1362       Put_Line ("");
1363
1364       if Display_Slots then
1365          Data := Backtrace_Htable.Get_First;
1366          while Data /= null loop
1367             if Data.Kind in Alloc .. Dealloc then
1368                Elem :=
1369                  new Traceback_Htable_Elem'
1370                       (Traceback => new Tracebacks_Array'(Data.Traceback.all),
1371                        Count     => Data.Count,
1372                        Kind      => Data.Kind,
1373                        Total     => Data.Total,
1374                        Next      => null);
1375                Backtrace_Htable_Cumulate.Set (Elem);
1376
1377                if Cumulate then
1378                   if Data.Kind = Alloc then
1379                      K := Indirect_Alloc;
1380                   else
1381                      K := Indirect_Dealloc;
1382                   end if;
1383
1384                   --  Propagate the direct call to all its parents
1385
1386                   for T in Data.Traceback'First + 1 .. Data.Traceback'Last loop
1387                      Elem := Backtrace_Htable_Cumulate.Get
1388                        (Data.Traceback
1389                           (T .. Data.Traceback'Last)'Unrestricted_Access);
1390
1391                      --  If not, insert it
1392
1393                      if Elem = null then
1394                         Elem := new Traceback_Htable_Elem'
1395                           (Traceback => new Tracebacks_Array'
1396                              (Data.Traceback (T .. Data.Traceback'Last)),
1397                            Count     => Data.Count,
1398                            Kind      => K,
1399                            Total     => Data.Total,
1400                            Next      => null);
1401                         Backtrace_Htable_Cumulate.Set (Elem);
1402
1403                         --  Properly take into account that the subprograms
1404                         --  indirectly called might be doing either allocations
1405                         --  or deallocations. This needs to be reflected in the
1406                         --  counts.
1407
1408                      else
1409                         Elem.Count := Elem.Count + Data.Count;
1410
1411                         if K = Elem.Kind then
1412                            Elem.Total := Elem.Total + Data.Total;
1413
1414                         elsif Elem.Total > Data.Total then
1415                            Elem.Total := Elem.Total - Data.Total;
1416
1417                         else
1418                            Elem.Kind  := K;
1419                            Elem.Total := Data.Total - Elem.Total;
1420                         end if;
1421                      end if;
1422                   end loop;
1423                end if;
1424
1425                Data := Backtrace_Htable.Get_Next;
1426             end if;
1427          end loop;
1428
1429          Put_Line ("List of allocations/deallocations: ");
1430
1431          Data := Backtrace_Htable_Cumulate.Get_First;
1432          while Data /= null loop
1433             case Data.Kind is
1434                when Alloc            => Put ("alloc (count:");
1435                when Indirect_Alloc   => Put ("indirect alloc (count:");
1436                when Dealloc          => Put ("free  (count:");
1437                when Indirect_Dealloc => Put ("indirect free  (count:");
1438             end case;
1439
1440             Put (Natural'Image (Data.Count) & ", total:" &
1441                  Byte_Count'Image (Data.Total) & ") ");
1442
1443             for T in Data.Traceback'Range loop
1444                Put ("0x" & Address_Image (PC_For (Data.Traceback (T))) & ' ');
1445             end loop;
1446
1447             Put_Line ("");
1448
1449             Data := Backtrace_Htable_Cumulate.Get_Next;
1450          end loop;
1451
1452          Backtrace_Htable_Cumulate.Reset;
1453       end if;
1454
1455       if Display_Leaks then
1456          Put_Line ("");
1457          Put_Line ("List of not deallocated blocks:");
1458
1459          --  Do not try to group the blocks with the same stack traces
1460          --  together. This is done by the gnatmem output.
1461
1462          Current := Pool.First_Used_Block;
1463          while Current /= System.Null_Address loop
1464             Header := Header_Of (Current);
1465
1466             Put ("Size: " & Storage_Count'Image (Header.Block_Size) & " at: ");
1467
1468             for T in Header.Alloc_Traceback.Traceback'Range loop
1469                Put ("0x" & Address_Image
1470                       (PC_For (Header.Alloc_Traceback.Traceback (T))) & ' ');
1471             end loop;
1472
1473             Put_Line ("");
1474             Current := Header.Next;
1475          end loop;
1476       end if;
1477    end Print_Info;
1478
1479    ------------------
1480    -- Storage_Size --
1481    ------------------
1482
1483    function Storage_Size (Pool : Debug_Pool) return Storage_Count is
1484       pragma Unreferenced (Pool);
1485    begin
1486       return Storage_Count'Last;
1487    end Storage_Size;
1488
1489    ---------------
1490    -- Configure --
1491    ---------------
1492
1493    procedure Configure
1494      (Pool                           : in out Debug_Pool;
1495       Stack_Trace_Depth              : Natural := Default_Stack_Trace_Depth;
1496       Maximum_Logically_Freed_Memory : SSC     := Default_Max_Freed;
1497       Minimum_To_Free                : SSC     := Default_Min_Freed;
1498       Reset_Content_On_Free          : Boolean := Default_Reset_Content;
1499       Raise_Exceptions               : Boolean := Default_Raise_Exceptions;
1500       Advanced_Scanning              : Boolean := Default_Advanced_Scanning)
1501    is
1502    begin
1503       Pool.Stack_Trace_Depth              := Stack_Trace_Depth;
1504       Pool.Maximum_Logically_Freed_Memory := Maximum_Logically_Freed_Memory;
1505       Pool.Reset_Content_On_Free          := Reset_Content_On_Free;
1506       Pool.Raise_Exceptions               := Raise_Exceptions;
1507       Pool.Minimum_To_Free                := Minimum_To_Free;
1508       Pool.Advanced_Scanning              := Advanced_Scanning;
1509    end Configure;
1510
1511    ----------------
1512    -- Print_Pool --
1513    ----------------
1514
1515    procedure Print_Pool (A : System.Address) is
1516       Storage : constant Address := A;
1517       Valid   : constant Boolean := Is_Valid (Storage);
1518       Header  : Allocation_Header_Access;
1519
1520    begin
1521       --  We might get Null_Address if the call from gdb was done
1522       --  incorrectly. For instance, doing a "print_pool(my_var)" passes 0x0,
1523       --  instead of passing the value of my_var
1524
1525       if A = System.Null_Address then
1526          Put_Line ("Memory not under control of the storage pool");
1527          return;
1528       end if;
1529
1530       if not Valid then
1531          Put_Line ("Memory not under control of the storage pool");
1532
1533       else
1534          Header := Header_Of (Storage);
1535          Put_Line ("0x" & Address_Image (A)
1536                      & " allocated at:");
1537          Put_Line (0, Header.Alloc_Traceback.Traceback);
1538
1539          if To_Traceback (Header.Dealloc_Traceback) /= null then
1540             Put_Line ("0x" & Address_Image (A)
1541                       & " logically freed memory, deallocated at:");
1542             Put_Line (0, To_Traceback (Header.Dealloc_Traceback).Traceback);
1543          end if;
1544       end if;
1545    end Print_Pool;
1546
1547    -----------------------
1548    -- Print_Info_Stdout --
1549    -----------------------
1550
1551    procedure Print_Info_Stdout
1552      (Pool          : Debug_Pool;
1553       Cumulate      : Boolean := False;
1554       Display_Slots : Boolean := False;
1555       Display_Leaks : Boolean := False)
1556    is
1557       procedure Internal is new Print_Info
1558         (Put_Line => GNAT.IO.Put_Line,
1559          Put      => GNAT.IO.Put);
1560    begin
1561       Internal (Pool, Cumulate, Display_Slots, Display_Leaks);
1562    end Print_Info_Stdout;
1563
1564    ------------------
1565    -- Dump_Gnatmem --
1566    ------------------
1567
1568    procedure Dump_Gnatmem (Pool : Debug_Pool; File_Name : String) is
1569       type File_Ptr is new System.Address;
1570
1571       function fopen (Path : String; Mode : String) return File_Ptr;
1572       pragma Import (C, fopen);
1573
1574       procedure fwrite
1575         (Ptr    : System.Address;
1576          Size   : size_t;
1577          Nmemb  : size_t;
1578          Stream : File_Ptr);
1579
1580       procedure fwrite
1581         (Str    : String;
1582          Size   : size_t;
1583          Nmemb  : size_t;
1584          Stream : File_Ptr);
1585       pragma Import (C, fwrite);
1586
1587       procedure fputc (C : Integer; Stream : File_Ptr);
1588       pragma Import (C, fputc);
1589
1590       procedure fclose (Stream : File_Ptr);
1591       pragma Import (C, fclose);
1592
1593       Address_Size : constant size_t :=
1594                        System.Address'Max_Size_In_Storage_Elements;
1595       --  Size in bytes of a pointer
1596
1597       File        : File_Ptr;
1598       Current     : System.Address;
1599       Header      : Allocation_Header_Access;
1600       Actual_Size : size_t;
1601       Num_Calls   : Integer;
1602       Tracebk     : Tracebacks_Array_Access;
1603
1604    begin
1605       File := fopen (File_Name & ASCII.NUL, "wb" & ASCII.NUL);
1606       fwrite ("GMEM DUMP" & ASCII.LF, 10, 1, File);
1607
1608       --  List of not deallocated blocks (see Print_Info)
1609
1610       Current := Pool.First_Used_Block;
1611       while Current /= System.Null_Address loop
1612          Header := Header_Of (Current);
1613
1614          Actual_Size := size_t (Header.Block_Size);
1615          Tracebk := Header.Alloc_Traceback.Traceback;
1616          Num_Calls := Tracebk'Length;
1617
1618          --  (Code taken from memtrack.adb in GNAT's sources)
1619
1620          --  Logs allocation call using the format:
1621
1622          --   'A' <mem addr> <size chunk> <len backtrace> <addr1> ... <addrn>
1623
1624          fputc (Character'Pos ('A'), File);
1625          fwrite (Current'Address, Address_Size, 1, File);
1626          fwrite (Actual_Size'Address, size_t'Max_Size_In_Storage_Elements, 1,
1627                  File);
1628          fwrite (Num_Calls'Address, Integer'Max_Size_In_Storage_Elements, 1,
1629                  File);
1630
1631          for J in Tracebk'First .. Tracebk'First + Num_Calls - 1 loop
1632             declare
1633                Ptr : System.Address := PC_For (Tracebk (J));
1634             begin
1635                fwrite (Ptr'Address, Address_Size, 1, File);
1636             end;
1637          end loop;
1638
1639          Current := Header.Next;
1640       end loop;
1641
1642       fclose (File);
1643    end Dump_Gnatmem;
1644
1645 begin
1646    Allocate_End;
1647    Deallocate_End;
1648    Dereference_End;
1649 end GNAT.Debug_Pools;