diff --git a/.gitignore b/.gitignore index 4446456c3..6f1e869b8 100644 --- a/.gitignore +++ b/.gitignore @@ -158,6 +158,8 @@ /callgrind/tests/callgrind.out.* /callgrind/tests/vgcore.* /callgrind/tests/clreq +/callgrind/tests/register_desc +/callgrind/tests/register_desc_threads /callgrind/tests/simwork /callgrind/tests/threads /callgrind/tests/inline-samefile diff --git a/callgrind/callgrind.h b/callgrind/callgrind.h index 41fae04f9..8a66f8d5f 100644 --- a/callgrind/callgrind.h +++ b/callgrind/callgrind.h @@ -79,7 +79,8 @@ typedef VG_USERREQ__DUMP_STATS_AT, VG_USERREQ__START_INSTRUMENTATION, VG_USERREQ__STOP_INSTRUMENTATION, - VG_USERREQ__ADD_OBJ_SKIP + VG_USERREQ__ADD_OBJ_SKIP, + VG_USERREQ__ADD_DESC } Vg_CallgrindClientRequest; /* Dump current state of cost centers, and zero them afterwards */ @@ -133,4 +134,12 @@ typedef VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__ADD_OBJ_SKIP, \ path, 0, 0, 0, 0) +/* Attach a "desc: " line to the header of the next dumped part, without + dumping. Use the ": " form other desc lines follow. Line breaks + in desc are written as spaces. Lines registered after the last dump are + dropped if the process exits or execs before dumping again. */ +#define CALLGRIND_ADD_DESC(desc) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__ADD_DESC, \ + desc, 0, 0, 0, 0) + #endif /* __CALLGRIND_H */ diff --git a/callgrind/dump.c b/callgrind/dump.c index 26c5b0351..09a4a2152 100644 --- a/callgrind/dump.c +++ b/callgrind/dump.c @@ -197,7 +197,7 @@ static void print_fn(VgFile *fp, const HChar* tag, const fn_node* fn) VG_(fprintf)(fp, "%s\n", fn->name); } -static void print_mangled_fn(VgFile *fp, const HChar* tag, +static void print_mangled_fn(VgFile *fp, const HChar* tag, Context* cxt, int rec_index) { int i; @@ -235,7 +235,7 @@ static void print_mangled_fn(VgFile *fp, const HChar* tag, if (rec_index >0) VG_(fprintf)(fp, "'%d", rec_index +1); for(i=1;isize;i++) - VG_(fprintf)(fp, "'(%u)", + VG_(fprintf)(fp, "'(%u)", cxt->fn[i]->pure_cxt->base_number); VG_(fprintf)(fp, "\n"); @@ -291,7 +291,7 @@ static Bool print_fn_pos(VgFile *fp, FnPos* last, BBCC* bbcc) last->cxt = 0; /* reprint context */ res = True; } - + if (last->cxt != bbcc->cxt) { fn_node* last_from = (last->cxt && last->cxt->size >1) ? last->cxt->fn[1] : 0; @@ -346,7 +346,7 @@ static Bool print_fn_pos(VgFile *fp, FnPos* last, BBCC* bbcc) last->cxt = bbcc->cxt; CLG_DEBUG(2, "- print_fn_pos: %s\n", res ? "changed" : ""); - + return res; } @@ -381,7 +381,7 @@ Bool get_debug_pos(BBCC* bbcc, Addr addr, AddrPos* p) Bool found_file_line; int cachepos = addr % DEBUG_CACHE_SIZE; - + if (debug_cache_addr[cachepos] == addr) { p->line = debug_cache_line[cachepos]; p->file = debug_cache_file[cachepos]; @@ -476,7 +476,7 @@ static void copy_apos(AddrPos* dst, AddrPos* src) dst->bb_addr = src->bb_addr; dst->file = src->file; dst->line = src->line; -} +} /* copy file position and init cost */ static void init_fcost(AddrCost* c, Addr addr, Addr bbaddr, file_node* file) @@ -554,7 +554,7 @@ void fprint_pos(VgFile *fp, const AddrPos* curr, const AddrPos* last) else { if (CLG_(clo).dump_instr) { int diff = curr->addr - last->addr; - if ( CLG_(clo).compress_pos && (last->addr >0) && + if ( CLG_(clo).compress_pos && (last->addr >0) && (diff > -100) && (diff < 100)) { if (diff >0) VG_(fprintf)(fp, "+%d ", diff); @@ -569,7 +569,7 @@ void fprint_pos(VgFile *fp, const AddrPos* curr, const AddrPos* last) if (CLG_(clo).dump_bb) { int diff = curr->bb_addr - last->bb_addr; - if ( CLG_(clo).compress_pos && (last->bb_addr >0) && + if ( CLG_(clo).compress_pos && (last->bb_addr >0) && (diff > -100) && (diff < 100)) { if (diff >0) VG_(fprintf)(fp, "+%d ", diff); @@ -584,7 +584,7 @@ void fprint_pos(VgFile *fp, const AddrPos* curr, const AddrPos* last) if (CLG_(clo).dump_line) { int diff = curr->line - last->line; - if ( CLG_(clo).compress_pos && (last->line >0) && + if ( CLG_(clo).compress_pos && (last->line >0) && (diff > -100) && (diff < 100)) { if (diff >0) @@ -627,7 +627,7 @@ static void fprint_fcost(VgFile *fp, AddrCost* c, AddrPos* last) c->p.file->name, c->p.line, c->p.bb_addr, c->p.addr); CLG_(print_cost)(-5, CLG_(sets).full, c->cost); } - + fprint_pos(fp, &(c->p), last); copy_apos( last, &(c->p) ); /* update last to current position */ @@ -654,17 +654,17 @@ static void fprint_jcc(VgFile *fp, jCC* jcc, AddrPos* curr, AddrPos* last, CLG_ASSERT(jcc->to !=0); CLG_ASSERT(jcc->from !=0); - + if (!get_debug_pos(jcc->to, bb_addr(jcc->to->bb), &target)) { /* if we don't have debug info, don't switch to file "???" */ target.file = last->file; } if ((jcc->jmpkind == jk_CondJump) || (jcc->jmpkind == jk_Jump)) { - + /* this is a JCC for a followed conditional or boring jump. */ CLG_ASSERT(CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost)); - + /* objects among jumps should be the same. * Otherwise this jump would have been changed to a call * (see setup_bbcc) @@ -683,7 +683,7 @@ static void fprint_jcc(VgFile *fp, jCC* jcc, AddrPos* curr, AddrPos* last, if (last->file != target.file) { print_file(fp, "jfi=", target.file); } - + if (jcc->from->cxt != jcc->to->cxt) { if (CLG_(clo).mangle_names) print_mangled_fn(fp, "jfn", @@ -691,7 +691,7 @@ static void fprint_jcc(VgFile *fp, jCC* jcc, AddrPos* curr, AddrPos* last, else print_fn(fp, "jfn", jcc->to->cxt->fn[0]); } - + if (jcc->jmpkind == jk_CondJump) { /* format: jcnd=/ */ VG_(fprintf)(fp, "jcnd=%llu/%llu ", @@ -702,7 +702,7 @@ static void fprint_jcc(VgFile *fp, jCC* jcc, AddrPos* curr, AddrPos* last, VG_(fprintf)(fp, "jump=%llu ", jcc->call_counter); } - + fprint_pos(fp, &target, last); VG_(fprintf)(fp, "\n"); fprint_pos(fp, curr, last); @@ -714,7 +714,7 @@ static void fprint_jcc(VgFile *fp, jCC* jcc, AddrPos* curr, AddrPos* last, file = jcc->to->cxt->fn[0]->file; obj = jcc->to->bb->obj; - + /* object of called position different to object of this function?*/ if (jcc->from->cxt->fn[0]->file->obj != obj) { print_obj(fp, "cob=", obj); @@ -731,7 +731,7 @@ static void fprint_jcc(VgFile *fp, jCC* jcc, AddrPos* curr, AddrPos* last, print_fn(fp, "cfn", jcc->to->cxt->fn[0]); if (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost)) { - VG_(fprintf)(fp, "calls=%llu ", + VG_(fprintf)(fp, "calls=%llu ", jcc->call_counter); fprint_pos(fp, &target, last); @@ -804,7 +804,7 @@ static jCC* sort_jcc_list(jCC* head) { * Print all costs of a BBCC: * - FCCs of instructions * - JCCs of the unique jump of this BB - * returns True if something was written + * returns True if something was written */ static Bool fprint_bbcc(VgFile *fp, BBCC* bbcc, AddrPos* last) { @@ -843,7 +843,7 @@ static Bool fprint_bbcc(VgFile *fp, BBCC* bbcc, AddrPos* last) if (CLG_(clo).dump_bbs || CLG_(clo).dump_instr || (newCost->p.line != currCost->p.line) || (newCost->p.file != currCost->p.file)) { - + if (!CLG_(is_zero_cost)( CLG_(sets).full, currCost->cost )) { something_written = True; @@ -852,13 +852,13 @@ static Bool fprint_bbcc(VgFile *fp, BBCC* bbcc, AddrPos* last) fprint_fcost(fp, currCost, last); } - + /* switch buffers */ currSum = 1 - currSum; currCost = &(ccSum[currSum]); newCost = &(ccSum[1-currSum]); } - + /* add line cost to current cost sum */ (*CLG_(cachesim).add_icost)(currCost->cost, bbcc, instr_info, ecounter); @@ -870,7 +870,7 @@ static Bool fprint_bbcc(VgFile *fp, BBCC* bbcc, AddrPos* last) (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost ))) jcc_count++; - if (jcc_count>0) { + if (jcc_count>0) { if (!CLG_(is_zero_cost)( CLG_(sets).full, currCost->cost )) { /* no need to switch buffers, as position is the same */ fprint_apos(fp, &(currCost->p), last, bbcc->cxt->fn[0]->file, bbcc); @@ -897,7 +897,7 @@ static Bool fprint_bbcc(VgFile *fp, BBCC* bbcc, AddrPos* last) jmp++; } } - + /* jCCs at end? If yes, dump cumulated line info first */ jcc_count = 0; for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from) { @@ -906,7 +906,7 @@ static Bool fprint_bbcc(VgFile *fp, BBCC* bbcc, AddrPos* last) (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost ))) jcc_count++; } - + if ( (bbcc->skipped && !CLG_(is_zero_cost)(CLG_(sets).full, bbcc->skipped)) || (jcc_count>0) ) { @@ -916,11 +916,11 @@ static Bool fprint_bbcc(VgFile *fp, BBCC* bbcc, AddrPos* last) fprint_apos(fp, &(currCost->p), last, bbcc->cxt->fn[0]->file, bbcc); fprint_fcost(fp, currCost, last); } - + get_debug_pos(bbcc, bb_jmpaddr(bb), &(currCost->p)); fprint_apos(fp, &(currCost->p), last, bbcc->cxt->fn[0]->file, bbcc); something_written = True; - + /* first, print skipped costs for calls */ if (bbcc->skipped && !CLG_(is_zero_cost)( CLG_(sets).full, bbcc->skipped )) { @@ -953,20 +953,20 @@ static Bool fprint_bbcc(VgFile *fp, BBCC* bbcc, AddrPos* last) fprint_fcost(fp, currCost, last); } if (CLG_(clo).dump_bbs) VG_(fprintf)(fp, "\n"); - + /* when every cost was immediately written, we must have done so, * as this function is only called when there's cost in a BBCC */ CLG_ASSERT(something_written); } - + bbcc->ecounter_sum = 0; for(i=0; i<=bbcc->bb->cjmp_count; i++) bbcc->jmp[i].ecounter = 0; bbcc->ret_counter = 0; - + CLG_DEBUG(1, "- fprint_bbcc: JCCs %d\n", jcc_count); - + return something_written; } @@ -1072,7 +1072,7 @@ static void CLG_(qsort)(BBCC **a, int n, int (*cmp)(BBCC**,BBCC**)) for (pm = a; pm < a+n; pm++) { VG_(printf)(" %3ld BB %#lx, ", pm - qsort_start + 0L, - bb_addr((*pm)->bb)); + bb_addr((*pm)->bb)); CLG_(print_cxt)(9, (*pm)->cxt, (*pm)->rec_index); } } @@ -1100,14 +1100,14 @@ static void CLG_(qsort)(BBCC **a, int n, int (*cmp)(BBCC**,BBCC**)) while ((pb <= pc) && ((r=cmp(pb, pv)) <= 0)) { if (r==0) { /* same as pivot, to start */ - swap(pa,pb); pa++; + swap(pa,pb); pa++; } pb ++; } while ((pb <= pc) && ((r=cmp(pc, pv)) >= 0)) { if (r==0) { /* same as pivot, to end */ - swap(pc,pd); pd--; + swap(pc,pd); pd--; } pc --; } @@ -1122,7 +1122,7 @@ static void CLG_(qsort)(BBCC **a, int n, int (*cmp)(BBCC**,BBCC**)) /* put pivot from start into middle */ if ((s = pa-a)>0) { for(r=0;r0) { for(r=0;r0) { for(r=0;rbb)); @@ -1188,18 +1188,18 @@ static void cs_addCount(thread_info* ti) /* add BBCCs with active call in call stack of current thread. * update cost sums for active calls */ - + for(i = 0; i < CLG_(current_call_stack).sp; i++) { call_entry* e = &(CLG_(current_call_stack).entry[i]); if (e->jcc == 0) continue; - + CLG_(add_diff_cost_lz)( CLG_(sets).full, &(e->jcc->cost), e->enter_cost, CLG_(current_state).cost); bbcc = e->jcc->from; CLG_DEBUG(1, " [%2d] (tid %u), added active: %s\n", i,CLG_(current_tid),bbcc->cxt->fn[0]->name); - + if (bbcc->ecounter_sum>0 || bbcc->ret_counter>0) { /* already counted */ continue; @@ -1216,13 +1216,13 @@ static void cs_addPtr(thread_info* ti) /* add BBCCs with active call in call stack of current thread. * update cost sums for active calls */ - + for(i = 0; i < CLG_(current_call_stack).sp; i++) { call_entry* e = &(CLG_(current_call_stack).entry[i]); if (e->jcc == 0) continue; bbcc = e->jcc->from; - + if (bbcc->ecounter_sum>0 || bbcc->ret_counter>0) { /* already counted */ continue; @@ -1236,7 +1236,7 @@ static void cs_addPtr(thread_info* ti) /** * Put all BBCCs with costs into a sorted array. - * The returned arrays ends with a null pointer. + * The returned arrays ends with a null pointer. * Must be freed after dumping. */ static @@ -1245,7 +1245,7 @@ BBCC** prepare_dump(void) BBCC **array; prepare_count = 0; - + /* if we do not separate among threads, this gives all */ /* count number of BBCCs with >0 executions */ CLG_(forall_bbccs)(hash_addCount); @@ -1262,7 +1262,7 @@ BBCC** prepare_dump(void) /* allocate bbcc array, insert BBCCs and sort */ prepare_ptr = array = (BBCC**) CLG_MALLOC("cl.dump.pd.1", - (prepare_count+1) * sizeof(BBCC*)); + (prepare_count+1) * sizeof(BBCC*)); CLG_(forall_bbccs)(hash_addPtr); @@ -1300,6 +1300,48 @@ static void fprint_cost_ln(VgFile *fp, const HChar* prefix, static ULong bbs_done = 0; static HChar* filename = 0; +/* "desc:" lines queued by the client for the next part. Written in every + * section of that part, like the spawned children, then dropped. */ +static HChar** part_descs = 0; +static Int n_part_descs = 0; +static Int part_descs_capacity = 0; + +void CLG_(add_part_desc)(const HChar* desc) +{ + HChar* copy; + Int i; + + if (n_part_descs == part_descs_capacity) { + part_descs_capacity = part_descs_capacity ? part_descs_capacity * 2 : 4; + part_descs = VG_(realloc)("cl.dump.rpd.1", part_descs, + part_descs_capacity * sizeof(HChar*)); + } + + copy = VG_(strdup)("cl.dump.rpd.2", desc); + /* a line break would end the desc line and corrupt the header */ + for (i = 0; copy[i]; i++) + if (copy[i] == '\n' || copy[i] == '\r') + copy[i] = ' '; + part_descs[n_part_descs++] = copy; +} + +static void print_part_descs(VgFile* fp) +{ + Int i; + + for (i = 0; i < n_part_descs; i++) + VG_(fprintf)(fp, "desc: %s\n", part_descs[i]); +} + +void CLG_(forget_part_descs)(void) +{ + Int i; + + for (i = 0; i < n_part_descs; i++) + VG_(free)(part_descs[i]); + n_part_descs = 0; +} + static void file_err(void) { @@ -1334,7 +1376,7 @@ static VgFile *new_dumpfile(thread_info* ti, const HChar* trigger) if (!CLG_(clo).combine_dumps) { i = VG_(sprintf)(filename, "%s", out_file); - + if (trigger) i += VG_(sprintf)(filename+i, ".%d", out_counter); @@ -1390,6 +1432,7 @@ static VgFile *new_dumpfile(thread_info* ti, const HChar* trigger) /* Per-part, not in the once-per-file header, so a child spawned during a * later part is still recorded. */ CLG_(print_spawned_children)(fp); + print_part_descs(fp); if (CLG_(clo).separate_threads) { const HChar* tname = CLG_(thread_name)(ti); @@ -1441,7 +1484,7 @@ static VgFile *new_dumpfile(thread_info* ti, const HChar* trigger) if (fnc->dump_at_enter) { VG_(fprintf)(fp, "desc: Option: --fn-dump-at-enter=%s\n", fnc->name); - } + } if (fnc->dump_at_leave) { VG_(fprintf)(fp, "desc: Option: --fn-dump-at-leave=%s\n", fnc->name); @@ -1449,11 +1492,11 @@ static VgFile *new_dumpfile(thread_info* ti, const HChar* trigger) if (fnc->separate_callers != CLG_(clo).separate_callers) { VG_(fprintf)(fp, "desc: Option: --separate-callers%d=%s\n", fnc->separate_callers, fnc->name); - } + } if (fnc->separate_recursions != CLG_(clo).separate_recursions) { VG_(fprintf)(fp, "desc: Option: --separate-recs%d=%s\n", fnc->separate_recursions, fnc->name); - } + } fnc = fnc->next; } } @@ -1532,7 +1575,7 @@ static void close_dumpfile(VgFile *fp) fprint_cost_ln(fp, "totals: ", CLG_(dumpmap), dump_total_cost); //fprint_fcc_ln(fp, "summary: ", &dump_total_fcc); - CLG_(add_cost_lz)(CLG_(sets).full, + CLG_(add_cost_lz)(CLG_(sets).full, &CLG_(total_cost), dump_total_cost); VG_(fclose)(fp); @@ -1606,7 +1649,7 @@ static void print_bbccs_of_thread(thread_info* ti) while(1) { /* on context/function change, print old cost buffer before */ - if (lastFnPos.cxt && ((*p==0) || + if (lastFnPos.cxt && ((*p==0) || (lastFnPos.cxt != (*p)->cxt) || (lastFnPos.rec_index != (*p)->rec_index))) { if (!CLG_(is_zero_cost)( CLG_(sets).full, ccSum[currSum].cost )) { @@ -1615,18 +1658,18 @@ static void print_bbccs_of_thread(thread_info* ti) lastFnPos.cxt->fn[0]->file, 0); fprint_fcost(print_fp, &ccSum[currSum], &lastAPos); } - + if (ccSum[currSum].p.file != lastFnPos.cxt->fn[0]->file) { /* switch back to file of function */ print_file(print_fp, "fe=", lastFnPos.cxt->fn[0]->file); } VG_(fprintf)(print_fp, "\n"); } - + if (*p == 0) break; - + if (print_fn_pos(print_fp, &lastFnPos, *p)) { - + /* new function */ init_apos(&lastAPos, 0, 0, (*p)->cxt->fn[0]->file); init_fcost(&ccSum[0], 0, 0, 0); @@ -1634,31 +1677,31 @@ static void print_bbccs_of_thread(thread_info* ti) currSum = 0; last_inline_fn = 0; /* reset inline function tracking */ } - + if (CLG_(clo).dump_bbs) { /* FIXME: Specify Object of BB if different to object of fn */ int i; ULong ecounter = (*p)->ecounter_sum; VG_(fprintf)(print_fp, "bb=%#lx ", (UWord)(*p)->bb->offset); for(i = 0; i<(*p)->bb->cjmp_count;i++) { - VG_(fprintf)(print_fp, "%u %llu ", + VG_(fprintf)(print_fp, "%u %llu ", (*p)->bb->jmp[i].instr, ecounter); ecounter -= (*p)->jmp[i].ecounter; } - VG_(fprintf)(print_fp, "%u %llu\n", + VG_(fprintf)(print_fp, "%u %llu\n", (*p)->bb->instr_count, ecounter); } - + fprint_bbcc(print_fp, *p, &lastAPos); - + p++; } close_dumpfile(print_fp); VG_(free)(array); - + /* set counters of last dump */ CLG_(copy_cost)( CLG_(sets).full, ti->lastdump_cost, CLG_(current_state).cost ); @@ -1709,6 +1752,7 @@ static void print_bbccs(const HChar* trigger, Bool only_current_thread) /* All of these have just been emitted under the part being dumped; a later * part cannot reference them. */ CLG_(forget_spawned_children)(); + CLG_(forget_part_descs)(); free_dump_array(); } @@ -1814,7 +1858,7 @@ void CLG_(init_dumps)(void) return; } thisPID = currentPID; - + if (!CLG_(clo).out_format) CLG_(clo).out_format = DEFAULT_OUTFORMAT; @@ -1832,20 +1876,20 @@ void CLG_(init_dumps)(void) /* allocate space big enough for final filenames */ filename = (HChar*) CLG_MALLOC("cl.dump.init_dumps.2", VG_(strlen)(out_file)+32); - + /* Make sure the output base file can be written. * This is used for the dump at program termination. * We stop with an error here if we can not create the * file: This is probably because of missing rights, * and trace parts wouldn't be allowed to be written, too. - */ + */ VG_(strcpy)(filename, out_file); res = VG_(open)(filename, VKI_O_WRONLY|VKI_O_TRUNC, 0); - if (sr_isError(res)) { + if (sr_isError(res)) { res = VG_(open)(filename, VKI_O_CREAT|VKI_O_WRONLY, VKI_S_IRUSR|VKI_S_IWUSR); if (sr_isError(res)) { - file_err(); + file_err(); } } if (!sr_isError(res)) VG_(close)( (Int)sr_Res(res) ); diff --git a/callgrind/global.h b/callgrind/global.h index 1b41a4711..174672972 100644 --- a/callgrind/global.h +++ b/callgrind/global.h @@ -104,10 +104,10 @@ struct _CommandLineOptions { Bool dump_instr; Bool dump_bb; Bool dump_bbs; /* Dump basic block information? */ - + /* Dump generation options */ ULong dump_every_bb; /* Dump every xxx BBs. */ - + /* Collection options */ Bool separate_threads; /* Separate threads in dump? */ Int separate_callers; /* Separate dependent on how many callers? */ @@ -162,7 +162,7 @@ struct _Statistics { ULong bb_executions; Int context_counter; - Int bb_retranslations; + Int bb_retranslations; Int distinct_objs; Int distinct_files; @@ -262,7 +262,7 @@ struct _jCC { }; -/* +/* * Info for one instruction of a basic block. */ typedef struct _InstrInfo InstrInfo; @@ -316,12 +316,12 @@ struct _BB { VgSectKind sect_kind; /* section of this BB, e.g. PLT */ UInt instr_count; - + /* filled by CLG_(get_fn_node) if debug info is available */ fn_node* fn; /* debug info for this BB */ UInt line; Bool is_entry; /* True if this BB is a function entry */ - + BBCC* bbcc_list; /* BBCCs for same BB (see next_bbcc in BBCC) */ BBCC* last_bbcc; /* Temporary: Cached for faster access (LRU) */ @@ -395,19 +395,19 @@ struct _BBCC { * 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 + BBCC** rec_array; /* Variable sized array of pointers to * recursion BBCCs. Shared. */ ULong ret_counter; /* how often returned from jccs of this bbcc; * used to check if a dump for this BBCC is needed */ - + BBCC* next_bbcc; /* Chain of BBCCs for same BB */ BBCC* lru_next_bbcc; /* BBCC executed next the last time */ - + jCC* lru_from_jcc; /* Temporary: Cached for faster access (LRU) */ jCC* lru_to_jcc; /* Temporary: Cached for faster access (LRU) */ - FullCost skipped; /* cost for skipped functions called from + FullCost skipped; /* cost for skipped functions called from * jmp_addr. Allocated lazy */ - + BBCC* next; /* entry chain in hash */ ULong* cost; /* start of 64bit costs for this BBCC */ ULong ecounter_sum; /* execution counter for first instruction of BB */ @@ -482,7 +482,7 @@ struct _obj_node { * * is 0 if the function called is not skipped (usual case). * Otherwise, it is the last non-skipped BBCC. This one gets all - * the calls to non-skipped functions and all costs in skipped + * the calls to non-skipped functions and all costs in skipped * instructions. */ struct _call_entry { @@ -514,14 +514,14 @@ struct _exec_state { /* the signum of the handler, 0 for main thread context */ Int sig; - + /* the old call stack pointer at entering the signal handler */ Int orig_sp; - + FullCost cost; Bool collect; Context* cxt; - + /* number of conditional jumps passed in last BB */ Int jmps_passed; BBCC* bbcc; /* last BB executed */ @@ -541,7 +541,7 @@ typedef struct _cxt_hash cxt_hash; struct _cxt_hash { UInt size, entries; Context** table; -}; +}; /* Thread specific state structures, i.e. parts of a thread state. * There are variables for the current state of each part, @@ -590,7 +590,7 @@ struct _exec_stack { exec_state* entry[MAX_SIGHANDLERS]; }; -/* Thread State +/* Thread State * * This structure stores thread specific info while a thread is *not* * running. See function switch_thread() for save/restore on thread switch. @@ -676,7 +676,7 @@ struct cachesim_if void (*printstat)(Int,Int,Int); void (*add_icost)(SimCost, BBCC*, InstrInfo*, ULong); void (*finish)(void); - + void (*log_1I0D)(InstrInfo*) VG_REGPARM(1); void (*log_2I0D)(InstrInfo*, InstrInfo*) VG_REGPARM(2); void (*log_3I0D)(InstrInfo*, InstrInfo*, InstrInfo*) VG_REGPARM(3); @@ -837,6 +837,9 @@ void CLG_(run_post_signal_on_call_stack_bottom)(void); /* from dump.c */ void CLG_(init_dumps)(void); +/* Queue a "desc:" line for the header of the next dumped part. */ +void CLG_(add_part_desc)(const HChar* desc); +void CLG_(forget_part_descs)(void); /* from subprocess.c */ void CLG_(init_subprocess)(void); diff --git a/callgrind/main.c b/callgrind/main.c index 44b9e6d3c..54ecb178b 100644 --- a/callgrind/main.c +++ b/callgrind/main.c @@ -706,8 +706,8 @@ void addEvent_D_guarded ( ClgState* clgs, InstrInfo* inode, ea, mkIRExpr_HWord( datasize ) ); regparms = 3; di = unsafeIRDirty_0_N( - regparms, - helperName, VG_(fnptr_to_fnentry)( helperAddr ), + regparms, + helperName, VG_(fnptr_to_fnentry)( helperAddr ), argv ); di->guard = guard; addStmtToIRSB( clgs->sbOut, IRStmt_Dirty(di) ); @@ -914,7 +914,7 @@ void addConstMemStoreStmt( IRSB* bbOut, UWord addr, UInt val, IRType hWordTy) IRConst_U32( addr ) : IRConst_U64( addr )), IRExpr_Const(IRConst_U32(val)) )); -} +} /* add helper call to setup_bbcc, with pointer to BB struct as argument @@ -999,7 +999,7 @@ IRSB* CLG_(instrument)( VgCallbackClosure* closure, CLG_ASSERT(Ist_IMark == st->tag); origAddr = st->Ist.IMark.addr + st->Ist.IMark.delta; - CLG_ASSERT(origAddr == st->Ist.IMark.addr + CLG_ASSERT(origAddr == st->Ist.IMark.addr + st->Ist.IMark.delta); // XXX: check no overflow /* Get BB struct (creating if necessary). @@ -1424,7 +1424,7 @@ static void zero_thread_cost(thread_info* t) if (!CLG_(current_call_stack).entry[i].jcc) continue; /* reset call counters to current for active calls */ - CLG_(copy_cost)( CLG_(sets).full, + CLG_(copy_cost)( CLG_(sets).full, CLG_(current_call_stack).entry[i].enter_cost, CLG_(current_state).cost ); CLG_(current_call_stack).entry[i].jcc->call_counter = 0; @@ -1433,7 +1433,7 @@ static void zero_thread_cost(thread_info* t) CLG_(forall_bbccs)(CLG_(zero_bbcc)); /* set counter for last dump */ - CLG_(copy_cost)( CLG_(sets).full, + CLG_(copy_cost)( CLG_(sets).full, t->lastdump_cost, CLG_(current_state).cost ); } @@ -1525,18 +1525,18 @@ static void dump_state_of_thread_togdb(thread_info* ti) ce = CLG_(get_call_entry)(i); /* if this frame is skipped, we don't have counters */ if (!ce->jcc) continue; - + from = ce->jcc->from; VG_(gdb_printf)("function-%d-%d: %s\n",t, i, from->cxt->fn[0]->name); VG_(gdb_printf)("calls-%d-%d: %llu\n",t, i, ce->jcc->call_counter); - + /* FIXME: EventSets! */ CLG_(copy_cost)( CLG_(sets).full, sum, ce->jcc->cost ); CLG_(copy_cost)( CLG_(sets).full, tmp, ce->enter_cost ); CLG_(add_diff_cost)( CLG_(sets).full, sum, ce->enter_cost, CLG_(current_state).cost ); CLG_(copy_cost)( CLG_(sets).full, ce->enter_cost, tmp ); - + mcost = CLG_(mappingcost_as_string)(CLG_(dumpmap), sum); VG_(gdb_printf)("events-%d-%d: %s\n",t, i, mcost); VG_(free)(mcost); @@ -1571,7 +1571,7 @@ static void dump_state_togdb(void) VG_(free)(evmap); /* "part:" line (number of last part. Is 0 at start */ VG_(gdb_printf)("part: %d\n", CLG_(get_dump_counter)()); - + /* threads */ th = CLG_(get_threads)(); VG_(gdb_printf)("threads:"); @@ -1584,7 +1584,7 @@ static void dump_state_togdb(void) CLG_(forall_threads)(dump_state_of_thread_togdb); } - + static void print_monitor_help ( void ) { VG_(gdb_printf) ("\n"); @@ -1610,7 +1610,7 @@ static Bool handle_gdb_monitor_command (ThreadId tid, const HChar *req) VG_(strcpy) (s, req); wcmd = VG_(strtok_r) (s, " ", &ssaveptr); - switch (VG_(keyword_id) ("help dump zero status instrumentation", + switch (VG_(keyword_id) ("help dump zero status instrumentation", wcmd, kwd_report_duplicated_matches)) { case -2: /* multiple matches */ return True; @@ -1660,7 +1660,7 @@ static Bool handle_gdb_monitor_command (ThreadId tid, const HChar *req) return True; } - default: + default: tl_assert(0); return False; } @@ -1674,7 +1674,7 @@ Bool CLG_(handle_client_request)(ThreadId tid, UWord *args, UWord *ret) return False; switch(args[0]) { - case VG_USERREQ__DUMP_STATS: + case VG_USERREQ__DUMP_STATS: CLG_(dump_profile)("Client Request", True); *ret = 0; /* meaningless */ break; @@ -1722,6 +1722,11 @@ Bool CLG_(handle_client_request)(ThreadId tid, UWord *args, UWord *ret) break; } + case VG_USERREQ__ADD_DESC: + CLG_(add_part_desc)((const HChar*)args[1]); + *ret = 0; + break; + case VG_USERREQ__GDB_MONITOR_COMMAND: { Bool handled = handle_gdb_monitor_command (tid, (HChar*)args[1]); if (handled) @@ -2015,7 +2020,7 @@ void finish(void) CLG_(dump_profile)(0, False); if (VG_(clo_verbosity) == 0) return; - + if (VG_(clo_stats)) { VG_(message)(Vg_DebugMsg, "\n"); clg_print_stats(); @@ -2105,7 +2110,7 @@ void CLG_(post_clo_init)(void) CLG_DEBUG(1, " Using user specified value for " "--vex-iropt-register-updates\n"); } else { - CLG_DEBUG(1, + CLG_DEBUG(1, " Using default --vex-iropt-register-updates=" "sp-at-mem-access\n"); } @@ -2135,13 +2140,13 @@ void CLG_(post_clo_init)(void) CLG_DEBUG(1, " Using user specified value for " "--px-file-backed\n"); } else { - CLG_DEBUG(1, + CLG_DEBUG(1, " Using default --px-file-backed=" "sp-at-mem-access\n"); } if (VG_(clo_vex_control).iropt_unroll_thresh != 0) { - VG_(message)(Vg_UserMsg, + VG_(message)(Vg_UserMsg, "callgrind only works with --vex-iropt-unroll-thresh=0\n" "=> resetting it back to 0\n"); VG_(clo_vex_control).iropt_unroll_thresh = 0; // cannot be overridden. @@ -2152,7 +2157,7 @@ void CLG_(post_clo_init)(void) "=> resetting it back to 'no'\n"); VG_(clo_vex_control).guest_chase = False; // cannot be overridden. } - + CLG_DEBUG(1, " dump threads: %s\n", CLG_(clo).separate_threads ? "Yes":"No"); CLG_DEBUG(1, " call sep. : %d\n", CLG_(clo).separate_callers); CLG_DEBUG(1, " rec. sep. : %d\n", CLG_(clo).separate_recursions); diff --git a/callgrind/subprocess.c b/callgrind/subprocess.c index b88b0c819..320afac22 100644 --- a/callgrind/subprocess.c +++ b/callgrind/subprocess.c @@ -139,6 +139,8 @@ static void clg_atfork_child(ThreadId tid) /* the inherited edges are the parent's; this process only reports the * children it spawns itself */ CLG_(forget_spawned_children)(); + /* likewise for desc lines the parent queued for its own next part */ + CLG_(forget_part_descs)(); /* the state file of the parent's PID belongs to the parent; advertise the * inherited in-memory state under our own PID */ diff --git a/callgrind/tests/Makefile.am b/callgrind/tests/Makefile.am index 261d357b4..a7b65c7ad 100644 --- a/callgrind/tests/Makefile.am +++ b/callgrind/tests/Makefile.am @@ -10,6 +10,9 @@ EXTRA_DIST = \ ann1.post.exp ann1.stderr.exp ann1.vgtest \ ann2.post.exp ann2.stderr.exp ann2.vgtest \ clreq.vgtest clreq.stderr.exp \ + register_desc.vgtest register_desc.stderr.exp register_desc.post.exp \ + register_desc_threads.vgtest register_desc_threads.stderr.exp \ + register_desc_threads.post.exp \ find_debuginfo.vgtest find_debuginfo.stderr.exp find_debuginfo.post.exp \ runtime_obj_skip_py.vgtest runtime_obj_skip_py.stderr.exp runtime_obj_skip_py.post.exp \ runtime_obj_skip_py.py runtime_obj_skip_py_shim.c \ @@ -35,7 +38,7 @@ EXTRA_DIST = \ inline-crossfile.vgtest inline-crossfile.stderr.exp inline-crossfile.stdout.exp inline-crossfile.post.exp \ inline-crossfile-helper1.h inline-crossfile-helper2.h filter_inline -check_PROGRAMS = clreq find_debuginfo simwork threads inline-samefile inline-crossfile runtime_obj_skip_c runtime_obj_skip_underflow +check_PROGRAMS = clreq register_desc register_desc_threads find_debuginfo simwork threads inline-samefile inline-crossfile runtime_obj_skip_c runtime_obj_skip_underflow AM_CFLAGS += $(AM_FLAG_M3264_PRI) AM_CXXFLAGS += $(AM_FLAG_M3264_PRI) @@ -45,6 +48,7 @@ inline_samefile_CFLAGS = $(AM_CFLAGS) -O2 -g inline_crossfile_CFLAGS = $(AM_CFLAGS) -O2 -g threads_LDADD = -lpthread +register_desc_threads_LDADD = -lpthread # Shim loaded by runtime_obj_skip_py.py via ctypes. Built unconditionally; # the test's prereq skips it if the .so is missing. diff --git a/callgrind/tests/register_desc.c b/callgrind/tests/register_desc.c new file mode 100644 index 000000000..9c7fc6d31 --- /dev/null +++ b/callgrind/tests/register_desc.c @@ -0,0 +1,29 @@ +// CALLGRIND_ADD_DESC queues desc lines for the next dumped part only, and +// a fork child does not inherit the lines its parent queued. + +#include +#include + +#include "../callgrind.h" + +int main(void) +{ + pid_t child; + + CALLGRIND_ADD_DESC("Benchmark pid: 42"); + CALLGRIND_ADD_DESC("Other: a\nb"); + CALLGRIND_DUMP_STATS_AT("first"); + + CALLGRIND_DUMP_STATS_AT("second"); + + CALLGRIND_ADD_DESC("Pending: kept"); + child = fork(); + if (child == 0) { + CALLGRIND_DUMP_STATS_AT("child"); + _exit(0); + } + waitpid(child, 0, 0); + CALLGRIND_DUMP_STATS_AT("third"); + + return 0; +} diff --git a/callgrind/tests/register_desc.post.exp b/callgrind/tests/register_desc.post.exp new file mode 100644 index 000000000..a83d29517 --- /dev/null +++ b/callgrind/tests/register_desc.post.exp @@ -0,0 +1,17 @@ +part: 1 +desc: Benchmark pid: 42 +desc: Other: a b +desc: Trigger: Client Request: first +part: 2 +desc: Trigger: Client Request: second +part: 3 +desc: Pending: kept +desc: Trigger: Client Request: third +part: 4 +desc: Trigger: Program termination +--- +part: 1 +desc: Trigger: Client Request: child +part: 2 +desc: Trigger: Program termination +--- diff --git a/callgrind/tests/register_desc.stderr.exp b/callgrind/tests/register_desc.stderr.exp new file mode 100644 index 000000000..6b4ff8165 --- /dev/null +++ b/callgrind/tests/register_desc.stderr.exp @@ -0,0 +1,11 @@ + + +Events : Ir +Collected : + +I refs: + +Events : Ir +Collected : + +I refs: diff --git a/callgrind/tests/register_desc.vgtest b/callgrind/tests/register_desc.vgtest new file mode 100644 index 000000000..22e7e88e6 --- /dev/null +++ b/callgrind/tests/register_desc.vgtest @@ -0,0 +1,4 @@ +prog: register_desc +vgopts: --combine-dumps=yes --callgrind-out-file=callgrind.out.register_desc.%p +post: sh -c 'for f in callgrind.out.register_desc.*; do if grep -q "^desc: Trigger: Client Request: child" "$f"; then c=$f; else p=$f; fi; done; for f in "$p" "$c"; do grep -E "^(part:|desc: (Benchmark|Other|Pending|Trigger))" "$f"; echo ---; done' +cleanup: rm -f callgrind.out.register_desc.* diff --git a/callgrind/tests/register_desc_threads.c b/callgrind/tests/register_desc_threads.c new file mode 100644 index 000000000..290058f82 --- /dev/null +++ b/callgrind/tests/register_desc_threads.c @@ -0,0 +1,58 @@ +// With --separate-threads=yes, a part has one section per thread: every +// section of the next dumped part carries the queued desc lines, and the part +// after it carries none of them. + +#include +#include + +#include "../callgrind.h" + +static int to_main[2], to_worker[2]; + +// gives the calling thread a nonzero delta, so its section is not skipped +static unsigned long work(void) +{ + volatile unsigned long sink = 0; + int i; + + for (i = 0; i < 100000; i++) + sink += i; + return sink; +} + +static void *worker(void *arg) +{ + char c = 0; + int i; + + for (i = 0; i < 2; i++) { + work(); + write(to_main[1], &c, 1); + read(to_worker[0], &c, 1); + } + return 0; +} + +int main(void) +{ + pthread_t t; + char c = 0; + + pipe(to_main); + pipe(to_worker); + pthread_create(&t, 0, worker, 0); + + work(); + read(to_main[0], &c, 1); + CALLGRIND_ADD_DESC("Benchmark pid: 42"); + CALLGRIND_DUMP_STATS_AT("first"); + + write(to_worker[1], &c, 1); + work(); + read(to_main[0], &c, 1); + CALLGRIND_DUMP_STATS_AT("second"); + + write(to_worker[1], &c, 1); + pthread_join(t, 0); + return 0; +} diff --git a/callgrind/tests/register_desc_threads.post.exp b/callgrind/tests/register_desc_threads.post.exp new file mode 100644 index 000000000..4958d09fe --- /dev/null +++ b/callgrind/tests/register_desc_threads.post.exp @@ -0,0 +1,20 @@ +part: 1 +desc: Benchmark pid: 42 +thread: 1 +desc: Trigger: Client Request: first +part: 1 +desc: Benchmark pid: 42 +thread: 2 +desc: Trigger: Client Request: first +part: 2 +thread: 1 +desc: Trigger: Client Request: second +part: 2 +thread: 2 +desc: Trigger: Client Request: second +part: 3 +thread: 1 +desc: Trigger: Program termination +part: 3 +thread: 2 +desc: Trigger: Program termination diff --git a/callgrind/tests/register_desc_threads.stderr.exp b/callgrind/tests/register_desc_threads.stderr.exp new file mode 100644 index 000000000..d0b7820ae --- /dev/null +++ b/callgrind/tests/register_desc_threads.stderr.exp @@ -0,0 +1,6 @@ + + +Events : Ir +Collected : + +I refs: diff --git a/callgrind/tests/register_desc_threads.vgtest b/callgrind/tests/register_desc_threads.vgtest new file mode 100644 index 000000000..f178fe32a --- /dev/null +++ b/callgrind/tests/register_desc_threads.vgtest @@ -0,0 +1,4 @@ +prog: register_desc_threads +vgopts: --separate-threads=yes --combine-dumps=yes --callgrind-out-file=callgrind.out.register_desc_threads +post: grep -E "^(part:|thread:|desc: (Benchmark|Trigger))" callgrind.out.register_desc_threads | awk '/^thread:/ { if (!($2 in id)) id[$2] = ++n; $2 = id[$2] } { print }' +cleanup: rm -f callgrind.out.register_desc_threads