1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
5 -- G N A T . D E B U G _ P O O L S --
9 -- Copyright (C) 1992-2012, 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 3, 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. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 with Ada
.Exceptions
.Traceback
;
33 with GNAT
.IO
; use GNAT
.IO
;
35 with System
.Address_Image
;
36 with System
.Memory
; use System
.Memory
;
37 with System
.Soft_Links
; use System
.Soft_Links
;
39 with System
.Traceback_Entries
; use System
.Traceback_Entries
;
42 with GNAT
.Traceback
; use GNAT
.Traceback
;
44 with Ada
.Unchecked_Conversion
;
46 package body GNAT
.Debug_Pools
is
48 Default_Alignment
: constant := Standard
'Maximum_Alignment;
49 -- Alignment used for the memory chunks returned by Allocate. Using this
50 -- value guarantees that this alignment will be compatible with all types
51 -- and at the same time makes it easy to find the location of the extra
52 -- header allocated for each chunk.
54 Max_Ignored_Levels
: constant Natural := 10;
55 -- Maximum number of levels that will be ignored in backtraces. This is so
56 -- that we still have enough significant levels in the tracebacks returned
59 -- The value 10 is chosen as being greater than the maximum callgraph
60 -- in this package. Its actual value is not really relevant, as long as it
61 -- is high enough to make sure we still have enough frames to return to
62 -- the user after we have hidden the frames internal to this package.
64 ---------------------------
65 -- Back Trace Hash Table --
66 ---------------------------
68 -- This package needs to store one set of tracebacks for each allocation
69 -- point (when was it allocated or deallocated). This would use too much
70 -- memory, so the tracebacks are actually stored in a hash table, and
71 -- we reference elements in this hash table instead.
73 -- This hash-table will remain empty if the discriminant Stack_Trace_Depth
74 -- for the pools is set to 0.
76 -- This table is a global table, that can be shared among all debug pools
79 type Header
is range 1 .. 1023;
80 -- Number of elements in the hash-table
82 type Tracebacks_Array_Access
83 is access GNAT
.Traceback
.Tracebacks_Array
;
85 type Traceback_Kind
is (Alloc
, Dealloc
, Indirect_Alloc
, Indirect_Dealloc
);
87 type Traceback_Htable_Elem
;
88 type Traceback_Htable_Elem_Ptr
89 is access Traceback_Htable_Elem
;
91 type Traceback_Htable_Elem
is record
92 Traceback
: Tracebacks_Array_Access
;
93 Kind
: Traceback_Kind
;
96 Next
: Traceback_Htable_Elem_Ptr
;
99 -- Subprograms used for the Backtrace_Htable instantiation
102 (E
: Traceback_Htable_Elem_Ptr
;
103 Next
: Traceback_Htable_Elem_Ptr
);
104 pragma Inline
(Set_Next
);
107 (E
: Traceback_Htable_Elem_Ptr
) return Traceback_Htable_Elem_Ptr
;
108 pragma Inline
(Next
);
111 (E
: Traceback_Htable_Elem_Ptr
) return Tracebacks_Array_Access
;
112 pragma Inline
(Get_Key
);
114 function Hash
(T
: Tracebacks_Array_Access
) return Header
;
115 pragma Inline
(Hash
);
117 function Equal
(K1
, K2
: Tracebacks_Array_Access
) return Boolean;
118 -- Why is this not inlined???
120 -- The hash table for back traces
122 package Backtrace_Htable
is new GNAT
.HTable
.Static_HTable
123 (Header_Num
=> Header
,
124 Element
=> Traceback_Htable_Elem
,
125 Elmt_Ptr
=> Traceback_Htable_Elem_Ptr
,
127 Set_Next
=> Set_Next
,
129 Key
=> Tracebacks_Array_Access
,
134 -----------------------
135 -- Allocations table --
136 -----------------------
138 type Allocation_Header
;
139 type Allocation_Header_Access
is access Allocation_Header
;
141 type Traceback_Ptr_Or_Address
is new System
.Address
;
142 -- A type that acts as a C union, and is either a System.Address or a
143 -- Traceback_Htable_Elem_Ptr.
145 -- The following record stores extra information that needs to be
146 -- memorized for each block allocated with the special debug pool.
148 type Allocation_Header
is record
149 Allocation_Address
: System
.Address
;
150 -- Address of the block returned by malloc, possibly unaligned
152 Block_Size
: Storage_Offset
;
153 -- Needed only for advanced freeing algorithms (traverse all allocated
154 -- blocks for potential references). This value is negated when the
155 -- chunk of memory has been logically freed by the application. This
156 -- chunk has not been physically released yet.
158 Alloc_Traceback
: Traceback_Htable_Elem_Ptr
;
159 -- ??? comment required
161 Dealloc_Traceback
: Traceback_Ptr_Or_Address
;
162 -- Pointer to the traceback for the allocation (if the memory chunk is
163 -- still valid), or to the first deallocation otherwise. Make sure this
164 -- is a thin pointer to save space.
166 -- Dealloc_Traceback is also for blocks that are still allocated to
167 -- point to the previous block in the list. This saves space in this
168 -- header, and make manipulation of the lists of allocated pointers
171 Next
: System
.Address
;
172 -- Point to the next block of the same type (either allocated or
173 -- logically freed) in memory. This points to the beginning of the user
174 -- data, and does not include the header of that block.
177 function Header_Of
(Address
: System
.Address
)
178 return Allocation_Header_Access
;
179 pragma Inline
(Header_Of
);
180 -- Return the header corresponding to a previously allocated address
182 function To_Address
is new Ada
.Unchecked_Conversion
183 (Traceback_Ptr_Or_Address
, System
.Address
);
185 function To_Address
is new Ada
.Unchecked_Conversion
186 (System
.Address
, Traceback_Ptr_Or_Address
);
188 function To_Traceback
is new Ada
.Unchecked_Conversion
189 (Traceback_Ptr_Or_Address
, Traceback_Htable_Elem_Ptr
);
191 function To_Traceback
is new Ada
.Unchecked_Conversion
192 (Traceback_Htable_Elem_Ptr
, Traceback_Ptr_Or_Address
);
194 Header_Offset
: constant Storage_Count
:=
196 ((Allocation_Header
'Size / System
.Storage_Unit
197 + Default_Alignment
- 1) / Default_Alignment
);
198 -- Offset of user data after allocation header
200 Minimum_Allocation
: constant Storage_Count
:=
201 Default_Alignment
- 1 + Header_Offset
;
202 -- Minimal allocation: size of allocation_header rounded up to next
203 -- multiple of default alignment + worst-case padding.
205 -----------------------
206 -- Local subprograms --
207 -----------------------
209 function Find_Or_Create_Traceback
211 Kind
: Traceback_Kind
;
212 Size
: Storage_Count
;
213 Ignored_Frame_Start
: System
.Address
;
214 Ignored_Frame_End
: System
.Address
) return Traceback_Htable_Elem_Ptr
;
215 -- Return an element matching the current traceback (omitting the frames
216 -- that are in the current package). If this traceback already existed in
217 -- the htable, a pointer to this is returned to spare memory. Null is
218 -- returned if the pool is set not to store tracebacks. If the traceback
219 -- already existed in the table, the count is incremented so that
220 -- Dump_Tracebacks returns useful results. All addresses up to, and
221 -- including, an address between Ignored_Frame_Start .. Ignored_Frame_End
224 function Output_File
(Pool
: Debug_Pool
) return File_Type
;
225 pragma Inline
(Output_File
);
226 -- Returns file_type on which error messages have to be generated for Pool
231 Traceback
: Tracebacks_Array_Access
;
232 Ignored_Frame_Start
: System
.Address
:= System
.Null_Address
;
233 Ignored_Frame_End
: System
.Address
:= System
.Null_Address
);
234 -- Print Traceback to File. If Traceback is null, print the call_chain
235 -- at the current location, up to Depth levels, ignoring all addresses
236 -- up to the first one in the range:
237 -- Ignored_Frame_Start .. Ignored_Frame_End
240 function Is_Valid
(Storage
: System
.Address
) return Boolean;
241 pragma Inline
(Is_Valid
);
242 -- Return True if Storage is the address of a block that the debug pool
243 -- has under its control, in which case Header_Of may be used to access
244 -- the associated allocation header.
246 procedure Set_Valid
(Storage
: System
.Address
; Value
: Boolean);
247 pragma Inline
(Set_Valid
);
248 -- Mark the address Storage as being under control of the memory pool
249 -- (if Value is True), or not (if Value is False).
254 procedure Set_Dead_Beef
255 (Storage_Address
: System
.Address
;
256 Size_In_Storage_Elements
: Storage_Count
);
257 -- Set the contents of the memory block pointed to by Storage_Address to
258 -- the 16#DEADBEEF# pattern. If Size_In_Storage_Elements is not a multiple
259 -- of the length of this pattern, the last instance may be partial.
261 procedure Free_Physically
(Pool
: in out Debug_Pool
);
262 -- Start to physically release some memory to the system, until the amount
263 -- of logically (but not physically) freed memory is lower than the
264 -- expected amount in Pool.
266 procedure Allocate_End
;
267 procedure Deallocate_End
;
268 procedure Dereference_End
;
269 -- These procedures are used as markers when computing the stacktraces,
270 -- so that addresses in the debug pool itself are not reported to the user.
272 Code_Address_For_Allocate_End
: System
.Address
;
273 Code_Address_For_Deallocate_End
: System
.Address
;
274 Code_Address_For_Dereference_End
: System
.Address
;
275 -- Taking the address of the above procedures will not work on some
276 -- architectures (HPUX and VMS for instance). Thus we do the same thing
277 -- that is done in a-except.adb, and get the address of labels instead
279 procedure Skip_Levels
281 Trace
: Tracebacks_Array
;
283 Len
: in out Natural;
284 Ignored_Frame_Start
: System
.Address
;
285 Ignored_Frame_End
: System
.Address
);
286 -- Set Start .. Len to the range of values from Trace that should be output
287 -- to the user. This range of values excludes any address prior to the
288 -- first one in Ignored_Frame_Start .. Ignored_Frame_End (basically
289 -- addresses internal to this package). Depth is the number of levels that
290 -- the user is interested in.
296 function Header_Of
(Address
: System
.Address
)
297 return Allocation_Header_Access
299 function Convert
is new Ada
.Unchecked_Conversion
300 (System
.Address
, Allocation_Header_Access
);
302 return Convert
(Address
- Header_Offset
);
310 (E
: Traceback_Htable_Elem_Ptr
;
311 Next
: Traceback_Htable_Elem_Ptr
)
322 (E
: Traceback_Htable_Elem_Ptr
) return Traceback_Htable_Elem_Ptr
is
331 function Equal
(K1
, K2
: Tracebacks_Array_Access
) return Boolean is
332 use Ada
.Exceptions
.Traceback
;
334 return K1
.all = K2
.all;
342 (E
: Traceback_Htable_Elem_Ptr
) return Tracebacks_Array_Access
352 function Hash
(T
: Tracebacks_Array_Access
) return Header
is
353 Result
: Integer_Address
:= 0;
356 for X
in T
'Range loop
357 Result
:= Result
+ To_Integer
(PC_For
(T
(X
)));
360 return Header
(1 + Result
mod Integer_Address
(Header
'Last));
367 function Output_File
(Pool
: Debug_Pool
) return File_Type
is
369 if Pool
.Errors_To_Stdout
then
370 return Standard_Output
;
372 return Standard_Error
;
383 Traceback
: Tracebacks_Array_Access
;
384 Ignored_Frame_Start
: System
.Address
:= System
.Null_Address
;
385 Ignored_Frame_End
: System
.Address
:= System
.Null_Address
)
387 procedure Print
(Tr
: Tracebacks_Array
);
388 -- Print the traceback to standard_output
394 procedure Print
(Tr
: Tracebacks_Array
) is
396 for J
in Tr
'Range loop
397 Put
(File
, "0x" & Address_Image
(PC_For
(Tr
(J
))) & ' ');
399 Put
(File
, ASCII
.LF
);
402 -- Start of processing for Put_Line
405 if Traceback
= null then
407 Tr
: aliased Tracebacks_Array
(1 .. Depth
+ Max_Ignored_Levels
);
408 Start
, Len
: Natural;
411 Call_Chain
(Tr
, Len
);
412 Skip_Levels
(Depth
, Tr
, Start
, Len
,
413 Ignored_Frame_Start
, Ignored_Frame_End
);
414 Print
(Tr
(Start
.. Len
));
418 Print
(Traceback
.all);
426 procedure Skip_Levels
428 Trace
: Tracebacks_Array
;
430 Len
: in out Natural;
431 Ignored_Frame_Start
: System
.Address
;
432 Ignored_Frame_End
: System
.Address
)
435 Start
:= Trace
'First;
438 and then (PC_For
(Trace
(Start
)) < Ignored_Frame_Start
439 or else PC_For
(Trace
(Start
)) > Ignored_Frame_End
)
446 -- Just in case: make sure we have a traceback even if Ignore_Till
453 if Len
- Start
+ 1 > Depth
then
454 Len
:= Depth
+ Start
- 1;
458 ------------------------------
459 -- Find_Or_Create_Traceback --
460 ------------------------------
462 function Find_Or_Create_Traceback
464 Kind
: Traceback_Kind
;
465 Size
: Storage_Count
;
466 Ignored_Frame_Start
: System
.Address
;
467 Ignored_Frame_End
: System
.Address
) return Traceback_Htable_Elem_Ptr
470 if Pool
.Stack_Trace_Depth
= 0 then
475 Trace
: aliased Tracebacks_Array
476 (1 .. Integer (Pool
.Stack_Trace_Depth
) + Max_Ignored_Levels
);
477 Len
, Start
: Natural;
478 Elem
: Traceback_Htable_Elem_Ptr
;
481 Call_Chain
(Trace
, Len
);
482 Skip_Levels
(Pool
.Stack_Trace_Depth
, Trace
, Start
, Len
,
483 Ignored_Frame_Start
, Ignored_Frame_End
);
485 -- Check if the traceback is already in the table
488 Backtrace_Htable
.Get
(Trace
(Start
.. Len
)'Unrestricted_Access);
493 Elem
:= new Traceback_Htable_Elem
'
494 (Traceback => new Tracebacks_Array'(Trace
(Start
.. Len
)),
497 Total
=> Byte_Count
(Size
),
499 Backtrace_Htable
.Set
(Elem
);
502 Elem
.Count
:= Elem
.Count
+ 1;
503 Elem
.Total
:= Elem
.Total
+ Byte_Count
(Size
);
508 end Find_Or_Create_Traceback
;
514 package body Validity
is
516 -- The validity bits of the allocated blocks are kept in a has table.
517 -- Each component of the hash table contains the validity bits for a
518 -- 16 Mbyte memory chunk.
520 -- The reason the validity bits are kept for chunks of memory rather
521 -- than in a big array is that on some 64 bit platforms, it may happen
522 -- that two chunk of allocated data are very far from each other.
524 Memory_Chunk_Size
: constant Integer_Address
:= 2 ** 24; -- 16 MB
525 Validity_Divisor
: constant := Default_Alignment
* System
.Storage_Unit
;
527 Max_Validity_Byte_Index
: constant :=
528 Memory_Chunk_Size
/ Validity_Divisor
;
530 subtype Validity_Byte_Index
is Integer_Address
531 range 0 .. Max_Validity_Byte_Index
- 1;
533 type Byte
is mod 2 ** System
.Storage_Unit
;
535 type Validity_Bits
is array (Validity_Byte_Index
) of Byte
;
537 type Validity_Bits_Ref
is access all Validity_Bits
;
538 No_Validity_Bits
: constant Validity_Bits_Ref
:= null;
540 Max_Header_Num
: constant := 1023;
542 type Header_Num
is range 0 .. Max_Header_Num
- 1;
544 function Hash
(F
: Integer_Address
) return Header_Num
;
546 package Validy_Htable
is new GNAT
.HTable
.Simple_HTable
547 (Header_Num
=> Header_Num
,
548 Element
=> Validity_Bits_Ref
,
549 No_Element
=> No_Validity_Bits
,
550 Key
=> Integer_Address
,
553 -- Table to keep the validity bit blocks for the allocated data
555 function To_Pointer
is new Ada
.Unchecked_Conversion
556 (System
.Address
, Validity_Bits_Ref
);
558 procedure Memset
(A
: Address
; C
: Integer; N
: size_t
);
559 pragma Import
(C
, Memset
, "memset");
565 function Hash
(F
: Integer_Address
) return Header_Num
is
567 return Header_Num
(F
mod Max_Header_Num
);
574 function Is_Valid
(Storage
: System
.Address
) return Boolean is
575 Int_Storage
: constant Integer_Address
:= To_Integer
(Storage
);
578 -- The pool only returns addresses aligned on Default_Alignment so
579 -- anything off cannot be a valid block address and we can return
580 -- early in this case. We actually have to since our data structures
581 -- map validity bits for such aligned addresses only.
583 if Int_Storage
mod Default_Alignment
/= 0 then
588 Block_Number
: constant Integer_Address
:=
589 Int_Storage
/ Memory_Chunk_Size
;
590 Ptr
: constant Validity_Bits_Ref
:=
591 Validy_Htable
.Get
(Block_Number
);
592 Offset
: constant Integer_Address
:=
594 (Block_Number
* Memory_Chunk_Size
)) /
596 Bit
: constant Byte
:=
597 2 ** Natural (Offset
mod System
.Storage_Unit
);
599 if Ptr
= No_Validity_Bits
then
602 return (Ptr
(Offset
/ System
.Storage_Unit
) and Bit
) /= 0;
611 procedure Set_Valid
(Storage
: System
.Address
; Value
: Boolean) is
612 Int_Storage
: constant Integer_Address
:= To_Integer
(Storage
);
613 Block_Number
: constant Integer_Address
:=
614 Int_Storage
/ Memory_Chunk_Size
;
615 Ptr
: Validity_Bits_Ref
:= Validy_Htable
.Get
(Block_Number
);
616 Offset
: constant Integer_Address
:=
617 (Int_Storage
- (Block_Number
* Memory_Chunk_Size
)) /
619 Bit
: constant Byte
:=
620 2 ** Natural (Offset
mod System
.Storage_Unit
);
623 if Ptr
= No_Validity_Bits
then
625 -- First time in this memory area: allocate a new block and put
629 Ptr
:= To_Pointer
(Alloc
(size_t
(Max_Validity_Byte_Index
)));
630 Validy_Htable
.Set
(Block_Number
, Ptr
);
631 Memset
(Ptr
.all'Address, 0, size_t
(Max_Validity_Byte_Index
));
632 Ptr
(Offset
/ System
.Storage_Unit
) := Bit
;
637 Ptr
(Offset
/ System
.Storage_Unit
) :=
638 Ptr
(Offset
/ System
.Storage_Unit
) or Bit
;
641 Ptr
(Offset
/ System
.Storage_Unit
) :=
642 Ptr
(Offset
/ System
.Storage_Unit
) and (not Bit
);
654 (Pool
: in out Debug_Pool
;
655 Storage_Address
: out Address
;
656 Size_In_Storage_Elements
: Storage_Count
;
657 Alignment
: Storage_Count
)
659 pragma Unreferenced
(Alignment
);
660 -- Ignored, we always force 'Default_Alignment
662 type Local_Storage_Array
is new Storage_Array
663 (1 .. Size_In_Storage_Elements
+ Minimum_Allocation
);
665 type Ptr
is access Local_Storage_Array
;
666 -- On some systems, we might want to physically protect pages against
667 -- writing when they have been freed (of course, this is expensive in
668 -- terms of wasted memory). To do that, all we should have to do it to
669 -- set the size of this array to the page size. See mprotect().
671 Current
: Byte_Count
;
673 Trace
: Traceback_Htable_Elem_Ptr
;
679 -- If necessary, start physically releasing memory. The reason this is
680 -- done here, although Pool.Logically_Deallocated has not changed above,
681 -- is so that we do this only after a series of deallocations (e.g loop
682 -- that deallocates a big array). If we were doing that in Deallocate,
683 -- we might be physically freeing memory several times during the loop,
684 -- which is expensive.
686 if Pool
.Logically_Deallocated
>
687 Byte_Count
(Pool
.Maximum_Logically_Freed_Memory
)
689 Free_Physically
(Pool
);
692 -- Use standard (i.e. through malloc) allocations. This automatically
693 -- raises Storage_Error if needed. We also try once more to physically
694 -- release memory, so that even marked blocks, in the advanced scanning,
695 -- are freed. Note that we do not initialize the storage array since it
696 -- is not necessary to do so (however this will cause bogus valgrind
697 -- warnings, which should simply be ignored).
700 P
:= new Local_Storage_Array
;
703 when Storage_Error
=>
704 Free_Physically
(Pool
);
705 P
:= new Local_Storage_Array
;
711 ((To_Integer
(P
.all'Address) + Default_Alignment
- 1)
713 + Integer_Address
(Header_Offset
));
714 -- Computation is done in Integer_Address, not Storage_Offset, because
715 -- the range of Storage_Offset may not be large enough.
717 pragma Assert
((Storage_Address
- System
.Null_Address
)
718 mod Default_Alignment
= 0);
719 pragma Assert
(Storage_Address
+ Size_In_Storage_Elements
720 <= P
.all'Address + P
'Length);
722 Trace
:= Find_Or_Create_Traceback
723 (Pool
, Alloc
, Size_In_Storage_Elements
,
724 Allocate_Label
'Address, Code_Address_For_Allocate_End
);
726 pragma Warnings
(Off
);
727 -- Turn warning on alignment for convert call off. We know that in fact
728 -- this conversion is safe since P itself is always aligned on
729 -- Default_Alignment.
731 Header_Of
(Storage_Address
).all :=
732 (Allocation_Address
=> P
.all'Address,
733 Alloc_Traceback
=> Trace
,
734 Dealloc_Traceback
=> To_Traceback
(null),
735 Next
=> Pool
.First_Used_Block
,
736 Block_Size
=> Size_In_Storage_Elements
);
738 pragma Warnings
(On
);
740 -- Link this block in the list of used blocks. This will be used to list
741 -- memory leaks in Print_Info, and for the advanced schemes of
742 -- Physical_Free, where we want to traverse all allocated blocks and
743 -- search for possible references.
745 -- We insert in front, since most likely we'll be freeing the most
746 -- recently allocated blocks first (the older one might stay allocated
747 -- for the whole life of the application).
749 if Pool
.First_Used_Block
/= System
.Null_Address
then
750 Header_Of
(Pool
.First_Used_Block
).Dealloc_Traceback
:=
751 To_Address
(Storage_Address
);
754 Pool
.First_Used_Block
:= Storage_Address
;
756 -- Mark the new address as valid
758 Set_Valid
(Storage_Address
, True);
760 if Pool
.Low_Level_Traces
then
761 Put
(Output_File
(Pool
),
763 & Storage_Count
'Image (Size_In_Storage_Elements
)
764 & " bytes at 0x" & Address_Image
(Storage_Address
)
766 & Storage_Count
'Image (Local_Storage_Array
'Length)
767 & " bytes at 0x" & Address_Image
(P
.all'Address)
769 Put_Line
(Output_File
(Pool
), Pool
.Stack_Trace_Depth
, null,
770 Allocate_Label
'Address,
771 Code_Address_For_Deallocate_End
);
774 -- Update internal data
777 Pool
.Allocated
+ Byte_Count
(Size_In_Storage_Elements
);
779 Current
:= Pool
.Allocated
-
780 Pool
.Logically_Deallocated
-
781 Pool
.Physically_Deallocated
;
783 if Current
> Pool
.High_Water
then
784 Pool
.High_Water
:= Current
;
799 -- DO NOT MOVE, this must be right after Allocate. This is similar to what
800 -- is done in a-except, so that we can hide the traceback frames internal
803 procedure Allocate_End
is
805 <<Allocate_End_Label
>>
806 Code_Address_For_Allocate_End
:= Allocate_End_Label
'Address;
813 procedure Set_Dead_Beef
814 (Storage_Address
: System
.Address
;
815 Size_In_Storage_Elements
: Storage_Count
)
817 Dead_Bytes
: constant := 4;
819 type Data
is mod 2 ** (Dead_Bytes
* 8);
820 for Data
'Size use Dead_Bytes
* 8;
822 Dead
: constant Data
:= 16#DEAD_BEEF#
;
824 type Dead_Memory
is array
825 (1 .. Size_In_Storage_Elements
/ Dead_Bytes
) of Data
;
826 type Mem_Ptr
is access Dead_Memory
;
828 type Byte
is mod 2 ** 8;
831 type Dead_Memory_Bytes
is array (0 .. 2) of Byte
;
832 type Dead_Memory_Bytes_Ptr
is access Dead_Memory_Bytes
;
834 function From_Ptr
is new Ada
.Unchecked_Conversion
835 (System
.Address
, Mem_Ptr
);
837 function From_Ptr
is new Ada
.Unchecked_Conversion
838 (System
.Address
, Dead_Memory_Bytes_Ptr
);
840 M
: constant Mem_Ptr
:= From_Ptr
(Storage_Address
);
841 M2
: Dead_Memory_Bytes_Ptr
;
842 Modulo
: constant Storage_Count
:=
843 Size_In_Storage_Elements
mod Dead_Bytes
;
845 M
.all := (others => Dead
);
847 -- Any bytes left (up to three of them)
850 M2
:= From_Ptr
(Storage_Address
+ M
'Length * Dead_Bytes
);
863 ---------------------
864 -- Free_Physically --
865 ---------------------
867 procedure Free_Physically
(Pool
: in out Debug_Pool
) is
868 type Byte
is mod 256;
869 type Byte_Access
is access Byte
;
871 function To_Byte
is new Ada
.Unchecked_Conversion
872 (System
.Address
, Byte_Access
);
874 type Address_Access
is access System
.Address
;
876 function To_Address_Access
is new Ada
.Unchecked_Conversion
877 (System
.Address
, Address_Access
);
879 In_Use_Mark
: constant Byte
:= 16#D#
;
880 Free_Mark
: constant Byte
:= 16#F#
;
882 Total_Freed
: Storage_Count
:= 0;
884 procedure Reset_Marks
;
885 -- Unmark all the logically freed blocks, so that they are considered
886 -- for physical deallocation
889 (H
: Allocation_Header_Access
; A
: System
.Address
; In_Use
: Boolean);
890 -- Mark the user data block starting at A. For a block of size zero,
891 -- nothing is done. For a block with a different size, the first byte
892 -- is set to either "D" (in use) or "F" (free).
894 function Marked
(A
: System
.Address
) return Boolean;
895 -- Return true if the user data block starting at A might be in use
898 procedure Mark_Blocks
;
899 -- Traverse all allocated blocks, and search for possible references
900 -- to logically freed blocks. Mark them appropriately
902 procedure Free_Blocks
(Ignore_Marks
: Boolean);
903 -- Physically release blocks. Only the blocks that haven't been marked
904 -- will be released, unless Ignore_Marks is true.
910 procedure Free_Blocks
(Ignore_Marks
: Boolean) is
911 Header
: Allocation_Header_Access
;
912 Tmp
: System
.Address
:= Pool
.First_Free_Block
;
913 Next
: System
.Address
;
914 Previous
: System
.Address
:= System
.Null_Address
;
917 while Tmp
/= System
.Null_Address
918 and then Total_Freed
< Pool
.Minimum_To_Free
920 Header
:= Header_Of
(Tmp
);
922 -- If we know, or at least assume, the block is no longer
923 -- referenced anywhere, we can free it physically.
925 if Ignore_Marks
or else not Marked
(Tmp
) then
928 pragma Suppress
(All_Checks
);
929 -- Suppress the checks on this section. If they are overflow
930 -- errors, it isn't critical, and we'd rather avoid a
931 -- Constraint_Error in that case.
933 -- Note that block_size < zero for freed blocks
935 Pool
.Physically_Deallocated
:=
936 Pool
.Physically_Deallocated
-
937 Byte_Count
(Header
.Block_Size
);
939 Pool
.Logically_Deallocated
:=
940 Pool
.Logically_Deallocated
+
941 Byte_Count
(Header
.Block_Size
);
943 Total_Freed
:= Total_Freed
- Header
.Block_Size
;
948 if Pool
.Low_Level_Traces
then
951 "info: Freeing physical memory "
952 & Storage_Count
'Image
953 ((abs Header
.Block_Size
) + Minimum_Allocation
)
955 & Address_Image
(Header
.Allocation_Address
));
958 System
.Memory
.Free
(Header
.Allocation_Address
);
959 Set_Valid
(Tmp
, False);
961 -- Remove this block from the list
963 if Previous
= System
.Null_Address
then
964 Pool
.First_Free_Block
:= Next
;
966 Header_Of
(Previous
).Next
:= Next
;
983 (H
: Allocation_Header_Access
;
988 if H
.Block_Size
/= 0 then
989 To_Byte
(A
).all := (if In_Use
then In_Use_Mark
else Free_Mark
);
997 procedure Mark_Blocks
is
998 Tmp
: System
.Address
:= Pool
.First_Used_Block
;
999 Previous
: System
.Address
;
1000 Last
: System
.Address
;
1001 Pointed
: System
.Address
;
1002 Header
: Allocation_Header_Access
;
1005 -- For each allocated block, check its contents. Things that look
1006 -- like a possible address are used to mark the blocks so that we try
1007 -- and keep them, for better detection in case of invalid access.
1008 -- This mechanism is far from being fool-proof: it doesn't check the
1009 -- stacks of the threads, doesn't check possible memory allocated not
1010 -- under control of this debug pool. But it should allow us to catch
1013 while Tmp
/= System
.Null_Address
loop
1015 Last
:= Tmp
+ Header_Of
(Tmp
).Block_Size
;
1016 while Previous
< Last
loop
1017 -- ??? Should we move byte-per-byte, or consider that addresses
1018 -- are always aligned on 4-bytes boundaries ? Let's use the
1021 Pointed
:= To_Address_Access
(Previous
).all;
1022 if Is_Valid
(Pointed
) then
1023 Header
:= Header_Of
(Pointed
);
1025 -- Do not even attempt to mark blocks in use. That would
1026 -- screw up the whole application, of course.
1028 if Header
.Block_Size
< 0 then
1029 Mark
(Header
, Pointed
, In_Use
=> True);
1033 Previous
:= Previous
+ System
.Address
'Size;
1036 Tmp
:= Header_Of
(Tmp
).Next
;
1044 function Marked
(A
: System
.Address
) return Boolean is
1046 return To_Byte
(A
).all = In_Use_Mark
;
1053 procedure Reset_Marks
is
1054 Current
: System
.Address
:= Pool
.First_Free_Block
;
1055 Header
: Allocation_Header_Access
;
1057 while Current
/= System
.Null_Address
loop
1058 Header
:= Header_Of
(Current
);
1059 Mark
(Header
, Current
, False);
1060 Current
:= Header
.Next
;
1064 -- Start of processing for Free_Physically
1069 if Pool
.Advanced_Scanning
then
1071 -- Reset the mark for each freed block
1078 Free_Blocks
(Ignore_Marks
=> not Pool
.Advanced_Scanning
);
1080 -- The contract is that we need to free at least Minimum_To_Free bytes,
1081 -- even if this means freeing marked blocks in the advanced scheme
1083 if Total_Freed
< Pool
.Minimum_To_Free
1084 and then Pool
.Advanced_Scanning
1086 Pool
.Marked_Blocks_Deallocated
:= True;
1087 Free_Blocks
(Ignore_Marks
=> True);
1096 end Free_Physically
;
1102 procedure Deallocate
1103 (Pool
: in out Debug_Pool
;
1104 Storage_Address
: Address
;
1105 Size_In_Storage_Elements
: Storage_Count
;
1106 Alignment
: Storage_Count
)
1108 pragma Unreferenced
(Alignment
);
1110 Header
: constant Allocation_Header_Access
:=
1111 Header_Of
(Storage_Address
);
1113 Previous
: System
.Address
;
1116 <<Deallocate_Label
>>
1118 Valid
:= Is_Valid
(Storage_Address
);
1122 if Pool
.Raise_Exceptions
then
1123 raise Freeing_Not_Allocated_Storage
;
1125 Put
(Output_File
(Pool
),
1126 "error: Freeing not allocated storage, at ");
1127 Put_Line
(Output_File
(Pool
), Pool
.Stack_Trace_Depth
, null,
1128 Deallocate_Label
'Address,
1129 Code_Address_For_Deallocate_End
);
1132 elsif Header
.Block_Size
< 0 then
1134 if Pool
.Raise_Exceptions
then
1135 raise Freeing_Deallocated_Storage
;
1137 Put
(Output_File
(Pool
),
1138 "error: Freeing already deallocated storage, at ");
1139 Put_Line
(Output_File
(Pool
), Pool
.Stack_Trace_Depth
, null,
1140 Deallocate_Label
'Address,
1141 Code_Address_For_Deallocate_End
);
1142 Put
(Output_File
(Pool
), " Memory already deallocated at ");
1144 (Output_File
(Pool
), 0,
1145 To_Traceback
(Header
.Dealloc_Traceback
).Traceback
);
1146 Put
(Output_File
(Pool
), " Memory was allocated at ");
1147 Put_Line
(Output_File
(Pool
), 0, Header
.Alloc_Traceback
.Traceback
);
1151 -- Some sort of codegen problem or heap corruption caused the
1152 -- Size_In_Storage_Elements to be wrongly computed.
1153 -- The code below is all based on the assumption that Header.all
1154 -- is not corrupted, such that the error is non-fatal.
1156 if Header
.Block_Size
/= Size_In_Storage_Elements
then
1157 Put_Line
(Output_File
(Pool
),
1158 "error: Deallocate size "
1159 & Storage_Count
'Image (Size_In_Storage_Elements
)
1160 & " does not match allocate size "
1161 & Storage_Count
'Image (Header
.Block_Size
));
1164 if Pool
.Low_Level_Traces
then
1165 Put
(Output_File
(Pool
),
1167 & Storage_Count
'Image (Size_In_Storage_Elements
)
1168 & " bytes at 0x" & Address_Image
(Storage_Address
)
1170 & Storage_Count
'Image (Header
.Block_Size
+ Minimum_Allocation
)
1171 & " bytes at 0x" & Address_Image
(Header
.Allocation_Address
)
1173 Put_Line
(Output_File
(Pool
), Pool
.Stack_Trace_Depth
, null,
1174 Deallocate_Label
'Address,
1175 Code_Address_For_Deallocate_End
);
1176 Put
(Output_File
(Pool
), " Memory was allocated at ");
1177 Put_Line
(Output_File
(Pool
), 0, Header
.Alloc_Traceback
.Traceback
);
1180 -- Remove this block from the list of used blocks
1183 To_Address
(Header
.Dealloc_Traceback
);
1185 if Previous
= System
.Null_Address
then
1186 Pool
.First_Used_Block
:= Header_Of
(Pool
.First_Used_Block
).Next
;
1188 if Pool
.First_Used_Block
/= System
.Null_Address
then
1189 Header_Of
(Pool
.First_Used_Block
).Dealloc_Traceback
:=
1190 To_Traceback
(null);
1194 Header_Of
(Previous
).Next
:= Header
.Next
;
1196 if Header
.Next
/= System
.Null_Address
then
1198 (Header
.Next
).Dealloc_Traceback
:= To_Address
(Previous
);
1202 -- Update the header
1205 (Allocation_Address
=> Header
.Allocation_Address
,
1206 Alloc_Traceback
=> Header
.Alloc_Traceback
,
1207 Dealloc_Traceback
=> To_Traceback
1208 (Find_Or_Create_Traceback
1210 Size_In_Storage_Elements
,
1211 Deallocate_Label
'Address,
1212 Code_Address_For_Deallocate_End
)),
1213 Next
=> System
.Null_Address
,
1214 Block_Size
=> -Header
.Block_Size
);
1216 if Pool
.Reset_Content_On_Free
then
1217 Set_Dead_Beef
(Storage_Address
, -Header
.Block_Size
);
1220 Pool
.Logically_Deallocated
:=
1221 Pool
.Logically_Deallocated
+ Byte_Count
(-Header
.Block_Size
);
1223 -- Link this free block with the others (at the end of the list, so
1224 -- that we can start releasing the older blocks first later on).
1226 if Pool
.First_Free_Block
= System
.Null_Address
then
1227 Pool
.First_Free_Block
:= Storage_Address
;
1228 Pool
.Last_Free_Block
:= Storage_Address
;
1231 Header_Of
(Pool
.Last_Free_Block
).Next
:= Storage_Address
;
1232 Pool
.Last_Free_Block
:= Storage_Address
;
1235 -- Do not physically release the memory here, but in Alloc.
1236 -- See comment there for details.
1247 --------------------
1248 -- Deallocate_End --
1249 --------------------
1251 -- DO NOT MOVE, this must be right after Deallocate
1255 -- This is making assumptions about code order that may be invalid ???
1257 procedure Deallocate_End
is
1259 <<Deallocate_End_Label
>>
1260 Code_Address_For_Deallocate_End
:= Deallocate_End_Label
'Address;
1267 procedure Dereference
1268 (Pool
: in out Debug_Pool
;
1269 Storage_Address
: Address
;
1270 Size_In_Storage_Elements
: Storage_Count
;
1271 Alignment
: Storage_Count
)
1273 pragma Unreferenced
(Alignment
, Size_In_Storage_Elements
);
1275 Valid
: constant Boolean := Is_Valid
(Storage_Address
);
1276 Header
: Allocation_Header_Access
;
1279 -- Locking policy: we do not do any locking in this procedure. The
1280 -- tables are only read, not written to, and although a problem might
1281 -- appear if someone else is modifying the tables at the same time, this
1282 -- race condition is not intended to be detected by this storage_pool (a
1283 -- now invalid pointer would appear as valid). Instead, we prefer
1284 -- optimum performance for dereferences.
1286 <<Dereference_Label
>>
1289 if Pool
.Raise_Exceptions
then
1290 raise Accessing_Not_Allocated_Storage
;
1292 Put
(Output_File
(Pool
),
1293 "error: Accessing not allocated storage, at ");
1294 Put_Line
(Output_File
(Pool
), Pool
.Stack_Trace_Depth
, null,
1295 Dereference_Label
'Address,
1296 Code_Address_For_Dereference_End
);
1300 Header
:= Header_Of
(Storage_Address
);
1302 if Header
.Block_Size
< 0 then
1303 if Pool
.Raise_Exceptions
then
1304 raise Accessing_Deallocated_Storage
;
1306 Put
(Output_File
(Pool
),
1307 "error: Accessing deallocated storage, at ");
1309 (Output_File
(Pool
), Pool
.Stack_Trace_Depth
, null,
1310 Dereference_Label
'Address,
1311 Code_Address_For_Dereference_End
);
1312 Put
(Output_File
(Pool
), " First deallocation at ");
1314 (Output_File
(Pool
),
1315 0, To_Traceback
(Header
.Dealloc_Traceback
).Traceback
);
1316 Put
(Output_File
(Pool
), " Initial allocation at ");
1318 (Output_File
(Pool
),
1319 0, Header
.Alloc_Traceback
.Traceback
);
1325 ---------------------
1326 -- Dereference_End --
1327 ---------------------
1329 -- DO NOT MOVE: this must be right after Dereference
1333 -- This is making assumptions about code order that may be invalid ???
1335 procedure Dereference_End
is
1337 <<Dereference_End_Label
>>
1338 Code_Address_For_Dereference_End
:= Dereference_End_Label
'Address;
1339 end Dereference_End
;
1345 procedure Print_Info
1347 Cumulate
: Boolean := False;
1348 Display_Slots
: Boolean := False;
1349 Display_Leaks
: Boolean := False)
1352 package Backtrace_Htable_Cumulate
is new GNAT
.HTable
.Static_HTable
1353 (Header_Num
=> Header
,
1354 Element
=> Traceback_Htable_Elem
,
1355 Elmt_Ptr
=> Traceback_Htable_Elem_Ptr
,
1357 Set_Next
=> Set_Next
,
1359 Key
=> Tracebacks_Array_Access
,
1363 -- This needs a comment ??? probably some of the ones below do too???
1365 Data
: Traceback_Htable_Elem_Ptr
;
1366 Elem
: Traceback_Htable_Elem_Ptr
;
1367 Current
: System
.Address
;
1368 Header
: Allocation_Header_Access
;
1373 ("Total allocated bytes : " &
1374 Byte_Count
'Image (Pool
.Allocated
));
1377 ("Total logically deallocated bytes : " &
1378 Byte_Count
'Image (Pool
.Logically_Deallocated
));
1381 ("Total physically deallocated bytes : " &
1382 Byte_Count
'Image (Pool
.Physically_Deallocated
));
1384 if Pool
.Marked_Blocks_Deallocated
then
1385 Put_Line
("Marked blocks were physically deallocated. This is");
1386 Put_Line
("potentially dangerous, and you might want to run");
1387 Put_Line
("again with a lower value of Minimum_To_Free");
1391 ("Current Water Mark: " &
1393 (Pool
.Allocated
- Pool
.Logically_Deallocated
1394 - Pool
.Physically_Deallocated
));
1397 ("High Water Mark: " &
1398 Byte_Count
'Image (Pool
.High_Water
));
1402 if Display_Slots
then
1403 Data
:= Backtrace_Htable
.Get_First
;
1404 while Data
/= null loop
1405 if Data
.Kind
in Alloc
.. Dealloc
then
1407 new Traceback_Htable_Elem
'
1408 (Traceback => new Tracebacks_Array'(Data
.Traceback
.all),
1409 Count
=> Data
.Count
,
1411 Total
=> Data
.Total
,
1413 Backtrace_Htable_Cumulate
.Set
(Elem
);
1416 K
:= (if Data
.Kind
= Alloc
then Indirect_Alloc
1417 else Indirect_Dealloc
);
1419 -- Propagate the direct call to all its parents
1421 for T
in Data
.Traceback
'First + 1 .. Data
.Traceback
'Last loop
1422 Elem
:= Backtrace_Htable_Cumulate
.Get
1424 (T
.. Data
.Traceback
'Last)'Unrestricted_Access);
1426 -- If not, insert it
1429 Elem
:= new Traceback_Htable_Elem
'
1430 (Traceback => new Tracebacks_Array'
1431 (Data
.Traceback
(T
.. Data
.Traceback
'Last)),
1432 Count
=> Data
.Count
,
1434 Total
=> Data
.Total
,
1436 Backtrace_Htable_Cumulate
.Set
(Elem
);
1438 -- Properly take into account that the subprograms
1439 -- indirectly called might be doing either allocations
1440 -- or deallocations. This needs to be reflected in the
1444 Elem
.Count
:= Elem
.Count
+ Data
.Count
;
1446 if K
= Elem
.Kind
then
1447 Elem
.Total
:= Elem
.Total
+ Data
.Total
;
1449 elsif Elem
.Total
> Data
.Total
then
1450 Elem
.Total
:= Elem
.Total
- Data
.Total
;
1454 Elem
.Total
:= Data
.Total
- Elem
.Total
;
1460 Data
:= Backtrace_Htable
.Get_Next
;
1464 Put_Line
("List of allocations/deallocations: ");
1466 Data
:= Backtrace_Htable_Cumulate
.Get_First
;
1467 while Data
/= null loop
1469 when Alloc
=> Put
("alloc (count:");
1470 when Indirect_Alloc
=> Put
("indirect alloc (count:");
1471 when Dealloc
=> Put
("free (count:");
1472 when Indirect_Dealloc
=> Put
("indirect free (count:");
1475 Put
(Natural'Image (Data
.Count
) & ", total:" &
1476 Byte_Count
'Image (Data
.Total
) & ") ");
1478 for T
in Data
.Traceback
'Range loop
1479 Put
("0x" & Address_Image
(PC_For
(Data
.Traceback
(T
))) & ' ');
1484 Data
:= Backtrace_Htable_Cumulate
.Get_Next
;
1487 Backtrace_Htable_Cumulate
.Reset
;
1490 if Display_Leaks
then
1492 Put_Line
("List of not deallocated blocks:");
1494 -- Do not try to group the blocks with the same stack traces
1495 -- together. This is done by the gnatmem output.
1497 Current
:= Pool
.First_Used_Block
;
1498 while Current
/= System
.Null_Address
loop
1499 Header
:= Header_Of
(Current
);
1501 Put
("Size: " & Storage_Count
'Image (Header
.Block_Size
) & " at: ");
1503 for T
in Header
.Alloc_Traceback
.Traceback
'Range loop
1504 Put
("0x" & Address_Image
1505 (PC_For
(Header
.Alloc_Traceback
.Traceback
(T
))) & ' ');
1509 Current
:= Header
.Next
;
1518 function Storage_Size
(Pool
: Debug_Pool
) return Storage_Count
is
1519 pragma Unreferenced
(Pool
);
1521 return Storage_Count
'Last;
1529 (Pool
: in out Debug_Pool
;
1530 Stack_Trace_Depth
: Natural := Default_Stack_Trace_Depth
;
1531 Maximum_Logically_Freed_Memory
: SSC
:= Default_Max_Freed
;
1532 Minimum_To_Free
: SSC
:= Default_Min_Freed
;
1533 Reset_Content_On_Free
: Boolean := Default_Reset_Content
;
1534 Raise_Exceptions
: Boolean := Default_Raise_Exceptions
;
1535 Advanced_Scanning
: Boolean := Default_Advanced_Scanning
;
1536 Errors_To_Stdout
: Boolean := Default_Errors_To_Stdout
;
1537 Low_Level_Traces
: Boolean := Default_Low_Level_Traces
)
1540 Pool
.Stack_Trace_Depth
:= Stack_Trace_Depth
;
1541 Pool
.Maximum_Logically_Freed_Memory
:= Maximum_Logically_Freed_Memory
;
1542 Pool
.Reset_Content_On_Free
:= Reset_Content_On_Free
;
1543 Pool
.Raise_Exceptions
:= Raise_Exceptions
;
1544 Pool
.Minimum_To_Free
:= Minimum_To_Free
;
1545 Pool
.Advanced_Scanning
:= Advanced_Scanning
;
1546 Pool
.Errors_To_Stdout
:= Errors_To_Stdout
;
1547 Pool
.Low_Level_Traces
:= Low_Level_Traces
;
1554 procedure Print_Pool
(A
: System
.Address
) is
1555 Storage
: constant Address
:= A
;
1556 Valid
: constant Boolean := Is_Valid
(Storage
);
1557 Header
: Allocation_Header_Access
;
1560 -- We might get Null_Address if the call from gdb was done
1561 -- incorrectly. For instance, doing a "print_pool(my_var)" passes 0x0,
1562 -- instead of passing the value of my_var
1564 if A
= System
.Null_Address
then
1566 (Standard_Output
, "Memory not under control of the storage pool");
1572 (Standard_Output
, "Memory not under control of the storage pool");
1575 Header
:= Header_Of
(Storage
);
1576 Put_Line
(Standard_Output
, "0x" & Address_Image
(A
)
1577 & " allocated at:");
1578 Put_Line
(Standard_Output
, 0, Header
.Alloc_Traceback
.Traceback
);
1580 if To_Traceback
(Header
.Dealloc_Traceback
) /= null then
1581 Put_Line
(Standard_Output
, "0x" & Address_Image
(A
)
1582 & " logically freed memory, deallocated at:");
1584 (Standard_Output
, 0,
1585 To_Traceback
(Header
.Dealloc_Traceback
).Traceback
);
1590 -----------------------
1591 -- Print_Info_Stdout --
1592 -----------------------
1594 procedure Print_Info_Stdout
1596 Cumulate
: Boolean := False;
1597 Display_Slots
: Boolean := False;
1598 Display_Leaks
: Boolean := False)
1600 procedure Stdout_Put
(S
: String);
1601 procedure Stdout_Put_Line
(S
: String);
1602 -- Wrappers for Put and Put_Line that ensure we always write to stdout
1603 -- instead of the current output file defined in GNAT.IO.
1605 procedure Internal
is new Print_Info
1606 (Put_Line
=> Stdout_Put_Line
,
1613 procedure Stdout_Put
(S
: String) is
1615 Put_Line
(Standard_Output
, S
);
1618 ---------------------
1619 -- Stdout_Put_Line --
1620 ---------------------
1622 procedure Stdout_Put_Line
(S
: String) is
1624 Put_Line
(Standard_Output
, S
);
1625 end Stdout_Put_Line
;
1627 -- Start of processing for Print_Info_Stdout
1630 Internal
(Pool
, Cumulate
, Display_Slots
, Display_Leaks
);
1631 end Print_Info_Stdout
;
1637 procedure Dump_Gnatmem
(Pool
: Debug_Pool
; File_Name
: String) is
1638 type File_Ptr
is new System
.Address
;
1640 function fopen
(Path
: String; Mode
: String) return File_Ptr
;
1641 pragma Import
(C
, fopen
);
1644 (Ptr
: System
.Address
;
1654 pragma Import
(C
, fwrite
);
1656 procedure fputc
(C
: Integer; Stream
: File_Ptr
);
1657 pragma Import
(C
, fputc
);
1659 procedure fclose
(Stream
: File_Ptr
);
1660 pragma Import
(C
, fclose
);
1662 Address_Size
: constant size_t
:=
1663 System
.Address
'Max_Size_In_Storage_Elements;
1664 -- Size in bytes of a pointer
1667 Current
: System
.Address
;
1668 Header
: Allocation_Header_Access
;
1669 Actual_Size
: size_t
;
1670 Num_Calls
: Integer;
1671 Tracebk
: Tracebacks_Array_Access
;
1672 Dummy_Time
: Duration := 1.0;
1675 File
:= fopen
(File_Name
& ASCII
.NUL
, "wb" & ASCII
.NUL
);
1676 fwrite
("GMEM DUMP" & ASCII
.LF
, 10, 1, File
);
1677 fwrite
(Dummy_Time
'Address, Duration'Max_Size_In_Storage_Elements, 1,
1680 -- List of not deallocated blocks (see Print_Info)
1682 Current
:= Pool
.First_Used_Block
;
1683 while Current
/= System
.Null_Address
loop
1684 Header
:= Header_Of
(Current
);
1686 Actual_Size
:= size_t
(Header
.Block_Size
);
1687 Tracebk
:= Header
.Alloc_Traceback
.Traceback
;
1688 Num_Calls
:= Tracebk
'Length;
1690 -- (Code taken from memtrack.adb in GNAT's sources)
1692 -- Logs allocation call using the format:
1694 -- 'A' <mem addr> <size chunk> <len backtrace> <addr1> ... <addrn>
1696 fputc
(Character'Pos ('A'), File
);
1697 fwrite
(Current
'Address, Address_Size
, 1, File
);
1698 fwrite
(Actual_Size
'Address, size_t
'Max_Size_In_Storage_Elements, 1,
1700 fwrite
(Dummy_Time
'Address, Duration'Max_Size_In_Storage_Elements, 1,
1702 fwrite
(Num_Calls
'Address, Integer'Max_Size_In_Storage_Elements, 1,
1705 for J
in Tracebk
'First .. Tracebk
'First + Num_Calls
- 1 loop
1707 Ptr
: System
.Address
:= PC_For
(Tracebk
(J
));
1709 fwrite
(Ptr
'Address, Address_Size
, 1, File
);
1713 Current
:= Header
.Next
;
1719 -- Package initialization
1725 end GNAT
.Debug_Pools
;