]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - tools/perf/util/symbol.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / tools / perf / util / symbol.c
index 439ab947daf4af9da352caac290d372ce23846ac..b1bf490aff8808ad1a53d1b98491ee7a919386da 100644 (file)
@@ -11,6 +11,7 @@
 #include <sys/param.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <inttypes.h>
 #include "build-id.h"
 #include "debug.h"
 #include "symbol.h"
 #include <limits.h>
 #include <sys/utsname.h>
 
+#ifndef KSYM_NAME_LEN
+#define KSYM_NAME_LEN 128
+#endif
+
 #ifndef NT_GNU_BUILD_ID
 #define NT_GNU_BUILD_ID 3
 #endif
@@ -41,6 +46,7 @@ struct symbol_conf symbol_conf = {
        .exclude_other    = true,
        .use_modules      = true,
        .try_vmlinux_path = true,
+       .symfs            = "",
 };
 
 int dso__name_len(const struct dso *self)
@@ -92,7 +98,7 @@ static void symbols__fixup_end(struct rb_root *self)
                prev = curr;
                curr = rb_entry(nd, struct symbol, rb_node);
 
-               if (prev->end == prev->start)
+               if (prev->end == prev->start && prev->end != curr->start)
                        prev->end = curr->start - 1;
        }
 
@@ -121,7 +127,7 @@ static void __map_groups__fixup_end(struct map_groups *self, enum map_type type)
         * We still haven't the actual symbols, so guess the
         * last map final address.
         */
-       curr->end = ~0UL;
+       curr->end = ~0ULL;
 }
 
 static void map_groups__fixup_end(struct map_groups *self)
@@ -148,7 +154,7 @@ static struct symbol *symbol__new(u64 start, u64 len, u8 binding,
        self->binding = binding;
        self->namelen = namelen - 1;
 
-       pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end);
+       pr_debug4("%s: %s %#" PRIx64 "-%#" PRIx64 "\n", __func__, name, start, self->end);
 
        memcpy(self->name, name, namelen);
 
@@ -162,7 +168,7 @@ void symbol__delete(struct symbol *self)
 
 static size_t symbol__fprintf(struct symbol *self, FILE *fp)
 {
-       return fprintf(fp, " %llx-%llx %c %s\n",
+       return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n",
                       self->start, self->end,
                       self->binding == STB_GLOBAL ? 'g' :
                       self->binding == STB_LOCAL  ? 'l' : 'w',
@@ -425,16 +431,25 @@ size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp)
 
 int kallsyms__parse(const char *filename, void *arg,
                    int (*process_symbol)(void *arg, const char *name,
-                                                    char type, u64 start))
+                                         char type, u64 start, u64 end))
 {
        char *line = NULL;
        size_t n;
-       int err = 0;
+       int err = -1;
+       u64 prev_start = 0;
+       char prev_symbol_type = 0;
+       char *prev_symbol_name;
        FILE *file = fopen(filename, "r");
 
        if (file == NULL)
                goto out_failure;
 
+       prev_symbol_name = malloc(KSYM_NAME_LEN);
+       if (prev_symbol_name == NULL)
+               goto out_close;
+
+       err = 0;
+
        while (!feof(file)) {
                u64 start;
                int line_len, len;
@@ -454,14 +469,33 @@ int kallsyms__parse(const char *filename, void *arg,
                        continue;
 
                symbol_type = toupper(line[len]);
-               symbol_name = line + len + 2;
+               len += 2;
+               symbol_name = line + len;
+               len = line_len - len;
 
-               err = process_symbol(arg, symbol_name, symbol_type, start);
-               if (err)
+               if (len >= KSYM_NAME_LEN) {
+                       err = -1;
                        break;
+               }
+
+               if (prev_symbol_type) {
+                       u64 end = start;
+                       if (end != prev_start)
+                               --end;
+                       err = process_symbol(arg, prev_symbol_name,
+                                            prev_symbol_type, prev_start, end);
+                       if (err)
+                               break;
+               }
+
+               memcpy(prev_symbol_name, symbol_name, len + 1);
+               prev_symbol_type = symbol_type;
+               prev_start = start;
        }
 
+       free(prev_symbol_name);
        free(line);
+out_close:
        fclose(file);
        return err;
 
@@ -483,7 +517,7 @@ static u8 kallsyms2elf_type(char type)
 }
 
 static int map__process_kallsym_symbol(void *arg, const char *name,
-                                      char type, u64 start)
+                                      char type, u64 start, u64 end)
 {
        struct symbol *sym;
        struct process_kallsyms_args *a = arg;
@@ -492,11 +526,8 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
        if (!symbol_type__is_a(type, a->map->type))
                return 0;
 
-       /*
-        * Will fix up the end later, when we have all symbols sorted.
-        */
-       sym = symbol__new(start, 0, kallsyms2elf_type(type), name);
-
+       sym = symbol__new(start, end - start + 1,
+                         kallsyms2elf_type(type), name);
        if (sym == NULL)
                return -ENOMEM;
        /*
@@ -649,7 +680,6 @@ int dso__load_kallsyms(struct dso *self, const char *filename,
        if (dso__load_all_kallsyms(self, filename, map) < 0)
                return -1;
 
-       symbols__fixup_end(&self->symbols[map->type]);
        if (self->kernel == DSO_TYPE_GUEST_KERNEL)
                self->origin = DSO__ORIG_GUEST_KERNEL;
        else
@@ -839,8 +869,11 @@ static int dso__synthesize_plt_symbols(struct  dso *self, struct map *map,
        char sympltname[1024];
        Elf *elf;
        int nr = 0, symidx, fd, err = 0;
+       char name[PATH_MAX];
 
-       fd = open(self->long_name, O_RDONLY);
+       snprintf(name, sizeof(name), "%s%s",
+                symbol_conf.symfs, self->long_name);
+       fd = open(name, O_RDONLY);
        if (fd < 0)
                goto out;
 
@@ -1129,6 +1162,13 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
 
                section_name = elf_sec__name(&shdr, secstrs);
 
+               /* On ARM, symbols for thumb functions have 1 added to
+                * the symbol address as a flag - remove it */
+               if ((ehdr.e_machine == EM_ARM) &&
+                   (map->type == MAP__FUNCTION) &&
+                   (sym.st_value & 1))
+                       --sym.st_value;
+
                if (self->kernel != DSO_TYPE_USER || kmodule) {
                        char dso_name[PATH_MAX];
 
@@ -1176,8 +1216,8 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
                }
 
                if (curr_dso->adjust_symbols) {
-                       pr_debug4("%s: adjusting symbol: st_value: %#Lx "
-                                 "sh_addr: %#Lx sh_offset: %#Lx\n", __func__,
+                       pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " "
+                                 "sh_addr: %#" PRIx64 " sh_offset: %#" PRIx64 "\n", __func__,
                                  (u64)sym.st_value, (u64)shdr.sh_addr,
                                  (u64)shdr.sh_offset);
                        sym.st_value -= shdr.sh_addr - shdr.sh_offset;
@@ -1452,16 +1492,19 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
             self->origin++) {
                switch (self->origin) {
                case DSO__ORIG_BUILD_ID_CACHE:
-                       if (dso__build_id_filename(self, name, size) == NULL)
+                       /* skip the locally configured cache if a symfs is given */
+                       if (symbol_conf.symfs[0] ||
+                           (dso__build_id_filename(self, name, size) == NULL)) {
                                continue;
+                       }
                        break;
                case DSO__ORIG_FEDORA:
-                       snprintf(name, size, "/usr/lib/debug%s.debug",
-                                self->long_name);
+                       snprintf(name, size, "%s/usr/lib/debug%s.debug",
+                                symbol_conf.symfs, self->long_name);
                        break;
                case DSO__ORIG_UBUNTU:
-                       snprintf(name, size, "/usr/lib/debug%s",
-                                self->long_name);
+                       snprintf(name, size, "%s/usr/lib/debug%s",
+                                symbol_conf.symfs, self->long_name);
                        break;
                case DSO__ORIG_BUILDID: {
                        char build_id_hex[BUILD_ID_SIZE * 2 + 1];
@@ -1473,19 +1516,26 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
                                          sizeof(self->build_id),
                                          build_id_hex);
                        snprintf(name, size,
-                                "/usr/lib/debug/.build-id/%.2s/%s.debug",
-                                build_id_hex, build_id_hex + 2);
+                                "%s/usr/lib/debug/.build-id/%.2s/%s.debug",
+                                symbol_conf.symfs, build_id_hex, build_id_hex + 2);
                        }
                        break;
                case DSO__ORIG_DSO:
-                       snprintf(name, size, "%s", self->long_name);
+                       snprintf(name, size, "%s%s",
+                            symbol_conf.symfs, self->long_name);
                        break;
                case DSO__ORIG_GUEST_KMODULE:
                        if (map->groups && map->groups->machine)
                                root_dir = map->groups->machine->root_dir;
                        else
                                root_dir = "";
-                       snprintf(name, size, "%s%s", root_dir, self->long_name);
+                       snprintf(name, size, "%s%s%s", symbol_conf.symfs,
+                                root_dir, self->long_name);
+                       break;
+
+               case DSO__ORIG_KMODULE:
+                       snprintf(name, size, "%s%s", symbol_conf.symfs,
+                                self->long_name);
                        break;
 
                default:
@@ -1784,17 +1834,20 @@ int dso__load_vmlinux(struct dso *self, struct map *map,
                      const char *vmlinux, symbol_filter_t filter)
 {
        int err = -1, fd;
+       char symfs_vmlinux[PATH_MAX];
 
-       fd = open(vmlinux, O_RDONLY);
+       snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s",
+                symbol_conf.symfs, vmlinux);
+       fd = open(symfs_vmlinux, O_RDONLY);
        if (fd < 0)
                return -1;
 
        dso__set_loaded(self, map->type);
-       err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0);
+       err = dso__load_sym(self, map, symfs_vmlinux, fd, filter, 0, 0);
        close(fd);
 
        if (err > 0)
-               pr_debug("Using %s for symbols\n", vmlinux);
+               pr_debug("Using %s for symbols\n", symfs_vmlinux);
 
        return err;
 }
@@ -1836,8 +1889,8 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
        const char *kallsyms_filename = NULL;
        char *kallsyms_allocated_filename = NULL;
        /*
-        * Step 1: if the user specified a vmlinux filename, use it and only
-        * it, reporting errors to the user if it cannot be used.
+        * Step 1: if the user specified a kallsyms or vmlinux filename, use
+        * it and only it, reporting errors to the user if it cannot be used.
         *
         * For instance, try to analyse an ARM perf.data file _without_ a
         * build-id, or if the user specifies the wrong path to the right
@@ -1850,6 +1903,11 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
         * validation in dso__load_vmlinux and will bail out if they don't
         * match.
         */
+       if (symbol_conf.kallsyms_name != NULL) {
+               kallsyms_filename = symbol_conf.kallsyms_name;
+               goto do_kallsyms;
+       }
+
        if (symbol_conf.vmlinux_name != NULL) {
                err = dso__load_vmlinux(self, map,
                                        symbol_conf.vmlinux_name, filter);
@@ -1867,6 +1925,10 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
                        goto out_fixup;
        }
 
+       /* do not try local files if a symfs was given */
+       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,
@@ -2136,7 +2198,7 @@ struct process_args {
 };
 
 static int symbol__in_kernel(void *arg, const char *name,
-                            char type __used, u64 start)
+                            char type __used, u64 start, u64 end __used)
 {
        struct process_args *args = arg;
 
@@ -2257,9 +2319,6 @@ static int vmlinux_path__init(void)
        struct utsname uts;
        char bf[PATH_MAX];
 
-       if (uname(&uts) < 0)
-               return -1;
-
        vmlinux_path = malloc(sizeof(char *) * 5);
        if (vmlinux_path == NULL)
                return -1;
@@ -2272,6 +2331,14 @@ static int vmlinux_path__init(void)
        if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
                goto out_fail;
        ++vmlinux_path__nr_entries;
+
+       /* only try running kernel version if no symfs was given */
+       if (symbol_conf.symfs[0] != 0)
+               return 0;
+
+       if (uname(&uts) < 0)
+               return -1;
+
        snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release);
        vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
        if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
@@ -2331,6 +2398,8 @@ static int setup_list(struct strlist **list, const char *list_str,
 
 int symbol__init(void)
 {
+       const char *symfs;
+
        if (symbol_conf.initialized)
                return 0;
 
@@ -2359,6 +2428,18 @@ int symbol__init(void)
                       symbol_conf.sym_list_str, "symbol") < 0)
                goto out_free_comm_list;
 
+       /*
+        * A path to symbols of "/" is identical to ""
+        * reset here for simplicity.
+        */
+       symfs = realpath(symbol_conf.symfs, NULL);
+       if (symfs == NULL)
+               symfs = symbol_conf.symfs;
+       if (strcmp(symfs, "/") == 0)
+               symbol_conf.symfs = "";
+       if (symfs != symbol_conf.symfs)
+               free((void *)symfs);
+
        symbol_conf.initialized = true;
        return 0;