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:
Nicholas Nethercote 2009-03-09 22:52:24 +00:00
parent 5df14931d9
commit bafed25ae9
15 changed files with 1215 additions and 770 deletions

9
NEWS
View File

@ -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:

View File

@ -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>

View File

@ -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>

View File

@ -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);
}

View File

@ -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

View File

@ -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();

View File

@ -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, \

View File

@ -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 \

View File

@ -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 .../"

View 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)

View File

@ -0,0 +1,2 @@
prog: leak-cases
vgopts: -q --leak-check=full --leak-resolution=high

View 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

View File

@ -0,0 +1,2 @@
prog: leak-cases
vgopts: -q --leak-check=summary --leak-resolution=high

104
memcheck/tests/leak-cases.c Normal file
View 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;
}