Chromium Code Reviews| 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 bool IsPluginProcess(int process_type) { | |
| 45 return (process_type == content::PROCESS_TYPE_PLUGIN || | |
| 46 process_type == content::PROCESS_TYPE_PPAPI_PLUGIN || | |
| 47 process_type == content::PROCESS_TYPE_PPAPI_BROKER); | |
| 48 } | |
|
Ilya Sherman
2014/05/22 14:52:20
nit: Might be worth making this a public static me
Alexei Svitkine (slow)
2014/05/22 15:38:54
Done. Removed the equivalent static method from Me
| |
| 49 | |
| 50 // Fills |plugin| with the info contained in |plugin_info| and |plugin_prefs|. | |
| 51 void SetPluginInfo(const content::WebPluginInfo& plugin_info, | |
| 52 const PluginPrefs* plugin_prefs, | |
| 53 metrics::SystemProfileProto::Plugin* plugin) { | |
| 54 plugin->set_name(base::UTF16ToUTF8(plugin_info.name)); | |
| 55 plugin->set_filename(plugin_info.path.BaseName().AsUTF8Unsafe()); | |
| 56 plugin->set_version(base::UTF16ToUTF8(plugin_info.version)); | |
| 57 plugin->set_is_pepper(plugin_info.is_pepper_plugin()); | |
| 58 if (plugin_prefs) | |
| 59 plugin->set_is_disabled(!plugin_prefs->IsPluginEnabled(plugin_info)); | |
| 60 } | |
| 61 | |
| 62 } // namespace | |
| 63 | |
| 64 // This is used to quickly log stats from child process related notifications in | |
| 65 // PluginMetricsProvider::child_stats_buffer_. The buffer's contents are | |
| 66 // transferred out when Local State is periodically saved. The information is | |
| 67 // then reported to the UMA server on next launch. | |
| 68 struct PluginMetricsProvider::ChildProcessStats { | |
| 69 public: | |
| 70 explicit ChildProcessStats(int process_type) | |
| 71 : process_launches(0), | |
| 72 process_crashes(0), | |
| 73 instances(0), | |
| 74 loading_errors(0), | |
| 75 process_type(process_type) {} | |
| 76 | |
| 77 // This constructor is only used by the map to return some default value for | |
| 78 // an index for which no value has been assigned. | |
| 79 ChildProcessStats() | |
| 80 : process_launches(0), | |
| 81 process_crashes(0), | |
| 82 instances(0), | |
| 83 loading_errors(0), | |
| 84 process_type(content::PROCESS_TYPE_UNKNOWN) {} | |
| 85 | |
| 86 // The number of times that the given child process has been launched | |
| 87 int process_launches; | |
| 88 | |
| 89 // The number of times that the given child process has crashed | |
| 90 int process_crashes; | |
| 91 | |
| 92 // The number of instances of this child process that have been created. | |
| 93 // An instance is a DOM object rendered by this child process during a page | |
| 94 // load. | |
| 95 int instances; | |
| 96 | |
| 97 // The number of times there was an error loading an instance of this child | |
| 98 // process. | |
| 99 int loading_errors; | |
| 100 | |
| 101 int process_type; | |
| 102 }; | |
| 103 | |
| 104 PluginMetricsProvider::PluginMetricsProvider(PrefService* local_state) | |
| 105 : local_state_(local_state), | |
| 106 weak_ptr_factory_(this) { | |
| 107 DCHECK(local_state_); | |
| 108 | |
| 109 BrowserChildProcessObserver::Add(this); | |
| 110 } | |
| 111 | |
| 112 PluginMetricsProvider::~PluginMetricsProvider() { | |
| 113 BrowserChildProcessObserver::Remove(this); | |
| 114 } | |
| 115 | |
| 116 void PluginMetricsProvider::GetPluginInformation( | |
| 117 const base::Closure& done_callback) { | |
| 118 content::PluginService::GetInstance()->GetPlugins( | |
| 119 base::Bind(&PluginMetricsProvider::OnGotPlugins, | |
| 120 weak_ptr_factory_.GetWeakPtr(), | |
| 121 done_callback)); | |
| 122 } | |
| 123 | |
| 124 void PluginMetricsProvider::ProvideSystemProfileMetrics( | |
| 125 metrics::SystemProfileProto* system_profile_proto) { | |
| 126 PluginPrefs* plugin_prefs = GetPluginPrefs(); | |
| 127 for (size_t i = 0; i < plugins_.size(); ++i) { | |
| 128 SetPluginInfo(plugins_[i], plugin_prefs, | |
| 129 system_profile_proto->add_plugin()); | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 void PluginMetricsProvider::ProvideStabilityMetrics( | |
| 134 metrics::SystemProfileProto* system_profile_proto) { | |
| 135 // Now log plugin stability info. | |
|
Ilya Sherman
2014/05/22 14:52:20
nit: Drop this?
Alexei Svitkine (slow)
2014/05/22 15:38:54
Done.
| |
| 136 const base::ListValue* plugin_stats_list = local_state_->GetList( | |
| 137 prefs::kStabilityPluginStats); | |
| 138 if (!plugin_stats_list) | |
| 139 return; | |
| 140 | |
| 141 metrics::SystemProfileProto::Stability* stability = | |
| 142 system_profile_proto->mutable_stability(); | |
| 143 for (base::ListValue::const_iterator iter = plugin_stats_list->begin(); | |
| 144 iter != plugin_stats_list->end(); ++iter) { | |
| 145 if (!(*iter)->IsType(base::Value::TYPE_DICTIONARY)) { | |
| 146 NOTREACHED(); | |
| 147 continue; | |
| 148 } | |
| 149 base::DictionaryValue* plugin_dict = | |
| 150 static_cast<base::DictionaryValue*>(*iter); | |
| 151 | |
| 152 // Note that this search is potentially a quadratic operation, but given the | |
| 153 // low number of plugins installed on a "reasonable" setup, this should be | |
| 154 // fine. | |
| 155 // TODO(isherman): Verify that this does not show up as a hotspot in | |
| 156 // profiler runs. | |
| 157 const metrics::SystemProfileProto::Plugin* system_profile_plugin = NULL; | |
| 158 std::string plugin_name; | |
| 159 plugin_dict->GetString(prefs::kStabilityPluginName, &plugin_name); | |
| 160 for (int i = 0; i < system_profile_proto->plugin_size(); ++i) { | |
| 161 if (system_profile_proto->plugin(i).name() == plugin_name) { | |
| 162 system_profile_plugin = &system_profile_proto->plugin(i); | |
| 163 break; | |
| 164 } | |
| 165 } | |
| 166 | |
| 167 if (!system_profile_plugin) { | |
| 168 NOTREACHED(); | |
| 169 continue; | |
| 170 } | |
| 171 | |
| 172 metrics::SystemProfileProto::Stability::PluginStability* plugin_stability = | |
| 173 stability->add_plugin_stability(); | |
| 174 *plugin_stability->mutable_plugin() = *system_profile_plugin; | |
| 175 | |
| 176 int launches = 0; | |
| 177 plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches); | |
| 178 if (launches > 0) | |
| 179 plugin_stability->set_launch_count(launches); | |
| 180 | |
| 181 int instances = 0; | |
| 182 plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances); | |
| 183 if (instances > 0) | |
| 184 plugin_stability->set_instance_count(instances); | |
| 185 | |
| 186 int crashes = 0; | |
| 187 plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes); | |
| 188 if (crashes > 0) | |
| 189 plugin_stability->set_crash_count(crashes); | |
| 190 | |
| 191 int loading_errors = 0; | |
| 192 plugin_dict->GetInteger(prefs::kStabilityPluginLoadingErrors, | |
| 193 &loading_errors); | |
| 194 if (loading_errors > 0) | |
| 195 plugin_stability->set_loading_error_count(loading_errors); | |
| 196 } | |
| 197 | |
| 198 local_state_->ClearPref(prefs::kStabilityPluginStats); | |
| 199 } | |
| 200 | |
| 201 void PluginMetricsProvider::RecordPluginChanges() { | |
| 202 ListPrefUpdate update(local_state_, prefs::kStabilityPluginStats); | |
| 203 base::ListValue* plugins = update.Get(); | |
| 204 DCHECK(plugins); | |
| 205 | |
| 206 for (base::ListValue::iterator value_iter = plugins->begin(); | |
| 207 value_iter != plugins->end(); ++value_iter) { | |
| 208 if (!(*value_iter)->IsType(base::Value::TYPE_DICTIONARY)) { | |
| 209 NOTREACHED(); | |
| 210 continue; | |
| 211 } | |
| 212 | |
| 213 base::DictionaryValue* plugin_dict = | |
| 214 static_cast<base::DictionaryValue*>(*value_iter); | |
| 215 std::string plugin_name; | |
| 216 plugin_dict->GetString(prefs::kStabilityPluginName, &plugin_name); | |
| 217 if (plugin_name.empty()) { | |
| 218 NOTREACHED(); | |
| 219 continue; | |
| 220 } | |
| 221 | |
| 222 // TODO(viettrungluu): remove conversions | |
| 223 base::string16 name16 = base::UTF8ToUTF16(plugin_name); | |
| 224 if (child_process_stats_buffer_.find(name16) == | |
| 225 child_process_stats_buffer_.end()) { | |
| 226 continue; | |
| 227 } | |
| 228 | |
| 229 ChildProcessStats stats = child_process_stats_buffer_[name16]; | |
| 230 if (stats.process_launches) { | |
| 231 int launches = 0; | |
| 232 plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches); | |
| 233 launches += stats.process_launches; | |
| 234 plugin_dict->SetInteger(prefs::kStabilityPluginLaunches, launches); | |
| 235 } | |
| 236 if (stats.process_crashes) { | |
| 237 int crashes = 0; | |
| 238 plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes); | |
| 239 crashes += stats.process_crashes; | |
| 240 plugin_dict->SetInteger(prefs::kStabilityPluginCrashes, crashes); | |
| 241 } | |
| 242 if (stats.instances) { | |
| 243 int instances = 0; | |
| 244 plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances); | |
| 245 instances += stats.instances; | |
| 246 plugin_dict->SetInteger(prefs::kStabilityPluginInstances, instances); | |
| 247 } | |
| 248 if (stats.loading_errors) { | |
| 249 int loading_errors = 0; | |
| 250 plugin_dict->GetInteger(prefs::kStabilityPluginLoadingErrors, | |
| 251 &loading_errors); | |
| 252 loading_errors += stats.loading_errors; | |
| 253 plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors, | |
| 254 loading_errors); | |
| 255 } | |
| 256 | |
| 257 child_process_stats_buffer_.erase(name16); | |
| 258 } | |
| 259 | |
| 260 // Now go through and add dictionaries for plugins that didn't already have | |
| 261 // reports in Local State. | |
| 262 for (std::map<base::string16, ChildProcessStats>::iterator cache_iter = | |
| 263 child_process_stats_buffer_.begin(); | |
| 264 cache_iter != child_process_stats_buffer_.end(); ++cache_iter) { | |
| 265 ChildProcessStats stats = cache_iter->second; | |
| 266 | |
| 267 // Insert only plugins information into the plugins list. | |
| 268 if (!IsPluginProcess(stats.process_type)) | |
| 269 continue; | |
| 270 | |
| 271 // TODO(viettrungluu): remove conversion | |
| 272 std::string plugin_name = base::UTF16ToUTF8(cache_iter->first); | |
| 273 | |
| 274 base::DictionaryValue* plugin_dict = new base::DictionaryValue; | |
| 275 | |
| 276 plugin_dict->SetString(prefs::kStabilityPluginName, plugin_name); | |
| 277 plugin_dict->SetInteger(prefs::kStabilityPluginLaunches, | |
| 278 stats.process_launches); | |
| 279 plugin_dict->SetInteger(prefs::kStabilityPluginCrashes, | |
| 280 stats.process_crashes); | |
| 281 plugin_dict->SetInteger(prefs::kStabilityPluginInstances, | |
| 282 stats.instances); | |
| 283 plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors, | |
| 284 stats.loading_errors); | |
| 285 plugins->Append(plugin_dict); | |
| 286 } | |
| 287 child_process_stats_buffer_.clear(); | |
| 288 } | |
| 289 | |
| 290 void PluginMetricsProvider::LogPluginLoadingError( | |
| 291 const base::FilePath& plugin_path) { | |
| 292 content::WebPluginInfo plugin; | |
| 293 bool success = | |
| 294 content::PluginService::GetInstance()->GetPluginInfoByPath(plugin_path, | |
| 295 &plugin); | |
| 296 DCHECK(success); | |
| 297 ChildProcessStats& stats = child_process_stats_buffer_[plugin.name]; | |
| 298 // Initialize the type if this entry is new. | |
| 299 if (stats.process_type == content::PROCESS_TYPE_UNKNOWN) { | |
| 300 // The plug-in process might not actually of type PLUGIN (which means | |
|
Ilya Sherman
2014/05/22 14:52:20
nit: "might not actually of type" -> "might not ac
Alexei Svitkine (slow)
2014/05/22 15:38:54
Done.
| |
| 301 // NPAPI), but we only care that it is *a* plug-in process. | |
| 302 stats.process_type = content::PROCESS_TYPE_PLUGIN; | |
| 303 } else { | |
| 304 DCHECK(IsPluginProcess(stats.process_type)); | |
| 305 } | |
| 306 stats.loading_errors++; | |
| 307 } | |
| 308 | |
| 309 void PluginMetricsProvider::SetPluginsForTesting( | |
| 310 const std::vector<content::WebPluginInfo>& plugins) { | |
| 311 plugins_ = plugins; | |
| 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 |