Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / chromecast / base / metrics / cast_metrics_helper.cc
blob89c3d008e33aa92012aff2d329d7f7050cf24fb2
1 // Copyright 2014 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 "chromecast/base/metrics/cast_metrics_helper.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/location.h"
10 #include "base/metrics/histogram.h"
11 #include "base/metrics/user_metrics.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "chromecast/base/metrics/cast_histograms.h"
16 #include "chromecast/base/metrics/grouped_histogram.h"
18 namespace chromecast {
19 namespace metrics {
21 // A useful macro to make sure current member function runs on the valid thread.
22 #define MAKE_SURE_THREAD(callback, ...) \
23 if (!task_runner_->BelongsToCurrentThread()) { \
24 task_runner_->PostTask(FROM_HERE, \
25 base::Bind(&CastMetricsHelper::callback, \
26 base::Unretained(this), ##__VA_ARGS__)); \
27 return; \
30 namespace {
32 CastMetricsHelper* g_instance = NULL;
34 const char kMetricsNameAppInfoDelimiter = '#';
36 } // namespace
38 // static
40 // NOTE(gfhuang): This is a hacky way to encode/decode app infos into a
41 // string. Mainly because it's hard to add another metrics serialization type
42 // into components/metrics/serialization/.
43 // static
44 bool CastMetricsHelper::DecodeAppInfoFromMetricsName(
45 const std::string& metrics_name,
46 std::string* action_name,
47 std::string* app_id,
48 std::string* session_id,
49 std::string* sdk_version) {
50 DCHECK(action_name);
51 DCHECK(app_id);
52 DCHECK(session_id);
53 DCHECK(sdk_version);
54 if (metrics_name.find(kMetricsNameAppInfoDelimiter) == std::string::npos)
55 return false;
57 std::vector<std::string> tokens = base::SplitString(
58 metrics_name, std::string(1, kMetricsNameAppInfoDelimiter),
59 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
60 DCHECK_EQ(tokens.size(), 4u);
61 // The order of tokens should match EncodeAppInfoIntoMetricsName().
62 *action_name = tokens[0];
63 *app_id = tokens[1];
64 *session_id = tokens[2];
65 *sdk_version = tokens[3];
66 return true;
69 // static
70 std::string CastMetricsHelper::EncodeAppInfoIntoMetricsName(
71 const std::string& action_name,
72 const std::string& app_id,
73 const std::string& session_id,
74 const std::string& sdk_version) {
75 std::string result(action_name);
76 result.push_back(kMetricsNameAppInfoDelimiter);
77 result.append(app_id);
78 result.push_back(kMetricsNameAppInfoDelimiter);
79 result.append(session_id);
80 result.push_back(kMetricsNameAppInfoDelimiter);
81 result.append(sdk_version);
82 return result;
85 // static
86 CastMetricsHelper* CastMetricsHelper::GetInstance() {
87 DCHECK(g_instance);
88 return g_instance;
91 CastMetricsHelper::CastMetricsHelper(
92 scoped_refptr<base::SingleThreadTaskRunner> task_runner)
93 : task_runner_(task_runner),
94 metrics_sink_(NULL),
95 record_action_callback_(base::Bind(&base::RecordComputedAction)) {
96 DCHECK(task_runner_.get());
97 DCHECK(!g_instance);
98 g_instance = this;
101 CastMetricsHelper::CastMetricsHelper()
102 : metrics_sink_(NULL) {
103 DCHECK(!g_instance);
104 g_instance = this;
107 CastMetricsHelper::~CastMetricsHelper() {
108 DCHECK_EQ(g_instance, this);
109 g_instance = NULL;
112 void CastMetricsHelper::UpdateCurrentAppInfo(const std::string& app_id,
113 const std::string& session_id) {
114 MAKE_SURE_THREAD(UpdateCurrentAppInfo, app_id, session_id);
115 app_id_ = app_id;
116 session_id_ = session_id;
117 app_start_time_ = base::TimeTicks::Now();
118 TagAppStartForGroupedHistograms(app_id_);
119 sdk_version_.clear();
122 void CastMetricsHelper::UpdateSDKInfo(const std::string& sdk_version) {
123 MAKE_SURE_THREAD(UpdateSDKInfo, sdk_version);
124 sdk_version_ = sdk_version;
127 void CastMetricsHelper::LogMediaPlay() {
128 MAKE_SURE_THREAD(LogMediaPlay);
129 RecordSimpleAction(EncodeAppInfoIntoMetricsName(
130 "MediaPlay",
131 app_id_,
132 session_id_,
133 sdk_version_));
136 void CastMetricsHelper::LogMediaPause() {
137 MAKE_SURE_THREAD(LogMediaPause);
138 RecordSimpleAction(EncodeAppInfoIntoMetricsName(
139 "MediaPause",
140 app_id_,
141 session_id_,
142 sdk_version_));
145 void CastMetricsHelper::LogTimeToFirstPaint() {
146 MAKE_SURE_THREAD(LogTimeToFirstPaint);
147 if (app_id_.empty())
148 return;
149 base::TimeDelta launch_time = base::TimeTicks::Now() - app_start_time_;
150 const std::string uma_name(GetMetricsNameWithAppName("Startup",
151 "TimeToFirstPaint"));
152 LogMediumTimeHistogramEvent(uma_name, launch_time);
153 LOG(INFO) << uma_name << " is " << launch_time.InSecondsF() << " seconds.";
156 void CastMetricsHelper::LogTimeToBufferAv(BufferingType buffering_type,
157 base::TimeDelta time) {
158 MAKE_SURE_THREAD(LogTimeToBufferAv, buffering_type, time);
159 if (time < base::TimeDelta::FromSeconds(0)) {
160 LOG(WARNING) << "Negative time";
161 return;
164 const std::string uma_name(GetMetricsNameWithAppName(
165 "Media",
166 (buffering_type == kInitialBuffering ? "TimeToBufferAv" :
167 buffering_type == kBufferingAfterUnderrun ?
168 "TimeToBufferAvAfterUnderrun" :
169 buffering_type == kAbortedBuffering ? "TimeToBufferAvAfterAbort" : "")));
171 // Histogram from 250ms to 30s with 50 buckets.
172 // The ratio between 2 consecutive buckets is:
173 // exp( (ln(30000) - ln(250)) / 50 ) = 1.1
174 LogTimeHistogramEvent(
175 uma_name,
176 time,
177 base::TimeDelta::FromMilliseconds(250),
178 base::TimeDelta::FromMilliseconds(30000),
179 50);
182 std::string CastMetricsHelper::GetMetricsNameWithAppName(
183 const std::string& prefix,
184 const std::string& suffix) const {
185 DCHECK(task_runner_->BelongsToCurrentThread());
186 std::string metrics_name(prefix);
187 if (!app_id_.empty()) {
188 if (!metrics_name.empty())
189 metrics_name.push_back('.');
190 metrics_name.append(app_id_);
192 if (!suffix.empty()) {
193 if (!metrics_name.empty())
194 metrics_name.push_back('.');
195 metrics_name.append(suffix);
197 return metrics_name;
200 void CastMetricsHelper::SetMetricsSink(MetricsSink* delegate) {
201 MAKE_SURE_THREAD(SetMetricsSink, delegate);
202 metrics_sink_ = delegate;
205 void CastMetricsHelper::SetRecordActionCallback(
206 const RecordActionCallback& callback) {
207 DCHECK(task_runner_->BelongsToCurrentThread());
208 record_action_callback_ = callback;
211 void CastMetricsHelper::RecordSimpleAction(const std::string& action) {
212 MAKE_SURE_THREAD(RecordSimpleAction, action);
214 if (metrics_sink_) {
215 metrics_sink_->OnAction(action);
216 } else {
217 record_action_callback_.Run(action);
221 void CastMetricsHelper::LogEnumerationHistogramEvent(
222 const std::string& name, int value, int num_buckets) {
223 MAKE_SURE_THREAD(LogEnumerationHistogramEvent, name, value, num_buckets);
225 if (metrics_sink_) {
226 metrics_sink_->OnEnumerationEvent(name, value, num_buckets);
227 } else {
228 UMA_HISTOGRAM_ENUMERATION_NO_CACHE(name, value, num_buckets);
232 void CastMetricsHelper::LogTimeHistogramEvent(const std::string& name,
233 const base::TimeDelta& value,
234 const base::TimeDelta& min,
235 const base::TimeDelta& max,
236 int num_buckets) {
237 MAKE_SURE_THREAD(LogTimeHistogramEvent, name, value, min, max, num_buckets);
239 if (metrics_sink_) {
240 metrics_sink_->OnTimeEvent(name, value, min, max, num_buckets);
241 } else {
242 UMA_HISTOGRAM_CUSTOM_TIMES_NO_CACHE(name, value, min, max, num_buckets);
246 void CastMetricsHelper::LogMediumTimeHistogramEvent(
247 const std::string& name,
248 const base::TimeDelta& value) {
249 // Follow UMA_HISTOGRAM_MEDIUM_TIMES definition.
250 LogTimeHistogramEvent(name, value,
251 base::TimeDelta::FromMilliseconds(10),
252 base::TimeDelta::FromMinutes(3),
253 50);
256 } // namespace metrics
257 } // namespace chromecast