aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/tools/perf/util/probe-event.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/probe-event.c')
-rw-r--r--tools/perf/util/probe-event.c496
1 files changed, 365 insertions, 131 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index df713a5d1e26..2a0ad9ecf0a2 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -29,6 +29,7 @@
#include "color.h"
#include "map.h"
#include "maps.h"
+#include "mutex.h"
#include "symbol.h"
#include <api/fs/fs.h>
#include "trace-event.h" /* For __maybe_unused */
@@ -43,11 +44,17 @@
#include <linux/ctype.h>
#include <linux/zalloc.h>
+#ifdef HAVE_DEBUGINFOD_SUPPORT
+#include <elfutils/debuginfod.h>
+#endif
+
#define PERFPROBE_GROUP "probe"
bool probe_event_dry_run; /* Dry run flag */
struct probe_conf probe_conf = { .magic_num = DEFAULT_PROBE_MAGIC_NUM };
+static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
+
#define semantic_error(msg ...) pr_err("Semantic error :" msg)
int e_snprintf(char *str, size_t size, const char *format, ...)
@@ -69,7 +76,6 @@ int init_probe_symbol_maps(bool user_only)
{
int ret;
- symbol_conf.sort_by_name = true;
symbol_conf.allow_aliases = true;
ret = symbol__init(NULL);
if (ret < 0) {
@@ -104,7 +110,6 @@ void exit_probe_symbol_maps(void)
static struct ref_reloc_sym *kernel_get_ref_reloc_sym(struct map **pmap)
{
- /* kmap->ref_reloc_sym should be set if host_machine is initialized */
struct kmap *kmap;
struct map *map = machine__kernel_map(host_machine);
@@ -129,43 +134,61 @@ static int kernel_get_symbol_address_by_name(const char *name, u64 *addr,
struct map *map;
/* ref_reloc_sym is just a label. Need a special fix*/
- reloc_sym = kernel_get_ref_reloc_sym(NULL);
+ reloc_sym = kernel_get_ref_reloc_sym(&map);
if (reloc_sym && strcmp(name, reloc_sym->name) == 0)
- *addr = (reloc) ? reloc_sym->addr : reloc_sym->unrelocated_addr;
+ *addr = (!map__reloc(map) || reloc) ? reloc_sym->addr :
+ reloc_sym->unrelocated_addr;
else {
sym = machine__find_kernel_symbol_by_name(host_machine, name, &map);
if (!sym)
return -ENOENT;
- *addr = map->unmap_ip(map, sym->start) -
- ((reloc) ? 0 : map->reloc) -
- ((reladdr) ? map->start : 0);
+ *addr = map__unmap_ip(map, sym->start) -
+ ((reloc) ? 0 : map__reloc(map)) -
+ ((reladdr) ? map__start(map) : 0);
+ }
+ return 0;
+}
+
+struct kernel_get_module_map_cb_args {
+ const char *module;
+ struct map *result;
+};
+
+static int kernel_get_module_map_cb(struct map *map, void *data)
+{
+ struct kernel_get_module_map_cb_args *args = data;
+ struct dso *dso = map__dso(map);
+ const char *short_name = dso->short_name; /* short_name is "[module]" */
+ u16 short_name_len = dso->short_name_len;
+
+ if (strncmp(short_name + 1, args->module, short_name_len - 2) == 0 &&
+ args->module[short_name_len - 2] == '\0') {
+ args->result = map__get(map);
+ return 1;
}
return 0;
}
static struct map *kernel_get_module_map(const char *module)
{
- struct maps *maps = machine__kernel_maps(host_machine);
- struct map *pos;
+ struct kernel_get_module_map_cb_args args = {
+ .module = module,
+ .result = NULL,
+ };
/* A file path -- this is an offline module */
if (module && strchr(module, '/'))
return dso__new_map(module);
if (!module) {
- pos = machine__kernel_map(host_machine);
- return map__get(pos);
- }
+ struct map *map = machine__kernel_map(host_machine);
- maps__for_each_entry(maps, pos) {
- /* short_name is "[module]" */
- if (strncmp(pos->dso->short_name + 1, module,
- pos->dso->short_name_len - 2) == 0 &&
- module[pos->dso->short_name_len - 2] == '\0') {
- return map__get(pos);
- }
+ return map__get(map);
}
- return NULL;
+
+ maps__for_each_map(machine__kernel_maps(host_machine), kernel_get_module_map_cb, &args);
+
+ return args.result;
}
struct map *get_target_map(const char *target, struct nsinfo *nsi, bool user)
@@ -173,10 +196,16 @@ struct map *get_target_map(const char *target, struct nsinfo *nsi, bool user)
/* Init maps of given executable or kernel */
if (user) {
struct map *map;
+ struct dso *dso;
map = dso__new_map(target);
- if (map && map->dso)
- map->dso->nsinfo = nsinfo__get(nsi);
+ dso = map ? map__dso(map) : NULL;
+ if (dso) {
+ mutex_lock(&dso->lock);
+ nsinfo__put(dso->nsinfo);
+ dso->nsinfo = nsinfo__get(nsi);
+ mutex_unlock(&dso->lock);
+ }
return map;
} else {
return kernel_get_module_map(target);
@@ -233,15 +262,15 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
clear_probe_trace_event(tevs + i);
}
-static bool kprobe_blacklist__listed(unsigned long address);
-static bool kprobe_warn_out_range(const char *symbol, unsigned long address)
+static bool kprobe_blacklist__listed(u64 address);
+static bool kprobe_warn_out_range(const char *symbol, u64 address)
{
struct map *map;
bool ret = false;
map = kernel_get_module_map(NULL);
if (map) {
- ret = address <= map->start || map->end < address;
+ ret = address <= map__start(map) || map__end(map) < address;
if (ret)
pr_warning("%s is out of .text, skip it.\n", symbol);
map__put(map);
@@ -326,9 +355,10 @@ static int kernel_get_module_dso(const char *module, struct dso **pdso)
char module_name[128];
snprintf(module_name, sizeof(module_name), "[%s]", module);
- map = maps__find_by_name(&host_machine->kmaps, module_name);
+ map = maps__find_by_name(machine__kernel_maps(host_machine), module_name);
if (map) {
- dso = map->dso;
+ dso = map__dso(map);
+ map__put(map);
goto found;
}
pr_debug("Failed to find module %s.\n", module);
@@ -336,7 +366,9 @@ static int kernel_get_module_dso(const char *module, struct dso **pdso)
}
map = machine__kernel_map(host_machine);
- dso = map->dso;
+ dso = map__dso(map);
+ if (!dso->has_build_id)
+ dso__read_running_kernel_build_id(dso, host_machine);
vmlinux_name = symbol_conf.vmlinux_name;
dso->load_errno = 0;
@@ -364,6 +396,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
struct symbol *sym;
u64 address = 0;
int ret = -ENOENT;
+ size_t idx;
/* This can work only for function-name based one */
if (!pp->function || pp->file)
@@ -374,11 +407,15 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
return -EINVAL;
/* Find the address of given function */
- map__for_each_symbol_by_name(map, pp->function, sym) {
- if (uprobes)
+ map__for_each_symbol_by_name(map, pp->function, sym, idx) {
+ if (uprobes) {
address = sym->start;
- else
- address = map->unmap_ip(map, sym->start) - map->reloc;
+ if (sym->type == STT_GNU_IFUNC)
+ pr_warning("Warning: The probe function (%s) is a GNU indirect function.\n"
+ "Consider identifying the final function used at run time and set the probe directly on that.\n",
+ pp->function);
+ } else
+ address = map__unmap_ip(map, sym->start) - map__reloc(map);
break;
}
if (!address) {
@@ -388,8 +425,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
pr_debug("Symbol %s address found : %" PRIx64 "\n",
pp->function, address);
- ret = debuginfo__find_probe_point(dinfo, (unsigned long)address,
- result);
+ ret = debuginfo__find_probe_point(dinfo, address, result);
if (ret <= 0)
ret = (!ret) ? -ENOENT : ret;
else {
@@ -448,6 +484,49 @@ static int get_alternative_line_range(struct debuginfo *dinfo,
return ret;
}
+#ifdef HAVE_DEBUGINFOD_SUPPORT
+static struct debuginfo *open_from_debuginfod(struct dso *dso, struct nsinfo *nsi,
+ bool silent)
+{
+ debuginfod_client *c = debuginfod_begin();
+ char sbuild_id[SBUILD_ID_SIZE + 1];
+ struct debuginfo *ret = NULL;
+ struct nscookie nsc;
+ char *path;
+ int fd;
+
+ if (!c)
+ return NULL;
+
+ build_id__sprintf(&dso->bid, sbuild_id);
+ fd = debuginfod_find_debuginfo(c, (const unsigned char *)sbuild_id,
+ 0, &path);
+ if (fd >= 0)
+ close(fd);
+ debuginfod_end(c);
+ if (fd < 0) {
+ if (!silent)
+ pr_debug("Failed to find debuginfo in debuginfod.\n");
+ return NULL;
+ }
+ if (!silent)
+ pr_debug("Load debuginfo from debuginfod (%s)\n", path);
+
+ nsinfo__mountns_enter(nsi, &nsc);
+ ret = debuginfo__new((const char *)path);
+ nsinfo__mountns_exit(&nsc);
+ return ret;
+}
+#else
+static inline
+struct debuginfo *open_from_debuginfod(struct dso *dso __maybe_unused,
+ struct nsinfo *nsi __maybe_unused,
+ bool silent __maybe_unused)
+{
+ return NULL;
+}
+#endif
+
/* Open new debuginfo of given module */
static struct debuginfo *open_debuginfo(const char *module, struct nsinfo *nsi,
bool silent)
@@ -467,6 +546,10 @@ static struct debuginfo *open_debuginfo(const char *module, struct nsinfo *nsi,
strcpy(reason, "(unknown)");
} else
dso__strerror_load(dso, reason, STRERR_BUFSIZE);
+ if (dso)
+ ret = open_from_debuginfod(dso, nsi, silent);
+ if (ret)
+ return ret;
if (!silent) {
if (module)
pr_err("Module %s is not loaded, please specify its full path name.\n", module);
@@ -530,7 +613,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)
{
Elf *elf;
@@ -575,7 +658,7 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
bool is_kprobe)
{
struct debuginfo *dinfo = NULL;
- unsigned long stext = 0;
+ u64 stext = 0;
u64 addr = tp->address;
int ret = -ENOENT;
@@ -603,8 +686,7 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
dinfo = debuginfo_cache__open(tp->module, verbose <= 0);
if (dinfo)
- ret = debuginfo__find_probe_point(dinfo,
- (unsigned long)addr, pp);
+ ret = debuginfo__find_probe_point(dinfo, addr, pp);
else
ret = -ENOENT;
@@ -619,14 +701,19 @@ error:
/* Adjust symbol name and address */
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;
u64 addr = tp->address - offs;
sym = map__find_symbol(map, addr);
- if (!sym)
- return -ENOENT;
+ if (!sym) {
+ /*
+ * If the address is in the inittext section, map can not
+ * find it. Ignore it if we are probing offline kernel.
+ */
+ return (symbol_conf.ignore_vmlinux_buildid) ? 0 : -ENOENT;
+ }
if (strcmp(sym->name, tp->symbol)) {
/* If we have no realname, use symbol for it */
@@ -657,7 +744,7 @@ post_process_offline_probe_trace_events(struct probe_trace_event *tevs,
int ntevs, const char *pathname)
{
struct map *map;
- unsigned long stext = 0;
+ u64 stext = 0;
int i, ret = 0;
/* Prepare a map for offline binary */
@@ -683,7 +770,7 @@ static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
struct nsinfo *nsi)
{
int i, ret = 0;
- unsigned long stext = 0;
+ u64 stext = 0;
if (!exec)
return 0;
@@ -728,7 +815,7 @@ post_process_module_probe_trace_events(struct probe_trace_event *tevs,
mod_name = find_module_name(module);
for (i = 0; i < ntevs; i++) {
ret = post_process_probe_trace_point(&tevs[i].point,
- map, (unsigned long)text_offs);
+ map, text_offs);
if (ret < 0)
break;
tevs[i].point.module =
@@ -761,7 +848,10 @@ post_process_kernel_probe_trace_events(struct probe_trace_event *tevs,
reloc_sym = kernel_get_ref_reloc_sym(&map);
if (!reloc_sym) {
- pr_warning("Relocated base symbol is not found!\n");
+ pr_warning("Relocated base symbol is not found! "
+ "Check /proc/sys/kernel/kptr_restrict\n"
+ "and /proc/sys/kernel/perf_event_paranoid. "
+ "Or run as privileged perf user.\n\n");
return -EINVAL;
}
@@ -791,7 +881,8 @@ post_process_kernel_probe_trace_events(struct probe_trace_event *tevs,
free(tevs[i].point.symbol);
tevs[i].point.symbol = tmp;
tevs[i].point.offset = tevs[i].point.address -
- reloc_sym->unrelocated_addr;
+ (map__reloc(map) ? reloc_sym->unrelocated_addr :
+ reloc_sym->addr);
}
return skipped;
}
@@ -835,10 +926,20 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
struct debuginfo *dinfo;
int ntevs, ret = 0;
+ /* Workaround for gcc #98776 issue.
+ * Perf failed to add kretprobe event with debuginfo of vmlinux which is
+ * compiled by gcc with -fpatchable-function-entry option enabled. The
+ * same issue with kernel module. The retprobe doesn`t need debuginfo.
+ * This workaround solution use map to query the probe function address
+ * for retprobe event.
+ */
+ if (pev->point.retprobe)
+ return 0;
+
dinfo = open_debuginfo(pev->target, pev->nsi, !need_dwarf);
if (!dinfo) {
if (need_dwarf)
- return -ENOENT;
+ return -ENODATA;
pr_debug("Could not open debuginfo. Try to use symbols.\n");
return 0;
}
@@ -875,9 +976,10 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
debuginfo__delete(dinfo);
if (ntevs == 0) { /* No error but failed to find probe point. */
- pr_warning("Probe point '%s' not found.\n",
- synthesize_perf_probe_point(&pev->point));
- return -ENOENT;
+ char *probe_point = synthesize_perf_probe_point(&pev->point);
+ pr_warning("Probe point '%s' not found.\n", probe_point);
+ free(probe_point);
+ return -ENODEV;
} else if (ntevs < 0) {
/* Error path : ntevs < 0 */
pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs);
@@ -946,6 +1048,7 @@ static int _show_one_line(FILE *fp, int l, bool skip, bool show_num)
static int __show_line_range(struct line_range *lr, const char *module,
bool user)
{
+ struct build_id bid;
int l = 1;
struct int_node *ln;
struct debuginfo *dinfo;
@@ -953,6 +1056,7 @@ static int __show_line_range(struct line_range *lr, const char *module,
int ret;
char *tmp;
char sbuf[STRERR_BUFSIZE];
+ char sbuild_id[SBUILD_ID_SIZE] = "";
/* Search a line range */
dinfo = open_debuginfo(module, NULL, false);
@@ -965,6 +1069,10 @@ static int __show_line_range(struct line_range *lr, const char *module,
if (!ret)
ret = debuginfo__find_line_range(dinfo, lr);
}
+ if (dinfo->build_id) {
+ build_id__init(&bid, dinfo->build_id, BUILD_ID_SIZE);
+ build_id__sprintf(&bid, sbuild_id);
+ }
debuginfo__delete(dinfo);
if (ret == 0 || ret == -ENOENT) {
pr_warning("Specified source line is not found.\n");
@@ -976,7 +1084,7 @@ static int __show_line_range(struct line_range *lr, const char *module,
/* Convert source file path */
tmp = lr->path;
- ret = get_real_path(tmp, lr->comp_dir, &lr->path);
+ ret = find_source_path(tmp, sbuild_id, lr->comp_dir, &lr->path);
/* Free old path when new path is assigned */
if (tmp != lr->path)
@@ -1009,7 +1117,7 @@ static int __show_line_range(struct line_range *lr, const char *module,
}
intlist__for_each_entry(ln, lr->line_list) {
- for (; ln->i > l; l++) {
+ for (; ln->i > (unsigned long)l; l++) {
ret = show_one_line(fp, l - lr->offset);
if (ret < 0)
goto end;
@@ -1267,7 +1375,7 @@ int parse_line_range_desc(const char *arg, struct line_range *lr)
/*
* Adjust the number of lines here.
* If the number of lines == 1, the
- * the end of line should be equal to
+ * end of line should be equal to
* the start of line.
*/
lr->end--;
@@ -1452,7 +1560,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
* so tmp[1] should always valid (but could be '\0').
*/
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') {
semantic_error("Invalid absolute address.\n");
return -EINVAL;
@@ -1693,8 +1801,10 @@ int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)
if (!pev->event && pev->point.function && pev->point.line
&& !pev->point.lazy_line && !pev->point.offset) {
if (asprintf(&pev->event, "%s_L%d", pev->point.function,
- pev->point.line) < 0)
- return -ENOMEM;
+ pev->point.line) < 0) {
+ ret = -ENOMEM;
+ goto out;
+ }
}
/* Copy arguments and ensure return probe has no C argument */
@@ -1827,7 +1937,7 @@ int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev)
argv[i] = NULL;
argc -= 1;
} else
- tp->address = strtoul(fmt1_str, NULL, 0);
+ tp->address = strtoull(fmt1_str, NULL, 0);
} else {
/* Only the symbol-based probe has offset */
tp->symbol = strdup(fmt1_str);
@@ -1915,7 +2025,7 @@ out:
}
/* Compose only probe point (not argument) */
-char *synthesize_perf_probe_point(struct perf_probe_point *pp)
+static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
{
struct strbuf buf;
char *tmp, *ret = NULL;
@@ -1968,14 +2078,18 @@ char *synthesize_perf_probe_command(struct perf_probe_event *pev)
goto out;
tmp = synthesize_perf_probe_point(&pev->point);
- if (!tmp || strbuf_addstr(&buf, tmp) < 0)
+ if (!tmp || strbuf_addstr(&buf, tmp) < 0) {
+ free(tmp);
goto out;
+ }
free(tmp);
for (i = 0; i < pev->nargs; i++) {
tmp = synthesize_perf_probe_arg(pev->args + i);
- if (!tmp || strbuf_addf(&buf, " %s", tmp) < 0)
+ if (!tmp || strbuf_addf(&buf, " %s", tmp) < 0) {
+ free(tmp);
goto out;
+ }
free(tmp);
}
@@ -2045,69 +2159,81 @@ static int synthesize_probe_trace_arg(struct probe_trace_arg *arg,
}
static int
-synthesize_uprobe_trace_def(struct probe_trace_event *tev, struct strbuf *buf)
+synthesize_probe_trace_args(struct probe_trace_event *tev, struct strbuf *buf)
{
- struct probe_trace_point *tp = &tev->point;
- int err;
+ int i, ret = 0;
- err = strbuf_addf(buf, "%s:0x%lx", tp->module, tp->address);
+ for (i = 0; i < tev->nargs && ret >= 0; i++)
+ ret = synthesize_probe_trace_arg(&tev->args[i], buf);
- if (err >= 0 && tp->ref_ctr_offset) {
- if (!uprobe_ref_ctr_is_supported())
- return -1;
- err = strbuf_addf(buf, "(0x%lx)", tp->ref_ctr_offset);
- }
- return err >= 0 ? 0 : -1;
+ return ret;
}
-char *synthesize_probe_trace_command(struct probe_trace_event *tev)
+static int
+synthesize_uprobe_trace_def(struct probe_trace_point *tp, struct strbuf *buf)
{
- struct probe_trace_point *tp = &tev->point;
- struct strbuf buf;
- char *ret = NULL;
- int i, err;
+ int err;
/* Uprobes must have tp->module */
- if (tev->uprobes && !tp->module)
- return NULL;
-
- if (strbuf_init(&buf, 32) < 0)
- return NULL;
-
- if (strbuf_addf(&buf, "%c:%s/%s ", tp->retprobe ? 'r' : 'p',
- tev->group, tev->event) < 0)
- goto error;
+ if (!tp->module)
+ return -EINVAL;
/*
* If tp->address == 0, then this point must be a
* absolute address uprobe.
* try_to_find_absolute_address() should have made
* tp->symbol to "0x0".
*/
- if (tev->uprobes && !tp->address) {
- if (!tp->symbol || strcmp(tp->symbol, "0x0"))
- goto error;
- }
+ if (!tp->address && (!tp->symbol || strcmp(tp->symbol, "0x0")))
+ return -EINVAL;
/* Use the tp->address for uprobes */
- if (tev->uprobes) {
- err = synthesize_uprobe_trace_def(tev, &buf);
- } else if (!strncmp(tp->symbol, "0x", 2)) {
+ err = strbuf_addf(buf, "%s:0x%" PRIx64, tp->module, tp->address);
+
+ if (err >= 0 && tp->ref_ctr_offset) {
+ if (!uprobe_ref_ctr_is_supported())
+ return -EINVAL;
+ err = strbuf_addf(buf, "(0x%lx)", tp->ref_ctr_offset);
+ }
+ return err >= 0 ? 0 : err;
+}
+
+static int
+synthesize_kprobe_trace_def(struct probe_trace_point *tp, struct strbuf *buf)
+{
+ if (!strncmp(tp->symbol, "0x", 2)) {
/* Absolute address. See try_to_find_absolute_address() */
- err = strbuf_addf(&buf, "%s%s0x%lx", tp->module ?: "",
+ return strbuf_addf(buf, "%s%s0x%" PRIx64, tp->module ?: "",
tp->module ? ":" : "", tp->address);
} else {
- err = strbuf_addf(&buf, "%s%s%s+%lu", tp->module ?: "",
+ return strbuf_addf(buf, "%s%s%s+%lu", tp->module ?: "",
tp->module ? ":" : "", tp->symbol, tp->offset);
}
+}
- if (err)
+char *synthesize_probe_trace_command(struct probe_trace_event *tev)
+{
+ struct probe_trace_point *tp = &tev->point;
+ struct strbuf buf;
+ char *ret = NULL;
+ int err;
+
+ if (strbuf_init(&buf, 32) < 0)
+ return NULL;
+
+ if (strbuf_addf(&buf, "%c:%s/%s ", tp->retprobe ? 'r' : 'p',
+ tev->group, tev->event) < 0)
goto error;
- for (i = 0; i < tev->nargs; i++)
- if (synthesize_probe_trace_arg(&tev->args[i], &buf) < 0)
- goto error;
+ if (tev->uprobes)
+ err = synthesize_uprobe_trace_def(tp, &buf);
+ else
+ err = synthesize_kprobe_trace_def(tp, &buf);
- ret = strbuf_detach(&buf, NULL);
+ if (err >= 0)
+ err = synthesize_probe_trace_args(tev, &buf);
+
+ if (err >= 0)
+ ret = strbuf_detach(&buf, NULL);
error:
strbuf_release(&buf);
return ret;
@@ -2143,14 +2269,12 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp,
goto out;
pp->retprobe = tp->retprobe;
- pp->offset = addr - map->unmap_ip(map, sym->start);
+ pp->offset = addr - map__unmap_ip(map, sym->start);
pp->function = strdup(sym->name);
ret = pp->function ? 0 : -ENOMEM;
out:
- if (map && !is_kprobe) {
- map__put(map);
- }
+ map__put(map);
return ret;
}
@@ -2175,7 +2299,7 @@ static int convert_to_perf_probe_point(struct probe_trace_point *tp,
pp->function = strdup(tp->symbol);
pp->offset = tp->offset;
} else {
- ret = e_snprintf(buf, 128, "0x%" PRIx64, (u64)tp->address);
+ ret = e_snprintf(buf, 128, "0x%" PRIx64, tp->address);
if (ret < 0)
return ret;
pp->function = strdup(buf);
@@ -2356,8 +2480,8 @@ void clear_probe_trace_event(struct probe_trace_event *tev)
struct kprobe_blacklist_node {
struct list_head list;
- unsigned long start;
- unsigned long end;
+ u64 start;
+ u64 end;
char *symbol;
};
@@ -2402,7 +2526,7 @@ static int kprobe_blacklist__load(struct list_head *blacklist)
}
INIT_LIST_HEAD(&node->list);
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;
break;
}
@@ -2418,7 +2542,7 @@ static int kprobe_blacklist__load(struct list_head *blacklist)
ret = -ENOMEM;
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);
ret++;
}
@@ -2430,8 +2554,7 @@ static int kprobe_blacklist__load(struct list_head *blacklist)
}
static struct kprobe_blacklist_node *
-kprobe_blacklist__find_by_address(struct list_head *blacklist,
- unsigned long address)
+kprobe_blacklist__find_by_address(struct list_head *blacklist, u64 address)
{
struct kprobe_blacklist_node *node;
@@ -2459,7 +2582,7 @@ static void kprobe_blacklist__release(void)
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);
}
@@ -2695,13 +2818,18 @@ static void warn_uprobe_event_compat(struct probe_trace_event *tev)
if (!tev->uprobes || tev->nargs == 0 || !buf)
goto out;
- for (i = 0; i < tev->nargs; i++)
- if (strglobmatch(tev->args[i].value, "[$@+-]*")) {
- pr_warning("Please upgrade your kernel to at least "
- "3.14 to have access to feature %s\n",
+ for (i = 0; i < tev->nargs; i++) {
+ if (strchr(tev->args[i].value, '@')) {
+ pr_warning("%s accesses a variable by symbol name, but that is not supported for user application probe.\n",
+ tev->args[i].value);
+ break;
+ }
+ if (strglobmatch(tev->args[i].value, "[$+-]*")) {
+ pr_warning("Please upgrade your kernel to at least 3.14 to have access to feature %s\n",
tev->args[i].value);
break;
}
+ }
out:
free(buf);
}
@@ -2859,7 +2987,7 @@ static int find_probe_functions(struct map *map, char *name,
bool cut_version = true;
if (map__load(map) < 0)
- return 0;
+ return -EACCES; /* Possible permission error to load symbols */
/* If user gives a version, don't cut off the version from symbols */
if (strchr(name, '@'))
@@ -2898,6 +3026,17 @@ void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused,
struct map *map __maybe_unused,
struct symbol *sym __maybe_unused) { }
+
+static void pr_kallsyms_access_error(void)
+{
+ pr_err("Please ensure you can read the /proc/kallsyms symbol addresses.\n"
+ "If /proc/sys/kernel/kptr_restrict is '2', you can not read\n"
+ "kernel symbol addresses even if you are a superuser. Please change\n"
+ "it to '1'. If kptr_restrict is '1', the superuser can read the\n"
+ "symbol addresses.\n"
+ "In that case, please run this command again with sudo.\n");
+}
+
/*
* Find probe function addresses from map.
* Return an error or the number of found probe_trace_event
@@ -2934,8 +3073,16 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
*/
num_matched_functions = find_probe_functions(map, pp->function, syms);
if (num_matched_functions <= 0) {
- pr_err("Failed to find symbol %s in %s\n", pp->function,
- pev->target ? : "kernel");
+ if (num_matched_functions == -EACCES) {
+ pr_err("Failed to load symbols from %s\n",
+ pev->target ?: "/proc/kallsyms");
+ if (pev->target)
+ pr_err("Please ensure the file is not stripped.\n");
+ else
+ pr_kallsyms_access_error();
+ } else
+ pr_err("Failed to find symbol %s in %s\n", pp->function,
+ pev->target ? : "kernel");
ret = -ENOENT;
goto out;
} else if (num_matched_functions > probe_conf.max_probes) {
@@ -2950,7 +3097,10 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
(!pp->retprobe || kretprobe_offset_is_supported())) {
reloc_sym = kernel_get_ref_reloc_sym(NULL);
if (!reloc_sym) {
- pr_warning("Relocated base symbol is not found!\n");
+ pr_warning("Relocated base symbol is not found! "
+ "Check /proc/sys/kernel/kptr_restrict\n"
+ "and /proc/sys/kernel/perf_event_paranoid. "
+ "Or run as privileged perf user.\n\n");
ret = -EINVAL;
goto out;
}
@@ -2968,6 +3118,19 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
for (j = 0; j < num_matched_functions; j++) {
sym = syms[j];
+ if (sym->type != STT_FUNC)
+ continue;
+
+ /* There can be duplicated symbols in the map */
+ for (i = 0; i < j; i++)
+ if (sym->start == syms[i]->start) {
+ pr_debug("Found duplicated symbol %s @ %" PRIx64 "\n",
+ sym->name, sym->start);
+ break;
+ }
+ if (i != j)
+ continue;
+
tev = (*tevs) + ret;
tp = &tev->point;
if (ret == num_matched_functions) {
@@ -2983,7 +3146,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
goto err_out;
}
/* Add one probe point */
- tp->address = map->unmap_ip(map, sym->start) + pp->offset;
+ tp->address = map__unmap_ip(map, sym->start) + pp->offset;
/* Check the kprobe (not in module) is within .text */
if (!pev->uprobes && !pev->target &&
@@ -3095,7 +3258,7 @@ static int try_to_find_absolute_address(struct perf_probe_event *pev,
* In __add_probe_trace_events, a NULL symbol is interpreted as
* invalid.
*/
- if (asprintf(&tp->symbol, "0x%lx", tp->address) < 0)
+ if (asprintf(&tp->symbol, "0x%" PRIx64, tp->address) < 0)
goto errout;
/* For kprobe, check range */
@@ -3106,7 +3269,7 @@ static int try_to_find_absolute_address(struct perf_probe_event *pev,
goto errout;
}
- if (asprintf(&tp->realname, "abs_%lx", tp->address) < 0)
+ if (asprintf(&tp->realname, "abs_%" PRIx64, tp->address) < 0)
goto errout;
if (pev->target) {
@@ -3143,7 +3306,7 @@ errout:
return err;
}
-/* Concatinate two arrays */
+/* Concatenate two arrays */
static void *memcat(void *a, size_t sz_a, void *b, size_t sz_b)
{
void *ret;
@@ -3173,7 +3336,7 @@ concat_probe_trace_events(struct probe_trace_event **tevs, int *ntevs,
if (*ntevs + ntevs2 > probe_conf.max_probes)
ret = -E2BIG;
else {
- /* Concatinate the array of probe_trace_event */
+ /* Concatenate the array of probe_trace_event */
new_tevs = memcat(*tevs, (*ntevs) * sizeof(**tevs),
*tevs2, ntevs2 * sizeof(**tevs2));
if (!new_tevs)
@@ -3438,6 +3601,78 @@ int show_probe_trace_events(struct perf_probe_event *pevs, int npevs)
return ret;
}
+static int show_bootconfig_event(struct probe_trace_event *tev)
+{
+ struct probe_trace_point *tp = &tev->point;
+ struct strbuf buf;
+ char *ret = NULL;
+ int err;
+
+ if (strbuf_init(&buf, 32) < 0)
+ return -ENOMEM;
+
+ err = synthesize_kprobe_trace_def(tp, &buf);
+ if (err >= 0)
+ err = synthesize_probe_trace_args(tev, &buf);
+ if (err >= 0)
+ ret = strbuf_detach(&buf, NULL);
+ strbuf_release(&buf);
+
+ if (ret) {
+ printf("'%s'", ret);
+ free(ret);
+ }
+
+ return err;
+}
+
+int show_bootconfig_events(struct perf_probe_event *pevs, int npevs)
+{
+ struct strlist *namelist = strlist__new(NULL, NULL);
+ struct probe_trace_event *tev;
+ struct perf_probe_event *pev;
+ char *cur_name = NULL;
+ int i, j, ret = 0;
+
+ if (!namelist)
+ return -ENOMEM;
+
+ for (j = 0; j < npevs && !ret; j++) {
+ pev = &pevs[j];
+ if (pev->group && strcmp(pev->group, "probe"))
+ pr_warning("WARN: Group name %s is ignored\n", pev->group);
+ if (pev->uprobes) {
+ pr_warning("ERROR: Bootconfig doesn't support uprobes\n");
+ ret = -EINVAL;
+ break;
+ }
+ for (i = 0; i < pev->ntevs && !ret; i++) {
+ tev = &pev->tevs[i];
+ /* Skip if the symbol is out of .text or blacklisted */
+ if (!tev->point.symbol && !pev->uprobes)
+ continue;
+
+ /* Set new name for tev (and update namelist) */
+ ret = probe_trace_event__set_name(tev, pev,
+ namelist, true);
+ if (ret)
+ break;
+
+ if (!cur_name || strcmp(cur_name, tev->event)) {
+ printf("%sftrace.event.kprobes.%s.probe = ",
+ cur_name ? "\n" : "", tev->event);
+ cur_name = tev->event;
+ } else
+ printf(", ");
+ ret = show_bootconfig_event(tev);
+ }
+ }
+ printf("\n");
+ strlist__delete(namelist);
+
+ return ret;
+}
+
int apply_perf_probe_events(struct perf_probe_event *pevs, int npevs)
{
int i, ret = 0;
@@ -3526,8 +3761,8 @@ out:
int show_available_funcs(const char *target, struct nsinfo *nsi,
struct strfilter *_filter, bool user)
{
- struct rb_node *nd;
struct map *map;
+ struct dso *dso;
int ret;
ret = init_probe_symbol_maps(user);
@@ -3553,18 +3788,17 @@ int show_available_funcs(const char *target, struct nsinfo *nsi,
(target) ? : "kernel");
goto end;
}
- if (!dso__sorted_by_name(map->dso))
- dso__sort_by_name(map->dso);
+ dso = map__dso(map);
+ dso__sort_by_name(dso);
/* Show all (filtered) symbols */
setup_pager();
- for (nd = rb_first_cached(&map->dso->symbol_names); nd;
- nd = rb_next(nd)) {
- struct symbol_name_rb_node *pos = rb_entry(nd, struct symbol_name_rb_node, rb_node);
+ for (size_t i = 0; i < dso->symbol_names_len; i++) {
+ struct symbol *pos = dso->symbol_names[i];
- if (strfilter__compare(_filter, pos->sym.name))
- printf("%s\n", pos->sym.name);
+ if (strfilter__compare(_filter, pos->name))
+ printf("%s\n", pos->name);
}
end:
map__put(map);