Chromium Code Reviews| 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 |