]> Pileus Git - ~andy/linux/blobdiff - tools/perf/util/symbol.c
perf symbols: Destroy unused symsrcs
[~andy/linux] / tools / perf / util / symbol.c
index c0c36965fff0f0060b2f2a077edc17c569648bf2..e89afc097d8ad2d4b818f07e02321084110e6ddc 100644 (file)
 
 #include <elf.h>
 #include <limits.h>
+#include <symbol/kallsyms.h>
 #include <sys/utsname.h>
 
-#ifndef KSYM_NAME_LEN
-#define KSYM_NAME_LEN 256
-#endif
-
 static int dso__load_kernel_sym(struct dso *dso, struct map *map,
                                symbol_filter_t filter);
 static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map,
@@ -446,62 +443,6 @@ size_t dso__fprintf_symbols_by_name(struct dso *dso,
        return ret;
 }
 
-int kallsyms__parse(const char *filename, void *arg,
-                   int (*process_symbol)(void *arg, const char *name,
-                                         char type, u64 start))
-{
-       char *line = NULL;
-       size_t n;
-       int err = -1;
-       FILE *file = fopen(filename, "r");
-
-       if (file == NULL)
-               goto out_failure;
-
-       err = 0;
-
-       while (!feof(file)) {
-               u64 start;
-               int line_len, len;
-               char symbol_type;
-               char *symbol_name;
-
-               line_len = getline(&line, &n, file);
-               if (line_len < 0 || !line)
-                       break;
-
-               line[--line_len] = '\0'; /* \n */
-
-               len = hex2u64(line, &start);
-
-               len++;
-               if (len + 2 >= line_len)
-                       continue;
-
-               symbol_type = line[len];
-               len += 2;
-               symbol_name = line + len;
-               len = line_len - len;
-
-               if (len >= KSYM_NAME_LEN) {
-                       err = -1;
-                       break;
-               }
-
-               err = process_symbol(arg, symbol_name,
-                                    symbol_type, start);
-               if (err)
-                       break;
-       }
-
-       free(line);
-       fclose(file);
-       return err;
-
-out_failure:
-       return -1;
-}
-
 int modules__parse(const char *filename, void *arg,
                   int (*process_module)(void *arg, const char *name,
                                         u64 start))
@@ -565,12 +506,34 @@ struct process_kallsyms_args {
        struct dso *dso;
 };
 
-static u8 kallsyms2elf_type(char type)
+bool symbol__is_idle(struct symbol *sym)
 {
-       if (type == 'W')
-               return STB_WEAK;
+       const char * const idle_symbols[] = {
+               "cpu_idle",
+               "intel_idle",
+               "default_idle",
+               "native_safe_halt",
+               "enter_idle",
+               "exit_idle",
+               "mwait_idle",
+               "mwait_idle_with_hints",
+               "poll_idle",
+               "ppc64_runlatch_off",
+               "pseries_dedicated_idle_sleep",
+               NULL
+       };
+
+       int i;
+
+       if (!sym)
+               return false;
 
-       return isupper(type) ? STB_GLOBAL : STB_LOCAL;
+       for (i = 0; idle_symbols[i]; i++) {
+               if (!strcmp(idle_symbols[i], sym->name))
+                       return true;
+       }
+
+       return false;
 }
 
 static int map__process_kallsym_symbol(void *arg, const char *name,
@@ -664,7 +627,7 @@ static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map,
  * kernel range is broken in several maps, named [kernel].N, as we don't have
  * the original ELF section names vmlinux have.
  */
-static int dso__split_kallsyms(struct dso *dso, struct map *map,
+static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta,
                               symbol_filter_t filter)
 {
        struct map_groups *kmaps = map__kmap(map)->kmaps;
@@ -729,6 +692,12 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map,
                        char dso_name[PATH_MAX];
                        struct dso *ndso;
 
+                       if (delta) {
+                               /* Kernel was relocated at boot time */
+                               pos->start -= delta;
+                               pos->end -= delta;
+                       }
+
                        if (count == 0) {
                                curr_map = map;
                                goto filter_symbol;
@@ -758,6 +727,10 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map,
                        curr_map->map_ip = curr_map->unmap_ip = identity__map_ip;
                        map_groups__insert(kmaps, curr_map);
                        ++kernel_range;
+               } else if (delta) {
+                       /* Kernel was relocated at boot time */
+                       pos->start -= delta;
+                       pos->end -= delta;
                }
 filter_symbol:
                if (filter && filter(curr_map, pos)) {
@@ -833,7 +806,7 @@ static void delete_modules(struct rb_root *modules)
                mi = rb_entry(next, struct module_info, rb_node);
                next = rb_next(&mi->rb_node);
                rb_erase(&mi->rb_node, modules);
-               free(mi->name);
+               zfree(&mi->name);
                free(mi);
        }
 }
@@ -1013,6 +986,23 @@ static int validate_kcore_modules(const char *kallsyms_filename,
        return 0;
 }
 
+static int validate_kcore_addresses(const char *kallsyms_filename,
+                                   struct map *map)
+{
+       struct kmap *kmap = map__kmap(map);
+
+       if (kmap->ref_reloc_sym && kmap->ref_reloc_sym->name) {
+               u64 start;
+
+               start = kallsyms__get_function_start(kallsyms_filename,
+                                                    kmap->ref_reloc_sym->name);
+               if (start != kmap->ref_reloc_sym->addr)
+                       return -EINVAL;
+       }
+
+       return validate_kcore_modules(kallsyms_filename, map);
+}
+
 struct kcore_mapfn_data {
        struct dso *dso;
        enum map_type type;
@@ -1056,8 +1046,8 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
                                             kallsyms_filename))
                return -EINVAL;
 
-       /* All modules must be present at their original addresses */
-       if (validate_kcore_modules(kallsyms_filename, map))
+       /* Modules and kernel must be present at their original addresses */
+       if (validate_kcore_addresses(kallsyms_filename, map))
                return -EINVAL;
 
        md.dso = dso;
@@ -1126,10 +1116,10 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
         * dso__data_read_addr().
         */
        if (dso->kernel == DSO_TYPE_GUEST_KERNEL)
-               dso->data_type = DSO_BINARY_TYPE__GUEST_KCORE;
+               dso->binary_type = DSO_BINARY_TYPE__GUEST_KCORE;
        else
-               dso->data_type = DSO_BINARY_TYPE__KCORE;
-       dso__set_long_name(dso, strdup(kcore_filename));
+               dso->binary_type = DSO_BINARY_TYPE__KCORE;
+       dso__set_long_name(dso, strdup(kcore_filename), true);
 
        close(fd);
 
@@ -1150,15 +1140,41 @@ out_err:
        return -EINVAL;
 }
 
+/*
+ * If the kernel is relocated at boot time, kallsyms won't match.  Compute the
+ * delta based on the relocation reference symbol.
+ */
+static int kallsyms__delta(struct map *map, const char *filename, u64 *delta)
+{
+       struct kmap *kmap = map__kmap(map);
+       u64 addr;
+
+       if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->name)
+               return 0;
+
+       addr = kallsyms__get_function_start(filename,
+                                           kmap->ref_reloc_sym->name);
+       if (!addr)
+               return -1;
+
+       *delta = addr - kmap->ref_reloc_sym->addr;
+       return 0;
+}
+
 int dso__load_kallsyms(struct dso *dso, const char *filename,
                       struct map *map, symbol_filter_t filter)
 {
+       u64 delta = 0;
+
        if (symbol__restricted_filename(filename, "/proc/kallsyms"))
                return -1;
 
        if (dso__load_all_kallsyms(dso, filename, map) < 0)
                return -1;
 
+       if (kallsyms__delta(map, filename, &delta))
+               return -1;
+
        symbols__fixup_duplicate(&dso->symbols[map->type]);
        symbols__fixup_end(&dso->symbols[map->type]);
 
@@ -1170,7 +1186,7 @@ int dso__load_kallsyms(struct dso *dso, const char *filename,
        if (!dso__load_kcore(dso, map, filename))
                return dso__split_kallsyms_for_kcore(dso, map, filter);
        else
-               return dso__split_kallsyms(dso, map, filter);
+               return dso__split_kallsyms(dso, map, delta, filter);
 }
 
 static int dso__load_perf_map(struct dso *dso, struct map *map,
@@ -1295,8 +1311,8 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
 
                enum dso_binary_type symtab_type = binary_type_symtab[i];
 
-               if (dso__binary_type_file(dso, symtab_type,
-                                         root_dir, name, PATH_MAX))
+               if (dso__read_binary_type_filename(dso, symtab_type,
+                                                  root_dir, name, PATH_MAX))
                        continue;
 
                /* Name is now the name of the next image to try */
@@ -1306,6 +1322,8 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
                if (!syms_ss && symsrc__has_symtab(ss)) {
                        syms_ss = ss;
                        next_slot = true;
+                       if (!dso->symsrc_filename)
+                               dso->symsrc_filename = strdup(name);
                }
 
                if (!runtime_ss && symsrc__possibly_runtime(ss)) {
@@ -1318,6 +1336,8 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
 
                        if (syms_ss && runtime_ss)
                                break;
+               } else {
+                       symsrc__destroy(ss);
                }
 
        }
@@ -1376,7 +1396,8 @@ struct map *map_groups__find_by_name(struct map_groups *mg,
 }
 
 int dso__load_vmlinux(struct dso *dso, struct map *map,
-                     const char *vmlinux, symbol_filter_t filter)
+                     const char *vmlinux, bool vmlinux_allocated,
+                     symbol_filter_t filter)
 {
        int err = -1;
        struct symsrc ss;
@@ -1402,10 +1423,10 @@ int dso__load_vmlinux(struct dso *dso, struct map *map,
 
        if (err > 0) {
                if (dso->kernel == DSO_TYPE_GUEST_KERNEL)
-                       dso->data_type = DSO_BINARY_TYPE__GUEST_VMLINUX;
+                       dso->binary_type = DSO_BINARY_TYPE__GUEST_VMLINUX;
                else
-                       dso->data_type = DSO_BINARY_TYPE__VMLINUX;
-               dso__set_long_name(dso, (char *)vmlinux);
+                       dso->binary_type = DSO_BINARY_TYPE__VMLINUX;
+               dso__set_long_name(dso, vmlinux, vmlinux_allocated);
                dso__set_loaded(dso, map->type);
                pr_debug("Using %s for symbols\n", symfs_vmlinux);
        }
@@ -1424,21 +1445,16 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map,
 
        filename = dso__build_id_filename(dso, NULL, 0);
        if (filename != NULL) {
-               err = dso__load_vmlinux(dso, map, filename, filter);
-               if (err > 0) {
-                       dso->lname_alloc = 1;
+               err = dso__load_vmlinux(dso, map, filename, true, filter);
+               if (err > 0)
                        goto out;
-               }
                free(filename);
        }
 
        for (i = 0; i < vmlinux_path__nr_entries; ++i) {
-               err = dso__load_vmlinux(dso, map, vmlinux_path[i], filter);
-               if (err > 0) {
-                       dso__set_long_name(dso, strdup(vmlinux_path[i]));
-                       dso->lname_alloc = 1;
+               err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter);
+               if (err > 0)
                        break;
-               }
        }
 out:
        return err;
@@ -1463,7 +1479,7 @@ static int find_matching_kcore(struct map *map, char *dir, size_t dir_sz)
                        continue;
                scnprintf(kallsyms_filename, sizeof(kallsyms_filename),
                          "%s/%s/kallsyms", dir, dent->d_name);
-               if (!validate_kcore_modules(kallsyms_filename, map)) {
+               if (!validate_kcore_addresses(kallsyms_filename, map)) {
                        strlcpy(dir, kallsyms_filename, dir_sz);
                        ret = 0;
                        break;
@@ -1496,14 +1512,15 @@ static char *dso__find_kallsyms(struct dso *dso, struct map *map)
 
        build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id);
 
+       scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s", buildid_dir,
+                 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;
@@ -1517,7 +1534,7 @@ static char *dso__find_kallsyms(struct dso *dso, struct map *map)
                if (fd != -1) {
                        close(fd);
                        /* If module maps match go with /proc/kallsyms */
-                       if (!validate_kcore_modules("/proc/kallsyms", map))
+                       if (!validate_kcore_addresses("/proc/kallsyms", map))
                                goto proc_kallsyms;
                }
 
@@ -1528,6 +1545,10 @@ static char *dso__find_kallsyms(struct dso *dso, struct map *map)
                goto proc_kallsyms;
        }
 
+       /* Find kallsyms in build-id cache with kcore */
+       if (!find_matching_kcore(map, path, sizeof(path)))
+               return strdup(path);
+
        scnprintf(path, sizeof(path), "%s/[kernel.kallsyms]/%s",
                  buildid_dir, sbuild_id);
 
@@ -1570,15 +1591,8 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map,
        }
 
        if (!symbol_conf.ignore_vmlinux && symbol_conf.vmlinux_name != NULL) {
-               err = dso__load_vmlinux(dso, map,
-                                       symbol_conf.vmlinux_name, filter);
-               if (err > 0) {
-                       dso__set_long_name(dso,
-                                          strdup(symbol_conf.vmlinux_name));
-                       dso->lname_alloc = 1;
-                       return err;
-               }
-               return err;
+               return dso__load_vmlinux(dso, map, symbol_conf.vmlinux_name,
+                                        false, filter);
        }
 
        if (!symbol_conf.ignore_vmlinux && vmlinux_path != NULL) {
@@ -1604,7 +1618,7 @@ do_kallsyms:
        free(kallsyms_allocated_filename);
 
        if (err > 0 && !dso__is_kcore(dso)) {
-               dso__set_long_name(dso, strdup("[kernel.kallsyms]"));
+               dso__set_long_name(dso, "[kernel.kallsyms]", false);
                map__fixup_start(map);
                map__fixup_end(map);
        }
@@ -1634,7 +1648,8 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map,
                 */
                if (symbol_conf.default_guest_vmlinux_name != NULL) {
                        err = dso__load_vmlinux(dso, map,
-                               symbol_conf.default_guest_vmlinux_name, filter);
+                                               symbol_conf.default_guest_vmlinux_name,
+                                               false, filter);
                        return err;
                }
 
@@ -1651,7 +1666,7 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map,
                pr_debug("Using %s for symbols\n", kallsyms_filename);
        if (err > 0 && !dso__is_kcore(dso)) {
                machine__mmap_name(machine, path, sizeof(path));
-               dso__set_long_name(dso, strdup(path));
+               dso__set_long_name(dso, strdup(path), true);
                map__fixup_start(map);
                map__fixup_end(map);
        }
@@ -1661,13 +1676,10 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map,
 
 static void vmlinux_path__exit(void)
 {
-       while (--vmlinux_path__nr_entries >= 0) {
-               free(vmlinux_path[vmlinux_path__nr_entries]);
-               vmlinux_path[vmlinux_path__nr_entries] = NULL;
-       }
+       while (--vmlinux_path__nr_entries >= 0)
+               zfree(&vmlinux_path[vmlinux_path__nr_entries]);
 
-       free(vmlinux_path);
-       vmlinux_path = NULL;
+       zfree(&vmlinux_path);
 }
 
 static int vmlinux_path__init(void)
@@ -1719,7 +1731,7 @@ out_fail:
        return -1;
 }
 
-static int setup_list(struct strlist **list, const char *list_str,
+int setup_list(struct strlist **list, const char *list_str,
                      const char *list_name)
 {
        if (list_str == NULL)