diff options
-rw-r--r-- | debuggerd/Android.mk | 60 | ||||
-rw-r--r-- | debuggerd/arm/machine.cpp | 42 | ||||
-rw-r--r-- | debuggerd/arm64/machine.cpp | 67 | ||||
-rw-r--r-- | debuggerd/machine.h | 4 | ||||
-rw-r--r-- | debuggerd/mips/machine.cpp | 73 | ||||
-rw-r--r-- | debuggerd/mips64/machine.cpp | 73 | ||||
-rw-r--r-- | debuggerd/test/BacktraceMock.h | 97 | ||||
-rw-r--r-- | debuggerd/test/dump_memory_test.cpp | 504 | ||||
-rw-r--r-- | debuggerd/test/log_fake.cpp | 59 | ||||
-rw-r--r-- | debuggerd/test/log_fake.h | 26 | ||||
-rw-r--r-- | debuggerd/tombstone.cpp | 2 | ||||
-rw-r--r-- | debuggerd/utility.cpp | 142 | ||||
-rw-r--r-- | debuggerd/utility.h | 4 | ||||
-rw-r--r-- | debuggerd/x86/machine.cpp | 30 | ||||
-rw-r--r--[-rwxr-xr-x] | debuggerd/x86_64/machine.cpp | 65 |
15 files changed, 1000 insertions, 248 deletions
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk index dd53296..1a5f05e 100644 --- a/debuggerd/Android.mk +++ b/debuggerd/Android.mk @@ -1,4 +1,12 @@ -LOCAL_PATH:= $(call my-dir) +LOCAL_PATH := $(call my-dir) + +common_cppflags := \ + -std=gnu++11 \ + -W \ + -Wall \ + -Wextra \ + -Wunused \ + -Werror \ include $(CLEAR_VARS) @@ -17,11 +25,7 @@ LOCAL_SRC_FILES_mips64 := mips64/machine.cpp LOCAL_SRC_FILES_x86 := x86/machine.cpp LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp -LOCAL_CPPFLAGS := \ - -std=gnu++11 \ - -W -Wall -Wextra \ - -Wunused \ - -Werror \ +LOCAL_CPPFLAGS := $(common_cppflags) ifeq ($(TARGET_IS_64_BIT),true) LOCAL_CPPFLAGS += -DTARGET_IS_64_BIT @@ -70,3 +74,47 @@ LOCAL_MODULE_STEM_64 := crasher64 LOCAL_MULTILIB := both include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + utility.cpp \ + test/dump_memory_test.cpp \ + test/log_fake.cpp \ + +LOCAL_MODULE := debuggerd_test + +LOCAL_SHARED_LIBRARIES := \ + libbacktrace \ + libbase \ + +LOCAL_C_INCLUDES += $(LOCAL_PATH)/test +LOCAL_CPPFLAGS := $(common_cppflags) + +LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32 +LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64 +LOCAL_MULTILIB := both + +include $(BUILD_HOST_NATIVE_TEST) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + utility.cpp \ + test/dump_memory_test.cpp \ + test/log_fake.cpp \ + +LOCAL_MODULE := debuggerd_test + +LOCAL_SHARED_LIBRARIES := \ + libbacktrace \ + libbase \ + +LOCAL_C_INCLUDES += $(LOCAL_PATH)/test +LOCAL_CPPFLAGS := $(common_cppflags) + +LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32 +LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64 +LOCAL_MULTILIB := both + +include $(BUILD_NATIVE_TEST) diff --git a/debuggerd/arm/machine.cpp b/debuggerd/arm/machine.cpp index 50e78c5..b7d6997 100644 --- a/debuggerd/arm/machine.cpp +++ b/debuggerd/arm/machine.cpp @@ -16,53 +16,39 @@ */ #include <errno.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> +#include <stdint.h> #include <string.h> #include <sys/ptrace.h> -#include <sys/types.h> -#include <sys/user.h> -#include "../utility.h" -#include "../machine.h" +#include <backtrace/Backtrace.h> -void dump_memory_and_code(log_t* log, pid_t tid) { +#include "machine.h" +#include "utility.h" + +void dump_memory_and_code(log_t* log, Backtrace* backtrace) { pt_regs regs; - if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) { + if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, ®s)) { + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); return; } - static const char REG_NAMES[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp"; + 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, logtype::MEMORY, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); - dump_memory(log, tid, addr); + dump_memory(log, backtrace, regs.uregs[reg], "memory near %.2s:", ®_names[reg * 2]); } - // explicitly allow upload of code dump logging - _LOG(log, logtype::MEMORY, "\ncode around pc:\n"); - dump_memory(log, tid, static_cast<uintptr_t>(regs.ARM_pc)); + dump_memory(log, backtrace, static_cast<uintptr_t>(regs.ARM_pc), "code around pc:"); if (regs.ARM_pc != regs.ARM_lr) { - _LOG(log, logtype::MEMORY, "\ncode around lr:\n"); - dump_memory(log, tid, static_cast<uintptr_t>(regs.ARM_lr)); + dump_memory(log, backtrace, static_cast<uintptr_t>(regs.ARM_lr), "code around lr:"); } } void dump_registers(log_t* log, pid_t tid) { pt_regs r; if (ptrace(PTRACE_GETREGS, tid, 0, &r)) { - _LOG(log, logtype::REGISTERS, "cannot get registers: %s\n", strerror(errno)); + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); return; } @@ -82,7 +68,7 @@ void dump_registers(log_t* log, pid_t tid) { user_vfp vfp_regs; if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) { - _LOG(log, logtype::FP_REGISTERS, "cannot get FP registers: %s\n", strerror(errno)); + _LOG(log, logtype::ERROR, "cannot get FP registers: %s\n", strerror(errno)); return; } diff --git a/debuggerd/arm64/machine.cpp b/debuggerd/arm64/machine.cpp index 8b17d53..2e097da 100644 --- a/debuggerd/arm64/machine.cpp +++ b/debuggerd/arm64/machine.cpp @@ -17,50 +17,37 @@ #include <elf.h> #include <errno.h> -#include <inttypes.h> +#include <stdint.h> #include <string.h> -#include <sys/types.h> #include <sys/ptrace.h> -#include <sys/user.h> #include <sys/uio.h> -#include "../utility.h" -#include "../machine.h" - -void dump_memory_and_code(log_t* log, pid_t tid) { - struct user_pt_regs regs; - struct iovec io; - io.iov_base = ®s; - io.iov_len = sizeof(regs); - - if (ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, &io) == -1) { - _LOG(log, logtype::ERROR, "%s: ptrace failed to get registers: %s\n", - __func__, strerror(errno)); - return; - } - - for (int reg = 0; reg < 31; reg++) { - uintptr_t addr = regs.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 >= (1UL<<63)) { - continue; - } - - _LOG(log, logtype::MEMORY, "\nmemory near x%d:\n", reg); - dump_memory(log, tid, addr); - } - - _LOG(log, logtype::MEMORY, "\ncode around pc:\n"); - dump_memory(log, tid, (uintptr_t)regs.pc); - - if (regs.pc != regs.sp) { - _LOG(log, logtype::MEMORY, "\ncode around sp:\n"); - dump_memory(log, tid, (uintptr_t)regs.sp); - } +#include <backtrace/Backtrace.h> + +#include "machine.h" +#include "utility.h" + +void dump_memory_and_code(log_t* log, Backtrace* backtrace) { + struct user_pt_regs regs; + struct iovec io; + io.iov_base = ®s; + io.iov_len = sizeof(regs); + + if (ptrace(PTRACE_GETREGSET, backtrace->Tid(), reinterpret_cast<void*>(NT_PRSTATUS), &io) == -1) { + _LOG(log, logtype::ERROR, "%s: ptrace failed to get registers: %s", + __func__, strerror(errno)); + return; + } + + for (int reg = 0; reg < 31; reg++) { + dump_memory(log, backtrace, regs.regs[reg], "memory near x%d:", reg); + } + + dump_memory(log, backtrace, static_cast<uintptr_t>(regs.pc), "code around pc:"); + + if (regs.pc != regs.sp) { + dump_memory(log, backtrace, static_cast<uintptr_t>(regs.sp), "code around sp:"); + } } void dump_registers(log_t* log, pid_t tid) { diff --git a/debuggerd/machine.h b/debuggerd/machine.h index fca9fbe..e65b147 100644 --- a/debuggerd/machine.h +++ b/debuggerd/machine.h @@ -19,9 +19,11 @@ #include <sys/types.h> +#include <backtrace/Backtrace.h> + #include "utility.h" -void dump_memory_and_code(log_t* log, pid_t tid); +void dump_memory_and_code(log_t* log, Backtrace* backtrace); void dump_registers(log_t* log, pid_t tid); #endif // _DEBUGGERD_MACHINE_H diff --git a/debuggerd/mips/machine.cpp b/debuggerd/mips/machine.cpp index 1145963..f7b8a86 100644 --- a/debuggerd/mips/machine.cpp +++ b/debuggerd/mips/machine.cpp @@ -14,30 +14,29 @@ * limitations under the License. */ -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> #include <errno.h> -#include <sys/types.h> +#include <inttypes.h> +#include <stdint.h> +#include <string.h> #include <sys/ptrace.h> -#include <sys/user.h> +#include <backtrace/Backtrace.h> -#include "../utility.h" -#include "../machine.h" +#include "machine.h" +#include "utility.h" -#define R(x) (static_cast<unsigned int>(x)) +#define R(x) (static_cast<uintptr_t>(x)) // 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) { +void dump_memory_and_code(log_t* log, Backtrace* backtrace) { pt_regs r; - if (ptrace(PTRACE_GETREGS, tid, 0, &r)) { + if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) { + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); return; } - static const char REG_NAMES[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; + static const char reg_names[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; for (int reg = 0; reg < 32; reg++) { // skip uninteresting registers @@ -48,27 +47,14 @@ void dump_memory_and_code(log_t* log, pid_t tid) { ) 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, logtype::MEMORY, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); - dump_memory(log, tid, addr); + dump_memory(log, backtrace, R(r.regs[reg]), "memory near %.2s:", ®_names[reg * 2]); } - unsigned int pc = R(r.cp0_epc); - unsigned int ra = R(r.regs[31]); - - _LOG(log, logtype::MEMORY, "\ncode around pc:\n"); - dump_memory(log, tid, (uintptr_t)pc); - + uintptr_t pc = R(r.cp0_epc); + uintptr_t ra = R(r.regs[31]); + dump_memory(log, backtrace, pc, "code around pc:"); if (pc != ra) { - _LOG(log, logtype::MEMORY, "\ncode around ra:\n"); - dump_memory(log, tid, (uintptr_t)ra); + dump_memory(log, backtrace, ra, "code around ra:"); } } @@ -79,22 +65,31 @@ void dump_registers(log_t* log, pid_t tid) { return; } - _LOG(log, logtype::REGISTERS, " zr %08x at %08x v0 %08x v1 %08x\n", + _LOG(log, logtype::REGISTERS, " zr %08" PRIxPTR " at %08" PRIxPTR + " v0 %08" PRIxPTR " v1 %08" PRIxPTR "\n", R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3])); - _LOG(log, logtype::REGISTERS, " a0 %08x a1 %08x a2 %08x a3 %08x\n", + _LOG(log, logtype::REGISTERS, " a0 %08" PRIxPTR " a1 %08" PRIxPTR + " a2 %08" PRIxPTR " a3 %08" PRIxPTR "\n", R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7])); - _LOG(log, logtype::REGISTERS, " t0 %08x t1 %08x t2 %08x t3 %08x\n", + _LOG(log, logtype::REGISTERS, " t0 %08" PRIxPTR " t1 %08" PRIxPTR + " t2 %08" PRIxPTR " t3 %08" PRIxPTR "\n", R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11])); - _LOG(log, logtype::REGISTERS, " t4 %08x t5 %08x t6 %08x t7 %08x\n", + _LOG(log, logtype::REGISTERS, " t4 %08" PRIxPTR " t5 %08" PRIxPTR + " t6 %08" PRIxPTR " t7 %08" PRIxPTR "\n", R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15])); - _LOG(log, logtype::REGISTERS, " s0 %08x s1 %08x s2 %08x s3 %08x\n", + _LOG(log, logtype::REGISTERS, " s0 %08" PRIxPTR " s1 %08" PRIxPTR + " s2 %08" PRIxPTR " s3 %08" PRIxPTR "\n", R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19])); - _LOG(log, logtype::REGISTERS, " s4 %08x s5 %08x s6 %08x s7 %08x\n", + _LOG(log, logtype::REGISTERS, " s4 %08" PRIxPTR " s5 %08" PRIxPTR + " s6 %08" PRIxPTR " s7 %08" PRIxPTR "\n", R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23])); - _LOG(log, logtype::REGISTERS, " t8 %08x t9 %08x k0 %08x k1 %08x\n", + _LOG(log, logtype::REGISTERS, " t8 %08" PRIxPTR " t9 %08" PRIxPTR + " k0 %08" PRIxPTR " k1 %08" PRIxPTR "\n", R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27])); - _LOG(log, logtype::REGISTERS, " gp %08x sp %08x s8 %08x ra %08x\n", + _LOG(log, logtype::REGISTERS, " gp %08" PRIxPTR " sp %08" PRIxPTR + " s8 %08" PRIxPTR " ra %08" PRIxPTR "\n", R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31])); - _LOG(log, logtype::REGISTERS, " hi %08x lo %08x bva %08x epc %08x\n", + _LOG(log, logtype::REGISTERS, " hi %08" PRIxPTR " lo %08" PRIxPTR + " bva %08" PRIxPTR " epc %08" PRIxPTR "\n", R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc)); } diff --git a/debuggerd/mips64/machine.cpp b/debuggerd/mips64/machine.cpp index ef9092f..293dcf6 100644 --- a/debuggerd/mips64/machine.cpp +++ b/debuggerd/mips64/machine.cpp @@ -14,30 +14,29 @@ * limitations under the License. */ -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> #include <errno.h> -#include <sys/types.h> +#include <inttypes.h> +#include <stdint.h> +#include <string.h> #include <sys/ptrace.h> -#include <sys/user.h> +#include <backtrace/Backtrace.h> -#include "../utility.h" -#include "../machine.h" +#include "machine.h" +#include "utility.h" -#define R(x) (static_cast<unsigned long>(x)) +#define R(x) (static_cast<uintptr_t>(x)) // 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) { +void dump_memory_and_code(log_t* log, Backtrace* backtrace) { pt_regs r; - if (ptrace(PTRACE_GETREGS, tid, 0, &r)) { + if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) { + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); return; } - static const char REG_NAMES[] = "$0atv0v1a0a1a2a3a4a5a6a7t0t1t2t3s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; + static const char reg_names[] = "$0atv0v1a0a1a2a3a4a5a6a7t0t1t2t3s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; for (int reg = 0; reg < 32; reg++) { // skip uninteresting registers @@ -48,27 +47,14 @@ void dump_memory_and_code(log_t* log, pid_t tid) { ) 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 >= 0x4000000000000000) { - continue; - } - - _LOG(log, logtype::MEMORY, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); - dump_memory(log, tid, addr); + dump_memory(log, backtrace, R(r.regs[reg]), "memory near %.2s:", ®_names[reg * 2]); } - unsigned long pc = R(r.cp0_epc); - unsigned long ra = R(r.regs[31]); - - _LOG(log, logtype::MEMORY, "\ncode around pc:\n"); - dump_memory(log, tid, (uintptr_t)pc); - + uintptr_t pc = R(r.cp0_epc); + uintptr_t ra = R(r.regs[31]); + dump_memory(log, backtrace, pc, "code around pc:"); if (pc != ra) { - _LOG(log, logtype::MEMORY, "\ncode around ra:\n"); - dump_memory(log, tid, (uintptr_t)ra); + dump_memory(log, backtrace, ra, "code around ra:"); } } @@ -79,22 +65,31 @@ void dump_registers(log_t* log, pid_t tid) { return; } - _LOG(log, logtype::REGISTERS, " zr %016lx at %016lx v0 %016lx v1 %016lx\n", + _LOG(log, logtype::REGISTERS, " zr %016" PRIxPTR " at %016" PRIxPTR + " v0 %016" PRIxPTR " v1 %016" PRIxPTR "\n", R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3])); - _LOG(log, logtype::REGISTERS, " a0 %016lx a1 %016lx a2 %016lx a3 %016lx\n", + _LOG(log, logtype::REGISTERS, " a0 %016" PRIxPTR " a1 %016" PRIxPTR + " a2 %016" PRIxPTR " a3 %016" PRIxPTR "\n", R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7])); - _LOG(log, logtype::REGISTERS, " a4 %016lx a5 %016lx a6 %016lx a7 %016lx\n", + _LOG(log, logtype::REGISTERS, " a4 %016" PRIxPTR " a5 %016" PRIxPTR + " a6 %016" PRIxPTR " a7 %016" PRIxPTR "\n", R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11])); - _LOG(log, logtype::REGISTERS, " t0 %016lx t1 %016lx t2 %016lx t3 %016lx\n", + _LOG(log, logtype::REGISTERS, " t0 %016" PRIxPTR " t1 %016" PRIxPTR + " t2 %016" PRIxPTR " t3 %016" PRIxPTR "\n", R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15])); - _LOG(log, logtype::REGISTERS, " s0 %016lx s1 %016lx s2 %016lx s3 %016lx\n", + _LOG(log, logtype::REGISTERS, " s0 %016" PRIxPTR " s1 %016" PRIxPTR + " s2 %016" PRIxPTR " s3 %016" PRIxPTR "\n", R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19])); - _LOG(log, logtype::REGISTERS, " s4 %016lx s5 %016lx s6 %016lx s7 %016lx\n", + _LOG(log, logtype::REGISTERS, " s4 %016" PRIxPTR " s5 %016" PRIxPTR + " s6 %016" PRIxPTR " s7 %016" PRIxPTR "\n", R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23])); - _LOG(log, logtype::REGISTERS, " t8 %016lx t9 %016lx k0 %016lx k1 %016lx\n", + _LOG(log, logtype::REGISTERS, " t8 %016" PRIxPTR " t9 %016" PRIxPTR + " k0 %016" PRIxPTR " k1 %016" PRIxPTR "\n", R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27])); - _LOG(log, logtype::REGISTERS, " gp %016lx sp %016lx s8 %016lx ra %016lx\n", + _LOG(log, logtype::REGISTERS, " gp %016" PRIxPTR " sp %016" PRIxPTR + " s8 %016" PRIxPTR " ra %016" PRIxPTR "\n", R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31])); - _LOG(log, logtype::REGISTERS, " hi %016lx lo %016lx bva %016lx epc %016lx\n", + _LOG(log, logtype::REGISTERS, " hi %016" PRIxPTR " lo %016" PRIxPTR + " bva %016" PRIxPTR " epc %016" PRIxPTR "\n", R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc)); } diff --git a/debuggerd/test/BacktraceMock.h b/debuggerd/test/BacktraceMock.h new file mode 100644 index 0000000..05ad12b --- /dev/null +++ b/debuggerd/test/BacktraceMock.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DEBUGGERD_TEST_BACKTRACE_MOCK_H +#define _DEBUGGERD_TEST_BACKTRACE_MOCK_H + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ucontext.h> + +#include <string> +#include <vector> + +#include <backtrace/Backtrace.h> +#include <backtrace/BacktraceMap.h> + +class BacktraceMapMock : public BacktraceMap { + public: + BacktraceMapMock() : BacktraceMap(0) {} + virtual ~BacktraceMapMock() {} +}; + + +class BacktraceMock : public Backtrace { + public: + BacktraceMock(BacktraceMapMock* map) : Backtrace(0, 0, map) { + if (map_ == nullptr) { + abort(); + } + } + virtual ~BacktraceMock() {} + + virtual bool Unwind(size_t, ucontext_t*) { return false; } + virtual bool ReadWord(uintptr_t, word_t*) { return false;} + + virtual std::string GetFunctionNameRaw(uintptr_t, uintptr_t*) { return ""; } + + virtual size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) { + size_t offset = 0; + if (last_read_addr_ > 0) { + offset = addr - last_read_addr_; + } + size_t bytes_available = buffer_.size() - offset; + + if (bytes_partial_read_ > 0) { + // Do a partial read. + if (bytes > bytes_partial_read_) { + bytes = bytes_partial_read_; + } + bytes_partial_read_ -= bytes; + } else if (bytes > bytes_available) { + bytes = bytes_available; + } + + if (bytes > 0) { + memcpy(buffer, buffer_.data() + offset, bytes); + } + + last_read_addr_ = addr; + return bytes; + } + + void SetReadData(uint8_t* buffer, size_t bytes) { + buffer_.resize(bytes); + memcpy(buffer_.data(), buffer, bytes); + bytes_partial_read_ = 0; + last_read_addr_ = 0; + } + + void SetPartialReadAmount(size_t bytes) { + if (bytes > buffer_.size()) { + abort(); + } + bytes_partial_read_ = bytes; + } + + private: + std::vector<uint8_t> buffer_; + size_t bytes_partial_read_ = 0; + uintptr_t last_read_addr_ = 0; +}; + +#endif // _DEBUGGERD_TEST_BACKTRACE_MOCK_H diff --git a/debuggerd/test/dump_memory_test.cpp b/debuggerd/test/dump_memory_test.cpp new file mode 100644 index 0000000..fcb0108 --- /dev/null +++ b/debuggerd/test/dump_memory_test.cpp @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2015 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 <stdlib.h> + +#include <memory> +#include <string> + +#include <gtest/gtest.h> +#include <base/file.h> + +#include "BacktraceMock.h" +#include "log_fake.h" +#include "utility.h" + +const char g_expected_full_dump[] = +"\nmemory near r1:\n" +#if defined(__LP64__) +" 0000000012345658 0706050403020100 0f0e0d0c0b0a0908 ................\n" +" 0000000012345668 1716151413121110 1f1e1d1c1b1a1918 ................\n" +" 0000000012345678 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n" +" 0000000012345688 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n" +" 0000000012345698 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n" +" 00000000123456a8 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n" +" 00000000123456b8 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n" +" 00000000123456c8 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n" +" 00000000123456d8 8786858483828180 8f8e8d8c8b8a8988 ................\n" +" 00000000123456e8 9796959493929190 9f9e9d9c9b9a9998 ................\n" +" 00000000123456f8 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n" +" 0000000012345708 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n" +" 0000000012345718 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n" +" 0000000012345728 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n" +" 0000000012345738 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n" +" 0000000012345748 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n"; +#else +" 12345658 03020100 07060504 0b0a0908 0f0e0d0c ................\n" +" 12345668 13121110 17161514 1b1a1918 1f1e1d1c ................\n" +" 12345678 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n" +" 12345688 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n" +" 12345698 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n" +" 123456a8 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n" +" 123456b8 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n" +" 123456c8 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n" +" 123456d8 83828180 87868584 8b8a8988 8f8e8d8c ................\n" +" 123456e8 93929190 97969594 9b9a9998 9f9e9d9c ................\n" +" 123456f8 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n" +" 12345708 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n" +" 12345718 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n" +" 12345728 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n" +" 12345738 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n" +" 12345748 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n"; +#endif + +const char g_expected_partial_dump[] = \ +"\nmemory near pc:\n" +#if defined(__LP64__) +" 00000000123455e0 0706050403020100 0f0e0d0c0b0a0908 ................\n" +" 00000000123455f0 1716151413121110 1f1e1d1c1b1a1918 ................\n" +" 0000000012345600 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n" +" 0000000012345610 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n" +" 0000000012345620 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n" +" 0000000012345630 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n" +" 0000000012345640 6766656463626160 ---------------- `abcdefg........\n" +" 0000000012345650 ---------------- ---------------- ................\n" +" 0000000012345660 ---------------- ---------------- ................\n" +" 0000000012345670 ---------------- ---------------- ................\n" +" 0000000012345680 ---------------- ---------------- ................\n" +" 0000000012345690 ---------------- ---------------- ................\n" +" 00000000123456a0 ---------------- ---------------- ................\n" +" 00000000123456b0 ---------------- ---------------- ................\n" +" 00000000123456c0 ---------------- ---------------- ................\n" +" 00000000123456d0 ---------------- ---------------- ................\n"; +#else +" 123455e0 03020100 07060504 0b0a0908 0f0e0d0c ................\n" +" 123455f0 13121110 17161514 1b1a1918 1f1e1d1c ................\n" +" 12345600 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n" +" 12345610 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n" +" 12345620 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n" +" 12345630 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n" +" 12345640 63626160 67666564 -------- -------- `abcdefg........\n" +" 12345650 -------- -------- -------- -------- ................\n" +" 12345660 -------- -------- -------- -------- ................\n" +" 12345670 -------- -------- -------- -------- ................\n" +" 12345680 -------- -------- -------- -------- ................\n" +" 12345690 -------- -------- -------- -------- ................\n" +" 123456a0 -------- -------- -------- -------- ................\n" +" 123456b0 -------- -------- -------- -------- ................\n" +" 123456c0 -------- -------- -------- -------- ................\n" +" 123456d0 -------- -------- -------- -------- ................\n"; +#endif + +class DumpMemoryTest : public ::testing::Test { + protected: + virtual void SetUp() { + map_mock_.reset(new BacktraceMapMock()); + backtrace_mock_.reset(new BacktraceMock(map_mock_.get())); + + char tmp_file[256]; + const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX"; + memcpy(tmp_file, data_template, sizeof(data_template)); + int tombstone_fd = mkstemp(tmp_file); + if (tombstone_fd == -1) { + const char tmp_template[] = "/tmp/debuggerd_memory_testXXXXXX"; + memcpy(tmp_file, tmp_template, sizeof(tmp_template)); + tombstone_fd = mkstemp(tmp_file); + if (tombstone_fd == -1) { + abort(); + } + } + if (unlink(tmp_file) == -1) { + abort(); + } + + log_.tfd = tombstone_fd; + log_.amfd = -1; + log_.crashed_tid = 12; + log_.current_tid = 12; + log_.should_retrieve_logcat = false; + + resetLogs(); + } + + virtual void TearDown() { + if (log_.tfd >= 0) { + close(log_.tfd); + } + } + + std::unique_ptr<BacktraceMapMock> map_mock_; + std::unique_ptr<BacktraceMock> backtrace_mock_; + + log_t log_; +}; + +TEST_F(DumpMemoryTest, aligned_addr) { + uint8_t buffer[256]; + for (size_t i = 0; i < sizeof(buffer); i++) { + buffer[i] = i; + } + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + + dump_memory(&log_, backtrace_mock_.get(), 0x12345678, "memory near %.2s:", "r1"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, partial_read) { + uint8_t buffer[256]; + for (size_t i = 0; i < sizeof(buffer); i++) { + buffer[i] = i; + } + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + backtrace_mock_->SetPartialReadAmount(96); + + dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, unaligned_addr) { + uint8_t buffer[256]; + for (size_t i = 0; i < sizeof(buffer); i++) { + buffer[i] = i; + } + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + + dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, memory_unreadable) { + dump_memory(&log_, backtrace_mock_.get(), 0xa2345678, "memory near pc:"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + const char* expected_dump = \ +"\nmemory near pc:\n" +#if defined(__LP64__) +" 00000000a2345658 ---------------- ---------------- ................\n" +" 00000000a2345668 ---------------- ---------------- ................\n" +" 00000000a2345678 ---------------- ---------------- ................\n" +" 00000000a2345688 ---------------- ---------------- ................\n" +" 00000000a2345698 ---------------- ---------------- ................\n" +" 00000000a23456a8 ---------------- ---------------- ................\n" +" 00000000a23456b8 ---------------- ---------------- ................\n" +" 00000000a23456c8 ---------------- ---------------- ................\n" +" 00000000a23456d8 ---------------- ---------------- ................\n" +" 00000000a23456e8 ---------------- ---------------- ................\n" +" 00000000a23456f8 ---------------- ---------------- ................\n" +" 00000000a2345708 ---------------- ---------------- ................\n" +" 00000000a2345718 ---------------- ---------------- ................\n" +" 00000000a2345728 ---------------- ---------------- ................\n" +" 00000000a2345738 ---------------- ---------------- ................\n" +" 00000000a2345748 ---------------- ---------------- ................\n"; +#else +" a2345658 -------- -------- -------- -------- ................\n" +" a2345668 -------- -------- -------- -------- ................\n" +" a2345678 -------- -------- -------- -------- ................\n" +" a2345688 -------- -------- -------- -------- ................\n" +" a2345698 -------- -------- -------- -------- ................\n" +" a23456a8 -------- -------- -------- -------- ................\n" +" a23456b8 -------- -------- -------- -------- ................\n" +" a23456c8 -------- -------- -------- -------- ................\n" +" a23456d8 -------- -------- -------- -------- ................\n" +" a23456e8 -------- -------- -------- -------- ................\n" +" a23456f8 -------- -------- -------- -------- ................\n" +" a2345708 -------- -------- -------- -------- ................\n" +" a2345718 -------- -------- -------- -------- ................\n" +" a2345728 -------- -------- -------- -------- ................\n" +" a2345738 -------- -------- -------- -------- ................\n" +" a2345748 -------- -------- -------- -------- ................\n"; +#endif + ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, memory_partially_unreadable) { + uint8_t buffer[104]; + for (size_t i = 0; i < sizeof(buffer); i++) { + buffer[i] = i; + } + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + + dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, memory_partially_unreadable_unaligned_return) { + uint8_t buffer[104]; + for (size_t i = 0; i < sizeof(buffer); i++) { + buffer[i] = i; + } + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + backtrace_mock_->SetPartialReadAmount(102); + + dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str()); + +#if defined(__LP64__) + ASSERT_STREQ("DEBUG Bytes read 102, is not a multiple of 8\n", getFakeLogPrint().c_str()); +#else + ASSERT_STREQ("DEBUG Bytes read 102, is not a multiple of 4\n", getFakeLogPrint().c_str()); +#endif + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); +} + +TEST_F(DumpMemoryTest, memory_partially_unreadable_two_unaligned_reads) { + uint8_t buffer[106]; + for (size_t i = 0; i < sizeof(buffer); i++) { + buffer[i] = i; + } + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + backtrace_mock_->SetPartialReadAmount(45); + + dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str()); + +#if defined(__LP64__) + ASSERT_STREQ("DEBUG Bytes read 45, is not a multiple of 8\n" + "DEBUG Bytes after second read 106, is not a multiple of 8\n", + getFakeLogPrint().c_str()); +#else + ASSERT_STREQ("DEBUG Bytes read 45, is not a multiple of 4\n" + "DEBUG Bytes after second read 106, is not a multiple of 4\n", + getFakeLogPrint().c_str()); +#endif + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); +} + +TEST_F(DumpMemoryTest, address_low_fence) { + uint8_t buffer[256]; + memset(buffer, 0, sizeof(buffer)); + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + + dump_memory(&log_, backtrace_mock_.get(), 0x1000, "memory near %.2s:", "r1"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + const char* expected_dump = \ +"\nmemory near r1:\n" +#if defined(__LP64__) +" 0000000000001000 0000000000000000 0000000000000000 ................\n" +" 0000000000001010 0000000000000000 0000000000000000 ................\n" +" 0000000000001020 0000000000000000 0000000000000000 ................\n" +" 0000000000001030 0000000000000000 0000000000000000 ................\n" +" 0000000000001040 0000000000000000 0000000000000000 ................\n" +" 0000000000001050 0000000000000000 0000000000000000 ................\n" +" 0000000000001060 0000000000000000 0000000000000000 ................\n" +" 0000000000001070 0000000000000000 0000000000000000 ................\n" +" 0000000000001080 0000000000000000 0000000000000000 ................\n" +" 0000000000001090 0000000000000000 0000000000000000 ................\n" +" 00000000000010a0 0000000000000000 0000000000000000 ................\n" +" 00000000000010b0 0000000000000000 0000000000000000 ................\n" +" 00000000000010c0 0000000000000000 0000000000000000 ................\n" +" 00000000000010d0 0000000000000000 0000000000000000 ................\n" +" 00000000000010e0 0000000000000000 0000000000000000 ................\n" +" 00000000000010f0 0000000000000000 0000000000000000 ................\n"; +#else +" 00001000 00000000 00000000 00000000 00000000 ................\n" +" 00001010 00000000 00000000 00000000 00000000 ................\n" +" 00001020 00000000 00000000 00000000 00000000 ................\n" +" 00001030 00000000 00000000 00000000 00000000 ................\n" +" 00001040 00000000 00000000 00000000 00000000 ................\n" +" 00001050 00000000 00000000 00000000 00000000 ................\n" +" 00001060 00000000 00000000 00000000 00000000 ................\n" +" 00001070 00000000 00000000 00000000 00000000 ................\n" +" 00001080 00000000 00000000 00000000 00000000 ................\n" +" 00001090 00000000 00000000 00000000 00000000 ................\n" +" 000010a0 00000000 00000000 00000000 00000000 ................\n" +" 000010b0 00000000 00000000 00000000 00000000 ................\n" +" 000010c0 00000000 00000000 00000000 00000000 ................\n" +" 000010d0 00000000 00000000 00000000 00000000 ................\n" +" 000010e0 00000000 00000000 00000000 00000000 ................\n" +" 000010f0 00000000 00000000 00000000 00000000 ................\n"; +#endif + ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, memory_address_too_low) { + uint8_t buffer[256]; + memset(buffer, 0, sizeof(buffer)); + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + + dump_memory(&log_, backtrace_mock_.get(), 0, "memory near %.2s:", "r1"); + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ("", tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, memory_address_too_high) { + uint8_t buffer[256]; + memset(buffer, 0, sizeof(buffer)); + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + +#if defined(__LP64__) + dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL, "memory near %.2s:", "r1"); + dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 32, "memory near %.2s:", "r1"); + dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 216, "memory near %.2s:", "r1"); +#else + dump_memory(&log_, backtrace_mock_.get(), 0xffff0000, "memory near %.2s:", "r1"); + dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 32, "memory near %.2s:", "r1"); + dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 220, "memory near %.2s:", "r1"); +#endif + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ("", tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, memory_address_would_overflow) { + uint8_t buffer[256]; + memset(buffer, 0, sizeof(buffer)); + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + +#if defined(__LP64__) + dump_memory(&log_, backtrace_mock_.get(), 0xfffffffffffffff0, "memory near %.2s:", "r1"); +#else + dump_memory(&log_, backtrace_mock_.get(), 0xfffffff0, "memory near %.2s:", "r1"); +#endif + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + ASSERT_STREQ("", tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(DumpMemoryTest, memory_address_nearly_too_high) { + uint8_t buffer[256]; + for (size_t i = 0; i < sizeof(buffer); i++) { + buffer[i] = i; + } + backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + +#if defined(__LP64__) + dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 224, "memory near %.2s:", "r4"); +#else + dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 224, "memory near %.2s:", "r4"); +#endif + + std::string tombstone_contents; + ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); + ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); + const char* expected_dump = \ +"\nmemory near r4:\n" +#if defined(__LP64__) +" 3fffffffffffff00 0706050403020100 0f0e0d0c0b0a0908 ................\n" +" 3fffffffffffff10 1716151413121110 1f1e1d1c1b1a1918 ................\n" +" 3fffffffffffff20 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n" +" 3fffffffffffff30 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n" +" 3fffffffffffff40 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n" +" 3fffffffffffff50 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n" +" 3fffffffffffff60 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n" +" 3fffffffffffff70 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n" +" 3fffffffffffff80 8786858483828180 8f8e8d8c8b8a8988 ................\n" +" 3fffffffffffff90 9796959493929190 9f9e9d9c9b9a9998 ................\n" +" 3fffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n" +" 3fffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n" +" 3fffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n" +" 3fffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n" +" 3fffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n" +" 3ffffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n"; +#else +" fffeff00 03020100 07060504 0b0a0908 0f0e0d0c ................\n" +" fffeff10 13121110 17161514 1b1a1918 1f1e1d1c ................\n" +" fffeff20 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n" +" fffeff30 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n" +" fffeff40 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n" +" fffeff50 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n" +" fffeff60 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n" +" fffeff70 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n" +" fffeff80 83828180 87868584 8b8a8988 8f8e8d8c ................\n" +" fffeff90 93929190 97969594 9b9a9998 9f9e9d9c ................\n" +" fffeffa0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n" +" fffeffb0 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n" +" fffeffc0 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n" +" fffeffd0 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n" +" fffeffe0 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n" +" fffefff0 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n"; +#endif + ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + + // Verify that the log buf is empty, and no error messages. + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} diff --git a/debuggerd/test/log_fake.cpp b/debuggerd/test/log_fake.cpp new file mode 100644 index 0000000..1161afe --- /dev/null +++ b/debuggerd/test/log_fake.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2015 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 <stdarg.h> + +#include <string> + +#include <base/stringprintf.h> + +std::string g_fake_log_buf; + +std::string g_fake_log_print; + +void resetLogs() { + g_fake_log_buf = ""; + g_fake_log_print = ""; +} + +extern "C" int __android_log_buf_write(int, int, const char* tag, const char* msg) { + g_fake_log_buf += tag; + g_fake_log_buf += ' '; + g_fake_log_buf += msg; + return 1; +} + +std::string getFakeLogBuf() { + return g_fake_log_buf; +} + +extern "C" int __android_log_print(int, const char* tag, const char* fmt, ...) { + g_fake_log_print += tag; + g_fake_log_print += ' '; + + va_list ap; + va_start(ap, fmt); + android::base::StringAppendV(&g_fake_log_print, fmt, ap); + va_end(ap); + + g_fake_log_print += '\n'; + + return 1; +} + +std::string getFakeLogPrint() { + return g_fake_log_print; +} diff --git a/debuggerd/test/log_fake.h b/debuggerd/test/log_fake.h new file mode 100644 index 0000000..5418fce --- /dev/null +++ b/debuggerd/test/log_fake.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DEBUGGERD_TEST_LOG_FAKE_H +#define _DEBUGGERD_TEST_LOG_FAKE_H + +#include <string> + +void resetLogs(); +std::string getFakeLogBuf(); +std::string getFakeLogPrint(); + +#endif // _DEBUGGERD_TEST_LOG_FAKE_H diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp index ccdfe85..614edb6 100644 --- a/debuggerd/tombstone.cpp +++ b/debuggerd/tombstone.cpp @@ -654,7 +654,7 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, int si_code } else { ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid); } - dump_memory_and_code(log, tid); + dump_memory_and_code(log, backtrace.get()); if (map.get() != nullptr) { dump_all_maps(backtrace.get(), map.get(), log, tid); } diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp index e722f82..9f340a8 100644 --- a/debuggerd/utility.cpp +++ b/debuggerd/utility.cpp @@ -27,6 +27,7 @@ #include <backtrace/Backtrace.h> #include <base/file.h> +#include <base/stringprintf.h> #include <log/log.h> const int SLEEP_TIME_USEC = 50000; // 0.05 seconds @@ -118,68 +119,91 @@ int wait_for_sigstop(pid_t tid, int* total_sleep_time_usec, bool* detach_failed) return -1; } -void dump_memory(log_t* log, pid_t tid, uintptr_t addr) { - char code_buffer[64]; - char ascii_buffer[32]; - uintptr_t p, end; - - p = addr & ~(sizeof(long) - 1); - /* Dump 32 bytes before addr */ - p -= 32; - if (p > addr) { - /* catch underflow */ - p = 0; - } - /* Dump 256 bytes */ - end = p + 256; - /* catch overflow; 'end - p' has to be multiples of 16 */ - while (end < p) { - end -= 16; - } +#define MEMORY_BYTES_TO_DUMP 256 +#define MEMORY_BYTES_PER_LINE 16 + +void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...) { + std::string log_msg; + va_list ap; + va_start(ap, fmt); + android::base::StringAppendV(&log_msg, fmt, ap); + va_end(ap); + + // Align the address to sizeof(long) and start 32 bytes before the address. + addr &= ~(sizeof(long) - 1); + if (addr >= 4128) { + addr -= 32; + } - /* Dump the code around PC as: - * addr contents ascii - * 0000000000008d34 ef000000e8bd0090 e1b00000512fff1e ............../Q - * 0000000000008d44 ea00b1f9e92d0090 e3a070fcef000000 ......-..p...... - * On 32-bit machines, there are still 16 bytes per line but addresses and - * words are of course presented differently. - */ - while (p < end) { - char* asc_out = ascii_buffer; - - int len = snprintf(code_buffer, sizeof(code_buffer), "%" PRIPTR " ", p); - - for (size_t i = 0; i < 16/sizeof(long); i++) { - long data = ptrace(PTRACE_PEEKTEXT, tid, (void*)p, NULL); - if (data == -1 && errno != 0) { - // ptrace failed, probably because we're dumping memory in an - // unmapped or inaccessible page. -#ifdef __LP64__ - len += sprintf(code_buffer + len, "---------------- "); + // Don't bother if the address looks too low, or looks too high. + if (addr < 4096 || +#if defined(__LP64__) + addr > 0x4000000000000000UL - MEMORY_BYTES_TO_DUMP) { #else - len += sprintf(code_buffer + len, "-------- "); + addr > 0xffff0000 - MEMORY_BYTES_TO_DUMP) { #endif - } else { - len += sprintf(code_buffer + len, "%" PRIPTR " ", - static_cast<uintptr_t>(data)); - } - - for (size_t j = 0; j < sizeof(long); 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 += sizeof(long); + return; + } + + _LOG(log, logtype::MEMORY, "\n%s\n", log_msg.c_str()); + + // Dump 256 bytes + uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)]; + memset(data, 0, MEMORY_BYTES_TO_DUMP); + size_t bytes = backtrace->Read(addr, reinterpret_cast<uint8_t*>(data), sizeof(data)); + if (bytes % sizeof(uintptr_t) != 0) { + // This should never happen, but just in case. + ALOGE("Bytes read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t)); + bytes &= ~(sizeof(uintptr_t) - 1); + } + + if (bytes < MEMORY_BYTES_TO_DUMP && bytes > 0) { + // Try to do one more read. This could happen if a read crosses a map, but + // the maps do not have any break between them. Only requires one extra + // read because a map has to contain at least one page, and the total + // number of bytes to dump is smaller than a page. + size_t bytes2 = backtrace->Read(addr + bytes, reinterpret_cast<uint8_t*>(data) + bytes, + sizeof(data) - bytes); + bytes += bytes2; + if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) { + // This should never happen, but we'll try and continue any way. + ALOGE("Bytes after second read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t)); + bytes &= ~(sizeof(uintptr_t) - 1); + } + } + + // Dump the code around memory as: + // addr contents ascii + // 0000000000008d34 ef000000e8bd0090 e1b00000512fff1e ............../Q + // 0000000000008d44 ea00b1f9e92d0090 e3a070fcef000000 ......-..p...... + // On 32-bit machines, there are still 16 bytes per line but addresses and + // words are of course presented differently. + uintptr_t* data_ptr = data; + for (size_t line = 0; line < MEMORY_BYTES_TO_DUMP / MEMORY_BYTES_PER_LINE; line++) { + std::string logline; + android::base::StringAppendF(&logline, " %" PRIPTR, addr); + + addr += MEMORY_BYTES_PER_LINE; + std::string ascii; + for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++, data_ptr++) { + if (bytes >= sizeof(uintptr_t)) { + bytes -= sizeof(uintptr_t); + android::base::StringAppendF(&logline, " %" PRIPTR, *data_ptr); + + // Fill out the ascii string from the data. + uint8_t* ptr = reinterpret_cast<uint8_t*>(data_ptr); + for (size_t val = 0; val < sizeof(uintptr_t); val++, ptr++) { + if (*ptr >= 0x20 && *ptr < 0x7f) { + ascii += *ptr; + } else { + ascii += '.'; + } } - *asc_out = '\0'; - _LOG(log, logtype::MEMORY, " %s %s\n", code_buffer, ascii_buffer); + } else { + logline += ' ' + std::string(sizeof(uintptr_t) * 2, '-'); + ascii += std::string(sizeof(uintptr_t), '.'); + } } + _LOG(log, logtype::MEMORY, "%s %s\n", logline.c_str(), ascii.c_str()); + } } diff --git a/debuggerd/utility.h b/debuggerd/utility.h index 49b46e8..263374d 100644 --- a/debuggerd/utility.h +++ b/debuggerd/utility.h @@ -21,6 +21,8 @@ #include <stdbool.h> #include <sys/types.h> +#include <backtrace/Backtrace.h> + // Figure out the abi based on defined macros. #if defined(__arm__) #define ABI_STRING "arm" @@ -75,6 +77,6 @@ void _LOG(log_t* log, logtype ltype, const char *fmt, ...) int wait_for_sigstop(pid_t, int*, bool*); -void dump_memory(log_t* log, pid_t tid, uintptr_t addr); +void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...); #endif // _DEBUGGERD_UTILITY_H diff --git a/debuggerd/x86/machine.cpp b/debuggerd/x86/machine.cpp index 57330c1..c5f9259 100644 --- a/debuggerd/x86/machine.cpp +++ b/debuggerd/x86/machine.cpp @@ -14,18 +14,31 @@ * limitations under the License. */ -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> #include <errno.h> -#include <sys/types.h> +#include <stdint.h> +#include <string.h> #include <sys/ptrace.h> -#include "../utility.h" -#include "../machine.h" +#include <backtrace/Backtrace.h> + +#include "machine.h" +#include "utility.h" + +void dump_memory_and_code(log_t* log, Backtrace* backtrace) { + struct pt_regs r; + if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) { + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); + return; + } + + dump_memory(log, backtrace, static_cast<uintptr_t>(r.eax), "memory near eax:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.ebx), "memory near ebx:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.ecx), "memory near ecx:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.edx), "memory near edx:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.esi), "memory near esi:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.edi), "memory near edi:"); -void dump_memory_and_code(log_t*, pid_t) { + dump_memory(log, backtrace, static_cast<uintptr_t>(r.eip), "code around eip:"); } void dump_registers(log_t* log, pid_t tid) { @@ -34,6 +47,7 @@ void dump_registers(log_t* log, pid_t tid) { _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); return; } + _LOG(log, logtype::REGISTERS, " eax %08lx ebx %08lx ecx %08lx edx %08lx\n", r.eax, r.ebx, r.ecx, r.edx); _LOG(log, logtype::REGISTERS, " esi %08lx edi %08lx\n", diff --git a/debuggerd/x86_64/machine.cpp b/debuggerd/x86_64/machine.cpp index af4f35a..4f09a5d 100755..100644 --- a/debuggerd/x86_64/machine.cpp +++ b/debuggerd/x86_64/machine.cpp @@ -14,38 +14,51 @@ ** limitations under the License. */ -#include <stddef.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> #include <errno.h> -#include <sys/types.h> +#include <stdint.h> #include <sys/ptrace.h> +#include <string.h> #include <sys/user.h> -#include "../utility.h" -#include "../machine.h" +#include <backtrace/Backtrace.h> + +#include "machine.h" +#include "utility.h" -void dump_memory_and_code(log_t*, pid_t) { +void dump_memory_and_code(log_t* log, Backtrace* backtrace) { + struct user_regs_struct r; + if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) { + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); + return; + } + + dump_memory(log, backtrace, static_cast<uintptr_t>(r.rax), "memory near rax:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.rbx), "memory near rbx:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.rcx), "memory near rcx:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdx), "memory near rdx:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.rsi), "memory near rsi:"); + dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdi), "memory near rdi:"); + + dump_memory(log, backtrace, static_cast<uintptr_t>(r.rip), "code around rip:"); } void dump_registers(log_t* log, pid_t tid) { - struct user_regs_struct r; - if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) { - _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); - return; - } - _LOG(log, logtype::REGISTERS, " rax %016lx rbx %016lx rcx %016lx rdx %016lx\n", - r.rax, r.rbx, r.rcx, r.rdx); - _LOG(log, logtype::REGISTERS, " rsi %016lx rdi %016lx\n", - r.rsi, r.rdi); - _LOG(log, logtype::REGISTERS, " r8 %016lx r9 %016lx r10 %016lx r11 %016lx\n", - r.r8, r.r9, r.r10, r.r11); - _LOG(log, logtype::REGISTERS, " r12 %016lx r13 %016lx r14 %016lx r15 %016lx\n", - r.r12, r.r13, r.r14, r.r15); - _LOG(log, logtype::REGISTERS, " cs %016lx ss %016lx\n", - r.cs, r.ss); - _LOG(log, logtype::REGISTERS, " rip %016lx rbp %016lx rsp %016lx eflags %016lx\n", - r.rip, r.rbp, r.rsp, r.eflags); + struct user_regs_struct r; + if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) { + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); + return; + } + + _LOG(log, logtype::REGISTERS, " rax %016lx rbx %016lx rcx %016lx rdx %016lx\n", + r.rax, r.rbx, r.rcx, r.rdx); + _LOG(log, logtype::REGISTERS, " rsi %016lx rdi %016lx\n", + r.rsi, r.rdi); + _LOG(log, logtype::REGISTERS, " r8 %016lx r9 %016lx r10 %016lx r11 %016lx\n", + r.r8, r.r9, r.r10, r.r11); + _LOG(log, logtype::REGISTERS, " r12 %016lx r13 %016lx r14 %016lx r15 %016lx\n", + r.r12, r.r13, r.r14, r.r15); + _LOG(log, logtype::REGISTERS, " cs %016lx ss %016lx\n", + r.cs, r.ss); + _LOG(log, logtype::REGISTERS, " rip %016lx rbp %016lx rsp %016lx eflags %016lx\n", + r.rip, r.rbp, r.rsp, r.eflags); } |