mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
312 lines
10 KiB
C++
312 lines
10 KiB
C++
// 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 "base/bind.h"
|
|
#include "base/callback.h"
|
|
#include "base/memory/singleton.h"
|
|
#include "base/profiler/native_stack_sampler.h"
|
|
#include "base/synchronization/lock.h"
|
|
#include "base/timer/elapsed_timer.h"
|
|
|
|
namespace base {
|
|
|
|
// DefaultProfileProcessor ----------------------------------------------------
|
|
|
|
namespace {
|
|
|
|
// Singleton class responsible for providing the default processing for profiles
|
|
// (i.e. for profiles generated by profilers without their own completed
|
|
// callback).
|
|
class DefaultProfileProcessor {
|
|
public:
|
|
using CompletedCallback = StackSamplingProfiler::CompletedCallback;
|
|
|
|
~DefaultProfileProcessor();
|
|
|
|
static DefaultProfileProcessor* GetInstance();
|
|
|
|
// Sets the callback to use for processing profiles captured without a
|
|
// per-profiler completed callback. Pending completed profiles are stored in
|
|
// this object until a non-null callback is provided here. This function is
|
|
// thread-safe.
|
|
void SetCompletedCallback(CompletedCallback callback);
|
|
|
|
// Processes |profiles|. This function is thread safe.
|
|
void ProcessProfiles(
|
|
const StackSamplingProfiler::CallStackProfiles& profiles);
|
|
|
|
private:
|
|
friend struct DefaultSingletonTraits<DefaultProfileProcessor>;
|
|
|
|
DefaultProfileProcessor();
|
|
|
|
// Copies the pending profiles from |profiles_| into |profiles|, and clears
|
|
// |profiles_|. This function may be called on any thread.
|
|
void GetAndClearPendingProfiles(
|
|
StackSamplingProfiler::CallStackProfiles* profiles);
|
|
|
|
// Gets the current completed callback, with proper locking.
|
|
CompletedCallback GetCompletedCallback() const;
|
|
|
|
mutable Lock callback_lock_;
|
|
CompletedCallback default_completed_callback_;
|
|
|
|
Lock profiles_lock_;
|
|
StackSamplingProfiler::CallStackProfiles profiles_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(DefaultProfileProcessor);
|
|
};
|
|
|
|
DefaultProfileProcessor::~DefaultProfileProcessor() {}
|
|
|
|
// static
|
|
DefaultProfileProcessor* DefaultProfileProcessor::GetInstance() {
|
|
return Singleton<DefaultProfileProcessor>::get();
|
|
}
|
|
|
|
void DefaultProfileProcessor::SetCompletedCallback(CompletedCallback callback) {
|
|
{
|
|
AutoLock scoped_lock(callback_lock_);
|
|
default_completed_callback_ = callback;
|
|
}
|
|
|
|
if (!callback.is_null()) {
|
|
// Provide any pending profiles to the callback immediately.
|
|
StackSamplingProfiler::CallStackProfiles profiles;
|
|
GetAndClearPendingProfiles(&profiles);
|
|
if (!profiles.empty())
|
|
callback.Run(profiles);
|
|
}
|
|
}
|
|
|
|
void DefaultProfileProcessor::ProcessProfiles(
|
|
const StackSamplingProfiler::CallStackProfiles& profiles) {
|
|
CompletedCallback callback = GetCompletedCallback();
|
|
|
|
// Store pending profiles if we don't have a valid callback.
|
|
if (!callback.is_null()) {
|
|
callback.Run(profiles);
|
|
} else {
|
|
AutoLock scoped_lock(profiles_lock_);
|
|
profiles_.insert(profiles_.end(), profiles.begin(), profiles.end());
|
|
}
|
|
}
|
|
|
|
DefaultProfileProcessor::DefaultProfileProcessor() {}
|
|
|
|
void DefaultProfileProcessor::GetAndClearPendingProfiles(
|
|
StackSamplingProfiler::CallStackProfiles* profiles) {
|
|
profiles->clear();
|
|
|
|
AutoLock scoped_lock(profiles_lock_);
|
|
profiles_.swap(*profiles);
|
|
}
|
|
|
|
DefaultProfileProcessor::CompletedCallback
|
|
DefaultProfileProcessor::GetCompletedCallback() const {
|
|
AutoLock scoped_lock(callback_lock_);
|
|
return default_completed_callback_;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// StackSamplingProfiler::Module ----------------------------------------------
|
|
|
|
StackSamplingProfiler::Module::Module() : base_address(nullptr) {}
|
|
StackSamplingProfiler::Module::Module(const void* base_address,
|
|
const std::string& id,
|
|
const FilePath& filename)
|
|
: base_address(base_address), id(id), filename(filename) {}
|
|
|
|
StackSamplingProfiler::Module::~Module() {}
|
|
|
|
// StackSamplingProfiler::Frame -----------------------------------------------
|
|
|
|
StackSamplingProfiler::Frame::Frame(const void* instruction_pointer,
|
|
size_t module_index)
|
|
: instruction_pointer(instruction_pointer),
|
|
module_index(module_index) {}
|
|
|
|
StackSamplingProfiler::Frame::~Frame() {}
|
|
|
|
// StackSamplingProfiler::CallStackProfile ------------------------------------
|
|
|
|
StackSamplingProfiler::CallStackProfile::CallStackProfile()
|
|
: preserve_sample_ordering(false), user_data(0) {}
|
|
|
|
StackSamplingProfiler::CallStackProfile::~CallStackProfile() {}
|
|
|
|
// StackSamplingProfiler::SamplingThread --------------------------------------
|
|
|
|
StackSamplingProfiler::SamplingThread::SamplingThread(
|
|
scoped_ptr<NativeStackSampler> native_sampler,
|
|
const SamplingParams& params,
|
|
CompletedCallback completed_callback)
|
|
: native_sampler_(native_sampler.Pass()),
|
|
params_(params),
|
|
stop_event_(false, false),
|
|
completed_callback_(completed_callback) {
|
|
}
|
|
|
|
StackSamplingProfiler::SamplingThread::~SamplingThread() {}
|
|
|
|
void StackSamplingProfiler::SamplingThread::ThreadMain() {
|
|
PlatformThread::SetName("Chrome_SamplingProfilerThread");
|
|
|
|
CallStackProfiles profiles;
|
|
CollectProfiles(&profiles);
|
|
completed_callback_.Run(profiles);
|
|
}
|
|
|
|
// Depending on how long the sampling takes and the length of the sampling
|
|
// interval, a burst of samples could take arbitrarily longer than
|
|
// samples_per_burst * sampling_interval. In this case, we (somewhat
|
|
// arbitrarily) honor the number of samples requested rather than strictly
|
|
// adhering to the sampling intervals. Once we have established users for the
|
|
// StackSamplingProfiler and the collected data to judge, we may go the other
|
|
// way or make this behavior configurable.
|
|
bool StackSamplingProfiler::SamplingThread::CollectProfile(
|
|
CallStackProfile* profile,
|
|
TimeDelta* elapsed_time) {
|
|
ElapsedTimer profile_timer;
|
|
CallStackProfile current_profile;
|
|
native_sampler_->ProfileRecordingStarting(¤t_profile.modules);
|
|
current_profile.sampling_period = params_.sampling_interval;
|
|
bool burst_completed = true;
|
|
TimeDelta previous_elapsed_sample_time;
|
|
for (int i = 0; i < params_.samples_per_burst; ++i) {
|
|
if (i != 0) {
|
|
// Always wait, even if for 0 seconds, so we can observe a signal on
|
|
// stop_event_.
|
|
if (stop_event_.TimedWait(
|
|
std::max(params_.sampling_interval - previous_elapsed_sample_time,
|
|
TimeDelta()))) {
|
|
burst_completed = false;
|
|
break;
|
|
}
|
|
}
|
|
ElapsedTimer sample_timer;
|
|
current_profile.samples.push_back(Sample());
|
|
native_sampler_->RecordStackSample(¤t_profile.samples.back());
|
|
previous_elapsed_sample_time = sample_timer.Elapsed();
|
|
}
|
|
|
|
*elapsed_time = profile_timer.Elapsed();
|
|
current_profile.profile_duration = *elapsed_time;
|
|
current_profile.preserve_sample_ordering = params_.preserve_sample_ordering;
|
|
current_profile.user_data = params_.user_data;
|
|
native_sampler_->ProfileRecordingStopped();
|
|
|
|
if (burst_completed)
|
|
*profile = current_profile;
|
|
|
|
return burst_completed;
|
|
}
|
|
|
|
// In an analogous manner to CollectProfile() and samples exceeding the expected
|
|
// total sampling time, bursts may also exceed the burst_interval. We adopt the
|
|
// same wait-and-see approach here.
|
|
void StackSamplingProfiler::SamplingThread::CollectProfiles(
|
|
CallStackProfiles* profiles) {
|
|
if (stop_event_.TimedWait(params_.initial_delay))
|
|
return;
|
|
|
|
TimeDelta previous_elapsed_profile_time;
|
|
for (int i = 0; i < params_.bursts; ++i) {
|
|
if (i != 0) {
|
|
// Always wait, even if for 0 seconds, so we can observe a signal on
|
|
// stop_event_.
|
|
if (stop_event_.TimedWait(
|
|
std::max(params_.burst_interval - previous_elapsed_profile_time,
|
|
TimeDelta())))
|
|
return;
|
|
}
|
|
|
|
CallStackProfile profile;
|
|
if (!CollectProfile(&profile, &previous_elapsed_profile_time))
|
|
return;
|
|
profiles->push_back(profile);
|
|
}
|
|
}
|
|
|
|
void StackSamplingProfiler::SamplingThread::Stop() {
|
|
stop_event_.Signal();
|
|
}
|
|
|
|
// StackSamplingProfiler ------------------------------------------------------
|
|
|
|
StackSamplingProfiler::SamplingParams::SamplingParams()
|
|
: initial_delay(TimeDelta::FromMilliseconds(0)),
|
|
bursts(1),
|
|
burst_interval(TimeDelta::FromMilliseconds(10000)),
|
|
samples_per_burst(300),
|
|
sampling_interval(TimeDelta::FromMilliseconds(100)),
|
|
preserve_sample_ordering(false),
|
|
user_data(0) {
|
|
}
|
|
|
|
StackSamplingProfiler::StackSamplingProfiler(PlatformThreadId thread_id,
|
|
const SamplingParams& params)
|
|
: thread_id_(thread_id), params_(params) {}
|
|
|
|
StackSamplingProfiler::StackSamplingProfiler(PlatformThreadId thread_id,
|
|
const SamplingParams& params,
|
|
CompletedCallback callback)
|
|
: thread_id_(thread_id), params_(params), completed_callback_(callback) {}
|
|
|
|
StackSamplingProfiler::~StackSamplingProfiler() {
|
|
Stop();
|
|
if (!sampling_thread_handle_.is_null())
|
|
PlatformThread::Join(sampling_thread_handle_);
|
|
}
|
|
|
|
void StackSamplingProfiler::Start() {
|
|
scoped_ptr<NativeStackSampler> native_sampler =
|
|
NativeStackSampler::Create(thread_id_);
|
|
if (!native_sampler)
|
|
return;
|
|
|
|
CompletedCallback callback =
|
|
!completed_callback_.is_null() ? completed_callback_ :
|
|
Bind(&DefaultProfileProcessor::ProcessProfiles,
|
|
Unretained(DefaultProfileProcessor::GetInstance()));
|
|
sampling_thread_.reset(
|
|
new SamplingThread(native_sampler.Pass(), params_, callback));
|
|
if (!PlatformThread::Create(0, sampling_thread_.get(),
|
|
&sampling_thread_handle_))
|
|
sampling_thread_.reset();
|
|
}
|
|
|
|
void StackSamplingProfiler::Stop() {
|
|
if (sampling_thread_)
|
|
sampling_thread_->Stop();
|
|
}
|
|
|
|
// static
|
|
void StackSamplingProfiler::SetDefaultCompletedCallback(
|
|
CompletedCallback callback) {
|
|
DefaultProfileProcessor::GetInstance()->SetCompletedCallback(callback);
|
|
}
|
|
|
|
// StackSamplingProfiler::Frame global functions ------------------------------
|
|
|
|
bool operator==(const StackSamplingProfiler::Frame &a,
|
|
const StackSamplingProfiler::Frame &b) {
|
|
return a.instruction_pointer == b.instruction_pointer &&
|
|
a.module_index == b.module_index;
|
|
}
|
|
|
|
bool operator<(const StackSamplingProfiler::Frame &a,
|
|
const StackSamplingProfiler::Frame &b) {
|
|
return (a.module_index < b.module_index) ||
|
|
(a.module_index == b.module_index &&
|
|
a.instruction_pointer < b.instruction_pointer);
|
|
}
|
|
|
|
} // namespace base
|