blob: 70a03c668b6a233cf0e64199a9d43a054886813c [file] [log] [blame]
Eric Holk480d9812021-01-27 23:41:45 +00001/*
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 "metrics_reporter.h"
18
19#include "android-base/file.h"
20#include "base/scoped_flock.h"
21#include "runtime.h"
22#include "runtime_options.h"
23#include "thread-current-inl.h"
24
25#pragma clang diagnostic push
26#pragma clang diagnostic error "-Wconversion"
27
28namespace art {
29namespace metrics {
30
31using android::base::WriteStringToFd;
32
33std::unique_ptr<MetricsReporter> MetricsReporter::Create(ReportingConfig config, Runtime* runtime) {
34 // We can't use std::make_unique here because the MetricsReporter constructor is private.
35 return std::unique_ptr<MetricsReporter>{new MetricsReporter{std::move(config), runtime}};
36}
37
38MetricsReporter::MetricsReporter(ReportingConfig config, Runtime* runtime)
39 : config_{std::move(config)}, runtime_{runtime} {}
40
41MetricsReporter::~MetricsReporter() { MaybeStopBackgroundThread(); }
42
43void MetricsReporter::MaybeStartBackgroundThread() {
44 if (config_.BackgroundReportingEnabled()) {
45 CHECK(!thread_.has_value());
46
47 thread_.emplace(&MetricsReporter::BackgroundThreadRun, this);
48 }
49}
50
51void MetricsReporter::MaybeStopBackgroundThread() {
52 if (thread_.has_value()) {
53 messages_.SendMessage(ShutdownRequestedMessage{});
54 thread_->join();
55 }
56 // Do one final metrics report, if enabled.
57 if (config_.report_metrics_on_shutdown) {
58 ReportMetrics();
59 }
60}
61
62void MetricsReporter::BackgroundThreadRun() {
63 LOG_STREAM(DEBUG) << "Metrics reporting thread started";
64
65 // AttachCurrentThread is needed so we can safely use the ART concurrency primitives within the
66 // messages_ MessageQueue.
67 runtime_->AttachCurrentThread(kBackgroundThreadName,
68 /*as_daemon=*/true,
69 runtime_->GetSystemThreadGroup(),
70 /*create_peer=*/true);
71 bool running = true;
72
73 MaybeResetTimeout();
74
75 while (running) {
76 messages_.SwitchReceive(
77 [&]([[maybe_unused]] ShutdownRequestedMessage message) {
78 LOG_STREAM(DEBUG) << "Shutdown request received";
79 running = false;
80 },
81 [&]([[maybe_unused]] TimeoutExpiredMessage message) {
82 LOG_STREAM(DEBUG) << "Timer expired, reporting metrics";
83
84 ReportMetrics();
85
86 MaybeResetTimeout();
87 });
88 }
89
90 runtime_->DetachCurrentThread();
91 LOG_STREAM(DEBUG) << "Metrics reporting thread terminating";
92}
93
94void MetricsReporter::MaybeResetTimeout() {
95 if (config_.periodic_report_seconds.has_value()) {
96 messages_.SetTimeout(SecondsToMs(config_.periodic_report_seconds.value()));
97 }
98}
99
100void MetricsReporter::ReportMetrics() const {
101 if (config_.dump_to_logcat) {
102 LOG_STREAM(INFO) << "\n*** ART internal metrics ***\n\n";
103 // LOG_STREAM(INFO) destroys the stream at the end of the statement, which makes it tricky pass
104 // it to store as a field in StreamBackend. To get around this, we use an immediately-invoked
105 // lambda expression to act as a let-binding, letting us access the stream for long enough to
106 // dump the metrics.
107 [this](std::ostream& os) {
108 StreamBackend backend{os};
109 runtime_->GetMetrics()->ReportAllMetrics(&backend);
110 }(LOG_STREAM(INFO));
111 LOG_STREAM(INFO) << "\n*** Done dumping ART internal metrics ***\n";
112 }
113 if (config_.dump_to_file.has_value()) {
114 const auto& filename = config_.dump_to_file.value();
115 std::ostringstream stream;
116 StreamBackend backend{stream};
117 runtime_->GetMetrics()->ReportAllMetrics(&backend);
118
119 std::string error_message;
120 auto file{
121 LockedFile::Open(filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, true, &error_message)};
122 if (file.get() == nullptr) {
123 LOG(WARNING) << "Could open metrics file '" << filename << "': " << error_message;
124 } else {
125 if (!WriteStringToFd(stream.str(), file.get()->Fd())) {
126 PLOG(WARNING) << "Error writing metrics to file";
127 }
128 }
129 }
130}
131
132ReportingConfig ReportingConfig::FromRuntimeArguments(const RuntimeArgumentMap& args) {
133 using M = RuntimeArgumentMap;
134 return {
135 .dump_to_logcat = args.Exists(M::WriteMetricsToLog),
136 .dump_to_file = args.GetOptional(M::WriteMetricsToFile),
137 .report_metrics_on_shutdown = !args.Exists(M::DisableFinalMetricsReport),
138 .periodic_report_seconds = args.GetOptional(M::MetricsReportingPeriod),
139 };
140}
141
142} // namespace metrics
143} // namespace art
144
145#pragma clang diagnostic pop // -Wconversion