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/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"
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.
42 std::vector
<Profile
*> profiles
= profile_manager
->GetLoadedProfiles();
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());
58 plugin
->set_is_disabled(!plugin_prefs
->IsPluginEnabled(plugin_info
));
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
{
69 explicit ChildProcessStats(int process_type
)
70 : process_launches(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.
79 : process_launches(0),
83 process_type(content::PROCESS_TYPE_UNKNOWN
) {}
85 // The number of times that the given child process has been launched
88 // The number of times that the given child process has crashed
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
96 // The number of times there was an error loading an instance of this child
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(),
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
)
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
)) {
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
154 // TODO(isherman): Verify that this does not show up as a hotspot in
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
);
166 if (!system_profile_plugin
) {
171 metrics::SystemProfileProto::Stability::PluginStability
* plugin_stability
=
172 stability
->add_plugin_stability();
173 *plugin_stability
->mutable_plugin() = *system_profile_plugin
;
176 plugin_dict
->GetInteger(prefs::kStabilityPluginLaunches
, &launches
);
178 plugin_stability
->set_launch_count(launches
);
181 plugin_dict
->GetInteger(prefs::kStabilityPluginInstances
, &instances
);
183 plugin_stability
->set_instance_count(instances
);
186 plugin_dict
->GetInteger(prefs::kStabilityPluginCrashes
, &crashes
);
188 plugin_stability
->set_crash_count(crashes
);
190 int loading_errors
= 0;
191 plugin_dict
->GetInteger(prefs::kStabilityPluginLoadingErrors
,
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();
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
)) {
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()) {
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()) {
234 ChildProcessStats stats
= child_process_stats_buffer_
[name16
];
235 if (stats
.process_launches
) {
237 plugin_dict
->GetInteger(prefs::kStabilityPluginLaunches
, &launches
);
238 launches
+= stats
.process_launches
;
239 plugin_dict
->SetInteger(prefs::kStabilityPluginLaunches
, launches
);
241 if (stats
.process_crashes
) {
243 plugin_dict
->GetInteger(prefs::kStabilityPluginCrashes
, &crashes
);
244 crashes
+= stats
.process_crashes
;
245 plugin_dict
->SetInteger(prefs::kStabilityPluginCrashes
, crashes
);
247 if (stats
.instances
) {
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
,
257 loading_errors
+= stats
.loading_errors
;
258 plugin_dict
->SetInteger(prefs::kStabilityPluginLoadingErrors
,
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
))
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
,
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
;
299 content::PluginService::GetInstance()->GetPluginInfoByPath(plugin_path
,
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
;
309 DCHECK(IsPluginProcess(stats
.process_type
));
311 stats
.loading_errors
++;
312 RecordCurrentStateWithDelay(kRecordStateDelayMs
);
315 void PluginMetricsProvider::SetPluginsForTesting(
316 const std::vector
<content::WebPluginInfo
>& plugins
) {
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
);
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
) {
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
,
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 void PluginMetricsProvider::BrowserChildProcessKilled(
370 const content::ChildProcessData
& data
,
372 // Treat a kill as a crash, since Flash returns STATUS_DEBUGGER_INACTIVE for
373 // actual crashes, which is treated as a kill rather than a crash by
374 // base::GetTerminationStatus
375 GetChildProcessStats(data
).process_crashes
++;
376 RecordCurrentStateWithDelay(kRecordStateDelayMs
);
379 bool PluginMetricsProvider::RecordCurrentStateWithDelay(int delay_sec
) {
380 if (weak_ptr_factory_
.HasWeakPtrs())
383 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
385 base::Bind(&PluginMetricsProvider::RecordCurrentState
,
386 weak_ptr_factory_
.GetWeakPtr()),
387 base::TimeDelta::FromMilliseconds(delay_sec
));
391 bool PluginMetricsProvider::RecordCurrentStateIfPending() {
392 if (!weak_ptr_factory_
.HasWeakPtrs())
395 weak_ptr_factory_
.InvalidateWeakPtrs();
396 RecordCurrentState();