1 // Copyright (c) 2006-2008 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_log.h"
7 #include "base/file_util.h"
8 #include "base/file_version_info.h"
10 #include "base/scoped_ptr.h"
11 #include "base/string_util.h"
12 #include "base/sys_info.h"
13 #include "chrome/browser/autocomplete/autocomplete.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/common/logging_chrome.h"
16 #include "chrome/common/pref_names.h"
17 #include "chrome/common/pref_service.h"
18 #include "googleurl/src/gurl.h"
19 #include "net/base/base64.h"
21 #define OPEN_ELEMENT_FOR_SCOPE(name) ScopedElement scoped_element(this, name)
23 // libxml take xmlChar*, which is unsigned char*
24 inline const unsigned char* UnsignedChar(const char* input
) {
25 return reinterpret_cast<const unsigned char*>(input
);
29 void MetricsLog::RegisterPrefs(PrefService
* local_state
) {
30 local_state
->RegisterListPref(prefs::kStabilityPluginStats
);
33 MetricsLog::MetricsLog(const std::string
& client_id
, int session_id
)
34 : start_time_(Time::Now()),
39 client_id_(client_id
),
40 session_id_(IntToString(session_id
)) {
42 buffer_
= xmlBufferCreate();
45 writer_
= xmlNewTextWriterMemory(buffer_
, 0);
48 int result
= xmlTextWriterSetIndent(writer_
, 2);
52 WriteAttribute("clientid", client_id_
);
57 MetricsLog::~MetricsLog() {
59 xmlFreeTextWriter(writer_
);
62 xmlBufferFree(buffer_
);
65 void MetricsLog::CloseLog() {
69 int result
= xmlTextWriterEndDocument(writer_
);
72 result
= xmlTextWriterFlush(writer_
);
76 int MetricsLog::GetEncodedLogSize() {
81 bool MetricsLog::GetEncodedLog(char* buffer
, int buffer_size
) {
83 if (buffer_size
< GetEncodedLogSize())
86 memcpy(buffer
, buffer_
->content
, GetEncodedLogSize());
90 int MetricsLog::GetElapsedSeconds() {
91 return static_cast<int>((Time::Now() - start_time_
).InSeconds());
94 std::string
MetricsLog::CreateHash(const std::string
& value
) {
97 MD5Update(&ctx
, value
.data(), value
.length());
100 MD5Final(&digest
, &ctx
);
102 unsigned char reverse
[8]; // UMA only uses first 8 chars of hash.
103 DCHECK(arraysize(digest
.a
) >= arraysize(reverse
));
104 for (int i
= 0; i
< arraysize(reverse
); ++i
)
105 reverse
[i
] = digest
.a
[arraysize(reverse
) - i
- 1];
106 LOG(INFO
) << "Metrics: Hash numeric [" << value
<< "]=["
107 << *reinterpret_cast<const uint64
*>(&reverse
[0]) << "]";
108 return std::string(reinterpret_cast<char*>(digest
.a
), arraysize(digest
.a
));
111 std::string
MetricsLog::CreateBase64Hash(const std::string
& string
) {
112 std::string encoded_digest
;
113 if (net::Base64Encode(CreateHash(string
), &encoded_digest
)) {
114 DLOG(INFO
) << "Metrics: Hash [" << encoded_digest
<< "]=[" << string
<< "]";
115 return encoded_digest
;
117 return std::string();
120 void MetricsLog::RecordUserAction(const wchar_t* key
) {
123 std::string command_hash
= CreateBase64Hash(WideToUTF8(key
));
124 if (command_hash
.empty()) {
125 NOTREACHED() << "Unable generate encoded hash of command: " << key
;
129 StartElement("uielement");
130 WriteAttribute("action", "command");
131 WriteAttribute("targetidhash", command_hash
);
133 // TODO(jhughes): Properly track windows.
134 WriteIntAttribute("window", 0);
135 WriteCommonEventAttributes();
141 void MetricsLog::RecordLoadEvent(int window_id
,
143 PageTransition::Type origin
,
145 TimeDelta load_time
) {
148 StartElement("document");
149 WriteAttribute("action", "load");
150 WriteIntAttribute("docid", session_index
);
151 WriteIntAttribute("window", window_id
);
152 WriteAttribute("loadtime", Int64ToString(load_time
.InMilliseconds()));
154 std::string origin_string
;
156 switch (PageTransition::StripQualifier(origin
)) {
157 // TODO(jhughes): Some of these mappings aren't right... we need to add
158 // some values to the server's enum.
159 case PageTransition::LINK
:
160 case PageTransition::MANUAL_SUBFRAME
:
161 origin_string
= "link";
164 case PageTransition::TYPED
:
165 origin_string
= "typed";
168 case PageTransition::AUTO_BOOKMARK
:
169 origin_string
= "bookmark";
172 case PageTransition::AUTO_SUBFRAME
:
173 case PageTransition::RELOAD
:
174 origin_string
= "refresh";
177 case PageTransition::GENERATED
:
178 origin_string
= "global-history";
181 case PageTransition::START_PAGE
:
182 origin_string
= "start-page";
185 case PageTransition::FORM_SUBMIT
:
186 origin_string
= "form-submit";
190 NOTREACHED() << "Received an unknown page transition type: " <<
191 PageTransition::StripQualifier(origin
);
193 if (!origin_string
.empty())
194 WriteAttribute("origin", origin_string
);
196 WriteCommonEventAttributes();
203 const char* MetricsLog::WindowEventTypeToString(WindowEventType type
) {
223 void MetricsLog::RecordWindowEvent(WindowEventType type
,
228 StartElement("window");
229 WriteAttribute("action", WindowEventTypeToString(type
));
230 WriteAttribute("windowid", IntToString(window_id
));
232 WriteAttribute("parent", IntToString(parent_id
));
233 WriteCommonEventAttributes();
239 std::string
MetricsLog::GetCurrentTimeString() {
240 return Uint64ToString(Time::Now().ToTimeT());
243 // These are the attributes that are common to every event.
244 void MetricsLog::WriteCommonEventAttributes() {
245 WriteAttribute("session", session_id_
);
246 WriteAttribute("time", GetCurrentTimeString());
249 void MetricsLog::WriteAttribute(const std::string
& name
,
250 const std::string
& value
) {
252 DCHECK(!name
.empty());
254 int result
= xmlTextWriterWriteAttribute(writer_
,
255 UnsignedChar(name
.c_str()),
256 UnsignedChar(value
.c_str()));
257 DCHECK_GE(result
, 0);
260 void MetricsLog::WriteIntAttribute(const std::string
& name
, int value
) {
261 WriteAttribute(name
, IntToString(value
));
264 void MetricsLog::WriteInt64Attribute(const std::string
& name
, int64 value
) {
265 WriteAttribute(name
, Int64ToString(value
));
268 void MetricsLog::StartElement(const char* name
) {
272 int result
= xmlTextWriterStartElement(writer_
, UnsignedChar(name
));
273 DCHECK_GE(result
, 0);
276 void MetricsLog::EndElement() {
279 int result
= xmlTextWriterEndElement(writer_
);
280 DCHECK_GE(result
, 0);
283 std::string
MetricsLog::GetVersionString() const {
284 scoped_ptr
<FileVersionInfo
> version_info(
285 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
286 if (version_info
.get()) {
287 std::string version
= WideToUTF8(version_info
->product_version());
288 if (!version_info
->is_official_build())
289 version
.append("-devel");
292 NOTREACHED() << "Unable to retrieve version string.";
295 return std::string();
298 std::string
MetricsLog::GetInstallDate() const {
299 PrefService
* pref
= g_browser_process
->local_state();
301 return WideToUTF8(pref
->GetString(prefs::kMetricsClientIDTimestamp
));
308 void MetricsLog::WriteStabilityElement() {
311 PrefService
* pref
= g_browser_process
->local_state();
314 // Get stability attributes out of Local State, zeroing out stored values.
315 // NOTE: This could lead to some data loss if this report isn't successfully
316 // sent, but that's true for all the metrics.
318 StartElement("stability");
320 WriteIntAttribute("launchcount",
321 pref
->GetInteger(prefs::kStabilityLaunchCount
));
322 pref
->SetInteger(prefs::kStabilityLaunchCount
, 0);
323 WriteIntAttribute("crashcount",
324 pref
->GetInteger(prefs::kStabilityCrashCount
));
325 pref
->SetInteger(prefs::kStabilityCrashCount
, 0);
326 WriteIntAttribute("incompleteshutdowncount",
328 prefs::kStabilityIncompleteSessionEndCount
));
329 pref
->SetInteger(prefs::kStabilityIncompleteSessionEndCount
, 0);
330 WriteIntAttribute("pageloadcount",
331 pref
->GetInteger(prefs::kStabilityPageLoadCount
));
332 pref
->SetInteger(prefs::kStabilityPageLoadCount
, 0);
333 WriteIntAttribute("renderercrashcount",
334 pref
->GetInteger(prefs::kStabilityRendererCrashCount
));
335 pref
->SetInteger(prefs::kStabilityRendererCrashCount
, 0);
336 WriteIntAttribute("rendererhangcount",
337 pref
->GetInteger(prefs::kStabilityRendererHangCount
));
338 pref
->SetInteger(prefs::kStabilityRendererHangCount
, 0);
339 WriteIntAttribute("breakpadregistrationok",
340 pref
->GetInteger(prefs::kStabilityBreakpadRegistrationSuccess
));
341 pref
->SetInteger(prefs::kStabilityBreakpadRegistrationSuccess
, 0);
342 WriteIntAttribute("breakpadregistrationfail",
343 pref
->GetInteger(prefs::kStabilityBreakpadRegistrationFail
));
344 pref
->SetInteger(prefs::kStabilityBreakpadRegistrationFail
, 0);
345 WriteIntAttribute("debuggerpresent",
346 pref
->GetInteger(prefs::kStabilityDebuggerPresent
));
347 pref
->SetInteger(prefs::kStabilityDebuggerPresent
, 0);
348 WriteIntAttribute("debuggernotpresent",
349 pref
->GetInteger(prefs::kStabilityDebuggerNotPresent
));
350 pref
->SetInteger(prefs::kStabilityDebuggerNotPresent
, 0);
352 // Uptime is stored as a string, since there's no int64 in Value/JSON.
353 WriteAttribute("uptimesec",
354 WideToUTF8(pref
->GetString(prefs::kStabilityUptimeSec
)));
355 pref
->SetString(prefs::kStabilityUptimeSec
, L
"0");
357 // Now log plugin stability info
358 const ListValue
* plugin_stats_list
= pref
->GetList(
359 prefs::kStabilityPluginStats
);
360 if (plugin_stats_list
) {
361 StartElement("plugins");
362 for (ListValue::const_iterator iter
= plugin_stats_list
->begin();
363 iter
!= plugin_stats_list
->end(); ++iter
) {
364 if (!(*iter
)->IsType(Value::TYPE_DICTIONARY
)) {
368 DictionaryValue
* plugin_dict
= static_cast<DictionaryValue
*>(*iter
);
370 std::wstring plugin_path
;
371 plugin_dict
->GetString(prefs::kStabilityPluginPath
, &plugin_path
);
372 plugin_path
= file_util::GetFilenameFromPath(plugin_path
);
373 if (plugin_path
.empty()) {
378 StartElement("pluginstability");
379 WriteAttribute("filename", CreateBase64Hash(WideToUTF8(plugin_path
)));
382 plugin_dict
->GetInteger(prefs::kStabilityPluginLaunches
, &launches
);
383 WriteIntAttribute("launchcount", launches
);
386 plugin_dict
->GetInteger(prefs::kStabilityPluginInstances
, &instances
);
387 WriteIntAttribute("instancecount", instances
);
390 plugin_dict
->GetInteger(prefs::kStabilityPluginCrashes
, &crashes
);
391 WriteIntAttribute("crashcount", crashes
);
395 pref
->ClearPref(prefs::kStabilityPluginStats
);
402 void MetricsLog::WritePluginList(
403 const std::vector
<WebPluginInfo
>& plugin_list
) {
406 StartElement("plugins");
408 for (std::vector
<WebPluginInfo
>::const_iterator iter
= plugin_list
.begin();
409 iter
!= plugin_list
.end(); ++iter
) {
410 StartElement("plugin");
412 // Plugin name and filename are hashed for the privacy of those
413 // testing unreleased new extensions.
414 WriteAttribute("name", CreateBase64Hash(WideToUTF8((*iter
).name
)));
415 std::wstring filename
= file_util::GetFilenameFromPath((*iter
).file
);
416 WriteAttribute("filename", CreateBase64Hash(WideToUTF8(filename
)));
418 WriteAttribute("version", WideToUTF8((*iter
).version
));
426 void MetricsLog::RecordEnvironment(
427 const std::vector
<WebPluginInfo
>& plugin_list
,
428 const DictionaryValue
* profile_metrics
) {
431 PrefService
* pref
= g_browser_process
->local_state();
433 StartElement("profile");
434 WriteCommonEventAttributes();
436 StartElement("install");
437 WriteAttribute("installdate", GetInstallDate());
438 WriteIntAttribute("buildid", 0); // means that we're using appversion instead
439 WriteAttribute("appversion", GetVersionString());
442 WritePluginList(plugin_list
);
444 WriteStabilityElement();
447 OPEN_ELEMENT_FOR_SCOPE("cpu");
448 WriteAttribute("arch", base::SysInfo::CPUArchitecture());
452 OPEN_ELEMENT_FOR_SCOPE("security");
453 WriteIntAttribute("rendereronsboxdesktop",
454 pref
->GetInteger(prefs::kSecurityRendererOnSboxDesktop
));
455 pref
->SetInteger(prefs::kSecurityRendererOnSboxDesktop
, 0);
457 WriteIntAttribute("rendererondefaultdesktop",
458 pref
->GetInteger(prefs::kSecurityRendererOnDefaultDesktop
));
459 pref
->SetInteger(prefs::kSecurityRendererOnDefaultDesktop
, 0);
463 OPEN_ELEMENT_FOR_SCOPE("memory");
464 WriteIntAttribute("mb", base::SysInfo::AmountOfPhysicalMemoryMB());
468 OPEN_ELEMENT_FOR_SCOPE("os");
469 WriteAttribute("name",
470 base::SysInfo::OperatingSystemName());
471 WriteAttribute("version",
472 base::SysInfo::OperatingSystemVersion());
476 OPEN_ELEMENT_FOR_SCOPE("display");
479 base::SysInfo::GetPrimaryDisplayDimensions(&width
, &height
);
480 WriteIntAttribute("xsize", width
);
481 WriteIntAttribute("ysize", height
);
482 WriteIntAttribute("screens", base::SysInfo::DisplayCount());
486 OPEN_ELEMENT_FOR_SCOPE("bookmarks");
487 int num_bookmarks_on_bookmark_bar
=
488 pref
->GetInteger(prefs::kNumBookmarksOnBookmarkBar
);
489 int num_folders_on_bookmark_bar
=
490 pref
->GetInteger(prefs::kNumFoldersOnBookmarkBar
);
491 int num_bookmarks_in_other_bookmarks_folder
=
492 pref
->GetInteger(prefs::kNumBookmarksInOtherBookmarkFolder
);
493 int num_folders_in_other_bookmarks_folder
=
494 pref
->GetInteger(prefs::kNumFoldersInOtherBookmarkFolder
);
496 OPEN_ELEMENT_FOR_SCOPE("bookmarklocation");
497 WriteAttribute("name", "full-tree");
498 WriteIntAttribute("foldercount",
499 num_folders_on_bookmark_bar
+ num_folders_in_other_bookmarks_folder
);
500 WriteIntAttribute("itemcount",
501 num_bookmarks_on_bookmark_bar
+
502 num_bookmarks_in_other_bookmarks_folder
);
505 OPEN_ELEMENT_FOR_SCOPE("bookmarklocation");
506 WriteAttribute("name", "toolbar");
507 WriteIntAttribute("foldercount", num_folders_on_bookmark_bar
);
508 WriteIntAttribute("itemcount", num_bookmarks_on_bookmark_bar
);
513 OPEN_ELEMENT_FOR_SCOPE("keywords");
514 WriteIntAttribute("count", pref
->GetInteger(prefs::kNumKeywords
));
518 WriteAllProfilesMetrics(*profile_metrics
);
520 EndElement(); // profile
523 void MetricsLog::WriteAllProfilesMetrics(
524 const DictionaryValue
& all_profiles_metrics
) {
525 const std::wstring
profile_prefix(prefs::kProfilePrefix
);
526 for (DictionaryValue::key_iterator i
= all_profiles_metrics
.begin_keys();
527 i
!= all_profiles_metrics
.end_keys(); ++i
) {
528 const std::wstring
& key_name
= *i
;
529 if (key_name
.compare(0, profile_prefix
.size(), profile_prefix
) == 0) {
530 DictionaryValue
* profile
;
531 if (all_profiles_metrics
.GetDictionary(key_name
, &profile
))
532 WriteProfileMetrics(key_name
.substr(profile_prefix
.size()), *profile
);
537 void MetricsLog::WriteProfileMetrics(const std::wstring
& profileidhash
,
538 const DictionaryValue
& profile_metrics
) {
539 OPEN_ELEMENT_FOR_SCOPE("userprofile");
540 WriteAttribute("profileidhash", WideToUTF8(profileidhash
));
541 for (DictionaryValue::key_iterator i
= profile_metrics
.begin_keys();
542 i
!= profile_metrics
.end_keys(); ++i
) {
544 if (profile_metrics
.Get(*i
, &value
)) {
546 switch (value
->GetType()) {
547 case Value::TYPE_STRING
: {
548 std::wstring string_value
;
549 if (value
->GetAsString(&string_value
)) {
550 OPEN_ELEMENT_FOR_SCOPE("profileparam");
551 WriteAttribute("name", WideToUTF8(*i
));
552 WriteAttribute("value", WideToUTF8(string_value
));
557 case Value::TYPE_BOOLEAN
: {
559 if (value
->GetAsBoolean(&bool_value
)) {
560 OPEN_ELEMENT_FOR_SCOPE("profileparam");
561 WriteAttribute("name", WideToUTF8(*i
));
562 WriteIntAttribute("value", bool_value
? 1 : 0);
567 case Value::TYPE_INTEGER
: {
569 if (value
->GetAsInteger(&int_value
)) {
570 OPEN_ELEMENT_FOR_SCOPE("profileparam");
571 WriteAttribute("name", WideToUTF8(*i
));
572 WriteIntAttribute("value", int_value
);
585 void MetricsLog::RecordOmniboxOpenedURL(const AutocompleteLog
& log
) {
588 StartElement("uielement");
589 WriteAttribute("action", "autocomplete");
590 WriteAttribute("targetidhash", "");
591 // TODO(kochi): Properly track windows.
592 WriteIntAttribute("window", 0);
593 WriteCommonEventAttributes();
595 StartElement("autocomplete");
597 WriteIntAttribute("typedlength", static_cast<int>(log
.text
.length()));
598 WriteIntAttribute("completedlength",
599 static_cast<int>(log
.inline_autocompleted_length
));
600 WriteIntAttribute("selectedindex", static_cast<int>(log
.selected_index
));
602 for (AutocompleteResult::const_iterator
i(log
.result
.begin());
603 i
!= log
.result
.end(); ++i
) {
604 StartElement("autocompleteitem");
606 WriteAttribute("provider", i
->provider
->name());
607 WriteIntAttribute("relevance", i
->relevance
);
608 WriteIntAttribute("isstarred", i
->starred
? 1 : 0);
609 EndElement(); // autocompleteitem
611 EndElement(); // autocomplete
612 EndElement(); // uielement
617 // TODO(JAR): A The following should really be part of the histogram class.
618 // Internal state is being needlessly exposed, and it would be hard to reuse
619 // this code. If we moved this into the Histogram class, then we could use
620 // the same infrastructure for logging StatsCounters, RatesCounters, etc.
621 void MetricsLog::RecordHistogramDelta(const Histogram
& histogram
,
622 const Histogram::SampleSet
& snapshot
) {
624 DCHECK(0 != snapshot
.TotalCount());
625 snapshot
.CheckSize(histogram
);
627 // We will ignore the MAX_INT/infinite value in the last element of range[].
629 OPEN_ELEMENT_FOR_SCOPE("histogram");
631 WriteAttribute("name", CreateBase64Hash(histogram
.histogram_name()));
633 WriteInt64Attribute("sum", snapshot
.sum());
634 WriteInt64Attribute("sumsquares", snapshot
.square_sum());
636 for (size_t i
= 0; i
< histogram
.bucket_count(); i
++) {
637 if (snapshot
.counts(i
)) {
638 OPEN_ELEMENT_FOR_SCOPE("histogrambucket");
639 WriteIntAttribute("min", histogram
.ranges(i
));
640 WriteIntAttribute("max", histogram
.ranges(i
+ 1));
641 WriteIntAttribute("count", snapshot
.counts(i
));