Use libcorkscrew in debuggerd.

Change-Id: I5e3645a39d96c808f87075b49111d0262a19a0c8
This commit is contained in:
Jeff Brown 2011-10-21 12:14:56 -07:00
parent 10484a0684
commit 13e715b491
15 changed files with 551 additions and 2119 deletions

View File

@ -5,12 +5,9 @@ ifneq ($(filter arm x86,$(TARGET_ARCH)),)
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= debuggerd.c utility.c getevent.c $(TARGET_ARCH)/machine.c $(TARGET_ARCH)/unwind.c symbol_table.c
ifeq ($(TARGET_ARCH),arm)
LOCAL_SRC_FILES += $(TARGET_ARCH)/pr-support.c
endif
LOCAL_SRC_FILES:= debuggerd.c utility.c getevent.c $(TARGET_ARCH)/machine.c
LOCAL_CFLAGS := -Wall
LOCAL_CFLAGS := -Wall -Werror -std=gnu99
LOCAL_MODULE := debuggerd
ifeq ($(ARCH_ARM_HAVE_VFP),true)
@ -20,7 +17,7 @@ ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
LOCAL_CFLAGS += -DWITH_VFP_D32
endif # ARCH_ARM_HAVE_VFP_D32
LOCAL_STATIC_LIBRARIES := libcutils libc
LOCAL_SHARED_LIBRARIES := libcutils libc libcorkscrew
include $(BUILD_EXECUTABLE)

View File

@ -34,10 +34,11 @@
#include <linux/input.h>
#include <linux/user.h>
#include "utility.h"
#include "../utility.h"
#include "../machine.h"
/* enable to dump memory pointed to by every register */
#define DUMP_MEM_FOR_ALL_REGS 1
#define DUMP_MEMORY_FOR_ALL_REGISTERS 1
#ifdef WITH_VFP
#ifdef WITH_VFP_D32
@ -47,172 +48,22 @@
#endif
#endif
/* Main entry point to get the backtrace from the crashing process */
extern int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map,
unsigned int sp_list[],
int *frame0_pc_sane,
bool at_fault);
/*
* If this isn't clearly a null pointer dereference, dump the
* /proc/maps entries near the fault address.
*
* This only makes sense to do on the thread that crashed.
* If configured to do so, dump memory around *all* registers
* for the crashing thread.
*/
static void show_nearby_maps(int tfd, int pid, mapinfo *map)
{
siginfo_t si;
memset(&si, 0, sizeof(si));
if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) {
_LOG(tfd, false, "cannot get siginfo for %d: %s\n",
pid, strerror(errno));
static void dump_memory_and_code(int tfd, pid_t tid, bool at_fault) {
struct pt_regs regs;
if(ptrace(PTRACE_GETREGS, tid, 0, &regs)) {
return;
}
if (!signal_has_address(si.si_signo))
return;
uintptr_t addr = (uintptr_t) si.si_addr;
addr &= ~0xfff; /* round to 4K page boundary */
if (addr == 0) /* null-pointer deref */
return;
if (at_fault && DUMP_MEMORY_FOR_ALL_REGISTERS) {
static const char REG_NAMES[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp";
_LOG(tfd, false, "\nmemory map around addr %08x:\n", si.si_addr);
/*
* Search for a match, or for a hole where the match would be. The list
* is backward from the file content, so it starts at high addresses.
*/
bool found = false;
mapinfo *next = NULL;
mapinfo *prev = NULL;
while (map != NULL) {
if (addr >= map->start && addr < map->end) {
found = true;
next = map->next;
break;
} else if (addr >= map->end) {
/* map would be between "prev" and this entry */
next = map;
map = NULL;
break;
}
prev = map;
map = map->next;
}
/*
* Show "next" then "match" then "prev" so that the addresses appear in
* ascending order (like /proc/pid/maps).
*/
if (next != NULL) {
_LOG(tfd, false, "%08x-%08x %s\n", next->start, next->end, next->name);
} else {
_LOG(tfd, false, "(no map below)\n");
}
if (map != NULL) {
_LOG(tfd, false, "%08x-%08x %s\n", map->start, map->end, map->name);
} else {
_LOG(tfd, false, "(no map for address)\n");
}
if (prev != NULL) {
_LOG(tfd, false, "%08x-%08x %s\n", prev->start, prev->end, prev->name);
} else {
_LOG(tfd, false, "(no map above)\n");
}
}
/*
* Dumps a few bytes of memory, starting a bit before and ending a bit
* after the specified address.
*/
static void dump_memory(int tfd, int pid, uintptr_t addr,
bool only_in_tombstone)
{
char code_buffer[64]; /* actual 8+1+((8+1)*4) + 1 == 45 */
char ascii_buffer[32]; /* actual 16 + 1 == 17 */
uintptr_t p, end;
p = addr & ~3;
p -= 32;
if (p > addr) {
/* catch underflow */
p = 0;
}
end = p + 80;
/* catch overflow; 'end - p' has to be multiples of 16 */
while (end < p)
end -= 16;
/* Dump the code around PC as:
* addr contents ascii
* 00008d34 ef000000 e8bd0090 e1b00000 512fff1e ............../Q
* 00008d44 ea00b1f9 e92d0090 e3a070fc ef000000 ......-..p......
*/
while (p < end) {
char* asc_out = ascii_buffer;
sprintf(code_buffer, "%08x ", p);
int i;
for (i = 0; i < 4; i++) {
/*
* If we see (data == -1 && errno != 0), we know that the ptrace
* call failed, probably because we're dumping memory in an
* unmapped or inaccessible page. I don't know if there's
* value in making that explicit in the output -- it likely
* just complicates parsing and clarifies nothing for the
* enlightened reader.
*/
long data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL);
sprintf(code_buffer + strlen(code_buffer), "%08lx ", data);
int j;
for (j = 0; j < 4; j++) {
/*
* Our isprint() allows high-ASCII characters that display
* differently (often badly) in different viewers, so we
* just use a simpler test.
*/
char val = (data >> (j*8)) & 0xff;
if (val >= 0x20 && val < 0x7f) {
*asc_out++ = val;
} else {
*asc_out++ = '.';
}
}
p += 4;
}
*asc_out = '\0';
_LOG(tfd, only_in_tombstone, "%s %s\n", code_buffer, ascii_buffer);
}
}
void dump_stack_and_code(int tfd, int pid, mapinfo *map,
int unwind_depth, unsigned int sp_list[],
bool at_fault)
{
struct pt_regs r;
int sp_depth;
bool only_in_tombstone = !at_fault;
if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return;
if (DUMP_MEM_FOR_ALL_REGS && at_fault) {
/*
* If configured to do so, dump memory around *all* registers
* for the crashing thread.
*
* TODO: remove duplicates.
*/
static const char REG_NAMES[] = "R0R1R2R3R4R5R6R7R8R9SLFPIPSPLRPC";
int reg;
for (reg = 0; reg < 16; reg++) {
for (int reg = 0; reg < 14; reg++) {
/* this may not be a valid way to access, but it'll do for now */
uintptr_t addr = r.uregs[reg];
uintptr_t addr = regs.uregs[reg];
/*
* Don't bother if it looks like a small int or ~= null, or if
@ -222,152 +73,65 @@ void dump_stack_and_code(int tfd, int pid, mapinfo *map,
continue;
}
_LOG(tfd, only_in_tombstone, "\nmem near %.2s:\n",
&REG_NAMES[reg*2]);
dump_memory(tfd, pid, addr, false);
}
} else {
unsigned int pc, lr;
pc = r.ARM_pc;
lr = r.ARM_lr;
_LOG(tfd, only_in_tombstone, "\ncode around pc:\n");
dump_memory(tfd, pid, (uintptr_t) pc, only_in_tombstone);
if (lr != pc) {
_LOG(tfd, only_in_tombstone, "\ncode around lr:\n");
dump_memory(tfd, pid, (uintptr_t) lr, only_in_tombstone);
_LOG(tfd, false, "\nmemory near %.2s:\n", &REG_NAMES[reg * 2]);
dump_memory(tfd, tid, addr, at_fault);
}
}
if (at_fault) {
show_nearby_maps(tfd, pid, map);
}
_LOG(tfd, !at_fault, "\ncode around pc:\n");
dump_memory(tfd, tid, (uintptr_t)regs.ARM_pc, at_fault);
unsigned int p, end;
unsigned int sp = r.ARM_sp;
p = sp - 64;
if (p > sp)
p = 0;
p &= ~3;
if (unwind_depth != 0) {
if (unwind_depth < STACK_CONTENT_DEPTH) {
end = sp_list[unwind_depth-1];
}
else {
end = sp_list[STACK_CONTENT_DEPTH-1];
}
}
else {
end = p + 256;
/* 'end - p' has to be multiples of 4 */
if (end < p)
end = ~7;
}
_LOG(tfd, only_in_tombstone, "\nstack:\n");
/* If the crash is due to PC == 0, there will be two frames that
* have identical SP value.
*/
if (sp_list[0] == sp_list[1]) {
sp_depth = 1;
}
else {
sp_depth = 0;
}
while (p <= end) {
char *prompt;
char level[16];
long data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL);
if (p == sp_list[sp_depth]) {
sprintf(level, "#%02d", sp_depth++);
prompt = level;
}
else {
prompt = " ";
}
/* Print the stack content in the log for the first 3 frames. For the
* rest only print them in the tombstone file.
*/
_LOG(tfd, (sp_depth > 2) || only_in_tombstone,
"%s %08x %08x %s\n", prompt, p, data,
map_to_name(map, data, ""));
p += 4;
}
/* print another 64-byte of stack data after the last frame */
end = p+64;
/* 'end - p' has to be multiples of 4 */
if (end < p)
end = ~7;
while (p <= end) {
long data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL);
_LOG(tfd, (sp_depth > 2) || only_in_tombstone,
" %08x %08x %s\n", p, data,
map_to_name(map, data, ""));
p += 4;
if (regs.ARM_pc != regs.ARM_lr) {
_LOG(tfd, !at_fault, "\ncode around lr:\n");
dump_memory(tfd, tid, (uintptr_t)regs.ARM_lr, at_fault);
}
}
void dump_pc_and_lr(int tfd, int pid, mapinfo *map, int unwound_level,
bool at_fault)
{
struct pt_regs r;
if(ptrace(PTRACE_GETREGS, pid, 0, &r)) {
_LOG(tfd, !at_fault, "tid %d not responding!\n", pid);
return;
}
if (unwound_level == 0) {
_LOG(tfd, !at_fault, " #%02d pc %08x %s\n", 0, r.ARM_pc,
map_to_name(map, r.ARM_pc, "<unknown>"));
}
_LOG(tfd, !at_fault, " #%02d lr %08x %s\n", 1, r.ARM_lr,
map_to_name(map, r.ARM_lr, "<unknown>"));
}
void dump_registers(int tfd, int pid, bool at_fault)
void dump_registers(ptrace_context_t* context __attribute((unused)),
int tfd, pid_t tid, bool at_fault)
{
struct pt_regs r;
bool only_in_tombstone = !at_fault;
if(ptrace(PTRACE_GETREGS, pid, 0, &r)) {
_LOG(tfd, only_in_tombstone,
"cannot get registers: %s\n", strerror(errno));
if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
_LOG(tfd, only_in_tombstone, "cannot get registers: %s\n", strerror(errno));
return;
}
_LOG(tfd, only_in_tombstone, " r0 %08x r1 %08x r2 %08x r3 %08x\n",
r.ARM_r0, r.ARM_r1, r.ARM_r2, r.ARM_r3);
_LOG(tfd, only_in_tombstone, " r4 %08x r5 %08x r6 %08x r7 %08x\n",
r.ARM_r4, r.ARM_r5, r.ARM_r6, r.ARM_r7);
_LOG(tfd, only_in_tombstone, " r8 %08x r9 %08x 10 %08x fp %08x\n",
r.ARM_r8, r.ARM_r9, r.ARM_r10, r.ARM_fp);
_LOG(tfd, only_in_tombstone,
" ip %08x sp %08x lr %08x pc %08x cpsr %08x\n",
r.ARM_ip, r.ARM_sp, r.ARM_lr, r.ARM_pc, r.ARM_cpsr);
_LOG(tfd, only_in_tombstone, " r0 %08x r1 %08x r2 %08x r3 %08x\n",
(uint32_t)r.ARM_r0, (uint32_t)r.ARM_r1, (uint32_t)r.ARM_r2, (uint32_t)r.ARM_r3);
_LOG(tfd, only_in_tombstone, " r4 %08x r5 %08x r6 %08x r7 %08x\n",
(uint32_t)r.ARM_r4, (uint32_t)r.ARM_r5, (uint32_t)r.ARM_r6, (uint32_t)r.ARM_r7);
_LOG(tfd, only_in_tombstone, " r8 %08x r9 %08x sl %08x fp %08x\n",
(uint32_t)r.ARM_r8, (uint32_t)r.ARM_r9, (uint32_t)r.ARM_r10, (uint32_t)r.ARM_fp);
_LOG(tfd, only_in_tombstone, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n",
(uint32_t)r.ARM_ip, (uint32_t)r.ARM_sp, (uint32_t)r.ARM_lr,
(uint32_t)r.ARM_pc, (uint32_t)r.ARM_cpsr);
#ifdef WITH_VFP
struct user_vfp vfp_regs;
int i;
if(ptrace(PTRACE_GETVFPREGS, pid, 0, &vfp_regs)) {
_LOG(tfd, only_in_tombstone,
"cannot get registers: %s\n", strerror(errno));
if(ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) {
_LOG(tfd, only_in_tombstone, "cannot get registers: %s\n", strerror(errno));
return;
}
for (i = 0; i < NUM_VFP_REGS; i += 2) {
_LOG(tfd, only_in_tombstone,
" d%-2d %016llx d%-2d %016llx\n",
i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]);
_LOG(tfd, only_in_tombstone, " d%-2d %016llx d%-2d %016llx\n",
i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]);
}
_LOG(tfd, only_in_tombstone, " scr %08lx\n\n", vfp_regs.fpscr);
_LOG(tfd, only_in_tombstone, " scr %08lx\n\n", vfp_regs.fpscr);
#endif
}
void dump_thread(ptrace_context_t* context, int tfd, pid_t tid, bool at_fault) {
dump_registers(context, tfd, tid, at_fault);
dump_backtrace_and_stack(context, tfd, tid, at_fault);
if (at_fault) {
dump_memory_and_code(tfd, tid, at_fault);
dump_nearby_maps(context, tfd, tid);
}
}

View File

@ -1,345 +0,0 @@
/* ARM EABI compliant unwinding routines
Copyright (C) 2004, 2005 Free Software Foundation, Inc.
Contributed by Paul Brook
This file 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 2, or (at your option) any
later version.
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file. (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)
This file 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; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
/****************************************************************************
* The functions here are derived from gcc/config/arm/pr-support.c from the
* 4.3.x release. The main changes here involve the use of ptrace to retrieve
* memory/processor states from a remote process.
****************************************************************************/
#include <sys/types.h>
#include <unwind.h>
#include "utility.h"
/* We add a prototype for abort here to avoid creating a dependency on
target headers. */
extern void abort (void);
/* Derived from _Unwind_VRS_Pop to use ptrace */
extern _Unwind_VRS_Result
unwind_VRS_Pop_with_ptrace (_Unwind_Context *context,
_Unwind_VRS_RegClass regclass,
_uw discriminator,
_Unwind_VRS_DataRepresentation representation,
pid_t pid);
typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */
/* Misc constants. */
#define R_IP 12
#define R_SP 13
#define R_LR 14
#define R_PC 15
#define uint32_highbit (((_uw) 1) << 31)
void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp);
/* Unwind descriptors. */
typedef struct
{
_uw16 length;
_uw16 offset;
} EHT16;
typedef struct
{
_uw length;
_uw offset;
} EHT32;
/* Personality routine helper functions. */
#define CODE_FINISH (0xb0)
/* Derived from next_unwind_byte to use ptrace */
/* Return the next byte of unwinding information, or CODE_FINISH if there is
no data remaining. */
static inline _uw8
next_unwind_byte_with_ptrace (__gnu_unwind_state * uws, pid_t pid)
{
_uw8 b;
if (uws->bytes_left == 0)
{
/* Load another word */
if (uws->words_left == 0)
return CODE_FINISH; /* Nothing left. */
uws->words_left--;
uws->data = get_remote_word(pid, uws->next);
uws->next++;
uws->bytes_left = 3;
}
else
uws->bytes_left--;
/* Extract the most significant byte. */
b = (uws->data >> 24) & 0xff;
uws->data <<= 8;
return b;
}
/* Execute the unwinding instructions described by UWS. */
_Unwind_Reason_Code
unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws,
pid_t pid)
{
_uw op;
int set_pc;
_uw reg;
set_pc = 0;
for (;;)
{
op = next_unwind_byte_with_ptrace (uws, pid);
if (op == CODE_FINISH)
{
/* If we haven't already set pc then copy it from lr. */
if (!set_pc)
{
_Unwind_VRS_Get (context, _UVRSC_CORE, R_LR, _UVRSD_UINT32,
&reg);
_Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32,
&reg);
set_pc = 1;
}
/* Drop out of the loop. */
break;
}
if ((op & 0x80) == 0)
{
/* vsp = vsp +- (imm6 << 2 + 4). */
_uw offset;
offset = ((op & 0x3f) << 2) + 4;
_Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
if (op & 0x40)
reg -= offset;
else
reg += offset;
_Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
continue;
}
if ((op & 0xf0) == 0x80)
{
op = (op << 8) | next_unwind_byte_with_ptrace (uws, pid);
if (op == 0x8000)
{
/* Refuse to unwind. */
return _URC_FAILURE;
}
/* Pop r4-r15 under mask. */
op = (op << 4) & 0xfff0;
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op, _UVRSD_UINT32,
pid)
!= _UVRSR_OK)
return _URC_FAILURE;
if (op & (1 << R_PC))
set_pc = 1;
continue;
}
if ((op & 0xf0) == 0x90)
{
op &= 0xf;
if (op == 13 || op == 15)
/* Reserved. */
return _URC_FAILURE;
/* vsp = r[nnnn]. */
_Unwind_VRS_Get (context, _UVRSC_CORE, op, _UVRSD_UINT32, &reg);
_Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
continue;
}
if ((op & 0xf0) == 0xa0)
{
/* Pop r4-r[4+nnn], [lr]. */
_uw mask;
mask = (0xff0 >> (7 - (op & 7))) & 0xff0;
if (op & 8)
mask |= (1 << R_LR);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, mask, _UVRSD_UINT32,
pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
if ((op & 0xf0) == 0xb0)
{
/* op == 0xb0 already handled. */
if (op == 0xb1)
{
op = next_unwind_byte_with_ptrace (uws, pid);
if (op == 0 || ((op & 0xf0) != 0))
/* Spare. */
return _URC_FAILURE;
/* Pop r0-r4 under mask. */
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op,
_UVRSD_UINT32, pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
if (op == 0xb2)
{
/* vsp = vsp + 0x204 + (uleb128 << 2). */
int shift;
_Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32,
&reg);
op = next_unwind_byte_with_ptrace (uws, pid);
shift = 2;
while (op & 0x80)
{
reg += ((op & 0x7f) << shift);
shift += 7;
op = next_unwind_byte_with_ptrace (uws, pid);
}
reg += ((op & 0x7f) << shift) + 0x204;
_Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32,
&reg);
continue;
}
if (op == 0xb3)
{
/* Pop VFP registers with fldmx. */
op = next_unwind_byte_with_ptrace (uws, pid);
op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX,
pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
if ((op & 0xfc) == 0xb4)
{
/* Pop FPA E[4]-E[4+nn]. */
op = 0x40000 | ((op & 3) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX,
pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
/* op & 0xf8 == 0xb8. */
/* Pop VFP D[8]-D[8+nnn] with fldmx. */
op = 0x80000 | ((op & 7) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX, pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
if ((op & 0xf0) == 0xc0)
{
if (op == 0xc6)
{
/* Pop iWMMXt D registers. */
op = next_unwind_byte_with_ptrace (uws, pid);
op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op,
_UVRSD_UINT64, pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
if (op == 0xc7)
{
op = next_unwind_byte_with_ptrace (uws, pid);
if (op == 0 || (op & 0xf0) != 0)
/* Spare. */
return _URC_FAILURE;
/* Pop iWMMXt wCGR{3,2,1,0} under mask. */
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXC, op,
_UVRSD_UINT32, pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
if ((op & 0xf8) == 0xc0)
{
/* Pop iWMMXt wR[10]-wR[10+nnn]. */
op = 0xa0000 | ((op & 0xf) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op,
_UVRSD_UINT64, pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
if (op == 0xc8)
{
#ifndef __VFP_FP__
/* Pop FPA registers. */
op = next_unwind_byte_with_ptrace (uws, pid);
op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX,
pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
#else
/* Pop VFPv3 registers D[16+ssss]-D[16+ssss+cccc] with vldm. */
op = next_unwind_byte_with_ptrace (uws, pid);
op = (((op & 0xf0) + 16) << 12) | ((op & 0xf) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op,
_UVRSD_DOUBLE, pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
#endif
}
if (op == 0xc9)
{
/* Pop VFP registers with fldmd. */
op = next_unwind_byte_with_ptrace (uws, pid);
op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op,
_UVRSD_DOUBLE, pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
/* Spare. */
return _URC_FAILURE;
}
if ((op & 0xf8) == 0xd0)
{
/* Pop VFP D[8]-D[8+nnn] with fldmd. */
op = 0x80000 | ((op & 7) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_DOUBLE,
pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
/* Spare. */
return _URC_FAILURE;
}
return _URC_OK;
}

View File

@ -1,667 +0,0 @@
/* ARM EABI compliant unwinding routines.
Copyright (C) 2004, 2005 Free Software Foundation, Inc.
Contributed by Paul Brook
This file 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 2, or (at your option) any
later version.
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file. (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)
This file 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; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
/****************************************************************************
* The functions here are derived from gcc/config/arm/unwind-arm.c from the
* 4.3.x release. The main changes here involve the use of ptrace to retrieve
* memory/processor states from a remote process.
****************************************************************************/
#include <cutils/logd.h>
#include <sys/ptrace.h>
#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);
bool __attribute__((weak)) __cxa_begin_cleanup(_Unwind_Control_Block *ucbp);
bool __attribute__((weak)) __cxa_type_match(_Unwind_Control_Block *ucbp,
const type_info *rttip,
bool is_reference,
void **matched_object);
/* Misc constants. */
#define R_IP 12
#define R_SP 13
#define R_LR 14
#define R_PC 15
#define EXIDX_CANTUNWIND 1
#define uint32_highbit (((_uw) 1) << 31)
#define UCB_FORCED_STOP_FN(ucbp) ((ucbp)->unwinder_cache.reserved1)
#define UCB_PR_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved2)
#define UCB_SAVED_CALLSITE_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved3)
#define UCB_FORCED_STOP_ARG(ucbp) ((ucbp)->unwinder_cache.reserved4)
struct core_regs
{
_uw r[16];
};
/* We use normal integer types here to avoid the compiler generating
coprocessor instructions. */
struct vfp_regs
{
_uw64 d[16];
_uw pad;
};
struct vfpv3_regs
{
/* Always populated via VSTM, so no need for the "pad" field from
vfp_regs (which is used to store the format word for FSTMX). */
_uw64 d[16];
};
struct fpa_reg
{
_uw w[3];
};
struct fpa_regs
{
struct fpa_reg f[8];
};
struct wmmxd_regs
{
_uw64 wd[16];
};
struct wmmxc_regs
{
_uw wc[4];
};
/* Unwind descriptors. */
typedef struct
{
_uw16 length;
_uw16 offset;
} EHT16;
typedef struct
{
_uw length;
_uw offset;
} EHT32;
/* The ABI specifies that the unwind routines may only use core registers,
except when actually manipulating coprocessor state. This allows
us to write one implementation that works on all platforms by
demand-saving coprocessor registers.
During unwinding we hold the coprocessor state in the actual hardware
registers and allocate demand-save areas for use during phase1
unwinding. */
typedef struct
{
/* The first fields must be the same as a phase2_vrs. */
_uw demand_save_flags;
struct core_regs core;
_uw prev_sp; /* Only valid during forced unwinding. */
struct vfp_regs vfp;
struct vfpv3_regs vfp_regs_16_to_31;
struct fpa_regs fpa;
struct wmmxd_regs wmmxd;
struct wmmxc_regs wmmxc;
} phase1_vrs;
/* This must match the structure created by the assembly wrappers. */
typedef struct
{
_uw demand_save_flags;
struct core_regs core;
} phase2_vrs;
/* An exception index table entry. */
typedef struct __EIT_entry
{
_uw fnoffset;
_uw content;
} __EIT_entry;
/* Derived version to use ptrace */
typedef _Unwind_Reason_Code (*personality_routine_with_ptrace)
(_Unwind_State,
_Unwind_Control_Block *,
_Unwind_Context *,
pid_t);
/* Derived version to use ptrace */
/* ABI defined personality routines. */
static _Unwind_Reason_Code unwind_cpp_pr0_with_ptrace (_Unwind_State,
_Unwind_Control_Block *, _Unwind_Context *, pid_t);
static _Unwind_Reason_Code unwind_cpp_pr1_with_ptrace (_Unwind_State,
_Unwind_Control_Block *, _Unwind_Context *, pid_t);
static _Unwind_Reason_Code unwind_cpp_pr2_with_ptrace (_Unwind_State,
_Unwind_Control_Block *, _Unwind_Context *, pid_t);
/* Execute the unwinding instructions described by UWS. */
extern _Unwind_Reason_Code
unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws,
pid_t pid);
/* Derived version to use ptrace. Only handles core registers. Disregards
* FP and others.
*/
/* ABI defined function to pop registers off the stack. */
_Unwind_VRS_Result unwind_VRS_Pop_with_ptrace (_Unwind_Context *context,
_Unwind_VRS_RegClass regclass,
_uw discriminator,
_Unwind_VRS_DataRepresentation representation,
pid_t pid)
{
phase1_vrs *vrs = (phase1_vrs *) context;
switch (regclass)
{
case _UVRSC_CORE:
{
_uw *ptr;
_uw mask;
int i;
if (representation != _UVRSD_UINT32)
return _UVRSR_FAILED;
mask = discriminator & 0xffff;
ptr = (_uw *) vrs->core.r[R_SP];
/* Pop the requested registers. */
for (i = 0; i < 16; i++)
{
if (mask & (1 << i)) {
vrs->core.r[i] = get_remote_word(pid, ptr);
ptr++;
}
}
/* Writeback the stack pointer value if it wasn't restored. */
if ((mask & (1 << R_SP)) == 0)
vrs->core.r[R_SP] = (_uw) ptr;
}
return _UVRSR_OK;
default:
return _UVRSR_FAILED;
}
}
/* Core unwinding functions. */
/* Calculate the address encoded by a 31-bit self-relative offset at address
P. */
static inline _uw
selfrel_offset31 (const _uw *p, pid_t pid)
{
_uw offset = get_remote_word(pid, (void*)p);
//offset = *p;
/* Sign extend to 32 bits. */
if (offset & (1 << 30))
offset |= 1u << 31;
else
offset &= ~(1u << 31);
return offset + (_uw) p;
}
/* Perform a binary search for RETURN_ADDRESS in TABLE. The table contains
NREC entries. */
static const __EIT_entry *
search_EIT_table (const __EIT_entry * table, int nrec, _uw return_address,
pid_t pid)
{
_uw next_fn;
_uw this_fn;
int n, left, right;
if (nrec == 0)
return (__EIT_entry *) 0;
left = 0;
right = nrec - 1;
while (1)
{
n = (left + right) / 2;
this_fn = selfrel_offset31 (&table[n].fnoffset, pid);
if (n != nrec - 1)
next_fn = selfrel_offset31 (&table[n + 1].fnoffset, pid) - 1;
else
next_fn = (_uw)0 - 1;
if (return_address < this_fn)
{
if (n == left)
return (__EIT_entry *) 0;
right = n - 1;
}
else if (return_address <= next_fn)
return &table[n];
else
left = n + 1;
}
}
/* Find the exception index table eintry for the given address. */
static const __EIT_entry*
get_eitp(_uw return_address, pid_t pid, mapinfo *map, mapinfo **containing_map)
{
const __EIT_entry *eitp = NULL;
int nrec;
mapinfo *mi;
/* The return address is the address of the instruction following the
call instruction (plus one in thumb mode). If this was the last
instruction in the function the address will lie in the following
function. Subtract 2 from the address so that it points within the call
instruction itself. */
if (return_address >= 2)
return_address -= 2;
for (mi = map; mi != NULL; mi = mi->next) {
if (return_address >= mi->start && return_address <= mi->end) break;
}
if (mi) {
if (containing_map) *containing_map = mi;
eitp = (__EIT_entry *) mi->exidx_start;
nrec = (mi->exidx_end - mi->exidx_start)/sizeof(__EIT_entry);
eitp = search_EIT_table (eitp, nrec, return_address, pid);
}
return eitp;
}
/* Find the exception index table eintry for the given address.
Fill in the relevant fields of the UCB.
Returns _URC_FAILURE if an error occurred, _URC_OK on success. */
static _Unwind_Reason_Code
get_eit_entry (_Unwind_Control_Block *ucbp, _uw return_address, pid_t pid,
mapinfo *map, mapinfo **containing_map)
{
const __EIT_entry *eitp;
eitp = get_eitp(return_address, pid, map, containing_map);
if (!eitp)
{
UCB_PR_ADDR (ucbp) = 0;
return _URC_FAILURE;
}
ucbp->pr_cache.fnstart = selfrel_offset31 (&eitp->fnoffset, pid);
_uw eitp_content = get_remote_word(pid, (void *)&eitp->content);
/* Can this frame be unwound at all? */
if (eitp_content == EXIDX_CANTUNWIND)
{
UCB_PR_ADDR (ucbp) = 0;
return _URC_END_OF_STACK;
}
/* Obtain the address of the "real" __EHT_Header word. */
if (eitp_content & uint32_highbit)
{
/* It is immediate data. */
ucbp->pr_cache.ehtp = (_Unwind_EHT_Header *)&eitp->content;
ucbp->pr_cache.additional = 1;
}
else
{
/* The low 31 bits of the content field are a self-relative
offset to an _Unwind_EHT_Entry structure. */
ucbp->pr_cache.ehtp =
(_Unwind_EHT_Header *) selfrel_offset31 (&eitp->content, pid);
ucbp->pr_cache.additional = 0;
}
/* Discover the personality routine address. */
if (get_remote_word(pid, ucbp->pr_cache.ehtp) & (1u << 31))
{
/* One of the predefined standard routines. */
_uw idx = (get_remote_word(pid, ucbp->pr_cache.ehtp) >> 24) & 0xf;
if (idx == 0)
UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr0_with_ptrace;
else if (idx == 1)
UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr1_with_ptrace;
else if (idx == 2)
UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr2_with_ptrace;
else
{ /* Failed */
UCB_PR_ADDR (ucbp) = 0;
return _URC_FAILURE;
}
}
else
{
/* Execute region offset to PR */
UCB_PR_ADDR (ucbp) = selfrel_offset31 (ucbp->pr_cache.ehtp, pid);
/* Since we are unwinding the stack from a different process, it is
* impossible to execute the personality routine in debuggerd. Punt here.
*/
return _URC_FAILURE;
}
return _URC_OK;
}
/* Print out the current call level, pc, and module name in the crash log */
static _Unwind_Reason_Code log_function(_Unwind_Context *context, pid_t pid,
int tfd,
int stack_level,
mapinfo *map,
unsigned int sp_list[],
bool at_fault)
{
_uw pc;
_uw rel_pc;
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];
}
pc = vrs->core.r[R_PC];
// Top level frame
if (stack_level == 0) {
pc &= ~1;
}
// For deeper framers, rollback pc by one instruction
else {
pc = vrs->core.r[R_PC];
/* Thumb mode - need to check whether the bl(x) has long offset or not.
* Examples:
*
* arm blx in the middle of thumb:
* 187ae: 2300 movs r3, #0
* 187b0: f7fe ee1c blx 173ec
* 187b4: 2c00 cmp r4, #0
*
* arm bl in the middle of thumb:
* 187d8: 1c20 adds r0, r4, #0
* 187da: f136 fd15 bl 14f208
* 187de: 2800 cmp r0, #0
*
* pure thumb:
* 18894: 189b adds r3, r3, r2
* 18896: 4798 blx r3
* 18898: b001 add sp, #4
*/
if (pc & 1) {
_uw prev_word;
pc = (pc & ~1);
prev_word = get_remote_word(pid, (char *) pc-4);
// Long offset
if ((prev_word & 0xf0000000) == 0xf0000000 &&
(prev_word & 0x0000e000) == 0x0000e000) {
pc -= 4;
}
else {
pc -= 2;
}
}
else {
pc -= 4;
}
}
/* We used to print the absolute PC in the back trace, and mask out the top
* 3 bits to guesstimate the offset in the .so file. This is not working for
* non-prelinked libraries since the starting offset may not be aligned on
* 1MB boundaries, and the library may be larger than 1MB. So for .so
* addresses we print the relative offset in back trace.
*/
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;
}
/* Derived from __gnu_Unwind_Backtrace to use ptrace */
/* Perform stack backtrace through unwind data. Return the level of stack it
* unwinds.
*/
int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map,
unsigned int sp_list[], int *frame0_pc_sane,
bool at_fault)
{
phase1_vrs saved_vrs;
_Unwind_Reason_Code code = _URC_OK;
struct pt_regs r;
int i;
int stack_level = 0;
_Unwind_Control_Block ucb;
_Unwind_Control_Block *ucbp = &ucb;
if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return 0;
for (i = 0; i < 16; i++) {
saved_vrs.core.r[i] = r.uregs[i];
/*
_LOG(tfd, "r[%d] = 0x%x\n", i, saved_vrs.core.r[i]);
*/
}
/* Set demand-save flags. */
saved_vrs.demand_save_flags = ~(_uw) 0;
/*
* If the app crashes because of calling the weeds, we cannot pass the PC
* to the usual unwinding code as the EXIDX mapping will fail.
* Instead, we simply print out the 0 as the top frame, and resume the
* unwinding process with the value stored in LR.
*/
if (get_eitp(saved_vrs.core.r[R_PC], pid, map, NULL) == NULL) {
*frame0_pc_sane = 0;
log_function ((_Unwind_Context *) &saved_vrs, pid, tfd, stack_level,
map, sp_list, at_fault);
saved_vrs.core.r[R_PC] = saved_vrs.core.r[R_LR];
stack_level++;
}
do {
mapinfo *this_map = NULL;
/* Find the entry for this routine. */
if (get_eit_entry(ucbp, saved_vrs.core.r[R_PC], pid, map, &this_map)
!= _URC_OK) {
/* Uncomment the code below to study why the unwinder failed */
#if 0
/* Shed more debugging info for stack unwinder improvement */
if (this_map) {
_LOG(tfd, 1,
"Relative PC=%#x from %s not contained in EXIDX\n",
saved_vrs.core.r[R_PC] - this_map->start, this_map->name);
}
_LOG(tfd, 1, "PC=%#x SP=%#x\n",
saved_vrs.core.r[R_PC], saved_vrs.core.r[R_SP]);
#endif
code = _URC_FAILURE;
break;
}
/* The dwarf unwinder assumes the context structure holds things
like the function and LSDA pointers. The ARM implementation
caches these in the exception header (UCB). To avoid
rewriting everything we make the virtual IP register point at
the UCB. */
_Unwind_SetGR((_Unwind_Context *)&saved_vrs, 12, (_Unwind_Ptr) ucbp);
/* Call log function. */
if (log_function ((_Unwind_Context *) &saved_vrs, pid, tfd, stack_level,
map, sp_list, at_fault) != _URC_NO_REASON) {
code = _URC_FAILURE;
break;
}
stack_level++;
/* Call the pr to decide what to do. */
code = ((personality_routine_with_ptrace) UCB_PR_ADDR (ucbp))(
_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, ucbp,
(void *) &saved_vrs, pid);
/*
* In theory the unwinding process will stop when the end of stack is
* reached or there is no unwinding information for the code address.
* To add another level of guarantee that the unwinding process
* will terminate we will stop it when the STACK_CONTENT_DEPTH is reached.
*/
} while (code != _URC_END_OF_STACK && code != _URC_FAILURE &&
stack_level < STACK_CONTENT_DEPTH);
return stack_level;
}
/* Derived version to use ptrace */
/* Common implementation for ARM ABI defined personality routines.
ID is the index of the personality routine, other arguments are as defined
by __aeabi_unwind_cpp_pr{0,1,2}. */
static _Unwind_Reason_Code
unwind_pr_common_with_ptrace (_Unwind_State state,
_Unwind_Control_Block *ucbp,
_Unwind_Context *context,
int id,
pid_t pid)
{
__gnu_unwind_state uws;
_uw *data;
int phase2_call_unexpected_after_unwind = 0;
state &= _US_ACTION_MASK;
data = (_uw *) ucbp->pr_cache.ehtp;
uws.data = get_remote_word(pid, data);
data++;
uws.next = data;
if (id == 0)
{
uws.data <<= 8;
uws.words_left = 0;
uws.bytes_left = 3;
}
else
{
uws.words_left = (uws.data >> 16) & 0xff;
uws.data <<= 16;
uws.bytes_left = 2;
data += uws.words_left;
}
/* Restore the saved pointer. */
if (state == _US_UNWIND_FRAME_RESUME)
data = (_uw *) ucbp->cleanup_cache.bitpattern[0];
if ((ucbp->pr_cache.additional & 1) == 0)
{
/* Process descriptors. */
while (get_remote_word(pid, data)) {
/**********************************************************************
* The original code here seems to deal with exceptions that are not
* applicable in our toolchain, thus there is no way to test it for now.
* Instead of leaving it here and causing potential instability in
* debuggerd, we'd better punt here and leave the stack unwound.
* In the future when we discover cases where the stack should be unwound
* further but is not, we can revisit the code here.
**********************************************************************/
return _URC_FAILURE;
}
/* Finished processing this descriptor. */
}
if (unwind_execute_with_ptrace (context, &uws, pid) != _URC_OK)
return _URC_FAILURE;
if (phase2_call_unexpected_after_unwind)
{
/* Enter __cxa_unexpected as if called from the call site. */
_Unwind_SetGR (context, R_LR, _Unwind_GetGR (context, R_PC));
_Unwind_SetGR (context, R_PC, (_uw) &__cxa_call_unexpected);
return _URC_INSTALL_CONTEXT;
}
return _URC_CONTINUE_UNWIND;
}
/* ABI defined personality routine entry points. */
static _Unwind_Reason_Code
unwind_cpp_pr0_with_ptrace (_Unwind_State state,
_Unwind_Control_Block *ucbp,
_Unwind_Context *context,
pid_t pid)
{
return unwind_pr_common_with_ptrace (state, ucbp, context, 0, pid);
}
static _Unwind_Reason_Code
unwind_cpp_pr1_with_ptrace (_Unwind_State state,
_Unwind_Control_Block *ucbp,
_Unwind_Context *context,
pid_t pid)
{
return unwind_pr_common_with_ptrace (state, ucbp, context, 1, pid);
}
static _Unwind_Reason_Code
unwind_cpp_pr2_with_ptrace (_Unwind_State state,
_Unwind_Control_Block *ucbp,
_Unwind_Context *context,
pid_t pid)
{
return unwind_pr_common_with_ptrace (state, ucbp, context, 2, pid);
}

View File

@ -34,77 +34,19 @@
#include <cutils/logger.h>
#include <cutils/properties.h>
#include <corkscrew/backtrace.h>
#include <linux/input.h>
#include <private/android_filesystem_config.h>
#include "debuggerd.h"
#include "getevent.h"
#include "machine.h"
#include "utility.h"
#define ANDROID_LOG_INFO 4
void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...)
__attribute__ ((format(printf, 3, 4)));
/* Log information onto the tombstone */
void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...)
{
char buf[512];
va_list ap;
va_start(ap, fmt);
if (tfd >= 0) {
int len;
vsnprintf(buf, sizeof(buf), fmt, ap);
len = strlen(buf);
if(tfd >= 0) write(tfd, buf, len);
}
if (!in_tombstone_only)
__android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap);
va_end(ap);
}
// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
// 012345678901234567890123456789012345678901234567890123456789
// 0 1 2 3 4 5
mapinfo *parse_maps_line(char *line)
{
mapinfo *mi;
int len = strlen(line);
if (len < 1) return 0; /* not expected */
line[--len] = 0;
if (len < 50) {
mi = malloc(sizeof(mapinfo) + 1);
} else {
mi = malloc(sizeof(mapinfo) + (len - 47));
}
if (mi == 0) return 0;
mi->isExecutable = (line[20] == 'x');
mi->start = strtoul(line, 0, 16);
mi->end = strtoul(line + 9, 0, 16);
/* 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;
if (len < 50) {
mi->name[0] = '\0';
} else {
strcpy(mi->name, line + 49);
}
return mi;
}
void dump_build_info(int tfd)
static void dump_build_info(int tfd)
{
char fingerprint[PROPERTY_VALUE_MAX];
@ -113,7 +55,7 @@ void dump_build_info(int tfd)
_LOG(tfd, false, "Build fingerprint: '%s'\n", fingerprint);
}
const char *get_signame(int sig)
static const char *get_signame(int sig)
{
switch(sig) {
case SIGILL: return "SIGILL";
@ -126,7 +68,7 @@ const char *get_signame(int sig)
}
}
const char *get_sigcode(int signo, int code)
static const char *get_sigcode(int signo, int code)
{
switch (signo) {
case SIGILL:
@ -170,7 +112,7 @@ const char *get_sigcode(int signo, int code)
return "?";
}
void dump_fault_addr(int tfd, int pid, int sig)
static void dump_fault_addr(int tfd, pid_t pid, int sig)
{
siginfo_t si;
@ -188,7 +130,7 @@ void dump_fault_addr(int tfd, int pid, int sig)
}
}
void dump_crash_banner(int tfd, unsigned pid, unsigned tid, int sig)
static void dump_crash_banner(int tfd, pid_t pid, pid_t tid, int sig)
{
char data[1024];
char *x = 0;
@ -202,187 +144,19 @@ void dump_crash_banner(int tfd, unsigned pid, unsigned tid, int sig)
}
_LOG(tfd, false,
"*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
"*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
dump_build_info(tfd);
_LOG(tfd, false, "pid: %d, tid: %d >>> %s <<<\n",
pid, tid, x ? x : "UNKNOWN");
if(sig) dump_fault_addr(tfd, tid, sig);
}
static void parse_elf_info(mapinfo *milist, pid_t pid)
{
mapinfo *mi;
for (mi = milist; mi != NULL; mi = mi->next) {
if (!mi->isExecutable)
continue;
Elf32_Ehdr ehdr;
memset(&ehdr, 0, sizeof(Elf32_Ehdr));
/* Read in sizeof(Elf32_Ehdr) worth of data from the beginning of
* mapped section.
*/
get_remote_struct(pid, (void *) (mi->start), &ehdr,
sizeof(Elf32_Ehdr));
/* Check if it has the matching magic words */
if (IS_ELF(ehdr)) {
Elf32_Phdr phdr;
Elf32_Phdr *ptr;
int i;
ptr = (Elf32_Phdr *) (mi->start + ehdr.e_phoff);
for (i = 0; i < ehdr.e_phnum; i++) {
/* Parse the program header */
get_remote_struct(pid, (char *) (ptr+i), &phdr,
sizeof(Elf32_Phdr));
#ifdef __arm__
/* Found a EXIDX segment? */
if (phdr.p_type == PT_ARM_EXIDX) {
mi->exidx_start = mi->start + phdr.p_offset;
mi->exidx_end = mi->exidx_start + phdr.p_filesz;
break;
}
#endif
}
/* Try to load symbols from this file */
mi->symbols = symbol_table_create(mi->name);
}
if(sig) {
dump_fault_addr(tfd, tid, sig);
}
}
void dump_crash_report(int tfd, unsigned pid, unsigned tid, bool at_fault)
{
char data[1024];
FILE *fp;
mapinfo *milist = 0;
unsigned int sp_list[STACK_CONTENT_DEPTH];
int stack_depth;
#ifdef __arm__
int frame0_pc_sane = 1;
#endif
if (!at_fault) {
_LOG(tfd, true,
"--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
_LOG(tfd, true, "pid: %d, tid: %d\n", pid, tid);
}
dump_registers(tfd, tid, at_fault);
/* Clear stack pointer records */
memset(sp_list, 0, sizeof(sp_list));
sprintf(data, "/proc/%d/maps", pid);
fp = fopen(data, "r");
if(fp) {
while(fgets(data, 1024, fp)) {
mapinfo *mi = parse_maps_line(data);
if(mi) {
mi->next = milist;
milist = mi;
}
}
fclose(fp);
}
parse_elf_info(milist, tid);
#if __arm__
/* If stack unwinder fails, use the default solution to dump the stack
* content.
*/
stack_depth = unwind_backtrace_with_ptrace(tfd, tid, milist, sp_list,
&frame0_pc_sane, at_fault);
/* The stack unwinder should at least unwind two levels of stack. If less
* level is seen we make sure at lease pc and lr are dumped.
*/
if (stack_depth < 2) {
dump_pc_and_lr(tfd, tid, milist, stack_depth, at_fault);
}
dump_stack_and_code(tfd, tid, milist, stack_depth, sp_list, at_fault);
#elif __i386__
/* If stack unwinder fails, use the default solution to dump the stack
* content.
*/
stack_depth = unwind_backtrace_with_ptrace_x86(tfd, tid, milist,at_fault);
#else
#error "Unsupported architecture"
#endif
while(milist) {
mapinfo *next = milist->next;
symbol_table_free(milist->symbols);
free(milist);
milist = next;
}
}
#define MAX_TOMBSTONES 10
#define typecheck(x,y) { \
typeof(x) __dummy1; \
typeof(y) __dummy2; \
(void)(&__dummy1 == &__dummy2); }
#define TOMBSTONE_DIR "/data/tombstones"
/*
* find_and_open_tombstone - find an available tombstone slot, if any, of the
* form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
* file is available, we reuse the least-recently-modified file.
*/
static int find_and_open_tombstone(void)
{
unsigned long mtime = ULONG_MAX;
struct stat sb;
char path[128];
int fd, i, oldest = 0;
/*
* XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought
* to, our logic breaks. This check will generate a warning if that happens.
*/
typecheck(mtime, sb.st_mtime);
/*
* In a single wolf-like pass, find an available slot and, in case none
* exist, find and record the least-recently-modified file.
*/
for (i = 0; i < MAX_TOMBSTONES; i++) {
snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i);
if (!stat(path, &sb)) {
if (sb.st_mtime < mtime) {
oldest = i;
mtime = sb.st_mtime;
}
continue;
}
if (errno != ENOENT)
continue;
fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600);
if (fd < 0)
continue; /* raced ? */
fchown(fd, AID_SYSTEM, AID_SYSTEM);
return fd;
}
/* we didn't find an available file, so we clobber the oldest one */
snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest);
fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
fchown(fd, AID_SYSTEM, AID_SYSTEM);
return fd;
}
/* Return true if some thread is not detached cleanly */
static bool dump_sibling_thread_report(int tfd, unsigned pid, unsigned tid)
static bool dump_sibling_thread_report(ptrace_context_t* context,
int tfd, pid_t pid, pid_t tid)
{
char task_path[1024];
@ -398,7 +172,7 @@ static bool dump_sibling_thread_report(int tfd, unsigned pid, unsigned tid)
return false;
}
while ((de = readdir(d)) != NULL) {
unsigned new_tid;
pid_t new_tid;
/* Ignore "." and ".." */
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
continue;
@ -411,7 +185,11 @@ static bool dump_sibling_thread_report(int tfd, unsigned pid, unsigned tid)
if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0)
continue;
dump_crash_report(tfd, pid, new_tid, false);
_LOG(tfd, true,
"--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
_LOG(tfd, true, "pid: %d, tid: %d\n", pid, new_tid);
dump_thread(context, tfd, new_tid, false);
if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
XLOG("detach of tid %d failed: %s\n", new_tid, strerror(errno));
@ -429,7 +207,7 @@ static bool dump_sibling_thread_report(int tfd, unsigned pid, unsigned tid)
*
* If "tailOnly" is set, we only print the last few lines.
*/
static void dump_log_file(int tfd, unsigned pid, const char* filename,
static void dump_log_file(int tfd, pid_t pid, const char* filename,
bool tailOnly)
{
bool first = true;
@ -561,23 +339,112 @@ static void dump_log_file(int tfd, unsigned pid, const char* filename,
* Dumps the logs generated by the specified pid to the tombstone, from both
* "system" and "main" log devices. Ideally we'd interleave the output.
*/
static void dump_logs(int tfd, unsigned pid, bool tailOnly)
static void dump_logs(int tfd, pid_t pid, bool tailOnly)
{
dump_log_file(tfd, pid, "/dev/log/system", tailOnly);
dump_log_file(tfd, pid, "/dev/log/main", tailOnly);
}
/* Return true if some thread is not detached cleanly */
static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid,
int signal)
/*
* Dumps all information about the specified pid to the tombstone.
*/
static bool dump_crash(int tfd, pid_t pid, pid_t tid, int signal,
bool dump_sibling_threads)
{
int fd;
bool need_cleanup = false;
/* don't copy log messages to tombstone unless this is a dev device */
char value[PROPERTY_VALUE_MAX];
property_get("ro.debuggable", value, "0");
bool wantLogs = (value[0] == '1');
bool need_cleanup = false;
dump_crash_banner(tfd, pid, tid, signal);
ptrace_context_t* context = load_ptrace_context(pid);
dump_thread(context, tfd, tid, true);
if (wantLogs) {
dump_logs(tfd, pid, true);
}
if (dump_sibling_threads) {
need_cleanup = dump_sibling_thread_report(context, tfd, pid, tid);
}
free_ptrace_context(context);
if (wantLogs) {
dump_logs(tfd, pid, false);
}
return need_cleanup;
}
#define MAX_TOMBSTONES 10
#define typecheck(x,y) { \
typeof(x) __dummy1; \
typeof(y) __dummy2; \
(void)(&__dummy1 == &__dummy2); }
#define TOMBSTONE_DIR "/data/tombstones"
/*
* find_and_open_tombstone - find an available tombstone slot, if any, of the
* form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
* file is available, we reuse the least-recently-modified file.
*/
static int find_and_open_tombstone(void)
{
unsigned long mtime = ULONG_MAX;
struct stat sb;
char path[128];
int fd, i, oldest = 0;
/*
* XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought
* to, our logic breaks. This check will generate a warning if that happens.
*/
typecheck(mtime, sb.st_mtime);
/*
* In a single wolf-like pass, find an available slot and, in case none
* exist, find and record the least-recently-modified file.
*/
for (i = 0; i < MAX_TOMBSTONES; i++) {
snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i);
if (!stat(path, &sb)) {
if (sb.st_mtime < mtime) {
oldest = i;
mtime = sb.st_mtime;
}
continue;
}
if (errno != ENOENT)
continue;
fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600);
if (fd < 0)
continue; /* raced ? */
fchown(fd, AID_SYSTEM, AID_SYSTEM);
return fd;
}
/* we didn't find an available file, so we clobber the oldest one */
snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest);
fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
fchown(fd, AID_SYSTEM, AID_SYSTEM);
return fd;
}
/* Return true if some thread is not detached cleanly */
static bool engrave_tombstone(pid_t pid, pid_t tid, int signal,
bool dump_sibling_threads)
{
int fd;
bool need_cleanup = false;
mkdir(TOMBSTONE_DIR, 0755);
chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM);
@ -586,24 +453,7 @@ static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid,
if (fd < 0)
return need_cleanup;
dump_crash_banner(fd, pid, tid, signal);
dump_crash_report(fd, pid, tid, true);
if (wantLogs) {
dump_logs(fd, pid, true);
}
/*
* If the user has requested to attach gdb, don't collect the per-thread
* information as it increases the chance to lose track of the process.
*/
if ((signed)pid > debug_uid) {
need_cleanup = dump_sibling_thread_report(fd, pid, tid);
}
if (wantLogs) {
dump_logs(fd, pid, false);
}
need_cleanup = dump_crash(fd, pid, tid, signal, dump_sibling_threads);
close(fd);
return need_cleanup;
@ -654,11 +504,7 @@ void disable_debug_led(void)
write_string("/sys/class/leds/left/cadence", "0,0");
}
extern int init_getevent();
extern void uninit_getevent();
extern int get_event(struct input_event* event, int timeout);
static void wait_for_user_action(unsigned tid, struct ucred* cr)
static void wait_for_user_action(pid_t tid, struct ucred* cr)
{
(void)tid;
/* First log a helpful message */
@ -718,19 +564,19 @@ static void handle_crashing_process(int fd)
{
char buf[64];
struct stat s;
unsigned tid;
pid_t tid;
struct ucred cr;
int n, len, status;
int tid_attach_status = -1;
unsigned retry = 30;
bool need_cleanup = false;
XLOG("handle_crashing_process(%d)\n", fd);
char value[PROPERTY_VALUE_MAX];
property_get("debug.db.uid", value, "-1");
int debug_uid = atoi(value);
XLOG("handle_crashing_process(%d)\n", fd);
len = sizeof(cr);
n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
if(n != 0) {
@ -740,7 +586,7 @@ static void handle_crashing_process(int fd)
XLOG("reading tid\n");
fcntl(fd, F_SETFL, O_NONBLOCK);
while((n = read(fd, &tid, sizeof(unsigned))) != sizeof(unsigned)) {
while((n = read(fd, &tid, sizeof(pid_t))) != sizeof(pid_t)) {
if(errno == EINTR) continue;
if(errno == EWOULDBLOCK) {
if(retry-- > 0) {
@ -764,6 +610,12 @@ static void handle_crashing_process(int fd)
XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", cr.pid, cr.uid, cr.gid, tid);
/*
* If the user has requested to attach gdb, don't collect the per-thread
* information as it increases the chance to lose track of the process.
*/
bool dump_sibling_threads = (signed)cr.pid > debug_uid;
/* Note that at this point, the target thread's signal handler
* is blocked in a read() call. This gives us the time to PTRACE_ATTACH
* to it before it has a chance to really fault.
@ -837,7 +689,8 @@ static void handle_crashing_process(int fd)
case SIGSEGV:
case SIGSTKFLT: {
XLOG("stopped -- fatal signal\n");
need_cleanup = engrave_tombstone(cr.pid, tid, debug_uid, n);
need_cleanup = engrave_tombstone(cr.pid, tid, n,
dump_sibling_threads);
kill(tid, SIGSTOP);
goto done;
}

View File

@ -1,39 +0,0 @@
/* system/debuggerd/debuggerd.h
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include <cutils/logd.h>
#include <sys/ptrace.h>
#include <unwind.h>
#include "utility.h"
#include "symbol_table.h"
/* Main entry point to get the backtrace from the crashing process */
extern int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map,
unsigned int sp_list[],
int *frame0_pc_sane,
bool at_fault);
extern void dump_registers(int tfd, int pid, bool at_fault);
extern int unwind_backtrace_with_ptrace_x86(int tfd, pid_t pid, mapinfo *map, bool at_fault);
void dump_pc_and_lr(int tfd, int pid, mapinfo *map, int unwound_level, bool at_fault);
void dump_stack_and_code(int tfd, int pid, mapinfo *map,
int unwind_depth, unsigned int sp_list[],
bool at_fault);

24
debuggerd/getevent.h Normal file
View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _DEBUGGERD_GETEVENT_H
#define _DEBUGGERD_GETEVENT_H
int init_getevent();
void uninit_getevent();
int get_event(struct input_event* event, int timeout);
#endif // _DEBUGGERD_GETEVENT_H

25
debuggerd/machine.h Normal file
View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _DEBUGGERD_MACHINE_H
#define _DEBUGGERD_MACHINE_H
#include <corkscrew/backtrace.h>
#include <sys/types.h>
void dump_thread(ptrace_context_t* context, int tfd, pid_t tid, bool at_fault);
#endif // _DEBUGGERD_MACHINE_H

View File

@ -1,240 +0,0 @@
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "symbol_table.h"
#include "utility.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;
XLOG2("Creating symbol table for %s\n", filename);
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 sym_idx = -1;
int dynsym_idx = -1;
int i;
for(i = 0; i < hdr->e_shnum; i++) {
if(shdr[i].sh_type == SHT_SYMTAB ) {
sym_idx = i;
}
if(shdr[i].sh_type == SHT_DYNSYM ) {
dynsym_idx = i;
}
}
if ((dynsym_idx == -1) && (sym_idx == -1)) {
goto out_unmap;
}
table = malloc(sizeof(struct symbol_table));
if(!table) {
goto out_unmap;
}
table->name = strdup(filename);
table->num_symbols = 0;
Elf32_Sym *dynsyms = NULL;
Elf32_Sym *syms = NULL;
int dynnumsyms = 0;
int numsyms = 0;
char *dynstr = NULL;
char *str = NULL;
if (dynsym_idx != -1) {
dynsyms = (Elf32_Sym*)(base + shdr[dynsym_idx].sh_offset);
dynnumsyms = shdr[dynsym_idx].sh_size / shdr[dynsym_idx].sh_entsize;
int dynstr_idx = shdr[dynsym_idx].sh_link;
dynstr = base + shdr[dynstr_idx].sh_offset;
}
if (sym_idx != -1) {
syms = (Elf32_Sym*)(base + shdr[sym_idx].sh_offset);
numsyms = shdr[sym_idx].sh_size / shdr[sym_idx].sh_entsize;
int str_idx = shdr[sym_idx].sh_link;
str = base + shdr[str_idx].sh_offset;
}
int symbol_count = 0;
int dynsymbol_count = 0;
if (dynsym_idx != -1) {
// Iterate through the dynamic symbol table, and count how many symbols
// are actually defined
for(i = 0; i < dynnumsyms; i++) {
if(dynsyms[i].st_shndx != SHN_UNDEF) {
dynsymbol_count++;
}
}
XLOG2("Dynamic Symbol count: %d\n", dynsymbol_count);
}
if (sym_idx != -1) {
// Iterate through the symbol table, and count how many symbols
// are actually defined
for(i = 0; i < numsyms; i++) {
if((syms[i].st_shndx != SHN_UNDEF) &&
(strlen(str+syms[i].st_name)) &&
(syms[i].st_value != 0) && (syms[i].st_size != 0)) {
symbol_count++;
}
}
XLOG2("Symbol count: %d\n", symbol_count);
}
// Now, create an entry in our symbol table structure for each symbol...
table->num_symbols += symbol_count + dynsymbol_count;
table->symbols = malloc(table->num_symbols * sizeof(struct symbol));
if(!table->symbols) {
free(table);
table = NULL;
goto out_unmap;
}
int j = 0;
if (dynsym_idx != -1) {
// ...and populate them
for(i = 0; i < dynnumsyms; 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;
XLOG2("name: %s, addr: %x, size: %x\n",
table->symbols[j].name, table->symbols[j].addr, table->symbols[j].size);
j++;
}
}
}
if (sym_idx != -1) {
// ...and populate them
for(i = 0; i < numsyms; i++) {
if((syms[i].st_shndx != SHN_UNDEF) &&
(strlen(str+syms[i].st_name)) &&
(syms[i].st_value != 0) && (syms[i].st_size != 0)) {
table->symbols[j].name = strdup(str + syms[i].st_name);
table->symbols[j].addr = syms[i].st_value;
table->symbols[j].size = syms[i].st_size;
XLOG2("name: %s, addr: %x, size: %x\n",
table->symbols[j].name, table->symbols[j].addr, table->symbols[j].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);
}

View File

@ -1,20 +0,0 @@
#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;
char *name;
};
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

@ -15,81 +15,37 @@
** limitations under the License.
*/
#include <sys/ptrace.h>
#include <sys/exec_elf.h>
#include <signal.h>
#include <assert.h>
#include <string.h>
#include <cutils/logd.h>
#include <sys/ptrace.h>
#include <errno.h>
#include <corkscrew/demangle.h>
#include "utility.h"
/* Get a word from pid using ptrace. The result is the return value. */
int get_remote_word(int pid, void *src)
{
return ptrace(PTRACE_PEEKTEXT, pid, src, NULL);
}
#define STACK_DEPTH 32
#define STACK_WORDS 16
void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...) {
char buf[512];
/* Handy routine to read aggregated data from pid using ptrace. The read
* values are written to the dest locations directly.
*/
void get_remote_struct(int pid, void *src, void *dst, size_t size)
{
unsigned int i;
va_list ap;
va_start(ap, fmt);
for (i = 0; i+4 <= size; i+=4) {
*(int *)((char *)dst+i) = ptrace(PTRACE_PEEKTEXT, pid, (char *)src+i, NULL);
if (tfd >= 0) {
int len;
vsnprintf(buf, sizeof(buf), fmt, ap);
len = strlen(buf);
if(tfd >= 0) write(tfd, buf, len);
}
if (i < size) {
int val;
assert((size - i) < 4);
val = ptrace(PTRACE_PEEKTEXT, pid, (char *)src+i, NULL);
while (i < size) {
((unsigned char *)dst)[i] = val & 0xff;
i++;
val >>= 8;
}
}
if (!in_tombstone_only)
__android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap);
va_end(ap);
}
/* Map a pc address to the name of the containing ELF file */
const char *map_to_name(mapinfo *mi, unsigned pc, const char* def)
{
while(mi) {
if((pc >= mi->start) && (pc < mi->end)){
return mi->name;
}
mi = mi->next;
}
return def;
}
/* Find the containing map info for the pc */
const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc, unsigned *rel_pc)
{
*rel_pc = pc;
while(mi) {
if((pc >= mi->start) && (pc < mi->end)){
// Only calculate the relative offset for shared libraries
if (strstr(mi->name, ".so")) {
*rel_pc -= mi->start;
}
return mi;
}
mi = mi->next;
}
return NULL;
}
/*
* Returns true if the specified signal has an associated address (i.e. it
* sets siginfo_t.si_addr).
*/
bool signal_has_address(int sig)
{
bool signal_has_address(int sig) {
switch (sig) {
case SIGILL:
case SIGFPE:
@ -100,3 +56,248 @@ bool signal_has_address(int sig)
return false;
}
}
static void dump_backtrace(ptrace_context_t* context __attribute((unused)),
int tfd, int pid __attribute((unused)), bool at_fault,
const backtrace_frame_t* backtrace, size_t frames) {
_LOG(tfd, !at_fault, "\nbacktrace:\n");
backtrace_symbol_t backtrace_symbols[STACK_DEPTH];
get_backtrace_symbols_ptrace(context, backtrace, frames, backtrace_symbols);
for (size_t i = 0; i < frames; i++) {
const backtrace_symbol_t* symbol = &backtrace_symbols[i];
const char* map_name = symbol->map_info ? symbol->map_info->name : "<unknown>";
const char* symbol_name = symbol->demangled_name ? symbol->demangled_name : symbol->name;
if (symbol_name) {
_LOG(tfd, !at_fault, " #%02d pc %08x %s (%s)\n",
(int)i, symbol->relative_pc, map_name, symbol_name);
} else {
_LOG(tfd, !at_fault, " #%02d pc %08x %s\n",
(int)i, symbol->relative_pc, map_name);
}
}
free_backtrace_symbols(backtrace_symbols, frames);
}
static void dump_stack_segment(ptrace_context_t* context, int tfd, int pid,
bool only_in_tombstone, uintptr_t* sp, size_t words, int label) {
for (size_t i = 0; i < words; i++) {
uint32_t stack_content;
if (!try_get_word(pid, *sp, &stack_content)) {
break;
}
const map_info_t* mi;
const symbol_t* symbol;
find_symbol_ptrace(context, stack_content, &mi, &symbol);
if (symbol) {
char* demangled_name = demangle_symbol_name(symbol->name);
const char* symbol_name = demangled_name ? demangled_name : symbol->name;
if (!i && label >= 0) {
_LOG(tfd, only_in_tombstone, " #%02d %08x %08x %s (%s)\n",
label, *sp, stack_content, mi ? mi->name : "", symbol_name);
} else {
_LOG(tfd, only_in_tombstone, " %08x %08x %s (%s)\n",
*sp, stack_content, mi ? mi->name : "", symbol_name);
}
free(demangled_name);
} else {
if (!i && label >= 0) {
_LOG(tfd, only_in_tombstone, " #%02d %08x %08x %s\n",
label, *sp, stack_content, mi ? mi->name : "");
} else {
_LOG(tfd, only_in_tombstone, " %08x %08x %s\n",
*sp, stack_content, mi ? mi->name : "");
}
}
*sp += sizeof(uint32_t);
}
}
static void dump_stack(ptrace_context_t* context, int tfd, int pid, bool at_fault,
const backtrace_frame_t* backtrace, size_t frames) {
bool have_first = false;
size_t first, last;
for (size_t i = 0; i < frames; i++) {
if (backtrace[i].stack_top) {
if (!have_first) {
have_first = true;
first = i;
}
last = i;
}
}
if (!have_first) {
return;
}
_LOG(tfd, !at_fault, "\nstack:\n");
// Dump a few words before the first frame.
bool only_in_tombstone = !at_fault;
uintptr_t sp = backtrace[first].stack_top - STACK_WORDS * sizeof(uint32_t);
dump_stack_segment(context, tfd, pid, only_in_tombstone, &sp, STACK_WORDS, -1);
// Dump a few words from all successive frames.
// Only log the first 3 frames, put the rest in the tombstone.
for (size_t i = first; i <= last; i++) {
const backtrace_frame_t* frame = &backtrace[i];
if (sp != frame->stack_top) {
_LOG(tfd, only_in_tombstone, " ........ ........\n");
sp = frame->stack_top;
}
if (i - first == 3) {
only_in_tombstone = true;
}
if (i == last) {
dump_stack_segment(context, tfd, pid, only_in_tombstone, &sp, STACK_WORDS, i);
if (sp < frame->stack_top + frame->stack_size) {
_LOG(tfd, only_in_tombstone, " ........ ........\n");
}
} else {
size_t words = frame->stack_size / sizeof(uint32_t);
if (words == 0) {
words = 1;
} else if (words > STACK_WORDS) {
words = STACK_WORDS;
}
dump_stack_segment(context, tfd, pid, only_in_tombstone, &sp, words, i);
}
}
}
void dump_backtrace_and_stack(ptrace_context_t* context, int tfd, pid_t tid, bool at_fault) {
backtrace_frame_t backtrace[STACK_DEPTH];
ssize_t frames = unwind_backtrace_ptrace(tid, context, backtrace, 0, STACK_DEPTH);
if (frames > 0) {
dump_backtrace(context, tfd, tid, at_fault, backtrace, frames);
dump_stack(context, tfd, tid, at_fault, backtrace, frames);
}
}
void dump_memory(int tfd, pid_t tid, uintptr_t addr, bool at_fault) {
char code_buffer[64]; /* actual 8+1+((8+1)*4) + 1 == 45 */
char ascii_buffer[32]; /* actual 16 + 1 == 17 */
uintptr_t p, end;
p = addr & ~3;
p -= 32;
if (p > addr) {
/* catch underflow */
p = 0;
}
end = p + 80;
/* catch overflow; 'end - p' has to be multiples of 16 */
while (end < p)
end -= 16;
/* Dump the code around PC as:
* addr contents ascii
* 00008d34 ef000000 e8bd0090 e1b00000 512fff1e ............../Q
* 00008d44 ea00b1f9 e92d0090 e3a070fc ef000000 ......-..p......
*/
while (p < end) {
char* asc_out = ascii_buffer;
sprintf(code_buffer, "%08x ", p);
int i;
for (i = 0; i < 4; i++) {
/*
* If we see (data == -1 && errno != 0), we know that the ptrace
* call failed, probably because we're dumping memory in an
* unmapped or inaccessible page. I don't know if there's
* value in making that explicit in the output -- it likely
* just complicates parsing and clarifies nothing for the
* enlightened reader.
*/
long data = ptrace(PTRACE_PEEKTEXT, tid, (void*)p, NULL);
sprintf(code_buffer + strlen(code_buffer), "%08lx ", data);
int j;
for (j = 0; j < 4; j++) {
/*
* Our isprint() allows high-ASCII characters that display
* differently (often badly) in different viewers, so we
* just use a simpler test.
*/
char val = (data >> (j*8)) & 0xff;
if (val >= 0x20 && val < 0x7f) {
*asc_out++ = val;
} else {
*asc_out++ = '.';
}
}
p += 4;
}
*asc_out = '\0';
_LOG(tfd, !at_fault, " %s %s\n", code_buffer, ascii_buffer);
}
}
void dump_nearby_maps(ptrace_context_t* context, int tfd, pid_t tid) {
siginfo_t si;
memset(&si, 0, sizeof(si));
if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) {
_LOG(tfd, false, "cannot get siginfo for %d: %s\n",
tid, strerror(errno));
return;
}
if (!signal_has_address(si.si_signo)) {
return;
}
uintptr_t addr = (uintptr_t) si.si_addr;
addr &= ~0xfff; /* round to 4K page boundary */
if (addr == 0) { /* null-pointer deref */
return;
}
_LOG(tfd, false, "\nmemory map around fault addr %08x:\n", (int)si.si_addr);
/*
* Search for a match, or for a hole where the match would be. The list
* is backward from the file content, so it starts at high addresses.
*/
bool found = false;
map_info_t* map = context->map_info_list;
map_info_t *next = NULL;
map_info_t *prev = NULL;
while (map != NULL) {
if (addr >= map->start && addr < map->end) {
found = true;
next = map->next;
break;
} else if (addr >= map->end) {
/* map would be between "prev" and this entry */
next = map;
map = NULL;
break;
}
prev = map;
map = map->next;
}
/*
* Show "next" then "match" then "prev" so that the addresses appear in
* ascending order (like /proc/pid/maps).
*/
if (next != NULL) {
_LOG(tfd, false, " %08x-%08x %s\n", next->start, next->end, next->name);
} else {
_LOG(tfd, false, " (no map below)\n");
}
if (map != NULL) {
_LOG(tfd, false, " %08x-%08x %s\n", map->start, map->end, map->name);
} else {
_LOG(tfd, false, " (no map for address)\n");
}
if (prev != NULL) {
_LOG(tfd, false, " %08x-%08x %s\n", prev->start, prev->end, prev->name);
} else {
_LOG(tfd, false, " (no map above)\n");
}
}

View File

@ -15,50 +15,17 @@
** limitations under the License.
*/
#ifndef __utility_h
#define __utility_h
#ifndef _DEBUGGERD_UTILITY_H
#define _DEBUGGERD_UTILITY_H
#include <stddef.h>
#include <stdbool.h>
#include <sys/types.h>
#include <corkscrew/backtrace.h>
#include "symbol_table.h"
#ifndef PT_ARM_EXIDX
#define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */
#endif
#define STACK_CONTENT_DEPTH 32
typedef struct mapinfo {
struct mapinfo *next;
unsigned start;
unsigned end;
unsigned exidx_start;
unsigned exidx_end;
struct symbol_table *symbols;
bool isExecutable;
char name[];
} mapinfo;
/* Get a word from pid using ptrace. The result is the return value. */
extern int get_remote_word(int pid, void *src);
/* Handy routine to read aggregated data from pid using ptrace. The read
* values are written to the dest locations directly.
*/
extern void get_remote_struct(int pid, void *src, void *dst, size_t size);
/* Find the containing map for the pc */
const mapinfo *pc_to_mapinfo (mapinfo *mi, unsigned pc, unsigned *rel_pc);
/* Map a pc address to the name of the containing ELF file */
const char *map_to_name(mapinfo *mi, unsigned pc, const char* def);
/* Log information onto the tombstone */
extern void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...);
/* Determine whether si_addr is valid for this signal */
bool signal_has_address(int sig);
/* Log information onto the tombstone. */
void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...)
__attribute__ ((format(printf, 3, 4)));
#define LOG(fmt...) _LOG(-1, 0, fmt)
@ -76,4 +43,30 @@ bool signal_has_address(int sig);
#define XLOG2(fmt...) do {} while(0)
#endif
#endif
/*
* Returns true if the specified signal has an associated address.
* (i.e. it sets siginfo_t.si_addr).
*/
bool signal_has_address(int sig);
/*
* Dumps the backtrace and contents of the stack.
*/
void dump_backtrace_and_stack(ptrace_context_t* context, int tfd, pid_t pid, bool at_fault);
/*
* Dumps a few bytes of memory, starting a bit before and ending a bit
* after the specified address.
*/
void dump_memory(int tfd, pid_t tid, uintptr_t addr, bool at_fault);
/*
* If this isn't clearly a null pointer dereference, dump the
* /proc/maps entries near the fault address.
*
* This only makes sense to do on the thread that crashed.
*/
void dump_nearby_maps(ptrace_context_t* context, int tfd, pid_t tid);
#endif // _DEBUGGERD_UTILITY_H

View File

@ -31,31 +31,43 @@
#include <cutils/sockets.h>
#include <cutils/properties.h>
#include <corkscrew/backtrace.h>
#include <corkscrew/ptrace.h>
#include <linux/input.h>
#include "../machine.h"
#include "../utility.h"
#include "x86_utility.h"
void dump_registers(int tfd, int pid, bool at_fault)
{
static void dump_registers(ptrace_context_t* context __attribute((unused)),
int tfd, pid_t pid, bool at_fault) {
struct pt_regs_x86 r;
bool only_in_tombstone = !at_fault;
if(ptrace(PTRACE_GETREGS, pid, 0, &r)) {
_LOG(tfd, only_in_tombstone,
"cannot get registers: %s\n", strerror(errno));
_LOG(tfd, only_in_tombstone, "cannot get registers: %s\n", strerror(errno));
return;
}
//if there is no stack, no print just like arm
//if there is no stack, no print just like arm
if(!r.ebp)
return;
_LOG(tfd, only_in_tombstone, " eax %08x ebx %08x ecx %08x edx %08x\n",
_LOG(tfd, only_in_tombstone, " eax %08x ebx %08x ecx %08x edx %08x\n",
r.eax, r.ebx, r.ecx, r.edx);
_LOG(tfd, only_in_tombstone, " esi %08x edi %08x\n",
_LOG(tfd, only_in_tombstone, " esi %08x edi %08x\n",
r.esi, r.edi);
_LOG(tfd, only_in_tombstone, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n",
_LOG(tfd, only_in_tombstone, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n",
r.xcs, r.xds, r.xes, r.xfs, r.xss);
_LOG(tfd, only_in_tombstone,
" eip %08x ebp %08x esp %08x flags %08x\n",
_LOG(tfd, only_in_tombstone, " eip %08x ebp %08x esp %08x flags %08x\n",
r.eip, r.ebp, r.esp, r.eflags);
}
void dump_thread(ptrace_context_t* context, int tfd, pid_t tid, bool at_fault) {
dump_registers(context, tfd, tid, at_fault);
dump_backtrace_and_stack(context, tfd, tid, at_fault);
if (at_fault) {
dump_nearby_maps(context, tfd, tid);
}
}

View File

@ -1,86 +0,0 @@
#include <cutils/logd.h>
#include <sys/ptrace.h>
#include "../utility.h"
#include "x86_utility.h"
int unwind_backtrace_with_ptrace_x86(int tfd, pid_t pid, mapinfo *map,
bool at_fault)
{
struct pt_regs_x86 r;
unsigned int stack_level = 0;
unsigned int stack_depth = 0;
unsigned int rel_pc;
unsigned int stack_ptr;
unsigned int stack_content;
if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return 0;
unsigned int eip = (unsigned int)r.eip;
unsigned int ebp = (unsigned int)r.ebp;
unsigned int cur_sp = (unsigned int)r.esp;
const mapinfo *mi;
const struct symbol* sym = 0;
//ebp==0, it indicates that the stack is poped to the bottom or there is no stack at all.
while (ebp) {
mi = pc_to_mapinfo(map, eip, &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, !at_fault, " #%02d eip: %08x %s (%s)\n",
stack_level, eip, mi ? mi->name : "", sym->name);
} else {
_LOG(tfd, !at_fault, " #%02d eip: %08x %s\n",
stack_level, eip, mi ? mi->name : "");
}
stack_level++;
if (stack_level >= STACK_DEPTH || eip == 0)
break;
eip = ptrace(PTRACE_PEEKTEXT, pid, (void*)(ebp + 4), NULL);
ebp = ptrace(PTRACE_PEEKTEXT, pid, (void*)ebp, NULL);
}
ebp = (unsigned int)r.ebp;
stack_depth = stack_level;
stack_level = 0;
if (ebp)
_LOG(tfd, !at_fault, "stack: \n");
while (ebp) {
stack_ptr = cur_sp;
while((int)(ebp - stack_ptr) >= 0) {
stack_content = ptrace(PTRACE_PEEKTEXT, pid, (void*)stack_ptr, NULL);
mi = pc_to_mapinfo(map, stack_content, &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, !at_fault, " #%02d %08x %08x %s (%s)\n",
stack_level, stack_ptr, stack_content, mi ? mi->name : "", sym->name);
} else {
_LOG(tfd, !at_fault, " #%02d %08x %08x %s\n",
stack_level, stack_ptr, stack_content, mi ? mi->name : "");
}
stack_ptr = stack_ptr + 4;
//the stack frame may be very deep.
if((int)(stack_ptr - cur_sp) >= STACK_FRAME_DEPTH) {
_LOG(tfd, !at_fault, " ...... ...... \n");
break;
}
}
cur_sp = ebp + 4;
stack_level++;
if (stack_level >= STACK_DEPTH || stack_level >= stack_depth)
break;
ebp = ptrace(PTRACE_PEEKTEXT, pid, (void*)ebp, NULL);
}
return stack_depth;
}

View File

@ -1,40 +0,0 @@
/*
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#define STACK_DEPTH 8
#define STACK_FRAME_DEPTH 64
typedef struct pt_regs_x86 {
long ebx;
long ecx;
long edx;
long esi;
long edi;
long ebp;
long eax;
int xds;
int xes;
int xfs;
int xgs;
long orig_eax;
long eip;
int xcs;
long eflags;
long esp;
int xss;
}pt_regs_x86;