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 |