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