Merge branches 'fixes' and 'misc' into for-next
[firefly-linux-kernel-4.4.55.git] / tools / perf / util / probe-event.c
index 9c6989ca2bea0242edcfed3f75803dd4e923068d..d8b048c20cdee51ac894b5394b15c70a1897c106 100644 (file)
@@ -40,7 +40,7 @@
 #include "color.h"
 #include "symbol.h"
 #include "thread.h"
-#include <lk/debugfs.h>
+#include <api/fs/debugfs.h>
 #include "trace-event.h"       /* For __maybe_unused */
 #include "probe-event.h"
 #include "probe-finder.h"
@@ -72,6 +72,7 @@ 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;
 
 /* Initialize symbol maps and path of vmlinux/modules */
@@ -154,7 +155,7 @@ static struct dso *kernel_get_module_dso(const char *module)
 
        vmlinux_name = symbol_conf.vmlinux_name;
        if (vmlinux_name) {
-               if (dso__load_vmlinux(dso, map, vmlinux_name, NULL) <= 0)
+               if (dso__load_vmlinux(dso, map, vmlinux_name, false, NULL) <= 0)
                        return NULL;
        } else {
                if (dso__load_vmlinux_path(dso, map, NULL) <= 0) {
@@ -186,6 +187,37 @@ static int init_user_exec(void)
        return ret;
 }
 
+static int convert_exec_to_group(const char *exec, char **result)
+{
+       char *ptr1, *ptr2, *exec_copy;
+       char buf[64];
+       int ret;
+
+       exec_copy = strdup(exec);
+       if (!exec_copy)
+               return -ENOMEM;
+
+       ptr1 = basename(exec_copy);
+       if (!ptr1) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ptr2 = strpbrk(ptr1, "-._");
+       if (ptr2)
+               *ptr2 = '\0';
+       ret = e_snprintf(buf, 64, "%s_%s", PERFPROBE_GROUP, ptr1);
+       if (ret < 0)
+               goto out;
+
+       *result = strdup(buf);
+       ret = *result ? 0 : -ENOMEM;
+
+out:
+       free(exec_copy);
+       return ret;
+}
+
 static int convert_to_perf_probe_point(struct probe_trace_point *tp,
                                        struct perf_probe_point *pp)
 {
@@ -261,6 +293,68 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
        return 0;
 }
 
+static int get_text_start_address(const char *exec, unsigned long *address)
+{
+       Elf *elf;
+       GElf_Ehdr ehdr;
+       GElf_Shdr shdr;
+       int fd, ret = -ENOENT;
+
+       fd = open(exec, O_RDONLY);
+       if (fd < 0)
+               return -errno;
+
+       elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+       if (elf == NULL)
+               return -EINVAL;
+
+       if (gelf_getehdr(elf, &ehdr) == NULL)
+               goto out;
+
+       if (!elf_section_by_name(elf, &ehdr, &shdr, ".text", NULL))
+               goto out;
+
+       *address = shdr.sh_addr - shdr.sh_offset;
+       ret = 0;
+out:
+       elf_end(elf);
+       return ret;
+}
+
+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];
+
+       if (!exec)
+               return 0;
+
+       ret = get_text_start_address(exec, &stext);
+       if (ret < 0)
+               return ret;
+
+       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.module = strdup(exec);
+               tevs[i].point.symbol = strdup(buf);
+               if (!tevs[i].point.symbol || !tevs[i].point.module) {
+                       ret = -ENOMEM;
+                       break;
+               }
+               tevs[i].uprobes = true;
+       }
+
+       return ret;
+}
+
 static int add_module_to_probe_trace_events(struct probe_trace_event *tevs,
                                            int ntevs, const char *module)
 {
@@ -290,12 +384,18 @@ static int add_module_to_probe_trace_events(struct probe_trace_event *tevs,
                }
        }
 
-       if (tmp)
-               free(tmp);
-
+       free(tmp);
        return ret;
 }
 
+static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
+{
+       int i;
+
+       for (i = 0; i < ntevs; i++)
+               clear_probe_trace_event(tevs + i);
+}
+
 /* Try to find perf_probe_event with debuginfo */
 static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
                                          struct probe_trace_event **tevs,
@@ -305,15 +405,6 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
        struct debuginfo *dinfo;
        int ntevs, ret = 0;
 
-       if (pev->uprobes) {
-               if (need_dwarf) {
-                       pr_warning("Debuginfo-analysis is not yet supported"
-                                       " with -x/--exec option.\n");
-                       return -ENOSYS;
-               }
-               return convert_name_to_addr(pev, target);
-       }
-
        dinfo = open_debuginfo(target);
 
        if (!dinfo) {
@@ -332,9 +423,18 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
 
        if (ntevs > 0) {        /* Succeeded to find trace events */
                pr_debug("find %d probe_trace_events.\n", ntevs);
-               if (target)
-                       ret = add_module_to_probe_trace_events(*tevs, ntevs,
-                                                              target);
+               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);
+               }
+               if (ret < 0) {
+                       clear_probe_trace_events(*tevs, ntevs);
+                       zfree(tevs);
+               }
                return ret < 0 ? ret : ntevs;
        }
 
@@ -401,15 +501,13 @@ static int get_real_path(const char *raw_path, const char *comp_dir,
                case EFAULT:
                        raw_path = strchr(++raw_path, '/');
                        if (!raw_path) {
-                               free(*new_path);
-                               *new_path = NULL;
+                               zfree(new_path);
                                return -ENOENT;
                        }
                        continue;
 
                default:
-                       free(*new_path);
-                       *new_path = NULL;
+                       zfree(new_path);
                        return -errno;
                }
        }
@@ -580,7 +678,7 @@ static int show_available_vars_at(struct debuginfo *dinfo,
                 */
                fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol,
                        vl->point.offset);
-               free(vl->point.symbol);
+               zfree(&vl->point.symbol);
                nvars = 0;
                if (vl->vars) {
                        strlist__for_each(node, vl->vars) {
@@ -647,16 +745,14 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
 
 static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
                                struct probe_trace_event **tevs __maybe_unused,
-                               int max_tevs __maybe_unused, const char *target)
+                               int max_tevs __maybe_unused,
+                               const char *target __maybe_unused)
 {
        if (perf_probe_event_need_dwarf(pev)) {
                pr_warning("Debuginfo-analysis is not supported.\n");
                return -ENOSYS;
        }
 
-       if (pev->uprobes)
-               return convert_name_to_addr(pev, target);
-
        return 0;
 }
 
@@ -678,6 +774,28 @@ int show_available_vars(struct perf_probe_event *pevs __maybe_unused,
 }
 #endif
 
+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);
+       }
+       memset(lr, 0, sizeof(*lr));
+}
+
+void line_range__init(struct line_range *lr)
+{
+       memset(lr, 0, sizeof(*lr));
+       INIT_LIST_HEAD(&lr->line_list);
+}
+
 static int parse_line_num(char **ptr, int *val, const char *what)
 {
        const char *start = *ptr;
@@ -1278,8 +1396,7 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
 error:
        pr_debug("Failed to synthesize perf probe point: %s\n",
                 strerror(-ret));
-       if (buf)
-               free(buf);
+       free(buf);
        return NULL;
 }
 
@@ -1480,34 +1597,25 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
        struct perf_probe_arg_field *field, *next;
        int i;
 
-       if (pev->event)
-               free(pev->event);
-       if (pev->group)
-               free(pev->group);
-       if (pp->file)
-               free(pp->file);
-       if (pp->function)
-               free(pp->function);
-       if (pp->lazy_line)
-               free(pp->lazy_line);
+       free(pev->event);
+       free(pev->group);
+       free(pp->file);
+       free(pp->function);
+       free(pp->lazy_line);
+
        for (i = 0; i < pev->nargs; i++) {
-               if (pev->args[i].name)
-                       free(pev->args[i].name);
-               if (pev->args[i].var)
-                       free(pev->args[i].var);
-               if (pev->args[i].type)
-                       free(pev->args[i].type);
+               free(pev->args[i].name);
+               free(pev->args[i].var);
+               free(pev->args[i].type);
                field = pev->args[i].field;
                while (field) {
                        next = field->next;
-                       if (field->name)
-                               free(field->name);
+                       zfree(&field->name);
                        free(field);
                        field = next;
                }
        }
-       if (pev->args)
-               free(pev->args);
+       free(pev->args);
        memset(pev, 0, sizeof(*pev));
 }
 
@@ -1516,21 +1624,14 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)
        struct probe_trace_arg_ref *ref, *next;
        int i;
 
-       if (tev->event)
-               free(tev->event);
-       if (tev->group)
-               free(tev->group);
-       if (tev->point.symbol)
-               free(tev->point.symbol);
-       if (tev->point.module)
-               free(tev->point.module);
+       free(tev->event);
+       free(tev->group);
+       free(tev->point.symbol);
+       free(tev->point.module);
        for (i = 0; i < tev->nargs; i++) {
-               if (tev->args[i].name)
-                       free(tev->args[i].name);
-               if (tev->args[i].value)
-                       free(tev->args[i].value);
-               if (tev->args[i].type)
-                       free(tev->args[i].type);
+               free(tev->args[i].name);
+               free(tev->args[i].value);
+               free(tev->args[i].type);
                ref = tev->args[i].ref;
                while (ref) {
                        next = ref->next;
@@ -1538,8 +1639,7 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)
                        ref = next;
                }
        }
-       if (tev->args)
-               free(tev->args);
+       free(tev->args);
        memset(tev, 0, sizeof(*tev));
 }
 
@@ -1913,14 +2013,29 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
                                          int max_tevs, const char *target)
 {
        struct symbol *sym;
-       int ret = 0, i;
+       int ret, i;
        struct probe_trace_event *tev;
 
+       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;
+               }
+       }
+
        /* 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;
+       }
+
        /* Allocate trace event buffer */
        tev = *tevs = zalloc(sizeof(struct probe_trace_event));
        if (tev == NULL)
@@ -2056,7 +2171,7 @@ end:
        for (i = 0; i < npevs; i++) {
                for (j = 0; j < pkgs[i].ntevs; j++)
                        clear_probe_trace_event(&pkgs[i].tevs[j]);
-               free(pkgs[i].tevs);
+               zfree(&pkgs[i].tevs);
        }
        free(pkgs);
 
@@ -2281,7 +2396,7 @@ 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, *name = NULL;
+       char *function = NULL;
        int ret = -EINVAL;
        unsigned long long vaddr = 0;
 
@@ -2297,12 +2412,7 @@ static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec)
                goto out;
        }
 
-       name = realpath(exec, NULL);
-       if (!name) {
-               pr_warning("Cannot find realpath for %s.\n", exec);
-               goto out;
-       }
-       map = dso__new_map(name);
+       map = dso__new_map(exec);
        if (!map) {
                pr_warning("Cannot find appropriate DSO for %s.\n", exec);
                goto out;
@@ -2367,7 +2477,5 @@ out:
        }
        if (function)
                free(function);
-       if (name)
-               free(name);
        return ret;
 }