blob: 55432f4357e7dc4a1d91310bc317ebd30afdcb1b [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
44 is >> entry.apex_version >> std::ws;
45 is >> entry.trigger >> std::ws;
46 is >> entry.when >> std::ws;
47 is >> entry.exit_code >> std::ws;
48
49 // Restore I/O related exceptions
50 is.exceptions(saved_exceptions);
51 return is;
52}
53
54std::ostream& operator<<(std::ostream& os, const OdrCompilationLogEntry& entry) {
55 static const char kSpace = ' ';
56
57 // Block I/O related exceptions
58 auto saved_exceptions = os.exceptions();
59 os.exceptions(std::ios_base::iostate {});
60
61 os << entry.apex_version << kSpace;
62 os << entry.trigger << kSpace;
63 os << entry.when << kSpace;
64 os << entry.exit_code << std::endl;
65
66 // Restore I/O related exceptions
67 os.exceptions(saved_exceptions);
68 return os;
69}
70
71bool operator==(const OdrCompilationLogEntry& lhs, const OdrCompilationLogEntry& rhs) {
72 return lhs.apex_version == rhs.apex_version && lhs.trigger == rhs.trigger &&
73 lhs.when == rhs.when && lhs.exit_code == rhs.exit_code;
74}
75
76bool operator!=(const OdrCompilationLogEntry& lhs, const OdrCompilationLogEntry& rhs) {
77 return !(lhs == rhs);
78}
79
80OdrCompilationLog::OdrCompilationLog(const char* compilation_log_path)
81 : log_path_(compilation_log_path) {
82 if (log_path_ != nullptr && OS::FileExists(log_path_)) {
83 if (!Read()) {
84 PLOG(ERROR) << "Failed to read compilation log: " << log_path_;
85 }
86 }
87}
88
89OdrCompilationLog::~OdrCompilationLog() {
90 if (log_path_ != nullptr && !Write()) {
91 PLOG(ERROR) << "Failed to write compilation log: " << log_path_;
92 }
93}
94
95bool OdrCompilationLog::Read() {
96 std::ifstream ifs(log_path_);
97 if (!ifs.good()) {
98 return false;
99 }
100
101 while (!ifs.eof()) {
102 OdrCompilationLogEntry entry;
103 ifs >> entry;
104 if (ifs.fail()) {
105 entries_.clear();
106 return false;
107 }
108 entries_.push_back(entry);
109 }
110
111 return true;
112}
113
114bool OdrCompilationLog::Write() const {
115 std::ofstream ofs(log_path_, std::ofstream::trunc);
116 if (!ofs.good()) {
117 return false;
118 }
119
120 for (const auto& entry : entries_) {
121 ofs << entry;
122 if (ofs.fail()) {
123 return false;
124 }
125 }
126
127 return true;
128}
129
130void OdrCompilationLog::Truncate() {
131 if (entries_.size() < kMaxLoggedEntries) {
132 return;
133 }
134
135 size_t excess = entries_.size() - kMaxLoggedEntries;
136 entries_.erase(entries_.begin(), entries_.begin() + excess);
137}
138
139size_t OdrCompilationLog::NumberOfEntries() const {
140 return entries_.size();
141}
142
143const OdrCompilationLogEntry* OdrCompilationLog::Peek(size_t index) const {
144 if (index >= entries_.size()) {
145 return nullptr;
146 }
147 return &entries_[index];
148}
149
150void OdrCompilationLog::Log(int64_t apex_version,
151 OdrMetrics::Trigger trigger,
152 ExitCode compilation_result) {
153 time_t now;
154 time(&now);
155 Log(apex_version, trigger, now, compilation_result);
156}
157
158void OdrCompilationLog::Log(int64_t apex_version,
159 OdrMetrics::Trigger trigger,
160 time_t when,
161 ExitCode compilation_result) {
162 entries_.push_back(OdrCompilationLogEntry{
163 apex_version, static_cast<int32_t>(trigger), when, static_cast<int32_t>(compilation_result)});
164 Truncate();
165}
166
167bool OdrCompilationLog::ShouldAttemptCompile(int64_t apex_version,
168 OdrMetrics::Trigger trigger,
169 time_t now) const {
170 if (entries_.size() == 0) {
171 // We have no history, try to compile.
172 return true;
173 }
174
175 if (apex_version != entries_.back().apex_version) {
176 // There is a new ART APEX, we should use compile right away.
177 return true;
178 }
179
180 if (trigger == OdrMetrics::Trigger::kDexFilesChanged) {
181 // The DEX files in the classpaths have changed, possibly an OTA has updated them.
182 return true;
183 }
184
185 // Compute the backoff time based on the number of consecutive failures.
186 //
187 // Wait 12 hrs * pow(2, consecutive_failures) since the last compilation attempt.
188 static const int kSecondsPerDay = 86'400;
189 time_t backoff = kSecondsPerDay / 2;
190 for (auto it = entries_.crbegin(); it != entries_.crend(); ++it, backoff *= 2) {
191 if (it->exit_code == ExitCode::kCompilationSuccess) {
192 break;
193 }
194 }
195
196 if (now == 0) {
197 time(&now);
198 }
199
200 const time_t last_attempt = entries_.back().when;
201 const time_t threshold = last_attempt + backoff;
202 return now >= threshold;
203}
204
205} // namespace odrefresh
206} // namespace art