2008-10-31 15:37:58 +00:00
|
|
|
/* cairo-trace - a utility to record and replay calls to the Cairo library.
|
|
|
|
|
*
|
|
|
|
|
* Copyright © 2008 Chris Wilson
|
|
|
|
|
*
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* A less hacky utility to lookup the debug strings for a particular
|
|
|
|
|
* .text address.
|
|
|
|
|
* Derived from backtrace-symbols.c in cairo by Chris Wilson.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
A hacky replacement for backtrace_symbols in glibc
|
|
|
|
|
|
|
|
|
|
backtrace_symbols in glibc looks up symbols using dladdr which is limited
|
|
|
|
|
in the symbols that it sees. libbacktracesymbols opens the executable and
|
|
|
|
|
shared libraries using libbfd and will look up backtrace information using
|
|
|
|
|
the symbol table and the dwarf line information.
|
|
|
|
|
|
|
|
|
|
It may make more sense for this program to use libelf instead of libbfd.
|
|
|
|
|
However, I have not investigated that yet.
|
|
|
|
|
|
|
|
|
|
Derived from addr2line.c from GNU Binutils by Jeff Muizelaar
|
|
|
|
|
|
|
|
|
|
Copyright 2007 Jeff Muizelaar
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* addr2line.c -- convert addresses to line number and function name
|
|
|
|
|
Copyright 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
|
|
|
|
|
Contributed by Ulrich Lauther <Ulrich.Lauther@mchp.siemens.de>
|
|
|
|
|
|
|
|
|
|
This file was part of GNU Binutils.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define true 1
|
|
|
|
|
#define false 0
|
|
|
|
|
|
|
|
|
|
#include "lookup-symbol.h"
|
|
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <link.h>
|
|
|
|
|
#include <string.h>
|
2008-11-18 00:32:54 +00:00
|
|
|
#include <pthread.h>
|
2008-10-31 15:37:58 +00:00
|
|
|
|
2008-11-03 11:30:38 +00:00
|
|
|
#if HAVE_BFD
|
2008-10-31 15:37:58 +00:00
|
|
|
#include <bfd.h>
|
|
|
|
|
#include <libiberty.h>
|
|
|
|
|
|
|
|
|
|
struct symtab {
|
|
|
|
|
bfd *bfd;
|
|
|
|
|
asymbol **syms;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct symbol {
|
|
|
|
|
int found;
|
|
|
|
|
bfd_vma pc;
|
|
|
|
|
struct symtab *symtab;
|
|
|
|
|
const char *filename;
|
|
|
|
|
const char *functionname;
|
|
|
|
|
unsigned int line;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_symtab_fini (struct symtab *symtab)
|
|
|
|
|
{
|
2011-07-30 11:04:16 +02:00
|
|
|
free (symtab->syms);
|
2008-10-31 15:37:58 +00:00
|
|
|
if (symtab->bfd != NULL)
|
|
|
|
|
bfd_close (symtab->bfd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read in the symbol table. */
|
|
|
|
|
static int
|
|
|
|
|
_symtab_init (struct symtab *symtab, const char *filename)
|
|
|
|
|
{
|
|
|
|
|
char **matching;
|
|
|
|
|
long symcount;
|
|
|
|
|
unsigned int size;
|
|
|
|
|
|
|
|
|
|
symtab->bfd = NULL;
|
|
|
|
|
symtab->syms = NULL;
|
|
|
|
|
|
|
|
|
|
symtab->bfd = bfd_openr (filename, NULL);
|
|
|
|
|
if (symtab->bfd == NULL)
|
|
|
|
|
goto BAIL;
|
|
|
|
|
|
|
|
|
|
if (bfd_check_format (symtab->bfd, bfd_archive))
|
|
|
|
|
goto BAIL;
|
|
|
|
|
|
|
|
|
|
if (! bfd_check_format_matches (symtab->bfd, bfd_object, &matching))
|
|
|
|
|
goto BAIL;
|
|
|
|
|
|
|
|
|
|
symcount = bfd_read_minisymbols (symtab->bfd, false, (PTR) &symtab->syms, &size);
|
|
|
|
|
if (symcount == 0) {
|
|
|
|
|
symcount = bfd_read_minisymbols (symtab->bfd, true /* dynamic */ ,
|
|
|
|
|
(PTR) &symtab->syms, &size);
|
|
|
|
|
}
|
|
|
|
|
if (symcount < 0)
|
|
|
|
|
goto BAIL;
|
|
|
|
|
|
|
|
|
|
if ((bfd_get_file_flags (symtab->bfd) & HAS_SYMS) == 0)
|
|
|
|
|
goto BAIL;
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
BAIL:
|
|
|
|
|
_symtab_fini (symtab);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Look for an address in a section.
|
|
|
|
|
* This is called via bfd_map_over_sections.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
find_address_in_section (bfd *abfd,
|
|
|
|
|
asection *section,
|
|
|
|
|
void *data)
|
|
|
|
|
{
|
|
|
|
|
bfd_vma vma;
|
|
|
|
|
bfd_size_type size;
|
|
|
|
|
struct symbol *symbol = data;
|
|
|
|
|
struct symtab *symtab = symbol->symtab;
|
|
|
|
|
|
|
|
|
|
if (symbol->found)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if ((bfd_get_section_flags (symtab->bfd, section) & SEC_ALLOC) == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
vma = bfd_get_section_vma (symtab->bfd, section);
|
|
|
|
|
if (symbol->pc < vma)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
size = bfd_section_size (symtab->bfd, section);
|
|
|
|
|
if (symbol->pc >= vma + size)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
symbol->found = bfd_find_nearest_line (symtab->bfd, section,
|
|
|
|
|
symtab->syms,
|
|
|
|
|
symbol->pc - vma,
|
|
|
|
|
&symbol->filename,
|
|
|
|
|
&symbol->functionname,
|
|
|
|
|
&symbol->line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_symbol_fini (struct symbol *symbol)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_symbol_init (struct symbol *symbol, struct symtab *symtab, bfd_vma addr)
|
|
|
|
|
{
|
|
|
|
|
symbol->found = false;
|
|
|
|
|
symbol->symtab = symtab;
|
|
|
|
|
symbol->pc = addr;
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-13 10:45:15 +00:00
|
|
|
static void
|
2008-10-31 15:37:58 +00:00
|
|
|
_symbol_print (struct symbol *symbol, char *buf, int buflen, const char *filename)
|
|
|
|
|
{
|
2008-11-13 10:45:15 +00:00
|
|
|
const char *name, *h;
|
|
|
|
|
char path[1024];
|
|
|
|
|
|
|
|
|
|
if (! symbol->found)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
name = symbol->functionname;
|
|
|
|
|
if (name == NULL || *name == '\0')
|
|
|
|
|
name = "??";
|
|
|
|
|
|
|
|
|
|
if (symbol->filename != NULL)
|
|
|
|
|
filename = symbol->filename;
|
|
|
|
|
if (strcmp (filename, "/proc/self/exe") == 0) {
|
|
|
|
|
int len = readlink ("/proc/self/exe", path, sizeof (path) - 1);
|
|
|
|
|
if (len != -1) {
|
|
|
|
|
path[len] = '\0';
|
|
|
|
|
filename = path;
|
2008-10-31 15:37:58 +00:00
|
|
|
}
|
|
|
|
|
}
|
2008-11-13 10:45:15 +00:00
|
|
|
h = strrchr (filename, '/');
|
|
|
|
|
if (h != NULL)
|
|
|
|
|
filename = h + 1;
|
2008-10-31 15:37:58 +00:00
|
|
|
|
2008-11-13 10:45:15 +00:00
|
|
|
if (symbol->line) {
|
|
|
|
|
snprintf (buf, buflen, "%s() [%s:%u]",
|
|
|
|
|
name, filename, symbol->line);
|
|
|
|
|
} else {
|
|
|
|
|
snprintf (buf, buflen, "%s() [%s]", name, filename);
|
|
|
|
|
}
|
2008-10-31 15:37:58 +00:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
struct file_match {
|
|
|
|
|
const char *file;
|
|
|
|
|
ElfW(Addr) address;
|
|
|
|
|
ElfW(Addr) base;
|
|
|
|
|
void *hdr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
find_matching_file (struct dl_phdr_info *info, size_t size, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct file_match *match = data;
|
|
|
|
|
/* This code is modeled from Gfind_proc_info-lsb.c:callback() from libunwind */
|
|
|
|
|
long n;
|
|
|
|
|
const ElfW(Phdr) *phdr;
|
|
|
|
|
ElfW(Addr) load_base = info->dlpi_addr;
|
|
|
|
|
|
|
|
|
|
phdr = info->dlpi_phdr;
|
|
|
|
|
for (n = info->dlpi_phnum; --n >= 0; phdr++) {
|
|
|
|
|
if (phdr->p_type == PT_LOAD) {
|
|
|
|
|
ElfW(Addr) vaddr = phdr->p_vaddr + load_base;
|
|
|
|
|
if (match->address >= vaddr &&
|
|
|
|
|
match->address < vaddr + phdr->p_memsz)
|
|
|
|
|
{
|
|
|
|
|
/* we found a match */
|
|
|
|
|
match->file = info->dlpi_name;
|
|
|
|
|
match->base = info->dlpi_addr;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-18 00:32:54 +00:00
|
|
|
struct symbol_cache_entry {
|
|
|
|
|
const void *ptr;
|
|
|
|
|
struct symbol_cache_entry *hash_prev, *hash_next;
|
|
|
|
|
char name[0];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct symbol_cache_entry *symbol_cache_hash[13477];
|
|
|
|
|
static pthread_mutex_t symbol_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
|
2008-10-31 15:37:58 +00:00
|
|
|
char *
|
|
|
|
|
lookup_symbol (char *buf, int buflen, const void *ptr)
|
|
|
|
|
{
|
|
|
|
|
struct file_match match;
|
2008-11-03 11:30:38 +00:00
|
|
|
#if HAVE_BFD
|
2008-10-31 15:37:58 +00:00
|
|
|
struct symtab symtab;
|
|
|
|
|
struct symbol symbol;
|
|
|
|
|
#endif
|
2008-11-18 00:32:54 +00:00
|
|
|
struct symbol_cache_entry *cache;
|
|
|
|
|
int bucket;
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
bucket = (unsigned long) ptr % (sizeof (symbol_cache_hash) / sizeof (symbol_cache_hash[0]));
|
|
|
|
|
pthread_mutex_lock (&symbol_cache_mutex);
|
|
|
|
|
for (cache = symbol_cache_hash[bucket];
|
|
|
|
|
cache != NULL;
|
|
|
|
|
cache = cache->hash_next)
|
|
|
|
|
{
|
|
|
|
|
if (cache->ptr == ptr) {
|
|
|
|
|
if (cache->hash_prev != NULL) {
|
|
|
|
|
cache->hash_prev->hash_next = cache->hash_next;
|
|
|
|
|
if (cache->hash_next != NULL)
|
|
|
|
|
cache->hash_next->hash_prev = cache->hash_prev;
|
|
|
|
|
cache->hash_prev = NULL;
|
|
|
|
|
cache->hash_next = symbol_cache_hash[bucket];
|
|
|
|
|
symbol_cache_hash[bucket]->hash_prev = cache;
|
|
|
|
|
symbol_cache_hash[bucket] = cache;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pthread_mutex_unlock (&symbol_cache_mutex);
|
|
|
|
|
return cache->name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pthread_mutex_unlock (&symbol_cache_mutex);
|
2008-10-31 15:37:58 +00:00
|
|
|
|
2008-11-13 10:45:15 +00:00
|
|
|
match.file = NULL;
|
2008-10-31 15:37:58 +00:00
|
|
|
match.address = (ElfW(Addr)) ptr;
|
|
|
|
|
dl_iterate_phdr (find_matching_file, &match);
|
|
|
|
|
|
2008-11-13 10:45:15 +00:00
|
|
|
snprintf (buf, buflen, "0x%llx",
|
|
|
|
|
(long long unsigned int) match.address);
|
2008-10-31 15:37:58 +00:00
|
|
|
|
|
|
|
|
if (match.file == NULL || *match.file == '\0')
|
|
|
|
|
match.file = "/proc/self/exe";
|
|
|
|
|
|
2008-11-03 11:30:38 +00:00
|
|
|
#if HAVE_BFD
|
2008-11-18 00:32:54 +00:00
|
|
|
if (_symtab_init (&symtab, match.file)) {
|
|
|
|
|
_symbol_init (&symbol, &symtab, match.address - match.base);
|
|
|
|
|
bfd_map_over_sections (symtab.bfd, find_address_in_section, &symbol);
|
|
|
|
|
if (symbol.found)
|
|
|
|
|
_symbol_print (&symbol, buf, buflen, match.file);
|
|
|
|
|
_symbol_fini (&symbol);
|
|
|
|
|
|
|
|
|
|
_symtab_fini (&symtab);
|
|
|
|
|
}
|
2008-10-31 15:37:58 +00:00
|
|
|
#endif
|
|
|
|
|
|
2008-11-18 00:32:54 +00:00
|
|
|
len = strlen (buf);
|
|
|
|
|
cache = malloc (sizeof (struct symbol_cache_entry) + len + 1);
|
|
|
|
|
if (cache != NULL) {
|
|
|
|
|
cache->ptr = ptr;
|
|
|
|
|
memcpy (cache->name, buf, len + 1);
|
|
|
|
|
|
|
|
|
|
pthread_mutex_lock (&symbol_cache_mutex);
|
|
|
|
|
cache->hash_prev = NULL;
|
|
|
|
|
cache->hash_next = symbol_cache_hash[bucket];
|
|
|
|
|
if (symbol_cache_hash[bucket] != NULL)
|
|
|
|
|
symbol_cache_hash[bucket]->hash_prev = cache;
|
|
|
|
|
symbol_cache_hash[bucket] = cache;
|
|
|
|
|
pthread_mutex_unlock (&symbol_cache_mutex);
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-31 15:37:58 +00:00
|
|
|
return buf;
|
|
|
|
|
}
|