linux_aura: Disable the plugin install infobar.
[chromium-blink-merge.git] / components / metrics / persisted_logs.cc
blob006f095281fb0d1a019c7540b1b6be40ecfb9cde
1 // Copyright 2014 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.
5 #include "components/metrics/persisted_logs.h"
7 #include <string>
9 #include "base/base64.h"
10 #include "base/md5.h"
11 #include "base/metrics/histogram.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/prefs/scoped_user_pref_update.h"
14 #include "base/sha1.h"
15 #include "base/timer/elapsed_timer.h"
17 namespace metrics {
19 namespace {
21 // We append (2) more elements to persisted lists: the size of the list and a
22 // checksum of the elements.
23 const size_t kChecksumEntryCount = 2;
25 PersistedLogs::LogReadStatus MakeRecallStatusHistogram(
26 PersistedLogs::LogReadStatus status) {
27 UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecallProtobufs",
28 status, PersistedLogs::END_RECALL_STATUS);
29 return status;
32 } // namespace
34 void PersistedLogs::LogHashPair::SwapLog(std::string* input) {
35 log.swap(*input);
36 if (!log.empty())
37 hash = base::SHA1HashString(log);
38 else
39 hash.clear();
42 void PersistedLogs::LogHashPair::Swap(PersistedLogs::LogHashPair* input) {
43 log.swap(input->log);
44 hash.swap(input->hash);
47 PersistedLogs::PersistedLogs(PrefService* local_state,
48 const char* pref_name,
49 size_t min_log_count,
50 size_t min_log_bytes,
51 size_t max_log_size)
52 : local_state_(local_state),
53 pref_name_(pref_name),
54 min_log_count_(min_log_count),
55 min_log_bytes_(min_log_bytes),
56 max_log_size_(max_log_size),
57 last_provisional_store_index_(-1) {
58 DCHECK(local_state_);
59 // One of the limit arguments must be non-zero.
60 DCHECK(min_log_count_ > 0 || min_log_bytes_ > 0);
63 PersistedLogs::~PersistedLogs() {}
65 void PersistedLogs::SerializeLogs() {
66 // Remove any logs that are over the serialization size limit.
67 if (max_log_size_) {
68 for (std::vector<LogHashPair>::iterator it = list_.begin();
69 it != list_.end();) {
70 size_t log_size = it->log.length();
71 if (log_size > max_log_size_) {
72 UMA_HISTOGRAM_COUNTS("UMA.Large Accumulated Log Not Persisted",
73 static_cast<int>(log_size));
74 it = list_.erase(it);
75 } else {
76 ++it;
80 ListPrefUpdate update(local_state_, pref_name_);
81 WriteLogsToPrefList(update.Get());
84 PersistedLogs::LogReadStatus PersistedLogs::DeserializeLogs() {
85 const base::ListValue* unsent_logs = local_state_->GetList(pref_name_);
86 return ReadLogsFromPrefList(*unsent_logs);
89 void PersistedLogs::StoreLog(std::string* input) {
90 list_.push_back(LogHashPair());
91 list_.back().SwapLog(input);
94 void PersistedLogs::StageLog() {
95 // CHECK, rather than DCHECK, because swap()ing with an empty list causes
96 // hard-to-identify crashes much later.
97 CHECK(!list_.empty());
98 DCHECK(!has_staged_log());
99 staged_log_.Swap(&list_.back());
100 list_.pop_back();
102 // If the staged log was the last provisional store, clear that.
103 if (static_cast<size_t>(last_provisional_store_index_) == list_.size())
104 last_provisional_store_index_ = -1;
105 DCHECK(has_staged_log());
108 void PersistedLogs::DiscardStagedLog() {
109 DCHECK(has_staged_log());
110 staged_log_.log.clear();
113 void PersistedLogs::StoreStagedLogAsUnsent(StoreType store_type) {
114 list_.push_back(LogHashPair());
115 list_.back().Swap(&staged_log_);
116 if (store_type == PROVISIONAL_STORE)
117 last_provisional_store_index_ = list_.size() - 1;
120 void PersistedLogs::DiscardLastProvisionalStore() {
121 if (last_provisional_store_index_ == -1)
122 return;
123 DCHECK_LT(static_cast<size_t>(last_provisional_store_index_), list_.size());
124 list_.erase(list_.begin() + last_provisional_store_index_);
125 last_provisional_store_index_ = -1;
128 void PersistedLogs::WriteLogsToPrefList(base::ListValue* list_value) {
129 list_value->Clear();
131 // Leave the list completely empty if there are no storable values.
132 if (list_.empty())
133 return;
135 size_t start = 0;
136 // If there are too many logs, keep the most recent logs up to the length
137 // limit, and at least to the minimum number of bytes.
138 if (list_.size() > min_log_count_) {
139 start = list_.size();
140 size_t bytes_used = 0;
141 std::vector<LogHashPair>::const_reverse_iterator end = list_.rend();
142 for (std::vector<LogHashPair>::const_reverse_iterator it = list_.rbegin();
143 it != end; ++it) {
144 size_t log_size = it->log.length();
145 if (bytes_used >= min_log_bytes_ &&
146 (list_.size() - start) >= min_log_count_) {
147 break;
149 bytes_used += log_size;
150 --start;
153 DCHECK_LT(start, list_.size());
154 if (start >= list_.size())
155 return;
157 // Store size at the beginning of the list_value.
158 list_value->Append(base::Value::CreateIntegerValue(list_.size() - start));
160 base::MD5Context ctx;
161 base::MD5Init(&ctx);
162 std::string encoded_log;
163 for (std::vector<LogHashPair>::const_iterator it = list_.begin() + start;
164 it != list_.end(); ++it) {
165 // We encode the compressed log as Value::CreateStringValue() expects to
166 // take a valid UTF8 string.
167 base::Base64Encode(it->log, &encoded_log);
168 base::MD5Update(&ctx, encoded_log);
169 list_value->Append(base::Value::CreateStringValue(encoded_log));
172 // Append hash to the end of the list_value.
173 base::MD5Digest digest;
174 base::MD5Final(&digest, &ctx);
175 list_value->Append(base::Value::CreateStringValue(
176 base::MD5DigestToBase16(digest)));
177 // Minimum of 3 elements (size, data, hash).
178 DCHECK_GE(list_value->GetSize(), 3U);
181 PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromPrefList(
182 const base::ListValue& list_value) {
183 if (list_value.GetSize() == 0)
184 return MakeRecallStatusHistogram(LIST_EMPTY);
185 if (list_value.GetSize() <= kChecksumEntryCount)
186 return MakeRecallStatusHistogram(LIST_SIZE_TOO_SMALL);
188 // The size is stored at the beginning of the list_value.
189 int size;
190 bool valid = (*list_value.begin())->GetAsInteger(&size);
191 if (!valid)
192 return MakeRecallStatusHistogram(LIST_SIZE_MISSING);
193 // Account for checksum and size included in the list_value.
194 if (static_cast<size_t>(size) != list_value.GetSize() - kChecksumEntryCount)
195 return MakeRecallStatusHistogram(LIST_SIZE_CORRUPTION);
197 // Allocate strings for all of the logs we are going to read in.
198 // Do this ahead of time so that we can decode the string values directly into
199 // the elements of |list_|, and thereby avoid making copies of the
200 // serialized logs, which can be fairly large.
201 DCHECK(list_.empty());
202 list_.resize(size);
204 base::MD5Context ctx;
205 base::MD5Init(&ctx);
206 std::string encoded_log;
207 size_t local_index = 0;
208 for (base::ListValue::const_iterator it = list_value.begin() + 1;
209 it != list_value.end() - 1; // Last element is the checksum.
210 ++it, ++local_index) {
211 bool valid = (*it)->GetAsString(&encoded_log);
212 if (!valid) {
213 list_.clear();
214 return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION);
217 base::MD5Update(&ctx, encoded_log);
219 std::string log_text;
220 if (!base::Base64Decode(encoded_log, &log_text)) {
221 list_.clear();
222 return MakeRecallStatusHistogram(DECODE_FAIL);
225 DCHECK_LT(local_index, list_.size());
226 list_[local_index].SwapLog(&log_text);
229 // Verify checksum.
230 base::MD5Digest digest;
231 base::MD5Final(&digest, &ctx);
232 std::string recovered_md5;
233 // We store the hash at the end of the list_value.
234 valid = (*(list_value.end() - 1))->GetAsString(&recovered_md5);
235 if (!valid) {
236 list_.clear();
237 return MakeRecallStatusHistogram(CHECKSUM_STRING_CORRUPTION);
239 if (recovered_md5 != base::MD5DigestToBase16(digest)) {
240 list_.clear();
241 return MakeRecallStatusHistogram(CHECKSUM_CORRUPTION);
243 return MakeRecallStatusHistogram(RECALL_SUCCESS);
246 } // namespace metrics