| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/profiler/win32_stack_frame_unwinder.h" | 5 #include "base/profiler/win32_stack_frame_unwinder.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 void VirtualUnwind(DWORD64 image_base, | 24 void VirtualUnwind(DWORD64 image_base, |
| 25 DWORD64 program_counter, | 25 DWORD64 program_counter, |
| 26 PRUNTIME_FUNCTION runtime_function, | 26 PRUNTIME_FUNCTION runtime_function, |
| 27 CONTEXT* context) override; | 27 CONTEXT* context) override; |
| 28 ScopedModuleHandle GetModuleForProgramCounter( | 28 ScopedModuleHandle GetModuleForProgramCounter( |
| 29 DWORD64 program_counter) override; | 29 DWORD64 program_counter) override; |
| 30 | 30 |
| 31 // Instructs GetModuleForProgramCounter to return null on the next call. | 31 // Instructs GetModuleForProgramCounter to return null on the next call. |
| 32 void SetUnloadedModule(); | 32 void SetUnloadedModule(); |
| 33 | 33 |
| 34 // These functions set whether the next frame will have a RUNTIME_FUNCTION, | 34 // These functions set whether the next frame will have a RUNTIME_FUNCTION. |
| 35 // and allow specification of a custom image_base. | |
| 36 void SetHasRuntimeFunction(CONTEXT* context); | 35 void SetHasRuntimeFunction(CONTEXT* context); |
| 37 void SetHasRuntimeFunction(DWORD64 image_base, CONTEXT* context); | |
| 38 void SetHasRuntimeFunction(DWORD64 image_base, | |
| 39 const RUNTIME_FUNCTION& runtime_function, | |
| 40 DWORD program_counter_offset, | |
| 41 CONTEXT* context); | |
| 42 void SetNoRuntimeFunction(CONTEXT* context); | 36 void SetNoRuntimeFunction(CONTEXT* context); |
| 43 void SetNoRuntimeFunction(DWORD64 image_base, CONTEXT* context); | |
| 44 | 37 |
| 45 private: | 38 private: |
| 46 enum RuntimeFunctionState { NO_RUNTIME_FUNCTION, HAS_RUNTIME_FUNCTION }; | |
| 47 | |
| 48 enum { kImageBaseIncrement = 1 << 20 }; | 39 enum { kImageBaseIncrement = 1 << 20 }; |
| 49 | 40 |
| 50 // Sets whether the next frame should have a RUNTIME_FUNCTION, and allows | |
| 51 // specification of a custom image_base. | |
| 52 void SetNextFrameState(RuntimeFunctionState runtime_function_state, | |
| 53 DWORD64 image_base, | |
| 54 CONTEXT* context); | |
| 55 | |
| 56 static RUNTIME_FUNCTION* const kInvalidRuntimeFunction; | 41 static RUNTIME_FUNCTION* const kInvalidRuntimeFunction; |
| 57 | 42 |
| 58 bool module_is_loaded_; | 43 bool module_is_loaded_; |
| 59 DWORD64 expected_program_counter_; | 44 DWORD64 expected_program_counter_; |
| 60 DWORD64 custom_image_base_; | |
| 61 DWORD64 next_image_base_; | 45 DWORD64 next_image_base_; |
| 62 DWORD64 expected_image_base_; | 46 DWORD64 expected_image_base_; |
| 63 RUNTIME_FUNCTION* next_runtime_function_; | 47 RUNTIME_FUNCTION* next_runtime_function_; |
| 64 std::vector<RUNTIME_FUNCTION> runtime_functions_; | 48 std::vector<RUNTIME_FUNCTION> runtime_functions_; |
| 65 | 49 |
| 66 DISALLOW_COPY_AND_ASSIGN(TestUnwindFunctions); | 50 DISALLOW_COPY_AND_ASSIGN(TestUnwindFunctions); |
| 67 }; | 51 }; |
| 68 | 52 |
| 69 RUNTIME_FUNCTION* const TestUnwindFunctions::kInvalidRuntimeFunction = | 53 RUNTIME_FUNCTION* const TestUnwindFunctions::kInvalidRuntimeFunction = |
| 70 reinterpret_cast<RUNTIME_FUNCTION*>(static_cast<uintptr_t>(-1)); | 54 reinterpret_cast<RUNTIME_FUNCTION*>(static_cast<uintptr_t>(-1)); |
| 71 | 55 |
| 72 TestUnwindFunctions::TestUnwindFunctions() | 56 TestUnwindFunctions::TestUnwindFunctions() |
| 73 : module_is_loaded_(true), | 57 : module_is_loaded_(true), |
| 74 expected_program_counter_(0), | 58 expected_program_counter_(0), |
| 75 custom_image_base_(0), | |
| 76 next_image_base_(kImageBaseIncrement), | 59 next_image_base_(kImageBaseIncrement), |
| 77 expected_image_base_(0), | 60 expected_image_base_(0), |
| 78 next_runtime_function_(kInvalidRuntimeFunction) { | 61 next_runtime_function_(kInvalidRuntimeFunction) { |
| 79 } | 62 } |
| 80 | 63 |
| 81 PRUNTIME_FUNCTION TestUnwindFunctions::LookupFunctionEntry( | 64 PRUNTIME_FUNCTION TestUnwindFunctions::LookupFunctionEntry( |
| 82 DWORD64 program_counter, | 65 DWORD64 program_counter, |
| 83 PDWORD64 image_base) { | 66 PDWORD64 image_base) { |
| 84 EXPECT_EQ(expected_program_counter_, program_counter); | 67 EXPECT_EQ(expected_program_counter_, program_counter); |
| 85 if (custom_image_base_) { | 68 *image_base = expected_image_base_ = next_image_base_; |
| 86 *image_base = expected_image_base_ = custom_image_base_; | 69 next_image_base_ += kImageBaseIncrement; |
| 87 custom_image_base_ = 0; | |
| 88 } else { | |
| 89 *image_base = expected_image_base_ = next_image_base_; | |
| 90 next_image_base_ += kImageBaseIncrement; | |
| 91 } | |
| 92 RUNTIME_FUNCTION* return_value = next_runtime_function_; | 70 RUNTIME_FUNCTION* return_value = next_runtime_function_; |
| 93 next_runtime_function_ = kInvalidRuntimeFunction; | 71 next_runtime_function_ = kInvalidRuntimeFunction; |
| 94 return return_value; | 72 return return_value; |
| 95 } | 73 } |
| 96 | 74 |
| 97 void TestUnwindFunctions::VirtualUnwind(DWORD64 image_base, | 75 void TestUnwindFunctions::VirtualUnwind(DWORD64 image_base, |
| 98 DWORD64 program_counter, | 76 DWORD64 program_counter, |
| 99 PRUNTIME_FUNCTION runtime_function, | 77 PRUNTIME_FUNCTION runtime_function, |
| 100 CONTEXT* context) { | 78 CONTEXT* context) { |
| 101 ASSERT_NE(kInvalidRuntimeFunction, runtime_function) | 79 ASSERT_NE(kInvalidRuntimeFunction, runtime_function) |
| (...skipping 15 matching lines...) Expand all Loading... |
| 117 return ScopedModuleHandle(return_non_null_value ? | 95 return ScopedModuleHandle(return_non_null_value ? |
| 118 ModuleHandleTraits::kNonNullModuleForTesting : | 96 ModuleHandleTraits::kNonNullModuleForTesting : |
| 119 nullptr); | 97 nullptr); |
| 120 } | 98 } |
| 121 | 99 |
| 122 void TestUnwindFunctions::SetUnloadedModule() { | 100 void TestUnwindFunctions::SetUnloadedModule() { |
| 123 module_is_loaded_ = false; | 101 module_is_loaded_ = false; |
| 124 } | 102 } |
| 125 | 103 |
| 126 void TestUnwindFunctions::SetHasRuntimeFunction(CONTEXT* context) { | 104 void TestUnwindFunctions::SetHasRuntimeFunction(CONTEXT* context) { |
| 127 SetNextFrameState(HAS_RUNTIME_FUNCTION, 0, context); | 105 RUNTIME_FUNCTION runtime_function = {}; |
| 128 } | 106 runtime_function.BeginAddress = 16; |
| 129 | 107 runtime_function.EndAddress = runtime_function.BeginAddress + 256; |
| 130 void TestUnwindFunctions::SetHasRuntimeFunction(DWORD64 image_base, | |
| 131 CONTEXT* context) { | |
| 132 SetNextFrameState(HAS_RUNTIME_FUNCTION, image_base, context); | |
| 133 } | |
| 134 | |
| 135 void TestUnwindFunctions::SetHasRuntimeFunction( | |
| 136 DWORD64 image_base, | |
| 137 const RUNTIME_FUNCTION& runtime_function, | |
| 138 DWORD program_counter_offset, | |
| 139 CONTEXT* context) { | |
| 140 custom_image_base_ = image_base; | |
| 141 runtime_functions_.push_back(runtime_function); | 108 runtime_functions_.push_back(runtime_function); |
| 142 next_runtime_function_ = &runtime_functions_.back(); | 109 next_runtime_function_ = &runtime_functions_.back(); |
| 110 |
| 143 expected_program_counter_ = context->Rip = | 111 expected_program_counter_ = context->Rip = |
| 144 image_base + program_counter_offset; | 112 next_image_base_ + runtime_function.BeginAddress + 8; |
| 145 } | 113 } |
| 146 | 114 |
| 147 void TestUnwindFunctions::SetNoRuntimeFunction(CONTEXT* context) { | 115 void TestUnwindFunctions::SetNoRuntimeFunction(CONTEXT* context) { |
| 148 SetNextFrameState(NO_RUNTIME_FUNCTION, 0, context); | 116 expected_program_counter_ = context->Rip = 100; |
| 149 } | 117 next_runtime_function_ = nullptr; |
| 150 | |
| 151 void TestUnwindFunctions::SetNoRuntimeFunction(DWORD64 image_base, | |
| 152 CONTEXT* context) { | |
| 153 SetNextFrameState(NO_RUNTIME_FUNCTION, image_base, context); | |
| 154 } | |
| 155 | |
| 156 | |
| 157 void TestUnwindFunctions::SetNextFrameState( | |
| 158 RuntimeFunctionState runtime_function_state, | |
| 159 DWORD64 image_base, | |
| 160 CONTEXT* context) { | |
| 161 if (image_base) | |
| 162 custom_image_base_ = image_base; | |
| 163 | |
| 164 if (runtime_function_state == HAS_RUNTIME_FUNCTION) { | |
| 165 RUNTIME_FUNCTION runtime_function = {}; | |
| 166 runtime_function.BeginAddress = 16; | |
| 167 runtime_function.EndAddress = runtime_function.BeginAddress + 256; | |
| 168 runtime_functions_.push_back(runtime_function); | |
| 169 next_runtime_function_ = &runtime_functions_.back(); | |
| 170 | |
| 171 DWORD64 image_base = custom_image_base_ ? custom_image_base_ : | |
| 172 next_image_base_; | |
| 173 expected_program_counter_ = context->Rip = | |
| 174 image_base + runtime_function.BeginAddress + 8; | |
| 175 } else { | |
| 176 expected_program_counter_ = context->Rip = 100; | |
| 177 next_runtime_function_ = nullptr; | |
| 178 } | |
| 179 } | 118 } |
| 180 | 119 |
| 181 } // namespace | 120 } // namespace |
| 182 | 121 |
| 183 class Win32StackFrameUnwinderTest : public testing::Test { | 122 class Win32StackFrameUnwinderTest : public testing::Test { |
| 184 protected: | 123 protected: |
| 185 Win32StackFrameUnwinderTest() {} | 124 Win32StackFrameUnwinderTest() {} |
| 186 | 125 |
| 187 // This exists so that Win32StackFrameUnwinder's constructor can be private | 126 // This exists so that Win32StackFrameUnwinder's constructor can be private |
| 188 // with a single friend declaration of this test fixture. | 127 // with a single friend declaration of this test fixture. |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 233 unwind_functions_->SetUnloadedModule(); | 172 unwind_functions_->SetUnloadedModule(); |
| 234 EXPECT_FALSE(unwinder->TryUnwind(&context, &module)); | 173 EXPECT_FALSE(unwinder->TryUnwind(&context, &module)); |
| 235 } | 174 } |
| 236 | 175 |
| 237 // Checks that the CONTEXT's stack pointer gets popped when the top frame has no | 176 // Checks that the CONTEXT's stack pointer gets popped when the top frame has no |
| 238 // unwind information. | 177 // unwind information. |
| 239 TEST_F(Win32StackFrameUnwinderTest, FrameAtTopWithoutUnwindInfo) { | 178 TEST_F(Win32StackFrameUnwinderTest, FrameAtTopWithoutUnwindInfo) { |
| 240 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); | 179 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); |
| 241 CONTEXT context = {0}; | 180 CONTEXT context = {0}; |
| 242 ScopedModuleHandle module; | 181 ScopedModuleHandle module; |
| 243 const DWORD64 original_rsp = 128; | 182 DWORD64 next_ip = 0x0123456789abcdef; |
| 183 DWORD64 original_rsp = reinterpret_cast<DWORD64>(&next_ip); |
| 244 context.Rsp = original_rsp; | 184 context.Rsp = original_rsp; |
| 245 | 185 |
| 246 unwind_functions_->SetNoRuntimeFunction(&context); | 186 unwind_functions_->SetNoRuntimeFunction(&context); |
| 247 EXPECT_TRUE(unwinder->TryUnwind(&context, &module)); | 187 EXPECT_TRUE(unwinder->TryUnwind(&context, &module)); |
| 248 EXPECT_EQ(original_rsp, context.Rip); | 188 EXPECT_EQ(next_ip, context.Rip); |
| 249 EXPECT_EQ(original_rsp + 8, context.Rsp); | 189 EXPECT_EQ(original_rsp + 8, context.Rsp); |
| 250 EXPECT_TRUE(module.IsValid()); | 190 EXPECT_TRUE(module.IsValid()); |
| 251 | 191 |
| 252 unwind_functions_->SetHasRuntimeFunction(&context); | 192 unwind_functions_->SetHasRuntimeFunction(&context); |
| 253 module.Set(nullptr); | 193 module.Set(nullptr); |
| 254 EXPECT_TRUE(unwinder->TryUnwind(&context, &module)); | 194 EXPECT_TRUE(unwinder->TryUnwind(&context, &module)); |
| 255 EXPECT_TRUE(module.IsValid()); | 195 EXPECT_TRUE(module.IsValid()); |
| 256 | 196 |
| 257 unwind_functions_->SetHasRuntimeFunction(&context); | 197 unwind_functions_->SetHasRuntimeFunction(&context); |
| 258 module.Set(nullptr); | 198 module.Set(nullptr); |
| 259 EXPECT_TRUE(unwinder->TryUnwind(&context, &module)); | 199 EXPECT_TRUE(unwinder->TryUnwind(&context, &module)); |
| 260 EXPECT_TRUE(module.IsValid()); | 200 EXPECT_TRUE(module.IsValid()); |
| 261 } | 201 } |
| 262 | 202 |
| 263 // Checks that a frame below the top of the stack with missing unwind info | 203 // Checks that a frame below the top of the stack with missing unwind info |
| 264 // results in blacklisting the module. | 204 // terminates the unwinding. |
| 265 TEST_F(Win32StackFrameUnwinderTest, BlacklistedModule) { | 205 TEST_F(Win32StackFrameUnwinderTest, FrameBelowTopWithoutUnwindInfo) { |
| 266 const DWORD64 image_base_for_module_with_bad_function = 1024; | |
| 267 { | 206 { |
| 268 // First stack, with a bad function below the top of the stack. | 207 // First stack, with a bad function below the top of the stack. |
| 269 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); | 208 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); |
| 270 CONTEXT context = {0}; | 209 CONTEXT context = {0}; |
| 271 ScopedModuleHandle module; | 210 ScopedModuleHandle module; |
| 272 unwind_functions_->SetHasRuntimeFunction(&context); | 211 unwind_functions_->SetHasRuntimeFunction(&context); |
| 273 EXPECT_TRUE(unwinder->TryUnwind(&context, &module)); | 212 EXPECT_TRUE(unwinder->TryUnwind(&context, &module)); |
| 274 EXPECT_TRUE(module.IsValid()); | 213 EXPECT_TRUE(module.IsValid()); |
| 275 | 214 |
| 276 unwind_functions_->SetNoRuntimeFunction( | 215 unwind_functions_->SetNoRuntimeFunction(&context); |
| 277 image_base_for_module_with_bad_function, | |
| 278 &context); | |
| 279 EXPECT_FALSE(unwinder->TryUnwind(&context, &module)); | 216 EXPECT_FALSE(unwinder->TryUnwind(&context, &module)); |
| 280 } | 217 } |
| 281 | |
| 282 { | |
| 283 // Second stack; check that a function at the top of the stack without | |
| 284 // unwind info from the previously-seen module is blacklisted. | |
| 285 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); | |
| 286 CONTEXT context = {0}; | |
| 287 ScopedModuleHandle module; | |
| 288 unwind_functions_->SetNoRuntimeFunction( | |
| 289 image_base_for_module_with_bad_function, | |
| 290 &context); | |
| 291 EXPECT_FALSE(unwinder->TryUnwind(&context, &module)); | |
| 292 } | |
| 293 | |
| 294 { | |
| 295 // Third stack; check that a function at the top of the stack *with* unwind | |
| 296 // info from the previously-seen module is not blacklisted. Then check that | |
| 297 // functions below the top of the stack with unwind info are not | |
| 298 // blacklisted, regardless of whether they are in the previously-seen | |
| 299 // module. | |
| 300 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); | |
| 301 CONTEXT context = {0}; | |
| 302 ScopedModuleHandle module; | |
| 303 unwind_functions_->SetHasRuntimeFunction( | |
| 304 image_base_for_module_with_bad_function, | |
| 305 &context); | |
| 306 EXPECT_TRUE(unwinder->TryUnwind(&context, &module)); | |
| 307 EXPECT_TRUE(module.IsValid()); | |
| 308 | |
| 309 unwind_functions_->SetHasRuntimeFunction(&context); | |
| 310 module.Set(nullptr); | |
| 311 EXPECT_TRUE(unwinder->TryUnwind(&context, &module)); | |
| 312 EXPECT_TRUE(module.IsValid()); | |
| 313 | |
| 314 unwind_functions_->SetHasRuntimeFunction( | |
| 315 image_base_for_module_with_bad_function, | |
| 316 &context); | |
| 317 module.Set(nullptr); | |
| 318 EXPECT_TRUE(unwinder->TryUnwind(&context, &module)); | |
| 319 EXPECT_TRUE(module.IsValid()); | |
| 320 } | |
| 321 | |
| 322 { | |
| 323 // Fourth stack; check that a function at the top of the stack without | |
| 324 // unwind info and not from the previously-seen module is not | |
| 325 // blacklisted. Then check that functions below the top of the stack with | |
| 326 // unwind info are not blacklisted, regardless of whether they are in the | |
| 327 // previously-seen module. | |
| 328 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); | |
| 329 CONTEXT context = {0}; | |
| 330 ScopedModuleHandle module; | |
| 331 unwind_functions_->SetNoRuntimeFunction(&context); | |
| 332 EXPECT_TRUE(unwinder->TryUnwind(&context, &module)); | |
| 333 EXPECT_TRUE(module.IsValid()); | |
| 334 | |
| 335 unwind_functions_->SetHasRuntimeFunction(&context); | |
| 336 module.Set(nullptr); | |
| 337 EXPECT_TRUE(unwinder->TryUnwind(&context, &module)); | |
| 338 EXPECT_TRUE(module.IsValid()); | |
| 339 | |
| 340 unwind_functions_->SetHasRuntimeFunction( | |
| 341 image_base_for_module_with_bad_function, | |
| 342 &context); | |
| 343 module.Set(nullptr); | |
| 344 EXPECT_TRUE(unwinder->TryUnwind(&context, &module)); | |
| 345 EXPECT_TRUE(module.IsValid()); | |
| 346 } | |
| 347 } | |
| 348 | |
| 349 // Checks that a frame below the top of the stack with missing unwind info does | |
| 350 // not result in blacklisting the module if the first frame also was missing | |
| 351 // unwind info. This ensures we don't blacklist an innocent module because the | |
| 352 // first frame was bad but we didn't know it at the time. | |
| 353 TEST_F(Win32StackFrameUnwinderTest, ModuleFromQuestionableFrameNotBlacklisted) { | |
| 354 const DWORD64 image_base_for_questionable_module = 2048; | |
| 355 { | |
| 356 // First stack, with both the first and second frames missing unwind info. | |
| 357 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); | |
| 358 CONTEXT context = {0}; | |
| 359 ScopedModuleHandle module; | |
| 360 unwind_functions_->SetNoRuntimeFunction(&context); | |
| 361 EXPECT_TRUE(unwinder->TryUnwind(&context, &module)); | |
| 362 EXPECT_TRUE(module.IsValid()); | |
| 363 | |
| 364 unwind_functions_->SetNoRuntimeFunction(image_base_for_questionable_module, | |
| 365 &context); | |
| 366 EXPECT_FALSE(unwinder->TryUnwind(&context, &module)); | |
| 367 } | |
| 368 | |
| 369 { | |
| 370 // Second stack; check that the questionable module was not blacklisted. | |
| 371 scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder(); | |
| 372 CONTEXT context = {0}; | |
| 373 ScopedModuleHandle module; | |
| 374 unwind_functions_->SetNoRuntimeFunction(image_base_for_questionable_module, | |
| 375 &context); | |
| 376 EXPECT_TRUE(unwinder->TryUnwind(&context, &module)); | |
| 377 EXPECT_TRUE(module.IsValid()); | |
| 378 } | |
| 379 } | 218 } |
| 380 | 219 |
| 381 } // namespace base | 220 } // namespace base |
| OLD | NEW |