mirror of
https://git.suyu.dev/suyu/breakpad.git
synced 2025-12-26 09:14:58 +01:00
Process minidumps generated on ARM64 in iOS apps.
Patch by Colin Blundell <blundell@chromium.org> BUG=542 Review URL: https://breakpad.appspot.com/704002/ git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1236 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
77022ac0df
commit
e9165f4353
17 changed files with 5388 additions and 2643 deletions
|
|
@ -73,6 +73,30 @@ using std::ifstream;
|
|||
using std::numeric_limits;
|
||||
using std::vector;
|
||||
|
||||
// Returns true iff |context_size| matches exactly one of the sizes of the
|
||||
// various MDRawContext* types.
|
||||
// TODO(blundell): This function can be removed once
|
||||
// http://code.google.com/p/google-breakpad/issues/detail?id=550 is fixed.
|
||||
static bool IsContextSizeUnique(uint32_t context_size) {
|
||||
int num_matching_contexts = 0;
|
||||
if (context_size == sizeof(MDRawContextX86))
|
||||
num_matching_contexts++;
|
||||
if (context_size == sizeof(MDRawContextPPC))
|
||||
num_matching_contexts++;
|
||||
if (context_size == sizeof(MDRawContextPPC64))
|
||||
num_matching_contexts++;
|
||||
if (context_size == sizeof(MDRawContextAMD64))
|
||||
num_matching_contexts++;
|
||||
if (context_size == sizeof(MDRawContextSPARC))
|
||||
num_matching_contexts++;
|
||||
if (context_size == sizeof(MDRawContextARM))
|
||||
num_matching_contexts++;
|
||||
if (context_size == sizeof(MDRawContextARM64))
|
||||
num_matching_contexts++;
|
||||
if (context_size == sizeof(MDRawContextMIPS))
|
||||
num_matching_contexts++;
|
||||
return num_matching_contexts == 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Swapping routines
|
||||
|
|
@ -361,6 +385,23 @@ MinidumpContext::~MinidumpContext() {
|
|||
bool MinidumpContext::Read(uint32_t expected_size) {
|
||||
valid_ = false;
|
||||
|
||||
// Certain raw context types are currently assumed to have unique sizes.
|
||||
if (!IsContextSizeUnique(sizeof(MDRawContextAMD64))) {
|
||||
BPLOG(ERROR) << "sizeof(MDRawContextAMD64) cannot match the size of any "
|
||||
<< "other raw context";
|
||||
return false;
|
||||
}
|
||||
if (!IsContextSizeUnique(sizeof(MDRawContextPPC64))) {
|
||||
BPLOG(ERROR) << "sizeof(MDRawContextPPC64) cannot match the size of any "
|
||||
<< "other raw context";
|
||||
return false;
|
||||
}
|
||||
if (!IsContextSizeUnique(sizeof(MDRawContextARM64))) {
|
||||
BPLOG(ERROR) << "sizeof(MDRawContextARM64) cannot match the size of any "
|
||||
<< "other raw context";
|
||||
return false;
|
||||
}
|
||||
|
||||
FreeContext();
|
||||
|
||||
// First, figure out what type of CPU this context structure is for.
|
||||
|
|
@ -390,9 +431,8 @@ bool MinidumpContext::Read(uint32_t expected_size) {
|
|||
}
|
||||
|
||||
if (cpu_type != MD_CONTEXT_AMD64) {
|
||||
// TODO: fall through to switch below?
|
||||
// need a Tell method to be able to SeekSet back to beginning
|
||||
// http://code.google.com/p/google-breakpad/issues/detail?id=224
|
||||
// TODO: Fall through to switch below.
|
||||
// http://code.google.com/p/google-breakpad/issues/detail?id=550
|
||||
BPLOG(ERROR) << "MinidumpContext not actually amd64 context";
|
||||
return false;
|
||||
}
|
||||
|
|
@ -483,6 +523,21 @@ bool MinidumpContext::Read(uint32_t expected_size) {
|
|||
uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
|
||||
scoped_ptr<MDRawContextPPC64> context_ppc64(new MDRawContextPPC64());
|
||||
|
||||
if (cpu_type == 0) {
|
||||
if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) {
|
||||
context_ppc64->context_flags |= cpu_type;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Failed to preserve the current stream position";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu_type != MD_CONTEXT_PPC64) {
|
||||
// TODO: Fall through to switch below.
|
||||
// http://code.google.com/p/google-breakpad/issues/detail?id=550
|
||||
BPLOG(ERROR) << "MinidumpContext not actually ppc64 context";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the context_flags member, which has already been read, and
|
||||
// read the rest of the structure beginning with the first member
|
||||
|
|
@ -548,6 +603,83 @@ bool MinidumpContext::Read(uint32_t expected_size) {
|
|||
}
|
||||
|
||||
context_.ppc64 = context_ppc64.release();
|
||||
context_flags_ = context_flags;
|
||||
} else if (expected_size == sizeof(MDRawContextARM64)) {
|
||||
// |context_flags| of MDRawContextARM64 is 64 bits, but other MDRawContext
|
||||
// in the else case have 32 bits |context_flags|, so special case it here.
|
||||
uint64_t context_flags;
|
||||
|
||||
BPLOG(INFO) << "MinidumpContext: looks like ARM64 context";
|
||||
|
||||
if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) {
|
||||
BPLOG(ERROR) << "MinidumpContext could not read context flags";
|
||||
return false;
|
||||
}
|
||||
if (minidump_->swap())
|
||||
Swap(&context_flags);
|
||||
|
||||
scoped_ptr<MDRawContextARM64> context_arm64(new MDRawContextARM64());
|
||||
|
||||
uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
|
||||
if (cpu_type == 0) {
|
||||
if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) {
|
||||
context_arm64->context_flags |= cpu_type;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Failed to preserve the current stream position";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu_type != MD_CONTEXT_ARM64) {
|
||||
// TODO: Fall through to switch below.
|
||||
// http://code.google.com/p/google-breakpad/issues/detail?id=550
|
||||
BPLOG(ERROR) << "MinidumpContext not actually arm64 context";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the context_flags member, which has already been read, and
|
||||
// read the rest of the structure beginning with the first member
|
||||
// after context_flags.
|
||||
context_arm64->context_flags = context_flags;
|
||||
|
||||
size_t flags_size = sizeof(context_arm64->context_flags);
|
||||
uint8_t* context_after_flags =
|
||||
reinterpret_cast<uint8_t*>(context_arm64.get()) + flags_size;
|
||||
if (!minidump_->ReadBytes(context_after_flags,
|
||||
sizeof(MDRawContextARM64) - flags_size)) {
|
||||
BPLOG(ERROR) << "MinidumpContext could not read arm64 context";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do this after reading the entire MDRawContext structure because
|
||||
// GetSystemInfo may seek minidump to a new position.
|
||||
if (!CheckAgainstSystemInfo(cpu_type)) {
|
||||
BPLOG(ERROR) << "MinidumpContext arm64 does not match system info";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (minidump_->swap()) {
|
||||
// context_arm64->context_flags was already swapped.
|
||||
for (unsigned int ireg_index = 0;
|
||||
ireg_index < MD_CONTEXT_ARM64_GPR_COUNT;
|
||||
++ireg_index) {
|
||||
Swap(&context_arm64->iregs[ireg_index]);
|
||||
}
|
||||
Swap(&context_arm64->cpsr);
|
||||
Swap(&context_arm64->float_save.fpsr);
|
||||
Swap(&context_arm64->float_save.fpcr);
|
||||
for (unsigned int fpr_index = 0;
|
||||
fpr_index < MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT;
|
||||
++fpr_index) {
|
||||
// While ARM64 is bi-endian, iOS (currently the only platform
|
||||
// for which ARM64 support has been brought up) uses ARM64 exclusively
|
||||
// in little-endian mode.
|
||||
Normalize128(&context_arm64->float_save.regs[fpr_index], false);
|
||||
Swap(&context_arm64->float_save.regs[fpr_index]);
|
||||
}
|
||||
}
|
||||
context_.arm64 = context_arm64.release();
|
||||
context_flags_ = context_flags;
|
||||
} else {
|
||||
uint32_t context_flags;
|
||||
if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) {
|
||||
|
|
@ -954,6 +1086,9 @@ bool MinidumpContext::GetInstructionPointer(uint64_t* ip) const {
|
|||
case MD_CONTEXT_ARM:
|
||||
*ip = context_.arm->iregs[MD_CONTEXT_ARM_REG_PC];
|
||||
break;
|
||||
case MD_CONTEXT_ARM64:
|
||||
*ip = context_.arm64->iregs[MD_CONTEXT_ARM64_REG_PC];
|
||||
break;
|
||||
case MD_CONTEXT_PPC:
|
||||
*ip = context_.ppc->srr0;
|
||||
break;
|
||||
|
|
@ -1033,6 +1168,15 @@ const MDRawContextARM* MinidumpContext::GetContextARM() const {
|
|||
return context_.arm;
|
||||
}
|
||||
|
||||
const MDRawContextARM64* MinidumpContext::GetContextARM64() const {
|
||||
if (GetContextCPU() != MD_CONTEXT_ARM64) {
|
||||
BPLOG(ERROR) << "MinidumpContext cannot get arm64 context";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return context_.arm64;
|
||||
}
|
||||
|
||||
const MDRawContextMIPS* MinidumpContext::GetContextMIPS() const {
|
||||
if (GetContextCPU() != MD_CONTEXT_MIPS) {
|
||||
BPLOG(ERROR) << "MinidumpContext cannot get MIPS context";
|
||||
|
|
@ -1068,6 +1212,10 @@ void MinidumpContext::FreeContext() {
|
|||
delete context_.arm;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_ARM64:
|
||||
delete context_.arm64;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_MIPS:
|
||||
delete context_.ctx_mips;
|
||||
break;
|
||||
|
|
@ -1142,6 +1290,11 @@ bool MinidumpContext::CheckAgainstSystemInfo(uint32_t context_cpu_type) {
|
|||
return_value = true;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_ARM64:
|
||||
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM64)
|
||||
return_value = true;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_MIPS:
|
||||
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_MIPS)
|
||||
return_value = true;
|
||||
|
|
@ -1423,6 +1576,31 @@ void MinidumpContext::Print() {
|
|||
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CONTEXT_ARM64: {
|
||||
const MDRawContextARM64* context_arm64 = GetContextARM64();
|
||||
printf("MDRawContextARM64\n");
|
||||
printf(" context_flags = 0x%llx\n",
|
||||
context_arm64->context_flags);
|
||||
for (unsigned int ireg_index = 0;
|
||||
ireg_index < MD_CONTEXT_ARM64_GPR_COUNT;
|
||||
++ireg_index) {
|
||||
printf(" iregs[%2d] = 0x%llx\n",
|
||||
ireg_index, context_arm64->iregs[ireg_index]);
|
||||
}
|
||||
printf(" cpsr = 0x%x\n", context_arm64->cpsr);
|
||||
printf(" float_save.fpsr = 0x%x\n", context_arm64->float_save.fpsr);
|
||||
printf(" float_save.fpcr = 0x%x\n", context_arm64->float_save.fpcr);
|
||||
|
||||
for (unsigned int freg_index = 0;
|
||||
freg_index < MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT;
|
||||
++freg_index) {
|
||||
uint128_struct fp_value = context_arm64->float_save.regs[freg_index];
|
||||
printf(" float_save.regs[%2d] = 0x%llx%llx\n",
|
||||
freg_index, fp_value.high, fp_value.low);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CONTEXT_MIPS: {
|
||||
const MDRawContextMIPS* context_mips = GetContextMIPS();
|
||||
|
|
@ -3530,6 +3708,10 @@ string MinidumpSystemInfo::GetCPU() {
|
|||
cpu = "arm";
|
||||
break;
|
||||
|
||||
case MD_CPU_ARCHITECTURE_ARM64:
|
||||
cpu = "arm64";
|
||||
break;
|
||||
|
||||
default:
|
||||
BPLOG(ERROR) << "MinidumpSystemInfo unknown CPU for architecture " <<
|
||||
HexString(system_info_.processor_architecture);
|
||||
|
|
@ -4265,6 +4447,9 @@ bool Minidump::GetContextCPUFlagsFromSystemInfo(uint32_t *context_cpu_flags) {
|
|||
case MD_CPU_ARCHITECTURE_ARM:
|
||||
*context_cpu_flags = MD_CONTEXT_ARM;
|
||||
break;
|
||||
case MD_CPU_ARCHITECTURE_ARM64:
|
||||
*context_cpu_flags = MD_CONTEXT_ARM64;
|
||||
break;
|
||||
case MD_CPU_ARCHITECTURE_IA64:
|
||||
*context_cpu_flags = MD_CONTEXT_IA64;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -505,6 +505,11 @@ bool MinidumpProcessor::GetCPUInfo(Minidump *dump, SystemInfo *info) {
|
|||
break;
|
||||
}
|
||||
|
||||
case MD_CPU_ARCHITECTURE_ARM64: {
|
||||
info->cpu = "arm64";
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CPU_ARCHITECTURE_MIPS: {
|
||||
info->cpu = "mips";
|
||||
break;
|
||||
|
|
@ -668,7 +673,9 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) {
|
|||
default:
|
||||
// arm and ppc overlap
|
||||
if (raw_system_info->processor_architecture ==
|
||||
MD_CPU_ARCHITECTURE_ARM) {
|
||||
MD_CPU_ARCHITECTURE_ARM ||
|
||||
raw_system_info->processor_architecture ==
|
||||
MD_CPU_ARCHITECTURE_ARM64) {
|
||||
switch (exception_flags) {
|
||||
case MD_EXCEPTION_CODE_MAC_ARM_DA_ALIGN:
|
||||
reason.append("EXC_ARM_DA_ALIGN");
|
||||
|
|
@ -708,7 +715,8 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) {
|
|||
case MD_EXCEPTION_MAC_BAD_INSTRUCTION:
|
||||
reason = "EXC_BAD_INSTRUCTION / ";
|
||||
switch (raw_system_info->processor_architecture) {
|
||||
case MD_CPU_ARCHITECTURE_ARM: {
|
||||
case MD_CPU_ARCHITECTURE_ARM:
|
||||
case MD_CPU_ARCHITECTURE_ARM64: {
|
||||
switch (exception_flags) {
|
||||
case MD_EXCEPTION_CODE_MAC_ARM_UNDEFINED:
|
||||
reason.append("EXC_ARM_UNDEFINED");
|
||||
|
|
@ -887,7 +895,8 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) {
|
|||
case MD_EXCEPTION_MAC_BREAKPOINT:
|
||||
reason = "EXC_BREAKPOINT / ";
|
||||
switch (raw_system_info->processor_architecture) {
|
||||
case MD_CPU_ARCHITECTURE_ARM: {
|
||||
case MD_CPU_ARCHITECTURE_ARM:
|
||||
case MD_CPU_ARCHITECTURE_ARM64: {
|
||||
switch (exception_flags) {
|
||||
case MD_EXCEPTION_CODE_MAC_ARM_DA_ALIGN:
|
||||
reason.append("EXC_ARM_DA_ALIGN");
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ using google_breakpad::StackFrameSPARC;
|
|||
using google_breakpad::StackFrameX86;
|
||||
using google_breakpad::StackFrameAMD64;
|
||||
using google_breakpad::StackFrameARM;
|
||||
using google_breakpad::StackFrameARM64;
|
||||
using google_breakpad::StackFrameMIPS;
|
||||
|
||||
// Separator character for machine readable output.
|
||||
|
|
@ -272,6 +273,144 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
|
|||
sequence = PrintRegister("lr", frame_arm->context.iregs[14], sequence);
|
||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC)
|
||||
sequence = PrintRegister("pc", frame_arm->context.iregs[15], sequence);
|
||||
} else if (cpu == "arm64") {
|
||||
const StackFrameARM64 *frame_arm64 =
|
||||
reinterpret_cast<const StackFrameARM64*>(frame);
|
||||
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X0) {
|
||||
sequence =
|
||||
PrintRegister64("x0", frame_arm64->context.iregs[0], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X1) {
|
||||
sequence =
|
||||
PrintRegister64("x1", frame_arm64->context.iregs[1], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X2) {
|
||||
sequence =
|
||||
PrintRegister64("x2", frame_arm64->context.iregs[2], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X3) {
|
||||
sequence =
|
||||
PrintRegister64("x3", frame_arm64->context.iregs[3], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X4) {
|
||||
sequence =
|
||||
PrintRegister64("x4", frame_arm64->context.iregs[4], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X5) {
|
||||
sequence =
|
||||
PrintRegister64("x5", frame_arm64->context.iregs[5], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X6) {
|
||||
sequence =
|
||||
PrintRegister64("x6", frame_arm64->context.iregs[6], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X7) {
|
||||
sequence =
|
||||
PrintRegister64("x7", frame_arm64->context.iregs[7], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X8) {
|
||||
sequence =
|
||||
PrintRegister64("x8", frame_arm64->context.iregs[8], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X9) {
|
||||
sequence =
|
||||
PrintRegister64("x9", frame_arm64->context.iregs[9], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X10) {
|
||||
sequence =
|
||||
PrintRegister64("x10", frame_arm64->context.iregs[10], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X11) {
|
||||
sequence =
|
||||
PrintRegister64("x11", frame_arm64->context.iregs[11], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X12) {
|
||||
sequence =
|
||||
PrintRegister64("x12", frame_arm64->context.iregs[12], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X13) {
|
||||
sequence =
|
||||
PrintRegister64("x13", frame_arm64->context.iregs[13], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X14) {
|
||||
sequence =
|
||||
PrintRegister64("x14", frame_arm64->context.iregs[14], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X15) {
|
||||
sequence =
|
||||
PrintRegister64("x15", frame_arm64->context.iregs[15], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X16) {
|
||||
sequence =
|
||||
PrintRegister64("x16", frame_arm64->context.iregs[16], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X17) {
|
||||
sequence =
|
||||
PrintRegister64("x17", frame_arm64->context.iregs[17], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X18) {
|
||||
sequence =
|
||||
PrintRegister64("x18", frame_arm64->context.iregs[18], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X19) {
|
||||
sequence =
|
||||
PrintRegister64("x19", frame_arm64->context.iregs[19], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X20) {
|
||||
sequence =
|
||||
PrintRegister64("x20", frame_arm64->context.iregs[20], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X21) {
|
||||
sequence =
|
||||
PrintRegister64("x21", frame_arm64->context.iregs[21], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X22) {
|
||||
sequence =
|
||||
PrintRegister64("x22", frame_arm64->context.iregs[22], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X23) {
|
||||
sequence =
|
||||
PrintRegister64("x23", frame_arm64->context.iregs[23], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X24) {
|
||||
sequence =
|
||||
PrintRegister64("x24", frame_arm64->context.iregs[24], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X25) {
|
||||
sequence =
|
||||
PrintRegister64("x25", frame_arm64->context.iregs[25], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X26) {
|
||||
sequence =
|
||||
PrintRegister64("x26", frame_arm64->context.iregs[26], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X27) {
|
||||
sequence =
|
||||
PrintRegister64("x27", frame_arm64->context.iregs[27], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X28) {
|
||||
sequence =
|
||||
PrintRegister64("x28", frame_arm64->context.iregs[28], sequence);
|
||||
}
|
||||
|
||||
// Registers with a dedicated or conventional purpose.
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_FP) {
|
||||
sequence =
|
||||
PrintRegister64("fp", frame_arm64->context.iregs[29], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_LR) {
|
||||
sequence =
|
||||
PrintRegister64("lr", frame_arm64->context.iregs[30], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_SP) {
|
||||
sequence =
|
||||
PrintRegister64("sp", frame_arm64->context.iregs[31], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_PC) {
|
||||
sequence =
|
||||
PrintRegister64("pc", frame_arm64->context.iregs[32], sequence);
|
||||
}
|
||||
} else if (cpu == "mips") {
|
||||
const StackFrameMIPS* frame_mips =
|
||||
reinterpret_cast<const StackFrameMIPS*>(frame);
|
||||
|
|
@ -328,7 +467,7 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
|
|||
sequence = PrintRegister64("s7",
|
||||
frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S7],
|
||||
sequence);
|
||||
}
|
||||
}
|
||||
printf("\n Found by: %s\n", frame->trust_description().c_str());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
79
src/processor/stack_frame_cpu.cc
Normal file
79
src/processor/stack_frame_cpu.cc
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2013 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// stack_frame_cpu.h: CPU-specific StackFrame extensions.
|
||||
//
|
||||
// See google_breakpad/processor/stack_frame_cpu.h for documentation.
|
||||
//
|
||||
// Author: Colin Blundell
|
||||
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X0;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X1;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X2;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X3;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X4;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X5;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X6;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X7;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X8;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X9;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X10;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X11;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X12;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X13;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X14;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X15;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X16;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X17;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X18;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X19;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X20;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X21;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X22;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X23;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X24;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X25;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X26;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X27;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X28;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X29;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X30;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X31;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X32;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_FP;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_LR;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_SP;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_PC;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_ALL;
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
@ -53,6 +53,7 @@
|
|||
#include "processor/stackwalker_x86.h"
|
||||
#include "processor/stackwalker_amd64.h"
|
||||
#include "processor/stackwalker_arm.h"
|
||||
#include "processor/stackwalker_arm64.h"
|
||||
#include "processor/stackwalker_mips.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
|
@ -239,6 +240,7 @@ Stackwalker* Stackwalker::StackwalkerForCPU(
|
|||
break;
|
||||
|
||||
case MD_CONTEXT_ARM:
|
||||
{
|
||||
int fp_register = -1;
|
||||
if (system_info->os_short == "ios")
|
||||
fp_register = MD_CONTEXT_ARM_REG_IOS_FP;
|
||||
|
|
@ -247,6 +249,14 @@ Stackwalker* Stackwalker::StackwalkerForCPU(
|
|||
fp_register, memory, modules,
|
||||
frame_symbolizer);
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CONTEXT_ARM64:
|
||||
cpu_stackwalker = new StackwalkerARM64(system_info,
|
||||
context->GetContextARM64(),
|
||||
memory, modules,
|
||||
frame_symbolizer);
|
||||
break;
|
||||
}
|
||||
|
||||
BPLOG_IF(ERROR, !cpu_stackwalker) << "Unknown CPU type " << HexString(cpu) <<
|
||||
|
|
|
|||
209
src/processor/stackwalker_arm64.cc
Normal file
209
src/processor/stackwalker_arm64.cc
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
// Copyright (c) 2013 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// stackwalker_arm64.cc: arm64-specific stackwalker.
|
||||
//
|
||||
// See stackwalker_arm64.h for documentation.
|
||||
//
|
||||
// Author: Mark Mentovai, Ted Mielczarek, Jim Blandy, Colin Blundell
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/memory_region.h"
|
||||
#include "google_breakpad/processor/source_line_resolver_interface.h"
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
#include "processor/cfi_frame_info.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/stackwalker_arm64.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
|
||||
StackwalkerARM64::StackwalkerARM64(const SystemInfo* system_info,
|
||||
const MDRawContextARM64* context,
|
||||
MemoryRegion* memory,
|
||||
const CodeModules* modules,
|
||||
StackFrameSymbolizer* resolver_helper)
|
||||
: Stackwalker(system_info, memory, modules, resolver_helper),
|
||||
context_(context),
|
||||
context_frame_validity_(StackFrameARM64::CONTEXT_VALID_ALL) { }
|
||||
|
||||
|
||||
StackFrame* StackwalkerARM64::GetContextFrame() {
|
||||
if (!context_) {
|
||||
BPLOG(ERROR) << "Can't get context frame without context";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StackFrameARM64* frame = new StackFrameARM64();
|
||||
|
||||
// The instruction pointer is stored directly in a register (x32), so pull it
|
||||
// straight out of the CPU context structure.
|
||||
frame->context = *context_;
|
||||
frame->context_validity = context_frame_validity_;
|
||||
frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
|
||||
frame->instruction = frame->context.iregs[MD_CONTEXT_ARM64_REG_PC];
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
StackFrameARM64* StackwalkerARM64::GetCallerByCFIFrameInfo(
|
||||
const vector<StackFrame*> &frames,
|
||||
CFIFrameInfo* cfi_frame_info) {
|
||||
// Obtaining the stack frame from CFI info is not yet supported for ARM64.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StackFrameARM64* StackwalkerARM64::GetCallerByStackScan(
|
||||
const vector<StackFrame*> &frames) {
|
||||
StackFrameARM64* last_frame = static_cast<StackFrameARM64*>(frames.back());
|
||||
uint64_t last_sp = last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP];
|
||||
uint64_t caller_sp, caller_pc;
|
||||
|
||||
if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc,
|
||||
frames.size() == 1 /* is_context_frame */)) {
|
||||
// No plausible return address was found.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ScanForReturnAddress found a reasonable return address. Advance
|
||||
// %sp to the location above the one where the return address was
|
||||
// found.
|
||||
caller_sp += 8;
|
||||
|
||||
// Create a new stack frame (ownership will be transferred to the caller)
|
||||
// and fill it in.
|
||||
StackFrameARM64* frame = new StackFrameARM64();
|
||||
|
||||
frame->trust = StackFrame::FRAME_TRUST_SCAN;
|
||||
frame->context = last_frame->context;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] = caller_pc;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_SP] = caller_sp;
|
||||
frame->context_validity = StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_SP;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
StackFrameARM64* StackwalkerARM64::GetCallerByFramePointer(
|
||||
const vector<StackFrame*> &frames) {
|
||||
StackFrameARM64* last_frame = static_cast<StackFrameARM64*>(frames.back());
|
||||
|
||||
uint64_t last_fp = last_frame->context.iregs[MD_CONTEXT_ARM64_REG_FP];
|
||||
|
||||
uint64_t caller_fp = 0;
|
||||
if (last_fp && !memory_->GetMemoryAtAddress(last_fp, &caller_fp)) {
|
||||
BPLOG(ERROR) << "Unable to read caller_fp from last_fp: 0x"
|
||||
<< std::hex << last_fp;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t caller_lr = 0;
|
||||
if (last_fp && !memory_->GetMemoryAtAddress(last_fp + 8, &caller_lr)) {
|
||||
BPLOG(ERROR) << "Unable to read caller_lr from last_fp + 8: 0x"
|
||||
<< std::hex << (last_fp + 8);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t caller_sp = last_fp ? last_fp + 16 :
|
||||
last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP];
|
||||
|
||||
// Create a new stack frame (ownership will be transferred to the caller)
|
||||
// and fill it in.
|
||||
StackFrameARM64* frame = new StackFrameARM64();
|
||||
|
||||
frame->trust = StackFrame::FRAME_TRUST_FP;
|
||||
frame->context = last_frame->context;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_FP] = caller_fp;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_SP] = caller_sp;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] =
|
||||
last_frame->context.iregs[MD_CONTEXT_ARM64_REG_LR];
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_LR] = caller_lr;
|
||||
frame->context_validity = StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_LR |
|
||||
StackFrameARM64::CONTEXT_VALID_FP |
|
||||
StackFrameARM64::CONTEXT_VALID_SP;
|
||||
return frame;
|
||||
}
|
||||
|
||||
StackFrame* StackwalkerARM64::GetCallerFrame(const CallStack* stack,
|
||||
bool stack_scan_allowed) {
|
||||
if (!memory_ || !stack) {
|
||||
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const vector<StackFrame*> &frames = *stack->frames();
|
||||
StackFrameARM64* last_frame = static_cast<StackFrameARM64*>(frames.back());
|
||||
scoped_ptr<StackFrameARM64> frame;
|
||||
|
||||
// See if there is DWARF call frame information covering this address.
|
||||
scoped_ptr<CFIFrameInfo> cfi_frame_info(
|
||||
frame_symbolizer_->FindCFIFrameInfo(last_frame));
|
||||
if (cfi_frame_info.get())
|
||||
frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get()));
|
||||
|
||||
// If CFI failed, or there wasn't CFI available, fall back to frame pointer.
|
||||
if (!frame.get())
|
||||
frame.reset(GetCallerByFramePointer(frames));
|
||||
|
||||
// If everything failed, fall back to stack scanning.
|
||||
if (stack_scan_allowed && !frame.get())
|
||||
frame.reset(GetCallerByStackScan(frames));
|
||||
|
||||
// If nothing worked, tell the caller.
|
||||
if (!frame.get())
|
||||
return NULL;
|
||||
|
||||
// An instruction address of zero marks the end of the stack.
|
||||
if (frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] == 0)
|
||||
return NULL;
|
||||
|
||||
// If the new stack pointer is at a lower address than the old, then
|
||||
// that's clearly incorrect. Treat this as end-of-stack to enforce
|
||||
// progress and avoid infinite loops.
|
||||
if (frame->context.iregs[MD_CONTEXT_ARM64_REG_SP]
|
||||
< last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP])
|
||||
return NULL;
|
||||
|
||||
// The new frame's context's PC is the return address, which is one
|
||||
// instruction past the instruction that caused us to arrive at the callee.
|
||||
// ARM64 instructions have a uniform 4-byte encoding, so subtracting 4 off
|
||||
// the return address gets back to the beginning of the call instruction.
|
||||
// Callers that require the exact return address value may access
|
||||
// frame->context.iregs[MD_CONTEXT_ARM64_REG_PC].
|
||||
frame->instruction = frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] - 4;
|
||||
|
||||
return frame.release();
|
||||
}
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
||||
102
src/processor/stackwalker_arm64.h
Normal file
102
src/processor/stackwalker_arm64.h
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
// -*- mode: C++ -*-
|
||||
|
||||
// Copyright (c) 2013 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// stackwalker_arm64.h: arm64-specific stackwalker.
|
||||
//
|
||||
// Provides stack frames given arm64 register context and a memory region
|
||||
// corresponding to an arm64 stack.
|
||||
//
|
||||
// Author: Mark Mentovai, Ted Mielczarek, Colin Blundell
|
||||
|
||||
|
||||
#ifndef PROCESSOR_STACKWALKER_ARM64_H__
|
||||
#define PROCESSOR_STACKWALKER_ARM64_H__
|
||||
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "google_breakpad/processor/stackwalker.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class CodeModules;
|
||||
|
||||
class StackwalkerARM64 : public Stackwalker {
|
||||
public:
|
||||
// context is an arm64 context object that gives access to arm64-specific
|
||||
// register state corresponding to the innermost called frame to be
|
||||
// included in the stack. The other arguments are passed directly through
|
||||
// to the base Stackwalker constructor.
|
||||
StackwalkerARM64(const SystemInfo* system_info,
|
||||
const MDRawContextARM64* context,
|
||||
MemoryRegion* memory,
|
||||
const CodeModules* modules,
|
||||
StackFrameSymbolizer* frame_symbolizer);
|
||||
|
||||
// Change the context validity mask of the frame returned by
|
||||
// GetContextFrame to VALID. This is only for use by unit tests; the
|
||||
// default behavior is correct for all application code.
|
||||
void SetContextFrameValidity(int valid) { context_frame_validity_ = valid; }
|
||||
|
||||
private:
|
||||
// Implementation of Stackwalker, using arm64 context and stack conventions.
|
||||
virtual StackFrame* GetContextFrame();
|
||||
virtual StackFrame* GetCallerFrame(const CallStack* stack,
|
||||
bool stack_scan_allowed);
|
||||
|
||||
// Use cfi_frame_info (derived from STACK CFI records) to construct
|
||||
// the frame that called frames.back(). The caller takes ownership
|
||||
// of the returned frame. Return NULL on failure.
|
||||
StackFrameARM64* GetCallerByCFIFrameInfo(const vector<StackFrame*> &frames,
|
||||
CFIFrameInfo* cfi_frame_info);
|
||||
|
||||
// Use the frame pointer. The caller takes ownership of the returned frame.
|
||||
// Return NULL on failure.
|
||||
StackFrameARM64* GetCallerByFramePointer(const vector<StackFrame*> &frames);
|
||||
|
||||
// Scan the stack for plausible return addresses. The caller takes ownership
|
||||
// of the returned frame. Return NULL on failure.
|
||||
StackFrameARM64* GetCallerByStackScan(const vector<StackFrame*> &frames);
|
||||
|
||||
// Stores the CPU context corresponding to the youngest stack frame, to
|
||||
// be returned by GetContextFrame.
|
||||
const MDRawContextARM64* context_;
|
||||
|
||||
// Validity mask for youngest stack frame. This is always
|
||||
// CONTEXT_VALID_ALL in real use; it is only changeable for the sake of
|
||||
// unit tests.
|
||||
uint64_t context_frame_validity_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
||||
#endif // PROCESSOR_STACKWALKER_ARM64_H__
|
||||
536
src/processor/stackwalker_arm64_unittest.cc
Normal file
536
src/processor/stackwalker_arm64_unittest.cc
Normal file
|
|
@ -0,0 +1,536 @@
|
|||
// Copyright (c) 2010, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// stackwalker_arm64_unittest.cc: Unit tests for StackwalkerARM64 class.
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/test_assembler.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "google_breakpad/processor/basic_source_line_resolver.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/code_module.h"
|
||||
#include "google_breakpad/processor/source_line_resolver_interface.h"
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
#include "processor/stackwalker_unittest_utils.h"
|
||||
#include "processor/stackwalker_arm64.h"
|
||||
#include "processor/windows_frame_info.h"
|
||||
|
||||
using google_breakpad::BasicSourceLineResolver;
|
||||
using google_breakpad::CallStack;
|
||||
using google_breakpad::CodeModule;
|
||||
using google_breakpad::StackFrameSymbolizer;
|
||||
using google_breakpad::StackFrame;
|
||||
using google_breakpad::StackFrameARM64;
|
||||
using google_breakpad::Stackwalker;
|
||||
using google_breakpad::StackwalkerARM64;
|
||||
using google_breakpad::SystemInfo;
|
||||
using google_breakpad::WindowsFrameInfo;
|
||||
using google_breakpad::test_assembler::kLittleEndian;
|
||||
using google_breakpad::test_assembler::Label;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
using std::vector;
|
||||
using testing::_;
|
||||
using testing::AnyNumber;
|
||||
using testing::Return;
|
||||
using testing::SetArgumentPointee;
|
||||
using testing::Test;
|
||||
|
||||
class StackwalkerARM64Fixture {
|
||||
public:
|
||||
StackwalkerARM64Fixture()
|
||||
: stack_section(kLittleEndian),
|
||||
// Give the two modules reasonable standard locations and names
|
||||
// for tests to play with.
|
||||
module1(0x40000000, 0x10000, "module1", "version1"),
|
||||
module2(0x50000000, 0x10000, "module2", "version2") {
|
||||
// Identify the system as an iOS system, since that is the only platform
|
||||
// for which ARM64 support is currently enabled.
|
||||
system_info.os = "iOS";
|
||||
system_info.os_short = "ios";
|
||||
system_info.cpu = "arm64";
|
||||
system_info.cpu_info = "";
|
||||
|
||||
// Put distinctive values in the raw CPU context.
|
||||
BrandContext(&raw_context);
|
||||
|
||||
// Create some modules with some stock debugging information.
|
||||
modules.Add(&module1);
|
||||
modules.Add(&module2);
|
||||
|
||||
// By default, none of the modules have symbol info; call
|
||||
// SetModuleSymbols to override this.
|
||||
EXPECT_CALL(supplier, GetCStringSymbolData(_, _, _, _, _))
|
||||
.WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND));
|
||||
|
||||
// Avoid GMOCK WARNING "Uninteresting mock function call - returning
|
||||
// directly" for FreeSymbolData().
|
||||
EXPECT_CALL(supplier, FreeSymbolData(_)).Times(AnyNumber());
|
||||
|
||||
// Reset max_frames_scanned since it's static.
|
||||
Stackwalker::set_max_frames_scanned(1024);
|
||||
}
|
||||
|
||||
// Set the Breakpad symbol information that supplier should return for
|
||||
// MODULE to INFO.
|
||||
void SetModuleSymbols(MockCodeModule *module, const string &info) {
|
||||
size_t buffer_size;
|
||||
char *buffer = supplier.CopySymbolDataAndOwnTheCopy(info, &buffer_size);
|
||||
EXPECT_CALL(supplier, GetCStringSymbolData(module, &system_info, _, _, _))
|
||||
.WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer),
|
||||
SetArgumentPointee<4>(buffer_size),
|
||||
Return(MockSymbolSupplier::FOUND)));
|
||||
}
|
||||
|
||||
// Populate stack_region with the contents of stack_section. Use
|
||||
// stack_section.start() as the region's starting address.
|
||||
void RegionFromSection() {
|
||||
string contents;
|
||||
ASSERT_TRUE(stack_section.GetContents(&contents));
|
||||
stack_region.Init(stack_section.start().Value(), contents);
|
||||
}
|
||||
|
||||
// Fill RAW_CONTEXT with pseudo-random data, for round-trip checking.
|
||||
void BrandContext(MDRawContextARM64 *raw_context) {
|
||||
uint8_t x = 173;
|
||||
for (size_t i = 0; i < sizeof(*raw_context); i++)
|
||||
reinterpret_cast<uint8_t *>(raw_context)[i] = (x += 17);
|
||||
}
|
||||
|
||||
SystemInfo system_info;
|
||||
MDRawContextARM64 raw_context;
|
||||
Section stack_section;
|
||||
MockMemoryRegion stack_region;
|
||||
MockCodeModule module1;
|
||||
MockCodeModule module2;
|
||||
MockCodeModules modules;
|
||||
MockSymbolSupplier supplier;
|
||||
BasicSourceLineResolver resolver;
|
||||
CallStack call_stack;
|
||||
const vector<StackFrame *> *frames;
|
||||
};
|
||||
|
||||
class SanityCheck: public StackwalkerARM64Fixture, public Test { };
|
||||
|
||||
TEST_F(SanityCheck, NoResolver) {
|
||||
// Since the context's frame pointer is garbage, the stack walk will end after
|
||||
// the first frame.
|
||||
StackFrameSymbolizer frame_symbolizer(NULL, NULL);
|
||||
StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
// This should succeed even without a resolver or supplier.
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(0U, modules_without_symbols.size());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(1U, frames->size());
|
||||
StackFrameARM64 *frame = static_cast<StackFrameARM64 *>(frames->at(0));
|
||||
// Check that the values from the original raw context made it
|
||||
// through to the context in the stack frame.
|
||||
EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
|
||||
}
|
||||
|
||||
class GetContextFrame: public StackwalkerARM64Fixture, public Test { };
|
||||
|
||||
// The stackwalker should be able to produce the context frame even
|
||||
// without stack memory present.
|
||||
TEST_F(GetContextFrame, NoStackMemory) {
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerARM64 walker(&system_info, &raw_context, NULL, &modules,
|
||||
&frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(0U, modules_without_symbols.size());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(1U, frames->size());
|
||||
StackFrameARM64 *frame = static_cast<StackFrameARM64 *>(frames->at(0));
|
||||
// Check that the values from the original raw context made it
|
||||
// through to the context in the stack frame.
|
||||
EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
|
||||
}
|
||||
|
||||
class GetCallerFrame: public StackwalkerARM64Fixture, public Test { };
|
||||
|
||||
TEST_F(GetCallerFrame, ScanWithoutSymbols) {
|
||||
// When the stack walker resorts to scanning the stack,
|
||||
// only addresses located within loaded modules are
|
||||
// considered valid return addresses.
|
||||
// Force scanning through three frames to ensure that the
|
||||
// stack pointer is set properly in scan-recovered frames.
|
||||
stack_section.start() = 0x80000000;
|
||||
uint64_t return_address1 = 0x50000100;
|
||||
uint64_t return_address2 = 0x50000900;
|
||||
Label frame1_sp, frame2_sp;
|
||||
stack_section
|
||||
// frame 0
|
||||
.Append(16, 0) // space
|
||||
|
||||
.D64(0x40090000) // junk that's not
|
||||
.D64(0x60000000) // a return address
|
||||
|
||||
.D64(return_address1) // actual return address
|
||||
// frame 1
|
||||
.Mark(&frame1_sp)
|
||||
.Append(16, 0) // space
|
||||
|
||||
.D64(0xF0000000) // more junk
|
||||
.D64(0x0000000D)
|
||||
|
||||
.D64(return_address2) // actual return address
|
||||
// frame 2
|
||||
.Mark(&frame2_sp)
|
||||
.Append(64, 0); // end of stack
|
||||
RegionFromSection();
|
||||
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40005510;
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(2U, modules_without_symbols.size());
|
||||
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
|
||||
ASSERT_EQ("module2", modules_without_symbols[1]->debug_file());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(3U, frames->size());
|
||||
|
||||
StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
|
||||
ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
|
||||
frame0->context_validity);
|
||||
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
|
||||
|
||||
StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
|
||||
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_SP),
|
||||
frame1->context_validity);
|
||||
EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
|
||||
EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
|
||||
|
||||
StackFrameARM64 *frame2 = static_cast<StackFrameARM64 *>(frames->at(2));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame2->trust);
|
||||
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_SP),
|
||||
frame2->context_validity);
|
||||
EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
|
||||
EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
|
||||
}
|
||||
|
||||
TEST_F(GetCallerFrame, ScanWithFunctionSymbols) {
|
||||
// During stack scanning, if a potential return address
|
||||
// is located within a loaded module that has symbols,
|
||||
// it is only considered a valid return address if it
|
||||
// lies within a function's bounds.
|
||||
stack_section.start() = 0x80000000;
|
||||
uint64_t return_address = 0x50000200;
|
||||
Label frame1_sp;
|
||||
|
||||
stack_section
|
||||
// frame 0
|
||||
.Append(16, 0) // space
|
||||
|
||||
.D64(0x40090000) // junk that's not
|
||||
.D64(0x60000000) // a return address
|
||||
|
||||
.D64(0x40001000) // a couple of plausible addresses
|
||||
.D64(0x5000F000) // that are not within functions
|
||||
|
||||
.D64(return_address) // actual return address
|
||||
// frame 1
|
||||
.Mark(&frame1_sp)
|
||||
.Append(64, 0); // end of stack
|
||||
RegionFromSection();
|
||||
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40000200;
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
|
||||
|
||||
SetModuleSymbols(&module1,
|
||||
// The youngest frame's function.
|
||||
"FUNC 100 400 10 monotreme\n");
|
||||
SetModuleSymbols(&module2,
|
||||
// The calling frame's function.
|
||||
"FUNC 100 400 10 marsupial\n");
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(0U, modules_without_symbols.size());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(2U, frames->size());
|
||||
|
||||
StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
|
||||
ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
|
||||
frame0->context_validity);
|
||||
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
|
||||
EXPECT_EQ("monotreme", frame0->function_name);
|
||||
EXPECT_EQ(0x40000100ULL, frame0->function_base);
|
||||
|
||||
StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
|
||||
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_SP),
|
||||
frame1->context_validity);
|
||||
EXPECT_EQ(return_address, frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
|
||||
EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
|
||||
EXPECT_EQ("marsupial", frame1->function_name);
|
||||
EXPECT_EQ(0x50000100ULL, frame1->function_base);
|
||||
}
|
||||
|
||||
TEST_F(GetCallerFrame, ScanFirstFrame) {
|
||||
// If the stackwalker resorts to stack scanning, it will scan much
|
||||
// farther to find the caller of the context frame.
|
||||
stack_section.start() = 0x80000000;
|
||||
uint64_t return_address1 = 0x50000100;
|
||||
uint64_t return_address2 = 0x50000900;
|
||||
Label frame1_sp, frame2_sp;
|
||||
stack_section
|
||||
// frame 0
|
||||
.Append(32, 0) // space
|
||||
|
||||
.D64(0x40090000) // junk that's not
|
||||
.D64(0x60000000) // a return address
|
||||
|
||||
.Append(96, 0) // more space
|
||||
|
||||
.D64(return_address1) // actual return address
|
||||
// frame 1
|
||||
.Mark(&frame1_sp)
|
||||
.Append(32, 0) // space
|
||||
|
||||
.D64(0xF0000000) // more junk
|
||||
.D64(0x0000000D)
|
||||
|
||||
.Append(256, 0) // more space
|
||||
|
||||
.D64(return_address2) // actual return address
|
||||
// (won't be found)
|
||||
// frame 2
|
||||
.Mark(&frame2_sp)
|
||||
.Append(64, 0); // end of stack
|
||||
RegionFromSection();
|
||||
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40005510;
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(2U, modules_without_symbols.size());
|
||||
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
|
||||
ASSERT_EQ("module2", modules_without_symbols[1]->debug_file());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(2U, frames->size());
|
||||
|
||||
StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
|
||||
ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
|
||||
frame0->context_validity);
|
||||
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
|
||||
|
||||
StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
|
||||
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_SP),
|
||||
frame1->context_validity);
|
||||
EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
|
||||
EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
|
||||
}
|
||||
|
||||
// Test that set_max_frames_scanned prevents using stack scanning
|
||||
// to find caller frames.
|
||||
TEST_F(GetCallerFrame, ScanningNotAllowed) {
|
||||
// When the stack walker resorts to scanning the stack,
|
||||
// only addresses located within loaded modules are
|
||||
// considered valid return addresses.
|
||||
stack_section.start() = 0x80000000;
|
||||
uint64_t return_address1 = 0x50000100;
|
||||
uint64_t return_address2 = 0x50000900;
|
||||
Label frame1_sp, frame2_sp;
|
||||
stack_section
|
||||
// frame 0
|
||||
.Append(16, 0) // space
|
||||
|
||||
.D64(0x40090000) // junk that's not
|
||||
.D64(0x60000000) // a return address
|
||||
|
||||
.D64(return_address1) // actual return address
|
||||
// frame 1
|
||||
.Mark(&frame1_sp)
|
||||
.Append(16, 0) // space
|
||||
|
||||
.D64(0xF0000000) // more junk
|
||||
.D64(0x0000000D)
|
||||
|
||||
.D64(return_address2) // actual return address
|
||||
// frame 2
|
||||
.Mark(&frame2_sp)
|
||||
.Append(64, 0); // end of stack
|
||||
RegionFromSection();
|
||||
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40005510;
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
Stackwalker::set_max_frames_scanned(0);
|
||||
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(1U, modules_without_symbols.size());
|
||||
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(1U, frames->size());
|
||||
|
||||
StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
|
||||
ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
|
||||
frame0->context_validity);
|
||||
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
|
||||
}
|
||||
|
||||
class GetFramesByFramePointer: public StackwalkerARM64Fixture, public Test { };
|
||||
|
||||
TEST_F(GetFramesByFramePointer, OnlyFramePointer) {
|
||||
stack_section.start() = 0x80000000;
|
||||
uint64_t return_address1 = 0x50000100;
|
||||
uint64_t return_address2 = 0x50000900;
|
||||
Label frame1_sp, frame2_sp;
|
||||
Label frame1_fp, frame2_fp;
|
||||
stack_section
|
||||
// frame 0
|
||||
.Append(64, 0) // Whatever values on the stack.
|
||||
.D64(0x0000000D) // junk that's not
|
||||
.D64(0xF0000000) // a return address.
|
||||
|
||||
.Mark(&frame1_fp) // Next fp will point to the next value.
|
||||
.D64(frame2_fp) // Save current frame pointer.
|
||||
.D64(return_address2) // Save current link register.
|
||||
.Mark(&frame1_sp)
|
||||
|
||||
// frame 1
|
||||
.Append(64, 0) // Whatever values on the stack.
|
||||
.D64(0x0000000D) // junk that's not
|
||||
.D64(0xF0000000) // a return address.
|
||||
|
||||
.Mark(&frame2_fp)
|
||||
.D64(0)
|
||||
.D64(0)
|
||||
.Mark(&frame2_sp)
|
||||
|
||||
// frame 2
|
||||
.Append(64, 0) // Whatever values on the stack.
|
||||
.D64(0x0000000D) // junk that's not
|
||||
.D64(0xF0000000); // a return address.
|
||||
RegionFromSection();
|
||||
|
||||
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40005510;
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_LR] = return_address1;
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_FP] = frame1_fp.Value();
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerARM64 walker(&system_info, &raw_context,
|
||||
&stack_region, &modules, &frame_symbolizer);
|
||||
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(2U, modules_without_symbols.size());
|
||||
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
|
||||
ASSERT_EQ("module2", modules_without_symbols[1]->debug_file());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(3U, frames->size());
|
||||
|
||||
StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
|
||||
ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
|
||||
frame0->context_validity);
|
||||
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
|
||||
|
||||
StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
|
||||
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_LR |
|
||||
StackFrameARM64::CONTEXT_VALID_FP |
|
||||
StackFrameARM64::CONTEXT_VALID_SP),
|
||||
frame1->context_validity);
|
||||
EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
|
||||
EXPECT_EQ(return_address2, frame1->context.iregs[MD_CONTEXT_ARM64_REG_LR]);
|
||||
EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
|
||||
EXPECT_EQ(frame2_fp.Value(),
|
||||
frame1->context.iregs[MD_CONTEXT_ARM64_REG_FP]);
|
||||
|
||||
StackFrameARM64 *frame2 = static_cast<StackFrameARM64 *>(frames->at(2));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame2->trust);
|
||||
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_LR |
|
||||
StackFrameARM64::CONTEXT_VALID_FP |
|
||||
StackFrameARM64::CONTEXT_VALID_SP),
|
||||
frame2->context_validity);
|
||||
EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
|
||||
EXPECT_EQ(0U, frame2->context.iregs[MD_CONTEXT_ARM64_REG_LR]);
|
||||
EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
|
||||
EXPECT_EQ(0U, frame2->context.iregs[MD_CONTEXT_ARM64_REG_FP]);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue