123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913 |
- // Copyright 2015 The Chromium Authors. All rights reserved.
- // 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 <algorithm>
- #include <cmath>
- #include <map>
- #include <utility>
- #include "base/atomic_sequence_num.h"
- #include "base/atomicops.h"
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/callback_helpers.h"
- #include "base/location.h"
- #include "base/memory/ptr_util.h"
- #include "base/memory/raw_ptr.h"
- #include "base/memory/singleton.h"
- #include "base/profiler/profiler_buildflags.h"
- #include "base/profiler/stack_buffer.h"
- #include "base/profiler/stack_sampler.h"
- #include "base/profiler/unwinder.h"
- #include "base/synchronization/lock.h"
- #include "base/synchronization/waitable_event.h"
- #include "base/thread_annotations.h"
- #include "base/threading/thread.h"
- #include "base/threading/thread_restrictions.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/time/time.h"
- #include "base/trace_event/base_tracing.h"
- #include "build/build_config.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- #if BUILDFLAG(IS_WIN)
- #include "base/win/static_constants.h"
- #endif
- #if BUILDFLAG(IS_APPLE)
- #include "base/mac/mac_util.h"
- #endif
- namespace base {
- // Allows StackSamplingProfiler to recall a thread which should already pretty
- // much be dead (thus it should be a fast Join()).
- class ScopedAllowThreadRecallForStackSamplingProfiler
- : public ScopedAllowBaseSyncPrimitivesOutsideBlockingScope {};
- namespace {
- // This value is used to initialize the WaitableEvent object. This MUST BE set
- // to MANUAL for correct operation of the IsSignaled() call in Start(). See the
- // comment there for why.
- constexpr WaitableEvent::ResetPolicy kResetPolicy =
- WaitableEvent::ResetPolicy::MANUAL;
- // This value is used when there is no collection in progress and thus no ID
- // for referencing the active collection to the SamplingThread.
- const int kNullProfilerId = -1;
- TimeTicks GetNextSampleTimeImpl(TimeTicks scheduled_current_sample_time,
- TimeDelta sampling_interval,
- TimeTicks now) {
- // Schedule the next sample at the next sampling_interval-aligned time in
- // the future that's sufficiently far enough from the current sample. In the
- // general case this will be one sampling_interval from the current
- // sample. In cases where sample tasks were unable to be executed, such as
- // during system suspend or bad system-wide jank, we may have missed some
- // samples. The right thing to do for those cases is to skip the missed
- // samples since the rest of the systems also wasn't executing.
- // Ensure that the next sample time is at least half a sampling interval
- // away. This causes the second sample after resume to be taken between 0.5
- // and 1.5 samples after the first, or 1 sample interval on average. The delay
- // also serves to provide a grace period in the normal sampling case where the
- // current sample may be taken slightly later than its scheduled time.
- const TimeTicks earliest_next_sample_time = now + sampling_interval / 2;
- const TimeDelta minimum_time_delta_to_next_sample =
- earliest_next_sample_time - scheduled_current_sample_time;
- // The minimum number of sampling intervals required to get from the scheduled
- // current sample time to the earliest next sample time.
- const int64_t required_sampling_intervals = static_cast<int64_t>(
- std::ceil(minimum_time_delta_to_next_sample / sampling_interval));
- return scheduled_current_sample_time +
- required_sampling_intervals * sampling_interval;
- }
- } // namespace
- // StackSamplingProfiler::SamplingThread --------------------------------------
- class StackSamplingProfiler::SamplingThread : public Thread {
- public:
- class TestPeer {
- public:
- // Reset the existing sampler. This will unfortunately create the object
- // unnecessarily if it doesn't already exist but there's no way around that.
- static void Reset();
- // Disables inherent idle-shutdown behavior.
- static void DisableIdleShutdown();
- // Begins an idle shutdown as if the idle-timer had expired and wait for
- // it to execute. Since the timer would have only been started at a time
- // when the sampling thread actually was idle, this must be called only
- // when it is known that there are no active sampling threads. If
- // |simulate_intervening_add| is true then, when executed, the shutdown
- // task will believe that a new collection has been added since it was
- // posted.
- static void ShutdownAssumingIdle(bool simulate_intervening_add);
- private:
- // Calls the sampling threads ShutdownTask and then signals an event.
- static void ShutdownTaskAndSignalEvent(SamplingThread* sampler,
- int add_events,
- WaitableEvent* event);
- };
- struct CollectionContext {
- CollectionContext(PlatformThreadId thread_id,
- const SamplingParams& params,
- WaitableEvent* finished,
- std::unique_ptr<StackSampler> sampler,
- std::unique_ptr<ProfileBuilder> profile_builder)
- : collection_id(next_collection_id.GetNext()),
- thread_id(thread_id),
- params(params),
- finished(finished),
- profile_builder(std::move(profile_builder)),
- sampler(std::move(sampler)) {}
- ~CollectionContext() = default;
- // An identifier for this collection, used to uniquely identify the
- // collection to outside interests.
- const int collection_id;
- const PlatformThreadId thread_id; // Thread id of the sampled thread.
- const SamplingParams params; // Information about how to sample.
- const raw_ptr<WaitableEvent>
- finished; // Signaled when all sampling complete.
- // Receives the sampling data and builds a CallStackProfile.
- std::unique_ptr<ProfileBuilder> profile_builder;
- // Platform-specific module that does the actual sampling.
- std::unique_ptr<StackSampler> sampler;
- // The absolute time for the next sample.
- TimeTicks next_sample_time;
- // The time that a profile was started, for calculating the total duration.
- TimeTicks profile_start_time;
- // Counter that indicates the current sample position along the acquisition.
- int sample_count = 0;
- // Sequence number for generating new collection ids.
- static AtomicSequenceNumber next_collection_id;
- };
- // Gets the single instance of this class.
- static SamplingThread* GetInstance();
- SamplingThread(const SamplingThread&) = delete;
- SamplingThread& operator=(const SamplingThread&) = delete;
- // Adds a new CollectionContext to the thread. This can be called externally
- // from any thread. This returns a collection id that can later be used to
- // stop the sampling.
- int Add(std::unique_ptr<CollectionContext> collection);
- // Adds an auxiliary unwinder to be used for the collection, to handle
- // additional, non-native-code unwind scenarios.
- void AddAuxUnwinder(int collection_id, std::unique_ptr<Unwinder> unwinder);
- // Applies the metadata to already recorded samples in all collections.
- void ApplyMetadataToPastSamples(base::TimeTicks period_start,
- base::TimeTicks period_end,
- uint64_t name_hash,
- absl::optional<int64_t> key,
- int64_t value,
- absl::optional<PlatformThreadId> thread_id);
- // Removes an active collection based on its collection id, forcing it to run
- // its callback if any data has been collected. This can be called externally
- // from any thread.
- void Remove(int collection_id);
- private:
- friend struct DefaultSingletonTraits<SamplingThread>;
- // The different states in which the sampling-thread can be.
- enum ThreadExecutionState {
- // The thread is not running because it has never been started. It will be
- // started when a sampling request is received.
- NOT_STARTED,
- // The thread is running and processing tasks. This is the state when any
- // sampling requests are active and during the "idle" period afterward
- // before the thread is stopped.
- RUNNING,
- // Once all sampling requests have finished and the "idle" period has
- // expired, the thread will be set to this state and its shutdown
- // initiated. A call to Stop() must be made to ensure the previous thread
- // has completely exited before calling Start() and moving back to the
- // RUNNING state.
- EXITING,
- };
- SamplingThread();
- ~SamplingThread() override;
- // Get task runner that is usable from the outside.
- scoped_refptr<SingleThreadTaskRunner> GetOrCreateTaskRunnerForAdd();
- scoped_refptr<SingleThreadTaskRunner> GetTaskRunner(
- ThreadExecutionState* out_state);
- // Get task runner that is usable from the sampling thread itself.
- scoped_refptr<SingleThreadTaskRunner> GetTaskRunnerOnSamplingThread();
- // Finishes a collection. The collection's |finished| waitable event will be
- // signalled. The |collection| should already have been removed from
- // |active_collections_| by the caller, as this is needed to avoid flakiness
- // in unit tests.
- void FinishCollection(std::unique_ptr<CollectionContext> collection);
- // Check if the sampling thread is idle and begin a shutdown if it is.
- void ScheduleShutdownIfIdle();
- // These methods are tasks that get posted to the internal message queue.
- void AddCollectionTask(std::unique_ptr<CollectionContext> collection);
- void AddAuxUnwinderTask(int collection_id,
- std::unique_ptr<Unwinder> unwinder);
- void ApplyMetadataToPastSamplesTask(
- base::TimeTicks period_start,
- base::TimeTicks period_end,
- uint64_t name_hash,
- absl::optional<int64_t> key,
- int64_t value,
- absl::optional<PlatformThreadId> thread_id);
- void RemoveCollectionTask(int collection_id);
- void RecordSampleTask(int collection_id);
- void ShutdownTask(int add_events);
- // Thread:
- void CleanUp() override;
- // A stack-buffer used by the sampler for its work. This buffer is re-used
- // across multiple sampler objects since their execution is serialized on the
- // sampling thread.
- std::unique_ptr<StackBuffer> stack_buffer_;
- // A map of collection ids to collection contexts. Because this class is a
- // singleton that is never destroyed, context objects will never be destructed
- // except by explicit action. Thus, it's acceptable to pass unretained
- // pointers to these objects when posting tasks.
- std::map<int, std::unique_ptr<CollectionContext>> active_collections_;
- // State maintained about the current execution (or non-execution) of
- // the thread. This state must always be accessed while holding the
- // lock. A copy of the task-runner is maintained here for use by any
- // calling thread; this is necessary because Thread's accessor for it is
- // not itself thread-safe. The lock is also used to order calls to the
- // Thread API (Start, Stop, StopSoon, & DetachFromSequence) so that
- // multiple threads may make those calls.
- Lock thread_execution_state_lock_; // Protects all thread_execution_state_*
- ThreadExecutionState thread_execution_state_
- GUARDED_BY(thread_execution_state_lock_) = NOT_STARTED;
- scoped_refptr<SingleThreadTaskRunner> thread_execution_state_task_runner_
- GUARDED_BY(thread_execution_state_lock_);
- bool thread_execution_state_disable_idle_shutdown_for_testing_
- GUARDED_BY(thread_execution_state_lock_) = false;
- // A counter that notes adds of new collection requests. It is incremented
- // when changes occur so that delayed shutdown tasks are able to detect if
- // something new has happened while it was waiting. Like all "execution_state"
- // vars, this must be accessed while holding |thread_execution_state_lock_|.
- int thread_execution_state_add_events_
- GUARDED_BY(thread_execution_state_lock_) = 0;
- };
- // static
- void StackSamplingProfiler::SamplingThread::TestPeer::Reset() {
- SamplingThread* sampler = SamplingThread::GetInstance();
- ThreadExecutionState state;
- {
- AutoLock lock(sampler->thread_execution_state_lock_);
- state = sampler->thread_execution_state_;
- DCHECK(sampler->active_collections_.empty());
- }
- // Stop the thread and wait for it to exit. This has to be done through by
- // the thread itself because it has taken ownership of its own lifetime.
- if (state == RUNNING) {
- ShutdownAssumingIdle(false);
- state = EXITING;
- }
- // Make sure thread is cleaned up since state will be reset to NOT_STARTED.
- if (state == EXITING)
- sampler->Stop();
- // Reset internal variables to the just-initialized state.
- {
- AutoLock lock(sampler->thread_execution_state_lock_);
- sampler->thread_execution_state_ = NOT_STARTED;
- sampler->thread_execution_state_task_runner_ = nullptr;
- sampler->thread_execution_state_disable_idle_shutdown_for_testing_ = false;
- sampler->thread_execution_state_add_events_ = 0;
- }
- }
- // static
- void StackSamplingProfiler::SamplingThread::TestPeer::DisableIdleShutdown() {
- SamplingThread* sampler = SamplingThread::GetInstance();
- {
- AutoLock lock(sampler->thread_execution_state_lock_);
- sampler->thread_execution_state_disable_idle_shutdown_for_testing_ = true;
- }
- }
- // static
- void StackSamplingProfiler::SamplingThread::TestPeer::ShutdownAssumingIdle(
- bool simulate_intervening_add) {
- SamplingThread* sampler = SamplingThread::GetInstance();
- ThreadExecutionState state;
- scoped_refptr<SingleThreadTaskRunner> task_runner =
- sampler->GetTaskRunner(&state);
- DCHECK_EQ(RUNNING, state);
- DCHECK(task_runner);
- int add_events;
- {
- AutoLock lock(sampler->thread_execution_state_lock_);
- add_events = sampler->thread_execution_state_add_events_;
- if (simulate_intervening_add)
- ++sampler->thread_execution_state_add_events_;
- }
- WaitableEvent executed(WaitableEvent::ResetPolicy::MANUAL,
- WaitableEvent::InitialState::NOT_SIGNALED);
- // PostTaskAndReply won't work because thread and associated message-loop may
- // be shut down.
- task_runner->PostTask(
- FROM_HERE, BindOnce(&ShutdownTaskAndSignalEvent, Unretained(sampler),
- add_events, Unretained(&executed)));
- executed.Wait();
- }
- // static
- void StackSamplingProfiler::SamplingThread::TestPeer::
- ShutdownTaskAndSignalEvent(SamplingThread* sampler,
- int add_events,
- WaitableEvent* event) {
- sampler->ShutdownTask(add_events);
- event->Signal();
- }
- AtomicSequenceNumber StackSamplingProfiler::SamplingThread::CollectionContext::
- next_collection_id;
- StackSamplingProfiler::SamplingThread::SamplingThread()
- : Thread("StackSamplingProfiler") {}
- StackSamplingProfiler::SamplingThread::~SamplingThread() = default;
- StackSamplingProfiler::SamplingThread*
- StackSamplingProfiler::SamplingThread::GetInstance() {
- return Singleton<SamplingThread, LeakySingletonTraits<SamplingThread>>::get();
- }
- int StackSamplingProfiler::SamplingThread::Add(
- std::unique_ptr<CollectionContext> collection) {
- // This is not to be run on the sampling thread.
- int collection_id = collection->collection_id;
- scoped_refptr<SingleThreadTaskRunner> task_runner =
- GetOrCreateTaskRunnerForAdd();
- task_runner->PostTask(
- FROM_HERE, BindOnce(&SamplingThread::AddCollectionTask, Unretained(this),
- std::move(collection)));
- return collection_id;
- }
- void StackSamplingProfiler::SamplingThread::AddAuxUnwinder(
- int collection_id,
- std::unique_ptr<Unwinder> unwinder) {
- ThreadExecutionState state;
- scoped_refptr<SingleThreadTaskRunner> task_runner = GetTaskRunner(&state);
- if (state != RUNNING)
- return;
- DCHECK(task_runner);
- task_runner->PostTask(
- FROM_HERE, BindOnce(&SamplingThread::AddAuxUnwinderTask, Unretained(this),
- collection_id, std::move(unwinder)));
- }
- void StackSamplingProfiler::SamplingThread::ApplyMetadataToPastSamples(
- base::TimeTicks period_start,
- base::TimeTicks period_end,
- uint64_t name_hash,
- absl::optional<int64_t> key,
- int64_t value,
- absl::optional<PlatformThreadId> thread_id) {
- ThreadExecutionState state;
- scoped_refptr<SingleThreadTaskRunner> task_runner = GetTaskRunner(&state);
- if (state != RUNNING)
- return;
- DCHECK(task_runner);
- task_runner->PostTask(
- FROM_HERE, BindOnce(&SamplingThread::ApplyMetadataToPastSamplesTask,
- Unretained(this), period_start, period_end, name_hash,
- key, value, thread_id));
- }
- void StackSamplingProfiler::SamplingThread::Remove(int collection_id) {
- // This is not to be run on the sampling thread.
- ThreadExecutionState state;
- scoped_refptr<SingleThreadTaskRunner> task_runner = GetTaskRunner(&state);
- if (state != RUNNING)
- return;
- DCHECK(task_runner);
- // This can fail if the thread were to exit between acquisition of the task
- // runner above and the call below. In that case, however, everything has
- // stopped so there's no need to try to stop it.
- task_runner->PostTask(FROM_HERE,
- BindOnce(&SamplingThread::RemoveCollectionTask,
- Unretained(this), collection_id));
- }
- scoped_refptr<SingleThreadTaskRunner>
- StackSamplingProfiler::SamplingThread::GetOrCreateTaskRunnerForAdd() {
- AutoLock lock(thread_execution_state_lock_);
- // The increment of the "add events" count is why this method is to be only
- // called from "add".
- ++thread_execution_state_add_events_;
- if (thread_execution_state_ == RUNNING) {
- DCHECK(thread_execution_state_task_runner_);
- // This shouldn't be called from the sampling thread as it's inefficient.
- // Use GetTaskRunnerOnSamplingThread() instead.
- DCHECK_NE(GetThreadId(), PlatformThread::CurrentId());
- return thread_execution_state_task_runner_;
- }
- if (thread_execution_state_ == EXITING) {
- // StopSoon() was previously called to shut down the thread
- // asynchonously. Stop() must now be called before calling Start() again to
- // reset the thread state.
- //
- // We must allow blocking here to satisfy the Thread implementation, but in
- // practice the Stop() call is unlikely to actually block. For this to
- // happen a new profiling request would have to be made within the narrow
- // window between StopSoon() and thread exit following the end of the 60
- // second idle period.
- ScopedAllowThreadRecallForStackSamplingProfiler allow_thread_join;
- Stop();
- }
- DCHECK(!stack_buffer_);
- stack_buffer_ = StackSampler::CreateStackBuffer();
- // The thread is not running. Start it and get associated runner. The task-
- // runner has to be saved for future use because though it can be used from
- // any thread, it can be acquired via task_runner() only on the created
- // thread and the thread that creates it (i.e. this thread) for thread-safety
- // reasons which are alleviated in SamplingThread by gating access to it with
- // the |thread_execution_state_lock_|.
- Start();
- thread_execution_state_ = RUNNING;
- thread_execution_state_task_runner_ = Thread::task_runner();
- // Detach the sampling thread from the "sequence" (i.e. thread) that
- // started it so that it can be self-managed or stopped by another thread.
- DetachFromSequence();
- return thread_execution_state_task_runner_;
- }
- scoped_refptr<SingleThreadTaskRunner>
- StackSamplingProfiler::SamplingThread::GetTaskRunner(
- ThreadExecutionState* out_state) {
- AutoLock lock(thread_execution_state_lock_);
- if (out_state)
- *out_state = thread_execution_state_;
- if (thread_execution_state_ == RUNNING) {
- // This shouldn't be called from the sampling thread as it's inefficient.
- // Use GetTaskRunnerOnSamplingThread() instead.
- DCHECK_NE(GetThreadId(), PlatformThread::CurrentId());
- DCHECK(thread_execution_state_task_runner_);
- } else {
- DCHECK(!thread_execution_state_task_runner_);
- }
- return thread_execution_state_task_runner_;
- }
- scoped_refptr<SingleThreadTaskRunner>
- StackSamplingProfiler::SamplingThread::GetTaskRunnerOnSamplingThread() {
- // This should be called only from the sampling thread as it has limited
- // accessibility.
- DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
- return Thread::task_runner();
- }
- void StackSamplingProfiler::SamplingThread::FinishCollection(
- std::unique_ptr<CollectionContext> collection) {
- DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
- DCHECK_EQ(0u, active_collections_.count(collection->collection_id));
- TimeDelta profile_duration = TimeTicks::Now() -
- collection->profile_start_time +
- collection->params.sampling_interval;
- collection->profile_builder->OnProfileCompleted(
- profile_duration, collection->params.sampling_interval);
- // Signal that this collection is finished.
- WaitableEvent* collection_finished = collection->finished;
- // Ensure the collection is destroyed before signaling, so that it may
- // not outlive StackSamplingProfiler.
- collection.reset();
- collection_finished->Signal();
- ScheduleShutdownIfIdle();
- }
- void StackSamplingProfiler::SamplingThread::ScheduleShutdownIfIdle() {
- DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
- if (!active_collections_.empty())
- return;
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
- "StackSamplingProfiler::SamplingThread::ScheduleShutdownIfIdle");
- int add_events;
- {
- AutoLock lock(thread_execution_state_lock_);
- if (thread_execution_state_disable_idle_shutdown_for_testing_)
- return;
- add_events = thread_execution_state_add_events_;
- }
- GetTaskRunnerOnSamplingThread()->PostDelayedTask(
- FROM_HERE,
- BindOnce(&SamplingThread::ShutdownTask, Unretained(this), add_events),
- Seconds(60));
- }
- void StackSamplingProfiler::SamplingThread::AddAuxUnwinderTask(
- int collection_id,
- std::unique_ptr<Unwinder> unwinder) {
- DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
- auto loc = active_collections_.find(collection_id);
- if (loc == active_collections_.end())
- return;
- loc->second->sampler->AddAuxUnwinder(std::move(unwinder));
- }
- void StackSamplingProfiler::SamplingThread::ApplyMetadataToPastSamplesTask(
- base::TimeTicks period_start,
- base::TimeTicks period_end,
- uint64_t name_hash,
- absl::optional<int64_t> key,
- int64_t value,
- absl::optional<PlatformThreadId> thread_id) {
- DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
- MetadataRecorder::Item item(name_hash, key, thread_id, value);
- for (auto& id_collection_pair : active_collections_) {
- if (thread_id && id_collection_pair.second->thread_id != thread_id)
- continue;
- id_collection_pair.second->profile_builder->ApplyMetadataRetrospectively(
- period_start, period_end, item);
- }
- }
- void StackSamplingProfiler::SamplingThread::AddCollectionTask(
- std::unique_ptr<CollectionContext> collection) {
- DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
- const int collection_id = collection->collection_id;
- const TimeDelta initial_delay = collection->params.initial_delay;
- collection->sampler->Initialize();
- active_collections_.insert(
- std::make_pair(collection_id, std::move(collection)));
- GetTaskRunnerOnSamplingThread()->PostDelayedTask(
- FROM_HERE,
- BindOnce(&SamplingThread::RecordSampleTask, Unretained(this),
- collection_id),
- initial_delay);
- // Another increment of "add events" serves to invalidate any pending
- // shutdown tasks that may have been initiated between the Add() and this
- // task running.
- {
- AutoLock lock(thread_execution_state_lock_);
- ++thread_execution_state_add_events_;
- }
- }
- void StackSamplingProfiler::SamplingThread::RemoveCollectionTask(
- int collection_id) {
- DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
- auto found = active_collections_.find(collection_id);
- if (found == active_collections_.end())
- return;
- // Remove |collection| from |active_collections_|.
- std::unique_ptr<CollectionContext> collection = std::move(found->second);
- size_t count = active_collections_.erase(collection_id);
- DCHECK_EQ(1U, count);
- FinishCollection(std::move(collection));
- }
- void StackSamplingProfiler::SamplingThread::RecordSampleTask(
- int collection_id) {
- DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
- auto found = active_collections_.find(collection_id);
- // The task won't be found if it has been stopped.
- if (found == active_collections_.end())
- return;
- CollectionContext* collection = found->second.get();
- // If this is the first sample, the collection params need to be filled.
- if (collection->sample_count == 0) {
- collection->profile_start_time = TimeTicks::Now();
- collection->next_sample_time = TimeTicks::Now();
- }
- // Record a single sample.
- collection->sampler->RecordStackFrames(stack_buffer_.get(),
- collection->profile_builder.get(),
- collection->thread_id);
- // Schedule the next sample recording if there is one.
- if (++collection->sample_count < collection->params.samples_per_profile) {
- collection->next_sample_time = GetNextSampleTimeImpl(
- collection->next_sample_time, collection->params.sampling_interval,
- TimeTicks::Now());
- bool success = GetTaskRunnerOnSamplingThread()->PostDelayedTask(
- FROM_HERE,
- BindOnce(&SamplingThread::RecordSampleTask, Unretained(this),
- collection_id),
- std::max(collection->next_sample_time - TimeTicks::Now(), TimeDelta()));
- DCHECK(success);
- return;
- }
- // Take ownership of |collection| and remove it from the map.
- std::unique_ptr<CollectionContext> owned_collection =
- std::move(found->second);
- size_t count = active_collections_.erase(collection_id);
- DCHECK_EQ(1U, count);
- // All capturing has completed so finish the collection.
- FinishCollection(std::move(owned_collection));
- }
- void StackSamplingProfiler::SamplingThread::ShutdownTask(int add_events) {
- DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
- // Holding this lock ensures that any attempt to start another job will
- // get postponed until |thread_execution_state_| is updated, thus eliminating
- // the race in starting a new thread while the previous one is exiting.
- AutoLock lock(thread_execution_state_lock_);
- // If the current count of creation requests doesn't match the passed count
- // then other tasks have been created since this was posted. Abort shutdown.
- if (thread_execution_state_add_events_ != add_events)
- return;
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
- "StackSamplingProfiler::SamplingThread::ShutdownTask");
- // There can be no new AddCollectionTasks at this point because creating
- // those always increments "add events". There may be other requests, like
- // Remove, but it's okay to schedule the thread to stop once they've been
- // executed (i.e. "soon").
- DCHECK(active_collections_.empty());
- StopSoon();
- // StopSoon will have set the owning sequence (again) so it must be detached
- // (again) in order for Stop/Start to be called (again) should more work
- // come in. Holding the |thread_execution_state_lock_| ensures the necessary
- // happens-after with regard to this detach and future Thread API calls.
- DetachFromSequence();
- // Set the thread_state variable so the thread will be restarted when new
- // work comes in. Remove the |thread_execution_state_task_runner_| to avoid
- // confusion.
- thread_execution_state_ = EXITING;
- thread_execution_state_task_runner_ = nullptr;
- stack_buffer_.reset();
- }
- void StackSamplingProfiler::SamplingThread::CleanUp() {
- DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
- // There should be no collections remaining when the thread stops.
- DCHECK(active_collections_.empty());
- // Let the parent clean up.
- Thread::CleanUp();
- }
- // StackSamplingProfiler ------------------------------------------------------
- // static
- void StackSamplingProfiler::TestPeer::Reset() {
- SamplingThread::TestPeer::Reset();
- }
- // static
- bool StackSamplingProfiler::TestPeer::IsSamplingThreadRunning() {
- return SamplingThread::GetInstance()->IsRunning();
- }
- // static
- void StackSamplingProfiler::TestPeer::DisableIdleShutdown() {
- SamplingThread::TestPeer::DisableIdleShutdown();
- }
- // static
- void StackSamplingProfiler::TestPeer::PerformSamplingThreadIdleShutdown(
- bool simulate_intervening_start) {
- SamplingThread::TestPeer::ShutdownAssumingIdle(simulate_intervening_start);
- }
- // static
- TimeTicks StackSamplingProfiler::TestPeer::GetNextSampleTime(
- TimeTicks scheduled_current_sample_time,
- TimeDelta sampling_interval,
- TimeTicks now) {
- return GetNextSampleTimeImpl(scheduled_current_sample_time, sampling_interval,
- now);
- }
- // static
- // The profiler is currently supported for Windows x64, macOS, iOS 64-bit, and
- // Android ARM32.
- bool StackSamplingProfiler::IsSupportedForCurrentPlatform() {
- #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))
- #if BUILDFLAG(IS_WIN)
- // Do not start the profiler when Application Verifier is in use; running them
- // simultaneously can cause crashes and has no known use case.
- if (GetModuleHandleA(base::win::kApplicationVerifierDllName))
- return false;
- // Checks if Trend Micro DLLs are loaded in process, so we can disable the
- // profiler to avoid hitting their performance bug. See
- // https://crbug.com/1018291 and https://crbug.com/1113832.
- if (GetModuleHandleA("tmmon64.dll") || GetModuleHandleA("tmmonmgr64.dll"))
- return false;
- #endif
- return true;
- #else
- return false;
- #endif
- }
- StackSamplingProfiler::StackSamplingProfiler(
- SamplingProfilerThreadToken thread_token,
- const SamplingParams& params,
- std::unique_ptr<ProfileBuilder> profile_builder,
- UnwindersFactory core_unwinders_factory,
- RepeatingClosure record_sample_callback,
- StackSamplerTestDelegate* test_delegate)
- : StackSamplingProfiler(thread_token,
- params,
- std::move(profile_builder),
- std::unique_ptr<StackSampler>()) {
- sampler_ =
- StackSampler::Create(thread_token, profile_builder_->GetModuleCache(),
- std::move(core_unwinders_factory),
- std::move(record_sample_callback), test_delegate);
- }
- StackSamplingProfiler::StackSamplingProfiler(
- SamplingProfilerThreadToken thread_token,
- const SamplingParams& params,
- std::unique_ptr<ProfileBuilder> profile_builder,
- std::unique_ptr<StackSampler> sampler)
- : thread_token_(thread_token),
- params_(params),
- profile_builder_(std::move(profile_builder)),
- sampler_(std::move(sampler)),
- // The event starts "signaled" so code knows it's safe to start thread
- // and "manual" so that it can be waited in multiple places.
- profiling_inactive_(kResetPolicy, WaitableEvent::InitialState::SIGNALED),
- profiler_id_(kNullProfilerId) {
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
- "StackSamplingProfiler::StackSamplingProfiler");
- DCHECK(profile_builder_);
- }
- StackSamplingProfiler::~StackSamplingProfiler() {
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
- "StackSamplingProfiler::~StackSamplingProfiler");
- // Stop returns immediately but the shutdown runs asynchronously. There is a
- // non-zero probability that one more sample will be taken after this call
- // returns.
- Stop();
- // The behavior of sampling a thread that has exited is undefined and could
- // cause Bad Things(tm) to occur. The safety model provided by this class is
- // that an instance of this object is expected to live at least as long as
- // the thread it is sampling. However, because the sampling is performed
- // asynchronously by the SamplingThread, there is no way to guarantee this
- // is true without waiting for it to signal that it has finished.
- //
- // The wait time should, at most, be only as long as it takes to collect one
- // sample (~200us) or none at all if sampling has already completed.
- ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
- profiling_inactive_.Wait();
- }
- void StackSamplingProfiler::Start() {
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
- "StackSamplingProfiler::Start");
- // Multiple calls to Start() for a single StackSamplingProfiler object is not
- // allowed. If profile_builder_ is nullptr, then Start() has been called
- // already.
- DCHECK(profile_builder_);
- // |sampler_| will be null if sampling isn't supported on the current
- // platform.
- if (!sampler_)
- return;
- // The IsSignaled() check below requires that the WaitableEvent be manually
- // reset, to avoid signaling the event in IsSignaled() itself.
- static_assert(kResetPolicy == WaitableEvent::ResetPolicy::MANUAL,
- "The reset policy must be set to MANUAL");
- // If a previous profiling phase is still winding down, wait for it to
- // complete. We can't use task posting for this coordination because the
- // thread owning the profiler may not have a message loop.
- if (!profiling_inactive_.IsSignaled())
- profiling_inactive_.Wait();
- profiling_inactive_.Reset();
- DCHECK_EQ(kNullProfilerId, profiler_id_);
- profiler_id_ = SamplingThread::GetInstance()->Add(
- std::make_unique<SamplingThread::CollectionContext>(
- thread_token_.id, params_, &profiling_inactive_, std::move(sampler_),
- std::move(profile_builder_)));
- DCHECK_NE(kNullProfilerId, profiler_id_);
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
- "StackSamplingProfiler::Started", "profiler_id", profiler_id_);
- }
- void StackSamplingProfiler::Stop() {
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
- "StackSamplingProfiler::Stop", "profiler_id", profiler_id_);
- SamplingThread::GetInstance()->Remove(profiler_id_);
- profiler_id_ = kNullProfilerId;
- }
- void StackSamplingProfiler::AddAuxUnwinder(std::unique_ptr<Unwinder> unwinder) {
- if (profiler_id_ == kNullProfilerId) {
- // We haven't started sampling, and so we can add |unwinder| to the sampler
- // directly
- if (sampler_)
- sampler_->AddAuxUnwinder(std::move(unwinder));
- return;
- }
- SamplingThread::GetInstance()->AddAuxUnwinder(profiler_id_,
- std::move(unwinder));
- }
- // static
- void StackSamplingProfiler::ApplyMetadataToPastSamples(
- base::TimeTicks period_start,
- base::TimeTicks period_end,
- uint64_t name_hash,
- absl::optional<int64_t> key,
- int64_t value,
- absl::optional<PlatformThreadId> thread_id) {
- SamplingThread::GetInstance()->ApplyMetadataToPastSamples(
- period_start, period_end, name_hash, key, value, thread_id);
- }
- } // namespace base
|