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 @@ ...@@ -15,61 +15,102 @@
namespace base { namespace base {
// PendingProfiles ------------------------------------------------------------ // DefaultProfileProcessor ----------------------------------------------------
namespace { namespace {
// Thread-safe singleton class that stores collected call stack profiles waiting // Singleton class responsible for providing the default processing for profiles
// to be processed. // (i.e. for profiles generated by profilers without their own completed
class PendingProfiles { // callback).
class DefaultProfileProcessor {
public: public:
~PendingProfiles(); using CompletedCallback = StackSamplingProfiler::CompletedCallback;
static PendingProfiles* GetInstance(); ~DefaultProfileProcessor();
// Appends |profiles| to |profiles_|. This function may be called on any static DefaultProfileProcessor* GetInstance();
// thread.
void AppendProfiles( // Sets the callback to use for processing profiles captured without a
const std::vector<StackSamplingProfiler::CallStackProfile>& profiles); // 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 // Copies the pending profiles from |profiles_| into |profiles|, and clears
// |profiles_|. This function may be called on any thread. // |profiles_|. This function may be called on any thread.
void GetAndClearPendingProfiles( void GetAndClearPendingProfiles(
std::vector<StackSamplingProfiler::CallStackProfile>* profiles); StackSamplingProfiler::CallStackProfiles* profiles);
private: // Gets the current completed callback, with proper locking.
friend struct DefaultSingletonTraits<PendingProfiles>; CompletedCallback GetCompletedCallback() const;
PendingProfiles(); mutable Lock callback_lock_;
CompletedCallback default_completed_callback_;
Lock profiles_lock_; Lock profiles_lock_;
std::vector<StackSamplingProfiler::CallStackProfile> profiles_; StackSamplingProfiler::CallStackProfiles profiles_;
DISALLOW_COPY_AND_ASSIGN(PendingProfiles); DISALLOW_COPY_AND_ASSIGN(DefaultProfileProcessor);
}; };
PendingProfiles::PendingProfiles() {} DefaultProfileProcessor::~DefaultProfileProcessor() {}
PendingProfiles::~PendingProfiles() {}
// static // static
PendingProfiles* PendingProfiles::GetInstance() { DefaultProfileProcessor* DefaultProfileProcessor::GetInstance() {
return Singleton<PendingProfiles>::get(); return Singleton<DefaultProfileProcessor>::get();
} }
void PendingProfiles::AppendProfiles( void DefaultProfileProcessor::SetCompletedCallback(CompletedCallback callback) {
const std::vector<StackSamplingProfiler::CallStackProfile>& profiles) { {
AutoLock scoped_lock(profiles_lock_); AutoLock scoped_lock(callback_lock_);
profiles_.insert(profiles_.end(), profiles.begin(), profiles.end()); 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( DefaultProfileProcessor::DefaultProfileProcessor() {}
std::vector<StackSamplingProfiler::CallStackProfile>* profiles) {
void DefaultProfileProcessor::GetAndClearPendingProfiles(
StackSamplingProfiler::CallStackProfiles* profiles) {
profiles->clear(); profiles->clear();
AutoLock scoped_lock(profiles_lock_); AutoLock scoped_lock(profiles_lock_);
profiles_.swap(*profiles); profiles_.swap(*profiles);
} }
DefaultProfileProcessor::CompletedCallback
DefaultProfileProcessor::GetCompletedCallback() const {
AutoLock scoped_lock(callback_lock_);
return default_completed_callback_;
}
} // namespace } // namespace
...@@ -209,7 +250,16 @@ StackSamplingProfiler::StackSamplingProfiler(PlatformThreadId thread_id, ...@@ -209,7 +250,16 @@ StackSamplingProfiler::StackSamplingProfiler(PlatformThreadId thread_id,
const SamplingParams& params) const SamplingParams& params)
: thread_id_(thread_id), params_(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() { void StackSamplingProfiler::Start() {
scoped_ptr<NativeStackSampler> native_sampler = scoped_ptr<NativeStackSampler> native_sampler =
...@@ -217,14 +267,14 @@ void StackSamplingProfiler::Start() { ...@@ -217,14 +267,14 @@ void StackSamplingProfiler::Start() {
if (!native_sampler) if (!native_sampler)
return; return;
CompletedCallback callback =
!completed_callback_.is_null() ? completed_callback_ :
Bind(&DefaultProfileProcessor::ProcessProfiles,
Unretained(DefaultProfileProcessor::GetInstance()));
sampling_thread_.reset( sampling_thread_.reset(
new SamplingThread( new SamplingThread(native_sampler.Pass(), params_, callback));
native_sampler.Pass(), params_, if (!PlatformThread::Create(0, sampling_thread_.get(),
(custom_completed_callback_.is_null() ? &sampling_thread_handle_))
Bind(&PendingProfiles::AppendProfiles,
Unretained(PendingProfiles::GetInstance())) :
custom_completed_callback_)));
if (!PlatformThread::CreateNonJoinable(0, sampling_thread_.get()))
sampling_thread_.reset(); sampling_thread_.reset();
} }
...@@ -234,8 +284,9 @@ void StackSamplingProfiler::Stop() { ...@@ -234,8 +284,9 @@ void StackSamplingProfiler::Stop() {
} }
// static // static
void StackSamplingProfiler::GetPendingProfiles(CallStackProfiles* profiles) { void StackSamplingProfiler::SetDefaultCompletedCallback(
PendingProfiles::GetInstance()->GetAndClearPendingProfiles(profiles); CompletedCallback callback) {
DefaultProfileProcessor::GetInstance()->SetCompletedCallback(callback);
} }
// StackSamplingProfiler::Frame global functions ------------------------------ // StackSamplingProfiler::Frame global functions ------------------------------
......
...@@ -34,11 +34,12 @@ class NativeStackSampler; ...@@ -34,11 +34,12 @@ class NativeStackSampler;
// base::StackSamplingProfiler profiler(base::PlatformThread::CurrentId()), // base::StackSamplingProfiler profiler(base::PlatformThread::CurrentId()),
// params); // params);
// //
// // To process the call stack profiles within Chrome rather than via UMA, // // Or, to process the profiles within Chrome rather than via UMA, use a
// // set a custom completed callback: // // custom completed callback:
// base::StackStackSamplingProfiler::CompletedCallback // base::StackStackSamplingProfiler::CompletedCallback
// thread_safe_callback = ...; // thread_safe_callback = ...;
// profiler.SetCustomCompletedCallback(thread_safe_callback); // base::StackSamplingProfiler profiler(base::PlatformThread::CurrentId()),
// params, thread_safe_callback);
// //
// profiler.Start(); // profiler.Start();
// // ... work being done on the target thread here ... // // ... work being done on the target thread here ...
...@@ -49,13 +50,13 @@ class NativeStackSampler; ...@@ -49,13 +50,13 @@ class NativeStackSampler;
// altered as desired. // altered as desired.
// //
// When all call stack profiles are complete or the profiler is stopped, if the // 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 // custom completed callback was set it is called from a thread created by the
// the completed profiles. A profile is considered complete if all requested // profiler with the completed profiles. A profile is considered complete if all
// samples were recorded for the profile (i.e. it was not stopped // requested samples were recorded for the profile (i.e. it was not stopped
// prematurely). If no callback was set, the completed profiles are stored // prematurely). If no callback was set, the default completed callback will be
// internally and retrieved for UMA through GetPendingProfiles(). // called with the profiles. It is expected that the the default completed
// GetPendingProfiles() should never be called by other code; to retrieve // callback is set by the metrics system to allow profiles to be provided via
// profiles for in-process processing, set a completed callback. // UMA.
// //
// The results of the profiling are passed to the completed callback and consist // The results of the profiling are passed to the completed callback and consist
// of a vector of CallStackProfiles. Each CallStackProfile corresponds to a // of a vector of CallStackProfiles. Each CallStackProfile corresponds to a
...@@ -164,8 +165,14 @@ class BASE_EXPORT StackSamplingProfiler { ...@@ -164,8 +165,14 @@ class BASE_EXPORT StackSamplingProfiler {
// thread-safe callback implementation. // thread-safe callback implementation.
using CompletedCallback = Callback<void(const CallStackProfiles&)>; using CompletedCallback = Callback<void(const CallStackProfiles&)>;
// Creates a profiler that sends completed profiles to the default completed
// callback.
StackSamplingProfiler(PlatformThreadId thread_id, StackSamplingProfiler(PlatformThreadId thread_id,
const SamplingParams& params); const SamplingParams& params);
// Creates a profiler that sends completed profiles to |completed_callback|.
StackSamplingProfiler(PlatformThreadId thread_id,
const SamplingParams& params,
CompletedCallback callback);
~StackSamplingProfiler(); ~StackSamplingProfiler();
// Initializes the profiler and starts sampling. // Initializes the profiler and starts sampling.
...@@ -176,19 +183,14 @@ class BASE_EXPORT StackSamplingProfiler { ...@@ -176,19 +183,14 @@ class BASE_EXPORT StackSamplingProfiler {
// specified in the SamplingParams are completed. // specified in the SamplingParams are completed.
void Stop(); void Stop();
// Moves all pending call stack profiles from internal storage to // Sets a callback to process profiles collected by profiler instances without
// |profiles|. This function is thread safe. // 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 // The callback is typically called on a thread created by the profiler. If
// collected profiles should use SetCustomCompletedCallback. // completed profiles are queued when set, however, it will also be called
static void GetPendingProfiles(CallStackProfiles* call_stack_profiles); // immediately on the calling thread.
static void SetDefaultCompletedCallback(CompletedCallback callback);
// 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;
}
private: private:
// SamplingThread is a separate thread used to suspend and sample stacks from // SamplingThread is a separate thread used to suspend and sample stacks from
...@@ -226,7 +228,7 @@ class BASE_EXPORT StackSamplingProfiler { ...@@ -226,7 +228,7 @@ class BASE_EXPORT StackSamplingProfiler {
// terminate before all the samples specified in |params_| are collected. // terminate before all the samples specified in |params_| are collected.
WaitableEvent stop_event_; WaitableEvent stop_event_;
CompletedCallback completed_callback_; const CompletedCallback completed_callback_;
DISALLOW_COPY_AND_ASSIGN(SamplingThread); DISALLOW_COPY_AND_ASSIGN(SamplingThread);
}; };
...@@ -237,8 +239,9 @@ class BASE_EXPORT StackSamplingProfiler { ...@@ -237,8 +239,9 @@ class BASE_EXPORT StackSamplingProfiler {
const SamplingParams params_; const SamplingParams params_;
scoped_ptr<SamplingThread> sampling_thread_; scoped_ptr<SamplingThread> sampling_thread_;
PlatformThreadHandle sampling_thread_handle_;
CompletedCallback custom_completed_callback_; const CompletedCallback completed_callback_;
DISALLOW_COPY_AND_ASSIGN(StackSamplingProfiler); DISALLOW_COPY_AND_ASSIGN(StackSamplingProfiler);
}; };
......
...@@ -577,11 +577,14 @@ ChromeBrowserMainParts::ChromeBrowserMainParts( ...@@ -577,11 +577,14 @@ ChromeBrowserMainParts::ChromeBrowserMainParts(
startup_watcher_(new StartupTimeBomb()), startup_watcher_(new StartupTimeBomb()),
shutdown_watcher_(new ShutdownWatcherHelper()), shutdown_watcher_(new ShutdownWatcherHelper()),
browser_field_trials_(parameters.command_line), browser_field_trials_(parameters.command_line),
sampling_profiler_(base::PlatformThread::CurrentId(),
GetStartupSamplingParams()),
profile_(NULL), profile_(NULL),
run_message_loop_(true), run_message_loop_(true),
notify_result_(ProcessSingleton::PROCESS_NONE), notify_result_(ProcessSingleton::PROCESS_NONE),
local_state_(NULL), local_state_(NULL),
restart_last_session_(false) { restart_last_session_(false) {
sampling_profiler_.Start();
// If we're running tests (ui_task is non-null). // If we're running tests (ui_task is non-null).
if (parameters.ui_task) if (parameters.ui_task)
browser_defaults::enable_help_app = false; browser_defaults::enable_help_app = false;
...@@ -723,6 +726,19 @@ void ChromeBrowserMainParts::RecordBrowserStartupTime() { ...@@ -723,6 +726,19 @@ void ChromeBrowserMainParts::RecordBrowserStartupTime() {
new LoadCompleteListener(); 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. // TODO(viettrungluu): move more/rest of BrowserMain() into BrowserMainParts.
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/metrics/field_trial.h" #include "base/metrics/field_trial.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "base/tracked_objects.h" #include "base/tracked_objects.h"
#include "chrome/browser/chrome_browser_field_trials.h" #include "chrome/browser/chrome_browser_field_trials.h"
#include "chrome/browser/chrome_process_singleton.h" #include "chrome/browser/chrome_process_singleton.h"
...@@ -109,10 +110,8 @@ class ChromeBrowserMainParts : public content::BrowserMainParts { ...@@ -109,10 +110,8 @@ class ChromeBrowserMainParts : public content::BrowserMainParts {
// Record time from process startup to present time in an UMA histogram. // Record time from process startup to present time in an UMA histogram.
void RecordBrowserStartupTime(); void RecordBrowserStartupTime();
// Records a time value to an UMA histogram in the context of the // Create parameters for sampling stacks during startup.
// PreReadExperiment field-trial. This also reports to the appropriate static base::StackSamplingProfiler::SamplingParams GetStartupSamplingParams();
// sub-histogram (_PreRead(Enabled|Disabled)).
void RecordPreReadExperimentTime(const char* name, base::TimeDelta time);
// Methods for Main Message Loop ------------------------------------------- // Methods for Main Message Loop -------------------------------------------
...@@ -148,6 +147,10 @@ class ChromeBrowserMainParts : public content::BrowserMainParts { ...@@ -148,6 +147,10 @@ class ChromeBrowserMainParts : public content::BrowserMainParts {
// Parts are deleted in the inverse order they are added. // Parts are deleted in the inverse order they are added.
std::vector<ChromeBrowserMainExtraParts*> chrome_extra_parts_; 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_ ------------ // Members initialized after / released before main_message_loop_ ------------
scoped_ptr<BrowserProcessImpl> browser_process_; scoped_ptr<BrowserProcessImpl> browser_process_;
......
...@@ -8,8 +8,11 @@ ...@@ -8,8 +8,11 @@
#include <map> #include <map>
#include <utility> #include <utility>
#include "base/bind.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/macros.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 "base/profiler/stack_sampling_profiler.h"
#include "components/metrics/metrics_hashes.h" #include "components/metrics/metrics_hashes.h"
#include "components/metrics/proto/chrome_user_metrics_extension.pb.h" #include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
...@@ -20,6 +23,12 @@ namespace metrics { ...@@ -20,6 +23,12 @@ namespace metrics {
namespace { 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. // The protobuf expects the MD5 checksum prefix of the module name.
uint64 HashModuleFilename(const base::FilePath& filename) { uint64 HashModuleFilename(const base::FilePath& filename) {
const base::FilePath::StringType basename = filename.BaseName().value(); const base::FilePath::StringType basename = filename.BaseName().value();
...@@ -104,30 +113,80 @@ void CopyProfileToProto( ...@@ -104,30 +113,80 @@ void CopyProfileToProto(
proto_profile->set_sampling_period_ms( proto_profile->set_sampling_period_ms(
profile.sampling_period.InMilliseconds()); profile.sampling_period.InMilliseconds());
} }
} // namespace } // 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( void CallStackProfileMetricsProvider::ProvideGeneralMetrics(
ChromeUserMetricsExtension* uma_proto) { ChromeUserMetricsExtension* uma_proto) {
std::vector<StackSamplingProfiler::CallStackProfile> profiles; DCHECK(IsSamplingProfilingReportingEnabled() || pending_profiles_.empty());
if (!source_profiles_for_test_.empty()) for (const StackSamplingProfiler::CallStackProfile& profile :
profiles.swap(source_profiles_for_test_); pending_profiles_) {
else
StackSamplingProfiler::GetPendingProfiles(&profiles);
for (const StackSamplingProfiler::CallStackProfile& profile : profiles) {
CallStackProfile* call_stack_profile = CallStackProfile* call_stack_profile =
uma_proto->add_sampled_profile()->mutable_call_stack_profile(); uma_proto->add_sampled_profile()->mutable_call_stack_profile();
CopyProfileToProto(profile, call_stack_profile); CopyProfileToProto(profile, call_stack_profile);
} }
pending_profiles_.clear();
} }
void CallStackProfileMetricsProvider::SetSourceProfilesForTesting( void CallStackProfileMetricsProvider::AppendSourceProfilesForTesting(
const std::vector<StackSamplingProfiler::CallStackProfile>& profiles) { 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 } // namespace metrics
...@@ -7,9 +7,15 @@ ...@@ -7,9 +7,15 @@
#include <vector> #include <vector>
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/profiler/stack_sampling_profiler.h" #include "base/profiler/stack_sampling_profiler.h"
#include "components/metrics/metrics_provider.h" #include "components/metrics/metrics_provider.h"
namespace base {
class MessageLoopProxy;
} // namespace base
namespace metrics { namespace metrics {
class ChromeUserMetricsExtension; class ChromeUserMetricsExtension;
...@@ -20,18 +26,38 @@ class CallStackProfileMetricsProvider : public MetricsProvider { ...@@ -20,18 +26,38 @@ class CallStackProfileMetricsProvider : public MetricsProvider {
~CallStackProfileMetricsProvider() override; ~CallStackProfileMetricsProvider() override;
// MetricsProvider: // MetricsProvider:
void OnRecordingEnabled() override;
void OnRecordingDisabled() override;
void ProvideGeneralMetrics(ChromeUserMetricsExtension* uma_proto) override; void ProvideGeneralMetrics(ChromeUserMetricsExtension* uma_proto) override;
// Uses |profiles| as the source data for the next invocation of // Appends |profiles| for use by the next invocation of ProvideGeneralMetrics,
// ProvideGeneralMetrics, rather than sourcing them from the // rather than sourcing them from the StackSamplingProfiler.
// StackSamplingProfiler. void AppendSourceProfilesForTesting(
void SetSourceProfilesForTesting(
const std::vector<base::StackSamplingProfiler::CallStackProfile>& const std::vector<base::StackSamplingProfiler::CallStackProfile>&
profiles); profiles);
protected:
// Finch field trial and group for reporting profiles. Provided here for test
// use.
static const char kFieldTrialName[];
static const char kReportProfilesGroupName[];
private: private:
std::vector<base::StackSamplingProfiler::CallStackProfile> // Returns true if reporting of profiles is enabled according to the
source_profiles_for_test_; // 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); DISALLOW_COPY_AND_ASSIGN(CallStackProfileMetricsProvider);
}; };
......
...@@ -4,9 +4,11 @@ ...@@ -4,9 +4,11 @@
#include "components/metrics/call_stack_profile_metrics_provider.h" #include "components/metrics/call_stack_profile_metrics_provider.h"
#include "base/metrics/field_trial.h"
#include "base/profiler/stack_sampling_profiler.h" #include "base/profiler/stack_sampling_profiler.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "components/metrics/proto/chrome_user_metrics_extension.pb.h" #include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
#include "components/variations/entropy_provider.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
using base::StackSamplingProfiler; using base::StackSamplingProfiler;
...@@ -17,8 +19,33 @@ using Sample = StackSamplingProfiler::Sample; ...@@ -17,8 +19,33 @@ using Sample = StackSamplingProfiler::Sample;
namespace metrics { 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. // 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 module1_base_address = 0x1000;
const uintptr_t module2_base_address = 0x2000; const uintptr_t module2_base_address = 0x2000;
const uintptr_t module3_base_address = 0x3000; const uintptr_t module3_base_address = 0x3000;
...@@ -165,7 +192,7 @@ TEST(CallStackProfileMetricsProviderTest, MultipleProfiles) { ...@@ -165,7 +192,7 @@ TEST(CallStackProfileMetricsProviderTest, MultipleProfiles) {
} }
CallStackProfileMetricsProvider provider; CallStackProfileMetricsProvider provider;
provider.SetSourceProfilesForTesting(profiles); provider.AppendSourceProfilesForTesting(profiles);
ChromeUserMetricsExtension uma_proto; ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto); provider.ProvideGeneralMetrics(&uma_proto);
...@@ -229,7 +256,7 @@ TEST(CallStackProfileMetricsProviderTest, MultipleProfiles) { ...@@ -229,7 +256,7 @@ TEST(CallStackProfileMetricsProviderTest, MultipleProfiles) {
// Checks that all duplicate samples are collapsed with // Checks that all duplicate samples are collapsed with
// preserve_sample_ordering = false. // preserve_sample_ordering = false.
TEST(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) { TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) {
const uintptr_t module_base_address = 0x1000; const uintptr_t module_base_address = 0x1000;
const Module modules[] = { const Module modules[] = {
...@@ -268,7 +295,7 @@ TEST(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) { ...@@ -268,7 +295,7 @@ TEST(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) {
profile.preserve_sample_ordering = false; profile.preserve_sample_ordering = false;
CallStackProfileMetricsProvider provider; CallStackProfileMetricsProvider provider;
provider.SetSourceProfilesForTesting(std::vector<Profile>(1, profile)); provider.AppendSourceProfilesForTesting(std::vector<Profile>(1, profile));
ChromeUserMetricsExtension uma_proto; ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto); provider.ProvideGeneralMetrics(&uma_proto);
...@@ -306,7 +333,7 @@ TEST(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) { ...@@ -306,7 +333,7 @@ TEST(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) {
// Checks that only contiguous duplicate samples are collapsed with // Checks that only contiguous duplicate samples are collapsed with
// preserve_sample_ordering = true. // preserve_sample_ordering = true.
TEST(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) { TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) {
const uintptr_t module_base_address = 0x1000; const uintptr_t module_base_address = 0x1000;
const Module modules[] = { const Module modules[] = {
...@@ -345,7 +372,7 @@ TEST(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) { ...@@ -345,7 +372,7 @@ TEST(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) {
profile.preserve_sample_ordering = true; profile.preserve_sample_ordering = true;
CallStackProfileMetricsProvider provider; CallStackProfileMetricsProvider provider;
provider.SetSourceProfilesForTesting(std::vector<Profile>(1, profile)); provider.AppendSourceProfilesForTesting(std::vector<Profile>(1, profile));
ChromeUserMetricsExtension uma_proto; ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto); provider.ProvideGeneralMetrics(&uma_proto);
...@@ -381,9 +408,8 @@ TEST(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) { ...@@ -381,9 +408,8 @@ TEST(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) {
} }
} }
// Checks that unknown modules produce an empty Entry. // Checks that unknown modules produce an empty Entry.
TEST(CallStackProfileMetricsProviderTest, UnknownModule) { TEST_F(CallStackProfileMetricsProviderTest, UnknownModule) {
const Frame frame(reinterpret_cast<const void*>(0x1000), const Frame frame(reinterpret_cast<const void*>(0x1000),
Frame::kUnknownModuleIndex); Frame::kUnknownModuleIndex);
...@@ -396,7 +422,7 @@ TEST(CallStackProfileMetricsProviderTest, UnknownModule) { ...@@ -396,7 +422,7 @@ TEST(CallStackProfileMetricsProviderTest, UnknownModule) {
profile.preserve_sample_ordering = false; profile.preserve_sample_ordering = false;
CallStackProfileMetricsProvider provider; CallStackProfileMetricsProvider provider;
provider.SetSourceProfilesForTesting(std::vector<Profile>(1, profile)); provider.AppendSourceProfilesForTesting(std::vector<Profile>(1, profile));
ChromeUserMetricsExtension uma_proto; ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto); provider.ProvideGeneralMetrics(&uma_proto);
...@@ -417,4 +443,32 @@ TEST(CallStackProfileMetricsProviderTest, UnknownModule) { ...@@ -417,4 +443,32 @@ TEST(CallStackProfileMetricsProviderTest, UnknownModule) {
EXPECT_FALSE(entry.has_module_id_index()); 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 } // 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