OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/metrics/metrics_log_serializer.h" | 5 #include "chrome/browser/metrics/metrics_log_serializer.h" |
6 | 6 |
7 #include <utility> | |
8 | |
7 #include "base/base64.h" | 9 #include "base/base64.h" |
8 #include "base/md5.h" | 10 #include "base/md5.h" |
9 #include "base/metrics/histogram.h" | 11 #include "base/metrics/histogram.h" |
10 #include "chrome/browser/browser_process.h" | 12 #include "chrome/browser/browser_process.h" |
11 #include "chrome/browser/prefs/pref_service.h" | 13 #include "chrome/browser/prefs/pref_service.h" |
12 #include "chrome/browser/prefs/scoped_user_pref_update.h" | 14 #include "chrome/browser/prefs/scoped_user_pref_update.h" |
13 #include "chrome/common/pref_names.h" | 15 #include "chrome/common/pref_names.h" |
14 | 16 |
17 namespace { | |
18 | |
15 // The number of "initial" logs we're willing to save, and hope to send during | 19 // The number of "initial" logs we're willing to save, and hope to send during |
16 // a future Chrome session. Initial logs contain crash stats, and are pretty | 20 // a future Chrome session. Initial logs contain crash stats, and are pretty |
17 // small. | 21 // small. |
18 static const size_t kMaxInitialLogsPersisted = 20; | 22 const size_t kMaxInitialLogsPersisted = 20; |
jar (doing other things)
2012/02/23 01:59:18
We should really have a style guide opinion for us
ian fette
2012/02/23 02:08:43
"Unnamed namespaces are allowed and even encourage
jar (doing other things)
2012/02/23 03:03:06
You missed my point (or perhaps I stated it poorly
Ilya Sherman
2012/02/24 02:10:06
I think the relevant style guide rule is this one:
| |
19 | 23 |
20 // The number of ongoing logs we're willing to save persistently, and hope to | 24 // The number of ongoing logs we're willing to save persistently, and hope to |
21 // send during a this or future sessions. Note that each log may be pretty | 25 // send during a this or future sessions. Note that each log may be pretty |
22 // large, as presumably the related "initial" log wasn't sent (probably nothing | 26 // large, as presumably the related "initial" log wasn't sent (probably nothing |
23 // was, as the user was probably off-line). As a result, the log probably kept | 27 // was, as the user was probably off-line). As a result, the log probably kept |
24 // accumulating while the "initial" log was stalled, and couldn't be sent. As a | 28 // accumulating while the "initial" log was stalled, and couldn't be sent. As a |
25 // result, we don't want to save too many of these mega-logs. | 29 // result, we don't want to save too many of these mega-logs. |
26 // A "standard shutdown" will create a small log, including just the data that | 30 // A "standard shutdown" will create a small log, including just the data that |
27 // was not yet been transmitted, and that is normal (to have exactly one | 31 // was not yet been transmitted, and that is normal (to have exactly one |
28 // ongoing_log_ at startup). | 32 // ongoing_log_ at startup). |
29 static const size_t kMaxOngoingLogsPersisted = 8; | 33 const size_t kMaxOngoingLogsPersisted = 8; |
30 | 34 |
31 // We append (2) more elements to persisted lists: the size of the list and a | 35 // We append (2) more elements to persisted lists: the size of the list and a |
32 // checksum of the elements. | 36 // checksum of the elements. |
33 static const size_t kChecksumEntryCount = 2; | 37 const size_t kChecksumEntryCount = 2; |
34 | 38 |
35 namespace { | 39 // TODO(isherman): Remove this histogram once it's confirmed that there are no |
36 // TODO(ziadh): This is here temporarily for a side experiment. Remove later | 40 // encoding failures for protobuf logs. |
37 // on. | |
38 enum LogStoreStatus { | 41 enum LogStoreStatus { |
39 STORE_SUCCESS, // Successfully presisted log. | 42 STORE_SUCCESS, // Successfully presisted log. |
40 ENCODE_FAIL, // Failed to encode log. | 43 ENCODE_FAIL, // Failed to encode log. |
41 COMPRESS_FAIL, // Failed to compress log. | 44 COMPRESS_FAIL, // Failed to compress log. |
42 END_STORE_STATUS // Number of bins to use to create the histogram. | 45 END_STORE_STATUS // Number of bins to use to create the histogram. |
43 }; | 46 }; |
44 | 47 |
45 MetricsLogSerializer::LogReadStatus MakeRecallStatusHistogram( | 48 MetricsLogSerializer::LogReadStatus MakeRecallStatusHistogram( |
46 MetricsLogSerializer::LogReadStatus status) { | 49 MetricsLogSerializer::LogReadStatus status, |
47 UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecall", status, | 50 bool is_xml) { |
48 MetricsLogSerializer::END_RECALL_STATUS); | 51 if (is_xml) { |
52 UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecall", | |
53 status, MetricsLogSerializer::END_RECALL_STATUS); | |
54 } else { | |
55 UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecallProtobufs", | |
56 status, MetricsLogSerializer::END_RECALL_STATUS); | |
57 } | |
49 return status; | 58 return status; |
50 } | 59 } |
51 | 60 |
52 // TODO(ziadh): Remove this when done with experiment. | |
53 void MakeStoreStatusHistogram(LogStoreStatus status) { | 61 void MakeStoreStatusHistogram(LogStoreStatus status) { |
54 UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogStore2", status, | 62 UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogStore2", status, |
55 END_STORE_STATUS); | 63 END_STORE_STATUS); |
56 } | 64 } |
65 | |
57 } // namespace | 66 } // namespace |
58 | 67 |
59 | 68 |
60 MetricsLogSerializer::MetricsLogSerializer() {} | 69 MetricsLogSerializer::MetricsLogSerializer() {} |
61 | 70 |
62 MetricsLogSerializer::~MetricsLogSerializer() {} | 71 MetricsLogSerializer::~MetricsLogSerializer() {} |
63 | 72 |
64 void MetricsLogSerializer::SerializeLogs(const std::vector<std::string>& logs, | 73 void MetricsLogSerializer::SerializeLogs( |
65 MetricsLogManager::LogType log_type) { | 74 const std::vector<std::pair<std::string, std::string> >& logs, |
jar (doing other things)
2012/02/23 01:59:18
This makes it clearer (reading the code) what the
| |
75 MetricsLogManager::LogType log_type) { | |
66 PrefService* local_state = g_browser_process->local_state(); | 76 PrefService* local_state = g_browser_process->local_state(); |
67 DCHECK(local_state); | 77 DCHECK(local_state); |
68 const char* pref = NULL; | 78 const char* pref_xml = NULL; |
79 const char* pref_proto = NULL; | |
69 size_t max_store_count = 0; | 80 size_t max_store_count = 0; |
70 switch (log_type) { | 81 switch (log_type) { |
71 case MetricsLogManager::INITIAL_LOG: | 82 case MetricsLogManager::INITIAL_LOG: |
72 pref = prefs::kMetricsInitialLogs; | 83 pref_xml = prefs::kMetricsInitialLogsXml; |
84 pref_proto = prefs::kMetricsInitialLogsProto; | |
73 max_store_count = kMaxInitialLogsPersisted; | 85 max_store_count = kMaxInitialLogsPersisted; |
74 break; | 86 break; |
75 case MetricsLogManager::ONGOING_LOG: | 87 case MetricsLogManager::ONGOING_LOG: |
76 pref = prefs::kMetricsOngoingLogs; | 88 pref_xml = prefs::kMetricsOngoingLogsXml; |
89 pref_proto = prefs::kMetricsOngoingLogsProto; | |
77 max_store_count = kMaxOngoingLogsPersisted; | 90 max_store_count = kMaxOngoingLogsPersisted; |
78 break; | 91 break; |
79 default: | 92 default: |
80 NOTREACHED(); | 93 NOTREACHED(); |
81 return; | 94 return; |
82 }; | 95 }; |
83 ListPrefUpdate update(local_state, pref); | 96 |
84 ListValue* pref_list = update.Get(); | 97 std::vector<std::string> logs_xml(logs.size()); |
85 WriteLogsToPrefList(logs, max_store_count, pref_list); | 98 std::vector<std::string> logs_proto(logs.size()); |
99 for (size_t i = 0; i < logs.size(); ++i) { | |
100 logs_xml[i] = logs[i].first; | |
jar (doing other things)
2012/02/23 01:59:18
These logs get to be big.... so this copying of st
ian fette
2012/02/23 02:08:43
std::strings are copy on write. Creating this sort
ian fette
2012/02/23 03:02:53
actually, rsleevi reminds me that after GCC 4.6, s
jar (doing other things)
2012/02/23 03:03:06
As noted by Sleevi, this copy-on-write semantic (r
Ryan Sleevi
2012/02/23 03:28:06
The relevant item from C++11 that I mentioned was
Ilya Sherman
2012/02/24 02:10:06
No longer copying the strings needlessly.
| |
101 logs_proto[i] = logs[i].second; | |
102 } | |
103 | |
104 // Write the XML version. | |
105 ListPrefUpdate update_xml(local_state, pref_xml); | |
106 ListValue* pref_list_xml = update_xml.Get(); | |
107 WriteLogsToPrefList(logs_xml, max_store_count, pref_list_xml); | |
108 | |
109 // Write the protobuf version. | |
110 ListPrefUpdate update_proto(local_state, pref_proto); | |
111 ListValue* pref_list_proto = update_proto.Get(); | |
112 WriteLogsToPrefList(logs_proto, max_store_count, pref_list_proto); | |
86 } | 113 } |
87 | 114 |
88 void MetricsLogSerializer::DeserializeLogs(MetricsLogManager::LogType log_type, | 115 void MetricsLogSerializer::DeserializeLogs( |
89 std::vector<std::string>* logs) { | 116 MetricsLogManager::LogType log_type, |
117 std::vector<std::pair<std::string, std::string> >* logs) { | |
90 DCHECK(logs); | 118 DCHECK(logs); |
91 PrefService* local_state = g_browser_process->local_state(); | 119 PrefService* local_state = g_browser_process->local_state(); |
92 DCHECK(local_state); | 120 DCHECK(local_state); |
93 | 121 |
94 const char* pref = (log_type == MetricsLogManager::INITIAL_LOG) ? | 122 const char* pref_xml; |
95 prefs::kMetricsInitialLogs : prefs::kMetricsOngoingLogs; | 123 const char* pref_proto; |
96 const ListValue* unsent_logs = local_state->GetList(pref); | 124 if (log_type == MetricsLogManager::INITIAL_LOG) { |
97 ReadLogsFromPrefList(*unsent_logs, logs); | 125 pref_xml = prefs::kMetricsInitialLogsXml; |
126 pref_proto = prefs::kMetricsInitialLogsProto; | |
127 } else { | |
128 pref_xml = prefs::kMetricsOngoingLogsXml; | |
129 pref_proto = prefs::kMetricsOngoingLogsProto; | |
130 } | |
131 | |
132 const ListValue* unsent_logs_xml = local_state->GetList(pref_xml); | |
133 const ListValue* unsent_logs_proto = local_state->GetList(pref_proto); | |
134 std::vector<std::string> logs_xml; | |
135 std::vector<std::string> logs_proto; | |
136 if (ReadLogsFromPrefList(*unsent_logs_xml, true, &logs_xml) != RECALL_SUCCESS) | |
137 return; | |
138 if (ReadLogsFromPrefList(*unsent_logs_proto, false, &logs_proto) != | |
139 RECALL_SUCCESS) | |
140 return; | |
141 | |
142 if (logs_xml.size() != logs_proto.size()) { | |
143 MakeRecallStatusHistogram(XML_PROTO_MISMATCH, false); | |
144 return; | |
145 } | |
146 | |
147 logs->resize(logs_xml.size()); | |
148 for (size_t i = 0; i < logs->size(); ++i) { | |
149 (*logs)[i] = std::make_pair(logs_xml[i], logs_proto[i]); | |
150 } | |
98 } | 151 } |
99 | 152 |
100 // static | 153 // static |
101 void MetricsLogSerializer::WriteLogsToPrefList( | 154 void MetricsLogSerializer::WriteLogsToPrefList( |
102 const std::vector<std::string>& local_list, | 155 const std::vector<std::string>& local_list, |
103 const size_t kMaxLocalListSize, | 156 const size_t kMaxLocalListSize, |
104 ListValue* list) { | 157 ListValue* list) { |
105 list->Clear(); | 158 list->Clear(); |
106 size_t start = 0; | 159 size_t start = 0; |
107 if (local_list.size() > kMaxLocalListSize) | 160 if (local_list.size() > kMaxLocalListSize) |
108 start = local_list.size() - kMaxLocalListSize; | 161 start = local_list.size() - kMaxLocalListSize; |
109 DCHECK(start <= local_list.size()); | 162 DCHECK_LE(start, local_list.size()); |
110 if (local_list.size() <= start) | 163 if (local_list.size() <= start) |
111 return; | 164 return; |
112 | 165 |
113 // Store size at the beginning of the list. | 166 // Store size at the beginning of the list. |
114 list->Append(Value::CreateIntegerValue(local_list.size() - start)); | 167 list->Append(Value::CreateIntegerValue(local_list.size() - start)); |
115 | 168 |
116 base::MD5Context ctx; | 169 base::MD5Context ctx; |
117 base::MD5Init(&ctx); | 170 base::MD5Init(&ctx); |
118 std::string encoded_log; | 171 std::string encoded_log; |
119 for (std::vector<std::string>::const_iterator it = local_list.begin() + start; | 172 for (std::vector<std::string>::const_iterator it = local_list.begin() + start; |
(...skipping 13 matching lines...) Expand all Loading... | |
133 base::MD5Digest digest; | 186 base::MD5Digest digest; |
134 base::MD5Final(&digest, &ctx); | 187 base::MD5Final(&digest, &ctx); |
135 list->Append(Value::CreateStringValue(base::MD5DigestToBase16(digest))); | 188 list->Append(Value::CreateStringValue(base::MD5DigestToBase16(digest))); |
136 DCHECK(list->GetSize() >= 3); // Minimum of 3 elements (size, data, hash). | 189 DCHECK(list->GetSize() >= 3); // Minimum of 3 elements (size, data, hash). |
137 MakeStoreStatusHistogram(STORE_SUCCESS); | 190 MakeStoreStatusHistogram(STORE_SUCCESS); |
138 } | 191 } |
139 | 192 |
140 // static | 193 // static |
141 MetricsLogSerializer::LogReadStatus MetricsLogSerializer::ReadLogsFromPrefList( | 194 MetricsLogSerializer::LogReadStatus MetricsLogSerializer::ReadLogsFromPrefList( |
142 const ListValue& list, | 195 const ListValue& list, |
196 bool is_xml, | |
143 std::vector<std::string>* local_list) { | 197 std::vector<std::string>* local_list) { |
144 DCHECK(local_list->empty()); | 198 DCHECK(local_list->empty()); |
145 if (list.GetSize() == 0) | 199 if (list.GetSize() == 0) |
146 return MakeRecallStatusHistogram(LIST_EMPTY); | 200 return MakeRecallStatusHistogram(LIST_EMPTY, is_xml); |
147 if (list.GetSize() < 3) | 201 if (list.GetSize() < 3) |
148 return MakeRecallStatusHistogram(LIST_SIZE_TOO_SMALL); | 202 return MakeRecallStatusHistogram(LIST_SIZE_TOO_SMALL, is_xml); |
149 | 203 |
150 // The size is stored at the beginning of the list. | 204 // The size is stored at the beginning of the list. |
151 int size; | 205 int size; |
152 bool valid = (*list.begin())->GetAsInteger(&size); | 206 bool valid = (*list.begin())->GetAsInteger(&size); |
153 if (!valid) | 207 if (!valid) |
154 return MakeRecallStatusHistogram(LIST_SIZE_MISSING); | 208 return MakeRecallStatusHistogram(LIST_SIZE_MISSING, is_xml); |
155 | 209 |
156 // Account for checksum and size included in the list. | 210 // Account for checksum and size included in the list. |
157 if (static_cast<unsigned int>(size) != | 211 if (static_cast<unsigned int>(size) != |
158 list.GetSize() - kChecksumEntryCount) { | 212 list.GetSize() - kChecksumEntryCount) { |
159 return MakeRecallStatusHistogram(LIST_SIZE_CORRUPTION); | 213 return MakeRecallStatusHistogram(LIST_SIZE_CORRUPTION, is_xml); |
160 } | 214 } |
161 | 215 |
162 base::MD5Context ctx; | 216 base::MD5Context ctx; |
163 base::MD5Init(&ctx); | 217 base::MD5Init(&ctx); |
164 std::string encoded_log; | 218 std::string encoded_log; |
165 std::string decoded_log; | 219 std::string decoded_log; |
166 for (ListValue::const_iterator it = list.begin() + 1; | 220 for (ListValue::const_iterator it = list.begin() + 1; |
167 it != list.end() - 1; ++it) { // Last element is the checksum. | 221 it != list.end() - 1; ++it) { // Last element is the checksum. |
168 valid = (*it)->GetAsString(&encoded_log); | 222 valid = (*it)->GetAsString(&encoded_log); |
169 if (!valid) { | 223 if (!valid) { |
170 local_list->clear(); | 224 local_list->clear(); |
171 return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION); | 225 return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION, is_xml); |
172 } | 226 } |
173 | 227 |
174 base::MD5Update(&ctx, encoded_log); | 228 base::MD5Update(&ctx, encoded_log); |
175 | 229 |
176 if (!base::Base64Decode(encoded_log, &decoded_log)) { | 230 if (!base::Base64Decode(encoded_log, &decoded_log)) { |
177 local_list->clear(); | 231 local_list->clear(); |
178 return MakeRecallStatusHistogram(DECODE_FAIL); | 232 return MakeRecallStatusHistogram(DECODE_FAIL, is_xml); |
179 } | 233 } |
180 local_list->push_back(decoded_log); | 234 local_list->push_back(decoded_log); |
181 } | 235 } |
182 | 236 |
183 // Verify checksum. | 237 // Verify checksum. |
184 base::MD5Digest digest; | 238 base::MD5Digest digest; |
185 base::MD5Final(&digest, &ctx); | 239 base::MD5Final(&digest, &ctx); |
186 std::string recovered_md5; | 240 std::string recovered_md5; |
187 // We store the hash at the end of the list. | 241 // We store the hash at the end of the list. |
188 valid = (*(list.end() - 1))->GetAsString(&recovered_md5); | 242 valid = (*(list.end() - 1))->GetAsString(&recovered_md5); |
189 if (!valid) { | 243 if (!valid) { |
190 local_list->clear(); | 244 local_list->clear(); |
191 return MakeRecallStatusHistogram(CHECKSUM_STRING_CORRUPTION); | 245 return MakeRecallStatusHistogram(CHECKSUM_STRING_CORRUPTION, is_xml); |
192 } | 246 } |
193 if (recovered_md5 != base::MD5DigestToBase16(digest)) { | 247 if (recovered_md5 != base::MD5DigestToBase16(digest)) { |
194 local_list->clear(); | 248 local_list->clear(); |
195 return MakeRecallStatusHistogram(CHECKSUM_CORRUPTION); | 249 return MakeRecallStatusHistogram(CHECKSUM_CORRUPTION, is_xml); |
196 } | 250 } |
197 return MakeRecallStatusHistogram(RECALL_SUCCESS); | 251 return MakeRecallStatusHistogram(RECALL_SUCCESS, is_xml); |
198 } | 252 } |
OLD | NEW |