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