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 &lt;max_loss_records_output&gt;]
           </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)(