• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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