1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "base/profiler/stack_sampler.h"
11
12 #include <algorithm>
13 #include <cstring>
14 #include <iterator>
15 #include <memory>
16 #include <numeric>
17 #include <utility>
18
19 #include "base/functional/bind.h"
20 #include "base/memory/ptr_util.h"
21 #include "base/memory/raw_ptr.h"
22 #include "base/memory/raw_ref.h"
23 #include "base/profiler/module_cache.h"
24 #include "base/profiler/profile_builder.h"
25 #include "base/profiler/stack_buffer.h"
26 #include "base/profiler/stack_copier.h"
27 #include "base/profiler/stack_sampling_profiler_test_util.h"
28 #include "base/profiler/suspendable_thread_delegate.h"
29 #include "base/profiler/unwinder.h"
30 #include "base/test/metrics/histogram_tester.h"
31 #include "base/test/task_environment.h"
32 #include "base/test/test_future.h"
33 #include "build/build_config.h"
34 #include "testing/gmock/include/gmock/gmock.h"
35 #include "testing/gtest/include/gtest/gtest.h"
36
37 namespace base {
38
39 namespace {
40
41 using ::testing::ElementsAre;
42
43 class StackSamplerTest : public ::testing::Test {
44 protected:
45 ModuleCache module_cache_;
46
47 private:
48 base::test::TaskEnvironment task_environment_;
49 };
50
51 class TestProfileBuilder : public ProfileBuilder {
52 public:
TestProfileBuilder(ModuleCache * module_cache)53 TestProfileBuilder(ModuleCache* module_cache) : module_cache_(module_cache) {}
54
55 TestProfileBuilder(const TestProfileBuilder&) = delete;
56 TestProfileBuilder& operator=(const TestProfileBuilder&) = delete;
57
58 // ProfileBuilder
GetModuleCache()59 ModuleCache* GetModuleCache() override { return module_cache_; }
RecordMetadata(const MetadataRecorder::MetadataProvider & metadata_provider)60 void RecordMetadata(
61 const MetadataRecorder::MetadataProvider& metadata_provider) override {}
62
OnSampleCompleted(std::vector<Frame> frames,TimeTicks sample_timestamp)63 void OnSampleCompleted(std::vector<Frame> frames,
64 TimeTicks sample_timestamp) override {
65 last_timestamp_ = sample_timestamp;
66 }
67
OnProfileCompleted(TimeDelta profile_duration,TimeDelta sampling_period)68 void OnProfileCompleted(TimeDelta profile_duration,
69 TimeDelta sampling_period) override {}
70
last_timestamp()71 TimeTicks last_timestamp() { return last_timestamp_; }
72
73 private:
74 raw_ptr<ModuleCache> module_cache_;
75 TimeTicks last_timestamp_;
76 };
77
78 // A stack copier for use in tests that provides the expected behavior when
79 // operating on the supplied fake stack.
80 class TestStackCopier : public StackCopier {
81 public:
TestStackCopier(const std::vector<uintptr_t> & fake_stack,TimeTicks timestamp=TimeTicks ())82 TestStackCopier(const std::vector<uintptr_t>& fake_stack,
83 TimeTicks timestamp = TimeTicks())
84 : fake_stack_(fake_stack), timestamp_(timestamp) {}
85
CopyStack(StackBuffer * stack_buffer,uintptr_t * stack_top,TimeTicks * timestamp,RegisterContext * thread_context,Delegate * delegate)86 bool CopyStack(StackBuffer* stack_buffer,
87 uintptr_t* stack_top,
88 TimeTicks* timestamp,
89 RegisterContext* thread_context,
90 Delegate* delegate) override {
91 std::memcpy(stack_buffer->buffer(), &(*fake_stack_)[0],
92 fake_stack_->size() * sizeof((*fake_stack_)[0]));
93 *stack_top = reinterpret_cast<uintptr_t>(stack_buffer->buffer() +
94 fake_stack_->size());
95 // Set the stack pointer to be consistent with the copied stack.
96 *thread_context = {};
97 RegisterContextStackPointer(thread_context) =
98 reinterpret_cast<uintptr_t>(stack_buffer->buffer());
99
100 *timestamp = timestamp_;
101
102 return true;
103 }
104
105 protected:
GetRegistersToRewrite(RegisterContext * thread_context)106 std::vector<uintptr_t*> GetRegistersToRewrite(
107 RegisterContext* thread_context) override {
108 return {&RegisterContextStackPointer(thread_context)};
109 }
110
111 private:
112 // Must be a reference to retain the underlying allocation from the vector
113 // passed to the constructor.
114 const raw_ref<const std::vector<uintptr_t>> fake_stack_;
115
116 const TimeTicks timestamp_;
117 };
118
119 // A StackCopier that just invokes the expected functions on the delegate.
120 class DelegateInvokingStackCopier : public StackCopier {
121 public:
CopyStack(StackBuffer * stack_buffer,uintptr_t * stack_top,TimeTicks * timestamp,RegisterContext * thread_context,Delegate * delegate)122 bool CopyStack(StackBuffer* stack_buffer,
123 uintptr_t* stack_top,
124 TimeTicks* timestamp,
125 RegisterContext* thread_context,
126 Delegate* delegate) override {
127 // Returning true means the various out params should be populated.
128 *stack_top = reinterpret_cast<uintptr_t>(stack_buffer->buffer() +
129 stack_buffer->size());
130 // Set the stack pointer to be consistent with the copied stack.
131 *thread_context = {};
132 RegisterContextStackPointer(thread_context) =
133 reinterpret_cast<uintptr_t>(stack_buffer->buffer());
134 *timestamp = TimeTicks::Now();
135 delegate->OnStackCopy();
136 return true;
137 }
138
139 protected:
GetRegistersToRewrite(RegisterContext * thread_context)140 std::vector<uintptr_t*> GetRegistersToRewrite(
141 RegisterContext* thread_context) override {
142 return {&RegisterContextStackPointer(thread_context)};
143 }
144 };
145
146 // Trivial unwinder implementation for testing.
147 class TestUnwinder : public Unwinder {
148 public:
TestUnwinder(std::vector<uintptr_t> * stack_copy)149 explicit TestUnwinder(std::vector<uintptr_t>* stack_copy)
150 : stack_copy_(stack_copy) {}
151
CanUnwindFrom(const Frame & current_frame) const152 bool CanUnwindFrom(const Frame& current_frame) const override { return true; }
153
TryUnwind(UnwinderStateCapture * capture_state,RegisterContext * thread_context,uintptr_t stack_top,std::vector<Frame> * stack)154 UnwindResult TryUnwind(UnwinderStateCapture* capture_state,
155 RegisterContext* thread_context,
156 uintptr_t stack_top,
157 std::vector<Frame>* stack) override {
158 auto* bottom = reinterpret_cast<uintptr_t*>(
159 RegisterContextStackPointer(thread_context));
160 *stack_copy_ =
161 std::vector<uintptr_t>(bottom, reinterpret_cast<uintptr_t*>(stack_top));
162 return UnwindResult::kCompleted;
163 }
164
165 private:
166 raw_ptr<std::vector<uintptr_t>> stack_copy_;
167 };
168
169 // Records invocations of calls to OnStackCapture()/UpdateModules().
170 class CallRecordingUnwinder : public Unwinder {
171 public:
OnStackCapture(UnwinderStateCapture * capture_state)172 void OnStackCapture(UnwinderStateCapture* capture_state) override {
173 on_stack_capture_was_invoked_ = true;
174 }
175
UpdateModules(UnwinderStateCapture * capture_state)176 void UpdateModules(UnwinderStateCapture* capture_state) override {
177 update_modules_was_invoked_ = true;
178 }
179
CanUnwindFrom(const Frame & current_frame) const180 bool CanUnwindFrom(const Frame& current_frame) const override { return true; }
181
TryUnwind(UnwinderStateCapture * capture_state,RegisterContext * thread_context,uintptr_t stack_top,std::vector<Frame> * stack)182 UnwindResult TryUnwind(UnwinderStateCapture* capture_state,
183 RegisterContext* thread_context,
184 uintptr_t stack_top,
185 std::vector<Frame>* stack) override {
186 return UnwindResult::kUnrecognizedFrame;
187 }
188
on_stack_capture_was_invoked() const189 bool on_stack_capture_was_invoked() const {
190 return on_stack_capture_was_invoked_;
191 }
192
update_modules_was_invoked() const193 bool update_modules_was_invoked() const {
194 return update_modules_was_invoked_;
195 }
196
197 private:
198 bool on_stack_capture_was_invoked_ = false;
199 bool update_modules_was_invoked_ = false;
200 };
201
202 // Utility function to form a vector from a single module.
ToModuleVector(std::unique_ptr<const ModuleCache::Module> module)203 std::vector<std::unique_ptr<const ModuleCache::Module>> ToModuleVector(
204 std::unique_ptr<const ModuleCache::Module> module) {
205 return std::vector<std::unique_ptr<const ModuleCache::Module>>(
206 std::make_move_iterator(&module), std::make_move_iterator(&module + 1));
207 }
208
209 // Injects a fake module covering the initial instruction pointer value, to
210 // avoid asking the OS to look it up. Windows doesn't return a consistent error
211 // code when doing so, and we DCHECK_EQ the expected error code.
InjectModuleForContextInstructionPointer(const std::vector<uintptr_t> & stack,ModuleCache * module_cache)212 void InjectModuleForContextInstructionPointer(
213 const std::vector<uintptr_t>& stack,
214 ModuleCache* module_cache) {
215 module_cache->AddCustomNativeModule(
216 std::make_unique<TestModule>(stack[0], sizeof(uintptr_t)));
217 }
218
219 // Returns a plausible instruction pointer value for use in tests that don't
220 // care about the instruction pointer value in the context, and hence don't need
221 // InjectModuleForContextInstructionPointer().
GetTestInstructionPointer()222 uintptr_t GetTestInstructionPointer() {
223 return reinterpret_cast<uintptr_t>(&GetTestInstructionPointer);
224 }
225
226 // An unwinder fake that replays the provided outputs.
227 class FakeTestUnwinder : public Unwinder {
228 public:
229 struct Result {
Resultbase::__anon54ff95bf0111::FakeTestUnwinder::Result230 Result(bool can_unwind)
231 : can_unwind(can_unwind), result(UnwindResult::kUnrecognizedFrame) {}
232
Resultbase::__anon54ff95bf0111::FakeTestUnwinder::Result233 Result(UnwindResult result, std::vector<uintptr_t> instruction_pointers)
234 : can_unwind(true),
235 result(result),
236 instruction_pointers(instruction_pointers) {}
237
238 bool can_unwind;
239 UnwindResult result;
240 std::vector<uintptr_t> instruction_pointers;
241 };
242
243 // Construct the unwinder with the outputs. The relevant unwinder functions
244 // are expected to be invoked at least as many times as the number of values
245 // specified in the arrays (except for CanUnwindFrom() which will always
246 // return true if provided an empty array.
FakeTestUnwinder(std::vector<Result> results)247 explicit FakeTestUnwinder(std::vector<Result> results)
248 : results_(std::move(results)) {}
249
250 FakeTestUnwinder(const FakeTestUnwinder&) = delete;
251 FakeTestUnwinder& operator=(const FakeTestUnwinder&) = delete;
252
CanUnwindFrom(const Frame & current_frame) const253 bool CanUnwindFrom(const Frame& current_frame) const override {
254 bool can_unwind = results_[current_unwind_].can_unwind;
255 // NB: If CanUnwindFrom() returns false then TryUnwind() will not be
256 // invoked, so current_unwind_ is guarantee to be incremented only once for
257 // each result.
258 if (!can_unwind)
259 ++current_unwind_;
260 return can_unwind;
261 }
262
TryUnwind(UnwinderStateCapture * capture_state,RegisterContext * thread_context,uintptr_t stack_top,std::vector<Frame> * stack)263 UnwindResult TryUnwind(UnwinderStateCapture* capture_state,
264 RegisterContext* thread_context,
265 uintptr_t stack_top,
266 std::vector<Frame>* stack) override {
267 CHECK_LT(current_unwind_, results_.size());
268 const Result& current_result = results_[current_unwind_];
269 ++current_unwind_;
270 CHECK(current_result.can_unwind);
271 for (const auto instruction_pointer : current_result.instruction_pointers)
272 stack->emplace_back(
273 instruction_pointer,
274 module_cache()->GetModuleForAddress(instruction_pointer));
275 return current_result.result;
276 }
277
278 private:
279 mutable size_t current_unwind_ = 0;
280 std::vector<Result> results_;
281 };
282
MakeUnwindersFactory(std::unique_ptr<Unwinder> unwinder)283 StackSampler::UnwindersFactory MakeUnwindersFactory(
284 std::unique_ptr<Unwinder> unwinder) {
285 return BindOnce(
286 [](std::unique_ptr<Unwinder> unwinder) {
287 std::vector<std::unique_ptr<Unwinder>> unwinders;
288 unwinders.push_back(std::move(unwinder));
289 return unwinders;
290 },
291 std::move(unwinder));
292 }
293
MakeUnwinderStateVector(Unwinder * native_unwinder,Unwinder * aux_unwinder)294 std::vector<UnwinderCapture> MakeUnwinderStateVector(Unwinder* native_unwinder,
295 Unwinder* aux_unwinder) {
296 std::vector<UnwinderCapture> unwinders;
297 if (aux_unwinder)
298 unwinders.emplace_back(aux_unwinder, nullptr);
299 if (native_unwinder) {
300 unwinders.emplace_back(native_unwinder, nullptr);
301 }
302 return unwinders;
303 }
304
305 } // namespace
306
TEST_F(StackSamplerTest,CopyStack)307 TEST_F(StackSamplerTest, CopyStack) {
308 base::test::TestFuture<void> sample_completed;
309 auto unwind_data = std::make_unique<StackUnwindData>(
310 std::make_unique<TestProfileBuilder>(&module_cache_));
311 const std::vector<uintptr_t> stack = {0, 1, 2, 3, 4};
312 InjectModuleForContextInstructionPointer(stack, &module_cache_);
313 std::vector<uintptr_t> stack_copy;
314 std::unique_ptr<StackSampler> stack_sampler = StackSampler::CreateForTesting(
315 std::make_unique<TestStackCopier>(stack), std::move(unwind_data),
316 MakeUnwindersFactory(std::make_unique<TestUnwinder>(&stack_copy)));
317
318 stack_sampler->Initialize();
319
320 std::unique_ptr<StackBuffer> stack_buffer =
321 std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
322 stack_sampler->RecordStackFrames(stack_buffer.get(),
323 PlatformThread::CurrentId(),
324 sample_completed.GetCallback());
325
326 ASSERT_TRUE(sample_completed.Wait());
327 EXPECT_EQ(stack, stack_copy);
328 }
329
330 #if BUILDFLAG(IS_CHROMEOS)
TEST_F(StackSamplerTest,RecordStackFramesUMAMetric)331 TEST_F(StackSamplerTest, RecordStackFramesUMAMetric) {
332 base::test::TestFuture<void> sample_completed;
333 HistogramTester histogram_tester;
334 auto unwind_data = std::make_unique<StackUnwindData>(
335 std::make_unique<TestProfileBuilder>(&module_cache_));
336 std::vector<uintptr_t> stack;
337 constexpr size_t UIntPtrsPerKilobyte = 1024 / sizeof(uintptr_t);
338 // kExpectedSizeKB needs to be a fairly large number of kilobytes. The buckets
339 // in UmaHistogramMemoryKB are big enough that small values are in the same
340 // bucket as zero and less than zero, and testing that we added a sample in
341 // that bucket means that the test won't fail if, for example, the
342 // |stack_top - stack_bottom| subtraction was reversed and got a negative
343 // value.
344 constexpr int kExpectedSizeKB = 2048;
345 for (uintptr_t i = 0; i <= (kExpectedSizeKB * UIntPtrsPerKilobyte) + 1; i++) {
346 stack.push_back(i);
347 }
348 InjectModuleForContextInstructionPointer(stack, &module_cache_);
349 std::vector<uintptr_t> stack_copy;
350 std::unique_ptr<StackSampler> stack_sampler = StackSampler::CreateForTesting(
351 std::make_unique<TestStackCopier>(stack), std::move(unwind_data),
352 MakeUnwindersFactory(std::make_unique<TestUnwinder>(&stack_copy)));
353
354 stack_sampler->Initialize();
355
356 std::unique_ptr<StackBuffer> stack_buffer =
357 std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
358
359 for (uint32_t i = 0; i < StackSampler::kUMAHistogramDownsampleAmount - 1;
360 i++) {
361 stack_sampler->RecordStackFrames(stack_buffer.get(),
362 PlatformThread::CurrentId(), DoNothing());
363
364 // Should have no new samples in the
365 // Memory.StackSamplingProfiler.StackSampleSize2 histogram.
366 histogram_tester.ExpectUniqueSample(
367 "Memory.StackSamplingProfiler.StackSampleSize2", kExpectedSizeKB, 0);
368 }
369
370 stack_sampler->RecordStackFrames(stack_buffer.get(),
371 PlatformThread::CurrentId(),
372 sample_completed.GetCallback());
373 ASSERT_TRUE(sample_completed.Wait());
374 histogram_tester.ExpectUniqueSample(
375 "Memory.StackSamplingProfiler.StackSampleSize2", kExpectedSizeKB, 1);
376 }
377 #endif // #if BUILDFLAG(IS_CHROMEOS)
378
TEST_F(StackSamplerTest,CopyStackTimestamp)379 TEST_F(StackSamplerTest, CopyStackTimestamp) {
380 base::test::TestFuture<void> sample_completed;
381 auto unwind_data = std::make_unique<StackUnwindData>(
382 std::make_unique<TestProfileBuilder>(&module_cache_));
383 const std::vector<uintptr_t> stack = {0};
384 InjectModuleForContextInstructionPointer(stack, &module_cache_);
385 std::vector<uintptr_t> stack_copy;
386 TimeTicks timestamp = TimeTicks::UnixEpoch();
387 std::unique_ptr<StackSampler> stack_sampler = StackSampler::CreateForTesting(
388 std::make_unique<TestStackCopier>(stack, timestamp),
389 std::move(unwind_data),
390 MakeUnwindersFactory(std::make_unique<TestUnwinder>(&stack_copy)));
391
392 stack_sampler->Initialize();
393
394 std::unique_ptr<StackBuffer> stack_buffer =
395 std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
396 stack_sampler->RecordStackFrames(stack_buffer.get(),
397 PlatformThread::CurrentId(),
398 sample_completed.GetCallback());
399
400 ASSERT_TRUE(sample_completed.Wait());
401 EXPECT_EQ(timestamp,
402 static_cast<TestProfileBuilder*>(
403 stack_sampler->GetStackUnwindData()->profile_builder())
404 ->last_timestamp());
405 }
406
TEST_F(StackSamplerTest,UnwinderInvokedWhileRecordingStackFrames)407 TEST_F(StackSamplerTest, UnwinderInvokedWhileRecordingStackFrames) {
408 base::test::TestFuture<void> sample_completed;
409 std::unique_ptr<StackBuffer> stack_buffer = std::make_unique<StackBuffer>(10);
410 auto owned_unwinder = std::make_unique<CallRecordingUnwinder>();
411 CallRecordingUnwinder* unwinder = owned_unwinder.get();
412 auto unwind_data = std::make_unique<StackUnwindData>(
413 std::make_unique<TestProfileBuilder>(&module_cache_));
414 std::unique_ptr<StackSampler> stack_sampler = StackSampler::CreateForTesting(
415 std::make_unique<DelegateInvokingStackCopier>(), std::move(unwind_data),
416 MakeUnwindersFactory(std::move(owned_unwinder)));
417
418 stack_sampler->Initialize();
419
420 stack_sampler->RecordStackFrames(stack_buffer.get(),
421 PlatformThread::CurrentId(),
422 sample_completed.GetCallback());
423 ASSERT_TRUE(sample_completed.Wait());
424 EXPECT_TRUE(unwinder->on_stack_capture_was_invoked());
425 EXPECT_TRUE(unwinder->update_modules_was_invoked());
426 }
427
TEST_F(StackSamplerTest,AuxUnwinderInvokedWhileRecordingStackFrames)428 TEST_F(StackSamplerTest, AuxUnwinderInvokedWhileRecordingStackFrames) {
429 base::test::TestFuture<void> sample_completed;
430 std::unique_ptr<StackBuffer> stack_buffer = std::make_unique<StackBuffer>(10);
431 auto unwind_data = std::make_unique<StackUnwindData>(
432 std::make_unique<TestProfileBuilder>(&module_cache_));
433 std::unique_ptr<StackSampler> stack_sampler = StackSampler::CreateForTesting(
434 std::make_unique<DelegateInvokingStackCopier>(), std::move(unwind_data),
435 MakeUnwindersFactory(std::make_unique<CallRecordingUnwinder>()));
436
437 stack_sampler->Initialize();
438
439 auto owned_aux_unwinder = std::make_unique<CallRecordingUnwinder>();
440 CallRecordingUnwinder* aux_unwinder = owned_aux_unwinder.get();
441 stack_sampler->AddAuxUnwinder(std::move(owned_aux_unwinder));
442
443 // The definition of this unwinder needs to be outside of the conditional
444 // for the `thread_pool_runner` otherwise we will hit a TSAN failure as
445 // we Record this thread's stack later in `RecordStackFrames`.
446 base::test::TestFuture<void> unwinder_added;
447 // If we are running a thread pool we need to wait for the aux unwinder to
448 // actually be added before we start recording stack frames.
449 if (stack_sampler->thread_pool_runner_) {
450 stack_sampler->thread_pool_runner_->PostTaskAndReply(
451 FROM_HERE, base::DoNothing(), unwinder_added.GetCallback());
452 ASSERT_TRUE(unwinder_added.Wait());
453 }
454
455 stack_sampler->RecordStackFrames(stack_buffer.get(),
456 PlatformThread::CurrentId(),
457 sample_completed.GetCallback());
458 ASSERT_TRUE(sample_completed.Wait());
459 EXPECT_TRUE(aux_unwinder->on_stack_capture_was_invoked());
460 EXPECT_TRUE(aux_unwinder->update_modules_was_invoked());
461 }
462
TEST_F(StackSamplerTest,WalkStack_Completed)463 TEST_F(StackSamplerTest, WalkStack_Completed) {
464 RegisterContext thread_context;
465 RegisterContextInstructionPointer(&thread_context) =
466 GetTestInstructionPointer();
467 module_cache_.AddCustomNativeModule(std::make_unique<TestModule>(1u, 1u));
468 auto native_unwinder =
469 WrapUnique(new FakeTestUnwinder({{UnwindResult::kCompleted, {1u}}}));
470 native_unwinder->Initialize(&module_cache_);
471
472 std::vector<Frame> stack = StackSampler::WalkStackForTesting(
473 &module_cache_, &thread_context, 0u,
474 MakeUnwinderStateVector(native_unwinder.get(), nullptr));
475
476 ASSERT_EQ(2u, stack.size());
477 EXPECT_EQ(1u, stack[1].instruction_pointer);
478 }
479
TEST_F(StackSamplerTest,WalkStack_Aborted)480 TEST_F(StackSamplerTest, WalkStack_Aborted) {
481 RegisterContext thread_context;
482 RegisterContextInstructionPointer(&thread_context) =
483 GetTestInstructionPointer();
484 module_cache_.AddCustomNativeModule(std::make_unique<TestModule>(1u, 1u));
485 auto native_unwinder =
486 WrapUnique(new FakeTestUnwinder({{UnwindResult::kAborted, {1u}}}));
487 native_unwinder->Initialize(&module_cache_);
488
489 std::vector<Frame> stack = StackSampler::WalkStackForTesting(
490 &module_cache_, &thread_context, 0u,
491 MakeUnwinderStateVector(native_unwinder.get(), nullptr));
492
493 ASSERT_EQ(2u, stack.size());
494 EXPECT_EQ(1u, stack[1].instruction_pointer);
495 }
496
TEST_F(StackSamplerTest,WalkStack_NotUnwound)497 TEST_F(StackSamplerTest, WalkStack_NotUnwound) {
498 RegisterContext thread_context;
499 RegisterContextInstructionPointer(&thread_context) =
500 GetTestInstructionPointer();
501 auto native_unwinder = WrapUnique(
502 new FakeTestUnwinder({{UnwindResult::kUnrecognizedFrame, {}}}));
503 native_unwinder->Initialize(&module_cache_);
504
505 std::vector<Frame> stack = StackSampler::WalkStackForTesting(
506 &module_cache_, &thread_context, 0u,
507 MakeUnwinderStateVector(native_unwinder.get(), nullptr));
508
509 ASSERT_EQ(1u, stack.size());
510 }
511
TEST_F(StackSamplerTest,WalkStack_AuxUnwind)512 TEST_F(StackSamplerTest, WalkStack_AuxUnwind) {
513 RegisterContext thread_context;
514 RegisterContextInstructionPointer(&thread_context) =
515 GetTestInstructionPointer();
516
517 // Treat the context instruction pointer as being in the aux unwinder's
518 // non-native module.
519 module_cache_.UpdateNonNativeModules(
520 {}, ToModuleVector(std::make_unique<TestModule>(
521 GetTestInstructionPointer(), 1u, false)));
522
523 auto aux_unwinder =
524 WrapUnique(new FakeTestUnwinder({{UnwindResult::kAborted, {1u}}}));
525 aux_unwinder->Initialize(&module_cache_);
526 std::vector<Frame> stack = StackSampler::WalkStackForTesting(
527 &module_cache_, &thread_context, 0u,
528 MakeUnwinderStateVector(nullptr, aux_unwinder.get()));
529
530 ASSERT_EQ(2u, stack.size());
531 EXPECT_EQ(GetTestInstructionPointer(), stack[0].instruction_pointer);
532 EXPECT_EQ(1u, stack[1].instruction_pointer);
533 }
534
TEST_F(StackSamplerTest,WalkStack_AuxThenNative)535 TEST_F(StackSamplerTest, WalkStack_AuxThenNative) {
536 RegisterContext thread_context;
537 RegisterContextInstructionPointer(&thread_context) = 0u;
538
539 // Treat the context instruction pointer as being in the aux unwinder's
540 // non-native module.
541 module_cache_.UpdateNonNativeModules(
542 {}, ToModuleVector(std::make_unique<TestModule>(0u, 1u, false)));
543 // Inject a fake native module for the second frame.
544 module_cache_.AddCustomNativeModule(std::make_unique<TestModule>(1u, 1u));
545
546 auto aux_unwinder = WrapUnique(
547 new FakeTestUnwinder({{UnwindResult::kUnrecognizedFrame, {1u}}, false}));
548 aux_unwinder->Initialize(&module_cache_);
549 auto native_unwinder =
550 WrapUnique(new FakeTestUnwinder({{UnwindResult::kCompleted, {2u}}}));
551 native_unwinder->Initialize(&module_cache_);
552
553 std::vector<Frame> stack = StackSampler::WalkStackForTesting(
554 &module_cache_, &thread_context, 0u,
555 MakeUnwinderStateVector(native_unwinder.get(), aux_unwinder.get()));
556
557 ASSERT_EQ(3u, stack.size());
558 EXPECT_EQ(0u, stack[0].instruction_pointer);
559 EXPECT_EQ(1u, stack[1].instruction_pointer);
560 EXPECT_EQ(2u, stack[2].instruction_pointer);
561 }
562
TEST_F(StackSamplerTest,WalkStack_NativeThenAux)563 TEST_F(StackSamplerTest, WalkStack_NativeThenAux) {
564 RegisterContext thread_context;
565 RegisterContextInstructionPointer(&thread_context) = 0u;
566
567 // Inject fake native modules for the instruction pointer from the context and
568 // the third frame.
569 module_cache_.AddCustomNativeModule(std::make_unique<TestModule>(0u, 1u));
570 module_cache_.AddCustomNativeModule(std::make_unique<TestModule>(2u, 1u));
571 // Treat the second frame's pointer as being in the aux unwinder's non-native
572 // module.
573 module_cache_.UpdateNonNativeModules(
574 {}, ToModuleVector(std::make_unique<TestModule>(1u, 1u, false)));
575
576 auto aux_unwinder = WrapUnique(new FakeTestUnwinder(
577 {{false}, {UnwindResult::kUnrecognizedFrame, {2u}}, {false}}));
578 aux_unwinder->Initialize(&module_cache_);
579 auto native_unwinder =
580 WrapUnique(new FakeTestUnwinder({{UnwindResult::kUnrecognizedFrame, {1u}},
581 {UnwindResult::kCompleted, {3u}}}));
582 native_unwinder->Initialize(&module_cache_);
583
584 std::vector<Frame> stack = StackSampler::WalkStackForTesting(
585 &module_cache_,
586
587 &thread_context, 0u,
588 MakeUnwinderStateVector(native_unwinder.get(), aux_unwinder.get()));
589
590 ASSERT_EQ(4u, stack.size());
591 EXPECT_EQ(0u, stack[0].instruction_pointer);
592 EXPECT_EQ(1u, stack[1].instruction_pointer);
593 EXPECT_EQ(2u, stack[2].instruction_pointer);
594 EXPECT_EQ(3u, stack[3].instruction_pointer);
595 }
596
597 } // namespace base
598