objc-act.c (synth_module_prologue): Use TREE_NO_WARNING instead of DECL_IN_SYSTEM_HEADER.
[official-gcc.git] / gcc / ada / gnatmem.adb
blobd6ac07834a9d249d371e6394ccb2b1bb75548e1f
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT COMPILER COMPONENTS --
4 -- --
5 -- G N A T M E M --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 1997-2008, AdaCore --
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 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. 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 COPYING3. If not, go to --
19 -- http://www.gnu.org/licenses for a complete copy of the license. --
20 -- --
21 -- GNAT was originally developed by the GNAT team at New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
23 -- --
24 ------------------------------------------------------------------------------
26 -- GNATMEM is a utility that tracks memory leaks. It is based on a simple
27 -- idea:
29 -- - Read the allocation log generated by the application linked using
30 -- instrumented memory allocation and deallocation (see memtrack.adb for
31 -- this circuitry). To get access to this functionality, the application
32 -- must be relinked with library libgmem.a:
34 -- $ gnatmake my_prog -largs -lgmem
36 -- The running my_prog will produce a file named gmem.out that will be
37 -- parsed by gnatmem.
39 -- - Record a reference to the allocated memory on each allocation call
41 -- - Suppress this reference on deallocation
43 -- - At the end of the program, remaining references are potential leaks.
44 -- sort them out the best possible way in order to locate the root of
45 -- the leak.
47 -- This capability is not supported on all platforms, please refer to
48 -- memtrack.adb for further information.
50 -- In order to help finding out the real leaks, the notion of "allocation
51 -- root" is defined. An allocation root is a specific point in the program
52 -- execution generating memory allocation where data is collected (such as
53 -- number of allocations, amount of memory allocated, high water mark, etc.)
55 with Ada.Float_Text_IO;
56 with Ada.Integer_Text_IO;
57 with Ada.Text_IO; use Ada.Text_IO;
59 with System; use System;
60 with System.Storage_Elements; use System.Storage_Elements;
62 with GNAT.Command_Line; use GNAT.Command_Line;
63 with GNAT.Heap_Sort_G;
64 with GNAT.OS_Lib; use GNAT.OS_Lib;
65 with GNAT.HTable; use GNAT.HTable;
67 with Gnatvsn; use Gnatvsn;
68 with Memroot; use Memroot;
70 procedure Gnatmem is
72 package Int_IO renames Ada.Integer_Text_IO;
74 ------------------------
75 -- Other Declarations --
76 ------------------------
78 type Storage_Elmt is record
79 Elmt : Character;
80 -- * = End of log file
81 -- A = found a ALLOC mark in the log
82 -- D = found a DEALL mark in the log
84 Address : Integer_Address;
85 Size : Storage_Count;
86 Timestamp : Duration;
87 end record;
88 -- This type is used to read heap operations from the log file.
89 -- Elmt contains the type of the operation, which can be either
90 -- allocation, deallocation, or a special mark indicating the
91 -- end of the log file. Address is used to store address on the
92 -- heap where a chunk was allocated/deallocated, size is only
93 -- for A event and contains size of the allocation, and Timestamp
94 -- is the clock value at the moment of allocation
96 Log_Name : String_Access;
97 -- Holds the name of the heap operations log file
99 Program_Name : String_Access;
100 -- Holds the name of the user executable
102 function Read_Next return Storage_Elmt;
103 -- Reads next dynamic storage operation from the log file
105 function Mem_Image (X : Storage_Count) return String;
106 -- X is a size in storage_element. Returns a value
107 -- in Megabytes, Kilobytes or Bytes as appropriate.
109 procedure Process_Arguments;
110 -- Read command line arguments
112 procedure Usage;
113 -- Prints out the option help
115 function Gmem_Initialize (Dumpname : String) return Boolean;
116 -- Opens the file represented by Dumpname and prepares it for
117 -- work. Returns False if the file does not have the correct format, True
118 -- otherwise.
120 procedure Gmem_A2l_Initialize (Exename : String);
121 -- Initialises the convert_addresses interface by supplying it with
122 -- the name of the executable file Exename
124 -----------------------------------
125 -- HTable address --> Allocation --
126 -----------------------------------
128 type Allocation is record
129 Root : Root_Id;
130 Size : Storage_Count;
131 end record;
133 type Address_Range is range 0 .. 4097;
134 function H (A : Integer_Address) return Address_Range;
135 No_Alloc : constant Allocation := (No_Root_Id, 0);
137 package Address_HTable is new GNAT.HTable.Simple_HTable (
138 Header_Num => Address_Range,
139 Element => Allocation,
140 No_Element => No_Alloc,
141 Key => Integer_Address,
142 Hash => H,
143 Equal => "=");
145 BT_Depth : Integer := 1;
147 -- Some global statistics
149 Global_Alloc_Size : Storage_Count := 0;
150 -- Total number of bytes allocated during the lifetime of a program
152 Global_High_Water_Mark : Storage_Count := 0;
153 -- Largest amount of storage ever in use during the lifetime
155 Global_Nb_Alloc : Integer := 0;
156 -- Total number of allocations
158 Global_Nb_Dealloc : Integer := 0;
159 -- Total number of deallocations
161 Nb_Root : Integer := 0;
162 -- Total number of allocation roots
164 Nb_Wrong_Deall : Integer := 0;
165 -- Total number of wrong deallocations (i.e. without matching alloc)
167 Minimum_Nb_Leaks : Integer := 1;
168 -- How many unfreed allocs should be in a root for it to count as leak
170 T0 : Duration := 0.0;
171 -- The moment at which memory allocation routines initialized (should
172 -- be pretty close to the moment the program started since there are
173 -- always some allocations at RTL elaboration
175 Tmp_Alloc : Allocation;
176 Dump_Log_Mode : Boolean := False;
177 Quiet_Mode : Boolean := False;
179 ------------------------------
180 -- Allocation Roots Sorting --
181 ------------------------------
183 Sort_Order : String (1 .. 3) := "nwh";
184 -- This is the default order in which sorting criteria will be applied
185 -- n - Total number of unfreed allocations
186 -- w - Final watermark
187 -- h - High watermark
189 --------------------------------
190 -- GMEM functionality binding --
191 --------------------------------
193 ---------------------
194 -- Gmem_Initialize --
195 ---------------------
197 function Gmem_Initialize (Dumpname : String) return Boolean is
198 function Initialize (Dumpname : System.Address) return Duration;
199 pragma Import (C, Initialize, "__gnat_gmem_initialize");
201 S : aliased String := Dumpname & ASCII.NUL;
203 begin
204 T0 := Initialize (S'Address);
205 return T0 > 0.0;
206 end Gmem_Initialize;
208 -------------------------
209 -- Gmem_A2l_Initialize --
210 -------------------------
212 procedure Gmem_A2l_Initialize (Exename : String) is
213 procedure A2l_Initialize (Exename : System.Address);
214 pragma Import (C, A2l_Initialize, "__gnat_gmem_a2l_initialize");
216 S : aliased String := Exename & ASCII.NUL;
218 begin
219 A2l_Initialize (S'Address);
220 end Gmem_A2l_Initialize;
222 ---------------
223 -- Read_Next --
224 ---------------
226 function Read_Next return Storage_Elmt is
227 procedure Read_Next (buf : System.Address);
228 pragma Import (C, Read_Next, "__gnat_gmem_read_next");
230 S : Storage_Elmt;
232 begin
233 Read_Next (S'Address);
234 return S;
235 end Read_Next;
237 -------
238 -- H --
239 -------
241 function H (A : Integer_Address) return Address_Range is
242 begin
243 return Address_Range (A mod Integer_Address (Address_Range'Last));
244 end H;
246 ---------------
247 -- Mem_Image --
248 ---------------
250 function Mem_Image (X : Storage_Count) return String is
251 Ks : constant Storage_Count := X / 1024;
252 Megs : constant Storage_Count := Ks / 1024;
253 Buff : String (1 .. 7);
255 begin
256 if Megs /= 0 then
257 Ada.Float_Text_IO.Put (Buff, Float (X) / 1024.0 / 1024.0, 2, 0);
258 return Buff & " Megabytes";
260 elsif Ks /= 0 then
261 Ada.Float_Text_IO.Put (Buff, Float (X) / 1024.0, 2, 0);
262 return Buff & " Kilobytes";
264 else
265 Ada.Integer_Text_IO.Put (Buff (1 .. 4), Integer (X));
266 return Buff (1 .. 4) & " Bytes";
267 end if;
268 end Mem_Image;
270 -----------
271 -- Usage --
272 -----------
274 procedure Usage is
275 begin
276 New_Line;
277 Put ("GNATMEM ");
278 Put_Line (Gnat_Version_String);
279 Put_Line ("Copyright 1997-2007, Free Software Foundation, Inc.");
280 New_Line;
282 Put_Line ("Usage: gnatmem switches [depth] exename");
283 New_Line;
284 Put_Line (" depth backtrace depth to take into account, default is"
285 & Integer'Image (BT_Depth));
286 Put_Line (" exename the name of the executable to be analyzed");
287 New_Line;
288 Put_Line ("Switches:");
289 Put_Line (" -b n same as depth parameter");
290 Put_Line (" -i file read the allocation log from specific file");
291 Put_Line (" default is gmem.out in the current directory");
292 Put_Line (" -m n masks roots with less than n leaks, default is 1");
293 Put_Line (" specify 0 to see even released allocation roots");
294 Put_Line (" -q quiet, minimum output");
295 Put_Line (" -s order sort allocation roots according to an order of");
296 Put_Line (" sort criteria");
297 GNAT.OS_Lib.OS_Exit (1);
298 end Usage;
300 -----------------------
301 -- Process_Arguments --
302 -----------------------
304 procedure Process_Arguments is
305 begin
306 -- Parse the options first
308 loop
309 case Getopt ("b: dd m: i: q s:") is
310 when ASCII.NUL => exit;
312 when 'b' =>
313 begin
314 BT_Depth := Natural'Value (Parameter);
315 exception
316 when Constraint_Error =>
317 Usage;
318 end;
320 when 'd' =>
321 Dump_Log_Mode := True;
323 when 'm' =>
324 begin
325 Minimum_Nb_Leaks := Natural'Value (Parameter);
326 exception
327 when Constraint_Error =>
328 Usage;
329 end;
331 when 'i' =>
332 Log_Name := new String'(Parameter);
334 when 'q' =>
335 Quiet_Mode := True;
337 when 's' =>
338 declare
339 S : constant String (Sort_Order'Range) := Parameter;
340 begin
341 for J in Sort_Order'Range loop
342 if S (J) = 'n' or else
343 S (J) = 'w' or else
344 S (J) = 'h'
345 then
346 Sort_Order (J) := S (J);
347 else
348 Put_Line ("Invalid sort criteria string.");
349 GNAT.OS_Lib.OS_Exit (1);
350 end if;
351 end loop;
352 end;
354 when others =>
355 null;
356 end case;
357 end loop;
359 -- Set default log file if -i hasn't been specified
361 if Log_Name = null then
362 Log_Name := new String'("gmem.out");
363 end if;
365 -- Get the optional backtrace length and program name
367 declare
368 Str1 : constant String := GNAT.Command_Line.Get_Argument;
369 Str2 : constant String := GNAT.Command_Line.Get_Argument;
371 begin
372 if Str1 = "" then
373 Usage;
374 end if;
376 if Str2 = "" then
377 Program_Name := new String'(Str1);
378 else
379 BT_Depth := Natural'Value (Str1);
380 Program_Name := new String'(Str2);
381 end if;
383 exception
384 when Constraint_Error =>
385 Usage;
386 end;
388 -- Ensure presence of executable suffix in Program_Name
390 declare
391 Suffix : String_Access := Get_Executable_Suffix;
392 Tmp : String_Access;
394 begin
395 if Suffix.all /= ""
396 and then
397 Program_Name.all
398 (Program_Name.all'Last - Suffix.all'Length + 1 ..
399 Program_Name.all'Last) /= Suffix.all
400 then
401 Tmp := new String'(Program_Name.all & Suffix.all);
402 Free (Program_Name);
403 Program_Name := Tmp;
404 end if;
406 Free (Suffix);
408 -- Search the executable on the path. If not found in the PATH, we
409 -- default to the current directory. Otherwise, libaddr2line will
410 -- fail with an error:
412 -- (null): Bad address
414 Tmp := Locate_Exec_On_Path (Program_Name.all);
416 if Tmp = null then
417 Tmp := new String'('.' & Directory_Separator & Program_Name.all);
418 end if;
420 Free (Program_Name);
421 Program_Name := Tmp;
422 end;
424 if not Is_Regular_File (Log_Name.all) then
425 Put_Line ("Couldn't find " & Log_Name.all);
426 GNAT.OS_Lib.OS_Exit (1);
427 end if;
429 if not Gmem_Initialize (Log_Name.all) then
430 Put_Line ("File " & Log_Name.all & " is not a gnatmem log file");
431 GNAT.OS_Lib.OS_Exit (1);
432 end if;
434 if not Is_Regular_File (Program_Name.all) then
435 Put_Line ("Couldn't find " & Program_Name.all);
436 end if;
438 Gmem_A2l_Initialize (Program_Name.all);
440 exception
441 when GNAT.Command_Line.Invalid_Switch =>
442 Ada.Text_IO.Put_Line ("Invalid switch : "
443 & GNAT.Command_Line.Full_Switch);
444 Usage;
445 end Process_Arguments;
447 -- Local variables
449 Cur_Elmt : Storage_Elmt;
450 Buff : String (1 .. 16);
452 -- Start of processing for Gnatmem
454 begin
455 Process_Arguments;
457 if Dump_Log_Mode then
458 Put_Line ("Full dump of dynamic memory operations history");
459 Put_Line ("----------------------------------------------");
461 declare
462 function CTime (Clock : Address) return Address;
463 pragma Import (C, CTime, "ctime");
465 Int_T0 : Integer := Integer (T0);
466 CTime_Addr : constant Address := CTime (Int_T0'Address);
468 Buffer : String (1 .. 30);
469 for Buffer'Address use CTime_Addr;
471 begin
472 Put_Line ("Log started at T0 =" & Duration'Image (T0) & " ("
473 & Buffer (1 .. 24) & ")");
474 end;
475 end if;
477 -- Main loop analysing the data generated by the instrumented routines.
478 -- For each allocation, the backtrace is kept and stored in a htable
479 -- whose entry is the address. For each deallocation, we look for the
480 -- corresponding allocation and cancel it.
482 Main : loop
483 Cur_Elmt := Read_Next;
485 case Cur_Elmt.Elmt is
486 when '*' =>
487 exit Main;
489 when 'A' =>
491 -- Read the corresponding back trace
493 Tmp_Alloc.Root := Read_BT (BT_Depth);
495 if Quiet_Mode then
497 if Nb_Alloc (Tmp_Alloc.Root) = 0 then
498 Nb_Root := Nb_Root + 1;
499 end if;
501 Set_Nb_Alloc (Tmp_Alloc.Root, Nb_Alloc (Tmp_Alloc.Root) + 1);
502 Address_HTable.Set (Cur_Elmt.Address, Tmp_Alloc);
504 elsif Cur_Elmt.Size > 0 then
506 -- Update global counters if the allocated size is meaningful
508 Global_Alloc_Size := Global_Alloc_Size + Cur_Elmt.Size;
509 Global_Nb_Alloc := Global_Nb_Alloc + 1;
511 if Global_High_Water_Mark < Global_Alloc_Size then
512 Global_High_Water_Mark := Global_Alloc_Size;
513 end if;
515 -- Update the number of allocation root if this is a new one
517 if Nb_Alloc (Tmp_Alloc.Root) = 0 then
518 Nb_Root := Nb_Root + 1;
519 end if;
521 -- Update allocation root specific counters
523 Set_Alloc_Size (Tmp_Alloc.Root,
524 Alloc_Size (Tmp_Alloc.Root) + Cur_Elmt.Size);
526 Set_Nb_Alloc (Tmp_Alloc.Root, Nb_Alloc (Tmp_Alloc.Root) + 1);
528 if High_Water_Mark (Tmp_Alloc.Root) <
529 Alloc_Size (Tmp_Alloc.Root)
530 then
531 Set_High_Water_Mark (Tmp_Alloc.Root,
532 Alloc_Size (Tmp_Alloc.Root));
533 end if;
535 -- Associate this allocation root to the allocated address
537 Tmp_Alloc.Size := Cur_Elmt.Size;
538 Address_HTable.Set (Cur_Elmt.Address, Tmp_Alloc);
540 end if;
542 when 'D' =>
544 -- Get the corresponding Dealloc_Size and Root
546 Tmp_Alloc := Address_HTable.Get (Cur_Elmt.Address);
548 if Tmp_Alloc.Root = No_Root_Id then
550 -- There was no prior allocation at this address, something is
551 -- very wrong. Mark this allocation root as problematic.
553 Tmp_Alloc.Root := Read_BT (BT_Depth);
555 if Nb_Alloc (Tmp_Alloc.Root) = 0 then
556 Set_Nb_Alloc (Tmp_Alloc.Root, Nb_Alloc (Tmp_Alloc.Root) - 1);
557 Nb_Wrong_Deall := Nb_Wrong_Deall + 1;
558 end if;
560 else
561 -- Update global counters
563 if not Quiet_Mode then
564 Global_Alloc_Size := Global_Alloc_Size - Tmp_Alloc.Size;
565 end if;
567 Global_Nb_Dealloc := Global_Nb_Dealloc + 1;
569 -- Update allocation root specific counters
571 if not Quiet_Mode then
572 Set_Alloc_Size (Tmp_Alloc.Root,
573 Alloc_Size (Tmp_Alloc.Root) - Tmp_Alloc.Size);
574 end if;
576 Set_Nb_Alloc (Tmp_Alloc.Root, Nb_Alloc (Tmp_Alloc.Root) - 1);
578 -- Update the number of allocation root if this one disappears
580 if Nb_Alloc (Tmp_Alloc.Root) = 0
581 and then Minimum_Nb_Leaks > 0 then
582 Nb_Root := Nb_Root - 1;
583 end if;
585 -- Deassociate the deallocated address
587 Address_HTable.Remove (Cur_Elmt.Address);
588 end if;
590 when others =>
591 raise Program_Error;
592 end case;
594 if Dump_Log_Mode then
595 case Cur_Elmt.Elmt is
596 when 'A' =>
597 Put ("ALLOC");
598 Int_IO.Put (Buff (1 .. 16), Integer (Cur_Elmt.Address), 16);
599 Put (Buff);
600 Int_IO.Put (Buff (1 .. 8), Integer (Cur_Elmt.Size));
601 Put (Buff (1 .. 8) & " bytes at moment T0 +");
602 Put_Line (Duration'Image (Cur_Elmt.Timestamp - T0));
604 when 'D' =>
605 Put ("DEALL");
606 Int_IO.Put (Buff (1 .. 16), Integer (Cur_Elmt.Address), 16);
607 Put (Buff);
608 Put_Line (" at moment T0 +"
609 & Duration'Image (Cur_Elmt.Timestamp - T0));
610 when others =>
611 raise Program_Error;
612 end case;
614 Print_BT (Tmp_Alloc.Root);
615 end if;
617 end loop Main;
619 -- Print out general information about overall allocation
621 if not Quiet_Mode then
622 Put_Line ("Global information");
623 Put_Line ("------------------");
625 Put (" Total number of allocations :");
626 Ada.Integer_Text_IO.Put (Global_Nb_Alloc, 4);
627 New_Line;
629 Put (" Total number of deallocations :");
630 Ada.Integer_Text_IO.Put (Global_Nb_Dealloc, 4);
631 New_Line;
633 Put_Line (" Final Water Mark (non freed mem) :"
634 & Mem_Image (Global_Alloc_Size));
635 Put_Line (" High Water Mark :"
636 & Mem_Image (Global_High_Water_Mark));
637 New_Line;
638 end if;
640 -- Print out the back traces corresponding to potential leaks in order
641 -- greatest number of non-deallocated allocations.
643 Print_Back_Traces : declare
644 type Root_Array is array (Natural range <>) of Root_Id;
645 type Access_Root_Array is access Root_Array;
647 Leaks : constant Access_Root_Array :=
648 new Root_Array (0 .. Nb_Root);
649 Leak_Index : Natural := 0;
651 Bogus_Dealls : constant Access_Root_Array :=
652 new Root_Array (1 .. Nb_Wrong_Deall);
653 Deall_Index : Natural := 0;
654 Nb_Alloc_J : Natural := 0;
656 procedure Move (From : Natural; To : Natural);
657 function Lt (Op1, Op2 : Natural) return Boolean;
658 package Root_Sort is new GNAT.Heap_Sort_G (Move, Lt);
660 ----------
661 -- Move --
662 ----------
664 procedure Move (From : Natural; To : Natural) is
665 begin
666 Leaks (To) := Leaks (From);
667 end Move;
669 --------
670 -- Lt --
671 --------
673 function Lt (Op1, Op2 : Natural) return Boolean is
675 function Apply_Sort_Criterion (S : Character) return Integer;
676 -- Applies a specific sort criterion; returns -1, 0 or 1 if Op1 is
677 -- smaller than, equal, or greater than Op2 according to criterion.
679 --------------------------
680 -- Apply_Sort_Criterion --
681 --------------------------
683 function Apply_Sort_Criterion (S : Character) return Integer is
684 LOp1, LOp2 : Integer;
686 begin
687 case S is
688 when 'n' =>
689 LOp1 := Nb_Alloc (Leaks (Op1));
690 LOp2 := Nb_Alloc (Leaks (Op2));
692 when 'w' =>
693 LOp1 := Integer (Alloc_Size (Leaks (Op1)));
694 LOp2 := Integer (Alloc_Size (Leaks (Op2)));
696 when 'h' =>
697 LOp1 := Integer (High_Water_Mark (Leaks (Op1)));
698 LOp2 := Integer (High_Water_Mark (Leaks (Op2)));
700 when others =>
701 return 0; -- Can't actually happen
702 end case;
704 if LOp1 < LOp2 then
705 return -1;
706 elsif LOp1 > LOp2 then
707 return 1;
708 else
709 return 0;
710 end if;
712 exception
713 when Constraint_Error =>
714 return 0;
715 end Apply_Sort_Criterion;
717 -- Local Variables
719 Result : Integer;
721 -- Start of processing for Lt
723 begin
724 for S in Sort_Order'Range loop
725 Result := Apply_Sort_Criterion (Sort_Order (S));
726 if Result = -1 then
727 return False;
728 elsif Result = 1 then
729 return True;
730 end if;
731 end loop;
732 return False;
733 end Lt;
735 -- Start of processing for Print_Back_Traces
737 begin
738 -- Transfer all the relevant Roots in the Leaks and a Bogus_Deall arrays
740 Tmp_Alloc.Root := Get_First;
741 while Tmp_Alloc.Root /= No_Root_Id loop
742 if Nb_Alloc (Tmp_Alloc.Root) = 0 and then Minimum_Nb_Leaks > 0 then
743 null;
745 elsif Nb_Alloc (Tmp_Alloc.Root) < 0 then
746 Deall_Index := Deall_Index + 1;
747 Bogus_Dealls (Deall_Index) := Tmp_Alloc.Root;
749 else
750 Leak_Index := Leak_Index + 1;
751 Leaks (Leak_Index) := Tmp_Alloc.Root;
752 end if;
754 Tmp_Alloc.Root := Get_Next;
755 end loop;
757 -- Print out wrong deallocations
759 if Nb_Wrong_Deall > 0 then
760 Put_Line ("Releasing deallocated memory at :");
761 if not Quiet_Mode then
762 Put_Line ("--------------------------------");
763 end if;
765 for J in 1 .. Bogus_Dealls'Last loop
766 Print_BT (Bogus_Dealls (J), Short => Quiet_Mode);
767 New_Line;
768 end loop;
769 end if;
771 -- Print out all allocation Leaks
773 if Leak_Index > 0 then
775 -- Sort the Leaks so that potentially important leaks appear first
777 Root_Sort.Sort (Leak_Index);
779 for J in 1 .. Leak_Index loop
780 Nb_Alloc_J := Nb_Alloc (Leaks (J));
782 if Nb_Alloc_J >= Minimum_Nb_Leaks then
783 if Quiet_Mode then
784 if Nb_Alloc_J = 1 then
785 Put_Line (" 1 leak at :");
786 else
787 Put_Line (Integer'Image (Nb_Alloc_J) & " leaks at :");
788 end if;
790 else
791 Put_Line ("Allocation Root #" & Integer'Image (J));
792 Put_Line ("-------------------");
794 Put (" Number of non freed allocations :");
795 Ada.Integer_Text_IO.Put (Nb_Alloc_J, 4);
796 New_Line;
798 Put_Line
799 (" Final Water Mark (non freed mem) :"
800 & Mem_Image (Alloc_Size (Leaks (J))));
802 Put_Line
803 (" High Water Mark :"
804 & Mem_Image (High_Water_Mark (Leaks (J))));
806 Put_Line (" Backtrace :");
807 end if;
809 Print_BT (Leaks (J), Short => Quiet_Mode);
810 New_Line;
811 end if;
812 end loop;
813 end if;
814 end Print_Back_Traces;
815 end Gnatmem;