perf probe: Fix add event failure when running 32-bit perf in a 64-bit kernel

The "address" member of "struct probe_trace_point" uses long data type.
If kernel is 64-bit and perf program is 32-bit, size of "address"
variable is 32 bits.

As a result, upper 32 bits of address read from kernel are truncated, an
error occurs during address comparison in kprobe_warn_out_range().

Before:

  # perf probe -a schedule
  schedule is out of .text, skip it.
    Error: Failed to add events.

Solution:
  Change data type of "address" variable to u64 and change corresponding
address printing and value assignment.

After:

  # perf.new.new probe -a schedule
  Added new event:
    probe:schedule       (on schedule)

  You can now use it in all perf tools, such as:

          perf record -e probe:schedule -aR sleep 1

  # perf probe -l
    probe:schedule       (on schedule@kernel/sched/core.c)
  # perf record -e probe:schedule -aR sleep 1
  [ perf record: Woken up 1 times to write data ]
  [ perf record: Captured and wrote 0.156 MB perf.data (1366 samples) ]
  # perf report --stdio
  # To display the perf.data header info, please use --header/--header-only options.
  #
  #
  # Total Lost Samples: 0
  #
  # Samples: 1K of event 'probe:schedule'
  # Event count (approx.): 1366
  #
  # Overhead  Command          Shared Object      Symbol
  # ........  ...............  .................  ............
  #
       6.22%  migration/0      [kernel.kallsyms]  [k] schedule
       6.22%  migration/1      [kernel.kallsyms]  [k] schedule
       6.22%  migration/2      [kernel.kallsyms]  [k] schedule
       6.22%  migration/3      [kernel.kallsyms]  [k] schedule
       6.15%  migration/10     [kernel.kallsyms]  [k] schedule
       6.15%  migration/11     [kernel.kallsyms]  [k] schedule
       6.15%  migration/12     [kernel.kallsyms]  [k] schedule
       6.15%  migration/13     [kernel.kallsyms]  [k] schedule
       6.15%  migration/14     [kernel.kallsyms]  [k] schedule
       6.15%  migration/15     [kernel.kallsyms]  [k] schedule
       6.15%  migration/4      [kernel.kallsyms]  [k] schedule
       6.15%  migration/5      [kernel.kallsyms]  [k] schedule
       6.15%  migration/6      [kernel.kallsyms]  [k] schedule
       6.15%  migration/7      [kernel.kallsyms]  [k] schedule
       6.15%  migration/8      [kernel.kallsyms]  [k] schedule
       6.15%  migration/9      [kernel.kallsyms]  [k] schedule
       0.22%  rcu_sched        [kernel.kallsyms]  [k] schedule
  ...
  #
  # (Cannot load tips.txt file, please install perf!)
  #

Signed-off-by: Yang Jihong <yangjihong1@huawei.com>
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Frank Ch. Eigler <fche@redhat.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Jianlin Lv <jianlin.lv@arm.com>
Cc: Jin Yao <yao.jin@linux.intel.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Li Huafei <lihuafei1@huawei.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Link: http://lore.kernel.org/lkml/20210715063723.11926-1-yangjihong1@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Yang Jihong 2021-07-15 14:37:23 +08:00 committed by Arnaldo Carvalho de Melo
parent d4b3eedce1
commit 22a665513b
6 changed files with 38 additions and 42 deletions

View File

@ -113,14 +113,14 @@ static Dwarf_Line *cu_getsrc_die(Dwarf_Die *cu_die, Dwarf_Addr addr)
* *
* Find a line number and file name for @addr in @cu_die. * Find a line number and file name for @addr in @cu_die.
*/ */
int cu_find_lineinfo(Dwarf_Die *cu_die, unsigned long addr, int cu_find_lineinfo(Dwarf_Die *cu_die, Dwarf_Addr addr,
const char **fname, int *lineno) const char **fname, int *lineno)
{ {
Dwarf_Line *line; Dwarf_Line *line;
Dwarf_Die die_mem; Dwarf_Die die_mem;
Dwarf_Addr faddr; Dwarf_Addr faddr;
if (die_find_realfunc(cu_die, (Dwarf_Addr)addr, &die_mem) if (die_find_realfunc(cu_die, addr, &die_mem)
&& die_entrypc(&die_mem, &faddr) == 0 && && die_entrypc(&die_mem, &faddr) == 0 &&
faddr == addr) { faddr == addr) {
*fname = dwarf_decl_file(&die_mem); *fname = dwarf_decl_file(&die_mem);
@ -128,7 +128,7 @@ int cu_find_lineinfo(Dwarf_Die *cu_die, unsigned long addr,
goto out; goto out;
} }
line = cu_getsrc_die(cu_die, (Dwarf_Addr)addr); line = cu_getsrc_die(cu_die, addr);
if (line && dwarf_lineno(line, lineno) == 0) { if (line && dwarf_lineno(line, lineno) == 0) {
*fname = dwarf_linesrc(line, NULL, NULL); *fname = dwarf_linesrc(line, NULL, NULL);
if (!*fname) if (!*fname)

View File

@ -19,7 +19,7 @@ const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname);
const char *cu_get_comp_dir(Dwarf_Die *cu_die); const char *cu_get_comp_dir(Dwarf_Die *cu_die);
/* Get a line number and file name for given address */ /* Get a line number and file name for given address */
int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, int cu_find_lineinfo(Dwarf_Die *cudie, Dwarf_Addr addr,
const char **fname, int *lineno); const char **fname, int *lineno);
/* Walk on functions at given address */ /* Walk on functions at given address */

View File

@ -239,8 +239,8 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
clear_probe_trace_event(tevs + i); clear_probe_trace_event(tevs + i);
} }
static bool kprobe_blacklist__listed(unsigned long address); static bool kprobe_blacklist__listed(u64 address);
static bool kprobe_warn_out_range(const char *symbol, unsigned long address) static bool kprobe_warn_out_range(const char *symbol, u64 address)
{ {
struct map *map; struct map *map;
bool ret = false; bool ret = false;
@ -400,8 +400,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
pr_debug("Symbol %s address found : %" PRIx64 "\n", pr_debug("Symbol %s address found : %" PRIx64 "\n",
pp->function, address); pp->function, address);
ret = debuginfo__find_probe_point(dinfo, (unsigned long)address, ret = debuginfo__find_probe_point(dinfo, address, result);
result);
if (ret <= 0) if (ret <= 0)
ret = (!ret) ? -ENOENT : ret; ret = (!ret) ? -ENOENT : ret;
else { else {
@ -589,7 +588,7 @@ static void debuginfo_cache__exit(void)
} }
static int get_text_start_address(const char *exec, unsigned long *address, static int get_text_start_address(const char *exec, u64 *address,
struct nsinfo *nsi) struct nsinfo *nsi)
{ {
Elf *elf; Elf *elf;
@ -634,7 +633,7 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
bool is_kprobe) bool is_kprobe)
{ {
struct debuginfo *dinfo = NULL; struct debuginfo *dinfo = NULL;
unsigned long stext = 0; u64 stext = 0;
u64 addr = tp->address; u64 addr = tp->address;
int ret = -ENOENT; int ret = -ENOENT;
@ -662,8 +661,7 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
dinfo = debuginfo_cache__open(tp->module, verbose <= 0); dinfo = debuginfo_cache__open(tp->module, verbose <= 0);
if (dinfo) if (dinfo)
ret = debuginfo__find_probe_point(dinfo, ret = debuginfo__find_probe_point(dinfo, addr, pp);
(unsigned long)addr, pp);
else else
ret = -ENOENT; ret = -ENOENT;
@ -678,7 +676,7 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
/* Adjust symbol name and address */ /* Adjust symbol name and address */
static int post_process_probe_trace_point(struct probe_trace_point *tp, static int post_process_probe_trace_point(struct probe_trace_point *tp,
struct map *map, unsigned long offs) struct map *map, u64 offs)
{ {
struct symbol *sym; struct symbol *sym;
u64 addr = tp->address - offs; u64 addr = tp->address - offs;
@ -721,7 +719,7 @@ post_process_offline_probe_trace_events(struct probe_trace_event *tevs,
int ntevs, const char *pathname) int ntevs, const char *pathname)
{ {
struct map *map; struct map *map;
unsigned long stext = 0; u64 stext = 0;
int i, ret = 0; int i, ret = 0;
/* Prepare a map for offline binary */ /* Prepare a map for offline binary */
@ -747,7 +745,7 @@ static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
struct nsinfo *nsi) struct nsinfo *nsi)
{ {
int i, ret = 0; int i, ret = 0;
unsigned long stext = 0; u64 stext = 0;
if (!exec) if (!exec)
return 0; return 0;
@ -792,7 +790,7 @@ post_process_module_probe_trace_events(struct probe_trace_event *tevs,
mod_name = find_module_name(module); mod_name = find_module_name(module);
for (i = 0; i < ntevs; i++) { for (i = 0; i < ntevs; i++) {
ret = post_process_probe_trace_point(&tevs[i].point, ret = post_process_probe_trace_point(&tevs[i].point,
map, (unsigned long)text_offs); map, text_offs);
if (ret < 0) if (ret < 0)
break; break;
tevs[i].point.module = tevs[i].point.module =
@ -1536,7 +1534,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
* so tmp[1] should always valid (but could be '\0'). * so tmp[1] should always valid (but could be '\0').
*/ */
if (tmp && !strncmp(tmp, "0x", 2)) { if (tmp && !strncmp(tmp, "0x", 2)) {
pp->abs_address = strtoul(pp->function, &tmp, 0); pp->abs_address = strtoull(pp->function, &tmp, 0);
if (*tmp != '\0') { if (*tmp != '\0') {
semantic_error("Invalid absolute address.\n"); semantic_error("Invalid absolute address.\n");
return -EINVAL; return -EINVAL;
@ -1911,7 +1909,7 @@ int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev)
argv[i] = NULL; argv[i] = NULL;
argc -= 1; argc -= 1;
} else } else
tp->address = strtoul(fmt1_str, NULL, 0); tp->address = strtoull(fmt1_str, NULL, 0);
} else { } else {
/* Only the symbol-based probe has offset */ /* Only the symbol-based probe has offset */
tp->symbol = strdup(fmt1_str); tp->symbol = strdup(fmt1_str);
@ -2157,7 +2155,7 @@ synthesize_uprobe_trace_def(struct probe_trace_point *tp, struct strbuf *buf)
return -EINVAL; return -EINVAL;
/* Use the tp->address for uprobes */ /* Use the tp->address for uprobes */
err = strbuf_addf(buf, "%s:0x%lx", tp->module, tp->address); err = strbuf_addf(buf, "%s:0x%" PRIx64, tp->module, tp->address);
if (err >= 0 && tp->ref_ctr_offset) { if (err >= 0 && tp->ref_ctr_offset) {
if (!uprobe_ref_ctr_is_supported()) if (!uprobe_ref_ctr_is_supported())
@ -2172,7 +2170,7 @@ synthesize_kprobe_trace_def(struct probe_trace_point *tp, struct strbuf *buf)
{ {
if (!strncmp(tp->symbol, "0x", 2)) { if (!strncmp(tp->symbol, "0x", 2)) {
/* Absolute address. See try_to_find_absolute_address() */ /* Absolute address. See try_to_find_absolute_address() */
return strbuf_addf(buf, "%s%s0x%lx", tp->module ?: "", return strbuf_addf(buf, "%s%s0x%" PRIx64, tp->module ?: "",
tp->module ? ":" : "", tp->address); tp->module ? ":" : "", tp->address);
} else { } else {
return strbuf_addf(buf, "%s%s%s+%lu", tp->module ?: "", return strbuf_addf(buf, "%s%s%s+%lu", tp->module ?: "",
@ -2271,7 +2269,7 @@ static int convert_to_perf_probe_point(struct probe_trace_point *tp,
pp->function = strdup(tp->symbol); pp->function = strdup(tp->symbol);
pp->offset = tp->offset; pp->offset = tp->offset;
} else { } else {
ret = e_snprintf(buf, 128, "0x%" PRIx64, (u64)tp->address); ret = e_snprintf(buf, 128, "0x%" PRIx64, tp->address);
if (ret < 0) if (ret < 0)
return ret; return ret;
pp->function = strdup(buf); pp->function = strdup(buf);
@ -2452,8 +2450,8 @@ void clear_probe_trace_event(struct probe_trace_event *tev)
struct kprobe_blacklist_node { struct kprobe_blacklist_node {
struct list_head list; struct list_head list;
unsigned long start; u64 start;
unsigned long end; u64 end;
char *symbol; char *symbol;
}; };
@ -2498,7 +2496,7 @@ static int kprobe_blacklist__load(struct list_head *blacklist)
} }
INIT_LIST_HEAD(&node->list); INIT_LIST_HEAD(&node->list);
list_add_tail(&node->list, blacklist); list_add_tail(&node->list, blacklist);
if (sscanf(buf, "0x%lx-0x%lx", &node->start, &node->end) != 2) { if (sscanf(buf, "0x%" PRIx64 "-0x%" PRIx64, &node->start, &node->end) != 2) {
ret = -EINVAL; ret = -EINVAL;
break; break;
} }
@ -2514,7 +2512,7 @@ static int kprobe_blacklist__load(struct list_head *blacklist)
ret = -ENOMEM; ret = -ENOMEM;
break; break;
} }
pr_debug2("Blacklist: 0x%lx-0x%lx, %s\n", pr_debug2("Blacklist: 0x%" PRIx64 "-0x%" PRIx64 ", %s\n",
node->start, node->end, node->symbol); node->start, node->end, node->symbol);
ret++; ret++;
} }
@ -2526,8 +2524,7 @@ static int kprobe_blacklist__load(struct list_head *blacklist)
} }
static struct kprobe_blacklist_node * static struct kprobe_blacklist_node *
kprobe_blacklist__find_by_address(struct list_head *blacklist, kprobe_blacklist__find_by_address(struct list_head *blacklist, u64 address)
unsigned long address)
{ {
struct kprobe_blacklist_node *node; struct kprobe_blacklist_node *node;
@ -2555,7 +2552,7 @@ static void kprobe_blacklist__release(void)
kprobe_blacklist__delete(&kprobe_blacklist); kprobe_blacklist__delete(&kprobe_blacklist);
} }
static bool kprobe_blacklist__listed(unsigned long address) static bool kprobe_blacklist__listed(u64 address)
{ {
return !!kprobe_blacklist__find_by_address(&kprobe_blacklist, address); return !!kprobe_blacklist__find_by_address(&kprobe_blacklist, address);
} }
@ -3223,7 +3220,7 @@ static int try_to_find_absolute_address(struct perf_probe_event *pev,
* In __add_probe_trace_events, a NULL symbol is interpreted as * In __add_probe_trace_events, a NULL symbol is interpreted as
* invalid. * invalid.
*/ */
if (asprintf(&tp->symbol, "0x%lx", tp->address) < 0) if (asprintf(&tp->symbol, "0x%" PRIx64, tp->address) < 0)
goto errout; goto errout;
/* For kprobe, check range */ /* For kprobe, check range */
@ -3234,7 +3231,7 @@ static int try_to_find_absolute_address(struct perf_probe_event *pev,
goto errout; goto errout;
} }
if (asprintf(&tp->realname, "abs_%lx", tp->address) < 0) if (asprintf(&tp->realname, "abs_%" PRIx64, tp->address) < 0)
goto errout; goto errout;
if (pev->target) { if (pev->target) {

View File

@ -33,7 +33,7 @@ struct probe_trace_point {
char *module; /* Module name */ char *module; /* Module name */
unsigned long offset; /* Offset from symbol */ unsigned long offset; /* Offset from symbol */
unsigned long ref_ctr_offset; /* SDT reference counter offset */ unsigned long ref_ctr_offset; /* SDT reference counter offset */
unsigned long address; /* Actual address of the trace point */ u64 address; /* Actual address of the trace point */
bool retprobe; /* Return probe flag */ bool retprobe; /* Return probe flag */
}; };
@ -70,7 +70,7 @@ struct perf_probe_point {
bool retprobe; /* Return probe flag */ bool retprobe; /* Return probe flag */
char *lazy_line; /* Lazy matching pattern */ char *lazy_line; /* Lazy matching pattern */
unsigned long offset; /* Offset from function entry */ unsigned long offset; /* Offset from function entry */
unsigned long abs_address; /* Absolute address of the point */ u64 abs_address; /* Absolute address of the point */
}; };
/* Perf probe probing argument field chain */ /* Perf probe probing argument field chain */

View File

@ -668,7 +668,7 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod,
} }
tp->offset = (unsigned long)(paddr - eaddr); tp->offset = (unsigned long)(paddr - eaddr);
tp->address = (unsigned long)paddr; tp->address = paddr;
tp->symbol = strdup(symbol); tp->symbol = strdup(symbol);
if (!tp->symbol) if (!tp->symbol)
return -ENOMEM; return -ENOMEM;
@ -1707,7 +1707,7 @@ int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs,
} }
/* Reverse search */ /* Reverse search */
int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, int debuginfo__find_probe_point(struct debuginfo *dbg, u64 addr,
struct perf_probe_point *ppt) struct perf_probe_point *ppt)
{ {
Dwarf_Die cudie, spdie, indie; Dwarf_Die cudie, spdie, indie;
@ -1720,14 +1720,14 @@ int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr,
addr += baseaddr; addr += baseaddr;
/* Find cu die */ /* Find cu die */
if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr, &cudie)) { if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr, &cudie)) {
pr_warning("Failed to find debug information for address %lx\n", pr_warning("Failed to find debug information for address %" PRIx64 "\n",
addr); addr);
ret = -EINVAL; ret = -EINVAL;
goto end; goto end;
} }
/* Find a corresponding line (filename and lineno) */ /* Find a corresponding line (filename and lineno) */
cu_find_lineinfo(&cudie, addr, &fname, &lineno); cu_find_lineinfo(&cudie, (Dwarf_Addr)addr, &fname, &lineno);
/* Don't care whether it failed or not */ /* Don't care whether it failed or not */
/* Find a corresponding function (name, baseline and baseaddr) */ /* Find a corresponding function (name, baseline and baseaddr) */
@ -1742,7 +1742,7 @@ int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr,
} }
fname = dwarf_decl_file(&spdie); fname = dwarf_decl_file(&spdie);
if (addr == (unsigned long)baseaddr) { if (addr == baseaddr) {
/* Function entry - Relative line number is 0 */ /* Function entry - Relative line number is 0 */
lineno = baseline; lineno = baseline;
goto post; goto post;
@ -1788,7 +1788,7 @@ int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr,
if (lineno) if (lineno)
ppt->line = lineno - baseline; ppt->line = lineno - baseline;
else if (basefunc) { else if (basefunc) {
ppt->offset = addr - (unsigned long)baseaddr; ppt->offset = addr - baseaddr;
func = basefunc; func = basefunc;
} }
@ -1828,8 +1828,7 @@ static int line_range_add_line(const char *src, unsigned int lineno,
} }
static int line_range_walk_cb(const char *fname, int lineno, static int line_range_walk_cb(const char *fname, int lineno,
Dwarf_Addr addr __maybe_unused, Dwarf_Addr addr, void *data)
void *data)
{ {
struct line_finder *lf = data; struct line_finder *lf = data;
const char *__fname; const char *__fname;

View File

@ -46,7 +46,7 @@ int debuginfo__find_trace_events(struct debuginfo *dbg,
struct probe_trace_event **tevs); struct probe_trace_event **tevs);
/* Find a perf_probe_point from debuginfo */ /* Find a perf_probe_point from debuginfo */
int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, int debuginfo__find_probe_point(struct debuginfo *dbg, u64 addr,
struct perf_probe_point *ppt); struct perf_probe_point *ppt);
int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs, int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs,