win32_stack_frame_unwinder_unittest.cc 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // Copyright 2015 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/profiler/win32_stack_frame_unwinder.h"
  5. #include <memory>
  6. #include <utility>
  7. #include <vector>
  8. #include "base/compiler_specific.h"
  9. #include "base/memory/ptr_util.h"
  10. #include "base/memory/raw_ptr.h"
  11. #include "base/profiler/stack_sampling_profiler_test_util.h"
  12. #include "build/build_config.h"
  13. #include "testing/gtest/include/gtest/gtest.h"
  14. namespace base {
  15. namespace {
  16. // The image base returned by LookupFunctionEntry starts at this value and is
  17. // incremented by the same value with each call.
  18. const uintptr_t kImageBaseIncrement = 1 << 20;
  19. class TestUnwindFunctions : public Win32StackFrameUnwinder::UnwindFunctions {
  20. public:
  21. TestUnwindFunctions();
  22. TestUnwindFunctions(const TestUnwindFunctions&) = delete;
  23. TestUnwindFunctions& operator=(const TestUnwindFunctions&) = delete;
  24. PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter,
  25. PDWORD64 image_base) override;
  26. void VirtualUnwind(DWORD64 image_base,
  27. DWORD64 program_counter,
  28. PRUNTIME_FUNCTION runtime_function,
  29. CONTEXT* context) override;
  30. // These functions set whether the next frame will have a RUNTIME_FUNCTION.
  31. void SetHasRuntimeFunction(CONTEXT* context);
  32. void SetNoRuntimeFunction(CONTEXT* context);
  33. private:
  34. static RUNTIME_FUNCTION* const kInvalidRuntimeFunction;
  35. DWORD64 expected_program_counter_;
  36. DWORD64 next_image_base_;
  37. DWORD64 expected_image_base_;
  38. // TODO(crbug.com/1298696): base_unittests breaks with MTECheckedPtr
  39. // enabled. Triage.
  40. //
  41. // This is probably because of kInvalidRuntimeFunction == (uintptr_t) -1.
  42. raw_ptr<RUNTIME_FUNCTION, DegradeToNoOpWhenMTE> next_runtime_function_;
  43. std::vector<RUNTIME_FUNCTION> runtime_functions_;
  44. };
  45. RUNTIME_FUNCTION* const TestUnwindFunctions::kInvalidRuntimeFunction =
  46. reinterpret_cast<RUNTIME_FUNCTION*>(static_cast<uintptr_t>(-1));
  47. TestUnwindFunctions::TestUnwindFunctions()
  48. : expected_program_counter_(0),
  49. next_image_base_(kImageBaseIncrement),
  50. expected_image_base_(0),
  51. next_runtime_function_(kInvalidRuntimeFunction) {}
  52. PRUNTIME_FUNCTION TestUnwindFunctions::LookupFunctionEntry(
  53. DWORD64 program_counter,
  54. PDWORD64 image_base) {
  55. EXPECT_EQ(expected_program_counter_, program_counter);
  56. *image_base = expected_image_base_ = next_image_base_;
  57. next_image_base_ += kImageBaseIncrement;
  58. RUNTIME_FUNCTION* return_value = next_runtime_function_;
  59. next_runtime_function_ = kInvalidRuntimeFunction;
  60. return return_value;
  61. }
  62. void TestUnwindFunctions::VirtualUnwind(DWORD64 image_base,
  63. DWORD64 program_counter,
  64. PRUNTIME_FUNCTION runtime_function,
  65. CONTEXT* context) {
  66. ASSERT_NE(kInvalidRuntimeFunction, runtime_function)
  67. << "expected call to SetHasRuntimeFunction() or SetNoRuntimeFunction() "
  68. << "before invoking TryUnwind()";
  69. EXPECT_EQ(expected_image_base_, image_base);
  70. expected_image_base_ = 0;
  71. EXPECT_EQ(expected_program_counter_, program_counter);
  72. expected_program_counter_ = 0;
  73. // This function should only be called when LookupFunctionEntry returns
  74. // a RUNTIME_FUNCTION.
  75. EXPECT_EQ(&runtime_functions_.back(), runtime_function);
  76. }
  77. static void SetContextPc(CONTEXT* context, DWORD64 val) {
  78. #if defined(ARCH_CPU_ARM64)
  79. context->Pc = val;
  80. #else
  81. context->Rip = val;
  82. #endif
  83. }
  84. void TestUnwindFunctions::SetHasRuntimeFunction(CONTEXT* context) {
  85. RUNTIME_FUNCTION runtime_function = {};
  86. runtime_function.BeginAddress = 16;
  87. #if defined(ARCH_CPU_ARM64)
  88. runtime_function.FunctionLength = 256;
  89. #else
  90. runtime_function.EndAddress = runtime_function.BeginAddress + 256;
  91. #endif
  92. runtime_functions_.push_back(runtime_function);
  93. next_runtime_function_ = &runtime_functions_.back();
  94. expected_program_counter_ =
  95. next_image_base_ + runtime_function.BeginAddress + 8;
  96. SetContextPc(context, expected_program_counter_);
  97. }
  98. void TestUnwindFunctions::SetNoRuntimeFunction(CONTEXT* context) {
  99. expected_program_counter_ = 100;
  100. SetContextPc(context, expected_program_counter_);
  101. next_runtime_function_ = nullptr;
  102. }
  103. } // namespace
  104. class Win32StackFrameUnwinderTest : public testing::Test {
  105. public:
  106. Win32StackFrameUnwinderTest(const Win32StackFrameUnwinderTest&) = delete;
  107. Win32StackFrameUnwinderTest& operator=(const Win32StackFrameUnwinderTest&) =
  108. delete;
  109. protected:
  110. Win32StackFrameUnwinderTest() {}
  111. // This exists so that Win32StackFrameUnwinder's constructor can be private
  112. // with a single friend declaration of this test fixture.
  113. std::unique_ptr<Win32StackFrameUnwinder> CreateUnwinder();
  114. // Weak pointer to the unwind functions used by last created unwinder.
  115. raw_ptr<TestUnwindFunctions> unwind_functions_;
  116. };
  117. std::unique_ptr<Win32StackFrameUnwinder>
  118. Win32StackFrameUnwinderTest::CreateUnwinder() {
  119. std::unique_ptr<TestUnwindFunctions> unwind_functions(
  120. new TestUnwindFunctions);
  121. unwind_functions_ = unwind_functions.get();
  122. return WrapUnique(
  123. new Win32StackFrameUnwinder(std::move(unwind_functions)));
  124. }
  125. // Checks the case where all frames have unwind information.
  126. TEST_F(Win32StackFrameUnwinderTest, FramesWithUnwindInfo) {
  127. std::unique_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
  128. CONTEXT context = {0};
  129. TestModule stub_module1(kImageBaseIncrement);
  130. unwind_functions_->SetHasRuntimeFunction(&context);
  131. EXPECT_TRUE(unwinder->TryUnwind(true, &context, &stub_module1));
  132. TestModule stub_module2(kImageBaseIncrement * 2);
  133. unwind_functions_->SetHasRuntimeFunction(&context);
  134. EXPECT_TRUE(unwinder->TryUnwind(false, &context, &stub_module2));
  135. TestModule stub_module3(kImageBaseIncrement * 3);
  136. unwind_functions_->SetHasRuntimeFunction(&context);
  137. EXPECT_TRUE(unwinder->TryUnwind(false, &context, &stub_module3));
  138. }
  139. // Checks that the CONTEXT's stack pointer gets popped when the top frame has no
  140. // unwind information.
  141. TEST_F(Win32StackFrameUnwinderTest, FrameAtTopWithoutUnwindInfo) {
  142. std::unique_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
  143. CONTEXT context = {0};
  144. DWORD64 next_ip = 0x0123456789abcdef;
  145. DWORD64 original_rsp = reinterpret_cast<DWORD64>(&next_ip);
  146. #if defined(ARCH_CPU_ARM64)
  147. context.Sp = original_rsp;
  148. context.Lr = next_ip;
  149. context.ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
  150. #else
  151. context.Rsp = original_rsp;
  152. #endif
  153. TestModule stub_module(kImageBaseIncrement);
  154. unwind_functions_->SetNoRuntimeFunction(&context);
  155. EXPECT_TRUE(unwinder->TryUnwind(true, &context, &stub_module));
  156. #if defined(ARCH_CPU_ARM64)
  157. EXPECT_EQ(next_ip, context.Pc);
  158. #else
  159. EXPECT_EQ(next_ip, context.Rip);
  160. EXPECT_EQ(original_rsp + 8, context.Rsp);
  161. #endif
  162. }
  163. // Checks that a frame below the top of the stack with missing unwind info
  164. // terminates the unwinding.
  165. TEST_F(Win32StackFrameUnwinderTest, FrameBelowTopWithoutUnwindInfo) {
  166. {
  167. // First stack, with a bad function below the top of the stack.
  168. std::unique_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
  169. CONTEXT context = {0};
  170. TestModule stub_module(kImageBaseIncrement);
  171. unwind_functions_->SetNoRuntimeFunction(&context);
  172. EXPECT_FALSE(unwinder->TryUnwind(false, &context, &stub_module));
  173. }
  174. }
  175. } // namespace base