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 <stddef.h>
8 
9 #include <string>
10 #include <utility>
11 
12 #include "base/macros.h"
13 #include "base/run_loop.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/test/task_environment.h"
16 #include "chrome/common/pref_names.h"
17 #include "components/prefs/pref_service.h"
18 #include "components/prefs/scoped_user_pref_update.h"
19 #include "components/prefs/testing_pref_service.h"
20 #include "content/public/browser/child_process_data.h"
21 #include "content/public/browser/child_process_termination_info.h"
22 #include "content/public/common/process_type.h"
23 #include "content/public/common/webplugininfo.h"
24 #include "content/public/test/browser_task_environment.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "third_party/metrics_proto/system_profile.pb.h"
27 
28 namespace {
29 
CreateFakePluginInfo(const std::string & name,const base::FilePath::CharType * path,const std::string & version)30 content::WebPluginInfo CreateFakePluginInfo(
31     const std::string& name,
32     const base::FilePath::CharType* path,
33     const std::string& version) {
34   content::WebPluginInfo plugin(base::UTF8ToUTF16(name),
35                                 base::FilePath(path),
36                                 base::UTF8ToUTF16(version),
37                                 base::string16());
38   plugin.type = content::WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS;
39   return plugin;
40 }
41 
42 class PluginMetricsProviderTest : public ::testing::Test {
43  protected:
PluginMetricsProviderTest()44   PluginMetricsProviderTest()
45       : prefs_(new TestingPrefServiceSimple) {
46     PluginMetricsProvider::RegisterPrefs(prefs()->registry());
47   }
48 
prefs()49   TestingPrefServiceSimple* prefs() {
50     return prefs_.get();
51   }
52 
53  private:
54   std::unique_ptr<TestingPrefServiceSimple> prefs_;
55 
56   DISALLOW_COPY_AND_ASSIGN(PluginMetricsProviderTest);
57 };
58 
59 }  // namespace
60 
TEST_F(PluginMetricsProviderTest,IsPluginProcess)61 TEST_F(PluginMetricsProviderTest, IsPluginProcess) {
62   EXPECT_TRUE(PluginMetricsProvider::IsPluginProcess(
63       content::PROCESS_TYPE_PPAPI_PLUGIN));
64   EXPECT_FALSE(PluginMetricsProvider::IsPluginProcess(
65       content::PROCESS_TYPE_GPU));
66 }
67 
TEST_F(PluginMetricsProviderTest,Plugins)68 TEST_F(PluginMetricsProviderTest, Plugins) {
69   content::BrowserTaskEnvironment task_environment;
70 
71   PluginMetricsProvider provider(prefs());
72 
73   std::vector<content::WebPluginInfo> plugins;
74   plugins.push_back(CreateFakePluginInfo("p1", FILE_PATH_LITERAL("p1.plugin"),
75                                          "1.5"));
76   plugins.push_back(CreateFakePluginInfo("p2", FILE_PATH_LITERAL("p2.plugin"),
77                                          "2.0"));
78   provider.SetPluginsForTesting(plugins);
79 
80   metrics::SystemProfileProto system_profile;
81   provider.ProvideSystemProfileMetrics(&system_profile);
82 
83   ASSERT_EQ(2, system_profile.plugin_size());
84   EXPECT_EQ("p1", system_profile.plugin(0).name());
85   EXPECT_EQ("p1.plugin", system_profile.plugin(0).filename());
86   EXPECT_EQ("1.5", system_profile.plugin(0).version());
87   EXPECT_TRUE(system_profile.plugin(0).is_pepper());
88   EXPECT_EQ("p2", system_profile.plugin(1).name());
89   EXPECT_EQ("p2.plugin", system_profile.plugin(1).filename());
90   EXPECT_EQ("2.0", system_profile.plugin(1).version());
91   EXPECT_TRUE(system_profile.plugin(1).is_pepper());
92 
93   // Now set some plugin stability stats for p2 and verify they're recorded.
94   std::unique_ptr<base::DictionaryValue> plugin_dict(new base::DictionaryValue);
95   plugin_dict->SetString(prefs::kStabilityPluginName, "p2");
96   plugin_dict->SetInteger(prefs::kStabilityPluginLaunches, 1);
97   plugin_dict->SetInteger(prefs::kStabilityPluginCrashes, 2);
98   plugin_dict->SetInteger(prefs::kStabilityPluginInstances, 3);
99   plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors, 4);
100   {
101     ListPrefUpdate update(prefs(), prefs::kStabilityPluginStats);
102     update.Get()->Append(std::move(plugin_dict));
103   }
104 
105   provider.ProvideStabilityMetrics(&system_profile);
106 
107   const metrics::SystemProfileProto_Stability& stability =
108       system_profile.stability();
109   ASSERT_EQ(1, stability.plugin_stability_size());
110   EXPECT_EQ("p2", stability.plugin_stability(0).plugin().name());
111   EXPECT_EQ("p2.plugin", stability.plugin_stability(0).plugin().filename());
112   EXPECT_EQ("2.0", stability.plugin_stability(0).plugin().version());
113   EXPECT_TRUE(stability.plugin_stability(0).plugin().is_pepper());
114   EXPECT_EQ(1, stability.plugin_stability(0).launch_count());
115   EXPECT_EQ(2, stability.plugin_stability(0).crash_count());
116   EXPECT_EQ(3, stability.plugin_stability(0).instance_count());
117   EXPECT_EQ(4, stability.plugin_stability(0).loading_error_count());
118 }
119 
TEST_F(PluginMetricsProviderTest,RecordCurrentStateWithDelay)120 TEST_F(PluginMetricsProviderTest, RecordCurrentStateWithDelay) {
121   content::BrowserTaskEnvironment task_environment(
122       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
123 
124   PluginMetricsProvider provider(prefs());
125 
126   EXPECT_TRUE(provider.RecordCurrentStateWithDelay());
127   EXPECT_FALSE(provider.RecordCurrentStateWithDelay());
128 
129   task_environment.FastForwardBy(PluginMetricsProvider::GetRecordStateDelay());
130   base::RunLoop().RunUntilIdle();
131 
132   EXPECT_TRUE(provider.RecordCurrentStateWithDelay());
133 }
134 
TEST_F(PluginMetricsProviderTest,RecordCurrentStateIfPending)135 TEST_F(PluginMetricsProviderTest, RecordCurrentStateIfPending) {
136   content::BrowserTaskEnvironment task_environment(
137       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
138 
139   PluginMetricsProvider provider(prefs());
140 
141   // First there should be no need to force RecordCurrentState.
142   EXPECT_FALSE(provider.RecordCurrentStateIfPending());
143 
144   // After delayed task is posted RecordCurrentStateIfPending should return
145   // true.
146   EXPECT_TRUE(provider.RecordCurrentStateWithDelay());
147   EXPECT_TRUE(provider.RecordCurrentStateIfPending());
148 
149   // If RecordCurrentStateIfPending was successful then we should be able to
150   // post a new delayed task.
151   EXPECT_TRUE(provider.RecordCurrentStateWithDelay());
152 }
153 
TEST_F(PluginMetricsProviderTest,ProvideStabilityMetricsWhenPendingTask)154 TEST_F(PluginMetricsProviderTest, ProvideStabilityMetricsWhenPendingTask) {
155   content::BrowserTaskEnvironment task_environment;
156 
157   PluginMetricsProvider provider(prefs());
158 
159   // Create plugin information for testing.
160   std::vector<content::WebPluginInfo> plugins;
161   plugins.push_back(
162       CreateFakePluginInfo("p1", FILE_PATH_LITERAL("p1.plugin"), "1.5"));
163   plugins.push_back(
164       CreateFakePluginInfo("p2", FILE_PATH_LITERAL("p2.plugin"), "1.5"));
165   provider.SetPluginsForTesting(plugins);
166   metrics::SystemProfileProto system_profile;
167   provider.ProvideSystemProfileMetrics(&system_profile);
168 
169   // Increase number of process launches which should also start a delayed
170   // task.
171   content::ChildProcessTerminationInfo abnormal_termination_info;
172   abnormal_termination_info.status =
173       base::TERMINATION_STATUS_ABNORMAL_TERMINATION;
174   abnormal_termination_info.exit_code = 1;
175   content::ChildProcessData child_process_data1(
176       content::PROCESS_TYPE_PPAPI_PLUGIN);
177   child_process_data1.name = base::UTF8ToUTF16("p1");
178   provider.BrowserChildProcessHostConnected(child_process_data1);
179   provider.BrowserChildProcessCrashed(child_process_data1,
180                                       abnormal_termination_info);
181 
182   // A disconnect should not generate a crash event.
183   provider.BrowserChildProcessHostConnected(child_process_data1);
184   provider.BrowserChildProcessHostDisconnected(child_process_data1);
185 
186   content::ChildProcessData child_process_data2(
187       content::PROCESS_TYPE_PPAPI_PLUGIN);
188   child_process_data2.name = base::UTF8ToUTF16("p2");
189   provider.BrowserChildProcessHostConnected(child_process_data2);
190   provider.BrowserChildProcessCrashed(child_process_data2,
191                                       abnormal_termination_info);
192 
193   // A kill should generate a crash event
194   provider.BrowserChildProcessHostConnected(child_process_data2);
195   provider.BrowserChildProcessKilled(child_process_data2,
196                                      abnormal_termination_info);
197 
198   // Call ProvideStabilityMetrics to check that it will force pending tasks to
199   // be executed immediately.
200   provider.ProvideStabilityMetrics(&system_profile);
201 
202   // Check current number of instances created.
203   const metrics::SystemProfileProto_Stability& stability =
204       system_profile.stability();
205   size_t found = 0;
206   EXPECT_EQ(stability.plugin_stability_size(), 2);
207   for (int i = 0; i < 2; i++) {
208     std::string name = stability.plugin_stability(i).plugin().name();
209     if (name == "p1") {
210       EXPECT_EQ(2, stability.plugin_stability(i).launch_count());
211       EXPECT_EQ(1, stability.plugin_stability(i).crash_count());
212       found++;
213     } else if (name == "p2") {
214       EXPECT_EQ(2, stability.plugin_stability(i).launch_count());
215       EXPECT_EQ(2, stability.plugin_stability(i).crash_count());
216       found++;
217     } else {
218       GTEST_FAIL() << "Unexpected plugin name : " << name;
219     }
220   }
221   EXPECT_EQ(found, 2U);
222 }
223