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 |