blob: 37804a2385a9f6648a53bf446a79740e3c63c116 [file] [log] [blame]
Orion Hodson102408a2021-06-09 10:50:57 +01001/*
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 <odr_compilation_log.h>
18
19#include <errno.h>
20
21#include <fstream>
22#include <ios>
23#include <iosfwd>
24#include <istream>
25#include <ostream>
26#include <streambuf>
27#include <string>
28#include <vector>
29
30#include "android-base/logging.h"
31#include "base/os.h"
32
33#include "odrefresh/odrefresh.h"
34#include "odr_metrics.h"
35
36namespace art {
37namespace odrefresh {
38
39std::istream& operator>>(std::istream& is, OdrCompilationLogEntry& entry) {
40 // Block I/O related exceptions
41 auto saved_exceptions = is.exceptions();
42 is.exceptions(std::ios_base::iostate {});
43
Orion Hodsona15703f2021-07-07 08:28:31 +010044 // Write log entry. NB update OdrCompilationLog::kLogVersion if changing the format here.
Orion Hodson102408a2021-06-09 10:50:57 +010045 is >> entry.apex_version >> std::ws;
Orion Hodsona15703f2021-07-07 08:28:31 +010046 is >> entry.last_update_millis >> std::ws;
Orion Hodson102408a2021-06-09 10:50:57 +010047 is >> entry.trigger >> std::ws;
48 is >> entry.when >> std::ws;
49 is >> entry.exit_code >> std::ws;
50
51 // Restore I/O related exceptions
52 is.exceptions(saved_exceptions);
53 return is;
54}
55
56std::ostream& operator<<(std::ostream& os, const OdrCompilationLogEntry& entry) {
57 static const char kSpace = ' ';
58
59 // Block I/O related exceptions
60 auto saved_exceptions = os.exceptions();
61 os.exceptions(std::ios_base::iostate {});
62
63 os << entry.apex_version << kSpace;
Orion Hodsona15703f2021-07-07 08:28:31 +010064 os << entry.last_update_millis << kSpace;
Orion Hodson102408a2021-06-09 10:50:57 +010065 os << entry.trigger << kSpace;
66 os << entry.when << kSpace;
67 os << entry.exit_code << std::endl;
68
69 // Restore I/O related exceptions
70 os.exceptions(saved_exceptions);
71 return os;
72}
73
74bool operator==(const OdrCompilationLogEntry& lhs, const OdrCompilationLogEntry& rhs) {
Orion Hodsona15703f2021-07-07 08:28:31 +010075 return lhs.apex_version == rhs.apex_version && lhs.last_update_millis == rhs.last_update_millis &&
76 lhs.trigger == rhs.trigger && lhs.when == rhs.when && lhs.exit_code == rhs.exit_code;
Orion Hodson102408a2021-06-09 10:50:57 +010077}
78
79bool operator!=(const OdrCompilationLogEntry& lhs, const OdrCompilationLogEntry& rhs) {
80 return !(lhs == rhs);
81}
82
83OdrCompilationLog::OdrCompilationLog(const char* compilation_log_path)
84 : log_path_(compilation_log_path) {
85 if (log_path_ != nullptr && OS::FileExists(log_path_)) {
86 if (!Read()) {
87 PLOG(ERROR) << "Failed to read compilation log: " << log_path_;
88 }
89 }
90}
91
92OdrCompilationLog::~OdrCompilationLog() {
93 if (log_path_ != nullptr && !Write()) {
94 PLOG(ERROR) << "Failed to write compilation log: " << log_path_;
95 }
96}
97
98bool OdrCompilationLog::Read() {
99 std::ifstream ifs(log_path_);
100 if (!ifs.good()) {
101 return false;
102 }
103
Orion Hodsona15703f2021-07-07 08:28:31 +0100104 std::string log_version;
105 ifs >> log_version >> std::ws;
106 if (log_version != kLogVersion) {
107 return false;
108 }
109
Orion Hodson102408a2021-06-09 10:50:57 +0100110 while (!ifs.eof()) {
111 OdrCompilationLogEntry entry;
112 ifs >> entry;
113 if (ifs.fail()) {
114 entries_.clear();
115 return false;
116 }
117 entries_.push_back(entry);
118 }
119
120 return true;
121}
122
123bool OdrCompilationLog::Write() const {
124 std::ofstream ofs(log_path_, std::ofstream::trunc);
125 if (!ofs.good()) {
126 return false;
127 }
128
Orion Hodsona15703f2021-07-07 08:28:31 +0100129 ofs << kLogVersion << std::endl;
Orion Hodson102408a2021-06-09 10:50:57 +0100130 for (const auto& entry : entries_) {
131 ofs << entry;
132 if (ofs.fail()) {
133 return false;
134 }
135 }
136
137 return true;
138}
139
140void OdrCompilationLog::Truncate() {
141 if (entries_.size() < kMaxLoggedEntries) {
142 return;
143 }
144
145 size_t excess = entries_.size() - kMaxLoggedEntries;
146 entries_.erase(entries_.begin(), entries_.begin() + excess);
147}
148
149size_t OdrCompilationLog::NumberOfEntries() const {
150 return entries_.size();
151}
152
153const OdrCompilationLogEntry* OdrCompilationLog::Peek(size_t index) const {
154 if (index >= entries_.size()) {
155 return nullptr;
156 }
157 return &entries_[index];
158}
159
160void OdrCompilationLog::Log(int64_t apex_version,
Orion Hodsona15703f2021-07-07 08:28:31 +0100161 int64_t last_update_millis,
Orion Hodson102408a2021-06-09 10:50:57 +0100162 OdrMetrics::Trigger trigger,
163 ExitCode compilation_result) {
164 time_t now;
165 time(&now);
Orion Hodsona15703f2021-07-07 08:28:31 +0100166 Log(apex_version, last_update_millis, trigger, now, compilation_result);
Orion Hodson102408a2021-06-09 10:50:57 +0100167}
168
169void OdrCompilationLog::Log(int64_t apex_version,
Orion Hodsona15703f2021-07-07 08:28:31 +0100170 int64_t last_update_millis,
Orion Hodson102408a2021-06-09 10:50:57 +0100171 OdrMetrics::Trigger trigger,
172 time_t when,
173 ExitCode compilation_result) {
Orion Hodsona15703f2021-07-07 08:28:31 +0100174 entries_.push_back(OdrCompilationLogEntry{apex_version,
175 last_update_millis,
176 static_cast<int32_t>(trigger),
177 when,
178 static_cast<int32_t>(compilation_result)});
Orion Hodson102408a2021-06-09 10:50:57 +0100179 Truncate();
180}
181
182bool OdrCompilationLog::ShouldAttemptCompile(int64_t apex_version,
Orion Hodsona15703f2021-07-07 08:28:31 +0100183 int64_t last_update_millis,
Orion Hodson102408a2021-06-09 10:50:57 +0100184 OdrMetrics::Trigger trigger,
185 time_t now) const {
186 if (entries_.size() == 0) {
187 // We have no history, try to compile.
188 return true;
189 }
190
191 if (apex_version != entries_.back().apex_version) {
Orion Hodsona15703f2021-07-07 08:28:31 +0100192 // There is a new ART APEX, we should compile right away.
193 return true;
194 }
195
196 if (last_update_millis != entries_.back().last_update_millis) {
197 // There is a samegrade ART APEX update, we should compile right away.
Orion Hodson102408a2021-06-09 10:50:57 +0100198 return true;
199 }
200
201 if (trigger == OdrMetrics::Trigger::kDexFilesChanged) {
202 // The DEX files in the classpaths have changed, possibly an OTA has updated them.
203 return true;
204 }
205
206 // Compute the backoff time based on the number of consecutive failures.
207 //
208 // Wait 12 hrs * pow(2, consecutive_failures) since the last compilation attempt.
209 static const int kSecondsPerDay = 86'400;
210 time_t backoff = kSecondsPerDay / 2;
211 for (auto it = entries_.crbegin(); it != entries_.crend(); ++it, backoff *= 2) {
212 if (it->exit_code == ExitCode::kCompilationSuccess) {
213 break;
214 }
215 }
216
217 if (now == 0) {
218 time(&now);
219 }
220
221 const time_t last_attempt = entries_.back().when;
222 const time_t threshold = last_attempt + backoff;
223 return now >= threshold;
224}
225
226} // namespace odrefresh
227} // namespace art