linux/tools/perf/util/srcline.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <inttypes.h>
   3#include <stdio.h>
   4#include <stdlib.h>
   5#include <string.h>
   6
   7#include <linux/kernel.h>
   8
   9#include "util/dso.h"
  10#include "util/util.h"
  11#include "util/debug.h"
  12#include "util/callchain.h"
  13#include "srcline.h"
  14#include "string2.h"
  15#include "symbol.h"
  16
  17bool srcline_full_filename;
  18
  19static const char *dso__name(struct dso *dso)
  20{
  21        const char *dso_name;
  22
  23        if (dso->symsrc_filename)
  24                dso_name = dso->symsrc_filename;
  25        else
  26                dso_name = dso->long_name;
  27
  28        if (dso_name[0] == '[')
  29                return NULL;
  30
  31        if (!strncmp(dso_name, "/tmp/perf-", 10))
  32                return NULL;
  33
  34        return dso_name;
  35}
  36
  37static int inline_list__append(struct symbol *symbol, char *srcline,
  38                               struct inline_node *node)
  39{
  40        struct inline_list *ilist;
  41
  42        ilist = zalloc(sizeof(*ilist));
  43        if (ilist == NULL)
  44                return -1;
  45
  46        ilist->symbol = symbol;
  47        ilist->srcline = srcline;
  48
  49        if (callchain_param.order == ORDER_CALLEE)
  50                list_add_tail(&ilist->list, &node->val);
  51        else
  52                list_add(&ilist->list, &node->val);
  53
  54        return 0;
  55}
  56
  57/* basename version that takes a const input string */
  58static const char *gnu_basename(const char *path)
  59{
  60        const char *base = strrchr(path, '/');
  61
  62        return base ? base + 1 : path;
  63}
  64
  65static char *srcline_from_fileline(const char *file, unsigned int line)
  66{
  67        char *srcline;
  68
  69        if (!file)
  70                return NULL;
  71
  72        if (!srcline_full_filename)
  73                file = gnu_basename(file);
  74
  75        if (asprintf(&srcline, "%s:%u", file, line) < 0)
  76                return NULL;
  77
  78        return srcline;
  79}
  80
  81static struct symbol *new_inline_sym(struct dso *dso,
  82                                     struct symbol *base_sym,
  83                                     const char *funcname)
  84{
  85        struct symbol *inline_sym;
  86        char *demangled = NULL;
  87
  88        if (!funcname)
  89                funcname = "??";
  90
  91        if (dso) {
  92                demangled = dso__demangle_sym(dso, 0, funcname);
  93                if (demangled)
  94                        funcname = demangled;
  95        }
  96
  97        if (base_sym && strcmp(funcname, base_sym->name) == 0) {
  98                /* reuse the real, existing symbol */
  99                inline_sym = base_sym;
 100                /* ensure that we don't alias an inlined symbol, which could
 101                 * lead to double frees in inline_node__delete
 102                 */
 103                assert(!base_sym->inlined);
 104        } else {
 105                /* create a fake symbol for the inline frame */
 106                inline_sym = symbol__new(base_sym ? base_sym->start : 0,
 107                                         base_sym ? base_sym->end : 0,
 108                                         base_sym ? base_sym->binding : 0,
 109                                         base_sym ? base_sym->type : 0,
 110                                         funcname);
 111                if (inline_sym)
 112                        inline_sym->inlined = 1;
 113        }
 114
 115        free(demangled);
 116
 117        return inline_sym;
 118}
 119
 120#ifdef HAVE_LIBBFD_SUPPORT
 121
 122/*
 123 * Implement addr2line using libbfd.
 124 */
 125#define PACKAGE "perf"
 126#include <bfd.h>
 127
 128struct a2l_data {
 129        const char      *input;
 130        u64             addr;
 131
 132        bool            found;
 133        const char      *filename;
 134        const char      *funcname;
 135        unsigned        line;
 136
 137        bfd             *abfd;
 138        asymbol         **syms;
 139};
 140
 141static int bfd_error(const char *string)
 142{
 143        const char *errmsg;
 144
 145        errmsg = bfd_errmsg(bfd_get_error());
 146        fflush(stdout);
 147
 148        if (string)
 149                pr_debug("%s: %s\n", string, errmsg);
 150        else
 151                pr_debug("%s\n", errmsg);
 152
 153        return -1;
 154}
 155
 156static int slurp_symtab(bfd *abfd, struct a2l_data *a2l)
 157{
 158        long storage;
 159        long symcount;
 160        asymbol **syms;
 161        bfd_boolean dynamic = FALSE;
 162
 163        if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0)
 164                return bfd_error(bfd_get_filename(abfd));
 165
 166        storage = bfd_get_symtab_upper_bound(abfd);
 167        if (storage == 0L) {
 168                storage = bfd_get_dynamic_symtab_upper_bound(abfd);
 169                dynamic = TRUE;
 170        }
 171        if (storage < 0L)
 172                return bfd_error(bfd_get_filename(abfd));
 173
 174        syms = malloc(storage);
 175        if (dynamic)
 176                symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
 177        else
 178                symcount = bfd_canonicalize_symtab(abfd, syms);
 179
 180        if (symcount < 0) {
 181                free(syms);
 182                return bfd_error(bfd_get_filename(abfd));
 183        }
 184
 185        a2l->syms = syms;
 186        return 0;
 187}
 188
 189static void find_address_in_section(bfd *abfd, asection *section, void *data)
 190{
 191        bfd_vma pc, vma;
 192        bfd_size_type size;
 193        struct a2l_data *a2l = data;
 194
 195        if (a2l->found)
 196                return;
 197
 198        if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0)
 199                return;
 200
 201        pc = a2l->addr;
 202        vma = bfd_get_section_vma(abfd, section);
 203        size = bfd_get_section_size(section);
 204
 205        if (pc < vma || pc >= vma + size)
 206                return;
 207
 208        a2l->found = bfd_find_nearest_line(abfd, section, a2l->syms, pc - vma,
 209                                           &a2l->filename, &a2l->funcname,
 210                                           &a2l->line);
 211
 212        if (a2l->filename && !strlen(a2l->filename))
 213                a2l->filename = NULL;
 214}
 215
 216static struct a2l_data *addr2line_init(const char *path)
 217{
 218        bfd *abfd;
 219        struct a2l_data *a2l = NULL;
 220
 221        abfd = bfd_openr(path, NULL);
 222        if (abfd == NULL)
 223                return NULL;
 224
 225        if (!bfd_check_format(abfd, bfd_object))
 226                goto out;
 227
 228        a2l = zalloc(sizeof(*a2l));
 229        if (a2l == NULL)
 230                goto out;
 231
 232        a2l->abfd = abfd;
 233        a2l->input = strdup(path);
 234        if (a2l->input == NULL)
 235                goto out;
 236
 237        if (slurp_symtab(abfd, a2l))
 238                goto out;
 239
 240        return a2l;
 241
 242out:
 243        if (a2l) {
 244                zfree((char **)&a2l->input);
 245                free(a2l);
 246        }
 247        bfd_close(abfd);
 248        return NULL;
 249}
 250
 251static void addr2line_cleanup(struct a2l_data *a2l)
 252{
 253        if (a2l->abfd)
 254                bfd_close(a2l->abfd);
 255        zfree((char **)&a2l->input);
 256        zfree(&a2l->syms);
 257        free(a2l);
 258}
 259
 260#define MAX_INLINE_NEST 1024
 261
 262static int inline_list__append_dso_a2l(struct dso *dso,
 263                                       struct inline_node *node,
 264                                       struct symbol *sym)
 265{
 266        struct a2l_data *a2l = dso->a2l;
 267        struct symbol *inline_sym = new_inline_sym(dso, sym, a2l->funcname);
 268        char *srcline = NULL;
 269
 270        if (a2l->filename)
 271                srcline = srcline_from_fileline(a2l->filename, a2l->line);
 272
 273        return inline_list__append(inline_sym, srcline, node);
 274}
 275
 276static int addr2line(const char *dso_name, u64 addr,
 277                     char **file, unsigned int *line, struct dso *dso,
 278                     bool unwind_inlines, struct inline_node *node,
 279                     struct symbol *sym)
 280{
 281        int ret = 0;
 282        struct a2l_data *a2l = dso->a2l;
 283
 284        if (!a2l) {
 285                dso->a2l = addr2line_init(dso_name);
 286                a2l = dso->a2l;
 287        }
 288
 289        if (a2l == NULL) {
 290                pr_warning("addr2line_init failed for %s\n", dso_name);
 291                return 0;
 292        }
 293
 294        a2l->addr = addr;
 295        a2l->found = false;
 296
 297        bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l);
 298
 299        if (!a2l->found)
 300                return 0;
 301
 302        if (unwind_inlines) {
 303                int cnt = 0;
 304
 305                if (node && inline_list__append_dso_a2l(dso, node, sym))
 306                        return 0;
 307
 308                while (bfd_find_inliner_info(a2l->abfd, &a2l->filename,
 309                                             &a2l->funcname, &a2l->line) &&
 310                       cnt++ < MAX_INLINE_NEST) {
 311
 312                        if (a2l->filename && !strlen(a2l->filename))
 313                                a2l->filename = NULL;
 314
 315                        if (node != NULL) {
 316                                if (inline_list__append_dso_a2l(dso, node, sym))
 317                                        return 0;
 318                                // found at least one inline frame
 319                                ret = 1;
 320                        }
 321                }
 322        }
 323
 324        if (file) {
 325                *file = a2l->filename ? strdup(a2l->filename) : NULL;
 326                ret = *file ? 1 : 0;
 327        }
 328
 329        if (line)
 330                *line = a2l->line;
 331
 332        return ret;
 333}
 334
 335void dso__free_a2l(struct dso *dso)
 336{
 337        struct a2l_data *a2l = dso->a2l;
 338
 339        if (!a2l)
 340                return;
 341
 342        addr2line_cleanup(a2l);
 343
 344        dso->a2l = NULL;
 345}
 346
 347static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
 348                                        struct dso *dso, struct symbol *sym)
 349{
 350        struct inline_node *node;
 351
 352        node = zalloc(sizeof(*node));
 353        if (node == NULL) {
 354                perror("not enough memory for the inline node");
 355                return NULL;
 356        }
 357
 358        INIT_LIST_HEAD(&node->val);
 359        node->addr = addr;
 360
 361        addr2line(dso_name, addr, NULL, NULL, dso, true, node, sym);
 362        return node;
 363}
 364
 365#else /* HAVE_LIBBFD_SUPPORT */
 366
 367static int filename_split(char *filename, unsigned int *line_nr)
 368{
 369        char *sep;
 370
 371        sep = strchr(filename, '\n');
 372        if (sep)
 373                *sep = '\0';
 374
 375        if (!strcmp(filename, "??:0"))
 376                return 0;
 377
 378        sep = strchr(filename, ':');
 379        if (sep) {
 380                *sep++ = '\0';
 381                *line_nr = strtoul(sep, NULL, 0);
 382                return 1;
 383        }
 384
 385        return 0;
 386}
 387
 388static int addr2line(const char *dso_name, u64 addr,
 389                     char **file, unsigned int *line_nr,
 390                     struct dso *dso __maybe_unused,
 391                     bool unwind_inlines __maybe_unused,
 392                     struct inline_node *node __maybe_unused,
 393                     struct symbol *sym __maybe_unused)
 394{
 395        FILE *fp;
 396        char cmd[PATH_MAX];
 397        char *filename = NULL;
 398        size_t len;
 399        int ret = 0;
 400
 401        scnprintf(cmd, sizeof(cmd), "addr2line -e %s %016"PRIx64,
 402                  dso_name, addr);
 403
 404        fp = popen(cmd, "r");
 405        if (fp == NULL) {
 406                pr_warning("popen failed for %s\n", dso_name);
 407                return 0;
 408        }
 409
 410        if (getline(&filename, &len, fp) < 0 || !len) {
 411                pr_warning("addr2line has no output for %s\n", dso_name);
 412                goto out;
 413        }
 414
 415        ret = filename_split(filename, line_nr);
 416        if (ret != 1) {
 417                free(filename);
 418                goto out;
 419        }
 420
 421        *file = filename;
 422
 423out:
 424        pclose(fp);
 425        return ret;
 426}
 427
 428void dso__free_a2l(struct dso *dso __maybe_unused)
 429{
 430}
 431
 432static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
 433                                        struct dso *dso __maybe_unused,
 434                                        struct symbol *sym)
 435{
 436        FILE *fp;
 437        char cmd[PATH_MAX];
 438        struct inline_node *node;
 439        char *filename = NULL;
 440        char *funcname = NULL;
 441        size_t filelen, funclen;
 442        unsigned int line_nr = 0;
 443
 444        scnprintf(cmd, sizeof(cmd), "addr2line -e %s -i -f %016"PRIx64,
 445                  dso_name, addr);
 446
 447        fp = popen(cmd, "r");
 448        if (fp == NULL) {
 449                pr_err("popen failed for %s\n", dso_name);
 450                return NULL;
 451        }
 452
 453        node = zalloc(sizeof(*node));
 454        if (node == NULL) {
 455                perror("not enough memory for the inline node");
 456                goto out;
 457        }
 458
 459        INIT_LIST_HEAD(&node->val);
 460        node->addr = addr;
 461
 462        /* addr2line -f generates two lines for each inlined functions */
 463        while (getline(&funcname, &funclen, fp) != -1) {
 464                char *srcline;
 465                struct symbol *inline_sym;
 466
 467                rtrim(funcname);
 468
 469                if (getline(&filename, &filelen, fp) == -1)
 470                        goto out;
 471
 472                if (filename_split(filename, &line_nr) != 1)
 473                        goto out;
 474
 475                srcline = srcline_from_fileline(filename, line_nr);
 476                inline_sym = new_inline_sym(dso, sym, funcname);
 477
 478                if (inline_list__append(inline_sym, srcline, node) != 0) {
 479                        free(srcline);
 480                        if (inline_sym && inline_sym->inlined)
 481                                symbol__delete(inline_sym);
 482                        goto out;
 483                }
 484        }
 485
 486out:
 487        pclose(fp);
 488        free(filename);
 489        free(funcname);
 490
 491        return node;
 492}
 493
 494#endif /* HAVE_LIBBFD_SUPPORT */
 495
 496/*
 497 * Number of addr2line failures (without success) before disabling it for that
 498 * dso.
 499 */
 500#define A2L_FAIL_LIMIT 123
 501
 502char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
 503                  bool show_sym, bool show_addr, bool unwind_inlines,
 504                  u64 ip)
 505{
 506        char *file = NULL;
 507        unsigned line = 0;
 508        char *srcline;
 509        const char *dso_name;
 510
 511        if (!dso->has_srcline)
 512                goto out;
 513
 514        dso_name = dso__name(dso);
 515        if (dso_name == NULL)
 516                goto out;
 517
 518        if (!addr2line(dso_name, addr, &file, &line, dso,
 519                       unwind_inlines, NULL, sym))
 520                goto out;
 521
 522        srcline = srcline_from_fileline(file, line);
 523        free(file);
 524
 525        if (!srcline)
 526                goto out;
 527
 528        dso->a2l_fails = 0;
 529
 530        return srcline;
 531
 532out:
 533        if (dso->a2l_fails && ++dso->a2l_fails > A2L_FAIL_LIMIT) {
 534                dso->has_srcline = 0;
 535                dso__free_a2l(dso);
 536        }
 537
 538        if (!show_addr)
 539                return (show_sym && sym) ?
 540                            strndup(sym->name, sym->namelen) : NULL;
 541
 542        if (sym) {
 543                if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "",
 544                                        ip - sym->start) < 0)
 545                        return SRCLINE_UNKNOWN;
 546        } else if (asprintf(&srcline, "%s[%" PRIx64 "]", dso->short_name, addr) < 0)
 547                return SRCLINE_UNKNOWN;
 548        return srcline;
 549}
 550
 551void free_srcline(char *srcline)
 552{
 553        if (srcline && strcmp(srcline, SRCLINE_UNKNOWN) != 0)
 554                free(srcline);
 555}
 556
 557char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
 558                  bool show_sym, bool show_addr, u64 ip)
 559{
 560        return __get_srcline(dso, addr, sym, show_sym, show_addr, false, ip);
 561}
 562
 563struct srcline_node {
 564        u64                     addr;
 565        char                    *srcline;
 566        struct rb_node          rb_node;
 567};
 568
 569void srcline__tree_insert(struct rb_root *tree, u64 addr, char *srcline)
 570{
 571        struct rb_node **p = &tree->rb_node;
 572        struct rb_node *parent = NULL;
 573        struct srcline_node *i, *node;
 574
 575        node = zalloc(sizeof(struct srcline_node));
 576        if (!node) {
 577                perror("not enough memory for the srcline node");
 578                return;
 579        }
 580
 581        node->addr = addr;
 582        node->srcline = srcline;
 583
 584        while (*p != NULL) {
 585                parent = *p;
 586                i = rb_entry(parent, struct srcline_node, rb_node);
 587                if (addr < i->addr)
 588                        p = &(*p)->rb_left;
 589                else
 590                        p = &(*p)->rb_right;
 591        }
 592        rb_link_node(&node->rb_node, parent, p);
 593        rb_insert_color(&node->rb_node, tree);
 594}
 595
 596char *srcline__tree_find(struct rb_root *tree, u64 addr)
 597{
 598        struct rb_node *n = tree->rb_node;
 599
 600        while (n) {
 601                struct srcline_node *i = rb_entry(n, struct srcline_node,
 602                                                  rb_node);
 603
 604                if (addr < i->addr)
 605                        n = n->rb_left;
 606                else if (addr > i->addr)
 607                        n = n->rb_right;
 608                else
 609                        return i->srcline;
 610        }
 611
 612        return NULL;
 613}
 614
 615void srcline__tree_delete(struct rb_root *tree)
 616{
 617        struct srcline_node *pos;
 618        struct rb_node *next = rb_first(tree);
 619
 620        while (next) {
 621                pos = rb_entry(next, struct srcline_node, rb_node);
 622                next = rb_next(&pos->rb_node);
 623                rb_erase(&pos->rb_node, tree);
 624                free_srcline(pos->srcline);
 625                zfree(&pos);
 626        }
 627}
 628
 629struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr,
 630                                            struct symbol *sym)
 631{
 632        const char *dso_name;
 633
 634        dso_name = dso__name(dso);
 635        if (dso_name == NULL)
 636                return NULL;
 637
 638        return addr2inlines(dso_name, addr, dso, sym);
 639}
 640
 641void inline_node__delete(struct inline_node *node)
 642{
 643        struct inline_list *ilist, *tmp;
 644
 645        list_for_each_entry_safe(ilist, tmp, &node->val, list) {
 646                list_del_init(&ilist->list);
 647                free_srcline(ilist->srcline);
 648                /* only the inlined symbols are owned by the list */
 649                if (ilist->symbol && ilist->symbol->inlined)
 650                        symbol__delete(ilist->symbol);
 651                free(ilist);
 652        }
 653
 654        free(node);
 655}
 656
 657void inlines__tree_insert(struct rb_root *tree, struct inline_node *inlines)
 658{
 659        struct rb_node **p = &tree->rb_node;
 660        struct rb_node *parent = NULL;
 661        const u64 addr = inlines->addr;
 662        struct inline_node *i;
 663
 664        while (*p != NULL) {
 665                parent = *p;
 666                i = rb_entry(parent, struct inline_node, rb_node);
 667                if (addr < i->addr)
 668                        p = &(*p)->rb_left;
 669                else
 670                        p = &(*p)->rb_right;
 671        }
 672        rb_link_node(&inlines->rb_node, parent, p);
 673        rb_insert_color(&inlines->rb_node, tree);
 674}
 675
 676struct inline_node *inlines__tree_find(struct rb_root *tree, u64 addr)
 677{
 678        struct rb_node *n = tree->rb_node;
 679
 680        while (n) {
 681                struct inline_node *i = rb_entry(n, struct inline_node,
 682                                                 rb_node);
 683
 684                if (addr < i->addr)
 685                        n = n->rb_left;
 686                else if (addr > i->addr)
 687                        n = n->rb_right;
 688                else
 689                        return i;
 690        }
 691
 692        return NULL;
 693}
 694
 695void inlines__tree_delete(struct rb_root *tree)
 696{
 697        struct inline_node *pos;
 698        struct rb_node *next = rb_first(tree);
 699
 700        while (next) {
 701                pos = rb_entry(next, struct inline_node, rb_node);
 702                next = rb_next(&pos->rb_node);
 703                rb_erase(&pos->rb_node, tree);
 704                inline_node__delete(pos);
 705        }
 706}
 707