mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-03 10:05:29 +00:00
This commit completely overhauls the leak checker. In particular:
- It heavily refactors the code: uses better names for things, splits up complex functions that behaved very differently depending on how they were called, removes some redundancies, and generally makes it much simpler and easier to follow. - It adds lots of comments, both inline, and also a big explanatory one at the top which makes it clear exactly how the leak checker works and also exactly what is meant by definite, possible, and indirect leaks. It also has some ideas for future improvements. - All tabs have been converted to spaces. It also improves the functionality: - Previously if you did --leak-check=summary, indirect and suppressed blocks were counted as definite leaks. Now they are done properly, and so the summary results from --leak-check=summary match those from --leak-check=yes. - Previously, some possibly reachable blocks were miscategorised as definitely reachable, because only the pointer to the block itself was considered, not any preceding pointers in the chain. This is now fixed. - Added memcheck/tests/leak-cases, which fully tests all the possible combinations of directly/indirectly reachable and possibly/definitely reachable. And it improves the manual quite a bit, and the FAQ a little bit. This doesn't fix the leak checker to handle MALLOCLIKE_BLOCK works that have been taken from within malloc'd blocks, but I think I know how to do it and hope to do so in a subsequent commit. It also changes all instances of "<constant>memcheck</constant>" in the Memcheck manual to "Memcheck", for consistency and because "Memcheck" is easier to write. There's one similar case for DRD but I didn't change that. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@9330
This commit is contained in:
parent
5df14931d9
commit
bafed25ae9
9
NEWS
9
NEWS
@ -6,6 +6,15 @@ Release 3.5.0 (???)
|
||||
[XXX: consider adding VALGRIND_COUNT_LEAK_BYTES as a synonym and
|
||||
deprecating VALGRIND_COUNT_LEAKS, which wasn't a good name to begin with]
|
||||
|
||||
* Memcheck's leak checker has been improved.
|
||||
- The results for --leak-check=summary now match the summary results for
|
||||
--leak-check=full. Previously they could differ because
|
||||
--leak-check=summary counted "indirectly lost" blocks and "suppressed"
|
||||
blocks as "definitely lost".
|
||||
- Blocks that are only reachable via at least one interior-pointer, but
|
||||
are directly pointed to by a start-pointer, were previously marked as
|
||||
"still reachable". They are now correctly marked as "possibly lost".
|
||||
|
||||
* The location of some install files has changed. This should not affect
|
||||
most users. Those who might be affected:
|
||||
|
||||
|
||||
@ -500,21 +500,28 @@ int main(void)
|
||||
<qandaentry id="faq.deflost">
|
||||
<question id="q-deflost">
|
||||
<para>With Memcheck's memory leak detector, what's the
|
||||
difference between "definitely lost", "possibly lost", "still
|
||||
reachable", and "suppressed"?</para>
|
||||
difference between "definitely lost", "indirectly lost", "possibly
|
||||
lost", "still reachable", and "suppressed"?</para>
|
||||
</question>
|
||||
<answer id="a-deflost">
|
||||
<para>The details are in the Memcheck section of the user
|
||||
manual.</para>
|
||||
<para>The details are in the Memcheck section of the user manual.</para>
|
||||
|
||||
<para>In short:</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>"definitely lost" means your program is leaking memory --
|
||||
fix it!</para>
|
||||
fix those leaks!</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>"possibly lost" means your program is probably leaking
|
||||
<para>"indirectly lost" means your program is leaking memory in
|
||||
a pointer-based structure. (E.g. if the root node of a binary tree
|
||||
is "definitely lost", all the children will be "indirectly lost".)
|
||||
If you fix the "definitely lost" leaks, the "indirectly lost" leaks
|
||||
should go away.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>"possibly lost" means your program is leaking
|
||||
memory, unless you're doing funny things with pointers.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
|
||||
@ -65,11 +65,11 @@ the following problems:</para>
|
||||
<option><![CDATA[--undef-value-errors=<yes|no> [default: yes] ]]></option>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>Controls whether <constant>memcheck</constant> reports
|
||||
<para>Controls whether Memcheck reports
|
||||
uses of undefined value errors. Set this to
|
||||
<varname>no</varname> if you don't want to see undefined value
|
||||
errors. It also has the side effect of speeding up
|
||||
<constant>memcheck</constant> somewhat.
|
||||
memcheck somewhat.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -79,7 +79,7 @@ the following problems:</para>
|
||||
<option><![CDATA[--track-origins=<yes|no> [default: no] ]]></option>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>Controls whether <constant>memcheck</constant> tracks
|
||||
<para>Controls whether Memcheck tracks
|
||||
the origin of uninitialised values. By default, it does not,
|
||||
which means that although it can tell you that an
|
||||
uninitialised value is being used in a dangerous way, it
|
||||
@ -87,43 +87,42 @@ the following problems:</para>
|
||||
often makes it difficult to track down the root problem.
|
||||
</para>
|
||||
<para>When set
|
||||
to <varname>yes</varname>, <constant>memcheck</constant> keeps
|
||||
to <varname>yes</varname>, Memcheck keeps
|
||||
track of the origins of all uninitialised values. Then, when
|
||||
an uninitialised value error is
|
||||
reported, <constant>memcheck</constant> will try to show the
|
||||
reported, Memcheck will try to show the
|
||||
origin of the value. An origin can be one of the following
|
||||
four places: a heap block, a stack allocation, a client
|
||||
request, or miscellaneous other sources (eg, a call
|
||||
to <varname>brk</varname>).
|
||||
</para>
|
||||
<para>For uninitialised values originating from a heap
|
||||
block, <constant>memcheck</constant> shows where the block was
|
||||
block, Memcheck shows where the block was
|
||||
allocated. For uninitialised values originating from a stack
|
||||
allocation, <constant>memcheck</constant> can tell you which
|
||||
allocation, Memcheck can tell you which
|
||||
function allocated the value, but no more than that -- typically
|
||||
it shows you the source location of the opening brace of the
|
||||
function. So you should carefully check that all of the
|
||||
function's local variables are initialised properly.
|
||||
</para>
|
||||
<para>Performance overhead: origin tracking is expensive. It
|
||||
halves <constant>memcheck</constant>'s speed and increases
|
||||
halves Memcheck's speed and increases
|
||||
memory use by a minimum of 100MB, and possibly more.
|
||||
Nevertheless it can drastically reduce the effort required to
|
||||
identify the root cause of uninitialised value errors, and so
|
||||
is often a programmer productivity win, despite running
|
||||
more slowly.
|
||||
</para>
|
||||
<para>Accuracy: <constant>memcheck</constant> tracks origins
|
||||
<para>Accuracy: Memcheck tracks origins
|
||||
quite accurately. To avoid very large space and time
|
||||
overheads, some approximations are made. It is possible,
|
||||
although unlikely, that
|
||||
<constant>memcheck</constant> will report an incorrect origin,
|
||||
or not be able to identify any origin.
|
||||
although unlikely, that Memcheck will report an incorrect origin, or
|
||||
not be able to identify any origin.
|
||||
</para>
|
||||
<para>Note that the combination
|
||||
<option>--track-origins=yes</option>
|
||||
and <option>--undef-value-errors=no</option> is
|
||||
nonsensical. <constant>memcheck</constant> checks for and
|
||||
nonsensical. Memcheck checks for and
|
||||
rejects this combination at startup.
|
||||
</para>
|
||||
<para>Origin tracking is a new feature, introduced in Valgrind
|
||||
@ -138,12 +137,9 @@ the following problems:</para>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>When enabled, search for memory leaks when the client
|
||||
program finishes. A memory leak means a malloc'd block, which has
|
||||
not yet been free'd, but to which no pointer can be found. Such a
|
||||
block can never be free'd by the program, since no pointer to it
|
||||
exists. If set to <varname>summary</varname>, it says how many
|
||||
leaks occurred. If set to <varname>full</varname> or
|
||||
<varname>yes</varname>, it gives details of each individual
|
||||
program finishes. If set to <varname>summary</varname>, it says how
|
||||
many leaks occurred. If set to <varname>full</varname> or
|
||||
<varname>yes</varname>, it also gives details of each individual
|
||||
leak.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -153,17 +149,12 @@ the following problems:</para>
|
||||
<option><![CDATA[--show-reachable=<yes|no> [default: no] ]]></option>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>When disabled, the memory leak detector only shows blocks
|
||||
for which it cannot find a pointer to at all, or it can only find
|
||||
a pointer to the middle of. These blocks are prime candidates for
|
||||
memory leaks. When enabled, the leak detector also reports on
|
||||
blocks which it could find a pointer to. Your program could, at
|
||||
least in principle, have freed such blocks before exit. Contrast
|
||||
this to blocks for which no pointer, or only an interior pointer
|
||||
could be found: they are more likely to indicate memory leaks,
|
||||
because you do not actually have a pointer to the start of the
|
||||
block which you can hand to <function>free</function>, even if you
|
||||
wanted to.</para>
|
||||
<para>When disabled, the memory leak detector only shows "definitely
|
||||
lost" and "possibly lost" blocks. When enabled, the leak detector also
|
||||
shows "reachable" and "indirectly lost" blocks. (In other words, it
|
||||
shows all blocks, except suppressed ones, so
|
||||
<computeroutput>--show-all</computeroutput> would be a better name for
|
||||
it.)
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -173,7 +164,7 @@ the following problems:</para>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>When doing leak checking, determines how willing
|
||||
<constant>memcheck</constant> is to consider different backtraces to
|
||||
Memcheck is to consider different backtraces to
|
||||
be the same. When set to <varname>low</varname>, only the first
|
||||
two entries need match. When <varname>med</varname>, four entries
|
||||
have to match. When <varname>high</varname>, all entries need to
|
||||
@ -183,11 +174,11 @@ the following problems:</para>
|
||||
<option>--leak-resolution=high</option> together with
|
||||
<option>--num-callers=40</option> or some such large number. Note
|
||||
however that this can give an overwhelming amount of information,
|
||||
which is why the defaults are 4 callers and low-resolution
|
||||
matching.</para>
|
||||
which is why the defaults give less information.
|
||||
</para>
|
||||
|
||||
<para>Note that the <option>--leak-resolution=</option> setting
|
||||
does not affect <constant>memcheck's</constant> ability to find
|
||||
does not affect Memcheck's ability to find
|
||||
leaks. It only changes how the results are presented.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -204,14 +195,14 @@ the following problems:</para>
|
||||
and placed in a queue of freed blocks. The purpose is to defer as
|
||||
long as possible the point at which freed-up memory comes back
|
||||
into circulation. This increases the chance that
|
||||
<constant>memcheck</constant> will be able to detect invalid
|
||||
Memcheck will be able to detect invalid
|
||||
accesses to blocks for some significant period of time after they
|
||||
have been freed.</para>
|
||||
|
||||
<para>This flag specifies the maximum total size, in bytes, of the
|
||||
blocks in the queue. The default value is ten million bytes.
|
||||
Increasing this increases the total amount of memory used by
|
||||
<constant>memcheck</constant> but may detect invalid uses of freed
|
||||
Memcheck but may detect invalid uses of freed
|
||||
blocks which would otherwise go undetected.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -245,7 +236,7 @@ the following problems:</para>
|
||||
<option><![CDATA[--partial-loads-ok=<yes|no> [default: no] ]]></option>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>Controls how <constant>memcheck</constant> handles word-sized,
|
||||
<para>Controls how Memcheck handles word-sized,
|
||||
word-aligned loads from addresses for which some bytes are
|
||||
addressable and others are not. When <varname>yes</varname>, such
|
||||
loads do not produce an address error. Instead, loaded bytes
|
||||
@ -632,45 +623,126 @@ which blocks have not been freed.
|
||||
</para>
|
||||
|
||||
<para>If <option>--leak-check</option> is set appropriately, for each
|
||||
remaining block, Memcheck scans the entire address space of the process,
|
||||
looking for pointers to the block. Each block fits into one of the
|
||||
three following categories.</para>
|
||||
remaining block, Memcheck determines if the block is reachable from pointers
|
||||
within the root-set. The root-set consists of (a) general purpose registers
|
||||
of all threads, and (b) initialised, aligned, pointer-sized data words in
|
||||
accessible client memory, including stacks.</para>
|
||||
|
||||
<para>There are two ways a block can be reached. The first is with a
|
||||
"start-pointer", i.e. a pointer to the start of the block. The second is
|
||||
with an "interior-pointer", i.e. a pointer to the middle of the block. The
|
||||
pointer might have originally been a start-pointer and have been moved
|
||||
along, or it might be entirely unrelated, just a coincidence. It's unclear
|
||||
whether such a pointer should be considered as genuinely pointing to the
|
||||
block.</para>
|
||||
|
||||
<para>With that in mind, consider the nine possible cases described by the
|
||||
following figure.</para>
|
||||
|
||||
<programlisting><![CDATA[
|
||||
Pointer chain AAA Category BBB Category
|
||||
------------- ------------ ------------
|
||||
(1) RRR ------------> BBB DR
|
||||
(2) RRR ---> AAA ---> BBB DR IR
|
||||
(3) RRR BBB DL
|
||||
(4) RRR AAA ---> BBB DL IL
|
||||
(5) RRR ------?-----> BBB (y)DR, (n)DL
|
||||
(6) RRR ---> AAA -?-> BBB DR (y)IR, (n)DL
|
||||
(7) RRR -?-> AAA ---> BBB (y)DR, (n)DL (y)IR, (n)IL
|
||||
(8) RRR -?-> AAA -?-> BBB (y)DR, (n)DL (y,y)IR, (n,y)IL, (_,n)DL
|
||||
(9) RRR AAA -?-> BBB DL (y)IL, (n)DL
|
||||
|
||||
Pointer chain legend:
|
||||
- RRR: a root set node or DR block
|
||||
- AAA, BBB: heap blocks
|
||||
- --->: a start-pointer
|
||||
- -?->: an interior-pointer
|
||||
|
||||
Category legend:
|
||||
- DR: Directly reachable
|
||||
- IR: Indirectly reachable
|
||||
- DL: Directly lost
|
||||
- IL: Indirectly lost
|
||||
- (y)XY: it's XY if the interior-pointer is a real pointer
|
||||
- (n)XY: it's XY if the interior-pointer is not a real pointer
|
||||
- (_)XY: it's XY in either case
|
||||
]]></programlisting>
|
||||
|
||||
<para>Every possible case can be reduced to one of the above nine. Memcheck
|
||||
merges some of these cases in its output, resulting in the following four
|
||||
categories.</para>
|
||||
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>Still reachable: A pointer to the start of the block is found.
|
||||
This usually indicates programming sloppiness. Since the block is
|
||||
still pointed at, the programmer could, at least in principle, free
|
||||
it before program exit. Because these are very common and arguably
|
||||
not a problem, Memcheck won't report such blocks unless
|
||||
<option>--show-reachable=yes</option> is specified.</para>
|
||||
<para>"Still reachable". This covers cases 1 and 2 (for the BBB blocks)
|
||||
above. A start-pointer or chain of start-pointers to the block is
|
||||
found. Since the block is still pointed at, the programmer could, at
|
||||
least in principle, have freed it before program exit. Because these
|
||||
are very common and arguably not a problem, Memcheck won't report such
|
||||
blocks individually unless <option>--show-reachable=yes</option> is
|
||||
specified.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Possibly lost, or "dubious": A pointer to the interior of the
|
||||
block is found. The pointer might originally have pointed to the
|
||||
start and have been moved along, or it might be entirely unrelated.
|
||||
Memcheck deems such a block as "dubious", because it's unclear
|
||||
whether or not a pointer to it still exists.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Definitely lost, or "leaked": The worst outcome is that no
|
||||
pointer to the block can be found. The block is classified as
|
||||
"leaked", because the programmer could not possibly have freed it at
|
||||
program exit, since no pointer to it exists. This is likely a
|
||||
symptom of having lost the pointer at some earlier point in the
|
||||
program.</para>
|
||||
<para>"Definitely lost". This covers case 3 (for the BBB blocks) above.
|
||||
This means that no pointer to the block can be found. The block is
|
||||
classified as "lost", because the programmer could not possibly have
|
||||
freed it at program exit, since no pointer to it exists. This is likely
|
||||
a symptom of having lost the pointer at some earlier point in the
|
||||
program. Such cases should be fixed by the programmer.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>"Indirectly lost". This covers cases 4 and 9 (for the BBB blocks)
|
||||
above. This means that the block is lost, not because there are no
|
||||
pointers to it, but rather because all the blocks that point to it are
|
||||
themselves lost. For example, if you have a binary tree and the root
|
||||
node is lost, all its children nodes will be indirectly lost. Because
|
||||
the problem will disappear if the definitely lost block that caused the
|
||||
indirect leak is fixed, Memcheck won't report such blocks individually
|
||||
unless <option>--show-reachable=yes</option> is specified.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>"Possibly lost". This covers cases 5--8 (for the BBB blocks)
|
||||
above. This means that a chain of one or more pointers to the block has
|
||||
been found, but at least one of the pointers is an interior-pointer.
|
||||
This could just be a random value in memory that happens to point into a
|
||||
block, and so you shouldn't consider this ok unless you know you have
|
||||
interior-pointers.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<para>For each block mentioned, Memcheck will also tell you where the
|
||||
block was allocated. It cannot tell you how or why the pointer to a
|
||||
leaked block has been lost; you have to work that out for yourself. In
|
||||
general, you should attempt to ensure your programs do not have any
|
||||
leaked or dubious blocks at exit.</para>
|
||||
<para>(Note: This mapping of the nine possible cases onto four categories is
|
||||
not necessarily the best way that leaks could be reported; in particular,
|
||||
interior-pointers are treated inconsistently. It is possible the
|
||||
categorisation may be improved in the future.)</para>
|
||||
|
||||
<para>Furthermore, if suppressions exists for a block, it will be reported
|
||||
as "suppressed" no matter what which of the above four categories it belongs
|
||||
to.</para>
|
||||
|
||||
|
||||
<para>The following is an example leak summary.</para>
|
||||
|
||||
<programlisting><![CDATA[
|
||||
LEAK SUMMARY:
|
||||
definitely lost: 48 bytes in 3 blocks.
|
||||
indirectly lost: 32 bytes in 2 blocks.
|
||||
possibly lost: 96 bytes in 6 blocks.
|
||||
still reachable: 64 bytes in 4 blocks.
|
||||
suppressed: 0 bytes in 0 blocks.
|
||||
]]></programlisting>
|
||||
|
||||
<para>If <computeroutput>--leak-check=full</computeroutput> is specified,
|
||||
Memcheck will give details for each definitely lost or possibly lost block,
|
||||
including where it was allocated. It cannot tell you when or how or why the
|
||||
pointer to a leaked block was lost; you have to work that out for yourself.
|
||||
In general, you should attempt to ensure your programs do not have any
|
||||
definitely lost or possibly lost blocks at exit.</para>
|
||||
|
||||
<para>For example:</para>
|
||||
<programlisting><![CDATA[
|
||||
@ -679,23 +751,32 @@ leaked or dubious blocks at exit.</para>
|
||||
by 0x........: mk (leak-tree.c:11)
|
||||
by 0x........: main (leak-tree.c:39)
|
||||
|
||||
88 (8 direct, 80 indirect) bytes in 1 blocks are definitely lost
|
||||
in loss record 13 of 14
|
||||
88 (8 direct, 80 indirect) bytes in 1 blocks are definitely lost in loss record 13 of 14
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-tree.c:11)
|
||||
by 0x........: main (leak-tree.c:25)
|
||||
]]></programlisting>
|
||||
|
||||
<para>The first message describes a simple case of a single 8 byte block
|
||||
that has been definitely lost. The second case mentions both "direct"
|
||||
and "indirect" leaks. The distinction is that a direct leak is a block
|
||||
which has no pointers to it. An indirect leak is a block which is only
|
||||
pointed to by other leaked blocks. Both kinds of leak are bad.</para>
|
||||
that has been definitely lost. The second case mentions another 8 byte
|
||||
block that has been definitely lost; the difference is that a further 80
|
||||
bytes in other blocks are indirectly lost because of this lost block.</para>
|
||||
|
||||
<para>The precise area of memory in which Memcheck searches for pointers
|
||||
is: all naturally-aligned machine-word-sized words found in memory
|
||||
that Memcheck's records indicate is both accessible and initialised.
|
||||
</para>
|
||||
<para>If you specify <computeroutput>--show-reachable=yes</computeroutput>,
|
||||
reachable and indirectly lost blocks will also be shown, as the following
|
||||
two examples show.</para>
|
||||
|
||||
<programlisting><![CDATA[
|
||||
64 bytes in 4 blocks are still reachable in loss record 2 of 4
|
||||
at 0x........: malloc (vg_replace_malloc.c:177)
|
||||
by 0x........: mk (leak-cases.c:52)
|
||||
by 0x........: main (leak-cases.c:74)
|
||||
|
||||
32 bytes in 2 blocks are indirectly lost in loss record 1 of 4
|
||||
at 0x........: malloc (vg_replace_malloc.c:177)
|
||||
by 0x........: mk (leak-cases.c:52)
|
||||
by 0x........: main (leak-cases.c:80)
|
||||
]]></programlisting>
|
||||
|
||||
</sect2>
|
||||
|
||||
@ -1159,22 +1240,30 @@ arguments.</para>
|
||||
<para><varname>VALGRIND_CHECK_VALUE_IS_DEFINED</varname>: a quick and easy
|
||||
way to find out whether Valgrind thinks a particular value
|
||||
(lvalue, to be precise) is addressable and defined. Prints an error
|
||||
message if not. Returns no value.</para>
|
||||
message if not. It has no return value.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><varname>VALGRIND_DO_LEAK_CHECK</varname>: runs the memory
|
||||
leak detector right now. Is useful for incrementally checking for
|
||||
leaks between arbitrary places in the program's execution. Returns
|
||||
no value.</para>
|
||||
<para><varname>VALGRIND_DO_LEAK_CHECK</varname>: does a full memory leak
|
||||
check (like <computeroutput>--leak-check=full</computeroutput> right now.
|
||||
This is useful for incrementally checking for leaks between arbitrary
|
||||
places in the program's execution. It has no return value.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><varname>VALGRIND_DO_QUICK_LEAK_CHECK</varname>: like
|
||||
<varname>VALGRIND_DO_LEAK_CHECK</varname>, except it produces only a leak
|
||||
summary (like <computeroutput>--leak-check=summary</computeroutput>).
|
||||
It has no return value.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><varname>VALGRIND_COUNT_LEAKS</varname>: fills in the four
|
||||
arguments with the number of bytes of memory found by the previous
|
||||
leak check to be leaked, dubious, reachable and suppressed. Again,
|
||||
useful in test harness code, after calling
|
||||
<varname>VALGRIND_DO_LEAK_CHECK</varname>.</para>
|
||||
leak check to be leaked (i.e. the sum of direct leaks and indirect leaks),
|
||||
dubious, reachable and suppressed. Again, useful in test harness code,
|
||||
after calling <varname>VALGRIND_DO_LEAK_CHECK</varname> or
|
||||
<varname>VALGRIND_DO_QUICK_LEAK_CHECK</varname>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
|
||||
@ -355,8 +355,8 @@ static const HChar* str_leak_lossmode ( Reachedness lossmode )
|
||||
switch (lossmode) {
|
||||
case Unreached: loss = "definitely lost"; break;
|
||||
case IndirectLeak: loss = "indirectly lost"; break;
|
||||
case Interior: loss = "possibly lost"; break;
|
||||
case Proper: loss = "still reachable"; break;
|
||||
case Possible: loss = "possibly lost"; break;
|
||||
case Reachable: loss = "still reachable"; break;
|
||||
}
|
||||
return loss;
|
||||
}
|
||||
@ -367,8 +367,8 @@ static const HChar* xml_leak_kind ( Reachedness lossmode )
|
||||
switch (lossmode) {
|
||||
case Unreached: loss = "Leak_DefinitelyLost"; break;
|
||||
case IndirectLeak: loss = "Leak_IndirectlyLost"; break;
|
||||
case Interior: loss = "Leak_PossiblyLost"; break;
|
||||
case Proper: loss = "Leak_StillReachable"; break;
|
||||
case Possible: loss = "Leak_PossiblyLost"; break;
|
||||
case Reachable: loss = "Leak_StillReachable"; break;
|
||||
}
|
||||
return loss;
|
||||
}
|
||||
@ -574,20 +574,20 @@ void MC_(pp_Error) ( Error* err )
|
||||
VG_(message)(Vg_UserMsg, "");
|
||||
}
|
||||
|
||||
if (l->indirect_bytes) {
|
||||
if (l->indirect_szB) {
|
||||
VG_(message)(Vg_UserMsg,
|
||||
"%s%'lu (%'lu direct, %'lu indirect) bytes in %'u blocks"
|
||||
" are %s in loss record %'u of %'u%s",
|
||||
xpre,
|
||||
l->total_bytes + l->indirect_bytes,
|
||||
l->total_bytes, l->indirect_bytes, l->num_blocks,
|
||||
l->total_bytes + l->indirect_szB,
|
||||
l->total_bytes, l->indirect_szB, l->num_blocks,
|
||||
str_leak_lossmode(l->loss_mode), n_this_record, n_total_records,
|
||||
xpost
|
||||
);
|
||||
if (VG_(clo_xml)) {
|
||||
// Nb: don't put commas in these XML numbers
|
||||
VG_(message)(Vg_UserMsg, " <leakedbytes>%lu</leakedbytes>",
|
||||
l->total_bytes + l->indirect_bytes);
|
||||
l->total_bytes + l->indirect_szB);
|
||||
VG_(message)(Vg_UserMsg, " <leakedblocks>%u</leakedblocks>",
|
||||
l->num_blocks);
|
||||
}
|
||||
|
||||
@ -55,14 +55,15 @@ typedef
|
||||
}
|
||||
MC_AllocKind;
|
||||
|
||||
/* Nb: first two fields must match core's VgHashNode. */
|
||||
/* This describes a heap block. Nb: first two fields must match core's
|
||||
* VgHashNode. */
|
||||
typedef
|
||||
struct _MC_Chunk {
|
||||
struct _MC_Chunk* next;
|
||||
Addr data; // ptr to actual block
|
||||
SizeT szB : (sizeof(UWord)*8)-2; // size requested; 30 or 62 bits
|
||||
MC_AllocKind allockind : 2; // which wrapper did the allocation
|
||||
ExeContext* where; // where it was allocated
|
||||
Addr data; // Address of the actual block.
|
||||
SizeT szB : (sizeof(SizeT)*8)-2; // Size requested; 30 or 62 bits.
|
||||
MC_AllocKind allockind : 2; // Which operation did the allocation.
|
||||
ExeContext* where; // Where it was allocated.
|
||||
}
|
||||
MC_Chunk;
|
||||
|
||||
@ -227,18 +228,15 @@ HChar* MC_(event_ctr_name)[N_PROF_EVENTS];
|
||||
/*--- Leak checking ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
/* A block is either
|
||||
-- Proper-ly reached; a pointer to its start has been found
|
||||
-- Interior-ly reached; only an interior pointer to it has been found
|
||||
-- Unreached; so far, no pointers to any part of it have been found.
|
||||
-- IndirectLeak; leaked, but referred to by another leaked block
|
||||
*/
|
||||
typedef
|
||||
enum {
|
||||
Unreached =0,
|
||||
IndirectLeak =1,
|
||||
Interior =2,
|
||||
Proper =3
|
||||
Unreached =0, // Not reached, ie. leaked.
|
||||
// (At best, only reachable from itself via a cycle.
|
||||
IndirectLeak =1, // Leaked, but reachable from another leaked block
|
||||
// (be it Unreached or IndirectLeak).
|
||||
Possible =2, // Possibly reachable from root-set; involves at
|
||||
// least one interior-pointer along the way.
|
||||
Reachable =3 // Definitely reachable from root-set.
|
||||
}
|
||||
Reachedness;
|
||||
|
||||
@ -274,16 +272,15 @@ typedef
|
||||
Reachedness loss_mode;
|
||||
/* Number of blocks and total # bytes involved. */
|
||||
SizeT total_bytes;
|
||||
SizeT indirect_bytes;
|
||||
SizeT indirect_szB;
|
||||
UInt num_blocks;
|
||||
}
|
||||
LossRecord;
|
||||
|
||||
void MC_(do_detect_memory_leaks) (
|
||||
ThreadId tid, LeakCheckMode mode,
|
||||
Bool (*is_within_valid_secondary) ( Addr ),
|
||||
Bool (*is_valid_aligned_word) ( Addr )
|
||||
);
|
||||
void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckMode mode );
|
||||
|
||||
Bool MC_(is_valid_aligned_word) ( Addr a );
|
||||
Bool MC_(is_within_valid_secondary) ( Addr a );
|
||||
|
||||
void MC_(pp_LeakError)(UInt n_this_record, UInt n_total_records,
|
||||
LossRecord* l);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -4459,8 +4459,7 @@ static Int mc_get_or_set_vbits_for_client (
|
||||
address space is possibly in use, or not. If in doubt return
|
||||
True.
|
||||
*/
|
||||
static
|
||||
Bool mc_is_within_valid_secondary ( Addr a )
|
||||
Bool MC_(is_within_valid_secondary) ( Addr a )
|
||||
{
|
||||
SecMap* sm = maybe_get_secmap_for ( a );
|
||||
if (sm == NULL || sm == &sm_distinguished[SM_DIST_NOACCESS]
|
||||
@ -4475,15 +4474,10 @@ Bool mc_is_within_valid_secondary ( Addr a )
|
||||
|
||||
/* For the memory leak detector, say whether or not a given word
|
||||
address is to be regarded as valid. */
|
||||
static
|
||||
Bool mc_is_valid_aligned_word ( Addr a )
|
||||
Bool MC_(is_valid_aligned_word) ( Addr a )
|
||||
{
|
||||
tl_assert(sizeof(UWord) == 4 || sizeof(UWord) == 8);
|
||||
if (sizeof(UWord) == 4) {
|
||||
tl_assert(VG_IS_4_ALIGNED(a));
|
||||
} else {
|
||||
tl_assert(VG_IS_8_ALIGNED(a));
|
||||
}
|
||||
tl_assert(VG_IS_WORD_ALIGNED(a));
|
||||
if (is_mem_defined( a, sizeof(UWord), NULL, NULL) == MC_Ok
|
||||
&& !MC_(in_ignored_range)(a)) {
|
||||
return True;
|
||||
@ -4493,20 +4487,6 @@ Bool mc_is_valid_aligned_word ( Addr a )
|
||||
}
|
||||
|
||||
|
||||
/* Leak detector for this tool. We don't actually do anything, merely
|
||||
run the generic leak detector with suitable parameters for this
|
||||
tool. */
|
||||
static void mc_detect_memory_leaks ( ThreadId tid, LeakCheckMode mode )
|
||||
{
|
||||
MC_(do_detect_memory_leaks) (
|
||||
tid,
|
||||
mode,
|
||||
mc_is_within_valid_secondary,
|
||||
mc_is_valid_aligned_word
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Initialisation ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
@ -4933,7 +4913,7 @@ static Bool mc_handle_client_request ( ThreadId tid, UWord* arg, UWord* ret )
|
||||
}
|
||||
|
||||
case VG_USERREQ__DO_LEAK_CHECK:
|
||||
mc_detect_memory_leaks(tid, arg[1] ? LC_Summary : LC_Full);
|
||||
MC_(detect_memory_leaks)(tid, arg[1] ? LC_Summary : LC_Full);
|
||||
*ret = 0; /* return value is meaningless */
|
||||
break;
|
||||
|
||||
@ -5590,7 +5570,7 @@ static void mc_fini ( Int exitcode )
|
||||
}
|
||||
|
||||
if (MC_(clo_leak_check) != LC_Off)
|
||||
mc_detect_memory_leaks(1/*bogus ThreadId*/, MC_(clo_leak_check));
|
||||
MC_(detect_memory_leaks)(1/*bogus ThreadId*/, MC_(clo_leak_check));
|
||||
|
||||
done_prof_mem();
|
||||
|
||||
|
||||
@ -206,7 +206,7 @@ typedef
|
||||
(unsigned long)(sizeof (__lvalue)))
|
||||
|
||||
|
||||
/* Do a memory leak check mid-execution. */
|
||||
/* Do a full memory leak check (like --leak-check=full) mid-execution. */
|
||||
#define VALGRIND_DO_LEAK_CHECK \
|
||||
{unsigned long _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
|
||||
@ -214,8 +214,7 @@ typedef
|
||||
0, 0, 0, 0, 0); \
|
||||
}
|
||||
|
||||
/* Just display summaries of leaked memory, rather than all the
|
||||
details */
|
||||
/* Do a summary memory leak check (like --leak-check=summary) mid-execution. */
|
||||
#define VALGRIND_DO_QUICK_LEAK_CHECK \
|
||||
{unsigned long _qzz_res; \
|
||||
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
|
||||
|
||||
@ -59,6 +59,8 @@ EXTRA_DIST = $(noinst_SCRIPTS) \
|
||||
inits.stderr.exp inits.vgtest \
|
||||
inline.stderr.exp inline.stdout.exp inline.vgtest \
|
||||
leak-0.vgtest leak-0.stderr.exp \
|
||||
leak-cases-full.vgtest leak-cases-full.stderr.exp \
|
||||
leak-cases-summary.vgtest leak-cases-summary.stderr.exp \
|
||||
leak-cycle.vgtest leak-cycle.stderr.exp \
|
||||
leak-pool-0.vgtest leak-pool-0.stderr.exp \
|
||||
leak-pool-1.vgtest leak-pool-1.stderr.exp \
|
||||
@ -187,7 +189,9 @@ check_PROGRAMS = \
|
||||
doublefree error_counts errs1 exitprog execve execve2 erringfds \
|
||||
file_locking \
|
||||
fprw fwrite inits inline \
|
||||
leak-0 leak-cycle leak-pool leak-tree leakotron \
|
||||
leak-0 \
|
||||
leak-cases \
|
||||
leak-cycle leak-pool leak-tree leakotron \
|
||||
linux-syslog-syscall \
|
||||
linux-syscalls-2007 \
|
||||
long_namespace_xml \
|
||||
|
||||
@ -39,5 +39,5 @@ sed "s/checked [0-9,]* bytes./checked ... bytes./" |
|
||||
# More leak check filtering. For systems that do extra libc allocations
|
||||
# (eg. Darwin) there may be extra (reachable, and thus not shown) loss
|
||||
# records. So we filter out the loss record numbers.
|
||||
perl -p -e "s/lost in loss record \d+ of \d+/lost in loss record ... of .../"
|
||||
perl -p -e "s/in loss record \d+ of \d+/in loss record ... of .../"
|
||||
|
||||
|
||||
57
memcheck/tests/leak-cases-full.stderr.exp
Normal file
57
memcheck/tests/leak-cases-full.stderr.exp
Normal file
@ -0,0 +1,57 @@
|
||||
leaked: 80 bytes in 5 blocks
|
||||
dubious: 96 bytes in 6 blocks
|
||||
reachable: 64 bytes in 4 blocks
|
||||
suppressed: 0 bytes in 0 blocks
|
||||
|
||||
16 bytes in 1 blocks are possibly lost in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-cases.c:52)
|
||||
by 0x........: main (leak-cases.c:91)
|
||||
|
||||
|
||||
16 bytes in 1 blocks are possibly lost in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-cases.c:52)
|
||||
by 0x........: main (leak-cases.c:91)
|
||||
|
||||
|
||||
16 bytes in 1 blocks are possibly lost in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-cases.c:52)
|
||||
by 0x........: main (leak-cases.c:88)
|
||||
|
||||
|
||||
16 bytes in 1 blocks are possibly lost in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-cases.c:52)
|
||||
by 0x........: main (leak-cases.c:88)
|
||||
|
||||
|
||||
16 bytes in 1 blocks are possibly lost in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-cases.c:52)
|
||||
by 0x........: main (leak-cases.c:85)
|
||||
|
||||
|
||||
16 bytes in 1 blocks are possibly lost in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-cases.c:52)
|
||||
by 0x........: main (leak-cases.c:82)
|
||||
|
||||
|
||||
16 bytes in 1 blocks are definitely lost in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-cases.c:52)
|
||||
by 0x........: main (leak-cases.c:78)
|
||||
|
||||
|
||||
32 (16 direct, 16 indirect) bytes in 1 blocks are definitely lost in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-cases.c:52)
|
||||
by 0x........: main (leak-cases.c:80)
|
||||
|
||||
|
||||
32 (16 direct, 16 indirect) bytes in 1 blocks are definitely lost in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-cases.c:52)
|
||||
by 0x........: main (leak-cases.c:95)
|
||||
2
memcheck/tests/leak-cases-full.vgtest
Normal file
2
memcheck/tests/leak-cases-full.vgtest
Normal file
@ -0,0 +1,2 @@
|
||||
prog: leak-cases
|
||||
vgopts: -q --leak-check=full --leak-resolution=high
|
||||
4
memcheck/tests/leak-cases-summary.stderr.exp
Normal file
4
memcheck/tests/leak-cases-summary.stderr.exp
Normal file
@ -0,0 +1,4 @@
|
||||
leaked: 80 bytes in 5 blocks
|
||||
dubious: 96 bytes in 6 blocks
|
||||
reachable: 64 bytes in 4 blocks
|
||||
suppressed: 0 bytes in 0 blocks
|
||||
2
memcheck/tests/leak-cases-summary.vgtest
Normal file
2
memcheck/tests/leak-cases-summary.vgtest
Normal file
@ -0,0 +1,2 @@
|
||||
prog: leak-cases
|
||||
vgopts: -q --leak-check=summary --leak-resolution=high
|
||||
104
memcheck/tests/leak-cases.c
Normal file
104
memcheck/tests/leak-cases.c
Normal file
@ -0,0 +1,104 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "leak.h"
|
||||
#include "../memcheck.h"
|
||||
|
||||
// Pointer chain AAA Category/output BBB Category/output
|
||||
// ------------- ------------------- ------------
|
||||
// p1 ---> AAA DR / R
|
||||
// p2 ---> AAA ---> BBB DR / R IR / R
|
||||
// p3 AAA DL / L
|
||||
// p4 AAA ---> BBB DL / I IL / L
|
||||
// p5 -?-> AAA (y)DR, (n)DL / P
|
||||
// p6 ---> AAA -?-> BBB DR / R (y)IR, (n)DL / P
|
||||
// p7 -?-> AAA ---> BBB (y)DR, (n)DL / P (y)IR, (n)IL / P
|
||||
// p8 -?-> AAA -?-> BBB (y)DR, (n)DL / P (y,y)IR, (n,y)IL, (_,n)DL / P
|
||||
// p9 AAA -?-> BBB DL / L (y)IL, (n)DL / I
|
||||
//
|
||||
// Pointer chain legend:
|
||||
// - pN: a root set pointer
|
||||
// - AAA, BBB: heap blocks
|
||||
// - --->: a start-pointer
|
||||
// - -?->: an interior-pointer
|
||||
//
|
||||
// Category legend:
|
||||
// - DR: Directly reachable
|
||||
// - IR: Indirectly reachable
|
||||
// - DL: Directly lost
|
||||
// - IL: Indirectly lost
|
||||
// - (y)XY: it's XY if the interior-pointer is a real pointer
|
||||
// - (n)XY: it's XY if the interior-pointer is not a real pointer
|
||||
// - (_)XY: it's XY in either case
|
||||
//
|
||||
// How we handle the 9 cases:
|
||||
// - "directly lost": case 3
|
||||
// - "indirectly lost": cases 4, 9
|
||||
// - "possibly lost": cases 5..8
|
||||
// - "still reachable": cases 1, 2
|
||||
|
||||
|
||||
typedef
|
||||
struct _Node {
|
||||
struct _Node* next;
|
||||
// Padding ensures the structu is the same size on 32-bit and 64-bit
|
||||
// machines.
|
||||
char padding[8 - sizeof(struct _Node*)];
|
||||
} Node;
|
||||
|
||||
Node* mk(Node* next)
|
||||
{
|
||||
// We allocate two nodes, so we can do p+1 and still point within the
|
||||
// block.
|
||||
Node* x = malloc(2 * sizeof(Node));
|
||||
x->next = next;
|
||||
return x;
|
||||
}
|
||||
|
||||
// These are definite roots.
|
||||
Node* p1;
|
||||
Node* p2;
|
||||
Node* p3;
|
||||
Node* p4;
|
||||
Node* p5;
|
||||
Node* p6;
|
||||
Node* p7;
|
||||
Node* p8;
|
||||
Node* p9;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
DECLARE_LEAK_COUNTERS;
|
||||
|
||||
GET_INITIAL_LEAK_COUNTS;
|
||||
|
||||
p1 = mk(NULL); // Case 1: 16/1 still reachable
|
||||
|
||||
p2 = mk(mk(NULL)); // Case 2: 16/1 still reachable
|
||||
// 16/1 still reachable
|
||||
(void)mk(NULL); // Case 3: 16/1 definitely lost
|
||||
|
||||
(void)mk(mk(NULL)); // Case 4: 16/1 indirectly lost (counted again below!)
|
||||
// 32(16d,16i)/1 definitely lost (double count!)
|
||||
p5 = mk(NULL); // Case 5: 16/1 possibly lost (ok)
|
||||
p5++;
|
||||
|
||||
p6 = mk(mk(NULL)); // Case 6: 16/1 still reachable
|
||||
(p6->next)++; // 16/1 possibly lost
|
||||
|
||||
p7 = mk(mk(NULL)); // Case 7: 16/1 possibly lost
|
||||
p7++; // 16/1 possibly lost
|
||||
|
||||
p8 = mk(mk(NULL)); // Case 8: 16/1 possibly lost
|
||||
(p8->next)++; // 16/1 possibly lost
|
||||
p8++;
|
||||
|
||||
p9 = mk(mk(NULL)); // Case 9: 16/1 indirectly lost (counted again below!)
|
||||
(p9->next)++; // 32(16d,16i)/1 definitely lost (double count!)
|
||||
p9 = NULL;
|
||||
|
||||
GET_FINAL_LEAK_COUNTS;
|
||||
|
||||
PRINT_LEAK_COUNTS(stderr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user