Am 25.07.2015 um 00:42 schrieb Kay Hayen: > I am therefore considering an improvement to callgrind, that would try > to make it emit the Python function name, and the Python source file > instead. Do you think that is possible, and can you give me pointers to > inside the valgrind source distribution, what to change, where to hook? > > Looking at callstack.c in callgrind directory, would it be possible to > make direct accesses to the running binary to capture stuff from its > data, from function arguments, or is that not possible?
When callgrind detects that a function is entered, it is quite simple to modify the captured function name with whatever information is available, which directly is reflected in the profile output. See attached patch, which allows to add parameter values to function names to better distinguish recursive calls of same functions but with different parameters. Not sure this patch still applies, but should provide the direction. It was never added to main line, as the code depends on the calling convention and thus is not platform independent as VG tools are expected to be... the patch directly accesses stack values from the client code. For you, passing the currently executed Phython function name to callgrind for extending the function names should work fine. This can be done by adding a client call, are by setting a global var which is recognized by callgrind. Hope this helps. Josef > > Should this be possible, it would enable profiling of Python code with > valgrind that is accurate for even single iterations as it's based on > ticks. So please help me there, if you can. > > Yours, > Kay Hayen > > > ------------------------------------------------------------------------------ > _______________________________________________ > Valgrind-users mailing list > Valgrind-users@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/valgrind-users > -- Dr. Josef Weidendorfer, Informatik, Technische Universität München TUM I-10 - FMI 01.04.035 - Tel. 089 / 289-18454
diff --git a/callgrind/bbcc.c b/callgrind/bbcc.c index 7917c25..6949ed3 100644 --- a/callgrind/bbcc.c +++ b/callgrind/bbcc.c @@ -541,6 +541,47 @@ static void handleUnderflow(BB* bb) CLG_(zero_cost)( CLG_(sets).full, call_entry_up->enter_cost ); } +static +fn_node* seppar_node(fn_node* node, Int* sp) +{ + static Char fnname[FN_NAME_LEN]; + static Int pos; + fn_sep_param* sep; + fn_node* new_node; + + pos = VG_(sprintf)(fnname, "%s", node->name); + sep = node->separate_parameters; + + while(sep) { + Int v = sp[sep->parnum]; + if (sep->vcount == 0) + pos += VG_(sprintf)(fnname+pos,":p%d=%d", + sep->parnum, v); + else { + Int bucket = 0; + while((bucket<sep->vcount) && (v>=sep->val[bucket])) + bucket++; + if (bucket==0) + pos += VG_(sprintf)(fnname+pos,":p%d<%d", + sep->parnum, sep->val[0]); + else if (bucket == sep->vcount) + pos += VG_(sprintf)(fnname+pos,":p%d>%d", + sep->parnum, sep->val[bucket-1]-1); + else + pos += VG_(sprintf)(fnname+pos,":%d<p%d<%d", + sep->val[bucket-1]-1, + sep->parnum, sep->val[bucket]); + } + sep=sep->next; + } + + new_node = CLG_(get_fn_node_infile)(node->file, fnname); + if (new_node->pure_cxt == 0) { + new_node->pure_cxt = node->pure_cxt; + new_node->skip = node->skip; + } + return new_node; +} /* * Helper function called at start of each instrumented BB to setup @@ -754,7 +795,12 @@ void CLG_(setup_bbcc)(BB* bb) /* Change new context if needed, taking delayed_push into account */ if ((delayed_push && !skip) || (CLG_(current_state).cxt == 0)) { - CLG_(push_cxt)(CLG_(get_fn_node)(bb)); + fn_node* node = CLG_(get_fn_node)(bb); + + if (node->separate_parameters) + node = seppar_node(node, (Int*) sp); + + CLG_(push_cxt)(node); } CLG_ASSERT(CLG_(current_fn_stack).top > CLG_(current_fn_stack).bottom); diff --git a/callgrind/clo.c b/callgrind/clo.c index 881f3ea..7f66191 100644 --- a/callgrind/clo.c +++ b/callgrind/clo.c @@ -56,6 +56,9 @@ struct _fn_config { Int separate_callers; /* separate logging dependent on caller */ Int separate_recursions; /* separate logging of rec. levels */ + /* separate logging dependent on function parameters */ + fn_sep_param* separate_parameters; + #if CLG_ENABLE_DEBUG Int verbosity; /* Change debug verbosity level while in function */ #endif @@ -71,7 +74,7 @@ struct _fn_config { * - "*::print(" matches C++ methods "print" in all classes * without namespace. I.e. "*" doesn't match a "::". * - * We build a trie from patterns, and for a given function, we + * We build a hash trie from patterns, and for a given function, we * go down the tree and apply all non-default configurations. */ @@ -110,6 +113,7 @@ fn_config* new_fnc(void) fnc->group = CONFIG_DEFAULT; fnc->separate_callers = CONFIG_DEFAULT; fnc->separate_recursions = CONFIG_DEFAULT; + fnc->separate_parameters = NULL; #if CLG_ENABLE_DEBUG fnc->verbosity = CONFIG_DEFAULT; @@ -313,7 +317,12 @@ static fn_config* get_fnc(Char* name) return fnc; } - +static void append_seppar(fn_sep_param** list, + fn_sep_param* config) +{ + // FIXME: should do a deep copy of config and append to list + *list = config; +} static void update_fn_config1(fn_node* fn, fn_config* fnc) { @@ -344,6 +353,10 @@ static void update_fn_config1(fn_node* fn, fn_config* fnc) if (fnc->separate_recursions != CONFIG_DEFAULT) fn->separate_recursions = fnc->separate_recursions; + if (fnc->separate_parameters) + append_seppar(&fn->separate_parameters, + fnc->separate_parameters); + #if CLG_ENABLE_DEBUG if (fnc->verbosity != CONFIG_DEFAULT) fn->verbosity = fnc->verbosity; @@ -509,6 +522,72 @@ Bool CLG_(process_cmd_line_option)(Char* arg) fnc->separate_recursions = n; } + else if VG_STREQN(15, arg, "--separate-par=") { + // separation by function parameters: format is + // --separate-par=<fn_pattern>':'<intpar_num>[':'<intval>(','<intval>)*] + UInt vcount; + fn_sep_param* sep_param; + fn_config* fnc; + Char *s, *fpattern, *vals; + UInt parnum; + + s = arg+15; + fpattern = s; + // search for number after colon as end of function pattern + while(*s) { + if ((s[0] == ':') && (s[1]>='0') && (s[1]<='9')) break; + s++; + } + if (*s == 0) return False; + *s = 0; + parnum = VG_(strtoll10)(s+1, &s); + if (parnum == 0) return False; /* first parameter is #1 */ + if (*s == 0) + vcount = 0; + else { + if (*s != ':') return False; + vals = ++s; + vcount = 1; + while(*s) { + if (s[0] == ',') vcount++; + s++; + } + } + + sep_param = (fn_sep_param*) CLG_MALLOC("cl.clo.seppar.1", + sizeof(fn_sep_param) + + vcount * sizeof(int)); + sep_param->parnum = parnum; + sep_param->vcount = vcount; + sep_param->next = 0; + if (vcount>0) { + vcount = 0; + while(*vals) { + // value of 0 is acceptable + sep_param->val[vcount++] = VG_(strtoll10)(vals, &s); + while(*s && (s[0]!=',')) s++; + if (*s == 0) break; + vals = s+1; + } + CLG_ASSERT(vcount == sep_param->vcount); + } + fnc = get_fnc(fpattern); + // FIXME: Merge borders for same parameter, sort according parnum + sep_param->next = fnc->separate_parameters; + fnc->separate_parameters = sep_param; + + CLG_DEBUGIF(1) { + CLG_DEBUG(1, " seppar: fnpat '%s', number %d, vcount %d (", + fpattern, parnum, vcount); + if (vcount>0) { + VG_(printf)("%d", sep_param->val[0]); + for(vcount=1; vcount<sep_param->vcount; vcount++) + VG_(printf)(", %d", sep_param->val[vcount]); + } + VG_(printf)(")\n"); + } + } + else if VG_STR_CLO(arg, "--callgrind-out-file", CLG_(clo).out_format) {} else if VG_BOOL_CLO(arg, "--mangle-names", CLG_(clo).mangle_names) {} @@ -580,10 +659,12 @@ void CLG_(print_usage)(void) "\n cost entity separation options:\n" " --separate-threads=no|yes Separate data per thread [no]\n" " --separate-callers=<n> Separate functions by call chain length [0]\n" -" --separate-recs=<n> Separate function recursions upto level [2]\n" +" --separate-recs=<n> Separate function recursions upto level [1]\n" " --skip-plt=no|yes Ignore calls to/from PLT sections? [yes]\n" " --separate-recs<n>=<f> Separate <n> recursions for function <f>\n" " --separate-callers<n>=<f> Separate <n> callers for function <f>\n" +" --separate-par=<f>:<par>[:<v1>,...] Separate function <f> by integer\n" +" parameter <par>, optionally by bucket borders\n" " --skip-direct-rec=no|yes Ignore direct recursions? [yes]\n" " --fn-skip=<function> Ignore calls to/from function?\n" #if CLG_EXPERIMENTAL @@ -642,7 +723,7 @@ void CLG_(set_clo_defaults)(void) CLG_(clo).skip_plt = True; CLG_(clo).separate_callers = 0; - CLG_(clo).separate_recursions = 2; + CLG_(clo).separate_recursions = 1; CLG_(clo).skip_direct_recursion = False; /* Instrumentation */ diff --git a/callgrind/fn.c b/callgrind/fn.c index 6ed6f60..716240a 100644 --- a/callgrind/fn.c +++ b/callgrind/fn.c @@ -357,6 +357,7 @@ fn_node* new_fn_node(Char fnname[FILENAME_LEN], fn->group = 0; fn->separate_callers = CLG_(clo).separate_callers; fn->separate_recursions = CLG_(clo).separate_recursions; + fn->separate_parameters = NULL; #if CLG_ENABLE_DEBUG fn->verbosity = -1; @@ -372,9 +373,8 @@ fn_node* new_fn_node(Char fnname[FILENAME_LEN], /* Get a function node in hash2 with known file node. * hash nodes are created if needed */ -static -fn_node* get_fn_node_infile(file_node* curr_file_node, - Char fnname[FN_NAME_LEN]) +fn_node* CLG_(get_fn_node_infile)(file_node* curr_file_node, + Char fnname[FN_NAME_LEN]) { fn_node* curr_fn_node; UInt fnname_hash; @@ -408,7 +408,7 @@ fn_node* get_fn_node_inseg(DebugInfo* di, { obj_node *obj = CLG_(get_obj_node)(di); file_node *file = CLG_(get_file_node)(obj, filename); - fn_node *fn = get_fn_node_infile(file, fnname); + fn_node *fn = CLG_(get_fn_node_infile)(file, fnname); return fn; } diff --git a/callgrind/global.h b/callgrind/global.h index 367f2d7..b2c30cd 100644 --- a/callgrind/global.h +++ b/callgrind/global.h @@ -221,6 +221,7 @@ typedef struct _obj_node obj_node; typedef struct _fn_config fn_config; typedef struct _call_entry call_entry; typedef struct _thread_info thread_info; +typedef struct _fn_sep_param fn_sep_param; /* Costs of event sets. Aliases to arrays of 64-bit values */ typedef ULong* SimCost; /* All events the simulator can produce */ @@ -405,6 +406,16 @@ struct _BBCC { JmpData jmp[0]; }; +/* + struct holding information on function that should be logged separately + depending on its parameters +*/ +struct _fn_sep_param { + UInt parnum; /* nr of parameter to test */ + UInt vcount; + fn_sep_param* next; + Int val[0]; +}; /* the <number> of fn_node, file_node and obj_node are for compressed dumping * and a index into the dump boolean table and fn_info_table @@ -432,6 +443,8 @@ struct _fn_node { Int group; Int separate_callers; Int separate_recursions; + fn_sep_param* separate_parameters; + #if CLG_ENABLE_DEBUG Int verbosity; /* Stores old verbosity level while in function */ #endif @@ -733,11 +746,11 @@ void CLG_(copy_current_fn_array)(fn_array* dst); fn_array* CLG_(get_current_fn_array)(void); void CLG_(set_current_fn_array)(fn_array*); UInt* CLG_(get_fn_entry)(Int n); - void CLG_(init_obj_table)(void); obj_node* CLG_(get_obj_node)(DebugInfo* si); file_node* CLG_(get_file_node)(obj_node*, Char* filename); fn_node* CLG_(get_fn_node)(BB* bb); +fn_node* CLG_(get_fn_node_infile)(file_node* n, Char fnname[FN_NAME_LEN]); /* from bbcc.c */ void CLG_(init_bbcc_hash)(bbcc_hash* bbccs);
------------------------------------------------------------------------------
_______________________________________________ Valgrind-users mailing list Valgrind-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/valgrind-users