Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 5 #include "base/compiler_specific.h" | |
| 6 #include "base/memory/scoped_ptr.h" | |
| 7 #include "base/profiler/win32_stack_frame_unwinder.h" | |
| 8 #include "testing/gtest/include/gtest/gtest.h" | |
| 9 | |
| 10 namespace base { | |
| 11 | |
| 12 namespace { | |
| 13 | |
| 14 class TestUnwindFunctions : public Win32StackFrameUnwinder::UnwindFunctions { | |
| 15 public: | |
| 16 TestUnwindFunctions(); | |
| 17 | |
| 18 PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter, | |
| 19 PDWORD64 image_base) override; | |
| 20 void VirtualUnwind(DWORD64 image_base, | |
| 21 DWORD64 program_counter, | |
| 22 PRUNTIME_FUNCTION runtime_function, | |
| 23 CONTEXT* context) override; | |
| 24 | |
| 25 void SetNoUnwindInfoForNextFrame(); | |
| 26 void SetImageBaseForNextFrame(DWORD64 image_base); | |
| 27 | |
| 28 private: | |
| 29 enum { kRuntimeFunctionPointerIncrement = 1, kImageBaseIncrement = 1 << 20 }; | |
| 30 | |
| 31 static const PRUNTIME_FUNCTION kNonNullRuntimeFunctionPointer; | |
| 32 | |
| 33 DWORD64 supplied_program_counter_; | |
| 34 DWORD64 custom_image_base_; | |
| 35 DWORD64 next_image_base_; | |
| 36 bool next_lookup_returns_null_; | |
| 37 | |
| 38 DISALLOW_COPY_AND_ASSIGN(TestUnwindFunctions); | |
| 39 }; | |
| 40 | |
| 41 // This value is opaque to Win32StackFrameUnwinder. | |
| 42 const PRUNTIME_FUNCTION TestUnwindFunctions::kNonNullRuntimeFunctionPointer = | |
| 43 reinterpret_cast<PRUNTIME_FUNCTION>(8); | |
| 44 | |
| 45 TestUnwindFunctions::TestUnwindFunctions() | |
| 46 : supplied_program_counter_(0), | |
| 47 custom_image_base_(0), | |
| 48 next_image_base_(1 << 20), | |
|
Nico
2015/08/18 23:20:24
s/1 << 20/kImageBaseIncrement/ (?)
Mike Wittman
2015/08/18 23:43:10
Done.
| |
| 49 next_lookup_returns_null_(false) { | |
| 50 } | |
| 51 | |
| 52 PRUNTIME_FUNCTION TestUnwindFunctions::LookupFunctionEntry( | |
| 53 DWORD64 program_counter, | |
| 54 PDWORD64 image_base) { | |
| 55 supplied_program_counter_ = program_counter; | |
| 56 if (custom_image_base_) { | |
| 57 *image_base = custom_image_base_; | |
| 58 custom_image_base_ = 0; | |
| 59 } else { | |
| 60 *image_base = next_image_base_; | |
| 61 next_image_base_ += kImageBaseIncrement; | |
| 62 } | |
| 63 if (next_lookup_returns_null_) { | |
| 64 next_lookup_returns_null_ = false; | |
| 65 return nullptr; | |
| 66 } | |
| 67 | |
| 68 return kNonNullRuntimeFunctionPointer; | |
| 69 } | |
| 70 | |
| 71 void TestUnwindFunctions::VirtualUnwind(DWORD64 image_base, | |
| 72 DWORD64 program_counter, | |
| 73 PRUNTIME_FUNCTION runtime_function, | |
| 74 CONTEXT* context) { | |
| 75 EXPECT_EQ(next_image_base_ - kImageBaseIncrement, image_base); | |
| 76 EXPECT_EQ(supplied_program_counter_, program_counter); | |
| 77 // This function should only be called when LookupFunctionEntry returns a | |
| 78 // non-null value. | |
| 79 EXPECT_EQ(kNonNullRuntimeFunctionPointer, runtime_function); | |
| 80 } | |
| 81 | |
| 82 void TestUnwindFunctions::SetNoUnwindInfoForNextFrame() { | |
| 83 next_lookup_returns_null_ = true; | |
| 84 } | |
| 85 | |
| 86 void TestUnwindFunctions::SetImageBaseForNextFrame(DWORD64 image_base) { | |
| 87 next_image_base_ = image_base; | |
| 88 } | |
| 89 | |
| 90 } // namespace | |
| 91 | |
| 92 class Win32StackFrameUnwinderTest : public testing::Test { | |
| 93 protected: | |
| 94 Win32StackFrameUnwinderTest() {} | |
| 95 | |
| 96 scoped_ptr<Win32StackFrameUnwinder> CreateUnwinder(); | |
| 97 | |
| 98 TestUnwindFunctions unwind_functions_; | |
| 99 | |
| 100 private: | |
| 101 DISALLOW_COPY_AND_ASSIGN(Win32StackFrameUnwinderTest); | |
| 102 }; | |
| 103 | |
| 104 scoped_ptr<Win32StackFrameUnwinder> | |
| 105 Win32StackFrameUnwinderTest::CreateUnwinder() { | |
| 106 return make_scoped_ptr(new Win32StackFrameUnwinder(&unwind_functions_)); | |
| 107 } | |
| 108 | |
| 109 // Checks the case where all frames have unwind information. | |
| 110 TEST_F(Win32StackFrameUnwinderTest, FramesWithUnwindInfo) { | |
| 111 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); | |
|
Nico
2015/08/18 23:20:24
Can't this just live on the stack? (also below) Th
Mike Wittman
2015/08/18 23:43:10
It could, but I believe I'd have to do one of: mak
Nico
2015/08/18 23:49:48
Maybe add a "// This exists so that Win32StackFram
Mike Wittman
2015/08/18 23:58:18
Done.
| |
| 112 CONTEXT context = {0}; | |
| 113 EXPECT_TRUE(unwinder->TryUnwind(&context)); | |
| 114 EXPECT_TRUE(unwinder->TryUnwind(&context)); | |
| 115 EXPECT_TRUE(unwinder->TryUnwind(&context)); | |
| 116 } | |
| 117 | |
| 118 // Checks that the CONTEXT's stack pointer gets popped when the top frame has no | |
| 119 // unwind information. | |
| 120 TEST_F(Win32StackFrameUnwinderTest, FrameAtTopWithoutUnwindInfo) { | |
| 121 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); | |
| 122 CONTEXT context = {0}; | |
| 123 const DWORD64 original_rsp = 128; | |
| 124 context.Rsp = original_rsp; | |
| 125 unwind_functions_.SetNoUnwindInfoForNextFrame(); | |
| 126 EXPECT_TRUE(unwinder->TryUnwind(&context)); | |
| 127 EXPECT_EQ(original_rsp, context.Rip); | |
| 128 EXPECT_EQ(original_rsp + 8, context.Rsp); | |
| 129 | |
| 130 EXPECT_TRUE(unwinder->TryUnwind(&context)); | |
| 131 EXPECT_TRUE(unwinder->TryUnwind(&context)); | |
| 132 } | |
| 133 | |
| 134 // Checks that a frame below the top of the stack with missing unwind info | |
| 135 // results in blacklisting the module. | |
| 136 TEST_F(Win32StackFrameUnwinderTest, BlacklistedModule) { | |
| 137 const DWORD64 image_base_for_module_with_bad_function = 1024; | |
| 138 { | |
| 139 // First stack, with a bad function below the top of the stack. | |
| 140 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); | |
| 141 CONTEXT context = {0}; | |
| 142 EXPECT_TRUE(unwinder->TryUnwind(&context)); | |
| 143 | |
| 144 unwind_functions_.SetNoUnwindInfoForNextFrame(); | |
| 145 unwind_functions_.SetImageBaseForNextFrame( | |
| 146 image_base_for_module_with_bad_function); | |
| 147 EXPECT_FALSE(unwinder->TryUnwind(&context)); | |
| 148 } | |
| 149 | |
| 150 { | |
| 151 // Second stack; check that a function at the top of the stack without | |
| 152 // unwind info from the previously-seen module is blacklisted. | |
| 153 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); | |
| 154 CONTEXT context = {0}; | |
| 155 unwind_functions_.SetNoUnwindInfoForNextFrame(); | |
| 156 unwind_functions_.SetImageBaseForNextFrame( | |
| 157 image_base_for_module_with_bad_function); | |
| 158 EXPECT_FALSE(unwinder->TryUnwind(&context)); | |
| 159 } | |
| 160 | |
| 161 { | |
| 162 // Third stack; check that a function at the top of the stack *with* unwind | |
| 163 // info from the previously-seen module is not blacklisted. Then check that | |
| 164 // functions below the top of the stack with unwind info are not | |
| 165 // blacklisted, regardless of whether they are in the previously-seen | |
| 166 // module. | |
| 167 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); | |
| 168 CONTEXT context = {0}; | |
| 169 unwind_functions_.SetImageBaseForNextFrame( | |
| 170 image_base_for_module_with_bad_function); | |
| 171 EXPECT_TRUE(unwinder->TryUnwind(&context)); | |
| 172 | |
| 173 EXPECT_TRUE(unwinder->TryUnwind(&context)); | |
| 174 | |
| 175 unwind_functions_.SetImageBaseForNextFrame( | |
| 176 image_base_for_module_with_bad_function); | |
| 177 EXPECT_TRUE(unwinder->TryUnwind(&context)); | |
| 178 } | |
| 179 | |
| 180 { | |
| 181 // Fourth stack; check that a function at the top of the stack without | |
| 182 // unwind info and not from the previously-seen module is not | |
| 183 // blacklisted. Then check that functions below the top of the stack with | |
| 184 // unwind info are not blacklisted, regardless of whether they are in the | |
| 185 // previously-seen module. | |
| 186 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); | |
| 187 CONTEXT context = {0}; | |
| 188 unwind_functions_.SetNoUnwindInfoForNextFrame(); | |
| 189 EXPECT_TRUE(unwinder->TryUnwind(&context)); | |
| 190 | |
| 191 EXPECT_TRUE(unwinder->TryUnwind(&context)); | |
| 192 | |
| 193 unwind_functions_.SetImageBaseForNextFrame( | |
| 194 image_base_for_module_with_bad_function); | |
| 195 EXPECT_TRUE(unwinder->TryUnwind(&context)); | |
| 196 } | |
| 197 } | |
| 198 | |
| 199 // Checks that a frame below the top of the stack with missing unwind info does | |
| 200 // not result in blacklisting the module if the first frame also was missing | |
| 201 // unwind info. This ensures we don't blacklist an innocent module because the | |
| 202 // first frame was bad but we didn't know it at the time. | |
| 203 TEST_F(Win32StackFrameUnwinderTest, ModuleFromQuestionableFrameNotBlacklisted) { | |
| 204 const DWORD64 image_base_for_questionable_module = 2048; | |
| 205 { | |
| 206 // First stack, with both the first and second frames missing unwind info. | |
| 207 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); | |
| 208 CONTEXT context = {0}; | |
| 209 unwind_functions_.SetNoUnwindInfoForNextFrame(); | |
| 210 EXPECT_TRUE(unwinder->TryUnwind(&context)); | |
| 211 | |
| 212 unwind_functions_.SetNoUnwindInfoForNextFrame(); | |
| 213 unwind_functions_.SetImageBaseForNextFrame( | |
| 214 image_base_for_questionable_module); | |
| 215 EXPECT_FALSE(unwinder->TryUnwind(&context)); | |
| 216 } | |
| 217 | |
| 218 { | |
| 219 // Second stack; check that the questionable module was not blacklisted. | |
| 220 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); | |
| 221 CONTEXT context = {0}; | |
| 222 unwind_functions_.SetNoUnwindInfoForNextFrame(); | |
| 223 unwind_functions_.SetImageBaseForNextFrame( | |
| 224 image_base_for_questionable_module); | |
| 225 EXPECT_TRUE(unwinder->TryUnwind(&context)); | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 } // namespace base | |
| OLD | NEW |