diff options
Diffstat (limited to 'tools/perf/util/probe-finder.c')
-rw-r--r-- | tools/perf/util/probe-finder.c | 193 |
1 files changed, 162 insertions, 31 deletions
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index cd9f95e5044e..c470c49a804f 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -177,6 +177,17 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL) goto static_var; + /* Constant value */ + if (dwarf_attr(vr_die, DW_AT_const_value, &attr) && + immediate_value_is_supported()) { + Dwarf_Sword snum; + + dwarf_formsdata(&attr, &snum); + ret = asprintf(&tvar->value, "\\%ld", (long)snum); + + return ret < 0 ? -ENOMEM : 0; + } + /* TODO: handle more than 1 exprs */ if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) return -EINVAL; /* Broken DIE ? */ @@ -525,6 +536,14 @@ next: return 0; } +static void print_var_not_found(const char *varname) +{ + pr_err("Failed to find the location of the '%s' variable at this address.\n" + " Perhaps it has been optimized out.\n" + " Use -V with the --range option to show '%s' location range.\n", + varname, varname); +} + /* Show a variables in kprobe event format */ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) { @@ -536,11 +555,11 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, &pf->sp_die, pf->machine, pf->tvar); + if (ret == -ENOENT && pf->skip_empty_arg) + /* This can be found in other place. skip it */ + return 0; if (ret == -ENOENT || ret == -EINVAL) { - pr_err("Failed to find the location of the '%s' variable at this address.\n" - " Perhaps it has been optimized out.\n" - " Use -V with the --range option to show '%s' location range.\n", - pf->pvar->var, pf->pvar->var); + print_var_not_found(pf->pvar->var); } else if (ret == -ENOTSUP) pr_err("Sorry, we don't support this variable location yet.\n"); else if (ret == 0 && pf->pvar->field) { @@ -587,6 +606,8 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) /* Search again in global variables */ if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die)) { + if (pf->skip_empty_arg) + return 0; pr_warning("Failed to find '%s' in this function.\n", pf->pvar->var); ret = -ENOENT; @@ -604,38 +625,26 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, const char *function, struct probe_trace_point *tp) { - Dwarf_Addr eaddr, highaddr; + Dwarf_Addr eaddr; GElf_Sym sym; const char *symbol; /* Verify the address is correct */ - if (dwarf_entrypc(sp_die, &eaddr) != 0) { - pr_warning("Failed to get entry address of %s\n", - dwarf_diename(sp_die)); - return -ENOENT; - } - if (dwarf_highpc(sp_die, &highaddr) != 0) { - pr_warning("Failed to get end address of %s\n", - dwarf_diename(sp_die)); - return -ENOENT; - } - if (paddr > highaddr) { - pr_warning("Offset specified is greater than size of %s\n", + if (!dwarf_haspc(sp_die, paddr)) { + pr_warning("Specified offset is out of %s\n", dwarf_diename(sp_die)); return -EINVAL; } - symbol = dwarf_diename(sp_die); + /* Try to get actual symbol name from symtab */ + symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL); if (!symbol) { - /* Try to get the symbol name from symtab */ - symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL); - if (!symbol) { - pr_warning("Failed to find symbol at 0x%lx\n", - (unsigned long)paddr); - return -ENOENT; - } - eaddr = sym.st_value; + pr_warning("Failed to find symbol at 0x%lx\n", + (unsigned long)paddr); + return -ENOENT; } + eaddr = sym.st_value; + tp->offset = (unsigned long)(paddr - eaddr); tp->address = (unsigned long)paddr; tp->symbol = strdup(symbol); @@ -756,6 +765,16 @@ static int find_best_scope_cb(Dwarf_Die *fn_die, void *data) return 0; } +/* Return innermost DIE */ +static int find_inner_scope_cb(Dwarf_Die *fn_die, void *data) +{ + struct find_scope_param *fsp = data; + + memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); + fsp->found = true; + return 1; +} + /* Find an appropriate scope fits to given conditions */ static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem) { @@ -767,12 +786,50 @@ static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem) .die_mem = die_mem, .found = false, }; + int ret; - cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb, &fsp); + ret = cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb, + &fsp); + if (!ret && !fsp.found) + cu_walk_functions_at(&pf->cu_die, pf->addr, + find_inner_scope_cb, &fsp); return fsp.found ? die_mem : NULL; } +static int verify_representive_line(struct probe_finder *pf, const char *fname, + int lineno, Dwarf_Addr addr) +{ + const char *__fname, *__func = NULL; + Dwarf_Die die_mem; + int __lineno; + + /* Verify line number and address by reverse search */ + if (cu_find_lineinfo(&pf->cu_die, addr, &__fname, &__lineno) < 0) + return 0; + + pr_debug2("Reversed line: %s:%d\n", __fname, __lineno); + if (strcmp(fname, __fname) || lineno == __lineno) + return 0; + + pr_warning("This line is sharing the address with other lines.\n"); + + if (pf->pev->point.function) { + /* Find best match function name and lines */ + pf->addr = addr; + if (find_best_scope(pf, &die_mem) + && die_match_name(&die_mem, pf->pev->point.function) + && dwarf_decl_line(&die_mem, &lineno) == 0) { + __func = dwarf_diename(&die_mem); + __lineno -= lineno; + } + } + pr_warning("Please try to probe at %s:%d instead.\n", + __func ? : __fname, __lineno); + + return -ENOENT; +} + static int probe_point_line_walker(const char *fname, int lineno, Dwarf_Addr addr, void *data) { @@ -783,6 +840,9 @@ static int probe_point_line_walker(const char *fname, int lineno, if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) return 0; + if (verify_representive_line(pf, fname, lineno, addr)) + return -ENOENT; + pf->addr = addr; sc_die = find_best_scope(pf, &die_mem); if (!sc_die) { @@ -942,7 +1002,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) ret = find_probe_point_lazy(in_die, pf); else { /* Get probe address */ - if (dwarf_entrypc(in_die, &addr) != 0) { + if (die_entrypc(in_die, &addr) != 0) { pr_warning("Failed to get entry address of %s.\n", dwarf_diename(in_die)); return -ENOENT; @@ -994,7 +1054,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) param->retval = find_probe_point_by_line(pf); } else if (die_is_func_instance(sp_die)) { /* Instances always have the entry address */ - dwarf_entrypc(sp_die, &pf->addr); + die_entrypc(sp_die, &pf->addr); /* But in some case the entry address is 0 */ if (pf->addr == 0) { pr_debug("%s has no entry PC. Skipped\n", @@ -1334,6 +1394,44 @@ end: return ret; } +static int fill_empty_trace_arg(struct perf_probe_event *pev, + struct probe_trace_event *tevs, int ntevs) +{ + char **valp; + char *type; + int i, j, ret; + + for (i = 0; i < pev->nargs; i++) { + type = NULL; + for (j = 0; j < ntevs; j++) { + if (tevs[j].args[i].value) { + type = tevs[j].args[i].type; + break; + } + } + if (j == ntevs) { + print_var_not_found(pev->args[i].var); + return -ENOENT; + } + for (j = 0; j < ntevs; j++) { + valp = &tevs[j].args[i].value; + if (*valp) + continue; + + ret = asprintf(valp, "\\%lx", probe_conf.magic_num); + if (ret < 0) + return -ENOMEM; + /* Note that type can be NULL */ + if (type) { + tevs[j].args[i].type = strdup(type); + if (!tevs[j].args[i].type) + return -ENOMEM; + } + } + } + return 0; +} + /* Find probe_trace_events specified by perf_probe_event from debuginfo */ int debuginfo__find_trace_events(struct debuginfo *dbg, struct perf_probe_event *pev, @@ -1352,7 +1450,13 @@ int debuginfo__find_trace_events(struct debuginfo *dbg, tf.tevs = *tevs; tf.ntevs = 0; + if (pev->nargs != 0 && immediate_value_is_supported()) + tf.pf.skip_empty_arg = true; + ret = debuginfo__find_probes(dbg, &tf.pf); + if (ret >= 0 && tf.pf.skip_empty_arg) + ret = fill_empty_trace_arg(pev, tf.tevs, tf.ntevs); + if (ret < 0) { for (i = 0; i < tf.ntevs; i++) clear_probe_trace_event(&tf.tevs[i]); @@ -1425,6 +1529,18 @@ error: return DIE_FIND_CB_END; } +static bool available_var_finder_overlap(struct available_var_finder *af) +{ + int i; + + for (i = 0; i < af->nvls; i++) { + if (af->pf.addr == af->vls[i].point.address) + return true; + } + return false; + +} + /* Add a found vars into available variables list */ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) { @@ -1435,6 +1551,14 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) Dwarf_Die die_mem; int ret; + /* + * For some reason (e.g. different column assigned to same address), + * this callback can be called with the address which already passed. + * Ignore it first. + */ + if (available_var_finder_overlap(af)) + return 0; + /* Check number of tevs */ if (af->nvls == af->max_vls) { pr_warning("Too many( > %d) probe point found.\n", af->max_vls); @@ -1578,7 +1702,7 @@ int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, /* Get function entry information */ func = basefunc = dwarf_diename(&spdie); if (!func || - dwarf_entrypc(&spdie, &baseaddr) != 0 || + die_entrypc(&spdie, &baseaddr) != 0 || dwarf_decl_line(&spdie, &baseline) != 0) { lineno = 0; goto post; @@ -1595,7 +1719,7 @@ int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, while (die_find_top_inlinefunc(&spdie, (Dwarf_Addr)addr, &indie)) { /* There is an inline function */ - if (dwarf_entrypc(&indie, &_addr) == 0 && + if (die_entrypc(&indie, &_addr) == 0 && _addr == addr) { /* * addr is at an inline function entry. @@ -1675,12 +1799,19 @@ static int line_range_walk_cb(const char *fname, int lineno, void *data) { struct line_finder *lf = data; + const char *__fname; + int __lineno; int err; if ((strtailcmp(fname, lf->fname) != 0) || (lf->lno_s > lineno || lf->lno_e < lineno)) return 0; + /* Make sure this line can be reversable */ + if (cu_find_lineinfo(&lf->cu_die, addr, &__fname, &__lineno) > 0 + && (lineno != __lineno || strcmp(fname, __fname))) + return 0; + err = line_range_add_line(fname, lineno, lf->lr); if (err < 0 && err != -EEXIST) return err; |