Commit ae2a08ac authored by wittman's avatar wittman Committed by Commit bot

Enable startup profiling by Win x64 stack sampling profiler

Collects stack samples at 10 Hz for 30 seconds at startup. Uploading of
samples to UMA is controlled by a Finch experiment.

This change moves to a callback-based mechanism for communicating
completed profiles to the metrics provider to avoid profiles lingering
in memory when reporting is disabled.

BUG=464929

Review URL: https://codereview.chromium.org/1029653002

Cr-Commit-Position: refs/heads/master@{#324175}
parent c438c184
......@@ -15,61 +15,102 @@
namespace base {
// PendingProfiles ------------------------------------------------------------
// DefaultProfileProcessor ----------------------------------------------------
namespace {
// Thread-safe singleton class that stores collected call stack profiles waiting
// to be processed.
class PendingProfiles {
// 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:
~PendingProfiles();
using CompletedCallback = StackSamplingProfiler::CompletedCallback;
static PendingProfiles* GetInstance();
~DefaultProfileProcessor();
// Appends |profiles| to |profiles_|. This function may be called on any
// thread.
void AppendProfiles(
const std::vector<StackSamplingProfiler::CallStackProfile>& profiles);
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(
std::vector<StackSamplingProfiler::CallStackProfile>* profiles);
StackSamplingProfiler::CallStackProfiles* profiles);
private:
friend struct DefaultSingletonTraits<PendingProfiles>;
// Gets the current completed callback, with proper locking.
CompletedCallback GetCompletedCallback() const;
PendingProfiles();
mutable Lock callback_lock_;
CompletedCallback default_completed_callback_;
Lock profiles_lock_;
std::vector<StackSamplingProfiler::CallStackProfile> profiles_;
StackSamplingProfiler::CallStackProfiles profiles_;
DISALLOW_COPY_AND_ASSIGN(PendingProfiles);
DISALLOW_COPY_AND_ASSIGN(DefaultProfileProcessor);
};
PendingProfiles::PendingProfiles() {}
PendingProfiles::~PendingProfiles() {}
DefaultProfileProcessor::~DefaultProfileProcessor() {}
// static
PendingProfiles* PendingProfiles::GetInstance() {
return Singleton<PendingProfiles>::get();
DefaultProfileProcessor* DefaultProfileProcessor::GetInstance() {
return Singleton<DefaultProfileProcessor>::get();
}
void PendingProfiles::AppendProfiles(
const std::vector<StackSamplingProfiler::CallStackProfile>& profiles) {
AutoLock scoped_lock(profiles_lock_);
profiles_.insert(profiles_.end(), profiles.begin(), profiles.end());
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());
}
}
void PendingProfiles::GetAndClearPendingProfiles(
std::vector<StackSamplingProfiler::CallStackProfile>* profiles) {
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
......@@ -209,7 +250,16 @@ StackSamplingProfiler::StackSamplingProfiler(PlatformThreadId thread_id,
const SamplingParams& params)
: thread_id_(thread_id), params_(params) {}
StackSamplingProfiler::~StackSamplingProfiler() {}
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 =
......@@ -217,14 +267,14 @@ void StackSamplingProfiler::Start() {
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_,
(custom_completed_callback_.is_null() ?
Bind(&PendingProfiles::AppendProfiles,
Unretained(PendingProfiles::GetInstance())) :
custom_completed_callback_)));
if (!PlatformThread::CreateNonJoinable(0, sampling_thread_.get()))
new SamplingThread(native_sampler.Pass(), params_, callback));
if (!PlatformThread::Create(0, sampling_thread_.get(),
&sampling_thread_handle_))
sampling_thread_.reset();
}
......@@ -234,8 +284,9 @@ void StackSamplingProfiler::Stop() {
}
// static
void StackSamplingProfiler::GetPendingProfiles(CallStackProfiles* profiles) {
PendingProfiles::GetInstance()->GetAndClearPendingProfiles(profiles);
void StackSamplingProfiler::SetDefaultCompletedCallback(
CompletedCallback callback) {
DefaultProfileProcessor::GetInstance()->SetCompletedCallback(callback);
}
// StackSamplingProfiler::Frame global functions ------------------------------
......
......@@ -34,11 +34,12 @@ class NativeStackSampler;
// base::StackSamplingProfiler profiler(base::PlatformThread::CurrentId()),
// params);
//
// // To process the call stack profiles within Chrome rather than via UMA,
// // set a custom completed callback:
// // Or, to process the profiles within Chrome rather than via UMA, use a
// // custom completed callback:
// base::StackStackSamplingProfiler::CompletedCallback
// thread_safe_callback = ...;
// profiler.SetCustomCompletedCallback(thread_safe_callback);
// base::StackSamplingProfiler profiler(base::PlatformThread::CurrentId()),
// params, thread_safe_callback);
//
// profiler.Start();
// // ... work being done on the target thread here ...
......@@ -49,13 +50,13 @@ class NativeStackSampler;
// altered as desired.
//
// When all call stack profiles are complete or the profiler is stopped, if the
// custom completed callback was set it is called from the profiler thread with
// the completed profiles. A profile is considered complete if all requested
// samples were recorded for the profile (i.e. it was not stopped
// prematurely). If no callback was set, the completed profiles are stored
// internally and retrieved for UMA through GetPendingProfiles().
// GetPendingProfiles() should never be called by other code; to retrieve
// profiles for in-process processing, set a completed callback.
// custom completed callback was set it is called from a thread created by the
// profiler with the completed profiles. A profile is considered complete if all
// requested samples were recorded for the profile (i.e. it was not stopped
// prematurely). If no callback was set, the default completed callback will be
// called with the profiles. It is expected that the the default completed
// callback is set by the metrics system to allow profiles to be provided via
// UMA.
//
// The results of the profiling are passed to the completed callback and consist
// of a vector of CallStackProfiles. Each CallStackProfile corresponds to a
......@@ -164,8 +165,14 @@ class BASE_EXPORT StackSamplingProfiler {
// thread-safe callback implementation.
using CompletedCallback = Callback<void(const CallStackProfiles&)>;
// Creates a profiler that sends completed profiles to the default completed
// callback.
StackSamplingProfiler(PlatformThreadId thread_id,
const SamplingParams& params);
// Creates a profiler that sends completed profiles to |completed_callback|.
StackSamplingProfiler(PlatformThreadId thread_id,
const SamplingParams& params,
CompletedCallback callback);
~StackSamplingProfiler();
// Initializes the profiler and starts sampling.
......@@ -176,19 +183,14 @@ class BASE_EXPORT StackSamplingProfiler {
// specified in the SamplingParams are completed.
void Stop();
// Moves all pending call stack profiles from internal storage to
// |profiles|. This function is thread safe.
// Sets a callback to process profiles collected by profiler instances without
// a completed callback. Profiles are queued internally until a non-null
// callback is provided to this function,
//
// ***This is intended for use only by UMA.*** Callers who want to process the
// collected profiles should use SetCustomCompletedCallback.
static void GetPendingProfiles(CallStackProfiles* call_stack_profiles);
// By default, collected call stack profiles are stored internally and can be
// retrieved by GetPendingProfiles. If a callback is provided via this
// function, however, it is called with the collected profiles instead.
void set_custom_completed_callback(CompletedCallback callback) {
custom_completed_callback_ = callback;
}
// The callback is typically called on a thread created by the profiler. If
// completed profiles are queued when set, however, it will also be called
// immediately on the calling thread.
static void SetDefaultCompletedCallback(CompletedCallback callback);
private:
// SamplingThread is a separate thread used to suspend and sample stacks from
......@@ -226,7 +228,7 @@ class BASE_EXPORT StackSamplingProfiler {
// terminate before all the samples specified in |params_| are collected.
WaitableEvent stop_event_;
CompletedCallback completed_callback_;
const CompletedCallback completed_callback_;
DISALLOW_COPY_AND_ASSIGN(SamplingThread);
};
......@@ -237,8 +239,9 @@ class BASE_EXPORT StackSamplingProfiler {
const SamplingParams params_;
scoped_ptr<SamplingThread> sampling_thread_;
PlatformThreadHandle sampling_thread_handle_;
CompletedCallback custom_completed_callback_;
const CompletedCallback completed_callback_;
DISALLOW_COPY_AND_ASSIGN(StackSamplingProfiler);
};
......
......@@ -14,10 +14,12 @@
namespace base {
using SamplingParams = StackSamplingProfiler::SamplingParams;
using Frame = StackSamplingProfiler::Frame;
using Module = StackSamplingProfiler::Module;
using Sample = StackSamplingProfiler::Sample;
using CallStackProfile = StackSamplingProfiler::CallStackProfile;
using CallStackProfiles = StackSamplingProfiler::CallStackProfiles;
namespace {
......@@ -85,45 +87,99 @@ NOINLINE void TargetThread::SignalAndWaitUntilSignaled(
ALLOW_UNUSED_LOCAL(x);
}
// Called on the profiler thread when complete, to collect profiles.
void SaveProfiles(CallStackProfiles* profiles,
const CallStackProfiles& pending_profiles) {
*profiles = pending_profiles;
}
// Called on the profiler thread when complete. Collects profiles produced by
// the profiler, and signals an event to allow the main thread to know that that
// the profiler is done.
void SaveProfilesAndSignalEvent(
std::vector<CallStackProfile>* profiles,
WaitableEvent* event,
const std::vector<CallStackProfile>& pending_profiles) {
void SaveProfilesAndSignalEvent(CallStackProfiles* profiles,
WaitableEvent* event,
const CallStackProfiles& pending_profiles) {
*profiles = pending_profiles;
event->Signal();
}
// Captures call stack profiles as specified by |params| on the TargetThread,
// and returns them in |profiles|. Waits up to |profiler_wait_time| for the
// profiler to complete.
void CaptureProfiles(const StackSamplingProfiler::SamplingParams& params,
std::vector<CallStackProfile>* profiles,
TimeDelta profiler_wait_time) {
// Executes the function with the target thread running and executing within
// SignalAndWaitUntilSignaled(). Performs all necessary target thread startup
// and shutdown work before and afterward.
template <class Function>
void WithTargetThread(Function function) {
TargetThread target_thread;
PlatformThreadHandle target_thread_handle;
EXPECT_TRUE(PlatformThread::Create(0, &target_thread, &target_thread_handle));
target_thread.WaitForThreadStart();
WaitableEvent sampling_thread_completed(true, false);
profiles->clear();
StackSamplingProfiler profiler(target_thread.id(), params);
profiler.set_custom_completed_callback(
Bind(&SaveProfilesAndSignalEvent, Unretained(profiles),
Unretained(&sampling_thread_completed)));
profiler.Start();
sampling_thread_completed.TimedWait(profiler_wait_time);
profiler.Stop();
sampling_thread_completed.Wait();
function(target_thread.id());
target_thread.SignalThreadToFinish();
PlatformThread::Join(target_thread_handle);
}
// Captures profiles as specified by |params| on the TargetThread, and returns
// them in |profiles|. Waits up to |profiler_wait_time| for the profiler to
// complete.
void CaptureProfilesWithObjectCallback(const SamplingParams& params,
CallStackProfiles* profiles,
TimeDelta profiler_wait_time) {
profiles->clear();
WithTargetThread([&params, profiles, profiler_wait_time](
PlatformThreadId target_thread_id) {
WaitableEvent sampling_thread_completed(true, false);
const StackSamplingProfiler::CompletedCallback callback =
Bind(&SaveProfilesAndSignalEvent, Unretained(profiles),
Unretained(&sampling_thread_completed));
StackSamplingProfiler profiler(target_thread_id, params, callback);
profiler.Start();
sampling_thread_completed.TimedWait(profiler_wait_time);
profiler.Stop();
sampling_thread_completed.Wait();
});
}
// Captures profiles as specified by |params| on the TargetThread, and returns
// them in |profiles|. Uses the default callback rather than a per-object
// callback.
void CaptureProfilesWithDefaultCallback(const SamplingParams& params,
CallStackProfiles* profiles) {
profiles->clear();
WithTargetThread([&params, profiles](PlatformThreadId target_thread_id) {
WaitableEvent sampling_thread_completed(false, false);
StackSamplingProfiler::SetDefaultCompletedCallback(
Bind(&SaveProfilesAndSignalEvent, Unretained(profiles),
Unretained(&sampling_thread_completed)));
StackSamplingProfiler profiler(target_thread_id, params);
profiler.Start();
sampling_thread_completed.Wait();
StackSamplingProfiler::SetDefaultCompletedCallback(
StackSamplingProfiler::CompletedCallback());
});
}
// Runs the profiler with |params| on the TargetThread, with no default or
// per-object callback.
void RunProfilerWithNoCallback(const SamplingParams& params,
TimeDelta profiler_wait_time) {
WithTargetThread(
[&params, profiler_wait_time](PlatformThreadId target_thread_id) {
StackSamplingProfiler profiler(target_thread_id, params);
profiler.Start();
// Since we don't specify a callback, we don't have a synchronization
// mechanism with the sampling thread. Just sleep instead.
PlatformThread::Sleep(profiler_wait_time);
profiler.Stop();
});
}
// If this executable was linked with /INCREMENTAL (the default for non-official
// debug and release builds on Windows), function addresses do not correspond to
// function code itself, but instead to instructions in the Incremental Link
......@@ -195,12 +251,12 @@ TimeDelta AVeryLongTimeDelta() { return TimeDelta::FromDays(1); }
#define MAYBE_Basic DISABLED_Basic
#endif
TEST(StackSamplingProfilerTest, MAYBE_Basic) {
StackSamplingProfiler::SamplingParams params;
SamplingParams params;
params.sampling_interval = TimeDelta::FromMilliseconds(0);
params.samples_per_burst = 1;
std::vector<CallStackProfile> profiles;
CaptureProfiles(params, &profiles, AVeryLongTimeDelta());
CaptureProfilesWithObjectCallback(params, &profiles, AVeryLongTimeDelta());
// Check that the profile and samples sizes are correct, and the module
// indices are in range.
......@@ -244,14 +300,14 @@ TEST(StackSamplingProfilerTest, MAYBE_Basic) {
#define MAYBE_MultipleProfilesAndSamples DISABLED_MultipleProfilesAndSamples
#endif
TEST(StackSamplingProfilerTest, MAYBE_MultipleProfilesAndSamples) {
StackSamplingProfiler::SamplingParams params;
SamplingParams params;
params.burst_interval = params.sampling_interval =
TimeDelta::FromMilliseconds(0);
params.bursts = 2;
params.samples_per_burst = 3;
std::vector<CallStackProfile> profiles;
CaptureProfiles(params, &profiles, AVeryLongTimeDelta());
CaptureProfilesWithObjectCallback(params, &profiles, AVeryLongTimeDelta());
ASSERT_EQ(2u, profiles.size());
EXPECT_EQ(3u, profiles[0].samples.size());
......@@ -266,11 +322,12 @@ TEST(StackSamplingProfilerTest, MAYBE_MultipleProfilesAndSamples) {
#define MAYBE_StopDuringInitialDelay DISABLED_StopDuringInitialDelay
#endif
TEST(StackSamplingProfilerTest, MAYBE_StopDuringInitialDelay) {
StackSamplingProfiler::SamplingParams params;
SamplingParams params;
params.initial_delay = TimeDelta::FromSeconds(60);
std::vector<CallStackProfile> profiles;
CaptureProfiles(params, &profiles, TimeDelta::FromMilliseconds(0));
CaptureProfilesWithObjectCallback(params, &profiles,
TimeDelta::FromMilliseconds(0));
EXPECT_TRUE(profiles.empty());
}
......@@ -283,14 +340,15 @@ TEST(StackSamplingProfilerTest, MAYBE_StopDuringInitialDelay) {
#define MAYBE_StopDuringInterBurstInterval DISABLED_StopDuringInterBurstInterval
#endif
TEST(StackSamplingProfilerTest, MAYBE_StopDuringInterBurstInterval) {
StackSamplingProfiler::SamplingParams params;
SamplingParams params;
params.sampling_interval = TimeDelta::FromMilliseconds(0);
params.burst_interval = TimeDelta::FromSeconds(60);
params.bursts = 2;
params.samples_per_burst = 1;
std::vector<CallStackProfile> profiles;
CaptureProfiles(params, &profiles, TimeDelta::FromMilliseconds(50));
CaptureProfilesWithObjectCallback(params, &profiles,
TimeDelta::FromMilliseconds(50));
ASSERT_EQ(1u, profiles.size());
EXPECT_EQ(1u, profiles[0].samples.size());
......@@ -304,14 +362,80 @@ TEST(StackSamplingProfilerTest, MAYBE_StopDuringInterBurstInterval) {
DISABLED_StopDuringInterSampleInterval
#endif
TEST(StackSamplingProfilerTest, MAYBE_StopDuringInterSampleInterval) {
StackSamplingProfiler::SamplingParams params;
SamplingParams params;
params.sampling_interval = TimeDelta::FromSeconds(60);
params.samples_per_burst = 2;
std::vector<CallStackProfile> profiles;
CaptureProfiles(params, &profiles, TimeDelta::FromMilliseconds(50));
CaptureProfilesWithObjectCallback(params, &profiles,
TimeDelta::FromMilliseconds(50));
EXPECT_TRUE(profiles.empty());
}
// Checks that profiles are captured via the default completed callback.
#if defined(_WIN64)
#define MAYBE_DefaultCallback DefaultCallback
#else
#define MAYBE_DefaultCallback DISABLED_DefaultCallback
#endif
TEST(StackSamplingProfilerTest, MAYBE_DefaultCallback) {
SamplingParams params;
params.samples_per_burst = 1;
CallStackProfiles profiles;
CaptureProfilesWithDefaultCallback(params, &profiles);
EXPECT_EQ(1u, profiles.size());
EXPECT_EQ(1u, profiles[0].samples.size());
}
// Checks that profiles are queued until a default callback is set, then
// delivered.
#if defined(_WIN64)
#define MAYBE_ProfilesQueuedWithNoCallback ProfilesQueuedWithNoCallback
#else
#define MAYBE_ProfilesQueuedWithNoCallback DISABLED_ProfilesQueuedWithNoCallback
#endif
TEST(StackSamplingProfilerTest, MAYBE_ProfilesQueuedWithNoCallback) {
SamplingParams params;
params.samples_per_burst = 1;
RunProfilerWithNoCallback(params, TimeDelta::FromMilliseconds(50));
CallStackProfiles profiles;
// This should immediately call SaveProfiles on this thread.
StackSamplingProfiler::SetDefaultCompletedCallback(
Bind(&SaveProfiles, Unretained(&profiles)));
EXPECT_EQ(1u, profiles.size());
EXPECT_EQ(1u, profiles[0].samples.size());
StackSamplingProfiler::SetDefaultCompletedCallback(
StackSamplingProfiler::CompletedCallback());
}
// Checks that we can destroy the profiler while profiling.
#if defined(_WIN64)
#define MAYBE_DestroyProfilerWhileProfiling DestroyProfilerWhileProfiling
#else
#define MAYBE_DestroyProfilerWhileProfiling \
DISABLED_DestroyProfilerWhileProfiling
#endif
TEST(StackSamplingProfilerTest, MAYBE_DestroyProfilerWhileProfiling) {
SamplingParams params;
params.sampling_interval = TimeDelta::FromMilliseconds(10);
CallStackProfiles profiles;
WithTargetThread([&params, &profiles](PlatformThreadId target_thread_id) {
scoped_ptr<StackSamplingProfiler> profiler;
profiler.reset(new StackSamplingProfiler(
target_thread_id, params, Bind(&SaveProfiles, Unretained(&profiles))));
profiler->Start();
profiler.reset();
// Wait longer than a sample interval to catch any use-after-free actions by
// the profiler thread.
PlatformThread::Sleep(TimeDelta::FromMilliseconds(50));
});
}
} // namespace base
......@@ -577,11 +577,14 @@ ChromeBrowserMainParts::ChromeBrowserMainParts(
startup_watcher_(new StartupTimeBomb()),
shutdown_watcher_(new ShutdownWatcherHelper()),
browser_field_trials_(parameters.command_line),
sampling_profiler_(base::PlatformThread::CurrentId(),
GetStartupSamplingParams()),
profile_(NULL),
run_message_loop_(true),
notify_result_(ProcessSingleton::PROCESS_NONE),
local_state_(NULL),
restart_last_session_(false) {
sampling_profiler_.Start();
// If we're running tests (ui_task is non-null).
if (parameters.ui_task)
browser_defaults::enable_help_app = false;
......@@ -723,6 +726,19 @@ void ChromeBrowserMainParts::RecordBrowserStartupTime() {
new LoadCompleteListener();
}
// static
base::StackSamplingProfiler::SamplingParams
ChromeBrowserMainParts::GetStartupSamplingParams() {
// Sample at 10Hz for 30 seconds.
base::StackSamplingProfiler::SamplingParams params;
params.initial_delay = base::TimeDelta::FromMilliseconds(0);
params.bursts = 1;
params.samples_per_burst = 300;
params.sampling_interval = base::TimeDelta::FromMilliseconds(100);
params.preserve_sample_ordering = false;
return params;
}
// -----------------------------------------------------------------------------
// TODO(viettrungluu): move more/rest of BrowserMain() into BrowserMainParts.
......
......@@ -8,6 +8,7 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/field_trial.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "base/tracked_objects.h"
#include "chrome/browser/chrome_browser_field_trials.h"
#include "chrome/browser/chrome_process_singleton.h"
......@@ -109,10 +110,8 @@ class ChromeBrowserMainParts : public content::BrowserMainParts {
// Record time from process startup to present time in an UMA histogram.
void RecordBrowserStartupTime();
// Records a time value to an UMA histogram in the context of the
// PreReadExperiment field-trial. This also reports to the appropriate
// sub-histogram (_PreRead(Enabled|Disabled)).
void RecordPreReadExperimentTime(const char* name, base::TimeDelta time);
// Create parameters for sampling stacks during startup.
static base::StackSamplingProfiler::SamplingParams GetStartupSamplingParams();
// Methods for Main Message Loop -------------------------------------------
......@@ -148,6 +147,10 @@ class ChromeBrowserMainParts : public content::BrowserMainParts {
// Parts are deleted in the inverse order they are added.
std::vector<ChromeBrowserMainExtraParts*> chrome_extra_parts_;
// A profiler that periodically samples stack traces. Used to sample startup
// behavior.
base::StackSamplingProfiler sampling_profiler_;
// Members initialized after / released before main_message_loop_ ------------
scoped_ptr<BrowserProcessImpl> browser_process_;
......
......@@ -8,8 +8,11 @@
#include <map>
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/metrics/field_trial.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "components/metrics/metrics_hashes.h"
#include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
......@@ -20,6 +23,12 @@ namespace metrics {
namespace {
// Accepts and ignores the completed profiles. Used when metrics reporting is
// disabled.
void IgnoreCompletedProfiles(
const StackSamplingProfiler::CallStackProfiles& profiles) {
}
// The protobuf expects the MD5 checksum prefix of the module name.
uint64 HashModuleFilename(const base::FilePath& filename) {
const base::FilePath::StringType basename = filename.BaseName().value();
......@@ -104,30 +113,80 @@ void CopyProfileToProto(
proto_profile->set_sampling_period_ms(
profile.sampling_period.InMilliseconds());
}
} // namespace
CallStackProfileMetricsProvider::CallStackProfileMetricsProvider() {}
const char CallStackProfileMetricsProvider::kFieldTrialName[] =
"StackProfiling";
const char CallStackProfileMetricsProvider::kReportProfilesGroupName[] =
"Report profiles";
CallStackProfileMetricsProvider::CallStackProfileMetricsProvider()
: weak_factory_(this) {
}
CallStackProfileMetricsProvider::~CallStackProfileMetricsProvider() {
StackSamplingProfiler::SetDefaultCompletedCallback(
StackSamplingProfiler::CompletedCallback());
}
void CallStackProfileMetricsProvider::OnRecordingEnabled() {
StackSamplingProfiler::SetDefaultCompletedCallback(
base::Bind(&CallStackProfileMetricsProvider::ReceiveCompletedProfiles,
base::MessageLoopProxy::current(),
weak_factory_.GetWeakPtr()));
}
CallStackProfileMetricsProvider::~CallStackProfileMetricsProvider() {}
void CallStackProfileMetricsProvider::OnRecordingDisabled() {
StackSamplingProfiler::SetDefaultCompletedCallback(
base::Bind(&IgnoreCompletedProfiles));
pending_profiles_.clear();
}
void CallStackProfileMetricsProvider::ProvideGeneralMetrics(
ChromeUserMetricsExtension* uma_proto) {
std::vector<StackSamplingProfiler::CallStackProfile> profiles;
if (!source_profiles_for_test_.empty())
profiles.swap(source_profiles_for_test_);
else
StackSamplingProfiler::GetPendingProfiles(&profiles);
for (const StackSamplingProfiler::CallStackProfile& profile : profiles) {
DCHECK(IsSamplingProfilingReportingEnabled() || pending_profiles_.empty());
for (const StackSamplingProfiler::CallStackProfile& profile :
pending_profiles_) {
CallStackProfile* call_stack_profile =
uma_proto->add_sampled_profile()->mutable_call_stack_profile();
CopyProfileToProto(profile, call_stack_profile);
}
pending_profiles_.clear();
}
void CallStackProfileMetricsProvider::SetSourceProfilesForTesting(
void CallStackProfileMetricsProvider::AppendSourceProfilesForTesting(
const std::vector<StackSamplingProfiler::CallStackProfile>& profiles) {
source_profiles_for_test_ = profiles;
AppendCompletedProfiles(profiles);
}
// static
bool CallStackProfileMetricsProvider::IsSamplingProfilingReportingEnabled() {
const std::string group_name = base::FieldTrialList::FindFullName(
CallStackProfileMetricsProvider::kFieldTrialName);
return group_name ==
CallStackProfileMetricsProvider::kReportProfilesGroupName;
}
// static
// Posts a message back to our own thread to collect the profiles.
void CallStackProfileMetricsProvider::ReceiveCompletedProfiles(
scoped_refptr<base::MessageLoopProxy> message_loop,
base::WeakPtr<CallStackProfileMetricsProvider> provider,
const StackSamplingProfiler::CallStackProfiles& profiles) {
message_loop->PostTask(
FROM_HERE,
base::Bind(&CallStackProfileMetricsProvider::AppendCompletedProfiles,
provider, profiles));
}
void CallStackProfileMetricsProvider::AppendCompletedProfiles(
const StackSamplingProfiler::CallStackProfiles& profiles) {
// Don't bother to record profiles if reporting is not enabled.
if (IsSamplingProfilingReportingEnabled()) {
pending_profiles_.insert(pending_profiles_.end(), profiles.begin(),
profiles.end());
}
}
} // namespace metrics
......@@ -7,9 +7,15 @@
#include <vector>
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "components/metrics/metrics_provider.h"
namespace base {
class MessageLoopProxy;
} // namespace base
namespace metrics {
class ChromeUserMetricsExtension;
......@@ -20,18 +26,38 @@ class CallStackProfileMetricsProvider : public MetricsProvider {
~CallStackProfileMetricsProvider() override;
// MetricsProvider:
void OnRecordingEnabled() override;
void OnRecordingDisabled() override;
void ProvideGeneralMetrics(ChromeUserMetricsExtension* uma_proto) override;
// Uses |profiles| as the source data for the next invocation of
// ProvideGeneralMetrics, rather than sourcing them from the
// StackSamplingProfiler.
void SetSourceProfilesForTesting(
// Appends |profiles| for use by the next invocation of ProvideGeneralMetrics,
// rather than sourcing them from the StackSamplingProfiler.
void AppendSourceProfilesForTesting(
const std::vector<base::StackSamplingProfiler::CallStackProfile>&
profiles);
protected:
// Finch field trial and group for reporting profiles. Provided here for test
// use.
static const char kFieldTrialName[];
static const char kReportProfilesGroupName[];
private:
std::vector<base::StackSamplingProfiler::CallStackProfile>
source_profiles_for_test_;
// Returns true if reporting of profiles is enabled according to the
// controlling Finch field trial.
static bool IsSamplingProfilingReportingEnabled();
// Invoked by the profiler on another thread.
static void ReceiveCompletedProfiles(
scoped_refptr<base::MessageLoopProxy> message_loop,
base::WeakPtr<CallStackProfileMetricsProvider> provider,
const base::StackSamplingProfiler::CallStackProfiles& profiles);
void AppendCompletedProfiles(
const base::StackSamplingProfiler::CallStackProfiles& profiles);
base::StackSamplingProfiler::CallStackProfiles pending_profiles_;
base::WeakPtrFactory<CallStackProfileMetricsProvider> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(CallStackProfileMetricsProvider);
};
......
......@@ -4,9 +4,11 @@
#include "components/metrics/call_stack_profile_metrics_provider.h"
#include "base/metrics/field_trial.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "base/strings/string_number_conversions.h"
#include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
#include "components/variations/entropy_provider.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::StackSamplingProfiler;
......@@ -17,8 +19,33 @@ using Sample = StackSamplingProfiler::Sample;
namespace metrics {
// This test fixture enables the field trial that
// CallStackProfileMetricsProvider depends on to report profiles.
class CallStackProfileMetricsProviderTest : public testing::Test {
public:
CallStackProfileMetricsProviderTest()
: field_trial_list_(new base::FieldTrialList(
new metrics::SHA1EntropyProvider("foo"))) {
base::FieldTrialList::CreateFieldTrial(
FieldTrialState::kFieldTrialName,
FieldTrialState::kReportProfilesGroupName);
}
~CallStackProfileMetricsProviderTest() override {}
private:
// Exposes field trial/group names from the CallStackProfileMetricsProvider.
class FieldTrialState : public CallStackProfileMetricsProvider {
public:
using CallStackProfileMetricsProvider::kFieldTrialName;
using CallStackProfileMetricsProvider::kReportProfilesGroupName;
};
const scoped_ptr<base::FieldTrialList> field_trial_list_;
};
// Checks that all properties from multiple profiles are filled as expected.
TEST(CallStackProfileMetricsProviderTest, MultipleProfiles) {
TEST_F(CallStackProfileMetricsProviderTest, MultipleProfiles) {
const uintptr_t module1_base_address = 0x1000;
const uintptr_t module2_base_address = 0x2000;
const uintptr_t module3_base_address = 0x3000;
......@@ -165,7 +192,7 @@ TEST(CallStackProfileMetricsProviderTest, MultipleProfiles) {
}
CallStackProfileMetricsProvider provider;
provider.SetSourceProfilesForTesting(profiles);
provider.AppendSourceProfilesForTesting(profiles);
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
......@@ -229,7 +256,7 @@ TEST(CallStackProfileMetricsProviderTest, MultipleProfiles) {
// Checks that all duplicate samples are collapsed with
// preserve_sample_ordering = false.
TEST(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) {
TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) {
const uintptr_t module_base_address = 0x1000;
const Module modules[] = {
......@@ -268,7 +295,7 @@ TEST(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) {
profile.preserve_sample_ordering = false;
CallStackProfileMetricsProvider provider;
provider.SetSourceProfilesForTesting(std::vector<Profile>(1, profile));
provider.AppendSourceProfilesForTesting(std::vector<Profile>(1, profile));
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
......@@ -306,7 +333,7 @@ TEST(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) {
// Checks that only contiguous duplicate samples are collapsed with
// preserve_sample_ordering = true.
TEST(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) {
TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) {
const uintptr_t module_base_address = 0x1000;
const Module modules[] = {
......@@ -345,7 +372,7 @@ TEST(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) {
profile.preserve_sample_ordering = true;
CallStackProfileMetricsProvider provider;
provider.SetSourceProfilesForTesting(std::vector<Profile>(1, profile));
provider.AppendSourceProfilesForTesting(std::vector<Profile>(1, profile));
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
......@@ -381,9 +408,8 @@ TEST(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) {
}
}
// Checks that unknown modules produce an empty Entry.
TEST(CallStackProfileMetricsProviderTest, UnknownModule) {
TEST_F(CallStackProfileMetricsProviderTest, UnknownModule) {
const Frame frame(reinterpret_cast<const void*>(0x1000),
Frame::kUnknownModuleIndex);
......@@ -396,7 +422,7 @@ TEST(CallStackProfileMetricsProviderTest, UnknownModule) {
profile.preserve_sample_ordering = false;
CallStackProfileMetricsProvider provider;
provider.SetSourceProfilesForTesting(std::vector<Profile>(1, profile));
provider.AppendSourceProfilesForTesting(std::vector<Profile>(1, profile));
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
......@@ -417,4 +443,32 @@ TEST(CallStackProfileMetricsProviderTest, UnknownModule) {
EXPECT_FALSE(entry.has_module_id_index());
}
// Checks that pending profiles are only passed back to ProvideGeneralMetrics
// once.
TEST_F(CallStackProfileMetricsProviderTest, ProfilesProvidedOnlyOnce) {
CallStackProfileMetricsProvider provider;
for (int i = 0; i < 2; ++i) {
Profile profile;
profile.samples.push_back(Sample(1, Frame(
reinterpret_cast<const void*>(0x1000), Frame::kUnknownModuleIndex)));
profile.profile_duration = base::TimeDelta::FromMilliseconds(100);
// Use the sampling period to distinguish the two profiles.
profile.sampling_period = base::TimeDelta::FromMilliseconds(i);
profile.preserve_sample_ordering = false;
provider.AppendSourceProfilesForTesting(std::vector<Profile>(1, profile));
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
ASSERT_EQ(1, uma_proto.sampled_profile().size());
const SampledProfile& sampled_profile = uma_proto.sampled_profile().Get(0);
ASSERT_TRUE(sampled_profile.has_call_stack_profile());
const CallStackProfile& call_stack_profile =
sampled_profile.call_stack_profile();
ASSERT_TRUE(call_stack_profile.has_sampling_period_ms());
EXPECT_EQ(i, call_stack_profile.sampling_period_ms());
}
}
} // namespace metrics
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment