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