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.
5 #include "chrome/browser/metrics/metrics_log_serializer.h"
9 #include "base/base64.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 "chrome/browser/browser_process.h"
15 #include "chrome/common/pref_names.h"
17 using metrics::MetricsLogBase
;
18 using metrics::MetricsLogManager
;
22 // The number of "initial" logs to save, and hope to send during a future Chrome
23 // session. Initial logs contain crash stats, and are pretty small.
24 const size_t kInitialLogsPersistLimit
= 20;
26 // The number of ongoing logs to save persistently, and hope to
27 // send during a this or future sessions. Note that each log may be pretty
28 // large, as presumably the related "initial" log wasn't sent (probably nothing
29 // was, as the user was probably off-line). As a result, the log probably kept
30 // accumulating while the "initial" log was stalled, and couldn't be sent. As a
31 // result, we don't want to save too many of these mega-logs.
32 // A "standard shutdown" will create a small log, including just the data that
33 // was not yet been transmitted, and that is normal (to have exactly one
34 // ongoing_log_ at startup).
35 const size_t kOngoingLogsPersistLimit
= 8;
37 // The number of bytes each of initial and ongoing logs that must be stored.
38 // This ensures that a reasonable amount of history will be stored even if there
39 // is a long series of very small logs.
40 const size_t kStorageByteLimitPerLogType
= 300000;
42 // We append (2) more elements to persisted lists: the size of the list and a
43 // checksum of the elements.
44 const size_t kChecksumEntryCount
= 2;
46 MetricsLogSerializer::LogReadStatus
MakeRecallStatusHistogram(
47 MetricsLogSerializer::LogReadStatus status
) {
48 UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecallProtobufs",
49 status
, MetricsLogSerializer::END_RECALL_STATUS
);
56 MetricsLogSerializer::MetricsLogSerializer() {}
58 MetricsLogSerializer::~MetricsLogSerializer() {}
60 void MetricsLogSerializer::SerializeLogs(
61 const std::vector
<MetricsLogManager::SerializedLog
>& logs
,
62 MetricsLogManager::LogType log_type
) {
63 PrefService
* local_state
= g_browser_process
->local_state();
65 const char* pref
= NULL
;
66 size_t store_length_limit
= 0;
68 case MetricsLogBase::INITIAL_STABILITY_LOG
:
69 pref
= prefs::kMetricsInitialLogs
;
70 store_length_limit
= kInitialLogsPersistLimit
;
72 case MetricsLogBase::ONGOING_LOG
:
73 pref
= prefs::kMetricsOngoingLogs
;
74 store_length_limit
= kOngoingLogsPersistLimit
;
76 case MetricsLogBase::NO_LOG
:
81 ListPrefUpdate
update(local_state
, pref
);
82 WriteLogsToPrefList(logs
, store_length_limit
, kStorageByteLimitPerLogType
,
86 void MetricsLogSerializer::DeserializeLogs(
87 MetricsLogManager::LogType log_type
,
88 std::vector
<MetricsLogManager::SerializedLog
>* logs
) {
90 PrefService
* local_state
= g_browser_process
->local_state();
94 if (log_type
== MetricsLogBase::INITIAL_STABILITY_LOG
)
95 pref
= prefs::kMetricsInitialLogs
;
97 pref
= prefs::kMetricsOngoingLogs
;
99 const base::ListValue
* unsent_logs
= local_state
->GetList(pref
);
100 ReadLogsFromPrefList(*unsent_logs
, logs
);
104 void MetricsLogSerializer::WriteLogsToPrefList(
105 const std::vector
<MetricsLogManager::SerializedLog
>& local_list
,
106 size_t list_length_limit
,
108 base::ListValue
* list
) {
109 // One of the limit arguments must be non-zero.
110 DCHECK(list_length_limit
> 0 || byte_limit
> 0);
113 if (local_list
.size() == 0)
117 // If there are too many logs, keep the most recent logs up to the length
118 // limit, and at least to the minimum number of bytes.
119 if (local_list
.size() > list_length_limit
) {
120 start
= local_list
.size();
121 size_t bytes_used
= 0;
122 for (std::vector
<MetricsLogManager::SerializedLog
>::const_reverse_iterator
123 it
= local_list
.rbegin(); it
!= local_list
.rend(); ++it
) {
124 size_t log_size
= it
->log_text().length();
125 if (bytes_used
>= byte_limit
&&
126 (local_list
.size() - start
) >= list_length_limit
)
128 bytes_used
+= log_size
;
132 DCHECK_LT(start
, local_list
.size());
133 if (start
>= local_list
.size())
136 // Store size at the beginning of the list.
137 list
->Append(base::Value::CreateIntegerValue(local_list
.size() - start
));
139 base::MD5Context ctx
;
141 std::string encoded_log
;
142 for (std::vector
<MetricsLogManager::SerializedLog
>::const_iterator it
=
143 local_list
.begin() + start
;
144 it
!= local_list
.end(); ++it
) {
145 // We encode the compressed log as Value::CreateStringValue() expects to
146 // take a valid UTF8 string.
147 base::Base64Encode(it
->log_text(), &encoded_log
);
148 base::MD5Update(&ctx
, encoded_log
);
149 list
->Append(base::Value::CreateStringValue(encoded_log
));
152 // Append hash to the end of the list.
153 base::MD5Digest digest
;
154 base::MD5Final(&digest
, &ctx
);
155 list
->Append(base::Value::CreateStringValue(base::MD5DigestToBase16(digest
)));
156 DCHECK(list
->GetSize() >= 3); // Minimum of 3 elements (size, data, hash).
160 MetricsLogSerializer::LogReadStatus
MetricsLogSerializer::ReadLogsFromPrefList(
161 const base::ListValue
& list
,
162 std::vector
<MetricsLogManager::SerializedLog
>* local_list
) {
163 if (list
.GetSize() == 0)
164 return MakeRecallStatusHistogram(LIST_EMPTY
);
165 if (list
.GetSize() < 3)
166 return MakeRecallStatusHistogram(LIST_SIZE_TOO_SMALL
);
168 // The size is stored at the beginning of the list.
170 bool valid
= (*list
.begin())->GetAsInteger(&size
);
172 return MakeRecallStatusHistogram(LIST_SIZE_MISSING
);
173 // Account for checksum and size included in the list.
174 if (static_cast<unsigned int>(size
) !=
175 list
.GetSize() - kChecksumEntryCount
) {
176 return MakeRecallStatusHistogram(LIST_SIZE_CORRUPTION
);
179 // Allocate strings for all of the logs we are going to read in.
180 // Do this ahead of time so that we can decode the string values directly into
181 // the elements of |local_list|, and thereby avoid making copies of the
182 // serialized logs, which can be fairly large.
183 DCHECK(local_list
->empty());
184 local_list
->resize(size
);
186 base::MD5Context ctx
;
188 std::string encoded_log
;
189 size_t local_index
= 0;
190 for (base::ListValue::const_iterator it
= list
.begin() + 1;
191 it
!= list
.end() - 1; // Last element is the checksum.
192 ++it
, ++local_index
) {
193 bool valid
= (*it
)->GetAsString(&encoded_log
);
196 return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION
);
199 base::MD5Update(&ctx
, encoded_log
);
201 std::string log_text
;
202 if (!base::Base64Decode(encoded_log
, &log_text
)) {
204 return MakeRecallStatusHistogram(DECODE_FAIL
);
207 DCHECK_LT(local_index
, local_list
->size());
208 (*local_list
)[local_index
].SwapLogText(&log_text
);
212 base::MD5Digest digest
;
213 base::MD5Final(&digest
, &ctx
);
214 std::string recovered_md5
;
215 // We store the hash at the end of the list.
216 valid
= (*(list
.end() - 1))->GetAsString(&recovered_md5
);
219 return MakeRecallStatusHistogram(CHECKSUM_STRING_CORRUPTION
);
221 if (recovered_md5
!= base::MD5DigestToBase16(digest
)) {
223 return MakeRecallStatusHistogram(CHECKSUM_CORRUPTION
);
225 return MakeRecallStatusHistogram(RECALL_SUCCESS
);