• 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 "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"
24 
25 namespace {
26 
27 // Delay for RecordCurrentState execution.
28 const int kRecordStateDelayMs = 15 * base::Time::kMillisecondsPerSecond;
29 
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.
GetPluginPrefs()33 PluginPrefs* GetPluginPrefs() {
34   ProfileManager* profile_manager = g_browser_process->profile_manager();
35 
36   if (!profile_manager) {
37     // The profile manager can be NULL when testing.
38     return NULL;
39   }
40 
41   std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles();
42   if (profiles.empty())
43     return NULL;
44 
45   return PluginPrefs::GetForProfile(profiles.front()).get();
46 }
47 
48 // 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)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());
56   if (plugin_prefs)
57     plugin->set_is_disabled(!plugin_prefs->IsPluginEnabled(plugin_info));
58 }
59 
60 }  // namespace
61 
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 {
67  public:
ChildProcessStatsPluginMetricsProvider::ChildProcessStats68   explicit ChildProcessStats(int process_type)
69       : process_launches(0),
70         process_crashes(0),
71         instances(0),
72         loading_errors(0),
73         process_type(process_type) {}
74 
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.
ChildProcessStatsPluginMetricsProvider::ChildProcessStats77   ChildProcessStats()
78       : process_launches(0),
79         process_crashes(0),
80         instances(0),
81         loading_errors(0),
82         process_type(content::PROCESS_TYPE_UNKNOWN) {}
83 
84   // The number of times that the given child process has been launched
85   int process_launches;
86 
87   // The number of times that the given child process has crashed
88   int process_crashes;
89 
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
92   // load.
93   int instances;
94 
95   // The number of times there was an error loading an instance of this child
96   // process.
97   int loading_errors;
98 
99   int process_type;
100 };
101 
PluginMetricsProvider(PrefService * local_state)102 PluginMetricsProvider::PluginMetricsProvider(PrefService* local_state)
103     : local_state_(local_state),
104       weak_ptr_factory_(this) {
105   DCHECK(local_state_);
106 
107   BrowserChildProcessObserver::Add(this);
108 }
109 
~PluginMetricsProvider()110 PluginMetricsProvider::~PluginMetricsProvider() {
111   BrowserChildProcessObserver::Remove(this);
112 }
113 
GetPluginInformation(const base::Closure & done_callback)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(),
119                  done_callback));
120 }
121 
ProvideSystemProfileMetrics(metrics::SystemProfileProto * system_profile_proto)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());
128   }
129 }
130 
ProvideStabilityMetrics(metrics::SystemProfileProto * system_profile_proto)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)
137     return;
138 
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)) {
144       NOTREACHED();
145       continue;
146     }
147     base::DictionaryValue* plugin_dict =
148         static_cast<base::DictionaryValue*>(*iter);
149 
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
152     // fine.
153     // TODO(isherman): Verify that this does not show up as a hotspot in
154     // profiler runs.
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);
161         break;
162       }
163     }
164 
165     if (!system_profile_plugin) {
166       NOTREACHED();
167       continue;
168     }
169 
170     metrics::SystemProfileProto::Stability::PluginStability* plugin_stability =
171         stability->add_plugin_stability();
172     *plugin_stability->mutable_plugin() = *system_profile_plugin;
173 
174     int launches = 0;
175     plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches);
176     if (launches > 0)
177       plugin_stability->set_launch_count(launches);
178 
179     int instances = 0;
180     plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances);
181     if (instances > 0)
182       plugin_stability->set_instance_count(instances);
183 
184     int crashes = 0;
185     plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes);
186     if (crashes > 0)
187       plugin_stability->set_crash_count(crashes);
188 
189     int loading_errors = 0;
190     plugin_dict->GetInteger(prefs::kStabilityPluginLoadingErrors,
191                             &loading_errors);
192     if (loading_errors > 0)
193       plugin_stability->set_loading_error_count(loading_errors);
194   }
195 
196   local_state_->ClearPref(prefs::kStabilityPluginStats);
197 }
198 
ClearSavedStabilityMetrics()199 void PluginMetricsProvider::ClearSavedStabilityMetrics() {
200   local_state_->ClearPref(prefs::kStabilityPluginStats);
201 }
202 
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).
RecordCurrentState()205 void PluginMetricsProvider::RecordCurrentState() {
206   ListPrefUpdate update(local_state_, prefs::kStabilityPluginStats);
207   base::ListValue* plugins = update.Get();
208   DCHECK(plugins);
209 
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)) {
213       NOTREACHED();
214       continue;
215     }
216 
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()) {
222       NOTREACHED();
223       continue;
224     }
225 
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()) {
230       continue;
231     }
232 
233     ChildProcessStats stats = child_process_stats_buffer_[name16];
234     if (stats.process_launches) {
235       int launches = 0;
236       plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches);
237       launches += stats.process_launches;
238       plugin_dict->SetInteger(prefs::kStabilityPluginLaunches, launches);
239     }
240     if (stats.process_crashes) {
241       int crashes = 0;
242       plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes);
243       crashes += stats.process_crashes;
244       plugin_dict->SetInteger(prefs::kStabilityPluginCrashes, crashes);
245     }
246     if (stats.instances) {
247       int instances = 0;
248       plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances);
249       instances += stats.instances;
250       plugin_dict->SetInteger(prefs::kStabilityPluginInstances, instances);
251     }
252     if (stats.loading_errors) {
253       int loading_errors = 0;
254       plugin_dict->GetInteger(prefs::kStabilityPluginLoadingErrors,
255                               &loading_errors);
256       loading_errors += stats.loading_errors;
257       plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors,
258                               loading_errors);
259     }
260 
261     child_process_stats_buffer_.erase(name16);
262   }
263 
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;
270 
271     // Insert only plugins information into the plugins list.
272     if (!IsPluginProcess(stats.process_type))
273       continue;
274 
275     // TODO(viettrungluu): remove conversion
276     std::string plugin_name = base::UTF16ToUTF8(cache_iter->first);
277 
278     base::DictionaryValue* plugin_dict = new base::DictionaryValue;
279 
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,
286                             stats.instances);
287     plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors,
288                             stats.loading_errors);
289     plugins->Append(plugin_dict);
290   }
291   child_process_stats_buffer_.clear();
292 }
293 
LogPluginLoadingError(const base::FilePath & plugin_path)294 void PluginMetricsProvider::LogPluginLoadingError(
295     const base::FilePath& plugin_path) {
296   content::WebPluginInfo plugin;
297   bool success =
298       content::PluginService::GetInstance()->GetPluginInfoByPath(plugin_path,
299                                                                  &plugin);
300   DCHECK(success);
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;
307   } else {
308     DCHECK(IsPluginProcess(stats.process_type));
309   }
310   stats.loading_errors++;
311   RecordCurrentStateWithDelay(kRecordStateDelayMs);
312 }
313 
SetPluginsForTesting(const std::vector<content::WebPluginInfo> & plugins)314 void PluginMetricsProvider::SetPluginsForTesting(
315     const std::vector<content::WebPluginInfo>& plugins) {
316   plugins_ = plugins;
317 }
318 
319 // static
IsPluginProcess(int process_type)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);
324 }
325 
326 // static
RegisterPrefs(PrefRegistrySimple * registry)327 void PluginMetricsProvider::RegisterPrefs(PrefRegistrySimple* registry) {
328   registry->RegisterListPref(prefs::kStabilityPluginStats);
329 }
330 
OnGotPlugins(const base::Closure & done_callback,const std::vector<content::WebPluginInfo> & plugins)331 void PluginMetricsProvider::OnGotPlugins(
332     const base::Closure& done_callback,
333     const std::vector<content::WebPluginInfo>& plugins) {
334   plugins_ = plugins;
335   done_callback.Run();
336 }
337 
338 PluginMetricsProvider::ChildProcessStats&
GetChildProcessStats(const content::ChildProcessData & data)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);
345   }
346   return child_process_stats_buffer_[child_name];
347 }
348 
BrowserChildProcessHostConnected(const content::ChildProcessData & data)349 void PluginMetricsProvider::BrowserChildProcessHostConnected(
350     const content::ChildProcessData& data) {
351   GetChildProcessStats(data).process_launches++;
352   RecordCurrentStateWithDelay(kRecordStateDelayMs);
353 }
354 
BrowserChildProcessCrashed(const content::ChildProcessData & data)355 void PluginMetricsProvider::BrowserChildProcessCrashed(
356     const content::ChildProcessData& data) {
357   GetChildProcessStats(data).process_crashes++;
358   RecordCurrentStateWithDelay(kRecordStateDelayMs);
359 }
360 
BrowserChildProcessInstanceCreated(const content::ChildProcessData & data)361 void PluginMetricsProvider::BrowserChildProcessInstanceCreated(
362     const content::ChildProcessData& data) {
363   GetChildProcessStats(data).instances++;
364   RecordCurrentStateWithDelay(kRecordStateDelayMs);
365 }
366 
RecordCurrentStateWithDelay(int delay_sec)367 bool PluginMetricsProvider::RecordCurrentStateWithDelay(int delay_sec) {
368   if (weak_ptr_factory_.HasWeakPtrs())
369     return false;
370 
371   base::MessageLoopProxy::current()->PostDelayedTask(
372       FROM_HERE,
373       base::Bind(&PluginMetricsProvider::RecordCurrentState,
374                 weak_ptr_factory_.GetWeakPtr()),
375                 base::TimeDelta::FromMilliseconds(delay_sec));
376   return true;
377 }
378 
RecordCurrentStateIfPending()379 bool PluginMetricsProvider::RecordCurrentStateIfPending() {
380   if (!weak_ptr_factory_.HasWeakPtrs())
381     return false;
382 
383   weak_ptr_factory_.InvalidateWeakPtrs();
384   RecordCurrentState();
385   return true;
386 }
387