1 /*
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 <time.h>
20
21 #include <cstdint>
22 #include <ctime>
23 #include <iosfwd>
24 #include <istream>
25 #include <limits>
26 #include <ostream>
27 #include <sstream>
28 #include <string>
29 #include <vector>
30
31 #include "android-base/file.h"
32 #include "base/common_art_test.h"
33 #include "odrefresh/odrefresh.h"
34 #include "odr_metrics.h"
35
36 namespace art {
37 namespace odrefresh {
38
39 const time_t kSecondsPerDay = 86'400;
40
41 class OdrCompilationLogTest : public CommonArtTest {};
42
TEST(OdrCompilationLogEntry,Equality)43 TEST(OdrCompilationLogEntry, Equality) {
44 OdrCompilationLogEntry a{1, 2, 3, 4, 5};
45
46 ASSERT_EQ(a, (OdrCompilationLogEntry{1, 2, 3, 4, 5}));
47 ASSERT_NE(a, (OdrCompilationLogEntry{9, 2, 3, 4, 5}));
48 ASSERT_NE(a, (OdrCompilationLogEntry{1, 9, 3, 4, 5}));
49 ASSERT_NE(a, (OdrCompilationLogEntry{1, 2, 9, 4, 5}));
50 ASSERT_NE(a, (OdrCompilationLogEntry{2, 2, 3, 9, 5}));
51 ASSERT_NE(a, (OdrCompilationLogEntry{2, 2, 3, 5, 9}));
52 }
53
TEST(OdrCompilationLogEntry,InputOutput)54 TEST(OdrCompilationLogEntry, InputOutput) {
55 const OdrCompilationLogEntry entries[] = {
56 {1, 2, 3, 4, 5},
57 {std::numeric_limits<int64_t>::min(),
58 std::numeric_limits<int64_t>::min(),
59 std::numeric_limits<int32_t>::min(),
60 std::numeric_limits<time_t>::min(),
61 std::numeric_limits<int32_t>::min()},
62 {std::numeric_limits<int64_t>::max(),
63 std::numeric_limits<int64_t>::max(),
64 std::numeric_limits<int32_t>::max(),
65 std::numeric_limits<time_t>::max(),
66 std::numeric_limits<int32_t>::max()},
67 {0, 0, 0, 0, 0},
68 {0x7fedcba9'87654321, 0x5a5a5a5a'5a5a5a5a, 0x12345678, 0x2346789, 0x76543210}
69 };
70 for (const auto& entry : entries) {
71 std::stringstream ss;
72 ss << entry;
73 OdrCompilationLogEntry actual;
74 ss >> actual;
75 ASSERT_EQ(entry, actual);
76 }
77 }
78
TEST(OdrCompilationLogEntry,TruncatedInput)79 TEST(OdrCompilationLogEntry, TruncatedInput) {
80 std::stringstream ss;
81 ss << "1 2";
82
83 OdrCompilationLogEntry entry;
84 ss >> entry;
85
86 ASSERT_TRUE(ss.fail());
87 ASSERT_FALSE(ss.bad());
88 }
89
TEST(OdrCompilationLogEntry,ReadMultiple)90 TEST(OdrCompilationLogEntry, ReadMultiple) {
91 std::stringstream ss;
92 ss << "0 1 2 3 4\n5 6 7 8 9\n";
93
94 OdrCompilationLogEntry entry0, entry1;
95 ss >> entry0 >> entry1;
96 ASSERT_EQ(entry0, (OdrCompilationLogEntry{0, 1, 2, 3, 4}));
97 ASSERT_EQ(entry1, (OdrCompilationLogEntry{5, 6, 7, 8, 9}));
98
99 ASSERT_FALSE(ss.fail());
100 ASSERT_FALSE(ss.bad());
101 }
102
TEST(OdrCompilationLog,ShouldAttemptCompile)103 TEST(OdrCompilationLog, ShouldAttemptCompile) {
104 OdrCompilationLog ocl(/*compilation_log_path=*/nullptr);
105
106 ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, 0));
107
108 ocl.Log(
109 /*apex_version=*/1,
110 /*last_update_millis=*/762,
111 OdrMetrics::Trigger::kApexVersionMismatch,
112 ExitCode::kCompilationSuccess);
113 ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kApexVersionMismatch));
114 ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kDexFilesChanged));
115 ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown));
116 }
117
TEST(OdrCompilationLog,BackOffNoHistory)118 TEST(OdrCompilationLog, BackOffNoHistory) {
119 time_t start_time;
120 time(&start_time);
121
122 OdrCompilationLog ocl(/*compilation_log_path=*/nullptr);
123
124 ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
125
126 // Start log
127 ocl.Log(/*apex_version=*/1,
128 /*last_update_millis=*/0,
129 OdrMetrics::Trigger::kApexVersionMismatch,
130 start_time,
131 ExitCode::kCompilationFailed);
132 ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
133 ASSERT_FALSE(
134 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 2));
135 ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay));
136
137 // Add one more log entry
138 ocl.Log(/*apex_version=*/1,
139 /*last_update_millis=*/0,
140 OdrMetrics::Trigger::kApexVersionMismatch,
141 start_time,
142 ExitCode::kCompilationFailed);
143 ASSERT_FALSE(
144 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay));
145 ASSERT_TRUE(
146 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 2 * kSecondsPerDay));
147
148 // One more.
149 ocl.Log(/*apex_version=*/1,
150 /*last_update_millis=*/0,
151 OdrMetrics::Trigger::kApexVersionMismatch,
152 start_time,
153 ExitCode::kCompilationFailed);
154 ASSERT_FALSE(
155 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 3 * kSecondsPerDay));
156 ASSERT_TRUE(
157 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 4 * kSecondsPerDay));
158
159 // And one for the road.
160 ocl.Log(/*apex_version=*/1,
161 /*last_update_millis=*/0,
162 OdrMetrics::Trigger::kApexVersionMismatch,
163 start_time,
164 ExitCode::kCompilationFailed);
165 ASSERT_FALSE(
166 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 7 * kSecondsPerDay));
167 ASSERT_TRUE(
168 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 8 * kSecondsPerDay));
169 }
170
TEST(OdrCompilationLog,BackOffHappyHistory)171 TEST(OdrCompilationLog, BackOffHappyHistory) {
172 time_t start_time;
173 time(&start_time);
174
175 OdrCompilationLog ocl(/*compilation_log_path=*/nullptr);
176
177 // Start log with a successful entry.
178 ocl.Log(/*apex_version=*/1,
179 /*last_update_millis=*/0,
180 OdrMetrics::Trigger::kApexVersionMismatch,
181 start_time,
182 ExitCode::kCompilationSuccess);
183 ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
184 ASSERT_FALSE(
185 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 4));
186 ASSERT_TRUE(
187 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 2));
188
189 // Add a log entry for a failed compilation.
190 ocl.Log(/*apex_version=*/1,
191 /*last_update_millis=*/0,
192 OdrMetrics::Trigger::kApexVersionMismatch,
193 start_time,
194 ExitCode::kCompilationFailed);
195 ASSERT_FALSE(
196 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 2));
197 ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay));
198 }
199
TEST_F(OdrCompilationLogTest,LogNumberOfEntriesAndPeek)200 TEST_F(OdrCompilationLogTest, LogNumberOfEntriesAndPeek) {
201 OdrCompilationLog ocl(/*compilation_log_path=*/nullptr);
202
203 std::vector<OdrCompilationLogEntry> entries = {
204 { 0, 1, 2, 3, 4 },
205 { 1, 2, 3, 4, 5 },
206 { 2, 3, 4, 5, 6 },
207 { 3, 4, 5, 6, 7 },
208 { 4, 5, 6, 7, 8 },
209 { 5, 6, 7, 8, 9 },
210 { 6, 7, 8, 9, 10 }
211 };
212
213 for (size_t i = 0; i < entries.size(); ++i) {
214 OdrCompilationLogEntry& e = entries[i];
215 ocl.Log(e.apex_version,
216 e.last_update_millis,
217 static_cast<OdrMetrics::Trigger>(e.trigger),
218 e.when,
219 static_cast<ExitCode>(e.exit_code));
220 if (i < OdrCompilationLog::kMaxLoggedEntries) {
221 ASSERT_EQ(i + 1, ocl.NumberOfEntries());
222 } else {
223 ASSERT_EQ(OdrCompilationLog::kMaxLoggedEntries, ocl.NumberOfEntries());
224 }
225
226 for (size_t j = 0; j < ocl.NumberOfEntries(); ++j) {
227 const OdrCompilationLogEntry* logged = ocl.Peek(j);
228 ASSERT_TRUE(logged != nullptr);
229 const OdrCompilationLogEntry& expected = entries[i + 1 - ocl.NumberOfEntries() + j];
230 ASSERT_EQ(expected, *logged);
231 }
232 }
233 }
234
TEST_F(OdrCompilationLogTest,LogReadWrite)235 TEST_F(OdrCompilationLogTest, LogReadWrite) {
236 std::vector<OdrCompilationLogEntry> entries = {
237 { 0, 1, 2, 3, 4 },
238 { 1, 2, 3, 4, 5 },
239 { 2, 3, 4, 5, 6 },
240 { 3, 4, 5, 6, 7 },
241 { 4, 5, 6, 7, 8 },
242 { 5, 6, 7, 8, 9 },
243 { 6, 7, 8, 9, 10 }
244 };
245
246 ScratchFile scratch_file;
247 scratch_file.Close();
248
249 for (size_t i = 0; i < entries.size(); ++i) {
250 {
251 OdrCompilationLog ocl(scratch_file.GetFilename().c_str());
252 OdrCompilationLogEntry& e = entries[i];
253 ocl.Log(e.apex_version,
254 e.last_update_millis,
255 static_cast<OdrMetrics::Trigger>(e.trigger),
256 e.when,
257 static_cast<ExitCode>(e.exit_code));
258 }
259
260 {
261 OdrCompilationLog ocl(scratch_file.GetFilename().c_str());
262 if (i < OdrCompilationLog::kMaxLoggedEntries) {
263 ASSERT_EQ(i + 1, ocl.NumberOfEntries());
264 } else {
265 ASSERT_EQ(OdrCompilationLog::kMaxLoggedEntries, ocl.NumberOfEntries());
266 }
267
268 for (size_t j = 0; j < ocl.NumberOfEntries(); ++j) {
269 const OdrCompilationLogEntry* logged = ocl.Peek(j);
270 ASSERT_TRUE(logged != nullptr);
271 const OdrCompilationLogEntry& expected = entries[i + 1 - ocl.NumberOfEntries() + j];
272 ASSERT_EQ(expected, *logged);
273 }
274 }
275 }
276 }
277
TEST_F(OdrCompilationLogTest,BackoffBasedOnLog)278 TEST_F(OdrCompilationLogTest, BackoffBasedOnLog) {
279 time_t start_time;
280 time(&start_time);
281
282 ScratchFile scratch_file;
283 scratch_file.Close();
284
285 const char* log_path = scratch_file.GetFilename().c_str();
286 {
287 OdrCompilationLog ocl(log_path);
288
289 ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
290 }
291
292 {
293 OdrCompilationLog ocl(log_path);
294
295 // Start log
296 ocl.Log(/*apex_version=*/1,
297 /*last_update_millis=*/0,
298 OdrMetrics::Trigger::kApexVersionMismatch,
299 start_time,
300 ExitCode::kCompilationFailed);
301 }
302
303 {
304 OdrCompilationLog ocl(log_path);
305 ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
306 ASSERT_FALSE(
307 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 2));
308 ASSERT_TRUE(
309 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay));
310 }
311
312 {
313 // Add one more log entry
314 OdrCompilationLog ocl(log_path);
315 ocl.Log(/*apex_version=*/1,
316 /*last_update_millis=*/0,
317 OdrMetrics::Trigger::kApexVersionMismatch,
318 start_time,
319 ExitCode::kCompilationFailed);
320 }
321
322 {
323 OdrCompilationLog ocl(log_path);
324
325 ASSERT_FALSE(
326 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay));
327 ASSERT_TRUE(
328 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 2 * kSecondsPerDay));
329 }
330
331 {
332 // One more log entry.
333 OdrCompilationLog ocl(log_path);
334 ocl.Log(/*apex_version=*/1,
335 /*last_update_millis=*/0,
336 OdrMetrics::Trigger::kApexVersionMismatch,
337 start_time,
338 ExitCode::kCompilationFailed);
339 }
340
341 {
342 OdrCompilationLog ocl(log_path);
343 ASSERT_FALSE(
344 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 3 * kSecondsPerDay));
345 ASSERT_TRUE(
346 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 4 * kSecondsPerDay));
347 }
348
349 {
350 // And one for the road.
351 OdrCompilationLog ocl(log_path);
352 ocl.Log(/*apex_version=*/1,
353 /*last_update_millis=*/0,
354 OdrMetrics::Trigger::kApexVersionMismatch,
355 start_time,
356 ExitCode::kCompilationFailed);
357 }
358
359 {
360 OdrCompilationLog ocl(log_path);
361 ASSERT_FALSE(
362 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 7 * kSecondsPerDay));
363 ASSERT_TRUE(
364 ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 8 * kSecondsPerDay));
365 }
366 }
367
TEST_F(OdrCompilationLogTest,NewLogVersionTriggersCompilation)368 TEST_F(OdrCompilationLogTest, NewLogVersionTriggersCompilation) {
369 static const int64_t kApexVersion = 1066;
370 static const int64_t kLastUpdateMillis = 777;
371 time_t start_time;
372 time(&start_time);
373
374 ScratchFile scratch_file;
375 scratch_file.Close();
376
377 // Generate a compilation log.
378 {
379 OdrCompilationLog ocl(scratch_file.GetFilename().c_str());
380 for (size_t i = 0; i < OdrCompilationLog::kMaxLoggedEntries; ++i) {
381 ocl.Log(kApexVersion,
382 kLastUpdateMillis,
383 OdrMetrics::Trigger::kApexVersionMismatch,
384 start_time,
385 ExitCode::kCompilationSuccess);
386 ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
387 }
388 }
389
390 // Replace version string in the compilation log.
391 std::string log_text;
392 ASSERT_TRUE(android::base::ReadFileToString(scratch_file.GetFilename(), &log_text));
393 std::string new_log_version = std::string(OdrCompilationLog::kLogVersion) + "a";
394 log_text.replace(0, new_log_version.size() - 1, new_log_version);
395 ASSERT_TRUE(android::base::WriteStringToFile(log_text, scratch_file.GetFilename()));
396
397 // Read log with updated version entry, check it is treated as out-of-date.
398 {
399 OdrCompilationLog ocl(scratch_file.GetFilename().c_str());
400 ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
401 ASSERT_EQ(0u, ocl.NumberOfEntries());
402 }
403 }
404
405 } // namespace odrefresh
406 } // namespace art
407