]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/ada/g-debpoo.adb
g-comlin.ads, [...]: Add new warning for renaming of function return objects
[thirdparty/gcc.git] / gcc / ada / g-debpoo.adb
CommitLineData
38cbfe40
RK
1------------------------------------------------------------------------------
2-- --
3-- GNAT COMPILER COMPONENTS --
4-- --
fbf5a39b 5-- G N A T . D E B U G _ P O O L S --
38cbfe40 6-- --
fbf5a39b 7-- B o d y --
38cbfe40 8-- --
5b8b9057 9-- Copyright (C) 1992-2006, Free Software Foundation, Inc. --
38cbfe40
RK
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 --
cb5fee25
KC
19-- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
20-- Boston, MA 02110-1301, USA. --
38cbfe40
RK
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. --
71ff80dc 30-- Extensive contributions were provided by Ada Core Technologies Inc. --
38cbfe40
RK
31-- --
32------------------------------------------------------------------------------
33
fbf5a39b
AC
34with Ada.Exceptions.Traceback;
35with GNAT.IO; use GNAT.IO;
36
37with System.Address_Image;
38with System.Memory; use System.Memory;
39with System.Soft_Links; use System.Soft_Links;
40
41with System.Traceback_Entries; use System.Traceback_Entries;
42
38cbfe40 43with GNAT.HTable;
fbf5a39b 44with GNAT.Traceback; use GNAT.Traceback;
38cbfe40 45
fbf5a39b 46with Ada.Unchecked_Conversion;
38cbfe40
RK
47
48package body GNAT.Debug_Pools is
38cbfe40 49
2989065e 50 Default_Alignment : constant := Standard'Maximum_Alignment;
fbf5a39b
AC
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.
55
fbf5a39b
AC
56 Max_Ignored_Levels : constant Natural := 10;
57 -- Maximum number of levels that will be ignored in backtraces. This is so
58 -- that we still have enough significant levels in the tracebacks returned
59 -- to the user.
2989065e 60 --
fbf5a39b
AC
61 -- The value 10 is chosen as being greater than the maximum callgraph
62 -- in this package. Its actual value is not really relevant, as long as it
63 -- is high enough to make sure we still have enough frames to return to
64 -- the user after we have hidden the frames internal to this package.
65
2989065e
RD
66 ---------------------------
67 -- Back Trace Hash Table --
68 ---------------------------
38cbfe40 69
fbf5a39b
AC
70 -- This package needs to store one set of tracebacks for each allocation
71 -- point (when was it allocated or deallocated). This would use too much
72 -- memory, so the tracebacks are actually stored in a hash table, and
73 -- we reference elements in this hash table instead.
74
75 -- This hash-table will remain empty if the discriminant Stack_Trace_Depth
76 -- for the pools is set to 0.
77
78 -- This table is a global table, that can be shared among all debug pools
79 -- with no problems.
38cbfe40
RK
80
81 type Header is range 1 .. 1023;
fbf5a39b
AC
82 -- Number of elements in the hash-table
83
84 type Tracebacks_Array_Access
85 is access GNAT.Traceback.Tracebacks_Array;
86
87 type Traceback_Kind is (Alloc, Dealloc, Indirect_Alloc, Indirect_Dealloc);
88
89 type Traceback_Htable_Elem;
90 type Traceback_Htable_Elem_Ptr
91 is access Traceback_Htable_Elem;
92
93 type Traceback_Htable_Elem is record
94 Traceback : Tracebacks_Array_Access;
95 Kind : Traceback_Kind;
96 Count : Natural;
97 Total : Byte_Count;
98 Next : Traceback_Htable_Elem_Ptr;
99 end record;
100
2989065e
RD
101 -- Subprograms used for the Backtrace_Htable instantiation
102
fbf5a39b
AC
103 procedure Set_Next
104 (E : Traceback_Htable_Elem_Ptr;
105 Next : Traceback_Htable_Elem_Ptr);
2989065e
RD
106 pragma Inline (Set_Next);
107
fbf5a39b 108 function Next
2989065e
RD
109 (E : Traceback_Htable_Elem_Ptr) return Traceback_Htable_Elem_Ptr;
110 pragma Inline (Next);
111
fbf5a39b 112 function Get_Key
2989065e
RD
113 (E : Traceback_Htable_Elem_Ptr) return Tracebacks_Array_Access;
114 pragma Inline (Get_Key);
115
fbf5a39b 116 function Hash (T : Tracebacks_Array_Access) return Header;
2989065e
RD
117 pragma Inline (Hash);
118
fbf5a39b 119 function Equal (K1, K2 : Tracebacks_Array_Access) return Boolean;
2989065e
RD
120 -- Why is this not inlined???
121
122 -- The hash table for back traces
fbf5a39b
AC
123
124 package Backtrace_Htable is new GNAT.HTable.Static_HTable
125 (Header_Num => Header,
126 Element => Traceback_Htable_Elem,
127 Elmt_Ptr => Traceback_Htable_Elem_Ptr,
128 Null_Ptr => null,
129 Set_Next => Set_Next,
130 Next => Next,
131 Key => Tracebacks_Array_Access,
132 Get_Key => Get_Key,
133 Hash => Hash,
134 Equal => Equal);
135
136 -----------------------
137 -- Allocations table --
138 -----------------------
139
140 type Allocation_Header;
141 type Allocation_Header_Access is access Allocation_Header;
142
fbf5a39b
AC
143 type Traceback_Ptr_Or_Address is new System.Address;
144 -- A type that acts as a C union, and is either a System.Address or a
145 -- Traceback_Htable_Elem_Ptr.
146
2989065e
RD
147 -- The following record stores extra information that needs to be
148 -- memorized for each block allocated with the special debug pool.
149
fbf5a39b 150 type Allocation_Header is record
cc335f43 151 Allocation_Address : System.Address;
2989065e 152 -- Address of the block returned by malloc, possibly unaligned
cc335f43 153
2989065e 154 Block_Size : Storage_Offset;
fbf5a39b
AC
155 -- Needed only for advanced freeing algorithms (traverse all allocated
156 -- blocks for potential references). This value is negated when the
157 -- chunk of memory has been logically freed by the application. This
158 -- chunk has not been physically released yet.
159
2989065e
RD
160 Alloc_Traceback : Traceback_Htable_Elem_Ptr;
161 -- ??? comment required
162
fbf5a39b 163 Dealloc_Traceback : Traceback_Ptr_Or_Address;
cc335f43 164 -- Pointer to the traceback for the allocation (if the memory chunk is
fbf5a39b
AC
165 -- still valid), or to the first deallocation otherwise. Make sure this
166 -- is a thin pointer to save space.
167 --
168 -- Dealloc_Traceback is also for blocks that are still allocated to
169 -- point to the previous block in the list. This saves space in this
170 -- header, and make manipulation of the lists of allocated pointers
171 -- faster.
172
173 Next : System.Address;
174 -- Point to the next block of the same type (either allocated or
175 -- logically freed) in memory. This points to the beginning of the user
176 -- data, and does not include the header of that block.
177 end record;
178
179 function Header_Of (Address : System.Address)
180 return Allocation_Header_Access;
181 pragma Inline (Header_Of);
182 -- Return the header corresponding to a previously allocated address
183
184 function To_Address is new Ada.Unchecked_Conversion
185 (Traceback_Ptr_Or_Address, System.Address);
2989065e 186
fbf5a39b
AC
187 function To_Address is new Ada.Unchecked_Conversion
188 (System.Address, Traceback_Ptr_Or_Address);
2989065e 189
fbf5a39b
AC
190 function To_Traceback is new Ada.Unchecked_Conversion
191 (Traceback_Ptr_Or_Address, Traceback_Htable_Elem_Ptr);
2989065e 192
fbf5a39b
AC
193 function To_Traceback is new Ada.Unchecked_Conversion
194 (Traceback_Htable_Elem_Ptr, Traceback_Ptr_Or_Address);
195
2989065e
RD
196 Header_Offset : constant Storage_Count :=
197 Default_Alignment *
198 ((Allocation_Header'Size / System.Storage_Unit
199 + Default_Alignment - 1) / Default_Alignment);
200 -- Offset of user data after allocation header
cc335f43 201
fbf5a39b 202 Minimum_Allocation : constant Storage_Count :=
2989065e 203 Default_Alignment - 1 + Header_Offset;
cc335f43
AC
204 -- Minimal allocation: size of allocation_header rounded up to next
205 -- multiple of default alignment + worst-case padding.
fbf5a39b 206
fbf5a39b
AC
207 -----------------------
208 -- Local subprograms --
209 -----------------------
210
211 function Find_Or_Create_Traceback
212 (Pool : Debug_Pool;
213 Kind : Traceback_Kind;
214 Size : Storage_Count;
215 Ignored_Frame_Start : System.Address;
2989065e 216 Ignored_Frame_End : System.Address) return Traceback_Htable_Elem_Ptr;
fbf5a39b
AC
217 -- Return an element matching the current traceback (omitting the frames
218 -- that are in the current package). If this traceback already existed in
219 -- the htable, a pointer to this is returned to spare memory. Null is
220 -- returned if the pool is set not to store tracebacks. If the traceback
221 -- already existed in the table, the count is incremented so that
2989065e
RD
222 -- Dump_Tracebacks returns useful results. All addresses up to, and
223 -- including, an address between Ignored_Frame_Start .. Ignored_Frame_End
224 -- are ignored.
fbf5a39b
AC
225
226 procedure Put_Line
227 (Depth : Natural;
228 Traceback : Tracebacks_Array_Access;
229 Ignored_Frame_Start : System.Address := System.Null_Address;
230 Ignored_Frame_End : System.Address := System.Null_Address);
231 -- Print Traceback to Standard_Output. If Traceback is null, print the
232 -- call_chain at the current location, up to Depth levels, ignoring all
233 -- addresses up to the first one in the range
234 -- Ignored_Frame_Start .. Ignored_Frame_End
235
11f03980
VC
236 package Validity is
237 function Is_Valid (Storage : System.Address) return Boolean;
238 pragma Inline (Is_Valid);
239 -- Return True if Storage is an address that the debug pool has under
240 -- its control.
fbf5a39b 241
11f03980
VC
242 procedure Set_Valid (Storage : System.Address; Value : Boolean);
243 pragma Inline (Set_Valid);
244 -- Mark the address Storage as being under control of the memory pool
245 -- (if Value is True), or not (if Value is False).
246 end Validity;
247
248 use Validity;
fbf5a39b
AC
249
250 procedure Set_Dead_Beef
251 (Storage_Address : System.Address;
252 Size_In_Storage_Elements : Storage_Count);
253 -- Set the contents of the memory block pointed to by Storage_Address to
254 -- the 16#DEADBEEF# pattern. If Size_In_Storage_Elements is not a multiple
255 -- of the length of this pattern, the last instance may be partial.
256
257 procedure Free_Physically (Pool : in out Debug_Pool);
258 -- Start to physically release some memory to the system, until the amount
259 -- of logically (but not physically) freed memory is lower than the
260 -- expected amount in Pool.
261
262 procedure Allocate_End;
263 procedure Deallocate_End;
264 procedure Dereference_End;
265 -- These procedures are used as markers when computing the stacktraces,
266 -- so that addresses in the debug pool itself are not reported to the user.
267
268 Code_Address_For_Allocate_End : System.Address;
269 Code_Address_For_Deallocate_End : System.Address;
270 Code_Address_For_Dereference_End : System.Address;
271 -- Taking the address of the above procedures will not work on some
272 -- architectures (HPUX and VMS for instance). Thus we do the same thing
273 -- that is done in a-except.adb, and get the address of labels instead
274
275 procedure Skip_Levels
276 (Depth : Natural;
277 Trace : Tracebacks_Array;
278 Start : out Natural;
279 Len : in out Natural;
280 Ignored_Frame_Start : System.Address;
281 Ignored_Frame_End : System.Address);
282 -- Set Start .. Len to the range of values from Trace that should be output
283 -- to the user. This range of values exludes any address prior to the first
284 -- one in Ignored_Frame_Start .. Ignored_Frame_End (basically addresses
285 -- internal to this package). Depth is the number of levels that the user
286 -- is interested in.
287
288 ---------------
289 -- Header_Of --
290 ---------------
291
292 function Header_Of (Address : System.Address)
293 return Allocation_Header_Access
294 is
295 function Convert is new Ada.Unchecked_Conversion
296 (System.Address, Allocation_Header_Access);
297 begin
cc335f43 298 return Convert (Address - Header_Offset);
fbf5a39b
AC
299 end Header_Of;
300
301 --------------
302 -- Set_Next --
303 --------------
304
305 procedure Set_Next
306 (E : Traceback_Htable_Elem_Ptr;
307 Next : Traceback_Htable_Elem_Ptr)
308 is
309 begin
310 E.Next := Next;
311 end Set_Next;
312
313 ----------
314 -- Next --
315 ----------
316
317 function Next
2989065e 318 (E : Traceback_Htable_Elem_Ptr) return Traceback_Htable_Elem_Ptr is
fbf5a39b
AC
319 begin
320 return E.Next;
321 end Next;
322
323 -----------
324 -- Equal --
325 -----------
326
327 function Equal (K1, K2 : Tracebacks_Array_Access) return Boolean is
328 use Ada.Exceptions.Traceback;
fbf5a39b
AC
329 begin
330 return K1.all = K2.all;
331 end Equal;
332
333 -------------
334 -- Get_Key --
335 -------------
336
337 function Get_Key
2989065e 338 (E : Traceback_Htable_Elem_Ptr) return Tracebacks_Array_Access
fbf5a39b
AC
339 is
340 begin
341 return E.Traceback;
342 end Get_Key;
343
344 ----------
345 -- Hash --
346 ----------
38cbfe40 347
fbf5a39b
AC
348 function Hash (T : Tracebacks_Array_Access) return Header is
349 Result : Integer_Address := 0;
2989065e 350
fbf5a39b
AC
351 begin
352 for X in T'Range loop
353 Result := Result + To_Integer (PC_For (T (X)));
354 end loop;
2989065e 355
fbf5a39b
AC
356 return Header (1 + Result mod Integer_Address (Header'Last));
357 end Hash;
358
359 --------------
360 -- Put_Line --
361 --------------
362
363 procedure Put_Line
364 (Depth : Natural;
365 Traceback : Tracebacks_Array_Access;
366 Ignored_Frame_Start : System.Address := System.Null_Address;
367 Ignored_Frame_End : System.Address := System.Null_Address)
368 is
369 procedure Print (Tr : Tracebacks_Array);
370 -- Print the traceback to standard_output
371
372 -----------
373 -- Print --
374 -----------
375
376 procedure Print (Tr : Tracebacks_Array) is
377 begin
378 for J in Tr'Range loop
379 Put ("0x" & Address_Image (PC_For (Tr (J))) & ' ');
380 end loop;
381 Put (ASCII.LF);
382 end Print;
383
384 -- Start of processing for Put_Line
385
386 begin
387 if Traceback = null then
388 declare
389 Tr : aliased Tracebacks_Array (1 .. Depth + Max_Ignored_Levels);
390 Start, Len : Natural;
391
392 begin
393 Call_Chain (Tr, Len);
394 Skip_Levels (Depth, Tr, Start, Len,
395 Ignored_Frame_Start, Ignored_Frame_End);
396 Print (Tr (Start .. Len));
397 end;
398
399 else
400 Print (Traceback.all);
401 end if;
402 end Put_Line;
403
404 -----------------
405 -- Skip_Levels --
406 -----------------
407
408 procedure Skip_Levels
409 (Depth : Natural;
410 Trace : Tracebacks_Array;
411 Start : out Natural;
412 Len : in out Natural;
413 Ignored_Frame_Start : System.Address;
414 Ignored_Frame_End : System.Address)
415 is
416 begin
417 Start := Trace'First;
418
419 while Start <= Len
420 and then (PC_For (Trace (Start)) < Ignored_Frame_Start
421 or else PC_For (Trace (Start)) > Ignored_Frame_End)
422 loop
423 Start := Start + 1;
424 end loop;
425
426 Start := Start + 1;
427
428 -- Just in case: make sure we have a traceback even if Ignore_Till
429 -- wasn't found.
430
431 if Start > Len then
432 Start := 1;
433 end if;
434
435 if Len - Start + 1 > Depth then
436 Len := Depth + Start - 1;
437 end if;
438 end Skip_Levels;
439
440 ------------------------------
441 -- Find_Or_Create_Traceback --
442 ------------------------------
443
444 function Find_Or_Create_Traceback
445 (Pool : Debug_Pool;
446 Kind : Traceback_Kind;
447 Size : Storage_Count;
448 Ignored_Frame_Start : System.Address;
2989065e 449 Ignored_Frame_End : System.Address) return Traceback_Htable_Elem_Ptr
fbf5a39b
AC
450 is
451 begin
452 if Pool.Stack_Trace_Depth = 0 then
453 return null;
454 end if;
455
456 declare
457 Trace : aliased Tracebacks_Array
458 (1 .. Integer (Pool.Stack_Trace_Depth) + Max_Ignored_Levels);
459 Len, Start : Natural;
460 Elem : Traceback_Htable_Elem_Ptr;
461
462 begin
463 Call_Chain (Trace, Len);
464 Skip_Levels (Pool.Stack_Trace_Depth, Trace, Start, Len,
465 Ignored_Frame_Start, Ignored_Frame_End);
466
2989065e 467 -- Check if the traceback is already in the table
fbf5a39b
AC
468
469 Elem :=
470 Backtrace_Htable.Get (Trace (Start .. Len)'Unrestricted_Access);
471
472 -- If not, insert it
473
474 if Elem = null then
475 Elem := new Traceback_Htable_Elem'
476 (Traceback => new Tracebacks_Array'(Trace (Start .. Len)),
477 Count => 1,
478 Kind => Kind,
479 Total => Byte_Count (Size),
480 Next => null);
481 Backtrace_Htable.Set (Elem);
482
483 else
484 Elem.Count := Elem.Count + 1;
485 Elem.Total := Elem.Total + Byte_Count (Size);
486 end if;
487
488 return Elem;
489 end;
490 end Find_Or_Create_Traceback;
491
492 --------------
11f03980 493 -- Validity --
fbf5a39b
AC
494 --------------
495
11f03980 496 package body Validity is
5b8b9057 497
11f03980
VC
498 -- The validity bits of the allocated blocks are kept in a has table.
499 -- Each component of the hash table contains the validity bits for a
500 -- 16 Mbyte memory chunk.
5b8b9057 501
11f03980
VC
502 -- The reason the validity bits are kept for chunks of memory rather
503 -- than in a big array is that on some 64 bit platforms, it may happen
504 -- that two chunk of allocated data are very far from each other.
5b8b9057 505
11f03980
VC
506 Memory_Chunk_Size : constant Integer_Address := 2 ** 24; -- 16 MB
507 Validity_Divisor : constant := Default_Alignment * System.Storage_Unit;
5b8b9057 508
11f03980
VC
509 Max_Validity_Byte_Index : constant :=
510 Memory_Chunk_Size / Validity_Divisor;
fbf5a39b 511
11f03980
VC
512 subtype Validity_Byte_Index is Integer_Address
513 range 0 .. Max_Validity_Byte_Index - 1;
fbf5a39b 514
11f03980 515 type Byte is mod 2 ** System.Storage_Unit;
fbf5a39b 516
11f03980 517 type Validity_Bits is array (Validity_Byte_Index) of Byte;
fbf5a39b 518
11f03980
VC
519 type Validity_Bits_Ref is access all Validity_Bits;
520 No_Validity_Bits : constant Validity_Bits_Ref := null;
fbf5a39b 521
11f03980 522 Max_Header_Num : constant := 1023;
fbf5a39b 523
11f03980 524 type Header_Num is range 0 .. Max_Header_Num - 1;
fbf5a39b 525
11f03980 526 function Hash (F : Integer_Address) return Header_Num;
fbf5a39b 527
11f03980
VC
528 package Validy_Htable is new GNAT.HTable.Simple_HTable
529 (Header_Num => Header_Num,
530 Element => Validity_Bits_Ref,
531 No_Element => No_Validity_Bits,
532 Key => Integer_Address,
533 Hash => Hash,
534 Equal => "=");
535 -- Table to keep the validity bit blocks for the allocated data
fbf5a39b 536
11f03980
VC
537 function To_Pointer is new Ada.Unchecked_Conversion
538 (System.Address, Validity_Bits_Ref);
fbf5a39b 539
11f03980
VC
540 procedure Memset (A : Address; C : Integer; N : size_t);
541 pragma Import (C, Memset, "memset");
2989065e 542
11f03980
VC
543 ----------
544 -- Hash --
545 ----------
2989065e 546
11f03980
VC
547 function Hash (F : Integer_Address) return Header_Num is
548 begin
549 return Header_Num (F mod Max_Header_Num);
550 end Hash;
551
552 --------------
553 -- Is_Valid --
554 --------------
555
556 function Is_Valid (Storage : System.Address) return Boolean is
557 Int_Storage : constant Integer_Address := To_Integer (Storage);
558 Block_Number : constant Integer_Address :=
559 Int_Storage / Memory_Chunk_Size;
560 Ptr : constant Validity_Bits_Ref :=
561 Validy_Htable.Get (Block_Number);
562 Offset : constant Integer_Address :=
563 (Int_Storage - (Block_Number * Memory_Chunk_Size)) /
564 Default_Alignment;
565 Bit : constant Byte :=
566 2 ** Natural (Offset mod System.Storage_Unit);
567 begin
568 if Ptr = No_Validity_Bits then
569 return False;
570 else
571 return (Ptr (Offset / System.Storage_Unit) and Bit) /= 0;
572 end if;
573 end Is_Valid;
574
575 ---------------
576 -- Set_Valid --
577 ---------------
578
579 procedure Set_Valid (Storage : System.Address; Value : Boolean) is
580 Int_Storage : constant Integer_Address := To_Integer (Storage);
581 Block_Number : constant Integer_Address :=
582 Int_Storage / Memory_Chunk_Size;
583 Ptr : Validity_Bits_Ref := Validy_Htable.Get (Block_Number);
584 Offset : constant Integer_Address :=
585 (Int_Storage - (Block_Number * Memory_Chunk_Size)) /
586 Default_Alignment;
587 Bit : constant Byte :=
588 2 ** Natural (Offset mod System.Storage_Unit);
2989065e 589
11f03980
VC
590 begin
591 if Ptr = No_Validity_Bits then
fbf5a39b 592
11f03980
VC
593 -- First time in this memory area: allocate a new block and put
594 -- it in the table.
fbf5a39b 595
11f03980
VC
596 if Value then
597 Ptr := To_Pointer (Alloc (size_t (Max_Validity_Byte_Index)));
598 Validy_Htable.Set (Block_Number, Ptr);
599 Memset (Ptr.all'Address, 0, size_t (Max_Validity_Byte_Index));
600 Ptr (Offset / System.Storage_Unit) := Bit;
601 end if;
fbf5a39b 602
11f03980
VC
603 else
604 if Value then
605 Ptr (Offset / System.Storage_Unit) :=
606 Ptr (Offset / System.Storage_Unit) or Bit;
fbf5a39b 607
11f03980
VC
608 else
609 Ptr (Offset / System.Storage_Unit) :=
610 Ptr (Offset / System.Storage_Unit) and (not Bit);
611 end if;
612 end if;
613 end Set_Valid;
fbf5a39b 614
11f03980 615 end Validity;
38cbfe40
RK
616
617 --------------
618 -- Allocate --
619 --------------
620
621 procedure Allocate
622 (Pool : in out Debug_Pool;
623 Storage_Address : out Address;
624 Size_In_Storage_Elements : Storage_Count;
07fc65c4
GB
625 Alignment : Storage_Count)
626 is
fbf5a39b
AC
627 pragma Unreferenced (Alignment);
628 -- Ignored, we always force 'Default_Alignment
629
630 type Local_Storage_Array is new Storage_Array
631 (1 .. Size_In_Storage_Elements + Minimum_Allocation);
fbf5a39b
AC
632
633 type Ptr is access Local_Storage_Array;
11f03980
VC
634 -- On some systems, we might want to physically protect pages against
635 -- writing when they have been freed (of course, this is expensive in
636 -- terms of wasted memory). To do that, all we should have to do it to
637 -- set the size of this array to the page size. See mprotect().
fbf5a39b
AC
638
639 P : Ptr;
640
641 Current : Byte_Count;
642 Trace : Traceback_Htable_Elem_Ptr;
07fc65c4 643
38cbfe40 644 begin
fbf5a39b
AC
645 <<Allocate_Label>>
646 Lock_Task.all;
38cbfe40 647
fbf5a39b
AC
648 -- If necessary, start physically releasing memory. The reason this is
649 -- done here, although Pool.Logically_Deallocated has not changed above,
11f03980
VC
650 -- is so that we do this only after a series of deallocations (e.g loop
651 -- that deallocates a big array). If we were doing that in Deallocate,
652 -- we might be physically freeing memory several times during the loop,
653 -- which is expensive.
38cbfe40 654
fbf5a39b
AC
655 if Pool.Logically_Deallocated >
656 Byte_Count (Pool.Maximum_Logically_Freed_Memory)
657 then
658 Free_Physically (Pool);
659 end if;
660
661 -- Use standard (ie through malloc) allocations. This automatically
662 -- raises Storage_Error if needed. We also try once more to physically
663 -- release memory, so that even marked blocks, in the advanced scanning,
664 -- are freed.
665
666 begin
667 P := new Local_Storage_Array;
668
669 exception
670 when Storage_Error =>
671 Free_Physically (Pool);
672 P := new Local_Storage_Array;
673 end;
674
2989065e
RD
675 Storage_Address :=
676 System.Null_Address + Default_Alignment
677 * (((P.all'Address + Default_Alignment - 1) - System.Null_Address)
678 / Default_Alignment)
cc335f43 679 + Header_Offset;
2989065e 680
cc335f43
AC
681 pragma Assert ((Storage_Address - System.Null_Address)
682 mod Default_Alignment = 0);
683 pragma Assert (Storage_Address + Size_In_Storage_Elements
684 <= P.all'Address + P'Length);
fbf5a39b
AC
685
686 Trace := Find_Or_Create_Traceback
687 (Pool, Alloc, Size_In_Storage_Elements,
688 Allocate_Label'Address, Code_Address_For_Allocate_End);
689
690 pragma Warnings (Off);
11f03980
VC
691 -- Turn warning on alignment for convert call off. We know that in fact
692 -- this conversion is safe since P itself is always aligned on
fbf5a39b
AC
693 -- Default_Alignment.
694
695 Header_Of (Storage_Address).all :=
cc335f43
AC
696 (Allocation_Address => P.all'Address,
697 Alloc_Traceback => Trace,
698 Dealloc_Traceback => To_Traceback (null),
699 Next => Pool.First_Used_Block,
700 Block_Size => Size_In_Storage_Elements);
fbf5a39b
AC
701
702 pragma Warnings (On);
703
704 -- Link this block in the list of used blocks. This will be used to list
705 -- memory leaks in Print_Info, and for the advanced schemes of
706 -- Physical_Free, where we want to traverse all allocated blocks and
707 -- search for possible references.
708
709 -- We insert in front, since most likely we'll be freeing the most
710 -- recently allocated blocks first (the older one might stay allocated
711 -- for the whole life of the application).
712
713 if Pool.First_Used_Block /= System.Null_Address then
714 Header_Of (Pool.First_Used_Block).Dealloc_Traceback :=
715 To_Address (Storage_Address);
716 end if;
717
718 Pool.First_Used_Block := Storage_Address;
719
720 -- Mark the new address as valid
721
722 Set_Valid (Storage_Address, True);
723
724 -- Update internal data
725
726 Pool.Allocated :=
727 Pool.Allocated + Byte_Count (Size_In_Storage_Elements);
728
729 Current := Pool.Allocated -
730 Pool.Logically_Deallocated -
731 Pool.Physically_Deallocated;
732
733 if Current > Pool.High_Water then
734 Pool.High_Water := Current;
38cbfe40 735 end if;
fbf5a39b
AC
736
737 Unlock_Task.all;
af4b9434
AC
738
739 exception
740 when others =>
741 Unlock_Task.all;
742 raise;
38cbfe40
RK
743 end Allocate;
744
fbf5a39b
AC
745 ------------------
746 -- Allocate_End --
747 ------------------
748
11f03980
VC
749 -- DO NOT MOVE, this must be right after Allocate. This is similar to what
750 -- is done in a-except, so that we can hide the traceback frames internal
751 -- to this package
fbf5a39b
AC
752
753 procedure Allocate_End is
754 begin
755 <<Allocate_End_Label>>
756 Code_Address_For_Allocate_End := Allocate_End_Label'Address;
757 end Allocate_End;
758
759 -------------------
760 -- Set_Dead_Beef --
761 -------------------
762
763 procedure Set_Dead_Beef
764 (Storage_Address : System.Address;
765 Size_In_Storage_Elements : Storage_Count)
766 is
767 Dead_Bytes : constant := 4;
768
769 type Data is mod 2 ** (Dead_Bytes * 8);
770 for Data'Size use Dead_Bytes * 8;
771
772 Dead : constant Data := 16#DEAD_BEEF#;
773
774 type Dead_Memory is array
775 (1 .. Size_In_Storage_Elements / Dead_Bytes) of Data;
776 type Mem_Ptr is access Dead_Memory;
777
778 type Byte is mod 2 ** 8;
779 for Byte'Size use 8;
780
781 type Dead_Memory_Bytes is array (0 .. 2) of Byte;
782 type Dead_Memory_Bytes_Ptr is access Dead_Memory_Bytes;
783
784 function From_Ptr is new Ada.Unchecked_Conversion
785 (System.Address, Mem_Ptr);
786
787 function From_Ptr is new Ada.Unchecked_Conversion
788 (System.Address, Dead_Memory_Bytes_Ptr);
789
790 M : constant Mem_Ptr := From_Ptr (Storage_Address);
791 M2 : Dead_Memory_Bytes_Ptr;
792 Modulo : constant Storage_Count :=
793 Size_In_Storage_Elements mod Dead_Bytes;
794 begin
795 M.all := (others => Dead);
796
797 -- Any bytes left (up to three of them)
798
799 if Modulo /= 0 then
800 M2 := From_Ptr (Storage_Address + M'Length * Dead_Bytes);
801
802 M2 (0) := 16#DE#;
803 if Modulo >= 2 then
804 M2 (1) := 16#AD#;
805
806 if Modulo >= 3 then
807 M2 (2) := 16#BE#;
808 end if;
809 end if;
810 end if;
811 end Set_Dead_Beef;
812
813 ---------------------
814 -- Free_Physically --
815 ---------------------
816
817 procedure Free_Physically (Pool : in out Debug_Pool) is
818 type Byte is mod 256;
819 type Byte_Access is access Byte;
820
821 function To_Byte is new Ada.Unchecked_Conversion
822 (System.Address, Byte_Access);
823
824 type Address_Access is access System.Address;
825
826 function To_Address_Access is new Ada.Unchecked_Conversion
827 (System.Address, Address_Access);
828
829 In_Use_Mark : constant Byte := 16#D#;
830 Free_Mark : constant Byte := 16#F#;
831
832 Total_Freed : Storage_Count := 0;
833
834 procedure Reset_Marks;
835 -- Unmark all the logically freed blocks, so that they are considered
836 -- for physical deallocation
837
838 procedure Mark
839 (H : Allocation_Header_Access; A : System.Address; In_Use : Boolean);
840 -- Mark the user data block starting at A. For a block of size zero,
841 -- nothing is done. For a block with a different size, the first byte
842 -- is set to either "D" (in use) or "F" (free).
843
844 function Marked (A : System.Address) return Boolean;
845 -- Return true if the user data block starting at A might be in use
846 -- somewhere else
847
848 procedure Mark_Blocks;
849 -- Traverse all allocated blocks, and search for possible references
850 -- to logically freed blocks. Mark them appropriately
851
852 procedure Free_Blocks (Ignore_Marks : Boolean);
853 -- Physically release blocks. Only the blocks that haven't been marked
854 -- will be released, unless Ignore_Marks is true.
855
856 -----------------
857 -- Free_Blocks --
858 -----------------
859
860 procedure Free_Blocks (Ignore_Marks : Boolean) is
861 Header : Allocation_Header_Access;
862 Tmp : System.Address := Pool.First_Free_Block;
863 Next : System.Address;
864 Previous : System.Address := System.Null_Address;
865
866 begin
867 while Tmp /= System.Null_Address
868 and then Total_Freed < Pool.Minimum_To_Free
869 loop
870 Header := Header_Of (Tmp);
871
872 -- If we know, or at least assume, the block is no longer
11f03980 873 -- referenced anywhere, we can free it physically.
fbf5a39b
AC
874
875 if Ignore_Marks or else not Marked (Tmp) then
876
877 declare
878 pragma Suppress (All_Checks);
879 -- Suppress the checks on this section. If they are overflow
880 -- errors, it isn't critical, and we'd rather avoid a
881 -- Constraint_Error in that case.
882 begin
883 -- Note that block_size < zero for freed blocks
884
885 Pool.Physically_Deallocated :=
886 Pool.Physically_Deallocated -
887 Byte_Count (Header.Block_Size);
888
889 Pool.Logically_Deallocated :=
890 Pool.Logically_Deallocated +
891 Byte_Count (Header.Block_Size);
892
893 Total_Freed := Total_Freed - Header.Block_Size;
894 end;
895
896 Next := Header.Next;
cc335f43 897 System.Memory.Free (Header.Allocation_Address);
fbf5a39b
AC
898 Set_Valid (Tmp, False);
899
2989065e 900 -- Remove this block from the list
fbf5a39b
AC
901
902 if Previous = System.Null_Address then
903 Pool.First_Free_Block := Next;
904 else
905 Header_Of (Previous).Next := Next;
906 end if;
907
908 Tmp := Next;
909
910 else
911 Previous := Tmp;
912 Tmp := Header.Next;
913 end if;
914 end loop;
915 end Free_Blocks;
916
917 ----------
918 -- Mark --
919 ----------
920
921 procedure Mark
922 (H : Allocation_Header_Access;
923 A : System.Address;
924 In_Use : Boolean)
925 is
926 begin
927 if H.Block_Size /= 0 then
928 if In_Use then
929 To_Byte (A).all := In_Use_Mark;
930 else
931 To_Byte (A).all := Free_Mark;
932 end if;
933 end if;
934 end Mark;
935
936 -----------------
937 -- Mark_Blocks --
938 -----------------
939
940 procedure Mark_Blocks is
941 Tmp : System.Address := Pool.First_Used_Block;
942 Previous : System.Address;
943 Last : System.Address;
944 Pointed : System.Address;
945 Header : Allocation_Header_Access;
946
947 begin
948 -- For each allocated block, check its contents. Things that look
949 -- like a possible address are used to mark the blocks so that we try
950 -- and keep them, for better detection in case of invalid access.
951 -- This mechanism is far from being fool-proof: it doesn't check the
952 -- stacks of the threads, doesn't check possible memory allocated not
953 -- under control of this debug pool. But it should allow us to catch
954 -- more cases.
955
956 while Tmp /= System.Null_Address loop
957 Previous := Tmp;
958 Last := Tmp + Header_Of (Tmp).Block_Size;
959 while Previous < Last loop
960 -- ??? Should we move byte-per-byte, or consider that addresses
961 -- are always aligned on 4-bytes boundaries ? Let's use the
962 -- fastest for now.
963
964 Pointed := To_Address_Access (Previous).all;
965 if Is_Valid (Pointed) then
966 Header := Header_Of (Pointed);
967
968 -- Do not even attempt to mark blocks in use. That would
969 -- screw up the whole application, of course.
11f03980 970
fbf5a39b
AC
971 if Header.Block_Size < 0 then
972 Mark (Header, Pointed, In_Use => True);
973 end if;
974 end if;
975
976 Previous := Previous + System.Address'Size;
977 end loop;
978
979 Tmp := Header_Of (Tmp).Next;
980 end loop;
981 end Mark_Blocks;
982
983 ------------
984 -- Marked --
985 ------------
986
987 function Marked (A : System.Address) return Boolean is
988 begin
989 return To_Byte (A).all = In_Use_Mark;
990 end Marked;
991
992 -----------------
993 -- Reset_Marks --
994 -----------------
995
996 procedure Reset_Marks is
997 Current : System.Address := Pool.First_Free_Block;
998 Header : Allocation_Header_Access;
fbf5a39b
AC
999 begin
1000 while Current /= System.Null_Address loop
1001 Header := Header_Of (Current);
1002 Mark (Header, Current, False);
1003 Current := Header.Next;
1004 end loop;
1005 end Reset_Marks;
1006
1007 -- Start of processing for Free_Physically
1008
1009 begin
1010 Lock_Task.all;
1011
1012 if Pool.Advanced_Scanning then
11f03980
VC
1013
1014 -- Reset the mark for each freed block
1015
1016 Reset_Marks;
1017
fbf5a39b
AC
1018 Mark_Blocks;
1019 end if;
1020
1021 Free_Blocks (Ignore_Marks => not Pool.Advanced_Scanning);
1022
1023 -- The contract is that we need to free at least Minimum_To_Free bytes,
1024 -- even if this means freeing marked blocks in the advanced scheme
1025
1026 if Total_Freed < Pool.Minimum_To_Free
1027 and then Pool.Advanced_Scanning
1028 then
1029 Pool.Marked_Blocks_Deallocated := True;
1030 Free_Blocks (Ignore_Marks => True);
1031 end if;
1032
1033 Unlock_Task.all;
af4b9434
AC
1034
1035 exception
1036 when others =>
1037 Unlock_Task.all;
1038 raise;
fbf5a39b
AC
1039 end Free_Physically;
1040
38cbfe40
RK
1041 ----------------
1042 -- Deallocate --
1043 ----------------
1044
1045 procedure Deallocate
1046 (Pool : in out Debug_Pool;
1047 Storage_Address : Address;
1048 Size_In_Storage_Elements : Storage_Count;
1049 Alignment : Storage_Count)
1050 is
fbf5a39b
AC
1051 pragma Unreferenced (Alignment);
1052
1053 Header : constant Allocation_Header_Access :=
1054 Header_Of (Storage_Address);
1055 Valid : Boolean;
1056 Previous : System.Address;
1057
1058 begin
1059 <<Deallocate_Label>>
1060 Lock_Task.all;
1061 Valid := Is_Valid (Storage_Address);
1062
1063 if not Valid then
1064 Unlock_Task.all;
1065 if Pool.Raise_Exceptions then
1066 raise Freeing_Not_Allocated_Storage;
1067 else
5453d5bd 1068 Put ("error: Freeing not allocated storage, at ");
fbf5a39b
AC
1069 Put_Line (Pool.Stack_Trace_Depth, null,
1070 Deallocate_Label'Address,
1071 Code_Address_For_Deallocate_End);
1072 end if;
07fc65c4 1073
fbf5a39b
AC
1074 elsif Header.Block_Size < 0 then
1075 Unlock_Task.all;
1076 if Pool.Raise_Exceptions then
1077 raise Freeing_Deallocated_Storage;
1078 else
5453d5bd 1079 Put ("error: Freeing already deallocated storage, at ");
fbf5a39b
AC
1080 Put_Line (Pool.Stack_Trace_Depth, null,
1081 Deallocate_Label'Address,
1082 Code_Address_For_Deallocate_End);
1083 Put (" Memory already deallocated at ");
1084 Put_Line (0, To_Traceback (Header.Dealloc_Traceback).Traceback);
ee936a69
EB
1085 Put (" Memory was allocated at ");
1086 Put_Line (0, Header.Alloc_Traceback.Traceback);
fbf5a39b 1087 end if;
38cbfe40 1088
fbf5a39b 1089 else
2989065e 1090 -- Remove this block from the list of used blocks
38cbfe40 1091
fbf5a39b
AC
1092 Previous :=
1093 To_Address (Header_Of (Storage_Address).Dealloc_Traceback);
38cbfe40 1094
fbf5a39b
AC
1095 if Previous = System.Null_Address then
1096 Pool.First_Used_Block := Header_Of (Pool.First_Used_Block).Next;
38cbfe40 1097
fbf5a39b
AC
1098 if Pool.First_Used_Block /= System.Null_Address then
1099 Header_Of (Pool.First_Used_Block).Dealloc_Traceback :=
1100 To_Traceback (null);
1101 end if;
38cbfe40 1102
fbf5a39b
AC
1103 else
1104 Header_Of (Previous).Next := Header_Of (Storage_Address).Next;
38cbfe40 1105
fbf5a39b
AC
1106 if Header_Of (Storage_Address).Next /= System.Null_Address then
1107 Header_Of
1108 (Header_Of (Storage_Address).Next).Dealloc_Traceback :=
1109 To_Address (Previous);
1110 end if;
38cbfe40 1111 end if;
38cbfe40 1112
fbf5a39b 1113 -- Update the header
38cbfe40 1114
fbf5a39b 1115 Header.all :=
cc335f43
AC
1116 (Allocation_Address => Header.Allocation_Address,
1117 Alloc_Traceback => Header.Alloc_Traceback,
1118 Dealloc_Traceback => To_Traceback
1119 (Find_Or_Create_Traceback
1120 (Pool, Dealloc,
1121 Size_In_Storage_Elements,
1122 Deallocate_Label'Address,
1123 Code_Address_For_Deallocate_End)),
1124 Next => System.Null_Address,
1125 Block_Size => -Size_In_Storage_Elements);
38cbfe40 1126
fbf5a39b
AC
1127 if Pool.Reset_Content_On_Free then
1128 Set_Dead_Beef (Storage_Address, Size_In_Storage_Elements);
1129 end if;
38cbfe40 1130
fbf5a39b
AC
1131 Pool.Logically_Deallocated :=
1132 Pool.Logically_Deallocated +
1133 Byte_Count (Size_In_Storage_Elements);
38cbfe40 1134
fbf5a39b
AC
1135 -- Link this free block with the others (at the end of the list, so
1136 -- that we can start releasing the older blocks first later on).
1137
1138 if Pool.First_Free_Block = System.Null_Address then
1139 Pool.First_Free_Block := Storage_Address;
1140 Pool.Last_Free_Block := Storage_Address;
1141
1142 else
1143 Header_Of (Pool.Last_Free_Block).Next := Storage_Address;
1144 Pool.Last_Free_Block := Storage_Address;
1145 end if;
1146
1147 -- Do not physically release the memory here, but in Alloc.
1148 -- See comment there for details.
1149
1150 Unlock_Task.all;
1151 end if;
af4b9434
AC
1152
1153 exception
1154 when others =>
1155 Unlock_Task.all;
1156 raise;
38cbfe40
RK
1157 end Deallocate;
1158
fbf5a39b
AC
1159 --------------------
1160 -- Deallocate_End --
1161 --------------------
1162
1163 -- DO NOT MOVE, this must be right after Deallocate
11f03980 1164
fbf5a39b
AC
1165 -- See Allocate_End
1166
11f03980
VC
1167 -- This is making assumptions about code order that may be invalid ???
1168
fbf5a39b
AC
1169 procedure Deallocate_End is
1170 begin
1171 <<Deallocate_End_Label>>
1172 Code_Address_For_Deallocate_End := Deallocate_End_Label'Address;
1173 end Deallocate_End;
1174
38cbfe40
RK
1175 -----------------
1176 -- Dereference --
1177 -----------------
1178
1179 procedure Dereference
1180 (Pool : in out Debug_Pool;
1181 Storage_Address : Address;
1182 Size_In_Storage_Elements : Storage_Count;
1183 Alignment : Storage_Count)
1184 is
fbf5a39b 1185 pragma Unreferenced (Alignment, Size_In_Storage_Elements);
07fc65c4 1186
fbf5a39b
AC
1187 Valid : constant Boolean := Is_Valid (Storage_Address);
1188 Header : Allocation_Header_Access;
38cbfe40
RK
1189
1190 begin
fbf5a39b
AC
1191 -- Locking policy: we do not do any locking in this procedure. The
1192 -- tables are only read, not written to, and although a problem might
1193 -- appear if someone else is modifying the tables at the same time, this
1194 -- race condition is not intended to be detected by this storage_pool (a
1195 -- now invalid pointer would appear as valid). Instead, we prefer
1196 -- optimum performance for dereferences.
38cbfe40 1197
fbf5a39b 1198 <<Dereference_Label>>
38cbfe40 1199
fbf5a39b
AC
1200 if not Valid then
1201 if Pool.Raise_Exceptions then
38cbfe40 1202 raise Accessing_Not_Allocated_Storage;
fbf5a39b 1203 else
5453d5bd 1204 Put ("error: Accessing not allocated storage, at ");
fbf5a39b
AC
1205 Put_Line (Pool.Stack_Trace_Depth, null,
1206 Dereference_Label'Address,
1207 Code_Address_For_Dereference_End);
1208 end if;
38cbfe40 1209
fbf5a39b
AC
1210 else
1211 Header := Header_Of (Storage_Address);
38cbfe40 1212
fbf5a39b
AC
1213 if Header.Block_Size < 0 then
1214 if Pool.Raise_Exceptions then
1215 raise Accessing_Deallocated_Storage;
1216 else
5453d5bd 1217 Put ("error: Accessing deallocated storage, at ");
fbf5a39b
AC
1218 Put_Line
1219 (Pool.Stack_Trace_Depth, null,
1220 Dereference_Label'Address,
1221 Code_Address_For_Dereference_End);
1222 Put (" First deallocation at ");
1223 Put_Line (0, To_Traceback (Header.Dealloc_Traceback).Traceback);
ee936a69
EB
1224 Put (" Initial allocation at ");
1225 Put_Line (0, Header.Alloc_Traceback.Traceback);
fbf5a39b
AC
1226 end if;
1227 end if;
1228 end if;
38cbfe40
RK
1229 end Dereference;
1230
fbf5a39b
AC
1231 ---------------------
1232 -- Dereference_End --
1233 ---------------------
1234
1235 -- DO NOT MOVE: this must be right after Dereference
11f03980 1236
fbf5a39b 1237 -- See Allocate_End
38cbfe40 1238
11f03980
VC
1239 -- This is making assumptions about code order that may be invalid ???
1240
fbf5a39b 1241 procedure Dereference_End is
38cbfe40 1242 begin
fbf5a39b
AC
1243 <<Dereference_End_Label>>
1244 Code_Address_For_Dereference_End := Dereference_End_Label'Address;
1245 end Dereference_End;
38cbfe40
RK
1246
1247 ----------------
1248 -- Print_Info --
1249 ----------------
1250
fbf5a39b
AC
1251 procedure Print_Info
1252 (Pool : Debug_Pool;
1253 Cumulate : Boolean := False;
1254 Display_Slots : Boolean := False;
1255 Display_Leaks : Boolean := False)
1256 is
38cbfe40 1257
fbf5a39b
AC
1258 package Backtrace_Htable_Cumulate is new GNAT.HTable.Static_HTable
1259 (Header_Num => Header,
1260 Element => Traceback_Htable_Elem,
1261 Elmt_Ptr => Traceback_Htable_Elem_Ptr,
1262 Null_Ptr => null,
1263 Set_Next => Set_Next,
1264 Next => Next,
1265 Key => Tracebacks_Array_Access,
1266 Get_Key => Get_Key,
1267 Hash => Hash,
1268 Equal => Equal);
1269 -- This needs a comment ??? probably some of the ones below do too???
1270
1271 Data : Traceback_Htable_Elem_Ptr;
1272 Elem : Traceback_Htable_Elem_Ptr;
1273 Current : System.Address;
1274 Header : Allocation_Header_Access;
1275 K : Traceback_Kind;
1276
38cbfe40 1277 begin
fbf5a39b
AC
1278 Put_Line
1279 ("Total allocated bytes : " &
1280 Byte_Count'Image (Pool.Allocated));
38cbfe40 1281
fbf5a39b
AC
1282 Put_Line
1283 ("Total logically deallocated bytes : " &
1284 Byte_Count'Image (Pool.Logically_Deallocated));
38cbfe40 1285
fbf5a39b
AC
1286 Put_Line
1287 ("Total physically deallocated bytes : " &
1288 Byte_Count'Image (Pool.Physically_Deallocated));
1289
1290 if Pool.Marked_Blocks_Deallocated then
1291 Put_Line ("Marked blocks were physically deallocated. This is");
1292 Put_Line ("potentially dangereous, and you might want to run");
1293 Put_Line ("again with a lower value of Minimum_To_Free");
1294 end if;
1295
1296 Put_Line
1297 ("Current Water Mark: " &
1298 Byte_Count'Image
1299 (Pool.Allocated - Pool.Logically_Deallocated
1300 - Pool.Physically_Deallocated));
1301
1302 Put_Line
1303 ("High Water Mark: " &
1304 Byte_Count'Image (Pool.High_Water));
38cbfe40 1305
38cbfe40 1306 Put_Line ("");
fbf5a39b 1307
af4b9434
AC
1308 if Display_Slots then
1309 Data := Backtrace_Htable.Get_First;
1310 while Data /= null loop
1311 if Data.Kind in Alloc .. Dealloc then
1312 Elem :=
1313 new Traceback_Htable_Elem'
1314 (Traceback => new Tracebacks_Array'(Data.Traceback.all),
1315 Count => Data.Count,
1316 Kind => Data.Kind,
1317 Total => Data.Total,
1318 Next => null);
1319 Backtrace_Htable_Cumulate.Set (Elem);
1320
1321 if Cumulate then
1322 if Data.Kind = Alloc then
1323 K := Indirect_Alloc;
1324 else
1325 K := Indirect_Dealloc;
1326 end if;
fbf5a39b 1327
af4b9434 1328 -- Propagate the direct call to all its parents
fbf5a39b 1329
af4b9434
AC
1330 for T in Data.Traceback'First + 1 .. Data.Traceback'Last loop
1331 Elem := Backtrace_Htable_Cumulate.Get
1332 (Data.Traceback
1333 (T .. Data.Traceback'Last)'Unrestricted_Access);
fbf5a39b 1334
af4b9434 1335 -- If not, insert it
fbf5a39b 1336
af4b9434
AC
1337 if Elem = null then
1338 Elem := new Traceback_Htable_Elem'
1339 (Traceback => new Tracebacks_Array'
1340 (Data.Traceback (T .. Data.Traceback'Last)),
1341 Count => Data.Count,
1342 Kind => K,
1343 Total => Data.Total,
1344 Next => null);
1345 Backtrace_Htable_Cumulate.Set (Elem);
fbf5a39b 1346
af4b9434
AC
1347 -- Properly take into account that the subprograms
1348 -- indirectly called might be doing either allocations
1349 -- or deallocations. This needs to be reflected in the
1350 -- counts.
fbf5a39b 1351
af4b9434
AC
1352 else
1353 Elem.Count := Elem.Count + Data.Count;
fbf5a39b 1354
af4b9434
AC
1355 if K = Elem.Kind then
1356 Elem.Total := Elem.Total + Data.Total;
fbf5a39b 1357
af4b9434
AC
1358 elsif Elem.Total > Data.Total then
1359 Elem.Total := Elem.Total - Data.Total;
fbf5a39b 1360
af4b9434
AC
1361 else
1362 Elem.Kind := K;
1363 Elem.Total := Data.Total - Elem.Total;
1364 end if;
fbf5a39b 1365 end if;
af4b9434
AC
1366 end loop;
1367 end if;
fbf5a39b 1368
af4b9434
AC
1369 Data := Backtrace_Htable.Get_Next;
1370 end if;
1371 end loop;
fbf5a39b 1372
fbf5a39b
AC
1373 Put_Line ("List of allocations/deallocations: ");
1374
1375 Data := Backtrace_Htable_Cumulate.Get_First;
1376 while Data /= null loop
1377 case Data.Kind is
1378 when Alloc => Put ("alloc (count:");
1379 when Indirect_Alloc => Put ("indirect alloc (count:");
1380 when Dealloc => Put ("free (count:");
1381 when Indirect_Dealloc => Put ("indirect free (count:");
1382 end case;
1383
1384 Put (Natural'Image (Data.Count) & ", total:" &
1385 Byte_Count'Image (Data.Total) & ") ");
1386
1387 for T in Data.Traceback'Range loop
1388 Put ("0x" & Address_Image (PC_For (Data.Traceback (T))) & ' ');
1389 end loop;
1390
1391 Put_Line ("");
1392
1393 Data := Backtrace_Htable_Cumulate.Get_Next;
1394 end loop;
af4b9434
AC
1395
1396 Backtrace_Htable_Cumulate.Reset;
fbf5a39b
AC
1397 end if;
1398
1399 if Display_Leaks then
1400 Put_Line ("");
1401 Put_Line ("List of not deallocated blocks:");
1402
1403 -- Do not try to group the blocks with the same stack traces
1404 -- together. This is done by the gnatmem output.
1405
1406 Current := Pool.First_Used_Block;
1407 while Current /= System.Null_Address loop
1408 Header := Header_Of (Current);
1409
1410 Put ("Size: " & Storage_Count'Image (Header.Block_Size) & " at: ");
1411
1412 for T in Header.Alloc_Traceback.Traceback'Range loop
1413 Put ("0x" & Address_Image
1414 (PC_For (Header.Alloc_Traceback.Traceback (T))) & ' ');
1415 end loop;
1416
1417 Put_Line ("");
1418 Current := Header.Next;
1419 end loop;
1420 end if;
38cbfe40
RK
1421 end Print_Info;
1422
1423 ------------------
1424 -- Storage_Size --
1425 ------------------
1426
1427 function Storage_Size (Pool : Debug_Pool) return Storage_Count is
fbf5a39b 1428 pragma Unreferenced (Pool);
38cbfe40
RK
1429 begin
1430 return Storage_Count'Last;
1431 end Storage_Size;
1432
fbf5a39b
AC
1433 ---------------
1434 -- Configure --
1435 ---------------
1436
1437 procedure Configure
1438 (Pool : in out Debug_Pool;
1439 Stack_Trace_Depth : Natural := Default_Stack_Trace_Depth;
1440 Maximum_Logically_Freed_Memory : SSC := Default_Max_Freed;
1441 Minimum_To_Free : SSC := Default_Min_Freed;
1442 Reset_Content_On_Free : Boolean := Default_Reset_Content;
1443 Raise_Exceptions : Boolean := Default_Raise_Exceptions;
1444 Advanced_Scanning : Boolean := Default_Advanced_Scanning)
1445 is
1446 begin
1447 Pool.Stack_Trace_Depth := Stack_Trace_Depth;
1448 Pool.Maximum_Logically_Freed_Memory := Maximum_Logically_Freed_Memory;
1449 Pool.Reset_Content_On_Free := Reset_Content_On_Free;
1450 Pool.Raise_Exceptions := Raise_Exceptions;
1451 Pool.Minimum_To_Free := Minimum_To_Free;
1452 Pool.Advanced_Scanning := Advanced_Scanning;
1453 end Configure;
1454
1455 ----------------
1456 -- Print_Pool --
1457 ----------------
1458
1459 procedure Print_Pool (A : System.Address) is
1460 Storage : constant Address := A;
1461 Valid : constant Boolean := Is_Valid (Storage);
1462 Header : Allocation_Header_Access;
1463
1464 begin
1465 -- We might get Null_Address if the call from gdb was done
1466 -- incorrectly. For instance, doing a "print_pool(my_var)" passes 0x0,
1467 -- instead of passing the value of my_var
1468
1469 if A = System.Null_Address then
1470 Put_Line ("Memory not under control of the storage pool");
1471 return;
1472 end if;
1473
1474 if not Valid then
1475 Put_Line ("Memory not under control of the storage pool");
1476
1477 else
1478 Header := Header_Of (Storage);
1479 Put_Line ("0x" & Address_Image (A)
1480 & " allocated at:");
1481 Put_Line (0, Header.Alloc_Traceback.Traceback);
1482
1483 if To_Traceback (Header.Dealloc_Traceback) /= null then
1484 Put_Line ("0x" & Address_Image (A)
1485 & " logically freed memory, deallocated at:");
1486 Put_Line (0, To_Traceback (Header.Dealloc_Traceback).Traceback);
1487 end if;
1488 end if;
1489 end Print_Pool;
1490
1491 -----------------------
1492 -- Print_Info_Stdout --
1493 -----------------------
1494
1495 procedure Print_Info_Stdout
1496 (Pool : Debug_Pool;
1497 Cumulate : Boolean := False;
1498 Display_Slots : Boolean := False;
1499 Display_Leaks : Boolean := False)
1500 is
1501 procedure Internal is new Print_Info
1502 (Put_Line => GNAT.IO.Put_Line,
1503 Put => GNAT.IO.Put);
fbf5a39b
AC
1504 begin
1505 Internal (Pool, Cumulate, Display_Slots, Display_Leaks);
1506 end Print_Info_Stdout;
1507
1508 ------------------
1509 -- Dump_Gnatmem --
1510 ------------------
1511
1512 procedure Dump_Gnatmem (Pool : Debug_Pool; File_Name : String) is
1513 type File_Ptr is new System.Address;
1514
1515 function fopen (Path : String; Mode : String) return File_Ptr;
1516 pragma Import (C, fopen);
1517
1518 procedure fwrite
1519 (Ptr : System.Address;
1520 Size : size_t;
1521 Nmemb : size_t;
1522 Stream : File_Ptr);
1523
1524 procedure fwrite
1525 (Str : String;
1526 Size : size_t;
1527 Nmemb : size_t;
1528 Stream : File_Ptr);
1529 pragma Import (C, fwrite);
1530
1531 procedure fputc (C : Integer; Stream : File_Ptr);
1532 pragma Import (C, fputc);
1533
1534 procedure fclose (Stream : File_Ptr);
1535 pragma Import (C, fclose);
1536
1537 Address_Size : constant size_t :=
1538 System.Address'Max_Size_In_Storage_Elements;
1539 -- Size in bytes of a pointer
1540
1541 File : File_Ptr;
1542 Current : System.Address;
1543 Header : Allocation_Header_Access;
1544 Actual_Size : size_t;
1545 Num_Calls : Integer;
1546 Tracebk : Tracebacks_Array_Access;
1547
1548 begin
1549 File := fopen (File_Name & ASCII.NUL, "wb" & ASCII.NUL);
1550 fwrite ("GMEM DUMP" & ASCII.LF, 10, 1, File);
1551
1552 -- List of not deallocated blocks (see Print_Info)
1553
1554 Current := Pool.First_Used_Block;
1555 while Current /= System.Null_Address loop
1556 Header := Header_Of (Current);
1557
1558 Actual_Size := size_t (Header.Block_Size);
1559 Tracebk := Header.Alloc_Traceback.Traceback;
1560 Num_Calls := Tracebk'Length;
1561
2989065e
RD
1562 -- (Code taken from memtrack.adb in GNAT's sources)
1563
1564 -- Logs allocation call using the format:
1565
fbf5a39b
AC
1566 -- 'A' <mem addr> <size chunk> <len backtrace> <addr1> ... <addrn>
1567
1568 fputc (Character'Pos ('A'), File);
1569 fwrite (Current'Address, Address_Size, 1, File);
1570 fwrite (Actual_Size'Address, size_t'Max_Size_In_Storage_Elements, 1,
1571 File);
1572 fwrite (Num_Calls'Address, Integer'Max_Size_In_Storage_Elements, 1,
1573 File);
1574
1575 for J in Tracebk'First .. Tracebk'First + Num_Calls - 1 loop
1576 declare
1577 Ptr : System.Address := PC_For (Tracebk (J));
1578 begin
1579 fwrite (Ptr'Address, Address_Size, 1, File);
1580 end;
1581 end loop;
1582
1583 Current := Header.Next;
1584 end loop;
1585
1586 fclose (File);
1587 end Dump_Gnatmem;
1588
11f03980
VC
1589-- Package initialization
1590
fbf5a39b
AC
1591begin
1592 Allocate_End;
1593 Deallocate_End;
1594 Dereference_End;
38cbfe40 1595end GNAT.Debug_Pools;