Update JIT Zygote to support compiling the entire bootclasspath.
After this change:
1. The runtime does not assume that the primary boot image always
exists, and compiles the primary boot image if it is missing or
broken.
2. Profiles can be specified for the the primary boot image in the
"-Ximage" flag.
3. Multiple profiles can be specified for any image in the "-Ximage"
flag (separated by "!").
Bug: 203492478
Test: atest art_standalone_runtime_tests
Test: manual -
1. Build test_jitzygote_com.android.art and install it.
2. adb shell setprop dalvik.vm.extra-opts '-verbose:image'
3. adb shell setprop dalvik.vm.boot-image "/nowhere/boot.art\!/apex/com.android.art/etc/boot-image.prof\!/system/etc/boot-image.prof"
4. adb shell setprop ctl.restart zygote
5. See the logs: http://gpaste/5150329952796672 (and similar logs for 32bit)
Change-Id: I317efda2f17e34808f34a10d095c2598ec056233
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 32016d1..828aa18 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -15,16 +15,15 @@
*/
#include <inttypes.h>
+#include <log/log.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
-#include "base/memory_tool.h"
#include <forward_list>
#include <fstream>
#include <iostream>
#include <limits>
-#include <log/log.h>
#include <memory>
#include <sstream>
#include <string>
@@ -51,8 +50,10 @@
#include "base/dumpable.h"
#include "base/fast_exit.h"
#include "base/file_utils.h"
+#include "base/globals.h"
#include "base/leb128.h"
#include "base/macros.h"
+#include "base/memory_tool.h"
#include "base/mutex.h"
#include "base/os.h"
#include "base/scoped_flock.h"
@@ -770,8 +771,11 @@
// Use the default, i.e. multi-image for boot image and boot image extension.
multi_image_ = IsBootImage() || IsBootImageExtension(); // Shall pass checks below.
}
- if (IsBootImage() && !multi_image_) {
- Usage("--single-image specified for primary boot image");
+ // On target we support generating a single image for the primary boot image.
+ if (!kIsTargetBuild) {
+ if (IsBootImage() && !multi_image_) {
+ Usage("--single-image specified for primary boot image on host");
+ }
}
if (IsAppImage() && multi_image_) {
Usage("--multi-image specified for app image");
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 09cdd50..2978fb4 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -592,9 +592,9 @@
for (bool r : { false, true }) {
relocate = r;
- // Try and fail to load everything as compiled extension.
+ // Load primary boot image with a profile name.
bool load_ok = silent_load(base_location + "!" + single_profile_filename);
- ASSERT_FALSE(load_ok);
+ ASSERT_TRUE(load_ok);
// Try and fail to load with invalid spec, two profile name separators.
load_ok = silent_load(base_location + ":" + single_location + "!!arbitrary-profile-name");
diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h
index 268877a..a33c632 100644
--- a/libartbase/base/common_art_test.h
+++ b/libartbase/base/common_art_test.h
@@ -297,6 +297,12 @@
return; \
}
+#define TEST_DISABLED_FOR_HOST() \
+ if (!kIsTargetBuild) { \
+ printf("WARNING: TEST DISABLED FOR HOST\n"); \
+ return; \
+ }
+
#define TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS() \
if (!kHostStaticBuildEnabled) { \
printf("WARNING: TEST DISABLED FOR NON-STATIC HOST BUILDS\n"); \
diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc
index 222b3d5..a0ea60d 100644
--- a/runtime/gc/collector/immune_spaces_test.cc
+++ b/runtime/gc/collector/immune_spaces_test.cc
@@ -46,7 +46,7 @@
MemMap&& oat_map)
: ImageSpace("FakeImageSpace",
/*image_location=*/"",
- /*profile_file=*/"",
+ /*profile_file=*/{},
std::move(map),
std::move(live_bitmap),
map.End()),
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 2659e72..0885159 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -78,7 +78,7 @@
ImageSpace::ImageSpace(const std::string& image_filename,
const char* image_location,
- const char* profile_file,
+ const std::vector<std::string>& profile_files,
MemMap&& mem_map,
accounting::ContinuousSpaceBitmap&& live_bitmap,
uint8_t* end)
@@ -91,7 +91,7 @@
live_bitmap_(std::move(live_bitmap)),
oat_file_non_owned_(nullptr),
image_location_(image_location),
- profile_file_(profile_file) {
+ profile_files_(profile_files) {
DCHECK(live_bitmap_.IsValid());
}
@@ -608,7 +608,7 @@
return Init(file.get(),
image_filename,
image_location,
- /* profile_file=*/ "",
+ /*profile_files=*/ {},
/*allow_direct_mapping=*/ true,
logger,
image_reservation,
@@ -618,7 +618,7 @@
static std::unique_ptr<ImageSpace> Init(File* file,
const char* image_filename,
const char* image_location,
- const char* profile_file,
+ const std::vector<std::string>& profile_files,
bool allow_direct_mapping,
TimingLogger* logger,
/*inout*/MemMap* image_reservation,
@@ -731,7 +731,7 @@
// We only want the mirror object, not the ArtFields and ArtMethods.
std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename,
image_location,
- profile_file,
+ profile_files,
std::move(map),
std::move(bitmap),
image_end));
@@ -1395,7 +1395,7 @@
struct ImageChunk {
std::string base_location;
std::string base_filename;
- std::string profile_file;
+ std::vector<std::string> profile_files;
size_t start_index;
uint32_t component_count;
uint32_t image_space_count;
@@ -1466,7 +1466,7 @@
struct NamedComponentLocation {
std::string base_location;
size_t bcp_index;
- std::string profile_filename;
+ std::vector<std::string> profile_filenames;
};
std::string ExpandLocationImpl(const std::string& location,
@@ -1519,12 +1519,14 @@
size_t bcp_index,
/*out*/std::string* error_msg);
- bool CompileExtension(const std::string& base_location,
- const std::string& base_filename,
- size_t bcp_index,
- const std::string& profile_filename,
- ArrayRef<const std::string> dependencies,
- /*out*/std::string* error_msg);
+ // Compiles a consecutive subsequence of bootclasspath dex files, whose contents are included in
+ // the profiles specified by `profile_filenames`, starting from `bcp_index`.
+ bool CompileBootclasspathElements(const std::string& base_location,
+ const std::string& base_filename,
+ size_t bcp_index,
+ const std::vector<std::string>& profile_filenames,
+ ArrayRef<const std::string> dependencies,
+ /*out*/std::string* error_msg);
bool CheckAndRemoveLastChunkChecksum(/*inout*/std::string_view* oat_checksums,
/*out*/std::string* error_msg);
@@ -1556,6 +1558,10 @@
std::string ImageSpace::BootImageLayout::GetPrimaryImageLocation() {
DCHECK(!image_locations_.empty());
std::string location = image_locations_[0];
+ size_t profile_separator_pos = location.find(kProfileSeparator);
+ if (profile_separator_pos != std::string::npos) {
+ location.resize(profile_separator_pos);
+ }
if (location.find('/') == std::string::npos) {
// No path, so use the path from the first boot class path component.
size_t slash_pos = boot_class_path_.empty()
@@ -1594,7 +1600,7 @@
for (size_t i = 0; i != components_size; ++i) {
const std::string& component = components[i];
DCHECK(!component.empty()); // Guaranteed by Split().
- const size_t profile_separator_pos = component.find(kProfileSeparator);
+ std::vector<std::string> parts = android::base::Split(component, {kProfileSeparator});
size_t wildcard_pos = component.find('*');
if (wildcard_pos == std::string::npos) {
if (wildcards_start != components.size()) {
@@ -1603,31 +1609,21 @@
component.c_str());
return false;
}
- if (profile_separator_pos != std::string::npos) {
- if (component.find(kProfileSeparator, profile_separator_pos + 1u) != std::string::npos) {
- *error_msg = StringPrintf("Multiple profile delimiters in %s", component.c_str());
- return false;
- }
- if (profile_separator_pos == 0u || profile_separator_pos + 1u == component.size()) {
+ for (size_t j = 0; j < parts.size(); j++) {
+ if (parts[j].empty()) {
*error_msg = StringPrintf("Missing component and/or profile name in %s",
component.c_str());
return false;
}
- if (component.back() == '/') {
- *error_msg = StringPrintf("Profile name ends with path separator: %s",
+ if (parts[j].back() == '/') {
+ *error_msg = StringPrintf("%s name ends with path separator: %s",
+ j == 0 ? "Image component" : "Profile",
component.c_str());
return false;
}
}
- size_t component_name_length =
- profile_separator_pos != std::string::npos ? profile_separator_pos : component.size();
- if (component[component_name_length - 1u] == '/') {
- *error_msg = StringPrintf("Image component ends with path separator: %s",
- component.c_str());
- return false;
- }
} else {
- if (profile_separator_pos != std::string::npos) {
+ if (parts.size() > 1) {
*error_msg = StringPrintf("Unsupproted wildcard (*) and profile delimiter (!) in %s",
component.c_str());
return false;
@@ -1669,13 +1665,16 @@
std::string base_name;
for (size_t i = 0, size = named_components.size(); i != size; ++i) {
std::string component = named_components[i];
- std::string profile_filename; // Empty.
- const size_t profile_separator_pos = component.find(kProfileSeparator);
- if (profile_separator_pos != std::string::npos) {
- profile_filename = component.substr(profile_separator_pos + 1u);
- DCHECK(!profile_filename.empty()); // Checked by VerifyImageLocation()
- component.resize(profile_separator_pos);
- DCHECK(!component.empty()); // Checked by VerifyImageLocation()
+ std::vector<std::string> profile_filenames; // Empty.
+ std::vector<std::string> parts = android::base::Split(component, {kProfileSeparator});
+ for (size_t j = 0; j < parts.size(); j++) {
+ if (j == 0) {
+ component = std::move(parts[j]);
+ DCHECK(!component.empty()); // Checked by VerifyImageLocation()
+ } else {
+ profile_filenames.push_back(std::move(parts[j]));
+ DCHECK(!profile_filenames.back().empty()); // Checked by VerifyImageLocation()
+ }
}
size_t slash_pos = component.rfind('/');
std::string base_location;
@@ -1714,13 +1713,15 @@
}
}
}
- if (!profile_filename.empty() && profile_filename.find('/') == std::string::npos) {
- profile_filename.insert(/*pos*/ 0u, GetBcpComponentPath(bcp_pos));
+ for (std::string& profile_filename : profile_filenames) {
+ if (profile_filename.find('/') == std::string::npos) {
+ profile_filename.insert(/*pos*/ 0u, GetBcpComponentPath(bcp_pos));
+ }
}
NamedComponentLocation location;
location.base_location = base_location;
location.bcp_index = bcp_pos;
- location.profile_filename = profile_filename;
+ location.profile_filenames = profile_filenames;
named_component_locations->push_back(location);
++bcp_pos;
}
@@ -1882,17 +1883,18 @@
return true;
}
-bool ImageSpace::BootImageLayout::CompileExtension(const std::string& base_location,
- const std::string& base_filename,
- size_t bcp_index,
- const std::string& profile_filename,
- ArrayRef<const std::string> dependencies,
- /*out*/std::string* error_msg) {
+bool ImageSpace::BootImageLayout::CompileBootclasspathElements(
+ const std::string& base_location,
+ const std::string& base_filename,
+ size_t bcp_index,
+ const std::vector<std::string>& profile_filenames,
+ ArrayRef<const std::string> dependencies,
+ /*out*/std::string* error_msg) {
DCHECK_LE(total_component_count_, next_bcp_index_);
DCHECK_LE(next_bcp_index_, bcp_index);
size_t bcp_component_count = boot_class_path_.size();
DCHECK_LT(bcp_index, bcp_component_count);
- DCHECK(!profile_filename.empty());
+ DCHECK(!profile_filenames.empty());
if (total_component_count_ != bcp_index) {
// We require all previous BCP components to have a boot image space (primary or extension).
*error_msg = "Cannot compile extension because of missing dependencies.";
@@ -1900,12 +1902,12 @@
}
Runtime* runtime = Runtime::Current();
if (!runtime->IsImageDex2OatEnabled()) {
- *error_msg = "Cannot compile extension because dex2oat for image compilation is disabled.";
+ *error_msg = "Cannot compile bootclasspath because dex2oat for image compilation is disabled.";
return false;
}
// Check dependencies.
- DCHECK(!dependencies.empty());
+ DCHECK_EQ(dependencies.empty(), bcp_index == 0);
size_t dependency_component_count = 0;
for (size_t i = 0, size = dependencies.size(); i != size; ++i) {
if (chunks_.size() == i || chunks_[i].start_index != dependency_component_count) {
@@ -1917,7 +1919,7 @@
// Collect locations from the profile.
std::set<std::string> dex_locations;
- {
+ for (const std::string& profile_filename : profile_filenames) {
std::unique_ptr<File> profile_file(OS::OpenFileForReading(profile_filename.c_str()));
if (profile_file == nullptr) {
*error_msg = StringPrintf("Failed to open profile file \"%s\" for reading, error: %s",
@@ -1968,7 +1970,7 @@
android::base::unique_fd vdex_fd(memfd_create_compat(vdex_filename.c_str(), /*flags=*/ 0));
android::base::unique_fd oat_fd(memfd_create_compat(oat_filename.c_str(), /*flags=*/ 0));
if (art_fd.get() == -1 || vdex_fd.get() == -1 || oat_fd.get() == -1) {
- *error_msg = StringPrintf("Failed to create memfd handles for compiling extension for %s",
+ *error_msg = StringPrintf("Failed to create memfd handles for compiling bootclasspath for %s",
boot_class_path_locations_[bcp_index].c_str());
return false;
}
@@ -1979,13 +1981,17 @@
boot_class_path_.SubArray(/*pos=*/ 0u, /*length=*/ dependency_component_count);
ArrayRef<const std::string> head_bcp_locations =
boot_class_path_locations_.SubArray(/*pos=*/ 0u, /*length=*/ dependency_component_count);
- ArrayRef<const std::string> extension_bcp =
+ ArrayRef<const std::string> bcp_to_compile =
boot_class_path_.SubArray(/*pos=*/ bcp_index, /*length=*/ bcp_end - bcp_index);
- ArrayRef<const std::string> extension_bcp_locations =
+ ArrayRef<const std::string> bcp_to_compile_locations =
boot_class_path_locations_.SubArray(/*pos=*/ bcp_index, /*length=*/ bcp_end - bcp_index);
- std::string boot_class_path = Join(head_bcp, ':') + ':' + Join(extension_bcp, ':');
+ std::string boot_class_path = head_bcp.empty() ?
+ Join(bcp_to_compile, ':') :
+ Join(head_bcp, ':') + ':' + Join(bcp_to_compile, ':');
std::string boot_class_path_locations =
- Join(head_bcp_locations, ':') + ':' + Join(extension_bcp_locations, ':');
+ head_bcp_locations.empty() ?
+ Join(bcp_to_compile_locations, ':') :
+ Join(head_bcp_locations, ':') + ':' + Join(bcp_to_compile_locations, ':');
std::vector<std::string> args;
args.push_back(dex2oat);
@@ -1993,7 +1999,11 @@
args.push_back("-Xbootclasspath:" + boot_class_path);
args.push_back("--runtime-arg");
args.push_back("-Xbootclasspath-locations:" + boot_class_path_locations);
- args.push_back("--boot-image=" + Join(dependencies, kComponentSeparator));
+ if (dependencies.empty()) {
+ args.push_back(android::base::StringPrintf("--base=0x%08x", ART_BASE_ADDRESS));
+ } else {
+ args.push_back("--boot-image=" + Join(dependencies, kComponentSeparator));
+ }
for (size_t i = bcp_index; i != bcp_end; ++i) {
args.push_back("--dex-file=" + boot_class_path_[i]);
args.push_back("--dex-location=" + boot_class_path_locations_[i]);
@@ -2009,8 +2019,10 @@
// And we do not want to compile anything, compilation should be done by JIT in zygote.
args.push_back("--compiler-filter=verify");
- // Pass the profile.
- args.push_back("--profile-file=" + profile_filename);
+ // Pass the profiles.
+ for (const std::string& profile_filename : profile_filenames) {
+ args.push_back("--profile-file=" + profile_filename);
+ }
// Do not let the file descriptor numbers change the compilation output.
args.push_back("--avoid-storing-invocation");
@@ -2026,8 +2038,8 @@
args.push_back(compiler_option);
}
- // Compile the extension.
- VLOG(image) << "Compiling boot image extension for " << (bcp_end - bcp_index)
+ // Compile.
+ VLOG(image) << "Compiling boot bootclasspath for " << (bcp_end - bcp_index)
<< " components, starting from " << boot_class_path_locations_[bcp_index];
if (!Exec(args, error_msg)) {
return false;
@@ -2047,11 +2059,11 @@
return false;
}
- DCHECK(!chunks_.empty());
+ DCHECK_EQ(chunks_.empty(), dependencies.empty());
ImageChunk chunk;
chunk.base_location = base_location;
chunk.base_filename = base_filename;
- chunk.profile_file = profile_filename;
+ chunk.profile_files = profile_filenames;
chunk.start_index = bcp_index;
chunk.component_count = header.GetComponentCount();
chunk.image_space_count = header.GetImageSpaceCount();
@@ -2131,16 +2143,12 @@
DCHECK_EQ(named_component_locations.size(), named_components.size());
const size_t bcp_component_count = boot_class_path_.size();
size_t bcp_pos = 0u;
- ArrayRef<const std::string> extension_dependencies;
for (size_t i = 0, size = named_components.size(); i != size; ++i) {
const std::string& base_location = named_component_locations[i].base_location;
size_t bcp_index = named_component_locations[i].bcp_index;
- const std::string& profile_filename = named_component_locations[i].profile_filename;
- if (extension_dependencies.empty() && !profile_filename.empty()) {
- // Each extension is compiled against the same dependencies, namely the leading
- // named components that were specified without providing the profile filename.
- extension_dependencies = components.SubArray(/*pos=*/ 0, /*length=*/ i);
- }
+ const std::vector<std::string>& profile_filenames =
+ named_component_locations[i].profile_filenames;
+ DCHECK_EQ(i == 0, bcp_index == 0);
if (bcp_index < bcp_pos) {
DCHECK_NE(i, 0u);
LOG(ERROR) << "Named image component already covered by previous image: " << base_location;
@@ -2152,25 +2160,38 @@
return false;
}
std::string local_error_msg;
- std::string* err_msg = (i == 0 || validate) ? error_msg : &local_error_msg;
+ std::string* err_msg = validate ? error_msg : &local_error_msg;
std::string base_filename;
if (!filename_fn(base_location, &base_filename, err_msg) ||
!ReadHeader(base_location, base_filename, bcp_index, err_msg)) {
- if (i == 0u || validate) {
+ if (validate) {
return false;
}
VLOG(image) << "Error reading named image component header for " << base_location
<< ", error: " << local_error_msg;
- if (profile_filename.empty() ||
- !CompileExtension(base_location,
- base_filename,
- bcp_index,
- profile_filename,
- extension_dependencies,
- &local_error_msg)) {
- if (!profile_filename.empty()) {
- VLOG(image) << "Error compiling extension for " << boot_class_path_[bcp_index]
+ if (profile_filenames.empty() ||
+ !CompileBootclasspathElements(base_location,
+ base_filename,
+ bcp_index,
+ profile_filenames,
+ /*dependencies=*/ bcp_index == 0 ?
+ ArrayRef<const std::string>{} :
+ components.SubArray(/*pos=*/ 0, /*length=*/ 1),
+ &local_error_msg)) {
+ if (!profile_filenames.empty()) {
+ VLOG(image) << "Error compiling bootclasspath for " << boot_class_path_[bcp_index]
<< " error: " << local_error_msg;
+ // We cannot continue without the primary boot image because other boot images use it as
+ // a dependency.
+ if (bcp_index == 0) {
+ LOG(ERROR) << "Primary boot image cannot be compiled: " << local_error_msg;
+ return false;
+ }
+ } else {
+ if (bcp_index == 0) {
+ LOG(ERROR) << "Primary boot image cannot be compiled because no profile is provided.";
+ return false;
+ }
}
bcp_pos = bcp_index + 1u; // Skip at least this component.
DCHECK_GT(bcp_pos, GetNextBcpIndex());
@@ -2773,7 +2794,9 @@
const ImageHeader& primary_header = spaces.front()->GetImageHeader();
size_t primary_image_count = primary_header.GetImageSpaceCount();
DCHECK_LE(primary_image_count, num_spaces);
- DCHECK_EQ(primary_image_count, primary_header.GetComponentCount());
+ // The primary boot image can be generated with `--single-image` on device, when generated
+ // in-memory or with odrefresh.
+ DCHECK(primary_image_count == primary_header.GetComponentCount() || primary_image_count == 1);
size_t component_count = primary_image_count;
size_t space_pos = primary_image_count;
while (space_pos != num_spaces) {
@@ -2817,7 +2840,7 @@
std::unique_ptr<ImageSpace> Load(const std::string& image_location,
const std::string& image_filename,
- const std::string& profile_file,
+ const std::vector<std::string>& profile_files,
android::base::unique_fd art_fd,
TimingLogger* logger,
/*inout*/MemMap* image_reservation,
@@ -2833,7 +2856,7 @@
std::unique_ptr<ImageSpace> result = Loader::Init(&image_file,
image_filename.c_str(),
image_location.c_str(),
- profile_file.c_str(),
+ profile_files,
/*allow_direct_mapping=*/ false,
logger,
image_reservation,
@@ -3055,7 +3078,7 @@
for (size_t i = 0u, size = locations.size(); i != size; ++i) {
spaces->push_back(Load(locations[i],
filenames[i],
- chunk.profile_file,
+ chunk.profile_files,
std::move(chunk.art_fd),
logger,
image_reservation,
@@ -3285,15 +3308,11 @@
std::vector<std::string> error_msgs;
std::string error_msg;
- if (loader.HasSystem()) {
- if (loader.LoadFromSystem(extra_reservation_size,
- boot_image_spaces,
- extra_reservation,
- &error_msg)) {
- return true;
- }
- error_msgs.push_back(error_msg);
+ if (loader.LoadFromSystem(
+ extra_reservation_size, boot_image_spaces, extra_reservation, &error_msg)) {
+ return true;
}
+ error_msgs.push_back(error_msg);
std::ostringstream oss;
bool first = true;
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 1dfafe8..5cf751c 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -59,6 +59,13 @@
// <path>/<base-name>
// <base-name>
// and the path of the first BCP component is used for the second form.
+ // The specification may be followed by one or more profile specifications, where each profile
+ // specification is one of
+ // !<profile-path>/<profile-name>
+ // !<profile-name>
+ // and the profiles will be used to compile the primary boot image when loading the boot image if
+ // the on-disk version is not acceptable (either not present or fails validation, presumably
+ // because it's out of date). The primary boot image is compiled with no dependency.
//
// Named extension specifications must correspond to an expansion of the
// <base-name> with a BCP component (for example boot.art with the BCP
@@ -67,15 +74,14 @@
// <ext-path>/<ext-name>
// <ext-name>
// and must be listed in the order of their corresponding BCP components.
- // The specification may have a suffix with profile specification, one of
- // !<ext-path>/<ext-name>
- // !<ext-name>
- // and this profile will be used to compile the extension when loading the
- // boot image if the on-disk version is not acceptable (either not present
- // or fails validation, presumably because it's out of date). The first
- // extension specification that includes the profile specification also
- // terminates the list of the boot image dependencies that each extension
- // is compiled against.
+ // Similarly, the specification may be followed by one or more profile specifications, where each
+ // profile specification is one of
+ // !<profile-path>/<profile-name>
+ // !<profile-name>
+ // and the profiles will be used to compile the extension when loading the boot image if the
+ // on-disk version is not acceptable (either not present or fails validation, presumably because
+ // it's out of date). The primary boot image (i.e., the first element in "image location") is the
+ // dependency that each extension is compiled against.
//
// Search paths for remaining extensions can be specified after named
// components as one of
@@ -90,6 +96,9 @@
// Example image locations:
// /system/framework/boot.art
// - only primary boot image with full path.
+ // /data/misc/apexdata/com.android.art/dalvik-cache/boot.art!/apex/com.android.art/etc/boot-image.prof!/system/etc/boot-image.prof
+ // - only primary boot image with full path; if the primary boot image is not found or
+ // broken, compile it in memory using two specified profile files at the exact paths.
// boot.art:boot-framework.art
// - primary and one extension, use BCP component paths.
// /apex/com.android.art/boot.art:*
@@ -178,9 +187,7 @@
return image_location_;
}
- const std::string GetProfileFile() const {
- return profile_file_;
- }
+ const std::vector<std::string>& GetProfileFiles() const { return profile_files_; }
accounting::ContinuousSpaceBitmap* GetLiveBitmap() override {
return &live_bitmap_;
@@ -302,7 +309,7 @@
ImageSpace(const std::string& name,
const char* image_location,
- const char* profile_file,
+ const std::vector<std::string>& profile_files,
MemMap&& mem_map,
accounting::ContinuousSpaceBitmap&& live_bitmap,
uint8_t* end);
@@ -317,7 +324,7 @@
const OatFile* oat_file_non_owned_;
const std::string image_location_;
- const std::string profile_file_;
+ const std::vector<std::string> profile_files_;
friend class Space;
diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc
index 22550f6..ce984f9 100644
--- a/runtime/gc/space/image_space_test.cc
+++ b/runtime/gc/space/image_space_test.cc
@@ -19,11 +19,11 @@
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
-
+#include "base/globals.h"
#include "base/stl_util.h"
#include "class_linker.h"
-#include "dexopt_test.h"
#include "dex/utf.h"
+#include "dexopt_test.h"
#include "intern_table-inl.h"
#include "noop_compiler_callbacks.h"
#include "oat_file.h"
@@ -371,7 +371,7 @@
}
}
-template <bool kImage, bool kRelocate>
+template <bool kImage, bool kRelocate, bool kProfile>
class ImageSpaceLoadingTest : public CommonRuntimeTest {
protected:
void SetUpRuntimeOptions(RuntimeOptions* options) override {
@@ -380,6 +380,21 @@
missing_image_base_ = std::make_unique<ScratchFile>();
image_location = missing_image_base_->GetFilename() + ".art";
}
+ // Compiling the primary boot image into a single image is not allowed on host.
+ if (kProfile && kIsTargetBuild) {
+ std::vector<std::string> dex_files(GetLibCoreDexFileNames());
+ profile1_ = std::make_unique<ScratchFile>();
+ GenerateBootProfile(ArrayRef<const std::string>(dex_files),
+ profile1_->GetFile(),
+ /*method_frequency=*/6,
+ /*type_frequency=*/6);
+ profile2_ = std::make_unique<ScratchFile>();
+ GenerateBootProfile(ArrayRef<const std::string>(dex_files),
+ profile2_->GetFile(),
+ /*method_frequency=*/8,
+ /*type_frequency=*/8);
+ image_location += "!" + profile1_->GetFilename() + "!" + profile2_->GetFilename();
+ }
options->emplace_back(android::base::StringPrintf("-Ximage:%s", image_location.c_str()),
nullptr);
options->emplace_back(kRelocate ? "-Xrelocate" : "-Xnorelocate", nullptr);
@@ -410,19 +425,47 @@
private:
std::unique_ptr<ScratchFile> missing_image_base_;
+ std::unique_ptr<ScratchFile> profile1_;
+ std::unique_ptr<ScratchFile> profile2_;
UniqueCPtr<const char[]> old_dex2oat_bcp_;
};
-using ImageSpaceNoDex2oatTest = ImageSpaceLoadingTest<true, true>;
+using ImageSpaceNoDex2oatTest =
+ ImageSpaceLoadingTest</*kImage=*/true, /*kRelocate=*/true, /*kProfile=*/false>;
TEST_F(ImageSpaceNoDex2oatTest, Test) {
EXPECT_FALSE(Runtime::Current()->GetHeap()->GetBootImageSpaces().empty());
}
-using ImageSpaceNoRelocateNoDex2oatTest = ImageSpaceLoadingTest<true, false>;
+using ImageSpaceNoRelocateNoDex2oatTest =
+ ImageSpaceLoadingTest</*kImage=*/true, /*kRelocate=*/false, /*kProfile=*/false>;
TEST_F(ImageSpaceNoRelocateNoDex2oatTest, Test) {
EXPECT_FALSE(Runtime::Current()->GetHeap()->GetBootImageSpaces().empty());
}
+using ImageSpaceNoImageNoProfileTest =
+ ImageSpaceLoadingTest</*kImage=*/false, /*kRelocate=*/true, /*kProfile=*/false>;
+TEST_F(ImageSpaceNoImageNoProfileTest, Test) {
+ // Imageless mode.
+ EXPECT_TRUE(Runtime::Current()->GetHeap()->GetBootImageSpaces().empty());
+}
+
+using ImageSpaceNoImageTest =
+ ImageSpaceLoadingTest</*kImage=*/false, /*kRelocate=*/true, /*kProfile=*/true>;
+TEST_F(ImageSpaceNoImageTest, Test) {
+ // Compiling the primary boot image into a single image is not allowed on host.
+ TEST_DISABLED_FOR_HOST();
+
+ const std::vector<ImageSpace*>& image_spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ ASSERT_FALSE(image_spaces.empty());
+
+ const OatFile* oat_file = image_spaces[0]->GetOatFile();
+ ASSERT_TRUE(oat_file != nullptr);
+
+ // Compiled by JIT Zygote.
+ EXPECT_EQ(oat_file->GetCompilerFilter(), CompilerFilter::Filter::kVerify);
+}
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 2ab10ff..aaf507c 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -920,26 +920,27 @@
Runtime* runtime = Runtime::Current();
uint32_t added_to_queue = 0;
for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
- const std::string& profile_file = space->GetProfileFile();
- if (profile_file.empty()) {
- continue;
- }
- LOG(INFO) << "JIT Zygote looking at profile " << profile_file;
-
const std::vector<const DexFile*>& boot_class_path =
runtime->GetClassLinker()->GetBootClassPath();
ScopedNullHandle<mirror::ClassLoader> null_handle;
- // We add to the queue for zygote so that we can fork processes in-between
- // compilations.
+ // We avoid doing compilation at boot for the secondary zygote, as apps forked from it are not
+ // critical for boot.
if (Runtime::Current()->IsPrimaryZygote()) {
- std::string boot_profile = GetBootProfileFile(profile_file);
- // We avoid doing compilation at boot for the secondary zygote, as apps
- // forked from it are not critical for boot.
- added_to_queue += runtime->GetJit()->CompileMethodsFromBootProfile(
- self, boot_class_path, boot_profile, null_handle, /* add_to_queue= */ true);
+ for (const std::string& profile_file : space->GetProfileFiles()) {
+ std::string boot_profile = GetBootProfileFile(profile_file);
+ LOG(INFO) << "JIT Zygote looking at boot profile " << boot_profile;
+
+ // We add to the queue for zygote so that we can fork processes in-between compilations.
+ added_to_queue += runtime->GetJit()->CompileMethodsFromBootProfile(
+ self, boot_class_path, boot_profile, null_handle, /* add_to_queue= */ true);
+ }
}
- added_to_queue += runtime->GetJit()->CompileMethodsFromProfile(
- self, boot_class_path, profile_file, null_handle, /* add_to_queue= */ true);
+ for (const std::string& profile_file : space->GetProfileFiles()) {
+ LOG(INFO) << "JIT Zygote looking at profile " << profile_file;
+
+ added_to_queue += runtime->GetJit()->CompileMethodsFromProfile(
+ self, boot_class_path, profile_file, null_handle, /* add_to_queue= */ true);
+ }
}
JitCodeCache* code_cache = runtime->GetJit()->GetCodeCache();
@@ -1149,7 +1150,7 @@
// methods in that profile for performance.
static bool HasImageWithProfile() {
for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
- if (!space->GetProfileFile().empty()) {
+ if (!space->GetProfileFiles().empty()) {
return true;
}
}
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 3bcd9bb..f172ee0 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -159,7 +159,7 @@
oat_files.reserve(spaces.size());
for (gc::space::ImageSpace* space : spaces) {
// The oat file was generated in memory if the image space has a profile.
- bool in_memory = !space->GetProfileFile().empty();
+ bool in_memory = !space->GetProfileFiles().empty();
oat_files.push_back(RegisterOatFile(space->ReleaseOatFile(), in_memory));
}
return oat_files;