Files
rocm-systems/src/include/BfdBacktrace.hpp
T

240 rivejä
6.2 KiB
C++
Executable File

#ifndef __BFD_BACKTRACE__
#define __BFD_BACKTRACE__
/*
* Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED.
* Modification Copyright (C) Advanced Micro Devices, Inc, 2022. ALL RIGHTS RESERVED
*
* This code is based on the UCX library's mechanism to extract the call stack
* using the BFD library (ucx/src/ucs/debug/debug.c).
*/
#include <dirent.h>
#include <link.h>
#include <dlfcn.h>
#include <execinfo.h>
#include <bfd.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#ifdef HAVE_CPLUS_DEMANGLE
#define HAVE_DECL_BASENAME 1
#include <demangle.h>
#endif
struct dl_address_search {
unsigned long address;
const char *filename;
unsigned long base;
};
struct backtrace_file {
struct dl_address_search dl;
bfd *abfd;
asymbol **syms;
};
struct backtrace_line {
unsigned long address;
char *file;
char *function;
unsigned lineno;
};
#define BACKTRACE_MAX 64
struct backtrace {
struct backtrace_line lines[BACKTRACE_MAX];
int size;
int position;
};
typedef struct backtrace backtrace_h;
struct backtrace_search {
int count;
struct backtrace_file *file;
int backoff; /* search the line where the function call
took place, instead of return address */
struct backtrace_line *lines;
int max_lines;
};
static const char *get_exe()
{
static char exe[1024];
int ret;
ret = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
if (ret < 0) {
exe[0] = '\0';
} else {
exe[ret] = '\0';
}
return exe;
}
static int dl_match_address(struct dl_phdr_info *info, size_t size, void *data)
{
struct dl_address_search *dl = (struct dl_address_search *) data;
const ElfW(Phdr) *phdr;
ElfW(Addr) load_base = info->dlpi_addr;
long n;
phdr = info->dlpi_phdr;
for (n = info->dlpi_phnum; --n >= 0; phdr++) {
if (phdr->p_type == PT_LOAD) {
ElfW(Addr) vbaseaddr = phdr->p_vaddr + load_base;
if (dl->address >= vbaseaddr && dl->address < vbaseaddr + phdr->p_memsz) {
dl->filename = info->dlpi_name;
dl->base = info->dlpi_addr;
}
}
}
return 0;
}
static int dl_lookup_address(struct dl_address_search *dl)
{
dl->filename = NULL;
dl->base = 0;
dl_iterate_phdr(dl_match_address, dl);
if (dl->filename == NULL) {
return 0;
}
if (strlen(dl->filename) == 0) {
dl->filename = get_exe();
}
return 1;
}
static int load_file(struct backtrace_file *file)
{
long symcount;
unsigned int size;
char **matching;
file->syms = NULL;
file->abfd = bfd_openr(file->dl.filename, NULL);
if (!file->abfd) {
goto err;
}
if (bfd_check_format(file->abfd, bfd_archive)) {
goto err_close;
}
if (!bfd_check_format_matches(file->abfd, bfd_object, &matching)) {
goto err_close;
}
if ((bfd_get_file_flags(file->abfd) & HAS_SYMS) == 0) {
goto err_close;
}
symcount = bfd_read_minisymbols(file->abfd, 0, (void**)&file->syms, &size);
if (symcount == 0) {
free(file->syms);
symcount = bfd_read_minisymbols(file->abfd, 1, (void**)&file->syms, &size);
}
if (symcount < 0) {
goto err_close;
}
return 1;
err_close:
bfd_close(file->abfd);
err:
return 0;
}
static void unload_file(struct backtrace_file *file)
{
free(file->syms);
bfd_close(file->abfd);
}
static void find_address_in_section(bfd *abfd, asection *section, void *data)
{
struct backtrace_search *search = (backtrace_search *)data;
bfd_size_type size;
bfd_vma vma;
unsigned long address;
const char *filename, *function;
unsigned lineno;
int found;
if ((search->count > 0) || (search->max_lines == 0) ||
#ifdef HAVE_DECL_BFD_GET_SECTION_FLAGS
((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0)) {
#else
((bfd_section_flags(section) & SEC_ALLOC) == 0)) {
#endif
return;
}
address = search->file->dl.address - search->file->dl.base;
#ifdef HAVE_DECL_BFD_GET_SECTION_VMA
vma = bfd_get_section_vma(abfd, section);
#else
vma = bfd_section_vma(section);
#endif
if (address < vma) {
return;
}
#ifdef HAVE_TWO_ARG_BFD_SECTION_SIZE
size = bfd_section_size(abfd, section);
#else
size = bfd_section_size(section);
#endif
if (address >= vma + size) {
return;
}
/* Search in address-1 to get the calling line instead of return address */
found = bfd_find_nearest_line(abfd, section, search->file->syms,
address - vma - search->backoff,
&filename, &function, &lineno);
do {
search->lines[search->count].address = address;
search->lines[search->count].file = strdup(filename ? filename :
"UNKNOWN_FILE");
search->lines[search->count].function = function ?
#ifdef HAVE_CPLUS_DEMANGLE
cplus_demangle(function, 0) : strdup("UNKNOWN_FUNCTION");
#else
strdup(function) : strdup("UNKNOWN_FUNCTION");
#endif
search->lines[search->count].lineno = lineno;
if (search->count == 0) {
/* To get the inliner info, search at the original address */
bfd_find_nearest_line(abfd, section, search->file->syms, address - vma,
&filename, &function, &lineno);
}
++search->count;
found = bfd_find_inliner_info(abfd, &filename, &function, &lineno);
} while (found && (search->count < search->max_lines));
}
static int get_line_info(struct backtrace_file *file, int backoff,
struct backtrace_line *lines, int max)
{
struct backtrace_search search;
search.file = file;
search.backoff = backoff;
search.count = 0;
search.lines = lines;
search.max_lines = max;
bfd_map_over_sections(file->abfd, find_address_in_section, &search);
return search.count;
}
#endif