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