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(std::make_unique<base::Value>(
62 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(OneArgument(
70 std::make_unique<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(dict ? OneArgument(std::move(dict)) : NoArguments());
86 }
87
88 ExtensionFunction::ResponseAction
Run()89 MetricsPrivateRecordUserActionFunction::Run() {
90 std::unique_ptr<RecordUserAction::Params> params(
91 RecordUserAction::Params::Create(*args_));
92 EXTENSION_FUNCTION_VALIDATE(params.get());
93
94 base::RecordComputedAction(params->name);
95 return RespondNow(NoArguments());
96 }
97
RecordValue(const std::string & name,base::HistogramType type,int min,int max,size_t buckets,int sample)98 void MetricsHistogramHelperFunction::RecordValue(const std::string& name,
99 base::HistogramType type,
100 int min,
101 int max,
102 size_t buckets,
103 int sample) {
104 // Make sure toxic values don't get to internal code.
105 // Fix for maximums
106 min = std::min(min, INT_MAX - 3);
107 max = std::min(max, INT_MAX - 3);
108 buckets = std::min(buckets, kMaxBuckets);
109 // Fix for minimums.
110 min = std::max(min, 1);
111 max = std::max(max, min + 1);
112 buckets = std::max(buckets, static_cast<size_t>(3));
113 // Trim buckets down to a maximum of the given range + over/underflow buckets
114 if (buckets > static_cast<size_t>(max - min + 2))
115 buckets = max - min + 2;
116
117 base::HistogramBase* counter;
118 if (type == base::LINEAR_HISTOGRAM) {
119 counter = base::LinearHistogram::FactoryGet(
120 name, min, max, buckets,
121 base::HistogramBase::kUmaTargetedHistogramFlag);
122 } else {
123 counter = base::Histogram::FactoryGet(
124 name, min, max, buckets,
125 base::HistogramBase::kUmaTargetedHistogramFlag);
126 }
127
128 // The histogram can be NULL if it is constructed with bad arguments. Ignore
129 // that data for this API. An error message will be logged.
130 if (counter)
131 counter->Add(sample);
132 }
133
Run()134 ExtensionFunction::ResponseAction MetricsPrivateRecordValueFunction::Run() {
135 std::unique_ptr<RecordValue::Params> params(
136 RecordValue::Params::Create(*args_));
137 EXTENSION_FUNCTION_VALIDATE(params.get());
138
139 // Get the histogram parameters from the metric type object.
140 std::string type = api::metrics_private::ToString(params->metric.type);
141
142 base::HistogramType histogram_type(
143 type == "histogram-linear" ? base::LINEAR_HISTOGRAM : base::HISTOGRAM);
144 RecordValue(params->metric.metric_name, histogram_type, params->metric.min,
145 params->metric.max, params->metric.buckets, params->value);
146 return RespondNow(NoArguments());
147 }
148
149 ExtensionFunction::ResponseAction
Run()150 MetricsPrivateRecordSparseHashableFunction::Run() {
151 auto params = RecordSparseHashable::Params::Create(*args_);
152 EXTENSION_FUNCTION_VALIDATE(params);
153 base::UmaHistogramSparse(params->metric_name,
154 base::PersistentHash(params->value));
155 return RespondNow(NoArguments());
156 }
157
158 ExtensionFunction::ResponseAction
Run()159 MetricsPrivateRecordSparseValueFunction::Run() {
160 std::unique_ptr<RecordSparseValue::Params> params(
161 RecordSparseValue::Params::Create(*args_));
162 EXTENSION_FUNCTION_VALIDATE(params.get());
163 base::UmaHistogramSparse(params->metric_name, params->value);
164 return RespondNow(NoArguments());
165 }
166
Run()167 ExtensionFunction::ResponseAction MetricsPrivateRecordBooleanFunction::Run() {
168 std::unique_ptr<RecordBoolean::Params> params(
169 RecordBoolean::Params::Create(*args_));
170 EXTENSION_FUNCTION_VALIDATE(params.get());
171 base::UmaHistogramBoolean(params->metric_name, params->value);
172 return RespondNow(NoArguments());
173 }
174
175 ExtensionFunction::ResponseAction
Run()176 MetricsPrivateRecordEnumerationValueFunction::Run() {
177 std::unique_ptr<RecordEnumerationValue::Params> params(
178 RecordEnumerationValue::Params::Create(*args_));
179 EXTENSION_FUNCTION_VALIDATE(params.get());
180 // Uses UmaHistogramExactLinear instead of UmaHistogramEnumeration
181 // because we don't have an enum type on params->value.
182 base::UmaHistogramExactLinear(params->metric_name, params->value,
183 params->enum_size);
184 return RespondNow(NoArguments());
185 }
186
187 ExtensionFunction::ResponseAction
Run()188 MetricsPrivateRecordPercentageFunction::Run() {
189 std::unique_ptr<RecordPercentage::Params> params(
190 RecordPercentage::Params::Create(*args_));
191 EXTENSION_FUNCTION_VALIDATE(params.get());
192 RecordValue(params->metric_name, base::LINEAR_HISTOGRAM, 1, 101, 102,
193 params->value);
194 return RespondNow(NoArguments());
195 }
196
Run()197 ExtensionFunction::ResponseAction MetricsPrivateRecordCountFunction::Run() {
198 std::unique_ptr<RecordCount::Params> params(
199 RecordCount::Params::Create(*args_));
200 EXTENSION_FUNCTION_VALIDATE(params.get());
201 RecordValue(params->metric_name, base::HISTOGRAM, 1, 1000000, 50,
202 params->value);
203 return RespondNow(NoArguments());
204 }
205
206 ExtensionFunction::ResponseAction
Run()207 MetricsPrivateRecordSmallCountFunction::Run() {
208 std::unique_ptr<RecordSmallCount::Params> params(
209 RecordSmallCount::Params::Create(*args_));
210 EXTENSION_FUNCTION_VALIDATE(params.get());
211 RecordValue(params->metric_name, base::HISTOGRAM, 1, 100, 50, params->value);
212 return RespondNow(NoArguments());
213 }
214
215 ExtensionFunction::ResponseAction
Run()216 MetricsPrivateRecordMediumCountFunction::Run() {
217 std::unique_ptr<RecordMediumCount::Params> params(
218 RecordMediumCount::Params::Create(*args_));
219 EXTENSION_FUNCTION_VALIDATE(params.get());
220 RecordValue(params->metric_name, base::HISTOGRAM, 1, 10000, 50,
221 params->value);
222 return RespondNow(NoArguments());
223 }
224
Run()225 ExtensionFunction::ResponseAction MetricsPrivateRecordTimeFunction::Run() {
226 std::unique_ptr<RecordTime::Params> params(
227 RecordTime::Params::Create(*args_));
228 EXTENSION_FUNCTION_VALIDATE(params.get());
229 static const int kTenSecMs = 10 * 1000;
230 RecordValue(params->metric_name, base::HISTOGRAM, 1, kTenSecMs, 50,
231 params->value);
232 return RespondNow(NoArguments());
233 }
234
235 ExtensionFunction::ResponseAction
Run()236 MetricsPrivateRecordMediumTimeFunction::Run() {
237 std::unique_ptr<RecordMediumTime::Params> params(
238 RecordMediumTime::Params::Create(*args_));
239 EXTENSION_FUNCTION_VALIDATE(params.get());
240 static const int kThreeMinMs = 3 * 60 * 1000;
241 RecordValue(params->metric_name, base::HISTOGRAM, 1, kThreeMinMs, 50,
242 params->value);
243 return RespondNow(NoArguments());
244 }
245
Run()246 ExtensionFunction::ResponseAction MetricsPrivateRecordLongTimeFunction::Run() {
247 std::unique_ptr<RecordLongTime::Params> params(
248 RecordLongTime::Params::Create(*args_));
249 EXTENSION_FUNCTION_VALIDATE(params.get());
250 static const int kOneHourMs = 60 * 60 * 1000;
251 RecordValue(params->metric_name, base::HISTOGRAM, 1, kOneHourMs, 50,
252 params->value);
253 return RespondNow(NoArguments());
254 }
255
256 MetricsPrivateGetHistogramFunction::~MetricsPrivateGetHistogramFunction() =
257 default;
258
Run()259 ExtensionFunction::ResponseAction MetricsPrivateGetHistogramFunction::Run() {
260 std::unique_ptr<api::metrics_private::GetHistogram::Params> params(
261 api::metrics_private::GetHistogram::Params::Create(*args_));
262 EXTENSION_FUNCTION_VALIDATE(params);
263
264 // Collect histogram data from other processes before responding. Otherwise,
265 // we'd report stale data for histograms that are e.g. recorded by renderers.
266 content::FetchHistogramsAsynchronously(
267 base::ThreadTaskRunnerHandle::Get(),
268 base::BindOnce(
269 &MetricsPrivateGetHistogramFunction::RespondOnHistogramsFetched, this,
270 params->name),
271 kHistogramsRefreshTimeout);
272 return RespondLater();
273 }
274
RespondOnHistogramsFetched(const std::string & name)275 void MetricsPrivateGetHistogramFunction::RespondOnHistogramsFetched(
276 const std::string& name) {
277 // Incorporate the data collected by content::FetchHistogramsAsynchronously().
278 base::StatisticsRecorder::ImportProvidedHistograms();
279 Respond(GetHistogram(name));
280 }
281
282 ExtensionFunction::ResponseValue
GetHistogram(const std::string & name)283 MetricsPrivateGetHistogramFunction::GetHistogram(const std::string& name) {
284 const base::HistogramBase* histogram =
285 base::StatisticsRecorder::FindHistogram(name);
286 if (!histogram)
287 return Error(base::StrCat({"Histogram ", name, " not found"}));
288
289 std::unique_ptr<base::HistogramSamples> samples =
290 histogram->SnapshotSamples();
291 api::metrics_private::Histogram result;
292 result.sum = samples->sum();
293
294 for (std::unique_ptr<base::SampleCountIterator> it = samples->Iterator();
295 !it->Done(); it->Next()) {
296 base::HistogramBase::Sample min = 0;
297 int64_t max = 0;
298 base::HistogramBase::Count count = 0;
299 it->Get(&min, &max, &count);
300
301 api::metrics_private::HistogramBucket bucket;
302 bucket.min = min;
303 bucket.max = max;
304 bucket.count = count;
305 result.buckets.push_back(std::move(bucket));
306 }
307
308 return OneArgument(result.ToValue());
309 }
310
311 } // namespace extensions
312