diff options
author | Jeff Brown <jeffbrown@google.com> | 2011-10-21 12:14:56 -0700 |
---|---|---|
committer | Jeff Brown <jeffbrown@google.com> | 2011-10-22 16:43:00 -0700 |
commit | 13e715b491e876865e752a3a69dd6f347049a488 (patch) | |
tree | 1f4c2193ecf40157b01fed7b9ee7d04d7b963dfd | |
parent | 10484a068412613aaf3924f63a0b2f61400c7d1e (diff) | |
download | system_core-13e715b491e876865e752a3a69dd6f347049a488.zip system_core-13e715b491e876865e752a3a69dd6f347049a488.tar.gz system_core-13e715b491e876865e752a3a69dd6f347049a488.tar.bz2 |
Use libcorkscrew in debuggerd.
Change-Id: I5e3645a39d96c808f87075b49111d0262a19a0c8
-rw-r--r-- | debuggerd/Android.mk | 9 | ||||
-rw-r--r-- | debuggerd/arm/machine.c | 332 | ||||
-rw-r--r-- | debuggerd/arm/pr-support.c | 345 | ||||
-rw-r--r-- | debuggerd/arm/unwind.c | 667 | ||||
-rw-r--r-- | debuggerd/debuggerd.c | 403 | ||||
-rw-r--r-- | debuggerd/debuggerd.h | 39 | ||||
-rw-r--r-- | debuggerd/getevent.h | 24 | ||||
-rw-r--r-- | debuggerd/machine.h | 25 | ||||
-rw-r--r-- | debuggerd/symbol_table.c | 240 | ||||
-rw-r--r-- | debuggerd/symbol_table.h | 20 | ||||
-rw-r--r-- | debuggerd/utility.c | 315 | ||||
-rw-r--r-- | debuggerd/utility.h | 75 | ||||
-rw-r--r-- | debuggerd/x86/machine.c | 34 | ||||
-rw-r--r-- | debuggerd/x86/unwind.c | 86 | ||||
-rw-r--r-- | debuggerd/x86/x86_utility.h | 40 |
15 files changed, 543 insertions, 2111 deletions
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk index 6cfe79b..f127363 100644 --- a/debuggerd/Android.mk +++ b/debuggerd/Android.mk @@ -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) diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c index 5055444..891b1ef 100644 --- a/debuggerd/arm/machine.c +++ b/debuggerd/arm/machine.c @@ -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)); - return; - } - if (!signal_has_address(si.si_signo)) +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; - - 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 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"; + if (at_fault && DUMP_MEMORY_FOR_ALL_REGISTERS) { + static const char REG_NAMES[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp"; - 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); - } - } - - if (at_fault) { - show_nearby_maps(tfd, pid, map); - } - - 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]; + _LOG(tfd, false, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); + dump_memory(tfd, tid, addr, at_fault); } - 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 = " "; - } + _LOG(tfd, !at_fault, "\ncode around pc:\n"); + dump_memory(tfd, tid, (uintptr_t)regs.ARM_pc, at_fault); - /* 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; + 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); } - /* 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; - } -} - -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); + } +} diff --git a/debuggerd/arm/pr-support.c b/debuggerd/arm/pr-support.c deleted file mode 100644 index 358d9b7..0000000 --- a/debuggerd/arm/pr-support.c +++ /dev/null @@ -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; -} diff --git a/debuggerd/arm/unwind.c b/debuggerd/arm/unwind.c deleted file mode 100644 index d9600b7..0000000 --- a/debuggerd/arm/unwind.c +++ /dev/null @@ -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); -} diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c index 2acf26d..1599439 100644 --- a/debuggerd/debuggerd.c +++ b/debuggerd/debuggerd.c @@ -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); - } - } -} - -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; + if(sig) { + dump_fault_addr(tfd, tid, sig); } - - /* 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,49 +339,121 @@ 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; - mkdir(TOMBSTONE_DIR, 0755); - chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM); + dump_crash_banner(tfd, pid, tid, signal); - fd = find_and_open_tombstone(); - if (fd < 0) - return need_cleanup; + ptrace_context_t* context = load_ptrace_context(pid); - dump_crash_banner(fd, pid, tid, signal); - dump_crash_report(fd, pid, tid, true); + dump_thread(context, tfd, tid, true); if (wantLogs) { - dump_logs(fd, pid, true); + dump_logs(tfd, 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 (dump_sibling_threads) { + need_cleanup = dump_sibling_thread_report(context, tfd, pid, tid); } + free_ptrace_context(context); + if (wantLogs) { - dump_logs(fd, pid, false); + 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); + + fd = find_and_open_tombstone(); + if (fd < 0) + return need_cleanup; + + 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; } diff --git a/debuggerd/debuggerd.h b/debuggerd/debuggerd.h deleted file mode 100644 index e3cdc7c..0000000 --- a/debuggerd/debuggerd.h +++ /dev/null @@ -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); diff --git a/debuggerd/getevent.h b/debuggerd/getevent.h new file mode 100644 index 0000000..426139d --- /dev/null +++ b/debuggerd/getevent.h @@ -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 diff --git a/debuggerd/machine.h b/debuggerd/machine.h new file mode 100644 index 0000000..f9ca6bd --- /dev/null +++ b/debuggerd/machine.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 diff --git a/debuggerd/symbol_table.c b/debuggerd/symbol_table.c deleted file mode 100644 index 23572a3..0000000 --- a/debuggerd/symbol_table.c +++ /dev/null @@ -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); -} diff --git a/debuggerd/symbol_table.h b/debuggerd/symbol_table.h deleted file mode 100644 index 7f41f91..0000000 --- a/debuggerd/symbol_table.h +++ /dev/null @@ -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 diff --git a/debuggerd/utility.c b/debuggerd/utility.c index 409209c..c0fb13a 100644 --- a/debuggerd/utility.c +++ b/debuggerd/utility.c @@ -15,88 +15,289 @@ ** 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]; + + 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); } +bool signal_has_address(int sig) { + switch (sig) { + case SIGILL: + case SIGFPE: + case SIGSEGV: + case SIGBUS: + return true; + default: + return false; + } +} -/* 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; +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"); - for (i = 0; i+4 <= size; i+=4) { - *(int *)((char *)dst+i) = ptrace(PTRACE_PEEKTEXT, pid, (char *)src+i, NULL); + 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; + } - if (i < size) { - int val; + const map_info_t* mi; + const symbol_t* symbol; + find_symbol_ptrace(context, stack_content, &mi, &symbol); - 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 (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); } } -/* 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; +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; } - mi = mi->next; + 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); } - 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; +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++ = '.'; + } } - return mi; + p += 4; } - mi = mi->next; + *asc_out = '\0'; + _LOG(tfd, !at_fault, " %s %s\n", code_buffer, ascii_buffer); } - 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) -{ - switch (sig) { - case SIGILL: - case SIGFPE: - case SIGSEGV: - case SIGBUS: - return true; - default: - return false; +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"); } } diff --git a/debuggerd/utility.h b/debuggerd/utility.h index 4a935d2..879c8b4 100644 --- a/debuggerd/utility.h +++ b/debuggerd/utility.h @@ -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 diff --git a/debuggerd/x86/machine.c b/debuggerd/x86/machine.c index 9d418cf..57f51c8 100644 --- a/debuggerd/x86/machine.c +++ b/debuggerd/x86/machine.c @@ -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); + } +} + diff --git a/debuggerd/x86/unwind.c b/debuggerd/x86/unwind.c deleted file mode 100644 index 0a7f04c..0000000 --- a/debuggerd/x86/unwind.c +++ /dev/null @@ -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; -} - diff --git a/debuggerd/x86/x86_utility.h b/debuggerd/x86/x86_utility.h deleted file mode 100644 index ac6a885..0000000 --- a/debuggerd/x86/x86_utility.h +++ /dev/null @@ -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; - |