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