Fixing build: GetViewContainer changed name from under me. :)
[chromium-blink-merge.git] / chrome / browser / metrics_log.cc
bloba68ca2c543acdcd6bccb730ae7bedef6b0c50602
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"
9 #include "base/md5.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);
28 // static
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()),
35 num_events_(0),
36 locked_(false),
37 buffer_(NULL),
38 writer_(NULL),
39 client_id_(client_id),
40 session_id_(IntToString(session_id)) {
42 buffer_ = xmlBufferCreate();
43 DCHECK(buffer_);
45 writer_ = xmlNewTextWriterMemory(buffer_, 0);
46 DCHECK(writer_);
48 int result = xmlTextWriterSetIndent(writer_, 2);
49 DCHECK_EQ(0, result);
51 StartElement("log");
52 WriteAttribute("clientid", client_id_);
54 DCHECK_GE(result, 0);
57 MetricsLog::~MetricsLog() {
58 if (writer_)
59 xmlFreeTextWriter(writer_);
61 if (buffer_)
62 xmlBufferFree(buffer_);
65 void MetricsLog::CloseLog() {
66 DCHECK(!locked_);
67 locked_ = true;
69 int result = xmlTextWriterEndDocument(writer_);
70 DCHECK(result >= 0);
72 result = xmlTextWriterFlush(writer_);
73 DCHECK(result >= 0);
76 int MetricsLog::GetEncodedLogSize() {
77 DCHECK(locked_);
78 return buffer_->use;
81 bool MetricsLog::GetEncodedLog(char* buffer, int buffer_size) {
82 DCHECK(locked_);
83 if (buffer_size < GetEncodedLogSize())
84 return false;
86 memcpy(buffer, buffer_->content, GetEncodedLogSize());
87 return true;
90 int MetricsLog::GetElapsedSeconds() {
91 return static_cast<int>((Time::Now() - start_time_).InSeconds());
94 std::string MetricsLog::CreateHash(const std::string& value) {
95 MD5Context ctx;
96 MD5Init(&ctx);
97 MD5Update(&ctx, value.data(), value.length());
99 MD5Digest digest;
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) {
121 DCHECK(!locked_);
123 std::string command_hash = CreateBase64Hash(WideToUTF8(key));
124 if (command_hash.empty()) {
125 NOTREACHED() << "Unable generate encoded hash of command: " << key;
126 return;
129 StartElement("uielement");
130 WriteAttribute("action", "command");
131 WriteAttribute("targetidhash", command_hash);
133 // TODO(jhughes): Properly track windows.
134 WriteIntAttribute("window", 0);
135 WriteCommonEventAttributes();
136 EndElement();
138 ++num_events_;
141 void MetricsLog::RecordLoadEvent(int window_id,
142 const GURL& url,
143 PageTransition::Type origin,
144 int session_index,
145 TimeDelta load_time) {
146 DCHECK(!locked_);
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";
162 break;
164 case PageTransition::TYPED:
165 origin_string = "typed";
166 break;
168 case PageTransition::AUTO_BOOKMARK:
169 origin_string = "bookmark";
170 break;
172 case PageTransition::AUTO_SUBFRAME:
173 case PageTransition::RELOAD:
174 origin_string = "refresh";
175 break;
177 case PageTransition::GENERATED:
178 origin_string = "global-history";
179 break;
181 case PageTransition::START_PAGE:
182 origin_string = "start-page";
183 break;
185 case PageTransition::FORM_SUBMIT:
186 origin_string = "form-submit";
187 break;
189 default:
190 NOTREACHED() << "Received an unknown page transition type: " <<
191 PageTransition::StripQualifier(origin);
193 if (!origin_string.empty())
194 WriteAttribute("origin", origin_string);
196 WriteCommonEventAttributes();
197 EndElement();
199 ++num_events_;
202 // static
203 const char* MetricsLog::WindowEventTypeToString(WindowEventType type) {
204 switch (type) {
205 case WINDOW_CREATE:
206 return "create";
208 case WINDOW_OPEN:
209 return "open";
211 case WINDOW_CLOSE:
212 return "close";
214 case WINDOW_DESTROY:
215 return "destroy";
217 default:
218 NOTREACHED();
219 return "unknown";
223 void MetricsLog::RecordWindowEvent(WindowEventType type,
224 int window_id,
225 int parent_id) {
226 DCHECK(!locked_);
228 StartElement("window");
229 WriteAttribute("action", WindowEventTypeToString(type));
230 WriteAttribute("windowid", IntToString(window_id));
231 if (parent_id >= 0)
232 WriteAttribute("parent", IntToString(parent_id));
233 WriteCommonEventAttributes();
234 EndElement();
236 ++num_events_;
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) {
251 DCHECK(!locked_);
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) {
269 DCHECK(!locked_);
270 DCHECK(name);
272 int result = xmlTextWriterStartElement(writer_, UnsignedChar(name));
273 DCHECK_GE(result, 0);
276 void MetricsLog::EndElement() {
277 DCHECK(!locked_);
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");
290 return version;
291 } else {
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();
300 if (pref) {
301 return WideToUTF8(pref->GetString(prefs::kMetricsClientIDTimestamp));
302 } else {
303 NOTREACHED();
304 return "0";
308 void MetricsLog::WriteStabilityElement() {
309 DCHECK(!locked_);
311 PrefService* pref = g_browser_process->local_state();
312 DCHECK(pref);
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",
327 pref->GetInteger(
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)) {
365 NOTREACHED();
366 continue;
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()) {
374 NOTREACHED();
375 continue;
378 StartElement("pluginstability");
379 WriteAttribute("filename", CreateBase64Hash(WideToUTF8(plugin_path)));
381 int launches = 0;
382 plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches);
383 WriteIntAttribute("launchcount", launches);
385 int instances = 0;
386 plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances);
387 WriteIntAttribute("instancecount", instances);
389 int crashes = 0;
390 plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes);
391 WriteIntAttribute("crashcount", crashes);
392 EndElement();
395 pref->ClearPref(prefs::kStabilityPluginStats);
396 EndElement();
399 EndElement();
402 void MetricsLog::WritePluginList(
403 const std::vector<WebPluginInfo>& plugin_list) {
404 DCHECK(!locked_);
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));
420 EndElement();
423 EndElement();
426 void MetricsLog::RecordEnvironment(
427 const std::vector<WebPluginInfo>& plugin_list,
428 const DictionaryValue* profile_metrics) {
429 DCHECK(!locked_);
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());
440 EndElement();
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");
477 int width = 0;
478 int height = 0;
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));
517 if (profile_metrics)
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) {
543 Value* value;
544 if (profile_metrics.Get(*i, &value)) {
545 DCHECK(*i != L"id");
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));
554 break;
557 case Value::TYPE_BOOLEAN: {
558 bool bool_value;
559 if (value->GetAsBoolean(&bool_value)) {
560 OPEN_ELEMENT_FOR_SCOPE("profileparam");
561 WriteAttribute("name", WideToUTF8(*i));
562 WriteIntAttribute("value", bool_value ? 1 : 0);
564 break;
567 case Value::TYPE_INTEGER: {
568 int int_value;
569 if (value->GetAsInteger(&int_value)) {
570 OPEN_ELEMENT_FOR_SCOPE("profileparam");
571 WriteAttribute("name", WideToUTF8(*i));
572 WriteIntAttribute("value", int_value);
574 break;
577 default:
578 NOTREACHED();
579 break;
585 void MetricsLog::RecordOmniboxOpenedURL(const AutocompleteLog& log) {
586 DCHECK(!locked_);
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");
605 if (i->provider)
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
614 ++num_events_;
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) {
623 DCHECK(!locked_);
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));