xref: /aosp_15_r20/art/runtime/metrics/reporter_test.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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