| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <odr_compilation_log.h> |
| |
| #include <time.h> |
| |
| #include <cstdint> |
| #include <ctime> |
| #include <iosfwd> |
| #include <istream> |
| #include <limits> |
| #include <ostream> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| #include "android-base/file.h" |
| #include "base/common_art_test.h" |
| #include "odrefresh/odrefresh.h" |
| #include "odr_metrics.h" |
| |
| namespace art { |
| namespace odrefresh { |
| |
| const time_t kSecondsPerDay = 86'400; |
| |
| class OdrCompilationLogTest : public CommonArtTest {}; |
| |
| TEST(OdrCompilationLogEntry, Equality) { |
| OdrCompilationLogEntry a{1, 2, 3, 4, 5}; |
| |
| ASSERT_EQ(a, (OdrCompilationLogEntry{1, 2, 3, 4, 5})); |
| ASSERT_NE(a, (OdrCompilationLogEntry{9, 2, 3, 4, 5})); |
| ASSERT_NE(a, (OdrCompilationLogEntry{1, 9, 3, 4, 5})); |
| ASSERT_NE(a, (OdrCompilationLogEntry{1, 2, 9, 4, 5})); |
| ASSERT_NE(a, (OdrCompilationLogEntry{2, 2, 3, 9, 5})); |
| ASSERT_NE(a, (OdrCompilationLogEntry{2, 2, 3, 5, 9})); |
| } |
| |
| TEST(OdrCompilationLogEntry, InputOutput) { |
| const OdrCompilationLogEntry entries[] = { |
| {1, 2, 3, 4, 5}, |
| {std::numeric_limits<int64_t>::min(), |
| std::numeric_limits<int64_t>::min(), |
| std::numeric_limits<int32_t>::min(), |
| std::numeric_limits<time_t>::min(), |
| std::numeric_limits<int32_t>::min()}, |
| {std::numeric_limits<int64_t>::max(), |
| std::numeric_limits<int64_t>::max(), |
| std::numeric_limits<int32_t>::max(), |
| std::numeric_limits<time_t>::max(), |
| std::numeric_limits<int32_t>::max()}, |
| {0, 0, 0, 0, 0}, |
| {0x7fedcba9'87654321, 0x5a5a5a5a'5a5a5a5a, 0x12345678, 0x2346789, 0x76543210} |
| }; |
| for (const auto& entry : entries) { |
| std::stringstream ss; |
| ss << entry; |
| OdrCompilationLogEntry actual; |
| ss >> actual; |
| ASSERT_EQ(entry, actual); |
| } |
| } |
| |
| TEST(OdrCompilationLogEntry, TruncatedInput) { |
| std::stringstream ss; |
| ss << "1 2"; |
| |
| OdrCompilationLogEntry entry; |
| ss >> entry; |
| |
| ASSERT_TRUE(ss.fail()); |
| ASSERT_FALSE(ss.bad()); |
| } |
| |
| TEST(OdrCompilationLogEntry, ReadMultiple) { |
| std::stringstream ss; |
| ss << "0 1 2 3 4\n5 6 7 8 9\n"; |
| |
| OdrCompilationLogEntry entry0, entry1; |
| ss >> entry0 >> entry1; |
| ASSERT_EQ(entry0, (OdrCompilationLogEntry{0, 1, 2, 3, 4})); |
| ASSERT_EQ(entry1, (OdrCompilationLogEntry{5, 6, 7, 8, 9})); |
| |
| ASSERT_FALSE(ss.fail()); |
| ASSERT_FALSE(ss.bad()); |
| } |
| |
| TEST(OdrCompilationLog, ShouldAttemptCompile) { |
| OdrCompilationLog ocl(/*compilation_log_path=*/nullptr); |
| |
| ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, 0)); |
| |
| ocl.Log( |
| /*apex_version=*/1, |
| /*last_update_millis=*/762, |
| OdrMetrics::Trigger::kApexVersionMismatch, |
| ExitCode::kCompilationFailed); |
| ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kApexVersionMismatch)); |
| ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kDexFilesChanged)); |
| ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown)); |
| } |
| |
| TEST(OdrCompilationLog, BackOffNoHistory) { |
| time_t start_time; |
| time(&start_time); |
| |
| OdrCompilationLog ocl(/*compilation_log_path=*/nullptr); |
| |
| ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time)); |
| |
| // Start log |
| ocl.Log(/*apex_version=*/1, |
| /*last_update_millis=*/0, |
| OdrMetrics::Trigger::kApexVersionMismatch, |
| start_time, |
| ExitCode::kCompilationFailed); |
| ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time)); |
| ASSERT_FALSE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 2)); |
| ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay)); |
| |
| // Add one more log entry |
| ocl.Log(/*apex_version=*/1, |
| /*last_update_millis=*/0, |
| OdrMetrics::Trigger::kApexVersionMismatch, |
| start_time, |
| ExitCode::kCompilationFailed); |
| ASSERT_FALSE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay)); |
| ASSERT_TRUE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 2 * kSecondsPerDay)); |
| |
| // One more. |
| ocl.Log(/*apex_version=*/1, |
| /*last_update_millis=*/0, |
| OdrMetrics::Trigger::kApexVersionMismatch, |
| start_time, |
| ExitCode::kCompilationFailed); |
| ASSERT_FALSE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 3 * kSecondsPerDay)); |
| ASSERT_TRUE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 4 * kSecondsPerDay)); |
| |
| // And one for the road. |
| ocl.Log(/*apex_version=*/1, |
| /*last_update_millis=*/0, |
| OdrMetrics::Trigger::kApexVersionMismatch, |
| start_time, |
| ExitCode::kCompilationFailed); |
| ASSERT_FALSE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 7 * kSecondsPerDay)); |
| ASSERT_TRUE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 8 * kSecondsPerDay)); |
| } |
| |
| TEST(OdrCompilationLog, BackOffHappyHistory) { |
| time_t start_time; |
| time(&start_time); |
| |
| OdrCompilationLog ocl(/*compilation_log_path=*/nullptr); |
| |
| // Start log with a successful entry. |
| ocl.Log(/*apex_version=*/1, |
| /*last_update_millis=*/0, |
| OdrMetrics::Trigger::kApexVersionMismatch, |
| start_time, |
| ExitCode::kCompilationSuccess); |
| ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time)); |
| ASSERT_TRUE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 4)); |
| ASSERT_TRUE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 2)); |
| |
| // Add a log entry for a failed compilation. |
| ocl.Log(/*apex_version=*/1, |
| /*last_update_millis=*/0, |
| OdrMetrics::Trigger::kApexVersionMismatch, |
| start_time, |
| ExitCode::kCompilationFailed); |
| ASSERT_FALSE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 2)); |
| ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay)); |
| } |
| |
| TEST_F(OdrCompilationLogTest, LogNumberOfEntriesAndPeek) { |
| OdrCompilationLog ocl(/*compilation_log_path=*/nullptr); |
| |
| std::vector<OdrCompilationLogEntry> entries = { |
| { 0, 1, 2, 3, 4 }, |
| { 1, 2, 3, 4, 5 }, |
| { 2, 3, 4, 5, 6 }, |
| { 3, 4, 5, 6, 7 }, |
| { 4, 5, 6, 7, 8 }, |
| { 5, 6, 7, 8, 9 }, |
| { 6, 7, 8, 9, 10 } |
| }; |
| |
| for (size_t i = 0; i < entries.size(); ++i) { |
| OdrCompilationLogEntry& e = entries[i]; |
| ocl.Log(e.apex_version, |
| e.last_update_millis, |
| static_cast<OdrMetrics::Trigger>(e.trigger), |
| e.when, |
| static_cast<ExitCode>(e.exit_code)); |
| if (i < OdrCompilationLog::kMaxLoggedEntries) { |
| ASSERT_EQ(i + 1, ocl.NumberOfEntries()); |
| } else { |
| ASSERT_EQ(OdrCompilationLog::kMaxLoggedEntries, ocl.NumberOfEntries()); |
| } |
| |
| for (size_t j = 0; j < ocl.NumberOfEntries(); ++j) { |
| const OdrCompilationLogEntry* logged = ocl.Peek(j); |
| ASSERT_TRUE(logged != nullptr); |
| const OdrCompilationLogEntry& expected = entries[i + 1 - ocl.NumberOfEntries() + j]; |
| ASSERT_EQ(expected, *logged); |
| } |
| } |
| } |
| |
| TEST_F(OdrCompilationLogTest, LogReadWrite) { |
| std::vector<OdrCompilationLogEntry> entries = { |
| { 0, 1, 2, 3, 4 }, |
| { 1, 2, 3, 4, 5 }, |
| { 2, 3, 4, 5, 6 }, |
| { 3, 4, 5, 6, 7 }, |
| { 4, 5, 6, 7, 8 }, |
| { 5, 6, 7, 8, 9 }, |
| { 6, 7, 8, 9, 10 } |
| }; |
| |
| ScratchFile scratch_file; |
| scratch_file.Close(); |
| |
| for (size_t i = 0; i < entries.size(); ++i) { |
| { |
| OdrCompilationLog ocl(scratch_file.GetFilename().c_str()); |
| OdrCompilationLogEntry& e = entries[i]; |
| ocl.Log(e.apex_version, |
| e.last_update_millis, |
| static_cast<OdrMetrics::Trigger>(e.trigger), |
| e.when, |
| static_cast<ExitCode>(e.exit_code)); |
| } |
| |
| { |
| OdrCompilationLog ocl(scratch_file.GetFilename().c_str()); |
| if (i < OdrCompilationLog::kMaxLoggedEntries) { |
| ASSERT_EQ(i + 1, ocl.NumberOfEntries()); |
| } else { |
| ASSERT_EQ(OdrCompilationLog::kMaxLoggedEntries, ocl.NumberOfEntries()); |
| } |
| |
| for (size_t j = 0; j < ocl.NumberOfEntries(); ++j) { |
| const OdrCompilationLogEntry* logged = ocl.Peek(j); |
| ASSERT_TRUE(logged != nullptr); |
| const OdrCompilationLogEntry& expected = entries[i + 1 - ocl.NumberOfEntries() + j]; |
| ASSERT_EQ(expected, *logged); |
| } |
| } |
| } |
| } |
| |
| TEST_F(OdrCompilationLogTest, BackoffBasedOnLog) { |
| time_t start_time; |
| time(&start_time); |
| |
| ScratchFile scratch_file; |
| scratch_file.Close(); |
| |
| const char* log_path = scratch_file.GetFilename().c_str(); |
| { |
| OdrCompilationLog ocl(log_path); |
| |
| ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time)); |
| } |
| |
| { |
| OdrCompilationLog ocl(log_path); |
| |
| // Start log |
| ocl.Log(/*apex_version=*/1, |
| /*last_update_millis=*/0, |
| OdrMetrics::Trigger::kApexVersionMismatch, |
| start_time, |
| ExitCode::kCompilationFailed); |
| } |
| |
| { |
| OdrCompilationLog ocl(log_path); |
| ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time)); |
| ASSERT_FALSE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 2)); |
| ASSERT_TRUE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay)); |
| } |
| |
| { |
| // Add one more log entry |
| OdrCompilationLog ocl(log_path); |
| ocl.Log(/*apex_version=*/1, |
| /*last_update_millis=*/0, |
| OdrMetrics::Trigger::kApexVersionMismatch, |
| start_time, |
| ExitCode::kCompilationFailed); |
| } |
| |
| { |
| OdrCompilationLog ocl(log_path); |
| |
| ASSERT_FALSE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay)); |
| ASSERT_TRUE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 2 * kSecondsPerDay)); |
| } |
| |
| { |
| // One more log entry. |
| OdrCompilationLog ocl(log_path); |
| ocl.Log(/*apex_version=*/1, |
| /*last_update_millis=*/0, |
| OdrMetrics::Trigger::kApexVersionMismatch, |
| start_time, |
| ExitCode::kCompilationFailed); |
| } |
| |
| { |
| OdrCompilationLog ocl(log_path); |
| ASSERT_FALSE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 3 * kSecondsPerDay)); |
| ASSERT_TRUE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 4 * kSecondsPerDay)); |
| } |
| |
| { |
| // And one for the road. |
| OdrCompilationLog ocl(log_path); |
| ocl.Log(/*apex_version=*/1, |
| /*last_update_millis=*/0, |
| OdrMetrics::Trigger::kApexVersionMismatch, |
| start_time, |
| ExitCode::kCompilationFailed); |
| } |
| |
| { |
| OdrCompilationLog ocl(log_path); |
| ASSERT_FALSE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 7 * kSecondsPerDay)); |
| ASSERT_TRUE( |
| ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + 8 * kSecondsPerDay)); |
| } |
| } |
| |
| TEST_F(OdrCompilationLogTest, NewLogVersionTriggersCompilation) { |
| static const int64_t kApexVersion = 1066; |
| static const int64_t kLastUpdateMillis = 777; |
| time_t start_time; |
| time(&start_time); |
| |
| ScratchFile scratch_file; |
| scratch_file.Close(); |
| |
| // Generate a compilation log. |
| { |
| OdrCompilationLog ocl(scratch_file.GetFilename().c_str()); |
| for (size_t i = 0; i < OdrCompilationLog::kMaxLoggedEntries; ++i) { |
| ocl.Log(kApexVersion, |
| kLastUpdateMillis, |
| OdrMetrics::Trigger::kApexVersionMismatch, |
| start_time, |
| ExitCode::kCompilationFailed); |
| ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time)); |
| } |
| } |
| |
| // Replace version string in the compilation log. |
| std::string log_text; |
| ASSERT_TRUE(android::base::ReadFileToString(scratch_file.GetFilename(), &log_text)); |
| std::string new_log_version = std::string(OdrCompilationLog::kLogVersion) + "a"; |
| log_text.replace(0, new_log_version.size() - 1, new_log_version); |
| ASSERT_TRUE(android::base::WriteStringToFile(log_text, scratch_file.GetFilename())); |
| |
| // Read log with updated version entry, check it is treated as out-of-date. |
| { |
| OdrCompilationLog ocl(scratch_file.GetFilename().c_str()); |
| ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time)); |
| ASSERT_EQ(0u, ocl.NumberOfEntries()); |
| } |
| } |
| |
| } // namespace odrefresh |
| } // namespace art |