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