1 // Copyright 2015 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/profiler/win32_stack_frame_unwinder.h"
11 #include "base/compiler_specific.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/memory/raw_ptr.h"
14 #include "base/profiler/stack_sampling_profiler_test_util.h"
15 #include "build/build_config.h"
16 #include "testing/gtest/include/gtest/gtest.h"
22 // The image base returned by LookupFunctionEntry starts at this value and is
23 // incremented by the same value with each call.
24 const uintptr_t kImageBaseIncrement = 1 << 20;
26 class TestUnwindFunctions : public Win32StackFrameUnwinder::UnwindFunctions {
28 TestUnwindFunctions();
30 TestUnwindFunctions(const TestUnwindFunctions&) = delete;
31 TestUnwindFunctions& operator=(const TestUnwindFunctions&) = delete;
33 PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter,
34 PDWORD64 image_base) override;
35 void VirtualUnwind(DWORD64 image_base,
36 DWORD64 program_counter,
37 PRUNTIME_FUNCTION runtime_function,
38 CONTEXT* context) override;
40 // These functions set whether the next frame will have a RUNTIME_FUNCTION.
41 void SetHasRuntimeFunction(CONTEXT* context);
42 void SetNoRuntimeFunction(CONTEXT* context);
45 static RUNTIME_FUNCTION* const kInvalidRuntimeFunction;
47 DWORD64 expected_program_counter_;
48 DWORD64 next_image_base_;
49 DWORD64 expected_image_base_;
50 raw_ptr<RUNTIME_FUNCTION> next_runtime_function_;
51 std::vector<RUNTIME_FUNCTION> runtime_functions_;
54 RUNTIME_FUNCTION* const TestUnwindFunctions::kInvalidRuntimeFunction =
55 reinterpret_cast<RUNTIME_FUNCTION*>(static_cast<uintptr_t>(-1));
57 TestUnwindFunctions::TestUnwindFunctions()
58 : expected_program_counter_(0),
59 next_image_base_(kImageBaseIncrement),
60 expected_image_base_(0),
61 next_runtime_function_(kInvalidRuntimeFunction) {}
63 PRUNTIME_FUNCTION TestUnwindFunctions::LookupFunctionEntry(
64 DWORD64 program_counter,
65 PDWORD64 image_base) {
66 EXPECT_EQ(expected_program_counter_, program_counter);
67 *image_base = expected_image_base_ = next_image_base_;
68 next_image_base_ += kImageBaseIncrement;
69 RUNTIME_FUNCTION* return_value = next_runtime_function_;
70 next_runtime_function_ = kInvalidRuntimeFunction;
74 void TestUnwindFunctions::VirtualUnwind(DWORD64 image_base,
75 DWORD64 program_counter,
76 PRUNTIME_FUNCTION runtime_function,
78 ASSERT_NE(kInvalidRuntimeFunction, runtime_function)
79 << "expected call to SetHasRuntimeFunction() or SetNoRuntimeFunction() "
80 << "before invoking TryUnwind()";
81 EXPECT_EQ(expected_image_base_, image_base);
82 expected_image_base_ = 0;
83 EXPECT_EQ(expected_program_counter_, program_counter);
84 expected_program_counter_ = 0;
85 // This function should only be called when LookupFunctionEntry returns
86 // a RUNTIME_FUNCTION.
87 EXPECT_EQ(&runtime_functions_.back(), runtime_function);
90 static void SetContextPc(CONTEXT* context, DWORD64 val) {
91 #if defined(ARCH_CPU_ARM64)
98 void TestUnwindFunctions::SetHasRuntimeFunction(CONTEXT* context) {
99 RUNTIME_FUNCTION runtime_function = {};
100 runtime_function.BeginAddress = 16;
101 #if defined(ARCH_CPU_ARM64)
102 runtime_function.FunctionLength = 256;
104 runtime_function.EndAddress = runtime_function.BeginAddress + 256;
107 runtime_functions_.push_back(runtime_function);
108 next_runtime_function_ = &runtime_functions_.back();
109 expected_program_counter_ =
110 next_image_base_ + runtime_function.BeginAddress + 8;
111 SetContextPc(context, expected_program_counter_);
114 void TestUnwindFunctions::SetNoRuntimeFunction(CONTEXT* context) {
115 expected_program_counter_ = 100;
116 SetContextPc(context, expected_program_counter_);
117 next_runtime_function_ = nullptr;
122 class Win32StackFrameUnwinderTest : public testing::Test {
124 Win32StackFrameUnwinderTest(const Win32StackFrameUnwinderTest&) = delete;
125 Win32StackFrameUnwinderTest& operator=(const Win32StackFrameUnwinderTest&) =
129 Win32StackFrameUnwinderTest() {}
131 // This exists so that Win32StackFrameUnwinder's constructor can be private
132 // with a single friend declaration of this test fixture.
133 std::unique_ptr<Win32StackFrameUnwinder> CreateUnwinder();
135 // Weak pointer to the unwind functions used by last created unwinder.
136 raw_ptr<TestUnwindFunctions, DanglingUntriaged> unwind_functions_;
139 std::unique_ptr<Win32StackFrameUnwinder>
140 Win32StackFrameUnwinderTest::CreateUnwinder() {
141 std::unique_ptr<TestUnwindFunctions> unwind_functions(
142 new TestUnwindFunctions);
143 unwind_functions_ = unwind_functions.get();
145 new Win32StackFrameUnwinder(std::move(unwind_functions)));
148 // Checks the case where all frames have unwind information.
149 TEST_F(Win32StackFrameUnwinderTest, FramesWithUnwindInfo) {
150 std::unique_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
151 CONTEXT context = {0};
153 TestModule stub_module1(kImageBaseIncrement);
154 unwind_functions_->SetHasRuntimeFunction(&context);
155 EXPECT_TRUE(unwinder->TryUnwind(true, &context, &stub_module1));
157 TestModule stub_module2(kImageBaseIncrement * 2);
158 unwind_functions_->SetHasRuntimeFunction(&context);
159 EXPECT_TRUE(unwinder->TryUnwind(false, &context, &stub_module2));
161 TestModule stub_module3(kImageBaseIncrement * 3);
162 unwind_functions_->SetHasRuntimeFunction(&context);
163 EXPECT_TRUE(unwinder->TryUnwind(false, &context, &stub_module3));
166 // Checks that the CONTEXT's stack pointer gets popped when the top frame has no
167 // unwind information.
168 TEST_F(Win32StackFrameUnwinderTest, FrameAtTopWithoutUnwindInfo) {
169 std::unique_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
170 CONTEXT context = {0};
171 DWORD64 next_ip = 0x0123456789abcdef;
172 DWORD64 original_rsp = reinterpret_cast<DWORD64>(&next_ip);
173 #if defined(ARCH_CPU_ARM64)
174 context.Sp = original_rsp;
175 context.Lr = next_ip;
176 context.ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
178 context.Rsp = original_rsp;
181 TestModule stub_module(kImageBaseIncrement);
182 unwind_functions_->SetNoRuntimeFunction(&context);
183 EXPECT_TRUE(unwinder->TryUnwind(true, &context, &stub_module));
184 #if defined(ARCH_CPU_ARM64)
185 EXPECT_EQ(next_ip, context.Pc);
187 EXPECT_EQ(next_ip, context.Rip);
188 EXPECT_EQ(original_rsp + 8, context.Rsp);
192 // Checks that a frame below the top of the stack with missing unwind info
193 // terminates the unwinding.
194 TEST_F(Win32StackFrameUnwinderTest, FrameBelowTopWithoutUnwindInfo) {
196 // First stack, with a bad function below the top of the stack.
197 std::unique_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
198 CONTEXT context = {0};
200 TestModule stub_module(kImageBaseIncrement);
201 unwind_functions_->SetNoRuntimeFunction(&context);
202 EXPECT_FALSE(unwinder->TryUnwind(false, &context, &stub_module));