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