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