1 /* cairo-trace - a utility to record and replay calls to the Cairo library.
3 * Copyright © 2008 Chris Wilson
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * A less hacky utility to lookup the debug strings for a particular
22 * Derived from backtrace-symbols.c in cairo by Chris Wilson.
26 A hacky replacement for backtrace_symbols in glibc
28 backtrace_symbols in glibc looks up symbols using dladdr which is limited
29 in the symbols that it sees. libbacktracesymbols opens the executable and
30 shared libraries using libbfd and will look up backtrace information using
31 the symbol table and the dwarf line information.
33 It may make more sense for this program to use libelf instead of libbfd.
34 However, I have not investigated that yet.
36 Derived from addr2line.c from GNU Binutils by Jeff Muizelaar
38 Copyright 2007 Jeff Muizelaar
41 /* addr2line.c -- convert addresses to line number and function name
42 Copyright 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
43 Contributed by Ulrich Lauther <Ulrich.Lauther@mchp.siemens.de>
45 This file was part of GNU Binutils.
57 #include "lookup-symbol.h"
68 #include <libiberty.h>
78 struct symtab *symtab;
80 const char *functionname;
86 _symtab_fini (struct symtab *symtab)
89 if (symtab->bfd != NULL)
90 bfd_close (symtab->bfd);
93 /* Read in the symbol table. */
95 _symtab_init (struct symtab *symtab, const char *filename)
104 symtab->bfd = bfd_openr (filename, NULL);
105 if (symtab->bfd == NULL)
108 if (bfd_check_format (symtab->bfd, bfd_archive))
111 if (! bfd_check_format_matches (symtab->bfd, bfd_object, &matching))
114 symcount = bfd_read_minisymbols (symtab->bfd, false, (PTR) &symtab->syms, &size);
116 symcount = bfd_read_minisymbols (symtab->bfd, true /* dynamic */ ,
117 (PTR) &symtab->syms, &size);
122 if ((bfd_get_file_flags (symtab->bfd) & HAS_SYMS) == 0)
128 _symtab_fini (symtab);
132 /* Look for an address in a section.
133 * This is called via bfd_map_over_sections.
136 find_address_in_section (bfd *abfd,
142 struct symbol *symbol = data;
143 struct symtab *symtab = symbol->symtab;
148 if ((bfd_get_section_flags (symtab->bfd, section) & SEC_ALLOC) == 0)
151 vma = bfd_get_section_vma (symtab->bfd, section);
152 if (symbol->pc < vma)
155 size = bfd_section_size (symtab->bfd, section);
156 if (symbol->pc >= vma + size)
159 symbol->found = bfd_find_nearest_line (symtab->bfd, section,
163 &symbol->functionname,
168 _symbol_fini (struct symbol *symbol)
173 _symbol_init (struct symbol *symbol, struct symtab *symtab, bfd_vma addr)
175 symbol->found = false;
176 symbol->symtab = symtab;
181 _symbol_print (struct symbol *symbol, char *buf, int buflen, const char *filename)
183 const char *name, *h;
189 name = symbol->functionname;
190 if (name == NULL || *name == '\0')
193 if (symbol->filename != NULL)
194 filename = symbol->filename;
195 if (strcmp (filename, "/proc/self/exe") == 0) {
196 int len = readlink ("/proc/self/exe", path, sizeof (path) - 1);
202 h = strrchr (filename, '/');
207 snprintf (buf, buflen, "%s() [%s:%u]",
208 name, filename, symbol->line);
210 snprintf (buf, buflen, "%s() [%s]", name, filename);
223 find_matching_file (struct dl_phdr_info *info, size_t size, void *data)
225 struct file_match *match = data;
226 /* This code is modeled from Gfind_proc_info-lsb.c:callback() from libunwind */
228 const ElfW(Phdr) *phdr;
229 ElfW(Addr) load_base = info->dlpi_addr;
231 phdr = info->dlpi_phdr;
232 for (n = info->dlpi_phnum; --n >= 0; phdr++) {
233 if (phdr->p_type == PT_LOAD) {
234 ElfW(Addr) vaddr = phdr->p_vaddr + load_base;
235 if (match->address >= vaddr &&
236 match->address < vaddr + phdr->p_memsz)
238 /* we found a match */
239 match->file = info->dlpi_name;
240 match->base = info->dlpi_addr;
249 struct symbol_cache_entry {
251 struct symbol_cache_entry *hash_prev, *hash_next;
255 static struct symbol_cache_entry *symbol_cache_hash[13477];
256 static pthread_mutex_t symbol_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
259 lookup_symbol (char *buf, int buflen, const void *ptr)
261 struct file_match match;
263 struct symtab symtab;
264 struct symbol symbol;
266 struct symbol_cache_entry *cache;
270 bucket = (unsigned long) ptr % (sizeof (symbol_cache_hash) / sizeof (symbol_cache_hash[0]));
271 pthread_mutex_lock (&symbol_cache_mutex);
272 for (cache = symbol_cache_hash[bucket];
274 cache = cache->hash_next)
276 if (cache->ptr == ptr) {
277 if (cache->hash_prev != NULL) {
278 cache->hash_prev->hash_next = cache->hash_next;
279 if (cache->hash_next != NULL)
280 cache->hash_next->hash_prev = cache->hash_prev;
281 cache->hash_prev = NULL;
282 cache->hash_next = symbol_cache_hash[bucket];
283 symbol_cache_hash[bucket]->hash_prev = cache;
284 symbol_cache_hash[bucket] = cache;
287 pthread_mutex_unlock (&symbol_cache_mutex);
291 pthread_mutex_unlock (&symbol_cache_mutex);
294 match.address = (ElfW(Addr)) ptr;
295 dl_iterate_phdr (find_matching_file, &match);
297 snprintf (buf, buflen, "0x%llx",
298 (long long unsigned int) match.address);
300 if (match.file == NULL || *match.file == '\0')
301 match.file = "/proc/self/exe";
304 if (_symtab_init (&symtab, match.file)) {
305 _symbol_init (&symbol, &symtab, match.address - match.base);
306 bfd_map_over_sections (symtab.bfd, find_address_in_section, &symbol);
308 _symbol_print (&symbol, buf, buflen, match.file);
309 _symbol_fini (&symbol);
311 _symtab_fini (&symtab);
316 cache = malloc (sizeof (struct symbol_cache_entry) + len + 1);
319 memcpy (cache->name, buf, len + 1);
321 pthread_mutex_lock (&symbol_cache_mutex);
322 cache->hash_prev = NULL;
323 cache->hash_next = symbol_cache_hash[bucket];
324 if (symbol_cache_hash[bucket] != NULL)
325 symbol_cache_hash[bucket]->hash_prev = cache;
326 symbol_cache_hash[bucket] = cache;
327 pthread_mutex_unlock (&symbol_cache_mutex);