[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

15. Finding memory problems with gnatmem

gnatmem, is a tool that monitors dynamic allocation and deallocation activity in a program, and displays information about incorrect deallocations and possible sources of memory leaks. Gnatmem provides three type of information:

15.1 Running gnatmem  
15.2 Switches for gnatmem  
15.3 Example of gnatmem Usage  
15.4 Implementation note  


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

15.1 Running gnatmem

The gnatmem command has the form

 
   $ gnatmem [n] [-o file] user_program [program_arg]*
or
   $ gnatmem [n] -i file

Gnatmem must be supplied with the executable to examine, followed by its run-time inputs. For example, if a program is executed with the command:
 
   $ my_program arg1 arg2
then it can be run under gnatmem control using the command:
 
   $ gnatmem my_program arg1 arg2

The program is transparently executed under the control of the debugger 20.1 The GNAT Debugger GDB. This does not affect the behavior of the program, except for sensitive real-time programs. When the program has completed execution, gnatmem outputs a report containing general allocation/deallocation information and potential memory leak. For better results, the user program should be compiled with debugging options 3.2 Switches for gnatgcc.

Here is a simple example of use:

*************** debut cc
 
   $ gnatmem test_gm

   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                          :
      test_gm.adb:23 test_gm.alloc
   .
   .
   .

The first block of output give 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] [ ? ]

15.2 Switches for gnatmem

gnatmem recognizes the following switches:

-q
Quiet. Gives the minimum output needed to identify the origin of the memory leaks. Omit statistical information.

n
N is an integer literal (usually between 1 and 10) which controls the depth of the backtraces defining allocation root. The default value for N is 1. The deeper the backtrace, the more precise the localization of the root. Note that the total number of roots can depend on this parameter.

-o file
Direct the gnatgdb output to the specified file. The gnatgdb script used to generate this output is also saved in the file `gnatmem.tmp'.

-i file
Do the gnatmem processing starting from file which has been generated by a previous call to gnatmem with the -o switch. This is useful for post mortem processing.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

15.3 Example of gnatmem Usage

The first example shows the use of gnatmem on a simple 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:

 
   $ gnatmake -g test_gm

gnatmem is invoked simply with
 
   $ gnatmem test_gm

which produces 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    :  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 #1. This is a normal behavior when the number of non freed allocations is one, it locates 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.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

15.4 Implementation note

gnatmem executes the user program under the control of gnatgdb using a script that sets breakpoints and gathers information on each dynamic allocation and deallocation. The output of the script is then analyzed by gnatmem in order to locate memory leaks and their origin in the program. Gnatmem works by recording each address returned by the allocation procedure (__gnat_malloc) along with the backtrace at the allocation point. On each deallocation, the deallocated address is matched with the corresponding allocation. At the end of the processing, the unmatched allocations are considered potential leaks. All the allocations associated with the same backtrace are grouped together and form an allocation root. The allocation roots are then sorted so that those with the biggest number of unmatched allocation are printed first. A delicate aspect of this technique is to distinguish between the data produced by the user program and the data produced by the gnatgdb script. Currently, on systems that allow probing the terminal, the gnatgdb command "tty" is used to force the program output to be redirected to the current terminal while the gnatgdb output is directed to a file or to a pipe in order to be processed subsequently by gnatmem.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Tom Bennet on August, 25 2000 using texi2html