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
14 changes: 7 additions & 7 deletions callgrind/bbcc.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ BBCC* lookup_bbcc(BB* bb, Context* cxt)
/* if we don't dump threads separate, tid doesn't have to match */
return bbcc;
}
if (bbcc->tid == CLG_(current_tid)) return bbcc;
if (bbcc->tid == CLG_(current_thread_tid)()) return bbcc;
}

CLG_(stat).bbcc_lru_misses++;
Expand All @@ -178,8 +178,8 @@ BBCC* lookup_bbcc(BB* bb, Context* cxt)
bbcc = bbcc->next;
}

CLG_DEBUG(2," lookup_bbcc(BB %#lx, Cxt %u, fn '%s'): %p (tid %u)\n",
bb_addr(bb), cxt->base_number, cxt->fn[0]->name,
CLG_DEBUG(2," lookup_bbcc(BB %#lx, Cxt %u, fn '%s'): %p (tid %d)\n",
bb_addr(bb), cxt->base_number, cxt->fn[0]->name,
bbcc, bbcc ? bbcc->tid : 0);

CLG_DEBUGIF(2)
Expand Down Expand Up @@ -274,7 +274,7 @@ BBCC* new_bbcc(BB* bb)
sizeof(BBCC) +
(bb->cjmp_count+1) * sizeof(JmpData));
bbcc->bb = bb;
bbcc->tid = CLG_(current_tid);
bbcc->tid = CLG_(current_thread_tid)();

bbcc->ret_counter = 0;
bbcc->skipped = 0;
Expand Down Expand Up @@ -381,8 +381,8 @@ static BBCC* clone_bbcc(BBCC* orig, Context* cxt, Int rec_index)

if (rec_index == 0) {

/* hash insertion is only allowed if tid or cxt is different */
CLG_ASSERT((orig->tid != CLG_(current_tid)) ||
/* hash insertion is only allowed if thread or cxt is different */
CLG_ASSERT((orig->tid != CLG_(current_thread_tid)()) ||
(orig->cxt != cxt));

bbcc->rec_index = 0;
Expand All @@ -394,7 +394,7 @@ static BBCC* clone_bbcc(BBCC* orig, Context* cxt, Int rec_index)
}
else {
if (CLG_(clo).separate_threads)
CLG_ASSERT(orig->tid == CLG_(current_tid));
CLG_ASSERT(orig->tid == CLG_(current_thread_tid)());

CLG_ASSERT(orig->cxt == cxt);
CLG_ASSERT(orig->rec_array);
Expand Down
132 changes: 119 additions & 13 deletions callgrind/dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ static Int out_counter = 0;
static HChar* out_file = 0;
static Bool dumps_initialized = False;

/* With --combine-dumps, whether the single output file already carries its
* format header. A part can be skipped entirely (no thread had costs), so the
* first actual write may happen at out_counter > 1 yet still needs the header;
* hence tracked as file state, not derived from out_counter. */
static Bool combined_header_written = False;

/* Command */
static HChar *cmdbuf;

Expand Down Expand Up @@ -1290,12 +1296,16 @@ void file_err(void)
* Create a new dump file and write header.
*
* Naming: <CLG_(clo).filename_base>.<pid>[.<part>][-<tid>]
* <part> is skipped for final dump (trigger==0)
* <tid> is skipped for thread 1 with CLG_(clo).separate_threads=no
* <part> is skipped for final dump (trigger==0)
* <tid> is the thread's kernel id, skipped with
* CLG_(clo).separate_threads=no
*
* ti is the thread being dumped (used for its tid and name); its costs
* must already be installed as the current state.
*
* Returns the file descriptor, and -1 on error (no write permission)
*/
static VgFile *new_dumpfile(int tid, const HChar* trigger)
static VgFile *new_dumpfile(thread_info* ti, const HChar* trigger)
{
Bool appending = False;
int i;
Expand All @@ -1305,21 +1315,25 @@ static VgFile *new_dumpfile(int tid, const HChar* trigger)
CLG_ASSERT(dumps_initialized);
CLG_ASSERT(filename != 0);

/* A thread that only ran a handful of blocks may not have had its kernel
* id resolved yet; make sure the suffix and header below carry it. */
CLG_(resolve_thread_tid)(ti);

if (!CLG_(clo).combine_dumps) {
i = VG_(sprintf)(filename, "%s", out_file);

if (trigger)
i += VG_(sprintf)(filename+i, ".%d", out_counter);

if (CLG_(clo).separate_threads)
VG_(sprintf)(filename+i, "-%02d", tid);
VG_(sprintf)(filename+i, "-%02d", ti->tid);

fp = VG_(fopen)(filename, VKI_O_WRONLY|VKI_O_TRUNC, 0);
}
else {
VG_(sprintf)(filename, "%s", out_file);
fp = VG_(fopen)(filename, VKI_O_WRONLY|VKI_O_APPEND, 0);
if (fp && out_counter>1)
if (fp && combined_header_written)
appending = True;
}

Expand Down Expand Up @@ -1354,11 +1368,22 @@ static VgFile *new_dumpfile(int tid, const HChar* trigger)

/* "cmd:" line */
VG_(fprintf)(fp, "cmd: %s", cmdbuf);

combined_header_written = True;
}

VG_(fprintf)(fp, "\npart: %d\n", out_counter);
if (CLG_(clo).separate_threads) {
VG_(fprintf)(fp, "thread: %d\n", tid);
const HChar* tname;

VG_(fprintf)(fp, "thread: %d\n", ti->tid);

/* Retired threads carry a name snapshot; live threads are read from
* the core. */
tname = ti->name ? ti->name
: VG_(get_thread_name)(CLG_(current_tid));
if (tname && tname[0])
VG_(fprintf)(fp, "desc: Thread name: %s\n", tname);
}

/* "desc:" lines */
Expand Down Expand Up @@ -1456,7 +1481,6 @@ static VgFile *new_dumpfile(int tid, const HChar* trigger)
sum = CLG_(get_eventset_cost)( CLG_(sets).full );
CLG_(zero_cost)(CLG_(sets).full, sum);
if (CLG_(clo).separate_threads) {
thread_info* ti = CLG_(get_current_thread)();
CLG_(add_diff_cost)(CLG_(sets).full, sum, ti->lastdump_cost,
ti->states.entry[0]->cost);
}
Expand Down Expand Up @@ -1514,21 +1538,55 @@ static void close_dumpfile(VgFile *fp)

static const HChar* print_trigger;

/* Whether any thread section was written for the part being dumped, and
* whether to bypass the zero-delta skip (used to force an empty section so
* the part still appears in the output). */
static Bool part_section_written;
static Bool force_empty_section;

static void print_bbccs_of_thread(thread_info* ti)
{
BBCC **p, **array;
FnPos lastFnPos;
AddrPos lastAPos;
VgFile *print_fp;

CLG_DEBUG(1, "+ print_bbccs(tid %u)\n", CLG_(current_tid));

VgFile *print_fp = new_dumpfile(CLG_(current_tid), print_trigger);
p = array = prepare_dump();

/* With per-thread dumps, skip a thread that did nothing since the last dump
* (no BBCCs and a zero exec-state delta) so parts don't grow empty sections.
* A thread with never-dumped cost still has a nonzero delta (its
* lastdump_cost baseline is zero), so this never drops real data. */
if (CLG_(clo).separate_threads && !force_empty_section && array[0] == 0) {
/* Compared element-wise rather than via a scratch cost buffer: those are
* pool-allocated and must not be individually freed, and add_diff_cost
* would clobber lastdump_cost, which the real dump updates below. */
EventSet* es = CLG_(sets).full;
ULong* last = ti->lastdump_cost;
ULong* cur = ti->states.entry[0]->cost;
Bool empty = True;
Int i;
for(i = 0; i < es->size; i++)
if (cur[i] != last[i]) { empty = False; break; }
if (empty) {
CLG_DEBUG(1, "- print_bbccs(tid %u): empty delta, skipped\n",
CLG_(current_tid));
VG_(free)(array);
return;
}
}

print_fp = new_dumpfile(ti, print_trigger);
if (print_fp == NULL) {
CLG_DEBUG(1, "- print_bbccs(tid %u): No output...\n", CLG_(current_tid));
VG_(free)(array);
return;
}
part_section_written = True;

p = array = prepare_dump();
p = array;
init_fpos(&lastFnPos);
init_apos(&lastAPos, 0, 0, 0);

Expand Down Expand Up @@ -1596,6 +1654,27 @@ static void print_bbccs_of_thread(thread_info* ti)
}


/* Dump a retired thread, whose state is not reachable via switch_thread.
* Install its containers and (already-unwound) stacks as the current state
* so the shared dumping machinery operates on it, then dump. */
static void print_bbccs_of_retired_thread(thread_info* ti, Bool retired)
{
if (!retired) {
print_bbccs_of_thread(ti);
return;
}

CLG_(set_current_exec_stack)( &(ti->states) );
CLG_(set_current_call_stack)( &(ti->calls) );
CLG_(set_current_fn_stack) ( &(ti->fns) );
CLG_(set_current_fn_array) ( &(ti->fn_active) );
CLG_(set_current_bbcc_hash) ( &(ti->bbccs) );
CLG_(set_current_jcc_hash) ( &(ti->jccs) );
CLG_(current_state).cost = ti->states.entry[0]->cost;

print_bbccs_of_thread(ti);
}

static void print_bbccs(const HChar* trigger, Bool only_current_thread)
{
init_dump_array();
Expand All @@ -1611,10 +1690,36 @@ static void print_bbccs(const HChar* trigger, Bool only_current_thread)
print_bbccs_of_thread( CLG_(get_current_thread)() );
CLG_(switch_thread)(orig_tid);
}
else if (only_current_thread)
print_bbccs_of_thread( CLG_(get_current_thread)() );
else
CLG_(forall_threads)(print_bbccs_of_thread);
else {
/* Per-thread dumps flush every thread, live and retired, so each thread's
* delta lands under the part being dumped -- otherwise threads other than
* the caller only ever get written by the termination dump. The
* only_current_thread hint is therefore ignored in this mode. Retired
* threads are freed once flushed. */
(void) only_current_thread;
part_section_written = False;
CLG_(forall_threads_incl_retired)(print_bbccs_of_retired_thread);
if (!part_section_written) {
/* Every thread was skipped as zero-delta, but the part header itself
* carries metadata (trigger, timerange) that consumers rely on: force
* one empty section so the part still appears. Use the triggering
* thread; at termination it may already be retired. */
thread_info* ti = 0;
Bool retired = False;
if (CLG_(current_tid) != VG_INVALID_THREADID)
ti = CLG_(get_current_thread)();
if (!ti) {
ti = CLG_(newest_retired_thread)();
retired = (ti != 0);
}
if (ti) {
force_empty_section = True;
print_bbccs_of_retired_thread(ti, retired);
force_empty_section = False;
}
}
CLG_(drop_retired_threads)();
}

free_dump_array();
}
Expand Down Expand Up @@ -1755,6 +1860,7 @@ void CLG_(init_dumps)(void)
}
}
if (!sr_isError(res)) VG_(close)( (Int)sr_Res(res) );
combined_header_written = False;

if (!dumps_initialized)
init_cmdbuf();
Expand Down
30 changes: 29 additions & 1 deletion callgrind/global.h
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,9 @@ struct _BBCC {
BB* bb; /* BB for this cost center */

Context* cxt; /* execution context of this BBCC */
ThreadId tid; /* only for assertion check purpose */
Int tid; /* owning thread's kernel id (LWP id); identifies
* the thread across recycled valgrind ThreadId
* slots. Only for lookup/assertion purposes. */
UInt rec_index; /* Recursion index in rec->bbcc for this bbcc */
BBCC** rec_array; /* Variable sized array of pointers to
* recursion BBCCs. Shared. */
Expand Down Expand Up @@ -588,6 +590,17 @@ struct _exec_stack {
*/
struct _thread_info {

/* Kernel thread id (LWP id), snapshotted at creation. Stable identity
* independent of the recycled valgrind ThreadId slot: used for the
* "thread:" dump header and the "-NN" dump file suffix so a new OS thread
* reusing a slot is never conflated with a dead one. */
Int tid;
/* Name the thread gave itself, snapshotted at retirement (NULL if none);
* for live threads the current name is read from the core at dump time. */
HChar* name;
/* Link for the retired-thread list; unused while the thread is live. */
struct _thread_info* retired_next;

/* state */
fn_stack fns; /* function stack */
call_stack calls; /* context call arc stack */
Expand Down Expand Up @@ -783,9 +796,24 @@ void CLG_(push_cxt)(fn_node* fn);
void CLG_(init_threads)(void);
thread_info** CLG_(get_threads)(void);
thread_info* CLG_(get_current_thread)(void);
/* Kernel thread id (LWP id) of the current thread; identifies a thread across
* recycled valgrind ThreadId slots. */
Int CLG_(current_thread_tid)(void);
/* Resolve (and cache) a thread's kernel id from the core, filling it in the
* first time it is available. Returns 0 for a NULL thread. */
Int CLG_(resolve_thread_tid)(thread_info* t);
void CLG_(switch_thread)(ThreadId tid);
void CLG_(forall_threads)(void (*func)(thread_info*));
/* Like forall_threads, but also visits retired (exited) threads, in
* ascending kernel-tid order across both groups. The retired argument is
* True for threads from the retired list. */
void CLG_(forall_threads_incl_retired)(void (*func)(thread_info*, Bool retired));
/* Drop all retired threads, freeing their state. */
void CLG_(drop_retired_threads)(void);
/* Most recently retired thread, or NULL if none are pending. */
thread_info* CLG_(newest_retired_thread)(void);
void CLG_(run_thread)(ThreadId tid);
void CLG_(pre_thread_ll_exit)(ThreadId tid);

void CLG_(init_exec_state)(exec_state* es);
void CLG_(init_exec_stack)(exec_stack*);
Expand Down
10 changes: 9 additions & 1 deletion callgrind/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1686,7 +1686,14 @@ Bool CLG_(handle_client_request)(ThreadId tid, UWord *args, UWord *ret)
break;

case VG_USERREQ__ZERO_STATS:
CLG_(zero_all_cost)(True);
/* Zero every thread under per-thread dumps so part boundaries stay
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Comment thread
GuillaumeLagrange marked this conversation as resolved.
* consistent across threads; otherwise only the caller is zeroed. */
CLG_(zero_all_cost)(!CLG_(clo).separate_threads);
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Comment thread
greptile-apps[bot] marked this conversation as resolved.
/* Live threads are reset above, but an already-exited thread sits on the
* retired list untouched, and all of its cost predates this zero. Drop it
* so a later dump can't carry that pre-zero cost past the boundary. */
if (CLG_(clo).separate_threads)
CLG_(drop_retired_threads)();
*ret = 0; /* meaningless */
break;

Expand Down Expand Up @@ -2207,6 +2214,7 @@ void CLG_(pre_clo_init)(void)
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) );

CLG_(set_clo_defaults)();

Expand Down
Loading
Loading