1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "reporter.h"
18
19 #include "gtest/gtest.h"
20
21 #include "common_runtime_test.h"
22 #include "base/metrics/metrics.h"
23
24 #pragma clang diagnostic push
25 #pragma clang diagnostic error "-Wconversion"
26
27 namespace art HIDDEN {
28 namespace metrics {
29
30 // Helper class to verify the metrics reporter.
31 // The functionality is identical to the MetricsReporter with the exception of
32 // the metrics source. Instead of taking its metrics from the current Runtime,
33 // this class will keep its own copy so that it does not get interference from
34 // other runtime setup logic.
35 class MockMetricsReporter : public MetricsReporter {
36 protected:
MockMetricsReporter(const ReportingConfig & config,Runtime * runtime)37 MockMetricsReporter(const ReportingConfig& config, Runtime* runtime)
38 : MetricsReporter(config, runtime), art_metrics_(std::make_unique<ArtMetrics>()) {}
39
GetMetrics()40 ArtMetrics* GetMetrics() override { return art_metrics_.get(); }
41
42 std::unique_ptr<ArtMetrics> art_metrics_;
43
44 friend class MetricsReporterTest;
45 };
46
47 // A test backend which keeps track of all metrics reporting.
48 class TestBackend : public MetricsBackend {
49 public:
50 struct Report {
51 uint64_t timestamp_millis;
52 SafeMap<DatumId, uint64_t> data;
53 };
54
BeginOrUpdateSession(const SessionData & session_data)55 void BeginOrUpdateSession(const SessionData& session_data) override {
56 session_data_ = session_data;
57 }
58
BeginReport(uint64_t timestamp_millis)59 void BeginReport(uint64_t timestamp_millis) override {
60 current_report_.reset(new Report());
61 current_report_->timestamp_millis = timestamp_millis;
62 }
63
ReportCounter(DatumId counter_type,uint64_t value)64 void ReportCounter(DatumId counter_type, uint64_t value) override {
65 current_report_->data.Put(counter_type, value);
66 }
67
ReportHistogram(DatumId histogram_type,int64_t low_value,int64_t high_value,const std::vector<uint32_t> & buckets)68 void ReportHistogram([[maybe_unused]] DatumId histogram_type,
69 [[maybe_unused]] int64_t low_value,
70 [[maybe_unused]] int64_t high_value,
71 [[maybe_unused]] const std::vector<uint32_t>& buckets) override {
72 // TODO: nothing yet. We should implement and test histograms as well.
73 }
74
EndReport()75 void EndReport() override {
76 reports_.push_back(*current_report_);
77 current_report_ = nullptr;
78 }
79
GetReports()80 const std::vector<Report>& GetReports() {
81 return reports_;
82 }
83
GetSessionData()84 const SessionData& GetSessionData() {
85 return session_data_;
86 }
87
88 private:
89 SessionData session_data_;
90 std::vector<Report> reports_;
91 std::unique_ptr<Report> current_report_;
92 };
93
94 // The actual metrics test class
95 class MetricsReporterTest : public CommonRuntimeTest {
96 protected:
SetUp()97 void SetUp() override {
98 // Do the normal setup.
99 CommonRuntimeTest::SetUp();
100
101 // We need to start the runtime in order to run threads.
102 Thread::Current()->TransitionFromSuspendedToRunnable();
103 bool started = runtime_->Start();
104 CHECK(started);
105 }
106
107 // Configures the metric reporting.
SetupReporter(const char * period_spec,uint32_t session_id=1,uint32_t reporting_mods=100)108 void SetupReporter(const char* period_spec,
109 uint32_t session_id = 1,
110 uint32_t reporting_mods = 100) {
111 ReportingConfig config;
112 if (period_spec != nullptr) {
113 std::string error;
114 config.reporting_mods = reporting_mods;
115 config.period_spec = ReportingPeriodSpec::Parse(period_spec, &error);
116 ASSERT_TRUE(config.period_spec.has_value());
117 }
118
119 reporter_.reset(new MockMetricsReporter(std::move(config), Runtime::Current()));
120 backend_ = new TestBackend();
121 reporter_->backends_.emplace_back(backend_);
122
123 session_data_ = metrics::SessionData::CreateDefault();
124 session_data_.session_id = session_id;
125 }
126
TearDown()127 void TearDown() override {
128 reporter_->MaybeStopBackgroundThread();
129 reporter_ = nullptr;
130 backend_ = nullptr;
131 }
132
ShouldReportAtStartup()133 bool ShouldReportAtStartup() {
134 return reporter_->ShouldReportAtStartup();
135 }
136
ShouldContinueReporting()137 bool ShouldContinueReporting() {
138 return reporter_->ShouldContinueReporting();
139 }
140
GetNextPeriodSeconds()141 uint32_t GetNextPeriodSeconds() {
142 return reporter_->GetNextPeriodSeconds();
143 }
144
ReportMetrics()145 void ReportMetrics() {
146 reporter_->ReportMetrics();
147 }
148
NotifyStartupCompleted()149 void NotifyStartupCompleted() {
150 reporter_->NotifyStartupCompleted();
151 }
152
153 // Starts the reporting thread and adds some metrics if necessary.
MaybeStartBackgroundThread(bool add_metrics)154 bool MaybeStartBackgroundThread(bool add_metrics) {
155 // TODO: set session_data.compilation_reason and session_data.compiler_filter
156 bool result = reporter_->MaybeStartBackgroundThread(session_data_);
157 if (add_metrics) {
158 reporter_->art_metrics_->JitMethodCompileCount()->Add(1);
159 reporter_->art_metrics_->ClassVerificationCount()->Add(2);
160 }
161 return result;
162 }
163
164 // Right now we either:
165 // 1) don't add metrics (with_metrics = false)
166 // 2) or always add the same metrics (see MaybeStartBackgroundThread)
167 // So we can write a global verify method.
VerifyReports(uint32_t size,bool with_metrics,CompilerFilterReporting filter=CompilerFilterReporting::kUnknown,CompilationReason reason=CompilationReason::kUnknown)168 void VerifyReports(
169 uint32_t size,
170 bool with_metrics,
171 CompilerFilterReporting filter = CompilerFilterReporting::kUnknown,
172 CompilationReason reason = CompilationReason::kUnknown) {
173 // TODO: we should iterate through all the other metrics to make sure they were not
174 // reported. However, we don't have an easy to use iteration mechanism over metrics yet.
175 // We should add one
176 ASSERT_EQ(backend_->GetReports().size(), size);
177 for (const TestBackend::Report& report : backend_->GetReports()) {
178 ASSERT_EQ(report.data.Get(DatumId::kClassVerificationCount), with_metrics ? 2u : 0u);
179 ASSERT_EQ(report.data.Get(DatumId::kJitMethodCompileCount), with_metrics ? 1u : 0u);
180 }
181
182 ASSERT_EQ(backend_->GetSessionData().compiler_filter, filter);
183 ASSERT_EQ(backend_->GetSessionData().compilation_reason, reason);
184 }
185
186 // Sleeps until the backend received the give number of reports.
WaitForReport(uint32_t report_count,uint32_t sleep_period_ms)187 void WaitForReport(uint32_t report_count, uint32_t sleep_period_ms) {
188 while (true) {
189 if (backend_->GetReports().size() == report_count) {
190 return;
191 }
192 usleep(sleep_period_ms * 1000);
193 }
194 }
195
NotifyAppInfoUpdated(AppInfo * app_info)196 void NotifyAppInfoUpdated(AppInfo* app_info) {
197 reporter_->NotifyAppInfoUpdated(app_info);
198 }
199
200 private:
201 std::unique_ptr<MockMetricsReporter> reporter_;
202 TestBackend* backend_;
203 metrics::SessionData session_data_;
204 };
205
206 // Verifies startup reporting.
TEST_F(MetricsReporterTest,StartupOnly)207 TEST_F(MetricsReporterTest, StartupOnly) {
208 SetupReporter("S");
209
210 // Verify startup conditions
211 ASSERT_TRUE(ShouldReportAtStartup());
212 ASSERT_FALSE(ShouldContinueReporting());
213
214 // Start the thread and notify the startup. This will advance the state.
215 MaybeStartBackgroundThread(/*add_metrics=*/ true);
216
217 NotifyStartupCompleted();
218 WaitForReport(/*report_count=*/ 1, /*sleep_period_ms=*/ 50);
219 VerifyReports(/*size=*/ 1, /*with_metrics*/ true);
220
221 // We should still not report continuously.
222 ASSERT_FALSE(ShouldContinueReporting());
223 }
224
225 // LARGE TEST: This test takes 1s to run.
226 // Verifies startup reporting, followed by a fixed, one time only reporting.
TEST_F(MetricsReporterTest,StartupAndPeriod)227 TEST_F(MetricsReporterTest, StartupAndPeriod) {
228 SetupReporter("S,1");
229
230 // Verify startup conditions
231 ASSERT_TRUE(ShouldReportAtStartup());
232 ASSERT_FALSE(ShouldContinueReporting());
233
234 // Start the thread and notify the startup. This will advance the state.
235 MaybeStartBackgroundThread(/*add_metrics=*/ true);
236 NotifyStartupCompleted();
237
238 // We're waiting for 2 reports: the startup one, and the 1s one.
239 WaitForReport(/*report_count=*/ 2, /*sleep_period_ms=*/ 500);
240 VerifyReports(/*size=*/ 2, /*with_metrics*/ true);
241
242 // We should no longer report continuously.
243 ASSERT_FALSE(ShouldContinueReporting());
244 }
245
246 // LARGE TEST: This test take 2s to run.
247 // Verifies startup reporting, followed by continuous reporting.
TEST_F(MetricsReporterTest,StartupAndPeriodContinuous)248 TEST_F(MetricsReporterTest, StartupAndPeriodContinuous) {
249 SetupReporter("S,1,*");
250
251 // Verify startup conditions
252 ASSERT_TRUE(ShouldReportAtStartup());
253 ASSERT_FALSE(ShouldContinueReporting());
254
255 // Start the thread and notify the startup. This will advance the state.
256 MaybeStartBackgroundThread(/*add_metrics=*/ true);
257 NotifyStartupCompleted();
258
259 // We're waiting for 3 reports: the startup one, and the 1s one.
260 WaitForReport(/*report_count=*/ 3, /*sleep_period_ms=*/ 500);
261 VerifyReports(/*size=*/ 3, /*with_metrics*/ true);
262
263 // We should keep reporting continuously.
264 ASSERT_TRUE(ShouldContinueReporting());
265 }
266
267 // LARGE TEST: This test takes 1s to run.
268 // Verifies a fixed, one time only reporting.
TEST_F(MetricsReporterTest,OneTime)269 TEST_F(MetricsReporterTest, OneTime) {
270 SetupReporter("1");
271
272 // Verify startup conditions
273 ASSERT_FALSE(ShouldReportAtStartup());
274 ASSERT_TRUE(ShouldContinueReporting());
275
276 // Start the thread and notify the startup. This will advance the state.
277 MaybeStartBackgroundThread(/*add_metrics=*/ true);
278
279 // We're waiting for 1 report
280 WaitForReport(/*report_count=*/ 1, /*sleep_period_ms=*/ 500);
281 VerifyReports(/*size=*/ 1, /*with_metrics*/ true);
282
283 // We should no longer report continuously.
284 ASSERT_FALSE(ShouldContinueReporting());
285 }
286
287 // LARGE TEST: This test takes 5s to run.
288 // Verifies a sequence of reporting, at different interval of times.
TEST_F(MetricsReporterTest,PeriodContinuous)289 TEST_F(MetricsReporterTest, PeriodContinuous) {
290 SetupReporter("1,2,*");
291
292 // Verify startup conditions
293 ASSERT_FALSE(ShouldReportAtStartup());
294 ASSERT_TRUE(ShouldContinueReporting());
295
296 // Start the thread and notify the startup. This will advance the state.
297 MaybeStartBackgroundThread(/*add_metrics=*/ true);
298 NotifyStartupCompleted();
299
300 // We're waiting for 2 reports: the startup one, and the 1s one.
301 WaitForReport(/*report_count=*/ 3, /*sleep_period_ms=*/ 500);
302 VerifyReports(/*size=*/ 3, /*with_metrics*/ true);
303
304 // We should keep reporting continuously.
305 ASSERT_TRUE(ShouldContinueReporting());
306 }
307
308 // LARGE TEST: This test takes 1s to run.
309 // Verifies reporting when no metrics where recorded.
TEST_F(MetricsReporterTest,NoMetrics)310 TEST_F(MetricsReporterTest, NoMetrics) {
311 SetupReporter("1");
312
313 // Verify startup conditions
314 ASSERT_FALSE(ShouldReportAtStartup());
315 ASSERT_TRUE(ShouldContinueReporting());
316
317 // Start the thread and notify the startup. This will advance the state.
318 MaybeStartBackgroundThread(/*add_metrics=*/ false);
319
320 // We're waiting for 1 report
321 WaitForReport(/*report_count=*/ 1, /*sleep_period_ms=*/ 500);
322 VerifyReports(/*size=*/ 1, /*with_metrics*/ false);
323
324 // We should no longer report continuously.
325 ASSERT_FALSE(ShouldContinueReporting());
326 }
327
328 // Verify we don't start reporting if the sample rate is set to 0.
TEST_F(MetricsReporterTest,SampleRateDisable)329 TEST_F(MetricsReporterTest, SampleRateDisable) {
330 SetupReporter("1,*", /*session_id=*/ 1, /*reporting_mods=*/ 0);
331
332 // The background thread should not start.
333 ASSERT_FALSE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
334
335 ASSERT_FALSE(ShouldReportAtStartup());
336 ASSERT_FALSE(ShouldContinueReporting());
337 }
338
339 // Verify we don't start reporting if the sample rate is low and the session does
340 // not meet conditions.
TEST_F(MetricsReporterTest,SampleRateDisable24)341 TEST_F(MetricsReporterTest, SampleRateDisable24) {
342 SetupReporter("1,*", /*session_id=*/ 125, /*reporting_mods=*/ 24);
343
344 // The background thread should not start.
345 ASSERT_FALSE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
346
347 ASSERT_FALSE(ShouldReportAtStartup());
348 ASSERT_FALSE(ShouldContinueReporting());
349 }
350
351 // Verify we start reporting if the sample rate and the session meet
352 // reporting conditions
TEST_F(MetricsReporterTest,SampleRateEnable50)353 TEST_F(MetricsReporterTest, SampleRateEnable50) {
354 SetupReporter("1,*", /*session_id=*/ 125, /*reporting_mods=*/ 50);
355
356 // The background thread should start.
357 ASSERT_TRUE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
358
359 ASSERT_FALSE(ShouldReportAtStartup());
360 ASSERT_TRUE(ShouldContinueReporting());
361 }
362
363 // Verify we start reporting if the sample rate and the session meet
364 // reporting conditions
TEST_F(MetricsReporterTest,SampleRateEnableAll)365 TEST_F(MetricsReporterTest, SampleRateEnableAll) {
366 SetupReporter("1,*", /*session_id=*/ 1099, /*reporting_mods=*/ 100);
367
368 // The background thread should start.
369 ASSERT_TRUE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
370
371 ASSERT_FALSE(ShouldReportAtStartup());
372 ASSERT_TRUE(ShouldContinueReporting());
373 }
374
TEST_F(MetricsReporterTest,CompilerFilter)375 TEST_F(MetricsReporterTest, CompilerFilter) {
376 SetupReporter("1", /*session_id=*/ 1099, /*reporting_mods=*/ 100);
377 ASSERT_TRUE(MaybeStartBackgroundThread(/*add_metrics=*/ true));
378
379 AppInfo app_info;
380 app_info.RegisterOdexStatus(
381 "code_location",
382 "verify",
383 "install",
384 "odex_status");
385 app_info.RegisterAppInfo(
386 "package_name",
387 std::vector<std::string>({"code_location"}),
388 "",
389 "",
390 AppInfo::CodeType::kPrimaryApk);
391 NotifyAppInfoUpdated(&app_info);
392
393 WaitForReport(/*report_count=*/ 1, /*sleep_period_ms=*/ 500);
394 VerifyReports(
395 /*size=*/ 1,
396 /*with_metrics*/ true,
397 CompilerFilterReporting::kVerify,
398 CompilationReason::kInstall);
399 }
400
401 // Test class for period spec parsing
402 class ReportingPeriodSpecTest : public testing::Test {
403 public:
VerifyFalse(const std::string & spec_str)404 void VerifyFalse(const std::string& spec_str) {
405 Verify(spec_str, false, false, false, {});
406 }
407
VerifyTrue(const std::string & spec_str,bool startup_first,bool continuous,const std::vector<uint32_t> & periods)408 void VerifyTrue(
409 const std::string& spec_str,
410 bool startup_first,
411 bool continuous,
412 const std::vector<uint32_t>& periods) {
413 Verify(spec_str, true, startup_first, continuous, periods);
414 }
415
Verify(const std::string & spec_str,bool valid,bool startup_first,bool continuous,const std::vector<uint32_t> & periods)416 void Verify(
417 const std::string& spec_str,
418 bool valid,
419 bool startup_first,
420 bool continuous,
421 const std::vector<uint32_t>& periods) {
422 std::string error_msg;
423 std::optional<ReportingPeriodSpec> spec = ReportingPeriodSpec::Parse(spec_str, &error_msg);
424
425 ASSERT_EQ(valid, spec.has_value()) << spec_str;
426 if (valid) {
427 ASSERT_EQ(spec->spec, spec_str) << spec_str;
428 ASSERT_EQ(spec->report_startup_first, startup_first) << spec_str;
429 ASSERT_EQ(spec->continuous_reporting, continuous) << spec_str;
430 ASSERT_EQ(spec->periods_seconds, periods) << spec_str;
431 }
432 }
433 };
434
TEST_F(ReportingPeriodSpecTest,ParseTestsInvalid)435 TEST_F(ReportingPeriodSpecTest, ParseTestsInvalid) {
436 VerifyFalse("");
437 VerifyFalse("*");
438 VerifyFalse("S,*");
439 VerifyFalse("foo");
440 VerifyFalse("-1");
441 VerifyFalse("1,S");
442 VerifyFalse("*,1");
443 VerifyFalse("1,2,3,-1,3");
444 VerifyFalse("1,*,2");
445 VerifyFalse("1,S,2");
446 }
447
TEST_F(ReportingPeriodSpecTest,ParseTestsValid)448 TEST_F(ReportingPeriodSpecTest, ParseTestsValid) {
449 VerifyTrue("S", true, false, {});
450 VerifyTrue("S,1", true, false, {1});
451 VerifyTrue("S,1,2,3,4", true, false, {1, 2, 3, 4});
452 VerifyTrue("S,1,*", true, true, {1});
453 VerifyTrue("S,1,2,3,4,*", true, true, {1, 2, 3, 4});
454
455 VerifyTrue("1", false, false, {1});
456 VerifyTrue("1,2,3,4", false, false, {1, 2, 3, 4});
457 VerifyTrue("1,*", false, true, {1});
458 VerifyTrue("1,2,3,4,*", false, true, {1, 2, 3, 4});
459 }
460
461 } // namespace metrics
462 } // namespace art
463
464 #pragma clang diagnostic pop // -Wconversion
465