diff --git a/src/processor/microdump_processor_unittest.cc b/src/processor/microdump_processor_unittest.cc index 83bdef95..00f23f55 100644 --- a/src/processor/microdump_processor_unittest.cc +++ b/src/processor/microdump_processor_unittest.cc @@ -232,7 +232,7 @@ TEST_F(MicrodumpProcessorTest, TestProcessMultiple) { ASSERT_EQ("arm", state.system_info()->cpu); ASSERT_EQ("lge/p1_tmo_us/p1:6.0/MRA58K/1603210524c8d:user/release-keys", state.system_info()->os_version); - ASSERT_EQ(5U, state.threads()->at(0)->frames()->size()); + ASSERT_EQ(6U, state.threads()->at(0)->frames()->size()); } TEST_F(MicrodumpProcessorTest, TestProcessMips) { diff --git a/src/processor/stackwalker_arm.cc b/src/processor/stackwalker_arm.cc index f6f2c9bf..f932ded9 100644 --- a/src/processor/stackwalker_arm.cc +++ b/src/processor/stackwalker_arm.cc @@ -238,6 +238,27 @@ StackFrameARM* StackwalkerARM::GetCallerByFramePointer( return frame; } +StackFrameARM* StackwalkerARM::GetCallerByLinkRegister( + const vector& frames) { + StackFrameARM* last_frame = static_cast(frames.back()); + uint32_t last_lr = last_frame->context.iregs[MD_CONTEXT_ARM_REG_LR]; + + if (!(last_frame->context_validity & StackFrameARM::CONTEXT_VALID_LR) || + !InstructionAddressSeemsValid(last_lr)) { + return NULL; + } + + StackFrameARM* frame = new StackFrameARM(); + + frame->trust = StackFrame::FRAME_TRUST_SCAN; + frame->context = last_frame->context; + frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = last_lr; + frame->context_validity = + context_frame_validity_ & (~StackFrameARM::CONTEXT_VALID_LR); + + return frame; +} + StackFrame* StackwalkerARM::GetCallerFrame(const CallStack* stack, bool stack_scan_allowed) { if (!memory_ || !stack) { @@ -264,6 +285,12 @@ StackFrame* StackwalkerARM::GetCallerFrame(const CallStack* stack, if (fp_register_ >= 0 && !frame.get()) frame.reset(GetCallerByFramePointer(frames)); + // For the first frame, return address may still be in the LR register at + // entry. Prefer to use LR register than scanning stack if LR register value + // points to a function range. + if (frames.size() == 1 && !frame.get()) + frame.reset(GetCallerByLinkRegister(frames)); + // If everuthing failed, fall back to stack scanning. if (stack_scan_allowed && !frame.get()) frame.reset(GetCallerByStackScan(frames)); diff --git a/src/processor/stackwalker_arm.h b/src/processor/stackwalker_arm.h index 72ddddcc..9c386070 100644 --- a/src/processor/stackwalker_arm.h +++ b/src/processor/stackwalker_arm.h @@ -82,6 +82,11 @@ class StackwalkerARM : public Stackwalker { // Return NULL on failure. StackFrameARM* GetCallerByFramePointer(const vector& frames); + // Use the link register if it seems to be a valid function adderss. + // The caller takes ownership of the returned frame. Return NULL on failure. + // This is useful when PC register is corrupted. + StackFrameARM* GetCallerByLinkRegister(const vector& frames); + // Scan the stack for plausible return addresses. The caller takes ownership // of the returned frame. Return NULL on failure. StackFrameARM* GetCallerByStackScan(const vector& frames);