OLD | NEW |
(Empty) | |
| 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 void PluginMetricsProvider::RecordPluginChanges() { |
| 195 ListPrefUpdate update(local_state_, prefs::kStabilityPluginStats); |
| 196 base::ListValue* plugins = update.Get(); |
| 197 DCHECK(plugins); |
| 198 |
| 199 for (base::ListValue::iterator value_iter = plugins->begin(); |
| 200 value_iter != plugins->end(); ++value_iter) { |
| 201 if (!(*value_iter)->IsType(base::Value::TYPE_DICTIONARY)) { |
| 202 NOTREACHED(); |
| 203 continue; |
| 204 } |
| 205 |
| 206 base::DictionaryValue* plugin_dict = |
| 207 static_cast<base::DictionaryValue*>(*value_iter); |
| 208 std::string plugin_name; |
| 209 plugin_dict->GetString(prefs::kStabilityPluginName, &plugin_name); |
| 210 if (plugin_name.empty()) { |
| 211 NOTREACHED(); |
| 212 continue; |
| 213 } |
| 214 |
| 215 // TODO(viettrungluu): remove conversions |
| 216 base::string16 name16 = base::UTF8ToUTF16(plugin_name); |
| 217 if (child_process_stats_buffer_.find(name16) == |
| 218 child_process_stats_buffer_.end()) { |
| 219 continue; |
| 220 } |
| 221 |
| 222 ChildProcessStats stats = child_process_stats_buffer_[name16]; |
| 223 if (stats.process_launches) { |
| 224 int launches = 0; |
| 225 plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches); |
| 226 launches += stats.process_launches; |
| 227 plugin_dict->SetInteger(prefs::kStabilityPluginLaunches, launches); |
| 228 } |
| 229 if (stats.process_crashes) { |
| 230 int crashes = 0; |
| 231 plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes); |
| 232 crashes += stats.process_crashes; |
| 233 plugin_dict->SetInteger(prefs::kStabilityPluginCrashes, crashes); |
| 234 } |
| 235 if (stats.instances) { |
| 236 int instances = 0; |
| 237 plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances); |
| 238 instances += stats.instances; |
| 239 plugin_dict->SetInteger(prefs::kStabilityPluginInstances, instances); |
| 240 } |
| 241 if (stats.loading_errors) { |
| 242 int loading_errors = 0; |
| 243 plugin_dict->GetInteger(prefs::kStabilityPluginLoadingErrors, |
| 244 &loading_errors); |
| 245 loading_errors += stats.loading_errors; |
| 246 plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors, |
| 247 loading_errors); |
| 248 } |
| 249 |
| 250 child_process_stats_buffer_.erase(name16); |
| 251 } |
| 252 |
| 253 // Now go through and add dictionaries for plugins that didn't already have |
| 254 // reports in Local State. |
| 255 for (std::map<base::string16, ChildProcessStats>::iterator cache_iter = |
| 256 child_process_stats_buffer_.begin(); |
| 257 cache_iter != child_process_stats_buffer_.end(); ++cache_iter) { |
| 258 ChildProcessStats stats = cache_iter->second; |
| 259 |
| 260 // Insert only plugins information into the plugins list. |
| 261 if (!IsPluginProcess(stats.process_type)) |
| 262 continue; |
| 263 |
| 264 // TODO(viettrungluu): remove conversion |
| 265 std::string plugin_name = base::UTF16ToUTF8(cache_iter->first); |
| 266 |
| 267 base::DictionaryValue* plugin_dict = new base::DictionaryValue; |
| 268 |
| 269 plugin_dict->SetString(prefs::kStabilityPluginName, plugin_name); |
| 270 plugin_dict->SetInteger(prefs::kStabilityPluginLaunches, |
| 271 stats.process_launches); |
| 272 plugin_dict->SetInteger(prefs::kStabilityPluginCrashes, |
| 273 stats.process_crashes); |
| 274 plugin_dict->SetInteger(prefs::kStabilityPluginInstances, |
| 275 stats.instances); |
| 276 plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors, |
| 277 stats.loading_errors); |
| 278 plugins->Append(plugin_dict); |
| 279 } |
| 280 child_process_stats_buffer_.clear(); |
| 281 } |
| 282 |
| 283 void PluginMetricsProvider::LogPluginLoadingError( |
| 284 const base::FilePath& plugin_path) { |
| 285 content::WebPluginInfo plugin; |
| 286 bool success = |
| 287 content::PluginService::GetInstance()->GetPluginInfoByPath(plugin_path, |
| 288 &plugin); |
| 289 DCHECK(success); |
| 290 ChildProcessStats& stats = child_process_stats_buffer_[plugin.name]; |
| 291 // Initialize the type if this entry is new. |
| 292 if (stats.process_type == content::PROCESS_TYPE_UNKNOWN) { |
| 293 // The plug-in process might not actually be of type PLUGIN (which means |
| 294 // NPAPI), but we only care that it is *a* plug-in process. |
| 295 stats.process_type = content::PROCESS_TYPE_PLUGIN; |
| 296 } else { |
| 297 DCHECK(IsPluginProcess(stats.process_type)); |
| 298 } |
| 299 stats.loading_errors++; |
| 300 } |
| 301 |
| 302 void PluginMetricsProvider::SetPluginsForTesting( |
| 303 const std::vector<content::WebPluginInfo>& plugins) { |
| 304 plugins_ = plugins; |
| 305 } |
| 306 |
| 307 // static |
| 308 bool PluginMetricsProvider::IsPluginProcess(int process_type) { |
| 309 return (process_type == content::PROCESS_TYPE_PLUGIN || |
| 310 process_type == content::PROCESS_TYPE_PPAPI_PLUGIN || |
| 311 process_type == content::PROCESS_TYPE_PPAPI_BROKER); |
| 312 } |
| 313 |
| 314 // static |
| 315 void PluginMetricsProvider::RegisterPrefs(PrefRegistrySimple* registry) { |
| 316 registry->RegisterListPref(prefs::kStabilityPluginStats); |
| 317 } |
| 318 |
| 319 void PluginMetricsProvider::OnGotPlugins( |
| 320 const base::Closure& done_callback, |
| 321 const std::vector<content::WebPluginInfo>& plugins) { |
| 322 plugins_ = plugins; |
| 323 done_callback.Run(); |
| 324 } |
| 325 |
| 326 PluginMetricsProvider::ChildProcessStats& |
| 327 PluginMetricsProvider::GetChildProcessStats( |
| 328 const content::ChildProcessData& data) { |
| 329 const base::string16& child_name = data.name; |
| 330 if (!ContainsKey(child_process_stats_buffer_, child_name)) { |
| 331 child_process_stats_buffer_[child_name] = |
| 332 ChildProcessStats(data.process_type); |
| 333 } |
| 334 return child_process_stats_buffer_[child_name]; |
| 335 } |
| 336 |
| 337 void PluginMetricsProvider::BrowserChildProcessHostConnected( |
| 338 const content::ChildProcessData& data) { |
| 339 GetChildProcessStats(data).process_launches++; |
| 340 } |
| 341 |
| 342 void PluginMetricsProvider::BrowserChildProcessCrashed( |
| 343 const content::ChildProcessData& data) { |
| 344 GetChildProcessStats(data).process_crashes++; |
| 345 } |
| 346 |
| 347 void PluginMetricsProvider::BrowserChildProcessInstanceCreated( |
| 348 const content::ChildProcessData& data) { |
| 349 GetChildProcessStats(data).instances++; |
| 350 } |
OLD | NEW |