autofill_metrics.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
1// Copyright 2013 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 "components/autofill/core/browser/autofill_metrics.h"
6
7#include "base/logging.h"
8#include "base/metrics/histogram.h"
9#include "base/metrics/sparse_histogram.h"
10#include "base/time/time.h"
11#include "components/autofill/core/browser/autofill_type.h"
12#include "components/autofill/core/browser/form_structure.h"
13#include "components/autofill/core/common/form_data.h"
14
15namespace autofill {
16
17namespace {
18
19// Server experiments we support.
20enum ServerExperiment {
21  NO_EXPERIMENT = 0,
22  UNKNOWN_EXPERIMENT,
23  ACCEPTANCE_RATIO_06,
24  ACCEPTANCE_RATIO_1,
25  ACCEPTANCE_RATIO_2,
26  ACCEPTANCE_RATIO_4,
27  ACCEPTANCE_RATIO_05_WINNER_LEAD_RATIO_15,
28  ACCEPTANCE_RATIO_05_WINNER_LEAD_RATIO_25,
29  ACCEPTANCE_RATIO_05_WINNER_LEAD_RATIO_15_MIN_FORM_SCORE_5,
30  TOOLBAR_DATA_ONLY,
31  ACCEPTANCE_RATIO_04_WINNER_LEAD_RATIO_3_MIN_FORM_SCORE_4,
32  NO_SERVER_RESPONSE,
33  PROBABILITY_PICKER_05,
34  PROBABILITY_PICKER_025,
35  PROBABILITY_PICKER_025_CC_THRESHOLD_03,
36  PROBABILITY_PICKER_025_CONTEXTUAL_CC_THRESHOLD_03,
37  PROBABILITY_PICKER_025_CONTEXTUAL_CC_THRESHOLD_03_WITH_FALLBACK,
38  PROBABILITY_PICKER_05_CC_NAME_THRESHOLD_03_EXPERIMENT_1,
39  NUM_SERVER_EXPERIMENTS
40};
41
42enum FieldTypeGroupForMetrics {
43  AMBIGUOUS = 0,
44  NAME,
45  COMPANY,
46  ADDRESS_LINE_1,
47  ADDRESS_LINE_2,
48  ADDRESS_CITY,
49  ADDRESS_STATE,
50  ADDRESS_ZIP,
51  ADDRESS_COUNTRY,
52  PHONE,
53  FAX,  // Deprecated.
54  EMAIL,
55  CREDIT_CARD_NAME,
56  CREDIT_CARD_NUMBER,
57  CREDIT_CARD_DATE,
58  CREDIT_CARD_TYPE,
59  PASSWORD,
60  NUM_FIELD_TYPE_GROUPS_FOR_METRICS
61};
62
63// First, translates |field_type| to the corresponding logical |group| from
64// |FieldTypeGroupForMetrics|.  Then, interpolates this with the given |metric|,
65// which should be in the range [0, |num_possible_metrics|).
66// Returns the interpolated index.
67//
68// The interpolation maps the pair (|group|, |metric|) to a single index, so
69// that all the indicies for a given group are adjacent.  In particular, with
70// the groups {AMBIGUOUS, NAME, ...} combining with the metrics {UNKNOWN, MATCH,
71// MISMATCH}, we create this set of mapped indices:
72// {
73//   AMBIGUOUS+UNKNOWN,
74//   AMBIGUOUS+MATCH,
75//   AMBIGUOUS+MISMATCH,
76//   NAME+UNKNOWN,
77//   NAME+MATCH,
78//   NAME+MISMATCH,
79//   ...
80// }.
81//
82// Clients must ensure that |field_type| is one of the types Chrome supports
83// natively, e.g. |field_type| must not be a billng address.
84int GetFieldTypeGroupMetric(const ServerFieldType field_type,
85                            const int metric,
86                            const int num_possible_metrics) {
87  DCHECK_LT(metric, num_possible_metrics);
88
89  FieldTypeGroupForMetrics group = AMBIGUOUS;
90  switch (AutofillType(field_type).group()) {
91    case ::autofill::NO_GROUP:
92      group = AMBIGUOUS;
93      break;
94
95    case ::autofill::NAME:
96    case ::autofill::NAME_BILLING:
97      group = NAME;
98      break;
99
100    case ::autofill::COMPANY:
101      group = COMPANY;
102      break;
103
104    case ::autofill::ADDRESS_HOME:
105    case ::autofill::ADDRESS_BILLING:
106      switch (AutofillType(field_type).GetStorableType()) {
107        case ADDRESS_HOME_LINE1:
108          group = ADDRESS_LINE_1;
109          break;
110        case ADDRESS_HOME_LINE2:
111          group = ADDRESS_LINE_2;
112          break;
113        case ADDRESS_HOME_CITY:
114          group = ADDRESS_CITY;
115          break;
116        case ADDRESS_HOME_STATE:
117          group = ADDRESS_STATE;
118          break;
119        case ADDRESS_HOME_ZIP:
120          group = ADDRESS_ZIP;
121          break;
122        case ADDRESS_HOME_COUNTRY:
123          group = ADDRESS_COUNTRY;
124          break;
125        default:
126          NOTREACHED();
127          group = AMBIGUOUS;
128          break;
129      }
130      break;
131
132    case ::autofill::EMAIL:
133      group = EMAIL;
134      break;
135
136    case ::autofill::PHONE_HOME:
137    case ::autofill::PHONE_BILLING:
138      group = PHONE;
139      break;
140
141    case ::autofill::CREDIT_CARD:
142      switch (field_type) {
143        case ::autofill::CREDIT_CARD_NAME:
144          group = CREDIT_CARD_NAME;
145          break;
146        case ::autofill::CREDIT_CARD_NUMBER:
147          group = CREDIT_CARD_NUMBER;
148          break;
149        case ::autofill::CREDIT_CARD_TYPE:
150          group = CREDIT_CARD_TYPE;
151          break;
152        case ::autofill::CREDIT_CARD_EXP_MONTH:
153        case ::autofill::CREDIT_CARD_EXP_2_DIGIT_YEAR:
154        case ::autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR:
155        case ::autofill::CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR:
156        case ::autofill::CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR:
157          group = CREDIT_CARD_DATE;
158          break;
159        default:
160          NOTREACHED();
161          group = AMBIGUOUS;
162          break;
163      }
164      break;
165
166    case ::autofill::PASSWORD_FIELD:
167      group = PASSWORD;
168      break;
169  }
170
171  // Interpolate the |metric| with the |group|, so that all metrics for a given
172  // |group| are adjacent.
173  return (group * num_possible_metrics) + metric;
174}
175
176std::string WalletApiMetricToString(
177    AutofillMetrics::WalletApiCallMetric metric) {
178  switch (metric) {
179    case AutofillMetrics::ACCEPT_LEGAL_DOCUMENTS:
180      return "AcceptLegalDocuments";
181    case AutofillMetrics::AUTHENTICATE_INSTRUMENT:
182      return "AuthenticateInstrument";
183    case AutofillMetrics::GET_FULL_WALLET:
184      return "GetFullWallet";
185    case AutofillMetrics::GET_WALLET_ITEMS:
186      return "GetWalletItems";
187    case AutofillMetrics::SAVE_TO_WALLET:
188      return "SaveToWallet";
189    case AutofillMetrics::UNKNOWN_API_CALL:
190    case AutofillMetrics::NUM_WALLET_API_CALLS:
191      NOTREACHED();
192      return "UnknownApiCall";
193  }
194
195  NOTREACHED();
196  return "UnknownApiCall";
197}
198
199// A version of the UMA_HISTOGRAM_ENUMERATION macro that allows the |name|
200// to vary over the program's runtime.
201void LogUMAHistogramEnumeration(const std::string& name,
202                                int sample,
203                                int boundary_value) {
204  DCHECK_LT(sample, boundary_value);
205
206  // Note: This leaks memory, which is expected behavior.
207  base::HistogramBase* histogram =
208      base::LinearHistogram::FactoryGet(
209          name,
210          1,
211          boundary_value,
212          boundary_value + 1,
213          base::HistogramBase::kUmaTargetedHistogramFlag);
214  histogram->Add(sample);
215}
216
217// A version of the UMA_HISTOGRAM_TIMES macro that allows the |name|
218// to vary over the program's runtime.
219void LogUMAHistogramTimes(const std::string& name,
220                          const base::TimeDelta& duration) {
221  // Note: This leaks memory, which is expected behavior.
222  base::HistogramBase* histogram =
223      base::Histogram::FactoryTimeGet(
224          name,
225          base::TimeDelta::FromMilliseconds(1),
226          base::TimeDelta::FromSeconds(10),
227          50,
228          base::HistogramBase::kUmaTargetedHistogramFlag);
229  histogram->AddTime(duration);
230}
231
232// A version of the UMA_HISTOGRAM_LONG_TIMES macro that allows the |name|
233// to vary over the program's runtime.
234void LogUMAHistogramLongTimes(const std::string& name,
235                              const base::TimeDelta& duration) {
236  // Note: This leaks memory, which is expected behavior.
237  base::HistogramBase* histogram =
238      base::Histogram::FactoryTimeGet(
239          name,
240          base::TimeDelta::FromMilliseconds(1),
241          base::TimeDelta::FromHours(1),
242          50,
243          base::HistogramBase::kUmaTargetedHistogramFlag);
244  histogram->AddTime(duration);
245}
246
247// Logs a type quality metric.  The primary histogram name is constructed based
248// on |base_name| and |experiment_id|.  The field-specific histogram name also
249// factors in the |field_type|.  Logs a sample of |metric|, which should be in
250// the range [0, |num_possible_metrics|).
251void LogTypeQualityMetric(const std::string& base_name,
252                          const int metric,
253                          const int num_possible_metrics,
254                          const ServerFieldType field_type,
255                          const std::string& experiment_id) {
256  DCHECK_LT(metric, num_possible_metrics);
257
258  std::string histogram_name = base_name;
259  if (!experiment_id.empty())
260    histogram_name += "_" + experiment_id;
261  LogUMAHistogramEnumeration(histogram_name, metric, num_possible_metrics);
262
263  std::string sub_histogram_name = base_name + ".ByFieldType";
264  if (!experiment_id.empty())
265    sub_histogram_name += "_" + experiment_id;
266  const int field_type_group_metric =
267      GetFieldTypeGroupMetric(field_type, metric, num_possible_metrics);
268  const int num_field_type_group_metrics =
269      num_possible_metrics * NUM_FIELD_TYPE_GROUPS_FOR_METRICS;
270  LogUMAHistogramEnumeration(sub_histogram_name,
271                             field_type_group_metric,
272                             num_field_type_group_metrics);
273}
274
275void LogServerExperimentId(const std::string& histogram_name,
276                           const std::string& experiment_id) {
277  ServerExperiment metric = UNKNOWN_EXPERIMENT;
278
279  const std::string default_experiment_name =
280      FormStructure(FormData()).server_experiment_id();
281  if (experiment_id.empty())
282    metric = NO_EXPERIMENT;
283  else if (experiment_id == "ar06")
284    metric = ACCEPTANCE_RATIO_06;
285  else if (experiment_id == "ar1")
286    metric = ACCEPTANCE_RATIO_1;
287  else if (experiment_id == "ar2")
288    metric = ACCEPTANCE_RATIO_2;
289  else if (experiment_id == "ar4")
290    metric = ACCEPTANCE_RATIO_4;
291  else if (experiment_id == "ar05wlr15")
292    metric = ACCEPTANCE_RATIO_05_WINNER_LEAD_RATIO_15;
293  else if (experiment_id == "ar05wlr25")
294    metric = ACCEPTANCE_RATIO_05_WINNER_LEAD_RATIO_25;
295  else if (experiment_id == "ar05wr15fs5")
296    metric = ACCEPTANCE_RATIO_05_WINNER_LEAD_RATIO_15_MIN_FORM_SCORE_5;
297  else if (experiment_id == "tbar1")
298    metric = TOOLBAR_DATA_ONLY;
299  else if (experiment_id == "ar04wr3fs4")
300    metric = ACCEPTANCE_RATIO_04_WINNER_LEAD_RATIO_3_MIN_FORM_SCORE_4;
301  else if (experiment_id == default_experiment_name)
302    metric = NO_SERVER_RESPONSE;
303  else if (experiment_id == "fp05")
304    metric = PROBABILITY_PICKER_05;
305  else if (experiment_id == "fp025")
306    metric = PROBABILITY_PICKER_025;
307  else if (experiment_id == "fp05cc03")
308    metric = PROBABILITY_PICKER_025_CC_THRESHOLD_03;
309  else if (experiment_id == "fp05cco03")
310    metric = PROBABILITY_PICKER_025_CONTEXTUAL_CC_THRESHOLD_03;
311  else if (experiment_id == "fp05cco03cstd")
312    metric = PROBABILITY_PICKER_025_CONTEXTUAL_CC_THRESHOLD_03_WITH_FALLBACK;
313  else if (experiment_id == "fp05cc03e1")
314    metric = PROBABILITY_PICKER_05_CC_NAME_THRESHOLD_03_EXPERIMENT_1;
315
316  DCHECK_LT(metric, NUM_SERVER_EXPERIMENTS);
317  LogUMAHistogramEnumeration(histogram_name, metric, NUM_SERVER_EXPERIMENTS);
318}
319
320}  // namespace
321
322AutofillMetrics::AutofillMetrics() {
323}
324
325AutofillMetrics::~AutofillMetrics() {
326}
327
328void AutofillMetrics::LogCreditCardInfoBarMetric(InfoBarMetric metric) const {
329  DCHECK_LT(metric, NUM_INFO_BAR_METRICS);
330
331  UMA_HISTOGRAM_ENUMERATION("Autofill.CreditCardInfoBar", metric,
332                            NUM_INFO_BAR_METRICS);
333}
334
335void AutofillMetrics::LogDialogDismissalState(
336    DialogDismissalState state) const {
337  UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.DismissalState",
338                            state, NUM_DIALOG_DISMISSAL_STATES);
339}
340
341void AutofillMetrics::LogDialogInitialUserState(
342    DialogInitialUserStateMetric user_type) const {
343  UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.InitialUserState",
344                            user_type, NUM_DIALOG_INITIAL_USER_STATE_METRICS);
345}
346
347void AutofillMetrics::LogDialogLatencyToShow(
348    const base::TimeDelta& duration) const {
349  LogUMAHistogramTimes("RequestAutocomplete.UiLatencyToShow", duration);
350}
351
352void AutofillMetrics::LogDialogPopupEvent(DialogPopupEvent event) const {
353  UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.PopupInDialog",
354                            event, NUM_DIALOG_POPUP_EVENTS);
355}
356
357void AutofillMetrics::LogDialogSecurityMetric(
358    DialogSecurityMetric metric) const {
359  UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.Security",
360                            metric, NUM_DIALOG_SECURITY_METRICS);
361}
362
363void AutofillMetrics::LogDialogUiDuration(
364    const base::TimeDelta& duration,
365    DialogDismissalAction dismissal_action) const {
366  std::string suffix;
367  switch (dismissal_action) {
368    case DIALOG_ACCEPTED:
369      suffix = "Submit";
370      break;
371
372    case DIALOG_CANCELED:
373      suffix = "Cancel";
374      break;
375  }
376
377  LogUMAHistogramLongTimes("RequestAutocomplete.UiDuration", duration);
378  LogUMAHistogramLongTimes("RequestAutocomplete.UiDuration." + suffix,
379                           duration);
380}
381
382void AutofillMetrics::LogDialogUiEvent(DialogUiEvent event) const {
383  UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.UiEvents", event,
384                            NUM_DIALOG_UI_EVENTS);
385}
386
387void AutofillMetrics::LogWalletErrorMetric(WalletErrorMetric metric) const {
388  UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.WalletErrors", metric,
389                            NUM_WALLET_ERROR_METRICS);
390}
391
392void AutofillMetrics::LogWalletApiCallDuration(
393    WalletApiCallMetric metric,
394    const base::TimeDelta& duration) const {
395  LogUMAHistogramTimes("Wallet.ApiCallDuration." +
396                       WalletApiMetricToString(metric), duration);
397}
398
399void AutofillMetrics::LogWalletMalformedResponseMetric(
400    WalletApiCallMetric metric) const {
401  UMA_HISTOGRAM_ENUMERATION("Wallet.MalformedResponse", metric,
402                            NUM_WALLET_API_CALLS);
403}
404
405void AutofillMetrics::LogWalletRequiredActionMetric(
406      WalletRequiredActionMetric required_action) const {
407  UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.WalletRequiredActions",
408                            required_action, NUM_WALLET_REQUIRED_ACTIONS);
409}
410
411void AutofillMetrics::LogWalletResponseCode(int response_code) const {
412  UMA_HISTOGRAM_SPARSE_SLOWLY("Wallet.ResponseCode", response_code);
413}
414
415void AutofillMetrics::LogDeveloperEngagementMetric(
416    DeveloperEngagementMetric metric) const {
417  DCHECK_LT(metric, NUM_DEVELOPER_ENGAGEMENT_METRICS);
418
419  UMA_HISTOGRAM_ENUMERATION("Autofill.DeveloperEngagement", metric,
420                            NUM_DEVELOPER_ENGAGEMENT_METRICS);
421}
422
423void AutofillMetrics::LogHeuristicTypePrediction(
424    FieldTypeQualityMetric metric,
425    ServerFieldType field_type,
426    const std::string& experiment_id) const {
427  LogTypeQualityMetric("Autofill.Quality.HeuristicType",
428                       metric, NUM_FIELD_TYPE_QUALITY_METRICS,
429                       field_type, experiment_id);
430}
431
432void AutofillMetrics::LogOverallTypePrediction(
433    FieldTypeQualityMetric metric,
434    ServerFieldType field_type,
435    const std::string& experiment_id) const {
436  LogTypeQualityMetric("Autofill.Quality.PredictedType",
437                       metric, NUM_FIELD_TYPE_QUALITY_METRICS,
438                       field_type, experiment_id);
439}
440
441void AutofillMetrics::LogServerTypePrediction(
442    FieldTypeQualityMetric metric,
443    ServerFieldType field_type,
444    const std::string& experiment_id) const {
445  LogTypeQualityMetric("Autofill.Quality.ServerType",
446                       metric, NUM_FIELD_TYPE_QUALITY_METRICS,
447                       field_type, experiment_id);
448}
449
450void AutofillMetrics::LogQualityMetric(QualityMetric metric,
451                                       const std::string& experiment_id) const {
452  DCHECK_LT(metric, NUM_QUALITY_METRICS);
453
454  std::string histogram_name = "Autofill.Quality";
455  if (!experiment_id.empty())
456    histogram_name += "_" + experiment_id;
457
458  LogUMAHistogramEnumeration(histogram_name, metric, NUM_QUALITY_METRICS);
459}
460
461void AutofillMetrics::LogServerQueryMetric(ServerQueryMetric metric) const {
462  DCHECK_LT(metric, NUM_SERVER_QUERY_METRICS);
463
464  UMA_HISTOGRAM_ENUMERATION("Autofill.ServerQueryResponse", metric,
465                            NUM_SERVER_QUERY_METRICS);
466}
467
468void AutofillMetrics::LogUserHappinessMetric(UserHappinessMetric metric) const {
469  DCHECK_LT(metric, NUM_USER_HAPPINESS_METRICS);
470
471  UMA_HISTOGRAM_ENUMERATION("Autofill.UserHappiness", metric,
472                            NUM_USER_HAPPINESS_METRICS);
473}
474
475void AutofillMetrics::LogFormFillDurationFromLoadWithAutofill(
476    const base::TimeDelta& duration) const {
477  UMA_HISTOGRAM_CUSTOM_TIMES("Autofill.FillDuration.FromLoad.WithAutofill",
478                             duration,
479                             base::TimeDelta::FromMilliseconds(100),
480                             base::TimeDelta::FromMinutes(10),
481                             50);
482}
483
484void AutofillMetrics::LogFormFillDurationFromLoadWithoutAutofill(
485    const base::TimeDelta& duration) const {
486  UMA_HISTOGRAM_CUSTOM_TIMES("Autofill.FillDuration.FromLoad.WithoutAutofill",
487                             duration,
488                             base::TimeDelta::FromMilliseconds(100),
489                             base::TimeDelta::FromMinutes(10),
490                             50);
491}
492
493void AutofillMetrics::LogFormFillDurationFromInteractionWithAutofill(
494    const base::TimeDelta& duration) const {
495  UMA_HISTOGRAM_CUSTOM_TIMES(
496      "Autofill.FillDuration.FromInteraction.WithAutofill",
497      duration,
498      base::TimeDelta::FromMilliseconds(100),
499      base::TimeDelta::FromMinutes(10),
500      50);
501}
502
503void AutofillMetrics::LogFormFillDurationFromInteractionWithoutAutofill(
504    const base::TimeDelta& duration) const {
505  UMA_HISTOGRAM_CUSTOM_TIMES(
506       "Autofill.FillDuration.FromInteraction.WithoutAutofill",
507       duration,
508       base::TimeDelta::FromMilliseconds(100),
509       base::TimeDelta::FromMinutes(10),
510       50);
511}
512
513void AutofillMetrics::LogIsAutofillEnabledAtStartup(bool enabled) const {
514  UMA_HISTOGRAM_BOOLEAN("Autofill.IsEnabled.Startup", enabled);
515}
516
517void AutofillMetrics::LogIsAutofillEnabledAtPageLoad(bool enabled) const {
518  UMA_HISTOGRAM_BOOLEAN("Autofill.IsEnabled.PageLoad", enabled);
519}
520
521void AutofillMetrics::LogStoredProfileCount(size_t num_profiles) const {
522  UMA_HISTOGRAM_COUNTS("Autofill.StoredProfileCount", num_profiles);
523}
524
525void AutofillMetrics::LogAddressSuggestionsCount(size_t num_suggestions) const {
526  UMA_HISTOGRAM_COUNTS("Autofill.AddressSuggestionsCount", num_suggestions);
527}
528
529void AutofillMetrics::LogServerExperimentIdForQuery(
530    const std::string& experiment_id) const {
531  LogServerExperimentId("Autofill.ServerExperimentId.Query", experiment_id);
532}
533
534void AutofillMetrics::LogServerExperimentIdForUpload(
535    const std::string& experiment_id) const {
536  LogServerExperimentId("Autofill.ServerExperimentId.Upload", experiment_id);
537}
538
539}  // namespace autofill
540