summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Vladimir Marko <vmarko@google.com> 2020-04-02 10:50:35 +0100
committer Vladimir Marko <vmarko@google.com> 2020-04-03 13:40:14 +0100
commitd1f73515701bc64b3a23727b3973da6906f1b167 (patch)
tree59d789c42edd402ae799caa748939b61aeb00be7
parentbda163d9c8313f0b92046abda5ffb1216af1e808 (diff)
dex2oat: add --updatable-bcp-packages-file argument.
Add a command line argument specifying a file that contains the list of updatable boot class path packages. Test: Updated module_exclusion_test Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Test: aosp_taimen-userdebug boots. Bug: 151314205 Change-Id: Ic6a66ad7e565a9b9b344cc467cb1ed550ab41b3f
-rw-r--r--build/Android.gtest.mk7
-rw-r--r--dex2oat/dex2oat.cc62
-rw-r--r--dex2oat/dex2oat_options.cc3
-rw-r--r--dex2oat/dex2oat_options.def1
-rw-r--r--runtime/aot_class_linker.cc44
-rw-r--r--runtime/aot_class_linker.h8
-rw-r--r--runtime/class_linker.cc35
-rw-r--r--runtime/class_linker.h2
-rw-r--r--runtime/module_exclusion_test.cc65
9 files changed, 197 insertions, 30 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index b3cac26374..f5c989202b 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -315,6 +315,11 @@ ART_GTEST_dex2oat_image_test_TARGET_DEPS := \
$(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \
$(TESTING_ART_APEX) # For dex2oatd.
+ART_GTEST_module_exclusion_test_HOST_DEPS := \
+ $(ART_GTEST_dex2oat_image_test_HOST_DEPS)
+ART_GTEST_module_exclusion_test_TARGET_DEPS := \
+ $(ART_GTEST_dex2oat_image_test_TARGET_DEPS)
+
# TODO: document why this is needed.
ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32)
@@ -742,6 +747,8 @@ ART_GTEST_dex2oat_test_TARGET_DEPS :=
ART_GTEST_dex2oat_image_test_DEX_DEPS :=
ART_GTEST_dex2oat_image_test_HOST_DEPS :=
ART_GTEST_dex2oat_image_test_TARGET_DEPS :=
+ART_GTEST_module_exclusion_test_HOST_DEPS :=
+ART_GTEST_module_exclusion_test_TARGET_DEPS :=
ART_GTEST_object_test_DEX_DEPS :=
ART_GTEST_proxy_test_DEX_DEPS :=
ART_GTEST_reflection_test_DEX_DEPS :=
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index cb49bfe127..8713886518 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -41,6 +41,7 @@
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "aot_class_linker.h"
#include "arch/instruction_set_features.h"
#include "art_method-inl.h"
#include "base/callee_save_type.h"
@@ -491,9 +492,14 @@ NO_RETURN static void Usage(const char* fmt, ...) {
UsageError(" for dex files in --class-loader-context. Their order must be the same as");
UsageError(" dex files in flattened class loader context.");
UsageError("");
- UsageError(" --dirty-image-objects=<directory-path>: list of known dirty objects in the image.");
+ UsageError(" --dirty-image-objects=<file-path>: list of known dirty objects in the image.");
UsageError(" The image writer will group them together.");
UsageError("");
+ UsageError(" --updatable-bcp-packages-file=<file-path>: file with a list of updatable");
+ UsageError(" boot class path packages. Classes in these packages and sub-packages");
+ UsageError(" shall not be resolved during app compilation to avoid AOT assumptions");
+ UsageError(" being invalidated after applying updates to these components.");
+ UsageError("");
UsageError(" --compact-dex-level=none|fast: None avoids generating compact dex, fast");
UsageError(" generates compact dex with low compile time. If speed-profile is specified as");
UsageError(" the compiler filter and the profile is not empty, the default compact dex");
@@ -793,6 +799,7 @@ class Dex2Oat final {
image_storage_mode_(ImageHeader::kStorageModeUncompressed),
passes_to_run_filename_(nullptr),
dirty_image_objects_filename_(nullptr),
+ updatable_bcp_packages_filename_(nullptr),
is_host_(false),
elf_writers_(),
oat_writers_(),
@@ -1066,6 +1073,10 @@ class Dex2Oat final {
}
}
+ if ((IsBootImage() || IsBootImageExtension()) && updatable_bcp_packages_filename_ != nullptr) {
+ Usage("Do not specify --updatable-bcp-packages-file for boot image compilation.");
+ }
+
if (!cpu_set_.empty()) {
SetCpuAffinity(cpu_set_);
}
@@ -1324,6 +1335,7 @@ class Dex2Oat final {
AssignIfExists(args, M::NoInlineFrom, &no_inline_from_string_);
AssignIfExists(args, M::ClasspathDir, &classpath_dir_);
AssignIfExists(args, M::DirtyImageObjects, &dirty_image_objects_filename_);
+ AssignIfExists(args, M::UpdatableBcpPackagesFile, &updatable_bcp_packages_filename_);
AssignIfExists(args, M::ImageFormat, &image_storage_mode_);
AssignIfExists(args, M::CompilationReason, &compilation_reason_);
AssignTrueIfExists(args, M::CheckLinkageConditions, &check_linkage_conditions_);
@@ -1879,6 +1891,11 @@ class Dex2Oat final {
class_loader_context_->EncodeContextForOatFile(classpath_dir_,
stored_class_loader_context_.get());
key_value_store_->Put(OatHeader::kClassPathKey, class_path_key);
+
+ // Prepare exclusion list for updatable boot class path packages.
+ if (!PrepareUpdatableBcpPackages()) {
+ return dex2oat::ReturnCode::kOther;
+ }
}
// Now that we have finalized key_value_store_, start writing the .rodata section.
@@ -2613,6 +2630,48 @@ class Dex2Oat final {
return true;
}
+ bool PrepareUpdatableBcpPackages() {
+ DCHECK(!IsBootImage() && !IsBootImageExtension());
+ AotClassLinker* aot_class_linker = down_cast<AotClassLinker*>(runtime_->GetClassLinker());
+ if (updatable_bcp_packages_filename_ != nullptr) {
+ std::unique_ptr<std::vector<std::string>> updatable_bcp_packages =
+ ReadCommentedInputFromFile<std::vector<std::string>>(updatable_bcp_packages_filename_,
+ nullptr); // No post-processing.
+ if (updatable_bcp_packages == nullptr) {
+ LOG(ERROR) << "Failed to load updatable boot class path packages from '"
+ << updatable_bcp_packages_filename_ << "'";
+ return false;
+ }
+ return aot_class_linker->SetUpdatableBootClassPackages(*updatable_bcp_packages);
+ } else {
+ // Use the default list based on updatable packages for Android 11.
+ return aot_class_linker->SetUpdatableBootClassPackages({
+ // Reserved conscrypt packages (includes sub-packages under these paths).
+ // "android.net.ssl", // Covered by android.net below.
+ "com.android.org.conscrypt",
+ // Reserved updatable-media package (includes sub-packages under this path).
+ "android.media",
+ // Reserved framework-mediaprovider package (includes sub-packages under this path).
+ "android.provider",
+ // Reserved framework-statsd packages (includes sub-packages under these paths).
+ "android.app",
+ "android.os",
+ "android.util",
+ // Reserved framework-permission packages (includes sub-packages under this path).
+ "android.permission",
+ // "android.app.role", // Covered by android.app above.
+ // Reserved framework-sdkextensions package (includes sub-packages under this path).
+ // "android.os.ext", // Covered by android.os above.
+ // Reserved framework-wifi packages (includes sub-packages under these paths).
+ "android.hardware.wifi",
+ // "android.net.wifi", // Covered by android.net below.
+ "android.x.net.wifi",
+ // Reserved framework-tethering package (includes sub-packages under this path).
+ "android.net",
+ });
+ }
+ }
+
void PruneNonExistentDexFiles() {
DCHECK_EQ(dex_filenames_.size(), dex_locations_.size());
size_t kept = 0u;
@@ -2985,6 +3044,7 @@ class Dex2Oat final {
ImageHeader::StorageMode image_storage_mode_;
const char* passes_to_run_filename_;
const char* dirty_image_objects_filename_;
+ const char* updatable_bcp_packages_filename_;
std::unique_ptr<HashSet<std::string>> dirty_image_objects_;
std::unique_ptr<std::vector<std::string>> passes_to_run_;
bool is_host_;
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index ea72d54e26..d282c3b1b8 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -131,6 +131,9 @@ static void AddImageMappings(Builder& builder) {
.Define("--dirty-image-objects=_")
.WithType<std::string>()
.IntoKey(M::DirtyImageObjects)
+ .Define("--updatable-bcp-packages-file=_")
+ .WithType<std::string>()
+ .IntoKey(M::UpdatableBcpPackagesFile)
.Define("--image-format=_")
.WithType<ImageHeader::StorageMode>()
.WithValueMap({{"lz4", ImageHeader::kStorageModeLZ4},
diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def
index e324e8bd20..805a13ee82 100644
--- a/dex2oat/dex2oat_options.def
+++ b/dex2oat/dex2oat_options.def
@@ -89,6 +89,7 @@ DEX2OAT_OPTIONS_KEY (std::string, ClassLoaderContext)
DEX2OAT_OPTIONS_KEY (std::string, ClassLoaderContextFds)
DEX2OAT_OPTIONS_KEY (std::string, StoredClassLoaderContext)
DEX2OAT_OPTIONS_KEY (std::string, DirtyImageObjects)
+DEX2OAT_OPTIONS_KEY (std::string, UpdatableBcpPackagesFile)
DEX2OAT_OPTIONS_KEY (std::vector<std::string>, RuntimeOptions)
DEX2OAT_OPTIONS_KEY (std::string, CompilationReason)
DEX2OAT_OPTIONS_KEY (Unit, CheckLinkageConditions)
diff --git a/runtime/aot_class_linker.cc b/runtime/aot_class_linker.cc
index 77faa2d2b9..6625b8dbd2 100644
--- a/runtime/aot_class_linker.cc
+++ b/runtime/aot_class_linker.cc
@@ -196,4 +196,48 @@ bool AotClassLinker::CanReferenceInBootImageExtension(ObjPtr<mirror::Class> klas
return true;
}
+bool AotClassLinker::SetUpdatableBootClassPackages(const std::vector<std::string>& packages) {
+ DCHECK(updatable_boot_class_path_descriptor_prefixes_.empty());
+ // Transform package names to descriptor prefixes.
+ std::vector<std::string> prefixes;
+ prefixes.reserve(packages.size());
+ for (const std::string& package : packages) {
+ if (package.empty() || package.find('/') != std::string::npos) {
+ LOG(ERROR) << "Invalid package name: " << package;
+ return false;
+ }
+ std::string prefix = 'L' + package + '/';
+ std::replace(prefix.begin(), prefix.end(), '.', '/');
+ prefixes.push_back(std::move(prefix));
+ }
+ // Sort and remove unnecessary prefixes.
+ std::sort(prefixes.begin(), prefixes.end());
+ std::string last_prefix;
+ auto end_it = std::remove_if(
+ prefixes.begin(),
+ prefixes.end(),
+ [&last_prefix](const std::string& s) {
+ if (!last_prefix.empty() && StartsWith(s, last_prefix)) {
+ return true;
+ } else {
+ last_prefix = s;
+ return false;
+ }
+ });
+ prefixes.resize(std::distance(prefixes.begin(), end_it));
+ prefixes.shrink_to_fit();
+ updatable_boot_class_path_descriptor_prefixes_.swap(prefixes);
+ return true;
+}
+
+bool AotClassLinker::IsUpdatableBootClassPathDescriptor(const char* descriptor) {
+ std::string_view descriptor_sv(descriptor);
+ for (const std::string& prefix : updatable_boot_class_path_descriptor_prefixes_) {
+ if (StartsWith(descriptor_sv, prefix)) {
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace art
diff --git a/runtime/aot_class_linker.h b/runtime/aot_class_linker.h
index 2b50c46b8c..76984bda99 100644
--- a/runtime/aot_class_linker.h
+++ b/runtime/aot_class_linker.h
@@ -35,6 +35,8 @@ class AotClassLinker : public ClassLinker {
static bool CanReferenceInBootImageExtension(ObjPtr<mirror::Class> klass, gc::Heap* heap)
REQUIRES_SHARED(Locks::mutator_lock_);
+ bool SetUpdatableBootClassPackages(const std::vector<std::string>& packages);
+
protected:
// Overridden version of PerformClassVerification allows skipping verification if the class was
// previously verified but unloaded.
@@ -59,7 +61,13 @@ class AotClassLinker : public ClassLinker {
override
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
+
+ bool IsUpdatableBootClassPathDescriptor(const char* descriptor) override;
+
+ private:
+ std::vector<std::string> updatable_boot_class_path_descriptor_prefixes_;
};
+
} // namespace art
#endif // ART_RUNTIME_AOT_CLASS_LINKER_H_
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 284f19ff3b..daabf01cb7 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3147,33 +3147,6 @@ ObjPtr<mirror::Class> ClassLinker::FindClass(Thread* self,
return result_ptr;
}
-static bool IsReservedBootClassPathDescriptor(const char* descriptor) {
- std::string_view descriptor_sv(descriptor);
- return
- // Reserved conscrypt packages (includes sub-packages under these paths).
- // StartsWith(descriptor_sv, "Landroid/net/ssl/") || // Covered by android.net below.
- StartsWith(descriptor_sv, "Lcom/android/org/conscrypt/") ||
- // Reserved updatable-media package (includes sub-packages under this path).
- StartsWith(descriptor_sv, "Landroid/media/") ||
- // Reserved framework-mediaprovider package (includes sub-packages under this path).
- StartsWith(descriptor_sv, "Landroid/provider/") ||
- // Reserved framework-statsd packages (includes sub-packages under these paths).
- StartsWith(descriptor_sv, "Landroid/app/") ||
- StartsWith(descriptor_sv, "Landroid/os/") ||
- StartsWith(descriptor_sv, "Landroid/util/") ||
- // Reserved framework-permission packages (includes sub-packages under this path).
- StartsWith(descriptor_sv, "Landroid/permission/") ||
- // StartsWith(descriptor_sv, "Landroid/app/role/") || // Covered by android.app above.
- // Reserved framework-sdkextensions package (includes sub-packages under this path).
- // StartsWith(descriptor_sv, "Landroid/os/ext/") || // Covered by android.os above.
- // Reserved framework-wifi packages (includes sub-packages under these paths).
- StartsWith(descriptor_sv, "Landroid/hardware/wifi/") ||
- // StartsWith(descriptor_sv, "Landroid/net/wifi/") || // Covered by android.net below.
- StartsWith(descriptor_sv, "Landroid/x/net/wifi/") ||
- // Reserved framework-tethering package (includes sub-packages under this path).
- StartsWith(descriptor_sv, "Landroid/net/");
-}
-
// Helper for maintaining DefineClass counting. We need to notify callbacks when we start/end a
// define-class and how many recursive DefineClasses we are at in order to allow for doing things
// like pausing class definition.
@@ -3251,7 +3224,7 @@ ObjPtr<mirror::Class> ClassLinker::DefineClass(Thread* self,
// with these modules as these classes could be resolved differently during execution.
if (class_loader != nullptr &&
Runtime::Current()->IsAotCompiler() &&
- IsReservedBootClassPathDescriptor(descriptor)) {
+ IsUpdatableBootClassPathDescriptor(descriptor)) {
ObjPtr<mirror::Throwable> pre_allocated =
Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
self->SetException(pre_allocated);
@@ -9861,6 +9834,12 @@ ObjPtr<mirror::IfTable> ClassLinker::AllocIfTable(Thread* self, size_t ifcount)
ifcount * mirror::IfTable::kMax)));
}
+bool ClassLinker::IsUpdatableBootClassPathDescriptor(const char* descriptor ATTRIBUTE_UNUSED) {
+ // Should not be called on ClassLinker, only on AotClassLinker that overrides this.
+ LOG(FATAL) << "UNREACHABLE";
+ UNREACHABLE();
+}
+
// Instantiate ClassLinker::ResolveMethod.
template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
uint32_t method_idx,
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 998f7392ce..4731203fad 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -829,6 +829,8 @@ class ClassLinker {
return true;
}
+ virtual bool IsUpdatableBootClassPathDescriptor(const char* descriptor);
+
private:
class LinkInterfaceMethodsHelper;
class VisiblyInitializedCallback;
diff --git a/runtime/module_exclusion_test.cc b/runtime/module_exclusion_test.cc
index 67b79d45db..1f5f87ef2b 100644
--- a/runtime/module_exclusion_test.cc
+++ b/runtime/module_exclusion_test.cc
@@ -16,6 +16,8 @@
#include "common_compiler_test.h"
+#include "aot_class_linker.h"
+#include "base/casts.h"
#include "class_linker-inl.h"
#include "handle.h"
#include "handle_scope-inl.h"
@@ -71,7 +73,15 @@ class ModuleExclusionTest : public CommonCompilerTest {
}
}
- private:
+ protected:
+ void SetUpRuntimeOptions(RuntimeOptions* options) override {
+ CommonCompilerTest::SetUpRuntimeOptions(options);
+
+ // Set up the image location to be used by StartDex2OatCommandLine().
+ // Using a prebuilt image also makes the test run faster.
+ options->push_back(std::make_pair("-Ximage:" + GetImageLocation(), nullptr));
+ }
+
std::string GetModuleFileName() const {
std::vector<std::string> filename = GetLibCoreDexFileNames({ module_ });
CHECK_EQ(filename.size(), 1u);
@@ -124,7 +134,60 @@ class ConscryptExclusionTest : public ModuleExclusionTest {
};
TEST_F(ConscryptExclusionTest, Test) {
+ Runtime* runtime = Runtime::Current();
+ ASSERT_TRUE(runtime->IsAotCompiler());
+ AotClassLinker* aot_class_linker = down_cast<AotClassLinker*>(runtime->GetClassLinker());
+ const std::vector<std::string> package_list = {
+ // Reserved conscrypt packages (includes sub-packages under these paths).
+ "android.net.ssl",
+ "com.android.org.conscrypt",
+ };
+ bool list_applied = aot_class_linker->SetUpdatableBootClassPackages(package_list);
+ ASSERT_TRUE(list_applied);
DoTest();
+
+ // Also test passing the list to dex2oat.
+ ScratchFile package_list_file;
+ for (const std::string& package : package_list) {
+ std::string data = package + '\n';
+ ASSERT_TRUE(package_list_file.GetFile()->WriteFully(data.data(), data.size()));
+ }
+ ASSERT_EQ(0, package_list_file.GetFile()->Flush());
+ ScratchDir scratch_dir;
+ std::string jar_name = GetModuleFileName();
+ std::string odex_name = scratch_dir.GetPath() + module_ + ".odex";
+ std::vector<std::string> argv;
+ std::string error_msg;
+ bool success = StartDex2OatCommandLine(&argv, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+ argv.insert(argv.end(), {
+ "--dex-file=" + jar_name,
+ "--dex-location=" + jar_name,
+ "--oat-file=" + odex_name,
+ "--compiler-filter=speed",
+ "--updatable-bcp-packages-file=" + package_list_file.GetFilename()
+ });
+ success = RunDex2Oat(argv, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+ // Load the odex file.
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1,
+ odex_name.c_str(),
+ odex_name.c_str(),
+ /*executable=*/ false,
+ /*low_4gb=*/ false,
+ jar_name,
+ &error_msg));
+ ASSERT_TRUE(odex_file != nullptr) << error_msg;
+ // Check that no classes have been resolved.
+ for (const OatDexFile* oat_dex_file : odex_file->GetOatDexFiles()) {
+ std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+ ASSERT_TRUE(dex_file != nullptr);
+ for (size_t i = 0, num_class_defs = dex_file->NumClassDefs(); i != num_class_defs; ++i) {
+ ClassStatus status = oat_dex_file->GetOatClass(i).GetStatus();
+ ASSERT_FALSE(mirror::Class::IsErroneous(status));
+ ASSERT_LT(status, ClassStatus::kResolved);
+ }
+ }
}
} // namespace art