Fix emulator build error
[platform/framework/web/chromium-efl.git] / base / profiler / win32_stack_frame_unwinder_unittest.cc
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.
4
5 #include "base/profiler/win32_stack_frame_unwinder.h"
6
7 #include <memory>
8 #include <utility>
9 #include <vector>
10
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"
17
18 namespace base {
19
20 namespace {
21
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;
25
26 class TestUnwindFunctions : public Win32StackFrameUnwinder::UnwindFunctions {
27  public:
28   TestUnwindFunctions();
29
30   TestUnwindFunctions(const TestUnwindFunctions&) = delete;
31   TestUnwindFunctions& operator=(const TestUnwindFunctions&) = delete;
32
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;
39
40   // These functions set whether the next frame will have a RUNTIME_FUNCTION.
41   void SetHasRuntimeFunction(CONTEXT* context);
42   void SetNoRuntimeFunction(CONTEXT* context);
43
44  private:
45   static RUNTIME_FUNCTION* const kInvalidRuntimeFunction;
46
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_;
52 };
53
54 RUNTIME_FUNCTION* const TestUnwindFunctions::kInvalidRuntimeFunction =
55     reinterpret_cast<RUNTIME_FUNCTION*>(static_cast<uintptr_t>(-1));
56
57 TestUnwindFunctions::TestUnwindFunctions()
58     : expected_program_counter_(0),
59       next_image_base_(kImageBaseIncrement),
60       expected_image_base_(0),
61       next_runtime_function_(kInvalidRuntimeFunction) {}
62
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;
71   return return_value;
72 }
73
74 void TestUnwindFunctions::VirtualUnwind(DWORD64 image_base,
75                                         DWORD64 program_counter,
76                                         PRUNTIME_FUNCTION runtime_function,
77                                         CONTEXT* context) {
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);
88 }
89
90 static void SetContextPc(CONTEXT* context, DWORD64 val) {
91 #if defined(ARCH_CPU_ARM64)
92   context->Pc = val;
93 #else
94   context->Rip = val;
95 #endif
96 }
97
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;
103 #else
104   runtime_function.EndAddress = runtime_function.BeginAddress + 256;
105 #endif
106
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_);
112 }
113
114 void TestUnwindFunctions::SetNoRuntimeFunction(CONTEXT* context) {
115   expected_program_counter_ = 100;
116   SetContextPc(context, expected_program_counter_);
117   next_runtime_function_ = nullptr;
118 }
119
120 }  // namespace
121
122 class Win32StackFrameUnwinderTest : public testing::Test {
123  public:
124   Win32StackFrameUnwinderTest(const Win32StackFrameUnwinderTest&) = delete;
125   Win32StackFrameUnwinderTest& operator=(const Win32StackFrameUnwinderTest&) =
126       delete;
127
128  protected:
129   Win32StackFrameUnwinderTest() {}
130
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();
134
135   // Weak pointer to the unwind functions used by last created unwinder.
136   raw_ptr<TestUnwindFunctions, DanglingUntriaged> unwind_functions_;
137 };
138
139 std::unique_ptr<Win32StackFrameUnwinder>
140 Win32StackFrameUnwinderTest::CreateUnwinder() {
141   std::unique_ptr<TestUnwindFunctions> unwind_functions(
142       new TestUnwindFunctions);
143   unwind_functions_ = unwind_functions.get();
144   return WrapUnique(
145       new Win32StackFrameUnwinder(std::move(unwind_functions)));
146 }
147
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};
152
153   TestModule stub_module1(kImageBaseIncrement);
154   unwind_functions_->SetHasRuntimeFunction(&context);
155   EXPECT_TRUE(unwinder->TryUnwind(true, &context, &stub_module1));
156
157   TestModule stub_module2(kImageBaseIncrement * 2);
158   unwind_functions_->SetHasRuntimeFunction(&context);
159   EXPECT_TRUE(unwinder->TryUnwind(false, &context, &stub_module2));
160
161   TestModule stub_module3(kImageBaseIncrement * 3);
162   unwind_functions_->SetHasRuntimeFunction(&context);
163   EXPECT_TRUE(unwinder->TryUnwind(false, &context, &stub_module3));
164 }
165
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;
177 #else
178   context.Rsp = original_rsp;
179 #endif
180
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);
186 #else
187   EXPECT_EQ(next_ip, context.Rip);
188   EXPECT_EQ(original_rsp + 8, context.Rsp);
189 #endif
190 }
191
192 // Checks that a frame below the top of the stack with missing unwind info
193 // terminates the unwinding.
194 TEST_F(Win32StackFrameUnwinderTest, FrameBelowTopWithoutUnwindInfo) {
195   {
196     // First stack, with a bad function below the top of the stack.
197     std::unique_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
198     CONTEXT context = {0};
199
200     TestModule stub_module(kImageBaseIncrement);
201     unwind_functions_->SetNoRuntimeFunction(&context);
202     EXPECT_FALSE(unwinder->TryUnwind(false, &context, &stub_module));
203   }
204 }
205
206 }  // namespace base