diff options
| -rw-r--r-- | odrefresh/CacheInfo.xsd | 14 | ||||
| -rw-r--r-- | odrefresh/odr_config.h | 23 | ||||
| -rw-r--r-- | odrefresh/odrefresh.cc | 134 | ||||
| -rw-r--r-- | odrefresh/odrefresh.h | 19 | ||||
| -rw-r--r-- | odrefresh/odrefresh_main.cc | 24 | ||||
| -rw-r--r-- | odrefresh/schema/current.txt | 16 | ||||
| -rw-r--r-- | test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java | 54 |
7 files changed, 264 insertions, 20 deletions
diff --git a/odrefresh/CacheInfo.xsd b/odrefresh/CacheInfo.xsd index 1cbef8a23e..196caf7527 100644 --- a/odrefresh/CacheInfo.xsd +++ b/odrefresh/CacheInfo.xsd @@ -27,6 +27,7 @@ <!-- True if the cache info is generated in the Compilation OS. --> <xs:attribute name="compilationOsMode" type="xs:boolean" /> <xs:sequence> + <xs:element name="systemProperties" minOccurs="1" maxOccurs="1" type="t:keyValuePairList" /> <xs:element name="artModuleInfo" minOccurs="1" maxOccurs="1" type="t:moduleInfo" /> <xs:element name="moduleInfoList" minOccurs="1" maxOccurs="1" type="t:moduleInfoList" /> <xs:element name="bootClasspath" minOccurs="1" maxOccurs="1" type="t:classpath" /> @@ -36,6 +37,19 @@ </xs:complexType> </xs:element> + <!-- List of key-value pairs. --> + <xs:complexType name="keyValuePairList"> + <xs:sequence> + <xs:element name="item" type="t:keyValuePair" /> + </xs:sequence> + </xs:complexType> + + <!-- A key-value pair. --> + <xs:complexType name="keyValuePair"> + <xs:attribute name="k" type="xs:string" use="required" /> + <xs:attribute name="v" type="xs:string" use="required" /> + </xs:complexType> + <!-- List of modules. --> <xs:complexType name="moduleInfoList"> <xs:sequence> diff --git a/odrefresh/odr_config.h b/odrefresh/odr_config.h index ec0290ccfe..c3f3c8010a 100644 --- a/odrefresh/odr_config.h +++ b/odrefresh/odr_config.h @@ -19,9 +19,11 @@ #include <optional> #include <string> +#include <unordered_map> #include <vector> #include "android-base/file.h" +#include "android-base/no_destructor.h" #include "arch/instruction_set.h" #include "base/file_utils.h" #include "base/globals.h" @@ -32,6 +34,17 @@ namespace art { namespace odrefresh { +struct SystemPropertyConfig { + const char* name; + const char* default_value; +}; + +// The system properties that odrefresh keeps track of. Odrefresh will recompile everything if any +// property changes. +const android::base::NoDestructor<std::vector<SystemPropertyConfig>> kSystemProperties{ + {SystemPropertyConfig{.name = "persist.device_config.runtime_native_boot.enable_uffd_gc", + .default_value = "false"}}}; + // An enumeration of the possible zygote configurations on Android. enum class ZygoteKind : uint8_t { // 32-bit primary zygote, no secondary zygote. @@ -66,6 +79,9 @@ class OdrConfig final { bool compilation_os_mode_ = false; bool minimal_ = false; + // The current values of system properties listed in `kSystemProperties`. + std::unordered_map<std::string, std::string> system_properties_; + // Staging directory for artifacts. The directory must exist and will be automatically removed // after compilation. If empty, use the default directory. std::string staging_dir_; @@ -148,6 +164,9 @@ class OdrConfig final { } bool GetCompilationOsMode() const { return compilation_os_mode_; } bool GetMinimal() const { return minimal_; } + const std::unordered_map<std::string, std::string>& GetSystemProperties() const { + return system_properties_; + } void SetApexInfoListFile(const std::string& file_path) { apex_info_list_file_ = file_path; } void SetArtBinDir(const std::string& art_bin_dir) { art_bin_dir_ = art_bin_dir; } @@ -199,6 +218,10 @@ class OdrConfig final { void SetMinimal(bool value) { minimal_ = value; } + std::unordered_map<std::string, std::string>* MutableSystemProperties() { + return &system_properties_; + } + private: // Returns a pair for the possible instruction sets for the configured instruction set // architecture. The first item is the 32-bit architecture and the second item is the 64-bit diff --git a/odrefresh/odrefresh.cc b/odrefresh/odrefresh.cc index 1d12b94fce..70cf48e918 100644 --- a/odrefresh/odrefresh.cc +++ b/odrefresh/odrefresh.cc @@ -648,6 +648,11 @@ Result<void> OnDeviceRefresh::WriteCacheInfo() const { return Errorf("Could not create directory {}", QuotePath(dir_name)); } + std::vector<art_apex::KeyValuePair> system_properties; + for (const auto& [key, value] : config_.GetSystemProperties()) { + system_properties.emplace_back(key, value); + } + std::optional<std::vector<apex::ApexInfo>> apex_info_list = GetApexInfoList(); if (!apex_info_list.has_value()) { return Errorf("Could not update {}: no APEX info", QuotePath(cache_info_filename_)); @@ -685,15 +690,16 @@ Result<void> OnDeviceRefresh::WriteCacheInfo() const { return Errorf("Cannot open {} for writing.", QuotePath(cache_info_filename_)); } - art_apex::CacheInfo info( + std::unique_ptr<art_apex::CacheInfo> info(new art_apex::CacheInfo( + {art_apex::KeyValuePairList(system_properties)}, {art_module_info}, {art_apex::ModuleInfoList(module_info_list)}, {art_apex::Classpath(bcp_components.value())}, {art_apex::Classpath(bcp_compilable_components.value())}, {art_apex::SystemServerComponents(system_server_components.value())}, - config_.GetCompilationOsMode() ? std::make_optional(true) : std::nullopt); + config_.GetCompilationOsMode() ? std::make_optional(true) : std::nullopt)); - art_apex::write(out, info); + art_apex::write(out, *info); out.close(); if (out.fail()) { return Errorf("Cannot write to {}", QuotePath(cache_info_filename_)); @@ -836,16 +842,106 @@ WARN_UNUSED bool OnDeviceRefresh::SystemServerArtifactsExist( return jars_missing_artifacts->empty(); } +WARN_UNUSED bool OnDeviceRefresh::CheckSystemPropertiesAreDefault() const { + const std::unordered_map<std::string, std::string>& system_properties = + config_.GetSystemProperties(); + + for (const SystemPropertyConfig& system_property_config : *kSystemProperties.get()) { + auto property = system_properties.find(system_property_config.name); + DCHECK(property != system_properties.end()); + + if (property->second != system_property_config.default_value) { + LOG(INFO) << "System property " << system_property_config.name << " has a non-default value (" + << property->second << ")."; + return false; + } + } + + return true; +} + +WARN_UNUSED bool OnDeviceRefresh::CheckSystemPropertiesHaveNotChanged( + const art_apex::CacheInfo& cache_info) const { + std::unordered_map<std::string, std::string> cached_system_properties; + const art_apex::KeyValuePairList* list = cache_info.getFirstSystemProperties(); + if (list == nullptr) { + // This should never happen. We have already checked the ART module version, and the cache + // info is generated by the latest version of the ART module if it exists. + LOG(ERROR) << "Missing system properties from cache-info."; + return false; + } + + for (const art_apex::KeyValuePair& pair : list->getItem()) { + cached_system_properties[pair.getK()] = pair.getV(); + } + + const std::unordered_map<std::string, std::string>& system_properties = + config_.GetSystemProperties(); + + for (const SystemPropertyConfig& system_property_config : *kSystemProperties.get()) { + auto property = system_properties.find(system_property_config.name); + DCHECK(property != system_properties.end()); + + auto cached_property = cached_system_properties.find(system_property_config.name); + if (cached_property == cached_system_properties.end()) { + // This should never happen. We have already checked the ART module version, and the cache + // info is generated by the latest version of the ART module if it exists. + LOG(ERROR) << "Missing system property from cache-info (" << system_property_config.name + << ")"; + return false; + } + + if (property->second != cached_property->second) { + LOG(INFO) << "System property " << system_property_config.name + << " value changed (before: " << cached_property->second + << ", now: " << property->second << ")."; + return false; + } + } + + return true; +} + +WARN_UNUSED bool OnDeviceRefresh::BootClasspathArtifactsOnSystemUsable( + const apex::ApexInfo& art_apex_info) const { + if (!art_apex_info.getIsFactory()) { + return false; + } + LOG(INFO) << "Factory ART APEX mounted."; + + if (!CheckSystemPropertiesAreDefault()) { + return false; + } + LOG(INFO) << "System properties are set to default values."; + + return true; +} + +WARN_UNUSED bool OnDeviceRefresh::SystemServerArtifactsOnSystemUsable( + const std::vector<apex::ApexInfo>& apex_info_list) const { + if (std::any_of(apex_info_list.begin(), + apex_info_list.end(), + [](const apex::ApexInfo& apex_info) { return !apex_info.getIsFactory(); })) { + return false; + } + LOG(INFO) << "Factory APEXes mounted."; + + if (!CheckSystemPropertiesAreDefault()) { + return false; + } + LOG(INFO) << "System properties are set to default values."; + + return true; +} + WARN_UNUSED bool OnDeviceRefresh::CheckBootClasspathArtifactsAreUpToDate( OdrMetrics& metrics, const InstructionSet isa, const apex::ApexInfo& art_apex_info, const std::optional<art_apex::CacheInfo>& cache_info, /*out*/ std::vector<std::string>* checked_artifacts) const { - if (art_apex_info.getIsFactory()) { - LOG(INFO) << "Factory ART APEX mounted."; - - // ART is not updated, so we can use the artifacts on /system. Check if they exist. + if (BootClasspathArtifactsOnSystemUsable(art_apex_info)) { + // We can use the artifacts on /system. Check if they exist. std::string error_msg; if (BootClasspathArtifactsExist(/*on_system=*/true, /*minimal=*/false, isa, &error_msg)) { return true; @@ -900,6 +996,14 @@ WARN_UNUSED bool OnDeviceRefresh::CheckBootClasspathArtifactsAreUpToDate( return false; } + if (!CheckSystemPropertiesHaveNotChanged(cache_info.value())) { + // We don't have a trigger kind for system property changes. For now, we reuse + // `kApexVersionMismatch` as it implies the expected behavior: re-compile regardless of the last + // compilation attempt. + metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch); + return false; + } + // Check boot class components. // // This checks the size and checksums of odrefresh compilable files on the DEX2OATBOOTCLASSPATH @@ -961,12 +1065,8 @@ bool OnDeviceRefresh::CheckSystemServerArtifactsAreUpToDate( std::set<std::string> jars_missing_artifacts_on_system; bool artifacts_on_system_up_to_date = false; - if (std::all_of(apex_info_list.begin(), - apex_info_list.end(), - [](const apex::ApexInfo& apex_info) { return apex_info.getIsFactory(); })) { - LOG(INFO) << "Factory APEXes mounted."; - - // APEXes are not updated, so we can use the artifacts on /system. Check if they exist. + if (SystemServerArtifactsOnSystemUsable(apex_info_list)) { + // We can use the artifacts on /system. Check if they exist. std::string error_msg; if (SystemServerArtifactsExist( /*on_system=*/true, &error_msg, &jars_missing_artifacts_on_system)) { @@ -1052,6 +1152,14 @@ bool OnDeviceRefresh::CheckSystemServerArtifactsAreUpToDate( } } + if (!CheckSystemPropertiesHaveNotChanged(cache_info.value())) { + // We don't have a trigger kind for system property changes. For now, we reuse + // `kApexVersionMismatch` as it implies the expected behavior: re-compile regardless of the last + // compilation attempt. + metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch); + return false; + } + // Check system server components. // // This checks the size and checksums of odrefresh compilable files on the diff --git a/odrefresh/odrefresh.h b/odrefresh/odrefresh.h index 58da57a1cd..9dd4009016 100644 --- a/odrefresh/odrefresh.h +++ b/odrefresh/odrefresh.h @@ -140,6 +140,25 @@ class OnDeviceRefresh final { /*out*/ std::set<std::string>* jars_missing_artifacts, /*out*/ std::vector<std::string>* checked_artifacts = nullptr) const; + // Returns true if all of the system properties listed in `kSystemProperties` are set to the + // default values. + WARN_UNUSED bool CheckSystemPropertiesAreDefault() const; + + // Returns true if none of the system properties listed in `kSystemProperties` has changed since + // the last compilation. + WARN_UNUSED bool CheckSystemPropertiesHaveNotChanged( + const com::android::art::CacheInfo& cache_info) const; + + // Returns true if boot classpath artifacts on /system are usable if they exist. Note that this + // function does not check file existence. + WARN_UNUSED bool BootClasspathArtifactsOnSystemUsable( + const com::android::apex::ApexInfo& art_apex_info) const; + + // Returns true if system_server artifacts on /system are usable if they exist. Note that this + // function does not check file existence. + WARN_UNUSED bool SystemServerArtifactsOnSystemUsable( + const std::vector<com::android::apex::ApexInfo>& apex_info_list) const; + // Checks whether all boot classpath artifacts are up to date. Returns true if all are present, // false otherwise. // If `checked_artifacts` is present, adds checked artifacts to `checked_artifacts`. diff --git a/odrefresh/odrefresh_main.cc b/odrefresh/odrefresh_main.cc index ffce81c692..f8cc0b15dc 100644 --- a/odrefresh/odrefresh_main.cc +++ b/odrefresh/odrefresh_main.cc @@ -18,6 +18,7 @@ #include <string> #include <string_view> +#include <unordered_map> #include "android-base/properties.h" #include "android-base/stringprintf.h" @@ -34,8 +35,10 @@ namespace { +using ::android::base::GetProperty; using ::art::odrefresh::CompilationOptions; using ::art::odrefresh::ExitCode; +using ::art::odrefresh::kSystemProperties; using ::art::odrefresh::OdrCompilationLog; using ::art::odrefresh::OdrConfig; using ::art::odrefresh::OdrMetrics; @@ -43,6 +46,7 @@ using ::art::odrefresh::OnDeviceRefresh; using ::art::odrefresh::QuotePath; using ::art::odrefresh::ShouldDisablePartialCompilation; using ::art::odrefresh::ShouldDisableRefresh; +using ::art::odrefresh::SystemPropertyConfig; using ::art::odrefresh::ZygoteKind; void UsageMsgV(const char* fmt, va_list ap) { @@ -156,7 +160,7 @@ int InitializeConfig(int argc, char** argv, OdrConfig* config) { if (zygote.empty()) { // Use ro.zygote by default, if not overridden by --zygote-arch flag. - zygote = android::base::GetProperty("ro.zygote", {}); + zygote = GetProperty("ro.zygote", {}); } ZygoteKind zygote_kind; if (!ParseZygoteKind(zygote.c_str(), &zygote_kind)) { @@ -165,25 +169,30 @@ int InitializeConfig(int argc, char** argv, OdrConfig* config) { config->SetZygoteKind(zygote_kind); if (config->GetSystemServerCompilerFilter().empty()) { - std::string filter = - android::base::GetProperty("dalvik.vm.systemservercompilerfilter", "speed"); + std::string filter = GetProperty("dalvik.vm.systemservercompilerfilter", "speed"); config->SetSystemServerCompilerFilter(filter); } if (!config->HasPartialCompilation() && ShouldDisablePartialCompilation( - android::base::GetProperty("ro.build.version.security_patch", /*default_value=*/""))) { + GetProperty("ro.build.version.security_patch", /*default_value=*/""))) { config->SetPartialCompilation(false); } - if (ShouldDisableRefresh( - android::base::GetProperty("ro.build.version.sdk", /*default_value=*/""))) { + if (ShouldDisableRefresh(GetProperty("ro.build.version.sdk", /*default_value=*/""))) { config->SetRefresh(false); } return n; } +void GetSystemProperties(std::unordered_map<std::string, std::string>* system_properties) { + for (const SystemPropertyConfig& system_property_config : *kSystemProperties.get()) { + (*system_properties)[system_property_config.name] = + GetProperty(system_property_config.name, system_property_config.default_value); + } +} + NO_RETURN void UsageHelp(const char* argv0) { std::string name(android::base::Basename(argv0)); UsageMsg("Usage: %s [OPTION...] ACTION", name.c_str()); @@ -236,6 +245,7 @@ int main(int argc, char** argv) { if (argc != 1) { ArgumentError("Expected 1 argument, but have %d.", argc); } + GetSystemProperties(config.MutableSystemProperties()); OdrMetrics metrics(config.GetArtifactDirectory()); OnDeviceRefresh odr(config); @@ -275,6 +285,6 @@ int main(int argc, char** argv) { } else if (action == "--help") { UsageHelp(argv[0]); } else { - ArgumentError("Unknown argument: ", action); + ArgumentError("Unknown argument: %s", action.data()); } } diff --git a/odrefresh/schema/current.txt b/odrefresh/schema/current.txt index 958f2a060a..c45870b6e9 100644 --- a/odrefresh/schema/current.txt +++ b/odrefresh/schema/current.txt @@ -8,12 +8,14 @@ package com.android.art { method public boolean getCompilationOsMode(); method public com.android.art.Classpath getDex2oatBootClasspath(); method public com.android.art.ModuleInfoList getModuleInfoList(); + method public com.android.art.KeyValuePairList getSystemProperties(); method public com.android.art.SystemServerComponents getSystemServerComponents(); method public void setArtModuleInfo(com.android.art.ModuleInfo); method public void setBootClasspath(com.android.art.Classpath); method public void setCompilationOsMode(boolean); method public void setDex2oatBootClasspath(com.android.art.Classpath); method public void setModuleInfoList(com.android.art.ModuleInfoList); + method public void setSystemProperties(com.android.art.KeyValuePairList); method public void setSystemServerComponents(com.android.art.SystemServerComponents); } @@ -33,6 +35,20 @@ package com.android.art { method public void setSize(java.math.BigInteger); } + public class KeyValuePair { + ctor public KeyValuePair(); + method public String getK(); + method public String getV(); + method public void setK(String); + method public void setV(String); + } + + public class KeyValuePairList { + ctor public KeyValuePairList(); + method public com.android.art.KeyValuePair getItem(); + method public void setItem(com.android.art.KeyValuePair); + } + public class ModuleInfo { ctor public ModuleInfo(); method public long getLastUpdateMillis(); diff --git a/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java b/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java index d27902f140..1b852d7ac2 100644 --- a/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java +++ b/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java @@ -144,6 +144,60 @@ public class OdrefreshHostTest extends BaseHostJUnit4Test { } @Test + public void verifyEnableUffdGcChangeTriggersCompilation() throws Exception { + try { + // Disable phenotype flag syncing. + getDevice().executeShellV2Command( + "device_config set_sync_disabled_for_tests until_reboot"); + + // Simulate that the phenotype flag is set to the default value. + getDevice().executeShellV2Command( + "device_config put runtime_native_boot enable_uffd_gc false"); + + long timeMs = mTestUtils.getCurrentTimeMs(); + getDevice().executeShellV2Command(ODREFRESH_COMMAND); + + // Artifacts should not be re-compiled. + assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs); + assertArtifactsNotModifiedAfter(getSystemServerArtifacts(), timeMs); + + // Simulate that the phenotype flag is set to true. + getDevice().executeShellV2Command( + "device_config put runtime_native_boot enable_uffd_gc true"); + + timeMs = mTestUtils.getCurrentTimeMs(); + getDevice().executeShellV2Command(ODREFRESH_COMMAND); + + // Artifacts should be re-compiled. + assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs); + assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs); + + // Run odrefresh again with the flag unchanged. + timeMs = mTestUtils.getCurrentTimeMs(); + getDevice().executeShellV2Command(ODREFRESH_COMMAND); + + // Artifacts should not be re-compiled. + assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs); + assertArtifactsNotModifiedAfter(getSystemServerArtifacts(), timeMs); + + // Simulate that the phenotype flag is set to false. + getDevice().executeShellV2Command( + "device_config put runtime_native_boot enable_uffd_gc false"); + + timeMs = mTestUtils.getCurrentTimeMs(); + getDevice().executeShellV2Command(ODREFRESH_COMMAND); + + // Artifacts should be re-compiled. + assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs); + assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs); + } finally { + getDevice().executeShellV2Command("device_config set_sync_disabled_for_tests none"); + getDevice().executeShellV2Command( + "device_config delete runtime_native_boot enable_uffd_gc"); + } + } + + @Test public void verifyNoCompilationWhenCacheIsGood() throws Exception { mTestUtils.removeCompilationLogToAvoidBackoff(); long timeMs = mTestUtils.getCurrentTimeMs(); |