diff options
-rw-r--r-- | debuggerd/arm/machine.cpp | 273 | ||||
-rw-r--r-- | debuggerd/backtrace.cpp | 182 | ||||
-rw-r--r-- | debuggerd/backtrace.h | 16 | ||||
-rw-r--r-- | debuggerd/debuggerd.cpp | 869 | ||||
-rw-r--r-- | debuggerd/getevent.cpp | 375 | ||||
-rw-r--r-- | debuggerd/mips/machine.cpp | 272 | ||||
-rw-r--r-- | debuggerd/tombstone.cpp | 1156 | ||||
-rw-r--r-- | debuggerd/tombstone.h | 2 | ||||
-rw-r--r-- | debuggerd/utility.cpp | 187 | ||||
-rw-r--r-- | debuggerd/x86/machine.cpp | 55 | ||||
-rw-r--r-- | include/backtrace/Backtrace.h | 8 | ||||
-rw-r--r-- | include/backtrace/backtrace.h | 1 | ||||
-rw-r--r-- | libbacktrace/Backtrace.cpp | 11 | ||||
-rw-r--r-- | libbacktrace/Corkscrew.cpp | 2 | ||||
-rw-r--r-- | libbacktrace/UnwindCurrent.cpp | 1 | ||||
-rw-r--r-- | libbacktrace/UnwindPtrace.cpp | 1 | ||||
-rw-r--r-- | libbacktrace/backtrace_test.cpp | 326 |
17 files changed, 1812 insertions, 1925 deletions
diff --git a/debuggerd/arm/machine.cpp b/debuggerd/arm/machine.cpp index 4ab6026..3fba6db 100644 --- a/debuggerd/arm/machine.cpp +++ b/debuggerd/arm/machine.cpp @@ -1,22 +1,21 @@ -/* system/debuggerd/debuggerd.c -** -** 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. -*/ +/* + * + * 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 <errno.h> -#include <stdbool.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> @@ -28,7 +27,7 @@ #include "../utility.h" #include "../machine.h" -/* enable to dump memory pointed to by every register */ +// enable to dump memory pointed to by every register #define DUMP_MEMORY_FOR_ALL_REGISTERS 1 #ifdef WITH_VFP @@ -40,140 +39,134 @@ #endif static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scope_flags) { - 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; - } - /* Dump more memory content for the crashing thread. */ - end = p + 256; - /* 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); - - /* Enable the following code blob to dump ASCII values */ + 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; + } + // Dump more memory content for the crashing thread. + end = p + 256; + // 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, reinterpret_cast<void*>(p), NULL); + sprintf(code_buffer + strlen(code_buffer), "%08lx ", data); + + // Enable the following code blob to dump ASCII values #if 0 - 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++ = '.'; - } - } -#endif - p += 4; + 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++ = '.'; } - *asc_out = '\0'; - _LOG(log, scope_flags, " %s %s\n", code_buffer, ascii_buffer); + } +#endif + p += 4; } + *asc_out = '\0'; + _LOG(log, scope_flags, " %s %s\n", code_buffer, ascii_buffer); + } } -/* - * If configured to do so, dump memory around *all* registers - * for the crashing thread. - */ +// If configured to do so, dump memory around *all* registers +// for the crashing thread. void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags) { - struct pt_regs regs; - if(ptrace(PTRACE_GETREGS, tid, 0, ®s)) { - return; + struct pt_regs regs; + if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) { + return; + } + + if (IS_AT_FAULT(scope_flags) && DUMP_MEMORY_FOR_ALL_REGISTERS) { + static const char REG_NAMES[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp"; + + 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 = regs.uregs[reg]; + + // Don't bother if it looks like a small int or ~= null, or if + // it's in the kernel area. + if (addr < 4096 || addr >= 0xc0000000) { + continue; + } + + _LOG(log, scope_flags | SCOPE_SENSITIVE, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); + dump_memory(log, tid, addr, scope_flags | SCOPE_SENSITIVE); } + } - if (IS_AT_FAULT(scope_flags) && DUMP_MEMORY_FOR_ALL_REGISTERS) { - static const char REG_NAMES[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp"; + // explicitly allow upload of code dump logging + _LOG(log, scope_flags, "\ncode around pc:\n"); + dump_memory(log, tid, static_cast<uintptr_t>(regs.ARM_pc), scope_flags); - 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 = regs.uregs[reg]; - - /* - * Don't bother if it looks like a small int or ~= null, or if - * it's in the kernel area. - */ - if (addr < 4096 || addr >= 0xc0000000) { - continue; - } - - _LOG(log, scope_flags | SCOPE_SENSITIVE, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); - dump_memory(log, tid, addr, scope_flags | SCOPE_SENSITIVE); - } - } - - /* explicitly allow upload of code dump logging */ - _LOG(log, scope_flags, "\ncode around pc:\n"); - dump_memory(log, tid, (uintptr_t)regs.ARM_pc, scope_flags); - - if (regs.ARM_pc != regs.ARM_lr) { - _LOG(log, scope_flags, "\ncode around lr:\n"); - dump_memory(log, tid, (uintptr_t)regs.ARM_lr, scope_flags); - } + if (regs.ARM_pc != regs.ARM_lr) { + _LOG(log, scope_flags, "\ncode around lr:\n"); + dump_memory(log, tid, static_cast<uintptr_t>(regs.ARM_lr), scope_flags); + } } -void dump_registers(log_t* log, pid_t tid, int scope_flags) -{ - struct pt_regs r; - if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { - _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); - return; - } - - _LOG(log, scope_flags, " 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(log, scope_flags, " 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(log, scope_flags, " 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(log, scope_flags, " 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); +void dump_registers(log_t* log, pid_t tid, int scope_flags) { + struct pt_regs r; + if (ptrace(PTRACE_GETREGS, tid, 0, &r)) { + _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); + return; + } + + _LOG(log, scope_flags, " r0 %08x r1 %08x r2 %08x r3 %08x\n", + static_cast<uint32_t>(r.ARM_r0), static_cast<uint32_t>(r.ARM_r1), + static_cast<uint32_t>(r.ARM_r2), static_cast<uint32_t>(r.ARM_r3)); + _LOG(log, scope_flags, " r4 %08x r5 %08x r6 %08x r7 %08x\n", + static_cast<uint32_t>(r.ARM_r4), static_cast<uint32_t>(r.ARM_r5), + static_cast<uint32_t>(r.ARM_r6), static_cast<uint32_t>(r.ARM_r7)); + _LOG(log, scope_flags, " r8 %08x r9 %08x sl %08x fp %08x\n", + static_cast<uint32_t>(r.ARM_r8), static_cast<uint32_t>(r.ARM_r9), + static_cast<uint32_t>(r.ARM_r10), static_cast<uint32_t>(r.ARM_fp)); + _LOG(log, scope_flags, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", + static_cast<uint32_t>(r.ARM_ip), static_cast<uint32_t>(r.ARM_sp), + static_cast<uint32_t>(r.ARM_lr), static_cast<uint32_t>(r.ARM_pc), + static_cast<uint32_t>(r.ARM_cpsr)); #ifdef WITH_VFP - struct user_vfp vfp_regs; - int i; - - if(ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) { - _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); - return; - } - - for (i = 0; i < NUM_VFP_REGS; i += 2) { - _LOG(log, scope_flags, " d%-2d %016llx d%-2d %016llx\n", - i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]); - } - _LOG(log, scope_flags, " scr %08lx\n", vfp_regs.fpscr); + struct user_vfp vfp_regs; + int i; + + if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) { + _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); + return; + } + + for (i = 0; i < NUM_VFP_REGS; i += 2) { + _LOG(log, scope_flags, " d%-2d %016llx d%-2d %016llx\n", + i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]); + } + _LOG(log, scope_flags, " scr %08lx\n", vfp_regs.fpscr); #endif } diff --git a/debuggerd/backtrace.cpp b/debuggerd/backtrace.cpp index aa7a3c2..d388348 100644 --- a/debuggerd/backtrace.cpp +++ b/debuggerd/backtrace.cpp @@ -15,7 +15,6 @@ */ #include <stddef.h> -#include <stdbool.h> #include <stdlib.h> #include <string.h> #include <stdio.h> @@ -27,121 +26,116 @@ #include <sys/types.h> #include <sys/ptrace.h> -#include <backtrace/backtrace.h> +#include <backtrace/Backtrace.h> +#include <UniquePtr.h> #include "backtrace.h" #include "utility.h" static void dump_process_header(log_t* log, pid_t pid) { - char path[PATH_MAX]; - char procnamebuf[1024]; - char* procname = NULL; - FILE* fp; - - snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); - if ((fp = fopen(path, "r"))) { - procname = fgets(procnamebuf, sizeof(procnamebuf), fp); - fclose(fp); - } - - time_t t = time(NULL); - struct tm tm; - localtime_r(&t, &tm); - char timestr[64]; - strftime(timestr, sizeof(timestr), "%F %T", &tm); - _LOG(log, SCOPE_AT_FAULT, "\n\n----- pid %d at %s -----\n", pid, timestr); - - if (procname) { - _LOG(log, SCOPE_AT_FAULT, "Cmd line: %s\n", procname); - } + char path[PATH_MAX]; + char procnamebuf[1024]; + char* procname = NULL; + FILE* fp; + + snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); + if ((fp = fopen(path, "r"))) { + procname = fgets(procnamebuf, sizeof(procnamebuf), fp); + fclose(fp); + } + + time_t t = time(NULL); + struct tm tm; + localtime_r(&t, &tm); + char timestr[64]; + strftime(timestr, sizeof(timestr), "%F %T", &tm); + _LOG(log, SCOPE_AT_FAULT, "\n\n----- pid %d at %s -----\n", pid, timestr); + + if (procname) { + _LOG(log, SCOPE_AT_FAULT, "Cmd line: %s\n", procname); + } } static void dump_process_footer(log_t* log, pid_t pid) { - _LOG(log, SCOPE_AT_FAULT, "\n----- end %d -----\n", pid); + _LOG(log, SCOPE_AT_FAULT, "\n----- end %d -----\n", pid); } -static void dump_thread(log_t* log, pid_t tid, bool attached, - bool* detach_failed, int* total_sleep_time_usec) { - char path[PATH_MAX]; - char threadnamebuf[1024]; - char* threadname = NULL; - FILE* fp; - - snprintf(path, sizeof(path), "/proc/%d/comm", tid); - if ((fp = fopen(path, "r"))) { - threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp); - fclose(fp); - if (threadname) { - size_t len = strlen(threadname); - if (len && threadname[len - 1] == '\n') { - threadname[len - 1] = '\0'; - } - } +static void dump_thread( + log_t* log, pid_t tid, bool attached, bool* detach_failed, int* total_sleep_time_usec) { + char path[PATH_MAX]; + char threadnamebuf[1024]; + char* threadname = NULL; + FILE* fp; + + snprintf(path, sizeof(path), "/proc/%d/comm", tid); + if ((fp = fopen(path, "r"))) { + threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp); + fclose(fp); + if (threadname) { + size_t len = strlen(threadname); + if (len && threadname[len - 1] == '\n') { + threadname[len - 1] = '\0'; + } } + } - _LOG(log, SCOPE_AT_FAULT, "\n\"%s\" sysTid=%d\n", - threadname ? threadname : "<unknown>", tid); + _LOG(log, SCOPE_AT_FAULT, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid); - if (!attached && ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) { - _LOG(log, SCOPE_AT_FAULT, "Could not attach to thread: %s\n", strerror(errno)); - return; - } + if (!attached && ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) { + _LOG(log, SCOPE_AT_FAULT, "Could not attach to thread: %s\n", strerror(errno)); + return; + } - wait_for_stop(tid, total_sleep_time_usec); + wait_for_stop(tid, total_sleep_time_usec); - backtrace_context_t context; - if (!backtrace_create_context(&context, tid, -1, 0)) { - _LOG(log, SCOPE_AT_FAULT, "Could not create backtrace context.\n"); - } else { - dump_backtrace_to_log(&context, log, SCOPE_AT_FAULT, " "); - backtrace_destroy_context(&context); - } + UniquePtr<Backtrace> backtrace(Backtrace::Create(tid, BACKTRACE_CURRENT_THREAD)); + if (backtrace->Unwind(0)) { + dump_backtrace_to_log(backtrace.get(), log, SCOPE_AT_FAULT, " "); + } - if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) { - LOG("ptrace detach from %d failed: %s\n", tid, strerror(errno)); - *detach_failed = true; - } + if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) { + LOG("ptrace detach from %d failed: %s\n", tid, strerror(errno)); + *detach_failed = true; + } } void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, - int* total_sleep_time_usec) { - log_t log; - log.tfd = fd; - log.amfd = amfd; - log.quiet = true; - - dump_process_header(&log, pid); - dump_thread(&log, tid, true, detach_failed, total_sleep_time_usec); - - char task_path[64]; - snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); - DIR* d = opendir(task_path); - if (d != NULL) { - struct dirent* de = NULL; - while ((de = readdir(d)) != NULL) { - if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { - continue; - } - - char* end; - pid_t new_tid = strtoul(de->d_name, &end, 10); - if (*end || new_tid == tid) { - continue; - } - - dump_thread(&log, new_tid, false, detach_failed, total_sleep_time_usec); - } - closedir(d); + int* total_sleep_time_usec) { + log_t log; + log.tfd = fd; + log.amfd = amfd; + log.quiet = true; + + dump_process_header(&log, pid); + dump_thread(&log, tid, true, detach_failed, total_sleep_time_usec); + + char task_path[64]; + snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); + DIR* d = opendir(task_path); + if (d != NULL) { + struct dirent* de = NULL; + while ((de = readdir(d)) != NULL) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { + continue; + } + + char* end; + pid_t new_tid = strtoul(de->d_name, &end, 10); + if (*end || new_tid == tid) { + continue; + } + + dump_thread(&log, new_tid, false, detach_failed, total_sleep_time_usec); } + closedir(d); + } - dump_process_footer(&log, pid); + dump_process_footer(&log, pid); } -void dump_backtrace_to_log(const backtrace_context_t* context, log_t* log, +void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, int scope_flags, const char* prefix) { - char buf[512]; - for (size_t i = 0; i < context->backtrace->num_frames; i++) { - backtrace_format_frame_data(context, i, buf, sizeof(buf)); - _LOG(log, scope_flags, "%s%s\n", prefix, buf); - } + for (size_t i = 0; i < backtrace->NumFrames(); i++) { + _LOG(log, scope_flags, "%s%s\n", prefix, backtrace->FormatFrameData(i).c_str()); + } } diff --git a/debuggerd/backtrace.h b/debuggerd/backtrace.h index 54a60b2..2ec8afb 100644 --- a/debuggerd/backtrace.h +++ b/debuggerd/backtrace.h @@ -17,21 +17,19 @@ #ifndef _DEBUGGERD_BACKTRACE_H #define _DEBUGGERD_BACKTRACE_H -#include <stddef.h> -#include <stdbool.h> #include <sys/types.h> -#include <backtrace/backtrace.h> - #include "utility.h" -/* Dumps a backtrace using a format similar to what Dalvik uses so that the result - * can be intermixed in a bug report. */ +class Backtrace; + +// Dumps a backtrace using a format similar to what Dalvik uses so that the result +// can be intermixed in a bug report. void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, - int* total_sleep_time_usec); + int* total_sleep_time_usec); /* Dumps the backtrace in the backtrace data structure to the log. */ -void dump_backtrace_to_log(const backtrace_context_t* context, log_t* log, - int scope_flags, const char* prefix); +void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, + int scope_flags, const char* prefix); #endif // _DEBUGGERD_BACKTRACE_H diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp index a9f59c8..de8ba9d 100644 --- a/debuggerd/debuggerd.cpp +++ b/debuggerd/debuggerd.cpp @@ -1,19 +1,18 @@ -/* system/debuggerd/debuggerd.c -** -** 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. -*/ +/* + * 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 <stdio.h> #include <errno.h> @@ -38,8 +37,6 @@ #include <cutils/properties.h> #include <cutils/debugger.h> -#include <corkscrew/backtrace.h> - #include <linux/input.h> #include <private/android_filesystem_config.h> @@ -50,490 +47,468 @@ #include "utility.h" typedef struct { - debugger_action_t action; - pid_t pid, tid; - uid_t uid, gid; - uintptr_t abort_msg_address; + debugger_action_t action; + pid_t pid, tid; + uid_t uid, gid; + uintptr_t abort_msg_address; } debugger_request_t; -static int -write_string(const char* file, const char* string) -{ - int len; - int fd; - ssize_t amt; - fd = open(file, O_RDWR); - len = strlen(string); - if (fd < 0) - return -errno; - amt = write(fd, string, len); - close(fd); - return amt >= 0 ? 0 : -errno; +static int write_string(const char* file, const char* string) { + int len; + int fd; + ssize_t amt; + fd = open(file, O_RDWR); + len = strlen(string); + if (fd < 0) + return -errno; + amt = write(fd, string, len); + close(fd); + return amt >= 0 ? 0 : -errno; } -static -void init_debug_led(void) -{ - // trout leds - write_string("/sys/class/leds/red/brightness", "0"); - write_string("/sys/class/leds/green/brightness", "0"); - write_string("/sys/class/leds/blue/brightness", "0"); - write_string("/sys/class/leds/red/device/blink", "0"); - // sardine leds - write_string("/sys/class/leds/left/cadence", "0,0"); +static void init_debug_led() { + // trout leds + write_string("/sys/class/leds/red/brightness", "0"); + write_string("/sys/class/leds/green/brightness", "0"); + write_string("/sys/class/leds/blue/brightness", "0"); + write_string("/sys/class/leds/red/device/blink", "0"); + // sardine leds + write_string("/sys/class/leds/left/cadence", "0,0"); } -static -void enable_debug_led(void) -{ - // trout leds - write_string("/sys/class/leds/red/brightness", "255"); - // sardine leds - write_string("/sys/class/leds/left/cadence", "1,0"); +static void enable_debug_led() { + // trout leds + write_string("/sys/class/leds/red/brightness", "255"); + // sardine leds + write_string("/sys/class/leds/left/cadence", "1,0"); } -static -void disable_debug_led(void) -{ - // trout leds - write_string("/sys/class/leds/red/brightness", "0"); - // sardine leds - write_string("/sys/class/leds/left/cadence", "0,0"); +static void disable_debug_led() { + // trout leds + write_string("/sys/class/leds/red/brightness", "0"); + // sardine leds + write_string("/sys/class/leds/left/cadence", "0,0"); } static void wait_for_user_action(pid_t pid) { - /* First log a helpful message */ - LOG( "********************************************************\n" - "* Process %d has been suspended while crashing. To\n" - "* attach gdbserver for a gdb connection on port 5039\n" - "* and start gdbclient:\n" - "*\n" - "* gdbclient app_process :5039 %d\n" - "*\n" - "* Wait for gdb to start, then press HOME or VOLUME DOWN key\n" - "* to let the process continue crashing.\n" - "********************************************************\n", - pid, pid); - - /* wait for HOME or VOLUME DOWN key */ - if (init_getevent() == 0) { - int ms = 1200 / 10; - int dit = 1; - int dah = 3*dit; - int _ = -dit; - int ___ = 3*_; - int _______ = 7*_; - const int codes[] = { - dit,_,dit,_,dit,___,dah,_,dah,_,dah,___,dit,_,dit,_,dit,_______ - }; - size_t s = 0; - struct input_event e; - bool done = false; - init_debug_led(); - enable_debug_led(); - do { - int timeout = abs(codes[s]) * ms; - int res = get_event(&e, timeout); - if (res == 0) { - if (e.type == EV_KEY - && (e.code == KEY_HOME || e.code == KEY_VOLUMEDOWN) - && e.value == 0) { - done = true; - } - } else if (res == 1) { - if (++s >= sizeof(codes)/sizeof(*codes)) - s = 0; - if (codes[s] > 0) { - enable_debug_led(); - } else { - disable_debug_led(); - } - } - } while (!done); - uninit_getevent(); - } - - /* don't forget to turn debug led off */ - disable_debug_led(); - LOG("debuggerd resuming process %d", pid); + // First log a helpful message + LOG( "********************************************************\n" + "* Process %d has been suspended while crashing. To\n" + "* attach gdbserver for a gdb connection on port 5039\n" + "* and start gdbclient:\n" + "*\n" + "* gdbclient app_process :5039 %d\n" + "*\n" + "* Wait for gdb to start, then press HOME or VOLUME DOWN key\n" + "* to let the process continue crashing.\n" + "********************************************************\n", + pid, pid); + + // wait for HOME or VOLUME DOWN key + if (init_getevent() == 0) { + int ms = 1200 / 10; + int dit = 1; + int dah = 3*dit; + int _ = -dit; + int ___ = 3*_; + int _______ = 7*_; + const int codes[] = { + dit,_,dit,_,dit,___,dah,_,dah,_,dah,___,dit,_,dit,_,dit,_______ + }; + size_t s = 0; + struct input_event e; + bool done = false; + init_debug_led(); + enable_debug_led(); + do { + int timeout = abs(codes[s]) * ms; + int res = get_event(&e, timeout); + if (res == 0) { + if (e.type == EV_KEY + && (e.code == KEY_HOME || e.code == KEY_VOLUMEDOWN) + && e.value == 0) { + done = true; + } + } else if (res == 1) { + if (++s >= sizeof(codes)/sizeof(*codes)) + s = 0; + if (codes[s] > 0) { + enable_debug_led(); + } else { + disable_debug_led(); + } + } + } while (!done); + uninit_getevent(); + } + + // don't forget to turn debug led off + disable_debug_led(); + LOG("debuggerd resuming process %d", pid); } static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* out_gid) { - char path[64]; - snprintf(path, sizeof(path), "/proc/%d/status", tid); - - FILE* fp = fopen(path, "r"); - if (!fp) { - return -1; - } - - int fields = 0; - char line[1024]; - while (fgets(line, sizeof(line), fp)) { - size_t len = strlen(line); - if (len > 6 && !memcmp(line, "Tgid:\t", 6)) { - *out_pid = atoi(line + 6); - fields |= 1; - } else if (len > 5 && !memcmp(line, "Uid:\t", 5)) { - *out_uid = atoi(line + 5); - fields |= 2; - } else if (len > 5 && !memcmp(line, "Gid:\t", 5)) { - *out_gid = atoi(line + 5); - fields |= 4; - } + char path[64]; + snprintf(path, sizeof(path), "/proc/%d/status", tid); + + FILE* fp = fopen(path, "r"); + if (!fp) { + return -1; + } + + int fields = 0; + char line[1024]; + while (fgets(line, sizeof(line), fp)) { + size_t len = strlen(line); + if (len > 6 && !memcmp(line, "Tgid:\t", 6)) { + *out_pid = atoi(line + 6); + fields |= 1; + } else if (len > 5 && !memcmp(line, "Uid:\t", 5)) { + *out_uid = atoi(line + 5); + fields |= 2; + } else if (len > 5 && !memcmp(line, "Gid:\t", 5)) { + *out_gid = atoi(line + 5); + fields |= 4; } - fclose(fp); - return fields == 7 ? 0 : -1; + } + fclose(fp); + return fields == 7 ? 0 : -1; } static int read_request(int fd, debugger_request_t* out_request) { - struct ucred cr; - int len = sizeof(cr); - int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); - if (status != 0) { - LOG("cannot get credentials\n"); - return -1; - } - - XLOG("reading tid\n"); - fcntl(fd, F_SETFL, O_NONBLOCK); - - struct pollfd pollfds[1]; - pollfds[0].fd = fd; - pollfds[0].events = POLLIN; - pollfds[0].revents = 0; - status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000)); - if (status != 1) { - LOG("timed out reading tid (from pid=%d uid=%d)\n", cr.pid, cr.uid); - return -1; + struct ucred cr; + int len = sizeof(cr); + int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); + if (status != 0) { + LOG("cannot get credentials\n"); + return -1; + } + + XLOG("reading tid\n"); + fcntl(fd, F_SETFL, O_NONBLOCK); + + struct pollfd pollfds[1]; + pollfds[0].fd = fd; + pollfds[0].events = POLLIN; + pollfds[0].revents = 0; + status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000)); + if (status != 1) { + LOG("timed out reading tid (from pid=%d uid=%d)\n", cr.pid, cr.uid); + return -1; + } + + debugger_msg_t msg; + memset(&msg, 0, sizeof(msg)); + status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg))); + if (status < 0) { + LOG("read failure? %s (pid=%d uid=%d)\n", strerror(errno), cr.pid, cr.uid); + return -1; + } + if (status == sizeof(debugger_msg_t)) { + XLOG("crash request of size %d abort_msg_address=%#08x\n", status, msg.abort_msg_address); + } else { + LOG("invalid crash request of size %d (from pid=%d uid=%d)\n", status, cr.pid, cr.uid); + return -1; + } + + out_request->action = msg.action; + out_request->tid = msg.tid; + out_request->pid = cr.pid; + out_request->uid = cr.uid; + out_request->gid = cr.gid; + out_request->abort_msg_address = msg.abort_msg_address; + + if (msg.action == DEBUGGER_ACTION_CRASH) { + // Ensure that the tid reported by the crashing process is valid. + char buf[64]; + struct stat s; + snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid); + if (stat(buf, &s)) { + LOG("tid %d does not exist in pid %d. ignoring debug request\n", + out_request->tid, out_request->pid); + return -1; } - - debugger_msg_t msg; - memset(&msg, 0, sizeof(msg)); - status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg))); - if (status < 0) { - LOG("read failure? %s (pid=%d uid=%d)\n", - strerror(errno), cr.pid, cr.uid); - return -1; - } - if (status == sizeof(debugger_msg_t)) { - XLOG("crash request of size %d abort_msg_address=%#08x\n", status, msg.abort_msg_address); - } else { - LOG("invalid crash request of size %d (from pid=%d uid=%d)\n", - status, cr.pid, cr.uid); - return -1; - } - - out_request->action = msg.action; - out_request->tid = msg.tid; - out_request->pid = cr.pid; - out_request->uid = cr.uid; - out_request->gid = cr.gid; - out_request->abort_msg_address = msg.abort_msg_address; - - if (msg.action == DEBUGGER_ACTION_CRASH) { - /* Ensure that the tid reported by the crashing process is valid. */ - char buf[64]; - struct stat s; - snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid); - if(stat(buf, &s)) { - LOG("tid %d does not exist in pid %d. ignoring debug request\n", - out_request->tid, out_request->pid); - return -1; - } - } else if (cr.uid == 0 + } else if (cr.uid == 0 || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) { - /* Only root or system can ask us to attach to any process and dump it explicitly. - * However, system is only allowed to collect backtraces but cannot dump tombstones. */ - status = get_process_info(out_request->tid, &out_request->pid, - &out_request->uid, &out_request->gid); - if (status < 0) { - LOG("tid %d does not exist. ignoring explicit dump request\n", - out_request->tid); - return -1; - } - } else { - /* No one else is allowed to dump arbitrary processes. */ - return -1; + // Only root or system can ask us to attach to any process and dump it explicitly. + // However, system is only allowed to collect backtraces but cannot dump tombstones. + status = get_process_info(out_request->tid, &out_request->pid, + &out_request->uid, &out_request->gid); + if (status < 0) { + LOG("tid %d does not exist. ignoring explicit dump request\n", out_request->tid); + return -1; } - return 0; + } else { + // No one else is allowed to dump arbitrary processes. + return -1; + } + return 0; } static bool should_attach_gdb(debugger_request_t* request) { - if (request->action == DEBUGGER_ACTION_CRASH) { - char value[PROPERTY_VALUE_MAX]; - property_get("debug.db.uid", value, "-1"); - int debug_uid = atoi(value); - return debug_uid >= 0 && request->uid <= (uid_t)debug_uid; - } - return false; + if (request->action == DEBUGGER_ACTION_CRASH) { + char value[PROPERTY_VALUE_MAX]; + property_get("debug.db.uid", value, "-1"); + int debug_uid = atoi(value); + return debug_uid >= 0 && request->uid <= (uid_t)debug_uid; + } + return false; } static void handle_request(int fd) { - XLOG("handle_request(%d)\n", fd); - - debugger_request_t request; - memset(&request, 0, sizeof(request)); - int status = read_request(fd, &request); - if (!status) { - XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", - request.pid, request.uid, request.gid, request.tid); - - /* At this point, the thread that made the request is blocked in - * a read() call. If the thread has crashed, then this gives us - * time to PTRACE_ATTACH to it before it has a chance to really fault. - * - * The PTRACE_ATTACH sends a SIGSTOP to the target process, but it - * won't necessarily have stopped by the time ptrace() returns. (We - * currently assume it does.) We write to the file descriptor to - * ensure that it can run as soon as we call PTRACE_CONT below. - * See details in bionic/libc/linker/debugger.c, in function - * debugger_signal_handler(). - */ - if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) { - LOG("ptrace attach failed: %s\n", strerror(errno)); - } else { - bool detach_failed = false; - bool attach_gdb = should_attach_gdb(&request); - if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) { - LOG("failed responding to client: %s\n", strerror(errno)); - } else { - char* tombstone_path = NULL; - - if (request.action == DEBUGGER_ACTION_CRASH) { - close(fd); - fd = -1; - } + XLOG("handle_request(%d)\n", fd); + + debugger_request_t request; + memset(&request, 0, sizeof(request)); + int status = read_request(fd, &request); + if (!status) { + XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", + request.pid, request.uid, request.gid, request.tid); + + // At this point, the thread that made the request is blocked in + // a read() call. If the thread has crashed, then this gives us + // time to PTRACE_ATTACH to it before it has a chance to really fault. + // + // The PTRACE_ATTACH sends a SIGSTOP to the target process, but it + // won't necessarily have stopped by the time ptrace() returns. (We + // currently assume it does.) We write to the file descriptor to + // ensure that it can run as soon as we call PTRACE_CONT below. + // See details in bionic/libc/linker/debugger.c, in function + // debugger_signal_handler(). + if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) { + LOG("ptrace attach failed: %s\n", strerror(errno)); + } else { + bool detach_failed = false; + bool attach_gdb = should_attach_gdb(&request); + if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) { + LOG("failed responding to client: %s\n", strerror(errno)); + } else { + char* tombstone_path = NULL; + + if (request.action == DEBUGGER_ACTION_CRASH) { + close(fd); + fd = -1; + } - int total_sleep_time_usec = 0; - for (;;) { - int signal = wait_for_signal(request.tid, &total_sleep_time_usec); - if (signal < 0) { - break; - } - - switch (signal) { - case SIGSTOP: - if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { - XLOG("stopped -- dumping to tombstone\n"); - tombstone_path = engrave_tombstone(request.pid, request.tid, - signal, request.abort_msg_address, true, true, &detach_failed, - &total_sleep_time_usec); - } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) { - XLOG("stopped -- dumping to fd\n"); - dump_backtrace(fd, -1, - request.pid, request.tid, &detach_failed, - &total_sleep_time_usec); - } else { - XLOG("stopped -- continuing\n"); - status = ptrace(PTRACE_CONT, request.tid, 0, 0); - if (status) { - LOG("ptrace continue failed: %s\n", strerror(errno)); - } - continue; /* loop again */ - } - break; - - case SIGILL: - case SIGABRT: - case SIGBUS: - case SIGFPE: - case SIGSEGV: - case SIGPIPE: + int total_sleep_time_usec = 0; + for (;;) { + int signal = wait_for_signal(request.tid, &total_sleep_time_usec); + if (signal < 0) { + break; + } + + switch (signal) { + case SIGSTOP: + if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { + XLOG("stopped -- dumping to tombstone\n"); + tombstone_path = engrave_tombstone( + request.pid, request.tid, signal, request.abort_msg_address, true, true, + &detach_failed, &total_sleep_time_usec); + } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) { + XLOG("stopped -- dumping to fd\n"); + dump_backtrace(fd, -1, request.pid, request.tid, &detach_failed, + &total_sleep_time_usec); + } else { + XLOG("stopped -- continuing\n"); + status = ptrace(PTRACE_CONT, request.tid, 0, 0); + if (status) { + LOG("ptrace continue failed: %s\n", strerror(errno)); + } + continue; // loop again + } + break; + + case SIGILL: + case SIGABRT: + case SIGBUS: + case SIGFPE: + case SIGSEGV: + case SIGPIPE: #ifdef SIGSTKFLT - case SIGSTKFLT: + case SIGSTKFLT: #endif - { - XLOG("stopped -- fatal signal\n"); - /* - * Send a SIGSTOP to the process to make all of - * the non-signaled threads stop moving. Without - * this we get a lot of "ptrace detach failed: - * No such process". - */ - kill(request.pid, SIGSTOP); - /* don't dump sibling threads when attaching to GDB because it - * makes the process less reliable, apparently... */ - tombstone_path = engrave_tombstone(request.pid, request.tid, - signal, request.abort_msg_address, !attach_gdb, false, - &detach_failed, &total_sleep_time_usec); - break; - } - - default: - XLOG("stopped -- unexpected signal\n"); - LOG("process stopped due to unexpected signal %d\n", signal); - break; - } - break; - } - - if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { - if (tombstone_path) { - write(fd, tombstone_path, strlen(tombstone_path)); - } - close(fd); - fd = -1; - } - free(tombstone_path); - } - - XLOG("detaching\n"); - if (attach_gdb) { - /* stop the process so we can debug */ - kill(request.pid, SIGSTOP); - - /* detach so we can attach gdbserver */ - if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { - LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); - detach_failed = true; - } + XLOG("stopped -- fatal signal\n"); + // Send a SIGSTOP to the process to make all of + // the non-signaled threads stop moving. Without + // this we get a lot of "ptrace detach failed: + // No such process". + kill(request.pid, SIGSTOP); + // don't dump sibling threads when attaching to GDB because it + // makes the process less reliable, apparently... + tombstone_path = engrave_tombstone( + request.pid, request.tid, signal, request.abort_msg_address, !attach_gdb, + false, &detach_failed, &total_sleep_time_usec); + break; + + default: + XLOG("stopped -- unexpected signal\n"); + LOG("process stopped due to unexpected signal %d\n", signal); + break; + } + break; + } - /* - * if debug.db.uid is set, its value indicates if we should wait - * for user action for the crashing process. - * in this case, we log a message and turn the debug LED on - * waiting for a gdb connection (for instance) - */ - wait_for_user_action(request.pid); - } else { - /* just detach */ - if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { - LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); - detach_failed = true; - } - } - - /* resume stopped process (so it can crash in peace). */ - kill(request.pid, SIGCONT); - - /* If we didn't successfully detach, we're still the parent, and the - * actual parent won't receive a death notification via wait(2). At this point - * there's not much we can do about that. */ - if (detach_failed) { - LOG("debuggerd committing suicide to free the zombie!\n"); - kill(getpid(), SIGKILL); - } + if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { + if (tombstone_path) { + write(fd, tombstone_path, strlen(tombstone_path)); + } + close(fd); + fd = -1; + } + free(tombstone_path); + } + + XLOG("detaching\n"); + if (attach_gdb) { + // stop the process so we can debug + kill(request.pid, SIGSTOP); + + // detach so we can attach gdbserver + if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { + LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); + detach_failed = true; } + // if debug.db.uid is set, its value indicates if we should wait + // for user action for the crashing process. + // in this case, we log a message and turn the debug LED on + // waiting for a gdb connection (for instance) + wait_for_user_action(request.pid); + } else { + // just detach + if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { + LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); + detach_failed = true; + } + } + + // resume stopped process (so it can crash in peace). + kill(request.pid, SIGCONT); + + // If we didn't successfully detach, we're still the parent, and the + // actual parent won't receive a death notification via wait(2). At this point + // there's not much we can do about that. + if (detach_failed) { + LOG("debuggerd committing suicide to free the zombie!\n"); + kill(getpid(), SIGKILL); + } } - if (fd >= 0) { - close(fd); - } + + } + if (fd >= 0) { + close(fd); + } } static int do_server() { - int s; - struct sigaction act; - int logsocket = -1; - - /* - * debuggerd crashes can't be reported to debuggerd. Reset all of the - * crash handlers. - */ - signal(SIGILL, SIG_DFL); - signal(SIGABRT, SIG_DFL); - signal(SIGBUS, SIG_DFL); - signal(SIGFPE, SIG_DFL); - signal(SIGSEGV, SIG_DFL); + int s; + struct sigaction act; + int logsocket = -1; + + // debuggerd crashes can't be reported to debuggerd. Reset all of the + // crash handlers. + signal(SIGILL, SIG_DFL); + signal(SIGABRT, SIG_DFL); + signal(SIGBUS, SIG_DFL); + signal(SIGFPE, SIG_DFL); + signal(SIGSEGV, SIG_DFL); #ifdef SIGSTKFLT - signal(SIGSTKFLT, SIG_DFL); + signal(SIGSTKFLT, SIG_DFL); #endif - // Ignore failed writes to closed sockets - signal(SIGPIPE, SIG_IGN); + // Ignore failed writes to closed sockets + signal(SIGPIPE, SIG_IGN); - logsocket = socket_local_client("logd", - ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); - if(logsocket < 0) { - logsocket = -1; - } else { - fcntl(logsocket, F_SETFD, FD_CLOEXEC); - } + logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); + if (logsocket < 0) { + logsocket = -1; + } else { + fcntl(logsocket, F_SETFD, FD_CLOEXEC); + } - act.sa_handler = SIG_DFL; - sigemptyset(&act.sa_mask); - sigaddset(&act.sa_mask,SIGCHLD); - act.sa_flags = SA_NOCLDWAIT; - sigaction(SIGCHLD, &act, 0); - - s = socket_local_server(DEBUGGER_SOCKET_NAME, - ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); - if(s < 0) return 1; - fcntl(s, F_SETFD, FD_CLOEXEC); - - LOG("debuggerd: " __DATE__ " " __TIME__ "\n"); - - for(;;) { - struct sockaddr addr; - socklen_t alen; - int fd; - - alen = sizeof(addr); - XLOG("waiting for connection\n"); - fd = accept(s, &addr, &alen); - if(fd < 0) { - XLOG("accept failed: %s\n", strerror(errno)); - continue; - } + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + sigaddset(&act.sa_mask,SIGCHLD); + act.sa_flags = SA_NOCLDWAIT; + sigaction(SIGCHLD, &act, 0); - fcntl(fd, F_SETFD, FD_CLOEXEC); + s = socket_local_server(DEBUGGER_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + if (s < 0) + return 1; + fcntl(s, F_SETFD, FD_CLOEXEC); - handle_request(fd); + LOG("debuggerd: " __DATE__ " " __TIME__ "\n"); + + for (;;) { + struct sockaddr addr; + socklen_t alen; + int fd; + + alen = sizeof(addr); + XLOG("waiting for connection\n"); + fd = accept(s, &addr, &alen); + if (fd < 0) { + XLOG("accept failed: %s\n", strerror(errno)); + continue; } - return 0; + + fcntl(fd, F_SETFD, FD_CLOEXEC); + + handle_request(fd); + } + return 0; } static int do_explicit_dump(pid_t tid, bool dump_backtrace) { - fprintf(stdout, "Sending request to dump task %d.\n", tid); + fprintf(stdout, "Sending request to dump task %d.\n", tid); - if (dump_backtrace) { - fflush(stdout); - if (dump_backtrace_to_file(tid, fileno(stdout)) < 0) { - fputs("Error dumping backtrace.\n", stderr); - return 1; - } - } else { - char tombstone_path[PATH_MAX]; - if (dump_tombstone(tid, tombstone_path, sizeof(tombstone_path)) < 0) { - fputs("Error dumping tombstone.\n", stderr); - return 1; - } - fprintf(stderr, "Tombstone written to: %s\n", tombstone_path); + if (dump_backtrace) { + fflush(stdout); + if (dump_backtrace_to_file(tid, fileno(stdout)) < 0) { + fputs("Error dumping backtrace.\n", stderr); + return 1; } - return 0; + } else { + char tombstone_path[PATH_MAX]; + if (dump_tombstone(tid, tombstone_path, sizeof(tombstone_path)) < 0) { + fputs("Error dumping tombstone.\n", stderr); + return 1; + } + fprintf(stderr, "Tombstone written to: %s\n", tombstone_path); + } + return 0; } static void usage() { - fputs("Usage: -b [<tid>]\n" - " -b dump backtrace to console, otherwise dump full tombstone file\n" - "\n" - "If tid specified, sends a request to debuggerd to dump that task.\n" - "Otherwise, starts the debuggerd server.\n", stderr); + fputs("Usage: -b [<tid>]\n" + " -b dump backtrace to console, otherwise dump full tombstone file\n" + "\n" + "If tid specified, sends a request to debuggerd to dump that task.\n" + "Otherwise, starts the debuggerd server.\n", stderr); } int main(int argc, char** argv) { - if (argc == 1) { - return do_server(); - } - - bool dump_backtrace = false; - bool have_tid = false; - pid_t tid = 0; - for (int i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-b")) { - dump_backtrace = true; - } else if (!have_tid) { - tid = atoi(argv[i]); - have_tid = true; - } else { - usage(); - return 1; - } - } - if (!have_tid) { - usage(); - return 1; + if (argc == 1) { + return do_server(); + } + + bool dump_backtrace = false; + bool have_tid = false; + pid_t tid = 0; + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-b")) { + dump_backtrace = true; + } else if (!have_tid) { + tid = atoi(argv[i]); + have_tid = true; + } else { + usage(); + return 1; } - return do_explicit_dump(tid, dump_backtrace); + } + if (!have_tid) { + usage(); + return 1; + } + return do_explicit_dump(tid, dump_backtrace); } diff --git a/debuggerd/getevent.cpp b/debuggerd/getevent.cpp index 8d0b149..751c4fb 100644 --- a/debuggerd/getevent.cpp +++ b/debuggerd/getevent.cpp @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2014 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 <stdio.h> #include <stdlib.h> #include <string.h> @@ -12,208 +28,195 @@ #include <errno.h> #include <cutils/log.h> -static struct pollfd *ufds; -static char **device_names; +static struct pollfd* ufds; +static char** device_names; static int nfds; -static int open_device(const char *device) -{ - int version; - int fd; - struct pollfd *new_ufds; - char **new_device_names; - char name[80]; - char location[80]; - char idstr[80]; - struct input_id id; - - fd = open(device, O_RDWR); - if(fd < 0) { - return -1; - } - - if(ioctl(fd, EVIOCGVERSION, &version)) { - return -1; - } - if(ioctl(fd, EVIOCGID, &id)) { - return -1; - } - name[sizeof(name) - 1] = '\0'; - location[sizeof(location) - 1] = '\0'; - idstr[sizeof(idstr) - 1] = '\0'; - if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { - //fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno)); - name[0] = '\0'; - } - if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { - //fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno)); - location[0] = '\0'; - } - if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { - //fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno)); - idstr[0] = '\0'; - } - - new_ufds = reinterpret_cast<pollfd*>(realloc(ufds, sizeof(ufds[0]) * (nfds + 1))); - if(new_ufds == NULL) { - fprintf(stderr, "out of memory\n"); - return -1; - } - ufds = new_ufds; - new_device_names = reinterpret_cast<char**>(realloc(device_names, sizeof(device_names[0]) * (nfds + 1))); - if(new_device_names == NULL) { - fprintf(stderr, "out of memory\n"); - return -1; - } - device_names = new_device_names; - ufds[nfds].fd = fd; - ufds[nfds].events = POLLIN; - device_names[nfds] = strdup(device); - nfds++; - - return 0; -} +static int open_device(const char* device) { + int version; + int fd; + struct pollfd* new_ufds; + char** new_device_names; + char name[80]; + char location[80]; + char idstr[80]; + struct input_id id; + + fd = open(device, O_RDWR); + if (fd < 0) { + return -1; + } -int close_device(const char *device) -{ - int i; - for(i = 1; i < nfds; i++) { - if(strcmp(device_names[i], device) == 0) { - int count = nfds - i - 1; - free(device_names[i]); - memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count); - memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count); - nfds--; - return 0; - } - } + if (ioctl(fd, EVIOCGVERSION, &version)) { return -1; + } + if (ioctl(fd, EVIOCGID, &id)) { + return -1; + } + name[sizeof(name) - 1] = '\0'; + location[sizeof(location) - 1] = '\0'; + idstr[sizeof(idstr) - 1] = '\0'; + if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { + name[0] = '\0'; + } + if (ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { + location[0] = '\0'; + } + if (ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { + idstr[0] = '\0'; + } + + new_ufds = reinterpret_cast<pollfd*>(realloc(ufds, sizeof(ufds[0]) * (nfds + 1))); + if (new_ufds == NULL) { + fprintf(stderr, "out of memory\n"); + return -1; + } + ufds = new_ufds; + new_device_names = reinterpret_cast<char**>(realloc( + device_names, sizeof(device_names[0]) * (nfds + 1))); + if (new_device_names == NULL) { + fprintf(stderr, "out of memory\n"); + return -1; + } + device_names = new_device_names; + ufds[nfds].fd = fd; + ufds[nfds].events = POLLIN; + device_names[nfds] = strdup(device); + nfds++; + + return 0; } -static int read_notify(const char *dirname, int nfd) -{ - int res; - char devname[PATH_MAX]; - char *filename; - char event_buf[512]; - int event_size; - int event_pos = 0; - struct inotify_event *event; - - res = read(nfd, event_buf, sizeof(event_buf)); - if(res < (int)sizeof(*event)) { - if(errno == EINTR) - return 0; - fprintf(stderr, "could not get event, %s\n", strerror(errno)); - return 1; - } - //printf("got %d bytes of event information\n", res); - - strcpy(devname, dirname); - filename = devname + strlen(devname); - *filename++ = '/'; - - while(res >= (int)sizeof(*event)) { - event = (struct inotify_event *)(event_buf + event_pos); - //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); - if(event->len) { - strcpy(filename, event->name); - if(event->mask & IN_CREATE) { - open_device(devname); - } - else { - close_device(devname); - } - } - event_size = sizeof(*event) + event->len; - res -= event_size; - event_pos += event_size; - } - return 0; +int close_device(const char* device) { + int i; + for (i = 1; i < nfds; i++) { + if (strcmp(device_names[i], device) == 0) { + int count = nfds - i - 1; + free(device_names[i]); + memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count); + memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count); + nfds--; + return 0; + } + } + return -1; } -static int scan_dir(const char *dirname) -{ - char devname[PATH_MAX]; - char *filename; - DIR *dir; - struct dirent *de; - dir = opendir(dirname); - if(dir == NULL) - return -1; - strcpy(devname, dirname); - filename = devname + strlen(devname); - *filename++ = '/'; - while((de = readdir(dir))) { - if(de->d_name[0] == '.' && - (de->d_name[1] == '\0' || - (de->d_name[1] == '.' && de->d_name[2] == '\0'))) - continue; - strcpy(filename, de->d_name); +static int read_notify(const char* dirname, int nfd) { + int res; + char devname[PATH_MAX]; + char* filename; + char event_buf[512]; + int event_size; + int event_pos = 0; + struct inotify_event *event; + + res = read(nfd, event_buf, sizeof(event_buf)); + if (res < (int)sizeof(*event)) { + if (errno == EINTR) + return 0; + fprintf(stderr, "could not get event, %s\n", strerror(errno)); + return 1; + } + + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + + while (res >= (int)sizeof(*event)) { + event = reinterpret_cast<struct inotify_event*>(event_buf + event_pos); + if (event->len) { + strcpy(filename, event->name); + if (event->mask & IN_CREATE) { open_device(devname); - } - closedir(dir); - return 0; + } else { + close_device(devname); + } + } + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } + return 0; } -int init_getevent() -{ - int res; - const char *device_path = "/dev/input"; - - nfds = 1; - ufds = reinterpret_cast<pollfd*>(calloc(1, sizeof(ufds[0]))); - ufds[0].fd = inotify_init(); - ufds[0].events = POLLIN; +static int scan_dir(const char* dirname) { + char devname[PATH_MAX]; + char* filename; + DIR* dir; + struct dirent* de; + dir = opendir(dirname); + if (dir == NULL) + return -1; + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + while ((de = readdir(dir))) { + if ((de->d_name[0] == '.' && de->d_name[1] == '\0') || + (de->d_name[1] == '.' && de->d_name[2] == '\0')) + continue; + strcpy(filename, de->d_name); + open_device(devname); + } + closedir(dir); + return 0; +} - res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE); - if(res < 0) { - return 1; - } - res = scan_dir(device_path); - if(res < 0) { - return 1; - } - return 0; +int init_getevent() { + int res; + const char* device_path = "/dev/input"; + + nfds = 1; + ufds = reinterpret_cast<pollfd*>(calloc(1, sizeof(ufds[0]))); + ufds[0].fd = inotify_init(); + ufds[0].events = POLLIN; + + res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE); + if (res < 0) { + return 1; + } + res = scan_dir(device_path); + if (res < 0) { + return 1; + } + return 0; } -void uninit_getevent() -{ - int i; - for(i = 0; i < nfds; i++) { - close(ufds[i].fd); - } - free(ufds); - ufds = 0; - nfds = 0; +void uninit_getevent() { + int i; + for (i = 0; i < nfds; i++) { + close(ufds[i].fd); + } + free(ufds); + ufds = 0; + nfds = 0; } -int get_event(struct input_event* event, int timeout) -{ - int res; - int i; - int pollres; - const char *device_path = "/dev/input"; - while(1) { - pollres = poll(ufds, nfds, timeout); - if (pollres == 0) { - return 1; - } - if(ufds[0].revents & POLLIN) { - read_notify(device_path, ufds[0].fd); - } - for(i = 1; i < nfds; i++) { - if(ufds[i].revents) { - if(ufds[i].revents & POLLIN) { - res = read(ufds[i].fd, event, sizeof(*event)); - if(res < (int)sizeof(event)) { - fprintf(stderr, "could not get event\n"); - return -1; - } - return 0; - } - } +int get_event(struct input_event* event, int timeout) { + int res; + int i; + int pollres; + const char* device_path = "/dev/input"; + while (1) { + pollres = poll(ufds, nfds, timeout); + if (pollres == 0) { + return 1; + } + if (ufds[0].revents & POLLIN) { + read_notify(device_path, ufds[0].fd); + } + for (i = 1; i < nfds; i++) { + if (ufds[i].revents) { + if (ufds[i].revents & POLLIN) { + res = read(ufds[i].fd, event, sizeof(*event)); + if (res < static_cast<int>(sizeof(event))) { + fprintf(stderr, "could not get event\n"); + return -1; + } + return 0; } + } } - return 0; + } + return 0; } diff --git a/debuggerd/mips/machine.cpp b/debuggerd/mips/machine.cpp index 489f3c5..d1a7f2d 100644 --- a/debuggerd/mips/machine.cpp +++ b/debuggerd/mips/machine.cpp @@ -1,22 +1,20 @@ -/* system/debuggerd/debuggerd.c -** -** Copyright 2012, 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. -*/ +/* + * Copyright 2012, 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 <stddef.h> -#include <stdbool.h> #include <stdlib.h> #include <string.h> #include <stdio.h> @@ -31,144 +29,134 @@ #include "../utility.h" #include "../machine.h" -/* enable to dump memory pointed to by every register */ +// enable to dump memory pointed to by every register #define DUMP_MEMORY_FOR_ALL_REGISTERS 1 -#define R(x) ((unsigned int)(x)) +#define R(x) (static_cast<unsigned int>(x)) static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scope_flags) { - 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; + 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++ = '.'; } - *asc_out = '\0'; - _LOG(log, scope_flags, " %s %s\n", code_buffer, ascii_buffer); + } + p += 4; } + *asc_out = '\0'; + _LOG(log, scope_flags, " %s %s\n", code_buffer, ascii_buffer); + } } -/* - * If configured to do so, dump memory around *all* registers - * for the crashing thread. - */ +// If configured to do so, dump memory around *all* registers +// for the crashing thread. void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags) { - pt_regs_mips_t r; - if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { - return; + pt_regs_mips_t r; + if (ptrace(PTRACE_GETREGS, tid, 0, &r)) { + return; + } + + if (IS_AT_FAULT(scope_flags) && DUMP_MEMORY_FOR_ALL_REGISTERS) { + static const char REG_NAMES[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; + + for (int reg = 0; reg < 32; reg++) { + // skip uninteresting registers + if (reg == 0 // $0 + || reg == 26 // $k0 + || reg == 27 // $k1 + || reg == 31 // $ra (done below) + ) + continue; + + uintptr_t addr = R(r.regs[reg]); + + // Don't bother if it looks like a small int or ~= null, or if + // it's in the kernel area. + if (addr < 4096 || addr >= 0x80000000) { + continue; + } + + _LOG(log, scope_flags | SCOPE_SENSITIVE, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); + dump_memory(log, tid, addr, scope_flags | SCOPE_SENSITIVE); } + } - if (IS_AT_FAULT(scope_flags) && DUMP_MEMORY_FOR_ALL_REGISTERS) { - static const char REG_NAMES[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; - - for (int reg = 0; reg < 32; reg++) { - /* skip uninteresting registers */ - if (reg == 0 /* $0 */ - || reg == 26 /* $k0 */ - || reg == 27 /* $k1 */ - || reg == 31 /* $ra (done below) */ - ) - continue; - - uintptr_t addr = R(r.regs[reg]); - - /* - * Don't bother if it looks like a small int or ~= null, or if - * it's in the kernel area. - */ - if (addr < 4096 || addr >= 0x80000000) { - continue; - } - - _LOG(log, scope_flags | SCOPE_SENSITIVE, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); - dump_memory(log, tid, addr, scope_flags | SCOPE_SENSITIVE); - } - } - - unsigned int pc = R(r.cp0_epc); - unsigned int ra = R(r.regs[31]); + unsigned int pc = R(r.cp0_epc); + unsigned int ra = R(r.regs[31]); - _LOG(log, scope_flags, "\ncode around pc:\n"); - dump_memory(log, tid, (uintptr_t)pc, scope_flags); + _LOG(log, scope_flags, "\ncode around pc:\n"); + dump_memory(log, tid, (uintptr_t)pc, scope_flags); - if (pc != ra) { - _LOG(log, scope_flags, "\ncode around ra:\n"); - dump_memory(log, tid, (uintptr_t)ra, scope_flags); - } + if (pc != ra) { + _LOG(log, scope_flags, "\ncode around ra:\n"); + dump_memory(log, tid, (uintptr_t)ra, scope_flags); + } } -void dump_registers(log_t* log, pid_t tid, int scope_flags) -{ - pt_regs_mips_t r; - if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { - _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); - return; - } - - _LOG(log, scope_flags, " zr %08x at %08x v0 %08x v1 %08x\n", - R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3])); - _LOG(log, scope_flags, " a0 %08x a1 %08x a2 %08x a3 %08x\n", - R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7])); - _LOG(log, scope_flags, " t0 %08x t1 %08x t2 %08x t3 %08x\n", - R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11])); - _LOG(log, scope_flags, " t4 %08x t5 %08x t6 %08x t7 %08x\n", - R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15])); - _LOG(log, scope_flags, " s0 %08x s1 %08x s2 %08x s3 %08x\n", - R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19])); - _LOG(log, scope_flags, " s4 %08x s5 %08x s6 %08x s7 %08x\n", - R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23])); - _LOG(log, scope_flags, " t8 %08x t9 %08x k0 %08x k1 %08x\n", - R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27])); - _LOG(log, scope_flags, " gp %08x sp %08x s8 %08x ra %08x\n", - R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31])); - _LOG(log, scope_flags, " hi %08x lo %08x bva %08x epc %08x\n", - R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc)); +void dump_registers(log_t* log, pid_t tid, int scope_flags) { + pt_regs_mips_t r; + if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { + _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); + return; + } + + _LOG(log, scope_flags, " zr %08x at %08x v0 %08x v1 %08x\n", + R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3])); + _LOG(log, scope_flags, " a0 %08x a1 %08x a2 %08x a3 %08x\n", + R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7])); + _LOG(log, scope_flags, " t0 %08x t1 %08x t2 %08x t3 %08x\n", + R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11])); + _LOG(log, scope_flags, " t4 %08x t5 %08x t6 %08x t7 %08x\n", + R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15])); + _LOG(log, scope_flags, " s0 %08x s1 %08x s2 %08x s3 %08x\n", + R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19])); + _LOG(log, scope_flags, " s4 %08x s5 %08x s6 %08x s7 %08x\n", + R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23])); + _LOG(log, scope_flags, " t8 %08x t9 %08x k0 %08x k1 %08x\n", + R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27])); + _LOG(log, scope_flags, " gp %08x sp %08x s8 %08x ra %08x\n", + R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31])); + _LOG(log, scope_flags, " hi %08x lo %08x bva %08x epc %08x\n", + R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc)); } diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp index 1b08e8e..9c5d8c0 100644 --- a/debuggerd/tombstone.cpp +++ b/debuggerd/tombstone.cpp @@ -15,7 +15,6 @@ */ #include <stddef.h> -#include <stdbool.h> #include <stdlib.h> #include <signal.h> #include <string.h> @@ -26,19 +25,22 @@ #include <time.h> #include <sys/ptrace.h> #include <sys/stat.h> +#include <inttypes.h> #include <private/android_filesystem_config.h> #include <log/logger.h> #include <cutils/properties.h> -#include <backtrace/backtrace.h> +#include <backtrace/Backtrace.h> #include <sys/socket.h> #include <linux/un.h> #include <selinux/android.h> +#include <UniquePtr.h> + #include "machine.h" #include "tombstone.h" #include "backtrace.h" @@ -48,50 +50,48 @@ #define MAX_TOMBSTONES 10 #define TOMBSTONE_DIR "/data/tombstones" -/* Must match the path defined in NativeCrashListener.java */ +// Must match the path defined in NativeCrashListener.java #define NCRASH_SOCKET_PATH "/data/system/ndebugsocket" #define typecheck(x,y) { \ - typeof(x) __dummy1; \ - typeof(y) __dummy2; \ - (void)(&__dummy1 == &__dummy2); } + typeof(x) __dummy1; \ + typeof(y) __dummy2; \ + (void)(&__dummy1 == &__dummy2); } static bool signal_has_address(int sig) { - switch (sig) { - case SIGILL: - case SIGFPE: - case SIGSEGV: - case SIGBUS: - return true; - default: - return false; - } + switch (sig) { + case SIGILL: + case SIGFPE: + case SIGSEGV: + case SIGBUS: + return true; + default: + return false; + } } -static const char *get_signame(int sig) -{ - switch(sig) { - case SIGILL: return "SIGILL"; - case SIGABRT: return "SIGABRT"; - case SIGBUS: return "SIGBUS"; - case SIGFPE: return "SIGFPE"; - case SIGSEGV: return "SIGSEGV"; - case SIGPIPE: return "SIGPIPE"; +static const char* get_signame(int sig) { + switch(sig) { + case SIGILL: return "SIGILL"; + case SIGABRT: return "SIGABRT"; + case SIGBUS: return "SIGBUS"; + case SIGFPE: return "SIGFPE"; + case SIGSEGV: return "SIGSEGV"; + case SIGPIPE: return "SIGPIPE"; #ifdef SIGSTKFLT - case SIGSTKFLT: return "SIGSTKFLT"; + case SIGSTKFLT: return "SIGSTKFLT"; #endif - case SIGSTOP: return "SIGSTOP"; - default: return "?"; - } + case SIGSTOP: return "SIGSTOP"; + default: return "?"; + } } -static const char *get_sigcode(int signo, int code) -{ - // Try the signal-specific codes... - switch (signo) { +static const char* get_sigcode(int signo, int code) { + // Try the signal-specific codes... + switch (signo) { case SIGILL: - switch (code) { + switch (code) { case ILL_ILLOPC: return "ILL_ILLOPC"; case ILL_ILLOPN: return "ILL_ILLOPN"; case ILL_ILLADR: return "ILL_ILLADR"; @@ -100,17 +100,17 @@ static const char *get_sigcode(int signo, int code) case ILL_PRVREG: return "ILL_PRVREG"; case ILL_COPROC: return "ILL_COPROC"; case ILL_BADSTK: return "ILL_BADSTK"; - } - break; + } + break; case SIGBUS: - switch (code) { + switch (code) { case BUS_ADRALN: return "BUS_ADRALN"; case BUS_ADRERR: return "BUS_ADRERR"; case BUS_OBJERR: return "BUS_OBJERR"; - } - break; + } + break; case SIGFPE: - switch (code) { + switch (code) { case FPE_INTDIV: return "FPE_INTDIV"; case FPE_INTOVF: return "FPE_INTOVF"; case FPE_FLTDIV: return "FPE_FLTDIV"; @@ -119,496 +119,463 @@ static const char *get_sigcode(int signo, int code) case FPE_FLTRES: return "FPE_FLTRES"; case FPE_FLTINV: return "FPE_FLTINV"; case FPE_FLTSUB: return "FPE_FLTSUB"; - } - break; + } + break; case SIGSEGV: - switch (code) { + switch (code) { case SEGV_MAPERR: return "SEGV_MAPERR"; case SEGV_ACCERR: return "SEGV_ACCERR"; - } - break; + } + break; case SIGTRAP: - switch (code) { + switch (code) { case TRAP_BRKPT: return "TRAP_BRKPT"; case TRAP_TRACE: return "TRAP_TRACE"; - } - break; - } - // Then the other codes... - switch (code) { - case SI_USER: return "SI_USER"; + } + break; + } + // Then the other codes... + switch (code) { + case SI_USER: return "SI_USER"; #if defined(SI_KERNEL) - case SI_KERNEL: return "SI_KERNEL"; + case SI_KERNEL: return "SI_KERNEL"; #endif - case SI_QUEUE: return "SI_QUEUE"; - case SI_TIMER: return "SI_TIMER"; - case SI_MESGQ: return "SI_MESGQ"; + case SI_QUEUE: return "SI_QUEUE"; + case SI_TIMER: return "SI_TIMER"; + case SI_MESGQ: return "SI_MESGQ"; case SI_ASYNCIO: return "SI_ASYNCIO"; #if defined(SI_SIGIO) - case SI_SIGIO: return "SI_SIGIO"; + case SI_SIGIO: return "SI_SIGIO"; #endif #if defined(SI_TKILL) - case SI_TKILL: return "SI_TKILL"; + case SI_TKILL: return "SI_TKILL"; #endif - } - // Then give up... - return "?"; + } + // Then give up... + return "?"; } -static void dump_revision_info(log_t* log) -{ - char revision[PROPERTY_VALUE_MAX]; +static void dump_revision_info(log_t* log) { + char revision[PROPERTY_VALUE_MAX]; - property_get("ro.revision", revision, "unknown"); + property_get("ro.revision", revision, "unknown"); - _LOG(log, SCOPE_AT_FAULT, "Revision: '%s'\n", revision); + _LOG(log, SCOPE_AT_FAULT, "Revision: '%s'\n", revision); } -static void dump_build_info(log_t* log) -{ - char fingerprint[PROPERTY_VALUE_MAX]; +static void dump_build_info(log_t* log) { + char fingerprint[PROPERTY_VALUE_MAX]; - property_get("ro.build.fingerprint", fingerprint, "unknown"); + property_get("ro.build.fingerprint", fingerprint, "unknown"); - _LOG(log, SCOPE_AT_FAULT, "Build fingerprint: '%s'\n", fingerprint); + _LOG(log, SCOPE_AT_FAULT, "Build fingerprint: '%s'\n", fingerprint); } -static void dump_fault_addr(log_t* log, pid_t tid, int sig) -{ - siginfo_t si; - - memset(&si, 0, sizeof(si)); - if(ptrace(PTRACE_GETSIGINFO, tid, 0, &si)){ - _LOG(log, SCOPE_AT_FAULT, "cannot get siginfo: %s\n", strerror(errno)); - } else if (signal_has_address(sig)) { - _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr %08x\n", - sig, get_signame(sig), - si.si_code, get_sigcode(sig, si.si_code), - (uintptr_t) si.si_addr); - } else { - _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr --------\n", - sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code)); - } +static void dump_fault_addr(log_t* log, pid_t tid, int sig) { + siginfo_t si; + + memset(&si, 0, sizeof(si)); + if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)){ + _LOG(log, SCOPE_AT_FAULT, "cannot get siginfo: %s\n", strerror(errno)); + } else if (signal_has_address(sig)) { + _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr %0*" PRIxPTR "\n", + sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code), + sizeof(uintptr_t)*2, reinterpret_cast<uintptr_t>(si.si_addr)); + } else { + _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr --------\n", + sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code)); + } } static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, int scope_flags) { - char path[64]; - char threadnamebuf[1024]; - char* threadname = NULL; - FILE *fp; - - snprintf(path, sizeof(path), "/proc/%d/comm", tid); - if ((fp = fopen(path, "r"))) { - threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp); - fclose(fp); - if (threadname) { - size_t len = strlen(threadname); - if (len && threadname[len - 1] == '\n') { - threadname[len - 1] = '\0'; - } - } + char path[64]; + char threadnamebuf[1024]; + char* threadname = NULL; + FILE *fp; + + snprintf(path, sizeof(path), "/proc/%d/comm", tid); + if ((fp = fopen(path, "r"))) { + threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp); + fclose(fp); + if (threadname) { + size_t len = strlen(threadname); + if (len && threadname[len - 1] == '\n') { + threadname[len - 1] = '\0'; + } } + } - if (IS_AT_FAULT(scope_flags)) { - char procnamebuf[1024]; - char* procname = NULL; - - snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); - if ((fp = fopen(path, "r"))) { - procname = fgets(procnamebuf, sizeof(procnamebuf), fp); - fclose(fp); - } + if (IS_AT_FAULT(scope_flags)) { + char procnamebuf[1024]; + char* procname = NULL; - _LOG(log, SCOPE_AT_FAULT, "pid: %d, tid: %d, name: %s >>> %s <<<\n", pid, tid, - threadname ? threadname : "UNKNOWN", - procname ? procname : "UNKNOWN"); - } else { - _LOG(log, 0, "pid: %d, tid: %d, name: %s\n", - pid, tid, threadname ? threadname : "UNKNOWN"); + snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); + if ((fp = fopen(path, "r"))) { + procname = fgets(procnamebuf, sizeof(procnamebuf), fp); + fclose(fp); } + + _LOG(log, SCOPE_AT_FAULT, "pid: %d, tid: %d, name: %s >>> %s <<<\n", pid, tid, + threadname ? threadname : "UNKNOWN", procname ? procname : "UNKNOWN"); + } else { + _LOG(log, 0, "pid: %d, tid: %d, name: %s\n", pid, tid, threadname ? threadname : "UNKNOWN"); + } } -static void dump_stack_segment(const backtrace_context_t* context, log_t* log, - int scope_flags, uintptr_t *sp, size_t words, int label) { - for (size_t i = 0; i < words; i++) { - uint32_t stack_content; - if (!backtrace_read_word(context, *sp, &stack_content)) { - break; - } +static void dump_stack_segment( + Backtrace* backtrace, log_t* log, int scope_flags, uintptr_t* sp, size_t words, int label) { + for (size_t i = 0; i < words; i++) { + uint32_t stack_content; + if (!backtrace->ReadWord(*sp, &stack_content)) { + break; + } - const char* map_name = backtrace_get_map_name(context, stack_content, NULL); - if (!map_name) { - map_name = ""; + const char* map_name = backtrace->GetMapName(stack_content, NULL); + if (!map_name) { + map_name = ""; + } + uintptr_t offset = 0; + std::string func_name(backtrace->GetFunctionName(stack_content, &offset)); + if (!func_name.empty()) { + if (!i && label >= 0) { + if (offset) { + _LOG(log, scope_flags, " #%02d %08x %08x %s (%s+%u)\n", + label, *sp, stack_content, map_name, func_name.c_str(), offset); + } else { + _LOG(log, scope_flags, " #%02d %08x %08x %s (%s)\n", + label, *sp, stack_content, map_name, func_name.c_str()); } - uintptr_t offset = 0; - char* func_name = backtrace_get_func_name(context, stack_content, &offset); - if (func_name) { - if (!i && label >= 0) { - if (offset) { - _LOG(log, scope_flags, " #%02d %08x %08x %s (%s+%u)\n", - label, *sp, stack_content, map_name, func_name, offset); - } else { - _LOG(log, scope_flags, " #%02d %08x %08x %s (%s)\n", - label, *sp, stack_content, map_name, func_name); - } - } else { - if (offset) { - _LOG(log, scope_flags, " %08x %08x %s (%s+%u)\n", - *sp, stack_content, map_name, func_name, offset); - } else { - _LOG(log, scope_flags, " %08x %08x %s (%s)\n", - *sp, stack_content, map_name, func_name); - } - } - free(func_name); + } else { + if (offset) { + _LOG(log, scope_flags, " %08x %08x %s (%s+%u)\n", + *sp, stack_content, map_name, func_name.c_str(), offset); } else { - if (!i && label >= 0) { - _LOG(log, scope_flags, " #%02d %08x %08x %s\n", - label, *sp, stack_content, map_name); - } else { - _LOG(log, scope_flags, " %08x %08x %s\n", - *sp, stack_content, map_name); - } + _LOG(log, scope_flags, " %08x %08x %s (%s)\n", + *sp, stack_content, map_name, func_name.c_str()); } - - *sp += sizeof(uint32_t); + } + } else { + if (!i && label >= 0) { + _LOG(log, scope_flags, " #%02d %08x %08x %s\n", + label, *sp, stack_content, map_name); + } else { + _LOG(log, scope_flags, " %08x %08x %s\n", + *sp, stack_content, map_name); + } } + + *sp += sizeof(uint32_t); + } } -static void dump_stack(const backtrace_context_t* context, log_t* log, int scope_flags) { - const backtrace_t* backtrace = context->backtrace; - size_t first = 0, last; - for (size_t i = 0; i < backtrace->num_frames; i++) { - if (backtrace->frames[i].sp) { - if (!first) { - first = i+1; - } - last = i; - } - } - if (!first) { - return; +static void dump_stack(Backtrace* backtrace, log_t* log, int scope_flags) { + size_t first = 0, last; + for (size_t i = 0; i < backtrace->NumFrames(); i++) { + const backtrace_frame_data_t* frame = backtrace->GetFrame(i); + if (frame->sp) { + if (!first) { + first = i+1; + } + last = i; } - first--; - - scope_flags |= SCOPE_SENSITIVE; - - // Dump a few words before the first frame. - uintptr_t sp = backtrace->frames[first].sp - STACK_WORDS * sizeof(uint32_t); - dump_stack_segment(context, log, scope_flags, &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_data_t* frame = &backtrace->frames[i]; - if (sp != frame->sp) { - _LOG(log, scope_flags, " ........ ........\n"); - sp = frame->sp; - } - if (i - first == 3) { - scope_flags &= (~SCOPE_AT_FAULT); - } - if (i == last) { - dump_stack_segment(context, log, scope_flags, &sp, STACK_WORDS, i); - if (sp < frame->sp + frame->stack_size) { - _LOG(log, scope_flags, " ........ ........\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, log, scope_flags, &sp, words, i); - } + } + if (!first) { + return; + } + first--; + + scope_flags |= SCOPE_SENSITIVE; + + // Dump a few words before the first frame. + uintptr_t sp = backtrace->GetFrame(first)->sp - STACK_WORDS * sizeof(uint32_t); + dump_stack_segment(backtrace, log, scope_flags, &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_data_t* frame = backtrace->GetFrame(i); + if (sp != frame->sp) { + _LOG(log, scope_flags, " ........ ........\n"); + sp = frame->sp; + } + if (i - first == 3) { + scope_flags &= (~SCOPE_AT_FAULT); + } + if (i == last) { + dump_stack_segment(backtrace, log, scope_flags, &sp, STACK_WORDS, i); + if (sp < frame->sp + frame->stack_size) { + _LOG(log, scope_flags, " ........ ........\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(backtrace, log, scope_flags, &sp, words, i); } + } } -static void dump_backtrace_and_stack(const backtrace_context_t* context, - log_t* log, int scope_flags) { - if (context->backtrace->num_frames) { - _LOG(log, scope_flags, "\nbacktrace:\n"); - dump_backtrace_to_log(context, log, scope_flags, " "); +static void dump_backtrace_and_stack(Backtrace* backtrace, log_t* log, int scope_flags) { + if (backtrace->NumFrames()) { + _LOG(log, scope_flags, "\nbacktrace:\n"); + dump_backtrace_to_log(backtrace, log, scope_flags, " "); - _LOG(log, scope_flags, "\nstack:\n"); - dump_stack(context, log, scope_flags); - } + _LOG(log, scope_flags, "\nstack:\n"); + dump_stack(backtrace, log, scope_flags); + } } static void dump_map(log_t* log, const backtrace_map_info_t* m, const char* what, int scope_flags) { - if (m != NULL) { - _LOG(log, scope_flags, " %08x-%08x %c%c%c %s\n", m->start, m->end, - m->is_readable ? 'r' : '-', - m->is_writable ? 'w' : '-', - m->is_executable ? 'x' : '-', - m->name); - } else { - _LOG(log, scope_flags, " (no %s)\n", what); - } + if (m != NULL) { + _LOG(log, scope_flags, " %08x-%08x %c%c%c %s\n", m->start, m->end, + m->is_readable ? 'r' : '-', m->is_writable ? 'w' : '-', + m->is_executable ? 'x' : '-', m->name); + } else { + _LOG(log, scope_flags, " (no %s)\n", what); + } } static void dump_nearby_maps(const backtrace_map_info_t* map_info_list, log_t* log, pid_t tid, int scope_flags) { - scope_flags |= SCOPE_SENSITIVE; - siginfo_t si; - memset(&si, 0, sizeof(si)); - if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) { - _LOG(log, scope_flags, "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; - } + scope_flags |= SCOPE_SENSITIVE; + siginfo_t si; + memset(&si, 0, sizeof(si)); + if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) { + _LOG(log, scope_flags, "cannot get siginfo for %d: %s\n", tid, strerror(errno)); + return; + } + if (!signal_has_address(si.si_signo)) { + return; + } - _LOG(log, scope_flags, "\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. - */ - const backtrace_map_info_t* map = map_info_list; - const backtrace_map_info_t* next = NULL; - const backtrace_map_info_t* prev = NULL; - while (map != NULL) { - if (addr >= map->start && addr < map->end) { - next = map->next; - break; - } else if (addr >= map->end) { - /* map would be between "prev" and this entry */ - next = map; - map = NULL; - break; - } + uintptr_t addr = (uintptr_t) si.si_addr; + addr &= ~0xfff; // round to 4K page boundary + if (addr == 0) { // null-pointer deref + return; + } - prev = map; - map = map->next; + _LOG(log, scope_flags, "\nmemory map around fault addr %" PRIxPTR ":\n", + reinterpret_cast<uintptr_t>(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. + const backtrace_map_info_t* map = map_info_list; + const backtrace_map_info_t* next = NULL; + const backtrace_map_info_t* prev = NULL; + while (map != NULL) { + if (addr >= map->start && addr < map->end) { + next = map->next; + break; + } else if (addr >= map->end) { + // map would be between "prev" and this entry + next = map; + map = NULL; + break; } - /* - * Show "next" then "match" then "prev" so that the addresses appear in - * ascending order (like /proc/pid/maps). - */ - dump_map(log, next, "map below", scope_flags); - dump_map(log, map, "map for address", scope_flags); - dump_map(log, prev, "map above", scope_flags); + prev = map; + map = map->next; + } + + // Show "next" then "match" then "prev" so that the addresses appear in + // ascending order (like /proc/pid/maps). + dump_map(log, next, "map below", scope_flags); + dump_map(log, map, "map for address", scope_flags); + dump_map(log, prev, "map above", scope_flags); } -static void dump_thread(const backtrace_context_t* context, log_t* log, - int scope_flags, int* total_sleep_time_usec) { - const backtrace_t* backtrace = context->backtrace; - wait_for_stop(backtrace->tid, total_sleep_time_usec); +static void dump_thread( + Backtrace* backtrace, log_t* log, int scope_flags, int* total_sleep_time_usec) { + wait_for_stop(backtrace->Tid(), total_sleep_time_usec); - dump_registers(log, backtrace->tid, scope_flags); - dump_backtrace_and_stack(context, log, scope_flags); - if (IS_AT_FAULT(scope_flags)) { - dump_memory_and_code(log, backtrace->tid, scope_flags); - dump_nearby_maps(backtrace->map_info_list, log, backtrace->tid, scope_flags); - } + dump_registers(log, backtrace->Tid(), scope_flags); + dump_backtrace_and_stack(backtrace, log, scope_flags); + if (IS_AT_FAULT(scope_flags)) { + dump_memory_and_code(log, backtrace->Tid(), scope_flags); + dump_nearby_maps(backtrace->GetMapList(), log, backtrace->Tid(), scope_flags); + } } -/* Return true if some thread is not detached cleanly */ +// Return true if some thread is not detached cleanly static bool dump_sibling_thread_report( - log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec, backtrace_map_info_t* map_info) { - char task_path[64]; - snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); - - DIR* d = opendir(task_path); - /* Bail early if the task directory cannot be opened */ - if (d == NULL) { - XLOG("Cannot open /proc/%d/task\n", pid); - return false; + log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec, backtrace_map_info_t* map_info) { + char task_path[64]; + snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); + + DIR* d = opendir(task_path); + // Bail early if the task directory cannot be opened + if (d == NULL) { + XLOG("Cannot open /proc/%d/task\n", pid); + return false; + } + + bool detach_failed = false; + struct dirent* de; + while ((de = readdir(d)) != NULL) { + // Ignore "." and ".." + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { + continue; } - bool detach_failed = false; - struct dirent* de; - while ((de = readdir(d)) != NULL) { - /* Ignore "." and ".." */ - if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { - continue; - } + // The main thread at fault has been handled individually + char* end; + pid_t new_tid = strtoul(de->d_name, &end, 10); + if (*end || new_tid == tid) { + continue; + } - /* The main thread at fault has been handled individually */ - char* end; - pid_t new_tid = strtoul(de->d_name, &end, 10); - if (*end || new_tid == tid) { - continue; - } + // Skip this thread if cannot ptrace it + if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) { + continue; + } - /* Skip this thread if cannot ptrace it */ - if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) { - continue; - } + _LOG(log, 0, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n"); + dump_thread_info(log, pid, new_tid, 0); - _LOG(log, 0, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n"); - dump_thread_info(log, pid, new_tid, 0); - backtrace_context_t new_context; - if (backtrace_create_context_with_map(&new_context, pid, new_tid, 0, map_info)) { - dump_thread(&new_context, log, 0, total_sleep_time_usec); - backtrace_destroy_context(&new_context); - } + UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, new_tid, map_info)); + if (backtrace->Unwind(0)) { + dump_thread(backtrace.get(), log, 0, total_sleep_time_usec); + } - if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) { - LOG("ptrace detach from %d failed: %s\n", new_tid, strerror(errno)); - detach_failed = true; - } + if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) { + LOG("ptrace detach from %d failed: %s\n", new_tid, strerror(errno)); + detach_failed = true; } + } - closedir(d); - return detach_failed; + closedir(d); + return detach_failed; } -/* - * Reads the contents of the specified log device, filters out the entries - * that don't match the specified pid, and writes them to the tombstone file. - * - * If "tailOnly" is set, we only print the last few lines. - */ -static void dump_log_file(log_t* log, pid_t pid, const char* filename, - bool tailOnly) -{ - bool first = true; - - /* circular buffer, for "tailOnly" mode */ - const int kShortLogMaxLines = 5; - const int kShortLogLineLen = 256; - char shortLog[kShortLogMaxLines][kShortLogLineLen]; - int shortLogCount = 0; - int shortLogNext = 0; - - int logfd = open(filename, O_RDONLY | O_NONBLOCK); - if (logfd < 0) { - XLOG("Unable to open %s: %s\n", filename, strerror(errno)); - return; +// Reads the contents of the specified log device, filters out the entries +// that don't match the specified pid, and writes them to the tombstone file. +// +// If "tailOnly" is set, we only print the last few lines. +static void dump_log_file(log_t* log, pid_t pid, const char* filename, bool tailOnly) { + bool first = true; + + // circular buffer, for "tailOnly" mode + const int kShortLogMaxLines = 5; + const int kShortLogLineLen = 256; + char shortLog[kShortLogMaxLines][kShortLogLineLen]; + int shortLogCount = 0; + int shortLogNext = 0; + + int logfd = open(filename, O_RDONLY | O_NONBLOCK); + if (logfd < 0) { + XLOG("Unable to open %s: %s\n", filename, strerror(errno)); + return; + } + + union { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; + struct logger_entry entry; + } log_entry; + + while (true) { + ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN); + if (actual < 0) { + if (errno == EINTR) { + // interrupted by signal, retry + continue; + } else if (errno == EAGAIN) { + // non-blocking EOF; we're done + break; + } else { + _LOG(log, 0, "Error while reading log: %s\n", strerror(errno)); + break; + } + } else if (actual == 0) { + _LOG(log, 0, "Got zero bytes while reading log: %s\n", strerror(errno)); + break; } - union { - unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; - struct logger_entry entry; - } log_entry; - - while (true) { - ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN); - if (actual < 0) { - if (errno == EINTR) { - /* interrupted by signal, retry */ - continue; - } else if (errno == EAGAIN) { - /* non-blocking EOF; we're done */ - break; - } else { - _LOG(log, 0, "Error while reading log: %s\n", - strerror(errno)); - break; - } - } else if (actual == 0) { - _LOG(log, 0, "Got zero bytes while reading log: %s\n", - strerror(errno)); - break; - } + // NOTE: if you XLOG something here, this will spin forever, + // because you will be writing as fast as you're reading. Any + // high-frequency debug diagnostics should just be written to + // the tombstone file. + struct logger_entry* entry = &log_entry.entry; - /* - * NOTE: if you XLOG something here, this will spin forever, - * because you will be writing as fast as you're reading. Any - * high-frequency debug diagnostics should just be written to - * the tombstone file. - */ + if (entry->pid != static_cast<int32_t>(pid)) { + // wrong pid, ignore + continue; + } - struct logger_entry* entry = &log_entry.entry; + if (first) { + _LOG(log, 0, "--------- %slog %s\n", tailOnly ? "tail end of " : "", filename); + first = false; + } - if (entry->pid != (int32_t) pid) { - /* wrong pid, ignore */ - continue; - } + // Msg format is: <priority:1><tag:N>\0<message:N>\0 + // + // We want to display it in the same format as "logcat -v threadtime" + // (although in this case the pid is redundant). + // + // TODO: scan for line breaks ('\n') and display each text line + // on a separate line, prefixed with the header, like logcat does. + static const char* kPrioChars = "!.VDIWEFS"; + unsigned char prio = entry->msg[0]; + char* tag = entry->msg + 1; + char* msg = tag + strlen(tag) + 1; - if (first) { - _LOG(log, 0, "--------- %slog %s\n", - tailOnly ? "tail end of " : "", filename); - first = false; - } + // consume any trailing newlines + char* eatnl = msg + strlen(msg) - 1; + while (eatnl >= msg && *eatnl == '\n') { + *eatnl-- = '\0'; + } - /* - * Msg format is: <priority:1><tag:N>\0<message:N>\0 - * - * We want to display it in the same format as "logcat -v threadtime" - * (although in this case the pid is redundant). - * - * TODO: scan for line breaks ('\n') and display each text line - * on a separate line, prefixed with the header, like logcat does. - */ - static const char* kPrioChars = "!.VDIWEFS"; - unsigned char prio = entry->msg[0]; - char* tag = entry->msg + 1; - char* msg = tag + strlen(tag) + 1; - - /* consume any trailing newlines */ - char* eatnl = msg + strlen(msg) - 1; - while (eatnl >= msg && *eatnl == '\n') { - *eatnl-- = '\0'; - } + char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?'); - char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?'); - - char timeBuf[32]; - time_t sec = (time_t) entry->sec; - struct tm tmBuf; - struct tm* ptm; - ptm = localtime_r(&sec, &tmBuf); - strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); - - if (tailOnly) { - snprintf(shortLog[shortLogNext], kShortLogLineLen, - "%s.%03d %5d %5d %c %-8s: %s", - timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, - prioChar, tag, msg); - shortLogNext = (shortLogNext + 1) % kShortLogMaxLines; - shortLogCount++; - } else { - _LOG(log, 0, "%s.%03d %5d %5d %c %-8s: %s\n", - timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, - prioChar, tag, msg); - } - } + char timeBuf[32]; + time_t sec = static_cast<time_t>(entry->sec); + struct tm tmBuf; + struct tm* ptm; + ptm = localtime_r(&sec, &tmBuf); + strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); if (tailOnly) { - int i; - - /* - * If we filled the buffer, we want to start at "next", which has - * the oldest entry. If we didn't, we want to start at zero. - */ - if (shortLogCount < kShortLogMaxLines) { - shortLogNext = 0; - } else { - shortLogCount = kShortLogMaxLines; /* cap at window size */ - } + snprintf(shortLog[shortLogNext], kShortLogLineLen, + "%s.%03d %5d %5d %c %-8s: %s", + timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, + prioChar, tag, msg); + shortLogNext = (shortLogNext + 1) % kShortLogMaxLines; + shortLogCount++; + } else { + _LOG(log, 0, "%s.%03d %5d %5d %c %-8s: %s\n", + timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, prioChar, tag, msg); + } + } - for (i = 0; i < shortLogCount; i++) { - _LOG(log, 0, "%s\n", shortLog[shortLogNext]); - shortLogNext = (shortLogNext + 1) % kShortLogMaxLines; - } + if (tailOnly) { + int i; + + // If we filled the buffer, we want to start at "next", which has + // the oldest entry. If we didn't, we want to start at zero. + if (shortLogCount < kShortLogMaxLines) { + shortLogNext = 0; + } else { + shortLogCount = kShortLogMaxLines; // cap at window size + } + + for (i = 0; i < shortLogCount; i++) { + _LOG(log, 0, "%s\n", shortLog[shortLogNext]); + shortLogNext = (shortLogNext + 1) % kShortLogMaxLines; } + } - close(logfd); + close(logfd); } -/* - * 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(log_t* log, pid_t pid, bool tailOnly) -{ - dump_log_file(log, pid, "/dev/log/system", tailOnly); - dump_log_file(log, pid, "/dev/log/main", tailOnly); +// 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(log_t* log, pid_t pid, bool tailOnly) { + dump_log_file(log, pid, "/dev/log/system", tailOnly); + dump_log_file(log, pid, "/dev/log/main", tailOnly); } -static void dump_abort_message(const backtrace_context_t* context, log_t* log, uintptr_t address) { +static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t address) { if (address == 0) { return; } @@ -620,7 +587,7 @@ static void dump_abort_message(const backtrace_context_t* context, log_t* log, u char* p = &msg[0]; while (p < &msg[sizeof(msg)]) { uint32_t data; - if (!backtrace_read_word(context, address, &data)) { + if (!backtrace->ReadWord(address, &data)) { break; } address += sizeof(uint32_t); @@ -643,186 +610,175 @@ static void dump_abort_message(const backtrace_context_t* context, log_t* log, u _LOG(log, SCOPE_AT_FAULT, "Abort message: '%s'\n", msg); } -/* - * Dumps all information about the specified pid to the tombstone. - */ +// Dumps all information about the specified pid to the tombstone. static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, - bool dump_sibling_threads, int* total_sleep_time_usec) -{ - /* 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 want_logs = (value[0] == '1'); - - if (log->amfd >= 0) { - /* - * Activity Manager protocol: binary 32-bit network-byte-order ints for the - * pid and signal number, followed by the raw text of the dump, culminating - * in a zero byte that marks end-of-data. - */ - uint32_t datum = htonl(pid); - TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) ); - datum = htonl(signal); - TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) ); - } - - _LOG(log, SCOPE_AT_FAULT, - "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); - dump_build_info(log); - dump_revision_info(log); - dump_thread_info(log, pid, tid, SCOPE_AT_FAULT); - if (signal) { - dump_fault_addr(log, tid, signal); - } + bool dump_sibling_threads, int* total_sleep_time_usec) { + // 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 want_logs = (value[0] == '1'); + + if (log->amfd >= 0) { + // Activity Manager protocol: binary 32-bit network-byte-order ints for the + // pid and signal number, followed by the raw text of the dump, culminating + // in a zero byte that marks end-of-data. + uint32_t datum = htonl(pid); + TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) ); + datum = htonl(signal); + TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) ); + } - backtrace_context_t context; - /* Gather the map info once for all this process' threads. */ - backtrace_map_info_t* map_info = backtrace_create_map_info_list(pid); - if (backtrace_create_context_with_map(&context, pid, tid, 0, map_info)) { - dump_abort_message(&context, log, abort_msg_address); - dump_thread(&context, log, SCOPE_AT_FAULT, total_sleep_time_usec); - backtrace_destroy_context(&context); - } + _LOG(log, SCOPE_AT_FAULT, + "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); + dump_build_info(log); + dump_revision_info(log); + dump_thread_info(log, pid, tid, SCOPE_AT_FAULT); + if (signal) { + dump_fault_addr(log, tid, signal); + } - if (want_logs) { - dump_logs(log, pid, true); - } + // Gather the map info once for all this process' threads. + backtrace_map_info_t* map_info = backtrace_create_map_info_list(pid); - bool detach_failed = false; - if (dump_sibling_threads) { - detach_failed = dump_sibling_thread_report(log, pid, tid, total_sleep_time_usec, map_info); - } + UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, tid, map_info)); + if (backtrace->Unwind(0)) { + dump_abort_message(backtrace.get(), log, abort_msg_address); + dump_thread(backtrace.get(), log, SCOPE_AT_FAULT, total_sleep_time_usec); + } - /* Destroy the previously created map info. */ - backtrace_destroy_map_info_list(map_info); + if (want_logs) { + dump_logs(log, pid, true); + } - if (want_logs) { - dump_logs(log, pid, false); - } + bool detach_failed = false; + if (dump_sibling_threads) { + detach_failed = dump_sibling_thread_report(log, pid, tid, total_sleep_time_usec, map_info); + } - /* send EOD to the Activity Manager, then wait for its ack to avoid racing ahead - * and killing the target out from under it */ - if (log->amfd >= 0) { - uint8_t eodMarker = 0; - TEMP_FAILURE_RETRY( write(log->amfd, &eodMarker, 1) ); - /* 3 sec timeout reading the ack; we're fine if that happens */ - TEMP_FAILURE_RETRY( read(log->amfd, &eodMarker, 1) ); - } + // Destroy the previously created map info. + backtrace_destroy_map_info_list(map_info); - return detach_failed; -} + if (want_logs) { + dump_logs(log, pid, false); + } -/* - * 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. - * - * Returns the path of the tombstone file, allocated using malloc(). Caller must free() it. - */ -static char* find_and_open_tombstone(int* fd) -{ - unsigned long mtime = ULONG_MAX; - struct stat sb; - - /* - * 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. - */ - char path[128]; - int oldest = 0; - for (int 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; + // send EOD to the Activity Manager, then wait for its ack to avoid racing ahead + // and killing the target out from under it + if (log->amfd >= 0) { + uint8_t eodMarker = 0; + TEMP_FAILURE_RETRY( write(log->amfd, &eodMarker, 1) ); + // 3 sec timeout reading the ack; we're fine if that happens + TEMP_FAILURE_RETRY( read(log->amfd, &eodMarker, 1) ); + } - *fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600); - if (*fd < 0) - continue; /* raced ? */ + return detach_failed; +} - fchown(*fd, AID_SYSTEM, AID_SYSTEM); - return strdup(path); - } +// 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. +// +// Returns the path of the tombstone file, allocated using malloc(). Caller must free() it. +static char* find_and_open_tombstone(int* fd) { + unsigned long mtime = ULONG_MAX; + struct stat sb; + + // 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. + char path[128]; + int oldest = 0; + for (int 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 ? - /* 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); - if (*fd < 0) { - LOG("failed to open tombstone file '%s': %s\n", path, strerror(errno)); - return NULL; - } fchown(*fd, AID_SYSTEM, AID_SYSTEM); return strdup(path); + } + + // 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); + if (*fd < 0) { + LOG("failed to open tombstone file '%s': %s\n", path, strerror(errno)); + return NULL; + } + fchown(*fd, AID_SYSTEM, AID_SYSTEM); + return strdup(path); } static int activity_manager_connect() { - int amfd = socket(PF_UNIX, SOCK_STREAM, 0); - if (amfd >= 0) { - struct sockaddr_un address; - int err; - - memset(&address, 0, sizeof(address)); - address.sun_family = AF_UNIX; - strncpy(address.sun_path, NCRASH_SOCKET_PATH, sizeof(address.sun_path)); - err = TEMP_FAILURE_RETRY( connect(amfd, (struct sockaddr*) &address, sizeof(address)) ); - if (!err) { - struct timeval tv; - memset(&tv, 0, sizeof(tv)); - tv.tv_sec = 1; // tight leash - err = setsockopt(amfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); - if (!err) { - tv.tv_sec = 3; // 3 seconds on handshake read - err = setsockopt(amfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - } - } - if (err) { - close(amfd); - amfd = -1; - } + int amfd = socket(PF_UNIX, SOCK_STREAM, 0); + if (amfd >= 0) { + struct sockaddr_un address; + int err; + + memset(&address, 0, sizeof(address)); + address.sun_family = AF_UNIX; + strncpy(address.sun_path, NCRASH_SOCKET_PATH, sizeof(address.sun_path)); + err = TEMP_FAILURE_RETRY(connect( + amfd, reinterpret_cast<struct sockaddr*>(&address), sizeof(address))); + if (!err) { + struct timeval tv; + memset(&tv, 0, sizeof(tv)); + tv.tv_sec = 1; // tight leash + err = setsockopt(amfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + if (!err) { + tv.tv_sec = 3; // 3 seconds on handshake read + err = setsockopt(amfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + } + } + if (err) { + close(amfd); + amfd = -1; } + } - return amfd; + return amfd; } -char* engrave_tombstone(pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, - bool dump_sibling_threads, bool quiet, bool* detach_failed, - int* total_sleep_time_usec) { - mkdir(TOMBSTONE_DIR, 0755); - chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM); +char* engrave_tombstone( + pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, bool dump_sibling_threads, + bool quiet, bool* detach_failed, int* total_sleep_time_usec) { + mkdir(TOMBSTONE_DIR, 0755); + chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM); - if (selinux_android_restorecon(TOMBSTONE_DIR) == -1) { - *detach_failed = false; - return NULL; - } + if (selinux_android_restorecon(TOMBSTONE_DIR) == -1) { + *detach_failed = false; + return NULL; + } - int fd; - char* path = find_and_open_tombstone(&fd); - if (!path) { - *detach_failed = false; - return NULL; - } + int fd; + char* path = find_and_open_tombstone(&fd); + if (!path) { + *detach_failed = false; + return NULL; + } - log_t log; - log.tfd = fd; - log.amfd = activity_manager_connect(); - log.quiet = quiet; - *detach_failed = dump_crash(&log, pid, tid, signal, abort_msg_address, dump_sibling_threads, - total_sleep_time_usec); + log_t log; + log.tfd = fd; + log.amfd = activity_manager_connect(); + log.quiet = quiet; + *detach_failed = dump_crash( + &log, pid, tid, signal, abort_msg_address, dump_sibling_threads, total_sleep_time_usec); - close(log.amfd); - close(fd); - return path; + close(log.amfd); + close(fd); + return path; } diff --git a/debuggerd/tombstone.h b/debuggerd/tombstone.h index d4a1a96..e9878bf 100644 --- a/debuggerd/tombstone.h +++ b/debuggerd/tombstone.h @@ -21,8 +21,6 @@ #include <stdbool.h> #include <sys/types.h> -#include <corkscrew/ptrace.h> - /* Creates a tombstone file and writes the crash dump to it. * Returns the path of the tombstone, which must be freed using free(). */ char* engrave_tombstone(pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp index 41be982..35f061e 100644 --- a/debuggerd/utility.cpp +++ b/debuggerd/utility.cpp @@ -1,22 +1,20 @@ -/* system/debuggerd/utility.c -** -** Copyright 2008, 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. -*/ +/* + * Copyright 2008, 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 <stddef.h> -#include <stdbool.h> #include <stdio.h> #include <string.h> #include <errno.h> @@ -30,101 +28,102 @@ #include "utility.h" -const int sleep_time_usec = 50000; /* 0.05 seconds */ -const int max_total_sleep_usec = 10000000; /* 10 seconds */ +const int sleep_time_usec = 50000; // 0.05 seconds +const int max_total_sleep_usec = 10000000; // 10 seconds static int write_to_am(int fd, const char* buf, int len) { - int to_write = len; - while (to_write > 0) { - int written = TEMP_FAILURE_RETRY( write(fd, buf + len - to_write, to_write) ); - if (written < 0) { - /* hard failure */ - LOG("AM write failure (%d / %s)\n", errno, strerror(errno)); - return -1; - } - to_write -= written; + int to_write = len; + while (to_write > 0) { + int written = TEMP_FAILURE_RETRY( write(fd, buf + len - to_write, to_write) ); + if (written < 0) { + // hard failure + LOG("AM write failure (%d / %s)\n", errno, strerror(errno)); + return -1; } - return len; + to_write -= written; + } + return len; } -void _LOG(log_t* log, int scopeFlags, const char *fmt, ...) { - char buf[512]; - bool want_tfd_write; - bool want_log_write; - bool want_amfd_write; - int len = 0; +void _LOG(log_t* log, int scopeFlags, const char* fmt, ...) { + char buf[512]; + bool want_tfd_write; + bool want_log_write; + bool want_amfd_write; + int len = 0; - va_list ap; - va_start(ap, fmt); + va_list ap; + va_start(ap, fmt); - // where is the information going to go? - want_tfd_write = log && log->tfd >= 0; - want_log_write = IS_AT_FAULT(scopeFlags) && (!log || !log->quiet); - want_amfd_write = IS_AT_FAULT(scopeFlags) && !IS_SENSITIVE(scopeFlags) && log && log->amfd >= 0; + // where is the information going to go? + want_tfd_write = log && log->tfd >= 0; + want_log_write = IS_AT_FAULT(scopeFlags) && (!log || !log->quiet); + want_amfd_write = IS_AT_FAULT(scopeFlags) && !IS_SENSITIVE(scopeFlags) && log && log->amfd >= 0; - // if we're going to need the literal string, generate it once here - if (want_tfd_write || want_amfd_write) { - vsnprintf(buf, sizeof(buf), fmt, ap); - len = strlen(buf); - } + // if we're going to need the literal string, generate it once here + if (want_tfd_write || want_amfd_write) { + vsnprintf(buf, sizeof(buf), fmt, ap); + len = strlen(buf); + } - if (want_tfd_write) { - write(log->tfd, buf, len); - } + if (want_tfd_write) { + write(log->tfd, buf, len); + } - if (want_log_write) { - // whatever goes to logcat also goes to the Activity Manager - __android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap); - if (want_amfd_write && len > 0) { - int written = write_to_am(log->amfd, buf, len); - if (written <= 0) { - // timeout or other failure on write; stop informing the activity manager - log->amfd = -1; - } - } + if (want_log_write) { + // whatever goes to logcat also goes to the Activity Manager + __android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap); + if (want_amfd_write && len > 0) { + int written = write_to_am(log->amfd, buf, len); + if (written <= 0) { + // timeout or other failure on write; stop informing the activity manager + log->amfd = -1; + } } - va_end(ap); + } + va_end(ap); } int wait_for_signal(pid_t tid, int* total_sleep_time_usec) { - for (;;) { - int status; - pid_t n = waitpid(tid, &status, __WALL | WNOHANG); - if (n < 0) { - if(errno == EAGAIN) continue; - LOG("waitpid failed: %s\n", strerror(errno)); - return -1; - } else if (n > 0) { - XLOG("waitpid: n=%d status=%08x\n", n, status); - if (WIFSTOPPED(status)) { - return WSTOPSIG(status); - } else { - LOG("unexpected waitpid response: n=%d, status=%08x\n", n, status); - return -1; - } - } - - if (*total_sleep_time_usec > max_total_sleep_usec) { - LOG("timed out waiting for tid=%d to die\n", tid); - return -1; - } + for (;;) { + int status; + pid_t n = waitpid(tid, &status, __WALL | WNOHANG); + if (n < 0) { + if (errno == EAGAIN) + continue; + LOG("waitpid failed: %s\n", strerror(errno)); + return -1; + } else if (n > 0) { + XLOG("waitpid: n=%d status=%08x\n", n, status); + if (WIFSTOPPED(status)) { + return WSTOPSIG(status); + } else { + LOG("unexpected waitpid response: n=%d, status=%08x\n", n, status); + return -1; + } + } - /* not ready yet */ - XLOG("not ready yet\n"); - usleep(sleep_time_usec); - *total_sleep_time_usec += sleep_time_usec; + if (*total_sleep_time_usec > max_total_sleep_usec) { + LOG("timed out waiting for tid=%d to die\n", tid); + return -1; } + + // not ready yet + XLOG("not ready yet\n"); + usleep(sleep_time_usec); + *total_sleep_time_usec += sleep_time_usec; + } } void wait_for_stop(pid_t tid, int* total_sleep_time_usec) { - siginfo_t si; - while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) < 0 && errno == ESRCH) { - if (*total_sleep_time_usec > max_total_sleep_usec) { - LOG("timed out waiting for tid=%d to stop\n", tid); - break; - } - - usleep(sleep_time_usec); - *total_sleep_time_usec += sleep_time_usec; + siginfo_t si; + while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) < 0 && errno == ESRCH) { + if (*total_sleep_time_usec > max_total_sleep_usec) { + LOG("timed out waiting for tid=%d to stop\n", tid); + break; } + + usleep(sleep_time_usec); + *total_sleep_time_usec += sleep_time_usec; + } } diff --git a/debuggerd/x86/machine.cpp b/debuggerd/x86/machine.cpp index db44b11..141f19a 100644 --- a/debuggerd/x86/machine.cpp +++ b/debuggerd/x86/machine.cpp @@ -1,21 +1,20 @@ /* -** 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. -*/ + * 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 <stddef.h> -#include <stdbool.h> #include <stdlib.h> #include <string.h> #include <stdio.h> @@ -30,17 +29,17 @@ void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags) { } void dump_registers(log_t* log, pid_t tid, int scope_flags) { - struct pt_regs r; - if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) { - _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); - return; - } - _LOG(log, scope_flags, " eax %08lx ebx %08lx ecx %08lx edx %08lx\n", - r.eax, r.ebx, r.ecx, r.edx); - _LOG(log, scope_flags, " esi %08lx edi %08lx\n", - r.esi, r.edi); - _LOG(log, scope_flags, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n", - r.xcs, r.xds, r.xes, r.xfs, r.xss); - _LOG(log, scope_flags, " eip %08lx ebp %08lx esp %08lx flags %08lx\n", - r.eip, r.ebp, r.esp, r.eflags); + struct pt_regs r; + if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) { + _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); + return; + } + _LOG(log, scope_flags, " eax %08lx ebx %08lx ecx %08lx edx %08lx\n", + r.eax, r.ebx, r.ecx, r.edx); + _LOG(log, scope_flags, " esi %08lx edi %08lx\n", + r.esi, r.edi); + _LOG(log, scope_flags, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n", + r.xcs, r.xds, r.xes, r.xfs, r.xss); + _LOG(log, scope_flags, " eip %08lx ebp %08lx esp %08lx flags %08lx\n", + r.eip, r.ebp, r.esp, r.eflags); } diff --git a/include/backtrace/Backtrace.h b/include/backtrace/Backtrace.h index bf4efd3..df40b87 100644 --- a/include/backtrace/Backtrace.h +++ b/include/backtrace/Backtrace.h @@ -60,6 +60,7 @@ public: // Create a string representing the formatted line of backtrace information // for a single frame. virtual std::string FormatFrameData(size_t frame_num); + virtual std::string FormatFrameData(const backtrace_frame_data_t* frame); pid_t Pid() { return backtrace_.pid; } pid_t Tid() { return backtrace_.tid; } @@ -68,9 +69,16 @@ public: const backtrace_t* GetBacktrace() { return &backtrace_; } const backtrace_frame_data_t* GetFrame(size_t frame_num) { + if (frame_num > NumFrames()) { + return NULL; + } return &backtrace_.frames[frame_num]; } + const backtrace_map_info_t* GetMapList() { + return map_info_; + } + protected: Backtrace(BacktraceImpl* impl, pid_t pid, backtrace_map_info_t* map_info); diff --git a/include/backtrace/backtrace.h b/include/backtrace/backtrace.h index fa81f21..cfcbf0f 100644 --- a/include/backtrace/backtrace.h +++ b/include/backtrace/backtrace.h @@ -44,6 +44,7 @@ typedef struct backtrace_map_info { } backtrace_map_info_t; typedef struct { + size_t num; /* The current fame number. */ uintptr_t pc; /* The absolute pc. */ uintptr_t sp; /* The top of the stack. */ size_t stack_size; /* The size of the stack, zero indicate an unknown stack size. */ diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp index a7568e0..fa85872 100644 --- a/libbacktrace/Backtrace.cpp +++ b/libbacktrace/Backtrace.cpp @@ -125,7 +125,10 @@ const backtrace_map_info_t* Backtrace::FindMapInfo(uintptr_t ptr) { } std::string Backtrace::FormatFrameData(size_t frame_num) { - backtrace_frame_data_t* frame = &backtrace_.frames[frame_num]; + return FormatFrameData(&backtrace_.frames[frame_num]); +} + +std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) { const char* map_name; if (frame->map_name) { map_name = frame->map_name; @@ -142,13 +145,13 @@ std::string Backtrace::FormatFrameData(size_t frame_num) { char buf[512]; if (frame->func_name && frame->func_offset) { snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s+%" PRIuPTR ")", - frame_num, (int)sizeof(uintptr_t)*2, relative_pc, map_name, + frame->num, (int)sizeof(uintptr_t)*2, relative_pc, map_name, frame->func_name, frame->func_offset); } else if (frame->func_name) { - snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s)", frame_num, + snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s)", frame->num, (int)sizeof(uintptr_t)*2, relative_pc, map_name, frame->func_name); } else { - snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s", frame_num, + snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s", frame->num, (int)sizeof(uintptr_t)*2, relative_pc, map_name); } diff --git a/libbacktrace/Corkscrew.cpp b/libbacktrace/Corkscrew.cpp index eae13c6..63b8c7c 100644 --- a/libbacktrace/Corkscrew.cpp +++ b/libbacktrace/Corkscrew.cpp @@ -45,6 +45,7 @@ bool CorkscrewCommon::GenerateFrameData( data->num_frames = num_frames; for (size_t i = 0; i < data->num_frames; i++) { backtrace_frame_data_t* frame = &data->frames[i]; + frame->num = i; frame->pc = cork_frames[i].absolute_pc; frame->sp = cork_frames[i].stack_top; frame->stack_size = cork_frames[i].stack_size; @@ -146,6 +147,7 @@ void CorkscrewThread::ThreadUnwind( data->num_frames = num_frames; for (size_t i = 0; i < data->num_frames; i++) { backtrace_frame_data_t* frame = &data->frames[i]; + frame->num = i; frame->pc = frames[i].absolute_pc; frame->sp = frames[i].stack_top; frame->stack_size = frames[i].stack_size; diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp index 94a82fe..6cdbc42 100644 --- a/libbacktrace/UnwindCurrent.cpp +++ b/libbacktrace/UnwindCurrent.cpp @@ -108,6 +108,7 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { if (num_ignore_frames == 0) { size_t num_frames = backtrace->num_frames; backtrace_frame_data_t* frame = &backtrace->frames[num_frames]; + frame->num = num_frames; frame->pc = static_cast<uintptr_t>(pc); frame->sp = static_cast<uintptr_t>(sp); frame->stack_size = 0; diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp index 7a9bbdd..6fecb76 100644 --- a/libbacktrace/UnwindPtrace.cpp +++ b/libbacktrace/UnwindPtrace.cpp @@ -82,6 +82,7 @@ bool UnwindPtrace::Unwind(size_t num_ignore_frames) { if (num_ignore_frames == 0) { size_t num_frames = backtrace->num_frames; backtrace_frame_data_t* frame = &backtrace->frames[num_frames]; + frame->num = num_frames; frame->pc = static_cast<uintptr_t>(pc); frame->sp = static_cast<uintptr_t>(sp); frame->stack_size = 0; diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp index b894446..2fed993 100644 --- a/libbacktrace/backtrace_test.cpp +++ b/libbacktrace/backtrace_test.cpp @@ -28,7 +28,8 @@ #include <time.h> #include <unistd.h> -#include <backtrace/backtrace.h> +#include <backtrace/Backtrace.h> +#include <UniquePtr.h> #include <cutils/atomic.h> #include <gtest/gtest.h> @@ -57,7 +58,7 @@ typedef struct { typedef struct { thread_t thread; - backtrace_context_t context; + Backtrace* backtrace; int32_t* now; int32_t done; } dump_thread_t; @@ -75,15 +76,14 @@ uint64_t NanoTime() { return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec); } -void DumpFrames(const backtrace_context_t* context) { - if (context->backtrace->num_frames == 0) { +void DumpFrames(Backtrace* backtrace) { + if (backtrace->NumFrames() == 0) { printf(" No frames to dump\n"); - } else { - char line[512]; - for (size_t i = 0; i < context->backtrace->num_frames; i++) { - backtrace_format_frame_data(context, i, line, sizeof(line)); - printf(" %s\n", line); - } + return; + } + + for (size_t i = 0; i < backtrace->NumFrames(); i++) { + printf(" %s\n", backtrace->FormatFrameData(i).c_str()); } } @@ -100,12 +100,12 @@ void WaitForStop(pid_t pid) { } } -bool ReadyLevelBacktrace(const backtrace_t* backtrace) { +bool ReadyLevelBacktrace(Backtrace* backtrace) { // See if test_level_four is in the backtrace. bool found = false; - for (size_t i = 0; i < backtrace->num_frames; i++) { - if (backtrace->frames[i].func_name != NULL && - strcmp(backtrace->frames[i].func_name, "test_level_four") == 0) { + for (size_t i = 0; i < backtrace->NumFrames(); i++) { + const backtrace_frame_data_t* frame = backtrace->GetFrame(i); + if (frame->func_name != NULL && strcmp(frame->func_name, "test_level_four") == 0) { found = true; break; } @@ -114,64 +114,61 @@ bool ReadyLevelBacktrace(const backtrace_t* backtrace) { return found; } -void VerifyLevelDump(const backtrace_t* backtrace) { - ASSERT_GT(backtrace->num_frames, static_cast<size_t>(0)); - ASSERT_LT(backtrace->num_frames, static_cast<size_t>(MAX_BACKTRACE_FRAMES)); +void VerifyLevelDump(Backtrace* backtrace) { + ASSERT_GT(backtrace->NumFrames(), static_cast<size_t>(0)); + ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES)); // Look through the frames starting at the highest to find the // frame we want. size_t frame_num = 0; - for (size_t i = backtrace->num_frames-1; i > 2; i--) { - if (backtrace->frames[i].func_name != NULL && - strcmp(backtrace->frames[i].func_name, "test_level_one") == 0) { + for (size_t i = backtrace->NumFrames()-1; i > 2; i--) { + if (backtrace->GetFrame(i)->func_name != NULL && + strcmp(backtrace->GetFrame(i)->func_name, "test_level_one") == 0) { frame_num = i; break; } } - ASSERT_GT(frame_num, static_cast<size_t>(0)); + ASSERT_LT(static_cast<size_t>(0), frame_num); + ASSERT_LE(static_cast<size_t>(3), frame_num); - ASSERT_TRUE(NULL != backtrace->frames[frame_num].func_name); - ASSERT_STREQ(backtrace->frames[frame_num].func_name, "test_level_one"); - ASSERT_TRUE(NULL != backtrace->frames[frame_num-1].func_name); - ASSERT_STREQ(backtrace->frames[frame_num-1].func_name, "test_level_two"); - ASSERT_TRUE(NULL != backtrace->frames[frame_num-2].func_name); - ASSERT_STREQ(backtrace->frames[frame_num-2].func_name, "test_level_three"); - ASSERT_TRUE(NULL != backtrace->frames[frame_num-3].func_name); - ASSERT_STREQ(backtrace->frames[frame_num-3].func_name, "test_level_four"); + ASSERT_TRUE(NULL != backtrace->GetFrame(frame_num)->func_name); + ASSERT_STREQ(backtrace->GetFrame(frame_num)->func_name, "test_level_one"); + ASSERT_TRUE(NULL != backtrace->GetFrame(frame_num-1)->func_name); + ASSERT_STREQ(backtrace->GetFrame(frame_num-1)->func_name, "test_level_two"); + ASSERT_TRUE(NULL != backtrace->GetFrame(frame_num-2)->func_name); + ASSERT_STREQ(backtrace->GetFrame(frame_num-2)->func_name, "test_level_three"); + ASSERT_TRUE(NULL != backtrace->GetFrame(frame_num-3)->func_name); + ASSERT_STREQ(backtrace->GetFrame(frame_num-3)->func_name, "test_level_four"); } void VerifyLevelBacktrace(void*) { - backtrace_context_t context; + UniquePtr<Backtrace> backtrace( + Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(backtrace.get() != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); - ASSERT_TRUE(backtrace_create_context(&context, BACKTRACE_CURRENT_PROCESS, - BACKTRACE_CURRENT_THREAD, 0)); - - VerifyLevelDump(context.backtrace); - - backtrace_destroy_context(&context); + VerifyLevelDump(backtrace.get()); } -bool ReadyMaxBacktrace(const backtrace_t* backtrace) { - return (backtrace->num_frames == MAX_BACKTRACE_FRAMES); +bool ReadyMaxBacktrace(Backtrace* backtrace) { + return (backtrace->NumFrames() == MAX_BACKTRACE_FRAMES); } -void VerifyMaxDump(const backtrace_t* backtrace) { - ASSERT_EQ(backtrace->num_frames, static_cast<size_t>(MAX_BACKTRACE_FRAMES)); +void VerifyMaxDump(Backtrace* backtrace) { + ASSERT_EQ(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES)); // Verify that the last frame is our recursive call. - ASSERT_TRUE(NULL != backtrace->frames[MAX_BACKTRACE_FRAMES-1].func_name); - ASSERT_STREQ(backtrace->frames[MAX_BACKTRACE_FRAMES-1].func_name, + ASSERT_TRUE(NULL != backtrace->GetFrame(MAX_BACKTRACE_FRAMES-1)->func_name); + ASSERT_STREQ(backtrace->GetFrame(MAX_BACKTRACE_FRAMES-1)->func_name, "test_recursive_call"); } void VerifyMaxBacktrace(void*) { - backtrace_context_t context; - - ASSERT_TRUE(backtrace_create_context(&context, BACKTRACE_CURRENT_PROCESS, - BACKTRACE_CURRENT_THREAD, 0)); + UniquePtr<Backtrace> backtrace( + Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(backtrace.get() != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); - VerifyMaxDump(context.backtrace); - - backtrace_destroy_context(&context); + VerifyMaxDump(backtrace.get()); } void ThreadSetState(void* data) { @@ -183,14 +180,12 @@ void ThreadSetState(void* data) { } } -void VerifyThreadTest(pid_t tid, void (*VerifyFunc)(const backtrace_t*)) { - backtrace_context_t context; - - backtrace_create_context(&context, getpid(), tid, 0); +void VerifyThreadTest(pid_t tid, void (*VerifyFunc)(Backtrace*)) { + UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), tid)); + ASSERT_TRUE(backtrace.get() != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); - VerifyFunc(context.backtrace); - - backtrace_destroy_context(&context); + VerifyFunc(backtrace.get()); } bool WaitForNonZero(int32_t* value, uint64_t seconds) { @@ -208,52 +203,47 @@ TEST(libbacktrace, local_trace) { } void VerifyIgnoreFrames( - const backtrace_t* bt_all, const backtrace_t* bt_ign1, - const backtrace_t* bt_ign2, const char* cur_proc) { - EXPECT_EQ(bt_all->num_frames, bt_ign1->num_frames + 1); - EXPECT_EQ(bt_all->num_frames, bt_ign2->num_frames + 2); + Backtrace* bt_all, Backtrace* bt_ign1, + Backtrace* bt_ign2, const char* cur_proc) { + EXPECT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1); + EXPECT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2); // Check all of the frames are the same > the current frame. bool check = (cur_proc == NULL); - for (size_t i = 0; i < bt_ign2->num_frames; i++) { + for (size_t i = 0; i < bt_ign2->NumFrames(); i++) { if (check) { - EXPECT_EQ(bt_ign2->frames[i].pc, bt_ign1->frames[i+1].pc); - EXPECT_EQ(bt_ign2->frames[i].sp, bt_ign1->frames[i+1].sp); - EXPECT_EQ(bt_ign2->frames[i].stack_size, bt_ign1->frames[i+1].stack_size); + EXPECT_EQ(bt_ign2->GetFrame(i)->pc, bt_ign1->GetFrame(i+1)->pc); + EXPECT_EQ(bt_ign2->GetFrame(i)->sp, bt_ign1->GetFrame(i+1)->sp); + EXPECT_EQ(bt_ign2->GetFrame(i)->stack_size, bt_ign1->GetFrame(i+1)->stack_size); - EXPECT_EQ(bt_ign2->frames[i].pc, bt_all->frames[i+2].pc); - EXPECT_EQ(bt_ign2->frames[i].sp, bt_all->frames[i+2].sp); - EXPECT_EQ(bt_ign2->frames[i].stack_size, bt_all->frames[i+2].stack_size); + EXPECT_EQ(bt_ign2->GetFrame(i)->pc, bt_all->GetFrame(i+2)->pc); + EXPECT_EQ(bt_ign2->GetFrame(i)->sp, bt_all->GetFrame(i+2)->sp); + EXPECT_EQ(bt_ign2->GetFrame(i)->stack_size, bt_all->GetFrame(i+2)->stack_size); } - if (!check && bt_ign2->frames[i].func_name && - strcmp(bt_ign2->frames[i].func_name, cur_proc) == 0) { + if (!check && bt_ign2->GetFrame(i)->func_name && + strcmp(bt_ign2->GetFrame(i)->func_name, cur_proc) == 0) { check = true; } } } void VerifyLevelIgnoreFrames(void*) { - backtrace_context_t all; - ASSERT_TRUE(backtrace_create_context(&all, BACKTRACE_CURRENT_PROCESS, - BACKTRACE_CURRENT_THREAD, 0)); - ASSERT_TRUE(all.backtrace != NULL); - - backtrace_context_t ign1; - ASSERT_TRUE(backtrace_create_context(&ign1, BACKTRACE_CURRENT_PROCESS, - BACKTRACE_CURRENT_THREAD, 1)); - ASSERT_TRUE(ign1.backtrace != NULL); + UniquePtr<Backtrace> all( + Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(all.get() != NULL); + ASSERT_TRUE(all->Unwind(0)); - backtrace_context_t ign2; - ASSERT_TRUE(backtrace_create_context(&ign2, BACKTRACE_CURRENT_PROCESS, - BACKTRACE_CURRENT_THREAD, 2)); - ASSERT_TRUE(ign2.backtrace != NULL); + UniquePtr<Backtrace> ign1( + Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(ign1.get() != NULL); + ASSERT_TRUE(ign1->Unwind(1)); - VerifyIgnoreFrames(all.backtrace, ign1.backtrace, ign2.backtrace, - "VerifyLevelIgnoreFrames"); + UniquePtr<Backtrace> ign2( + Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(ign2.get() != NULL); + ASSERT_TRUE(ign2->Unwind(2)); - backtrace_destroy_context(&all); - backtrace_destroy_context(&ign1); - backtrace_destroy_context(&ign2); + VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames"); } TEST(libbacktrace, local_trace_ignore_frames) { @@ -265,8 +255,8 @@ TEST(libbacktrace, local_max_trace) { } void VerifyProcTest(pid_t pid, pid_t tid, - bool (*ReadyFunc)(const backtrace_t*), - void (*VerifyFunc)(const backtrace_t*)) { + bool (*ReadyFunc)(Backtrace*), + void (*VerifyFunc)(Backtrace*)) { pid_t ptrace_tid; if (tid < 0) { ptrace_tid = pid; @@ -281,13 +271,14 @@ void VerifyProcTest(pid_t pid, pid_t tid, // Wait for the process to get to a stopping point. WaitForStop(ptrace_tid); - backtrace_context_t context; - ASSERT_TRUE(backtrace_create_context(&context, pid, tid, 0)); - if (ReadyFunc(context.backtrace)) { - VerifyFunc(context.backtrace); + UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, tid)); + ASSERT_TRUE(backtrace->Unwind(0)); + ASSERT_TRUE(backtrace.get() != NULL); + if (ReadyFunc(backtrace.get())) { + VerifyFunc(backtrace.get()); verified = true; } - backtrace_destroy_context(&context); + ASSERT_TRUE(ptrace(PTRACE_DETACH, ptrace_tid, 0, 0) == 0); } // If 5 seconds have passed, then we are done. @@ -321,21 +312,16 @@ TEST(libbacktrace, ptrace_max_trace) { ASSERT_EQ(waitpid(pid, &status, 0), pid); } -void VerifyProcessIgnoreFrames(const backtrace_t* bt_all) { - pid_t pid = bt_all->pid; - - backtrace_context_t ign1; - ASSERT_TRUE(backtrace_create_context(&ign1, pid, BACKTRACE_CURRENT_THREAD, 1)); - ASSERT_TRUE(ign1.backtrace != NULL); - - backtrace_context_t ign2; - ASSERT_TRUE(backtrace_create_context(&ign2, pid, BACKTRACE_CURRENT_THREAD, 2)); - ASSERT_TRUE(ign2.backtrace != NULL); +void VerifyProcessIgnoreFrames(Backtrace* bt_all) { + UniquePtr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(ign1.get() != NULL); + ASSERT_TRUE(ign1->Unwind(1)); - VerifyIgnoreFrames(bt_all, ign1.backtrace, ign2.backtrace, NULL); + UniquePtr<Backtrace> ign2(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(ign2.get() != NULL); + ASSERT_TRUE(ign2->Unwind(2)); - backtrace_destroy_context(&ign1); - backtrace_destroy_context(&ign2); + VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), NULL); } TEST(libbacktrace, ptrace_ignore_frames) { @@ -418,13 +404,11 @@ TEST(libbacktrace, ptrace_threads) { } void VerifyLevelThread(void*) { - backtrace_context_t context; + UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid())); + ASSERT_TRUE(backtrace.get() != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); - ASSERT_TRUE(backtrace_create_context(&context, getpid(), gettid(), 0)); - - VerifyLevelDump(context.backtrace); - - backtrace_destroy_context(&context); + VerifyLevelDump(backtrace.get()); } TEST(libbacktrace, thread_current_level) { @@ -432,13 +416,11 @@ TEST(libbacktrace, thread_current_level) { } void VerifyMaxThread(void*) { - backtrace_context_t context; - - ASSERT_TRUE(backtrace_create_context(&context, getpid(), gettid(), 0)); - - VerifyMaxDump(context.backtrace); + UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid())); + ASSERT_TRUE(backtrace.get() != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); - backtrace_destroy_context(&context); + VerifyMaxDump(backtrace.get()); } TEST(libbacktrace, thread_current_max) { @@ -469,13 +451,11 @@ TEST(libbacktrace, thread_level_trace) { struct sigaction cur_action; ASSERT_TRUE(sigaction(SIGURG, NULL, &cur_action) == 0); - backtrace_context_t context; + UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid)); + ASSERT_TRUE(backtrace.get() != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); - ASSERT_TRUE(backtrace_create_context(&context, getpid(), thread_data.tid,0)); - - VerifyLevelDump(context.backtrace); - - backtrace_destroy_context(&context); + VerifyLevelDump(backtrace.get()); // Tell the thread to exit its infinite loop. android_atomic_acquire_store(0, &thread_data.state); @@ -499,20 +479,19 @@ TEST(libbacktrace, thread_ignore_frames) { // Wait up to 2 seconds for the tid to be set. ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); - backtrace_context_t all; - ASSERT_TRUE(backtrace_create_context(&all, getpid(), thread_data.tid, 0)); - - backtrace_context_t ign1; - ASSERT_TRUE(backtrace_create_context(&ign1, getpid(), thread_data.tid, 1)); + UniquePtr<Backtrace> all(Backtrace::Create(getpid(), thread_data.tid)); + ASSERT_TRUE(all.get() != NULL); + ASSERT_TRUE(all->Unwind(0)); - backtrace_context_t ign2; - ASSERT_TRUE(backtrace_create_context(&ign2, getpid(), thread_data.tid, 2)); + UniquePtr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid)); + ASSERT_TRUE(ign1.get() != NULL); + ASSERT_TRUE(ign1->Unwind(1)); - VerifyIgnoreFrames(all.backtrace, ign1.backtrace, ign2.backtrace, NULL); + UniquePtr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid)); + ASSERT_TRUE(ign2.get() != NULL); + ASSERT_TRUE(ign2->Unwind(2)); - backtrace_destroy_context(&all); - backtrace_destroy_context(&ign1); - backtrace_destroy_context(&ign2); + VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), NULL); // Tell the thread to exit its infinite loop. android_atomic_acquire_store(0, &thread_data.state); @@ -538,13 +517,11 @@ TEST(libbacktrace, thread_max_trace) { // Wait for the tid to be set. ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); - backtrace_context_t context; + UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid)); + ASSERT_TRUE(backtrace.get() != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); - ASSERT_TRUE(backtrace_create_context(&context, getpid(), thread_data.tid, 0)); - - VerifyMaxDump(context.backtrace); - - backtrace_destroy_context(&context); + VerifyMaxDump(backtrace.get()); // Tell the thread to exit its infinite loop. android_atomic_acquire_store(0, &thread_data.state); @@ -558,11 +535,9 @@ void* ThreadDump(void* data) { } } - dump->context.data = NULL; - dump->context.backtrace = NULL; - // The status of the actual unwind will be checked elsewhere. - backtrace_create_context(&dump->context, getpid(), dump->thread.tid, 0); + dump->backtrace = Backtrace::Create(getpid(), dump->thread.tid); + dump->backtrace->Unwind(0); android_atomic_acquire_store(1, &dump->done); @@ -610,58 +585,51 @@ TEST(libbacktrace, thread_multiple_dump) { // Tell the runner thread to exit its infinite loop. android_atomic_acquire_store(0, &runners[i].state); - ASSERT_TRUE(dumpers[i].context.backtrace != NULL); - VerifyMaxDump(dumpers[i].context.backtrace); - backtrace_destroy_context(&dumpers[i].context); + ASSERT_TRUE(dumpers[i].backtrace != NULL); + VerifyMaxDump(dumpers[i].backtrace); + + delete dumpers[i].backtrace; + dumpers[i].backtrace = NULL; } } TEST(libbacktrace, format_test) { - backtrace_context_t context; + UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD)); + ASSERT_TRUE(backtrace.get() != NULL); - ASSERT_TRUE(backtrace_create_context(&context, BACKTRACE_CURRENT_PROCESS, - BACKTRACE_CURRENT_THREAD, 0)); - ASSERT_TRUE(context.backtrace != NULL); + backtrace_frame_data_t frame; + memset(&frame, 0, sizeof(backtrace_frame_data_t)); - backtrace_frame_data_t* frame = - const_cast<backtrace_frame_data_t*>(&context.backtrace->frames[1]); - backtrace_frame_data_t save_frame = *frame; - - memset(frame, 0, sizeof(backtrace_frame_data_t)); - char buf[512]; - backtrace_format_frame_data(&context, 1, buf, sizeof(buf)); + frame.num = 1; #if defined(__LP64__) - EXPECT_STREQ(buf, "#01 pc 0000000000000000 <unknown>"); + EXPECT_STREQ("#01 pc 0000000000000000 <unknown>", #else - EXPECT_STREQ(buf, "#01 pc 00000000 <unknown>"); + EXPECT_STREQ("#01 pc 00000000 <unknown>", #endif + backtrace->FormatFrameData(&frame).c_str()); - frame->pc = 0x12345678; - frame->map_name = "MapFake"; - backtrace_format_frame_data(&context, 1, buf, sizeof(buf)); + frame.pc = 0x12345678; + frame.map_name = "MapFake"; #if defined(__LP64__) - EXPECT_STREQ(buf, "#01 pc 0000000012345678 MapFake"); + EXPECT_STREQ("#01 pc 0000000012345678 MapFake", #else - EXPECT_STREQ(buf, "#01 pc 12345678 MapFake"); + EXPECT_STREQ("#01 pc 12345678 MapFake", #endif + backtrace->FormatFrameData(&frame).c_str()); - frame->func_name = const_cast<char*>("ProcFake"); - backtrace_format_frame_data(&context, 1, buf, sizeof(buf)); + frame.func_name = const_cast<char*>("ProcFake"); #if defined(__LP64__) - EXPECT_STREQ(buf, "#01 pc 0000000012345678 MapFake (ProcFake)"); + EXPECT_STREQ("#01 pc 0000000012345678 MapFake (ProcFake)", #else - EXPECT_STREQ(buf, "#01 pc 12345678 MapFake (ProcFake)"); + EXPECT_STREQ("#01 pc 12345678 MapFake (ProcFake)", #endif + backtrace->FormatFrameData(&frame).c_str()); - frame->func_offset = 645; - backtrace_format_frame_data(&context, 1, buf, sizeof(buf)); + frame.func_offset = 645; #if defined(__LP64__) - EXPECT_STREQ(buf, "#01 pc 0000000012345678 MapFake (ProcFake+645)"); + EXPECT_STREQ("#01 pc 0000000012345678 MapFake (ProcFake+645)", #else - EXPECT_STREQ(buf, "#01 pc 12345678 MapFake (ProcFake+645)"); + EXPECT_STREQ("#01 pc 12345678 MapFake (ProcFake+645)", #endif - - *frame = save_frame; - - backtrace_destroy_context(&context); + backtrace->FormatFrameData(&frame).c_str()); } |