plugin_metrics_provider.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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
24namespace {
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.
29PluginPrefs* 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|.
45void 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.
62struct PluginMetricsProvider::ChildProcessStats {
63 public:
64  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.
73  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
98PluginMetricsProvider::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
106PluginMetricsProvider::~PluginMetricsProvider() {
107  BrowserChildProcessObserver::Remove(this);
108}
109
110void 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
118void 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
127void 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
194void PluginMetricsProvider::RecordPluginChanges() {
195  ListPrefUpdate update(local_state_, prefs::kStabilityPluginStats);
196  base::ListValue* plugins = update.Get();
197  DCHECK(plugins);
198
199  for (base::ListValue::iterator value_iter = plugins->begin();
200       value_iter != plugins->end(); ++value_iter) {
201    if (!(*value_iter)->IsType(base::Value::TYPE_DICTIONARY)) {
202      NOTREACHED();
203      continue;
204    }
205
206    base::DictionaryValue* plugin_dict =
207        static_cast<base::DictionaryValue*>(*value_iter);
208    std::string plugin_name;
209    plugin_dict->GetString(prefs::kStabilityPluginName, &plugin_name);
210    if (plugin_name.empty()) {
211      NOTREACHED();
212      continue;
213    }
214
215    // TODO(viettrungluu): remove conversions
216    base::string16 name16 = base::UTF8ToUTF16(plugin_name);
217    if (child_process_stats_buffer_.find(name16) ==
218        child_process_stats_buffer_.end()) {
219      continue;
220    }
221
222    ChildProcessStats stats = child_process_stats_buffer_[name16];
223    if (stats.process_launches) {
224      int launches = 0;
225      plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches);
226      launches += stats.process_launches;
227      plugin_dict->SetInteger(prefs::kStabilityPluginLaunches, launches);
228    }
229    if (stats.process_crashes) {
230      int crashes = 0;
231      plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes);
232      crashes += stats.process_crashes;
233      plugin_dict->SetInteger(prefs::kStabilityPluginCrashes, crashes);
234    }
235    if (stats.instances) {
236      int instances = 0;
237      plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances);
238      instances += stats.instances;
239      plugin_dict->SetInteger(prefs::kStabilityPluginInstances, instances);
240    }
241    if (stats.loading_errors) {
242      int loading_errors = 0;
243      plugin_dict->GetInteger(prefs::kStabilityPluginLoadingErrors,
244                              &loading_errors);
245      loading_errors += stats.loading_errors;
246      plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors,
247                              loading_errors);
248    }
249
250    child_process_stats_buffer_.erase(name16);
251  }
252
253  // Now go through and add dictionaries for plugins that didn't already have
254  // reports in Local State.
255  for (std::map<base::string16, ChildProcessStats>::iterator cache_iter =
256           child_process_stats_buffer_.begin();
257       cache_iter != child_process_stats_buffer_.end(); ++cache_iter) {
258    ChildProcessStats stats = cache_iter->second;
259
260    // Insert only plugins information into the plugins list.
261    if (!IsPluginProcess(stats.process_type))
262      continue;
263
264    // TODO(viettrungluu): remove conversion
265    std::string plugin_name = base::UTF16ToUTF8(cache_iter->first);
266
267    base::DictionaryValue* plugin_dict = new base::DictionaryValue;
268
269    plugin_dict->SetString(prefs::kStabilityPluginName, plugin_name);
270    plugin_dict->SetInteger(prefs::kStabilityPluginLaunches,
271                            stats.process_launches);
272    plugin_dict->SetInteger(prefs::kStabilityPluginCrashes,
273                            stats.process_crashes);
274    plugin_dict->SetInteger(prefs::kStabilityPluginInstances,
275                            stats.instances);
276    plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors,
277                            stats.loading_errors);
278    plugins->Append(plugin_dict);
279  }
280  child_process_stats_buffer_.clear();
281}
282
283void PluginMetricsProvider::LogPluginLoadingError(
284    const base::FilePath& plugin_path) {
285  content::WebPluginInfo plugin;
286  bool success =
287      content::PluginService::GetInstance()->GetPluginInfoByPath(plugin_path,
288                                                                 &plugin);
289  DCHECK(success);
290  ChildProcessStats& stats = child_process_stats_buffer_[plugin.name];
291  // Initialize the type if this entry is new.
292  if (stats.process_type == content::PROCESS_TYPE_UNKNOWN) {
293    // The plug-in process might not actually be of type PLUGIN (which means
294    // NPAPI), but we only care that it is *a* plug-in process.
295    stats.process_type = content::PROCESS_TYPE_PLUGIN;
296  } else {
297    DCHECK(IsPluginProcess(stats.process_type));
298  }
299  stats.loading_errors++;
300}
301
302void PluginMetricsProvider::SetPluginsForTesting(
303    const std::vector<content::WebPluginInfo>& plugins) {
304  plugins_ = plugins;
305}
306
307// static
308bool PluginMetricsProvider::IsPluginProcess(int process_type) {
309  return (process_type == content::PROCESS_TYPE_PLUGIN ||
310          process_type == content::PROCESS_TYPE_PPAPI_PLUGIN ||
311          process_type == content::PROCESS_TYPE_PPAPI_BROKER);
312}
313
314// static
315void PluginMetricsProvider::RegisterPrefs(PrefRegistrySimple* registry) {
316  registry->RegisterListPref(prefs::kStabilityPluginStats);
317}
318
319void PluginMetricsProvider::OnGotPlugins(
320    const base::Closure& done_callback,
321    const std::vector<content::WebPluginInfo>& plugins) {
322  plugins_ = plugins;
323  done_callback.Run();
324}
325
326PluginMetricsProvider::ChildProcessStats&
327PluginMetricsProvider::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
337void PluginMetricsProvider::BrowserChildProcessHostConnected(
338    const content::ChildProcessData& data) {
339  GetChildProcessStats(data).process_launches++;
340}
341
342void PluginMetricsProvider::BrowserChildProcessCrashed(
343    const content::ChildProcessData& data) {
344  GetChildProcessStats(data).process_crashes++;
345}
346
347void PluginMetricsProvider::BrowserChildProcessInstanceCreated(
348    const content::ChildProcessData& data) {
349  GetChildProcessStats(data).instances++;
350}
351