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