From: Julian Seward Date: Sun, 26 Jun 2011 12:41:33 +0000 (+0000) Subject: Memcheck: X-Git-Tag: svn/VALGRIND_3_7_0~399 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5c1e65aa42b3bcf5d2f9da34466457bf88227b36;p=thirdparty%2Fvalgrind.git 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 --- diff --git a/docs/xml/manual-core-adv.xml b/docs/xml/manual-core-adv.xml index c9dd667b3b..9e3f0fc06d 100644 --- a/docs/xml/manual-core-adv.xml +++ b/docs/xml/manual-core-adv.xml @@ -495,12 +495,12 @@ of the Valgrind core monitor commands. Each tool can also provide tool-specific monitor commands. An example of a tool specific monitor command is the Memcheck monitor -command mc.leak_check any full -reachable. This requests a full reporting of the +command mc.leak_check full +reachable any. This requests a full reporting of the allocated memory blocks. To have this leak check executed, use the GDB command: @@ -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: mc.leak_check command can also be typed as: The letters mo are recognised by GDB as being an abbreviation for monitor. So GDB sends the -string mc.l a f r to the Valgrind +string mc.l f r a 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: Note that the Valgrind gdbserver automatically continues the diff --git a/gdbserver_tests/mchelp.stdoutB.exp b/gdbserver_tests/mchelp.stdoutB.exp index 6499f334a7..90a93de2b0 100644 --- a/gdbserver_tests/mchelp.stdoutB.exp +++ b/gdbserver_tests/mchelp.stdoutB.exp @@ -21,11 +21,11 @@ memcheck monitor commands: mc.check_memory [addressable|defined] [] check that (or 1) bytes at have the given accessibility and outputs a description of - 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] [] check that (or 1) bytes at have the given accessibility and outputs a description of - 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 diff --git a/gdbserver_tests/mcleak.stderr.exp b/gdbserver_tests/mcleak.stderr.exp index 734ff4a541..76c4fdbcc3 100644 --- a/gdbserver_tests/mcleak.stderr.exp +++ b/gdbserver_tests/mcleak.stderr.exp @@ -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:...) diff --git a/gdbserver_tests/mcleak.stdinB.gdb b/gdbserver_tests/mcleak.stdinB.gdb index 15c533b4ae..a185f72bc5 100644 --- a/gdbserver_tests/mcleak.stdinB.gdb +++ b/gdbserver_tests/mcleak.stdinB.gdb @@ -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; # diff --git a/gdbserver_tests/mcleak.stdoutB.exp b/gdbserver_tests/mcleak.stdoutB.exp index b3e1aec612..ff66b9da56 100644 --- a/gdbserver_tests/mcleak.stdoutB.exp +++ b/gdbserver_tests/mcleak.stdoutB.exp @@ -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 diff --git a/gdbserver_tests/mcleak.vgtest b/gdbserver_tests/mcleak.vgtest index 71d87bc281..1c410a4727 100644 --- a/gdbserver_tests/mcleak.vgtest +++ b/gdbserver_tests/mcleak.vgtest @@ -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 diff --git a/memcheck/docs/mc-manual.xml b/memcheck/docs/mc-manual.xml index 8493e319c3..29c8df0946 100644 --- a/memcheck/docs/mc-manual.xml +++ b/memcheck/docs/mc-manual.xml @@ -1272,23 +1272,27 @@ is: Memcheck Monitor Commands -The Memcheck tool provides monitor commands handled by the Valgrind -gdbserver (see ). +The Memcheck tool provides monitor commands handled by Valgrind's +built-in gdbserver (see ). mc.get_vbits <addr> [<len>] - 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. + 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 __ (a double underscore). + + + In the following example, string10 is an array + of 10 characters, in which the even numbered bytes are + undefined. In the below example, the byte corresponding + to string10[5] is not addressable. + - mc.make_memory [noaccess|undefined|defined|ifaddressabledefined] <addr> [<len>] - 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. + mc.make_memory + [noaccess|undefined|defined|ifaddressabledefined] <addr> + [<len>] marks the range of <len> (default 1) + bytes at <addr> as having the given status. Parameter + noaccess marks the range as non-accessible, so + Memcheck will report an error on any access to it. + undefined or defined mark + the area as accessible, but Memcheck regards the bytes in it + respectively as having undefined or defined values. + ifaddressabledefined 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. + + + In the following example, the first byte of the + string10 is marked as defined: mc.check_memory [addressable|defined] <addr> [<len>] 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 + was given Valgrind at startup: - mc.leak_check - [full*|summary] [reachable|leakpossible*|definiteleak] - starts a leak checking. The * in the arguments above indicates the - default value. + mc.leak_check [full*|summary] + [reachable|leakpossible*|definiteleak] + [increased*|changed|any] + + performs a leak check. The * in the arguments + indicates the default value. + + If the first argument is summary, 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. + - If the first argument is 'summary', only a summary of - the leak search is given. + The second argument controls what kind of blocks are shown for + a full leak search. The + value definiteleak specifies that only + definitely leaked blocks should be shown. The + value leakpossible will also show possibly + leaked blocks (those for which only an interior pointer was + found). The value + reachable will show all block categories + (reachable, possibly leaked, definitely leaked). - 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). + The third argument controls what kinds of changes are shown + for a full leak search. The + value increased 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 changed specifies that allocation stacks + with any change since the previous leak check should be shown. + The value any specifies that all leak entries + should be shown, regardless of any increase or decrease. When + If increased or changed are + specified, the leak report entries will show the delta relative to + the previous leak report. - The below is an example of using the mc.leak_check monitor - command on the leak-cases Memcheck regression tests. + + The following example shows usage of the + mc.leak_check monitor command on + the memcheck/tests/leak-cases.c 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. - 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'). + Note that when using Valgrind's gdbserver, it is not + necessary to rerun + with + to see the reachable + blocks. You can obtain the same information without rerunning by + using the GDB command monitor mc.leak_check full + reachable any (or, using + abbreviation: mo mc.l f r a). @@ -1442,6 +1490,22 @@ arguments. places in the program's execution. It has no return value. + + VALGRIND_DO_ADDED_LEAK_CHECK: same as + VALGRIND_DO_LEAK_CHECK 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. + + + + VALGRIND_DO_CHANGED_LEAK_CHECK: same as + VALGRIND_DO_LEAK_CHECK 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. + + VALGRIND_DO_QUICK_LEAK_CHECK: like VALGRIND_DO_LEAK_CHECK, except it produces only a leak diff --git a/memcheck/mc_errors.c b/memcheck/mc_errors.c index e01332896f..09074c2fa4 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(" %s\n", xml_leak_kind(lr->key.state)); if (lr->indirect_szB > 0) { emit( " \n" ); - emit( " %'lu (%'lu direct, %'lu indirect) bytes " - "in %'u blocks" + emit( " %'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, + 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( " \n" ); } else { emit( " \n" ); - emit( " %'lu bytes in %'u blocks" + emit( " %'lu%s bytes in %'u%s blocks" " are %s in loss record %'u of %'u\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( " %ld\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 d6edbf2d7d..0b9d345dfb 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 016109c75a..41c9b71208 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 0cdb4a509a..24ece12e12 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] []\n" " check that (or 1) bytes at have the given accessibility\n" " and outputs a description of \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 628511be0e..19acca23f2 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 1d54f374fd..93939a9a02 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 0000000000..b95a8feba0 --- /dev/null +++ b/memcheck/tests/leak-delta.c @@ -0,0 +1,68 @@ +#include +#include +#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 0000000000..ad8eebc1a7 --- /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 0000000000..db86b27eeb --- /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