1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
|
***********************************
**Memory**: Monitoring memory usage
***********************************
.. highlight:: ada
The GNAT compiler allocates and deallocates all memory either through
type-specific debug pools that you have defined yourself, or defaults to
the standard malloc and free system calls. However, it calls those through
an Ada proxy, in the package `System.Memory` that you can also
replace in your own application if need be.
Like this::
procedure Ada
`gnatcoll` provides such a possible replacement. Its implementation
is also based on `malloc` and `free`, but if you so chose you
can activate extra monitoring capabilities to help you find out which parts
of your program is allocating the most memory, or where memory is allocated
at any moment in the life of your application.
This package is called `GNATCOLL.Memory`. To use it requires a bit of
preparation in your application:
* You need to create your own version of :file:`s-memory.adb` with the
template below, and put it somewhere in your source path. This file should
contain the following bit of code::
with GNATCOLL.Memory;
package body System.Memory is
package M renames GNATCOLL.Memory;
function Alloc (Size : size_t) return System.Address is
begin
return M.Alloc (M.size_t (Size));
end Alloc;
procedure Free (Ptr : System.Address) renames M.Free;
function Realloc
(Ptr : System.Address;
Size : size_t)
return System.Address is
begin
return M.Realloc (Ptr, M.size_t (Size));
end Realloc;
end;
* You then need to compile your application with the extra switch
`-a` passed to `gnatmake` or `gprbuild`, so that this
file is appropriately compiled and linked with your application
* If you only do this, the monitor is disabled by default. This
basically has zero overhead for your application (apart from the initial
small allocation of some internal data). When you call the procedure
`GNATCOLL.Memory.Configure` to activate the monitor, each memory
allocation or deallocation will result in extra overhead that will slow
down your application a bit. But at that point you can then get access
to the information stored in the monitor
We actually recommend that the activation of the monitor be based on an
environment variable or command line switch of your application, so that
you can decide at any time to rerun your application with the monitor
activated, rather than have to go through an extra recompilation.
All allocations and deallocations are monitor automatically when this
module is activated. However, you can also manually call
`GNATCOLL.Memory.Mark_Traceback` to add a dummy entry in the
internal tables that matches the current stack trace. This is helpful
for instance if you want to monitor the calls to a specific subprogram,
and know both the number of calls, and which callers executed it how
many times. This can help find hotspots in your application to optimize
the code.
The information that is available through the monitor is the list of
all chunks of memory that were allocated in Ada (this does not include
allocations done in other languages like C). These chunks are grouped
based on the stack trace at the time of their invocation, and this
package knows how many times each stack trace executed each allocation.
As a result, you can call the function `GNATCOLL.Memory.Dump` to
dump on the standard output various types of data, sorted. To limit the
output to a somewhat usable format, `Dump` asks you to specify
how many blocks it should output.
*Debugging dangling pointer*
Using a dangling pointer can lead (and usually it does) to no crash or no side
effects. Frequently, freed buffers still contains valid data and are still part
of pages owned by your process. Probably, this occurs more often on linux
compare to windows.
Writing 0 or 0xDD pattern when a memory is freed will be (because of the
exception that will be thrown) detected at the first usage of a freed buffer.
The crash occurrence will be higher and less random. This makes solid reproducer
more easy to build.
For dangling pointer usage debugging, use Memory_Free_Pattern parameter when
calling `GNATCOLL.Memory.Configure` procedure.
*Memory usage*
Blocks are sorted based on the amount of memory they have allocated and
is still allocated. This helps you find which part of your application
is currently using the most memory.
*Allocations count*
Blocks are sorted based on the number of allocation that are still
allocated. This helps you find which part of your application has done
the most number of allocations (since malloc is a rather slow system
call, it is in general a good idea to try and reduce the number of
allocations in an application).
*Total number of allocations*
This is similar to the above, but includes all allocations ever done
in this block, even if memory has been deallocated since then.
*Marked blocks*
These are the blocks that were created through your calls to
`GNATCOLL.Memory.Mark_Traceback`. They are sorted by the number
of allocation for that stacktrace, and also shows you the total number
of such allocations in marked blocks. This is useful to monitor and
analyze calls to specific places in your code
|