1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
5 -- G N A T . D E B U G _ P O O L S --
9 -- Copyright (C) 1992-2006, Free Software Foundation, Inc. --
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. --
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. --
29 -- GNAT was originally developed by the GNAT team at New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc. --
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
;
44 with GNAT
.Traceback
; use GNAT
.Traceback
;
46 with Ada
.Unchecked_Conversion
;
48 package body GNAT
.Debug_Pools
is
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.
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
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.
72 ---------------------------
73 -- Back Trace Hash Table --
74 ---------------------------
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.
81 -- This hash-table will remain empty if the discriminant Stack_Trace_Depth
82 -- for the pools is set to 0.
84 -- This table is a global table, that can be shared among all debug pools
87 type Header
is range 1 .. 1023;
88 -- Number of elements in the hash-table
90 type Tracebacks_Array_Access
91 is access GNAT
.Traceback
.Tracebacks_Array
;
93 type Traceback_Kind
is (Alloc
, Dealloc
, Indirect_Alloc
, Indirect_Dealloc
);
95 type Traceback_Htable_Elem
;
96 type Traceback_Htable_Elem_Ptr
97 is access Traceback_Htable_Elem
;
99 type Traceback_Htable_Elem
is record
100 Traceback
: Tracebacks_Array_Access
;
101 Kind
: Traceback_Kind
;
104 Next
: Traceback_Htable_Elem_Ptr
;
107 -- Subprograms used for the Backtrace_Htable instantiation
110 (E
: Traceback_Htable_Elem_Ptr
;
111 Next
: Traceback_Htable_Elem_Ptr
);
112 pragma Inline
(Set_Next
);
115 (E
: Traceback_Htable_Elem_Ptr
) return Traceback_Htable_Elem_Ptr
;
116 pragma Inline
(Next
);
119 (E
: Traceback_Htable_Elem_Ptr
) return Tracebacks_Array_Access
;
120 pragma Inline
(Get_Key
);
122 function Hash
(T
: Tracebacks_Array_Access
) return Header
;
123 pragma Inline
(Hash
);
125 function Equal
(K1
, K2
: Tracebacks_Array_Access
) return Boolean;
126 -- Why is this not inlined???
128 -- The hash table for back traces
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
,
135 Set_Next
=> Set_Next
,
137 Key
=> Tracebacks_Array_Access
,
142 -----------------------
143 -- Allocations table --
144 -----------------------
146 type Allocation_Header
;
147 type Allocation_Header_Access
is access Allocation_Header
;
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.
153 -- The following record stores extra information that needs to be
154 -- memorized for each block allocated with the special debug pool.
156 type Allocation_Header
is record
157 Allocation_Address
: System
.Address
;
158 -- Address of the block returned by malloc, possibly unaligned
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.
166 Alloc_Traceback
: Traceback_Htable_Elem_Ptr
;
167 -- ??? comment required
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.
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
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.
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
190 function To_Address
is new Ada
.Unchecked_Conversion
191 (Traceback_Ptr_Or_Address
, System
.Address
);
193 function To_Address
is new Ada
.Unchecked_Conversion
194 (System
.Address
, Traceback_Ptr_Or_Address
);
196 function To_Traceback
is new Ada
.Unchecked_Conversion
197 (Traceback_Ptr_Or_Address
, Traceback_Htable_Elem_Ptr
);
199 function To_Traceback
is new Ada
.Unchecked_Conversion
200 (Traceback_Htable_Elem_Ptr
, Traceback_Ptr_Or_Address
);
202 Header_Offset
: constant Storage_Count
:=
204 ((Allocation_Header
'Size / System
.Storage_Unit
205 + Default_Alignment
- 1) / Default_Alignment
);
206 -- Offset of user data after allocation header
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.
213 -----------------------
214 -- Allocations table --
215 -----------------------
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.
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
226 type Byte
is mod 2 ** System
.Storage_Unit
;
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).
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).
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)
243 -- ((Address - Edata'Address) / Default_Alignment)
247 -- See the subprograms Is_Valid and Set_Valid for proper manipulation of
250 type Table_Ptr
is access Big_Table
;
251 function To_Pointer
is new Ada
.Unchecked_Conversion
252 (System
.Address
, Table_Ptr
);
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).
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.
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.
271 -----------------------
272 -- Local subprograms --
273 -----------------------
275 function Find_Or_Create_Traceback
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
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
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
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.
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.
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.
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.
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
336 procedure Skip_Levels
338 Trace
: Tracebacks_Array
;
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
353 function Header_Of
(Address
: System
.Address
)
354 return Allocation_Header_Access
356 function Convert
is new Ada
.Unchecked_Conversion
357 (System
.Address
, Allocation_Header_Access
);
359 return Convert
(Address
- Header_Offset
);
367 (E
: Traceback_Htable_Elem_Ptr
;
368 Next
: Traceback_Htable_Elem_Ptr
)
379 (E
: Traceback_Htable_Elem_Ptr
) return Traceback_Htable_Elem_Ptr
is
388 function Equal
(K1
, K2
: Tracebacks_Array_Access
) return Boolean is
389 use Ada
.Exceptions
.Traceback
;
391 return K1
.all = K2
.all;
399 (E
: Traceback_Htable_Elem_Ptr
) return Tracebacks_Array_Access
409 function Hash
(T
: Tracebacks_Array_Access
) return Header
is
410 Result
: Integer_Address
:= 0;
413 for X
in T
'Range loop
414 Result
:= Result
+ To_Integer
(PC_For
(T
(X
)));
417 return Header
(1 + Result
mod Integer_Address
(Header
'Last));
426 Traceback
: Tracebacks_Array_Access
;
427 Ignored_Frame_Start
: System
.Address
:= System
.Null_Address
;
428 Ignored_Frame_End
: System
.Address
:= System
.Null_Address
)
430 procedure Print
(Tr
: Tracebacks_Array
);
431 -- Print the traceback to standard_output
437 procedure Print
(Tr
: Tracebacks_Array
) is
439 for J
in Tr
'Range loop
440 Put
("0x" & Address_Image
(PC_For
(Tr
(J
))) & ' ');
445 -- Start of processing for Put_Line
448 if Traceback
= null then
450 Tr
: aliased Tracebacks_Array
(1 .. Depth
+ Max_Ignored_Levels
);
451 Start
, Len
: Natural;
454 Call_Chain
(Tr
, Len
);
455 Skip_Levels
(Depth
, Tr
, Start
, Len
,
456 Ignored_Frame_Start
, Ignored_Frame_End
);
457 Print
(Tr
(Start
.. Len
));
461 Print
(Traceback
.all);
469 procedure Skip_Levels
471 Trace
: Tracebacks_Array
;
473 Len
: in out Natural;
474 Ignored_Frame_Start
: System
.Address
;
475 Ignored_Frame_End
: System
.Address
)
478 Start
:= Trace
'First;
481 and then (PC_For
(Trace
(Start
)) < Ignored_Frame_Start
482 or else PC_For
(Trace
(Start
)) > Ignored_Frame_End
)
489 -- Just in case: make sure we have a traceback even if Ignore_Till
496 if Len
- Start
+ 1 > Depth
then
497 Len
:= Depth
+ Start
- 1;
501 ------------------------------
502 -- Find_Or_Create_Traceback --
503 ------------------------------
505 function Find_Or_Create_Traceback
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
513 if Pool
.Stack_Trace_Depth
= 0 then
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
;
524 Call_Chain
(Trace
, Len
);
525 Skip_Levels
(Pool
.Stack_Trace_Depth
, Trace
, Start
, Len
,
526 Ignored_Frame_Start
, Ignored_Frame_End
);
528 -- Check if the traceback is already in the table
531 Backtrace_Htable
.Get
(Trace
(Start
.. Len
)'Unrestricted_Access);
536 Elem
:= new Traceback_Htable_Elem
'
537 (Traceback => new Tracebacks_Array'(Trace
(Start
.. Len
)),
540 Total
=> Byte_Count
(Size
),
542 Backtrace_Htable
.Set
(Elem
);
545 Elem
.Count
:= Elem
.Count
+ 1;
546 Elem
.Total
:= Elem
.Total
+ Byte_Count
(Size
);
551 end Find_Or_Create_Traceback
;
557 function Is_Valid
(Storage
: System
.Address
) return Boolean is
559 -- We use the following constant declaration, instead of
560 -- Offset : constant Storage_Offset :=
561 -- (Storage - Edata) / Default_Alignment;
562 -- See comments in Set_Valid for details.
564 Offset
: constant Storage_Offset
:=
565 Storage_Offset
((To_Integer
(Storage
) - To_Integer
(Edata
)) /
568 Bit
: constant Byte
:= 2 ** Natural (Offset
mod System
.Storage_Unit
);
571 return (Storage
mod Default_Alignment
) = 0
573 and then Offset
< Valid_Blocks_Size
* Storage_Unit
574 and then (Valid_Blocks
(Offset
/ Storage_Unit
) and Bit
) /= 0;
581 procedure Set_Valid
(Storage
: System
.Address
; Value
: Boolean) is
582 Offset
: Storage_Offset
;
584 Bytes
: Storage_Offset
;
585 Tmp
: constant Table_Ptr
:= Valid_Blocks
;
587 Edata_Align
: constant Storage_Offset
:=
588 Default_Alignment
* Storage_Unit
;
590 procedure Memset
(A
: Address
; C
: Integer; N
: size_t
);
591 pragma Import
(C
, Memset
, "memset");
593 procedure Memmove
(Dest
, Src
: Address
; N
: size_t
);
594 pragma Import
(C
, Memmove
, "memmove");
597 -- Allocate, or reallocate, the valid blocks table as needed. We start
598 -- with a size big enough to handle Initial_Memory_Size bytes of memory,
599 -- to avoid too many reallocations. The table will typically be around
600 -- 16Mb in that case, which is still small enough.
602 if Valid_Blocks_Size
= 0 then
603 Valid_Blocks_Size
:= (Initial_Memory_Size
/ Default_Alignment
)
605 Valid_Blocks
:= To_Pointer
(Alloc
(size_t
(Valid_Blocks_Size
)));
608 -- Reset the memory using memset, which is much faster than the
609 -- standard Ada code with "when others"
611 Memset
(Valid_Blocks
.all'Address, 0, size_t
(Valid_Blocks_Size
));
614 -- First case : the new address is outside of the current scope of
615 -- Valid_Blocks, before the current start address. We need to reallocate
616 -- the table accordingly. This should be a rare occurence, since in most
617 -- cases, the first allocation will also have the lowest address. But
618 -- there is no garantee...
620 if Storage
< Edata
then
622 -- The difference between the new Edata and the current one must be
623 -- a multiple of Default_Alignment * Storage_Unit, so that the bit
624 -- representing an address in Valid_Blocks are kept the same.
626 Offset
:= ((Edata
- Storage
) / Edata_Align
+ 1) * Edata_Align
;
627 Offset
:= Offset
/ Default_Alignment
;
628 Bytes
:= Offset
/ Storage_Unit
;
630 To_Pointer
(Alloc
(Size
=> size_t
(Valid_Blocks_Size
+ Bytes
)));
631 Memmove
(Dest
=> Valid_Blocks
.all'Address + Bytes
,
632 Src
=> Tmp
.all'Address,
633 N
=> size_t
(Valid_Blocks_Size
));
634 Memset
(A
=> Valid_Blocks
.all'Address,
636 N
=> size_t
(Bytes
));
637 Free
(Tmp
.all'Address);
638 Valid_Blocks_Size
:= Valid_Blocks_Size
+ Bytes
;
640 -- Take into the account the new start address
642 Edata
:= Storage
- Edata_Align
+ (Edata
- Storage
) mod Edata_Align
;
645 -- Second case : the new address is outside of the current scope of
646 -- Valid_Blocks, so we have to grow the table as appropriate.
648 -- Note: it might seem more natural for the following statement to
651 -- Offset := (Storage - Edata) / Default_Alignment;
653 -- but that won't work since Storage_Offset is signed, and it is
654 -- possible to subtract a small address from a large address and
655 -- get a negative value. This may seem strange, but it is quite
656 -- specifically allowed in the RM, and is what most implementations
657 -- including GNAT actually do. Hence the conversion to Integer_Address
658 -- which is a full range modular type, not subject to this glitch.
660 Offset
:= Storage_Offset
((To_Integer
(Storage
) - To_Integer
(Edata
)) /
663 if Offset
>= Valid_Blocks_Size
* System
.Storage_Unit
then
664 Bytes
:= Valid_Blocks_Size
;
667 exit when Offset
<= Bytes
* System
.Storage_Unit
;
670 Valid_Blocks
:= To_Pointer
671 (Realloc
(Ptr
=> Valid_Blocks
.all'Address,
672 Size
=> size_t
(Bytes
)));
674 (Valid_Blocks
.all'Address + Valid_Blocks_Size
,
676 size_t
(Bytes
- Valid_Blocks_Size
));
677 Valid_Blocks_Size
:= Bytes
;
680 Bit
:= 2 ** Natural (Offset
mod System
.Storage_Unit
);
681 Bytes
:= Offset
/ Storage_Unit
;
683 -- Then set the value as valid
686 Valid_Blocks
(Bytes
) := Valid_Blocks
(Bytes
) or Bit
;
688 Valid_Blocks
(Bytes
) := Valid_Blocks
(Bytes
) and (not Bit
);
697 (Pool
: in out Debug_Pool
;
698 Storage_Address
: out Address
;
699 Size_In_Storage_Elements
: Storage_Count
;
700 Alignment
: Storage_Count
)
702 pragma Unreferenced
(Alignment
);
703 -- Ignored, we always force 'Default_Alignment
705 type Local_Storage_Array
is new Storage_Array
706 (1 .. Size_In_Storage_Elements
+ Minimum_Allocation
);
708 type Ptr
is access Local_Storage_Array
;
709 -- On some systems, we might want to physically protect pages
710 -- against writing when they have been freed (of course, this is
711 -- expensive in terms of wasted memory). To do that, all we should
712 -- have to do it to set the size of this array to the page size.
717 Current
: Byte_Count
;
718 Trace
: Traceback_Htable_Elem_Ptr
;
724 -- If necessary, start physically releasing memory. The reason this is
725 -- done here, although Pool.Logically_Deallocated has not changed above,
726 -- is so that we do this only after a series of deallocations (e.g a
727 -- loop that deallocates a big array). If we were doing that in
728 -- Deallocate, we might be physically freeing memory several times
729 -- during the loop, which is expensive.
731 if Pool
.Logically_Deallocated
>
732 Byte_Count
(Pool
.Maximum_Logically_Freed_Memory
)
734 Free_Physically
(Pool
);
737 -- Use standard (ie through malloc) allocations. This automatically
738 -- raises Storage_Error if needed. We also try once more to physically
739 -- release memory, so that even marked blocks, in the advanced scanning,
743 P
:= new Local_Storage_Array
;
746 when Storage_Error
=>
747 Free_Physically
(Pool
);
748 P
:= new Local_Storage_Array
;
752 System
.Null_Address
+ Default_Alignment
753 * (((P
.all'Address + Default_Alignment
- 1) - System
.Null_Address
)
757 pragma Assert
((Storage_Address
- System
.Null_Address
)
758 mod Default_Alignment
= 0);
759 pragma Assert
(Storage_Address
+ Size_In_Storage_Elements
760 <= P
.all'Address + P
'Length);
762 Trace
:= Find_Or_Create_Traceback
763 (Pool
, Alloc
, Size_In_Storage_Elements
,
764 Allocate_Label
'Address, Code_Address_For_Allocate_End
);
766 pragma Warnings
(Off
);
767 -- Turn warning on alignment for convert call off. We know that in
768 -- fact this conversion is safe since P itself is always aligned on
769 -- Default_Alignment.
771 Header_Of
(Storage_Address
).all :=
772 (Allocation_Address
=> P
.all'Address,
773 Alloc_Traceback
=> Trace
,
774 Dealloc_Traceback
=> To_Traceback
(null),
775 Next
=> Pool
.First_Used_Block
,
776 Block_Size
=> Size_In_Storage_Elements
);
778 pragma Warnings
(On
);
780 -- Link this block in the list of used blocks. This will be used to list
781 -- memory leaks in Print_Info, and for the advanced schemes of
782 -- Physical_Free, where we want to traverse all allocated blocks and
783 -- search for possible references.
785 -- We insert in front, since most likely we'll be freeing the most
786 -- recently allocated blocks first (the older one might stay allocated
787 -- for the whole life of the application).
789 if Pool
.First_Used_Block
/= System
.Null_Address
then
790 Header_Of
(Pool
.First_Used_Block
).Dealloc_Traceback
:=
791 To_Address
(Storage_Address
);
794 Pool
.First_Used_Block
:= Storage_Address
;
796 -- Mark the new address as valid
798 Set_Valid
(Storage_Address
, True);
800 -- Update internal data
803 Pool
.Allocated
+ Byte_Count
(Size_In_Storage_Elements
);
805 Current
:= Pool
.Allocated
-
806 Pool
.Logically_Deallocated
-
807 Pool
.Physically_Deallocated
;
809 if Current
> Pool
.High_Water
then
810 Pool
.High_Water
:= Current
;
825 -- DO NOT MOVE, this must be right after Allocate. This is similar to
826 -- what is done in a-except, so that we can hide the traceback frames
827 -- internal to this package
829 procedure Allocate_End
is
831 <<Allocate_End_Label
>>
832 Code_Address_For_Allocate_End
:= Allocate_End_Label
'Address;
839 procedure Set_Dead_Beef
840 (Storage_Address
: System
.Address
;
841 Size_In_Storage_Elements
: Storage_Count
)
843 Dead_Bytes
: constant := 4;
845 type Data
is mod 2 ** (Dead_Bytes
* 8);
846 for Data
'Size use Dead_Bytes
* 8;
848 Dead
: constant Data
:= 16#DEAD_BEEF#
;
850 type Dead_Memory
is array
851 (1 .. Size_In_Storage_Elements
/ Dead_Bytes
) of Data
;
852 type Mem_Ptr
is access Dead_Memory
;
854 type Byte
is mod 2 ** 8;
857 type Dead_Memory_Bytes
is array (0 .. 2) of Byte
;
858 type Dead_Memory_Bytes_Ptr
is access Dead_Memory_Bytes
;
860 function From_Ptr
is new Ada
.Unchecked_Conversion
861 (System
.Address
, Mem_Ptr
);
863 function From_Ptr
is new Ada
.Unchecked_Conversion
864 (System
.Address
, Dead_Memory_Bytes_Ptr
);
866 M
: constant Mem_Ptr
:= From_Ptr
(Storage_Address
);
867 M2
: Dead_Memory_Bytes_Ptr
;
868 Modulo
: constant Storage_Count
:=
869 Size_In_Storage_Elements
mod Dead_Bytes
;
871 M
.all := (others => Dead
);
873 -- Any bytes left (up to three of them)
876 M2
:= From_Ptr
(Storage_Address
+ M
'Length * Dead_Bytes
);
889 ---------------------
890 -- Free_Physically --
891 ---------------------
893 procedure Free_Physically
(Pool
: in out Debug_Pool
) is
894 type Byte
is mod 256;
895 type Byte_Access
is access Byte
;
897 function To_Byte
is new Ada
.Unchecked_Conversion
898 (System
.Address
, Byte_Access
);
900 type Address_Access
is access System
.Address
;
902 function To_Address_Access
is new Ada
.Unchecked_Conversion
903 (System
.Address
, Address_Access
);
905 In_Use_Mark
: constant Byte
:= 16#D#
;
906 Free_Mark
: constant Byte
:= 16#F#
;
908 Total_Freed
: Storage_Count
:= 0;
910 procedure Reset_Marks
;
911 -- Unmark all the logically freed blocks, so that they are considered
912 -- for physical deallocation
915 (H
: Allocation_Header_Access
; A
: System
.Address
; In_Use
: Boolean);
916 -- Mark the user data block starting at A. For a block of size zero,
917 -- nothing is done. For a block with a different size, the first byte
918 -- is set to either "D" (in use) or "F" (free).
920 function Marked
(A
: System
.Address
) return Boolean;
921 -- Return true if the user data block starting at A might be in use
924 procedure Mark_Blocks
;
925 -- Traverse all allocated blocks, and search for possible references
926 -- to logically freed blocks. Mark them appropriately
928 procedure Free_Blocks
(Ignore_Marks
: Boolean);
929 -- Physically release blocks. Only the blocks that haven't been marked
930 -- will be released, unless Ignore_Marks is true.
936 procedure Free_Blocks
(Ignore_Marks
: Boolean) is
937 Header
: Allocation_Header_Access
;
938 Tmp
: System
.Address
:= Pool
.First_Free_Block
;
939 Next
: System
.Address
;
940 Previous
: System
.Address
:= System
.Null_Address
;
943 while Tmp
/= System
.Null_Address
944 and then Total_Freed
< Pool
.Minimum_To_Free
946 Header
:= Header_Of
(Tmp
);
948 -- If we know, or at least assume, the block is no longer
949 -- reference anywhere, we can free it physically.
951 if Ignore_Marks
or else not Marked
(Tmp
) then
954 pragma Suppress
(All_Checks
);
955 -- Suppress the checks on this section. If they are overflow
956 -- errors, it isn't critical, and we'd rather avoid a
957 -- Constraint_Error in that case.
959 -- Note that block_size < zero for freed blocks
961 Pool
.Physically_Deallocated
:=
962 Pool
.Physically_Deallocated
-
963 Byte_Count
(Header
.Block_Size
);
965 Pool
.Logically_Deallocated
:=
966 Pool
.Logically_Deallocated
+
967 Byte_Count
(Header
.Block_Size
);
969 Total_Freed
:= Total_Freed
- Header
.Block_Size
;
973 System
.Memory
.Free
(Header
.Allocation_Address
);
974 Set_Valid
(Tmp
, False);
976 -- Remove this block from the list
978 if Previous
= System
.Null_Address
then
979 Pool
.First_Free_Block
:= Next
;
981 Header_Of
(Previous
).Next
:= Next
;
998 (H
: Allocation_Header_Access
;
1003 if H
.Block_Size
/= 0 then
1005 To_Byte
(A
).all := In_Use_Mark
;
1007 To_Byte
(A
).all := Free_Mark
;
1016 procedure Mark_Blocks
is
1017 Tmp
: System
.Address
:= Pool
.First_Used_Block
;
1018 Previous
: System
.Address
;
1019 Last
: System
.Address
;
1020 Pointed
: System
.Address
;
1021 Header
: Allocation_Header_Access
;
1024 -- For each allocated block, check its contents. Things that look
1025 -- like a possible address are used to mark the blocks so that we try
1026 -- and keep them, for better detection in case of invalid access.
1027 -- This mechanism is far from being fool-proof: it doesn't check the
1028 -- stacks of the threads, doesn't check possible memory allocated not
1029 -- under control of this debug pool. But it should allow us to catch
1032 while Tmp
/= System
.Null_Address
loop
1034 Last
:= Tmp
+ Header_Of
(Tmp
).Block_Size
;
1035 while Previous
< Last
loop
1036 -- ??? Should we move byte-per-byte, or consider that addresses
1037 -- are always aligned on 4-bytes boundaries ? Let's use the
1040 Pointed
:= To_Address_Access
(Previous
).all;
1041 if Is_Valid
(Pointed
) then
1042 Header
:= Header_Of
(Pointed
);
1044 -- Do not even attempt to mark blocks in use. That would
1045 -- screw up the whole application, of course.
1046 if Header
.Block_Size
< 0 then
1047 Mark
(Header
, Pointed
, In_Use
=> True);
1051 Previous
:= Previous
+ System
.Address
'Size;
1054 Tmp
:= Header_Of
(Tmp
).Next
;
1062 function Marked
(A
: System
.Address
) return Boolean is
1064 return To_Byte
(A
).all = In_Use_Mark
;
1071 procedure Reset_Marks
is
1072 Current
: System
.Address
:= Pool
.First_Free_Block
;
1073 Header
: Allocation_Header_Access
;
1075 while Current
/= System
.Null_Address
loop
1076 Header
:= Header_Of
(Current
);
1077 Mark
(Header
, Current
, False);
1078 Current
:= Header
.Next
;
1082 -- Start of processing for Free_Physically
1087 if Pool
.Advanced_Scanning
then
1088 Reset_Marks
; -- Reset the mark for each freed block
1092 Free_Blocks
(Ignore_Marks
=> not Pool
.Advanced_Scanning
);
1094 -- The contract is that we need to free at least Minimum_To_Free bytes,
1095 -- even if this means freeing marked blocks in the advanced scheme
1097 if Total_Freed
< Pool
.Minimum_To_Free
1098 and then Pool
.Advanced_Scanning
1100 Pool
.Marked_Blocks_Deallocated
:= True;
1101 Free_Blocks
(Ignore_Marks
=> True);
1110 end Free_Physically
;
1116 procedure Deallocate
1117 (Pool
: in out Debug_Pool
;
1118 Storage_Address
: Address
;
1119 Size_In_Storage_Elements
: Storage_Count
;
1120 Alignment
: Storage_Count
)
1122 pragma Unreferenced
(Alignment
);
1124 Header
: constant Allocation_Header_Access
:=
1125 Header_Of
(Storage_Address
);
1127 Previous
: System
.Address
;
1130 <<Deallocate_Label
>>
1132 Valid
:= Is_Valid
(Storage_Address
);
1136 if Pool
.Raise_Exceptions
then
1137 raise Freeing_Not_Allocated_Storage
;
1139 Put
("error: Freeing not allocated storage, at ");
1140 Put_Line
(Pool
.Stack_Trace_Depth
, null,
1141 Deallocate_Label
'Address,
1142 Code_Address_For_Deallocate_End
);
1145 elsif Header
.Block_Size
< 0 then
1147 if Pool
.Raise_Exceptions
then
1148 raise Freeing_Deallocated_Storage
;
1150 Put
("error: Freeing already deallocated storage, at ");
1151 Put_Line
(Pool
.Stack_Trace_Depth
, null,
1152 Deallocate_Label
'Address,
1153 Code_Address_For_Deallocate_End
);
1154 Put
(" Memory already deallocated at ");
1155 Put_Line
(0, To_Traceback
(Header
.Dealloc_Traceback
).Traceback
);
1156 Put
(" Memory was allocated at ");
1157 Put_Line
(0, Header
.Alloc_Traceback
.Traceback
);
1161 -- Remove this block from the list of used blocks
1164 To_Address
(Header_Of
(Storage_Address
).Dealloc_Traceback
);
1166 if Previous
= System
.Null_Address
then
1167 Pool
.First_Used_Block
:= Header_Of
(Pool
.First_Used_Block
).Next
;
1169 if Pool
.First_Used_Block
/= System
.Null_Address
then
1170 Header_Of
(Pool
.First_Used_Block
).Dealloc_Traceback
:=
1171 To_Traceback
(null);
1175 Header_Of
(Previous
).Next
:= Header_Of
(Storage_Address
).Next
;
1177 if Header_Of
(Storage_Address
).Next
/= System
.Null_Address
then
1179 (Header_Of
(Storage_Address
).Next
).Dealloc_Traceback
:=
1180 To_Address
(Previous
);
1184 -- Update the header
1187 (Allocation_Address
=> Header
.Allocation_Address
,
1188 Alloc_Traceback
=> Header
.Alloc_Traceback
,
1189 Dealloc_Traceback
=> To_Traceback
1190 (Find_Or_Create_Traceback
1192 Size_In_Storage_Elements
,
1193 Deallocate_Label
'Address,
1194 Code_Address_For_Deallocate_End
)),
1195 Next
=> System
.Null_Address
,
1196 Block_Size
=> -Size_In_Storage_Elements
);
1198 if Pool
.Reset_Content_On_Free
then
1199 Set_Dead_Beef
(Storage_Address
, Size_In_Storage_Elements
);
1202 Pool
.Logically_Deallocated
:=
1203 Pool
.Logically_Deallocated
+
1204 Byte_Count
(Size_In_Storage_Elements
);
1206 -- Link this free block with the others (at the end of the list, so
1207 -- that we can start releasing the older blocks first later on).
1209 if Pool
.First_Free_Block
= System
.Null_Address
then
1210 Pool
.First_Free_Block
:= Storage_Address
;
1211 Pool
.Last_Free_Block
:= Storage_Address
;
1214 Header_Of
(Pool
.Last_Free_Block
).Next
:= Storage_Address
;
1215 Pool
.Last_Free_Block
:= Storage_Address
;
1218 -- Do not physically release the memory here, but in Alloc.
1219 -- See comment there for details.
1230 --------------------
1231 -- Deallocate_End --
1232 --------------------
1234 -- DO NOT MOVE, this must be right after Deallocate
1237 procedure Deallocate_End
is
1239 <<Deallocate_End_Label
>>
1240 Code_Address_For_Deallocate_End
:= Deallocate_End_Label
'Address;
1247 procedure Dereference
1248 (Pool
: in out Debug_Pool
;
1249 Storage_Address
: Address
;
1250 Size_In_Storage_Elements
: Storage_Count
;
1251 Alignment
: Storage_Count
)
1253 pragma Unreferenced
(Alignment
, Size_In_Storage_Elements
);
1255 Valid
: constant Boolean := Is_Valid
(Storage_Address
);
1256 Header
: Allocation_Header_Access
;
1259 -- Locking policy: we do not do any locking in this procedure. The
1260 -- tables are only read, not written to, and although a problem might
1261 -- appear if someone else is modifying the tables at the same time, this
1262 -- race condition is not intended to be detected by this storage_pool (a
1263 -- now invalid pointer would appear as valid). Instead, we prefer
1264 -- optimum performance for dereferences.
1266 <<Dereference_Label
>>
1269 if Pool
.Raise_Exceptions
then
1270 raise Accessing_Not_Allocated_Storage
;
1272 Put
("error: Accessing not allocated storage, at ");
1273 Put_Line
(Pool
.Stack_Trace_Depth
, null,
1274 Dereference_Label
'Address,
1275 Code_Address_For_Dereference_End
);
1279 Header
:= Header_Of
(Storage_Address
);
1281 if Header
.Block_Size
< 0 then
1282 if Pool
.Raise_Exceptions
then
1283 raise Accessing_Deallocated_Storage
;
1285 Put
("error: Accessing deallocated storage, at ");
1287 (Pool
.Stack_Trace_Depth
, null,
1288 Dereference_Label
'Address,
1289 Code_Address_For_Dereference_End
);
1290 Put
(" First deallocation at ");
1291 Put_Line
(0, To_Traceback
(Header
.Dealloc_Traceback
).Traceback
);
1292 Put
(" Initial allocation at ");
1293 Put_Line
(0, Header
.Alloc_Traceback
.Traceback
);
1299 ---------------------
1300 -- Dereference_End --
1301 ---------------------
1303 -- DO NOT MOVE: this must be right after Dereference
1306 procedure Dereference_End
is
1308 <<Dereference_End_Label
>>
1309 Code_Address_For_Dereference_End
:= Dereference_End_Label
'Address;
1310 end Dereference_End
;
1316 procedure Print_Info
1318 Cumulate
: Boolean := False;
1319 Display_Slots
: Boolean := False;
1320 Display_Leaks
: Boolean := False)
1323 package Backtrace_Htable_Cumulate
is new GNAT
.HTable
.Static_HTable
1324 (Header_Num
=> Header
,
1325 Element
=> Traceback_Htable_Elem
,
1326 Elmt_Ptr
=> Traceback_Htable_Elem_Ptr
,
1328 Set_Next
=> Set_Next
,
1330 Key
=> Tracebacks_Array_Access
,
1334 -- This needs a comment ??? probably some of the ones below do too???
1336 Data
: Traceback_Htable_Elem_Ptr
;
1337 Elem
: Traceback_Htable_Elem_Ptr
;
1338 Current
: System
.Address
;
1339 Header
: Allocation_Header_Access
;
1344 ("Total allocated bytes : " &
1345 Byte_Count
'Image (Pool
.Allocated
));
1348 ("Total logically deallocated bytes : " &
1349 Byte_Count
'Image (Pool
.Logically_Deallocated
));
1352 ("Total physically deallocated bytes : " &
1353 Byte_Count
'Image (Pool
.Physically_Deallocated
));
1355 if Pool
.Marked_Blocks_Deallocated
then
1356 Put_Line
("Marked blocks were physically deallocated. This is");
1357 Put_Line
("potentially dangereous, and you might want to run");
1358 Put_Line
("again with a lower value of Minimum_To_Free");
1362 ("Current Water Mark: " &
1364 (Pool
.Allocated
- Pool
.Logically_Deallocated
1365 - Pool
.Physically_Deallocated
));
1368 ("High Water Mark: " &
1369 Byte_Count
'Image (Pool
.High_Water
));
1373 if Display_Slots
then
1374 Data
:= Backtrace_Htable
.Get_First
;
1375 while Data
/= null loop
1376 if Data
.Kind
in Alloc
.. Dealloc
then
1378 new Traceback_Htable_Elem
'
1379 (Traceback => new Tracebacks_Array'(Data
.Traceback
.all),
1380 Count
=> Data
.Count
,
1382 Total
=> Data
.Total
,
1384 Backtrace_Htable_Cumulate
.Set
(Elem
);
1387 if Data
.Kind
= Alloc
then
1388 K
:= Indirect_Alloc
;
1390 K
:= Indirect_Dealloc
;
1393 -- Propagate the direct call to all its parents
1395 for T
in Data
.Traceback
'First + 1 .. Data
.Traceback
'Last loop
1396 Elem
:= Backtrace_Htable_Cumulate
.Get
1398 (T
.. Data
.Traceback
'Last)'Unrestricted_Access);
1400 -- If not, insert it
1403 Elem
:= new Traceback_Htable_Elem
'
1404 (Traceback => new Tracebacks_Array'
1405 (Data
.Traceback
(T
.. Data
.Traceback
'Last)),
1406 Count
=> Data
.Count
,
1408 Total
=> Data
.Total
,
1410 Backtrace_Htable_Cumulate
.Set
(Elem
);
1412 -- Properly take into account that the subprograms
1413 -- indirectly called might be doing either allocations
1414 -- or deallocations. This needs to be reflected in the
1418 Elem
.Count
:= Elem
.Count
+ Data
.Count
;
1420 if K
= Elem
.Kind
then
1421 Elem
.Total
:= Elem
.Total
+ Data
.Total
;
1423 elsif Elem
.Total
> Data
.Total
then
1424 Elem
.Total
:= Elem
.Total
- Data
.Total
;
1428 Elem
.Total
:= Data
.Total
- Elem
.Total
;
1434 Data
:= Backtrace_Htable
.Get_Next
;
1438 Put_Line
("List of allocations/deallocations: ");
1440 Data
:= Backtrace_Htable_Cumulate
.Get_First
;
1441 while Data
/= null loop
1443 when Alloc
=> Put
("alloc (count:");
1444 when Indirect_Alloc
=> Put
("indirect alloc (count:");
1445 when Dealloc
=> Put
("free (count:");
1446 when Indirect_Dealloc
=> Put
("indirect free (count:");
1449 Put
(Natural'Image (Data
.Count
) & ", total:" &
1450 Byte_Count
'Image (Data
.Total
) & ") ");
1452 for T
in Data
.Traceback
'Range loop
1453 Put
("0x" & Address_Image
(PC_For
(Data
.Traceback
(T
))) & ' ');
1458 Data
:= Backtrace_Htable_Cumulate
.Get_Next
;
1461 Backtrace_Htable_Cumulate
.Reset
;
1464 if Display_Leaks
then
1466 Put_Line
("List of not deallocated blocks:");
1468 -- Do not try to group the blocks with the same stack traces
1469 -- together. This is done by the gnatmem output.
1471 Current
:= Pool
.First_Used_Block
;
1472 while Current
/= System
.Null_Address
loop
1473 Header
:= Header_Of
(Current
);
1475 Put
("Size: " & Storage_Count
'Image (Header
.Block_Size
) & " at: ");
1477 for T
in Header
.Alloc_Traceback
.Traceback
'Range loop
1478 Put
("0x" & Address_Image
1479 (PC_For
(Header
.Alloc_Traceback
.Traceback
(T
))) & ' ');
1483 Current
:= Header
.Next
;
1492 function Storage_Size
(Pool
: Debug_Pool
) return Storage_Count
is
1493 pragma Unreferenced
(Pool
);
1495 return Storage_Count
'Last;
1503 (Pool
: in out Debug_Pool
;
1504 Stack_Trace_Depth
: Natural := Default_Stack_Trace_Depth
;
1505 Maximum_Logically_Freed_Memory
: SSC
:= Default_Max_Freed
;
1506 Minimum_To_Free
: SSC
:= Default_Min_Freed
;
1507 Reset_Content_On_Free
: Boolean := Default_Reset_Content
;
1508 Raise_Exceptions
: Boolean := Default_Raise_Exceptions
;
1509 Advanced_Scanning
: Boolean := Default_Advanced_Scanning
)
1512 Pool
.Stack_Trace_Depth
:= Stack_Trace_Depth
;
1513 Pool
.Maximum_Logically_Freed_Memory
:= Maximum_Logically_Freed_Memory
;
1514 Pool
.Reset_Content_On_Free
:= Reset_Content_On_Free
;
1515 Pool
.Raise_Exceptions
:= Raise_Exceptions
;
1516 Pool
.Minimum_To_Free
:= Minimum_To_Free
;
1517 Pool
.Advanced_Scanning
:= Advanced_Scanning
;
1524 procedure Print_Pool
(A
: System
.Address
) is
1525 Storage
: constant Address
:= A
;
1526 Valid
: constant Boolean := Is_Valid
(Storage
);
1527 Header
: Allocation_Header_Access
;
1530 -- We might get Null_Address if the call from gdb was done
1531 -- incorrectly. For instance, doing a "print_pool(my_var)" passes 0x0,
1532 -- instead of passing the value of my_var
1534 if A
= System
.Null_Address
then
1535 Put_Line
("Memory not under control of the storage pool");
1540 Put_Line
("Memory not under control of the storage pool");
1543 Header
:= Header_Of
(Storage
);
1544 Put_Line
("0x" & Address_Image
(A
)
1545 & " allocated at:");
1546 Put_Line
(0, Header
.Alloc_Traceback
.Traceback
);
1548 if To_Traceback
(Header
.Dealloc_Traceback
) /= null then
1549 Put_Line
("0x" & Address_Image
(A
)
1550 & " logically freed memory, deallocated at:");
1551 Put_Line
(0, To_Traceback
(Header
.Dealloc_Traceback
).Traceback
);
1556 -----------------------
1557 -- Print_Info_Stdout --
1558 -----------------------
1560 procedure Print_Info_Stdout
1562 Cumulate
: Boolean := False;
1563 Display_Slots
: Boolean := False;
1564 Display_Leaks
: Boolean := False)
1566 procedure Internal
is new Print_Info
1567 (Put_Line
=> GNAT
.IO
.Put_Line
,
1568 Put
=> GNAT
.IO
.Put
);
1570 Internal
(Pool
, Cumulate
, Display_Slots
, Display_Leaks
);
1571 end Print_Info_Stdout
;
1577 procedure Dump_Gnatmem
(Pool
: Debug_Pool
; File_Name
: String) is
1578 type File_Ptr
is new System
.Address
;
1580 function fopen
(Path
: String; Mode
: String) return File_Ptr
;
1581 pragma Import
(C
, fopen
);
1584 (Ptr
: System
.Address
;
1594 pragma Import
(C
, fwrite
);
1596 procedure fputc
(C
: Integer; Stream
: File_Ptr
);
1597 pragma Import
(C
, fputc
);
1599 procedure fclose
(Stream
: File_Ptr
);
1600 pragma Import
(C
, fclose
);
1602 Address_Size
: constant size_t
:=
1603 System
.Address
'Max_Size_In_Storage_Elements;
1604 -- Size in bytes of a pointer
1607 Current
: System
.Address
;
1608 Header
: Allocation_Header_Access
;
1609 Actual_Size
: size_t
;
1610 Num_Calls
: Integer;
1611 Tracebk
: Tracebacks_Array_Access
;
1614 File
:= fopen
(File_Name
& ASCII
.NUL
, "wb" & ASCII
.NUL
);
1615 fwrite
("GMEM DUMP" & ASCII
.LF
, 10, 1, File
);
1617 -- List of not deallocated blocks (see Print_Info)
1619 Current
:= Pool
.First_Used_Block
;
1620 while Current
/= System
.Null_Address
loop
1621 Header
:= Header_Of
(Current
);
1623 Actual_Size
:= size_t
(Header
.Block_Size
);
1624 Tracebk
:= Header
.Alloc_Traceback
.Traceback
;
1625 Num_Calls
:= Tracebk
'Length;
1627 -- (Code taken from memtrack.adb in GNAT's sources)
1629 -- Logs allocation call using the format:
1631 -- 'A' <mem addr> <size chunk> <len backtrace> <addr1> ... <addrn>
1633 fputc
(Character'Pos ('A'), File
);
1634 fwrite
(Current
'Address, Address_Size
, 1, File
);
1635 fwrite
(Actual_Size
'Address, size_t
'Max_Size_In_Storage_Elements, 1,
1637 fwrite
(Num_Calls
'Address, Integer'Max_Size_In_Storage_Elements, 1,
1640 for J
in Tracebk
'First .. Tracebk
'First + Num_Calls
- 1 loop
1642 Ptr
: System
.Address
:= PC_For
(Tracebk
(J
));
1644 fwrite
(Ptr
'Address, Address_Size
, 1, File
);
1648 Current
:= Header
.Next
;
1658 end GNAT
.Debug_Pools
;