summaryrefslogtreecommitdiff
path: root/memcheck
diff options
context:
space:
mode:
authorsewardj <sewardj@a5019735-40e9-0310-863c-91ae7b9d1cf9>2011-06-26 12:41:33 +0000
committersewardj <sewardj@a5019735-40e9-0310-863c-91ae7b9d1cf9>2011-06-26 12:41:33 +0000
commitc8bd1df6c23e2409512e1e50616e7dc3bae501a2 (patch)
treec4b1b3fb9426e3bb6f149495f412777cc34c35bb /memcheck
parentb2572b52b37d56d302408395f7fed9f509423af1 (diff)
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 a5019735-40e9-0310-863c-91ae7b9d1cf9
Diffstat (limited to 'memcheck')
-rw-r--r--memcheck/docs/mc-manual.xml178
-rw-r--r--memcheck/mc_errors.c68
-rw-r--r--memcheck/mc_include.h34
-rw-r--r--memcheck/mc_leakcheck.c168
-rw-r--r--memcheck/mc_main.c98
-rw-r--r--memcheck/memcheck.h20
-rw-r--r--memcheck/tests/Makefile.am2
-rw-r--r--memcheck/tests/leak-delta.c68
-rw-r--r--memcheck/tests/leak-delta.stderr.exp89
-rw-r--r--memcheck/tests/leak-delta.vgtest2
10 files changed, 598 insertions, 129 deletions
diff --git a/memcheck/docs/mc-manual.xml b/memcheck/docs/mc-manual.xml
index 8493e319..29c8df09 100644
--- a/memcheck/docs/mc-manual.xml
+++ b/memcheck/docs/mc-manual.xml
@@ -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 <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> If the first argument is 'summary', only a summary of
- the leak search is given.
+ <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 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 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 below is an example of using the mc.leak_check monitor
- command on the leak-cases Memcheck regression tests.</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>
@@ -1443,6 +1491,22 @@ arguments.</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
summary (like <option>--leak-check=summary</option>).
diff --git a/memcheck/mc_errors.c b/memcheck/mc_errors.c
index e0133289..09074c2f 100644
--- a/memcheck/mc_errors.c
+++ b/memcheck/mc_errors.c
@@ -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
);
}
diff --git a/memcheck/mc_include.h b/memcheck/mc_include.h
index d6edbf2d..0b9d345d 100644
--- a/memcheck/mc_include.h
+++ b/memcheck/mc_include.h
@@ -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 );
diff --git a/memcheck/mc_leakcheck.c b/memcheck/mc_leakcheck.c
index 016109c7..41c9b712 100644
--- a/memcheck/mc_leakcheck.c
+++ b/memcheck/mc_leakcheck.c
@@ -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 );
diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c
index 0cdb4a50..24ece12e 100644
--- a/memcheck/mc_main.c
+++ b/memcheck/mc_main.c
@@ -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)(
diff --git a/memcheck/memcheck.h b/memcheck/memcheck.h
index 628511be..19acca23 100644
--- a/memcheck/memcheck.h
+++ b/memcheck/memcheck.h
@@ -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, \
diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am
index 1d54f374..93939a9a 100644
--- a/memcheck/tests/Makefile.am
+++ b/memcheck/tests/Makefile.am
@@ -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 \
diff --git a/memcheck/tests/leak-delta.c b/memcheck/tests/leak-delta.c
new file mode 100644
index 00000000..b95a8feb
--- /dev/null
+++ b/memcheck/tests/leak-delta.c
@@ -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;
+}
diff --git a/memcheck/tests/leak-delta.stderr.exp b/memcheck/tests/leak-delta.stderr.exp
new file mode 100644
index 00000000..ad8eebc1
--- /dev/null
+++ b/memcheck/tests/leak-delta.stderr.exp
@@ -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)
+
diff --git a/memcheck/tests/leak-delta.vgtest b/memcheck/tests/leak-delta.vgtest
new file mode 100644
index 00000000..db86b27e
--- /dev/null
+++ b/memcheck/tests/leak-delta.vgtest
@@ -0,0 +1,2 @@
+prog: leak-delta
+vgopts: -q --leak-check=yes --show-reachable=yes --leak-resolution=high