chromium/base/profiler/stack_sampling_profiler_unittest.cc

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

#include "base/profiler/stack_sampling_profiler.h"

#include <stddef.h>
#include <stdint.h>

#include <cstdlib>
#include <memory>
#include <set>
#include <utility>
#include <vector>

#include "base/compiler_specific.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/metrics_hashes.h"
#include "base/notimplemented.h"
#include "base/profiler/profiler_buildflags.h"
#include "base/profiler/sample_metadata.h"
#include "base/profiler/stack_sampler.h"
#include "base/profiler/stack_sampling_profiler_test_util.h"
#include "base/profiler/unwinder.h"
#include "base/ranges/algorithm.h"
#include "base/run_loop.h"
#include "base/scoped_native_library.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/threading/simple_thread.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(IS_WIN)
#include <windows.h>

#include <intrin.h>
#include <malloc.h>
#else
#include <alloca.h>
#endif

// STACK_SAMPLING_PROFILER_SUPPORTED is used to conditionally enable the tests
// below for supported platforms (currently Win x64, Mac, iOS 64, some
// Android, and ChromeOS x64).
// ChromeOS: These don't run under MSan because parts of the stack aren't
// initialized.
#if (BUILDFLAG(IS_WIN) && defined(ARCH_CPU_X86_64)) || (BUILDFLAG(IS_MAC)) || \
    (BUILDFLAG(IS_IOS) && defined(ARCH_CPU_64_BITS)) ||                       \
    (BUILDFLAG(IS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)) ||             \
    (BUILDFLAG(IS_CHROMEOS) &&                                                \
     (defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64)) &&                 \
     !defined(MEMORY_SANITIZER))
#define STACK_SAMPLING_PROFILER_SUPPORTED
#endif

namespace base {

#if defined(STACK_SAMPLING_PROFILER_SUPPORTED)
#define PROFILER_TEST_F
#else
#define PROFILER_TEST_F(TestClass, TestName)
#endif

SamplingParams;

namespace {

// State provided to the ProfileBuilder's ApplyMetadataRetrospectively function.
struct RetrospectiveMetadata {};

// Profile consists of a set of samples and other sampling information.
struct Profile {};

// The callback type used to collect a profile. The passed Profile is move-only.
// Other threads, including the UI thread, may block on callback completion so
// this should run as quickly as possible.
ProfileCompletedCallback;

// TestProfileBuilder collects samples produced by the profiler.
class TestProfileBuilder : public ProfileBuilder {};

TestProfileBuilder::TestProfileBuilder(ModuleCache* module_cache,
                                       ProfileCompletedCallback callback)
    :{}

TestProfileBuilder::~TestProfileBuilder() = default;

ModuleCache* TestProfileBuilder::GetModuleCache() {}

void TestProfileBuilder::RecordMetadata(
    const MetadataRecorder::MetadataProvider& metadata_provider) {}

void TestProfileBuilder::ApplyMetadataRetrospectively(
    TimeTicks period_start,
    TimeTicks period_end,
    const MetadataRecorder::Item& item) {}

void TestProfileBuilder::AddProfileMetadata(
    const MetadataRecorder::Item& item) {}

void TestProfileBuilder::OnSampleCompleted(std::vector<Frame> sample,
                                           TimeTicks sample_timestamp) {}

void TestProfileBuilder::OnProfileCompleted(TimeDelta profile_duration,
                                            TimeDelta sampling_period) {}

// Unloads |library| and returns when it has completed unloading. Unloading a
// library is asynchronous on Windows, so simply calling UnloadNativeLibrary()
// is insufficient to ensure it's been unloaded.
void SynchronousUnloadNativeLibrary(NativeLibrary library) {}

void WithTargetThread(ProfileCallback profile_callback) {}

struct TestProfilerInfo {};

// Captures samples as specified by |params| on the TargetThread, and returns
// them. Waits up to |profiler_wait_time| for the profiler to complete.
std::vector<std::vector<Frame>> CaptureSamples(const SamplingParams& params,
                                               TimeDelta profiler_wait_time,
                                               ModuleCache* module_cache) {}

// Waits for one of multiple samplings to complete.
size_t WaitForSamplingComplete(
    const std::vector<std::unique_ptr<TestProfilerInfo>>& infos) {}

// Returns a duration that is longer than the test timeout. We would use
// TimeDelta::Max() but https://crbug.com/465948.
TimeDelta AVeryLongTimeDelta() {}

// Tests the scenario where the library is unloaded after copying the stack, but
// before walking it. If |wait_until_unloaded| is true, ensures that the
// asynchronous library loading has completed before walking the stack. If
// false, the unloading may still be occurring during the stack walk.
void TestLibraryUnload(bool wait_until_unloaded, ModuleCache* module_cache) {}

// Provide a suitable (and clean) environment for the tests below. All tests
// must use this class to ensure that proper clean-up is done and thus be
// usable in a later test.
class StackSamplingProfilerTest : public testing::Test {};

}  // namespace

// Checks that the basic expected information is present in sampled frames.
//
// macOS ASAN is not yet supported - crbug.com/718628.
//
// TODO(crbug.com/40702833): Enable this test again for Android with
// ASAN. This is now disabled because the android-asan bot fails.
//
// If we're running the ChromeOS unit tests on Linux, this test will never pass
// because Ubuntu's libc isn't compiled with frame pointers. Skip if not a real
// ChromeOS device.
#if (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_APPLE)) ||   \
    (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_ANDROID)) || \
    (BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_CHROMEOS_DEVICE))
#define MAYBE_Basic
#else
#define MAYBE_Basic
#endif
PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_Basic) {}

// A simple unwinder that always generates one frame then aborts the stack walk.
class TestAuxUnwinder : public Unwinder {};

// Checks that the profiler handles stacks containing dynamically-allocated
// stack memory.
// macOS ASAN is not yet supported - crbug.com/718628.
// Android is not supported since Chrome unwind tables don't support dynamic
// frames.
// If we're running the ChromeOS unit tests on Linux, this test will never pass
// because Ubuntu's libc isn't compiled with frame pointers. Skip if not a real
// ChromeOS device.
#if (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_APPLE)) || \
    BUILDFLAG(IS_ANDROID) ||                               \
    (BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_CHROMEOS_DEVICE))
#define MAYBE_Alloca
#else
#define MAYBE_Alloca
#endif
PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_Alloca) {}

// Checks that a stack that runs through another library produces a stack with
// the expected functions.
// macOS ASAN is not yet supported - crbug.com/718628.
// iOS chrome doesn't support loading native libraries.
// Android is not supported when EXCLUDE_UNWIND_TABLES |other_library| doesn't
// have unwind tables.
// TODO(crbug.com/40702833): Enable this test again for Android with
// ASAN. This is now disabled because the android-asan bot fails.
// If we're running the ChromeOS unit tests on Linux, this test will never pass
// because Ubuntu's libc isn't compiled with frame pointers. Skip if not a real
// ChromeOS device.
#if (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_APPLE)) ||         \
    BUILDFLAG(IS_IOS) ||                                           \
    (BUILDFLAG(IS_ANDROID) && BUILDFLAG(EXCLUDE_UNWIND_TABLES)) || \
    (BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)) ||       \
    (BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_CHROMEOS_DEVICE))
#define MAYBE_OtherLibrary
#else
#define MAYBE_OtherLibrary
#endif
PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_OtherLibrary) {}

// Checks that a stack that runs through a library that is unloading produces a
// stack, and doesn't crash.
// Unloading is synchronous on the Mac, so this test is inapplicable.
// Android is not supported when EXCLUDE_UNWIND_TABLES |other_library| doesn't
// have unwind tables.
// TODO(crbug.com/40702833): Enable this test again for Android with
// ASAN. This is now disabled because the android-asan bot fails.
// If we're running the ChromeOS unit tests on Linux, this test will never pass
// because Ubuntu's libc isn't compiled with frame pointers. Skip if not a real
// ChromeOS device.
#if BUILDFLAG(IS_APPLE) ||                                         \
    (BUILDFLAG(IS_ANDROID) && BUILDFLAG(EXCLUDE_UNWIND_TABLES)) || \
    (BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)) ||       \
    (BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_CHROMEOS_DEVICE))
#define MAYBE_UnloadingLibrary
#else
#define MAYBE_UnloadingLibrary
#endif
PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_UnloadingLibrary) {}

// Checks that a stack that runs through a library that has been unloaded
// produces a stack, and doesn't crash.
// macOS ASAN is not yet supported - crbug.com/718628.
// Android is not supported since modules are found before unwinding.
// If we're running the ChromeOS unit tests on Linux, this test will never pass
// because Ubuntu's libc isn't compiled with frame pointers. Skip if not a real
// ChromeOS device.
#if (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_APPLE)) || \
    BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) ||          \
    (BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_CHROMEOS_DEVICE))
#define MAYBE_UnloadedLibrary
#else
#define MAYBE_UnloadedLibrary
#endif
PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_UnloadedLibrary) {}

// Checks that a profiler can stop/destruct without ever having started.
PROFILER_TEST_F(StackSamplingProfilerTest, StopWithoutStarting) {}

// Checks that its okay to stop a profiler before it finishes even when the
// sampling thread continues to run.
PROFILER_TEST_F(StackSamplingProfilerTest, StopSafely) {}

// Checks that no sample are captured if the profiling is stopped during the
// initial delay.
PROFILER_TEST_F(StackSamplingProfilerTest, StopDuringInitialDelay) {}

// Checks that tasks can be stopped before completion and incomplete samples are
// captured.
PROFILER_TEST_F(StackSamplingProfilerTest, StopDuringInterSampleInterval) {}

PROFILER_TEST_F(StackSamplingProfilerTest, GetNextSampleTime_NormalExecution) {}

PROFILER_TEST_F(StackSamplingProfilerTest, GetNextSampleTime_DelayedExecution) {}

// Checks that we can destroy the profiler while profiling.
PROFILER_TEST_F(StackSamplingProfilerTest, DestroyProfilerWhileProfiling) {}

// Checks that the different profilers may be run.
PROFILER_TEST_F(StackSamplingProfilerTest, CanRunMultipleProfilers) {}

// Checks that a sampler can be started while another is running.
PROFILER_TEST_F(StackSamplingProfilerTest, MultipleStart) {}

// Checks that the profile duration and the sampling interval are calculated
// correctly. Also checks that RecordMetadata() is invoked each time a sample
// is recorded.
PROFILER_TEST_F(StackSamplingProfilerTest, ProfileGeneralInfo) {}

// Checks that the sampling thread can shut down.
PROFILER_TEST_F(StackSamplingProfilerTest, SamplerIdleShutdown) {}

// Checks that additional requests will restart a stopped profiler.
PROFILER_TEST_F(StackSamplingProfilerTest,
                WillRestartSamplerAfterIdleShutdown) {}

// Checks that it's safe to stop a task after it's completed and the sampling
// thread has shut-down for being idle.
PROFILER_TEST_F(StackSamplingProfilerTest, StopAfterIdleShutdown) {}

// Checks that profilers can run both before and after the sampling thread has
// started.
PROFILER_TEST_F(StackSamplingProfilerTest,
                ProfileBeforeAndAfterSamplingThreadRunning) {}

// Checks that an idle-shutdown task will abort if a new profiler starts
// between when it was posted and when it runs.
PROFILER_TEST_F(StackSamplingProfilerTest, IdleShutdownAbort) {}

// Checks that synchronized multiple sampling requests execute in parallel.
PROFILER_TEST_F(StackSamplingProfilerTest, ConcurrentProfiling_InSync) {}

// Checks that several mixed sampling requests execute in parallel.
PROFILER_TEST_F(StackSamplingProfilerTest, ConcurrentProfiling_Mixed) {}

// Checks that different threads can be sampled in parallel.
PROFILER_TEST_F(StackSamplingProfilerTest, MultipleSampledThreads) {}

// A simple thread that runs a profiler on another thread.
class ProfilerThread : public SimpleThread {};

// Checks that different threads can run samplers in parallel.
PROFILER_TEST_F(StackSamplingProfilerTest, MultipleProfilerThreads) {}

PROFILER_TEST_F(StackSamplingProfilerTest, AddAuxUnwinder_BeforeStart) {}

PROFILER_TEST_F(StackSamplingProfilerTest, AddAuxUnwinder_AfterStart) {}

PROFILER_TEST_F(StackSamplingProfilerTest, AddAuxUnwinder_AfterStop) {}

// Checks that requests to apply metadata to past samples are passed on to the
// profile builder.
PROFILER_TEST_F(StackSamplingProfilerTest,
                ApplyMetadataToPastSamples_PassedToProfileBuilder) {}

PROFILER_TEST_F(
    StackSamplingProfilerTest,
    ApplyMetadataToPastSamples_PassedToProfileBuilder_MultipleCollections) {}

// Checks that requests to add profile metadata are passed on to the profile
// builder.
PROFILER_TEST_F(StackSamplingProfilerTest,
                AddProfileMetadata_PassedToProfileBuilder) {}

PROFILER_TEST_F(StackSamplingProfilerTest,
                AddProfileMetadata_PassedToProfileBuilder_MultipleCollections) {}

}  // namespace base