1// Copyright (c) 2011 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 "chrome/browser/spellchecker/spellcheck_host_metrics.h"
6
7#include "base/md5.h"
8#include "base/metrics/histogram.h"
9
10SpellCheckHostMetrics::SpellCheckHostMetrics()
11    : misspelled_word_count_(0),
12      last_misspelled_word_count_(-1),
13      spellchecked_word_count_(0),
14      last_spellchecked_word_count_(-1),
15      suggestion_show_count_(0),
16      last_suggestion_show_count_(-1),
17      replaced_word_count_(0),
18      last_replaced_word_count_(-1),
19      last_unique_word_count_(-1),
20      start_time_(base::TimeTicks::Now()) {
21  const uint64 kHistogramTimerDurationInMinutes = 30;
22  recording_timer_.Start(FROM_HERE,
23      base::TimeDelta::FromMinutes(kHistogramTimerDurationInMinutes),
24      this, &SpellCheckHostMetrics::OnHistogramTimerExpired);
25  RecordWordCounts();
26}
27
28SpellCheckHostMetrics::~SpellCheckHostMetrics() {
29}
30
31// static
32void SpellCheckHostMetrics::RecordCustomWordCountStats(size_t count) {
33  UMA_HISTOGRAM_COUNTS("SpellCheck.CustomWords", count);
34}
35
36void SpellCheckHostMetrics::RecordEnabledStats(bool enabled) {
37  UMA_HISTOGRAM_BOOLEAN("SpellCheck.Enabled", enabled);
38  // Because SpellCheckHost is instantiated lazily, the size of
39  // custom dictionary is unknown at this time. We mark it as -1 and
40  // record actual value later. See SpellCheckHost for more detail.
41  if (enabled)
42    RecordCustomWordCountStats(static_cast<size_t>(-1));
43}
44
45void SpellCheckHostMetrics::RecordCheckedWordStats(const base::string16& word,
46                                                   bool misspell) {
47  spellchecked_word_count_++;
48  if (misspell) {
49    misspelled_word_count_++;
50    // If an user misspelled, that user should be counted as a part of
51    // the population.  So we ensure to instantiate the histogram
52    // entries here at the first time.
53    if (misspelled_word_count_ == 1)
54      RecordReplacedWordStats(0);
55  }
56
57  int percentage = (100 * misspelled_word_count_) / spellchecked_word_count_;
58  UMA_HISTOGRAM_PERCENTAGE("SpellCheck.MisspellRatio", percentage);
59
60  // Collects actual number of checked words, excluding duplication.
61  base::MD5Digest digest;
62  base::MD5Sum(reinterpret_cast<const unsigned char*>(word.c_str()),
63         word.size() * sizeof(base::char16), &digest);
64  checked_word_hashes_.insert(base::MD5DigestToBase16(digest));
65
66  RecordWordCounts();
67}
68
69void SpellCheckHostMetrics::OnHistogramTimerExpired() {
70  if (0 < spellchecked_word_count_) {
71    // Collects word checking rate, which is represented
72    // as a word count per hour.
73    base::TimeDelta since_start = base::TimeTicks::Now() - start_time_;
74    // This shouldn't happen since OnHistogramTimerExpired() is called on
75    // a 30 minute interval. If the time was 0 we will end up dividing by zero.
76    CHECK_NE(0, since_start.InSeconds());
77    size_t checked_words_per_hour = spellchecked_word_count_ *
78        base::TimeDelta::FromHours(1).InSeconds() / since_start.InSeconds();
79    UMA_HISTOGRAM_COUNTS("SpellCheck.CheckedWordsPerHour",
80                         checked_words_per_hour);
81  }
82}
83
84void SpellCheckHostMetrics::RecordDictionaryCorruptionStats(bool corrupted) {
85  UMA_HISTOGRAM_BOOLEAN("SpellCheck.DictionaryCorrupted", corrupted);
86}
87
88void SpellCheckHostMetrics::RecordSuggestionStats(int delta) {
89  suggestion_show_count_ += delta;
90  // RecordReplacedWordStats() Calls RecordWordCounts() eventually.
91  RecordReplacedWordStats(0);
92}
93
94void SpellCheckHostMetrics::RecordReplacedWordStats(int delta) {
95  replaced_word_count_ += delta;
96
97  if (misspelled_word_count_) {
98    // zero |misspelled_word_count_| is possible when an extension
99    // gives the misspelling, which is not recorded as a part of this
100    // metrics.
101    int percentage = (100 * replaced_word_count_) / misspelled_word_count_;
102    UMA_HISTOGRAM_PERCENTAGE("SpellCheck.ReplaceRatio", percentage);
103  }
104
105  if (suggestion_show_count_) {
106    int percentage = (100 * replaced_word_count_) / suggestion_show_count_;
107    UMA_HISTOGRAM_PERCENTAGE("SpellCheck.SuggestionHitRatio", percentage);
108  }
109
110  RecordWordCounts();
111}
112
113void SpellCheckHostMetrics::RecordWordCounts() {
114  if (spellchecked_word_count_ != last_spellchecked_word_count_) {
115    DCHECK(spellchecked_word_count_ > last_spellchecked_word_count_);
116    UMA_HISTOGRAM_COUNTS("SpellCheck.CheckedWords", spellchecked_word_count_);
117    last_spellchecked_word_count_ = spellchecked_word_count_;
118  }
119
120  if (misspelled_word_count_ != last_misspelled_word_count_) {
121    DCHECK(misspelled_word_count_ > last_misspelled_word_count_);
122    UMA_HISTOGRAM_COUNTS("SpellCheck.MisspelledWords", misspelled_word_count_);
123    last_misspelled_word_count_ = misspelled_word_count_;
124  }
125
126  if (replaced_word_count_ != last_replaced_word_count_) {
127    DCHECK(replaced_word_count_ > last_replaced_word_count_);
128    UMA_HISTOGRAM_COUNTS("SpellCheck.ReplacedWords", replaced_word_count_);
129    last_replaced_word_count_ = replaced_word_count_;
130  }
131
132  if (((int)checked_word_hashes_.size()) != last_unique_word_count_) {
133    DCHECK((int)checked_word_hashes_.size() > last_unique_word_count_);
134    UMA_HISTOGRAM_COUNTS("SpellCheck.UniqueWords", checked_word_hashes_.size());
135    last_unique_word_count_ = checked_word_hashes_.size();
136  }
137
138  if (suggestion_show_count_ != last_suggestion_show_count_) {
139    DCHECK(suggestion_show_count_ > last_suggestion_show_count_);
140    UMA_HISTOGRAM_COUNTS("SpellCheck.ShownSuggestions", suggestion_show_count_);
141    last_suggestion_show_count_ = suggestion_show_count_;
142  }
143}
144
145void SpellCheckHostMetrics::RecordSpellingServiceStats(bool enabled) {
146  UMA_HISTOGRAM_BOOLEAN("SpellCheck.SpellingService.Enabled", enabled);
147}
148