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"
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"
27 // Delay for RecordCurrentState execution.
28 const int kRecordStateDelayMs
= 15 * base::Time::kMillisecondsPerSecond
;
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();
36 if (!profile_manager
) {
37 // The profile manager can be NULL when testing.
41 std::vector
<Profile
*> profiles
= profile_manager
->GetLoadedProfiles();
45 return PluginPrefs::GetForProfile(profiles
.front()).get();
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());
57 plugin
->set_is_disabled(!plugin_prefs
->IsPluginEnabled(plugin_info
));
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
{
68 explicit ChildProcessStats(int process_type
)
69 : process_launches(0),
73 process_type(process_type
) {}
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.
78 : process_launches(0),
82 process_type(content::PROCESS_TYPE_UNKNOWN
) {}
84 // The number of times that the given child process has been launched
87 // The number of times that the given child process has crashed
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
95 // The number of times there was an error loading an instance of this child
102 PluginMetricsProvider::PluginMetricsProvider(PrefService
* local_state
)
103 : local_state_(local_state
),
104 weak_ptr_factory_(this) {
105 DCHECK(local_state_
);
107 BrowserChildProcessObserver::Add(this);
110 PluginMetricsProvider::~PluginMetricsProvider() {
111 BrowserChildProcessObserver::Remove(this);
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(),
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());
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
)
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
)) {
147 base::DictionaryValue
* plugin_dict
=
148 static_cast<base::DictionaryValue
*>(*iter
);
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
153 // TODO(isherman): Verify that this does not show up as a hotspot in
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
);
165 if (!system_profile_plugin
) {
170 metrics::SystemProfileProto::Stability::PluginStability
* plugin_stability
=
171 stability
->add_plugin_stability();
172 *plugin_stability
->mutable_plugin() = *system_profile_plugin
;
175 plugin_dict
->GetInteger(prefs::kStabilityPluginLaunches
, &launches
);
177 plugin_stability
->set_launch_count(launches
);
180 plugin_dict
->GetInteger(prefs::kStabilityPluginInstances
, &instances
);
182 plugin_stability
->set_instance_count(instances
);
185 plugin_dict
->GetInteger(prefs::kStabilityPluginCrashes
, &crashes
);
187 plugin_stability
->set_crash_count(crashes
);
189 int loading_errors
= 0;
190 plugin_dict
->GetInteger(prefs::kStabilityPluginLoadingErrors
,
192 if (loading_errors
> 0)
193 plugin_stability
->set_loading_error_count(loading_errors
);
196 local_state_
->ClearPref(prefs::kStabilityPluginStats
);
199 void PluginMetricsProvider::ClearSavedStabilityMetrics() {
200 local_state_
->ClearPref(prefs::kStabilityPluginStats
);
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();
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
)) {
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()) {
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()) {
233 ChildProcessStats stats
= child_process_stats_buffer_
[name16
];
234 if (stats
.process_launches
) {
236 plugin_dict
->GetInteger(prefs::kStabilityPluginLaunches
, &launches
);
237 launches
+= stats
.process_launches
;
238 plugin_dict
->SetInteger(prefs::kStabilityPluginLaunches
, launches
);
240 if (stats
.process_crashes
) {
242 plugin_dict
->GetInteger(prefs::kStabilityPluginCrashes
, &crashes
);
243 crashes
+= stats
.process_crashes
;
244 plugin_dict
->SetInteger(prefs::kStabilityPluginCrashes
, crashes
);
246 if (stats
.instances
) {
248 plugin_dict
->GetInteger(prefs::kStabilityPluginInstances
, &instances
);
249 instances
+= stats
.instances
;
250 plugin_dict
->SetInteger(prefs::kStabilityPluginInstances
, instances
);
252 if (stats
.loading_errors
) {
253 int loading_errors
= 0;
254 plugin_dict
->GetInteger(prefs::kStabilityPluginLoadingErrors
,
256 loading_errors
+= stats
.loading_errors
;
257 plugin_dict
->SetInteger(prefs::kStabilityPluginLoadingErrors
,
261 child_process_stats_buffer_
.erase(name16
);
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
;
271 // Insert only plugins information into the plugins list.
272 if (!IsPluginProcess(stats
.process_type
))
275 // TODO(viettrungluu): remove conversion
276 std::string plugin_name
= base::UTF16ToUTF8(cache_iter
->first
);
278 base::DictionaryValue
* plugin_dict
= new base::DictionaryValue
;
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
,
287 plugin_dict
->SetInteger(prefs::kStabilityPluginLoadingErrors
,
288 stats
.loading_errors
);
289 plugins
->Append(plugin_dict
);
291 child_process_stats_buffer_
.clear();
294 void PluginMetricsProvider::LogPluginLoadingError(
295 const base::FilePath
& plugin_path
) {
296 content::WebPluginInfo plugin
;
298 content::PluginService::GetInstance()->GetPluginInfoByPath(plugin_path
,
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
;
308 DCHECK(IsPluginProcess(stats
.process_type
));
310 stats
.loading_errors
++;
311 RecordCurrentStateWithDelay(kRecordStateDelayMs
);
314 void PluginMetricsProvider::SetPluginsForTesting(
315 const std::vector
<content::WebPluginInfo
>& plugins
) {
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
);
327 void PluginMetricsProvider::RegisterPrefs(PrefRegistrySimple
* registry
) {
328 registry
->RegisterListPref(prefs::kStabilityPluginStats
);
331 void PluginMetricsProvider::OnGotPlugins(
332 const base::Closure
& done_callback
,
333 const std::vector
<content::WebPluginInfo
>& plugins
) {
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
);
346 return child_process_stats_buffer_
[child_name
];
349 void PluginMetricsProvider::BrowserChildProcessHostConnected(
350 const content::ChildProcessData
& data
) {
351 GetChildProcessStats(data
).process_launches
++;
352 RecordCurrentStateWithDelay(kRecordStateDelayMs
);
355 void PluginMetricsProvider::BrowserChildProcessCrashed(
356 const content::ChildProcessData
& data
) {
357 GetChildProcessStats(data
).process_crashes
++;
358 RecordCurrentStateWithDelay(kRecordStateDelayMs
);
361 void PluginMetricsProvider::BrowserChildProcessInstanceCreated(
362 const content::ChildProcessData
& data
) {
363 GetChildProcessStats(data
).instances
++;
364 RecordCurrentStateWithDelay(kRecordStateDelayMs
);
367 bool PluginMetricsProvider::RecordCurrentStateWithDelay(int delay_sec
) {
368 if (weak_ptr_factory_
.HasWeakPtrs())
371 base::MessageLoopProxy::current()->PostDelayedTask(
373 base::Bind(&PluginMetricsProvider::RecordCurrentState
,
374 weak_ptr_factory_
.GetWeakPtr()),
375 base::TimeDelta::FromMilliseconds(delay_sec
));
379 bool PluginMetricsProvider::RecordCurrentStateIfPending() {
380 if (!weak_ptr_factory_
.HasWeakPtrs())
383 weak_ptr_factory_
.InvalidateWeakPtrs();
384 RecordCurrentState();