xref: /aosp_15_r20/external/cronet/base/profiler/stack_sampler_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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