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