debuggerd: Show function names in tombstone backtraces

This change enables debuggerd to provide backtraces with function
names in tombstone files and log messages.  It does this by reading
the image file that the address is found in, and parsing the dynamic
symbol table to try to extract the symbol corresponding to the given
address.

This works best when "-Wl,-export-dynamic" is added to the LDFLAGS
of each library and executable, because this will cause all symbols
to be added to the dynamic symbol table.  If this flag is not present,
it will still work, but it will only be able to identify functions
which are part of the external API of the library/executable.

Change-Id: I618baaff9ed9143b7d1a1f302224e9f21d2b0626
This commit is contained in:
Meng Hu 2009-11-05 16:10:50 -06:00 committed by Garmin Android technology group
parent 93ac1559b8
commit ae7b91b680
6 changed files with 227 additions and 8 deletions

View file

@ -5,7 +5,7 @@ ifeq ($(TARGET_ARCH),arm)
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= debuggerd.c getevent.c unwind-arm.c pr-support.c utility.c
LOCAL_SRC_FILES:= debuggerd.c getevent.c unwind-arm.c pr-support.c utility.c symbol_table.c
LOCAL_CFLAGS := -Wall
LOCAL_MODULE := debuggerd

View file

@ -55,7 +55,7 @@ static int logsocket = -1;
/* Log information onto the tombstone */
void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...)
{
char buf[128];
char buf[512];
va_list ap;
va_start(ap, fmt);
@ -98,10 +98,11 @@ mapinfo *parse_maps_line(char *line)
mi->start = strtoul(line, 0, 16);
mi->end = strtoul(line + 9, 0, 16);
/* To be filled in parse_exidx_info if the mapped section starts with
/* To be filled in parse_elf_info if the mapped section starts with
* elf_header
*/
mi->exidx_start = mi->exidx_end = 0;
mi->symbols = 0;
mi->next = 0;
strcpy(mi->name, line + 49);
@ -335,7 +336,7 @@ void dump_crash_banner(int tfd, unsigned pid, unsigned tid, int sig)
if(sig) dump_fault_addr(tfd, tid, sig);
}
static void parse_exidx_info(mapinfo *milist, pid_t pid)
static void parse_elf_info(mapinfo *milist, pid_t pid)
{
mapinfo *mi;
for (mi = milist; mi != NULL; mi = mi->next) {
@ -365,6 +366,9 @@ static void parse_exidx_info(mapinfo *milist, pid_t pid)
break;
}
}
/* Try to load symbols from this file */
mi->symbols = symbol_table_create(mi->name);
}
}
}
@ -402,7 +406,7 @@ void dump_crash_report(int tfd, unsigned pid, unsigned tid, bool at_fault)
fclose(fp);
}
parse_exidx_info(milist, tid);
parse_elf_info(milist, tid);
/* If stack unwinder fails, use the default solution to dump the stack
* content.
@ -422,6 +426,7 @@ void dump_crash_report(int tfd, unsigned pid, unsigned tid, bool at_fault)
while(milist) {
mapinfo *next = milist->next;
symbol_table_free(milist->symbols);
free(milist);
milist = next;
}

178
debuggerd/symbol_table.c Normal file
View file

@ -0,0 +1,178 @@
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "symbol_table.h"
#include <linux/elf.h>
// Compare func for qsort
static int qcompar(const void *a, const void *b)
{
return ((struct symbol*)a)->addr - ((struct symbol*)b)->addr;
}
// Compare func for bsearch
static int bcompar(const void *addr, const void *element)
{
struct symbol *symbol = (struct symbol*)element;
if((unsigned int)addr < symbol->addr) {
return -1;
}
if((unsigned int)addr - symbol->addr >= symbol->size) {
return 1;
}
return 0;
}
/*
* Create a symbol table from a given file
*
* Parameters:
* filename - Filename to process
*
* Returns:
* A newly-allocated SymbolTable structure, or NULL if error.
* Free symbol table with symbol_table_free()
*/
struct symbol_table *symbol_table_create(const char *filename)
{
struct symbol_table *table = NULL;
// Open the file, and map it into memory
struct stat sb;
int length;
char *base;
int fd = open(filename, O_RDONLY);
if(fd < 0) {
goto out;
}
fstat(fd, &sb);
length = sb.st_size;
base = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
if(!base) {
goto out_close;
}
// Parse the file header
Elf32_Ehdr *hdr = (Elf32_Ehdr*)base;
Elf32_Shdr *shdr = (Elf32_Shdr*)(base + hdr->e_shoff);
// Search for the dynamic symbols section
int dynsym_idx = -1;
int i;
for(i = 0; i < hdr->e_shnum; i++) {
if(shdr[i].sh_type == SHT_DYNSYM ) {
dynsym_idx = i;
}
}
if(dynsym_idx == -1) {
goto out_unmap;
}
Elf32_Sym *dynsyms = (Elf32_Sym*)(base + shdr[dynsym_idx].sh_offset);
int numsyms = shdr[dynsym_idx].sh_size / shdr[dynsym_idx].sh_entsize;
table = malloc(sizeof(struct symbol_table));
if(!table) {
goto out_unmap;
}
table->num_symbols = 0;
// Iterate through the dynamic symbol table, and count how many symbols
// are actually defined
for(i = 0; i < numsyms; i++) {
if(dynsyms[i].st_shndx != SHN_UNDEF) {
table->num_symbols++;
}
}
int dynstr_idx = shdr[dynsym_idx].sh_link;
char *dynstr = base + shdr[dynstr_idx].sh_offset;
// Now, create an entry in our symbol table structure for each symbol...
table->symbols = malloc(table->num_symbols * sizeof(struct symbol));
if(!table->symbols) {
free(table);
table = NULL;
goto out_unmap;
}
// ...and populate them
int j = 0;
for(i = 0; i < numsyms; i++) {
if(dynsyms[i].st_shndx != SHN_UNDEF) {
table->symbols[j].name = strdup(dynstr + dynsyms[i].st_name);
table->symbols[j].addr = dynsyms[i].st_value;
table->symbols[j].size = dynsyms[i].st_size;
j++;
}
}
// Sort the symbol table entries, so they can be bsearched later
qsort(table->symbols, table->num_symbols, sizeof(struct symbol), qcompar);
out_unmap:
munmap(base, length);
out_close:
close(fd);
out:
return table;
}
/*
* Free a symbol table
*
* Parameters:
* table - Table to free
*/
void symbol_table_free(struct symbol_table *table)
{
int i;
if(!table) {
return;
}
for(i=0; i<table->num_symbols; i++) {
free(table->symbols[i].name);
}
free(table->symbols);
free(table);
}
/*
* Search for an address in the symbol table
*
* Parameters:
* table - Table to search in
* addr - Address to search for.
*
* Returns:
* A pointer to the Symbol structure corresponding to the
* symbol which contains this address, or NULL if no symbol
* contains it.
*/
const struct symbol *symbol_table_lookup(struct symbol_table *table, unsigned int addr)
{
if(!table) {
return NULL;
}
return bsearch((void*)addr, table->symbols, table->num_symbols, sizeof(struct symbol), bcompar);
}

19
debuggerd/symbol_table.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef SYMBOL_TABLE_H
#define SYMBOL_TABLE_H
struct symbol {
unsigned int addr;
unsigned int size;
char *name;
};
struct symbol_table {
struct symbol *symbols;
int num_symbols;
};
struct symbol_table *symbol_table_create(const char *filename);
void symbol_table_free(struct symbol_table *table);
const struct symbol *symbol_table_lookup(struct symbol_table *table, unsigned int addr);
#endif

View file

@ -37,6 +37,8 @@
#include <unwind.h>
#include "utility.h"
#include "symbol_table.h"
typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */
void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp);
@ -393,6 +395,7 @@ static _Unwind_Reason_Code log_function(_Unwind_Context *context, pid_t pid,
phase2_vrs *vrs = (phase2_vrs*) context;
const mapinfo *mi;
bool only_in_tombstone = !at_fault;
const struct symbol* sym = 0;
if (stack_level < STACK_CONTENT_DEPTH) {
sp_list[stack_level] = vrs->core.r[R_SP];
@ -451,9 +454,20 @@ static _Unwind_Reason_Code log_function(_Unwind_Context *context, pid_t pid,
rel_pc = pc;
mi = pc_to_mapinfo(map, pc, &rel_pc);
_LOG(tfd, only_in_tombstone,
" #%02d pc %08x %s\n", stack_level, rel_pc,
mi ? mi->name : "");
/* See if we can determine what symbol this stack frame resides in */
if (mi != 0 && mi->symbols != 0) {
sym = symbol_table_lookup(mi->symbols, rel_pc);
}
if (sym) {
_LOG(tfd, only_in_tombstone,
" #%02d pc %08x %s (%s)\n", stack_level, rel_pc,
mi ? mi->name : "", sym->name);
} else {
_LOG(tfd, only_in_tombstone,
" #%02d pc %08x %s\n", stack_level, rel_pc,
mi ? mi->name : "");
}
return _URC_NO_REASON;
}

View file

@ -21,6 +21,8 @@
#include <stddef.h>
#include <stdbool.h>
#include "symbol_table.h"
#ifndef PT_ARM_EXIDX
#define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */
#endif
@ -33,6 +35,7 @@ typedef struct mapinfo {
unsigned end;
unsigned exidx_start;
unsigned exidx_end;
struct symbol_table *symbols;
char name[];
} mapinfo;