[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This chapter describes some useful memory pools provided in the GNAT library
and in particular the GNAT Debug Pool facility, which can be used to detect
incorrect uses of access values (including "dangling references").
It also describes the gnatmem
tool, which can be used to track down
"memory leaks".
21.1 Some Useful Memory Pools 21.2 The GNAT Debug Pool Facility 21.3 The gnatmem
Tool
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The System.Pool_Global
package offers the Unbounded_No_Reclaim_Pool
storage pool. Allocations use the standard system call malloc
while
deallocations use the standard system call free
. No reclamation is
performed when the pool goes out of scope. For performance reasons, the
standard default Ada allocators/deallocators do not use any explicit storage
pools but if they did, they could use this storage pool without any change in
behavior. That is why this storage pool is used when the user
manages to make the default implicit allocator explicit as in this example:
type T1 is access Something; -- no Storage pool is defined for T2 type T2 is access Something_Else; for T2'Storage_Pool use T1'Storage_Pool; -- the above is equivalent to for T2'Storage_Pool use System.Pool_Global.Global_Pool_Object; |
The System.Pool_Local
package offers the Unbounded_Reclaim_Pool storage
pool. The allocation strategy is similar to Pool_Local
's
except that the all
storage allocated with this pool is reclaimed when the pool object goes out of
scope. This pool provides a explicit mechanism similar to the implicit one
provided by several Ada 83 compilers for allocations performed through a local
access type and whose purpose was to reclaim memory when exiting the
scope of a given local access. As an example, the following program does not
leak memory even though it does not perform explicit deallocation:
with System.Pool_Local; procedure Pooloc1 is procedure Internal is type A is access Integer; X : System.Pool_Local.Unbounded_Reclaim_Pool; for A'Storage_Pool use X; v : A; begin for I in 1 .. 50 loop v := new Integer; end loop; end Internal; begin for I in 1 .. 100 loop Internal; end loop; end Pooloc1; |
The System.Pool_Size
package implements the Stack_Bounded_Pool used when
Storage_Size
is specified for an access type.
The whole storage for the pool is
allocated at once, usually on the stack at the point where the access type is
elaborated. It is automatically reclaimed when exiting the scope where the
access type is defined. This package is not intended to be used directly by the
user and it is implicitly used for each such declaration:
type T1 is access Something; for T1'Storage_Size use 10_000; |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The use of unchecked deallocation and unchecked conversion can easily
lead to incorrect memory references. The problems generated by such
references are usually difficult to tackle because the symptoms can be
very remote from the origin of the problem. In such cases, it is
very helpful to detect the problem as early as possible. This is the
purpose of the Storage Pool provided by GNAT.Debug_Pools
.
In order to use the GNAT specific debugging pool, the user must associate a debug pool object with each of the access types that may be related to suspected memory problems. See Ada Reference Manual 13.11.
type Ptr is access Some_Type; Pool : GNAT.Debug_Pools.Debug_Pool; for Ptr'Storage_Pool use Pool; |
GNAT.Debug_Pools
is derived from a GNAT-specific kind of
pool: the Checked_Pool
. Such pools, like standard Ada storage pools,
allow the user to redefine allocation and deallocation strategies. They
also provide a checkpoint for each dereference, through the use of
the primitive operation Dereference
which is implicitly called at
each dereference of an access value.
Once an access type has been associated with a debug pool, operations on values of the type may raise four distinct exceptions, which correspond to four potential kinds of memory corruption:
GNAT.Debug_Pools.Accessing_Not_Allocated_Storage
GNAT.Debug_Pools.Accessing_Deallocated_Storage
GNAT.Debug_Pools.Freeing_Not_Allocated_Storage
GNAT.Debug_Pools.Freeing_Deallocated_Storage
For types associated with a Debug_Pool, dynamic allocation is performed using
the standard GNAT allocation routine. References to all allocated chunks of
memory are kept in an internal dictionary. Several deallocation strategies are
provided, whereupon the user can choose to release the memory to the system,
keep it allocated for further invalid access checks, or fill it with an easily
recognizable pattern for debug sessions. The memory pattern is the old IBM
hexadecimal convention: 16#DEADBEEF#
.
See the documentation in the file g-debpoo.ads for more information on the various strategies.
Upon each dereference, a check is made that the access value denotes a
properly allocated memory location. Here is a complete example of use of
Debug_Pools
, that includes typical instances of memory corruption:
with Gnat.Io; use Gnat.Io; with Unchecked_Deallocation; with Unchecked_Conversion; with GNAT.Debug_Pools; with System.Storage_Elements; with Ada.Exceptions; use Ada.Exceptions; procedure Debug_Pool_Test is type T is access Integer; type U is access all T; P : GNAT.Debug_Pools.Debug_Pool; for T'Storage_Pool use P; procedure Free is new Unchecked_Deallocation (Integer, T); function UC is new Unchecked_Conversion (U, T); A, B : aliased T; procedure Info is new GNAT.Debug_Pools.Print_Info(Put_Line); begin Info (P); A := new Integer; B := new Integer; B := A; Info (P); Free (A); begin Put_Line (Integer'Image(B.all)); exception when E : others => Put_Line ("raised: " & Exception_Name (E)); end; begin Free (B); exception when E : others => Put_Line ("raised: " & Exception_Name (E)); end; B := UC(A'Access); begin Put_Line (Integer'Image(B.all)); exception when E : others => Put_Line ("raised: " & Exception_Name (E)); end; begin Free (B); exception when E : others => Put_Line ("raised: " & Exception_Name (E)); end; Info (P); end Debug_Pool_Test; |
The debug pool mechanism provides the following precise diagnostics on the execution of this erroneous program:
Debug Pool info: Total allocated bytes : 0 Total deallocated bytes : 0 Current Water Mark: 0 High Water Mark: 0 Debug Pool info: Total allocated bytes : 8 Total deallocated bytes : 0 Current Water Mark: 8 High Water Mark: 8 raised: GNAT.DEBUG_POOLS.ACCESSING_DEALLOCATED_STORAGE raised: GNAT.DEBUG_POOLS.FREEING_DEALLOCATED_STORAGE raised: GNAT.DEBUG_POOLS.ACCESSING_NOT_ALLOCATED_STORAGE raised: GNAT.DEBUG_POOLS.FREEING_NOT_ALLOCATED_STORAGE Debug Pool info: Total allocated bytes : 8 Total deallocated bytes : 4 Current Water Mark: 4 High Water Mark: 8 |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
gnatmem
Tool
The gnatmem
utility monitors dynamic allocation and
deallocation activity in a program, and displays information about
incorrect deallocations and possible sources of memory leaks.
It provides three type of information:
21.3.1 Running gnatmem
21.3.2 Switches for gnatmem
21.3.3 Example of gnatmem
Usage
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
gnatmem
gnatmem
makes use of the output created by the special version of
allocation and deallocation routines that record call information. This
allows to obtain accurate dynamic memory usage history at a minimal cost to
the execution speed. Note however, that gnatmem
is not supported on
all platforms (currently, it is supported on AIX, HP-UX, GNU/Linux x86,
32-bit Solaris (sparc and x86) and Windows NT/2000/XP (x86).
The gnatmem
command has the form
$ gnatmem [switches] user_program |
The program must have been linked with the instrumented version of the
allocation and deallocation routines. This is done by linking with the
`libgmem.a' library. For correct symbolic backtrace information,
the user program should be compiled with debugging options
3.2 Switches for gcc
. For example to build `my_program':
$ gnatmake -g my_program -largs -lgmem |
When running `my_program' the file `gmem.out' is produced. This file
contains information about all allocations and deallocations done by the
program. It is produced by the instrumented allocations and
deallocations routines and will be used by gnatmem
.
Gnatmem must be supplied with the `gmem.out' file and the executable to
examine. If the location of `gmem.out' file was not explicitly supplied by
-i
switch, gnatmem will assume that this file can be found in the
current directory. For example, after you have executed `my_program',
`gmem.out' can be analyzed by gnatmem
using the command:
$ gnatmem my_program |
This will produce the output with the following format:
*************** debut cc
$ gnatmem my_program Global information ------------------ Total number of allocations : 45 Total number of deallocations : 6 Final Water Mark (non freed mem) : 11.29 Kilobytes High Water Mark : 11.40 Kilobytes . . . Allocation Root # 2 ------------------- Number of non freed allocations : 11 Final Water Mark (non freed mem) : 1.16 Kilobytes High Water Mark : 1.27 Kilobytes Backtrace : my_program.adb:23 my_program.alloc . . . |
The first block of output gives general information. In this case, the
Ada construct "new
" was executed 45 times, and only 6 calls to an
Unchecked_Deallocation routine occurred.
Subsequent paragraphs display information on all allocation roots.
An allocation root is a specific point in the execution of the program
that generates some dynamic allocation, such as a "new
"
construct. This root is represented by an execution backtrace (or subprogram
call stack). By default the backtrace depth for allocations roots is 1, so
that a root corresponds exactly to a source location. The backtrace can
be made deeper, to make the root more specific.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
gnatmem
gnatmem
recognizes the following switches:
gnatmem
processing starting from `file', rather than
`gmem.out' in the current directory.
gnatmem
to mask the allocation roots that have less
than n leaks. The default value is 1. Specifying the value of 0 will allow to
examine even the roots that didn't result in leaks.
gnatmem
to sort the allocation roots according to the
specified order of sort criteria, each identified by a single letter. The
currently supported criteria are n, h, w
standing respectively for
number of unfreed allocations, high watermark, and final watermark
corresponding to a specific root. The default order is nwh
.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
gnatmem
Usage
The following example shows the use of gnatmem
on a simple memory-leaking program.
Suppose that we have the following Ada program:
with Unchecked_Deallocation; procedure Test_Gm is type T is array (1..1000) of Integer; type Ptr is access T; procedure Free is new Unchecked_Deallocation (T, Ptr); A : Ptr; procedure My_Alloc is begin A := new T; end My_Alloc; procedure My_DeAlloc is B : Ptr := A; begin Free (B); end My_DeAlloc; begin My_Alloc; for I in 1 .. 5 loop for J in I .. 5 loop My_Alloc; end loop; My_Dealloc; end loop; end; |
The program needs to be compiled with debugging option and linked with
gmem
library:
$ gnatmake -g test_gm -largs -lgmem |
Then we execute the program as usual:
$ test_gm |
Then gnatmem
is invoked simply with
$ gnatmem test_gm |
which produces the following output (result may vary on different platforms):
Global information ------------------ Total number of allocations : 18 Total number of deallocations : 5 Final Water Mark (non freed mem) : 53.00 Kilobytes High Water Mark : 56.90 Kilobytes Allocation Root # 1 ------------------- Number of non freed allocations : 11 Final Water Mark (non freed mem) : 42.97 Kilobytes High Water Mark : 46.88 Kilobytes Backtrace : test_gm.adb:11 test_gm.my_alloc Allocation Root # 2 ------------------- Number of non freed allocations : 1 Final Water Mark (non freed mem) : 10.02 Kilobytes High Water Mark : 10.02 Kilobytes Backtrace : s-secsta.adb:81 system.secondary_stack.ss_init Allocation Root # 3 ------------------- Number of non freed allocations : 1 Final Water Mark (non freed mem) : 12 Bytes High Water Mark : 12 Bytes Backtrace : s-secsta.adb:181 system.secondary_stack.ss_init |
Note that the GNAT run time contains itself a certain number of allocations that have no corresponding deallocation, as shown here for root #2 and root #3. This is a normal behavior when the number of non freed allocations is one, it allocates dynamic data structures that the run time needs for the complete lifetime of the program. Note also that there is only one allocation root in the user program with a single line back trace: test_gm.adb:11 test_gm.my_alloc, whereas a careful analysis of the program shows that 'My_Alloc' is called at 2 different points in the source (line 21 and line 24). If those two allocation roots need to be distinguished, the backtrace depth parameter can be used:
$ gnatmem 3 test_gm |
which will give the following output:
Global information ------------------ Total number of allocations : 18 Total number of deallocations : 5 Final Water Mark (non freed mem) : 53.00 Kilobytes High Water Mark : 56.90 Kilobytes Allocation Root # 1 ------------------- Number of non freed allocations : 10 Final Water Mark (non freed mem) : 39.06 Kilobytes High Water Mark : 42.97 Kilobytes Backtrace : test_gm.adb:11 test_gm.my_alloc test_gm.adb:24 test_gm b_test_gm.c:52 main Allocation Root # 2 ------------------- Number of non freed allocations : 1 Final Water Mark (non freed mem) : 10.02 Kilobytes High Water Mark : 10.02 Kilobytes Backtrace : s-secsta.adb:81 system.secondary_stack.ss_init s-secsta.adb:283 <system__secondary_stack___elabb> b_test_gm.c:33 adainit Allocation Root # 3 ------------------- Number of non freed allocations : 1 Final Water Mark (non freed mem) : 3.91 Kilobytes High Water Mark : 3.91 Kilobytes Backtrace : test_gm.adb:11 test_gm.my_alloc test_gm.adb:21 test_gm b_test_gm.c:52 main Allocation Root # 4 ------------------- Number of non freed allocations : 1 Final Water Mark (non freed mem) : 12 Bytes High Water Mark : 12 Bytes Backtrace : s-secsta.adb:181 system.secondary_stack.ss_init s-secsta.adb:283 <system__secondary_stack___elabb> b_test_gm.c:33 adainit |
The allocation root #1 of the first example has been split in 2 roots #1 and #3 thanks to the more precise associated backtrace.
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |