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