Memcheck:

* add delta leak checking functionality
* some editing of related manual sections
(Philippe Waroquiers, philippe.waroquiers@skynet.be).  Bug 214909
comment 105.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11838
This commit is contained in:
Julian Seward 2011-06-26 12:41:33 +00:00
parent 64940ed44a
commit 5c1e65aa42
16 changed files with 623 additions and 155 deletions

View File

@ -495,12 +495,12 @@ of the Valgrind core monitor commands.
<para>Each tool can also provide tool-specific monitor commands.
An example of a tool specific monitor command is the Memcheck monitor
command <computeroutput>mc.leak_check any full
reachable</computeroutput>. This requests a full reporting of the
command <computeroutput>mc.leak_check full
reachable any</computeroutput>. This requests a full reporting of the
allocated memory blocks. To have this leak check executed, use the GDB
command:
<screen><![CDATA[
(gdb) monitor mc.leak_check any full reachable
(gdb) monitor mc.leak_check full reachable any
]]></screen>
</para>
@ -511,7 +511,7 @@ monitor command. If it is not recognised as such, it is assumed to
be tool-specific and is handed to the tool for execution. For example:
</para>
<programlisting><![CDATA[
(gdb) monitor mc.leak_check any full reachable
(gdb) monitor mc.leak_check full reachable any
==2418== 100 bytes in 1 blocks are still reachable in loss record 1 of 1
==2418== at 0x4006E9E: malloc (vg_replace_malloc.c:236)
==2418== by 0x804884F: main (prog.c:88)
@ -532,12 +532,12 @@ abbreviation is unambiguous. For example, the above
<computeroutput>mc.leak_check</computeroutput>
command can also be typed as:
<screen><![CDATA[
(gdb) mo mc.l a f r
(gdb) mo mc.l f r a
]]></screen>
The letters <computeroutput>mo</computeroutput> are recognised by GDB as being
an abbreviation for <computeroutput>monitor</computeroutput>. So GDB sends the
string <computeroutput>mc.l a f r</computeroutput> to the Valgrind
string <computeroutput>mc.l f r a</computeroutput> to the Valgrind
gdbserver. The letters provided in this string are unambiguous for the
Valgrind gdbserver. This therefore gives the same output as the
unabbreviated command and arguments. If the provided abbreviation is
@ -556,7 +556,7 @@ lines, when given in a shell, will cause the same leak search to be executed
by the process 3145:
<screen><![CDATA[
vgdb --pid=3145 mc.leak_check any full reachable
vgdb --pid=3145 mc.l a f r
vgdb --pid=3145 mc.l f r a
]]></screen></para>
<para>Note that the Valgrind gdbserver automatically continues the

View File

@ -21,11 +21,11 @@ memcheck monitor commands:
mc.check_memory [addressable|defined] <addr> [<len>]
check that <len> (or 1) bytes at <addr> have the given accessibility
and outputs a description of <addr>
mc.leak_check [full*|summary]
[reachable|leakpossible*|definiteleak]
mc.leak_check [full*|summary] [reachable|leakpossible*|definiteleak]
[increased*|changed|any]
* = defaults
Examples: mc.leak_check
mc.leak_check any summary
mc.leak_check summary any
general valgrind monitor commands:
help [debug] : monitor command help. With debug: + debugging commands
@ -57,10 +57,10 @@ memcheck monitor commands:
mc.check_memory [addressable|defined] <addr> [<len>]
check that <len> (or 1) bytes at <addr> have the given accessibility
and outputs a description of <addr>
mc.leak_check [full*|summary]
[reachable|leakpossible*|definiteleak]
mc.leak_check [full*|summary] [reachable|leakpossible*|definiteleak]
[increased*|changed|any]
* = defaults
Examples: mc.leak_check
mc.leak_check any summary
mc.leak_check summary any
monitor command request to kill this process

View File

@ -1,4 +1,6 @@
(action at startup) vgdb me ...
expecting details 10 bytes reachable
10 bytes in 1 blocks are still reachable in loss record ... of ...
at 0x........: malloc (vg_replace_malloc.c:...)

View File

@ -13,13 +13,13 @@ continue
#
# fprintf(stderr, "expecting details 10 bytes reachable\n"); fflush(stderr); breakme();
up
monitor mc.leak_check any reachable full
monitor mc.leak_check full reachable any
continue
# VALGRIND_DO_LEAK_CHECK;
#
# fprintf(stderr, "expecting to have NO details\n"); fflush(stderr);
up
monitor mc.leak_check increased reachable full
monitor mc.leak_check full reachable increased
continue
# VALGRIND_DO_ADDED_LEAK_CHECK;
#
@ -27,7 +27,7 @@ continue
# b21 = malloc (21);
# fprintf(stderr, "expecting details +10 bytes lost, +21 bytes reachable\n"); fflush(stderr); breakme();
up
monitor mc.leak_check increased reachable full
monitor mc.leak_check full reachable increased
continue
# VALGRIND_DO_ADDED_LEAK_CHECK;
#
@ -35,41 +35,41 @@ continue
# b32_33[i] = malloc (32+i);
# fprintf(stderr, "expecting details +65 bytes reachable\n"); fflush(stderr); breakme();
up
monitor mc.leak_check increased reachable full
monitor mc.leak_check full reachable increased
continue
# VALGRIND_DO_ADDED_LEAK_CHECK;
#
# fprintf(stderr, "expecting to have NO details\n"); fflush(stderr); breakme();
up
monitor mc.leak_check increased reachable full
monitor mc.leak_check full reachable increased
continue
# VALGRIND_DO_ADDED_LEAK_CHECK;
#
# b10++;
# fprintf(stderr, "expecting details +10 bytes reachable\n"); fflush(stderr); breakme();
up
monitor mc.leak_check increased reachable full
monitor mc.leak_check full reachable increased
continue
# VALGRIND_DO_ADDED_LEAK_CHECK;
#
# b10--;
# fprintf(stderr, "expecting details -10 bytes reachable, +10 bytes lost\n"); fflush(stderr); breakme();
up
monitor mc.leak_check changed reachable full
monitor mc.leak_check full reachable changed
continue
# VALGRIND_DO_CHANGED_LEAK_CHECK;
#
# b10++;
# fprintf(stderr, "expecting details -10 bytes lost, +10 bytes reachable\n"); fflush(stderr); breakme();
up
monitor mc.leak_check changed reachable full
monitor mc.leak_check full reachable changed
continue
# VALGRIND_DO_CHANGED_LEAK_CHECK;
#
# b32_33[0]--;
# fprintf(stderr, "expecting details 32 (+32) bytes lost, 33 (-32) bytes reachable\n"); fflush(stderr); breakme();
up
monitor mc.leak_check changed reachable full
monitor mc.leak_check full reachable changed
continue
# VALGRIND_DO_CHANGED_LEAK_CHECK;
#

View File

@ -1,5 +1,3 @@
Remote debugging using | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcleak
0x........ in _start () from ...start file...
Breakpoint 1 at 0x........: file leak-delta.c, line 9.
Continuing.
Breakpoint 1, breakme () at leak-delta.c:9

View File

@ -1,8 +1,7 @@
# test the memcheck leak functionality.
prog: ../memcheck/tests/leak-delta
vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcleak -q --leak-check=yes --show-reachable=yes --leak-resolution=high
# temorarily disabled, waiting for leak-delta test program (next patch)
prereq: test -e gdb -a -x ../memcheck/tests/leak-delta
prereq: test -e gdb
stdout_filter: filter_memcheck_monitor
stderr_filter: filter_memcheck_monitor
progB: gdb

View File

@ -1272,23 +1272,27 @@ is:</para>
<sect1 id="mc-manual.monitor-commands" xreflabel="Memcheck Monitor Commands">
<title>Memcheck Monitor Commands</title>
<para>The Memcheck tool provides monitor commands handled by the Valgrind
gdbserver (see <xref linkend="manual-core-adv.gdbserver-commandhandling"/>).
<para>The Memcheck tool provides monitor commands handled by Valgrind's
built-in gdbserver (see <xref linkend="manual-core-adv.gdbserver-commandhandling"/>).
</para>
<itemizedlist>
<listitem>
<para><varname>mc.get_vbits &lt;addr&gt; [&lt;len&gt;]</varname>
outputs the validity bits for the range of &lt;len&gt; (default 1)
bytes at &lt;addr&gt;. The validity of each byte of the range is
given using two hexadecimal digits. These hexadecimal digits are
encoding the validity of each bit of the corresponding byte, using
0 if the bit is valid and 1 if the bit is invalid. In the
following example, 'string10' is an array of 10 characters in
which one byte on two is undefined. If a byte is not addressable,
its validity bits are replaced by __. In the below example, the byte
corresponding to string10[5]
is not addressable.</para>
shows the definedness (V) bits for &lt;len&gt; (default 1) bytes
starting at &lt;addr&gt;. The definedness of each byte in the
range is given using two hexadecimal digits. These hexadecimal
digits encode the validity of each bit of the corresponding byte,
using 0 if the bit is defined and 1 if the bit is undefined.
If a byte is not addressable, its validity bits are replaced
by <varname>__</varname> (a double underscore).
</para>
<para>
In the following example, <varname>string10</varname> is an array
of 10 characters, in which the even numbered bytes are
undefined. In the below example, the byte corresponding
to <varname>string10[5]</varname> is not addressable.
</para>
<programlisting><![CDATA[
(gdb) p &string10
$4 = (char (*)[10]) 0x8049e28
@ -1299,14 +1303,22 @@ ff00ff00 ff__ff00 ff00
</listitem>
<listitem>
<para><varname>mc.make_memory [noaccess|undefined|defined|ifaddressabledefined] &lt;addr&gt; [&lt;len&gt;]</varname>
marks the range of &lt;len&gt; (default 1) bytes at &lt;addr&gt;
with the given accessibility. Marking with 'noaccess' changes the
(A) bits of the range to be not addressable. Marking with
'undefined' or 'defined' are changing the definedness of the
range. 'ifaddressabledefined' marks the range as defined but only
if the range is addressable. In the following example, the first
byte of the 'string10' is marked as defined.
<para><varname>mc.make_memory
[noaccess|undefined|defined|ifaddressabledefined] &lt;addr&gt;
[&lt;len&gt;]</varname> marks the range of &lt;len&gt; (default 1)
bytes at &lt;addr&gt; as having the given status. Parameter
<varname>noaccess</varname> marks the range as non-accessible, so
Memcheck will report an error on any access to it.
<varname>undefined</varname> or <varname>defined</varname> mark
the area as accessible, but Memcheck regards the bytes in it
respectively as having undefined or defined values.
<varname>ifaddressabledefined</varname> marks as defined, bytes in
the range which are already addressible, but makes no change to
the status of bytes in the range which are not addressible.</para>
<para>
In the following example, the first byte of the
<varname>string10</varname> is marked as defined:
</para>
<programlisting><![CDATA[
(gdb) monitor mc.make_memory defined 0x8049e28 1
@ -1319,10 +1331,11 @@ ff00ff00 ff__ff00 ff00
<listitem>
<para><varname>mc.check_memory [addressable|defined] &lt;addr&gt;
[&lt;len&gt;]</varname> checks that the range of &lt;len&gt;
(default 1) bytes at &lt;addr&gt; has the given accessibility. It
then outputs a description of &lt;addr&gt;. In the below case, a
detailed description is given as the option --read-var-info=yes
was used to start Valgrind.
(default 1) bytes at &lt;addr&gt; has the specified accessibility. It
then outputs a description of &lt;addr&gt;. In the following example, a
detailed description is given available because
the option <option>--read-var-info=yes</option>
was given Valgrind at startup:
</para>
<programlisting><![CDATA[
(gdb) monitor mc.check_memory defined 0x8049e28 1
@ -1334,58 +1347,93 @@ Address 0x8049E28 len 1 defined
</listitem>
<listitem>
<para><varname>mc.leak_check
[full*|summary] [reachable|leakpossible*|definiteleak]</varname>
starts a leak checking. The * in the arguments above indicates the
default value. </para>
<para><varname>mc.leak_check [full*|summary]
[reachable|leakpossible*|definiteleak]
[increased*|changed|any]
</varname>
performs a leak check. The <varname>*</varname> in the arguments
indicates the default value. </para>
<para> If the first argument is 'summary', only a summary of
the leak search is given.
<para> If the first argument is <varname>summary</varname>, only a
summary of the leak search is given; otherwise a full leak report
is produced. A full leak report gives detailed information for
each leak: the stack trace where the leaked blocks were allocated,
the number of blocks leaked and their total size. When a full
report is requested, the next two arguments further specify what
kind of leaks to report. A leak's details are shown if they match
both the second and third argument.
</para>
<para>The second argument controls which entries are output
for a 'full' leak search. The value 'definiteleak' indicates to
output only the definitely leaked blocks. The value 'leakpossible'
will output in addition the possibly leaked blocks. The value
'reachable' will output all blocks (reachable, possibly leaked,
definitely leaked).
<para>The second argument controls what kind of blocks are shown for
a <varname>full</varname> leak search. The
value <varname>definiteleak</varname> specifies that only
definitely leaked blocks should be shown. The
value <varname>leakpossible</varname> will also show possibly
leaked blocks (those for which only an interior pointer was
found). The value
<varname>reachable</varname> will show all block categories
(reachable, possibly leaked, definitely leaked).
</para>
<para>The below is an example of using the mc.leak_check monitor
command on the leak-cases Memcheck regression tests.</para>
<para>The third argument controls what kinds of changes are shown
for a <varname>full</varname> leak search. The
value <varname>increased</varname> specifies that only block
allocation stacks with an increased number of leaked bytes or
blocks since the previous leak check should be shown. The
value <varname>changed</varname> specifies that allocation stacks
with any change since the previous leak check should be shown.
The value <varname>any</varname> specifies that all leak entries
should be shown, regardless of any increase or decrease. When
If <varname>increased</varname> or <varname>changed</varname> are
specified, the leak report entries will show the delta relative to
the previous leak report.
</para>
<para>The following example shows usage of the
<varname>mc.leak_check monitor</varname> command on
the <varname>memcheck/tests/leak-cases.c</varname> regression
test. The first command outputs one entry having an increase in
the leaked bytes. The second command is the same as the first
command, but uses the abbreviated forms accepted by GDB and the
Valgrind gdbserver. It only outputs the summary information, as
there was no increase since the previous leak search.</para>
<programlisting><![CDATA[
(gdb) monitor mc.leak_check full leakpossible
==14729== 16 bytes in 1 blocks are possibly lost in loss record 13 of 16
(gdb) monitor mc.leak_check full leakpossible increased
==14729== 16 (+16) bytes in 1 (+1) blocks are possibly lost in loss record 13 of 16
==14729== at 0x4006E9E: malloc (vg_replace_malloc.c:236)
==14729== by 0x80484D5: mk (leak-cases.c:52)
==14729== by 0x804855F: f (leak-cases.c:81)
==14729== by 0x80488F5: main (leak-cases.c:107)
==14729==
==14729== LEAK SUMMARY:
==14729== definitely lost: 32 bytes in 2 blocks
==14729== indirectly lost: 16 bytes in 1 blocks
==14729== possibly lost: 32 bytes in 2 blocks
==14729== still reachable: 96 bytes in 6 blocks
==14729== suppressed: 0 bytes in 0 blocks
==14729== definitely lost: 32 (+0) bytes in 2 (+0) blocks
==14729== indirectly lost: 16 (+0) bytes in 1 (+0) blocks
==14729== possibly lost: 32 (+16) bytes in 2 (+1) blocks
==14729== still reachable: 96 (+16) bytes in 6 (+1) blocks
==14729== suppressed: 0 (+0) bytes in 0 (+0) blocks
==14729== Reachable blocks (those to which a pointer was found) are not shown.
==14729== To see them, rerun with: --leak-check=full --show-reachable=yes
==14729== o see them, add 'reachable any' args to mc.leak_check
==14729==
(gdb) mo mc.l
==14729== LEAK SUMMARY:
==14729== definitely lost: 32 bytes in 2 blocks
==14729== indirectly lost: 16 bytes in 1 blocks
==14729== possibly lost: 32 bytes in 2 blocks
==14729== still reachable: 96 bytes in 6 blocks
==14729== suppressed: 0 bytes in 0 blocks
==14729== definitely lost: 32 (+0) bytes in 2 (+0) blocks
==14729== indirectly lost: 16 (+0) bytes in 1 (+0) blocks
==14729== possibly lost: 32 (+0) bytes in 2 (+0) blocks
==14729== still reachable: 96 (+0) bytes in 6 (+0) blocks
==14729== suppressed: 0 (+0) bytes in 0 (+0) blocks
==14729== Reachable blocks (those to which a pointer was found) are not shown.
==14729== To see them, rerun with: --leak-check=full --show-reachable=yes
==14729== To see them, add 'reachable any' args to mc.leak_check
==14729==
(gdb)
]]></programlisting>
<para>Note that when using the Valgrind gdbserver, it is not
needed to rerun with --leak-check=full --show-reachable=yes to see
the reachable blocks. You can obtain the same information without
rerunning by using the gdb command 'monitor mc.leak_check full
reachable' (or, using abbreviation: 'mo mc.l f r').
<para>Note that when using Valgrind's gdbserver, it is not
necessary to rerun
with <option>--leak-check=full</option>
<option>--show-reachable=yes</option> to see the reachable
blocks. You can obtain the same information without rerunning by
using the GDB command <computeroutput>monitor mc.leak_check full
reachable any</computeroutput> (or, using
abbreviation: <computeroutput>mo mc.l f r a</computeroutput>).
</para>
</listitem>
</itemizedlist>
@ -1442,6 +1490,22 @@ arguments.</para>
places in the program's execution. It has no return value.</para>
</listitem>
<listitem>
<para><varname>VALGRIND_DO_ADDED_LEAK_CHECK</varname>: same as
<varname> VALGRIND_DO_LEAK_CHECK</varname> but only shows the
entries for which there was an increase in leaked bytes or leaked
number of blocks since the previous leak search. It has no return
value.</para>
</listitem>
<listitem>
<para><varname>VALGRIND_DO_CHANGED_LEAK_CHECK</varname>: same as
<varname>VALGRIND_DO_LEAK_CHECK</varname> but only shows the
entries for which there was an increase or decrease in leaked
bytes or leaked number of blocks since the previous leak search. 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

View File

@ -432,6 +432,20 @@ static void mc_pp_origin ( ExeContext* ec, UInt okind )
}
}
char * MC_(snprintf_delta) (char * buf, Int size,
SizeT current_val, SizeT old_val,
LeakCheckDeltaMode delta_mode)
{
if (delta_mode == LCD_Any)
buf[0] = '\0';
else if (current_val >= old_val)
VG_(snprintf) (buf, size, " (+%'lu)", current_val - old_val);
else
VG_(snprintf) (buf, size, " (-%'lu)", old_val - current_val);
return buf;
}
void MC_(pp_Error) ( Error* err )
{
const Bool xml = VG_(clo_xml); /* a shorthand */
@ -702,15 +716,41 @@ void MC_(pp_Error) ( Error* err )
UInt n_this_record = extra->Err.Leak.n_this_record;
UInt n_total_records = extra->Err.Leak.n_total_records;
LossRecord* lr = extra->Err.Leak.lr;
// char arrays to produce the indication of increase/decrease in case
// of delta_mode != LC_Any
char d_bytes[20];
char d_direct_bytes[20];
char d_indirect_bytes[20];
char d_num_blocks[20];
MC_(snprintf_delta) (d_bytes, 20,
lr->szB + lr->indirect_szB,
lr->old_szB + lr->old_indirect_szB,
MC_(detect_memory_leaks_last_delta_mode));
MC_(snprintf_delta) (d_direct_bytes, 20,
lr->szB,
lr->old_szB,
MC_(detect_memory_leaks_last_delta_mode));
MC_(snprintf_delta) (d_indirect_bytes, 20,
lr->indirect_szB,
lr->old_indirect_szB,
MC_(detect_memory_leaks_last_delta_mode));
MC_(snprintf_delta) (d_num_blocks, 20,
(SizeT) lr->num_blocks,
(SizeT) lr->old_num_blocks,
MC_(detect_memory_leaks_last_delta_mode));
if (xml) {
emit(" <kind>%s</kind>\n", xml_leak_kind(lr->key.state));
if (lr->indirect_szB > 0) {
emit( " <xwhat>\n" );
emit( " <text>%'lu (%'lu direct, %'lu indirect) bytes "
"in %'u blocks"
emit( " <text>%'lu%s (%'lu%s direct, %'lu%s indirect) bytes "
"in %'u%s blocks"
" are %s in loss record %'u of %'u</text>\n",
lr->szB + lr->indirect_szB, lr->szB, lr->indirect_szB,
lr->num_blocks,
lr->szB + lr->indirect_szB, d_bytes,
lr->szB, d_direct_bytes,
lr->indirect_szB, d_indirect_bytes,
lr->num_blocks, d_num_blocks,
str_leak_lossmode(lr->key.state),
n_this_record, n_total_records );
// Nb: don't put commas in these XML numbers
@ -720,9 +760,10 @@ void MC_(pp_Error) ( Error* err )
emit( " </xwhat>\n" );
} else {
emit( " <xwhat>\n" );
emit( " <text>%'lu bytes in %'u blocks"
emit( " <text>%'lu%s bytes in %'u%s blocks"
" are %s in loss record %'u of %'u</text>\n",
lr->szB, lr->num_blocks,
lr->szB, d_direct_bytes,
lr->num_blocks, d_num_blocks,
str_leak_lossmode(lr->key.state),
n_this_record, n_total_records );
emit( " <leakedbytes>%ld</leakedbytes>\n", lr->szB);
@ -733,16 +774,21 @@ void MC_(pp_Error) ( Error* err )
} else { /* ! if (xml) */
if (lr->indirect_szB > 0) {
emit(
"%'lu (%'lu direct, %'lu indirect) bytes in %'u blocks"
"%'lu%s (%'lu%s direct, %'lu%s indirect) bytes in %'u%s blocks"
" are %s in loss record %'u of %'u\n",
lr->szB + lr->indirect_szB, lr->szB, lr->indirect_szB,
lr->num_blocks, str_leak_lossmode(lr->key.state),
lr->szB + lr->indirect_szB, d_bytes,
lr->szB, d_direct_bytes,
lr->indirect_szB, d_indirect_bytes,
lr->num_blocks, d_num_blocks,
str_leak_lossmode(lr->key.state),
n_this_record, n_total_records
);
} else {
emit(
"%'lu bytes in %'u blocks are %s in loss record %'u of %'u\n",
lr->szB, lr->num_blocks, str_leak_lossmode(lr->key.state),
"%'lu%s bytes in %'u%s blocks are %s in loss record %'u of %'u\n",
lr->szB, d_direct_bytes,
lr->num_blocks, d_num_blocks,
str_leak_lossmode(lr->key.state),
n_this_record, n_total_records
);
}

View File

@ -270,6 +270,15 @@ typedef
}
LeakCheckMode;
typedef
enum {
LCD_Any, // output all loss records, whatever the delta
LCD_Increased, // output loss records with an increase in size or blocks
LCD_Changed, // output loss records with an increase or
//decrease in size or blocks
}
LeakCheckDeltaMode;
/* When a LossRecord is put into an OSet, these elements represent the key. */
typedef
struct _LossRecordKey {
@ -287,10 +296,33 @@ typedef
SizeT szB; // Sum of all MC_Chunk.szB values.
SizeT indirect_szB; // Sum of all LC_Extra.indirect_szB values.
UInt num_blocks; // Number of blocks represented by the record.
SizeT old_szB; // old_* values are the values found during the
SizeT old_indirect_szB; // previous leak search. old_* values are used to
UInt old_num_blocks; // output only the changed/new loss records
}
LossRecord;
void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckMode mode );
typedef
struct _LeakCheckParams {
LeakCheckMode mode;
Bool show_reachable;
Bool show_possibly_lost;
LeakCheckDeltaMode deltamode;
Bool requested_by_monitor_command; // True when requested by gdb/vgdb.
}
LeakCheckParams;
void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckParams lcp);
// maintains the lcp.deltamode given in the last call to detect_memory_leaks
extern LeakCheckDeltaMode MC_(detect_memory_leaks_last_delta_mode);
// if delta_mode == LC_Any, prints in buf an empty string
// otherwise prints a delta in the layout " (+%'lu)" or " (-%'lu)"
extern char * MC_(snprintf_delta) (char * buf, Int size,
SizeT current_val, SizeT old_val,
LeakCheckDeltaMode delta_mode);
Bool MC_(is_valid_aligned_word) ( Addr a );
Bool MC_(is_within_valid_secondary) ( Addr a );

View File

@ -434,6 +434,16 @@ typedef
static MC_Chunk** lc_chunks;
// How many chunks we're dealing with.
static Int lc_n_chunks;
// chunks will be converted and merged in loss record, maintained in lr_table
// lr_table elements are kept from one leak_search to another to implement
// the "print new/changed leaks" client request
static OSet* lr_table;
// DeltaMode used the last time we called detect_memory_leaks.
// The recorded leak errors must be output using a logic based on this delta_mode.
// The below avoids replicating the delta_mode in each LossRecord.
LeakCheckDeltaMode MC_(detect_memory_leaks_last_delta_mode);
// This has the same number of entries as lc_chunks, and each entry
// in lc_chunks corresponds with the entry here (ie. lc_chunks[i] and
@ -770,20 +780,35 @@ static Int cmp_LossRecords(void* va, void* vb)
return 0;
}
static void print_results(ThreadId tid, Bool is_full_check)
static void print_results(ThreadId tid, LeakCheckParams lcp)
{
Int i, n_lossrecords;
OSet* lr_table;
LossRecord** lr_array;
LossRecord* lr;
Bool is_suppressed;
SizeT old_bytes_leaked = MC_(bytes_leaked); /* to report delta in summary */
SizeT old_bytes_indirect = MC_(bytes_indirect);
SizeT old_bytes_dubious = MC_(bytes_dubious);
SizeT old_bytes_reachable = MC_(bytes_reachable);
SizeT old_bytes_suppressed = MC_(bytes_suppressed);
SizeT old_blocks_leaked = MC_(blocks_leaked);
SizeT old_blocks_indirect = MC_(blocks_indirect);
SizeT old_blocks_dubious = MC_(blocks_dubious);
SizeT old_blocks_reachable = MC_(blocks_reachable);
SizeT old_blocks_suppressed = MC_(blocks_suppressed);
if (lr_table == NULL)
// Create the lr_table, which holds the loss records.
// If the lr_table already exists, it means it contains
// loss_records from the previous leak search. The old_*
// values in these records are used to implement the
// leak check delta mode
lr_table =
VG_(OSetGen_Create)(offsetof(LossRecord, key),
cmp_LossRecordKey_LossRecord,
VG_(malloc), "mc.pr.1",
VG_(free));
// Create the lr_table, which holds the loss records.
lr_table =
VG_(OSetGen_Create)(offsetof(LossRecord, key),
cmp_LossRecordKey_LossRecord,
VG_(malloc), "mc.pr.1",
VG_(free));
// Convert the chunks into loss records, merging them where appropriate.
for (i = 0; i < lc_n_chunks; i++) {
@ -810,6 +835,9 @@ static void print_results(ThreadId tid, Bool is_full_check)
lr->szB = ch->szB;
lr->indirect_szB = ex->indirect_szB;
lr->num_blocks = 1;
lr->old_szB = 0;
lr->old_indirect_szB = 0;
lr->old_num_blocks = 0;
VG_(OSetGen_Insert)(lr_table, lr);
}
}
@ -837,7 +865,7 @@ static void print_results(ThreadId tid, Bool is_full_check)
// Print the loss records (in size order) and collect summary stats.
for (i = 0; i < n_lossrecords; i++) {
Bool count_as_error, print_record;
Bool count_as_error, print_record, delta_considered;
// Rules for printing:
// - We don't show suppressed loss records ever (and that's controlled
// within the error manager).
@ -851,18 +879,37 @@ static void print_results(ThreadId tid, Bool is_full_check)
// includes indirectly lost blocks!
//
lr = lr_array[i];
print_record = is_full_check &&
( MC_(clo_show_reachable) ||
switch (lcp.deltamode) {
case LCD_Any:
delta_considered = lr->num_blocks > 0;
break;
case LCD_Increased:
delta_considered
= lr_array[i]->szB > lr_array[i]->old_szB
|| lr_array[i]->indirect_szB > lr_array[i]->old_indirect_szB
|| lr->num_blocks > lr->old_num_blocks;
break;
case LCD_Changed:
delta_considered = lr_array[i]->szB != lr_array[i]->old_szB
|| lr_array[i]->indirect_szB != lr_array[i]->old_indirect_szB
|| lr->num_blocks != lr->old_num_blocks;
break;
default:
tl_assert(0);
}
print_record = lcp.mode == LC_Full && delta_considered &&
( lcp.show_reachable ||
Unreached == lr->key.state ||
( MC_(clo_show_possibly_lost) &&
( lcp.show_possibly_lost &&
Possible == lr->key.state ) );
// We don't count a leaks as errors with --leak-check=summary.
// We don't count a leaks as errors with lcp.mode==LC_Summary.
// Otherwise you can get high error counts with few or no error
// messages, which can be confusing. Also, you could argue that
// indirect leaks should be counted as errors, but it seems better to
// make the counting criteria similar to the printing criteria. So we
// don't count them.
count_as_error = is_full_check &&
count_as_error = lcp.mode == LC_Full && delta_considered &&
( Unreached == lr->key.state ||
Possible == lr->key.state );
is_suppressed =
@ -894,31 +941,74 @@ static void print_results(ThreadId tid, Bool is_full_check)
}
}
for (i = 0; i < n_lossrecords; i++)
{
if (lr->num_blocks == 0)
// remove from lr_table the old loss_records with 0 bytes found
VG_(OSetGen_Remove) (lr_table, &lr_array[i]->key);
else
{
// move the leak sizes to old_* and zero the current sizes
// for next leak search
lr_array[i]->old_szB = lr_array[i]->szB;
lr_array[i]->old_indirect_szB = lr_array[i]->indirect_szB;
lr_array[i]->old_num_blocks = lr_array[i]->num_blocks;
lr_array[i]->szB = 0;
lr_array[i]->indirect_szB = 0;
lr_array[i]->num_blocks = 0;
}
}
VG_(free)(lr_array);
if (VG_(clo_verbosity) > 0 && !VG_(clo_xml)) {
char d_bytes[20];
char d_blocks[20];
VG_(umsg)("LEAK SUMMARY:\n");
VG_(umsg)(" definitely lost: %'lu bytes in %'lu blocks\n",
MC_(bytes_leaked), MC_(blocks_leaked) );
VG_(umsg)(" indirectly lost: %'lu bytes in %'lu blocks\n",
MC_(bytes_indirect), MC_(blocks_indirect) );
VG_(umsg)(" possibly lost: %'lu bytes in %'lu blocks\n",
MC_(bytes_dubious), MC_(blocks_dubious) );
VG_(umsg)(" still reachable: %'lu bytes in %'lu blocks\n",
MC_(bytes_reachable), MC_(blocks_reachable) );
VG_(umsg)(" suppressed: %'lu bytes in %'lu blocks\n",
MC_(bytes_suppressed), MC_(blocks_suppressed) );
if (!is_full_check &&
VG_(umsg)(" definitely lost: %'lu%s bytes in %'lu%s blocks\n",
MC_(bytes_leaked),
MC_(snprintf_delta) (d_bytes, 20, MC_(bytes_leaked), old_bytes_leaked, lcp.deltamode),
MC_(blocks_leaked),
MC_(snprintf_delta) (d_blocks, 20, MC_(blocks_leaked), old_blocks_leaked, lcp.deltamode));
VG_(umsg)(" indirectly lost: %'lu%s bytes in %'lu%s blocks\n",
MC_(bytes_indirect),
MC_(snprintf_delta) (d_bytes, 20, MC_(bytes_indirect), old_bytes_indirect, lcp.deltamode),
MC_(blocks_indirect),
MC_(snprintf_delta) (d_blocks, 20, MC_(blocks_indirect), old_blocks_indirect, lcp.deltamode) );
VG_(umsg)(" possibly lost: %'lu%s bytes in %'lu%s blocks\n",
MC_(bytes_dubious),
MC_(snprintf_delta) (d_bytes, 20, MC_(bytes_dubious), old_bytes_dubious, lcp.deltamode),
MC_(blocks_dubious),
MC_(snprintf_delta) (d_blocks, 20, MC_(blocks_dubious), old_blocks_dubious, lcp.deltamode) );
VG_(umsg)(" still reachable: %'lu%s bytes in %'lu%s blocks\n",
MC_(bytes_reachable),
MC_(snprintf_delta) (d_bytes, 20, MC_(bytes_reachable), old_bytes_reachable, lcp.deltamode),
MC_(blocks_reachable),
MC_(snprintf_delta) (d_blocks, 20, MC_(blocks_reachable), old_blocks_reachable, lcp.deltamode) );
VG_(umsg)(" suppressed: %'lu%s bytes in %'lu%s blocks\n",
MC_(bytes_suppressed),
MC_(snprintf_delta) (d_bytes, 20, MC_(bytes_suppressed), old_bytes_suppressed, lcp.deltamode),
MC_(blocks_suppressed),
MC_(snprintf_delta) (d_blocks, 20, MC_(blocks_suppressed), old_blocks_suppressed, lcp.deltamode) );
if (lcp.mode != LC_Full &&
(MC_(blocks_leaked) + MC_(blocks_indirect) +
MC_(blocks_dubious) + MC_(blocks_reachable)) > 0) {
VG_(umsg)("Rerun with --leak-check=full to see details "
"of leaked memory\n");
if (lcp.requested_by_monitor_command)
VG_(umsg)("To see details of leaked memory, give 'full' arg to mc.leak_check\n");
else
VG_(umsg)("Rerun with --leak-check=full to see details "
"of leaked memory\n");
}
if (is_full_check &&
MC_(blocks_reachable) > 0 && !MC_(clo_show_reachable))
if (lcp.mode == LC_Full &&
MC_(blocks_reachable) > 0 && !lcp.show_reachable)
{
VG_(umsg)("Reachable blocks (those to which a pointer "
"was found) are not shown.\n");
VG_(umsg)("To see them, rerun with: --leak-check=full "
"--show-reachable=yes\n");
if (lcp.requested_by_monitor_command)
VG_(umsg)("To see them, add 'reachable any' args to mc.leak_check\n");
else
VG_(umsg)("To see them, rerun with: --leak-check=full "
"--show-reachable=yes\n");
}
VG_(umsg)("\n");
}
@ -928,16 +1018,26 @@ static void print_results(ThreadId tid, Bool is_full_check)
/*--- Top-level entry point. ---*/
/*------------------------------------------------------------*/
void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckMode mode )
void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckParams lcp)
{
Int i, j;
tl_assert(mode != LC_Off);
tl_assert(lcp.mode != LC_Off);
MC_(detect_memory_leaks_last_delta_mode) = lcp.deltamode;
// Get the chunks, stop if there were none.
lc_chunks = find_active_chunks(&lc_n_chunks);
if (lc_n_chunks == 0) {
tl_assert(lc_chunks == NULL);
if (lr_table != NULL) {
// forget the previous recorded LossRecords as next leak search will in any case
// just create new leaks.
// Maybe it would be better to rather call print_result ?
// (at least when leak decrease are requested)
// This will then output all LossRecords with a size decreasing to 0
VG_(OSetGen_Destroy) (lr_table);
}
if (VG_(clo_verbosity) >= 1 && !VG_(clo_xml)) {
VG_(umsg)("All heap blocks were freed -- no leaks are possible\n");
VG_(umsg)("\n");
@ -1124,7 +1224,7 @@ void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckMode mode )
}
}
print_results( tid, ( mode == LC_Full ? True : False ) );
print_results( tid, lcp);
VG_(free) ( lc_chunks );
VG_(free) ( lc_extras );

View File

@ -4945,11 +4945,11 @@ static void print_monitor_help ( void )
" mc.check_memory [addressable|defined] <addr> [<len>]\n"
" check that <len> (or 1) bytes at <addr> have the given accessibility\n"
" and outputs a description of <addr>\n"
" mc.leak_check [full*|summary]\n"
" [reachable|leakpossible*|definiteleak]\n"
" mc.leak_check [full*|summary] [reachable|leakpossible*|definiteleak]\n"
" [increased*|changed|any]\n"
" * = defaults\n"
" Examples: mc.leak_check\n"
" mc.leak_check any summary\n"
" mc.leak_check summary any\n"
"\n");
}
@ -5013,40 +5013,50 @@ static Bool handle_gdb_monitor_command (ThreadId tid, Char *req)
}
case 2: { /* mc.leak_check */
Int err = 0;
Bool save_clo_show_reachable = MC_(clo_show_reachable);
Bool save_clo_show_possibly_lost = MC_(clo_show_possibly_lost);
LeakCheckParams lcp;
Char* kw;
LeakCheckMode mode;
MC_(clo_show_reachable) = False;
mode = LC_Full;
lcp.mode = LC_Full;
lcp.show_reachable = False;
lcp.show_possibly_lost = True;
lcp.deltamode = LCD_Increased;
lcp.requested_by_monitor_command = True;
for (kw = VG_(strtok_r) (NULL, " ", &ssaveptr);
kw != NULL;
kw = VG_(strtok_r) (NULL, " ", &ssaveptr)) {
switch (VG_(keyword_id)
("full summary "
"reachable leakpossible definiteleak",
"reachable leakpossible definiteleak "
"increased changed any",
kw, kwd_report_all)) {
case -2: err++; break;
case -1: err++; break;
case 0: mode = LC_Full; break;
case 1: mode = LC_Summary; break;
case 2: MC_(clo_show_reachable) = True;
MC_(clo_show_possibly_lost) = True; break;
case 3: MC_(clo_show_reachable) = False;
MC_(clo_show_possibly_lost) = True; break;
case 4: MC_(clo_show_reachable) = False;
MC_(clo_show_possibly_lost) = False; break;
default: tl_assert (0);
case 0: /* full */
lcp.mode = LC_Full; break;
case 1: /* summary */
lcp.mode = LC_Summary; break;
case 2: /* reachable */
lcp.show_reachable = True;
lcp.show_possibly_lost = True; break;
case 3: /* leakpossible */
lcp.show_reachable = False;
lcp.show_possibly_lost = True; break;
case 4: /* definiteleak */
lcp.show_reachable = False;
lcp.show_possibly_lost = False; break;
case 5: /* increased */
lcp.deltamode = LCD_Increased; break;
case 6: /* changed */
lcp.deltamode = LCD_Changed; break;
case 7: /* any */
lcp.deltamode = LCD_Any; break;
default:
tl_assert (0);
}
}
if (!err)
MC_(detect_memory_leaks)(tid, mode);
MC_(clo_show_reachable) = save_clo_show_reachable;
MC_(clo_show_possibly_lost) = save_clo_show_possibly_lost;
MC_(detect_memory_leaks)(tid, lcp);
return True;
}
@ -5189,10 +5199,40 @@ static Bool mc_handle_client_request ( ThreadId tid, UWord* arg, UWord* ret )
break;
}
case VG_USERREQ__DO_LEAK_CHECK:
MC_(detect_memory_leaks)(tid, arg[1] ? LC_Summary : LC_Full);
case VG_USERREQ__DO_LEAK_CHECK: {
LeakCheckParams lcp;
if (arg[1] == 0)
lcp.mode = LC_Full;
else if (arg[1] == 1)
lcp.mode = LC_Summary;
else {
VG_(message)(Vg_UserMsg,
"Warning: unknown memcheck leak search mode\n");
lcp.mode = LC_Full;
}
lcp.show_reachable = MC_(clo_show_reachable);
lcp.show_possibly_lost = MC_(clo_show_possibly_lost);
if (arg[2] == 0)
lcp.deltamode = LCD_Any;
else if (arg[2] == 1)
lcp.deltamode = LCD_Increased;
else if (arg[2] == 2)
lcp.deltamode = LCD_Changed;
else {
VG_(message)
(Vg_UserMsg,
"Warning: unknown memcheck leak search deltamode\n");
lcp.deltamode = LCD_Any;
}
lcp.requested_by_monitor_command = False;
MC_(detect_memory_leaks)(tid, lcp);
*ret = 0; /* return value is meaningless */
break;
}
case VG_USERREQ__MAKE_MEM_NOACCESS:
MC_(make_mem_noaccess) ( arg[1], arg[2] );
@ -5854,7 +5894,13 @@ static void mc_fini ( Int exitcode )
MC_(print_malloc_stats)();
if (MC_(clo_leak_check) != LC_Off) {
MC_(detect_memory_leaks)(1/*bogus ThreadId*/, MC_(clo_leak_check));
LeakCheckParams lcp;
lcp.mode = MC_(clo_leak_check);
lcp.show_reachable = MC_(clo_show_reachable);
lcp.show_possibly_lost = MC_(clo_show_possibly_lost);
lcp.deltamode = LCD_Any;
lcp.requested_by_monitor_command = False;
MC_(detect_memory_leaks)(1/*bogus ThreadId*/, lcp);
} else {
if (VG_(clo_verbosity) == 1 && !VG_(clo_xml)) {
VG_(umsg)(

View File

@ -188,6 +188,26 @@ typedef
VG_USERREQ__DO_LEAK_CHECK, \
0, 0, 0, 0, 0)
/* Same as VALGRIND_DO_LEAK_CHECK but only showing the entries for
which there was an increase in leaked bytes or leaked nr of blocks
since the previous leak search. */
#define VALGRIND_DO_ADDED_LEAK_CHECK \
{unsigned long _qzz_res; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
VG_USERREQ__DO_LEAK_CHECK, \
0, 1, 0, 0, 0); \
}
/* Same as VALGRIND_DO_ADDED_LEAK_CHECK but showing entries with
increased or decreased leaked bytes/blocks since previous leak
search. */
#define VALGRIND_DO_CHANGED_LEAK_CHECK \
{unsigned long _qzz_res; \
VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
VG_USERREQ__DO_LEAK_CHECK, \
0, 2, 0, 0, 0); \
}
/* Do a summary memory leak check (like --leak-check=summary) mid-execution. */
#define VALGRIND_DO_QUICK_LEAK_CHECK \
VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \

View File

@ -88,6 +88,7 @@ EXTRA_DIST = \
leak-cases-possible.vgtest leak-cases-possible.stderr.exp \
leak-cases-summary.vgtest leak-cases-summary.stderr.exp \
leak-cycle.vgtest leak-cycle.stderr.exp \
leak-delta.vgtest leak-delta.stderr.exp \
leak-pool-0.vgtest leak-pool-0.stderr.exp \
leak-pool-1.vgtest leak-pool-1.stderr.exp \
leak-pool-2.vgtest leak-pool-2.stderr.exp \
@ -219,6 +220,7 @@ check_PROGRAMS = \
leak-0 \
leak-cases \
leak-cycle \
leak-delta \
leak-pool \
leak-tree \
long_namespace_xml \

View File

@ -0,0 +1,68 @@
#include <stdio.h>
#include <stdlib.h>
#include "../memcheck.h"
#include "leak.h"
char *b10;
char *b21;
char *b32_33[2];
static void breakme() {};
void f(void)
{
int i;
b10 = malloc (10);
fprintf(stderr, "expecting details 10 bytes reachable\n"); fflush(stderr); breakme();
VALGRIND_DO_LEAK_CHECK;
fprintf(stderr, "expecting to have NO details\n"); fflush(stderr); breakme();
VALGRIND_DO_ADDED_LEAK_CHECK;
b10--; // lose b10
b21 = malloc (21);
fprintf(stderr, "expecting details +10 bytes lost, +21 bytes reachable\n"); fflush(stderr); breakme();
VALGRIND_DO_ADDED_LEAK_CHECK;
for (i = 0; i < 2; i ++)
b32_33[i] = malloc (32+i);
fprintf(stderr, "expecting details +65 bytes reachable\n"); fflush(stderr); breakme();
VALGRIND_DO_ADDED_LEAK_CHECK;
fprintf(stderr, "expecting to have NO details\n"); fflush(stderr); breakme();
VALGRIND_DO_ADDED_LEAK_CHECK;
b10++;
fprintf(stderr, "expecting details +10 bytes reachable\n"); fflush(stderr); breakme();
VALGRIND_DO_ADDED_LEAK_CHECK;
b10--;
fprintf(stderr, "expecting details -10 bytes reachable, +10 bytes lost\n"); fflush(stderr); breakme();
VALGRIND_DO_CHANGED_LEAK_CHECK;
b10++;
fprintf(stderr, "expecting details -10 bytes lost, +10 bytes reachable\n"); fflush(stderr); breakme();
VALGRIND_DO_CHANGED_LEAK_CHECK;
b32_33[0]--;
fprintf(stderr, "expecting details 32 (+32) bytes lost, 33 (-32) bytes reachable\n"); fflush(stderr); breakme();
VALGRIND_DO_CHANGED_LEAK_CHECK;
fprintf(stderr, "finished\n");
}
int main(void)
{
DECLARE_LEAK_COUNTERS;
GET_INITIAL_LEAK_COUNTS;
f(); // see leak-cases.c
GET_FINAL_LEAK_COUNTS;
PRINT_LEAK_COUNTS(stderr);
return 0;
}

View File

@ -0,0 +1,89 @@
expecting details 10 bytes reachable
10 bytes in 1 blocks are still reachable in loss record ... of ...
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: f (leak-delta.c:14)
by 0x........: main (leak-delta.c:60)
expecting to have NO details
expecting details +10 bytes lost, +21 bytes reachable
10 (+10) bytes in 1 (+1) blocks are definitely lost in loss record ... of ...
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: f (leak-delta.c:14)
by 0x........: main (leak-delta.c:60)
21 (+21) bytes in 1 (+1) blocks are still reachable in loss record ... of ...
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: f (leak-delta.c:23)
by 0x........: main (leak-delta.c:60)
expecting details +65 bytes reachable
65 (+65) bytes in 2 (+2) blocks are still reachable in loss record ... of ...
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: f (leak-delta.c:28)
by 0x........: main (leak-delta.c:60)
expecting to have NO details
expecting details +10 bytes reachable
10 (+10) bytes in 1 (+1) blocks are still reachable in loss record ... of ...
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: f (leak-delta.c:14)
by 0x........: main (leak-delta.c:60)
expecting details -10 bytes reachable, +10 bytes lost
0 (-10) bytes in 0 (-1) blocks are still reachable in loss record ... of ...
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: f (leak-delta.c:14)
by 0x........: main (leak-delta.c:60)
10 (+10) bytes in 1 (+1) blocks are definitely lost in loss record ... of ...
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: f (leak-delta.c:14)
by 0x........: main (leak-delta.c:60)
expecting details -10 bytes lost, +10 bytes reachable
0 (-10) bytes in 0 (-1) blocks are definitely lost in loss record ... of ...
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: f (leak-delta.c:14)
by 0x........: main (leak-delta.c:60)
10 (+10) bytes in 1 (+1) blocks are still reachable in loss record ... of ...
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: f (leak-delta.c:14)
by 0x........: main (leak-delta.c:60)
expecting details 32 (+32) bytes lost, 33 (-32) bytes reachable
32 (+32) bytes in 1 (+1) blocks are definitely lost in loss record ... of ...
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: f (leak-delta.c:28)
by 0x........: main (leak-delta.c:60)
33 (-32) bytes in 1 (-1) blocks are still reachable in loss record ... of ...
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: f (leak-delta.c:28)
by 0x........: main (leak-delta.c:60)
finished
leaked: 32 bytes in 1 blocks
dubious: 0 bytes in 0 blocks
reachable: 64 bytes in 3 blocks
suppressed: 0 bytes in 0 blocks
10 bytes in 1 blocks are still reachable in loss record ... of ...
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: f (leak-delta.c:14)
by 0x........: main (leak-delta.c:60)
21 bytes in 1 blocks are still reachable in loss record ... of ...
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: f (leak-delta.c:23)
by 0x........: main (leak-delta.c:60)
32 bytes in 1 blocks are definitely lost in loss record ... of ...
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: f (leak-delta.c:28)
by 0x........: main (leak-delta.c:60)
33 bytes in 1 blocks are still reachable in loss record ... of ...
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: f (leak-delta.c:28)
by 0x........: main (leak-delta.c:60)

View File

@ -0,0 +1,2 @@
prog: leak-delta
vgopts: -q --leak-check=yes --show-reachable=yes --leak-resolution=high