| Index: profiler/win32_stack_frame_unwinder_unittest.cc
 | 
| diff --git a/profiler/win32_stack_frame_unwinder_unittest.cc b/profiler/win32_stack_frame_unwinder_unittest.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..0ea55c3cce9a8d29b83ae9f761db44df01b951ba
 | 
| --- /dev/null
 | 
| +++ b/profiler/win32_stack_frame_unwinder_unittest.cc
 | 
| @@ -0,0 +1,230 @@
 | 
| +// Copyright 2015 The Chromium Authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +#include "base/compiler_specific.h"
 | 
| +#include "base/memory/scoped_ptr.h"
 | 
| +#include "base/profiler/win32_stack_frame_unwinder.h"
 | 
| +#include "testing/gtest/include/gtest/gtest.h"
 | 
| +
 | 
| +namespace base {
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +class TestUnwindFunctions : public Win32StackFrameUnwinder::UnwindFunctions {
 | 
| + public:
 | 
| +  TestUnwindFunctions();
 | 
| +
 | 
| +  PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter,
 | 
| +                                        PDWORD64 image_base) override;
 | 
| +  void VirtualUnwind(DWORD64 image_base,
 | 
| +                     DWORD64 program_counter,
 | 
| +                     PRUNTIME_FUNCTION runtime_function,
 | 
| +                     CONTEXT* context) override;
 | 
| +
 | 
| +  void SetNoUnwindInfoForNextFrame();
 | 
| +  void SetImageBaseForNextFrame(DWORD64 image_base);
 | 
| +
 | 
| + private:
 | 
| +  enum { kRuntimeFunctionPointerIncrement = 1, kImageBaseIncrement = 1 << 20 };
 | 
| +
 | 
| +  static const PRUNTIME_FUNCTION kNonNullRuntimeFunctionPointer;
 | 
| +
 | 
| +  DWORD64 supplied_program_counter_;
 | 
| +  DWORD64 custom_image_base_;
 | 
| +  DWORD64 next_image_base_;
 | 
| +  bool next_lookup_returns_null_;
 | 
| +
 | 
| +  DISALLOW_COPY_AND_ASSIGN(TestUnwindFunctions);
 | 
| +};
 | 
| +
 | 
| +// This value is opaque to Win32StackFrameUnwinder.
 | 
| +const PRUNTIME_FUNCTION TestUnwindFunctions::kNonNullRuntimeFunctionPointer =
 | 
| +    reinterpret_cast<PRUNTIME_FUNCTION>(8);
 | 
| +
 | 
| +TestUnwindFunctions::TestUnwindFunctions()
 | 
| +    : supplied_program_counter_(0),
 | 
| +      custom_image_base_(0),
 | 
| +      next_image_base_(kImageBaseIncrement),
 | 
| +      next_lookup_returns_null_(false) {}
 | 
| +
 | 
| +PRUNTIME_FUNCTION TestUnwindFunctions::LookupFunctionEntry(
 | 
| +    DWORD64 program_counter,
 | 
| +    PDWORD64 image_base) {
 | 
| +  supplied_program_counter_ = program_counter;
 | 
| +  if (custom_image_base_) {
 | 
| +    *image_base = custom_image_base_;
 | 
| +    custom_image_base_ = 0;
 | 
| +  } else {
 | 
| +    *image_base = next_image_base_;
 | 
| +    next_image_base_ += kImageBaseIncrement;
 | 
| +  }
 | 
| +  if (next_lookup_returns_null_) {
 | 
| +    next_lookup_returns_null_ = false;
 | 
| +    return nullptr;
 | 
| +  }
 | 
| +
 | 
| +  return kNonNullRuntimeFunctionPointer;
 | 
| +}
 | 
| +
 | 
| +void TestUnwindFunctions::VirtualUnwind(DWORD64 image_base,
 | 
| +                                        DWORD64 program_counter,
 | 
| +                                        PRUNTIME_FUNCTION runtime_function,
 | 
| +                                        CONTEXT* context) {
 | 
| +  EXPECT_EQ(next_image_base_ - kImageBaseIncrement, image_base);
 | 
| +  EXPECT_EQ(supplied_program_counter_, program_counter);
 | 
| +  // This function should only be called when LookupFunctionEntry returns a
 | 
| +  // non-null value.
 | 
| +  EXPECT_EQ(kNonNullRuntimeFunctionPointer, runtime_function);
 | 
| +}
 | 
| +
 | 
| +void TestUnwindFunctions::SetNoUnwindInfoForNextFrame() {
 | 
| +  next_lookup_returns_null_ = true;
 | 
| +}
 | 
| +
 | 
| +void TestUnwindFunctions::SetImageBaseForNextFrame(DWORD64 image_base) {
 | 
| +  next_image_base_ = image_base;
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +class Win32StackFrameUnwinderTest : public testing::Test {
 | 
| + protected:
 | 
| +  Win32StackFrameUnwinderTest() {}
 | 
| +
 | 
| +  // This exists so that Win32StackFrameUnwinder's constructor can be private
 | 
| +  // with a single friend declaration of this test fixture.
 | 
| +  scoped_ptr<Win32StackFrameUnwinder> CreateUnwinder();
 | 
| +
 | 
| +  TestUnwindFunctions unwind_functions_;
 | 
| +
 | 
| + private:
 | 
| +  DISALLOW_COPY_AND_ASSIGN(Win32StackFrameUnwinderTest);
 | 
| +};
 | 
| +
 | 
| +scoped_ptr<Win32StackFrameUnwinder>
 | 
| +Win32StackFrameUnwinderTest::CreateUnwinder() {
 | 
| +  return make_scoped_ptr(new Win32StackFrameUnwinder(&unwind_functions_));
 | 
| +}
 | 
| +
 | 
| +// Checks the case where all frames have unwind information.
 | 
| +TEST_F(Win32StackFrameUnwinderTest, FramesWithUnwindInfo) {
 | 
| +  scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
 | 
| +  CONTEXT context = {0};
 | 
| +  EXPECT_TRUE(unwinder->TryUnwind(&context));
 | 
| +  EXPECT_TRUE(unwinder->TryUnwind(&context));
 | 
| +  EXPECT_TRUE(unwinder->TryUnwind(&context));
 | 
| +}
 | 
| +
 | 
| +// Checks that the CONTEXT's stack pointer gets popped when the top frame has no
 | 
| +// unwind information.
 | 
| +TEST_F(Win32StackFrameUnwinderTest, FrameAtTopWithoutUnwindInfo) {
 | 
| +  scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
 | 
| +  CONTEXT context = {0};
 | 
| +  const DWORD64 original_rsp = 128;
 | 
| +  context.Rsp = original_rsp;
 | 
| +  unwind_functions_.SetNoUnwindInfoForNextFrame();
 | 
| +  EXPECT_TRUE(unwinder->TryUnwind(&context));
 | 
| +  EXPECT_EQ(original_rsp, context.Rip);
 | 
| +  EXPECT_EQ(original_rsp + 8, context.Rsp);
 | 
| +
 | 
| +  EXPECT_TRUE(unwinder->TryUnwind(&context));
 | 
| +  EXPECT_TRUE(unwinder->TryUnwind(&context));
 | 
| +}
 | 
| +
 | 
| +// Checks that a frame below the top of the stack with missing unwind info
 | 
| +// results in blacklisting the module.
 | 
| +TEST_F(Win32StackFrameUnwinderTest, BlacklistedModule) {
 | 
| +  const DWORD64 image_base_for_module_with_bad_function = 1024;
 | 
| +  {
 | 
| +    // First stack, with a bad function below the top of the stack.
 | 
| +    scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
 | 
| +    CONTEXT context = {0};
 | 
| +    EXPECT_TRUE(unwinder->TryUnwind(&context));
 | 
| +
 | 
| +    unwind_functions_.SetNoUnwindInfoForNextFrame();
 | 
| +    unwind_functions_.SetImageBaseForNextFrame(
 | 
| +        image_base_for_module_with_bad_function);
 | 
| +    EXPECT_FALSE(unwinder->TryUnwind(&context));
 | 
| +  }
 | 
| +
 | 
| +  {
 | 
| +    // Second stack; check that a function at the top of the stack without
 | 
| +    // unwind info from the previously-seen module is blacklisted.
 | 
| +    scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
 | 
| +    CONTEXT context = {0};
 | 
| +    unwind_functions_.SetNoUnwindInfoForNextFrame();
 | 
| +    unwind_functions_.SetImageBaseForNextFrame(
 | 
| +        image_base_for_module_with_bad_function);
 | 
| +    EXPECT_FALSE(unwinder->TryUnwind(&context));
 | 
| +  }
 | 
| +
 | 
| +  {
 | 
| +    // Third stack; check that a function at the top of the stack *with* unwind
 | 
| +    // info from the previously-seen module is not blacklisted. Then check that
 | 
| +    // functions below the top of the stack with unwind info are not
 | 
| +    // blacklisted, regardless of whether they are in the previously-seen
 | 
| +    // module.
 | 
| +    scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
 | 
| +    CONTEXT context = {0};
 | 
| +    unwind_functions_.SetImageBaseForNextFrame(
 | 
| +        image_base_for_module_with_bad_function);
 | 
| +    EXPECT_TRUE(unwinder->TryUnwind(&context));
 | 
| +
 | 
| +    EXPECT_TRUE(unwinder->TryUnwind(&context));
 | 
| +
 | 
| +    unwind_functions_.SetImageBaseForNextFrame(
 | 
| +        image_base_for_module_with_bad_function);
 | 
| +    EXPECT_TRUE(unwinder->TryUnwind(&context));
 | 
| +  }
 | 
| +
 | 
| +  {
 | 
| +    // Fourth stack; check that a function at the top of the stack without
 | 
| +    // unwind info and not from the previously-seen module is not
 | 
| +    // blacklisted. Then check that functions below the top of the stack with
 | 
| +    // unwind info are not blacklisted, regardless of whether they are in the
 | 
| +    // previously-seen module.
 | 
| +    scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
 | 
| +    CONTEXT context = {0};
 | 
| +    unwind_functions_.SetNoUnwindInfoForNextFrame();
 | 
| +    EXPECT_TRUE(unwinder->TryUnwind(&context));
 | 
| +
 | 
| +    EXPECT_TRUE(unwinder->TryUnwind(&context));
 | 
| +
 | 
| +    unwind_functions_.SetImageBaseForNextFrame(
 | 
| +        image_base_for_module_with_bad_function);
 | 
| +    EXPECT_TRUE(unwinder->TryUnwind(&context));
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +// Checks that a frame below the top of the stack with missing unwind info does
 | 
| +// not result in blacklisting the module if the first frame also was missing
 | 
| +// unwind info. This ensures we don't blacklist an innocent module because the
 | 
| +// first frame was bad but we didn't know it at the time.
 | 
| +TEST_F(Win32StackFrameUnwinderTest, ModuleFromQuestionableFrameNotBlacklisted) {
 | 
| +  const DWORD64 image_base_for_questionable_module = 2048;
 | 
| +  {
 | 
| +    // First stack, with both the first and second frames missing unwind info.
 | 
| +    scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
 | 
| +    CONTEXT context = {0};
 | 
| +    unwind_functions_.SetNoUnwindInfoForNextFrame();
 | 
| +    EXPECT_TRUE(unwinder->TryUnwind(&context));
 | 
| +
 | 
| +    unwind_functions_.SetNoUnwindInfoForNextFrame();
 | 
| +    unwind_functions_.SetImageBaseForNextFrame(
 | 
| +        image_base_for_questionable_module);
 | 
| +    EXPECT_FALSE(unwinder->TryUnwind(&context));
 | 
| +  }
 | 
| +
 | 
| +  {
 | 
| +    // Second stack; check that the questionable module was not blacklisted.
 | 
| +    scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
 | 
| +    CONTEXT context = {0};
 | 
| +    unwind_functions_.SetNoUnwindInfoForNextFrame();
 | 
| +    unwind_functions_.SetImageBaseForNextFrame(
 | 
| +        image_base_for_questionable_module);
 | 
| +    EXPECT_TRUE(unwinder->TryUnwind(&context));
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +}  // namespace base
 | 
| 
 |