// SPDX-License-Identifier: GPL-2.0
#include <inttypes.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/zalloc.h>
#include "util/dso.h"
#include "util/debug.h"
#include "util/callchain.h"
#include "util/symbol_conf.h"
#include "srcline.h"
#include "string2.h"
#include "symbol.h"
#include "subcmd/run-command.h"
bool srcline_full_filename;
static const char *dso__name(struct dso *dso)
{
const char *dso_name;
if (dso->symsrc_filename)
dso_name = dso->symsrc_filename;
else
dso_name = dso->long_name;
if (dso_name[0] == '[')
return NULL;
if (!strncmp(dso_name, "/tmp/perf-", 10))
return NULL;
return dso_name;
}
static int inline_list__append(struct symbol *symbol, char *srcline,
struct inline_node *node)
{
struct inline_list *ilist;
ilist = zalloc(sizeof(*ilist));
if (ilist == NULL)
return -1;
ilist->symbol = symbol;
ilist->srcline = srcline;
if (callchain_param.order == ORDER_CALLEE)
list_add_tail(&ilist->list, &node->val);
else
list_add(&ilist->list, &node->val);
return 0;
}
/* basename version that takes a const input string */
static const char *gnu_basename(const char *path)
{
const char *base = strrchr(path, '/');
return base ? base + 1 : path;
}
static char *srcline_from_fileline(const char *file, unsigned int line)
{
char *srcline;
if (!file)
return NULL;
if (!srcline_full_filename)
file = gnu_basename(file);
if (asprintf(&srcline, "%s:%u", file, line) < 0)
return NULL;
return srcline;
}
static struct symbol *new_inline_sym(struct dso *dso,
struct symbol *base_sym,
const char *funcname)
{
struct symbol *inline_sym;
char *demangled = NULL;
if (!funcname)
funcname = "??";
if (dso) {
demangled = dso__demangle_sym(dso, 0, funcname);
if (demangled)
funcname = demangled;
}
if (base_sym && strcmp(funcname, base_sym->name) == 0) {
/* reuse the real, existing symbol */
inline_sym = base_sym;
/* ensure that we don't alias an inlined symbol, which could
* lead to double frees in inline_node__delete
*/
assert(!base_sym->inlined);
} else {
/* create a fake symbol for the inline frame */
inline_sym = symbol__new(base_sym ? base_sym->start : 0,
base_sym ? (base_sym->end - base_sym->start) : 0,
base_sym ? base_sym->binding : 0,
base_sym ? base_sym->type : 0,
funcname);
if (inline_sym)
inline_sym->inlined = 1;
}
free(demangled);
return inline_sym;
}
#define MAX_INLINE_NEST 1024
#ifdef HAVE_LIBBFD_SUPPORT
/*
* Implement addr2line using libbfd.
*/
#define PACKAGE "perf"
#include <bfd.h>
struct a2l_data {
const char *input;
u64 addr;
bool found;
const char *filename;
const char *funcname;
unsigned line;
bfd *abfd;
asymbol **syms;
};
static int bfd_error(const char *string)
{
const char *errmsg;
errmsg = bfd_errmsg(bfd_get_error());
fflush(stdout);
if (string)
pr_debug("%s: %s\n", string, errmsg);
else
pr_debug("%s\n", errmsg);
return -1;
}
static int slurp_symtab(bfd *abfd, struct a2l_data *a2l)
{
long storage;
long symcount;
asymbol **syms;
bfd_boolean dynamic = FALSE;
if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0)
return bfd_error(bfd_get_filename(abfd));
storage = bfd_get_symtab_upper_bound(abfd);
if (storage == 0L) {
storage = bfd_get_dynamic_symtab_upper_bound(abfd);
dynamic = TRUE;
}
if (storage < 0L)
return bfd_error(bfd_get_filename(abfd));
syms = malloc(storage);
if (dynamic)
symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
else
symcount = bfd_canonicalize_symtab(abfd, syms);
if (symcount < 0) {
free(syms);
return bfd_error(bfd_get_filename(abfd));
}
a2l->syms = syms;
return 0;
}
static void find_address_in_section(bfd *abfd, asection *section, void *data)
{
bfd_vma pc, vma;
bfd_size_type size;
struct a2l_data *a2l = data;
flagword flags;
if (a2l->found)
return;
#ifdef bfd_get_section_flags
flags = bfd_get_section_flags(abfd, section);
#else
flags = bfd_section_flags(section);
#endif
if ((flags & SEC_ALLOC) == 0)
return;
pc = a2l->addr;
#ifdef bfd_get_section_vma
vma = bfd_get_section_vma(abfd, section);
#else
vma = bfd_section_vma(section);
#endif
#ifdef bfd_get_section_size
size = bfd_get_section_size(section);
#else
size = bfd_section_size(section);
#endif
if (pc < vma || pc >= vma + size)
return;
a2l->found = bfd_find_nearest_line