chromium/base/profiler/stack_sampling_profiler_test_util.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_sampling_profiler_test_util.h"

#include <string_view>
#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/profiler/native_unwinder_android_map_delegate.h"
#include "base/profiler/native_unwinder_android_memory_regions_map.h"
#include "base/profiler/profiler_buildflags.h"
#include "base/profiler/stack_buffer.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "base/profiler/unwinder.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(IS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)
#include "base/android/apk_assets.h"
#include "base/android/library_loader/anchor_functions.h"
#include "base/files/memory_mapped_file.h"
#include "base/no_destructor.h"
#include "base/profiler/chrome_unwinder_android.h"
#include "base/profiler/native_unwinder_android.h"
#endif

#if BUILDFLAG(IS_WIN)
// Windows doesn't provide an alloca function like Linux does.
// Fortunately, it provides _alloca, which functions identically.
#include <malloc.h>
#define alloca
#else
#include <alloca.h>
#endif

extern "C" {
// The address of |__executable_start| gives the start address of the
// executable or shared library. This value is used to find the offset address
// of the instruction in binary from PC.
extern char __executable_start;
}

namespace base {

namespace {

// A profile builder for test use that expects to receive exactly one sample.
class TestProfileBuilder : public ProfileBuilder {};

// The function to be executed by the code in the other library.
void OtherLibraryCallback(void* arg) {}

#if BUILDFLAG(IS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)
class NativeUnwinderAndroidMapDelegateForTesting
    : public NativeUnwinderAndroidMapDelegate {
 public:
  explicit NativeUnwinderAndroidMapDelegateForTesting(
      std::unique_ptr<NativeUnwinderAndroidMemoryRegionsMap> memory_regions_map)
      : memory_regions_map_(std::move(memory_regions_map)) {}

  NativeUnwinderAndroidMemoryRegionsMap* GetMapReference() override {
    return memory_regions_map_.get();
  }
  void ReleaseMapReference() override {}

 private:
  const std::unique_ptr<NativeUnwinderAndroidMemoryRegionsMap>
      memory_regions_map_;
};

// `map_delegate` should outlive the unwinder instance, so we cannot make a
// derived `NativeUnwinderAndroidForTesting` to own the `map_delegate`, as
// the base class outlives the derived class.
NativeUnwinderAndroidMapDelegateForTesting* GetMapDelegateForTesting() {
  static base::NoDestructor<NativeUnwinderAndroidMapDelegateForTesting>
      map_delegate(NativeUnwinderAndroid::CreateMemoryRegionsMap());
  return map_delegate.get();
}

std::unique_ptr<NativeUnwinderAndroid> CreateNativeUnwinderAndroidForTesting(
    uintptr_t exclude_module_with_base_address) {
  return std::make_unique<NativeUnwinderAndroid>(
      exclude_module_with_base_address, GetMapDelegateForTesting());
}

std::unique_ptr<Unwinder> CreateChromeUnwinderAndroidForTesting(
    uintptr_t chrome_module_base_address) {
  static constexpr char kCfiFileName[] = "assets/unwind_cfi_32_v2";

  // The wrapper class ensures that `MemoryMappedFile` has the same lifetime
  // as the unwinder.
  class ChromeUnwinderAndroidForTesting : public ChromeUnwinderAndroid {
   public:
    ChromeUnwinderAndroidForTesting(std::unique_ptr<MemoryMappedFile> cfi_file,
                                    const ChromeUnwindInfoAndroid& unwind_info,
                                    uintptr_t chrome_module_base_address,
                                    uintptr_t text_section_start_address)
        : ChromeUnwinderAndroid(unwind_info,
                                chrome_module_base_address,
                                text_section_start_address),
          cfi_file_(std::move(cfi_file)) {}
    ~ChromeUnwinderAndroidForTesting() override = default;

   private:
    std::unique_ptr<MemoryMappedFile> cfi_file_;
  };

  MemoryMappedFile::Region cfi_region;
  int fd = base::android::OpenApkAsset(kCfiFileName, &cfi_region);
  DCHECK_GT(fd, 0);
  auto cfi_file = std::make_unique<MemoryMappedFile>();
  bool ok = cfi_file->Initialize(base::File(fd), cfi_region);
  DCHECK(ok);
  return std::make_unique<ChromeUnwinderAndroidForTesting>(
      std::move(cfi_file),
      base::CreateChromeUnwindInfoAndroid(
          {cfi_file->data(), cfi_file->length()}),
      chrome_module_base_address,
      /* text_section_start_address= */ base::android::kStartOfText);
}
#endif  // #if BUILDFLAG(IS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)

}  // namespace

TargetThread::TargetThread(OnceClosure to_run) :{}

TargetThread::~TargetThread() = default;

void TargetThread::Start() {}

void TargetThread::Join() {}

void TargetThread::ThreadMain() {}

UnwindScenario::UnwindScenario(const SetupFunction& setup_function)
    :{}

UnwindScenario::~UnwindScenario() = default;

FunctionAddressRange UnwindScenario::GetWaitForSampleAddressRange() const {}

FunctionAddressRange UnwindScenario::GetSetupFunctionAddressRange() const {}

FunctionAddressRange UnwindScenario::GetOuterFunctionAddressRange() const {}

void UnwindScenario::Execute(SampleEvents* events) {}

// static
// Disable inlining for this function so that it gets its own stack frame.
NOINLINE FunctionAddressRange
UnwindScenario::InvokeSetupFunction(const SetupFunction& setup_function,
                                    SampleEvents* events) {}

// static
// Disable inlining for this function so that it gets its own stack frame.
NOINLINE FunctionAddressRange
UnwindScenario::WaitForSample(SampleEvents* events) {}

// Disable inlining for this function so that it gets its own stack frame.
NOINLINE FunctionAddressRange
CallWithPlainFunction(OnceClosure wait_for_sample) {}

// Disable inlining for this function so that it gets its own stack frame.
NOINLINE FunctionAddressRange CallWithAlloca(OnceClosure wait_for_sample) {}

// Disable inlining for this function so that it gets its own stack frame.
NOINLINE FunctionAddressRange
CallThroughOtherLibrary(NativeLibrary library, OnceClosure wait_for_sample) {}

void WithTargetThread(UnwindScenario* scenario,
                      ProfileCallback profile_callback) {}

std::vector<Frame> SampleScenario(UnwindScenario* scenario,
                                  ModuleCache* module_cache,
                                  UnwinderFactory aux_unwinder_factory) {}

std::string FormatSampleForDiagnosticOutput(const std::vector<Frame>& sample) {}

void ExpectStackContains(const std::vector<Frame>& stack,
                         const std::vector<FunctionAddressRange>& functions) {}

void ExpectStackDoesNotContain(
    const std::vector<Frame>& stack,
    const std::vector<FunctionAddressRange>& functions) {}

NativeLibrary LoadTestLibrary(std::string_view library_name) {}

NativeLibrary LoadOtherLibrary() {}

uintptr_t GetAddressInOtherLibrary(NativeLibrary library) {}

StackSamplingProfiler::UnwindersFactory CreateCoreUnwindersFactoryForTesting(
    ModuleCache* module_cache) {}

uintptr_t TestModule::GetBaseAddress() const {}
std::string TestModule::GetId() const {}
FilePath TestModule::GetDebugBasename() const {}
size_t TestModule::GetSize() const {}
bool TestModule::IsNative() const {}

bool operator==(const Frame& a, const Frame& b) {}

}  // namespace base