1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker * Copyright (C) 2021 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker *
4*795d594fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker *
8*795d594fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker *
10*795d594fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker */
16*795d594fSAndroid Build Coastguard Worker
17*795d594fSAndroid Build Coastguard Worker #include <odr_compilation_log.h>
18*795d594fSAndroid Build Coastguard Worker
19*795d594fSAndroid Build Coastguard Worker #include <errno.h>
20*795d594fSAndroid Build Coastguard Worker
21*795d594fSAndroid Build Coastguard Worker #include <fstream>
22*795d594fSAndroid Build Coastguard Worker #include <ios>
23*795d594fSAndroid Build Coastguard Worker #include <iosfwd>
24*795d594fSAndroid Build Coastguard Worker #include <istream>
25*795d594fSAndroid Build Coastguard Worker #include <ostream>
26*795d594fSAndroid Build Coastguard Worker #include <streambuf>
27*795d594fSAndroid Build Coastguard Worker #include <string>
28*795d594fSAndroid Build Coastguard Worker #include <vector>
29*795d594fSAndroid Build Coastguard Worker
30*795d594fSAndroid Build Coastguard Worker #include "android-base/logging.h"
31*795d594fSAndroid Build Coastguard Worker #include "base/os.h"
32*795d594fSAndroid Build Coastguard Worker
33*795d594fSAndroid Build Coastguard Worker #include "odrefresh/odrefresh.h"
34*795d594fSAndroid Build Coastguard Worker #include "odr_metrics.h"
35*795d594fSAndroid Build Coastguard Worker
36*795d594fSAndroid Build Coastguard Worker namespace art {
37*795d594fSAndroid Build Coastguard Worker namespace odrefresh {
38*795d594fSAndroid Build Coastguard Worker
operator >>(std::istream & is,OdrCompilationLogEntry & entry)39*795d594fSAndroid Build Coastguard Worker std::istream& operator>>(std::istream& is, OdrCompilationLogEntry& entry) {
40*795d594fSAndroid Build Coastguard Worker // Block I/O related exceptions
41*795d594fSAndroid Build Coastguard Worker auto saved_exceptions = is.exceptions();
42*795d594fSAndroid Build Coastguard Worker is.exceptions(std::ios_base::iostate {});
43*795d594fSAndroid Build Coastguard Worker
44*795d594fSAndroid Build Coastguard Worker // Write log entry. NB update OdrCompilationLog::kLogVersion if changing the format here.
45*795d594fSAndroid Build Coastguard Worker is >> entry.apex_version >> std::ws;
46*795d594fSAndroid Build Coastguard Worker is >> entry.last_update_millis >> std::ws;
47*795d594fSAndroid Build Coastguard Worker is >> entry.trigger >> std::ws;
48*795d594fSAndroid Build Coastguard Worker is >> entry.when >> std::ws;
49*795d594fSAndroid Build Coastguard Worker is >> entry.exit_code >> std::ws;
50*795d594fSAndroid Build Coastguard Worker
51*795d594fSAndroid Build Coastguard Worker // Restore I/O related exceptions
52*795d594fSAndroid Build Coastguard Worker is.exceptions(saved_exceptions);
53*795d594fSAndroid Build Coastguard Worker return is;
54*795d594fSAndroid Build Coastguard Worker }
55*795d594fSAndroid Build Coastguard Worker
operator <<(std::ostream & os,const OdrCompilationLogEntry & entry)56*795d594fSAndroid Build Coastguard Worker std::ostream& operator<<(std::ostream& os, const OdrCompilationLogEntry& entry) {
57*795d594fSAndroid Build Coastguard Worker static const char kSpace = ' ';
58*795d594fSAndroid Build Coastguard Worker
59*795d594fSAndroid Build Coastguard Worker // Block I/O related exceptions
60*795d594fSAndroid Build Coastguard Worker auto saved_exceptions = os.exceptions();
61*795d594fSAndroid Build Coastguard Worker os.exceptions(std::ios_base::iostate {});
62*795d594fSAndroid Build Coastguard Worker
63*795d594fSAndroid Build Coastguard Worker os << entry.apex_version << kSpace;
64*795d594fSAndroid Build Coastguard Worker os << entry.last_update_millis << kSpace;
65*795d594fSAndroid Build Coastguard Worker os << entry.trigger << kSpace;
66*795d594fSAndroid Build Coastguard Worker os << entry.when << kSpace;
67*795d594fSAndroid Build Coastguard Worker os << entry.exit_code << std::endl;
68*795d594fSAndroid Build Coastguard Worker
69*795d594fSAndroid Build Coastguard Worker // Restore I/O related exceptions
70*795d594fSAndroid Build Coastguard Worker os.exceptions(saved_exceptions);
71*795d594fSAndroid Build Coastguard Worker return os;
72*795d594fSAndroid Build Coastguard Worker }
73*795d594fSAndroid Build Coastguard Worker
operator ==(const OdrCompilationLogEntry & lhs,const OdrCompilationLogEntry & rhs)74*795d594fSAndroid Build Coastguard Worker bool operator==(const OdrCompilationLogEntry& lhs, const OdrCompilationLogEntry& rhs) {
75*795d594fSAndroid Build Coastguard Worker return lhs.apex_version == rhs.apex_version && lhs.last_update_millis == rhs.last_update_millis &&
76*795d594fSAndroid Build Coastguard Worker lhs.trigger == rhs.trigger && lhs.when == rhs.when && lhs.exit_code == rhs.exit_code;
77*795d594fSAndroid Build Coastguard Worker }
78*795d594fSAndroid Build Coastguard Worker
operator !=(const OdrCompilationLogEntry & lhs,const OdrCompilationLogEntry & rhs)79*795d594fSAndroid Build Coastguard Worker bool operator!=(const OdrCompilationLogEntry& lhs, const OdrCompilationLogEntry& rhs) {
80*795d594fSAndroid Build Coastguard Worker return !(lhs == rhs);
81*795d594fSAndroid Build Coastguard Worker }
82*795d594fSAndroid Build Coastguard Worker
OdrCompilationLog(const char * compilation_log_path)83*795d594fSAndroid Build Coastguard Worker OdrCompilationLog::OdrCompilationLog(const char* compilation_log_path)
84*795d594fSAndroid Build Coastguard Worker : log_path_(compilation_log_path) {
85*795d594fSAndroid Build Coastguard Worker if (log_path_ != nullptr && OS::FileExists(log_path_)) {
86*795d594fSAndroid Build Coastguard Worker if (!Read()) {
87*795d594fSAndroid Build Coastguard Worker PLOG(ERROR) << "Failed to read compilation log: " << log_path_;
88*795d594fSAndroid Build Coastguard Worker }
89*795d594fSAndroid Build Coastguard Worker }
90*795d594fSAndroid Build Coastguard Worker }
91*795d594fSAndroid Build Coastguard Worker
~OdrCompilationLog()92*795d594fSAndroid Build Coastguard Worker OdrCompilationLog::~OdrCompilationLog() {
93*795d594fSAndroid Build Coastguard Worker if (log_path_ != nullptr && !Write()) {
94*795d594fSAndroid Build Coastguard Worker PLOG(ERROR) << "Failed to write compilation log: " << log_path_;
95*795d594fSAndroid Build Coastguard Worker }
96*795d594fSAndroid Build Coastguard Worker }
97*795d594fSAndroid Build Coastguard Worker
Read()98*795d594fSAndroid Build Coastguard Worker bool OdrCompilationLog::Read() {
99*795d594fSAndroid Build Coastguard Worker std::ifstream ifs(log_path_);
100*795d594fSAndroid Build Coastguard Worker if (!ifs.good()) {
101*795d594fSAndroid Build Coastguard Worker return false;
102*795d594fSAndroid Build Coastguard Worker }
103*795d594fSAndroid Build Coastguard Worker
104*795d594fSAndroid Build Coastguard Worker std::string log_version;
105*795d594fSAndroid Build Coastguard Worker ifs >> log_version >> std::ws;
106*795d594fSAndroid Build Coastguard Worker if (log_version != kLogVersion) {
107*795d594fSAndroid Build Coastguard Worker return false;
108*795d594fSAndroid Build Coastguard Worker }
109*795d594fSAndroid Build Coastguard Worker
110*795d594fSAndroid Build Coastguard Worker while (!ifs.eof()) {
111*795d594fSAndroid Build Coastguard Worker OdrCompilationLogEntry entry;
112*795d594fSAndroid Build Coastguard Worker ifs >> entry;
113*795d594fSAndroid Build Coastguard Worker if (ifs.fail()) {
114*795d594fSAndroid Build Coastguard Worker entries_.clear();
115*795d594fSAndroid Build Coastguard Worker return false;
116*795d594fSAndroid Build Coastguard Worker }
117*795d594fSAndroid Build Coastguard Worker entries_.push_back(entry);
118*795d594fSAndroid Build Coastguard Worker }
119*795d594fSAndroid Build Coastguard Worker
120*795d594fSAndroid Build Coastguard Worker return true;
121*795d594fSAndroid Build Coastguard Worker }
122*795d594fSAndroid Build Coastguard Worker
Write() const123*795d594fSAndroid Build Coastguard Worker bool OdrCompilationLog::Write() const {
124*795d594fSAndroid Build Coastguard Worker std::ofstream ofs(log_path_, std::ofstream::trunc);
125*795d594fSAndroid Build Coastguard Worker if (!ofs.good()) {
126*795d594fSAndroid Build Coastguard Worker return false;
127*795d594fSAndroid Build Coastguard Worker }
128*795d594fSAndroid Build Coastguard Worker
129*795d594fSAndroid Build Coastguard Worker ofs << kLogVersion << std::endl;
130*795d594fSAndroid Build Coastguard Worker for (const auto& entry : entries_) {
131*795d594fSAndroid Build Coastguard Worker ofs << entry;
132*795d594fSAndroid Build Coastguard Worker if (ofs.fail()) {
133*795d594fSAndroid Build Coastguard Worker return false;
134*795d594fSAndroid Build Coastguard Worker }
135*795d594fSAndroid Build Coastguard Worker }
136*795d594fSAndroid Build Coastguard Worker
137*795d594fSAndroid Build Coastguard Worker return true;
138*795d594fSAndroid Build Coastguard Worker }
139*795d594fSAndroid Build Coastguard Worker
Truncate()140*795d594fSAndroid Build Coastguard Worker void OdrCompilationLog::Truncate() {
141*795d594fSAndroid Build Coastguard Worker if (entries_.size() < kMaxLoggedEntries) {
142*795d594fSAndroid Build Coastguard Worker return;
143*795d594fSAndroid Build Coastguard Worker }
144*795d594fSAndroid Build Coastguard Worker
145*795d594fSAndroid Build Coastguard Worker size_t excess = entries_.size() - kMaxLoggedEntries;
146*795d594fSAndroid Build Coastguard Worker entries_.erase(entries_.begin(), entries_.begin() + excess);
147*795d594fSAndroid Build Coastguard Worker }
148*795d594fSAndroid Build Coastguard Worker
NumberOfEntries() const149*795d594fSAndroid Build Coastguard Worker size_t OdrCompilationLog::NumberOfEntries() const {
150*795d594fSAndroid Build Coastguard Worker return entries_.size();
151*795d594fSAndroid Build Coastguard Worker }
152*795d594fSAndroid Build Coastguard Worker
Peek(size_t index) const153*795d594fSAndroid Build Coastguard Worker const OdrCompilationLogEntry* OdrCompilationLog::Peek(size_t index) const {
154*795d594fSAndroid Build Coastguard Worker if (index >= entries_.size()) {
155*795d594fSAndroid Build Coastguard Worker return nullptr;
156*795d594fSAndroid Build Coastguard Worker }
157*795d594fSAndroid Build Coastguard Worker return &entries_[index];
158*795d594fSAndroid Build Coastguard Worker }
159*795d594fSAndroid Build Coastguard Worker
Log(int64_t apex_version,int64_t last_update_millis,OdrMetrics::Trigger trigger,ExitCode compilation_result)160*795d594fSAndroid Build Coastguard Worker void OdrCompilationLog::Log(int64_t apex_version,
161*795d594fSAndroid Build Coastguard Worker int64_t last_update_millis,
162*795d594fSAndroid Build Coastguard Worker OdrMetrics::Trigger trigger,
163*795d594fSAndroid Build Coastguard Worker ExitCode compilation_result) {
164*795d594fSAndroid Build Coastguard Worker time_t now;
165*795d594fSAndroid Build Coastguard Worker time(&now);
166*795d594fSAndroid Build Coastguard Worker Log(apex_version, last_update_millis, trigger, now, compilation_result);
167*795d594fSAndroid Build Coastguard Worker }
168*795d594fSAndroid Build Coastguard Worker
Log(int64_t apex_version,int64_t last_update_millis,OdrMetrics::Trigger trigger,time_t when,ExitCode compilation_result)169*795d594fSAndroid Build Coastguard Worker void OdrCompilationLog::Log(int64_t apex_version,
170*795d594fSAndroid Build Coastguard Worker int64_t last_update_millis,
171*795d594fSAndroid Build Coastguard Worker OdrMetrics::Trigger trigger,
172*795d594fSAndroid Build Coastguard Worker time_t when,
173*795d594fSAndroid Build Coastguard Worker ExitCode compilation_result) {
174*795d594fSAndroid Build Coastguard Worker entries_.push_back(OdrCompilationLogEntry{apex_version,
175*795d594fSAndroid Build Coastguard Worker last_update_millis,
176*795d594fSAndroid Build Coastguard Worker static_cast<int32_t>(trigger),
177*795d594fSAndroid Build Coastguard Worker when,
178*795d594fSAndroid Build Coastguard Worker static_cast<int32_t>(compilation_result)});
179*795d594fSAndroid Build Coastguard Worker Truncate();
180*795d594fSAndroid Build Coastguard Worker }
181*795d594fSAndroid Build Coastguard Worker
ShouldAttemptCompile(OdrMetrics::Trigger trigger,time_t now) const182*795d594fSAndroid Build Coastguard Worker bool OdrCompilationLog::ShouldAttemptCompile(OdrMetrics::Trigger trigger, time_t now) const {
183*795d594fSAndroid Build Coastguard Worker if (entries_.size() == 0) {
184*795d594fSAndroid Build Coastguard Worker // We have no history, try to compile.
185*795d594fSAndroid Build Coastguard Worker return true;
186*795d594fSAndroid Build Coastguard Worker }
187*795d594fSAndroid Build Coastguard Worker
188*795d594fSAndroid Build Coastguard Worker // The backoff time is for avoiding too many failed attempts. It should not be applied if the last
189*795d594fSAndroid Build Coastguard Worker // compilation was successful.
190*795d594fSAndroid Build Coastguard Worker if (entries_.back().exit_code == ExitCode::kCompilationSuccess) {
191*795d594fSAndroid Build Coastguard Worker return true;
192*795d594fSAndroid Build Coastguard Worker }
193*795d594fSAndroid Build Coastguard Worker
194*795d594fSAndroid Build Coastguard Worker if (trigger == OdrMetrics::Trigger::kApexVersionMismatch ||
195*795d594fSAndroid Build Coastguard Worker trigger == OdrMetrics::Trigger::kDexFilesChanged) {
196*795d594fSAndroid Build Coastguard Worker // Things have changed since the last run.
197*795d594fSAndroid Build Coastguard Worker return true;
198*795d594fSAndroid Build Coastguard Worker }
199*795d594fSAndroid Build Coastguard Worker
200*795d594fSAndroid Build Coastguard Worker // Compute the backoff time based on the number of consecutive failures.
201*795d594fSAndroid Build Coastguard Worker //
202*795d594fSAndroid Build Coastguard Worker // Wait 12 hrs * pow(2, consecutive_failures) since the last compilation attempt.
203*795d594fSAndroid Build Coastguard Worker static const int kSecondsPerDay = 86'400;
204*795d594fSAndroid Build Coastguard Worker time_t backoff = kSecondsPerDay / 2;
205*795d594fSAndroid Build Coastguard Worker for (auto it = entries_.crbegin(); it != entries_.crend(); ++it, backoff *= 2) {
206*795d594fSAndroid Build Coastguard Worker if (it->exit_code == ExitCode::kCompilationSuccess) {
207*795d594fSAndroid Build Coastguard Worker break;
208*795d594fSAndroid Build Coastguard Worker }
209*795d594fSAndroid Build Coastguard Worker }
210*795d594fSAndroid Build Coastguard Worker
211*795d594fSAndroid Build Coastguard Worker if (now == 0) {
212*795d594fSAndroid Build Coastguard Worker time(&now);
213*795d594fSAndroid Build Coastguard Worker }
214*795d594fSAndroid Build Coastguard Worker
215*795d594fSAndroid Build Coastguard Worker const time_t last_attempt = entries_.back().when;
216*795d594fSAndroid Build Coastguard Worker const time_t threshold = last_attempt + backoff;
217*795d594fSAndroid Build Coastguard Worker return now >= threshold;
218*795d594fSAndroid Build Coastguard Worker }
219*795d594fSAndroid Build Coastguard Worker
220*795d594fSAndroid Build Coastguard Worker } // namespace odrefresh
221*795d594fSAndroid Build Coastguard Worker } // namespace art
222