• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <android-base/parseint.h>
20 
21 #include <algorithm>
22 
23 #include "base/flags.h"
24 #include "base/stl_util.h"
25 #include "oat_file_manager.h"
26 #include "runtime.h"
27 #include "runtime_options.h"
28 #include "statsd.h"
29 #include "thread-current-inl.h"
30 
31 #pragma clang diagnostic push
32 #pragma clang diagnostic error "-Wconversion"
33 
34 namespace art {
35 namespace metrics {
36 
Create(const ReportingConfig & config,Runtime * runtime)37 std::unique_ptr<MetricsReporter> MetricsReporter::Create(
38     const ReportingConfig& config, Runtime* runtime) {
39   // We can't use std::make_unique here because the MetricsReporter constructor is private.
40   return std::unique_ptr<MetricsReporter>{new MetricsReporter{std::move(config), runtime}};
41 }
42 
MetricsReporter(const ReportingConfig & config,Runtime * runtime)43 MetricsReporter::MetricsReporter(const ReportingConfig& config, Runtime* runtime)
44     : config_{config},
45       runtime_{runtime},
46       startup_reported_{false},
47       report_interval_index_{0} {}
48 
~MetricsReporter()49 MetricsReporter::~MetricsReporter() { MaybeStopBackgroundThread(); }
50 
ReloadConfig(const ReportingConfig & config)51 void MetricsReporter::ReloadConfig(const ReportingConfig& config) {
52   DCHECK(!thread_.has_value()) << "The config cannot be reloaded after the background "
53                                   "reporting thread is started.";
54   config_ = config;
55 }
56 
IsMetricsReportingEnabled(const SessionData & session_data) const57 bool MetricsReporter::IsMetricsReportingEnabled(const SessionData& session_data) const {
58   return session_data.session_id % config_.reporting_num_mods < config_.reporting_mods;
59 }
60 
MaybeStartBackgroundThread(SessionData session_data)61 bool MetricsReporter::MaybeStartBackgroundThread(SessionData session_data) {
62   CHECK(!thread_.has_value());
63 
64   session_data_ = session_data;
65   LOG_STREAM(DEBUG) << "Received session metadata: " << session_data_.session_id;
66 
67   if (!IsMetricsReportingEnabled(session_data_)) {
68     return false;
69   }
70 
71   thread_.emplace(&MetricsReporter::BackgroundThreadRun, this);
72   return true;
73 }
74 
MaybeStopBackgroundThread()75 void MetricsReporter::MaybeStopBackgroundThread() {
76   if (thread_.has_value()) {
77     messages_.SendMessage(ShutdownRequestedMessage{});
78     thread_->join();
79     thread_.reset();
80   }
81 }
82 
NotifyStartupCompleted()83 void MetricsReporter::NotifyStartupCompleted() {
84   if (ShouldReportAtStartup() && thread_.has_value()) {
85     messages_.SendMessage(StartupCompletedMessage{});
86   }
87 }
88 
NotifyAppInfoUpdated(AppInfo * app_info)89 void MetricsReporter::NotifyAppInfoUpdated(AppInfo* app_info) {
90   std::string compilation_reason;
91   std::string compiler_filter;
92 
93   app_info->GetPrimaryApkOptimizationStatus(
94       &compiler_filter, &compilation_reason);
95 
96   SetCompilationInfo(
97       CompilationReasonFromName(compilation_reason),
98       CompilerFilterReportingFromName(compiler_filter));
99 }
100 
RequestMetricsReport(bool synchronous)101 void MetricsReporter::RequestMetricsReport(bool synchronous) {
102   if (thread_.has_value()) {
103     messages_.SendMessage(RequestMetricsReportMessage{synchronous});
104     if (synchronous) {
105       thread_to_host_messages_.ReceiveMessage();
106     }
107   }
108 }
109 
SetCompilationInfo(CompilationReason compilation_reason,CompilerFilterReporting compiler_filter)110 void MetricsReporter::SetCompilationInfo(CompilationReason compilation_reason,
111                                          CompilerFilterReporting compiler_filter) {
112   if (thread_.has_value()) {
113     messages_.SendMessage(CompilationInfoMessage{compilation_reason, compiler_filter});
114   }
115 }
116 
BackgroundThreadRun()117 void MetricsReporter::BackgroundThreadRun() {
118   LOG_STREAM(DEBUG) << "Metrics reporting thread started";
119 
120   // AttachCurrentThread is needed so we can safely use the ART concurrency primitives within the
121   // messages_ MessageQueue.
122   const bool attached = runtime_->AttachCurrentThread(kBackgroundThreadName,
123                                                       /*as_daemon=*/true,
124                                                       runtime_->GetSystemThreadGroup(),
125                                                       /*create_peer=*/true);
126   bool running = true;
127 
128   // Configure the backends
129   if (config_.dump_to_logcat) {
130     backends_.emplace_back(new LogBackend(std::make_unique<TextFormatter>(), LogSeverity::INFO));
131   }
132   if (config_.dump_to_file.has_value()) {
133     std::unique_ptr<MetricsFormatter> formatter;
134     if (config_.metrics_format == "xml") {
135       formatter = std::make_unique<XmlFormatter>();
136     } else {
137       formatter = std::make_unique<TextFormatter>();
138     }
139 
140     backends_.emplace_back(new FileBackend(std::move(formatter), config_.dump_to_file.value()));
141   }
142   if (config_.dump_to_statsd) {
143     auto backend = CreateStatsdBackend();
144     if (backend != nullptr) {
145       backends_.emplace_back(std::move(backend));
146     }
147   }
148 
149   MaybeResetTimeout();
150 
151   while (running) {
152     messages_.SwitchReceive(
153         [&]([[maybe_unused]] ShutdownRequestedMessage message) {
154           LOG_STREAM(DEBUG) << "Shutdown request received " << session_data_.session_id;
155           running = false;
156 
157           ReportMetrics();
158         },
159         [&](RequestMetricsReportMessage message) {
160           LOG_STREAM(DEBUG) << "Explicit report request received " << session_data_.session_id;
161           ReportMetrics();
162           if (message.synchronous) {
163             thread_to_host_messages_.SendMessage(ReportCompletedMessage{});
164           }
165         },
166         [&]([[maybe_unused]] TimeoutExpiredMessage message) {
167           LOG_STREAM(DEBUG) << "Timer expired, reporting metrics " << session_data_.session_id;
168 
169           ReportMetrics();
170           MaybeResetTimeout();
171         },
172         [&]([[maybe_unused]] StartupCompletedMessage message) {
173           LOG_STREAM(DEBUG) << "App startup completed, reporting metrics "
174               << session_data_.session_id;
175           ReportMetrics();
176           startup_reported_ = true;
177           MaybeResetTimeout();
178         },
179         [&](CompilationInfoMessage message) {
180           LOG_STREAM(DEBUG) << "Compilation info received " << session_data_.session_id;
181           session_data_.compilation_reason = message.compilation_reason;
182           session_data_.compiler_filter = message.compiler_filter;
183 
184           UpdateSessionInBackends();
185         });
186   }
187 
188   if (attached) {
189     runtime_->DetachCurrentThread();
190   }
191   LOG_STREAM(DEBUG) << "Metrics reporting thread terminating " << session_data_.session_id;
192 }
193 
MaybeResetTimeout()194 void MetricsReporter::MaybeResetTimeout() {
195   if (ShouldContinueReporting()) {
196     messages_.SetTimeout(SecondsToMs(GetNextPeriodSeconds()));
197   }
198 }
199 
GetMetrics()200 ArtMetrics* MetricsReporter::GetMetrics() { return runtime_->GetMetrics(); }
201 
ReportMetrics()202 void MetricsReporter::ReportMetrics() {
203   ArtMetrics* metrics = GetMetrics();
204 
205   if (!session_started_) {
206     for (auto& backend : backends_) {
207       backend->BeginOrUpdateSession(session_data_);
208     }
209     session_started_ = true;
210   }
211 
212   metrics->ReportAllMetricsAndResetValueMetrics(MakeNonOwningPointerVector(backends_));
213 }
214 
UpdateSessionInBackends()215 void MetricsReporter::UpdateSessionInBackends() {
216   if (session_started_) {
217     for (auto& backend : backends_) {
218       backend->BeginOrUpdateSession(session_data_);
219     }
220   }
221 }
222 
ShouldReportAtStartup() const223 bool MetricsReporter::ShouldReportAtStartup() const {
224   return IsMetricsReportingEnabled(session_data_) &&
225       config_.period_spec.has_value() &&
226       config_.period_spec->report_startup_first;
227 }
228 
ShouldContinueReporting() const229 bool MetricsReporter::ShouldContinueReporting() const {
230   bool result =
231       // Only if the reporting is enabled
232       IsMetricsReportingEnabled(session_data_) &&
233       // and if we have period spec
234       config_.period_spec.has_value() &&
235       // and the periods are non empty
236       !config_.period_spec->periods_seconds.empty() &&
237       // and we already reported startup or not required to report startup
238       (startup_reported_ || !config_.period_spec->report_startup_first) &&
239       // and we still have unreported intervals or we are asked to report continuously.
240       (config_.period_spec->continuous_reporting ||
241               (report_interval_index_ < config_.period_spec->periods_seconds.size()));
242   return result;
243 }
244 
GetNextPeriodSeconds()245 uint32_t MetricsReporter::GetNextPeriodSeconds() {
246   DCHECK(ShouldContinueReporting());
247 
248   // The index is either the current report_interval_index or the last index
249   // if we are in continuous mode and reached the end.
250   uint32_t index = std::min(
251       report_interval_index_,
252       static_cast<uint32_t>(config_.period_spec->periods_seconds.size() - 1));
253 
254   uint32_t result = config_.period_spec->periods_seconds[index];
255 
256   // Advance the index if we didn't get to the end.
257   if (report_interval_index_ < config_.period_spec->periods_seconds.size()) {
258     report_interval_index_++;
259   }
260   return result;
261 }
262 
FromFlags(bool is_system_server)263 ReportingConfig ReportingConfig::FromFlags(bool is_system_server) {
264   std::optional<std::string> spec_str = is_system_server
265       ? gFlags.MetricsReportingSpecSystemServer.GetValueOptional()
266       : gFlags.MetricsReportingSpec.GetValueOptional();
267 
268   std::optional<ReportingPeriodSpec> period_spec = std::nullopt;
269 
270   if (spec_str.has_value()) {
271     std::string error;
272     period_spec = ReportingPeriodSpec::Parse(spec_str.value(), &error);
273     if (!period_spec.has_value()) {
274       LOG(ERROR) << "Failed to create metrics reporting spec from: " << spec_str.value()
275           << " with error: " << error;
276     }
277   }
278 
279   uint32_t reporting_num_mods = is_system_server
280       ? gFlags.MetricsReportingNumModsServer()
281       : gFlags.MetricsReportingNumMods();
282   uint32_t reporting_mods = is_system_server
283       ? gFlags.MetricsReportingModsServer()
284       : gFlags.MetricsReportingMods();
285 
286   if (reporting_mods > reporting_num_mods || reporting_num_mods == 0) {
287     LOG(ERROR) << "Invalid metrics reporting mods: " << reporting_mods
288         << " num modes=" << reporting_num_mods
289         << ". The reporting is disabled";
290     reporting_mods = 0;
291     reporting_num_mods = 100;
292   }
293 
294   return {
295       .dump_to_logcat = gFlags.MetricsWriteToLogcat(),
296       .dump_to_file = gFlags.MetricsWriteToFile.GetValueOptional(),
297       .dump_to_statsd = gFlags.MetricsWriteToStatsd(),
298       .metrics_format = gFlags.MetricsFormat(),
299       .period_spec = period_spec,
300       .reporting_num_mods = reporting_num_mods,
301       .reporting_mods = reporting_mods,
302   };
303 }
304 
Parse(const std::string & spec_str,std::string * error_msg)305 std::optional<ReportingPeriodSpec> ReportingPeriodSpec::Parse(
306     const std::string& spec_str, std::string* error_msg) {
307   *error_msg = "";
308   if (spec_str.empty()) {
309     *error_msg = "Invalid empty spec.";
310     return std::nullopt;
311   }
312 
313   // Split the string. Each element is separated by comma.
314   std::vector<std::string> elems;
315   Split(spec_str, ',', &elems);
316 
317   // Check the startup marker (front) and the continuous one (back).
318   std::optional<ReportingPeriodSpec> spec = std::make_optional(ReportingPeriodSpec());
319   spec->spec = spec_str;
320   spec->report_startup_first = elems.front() == "S";
321   spec->continuous_reporting = elems.back() == "*";
322 
323   // Compute the indices for the period values.
324   size_t start_interval_idx = spec->report_startup_first ? 1 : 0;
325   size_t end_interval_idx = spec->continuous_reporting ? (elems.size() - 1) : elems.size();
326 
327   // '*' needs a numeric interval before in order to be valid.
328   if (spec->continuous_reporting &&
329       end_interval_idx == start_interval_idx) {
330     *error_msg = "Invalid period value in spec: " + spec_str;
331     return std::nullopt;
332   }
333 
334   // Parse the periods.
335   for (size_t i = start_interval_idx; i < end_interval_idx; i++) {
336     uint32_t period;
337     if (!android::base::ParseUint(elems[i], &period)) {
338         *error_msg = "Invalid period value in spec: " + spec_str;
339         return std::nullopt;
340     }
341     spec->periods_seconds.push_back(period);
342   }
343 
344   return spec;
345 }
346 
347 }  // namespace metrics
348 }  // namespace art
349 
350 #pragma clang diagnostic pop  // -Wconversion
351