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:
parent
93ac1559b8
commit
ae7b91b680
6 changed files with 227 additions and 8 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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
178
debuggerd/symbol_table.c
Normal 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
19
debuggerd/symbol_table.h
Normal 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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue