diff --git a/NEWS b/NEWS index dfef85304..81c34e975 100644 --- a/NEWS +++ b/NEWS @@ -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= 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 8c5f2c131..8a9c9697a 100644 --- a/coregrind/m_gdbserver/remote-utils.c +++ b/coregrind/m_gdbserver/remote-utils.c @@ -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. */ diff --git a/coregrind/m_gdbserver/server.c b/coregrind/m_gdbserver/server.c index 90340a65e..c4f3850a8 100644 --- a/coregrind/m_gdbserver/server.c +++ b/coregrind/m_gdbserver/server.c @@ -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 : merge recursive calls in max frames\n" " v.set vgdb-error : debug me at error >= \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) diff --git a/coregrind/m_gdbserver/server.h b/coregrind/m_gdbserver/server.h index f68f11ecd..655a2abbb 100644 --- a/coregrind/m_gdbserver/server.h +++ b/coregrind/m_gdbserver/server.h @@ -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) diff --git a/coregrind/m_main.c b/coregrind/m_main.c index ffa48f2c6..ad028c859 100644 --- a/coregrind/m_main.c +++ b/coregrind/m_main.c @@ -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= merge frames between identical\n" +" program counters in max 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); diff --git a/coregrind/m_options.c b/coregrind/m_options.c index 323fec4ba..dbdb1244d 100644 --- a/coregrind/m_options.c +++ b/coregrind/m_options.c @@ -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; diff --git a/coregrind/m_scheduler/scheduler.c b/coregrind/m_scheduler/scheduler.c index 248dc3595..d6905e49c 100644 --- a/coregrind/m_scheduler/scheduler.c +++ b/coregrind/m_scheduler/scheduler.c @@ -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: diff --git a/coregrind/m_stacktrace.c b/coregrind/m_stacktrace.c index 28e999cd2..49964b055 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 @@ 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. */ diff --git a/coregrind/pub_core_gdbserver.h b/coregrind/pub_core_gdbserver.h index cf135f3eb..d172a3629 100644 --- a/coregrind/pub_core_gdbserver.h +++ b/coregrind/pub_core_gdbserver.h @@ -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. diff --git a/coregrind/pub_core_options.h b/coregrind/pub_core_options.h index a02f398ed..f1dd80a2c 100644 --- a/coregrind/pub_core_options.h +++ b/coregrind/pub_core_options.h @@ -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); diff --git a/docs/xml/manual-core.xml b/docs/xml/manual-core.xml index 772ac5269..70b0989ef 100644 --- a/docs/xml/manual-core.xml +++ b/docs/xml/manual-core.xml @@ -1809,6 +1809,39 @@ need to use them. + + + + + + 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. + + The option + instructs Valgrind to detect and merge recursive call cycles + having a size of up to + frames. When such a cycle is detected, Valgrind records the + cycle in the stack trace as a unique program counter. + + + 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. + + Note: recursive calls are detected based on program counters. + The cycles are not detected based on function names. + + + diff --git a/gdbserver_tests/mchelp.stdoutB.exp b/gdbserver_tests/mchelp.stdoutB.exp index e118ef55e..721c23f63 100644 --- a/gdbserver_tests/mchelp.stdoutB.exp +++ b/gdbserver_tests/mchelp.stdoutB.exp @@ -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 : merge recursive calls in max frames v.set vgdb-error : debug me at error >= 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 : merge recursive calls in max frames v.set vgdb-error : debug me at error >= 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 451d62206..551865f32 100644 --- a/gdbserver_tests/mssnapshot.stderrB.exp +++ b/gdbserver_tests/mssnapshot.stderrB.exp @@ -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 : merge recursive calls in max frames v.set vgdb-error : debug me at error >= massif monitor commands: diff --git a/include/valgrind.h b/include/valgrind.h index 315da5b0f..0f2f96951 100644 --- a/include/valgrind.h +++ b/include/valgrind.h @@ -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 diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am index 23f634c37..13ffec856 100644 --- a/memcheck/tests/Makefile.am +++ b/memcheck/tests/Makefile.am @@ -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 \ diff --git a/memcheck/tests/recursive-merge.c b/memcheck/tests/recursive-merge.c new file mode 100644 index 000000000..c8f5aadf6 --- /dev/null +++ b/memcheck/tests/recursive-merge.c @@ -0,0 +1,71 @@ +#include +#include +#include + +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 000000000..e925ac84e --- /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 000000000..bfedf735c --- /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 9eb70d1ac..ed662fbf6 100644 --- a/none/tests/cmdline1.stdout.exp +++ b/none/tests/cmdline1.stdout.exp @@ -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= merge frames between identical + program counters in max 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 e2b7b6ed1..bc5a1e1c6 100644 --- a/none/tests/cmdline2.stdout.exp +++ b/none/tests/cmdline2.stdout.exp @@ -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= merge frames between identical + program counters in max 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