perf probe: Support glob wildcards for function name
Support glob wildcards for function name when adding new probes. This will allow us to build caches of function-entry level information with $params. e.g. ---- # perf probe --no-inlines --add 'kmalloc* $params' Added new events: probe:kmalloc_slab (on kmalloc* with $params) probe:kmalloc_large_node (on kmalloc* with $params) probe:kmalloc_order_trace (on kmalloc* with $params) You can now use it in all perf tools, such as: perf record -e probe:kmalloc_order_trace -aR sleep 1 # perf probe --list probe:kmalloc_large_node (on kmalloc_large_node@mm/slub.c with size flags node) probe:kmalloc_order_trace (on kmalloc_order_trace@mm/slub.c with size flags order) probe:kmalloc_slab (on kmalloc_slab@mm/slab_common.c with size flags) ---- Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com> Cc: David Ahern <dsahern@gmail.com> Cc: Hemant Kumar <hemant@linux.vnet.ibm.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Link: http://lkml.kernel.org/r/20150508010335.24812.19972.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
6cfd1f6805
commit
4c85935122
6 changed files with 59 additions and 11 deletions
|
@ -139,10 +139,26 @@ int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr,
|
||||||
bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
|
bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
|
||||||
{
|
{
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
name = dwarf_diename(dw_die);
|
name = dwarf_diename(dw_die);
|
||||||
return name ? (strcmp(tname, name) == 0) : false;
|
return name ? (strcmp(tname, name) == 0) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* die_match_name - Match diename and glob
|
||||||
|
* @dw_die: a DIE
|
||||||
|
* @glob: a string of target glob pattern
|
||||||
|
*
|
||||||
|
* Glob matching the name of @dw_die and @glob. Return false if matching fail.
|
||||||
|
*/
|
||||||
|
bool die_match_name(Dwarf_Die *dw_die, const char *glob)
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
name = dwarf_diename(dw_die);
|
||||||
|
return name ? strglobmatch(name, glob) : false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* die_get_call_lineno - Get callsite line number of inline-function instance
|
* die_get_call_lineno - Get callsite line number of inline-function instance
|
||||||
* @in_die: a DIE of an inlined function instance
|
* @in_die: a DIE of an inlined function instance
|
||||||
|
|
|
@ -47,6 +47,9 @@ extern bool die_is_func_instance(Dwarf_Die *dw_die);
|
||||||
/* Compare diename and tname */
|
/* Compare diename and tname */
|
||||||
extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname);
|
extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname);
|
||||||
|
|
||||||
|
/* Matching diename with glob pattern */
|
||||||
|
extern bool die_match_name(Dwarf_Die *dw_die, const char *glob);
|
||||||
|
|
||||||
/* Get callsite line number of inline-function instance */
|
/* Get callsite line number of inline-function instance */
|
||||||
extern int die_get_call_lineno(Dwarf_Die *in_die);
|
extern int die_get_call_lineno(Dwarf_Die *in_die);
|
||||||
|
|
||||||
|
|
|
@ -589,7 +589,11 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs,
|
||||||
if (!tmp)
|
if (!tmp)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
free(tevs[i].point.symbol);
|
/* If we have no realname, use symbol for it */
|
||||||
|
if (!tevs[i].point.realname)
|
||||||
|
tevs[i].point.realname = tevs[i].point.symbol;
|
||||||
|
else
|
||||||
|
free(tevs[i].point.symbol);
|
||||||
tevs[i].point.symbol = tmp;
|
tevs[i].point.symbol = tmp;
|
||||||
tevs[i].point.offset = tevs[i].point.address -
|
tevs[i].point.offset = tevs[i].point.address -
|
||||||
reloc_sym->unrelocated_addr;
|
reloc_sym->unrelocated_addr;
|
||||||
|
@ -1900,6 +1904,7 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)
|
||||||
free(tev->event);
|
free(tev->event);
|
||||||
free(tev->group);
|
free(tev->group);
|
||||||
free(tev->point.symbol);
|
free(tev->point.symbol);
|
||||||
|
free(tev->point.realname);
|
||||||
free(tev->point.module);
|
free(tev->point.module);
|
||||||
for (i = 0; i < tev->nargs; i++) {
|
for (i = 0; i < tev->nargs; i++) {
|
||||||
free(tev->args[i].name);
|
free(tev->args[i].name);
|
||||||
|
@ -2377,6 +2382,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
|
||||||
struct strlist *namelist;
|
struct strlist *namelist;
|
||||||
LIST_HEAD(blacklist);
|
LIST_HEAD(blacklist);
|
||||||
struct kprobe_blacklist_node *node;
|
struct kprobe_blacklist_node *node;
|
||||||
|
bool safename;
|
||||||
|
|
||||||
if (pev->uprobes)
|
if (pev->uprobes)
|
||||||
fd = open_uprobe_events(true);
|
fd = open_uprobe_events(true);
|
||||||
|
@ -2402,6 +2408,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
|
||||||
pr_debug("No kprobe blacklist support, ignored\n");
|
pr_debug("No kprobe blacklist support, ignored\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
safename = (pev->point.function && !strisglob(pev->point.function));
|
||||||
ret = 0;
|
ret = 0;
|
||||||
pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
|
pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
|
||||||
for (i = 0; i < ntevs; i++) {
|
for (i = 0; i < ntevs; i++) {
|
||||||
|
@ -2420,10 +2427,10 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
|
||||||
if (pev->event)
|
if (pev->event)
|
||||||
event = pev->event;
|
event = pev->event;
|
||||||
else
|
else
|
||||||
if (pev->point.function)
|
if (safename)
|
||||||
event = pev->point.function;
|
event = pev->point.function;
|
||||||
else
|
else
|
||||||
event = tev->point.symbol;
|
event = tev->point.realname;
|
||||||
if (pev->group)
|
if (pev->group)
|
||||||
group = pev->group;
|
group = pev->group;
|
||||||
else
|
else
|
||||||
|
@ -2488,9 +2495,11 @@ static int find_probe_functions(struct map *map, char *name)
|
||||||
{
|
{
|
||||||
int found = 0;
|
int found = 0;
|
||||||
struct symbol *sym;
|
struct symbol *sym;
|
||||||
|
struct rb_node *tmp;
|
||||||
|
|
||||||
map__for_each_symbol_by_name(map, name, sym) {
|
map__for_each_symbol(map, sym, tmp) {
|
||||||
found++;
|
if (strglobmatch(sym->name, name))
|
||||||
|
found++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return found;
|
return found;
|
||||||
|
|
|
@ -18,6 +18,7 @@ extern bool probe_event_dry_run;
|
||||||
|
|
||||||
/* kprobe-tracer and uprobe-tracer tracing point */
|
/* kprobe-tracer and uprobe-tracer tracing point */
|
||||||
struct probe_trace_point {
|
struct probe_trace_point {
|
||||||
|
char *realname; /* function real name (if needed) */
|
||||||
char *symbol; /* Base symbol */
|
char *symbol; /* Base symbol */
|
||||||
char *module; /* Module name */
|
char *module; /* Module name */
|
||||||
unsigned long offset; /* Offset from symbol */
|
unsigned long offset; /* Offset from symbol */
|
||||||
|
|
|
@ -717,7 +717,7 @@ static int find_best_scope_cb(Dwarf_Die *fn_die, void *data)
|
||||||
}
|
}
|
||||||
/* If the function name is given, that's what user expects */
|
/* If the function name is given, that's what user expects */
|
||||||
if (fsp->function) {
|
if (fsp->function) {
|
||||||
if (die_compare_name(fn_die, fsp->function)) {
|
if (die_match_name(fn_die, fsp->function)) {
|
||||||
memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die));
|
memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die));
|
||||||
fsp->found = true;
|
fsp->found = true;
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -920,13 +920,14 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
|
||||||
|
|
||||||
/* Check tag and diename */
|
/* Check tag and diename */
|
||||||
if (!die_is_func_def(sp_die) ||
|
if (!die_is_func_def(sp_die) ||
|
||||||
!die_compare_name(sp_die, pp->function))
|
!die_match_name(sp_die, pp->function))
|
||||||
return DWARF_CB_OK;
|
return DWARF_CB_OK;
|
||||||
|
|
||||||
/* Check declared file */
|
/* Check declared file */
|
||||||
if (pp->file && strtailcmp(pp->file, dwarf_decl_file(sp_die)))
|
if (pp->file && strtailcmp(pp->file, dwarf_decl_file(sp_die)))
|
||||||
return DWARF_CB_OK;
|
return DWARF_CB_OK;
|
||||||
|
|
||||||
|
pr_debug("Matched function: %s\n", dwarf_diename(sp_die));
|
||||||
pf->fname = dwarf_decl_file(sp_die);
|
pf->fname = dwarf_decl_file(sp_die);
|
||||||
if (pp->line) { /* Function relative line */
|
if (pp->line) { /* Function relative line */
|
||||||
dwarf_decl_line(sp_die, &pf->lno);
|
dwarf_decl_line(sp_die, &pf->lno);
|
||||||
|
@ -943,10 +944,20 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
|
||||||
/* TODO: Check the address in this function */
|
/* TODO: Check the address in this function */
|
||||||
param->retval = call_probe_finder(sp_die, pf);
|
param->retval = call_probe_finder(sp_die, pf);
|
||||||
}
|
}
|
||||||
} else if (!probe_conf.no_inlines)
|
} else if (!probe_conf.no_inlines) {
|
||||||
/* Inlined function: search instances */
|
/* Inlined function: search instances */
|
||||||
param->retval = die_walk_instances(sp_die,
|
param->retval = die_walk_instances(sp_die,
|
||||||
probe_point_inline_cb, (void *)pf);
|
probe_point_inline_cb, (void *)pf);
|
||||||
|
/* This could be a non-existed inline definition */
|
||||||
|
if (param->retval == -ENOENT && strisglob(pp->function))
|
||||||
|
param->retval = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We need to find other candidates */
|
||||||
|
if (strisglob(pp->function) && param->retval >= 0) {
|
||||||
|
param->retval = 0; /* We have to clear the result */
|
||||||
|
return DWARF_CB_OK;
|
||||||
|
}
|
||||||
|
|
||||||
return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */
|
return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */
|
||||||
}
|
}
|
||||||
|
@ -975,7 +986,7 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data)
|
||||||
if (dwarf_tag(param->sp_die) != DW_TAG_subprogram)
|
if (dwarf_tag(param->sp_die) != DW_TAG_subprogram)
|
||||||
return DWARF_CB_OK;
|
return DWARF_CB_OK;
|
||||||
|
|
||||||
if (die_compare_name(param->sp_die, param->function)) {
|
if (die_match_name(param->sp_die, param->function)) {
|
||||||
if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die))
|
if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die))
|
||||||
return DWARF_CB_OK;
|
return DWARF_CB_OK;
|
||||||
|
|
||||||
|
@ -1028,7 +1039,7 @@ static int debuginfo__find_probes(struct debuginfo *dbg,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* Fastpath: lookup by function name from .debug_pubnames section */
|
/* Fastpath: lookup by function name from .debug_pubnames section */
|
||||||
if (pp->function) {
|
if (pp->function && !strisglob(pp->function)) {
|
||||||
struct pubname_callback_param pubname_param = {
|
struct pubname_callback_param pubname_param = {
|
||||||
.function = pp->function,
|
.function = pp->function,
|
||||||
.file = pp->file,
|
.file = pp->file,
|
||||||
|
@ -1177,6 +1188,10 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
tev->point.realname = strdup(dwarf_diename(sc_die));
|
||||||
|
if (!tev->point.realname)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
|
pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
|
||||||
tev->point.offset);
|
tev->point.offset);
|
||||||
|
|
||||||
|
@ -1535,7 +1550,7 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
|
||||||
return DWARF_CB_OK;
|
return DWARF_CB_OK;
|
||||||
|
|
||||||
if (die_is_func_def(sp_die) &&
|
if (die_is_func_def(sp_die) &&
|
||||||
die_compare_name(sp_die, lr->function)) {
|
die_match_name(sp_die, lr->function)) {
|
||||||
lf->fname = dwarf_decl_file(sp_die);
|
lf->fname = dwarf_decl_file(sp_die);
|
||||||
dwarf_decl_line(sp_die, &lr->offset);
|
dwarf_decl_line(sp_die, &lr->offset);
|
||||||
pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset);
|
pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset);
|
||||||
|
|
|
@ -257,6 +257,10 @@ char **argv_split(const char *str, int *argcp);
|
||||||
void argv_free(char **argv);
|
void argv_free(char **argv);
|
||||||
bool strglobmatch(const char *str, const char *pat);
|
bool strglobmatch(const char *str, const char *pat);
|
||||||
bool strlazymatch(const char *str, const char *pat);
|
bool strlazymatch(const char *str, const char *pat);
|
||||||
|
static inline bool strisglob(const char *str)
|
||||||
|
{
|
||||||
|
return strpbrk(str, "*?[") != NULL;
|
||||||
|
}
|
||||||
int strtailcmp(const char *s1, const char *s2);
|
int strtailcmp(const char *s1, const char *s2);
|
||||||
char *strxfrchar(char *s, char from, char to);
|
char *strxfrchar(char *s, char from, char to);
|
||||||
unsigned long convert_unit(unsigned long value, char *unit);
|
unsigned long convert_unit(unsigned long value, char *unit);
|
||||||
|
|
Loading…
Add table
Reference in a new issue