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