Roll src/third_party/WebKit c63b89c:29324ab (svn 202546:202547)
[chromium-blink-merge.git] / components / metrics / call_stack_profile_metrics_provider.cc
blob15840e9622919e8a454740d5942c0eb3535e8912
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/metrics/call_stack_profile_metrics_provider.h"
7 #include <algorithm>
8 #include <cstring>
9 #include <map>
10 #include <utility>
11 #include <vector>
13 #include "base/bind.h"
14 #include "base/location.h"
15 #include "base/logging.h"
16 #include "base/macros.h"
17 #include "base/memory/singleton.h"
18 #include "base/metrics/field_trial.h"
19 #include "base/profiler/stack_sampling_profiler.h"
20 #include "base/single_thread_task_runner.h"
21 #include "base/synchronization/lock.h"
22 #include "base/thread_task_runner_handle.h"
23 #include "base/time/time.h"
24 #include "components/metrics/metrics_hashes.h"
25 #include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
27 using base::StackSamplingProfiler;
29 namespace metrics {
31 namespace {
33 // ProfilesState --------------------------------------------------------------
35 // A set of profiles and the CallStackProfileMetricsProvider state associated
36 // with them.
37 struct ProfilesState {
38 ProfilesState(const CallStackProfileMetricsProvider::Params& params,
39 const base::StackSamplingProfiler::CallStackProfiles& profiles,
40 base::TimeTicks start_timestamp);
42 // The metrics-related parameters provided to
43 // CallStackProfileMetricsProvider::GetProfilerCallback().
44 CallStackProfileMetricsProvider::Params params;
46 // The call stack profiles collected by the profiler.
47 base::StackSamplingProfiler::CallStackProfiles profiles;
49 // The time at which the CallStackProfileMetricsProvider became aware of the
50 // request for profiling. In particular, this is when callback was requested
51 // via CallStackProfileMetricsProvider::GetProfilerCallback(). Used to
52 // determine if collection was disabled during the collection of the profile.
53 base::TimeTicks start_timestamp;
56 ProfilesState::ProfilesState(
57 const CallStackProfileMetricsProvider::Params& params,
58 const base::StackSamplingProfiler::CallStackProfiles& profiles,
59 base::TimeTicks start_timestamp)
60 : params(params),
61 profiles(profiles),
62 start_timestamp(start_timestamp) {
65 // PendingProfiles ------------------------------------------------------------
67 // Singleton class responsible for retaining profiles received via the callback
68 // created by CallStackProfileMetricsProvider::GetProfilerCallback(). These are
69 // then sent to UMA on the invocation of
70 // CallStackProfileMetricsProvider::ProvideGeneralMetrics(). We need to store
71 // the profiles outside of a CallStackProfileMetricsProvider instance since
72 // callers may start profiling before the CallStackProfileMetricsProvider is
73 // created.
75 // Member functions on this class may be called on any thread.
76 class PendingProfiles {
77 public:
78 static PendingProfiles* GetInstance();
80 void Clear();
81 void Swap(std::vector<ProfilesState>* profiles);
83 // Enables the collection of profiles by CollectProfilesIfCollectionEnabled if
84 // |enabled| is true. Otherwise, clears current profiles and ignores profiles
85 // provided to future invocations of CollectProfilesIfCollectionEnabled.
86 void SetCollectionEnabled(bool enabled);
88 // True if profiles are being collected.
89 bool IsCollectionEnabled() const;
91 // Adds |profile| to the list of profiles if collection is enabled.
92 void CollectProfilesIfCollectionEnabled(const ProfilesState& profiles);
94 // Allows testing against the initial state multiple times.
95 void ResetToDefaultStateForTesting();
97 private:
98 friend struct base::DefaultSingletonTraits<PendingProfiles>;
100 PendingProfiles();
101 ~PendingProfiles();
103 mutable base::Lock lock_;
105 // If true, profiles provided to CollectProfilesIfCollectionEnabled should be
106 // collected. Otherwise they will be ignored.
107 bool collection_enabled_;
109 // The last time collection was disabled. Used to determine if collection was
110 // disabled at any point since a profile was started.
111 base::TimeTicks last_collection_disable_time_;
113 // The set of completed profiles that should be reported.
114 std::vector<ProfilesState> profiles_;
116 DISALLOW_COPY_AND_ASSIGN(PendingProfiles);
119 // static
120 PendingProfiles* PendingProfiles::GetInstance() {
121 // Leaky for performance rather than correctness reasons.
122 return base::Singleton<PendingProfiles,
123 base::LeakySingletonTraits<PendingProfiles>>::get();
126 void PendingProfiles::Clear() {
127 base::AutoLock scoped_lock(lock_);
128 profiles_.clear();
131 void PendingProfiles::Swap(std::vector<ProfilesState>* profiles) {
132 base::AutoLock scoped_lock(lock_);
133 profiles_.swap(*profiles);
136 void PendingProfiles::SetCollectionEnabled(bool enabled) {
137 base::AutoLock scoped_lock(lock_);
139 collection_enabled_ = enabled;
141 if (!collection_enabled_) {
142 profiles_.clear();
143 last_collection_disable_time_ = base::TimeTicks::Now();
147 bool PendingProfiles::IsCollectionEnabled() const {
148 base::AutoLock scoped_lock(lock_);
149 return collection_enabled_;
152 void PendingProfiles::CollectProfilesIfCollectionEnabled(
153 const ProfilesState& profiles) {
154 base::AutoLock scoped_lock(lock_);
156 // Only collect if collection is not disabled and hasn't been disabled
157 // since the start of collection for this profile.
158 if (!collection_enabled_ ||
159 (!last_collection_disable_time_.is_null() &&
160 last_collection_disable_time_ >= profiles.start_timestamp)) {
161 return;
164 profiles_.push_back(profiles);
167 void PendingProfiles::ResetToDefaultStateForTesting() {
168 base::AutoLock scoped_lock(lock_);
170 collection_enabled_ = true;
171 last_collection_disable_time_ = base::TimeTicks();
172 profiles_.clear();
175 // |collection_enabled_| is initialized to true to collect any profiles that are
176 // generated prior to creation of the CallStackProfileMetricsProvider. The
177 // ultimate disposition of these pre-creation collected profiles will be
178 // determined by the initial recording state provided to
179 // CallStackProfileMetricsProvider.
180 PendingProfiles::PendingProfiles() : collection_enabled_(true) {}
182 PendingProfiles::~PendingProfiles() {}
184 // Functions to process completed profiles ------------------------------------
186 // Invoked on the profiler's thread. Provides the profiles to PendingProfiles to
187 // append, if the collecting state allows.
188 void ReceiveCompletedProfiles(
189 const CallStackProfileMetricsProvider::Params& params,
190 base::TimeTicks start_timestamp,
191 const StackSamplingProfiler::CallStackProfiles& profiles) {
192 PendingProfiles::GetInstance()->CollectProfilesIfCollectionEnabled(
193 ProfilesState(params, profiles, start_timestamp));
196 // Invoked on an arbitrary thread. Ignores the provided profiles.
197 void IgnoreCompletedProfiles(
198 const StackSamplingProfiler::CallStackProfiles& profiles) {
201 // Functions to encode protobufs ----------------------------------------------
203 // The protobuf expects the MD5 checksum prefix of the module name.
204 uint64 HashModuleFilename(const base::FilePath& filename) {
205 const base::FilePath::StringType basename = filename.BaseName().value();
206 // Copy the bytes in basename into a string buffer.
207 size_t basename_length_in_bytes =
208 basename.size() * sizeof(base::FilePath::CharType);
209 std::string name_bytes(basename_length_in_bytes, '\0');
210 memcpy(&name_bytes[0], &basename[0], basename_length_in_bytes);
211 return HashMetricName(name_bytes);
214 // Transcode |sample| into |proto_sample|, using base addresses in |modules| to
215 // compute module instruction pointer offsets.
216 void CopySampleToProto(
217 const StackSamplingProfiler::Sample& sample,
218 const std::vector<StackSamplingProfiler::Module>& modules,
219 CallStackProfile::Sample* proto_sample) {
220 for (const StackSamplingProfiler::Frame& frame : sample) {
221 CallStackProfile::Entry* entry = proto_sample->add_entry();
222 // A frame may not have a valid module. If so, we can't compute the
223 // instruction pointer offset, and we don't want to send bare pointers, so
224 // leave call_stack_entry empty.
225 if (frame.module_index == StackSamplingProfiler::Frame::kUnknownModuleIndex)
226 continue;
227 int64 module_offset =
228 reinterpret_cast<const char*>(frame.instruction_pointer) -
229 reinterpret_cast<const char*>(modules[frame.module_index].base_address);
230 DCHECK_GE(module_offset, 0);
231 entry->set_address(static_cast<uint64>(module_offset));
232 entry->set_module_id_index(frame.module_index);
236 // Transcode |profile| into |proto_profile|.
237 void CopyProfileToProto(
238 const StackSamplingProfiler::CallStackProfile& profile,
239 bool preserve_sample_ordering,
240 CallStackProfile* proto_profile) {
241 if (profile.samples.empty())
242 return;
244 if (preserve_sample_ordering) {
245 // Collapse only consecutive repeated samples together.
246 CallStackProfile::Sample* current_sample_proto = nullptr;
247 for (auto it = profile.samples.begin(); it != profile.samples.end(); ++it) {
248 if (!current_sample_proto || *it != *(it - 1)) {
249 current_sample_proto = proto_profile->add_sample();
250 CopySampleToProto(*it, profile.modules, current_sample_proto);
251 current_sample_proto->set_count(1);
252 } else {
253 current_sample_proto->set_count(current_sample_proto->count() + 1);
256 } else {
257 // Collapse all repeated samples together.
258 std::map<StackSamplingProfiler::Sample, int> sample_index;
259 for (auto it = profile.samples.begin(); it != profile.samples.end(); ++it) {
260 auto location = sample_index.find(*it);
261 if (location == sample_index.end()) {
262 CallStackProfile::Sample* sample_proto = proto_profile->add_sample();
263 CopySampleToProto(*it, profile.modules, sample_proto);
264 sample_proto->set_count(1);
265 sample_index.insert(
266 std::make_pair(
267 *it, static_cast<int>(proto_profile->sample().size()) - 1));
268 } else {
269 CallStackProfile::Sample* sample_proto =
270 proto_profile->mutable_sample()->Mutable(location->second);
271 sample_proto->set_count(sample_proto->count() + 1);
276 for (const StackSamplingProfiler::Module& module : profile.modules) {
277 CallStackProfile::ModuleIdentifier* module_id =
278 proto_profile->add_module_id();
279 module_id->set_build_id(module.id);
280 module_id->set_name_md5_prefix(HashModuleFilename(module.filename));
283 proto_profile->set_profile_duration_ms(
284 profile.profile_duration.InMilliseconds());
285 proto_profile->set_sampling_period_ms(
286 profile.sampling_period.InMilliseconds());
289 // Translates CallStackProfileMetricsProvider's trigger to the corresponding
290 // SampledProfile TriggerEvent.
291 SampledProfile::TriggerEvent ToSampledProfileTriggerEvent(
292 CallStackProfileMetricsProvider::Trigger trigger) {
293 switch (trigger) {
294 case CallStackProfileMetricsProvider::UNKNOWN:
295 return SampledProfile::UNKNOWN_TRIGGER_EVENT;
296 break;
297 case CallStackProfileMetricsProvider::PROCESS_STARTUP:
298 return SampledProfile::PROCESS_STARTUP;
299 break;
300 case CallStackProfileMetricsProvider::JANKY_TASK:
301 return SampledProfile::JANKY_TASK;
302 break;
303 case CallStackProfileMetricsProvider::THREAD_HUNG:
304 return SampledProfile::THREAD_HUNG;
305 break;
307 NOTREACHED();
308 return SampledProfile::UNKNOWN_TRIGGER_EVENT;
311 } // namespace
313 // CallStackProfileMetricsProvider::Params ------------------------------------
315 CallStackProfileMetricsProvider::Params::Params(
316 CallStackProfileMetricsProvider::Trigger trigger)
317 : Params(trigger, false) {
320 CallStackProfileMetricsProvider::Params::Params(
321 CallStackProfileMetricsProvider::Trigger trigger,
322 bool preserve_sample_ordering)
323 : trigger(trigger),
324 preserve_sample_ordering(preserve_sample_ordering) {
327 // CallStackProfileMetricsProvider --------------------------------------------
329 const char CallStackProfileMetricsProvider::kFieldTrialName[] =
330 "StackProfiling";
331 const char CallStackProfileMetricsProvider::kReportProfilesGroupName[] =
332 "Report profiles";
334 CallStackProfileMetricsProvider::CallStackProfileMetricsProvider() {
337 CallStackProfileMetricsProvider::~CallStackProfileMetricsProvider() {
340 // This function can be invoked on an abitrary thread.
341 base::StackSamplingProfiler::CompletedCallback
342 CallStackProfileMetricsProvider::GetProfilerCallback(const Params& params) {
343 // Ignore the profiles if the collection is disabled. If the collection state
344 // changes while collecting, this will be detected by the callback and
345 // profiles will be ignored at that point.
346 if (!PendingProfiles::GetInstance()->IsCollectionEnabled())
347 return base::Bind(&IgnoreCompletedProfiles);
349 return base::Bind(&ReceiveCompletedProfiles, params, base::TimeTicks::Now());
352 void CallStackProfileMetricsProvider::OnRecordingEnabled() {
353 PendingProfiles::GetInstance()->SetCollectionEnabled(true);
356 void CallStackProfileMetricsProvider::OnRecordingDisabled() {
357 PendingProfiles::GetInstance()->SetCollectionEnabled(false);
360 void CallStackProfileMetricsProvider::ProvideGeneralMetrics(
361 ChromeUserMetricsExtension* uma_proto) {
362 std::vector<ProfilesState> pending_profiles;
363 PendingProfiles::GetInstance()->Swap(&pending_profiles);
365 DCHECK(IsReportingEnabledByFieldTrial() || pending_profiles.empty());
367 for (const ProfilesState& profiles_state : pending_profiles) {
368 for (const StackSamplingProfiler::CallStackProfile& profile :
369 profiles_state.profiles) {
370 SampledProfile* sampled_profile = uma_proto->add_sampled_profile();
371 sampled_profile->set_trigger_event(ToSampledProfileTriggerEvent(
372 profiles_state.params.trigger));
373 CopyProfileToProto(profile,
374 profiles_state.params.preserve_sample_ordering,
375 sampled_profile->mutable_call_stack_profile());
380 // static
381 void CallStackProfileMetricsProvider::ResetStaticStateForTesting() {
382 PendingProfiles::GetInstance()->ResetToDefaultStateForTesting();
385 // static
386 bool CallStackProfileMetricsProvider::IsReportingEnabledByFieldTrial() {
387 const std::string group_name = base::FieldTrialList::FindFullName(
388 CallStackProfileMetricsProvider::kFieldTrialName);
389 return group_name ==
390 CallStackProfileMetricsProvider::kReportProfilesGroupName;
393 } // namespace metrics