Skip to content

Commit

Permalink
Re-compile on userfaultfd phenotype flag change.
Browse files Browse the repository at this point in the history
After this change, odrefresh re-compiles everything when the phenotype
flag `runtime_native_boot.enable_uffd_gc` changes. It writes the value
of the flag to cache-info.xml for change detection.

According to go/platform-experiment-namespaces#namespace-types, the
phenotype flag is back by a persistent system property. Therefore, we
can directly read the flag from the system property instead of depending
on the `server_configurable_flags` library. This behavior is consistent
with the existing ART code (particularly, `art::Flag`), which reads
other phenotype flags.

Bug: 231298279
Test: atest odsign_e2e_tests_full
Ignore-AOSP-First: Merge conflict. Will cherry-pick later.
Change-Id: I02b70569ecd96d4ded6d2d3be22c34b2c6a4c5b4
  • Loading branch information
jiakaiz-g authored and TreeHugger Robot committed May 6, 2022
1 parent 36c0cb0 commit 45d0881
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 20 deletions.
14 changes: 14 additions & 0 deletions odrefresh/CacheInfo.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand All @@ -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>
Expand Down
23 changes: 23 additions & 0 deletions odrefresh/odr_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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.
Expand Down Expand Up @@ -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_;
Expand Down Expand Up @@ -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; }
Expand Down Expand Up @@ -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
Expand Down
134 changes: 121 additions & 13 deletions odrefresh/odrefresh.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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_));
Expand Down Expand Up @@ -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_));
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions odrefresh/odrefresh.h
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
24 changes: 17 additions & 7 deletions odrefresh/odrefresh_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include <string>
#include <string_view>
#include <unordered_map>

#include "android-base/properties.h"
#include "android-base/stringprintf.h"
Expand All @@ -34,15 +35,18 @@

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;
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) {
Expand Down Expand Up @@ -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)) {
Expand All @@ -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());
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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());
}
}
Loading

0 comments on commit 45d0881

Please sign in to comment.