Enhance block_list memcheck gdbserver monitor command
Due to the (still to be done) default activation of --leak-check-heuristics=all,
improve the block_list monitor command for easier display of blocks
found reachable via heuristics.



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@15617 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/memcheck/docs/mc-manual.xml b/memcheck/docs/mc-manual.xml
index 69cf90d..39c032b 100644
--- a/memcheck/docs/mc-manual.xml
+++ b/memcheck/docs/mc-manual.xml
@@ -1903,12 +1903,19 @@
   </listitem>
 
   <listitem>
-    <para><varname>block_list &lt;loss_record_nr&gt;
-                              [unlimited*|limited &lt;max_blocks&gt;]</varname>
-      shows the list of blocks belonging to &lt;loss_record_nr&gt;.
+    <para><varname>block_list &lt;loss_record_nr&gt;|&lt;loss_record_nr_from&gt;..&lt;loss_record_nr_to&gt;
+        [unlimited*|limited &lt;max_blocks&gt;]
+        [heuristics heur1,heur2,...]
+      </varname>
+      shows the list of blocks belonging to
+      <varname>&lt;loss_record_nr&gt;</varname> (or to the loss records range
+      <varname>&lt;loss_record_nr_from&gt;..&lt;loss_record_nr_to&gt;</varname>).
       The nr of blocks to print can be controlled using the
       <varname>limited</varname> argument followed by the maximum nr
       of blocks to output.
+      If one or more heuristics are given, only prints the loss records
+      and blocks found via one of the given <varname>heur1,heur2,...</varname>
+      heuristics.
     </para>
 
     <para> A leak search merges the allocated blocks in loss records :
diff --git a/memcheck/mc_include.h b/memcheck/mc_include.h
index 876f0b6..3aeceb5 100644
--- a/memcheck/mc_include.h
+++ b/memcheck/mc_include.h
@@ -460,11 +460,16 @@
 // maintains the lcp.deltamode given in the last call to detect_memory_leaks
 extern LeakCheckDeltaMode MC_(detect_memory_leaks_last_delta_mode);
 
-// prints the list of blocks corresponding to the given loss_record_nr.
-// (up to maximum max_blocks)
-// Returns True if loss_record_nr identifies a correct loss record from last
-// leak search, returns False otherwise.
-Bool MC_(print_block_list) ( UInt loss_record_nr, UInt max_blocks);
+// prints the list of blocks corresponding to the given loss_record_nr slice
+// (from/to) (up to maximum max_blocks)
+// Returns True if loss_record_nr_from identifies a correct loss record
+// from last leak search, returns False otherwise.
+// Note that loss_record_nr_to can be bigger than the nr of loss records. All
+// loss records after from will then be examined and maybe printed.
+// If heuristics != 0, print only the loss records/blocks found via
+// one of the heuristics in the set.
+Bool MC_(print_block_list) ( UInt loss_record_nr_from, UInt loss_record_nr_to,
+                             UInt max_blocks, UInt heuristics);
 
 // Prints the addresses/registers/... at which a pointer to
 // the given range [address, address+szB[ is found.
diff --git a/memcheck/mc_leakcheck.c b/memcheck/mc_leakcheck.c
index e2f752b..e393022 100644
--- a/memcheck/mc_leakcheck.c
+++ b/memcheck/mc_leakcheck.c
@@ -1544,10 +1544,15 @@
    }
  }
 
-Bool MC_(print_block_list) ( UInt loss_record_nr, UInt max_blocks)
+Bool MC_(print_block_list) ( UInt loss_record_nr_from,
+                             UInt loss_record_nr_to,
+                             UInt max_blocks,
+                             UInt heuristics)
 {
+   UInt loss_record_nr;
    UInt         i,  n_lossrecords;
    LossRecord*  lr;
+   Bool lr_printed;
    UInt remaining = max_blocks;
 
    if (lr_table == NULL || lc_chunks == NULL || lc_extras == NULL) {
@@ -1561,52 +1566,75 @@
    }
 
    n_lossrecords = VG_(OSetGen_Size)(lr_table);
-   if (loss_record_nr >= n_lossrecords)
-      return False; // Invalid loss record nr.
+   if (loss_record_nr_from >= n_lossrecords)
+      return False; // Invalid starting loss record nr.
+
+   if (loss_record_nr_to >= n_lossrecords)
+      loss_record_nr_to = n_lossrecords - 1;
 
    tl_assert (lr_array);
-   lr = lr_array[loss_record_nr];
+
+   for (loss_record_nr = loss_record_nr_from;
+        loss_record_nr <= loss_record_nr_to && remaining > 0;
+        loss_record_nr++) {
+      lr = lr_array[loss_record_nr];
+      lr_printed = False;
+
+      /* If user asks to print a specific loss record, we print
+         the block details, even if no block will be shown for this lr.
+         If user asks to print a range of lr, we only print lr details
+         when at least one block is shown. */
+      if (loss_record_nr_from == loss_record_nr_to) {
+         /* (+1 on loss_record_nr as user numbering for loss records
+            starts at 1). */
+         MC_(pp_LossRecord)(loss_record_nr+1, n_lossrecords, lr);
+         lr_printed = True;
+      }
    
-   // (re-)print the loss record details.
-   // (+1 on loss_record_nr as user numbering for loss records starts at 1).
-   MC_(pp_LossRecord)(loss_record_nr+1, n_lossrecords, lr);
+      // Match the chunks with loss records.
+      for (i = 0; i < lc_n_chunks && remaining > 0; i++) {
+         MC_Chunk*     ch = lc_chunks[i];
+         LC_Extra*     ex = &(lc_extras)[i];
+         LossRecord*   old_lr;
+         LossRecordKey lrkey;
+         lrkey.state        = ex->state;
+         lrkey.allocated_at = MC_(allocated_at)(ch);
 
-   // Match the chunks with loss records.
-   for (i = 0; i < lc_n_chunks && remaining > 0; i++) {
-      MC_Chunk*     ch = lc_chunks[i];
-      LC_Extra*     ex = &(lc_extras)[i];
-      LossRecord*   old_lr;
-      LossRecordKey lrkey;
-      lrkey.state        = ex->state;
-      lrkey.allocated_at = MC_(allocated_at)(ch);
+         old_lr = VG_(OSetGen_Lookup)(lr_table, &lrkey);
+         if (old_lr) {
+            // We found an existing loss record matching this chunk.
+            // If this is the loss record we are looking for, output the
+            // pointer.
+            if (old_lr == lr_array[loss_record_nr]
+                && (heuristics == 0 || HiS(ex->heuristic, heuristics))) {
+               if (!lr_printed) {
+                  MC_(pp_LossRecord)(loss_record_nr+1, n_lossrecords, lr);
+                  lr_printed = True;
+               }
 
-      old_lr = VG_(OSetGen_Lookup)(lr_table, &lrkey);
-      if (old_lr) {
-         // We found an existing loss record matching this chunk.
-         // If this is the loss record we are looking for, output the pointer.
-         if (old_lr == lr_array[loss_record_nr]) {
-            if (ex->heuristic)
-               VG_(umsg)("%p[%lu] (found via heuristic %s)\n",
-                         (void *)ch->data, (SizeT)ch->szB,
-                         pp_heuristic (ex->heuristic));
-            else
-               VG_(umsg)("%p[%lu]\n",
-                         (void *)ch->data, (SizeT)ch->szB);
-            remaining--;
-            if (ex->state != Reachable) {
-               // We can print the clique in all states, except Reachable.
-               // In Unreached state, lc_chunk[i] is the clique leader.
-               // In IndirectLeak, lc_chunk[i] might have been a clique leader
-               // which was later collected in another clique.
-               // For Possible, lc_chunk[i] might be the top of a clique
-               // or an intermediate clique.
-               print_clique(i, 1, &remaining);
+               if (ex->heuristic)
+                  VG_(umsg)("%p[%lu] (found via heuristic %s)\n",
+                            (void *)ch->data, (SizeT)ch->szB,
+                            pp_heuristic (ex->heuristic));
+               else
+                  VG_(umsg)("%p[%lu]\n",
+                            (void *)ch->data, (SizeT)ch->szB);
+               remaining--;
+               if (ex->state != Reachable) {
+                  // We can print the clique in all states, except Reachable.
+                  // In Unreached state, lc_chunk[i] is the clique leader.
+                  // In IndirectLeak, lc_chunk[i] might have been a clique
+                  // leader which was later collected in another clique.
+                  // For Possible, lc_chunk[i] might be the top of a clique
+                  // or an intermediate clique.
+                  print_clique(i, 1, &remaining);
+               }
             }
+         } else {
+            // No existing loss record matches this chunk ???
+            VG_(umsg)("error: no loss record found for %p[%lu]?????\n",
+                      (void *)ch->data, (SizeT)ch->szB);
          }
-      } else {
-         // No existing loss record matches this chunk ???
-         VG_(umsg)("error: no loss record found for %p[%lu]?????\n",
-                   (void *)ch->data, (SizeT)ch->szB);
       }
    }
    return True;
diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c
index 2902c6b..838a296 100644
--- a/memcheck/mc_main.c
+++ b/memcheck/mc_main.c
@@ -6032,8 +6032,12 @@
 "                 leak_check summary any\n"
 "                 leak_check full kinds indirect,possible\n"
 "                 leak_check full reachable any limited 100\n"
-"  block_list <loss_record_nr> [unlimited*|limited <max_blocks>]\n"
+"  block_list <loss_record_nr>|<loss_record_nr_from>..<loss_record_nr_to>\n"
+"                [unlimited*|limited <max_blocks>]\n"
+"                [heuristics heur1,heur2,...]\n"
 "        after a leak search, shows the list of blocks of <loss_record_nr>\n"
+"        (or of the range <loss_record_nr_from>..<loss_record_nr_to>).\n"
+"        With heuristics, only shows the blocks found via heur1,heur2,...\n"
 "            * = defaults\n"
 "  who_points_at <addr> [<len>]\n"
 "        shows places pointing inside <len> (default 1) bytes at <addr>\n"
@@ -6064,6 +6068,81 @@
    VG_(printf) ("\n"); // Terminate previous line
 }
 
+
+/* Returns the address of the next non space character,
+   or address of the string terminator. */
+static HChar* next_non_space (HChar *s)
+{
+   while (*s && *s == ' ')
+      s++;
+   return s;
+}
+
+/* Parse an integer slice, i.e. a single integer or a range of integer.
+   Syntax is:
+       <integer>[..<integer> ]
+   (spaces are allowed before and/or after ..).
+   Return True if range correctly parsed, False otherwise. */
+static Bool VG_(parse_slice) (HChar* s, HChar** saveptr,
+                              UInt *from, UInt *to)
+{
+   HChar* wl;
+   HChar *endptr;
+   endptr = NULL;////
+   wl = VG_(strtok_r) (s, " ", saveptr);
+
+   /* slice must start with an integer. */
+   if (wl == NULL) {
+      VG_(gdb_printf) ("expecting integer or slice <from>..<to>\n");
+      return False;
+   }
+   *from = VG_(strtoull10) (wl, &endptr);
+   if (endptr == wl) {
+      VG_(gdb_printf) ("invalid integer or slice <from>..<to>\n");
+      return False;
+   }
+
+   if (*endptr == '\0' && *next_non_space(*saveptr) != '.') {
+      /* wl token is an integer terminating the string
+         or else next token does not start with .
+         In both cases, the slice is a single integer. */
+      *to = *from;
+      return True;
+   }
+
+   if (*endptr == '\0') {
+      // iii ..    => get the next token
+      wl =  VG_(strtok_r) (NULL, " .", saveptr);
+   } else {
+      // It must be iii..
+      if (*endptr != '.' && *(endptr+1) != '.') {
+         VG_(gdb_printf) ("expecting slice <from>..<to>\n");
+         return False;
+      }
+      if ( *(endptr+2) == ' ') {
+         // It must be iii.. jjj  => get the next token
+         wl =  VG_(strtok_r) (NULL, " .", saveptr);
+      } else {
+         // It must be iii..jjj
+         wl = endptr+2;
+      }
+   }
+
+   *to = VG_(strtoull10) (wl, &endptr);
+   if (*endptr != '\0') {
+      VG_(gdb_printf) ("missing/wrong 'to' of slice <from>..<to>\n");
+      return False;
+   }
+
+   if (*from > *to) {
+      VG_(gdb_printf) ("<from> cannot be bigger than <to> "
+                       "in slice <from>..<to>\n");
+      return False;
+   }
+
+   return True;
+}
+
 /* return True if request recognised, False otherwise */
 static Bool handle_gdb_monitor_command (ThreadId tid, HChar *req)
 {
@@ -6316,22 +6395,19 @@
 
    case  5: { /* block_list */
       HChar* wl;
-      HChar *endptr;
       HChar *the_end;
-      UInt lr_nr = 0;
+      UInt lr_nr_from = 0;
+      UInt lr_nr_to = 0;
 
-      wl = VG_(strtok_r) (NULL, " ", &ssaveptr);
-      if (wl != NULL)
-         lr_nr = VG_(strtoull10) (wl, &endptr);
-      if (wl == NULL || *endptr != '\0') {
-         VG_(gdb_printf) ("malformed or missing integer\n");
-      } else {
-         UInt limit_blocks;
+      if (VG_(parse_slice) (NULL, &ssaveptr, &lr_nr_from, &lr_nr_to)) {
+         UInt limit_blocks = 999999999;
          Int int_value;
-
-         wl = VG_(strtok_r) (NULL, " ", &ssaveptr);
-         if (wl != NULL) {
-            switch (VG_(keyword_id) ("unlimited limited ", 
+         UInt heuristics = 0;
+         
+         for (wl = VG_(strtok_r) (NULL, " ", &ssaveptr);
+              wl != NULL;
+              wl = VG_(strtok_r) (NULL, " ", &ssaveptr)) {
+            switch (VG_(keyword_id) ("unlimited limited heuristics ", 
                                      wl,  kwd_report_all)) {
             case -2: return True;
             case -1: return True;
@@ -6355,15 +6431,27 @@
                }
                limit_blocks = (UInt) int_value;
                break;
+            case  2: /* heuristics */
+               wcmd = VG_(strtok_r) (NULL, " ", &ssaveptr);
+               if (wcmd == NULL 
+                   || !VG_(parse_enum_set)(MC_(parse_leak_heuristics_tokens),
+                                           True,/*allow_all*/
+                                           wcmd,
+                                           &heuristics)) {
+                  VG_(gdb_printf) ("missing or malformed heuristics set\n");
+                  return True;
+               }
+               break;
             default:
                tl_assert (0);
             }
-         } else {
-            limit_blocks = 999999999;
          }
-         /* lr_nr-1 as what is shown to the user is 1 more than the index
-            in lr_array. */
-         if (lr_nr == 0 || ! MC_(print_block_list) (lr_nr-1, limit_blocks))
+         /* substract 1 from lr_nr_from/lr_nr_to  as what is shown to the user
+            is 1 more than the index in lr_array. */
+         if (lr_nr_from == 0 || ! MC_(print_block_list) (lr_nr_from-1,
+                                                         lr_nr_to-1,
+                                                         limit_blocks,
+                                                         heuristics))
             VG_(gdb_printf) ("invalid loss record nr\n");
       }
       return True;