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
This commit is contained in:
Philippe Waroquiers 2013-01-20 17:11:58 +00:00
parent ef7a42868a
commit 6fb1158a78
20 changed files with 526 additions and 19 deletions

12
NEWS
View File

@ -30,6 +30,18 @@ Release 3.9.0 (?? ?????? 201?)
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"

View File

@ -771,19 +771,23 @@ int putpkt (char *buf)
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. */

View File

@ -106,6 +106,13 @@ void reset_valgrind_sink(const char *info)
}
}
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 @@ int handle_gdb_valgrind_command (char* mon, OutputSink* sink_wanted_at_return)
" 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 @@ int handle_gdb_valgrind_command (char* mon, OutputSink* sink_wanted_at_return)
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 @@ int handle_gdb_valgrind_command (char* mon, OutputSink* sink_wanted_at_return)
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 @@ void handle_set (char *arg_own_buf, int *new_packet_len_p)
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)

View File

@ -63,7 +63,7 @@ extern void gdbserver_terminate (void);
/* 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 @@ extern void remote_finish(FinishReason reason);
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)

View File

@ -193,6 +193,8 @@ static void usage_NORETURN ( Bool debug_help )
" --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 @@ void main_process_cmd_line_options ( /*OUT*/Bool* logging_to_fd,
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);

View File

@ -105,6 +105,7 @@ Int VG_(clo_core_redzone_size) = CORE_REDZONE_DEFAULT_SZB;
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;

View File

@ -1881,6 +1881,13 @@ void do_client_request ( ThreadId tid )
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:

View File

@ -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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
}
uregs.ia = uregs.lr - 1;
ips[i++] = uregs.lr - 1;
if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
continue;
}
@ -884,6 +916,7 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
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. */

View File

@ -88,6 +88,11 @@ extern void VG_(invoke_gdbserver) ( int check );
// 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.

View File

@ -252,6 +252,14 @@ extern Word VG_(clo_max_stackframe);
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);

View File

@ -1809,6 +1809,39 @@ need to use them.</para>
</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>

View File

@ -9,6 +9,7 @@ general valgrind monitor commands:
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 @@ general valgrind monitor commands:
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

View File

@ -11,6 +11,7 @@ general valgrind monitor commands:
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:

View File

@ -4464,8 +4464,8 @@ typedef
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_PRINTF_BACKTRACE(const char *format, ...)
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

View File

@ -184,6 +184,7 @@ EXTRA_DIST = \
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 @@ check_PROGRAMS = \
pipe pointer-trace \
post-syscall \
realloc1 realloc2 realloc3 \
recursive-merge \
sbfragment \
sh-mem sh-mem-random \
sigaltstack signal2 sigprocmask static_malloc sigkill \

View File

@ -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;
}

View File

@ -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)

View File

@ -0,0 +1,3 @@
prog: recursive-merge
args: a aa aaa aaaa abab abca abcda
vgopts: -q --leak-check=full --merge-recursive-frames=1

View File

@ -81,6 +81,8 @@ usage: valgrind [options] prog-and-args
--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

View File

@ -81,6 +81,8 @@ usage: valgrind [options] prog-and-args
--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