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

Side by Side Diff: chrome/common/metrics/metrics_log_base.cc

Issue 9232071: Upload UMA data using protocol buffers. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 11 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) 2012 The Chromium Authors. All rights reserved. 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 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/common/metrics/metrics_log_base.h" 5 #include "chrome/common/metrics/metrics_log_base.h"
6 6
7 #include "base/base64.h" 7 #include "base/base64.h"
8 #include "base/basictypes.h" 8 #include "base/basictypes.h"
9 #include "base/md5.h" 9 #include "base/md5.h"
10 #include "base/perftimer.h" 10 #include "base/perftimer.h"
11 #include "base/string_number_conversions.h" 11 #include "base/string_number_conversions.h"
12 #include "base/sys_byteorder.h"
12 #include "base/sys_info.h" 13 #include "base/sys_info.h"
13 #include "base/third_party/nspr/prtime.h" 14 #include "base/third_party/nspr/prtime.h"
14 #include "base/utf_string_conversions.h" 15 #include "base/utf_string_conversions.h"
15 #include "chrome/common/logging_chrome.h" 16 #include "chrome/common/logging_chrome.h"
17 #include "chrome/common/metrics/proto/histogram_event.pb.h"
18 #include "chrome/common/metrics/proto/user_action_event.pb.h"
16 #include "libxml/xmlwriter.h" 19 #include "libxml/xmlwriter.h"
17 20
18 #define OPEN_ELEMENT_FOR_SCOPE(name) ScopedElement scoped_element(this, name) 21 #define OPEN_ELEMENT_FOR_SCOPE(name) ScopedElement scoped_element(this, name)
19 22
20 using base::Histogram; 23 using base::Histogram;
21 using base::Time; 24 using base::Time;
22 using base::TimeDelta; 25 using base::TimeDelta;
23 26 using metrics::HistogramEventProto;
24 // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx 27 using metrics::UserActionEventProto;
25 #if defined(OS_WIN)
26 extern "C" IMAGE_DOS_HEADER __ImageBase;
27 #endif
28 28
29 namespace { 29 namespace {
30 30
31 // libxml take xmlChar*, which is unsigned char* 31 // libxml take xmlChar*, which is unsigned char*
32 inline const unsigned char* UnsignedChar(const char* input) { 32 inline const unsigned char* UnsignedChar(const char* input) {
33 return reinterpret_cast<const unsigned char*>(input); 33 return reinterpret_cast<const unsigned char*>(input);
34 } 34 }
35 35
36 // Any id less than 16 bytes is considered to be a testing id.
37 bool IsTestingID(const std::string& id) {
38 return id.size() < 16;
39 }
40
41 // Converts the 8-byte prefix of an MD5 hash into a uint64 value.
42 inline uint64 HashToUInt64(const std::string& hash) {
43 uint64 value;
44 DCHECK_GE(hash.size(), sizeof(value));
45 memcpy(&value, hash.data(), sizeof(value));
46 return base::htonll(value);
47 }
48
49 // Creates an MD5 hash of the given value, and returns hash as a byte buffer
50 // encoded as an std::string.
51 std::string CreateHash(const std::string& value) {
52 base::MD5Context context;
53 base::MD5Init(&context);
54 base::MD5Update(&context, value);
55
56 base::MD5Digest digest;
57 base::MD5Final(&digest, &context);
58
59 std::string hash(reinterpret_cast<char*>(digest.a), arraysize(digest.a));
60
61 // The following log is VERY helpful when folks add some named histogram into
62 // the code, but forgot to update the descriptive list of histograms. When
63 // that happens, all we get to see (server side) is a hash of the histogram
64 // name. We can then use this logging to find out what histogram name was
65 // being hashed to a given MD5 value by just running the version of Chromium
66 // in question with --enable-logging.
67 DVLOG(1) << "Metrics: Hash numeric [" << value << "]=[" << HashToUInt64(hash)
68 << "]";
69
70 return hash;
71 }
72
36 } // namespace 73 } // namespace
37 74
38 class MetricsLogBase::XmlWrapper { 75 class MetricsLogBase::XmlWrapper {
39 public: 76 public:
40 XmlWrapper() 77 XmlWrapper()
41 : doc_(NULL), 78 : doc_(NULL),
42 buffer_(NULL), 79 buffer_(NULL),
43 writer_(NULL) { 80 writer_(NULL) {
44 buffer_ = xmlBufferCreate(); 81 buffer_ = xmlBufferCreate();
45 DCHECK(buffer_); 82 DCHECK(buffer_);
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
85 }; 122 };
86 123
87 MetricsLogBase::MetricsLogBase(const std::string& client_id, int session_id, 124 MetricsLogBase::MetricsLogBase(const std::string& client_id, int session_id,
88 const std::string& version_string) 125 const std::string& version_string)
89 : start_time_(Time::Now()), 126 : start_time_(Time::Now()),
90 client_id_(client_id), 127 client_id_(client_id),
91 session_id_(base::IntToString(session_id)), 128 session_id_(base::IntToString(session_id)),
92 locked_(false), 129 locked_(false),
93 xml_wrapper_(new XmlWrapper), 130 xml_wrapper_(new XmlWrapper),
94 num_events_(0) { 131 num_events_(0) {
132 int64_t build_time = GetBuildTime();
95 133
134 // Write the XML version.
96 StartElement("log"); 135 StartElement("log");
97 WriteAttribute("clientid", client_id_); 136 WriteAttribute("clientid", client_id_);
98 WriteInt64Attribute("buildtime", GetBuildTime()); 137 WriteInt64Attribute("buildtime", build_time);
99 WriteAttribute("appversion", version_string); 138 WriteAttribute("appversion", version_string);
139
140 // Write the protobuf version.
141 if (IsTestingID(client_id_)) {
142 uma_proto_.set_user_id(0);
143 } else {
144 uma_proto_.set_user_id(HashToUInt64(CreateHash(client_id)));
145 }
146 uma_proto_.set_session_id(session_id);
147 uma_proto_.mutable_system_profile()->set_build_timestamp(build_time);
148 uma_proto_.mutable_system_profile()->set_app_version(version_string);
100 } 149 }
101 150
102 MetricsLogBase::~MetricsLogBase() { 151 MetricsLogBase::~MetricsLogBase() {
103 if (!locked_) { 152 if (!locked_) {
104 locked_ = true; 153 locked_ = true;
105 int result = xmlTextWriterEndDocument(xml_wrapper_->writer()); 154 int result = xmlTextWriterEndDocument(xml_wrapper_->writer());
106 DCHECK_GE(result, 0); 155 DCHECK_GE(result, 0);
107 } 156 }
108 delete xml_wrapper_; 157 delete xml_wrapper_;
109 xml_wrapper_ = NULL; 158 xml_wrapper_ = NULL;
(...skipping 10 matching lines...) Expand all
120 DCHECK_GE(result, 0); 169 DCHECK_GE(result, 0);
121 170
122 #if defined(OS_CHROMEOS) 171 #if defined(OS_CHROMEOS)
123 xmlNodePtr root = xmlDocGetRootElement(xml_wrapper_->doc()); 172 xmlNodePtr root = xmlDocGetRootElement(xml_wrapper_->doc());
124 if (!hardware_class_.empty()) { 173 if (!hardware_class_.empty()) {
125 // The hardware class is determined after the first ongoing log is 174 // The hardware class is determined after the first ongoing log is
126 // constructed, so this adds the root element's "hardwareclass" 175 // constructed, so this adds the root element's "hardwareclass"
127 // attribute when the log is closed instead. 176 // attribute when the log is closed instead.
128 xmlNewProp(root, UnsignedChar("hardwareclass"), 177 xmlNewProp(root, UnsignedChar("hardwareclass"),
129 UnsignedChar(hardware_class_.c_str())); 178 UnsignedChar(hardware_class_.c_str()));
179
180 // Write to the protobuf too.
181 uma_proto_.mutable_system_profile()->set_hardware_class(hardware_class_);
130 } 182 }
131 183
184 // TODO(isherman): Why is this done only on ChromeOS?
Ilya Sherman 2012/01/28 08:17:20 Anyone know?
132 // Flattens the XML tree into a character buffer. 185 // Flattens the XML tree into a character buffer.
133 PerfTimer dump_timer; 186 PerfTimer dump_timer;
134 result = xmlNodeDump(xml_wrapper_->buffer(), xml_wrapper_->doc(), 187 result = xmlNodeDump(xml_wrapper_->buffer(), xml_wrapper_->doc(),
135 root, /* level */ 0, /* format */ 1); 188 root, /* level */ 0, /* format */ 1);
136 DCHECK_GE(result, 0); 189 DCHECK_GE(result, 0);
137 UMA_HISTOGRAM_TIMES("UMA.XMLNodeDumpTime", dump_timer.Elapsed()); 190 UMA_HISTOGRAM_TIMES("UMA.XMLNodeDumpTime", dump_timer.Elapsed());
138 191
139 PerfTimer free_timer; 192 PerfTimer free_timer;
140 xml_wrapper_->FreeDocWriter(); 193 xml_wrapper_->FreeDocWriter();
141 UMA_HISTOGRAM_TIMES("UMA.XMLWriterDestructionTime", free_timer.Elapsed()); 194 UMA_HISTOGRAM_TIMES("UMA.XMLWriterDestructionTime", free_timer.Elapsed());
142 #endif // OS_CHROMEOS 195 #endif // OS_CHROMEOS
143 } 196 }
144 197
145 int MetricsLogBase::GetEncodedLogSize() { 198 int MetricsLogBase::GetEncodedLogSizeXml() {
146 DCHECK(locked_); 199 DCHECK(locked_);
147 CHECK(xml_wrapper_); 200 CHECK(xml_wrapper_);
148 CHECK(xml_wrapper_->buffer()); 201 CHECK(xml_wrapper_->buffer());
149 return xml_wrapper_->buffer()->use; 202 return xml_wrapper_->buffer()->use;
150 } 203 }
151 204
152 bool MetricsLogBase::GetEncodedLog(char* buffer, int buffer_size) { 205 bool MetricsLogBase::GetEncodedLogXml(char* buffer, int buffer_size) {
153 DCHECK(locked_); 206 DCHECK(locked_);
154 if (buffer_size < GetEncodedLogSize()) 207 if (buffer_size < GetEncodedLogSizeXml())
155 return false; 208 return false;
156 209
157 memcpy(buffer, xml_wrapper_->buffer()->content, GetEncodedLogSize()); 210 memcpy(buffer, xml_wrapper_->buffer()->content, GetEncodedLogSizeXml());
158 return true; 211 return true;
159 } 212 }
160 213
161 std::string MetricsLogBase::GetEncodedLogString() { 214 std::string MetricsLogBase::GetEncodedLogProto() {
162 DCHECK(locked_); 215 DCHECK(locked_);
163 return std::string(reinterpret_cast<char*>(xml_wrapper_->buffer()->content)); 216 return uma_proto_.SerializeAsString();
164 } 217 }
165 218
166 int MetricsLogBase::GetElapsedSeconds() { 219 int MetricsLogBase::GetElapsedSeconds() {
167 return static_cast<int>((Time::Now() - start_time_).InSeconds()); 220 return static_cast<int>((Time::Now() - start_time_).InSeconds());
168 } 221 }
169 222
170 std::string MetricsLogBase::CreateHash(const std::string& value) { 223 // static
171 base::MD5Context ctx; 224 void MetricsLogBase::CreateHashes(const std::string& string,
172 base::MD5Init(&ctx); 225 std::string* base64_encoded_hash,
173 base::MD5Update(&ctx, value); 226 uint64* numeric_hash) {
227 std::string hash = CreateHash(string);
174 228
175 base::MD5Digest digest; 229 std::string encoded_digest;
176 base::MD5Final(&digest, &ctx); 230 if (base::Base64Encode(hash, &encoded_digest))
231 DVLOG(1) << "Metrics: Hash [" << encoded_digest << "]=[" << string << "]";
177 232
178 uint64 reverse_uint64; 233 *base64_encoded_hash = encoded_digest;
179 // UMA only uses first 8 chars of hash. We use the above uint64 instead 234 *numeric_hash = HashToUInt64(hash);
180 // of a unsigned char[8] so that we don't run into strict aliasing issues
181 // in the LOG statement below when trying to interpret reverse as a uint64.
182 unsigned char* reverse = reinterpret_cast<unsigned char *>(&reverse_uint64);
183 DCHECK(arraysize(digest.a) >= sizeof(reverse_uint64));
184 for (size_t i = 0; i < sizeof(reverse_uint64); ++i)
185 reverse[i] = digest.a[sizeof(reverse_uint64) - i - 1];
186 // The following log is VERY helpful when folks add some named histogram into
187 // the code, but forgot to update the descriptive list of histograms. When
188 // that happens, all we get to see (server side) is a hash of the histogram
189 // name. We can then use this logging to find out what histogram name was
190 // being hashed to a given MD5 value by just running the version of Chromium
191 // in question with --enable-logging.
192 DVLOG(1) << "Metrics: Hash numeric [" << value
193 << "]=[" << reverse_uint64 << "]";
194 return std::string(reinterpret_cast<char*>(digest.a), arraysize(digest.a));
195 }
196
197 std::string MetricsLogBase::CreateBase64Hash(const std::string& string) {
198 std::string encoded_digest;
199 if (base::Base64Encode(CreateHash(string), &encoded_digest)) {
200 DVLOG(1) << "Metrics: Hash [" << encoded_digest << "]=[" << string << "]";
201 return encoded_digest;
202 }
203 return std::string();
204 } 235 }
205 236
206 void MetricsLogBase::RecordUserAction(const char* key) { 237 void MetricsLogBase::RecordUserAction(const char* key) {
207 DCHECK(!locked_); 238 DCHECK(!locked_);
208 239
209 std::string command_hash = CreateBase64Hash(key); 240 std::string base64_hash;
210 if (command_hash.empty()) { 241 uint64 numeric_hash;
242 CreateHashes(key, &base64_hash, &numeric_hash);
243 if (base64_hash.empty()) {
211 NOTREACHED() << "Unable generate encoded hash of command: " << key; 244 NOTREACHED() << "Unable generate encoded hash of command: " << key;
212 return; 245 return;
213 } 246 }
214 247
248 // Write the XML version.
215 OPEN_ELEMENT_FOR_SCOPE("uielement"); 249 OPEN_ELEMENT_FOR_SCOPE("uielement");
216 WriteAttribute("action", "command"); 250 WriteAttribute("action", "command");
217 WriteAttribute("targetidhash", command_hash); 251 WriteAttribute("targetidhash", base64_hash);
252
253 // Write the protobuf version.
254 UserActionEventProto* user_action = uma_proto_.add_user_action_event();
255 user_action->set_name_hash(numeric_hash);
256 user_action->set_time(Time::Now().ToTimeT());
218 257
219 // TODO(jhughes): Properly track windows. 258 // TODO(jhughes): Properly track windows.
220 WriteIntAttribute("window", 0); 259 WriteIntAttribute("window", 0);
221 WriteCommonEventAttributes(); 260 WriteCommonEventAttributes();
222 261
223 ++num_events_; 262 ++num_events_;
224 } 263 }
225 264
226 void MetricsLogBase::RecordLoadEvent(int window_id, 265 void MetricsLogBase::RecordLoadEvent(int window_id,
227 const GURL& url, 266 const GURL& url,
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
303 return base::Uint64ToString(Time::Now().ToTimeT()); 342 return base::Uint64ToString(Time::Now().ToTimeT());
304 } 343 }
305 344
306 // These are the attributes that are common to every event. 345 // These are the attributes that are common to every event.
307 void MetricsLogBase::WriteCommonEventAttributes() { 346 void MetricsLogBase::WriteCommonEventAttributes() {
308 WriteAttribute("session", session_id_); 347 WriteAttribute("session", session_id_);
309 WriteAttribute("time", GetCurrentTimeString()); 348 WriteAttribute("time", GetCurrentTimeString());
310 } 349 }
311 350
312 void MetricsLogBase::WriteAttribute(const std::string& name, 351 void MetricsLogBase::WriteAttribute(const std::string& name,
313 const std::string& value) { 352 const std::string& value) {
314 DCHECK(!locked_); 353 DCHECK(!locked_);
315 DCHECK(!name.empty()); 354 DCHECK(!name.empty());
316 355
317 int result = xmlTextWriterWriteAttribute(xml_wrapper_->writer(), 356 int result = xmlTextWriterWriteAttribute(xml_wrapper_->writer(),
318 UnsignedChar(name.c_str()), 357 UnsignedChar(name.c_str()),
319 UnsignedChar(value.c_str())); 358 UnsignedChar(value.c_str()));
320 DCHECK_GE(result, 0); 359 DCHECK_GE(result, 0);
321 } 360 }
322 361
323 void MetricsLogBase::WriteIntAttribute(const std::string& name, int value) { 362 void MetricsLogBase::WriteIntAttribute(const std::string& name, int value) {
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
380 const Histogram& histogram, 419 const Histogram& histogram,
381 const Histogram::SampleSet& snapshot) { 420 const Histogram::SampleSet& snapshot) {
382 DCHECK(!locked_); 421 DCHECK(!locked_);
383 DCHECK_NE(0, snapshot.TotalCount()); 422 DCHECK_NE(0, snapshot.TotalCount());
384 snapshot.CheckSize(histogram); 423 snapshot.CheckSize(histogram);
385 424
386 // We will ignore the MAX_INT/infinite value in the last element of range[]. 425 // We will ignore the MAX_INT/infinite value in the last element of range[].
387 426
388 OPEN_ELEMENT_FOR_SCOPE("histogram"); 427 OPEN_ELEMENT_FOR_SCOPE("histogram");
389 428
390 WriteAttribute("name", CreateBase64Hash(histogram.histogram_name())); 429 std::string base64_name_hash;
430 uint64 numeric_name_hash;
431 CreateHashes(histogram.histogram_name(),
432 &base64_name_hash,
433 &numeric_name_hash);
434
435 // Write the XML version.
436 WriteAttribute("name", base64_name_hash);
391 437
392 WriteInt64Attribute("sum", snapshot.sum()); 438 WriteInt64Attribute("sum", snapshot.sum());
393 // TODO(jar): Remove sumsquares when protobuffer accepts this as optional. 439 // TODO(jar): Remove sumsquares when protobuffer accepts this as optional.
394 WriteInt64Attribute("sumsquares", 0); 440 WriteInt64Attribute("sumsquares", 0);
395 441
396 for (size_t i = 0; i < histogram.bucket_count(); i++) { 442 for (size_t i = 0; i < histogram.bucket_count(); i++) {
397 if (snapshot.counts(i)) { 443 if (snapshot.counts(i)) {
398 OPEN_ELEMENT_FOR_SCOPE("histogrambucket"); 444 OPEN_ELEMENT_FOR_SCOPE("histogrambucket");
399 WriteIntAttribute("min", histogram.ranges(i)); 445 WriteIntAttribute("min", histogram.ranges(i));
400 WriteIntAttribute("max", histogram.ranges(i + 1)); 446 WriteIntAttribute("max", histogram.ranges(i + 1));
401 WriteIntAttribute("count", snapshot.counts(i)); 447 WriteIntAttribute("count", snapshot.counts(i));
402 } 448 }
403 } 449 }
450
451 // Write the protobuf version.
452 HistogramEventProto* histogram_proto = uma_proto_.add_histogram_event();
453 histogram_proto->set_name_hash(numeric_name_hash);
454 histogram_proto->set_sum(snapshot.sum());
455
456 for (size_t i = 0; i < histogram.bucket_count(); ++i) {
457 if (snapshot.counts(i)) {
458 HistogramEventProto::Bucket* bucket = histogram_proto->add_bucket();
459 bucket->set_min(histogram.ranges(i));
460 bucket->set_max(histogram.ranges(i + 1));
461 bucket->set_bucket_index(i);
462 bucket->set_count(snapshot.counts(i));
463 }
464 }
404 } 465 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698