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_ = nullptr;
129 backend_ = nullptr;
130 }
131
ShouldReportAtStartup()132 bool ShouldReportAtStartup() {
133 return reporter_->ShouldReportAtStartup();
134 }
135
ShouldContinueReporting()136 bool ShouldContinueReporting() {
137 return reporter_->ShouldContinueReporting();
138 }
139
GetNextPeriodSeconds()140 uint32_t GetNextPeriodSeconds() {
141 return reporter_->GetNextPeriodSeconds();
142 }
143
ReportMetrics()144 void ReportMetrics() {
145 reporter_->ReportMetrics();
146 }
147
NotifyStartupCompleted()148 void NotifyStartupCompleted() {
149 reporter_->NotifyStartupCompleted();
150 }
151
152 // Starts the reporting thread and adds some metrics if necessary.
MaybeStartBackgroundThread(bool add_metrics)153 bool MaybeStartBackgroundThread(bool add_metrics) {
154 // TODO: set session_data.compilation_reason and session_data.compiler_filter
155 bool result = reporter_->MaybeStartBackgroundThread(session_data_);
156 if (add_metrics) {
157 reporter_->art_metrics_->JitMethodCompileCount()->Add(1);
158 reporter_->art_metrics_->ClassVerificationCount()->Add(2);
159 }
160 return result;
161 }
162
163 // Right now we either:
164 // 1) don't add metrics (with_metrics = false)
165 // 2) or always add the same metrics (see MaybeStartBackgroundThread)
166 // So we can write a global verify method.
VerifyReports(uint32_t size,bool with_metrics,CompilerFilterReporting filter=CompilerFilterReporting::kUnknown,CompilationReason reason=CompilationReason::kUnknown)167 void VerifyReports(
168 uint32_t size,
169 bool with_metrics,
170 CompilerFilterReporting filter = CompilerFilterReporting::kUnknown,
171 CompilationReason reason = CompilationReason::kUnknown) {
172 // TODO: we should iterate through all the other metrics to make sure they were not
173 // reported. However, we don't have an easy to use iteration mechanism over metrics yet.
174 // We should add one
175 ASSERT_EQ(backend_->GetReports().size(), size);
176 for (const TestBackend::Report& report : backend_->GetReports()) {
177 ASSERT_EQ(report.data.Get(DatumId::kClassVerificationCount), with_metrics ? 2u : 0u);
178 ASSERT_EQ(report.data.Get(DatumId::kJitMethodCompileCount), with_metrics ? 1u : 0u);
179 }
180
181 ASSERT_EQ(backend_->GetSessionData().compiler_filter, filter);
182 ASSERT_EQ(backend_->GetSessionData().compilation_reason, reason);
183 }
184
185 // Sleeps until the backend received the give number of reports.
WaitForReport(uint32_t report_count,uint32_t sleep_period_ms)186 void WaitForReport(uint32_t report_count, uint32_t sleep_period_ms) {
187 while (true) {
188 if (backend_->GetReports().size() == report_count) {
189 return;
190 }
191 usleep(sleep_period_ms * 1000);
192 }
193 }
194
NotifyAppInfoUpdated(AppInfo * app_info)195 void NotifyAppInfoUpdated(AppInfo* app_info) {
196 reporter_->NotifyAppInfoUpdated(app_info);
197 }
198
199 private:
200 std::unique_ptr<MockMetricsReporter> reporter_;
201 TestBackend* backend_;
202 metrics::SessionData session_data_;
203 };
204
205 // Verifies startup reporting.
TEST_F(MetricsReporterTest,StartupOnly)206 TEST_F(MetricsReporterTest, StartupOnly) {
207 SetupReporter("S");
208
209 // Verify startup conditions
210 ASSERT_TRUE(ShouldReportAtStartup());
211 ASSERT_FALSE(ShouldContinueReporting());
212
213 // Start the thread and notify the startup. This will advance the state.
214 MaybeStartBackgroundThread(/*add_metrics=*/ true);
215
216 NotifyStartupCompleted();
217 WaitForReport(/*report_count=*/ 1, /*sleep_period_ms=*/ 50);
218 VerifyReports(/*size=*/ 1, /*with_metrics*/ true);
219
220 // We should still not report continuously.
221 ASSERT_FALSE(ShouldContinueReporting());
222 }
223
224 // LARGE TEST: This test takes 1s to run.
225 // Verifies startup reporting, followed by a fixed, one time only reporting.
TEST_F(MetricsReporterTest,StartupAndPeriod)226 TEST_F(MetricsReporterTest, StartupAndPeriod) {
227 SetupReporter("S,1");
228
229 // Verify startup conditions
230 ASSERT_TRUE(ShouldReportAtStartup());
231 ASSERT_FALSE(ShouldContinueReporting());
232
233 // Start the thread and notify the startup. This will advance the state.
234 MaybeStartBackgroundThread(/*add_metrics=*/ true);
235 NotifyStartupCompleted();
236
237 // We're waiting for 2 reports: the startup one, and the 1s one.
238 WaitForReport(/*report_count=*/ 2, /*sleep_period_ms=*/ 500);
239 VerifyReports(/*size=*/ 2, /*with_metrics*/ true);
240
241 // We should no longer report continuously.
242 ASSERT_FALSE(ShouldContinueReporting());
243 }
244
245 // LARGE TEST: This test take 2s to run.
246 // Verifies startup reporting, followed by continuous reporting.
TEST_F(MetricsReporterTest,StartupAndPeriodContinuous)247 TEST_F(MetricsReporterTest, StartupAndPeriodContinuous) {
248 SetupReporter("S,1,*");
249
250 // Verify startup conditions
251 ASSERT_TRUE(ShouldReportAtStartup());
252 ASSERT_FALSE(ShouldContinueReporting());
253
254 // Start the thread and notify the startup. This will advance the state.
255 MaybeStartBackgroundThread(/*add_metrics=*/ true);
256 NotifyStartupCompleted();
257
258 // We're waiting for 3 reports: the startup one, and the 1s one.
259 WaitForReport(/*report_count=*/ 3, /*sleep_period_ms=*/ 500);
260 VerifyReports(/*size=*/ 3, /*with_metrics*/ true);
261
262 // We should keep reporting continuously.
263 ASSERT_TRUE(ShouldContinueReporting());
264 }
265
266 // LARGE TEST: This test takes 1s to run.
267 // Verifies a fixed, one time only reporting.
TEST_F(MetricsReporterTest,OneTime)268 TEST_F(MetricsReporterTest, OneTime) {
269 SetupReporter("1");
270
271 // Verify startup conditions
272 ASSERT_FALSE(ShouldReportAtStartup());
273 ASSERT_TRUE(ShouldContinueReporting());
274
275 // Start the thread and notify the startup. This will advance the state.
276 MaybeStartBackgroundThread(/*add_metrics=*/ true);
277
278 // We're waiting for 1 report
279 WaitForReport(/*report_count=*/ 1, /*sleep_period_ms=*/ 500);
280 VerifyReports(/*size=*/ 1, /*with_metrics*/ true);
281
282 // We should no longer report continuously.
283 ASSERT_FALSE(ShouldContinueReporting());
284 }
285
286 // LARGE TEST: This test takes 5s to run.
287 // Verifies a sequence of reporting, at different interval of times.
TEST_F(MetricsReporterTest,PeriodContinuous)288 TEST_F(MetricsReporterTest, PeriodContinuous) {
289 SetupReporter("1,2,*");
290
291 // Verify startup conditions
292 ASSERT_FALSE(ShouldReportAtStartup());
293 ASSERT_TRUE(ShouldContinueReporting());
294
295 // Start the thread and notify the startup. This will advance the state.
296 MaybeStartBackgroundThread(/*add_metrics=*/ true);
297 NotifyStartupCompleted();
298
299 // We're waiting for 2 reports: the startup one, and the 1s one.
300 WaitForReport(/*report_count=*/ 3, /*sleep_period_ms=*/ 500);
301 VerifyReports(/*size=*/ 3, /*with_metrics*/ true);
302
303 // We should keep reporting continuously.
304 ASSERT_TRUE(ShouldContinueReporting());
305 }
306
307 // LARGE TEST: This test takes 1s to run.
308 // Verifies reporting when no metrics where recorded.
TEST_F(MetricsReporterTest,NoMetrics)309 TEST_F(MetricsReporterTest, NoMetrics) {
310 SetupReporter("1");
311
312 // Verify startup conditions
313 ASSERT_FALSE(ShouldReportAtStartup());
314 ASSERT_TRUE(ShouldContinueReporting());
315
316 // Start the thread and notify the startup. This will advance the state.
317 MaybeStartBackgroundThread(/*add_metrics=*/ false);
318
319 // We're waiting for 1 report
320 WaitForReport(/*report_count=*/ 1, /*sleep_period_ms=*/ 500);
321 VerifyReports(/*size=*/ 1, /*with_metrics*/ false);
322
323 // We should no longer report continuously.
324 ASSERT_FALSE(ShouldContinueReporting());
325 }
326
327 // Verify we don't start reporting if the sample rate is set to 0.
TEST_F(MetricsReporterTest,SampleRateDisable)328 TEST_F(MetricsReporterTest, SampleRateDisable) {
329 SetupReporter("1,*", /*session_id=*/ 1, /*reporting_mods=*/ 0);
330
331 // The background thread should not start.
332 ASSERT_FALSE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
333
334 ASSERT_FALSE(ShouldReportAtStartup());
335 ASSERT_FALSE(ShouldContinueReporting());
336 }
337
338 // Verify we don't start reporting if the sample rate is low and the session does
339 // not meet conditions.
TEST_F(MetricsReporterTest,SampleRateDisable24)340 TEST_F(MetricsReporterTest, SampleRateDisable24) {
341 SetupReporter("1,*", /*session_id=*/ 125, /*reporting_mods=*/ 24);
342
343 // The background thread should not start.
344 ASSERT_FALSE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
345
346 ASSERT_FALSE(ShouldReportAtStartup());
347 ASSERT_FALSE(ShouldContinueReporting());
348 }
349
350 // Verify we start reporting if the sample rate and the session meet
351 // reporting conditions
TEST_F(MetricsReporterTest,SampleRateEnable50)352 TEST_F(MetricsReporterTest, SampleRateEnable50) {
353 SetupReporter("1,*", /*session_id=*/ 125, /*reporting_mods=*/ 50);
354
355 // The background thread should start.
356 ASSERT_TRUE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
357
358 ASSERT_FALSE(ShouldReportAtStartup());
359 ASSERT_TRUE(ShouldContinueReporting());
360 }
361
362 // Verify we start reporting if the sample rate and the session meet
363 // reporting conditions
TEST_F(MetricsReporterTest,SampleRateEnableAll)364 TEST_F(MetricsReporterTest, SampleRateEnableAll) {
365 SetupReporter("1,*", /*session_id=*/ 1099, /*reporting_mods=*/ 100);
366
367 // The background thread should start.
368 ASSERT_TRUE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
369
370 ASSERT_FALSE(ShouldReportAtStartup());
371 ASSERT_TRUE(ShouldContinueReporting());
372 }
373
TEST_F(MetricsReporterTest,CompilerFilter)374 TEST_F(MetricsReporterTest, CompilerFilter) {
375 SetupReporter("1", /*session_id=*/ 1099, /*reporting_mods=*/ 100);
376 ASSERT_TRUE(MaybeStartBackgroundThread(/*add_metrics=*/ true));
377
378 AppInfo app_info;
379 app_info.RegisterOdexStatus(
380 "code_location",
381 "verify",
382 "install",
383 "odex_status");
384 app_info.RegisterAppInfo(
385 "package_name",
386 std::vector<std::string>({"code_location"}),
387 "",
388 "",
389 AppInfo::CodeType::kPrimaryApk);
390 NotifyAppInfoUpdated(&app_info);
391
392 WaitForReport(/*report_count=*/ 1, /*sleep_period_ms=*/ 500);
393 VerifyReports(
394 /*size=*/ 1,
395 /*with_metrics*/ true,
396 CompilerFilterReporting::kVerify,
397 CompilationReason::kInstall);
398 }
399
400 // Test class for period spec parsing
401 class ReportingPeriodSpecTest : public testing::Test {
402 public:
VerifyFalse(const std::string & spec_str)403 void VerifyFalse(const std::string& spec_str) {
404 Verify(spec_str, false, false, false, {});
405 }
406
VerifyTrue(const std::string & spec_str,bool startup_first,bool continuous,const std::vector<uint32_t> & periods)407 void VerifyTrue(
408 const std::string& spec_str,
409 bool startup_first,
410 bool continuous,
411 const std::vector<uint32_t>& periods) {
412 Verify(spec_str, true, startup_first, continuous, periods);
413 }
414
Verify(const std::string & spec_str,bool valid,bool startup_first,bool continuous,const std::vector<uint32_t> & periods)415 void Verify(
416 const std::string& spec_str,
417 bool valid,
418 bool startup_first,
419 bool continuous,
420 const std::vector<uint32_t>& periods) {
421 std::string error_msg;
422 std::optional<ReportingPeriodSpec> spec = ReportingPeriodSpec::Parse(spec_str, &error_msg);
423
424 ASSERT_EQ(valid, spec.has_value()) << spec_str;
425 if (valid) {
426 ASSERT_EQ(spec->spec, spec_str) << spec_str;
427 ASSERT_EQ(spec->report_startup_first, startup_first) << spec_str;
428 ASSERT_EQ(spec->continuous_reporting, continuous) << spec_str;
429 ASSERT_EQ(spec->periods_seconds, periods) << spec_str;
430 }
431 }
432 };
433
TEST_F(ReportingPeriodSpecTest,ParseTestsInvalid)434 TEST_F(ReportingPeriodSpecTest, ParseTestsInvalid) {
435 VerifyFalse("");
436 VerifyFalse("*");
437 VerifyFalse("S,*");
438 VerifyFalse("foo");
439 VerifyFalse("-1");
440 VerifyFalse("1,S");
441 VerifyFalse("*,1");
442 VerifyFalse("1,2,3,-1,3");
443 VerifyFalse("1,*,2");
444 VerifyFalse("1,S,2");
445 }
446
TEST_F(ReportingPeriodSpecTest,ParseTestsValid)447 TEST_F(ReportingPeriodSpecTest, ParseTestsValid) {
448 VerifyTrue("S", true, false, {});
449 VerifyTrue("S,1", true, false, {1});
450 VerifyTrue("S,1,2,3,4", true, false, {1, 2, 3, 4});
451 VerifyTrue("S,1,*", true, true, {1});
452 VerifyTrue("S,1,2,3,4,*", true, true, {1, 2, 3, 4});
453
454 VerifyTrue("1", false, false, {1});
455 VerifyTrue("1,2,3,4", false, false, {1, 2, 3, 4});
456 VerifyTrue("1,*", false, true, {1});
457 VerifyTrue("1,2,3,4,*", false, true, {1, 2, 3, 4});
458 }
459
460 } // namespace metrics
461 } // namespace art
462
463 #pragma clang diagnostic pop // -Wconversion
464