| Index: chrome/browser/metrics/plugin_metrics_provider.cc
|
| ===================================================================
|
| --- chrome/browser/metrics/plugin_metrics_provider.cc (revision 0)
|
| +++ chrome/browser/metrics/plugin_metrics_provider.cc (working copy)
|
| @@ -0,0 +1,350 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/metrics/plugin_metrics_provider.h"
|
| +
|
| +#include <string>
|
| +
|
| +#include "base/prefs/pref_registry_simple.h"
|
| +#include "base/prefs/pref_service.h"
|
| +#include "base/prefs/scoped_user_pref_update.h"
|
| +#include "base/stl_util.h"
|
| +#include "base/strings/utf_string_conversions.h"
|
| +#include "chrome/browser/browser_process.h"
|
| +#include "chrome/browser/plugins/plugin_prefs.h"
|
| +#include "chrome/browser/profiles/profile_manager.h"
|
| +#include "chrome/common/pref_names.h"
|
| +#include "components/metrics/proto/system_profile.pb.h"
|
| +#include "content/public/browser/child_process_data.h"
|
| +#include "content/public/browser/plugin_service.h"
|
| +#include "content/public/common/process_type.h"
|
| +#include "content/public/common/webplugininfo.h"
|
| +
|
| +namespace {
|
| +
|
| +// Returns the plugin preferences corresponding for this user, if available.
|
| +// If multiple user profiles are loaded, returns the preferences corresponding
|
| +// to an arbitrary one of the profiles.
|
| +PluginPrefs* GetPluginPrefs() {
|
| + ProfileManager* profile_manager = g_browser_process->profile_manager();
|
| +
|
| + if (!profile_manager) {
|
| + // The profile manager can be NULL when testing.
|
| + return NULL;
|
| + }
|
| +
|
| + std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles();
|
| + if (profiles.empty())
|
| + return NULL;
|
| +
|
| + return PluginPrefs::GetForProfile(profiles.front()).get();
|
| +}
|
| +
|
| +// Fills |plugin| with the info contained in |plugin_info| and |plugin_prefs|.
|
| +void SetPluginInfo(const content::WebPluginInfo& plugin_info,
|
| + const PluginPrefs* plugin_prefs,
|
| + metrics::SystemProfileProto::Plugin* plugin) {
|
| + plugin->set_name(base::UTF16ToUTF8(plugin_info.name));
|
| + plugin->set_filename(plugin_info.path.BaseName().AsUTF8Unsafe());
|
| + plugin->set_version(base::UTF16ToUTF8(plugin_info.version));
|
| + plugin->set_is_pepper(plugin_info.is_pepper_plugin());
|
| + if (plugin_prefs)
|
| + plugin->set_is_disabled(!plugin_prefs->IsPluginEnabled(plugin_info));
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// This is used to quickly log stats from child process related notifications in
|
| +// PluginMetricsProvider::child_stats_buffer_. The buffer's contents are
|
| +// transferred out when Local State is periodically saved. The information is
|
| +// then reported to the UMA server on next launch.
|
| +struct PluginMetricsProvider::ChildProcessStats {
|
| + public:
|
| + explicit ChildProcessStats(int process_type)
|
| + : process_launches(0),
|
| + process_crashes(0),
|
| + instances(0),
|
| + loading_errors(0),
|
| + process_type(process_type) {}
|
| +
|
| + // This constructor is only used by the map to return some default value for
|
| + // an index for which no value has been assigned.
|
| + ChildProcessStats()
|
| + : process_launches(0),
|
| + process_crashes(0),
|
| + instances(0),
|
| + loading_errors(0),
|
| + process_type(content::PROCESS_TYPE_UNKNOWN) {}
|
| +
|
| + // The number of times that the given child process has been launched
|
| + int process_launches;
|
| +
|
| + // The number of times that the given child process has crashed
|
| + int process_crashes;
|
| +
|
| + // The number of instances of this child process that have been created.
|
| + // An instance is a DOM object rendered by this child process during a page
|
| + // load.
|
| + int instances;
|
| +
|
| + // The number of times there was an error loading an instance of this child
|
| + // process.
|
| + int loading_errors;
|
| +
|
| + int process_type;
|
| +};
|
| +
|
| +PluginMetricsProvider::PluginMetricsProvider(PrefService* local_state)
|
| + : local_state_(local_state),
|
| + weak_ptr_factory_(this) {
|
| + DCHECK(local_state_);
|
| +
|
| + BrowserChildProcessObserver::Add(this);
|
| +}
|
| +
|
| +PluginMetricsProvider::~PluginMetricsProvider() {
|
| + BrowserChildProcessObserver::Remove(this);
|
| +}
|
| +
|
| +void PluginMetricsProvider::GetPluginInformation(
|
| + const base::Closure& done_callback) {
|
| + content::PluginService::GetInstance()->GetPlugins(
|
| + base::Bind(&PluginMetricsProvider::OnGotPlugins,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + done_callback));
|
| +}
|
| +
|
| +void PluginMetricsProvider::ProvideSystemProfileMetrics(
|
| + metrics::SystemProfileProto* system_profile_proto) {
|
| + PluginPrefs* plugin_prefs = GetPluginPrefs();
|
| + for (size_t i = 0; i < plugins_.size(); ++i) {
|
| + SetPluginInfo(plugins_[i], plugin_prefs,
|
| + system_profile_proto->add_plugin());
|
| + }
|
| +}
|
| +
|
| +void PluginMetricsProvider::ProvideStabilityMetrics(
|
| + metrics::SystemProfileProto* system_profile_proto) {
|
| + const base::ListValue* plugin_stats_list = local_state_->GetList(
|
| + prefs::kStabilityPluginStats);
|
| + if (!plugin_stats_list)
|
| + return;
|
| +
|
| + metrics::SystemProfileProto::Stability* stability =
|
| + system_profile_proto->mutable_stability();
|
| + for (base::ListValue::const_iterator iter = plugin_stats_list->begin();
|
| + iter != plugin_stats_list->end(); ++iter) {
|
| + if (!(*iter)->IsType(base::Value::TYPE_DICTIONARY)) {
|
| + NOTREACHED();
|
| + continue;
|
| + }
|
| + base::DictionaryValue* plugin_dict =
|
| + static_cast<base::DictionaryValue*>(*iter);
|
| +
|
| + // Note that this search is potentially a quadratic operation, but given the
|
| + // low number of plugins installed on a "reasonable" setup, this should be
|
| + // fine.
|
| + // TODO(isherman): Verify that this does not show up as a hotspot in
|
| + // profiler runs.
|
| + const metrics::SystemProfileProto::Plugin* system_profile_plugin = NULL;
|
| + std::string plugin_name;
|
| + plugin_dict->GetString(prefs::kStabilityPluginName, &plugin_name);
|
| + for (int i = 0; i < system_profile_proto->plugin_size(); ++i) {
|
| + if (system_profile_proto->plugin(i).name() == plugin_name) {
|
| + system_profile_plugin = &system_profile_proto->plugin(i);
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (!system_profile_plugin) {
|
| + NOTREACHED();
|
| + continue;
|
| + }
|
| +
|
| + metrics::SystemProfileProto::Stability::PluginStability* plugin_stability =
|
| + stability->add_plugin_stability();
|
| + *plugin_stability->mutable_plugin() = *system_profile_plugin;
|
| +
|
| + int launches = 0;
|
| + plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches);
|
| + if (launches > 0)
|
| + plugin_stability->set_launch_count(launches);
|
| +
|
| + int instances = 0;
|
| + plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances);
|
| + if (instances > 0)
|
| + plugin_stability->set_instance_count(instances);
|
| +
|
| + int crashes = 0;
|
| + plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes);
|
| + if (crashes > 0)
|
| + plugin_stability->set_crash_count(crashes);
|
| +
|
| + int loading_errors = 0;
|
| + plugin_dict->GetInteger(prefs::kStabilityPluginLoadingErrors,
|
| + &loading_errors);
|
| + if (loading_errors > 0)
|
| + plugin_stability->set_loading_error_count(loading_errors);
|
| + }
|
| +
|
| + local_state_->ClearPref(prefs::kStabilityPluginStats);
|
| +}
|
| +
|
| +void PluginMetricsProvider::RecordPluginChanges() {
|
| + ListPrefUpdate update(local_state_, prefs::kStabilityPluginStats);
|
| + base::ListValue* plugins = update.Get();
|
| + DCHECK(plugins);
|
| +
|
| + for (base::ListValue::iterator value_iter = plugins->begin();
|
| + value_iter != plugins->end(); ++value_iter) {
|
| + if (!(*value_iter)->IsType(base::Value::TYPE_DICTIONARY)) {
|
| + NOTREACHED();
|
| + continue;
|
| + }
|
| +
|
| + base::DictionaryValue* plugin_dict =
|
| + static_cast<base::DictionaryValue*>(*value_iter);
|
| + std::string plugin_name;
|
| + plugin_dict->GetString(prefs::kStabilityPluginName, &plugin_name);
|
| + if (plugin_name.empty()) {
|
| + NOTREACHED();
|
| + continue;
|
| + }
|
| +
|
| + // TODO(viettrungluu): remove conversions
|
| + base::string16 name16 = base::UTF8ToUTF16(plugin_name);
|
| + if (child_process_stats_buffer_.find(name16) ==
|
| + child_process_stats_buffer_.end()) {
|
| + continue;
|
| + }
|
| +
|
| + ChildProcessStats stats = child_process_stats_buffer_[name16];
|
| + if (stats.process_launches) {
|
| + int launches = 0;
|
| + plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches);
|
| + launches += stats.process_launches;
|
| + plugin_dict->SetInteger(prefs::kStabilityPluginLaunches, launches);
|
| + }
|
| + if (stats.process_crashes) {
|
| + int crashes = 0;
|
| + plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes);
|
| + crashes += stats.process_crashes;
|
| + plugin_dict->SetInteger(prefs::kStabilityPluginCrashes, crashes);
|
| + }
|
| + if (stats.instances) {
|
| + int instances = 0;
|
| + plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances);
|
| + instances += stats.instances;
|
| + plugin_dict->SetInteger(prefs::kStabilityPluginInstances, instances);
|
| + }
|
| + if (stats.loading_errors) {
|
| + int loading_errors = 0;
|
| + plugin_dict->GetInteger(prefs::kStabilityPluginLoadingErrors,
|
| + &loading_errors);
|
| + loading_errors += stats.loading_errors;
|
| + plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors,
|
| + loading_errors);
|
| + }
|
| +
|
| + child_process_stats_buffer_.erase(name16);
|
| + }
|
| +
|
| + // Now go through and add dictionaries for plugins that didn't already have
|
| + // reports in Local State.
|
| + for (std::map<base::string16, ChildProcessStats>::iterator cache_iter =
|
| + child_process_stats_buffer_.begin();
|
| + cache_iter != child_process_stats_buffer_.end(); ++cache_iter) {
|
| + ChildProcessStats stats = cache_iter->second;
|
| +
|
| + // Insert only plugins information into the plugins list.
|
| + if (!IsPluginProcess(stats.process_type))
|
| + continue;
|
| +
|
| + // TODO(viettrungluu): remove conversion
|
| + std::string plugin_name = base::UTF16ToUTF8(cache_iter->first);
|
| +
|
| + base::DictionaryValue* plugin_dict = new base::DictionaryValue;
|
| +
|
| + plugin_dict->SetString(prefs::kStabilityPluginName, plugin_name);
|
| + plugin_dict->SetInteger(prefs::kStabilityPluginLaunches,
|
| + stats.process_launches);
|
| + plugin_dict->SetInteger(prefs::kStabilityPluginCrashes,
|
| + stats.process_crashes);
|
| + plugin_dict->SetInteger(prefs::kStabilityPluginInstances,
|
| + stats.instances);
|
| + plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors,
|
| + stats.loading_errors);
|
| + plugins->Append(plugin_dict);
|
| + }
|
| + child_process_stats_buffer_.clear();
|
| +}
|
| +
|
| +void PluginMetricsProvider::LogPluginLoadingError(
|
| + const base::FilePath& plugin_path) {
|
| + content::WebPluginInfo plugin;
|
| + bool success =
|
| + content::PluginService::GetInstance()->GetPluginInfoByPath(plugin_path,
|
| + &plugin);
|
| + DCHECK(success);
|
| + ChildProcessStats& stats = child_process_stats_buffer_[plugin.name];
|
| + // Initialize the type if this entry is new.
|
| + if (stats.process_type == content::PROCESS_TYPE_UNKNOWN) {
|
| + // The plug-in process might not actually be of type PLUGIN (which means
|
| + // NPAPI), but we only care that it is *a* plug-in process.
|
| + stats.process_type = content::PROCESS_TYPE_PLUGIN;
|
| + } else {
|
| + DCHECK(IsPluginProcess(stats.process_type));
|
| + }
|
| + stats.loading_errors++;
|
| +}
|
| +
|
| +void PluginMetricsProvider::SetPluginsForTesting(
|
| + const std::vector<content::WebPluginInfo>& plugins) {
|
| + plugins_ = plugins;
|
| +}
|
| +
|
| +// static
|
| +bool PluginMetricsProvider::IsPluginProcess(int process_type) {
|
| + return (process_type == content::PROCESS_TYPE_PLUGIN ||
|
| + process_type == content::PROCESS_TYPE_PPAPI_PLUGIN ||
|
| + process_type == content::PROCESS_TYPE_PPAPI_BROKER);
|
| +}
|
| +
|
| +// static
|
| +void PluginMetricsProvider::RegisterPrefs(PrefRegistrySimple* registry) {
|
| + registry->RegisterListPref(prefs::kStabilityPluginStats);
|
| +}
|
| +
|
| +void PluginMetricsProvider::OnGotPlugins(
|
| + const base::Closure& done_callback,
|
| + const std::vector<content::WebPluginInfo>& plugins) {
|
| + plugins_ = plugins;
|
| + done_callback.Run();
|
| +}
|
| +
|
| +PluginMetricsProvider::ChildProcessStats&
|
| +PluginMetricsProvider::GetChildProcessStats(
|
| + const content::ChildProcessData& data) {
|
| + const base::string16& child_name = data.name;
|
| + if (!ContainsKey(child_process_stats_buffer_, child_name)) {
|
| + child_process_stats_buffer_[child_name] =
|
| + ChildProcessStats(data.process_type);
|
| + }
|
| + return child_process_stats_buffer_[child_name];
|
| +}
|
| +
|
| +void PluginMetricsProvider::BrowserChildProcessHostConnected(
|
| + const content::ChildProcessData& data) {
|
| + GetChildProcessStats(data).process_launches++;
|
| +}
|
| +
|
| +void PluginMetricsProvider::BrowserChildProcessCrashed(
|
| + const content::ChildProcessData& data) {
|
| + GetChildProcessStats(data).process_crashes++;
|
| +}
|
| +
|
| +void PluginMetricsProvider::BrowserChildProcessInstanceCreated(
|
| + const content::ChildProcessData& data) {
|
| + GetChildProcessStats(data).instances++;
|
| +}
|
|
|