1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
5 -- G N A T . D E B U G _ P O O L S --
9 -- Copyright (C) 1992-2016, 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 GNAT
.IO
; use GNAT
.IO
;
35 with System
.Memory
; use System
.Memory
;
36 with System
.Soft_Links
; use System
.Soft_Links
;
38 with System
.Traceback_Entries
;
40 with GNAT
.Debug_Utilities
; use GNAT
.Debug_Utilities
;
42 with GNAT
.Traceback
; use GNAT
.Traceback
;
44 with Ada
.Unchecked_Conversion
;
46 package body GNAT
.Debug_Pools
is
48 Storage_Alignment
: constant := Standard
'Maximum_Alignment;
49 -- Alignment enforced for all the memory chunks returned by Allocate,
50 -- maximized to make sure that it will be compatible with all types.
52 -- The addresses returned by the underlying low-level allocator (be it
53 -- 'new' or a straight 'malloc') aren't guaranteed to be that much aligned
54 -- on some targets, so we manage the needed alignment padding ourselves
55 -- systematically. Use of a common value for every allocation allows
56 -- significant simplifications in the code, nevertheless, for improved
57 -- robustness and efficiency overall.
59 -- We combine a few internal devices to offer the pool services:
61 -- * A management header attached to each allocated memory block, located
62 -- right ahead of it, like so:
64 -- Storage Address returned by the pool,
65 -- aligned on Storage_Alignment
67 -- +------+--------+---------------------
68 -- | ~~~~ | HEADER | USER DATA ... |
69 -- +------+--------+---------------------
74 -- The alignment padding is required
76 -- * A validity bitmap, which holds a validity bit for blocks managed by
77 -- the pool. Enforcing Storage_Alignment on those blocks allows efficient
78 -- validity management.
80 -- * A list of currently used blocks.
82 Max_Ignored_Levels
: constant Natural := 10;
83 -- Maximum number of levels that will be ignored in backtraces. This is so
84 -- that we still have enough significant levels in the tracebacks returned
87 -- The value 10 is chosen as being greater than the maximum callgraph
88 -- in this package. Its actual value is not really relevant, as long as it
89 -- is high enough to make sure we still have enough frames to return to
90 -- the user after we have hidden the frames internal to this package.
92 Disable
: Boolean := False;
93 -- This variable is used to avoid infinite loops, where this package would
94 -- itself allocate memory and then call itself recursively, forever. Useful
95 -- when System_Memory_Debug_Pool_Enabled is True.
97 System_Memory_Debug_Pool_Enabled
: Boolean := False;
98 -- If True, System.Memory allocation uses Debug_Pool
100 Allow_Unhandled_Memory
: Boolean := False;
101 -- If True, protects Deallocate against releasing memory allocated before
102 -- System_Memory_Debug_Pool_Enabled was set.
104 Traceback_Count
: Byte_Count
:= 0;
105 -- Total number of traceback elements
107 ---------------------------
108 -- Back Trace Hash Table --
109 ---------------------------
111 -- This package needs to store one set of tracebacks for each allocation
112 -- point (when was it allocated or deallocated). This would use too much
113 -- memory, so the tracebacks are actually stored in a hash table, and
114 -- we reference elements in this hash table instead.
116 -- This hash-table will remain empty if the discriminant Stack_Trace_Depth
117 -- for the pools is set to 0.
119 -- This table is a global table, that can be shared among all debug pools
122 type Header
is range 1 .. 1023;
123 -- Number of elements in the hash-table
125 type Tracebacks_Array_Access
is access Tracebacks_Array
;
127 type Traceback_Kind
is (Alloc
, Dealloc
, Indirect_Alloc
, Indirect_Dealloc
);
129 type Traceback_Htable_Elem
;
130 type Traceback_Htable_Elem_Ptr
131 is access Traceback_Htable_Elem
;
133 type Traceback_Htable_Elem
is record
134 Traceback
: Tracebacks_Array_Access
;
135 Kind
: Traceback_Kind
;
137 -- Size of the memory allocated/freed at Traceback since last Reset call
140 -- Number of chunk of memory allocated/freed at Traceback since last
144 -- Number of chunk of memory allocated at Traceback, currently freed
145 -- since last Reset call. (only for Alloc & Indirect_Alloc elements)
147 Total_Frees
: Byte_Count
;
148 -- Size of the memory allocated at Traceback, currently freed since last
149 -- Reset call. (only for Alloc & Indirect_Alloc elements)
151 Next
: Traceback_Htable_Elem_Ptr
;
154 -- Subprograms used for the Backtrace_Htable instantiation
157 (E
: Traceback_Htable_Elem_Ptr
;
158 Next
: Traceback_Htable_Elem_Ptr
);
159 pragma Inline
(Set_Next
);
162 (E
: Traceback_Htable_Elem_Ptr
) return Traceback_Htable_Elem_Ptr
;
163 pragma Inline
(Next
);
166 (E
: Traceback_Htable_Elem_Ptr
) return Tracebacks_Array_Access
;
167 pragma Inline
(Get_Key
);
169 function Hash
(T
: Tracebacks_Array_Access
) return Header
;
170 pragma Inline
(Hash
);
172 function Equal
(K1
, K2
: Tracebacks_Array_Access
) return Boolean;
173 -- Why is this not inlined???
175 -- The hash table for back traces
177 package Backtrace_Htable
is new GNAT
.HTable
.Static_HTable
178 (Header_Num
=> Header
,
179 Element
=> Traceback_Htable_Elem
,
180 Elmt_Ptr
=> Traceback_Htable_Elem_Ptr
,
182 Set_Next
=> Set_Next
,
184 Key
=> Tracebacks_Array_Access
,
189 -----------------------
190 -- Allocations table --
191 -----------------------
193 type Allocation_Header
;
194 type Allocation_Header_Access
is access Allocation_Header
;
196 type Traceback_Ptr_Or_Address
is new System
.Address
;
197 -- A type that acts as a C union, and is either a System.Address or a
198 -- Traceback_Htable_Elem_Ptr.
200 -- The following record stores extra information that needs to be
201 -- memorized for each block allocated with the special debug pool.
203 type Allocation_Header
is record
204 Allocation_Address
: System
.Address
;
205 -- Address of the block returned by malloc, possibly unaligned
207 Block_Size
: Storage_Offset
;
208 -- Needed only for advanced freeing algorithms (traverse all allocated
209 -- blocks for potential references). This value is negated when the
210 -- chunk of memory has been logically freed by the application. This
211 -- chunk has not been physically released yet.
213 Alloc_Traceback
: Traceback_Htable_Elem_Ptr
;
214 -- ??? comment required
216 Dealloc_Traceback
: Traceback_Ptr_Or_Address
;
217 -- Pointer to the traceback for the allocation (if the memory chunk is
218 -- still valid), or to the first deallocation otherwise. Make sure this
219 -- is a thin pointer to save space.
221 -- Dealloc_Traceback is also for blocks that are still allocated to
222 -- point to the previous block in the list. This saves space in this
223 -- header, and make manipulation of the lists of allocated pointers
226 Next
: System
.Address
;
227 -- Point to the next block of the same type (either allocated or
228 -- logically freed) in memory. This points to the beginning of the user
229 -- data, and does not include the header of that block.
233 (Address
: System
.Address
) return Allocation_Header_Access
;
234 pragma Inline
(Header_Of
);
235 -- Return the header corresponding to a previously allocated address
237 function To_Address
is new Ada
.Unchecked_Conversion
238 (Traceback_Ptr_Or_Address
, System
.Address
);
240 function To_Address
is new Ada
.Unchecked_Conversion
241 (System
.Address
, Traceback_Ptr_Or_Address
);
243 function To_Traceback
is new Ada
.Unchecked_Conversion
244 (Traceback_Ptr_Or_Address
, Traceback_Htable_Elem_Ptr
);
246 function To_Traceback
is new Ada
.Unchecked_Conversion
247 (Traceback_Htable_Elem_Ptr
, Traceback_Ptr_Or_Address
);
249 Header_Offset
: constant Storage_Count
:=
250 (Allocation_Header
'Object_Size / System
.Storage_Unit
);
251 -- Offset, in bytes, from start of allocation Header to start of User
252 -- data. The start of user data is assumed to be aligned at least as much
253 -- as what the header type requires, so applying this offset yields a
254 -- suitably aligned address as well.
256 Extra_Allocation
: constant Storage_Count
:=
257 (Storage_Alignment
- 1 + Header_Offset
);
258 -- Amount we need to secure in addition to the user data for a given
259 -- allocation request: room for the allocation header plus worst-case
260 -- alignment padding.
262 -----------------------
263 -- Local subprograms --
264 -----------------------
266 function Align
(Addr
: Integer_Address
) return Integer_Address
;
267 pragma Inline
(Align
);
268 -- Return the next address aligned on Storage_Alignment from Addr.
270 function Find_Or_Create_Traceback
272 Kind
: Traceback_Kind
;
273 Size
: Storage_Count
;
274 Ignored_Frame_Start
: System
.Address
;
275 Ignored_Frame_End
: System
.Address
) return Traceback_Htable_Elem_Ptr
;
276 -- Return an element matching the current traceback (omitting the frames
277 -- that are in the current package). If this traceback already existed in
278 -- the htable, a pointer to this is returned to spare memory. Null is
279 -- returned if the pool is set not to store tracebacks. If the traceback
280 -- already existed in the table, the count is incremented so that
281 -- Dump_Tracebacks returns useful results. All addresses up to, and
282 -- including, an address between Ignored_Frame_Start .. Ignored_Frame_End
285 function Output_File
(Pool
: Debug_Pool
) return File_Type
;
286 pragma Inline
(Output_File
);
287 -- Returns file_type on which error messages have to be generated for Pool
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 File. If Traceback is null, print the call_chain
296 -- at the current location, up to Depth levels, ignoring all addresses
297 -- up to the first one in the range:
298 -- Ignored_Frame_Start .. Ignored_Frame_End
300 procedure Stdout_Put
(S
: String);
301 -- Wrapper for Put that ensures we always write to stdout instead of the
302 -- current output file defined in GNAT.IO.
304 procedure Stdout_Put_Line
(S
: String);
305 -- Wrapper for Put_Line that ensures we always write to stdout instead of
306 -- the current output file defined in GNAT.IO.
308 procedure Print_Traceback
309 (Output_File
: File_Type
;
311 Traceback
: Traceback_Htable_Elem_Ptr
);
312 -- Output Prefix & Traceback & EOL. Print nothing if Traceback is null.
314 procedure Print_Address
(File
: File_Type
; Addr
: Address
);
315 -- Output System.Address without using secondary stack.
316 -- When System.Memory uses Debug_Pool, secondary stack cannot be used
317 -- during Allocate calls, as some Allocate calls are done to
318 -- register/initialize a secondary stack for a foreign thread.
319 -- During these calls, the secondary stack is not available yet.
322 function Is_Handled
(Storage
: System
.Address
) return Boolean;
323 pragma Inline
(Is_Handled
);
324 -- Return True if Storage is the address of a block that the debug pool
325 -- already had under its control. Used to allow System.Memory to use
328 function Is_Valid
(Storage
: System
.Address
) return Boolean;
329 pragma Inline
(Is_Valid
);
330 -- Return True if Storage is the address of a block that the debug pool
331 -- has under its control, in which case Header_Of may be used to access
332 -- the associated allocation header.
334 procedure Set_Valid
(Storage
: System
.Address
; Value
: Boolean);
335 pragma Inline
(Set_Valid
);
336 -- Mark the address Storage as being under control of the memory pool
337 -- (if Value is True), or not (if Value is False).
339 Validity_Count
: Byte_Count
:= 0;
340 -- Total number of validity elements
346 procedure Set_Dead_Beef
347 (Storage_Address
: System
.Address
;
348 Size_In_Storage_Elements
: Storage_Count
);
349 -- Set the contents of the memory block pointed to by Storage_Address to
350 -- the 16#DEADBEEF# pattern. If Size_In_Storage_Elements is not a multiple
351 -- of the length of this pattern, the last instance may be partial.
353 procedure Free_Physically
(Pool
: in out Debug_Pool
);
354 -- Start to physically release some memory to the system, until the amount
355 -- of logically (but not physically) freed memory is lower than the
356 -- expected amount in Pool.
358 procedure Allocate_End
;
359 procedure Deallocate_End
;
360 procedure Dereference_End
;
361 -- These procedures are used as markers when computing the stacktraces,
362 -- so that addresses in the debug pool itself are not reported to the user.
364 Code_Address_For_Allocate_End
: System
.Address
;
365 Code_Address_For_Deallocate_End
: System
.Address
;
366 Code_Address_For_Dereference_End
: System
.Address
;
367 -- Taking the address of the above procedures will not work on some
368 -- architectures (HPUX for instance). Thus we do the same thing that
369 -- is done in a-except.adb, and get the address of labels instead.
371 procedure Skip_Levels
373 Trace
: Tracebacks_Array
;
375 Len
: in out Natural;
376 Ignored_Frame_Start
: System
.Address
;
377 Ignored_Frame_End
: System
.Address
);
378 -- Set Start .. Len to the range of values from Trace that should be output
379 -- to the user. This range of values excludes any address prior to the
380 -- first one in Ignored_Frame_Start .. Ignored_Frame_End (basically
381 -- addresses internal to this package). Depth is the number of levels that
382 -- the user is interested in.
384 package STBE
renames System
.Traceback_Entries
;
386 function PC_For
(TB_Entry
: STBE
.Traceback_Entry
) return System
.Address
393 function Align
(Addr
: Integer_Address
) return Integer_Address
is
394 Factor
: constant Integer_Address
:= Storage_Alignment
;
396 return ((Addr
+ Factor
- 1) / Factor
) * Factor
;
403 function Header_Of
(Address
: System
.Address
)
404 return Allocation_Header_Access
406 function Convert
is new Ada
.Unchecked_Conversion
407 (System
.Address
, Allocation_Header_Access
);
409 return Convert
(Address
- Header_Offset
);
417 (E
: Traceback_Htable_Elem_Ptr
;
418 Next
: Traceback_Htable_Elem_Ptr
)
429 (E
: Traceback_Htable_Elem_Ptr
) return Traceback_Htable_Elem_Ptr
is
438 function Equal
(K1
, K2
: Tracebacks_Array_Access
) return Boolean is
439 use type Tracebacks_Array
;
441 return K1
.all = K2
.all;
449 (E
: Traceback_Htable_Elem_Ptr
) return Tracebacks_Array_Access
459 function Hash
(T
: Tracebacks_Array_Access
) return Header
is
460 Result
: Integer_Address
:= 0;
463 for X
in T
'Range loop
464 Result
:= Result
+ To_Integer
(PC_For
(T
(X
)));
467 return Header
(1 + Result
mod Integer_Address
(Header
'Last));
474 function Output_File
(Pool
: Debug_Pool
) return File_Type
is
476 if Pool
.Errors_To_Stdout
then
477 return Standard_Output
;
479 return Standard_Error
;
487 procedure Print_Address
(File
: File_Type
; Addr
: Address
) is
489 -- Warning: secondary stack cannot be used here. When System.Memory
490 -- implementation uses Debug_Pool, Print_Address can be called during
491 -- secondary stack creation for foreign threads.
493 Put
(File
, Image_C
(Addr
));
503 Traceback
: Tracebacks_Array_Access
;
504 Ignored_Frame_Start
: System
.Address
:= System
.Null_Address
;
505 Ignored_Frame_End
: System
.Address
:= System
.Null_Address
)
507 procedure Print
(Tr
: Tracebacks_Array
);
508 -- Print the traceback to standard_output
514 procedure Print
(Tr
: Tracebacks_Array
) is
516 for J
in Tr
'Range loop
517 Print_Address
(File
, PC_For
(Tr
(J
)));
520 Put
(File
, ASCII
.LF
);
523 -- Start of processing for Put_Line
526 if Traceback
= null then
530 Trace
: aliased Tracebacks_Array
(1 .. Depth
+ Max_Ignored_Levels
);
533 Call_Chain
(Trace
, Len
);
539 Ignored_Frame_Start
=> Ignored_Frame_Start
,
540 Ignored_Frame_End
=> Ignored_Frame_End
);
541 Print
(Trace
(Start
.. Len
));
545 Print
(Traceback
.all);
553 procedure Skip_Levels
555 Trace
: Tracebacks_Array
;
557 Len
: in out Natural;
558 Ignored_Frame_Start
: System
.Address
;
559 Ignored_Frame_End
: System
.Address
)
562 Start
:= Trace
'First;
565 and then (PC_For
(Trace
(Start
)) < Ignored_Frame_Start
566 or else PC_For
(Trace
(Start
)) > Ignored_Frame_End
)
573 -- Just in case: make sure we have a traceback even if Ignore_Till
580 if Len
- Start
+ 1 > Depth
then
581 Len
:= Depth
+ Start
- 1;
585 ------------------------------
586 -- Find_Or_Create_Traceback --
587 ------------------------------
589 function Find_Or_Create_Traceback
591 Kind
: Traceback_Kind
;
592 Size
: Storage_Count
;
593 Ignored_Frame_Start
: System
.Address
;
594 Ignored_Frame_End
: System
.Address
) return Traceback_Htable_Elem_Ptr
597 if Pool
.Stack_Trace_Depth
= 0 then
602 Disable_Exit_Value
: constant Boolean := Disable
;
604 Elem
: Traceback_Htable_Elem_Ptr
;
607 Trace
: aliased Tracebacks_Array
608 (1 .. Integer (Pool
.Stack_Trace_Depth
) +
613 Call_Chain
(Trace
, Len
);
615 (Depth
=> Pool
.Stack_Trace_Depth
,
619 Ignored_Frame_Start
=> Ignored_Frame_Start
,
620 Ignored_Frame_End
=> Ignored_Frame_End
);
622 -- Check if the traceback is already in the table
625 Backtrace_Htable
.Get
(Trace
(Start
.. Len
)'Unrestricted_Access);
631 new Traceback_Htable_Elem
'
633 new Tracebacks_Array'(Trace
(Start
.. Len
)),
636 Total
=> Byte_Count
(Size
),
640 Traceback_Count
:= Traceback_Count
+ 1;
641 Backtrace_Htable
.Set
(Elem
);
644 Elem
.Count
:= Elem
.Count
+ 1;
645 Elem
.Total
:= Elem
.Total
+ Byte_Count
(Size
);
648 Disable
:= Disable_Exit_Value
;
652 Disable
:= Disable_Exit_Value
;
655 end Find_Or_Create_Traceback
;
661 package body Validity
is
663 -- The validity bits of the allocated blocks are kept in a has table.
664 -- Each component of the hash table contains the validity bits for a
665 -- 16 Mbyte memory chunk.
667 -- The reason the validity bits are kept for chunks of memory rather
668 -- than in a big array is that on some 64 bit platforms, it may happen
669 -- that two chunk of allocated data are very far from each other.
671 Memory_Chunk_Size
: constant Integer_Address
:= 2 ** 24; -- 16 MB
672 Validity_Divisor
: constant := Storage_Alignment
* System
.Storage_Unit
;
674 Max_Validity_Byte_Index
: constant :=
675 Memory_Chunk_Size
/ Validity_Divisor
;
677 subtype Validity_Byte_Index
is
678 Integer_Address
range 0 .. Max_Validity_Byte_Index
- 1;
680 type Byte
is mod 2 ** System
.Storage_Unit
;
682 type Validity_Bits_Part
is array (Validity_Byte_Index
) of Byte
;
683 type Validity_Bits_Part_Ref
is access all Validity_Bits_Part
;
684 No_Validity_Bits_Part
: constant Validity_Bits_Part_Ref
:= null;
686 type Validity_Bits
is record
687 Valid
: Validity_Bits_Part_Ref
:= No_Validity_Bits_Part
;
688 -- True if chunk of memory at this address is currently allocated
690 Handled
: Validity_Bits_Part_Ref
:= No_Validity_Bits_Part
;
691 -- True if chunk of memory at this address was allocated once after
692 -- Allow_Unhandled_Memory was set to True. Used to know on Deallocate
693 -- if chunk of memory should be handled a block allocated by this
698 type Validity_Bits_Ref
is access all Validity_Bits
;
699 No_Validity_Bits
: constant Validity_Bits_Ref
:= null;
701 Max_Header_Num
: constant := 1023;
703 type Header_Num
is range 0 .. Max_Header_Num
- 1;
705 function Hash
(F
: Integer_Address
) return Header_Num
;
707 function Is_Valid_Or_Handled
708 (Storage
: System
.Address
;
709 Valid
: Boolean) return Boolean;
710 pragma Inline
(Is_Valid_Or_Handled
);
711 -- Internal implementation of Is_Valid and Is_Handled.
712 -- Valid is used to select Valid or Handled arrays.
714 package Validy_Htable
is new GNAT
.HTable
.Simple_HTable
715 (Header_Num
=> Header_Num
,
716 Element
=> Validity_Bits_Ref
,
717 No_Element
=> No_Validity_Bits
,
718 Key
=> Integer_Address
,
721 -- Table to keep the validity and handled bit blocks for the allocated
724 function To_Pointer
is new Ada
.Unchecked_Conversion
725 (System
.Address
, Validity_Bits_Part_Ref
);
727 procedure Memset
(A
: Address
; C
: Integer; N
: size_t
);
728 pragma Import
(C
, Memset
, "memset");
734 function Hash
(F
: Integer_Address
) return Header_Num
is
736 return Header_Num
(F
mod Max_Header_Num
);
739 -------------------------
740 -- Is_Valid_Or_Handled --
741 -------------------------
743 function Is_Valid_Or_Handled
744 (Storage
: System
.Address
;
745 Valid
: Boolean) return Boolean is
746 Int_Storage
: constant Integer_Address
:= To_Integer
(Storage
);
749 -- The pool only returns addresses aligned on Storage_Alignment so
750 -- anything off cannot be a valid block address and we can return
751 -- early in this case. We actually have to since our data structures
752 -- map validity bits for such aligned addresses only.
754 if Int_Storage
mod Storage_Alignment
/= 0 then
759 Block_Number
: constant Integer_Address
:=
760 Int_Storage
/ Memory_Chunk_Size
;
761 Ptr
: constant Validity_Bits_Ref
:=
762 Validy_Htable
.Get
(Block_Number
);
763 Offset
: constant Integer_Address
:=
765 (Block_Number
* Memory_Chunk_Size
)) /
767 Bit
: constant Byte
:=
768 2 ** Natural (Offset
mod System
.Storage_Unit
);
770 if Ptr
= No_Validity_Bits
then
774 return (Ptr
.Valid
(Offset
/ System
.Storage_Unit
)
777 if Ptr
.Handled
= No_Validity_Bits_Part
then
780 return (Ptr
.Handled
(Offset
/ System
.Storage_Unit
)
786 end Is_Valid_Or_Handled
;
792 function Is_Valid
(Storage
: System
.Address
) return Boolean is
794 return Is_Valid_Or_Handled
(Storage
=> Storage
, Valid
=> True);
801 function Is_Handled
(Storage
: System
.Address
) return Boolean is
803 return Is_Valid_Or_Handled
(Storage
=> Storage
, Valid
=> False);
810 procedure Set_Valid
(Storage
: System
.Address
; Value
: Boolean) is
811 Int_Storage
: constant Integer_Address
:= To_Integer
(Storage
);
812 Block_Number
: constant Integer_Address
:=
813 Int_Storage
/ Memory_Chunk_Size
;
814 Ptr
: Validity_Bits_Ref
:= Validy_Htable
.Get
(Block_Number
);
815 Offset
: constant Integer_Address
:=
816 (Int_Storage
- (Block_Number
* Memory_Chunk_Size
)) /
818 Bit
: constant Byte
:=
819 2 ** Natural (Offset
mod System
.Storage_Unit
);
821 procedure Set_Handled
;
822 pragma Inline
(Set_Handled
);
823 -- if Allow_Unhandled_Memory set Handled bit in table.
829 procedure Set_Handled
is
831 if Allow_Unhandled_Memory
then
832 if Ptr
.Handled
= No_Validity_Bits_Part
then
834 To_Pointer
(Alloc
(size_t
(Max_Validity_Byte_Index
)));
836 (A
=> Ptr
.Handled
.all'Address,
838 N
=> size_t
(Max_Validity_Byte_Index
));
841 Ptr
.Handled
(Offset
/ System
.Storage_Unit
) :=
842 Ptr
.Handled
(Offset
/ System
.Storage_Unit
) or Bit
;
846 -- Start of processing for Set_Valid
849 if Ptr
= No_Validity_Bits
then
851 -- First time in this memory area: allocate a new block and put
855 Ptr
:= new Validity_Bits
;
856 Validity_Count
:= Validity_Count
+ 1;
858 To_Pointer
(Alloc
(size_t
(Max_Validity_Byte_Index
)));
859 Validy_Htable
.Set
(Block_Number
, Ptr
);
861 (A
=> Ptr
.Valid
.all'Address,
863 N
=> size_t
(Max_Validity_Byte_Index
));
864 Ptr
.Valid
(Offset
/ System
.Storage_Unit
) := Bit
;
870 Ptr
.Valid
(Offset
/ System
.Storage_Unit
) :=
871 Ptr
.Valid
(Offset
/ System
.Storage_Unit
) or Bit
;
874 Ptr
.Valid
(Offset
/ System
.Storage_Unit
) :=
875 Ptr
.Valid
(Offset
/ System
.Storage_Unit
) and (not Bit
);
886 (Pool
: in out Debug_Pool
;
887 Storage_Address
: out Address
;
888 Size_In_Storage_Elements
: Storage_Count
;
889 Alignment
: Storage_Count
)
891 pragma Unreferenced
(Alignment
);
892 -- Ignored, we always force Storage_Alignment
894 type Local_Storage_Array
is new Storage_Array
895 (1 .. Size_In_Storage_Elements
+ Extra_Allocation
);
897 type Ptr
is access Local_Storage_Array
;
898 -- On some systems, we might want to physically protect pages against
899 -- writing when they have been freed (of course, this is expensive in
900 -- terms of wasted memory). To do that, all we should have to do it to
901 -- set the size of this array to the page size. See mprotect().
903 Current
: Byte_Count
;
905 Trace
: Traceback_Htable_Elem_Ptr
;
907 Reset_Disable_At_Exit
: Boolean := False;
915 System
.CRTL
.malloc
(System
.CRTL
.size_t
(Size_In_Storage_Elements
));
920 Reset_Disable_At_Exit
:= True;
923 Pool
.Alloc_Count
:= Pool
.Alloc_Count
+ 1;
925 -- If necessary, start physically releasing memory. The reason this is
926 -- done here, although Pool.Logically_Deallocated has not changed above,
927 -- is so that we do this only after a series of deallocations (e.g loop
928 -- that deallocates a big array). If we were doing that in Deallocate,
929 -- we might be physically freeing memory several times during the loop,
930 -- which is expensive.
932 if Pool
.Logically_Deallocated
>
933 Byte_Count
(Pool
.Maximum_Logically_Freed_Memory
)
935 Free_Physically
(Pool
);
938 -- Use standard (i.e. through malloc) allocations. This automatically
939 -- raises Storage_Error if needed. We also try once more to physically
940 -- release memory, so that even marked blocks, in the advanced scanning,
941 -- are freed. Note that we do not initialize the storage array since it
942 -- is not necessary to do so (however this will cause bogus valgrind
943 -- warnings, which should simply be ignored).
946 P
:= new Local_Storage_Array
;
949 when Storage_Error
=>
950 Free_Physically
(Pool
);
951 P
:= new Local_Storage_Array
;
954 -- Compute Storage_Address, aimed at receiving user data. We need room
955 -- for the allocation header just ahead of the user data space plus
956 -- alignment padding so Storage_Address is aligned on Storage_Alignment,
959 -- Storage_Address, aligned
960 -- on Storage_Alignment
962 -- | ~~~~ | Header | User data ... |
966 -- Header_Offset is fixed so moving back and forth between user data
967 -- and allocation header is straightforward. The value is also such
968 -- that the header type alignment is honored when starting from
969 -- Default_alignment.
971 -- For the purpose of computing Storage_Address, we just do as if the
972 -- header was located first, followed by the alignment padding:
975 To_Address
(Align
(To_Integer
(P
.all'Address) +
976 Integer_Address
(Header_Offset
)));
977 -- Computation is done in Integer_Address, not Storage_Offset, because
978 -- the range of Storage_Offset may not be large enough.
980 pragma Assert
((Storage_Address
- System
.Null_Address
)
981 mod Storage_Alignment
= 0);
982 pragma Assert
(Storage_Address
+ Size_In_Storage_Elements
983 <= P
.all'Address + P
'Length);
986 Find_Or_Create_Traceback
989 Size
=> Size_In_Storage_Elements
,
990 Ignored_Frame_Start
=> Allocate_Label
'Address,
991 Ignored_Frame_End
=> Code_Address_For_Allocate_End
);
993 pragma Warnings
(Off
);
994 -- Turn warning on alignment for convert call off. We know that in fact
995 -- this conversion is safe since P itself is always aligned on
996 -- Storage_Alignment.
998 Header_Of
(Storage_Address
).all :=
999 (Allocation_Address
=> P
.all'Address,
1000 Alloc_Traceback
=> Trace
,
1001 Dealloc_Traceback
=> To_Traceback
(null),
1002 Next
=> Pool
.First_Used_Block
,
1003 Block_Size
=> Size_In_Storage_Elements
);
1005 pragma Warnings
(On
);
1007 -- Link this block in the list of used blocks. This will be used to list
1008 -- memory leaks in Print_Info, and for the advanced schemes of
1009 -- Physical_Free, where we want to traverse all allocated blocks and
1010 -- search for possible references.
1012 -- We insert in front, since most likely we'll be freeing the most
1013 -- recently allocated blocks first (the older one might stay allocated
1014 -- for the whole life of the application).
1016 if Pool
.First_Used_Block
/= System
.Null_Address
then
1017 Header_Of
(Pool
.First_Used_Block
).Dealloc_Traceback
:=
1018 To_Address
(Storage_Address
);
1021 Pool
.First_Used_Block
:= Storage_Address
;
1023 -- Mark the new address as valid
1025 Set_Valid
(Storage_Address
, True);
1027 if Pool
.Low_Level_Traces
then
1028 Put
(Output_File
(Pool
),
1030 & Storage_Count
'Image (Size_In_Storage_Elements
)
1032 Print_Address
(Output_File
(Pool
), Storage_Address
);
1033 Put
(Output_File
(Pool
),
1035 & Storage_Count
'Image (Local_Storage_Array
'Length)
1037 Print_Address
(Output_File
(Pool
), P
.all'Address);
1038 Put
(Output_File
(Pool
),
1040 Put_Line
(Output_File
(Pool
), Pool
.Stack_Trace_Depth
, null,
1041 Allocate_Label
'Address,
1042 Code_Address_For_Deallocate_End
);
1045 -- Update internal data
1048 Pool
.Allocated
+ Byte_Count
(Size_In_Storage_Elements
);
1050 Current
:= Pool
.Current_Water_Mark
;
1052 if Current
> Pool
.High_Water
then
1053 Pool
.High_Water
:= Current
;
1062 if Reset_Disable_At_Exit
then
1073 -- DO NOT MOVE, this must be right after Allocate. This is similar to what
1074 -- is done in a-except, so that we can hide the traceback frames internal
1077 procedure Allocate_End
is
1079 <<Allocate_End_Label
>>
1080 Code_Address_For_Allocate_End
:= Allocate_End_Label
'Address;
1087 procedure Set_Dead_Beef
1088 (Storage_Address
: System
.Address
;
1089 Size_In_Storage_Elements
: Storage_Count
)
1091 Dead_Bytes
: constant := 4;
1093 type Data
is mod 2 ** (Dead_Bytes
* 8);
1094 for Data
'Size use Dead_Bytes
* 8;
1096 Dead
: constant Data
:= 16#DEAD_BEEF#
;
1098 type Dead_Memory
is array
1099 (1 .. Size_In_Storage_Elements
/ Dead_Bytes
) of Data
;
1100 type Mem_Ptr
is access Dead_Memory
;
1102 type Byte
is mod 2 ** 8;
1103 for Byte
'Size use 8;
1105 type Dead_Memory_Bytes
is array (0 .. 2) of Byte
;
1106 type Dead_Memory_Bytes_Ptr
is access Dead_Memory_Bytes
;
1108 function From_Ptr
is new Ada
.Unchecked_Conversion
1109 (System
.Address
, Mem_Ptr
);
1111 function From_Ptr
is new Ada
.Unchecked_Conversion
1112 (System
.Address
, Dead_Memory_Bytes_Ptr
);
1114 M
: constant Mem_Ptr
:= From_Ptr
(Storage_Address
);
1115 M2
: Dead_Memory_Bytes_Ptr
;
1116 Modulo
: constant Storage_Count
:=
1117 Size_In_Storage_Elements
mod Dead_Bytes
;
1119 M
.all := (others => Dead
);
1121 -- Any bytes left (up to three of them)
1124 M2
:= From_Ptr
(Storage_Address
+ M
'Length * Dead_Bytes
);
1137 ---------------------
1138 -- Free_Physically --
1139 ---------------------
1141 procedure Free_Physically
(Pool
: in out Debug_Pool
) is
1142 type Byte
is mod 256;
1143 type Byte_Access
is access Byte
;
1145 function To_Byte
is new Ada
.Unchecked_Conversion
1146 (System
.Address
, Byte_Access
);
1148 type Address_Access
is access System
.Address
;
1150 function To_Address_Access
is new Ada
.Unchecked_Conversion
1151 (System
.Address
, Address_Access
);
1153 In_Use_Mark
: constant Byte
:= 16#D#
;
1154 Free_Mark
: constant Byte
:= 16#F#
;
1156 Total_Freed
: Storage_Count
:= 0;
1158 procedure Reset_Marks
;
1159 -- Unmark all the logically freed blocks, so that they are considered
1160 -- for physical deallocation
1163 (H
: Allocation_Header_Access
; A
: System
.Address
; In_Use
: Boolean);
1164 -- Mark the user data block starting at A. For a block of size zero,
1165 -- nothing is done. For a block with a different size, the first byte
1166 -- is set to either "D" (in use) or "F" (free).
1168 function Marked
(A
: System
.Address
) return Boolean;
1169 -- Return true if the user data block starting at A might be in use
1172 procedure Mark_Blocks
;
1173 -- Traverse all allocated blocks, and search for possible references
1174 -- to logically freed blocks. Mark them appropriately
1176 procedure Free_Blocks
(Ignore_Marks
: Boolean);
1177 -- Physically release blocks. Only the blocks that haven't been marked
1178 -- will be released, unless Ignore_Marks is true.
1184 procedure Free_Blocks
(Ignore_Marks
: Boolean) is
1185 Header
: Allocation_Header_Access
;
1186 Tmp
: System
.Address
:= Pool
.First_Free_Block
;
1187 Next
: System
.Address
;
1188 Previous
: System
.Address
:= System
.Null_Address
;
1191 while Tmp
/= System
.Null_Address
1193 not (Total_Freed
> Pool
.Minimum_To_Free
1194 and Pool
.Logically_Deallocated
<
1195 Byte_Count
(Pool
.Maximum_Logically_Freed_Memory
))
1197 Header
:= Header_Of
(Tmp
);
1199 -- If we know, or at least assume, the block is no longer
1200 -- referenced anywhere, we can free it physically.
1202 if Ignore_Marks
or else not Marked
(Tmp
) then
1204 pragma Suppress
(All_Checks
);
1205 -- Suppress the checks on this section. If they are overflow
1206 -- errors, it isn't critical, and we'd rather avoid a
1207 -- Constraint_Error in that case.
1210 -- Note that block_size < zero for freed blocks
1212 Pool
.Physically_Deallocated
:=
1213 Pool
.Physically_Deallocated
-
1214 Byte_Count
(Header
.Block_Size
);
1216 Pool
.Logically_Deallocated
:=
1217 Pool
.Logically_Deallocated
+
1218 Byte_Count
(Header
.Block_Size
);
1220 Total_Freed
:= Total_Freed
- Header
.Block_Size
;
1223 Next
:= Header
.Next
;
1225 if Pool
.Low_Level_Traces
then
1227 (Output_File
(Pool
),
1228 "info: Freeing physical memory "
1229 & Storage_Count
'Image
1230 ((abs Header
.Block_Size
) + Extra_Allocation
)
1232 Print_Address
(Output_File
(Pool
),
1233 Header
.Allocation_Address
);
1234 Put_Line
(Output_File
(Pool
), "");
1237 if System_Memory_Debug_Pool_Enabled
then
1238 System
.CRTL
.free
(Header
.Allocation_Address
);
1240 System
.Memory
.Free
(Header
.Allocation_Address
);
1243 Set_Valid
(Tmp
, False);
1245 -- Remove this block from the list
1247 if Previous
= System
.Null_Address
then
1248 Pool
.First_Free_Block
:= Next
;
1250 Header_Of
(Previous
).Next
:= Next
;
1267 (H
: Allocation_Header_Access
;
1272 if H
.Block_Size
/= 0 then
1273 To_Byte
(A
).all := (if In_Use
then In_Use_Mark
else Free_Mark
);
1281 procedure Mark_Blocks
is
1282 Tmp
: System
.Address
:= Pool
.First_Used_Block
;
1283 Previous
: System
.Address
;
1284 Last
: System
.Address
;
1285 Pointed
: System
.Address
;
1286 Header
: Allocation_Header_Access
;
1289 -- For each allocated block, check its contents. Things that look
1290 -- like a possible address are used to mark the blocks so that we try
1291 -- and keep them, for better detection in case of invalid access.
1292 -- This mechanism is far from being fool-proof: it doesn't check the
1293 -- stacks of the threads, doesn't check possible memory allocated not
1294 -- under control of this debug pool. But it should allow us to catch
1297 while Tmp
/= System
.Null_Address
loop
1299 Last
:= Tmp
+ Header_Of
(Tmp
).Block_Size
;
1300 while Previous
< Last
loop
1301 -- ??? Should we move byte-per-byte, or consider that addresses
1302 -- are always aligned on 4-bytes boundaries ? Let's use the
1305 Pointed
:= To_Address_Access
(Previous
).all;
1306 if Is_Valid
(Pointed
) then
1307 Header
:= Header_Of
(Pointed
);
1309 -- Do not even attempt to mark blocks in use. That would
1310 -- screw up the whole application, of course.
1312 if Header
.Block_Size
< 0 then
1313 Mark
(Header
, Pointed
, In_Use
=> True);
1317 Previous
:= Previous
+ System
.Address
'Size;
1320 Tmp
:= Header_Of
(Tmp
).Next
;
1328 function Marked
(A
: System
.Address
) return Boolean is
1330 return To_Byte
(A
).all = In_Use_Mark
;
1337 procedure Reset_Marks
is
1338 Current
: System
.Address
:= Pool
.First_Free_Block
;
1339 Header
: Allocation_Header_Access
;
1341 while Current
/= System
.Null_Address
loop
1342 Header
:= Header_Of
(Current
);
1343 Mark
(Header
, Current
, False);
1344 Current
:= Header
.Next
;
1348 -- Start of processing for Free_Physically
1353 if Pool
.Advanced_Scanning
then
1355 -- Reset the mark for each freed block
1362 Free_Blocks
(Ignore_Marks
=> not Pool
.Advanced_Scanning
);
1364 -- The contract is that we need to free at least Minimum_To_Free bytes,
1365 -- even if this means freeing marked blocks in the advanced scheme
1367 if Total_Freed
< Pool
.Minimum_To_Free
1368 and then Pool
.Advanced_Scanning
1370 Pool
.Marked_Blocks_Deallocated
:= True;
1371 Free_Blocks
(Ignore_Marks
=> True);
1380 end Free_Physically
;
1387 (Storage_Address
: Address
;
1388 Size_In_Storage_Elements
: out Storage_Count
;
1389 Valid
: out Boolean) is
1393 Valid
:= Is_Valid
(Storage_Address
);
1395 if Is_Valid
(Storage_Address
) then
1397 Header
: constant Allocation_Header_Access
:=
1398 Header_Of
(Storage_Address
);
1400 if Header
.Block_Size
>= 0 then
1402 Size_In_Storage_Elements
:= Header
.Block_Size
;
1420 ---------------------
1421 -- Print_Traceback --
1422 ---------------------
1424 procedure Print_Traceback
1425 (Output_File
: File_Type
;
1427 Traceback
: Traceback_Htable_Elem_Ptr
) is
1429 if Traceback
/= null then
1430 Put
(Output_File
, Prefix
);
1431 Put_Line
(Output_File
, 0, Traceback
.Traceback
);
1433 end Print_Traceback
;
1439 procedure Deallocate
1440 (Pool
: in out Debug_Pool
;
1441 Storage_Address
: Address
;
1442 Size_In_Storage_Elements
: Storage_Count
;
1443 Alignment
: Storage_Count
)
1445 pragma Unreferenced
(Alignment
);
1447 Unlock_Task_Required
: Boolean := False;
1448 Header
: constant Allocation_Header_Access
:=
1449 Header_Of
(Storage_Address
);
1451 Previous
: System
.Address
;
1454 <<Deallocate_Label
>>
1456 Unlock_Task_Required
:= True;
1457 Valid
:= Is_Valid
(Storage_Address
);
1460 Unlock_Task_Required
:= False;
1463 if Storage_Address
= System
.Null_Address
then
1464 if Pool
.Raise_Exceptions
and then
1465 Size_In_Storage_Elements
/= Storage_Count
'Last
1467 raise Freeing_Not_Allocated_Storage
;
1469 Put
(Output_File
(Pool
),
1470 "error: Freeing Null_Address, at ");
1471 Put_Line
(Output_File
(Pool
), Pool
.Stack_Trace_Depth
, null,
1472 Deallocate_Label
'Address,
1473 Code_Address_For_Deallocate_End
);
1478 if Allow_Unhandled_Memory
and then not Is_Handled
(Storage_Address
)
1480 System
.CRTL
.free
(Storage_Address
);
1484 if Pool
.Raise_Exceptions
and then
1485 Size_In_Storage_Elements
/= Storage_Count
'Last
1487 raise Freeing_Not_Allocated_Storage
;
1489 Put
(Output_File
(Pool
),
1490 "error: Freeing not allocated storage, at ");
1491 Put_Line
(Output_File
(Pool
), Pool
.Stack_Trace_Depth
, null,
1492 Deallocate_Label
'Address,
1493 Code_Address_For_Deallocate_End
);
1496 elsif Header
.Block_Size
< 0 then
1497 Unlock_Task_Required
:= False;
1499 if Pool
.Raise_Exceptions
then
1500 raise Freeing_Deallocated_Storage
;
1502 Put
(Output_File
(Pool
),
1503 "error: Freeing already deallocated storage, at ");
1504 Put_Line
(Output_File
(Pool
), Pool
.Stack_Trace_Depth
, null,
1505 Deallocate_Label
'Address,
1506 Code_Address_For_Deallocate_End
);
1507 Print_Traceback
(Output_File
(Pool
),
1508 " Memory already deallocated at ",
1509 To_Traceback
(Header
.Dealloc_Traceback
));
1510 Print_Traceback
(Output_File
(Pool
), " Memory was allocated at ",
1511 Header
.Alloc_Traceback
);
1515 -- Some sort of codegen problem or heap corruption caused the
1516 -- Size_In_Storage_Elements to be wrongly computed.
1517 -- The code below is all based on the assumption that Header.all
1518 -- is not corrupted, such that the error is non-fatal.
1520 if Header
.Block_Size
/= Size_In_Storage_Elements
and then
1521 Size_In_Storage_Elements
/= Storage_Count
'Last
1523 Put_Line
(Output_File
(Pool
),
1524 "error: Deallocate size "
1525 & Storage_Count
'Image (Size_In_Storage_Elements
)
1526 & " does not match allocate size "
1527 & Storage_Count
'Image (Header
.Block_Size
));
1530 if Pool
.Low_Level_Traces
then
1531 Put
(Output_File
(Pool
),
1533 & Storage_Count
'Image (Header
.Block_Size
)
1535 Print_Address
(Output_File
(Pool
), Storage_Address
);
1536 Put
(Output_File
(Pool
),
1538 & Storage_Count
'Image (Header
.Block_Size
+ Extra_Allocation
)
1540 Print_Address
(Output_File
(Pool
), Header
.Allocation_Address
);
1541 Put
(Output_File
(Pool
), "), at ");
1543 Put_Line
(Output_File
(Pool
), Pool
.Stack_Trace_Depth
, null,
1544 Deallocate_Label
'Address,
1545 Code_Address_For_Deallocate_End
);
1546 Print_Traceback
(Output_File
(Pool
), " Memory was allocated at ",
1547 Header
.Alloc_Traceback
);
1550 -- Remove this block from the list of used blocks
1553 To_Address
(Header
.Dealloc_Traceback
);
1555 if Previous
= System
.Null_Address
then
1556 Pool
.First_Used_Block
:= Header_Of
(Pool
.First_Used_Block
).Next
;
1558 if Pool
.First_Used_Block
/= System
.Null_Address
then
1559 Header_Of
(Pool
.First_Used_Block
).Dealloc_Traceback
:=
1560 To_Traceback
(null);
1564 Header_Of
(Previous
).Next
:= Header
.Next
;
1566 if Header
.Next
/= System
.Null_Address
then
1568 (Header
.Next
).Dealloc_Traceback
:= To_Address
(Previous
);
1572 -- Update the Alloc_Traceback Frees/Total_Frees members (if present)
1574 if Header
.Alloc_Traceback
/= null then
1575 Header
.Alloc_Traceback
.Frees
:= Header
.Alloc_Traceback
.Frees
+ 1;
1576 Header
.Alloc_Traceback
.Total_Frees
:=
1577 Header
.Alloc_Traceback
.Total_Frees
+
1578 Byte_Count
(Header
.Block_Size
);
1581 Pool
.Free_Count
:= Pool
.Free_Count
+ 1;
1583 -- Update the header
1586 (Allocation_Address
=> Header
.Allocation_Address
,
1587 Alloc_Traceback
=> Header
.Alloc_Traceback
,
1588 Dealloc_Traceback
=> To_Traceback
1589 (Find_Or_Create_Traceback
1592 Deallocate_Label
'Address,
1593 Code_Address_For_Deallocate_End
)),
1594 Next
=> System
.Null_Address
,
1595 Block_Size
=> -Header
.Block_Size
);
1597 if Pool
.Reset_Content_On_Free
then
1598 Set_Dead_Beef
(Storage_Address
, -Header
.Block_Size
);
1601 Pool
.Logically_Deallocated
:=
1602 Pool
.Logically_Deallocated
+ Byte_Count
(-Header
.Block_Size
);
1604 -- Link this free block with the others (at the end of the list, so
1605 -- that we can start releasing the older blocks first later on).
1607 if Pool
.First_Free_Block
= System
.Null_Address
then
1608 Pool
.First_Free_Block
:= Storage_Address
;
1609 Pool
.Last_Free_Block
:= Storage_Address
;
1612 Header_Of
(Pool
.Last_Free_Block
).Next
:= Storage_Address
;
1613 Pool
.Last_Free_Block
:= Storage_Address
;
1616 -- Do not physically release the memory here, but in Alloc.
1617 -- See comment there for details.
1619 Unlock_Task_Required
:= False;
1625 if Unlock_Task_Required
then
1631 --------------------
1632 -- Deallocate_End --
1633 --------------------
1635 -- DO NOT MOVE, this must be right after Deallocate
1639 -- This is making assumptions about code order that may be invalid ???
1641 procedure Deallocate_End
is
1643 <<Deallocate_End_Label
>>
1644 Code_Address_For_Deallocate_End
:= Deallocate_End_Label
'Address;
1651 procedure Dereference
1652 (Pool
: in out Debug_Pool
;
1653 Storage_Address
: Address
;
1654 Size_In_Storage_Elements
: Storage_Count
;
1655 Alignment
: Storage_Count
)
1657 pragma Unreferenced
(Alignment
, Size_In_Storage_Elements
);
1659 Valid
: constant Boolean := Is_Valid
(Storage_Address
);
1660 Header
: Allocation_Header_Access
;
1663 -- Locking policy: we do not do any locking in this procedure. The
1664 -- tables are only read, not written to, and although a problem might
1665 -- appear if someone else is modifying the tables at the same time, this
1666 -- race condition is not intended to be detected by this storage_pool (a
1667 -- now invalid pointer would appear as valid). Instead, we prefer
1668 -- optimum performance for dereferences.
1670 <<Dereference_Label
>>
1673 if Pool
.Raise_Exceptions
then
1674 raise Accessing_Not_Allocated_Storage
;
1676 Put
(Output_File
(Pool
),
1677 "error: Accessing not allocated storage, at ");
1678 Put_Line
(Output_File
(Pool
), Pool
.Stack_Trace_Depth
, null,
1679 Dereference_Label
'Address,
1680 Code_Address_For_Dereference_End
);
1684 Header
:= Header_Of
(Storage_Address
);
1686 if Header
.Block_Size
< 0 then
1687 if Pool
.Raise_Exceptions
then
1688 raise Accessing_Deallocated_Storage
;
1690 Put
(Output_File
(Pool
),
1691 "error: Accessing deallocated storage, at ");
1693 (Output_File
(Pool
), Pool
.Stack_Trace_Depth
, null,
1694 Dereference_Label
'Address,
1695 Code_Address_For_Dereference_End
);
1696 Print_Traceback
(Output_File
(Pool
), " First deallocation at ",
1697 To_Traceback
(Header
.Dealloc_Traceback
));
1698 Print_Traceback
(Output_File
(Pool
), " Initial allocation at ",
1699 Header
.Alloc_Traceback
);
1705 ---------------------
1706 -- Dereference_End --
1707 ---------------------
1709 -- DO NOT MOVE: this must be right after Dereference
1713 -- This is making assumptions about code order that may be invalid ???
1715 procedure Dereference_End
is
1717 <<Dereference_End_Label
>>
1718 Code_Address_For_Dereference_End
:= Dereference_End_Label
'Address;
1719 end Dereference_End
;
1725 procedure Print_Info
1727 Cumulate
: Boolean := False;
1728 Display_Slots
: Boolean := False;
1729 Display_Leaks
: Boolean := False)
1732 package Backtrace_Htable_Cumulate
is new GNAT
.HTable
.Static_HTable
1733 (Header_Num
=> Header
,
1734 Element
=> Traceback_Htable_Elem
,
1735 Elmt_Ptr
=> Traceback_Htable_Elem_Ptr
,
1737 Set_Next
=> Set_Next
,
1739 Key
=> Tracebacks_Array_Access
,
1743 -- This needs a comment ??? probably some of the ones below do too???
1745 Data
: Traceback_Htable_Elem_Ptr
;
1746 Elem
: Traceback_Htable_Elem_Ptr
;
1747 Current
: System
.Address
;
1748 Header
: Allocation_Header_Access
;
1753 ("Total allocated bytes : " &
1754 Byte_Count
'Image (Pool
.Allocated
));
1757 ("Total logically deallocated bytes : " &
1758 Byte_Count
'Image (Pool
.Logically_Deallocated
));
1761 ("Total physically deallocated bytes : " &
1762 Byte_Count
'Image (Pool
.Physically_Deallocated
));
1764 if Pool
.Marked_Blocks_Deallocated
then
1765 Put_Line
("Marked blocks were physically deallocated. This is");
1766 Put_Line
("potentially dangerous, and you might want to run");
1767 Put_Line
("again with a lower value of Minimum_To_Free");
1771 ("Current Water Mark: " &
1772 Byte_Count
'Image (Pool
.Current_Water_Mark
));
1775 ("High Water Mark: " &
1776 Byte_Count
'Image (Pool
.High_Water
));
1780 if Display_Slots
then
1781 Data
:= Backtrace_Htable
.Get_First
;
1782 while Data
/= null loop
1783 if Data
.Kind
in Alloc
.. Dealloc
then
1785 new Traceback_Htable_Elem
'
1786 (Traceback => new Tracebacks_Array'(Data
.Traceback
.all),
1787 Count
=> Data
.Count
,
1789 Total
=> Data
.Total
,
1790 Frees
=> Data
.Frees
,
1791 Total_Frees
=> Data
.Total_Frees
,
1793 Backtrace_Htable_Cumulate
.Set
(Elem
);
1796 K
:= (if Data
.Kind
= Alloc
then Indirect_Alloc
1797 else Indirect_Dealloc
);
1799 -- Propagate the direct call to all its parents
1801 for T
in Data
.Traceback
'First + 1 .. Data
.Traceback
'Last loop
1802 Elem
:= Backtrace_Htable_Cumulate
.Get
1804 (T
.. Data
.Traceback
'Last)'Unrestricted_Access);
1806 -- If not, insert it
1809 Elem
:= new Traceback_Htable_Elem
'
1810 (Traceback => new Tracebacks_Array'
1811 (Data
.Traceback
(T
.. Data
.Traceback
'Last)),
1812 Count
=> Data
.Count
,
1814 Total
=> Data
.Total
,
1815 Frees
=> Data
.Frees
,
1816 Total_Frees
=> Data
.Total_Frees
,
1818 Backtrace_Htable_Cumulate
.Set
(Elem
);
1820 -- Properly take into account that the subprograms
1821 -- indirectly called might be doing either allocations
1822 -- or deallocations. This needs to be reflected in the
1826 Elem
.Count
:= Elem
.Count
+ Data
.Count
;
1828 if K
= Elem
.Kind
then
1829 Elem
.Total
:= Elem
.Total
+ Data
.Total
;
1831 elsif Elem
.Total
> Data
.Total
then
1832 Elem
.Total
:= Elem
.Total
- Data
.Total
;
1836 Elem
.Total
:= Data
.Total
- Elem
.Total
;
1842 Data
:= Backtrace_Htable
.Get_Next
;
1846 Put_Line
("List of allocations/deallocations: ");
1848 Data
:= Backtrace_Htable_Cumulate
.Get_First
;
1849 while Data
/= null loop
1851 when Alloc
=> Put
("alloc (count:");
1852 when Indirect_Alloc
=> Put
("indirect alloc (count:");
1853 when Dealloc
=> Put
("free (count:");
1854 when Indirect_Dealloc
=> Put
("indirect free (count:");
1857 Put
(Natural'Image (Data
.Count
) & ", total:" &
1858 Byte_Count
'Image (Data
.Total
) & ") ");
1860 for T
in Data
.Traceback
'Range loop
1861 Put
(Image_C
(PC_For
(Data
.Traceback
(T
))) & ' ');
1866 Data
:= Backtrace_Htable_Cumulate
.Get_Next
;
1869 Backtrace_Htable_Cumulate
.Reset
;
1872 if Display_Leaks
then
1874 Put_Line
("List of not deallocated blocks:");
1876 -- Do not try to group the blocks with the same stack traces
1877 -- together. This is done by the gnatmem output.
1879 Current
:= Pool
.First_Used_Block
;
1880 while Current
/= System
.Null_Address
loop
1881 Header
:= Header_Of
(Current
);
1883 Put
("Size: " & Storage_Count
'Image (Header
.Block_Size
) & " at: ");
1885 if Header
.Alloc_Traceback
/= null then
1886 for T
in Header
.Alloc_Traceback
.Traceback
'Range loop
1888 (PC_For
(Header
.Alloc_Traceback
.Traceback
(T
))) & ' ');
1893 Current
:= Header
.Next
;
1905 Report
: Report_Type
:= All_Reports
) is
1907 Total_Freed
: constant Byte_Count
:=
1908 Pool
.Logically_Deallocated
+ Pool
.Physically_Deallocated
;
1910 procedure Do_Report
(Sort
: Report_Type
);
1911 -- Do a specific type of report
1913 procedure Do_Report
(Sort
: Report_Type
) is
1914 Elem
: Traceback_Htable_Elem_Ptr
;
1916 Grand_Total
: Float;
1918 Max
: array (1 .. Size
) of Traceback_Htable_Elem_Ptr
:=
1920 -- Sorted array for the biggest memory users
1929 Put_Line
(Size
'Img & " biggest memory users at this time:");
1930 Put_Line
("Results include bytes and chunks still allocated");
1931 Grand_Total
:= Float (Pool
.Current_Water_Mark
);
1933 when Allocations_Count
=>
1934 Put_Line
(Size
'Img & " biggest number of live allocations:");
1935 Put_Line
("Results include bytes and chunks still allocated");
1936 Grand_Total
:= Float (Pool
.Current_Water_Mark
);
1938 when Sort_Total_Allocs
=>
1939 Put_Line
(Size
'Img & " biggest number of allocations:");
1940 Put_Line
("Results include total bytes and chunks allocated,");
1941 Put_Line
("even if no longer allocated - Deallocations are"
1943 Grand_Total
:= Float (Pool
.Allocated
);
1945 when Marked_Blocks
=>
1946 Put_Line
("Special blocks marked by Mark_Traceback");
1950 Elem
:= Backtrace_Htable
.Get_First
;
1951 while Elem
/= null loop
1952 -- Handle only alloc elememts
1953 if Elem
.Kind
= Alloc
then
1954 -- Ignore small blocks (depending on the sorting criteria) to
1957 if (Sort
= Memory_Usage
1958 and then Elem
.Total
- Elem
.Total_Frees
>= 1_000
)
1959 or else (Sort
= Allocations_Count
1960 and then Elem
.Count
- Elem
.Frees
>= 1)
1961 or else (Sort
= Sort_Total_Allocs
and then Elem
.Count
> 1)
1962 or else (Sort
= Marked_Blocks
1963 and then Elem
.Total
= 0)
1965 if Sort
= Marked_Blocks
then
1966 Grand_Total
:= Grand_Total
+ Float (Elem
.Count
);
1969 for M
in Max
'Range loop
1970 Bigger
:= Max
(M
) = null;
1977 Max
(M
).Total
- Max
(M
).Total_Frees
1978 < Elem
.Total
- Elem
.Total_Frees
;
1980 when Allocations_Count
=>
1982 Max
(M
).Count
- Max
(M
).Frees
1983 < Elem
.Count
- Elem
.Frees
;
1988 Bigger
:= Max
(M
).Count
< Elem
.Count
;
1993 Max
(M
+ 1 .. Max
'Last) := Max
(M
.. Max
'Last - 1);
2001 Elem
:= Backtrace_Htable
.Get_Next
;
2004 if Grand_Total
= 0.0 then
2008 for M
in Max
'Range loop
2009 exit when Max
(M
) = null;
2011 type Percent
is delta 0.1 range 0.0 .. 100.0;
2020 Total
:= Max
(M
).Total
- Max
(M
).Total_Frees
;
2022 when Sort_Total_Allocs
=>
2023 Total
:= Max
(M
).Total
;
2025 when Marked_Blocks
=>
2026 Total
:= Byte_Count
(Max
(M
).Count
);
2029 P
:= Percent
(100.0 * Float (Total
) / Grand_Total
);
2032 when Memory_Usage | Allocations_Count | All_Reports
=>
2034 Count
: constant Natural :=
2035 Max
(M
).Count
- Max
(M
).Frees
;
2037 Put
(P
'Img & "%:" & Total
'Img & " bytes in"
2038 & Count
'Img & " chunks at");
2040 when Sort_Total_Allocs
=>
2041 Put
(P
'Img & "%:" & Total
'Img & " bytes in"
2042 & Max
(M
).Count
'Img & " chunks at");
2043 when Marked_Blocks
=>
2045 & Max
(M
).Count
'Img & " chunks /"
2046 & Integer (Grand_Total
)'Img & " at");
2050 for J
in Max
(M
).Traceback
'Range loop
2051 Put
(" " & Image_C
(PC_For
(Max
(M
).Traceback
(J
))));
2059 Put_Line
("Traceback elements allocated: " & Traceback_Count
'Img);
2060 Put_Line
("Validity elements allocated: " & Validity_Count
'Img);
2063 Put_Line
("Ada Allocs:" & Pool
.Allocated
'Img
2064 & " bytes in" & Pool
.Alloc_Count
'Img & " chunks");
2065 Put_Line
("Ada Free:" & Total_Freed
'Img & " bytes in" &
2068 Put_Line
("Ada Current watermark: "
2069 & Byte_Count
'Image (Pool
.Current_Water_Mark
)
2070 & " in" & Byte_Count
'Image (Pool
.Alloc_Count
-
2071 Pool
.Free_Count
) & " chunks");
2072 Put_Line
("Ada High watermark: " & Pool
.High_Water_Mark
'Img);
2076 for Sort
in Report_Type
loop
2077 if Sort
/= All_Reports
then
2091 procedure Dump_Stdout
2094 Report
: Report_Type
:= All_Reports
)
2096 procedure Internal
is new Dump
2097 (Put_Line
=> Stdout_Put_Line
,
2100 -- Start of processing for Dump_Stdout
2103 Internal
(Pool
, Size
, Report
);
2111 Elem
: Traceback_Htable_Elem_Ptr
;
2113 Elem
:= Backtrace_Htable
.Get_First
;
2114 while Elem
/= null loop
2118 Elem
.Total_Frees
:= 0;
2119 Elem
:= Backtrace_Htable
.Get_Next
;
2127 function Storage_Size
(Pool
: Debug_Pool
) return Storage_Count
is
2128 pragma Unreferenced
(Pool
);
2130 return Storage_Count
'Last;
2133 ---------------------
2134 -- High_Water_Mark --
2135 ---------------------
2137 function High_Water_Mark
2138 (Pool
: Debug_Pool
) return Byte_Count
is
2140 return Pool
.High_Water
;
2141 end High_Water_Mark
;
2143 ------------------------
2144 -- Current_Water_Mark --
2145 ------------------------
2147 function Current_Water_Mark
2148 (Pool
: Debug_Pool
) return Byte_Count
is
2150 return Pool
.Allocated
- Pool
.Logically_Deallocated
-
2151 Pool
.Physically_Deallocated
;
2152 end Current_Water_Mark
;
2154 ------------------------------
2155 -- System_Memory_Debug_Pool --
2156 ------------------------------
2158 procedure System_Memory_Debug_Pool
2159 (Has_Unhandled_Memory
: Boolean := True) is
2161 System_Memory_Debug_Pool_Enabled
:= True;
2162 Allow_Unhandled_Memory
:= Has_Unhandled_Memory
;
2163 end System_Memory_Debug_Pool
;
2170 (Pool
: in out Debug_Pool
;
2171 Stack_Trace_Depth
: Natural := Default_Stack_Trace_Depth
;
2172 Maximum_Logically_Freed_Memory
: SSC
:= Default_Max_Freed
;
2173 Minimum_To_Free
: SSC
:= Default_Min_Freed
;
2174 Reset_Content_On_Free
: Boolean := Default_Reset_Content
;
2175 Raise_Exceptions
: Boolean := Default_Raise_Exceptions
;
2176 Advanced_Scanning
: Boolean := Default_Advanced_Scanning
;
2177 Errors_To_Stdout
: Boolean := Default_Errors_To_Stdout
;
2178 Low_Level_Traces
: Boolean := Default_Low_Level_Traces
)
2181 Pool
.Stack_Trace_Depth
:= Stack_Trace_Depth
;
2182 Pool
.Maximum_Logically_Freed_Memory
:= Maximum_Logically_Freed_Memory
;
2183 Pool
.Reset_Content_On_Free
:= Reset_Content_On_Free
;
2184 Pool
.Raise_Exceptions
:= Raise_Exceptions
;
2185 Pool
.Minimum_To_Free
:= Minimum_To_Free
;
2186 Pool
.Advanced_Scanning
:= Advanced_Scanning
;
2187 Pool
.Errors_To_Stdout
:= Errors_To_Stdout
;
2188 Pool
.Low_Level_Traces
:= Low_Level_Traces
;
2195 procedure Print_Pool
(A
: System
.Address
) is
2196 Storage
: constant Address
:= A
;
2197 Valid
: constant Boolean := Is_Valid
(Storage
);
2198 Header
: Allocation_Header_Access
;
2201 -- We might get Null_Address if the call from gdb was done
2202 -- incorrectly. For instance, doing a "print_pool(my_var)" passes 0x0,
2203 -- instead of passing the value of my_var
2205 if A
= System
.Null_Address
then
2207 (Standard_Output
, "Memory not under control of the storage pool");
2213 (Standard_Output
, "Memory not under control of the storage pool");
2216 Header
:= Header_Of
(Storage
);
2217 Print_Address
(Standard_Output
, A
);
2218 Put_Line
(Standard_Output
, " allocated at:");
2219 Print_Traceback
(Standard_Output
, "", Header
.Alloc_Traceback
);
2221 if To_Traceback
(Header
.Dealloc_Traceback
) /= null then
2222 Print_Address
(Standard_Output
, A
);
2223 Put_Line
(Standard_Output
,
2224 " logically freed memory, deallocated at:");
2225 Print_Traceback
(Standard_Output
, "",
2226 To_Traceback
(Header
.Dealloc_Traceback
));
2231 -----------------------
2232 -- Print_Info_Stdout --
2233 -----------------------
2235 procedure Print_Info_Stdout
2237 Cumulate
: Boolean := False;
2238 Display_Slots
: Boolean := False;
2239 Display_Leaks
: Boolean := False)
2242 procedure Internal
is new Print_Info
2243 (Put_Line
=> Stdout_Put_Line
,
2246 -- Start of processing for Print_Info_Stdout
2249 Internal
(Pool
, Cumulate
, Display_Slots
, Display_Leaks
);
2250 end Print_Info_Stdout
;
2256 procedure Dump_Gnatmem
(Pool
: Debug_Pool
; File_Name
: String) is
2257 type File_Ptr
is new System
.Address
;
2259 function fopen
(Path
: String; Mode
: String) return File_Ptr
;
2260 pragma Import
(C
, fopen
);
2263 (Ptr
: System
.Address
;
2273 pragma Import
(C
, fwrite
);
2275 procedure fputc
(C
: Integer; Stream
: File_Ptr
);
2276 pragma Import
(C
, fputc
);
2278 procedure fclose
(Stream
: File_Ptr
);
2279 pragma Import
(C
, fclose
);
2281 Address_Size
: constant size_t
:=
2282 System
.Address
'Max_Size_In_Storage_Elements;
2283 -- Size in bytes of a pointer
2286 Current
: System
.Address
;
2287 Header
: Allocation_Header_Access
;
2288 Actual_Size
: size_t
;
2289 Num_Calls
: Integer;
2290 Tracebk
: Tracebacks_Array_Access
;
2291 Dummy_Time
: Duration := 1.0;
2294 File
:= fopen
(File_Name
& ASCII
.NUL
, "wb" & ASCII
.NUL
);
2295 fwrite
("GMEM DUMP" & ASCII
.LF
, 10, 1, File
);
2296 fwrite
(Dummy_Time
'Address, Duration'Max_Size_In_Storage_Elements, 1,
2299 -- List of not deallocated blocks (see Print_Info)
2301 Current
:= Pool
.First_Used_Block
;
2302 while Current
/= System
.Null_Address
loop
2303 Header
:= Header_Of
(Current
);
2305 Actual_Size
:= size_t
(Header
.Block_Size
);
2306 Tracebk
:= Header
.Alloc_Traceback
.Traceback
;
2308 if Header
.Alloc_Traceback
/= null then
2309 Num_Calls
:= Tracebk
'Length;
2311 -- (Code taken from memtrack.adb in GNAT's sources)
2313 -- Logs allocation call using the format:
2315 -- 'A' <mem addr> <size chunk> <len backtrace> <addr1> ... <addrn>
2317 fputc
(Character'Pos ('A'), File
);
2318 fwrite
(Current
'Address, Address_Size
, 1, File
);
2319 fwrite
(Actual_Size
'Address, size_t
'Max_Size_In_Storage_Elements,
2321 fwrite
(Dummy_Time
'Address, Duration'Max_Size_In_Storage_Elements,
2323 fwrite
(Num_Calls
'Address, Integer'Max_Size_In_Storage_Elements, 1,
2326 for J
in Tracebk
'First .. Tracebk
'First + Num_Calls
- 1 loop
2328 Ptr
: System
.Address
:= PC_For
(Tracebk
(J
));
2330 fwrite
(Ptr
'Address, Address_Size
, 1, File
);
2336 Current
:= Header
.Next
;
2346 procedure Stdout_Put
(S
: String) is
2348 Put
(Standard_Output
, S
);
2351 ---------------------
2352 -- Stdout_Put_Line --
2353 ---------------------
2355 procedure Stdout_Put_Line
(S
: String) is
2357 Put_Line
(Standard_Output
, S
);
2358 end Stdout_Put_Line
;
2360 -- Package initialization
2366 end GNAT
.Debug_Pools
;