|
@@ -70,34 +70,32 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
|
|
|
}
|
|
|
|
|
|
static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
|
|
|
-static int convert_name_to_addr(struct perf_probe_event *pev,
|
|
|
- const char *exec);
|
|
|
static void clear_probe_trace_event(struct probe_trace_event *tev);
|
|
|
-static struct machine machine;
|
|
|
+static struct machine *host_machine;
|
|
|
|
|
|
/* Initialize symbol maps and path of vmlinux/modules */
|
|
|
-static int init_vmlinux(void)
|
|
|
+static int init_symbol_maps(bool user_only)
|
|
|
{
|
|
|
int ret;
|
|
|
|
|
|
symbol_conf.sort_by_name = true;
|
|
|
- if (symbol_conf.vmlinux_name == NULL)
|
|
|
- symbol_conf.try_vmlinux_path = true;
|
|
|
- else
|
|
|
- pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name);
|
|
|
ret = symbol__init();
|
|
|
if (ret < 0) {
|
|
|
pr_debug("Failed to init symbol map.\n");
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- ret = machine__init(&machine, "", HOST_KERNEL_ID);
|
|
|
- if (ret < 0)
|
|
|
- goto out;
|
|
|
+ if (host_machine || user_only) /* already initialized */
|
|
|
+ return 0;
|
|
|
|
|
|
- if (machine__create_kernel_maps(&machine) < 0) {
|
|
|
- pr_debug("machine__create_kernel_maps() failed.\n");
|
|
|
- goto out;
|
|
|
+ if (symbol_conf.vmlinux_name)
|
|
|
+ pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name);
|
|
|
+
|
|
|
+ host_machine = machine__new_host();
|
|
|
+ if (!host_machine) {
|
|
|
+ pr_debug("machine__new_host() failed.\n");
|
|
|
+ symbol__exit();
|
|
|
+ ret = -1;
|
|
|
}
|
|
|
out:
|
|
|
if (ret < 0)
|
|
@@ -105,21 +103,66 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static void exit_symbol_maps(void)
|
|
|
+{
|
|
|
+ if (host_machine) {
|
|
|
+ machine__delete(host_machine);
|
|
|
+ host_machine = NULL;
|
|
|
+ }
|
|
|
+ symbol__exit();
|
|
|
+}
|
|
|
+
|
|
|
static struct symbol *__find_kernel_function_by_name(const char *name,
|
|
|
struct map **mapp)
|
|
|
{
|
|
|
- return machine__find_kernel_function_by_name(&machine, name, mapp,
|
|
|
+ return machine__find_kernel_function_by_name(host_machine, name, mapp,
|
|
|
NULL);
|
|
|
}
|
|
|
|
|
|
+static struct symbol *__find_kernel_function(u64 addr, struct map **mapp)
|
|
|
+{
|
|
|
+ return machine__find_kernel_function(host_machine, addr, mapp, NULL);
|
|
|
+}
|
|
|
+
|
|
|
+static struct ref_reloc_sym *kernel_get_ref_reloc_sym(void)
|
|
|
+{
|
|
|
+ /* kmap->ref_reloc_sym should be set if host_machine is initialized */
|
|
|
+ struct kmap *kmap;
|
|
|
+
|
|
|
+ if (map__load(host_machine->vmlinux_maps[MAP__FUNCTION], NULL) < 0)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ kmap = map__kmap(host_machine->vmlinux_maps[MAP__FUNCTION]);
|
|
|
+ return kmap->ref_reloc_sym;
|
|
|
+}
|
|
|
+
|
|
|
+static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc)
|
|
|
+{
|
|
|
+ struct ref_reloc_sym *reloc_sym;
|
|
|
+ struct symbol *sym;
|
|
|
+ struct map *map;
|
|
|
+
|
|
|
+ /* ref_reloc_sym is just a label. Need a special fix*/
|
|
|
+ reloc_sym = kernel_get_ref_reloc_sym();
|
|
|
+ if (reloc_sym && strcmp(name, reloc_sym->name) == 0)
|
|
|
+ return (reloc) ? reloc_sym->addr : reloc_sym->unrelocated_addr;
|
|
|
+ else {
|
|
|
+ sym = __find_kernel_function_by_name(name, &map);
|
|
|
+ if (sym)
|
|
|
+ return map->unmap_ip(map, sym->start) -
|
|
|
+ (reloc) ? 0 : map->reloc;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static struct map *kernel_get_module_map(const char *module)
|
|
|
{
|
|
|
struct rb_node *nd;
|
|
|
- struct map_groups *grp = &machine.kmaps;
|
|
|
+ struct map_groups *grp = &host_machine->kmaps;
|
|
|
|
|
|
/* A file path -- this is an offline module */
|
|
|
if (module && strchr(module, '/'))
|
|
|
- return machine__new_module(&machine, 0, module);
|
|
|
+ return machine__new_module(host_machine, 0, module);
|
|
|
|
|
|
if (!module)
|
|
|
module = "kernel";
|
|
@@ -141,7 +184,7 @@ static struct dso *kernel_get_module_dso(const char *module)
|
|
|
const char *vmlinux_name;
|
|
|
|
|
|
if (module) {
|
|
|
- list_for_each_entry(dso, &machine.kernel_dsos, node) {
|
|
|
+ list_for_each_entry(dso, &host_machine->kernel_dsos, node) {
|
|
|
if (strncmp(dso->short_name + 1, module,
|
|
|
dso->short_name_len - 2) == 0)
|
|
|
goto found;
|
|
@@ -150,7 +193,7 @@ static struct dso *kernel_get_module_dso(const char *module)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
- map = machine.vmlinux_maps[MAP__FUNCTION];
|
|
|
+ map = host_machine->vmlinux_maps[MAP__FUNCTION];
|
|
|
dso = map->dso;
|
|
|
|
|
|
vmlinux_name = symbol_conf.vmlinux_name;
|
|
@@ -173,20 +216,6 @@ const char *kernel_get_module_path(const char *module)
|
|
|
return (dso) ? dso->long_name : NULL;
|
|
|
}
|
|
|
|
|
|
-static int init_user_exec(void)
|
|
|
-{
|
|
|
- int ret = 0;
|
|
|
-
|
|
|
- symbol_conf.try_vmlinux_path = false;
|
|
|
- symbol_conf.sort_by_name = true;
|
|
|
- ret = symbol__init();
|
|
|
-
|
|
|
- if (ret < 0)
|
|
|
- pr_debug("Failed to init symbol map.\n");
|
|
|
-
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
static int convert_exec_to_group(const char *exec, char **result)
|
|
|
{
|
|
|
char *ptr1, *ptr2, *exec_copy;
|
|
@@ -218,32 +247,23 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static int convert_to_perf_probe_point(struct probe_trace_point *tp,
|
|
|
- struct perf_probe_point *pp)
|
|
|
+static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
|
|
|
{
|
|
|
- pp->function = strdup(tp->symbol);
|
|
|
-
|
|
|
- if (pp->function == NULL)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- pp->offset = tp->offset;
|
|
|
- pp->retprobe = tp->retprobe;
|
|
|
+ int i;
|
|
|
|
|
|
- return 0;
|
|
|
+ for (i = 0; i < ntevs; i++)
|
|
|
+ clear_probe_trace_event(tevs + i);
|
|
|
}
|
|
|
|
|
|
#ifdef HAVE_DWARF_SUPPORT
|
|
|
+
|
|
|
/* Open new debuginfo of given module */
|
|
|
static struct debuginfo *open_debuginfo(const char *module)
|
|
|
{
|
|
|
- const char *path;
|
|
|
+ const char *path = module;
|
|
|
|
|
|
- /* A file path -- this is an offline module */
|
|
|
- if (module && strchr(module, '/'))
|
|
|
- path = module;
|
|
|
- else {
|
|
|
+ if (!module || !strchr(module, '/')) {
|
|
|
path = kernel_get_module_path(module);
|
|
|
-
|
|
|
if (!path) {
|
|
|
pr_err("Failed to find path of %s module.\n",
|
|
|
module ?: "kernel");
|
|
@@ -253,46 +273,6 @@ static struct debuginfo *open_debuginfo(const char *module)
|
|
|
return debuginfo__new(path);
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * Convert trace point to probe point with debuginfo
|
|
|
- * Currently only handles kprobes.
|
|
|
- */
|
|
|
-static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
|
|
|
- struct perf_probe_point *pp)
|
|
|
-{
|
|
|
- struct symbol *sym;
|
|
|
- struct map *map;
|
|
|
- u64 addr;
|
|
|
- int ret = -ENOENT;
|
|
|
- struct debuginfo *dinfo;
|
|
|
-
|
|
|
- sym = __find_kernel_function_by_name(tp->symbol, &map);
|
|
|
- if (sym) {
|
|
|
- addr = map->unmap_ip(map, sym->start + tp->offset);
|
|
|
- pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol,
|
|
|
- tp->offset, addr);
|
|
|
-
|
|
|
- dinfo = debuginfo__new_online_kernel(addr);
|
|
|
- if (dinfo) {
|
|
|
- ret = debuginfo__find_probe_point(dinfo,
|
|
|
- (unsigned long)addr, pp);
|
|
|
- debuginfo__delete(dinfo);
|
|
|
- } else {
|
|
|
- pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n",
|
|
|
- addr);
|
|
|
- ret = -ENOENT;
|
|
|
- }
|
|
|
- }
|
|
|
- if (ret <= 0) {
|
|
|
- pr_debug("Failed to find corresponding probes from "
|
|
|
- "debuginfo. Use kprobe event information.\n");
|
|
|
- return convert_to_perf_probe_point(tp, pp);
|
|
|
- }
|
|
|
- pp->retprobe = tp->retprobe;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
static int get_text_start_address(const char *exec, unsigned long *address)
|
|
|
{
|
|
|
Elf *elf;
|
|
@@ -321,12 +301,62 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Convert trace point to probe point with debuginfo
|
|
|
+ */
|
|
|
+static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
|
|
|
+ struct perf_probe_point *pp,
|
|
|
+ bool is_kprobe)
|
|
|
+{
|
|
|
+ struct debuginfo *dinfo = NULL;
|
|
|
+ unsigned long stext = 0;
|
|
|
+ u64 addr = tp->address;
|
|
|
+ int ret = -ENOENT;
|
|
|
+
|
|
|
+ /* convert the address to dwarf address */
|
|
|
+ if (!is_kprobe) {
|
|
|
+ if (!addr) {
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ ret = get_text_start_address(tp->module, &stext);
|
|
|
+ if (ret < 0)
|
|
|
+ goto error;
|
|
|
+ addr += stext;
|
|
|
+ } else {
|
|
|
+ addr = kernel_get_symbol_address_by_name(tp->symbol, false);
|
|
|
+ if (addr == 0)
|
|
|
+ goto error;
|
|
|
+ addr += tp->offset;
|
|
|
+ }
|
|
|
+
|
|
|
+ pr_debug("try to find information at %" PRIx64 " in %s\n", addr,
|
|
|
+ tp->module ? : "kernel");
|
|
|
+
|
|
|
+ dinfo = open_debuginfo(tp->module);
|
|
|
+ if (dinfo) {
|
|
|
+ ret = debuginfo__find_probe_point(dinfo,
|
|
|
+ (unsigned long)addr, pp);
|
|
|
+ debuginfo__delete(dinfo);
|
|
|
+ } else {
|
|
|
+ pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", addr);
|
|
|
+ ret = -ENOENT;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ret > 0) {
|
|
|
+ pp->retprobe = tp->retprobe;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+error:
|
|
|
+ pr_debug("Failed to find corresponding probes from debuginfo.\n");
|
|
|
+ return ret ? : -ENOENT;
|
|
|
+}
|
|
|
+
|
|
|
static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
|
|
|
int ntevs, const char *exec)
|
|
|
{
|
|
|
int i, ret = 0;
|
|
|
- unsigned long offset, stext = 0;
|
|
|
- char buf[32];
|
|
|
+ unsigned long stext = 0;
|
|
|
|
|
|
if (!exec)
|
|
|
return 0;
|
|
@@ -337,15 +367,9 @@ static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
|
|
|
|
|
|
for (i = 0; i < ntevs && ret >= 0; i++) {
|
|
|
/* point.address is the addres of point.symbol + point.offset */
|
|
|
- offset = tevs[i].point.address - stext;
|
|
|
- tevs[i].point.offset = 0;
|
|
|
- zfree(&tevs[i].point.symbol);
|
|
|
- ret = e_snprintf(buf, 32, "0x%lx", offset);
|
|
|
- if (ret < 0)
|
|
|
- break;
|
|
|
+ tevs[i].point.address -= stext;
|
|
|
tevs[i].point.module = strdup(exec);
|
|
|
- tevs[i].point.symbol = strdup(buf);
|
|
|
- if (!tevs[i].point.symbol || !tevs[i].point.module) {
|
|
|
+ if (!tevs[i].point.module) {
|
|
|
ret = -ENOMEM;
|
|
|
break;
|
|
|
}
|
|
@@ -388,12 +412,40 @@ static int add_module_to_probe_trace_events(struct probe_trace_event *tevs,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
|
|
|
+/* Post processing the probe events */
|
|
|
+static int post_process_probe_trace_events(struct probe_trace_event *tevs,
|
|
|
+ int ntevs, const char *module,
|
|
|
+ bool uprobe)
|
|
|
{
|
|
|
+ struct ref_reloc_sym *reloc_sym;
|
|
|
+ char *tmp;
|
|
|
int i;
|
|
|
|
|
|
- for (i = 0; i < ntevs; i++)
|
|
|
- clear_probe_trace_event(tevs + i);
|
|
|
+ if (uprobe)
|
|
|
+ return add_exec_to_probe_trace_events(tevs, ntevs, module);
|
|
|
+
|
|
|
+ /* Note that currently ref_reloc_sym based probe is not for drivers */
|
|
|
+ if (module)
|
|
|
+ return add_module_to_probe_trace_events(tevs, ntevs, module);
|
|
|
+
|
|
|
+ reloc_sym = kernel_get_ref_reloc_sym();
|
|
|
+ if (!reloc_sym) {
|
|
|
+ pr_warning("Relocated base symbol is not found!\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < ntevs; i++) {
|
|
|
+ if (tevs[i].point.address) {
|
|
|
+ tmp = strdup(reloc_sym->name);
|
|
|
+ if (!tmp)
|
|
|
+ return -ENOMEM;
|
|
|
+ free(tevs[i].point.symbol);
|
|
|
+ tevs[i].point.symbol = tmp;
|
|
|
+ tevs[i].point.offset = tevs[i].point.address -
|
|
|
+ reloc_sym->unrelocated_addr;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/* Try to find perf_probe_event with debuginfo */
|
|
@@ -416,21 +468,16 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+ pr_debug("Try to find probe point from debuginfo.\n");
|
|
|
/* Searching trace events corresponding to a probe event */
|
|
|
ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs);
|
|
|
|
|
|
debuginfo__delete(dinfo);
|
|
|
|
|
|
if (ntevs > 0) { /* Succeeded to find trace events */
|
|
|
- pr_debug("find %d probe_trace_events.\n", ntevs);
|
|
|
- if (target) {
|
|
|
- if (pev->uprobes)
|
|
|
- ret = add_exec_to_probe_trace_events(*tevs,
|
|
|
- ntevs, target);
|
|
|
- else
|
|
|
- ret = add_module_to_probe_trace_events(*tevs,
|
|
|
- ntevs, target);
|
|
|
- }
|
|
|
+ pr_debug("Found %d probe_trace_events.\n", ntevs);
|
|
|
+ ret = post_process_probe_trace_events(*tevs, ntevs,
|
|
|
+ target, pev->uprobes);
|
|
|
if (ret < 0) {
|
|
|
clear_probe_trace_events(*tevs, ntevs);
|
|
|
zfree(tevs);
|
|
@@ -563,20 +610,16 @@ static int _show_one_line(FILE *fp, int l, bool skip, bool show_num)
|
|
|
* Show line-range always requires debuginfo to find source file and
|
|
|
* line number.
|
|
|
*/
|
|
|
-int show_line_range(struct line_range *lr, const char *module)
|
|
|
+static int __show_line_range(struct line_range *lr, const char *module)
|
|
|
{
|
|
|
int l = 1;
|
|
|
- struct line_node *ln;
|
|
|
+ struct int_node *ln;
|
|
|
struct debuginfo *dinfo;
|
|
|
FILE *fp;
|
|
|
int ret;
|
|
|
char *tmp;
|
|
|
|
|
|
/* Search a line range */
|
|
|
- ret = init_vmlinux();
|
|
|
- if (ret < 0)
|
|
|
- return ret;
|
|
|
-
|
|
|
dinfo = open_debuginfo(module);
|
|
|
if (!dinfo) {
|
|
|
pr_warning("Failed to open debuginfo file.\n");
|
|
@@ -623,8 +666,8 @@ int show_line_range(struct line_range *lr, const char *module)
|
|
|
goto end;
|
|
|
}
|
|
|
|
|
|
- list_for_each_entry(ln, &lr->line_list, list) {
|
|
|
- for (; ln->line > l; l++) {
|
|
|
+ intlist__for_each(ln, lr->line_list) {
|
|
|
+ for (; ln->i > l; l++) {
|
|
|
ret = show_one_line(fp, l - lr->offset);
|
|
|
if (ret < 0)
|
|
|
goto end;
|
|
@@ -646,6 +689,19 @@ end:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+int show_line_range(struct line_range *lr, const char *module)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = init_symbol_maps(false);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ ret = __show_line_range(lr, module);
|
|
|
+ exit_symbol_maps();
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static int show_available_vars_at(struct debuginfo *dinfo,
|
|
|
struct perf_probe_event *pev,
|
|
|
int max_vls, struct strfilter *_filter,
|
|
@@ -707,14 +763,15 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
|
|
int i, ret = 0;
|
|
|
struct debuginfo *dinfo;
|
|
|
|
|
|
- ret = init_vmlinux();
|
|
|
+ ret = init_symbol_maps(false);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
dinfo = open_debuginfo(module);
|
|
|
if (!dinfo) {
|
|
|
pr_warning("Failed to open debuginfo file.\n");
|
|
|
- return -ENOENT;
|
|
|
+ ret = -ENOENT;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
setup_pager();
|
|
@@ -724,23 +781,19 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
|
|
externs);
|
|
|
|
|
|
debuginfo__delete(dinfo);
|
|
|
+out:
|
|
|
+ exit_symbol_maps();
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
#else /* !HAVE_DWARF_SUPPORT */
|
|
|
|
|
|
-static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
|
|
|
- struct perf_probe_point *pp)
|
|
|
+static int
|
|
|
+find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused,
|
|
|
+ struct perf_probe_point *pp __maybe_unused,
|
|
|
+ bool is_kprobe __maybe_unused)
|
|
|
{
|
|
|
- struct symbol *sym;
|
|
|
-
|
|
|
- sym = __find_kernel_function_by_name(tp->symbol, NULL);
|
|
|
- if (!sym) {
|
|
|
- pr_err("Failed to find symbol %s in kernel.\n", tp->symbol);
|
|
|
- return -ENOENT;
|
|
|
- }
|
|
|
-
|
|
|
- return convert_to_perf_probe_point(tp, pp);
|
|
|
+ return -ENOSYS;
|
|
|
}
|
|
|
|
|
|
static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
|
|
@@ -776,24 +829,22 @@ int show_available_vars(struct perf_probe_event *pevs __maybe_unused,
|
|
|
|
|
|
void line_range__clear(struct line_range *lr)
|
|
|
{
|
|
|
- struct line_node *ln;
|
|
|
-
|
|
|
free(lr->function);
|
|
|
free(lr->file);
|
|
|
free(lr->path);
|
|
|
free(lr->comp_dir);
|
|
|
- while (!list_empty(&lr->line_list)) {
|
|
|
- ln = list_first_entry(&lr->line_list, struct line_node, list);
|
|
|
- list_del(&ln->list);
|
|
|
- free(ln);
|
|
|
- }
|
|
|
+ intlist__delete(lr->line_list);
|
|
|
memset(lr, 0, sizeof(*lr));
|
|
|
}
|
|
|
|
|
|
-void line_range__init(struct line_range *lr)
|
|
|
+int line_range__init(struct line_range *lr)
|
|
|
{
|
|
|
memset(lr, 0, sizeof(*lr));
|
|
|
- INIT_LIST_HEAD(&lr->line_list);
|
|
|
+ lr->line_list = intlist__new(NULL);
|
|
|
+ if (!lr->line_list)
|
|
|
+ return -ENOMEM;
|
|
|
+ else
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static int parse_line_num(char **ptr, int *val, const char *what)
|
|
@@ -1267,16 +1318,21 @@ static int parse_probe_trace_command(const char *cmd,
|
|
|
} else
|
|
|
p = argv[1];
|
|
|
fmt1_str = strtok_r(p, "+", &fmt);
|
|
|
- tp->symbol = strdup(fmt1_str);
|
|
|
- if (tp->symbol == NULL) {
|
|
|
- ret = -ENOMEM;
|
|
|
- goto out;
|
|
|
+ if (fmt1_str[0] == '0') /* only the address started with 0x */
|
|
|
+ tp->address = strtoul(fmt1_str, NULL, 0);
|
|
|
+ else {
|
|
|
+ /* Only the symbol-based probe has offset */
|
|
|
+ tp->symbol = strdup(fmt1_str);
|
|
|
+ if (tp->symbol == NULL) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ fmt2_str = strtok_r(NULL, "", &fmt);
|
|
|
+ if (fmt2_str == NULL)
|
|
|
+ tp->offset = 0;
|
|
|
+ else
|
|
|
+ tp->offset = strtoul(fmt2_str, NULL, 10);
|
|
|
}
|
|
|
- fmt2_str = strtok_r(NULL, "", &fmt);
|
|
|
- if (fmt2_str == NULL)
|
|
|
- tp->offset = 0;
|
|
|
- else
|
|
|
- tp->offset = strtoul(fmt2_str, NULL, 10);
|
|
|
|
|
|
tev->nargs = argc - 2;
|
|
|
tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
|
|
@@ -1518,20 +1574,27 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
|
|
|
if (buf == NULL)
|
|
|
return NULL;
|
|
|
|
|
|
+ len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s ", tp->retprobe ? 'r' : 'p',
|
|
|
+ tev->group, tev->event);
|
|
|
+ if (len <= 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ /* Uprobes must have tp->address and tp->module */
|
|
|
+ if (tev->uprobes && (!tp->address || !tp->module))
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ /* Use the tp->address for uprobes */
|
|
|
if (tev->uprobes)
|
|
|
- len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s:%s",
|
|
|
- tp->retprobe ? 'r' : 'p',
|
|
|
- tev->group, tev->event,
|
|
|
- tp->module, tp->symbol);
|
|
|
+ ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s:0x%lx",
|
|
|
+ tp->module, tp->address);
|
|
|
else
|
|
|
- len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu",
|
|
|
- tp->retprobe ? 'r' : 'p',
|
|
|
- tev->group, tev->event,
|
|
|
+ ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s%s+%lu",
|
|
|
tp->module ?: "", tp->module ? ":" : "",
|
|
|
tp->symbol, tp->offset);
|
|
|
|
|
|
- if (len <= 0)
|
|
|
+ if (ret <= 0)
|
|
|
goto error;
|
|
|
+ len += ret;
|
|
|
|
|
|
for (i = 0; i < tev->nargs; i++) {
|
|
|
ret = synthesize_probe_trace_arg(&tev->args[i], buf + len,
|
|
@@ -1547,6 +1610,79 @@ error:
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+static int find_perf_probe_point_from_map(struct probe_trace_point *tp,
|
|
|
+ struct perf_probe_point *pp,
|
|
|
+ bool is_kprobe)
|
|
|
+{
|
|
|
+ struct symbol *sym = NULL;
|
|
|
+ struct map *map;
|
|
|
+ u64 addr;
|
|
|
+ int ret = -ENOENT;
|
|
|
+
|
|
|
+ if (!is_kprobe) {
|
|
|
+ map = dso__new_map(tp->module);
|
|
|
+ if (!map)
|
|
|
+ goto out;
|
|
|
+ addr = tp->address;
|
|
|
+ sym = map__find_symbol(map, addr, NULL);
|
|
|
+ } else {
|
|
|
+ addr = kernel_get_symbol_address_by_name(tp->symbol, true);
|
|
|
+ if (addr) {
|
|
|
+ addr += tp->offset;
|
|
|
+ sym = __find_kernel_function(addr, &map);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!sym)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ pp->retprobe = tp->retprobe;
|
|
|
+ pp->offset = addr - map->unmap_ip(map, sym->start);
|
|
|
+ pp->function = strdup(sym->name);
|
|
|
+ ret = pp->function ? 0 : -ENOMEM;
|
|
|
+
|
|
|
+out:
|
|
|
+ if (map && !is_kprobe) {
|
|
|
+ dso__delete(map->dso);
|
|
|
+ map__delete(map);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int convert_to_perf_probe_point(struct probe_trace_point *tp,
|
|
|
+ struct perf_probe_point *pp,
|
|
|
+ bool is_kprobe)
|
|
|
+{
|
|
|
+ char buf[128];
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = find_perf_probe_point_from_dwarf(tp, pp, is_kprobe);
|
|
|
+ if (!ret)
|
|
|
+ return 0;
|
|
|
+ ret = find_perf_probe_point_from_map(tp, pp, is_kprobe);
|
|
|
+ if (!ret)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ pr_debug("Failed to find probe point from both of dwarf and map.\n");
|
|
|
+
|
|
|
+ if (tp->symbol) {
|
|
|
+ pp->function = strdup(tp->symbol);
|
|
|
+ pp->offset = tp->offset;
|
|
|
+ } else if (!tp->module && !is_kprobe) {
|
|
|
+ ret = e_snprintf(buf, 128, "0x%" PRIx64, (u64)tp->address);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ pp->function = strdup(buf);
|
|
|
+ pp->offset = 0;
|
|
|
+ }
|
|
|
+ if (pp->function == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ pp->retprobe = tp->retprobe;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int convert_to_perf_probe_event(struct probe_trace_event *tev,
|
|
|
struct perf_probe_event *pev, bool is_kprobe)
|
|
|
{
|
|
@@ -1560,11 +1696,7 @@ static int convert_to_perf_probe_event(struct probe_trace_event *tev,
|
|
|
return -ENOMEM;
|
|
|
|
|
|
/* Convert trace_point to probe_point */
|
|
|
- if (is_kprobe)
|
|
|
- ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point);
|
|
|
- else
|
|
|
- ret = convert_to_perf_probe_point(&tev->point, &pev->point);
|
|
|
-
|
|
|
+ ret = convert_to_perf_probe_point(&tev->point, &pev->point, is_kprobe);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
@@ -1731,7 +1863,8 @@ static struct strlist *get_probe_trace_command_rawlist(int fd)
|
|
|
}
|
|
|
|
|
|
/* Show an event */
|
|
|
-static int show_perf_probe_event(struct perf_probe_event *pev)
|
|
|
+static int show_perf_probe_event(struct perf_probe_event *pev,
|
|
|
+ const char *module)
|
|
|
{
|
|
|
int i, ret;
|
|
|
char buf[128];
|
|
@@ -1747,6 +1880,8 @@ static int show_perf_probe_event(struct perf_probe_event *pev)
|
|
|
return ret;
|
|
|
|
|
|
printf(" %-20s (on %s", buf, place);
|
|
|
+ if (module)
|
|
|
+ printf(" in %s", module);
|
|
|
|
|
|
if (pev->nargs > 0) {
|
|
|
printf(" with");
|
|
@@ -1784,7 +1919,8 @@ static int __show_perf_probe_events(int fd, bool is_kprobe)
|
|
|
ret = convert_to_perf_probe_event(&tev, &pev,
|
|
|
is_kprobe);
|
|
|
if (ret >= 0)
|
|
|
- ret = show_perf_probe_event(&pev);
|
|
|
+ ret = show_perf_probe_event(&pev,
|
|
|
+ tev.point.module);
|
|
|
}
|
|
|
clear_perf_probe_event(&pev);
|
|
|
clear_probe_trace_event(&tev);
|
|
@@ -1807,7 +1943,7 @@ int show_perf_probe_events(void)
|
|
|
if (fd < 0)
|
|
|
return fd;
|
|
|
|
|
|
- ret = init_vmlinux();
|
|
|
+ ret = init_symbol_maps(false);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
@@ -1820,6 +1956,7 @@ int show_perf_probe_events(void)
|
|
|
close(fd);
|
|
|
}
|
|
|
|
|
|
+ exit_symbol_maps();
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -1982,7 +2119,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
|
|
|
group = pev->group;
|
|
|
pev->event = tev->event;
|
|
|
pev->group = tev->group;
|
|
|
- show_perf_probe_event(pev);
|
|
|
+ show_perf_probe_event(pev, tev->point.module);
|
|
|
/* Trick here - restore current event/group */
|
|
|
pev->event = (char *)event;
|
|
|
pev->group = (char *)group;
|
|
@@ -2008,113 +2145,175 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static int convert_to_probe_trace_events(struct perf_probe_event *pev,
|
|
|
- struct probe_trace_event **tevs,
|
|
|
- int max_tevs, const char *target)
|
|
|
+static char *looking_function_name;
|
|
|
+static int num_matched_functions;
|
|
|
+
|
|
|
+static int probe_function_filter(struct map *map __maybe_unused,
|
|
|
+ struct symbol *sym)
|
|
|
{
|
|
|
+ if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) &&
|
|
|
+ strcmp(looking_function_name, sym->name) == 0) {
|
|
|
+ num_matched_functions++;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+#define strdup_or_goto(str, label) \
|
|
|
+ ({ char *__p = strdup(str); if (!__p) goto label; __p; })
|
|
|
+
|
|
|
+/*
|
|
|
+ * Find probe function addresses from map.
|
|
|
+ * Return an error or the number of found probe_trace_event
|
|
|
+ */
|
|
|
+static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
|
|
|
+ struct probe_trace_event **tevs,
|
|
|
+ int max_tevs, const char *target)
|
|
|
+{
|
|
|
+ struct map *map = NULL;
|
|
|
+ struct kmap *kmap = NULL;
|
|
|
+ struct ref_reloc_sym *reloc_sym = NULL;
|
|
|
struct symbol *sym;
|
|
|
- int ret, i;
|
|
|
+ struct rb_node *nd;
|
|
|
struct probe_trace_event *tev;
|
|
|
+ struct perf_probe_point *pp = &pev->point;
|
|
|
+ struct probe_trace_point *tp;
|
|
|
+ int ret, i;
|
|
|
|
|
|
- if (pev->uprobes && !pev->group) {
|
|
|
- /* Replace group name if not given */
|
|
|
- ret = convert_exec_to_group(target, &pev->group);
|
|
|
- if (ret != 0) {
|
|
|
- pr_warning("Failed to make a group name.\n");
|
|
|
- return ret;
|
|
|
- }
|
|
|
+ /* Init maps of given executable or kernel */
|
|
|
+ if (pev->uprobes)
|
|
|
+ map = dso__new_map(target);
|
|
|
+ else
|
|
|
+ map = kernel_get_module_map(target);
|
|
|
+ if (!map) {
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
- /* Convert perf_probe_event with debuginfo */
|
|
|
- ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target);
|
|
|
- if (ret != 0)
|
|
|
- return ret; /* Found in debuginfo or got an error */
|
|
|
-
|
|
|
- if (pev->uprobes) {
|
|
|
- ret = convert_name_to_addr(pev, target);
|
|
|
- if (ret < 0)
|
|
|
- return ret;
|
|
|
+ /*
|
|
|
+ * Load matched symbols: Since the different local symbols may have
|
|
|
+ * same name but different addresses, this lists all the symbols.
|
|
|
+ */
|
|
|
+ num_matched_functions = 0;
|
|
|
+ looking_function_name = pp->function;
|
|
|
+ ret = map__load(map, probe_function_filter);
|
|
|
+ if (ret || num_matched_functions == 0) {
|
|
|
+ pr_err("Failed to find symbol %s in %s\n", pp->function,
|
|
|
+ target ? : "kernel");
|
|
|
+ ret = -ENOENT;
|
|
|
+ goto out;
|
|
|
+ } else if (num_matched_functions > max_tevs) {
|
|
|
+ pr_err("Too many functions matched in %s\n",
|
|
|
+ target ? : "kernel");
|
|
|
+ ret = -E2BIG;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
- /* Allocate trace event buffer */
|
|
|
- tev = *tevs = zalloc(sizeof(struct probe_trace_event));
|
|
|
- if (tev == NULL)
|
|
|
- return -ENOMEM;
|
|
|
+ if (!pev->uprobes) {
|
|
|
+ kmap = map__kmap(map);
|
|
|
+ reloc_sym = kmap->ref_reloc_sym;
|
|
|
+ if (!reloc_sym) {
|
|
|
+ pr_warning("Relocated base symbol is not found!\n");
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- /* Copy parameters */
|
|
|
- tev->point.symbol = strdup(pev->point.function);
|
|
|
- if (tev->point.symbol == NULL) {
|
|
|
+ /* Setup result trace-probe-events */
|
|
|
+ *tevs = zalloc(sizeof(*tev) * num_matched_functions);
|
|
|
+ if (!*tevs) {
|
|
|
ret = -ENOMEM;
|
|
|
- goto error;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
- if (target) {
|
|
|
- tev->point.module = strdup(target);
|
|
|
- if (tev->point.module == NULL) {
|
|
|
- ret = -ENOMEM;
|
|
|
- goto error;
|
|
|
+ ret = 0;
|
|
|
+ map__for_each_symbol(map, sym, nd) {
|
|
|
+ tev = (*tevs) + ret;
|
|
|
+ tp = &tev->point;
|
|
|
+ if (ret == num_matched_functions) {
|
|
|
+ pr_warning("Too many symbols are listed. Skip it.\n");
|
|
|
+ break;
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- tev->point.offset = pev->point.offset;
|
|
|
- tev->point.retprobe = pev->point.retprobe;
|
|
|
- tev->nargs = pev->nargs;
|
|
|
- tev->uprobes = pev->uprobes;
|
|
|
+ ret++;
|
|
|
|
|
|
- if (tev->nargs) {
|
|
|
- tev->args = zalloc(sizeof(struct probe_trace_arg)
|
|
|
- * tev->nargs);
|
|
|
- if (tev->args == NULL) {
|
|
|
- ret = -ENOMEM;
|
|
|
- goto error;
|
|
|
+ if (pp->offset > sym->end - sym->start) {
|
|
|
+ pr_warning("Offset %ld is bigger than the size of %s\n",
|
|
|
+ pp->offset, sym->name);
|
|
|
+ ret = -ENOENT;
|
|
|
+ goto err_out;
|
|
|
+ }
|
|
|
+ /* Add one probe point */
|
|
|
+ tp->address = map->unmap_ip(map, sym->start) + pp->offset;
|
|
|
+ if (reloc_sym) {
|
|
|
+ tp->symbol = strdup_or_goto(reloc_sym->name, nomem_out);
|
|
|
+ tp->offset = tp->address - reloc_sym->addr;
|
|
|
+ } else {
|
|
|
+ tp->symbol = strdup_or_goto(sym->name, nomem_out);
|
|
|
+ tp->offset = pp->offset;
|
|
|
+ }
|
|
|
+ tp->retprobe = pp->retprobe;
|
|
|
+ if (target)
|
|
|
+ tev->point.module = strdup_or_goto(target, nomem_out);
|
|
|
+ tev->uprobes = pev->uprobes;
|
|
|
+ tev->nargs = pev->nargs;
|
|
|
+ if (tev->nargs) {
|
|
|
+ tev->args = zalloc(sizeof(struct probe_trace_arg) *
|
|
|
+ tev->nargs);
|
|
|
+ if (tev->args == NULL)
|
|
|
+ goto nomem_out;
|
|
|
}
|
|
|
for (i = 0; i < tev->nargs; i++) {
|
|
|
- if (pev->args[i].name) {
|
|
|
- tev->args[i].name = strdup(pev->args[i].name);
|
|
|
- if (tev->args[i].name == NULL) {
|
|
|
- ret = -ENOMEM;
|
|
|
- goto error;
|
|
|
- }
|
|
|
- }
|
|
|
- tev->args[i].value = strdup(pev->args[i].var);
|
|
|
- if (tev->args[i].value == NULL) {
|
|
|
- ret = -ENOMEM;
|
|
|
- goto error;
|
|
|
- }
|
|
|
- if (pev->args[i].type) {
|
|
|
- tev->args[i].type = strdup(pev->args[i].type);
|
|
|
- if (tev->args[i].type == NULL) {
|
|
|
- ret = -ENOMEM;
|
|
|
- goto error;
|
|
|
- }
|
|
|
- }
|
|
|
+ if (pev->args[i].name)
|
|
|
+ tev->args[i].name =
|
|
|
+ strdup_or_goto(pev->args[i].name,
|
|
|
+ nomem_out);
|
|
|
+
|
|
|
+ tev->args[i].value = strdup_or_goto(pev->args[i].var,
|
|
|
+ nomem_out);
|
|
|
+ if (pev->args[i].type)
|
|
|
+ tev->args[i].type =
|
|
|
+ strdup_or_goto(pev->args[i].type,
|
|
|
+ nomem_out);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (pev->uprobes)
|
|
|
- return 1;
|
|
|
+out:
|
|
|
+ if (map && pev->uprobes) {
|
|
|
+ /* Only when using uprobe(exec) map needs to be released */
|
|
|
+ dso__delete(map->dso);
|
|
|
+ map__delete(map);
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
|
|
|
- /* Currently just checking function name from symbol map */
|
|
|
- sym = __find_kernel_function_by_name(tev->point.symbol, NULL);
|
|
|
- if (!sym) {
|
|
|
- pr_warning("Kernel symbol \'%s\' not found.\n",
|
|
|
- tev->point.symbol);
|
|
|
- ret = -ENOENT;
|
|
|
- goto error;
|
|
|
- } else if (tev->point.offset > sym->end - sym->start) {
|
|
|
- pr_warning("Offset specified is greater than size of %s\n",
|
|
|
- tev->point.symbol);
|
|
|
- ret = -ENOENT;
|
|
|
- goto error;
|
|
|
+nomem_out:
|
|
|
+ ret = -ENOMEM;
|
|
|
+err_out:
|
|
|
+ clear_probe_trace_events(*tevs, num_matched_functions);
|
|
|
+ zfree(tevs);
|
|
|
+ goto out;
|
|
|
+}
|
|
|
+
|
|
|
+static int convert_to_probe_trace_events(struct perf_probe_event *pev,
|
|
|
+ struct probe_trace_event **tevs,
|
|
|
+ int max_tevs, const char *target)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
|
|
|
+ if (pev->uprobes && !pev->group) {
|
|
|
+ /* Replace group name if not given */
|
|
|
+ ret = convert_exec_to_group(target, &pev->group);
|
|
|
+ if (ret != 0) {
|
|
|
+ pr_warning("Failed to make a group name.\n");
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- return 1;
|
|
|
-error:
|
|
|
- clear_probe_trace_event(tev);
|
|
|
- free(tev);
|
|
|
- *tevs = NULL;
|
|
|
- return ret;
|
|
|
+ /* Convert perf_probe_event with debuginfo */
|
|
|
+ ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target);
|
|
|
+ if (ret != 0)
|
|
|
+ return ret; /* Found in debuginfo or got an error */
|
|
|
+
|
|
|
+ return find_probe_trace_events_from_map(pev, tevs, max_tevs, target);
|
|
|
}
|
|
|
|
|
|
struct __event_package {
|
|
@@ -2135,12 +2334,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
|
|
|
if (pkgs == NULL)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- if (!pevs->uprobes)
|
|
|
- /* Init vmlinux path */
|
|
|
- ret = init_vmlinux();
|
|
|
- else
|
|
|
- ret = init_user_exec();
|
|
|
-
|
|
|
+ ret = init_symbol_maps(pevs->uprobes);
|
|
|
if (ret < 0) {
|
|
|
free(pkgs);
|
|
|
return ret;
|
|
@@ -2174,6 +2368,7 @@ end:
|
|
|
zfree(&pkgs[i].tevs);
|
|
|
}
|
|
|
free(pkgs);
|
|
|
+ exit_symbol_maps();
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
@@ -2323,159 +2518,51 @@ static struct strfilter *available_func_filter;
|
|
|
static int filter_available_functions(struct map *map __maybe_unused,
|
|
|
struct symbol *sym)
|
|
|
{
|
|
|
- if (sym->binding == STB_GLOBAL &&
|
|
|
+ if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) &&
|
|
|
strfilter__compare(available_func_filter, sym->name))
|
|
|
return 0;
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
-static int __show_available_funcs(struct map *map)
|
|
|
-{
|
|
|
- if (map__load(map, filter_available_functions)) {
|
|
|
- pr_err("Failed to load map.\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- if (!dso__sorted_by_name(map->dso, map->type))
|
|
|
- dso__sort_by_name(map->dso, map->type);
|
|
|
-
|
|
|
- dso__fprintf_symbols_by_name(map->dso, map->type, stdout);
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int available_kernel_funcs(const char *module)
|
|
|
+int show_available_funcs(const char *target, struct strfilter *_filter,
|
|
|
+ bool user)
|
|
|
{
|
|
|
struct map *map;
|
|
|
int ret;
|
|
|
|
|
|
- ret = init_vmlinux();
|
|
|
+ ret = init_symbol_maps(user);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
- map = kernel_get_module_map(module);
|
|
|
+ /* Get a symbol map */
|
|
|
+ if (user)
|
|
|
+ map = dso__new_map(target);
|
|
|
+ else
|
|
|
+ map = kernel_get_module_map(target);
|
|
|
if (!map) {
|
|
|
- pr_err("Failed to find %s map.\n", (module) ? : "kernel");
|
|
|
+ pr_err("Failed to get a map for %s\n", (target) ? : "kernel");
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
- return __show_available_funcs(map);
|
|
|
-}
|
|
|
-
|
|
|
-static int available_user_funcs(const char *target)
|
|
|
-{
|
|
|
- struct map *map;
|
|
|
- int ret;
|
|
|
-
|
|
|
- ret = init_user_exec();
|
|
|
- if (ret < 0)
|
|
|
- return ret;
|
|
|
-
|
|
|
- map = dso__new_map(target);
|
|
|
- ret = __show_available_funcs(map);
|
|
|
- dso__delete(map->dso);
|
|
|
- map__delete(map);
|
|
|
- return ret;
|
|
|
-}
|
|
|
|
|
|
-int show_available_funcs(const char *target, struct strfilter *_filter,
|
|
|
- bool user)
|
|
|
-{
|
|
|
- setup_pager();
|
|
|
+ /* Load symbols with given filter */
|
|
|
available_func_filter = _filter;
|
|
|
-
|
|
|
- if (!user)
|
|
|
- return available_kernel_funcs(target);
|
|
|
-
|
|
|
- return available_user_funcs(target);
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * uprobe_events only accepts address:
|
|
|
- * Convert function and any offset to address
|
|
|
- */
|
|
|
-static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec)
|
|
|
-{
|
|
|
- struct perf_probe_point *pp = &pev->point;
|
|
|
- struct symbol *sym;
|
|
|
- struct map *map = NULL;
|
|
|
- char *function = NULL;
|
|
|
- int ret = -EINVAL;
|
|
|
- unsigned long long vaddr = 0;
|
|
|
-
|
|
|
- if (!pp->function) {
|
|
|
- pr_warning("No function specified for uprobes");
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- function = strdup(pp->function);
|
|
|
- if (!function) {
|
|
|
- pr_warning("Failed to allocate memory by strdup.\n");
|
|
|
- ret = -ENOMEM;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- map = dso__new_map(exec);
|
|
|
- if (!map) {
|
|
|
- pr_warning("Cannot find appropriate DSO for %s.\n", exec);
|
|
|
- goto out;
|
|
|
- }
|
|
|
- available_func_filter = strfilter__new(function, NULL);
|
|
|
if (map__load(map, filter_available_functions)) {
|
|
|
- pr_err("Failed to load map.\n");
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- sym = map__find_symbol_by_name(map, function, NULL);
|
|
|
- if (!sym) {
|
|
|
- pr_warning("Cannot find %s in DSO %s\n", function, exec);
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- if (map->start > sym->start)
|
|
|
- vaddr = map->start;
|
|
|
- vaddr += sym->start + pp->offset + map->pgoff;
|
|
|
- pp->offset = 0;
|
|
|
-
|
|
|
- if (!pev->event) {
|
|
|
- pev->event = function;
|
|
|
- function = NULL;
|
|
|
- }
|
|
|
- if (!pev->group) {
|
|
|
- char *ptr1, *ptr2, *exec_copy;
|
|
|
-
|
|
|
- pev->group = zalloc(sizeof(char *) * 64);
|
|
|
- exec_copy = strdup(exec);
|
|
|
- if (!exec_copy) {
|
|
|
- ret = -ENOMEM;
|
|
|
- pr_warning("Failed to copy exec string.\n");
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- ptr1 = strdup(basename(exec_copy));
|
|
|
- if (ptr1) {
|
|
|
- ptr2 = strpbrk(ptr1, "-._");
|
|
|
- if (ptr2)
|
|
|
- *ptr2 = '\0';
|
|
|
- e_snprintf(pev->group, 64, "%s_%s", PERFPROBE_GROUP,
|
|
|
- ptr1);
|
|
|
- free(ptr1);
|
|
|
- }
|
|
|
- free(exec_copy);
|
|
|
- }
|
|
|
- free(pp->function);
|
|
|
- pp->function = zalloc(sizeof(char *) * MAX_PROBE_ARGS);
|
|
|
- if (!pp->function) {
|
|
|
- ret = -ENOMEM;
|
|
|
- pr_warning("Failed to allocate memory by zalloc.\n");
|
|
|
- goto out;
|
|
|
+ pr_err("Failed to load symbols in %s\n", (target) ? : "kernel");
|
|
|
+ goto end;
|
|
|
}
|
|
|
- e_snprintf(pp->function, MAX_PROBE_ARGS, "0x%llx", vaddr);
|
|
|
- ret = 0;
|
|
|
+ if (!dso__sorted_by_name(map->dso, map->type))
|
|
|
+ dso__sort_by_name(map->dso, map->type);
|
|
|
|
|
|
-out:
|
|
|
- if (map) {
|
|
|
+ /* Show all (filtered) symbols */
|
|
|
+ setup_pager();
|
|
|
+ dso__fprintf_symbols_by_name(map->dso, map->type, stdout);
|
|
|
+end:
|
|
|
+ if (user) {
|
|
|
dso__delete(map->dso);
|
|
|
map__delete(map);
|
|
|
}
|
|
|
- if (function)
|
|
|
- free(function);
|
|
|
+ exit_symbol_maps();
|
|
|
+
|
|
|
return ret;
|
|
|
}
|
|
|
+
|