Implement --merge-recursive-frames + provide VALGRIND_MONITOR_COMMAND client req.

In a big applications, some recursive algorithms have created
hundreds of thousands of stacktraces, taking a lot of memory.

Option --merge-recursive-frames=<number> tells Valgrind to
detect and merge (collapse) recursive calls when recording stack traces.
The value is changeable using the monitor command
'v.set merge-recursive-frames'.

Also, this provides a new client request: VALGRIND_MONITOR_COMMAND
allowing to execute a gdbsrv monitor command from the client
program.



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@13246 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/NEWS b/NEWS
index dfef853..81c34e9 100644
--- a/NEWS
+++ b/NEWS
@@ -30,6 +30,18 @@
     This can be used to analyse one possible cause of Valgrind high
     memory usage for some programs.
 
+  - Option --merge-recursive-frames=<number> tells Valgrind to
+    detect and merge (collapse) recursive calls when recording stack traces.
+    When your program has recursive algorithms, this limits
+    the memory used by Valgrind for recorded stack traces and avoid
+    recording uninteresting repeated calls.
+    The value is changeable using the monitor command
+    'v.set merge-recursive-frames'.
+
+  - valgrind.h has a new request VALGRIND_MONITOR_COMMAND.
+    This can be used to execute gdbserver monitor commands from
+    the client program.
+
 * ==================== FIXED BUGS ====================
 
 The following bugs have been fixed or resolved.  Note that "n-i-bz"
diff --git a/coregrind/m_gdbserver/remote-utils.c b/coregrind/m_gdbserver/remote-utils.c
index 8c5f2c1..8a9c969 100644
--- a/coregrind/m_gdbserver/remote-utils.c
+++ b/coregrind/m_gdbserver/remote-utils.c
@@ -771,19 +771,23 @@
 
 void monitor_output (char *s)
 {
-   const int len = strlen(s);
-   char *buf = malloc(1 + 2*len + 1);
-
-   buf[0] = 'O';
-   hexify(buf+1, s, len);
-   if (putpkt (buf) < 0) {
-      /* We probably have lost the connection with vgdb. */
-      reset_valgrind_sink("Error writing monitor output");
-      /* write again after reset */
-      VG_(printf) ("%s", s);
+   if (remote_connected()) {
+      const int len = strlen(s);
+      char *buf = malloc(1 + 2*len + 1);
+      
+      buf[0] = 'O';
+      hexify(buf+1, s, len);
+      if (putpkt (buf) < 0) {
+         /* We probably have lost the connection with vgdb. */
+         reset_valgrind_sink("Error writing monitor output");
+         /* write again after reset */
+         VG_(printf) ("%s", s);
+      }
+      
+      free (buf);
+   } else {
+      print_to_initial_valgrind_sink (s);
    }
-   
-   free (buf);
 }
 
 /* Returns next char from remote GDB.  -1 if error.  */
diff --git a/coregrind/m_gdbserver/server.c b/coregrind/m_gdbserver/server.c
index 90340a6..c4f3850 100644
--- a/coregrind/m_gdbserver/server.c
+++ b/coregrind/m_gdbserver/server.c
@@ -106,6 +106,13 @@
    }
 }
 
+void print_to_initial_valgrind_sink (const char *msg)
+{
+   vg_assert (initial_valgrind_sink_saved);
+   VG_(write) (initial_valgrind_sink.fd, msg, strlen(msg));
+}
+
+
 static
 void kill_request (const char *msg)
 {
@@ -172,6 +179,7 @@
 "  v.set gdb_output        : set valgrind output to gdb\n"
 "  v.set log_output        : set valgrind output to log\n"
 "  v.set mixed_output      : set valgrind output to log, interactive output to gdb\n"
+"  v.set merge-recursive-frames <num> : merge recursive calls in max <num> frames\n"
 "  v.set vgdb-error <errornr> : debug me at error >= <errornr> \n");
       if (int_value) { VG_(gdb_printf) (
 "debugging valgrind internals monitor commands:\n"
@@ -190,13 +198,15 @@
       ret = 1;
       wcmd = strtok_r (NULL, " ", &ssaveptr);
       switch (kwdid = VG_(keyword_id) 
-              ("vgdb-error debuglog gdb_output log_output mixed_output",
+              ("vgdb-error debuglog merge-recursive-frames"
+               " gdb_output log_output mixed_output",
                wcmd, kwd_report_all)) {
       case -2:
       case -1: 
          break;
       case 0: /* vgdb-error */
       case 1: /* debuglog */
+      case 2: /* merge-recursive-frames */
          wcmd = strtok_r (NULL, " ", &ssaveptr);
          if (wcmd == NULL) {
             int_value = 0;
@@ -216,21 +226,26 @@
             VG_(gdb_printf) ("debuglog value changed from %d to %d\n",
                              VG_(debugLog_getLevel)(), int_value);
             VG_(debugLog_startup) (int_value, "gdbsrv");
+         } else if (kwdid == 2) {
+            VG_(gdb_printf)
+               ("merge-recursive-frames value changed from %d to %d\n",
+                VG_(clo_merge_recursive_frames), int_value);
+            VG_(clo_merge_recursive_frames) = int_value;
          } else {
             vg_assert (0);
          }
          break;
-      case 2: /* gdb_output */
+      case 3: /* gdb_output */
          (*sink_wanted_at_return).fd = -2;
          command_output_to_log = False;
          VG_(gdb_printf) ("valgrind output will go to gdb\n");
          break;
-      case 3: /* log_output */
+      case 4: /* log_output */
          (*sink_wanted_at_return).fd = initial_valgrind_sink.fd;
          command_output_to_log = True;
          VG_(gdb_printf) ("valgrind output will go to log\n");
          break;
-      case 4: /* mixed output */
+      case 5: /* mixed output */
          (*sink_wanted_at_return).fd = initial_valgrind_sink.fd;
          command_output_to_log = False;
          VG_(gdb_printf)
@@ -459,6 +474,26 @@
    arg_own_buf[0] = 0;
 }
 
+Bool VG_(client_monitor_command) (HChar* cmd)
+{
+   const Bool connected = remote_connected();
+   const int saved_command_output_to_log = command_output_to_log;
+   Bool handled;
+
+   if (!connected)
+      command_output_to_log = True;
+   handled = handle_gdb_monitor_command (cmd);
+   if (!connected) {
+      // reset the log output unless cmd changed it.
+      if (command_output_to_log)
+         command_output_to_log = saved_command_output_to_log;
+   }
+   if (handled)
+      return False; // recognised
+   else
+      return True; // not recognised
+}
+
 /* Handle all of the extended 'q' packets.  */
 static
 void handle_query (char *arg_own_buf, int *new_packet_len_p)
diff --git a/coregrind/m_gdbserver/server.h b/coregrind/m_gdbserver/server.h
index f68f11e..655a2ab 100644
--- a/coregrind/m_gdbserver/server.h
+++ b/coregrind/m_gdbserver/server.h
@@ -63,7 +63,7 @@
 
 
 /* Output string s to the gdb debugging this process or to vgdb.
-   Do not call this directly. Rather use VG_(monitor_print) 
+   Do not call this directly. Rather use VG_(gdb_printf) 
    to output something to gdb, use normal valgrind messaging
    (e.g. VG_(umsg)) to send output that can either go
    to gdb or to log. */
@@ -94,6 +94,11 @@
       and does VG_(umsg). If info != NULL, info added in VG_(usmg). */
 extern void reset_valgrind_sink(const char* info);
 
+// VG_(gdb_printf) by default writes to vgdb/gdb.
+// If there is no connection, it will rather write to the initial (log)
+// valgrind fd using the below.
+extern void print_to_initial_valgrind_sink (const char *msg);
+
 /* For ARM usage.
    Guesses if pc is a thumb pc.
    In this case, returns pc with the thumb bit set (bit0)
diff --git a/coregrind/m_main.c b/coregrind/m_main.c
index ffa48f2..ad028c8 100644
--- a/coregrind/m_main.c
+++ b/coregrind/m_main.c
@@ -193,6 +193,8 @@
 "    --fair-sched=no|yes|try   schedule threads fairly on multicore systems [no]\n"
 "    --kernel-variant=variant1,variant2,...  known variants: bproc [none]\n"
 "                              handle non-standard kernel variants\n"
+"    --merge-recursive-frames=<number>  merge frames between identical\n"
+"           program counters in max <number> frames) [0]\n"
 "    --show-emwarns=no|yes     show warnings about emulation limits? [no]\n"
 "    --require-text-symbol=:sonamepattern:symbolpattern    abort run if the\n"
 "                              stated shared object doesn't have the stated\n"
@@ -599,6 +601,9 @@
       else if VG_INT_CLO (arg, "--sanity-level",     VG_(clo_sanity_level)) {}
       else if VG_BINT_CLO(arg, "--num-callers",      VG_(clo_backtrace_size), 1,
                                                      VG_DEEPEST_BACKTRACE) {}
+      else if VG_BINT_CLO(arg, "--merge-recursive-frames",
+                               VG_(clo_merge_recursive_frames), 0,
+                               VG_DEEPEST_BACKTRACE) {}
 
       else if VG_XACT_CLO(arg, "--smc-check=none",  VG_(clo_smc_check),
                                                     Vg_SmcNone);
diff --git a/coregrind/m_options.c b/coregrind/m_options.c
index 323fec4..dbdb124 100644
--- a/coregrind/m_options.c
+++ b/coregrind/m_options.c
@@ -105,6 +105,7 @@
 Int    VG_(clo_redzone_size)   = -1;
 Int    VG_(clo_dump_error)     = 0;
 Int    VG_(clo_backtrace_size) = 12;
+Int    VG_(clo_merge_recursive_frames) = 0; // default value: no merge
 const HChar* VG_(clo_sim_hints)      = NULL;
 Bool   VG_(clo_sym_offsets)    = False;
 Bool   VG_(clo_read_var_info)  = False;
diff --git a/coregrind/m_scheduler/scheduler.c b/coregrind/m_scheduler/scheduler.c
index 248dc35..d6905e4 100644
--- a/coregrind/m_scheduler/scheduler.c
+++ b/coregrind/m_scheduler/scheduler.c
@@ -1881,6 +1881,13 @@
          break;
       }
 
+      case VG_USERREQ__GDB_MONITOR_COMMAND: {
+         UWord ret;
+         ret = (UWord) VG_(client_monitor_command) ((HChar*)arg[1]);
+         SET_CLREQ_RETVAL(tid, ret);
+         break;
+      }
+
       case VG_USERREQ__MALLOCLIKE_BLOCK:
       case VG_USERREQ__RESIZEINPLACE_BLOCK:
       case VG_USERREQ__FREELIKE_BLOCK:
diff --git a/coregrind/m_stacktrace.c b/coregrind/m_stacktrace.c
index 28e999c..49964b0 100644
--- a/coregrind/m_stacktrace.c
+++ b/coregrind/m_stacktrace.c
@@ -62,6 +62,21 @@
    traces on ppc64-linux and has no effect on other platforms.
 */
 
+/* Do frame merging in the _i frames in _ips array of recursive cycles
+   of up to _nframes.  The merge is done during stack unwinding
+   (i.e. in platform specific unwinders) to collect as many
+   "interesting" stack traces as possible. */
+#define RECURSIVE_MERGE(_nframes,_ips,_i){                      \
+   Int dist;                                                    \
+   for (dist = 1; dist <= _nframes && dist < (Int)_i; dist++) { \
+      if (_ips[_i-1] == _ips[_i-1-dist]) {                      \
+         _i = _i - dist;                                        \
+         break;                                                 \
+      }                                                         \
+   }                                                            \
+}
+
+
 /* ------------------------ x86 ------------------------- */
 
 #if defined(VGP_x86_linux) || defined(VGP_x86_darwin)
@@ -76,6 +91,7 @@
    Int   i;
    Addr  fp_max;
    UInt  n_found = 0;
+   const Int cmrf = VG_(clo_merge_recursive_frames);
 
    vg_assert(sizeof(Addr) == sizeof(UWord));
    vg_assert(sizeof(Addr) == sizeof(void*));
@@ -178,6 +194,7 @@
             VG_(printf)("     ipsF[%d]=0x%08lx\n", i-1, ips[i-1]);
          uregs.xip = uregs.xip - 1;
             /* as per comment at the head of this loop */
+         if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
          continue;
       }
 
@@ -192,6 +209,7 @@
             VG_(printf)("     ipsC[%d]=0x%08lx\n", i-1, ips[i-1]);
          uregs.xip = uregs.xip - 1;
             /* as per comment at the head of this loop */
+         if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
          continue;
       }
 
@@ -205,6 +223,7 @@
          if (debug)
             VG_(printf)("     ipsC[%d]=0x%08lx\n", i-1, ips[i-1]);
          uregs.xip = uregs.xip - 1;
+         if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
          continue;
       }
 
@@ -232,6 +251,7 @@
    Int   i;
    Addr  fp_max;
    UInt  n_found = 0;
+   const Int cmrf = VG_(clo_merge_recursive_frames);
 
    vg_assert(sizeof(Addr) == sizeof(UWord));
    vg_assert(sizeof(Addr) == sizeof(void*));
@@ -314,6 +334,7 @@
          if (debug)
             VG_(printf)("     ipsC[%d]=%#08lx\n", i-1, ips[i-1]);
          uregs.xip = uregs.xip - 1; /* as per comment at the head of this loop */
+         if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
          continue;
       }
 
@@ -342,6 +363,7 @@
          if (debug)
             VG_(printf)("     ipsF[%d]=%#08lx\n", i-1, ips[i-1]);
          uregs.xip = uregs.xip - 1; /* as per comment at the head of this loop */
+         if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
          continue;
       }
 
@@ -371,6 +393,7 @@
             VG_(printf)("     ipsH[%d]=%#08lx\n", i-1, ips[i-1]);
          uregs.xip = uregs.xip - 1; /* as per comment at the head of this loop */
          uregs.xsp += 8;
+         if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
          continue;
       }
 
@@ -399,6 +422,7 @@
    Word redir_stack_size = 0;
    Word redirs_used      = 0;
 #  endif
+   const Int cmrf = VG_(clo_merge_recursive_frames);
 
    Bool  debug = False;
    Int   i;
@@ -557,6 +581,7 @@
             ip = ip - 1; /* ip is probably dead at this point, but
                             play safe, a la x86/amd64 above.  See
                             extensive comments above. */
+            if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
             continue;
          }
 
@@ -666,6 +691,7 @@
    Int   i;
    Addr  fp_max;
    UInt  n_found = 0;
+   const Int cmrf = VG_(clo_merge_recursive_frames);
 
    vg_assert(sizeof(Addr) == sizeof(UWord));
    vg_assert(sizeof(Addr) == sizeof(void*));
@@ -737,6 +763,7 @@
             VG_(printf)("USING CFI: r15: 0x%lx, r13: 0x%lx\n",
                         uregs.r15, uregs.r13);
          uregs.r15 = (uregs.r15 & 0xFFFFFFFE) - 1;
+         if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
          continue;
       }
       /* No luck.  We have to give up. */
@@ -759,6 +786,7 @@
             if (sps) sps[i] = 0;
             if (fps) fps[i] = 0;
             ips[i++] = cand;
+            if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
             nByStackScan++;
          }
       }
@@ -775,6 +803,7 @@
                if (sps) sps[i] = 0;
                if (fps) fps[i] = 0;
                ips[i++] = cand;
+               if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
                if (++nByStackScan >= 5) break;
             }
          }
@@ -802,6 +831,7 @@
    Int   i;
    Addr  fp_max;
    UInt  n_found = 0;
+   const Int cmrf = VG_(clo_merge_recursive_frames);
 
    vg_assert(sizeof(Addr) == sizeof(UWord));
    vg_assert(sizeof(Addr) == sizeof(void*));
@@ -841,6 +871,7 @@
          if (fps) fps[i] = uregs.fp;
          ips[i++] = uregs.ia - 1;
          uregs.ia = uregs.ia - 1;
+         if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
          continue;
       }
       /* A problem on the first frame? Lets assume it was a bad jump.
@@ -857,6 +888,7 @@
          }
          uregs.ia = uregs.lr - 1;
          ips[i++] = uregs.lr - 1;
+         if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
          continue;
       }
 
@@ -884,6 +916,7 @@
    Int   i;
    Addr  fp_max;
    UInt  n_found = 0;
+   const Int cmrf = VG_(clo_merge_recursive_frames);
 
    vg_assert(sizeof(Addr) == sizeof(UWord));
    vg_assert(sizeof(Addr) == sizeof(void*));
@@ -935,6 +968,7 @@
             if (fps) fps[i] = uregs.fp;
             ips[i++] = uregs.pc - 4;
             uregs.pc = uregs.pc - 4;
+            if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
             continue;
          } else
             uregs = uregs_copy;
@@ -993,6 +1027,7 @@
          if (0 == uregs.ra || 1 == uregs.ra) break;
          uregs.pc = uregs.ra - 8;
          ips[i++] = uregs.ra - 8;
+         if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
          continue;
       }
 
@@ -1008,6 +1043,7 @@
          if (0 == uregs.ra || 1 == uregs.ra) break;
          uregs.pc = uregs.ra - 8;
          ips[i++] = uregs.ra - 8;
+         if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
          continue;
       }
       /* No luck.  We have to give up. */
diff --git a/coregrind/pub_core_gdbserver.h b/coregrind/pub_core_gdbserver.h
index cf135f3..d172a36 100644
--- a/coregrind/pub_core_gdbserver.h
+++ b/coregrind/pub_core_gdbserver.h
@@ -88,6 +88,11 @@
 // guest program.
 extern Bool VG_(gdbserver_report_signal) (Int signo, ThreadId tid);
 
+/* Entry point invoked by scheduler.c to execute the request 
+   VALGRIND_CLIENT_MONITOR_COMMAND.
+   Returns True if command was not recognised. */
+extern Bool VG_(client_monitor_command) (HChar* cmd);
+
 /* software_breakpoint, single step and jump support ------------------------*/
 /* VG_(instrument_for_gdbserver_if_needed) allows to do "standard and easy"
    instrumentation for gdbserver.
diff --git a/coregrind/pub_core_options.h b/coregrind/pub_core_options.h
index a02f398..f1dd80a 100644
--- a/coregrind/pub_core_options.h
+++ b/coregrind/pub_core_options.h
@@ -252,6 +252,14 @@
    be? */
 extern Word VG_(clo_main_stacksize);
 
+/* If the same IP is found twice in a backtrace in a sequence of max
+   VG_(clo_merge_recursive_frames) frames, then the recursive call
+   is merged in the backtrace.
+   Note also that the merge is done during unwinding, to obtain
+   an much as possible significant backtrace.
+   Note that the value is changeable by a gdbsrv command. */
+extern Int VG_(clo_merge_recursive_frames);
+
 /* Delay startup to allow GDB to be attached?  Default: NO */
 extern Bool VG_(clo_wait_for_gdb);
 
diff --git a/docs/xml/manual-core.xml b/docs/xml/manual-core.xml
index 772ac52..70b0989 100644
--- a/docs/xml/manual-core.xml
+++ b/docs/xml/manual-core.xml
@@ -1809,6 +1809,39 @@
     </listitem>
   </varlistentry>
 
+  <varlistentry id="opt.merge-recursive-frames" xreflabel="--merge-recursive-frames">
+    <term>
+      <option><![CDATA[--merge-recursive-frames=<number> [default: 0] ]]></option>
+    </term>
+    <listitem>
+      <para>Some recursive algorithms (such as balanced binary tree
+      implementations) have the property to create many different
+      stack traces, containing cycles of calls.  A cycle is defined by
+      two identical program counters separated by 0 or more other
+      program counters.  Valgrind might then use a lot of memory to
+      record these stack traces, containing repeated uninteresting
+      recursive calls instead of more interesting information such as
+      the function that has initiated the recursive call.
+      </para>
+      <para>The option <option>--merge-recursive-frames=&lt;number&gt;</option>
+      instructs Valgrind to detect and merge recursive call cycles
+      having a size of up to <option>&lt;number&gt;</option>
+      frames. When such a cycle is detected, Valgrind records the
+      cycle in the stack trace as a unique program counter.
+      </para>
+      <para>
+      The value 0 (the default) causes no recursive call merging.
+      A value of 1 will cause stack traces of simple recursive algorithms
+      (for example, a factorial implementation) to be collapsed.
+      A value of 2 will usually be needed to collapsed stack traces produced
+      by recursive algorithms such binary trees, quick sort, ...
+      Higher values might be needed for more complex recursive algorithms.
+      </para>
+      <para>Note: recursive calls are detected based on program counters.
+      The cycles are not detected based on function names. </para>
+   </listitem>
+  </varlistentry>
+
   <varlistentry id="opt.show-emwarns" xreflabel="--show-emwarns">
     <term>
       <option><![CDATA[--show-emwarns=<yes|no> [default: no] ]]></option>
diff --git a/gdbserver_tests/mchelp.stdoutB.exp b/gdbserver_tests/mchelp.stdoutB.exp
index e118ef5..721c23f 100644
--- a/gdbserver_tests/mchelp.stdoutB.exp
+++ b/gdbserver_tests/mchelp.stdoutB.exp
@@ -9,6 +9,7 @@
   v.set gdb_output        : set valgrind output to gdb
   v.set log_output        : set valgrind output to log
   v.set mixed_output      : set valgrind output to log, interactive output to gdb
+  v.set merge-recursive-frames <num> : merge recursive calls in max <num> frames
   v.set vgdb-error <errornr> : debug me at error >= <errornr> 
 
 memcheck monitor commands:
@@ -50,6 +51,7 @@
   v.set gdb_output        : set valgrind output to gdb
   v.set log_output        : set valgrind output to log
   v.set mixed_output      : set valgrind output to log, interactive output to gdb
+  v.set merge-recursive-frames <num> : merge recursive calls in max <num> frames
   v.set vgdb-error <errornr> : debug me at error >= <errornr> 
 debugging valgrind internals monitor commands:
   v.info gdbserver_status : show gdbserver status
diff --git a/gdbserver_tests/mssnapshot.stderrB.exp b/gdbserver_tests/mssnapshot.stderrB.exp
index 451d622..551865f 100644
--- a/gdbserver_tests/mssnapshot.stderrB.exp
+++ b/gdbserver_tests/mssnapshot.stderrB.exp
@@ -11,6 +11,7 @@
   v.set gdb_output        : set valgrind output to gdb
   v.set log_output        : set valgrind output to log
   v.set mixed_output      : set valgrind output to log, interactive output to gdb
+  v.set merge-recursive-frames <num> : merge recursive calls in max <num> frames
   v.set vgdb-error <errornr> : debug me at error >= <errornr> 
 
 massif monitor commands:
diff --git a/include/valgrind.h b/include/valgrind.h
index 315da5b..0f2f969 100644
--- a/include/valgrind.h
+++ b/include/valgrind.h
@@ -4464,8 +4464,8 @@
              errors. */
           VG_USERREQ__COUNT_ERRORS = 0x1201,
 
-          /* Allows a string (gdb monitor command) to be passed to the tool
-             Used for interaction with vgdb/gdb */
+          /* Allows the client program and/or gdbserver to execute a monitor
+             command. */
           VG_USERREQ__GDB_MONITOR_COMMAND = 0x1202,
 
           /* These are useful and can be interpreted by any tool that
@@ -4893,6 +4893,16 @@
     VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \
                                     -1, 0, 0, 0, 0)
 
+/* Execute a monitor command from the client program.
+   If a connection is opened with GDB, the output will be sent
+   according to the output mode set for vgdb.
+   If no connection is opened, output will go to the log output.
+   Returns 1 if command not recognised, 0 otherwise. */
+#define VALGRIND_MONITOR_COMMAND(command)                               \
+   VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__GDB_MONITOR_COMMAND, \
+                                   command, 0, 0, 0, 0)
+
+
 #undef PLAT_x86_darwin
 #undef PLAT_amd64_darwin
 #undef PLAT_x86_win32
diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am
index 23f634c..13ffec8 100644
--- a/memcheck/tests/Makefile.am
+++ b/memcheck/tests/Makefile.am
@@ -184,6 +184,7 @@
 	realloc1.stderr.exp realloc1.vgtest \
 	realloc2.stderr.exp realloc2.vgtest \
 	realloc3.stderr.exp realloc3.vgtest \
+	recursive-merge.stderr.exp recursive-merge.vgtest \
 	sbfragment.stdout.exp sbfragment.stderr.exp sbfragment.vgtest \
 	sem.stderr.exp sem.vgtest \
 	sh-mem.stderr.exp sh-mem.vgtest \
@@ -289,6 +290,7 @@
 	pipe pointer-trace \
 	post-syscall \
 	realloc1 realloc2 realloc3 \
+	recursive-merge \
 	sbfragment \
 	sh-mem sh-mem-random \
 	sigaltstack signal2 sigprocmask static_malloc sigkill \
diff --git a/memcheck/tests/recursive-merge.c b/memcheck/tests/recursive-merge.c
new file mode 100644
index 0000000..c8f5aad
--- /dev/null
+++ b/memcheck/tests/recursive-merge.c
@@ -0,0 +1,71 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <valgrind.h>
+
+void (*fnptr[256])(char*, char*);
+
+#define BODY(f)                                 \
+{                                               \
+   fprintf(stderr, f);                          \
+   calls++;                                     \
+   (*fnptr[(int)*calls])(calls,seq);            \
+}
+
+void stacktrace(char*last, char* callsequence)
+{
+   fprintf(stderr, "\n");
+   VALGRIND_PRINTF_BACKTRACE (callsequence);
+}
+__attribute__((noinline)) void f_a(char *calls, char*seq);
+__attribute__((noinline)) void f_b(char *calls, char*seq);
+__attribute__((noinline)) void f_c(char *calls, char*seq);
+__attribute__((noinline)) void f_d(char *calls, char*seq);
+
+__attribute__((noinline)) void f_a(char *calls, char*seq)
+BODY("a")
+
+__attribute__((noinline)) void f_b(char *calls, char*seq)
+BODY("b")
+
+__attribute__((noinline)) void f_c(char *calls, char*seq)
+BODY("c");
+
+__attribute__((noinline)) void f_d(char *calls, char*seq)
+BODY("d");
+
+void doit (int argc, char**argv)
+{
+   int i;
+   for (i = 1; i < argc; i++) {
+      char* calls = argv[i];
+      char* seq = argv[i];
+      calls--;
+      BODY("test ")
+   }
+}
+
+int main(int argc, char**argv)
+{
+
+   fnptr[0] = stacktrace;
+   fnptr['a'] = f_a;
+   fnptr['b'] = f_b;
+   fnptr['c'] = f_c;
+   fnptr['d'] = f_d;
+
+   doit(argc, argv); // with default value of our argument.
+
+   VALGRIND_MONITOR_COMMAND("v.set merge-recursive-frames 3");
+   doit(argc, argv);
+
+   VALGRIND_MONITOR_COMMAND("v.set merge-recursive-frames 2");
+   doit(argc, argv);
+
+   VALGRIND_MONITOR_COMMAND("v.set merge-recursive-frames 1");
+   doit(argc, argv);
+
+   VALGRIND_MONITOR_COMMAND("v.set merge-recursive-frames 0");
+   doit(argc, argv);
+
+   return 0;
+}
diff --git a/memcheck/tests/recursive-merge.stderr.exp b/memcheck/tests/recursive-merge.stderr.exp
new file mode 100644
index 0000000..e925ac8
--- /dev/null
+++ b/memcheck/tests/recursive-merge.stderr.exp
@@ -0,0 +1,263 @@
+test a
+a   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:56)
+test aa
+aa   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:56)
+test aaa
+aaa   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:56)
+test aaaa
+aaaa   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:56)
+test abab
+abab   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:56)
+test abca
+abca   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_c (recursive-merge.c:31)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:56)
+test abcda
+abcda   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_d (recursive-merge.c:34)
+   by 0x........: f_c (recursive-merge.c:31)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:56)
+merge-recursive-frames value changed from 1 to 3
+test a
+a   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:59)
+test aa
+aa   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:59)
+test aaa
+aaa   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:59)
+test aaaa
+aaaa   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:59)
+test abab
+abab   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:59)
+test abca
+abca   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:59)
+test abcda
+abcda   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_d (recursive-merge.c:34)
+   by 0x........: f_c (recursive-merge.c:31)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:59)
+merge-recursive-frames value changed from 3 to 2
+test a
+a   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:62)
+test aa
+aa   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:62)
+test aaa
+aaa   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:62)
+test aaaa
+aaaa   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:62)
+test abab
+abab   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:62)
+test abca
+abca   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_c (recursive-merge.c:31)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:62)
+test abcda
+abcda   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_d (recursive-merge.c:34)
+   by 0x........: f_c (recursive-merge.c:31)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:62)
+merge-recursive-frames value changed from 2 to 1
+test a
+a   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:65)
+test aa
+aa   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:65)
+test aaa
+aaa   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:65)
+test aaaa
+aaaa   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:65)
+test abab
+abab   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:65)
+test abca
+abca   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_c (recursive-merge.c:31)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:65)
+test abcda
+abcda   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_d (recursive-merge.c:34)
+   by 0x........: f_c (recursive-merge.c:31)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:65)
+merge-recursive-frames value changed from 1 to 0
+test a
+a   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:68)
+test aa
+aa   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:68)
+test aaa
+aaa   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:68)
+test aaaa
+aaaa   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:68)
+test abab
+abab   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:68)
+test abca
+abca   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_c (recursive-merge.c:31)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:68)
+test abcda
+abcda   at 0x........: VALGRIND_PRINTF_BACKTRACE (valgrind.h:...)
+   by 0x........: stacktrace (recursive-merge.c:17)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: f_d (recursive-merge.c:34)
+   by 0x........: f_c (recursive-merge.c:31)
+   by 0x........: f_b (recursive-merge.c:28)
+   by 0x........: f_a (recursive-merge.c:25)
+   by 0x........: doit (recursive-merge.c:43)
+   by 0x........: main (recursive-merge.c:68)
diff --git a/memcheck/tests/recursive-merge.vgtest b/memcheck/tests/recursive-merge.vgtest
new file mode 100644
index 0000000..bfedf73
--- /dev/null
+++ b/memcheck/tests/recursive-merge.vgtest
@@ -0,0 +1,3 @@
+prog: recursive-merge
+args: a aa aaa aaaa abab abca abcda
+vgopts: -q --leak-check=full --merge-recursive-frames=1
diff --git a/none/tests/cmdline1.stdout.exp b/none/tests/cmdline1.stdout.exp
index 9eb70d1..ed662fb 100644
--- a/none/tests/cmdline1.stdout.exp
+++ b/none/tests/cmdline1.stdout.exp
@@ -81,6 +81,8 @@
     --fair-sched=no|yes|try   schedule threads fairly on multicore systems [no]
     --kernel-variant=variant1,variant2,...  known variants: bproc [none]
                               handle non-standard kernel variants
+    --merge-recursive-frames=<number>  merge frames between identical
+           program counters in max <number> frames) [0]
     --show-emwarns=no|yes     show warnings about emulation limits? [no]
     --require-text-symbol=:sonamepattern:symbolpattern    abort run if the
                               stated shared object doesn't have the stated
diff --git a/none/tests/cmdline2.stdout.exp b/none/tests/cmdline2.stdout.exp
index e2b7b6e..bc5a1e1 100644
--- a/none/tests/cmdline2.stdout.exp
+++ b/none/tests/cmdline2.stdout.exp
@@ -81,6 +81,8 @@
     --fair-sched=no|yes|try   schedule threads fairly on multicore systems [no]
     --kernel-variant=variant1,variant2,...  known variants: bproc [none]
                               handle non-standard kernel variants
+    --merge-recursive-frames=<number>  merge frames between identical
+           program counters in max <number> frames) [0]
     --show-emwarns=no|yes     show warnings about emulation limits? [no]
     --require-text-symbol=:sonamepattern:symbolpattern    abort run if the
                               stated shared object doesn't have the stated