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