Massif: a heap profiler
The next snapshot is detailed. As well as the basic counts, it gives an allocation tree which indicates exactly which
pieces of code were responsible for allocating heap memory:
9
9,072
9,072
9,000
72
0
99.21% (9,000B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->99.21% (9,000B) 0x804841A: main (example.c:20)
The allocation tree can be read from the top down.
The first line indicates all heap allocation functions such as
malloc
and C++
new
.
All heap allocations go through these functions, and so all 9,000 useful bytes (which is
99.21% of all allocated bytes) go through them.
But how were
malloc
and new called?
At this point, every
allocation so far has been due to line 20 inside
main
, hence the second line in the tree. The
->
indicates that main
(line 20) called
malloc
.
Let’s see what the subsequent output shows happened next:
--------------------------------------------------------------------------------
n
time(B)
total(B)
useful-heap(B) extra-heap(B)
stacks(B)
--------------------------------------------------------------------------------
10
10,080
10,080
10,000
80
0
11
12,088
12,088
12,000
88
0
12
16,096
16,096
16,000
96
0
13
20,104
20,104
20,000
104
0
14
20,104
20,104
20,000
104
0
99.48% (20,000B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->49.74% (10,000B) 0x804841A: main (example.c:20)
|
->39.79% (8,000B) 0x80483C2: g (example.c:5)
| ->19.90% (4,000B) 0x80483E2: f (example.c:11)
| | ->19.90% (4,000B) 0x8048431: main (example.c:23)
| |
| ->19.90% (4,000B) 0x8048436: main (example.c:25)
|
->09.95% (2,000B) 0x80483DA: f (example.c:10)
->09.95% (2,000B) 0x8048431: main (example.c:23)
The first four snapshots are similar to the previous ones. But then the global allocation peak is reached, and a detailed
snapshot (number 14) is taken. Its allocation tree shows that 20,000B of useful heap memory has been allocated, and
the lines and arrows indicate that this is from three different code locations: line 20, which is responsible for 10,000B
(49.74%); line 5, which is responsible for 8,000B (39.79%); and line 10, which is responsible for 2,000B (9.95%).
We can then drill down further in the allocation tree. For example, of the 8,000B asked for by line 5, half of it was
due to a call from line 11, and half was due to a call from line 25.
In short, Massif collates the stack trace of every single allocation point in the program into a single tree, which gives
a complete picture at a particular point in time of how and why all heap memory was allocated.
Note that the tree entries correspond not to functions, but to individual code locations.
For example, if function
A
calls
malloc
, and function
B
calls
A
twice, once on line 10 and once on line 11, then the two calls will result in two
143