Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(47)

Side by Side Diff: base/profiler/win32_stack_frame_unwinder_unittest.cc

Issue 1503273003: Stack sampling profiler: fix leaf function unwinding and remove blacklisting (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@lkcr
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/profiler/win32_stack_frame_unwinder.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
OLDNEW
« no previous file with comments | « base/profiler/win32_stack_frame_unwinder.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698