Added a new parameter to the memcheck 'leak_check' GDB monitor command
to let the user specify a max nr of loss records to output : on huge
applications, interactive display of a lot of records in gdb can
take a lot of time.
* mc_include.h :
- added UInt max_loss_records_output; to LeakCheckParams structure
- avoid passing LeakCheckParams by struct copy.
* modified test gdbserver_tests/mcleak to test the new parameter
* mc_main.c : parse or set max_loss_records_output in leak_check cmd
handling and calls.
* mc-manual.xml : document new leak_check parameter
* mc_leakcheck.c :
- extract printing rules logic in its own function
- in print_results, if there is a limit in LeakCheckParam,
compute from where the printing of loss records has to start
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@12329 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/memcheck/docs/mc-manual.xml b/memcheck/docs/mc-manual.xml
index 9a82b5a..64817e9 100644
--- a/memcheck/docs/mc-manual.xml
+++ b/memcheck/docs/mc-manual.xml
@@ -1388,9 +1388,10 @@
<para><varname>leak_check [full*|summary]
[reachable|possibleleak*|definiteleak]
[increased*|changed|any]
+ [unlimited*|limited <max_loss_records_output>]
</varname>
performs a leak check. The <varname>*</varname> in the arguments
- indicates the default value. </para>
+ indicates the default values. </para>
<para> If the first argument is <varname>summary</varname>, only a
summary of the leak search is given; otherwise a full leak report
@@ -1399,7 +1400,12 @@
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.
+ both the second and third argument. A full leak report might
+ output detailed information for many leaks. The nr of leaks for
+ which information is output can be controlled using
+ the <varname>limited</varname> argument followed by the maximum nr
+ of leak records to output. If this maximum is reached, the leak
+ search outputs the records with the biggest number of bytes.
</para>
<para>The second argument controls what kind of blocks are shown for
@@ -1428,7 +1434,7 @@
</para>
<para>The following example shows usage of the
- <varname>leak_check monitor</varname> command on
+ <varname>leak_check</varname> monitor 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
@@ -1474,6 +1480,7 @@
abbreviation: <computeroutput>mo l f r a</computeroutput>).
</para>
</listitem>
+
</itemizedlist>
</sect1>
diff --git a/memcheck/mc_include.h b/memcheck/mc_include.h
index 11e76a3..9173314 100644
--- a/memcheck/mc_include.h
+++ b/memcheck/mc_include.h
@@ -311,11 +311,12 @@
Bool show_reachable;
Bool show_possibly_lost;
LeakCheckDeltaMode deltamode;
+ UInt max_loss_records_output; // limit on the nr of loss records output.
Bool requested_by_monitor_command; // True when requested by gdb/vgdb.
}
LeakCheckParams;
-void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckParams lcp);
+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);
diff --git a/memcheck/mc_leakcheck.c b/memcheck/mc_leakcheck.c
index 944ef27..1f8cd65 100644
--- a/memcheck/mc_leakcheck.c
+++ b/memcheck/mc_leakcheck.c
@@ -781,9 +781,64 @@
return 0;
}
-static void print_results(ThreadId tid, LeakCheckParams lcp)
+
+static void get_printing_rules(LeakCheckParams* lcp,
+ LossRecord* lr,
+ Bool* count_as_error,
+ Bool* print_record)
{
- Int i, n_lossrecords;
+ // Rules for printing:
+ // - We don't show suppressed loss records ever (and that's controlled
+ // within the error manager).
+ // - We show non-suppressed loss records that are not "reachable" if
+ // --leak-check=yes.
+ // - We show all non-suppressed loss records if --leak-check=yes and
+ // --show-reachable=yes.
+ //
+ // Nb: here "reachable" means Reachable *or* IndirectLeak; note that
+ // this is different to "still reachable" used elsewhere because it
+ // includes indirectly lost blocks!
+
+ Bool delta_considered;
+
+ switch (lcp->deltamode) {
+ case LCD_Any:
+ delta_considered = lr->num_blocks > 0;
+ break;
+ case LCD_Increased:
+ delta_considered
+ = lr->szB > lr->old_szB
+ || lr->indirect_szB > lr->old_indirect_szB
+ || lr->num_blocks > lr->old_num_blocks;
+ break;
+ case LCD_Changed:
+ delta_considered = lr->szB != lr->old_szB
+ || lr->indirect_szB != lr->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 ||
+ ( lcp->show_possibly_lost &&
+ Possible == lr->key.state ) );
+ // 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 = lcp->mode == LC_Full && delta_considered &&
+ ( Unreached == lr->key.state ||
+ Possible == lr->key.state );
+}
+
+static void print_results(ThreadId tid, LeakCheckParams* lcp)
+{
+ Int i, n_lossrecords, start_lr_output_scan;
LossRecord** lr_array;
LossRecord* lr;
Bool is_suppressed;
@@ -864,55 +919,37 @@
MC_(blocks_reachable) = MC_(bytes_reachable) = 0;
MC_(blocks_suppressed) = MC_(bytes_suppressed) = 0;
- // Print the loss records (in size order) and collect summary stats.
- for (i = 0; i < n_lossrecords; i++) {
- 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).
- // - We show non-suppressed loss records that are not "reachable" if
- // --leak-check=yes.
- // - We show all non-suppressed loss records if --leak-check=yes and
- // --show-reachable=yes.
- //
- // Nb: here "reachable" means Reachable *or* IndirectLeak; note that
- // this is different to "still reachable" used elsewhere because it
- // includes indirectly lost blocks!
- //
- lr = lr_array[i];
- 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);
+ // If there is a maximum nr of loss records we can output, then first
+ // compute from where the output scan has to start.
+ // By default, start from the first loss record. Compute a higher
+ // value if there is a maximum to respect. We need to print the last
+ // records, as the one with the biggest sizes are more interesting.
+ start_lr_output_scan = 0;
+ if (lcp->mode == LC_Full && lcp->max_loss_records_output < n_lossrecords) {
+ Int nr_printable_records = 0;
+ for (i = n_lossrecords - 1; i >= 0 && start_lr_output_scan == 0; i--) {
+ Bool count_as_error, print_record;
+ lr = lr_array[i];
+ get_printing_rules (lcp, lr, &count_as_error, &print_record);
+ // Do not use get_printing_rules results for is_suppressed, as we
+ // only want to check if the record would be suppressed.
+ is_suppressed =
+ MC_(record_leak_error) ( tid, i+1, n_lossrecords, lr,
+ False /* print_record */,
+ False /* count_as_error */);
+ if (print_record && !is_suppressed) {
+ nr_printable_records++;
+ if (nr_printable_records == lcp->max_loss_records_output)
+ start_lr_output_scan = i;
+ }
}
+ }
- print_record = lcp.mode == LC_Full && delta_considered &&
- ( lcp.show_reachable ||
- Unreached == lr->key.state ||
- ( lcp.show_possibly_lost &&
- Possible == lr->key.state ) );
- // 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 = lcp.mode == LC_Full && delta_considered &&
- ( Unreached == lr->key.state ||
- Possible == lr->key.state );
+ // Print the loss records (in size order) and collect summary stats.
+ for (i = start_lr_output_scan; i < n_lossrecords; i++) {
+ Bool count_as_error, print_record;
+ lr = lr_array[i];
+ get_printing_rules(lcp, lr, &count_as_error, &print_record);
is_suppressed =
MC_(record_leak_error) ( tid, i+1, n_lossrecords, lr, print_record,
count_as_error );
@@ -968,44 +1005,44 @@
VG_(umsg)("LEAK SUMMARY:\n");
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_(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));
+ 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_(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) );
+ 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_(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) );
+ 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_(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) );
+ 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_(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_(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) {
- if (lcp.requested_by_monitor_command)
+ if (lcp->requested_by_monitor_command)
VG_(umsg)("To see details of leaked memory, give 'full' arg to leak_check\n");
else
VG_(umsg)("Rerun with --leak-check=full to see details "
"of leaked memory\n");
}
- if (lcp.mode == LC_Full &&
- MC_(blocks_reachable) > 0 && !lcp.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");
- if (lcp.requested_by_monitor_command)
+ if (lcp->requested_by_monitor_command)
VG_(umsg)("To see them, add 'reachable any' args to leak_check\n");
else
VG_(umsg)("To see them, rerun with: --leak-check=full "
@@ -1019,13 +1056,13 @@
/*--- Top-level entry point. ---*/
/*------------------------------------------------------------*/
-void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckParams lcp)
+void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckParams* lcp)
{
Int i, j;
- tl_assert(lcp.mode != LC_Off);
+ tl_assert(lcp->mode != LC_Off);
- MC_(detect_memory_leaks_last_delta_mode) = lcp.deltamode;
+ 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);
diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c
index b2a8fc4..2d116fd 100644
--- a/memcheck/mc_main.c
+++ b/memcheck/mc_main.c
@@ -5016,9 +5016,11 @@
" and outputs a description of <addr>\n"
" leak_check [full*|summary] [reachable|possibleleak*|definiteleak]\n"
" [increased*|changed|any]\n"
+" [unlimited*|limited <max_loss_records_output>]\n"
" * = defaults\n"
" Examples: leak_check\n"
" leak_check summary any\n"
+" leak_check full reachable any limited 100\n"
"\n");
}
@@ -5089,6 +5091,7 @@
lcp.show_reachable = False;
lcp.show_possibly_lost = True;
lcp.deltamode = LCD_Increased;
+ lcp.max_loss_records_output = 999999999;
lcp.requested_by_monitor_command = True;
for (kw = VG_(strtok_r) (NULL, " ", &ssaveptr);
@@ -5097,7 +5100,8 @@
switch (VG_(keyword_id)
("full summary "
"reachable possibleleak definiteleak "
- "increased changed any",
+ "increased changed any "
+ "unlimited limited ",
kw, kwd_report_all)) {
case -2: err++; break;
case -1: err++; break;
@@ -5120,12 +5124,34 @@
lcp.deltamode = LCD_Changed; break;
case 7: /* any */
lcp.deltamode = LCD_Any; break;
+ case 8: /* unlimited */
+ lcp.max_loss_records_output = 999999999; break;
+ case 9: { /* limited */
+ int int_value;
+ char* endptr;
+
+ wcmd = VG_(strtok_r) (NULL, " ", &ssaveptr);
+ if (wcmd == NULL) {
+ int_value = 0;
+ endptr = "empty"; /* to report an error below */
+ } else {
+ int_value = VG_(strtoll10) (wcmd, (Char **)&endptr);
+ }
+ if (*endptr != '\0')
+ VG_(gdb_printf) ("missing or malformed integer value\n");
+ else if (int_value > 0)
+ lcp.max_loss_records_output = (UInt) int_value;
+ else
+ VG_(gdb_printf) ("max_loss_records_output must be >= 1, got %d\n",
+ int_value);
+ break;
+ }
default:
tl_assert (0);
}
}
if (!err)
- MC_(detect_memory_leaks)(tid, lcp);
+ MC_(detect_memory_leaks)(tid, &lcp);
return True;
}
@@ -5316,9 +5342,10 @@
"Warning: unknown memcheck leak search deltamode\n");
lcp.deltamode = LCD_Any;
}
+ lcp.max_loss_records_output = 999999999;
lcp.requested_by_monitor_command = False;
- MC_(detect_memory_leaks)(tid, lcp);
+ MC_(detect_memory_leaks)(tid, &lcp);
*ret = 0; /* return value is meaningless */
break;
}
@@ -5995,8 +6022,9 @@
lcp.show_reachable = MC_(clo_show_reachable);
lcp.show_possibly_lost = MC_(clo_show_possibly_lost);
lcp.deltamode = LCD_Any;
+ lcp.max_loss_records_output = 999999999;
lcp.requested_by_monitor_command = False;
- MC_(detect_memory_leaks)(1/*bogus ThreadId*/, lcp);
+ MC_(detect_memory_leaks)(1/*bogus ThreadId*/, &lcp);
} else {
if (VG_(clo_verbosity) == 1 && !VG_(clo_xml)) {
VG_(umsg)(