1 // Copyright 2015 The Chromium Authors
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 "components/metrics/content/content_stability_metrics_provider.h"
6
7 #include "base/memory/raw_ptr.h"
8 #include "base/test/metrics/histogram_tester.h"
9 #include "build/build_config.h"
10 #include "components/metrics/content/extensions_helper.h"
11 #include "components/prefs/pref_service.h"
12 #include "components/prefs/scoped_user_pref_update.h"
13 #include "components/prefs/testing_pref_service.h"
14 #include "components/variations/hashing.h"
15 #include "content/public/browser/browser_context.h"
16 #include "content/public/browser/child_process_data.h"
17 #include "content/public/browser/child_process_termination_info.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "content/public/browser/site_instance.h"
20 #include "content/public/common/process_type.h"
21 #include "content/public/test/browser_task_environment.h"
22 #include "content/public/test/mock_render_process_host.h"
23 #include "content/public/test/test_browser_context.h"
24 #include "extensions/buildflags/buildflags.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "third_party/metrics_proto/system_profile.pb.h"
27
28 namespace metrics {
29
30 namespace {
31
32 const char kTestUtilityProcessName[] = "test_utility_process";
33
34 class MockExtensionsHelper : public ExtensionsHelper {
35 public:
36 MockExtensionsHelper() = default;
37 MockExtensionsHelper(const MockExtensionsHelper&) = delete;
38 MockExtensionsHelper& operator=(const MockExtensionsHelper&) = delete;
39 ~MockExtensionsHelper() override = default;
40
set_extension_host(content::RenderProcessHost * host)41 void set_extension_host(content::RenderProcessHost* host) { host_ = host; }
42 // ExtensionsHelper:
IsExtensionProcess(content::RenderProcessHost * render_process_host)43 bool IsExtensionProcess(
44 content::RenderProcessHost* render_process_host) override {
45 return render_process_host == host_;
46 }
47
48 private:
49 raw_ptr<content::RenderProcessHost> host_ = nullptr;
50 };
51
52 } // namespace
53
54 class ContentStabilityMetricsProviderTest : public testing::Test {
55 protected:
ContentStabilityMetricsProviderTest()56 ContentStabilityMetricsProviderTest()
57 : prefs_(std::make_unique<TestingPrefServiceSimple>()) {
58 metrics::StabilityMetricsHelper::RegisterPrefs(prefs()->registry());
59 }
60 ContentStabilityMetricsProviderTest(
61 const ContentStabilityMetricsProviderTest&) = delete;
62 ContentStabilityMetricsProviderTest& operator=(
63 const ContentStabilityMetricsProviderTest&) = delete;
64 ~ContentStabilityMetricsProviderTest() override = default;
65
prefs()66 TestingPrefServiceSimple* prefs() { return prefs_.get(); }
67
68 private:
69 std::unique_ptr<TestingPrefServiceSimple> prefs_;
70 content::BrowserTaskEnvironment task_environment_;
71 };
72
TEST_F(ContentStabilityMetricsProviderTest,BrowserChildProcessObserverUtility)73 TEST_F(ContentStabilityMetricsProviderTest,
74 BrowserChildProcessObserverUtility) {
75 base::HistogramTester histogram_tester;
76 metrics::ContentStabilityMetricsProvider provider(prefs(), nullptr);
77
78 content::ChildProcessData child_process_data(content::PROCESS_TYPE_UTILITY);
79 child_process_data.metrics_name = kTestUtilityProcessName;
80
81 provider.BrowserChildProcessLaunchedAndConnected(child_process_data);
82 const int kExitCode = 1;
83 content::ChildProcessTerminationInfo abnormal_termination_info;
84 abnormal_termination_info.status =
85 base::TERMINATION_STATUS_ABNORMAL_TERMINATION;
86 abnormal_termination_info.exit_code = kExitCode;
87 provider.BrowserChildProcessCrashed(child_process_data,
88 abnormal_termination_info);
89 provider.BrowserChildProcessCrashed(child_process_data,
90 abnormal_termination_info);
91
92 // Verify metrics.
93 histogram_tester.ExpectUniqueSample(
94 "ChildProcess.Launched.UtilityProcessHash",
95 variations::HashName(kTestUtilityProcessName), 1);
96 histogram_tester.ExpectBucketCount("Stability.Counts2",
97 StabilityEventType::kUtilityLaunch, 1);
98 histogram_tester.ExpectUniqueSample(
99 "ChildProcess.Crashed.UtilityProcessHash",
100 variations::HashName(kTestUtilityProcessName), 2);
101 histogram_tester.ExpectUniqueSample(
102 "ChildProcess.Crashed.UtilityProcessExitCode", kExitCode, 2);
103 histogram_tester.ExpectBucketCount("Stability.Counts2",
104 StabilityEventType::kUtilityCrash, 2);
105 }
106
107 #if !BUILDFLAG(IS_ANDROID)
TEST_F(ContentStabilityMetricsProviderTest,RenderProcessObserver)108 TEST_F(ContentStabilityMetricsProviderTest, RenderProcessObserver) {
109 metrics::ContentStabilityMetricsProvider provider(prefs(), nullptr);
110 content::TestBrowserContext browser_context;
111 content::MockRenderProcessHostFactory rph_factory;
112 scoped_refptr<content::SiteInstance> site_instance(
113 content::SiteInstance::Create(&browser_context));
114
115 // Owned by rph_factory.
116 content::RenderProcessHost* host(rph_factory.CreateRenderProcessHost(
117 &browser_context, site_instance.get()));
118
119 base::HistogramTester histogram_tester;
120
121 // Crash and abnormal termination should increment renderer crash count.
122 content::ChildProcessTerminationInfo crash_details;
123 crash_details.status = base::TERMINATION_STATUS_PROCESS_CRASHED;
124 crash_details.exit_code = 1;
125 provider.OnRenderProcessHostCreated(host);
126 provider.RenderProcessExited(host, crash_details);
127
128 content::ChildProcessTerminationInfo term_details;
129 term_details.status = base::TERMINATION_STATUS_ABNORMAL_TERMINATION;
130 term_details.exit_code = 1;
131 provider.OnRenderProcessHostCreated(host);
132 provider.RenderProcessExited(host, term_details);
133
134 // Kill does not increment renderer crash count.
135 content::ChildProcessTerminationInfo kill_details;
136 kill_details.status = base::TERMINATION_STATUS_PROCESS_WAS_KILLED;
137 kill_details.exit_code = 1;
138 provider.OnRenderProcessHostCreated(host);
139 provider.RenderProcessExited(host, kill_details);
140
141 // Failed launch increments failed launch count.
142 content::ChildProcessTerminationInfo failed_launch_details;
143 failed_launch_details.status = base::TERMINATION_STATUS_LAUNCH_FAILED;
144 failed_launch_details.exit_code = 1;
145 provider.OnRenderProcessHostCreated(host);
146 provider.RenderProcessExited(host, failed_launch_details);
147
148 // Verify metrics.
149 histogram_tester.ExpectBucketCount("Stability.Counts2",
150 StabilityEventType::kRendererCrash, 2);
151 histogram_tester.ExpectBucketCount(
152 "Stability.Counts2", StabilityEventType::kRendererFailedLaunch, 1);
153 histogram_tester.ExpectBucketCount("Stability.Counts2",
154 StabilityEventType::kExtensionCrash, 0);
155 }
156
TEST_F(ContentStabilityMetricsProviderTest,MetricsServicesWebContentsObserver)157 TEST_F(ContentStabilityMetricsProviderTest,
158 MetricsServicesWebContentsObserver) {
159 metrics::ContentStabilityMetricsProvider provider(prefs(), nullptr);
160 base::HistogramTester histogram_tester;
161 histogram_tester.ExpectBucketCount("Stability.Counts2",
162 StabilityEventType::kPageLoad, 0);
163
164 // Simulate page loads.
165 const auto expected_page_load_count = 4;
166 for (int i = 0; i < expected_page_load_count; i++) {
167 provider.OnPageLoadStarted();
168 }
169
170 // Verify metrics.
171 histogram_tester.ExpectBucketCount("Stability.Counts2",
172 StabilityEventType::kPageLoad,
173 expected_page_load_count);
174 }
175
176 #endif // !BUILDFLAG(IS_ANDROID)
177
178 // Assertions for an extension related crash.
179 // This test only works if extensions are enabled as there is a DCHECK in
180 // StabilityMetricsHelper that it is only called with a value of true for
181 // extension process if extensions are enabled.
182 #if BUILDFLAG(ENABLE_EXTENSIONS)
TEST_F(ContentStabilityMetricsProviderTest,ExtensionsNotificationObserver)183 TEST_F(ContentStabilityMetricsProviderTest, ExtensionsNotificationObserver) {
184 content::TestBrowserContext browser_context;
185 content::MockRenderProcessHostFactory rph_factory;
186 scoped_refptr<content::SiteInstance> site_instance(
187 content::SiteInstance::Create(&browser_context));
188
189 // Owned by rph_factory.
190 content::RenderProcessHost* extension_host =
191 rph_factory.CreateRenderProcessHost(&browser_context,
192 site_instance.get());
193 auto extensions_helper = std::make_unique<MockExtensionsHelper>();
194 extensions_helper->set_extension_host(extension_host);
195 metrics::ContentStabilityMetricsProvider provider(
196 prefs(), std::move(extensions_helper));
197
198 base::HistogramTester histogram_tester;
199
200 // Crash and abnormal termination should increment extension crash count.
201 content::ChildProcessTerminationInfo crash_details;
202 crash_details.status = base::TERMINATION_STATUS_PROCESS_CRASHED;
203 crash_details.exit_code = 1;
204 provider.OnRenderProcessHostCreated(extension_host);
205 provider.RenderProcessExited(extension_host, crash_details);
206
207 // Failed launch increments failed launch count.
208 content::ChildProcessTerminationInfo failed_launch_details;
209 failed_launch_details.status = base::TERMINATION_STATUS_LAUNCH_FAILED;
210 failed_launch_details.exit_code = 1;
211 provider.OnRenderProcessHostCreated(extension_host);
212 provider.RenderProcessExited(extension_host, failed_launch_details);
213
214 // Verify metrics.
215 histogram_tester.ExpectBucketCount("Stability.Counts2",
216 StabilityEventType::kRendererCrash, 0);
217 histogram_tester.ExpectBucketCount("Stability.Counts2",
218 StabilityEventType::kExtensionCrash, 1);
219 histogram_tester.ExpectBucketCount(
220 "Stability.Counts2", StabilityEventType::kExtensionRendererFailedLaunch,
221 1);
222 }
223 #endif
224
225 } // namespace metrics
226