Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(177)

Side by Side Diff: chrome/browser/metrics/metrics_log_serializer.cc

Issue 9474041: Upload UMA data using protocol buffers. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix crash in test Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 "base/base64.h" 7 #include "base/base64.h"
8 #include "base/md5.h" 8 #include "base/md5.h"
9 #include "base/metrics/histogram.h" 9 #include "base/metrics/histogram.h"
10 #include "chrome/browser/browser_process.h" 10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/prefs/pref_service.h" 11 #include "chrome/browser/prefs/pref_service.h"
12 #include "chrome/browser/prefs/scoped_user_pref_update.h" 12 #include "chrome/browser/prefs/scoped_user_pref_update.h"
13 #include "chrome/common/pref_names.h" 13 #include "chrome/common/pref_names.h"
14 14
15 namespace {
16
15 // The number of "initial" logs we're willing to save, and hope to send during 17 // 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 18 // a future Chrome session. Initial logs contain crash stats, and are pretty
17 // small. 19 // small.
18 static const size_t kMaxInitialLogsPersisted = 20; 20 const size_t kMaxInitialLogsPersisted = 20;
19 21
20 // The number of ongoing logs we're willing to save persistently, and hope to 22 // 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 23 // 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 24 // 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 25 // 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 26 // 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. 27 // 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 28 // 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 29 // was not yet been transmitted, and that is normal (to have exactly one
28 // ongoing_log_ at startup). 30 // ongoing_log_ at startup).
29 static const size_t kMaxOngoingLogsPersisted = 8; 31 const size_t kMaxOngoingLogsPersisted = 8;
30 32
31 // We append (2) more elements to persisted lists: the size of the list and a 33 // We append (2) more elements to persisted lists: the size of the list and a
32 // checksum of the elements. 34 // checksum of the elements.
33 static const size_t kChecksumEntryCount = 2; 35 const size_t kChecksumEntryCount = 2;
34 36
35 namespace { 37 // 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 38 // encoding failures for protobuf logs.
37 // on.
38 enum LogStoreStatus { 39 enum LogStoreStatus {
39 STORE_SUCCESS, // Successfully presisted log. 40 STORE_SUCCESS, // Successfully presisted log.
40 ENCODE_FAIL, // Failed to encode log. 41 ENCODE_FAIL, // Failed to encode log.
41 COMPRESS_FAIL, // Failed to compress log. 42 COMPRESS_FAIL, // Failed to compress log.
42 END_STORE_STATUS // Number of bins to use to create the histogram. 43 END_STORE_STATUS // Number of bins to use to create the histogram.
43 }; 44 };
44 45
45 MetricsLogSerializer::LogReadStatus MakeRecallStatusHistogram( 46 MetricsLogSerializer::LogReadStatus MakeRecallStatusHistogram(
46 MetricsLogSerializer::LogReadStatus status) { 47 MetricsLogSerializer::LogReadStatus status,
47 UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecall", status, 48 bool is_xml) {
48 MetricsLogSerializer::END_RECALL_STATUS); 49 if (is_xml) {
50 UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecall",
51 status, MetricsLogSerializer::END_RECALL_STATUS);
52 } else {
53 UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecallProtobufs",
54 status, MetricsLogSerializer::END_RECALL_STATUS);
55 }
49 return status; 56 return status;
50 } 57 }
51 58
52 // TODO(ziadh): Remove this when done with experiment.
53 void MakeStoreStatusHistogram(LogStoreStatus status) { 59 void MakeStoreStatusHistogram(LogStoreStatus status) {
54 UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogStore2", status, 60 UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogStore2", status,
55 END_STORE_STATUS); 61 END_STORE_STATUS);
56 } 62 }
63
57 } // namespace 64 } // namespace
58 65
59 66
60 MetricsLogSerializer::MetricsLogSerializer() {} 67 MetricsLogSerializer::MetricsLogSerializer() {}
61 68
62 MetricsLogSerializer::~MetricsLogSerializer() {} 69 MetricsLogSerializer::~MetricsLogSerializer() {}
63 70
64 void MetricsLogSerializer::SerializeLogs(const std::vector<std::string>& logs, 71 void MetricsLogSerializer::SerializeLogs(
65 MetricsLogManager::LogType log_type) { 72 const std::vector<MetricsLogManager::SerializedLog>& logs,
73 MetricsLogManager::LogType log_type) {
66 PrefService* local_state = g_browser_process->local_state(); 74 PrefService* local_state = g_browser_process->local_state();
67 DCHECK(local_state); 75 DCHECK(local_state);
68 const char* pref = NULL; 76 const char* pref_xml = NULL;
77 const char* pref_proto = NULL;
69 size_t max_store_count = 0; 78 size_t max_store_count = 0;
70 switch (log_type) { 79 switch (log_type) {
71 case MetricsLogManager::INITIAL_LOG: 80 case MetricsLogManager::INITIAL_LOG:
72 pref = prefs::kMetricsInitialLogs; 81 pref_xml = prefs::kMetricsInitialLogsXml;
82 pref_proto = prefs::kMetricsInitialLogsProto;
73 max_store_count = kMaxInitialLogsPersisted; 83 max_store_count = kMaxInitialLogsPersisted;
74 break; 84 break;
75 case MetricsLogManager::ONGOING_LOG: 85 case MetricsLogManager::ONGOING_LOG:
76 pref = prefs::kMetricsOngoingLogs; 86 pref_xml = prefs::kMetricsOngoingLogsXml;
87 pref_proto = prefs::kMetricsOngoingLogsProto;
77 max_store_count = kMaxOngoingLogsPersisted; 88 max_store_count = kMaxOngoingLogsPersisted;
78 break; 89 break;
79 default: 90 default:
80 NOTREACHED(); 91 NOTREACHED();
81 return; 92 return;
82 }; 93 };
83 ListPrefUpdate update(local_state, pref); 94
84 ListValue* pref_list = update.Get(); 95 // Write the XML version.
85 WriteLogsToPrefList(logs, max_store_count, pref_list); 96 ListPrefUpdate update_xml(local_state, pref_xml);
97 WriteLogsToPrefList(logs, true, max_store_count, update_xml.Get());
98
99 // Write the protobuf version.
100 ListPrefUpdate update_proto(local_state, pref_proto);
101 WriteLogsToPrefList(logs, false, max_store_count, update_proto.Get());
86 } 102 }
87 103
88 void MetricsLogSerializer::DeserializeLogs(MetricsLogManager::LogType log_type, 104 void MetricsLogSerializer::DeserializeLogs(
89 std::vector<std::string>* logs) { 105 MetricsLogManager::LogType log_type,
106 std::vector<MetricsLogManager::SerializedLog>* logs) {
90 DCHECK(logs); 107 DCHECK(logs);
91 PrefService* local_state = g_browser_process->local_state(); 108 PrefService* local_state = g_browser_process->local_state();
92 DCHECK(local_state); 109 DCHECK(local_state);
93 110
94 const char* pref = (log_type == MetricsLogManager::INITIAL_LOG) ? 111 const char* pref_xml;
95 prefs::kMetricsInitialLogs : prefs::kMetricsOngoingLogs; 112 const char* pref_proto;
96 const ListValue* unsent_logs = local_state->GetList(pref); 113 if (log_type == MetricsLogManager::INITIAL_LOG) {
97 ReadLogsFromPrefList(*unsent_logs, logs); 114 pref_xml = prefs::kMetricsInitialLogsXml;
115 pref_proto = prefs::kMetricsInitialLogsProto;
116 } else {
117 pref_xml = prefs::kMetricsOngoingLogsXml;
118 pref_proto = prefs::kMetricsOngoingLogsProto;
119 }
120
121 const ListValue* unsent_logs_xml = local_state->GetList(pref_xml);
122 const ListValue* unsent_logs_proto = local_state->GetList(pref_proto);
123 if (ReadLogsFromPrefList(*unsent_logs_xml, true, logs) == RECALL_SUCCESS) {
124 // In order to try to keep the data sent to both servers roughly in sync,
125 // only read the protobuf data if we read the XML data successfully.
126 ReadLogsFromPrefList(*unsent_logs_proto, false, logs);
127 }
98 } 128 }
99 129
100 // static 130 // static
101 void MetricsLogSerializer::WriteLogsToPrefList( 131 void MetricsLogSerializer::WriteLogsToPrefList(
102 const std::vector<std::string>& local_list, 132 const std::vector<MetricsLogManager::SerializedLog>& local_list,
103 const size_t kMaxLocalListSize, 133 bool is_xml,
104 ListValue* list) { 134 size_t max_list_size,
135 base::ListValue* list) {
105 list->Clear(); 136 list->Clear();
106 size_t start = 0; 137 size_t start = 0;
107 if (local_list.size() > kMaxLocalListSize) 138 if (local_list.size() > max_list_size)
108 start = local_list.size() - kMaxLocalListSize; 139 start = local_list.size() - max_list_size;
109 DCHECK(start <= local_list.size()); 140 DCHECK_LE(start, local_list.size());
110 if (local_list.size() <= start) 141 if (local_list.size() <= start)
111 return; 142 return;
112 143
113 // Store size at the beginning of the list. 144 // Store size at the beginning of the list.
114 list->Append(Value::CreateIntegerValue(local_list.size() - start)); 145 list->Append(Value::CreateIntegerValue(local_list.size() - start));
115 146
116 base::MD5Context ctx; 147 base::MD5Context ctx;
117 base::MD5Init(&ctx); 148 base::MD5Init(&ctx);
118 std::string encoded_log; 149 std::string encoded_log;
119 for (std::vector<std::string>::const_iterator it = local_list.begin() + start; 150 for (std::vector<MetricsLogManager::SerializedLog>::const_iterator it =
151 local_list.begin() + start;
120 it != local_list.end(); ++it) { 152 it != local_list.end(); ++it) {
153 const std::string& value = is_xml ? it->xml : it->proto;
121 // We encode the compressed log as Value::CreateStringValue() expects to 154 // We encode the compressed log as Value::CreateStringValue() expects to
122 // take a valid UTF8 string. 155 // take a valid UTF8 string.
123 if (!base::Base64Encode(*it, &encoded_log)) { 156 if (!base::Base64Encode(value, &encoded_log)) {
124 MakeStoreStatusHistogram(ENCODE_FAIL); 157 MakeStoreStatusHistogram(ENCODE_FAIL);
125 list->Clear(); 158 list->Clear();
126 return; 159 return;
127 } 160 }
128 base::MD5Update(&ctx, encoded_log); 161 base::MD5Update(&ctx, encoded_log);
129 list->Append(Value::CreateStringValue(encoded_log)); 162 list->Append(Value::CreateStringValue(encoded_log));
130 } 163 }
131 164
132 // Append hash to the end of the list. 165 // Append hash to the end of the list.
133 base::MD5Digest digest; 166 base::MD5Digest digest;
134 base::MD5Final(&digest, &ctx); 167 base::MD5Final(&digest, &ctx);
135 list->Append(Value::CreateStringValue(base::MD5DigestToBase16(digest))); 168 list->Append(Value::CreateStringValue(base::MD5DigestToBase16(digest)));
136 DCHECK(list->GetSize() >= 3); // Minimum of 3 elements (size, data, hash). 169 DCHECK(list->GetSize() >= 3); // Minimum of 3 elements (size, data, hash).
137 MakeStoreStatusHistogram(STORE_SUCCESS); 170 MakeStoreStatusHistogram(STORE_SUCCESS);
138 } 171 }
139 172
140 // static 173 // static
141 MetricsLogSerializer::LogReadStatus MetricsLogSerializer::ReadLogsFromPrefList( 174 MetricsLogSerializer::LogReadStatus MetricsLogSerializer::ReadLogsFromPrefList(
142 const ListValue& list, 175 const ListValue& list,
143 std::vector<std::string>* local_list) { 176 bool is_xml,
144 DCHECK(local_list->empty()); 177 std::vector<MetricsLogManager::SerializedLog>* local_list) {
145 if (list.GetSize() == 0) 178 if (list.GetSize() == 0)
146 return MakeRecallStatusHistogram(LIST_EMPTY); 179 return MakeRecallStatusHistogram(LIST_EMPTY, is_xml);
147 if (list.GetSize() < 3) 180 if (list.GetSize() < 3)
148 return MakeRecallStatusHistogram(LIST_SIZE_TOO_SMALL); 181 return MakeRecallStatusHistogram(LIST_SIZE_TOO_SMALL, is_xml);
149 182
150 // The size is stored at the beginning of the list. 183 // The size is stored at the beginning of the list.
151 int size; 184 int size;
152 bool valid = (*list.begin())->GetAsInteger(&size); 185 bool valid = (*list.begin())->GetAsInteger(&size);
153 if (!valid) 186 if (!valid)
154 return MakeRecallStatusHistogram(LIST_SIZE_MISSING); 187 return MakeRecallStatusHistogram(LIST_SIZE_MISSING, is_xml);
155
156 // Account for checksum and size included in the list. 188 // Account for checksum and size included in the list.
157 if (static_cast<unsigned int>(size) != 189 if (static_cast<unsigned int>(size) !=
158 list.GetSize() - kChecksumEntryCount) { 190 list.GetSize() - kChecksumEntryCount) {
159 return MakeRecallStatusHistogram(LIST_SIZE_CORRUPTION); 191 return MakeRecallStatusHistogram(LIST_SIZE_CORRUPTION, is_xml);
192 }
193
194 // Allocate strings for all of the logs we are going to read in.
195 // Do this ahead of time so that we can decode the string values directly into
196 // the elements of |local_list|, and thereby avoid making copies of the
197 // serialized logs, which can be fairly large.
198 if (is_xml) {
199 DCHECK(local_list->empty());
200 local_list->resize(size);
201 } else if (local_list->size() != static_cast<size_t>(size)) {
202 return MakeRecallStatusHistogram(XML_PROTO_MISMATCH, false);
160 } 203 }
161 204
162 base::MD5Context ctx; 205 base::MD5Context ctx;
163 base::MD5Init(&ctx); 206 base::MD5Init(&ctx);
164 std::string encoded_log; 207 std::string encoded_log;
165 std::string decoded_log; 208 size_t local_index = 0;
166 for (ListValue::const_iterator it = list.begin() + 1; 209 for (ListValue::const_iterator it = list.begin() + 1;
167 it != list.end() - 1; ++it) { // Last element is the checksum. 210 it != list.end() - 1; // Last element is the checksum.
168 valid = (*it)->GetAsString(&encoded_log); 211 ++it, ++local_index) {
212 bool valid = (*it)->GetAsString(&encoded_log);
169 if (!valid) { 213 if (!valid) {
170 local_list->clear(); 214 local_list->clear();
171 return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION); 215 return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION, is_xml);
172 } 216 }
173 217
174 base::MD5Update(&ctx, encoded_log); 218 base::MD5Update(&ctx, encoded_log);
175 219
220 DCHECK_LT(local_index, local_list->size());
221 std::string& decoded_log = is_xml ?
222 (*local_list)[local_index].xml :
223 (*local_list)[local_index].proto;
176 if (!base::Base64Decode(encoded_log, &decoded_log)) { 224 if (!base::Base64Decode(encoded_log, &decoded_log)) {
177 local_list->clear(); 225 local_list->clear();
178 return MakeRecallStatusHistogram(DECODE_FAIL); 226 return MakeRecallStatusHistogram(DECODE_FAIL, is_xml);
179 } 227 }
180 local_list->push_back(decoded_log);
181 } 228 }
182 229
183 // Verify checksum. 230 // Verify checksum.
184 base::MD5Digest digest; 231 base::MD5Digest digest;
185 base::MD5Final(&digest, &ctx); 232 base::MD5Final(&digest, &ctx);
186 std::string recovered_md5; 233 std::string recovered_md5;
187 // We store the hash at the end of the list. 234 // We store the hash at the end of the list.
188 valid = (*(list.end() - 1))->GetAsString(&recovered_md5); 235 valid = (*(list.end() - 1))->GetAsString(&recovered_md5);
189 if (!valid) { 236 if (!valid) {
190 local_list->clear(); 237 local_list->clear();
191 return MakeRecallStatusHistogram(CHECKSUM_STRING_CORRUPTION); 238 return MakeRecallStatusHistogram(CHECKSUM_STRING_CORRUPTION, is_xml);
192 } 239 }
193 if (recovered_md5 != base::MD5DigestToBase16(digest)) { 240 if (recovered_md5 != base::MD5DigestToBase16(digest)) {
194 local_list->clear(); 241 local_list->clear();
195 return MakeRecallStatusHistogram(CHECKSUM_CORRUPTION); 242 return MakeRecallStatusHistogram(CHECKSUM_CORRUPTION, is_xml);
196 } 243 }
197 return MakeRecallStatusHistogram(RECALL_SUCCESS); 244 return MakeRecallStatusHistogram(RECALL_SUCCESS, is_xml);
198 } 245 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698