From: Julian Seward Date: Thu, 5 Nov 2009 08:55:13 +0000 (+0000) Subject: New flag: --trace-children-skip=patt1,patt2,etc X-Git-Tag: svn/VALGRIND_3_6_0~482 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=426c13704588a1eca0facbd31a3ba536fae9951e;p=thirdparty%2Fvalgrind.git New flag: --trace-children-skip=patt1,patt2,etc Specifies a comma-separated list of executable-names (with "*" and "?" wildcards allowed) that should not be traced into even when --trace-children=yes. Modified version of a patch from Bill Hoffman. Fixes #148932. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@10927 --- diff --git a/coregrind/m_main.c b/coregrind/m_main.c index 190569c86b..b959c15361 100644 --- a/coregrind/m_main.c +++ b/coregrind/m_main.c @@ -121,6 +121,8 @@ static void usage_NORETURN ( Bool debug_help ) " -q --quiet run silently; only print error msgs\n" " -v --verbose be more verbose -- show misc extra info\n" " --trace-children=no|yes Valgrind-ise child processes (follow execve)? [no]\n" +" --trace-children-skip=patt1,patt2,... specifies a list of executables\n" +" that --trace-children=yes should not trace into\n" " --child-silent-after-fork=no|yes omit child output between fork & exec? [no]\n" " --track-fds=no|yes track open file descriptors? [no]\n" " --time-stamp=no|yes add timestamps to log messages? [no]\n" @@ -490,6 +492,8 @@ void main_process_cmd_line_options ( /*OUT*/Bool* logging_to_fd, else if VG_BOOL_CLO(arg, "--dsymutil", VG_(clo_dsymutil)) {} + else if VG_STR_CLO (arg, "--trace-children-skip", VG_(clo_trace_children_skip)) {} + else if VG_BINT_CLO(arg, "--vex-iropt-verbosity", VG_(clo_vex_control).iropt_verbosity, 0, 10) {} else if VG_BINT_CLO(arg, "--vex-iropt-level", diff --git a/coregrind/m_options.c b/coregrind/m_options.c index 3582818194..3d91fd9c59 100644 --- a/coregrind/m_options.c +++ b/coregrind/m_options.c @@ -38,6 +38,7 @@ #include "pub_core_libcprint.h" #include "pub_core_libcproc.h" #include "pub_core_mallocfree.h" +#include "pub_core_seqmatch.h" // VG_(string_match) // See pub_{core,tool}_options.h for explanations of all these. @@ -56,6 +57,7 @@ Bool VG_(clo_xml) = False; HChar* VG_(clo_xml_user_comment) = NULL; Bool VG_(clo_demangle) = True; Bool VG_(clo_trace_children) = False; +HChar* VG_(clo_trace_children_skip) = NULL; Bool VG_(clo_child_silent_after_fork) = False; Char* VG_(clo_log_fname_expanded) = NULL; Char* VG_(clo_xml_fname_expanded) = NULL; @@ -264,6 +266,69 @@ Char* VG_(expand_file_name)(Char* option_name, Char* format) } } +/*====================================================================*/ +/*=== --trace-children= support ===*/ +/*====================================================================*/ + +static HChar const* consume_commas ( HChar const* c ) { + while (*c && *c == ',') { + ++c; + } + return c; +} + +static HChar const* consume_field ( HChar const* c ) { + while (*c && *c != ',') { + ++c; + } + return c; +} + +/* Should we trace into this child executable (across execve etc) ? + This involves considering --trace-children=, --trace-children-skip= + and the name of the executable. */ +Bool VG_(should_we_trace_this_child) ( HChar* child_exe_name ) +{ + // child_exe_name is pulled out of the guest's space. We + // should be at least marginally cautious with it, lest it + // explode or burst into flames unexpectedly. + if (child_exe_name == NULL || VG_(strlen)(child_exe_name) == 0) + return VG_(clo_trace_children); // we know narfink + + // the main logic + // If --trace-children=no, the answer is simply NO. + if (! VG_(clo_trace_children)) + return False; + + // otherwise, return True, unless the exe name matches any of the + // patterns specified by --trace-children-skip=. + if (VG_(clo_trace_children_skip)) { + HChar const* last = VG_(clo_trace_children_skip); + HChar const* name = (HChar const*)child_exe_name; + while (*last) { + Bool matches; + HChar* patt; + HChar const* first = consume_commas(last); + last = consume_field(first); + if (first == last) + break; + vg_assert(last > first); + /* copy the candidate string into a temporary malloc'd block + so we can use VG_(string_match) on it. */ + patt = VG_(calloc)("m_options.swttc.1", last - first + 1, 1); + VG_(memcpy)(patt, first, last - first); + vg_assert(patt[last-first] == 0); + matches = VG_(string_match)(patt, name); + VG_(free)(patt); + if (matches) + return False; + } + } + + // --trace-children=yes, and this particular executable isn't + // excluded + return True; +} /*--------------------------------------------------------------------*/ diff --git a/coregrind/m_syswrap/syswrap-aix5.c b/coregrind/m_syswrap/syswrap-aix5.c index 6108ccee5b..e8280347ce 100644 --- a/coregrind/m_syswrap/syswrap-aix5.c +++ b/coregrind/m_syswrap/syswrap-aix5.c @@ -798,7 +798,8 @@ static void pre_argv_envp(Addr a, ThreadId tid, Char* s1, Char* s2) a += sizeof(char*); } } -static SysRes simple_pre_exec_check(const HChar* exe_name) +static SysRes simple_pre_exec_check ( const HChar* exe_name, + Bool trace_this_child ) { Int fd, ret; SysRes res; @@ -815,7 +816,7 @@ static SysRes simple_pre_exec_check(const HChar* exe_name) // Check we have execute permissions. We allow setuid executables // to be run only in the case when we are not simulating them, that // is, they to be run natively. - setuid_allowed = VG_(clo_trace_children) ? False : True; + setuid_allowed = trace_this_child ? False : True; ret = VG_(check_executable)(NULL/*&is_setuid*/, (HChar*)exe_name, setuid_allowed); if (0 != ret) { @@ -833,6 +834,7 @@ PRE(sys_execve) ThreadState* tst; Int i, j, tot_args; SysRes res; + Bool trace_this_child; PRINT("sys_execve ( %#lx(%s), %#lx, %#lx )", ARG1, (Char*)ARG1, ARG2, ARG3); PRE_REG_READ3(vki_off_t, "execve", @@ -862,10 +864,16 @@ PRE(sys_execve) // SET_STATUS_Failure( VKI_EFAULT ); // return; //} + if (ARG1 == 0 /* obviously bogus */) { + SET_STATUS_Failure( VKI_EFAULT ); + } + + // Decide whether or not we want to follow along + trace_this_child = VG_(should_we_trace_this_child)( (HChar*)ARG1 ); // Do the important checks: it is a file, is executable, permissions are // ok, etc. - res = simple_pre_exec_check((const HChar*)ARG1); + res = simple_pre_exec_check( (const HChar*)ARG1, trace_this_child ); if (res.isError) { SET_STATUS_Failure( res.err ); return; @@ -874,7 +882,7 @@ PRE(sys_execve) /* If we're tracing the child, and the launcher name looks bogus (possibly because launcher.c couldn't figure it out, see comments therein) then we have no option but to fail. */ - if (VG_(clo_trace_children) + if (trace_this_child && (VG_(name_of_launcher) == NULL || VG_(name_of_launcher)[0] != '/')) { SET_STATUS_Failure( VKI_ECHILD ); /* "No child processes" */ @@ -892,7 +900,7 @@ PRE(sys_execve) // Set up the child's exe path. // - if (VG_(clo_trace_children)) { + if (trace_this_child) { // We want to exec the launcher. Get its pre-remembered path. path = VG_(name_of_launcher); @@ -930,7 +938,7 @@ PRE(sys_execve) VG_(env_remove_valgrind_env_stuff)( envp ); } - if (VG_(clo_trace_children)) { + if (trace_this_child) { // Set VALGRIND_LIB in ARG3 (the environment) VG_(env_setenv)( &envp, VALGRIND_LIB, VG_(libdir)); } @@ -943,7 +951,7 @@ PRE(sys_execve) // except that the first VG_(args_for_valgrind_noexecpass) args // are omitted. // - if (!VG_(clo_trace_children)) { + if (!trace_this_child) { argv = (Char**)ARG2; } else { vg_assert( VG_(args_for_valgrind_noexecpass) >= 0 ); diff --git a/coregrind/m_syswrap/syswrap-darwin.c b/coregrind/m_syswrap/syswrap-darwin.c index 86c974f9ae..8c927dc112 100644 --- a/coregrind/m_syswrap/syswrap-darwin.c +++ b/coregrind/m_syswrap/syswrap-darwin.c @@ -2556,7 +2556,8 @@ static void pre_argv_envp(Addr a, ThreadId tid, Char* s1, Char* s2) a += sizeof(char*); } } -static SysRes simple_pre_exec_check(const HChar* exe_name) +static SysRes simple_pre_exec_check ( const HChar* exe_name, + Bool trace_this_child ) { Int fd, ret; SysRes res; @@ -2573,7 +2574,7 @@ static SysRes simple_pre_exec_check(const HChar* exe_name) // Check we have execute permissions. We allow setuid executables // to be run only in the case when we are not simulating them, that // is, they to be run natively. - setuid_allowed = VG_(clo_trace_children) ? False : True; + setuid_allowed = trace_this_child ? False : True; ret = VG_(check_executable)(NULL/*&is_setuid*/, (HChar*)exe_name, setuid_allowed); if (0 != ret) { @@ -2590,6 +2591,7 @@ PRE(posix_spawn) Char* launcher_basename = NULL; Int i, j, tot_args; SysRes res; + Bool trace_this_child; /* args: pid_t* pid char* path @@ -2622,15 +2624,19 @@ PRE(posix_spawn) syswrap-generic.c. */ /* Check that the name at least begins in client-accessible storage. */ - if (!VG_(am_is_valid_for_client)( ARG2, 1, VKI_PROT_READ )) { + if (ARG2 == 0 /* obviously bogus */ + || !VG_(am_is_valid_for_client)( ARG2, 1, VKI_PROT_READ )) { SET_STATUS_Failure( VKI_EFAULT ); return; } + // Decide whether or not we want to follow along + trace_this_child = VG_(should_we_trace_this_child)( (HChar*)ARG2 ); + // Do the important checks: it is a file, is executable, permissions are // ok, etc. We allow setuid executables to run only in the case when // we are not simulating them, that is, they to be run natively. - res = simple_pre_exec_check((const HChar*)ARG2); + res = simple_pre_exec_check( (const HChar*)ARG2, trace_this_child ); if (sr_isError(res)) { SET_STATUS_Failure( sr_Err(res) ); return; @@ -2639,7 +2645,7 @@ PRE(posix_spawn) /* If we're tracing the child, and the launcher name looks bogus (possibly because launcher.c couldn't figure it out, see comments therein) then we have no option but to fail. */ - if (VG_(clo_trace_children) + if (trace_this_child && (VG_(name_of_launcher) == NULL || VG_(name_of_launcher)[0] != '/')) { SET_STATUS_Failure( VKI_ECHILD ); /* "No child processes" */ @@ -2651,7 +2657,7 @@ PRE(posix_spawn) // Set up the child's exe path. // - if (VG_(clo_trace_children)) { + if (trace_this_child) { // We want to exec the launcher. Get its pre-remembered path. path = VG_(name_of_launcher); @@ -2685,7 +2691,7 @@ PRE(posix_spawn) VG_(env_remove_valgrind_env_stuff)( envp ); } - if (VG_(clo_trace_children)) { + if (trace_this_child) { // Set VALGRIND_LIB in ARG5 (the environment) VG_(env_setenv)( &envp, VALGRIND_LIB, VG_(libdir)); } @@ -2698,7 +2704,7 @@ PRE(posix_spawn) // except that the first VG_(args_for_valgrind_noexecpass) args // are omitted. // - if (!VG_(clo_trace_children)) { + if (!trace_this_child) { argv = (Char**)ARG4; } else { vg_assert( VG_(args_for_valgrind) ); diff --git a/coregrind/m_syswrap/syswrap-generic.c b/coregrind/m_syswrap/syswrap-generic.c index 5f66535600..63f8994a35 100644 --- a/coregrind/m_syswrap/syswrap-generic.c +++ b/coregrind/m_syswrap/syswrap-generic.c @@ -2488,7 +2488,7 @@ PRE(sys_execve) ThreadState* tst; Int i, j, tot_args; SysRes res; - Bool setuid_allowed; + Bool setuid_allowed, trace_this_child; PRINT("sys_execve ( %#lx(%s), %#lx, %#lx )", ARG1, (char*)ARG1, ARG2, ARG3); PRE_REG_READ3(vki_off_t, "execve", @@ -2510,15 +2510,19 @@ PRE(sys_execve) doing it. */ /* Check that the name at least begins in client-accessible storage. */ - if (!VG_(am_is_valid_for_client)( ARG1, 1, VKI_PROT_READ )) { + if (ARG1 == 0 /* obviously bogus */ + || !VG_(am_is_valid_for_client)( ARG1, 1, VKI_PROT_READ )) { SET_STATUS_Failure( VKI_EFAULT ); return; } + // Decide whether or not we want to follow along + trace_this_child = VG_(should_we_trace_this_child)( (HChar*)ARG1 ); + // Do the important checks: it is a file, is executable, permissions are // ok, etc. We allow setuid executables to run only in the case when // we are not simulating them, that is, they to be run natively. - setuid_allowed = VG_(clo_trace_children) ? False : True; + setuid_allowed = trace_this_child ? False : True; res = VG_(pre_exec_check)((const Char*)ARG1, NULL, setuid_allowed); if (sr_isError(res)) { SET_STATUS_Failure( sr_Err(res) ); @@ -2528,7 +2532,7 @@ PRE(sys_execve) /* If we're tracing the child, and the launcher name looks bogus (possibly because launcher.c couldn't figure it out, see comments therein) then we have no option but to fail. */ - if (VG_(clo_trace_children) + if (trace_this_child && (VG_(name_of_launcher) == NULL || VG_(name_of_launcher)[0] != '/')) { SET_STATUS_Failure( VKI_ECHILD ); /* "No child processes" */ @@ -2546,7 +2550,7 @@ PRE(sys_execve) // Set up the child's exe path. // - if (VG_(clo_trace_children)) { + if (trace_this_child) { // We want to exec the launcher. Get its pre-remembered path. path = VG_(name_of_launcher); @@ -2584,7 +2588,7 @@ PRE(sys_execve) VG_(env_remove_valgrind_env_stuff)( envp ); } - if (VG_(clo_trace_children)) { + if (trace_this_child) { // Set VALGRIND_LIB in ARG3 (the environment) VG_(env_setenv)( &envp, VALGRIND_LIB, VG_(libdir)); } @@ -2597,7 +2601,7 @@ PRE(sys_execve) // except that the first VG_(args_for_valgrind_noexecpass) args // are omitted. // - if (!VG_(clo_trace_children)) { + if (!trace_this_child) { argv = (Char**)ARG2; } else { vg_assert( VG_(args_for_valgrind) ); diff --git a/coregrind/pub_core_options.h b/coregrind/pub_core_options.h index 37242b5af0..e7b6abe06c 100644 --- a/coregrind/pub_core_options.h +++ b/coregrind/pub_core_options.h @@ -61,6 +61,9 @@ extern Int VG_(clo_sanity_level); extern Bool VG_(clo_demangle); /* Simulate child processes? default: NO */ extern Bool VG_(clo_trace_children); +/* String containing comma-separated patterns for executable names + that should not be traced into even when --trace-children=yes */ +extern HChar* VG_(clo_trace_children_skip); /* After a fork, the child's output can become confusingly intermingled with the parent's output. This is especially problematic when VG_(clo_xml) is True. Setting @@ -182,6 +185,10 @@ extern void VG_(err_missing_prog) ( void ); __attribute__((noreturn)) extern void VG_(err_config_error) ( Char* msg ); +/* Should we trace into this child executable (across execve etc) ? + This involves considering --trace-children=, --trace-children-skip= + and the name of the executable. */ +extern Bool VG_(should_we_trace_this_child) ( HChar* child_exe_name ); #endif // __PUB_CORE_OPTIONS_H diff --git a/docs/xml/manual-core.xml b/docs/xml/manual-core.xml index 924c75e46f..24ab6aeea3 100644 --- a/docs/xml/manual-core.xml +++ b/docs/xml/manual-core.xml @@ -660,6 +660,32 @@ in most cases. We group the available options by rough categories. + + + + + + This option only has an effect when + is specified. It allows + for some children to be skipped. The option takes a comma + separated list of patterns for the names of child executables + that Valgrind should not trace into. Patterns may include the + metacharacters ? + and *, which have the usual + meaning. + + This can be useful for pruning uninteresting branches from a + tree of processes being run on Valgrind. But you should be + careful when using it. When Valgrind skips tracing into an + executable, it doesn't just skip tracing that executable, it + also skips tracing any of that executable's child processes. + In other words, the flag doesn't merely cause tracing to stop + at the specified executables -- it skips tracing of entire + process subtrees rooted at any of the specified + executables. + + + diff --git a/none/tests/cmdline1.stdout.exp b/none/tests/cmdline1.stdout.exp index 2deb6bd589..903f72fbca 100644 --- a/none/tests/cmdline1.stdout.exp +++ b/none/tests/cmdline1.stdout.exp @@ -10,6 +10,8 @@ usage: valgrind [options] prog-and-args -q --quiet run silently; only print error msgs -v --verbose be more verbose -- show misc extra info --trace-children=no|yes Valgrind-ise child processes (follow execve)? [no] + --trace-children-skip=patt1,patt2,... specifies a list of executables + that --trace-children=yes should not trace into --child-silent-after-fork=no|yes omit child output between fork & exec? [no] --track-fds=no|yes track open file descriptors? [no] --time-stamp=no|yes add timestamps to log messages? [no] diff --git a/none/tests/cmdline2.stdout.exp b/none/tests/cmdline2.stdout.exp index 01df9ad010..14c192b0d5 100644 --- a/none/tests/cmdline2.stdout.exp +++ b/none/tests/cmdline2.stdout.exp @@ -10,6 +10,8 @@ usage: valgrind [options] prog-and-args -q --quiet run silently; only print error msgs -v --verbose be more verbose -- show misc extra info --trace-children=no|yes Valgrind-ise child processes (follow execve)? [no] + --trace-children-skip=patt1,patt2,... specifies a list of executables + that --trace-children=yes should not trace into --child-silent-after-fork=no|yes omit child output between fork & exec? [no] --track-fds=no|yes track open file descriptors? [no] --time-stamp=no|yes add timestamps to log messages? [no]