linux/tools/perf/util/srcline.c
<<
>>
Prefs
   1#include <stdio.h>
   2#include <stdlib.h>
   3#include <string.h>
   4
   5#include <linux/kernel.h>
   6
   7#include "util/dso.h"
   8#include "util/util.h"
   9#include "util/debug.h"
  10
  11#include "symbol.h"
  12
  13#ifdef HAVE_LIBBFD_SUPPORT
  14
  15/*
  16 * Implement addr2line using libbfd.
  17 */
  18#define PACKAGE "perf"
  19#include <bfd.h>
  20
  21struct a2l_data {
  22        const char      *input;
  23        u64             addr;
  24
  25        bool            found;
  26        const char      *filename;
  27        const char      *funcname;
  28        unsigned        line;
  29
  30        bfd             *abfd;
  31        asymbol         **syms;
  32};
  33
  34static int bfd_error(const char *string)
  35{
  36        const char *errmsg;
  37
  38        errmsg = bfd_errmsg(bfd_get_error());
  39        fflush(stdout);
  40
  41        if (string)
  42                pr_debug("%s: %s\n", string, errmsg);
  43        else
  44                pr_debug("%s\n", errmsg);
  45
  46        return -1;
  47}
  48
  49static int slurp_symtab(bfd *abfd, struct a2l_data *a2l)
  50{
  51        long storage;
  52        long symcount;
  53        asymbol **syms;
  54        bfd_boolean dynamic = FALSE;
  55
  56        if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0)
  57                return bfd_error(bfd_get_filename(abfd));
  58
  59        storage = bfd_get_symtab_upper_bound(abfd);
  60        if (storage == 0L) {
  61                storage = bfd_get_dynamic_symtab_upper_bound(abfd);
  62                dynamic = TRUE;
  63        }
  64        if (storage < 0L)
  65                return bfd_error(bfd_get_filename(abfd));
  66
  67        syms = malloc(storage);
  68        if (dynamic)
  69                symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
  70        else
  71                symcount = bfd_canonicalize_symtab(abfd, syms);
  72
  73        if (symcount < 0) {
  74                free(syms);
  75                return bfd_error(bfd_get_filename(abfd));
  76        }
  77
  78        a2l->syms = syms;
  79        return 0;
  80}
  81
  82static void find_address_in_section(bfd *abfd, asection *section, void *data)
  83{
  84        bfd_vma pc, vma;
  85        bfd_size_type size;
  86        struct a2l_data *a2l = data;
  87
  88        if (a2l->found)
  89                return;
  90
  91        if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0)
  92                return;
  93
  94        pc = a2l->addr;
  95        vma = bfd_get_section_vma(abfd, section);
  96        size = bfd_get_section_size(section);
  97
  98        if (pc < vma || pc >= vma + size)
  99                return;
 100
 101        a2l->found = bfd_find_nearest_line(abfd, section, a2l->syms, pc - vma,
 102                                           &a2l->filename, &a2l->funcname,
 103                                           &a2l->line);
 104}
 105
 106static struct a2l_data *addr2line_init(const char *path)
 107{
 108        bfd *abfd;
 109        struct a2l_data *a2l = NULL;
 110
 111        abfd = bfd_openr(path, NULL);
 112        if (abfd == NULL)
 113                return NULL;
 114
 115        if (!bfd_check_format(abfd, bfd_object))
 116                goto out;
 117
 118        a2l = zalloc(sizeof(*a2l));
 119        if (a2l == NULL)
 120                goto out;
 121
 122        a2l->abfd = abfd;
 123        a2l->input = strdup(path);
 124        if (a2l->input == NULL)
 125                goto out;
 126
 127        if (slurp_symtab(abfd, a2l))
 128                goto out;
 129
 130        return a2l;
 131
 132out:
 133        if (a2l) {
 134                zfree((char **)&a2l->input);
 135                free(a2l);
 136        }
 137        bfd_close(abfd);
 138        return NULL;
 139}
 140
 141static void addr2line_cleanup(struct a2l_data *a2l)
 142{
 143        if (a2l->abfd)
 144                bfd_close(a2l->abfd);
 145        zfree((char **)&a2l->input);
 146        zfree(&a2l->syms);
 147        free(a2l);
 148}
 149
 150static int addr2line(const char *dso_name, u64 addr,
 151                     char **file, unsigned int *line, struct dso *dso)
 152{
 153        int ret = 0;
 154        struct a2l_data *a2l = dso->a2l;
 155
 156        if (!a2l) {
 157                dso->a2l = addr2line_init(dso_name);
 158                a2l = dso->a2l;
 159        }
 160
 161        if (a2l == NULL) {
 162                pr_warning("addr2line_init failed for %s\n", dso_name);
 163                return 0;
 164        }
 165
 166        a2l->addr = addr;
 167        a2l->found = false;
 168
 169        bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l);
 170
 171        if (a2l->found && a2l->filename) {
 172                *file = strdup(a2l->filename);
 173                *line = a2l->line;
 174
 175                if (*file)
 176                        ret = 1;
 177        }
 178
 179        return ret;
 180}
 181
 182void dso__free_a2l(struct dso *dso)
 183{
 184        struct a2l_data *a2l = dso->a2l;
 185
 186        if (!a2l)
 187                return;
 188
 189        addr2line_cleanup(a2l);
 190
 191        dso->a2l = NULL;
 192}
 193
 194#else /* HAVE_LIBBFD_SUPPORT */
 195
 196static int addr2line(const char *dso_name, u64 addr,
 197                     char **file, unsigned int *line_nr,
 198                     struct dso *dso __maybe_unused)
 199{
 200        FILE *fp;
 201        char cmd[PATH_MAX];
 202        char *filename = NULL;
 203        size_t len;
 204        char *sep;
 205        int ret = 0;
 206
 207        scnprintf(cmd, sizeof(cmd), "addr2line -e %s %016"PRIx64,
 208                  dso_name, addr);
 209
 210        fp = popen(cmd, "r");
 211        if (fp == NULL) {
 212                pr_warning("popen failed for %s\n", dso_name);
 213                return 0;
 214        }
 215
 216        if (getline(&filename, &len, fp) < 0 || !len) {
 217                pr_warning("addr2line has no output for %s\n", dso_name);
 218                goto out;
 219        }
 220
 221        sep = strchr(filename, '\n');
 222        if (sep)
 223                *sep = '\0';
 224
 225        if (!strcmp(filename, "??:0")) {
 226                pr_debug("no debugging info in %s\n", dso_name);
 227                free(filename);
 228                goto out;
 229        }
 230
 231        sep = strchr(filename, ':');
 232        if (sep) {
 233                *sep++ = '\0';
 234                *file = filename;
 235                *line_nr = strtoul(sep, NULL, 0);
 236                ret = 1;
 237        }
 238out:
 239        pclose(fp);
 240        return ret;
 241}
 242
 243void dso__free_a2l(struct dso *dso __maybe_unused)
 244{
 245}
 246
 247#endif /* HAVE_LIBBFD_SUPPORT */
 248
 249/*
 250 * Number of addr2line failures (without success) before disabling it for that
 251 * dso.
 252 */
 253#define A2L_FAIL_LIMIT 123
 254
 255char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
 256                  bool show_sym)
 257{
 258        char *file = NULL;
 259        unsigned line = 0;
 260        char *srcline;
 261        const char *dso_name;
 262
 263        if (!dso->has_srcline)
 264                goto out;
 265
 266        if (dso->symsrc_filename)
 267                dso_name = dso->symsrc_filename;
 268        else
 269                dso_name = dso->long_name;
 270
 271        if (dso_name[0] == '[')
 272                goto out;
 273
 274        if (!strncmp(dso_name, "/tmp/perf-", 10))
 275                goto out;
 276
 277        if (!addr2line(dso_name, addr, &file, &line, dso))
 278                goto out;
 279
 280        if (asprintf(&srcline, "%s:%u", basename(file), line) < 0) {
 281                free(file);
 282                goto out;
 283        }
 284
 285        dso->a2l_fails = 0;
 286
 287        free(file);
 288        return srcline;
 289
 290out:
 291        if (dso->a2l_fails && ++dso->a2l_fails > A2L_FAIL_LIMIT) {
 292                dso->has_srcline = 0;
 293                dso__free_a2l(dso);
 294        }
 295        if (sym) {
 296                if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "",
 297                                        addr - sym->start) < 0)
 298                        return SRCLINE_UNKNOWN;
 299        } else if (asprintf(&srcline, "%s[%" PRIx64 "]", dso->short_name, addr) < 0)
 300                return SRCLINE_UNKNOWN;
 301        return srcline;
 302}
 303
 304void free_srcline(char *srcline)
 305{
 306        if (srcline && strcmp(srcline, SRCLINE_UNKNOWN) != 0)
 307                free(srcline);
 308}
 309