OSDN Git Service

* gcc-interface/decl.c (make_type_from_size) <INTEGER_TYPE>: Just copy
[pf3gnuchains/gcc-fork.git] / gcc / ada / memtrack.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT RUN-TIME COMPONENTS                         --
4 --                                                                          --
5 --                         S Y S T E M . M E M O R Y                        --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --          Copyright (C) 2001-2009, Free Software Foundation, Inc.         --
10 --                                                                          --
11 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
12 -- terms of the  GNU General Public License as published  by the Free Soft- --
13 -- ware  Foundation;  either version 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.                                     --
17 --                                                                          --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception,   --
20 -- version 3.1, as published by the Free Software Foundation.               --
21 --                                                                          --
22 -- You should have received a copy of the GNU General Public License and    --
23 -- a copy of the GCC Runtime Library Exception along with this program;     --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
25 -- <http://www.gnu.org/licenses/>.                                          --
26 --                                                                          --
27 -- GNAT was originally developed  by the GNAT team at  New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
29 --                                                                          --
30 ------------------------------------------------------------------------------
31
32 --  This version contains allocation tracking capability
33
34 --  The object file corresponding to this instrumented version is to be found
35 --  in libgmem.
36
37 --  When enabled, the subsystem logs all the calls to __gnat_malloc and
38 --  __gnat_free. This log can then be processed by gnatmem to detect
39 --  dynamic memory leaks.
40
41 --  To use this functionality, you must compile your application with -g
42 --  and then link with this object file:
43
44 --     gnatmake -g program -largs -lgmem
45
46 --  After compilation, you may use your program as usual except that upon
47 --  completion, it will generate in the current directory the file gmem.out.
48
49 --  You can then investigate for possible memory leaks and mismatch by calling
50 --  gnatmem with this file as an input:
51
52 --    gnatmem -i gmem.out program
53
54 --  See gnatmem section in the GNAT User's Guide for more details
55
56 --  NOTE: This capability is currently supported on the following targets:
57
58 --    Windows
59 --    AIX
60 --    GNU/Linux
61 --    HP-UX
62 --    Irix
63 --    Solaris
64 --    Tru64
65 --    Alpha OpenVMS
66
67 --  NOTE FOR FUTURE PLATFORMS SUPPORT: It is assumed that type Duration is
68 --  64 bit. If the need arises to support architectures where this assumption
69 --  is incorrect, it will require changing the way timestamps of allocation
70 --  events are recorded.
71
72 pragma Source_File_Name (System.Memory, Body_File_Name => "memtrack.adb");
73
74 with Ada.Exceptions;
75 with System.Soft_Links;
76 with System.Traceback;
77 with System.Traceback_Entries;
78 with GNAT.IO;
79 with System.OS_Primitives;
80
81 package body System.Memory is
82
83    use Ada.Exceptions;
84    use System.Soft_Links;
85    use System.Traceback;
86    use System.Traceback_Entries;
87    use GNAT.IO;
88
89    function c_malloc (Size : size_t) return System.Address;
90    pragma Import (C, c_malloc, "malloc");
91
92    procedure c_free (Ptr : System.Address);
93    pragma Import (C, c_free, "free");
94
95    function c_realloc
96      (Ptr : System.Address; Size : size_t) return System.Address;
97    pragma Import (C, c_realloc, "realloc");
98
99    subtype File_Ptr is System.Address;
100
101    function fopen (Path : String; Mode : String) return File_Ptr;
102    pragma Import (C, fopen);
103
104    procedure OS_Exit (Status : Integer);
105    pragma Import (C, OS_Exit, "__gnat_os_exit");
106    pragma No_Return (OS_Exit);
107
108    procedure fwrite
109      (Ptr    : System.Address;
110       Size   : size_t;
111       Nmemb  : size_t;
112       Stream : File_Ptr);
113
114    procedure fwrite
115      (Str    : String;
116       Size   : size_t;
117       Nmemb  : size_t;
118       Stream : File_Ptr);
119    pragma Import (C, fwrite);
120
121    procedure fputc (C : Integer; Stream : File_Ptr);
122    pragma Import (C, fputc);
123
124    procedure fclose (Stream : File_Ptr);
125    pragma Import (C, fclose);
126
127    procedure Finalize;
128    pragma Export (C, Finalize, "__gnat_finalize");
129    --  Replace the default __gnat_finalize to properly close the log file
130
131    Address_Size : constant := System.Address'Max_Size_In_Storage_Elements;
132    --  Size in bytes of a pointer
133
134    Max_Call_Stack : constant := 200;
135    --  Maximum number of frames supported
136
137    Tracebk   : aliased array (0 .. Max_Call_Stack) of Traceback_Entry;
138    Num_Calls : aliased Integer := 0;
139
140    Gmemfname : constant String := "gmem.out" & ASCII.NUL;
141    --  Allocation log of a program is saved in a file gmem.out
142    --  ??? What about Ada.Command_Line.Command_Name & ".out" instead of static
143    --  gmem.out
144
145    Gmemfile : File_Ptr;
146    --  Global C file pointer to the allocation log
147
148    Needs_Init : Boolean := True;
149    --  Reset after first call to Gmem_Initialize
150
151    procedure Gmem_Initialize;
152    --  Initialization routine; opens the file and writes a header string. This
153    --  header string is used as a magic-tag to know if the .out file is to be
154    --  handled by GDB or by the GMEM (instrumented malloc/free) implementation.
155
156    First_Call : Boolean := True;
157    --  Depending on implementation, some of the traceback routines may
158    --  themselves do dynamic allocation. We use First_Call flag to avoid
159    --  infinite recursion
160
161    -----------
162    -- Alloc --
163    -----------
164
165    function Alloc (Size : size_t) return System.Address is
166       Result      : aliased System.Address;
167       Actual_Size : aliased size_t := Size;
168       Timestamp   : aliased Duration;
169
170    begin
171       if Size = size_t'Last then
172          Raise_Exception (Storage_Error'Identity, "object too large");
173       end if;
174
175       --  Change size from zero to non-zero. We still want a proper pointer
176       --  for the zero case because pointers to zero length objects have to
177       --  be distinct, but we can't just go ahead and allocate zero bytes,
178       --  since some malloc's return zero for a zero argument.
179
180       if Size = 0 then
181          Actual_Size := 1;
182       end if;
183
184       Lock_Task.all;
185
186       Result := c_malloc (Actual_Size);
187
188       if First_Call then
189
190          --  Logs allocation call
191          --  format is:
192          --   'A' <mem addr> <size chunk> <len backtrace> <addr1> ... <addrn>
193
194          First_Call := False;
195
196          if Needs_Init then
197             Gmem_Initialize;
198          end if;
199
200          Timestamp := System.OS_Primitives.Clock;
201          Call_Chain (Tracebk'Address, Max_Call_Stack, Num_Calls,
202                      Skip_Frames => 2);
203          fputc (Character'Pos ('A'), Gmemfile);
204          fwrite (Result'Address, Address_Size, 1, Gmemfile);
205          fwrite (Actual_Size'Address, size_t'Max_Size_In_Storage_Elements, 1,
206                  Gmemfile);
207          fwrite (Timestamp'Address, Duration'Max_Size_In_Storage_Elements, 1,
208                  Gmemfile);
209          fwrite (Num_Calls'Address, Integer'Max_Size_In_Storage_Elements, 1,
210                  Gmemfile);
211
212          for J in Tracebk'First .. Tracebk'First + Num_Calls - 1 loop
213             declare
214                Ptr : System.Address := PC_For (Tracebk (J));
215             begin
216                fwrite (Ptr'Address, Address_Size, 1, Gmemfile);
217             end;
218          end loop;
219
220          First_Call := True;
221
222       end if;
223
224       Unlock_Task.all;
225
226       if Result = System.Null_Address then
227          Raise_Exception (Storage_Error'Identity, "heap exhausted");
228       end if;
229
230       return Result;
231    end Alloc;
232
233    --------------
234    -- Finalize --
235    --------------
236
237    procedure Finalize is
238    begin
239       if not Needs_Init then
240          fclose (Gmemfile);
241       end if;
242    end Finalize;
243
244    ----------
245    -- Free --
246    ----------
247
248    procedure Free (Ptr : System.Address) is
249       Addr      : aliased constant System.Address := Ptr;
250       Timestamp : aliased Duration;
251
252    begin
253       Lock_Task.all;
254
255       if First_Call then
256
257          --  Logs deallocation call
258          --  format is:
259          --   'D' <mem addr> <len backtrace> <addr1> ... <addrn>
260
261          First_Call := False;
262
263          if Needs_Init then
264             Gmem_Initialize;
265          end if;
266
267          Call_Chain (Tracebk'Address, Max_Call_Stack, Num_Calls,
268                      Skip_Frames => 2);
269          Timestamp := System.OS_Primitives.Clock;
270          fputc (Character'Pos ('D'), Gmemfile);
271          fwrite (Addr'Address, Address_Size, 1, Gmemfile);
272          fwrite (Timestamp'Address, Duration'Max_Size_In_Storage_Elements, 1,
273                  Gmemfile);
274          fwrite (Num_Calls'Address, Integer'Max_Size_In_Storage_Elements, 1,
275                  Gmemfile);
276
277          for J in Tracebk'First .. Tracebk'First + Num_Calls - 1 loop
278             declare
279                Ptr : System.Address := PC_For (Tracebk (J));
280             begin
281                fwrite (Ptr'Address, Address_Size, 1, Gmemfile);
282             end;
283          end loop;
284
285          c_free (Ptr);
286
287          First_Call := True;
288       end if;
289
290       Unlock_Task.all;
291    end Free;
292
293    ---------------------
294    -- Gmem_Initialize --
295    ---------------------
296
297    procedure Gmem_Initialize is
298       Timestamp : aliased Duration;
299
300    begin
301       if Needs_Init then
302          Needs_Init := False;
303          System.OS_Primitives.Initialize;
304          Timestamp := System.OS_Primitives.Clock;
305          Gmemfile := fopen (Gmemfname, "wb" & ASCII.NUL);
306
307          if Gmemfile = System.Null_Address then
308             Put_Line ("Couldn't open gnatmem log file for writing");
309             OS_Exit (255);
310          end if;
311
312          fwrite ("GMEM DUMP" & ASCII.LF, 10, 1, Gmemfile);
313          fwrite (Timestamp'Address, Duration'Max_Size_In_Storage_Elements, 1,
314                  Gmemfile);
315       end if;
316    end Gmem_Initialize;
317
318    -------------
319    -- Realloc --
320    -------------
321
322    function Realloc
323      (Ptr  : System.Address;
324       Size : size_t) return System.Address
325    is
326       Addr      : aliased constant System.Address := Ptr;
327       Result    : aliased System.Address;
328       Timestamp : aliased Duration;
329
330    begin
331       --  For the purposes of allocations logging, we treat realloc as a free
332       --  followed by malloc. This is not exactly accurate, but is a good way
333       --  to fit it into malloc/free-centered reports.
334
335       if Size = size_t'Last then
336          Raise_Exception (Storage_Error'Identity, "object too large");
337       end if;
338
339       Abort_Defer.all;
340       Lock_Task.all;
341
342       if First_Call then
343          First_Call := False;
344
345          --  We first log deallocation call
346
347          if Needs_Init then
348             Gmem_Initialize;
349          end if;
350          Call_Chain (Tracebk'Address, Max_Call_Stack, Num_Calls,
351                      Skip_Frames => 2);
352          Timestamp := System.OS_Primitives.Clock;
353          fputc (Character'Pos ('D'), Gmemfile);
354          fwrite (Addr'Address, Address_Size, 1, Gmemfile);
355          fwrite (Timestamp'Address, Duration'Max_Size_In_Storage_Elements, 1,
356                  Gmemfile);
357          fwrite (Num_Calls'Address, Integer'Max_Size_In_Storage_Elements, 1,
358                  Gmemfile);
359
360          for J in Tracebk'First .. Tracebk'First + Num_Calls - 1 loop
361             declare
362                Ptr : System.Address := PC_For (Tracebk (J));
363             begin
364                fwrite (Ptr'Address, Address_Size, 1, Gmemfile);
365             end;
366          end loop;
367
368          --  Now perform actual realloc
369
370          Result := c_realloc (Ptr, Size);
371
372          --   Log allocation call using the same backtrace
373
374          fputc (Character'Pos ('A'), Gmemfile);
375          fwrite (Result'Address, Address_Size, 1, Gmemfile);
376          fwrite (Size'Address, size_t'Max_Size_In_Storage_Elements, 1,
377                  Gmemfile);
378          fwrite (Timestamp'Address, Duration'Max_Size_In_Storage_Elements, 1,
379                  Gmemfile);
380          fwrite (Num_Calls'Address, Integer'Max_Size_In_Storage_Elements, 1,
381                  Gmemfile);
382
383          for J in Tracebk'First .. Tracebk'First + Num_Calls - 1 loop
384             declare
385                Ptr : System.Address := PC_For (Tracebk (J));
386             begin
387                fwrite (Ptr'Address, Address_Size, 1, Gmemfile);
388             end;
389          end loop;
390
391          First_Call := True;
392       end if;
393
394       Unlock_Task.all;
395       Abort_Undefer.all;
396
397       if Result = System.Null_Address then
398          Raise_Exception (Storage_Error'Identity, "heap exhausted");
399       end if;
400
401       return Result;
402    end Realloc;
403
404 end System.Memory;