aboutsummaryrefslogtreecommitdiff
path: root/src/processor/stackwalker_x86_unittest.cc
diff options
context:
space:
mode:
authorivan.penkov@gmail.com <ivan.penkov@gmail.com@4c0a9323-5329-0410-9bdc-e9ce6186880e>2012-11-02 21:43:58 +0000
committerivan.penkov@gmail.com <ivan.penkov@gmail.com@4c0a9323-5329-0410-9bdc-e9ce6186880e>2012-11-02 21:43:58 +0000
commit66a21aad61b57553b933bb6c0f9c8efa96df5612 (patch)
tree05f68eef6a20a53761b0e29149c6f7b91fb5c514 /src/processor/stackwalker_x86_unittest.cc
parentSuspendThread returns a DWORD value, so checking the return value with ">= 0" (diff)
downloadbreakpad-66a21aad61b57553b933bb6c0f9c8efa96df5612.tar.xz
Wrong %ebp after skipping a frame for which the instruction pointer is not in a known module.
http://breakpad.appspot.com/494002/ git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1076 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src/processor/stackwalker_x86_unittest.cc')
-rw-r--r--src/processor/stackwalker_x86_unittest.cc234
1 files changed, 234 insertions, 0 deletions
diff --git a/src/processor/stackwalker_x86_unittest.cc b/src/processor/stackwalker_x86_unittest.cc
index 23efe87d..bf76e881 100644
--- a/src/processor/stackwalker_x86_unittest.cc
+++ b/src/processor/stackwalker_x86_unittest.cc
@@ -1064,6 +1064,240 @@ TEST_F(GetCallerFrame, WindowsFPOSystemCall) {
}
}
+// Scan the stack for a better return address and potentially skip frames
+// when the calculated return address is not in a known module.
+// Note, that the span of this scan is somewhat arbitrarily limited to 30
+// search words (pointers):
+// const int kRASearchWords = 30;
+// This means that frames can be skipped only when their size is relatively
+// small: smaller than kRASearchWords * sizeof(InstructionType)
+TEST_F(GetCallerFrame, ReturnAddressIsNotInKnownModule) {
+ MockCodeModule msvcrt_dll(0x77be0000, 0x58000, "msvcrt.dll", "version1");
+ SetModuleSymbols(&msvcrt_dll, // msvcrt.dll
+ "PUBLIC 38180 0 wcsstr\n"
+ "STACK WIN 4 38180 61 10 0 8 0 0 0 1 $T0 $ebp = $eip $T0 "
+ "4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs "
+ "- = $P $T0 4 + .cbParams + =\n");
+
+ MockCodeModule kernel32_dll(0x7c800000, 0x103000, "kernel32.dll", "version1");
+ SetModuleSymbols(&kernel32_dll, // kernel32.dll
+ "PUBLIC efda 8 FindNextFileW\n"
+ "STACK WIN 4 efda 1bb c 0 8 8 3c 0 1 $T0 $ebp = $eip $T0 "
+ "4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs "
+ "- = $P $T0 4 + .cbParams + =\n");
+
+ MockCodeModule chrome_dll(0x1c30000, 0x28C8000, "chrome.dll", "version1");
+ SetModuleSymbols(&chrome_dll, // chrome.dll
+ "FUNC e3cff 4af 0 file_util::FileEnumerator::Next()\n"
+ "e3cff 1a 711 2505\n"
+ "STACK WIN 4 e3cff 4af 20 0 4 c 94 0 1 $T1 .raSearch = "
+ "$T0 $T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp "
+ "$T1 4 + = $20 $T0 152 - ^ = $23 $T0 156 - ^ = $24 "
+ "$T0 160 - ^ =\n");
+
+ // Create some modules with some stock debugging information.
+ MockCodeModules local_modules;
+ local_modules.Add(&msvcrt_dll);
+ local_modules.Add(&kernel32_dll);
+ local_modules.Add(&chrome_dll);
+
+ Label frame0_esp;
+ Label frame0_ebp;
+ Label frame1_ebp;
+ Label frame2_ebp;
+ Label frame3_ebp;
+
+ stack_section.start() = 0x0932f2d0;
+ stack_section
+ .Mark(&frame0_esp)
+ .D32(0x0764e000)
+ .D32(0x0764e068)
+ .Mark(&frame0_ebp)
+ .D32(frame1_ebp) // Child EBP
+ .D32(0x001767a0) // return address of frame 0
+ // Not in known module
+ .D32(0x0764e0c6)
+ .D32(0x001bb1b8)
+ .D32(0x0764e068)
+ .D32(0x00000003)
+ .D32(0x0764e068)
+ .D32(0x00000003)
+ .D32(0x07578828)
+ .D32(0x0764e000)
+ .D32(0x00000000)
+ .D32(0x001c0010)
+ .D32(0x0764e0c6)
+ .Mark(&frame1_ebp)
+ .D32(frame2_ebp) // Child EBP
+ .D32(0x7c80f10f) // return address of frame 1
+ // inside kernel32!FindNextFileW
+ .D32(0x000008f8)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x0932f34c)
+ .D32(0x0764e000)
+ .D32(0x00001000)
+ .D32(0x00000000)
+ .D32(0x00000001)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x0932f6a8)
+ .D32(0x00000000)
+ .D32(0x0932f6d8)
+ .D32(0x00000000)
+ .D32(0x000000d6)
+ .D32(0x0764e000)
+ .D32(0x7ff9a000)
+ .D32(0x0932f3fc)
+ .D32(0x00000001)
+ .D32(0x00000001)
+ .D32(0x07578828)
+ .D32(0x0000002e)
+ .D32(0x0932f340)
+ .D32(0x0932eef4)
+ .D32(0x0932ffdc)
+ .D32(0x7c839ad8)
+ .D32(0x7c80f0d8)
+ .D32(0x00000000)
+ .Mark(&frame2_ebp)
+ .D32(frame3_ebp) // Child EBP
+ .D32(0x01d13f91) // return address of frame 2
+ // inside chrome_dll!file_util::FileEnumerator::Next
+ .D32(0x07578828)
+ .D32(0x0932f6ac)
+ .D32(0x0932f9c4)
+ .D32(0x0932f9b4)
+ .D32(0x00000000)
+ .D32(0x00000003)
+ .D32(0x0932f978)
+ .D32(0x01094330)
+ .D32(0x00000000)
+ .D32(0x00000001)
+ .D32(0x01094330)
+ .D32(0x00000000)
+ .D32(0x00000000)
+ .D32(0x07f30000)
+ .D32(0x01c3ba17)
+ .D32(0x08bab840)
+ .D32(0x07f31580)
+ .D32(0x00000000)
+ .D32(0x00000007)
+ .D32(0x0932f940)
+ .D32(0x0000002e)
+ .D32(0x0932f40c)
+ .D32(0x01d13b53)
+ .D32(0x0932f958)
+ .D32(0x00000001)
+ .D32(0x00000007)
+ .D32(0x0932f940)
+ .D32(0x0000002e)
+ .D32(0x00000000)
+ .D32(0x0932f6ac)
+ .D32(0x01e13ef0)
+ .D32(0x00000001)
+ .D32(0x00000007)
+ .D32(0x0932f958)
+ .D32(0x08bab840)
+ .D32(0x0932f9b4)
+ .D32(0x00000000)
+ .D32(0x0932f9b4)
+ .D32(0x000000a7)
+ .D32(0x000000a7)
+ .D32(0x0932f998)
+ .D32(0x579627a2)
+ .Mark(&frame3_ebp)
+ .D32(0) // saved %ebp (stack end)
+ .D32(0); // saved %eip (stack end)
+
+ RegionFromSection();
+ raw_context.eip = 0x77c181cd; // inside msvcrt!wcsstr
+ raw_context.esp = frame0_esp.Value();
+ raw_context.ebp = frame0_ebp.Value();
+ // sanity
+ ASSERT_TRUE(raw_context.esp == stack_section.start().Value());
+ ASSERT_TRUE(raw_context.ebp == stack_section.start().Value() + 8);
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, &stack_region,
+ &local_modules, &frame_symbolizer);
+ ASSERT_TRUE(walker.Walk(&call_stack));
+ frames = call_stack.frames();
+
+ ASSERT_EQ(3U, frames->size());
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0x77c181cdU, frame0->instruction);
+ EXPECT_EQ(0x77c181cdU, frame0->context.eip);
+ EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
+ EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp);
+ EXPECT_EQ(&msvcrt_dll, frame0->module);
+ EXPECT_EQ("wcsstr", frame0->function_name);
+ ASSERT_TRUE(frame0->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame0->windows_frame_info->type_);
+ EXPECT_EQ("$T0 $ebp = $eip $T0 "
+ "4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs "
+ "- = $P $T0 4 + .cbParams + =",
+ frame0->windows_frame_info->program_string);
+ // It has program string, so allocates_base_pointer is not expected
+ EXPECT_FALSE(frame0->windows_frame_info->allocates_base_pointer);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP),
+ frame1->context_validity);
+ EXPECT_EQ(0x7c80f10fU, frame1->instruction + 1);
+ EXPECT_EQ(0x7c80f10fU, frame1->context.eip);
+ // frame 1 was skipped, so intead of frame1_ebp compare with frame2_ebp.
+ EXPECT_EQ(frame2_ebp.Value(), frame1->context.ebp);
+ EXPECT_EQ(&kernel32_dll, frame1->module);
+ EXPECT_EQ("FindNextFileW", frame1->function_name);
+ ASSERT_TRUE(frame1->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame1->windows_frame_info->type_);
+ EXPECT_EQ("$T0 $ebp = $eip $T0 "
+ "4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs "
+ "- = $P $T0 4 + .cbParams + =",
+ frame1->windows_frame_info->program_string);
+ EXPECT_FALSE(frame1->windows_frame_info->allocates_base_pointer);
+ }
+
+ { // To avoid reusing locals by mistake
+ StackFrameX86 *frame2 = static_cast<StackFrameX86 *>(frames->at(2));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame2->trust);
+ ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP),
+ frame2->context_validity);
+ EXPECT_EQ(0x01d13f91U, frame2->instruction + 1);
+ EXPECT_EQ(0x01d13f91U, frame2->context.eip);
+ // frame 1 was skipped, so intead of frame2_ebp compare with frame3_ebp.
+ EXPECT_EQ(frame3_ebp.Value(), frame2->context.ebp);
+ EXPECT_EQ(&chrome_dll, frame2->module);
+ EXPECT_EQ("file_util::FileEnumerator::Next()", frame2->function_name);
+ ASSERT_TRUE(frame2->windows_frame_info != NULL);
+ EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame2->windows_frame_info->valid);
+ EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA,
+ frame2->windows_frame_info->type_);
+ EXPECT_EQ("$T1 .raSearch = "
+ "$T0 $T1 4 - 8 @ = $ebp $T1 4 - ^ = $eip $T1 ^ = $esp "
+ "$T1 4 + = $20 $T0 152 - ^ = $23 $T0 156 - ^ = $24 "
+ "$T0 160 - ^ =",
+ frame2->windows_frame_info->program_string);
+ EXPECT_FALSE(frame2->windows_frame_info->allocates_base_pointer);
+ }
+}
+
struct CFIFixture: public StackwalkerX86Fixture {
CFIFixture() {
// Provide a bunch of STACK CFI records; individual tests walk to the