chromium/base/profiler/stack_sampler_unittest.cc

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif

#include "base/profiler/stack_sampler.h"

#include <algorithm>
#include <cstring>
#include <iterator>
#include <memory>
#include <numeric>
#include <utility>

#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/profiler/module_cache.h"
#include "base/profiler/profile_builder.h"
#include "base/profiler/stack_buffer.h"
#include "base/profiler/stack_copier.h"
#include "base/profiler/stack_sampling_profiler_test_util.h"
#include "base/profiler/suspendable_thread_delegate.h"
#include "base/profiler/unwinder.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace base {

namespace {

ElementsAre;

class StackSamplerTest : public ::testing::Test {};

class TestProfileBuilder : public ProfileBuilder {};

// A stack copier for use in tests that provides the expected behavior when
// operating on the supplied fake stack.
class TestStackCopier : public StackCopier {};

// A StackCopier that just invokes the expected functions on the delegate.
class DelegateInvokingStackCopier : public StackCopier {};

// Trivial unwinder implementation for testing.
class TestUnwinder : public Unwinder {};

// Records invocations of calls to OnStackCapture()/UpdateModules().
class CallRecordingUnwinder : public Unwinder {};

// Utility function to form a vector from a single module.
std::vector<std::unique_ptr<const ModuleCache::Module>> ToModuleVector(
    std::unique_ptr<const ModuleCache::Module> module) {}

// Injects a fake module covering the initial instruction pointer value, to
// avoid asking the OS to look it up. Windows doesn't return a consistent error
// code when doing so, and we DCHECK_EQ the expected error code.
void InjectModuleForContextInstructionPointer(
    const std::vector<uintptr_t>& stack,
    ModuleCache* module_cache) {}

// Returns a plausible instruction pointer value for use in tests that don't
// care about the instruction pointer value in the context, and hence don't need
// InjectModuleForContextInstructionPointer().
uintptr_t GetTestInstructionPointer() {}

// An unwinder fake that replays the provided outputs.
class FakeTestUnwinder : public Unwinder {};

StackSampler::UnwindersFactory MakeUnwindersFactory(
    std::unique_ptr<Unwinder> unwinder) {}

std::vector<UnwinderCapture> MakeUnwinderStateVector(Unwinder* native_unwinder,
                                                     Unwinder* aux_unwinder) {}

}  // namespace

TEST_F(StackSamplerTest, CopyStack) {}

#if BUILDFLAG(IS_CHROMEOS)
TEST_F(StackSamplerTest, RecordStackFramesUMAMetric) {
  base::test::TestFuture<void> sample_completed;
  HistogramTester histogram_tester;
  auto unwind_data = std::make_unique<StackUnwindData>(
      std::make_unique<TestProfileBuilder>(&module_cache_));
  std::vector<uintptr_t> stack;
  constexpr size_t UIntPtrsPerKilobyte = 1024 / sizeof(uintptr_t);
  // kExpectedSizeKB needs to be a fairly large number of kilobytes. The buckets
  // in UmaHistogramMemoryKB are big enough that small values are in the same
  // bucket as zero and less than zero, and testing that we added a sample in
  // that bucket means that the test won't fail if, for example, the
  // |stack_top - stack_bottom| subtraction was reversed and got a negative
  // value.
  constexpr int kExpectedSizeKB = 2048;
  for (uintptr_t i = 0; i <= (kExpectedSizeKB * UIntPtrsPerKilobyte) + 1; i++) {
    stack.push_back(i);
  }
  InjectModuleForContextInstructionPointer(stack, &module_cache_);
  std::vector<uintptr_t> stack_copy;
  std::unique_ptr<StackSampler> stack_sampler = StackSampler::CreateForTesting(
      std::make_unique<TestStackCopier>(stack), std::move(unwind_data),
      MakeUnwindersFactory(std::make_unique<TestUnwinder>(&stack_copy)));

  stack_sampler->Initialize();

  std::unique_ptr<StackBuffer> stack_buffer =
      std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));

  for (uint32_t i = 0; i < StackSampler::kUMAHistogramDownsampleAmount - 1;
       i++) {
    stack_sampler->RecordStackFrames(stack_buffer.get(),
                                     PlatformThread::CurrentId(), DoNothing());

    // Should have no new samples in the
    // Memory.StackSamplingProfiler.StackSampleSize2 histogram.
    histogram_tester.ExpectUniqueSample(
        "Memory.StackSamplingProfiler.StackSampleSize2", kExpectedSizeKB, 0);
  }

  stack_sampler->RecordStackFrames(stack_buffer.get(),
                                   PlatformThread::CurrentId(),
                                   sample_completed.GetCallback());
  ASSERT_TRUE(sample_completed.Wait());
  histogram_tester.ExpectUniqueSample(
      "Memory.StackSamplingProfiler.StackSampleSize2", kExpectedSizeKB, 1);
}
#endif  // #if BUILDFLAG(IS_CHROMEOS)

TEST_F(StackSamplerTest, CopyStackTimestamp) {}

TEST_F(StackSamplerTest, UnwinderInvokedWhileRecordingStackFrames) {}

TEST_F(StackSamplerTest, AuxUnwinderInvokedWhileRecordingStackFrames) {}

TEST_F(StackSamplerTest, WalkStack_Completed) {}

TEST_F(StackSamplerTest, WalkStack_Aborted) {}

TEST_F(StackSamplerTest, WalkStack_NotUnwound) {}

TEST_F(StackSamplerTest, WalkStack_AuxUnwind) {}

TEST_F(StackSamplerTest, WalkStack_AuxThenNative) {}

TEST_F(StackSamplerTest, WalkStack_NativeThenAux) {}

}  // namespace base