summaryrefslogtreecommitdiff
path: root/odrefresh/odrefresh.cc
diff options
context:
space:
mode:
Diffstat (limited to 'odrefresh/odrefresh.cc')
-rw-r--r--odrefresh/odrefresh.cc1617
1 files changed, 934 insertions, 683 deletions
diff --git a/odrefresh/odrefresh.cc b/odrefresh/odrefresh.cc
index f829bb8ee1..7bbc922a8c 100644
--- a/odrefresh/odrefresh.cc
+++ b/odrefresh/odrefresh.cc
@@ -53,19 +53,23 @@
#include <utility>
#include <vector>
+#include "android-base/chrono_utils.h"
#include "android-base/file.h"
#include "android-base/logging.h"
#include "android-base/macros.h"
+#include "android-base/parsebool.h"
#include "android-base/parseint.h"
#include "android-base/properties.h"
#include "android-base/result.h"
#include "android-base/scopeguard.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "android-modules-utils/sdk_level.h"
#include "android/log.h"
#include "arch/instruction_set.h"
#include "base/file_utils.h"
#include "base/globals.h"
+#include "base/logging.h"
#include "base/macros.h"
#include "base/os.h"
#include "base/stl_util.h"
@@ -76,6 +80,8 @@
#include "dex/art_dex_file_loader.h"
#include "dexoptanalyzer.h"
#include "exec_utils.h"
+#include "fmt/format.h"
+#include "gc/collector/mark_compact.h"
#include "log/log.h"
#include "odr_artifacts.h"
#include "odr_common.h"
@@ -86,31 +92,54 @@
#include "odrefresh/odrefresh.h"
#include "palette/palette.h"
#include "palette/palette_types.h"
+#include "read_barrier_config.h"
namespace art {
namespace odrefresh {
+namespace {
+
namespace apex = com::android::apex;
namespace art_apex = com::android::art;
-using android::base::Result;
-
-namespace {
+using ::android::base::Basename;
+using ::android::base::Dirname;
+using ::android::base::GetProperty;
+using ::android::base::Join;
+using ::android::base::ParseBool;
+using ::android::base::ParseBoolResult;
+using ::android::base::ParseInt;
+using ::android::base::Result;
+using ::android::base::SetProperty;
+using ::android::base::Split;
+using ::android::base::StartsWith;
+using ::android::base::StringPrintf;
+using ::android::base::Timer;
+using ::android::modules::sdklevel::IsAtLeastU;
+
+using ::fmt::literals::operator""_format; // NOLINT
// Name of cache info file in the ART Apex artifact cache.
constexpr const char* kCacheInfoFile = "cache-info.xml";
// Maximum execution time for odrefresh from start to end.
-constexpr time_t kMaximumExecutionSeconds = 300;
+constexpr time_t kMaximumExecutionSeconds = 480;
// Maximum execution time for any child process spawned.
-constexpr time_t kMaxChildProcessSeconds = 90;
+constexpr time_t kMaxChildProcessSeconds = 120;
constexpr mode_t kFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
constexpr const char* kFirstBootImageBasename = "boot.art";
constexpr const char* kMinimalBootImageBasename = "boot_minimal.art";
+// The default compiler filter for primary boot image.
+constexpr const char* kPrimaryCompilerFilter = "speed-profile";
+
+// The compiler filter for boot image mainline extension. We don't have profiles for mainline BCP
+// jars, so we always use "verify".
+constexpr const char* kMainlineCompilerFilter = "verify";
+
void EraseFiles(const std::vector<std::unique_ptr<File>>& files) {
for (auto& file : files) {
file->Erase(/*unlink=*/true);
@@ -127,9 +156,9 @@ bool MoveOrEraseFiles(const std::vector<std::unique_ptr<File>>& files,
std::string_view output_directory_path) {
std::vector<std::unique_ptr<File>> output_files;
for (auto& file : files) {
- const std::string file_basename(android::base::Basename(file->GetPath()));
- const std::string output_file_path = Concatenate({output_directory_path, "/", file_basename});
- const std::string input_file_path = file->GetPath();
+ std::string file_basename(Basename(file->GetPath()));
+ std::string output_file_path = "{}/{}"_format(output_directory_path, file_basename);
+ std::string input_file_path = file->GetPath();
output_files.emplace_back(OS::CreateEmptyFileWriteOnly(output_file_path.c_str()));
if (output_files.back() == nullptr) {
@@ -147,7 +176,7 @@ bool MoveOrEraseFiles(const std::vector<std::unique_ptr<File>>& files,
return false;
}
- const size_t file_bytes = file->GetLength();
+ size_t file_bytes = file->GetLength();
if (!output_files.back()->Copy(file.get(), /*offset=*/0, file_bytes)) {
PLOG(ERROR) << "Failed to copy " << QuotePath(file->GetPath()) << " to "
<< QuotePath(output_file_path);
@@ -204,10 +233,12 @@ std::vector<art_apex::ModuleInfo> GenerateModuleInfoList(
return module_info_list;
}
-// Returns a rewritten path based on ANDROID_ROOT if the path starts with "/system/".
-std::string AndroidRootRewrite(const std::string& path) {
+// Returns a rewritten path based on environment variables for interesting paths.
+std::string RewriteParentDirectoryIfNeeded(const std::string& path) {
if (StartsWith(path, "/system/")) {
- return Concatenate({GetAndroidRoot(), path.substr(7)});
+ return GetAndroidRoot() + path.substr(7);
+ } else if (StartsWith(path, "/system_ext/")) {
+ return GetSystemExtRoot() + path.substr(11);
} else {
return path;
}
@@ -279,9 +310,8 @@ std::vector<T> GenerateComponents(
custom_generator) {
std::vector<T> components;
- ArtDexFileLoader loader;
for (const std::string& path : jars) {
- std::string actual_path = AndroidRootRewrite(path);
+ std::string actual_path = RewriteParentDirectoryIfNeeded(path);
struct stat sb;
if (stat(actual_path.c_str(), &sb) == -1) {
PLOG(ERROR) << "Failed to stat component: " << QuotePath(actual_path);
@@ -291,7 +321,8 @@ std::vector<T> GenerateComponents(
std::vector<uint32_t> checksums;
std::vector<std::string> dex_locations;
std::string error_msg;
- if (!loader.GetMultiDexChecksums(actual_path.c_str(), &checksums, &dex_locations, &error_msg)) {
+ if (!ArtDexFileLoader::GetMultiDexChecksums(
+ actual_path.c_str(), &checksums, &dex_locations, &error_msg)) {
LOG(ERROR) << "Failed to get multi-dex checksums: " << error_msg;
return {};
}
@@ -301,7 +332,7 @@ std::vector<T> GenerateComponents(
if (i != 0) {
oss << ';';
}
- oss << android::base::StringPrintf("%08x", checksums[i]);
+ oss << StringPrintf("%08x", checksums[i]);
}
const std::string checksum = oss.str();
@@ -370,31 +401,47 @@ void AddDex2OatCommonOptions(/*inout*/ std::vector<std::string>& args) {
}
bool IsCpuSetSpecValid(const std::string& cpu_set) {
- for (auto& str : android::base::Split(cpu_set, ",")) {
+ for (const std::string& str : Split(cpu_set, ",")) {
int id;
- if (!android::base::ParseInt(str, &id, 0)) {
+ if (!ParseInt(str, &id, 0)) {
return false;
}
}
return true;
}
-bool AddDex2OatConcurrencyArguments(/*inout*/ std::vector<std::string>& args) {
- std::string threads = android::base::GetProperty("dalvik.vm.boot-dex2oat-threads", "");
+Result<void> AddDex2OatConcurrencyArguments(/*inout*/ std::vector<std::string>& args,
+ bool is_compilation_os) {
+ std::string threads;
+ if (is_compilation_os) {
+ threads = GetProperty("dalvik.vm.background-dex2oat-threads", "");
+ if (threads.empty()) {
+ threads = GetProperty("dalvik.vm.dex2oat-threads", "");
+ }
+ } else {
+ threads = GetProperty("dalvik.vm.boot-dex2oat-threads", "");
+ }
if (!threads.empty()) {
args.push_back("-j" + threads);
}
- std::string cpu_set = android::base::GetProperty("dalvik.vm.boot-dex2oat-cpu-set", "");
- if (cpu_set.empty()) {
- return true;
+ std::string cpu_set;
+ if (is_compilation_os) {
+ cpu_set = GetProperty("dalvik.vm.background-dex2oat-cpu-set", "");
+ if (cpu_set.empty()) {
+ cpu_set = GetProperty("dalvik.vm.dex2oat-cpu-set", "");
+ }
+ } else {
+ cpu_set = GetProperty("dalvik.vm.boot-dex2oat-cpu-set", "");
}
- if (!IsCpuSetSpecValid(cpu_set)) {
- LOG(ERROR) << "Invalid CPU set spec: " << cpu_set;
- return false;
+ if (!cpu_set.empty()) {
+ if (!IsCpuSetSpecValid(cpu_set)) {
+ return Errorf("Invalid CPU set spec '{}'", cpu_set);
+ }
+ args.push_back("--cpu-set=" + cpu_set);
}
- args.push_back("--cpu-set=" + cpu_set);
- return true;
+
+ return {};
}
void AddDex2OatDebugInfo(/*inout*/ std::vector<std::string>& args) {
@@ -404,10 +451,11 @@ void AddDex2OatDebugInfo(/*inout*/ std::vector<std::string>& args) {
void AddDex2OatInstructionSet(/*inout*/ std::vector<std::string>& args, InstructionSet isa) {
const char* isa_str = GetInstructionSetString(isa);
- args.emplace_back(Concatenate({"--instruction-set=", isa_str}));
+ args.emplace_back(StringPrintf("--instruction-set=%s", isa_str));
}
-void AddDex2OatProfileAndCompilerFilter(
+// Returns true if any profile has been added.
+bool AddDex2OatProfile(
/*inout*/ std::vector<std::string>& args,
/*inout*/ std::vector<std::unique_ptr<File>>& output_files,
const std::vector<std::string>& profile_paths) {
@@ -415,22 +463,17 @@ void AddDex2OatProfileAndCompilerFilter(
for (auto& path : profile_paths) {
std::unique_ptr<File> profile_file(OS::OpenFileForReading(path.c_str()));
if (profile_file && profile_file->IsOpened()) {
- args.emplace_back(android::base::StringPrintf("--profile-file-fd=%d", profile_file->Fd()));
+ args.emplace_back(StringPrintf("--profile-file-fd=%d", profile_file->Fd()));
output_files.emplace_back(std::move(profile_file));
has_any_profile = true;
}
}
-
- if (has_any_profile) {
- args.emplace_back("--compiler-filter=speed-profile");
- } else {
- args.emplace_back("--compiler-filter=speed");
- }
+ return has_any_profile;
}
-bool AddBootClasspathFds(/*inout*/ std::vector<std::string>& args,
- /*inout*/ std::vector<std::unique_ptr<File>>& output_files,
- const std::vector<std::string>& bcp_jars) {
+Result<void> AddBootClasspathFds(/*inout*/ std::vector<std::string>& args,
+ /*inout*/ std::vector<std::unique_ptr<File>>& output_files,
+ const std::vector<std::string>& bcp_jars) {
std::vector<std::string> bcp_fds;
for (const std::string& jar : bcp_jars) {
// Special treatment for Compilation OS. JARs in staged APEX may not be visible to Android, and
@@ -439,26 +482,38 @@ bool AddBootClasspathFds(/*inout*/ std::vector<std::string>& args,
if (StartsWith(jar, "/apex/")) {
bcp_fds.emplace_back("-1");
} else {
- std::string actual_path = AndroidRootRewrite(jar);
+ std::string actual_path = RewriteParentDirectoryIfNeeded(jar);
std::unique_ptr<File> jar_file(OS::OpenFileForReading(actual_path.c_str()));
if (!jar_file || !jar_file->IsValid()) {
- LOG(ERROR) << "Failed to open a BCP jar " << actual_path;
- return false;
+ return Errorf("Failed to open a BCP jar '{}'", actual_path);
}
bcp_fds.push_back(std::to_string(jar_file->Fd()));
output_files.push_back(std::move(jar_file));
}
}
args.emplace_back("--runtime-arg");
- args.emplace_back(Concatenate({"-Xbootclasspathfds:", android::base::Join(bcp_fds, ':')}));
- return true;
+ args.emplace_back("-Xbootclasspathfds:" + Join(bcp_fds, ':'));
+ return {};
+}
+
+Result<void> AddCacheInfoFd(/*inout*/ std::vector<std::string>& args,
+ /*inout*/ std::vector<std::unique_ptr<File>>& readonly_files_raii,
+ const std::string& cache_info_filename) {
+ std::unique_ptr<File> cache_info_file(OS::OpenFileForReading(cache_info_filename.c_str()));
+ if (cache_info_file == nullptr) {
+ return ErrnoErrorf("Failed to open a cache info file '{}'", cache_info_file);
+ }
+
+ args.emplace_back("--cache-info-fd=" + std::to_string(cache_info_file->Fd()));
+ readonly_files_raii.push_back(std::move(cache_info_file));
+ return {};
}
std::string GetBootImageComponentBasename(const std::string& jar_path, bool is_first_jar) {
if (is_first_jar) {
return kFirstBootImageBasename;
}
- const std::string jar_name = android::base::Basename(jar_path);
+ std::string jar_name = Basename(jar_path);
return "boot-" + ReplaceFileExtension(jar_name, "art");
}
@@ -466,17 +521,27 @@ void AddCompiledBootClasspathFdsIfAny(
/*inout*/ std::vector<std::string>& args,
/*inout*/ std::vector<std::unique_ptr<File>>& output_files,
const std::vector<std::string>& bcp_jars,
- const InstructionSet isa,
- const std::string& artifact_dir) {
+ InstructionSet isa,
+ const std::vector<std::string>& boot_image_locations) {
std::vector<std::string> bcp_image_fds;
std::vector<std::string> bcp_oat_fds;
std::vector<std::string> bcp_vdex_fds;
std::vector<std::unique_ptr<File>> opened_files;
bool added_any = false;
+ std::string artifact_dir;
for (size_t i = 0; i < bcp_jars.size(); i++) {
const std::string& jar = bcp_jars[i];
- std::string image_path =
- artifact_dir + "/" + GetBootImageComponentBasename(jar, /*is_first_jar=*/i == 0);
+ std::string basename = GetBootImageComponentBasename(jar, /*is_first_jar=*/i == 0);
+ // If there is an entry in `boot_image_locations` for the current jar, update `artifact_dir` for
+ // the current jar and the subsequent jars.
+ for (const std::string& location : boot_image_locations) {
+ if (Basename(location) == basename) {
+ artifact_dir = Dirname(location);
+ break;
+ }
+ }
+ CHECK(!artifact_dir.empty());
+ std::string image_path = artifact_dir + "/" + basename;
image_path = GetSystemImageFilename(image_path.c_str(), isa);
std::unique_ptr<File> image_file(OS::OpenFileForReading(image_path.c_str()));
if (image_file && image_file->IsValid()) {
@@ -512,19 +577,16 @@ void AddCompiledBootClasspathFdsIfAny(
std::move(opened_files.begin(), opened_files.end(), std::back_inserter(output_files));
args.emplace_back("--runtime-arg");
- args.emplace_back(
- Concatenate({"-Xbootclasspathimagefds:", android::base::Join(bcp_image_fds, ':')}));
+ args.emplace_back("-Xbootclasspathimagefds:" + Join(bcp_image_fds, ':'));
args.emplace_back("--runtime-arg");
- args.emplace_back(
- Concatenate({"-Xbootclasspathoatfds:", android::base::Join(bcp_oat_fds, ':')}));
+ args.emplace_back("-Xbootclasspathoatfds:" + Join(bcp_oat_fds, ':'));
args.emplace_back("--runtime-arg");
- args.emplace_back(
- Concatenate({"-Xbootclasspathvdexfds:", android::base::Join(bcp_vdex_fds, ':')}));
+ args.emplace_back("-Xbootclasspathvdexfds:" + Join(bcp_vdex_fds, ':'));
}
}
std::string GetStagingLocation(const std::string& staging_dir, const std::string& path) {
- return Concatenate({staging_dir, "/", android::base::Basename(path)});
+ return staging_dir + "/" + Basename(path);
}
WARN_UNUSED bool CheckCompilationSpace() {
@@ -553,13 +615,59 @@ WARN_UNUSED bool CheckCompilationSpace() {
return true;
}
-std::string GetSystemBootImageDir() { return GetAndroidRoot() + "/framework"; }
+bool HasVettedDeviceSystemServerProfiles() {
+ // While system_server profiles were bundled on the device prior to U+, they were not used by
+ // default or rigorously tested, so we cannot vouch for their efficacy.
+ static const bool kDeviceIsAtLeastU = IsAtLeastU();
+ return kDeviceIsAtLeastU;
+}
} // namespace
+CompilationOptions CompilationOptions::CompileAll(const OnDeviceRefresh& odr) {
+ CompilationOptions options;
+ for (InstructionSet isa : odr.Config().GetBootClasspathIsas()) {
+ options.boot_images_to_generate_for_isas.emplace_back(
+ isa, BootImages{.primary_boot_image = true, .boot_image_mainline_extension = true});
+ }
+ options.system_server_jars_to_compile = odr.AllSystemServerJars();
+ return options;
+}
+
+int BootImages::Count() const {
+ int count = 0;
+ if (primary_boot_image) {
+ count++;
+ }
+ if (boot_image_mainline_extension) {
+ count++;
+ }
+ return count;
+}
+
+OdrMetrics::BcpCompilationType BootImages::GetTypeForMetrics() const {
+ if (primary_boot_image && boot_image_mainline_extension) {
+ return OdrMetrics::BcpCompilationType::kPrimaryAndMainline;
+ }
+ if (boot_image_mainline_extension) {
+ return OdrMetrics::BcpCompilationType::kMainline;
+ }
+ LOG(FATAL) << "Unexpected BCP compilation type";
+ UNREACHABLE();
+}
+
+int CompilationOptions::CompilationUnitCount() const {
+ int count = 0;
+ for (const auto& [isa, boot_images] : boot_images_to_generate_for_isas) {
+ count += boot_images.Count();
+ }
+ count += system_server_jars_to_compile.size();
+ return count;
+}
+
OnDeviceRefresh::OnDeviceRefresh(const OdrConfig& config)
: OnDeviceRefresh(config,
- Concatenate({config.GetArtifactDirectory(), "/", kCacheInfoFile}),
+ config.GetArtifactDirectory() + "/" + kCacheInfoFile,
std::make_unique<ExecUtils>()) {}
OnDeviceRefresh::OnDeviceRefresh(const OdrConfig& config,
@@ -569,19 +677,17 @@ OnDeviceRefresh::OnDeviceRefresh(const OdrConfig& config,
cache_info_filename_{cache_info_filename},
start_time_{time(nullptr)},
exec_utils_{std::move(exec_utils)} {
- for (const std::string& jar : android::base::Split(config_.GetDex2oatBootClasspath(), ":")) {
- // Updatable APEXes should not have DEX files in the DEX2OATBOOTCLASSPATH. At the time of
- // writing i18n is a non-updatable APEX and so does appear in the DEX2OATBOOTCLASSPATH.
- boot_classpath_compilable_jars_.emplace_back(jar);
- }
+ // Updatable APEXes should not have DEX files in the DEX2OATBOOTCLASSPATH. At the time of
+ // writing i18n is a non-updatable APEX and so does appear in the DEX2OATBOOTCLASSPATH.
+ dex2oat_boot_classpath_jars_ = Split(config_.GetDex2oatBootClasspath(), ":");
- all_systemserver_jars_ = android::base::Split(config_.GetSystemServerClasspath(), ":");
+ all_systemserver_jars_ = Split(config_.GetSystemServerClasspath(), ":");
systemserver_classpath_jars_ = {all_systemserver_jars_.begin(), all_systemserver_jars_.end()};
- boot_classpath_jars_ = android::base::Split(config_.GetBootClasspath(), ":");
+ boot_classpath_jars_ = Split(config_.GetBootClasspath(), ":");
std::string standalone_system_server_jars_str = config_.GetStandaloneSystemServerJars();
if (!standalone_system_server_jars_str.empty()) {
std::vector<std::string> standalone_systemserver_jars =
- android::base::Split(standalone_system_server_jars_str, ":");
+ Split(standalone_system_server_jars_str, ":");
std::move(standalone_systemserver_jars.begin(),
standalone_systemserver_jars.end(),
std::back_inserter(all_systemserver_jars_));
@@ -610,8 +716,8 @@ std::optional<std::vector<apex::ApexInfo>> OnDeviceRefresh::GetApexInfoList() co
std::unordered_set<std::string_view> relevant_apexes;
relevant_apexes.reserve(info_list->getApexInfo().size());
for (const std::vector<std::string>* jar_list :
- {&boot_classpath_compilable_jars_, &all_systemserver_jars_, &boot_classpath_jars_}) {
- for (auto& jar : *jar_list) {
+ {&all_systemserver_jars_, &boot_classpath_jars_}) {
+ for (const std::string& jar : *jar_list) {
std::string_view apex = ApexNameFromLocation(jar);
if (!apex.empty()) {
relevant_apexes.insert(apex);
@@ -632,8 +738,16 @@ std::optional<std::vector<apex::ApexInfo>> OnDeviceRefresh::GetApexInfoList() co
return filtered_info_list;
}
-std::optional<art_apex::CacheInfo> OnDeviceRefresh::ReadCacheInfo() const {
- return art_apex::read(cache_info_filename_.c_str());
+Result<art_apex::CacheInfo> OnDeviceRefresh::ReadCacheInfo() const {
+ std::optional<art_apex::CacheInfo> cache_info = art_apex::read(cache_info_filename_.c_str());
+ if (!cache_info.has_value()) {
+ if (errno != 0) {
+ return ErrnoErrorf("Failed to load {}", QuotePath(cache_info_filename_));
+ } else {
+ return Errorf("Failed to parse {}", QuotePath(cache_info_filename_));
+ }
+ }
+ return cache_info.value();
}
Result<void> OnDeviceRefresh::WriteCacheInfo() const {
@@ -643,7 +757,7 @@ Result<void> OnDeviceRefresh::WriteCacheInfo() const {
}
}
- const std::string dir_name = android::base::Dirname(cache_info_filename_);
+ std::string dir_name = Dirname(cache_info_filename_);
if (!EnsureDirectoryExists(dir_name)) {
return Errorf("Could not create directory {}", QuotePath(dir_name));
}
@@ -667,23 +781,11 @@ Result<void> OnDeviceRefresh::WriteCacheInfo() const {
std::vector<art_apex::ModuleInfo> module_info_list =
GenerateModuleInfoList(apex_info_list.value());
- std::optional<std::vector<art_apex::Component>> bcp_components =
- GenerateBootClasspathComponents();
- if (!bcp_components.has_value()) {
- return Errorf("No boot classpath components.");
- }
-
- std::optional<std::vector<art_apex::Component>> bcp_compilable_components =
- GenerateBootClasspathCompilableComponents();
- if (!bcp_compilable_components.has_value()) {
- return Errorf("No boot classpath compilable components.");
- }
-
- std::optional<std::vector<art_apex::SystemServerComponent>> system_server_components =
+ std::vector<art_apex::Component> bcp_components = GenerateBootClasspathComponents();
+ std::vector<art_apex::Component> dex2oat_bcp_components =
+ GenerateDex2oatBootClasspathComponents();
+ std::vector<art_apex::SystemServerComponent> system_server_components =
GenerateSystemServerComponents();
- if (!system_server_components.has_value()) {
- return Errorf("No system_server components.");
- }
std::ofstream out(cache_info_filename_.c_str());
if (out.fail()) {
@@ -694,9 +796,9 @@ Result<void> OnDeviceRefresh::WriteCacheInfo() const {
{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())},
+ {art_apex::Classpath(bcp_components)},
+ {art_apex::Classpath(dex2oat_bcp_components)},
+ {art_apex::SystemServerComponents(system_server_components)},
config_.GetCompilationOsMode() ? std::make_optional(true) : std::nullopt));
art_apex::write(out, *info);
@@ -713,16 +815,15 @@ static void ReportNextBootAnimationProgress(uint32_t current_compilation,
// We arbitrarily show progress until 90%, expecting that our compilations take a large chunk of
// boot time.
uint32_t value = (90 * current_compilation) / number_of_compilations;
- android::base::SetProperty("service.bootanim.progress", std::to_string(value));
+ SetProperty("service.bootanim.progress", std::to_string(value));
}
std::vector<art_apex::Component> OnDeviceRefresh::GenerateBootClasspathComponents() const {
return GenerateComponents(boot_classpath_jars_);
}
-std::vector<art_apex::Component> OnDeviceRefresh::GenerateBootClasspathCompilableComponents()
- const {
- return GenerateComponents(boot_classpath_compilable_jars_);
+std::vector<art_apex::Component> OnDeviceRefresh::GenerateDex2oatBootClasspathComponents() const {
+ return GenerateComponents(dex2oat_boot_classpath_jars_);
}
std::vector<art_apex::SystemServerComponent> OnDeviceRefresh::GenerateSystemServerComponents()
@@ -735,7 +836,43 @@ std::vector<art_apex::SystemServerComponent> OnDeviceRefresh::GenerateSystemServ
});
}
-std::string OnDeviceRefresh::GetBootImage(bool on_system, bool minimal) const {
+std::vector<std::string> OnDeviceRefresh::GetArtBcpJars() const {
+ std::string art_root = GetArtRoot() + "/";
+ std::vector<std::string> art_bcp_jars;
+ for (const std::string& jar : dex2oat_boot_classpath_jars_) {
+ if (StartsWith(jar, art_root)) {
+ art_bcp_jars.push_back(jar);
+ }
+ }
+ CHECK(!art_bcp_jars.empty());
+ return art_bcp_jars;
+}
+
+std::vector<std::string> OnDeviceRefresh::GetFrameworkBcpJars() const {
+ std::string art_root = GetArtRoot() + "/";
+ std::vector<std::string> framework_bcp_jars;
+ for (const std::string& jar : dex2oat_boot_classpath_jars_) {
+ if (!StartsWith(jar, art_root)) {
+ framework_bcp_jars.push_back(jar);
+ }
+ }
+ CHECK(!framework_bcp_jars.empty());
+ return framework_bcp_jars;
+}
+
+std::vector<std::string> OnDeviceRefresh::GetMainlineBcpJars() const {
+ // Elements in `dex2oat_boot_classpath_jars_` should be at the beginning of
+ // `boot_classpath_jars_`, followed by mainline BCP jars.
+ CHECK_LT(dex2oat_boot_classpath_jars_.size(), boot_classpath_jars_.size());
+ CHECK(std::equal(dex2oat_boot_classpath_jars_.begin(),
+ dex2oat_boot_classpath_jars_.end(),
+ boot_classpath_jars_.begin(),
+ boot_classpath_jars_.begin() + dex2oat_boot_classpath_jars_.size()));
+ return {boot_classpath_jars_.begin() + dex2oat_boot_classpath_jars_.size(),
+ boot_classpath_jars_.end()};
+}
+
+std::string OnDeviceRefresh::GetPrimaryBootImage(bool on_system, bool minimal) const {
DCHECK(!on_system || !minimal);
const char* basename = minimal ? kMinimalBootImageBasename : kFirstBootImageBasename;
if (on_system) {
@@ -747,28 +884,74 @@ std::string OnDeviceRefresh::GetBootImage(bool on_system, bool minimal) const {
}
}
-std::string OnDeviceRefresh::GetBootImagePath(bool on_system,
- bool minimal,
- const InstructionSet isa) const {
+std::string OnDeviceRefresh::GetPrimaryBootImagePath(bool on_system,
+ bool minimal,
+ InstructionSet isa) const {
// Typically "/data/misc/apexdata/com.android.art/dalvik-cache/<isa>/boot.art".
- return GetSystemImageFilename(GetBootImage(on_system, minimal).c_str(), isa);
+ return GetSystemImageFilename(GetPrimaryBootImage(on_system, minimal).c_str(), isa);
}
-std::string OnDeviceRefresh::GetSystemBootImageExtension() const {
- std::string art_root = GetArtRoot() + "/";
- // Find the first boot extension jar.
- auto it = std::find_if_not(
- boot_classpath_compilable_jars_.begin(),
- boot_classpath_compilable_jars_.end(),
- [&](const std::string& jar) { return android::base::StartsWith(jar, art_root); });
- CHECK(it != boot_classpath_compilable_jars_.end());
+std::string OnDeviceRefresh::GetSystemBootImageFrameworkExtension() const {
+ std::vector<std::string> framework_bcp_jars = GetFrameworkBcpJars();
+ std::string basename =
+ GetBootImageComponentBasename(framework_bcp_jars[0], /*is_first_jar=*/false);
// Typically "/system/framework/boot-framework.art".
- return GetSystemBootImageDir() + "/" + GetBootImageComponentBasename(*it, /*is_first_jar=*/false);
+ return "{}/framework/{}"_format(GetAndroidRoot(), basename);
}
-std::string OnDeviceRefresh::GetSystemBootImageExtensionPath(const InstructionSet isa) const {
+std::string OnDeviceRefresh::GetSystemBootImageFrameworkExtensionPath(InstructionSet isa) const {
// Typically "/system/framework/<isa>/boot-framework.art".
- return GetSystemImageFilename(GetSystemBootImageExtension().c_str(), isa);
+ return GetSystemImageFilename(GetSystemBootImageFrameworkExtension().c_str(), isa);
+}
+
+std::string OnDeviceRefresh::GetBootImageMainlineExtension(bool on_system) const {
+ std::vector<std::string> mainline_bcp_jars = GetMainlineBcpJars();
+ std::string basename =
+ GetBootImageComponentBasename(mainline_bcp_jars[0], /*is_first_jar=*/false);
+ if (on_system) {
+ // Typically "/system/framework/boot-framework-adservices.art".
+ return "{}/framework/{}"_format(GetAndroidRoot(), basename);
+ } else {
+ // Typically "/data/misc/apexdata/com.android.art/dalvik-cache/boot-framework-adservices.art".
+ return "{}/{}"_format(config_.GetArtifactDirectory(), basename);
+ }
+}
+
+std::string OnDeviceRefresh::GetBootImageMainlineExtensionPath(bool on_system,
+ InstructionSet isa) const {
+ // Typically
+ // "/data/misc/apexdata/com.android.art/dalvik-cache/<isa>/boot-framework-adservices.art".
+ return GetSystemImageFilename(GetBootImageMainlineExtension(on_system).c_str(), isa);
+}
+
+std::vector<std::string> OnDeviceRefresh::GetBestBootImages(InstructionSet isa,
+ bool include_mainline_extension) const {
+ std::vector<std::string> locations;
+ std::string unused_error_msg;
+ bool primary_on_data = false;
+ if (PrimaryBootImageExist(
+ /*on_system=*/false, /*minimal=*/false, isa, &unused_error_msg)) {
+ primary_on_data = true;
+ locations.push_back(GetPrimaryBootImage(/*on_system=*/false, /*minimal=*/false));
+ } else {
+ locations.push_back(GetPrimaryBootImage(/*on_system=*/true, /*minimal=*/false));
+ if (!IsAtLeastU()) {
+ // Prior to U, there was a framework extension.
+ locations.push_back(GetSystemBootImageFrameworkExtension());
+ }
+ }
+ if (include_mainline_extension) {
+ if (BootImageMainlineExtensionExist(/*on_system=*/false, isa, &unused_error_msg)) {
+ locations.push_back(GetBootImageMainlineExtension(/*on_system=*/false));
+ } else {
+ // If the primary boot image is on /data, it means we have regenerated all boot images, so the
+ // mainline extension must be on /data too.
+ CHECK(!primary_on_data)
+ << "Mainline extension not found while primary boot image is on /data";
+ locations.push_back(GetBootImageMainlineExtension(/*on_system=*/true));
+ }
+ }
+ return locations;
}
std::string OnDeviceRefresh::GetSystemServerImagePath(bool on_system,
@@ -777,15 +960,15 @@ std::string OnDeviceRefresh::GetSystemServerImagePath(bool on_system,
if (LocationIsOnApex(jar_path)) {
return GetSystemOdexFilenameForApex(jar_path, config_.GetSystemServerIsa());
}
- const std::string jar_name = android::base::Basename(jar_path);
- const std::string image_name = ReplaceFileExtension(jar_name, "art");
+ std::string jar_name = Basename(jar_path);
+ std::string image_name = ReplaceFileExtension(jar_name, "art");
const char* isa_str = GetInstructionSetString(config_.GetSystemServerIsa());
// Typically "/system/framework/oat/<isa>/services.art".
- return Concatenate({GetAndroidRoot(), "/framework/oat/", isa_str, "/", image_name});
+ return "{}/oat/{}/{}"_format(Dirname(jar_path), isa_str, image_name);
} else {
// Typically
// "/data/misc/apexdata/.../dalvik-cache/<isa>/system@framework@services.jar@classes.art".
- const std::string image = GetApexDataImage(jar_path.c_str());
+ const std::string image = GetApexDataImage(jar_path);
return GetSystemImageFilename(image.c_str(), config_.GetSystemServerIsa());
}
}
@@ -799,21 +982,21 @@ WARN_UNUSED bool OnDeviceRefresh::RemoveArtifactsDirectory() const {
return RemoveDirectory(config_.GetArtifactDirectory());
}
-WARN_UNUSED bool OnDeviceRefresh::BootClasspathArtifactsExist(
+WARN_UNUSED bool OnDeviceRefresh::PrimaryBootImageExist(
bool on_system,
bool minimal,
- const InstructionSet isa,
+ InstructionSet isa,
/*out*/ std::string* error_msg,
/*out*/ std::vector<std::string>* checked_artifacts) const {
- std::string path = GetBootImagePath(on_system, minimal, isa);
+ std::string path = GetPrimaryBootImagePath(on_system, minimal, isa);
OdrArtifacts artifacts = OdrArtifacts::ForBootImage(path);
if (!ArtifactsExist(artifacts, /*check_art_file=*/true, error_msg, checked_artifacts)) {
return false;
}
- // There is a split between the primary boot image and the extension on /system, so they need to
- // be checked separately. This does not apply to the boot image on /data.
- if (on_system) {
- std::string extension_path = GetSystemBootImageExtensionPath(isa);
+ // Prior to U, there was a split between the primary boot image and the extension on /system, so
+ // they need to be checked separately. This does not apply to the boot image on /data.
+ if (on_system && !IsAtLeastU()) {
+ std::string extension_path = GetSystemBootImageFrameworkExtensionPath(isa);
OdrArtifacts extension_artifacts = OdrArtifacts::ForBootImage(extension_path);
if (!ArtifactsExist(
extension_artifacts, /*check_art_file=*/true, error_msg, checked_artifacts)) {
@@ -823,7 +1006,17 @@ WARN_UNUSED bool OnDeviceRefresh::BootClasspathArtifactsExist(
return true;
}
-WARN_UNUSED bool OnDeviceRefresh::SystemServerArtifactsExist(
+WARN_UNUSED bool OnDeviceRefresh::BootImageMainlineExtensionExist(
+ bool on_system,
+ InstructionSet isa,
+ /*out*/ std::string* error_msg,
+ /*out*/ std::vector<std::string>* checked_artifacts) const {
+ std::string path = GetBootImageMainlineExtensionPath(on_system, isa);
+ OdrArtifacts artifacts = OdrArtifacts::ForBootImage(path);
+ return ArtifactsExist(artifacts, /*check_art_file=*/true, error_msg, checked_artifacts);
+}
+
+bool OnDeviceRefresh::SystemServerArtifactsExist(
bool on_system,
/*out*/ std::string* error_msg,
/*out*/ std::set<std::string>* jars_missing_artifacts,
@@ -907,106 +1100,125 @@ WARN_UNUSED bool OnDeviceRefresh::CheckSystemPropertiesHaveNotChanged(
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()) {
+WARN_UNUSED bool OnDeviceRefresh::CheckBuildUserfaultFdGc() const {
+ auto it = config_.GetSystemProperties().find("ro.dalvik.vm.enable_uffd_gc");
+ bool build_enable_uffd_gc = it != config_.GetSystemProperties().end() ?
+ ParseBool(it->second) == ParseBoolResult::kTrue :
+ false;
+ bool kernel_supports_uffd = KernelSupportsUffd();
+ if (build_enable_uffd_gc && !kernel_supports_uffd) {
+ // Normally, this should not happen. If this happens, the system image was probably built with a
+ // wrong PRODUCT_ENABLE_UFFD_GC flag.
+ LOG(WARNING) << "Userfaultfd GC check failed (build-time: {}, runtime: {})."_format(
+ build_enable_uffd_gc, kernel_supports_uffd);
return false;
}
- LOG(INFO) << "System properties are set to default values.";
-
return true;
}
-WARN_UNUSED bool OnDeviceRefresh::SystemServerArtifactsOnSystemUsable(
+WARN_UNUSED PreconditionCheckResult OnDeviceRefresh::CheckPreconditionForSystem(
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;
+ return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kApexVersionMismatch);
}
- LOG(INFO) << "System properties are set to default values.";
- return true;
-}
+ if (!CheckBuildUserfaultFdGc()) {
+ return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kApexVersionMismatch);
+ }
-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 (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;
- }
+ std::optional<apex::ApexInfo> art_apex_info = GetArtApexInfo(apex_info_list);
+ if (!art_apex_info.has_value()) {
+ // This should never happen, further up-to-date checks are not possible if it does.
+ LOG(ERROR) << "Could not get ART APEX info.";
+ return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kUnknown);
+ }
- LOG(INFO) << "Incomplete boot classpath artifacts on /system. " << error_msg;
- LOG(INFO) << "Checking cache.";
+ if (!art_apex_info->getIsFactory()) {
+ LOG(INFO) << "Updated ART APEX mounted";
+ return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kApexVersionMismatch);
}
- if (!cache_info.has_value()) {
- // If the cache info file does not exist, it usually means on-device compilation has not been
- // done before because the device was using the factory version of modules, or artifacts were
- // cleared because an updated version was uninstalled. Set the trigger to be
- // `kApexVersionMismatch` so that compilation will always be performed.
- PLOG(INFO) << "No prior cache-info file: " << QuotePath(cache_info_filename_);
- metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- return false;
+ if (std::any_of(apex_info_list.begin(),
+ apex_info_list.end(),
+ [](const apex::ApexInfo& apex_info) { return !apex_info.getIsFactory(); })) {
+ LOG(INFO) << "Updated APEXes mounted";
+ return PreconditionCheckResult::BootImageMainlineExtensionNotOk(
+ OdrMetrics::Trigger::kApexVersionMismatch);
}
- // Check whether the current cache ART module info differs from the current ART module info.
- const art_apex::ModuleInfo* cached_art_info = cache_info->getFirstArtModuleInfo();
+ return PreconditionCheckResult::AllOk();
+}
- if (cached_art_info == nullptr) {
- LOG(INFO) << "Missing ART APEX info from cache-info.";
- metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
+WARN_UNUSED static bool CheckModuleInfo(const art_apex::ModuleInfo& cached_info,
+ const apex::ApexInfo& current_info) {
+ if (cached_info.getVersionCode() != current_info.getVersionCode()) {
+ LOG(INFO) << "APEX ({}) version code mismatch (before: {}, now: {})"_format(
+ current_info.getModuleName(), cached_info.getVersionCode(), current_info.getVersionCode());
return false;
}
- if (cached_art_info->getVersionCode() != art_apex_info.getVersionCode()) {
- LOG(INFO) << "ART APEX version code mismatch (" << cached_art_info->getVersionCode()
- << " != " << art_apex_info.getVersionCode() << ").";
- metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
+ if (cached_info.getVersionName() != current_info.getVersionName()) {
+ LOG(INFO) << "APEX ({}) version name mismatch (before: {}, now: {})"_format(
+ current_info.getModuleName(), cached_info.getVersionName(), current_info.getVersionName());
return false;
}
- if (cached_art_info->getVersionName() != art_apex_info.getVersionName()) {
- LOG(INFO) << "ART APEX version name mismatch (" << cached_art_info->getVersionName()
- << " != " << art_apex_info.getVersionName() << ").";
- metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
+ // Check lastUpdateMillis for samegrade installs. If `cached_info` is missing the lastUpdateMillis
+ // field then it is not current with the schema used by this binary so treat it as a samegrade
+ // update. Otherwise check whether the lastUpdateMillis changed.
+ const int64_t cached_last_update_millis =
+ cached_info.hasLastUpdateMillis() ? cached_info.getLastUpdateMillis() : -1;
+ if (cached_last_update_millis != current_info.getLastUpdateMillis()) {
+ LOG(INFO) << "APEX ({}) last update time mismatch (before: {}, now: {})"_format(
+ current_info.getModuleName(),
+ cached_info.getLastUpdateMillis(),
+ current_info.getLastUpdateMillis());
return false;
}
- // Check lastUpdateMillis for samegrade installs. If `cached_art_info` is missing the
- // lastUpdateMillis field then it is not current with the schema used by this binary so treat
- // it as a samegrade update. Otherwise check whether the lastUpdateMillis changed.
- const int64_t cached_art_last_update_millis =
- cached_art_info->hasLastUpdateMillis() ? cached_art_info->getLastUpdateMillis() : -1;
- if (cached_art_last_update_millis != art_apex_info.getLastUpdateMillis()) {
- LOG(INFO) << "ART APEX last update time mismatch (" << cached_art_last_update_millis
- << " != " << art_apex_info.getLastUpdateMillis() << ").";
- metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- return false;
+ return true;
+}
+
+WARN_UNUSED PreconditionCheckResult OnDeviceRefresh::CheckPreconditionForData(
+ const std::vector<com::android::apex::ApexInfo>& apex_info_list) const {
+ Result<art_apex::CacheInfo> cache_info = ReadCacheInfo();
+ if (!cache_info.ok()) {
+ if (cache_info.error().code() == ENOENT) {
+ // If the cache info file does not exist, it usually means it's the first boot, or the
+ // dalvik-cache directory is cleared by odsign due to corrupted files. Set the trigger to be
+ // `kApexVersionMismatch` to force generate the cache info file and compile if necessary.
+ LOG(INFO) << "No prior cache-info file: " << QuotePath(cache_info_filename_);
+ } else {
+ // This should not happen unless odrefresh is updated to a new version that is not compatible
+ // with an old cache-info file. Further up-to-date checks are not possible if it does.
+ LOG(ERROR) << cache_info.error().message();
+ }
+ return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kApexVersionMismatch);
}
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;
+ return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kApexVersionMismatch);
+ }
+
+ // Check whether the current cache ART module info differs from the current ART module info.
+ const art_apex::ModuleInfo* cached_art_info = cache_info->getFirstArtModuleInfo();
+ if (cached_art_info == nullptr) {
+ LOG(ERROR) << "Missing ART APEX info from cache-info.";
+ return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kApexVersionMismatch);
+ }
+
+ std::optional<apex::ApexInfo> current_art_info = GetArtApexInfo(apex_info_list);
+ if (!current_art_info.has_value()) {
+ // This should never happen, further up-to-date checks are not possible if it does.
+ LOG(ERROR) << "Could not get ART APEX info.";
+ return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kUnknown);
+ }
+
+ if (!CheckModuleInfo(*cached_art_info, *current_art_info)) {
+ return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kApexVersionMismatch);
}
// Check boot class components.
@@ -1017,102 +1229,33 @@ WARN_UNUSED bool OnDeviceRefresh::CheckBootClasspathArtifactsAreUpToDate(
//
// The boot class components may change unexpectedly, for example an OTA could update
// framework.jar.
- const std::vector<art_apex::Component> expected_bcp_compilable_components =
- GenerateBootClasspathCompilableComponents();
- if (expected_bcp_compilable_components.size() != 0 &&
- (!cache_info->hasDex2oatBootClasspath() ||
- !cache_info->getFirstDex2oatBootClasspath()->hasComponent())) {
+ const std::vector<art_apex::Component> current_dex2oat_bcp_components =
+ GenerateDex2oatBootClasspathComponents();
+
+ const art_apex::Classpath* cached_dex2oat_bcp_components =
+ cache_info->getFirstDex2oatBootClasspath();
+ if (cached_dex2oat_bcp_components == nullptr) {
LOG(INFO) << "Missing Dex2oatBootClasspath components.";
- metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
- return false;
+ return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kApexVersionMismatch);
}
- const std::vector<art_apex::Component>& bcp_compilable_components =
- cache_info->getFirstDex2oatBootClasspath()->getComponent();
- Result<void> result =
- CheckComponents(expected_bcp_compilable_components, bcp_compilable_components);
+ Result<void> result = CheckComponents(current_dex2oat_bcp_components,
+ cached_dex2oat_bcp_components->getComponent());
if (!result.ok()) {
LOG(INFO) << "Dex2OatClasspath components mismatch: " << result.error();
- metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
- return false;
- }
-
- // Cache info looks good, check all compilation artifacts exist.
- std::string error_msg;
- if (!BootClasspathArtifactsExist(
- /*on_system=*/false, /*minimal=*/false, isa, &error_msg, checked_artifacts)) {
- LOG(INFO) << "Incomplete boot classpath artifacts. " << error_msg;
- metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
- // Add the minimal boot image to `checked_artifacts` if exists. This is to prevent the minimal
- // boot image from being deleted. It does not affect the return value because we should still
- // attempt to generate a full boot image even if the minimal one exists.
- if (BootClasspathArtifactsExist(
- /*on_system=*/false, /*minimal=*/true, isa, &error_msg, checked_artifacts)) {
- LOG(INFO) << "Found minimal boot classpath artifacts.";
- }
- return false;
- }
-
- return true;
-}
-
-bool OnDeviceRefresh::CheckSystemServerArtifactsAreUpToDate(
- OdrMetrics& metrics,
- const std::vector<apex::ApexInfo>& apex_info_list,
- const std::optional<art_apex::CacheInfo>& cache_info,
- /*out*/ std::set<std::string>* jars_to_compile,
- /*out*/ std::vector<std::string>* checked_artifacts) const {
- auto compile_all = [&, this]() {
- *jars_to_compile = AllSystemServerJars();
- return false;
- };
-
- std::set<std::string> jars_missing_artifacts_on_system;
- bool artifacts_on_system_up_to_date = false;
-
- 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)) {
- return true;
- }
-
- LOG(INFO) << "Incomplete system server artifacts on /system. " << error_msg;
- LOG(INFO) << "Checking cache.";
- artifacts_on_system_up_to_date = true;
- }
-
- if (!cache_info.has_value()) {
- // If the cache info file does not exist, it usually means on-device compilation has not been
- // done before because the device was using the factory version of modules, or artifacts were
- // cleared because an updated version was uninstalled. Set the trigger to be
- // `kApexVersionMismatch` so that compilation will always be performed.
- PLOG(INFO) << "No prior cache-info file: " << QuotePath(cache_info_filename_);
- metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- if (artifacts_on_system_up_to_date) {
- *jars_to_compile = jars_missing_artifacts_on_system;
- return false;
- }
- return compile_all();
+ return PreconditionCheckResult::NoneOk(OdrMetrics::Trigger::kDexFilesChanged);
}
// Check whether the current cached module info differs from the current module info.
const art_apex::ModuleInfoList* cached_module_info_list = cache_info->getFirstModuleInfoList();
-
if (cached_module_info_list == nullptr) {
- LOG(INFO) << "Missing APEX info list from cache-info.";
- metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- return compile_all();
+ LOG(ERROR) << "Missing APEX info list from cache-info.";
+ return PreconditionCheckResult::BootImageMainlineExtensionNotOk(
+ OdrMetrics::Trigger::kApexVersionMismatch);
}
std::unordered_map<std::string, const art_apex::ModuleInfo*> cached_module_info_map;
for (const art_apex::ModuleInfo& module_info : cached_module_info_list->getModuleInfo()) {
- if (!module_info.hasName()) {
- LOG(INFO) << "Unexpected module info from cache-info. Missing module name.";
- metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- return compile_all();
- }
cached_module_info_map[module_info.getName()] = &module_info;
}
@@ -1125,44 +1268,33 @@ bool OnDeviceRefresh::CheckSystemServerArtifactsAreUpToDate(
auto it = cached_module_info_map.find(apex_name);
if (it == cached_module_info_map.end()) {
LOG(INFO) << "Missing APEX info from cache-info (" << apex_name << ").";
- metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- return compile_all();
+ return PreconditionCheckResult::BootImageMainlineExtensionNotOk(
+ OdrMetrics::Trigger::kApexVersionMismatch);
}
const art_apex::ModuleInfo* cached_module_info = it->second;
-
- if (cached_module_info->getVersionCode() != current_apex_info.getVersionCode()) {
- LOG(INFO) << "APEX (" << apex_name << ") version code mismatch ("
- << cached_module_info->getVersionCode()
- << " != " << current_apex_info.getVersionCode() << ").";
- metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- return compile_all();
+ if (!CheckModuleInfo(*cached_module_info, current_apex_info)) {
+ return PreconditionCheckResult::BootImageMainlineExtensionNotOk(
+ OdrMetrics::Trigger::kApexVersionMismatch);
}
+ }
- if (cached_module_info->getVersionName() != current_apex_info.getVersionName()) {
- LOG(INFO) << "APEX (" << apex_name << ") version name mismatch ("
- << cached_module_info->getVersionName()
- << " != " << current_apex_info.getVersionName() << ").";
- metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- return compile_all();
- }
+ const std::vector<art_apex::Component> current_bcp_components = GenerateBootClasspathComponents();
- if (!cached_module_info->hasLastUpdateMillis() ||
- cached_module_info->getLastUpdateMillis() != current_apex_info.getLastUpdateMillis()) {
- LOG(INFO) << "APEX (" << apex_name << ") last update time mismatch ("
- << cached_module_info->getLastUpdateMillis()
- << " != " << current_apex_info.getLastUpdateMillis() << ").";
- metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- return compile_all();
- }
+ const art_apex::Classpath* cached_bcp_components = cache_info->getFirstBootClasspath();
+ if (cached_bcp_components == nullptr) {
+ LOG(INFO) << "Missing BootClasspath components.";
+ return PreconditionCheckResult::BootImageMainlineExtensionNotOk(
+ OdrMetrics::Trigger::kApexVersionMismatch);
}
- 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;
+ result = CheckComponents(current_bcp_components, cached_bcp_components->getComponent());
+ if (!result.ok()) {
+ LOG(INFO) << "BootClasspath components mismatch: " << result.error();
+ // Boot classpath components can be dependencies of system_server components, so system_server
+ // components need to be recompiled if boot classpath components are changed.
+ return PreconditionCheckResult::BootImageMainlineExtensionNotOk(
+ OdrMetrics::Trigger::kDexFilesChanged);
}
// Check system server components.
@@ -1174,78 +1306,166 @@ bool OnDeviceRefresh::CheckSystemServerArtifactsAreUpToDate(
//
// The system_server components may change unexpectedly, for example an OTA could update
// services.jar.
- const std::vector<art_apex::SystemServerComponent> expected_system_server_components =
+ const std::vector<art_apex::SystemServerComponent> current_system_server_components =
GenerateSystemServerComponents();
- if (expected_system_server_components.size() != 0 &&
- (!cache_info->hasSystemServerComponents() ||
- !cache_info->getFirstSystemServerComponents()->hasComponent())) {
+
+ const art_apex::SystemServerComponents* cached_system_server_components =
+ cache_info->getFirstSystemServerComponents();
+ if (cached_system_server_components == nullptr) {
LOG(INFO) << "Missing SystemServerComponents.";
- metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
- return compile_all();
+ return PreconditionCheckResult::SystemServerNotOk(OdrMetrics::Trigger::kApexVersionMismatch);
}
- const std::vector<art_apex::SystemServerComponent>& system_server_components =
- cache_info->getFirstSystemServerComponents()->getComponent();
- Result<void> result =
- CheckSystemServerComponents(expected_system_server_components, system_server_components);
+ result = CheckSystemServerComponents(current_system_server_components,
+ cached_system_server_components->getComponent());
if (!result.ok()) {
LOG(INFO) << "SystemServerComponents mismatch: " << result.error();
- metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
- return compile_all();
+ return PreconditionCheckResult::SystemServerNotOk(OdrMetrics::Trigger::kDexFilesChanged);
}
- const std::vector<art_apex::Component> expected_bcp_components =
- GenerateBootClasspathComponents();
- if (expected_bcp_components.size() != 0 &&
- (!cache_info->hasBootClasspath() || !cache_info->getFirstBootClasspath()->hasComponent())) {
- LOG(INFO) << "Missing BootClasspath components.";
- metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
- return false;
+ return PreconditionCheckResult::AllOk();
+}
+
+WARN_UNUSED BootImages OnDeviceRefresh::CheckBootClasspathArtifactsAreUpToDate(
+ OdrMetrics& metrics,
+ InstructionSet isa,
+ const PreconditionCheckResult& system_result,
+ const PreconditionCheckResult& data_result,
+ /*out*/ std::vector<std::string>* checked_artifacts) const {
+ const char* isa_str = GetInstructionSetString(isa);
+
+ BootImages boot_images_on_system{.primary_boot_image = false,
+ .boot_image_mainline_extension = false};
+ if (system_result.IsPrimaryBootImageOk()) {
+ // We can use the artifacts on /system. Check if they exist.
+ std::string error_msg;
+ if (PrimaryBootImageExist(/*on_system=*/true, /*minimal=*/false, isa, &error_msg)) {
+ boot_images_on_system.primary_boot_image = true;
+ } else {
+ LOG(INFO) << "Incomplete primary boot image or framework extension on /system: " << error_msg;
+ }
}
- const std::vector<art_apex::Component>& bcp_components =
- cache_info->getFirstBootClasspath()->getComponent();
- result = CheckComponents(expected_bcp_components, bcp_components);
- if (!result.ok()) {
- LOG(INFO) << "BootClasspath components mismatch: " << result.error();
- metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
- // Boot classpath components can be dependencies of system_server components, so system_server
- // components need to be recompiled if boot classpath components are changed.
- return compile_all();
+ if (boot_images_on_system.primary_boot_image && system_result.IsBootImageMainlineExtensionOk()) {
+ std::string error_msg;
+ if (BootImageMainlineExtensionExist(/*on_system=*/true, isa, &error_msg)) {
+ boot_images_on_system.boot_image_mainline_extension = true;
+ } else {
+ LOG(INFO) << "Incomplete boot image mainline extension on /system: " << error_msg;
+ }
}
- std::string error_msg;
- std::set<std::string> jars_missing_artifacts_on_data;
- if (!SystemServerArtifactsExist(
- /*on_system=*/false, &error_msg, &jars_missing_artifacts_on_data, checked_artifacts)) {
- if (artifacts_on_system_up_to_date) {
- // Check if the remaining system_server artifacts are on /data.
- std::set_intersection(jars_missing_artifacts_on_system.begin(),
- jars_missing_artifacts_on_system.end(),
- jars_missing_artifacts_on_data.begin(),
- jars_missing_artifacts_on_data.end(),
- std::inserter(*jars_to_compile, jars_to_compile->end()));
- if (!jars_to_compile->empty()) {
- LOG(INFO) << "Incomplete system_server artifacts on /data. " << error_msg;
+ if (boot_images_on_system.Count() == BootImages::kMaxCount) {
+ LOG(INFO) << "Boot images on /system OK ({})"_format(isa_str);
+ // Nothing to compile.
+ return BootImages{.primary_boot_image = false, .boot_image_mainline_extension = false};
+ }
+
+ LOG(INFO) << "Checking boot images /data ({})"_format(isa_str);
+ BootImages boot_images_on_data{.primary_boot_image = false,
+ .boot_image_mainline_extension = false};
+
+ if (data_result.IsPrimaryBootImageOk()) {
+ std::string error_msg;
+ if (PrimaryBootImageExist(
+ /*on_system=*/false, /*minimal=*/false, isa, &error_msg, checked_artifacts)) {
+ boot_images_on_data.primary_boot_image = true;
+ } else {
+ LOG(INFO) << "Incomplete primary boot image on /data: " << error_msg;
+ metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
+ // Add the minimal boot image to `checked_artifacts` if exists. This is to prevent the minimal
+ // boot image from being deleted. It does not affect the return value because we should still
+ // attempt to generate a full boot image even if the minimal one exists.
+ if (PrimaryBootImageExist(
+ /*on_system=*/false, /*minimal=*/true, isa, &error_msg, checked_artifacts)) {
+ LOG(INFO) << "Found minimal primary boot image ({})"_format(isa_str);
+ }
+ }
+ } else {
+ metrics.SetTrigger(data_result.GetTrigger());
+ }
+
+ if (boot_images_on_data.primary_boot_image) {
+ if (data_result.IsBootImageMainlineExtensionOk()) {
+ std::string error_msg;
+ if (BootImageMainlineExtensionExist(
+ /*on_system=*/false, isa, &error_msg, checked_artifacts)) {
+ boot_images_on_data.boot_image_mainline_extension = true;
+ } else {
+ LOG(INFO) << "Incomplete boot image mainline extension on /data: " << error_msg;
metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
- return false;
}
+ } else {
+ metrics.SetTrigger(data_result.GetTrigger());
+ }
+ }
- LOG(INFO) << "Found the remaining system_server artifacts on /data.";
- return true;
+ BootImages boot_images_to_generate{
+ .primary_boot_image =
+ !boot_images_on_system.primary_boot_image && !boot_images_on_data.primary_boot_image,
+ .boot_image_mainline_extension = !boot_images_on_system.boot_image_mainline_extension &&
+ !boot_images_on_data.boot_image_mainline_extension,
+ };
+
+ if (boot_images_to_generate.Count() == 0) {
+ LOG(INFO) << "Boot images on /data OK ({})"_format(isa_str);
+ }
+
+ return boot_images_to_generate;
+}
+
+std::set<std::string> OnDeviceRefresh::CheckSystemServerArtifactsAreUpToDate(
+ OdrMetrics& metrics,
+ const PreconditionCheckResult& system_result,
+ const PreconditionCheckResult& data_result,
+ /*out*/ std::vector<std::string>* checked_artifacts) const {
+ std::set<std::string> jars_to_compile;
+ std::set<std::string> jars_missing_artifacts_on_system;
+ if (system_result.IsSystemServerOk()) {
+ // 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)) {
+ LOG(INFO) << "system_server artifacts on /system OK";
+ return {};
}
- LOG(INFO) << "Incomplete system_server artifacts. " << error_msg;
- metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
- *jars_to_compile = jars_missing_artifacts_on_data;
- return false;
+ LOG(INFO) << "Incomplete system server artifacts on /system: " << error_msg;
+ LOG(INFO) << "Checking system server artifacts /data";
+ } else {
+ jars_missing_artifacts_on_system = AllSystemServerJars();
}
- return true;
+ std::set<std::string> jars_missing_artifacts_on_data;
+ std::string error_msg;
+ if (data_result.IsSystemServerOk()) {
+ SystemServerArtifactsExist(
+ /*on_system=*/false, &error_msg, &jars_missing_artifacts_on_data, checked_artifacts);
+ } else {
+ jars_missing_artifacts_on_data = AllSystemServerJars();
+ }
+
+ std::set_intersection(jars_missing_artifacts_on_system.begin(),
+ jars_missing_artifacts_on_system.end(),
+ jars_missing_artifacts_on_data.begin(),
+ jars_missing_artifacts_on_data.end(),
+ std::inserter(jars_to_compile, jars_to_compile.end()));
+ if (!jars_to_compile.empty()) {
+ if (data_result.IsSystemServerOk()) {
+ LOG(INFO) << "Incomplete system_server artifacts on /data: " << error_msg;
+ metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
+ } else {
+ metrics.SetTrigger(data_result.GetTrigger());
+ }
+ return jars_to_compile;
+ }
+
+ LOG(INFO) << "system_server artifacts on /data OK";
+ return {};
}
Result<void> OnDeviceRefresh::CleanupArtifactDirectory(
- const std::vector<std::string>& artifacts_to_keep) const {
+ OdrMetrics& metrics, const std::vector<std::string>& artifacts_to_keep) const {
const std::string& artifact_dir = config_.GetArtifactDirectory();
std::unordered_set<std::string> artifact_set{artifacts_to_keep.begin(), artifacts_to_keep.end()};
@@ -1263,7 +1483,9 @@ Result<void> OnDeviceRefresh::CleanupArtifactDirectory(
// undefined behavior;
entries.push_back(entry);
}
- if (ec) {
+ if (ec && ec.value() != ENOENT) {
+ metrics.SetStatus(ec.value() == EPERM ? OdrMetrics::Status::kDalvikCachePermissionDenied :
+ OdrMetrics::Status::kIoError);
return Errorf("Failed to iterate over entries in the artifact directory: {}", ec.message());
}
@@ -1273,6 +1495,7 @@ Result<void> OnDeviceRefresh::CleanupArtifactDirectory(
if (!ContainsElement(artifact_set, path)) {
LOG(INFO) << "Removing " << path;
if (unlink(path.c_str()) != 0) {
+ metrics.SetStatus(OdrMetrics::Status::kIoError);
return ErrnoErrorf("Failed to remove file {}", QuotePath(path));
}
}
@@ -1280,6 +1503,7 @@ Result<void> OnDeviceRefresh::CleanupArtifactDirectory(
// Neither a regular file nor a directory. Unexpected file type.
LOG(INFO) << "Removing " << path;
if (unlink(path.c_str()) != 0) {
+ metrics.SetStatus(OdrMetrics::Status::kIoError);
return ErrnoErrorf("Failed to remove file {}", QuotePath(path));
}
}
@@ -1338,9 +1562,12 @@ OnDeviceRefresh::CheckArtifactsAreUpToDate(OdrMetrics& metrics,
// Clean-up helper used to simplify clean-ups and handling failures there.
auto cleanup_and_compile_all = [&, this]() {
- compilation_options->compile_boot_classpath_for_isas = config_.GetBootClasspathIsas();
- compilation_options->system_server_jars_to_compile = AllSystemServerJars();
- return RemoveArtifactsDirectory() ? ExitCode::kCompilationRequired : ExitCode::kCleanupFailed;
+ *compilation_options = CompilationOptions::CompileAll(*this);
+ if (!RemoveArtifactsDirectory()) {
+ metrics.SetStatus(OdrMetrics::Status::kIoError);
+ return ExitCode::kCleanupFailed;
+ }
+ return ExitCode::kCompilationRequired;
};
std::optional<std::vector<apex::ApexInfo>> apex_info_list = GetApexInfoList();
@@ -1368,23 +1595,18 @@ OnDeviceRefresh::CheckArtifactsAreUpToDate(OdrMetrics& metrics,
// Record ART APEX last update milliseconds (used in compilation log).
metrics.SetArtApexLastUpdateMillis(art_apex_info->getLastUpdateMillis());
- std::optional<art_apex::CacheInfo> cache_info = ReadCacheInfo();
- if (!cache_info.has_value() && OS::FileExists(cache_info_filename_.c_str())) {
- // This should not happen unless odrefresh is updated to a new version that is not
- // compatible with an old cache-info file. Further up-to-date checks are not possible if it
- // does.
- PLOG(ERROR) << "Failed to parse cache-info file: " << QuotePath(cache_info_filename_);
- metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- return cleanup_and_compile_all();
- }
-
InstructionSet system_server_isa = config_.GetSystemServerIsa();
std::vector<std::string> checked_artifacts;
- for (const InstructionSet isa : config_.GetBootClasspathIsas()) {
- if (!CheckBootClasspathArtifactsAreUpToDate(
- metrics, isa, art_apex_info.value(), cache_info, &checked_artifacts)) {
- compilation_options->compile_boot_classpath_for_isas.push_back(isa);
+ PreconditionCheckResult system_result = CheckPreconditionForSystem(apex_info_list.value());
+ PreconditionCheckResult data_result = CheckPreconditionForData(apex_info_list.value());
+
+ for (InstructionSet isa : config_.GetBootClasspathIsas()) {
+ BootImages boot_images_to_generate = CheckBootClasspathArtifactsAreUpToDate(
+ metrics, isa, system_result, data_result, &checked_artifacts);
+ if (boot_images_to_generate.Count() > 0) {
+ compilation_options->boot_images_to_generate_for_isas.emplace_back(isa,
+ boot_images_to_generate);
// system_server artifacts are invalid without valid boot classpath artifacts.
if (isa == system_server_isa) {
compilation_options->system_server_jars_to_compile = AllSystemServerJars();
@@ -1393,15 +1615,17 @@ OnDeviceRefresh::CheckArtifactsAreUpToDate(OdrMetrics& metrics,
}
if (compilation_options->system_server_jars_to_compile.empty()) {
- CheckSystemServerArtifactsAreUpToDate(metrics,
- apex_info_list.value(),
- cache_info,
- &compilation_options->system_server_jars_to_compile,
- &checked_artifacts);
+ compilation_options->system_server_jars_to_compile = CheckSystemServerArtifactsAreUpToDate(
+ metrics, system_result, data_result, &checked_artifacts);
}
- bool compilation_required = (!compilation_options->compile_boot_classpath_for_isas.empty() ||
- !compilation_options->system_server_jars_to_compile.empty());
+ bool compilation_required = compilation_options->CompilationUnitCount() > 0;
+
+ if (!compilation_required && !data_result.IsAllOk()) {
+ // Return kCompilationRequired to generate the cache info even if there's nothing to compile.
+ compilation_required = true;
+ metrics.SetTrigger(data_result.GetTrigger());
+ }
// If partial compilation is disabled, we should compile everything regardless of what's in
// `compilation_options`.
@@ -1409,12 +1633,10 @@ OnDeviceRefresh::CheckArtifactsAreUpToDate(OdrMetrics& metrics,
return cleanup_and_compile_all();
}
- // We should only keep the cache info if we have artifacts on /data.
- if (!checked_artifacts.empty()) {
- checked_artifacts.push_back(cache_info_filename_);
- }
+ // Always keep the cache info.
+ checked_artifacts.push_back(cache_info_filename_);
- Result<void> result = CleanupArtifactDirectory(checked_artifacts);
+ Result<void> result = CleanupArtifactDirectory(metrics, checked_artifacts);
if (!result.ok()) {
LOG(ERROR) << result.error();
return ExitCode::kCleanupFailed;
@@ -1423,304 +1645,364 @@ OnDeviceRefresh::CheckArtifactsAreUpToDate(OdrMetrics& metrics,
return compilation_required ? ExitCode::kCompilationRequired : ExitCode::kOkay;
}
-WARN_UNUSED bool OnDeviceRefresh::CompileBootClasspathArtifacts(
- const InstructionSet isa,
+WARN_UNUSED CompilationResult OnDeviceRefresh::RunDex2oat(
const std::string& staging_dir,
- OdrMetrics& metrics,
- const std::function<void()>& on_dex2oat_success,
- bool minimal,
- std::string* error_msg) const {
- ScopedOdrCompilationTimer compilation_timer(metrics);
+ const std::string& debug_message,
+ InstructionSet isa,
+ const std::vector<std::string>& dex_files,
+ const std::vector<std::string>& boot_classpath,
+ const std::vector<std::string>& input_boot_images,
+ const OdrArtifacts& artifacts,
+ const std::vector<std::string>& extra_args,
+ /*inout*/ std::vector<std::unique_ptr<File>>& readonly_files_raii) const {
std::vector<std::string> args;
args.push_back(config_.GetDex2Oat());
AddDex2OatCommonOptions(args);
AddDex2OatDebugInfo(args);
AddDex2OatInstructionSet(args, isa);
- if (!AddDex2OatConcurrencyArguments(args)) {
- return false;
- }
-
- std::vector<std::unique_ptr<File>> readonly_files_raii;
- const std::string art_boot_profile_file = GetArtRoot() + "/etc/boot-image.prof";
- const std::string framework_boot_profile_file = GetAndroidRoot() + "/etc/boot-image.prof";
- AddDex2OatProfileAndCompilerFilter(args, readonly_files_raii,
- {art_boot_profile_file, framework_boot_profile_file});
-
- // Compile as a single image for fewer files and slightly less memory overhead.
- args.emplace_back("--single-image");
-
- args.emplace_back(android::base::StringPrintf("--base=0x%08x", ART_BASE_ADDRESS));
-
- const std::string dirty_image_objects_file(GetAndroidRoot() + "/etc/dirty-image-objects");
- if (OS::FileExists(dirty_image_objects_file.c_str())) {
- std::unique_ptr<File> file(OS::OpenFileForReading(dirty_image_objects_file.c_str()));
- args.emplace_back(android::base::StringPrintf("--dirty-image-objects-fd=%d", file->Fd()));
- readonly_files_raii.push_back(std::move(file));
- } else {
- LOG(WARNING) << "Missing dirty objects file : " << QuotePath(dirty_image_objects_file);
- }
-
- const std::string preloaded_classes_file(GetAndroidRoot() + "/etc/preloaded-classes");
- if (OS::FileExists(preloaded_classes_file.c_str())) {
- std::unique_ptr<File> file(OS::OpenFileForReading(preloaded_classes_file.c_str()));
- args.emplace_back(android::base::StringPrintf("--preloaded-classes-fds=%d", file->Fd()));
- readonly_files_raii.push_back(std::move(file));
- } else {
- LOG(WARNING) << "Missing preloaded classes file : " << QuotePath(preloaded_classes_file);
+ Result<void> result = AddDex2OatConcurrencyArguments(args, config_.GetCompilationOsMode());
+ if (!result.ok()) {
+ return CompilationResult::Error(OdrMetrics::Status::kUnknown, result.error().message());
}
- // Add boot classpath jars to compile.
- std::vector<std::string> jars_to_compile = boot_classpath_compilable_jars_;
- if (minimal) {
- auto end =
- std::remove_if(jars_to_compile.begin(), jars_to_compile.end(), [](const std::string& jar) {
- return !android::base::StartsWith(jar, GetArtRoot());
- });
- jars_to_compile.erase(end, jars_to_compile.end());
+ // dex2oat reads some system properties from cache-info.xml generated by odrefresh.
+ result = AddCacheInfoFd(args, readonly_files_raii, cache_info_filename_);
+ if (!result.ok()) {
+ return CompilationResult::Error(OdrMetrics::Status::kUnknown, result.error().message());
}
- for (const std::string& component : jars_to_compile) {
- std::string actual_path = AndroidRootRewrite(component);
- args.emplace_back("--dex-file=" + component);
+ for (const std::string& dex_file : dex_files) {
+ std::string actual_path = RewriteParentDirectoryIfNeeded(dex_file);
+ args.emplace_back("--dex-file=" + dex_file);
std::unique_ptr<File> file(OS::OpenFileForReading(actual_path.c_str()));
- args.emplace_back(android::base::StringPrintf("--dex-fd=%d", file->Fd()));
+ args.emplace_back(StringPrintf("--dex-fd=%d", file->Fd()));
readonly_files_raii.push_back(std::move(file));
}
args.emplace_back("--runtime-arg");
- args.emplace_back(Concatenate({"-Xbootclasspath:", android::base::Join(jars_to_compile, ":")}));
- if (!AddBootClasspathFds(args, readonly_files_raii, jars_to_compile)) {
- return false;
+ args.emplace_back("-Xbootclasspath:" + Join(boot_classpath, ":"));
+ result = AddBootClasspathFds(args, readonly_files_raii, boot_classpath);
+ if (!result.ok()) {
+ return CompilationResult::Error(OdrMetrics::Status::kIoError, result.error().message());
}
- const std::string image_location = GetBootImagePath(/*on_system=*/false, minimal, isa);
- const OdrArtifacts artifacts = OdrArtifacts::ForBootImage(image_location);
+ if (!input_boot_images.empty()) {
+ args.emplace_back("--boot-image=" + Join(input_boot_images, ':'));
+ AddCompiledBootClasspathFdsIfAny(
+ args, readonly_files_raii, boot_classpath, isa, input_boot_images);
+ }
args.emplace_back("--oat-location=" + artifacts.OatPath());
- const std::pair<const std::string, const char*> location_kind_pairs[] = {
- std::make_pair(artifacts.ImagePath(), "image"),
+ std::pair<std::string, const char*> location_kind_pairs[] = {
+ std::make_pair(artifacts.ImagePath(), artifacts.ImageKind()),
std::make_pair(artifacts.OatPath(), "oat"),
std::make_pair(artifacts.VdexPath(), "output-vdex")};
std::vector<std::unique_ptr<File>> staging_files;
- for (const auto& location_kind_pair : location_kind_pairs) {
- auto& [location, kind] = location_kind_pair;
- const std::string staging_location = GetStagingLocation(staging_dir, location);
+ for (const auto& [location, kind] : location_kind_pairs) {
+ std::string staging_location = GetStagingLocation(staging_dir, location);
std::unique_ptr<File> staging_file(OS::CreateEmptyFile(staging_location.c_str()));
if (staging_file == nullptr) {
- PLOG(ERROR) << "Failed to create " << kind << " file: " << staging_location;
- metrics.SetStatus(OdrMetrics::Status::kIoError);
- EraseFiles(staging_files);
- return false;
- }
-
- if (fchmod(staging_file->Fd(), S_IRUSR | S_IWUSR) != 0) {
- PLOG(ERROR) << "Could not set file mode on " << QuotePath(staging_location);
- metrics.SetStatus(OdrMetrics::Status::kIoError);
- EraseFiles(staging_files);
- return false;
- }
-
- args.emplace_back(android::base::StringPrintf("--%s-fd=%d", kind, staging_file->Fd()));
+ return CompilationResult::Error(
+ OdrMetrics::Status::kIoError,
+ "Failed to create {} file '{}'"_format(kind, staging_location));
+ }
+ // Don't check the state of the staging file. It doesn't need to be flushed because it's removed
+ // after the compilation regardless of success or failure.
+ staging_file->MarkUnchecked();
+ args.emplace_back(StringPrintf("--%s-fd=%d", kind, staging_file->Fd()));
staging_files.emplace_back(std::move(staging_file));
}
- const std::string install_location = android::base::Dirname(image_location);
+ std::string install_location = Dirname(artifacts.OatPath());
if (!EnsureDirectoryExists(install_location)) {
- metrics.SetStatus(OdrMetrics::Status::kIoError);
- return false;
+ return CompilationResult::Error(
+ OdrMetrics::Status::kIoError,
+ "Error encountered when preparing directory '{}'"_format(install_location));
}
- const time_t timeout = GetSubprocessTimeout();
- const std::string cmd_line = android::base::Join(args, ' ');
- LOG(INFO) << android::base::StringPrintf("Compiling boot classpath (%s%s): %s [timeout %lds]",
- GetInstructionSetString(isa),
- minimal ? ", minimal" : "",
- cmd_line.c_str(),
- timeout);
+ std::copy(extra_args.begin(), extra_args.end(), std::back_inserter(args));
+
+ Timer timer;
+ time_t timeout = GetSubprocessTimeout();
+ std::string cmd_line = Join(args, ' ');
+ LOG(INFO) << "{}: {} [timeout {}s]"_format(debug_message, cmd_line, timeout);
if (config_.GetDryRun()) {
LOG(INFO) << "Compilation skipped (dry-run).";
- return true;
+ return CompilationResult::Ok();
}
- bool timed_out = false;
- int dex2oat_exit_code = exec_utils_->ExecAndReturnCode(args, timeout, &timed_out, error_msg);
+ std::string error_msg;
+ ExecResult dex2oat_result = exec_utils_->ExecAndReturnResult(args, timeout, &error_msg);
- if (dex2oat_exit_code != 0) {
- if (timed_out) {
- metrics.SetStatus(OdrMetrics::Status::kTimeLimitExceeded);
- } else {
- metrics.SetStatus(OdrMetrics::Status::kDex2OatError);
- }
- EraseFiles(staging_files);
- return false;
+ if (dex2oat_result.exit_code != 0) {
+ return CompilationResult::Dex2oatError(
+ dex2oat_result.exit_code < 0 ?
+ error_msg :
+ "dex2oat returned an unexpected code: {}"_format(dex2oat_result.exit_code),
+ timer.duration().count(),
+ dex2oat_result);
}
if (!MoveOrEraseFiles(staging_files, install_location)) {
- metrics.SetStatus(OdrMetrics::Status::kInstallFailed);
- return false;
+ return CompilationResult::Error(OdrMetrics::Status::kIoError,
+ "Failed to commit artifacts to '{}'"_format(install_location));
}
- on_dex2oat_success();
- return true;
+ return CompilationResult::Dex2oatOk(timer.duration().count(), dex2oat_result);
}
-WARN_UNUSED bool OnDeviceRefresh::CompileSystemServerArtifacts(
- const std::string& staging_dir,
- OdrMetrics& metrics,
- const std::set<std::string>& system_server_jars_to_compile,
- const std::function<void()>& on_dex2oat_success,
- std::string* error_msg) const {
- ScopedOdrCompilationTimer compilation_timer(metrics);
- std::vector<std::string> classloader_context;
+WARN_UNUSED CompilationResult
+OnDeviceRefresh::RunDex2oatForBootClasspath(const std::string& staging_dir,
+ const std::string& debug_name,
+ InstructionSet isa,
+ const std::vector<std::string>& dex_files,
+ const std::vector<std::string>& boot_classpath,
+ const std::vector<std::string>& input_boot_images,
+ const std::string& output_path) const {
+ std::vector<std::string> args;
+ std::vector<std::unique_ptr<File>> readonly_files_raii;
- const std::string dex2oat = config_.GetDex2Oat();
- const InstructionSet isa = config_.GetSystemServerIsa();
- for (const std::string& jar : all_systemserver_jars_) {
- auto scope_guard = android::base::make_scope_guard([&]() {
- if (ContainsElement(systemserver_classpath_jars_, jar)) {
- classloader_context.emplace_back(jar);
- }
- });
+ // Compile as a single image for fewer files and slightly less memory overhead.
+ args.emplace_back("--single-image");
- if (!ContainsElement(system_server_jars_to_compile, jar)) {
- continue;
+ if (input_boot_images.empty()) {
+ // Primary boot image.
+ std::string art_boot_profile_file = GetArtRoot() + "/etc/boot-image.prof";
+ std::string framework_boot_profile_file = GetAndroidRoot() + "/etc/boot-image.prof";
+ bool has_any_profile = AddDex2OatProfile(
+ args, readonly_files_raii, {art_boot_profile_file, framework_boot_profile_file});
+ if (!has_any_profile) {
+ return CompilationResult::Error(OdrMetrics::Status::kIoError, "Missing boot image profile");
+ }
+ const std::string& compiler_filter = config_.GetBootImageCompilerFilter();
+ if (!compiler_filter.empty()) {
+ args.emplace_back("--compiler-filter=" + compiler_filter);
+ } else {
+ args.emplace_back(StringPrintf("--compiler-filter=%s", kPrimaryCompilerFilter));
}
- std::vector<std::unique_ptr<File>> readonly_files_raii;
- std::vector<std::string> args;
- args.emplace_back(dex2oat);
- args.emplace_back("--dex-file=" + jar);
-
- std::string actual_jar_path = AndroidRootRewrite(jar);
- std::unique_ptr<File> dex_file(OS::OpenFileForReading(actual_jar_path.c_str()));
- args.emplace_back(android::base::StringPrintf("--dex-fd=%d", dex_file->Fd()));
- readonly_files_raii.push_back(std::move(dex_file));
+ args.emplace_back(StringPrintf("--base=0x%08x", ART_BASE_ADDRESS));
- AddDex2OatCommonOptions(args);
- AddDex2OatDebugInfo(args);
- AddDex2OatInstructionSet(args, isa);
- if (!AddDex2OatConcurrencyArguments(args)) {
- return false;
+ std::string dirty_image_objects_file(GetAndroidRoot() + "/etc/dirty-image-objects");
+ if (OS::FileExists(dirty_image_objects_file.c_str())) {
+ std::unique_ptr<File> file(OS::OpenFileForReading(dirty_image_objects_file.c_str()));
+ args.emplace_back(StringPrintf("--dirty-image-objects-fd=%d", file->Fd()));
+ readonly_files_raii.push_back(std::move(file));
+ } else {
+ LOG(WARNING) << "Missing dirty objects file: '{}'"_format(dirty_image_objects_file);
}
- const std::string jar_name(android::base::Basename(jar));
- const std::string profile = Concatenate({GetAndroidRoot(), "/framework/", jar_name, ".prof"});
- std::string compiler_filter = config_.GetSystemServerCompilerFilter();
- if (compiler_filter == "speed-profile") {
- AddDex2OatProfileAndCompilerFilter(args, readonly_files_raii, {profile});
+ std::string preloaded_classes_file(GetAndroidRoot() + "/etc/preloaded-classes");
+ if (OS::FileExists(preloaded_classes_file.c_str())) {
+ std::unique_ptr<File> file(OS::OpenFileForReading(preloaded_classes_file.c_str()));
+ args.emplace_back(StringPrintf("--preloaded-classes-fds=%d", file->Fd()));
+ readonly_files_raii.push_back(std::move(file));
} else {
- args.emplace_back("--compiler-filter=" + compiler_filter);
+ LOG(WARNING) << "Missing preloaded classes file: '{}'"_format(preloaded_classes_file);
}
+ } else {
+ // Mainline extension.
+ args.emplace_back(StringPrintf("--compiler-filter=%s", kMainlineCompilerFilter));
+ }
+
+ return RunDex2oat(
+ staging_dir,
+ "Compiling boot classpath ({}, {})"_format(GetInstructionSetString(isa), debug_name),
+ isa,
+ dex_files,
+ boot_classpath,
+ input_boot_images,
+ OdrArtifacts::ForBootImage(output_path),
+ args,
+ readonly_files_raii);
+}
- const std::string image_location = GetSystemServerImagePath(/*on_system=*/false, jar);
- const std::string install_location = android::base::Dirname(image_location);
- if (!EnsureDirectoryExists(install_location)) {
- metrics.SetStatus(OdrMetrics::Status::kIoError);
- return false;
- }
+WARN_UNUSED CompilationResult
+OnDeviceRefresh::CompileBootClasspath(const std::string& staging_dir,
+ InstructionSet isa,
+ BootImages boot_images,
+ const std::function<void()>& on_dex2oat_success) const {
+ DCHECK_GT(boot_images.Count(), 0);
+ DCHECK_IMPLIES(boot_images.primary_boot_image, boot_images.boot_image_mainline_extension);
- OdrArtifacts artifacts = OdrArtifacts::ForSystemServer(image_location);
- CHECK_EQ(artifacts.OatPath(), GetApexDataOdexFilename(jar.c_str(), isa));
+ CompilationResult result = CompilationResult::Ok();
- const std::pair<const std::string, const char*> location_kind_pairs[] = {
- std::make_pair(artifacts.ImagePath(), "app-image"),
- std::make_pair(artifacts.OatPath(), "oat"),
- std::make_pair(artifacts.VdexPath(), "output-vdex")};
+ if (config_.GetMinimal()) {
+ result.Merge(
+ CompilationResult::Error(OdrMetrics::Status::kUnknown, "Minimal boot image requested"));
+ }
- std::vector<std::unique_ptr<File>> staging_files;
- for (const auto& location_kind_pair : location_kind_pairs) {
- auto& [location, kind] = location_kind_pair;
- const std::string staging_location = GetStagingLocation(staging_dir, location);
- std::unique_ptr<File> staging_file(OS::CreateEmptyFile(staging_location.c_str()));
- if (staging_file == nullptr) {
- PLOG(ERROR) << "Failed to create " << kind << " file: " << staging_location;
- metrics.SetStatus(OdrMetrics::Status::kIoError);
- EraseFiles(staging_files);
- return false;
- }
- args.emplace_back(android::base::StringPrintf("--%s-fd=%d", kind, staging_file->Fd()));
- staging_files.emplace_back(std::move(staging_file));
+ if (!CheckCompilationSpace()) {
+ result.Merge(CompilationResult::Error(OdrMetrics::Status::kNoSpace, "Insufficient space"));
+ }
+
+ if (result.IsOk() && boot_images.primary_boot_image) {
+ CompilationResult primary_result = RunDex2oatForBootClasspath(
+ staging_dir,
+ "primary",
+ isa,
+ dex2oat_boot_classpath_jars_,
+ dex2oat_boot_classpath_jars_,
+ /*input_boot_images=*/{},
+ GetPrimaryBootImagePath(/*on_system=*/false, /*minimal=*/false, isa));
+ result.Merge(primary_result);
+
+ if (primary_result.IsOk()) {
+ on_dex2oat_success();
+
+ // Remove the minimal boot image only if the full boot image is successfully generated.
+ std::string path = GetPrimaryBootImagePath(/*on_system=*/false, /*minimal=*/true, isa);
+ OdrArtifacts artifacts = OdrArtifacts::ForBootImage(path);
+ unlink(artifacts.ImagePath().c_str());
+ unlink(artifacts.OatPath().c_str());
+ unlink(artifacts.VdexPath().c_str());
}
- args.emplace_back("--oat-location=" + artifacts.OatPath());
+ }
- args.emplace_back("--runtime-arg");
- args.emplace_back(Concatenate({"-Xbootclasspath:", config_.GetBootClasspath()}));
+ if (!result.IsOk() && boot_images.primary_boot_image) {
+ LOG(ERROR) << "Compilation of primary BCP failed: " << result.error_msg;
- auto bcp_jars = android::base::Split(config_.GetBootClasspath(), ":");
- if (!AddBootClasspathFds(args, readonly_files_raii, bcp_jars)) {
- return false;
+ // Fall back to generating a minimal boot image.
+ // The compilation of the full boot image will be retried on later reboots with a backoff
+ // time, and the minimal boot image will be removed once the compilation of the full boot
+ // image succeeds.
+ std::string ignored_error_msg;
+ if (PrimaryBootImageExist(
+ /*on_system=*/false, /*minimal=*/true, isa, &ignored_error_msg)) {
+ LOG(INFO) << "Minimal boot image already up-to-date";
+ return result;
}
- std::string unused_error_msg;
- // If the boot classpath artifacts are not on /data, then the boot classpath are not re-compiled
- // and the artifacts must exist on /system.
- bool boot_image_on_system = !BootClasspathArtifactsExist(
- /*on_system=*/false, /*minimal=*/false, isa, &unused_error_msg);
- AddCompiledBootClasspathFdsIfAny(
- args,
- readonly_files_raii,
- bcp_jars,
+ std::vector<std::string> art_bcp_jars = GetArtBcpJars();
+ CompilationResult minimal_result = RunDex2oatForBootClasspath(
+ staging_dir,
+ "minimal",
isa,
- boot_image_on_system ? GetSystemBootImageDir() : config_.GetArtifactDirectory());
- args.emplace_back(
- Concatenate({"--boot-image=",
- boot_image_on_system ? GetBootImage(/*on_system=*/true, /*minimal=*/false) +
- ":" + GetSystemBootImageExtension() :
- GetBootImage(/*on_system=*/false, /*minimal=*/false)}));
-
- const std::string context_path = android::base::Join(classloader_context, ':');
- if (art::ContainsElement(systemserver_classpath_jars_, jar)) {
- args.emplace_back("--class-loader-context=PCL[" + context_path + "]");
- } else {
- args.emplace_back("--class-loader-context=PCL[];PCL[" + context_path + "]");
- }
- if (!classloader_context.empty()) {
- std::vector<int> fds;
- for (const std::string& path : classloader_context) {
- std::string actual_path = AndroidRootRewrite(path);
- std::unique_ptr<File> file(OS::OpenFileForReading(actual_path.c_str()));
- if (!file->IsValid()) {
- PLOG(ERROR) << "Failed to open classloader context " << actual_path;
- metrics.SetStatus(OdrMetrics::Status::kIoError);
- return false;
- }
- fds.emplace_back(file->Fd());
- readonly_files_raii.emplace_back(std::move(file));
- }
- const std::string context_fds = android::base::Join(fds, ':');
- args.emplace_back(Concatenate({"--class-loader-context-fds=", context_fds}));
+ art_bcp_jars,
+ art_bcp_jars,
+ /*input_boot_images=*/{},
+ GetPrimaryBootImagePath(/*on_system=*/false, /*minimal=*/true, isa));
+ result.Merge(minimal_result);
+
+ if (!minimal_result.IsOk()) {
+ LOG(ERROR) << "Compilation of minimal BCP failed: " << result.error_msg;
}
- const time_t timeout = GetSubprocessTimeout();
- const std::string cmd_line = android::base::Join(args, ' ');
- LOG(INFO) << "Compiling " << jar << ": " << cmd_line << " [timeout " << timeout << "s]";
- if (config_.GetDryRun()) {
- LOG(INFO) << "Compilation skipped (dry-run).";
- return true;
+ return result;
+ }
+
+ if (result.IsOk() && boot_images.boot_image_mainline_extension) {
+ CompilationResult mainline_result =
+ RunDex2oatForBootClasspath(staging_dir,
+ "mainline",
+ isa,
+ GetMainlineBcpJars(),
+ boot_classpath_jars_,
+ GetBestBootImages(isa, /*include_mainline_extension=*/false),
+ GetBootImageMainlineExtensionPath(/*on_system=*/false, isa));
+ result.Merge(mainline_result);
+
+ if (mainline_result.IsOk()) {
+ on_dex2oat_success();
}
+ }
- bool timed_out = false;
- int dex2oat_exit_code = exec_utils_->ExecAndReturnCode(args, timeout, &timed_out, error_msg);
+ if (!result.IsOk() && boot_images.boot_image_mainline_extension) {
+ LOG(ERROR) << "Compilation of mainline BCP failed: " << result.error_msg;
+ }
- if (dex2oat_exit_code != 0) {
- if (timed_out) {
- metrics.SetStatus(OdrMetrics::Status::kTimeLimitExceeded);
- } else {
- metrics.SetStatus(OdrMetrics::Status::kDex2OatError);
+ return result;
+}
+
+WARN_UNUSED CompilationResult OnDeviceRefresh::RunDex2oatForSystemServer(
+ const std::string& staging_dir,
+ const std::string& dex_file,
+ const std::vector<std::string>& classloader_context) const {
+ std::vector<std::string> args;
+ std::vector<std::unique_ptr<File>> readonly_files_raii;
+ InstructionSet isa = config_.GetSystemServerIsa();
+ std::string output_path = GetSystemServerImagePath(/*on_system=*/false, dex_file);
+
+ std::string actual_jar_path = RewriteParentDirectoryIfNeeded(dex_file);
+ std::string profile = actual_jar_path + ".prof";
+ const std::string& compiler_filter = config_.GetSystemServerCompilerFilter();
+ bool maybe_add_profile = !compiler_filter.empty() || HasVettedDeviceSystemServerProfiles();
+ bool has_added_profile =
+ maybe_add_profile && AddDex2OatProfile(args, readonly_files_raii, {profile});
+ if (!compiler_filter.empty()) {
+ args.emplace_back("--compiler-filter=" + compiler_filter);
+ } else if (has_added_profile) {
+ args.emplace_back("--compiler-filter=speed-profile");
+ } else {
+ args.emplace_back("--compiler-filter=speed");
+ }
+
+ std::string context_path = Join(classloader_context, ':');
+ if (art::ContainsElement(systemserver_classpath_jars_, dex_file)) {
+ args.emplace_back("--class-loader-context=PCL[" + context_path + "]");
+ } else {
+ args.emplace_back("--class-loader-context=PCL[];PCL[" + context_path + "]");
+ }
+ if (!classloader_context.empty()) {
+ std::vector<int> fds;
+ for (const std::string& path : classloader_context) {
+ std::string actual_path = RewriteParentDirectoryIfNeeded(path);
+ std::unique_ptr<File> file(OS::OpenFileForReading(actual_path.c_str()));
+ if (!file->IsValid()) {
+ return CompilationResult::Error(
+ OdrMetrics::Status::kIoError,
+ "Failed to open classloader context '{}': {}"_format(actual_path, strerror(errno)));
}
- EraseFiles(staging_files);
- return false;
+ fds.emplace_back(file->Fd());
+ readonly_files_raii.emplace_back(std::move(file));
}
+ args.emplace_back("--class-loader-context-fds=" + Join(fds, ':'));
+ }
- if (!MoveOrEraseFiles(staging_files, install_location)) {
- metrics.SetStatus(OdrMetrics::Status::kInstallFailed);
- return false;
+ return RunDex2oat(staging_dir,
+ "Compiling {}"_format(Basename(dex_file)),
+ isa,
+ {dex_file},
+ boot_classpath_jars_,
+ GetBestBootImages(isa, /*include_mainline_extension=*/true),
+ OdrArtifacts::ForSystemServer(output_path),
+ args,
+ readonly_files_raii);
+}
+
+WARN_UNUSED CompilationResult
+OnDeviceRefresh::CompileSystemServer(const std::string& staging_dir,
+ const std::set<std::string>& system_server_jars_to_compile,
+ const std::function<void()>& on_dex2oat_success) const {
+ DCHECK(!system_server_jars_to_compile.empty());
+
+ CompilationResult result = CompilationResult::Ok();
+ std::vector<std::string> classloader_context;
+
+ if (!CheckCompilationSpace()) {
+ LOG(ERROR) << "Compilation of system_server failed: Insufficient space";
+ return CompilationResult::Error(OdrMetrics::Status::kNoSpace, "Insufficient space");
+ }
+
+ for (const std::string& jar : all_systemserver_jars_) {
+ if (ContainsElement(system_server_jars_to_compile, jar)) {
+ CompilationResult current_result =
+ RunDex2oatForSystemServer(staging_dir, jar, classloader_context);
+ result.Merge(current_result);
+
+ if (current_result.IsOk()) {
+ on_dex2oat_success();
+ } else {
+ LOG(ERROR) << "Compilation of {} failed: {}"_format(Basename(jar), result.error_msg);
+ }
}
- on_dex2oat_success();
+ if (ContainsElement(systemserver_classpath_jars_, jar)) {
+ classloader_context.emplace_back(jar);
+ }
}
- return true;
+ return result;
}
WARN_UNUSED ExitCode OnDeviceRefresh::Compile(OdrMetrics& metrics,
@@ -1728,10 +2010,18 @@ WARN_UNUSED ExitCode OnDeviceRefresh::Compile(OdrMetrics& metrics,
const char* staging_dir = nullptr;
metrics.SetStage(OdrMetrics::Stage::kPreparation);
+ if (!EnsureDirectoryExists(config_.GetArtifactDirectory())) {
+ LOG(ERROR) << "Failed to prepare artifact directory";
+ metrics.SetStatus(errno == EPERM ? OdrMetrics::Status::kDalvikCachePermissionDenied :
+ OdrMetrics::Status::kIoError);
+ return ExitCode::kCleanupFailed;
+ }
+
if (config_.GetRefresh()) {
Result<void> result = RefreshExistingArtifacts();
if (!result.ok()) {
LOG(ERROR) << "Failed to refresh existing artifacts: " << result.error();
+ metrics.SetStatus(OdrMetrics::Status::kIoError);
return ExitCode::kCleanupFailed;
}
}
@@ -1740,6 +2030,7 @@ WARN_UNUSED ExitCode OnDeviceRefresh::Compile(OdrMetrics& metrics,
Result<void> result = WriteCacheInfo();
if (!result.ok()) {
LOG(ERROR) << result.error();
+ metrics.SetStatus(OdrMetrics::Status::kIoError);
return ExitCode::kCleanupFailed;
}
@@ -1756,99 +2047,59 @@ WARN_UNUSED ExitCode OnDeviceRefresh::Compile(OdrMetrics& metrics,
std::string error_msg;
uint32_t dex2oat_invocation_count = 0;
- uint32_t total_dex2oat_invocation_count =
- compilation_options.compile_boot_classpath_for_isas.size() +
- compilation_options.system_server_jars_to_compile.size();
+ uint32_t total_dex2oat_invocation_count = compilation_options.CompilationUnitCount();
ReportNextBootAnimationProgress(dex2oat_invocation_count, total_dex2oat_invocation_count);
auto advance_animation_progress = [&]() {
ReportNextBootAnimationProgress(++dex2oat_invocation_count, total_dex2oat_invocation_count);
};
- const auto& bcp_instruction_sets = config_.GetBootClasspathIsas();
+ const std::vector<InstructionSet>& bcp_instruction_sets = config_.GetBootClasspathIsas();
DCHECK(!bcp_instruction_sets.empty() && bcp_instruction_sets.size() <= 2);
- bool full_compilation_failed = false;
- for (const InstructionSet isa : compilation_options.compile_boot_classpath_for_isas) {
- auto stage = (isa == bcp_instruction_sets.front()) ? OdrMetrics::Stage::kPrimaryBootClasspath :
- OdrMetrics::Stage::kSecondaryBootClasspath;
- metrics.SetStage(stage);
- if (!config_.GetMinimal()) {
- if (CheckCompilationSpace()) {
- if (CompileBootClasspathArtifacts(isa,
- staging_dir,
- metrics,
- advance_animation_progress,
- /*minimal=*/false,
- &error_msg)) {
- // Remove the minimal boot image only if the full boot image is successfully generated.
- std::string path = GetBootImagePath(/*on_system=*/false, /*minimal=*/true, isa);
- OdrArtifacts artifacts = OdrArtifacts::ForBootImage(path);
- unlink(artifacts.ImagePath().c_str());
- unlink(artifacts.OatPath().c_str());
- unlink(artifacts.VdexPath().c_str());
- continue;
- }
- LOG(ERROR) << "Compilation of BCP failed: " << error_msg;
- } else {
- metrics.SetStatus(OdrMetrics::Status::kNoSpace);
+ InstructionSet system_server_isa = config_.GetSystemServerIsa();
+
+ bool system_server_isa_failed = false;
+ std::optional<std::pair<OdrMetrics::Stage, OdrMetrics::Status>> first_failure;
+
+ for (const auto& [isa, boot_images_to_generate] :
+ compilation_options.boot_images_to_generate_for_isas) {
+ OdrMetrics::Stage stage = (isa == bcp_instruction_sets.front()) ?
+ OdrMetrics::Stage::kPrimaryBootClasspath :
+ OdrMetrics::Stage::kSecondaryBootClasspath;
+ CompilationResult bcp_result =
+ CompileBootClasspath(staging_dir, isa, boot_images_to_generate, advance_animation_progress);
+ metrics.SetDex2OatResult(stage, bcp_result.elapsed_time_ms, bcp_result.dex2oat_result);
+ metrics.SetBcpCompilationType(stage, boot_images_to_generate.GetTypeForMetrics());
+ if (!bcp_result.IsOk()) {
+ if (isa == system_server_isa) {
+ system_server_isa_failed = true;
}
+ first_failure = first_failure.value_or(std::make_pair(stage, bcp_result.status));
}
+ }
- // Fall back to generating a minimal boot image.
- // The compilation of the full boot image will be retried on later reboots with a backoff time,
- // and the minimal boot image will be removed once the compilation of the full boot image
- // succeeds.
- full_compilation_failed = true;
- std::string ignored_error_msg;
- if (BootClasspathArtifactsExist(
- /*on_system=*/false, /*minimal=*/true, isa, &ignored_error_msg)) {
- continue;
- }
- if (CompileBootClasspathArtifacts(isa,
- staging_dir,
- metrics,
- advance_animation_progress,
- /*minimal=*/true,
- &error_msg)) {
- continue;
+ // Don't compile system server if the compilation of BCP failed.
+ if (!system_server_isa_failed && !compilation_options.system_server_jars_to_compile.empty()) {
+ OdrMetrics::Stage stage = OdrMetrics::Stage::kSystemServerClasspath;
+ CompilationResult ss_result = CompileSystemServer(
+ staging_dir, compilation_options.system_server_jars_to_compile, advance_animation_progress);
+ metrics.SetDex2OatResult(stage, ss_result.elapsed_time_ms, ss_result.dex2oat_result);
+ if (!ss_result.IsOk()) {
+ first_failure = first_failure.value_or(std::make_pair(stage, ss_result.status));
}
- LOG(ERROR) << "Compilation of minimal BCP failed: " << error_msg;
- if (!config_.GetDryRun() && !RemoveDirectory(staging_dir)) {
- return ExitCode::kCleanupFailed;
- }
- return ExitCode::kCompilationFailed;
}
- if (full_compilation_failed) {
+ if (first_failure.has_value()) {
+ metrics.SetStage(first_failure->first);
+ metrics.SetStatus(first_failure->second);
+
if (!config_.GetDryRun() && !RemoveDirectory(staging_dir)) {
return ExitCode::kCleanupFailed;
}
return ExitCode::kCompilationFailed;
}
- if (!compilation_options.system_server_jars_to_compile.empty()) {
- metrics.SetStage(OdrMetrics::Stage::kSystemServerClasspath);
-
- if (!CheckCompilationSpace()) {
- metrics.SetStatus(OdrMetrics::Status::kNoSpace);
- // Return kCompilationFailed so odsign will keep and sign whatever we have been able to
- // compile.
- return ExitCode::kCompilationFailed;
- }
-
- if (!CompileSystemServerArtifacts(staging_dir,
- metrics,
- compilation_options.system_server_jars_to_compile,
- advance_animation_progress,
- &error_msg)) {
- LOG(ERROR) << "Compilation of system_server failed: " << error_msg;
- if (!config_.GetDryRun() && !RemoveDirectory(staging_dir)) {
- return ExitCode::kCleanupFailed;
- }
- return ExitCode::kCompilationFailed;
- }
- }
-
metrics.SetStage(OdrMetrics::Stage::kComplete);
+ metrics.SetStatus(OdrMetrics::Status::kOK);
return ExitCode::kCompilationSuccess;
}