Merge "Refactor dump_memory function."
This commit is contained in:
commit
a684d9dad5
15 changed files with 992 additions and 240 deletions
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
#include <backtrace/Backtrace.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);
|
||||
#include "machine.h"
|
||||
#include "utility.h"
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
|
||||
for (int reg = 0; reg < 31; reg++) {
|
||||
uintptr_t addr = regs.regs[reg];
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
for (int reg = 0; reg < 31; reg++) {
|
||||
dump_memory(log, backtrace, regs.regs[reg], "memory near x%d:", reg);
|
||||
}
|
||||
|
||||
_LOG(log, logtype::MEMORY, "\nmemory near x%d:\n", reg);
|
||||
dump_memory(log, tid, addr);
|
||||
}
|
||||
dump_memory(log, backtrace, static_cast<uintptr_t>(regs.pc), "code around pc:");
|
||||
|
||||
_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);
|
||||
}
|
||||
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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
97
debuggerd/test/BacktraceMock.h
Normal file
97
debuggerd/test/BacktraceMock.h
Normal file
|
@ -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
|
504
debuggerd/test/dump_memory_test.cpp
Normal file
504
debuggerd/test/dump_memory_test.cpp
Normal file
|
@ -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());
|
||||
}
|
59
debuggerd/test/log_fake.cpp
Normal file
59
debuggerd/test/log_fake.cpp
Normal file
|
@ -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;
|
||||
}
|
26
debuggerd/test/log_fake.h
Normal file
26
debuggerd/test/log_fake.h
Normal file
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
#define MEMORY_BYTES_TO_DUMP 256
|
||||
#define MEMORY_BYTES_PER_LINE 16
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
|
||||
/* 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;
|
||||
// Align the address to sizeof(long) and start 32 bytes before the address.
|
||||
addr &= ~(sizeof(long) - 1);
|
||||
if (addr >= 4128) {
|
||||
addr -= 32;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
*asc_out = '\0';
|
||||
_LOG(log, logtype::MEMORY, " %s %s\n", code_buffer, ascii_buffer);
|
||||
_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 += '.';
|
||||
}
|
||||
}
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
||||
void dump_memory_and_code(log_t*, pid_t) {
|
||||
#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:");
|
||||
|
||||
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",
|
||||
|
|
65
debuggerd/x86_64/machine.cpp
Executable file → Normal file
65
debuggerd/x86_64/machine.cpp
Executable file → Normal file
|
@ -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>
|
||||
|
||||
void dump_memory_and_code(log_t*, pid_t) {
|
||||
#include "machine.h"
|
||||
#include "utility.h"
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue