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