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