Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions callgrind/dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,58 @@ Int CLG_(get_dump_counter)(void)
return out_counter;
}

/* Restart part numbering in a fork child (see clg_atfork_child): its
* dumps go to a fresh per-pid file whose parts start at 1, like an
* exec'd child. init_dumps() would reset the counter anyway on the pid
* change, but only at the first dump - too late for the part number
* this process forwards to its own children in the meantime. */
void CLG_(reset_dump_counter)(void)
{
out_counter = 0;
}

/* Children spawned by this process, each tagged with the dump part that was
* being measured when the fork happened. Emitted as "desc: Spawned pid:"
* lines in that part's header so the spawn tree can be rebuilt (see
* clg_atfork_parent). */
typedef struct { Int part; Int pid; } SpawnedChild;
static SpawnedChild* spawned_children = 0;
static Int n_spawned = 0;
static Int spawned_capacity = 0;

void CLG_(record_spawned_child)(Int part, Int child_pid)
{
if (n_spawned == spawned_capacity) {
spawned_capacity = spawned_capacity ? spawned_capacity * 2 : 8;
spawned_children = VG_(realloc)("cl.dump.spawn.1", spawned_children,
spawned_capacity * sizeof(SpawnedChild));
}
spawned_children[n_spawned].part = part;
spawned_children[n_spawned].pid = child_pid;
n_spawned++;
}

/* A fork child inherits the parent's list but none of its spawns are its
* own; drop them so it only reports children it spawns itself. */
void CLG_(forget_spawned_children)(void)
{
n_spawned = 0;
}

/* Drop the records already emitted for the given part. A spawn is tagged with
* the part active when the fork happened, so once that part is written no
* later part can reference it; keeping them would grow the list and rescan
* cost without bound in a fork-heavy run. */
static void discard_spawned_children_of_part(Int part)
{
Int kept = 0;
for (Int s = 0; s < n_spawned; s++) {
if (spawned_children[s].part != part)
spawned_children[kept++] = spawned_children[s];
}
n_spawned = kept;
}

/*------------------------------------------------------------*/
/*--- Output file related stuff ---*/
/*------------------------------------------------------------*/
Expand Down Expand Up @@ -1373,6 +1425,16 @@ static VgFile *new_dumpfile(thread_info* ti, const HChar* trigger)
}

VG_(fprintf)(fp, "\npart: %d\n", out_counter);

/* Children this process spawned while this part was being measured, so
* the spawn tree can be rebuilt: a child pid found here is attributed to
* this (pid, part). Per-part, not in the once-per-file header, so a child
* spawned during a later part is still recorded. "desc:" lines so other
* consumers of the format (kcachegrind, callgrind_annotate) ignore them. */
for (int s = 0; s < n_spawned; s++) {
if (spawned_children[s].part == out_counter)
VG_(fprintf)(fp, "desc: Spawned pid: %d\n", spawned_children[s].pid);
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.
if (CLG_(clo).separate_threads) {
const HChar* tname;

Expand Down Expand Up @@ -1721,6 +1783,8 @@ static void print_bbccs(const HChar* trigger, Bool only_current_thread)
CLG_(drop_retired_threads)();
}

discard_spawned_children_of_part(out_counter);

free_dump_array();
}

Expand Down
3 changes: 3 additions & 0 deletions callgrind/global.h
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,9 @@ void CLG_(set_instrument_state)(const HChar*,Bool);
void CLG_(dump_profile)(const HChar* trigger,Bool only_current_thread);
void CLG_(zero_all_cost)(Bool only_current_thread);
Int CLG_(get_dump_counter)(void);
void CLG_(reset_dump_counter)(void);
void CLG_(record_spawned_child)(Int part, Int child_pid);
void CLG_(forget_spawned_children)(void);
void CLG_(fini)(Int exitcode);

/* from bb.c */
Expand Down
63 changes: 63 additions & 0 deletions callgrind/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2061,6 +2061,66 @@ void CLG_(fini)(Int exitcode)
}


/*--------------------------------------------------------------------*/
/*--- Subprocess tracking ---*/
/*--------------------------------------------------------------------*/

/* Extra valgrind args passed to the child's valgrind when a traced process
* execs (--trace-children=yes), see VG_(needs_child_exec_args): the current
* instrumentation state, so a process spawned while instrumentation is
* enabled is instrumented from the start. A plain fork inherits the state
* with the address space; this forwards it across the exec, which otherwise
* resets it to --instr-atstart.
*/
static HChar clg_child_instr_arg[32];
static const HChar* clg_child_exec_args[2];

static const HChar** clg_get_child_exec_args(void)
{
Int n = 0;
VG_(sprintf)(clg_child_instr_arg, "--instr-atstart=%s",
CLG_(instrument_state) ? "yes" : "no");
clg_child_exec_args[n++] = clg_child_instr_arg;
clg_child_exec_args[n] = NULL;
return clg_child_exec_args;
}

/* Fork parent hook: this process just spawned child_pid. Record the edge
* against the part being measured right now (out_counter+1 is the part the
* next dump will get), so the dump attributes the child to that part. The
* spawn tree is rebuilt from these edges: each process names the children
* it created, and in which of its own parts.
*/
static void clg_atfork_parent(ThreadId tid, Int child_pid)
{
CLG_(record_spawned_child)(CLG_(get_dump_counter)() + 1, child_pid);
}

/* Fork child hook: costs accumulated before the fork belong to - and are
* dumped by - the parent; zero everything so this process only reports its
* own work. Threads other than the forking one do not exist in the child;
* zeroing their copied state means they are skipped at dump time (zero
* delta). The part counter restarts at 1, matching an exec'd child.
*/
static void clg_atfork_child(ThreadId tid)
{
CLG_(reset_dump_counter)();
CLG_(forget_spawned_children)();

CLG_(zero_all_cost)(False);
if (CLG_(clo).separate_threads)
CLG_(drop_retired_threads)();

/* The fork itself is a syscall in flight: its start time was stamped
* in the parent, but the thread CPU clock restarts in the child, so
* the delta computed at the syscall exit would underflow. Re-stamp
* from the child's clocks. */
if (CLG_(clo).collect_systime != systime_no)
collect_time(&syscalltime[tid],
CLG_(clo).collect_systime == systime_nsec
? &syscallcputime[tid] : NULL);
}

/*--------------------------------------------------------------------*/
/*--- Setup ---*/
/*--------------------------------------------------------------------*/
Expand Down Expand Up @@ -2209,13 +2269,16 @@ void CLG_(pre_clo_init)(void)
CLG_(print_debug_usage));

VG_(needs_client_requests)(CLG_(handle_client_request));
VG_(needs_child_exec_args)(clg_get_child_exec_args);
VG_(needs_print_stats) (clg_print_stats);

VG_(track_start_client_code) ( & clg_start_client_code_callback );
VG_(track_pre_deliver_signal) ( & CLG_(pre_signal) );
VG_(track_post_deliver_signal)( & CLG_(post_signal) );
VG_(track_pre_thread_ll_exit) ( & CLG_(pre_thread_ll_exit) );

VG_(atfork)(NULL, clg_atfork_parent, clg_atfork_child);

CLG_(set_clo_defaults)();

}
Expand Down
12 changes: 6 additions & 6 deletions coregrind/m_libcproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1144,17 +1144,17 @@ UInt VG_(get_user_milliseconds)(void)
------------------------------------------------------------------ */

struct atfork {
vg_atfork_t pre;
vg_atfork_t parent;
vg_atfork_t child;
vg_atfork_t pre;
vg_atfork_parent_t parent;
vg_atfork_t child;
};

#define VG_MAX_ATFORK 10

static struct atfork atforks[VG_MAX_ATFORK];
static Int n_atfork = 0;

void VG_(atfork)(vg_atfork_t pre, vg_atfork_t parent, vg_atfork_t child)
void VG_(atfork)(vg_atfork_t pre, vg_atfork_parent_t parent, vg_atfork_t child)
{
Int i;

Expand Down Expand Up @@ -1185,13 +1185,13 @@ void VG_(do_atfork_pre)(ThreadId tid)
(*atforks[i].pre)(tid);
}

void VG_(do_atfork_parent)(ThreadId tid)
void VG_(do_atfork_parent)(ThreadId tid, Int child_pid)
{
Int i;

for (i = 0; i < n_atfork; i++)
if (atforks[i].parent != NULL)
(*atforks[i].parent)(tid);
(*atforks[i].parent)(tid, child_pid);
}

void VG_(do_atfork_child)(ThreadId tid)
Expand Down
15 changes: 15 additions & 0 deletions coregrind/m_syswrap/syswrap-darwin.c
Original file line number Diff line number Diff line change
Expand Up @@ -3612,6 +3612,17 @@ PRE(posix_spawn)
if (!trace_this_child) {
argv = (HChar**)ARG4;
} else {
// Extra valgrind args the tool wants to forward to the child's
// valgrind (e.g. current instrumentation state); appended after
// VG_(args_for_valgrind) so they override the original options.
const HChar** tool_args = NULL;
Int n_tool_args = 0;
if (VG_(needs).child_exec_args) {
tool_args = VG_(tdict).tool_child_exec_args();
if (tool_args)
while (tool_args[n_tool_args])
n_tool_args++;
}
vg_assert( VG_(args_for_valgrind) );
vg_assert( VG_(args_for_valgrind_noexecpass) >= 0 );
vg_assert( VG_(args_for_valgrind_noexecpass)
Expand All @@ -3622,6 +3633,8 @@ PRE(posix_spawn)
// V's args
tot_args += VG_(sizeXA)( VG_(args_for_valgrind) );
tot_args -= VG_(args_for_valgrind_noexecpass);
// tool-provided args for the child's valgrind
tot_args += n_tool_args;
// name of client exe
tot_args++;
// args for client exe, skipping [0]
Expand All @@ -3641,6 +3654,8 @@ PRE(posix_spawn)
continue;
argv[j++] = * (HChar**) VG_(indexXA)( VG_(args_for_valgrind), i );
}
for (i = 0; i < n_tool_args; i++)
argv[j++] = (HChar*)tool_args[i];
argv[j++] = (HChar*)ARG2;
if (arg2copy && arg2copy[0])
for (i = 1; arg2copy[i]; i++)
Expand Down
4 changes: 2 additions & 2 deletions coregrind/m_syswrap/syswrap-freebsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ SysRes ML_(do_fork) ( ThreadId tid )

} else {
/* parent */
VG_(do_atfork_parent)(tid);
VG_(do_atfork_parent)(tid, (Int)sr_Res(res));

if (VG_(clo_trace_syscalls)) {
VG_(printf)(" clone(fork): process %d created child %lu\n",
Expand Down Expand Up @@ -5770,7 +5770,7 @@ PRE(sys_pdfork)
/* restore signal mask */
VG_(sigprocmask)(VKI_SIG_SETMASK, &pdfork_saved_mask, NULL);
} else {
VG_(do_atfork_parent)(tid);
VG_(do_atfork_parent)(tid, child_pid);

PRINT(" fork: process %d created child %d\n", VG_(getpid)(), child_pid);

Expand Down
19 changes: 17 additions & 2 deletions coregrind/m_syswrap/syswrap-generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -3547,16 +3547,29 @@ void handle_pre_sys_execve(ThreadId tid, SyscallStatus *status, Addr pathname,
if (!trace_this_child) {
argv = (HChar**)(Addr)arg_2;
} else {
// Extra valgrind args the tool wants to forward to the child's
// valgrind (e.g. current instrumentation state); appended after
// VG_(args_for_valgrind) so they override the original options.
const HChar** tool_args = NULL;
Int n_tool_args = 0;
if (VG_(needs).child_exec_args) {
tool_args = VG_(tdict).tool_child_exec_args();
if (tool_args)
while (tool_args[n_tool_args])
n_tool_args++;
}
vg_assert( VG_(args_for_valgrind) );
vg_assert( VG_(args_for_valgrind_noexecpass) >= 0 );
vg_assert( VG_(args_for_valgrind_noexecpass)
vg_assert( VG_(args_for_valgrind_noexecpass)
<= VG_(sizeXA)( VG_(args_for_valgrind) ) );
/* how many args in total will there be? */
// launcher basename
tot_args = 1;
// V's args
tot_args += VG_(sizeXA)( VG_(args_for_valgrind) );
tot_args -= VG_(args_for_valgrind_noexecpass);
// tool-provided args for the child's valgrind
tot_args += n_tool_args;
// name of client exe
tot_args++;
// args for client exe, skipping [0]
Expand All @@ -3576,6 +3589,8 @@ void handle_pre_sys_execve(ThreadId tid, SyscallStatus *status, Addr pathname,
continue;
argv[j++] = * (HChar**) VG_(indexXA)( VG_(args_for_valgrind), i );
}
for (i = 0; i < n_tool_args; i++)
argv[j++] = (HChar*)tool_args[i];
argv[j++] = (HChar*)(Addr)pathname;
if (arg2copy && arg2copy[0])
for (i = 1; arg2copy[i]; i++)
Expand Down Expand Up @@ -3919,7 +3934,7 @@ PRE(sys_fork)
/* restore signal mask */
VG_(sigprocmask)(VKI_SIG_SETMASK, &fork_saved_mask, NULL);
} else {
VG_(do_atfork_parent)(tid);
VG_(do_atfork_parent)(tid, child_pid);

PRINT(" fork: process %d created child %d\n", VG_(getpid)(), child_pid);

Expand Down
2 changes: 1 addition & 1 deletion coregrind/m_syswrap/syswrap-linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ static SysRes ML_(do_fork_clone) ( ThreadId tid, UInt flags,
else
if (!sr_isError(res) && sr_Res(res) > 0) {
/* parent */
VG_(do_atfork_parent)(tid);
VG_(do_atfork_parent)(tid, (Int)sr_Res(res));

if (VG_(clo_trace_syscalls))
VG_(printf)(" clone(fork): process %d created child %" FMT_REGWORD "u\n",
Expand Down
18 changes: 17 additions & 1 deletion coregrind/m_syswrap/syswrap-solaris.c
Original file line number Diff line number Diff line change
Expand Up @@ -3800,6 +3800,18 @@ PRE(sys_execve)
else {
Int tot_args;

/* Extra valgrind args the tool wants to forward to the child's
valgrind (e.g. current instrumentation state); appended after
VG_(args_for_valgrind) so they override the original options. */
const HChar** tool_args = NULL;
Int n_tool_args = 0;
if (VG_(needs).child_exec_args) {
tool_args = VG_(tdict).tool_child_exec_args();
if (tool_args)
while (tool_args[n_tool_args])
n_tool_args++;
}

vg_assert(VG_(args_for_valgrind));
vg_assert(VG_(args_for_valgrind_noexecpass) >= 0);
vg_assert(VG_(args_for_valgrind_noexecpass)
Expand All @@ -3811,6 +3823,8 @@ PRE(sys_execve)
/* V's args */
tot_args += VG_(sizeXA)(VG_(args_for_valgrind));
tot_args -= VG_(args_for_valgrind_noexecpass);
/* tool-provided args for the child's valgrind */
tot_args += n_tool_args;
/* name of client exe */
tot_args++;
/* args for client exe, skipping [0] */
Expand All @@ -3828,6 +3842,8 @@ PRE(sys_execve)
continue;
argv[j++] = *(HChar**)VG_(indexXA)(VG_(args_for_valgrind), i);
}
for (i = 0; i < n_tool_args; i++)
argv[j++] = CONST_CAST(HChar *, tool_args[i]);
argv[j++] = CONST_CAST(HChar *, fname);
if (arg2copy[0] != NULL)
for (i = 1; arg2copy[i]; i++)
Expand Down Expand Up @@ -6636,7 +6652,7 @@ PRE(sys_forksys)
# endif /* SOLARIS_PT_SUNDWTRACE_THRP */
}
else {
VG_(do_atfork_parent)(tid);
VG_(do_atfork_parent)(tid, (Int)RES);

/* Print information about the fork. */
PRINT(" fork: process %d created child %d\n", VG_(getpid)(),
Expand Down
1 change: 0 additions & 1 deletion coregrind/m_threadstate.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ ThreadId VG_(get_running_tid)(void)
return VG_(running_tid);
}

// This function is for tools to call.
const HChar* VG_(get_thread_name)(ThreadId tid)
{
if (!VG_(is_valid_tid)(tid)) return NULL;
Expand Down
Loading