diff options
author | sewardj <sewardj@a5019735-40e9-0310-863c-91ae7b9d1cf9> | 2011-06-26 12:41:33 +0000 |
---|---|---|
committer | sewardj <sewardj@a5019735-40e9-0310-863c-91ae7b9d1cf9> | 2011-06-26 12:41:33 +0000 |
commit | c8bd1df6c23e2409512e1e50616e7dc3bae501a2 (patch) | |
tree | c4b1b3fb9426e3bb6f149495f412777cc34c35bb /memcheck | |
parent | b2572b52b37d56d302408395f7fed9f509423af1 (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.xml | 178 | ||||
-rw-r--r-- | memcheck/mc_errors.c | 68 | ||||
-rw-r--r-- | memcheck/mc_include.h | 34 | ||||
-rw-r--r-- | memcheck/mc_leakcheck.c | 168 | ||||
-rw-r--r-- | memcheck/mc_main.c | 98 | ||||
-rw-r--r-- | memcheck/memcheck.h | 20 | ||||
-rw-r--r-- | memcheck/tests/Makefile.am | 2 | ||||
-rw-r--r-- | memcheck/tests/leak-delta.c | 68 | ||||
-rw-r--r-- | memcheck/tests/leak-delta.stderr.exp | 89 | ||||
-rw-r--r-- | memcheck/tests/leak-delta.vgtest | 2 |
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 <addr> [<len>]</varname> - outputs the validity bits for the range of <len> (default 1) - bytes at <addr>. 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 <len> (default 1) bytes + starting at <addr>. 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] <addr> [<len>]</varname> - marks the range of <len> (default 1) bytes at <addr> - 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] <addr> + [<len>]</varname> marks the range of <len> (default 1) + bytes at <addr> 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] <addr> [<len>]</varname> checks that the range of <len> - (default 1) bytes at <addr> has the given accessibility. It - then outputs a description of <addr>. 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 <addr> has the specified accessibility. It + then outputs a description of <addr>. 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 |