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.
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"
14 class TestUnwindFunctions
: public Win32StackFrameUnwinder::UnwindFunctions
{
16 TestUnwindFunctions();
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
;
25 void SetNoUnwindInfoForNextFrame();
26 void SetImageBaseForNextFrame(DWORD64 image_base
);
29 enum { kRuntimeFunctionPointerIncrement
= 1, kImageBaseIncrement
= 1 << 20 };
31 static const PRUNTIME_FUNCTION kNonNullRuntimeFunctionPointer
;
33 DWORD64 supplied_program_counter_
;
34 DWORD64 custom_image_base_
;
35 DWORD64 next_image_base_
;
36 bool next_lookup_returns_null_
;
38 DISALLOW_COPY_AND_ASSIGN(TestUnwindFunctions
);
41 // This value is opaque to Win32StackFrameUnwinder.
42 const PRUNTIME_FUNCTION
TestUnwindFunctions::kNonNullRuntimeFunctionPointer
=
43 reinterpret_cast<PRUNTIME_FUNCTION
>(8);
45 TestUnwindFunctions::TestUnwindFunctions()
46 : supplied_program_counter_(0),
47 custom_image_base_(0),
48 next_image_base_(kImageBaseIncrement
),
49 next_lookup_returns_null_(false) {
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;
60 *image_base
= next_image_base_
;
61 next_image_base_
+= kImageBaseIncrement
;
63 if (next_lookup_returns_null_
) {
64 next_lookup_returns_null_
= false;
68 return kNonNullRuntimeFunctionPointer
;
71 void TestUnwindFunctions::VirtualUnwind(DWORD64 image_base
,
72 DWORD64 program_counter
,
73 PRUNTIME_FUNCTION runtime_function
,
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
79 EXPECT_EQ(kNonNullRuntimeFunctionPointer
, runtime_function
);
82 void TestUnwindFunctions::SetNoUnwindInfoForNextFrame() {
83 next_lookup_returns_null_
= true;
86 void TestUnwindFunctions::SetImageBaseForNextFrame(DWORD64 image_base
) {
87 next_image_base_
= image_base
;
92 class Win32StackFrameUnwinderTest
: public testing::Test
{
94 Win32StackFrameUnwinderTest() {}
96 // This exists so that Win32StackFrameUnwinder's constructor can be private
97 // with a single friend declaration of this test fixture.
98 scoped_ptr
<Win32StackFrameUnwinder
> CreateUnwinder();
100 TestUnwindFunctions unwind_functions_
;
103 DISALLOW_COPY_AND_ASSIGN(Win32StackFrameUnwinderTest
);
106 scoped_ptr
<Win32StackFrameUnwinder
>
107 Win32StackFrameUnwinderTest::CreateUnwinder() {
108 return make_scoped_ptr(new Win32StackFrameUnwinder(&unwind_functions_
));
111 // Checks the case where all frames have unwind information.
112 TEST_F(Win32StackFrameUnwinderTest
, FramesWithUnwindInfo
) {
113 scoped_ptr
<Win32StackFrameUnwinder
> unwinder
= CreateUnwinder();
114 CONTEXT context
= {0};
115 EXPECT_TRUE(unwinder
->TryUnwind(&context
));
116 EXPECT_TRUE(unwinder
->TryUnwind(&context
));
117 EXPECT_TRUE(unwinder
->TryUnwind(&context
));
120 // Checks that the CONTEXT's stack pointer gets popped when the top frame has no
121 // unwind information.
122 TEST_F(Win32StackFrameUnwinderTest
, FrameAtTopWithoutUnwindInfo
) {
123 scoped_ptr
<Win32StackFrameUnwinder
> unwinder
= CreateUnwinder();
124 CONTEXT context
= {0};
125 const DWORD64 original_rsp
= 128;
126 context
.Rsp
= original_rsp
;
127 unwind_functions_
.SetNoUnwindInfoForNextFrame();
128 EXPECT_TRUE(unwinder
->TryUnwind(&context
));
129 EXPECT_EQ(original_rsp
, context
.Rip
);
130 EXPECT_EQ(original_rsp
+ 8, context
.Rsp
);
132 EXPECT_TRUE(unwinder
->TryUnwind(&context
));
133 EXPECT_TRUE(unwinder
->TryUnwind(&context
));
136 // Checks that a frame below the top of the stack with missing unwind info
137 // results in blacklisting the module.
138 TEST_F(Win32StackFrameUnwinderTest
, BlacklistedModule
) {
139 const DWORD64 image_base_for_module_with_bad_function
= 1024;
141 // First stack, with a bad function below the top of the stack.
142 scoped_ptr
<Win32StackFrameUnwinder
> unwinder
= CreateUnwinder();
143 CONTEXT context
= {0};
144 EXPECT_TRUE(unwinder
->TryUnwind(&context
));
146 unwind_functions_
.SetNoUnwindInfoForNextFrame();
147 unwind_functions_
.SetImageBaseForNextFrame(
148 image_base_for_module_with_bad_function
);
149 EXPECT_FALSE(unwinder
->TryUnwind(&context
));
153 // Second stack; check that a function at the top of the stack without
154 // unwind info from the previously-seen module is blacklisted.
155 scoped_ptr
<Win32StackFrameUnwinder
> unwinder
= CreateUnwinder();
156 CONTEXT context
= {0};
157 unwind_functions_
.SetNoUnwindInfoForNextFrame();
158 unwind_functions_
.SetImageBaseForNextFrame(
159 image_base_for_module_with_bad_function
);
160 EXPECT_FALSE(unwinder
->TryUnwind(&context
));
164 // Third stack; check that a function at the top of the stack *with* unwind
165 // info from the previously-seen module is not blacklisted. Then check that
166 // functions below the top of the stack with unwind info are not
167 // blacklisted, regardless of whether they are in the previously-seen
169 scoped_ptr
<Win32StackFrameUnwinder
> unwinder
= CreateUnwinder();
170 CONTEXT context
= {0};
171 unwind_functions_
.SetImageBaseForNextFrame(
172 image_base_for_module_with_bad_function
);
173 EXPECT_TRUE(unwinder
->TryUnwind(&context
));
175 EXPECT_TRUE(unwinder
->TryUnwind(&context
));
177 unwind_functions_
.SetImageBaseForNextFrame(
178 image_base_for_module_with_bad_function
);
179 EXPECT_TRUE(unwinder
->TryUnwind(&context
));
183 // Fourth stack; check that a function at the top of the stack without
184 // unwind info and not from the previously-seen module is not
185 // blacklisted. Then check that functions below the top of the stack with
186 // unwind info are not blacklisted, regardless of whether they are in the
187 // previously-seen module.
188 scoped_ptr
<Win32StackFrameUnwinder
> unwinder
= CreateUnwinder();
189 CONTEXT context
= {0};
190 unwind_functions_
.SetNoUnwindInfoForNextFrame();
191 EXPECT_TRUE(unwinder
->TryUnwind(&context
));
193 EXPECT_TRUE(unwinder
->TryUnwind(&context
));
195 unwind_functions_
.SetImageBaseForNextFrame(
196 image_base_for_module_with_bad_function
);
197 EXPECT_TRUE(unwinder
->TryUnwind(&context
));
201 // Checks that a frame below the top of the stack with missing unwind info does
202 // not result in blacklisting the module if the first frame also was missing
203 // unwind info. This ensures we don't blacklist an innocent module because the
204 // first frame was bad but we didn't know it at the time.
205 TEST_F(Win32StackFrameUnwinderTest
, ModuleFromQuestionableFrameNotBlacklisted
) {
206 const DWORD64 image_base_for_questionable_module
= 2048;
208 // First stack, with both the first and second frames missing unwind info.
209 scoped_ptr
<Win32StackFrameUnwinder
> unwinder
= CreateUnwinder();
210 CONTEXT context
= {0};
211 unwind_functions_
.SetNoUnwindInfoForNextFrame();
212 EXPECT_TRUE(unwinder
->TryUnwind(&context
));
214 unwind_functions_
.SetNoUnwindInfoForNextFrame();
215 unwind_functions_
.SetImageBaseForNextFrame(
216 image_base_for_questionable_module
);
217 EXPECT_FALSE(unwinder
->TryUnwind(&context
));
221 // Second stack; check that the questionable module was not blacklisted.
222 scoped_ptr
<Win32StackFrameUnwinder
> unwinder
= CreateUnwinder();
223 CONTEXT context
= {0};
224 unwind_functions_
.SetNoUnwindInfoForNextFrame();
225 unwind_functions_
.SetImageBaseForNextFrame(
226 image_base_for_questionable_module
);
227 EXPECT_TRUE(unwinder
->TryUnwind(&context
));