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