Home | History | Annotate | Download | only in metrics
      1 // Copyright 2014 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/plugin_metrics_provider.h"
      6 
      7 #include <string>
      8 
      9 #include "base/prefs/pref_registry_simple.h"
     10 #include "base/prefs/pref_service.h"
     11 #include "base/prefs/scoped_user_pref_update.h"
     12 #include "base/stl_util.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "chrome/browser/browser_process.h"
     15 #include "chrome/browser/plugins/plugin_prefs.h"
     16 #include "chrome/browser/profiles/profile_manager.h"
     17 #include "chrome/common/pref_names.h"
     18 #include "components/metrics/proto/system_profile.pb.h"
     19 #include "content/public/browser/child_process_data.h"
     20 #include "content/public/browser/plugin_service.h"
     21 #include "content/public/common/process_type.h"
     22 #include "content/public/common/webplugininfo.h"
     23 
     24 namespace {
     25 
     26 // Returns the plugin preferences corresponding for this user, if available.
     27 // If multiple user profiles are loaded, returns the preferences corresponding
     28 // to an arbitrary one of the profiles.
     29 PluginPrefs* GetPluginPrefs() {
     30   ProfileManager* profile_manager = g_browser_process->profile_manager();
     31 
     32   if (!profile_manager) {
     33     // The profile manager can be NULL when testing.
     34     return NULL;
     35   }
     36 
     37   std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles();
     38   if (profiles.empty())
     39     return NULL;
     40 
     41   return PluginPrefs::GetForProfile(profiles.front()).get();
     42 }
     43 
     44 // Fills |plugin| with the info contained in |plugin_info| and |plugin_prefs|.
     45 void SetPluginInfo(const content::WebPluginInfo& plugin_info,
     46                    const PluginPrefs* plugin_prefs,
     47                    metrics::SystemProfileProto::Plugin* plugin) {
     48   plugin->set_name(base::UTF16ToUTF8(plugin_info.name));
     49   plugin->set_filename(plugin_info.path.BaseName().AsUTF8Unsafe());
     50   plugin->set_version(base::UTF16ToUTF8(plugin_info.version));
     51   plugin->set_is_pepper(plugin_info.is_pepper_plugin());
     52   if (plugin_prefs)
     53     plugin->set_is_disabled(!plugin_prefs->IsPluginEnabled(plugin_info));
     54 }
     55 
     56 }  // namespace
     57 
     58 // This is used to quickly log stats from child process related notifications in
     59 // PluginMetricsProvider::child_stats_buffer_.  The buffer's contents are
     60 // transferred out when Local State is periodically saved.  The information is
     61 // then reported to the UMA server on next launch.
     62 struct PluginMetricsProvider::ChildProcessStats {
     63  public:
     64   explicit ChildProcessStats(int process_type)
     65       : process_launches(0),
     66         process_crashes(0),
     67         instances(0),
     68         loading_errors(0),
     69         process_type(process_type) {}
     70 
     71   // This constructor is only used by the map to return some default value for
     72   // an index for which no value has been assigned.
     73   ChildProcessStats()
     74       : process_launches(0),
     75         process_crashes(0),
     76         instances(0),
     77         loading_errors(0),
     78         process_type(content::PROCESS_TYPE_UNKNOWN) {}
     79 
     80   // The number of times that the given child process has been launched
     81   int process_launches;
     82 
     83   // The number of times that the given child process has crashed
     84   int process_crashes;
     85 
     86   // The number of instances of this child process that have been created.
     87   // An instance is a DOM object rendered by this child process during a page
     88   // load.
     89   int instances;
     90 
     91   // The number of times there was an error loading an instance of this child
     92   // process.
     93   int loading_errors;
     94 
     95   int process_type;
     96 };
     97 
     98 PluginMetricsProvider::PluginMetricsProvider(PrefService* local_state)
     99     : local_state_(local_state),
    100       weak_ptr_factory_(this) {
    101   DCHECK(local_state_);
    102 
    103   BrowserChildProcessObserver::Add(this);
    104 }
    105 
    106 PluginMetricsProvider::~PluginMetricsProvider() {
    107   BrowserChildProcessObserver::Remove(this);
    108 }
    109 
    110 void PluginMetricsProvider::GetPluginInformation(
    111     const base::Closure& done_callback) {
    112   content::PluginService::GetInstance()->GetPlugins(
    113       base::Bind(&PluginMetricsProvider::OnGotPlugins,
    114                  weak_ptr_factory_.GetWeakPtr(),
    115                  done_callback));
    116 }
    117 
    118 void PluginMetricsProvider::ProvideSystemProfileMetrics(
    119     metrics::SystemProfileProto* system_profile_proto) {
    120   PluginPrefs* plugin_prefs = GetPluginPrefs();
    121   for (size_t i = 0; i < plugins_.size(); ++i) {
    122     SetPluginInfo(plugins_[i], plugin_prefs,
    123                   system_profile_proto->add_plugin());
    124   }
    125 }
    126 
    127 void PluginMetricsProvider::ProvideStabilityMetrics(
    128     metrics::SystemProfileProto* system_profile_proto) {
    129   const base::ListValue* plugin_stats_list = local_state_->GetList(
    130       prefs::kStabilityPluginStats);
    131   if (!plugin_stats_list)
    132     return;
    133 
    134   metrics::SystemProfileProto::Stability* stability =
    135       system_profile_proto->mutable_stability();
    136   for (base::ListValue::const_iterator iter = plugin_stats_list->begin();
    137        iter != plugin_stats_list->end(); ++iter) {
    138     if (!(*iter)->IsType(base::Value::TYPE_DICTIONARY)) {
    139       NOTREACHED();
    140       continue;
    141     }
    142     base::DictionaryValue* plugin_dict =
    143         static_cast<base::DictionaryValue*>(*iter);
    144 
    145     // Note that this search is potentially a quadratic operation, but given the
    146     // low number of plugins installed on a "reasonable" setup, this should be
    147     // fine.
    148     // TODO(isherman): Verify that this does not show up as a hotspot in
    149     // profiler runs.
    150     const metrics::SystemProfileProto::Plugin* system_profile_plugin = NULL;
    151     std::string plugin_name;
    152     plugin_dict->GetString(prefs::kStabilityPluginName, &plugin_name);
    153     for (int i = 0; i < system_profile_proto->plugin_size(); ++i) {
    154       if (system_profile_proto->plugin(i).name() == plugin_name) {
    155         system_profile_plugin = &system_profile_proto->plugin(i);
    156         break;
    157       }
    158     }
    159 
    160     if (!system_profile_plugin) {
    161       NOTREACHED();
    162       continue;
    163     }
    164 
    165     metrics::SystemProfileProto::Stability::PluginStability* plugin_stability =
    166         stability->add_plugin_stability();
    167     *plugin_stability->mutable_plugin() = *system_profile_plugin;
    168 
    169     int launches = 0;
    170     plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches);
    171     if (launches > 0)
    172       plugin_stability->set_launch_count(launches);
    173 
    174     int instances = 0;
    175     plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances);
    176     if (instances > 0)
    177       plugin_stability->set_instance_count(instances);
    178 
    179     int crashes = 0;
    180     plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes);
    181     if (crashes > 0)
    182       plugin_stability->set_crash_count(crashes);
    183 
    184     int loading_errors = 0;
    185     plugin_dict->GetInteger(prefs::kStabilityPluginLoadingErrors,
    186                             &loading_errors);
    187     if (loading_errors > 0)
    188       plugin_stability->set_loading_error_count(loading_errors);
    189   }
    190 
    191   local_state_->ClearPref(prefs::kStabilityPluginStats);
    192 }
    193 
    194 // Saves plugin-related updates from the in-object buffer to Local State
    195 // for retrieval next time we send a Profile log (generally next launch).
    196 void PluginMetricsProvider::RecordCurrentState() {
    197   ListPrefUpdate update(local_state_, prefs::kStabilityPluginStats);
    198   base::ListValue* plugins = update.Get();
    199   DCHECK(plugins);
    200 
    201   for (base::ListValue::iterator value_iter = plugins->begin();
    202        value_iter != plugins->end(); ++value_iter) {
    203     if (!(*value_iter)->IsType(base::Value::TYPE_DICTIONARY)) {
    204       NOTREACHED();
    205       continue;
    206     }
    207 
    208     base::DictionaryValue* plugin_dict =
    209         static_cast<base::DictionaryValue*>(*value_iter);
    210     std::string plugin_name;
    211     plugin_dict->GetString(prefs::kStabilityPluginName, &plugin_name);
    212     if (plugin_name.empty()) {
    213       NOTREACHED();
    214       continue;
    215     }
    216 
    217     // TODO(viettrungluu): remove conversions
    218     base::string16 name16 = base::UTF8ToUTF16(plugin_name);
    219     if (child_process_stats_buffer_.find(name16) ==
    220         child_process_stats_buffer_.end()) {
    221       continue;
    222     }
    223 
    224     ChildProcessStats stats = child_process_stats_buffer_[name16];
    225     if (stats.process_launches) {
    226       int launches = 0;
    227       plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches);
    228       launches += stats.process_launches;
    229       plugin_dict->SetInteger(prefs::kStabilityPluginLaunches, launches);
    230     }
    231     if (stats.process_crashes) {
    232       int crashes = 0;
    233       plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes);
    234       crashes += stats.process_crashes;
    235       plugin_dict->SetInteger(prefs::kStabilityPluginCrashes, crashes);
    236     }
    237     if (stats.instances) {
    238       int instances = 0;
    239       plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances);
    240       instances += stats.instances;
    241       plugin_dict->SetInteger(prefs::kStabilityPluginInstances, instances);
    242     }
    243     if (stats.loading_errors) {
    244       int loading_errors = 0;
    245       plugin_dict->GetInteger(prefs::kStabilityPluginLoadingErrors,
    246                               &loading_errors);
    247       loading_errors += stats.loading_errors;
    248       plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors,
    249                               loading_errors);
    250     }
    251 
    252     child_process_stats_buffer_.erase(name16);
    253   }
    254 
    255   // Now go through and add dictionaries for plugins that didn't already have
    256   // reports in Local State.
    257   for (std::map<base::string16, ChildProcessStats>::iterator cache_iter =
    258            child_process_stats_buffer_.begin();
    259        cache_iter != child_process_stats_buffer_.end(); ++cache_iter) {
    260     ChildProcessStats stats = cache_iter->second;
    261 
    262     // Insert only plugins information into the plugins list.
    263     if (!IsPluginProcess(stats.process_type))
    264       continue;
    265 
    266     // TODO(viettrungluu): remove conversion
    267     std::string plugin_name = base::UTF16ToUTF8(cache_iter->first);
    268 
    269     base::DictionaryValue* plugin_dict = new base::DictionaryValue;
    270 
    271     plugin_dict->SetString(prefs::kStabilityPluginName, plugin_name);
    272     plugin_dict->SetInteger(prefs::kStabilityPluginLaunches,
    273                             stats.process_launches);
    274     plugin_dict->SetInteger(prefs::kStabilityPluginCrashes,
    275                             stats.process_crashes);
    276     plugin_dict->SetInteger(prefs::kStabilityPluginInstances,
    277                             stats.instances);
    278     plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors,
    279                             stats.loading_errors);
    280     plugins->Append(plugin_dict);
    281   }
    282   child_process_stats_buffer_.clear();
    283 }
    284 
    285 void PluginMetricsProvider::LogPluginLoadingError(
    286     const base::FilePath& plugin_path) {
    287   content::WebPluginInfo plugin;
    288   bool success =
    289       content::PluginService::GetInstance()->GetPluginInfoByPath(plugin_path,
    290                                                                  &plugin);
    291   DCHECK(success);
    292   ChildProcessStats& stats = child_process_stats_buffer_[plugin.name];
    293   // Initialize the type if this entry is new.
    294   if (stats.process_type == content::PROCESS_TYPE_UNKNOWN) {
    295     // The plug-in process might not actually be of type PLUGIN (which means
    296     // NPAPI), but we only care that it is *a* plug-in process.
    297     stats.process_type = content::PROCESS_TYPE_PLUGIN;
    298   } else {
    299     DCHECK(IsPluginProcess(stats.process_type));
    300   }
    301   stats.loading_errors++;
    302 }
    303 
    304 void PluginMetricsProvider::SetPluginsForTesting(
    305     const std::vector<content::WebPluginInfo>& plugins) {
    306   plugins_ = plugins;
    307 }
    308 
    309 // static
    310 bool PluginMetricsProvider::IsPluginProcess(int process_type) {
    311   return (process_type == content::PROCESS_TYPE_PLUGIN ||
    312           process_type == content::PROCESS_TYPE_PPAPI_PLUGIN ||
    313           process_type == content::PROCESS_TYPE_PPAPI_BROKER);
    314 }
    315 
    316 // static
    317 void PluginMetricsProvider::RegisterPrefs(PrefRegistrySimple* registry) {
    318   registry->RegisterListPref(prefs::kStabilityPluginStats);
    319 }
    320 
    321 void PluginMetricsProvider::OnGotPlugins(
    322     const base::Closure& done_callback,
    323     const std::vector<content::WebPluginInfo>& plugins) {
    324   plugins_ = plugins;
    325   done_callback.Run();
    326 }
    327 
    328 PluginMetricsProvider::ChildProcessStats&
    329 PluginMetricsProvider::GetChildProcessStats(
    330     const content::ChildProcessData& data) {
    331   const base::string16& child_name = data.name;
    332   if (!ContainsKey(child_process_stats_buffer_, child_name)) {
    333     child_process_stats_buffer_[child_name] =
    334         ChildProcessStats(data.process_type);
    335   }
    336   return child_process_stats_buffer_[child_name];
    337 }
    338 
    339 void PluginMetricsProvider::BrowserChildProcessHostConnected(
    340     const content::ChildProcessData& data) {
    341   GetChildProcessStats(data).process_launches++;
    342 }
    343 
    344 void PluginMetricsProvider::BrowserChildProcessCrashed(
    345     const content::ChildProcessData& data) {
    346   GetChildProcessStats(data).process_crashes++;
    347 }
    348 
    349 void PluginMetricsProvider::BrowserChildProcessInstanceCreated(
    350     const content::ChildProcessData& data) {
    351   GetChildProcessStats(data).instances++;
    352 }
    353