]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - tools/perf/util/symbol.c
Merge remote-tracking branch 'tip/auto-latest'
[karo-tx-linux.git] / tools / perf / util / symbol.c
index 7eb0362f4ffd2468c81f6ae1a93686d00dfefe50..c0c36965fff0f0060b2f2a077edc17c569648bf2 100644 (file)
@@ -51,6 +51,7 @@ static enum dso_binary_type binary_type_symtab[] = {
        DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
        DSO_BINARY_TYPE__GUEST_KMODULE,
        DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE,
+       DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
        DSO_BINARY_TYPE__NOT_FOUND,
 };
 
@@ -159,10 +160,12 @@ again:
 
                if (choose_best_symbol(curr, next) == SYMBOL_A) {
                        rb_erase(&next->rb_node, symbols);
+                       symbol__delete(next);
                        goto again;
                } else {
                        nd = rb_next(&curr->rb_node);
                        rb_erase(&curr->rb_node, symbols);
+                       symbol__delete(curr);
                }
        }
 }
@@ -499,6 +502,64 @@ out_failure:
        return -1;
 }
 
+int modules__parse(const char *filename, void *arg,
+                  int (*process_module)(void *arg, const char *name,
+                                        u64 start))
+{
+       char *line = NULL;
+       size_t n;
+       FILE *file;
+       int err = 0;
+
+       file = fopen(filename, "r");
+       if (file == NULL)
+               return -1;
+
+       while (1) {
+               char name[PATH_MAX];
+               u64 start;
+               char *sep;
+               ssize_t line_len;
+
+               line_len = getline(&line, &n, file);
+               if (line_len < 0) {
+                       if (feof(file))
+                               break;
+                       err = -1;
+                       goto out;
+               }
+
+               if (!line) {
+                       err = -1;
+                       goto out;
+               }
+
+               line[--line_len] = '\0'; /* \n */
+
+               sep = strrchr(line, 'x');
+               if (sep == NULL)
+                       continue;
+
+               hex2u64(sep + 1, &start);
+
+               sep = strchr(line, ' ');
+               if (sep == NULL)
+                       continue;
+
+               *sep = '\0';
+
+               scnprintf(name, sizeof(name), "[%s]", line);
+
+               err = process_module(arg, name, start);
+               if (err)
+                       break;
+       }
+out:
+       free(line);
+       fclose(file);
+       return err;
+}
+
 struct process_kallsyms_args {
        struct map *map;
        struct dso *dso;
@@ -739,51 +800,242 @@ bool symbol__restricted_filename(const char *filename,
        return restricted;
 }
 
-struct kcore_mapfn_data {
-       struct dso *dso;
-       enum map_type type;
-       struct list_head maps;
+struct module_info {
+       struct rb_node rb_node;
+       char *name;
+       u64 start;
 };
 
-static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data)
+static void add_module(struct module_info *mi, struct rb_root *modules)
 {
-       struct kcore_mapfn_data *md = data;
-       struct map *map;
+       struct rb_node **p = &modules->rb_node;
+       struct rb_node *parent = NULL;
+       struct module_info *m;
 
-       map = map__new2(start, md->dso, md->type);
-       if (map == NULL)
+       while (*p != NULL) {
+               parent = *p;
+               m = rb_entry(parent, struct module_info, rb_node);
+               if (strcmp(mi->name, m->name) < 0)
+                       p = &(*p)->rb_left;
+               else
+                       p = &(*p)->rb_right;
+       }
+       rb_link_node(&mi->rb_node, parent, p);
+       rb_insert_color(&mi->rb_node, modules);
+}
+
+static void delete_modules(struct rb_root *modules)
+{
+       struct module_info *mi;
+       struct rb_node *next = rb_first(modules);
+
+       while (next) {
+               mi = rb_entry(next, struct module_info, rb_node);
+               next = rb_next(&mi->rb_node);
+               rb_erase(&mi->rb_node, modules);
+               free(mi->name);
+               free(mi);
+       }
+}
+
+static struct module_info *find_module(const char *name,
+                                      struct rb_root *modules)
+{
+       struct rb_node *n = modules->rb_node;
+
+       while (n) {
+               struct module_info *m;
+               int cmp;
+
+               m = rb_entry(n, struct module_info, rb_node);
+               cmp = strcmp(name, m->name);
+               if (cmp < 0)
+                       n = n->rb_left;
+               else if (cmp > 0)
+                       n = n->rb_right;
+               else
+                       return m;
+       }
+
+       return NULL;
+}
+
+static int __read_proc_modules(void *arg, const char *name, u64 start)
+{
+       struct rb_root *modules = arg;
+       struct module_info *mi;
+
+       mi = zalloc(sizeof(struct module_info));
+       if (!mi)
                return -ENOMEM;
 
-       map->end = map->start + len;
-       map->pgoff = pgoff;
+       mi->name = strdup(name);
+       mi->start = start;
 
-       list_add(&map->node, &md->maps);
+       if (!mi->name) {
+               free(mi);
+               return -ENOMEM;
+       }
+
+       add_module(mi, modules);
+
+       return 0;
+}
+
+static int read_proc_modules(const char *filename, struct rb_root *modules)
+{
+       if (symbol__restricted_filename(filename, "/proc/modules"))
+               return -1;
+
+       if (modules__parse(filename, modules, __read_proc_modules)) {
+               delete_modules(modules);
+               return -1;
+       }
 
        return 0;
 }
 
+int compare_proc_modules(const char *from, const char *to)
+{
+       struct rb_root from_modules = RB_ROOT;
+       struct rb_root to_modules = RB_ROOT;
+       struct rb_node *from_node, *to_node;
+       struct module_info *from_m, *to_m;
+       int ret = -1;
+
+       if (read_proc_modules(from, &from_modules))
+               return -1;
+
+       if (read_proc_modules(to, &to_modules))
+               goto out_delete_from;
+
+       from_node = rb_first(&from_modules);
+       to_node = rb_first(&to_modules);
+       while (from_node) {
+               if (!to_node)
+                       break;
+
+               from_m = rb_entry(from_node, struct module_info, rb_node);
+               to_m = rb_entry(to_node, struct module_info, rb_node);
+
+               if (from_m->start != to_m->start ||
+                   strcmp(from_m->name, to_m->name))
+                       break;
+
+               from_node = rb_next(from_node);
+               to_node = rb_next(to_node);
+       }
+
+       if (!from_node && !to_node)
+               ret = 0;
+
+       delete_modules(&to_modules);
+out_delete_from:
+       delete_modules(&from_modules);
+
+       return ret;
+}
+
+static int do_validate_kcore_modules(const char *filename, struct map *map,
+                                 struct map_groups *kmaps)
+{
+       struct rb_root modules = RB_ROOT;
+       struct map *old_map;
+       int err;
+
+       err = read_proc_modules(filename, &modules);
+       if (err)
+               return err;
+
+       old_map = map_groups__first(kmaps, map->type);
+       while (old_map) {
+               struct map *next = map_groups__next(old_map);
+               struct module_info *mi;
+
+               if (old_map == map || old_map->start == map->start) {
+                       /* The kernel map */
+                       old_map = next;
+                       continue;
+               }
+
+               /* Module must be in memory at the same address */
+               mi = find_module(old_map->dso->short_name, &modules);
+               if (!mi || mi->start != old_map->start) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               old_map = next;
+       }
+out:
+       delete_modules(&modules);
+       return err;
+}
+
 /*
- * If kallsyms is referenced by name then we look for kcore in the same
+ * If kallsyms is referenced by name then we look for filename in the same
  * directory.
  */
-static bool kcore_filename_from_kallsyms_filename(char *kcore_filename,
-                                                 const char *kallsyms_filename)
+static bool filename_from_kallsyms_filename(char *filename,
+                                           const char *base_name,
+                                           const char *kallsyms_filename)
 {
        char *name;
 
-       strcpy(kcore_filename, kallsyms_filename);
-       name = strrchr(kcore_filename, '/');
+       strcpy(filename, kallsyms_filename);
+       name = strrchr(filename, '/');
        if (!name)
                return false;
 
-       if (!strcmp(name, "/kallsyms")) {
-               strcpy(name, "/kcore");
+       name += 1;
+
+       if (!strcmp(name, "kallsyms")) {
+               strcpy(name, base_name);
                return true;
        }
 
        return false;
 }
 
+static int validate_kcore_modules(const char *kallsyms_filename,
+                                 struct map *map)
+{
+       struct map_groups *kmaps = map__kmap(map)->kmaps;
+       char modules_filename[PATH_MAX];
+
+       if (!filename_from_kallsyms_filename(modules_filename, "modules",
+                                            kallsyms_filename))
+               return -EINVAL;
+
+       if (do_validate_kcore_modules(modules_filename, map, kmaps))
+               return -EINVAL;
+
+       return 0;
+}
+
+struct kcore_mapfn_data {
+       struct dso *dso;
+       enum map_type type;
+       struct list_head maps;
+};
+
+static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data)
+{
+       struct kcore_mapfn_data *md = data;
+       struct map *map;
+
+       map = map__new2(start, md->dso, md->type);
+       if (map == NULL)
+               return -ENOMEM;
+
+       map->end = map->start + len;
+       map->pgoff = pgoff;
+
+       list_add(&map->node, &md->maps);
+
+       return 0;
+}
+
 static int dso__load_kcore(struct dso *dso, struct map *map,
                           const char *kallsyms_filename)
 {
@@ -800,8 +1052,12 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
        if (map != machine->vmlinux_maps[map->type])
                return -EINVAL;
 
-       if (!kcore_filename_from_kallsyms_filename(kcore_filename,
-                                                  kallsyms_filename))
+       if (!filename_from_kallsyms_filename(kcore_filename, "kcore",
+                                            kallsyms_filename))
+               return -EINVAL;
+
+       /* All modules must be present at their original addresses */
+       if (validate_kcore_modules(kallsyms_filename, map))
                return -EINVAL;
 
        md.dso = dso;
@@ -1188,6 +1444,105 @@ out:
        return err;
 }
 
+static int find_matching_kcore(struct map *map, char *dir, size_t dir_sz)
+{
+       char kallsyms_filename[PATH_MAX];
+       struct dirent *dent;
+       int ret = -1;
+       DIR *d;
+
+       d = opendir(dir);
+       if (!d)
+               return -1;
+
+       while (1) {
+               dent = readdir(d);
+               if (!dent)
+                       break;
+               if (dent->d_type != DT_DIR)
+                       continue;
+               scnprintf(kallsyms_filename, sizeof(kallsyms_filename),
+                         "%s/%s/kallsyms", dir, dent->d_name);
+               if (!validate_kcore_modules(kallsyms_filename, map)) {
+                       strlcpy(dir, kallsyms_filename, dir_sz);
+                       ret = 0;
+                       break;
+               }
+       }
+
+       closedir(d);
+
+       return ret;
+}
+
+static char *dso__find_kallsyms(struct dso *dso, struct map *map)
+{
+       u8 host_build_id[BUILD_ID_SIZE];
+       char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+       bool is_host = false;
+       char path[PATH_MAX];
+
+       if (!dso->has_build_id) {
+               /*
+                * Last resort, if we don't have a build-id and couldn't find
+                * any vmlinux file, try the running kernel kallsyms table.
+                */
+               goto proc_kallsyms;
+       }
+
+       if (sysfs__read_build_id("/sys/kernel/notes", host_build_id,
+                                sizeof(host_build_id)) == 0)
+               is_host = dso__build_id_equal(dso, host_build_id);
+
+       build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id);
+
+       /* Use /proc/kallsyms if possible */
+       if (is_host) {
+               DIR *d;
+               int fd;
+
+               /* If no cached kcore go with /proc/kallsyms */
+               scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s",
+                         buildid_dir, sbuild_id);
+               d = opendir(path);
+               if (!d)
+                       goto proc_kallsyms;
+               closedir(d);
+
+               /*
+                * Do not check the build-id cache, until we know we cannot use
+                * /proc/kcore.
+                */
+               fd = open("/proc/kcore", O_RDONLY);
+               if (fd != -1) {
+                       close(fd);
+                       /* If module maps match go with /proc/kallsyms */
+                       if (!validate_kcore_modules("/proc/kallsyms", map))
+                               goto proc_kallsyms;
+               }
+
+               /* Find kallsyms in build-id cache with kcore */
+               if (!find_matching_kcore(map, path, sizeof(path)))
+                       return strdup(path);
+
+               goto proc_kallsyms;
+       }
+
+       scnprintf(path, sizeof(path), "%s/[kernel.kallsyms]/%s",
+                 buildid_dir, sbuild_id);
+
+       if (access(path, F_OK)) {
+               pr_err("No kallsyms or vmlinux with build-id %s was found\n",
+                      sbuild_id);
+               return NULL;
+       }
+
+       return strdup(path);
+
+proc_kallsyms:
+       return strdup("/proc/kallsyms");
+}
+
 static int dso__load_kernel_sym(struct dso *dso, struct map *map,
                                symbol_filter_t filter)
 {
@@ -1214,7 +1569,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map,
                goto do_kallsyms;
        }
 
-       if (symbol_conf.vmlinux_name != NULL) {
+       if (!symbol_conf.ignore_vmlinux && symbol_conf.vmlinux_name != NULL) {
                err = dso__load_vmlinux(dso, map,
                                        symbol_conf.vmlinux_name, filter);
                if (err > 0) {
@@ -1226,7 +1581,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map,
                return err;
        }
 
-       if (vmlinux_path != NULL) {
+       if (!symbol_conf.ignore_vmlinux && vmlinux_path != NULL) {
                err = dso__load_vmlinux_path(dso, map, filter);
                if (err > 0)
                        return err;
@@ -1236,51 +1591,11 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map,
        if (symbol_conf.symfs[0] != 0)
                return -1;
 
-       /*
-        * Say the kernel DSO was created when processing the build-id header table,
-        * we have a build-id, so check if it is the same as the running kernel,
-        * using it if it is.
-        */
-       if (dso->has_build_id) {
-               u8 kallsyms_build_id[BUILD_ID_SIZE];
-               char sbuild_id[BUILD_ID_SIZE * 2 + 1];
-
-               if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id,
-                                        sizeof(kallsyms_build_id)) == 0) {
-                       if (dso__build_id_equal(dso, kallsyms_build_id)) {
-                               kallsyms_filename = "/proc/kallsyms";
-                               goto do_kallsyms;
-                       }
-               }
-               /*
-                * Now look if we have it on the build-id cache in
-                * $HOME/.debug/[kernel.kallsyms].
-                */
-               build_id__sprintf(dso->build_id, sizeof(dso->build_id),
-                                 sbuild_id);
-
-               if (asprintf(&kallsyms_allocated_filename,
-                            "%s/.debug/[kernel.kallsyms]/%s",
-                            getenv("HOME"), sbuild_id) == -1) {
-                       pr_err("Not enough memory for kallsyms file lookup\n");
-                       return -1;
-               }
-
-               kallsyms_filename = kallsyms_allocated_filename;
+       kallsyms_allocated_filename = dso__find_kallsyms(dso, map);
+       if (!kallsyms_allocated_filename)
+               return -1;
 
-               if (access(kallsyms_filename, F_OK)) {
-                       pr_err("No kallsyms or vmlinux with build-id %s "
-                              "was found\n", sbuild_id);
-                       free(kallsyms_allocated_filename);
-                       return -1;
-               }
-       } else {
-               /*
-                * Last resort, if we don't have a build-id and couldn't find
-                * any vmlinux file, try the running kernel kallsyms table.
-                */
-               kallsyms_filename = "/proc/kallsyms";
-       }
+       kallsyms_filename = kallsyms_allocated_filename;
 
 do_kallsyms:
        err = dso__load_kallsyms(dso, kallsyms_filename, map, filter);