blob: 836bc113a387f79901d36a0649c2e5ac9ec2732f [file]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_PROFILER_THREAD_GROUP_PROFILER_H_
#define BASE_PROFILER_THREAD_GROUP_PROFILER_H_
#include <memory>
#include <vector>
#include "base/base_export.h"
#include "base/containers/flat_map.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/profiler/periodic_sampling_scheduler.h"
#include "base/profiler/sampling_profiler_thread_token.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
namespace base {
class SequencedTaskRunner;
namespace internal {
class WorkerThread;
} // namespace internal
class ProfileBuilder;
class ThreadGroupProfilerClient;
// ThreadGroupProfiler manages sampling of active worker threads and
// schedules periodic sampling for a ThreadGroup.
//
// Once created, ThreadGroupProfiler will periodically profile active worker
// threads by creating a StackSamplingProfiler for each thread. At the beginning
// of a session, all active worker threads are sampled. During the session, if a
// worker thread becomes active (via MaybeAddWorkerThread) it will be sampled
// for the remainder of this session. Once the sampling starts for a thread it
// will continue until either the thread is exiting or the profile is completed.
// When a profile completes the associated StackSamplingProfiler is destroyed.
//
// All methods that access profiler state are expected to be externally
// synchronized by the owner (e.g. ThreadGroupImpl's CheckedLock).
class BASE_EXPORT ThreadGroupProfiler {
public:
// Interface for profiling stack samples from a specific thread.
// This provides an abstraction over StackSamplingProfiler to enable testing
// of ThreadGroupProfiler without depending on actual profiler implementation.
class BASE_EXPORT Profiler : public RefCountedThreadSafe<Profiler> {
public:
virtual void Start() = 0;
protected:
friend class RefCountedThreadSafe<Profiler>;
virtual ~Profiler() = default;
};
// Sets the instance of ThreadProfilerClient to provide embedder-specific
// implementation logic. This instance must be set early, before
// CreateThreadGroupProfiler() and IsProfilingEnabled() are called.
static void SetClient(std::unique_ptr<ThreadGroupProfilerClient> client);
// Must be called after SetClient().
static bool IsProfilingEnabled();
using ProfilerFactory = RepeatingCallback<scoped_refptr<Profiler>(
int64_t thread_group_type,
SamplingProfilerThreadToken thread_token,
const StackSamplingProfiler::SamplingParams& params,
std::unique_ptr<ProfileBuilder> profile_builder,
StackSamplingProfiler::UnwindersFactory unwinder_factory)>;
class BASE_EXPORT ActiveCollection;
// Interface implemented by the owner of ThreadGroupProfiler (e.g.
// ThreadGroupImpl) to receive start and end notifications for profiling
// sessions.
class BASE_EXPORT Delegate {
public:
virtual ~Delegate() = default;
// Called on the service thread sequence when a profiling session starts.
// The delegate is responsible for storing `active_collection` (under its
// lock) and initiating profiling on active worker threads.
virtual void OnStartProfilingSession(
ActiveCollection active_collection) = 0;
// Called on the service thread sequence when the profiling session ends.
// The delegate is responsible for destroying its active collection.
virtual void OnEndProfilingSession() = 0;
};
// ThreadGroupProfiler constructor. |thread_group_type| will be used to tag
// metadata on all samples collected in this profiler. |profiler_factory| is a
// repeating callback that will be used to make Profiler, intended to be used
// for dependency injection for testing.
explicit ThreadGroupProfiler(
int64_t thread_group_type,
Delegate* delegate,
std::unique_ptr<PeriodicSamplingScheduler> periodic_sampling_scheduler =
nullptr,
ProfilerFactory profiler_factory = GetDefaultProfilerFactory());
ThreadGroupProfiler(const ThreadGroupProfiler&) = delete;
ThreadGroupProfiler& operator=(const ThreadGroupProfiler&) = delete;
~ThreadGroupProfiler();
// Starts the periodic profiling loop on `service_thread_task_runner`.
void Start(scoped_refptr<SequencedTaskRunner> service_thread_task_runner);
// Represents an active sample collection phase and is responsible for
// creating profilers for active threads both at the beginning as well as
// during the sampling duration.
//
// All methods that access profiler state are expected to be externally
// synchronized by the owner (e.g. ThreadGroupImpl's CheckedLock).
class BASE_EXPORT ActiveCollection {
public:
explicit ActiveCollection(int64_t thread_group_type,
TimeDelta sampling_duration,
ProfilerFactory stack_sampling_profiler_factory);
~ActiveCollection();
ActiveCollection(const ActiveCollection&) = delete;
ActiveCollection& operator=(const ActiveCollection&) = delete;
ActiveCollection(ActiveCollection&&);
ActiveCollection& operator=(ActiveCollection&&);
// Maybe create a new profiler for worker_thread depending on how close
// the collection is to being complete. Returns the created profiler if any.
scoped_refptr<Profiler> MaybeAddWorkerThread(
internal::WorkerThread* worker_thread,
const SamplingProfilerThreadToken& token);
// Removes and returns the profiler for worker_thread if it exists, so that
// it can be destroyed outside locks.
scoped_refptr<Profiler> RemoveWorkerThread(
internal::WorkerThread* worker_thread);
private:
// Helper function for creating the StackSamplingProfiler.
scoped_refptr<Profiler> CreateSamplingProfilerForThread(
internal::WorkerThread* worker_thread,
const SamplingProfilerThreadToken& token,
const StackSamplingProfiler::SamplingParams& sampling_params);
int64_t thread_group_type_;
// A map that stores the active `StackSamplingProfiler` instances
// for each worker thread.
flat_map<internal::WorkerThread*, scoped_refptr<Profiler>> profilers_;
ProfilerFactory stack_sampling_profiler_factory_;
TimeDelta sampling_duration_;
// Tracks the end time (an estimate calculated at start of sampling by
// adding the sampling duration) of the current sampling session.
TimeTicks collection_end_time_;
};
private:
class ProfilerImpl;
// Creates a new ActiveCollection for a sampling session.
ActiveCollection CreateActiveCollection();
void ScheduleNextCollection();
void OnStartProfilingSessionTask();
void OnEndProfilingSessionTask();
// Retrieve the ThreadGroupProfilerClient instance provided via SetClient().
static ThreadGroupProfilerClient* GetClient();
static ProfilerFactory GetDefaultProfilerFactory();
// Retrieve the static sampling duration configured on the client.
static TimeDelta GetSamplingDuration();
SEQUENCE_CHECKER(sequence_checker_);
// Value to use as metadata for specifying which type of thread group is being
// profiled.
const int64_t thread_group_type_;
const raw_ptr<Delegate> delegate_;
std::unique_ptr<PeriodicSamplingScheduler> periodic_sampling_scheduler_;
ProfilerFactory stack_sampling_profiler_factory_;
scoped_refptr<SequencedTaskRunner> service_thread_task_runner_;
WeakPtrFactory<ThreadGroupProfiler> weak_ptr_factory_{this};
};
} // namespace base
#endif // BASE_PROFILER_THREAD_GROUP_PROFILER_H_