Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 50257cf1 authored by Meng Hu's avatar Meng Hu Committed by David 'Digit' Turner
Browse files

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.

Orig-Change-Id: I618baaff9ed9143b7d1a1f302224e9f21d2b0626
parent 1468cb54
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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

+9 −4
Original line number Diff line number Diff line
@@ -63,7 +63,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);
@@ -106,10 +106,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);

@@ -399,7 +400,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) {
@@ -429,6 +430,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);
        }
    }
}
@@ -466,7 +470,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.
@@ -485,6 +489,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 −0
Original line number Diff line number Diff line
#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 −0
Original line number Diff line number Diff line
#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
+17 −3
Original line number Diff line number Diff line
@@ -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);

    /* 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;
}
Loading