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