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
3// found in the LICENSE file.
4
5#include "chrome/browser/metrics/metrics_log.h"
6
7#include <string>
8#include <vector>
9
10#include "base/basictypes.h"
11#include "base/file_util.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/perftimer.h"
14#include "base/string_util.h"
15#include "base/sys_info.h"
16#include "base/third_party/nspr/prtime.h"
17#include "base/time.h"
18#include "base/utf_string_conversions.h"
19#include "chrome/browser/autocomplete/autocomplete.h"
20#include "chrome/browser/autocomplete/autocomplete_match.h"
21#include "chrome/browser/browser_process.h"
22#include "chrome/browser/gpu_data_manager.h"
23#include "chrome/browser/prefs/pref_service.h"
24#include "chrome/common/chrome_version_info.h"
25#include "chrome/common/logging_chrome.h"
26#include "chrome/common/pref_names.h"
27#include "googleurl/src/gurl.h"
28#include "webkit/plugins/npapi/webplugininfo.h"
29
30#define OPEN_ELEMENT_FOR_SCOPE(name) ScopedElement scoped_element(this, name)
31
32// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
33#if defined(OS_WIN)
34extern "C" IMAGE_DOS_HEADER __ImageBase;
35#endif
36
37MetricsLog::MetricsLog(const std::string& client_id, int session_id)
38    : MetricsLogBase(client_id, session_id, MetricsLog::GetVersionString()) {}
39
40MetricsLog::~MetricsLog() {}
41
42// static
43void MetricsLog::RegisterPrefs(PrefService* local_state) {
44  local_state->RegisterListPref(prefs::kStabilityPluginStats);
45}
46
47int64 MetricsLog::GetIncrementalUptime(PrefService* pref) {
48  base::TimeTicks now = base::TimeTicks::Now();
49  static base::TimeTicks last_updated_time(now);
50  int64 incremental_time = (now - last_updated_time).InSeconds();
51  last_updated_time = now;
52
53  if (incremental_time > 0) {
54    int64 metrics_uptime = pref->GetInt64(prefs::kUninstallMetricsUptimeSec);
55    metrics_uptime += incremental_time;
56    pref->SetInt64(prefs::kUninstallMetricsUptimeSec, metrics_uptime);
57  }
58
59  return incremental_time;
60}
61
62std::string MetricsLog::GetInstallDate() const {
63  PrefService* pref = g_browser_process->local_state();
64  if (pref) {
65    return pref->GetString(prefs::kMetricsClientIDTimestamp);
66  } else {
67    NOTREACHED();
68    return "0";
69  }
70}
71
72// static
73std::string MetricsLog::GetVersionString() {
74  chrome::VersionInfo version_info;
75  if (!version_info.is_valid()) {
76    NOTREACHED() << "Unable to retrieve version info.";
77    return std::string();
78  }
79
80  std::string version = version_info.Version();
81  if (!version_extension_.empty())
82    version += version_extension_;
83  if (!version_info.IsOfficialBuild())
84    version.append("-devel");
85  return version;
86}
87
88MetricsLog* MetricsLog::AsMetricsLog() {
89  return this;
90}
91
92void MetricsLog::RecordIncrementalStabilityElements() {
93  DCHECK(!locked_);
94
95  PrefService* pref = g_browser_process->local_state();
96  DCHECK(pref);
97
98  OPEN_ELEMENT_FOR_SCOPE("profile");
99  WriteCommonEventAttributes();
100
101  WriteInstallElement();
102
103  {
104    OPEN_ELEMENT_FOR_SCOPE("stability");  // Minimal set of stability elements.
105    WriteRequiredStabilityAttributes(pref);
106    WriteRealtimeStabilityAttributes(pref);
107
108    WritePluginStabilityElements(pref);
109  }
110}
111
112void MetricsLog::WriteStabilityElement(PrefService* pref) {
113  DCHECK(!locked_);
114
115  DCHECK(pref);
116
117  // Get stability attributes out of Local State, zeroing out stored values.
118  // NOTE: This could lead to some data loss if this report isn't successfully
119  //       sent, but that's true for all the metrics.
120
121  OPEN_ELEMENT_FOR_SCOPE("stability");
122  WriteRequiredStabilityAttributes(pref);
123  WriteRealtimeStabilityAttributes(pref);
124
125  // TODO(jar): The following are all optional, so we *could* optimize them for
126  // values of zero (and not include them).
127  WriteIntAttribute("incompleteshutdowncount",
128                    pref->GetInteger(
129                        prefs::kStabilityIncompleteSessionEndCount));
130  pref->SetInteger(prefs::kStabilityIncompleteSessionEndCount, 0);
131
132
133  WriteIntAttribute("breakpadregistrationok",
134      pref->GetInteger(prefs::kStabilityBreakpadRegistrationSuccess));
135  pref->SetInteger(prefs::kStabilityBreakpadRegistrationSuccess, 0);
136  WriteIntAttribute("breakpadregistrationfail",
137      pref->GetInteger(prefs::kStabilityBreakpadRegistrationFail));
138  pref->SetInteger(prefs::kStabilityBreakpadRegistrationFail, 0);
139  WriteIntAttribute("debuggerpresent",
140                   pref->GetInteger(prefs::kStabilityDebuggerPresent));
141  pref->SetInteger(prefs::kStabilityDebuggerPresent, 0);
142  WriteIntAttribute("debuggernotpresent",
143                   pref->GetInteger(prefs::kStabilityDebuggerNotPresent));
144  pref->SetInteger(prefs::kStabilityDebuggerNotPresent, 0);
145
146  WritePluginStabilityElements(pref);
147}
148
149void MetricsLog::WritePluginStabilityElements(PrefService* pref) {
150  // Now log plugin stability info.
151  const ListValue* plugin_stats_list = pref->GetList(
152      prefs::kStabilityPluginStats);
153  if (plugin_stats_list) {
154    OPEN_ELEMENT_FOR_SCOPE("plugins");
155    for (ListValue::const_iterator iter = plugin_stats_list->begin();
156         iter != plugin_stats_list->end(); ++iter) {
157      if (!(*iter)->IsType(Value::TYPE_DICTIONARY)) {
158        NOTREACHED();
159        continue;
160      }
161      DictionaryValue* plugin_dict = static_cast<DictionaryValue*>(*iter);
162
163      std::string plugin_name;
164      plugin_dict->GetString(prefs::kStabilityPluginName, &plugin_name);
165
166      OPEN_ELEMENT_FOR_SCOPE("pluginstability");
167      // Use "filename" instead of "name", otherwise we need to update the
168      // UMA servers.
169      WriteAttribute("filename", CreateBase64Hash(plugin_name));
170
171      int launches = 0;
172      plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches);
173      WriteIntAttribute("launchcount", launches);
174
175      int instances = 0;
176      plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances);
177      WriteIntAttribute("instancecount", instances);
178
179      int crashes = 0;
180      plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes);
181      WriteIntAttribute("crashcount", crashes);
182    }
183
184    pref->ClearPref(prefs::kStabilityPluginStats);
185  }
186}
187
188void MetricsLog::WriteRequiredStabilityAttributes(PrefService* pref) {
189  // The server refuses data that doesn't have certain values.  crashcount and
190  // launchcount are currently "required" in the "stability" group.
191  WriteIntAttribute("launchcount",
192                    pref->GetInteger(prefs::kStabilityLaunchCount));
193  pref->SetInteger(prefs::kStabilityLaunchCount, 0);
194  WriteIntAttribute("crashcount",
195                    pref->GetInteger(prefs::kStabilityCrashCount));
196  pref->SetInteger(prefs::kStabilityCrashCount, 0);
197}
198
199void MetricsLog::WriteRealtimeStabilityAttributes(PrefService* pref) {
200  // Update the stats which are critical for real-time stability monitoring.
201  // Since these are "optional," only list ones that are non-zero, as the counts
202  // are aggergated (summed) server side.
203
204  int count = pref->GetInteger(prefs::kStabilityPageLoadCount);
205  if (count) {
206    WriteIntAttribute("pageloadcount", count);
207    pref->SetInteger(prefs::kStabilityPageLoadCount, 0);
208  }
209
210  count = pref->GetInteger(prefs::kStabilityRendererCrashCount);
211  if (count) {
212    WriteIntAttribute("renderercrashcount", count);
213    pref->SetInteger(prefs::kStabilityRendererCrashCount, 0);
214  }
215
216  count = pref->GetInteger(prefs::kStabilityExtensionRendererCrashCount);
217  if (count) {
218    WriteIntAttribute("extensionrenderercrashcount", count);
219    pref->SetInteger(prefs::kStabilityExtensionRendererCrashCount, 0);
220  }
221
222  count = pref->GetInteger(prefs::kStabilityRendererHangCount);
223  if (count) {
224    WriteIntAttribute("rendererhangcount", count);
225    pref->SetInteger(prefs::kStabilityRendererHangCount, 0);
226  }
227
228  count = pref->GetInteger(prefs::kStabilityChildProcessCrashCount);
229  if (count) {
230    WriteIntAttribute("childprocesscrashcount", count);
231    pref->SetInteger(prefs::kStabilityChildProcessCrashCount, 0);
232  }
233
234#if defined(OS_CHROMEOS)
235  count = pref->GetInteger(prefs::kStabilityOtherUserCrashCount);
236  if (count) {
237    // TODO(kmixter): Write attribute once log server supports it
238    // and remove warning log.
239    // WriteIntAttribute("otherusercrashcount", count);
240    LOG(WARNING) << "Not yet able to send otherusercrashcount="
241                 << count;
242    pref->SetInteger(prefs::kStabilityOtherUserCrashCount, 0);
243  }
244
245  count = pref->GetInteger(prefs::kStabilityKernelCrashCount);
246  if (count) {
247    // TODO(kmixter): Write attribute once log server supports it
248    // and remove warning log.
249    // WriteIntAttribute("kernelcrashcount", count);
250    LOG(WARNING) << "Not yet able to send kernelcrashcount="
251                 << count;
252    pref->SetInteger(prefs::kStabilityKernelCrashCount, 0);
253  }
254
255  count = pref->GetInteger(prefs::kStabilitySystemUncleanShutdownCount);
256  if (count) {
257    // TODO(kmixter): Write attribute once log server supports it
258    // and remove warning log.
259    // WriteIntAttribute("systemuncleanshutdowns", count);
260    LOG(WARNING) << "Not yet able to send systemuncleanshutdowns="
261                 << count;
262    pref->SetInteger(prefs::kStabilitySystemUncleanShutdownCount, 0);
263  }
264#endif  // OS_CHROMEOS
265
266  int64 recent_duration = GetIncrementalUptime(pref);
267  if (recent_duration)
268    WriteInt64Attribute("uptimesec", recent_duration);
269}
270
271void MetricsLog::WritePluginList(
272    const std::vector<webkit::npapi::WebPluginInfo>& plugin_list) {
273  DCHECK(!locked_);
274
275  OPEN_ELEMENT_FOR_SCOPE("plugins");
276
277  for (std::vector<webkit::npapi::WebPluginInfo>::const_iterator iter =
278           plugin_list.begin();
279       iter != plugin_list.end(); ++iter) {
280    OPEN_ELEMENT_FOR_SCOPE("plugin");
281
282    // Plugin name and filename are hashed for the privacy of those
283    // testing unreleased new extensions.
284    WriteAttribute("name", CreateBase64Hash(UTF16ToUTF8(iter->name)));
285    std::string filename_bytes =
286#if defined(OS_WIN)
287        UTF16ToUTF8(iter->path.BaseName().value());
288#else
289        iter->path.BaseName().value();
290#endif
291    WriteAttribute("filename", CreateBase64Hash(filename_bytes));
292    WriteAttribute("version", UTF16ToUTF8(iter->version));
293  }
294}
295
296void MetricsLog::WriteInstallElement() {
297  OPEN_ELEMENT_FOR_SCOPE("install");
298  WriteAttribute("installdate", GetInstallDate());
299  WriteIntAttribute("buildid", 0);  // We're using appversion instead.
300}
301
302void MetricsLog::RecordEnvironment(
303         const std::vector<webkit::npapi::WebPluginInfo>& plugin_list,
304         const DictionaryValue* profile_metrics) {
305  DCHECK(!locked_);
306
307  PrefService* pref = g_browser_process->local_state();
308
309  OPEN_ELEMENT_FOR_SCOPE("profile");
310  WriteCommonEventAttributes();
311
312  WriteInstallElement();
313
314  WritePluginList(plugin_list);
315
316  WriteStabilityElement(pref);
317
318  {
319    OPEN_ELEMENT_FOR_SCOPE("cpu");
320    WriteAttribute("arch", base::SysInfo::CPUArchitecture());
321  }
322
323  {
324    OPEN_ELEMENT_FOR_SCOPE("memory");
325    WriteIntAttribute("mb", base::SysInfo::AmountOfPhysicalMemoryMB());
326#if defined(OS_WIN)
327    WriteIntAttribute("dllbase", reinterpret_cast<int>(&__ImageBase));
328#endif
329  }
330
331  {
332    OPEN_ELEMENT_FOR_SCOPE("os");
333    WriteAttribute("name",
334                   base::SysInfo::OperatingSystemName());
335    WriteAttribute("version",
336                   base::SysInfo::OperatingSystemVersion());
337  }
338
339  {
340    OPEN_ELEMENT_FOR_SCOPE("gpu");
341    GpuDataManager* gpu_data_manager = GpuDataManager::GetInstance();
342    if (gpu_data_manager) {
343      WriteIntAttribute("vendorid", gpu_data_manager->gpu_info().vendor_id);
344      WriteIntAttribute("deviceid", gpu_data_manager->gpu_info().device_id);
345    }
346  }
347
348  {
349    OPEN_ELEMENT_FOR_SCOPE("display");
350    int width = 0;
351    int height = 0;
352    base::SysInfo::GetPrimaryDisplayDimensions(&width, &height);
353    WriteIntAttribute("xsize", width);
354    WriteIntAttribute("ysize", height);
355    WriteIntAttribute("screens", base::SysInfo::DisplayCount());
356  }
357
358  {
359    OPEN_ELEMENT_FOR_SCOPE("bookmarks");
360    int num_bookmarks_on_bookmark_bar =
361        pref->GetInteger(prefs::kNumBookmarksOnBookmarkBar);
362    int num_folders_on_bookmark_bar =
363        pref->GetInteger(prefs::kNumFoldersOnBookmarkBar);
364    int num_bookmarks_in_other_bookmarks_folder =
365        pref->GetInteger(prefs::kNumBookmarksInOtherBookmarkFolder);
366    int num_folders_in_other_bookmarks_folder =
367        pref->GetInteger(prefs::kNumFoldersInOtherBookmarkFolder);
368    {
369      OPEN_ELEMENT_FOR_SCOPE("bookmarklocation");
370      WriteAttribute("name", "full-tree");
371      WriteIntAttribute("foldercount",
372          num_folders_on_bookmark_bar + num_folders_in_other_bookmarks_folder);
373      WriteIntAttribute("itemcount",
374          num_bookmarks_on_bookmark_bar +
375          num_bookmarks_in_other_bookmarks_folder);
376    }
377    {
378      OPEN_ELEMENT_FOR_SCOPE("bookmarklocation");
379      WriteAttribute("name", "toolbar");
380      WriteIntAttribute("foldercount", num_folders_on_bookmark_bar);
381      WriteIntAttribute("itemcount", num_bookmarks_on_bookmark_bar);
382    }
383  }
384
385  {
386    OPEN_ELEMENT_FOR_SCOPE("keywords");
387    WriteIntAttribute("count", pref->GetInteger(prefs::kNumKeywords));
388  }
389
390  if (profile_metrics)
391    WriteAllProfilesMetrics(*profile_metrics);
392}
393
394void MetricsLog::WriteAllProfilesMetrics(
395    const DictionaryValue& all_profiles_metrics) {
396  const std::string profile_prefix(prefs::kProfilePrefix);
397  for (DictionaryValue::key_iterator i = all_profiles_metrics.begin_keys();
398       i != all_profiles_metrics.end_keys(); ++i) {
399    const std::string& key_name = *i;
400    if (key_name.compare(0, profile_prefix.size(), profile_prefix) == 0) {
401      DictionaryValue* profile;
402      if (all_profiles_metrics.GetDictionaryWithoutPathExpansion(key_name,
403                                                                 &profile))
404        WriteProfileMetrics(key_name.substr(profile_prefix.size()), *profile);
405    }
406  }
407}
408
409void MetricsLog::WriteProfileMetrics(const std::string& profileidhash,
410                                     const DictionaryValue& profile_metrics) {
411  OPEN_ELEMENT_FOR_SCOPE("userprofile");
412  WriteAttribute("profileidhash", profileidhash);
413  for (DictionaryValue::key_iterator i = profile_metrics.begin_keys();
414       i != profile_metrics.end_keys(); ++i) {
415    Value* value;
416    if (profile_metrics.GetWithoutPathExpansion(*i, &value)) {
417      DCHECK(*i != "id");
418      switch (value->GetType()) {
419        case Value::TYPE_STRING: {
420          std::string string_value;
421          if (value->GetAsString(&string_value)) {
422            OPEN_ELEMENT_FOR_SCOPE("profileparam");
423            WriteAttribute("name", *i);
424            WriteAttribute("value", string_value);
425          }
426          break;
427        }
428
429        case Value::TYPE_BOOLEAN: {
430          bool bool_value;
431          if (value->GetAsBoolean(&bool_value)) {
432            OPEN_ELEMENT_FOR_SCOPE("profileparam");
433            WriteAttribute("name", *i);
434            WriteIntAttribute("value", bool_value ? 1 : 0);
435          }
436          break;
437        }
438
439        case Value::TYPE_INTEGER: {
440          int int_value;
441          if (value->GetAsInteger(&int_value)) {
442            OPEN_ELEMENT_FOR_SCOPE("profileparam");
443            WriteAttribute("name", *i);
444            WriteIntAttribute("value", int_value);
445          }
446          break;
447        }
448
449        default:
450          NOTREACHED();
451          break;
452      }
453    }
454  }
455}
456
457void MetricsLog::RecordOmniboxOpenedURL(const AutocompleteLog& log) {
458  DCHECK(!locked_);
459
460  OPEN_ELEMENT_FOR_SCOPE("uielement");
461  WriteAttribute("action", "autocomplete");
462  WriteAttribute("targetidhash", "");
463  // TODO(kochi): Properly track windows.
464  WriteIntAttribute("window", 0);
465  WriteCommonEventAttributes();
466
467  {
468    OPEN_ELEMENT_FOR_SCOPE("autocomplete");
469
470    WriteIntAttribute("typedlength", static_cast<int>(log.text.length()));
471    WriteIntAttribute("selectedindex", static_cast<int>(log.selected_index));
472    WriteIntAttribute("completedlength",
473                      static_cast<int>(log.inline_autocompleted_length));
474    const std::string input_type(
475        AutocompleteInput::TypeToString(log.input_type));
476    if (!input_type.empty())
477      WriteAttribute("inputtype", input_type);
478
479    for (AutocompleteResult::const_iterator i(log.result.begin());
480         i != log.result.end(); ++i) {
481      OPEN_ELEMENT_FOR_SCOPE("autocompleteitem");
482      if (i->provider)
483        WriteAttribute("provider", i->provider->name());
484      const std::string result_type(AutocompleteMatch::TypeToString(i->type));
485      if (!result_type.empty())
486        WriteAttribute("resulttype", result_type);
487      WriteIntAttribute("relevance", i->relevance);
488      WriteIntAttribute("isstarred", i->starred ? 1 : 0);
489    }
490  }
491
492  ++num_events_;
493}
494