Use libcorkscrew in debuggerd.
Change-Id: I5e3645a39d96c808f87075b49111d0262a19a0c8
This commit is contained in:
parent
10484a0684
commit
13e715b491
|
@ -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)
|
||||
|
||||
|
|
|
@ -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, ®s)) {
|
||||
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",
|
||||
®_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", ®_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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
®);
|
||||
_Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32,
|
||||
®);
|
||||
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, ®);
|
||||
if (op & 0x40)
|
||||
reg -= offset;
|
||||
else
|
||||
reg += offset;
|
||||
_Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®);
|
||||
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, ®);
|
||||
_Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®);
|
||||
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,
|
||||
®);
|
||||
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,
|
||||
®);
|
||||
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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
Loading…
Reference in New Issue