* config/rs6000/rs6000.md: Document why a pattern is not
[official-gcc.git] / gcc / ada / gnatmem.adb
blob21246b05f108b5a16e086ee4617bd38b04b07359
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-2004, Ada Core Technologies, 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, 59 Temple Place - Suite 330, Boston, --
20 -- MA 02111-1307, USA. --
21 -- --
22 -- GNAT was originally developed by the GNAT team at New York University. --
23 -- Extensive contributions were provided by Ada Core Technologies Inc. --
24 -- --
25 ------------------------------------------------------------------------------
27 -- GNATMEM is a utility that tracks memory leaks. It is based on a simple
28 -- idea:
30 -- - Read the allocation log generated by the application linked using
31 -- instrumented memory allocation and dealocation (see memtrack.adb for
32 -- this circuitry). To get access to this functionality, the application
33 -- must be relinked with library libgmem.a:
35 -- $ gnatmake my_prog -largs -lgmem
37 -- The running my_prog will produce a file named gmem.out that will be
38 -- parsed by gnatmem.
40 -- - Record a reference to the allocated memory on each allocation call.
42 -- - Suppress this reference on deallocation.
44 -- - At the end of the program, remaining references are potential leaks.
45 -- sort them out the best possible way in order to locate the root of
46 -- the leak.
48 -- This capability is not supported on all platforms, please refer to
49 -- memtrack.adb for further information.
51 -- In order to help finding out the real leaks, the notion of "allocation
52 -- root" is defined. An allocation root is a specific point in the program
53 -- execution generating memory allocation where data is collected (such as
54 -- number of allocations, amount of memory allocated, high water mark, etc.)
56 with Gnatvsn; use Gnatvsn;
59 with Ada.Text_IO; use Ada.Text_IO;
60 with Ada.Float_Text_IO;
61 with Ada.Integer_Text_IO;
63 with GNAT.Command_Line; use GNAT.Command_Line;
64 with GNAT.Heap_Sort_G;
65 with GNAT.OS_Lib; use GNAT.OS_Lib;
66 with GNAT.HTable; use GNAT.HTable;
68 with System; use System;
69 with System.Storage_Elements; use System.Storage_Elements;
71 with Memroot; use Memroot;
73 procedure Gnatmem is
75 ------------------------
76 -- Other Declarations --
77 ------------------------
79 type Storage_Elmt is record
80 Elmt : Character;
81 -- * = End of log file
82 -- A = found a ALLOC mark in the log
83 -- D = found a DEALL mark in the log
84 Address : Integer_Address;
85 Size : Storage_Count;
86 end record;
87 -- This needs a comment ???
89 Log_Name, Program_Name : String_Access;
90 -- These need comments, and should be on separate lines ???
92 function Read_Next return Storage_Elmt;
93 -- Reads next dynamic storage operation from the log file.
95 function Mem_Image (X : Storage_Count) return String;
96 -- X is a size in storage_element. Returns a value
97 -- in Megabytes, Kilobytes or Bytes as appropriate.
99 procedure Process_Arguments;
100 -- Read command line arguments
102 procedure Usage;
103 -- Prints out the option help
105 function Gmem_Initialize (Dumpname : String) return Boolean;
106 -- Opens the file represented by Dumpname and prepares it for
107 -- work. Returns False if the file does not have the correct format, True
108 -- otherwise.
110 procedure Gmem_A2l_Initialize (Exename : String);
111 -- Initialises the convert_addresses interface by supplying it with
112 -- the name of the executable file Exename
114 -----------------------------------
115 -- HTable address --> Allocation --
116 -----------------------------------
118 type Allocation is record
119 Root : Root_Id;
120 Size : Storage_Count;
121 end record;
123 type Address_Range is range 0 .. 4097;
124 function H (A : Integer_Address) return Address_Range;
125 No_Alloc : constant Allocation := (No_Root_Id, 0);
127 package Address_HTable is new GNAT.HTable.Simple_HTable (
128 Header_Num => Address_Range,
129 Element => Allocation,
130 No_Element => No_Alloc,
131 Key => Integer_Address,
132 Hash => H,
133 Equal => "=");
135 BT_Depth : Integer := 1;
137 -- The following need comments ???
139 Global_Alloc_Size : Storage_Count := 0;
140 Global_High_Water_Mark : Storage_Count := 0;
141 Global_Nb_Alloc : Integer := 0;
142 Global_Nb_Dealloc : Integer := 0;
143 Nb_Root : Integer := 0;
144 Nb_Wrong_Deall : Integer := 0;
145 Minimum_NB_Leaks : Integer := 1;
147 Tmp_Alloc : Allocation;
148 Quiet_Mode : Boolean := False;
150 -------------------------------
151 -- Allocation roots sorting --
152 -------------------------------
154 Sort_Order : String (1 .. 3) := "nwh";
155 -- This is the default order in which sorting criteria will be applied
156 -- n - Total number of unfreed allocations
157 -- w - Final watermark
158 -- h - High watermark
160 --------------------------------
161 -- GMEM functionality binding --
162 --------------------------------
164 function Gmem_Initialize (Dumpname : String) return Boolean is
165 function Initialize (Dumpname : System.Address) return Boolean;
166 pragma Import (C, Initialize, "__gnat_gmem_initialize");
168 S : aliased String := Dumpname & ASCII.NUL;
170 begin
171 return Initialize (S'Address);
172 end Gmem_Initialize;
174 procedure Gmem_A2l_Initialize (Exename : String) is
175 procedure A2l_Initialize (Exename : System.Address);
176 pragma Import (C, A2l_Initialize, "__gnat_gmem_a2l_initialize");
178 S : aliased String := Exename & ASCII.NUL;
180 begin
181 A2l_Initialize (S'Address);
182 end Gmem_A2l_Initialize;
184 function Read_Next return Storage_Elmt is
185 procedure Read_Next (buf : System.Address);
186 pragma Import (C, Read_Next, "__gnat_gmem_read_next");
188 S : Storage_Elmt;
190 begin
191 Read_Next (S'Address);
192 return S;
193 end Read_Next;
195 -------
196 -- H --
197 -------
199 function H (A : Integer_Address) return Address_Range is
200 begin
201 return Address_Range (A mod Integer_Address (Address_Range'Last));
202 end H;
204 ---------------
205 -- Mem_Image --
206 ---------------
208 function Mem_Image (X : Storage_Count) return String is
209 Ks : constant Storage_Count := X / 1024;
210 Megs : constant Storage_Count := Ks / 1024;
211 Buff : String (1 .. 7);
213 begin
214 if Megs /= 0 then
215 Ada.Float_Text_IO.Put (Buff, Float (X) / 1024.0 / 1024.0, 2, 0);
216 return Buff & " Megabytes";
218 elsif Ks /= 0 then
219 Ada.Float_Text_IO.Put (Buff, Float (X) / 1024.0, 2, 0);
220 return Buff & " Kilobytes";
222 else
223 Ada.Integer_Text_IO.Put (Buff (1 .. 4), Integer (X));
224 return Buff (1 .. 4) & " Bytes";
225 end if;
226 end Mem_Image;
228 -----------
229 -- Usage --
230 -----------
232 procedure Usage is
233 begin
234 New_Line;
235 Put ("GNATMEM ");
236 Put (Gnat_Version_String);
237 Put_Line (" Copyright 1997-2004 Free Software Foundation, Inc.");
238 New_Line;
240 Put_Line ("Usage: gnatmem switches [depth] exename");
241 New_Line;
242 Put_Line (" depth backtrace depth to take into account, default is"
243 & Integer'Image (BT_Depth));
244 Put_Line (" exename the name of the executable to be analyzed");
245 New_Line;
246 Put_Line ("Switches:");
247 Put_Line (" -b n same as depth parameter");
248 Put_Line (" -i file read the allocation log from specific file");
249 Put_Line (" default is gmem.out in the current directory");
250 Put_Line (" -m n masks roots with less than n leaks, default is 1");
251 Put_Line (" specify 0 to see even released allocation roots");
252 Put_Line (" -q quiet, minimum output");
253 Put_Line (" -s order sort allocation roots according to an order of");
254 Put_Line (" sort criteria");
255 GNAT.OS_Lib.OS_Exit (1);
256 end Usage;
258 -----------------------
259 -- Process_Arguments --
260 -----------------------
262 procedure Process_Arguments is
263 begin
264 -- Parse the options first
266 loop
267 case Getopt ("b: m: i: q s:") is
268 when ASCII.Nul => exit;
270 when 'b' =>
271 begin
272 BT_Depth := Natural'Value (Parameter);
273 exception
274 when Constraint_Error =>
275 Usage;
276 end;
278 when 'm' =>
279 begin
280 Minimum_NB_Leaks := Natural'Value (Parameter);
281 exception
282 when Constraint_Error =>
283 Usage;
284 end;
286 when 'i' =>
287 Log_Name := new String'(Parameter);
289 when 'q' =>
290 Quiet_Mode := True;
292 when 's' =>
293 declare
294 S : constant String (Sort_Order'Range) := Parameter;
296 begin
297 for J in Sort_Order'Range loop
298 if S (J) = 'n' or else
299 S (J) = 'w' or else
300 S (J) = 'h'
301 then
302 Sort_Order (J) := S (J);
303 else
304 Put_Line ("Invalid sort criteria string.");
305 GNAT.OS_Lib.OS_Exit (1);
306 end if;
307 end loop;
308 end;
310 when others =>
311 null;
312 end case;
313 end loop;
315 -- Set default log file if -i hasn't been specified
317 if Log_Name = null then
318 Log_Name := new String'("gmem.out");
319 end if;
321 -- Get the optional backtrace length and program name
323 declare
324 Str1 : constant String := GNAT.Command_Line.Get_Argument;
325 Str2 : constant String := GNAT.Command_Line.Get_Argument;
327 begin
328 if Str1 = "" then
329 Usage;
330 end if;
332 if Str2 = "" then
333 Program_Name := new String'(Str1);
334 else
335 BT_Depth := Natural'Value (Str1);
336 Program_Name := new String'(Str2);
337 end if;
339 exception
340 when Constraint_Error =>
341 Usage;
342 end;
344 -- Ensure presence of executable suffix in Program_Name
346 declare
347 Suffix : String_Access := Get_Executable_Suffix;
348 Tmp : String_Access;
350 begin
351 if Suffix.all /= ""
352 and then
353 Program_Name.all
354 (Program_Name.all'Last - Suffix.all'Length + 1 ..
355 Program_Name.all'Last) /= Suffix.all
356 then
357 Tmp := new String'(Program_Name.all & Suffix.all);
358 Free (Program_Name);
359 Program_Name := Tmp;
360 end if;
362 Free (Suffix);
364 -- Search the executable on the path. If not found in the PATH, we
365 -- default to the current directory. Otherwise, libaddr2line will
366 -- fail with an error:
368 -- (null): Bad address
370 Tmp := Locate_Exec_On_Path (Program_Name.all);
372 if Tmp = null then
373 Tmp := new String'('.' & Directory_Separator & Program_Name.all);
374 end if;
376 Free (Program_Name);
377 Program_Name := Tmp;
378 end;
380 if not Is_Regular_File (Log_Name.all) then
381 Put_Line ("Couldn't find " & Log_Name.all);
382 GNAT.OS_Lib.OS_Exit (1);
383 end if;
385 if not Gmem_Initialize (Log_Name.all) then
386 Put_Line ("File " & Log_Name.all & " is not a gnatmem log file");
387 GNAT.OS_Lib.OS_Exit (1);
388 end if;
390 if not Is_Regular_File (Program_Name.all) then
391 Put_Line ("Couldn't find " & Program_Name.all);
392 end if;
394 Gmem_A2l_Initialize (Program_Name.all);
396 exception
397 when GNAT.Command_Line.Invalid_Switch =>
398 Ada.Text_IO.Put_Line ("Invalid switch : "
399 & GNAT.Command_Line.Full_Switch);
400 Usage;
401 end Process_Arguments;
403 Cur_Elmt : Storage_Elmt;
405 -- Start of processing for Gnatmem
407 begin
408 Process_Arguments;
410 -- Main loop analysing the data generated by the instrumented routines.
411 -- For each allocation, the backtrace is kept and stored in a htable
412 -- whose entry is the address. For each deallocation, we look for the
413 -- corresponding allocation and cancel it.
415 Main : loop
416 Cur_Elmt := Read_Next;
418 case Cur_Elmt.Elmt is
419 when '*' =>
420 exit Main;
422 when 'A' =>
424 -- Update global counters if the allocated size is meaningful
426 if Quiet_Mode then
427 Tmp_Alloc.Root := Read_BT (BT_Depth);
429 if Nb_Alloc (Tmp_Alloc.Root) = 0 then
430 Nb_Root := Nb_Root + 1;
431 end if;
433 Set_Nb_Alloc (Tmp_Alloc.Root, Nb_Alloc (Tmp_Alloc.Root) + 1);
434 Address_HTable.Set (Cur_Elmt.Address, Tmp_Alloc);
436 elsif Cur_Elmt.Size > 0 then
438 Global_Alloc_Size := Global_Alloc_Size + Cur_Elmt.Size;
439 Global_Nb_Alloc := Global_Nb_Alloc + 1;
441 if Global_High_Water_Mark < Global_Alloc_Size then
442 Global_High_Water_Mark := Global_Alloc_Size;
443 end if;
445 -- Read the corresponding back trace
447 Tmp_Alloc.Root := Read_BT (BT_Depth);
449 -- Update the number of allocation root if this is a new one
451 if Nb_Alloc (Tmp_Alloc.Root) = 0 then
452 Nb_Root := Nb_Root + 1;
453 end if;
455 -- Update allocation root specific counters
457 Set_Alloc_Size (Tmp_Alloc.Root,
458 Alloc_Size (Tmp_Alloc.Root) + Cur_Elmt.Size);
460 Set_Nb_Alloc (Tmp_Alloc.Root, Nb_Alloc (Tmp_Alloc.Root) + 1);
462 if High_Water_Mark (Tmp_Alloc.Root) <
463 Alloc_Size (Tmp_Alloc.Root)
464 then
465 Set_High_Water_Mark (Tmp_Alloc.Root,
466 Alloc_Size (Tmp_Alloc.Root));
467 end if;
469 -- Associate this allocation root to the allocated address
471 Tmp_Alloc.Size := Cur_Elmt.Size;
472 Address_HTable.Set (Cur_Elmt.Address, Tmp_Alloc);
474 -- non meaningful output, just consumes the backtrace
476 else
477 Tmp_Alloc.Root := Read_BT (BT_Depth);
478 end if;
480 when 'D' =>
482 -- Get the corresponding Dealloc_Size and Root
484 Tmp_Alloc := Address_HTable.Get (Cur_Elmt.Address);
486 if Tmp_Alloc.Root = No_Root_Id then
488 -- There was no prior allocation at this address, something is
489 -- very wrong. Mark this allocation root as problematic
491 Tmp_Alloc.Root := Read_BT (BT_Depth);
493 if Nb_Alloc (Tmp_Alloc.Root) = 0 then
494 Set_Nb_Alloc (Tmp_Alloc.Root, Nb_Alloc (Tmp_Alloc.Root) - 1);
495 Nb_Wrong_Deall := Nb_Wrong_Deall + 1;
496 end if;
498 else
499 -- Update global counters
501 if not Quiet_Mode then
502 Global_Alloc_Size := Global_Alloc_Size - Tmp_Alloc.Size;
503 end if;
505 Global_Nb_Dealloc := Global_Nb_Dealloc + 1;
507 -- Update allocation root specific counters
509 if not Quiet_Mode then
510 Set_Alloc_Size (Tmp_Alloc.Root,
511 Alloc_Size (Tmp_Alloc.Root) - Tmp_Alloc.Size);
512 end if;
514 Set_Nb_Alloc (Tmp_Alloc.Root, Nb_Alloc (Tmp_Alloc.Root) - 1);
516 -- update the number of allocation root if this one disappear
518 if Nb_Alloc (Tmp_Alloc.Root) = 0
519 and then Minimum_NB_Leaks > 0 then
520 Nb_Root := Nb_Root - 1;
521 end if;
523 -- De-associate the deallocated address
525 Address_HTable.Remove (Cur_Elmt.Address);
526 end if;
528 when others =>
529 raise Program_Error;
530 end case;
531 end loop Main;
533 -- Print out general information about overall allocation
535 if not Quiet_Mode then
536 Put_Line ("Global information");
537 Put_Line ("------------------");
539 Put (" Total number of allocations :");
540 Ada.Integer_Text_IO.Put (Global_Nb_Alloc, 4);
541 New_Line;
543 Put (" Total number of deallocations :");
544 Ada.Integer_Text_IO.Put (Global_Nb_Dealloc, 4);
545 New_Line;
547 Put_Line (" Final Water Mark (non freed mem) :"
548 & Mem_Image (Global_Alloc_Size));
549 Put_Line (" High Water Mark :"
550 & Mem_Image (Global_High_Water_Mark));
551 New_Line;
552 end if;
554 -- Print out the back traces corresponding to potential leaks in order
555 -- greatest number of non-deallocated allocations
557 Print_Back_Traces : declare
558 type Root_Array is array (Natural range <>) of Root_Id;
559 Leaks : Root_Array (0 .. Nb_Root);
560 Leak_Index : Natural := 0;
562 Bogus_Dealls : Root_Array (1 .. Nb_Wrong_Deall);
563 Deall_Index : Natural := 0;
564 Nb_Alloc_J : Natural := 0;
566 procedure Move (From : Natural; To : Natural);
567 function Lt (Op1, Op2 : Natural) return Boolean;
568 package Root_Sort is new GNAT.Heap_Sort_G (Move, Lt);
570 procedure Move (From : Natural; To : Natural) is
571 begin
572 Leaks (To) := Leaks (From);
573 end Move;
575 function Lt (Op1, Op2 : Natural) return Boolean is
576 function Apply_Sort_Criterion (S : Character) return Integer;
577 -- Applies a specific sort criterion; returns -1, 0 or 1 if Op1 is
578 -- smaller than, equal, or greater than Op2 according to criterion
580 function Apply_Sort_Criterion (S : Character) return Integer is
581 LOp1, LOp2 : Integer;
582 begin
583 case S is
584 when 'n' =>
585 LOp1 := Nb_Alloc (Leaks (Op1));
586 LOp2 := Nb_Alloc (Leaks (Op2));
588 when 'w' =>
589 LOp1 := Integer (Alloc_Size (Leaks (Op1)));
590 LOp2 := Integer (Alloc_Size (Leaks (Op2)));
592 when 'h' =>
593 LOp1 := Integer (High_Water_Mark (Leaks (Op1)));
594 LOp2 := Integer (High_Water_Mark (Leaks (Op2)));
596 when others =>
597 return 0; -- Can't actually happen
598 end case;
600 if LOp1 < LOp2 then
601 return -1;
602 elsif LOp1 > LOp2 then
603 return 1;
604 else
605 return 0;
606 end if;
607 exception
608 when Constraint_Error =>
609 return 0;
610 end Apply_Sort_Criterion;
612 Result : Integer;
614 -- Start of processing for Lt
616 begin
617 for S in Sort_Order'Range loop
618 Result := Apply_Sort_Criterion (Sort_Order (S));
619 if Result = -1 then
620 return False;
621 elsif Result = 1 then
622 return True;
623 end if;
624 end loop;
625 return False;
626 end Lt;
628 -- Start of processing for Print_Back_Traces
630 begin
631 -- Transfer all the relevant Roots in the Leaks and a
632 -- Bogus_Deall arrays
634 Tmp_Alloc.Root := Get_First;
635 while Tmp_Alloc.Root /= No_Root_Id loop
636 if Nb_Alloc (Tmp_Alloc.Root) = 0 and then Minimum_NB_Leaks > 0 then
637 null;
639 elsif Nb_Alloc (Tmp_Alloc.Root) < 0 then
640 Deall_Index := Deall_Index + 1;
641 Bogus_Dealls (Deall_Index) := Tmp_Alloc.Root;
643 else
644 Leak_Index := Leak_Index + 1;
645 Leaks (Leak_Index) := Tmp_Alloc.Root;
646 end if;
648 Tmp_Alloc.Root := Get_Next;
649 end loop;
651 -- Print out wrong deallocations
653 if Nb_Wrong_Deall > 0 then
654 Put_Line ("Releasing deallocated memory at :");
655 if not Quiet_Mode then
656 Put_Line ("--------------------------------");
657 end if;
659 for J in 1 .. Bogus_Dealls'Last loop
660 Print_BT (Bogus_Dealls (J), Short => Quiet_Mode);
661 New_Line;
662 end loop;
663 end if;
665 -- Print out all allocation Leaks
667 if Nb_Root > 0 then
669 -- Sort the Leaks so that potentially important leaks appear first
671 Root_Sort.Sort (Nb_Root);
673 for J in 1 .. Leaks'Last loop
674 Nb_Alloc_J := Nb_Alloc (Leaks (J));
675 if Nb_Alloc_J >= Minimum_NB_Leaks then
676 if Quiet_Mode then
677 if Nb_Alloc_J = 1 then
678 Put_Line (" 1 leak at :");
679 else
680 Put_Line (Integer'Image (Nb_Alloc_J) & " leaks at :");
681 end if;
683 else
684 Put_Line ("Allocation Root #" & Integer'Image (J));
685 Put_Line ("-------------------");
687 Put (" Number of non freed allocations :");
688 Ada.Integer_Text_IO.Put (Nb_Alloc_J, 4);
689 New_Line;
691 Put_Line
692 (" Final Water Mark (non freed mem) :"
693 & Mem_Image (Alloc_Size (Leaks (J))));
695 Put_Line
696 (" High Water Mark :"
697 & Mem_Image (High_Water_Mark (Leaks (J))));
699 Put_Line (" Backtrace :");
700 end if;
702 Print_BT (Leaks (J), Short => Quiet_Mode);
703 New_Line;
704 end if;
705 end loop;
706 end if;
707 end Print_Back_Traces;
708 end Gnatmem;