• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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