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=<number></option>
+ instructs Valgrind to detect and merge recursive call cycles
+ having a size of up to <option><number></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