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.
4
5 #include "extensions/browser/api/metrics_private/metrics_private_api.h"
6
7 #include <limits.h>
8
9 #include <algorithm>
10 #include <memory>
11 #include <utility>
12
13 #include "base/hash/hash.h"
14 #include "base/metrics/field_trial.h"
15 #include "base/metrics/histogram_base.h"
16 #include "base/metrics/histogram_functions.h"
17 #include "base/metrics/histogram_samples.h"
18 #include "base/metrics/statistics_recorder.h"
19 #include "base/metrics/user_metrics.h"
20 #include "base/strings/strcat.h"
21 #include "components/variations/variations_associated_data.h"
22 #include "content/public/browser/histogram_fetcher.h"
23 #include "extensions/browser/api/extensions_api_client.h"
24 #include "extensions/browser/api/metrics_private/metrics_private_delegate.h"
25 #include "extensions/common/api/metrics_private.h"
26
27 namespace extensions {
28
29 namespace GetVariationParams = api::metrics_private::GetVariationParams;
30 namespace RecordUserAction = api::metrics_private::RecordUserAction;
31 namespace RecordValue = api::metrics_private::RecordValue;
32 namespace RecordBoolean = api::metrics_private::RecordBoolean;
33 namespace RecordEnumerationValue = api::metrics_private::RecordEnumerationValue;
34 namespace RecordSparseHashable = api::metrics_private::RecordSparseHashable;
35 namespace RecordSparseValue = api::metrics_private::RecordSparseValue;
36 namespace RecordPercentage = api::metrics_private::RecordPercentage;
37 namespace RecordCount = api::metrics_private::RecordCount;
38 namespace RecordSmallCount = api::metrics_private::RecordSmallCount;
39 namespace RecordMediumCount = api::metrics_private::RecordMediumCount;
40 namespace RecordTime = api::metrics_private::RecordTime;
41 namespace RecordMediumTime = api::metrics_private::RecordMediumTime;
42 namespace RecordLongTime = api::metrics_private::RecordLongTime;
43
44 namespace {
45
46 const size_t kMaxBuckets = 10000; // We don't ever want more than these many
47 // buckets; there is no real need for them
48 // and would cause crazy memory usage
49
50 // Amount of time to give other processes to report their histograms.
51 constexpr base::TimeDelta kHistogramsRefreshTimeout =
52 base::TimeDelta::FromSeconds(10);
53
54 } // namespace
55
56 ExtensionFunction::ResponseAction
Run()57 MetricsPrivateGetIsCrashReportingEnabledFunction::Run() {
58 MetricsPrivateDelegate* delegate =
59 ExtensionsAPIClient::Get()->GetMetricsPrivateDelegate();
60
61 return RespondNow(OneArgument(
62 base::Value(delegate && delegate->IsCrashReportingEnabled())));
63 }
64
Run()65 ExtensionFunction::ResponseAction MetricsPrivateGetFieldTrialFunction::Run() {
66 std::string name;
67 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &name));
68
69 return RespondNow(
70 OneArgument(base::Value(base::FieldTrialList::FindFullName(name))));
71 }
72
73 ExtensionFunction::ResponseAction
Run()74 MetricsPrivateGetVariationParamsFunction::Run() {
75 std::unique_ptr<GetVariationParams::Params> params(
76 GetVariationParams::Params::Create(*args_));
77 EXTENSION_FUNCTION_VALIDATE(params.get());
78
79 GetVariationParams::Results::Params result;
80 std::unique_ptr<base::DictionaryValue> dict;
81 if (variations::GetVariationParams(params->name,
82 &result.additional_properties)) {
83 dict = result.ToValue();
84 }
85 return RespondNow(
86 dict ? OneArgument(base::Value::FromUniquePtrValue(std::move(dict)))
87 : NoArguments());
88 }
89
90 ExtensionFunction::ResponseAction
Run()91 MetricsPrivateRecordUserActionFunction::Run() {
92 std::unique_ptr<RecordUserAction::Params> params(
93 RecordUserAction::Params::Create(*args_));
94 EXTENSION_FUNCTION_VALIDATE(params.get());
95
96 base::RecordComputedAction(params->name);
97 return RespondNow(NoArguments());
98 }
99
RecordValue(const std::string & name,base::HistogramType type,int min,int max,size_t buckets,int sample)100 void MetricsHistogramHelperFunction::RecordValue(const std::string& name,
101 base::HistogramType type,
102 int min,
103 int max,
104 size_t buckets,
105 int sample) {
106 // Make sure toxic values don't get to internal code.
107 // Fix for maximums
108 min = std::min(min, INT_MAX - 3);
109 max = std::min(max, INT_MAX - 3);
110 buckets = std::min(buckets, kMaxBuckets);
111 // Fix for minimums.
112 min = std::max(min, 1);
113 max = std::max(max, min + 1);
114 buckets = std::max(buckets, static_cast<size_t>(3));
115 // Trim buckets down to a maximum of the given range + over/underflow buckets
116 if (buckets > static_cast<size_t>(max - min + 2))
117 buckets = max - min + 2;
118
119 base::HistogramBase* counter;
120 if (type == base::LINEAR_HISTOGRAM) {
121 counter = base::LinearHistogram::FactoryGet(
122 name, min, max, buckets,
123 base::HistogramBase::kUmaTargetedHistogramFlag);
124 } else {
125 counter = base::Histogram::FactoryGet(
126 name, min, max, buckets,
127 base::HistogramBase::kUmaTargetedHistogramFlag);
128 }
129
130 // The histogram can be NULL if it is constructed with bad arguments. Ignore
131 // that data for this API. An error message will be logged.
132 if (counter)
133 counter->Add(sample);
134 }
135
Run()136 ExtensionFunction::ResponseAction MetricsPrivateRecordValueFunction::Run() {
137 std::unique_ptr<RecordValue::Params> params(
138 RecordValue::Params::Create(*args_));
139 EXTENSION_FUNCTION_VALIDATE(params.get());
140
141 // Get the histogram parameters from the metric type object.
142 std::string type = api::metrics_private::ToString(params->metric.type);
143
144 base::HistogramType histogram_type(
145 type == "histogram-linear" ? base::LINEAR_HISTOGRAM : base::HISTOGRAM);
146 RecordValue(params->metric.metric_name, histogram_type, params->metric.min,
147 params->metric.max, params->metric.buckets, params->value);
148 return RespondNow(NoArguments());
149 }
150
151 ExtensionFunction::ResponseAction
Run()152 MetricsPrivateRecordSparseHashableFunction::Run() {
153 auto params = RecordSparseHashable::Params::Create(*args_);
154 EXTENSION_FUNCTION_VALIDATE(params);
155 base::UmaHistogramSparse(params->metric_name,
156 base::PersistentHash(params->value));
157 return RespondNow(NoArguments());
158 }
159
160 ExtensionFunction::ResponseAction
Run()161 MetricsPrivateRecordSparseValueFunction::Run() {
162 std::unique_ptr<RecordSparseValue::Params> params(
163 RecordSparseValue::Params::Create(*args_));
164 EXTENSION_FUNCTION_VALIDATE(params.get());
165 base::UmaHistogramSparse(params->metric_name, params->value);
166 return RespondNow(NoArguments());
167 }
168
Run()169 ExtensionFunction::ResponseAction MetricsPrivateRecordBooleanFunction::Run() {
170 std::unique_ptr<RecordBoolean::Params> params(
171 RecordBoolean::Params::Create(*args_));
172 EXTENSION_FUNCTION_VALIDATE(params.get());
173 base::UmaHistogramBoolean(params->metric_name, params->value);
174 return RespondNow(NoArguments());
175 }
176
177 ExtensionFunction::ResponseAction
Run()178 MetricsPrivateRecordEnumerationValueFunction::Run() {
179 std::unique_ptr<RecordEnumerationValue::Params> params(
180 RecordEnumerationValue::Params::Create(*args_));
181 EXTENSION_FUNCTION_VALIDATE(params.get());
182 // Uses UmaHistogramExactLinear instead of UmaHistogramEnumeration
183 // because we don't have an enum type on params->value.
184 base::UmaHistogramExactLinear(params->metric_name, params->value,
185 params->enum_size);
186 return RespondNow(NoArguments());
187 }
188
189 ExtensionFunction::ResponseAction
Run()190 MetricsPrivateRecordPercentageFunction::Run() {
191 std::unique_ptr<RecordPercentage::Params> params(
192 RecordPercentage::Params::Create(*args_));
193 EXTENSION_FUNCTION_VALIDATE(params.get());
194 RecordValue(params->metric_name, base::LINEAR_HISTOGRAM, 1, 101, 102,
195 params->value);
196 return RespondNow(NoArguments());
197 }
198
Run()199 ExtensionFunction::ResponseAction MetricsPrivateRecordCountFunction::Run() {
200 std::unique_ptr<RecordCount::Params> params(
201 RecordCount::Params::Create(*args_));
202 EXTENSION_FUNCTION_VALIDATE(params.get());
203 RecordValue(params->metric_name, base::HISTOGRAM, 1, 1000000, 50,
204 params->value);
205 return RespondNow(NoArguments());
206 }
207
208 ExtensionFunction::ResponseAction
Run()209 MetricsPrivateRecordSmallCountFunction::Run() {
210 std::unique_ptr<RecordSmallCount::Params> params(
211 RecordSmallCount::Params::Create(*args_));
212 EXTENSION_FUNCTION_VALIDATE(params.get());
213 RecordValue(params->metric_name, base::HISTOGRAM, 1, 100, 50, params->value);
214 return RespondNow(NoArguments());
215 }
216
217 ExtensionFunction::ResponseAction
Run()218 MetricsPrivateRecordMediumCountFunction::Run() {
219 std::unique_ptr<RecordMediumCount::Params> params(
220 RecordMediumCount::Params::Create(*args_));
221 EXTENSION_FUNCTION_VALIDATE(params.get());
222 RecordValue(params->metric_name, base::HISTOGRAM, 1, 10000, 50,
223 params->value);
224 return RespondNow(NoArguments());
225 }
226
Run()227 ExtensionFunction::ResponseAction MetricsPrivateRecordTimeFunction::Run() {
228 std::unique_ptr<RecordTime::Params> params(
229 RecordTime::Params::Create(*args_));
230 EXTENSION_FUNCTION_VALIDATE(params.get());
231 static const int kTenSecMs = 10 * 1000;
232 RecordValue(params->metric_name, base::HISTOGRAM, 1, kTenSecMs, 50,
233 params->value);
234 return RespondNow(NoArguments());
235 }
236
237 ExtensionFunction::ResponseAction
Run()238 MetricsPrivateRecordMediumTimeFunction::Run() {
239 std::unique_ptr<RecordMediumTime::Params> params(
240 RecordMediumTime::Params::Create(*args_));
241 EXTENSION_FUNCTION_VALIDATE(params.get());
242 static const int kThreeMinMs = 3 * 60 * 1000;
243 RecordValue(params->metric_name, base::HISTOGRAM, 1, kThreeMinMs, 50,
244 params->value);
245 return RespondNow(NoArguments());
246 }
247
Run()248 ExtensionFunction::ResponseAction MetricsPrivateRecordLongTimeFunction::Run() {
249 std::unique_ptr<RecordLongTime::Params> params(
250 RecordLongTime::Params::Create(*args_));
251 EXTENSION_FUNCTION_VALIDATE(params.get());
252 static const int kOneHourMs = 60 * 60 * 1000;
253 RecordValue(params->metric_name, base::HISTOGRAM, 1, kOneHourMs, 50,
254 params->value);
255 return RespondNow(NoArguments());
256 }
257
258 MetricsPrivateGetHistogramFunction::~MetricsPrivateGetHistogramFunction() =
259 default;
260
Run()261 ExtensionFunction::ResponseAction MetricsPrivateGetHistogramFunction::Run() {
262 std::unique_ptr<api::metrics_private::GetHistogram::Params> params(
263 api::metrics_private::GetHistogram::Params::Create(*args_));
264 EXTENSION_FUNCTION_VALIDATE(params);
265
266 // Collect histogram data from other processes before responding. Otherwise,
267 // we'd report stale data for histograms that are e.g. recorded by renderers.
268 content::FetchHistogramsAsynchronously(
269 base::ThreadTaskRunnerHandle::Get(),
270 base::BindOnce(
271 &MetricsPrivateGetHistogramFunction::RespondOnHistogramsFetched, this,
272 params->name),
273 kHistogramsRefreshTimeout);
274 return RespondLater();
275 }
276
RespondOnHistogramsFetched(const std::string & name)277 void MetricsPrivateGetHistogramFunction::RespondOnHistogramsFetched(
278 const std::string& name) {
279 // Incorporate the data collected by content::FetchHistogramsAsynchronously().
280 base::StatisticsRecorder::ImportProvidedHistograms();
281 Respond(GetHistogram(name));
282 }
283
284 ExtensionFunction::ResponseValue
GetHistogram(const std::string & name)285 MetricsPrivateGetHistogramFunction::GetHistogram(const std::string& name) {
286 const base::HistogramBase* histogram =
287 base::StatisticsRecorder::FindHistogram(name);
288 if (!histogram)
289 return Error(base::StrCat({"Histogram ", name, " not found"}));
290
291 std::unique_ptr<base::HistogramSamples> samples =
292 histogram->SnapshotSamples();
293 api::metrics_private::Histogram result;
294 result.sum = samples->sum();
295
296 for (std::unique_ptr<base::SampleCountIterator> it = samples->Iterator();
297 !it->Done(); it->Next()) {
298 base::HistogramBase::Sample min = 0;
299 int64_t max = 0;
300 base::HistogramBase::Count count = 0;
301 it->Get(&min, &max, &count);
302
303 api::metrics_private::HistogramBucket bucket;
304 bucket.min = min;
305 bucket.max = max;
306 bucket.count = count;
307 result.buckets.push_back(std::move(bucket));
308 }
309
310 return OneArgument(base::Value::FromUniquePtrValue(result.ToValue()));
311 }
312
313 } // namespace extensions
314