1 // Copyright (c) 2012 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 "chrome/browser/chromeos/external_metrics.h"
10 #include "base/bind.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/metrics/field_trial.h"
14 #include "base/metrics/histogram.h"
15 #include "base/metrics/sparse_histogram.h"
16 #include "base/metrics/statistics_recorder.h"
17 #include "base/timer/elapsed_timer.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/metrics/chromeos_metrics_provider.h"
20 #include "components/metrics/metrics_service.h"
21 #include "components/metrics/serialization/metric_sample.h"
22 #include "components/metrics/serialization/serialization_utils.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/user_metrics.h"
26 using base::UserMetricsAction
;
27 using content::BrowserThread
;
33 bool CheckValues(const std::string
& name
,
36 size_t bucket_count
) {
37 if (!base::Histogram::InspectConstructionArguments(
38 name
, &minimum
, &maximum
, &bucket_count
))
40 base::HistogramBase
* histogram
=
41 base::StatisticsRecorder::FindHistogram(name
);
44 return histogram
->HasConstructionArguments(minimum
, maximum
, bucket_count
);
47 bool CheckLinearValues(const std::string
& name
, int maximum
) {
48 return CheckValues(name
, 1, maximum
, maximum
+ 1);
51 // Establishes field trial for wifi scanning in chromeos. crbug.com/242733.
52 void SetupProgressiveScanFieldTrial() {
53 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
54 const char name_of_experiment
[] = "ProgressiveScan";
55 const base::FilePath
group_file_path(
56 "/home/chronos/.progressive_scan_variation");
57 const base::FieldTrial::Probability kDivisor
= 1000;
58 scoped_refptr
<base::FieldTrial
> trial
=
59 base::FieldTrialList::FactoryGetFieldTrial(
60 name_of_experiment
, kDivisor
, "Default", 2013, 12, 31,
61 base::FieldTrial::SESSION_RANDOMIZED
, NULL
);
63 // Announce the groups with 0 percentage; the actual percentages come from
64 // the server configuration.
65 std::map
<int, std::string
> group_to_char
;
66 group_to_char
[trial
->AppendGroup("FullScan", 0)] = "c";
67 group_to_char
[trial
->AppendGroup("33Percent_4MinMax", 0)] = "1";
68 group_to_char
[trial
->AppendGroup("50Percent_4MinMax", 0)] = "2";
69 group_to_char
[trial
->AppendGroup("50Percent_8MinMax", 0)] = "3";
70 group_to_char
[trial
->AppendGroup("100Percent_8MinMax", 0)] = "4";
71 group_to_char
[trial
->AppendGroup("100Percent_1MinSeen_A", 0)] = "5";
72 group_to_char
[trial
->AppendGroup("100Percent_1MinSeen_B", 0)] = "6";
73 group_to_char
[trial
->AppendGroup("100Percent_1Min_4Max", 0)] = "7";
75 // Announce the experiment to any listeners (especially important is the UMA
76 // software, which will append the group names to UMA statistics).
77 const int group_num
= trial
->group();
78 std::string group_char
= "x";
79 if (ContainsKey(group_to_char
, group_num
))
80 group_char
= group_to_char
[group_num
];
82 // Write the group to the file to be read by ChromeOS.
83 int size
= static_cast<int>(group_char
.length());
84 if (base::WriteFile(group_file_path
, group_char
.c_str(), size
) == size
) {
85 VLOG(1) << "Configured in group '" << trial
->group_name()
86 << "' ('" << group_char
<< "') for "
87 << name_of_experiment
<< " field trial";
89 VLOG(1) << "Couldn't write to " << group_file_path
.value();
95 // The interval between external metrics collections in seconds
96 static const int kExternalMetricsCollectionIntervalSeconds
= 30;
97 const char kEventsFilePath
[] = "/var/run/metrics/uma-events";
99 ExternalMetrics::ExternalMetrics() : uma_events_file_(kEventsFilePath
) {
102 ExternalMetrics::~ExternalMetrics() {}
104 void ExternalMetrics::Start() {
105 // Register user actions external to the browser.
106 // tools/metrics/actions/extract_actions.py won't understand these lines, so
107 // all of these are explicitly added in that script.
108 // TODO(derat): We shouldn't need to verify actions before reporting them;
109 // remove all of this once http://crosbug.com/11125 is fixed.
110 valid_user_actions_
.insert("Cryptohome.PKCS11InitFail");
111 valid_user_actions_
.insert("Updater.ServerCertificateChanged");
112 valid_user_actions_
.insert("Updater.ServerCertificateFailed");
114 // Initialize here field trials that don't need to read from files.
115 // (None for the moment.)
117 // Initialize any chromeos field trials that need to read from a file (e.g.,
118 // those that have an upstart script determine their experimental group for
119 // them) then schedule the data collection. All of this is done on the file
121 bool task_posted
= BrowserThread::PostTask(
124 base::Bind(&chromeos::ExternalMetrics::SetupFieldTrialsOnFileThread
,
130 scoped_refptr
<ExternalMetrics
> ExternalMetrics::CreateForTesting(
131 const std::string
& filename
) {
132 scoped_refptr
<ExternalMetrics
> external_metrics(new ExternalMetrics());
133 external_metrics
->uma_events_file_
= filename
;
134 return external_metrics
;
137 void ExternalMetrics::RecordActionUI(std::string action_string
) {
138 if (valid_user_actions_
.count(action_string
)) {
139 content::RecordComputedAction(action_string
);
141 DLOG(ERROR
) << "undefined UMA action: " << action_string
;
145 void ExternalMetrics::RecordAction(const std::string
& action
) {
146 BrowserThread::PostTask(
149 base::Bind(&ExternalMetrics::RecordActionUI
, this, action
));
152 void ExternalMetrics::RecordCrashUI(const std::string
& crash_kind
) {
153 ChromeOSMetricsProvider::LogCrash(crash_kind
);
156 void ExternalMetrics::RecordCrash(const std::string
& crash_kind
) {
157 BrowserThread::PostTask(
158 BrowserThread::UI
, FROM_HERE
,
159 base::Bind(&ExternalMetrics::RecordCrashUI
, this, crash_kind
));
162 void ExternalMetrics::RecordHistogram(const metrics::MetricSample
& sample
) {
163 CHECK_EQ(metrics::MetricSample::HISTOGRAM
, sample
.type());
165 sample
.name(), sample
.min(), sample
.max(), sample
.bucket_count())) {
166 DLOG(ERROR
) << "Invalid histogram: " << sample
.name();
170 base::HistogramBase
* counter
=
171 base::Histogram::FactoryGet(sample
.name(),
174 sample
.bucket_count(),
175 base::Histogram::kUmaTargetedHistogramFlag
);
176 counter
->Add(sample
.sample());
179 void ExternalMetrics::RecordLinearHistogram(
180 const metrics::MetricSample
& sample
) {
181 CHECK_EQ(metrics::MetricSample::LINEAR_HISTOGRAM
, sample
.type());
182 if (!CheckLinearValues(sample
.name(), sample
.max())) {
183 DLOG(ERROR
) << "Invalid linear histogram: " << sample
.name();
186 base::HistogramBase
* counter
= base::LinearHistogram::FactoryGet(
191 base::Histogram::kUmaTargetedHistogramFlag
);
192 counter
->Add(sample
.sample());
195 void ExternalMetrics::RecordSparseHistogram(
196 const metrics::MetricSample
& sample
) {
197 CHECK_EQ(metrics::MetricSample::SPARSE_HISTOGRAM
, sample
.type());
198 base::HistogramBase
* counter
= base::SparseHistogram::FactoryGet(
199 sample
.name(), base::HistogramBase::kUmaTargetedHistogramFlag
);
200 counter
->Add(sample
.sample());
203 int ExternalMetrics::CollectEvents() {
204 ScopedVector
<metrics::MetricSample
> samples
;
205 metrics::SerializationUtils::ReadAndTruncateMetricsFromFile(uma_events_file_
,
208 for (ScopedVector
<metrics::MetricSample
>::iterator it
= samples
.begin();
211 const metrics::MetricSample
& sample
= **it
;
213 // Do not use the UMA_HISTOGRAM_... macros here. They cache the Histogram
214 // instance and thus only work if |sample.name()| is constant.
215 switch (sample
.type()) {
216 case metrics::MetricSample::CRASH
:
217 RecordCrash(sample
.name());
219 case metrics::MetricSample::USER_ACTION
:
220 RecordAction(sample
.name());
222 case metrics::MetricSample::HISTOGRAM
:
223 RecordHistogram(sample
);
225 case metrics::MetricSample::LINEAR_HISTOGRAM
:
226 RecordLinearHistogram(sample
);
228 case metrics::MetricSample::SPARSE_HISTOGRAM
:
229 RecordSparseHistogram(sample
);
234 return samples
.size();
237 void ExternalMetrics::CollectEventsAndReschedule() {
238 base::ElapsedTimer timer
;
240 UMA_HISTOGRAM_TIMES("UMA.CollectExternalEventsTime", timer
.Elapsed());
244 void ExternalMetrics::ScheduleCollector() {
246 result
= BrowserThread::PostDelayedTask(
247 BrowserThread::FILE, FROM_HERE
,
248 base::Bind(&chromeos::ExternalMetrics::CollectEventsAndReschedule
, this),
249 base::TimeDelta::FromSeconds(kExternalMetricsCollectionIntervalSeconds
));
253 void ExternalMetrics::SetupFieldTrialsOnFileThread() {
254 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
255 // Field trials that do not read from files can be initialized in
256 // ExternalMetrics::Start() above.
257 SetupProgressiveScanFieldTrial();
262 } // namespace chromeos