" -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"
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",
#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.
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;
}
}
+/*====================================================================*/
+/*=== --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;
+}
/*--------------------------------------------------------------------*/
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;
// 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) {
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",
// 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;
/* 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" */
// 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);
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));
}
// 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 );
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;
// 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) {
Char* launcher_basename = NULL;
Int i, j, tot_args;
SysRes res;
+ Bool trace_this_child;
/* args: pid_t* pid
char* path
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;
/* 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" */
// 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);
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));
}
// 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) );
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",
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) );
/* 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" */
// 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);
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));
}
// 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) );
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
__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
</listitem>
</varlistentry>
+ <varlistentry id="opt.trace-children-skip" xreflabel="--trace-children-skip">
+ <term>
+ <option><![CDATA[--trace-children-skip=patt1,patt2 ]]></option>
+ </term>
+ <listitem>
+ <para>This option only has an effect when
+ <option>--trace-children=yes</option> 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 <computeroutput>?</computeroutput>
+ and <computeroutput>*</computeroutput>, which have the usual
+ meaning.</para>
+ <para>
+ 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.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="opt.child-silent-after-fork"
xreflabel="--child-silent-after-fork">
<term>
-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]
-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]