diff options
Diffstat (limited to 'dex2oat')
46 files changed, 3051 insertions, 1609 deletions
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 26cbd51459..7341774889 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -29,6 +29,9 @@ art_cc_defaults { host_supported: true, srcs: [ "dex/quick_compiler_callbacks.cc", + "dex/verification_results.cc", + "driver/compiled_method.cc", + "driver/compiled_method_storage.cc", "driver/compiler_driver.cc", "linker/code_info_table_deduper.cc", "linker/elf_writer.cc", @@ -37,6 +40,7 @@ art_cc_defaults { "linker/multi_oat_relative_patcher.cc", "linker/oat_writer.cc", "linker/relative_patcher.cc", + "utils/swap_space.cc", ], codegen: { @@ -106,6 +110,9 @@ art_cc_library_static { "libartpalette", "libprofile", ], + static_libs: [ + "libelffile", + ], apex_available: [ "com.android.art", "com.android.art.debug", @@ -157,6 +164,9 @@ art_cc_library_static { "libartpalette", "libprofiled", ], + static_libs: [ + "libelffiled", + ], apex_available: [ "com.android.art.debug", ], @@ -242,6 +252,12 @@ cc_defaults { profile_file: "art/dex2oat_arm_arm64.profdata", }, }, + android_riscv64: { + pgo: { + enable_profile_use: false, + profile_file: "", + }, + }, android_x86_64: { pgo: { profile_file: "art/dex2oat_x86_x86_64.profdata", @@ -296,6 +312,7 @@ art_cc_binary { ], static_libs: [ "libart-dex2oat", + "libelffile", ], lto: { thin: true, @@ -320,6 +337,7 @@ art_cc_binary { apex_available: [ "com.android.art", "com.android.art.debug", + "test_broken_com.android.art", ], } @@ -355,6 +373,7 @@ art_cc_binary { ], static_libs: [ "libartd-dex2oat", + "libelffiled", ], }, host: { @@ -459,6 +478,7 @@ art_cc_defaults { name: "art_dex2oat_tests_defaults", data: [ ":art-gtest-jars-AbstractMethod", + ":art-gtest-jars-ArrayClassWithUnresolvedComponent", ":art-gtest-jars-DefaultMethods", ":art-gtest-jars-Dex2oatVdexPublicSdkDex", ":art-gtest-jars-Dex2oatVdexTestDex", @@ -473,12 +493,14 @@ art_cc_defaults { ":art-gtest-jars-ManyMethods", ":art-gtest-jars-MultiDex", ":art-gtest-jars-MultiDexModifiedSecondary", + ":art-gtest-jars-MultiDexUncompressedAligned", ":art-gtest-jars-MyClassNatives", ":art-gtest-jars-Nested", ":art-gtest-jars-ProfileTestMultiDex", ":art-gtest-jars-StaticLeafMethods", ":art-gtest-jars-Statics", ":art-gtest-jars-StringLiterals", + ":art-gtest-jars-SuperWithAccessChecks", ":art-gtest-jars-VerifierDeps", ":art-gtest-jars-VerifierDepsMulti", ":art-gtest-jars-VerifySoftFailDuringClinit", @@ -493,6 +515,7 @@ art_cc_defaults { "dex2oat_test.cc", "dex2oat_vdex_test.cc", "dex2oat_image_test.cc", + "driver/compiled_method_storage_test.cc", "driver/compiler_driver_test.cc", "linker/code_info_table_deduper_test.cc", "linker/elf_writer_test.cc", @@ -502,6 +525,7 @@ art_cc_defaults { "linker/multi_oat_relative_patcher_test.cc", "linker/oat_writer_test.cc", "verifier_deps_test.cc", + "utils/swap_space_test.cc", ], target: { host: { @@ -532,6 +556,9 @@ art_cc_defaults { }, }, + static_libs: [ + "libziparchive", + ], shared_libs: [ "libartpalette", "libbase", @@ -539,7 +566,7 @@ art_cc_defaults { "liblz4", // libart(d)-dex2oat dependency; must be repeated here since it's a static lib. "liblog", "libsigchain", - "libziparchive", + "libz", // libziparchive dependency; must be repeated here since it's a static lib. ], } @@ -553,13 +580,15 @@ art_cc_test { ], shared_libs: [ "libartbased", - "libartd-compiler", "libartd-dexlayout", + "liblzma", "libprofiled", ], static_libs: [ - "libartd-dex2oat-gtest", + "libartd-compiler", "libartd-dex2oat", + "libartd-dex2oat-gtest", + "libelffiled", "libvixld", ], } @@ -573,14 +602,16 @@ art_cc_test { ], data: [":generate-boot-image"], shared_libs: [ - "libartbase", - "libart-compiler", "libart-dexlayout", + "libartbase", + "liblzma", "libprofile", ], static_libs: [ - "libart-dex2oat-gtest", + "libart-compiler", "libart-dex2oat", + "libart-dex2oat-gtest", + "libelffile", "libvixl", ], test_config: "art_standalone_dex2oat_tests.xml", @@ -596,9 +627,16 @@ art_cc_test { ":art-gtest-jars-MainStripped", ":art-gtest-jars-MultiDex", ":art-gtest-jars-MultiDexModifiedSecondary", + ":art-gtest-jars-MultiDexUncompressedAligned", ":art-gtest-jars-Nested", ":generate-boot-image", ], + shared_libs: [ + "libz", // libziparchive dependency; must be repeated here since it's a static lib. + ], + static_libs: [ + "libziparchive", + ], test_config: "art_standalone_dex2oat_cts_tests.xml", test_suites: ["cts"], } diff --git a/dex2oat/art_standalone_dex2oat_cts_tests.xml b/dex2oat/art_standalone_dex2oat_cts_tests.xml index 0dabfdab6c..a47febe545 100644 --- a/dex2oat/art_standalone_dex2oat_cts_tests.xml +++ b/dex2oat/art_standalone_dex2oat_cts_tests.xml @@ -32,6 +32,7 @@ <option name="push" value="art-gtest-jars-MainStripped.jar->/data/local/tmp/art_standalone_dex2oat_cts_tests/art-gtest-jars-MainStripped.jar" /> <option name="push" value="art-gtest-jars-MultiDex.jar->/data/local/tmp/art_standalone_dex2oat_cts_tests/art-gtest-jars-MultiDex.jar" /> <option name="push" value="art-gtest-jars-MultiDexModifiedSecondary.jar->/data/local/tmp/art_standalone_dex2oat_cts_tests/art-gtest-jars-MultiDexModifiedSecondary.jar" /> + <option name="push" value="art-gtest-jars-MultiDexUncompressedAligned.jar->/data/local/tmp/art_standalone_dex2oat_cts_tests/art-gtest-jars-MultiDexUncompressedAligned.jar" /> <option name="push" value="art-gtest-jars-Nested.jar->/data/local/tmp/art_standalone_dex2oat_cts_tests/art-gtest-jars-Nested.jar" /> </target_preparer> diff --git a/dex2oat/art_standalone_dex2oat_tests.xml b/dex2oat/art_standalone_dex2oat_tests.xml index 32346f2f23..3a8c7b9bfe 100644 --- a/dex2oat/art_standalone_dex2oat_tests.xml +++ b/dex2oat/art_standalone_dex2oat_tests.xml @@ -14,6 +14,8 @@ limitations under the License. --> <configuration description="Runs art_standalone_dex2oat_tests."> + <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" /> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> <option name="cleanup" value="true" /> <option name="push" value="art_standalone_dex2oat_tests->/data/local/tmp/art_standalone_dex2oat_tests/art_standalone_dex2oat_tests" /> @@ -23,6 +25,8 @@ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> <option name="cleanup" value="true" /> <option name="push" value="art-gtest-jars-AbstractMethod.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-AbstractMethod.jar" /> + <option name="push" value="art-gtest-jars-ArrayClassWithUnresolvedComponent.dex->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-ArrayClassWithUnresolvedComponent.dex" /> + <option name="push" value="art-gtest-jars-SuperWithAccessChecks.dex->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-SuperWithAccessChecks.dex" /> <option name="push" value="art-gtest-jars-DefaultMethods.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-DefaultMethods.jar" /> <option name="push" value="art-gtest-jars-Dex2oatVdexPublicSdkDex.dex->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-Dex2oatVdexPublicSdkDex.dex" /> <option name="push" value="art-gtest-jars-Dex2oatVdexTestDex.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-Dex2oatVdexTestDex.jar" /> @@ -37,6 +41,7 @@ <option name="push" value="art-gtest-jars-ManyMethods.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-ManyMethods.jar" /> <option name="push" value="art-gtest-jars-MultiDex.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-MultiDex.jar" /> <option name="push" value="art-gtest-jars-MultiDexModifiedSecondary.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-MultiDexModifiedSecondary.jar" /> + <option name="push" value="art-gtest-jars-MultiDexUncompressedAligned.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-MultiDexUncompressedAligned.jar" /> <option name="push" value="art-gtest-jars-MyClassNatives.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-MyClassNatives.jar" /> <option name="push" value="art-gtest-jars-Nested.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-Nested.jar" /> <option name="push" value="art-gtest-jars-ProfileTestMultiDex.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-ProfileTestMultiDex.jar" /> @@ -62,6 +67,9 @@ </target_preparer> <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-timeout" value="5m" /> + <!-- Set this to the same as native-test-timeout to effectively disable per test case timeout. --> + <option name="test-case-timeout" value="5m" /> <option name="native-test-device-path" value="/data/local/tmp/art_standalone_dex2oat_tests" /> <option name="module-name" value="art_standalone_dex2oat_tests" /> <option name="ld-library-path-32" value="/apex/com.android.art/lib" /> diff --git a/dex2oat/common_compiler_driver_test.cc b/dex2oat/common_compiler_driver_test.cc index 7e71976729..7a1d11c298 100644 --- a/dex2oat/common_compiler_driver_test.cc +++ b/dex2oat/common_compiler_driver_test.cc @@ -21,6 +21,7 @@ #include "base/casts.h" #include "base/timing_logger.h" #include "dex/quick_compiler_callbacks.h" +#include "dex/verification_results.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "utils/atomic_dex_ref_map-inl.h" @@ -46,9 +47,7 @@ void CommonCompilerDriverTest::CompileAll(jobject class_loader, // Verification results in the `callback_` should not be used during compilation. down_cast<QuickCompilerCallbacks*>(callbacks_.get())->SetVerificationResults( reinterpret_cast<VerificationResults*>(inaccessible_page_)); - compiler_options_->verification_results_ = verification_results_.get(); compiler_driver_->CompileAll(class_loader, dex_files, timings); - compiler_options_->verification_results_ = nullptr; down_cast<QuickCompilerCallbacks*>(callbacks_.get())->SetVerificationResults( verification_results_.get()); @@ -89,6 +88,7 @@ void CommonCompilerDriverTest::CreateCompilerDriver() { compiler_options_->image_classes_.swap(*GetImageClasses()); compiler_options_->profile_compilation_info_ = GetProfileCompilationInfo(); compiler_driver_.reset(new CompilerDriver(compiler_options_.get(), + verification_results_.get(), compiler_kind_, number_of_threads_, /* swap_fd= */ -1)); @@ -97,6 +97,7 @@ void CommonCompilerDriverTest::CreateCompilerDriver() { void CommonCompilerDriverTest::SetUpRuntimeOptions(RuntimeOptions* options) { CommonCompilerTest::SetUpRuntimeOptions(options); + verification_results_.reset(new VerificationResults()); QuickCompilerCallbacks* callbacks = new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileApp); callbacks->SetVerificationResults(verification_results_.get()); @@ -121,6 +122,7 @@ void CommonCompilerDriverTest::TearDown() { } image_reservation_.Reset(); compiler_driver_.reset(); + verification_results_.reset(); CommonCompilerTest::TearDown(); } diff --git a/dex2oat/common_compiler_driver_test.h b/dex2oat/common_compiler_driver_test.h index 1ff88e5b61..b49d18fa10 100644 --- a/dex2oat/common_compiler_driver_test.h +++ b/dex2oat/common_compiler_driver_test.h @@ -31,6 +31,7 @@ class CompilerDriver; class DexFile; class ProfileCompilationInfo; class TimingLogger; +class VerificationResults; class CommonCompilerDriverTest : public CommonCompilerTest { public: @@ -62,6 +63,7 @@ class CommonCompilerDriverTest : public CommonCompilerTest { size_t number_of_threads_ = 2u; + std::unique_ptr<VerificationResults> verification_results_; std::unique_ptr<CompilerDriver> compiler_driver_; private: diff --git a/dex2oat/dex/quick_compiler_callbacks.cc b/dex2oat/dex/quick_compiler_callbacks.cc index 11223ff3c8..7611374561 100644 --- a/dex2oat/dex/quick_compiler_callbacks.cc +++ b/dex2oat/dex/quick_compiler_callbacks.cc @@ -28,6 +28,12 @@ void QuickCompilerCallbacks::AddUncompilableMethod(MethodReference ref) { } } +void QuickCompilerCallbacks::AddUncompilableClass(ClassReference ref) { + if (verification_results_ != nullptr) { + verification_results_->AddUncompilableClass(ref); + } +} + void QuickCompilerCallbacks::ClassRejected(ClassReference ref) { if (verification_results_ != nullptr) { verification_results_->AddRejectedClass(ref); diff --git a/dex2oat/dex/quick_compiler_callbacks.h b/dex2oat/dex/quick_compiler_callbacks.h index 4447e02ccb..a7a482b4ca 100644 --- a/dex2oat/dex/quick_compiler_callbacks.h +++ b/dex2oat/dex/quick_compiler_callbacks.h @@ -34,6 +34,7 @@ class QuickCompilerCallbacks final : public CompilerCallbacks { ~QuickCompilerCallbacks() { } void AddUncompilableMethod(MethodReference ref) override; + void AddUncompilableClass(ClassReference ref) override; void ClassRejected(ClassReference ref) override; diff --git a/dex2oat/dex/verification_results.cc b/dex2oat/dex/verification_results.cc new file mode 100644 index 0000000000..bc0329a74e --- /dev/null +++ b/dex2oat/dex/verification_results.cc @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "verification_results.h" + +#include <android-base/logging.h> + +#include "base/mutex-inl.h" +#include "base/stl_util.h" +#include "dex/class_accessor-inl.h" +#include "runtime.h" +#include "thread-current-inl.h" +#include "thread.h" + +namespace art { + +VerificationResults::VerificationResults() + : uncompilable_methods_lock_("compiler uncompilable methods lock"), + rejected_classes_lock_("compiler rejected classes lock") {} + +// Non-inline version of the destructor, as it does some implicit work not worth +// inlining. +VerificationResults::~VerificationResults() {} + +void VerificationResults::AddRejectedClass(ClassReference ref) { + { + WriterMutexLock mu(Thread::Current(), rejected_classes_lock_); + rejected_classes_.insert(ref); + } + DCHECK(IsClassRejected(ref)); +} + +bool VerificationResults::IsClassRejected(ClassReference ref) const { + ReaderMutexLock mu(Thread::Current(), rejected_classes_lock_); + return rejected_classes_.find(ref) != rejected_classes_.end(); +} + +void VerificationResults::AddUncompilableMethod(MethodReference ref) { + { + WriterMutexLock mu(Thread::Current(), uncompilable_methods_lock_); + uncompilable_methods_.insert(ref); + } + DCHECK(IsUncompilableMethod(ref)); +} + +void VerificationResults::AddUncompilableClass(ClassReference ref) { + const DexFile& dex_file = *ref.dex_file; + const dex::ClassDef& class_def = dex_file.GetClassDef(ref.ClassDefIdx()); + WriterMutexLock mu(Thread::Current(), uncompilable_methods_lock_); + ClassAccessor accessor(dex_file, class_def); + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + MethodReference method_ref(&dex_file, method.GetIndex()); + uncompilable_methods_.insert(method_ref); + } +} + +bool VerificationResults::IsUncompilableMethod(MethodReference ref) const { + ReaderMutexLock mu(Thread::Current(), uncompilable_methods_lock_); + return uncompilable_methods_.find(ref) != uncompilable_methods_.end(); +} + + +} // namespace art diff --git a/dex2oat/dex/verification_results.h b/dex2oat/dex/verification_results.h new file mode 100644 index 0000000000..0a85e438dd --- /dev/null +++ b/dex2oat/dex/verification_results.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_DEX2OAT_DEX_VERIFICATION_RESULTS_H_ +#define ART_DEX2OAT_DEX_VERIFICATION_RESULTS_H_ + +#include <set> + +#include "base/macros.h" +#include "base/mutex.h" +#include "dex/class_reference.h" +#include "dex/method_reference.h" + +namespace art { + +namespace verifier { +class VerifierDepsTest; +} // namespace verifier + +// Used by CompilerCallbacks to track verification information from the Runtime. +class VerificationResults { + public: + VerificationResults(); + ~VerificationResults(); + + void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_); + bool IsClassRejected(ClassReference ref) const REQUIRES(!rejected_classes_lock_); + + void AddUncompilableClass(ClassReference ref) REQUIRES(!uncompilable_methods_lock_); + void AddUncompilableMethod(MethodReference ref) REQUIRES(!uncompilable_methods_lock_); + bool IsUncompilableMethod(MethodReference ref) const REQUIRES(!uncompilable_methods_lock_); + + private: + // TODO: External locking during CompilerDriver::PreCompile(), no locking during compilation. + mutable ReaderWriterMutex uncompilable_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + std::set<MethodReference> uncompilable_methods_ GUARDED_BY(uncompilable_methods_lock_); + + // Rejected classes. + // TODO: External locking during CompilerDriver::PreCompile(), no locking during compilation. + mutable ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_); + + friend class verifier::VerifierDepsTest; +}; + +} // namespace art + +#endif // ART_DEX2OAT_DEX_VERIFICATION_RESULTS_H_ diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 9e6103b424..6c713721d8 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -20,6 +20,7 @@ #include <stdlib.h> #include <sys/stat.h> +#include <algorithm> #include <forward_list> #include <fstream> #include <iostream> @@ -39,6 +40,7 @@ #endif #include "android-base/parseint.h" +#include "android-base/properties.h" #include "android-base/scopeguard.h" #include "android-base/stringprintf.h" #include "android-base/strings.h" @@ -65,6 +67,7 @@ #include "base/zip_archive.h" #include "class_linker.h" #include "class_loader_context.h" +#include "class_root-inl.h" #include "cmdline_parser.h" #include "compiler.h" #include "compiler_callbacks.h" @@ -108,7 +111,6 @@ #include "stream/file_output_stream.h" #include "vdex_file.h" #include "verifier/verifier_deps.h" -#include "well_known_classes.h" namespace art { @@ -607,8 +609,13 @@ class Dex2Oat final { } void ParseInstructionSetVariant(const std::string& option, ParserOptions* parser_options) { - compiler_options_->instruction_set_features_ = InstructionSetFeatures::FromVariant( - compiler_options_->instruction_set_, option, &parser_options->error_msg); + if (kIsTargetBuild) { + compiler_options_->instruction_set_features_ = InstructionSetFeatures::FromVariantAndHwcap( + compiler_options_->instruction_set_, option, &parser_options->error_msg); + } else { + compiler_options_->instruction_set_features_ = InstructionSetFeatures::FromVariant( + compiler_options_->instruction_set_, option, &parser_options->error_msg); + } if (compiler_options_->instruction_set_features_ == nullptr) { Usage("%s", parser_options->error_msg.c_str()); } @@ -718,8 +725,15 @@ class Dex2Oat final { if (!IsBootImage() && boot_image_filename_.empty()) { DCHECK(!IsBootImageExtension()); - boot_image_filename_ = - GetDefaultBootImageLocation(android_root_, /*deny_art_apex_data_files=*/false); + if (std::any_of(runtime_args_.begin(), runtime_args_.end(), [](const char* arg) { + return android::base::StartsWith(arg, "-Xbootclasspath:"); + })) { + LOG(WARNING) << "--boot-image is not specified while -Xbootclasspath is specified. Running " + "dex2oat in imageless mode"; + } else { + boot_image_filename_ = + GetDefaultBootImageLocation(android_root_, /*deny_art_apex_data_files=*/false); + } } if (dex_filenames_.empty() && zip_fd_ == -1) { @@ -774,9 +788,12 @@ class Dex2Oat final { compiler_options_->multi_image_ = IsBootImage() || IsBootImageExtension(); } // On target we support generating a single image for the primary boot image. - if (!kIsTargetBuild) { + if (!kIsTargetBuild && !force_allow_oj_inlines_) { if (IsBootImage() && !compiler_options_->multi_image_) { - Usage("--single-image specified for primary boot image on host"); + Usage( + "--single-image specified for primary boot image on host. Please " + "use the flag --force-allow-oj-inlines and do not distribute " + "binaries."); } } if (IsAppImage() && compiler_options_->multi_image_) { @@ -839,9 +856,7 @@ class Dex2Oat final { // Set the compilation target's implicit checks options. switch (compiler_options_->GetInstructionSet()) { case InstructionSet::kArm64: - // TODO: Implicit suspend checks are currently disabled to facilitate search - // for unrelated memory use regressions. Bug: 213757852. - compiler_options_->implicit_suspend_checks_ = false; + compiler_options_->implicit_suspend_checks_ = true; FALLTHROUGH_INTENDED; case InstructionSet::kArm: case InstructionSet::kThumb2: @@ -957,7 +972,7 @@ class Dex2Oat final { compiler_options_->GetNativeDebuggable()); key_value_store_->Put(OatHeader::kCompilerFilter, CompilerFilter::NameOfFilter(compiler_options_->GetCompilerFilter())); - key_value_store_->Put(OatHeader::kConcurrentCopying, kUseReadBarrier); + key_value_store_->Put(OatHeader::kConcurrentCopying, gUseReadBarrier); if (invocation_file_.get() != -1) { std::ostringstream oss; for (int i = 0; i < argc; ++i) { @@ -1029,7 +1044,13 @@ class Dex2Oat final { original_argv = argv; Locks::Init(); - InitLogging(argv, Runtime::Abort); + + // Microdroid doesn't support logd logging, so don't override there. + if (android::base::GetProperty("ro.hardware", "") == "microdroid") { + android::base::SetAborter(Runtime::Abort); + } else { + InitLogging(argv, Runtime::Abort); + } compiler_options_.reset(new CompilerOptions()); @@ -1095,6 +1116,19 @@ class Dex2Oat final { AssignIfExists(args, M::PublicSdk, &public_sdk_); AssignIfExists(args, M::ApexVersions, &apex_versions_argument_); + // Check for phenotype flag to override compact_dex_level_, if it isn't "none" already. + // TODO(b/256664509): Clean this up. + if (compact_dex_level_ != CompactDexLevel::kCompactDexLevelNone) { + std::string ph_disable_compact_dex = + android::base::GetProperty(kPhDisableCompactDex, "false"); + if (ph_disable_compact_dex == "true") { + LOG(WARNING) + << "Overriding --compact-dex-level due to " + "persist.device_config.runtime_native_boot.disable_compact_dex set to `true`"; + compact_dex_level_ = CompactDexLevel::kCompactDexLevelNone; + } + } + AssignIfExists(args, M::Backend, &compiler_kind_); parser_options->requested_specific_compiler = args.Exists(M::Backend); @@ -1187,7 +1221,7 @@ class Dex2Oat final { if (!parser_options->boot_image_filename.empty()) { Usage("Option --boot-image and --force-jit-zygote cannot be specified together"); } - parser_options->boot_image_filename = "boot.art:/nonx/boot-framework.art"; + parser_options->boot_image_filename = GetJitZygoteBootImageLocation(); } // If we have a profile, change the default compiler filter to speed-profile @@ -1400,24 +1434,36 @@ class Dex2Oat final { } } - void LoadClassProfileDescriptors() { + void LoadImageClassDescriptors() { if (!IsImage()) { return; } + HashSet<std::string> image_classes; if (DoProfileGuidedOptimizations()) { // TODO: The following comment looks outdated or misplaced. // Filter out class path classes since we don't want to include these in the image. - HashSet<std::string> image_classes = profile_compilation_info_->GetClassDescriptors( + image_classes = profile_compilation_info_->GetClassDescriptors( compiler_options_->dex_files_for_oat_file_); VLOG(compiler) << "Loaded " << image_classes.size() << " image class descriptors from profile"; - if (VLOG_IS_ON(compiler)) { - for (const std::string& s : image_classes) { - LOG(INFO) << "Image class " << s; + } else if (compiler_options_->IsBootImage() || compiler_options_->IsBootImageExtension()) { + // If we are compiling a boot image but no profile is provided, include all classes in the + // image. This is to match pre-boot image extension work where we would load all boot image + // extension classes at startup. + for (const DexFile* dex_file : compiler_options_->dex_files_for_oat_file_) { + for (uint32_t i = 0; i < dex_file->NumClassDefs(); i++) { + const dex::ClassDef& class_def = dex_file->GetClassDef(i); + const char* descriptor = dex_file->GetClassDescriptor(class_def); + image_classes.insert(descriptor); } } - compiler_options_->image_classes_.swap(image_classes); } + if (VLOG_IS_ON(compiler)) { + for (const std::string& s : image_classes) { + LOG(INFO) << "Image class " << s; + } + } + compiler_options_->image_classes_ = std::move(image_classes); } // Set up the environment for compilation. Includes starting the runtime and loading/opening the @@ -1469,16 +1515,12 @@ class Dex2Oat final { return dex2oat::ReturnCode::kOther; } dex_files_per_oat_file_.push_back(MakeNonOwningPointerVector(opened_dex_files)); - if (opened_dex_files_map.empty()) { - DCHECK(opened_dex_files.empty()); - } else { - for (MemMap& map : opened_dex_files_map) { - opened_dex_files_maps_.push_back(std::move(map)); - } - for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files) { - dex_file_oat_index_map_.insert(std::make_pair(dex_file.get(), i)); - opened_dex_files_.push_back(std::move(dex_file)); - } + for (MemMap& map : opened_dex_files_map) { + opened_dex_files_maps_.push_back(std::move(map)); + } + for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files) { + dex_file_oat_index_map_.insert(std::make_pair(dex_file.get(), i)); + opened_dex_files_.push_back(std::move(dex_file)); } } } @@ -1798,7 +1840,7 @@ class Dex2Oat final { // 2. not verifying a vdex file, and // 3. using multidex, and // 4. not doing any AOT compilation. - // This means extract, no-vdex verify, and quicken, will use the individual compilation + // This means no-vdex verify will use the individual compilation // mode (to reduce RAM used by the compiler). return compile_individually_ && (!IsImage() && !use_existing_vdex_ && @@ -1815,7 +1857,7 @@ class Dex2Oat final { } // Set up and create the compiler driver and then invoke it to compile all the dex files. - jobject Compile() { + jobject Compile() REQUIRES(!Locks::mutator_lock_) { ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); TimingLogger::ScopedTiming t("dex2oat Compile", timings_); @@ -1864,7 +1906,7 @@ class Dex2Oat final { } } - if (android::base::StartsWith(dex_location, filter.c_str())) { + if (android::base::StartsWith(dex_location, filter)) { VLOG(compiler) << "Disabling inlining from " << dex_file->GetLocation(); no_inline_from_dex_files.push_back(dex_file); break; @@ -1879,6 +1921,7 @@ class Dex2Oat final { compiler_options_->profile_compilation_info_ = profile_compilation_info_.get(); driver_.reset(new CompilerDriver(compiler_options_.get(), + verification_results_.get(), compiler_kind_, thread_count_, swap_fd_)); @@ -1891,9 +1934,7 @@ class Dex2Oat final { const bool compile_individually = ShouldCompileDexFilesIndividually(); if (compile_individually) { - // Set the compiler driver in the callbacks so that we can avoid re-verification. This not - // only helps performance but also prevents reverifying quickened bytecodes. Attempting - // verify quickened bytecode causes verification failures. + // Set the compiler driver in the callbacks so that we can avoid re-verification. // Only set the compiler filter if we are doing separate compilation since there is a bit // of overhead when checking if a class was previously verified. callbacks_->SetDoesClassUnloading(true, driver_.get()); @@ -1964,7 +2005,6 @@ class Dex2Oat final { timings_, &compiler_options_->image_classes_); callbacks_->SetVerificationResults(nullptr); // Should not be needed anymore. - compiler_options_->verification_results_ = verification_results_.get(); driver_->CompileAll(class_loader, dex_files, timings_); driver_->FreeThreadPools(); return class_loader; @@ -2538,16 +2578,16 @@ class Dex2Oat final { } bool PreparePreloadedClasses() { - preloaded_classes_ = std::make_unique<HashSet<std::string>>(); if (!preloaded_classes_fds_.empty()) { for (int fd : preloaded_classes_fds_) { - if (!ReadCommentedInputFromFd(fd, nullptr, preloaded_classes_.get())) { + if (!ReadCommentedInputFromFd(fd, nullptr, &compiler_options_->preloaded_classes_)) { return false; } } } else { for (const std::string& file : preloaded_classes_files_) { - if (!ReadCommentedInputFromFile(file.c_str(), nullptr, preloaded_classes_.get())) { + if (!ReadCommentedInputFromFile( + file.c_str(), nullptr, &compiler_options_->preloaded_classes_)) { return false; } } @@ -2633,6 +2673,7 @@ class Dex2Oat final { bool do_oat_writer_layout = DoDexLayoutOptimizations() || DoOatLayoutOptimizations(); oat_writers_.emplace_back(new linker::OatWriter( *compiler_options_, + verification_results_.get(), timings_, do_oat_writer_layout ? profile_compilation_info_.get() : nullptr, compact_dex_level_)); @@ -2731,19 +2772,14 @@ class Dex2Oat final { interpreter::UnstartedRuntime::Initialize(); Thread* self = Thread::Current(); + runtime_->GetClassLinker()->RunEarlyRootClinits(self); + InitializeIntrinsics(); runtime_->RunRootClinits(self); // Runtime::Create acquired the mutator_lock_ that is normally given away when we // Runtime::Start, give it away now so that we don't starve GC. self->TransitionFromRunnableToSuspended(ThreadState::kNative); - // Now that we are in native state, initialize well known classes and - // intrinsics if we don't have a boot image. - WellKnownClasses::Init(self->GetJniEnv()); - if (IsBootImage() || runtime_->GetHeap()->GetBootImageSpaces().empty()) { - InitializeIntrinsics(); - } - WatchDog::SetRuntime(runtime_.get()); return true; @@ -2786,7 +2822,7 @@ class Dex2Oat final { template <typename T> static bool ReadCommentedInputFromFile( const char* input_filename, std::function<std::string(const char*)>* process, T* output) { - auto input_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(input_filename, "r"), fclose}; + auto input_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(input_filename, "re"), fclose}; if (!input_file) { LOG(ERROR) << "Failed to open input file " << input_filename; return false; @@ -2942,7 +2978,6 @@ class Dex2Oat final { const char* dirty_image_objects_filename_; int dirty_image_objects_fd_; std::unique_ptr<HashSet<std::string>> dirty_image_objects_; - std::unique_ptr<HashSet<std::string>> preloaded_classes_; std::unique_ptr<std::vector<std::string>> passes_to_run_; bool is_host_; std::string android_root_; @@ -3054,8 +3089,9 @@ class ScopedGlobalRef { jobject obj_; }; -static dex2oat::ReturnCode DoCompilation(Dex2Oat& dex2oat) { - dex2oat.LoadClassProfileDescriptors(); +static dex2oat::ReturnCode DoCompilation(Dex2Oat& dex2oat) REQUIRES(!Locks::mutator_lock_) { + Locks::mutator_lock_->AssertNotHeld(Thread::Current()); + dex2oat.LoadImageClassDescriptors(); jobject class_loader = dex2oat.Compile(); // Keep the class loader that was used for compilation live for the rest of the compilation // process. diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc index 2978fb4092..451890555a 100644 --- a/dex2oat/dex2oat_image_test.cc +++ b/dex2oat/dex2oat_image_test.cc @@ -431,6 +431,7 @@ TEST_F(Dex2oatImageTest, TestExtension) { relocate, /*executable=*/ true, /*extra_reservation_size=*/ 0u, + /*allow_in_memory_compilation=*/ true, &boot_image_spaces, &extra_reservation); }; diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc index 1f9138e8ff..4da219897b 100644 --- a/dex2oat/dex2oat_options.cc +++ b/dex2oat/dex2oat_options.cc @@ -55,6 +55,7 @@ using Parser = CmdlineParser<Dex2oatArgumentMap, Dex2oatArgumentMap::Key>; using Builder = Parser::Builder; static void AddInputMappings(Builder& builder) { + // clang-format off builder. Define("--dex-file=_") .WithType<std::vector<std::string>>().AppendValues() @@ -102,9 +103,11 @@ static void AddInputMappings(Builder& builder) { " class path components' paths)\n" "Default: $ANDROID_ROOT/system/framework/boot.art") .IntoKey(M::BootImage); + // clang-format on } static void AddGeneratedArtifactMappings(Builder& builder) { + // clang-format off builder. Define("--input-vdex-fd=_") .WithType<int>() @@ -156,9 +159,11 @@ static void AddGeneratedArtifactMappings(Builder& builder) { "specified by --oat-fd.\n" "Eg: --oat-location=/data/dalvik-cache/system@app@Calculator.apk.oat") .IntoKey(M::OatLocation); + // clang-format on } static void AddImageMappings(Builder& builder) { + // clang-format off builder. Define("--image=_") .WithType<std::string>() @@ -214,9 +219,11 @@ static void AddImageMappings(Builder& builder) { .WithHelp("Which format to store the image Defaults to uncompressed. Eg:" " --image-format=lz4") .IntoKey(M::ImageFormat); + // clang-format on } static void AddSwapMappings(Builder& builder) { + // clang-format off builder. Define("--swap-file=_") .WithType<std::string>() @@ -234,9 +241,11 @@ static void AddSwapMappings(Builder& builder) { .WithType<unsigned int>() .WithHelp("specifies the minimum number of dex file to allow the use of swap.") .IntoKey(M::SwapDexCountThreshold); + // clang-format on } static void AddCompilerMappings(Builder& builder) { + // clang-format off builder. Define("--run-passes=_") .WithType<std::string>() @@ -264,9 +273,11 @@ static void AddCompilerMappings(Builder& builder) { .WithType<std::vector<int>>().AppendValues() .WithHelp("Specify files containing list of classes preloaded in the zygote.") .IntoKey(M::PreloadedClassesFds); + // clang-format on } static void AddTargetMappings(Builder& builder) { + // clang-format off builder. Define("--instruction-set=_") .WithType<InstructionSet>() @@ -288,6 +299,7 @@ static void AddTargetMappings(Builder& builder) { "Example: --instruction-set-features=div\n" "Default: default") .IntoKey(M::TargetInstructionSetFeatures); + // clang-format on } Parser CreateDex2oatArgumentParser() { @@ -300,6 +312,7 @@ Parser CreateDex2oatArgumentParser() { AddCompilerMappings(*parser_builder); AddTargetMappings(*parser_builder); + // clang-format off parser_builder-> Define({"--watch-dog", "--no-watch-dog"}) .WithHelp("Enable or disable the watchdog timer.") @@ -448,7 +461,12 @@ Parser CreateDex2oatArgumentParser() { .IntoKey(M::ForceJitZygote) .Define("--force-palette-compilation-hooks") .WithHelp("Force PaletteNotify{Start,End}Dex2oatCompilation calls.") - .IntoKey(M::ForcePaletteCompilationHooks); + .IntoKey(M::ForcePaletteCompilationHooks) + .Ignore({ + "--comments=_", + "--cache-info-fd=_", // Handled in mark_compact.cc. + }); + // clang-format on AddCompilerOptionsArgumentParserOptions<Dex2oatArgumentMap>(*parser_builder); diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 9ee532a4e3..ca76254fc1 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -74,6 +74,7 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { bool use_fd = false) { std::unique_ptr<File> oat_file; std::vector<std::string> args; + args.reserve(dex_locations.size() + extra_args.size() + 6); // Add dex file args. for (const std::string& dex_location : dex_locations) { args.push_back("--dex-file=" + dex_location); @@ -103,14 +104,13 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { return status; } - ::testing::AssertionResult GenerateOdexForTest( - const std::string& dex_location, - const std::string& odex_location, - CompilerFilter::Filter filter, - const std::vector<std::string>& extra_args = {}, - bool expect_success = true, - bool use_fd = false, - bool use_zip_fd = false) WARN_UNUSED { + ::testing::AssertionResult GenerateOdexForTest(const std::string& dex_location, + const std::string& odex_location, + CompilerFilter::Filter filter, + const std::vector<std::string>& extra_args = {}, + bool expect_success = true, + bool use_fd = false, + bool use_zip_fd = false) WARN_UNUSED { return GenerateOdexForTest(dex_location, odex_location, filter, @@ -124,47 +124,42 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { bool test_accepts_odex_file_on_failure = false; template <typename T> - ::testing::AssertionResult GenerateOdexForTest( - const std::string& dex_location, - const std::string& odex_location, - CompilerFilter::Filter filter, - const std::vector<std::string>& extra_args, - bool expect_success, - bool use_fd, - bool use_zip_fd, - T check_oat) WARN_UNUSED { + ::testing::AssertionResult GenerateOdexForTest(const std::string& dex_location, + const std::string& odex_location, + CompilerFilter::Filter filter, + const std::vector<std::string>& extra_args, + bool expect_success, + bool use_fd, + bool use_zip_fd, + T check_oat) WARN_UNUSED { std::vector<std::string> dex_locations; if (use_zip_fd) { std::string loc_arg = "--zip-location=" + dex_location; - CHECK(std::any_of(extra_args.begin(), - extra_args.end(), - [&](const std::string& s) { return s == loc_arg; })); - CHECK(std::any_of(extra_args.begin(), - extra_args.end(), - [](const std::string& s) { return StartsWith(s, "--zip-fd="); })); + CHECK(std::any_of(extra_args.begin(), extra_args.end(), [&](const std::string& s) { + return s == loc_arg; + })); + CHECK(std::any_of(extra_args.begin(), extra_args.end(), [](const std::string& s) { + return StartsWith(s, "--zip-fd="); + })); } else { dex_locations.push_back(dex_location); } std::string error_msg; - int status = GenerateOdexForTestWithStatus(dex_locations, - odex_location, - filter, - &error_msg, - extra_args, - use_fd); + int status = GenerateOdexForTestWithStatus( + dex_locations, odex_location, filter, &error_msg, extra_args, use_fd); bool success = (WIFEXITED(status) && WEXITSTATUS(status) == 0); if (expect_success) { if (!success) { - return ::testing::AssertionFailure() - << "Failed to compile odex: " << error_msg << std::endl << output_; + return ::testing::AssertionFailure() << "Failed to compile odex: " << error_msg << std::endl + << output_; } // Verify the odex file was generated as expected. - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - odex_location.c_str(), - odex_location.c_str(), - /*executable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + odex_location, + odex_location, + /*executable=*/false, + /*low_4gb=*/false, dex_location, &error_msg)); if (odex_file == nullptr) { @@ -182,11 +177,11 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { if (!test_accepts_odex_file_on_failure) { // Verify there's no loadable odex file. - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - odex_location.c_str(), - odex_location.c_str(), - /*executable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + odex_location, + odex_location, + /*executable=*/false, + /*low_4gb=*/false, dex_location, &error_msg)); if (odex_file != nullptr) { @@ -212,9 +207,8 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { // to what's already huge test methods). class Dex2oatWithExpectedFilterTest : public Dex2oatTest { protected: - void CheckFilter( - CompilerFilter::Filter expected ATTRIBUTE_UNUSED, - CompilerFilter::Filter actual) override { + void CheckFilter(CompilerFilter::Filter expected ATTRIBUTE_UNUSED, + CompilerFilter::Filter actual) override { EXPECT_EQ(expected_filter_, actual); } @@ -297,27 +291,27 @@ class Dex2oatSwapTest : public Dex2oatTest { }; TEST_F(Dex2oatSwapTest, DoNotUseSwapDefaultSingleSmall) { - RunTest(/*use_fd=*/ false, /*expect_use=*/ false); - RunTest(/*use_fd=*/ true, /*expect_use=*/ false); + RunTest(/*use_fd=*/false, /*expect_use=*/false); + RunTest(/*use_fd=*/true, /*expect_use=*/false); } TEST_F(Dex2oatSwapTest, DoNotUseSwapSingle) { - RunTest(/*use_fd=*/ false, /*expect_use=*/ false, { "--swap-dex-size-threshold=0" }); - RunTest(/*use_fd=*/ true, /*expect_use=*/ false, { "--swap-dex-size-threshold=0" }); + RunTest(/*use_fd=*/false, /*expect_use=*/false, {"--swap-dex-size-threshold=0"}); + RunTest(/*use_fd=*/true, /*expect_use=*/false, {"--swap-dex-size-threshold=0"}); } TEST_F(Dex2oatSwapTest, DoNotUseSwapSmall) { - RunTest(/*use_fd=*/ false, /*expect_use=*/ false, { "--swap-dex-count-threshold=0" }); - RunTest(/*use_fd=*/ true, /*expect_use=*/ false, { "--swap-dex-count-threshold=0" }); + RunTest(/*use_fd=*/false, /*expect_use=*/false, {"--swap-dex-count-threshold=0"}); + RunTest(/*use_fd=*/true, /*expect_use=*/false, {"--swap-dex-count-threshold=0"}); } TEST_F(Dex2oatSwapTest, DoUseSwapSingleSmall) { - RunTest(/*use_fd=*/ false, - /*expect_use=*/ true, - { "--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0" }); - RunTest(/*use_fd=*/ true, - /*expect_use=*/ true, - { "--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0" }); + RunTest(/*use_fd=*/false, + /*expect_use=*/true, + {"--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0"}); + RunTest(/*use_fd=*/true, + /*expect_use=*/true, + {"--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0"}); } class Dex2oatSwapUseTest : public Dex2oatSwapTest { @@ -342,7 +336,7 @@ class Dex2oatSwapUseTest : public Dex2oatSwapTest { void GrabResult1() { if (!kIsTargetBuild) { native_alloc_1_ = ParseNativeAlloc(); - swap_1_ = ParseSwap(/*expected=*/ false); + swap_1_ = ParseSwap(/*expected=*/false); } else { native_alloc_1_ = std::numeric_limits<size_t>::max(); swap_1_ = 0; @@ -352,7 +346,7 @@ class Dex2oatSwapUseTest : public Dex2oatSwapTest { void GrabResult2() { if (!kIsTargetBuild) { native_alloc_2_ = ParseNativeAlloc(); - swap_2_ = ParseSwap(/*expected=*/ true); + swap_2_ = ParseSwap(/*expected=*/true); } else { native_alloc_2_ = 0; swap_2_ = std::numeric_limits<size_t>::max(); @@ -423,16 +417,16 @@ TEST_F(Dex2oatSwapUseTest, CheckSwapUsage) { TEST_DISABLED_FOR_X86(); TEST_DISABLED_FOR_X86_64(); - RunTest(/*use_fd=*/ false, - /*expect_use=*/ false); + RunTest(/*use_fd=*/false, + /*expect_use=*/false); GrabResult1(); std::string output_1 = output_; output_ = ""; - RunTest(/*use_fd=*/ false, - /*expect_use=*/ true, - { "--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0" }); + RunTest(/*use_fd=*/false, + /*expect_use=*/true, + {"--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0"}); GrabResult2(); std::string output_2 = output_; @@ -478,7 +472,6 @@ class Dex2oatVeryLargeTest : public Dex2oatTest { CheckResult(dex_location, odex_location, app_image_file, - filter, expected_filter, expect_large, expect_downgrade); @@ -487,7 +480,6 @@ class Dex2oatVeryLargeTest : public Dex2oatTest { void CheckResult(const std::string& dex_location, const std::string& odex_location, const std::string& app_image_file, - CompilerFilter::Filter filter, CompilerFilter::Filter expected_filter, bool expect_large, bool expect_downgrade) { @@ -496,11 +488,11 @@ class Dex2oatVeryLargeTest : public Dex2oatTest { } // Host/target independent checks. std::string error_msg; - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - odex_location.c_str(), - odex_location.c_str(), - /*executable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + odex_location, + odex_location, + /*executable=*/false, + /*low_4gb=*/false, dex_location, &error_msg)); ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; @@ -525,9 +517,7 @@ class Dex2oatVeryLargeTest : public Dex2oatTest { } // If the input filter was "below," it should have been used. - if (!CompilerFilter::IsAsGoodAs(CompilerFilter::kExtract, filter)) { - EXPECT_EQ(odex_file->GetCompilerFilter(), expected_filter); - } + EXPECT_EQ(odex_file->GetCompilerFilter(), expected_filter); // If expect large, make sure the app image isn't generated or is empty. if (file != nullptr) { @@ -582,18 +572,15 @@ class Dex2oatVeryLargeTest : public Dex2oatTest { TEST_F(Dex2oatVeryLargeTest, DontUseVeryLarge) { RunTest(CompilerFilter::kAssumeVerified, false, false); - RunTest(CompilerFilter::kExtract, false, false); RunTest(CompilerFilter::kSpeed, false, false); - RunTest(CompilerFilter::kAssumeVerified, false, false, { "--very-large-app-threshold=10000000" }); - RunTest(CompilerFilter::kExtract, false, false, { "--very-large-app-threshold=10000000" }); - RunTest(CompilerFilter::kSpeed, false, false, { "--very-large-app-threshold=10000000" }); + RunTest(CompilerFilter::kAssumeVerified, false, false, {"--very-large-app-threshold=10000000"}); + RunTest(CompilerFilter::kSpeed, false, false, {"--very-large-app-threshold=10000000"}); } TEST_F(Dex2oatVeryLargeTest, UseVeryLarge) { - RunTest(CompilerFilter::kAssumeVerified, true, false, { "--very-large-app-threshold=100" }); - RunTest(CompilerFilter::kExtract, true, false, { "--very-large-app-threshold=100" }); - RunTest(CompilerFilter::kSpeed, true, true, { "--very-large-app-threshold=100" }); + RunTest(CompilerFilter::kAssumeVerified, true, false, {"--very-large-app-threshold=100"}); + RunTest(CompilerFilter::kSpeed, true, true, {"--very-large-app-threshold=100"}); } // Regressin test for b/35665292. @@ -618,19 +605,18 @@ class Dex2oatLayoutTest : public Dex2oatTest { const char* location = dex_location.c_str(); std::string error_msg; std::vector<std::unique_ptr<const DexFile>> dex_files; - const ArtDexFileLoader dex_file_loader; + ArtDexFileLoader dex_file_loader(location); ASSERT_TRUE(dex_file_loader.Open( - location, location, /*verify=*/true, /*verify_checksum=*/true, &error_msg, &dex_files)); + /*verify=*/true, /*verify_checksum=*/true, &error_msg, &dex_files)); EXPECT_EQ(dex_files.size(), 1U); std::unique_ptr<const DexFile>& dex_file = dex_files[0]; - int profile_test_fd = open(test_profile.c_str(), - O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, - 0644); + int profile_test_fd = + open(test_profile.c_str(), O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, 0644); CHECK_GE(profile_test_fd, 0); ProfileCompilationInfo info; - std::vector<dex::TypeIndex> classes;; + std::vector<dex::TypeIndex> classes; for (size_t i = 0; i < num_classes; ++i) { classes.push_back(dex::TypeIndex(class_offset + 1 + i)); } @@ -661,12 +647,8 @@ class Dex2oatLayoutTest : public Dex2oatTest { copy.push_back("--app-image-file=" + app_image_file_name); } } - ASSERT_TRUE(GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::kSpeedProfile, - copy, - expect_success, - use_fd)); + ASSERT_TRUE(GenerateOdexForTest( + dex_location, odex_location, CompilerFilter::kSpeedProfile, copy, expect_success, use_fd)); if (app_image_file != nullptr) { ASSERT_EQ(app_image_file->FlushCloseOrErase(), 0) << "Could not flush and close art file"; } @@ -707,7 +689,7 @@ class Dex2oatLayoutTest : public Dex2oatTest { void RunTest(bool app_image) { std::string dex_location = GetScratchDir() + "/DexNoOat.jar"; std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex"; - std::string app_image_file = app_image ? (GetOdexDir() + "/DexOdexNoOat.art"): ""; + std::string app_image_file = app_image ? (GetOdexDir() + "/DexOdexNoOat.art") : ""; Copy(GetDexSrc2(), dex_location); uint32_t image_file_empty_profile = 0; @@ -715,8 +697,8 @@ class Dex2oatLayoutTest : public Dex2oatTest { CompileProfileOdex(dex_location, odex_location, app_image_file, - /*use_fd=*/ false, - /*num_profile_classes=*/ 0); + /*use_fd=*/false, + /*num_profile_classes=*/0); CheckValidity(); // Don't check the result since CheckResult relies on the class being in the profile. image_file_empty_profile = GetImageObjectSectionSize(app_image_file); @@ -728,8 +710,8 @@ class Dex2oatLayoutTest : public Dex2oatTest { CompileProfileOdex(dex_location, odex_location, app_image_file, - /*use_fd=*/ false, - /*num_profile_classes=*/ 1); + /*use_fd=*/false, + /*num_profile_classes=*/1); CheckValidity(); CheckResult(dex_location, odex_location, app_image_file); CheckCompilerFilter(dex_location, odex_location, CompilerFilter::Filter::kSpeedProfile); @@ -741,16 +723,15 @@ class Dex2oatLayoutTest : public Dex2oatTest { } } - void CheckCompilerFilter( - const std::string& dex_location, - const std::string& odex_location, - CompilerFilter::Filter expected_filter) { + void CheckCompilerFilter(const std::string& dex_location, + const std::string& odex_location, + CompilerFilter::Filter expected_filter) { std::string error_msg; - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - odex_location.c_str(), - odex_location.c_str(), - /*executable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + odex_location, + odex_location, + /*executable=*/false, + /*low_4gb=*/false, dex_location, &error_msg)); EXPECT_EQ(odex_file->GetCompilerFilter(), expected_filter); @@ -772,9 +753,9 @@ class Dex2oatLayoutTest : public Dex2oatTest { CompileProfileOdex(dex_location, odex_location, app_image_file_name, - /*use_fd=*/ true, - /*num_profile_classes=*/ 1, - { input_vdex, output_vdex }); + /*use_fd=*/true, + /*num_profile_classes=*/1, + {input_vdex, output_vdex}); EXPECT_GT(vdex_file1->GetLength(), 0u); } { @@ -784,10 +765,10 @@ class Dex2oatLayoutTest : public Dex2oatTest { CompileProfileOdex(dex_location, odex_location, app_image_file_name, - /*use_fd=*/ true, - /*num_profile_classes=*/ 1, - { input_vdex, output_vdex }, - /*expect_success=*/ true); + /*use_fd=*/true, + /*num_profile_classes=*/1, + {input_vdex, output_vdex}, + /*expect_success=*/true); EXPECT_GT(vdex_file2.GetFile()->GetLength(), 0u); } ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file"; @@ -799,20 +780,20 @@ class Dex2oatLayoutTest : public Dex2oatTest { const std::string& app_image_file_name) { // Host/target independent checks. std::string error_msg; - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - odex_location.c_str(), - odex_location.c_str(), - /*executable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + odex_location, + odex_location, + /*executable=*/false, + /*low_4gb=*/false, dex_location, &error_msg)); ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; const char* location = dex_location.c_str(); std::vector<std::unique_ptr<const DexFile>> dex_files; - const ArtDexFileLoader dex_file_loader; + ArtDexFileLoader dex_file_loader(location); ASSERT_TRUE(dex_file_loader.Open( - location, location, /*verify=*/ true, /*verify_checksum=*/ true, &error_msg, &dex_files)); + /*verify=*/true, /*verify_checksum=*/true, &error_msg, &dex_files)); EXPECT_EQ(dex_files.size(), 1U); std::unique_ptr<const DexFile>& old_dex_file = dex_files[0]; @@ -864,13 +845,9 @@ class Dex2oatLayoutTest : public Dex2oatTest { } }; -TEST_F(Dex2oatLayoutTest, TestLayout) { - RunTest(/*app_image=*/ false); -} +TEST_F(Dex2oatLayoutTest, TestLayout) { RunTest(/*app_image=*/false); } -TEST_F(Dex2oatLayoutTest, TestLayoutAppImage) { - RunTest(/*app_image=*/ true); -} +TEST_F(Dex2oatLayoutTest, TestLayoutAppImage) { RunTest(/*app_image=*/true); } TEST_F(Dex2oatLayoutTest, TestLayoutAppImageMissingBootImage) { std::string dex_location = GetScratchDir() + "/DexNoOat.jar"; @@ -881,18 +858,18 @@ TEST_F(Dex2oatLayoutTest, TestLayoutAppImageMissingBootImage) { CompileProfileOdex(dex_location, odex_location, app_image_file, - /*use_fd=*/ false, - /*num_profile_classes=*/ 1, - /*extra_args=*/ {"--boot-image=/nonx/boot.art"}, - /*expect_success=*/ true); + /*use_fd=*/false, + /*num_profile_classes=*/1, + /*extra_args=*/{"--boot-image=/nonx/boot.art"}, + /*expect_success=*/true); // Verify the odex file does not require an image. std::string error_msg; - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - odex_location.c_str(), - odex_location.c_str(), - /*executable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + odex_location, + odex_location, + /*executable=*/false, + /*low_4gb=*/false, dex_location, &error_msg)); ASSERT_TRUE(odex_file != nullptr) << "Could not open odex file: " << error_msg; @@ -990,9 +967,7 @@ TEST_F(Dex2oatLayoutTest, TestLayoutMultipleProfilesChecksumMismatch) { EXPECT_EQ(image_size_wrong_checksum, image_size_empty); } -TEST_F(Dex2oatLayoutTest, TestVdexLayout) { - RunTestVDex(); -} +TEST_F(Dex2oatLayoutTest, TestVdexLayout) { RunTestVDex(); } class Dex2oatWatchdogTest : public Dex2oatTest { protected: @@ -1007,16 +982,11 @@ class Dex2oatWatchdogTest : public Dex2oatTest { std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap"; copy.push_back("--swap-file=" + swap_location); copy.push_back("-j512"); // Excessive idle threads just slow down dex2oat. - ASSERT_TRUE(GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::kSpeed, - copy, - expect_success)); + ASSERT_TRUE(GenerateOdexForTest( + dex_location, odex_location, CompilerFilter::kSpeed, copy, expect_success)); } - std::string GetTestDexFileName() { - return GetDexSrc1(); - } + std::string GetTestDexFileName() { return GetDexSrc1(); } }; TEST_F(Dex2oatWatchdogTest, TestWatchdogOK) { @@ -1024,7 +994,7 @@ TEST_F(Dex2oatWatchdogTest, TestWatchdogOK) { RunTest(true); // Check with ten minutes. - RunTest(true, { "--watchdog-timeout=600000" }); + RunTest(true, {"--watchdog-timeout=600000"}); } TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) { @@ -1038,7 +1008,7 @@ TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) { test_accepts_odex_file_on_failure = true; // Check with ten milliseconds. - RunTest(false, { "--watchdog-timeout=10" }); + RunTest(false, {"--watchdog-timeout=10"}); } class Dex2oatReturnCodeTest : public Dex2oatTest { @@ -1050,16 +1020,11 @@ class Dex2oatReturnCodeTest : public Dex2oatTest { Copy(GetTestDexFileName(), dex_location); std::string error_msg; - return GenerateOdexForTestWithStatus({dex_location}, - odex_location, - CompilerFilter::kSpeed, - &error_msg, - extra_args); + return GenerateOdexForTestWithStatus( + {dex_location}, odex_location, CompilerFilter::kSpeed, &error_msg, extra_args); } - std::string GetTestDexFileName() { - return GetDexSrc1(); - } + std::string GetTestDexFileName() { return GetDexSrc1(); } }; class Dex2oatClassLoaderContextTest : public Dex2oatTest { @@ -1094,22 +1059,16 @@ class Dex2oatClassLoaderContextTest : public Dex2oatTest { CompilerFilter::kVerify, extra_args, expected_success, - /*use_fd=*/ false, - /*use_zip_fd=*/ false, + /*use_fd=*/false, + /*use_zip_fd=*/false, check_oat)); } - std::string GetUsedDexLocation() { - return GetScratchDir() + "/Context.jar"; - } + std::string GetUsedDexLocation() { return GetScratchDir() + "/Context.jar"; } - std::string GetUsedOatLocation() { - return GetOdexDir() + "/Context.odex"; - } + std::string GetUsedOatLocation() { return GetOdexDir() + "/Context.odex"; } - std::string GetUsedImageLocation() { - return GetOdexDir() + "/Context.art"; - } + std::string GetUsedImageLocation() { return GetOdexDir() + "/Context.art"; } const char* kEmptyClassPathKey = "PCL[]"; }; @@ -1131,8 +1090,8 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithOtherDexFiles) { std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("Nested"); std::string context = "PCL[" + dex_files[0]->GetLocation() + "]"; - std::string expected_classpath_key = "PCL[" + - dex_files[0]->GetLocation() + "*" + std::to_string(dex_files[0]->GetLocationChecksum()) + "]"; + std::string expected_classpath_key = "PCL[" + dex_files[0]->GetLocation() + "*" + + std::to_string(dex_files[0]->GetLocationChecksum()) + "]"; RunTest(context.c_str(), expected_classpath_key.c_str(), true); } @@ -1142,7 +1101,7 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithResourceOnlyDexFiles) { std::string context = "PCL[" + resource_only_classpath + "]"; // Expect an empty context because resource only dex files cannot be open. - RunTest(context.c_str(), kEmptyClassPathKey , /*expected_success*/ true); + RunTest(context.c_str(), kEmptyClassPathKey, /*expected_success*/ true); } TEST_F(Dex2oatClassLoaderContextTest, ContextWithNotExistentDexFiles) { @@ -1155,10 +1114,10 @@ TEST_F(Dex2oatClassLoaderContextTest, ChainContext) { std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Nested"); std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); - std::string context = "PCL[" + GetTestDexFileName("Nested") + "];" + - "DLC[" + GetTestDexFileName("MultiDex") + "]"; + std::string context = + "PCL[" + GetTestDexFileName("Nested") + "];" + "DLC[" + GetTestDexFileName("MultiDex") + "]"; std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "];" + - "DLC[" + CreateClassPathWithChecksums(dex_files2) + "]"; + "DLC[" + CreateClassPathWithChecksums(dex_files2) + "]"; RunTest(context.c_str(), expected_classpath_key.c_str(), true); } @@ -1167,10 +1126,10 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibrary) { std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Nested"); std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); - std::string context = "PCL[" + GetTestDexFileName("Nested") + "]" + - "{PCL[" + GetTestDexFileName("MultiDex") + "]}"; + std::string context = + "PCL[" + GetTestDexFileName("Nested") + "]" + "{PCL[" + GetTestDexFileName("MultiDex") + "]}"; std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" + - "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}"; + "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}"; RunTest(context.c_str(), expected_classpath_key.c_str(), true); } @@ -1178,49 +1137,49 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibraryAndImage) { std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Nested"); std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); - std::string context = "PCL[" + GetTestDexFileName("Nested") + "]" + - "{PCL[" + GetTestDexFileName("MultiDex") + "]}"; + std::string context = + "PCL[" + GetTestDexFileName("Nested") + "]" + "{PCL[" + GetTestDexFileName("MultiDex") + "]}"; std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" + - "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}"; + "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}"; RunTest(context.c_str(), expected_classpath_key.c_str(), - /*expected_success=*/ true, - /*use_second_source=*/ false, - /*generate_image=*/ true); + /*expected_success=*/true, + /*use_second_source=*/false, + /*generate_image=*/true); } TEST_F(Dex2oatClassLoaderContextTest, ContextWithSameSharedLibrariesAndImage) { std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Nested"); std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); - std::string context = "PCL[" + GetTestDexFileName("Nested") + "]" + - "{PCL[" + GetTestDexFileName("MultiDex") + "]" + - "#PCL[" + GetTestDexFileName("MultiDex") + "]}"; + std::string context = "PCL[" + GetTestDexFileName("Nested") + "]" + "{PCL[" + + GetTestDexFileName("MultiDex") + "]" + "#PCL[" + + GetTestDexFileName("MultiDex") + "]}"; std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" + - "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]" + - "#PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}"; + "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]" + + "#PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}"; RunTest(context.c_str(), expected_classpath_key.c_str(), - /*expected_success=*/ true, - /*use_second_source=*/ false, - /*generate_image=*/ true); + /*expected_success=*/true, + /*use_second_source=*/false, + /*generate_image=*/true); } TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibrariesDependenciesAndImage) { std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Nested"); std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); - std::string context = "PCL[" + GetTestDexFileName("Nested") + "]" + - "{PCL[" + GetTestDexFileName("MultiDex") + "]" + - "{PCL[" + GetTestDexFileName("Nested") + "]}}"; + std::string context = "PCL[" + GetTestDexFileName("Nested") + "]" + "{PCL[" + + GetTestDexFileName("MultiDex") + "]" + "{PCL[" + + GetTestDexFileName("Nested") + "]}}"; std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" + - "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]" + - "{PCL[" + CreateClassPathWithChecksums(dex_files1) + "]}}"; + "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]" + + "{PCL[" + CreateClassPathWithChecksums(dex_files1) + "]}}"; RunTest(context.c_str(), expected_classpath_key.c_str(), - /*expected_success=*/ true, - /*use_second_source=*/ false, - /*generate_image=*/ true); + /*expected_success=*/true, + /*use_second_source=*/false, + /*generate_image=*/true); } class Dex2oatDeterminism : public Dex2oatTest {}; @@ -1239,12 +1198,12 @@ TEST_F(Dex2oatDeterminism, UnloadCompile) { ASSERT_GT(spaces.size(), 0u); const std::string image_location = spaces[0]->GetImageLocation(); // Without passing in an app image, it will unload in between compilations. - const int res = GenerateOdexForTestWithStatus( - GetLibCoreDexFileNames(), - base_oat_name, - CompilerFilter::Filter::kVerify, - &error_msg, - {"--force-determinism", "--avoid-storing-invocation"}); + const int res = + GenerateOdexForTestWithStatus(GetLibCoreDexFileNames(), + base_oat_name, + CompilerFilter::Filter::kVerify, + &error_msg, + {"--force-determinism", "--avoid-storing-invocation"}); ASSERT_EQ(res, 0); Copy(base_oat_name, unload_oat_name); Copy(base_vdex_name, unload_vdex_name); @@ -1309,21 +1268,14 @@ TEST_F(Dex2oatTest, LayoutSections) { std::vector<uint16_t> post_methods = {methods[0], methods[2], methods[6]}; // Here, we build the profile from the method lists. ProfileCompilationInfo info; + info.AddMethodsForDex(static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagStartup), + dex.get(), + hot_methods.begin(), + hot_methods.end()); info.AddMethodsForDex( - static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagStartup), - dex.get(), - hot_methods.begin(), - hot_methods.end()); + Hotness::kFlagStartup, dex.get(), startup_methods.begin(), startup_methods.end()); info.AddMethodsForDex( - Hotness::kFlagStartup, - dex.get(), - startup_methods.begin(), - startup_methods.end()); - info.AddMethodsForDex( - Hotness::kFlagPostStartup, - dex.get(), - post_methods.begin(), - post_methods.end()); + Hotness::kFlagPostStartup, dex.get(), post_methods.begin(), post_methods.end()); for (uint16_t id : hot_methods) { EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsHot()); EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsStartup()); @@ -1346,16 +1298,15 @@ TEST_F(Dex2oatTest, LayoutSections) { oat_filename, CompilerFilter::Filter::kVerify, &error_msg, - {"--profile-file=" + profile_file.GetFilename(), - "--compact-dex-level=fast"}); + {"--profile-file=" + profile_file.GetFilename(), "--compact-dex-level=fast"}); EXPECT_EQ(res, 0); // Open our generated oat file. - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - oat_filename.c_str(), - oat_filename.c_str(), - /*executable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + oat_filename, + oat_filename, + /*executable=*/false, + /*low_4gb=*/false, dex->GetLocation(), &error_msg)); ASSERT_TRUE(odex_file != nullptr); @@ -1451,19 +1402,18 @@ TEST_F(Dex2oatTest, GenerateCompactDex) { const std::string vdex_filename = dir + "/base.vdex"; const std::string dex_location = GetTestDexFileName("MultiDex"); std::string error_msg; - const int res = GenerateOdexForTestWithStatus( - { dex_location }, - oat_filename, - CompilerFilter::Filter::kVerify, - &error_msg, - {"--compact-dex-level=fast"}); + const int res = GenerateOdexForTestWithStatus({dex_location}, + oat_filename, + CompilerFilter::Filter::kVerify, + &error_msg, + {"--compact-dex-level=fast"}); EXPECT_EQ(res, 0); // Open our generated oat file. - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - oat_filename.c_str(), - oat_filename.c_str(), - /*executable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + oat_filename, + oat_filename, + /*executable=*/false, + /*low_4gb=*/false, dex_location, &error_msg)); ASSERT_TRUE(odex_file != nullptr); @@ -1511,20 +1461,18 @@ TEST_F(Dex2oatVerifierAbort, HardFail) { std::string out_dir = GetScratchDir(); const std::string base_oat_name = out_dir + "/base.oat"; std::string error_msg; - const int res_fail = GenerateOdexForTestWithStatus( - {dex->GetLocation()}, - base_oat_name, - CompilerFilter::Filter::kVerify, - &error_msg, - {"--abort-on-hard-verifier-error"}); + const int res_fail = GenerateOdexForTestWithStatus({dex->GetLocation()}, + base_oat_name, + CompilerFilter::Filter::kVerify, + &error_msg, + {"--abort-on-hard-verifier-error"}); EXPECT_NE(0, res_fail); - const int res_no_fail = GenerateOdexForTestWithStatus( - {dex->GetLocation()}, - base_oat_name, - CompilerFilter::Filter::kVerify, - &error_msg, - {"--no-abort-on-hard-verifier-error"}); + const int res_no_fail = GenerateOdexForTestWithStatus({dex->GetLocation()}, + base_oat_name, + CompilerFilter::Filter::kVerify, + &error_msg, + {"--no-abort-on-hard-verifier-error"}); EXPECT_EQ(0, res_no_fail); } @@ -1536,28 +1484,25 @@ TEST_F(Dex2oatDedupeCode, DedupeTest) { std::string out_dir = GetScratchDir(); const std::string base_oat_name = out_dir + "/base.oat"; size_t no_dedupe_size = 0; - ASSERT_TRUE(GenerateOdexForTest(dex->GetLocation(), - base_oat_name, - CompilerFilter::Filter::kSpeed, - { "--deduplicate-code=false" }, - /*expect_success=*/ true, - /*use_fd=*/ false, - /*use_zip_fd=*/ false, - [&no_dedupe_size](const OatFile& o) { - no_dedupe_size = o.Size(); - })); + ASSERT_TRUE( + GenerateOdexForTest(dex->GetLocation(), + base_oat_name, + CompilerFilter::Filter::kSpeed, + {"--deduplicate-code=false"}, + /*expect_success=*/true, + /*use_fd=*/false, + /*use_zip_fd=*/false, + [&no_dedupe_size](const OatFile& o) { no_dedupe_size = o.Size(); })); size_t dedupe_size = 0; ASSERT_TRUE(GenerateOdexForTest(dex->GetLocation(), base_oat_name, CompilerFilter::Filter::kSpeed, - { "--deduplicate-code=true" }, - /*expect_success=*/ true, - /*use_fd=*/ false, - /*use_zip_fd=*/ false, - [&dedupe_size](const OatFile& o) { - dedupe_size = o.Size(); - })); + {"--deduplicate-code=true"}, + /*expect_success=*/true, + /*use_fd=*/false, + /*use_zip_fd=*/false, + [&dedupe_size](const OatFile& o) { dedupe_size = o.Size(); })); EXPECT_LT(dedupe_size, no_dedupe_size); } @@ -1569,13 +1514,11 @@ TEST_F(Dex2oatTest, UncompressedTest) { ASSERT_TRUE(GenerateOdexForTest(dex->GetLocation(), base_oat_name, CompilerFilter::Filter::kVerify, - { }, - /*expect_success=*/ true, - /*use_fd=*/ false, - /*use_zip_fd=*/ false, - [](const OatFile& o) { - CHECK(!o.ContainsDexCode()); - })); + {}, + /*expect_success=*/true, + /*use_fd=*/false, + /*use_zip_fd=*/false, + [](const OatFile& o) { CHECK(!o.ContainsDexCode()); })); } TEST_F(Dex2oatTest, MissingBootImageTest) { @@ -1594,13 +1537,12 @@ TEST_F(Dex2oatTest, EmptyUncompressedDexTest) { std::string out_dir = GetScratchDir(); const std::string base_oat_name = out_dir + "/base.oat"; std::string error_msg; - int status = GenerateOdexForTestWithStatus( - { GetTestDexFileName("MainEmptyUncompressed") }, - base_oat_name, - CompilerFilter::Filter::kVerify, - &error_msg, - { }, - /*use_fd*/ false); + int status = GenerateOdexForTestWithStatus({GetTestDexFileName("MainEmptyUncompressed")}, + base_oat_name, + CompilerFilter::Filter::kVerify, + &error_msg, + {}, + /*use_fd*/ false); // Expect to fail with code 1 and not SIGSEGV or SIGABRT. ASSERT_TRUE(WIFEXITED(status)); ASSERT_EQ(WEXITSTATUS(status), 1) << error_msg; @@ -1610,13 +1552,12 @@ TEST_F(Dex2oatTest, EmptyUncompressedAlignedDexTest) { std::string out_dir = GetScratchDir(); const std::string base_oat_name = out_dir + "/base.oat"; std::string error_msg; - int status = GenerateOdexForTestWithStatus( - { GetTestDexFileName("MainEmptyUncompressedAligned") }, - base_oat_name, - CompilerFilter::Filter::kVerify, - &error_msg, - { }, - /*use_fd*/ false); + int status = GenerateOdexForTestWithStatus({GetTestDexFileName("MainEmptyUncompressedAligned")}, + base_oat_name, + CompilerFilter::Filter::kVerify, + &error_msg, + {}, + /*use_fd*/ false); // Expect to fail with code 1 and not SIGSEGV or SIGABRT. ASSERT_TRUE(WIFEXITED(status)); ASSERT_EQ(WEXITSTATUS(status), 1) << error_msg; @@ -1702,20 +1643,18 @@ TEST_F(Dex2oatTest, CompactDexGenerationFailure) { ASSERT_TRUE(GenerateOdexForTest(temp_dex.GetFilename(), oat_filename, CompilerFilter::Filter::kVerify, - { }, - /*expect_success=*/ true, - /*use_fd=*/ false, - /*use_zip_fd=*/ false, - [](const OatFile& o) { - CHECK(o.ContainsDexCode()); - })); + {}, + /*expect_success=*/true, + /*use_fd=*/false, + /*use_zip_fd=*/false, + [](const OatFile& o) { CHECK(o.ContainsDexCode()); })); // Open our generated oat file. std::string error_msg; - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - oat_filename.c_str(), - oat_filename.c_str(), - /*executable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + oat_filename, + oat_filename, + /*executable=*/false, + /*low_4gb=*/false, temp_dex.GetFilename(), &error_msg)); ASSERT_TRUE(odex_file != nullptr); @@ -1750,11 +1689,8 @@ TEST_F(Dex2oatTest, CompactDexGenerationFailureMultiDex) { } const std::string& dex_location = apk_file.GetFilename(); const std::string odex_location = GetOdexDir() + "/output.odex"; - ASSERT_TRUE(GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::kVerify, - { "--compact-dex-level=fast" }, - true)); + ASSERT_TRUE(GenerateOdexForTest( + dex_location, odex_location, CompilerFilter::kVerify, {"--compact-dex-level=fast"}, true)); } TEST_F(Dex2oatTest, StderrLoggerOutput) { @@ -1767,7 +1703,7 @@ TEST_F(Dex2oatTest, StderrLoggerOutput) { ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kVerify, - { "--runtime-arg", "-Xuse-stderr-logger" }, + {"--runtime-arg", "-Xuse-stderr-logger"}, true)); // Look for some random part of dex2oat logging. With the stderr logger this should be captured, // even on device. @@ -1784,14 +1720,14 @@ TEST_F(Dex2oatTest, VerifyCompilationReason) { ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kVerify, - { "--compilation-reason=install" }, + {"--compilation-reason=install"}, true)); std::string error_msg; - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - odex_location.c_str(), - odex_location.c_str(), - /*executable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + odex_location, + odex_location, + /*executable=*/false, + /*low_4gb=*/false, dex_location, &error_msg)); ASSERT_TRUE(odex_file != nullptr); @@ -1805,17 +1741,13 @@ TEST_F(Dex2oatTest, VerifyNoCompilationReason) { // Test file doesn't matter. Copy(GetDexSrc1(), dex_location); - ASSERT_TRUE(GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::kVerify, - {}, - true)); + ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kVerify, {}, true)); std::string error_msg; - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - odex_location.c_str(), - odex_location.c_str(), - /*executable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + odex_location, + odex_location, + /*executable=*/false, + /*low_4gb=*/false, dex_location, &error_msg)); ASSERT_TRUE(odex_file != nullptr); @@ -1832,25 +1764,25 @@ TEST_F(Dex2oatTest, DontExtract) { ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, CompilerFilter::Filter::kVerify, - { "--copy-dex-files=false" }, - /*expect_success=*/ true, - /*use_fd=*/ false, - /*use_zip_fd=*/ false, + {"--copy-dex-files=false"}, + /*expect_success=*/true, + /*use_fd=*/false, + /*use_zip_fd=*/false, [](const OatFile&) {})); { // Check the vdex doesn't have dex. - std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_location.c_str(), - /*writable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_location, + /*writable=*/false, + /*low_4gb=*/false, &error_msg)); ASSERT_TRUE(vdex != nullptr); EXPECT_FALSE(vdex->HasDexSection()) << output_; } - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - odex_location.c_str(), - odex_location.c_str(), - /*executable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + odex_location, + odex_location, + /*executable=*/false, + /*low_4gb=*/false, dex_location, &error_msg)); ASSERT_TRUE(odex_file != nullptr) << dex_location; @@ -1888,18 +1820,16 @@ TEST_F(Dex2oatTest, DontExtract) { ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, filter, - { "--dump-timings", - "--dm-file=" + dm_file.GetFilename(), - // Pass -Xuse-stderr-logger have dex2oat output in output_ on - // target. - "--runtime-arg", - "-Xuse-stderr-logger" }, - /*expect_success=*/ true, - /*use_fd=*/ false, - /*use_zip_fd=*/ false, - [](const OatFile& o) { - CHECK(o.ContainsDexCode()); - })); + {"--dump-timings", + "--dm-file=" + dm_file.GetFilename(), + // Pass -Xuse-stderr-logger have dex2oat output in output_ on + // target. + "--runtime-arg", + "-Xuse-stderr-logger"}, + /*expect_success=*/true, + /*use_fd=*/false, + /*use_zip_fd=*/false, + [](const OatFile& o) { CHECK(o.ContainsDexCode()); })); // Check the output for "Fast verify", this is printed from --dump-timings. std::istringstream iss(output_); std::string line; @@ -1939,12 +1869,11 @@ TEST_F(Dex2oatTest, CompactDexInvalidSource) { const std::string& dex_location = invalid_dex.GetFilename(); const std::string odex_location = GetOdexDir() + "/output.odex"; std::string error_msg; - int status = GenerateOdexForTestWithStatus( - {dex_location}, - odex_location, - CompilerFilter::kVerify, - &error_msg, - { "--compact-dex-level=fast" }); + int status = GenerateOdexForTestWithStatus({dex_location}, + odex_location, + CompilerFilter::kVerify, + &error_msg, + {"--compact-dex-level=fast"}); ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) != 0) << status << " " << output_; } @@ -1978,20 +1907,18 @@ TEST_F(Dex2oatTest, CompactDexInZip) { std::string error_msg; int status = 0u; - status = GenerateOdexForTestWithStatus( - { invalid_dex_zip.GetFilename() }, - GetOdexDir() + "/output_apk.odex", - CompilerFilter::kVerify, - &error_msg, - { "--compact-dex-level=fast" }); + status = GenerateOdexForTestWithStatus({invalid_dex_zip.GetFilename()}, + GetOdexDir() + "/output_apk.odex", + CompilerFilter::kVerify, + &error_msg, + {"--compact-dex-level=fast"}); ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) != 0) << status << " " << output_; - status = GenerateOdexForTestWithStatus( - { invalid_dex.GetFilename() }, - GetOdexDir() + "/output.odex", - CompilerFilter::kVerify, - &error_msg, - { "--compact-dex-level=fast" }); + status = GenerateOdexForTestWithStatus({invalid_dex.GetFilename()}, + GetOdexDir() + "/output.odex", + CompilerFilter::kVerify, + &error_msg, + {"--compact-dex-level=fast"}); ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) != 0) << status << " " << output_; } @@ -2005,25 +1932,25 @@ TEST_F(Dex2oatWithExpectedFilterTest, AppImageNoProfile) { ASSERT_TRUE(GenerateOdexForTest(GetTestDexFileName("ManyMethods"), odex_location, CompilerFilter::Filter::kSpeedProfile, - { "--app-image-fd=" + std::to_string(app_image_file.GetFd()) }, - /*expect_success=*/ true, - /*use_fd=*/ false, - /*use_zip_fd=*/ false, + {"--app-image-fd=" + std::to_string(app_image_file.GetFd())}, + /*expect_success=*/true, + /*use_fd=*/false, + /*use_zip_fd=*/false, [](const OatFile&) {})); // Open our generated oat file. std::string error_msg; - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - odex_location.c_str(), - odex_location.c_str(), - /*executable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + odex_location, + odex_location, + /*executable=*/false, + /*low_4gb=*/false, &error_msg)); ASSERT_TRUE(odex_file != nullptr); ImageHeader header = {}; - ASSERT_TRUE(app_image_file.GetFile()->PreadFully( - reinterpret_cast<void*>(&header), - sizeof(header), - /*offset*/ 0u)) << app_image_file.GetFile()->GetLength(); + ASSERT_TRUE(app_image_file.GetFile()->PreadFully(reinterpret_cast<void*>(&header), + sizeof(header), + /*offset*/ 0u)) + << app_image_file.GetFile()->GetLength(); EXPECT_GT(header.GetImageSection(ImageHeader::kSectionObjects).Size(), 0u); EXPECT_EQ(header.GetImageSection(ImageHeader::kSectionArtMethods).Size(), 0u); EXPECT_EQ(header.GetImageSection(ImageHeader::kSectionArtFields).Size(), 0u); @@ -2042,9 +1969,9 @@ TEST_F(Dex2oatTest, ZipFd) { base_oat_name, CompilerFilter::Filter::kVerify, extra_args, - /*expect_success=*/ true, - /*use_fd=*/ false, - /*use_zip_fd=*/ true)); + /*expect_success=*/true, + /*use_fd=*/false, + /*use_zip_fd=*/true)); } TEST_F(Dex2oatWithExpectedFilterTest, AppImageEmptyDex) { @@ -2058,7 +1985,7 @@ TEST_F(Dex2oatWithExpectedFilterTest, AppImageEmptyDex) { std::vector<uint16_t> methods; std::vector<dex::TypeIndex> classes; { - MutateDexFile(temp_dex.GetFile(), GetTestDexFileName("StringLiterals"), [&] (DexFile* dex) { + MutateDexFile(temp_dex.GetFile(), GetTestDexFileName("StringLiterals"), [&](DexFile* dex) { // Modify the header to make the dex file valid but empty. DexFile::Header* header = const_cast<DexFile::Header*>(&dex->GetHeader()); header->string_ids_size_ = 0; @@ -2096,20 +2023,20 @@ TEST_F(Dex2oatWithExpectedFilterTest, AppImageEmptyDex) { ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, CompilerFilter::Filter::kSpeedProfile, - { "--app-image-file=" + app_image_location, - "--resolve-startup-const-strings=true", - "--profile-file=" + profile_file.GetFilename()}, - /*expect_success=*/ true, - /*use_fd=*/ false, - /*use_zip_fd=*/ false, + {"--app-image-file=" + app_image_location, + "--resolve-startup-const-strings=true", + "--profile-file=" + profile_file.GetFilename()}, + /*expect_success=*/true, + /*use_fd=*/false, + /*use_zip_fd=*/false, [](const OatFile&) {})); // Open our generated oat file. std::string error_msg; - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - odex_location.c_str(), - odex_location.c_str(), - /*executable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + odex_location, + odex_location, + /*executable=*/false, + /*low_4gb=*/false, &error_msg)); ASSERT_TRUE(odex_file != nullptr); } @@ -2144,9 +2071,9 @@ TEST_F(Dex2oatTest, DexFileFd) { base_oat_name, CompilerFilter::Filter::kVerify, extra_args, - /*expect_success=*/ true, - /*use_fd=*/ false, - /*use_zip_fd=*/ true)); + /*expect_success=*/true, + /*use_fd=*/false, + /*use_zip_fd=*/true)); } TEST_F(Dex2oatTest, AppImageResolveStrings) { @@ -2158,46 +2085,47 @@ TEST_F(Dex2oatTest, AppImageResolveStrings) { std::vector<uint16_t> methods; std::vector<dex::TypeIndex> classes; { - MutateDexFile(temp_dex.GetFile(), GetTestDexFileName("StringLiterals"), [&] (DexFile* dex) { - bool mutated_successfully = false; - // Change the dex instructions to make an opcode that spans past the end of the code item. - for (ClassAccessor accessor : dex->GetClasses()) { - if (accessor.GetDescriptor() == std::string("LStringLiterals$StartupClass;")) { - classes.push_back(accessor.GetClassIdx()); - } - for (const ClassAccessor::Method& method : accessor.GetMethods()) { - std::string method_name(dex->GetMethodName(dex->GetMethodId(method.GetIndex()))); - CodeItemInstructionAccessor instructions = method.GetInstructions(); - if (method_name == "startUpMethod2") { - // Make an instruction that runs past the end of the code item and verify that it - // doesn't cause dex2oat to crash. - ASSERT_TRUE(instructions.begin() != instructions.end()); - DexInstructionIterator last_instruction = instructions.begin(); - for (auto dex_it = instructions.begin(); dex_it != instructions.end(); ++dex_it) { - last_instruction = dex_it; + MutateDexFile( + temp_dex.GetFile(), GetTestDexFileName("StringLiterals"), [&](DexFile* dex) { + bool mutated_successfully = false; + // Change the dex instructions to make an opcode that spans past the end of the code item. + for (ClassAccessor accessor : dex->GetClasses()) { + if (accessor.GetDescriptor() == std::string("LStringLiterals$StartupClass;")) { + classes.push_back(accessor.GetClassIdx()); } - ASSERT_EQ(last_instruction->SizeInCodeUnits(), 1u); - // Set the opcode to something that will go past the end of the code item. - const_cast<Instruction&>(last_instruction.Inst()).SetOpcode( - Instruction::CONST_STRING_JUMBO); - mutated_successfully = true; - // Test that the safe iterator doesn't go past the end. - SafeDexInstructionIterator it2(instructions.begin(), instructions.end()); - while (!it2.IsErrorState()) { - ++it2; + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + std::string method_name(dex->GetMethodName(dex->GetMethodId(method.GetIndex()))); + CodeItemInstructionAccessor instructions = method.GetInstructions(); + if (method_name == "startUpMethod2") { + // Make an instruction that runs past the end of the code item and verify that it + // doesn't cause dex2oat to crash. + ASSERT_TRUE(instructions.begin() != instructions.end()); + DexInstructionIterator last_instruction = instructions.begin(); + for (auto dex_it = instructions.begin(); dex_it != instructions.end(); ++dex_it) { + last_instruction = dex_it; + } + ASSERT_EQ(last_instruction->SizeInCodeUnits(), 1u); + // Set the opcode to something that will go past the end of the code item. + const_cast<Instruction&>(last_instruction.Inst()) + .SetOpcode(Instruction::CONST_STRING_JUMBO); + mutated_successfully = true; + // Test that the safe iterator doesn't go past the end. + SafeDexInstructionIterator it2(instructions.begin(), instructions.end()); + while (!it2.IsErrorState()) { + ++it2; + } + EXPECT_TRUE(it2 == last_instruction); + EXPECT_TRUE(it2 < instructions.end()); + methods.push_back(method.GetIndex()); + mutated_successfully = true; + } else if (method_name == "startUpMethod") { + methods.push_back(method.GetIndex()); + } } - EXPECT_TRUE(it2 == last_instruction); - EXPECT_TRUE(it2 < instructions.end()); - methods.push_back(method.GetIndex()); - mutated_successfully = true; - } else if (method_name == "startUpMethod") { - methods.push_back(method.GetIndex()); } - } - } - CHECK(mutated_successfully) - << "Failed to find candidate code item with only one code unit in last instruction."; - }); + CHECK(mutated_successfully) + << "Failed to find candidate code item with only one code unit in last instruction."; + }); } std::unique_ptr<const DexFile> dex_file(OpenDexFile(temp_dex.GetFilename().c_str())); { @@ -2216,38 +2144,36 @@ TEST_F(Dex2oatTest, AppImageResolveStrings) { ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, CompilerFilter::Filter::kSpeedProfile, - { "--app-image-file=" + app_image_location, - "--resolve-startup-const-strings=true", - "--profile-file=" + profile_file.GetFilename()}, - /*expect_success=*/ true, - /*use_fd=*/ false, - /*use_zip_fd=*/ false, + {"--app-image-file=" + app_image_location, + "--resolve-startup-const-strings=true", + "--profile-file=" + profile_file.GetFilename()}, + /*expect_success=*/true, + /*use_fd=*/false, + /*use_zip_fd=*/false, [](const OatFile&) {})); // Open our generated oat file. std::string error_msg; - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - odex_location.c_str(), - odex_location.c_str(), - /*executable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + odex_location, + odex_location, + /*executable=*/false, + /*low_4gb=*/false, &error_msg)); ASSERT_TRUE(odex_file != nullptr); // Check the strings in the app image intern table only contain the "startup" strigs. { ScopedObjectAccess soa(Thread::Current()); - std::unique_ptr<gc::space::ImageSpace> space = - gc::space::ImageSpace::CreateFromAppImage(app_image_location.c_str(), - odex_file.get(), - &error_msg); + std::unique_ptr<gc::space::ImageSpace> space = gc::space::ImageSpace::CreateFromAppImage( + app_image_location.c_str(), odex_file.get(), &error_msg); ASSERT_TRUE(space != nullptr) << error_msg; std::set<std::string> seen; InternTable intern_table; - intern_table.AddImageStringsToTable(space.get(), [&](InternTable::UnorderedSet& interns) - REQUIRES_SHARED(Locks::mutator_lock_) { - for (const GcRoot<mirror::String>& str : interns) { - seen.insert(str.Read()->ToModifiedUtf8()); - } - }); + intern_table.AddImageStringsToTable( + space.get(), [&](InternTable::UnorderedSet& interns) REQUIRES_SHARED(Locks::mutator_lock_) { + for (const GcRoot<mirror::String>& str : interns) { + seen.insert(str.Read()->ToModifiedUtf8()); + } + }); // Normal methods EXPECT_TRUE(seen.find("Loading ") != seen.end()); EXPECT_TRUE(seen.find("Starting up") != seen.end()); @@ -2265,20 +2191,23 @@ TEST_F(Dex2oatTest, AppImageResolveStrings) { std::set<std::string> app_image_strings; MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); - intern_table.VisitInterns([&](const GcRoot<mirror::String>& root) - REQUIRES_SHARED(Locks::mutator_lock_) { - boot_image_strings.insert(root.Read()->ToModifiedUtf8()); - }, /*visit_boot_images=*/true, /*visit_non_boot_images=*/false); - intern_table.VisitInterns([&](const GcRoot<mirror::String>& root) - REQUIRES_SHARED(Locks::mutator_lock_) { - app_image_strings.insert(root.Read()->ToModifiedUtf8()); - }, /*visit_boot_images=*/false, /*visit_non_boot_images=*/true); + intern_table.VisitInterns( + [&](const GcRoot<mirror::String>& root) REQUIRES_SHARED(Locks::mutator_lock_) { + boot_image_strings.insert(root.Read()->ToModifiedUtf8()); + }, + /*visit_boot_images=*/true, + /*visit_non_boot_images=*/false); + intern_table.VisitInterns( + [&](const GcRoot<mirror::String>& root) REQUIRES_SHARED(Locks::mutator_lock_) { + app_image_strings.insert(root.Read()->ToModifiedUtf8()); + }, + /*visit_boot_images=*/false, + /*visit_non_boot_images=*/true); EXPECT_EQ(boot_image_strings.size(), 0u); EXPECT_TRUE(app_image_strings == seen); } } - TEST_F(Dex2oatClassLoaderContextTest, StoredClassLoaderContext) { std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("MultiDex"); const std::string out_dir = GetScratchDir(); @@ -2299,31 +2228,34 @@ TEST_F(Dex2oatClassLoaderContextTest, StoredClassLoaderContext) { expected_stored_context += "*" + std::to_string(dex_file->GetLocationChecksum()); ++index; } - expected_stored_context += + "]"; + expected_stored_context += "]"; // The class path should not be valid and should fail being stored. EXPECT_TRUE(GenerateOdexForTest(GetTestDexFileName("ManyMethods"), odex_location, CompilerFilter::Filter::kVerify, - { "--class-loader-context=" + stored_context }, - /*expect_success=*/ true, - /*use_fd=*/ false, - /*use_zip_fd=*/ false, + {"--class-loader-context=" + stored_context}, + /*expect_success=*/true, + /*use_fd=*/false, + /*use_zip_fd=*/false, [&](const OatFile& oat_file) { - EXPECT_NE(oat_file.GetClassLoaderContext(), stored_context) << output_; - EXPECT_NE(oat_file.GetClassLoaderContext(), valid_context) << output_; - })); + EXPECT_NE(oat_file.GetClassLoaderContext(), stored_context) + << output_; + EXPECT_NE(oat_file.GetClassLoaderContext(), valid_context) + << output_; + })); // The stored context should match what we expect even though it's invalid. - EXPECT_TRUE(GenerateOdexForTest(GetTestDexFileName("ManyMethods"), - odex_location, - CompilerFilter::Filter::kVerify, - { "--class-loader-context=" + valid_context, - "--stored-class-loader-context=" + stored_context }, - /*expect_success=*/ true, - /*use_fd=*/ false, - /*use_zip_fd=*/ false, - [&](const OatFile& oat_file) { - EXPECT_EQ(oat_file.GetClassLoaderContext(), expected_stored_context) << output_; - })); + EXPECT_TRUE(GenerateOdexForTest( + GetTestDexFileName("ManyMethods"), + odex_location, + CompilerFilter::Filter::kVerify, + {"--class-loader-context=" + valid_context, + "--stored-class-loader-context=" + stored_context}, + /*expect_success=*/true, + /*use_fd=*/false, + /*use_zip_fd=*/false, + [&](const OatFile& oat_file) { + EXPECT_EQ(oat_file.GetClassLoaderContext(), expected_stored_context) << output_; + })); } class Dex2oatISAFeaturesRuntimeDetectionTest : public Dex2oatTest { @@ -2334,15 +2266,11 @@ class Dex2oatISAFeaturesRuntimeDetectionTest : public Dex2oatTest { Copy(GetTestDexFileName(), dex_location); - ASSERT_TRUE(GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::kSpeed, - extra_args)); + ASSERT_TRUE( + GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, extra_args)); } - std::string GetTestDexFileName() { - return GetDexSrc1(); - } + std::string GetTestDexFileName() { return GetDexSrc1(); } }; TEST_F(Dex2oatISAFeaturesRuntimeDetectionTest, TestCurrentRuntimeFeaturesAsDex2OatArguments) { @@ -2368,20 +2296,19 @@ TEST_F(LinkageTest, LinkageEnabled) { std::string out_dir = GetScratchDir(); const std::string base_oat_name = out_dir + "/base.oat"; std::string error_msg; - const int res_fail = GenerateOdexForTestWithStatus( - {dex->GetLocation()}, - base_oat_name, - CompilerFilter::Filter::kSpeed, - &error_msg, - {"--check-linkage-conditions", "--crash-on-linkage-violation"}); + const int res_fail = + GenerateOdexForTestWithStatus({dex->GetLocation()}, + base_oat_name, + CompilerFilter::Filter::kSpeed, + &error_msg, + {"--check-linkage-conditions", "--crash-on-linkage-violation"}); EXPECT_NE(0, res_fail); - const int res_no_fail = GenerateOdexForTestWithStatus( - {dex->GetLocation()}, - base_oat_name, - CompilerFilter::Filter::kSpeed, - &error_msg, - {"--check-linkage-conditions"}); + const int res_no_fail = GenerateOdexForTestWithStatus({dex->GetLocation()}, + base_oat_name, + CompilerFilter::Filter::kSpeed, + &error_msg, + {"--check-linkage-conditions"}); EXPECT_EQ(0, res_no_fail); } @@ -2393,19 +2320,19 @@ TEST_F(Dex2oatTest, LoadOutOfDateOatFile) { ASSERT_TRUE(GenerateOdexForTest(dex->GetLocation(), base_oat_name, CompilerFilter::Filter::kSpeed, - { "--deduplicate-code=false" }, - /*expect_success=*/ true, - /*use_fd=*/ false, - /*use_zip_fd=*/ false)); + {"--deduplicate-code=false"}, + /*expect_success=*/true, + /*use_fd=*/false, + /*use_zip_fd=*/false)); // Check that we can open the oat file as executable. { std::string error_msg; - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - base_oat_name.c_str(), - base_oat_name.c_str(), - /*executable=*/ true, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + base_oat_name, + base_oat_name, + /*executable=*/true, + /*low_4gb=*/false, dex->GetLocation(), &error_msg)); ASSERT_TRUE(odex_file != nullptr) << error_msg; @@ -2421,19 +2348,19 @@ TEST_F(Dex2oatTest, LoadOutOfDateOatFile) { { std::string error_msg; std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file.get(), - /*writable=*/ false, - /*program_header_only=*/ true, - /*low_4gb=*/ false, + /*writable=*/false, + /*program_header_only=*/true, + /*low_4gb=*/false, &error_msg)); ASSERT_TRUE(elf_file != nullptr) << error_msg; ASSERT_TRUE(elf_file->Load(file.get(), - /*executable=*/ false, - /*low_4gb=*/ false, - /*reservation=*/ nullptr, - &error_msg)) << error_msg; - const uint8_t* base_address = elf_file->Is64Bit() - ? elf_file->GetImpl64()->GetBaseAddress() - : elf_file->GetImpl32()->GetBaseAddress(); + /*executable=*/false, + /*low_4gb=*/false, + /*reservation=*/nullptr, + &error_msg)) + << error_msg; + const uint8_t* base_address = elf_file->Is64Bit() ? elf_file->GetImpl64()->GetBaseAddress() : + elf_file->GetImpl32()->GetBaseAddress(); const uint8_t* oatdata = elf_file->FindDynamicSymbolAddress("oatdata"); ASSERT_TRUE(oatdata != nullptr); ASSERT_TRUE(oatdata > base_address); @@ -2478,11 +2405,11 @@ TEST_F(Dex2oatTest, LoadOutOfDateOatFile) { // Check that we reject the oat file without crashing. { std::string error_msg; - std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, - base_oat_name.c_str(), - base_oat_name.c_str(), - /*executable=*/ true, - /*low_4gb=*/ false, + std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, + base_oat_name, + base_oat_name, + /*executable=*/true, + /*low_4gb=*/false, dex->GetLocation(), &error_msg)); ASSERT_FALSE(odex_file != nullptr); diff --git a/dex2oat/dex2oat_vdex_test.cc b/dex2oat/dex2oat_vdex_test.cc index 895e9c21e3..27fcc180a5 100644 --- a/dex2oat/dex2oat_vdex_test.cc +++ b/dex2oat/dex2oat_vdex_test.cc @@ -19,10 +19,8 @@ #include "common_runtime_test.h" #include "dex2oat_environment_test.h" - #include "vdex_file.h" #include "verifier/verifier_deps.h" -#include "ziparchive/zip_writer.h" namespace art { @@ -51,7 +49,7 @@ class Dex2oatVdexTest : public Dex2oatEnvironmentTest { args.push_back("--public-sdk=" + *public_sdk); } args.push_back("--compiler-filter=" + - CompilerFilter::NameOfFilter(CompilerFilter::Filter::kVerify)); + CompilerFilter::NameOfFilter(CompilerFilter::Filter::kVerify)); args.push_back("--runtime-arg"); args.push_back("-Xnorelocate"); if (!copy_dex_files) { @@ -67,12 +65,12 @@ class Dex2oatVdexTest : public Dex2oatEnvironmentTest { return Dex2Oat(args, &output_, &error_msg_) == 0; } - std::unique_ptr<VerifierDeps> GetVerifierDeps( - const std::string& vdex_location, const DexFile* dex_file) { + std::unique_ptr<VerifierDeps> GetVerifierDeps(const std::string& vdex_location, + const DexFile* dex_file) { // Verify the vdex file content: only the classes using public APIs should be verified. - std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_location.c_str(), - /*writable=*/ false, - /*low_4gb=*/ false, + std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_location, + /*writable=*/false, + /*low_4gb=*/false, &error_msg_)); // Check the vdex doesn't have dex. if (vdex->HasDexSection()) { @@ -87,7 +85,7 @@ class Dex2oatVdexTest : public Dex2oatEnvironmentTest { std::vector<const DexFile*> dex_files; dex_files.push_back(dex_file); - std::unique_ptr<VerifierDeps> deps(new VerifierDeps(dex_files, /*output_only=*/ false)); + std::unique_ptr<VerifierDeps> deps(new VerifierDeps(dex_files, /*output_only=*/false)); if (!deps->ParseStoredData(dex_files, vdex->GetVerifierDepsData())) { ::testing::AssertionFailure() << error_msg_; @@ -113,23 +111,6 @@ class Dex2oatVdexTest : public Dex2oatEnvironmentTest { return deps->GetVerifiedClasses(dex_file)[class_def_idx]; } - void CreateDexMetadata(const std::string& vdex, const std::string& out_dm) { - // Read the vdex bytes. - std::unique_ptr<File> vdex_file(OS::OpenFileForReading(vdex.c_str())); - std::vector<uint8_t> data(vdex_file->GetLength()); - ASSERT_TRUE(vdex_file->ReadFully(data.data(), data.size())); - - // Zip the content. - FILE* file = fopen(out_dm.c_str(), "wb"); - ZipWriter writer(file); - writer.StartEntry("primary.vdex", ZipWriter::kAlign32); - writer.WriteBytes(data.data(), data.size()); - writer.FinishEntry(); - writer.Finish(); - fflush(file); - fclose(file); - } - std::string GetFilename(const std::unique_ptr<const DexFile>& dex_file) { const std::string& str = dex_file->GetLocation(); size_t idx = str.rfind('/'); @@ -219,11 +200,10 @@ TEST_F(Dex2oatVdexTest, VerifyPublicSdkStubsWithDexFiles) { std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Dex2oatVdexTestDex")); // Compile the subject app using the predefined API-stubs - ASSERT_TRUE(RunDex2oat( - dex_file->GetLocation(), - GetOdex(dex_file), - /*public_sdk=*/ nullptr, - /*copy_dex_files=*/ true)); + ASSERT_TRUE(RunDex2oat(dex_file->GetLocation(), + GetOdex(dex_file), + /*public_sdk=*/nullptr, + /*copy_dex_files=*/true)); // Create the .dm file with the output. std::string dm_file = GetScratchDir() + "/base.dm"; @@ -233,12 +213,11 @@ TEST_F(Dex2oatVdexTest, VerifyPublicSdkStubsWithDexFiles) { // Recompile again with the .dm file which contains a vdex with code. // The compilation will pass, but dex2oat will not use the vdex file. - ASSERT_TRUE(RunDex2oat( - dex_file->GetLocation(), - GetOdex(dex_file, "v2"), - /*public_sdk=*/ nullptr, - /*copy_dex_files=*/ true, - extra_args)); + ASSERT_TRUE(RunDex2oat(dex_file->GetLocation(), + GetOdex(dex_file, "v2"), + /*public_sdk=*/nullptr, + /*copy_dex_files=*/true, + extra_args)); } // Check that corrupt vdex files from .dm archives are ignored. @@ -257,12 +236,12 @@ TEST_F(Dex2oatVdexTest, VerifyCorruptVdexFile) { extra_args.push_back("--dm-file=" + dm_file); // Compile the dex file. Despite having a corrupt input .vdex, we should not crash. - ASSERT_TRUE(RunDex2oat( - dex_file->GetLocation(), - GetOdex(dex_file), - /*public_sdk=*/ nullptr, - /*copy_dex_files=*/ true, - extra_args)) << output_; + ASSERT_TRUE(RunDex2oat(dex_file->GetLocation(), + GetOdex(dex_file), + /*public_sdk=*/nullptr, + /*copy_dex_files=*/true, + extra_args)) + << output_; } // Check that if the input dm a vdex with mismatching checksums the compilation fails @@ -272,11 +251,10 @@ TEST_F(Dex2oatVdexTest, VerifyInputDmWithMismatchedChecksums) { // Generate a vdex file for Dex2oatVdexTestDex. std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Dex2oatVdexTestDex")); - ASSERT_TRUE(RunDex2oat( - dex_file->GetLocation(), - GetOdex(dex_file), - /*public_sdk=*/ nullptr, - /*copy_dex_files=*/ false)); + ASSERT_TRUE(RunDex2oat(dex_file->GetLocation(), + GetOdex(dex_file), + /*public_sdk=*/nullptr, + /*copy_dex_files=*/false)); // Create the .dm file with the output. std::string dm_file = GetScratchDir() + "/base.dm"; @@ -287,12 +265,12 @@ TEST_F(Dex2oatVdexTest, VerifyInputDmWithMismatchedChecksums) { // Try to compile Main using an input dm which contains the vdex for // Dex2oatVdexTestDex. It should fail. std::unique_ptr<const DexFile> dex_file2(OpenTestDexFile("Main")); - ASSERT_FALSE(RunDex2oat( - dex_file2->GetLocation(), - GetOdex(dex_file2, "v2"), - /*public_sdk=*/ nullptr, - /*copy_dex_files=*/ false, - extra_args)) << output_; + ASSERT_FALSE(RunDex2oat(dex_file2->GetLocation(), + GetOdex(dex_file2, "v2"), + /*public_sdk=*/nullptr, + /*copy_dex_files=*/false, + extra_args)) + << output_; } } // namespace art diff --git a/dex2oat/driver/compiled_method-inl.h b/dex2oat/driver/compiled_method-inl.h new file mode 100644 index 0000000000..77ac85c132 --- /dev/null +++ b/dex2oat/driver/compiled_method-inl.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_DEX2OAT_DRIVER_COMPILED_METHOD_INL_H_ +#define ART_DEX2OAT_DRIVER_COMPILED_METHOD_INL_H_ + +#include "compiled_method.h" + +#include "base/array_ref.h" +#include "base/length_prefixed_array.h" +#include "linker/linker_patch.h" + +namespace art { + +inline ArrayRef<const uint8_t> CompiledCode::GetQuickCode() const { + return GetArray(quick_code_); +} + +template <typename T> +inline ArrayRef<const T> CompiledCode::GetArray(const LengthPrefixedArray<T>* array) { + if (array == nullptr) { + return ArrayRef<const T>(); + } + DCHECK_NE(array->size(), 0u); + return ArrayRef<const T>(&array->At(0), array->size()); +} + +inline ArrayRef<const uint8_t> CompiledMethod::GetVmapTable() const { + return GetArray(vmap_table_); +} + +inline ArrayRef<const uint8_t> CompiledMethod::GetCFIInfo() const { + return GetArray(cfi_info_); +} + +inline ArrayRef<const linker::LinkerPatch> CompiledMethod::GetPatches() const { + return GetArray(patches_); +} + +} // namespace art + +#endif // ART_DEX2OAT_DRIVER_COMPILED_METHOD_INL_H_ diff --git a/dex2oat/driver/compiled_method.cc b/dex2oat/driver/compiled_method.cc new file mode 100644 index 0000000000..0a0a0057d4 --- /dev/null +++ b/dex2oat/driver/compiled_method.cc @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "compiled_method.h" + +#include "driver/compiled_method_storage.h" +#include "utils/swap_space.h" + +namespace art { + +CompiledCode::CompiledCode(CompiledMethodStorage* storage, + InstructionSet instruction_set, + const ArrayRef<const uint8_t>& quick_code) + : storage_(storage), + quick_code_(storage->DeduplicateCode(quick_code)), + packed_fields_(InstructionSetField::Encode(instruction_set)) { +} + +CompiledCode::~CompiledCode() { + GetStorage()->ReleaseCode(quick_code_); +} + +bool CompiledCode::operator==(const CompiledCode& rhs) const { + if (quick_code_ != nullptr) { + if (rhs.quick_code_ == nullptr) { + return false; + } else if (quick_code_->size() != rhs.quick_code_->size()) { + return false; + } else { + return std::equal(quick_code_->begin(), quick_code_->end(), rhs.quick_code_->begin()); + } + } + return (rhs.quick_code_ == nullptr); +} + +size_t CompiledCode::AlignCode(size_t offset) const { + return AlignCode(offset, GetInstructionSet()); +} + +size_t CompiledCode::AlignCode(size_t offset, InstructionSet instruction_set) { + return RoundUp(offset, GetInstructionSetCodeAlignment(instruction_set)); +} + +size_t CompiledCode::GetEntryPointAdjustment() const { + return GetInstructionSetEntryPointAdjustment(GetInstructionSet()); +} + +CompiledMethod::CompiledMethod(CompiledMethodStorage* storage, + InstructionSet instruction_set, + const ArrayRef<const uint8_t>& quick_code, + const ArrayRef<const uint8_t>& vmap_table, + const ArrayRef<const uint8_t>& cfi_info, + const ArrayRef<const linker::LinkerPatch>& patches) + : CompiledCode(storage, instruction_set, quick_code), + vmap_table_(storage->DeduplicateVMapTable(vmap_table)), + cfi_info_(storage->DeduplicateCFIInfo(cfi_info)), + patches_(storage->DeduplicateLinkerPatches(patches)) { +} + +CompiledMethod* CompiledMethod::SwapAllocCompiledMethod( + CompiledMethodStorage* storage, + InstructionSet instruction_set, + const ArrayRef<const uint8_t>& quick_code, + const ArrayRef<const uint8_t>& vmap_table, + const ArrayRef<const uint8_t>& cfi_info, + const ArrayRef<const linker::LinkerPatch>& patches) { + SwapAllocator<CompiledMethod> alloc(storage->GetSwapSpaceAllocator()); + CompiledMethod* ret = alloc.allocate(1); + alloc.construct(ret, + storage, + instruction_set, + quick_code, + vmap_table, + cfi_info, patches); + return ret; +} + +void CompiledMethod::ReleaseSwapAllocatedCompiledMethod(CompiledMethodStorage* storage, + CompiledMethod* m) { + SwapAllocator<CompiledMethod> alloc(storage->GetSwapSpaceAllocator()); + alloc.destroy(m); + alloc.deallocate(m, 1); +} + +CompiledMethod::~CompiledMethod() { + CompiledMethodStorage* storage = GetStorage(); + storage->ReleaseLinkerPatches(patches_); + storage->ReleaseCFIInfo(cfi_info_); + storage->ReleaseVMapTable(vmap_table_); +} + +} // namespace art diff --git a/dex2oat/driver/compiled_method.h b/dex2oat/driver/compiled_method.h new file mode 100644 index 0000000000..a92c75756e --- /dev/null +++ b/dex2oat/driver/compiled_method.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_DEX2OAT_DRIVER_COMPILED_METHOD_H_ +#define ART_DEX2OAT_DRIVER_COMPILED_METHOD_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "arch/instruction_set.h" +#include "base/bit_field.h" +#include "base/bit_utils.h" + +namespace art { + +template <typename T> class ArrayRef; +class CompiledMethodStorage; +template<typename T> class LengthPrefixedArray; + +namespace linker { +class LinkerPatch; +} // namespace linker + +class CompiledCode { + public: + // For Quick to supply an code blob + CompiledCode(CompiledMethodStorage* storage, + InstructionSet instruction_set, + const ArrayRef<const uint8_t>& quick_code); + + virtual ~CompiledCode(); + + InstructionSet GetInstructionSet() const { + return GetPackedField<InstructionSetField>(); + } + + ArrayRef<const uint8_t> GetQuickCode() const; + + bool operator==(const CompiledCode& rhs) const; + + // To align an offset from a page-aligned value to make it suitable + // for code storage. For example on ARM, to ensure that PC relative + // valu computations work out as expected. + size_t AlignCode(size_t offset) const; + static size_t AlignCode(size_t offset, InstructionSet instruction_set); + + // Returns the difference between the code address and a usable PC. + // Mainly to cope with `kThumb2` where the lower bit must be set. + size_t GetEntryPointAdjustment() const; + + protected: + static constexpr size_t kInstructionSetFieldSize = + MinimumBitsToStore(static_cast<size_t>(InstructionSet::kLast)); + static constexpr size_t kNumberOfCompiledCodePackedBits = kInstructionSetFieldSize; + static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte; + + template <typename T> + static ArrayRef<const T> GetArray(const LengthPrefixedArray<T>* array); + + CompiledMethodStorage* GetStorage() { + return storage_; + } + + template <typename BitFieldType> + typename BitFieldType::value_type GetPackedField() const { + return BitFieldType::Decode(packed_fields_); + } + + template <typename BitFieldType> + void SetPackedField(typename BitFieldType::value_type value) { + DCHECK(IsUint<BitFieldType::size>(static_cast<uintptr_t>(value))); + packed_fields_ = BitFieldType::Update(value, packed_fields_); + } + + private: + using InstructionSetField = BitField<InstructionSet, 0u, kInstructionSetFieldSize>; + + CompiledMethodStorage* const storage_; + + // Used to store the compiled code. + const LengthPrefixedArray<uint8_t>* const quick_code_; + + uint32_t packed_fields_; +}; + +class CompiledMethod final : public CompiledCode { + public: + // Constructs a CompiledMethod. + // Note: Consider using the static allocation methods below that will allocate the CompiledMethod + // in the swap space. + CompiledMethod(CompiledMethodStorage* storage, + InstructionSet instruction_set, + const ArrayRef<const uint8_t>& quick_code, + const ArrayRef<const uint8_t>& vmap_table, + const ArrayRef<const uint8_t>& cfi_info, + const ArrayRef<const linker::LinkerPatch>& patches); + + virtual ~CompiledMethod(); + + static CompiledMethod* SwapAllocCompiledMethod( + CompiledMethodStorage* storage, + InstructionSet instruction_set, + const ArrayRef<const uint8_t>& quick_code, + const ArrayRef<const uint8_t>& vmap_table, + const ArrayRef<const uint8_t>& cfi_info, + const ArrayRef<const linker::LinkerPatch>& patches); + + static void ReleaseSwapAllocatedCompiledMethod(CompiledMethodStorage* storage, CompiledMethod* m); + + bool IsIntrinsic() const { + return GetPackedField<IsIntrinsicField>(); + } + + // Marks the compiled method as being generated using an intrinsic codegen. + // Such methods have no relationships to their code items. + // This affects debug information generated at link time. + void MarkAsIntrinsic() { + DCHECK(!IsIntrinsic()); + SetPackedField<IsIntrinsicField>(/* value= */ true); + } + + ArrayRef<const uint8_t> GetVmapTable() const; + + ArrayRef<const uint8_t> GetCFIInfo() const; + + ArrayRef<const linker::LinkerPatch> GetPatches() const; + + private: + static constexpr size_t kIsIntrinsicLsb = kNumberOfCompiledCodePackedBits; + static constexpr size_t kIsIntrinsicSize = 1u; + static constexpr size_t kNumberOfCompiledMethodPackedBits = kIsIntrinsicLsb + kIsIntrinsicSize; + static_assert(kNumberOfCompiledMethodPackedBits <= CompiledCode::kMaxNumberOfPackedBits, + "Too many packed fields."); + + using IsIntrinsicField = BitField<bool, kIsIntrinsicLsb, kIsIntrinsicSize>; + + // For quick code, holds code infos which contain stack maps, inline information, and etc. + const LengthPrefixedArray<uint8_t>* const vmap_table_; + // For quick code, a FDE entry for the debug_frame section. + const LengthPrefixedArray<uint8_t>* const cfi_info_; + // For quick code, linker patches needed by the method. + const LengthPrefixedArray<linker::LinkerPatch>* const patches_; +}; + +} // namespace art + +#endif // ART_DEX2OAT_DRIVER_COMPILED_METHOD_H_ diff --git a/dex2oat/driver/compiled_method_storage.cc b/dex2oat/driver/compiled_method_storage.cc new file mode 100644 index 0000000000..0e46f4e070 --- /dev/null +++ b/dex2oat/driver/compiled_method_storage.cc @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <algorithm> +#include <ostream> + +#include "compiled_method_storage.h" + +#include <android-base/logging.h> + +#include "base/data_hash.h" +#include "base/utils.h" +#include "compiled_method.h" +#include "linker/linker_patch.h" +#include "thread-current-inl.h" +#include "utils/dedupe_set-inl.h" +#include "utils/swap_space.h" + +namespace art { + +namespace { // anonymous namespace + +template <typename T> +const LengthPrefixedArray<T>* CopyArray(SwapSpace* swap_space, const ArrayRef<const T>& array) { + DCHECK(!array.empty()); + SwapAllocator<uint8_t> allocator(swap_space); + void* storage = allocator.allocate(LengthPrefixedArray<T>::ComputeSize(array.size())); + LengthPrefixedArray<T>* array_copy = new(storage) LengthPrefixedArray<T>(array.size()); + std::copy(array.begin(), array.end(), array_copy->begin()); + return array_copy; +} + +template <typename T> +void ReleaseArray(SwapSpace* swap_space, const LengthPrefixedArray<T>* array) { + SwapAllocator<uint8_t> allocator(swap_space); + size_t size = LengthPrefixedArray<T>::ComputeSize(array->size()); + array->~LengthPrefixedArray<T>(); + allocator.deallocate(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(array)), size); +} + +} // anonymous namespace + +template <typename T, typename DedupeSetType> +inline const LengthPrefixedArray<T>* CompiledMethodStorage::AllocateOrDeduplicateArray( + const ArrayRef<const T>& data, + DedupeSetType* dedupe_set) { + if (data.empty()) { + return nullptr; + } else if (!DedupeEnabled()) { + return CopyArray(swap_space_.get(), data); + } else { + return dedupe_set->Add(Thread::Current(), data); + } +} + +template <typename T> +inline void CompiledMethodStorage::ReleaseArrayIfNotDeduplicated( + const LengthPrefixedArray<T>* array) { + if (array != nullptr && !DedupeEnabled()) { + ReleaseArray(swap_space_.get(), array); + } +} + +template <typename ContentType> +class CompiledMethodStorage::DedupeHashFunc { + private: + static constexpr bool kUseMurmur3Hash = true; + + public: + size_t operator()(const ArrayRef<ContentType>& array) const { + return DataHash()(array); + } +}; + +template <typename T> +class CompiledMethodStorage::LengthPrefixedArrayAlloc { + public: + explicit LengthPrefixedArrayAlloc(SwapSpace* swap_space) + : swap_space_(swap_space) { + } + + const LengthPrefixedArray<T>* Copy(const ArrayRef<const T>& array) { + return CopyArray(swap_space_, array); + } + + void Destroy(const LengthPrefixedArray<T>* array) { + ReleaseArray(swap_space_, array); + } + + private: + SwapSpace* const swap_space_; +}; + +class CompiledMethodStorage::ThunkMapKey { + public: + ThunkMapKey(linker::LinkerPatch::Type type, uint32_t custom_value1, uint32_t custom_value2) + : type_(type), custom_value1_(custom_value1), custom_value2_(custom_value2) {} + + bool operator<(const ThunkMapKey& other) const { + if (custom_value1_ != other.custom_value1_) { + return custom_value1_ < other.custom_value1_; + } + if (custom_value2_ != other.custom_value2_) { + return custom_value2_ < other.custom_value2_; + } + return type_ < other.type_; + } + + private: + linker::LinkerPatch::Type type_; + uint32_t custom_value1_; + uint32_t custom_value2_; +}; + +class CompiledMethodStorage::ThunkMapValue { + public: + ThunkMapValue(std::vector<uint8_t, SwapAllocator<uint8_t>>&& code, + const std::string& debug_name) + : code_(std::move(code)), debug_name_(debug_name) {} + + ArrayRef<const uint8_t> GetCode() const { + return ArrayRef<const uint8_t>(code_); + } + + const std::string& GetDebugName() const { + return debug_name_; + } + + private: + std::vector<uint8_t, SwapAllocator<uint8_t>> code_; + std::string debug_name_; +}; + +CompiledMethodStorage::CompiledMethodStorage(int swap_fd) + : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)), + dedupe_enabled_(true), + dedupe_code_("dedupe code", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())), + dedupe_vmap_table_("dedupe vmap table", + LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())), + dedupe_cfi_info_("dedupe cfi info", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())), + dedupe_linker_patches_("dedupe cfi info", + LengthPrefixedArrayAlloc<linker::LinkerPatch>(swap_space_.get())), + thunk_map_lock_("thunk_map_lock"), + thunk_map_(std::less<ThunkMapKey>(), SwapAllocator<ThunkMapValueType>(swap_space_.get())) { +} + +CompiledMethodStorage::~CompiledMethodStorage() { + // All done by member destructors. +} + +void CompiledMethodStorage::DumpMemoryUsage(std::ostream& os, bool extended) const { + if (swap_space_.get() != nullptr) { + const size_t swap_size = swap_space_->GetSize(); + os << " swap=" << PrettySize(swap_size) << " (" << swap_size << "B)"; + } + if (extended) { + Thread* self = Thread::Current(); + os << "\nCode dedupe: " << dedupe_code_.DumpStats(self); + os << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats(self); + os << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats(self); + } +} + +const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCode( + const ArrayRef<const uint8_t>& code) { + return AllocateOrDeduplicateArray(code, &dedupe_code_); +} + +void CompiledMethodStorage::ReleaseCode(const LengthPrefixedArray<uint8_t>* code) { + ReleaseArrayIfNotDeduplicated(code); +} + +size_t CompiledMethodStorage::UniqueCodeEntries() const { + DCHECK(DedupeEnabled()); + return dedupe_code_.Size(Thread::Current()); +} + +const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateVMapTable( + const ArrayRef<const uint8_t>& table) { + return AllocateOrDeduplicateArray(table, &dedupe_vmap_table_); +} + +void CompiledMethodStorage::ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table) { + ReleaseArrayIfNotDeduplicated(table); +} + +size_t CompiledMethodStorage::UniqueVMapTableEntries() const { + DCHECK(DedupeEnabled()); + return dedupe_vmap_table_.Size(Thread::Current()); +} + +const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCFIInfo( + const ArrayRef<const uint8_t>& cfi_info) { + return AllocateOrDeduplicateArray(cfi_info, &dedupe_cfi_info_); +} + +void CompiledMethodStorage::ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info) { + ReleaseArrayIfNotDeduplicated(cfi_info); +} + +size_t CompiledMethodStorage::UniqueCFIInfoEntries() const { + DCHECK(DedupeEnabled()); + return dedupe_cfi_info_.Size(Thread::Current()); +} + +const LengthPrefixedArray<linker::LinkerPatch>* CompiledMethodStorage::DeduplicateLinkerPatches( + const ArrayRef<const linker::LinkerPatch>& linker_patches) { + return AllocateOrDeduplicateArray(linker_patches, &dedupe_linker_patches_); +} + +void CompiledMethodStorage::ReleaseLinkerPatches( + const LengthPrefixedArray<linker::LinkerPatch>* linker_patches) { + ReleaseArrayIfNotDeduplicated(linker_patches); +} + +size_t CompiledMethodStorage::UniqueLinkerPatchesEntries() const { + DCHECK(DedupeEnabled()); + return dedupe_linker_patches_.Size(Thread::Current()); +} + +CompiledMethodStorage::ThunkMapKey CompiledMethodStorage::GetThunkMapKey( + const linker::LinkerPatch& linker_patch) { + uint32_t custom_value1 = 0u; + uint32_t custom_value2 = 0u; + switch (linker_patch.GetType()) { + case linker::LinkerPatch::Type::kCallEntrypoint: + custom_value1 = linker_patch.EntrypointOffset(); + break; + case linker::LinkerPatch::Type::kBakerReadBarrierBranch: + custom_value1 = linker_patch.GetBakerCustomValue1(); + custom_value2 = linker_patch.GetBakerCustomValue2(); + break; + case linker::LinkerPatch::Type::kCallRelative: + // No custom values. + break; + default: + LOG(FATAL) << "Unexpected patch type: " << linker_patch.GetType(); + UNREACHABLE(); + } + return ThunkMapKey(linker_patch.GetType(), custom_value1, custom_value2); +} + +CompiledMethod* CompiledMethodStorage::CreateCompiledMethod( + InstructionSet instruction_set, + ArrayRef<const uint8_t> code, + ArrayRef<const uint8_t> stack_map, + ArrayRef<const uint8_t> cfi, + ArrayRef<const linker::LinkerPatch> patches, + bool is_intrinsic) { + CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod( + this, instruction_set, code, stack_map, cfi, patches); + if (is_intrinsic) { + compiled_method->MarkAsIntrinsic(); + } + return compiled_method; +} + +ArrayRef<const uint8_t> CompiledMethodStorage::GetThunkCode(const linker::LinkerPatch& linker_patch, + /*out*/ std::string* debug_name) { + ThunkMapKey key = GetThunkMapKey(linker_patch); + MutexLock lock(Thread::Current(), thunk_map_lock_); + auto it = thunk_map_.find(key); + if (it != thunk_map_.end()) { + const ThunkMapValue& value = it->second; + if (debug_name != nullptr) { + *debug_name = value.GetDebugName(); + } + return value.GetCode(); + } else { + if (debug_name != nullptr) { + *debug_name = std::string(); + } + return ArrayRef<const uint8_t>(); + } +} + +void CompiledMethodStorage::SetThunkCode(const linker::LinkerPatch& linker_patch, + ArrayRef<const uint8_t> code, + const std::string& debug_name) { + DCHECK(!code.empty()); + ThunkMapKey key = GetThunkMapKey(linker_patch); + std::vector<uint8_t, SwapAllocator<uint8_t>> code_copy( + code.begin(), code.end(), SwapAllocator<uint8_t>(swap_space_.get())); + ThunkMapValue value(std::move(code_copy), debug_name); + MutexLock lock(Thread::Current(), thunk_map_lock_); + // Note: Multiple threads can try and compile the same thunk, so this may not create a new entry. + thunk_map_.emplace(key, std::move(value)); +} + +} // namespace art diff --git a/dex2oat/driver/compiled_method_storage.h b/dex2oat/driver/compiled_method_storage.h new file mode 100644 index 0000000000..3b0304ed2f --- /dev/null +++ b/dex2oat/driver/compiled_method_storage.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_DEX2OAT_DRIVER_COMPILED_METHOD_STORAGE_H_ +#define ART_DEX2OAT_DRIVER_COMPILED_METHOD_STORAGE_H_ + +#include <iosfwd> +#include <map> +#include <memory> + +#include "base/array_ref.h" +#include "base/length_prefixed_array.h" +#include "base/macros.h" +#include "driver/compiled_code_storage.h" +#include "utils/dedupe_set.h" +#include "utils/swap_space.h" + +namespace art { + +namespace linker { +class LinkerPatch; +} // namespace linker + +// TODO: Find a better name. This stores both method and non-method (thunks) code. +class CompiledMethodStorage final : public CompiledCodeStorage { + public: + explicit CompiledMethodStorage(int swap_fd); + ~CompiledMethodStorage(); + + void DumpMemoryUsage(std::ostream& os, bool extended) const; + + void SetDedupeEnabled(bool dedupe_enabled) { + dedupe_enabled_ = dedupe_enabled; + } + bool DedupeEnabled() const { + return dedupe_enabled_; + } + + SwapAllocator<void> GetSwapSpaceAllocator() { + return SwapAllocator<void>(swap_space_.get()); + } + + const LengthPrefixedArray<uint8_t>* DeduplicateCode(const ArrayRef<const uint8_t>& code); + void ReleaseCode(const LengthPrefixedArray<uint8_t>* code); + size_t UniqueCodeEntries() const; + + const LengthPrefixedArray<uint8_t>* DeduplicateVMapTable(const ArrayRef<const uint8_t>& table); + void ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table); + size_t UniqueVMapTableEntries() const; + + const LengthPrefixedArray<uint8_t>* DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info); + void ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info); + size_t UniqueCFIInfoEntries() const; + + const LengthPrefixedArray<linker::LinkerPatch>* DeduplicateLinkerPatches( + const ArrayRef<const linker::LinkerPatch>& linker_patches); + void ReleaseLinkerPatches(const LengthPrefixedArray<linker::LinkerPatch>* linker_patches); + size_t UniqueLinkerPatchesEntries() const; + + CompiledMethod* CreateCompiledMethod(InstructionSet instruction_set, + ArrayRef<const uint8_t> code, + ArrayRef<const uint8_t> stack_map, + ArrayRef<const uint8_t> cfi, + ArrayRef<const linker::LinkerPatch> patches, + bool is_intrinsic) override; + + // Returns the code associated with the given patch. + // If the code has not been set, returns empty data. + // If `debug_name` is not null, stores the associated debug name in `*debug_name`. + ArrayRef<const uint8_t> GetThunkCode(const linker::LinkerPatch& linker_patch, + /*out*/ std::string* debug_name = nullptr) override; + + // Sets the code and debug name associated with the given patch. + void SetThunkCode(const linker::LinkerPatch& linker_patch, + ArrayRef<const uint8_t> code, + const std::string& debug_name) override; + + private: + class ThunkMapKey; + class ThunkMapValue; + using ThunkMapValueType = std::pair<const ThunkMapKey, ThunkMapValue>; + using ThunkMap = std::map<ThunkMapKey, + ThunkMapValue, + std::less<ThunkMapKey>, + SwapAllocator<ThunkMapValueType>>; + static_assert(std::is_same<ThunkMapValueType, ThunkMap::value_type>::value, "Value type check."); + + static ThunkMapKey GetThunkMapKey(const linker::LinkerPatch& linker_patch); + + template <typename T, typename DedupeSetType> + const LengthPrefixedArray<T>* AllocateOrDeduplicateArray(const ArrayRef<const T>& data, + DedupeSetType* dedupe_set); + + template <typename T> + void ReleaseArrayIfNotDeduplicated(const LengthPrefixedArray<T>* array); + + // DeDuplication data structures. + template <typename ContentType> + class DedupeHashFunc; + + template <typename T> + class LengthPrefixedArrayAlloc; + + template <typename T> + using ArrayDedupeSet = DedupeSet<ArrayRef<const T>, + LengthPrefixedArray<T>, + LengthPrefixedArrayAlloc<T>, + size_t, + DedupeHashFunc<const T>, + 4>; + + // Swap pool and allocator used for native allocations. May be file-backed. Needs to be first + // as other fields rely on this. + std::unique_ptr<SwapSpace> swap_space_; + + bool dedupe_enabled_; + + ArrayDedupeSet<uint8_t> dedupe_code_; + ArrayDedupeSet<uint8_t> dedupe_vmap_table_; + ArrayDedupeSet<uint8_t> dedupe_cfi_info_; + ArrayDedupeSet<linker::LinkerPatch> dedupe_linker_patches_; + + Mutex thunk_map_lock_; + ThunkMap thunk_map_ GUARDED_BY(thunk_map_lock_); + + DISALLOW_COPY_AND_ASSIGN(CompiledMethodStorage); +}; + +} // namespace art + +#endif // ART_DEX2OAT_DRIVER_COMPILED_METHOD_STORAGE_H_ diff --git a/dex2oat/driver/compiled_method_storage_test.cc b/dex2oat/driver/compiled_method_storage_test.cc new file mode 100644 index 0000000000..05eacd848d --- /dev/null +++ b/dex2oat/driver/compiled_method_storage_test.cc @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "compiled_method_storage.h" + +#include <gtest/gtest.h> + +#include "compiled_method-inl.h" + +namespace art { + +TEST(CompiledMethodStorage, Deduplicate) { + CompiledMethodStorage storage(/* swap_fd= */ -1); + + ASSERT_TRUE(storage.DedupeEnabled()); // The default. + + const uint8_t raw_code1[] = { 1u, 2u, 3u }; + const uint8_t raw_code2[] = { 4u, 3u, 2u, 1u }; + ArrayRef<const uint8_t> code[] = { + ArrayRef<const uint8_t>(raw_code1), + ArrayRef<const uint8_t>(raw_code2), + }; + const uint8_t raw_vmap_table1[] = { 2, 4, 6 }; + const uint8_t raw_vmap_table2[] = { 7, 5, 3, 1 }; + ArrayRef<const uint8_t> vmap_table[] = { + ArrayRef<const uint8_t>(raw_vmap_table1), + ArrayRef<const uint8_t>(raw_vmap_table2), + }; + const uint8_t raw_cfi_info1[] = { 1, 3, 5 }; + const uint8_t raw_cfi_info2[] = { 8, 6, 4, 2 }; + ArrayRef<const uint8_t> cfi_info[] = { + ArrayRef<const uint8_t>(raw_cfi_info1), + ArrayRef<const uint8_t>(raw_cfi_info2), + }; + const linker::LinkerPatch raw_patches1[] = { + linker::LinkerPatch::IntrinsicReferencePatch(0u, 0u, 0u), + linker::LinkerPatch::RelativeMethodPatch(4u, nullptr, 0u, 1u), + }; + const linker::LinkerPatch raw_patches2[] = { + linker::LinkerPatch::IntrinsicReferencePatch(0u, 0u, 0u), + linker::LinkerPatch::RelativeMethodPatch(4u, nullptr, 0u, 2u), + }; + ArrayRef<const linker::LinkerPatch> patches[] = { + ArrayRef<const linker::LinkerPatch>(raw_patches1), + ArrayRef<const linker::LinkerPatch>(raw_patches2), + }; + + std::vector<CompiledMethod*> compiled_methods; + compiled_methods.reserve(1u << 4); + for (auto&& c : code) { + for (auto&& v : vmap_table) { + for (auto&& f : cfi_info) { + for (auto&& p : patches) { + compiled_methods.push_back(CompiledMethod::SwapAllocCompiledMethod( + &storage, InstructionSet::kNone, c, v, f, p)); + } + } + } + } + constexpr size_t code_bit = 1u << 3; + constexpr size_t vmap_table_bit = 1u << 2; + constexpr size_t cfi_info_bit = 1u << 1; + constexpr size_t patches_bit = 1u << 0; + CHECK_EQ(compiled_methods.size(), 1u << 4); + for (size_t i = 0; i != compiled_methods.size(); ++i) { + for (size_t j = 0; j != compiled_methods.size(); ++j) { + CompiledMethod* lhs = compiled_methods[i]; + CompiledMethod* rhs = compiled_methods[j]; + bool same_code = ((i ^ j) & code_bit) == 0u; + bool same_vmap_table = ((i ^ j) & vmap_table_bit) == 0u; + bool same_cfi_info = ((i ^ j) & cfi_info_bit) == 0u; + bool same_patches = ((i ^ j) & patches_bit) == 0u; + ASSERT_EQ(same_code, lhs->GetQuickCode().data() == rhs->GetQuickCode().data()) + << i << " " << j; + ASSERT_EQ(same_vmap_table, lhs->GetVmapTable().data() == rhs->GetVmapTable().data()) + << i << " " << j; + ASSERT_EQ(same_cfi_info, lhs->GetCFIInfo().data() == rhs->GetCFIInfo().data()) + << i << " " << j; + ASSERT_EQ(same_patches, lhs->GetPatches().data() == rhs->GetPatches().data()) + << i << " " << j; + } + } + for (CompiledMethod* method : compiled_methods) { + CompiledMethod::ReleaseSwapAllocatedCompiledMethod(&storage, method); + } +} + +} // namespace art diff --git a/dex2oat/driver/compiler_driver.cc b/dex2oat/driver/compiler_driver.cc index d9509b0c53..23bbe14efe 100644 --- a/dex2oat/driver/compiler_driver.cc +++ b/dex2oat/driver/compiler_driver.cc @@ -87,6 +87,7 @@ #include "verifier/class_verifier.h" #include "verifier/verifier_deps.h" #include "verifier/verifier_enums.h" +#include "well_known_classes-inl.h" namespace art { @@ -252,10 +253,12 @@ class CompilerDriver::AOTCompilationStats { CompilerDriver::CompilerDriver( const CompilerOptions* compiler_options, + const VerificationResults* verification_results, Compiler::Kind compiler_kind, size_t thread_count, int swap_fd) : compiler_options_(compiler_options), + verification_results_(verification_results), compiler_(), compiler_kind_(compiler_kind), number_of_soft_verifier_failures_(0), @@ -493,7 +496,7 @@ static void CompileMethodQuick( // Method is annotated with @NeverCompile and should not be compiled. } else { const CompilerOptions& compiler_options = driver->GetCompilerOptions(); - const VerificationResults* results = compiler_options.GetVerificationResults(); + const VerificationResults* results = driver->GetVerificationResults(); DCHECK(results != nullptr); MethodReference method_ref(&dex_file, method_idx); // Don't compile class initializers unless kEverything. @@ -1008,13 +1011,68 @@ class RecordImageClassesVisitor : public ClassVisitor { HashSet<std::string>* const image_classes_; }; -// Add classes which contain intrinsics methods to the list of image classes. -static void AddClassesContainingIntrinsics(/* out */ HashSet<std::string>* image_classes) { -#define ADD_INTRINSIC_OWNER_CLASS(_, __, ___, ____, _____, ClassName, ______, _______) \ - image_classes->insert(ClassName); +// Verify that classes which contain intrinsics methods are in the list of image classes. +static void VerifyClassesContainingIntrinsicsAreImageClasses(HashSet<std::string>* image_classes) { +#define CHECK_INTRINSIC_OWNER_CLASS(_, __, ___, ____, _____, ClassName, ______, _______) \ + CHECK(image_classes->find(std::string_view(ClassName)) != image_classes->end()); - INTRINSICS_LIST(ADD_INTRINSIC_OWNER_CLASS) -#undef ADD_INTRINSIC_OWNER_CLASS + INTRINSICS_LIST(CHECK_INTRINSIC_OWNER_CLASS) +#undef CHECK_INTRINSIC_OWNER_CLASS +} + +// We need to put classes required by app class loaders to the boot image, +// otherwise we would not be able to store app class loaders in app images. +static void AddClassLoaderClasses(/* out */ HashSet<std::string>* image_classes) { + ScopedObjectAccess soa(Thread::Current()); + // Well known classes have been loaded and shall be added to image classes + // by the `RecordImageClassesVisitor`. However, there are fields with array + // types which we need to add to the image classes explicitly. + ArtField* class_loader_array_fields[] = { + WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders, + // BaseDexClassLoader.sharedLibraryLoadersAfter has the same array type as above. + WellKnownClasses::dalvik_system_DexPathList_dexElements, + }; + for (ArtField* field : class_loader_array_fields) { + const char* field_type_descriptor = field->GetTypeDescriptor(); + DCHECK_EQ(field_type_descriptor[0], '['); + image_classes->insert(field_type_descriptor); + } +} + +static void VerifyClassLoaderClassesAreImageClasses(/* out */ HashSet<std::string>* image_classes) { + ScopedObjectAccess soa(Thread::Current()); + ScopedAssertNoThreadSuspension sants(__FUNCTION__); + ObjPtr<mirror::Class> class_loader_classes[] = { + WellKnownClasses::dalvik_system_BaseDexClassLoader.Get(), + WellKnownClasses::dalvik_system_DelegateLastClassLoader.Get(), + WellKnownClasses::dalvik_system_DexClassLoader.Get(), + WellKnownClasses::dalvik_system_DexFile.Get(), + WellKnownClasses::dalvik_system_DexPathList.Get(), + WellKnownClasses::dalvik_system_DexPathList__Element.Get(), + WellKnownClasses::dalvik_system_InMemoryDexClassLoader.Get(), + WellKnownClasses::dalvik_system_PathClassLoader.Get(), + WellKnownClasses::java_lang_BootClassLoader.Get(), + WellKnownClasses::java_lang_ClassLoader.Get(), + }; + for (ObjPtr<mirror::Class> klass : class_loader_classes) { + std::string temp; + std::string_view descriptor = klass->GetDescriptor(&temp); + CHECK(image_classes->find(descriptor) != image_classes->end()); + } + ArtField* class_loader_fields[] = { + WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList, + WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders, + WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter, + WellKnownClasses::dalvik_system_DexFile_cookie, + WellKnownClasses::dalvik_system_DexFile_fileName, + WellKnownClasses::dalvik_system_DexPathList_dexElements, + WellKnownClasses::dalvik_system_DexPathList__Element_dexFile, + WellKnownClasses::java_lang_ClassLoader_parent, + }; + for (ArtField* field : class_loader_fields) { + std::string_view field_type_descriptor = field->GetTypeDescriptor(); + CHECK(image_classes->find(field_type_descriptor) != image_classes->end()); + } } // Make a list of descriptors for classes to include in the image @@ -1028,7 +1086,10 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings, TimingLogger::ScopedTiming t("LoadImageClasses", timings); if (GetCompilerOptions().IsBootImage()) { - AddClassesContainingIntrinsics(image_classes); + // Image classes of intrinsics are loaded and shall be added + // to image classes by the `RecordImageClassesVisitor`. + // Add classes needed for storing class loaders in app images. + AddClassLoaderClasses(image_classes); } // Make a first pass to load all classes explicitly listed in the file @@ -1099,6 +1160,11 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings, RecordImageClassesVisitor visitor(image_classes); class_linker->VisitClasses(&visitor); + if (kIsDebugBuild && GetCompilerOptions().IsBootImage()) { + VerifyClassesContainingIntrinsicsAreImageClasses(image_classes); + VerifyClassLoaderClassesAreImageClasses(image_classes); + } + if (GetCompilerOptions().IsBootImage()) { CHECK(!image_classes->empty()); } @@ -1109,6 +1175,7 @@ static void MaybeAddToImageClasses(Thread* self, HashSet<std::string>* image_classes) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK_EQ(self, Thread::Current()); + DCHECK(klass->IsResolved()); Runtime* runtime = Runtime::Current(); gc::Heap* heap = runtime->GetHeap(); if (heap->ObjectIsInBootImageSpace(klass)) { @@ -1218,12 +1285,16 @@ class ClinitImageUpdate { data_->image_class_descriptors_->erase(it); } } else if (can_include_in_image) { - // Check whether it is initialized and has a clinit. They must be kept, too. - if (klass->IsInitialized() && klass->FindClassInitializer( - Runtime::Current()->GetClassLinker()->GetImagePointerSize()) != nullptr) { - DCHECK(!Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass->GetDexCache())) - << klass->PrettyDescriptor(); - data_->image_classes_.push_back(data_->hs_.NewHandle(klass)); + // Check whether the class is initialized and has a clinit or static fields. + // Such classes must be kept too. + if (klass->IsInitialized()) { + PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + if (klass->FindClassInitializer(pointer_size) != nullptr || + klass->NumStaticFields() != 0) { + DCHECK(!Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass->GetDexCache())) + << klass->PrettyDescriptor(); + data_->image_classes_.push_back(data_->hs_.NewHandle(klass)); + } } } return true; @@ -1536,8 +1607,7 @@ class ResolveTypeVisitor : public CompilationVisitor { mirror::Throwable* exception = soa.Self()->GetException(); DCHECK(exception != nullptr); VLOG(compiler) << "Exception during type resolution: " << exception->Dump(); - if (exception->GetClass() == - soa.Decode<mirror::Class>(WellKnownClasses::java_lang_OutOfMemoryError)) { + if (exception->GetClass() == WellKnownClasses::java_lang_OutOfMemoryError.Get()) { // There's little point continuing compilation if the heap is exhausted. // Trying to do so would also introduce non-deterministic compilation results. LOG(FATAL) << "Out of memory during type resolution for compilation"; @@ -1653,8 +1723,8 @@ static void LoadAndUpdateStatus(const ClassAccessor& accessor, bool CompilerDriver::FastVerify(jobject jclass_loader, const std::vector<const DexFile*>& dex_files, TimingLogger* timings) { - verifier::VerifierDeps* verifier_deps = - Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps(); + CompilerCallbacks* callbacks = Runtime::Current()->GetCompilerCallbacks(); + verifier::VerifierDeps* verifier_deps = callbacks->GetVerifierDeps(); // If there exist VerifierDeps that aren't the ones we just created to output, use them to verify. if (verifier_deps == nullptr || verifier_deps->OutputOnly()) { return false; @@ -1672,6 +1742,9 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, class_loader, dex_files, &error_msg)) { + // Clear the information we have as we are going to re-verify and we do not + // want to keep that a class is verified. + verifier_deps->ClearData(dex_files); LOG(WARNING) << "Fast verification failed: " << error_msg; return false; } @@ -1690,28 +1763,34 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, const std::vector<bool>& verified_classes = verifier_deps->GetVerifiedClasses(*dex_file); DCHECK_EQ(verified_classes.size(), dex_file->NumClassDefs()); for (ClassAccessor accessor : dex_file->GetClasses()) { - if (verified_classes[accessor.GetClassDefIndex()]) { - if (compiler_only_verifies) { - // Just update the compiled_classes_ map. The compiler doesn't need to resolve - // the type. - ClassReference ref(dex_file, accessor.GetClassDefIndex()); - const ClassStatus existing = ClassStatus::kNotReady; - ClassStateTable::InsertResult result = - compiled_classes_.Insert(ref, existing, ClassStatus::kVerifiedNeedsAccessChecks); - CHECK_EQ(result, ClassStateTable::kInsertResultSuccess) << ref.dex_file->GetLocation(); - } else { - // Update the class status, so later compilation stages know they don't need to verify - // the class. - LoadAndUpdateStatus( - accessor, ClassStatus::kVerifiedNeedsAccessChecks, class_loader, soa.Self()); - } - } else if (!compiler_only_verifies) { - // Make sure later compilation stages know they should not try to verify - // this class again. - LoadAndUpdateStatus(accessor, - ClassStatus::kRetryVerificationAtRuntime, - class_loader, - soa.Self()); + ClassStatus status = verified_classes[accessor.GetClassDefIndex()] + ? ClassStatus::kVerifiedNeedsAccessChecks + : ClassStatus::kRetryVerificationAtRuntime; + if (compiler_only_verifies) { + // Just update the compiled_classes_ map. The compiler doesn't need to resolve + // the type. + ClassReference ref(dex_file, accessor.GetClassDefIndex()); + const ClassStatus existing = ClassStatus::kNotReady; + // Note: when dex files are compiled inidividually, the class may have + // been verified in a previous stage. This means this insertion can + // fail, but that's OK. + compiled_classes_.Insert(ref, existing, status); + } else { + // Update the class status, so later compilation stages know they don't need to verify + // the class. + LoadAndUpdateStatus(accessor, status, class_loader, soa.Self()); + } + + // Vdex marks class as unverified for two reasons only: + // 1. It has a hard failure, or + // 2. Once of its method needs lock counting. + // + // The optimizing compiler expects a method to not have a hard failure before + // compiling it, so for simplicity just disable any compilation of methods + // of these classes. + if (status == ClassStatus::kRetryVerificationAtRuntime) { + ClassReference ref(dex_file, accessor.GetClassDefIndex()); + callbacks->AddUncompilableClass(ref); } } } @@ -2100,13 +2179,16 @@ class InitializeClassVisitor : public CompilationVisitor { bool too_many_encoded_fields = (!is_boot_image && !is_boot_image_extension) && klass->NumStaticFields() > kMaxEncodedFields; + bool have_profile = (compiler_options.GetProfileCompilationInfo() != nullptr) && + !compiler_options.GetProfileCompilationInfo()->IsEmpty(); // If the class was not initialized, we can proceed to see if we can initialize static // fields. Limit the max number of encoded fields. if (!klass->IsInitialized() && (is_app_image || is_boot_image || is_boot_image_extension) && - try_initialize_with_superclasses && - !too_many_encoded_fields && - compiler_options.IsImageClass(descriptor)) { + try_initialize_with_superclasses && !too_many_encoded_fields && + compiler_options.IsImageClass(descriptor) && + // TODO(b/274077782): remove this test. + (have_profile || !is_boot_image_extension)) { bool can_init_static_fields = false; if (is_boot_image || is_boot_image_extension) { // We need to initialize static fields, we only do this for image classes that aren't @@ -2232,6 +2314,19 @@ class InitializeClassVisitor : public CompilationVisitor { // Make sure the class initialization did not leave any local references. self->GetJniEnv()->AssertLocalsEmpty(); } + + if (!klass->IsVisiblyInitialized() && + (is_boot_image || is_boot_image_extension) && + !compiler_options.IsPreloadedClass(PrettyDescriptor(descriptor).c_str())) { + klass->SetInBootImageAndNotInPreloadedClasses(); + } + + if (compiler_options.CompileArtTest()) { + // For stress testing and unit-testing the clinit check in compiled code feature. + if (kIsDebugBuild || EndsWith(std::string_view(descriptor), "$NoPreloadHolder;")) { + klass->SetInBootImageAndNotInPreloadedClasses(); + } + } } private: @@ -2493,7 +2588,8 @@ static void CompileDexFile(CompilerDriver* driver, ClassAccessor accessor(dex_file, class_def_index); CompilerDriver* const driver = context.GetCompiler(); // Skip compiling classes with generic verifier failures since they will still fail at runtime - if (driver->GetCompilerOptions().GetVerificationResults()->IsClassRejected(ref)) { + DCHECK(driver->GetVerificationResults() != nullptr); + if (driver->GetVerificationResults()->IsClassRejected(ref)) { return; } // Use a scoped object access to perform to the quick SkipClass check. diff --git a/dex2oat/driver/compiler_driver.h b/dex2oat/driver/compiler_driver.h index ed8fc2f222..7985771246 100644 --- a/dex2oat/driver/compiler_driver.h +++ b/dex2oat/driver/compiler_driver.h @@ -85,6 +85,7 @@ class CompilerDriver { // can assume will be in the image, with null implying all available // classes. CompilerDriver(const CompilerOptions* compiler_options, + const VerificationResults* verification_results, Compiler::Kind compiler_kind, size_t thread_count, int swap_fd); @@ -115,6 +116,10 @@ class CompilerDriver { return *compiler_options_; } + const VerificationResults* GetVerificationResults() const { + return verification_results_; + } + Compiler* GetCompiler() const { return compiler_.get(); } @@ -295,6 +300,7 @@ class CompilerDriver { /*inout*/ TimingLogger* timings); const CompilerOptions* const compiler_options_; + const VerificationResults* const verification_results_; std::unique_ptr<Compiler> compiler_; Compiler::Kind compiler_kind_; diff --git a/dex2oat/driver/compiler_driver_test.cc b/dex2oat/driver/compiler_driver_test.cc index 65aa88869f..759426a1d3 100644 --- a/dex2oat/driver/compiler_driver_test.cc +++ b/dex2oat/driver/compiler_driver_test.cc @@ -25,6 +25,7 @@ #include "base/casts.h" #include "class_linker-inl.h" #include "common_compiler_driver_test.h" +#include "compiled_method-inl.h" #include "compiler_callbacks.h" #include "dex/dex_file.h" #include "dex/dex_file_types.h" @@ -80,14 +81,19 @@ class CompilerDriverTest : public CommonCompilerDriverTest { void MakeExecutable(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { CHECK(method != nullptr); - const CompiledMethod* compiled_method = nullptr; + const void* method_code = nullptr; if (!method->IsAbstract()) { - const DexFile& dex_file = *method->GetDexFile(); - compiled_method = - compiler_driver_->GetCompiledMethod(MethodReference(&dex_file, - method->GetDexMethodIndex())); + MethodReference method_ref(method->GetDexFile(), method->GetDexMethodIndex()); + const CompiledMethod* compiled_method = compiler_driver_->GetCompiledMethod(method_ref); + // If the code size is 0 it means the method was skipped due to profile guided compilation. + if (compiled_method != nullptr && compiled_method->GetQuickCode().size() != 0u) { + method_code = CommonCompilerTest::MakeExecutable(compiled_method->GetQuickCode(), + compiled_method->GetVmapTable(), + compiled_method->GetInstructionSet()); + LOG(INFO) << "MakeExecutable " << method->PrettyMethod() << " code=" << method_code; + } } - CommonCompilerTest::MakeExecutable(method, compiled_method); + runtime_->GetInstrumentation()->InitializeMethodsCode(method, /*aot_code=*/ method_code); } void MakeDexFileExecutable(jobject class_loader, const DexFile& dex_file) { @@ -124,19 +130,15 @@ TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) { ASSERT_TRUE(java_lang_dex_file_ != nullptr); const DexFile& dex = *java_lang_dex_file_; ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(soa.Self(), dex); - EXPECT_EQ(dex.NumStringIds(), dex_cache->NumStrings()); for (size_t i = 0; i < dex_cache->NumStrings(); i++) { const ObjPtr<mirror::String> string = dex_cache->GetResolvedString(dex::StringIndex(i)); EXPECT_TRUE(string != nullptr) << "string_idx=" << i; } - EXPECT_EQ(dex.NumTypeIds(), dex_cache->NumResolvedTypes()); for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { const ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(dex::TypeIndex(i)); EXPECT_TRUE(type != nullptr) << "type_idx=" << i << " " << dex.GetTypeDescriptor(dex.GetTypeId(dex::TypeIndex(i))); } - EXPECT_TRUE(dex_cache->StaticMethodSize() == dex_cache->NumResolvedMethods() - || dex.NumMethodIds() == dex_cache->NumResolvedMethods()); for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) { // FIXME: This is outdated for hash-based method array. ArtMethod* method = dex_cache->GetResolvedMethod(i); @@ -147,8 +149,6 @@ TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) { << " " << dex.GetMethodDeclaringClassDescriptor(dex.GetMethodId(i)) << " " << dex.GetMethodName(dex.GetMethodId(i)); } - EXPECT_TRUE(dex_cache->StaticArtFieldSize() == dex_cache->NumResolvedFields() - || dex.NumFieldIds() == dex_cache->NumResolvedFields()); for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) { // FIXME: This is outdated for hash-based field array. ArtField* field = dex_cache->GetResolvedField(i); diff --git a/dex2oat/linker/arm/relative_patcher_arm_base.cc b/dex2oat/linker/arm/relative_patcher_arm_base.cc index 35e799ac73..1cb72f6747 100644 --- a/dex2oat/linker/arm/relative_patcher_arm_base.cc +++ b/dex2oat/linker/arm/relative_patcher_arm_base.cc @@ -17,9 +17,9 @@ #include "linker/arm/relative_patcher_arm_base.h" #include "base/stl_util.h" -#include "compiled_method-inl.h" #include "debug/method_debug_info.h" #include "dex/dex_file_types.h" +#include "driver/compiled_method-inl.h" #include "linker/linker_patch.h" #include "oat.h" #include "oat_quick_method_header.h" @@ -462,7 +462,7 @@ void ArmBaseRelativePatcher::AddUnreservedThunk(ThunkData* data) { } unreserved_thunks_.insert(unreserved_thunks_.begin() + index, data); // We may need to update the max next offset(s) if the thunk code would not fit. - size_t alignment = GetInstructionSetAlignment(instruction_set_); + size_t alignment = GetInstructionSetCodeAlignment(instruction_set_); if (index + 1u != unreserved_thunks_.size()) { // Note: Ignore the return value as we need to process previous thunks regardless. data->MakeSpaceBefore(*unreserved_thunks_[index + 1u], alignment); @@ -501,7 +501,8 @@ void ArmBaseRelativePatcher::ResolveMethodCalls(uint32_t quick_code_offset, if (!result.first) { break; } - uint32_t target_offset = result.second - CompiledCode::CodeDelta(instruction_set_); + uint32_t target_offset = + result.second - GetInstructionSetEntryPointAdjustment(instruction_set_); if (target_offset >= patch_offset) { DCHECK_LE(target_offset - patch_offset, max_positive_displacement); } else if (patch_offset - target_offset > max_negative_displacement) { @@ -535,7 +536,7 @@ void ArmBaseRelativePatcher::ResolveMethodCalls(uint32_t quick_code_offset, inline uint32_t ArmBaseRelativePatcher::CalculateMaxNextOffset(uint32_t patch_offset, const ThunkKey& key) { return RoundDown(patch_offset + MaxPositiveDisplacement(key), - GetInstructionSetAlignment(instruction_set_)); + GetInstructionSetCodeAlignment(instruction_set_)); } inline ArmBaseRelativePatcher::ThunkData ArmBaseRelativePatcher::ThunkDataForPatch( diff --git a/dex2oat/linker/arm/relative_patcher_thumb2.cc b/dex2oat/linker/arm/relative_patcher_thumb2.cc index 99728cf52b..45a4e8b061 100644 --- a/dex2oat/linker/arm/relative_patcher_thumb2.cc +++ b/dex2oat/linker/arm/relative_patcher_thumb2.cc @@ -22,7 +22,7 @@ #include "art_method.h" #include "base/bit_utils.h" #include "base/malloc_arena_pool.h" -#include "compiled_method.h" +#include "driver/compiled_method.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "linker/linker_patch.h" #include "lock_word.h" diff --git a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc index 296bf61cbc..f7abd6b9eb 100644 --- a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc +++ b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc @@ -145,7 +145,7 @@ class Thumb2RelativePatcherTest : public RelativePatcherTest { const ArrayRef<const uint8_t>& last_method_code, const ArrayRef<const LinkerPatch>& last_method_patches, uint32_t distance_without_thunks) { - CHECK_EQ(distance_without_thunks % kArmAlignment, 0u); + CHECK_EQ(distance_without_thunks % kArmCodeAlignment, 0u); uint32_t method1_offset = kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader); AddCompiledMethod(MethodRef(1u), method1_code, method1_patches); @@ -153,7 +153,7 @@ class Thumb2RelativePatcherTest : public RelativePatcherTest { // We want to put the last method at a very precise offset. const uint32_t last_method_offset = method1_offset + distance_without_thunks; - CHECK_ALIGNED(last_method_offset, kArmAlignment); + CHECK_ALIGNED(last_method_offset, kArmCodeAlignment); const uint32_t gap_end = last_method_offset - sizeof(OatQuickMethodHeader); // Fill the gap with intermediate methods in chunks of 2MiB and the first in [2MiB, 4MiB). @@ -562,24 +562,25 @@ TEST_F(Thumb2RelativePatcherTest, CallOtherJustTooFarAfter) { bl_offset_in_method1 + just_over_max_positive_disp); ASSERT_EQ(kExpectedLastMethodIdx, last_method_idx); uint32_t method_after_thunk_idx = last_method_idx; - if (sizeof(OatQuickMethodHeader) < kArmAlignment) { - // The thunk needs to start on a kArmAlignment-aligned address before the address where the + if (sizeof(OatQuickMethodHeader) < kArmCodeAlignment) { + // The thunk needs to start on a kArmCodeAlignment-aligned address before the address where the // last method would have been if there was no thunk. If the size of the OatQuickMethodHeader - // is at least kArmAlignment, the thunk start shall fit between the previous filler method + // is at least kArmCodeAlignment, the thunk start shall fit between the previous filler method // and that address. Otherwise, it shall be inserted before that filler method. method_after_thunk_idx -= 1u; } uint32_t method1_offset = GetMethodOffset(1u); uint32_t method_after_thunk_offset = GetMethodOffset(method_after_thunk_idx); - ASSERT_TRUE(IsAligned<kArmAlignment>(method_after_thunk_offset)); + ASSERT_TRUE(IsAligned<kArmCodeAlignment>(method_after_thunk_offset)); uint32_t method_after_thunk_header_offset = method_after_thunk_offset - sizeof(OatQuickMethodHeader); uint32_t thunk_size = MethodCallThunkSize(); - uint32_t thunk_offset = RoundDown(method_after_thunk_header_offset - thunk_size, kArmAlignment); + uint32_t thunk_offset = + RoundDown(method_after_thunk_header_offset - thunk_size, kArmCodeAlignment); DCHECK_EQ(thunk_offset + thunk_size + CodeAlignmentSize(thunk_offset + thunk_size), method_after_thunk_header_offset); - ASSERT_TRUE(IsAligned<kArmAlignment>(thunk_offset)); + ASSERT_TRUE(IsAligned<kArmCodeAlignment>(thunk_offset)); uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1 + 4u /* PC adjustment */); ASSERT_TRUE(IsAligned<2u>(diff)); ASSERT_GE(diff, 16 * MB - (1u << 22)); // Simple encoding, unknown bits fit into imm10:imm11:0. @@ -725,7 +726,7 @@ void Thumb2RelativePatcherTest::TestBakerFieldWide(uint32_t offset, uint32_t ref Link(); // All thunks are at the end. - uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment); + uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmCodeAlignment); method_idx = 0u; for (uint32_t base_reg : kBakerValidRegs) { for (uint32_t holder_reg : kBakerValidRegs) { @@ -791,7 +792,7 @@ void Thumb2RelativePatcherTest::TestBakerFieldWide(uint32_t offset, uint32_t ref // Do not check the rest of the implementation. // The next thunk follows on the next aligned offset. - thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment); + thunk_offset += RoundUp(expected_thunk.size(), kArmCodeAlignment); } } } @@ -823,7 +824,7 @@ void Thumb2RelativePatcherTest::TestBakerFieldNarrow(uint32_t offset, uint32_t r Link(); // All thunks are at the end. - uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment); + uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmCodeAlignment); method_idx = 0u; for (uint32_t base_reg : kBakerValidRegs) { if (base_reg >= 8u) { @@ -892,7 +893,7 @@ void Thumb2RelativePatcherTest::TestBakerFieldNarrow(uint32_t offset, uint32_t r // Do not check the rest of the implementation. // The next thunk follows on the next aligned offset. - thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment); + thunk_offset += RoundUp(expected_thunk.size(), kArmCodeAlignment); } } } @@ -945,9 +946,10 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddle) { constexpr uint32_t expected_thunk_offset = kLiteralOffset1 + kPcAdjustment + /* kMaxBcondPositiveDisplacement */ ((1 << 20) - 2u); - static_assert(IsAligned<kArmAlignment>(expected_thunk_offset), "Target offset must be aligned."); + static_assert(IsAligned<kArmCodeAlignment>(expected_thunk_offset), + "Target offset must be aligned."); size_t filler1_size = expected_thunk_offset - - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment); + RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment); std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 2u); ArrayRef<const uint8_t> filler1_code(raw_filler1_code); AddCompiledMethod(MethodRef(2u), filler1_code); @@ -956,7 +958,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddle) { AddCompiledMethod(MethodRef(3u), kNopCode); constexpr uint32_t kLiteralOffset2 = 4; - static_assert(IsAligned<kArmAlignment>(kLiteralOffset2 + kPcAdjustment), + static_assert(IsAligned<kArmCodeAlignment>(kLiteralOffset2 + kPcAdjustment), "PC for BNE must be aligned."); // Allow reaching the thunk from the very beginning of a method almost 1MiB away. Backward branch @@ -968,8 +970,8 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddle) { CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false).size(); size_t filler2_size = 1 * MB - (kLiteralOffset2 + kPcAdjustment) - - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArmAlignment) - - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArmAlignment) + - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArmCodeAlignment) + - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment) - sizeof(OatQuickMethodHeader); std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 2u); ArrayRef<const uint8_t> filler2_code(raw_filler2_code); @@ -1013,16 +1015,18 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkBeforeFiller) { constexpr uint32_t expected_thunk_offset = kLiteralOffset1 + kPcAdjustment + /* kMaxBcondPositiveDisplacement + 2 */ (1u << 20); - static_assert(IsAligned<kArmAlignment>(expected_thunk_offset), "Target offset must be aligned."); + static_assert(IsAligned<kArmCodeAlignment>(expected_thunk_offset), + "Target offset must be aligned."); size_t filler1_size = expected_thunk_offset - - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment); + RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment); std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 2u); ArrayRef<const uint8_t> filler1_code(raw_filler1_code); AddCompiledMethod(MethodRef(2u), filler1_code); Link(); - const uint32_t bne = BneWWithOffset(kLiteralOffset1, RoundUp(raw_code1.size(), kArmAlignment)); + const uint32_t bne = + BneWWithOffset(kLiteralOffset1, RoundUp(raw_code1.size(), kArmCodeAlignment)); const std::vector<uint8_t> expected_code1 = RawCode({kNopWInsn, bne, kLdrWInsn, kNopInsn}); ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1))); } @@ -1043,9 +1047,10 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddleUnreachableFromLast constexpr uint32_t expected_thunk_offset = kLiteralOffset1 + kPcAdjustment + /* kMaxBcondPositiveDisplacement */ ((1 << 20) - 2u); - static_assert(IsAligned<kArmAlignment>(expected_thunk_offset), "Target offset must be aligned."); + static_assert(IsAligned<kArmCodeAlignment>(expected_thunk_offset), + "Target offset must be aligned."); size_t filler1_size = expected_thunk_offset - - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment); + RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment); std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 2u); ArrayRef<const uint8_t> filler1_code(raw_filler1_code); AddCompiledMethod(MethodRef(2u), filler1_code); @@ -1055,7 +1060,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddleUnreachableFromLast constexpr uint32_t kReachableFromOffset2 = 4; constexpr uint32_t kLiteralOffset2 = kReachableFromOffset2 + 2; - static_assert(IsAligned<kArmAlignment>(kReachableFromOffset2 + kPcAdjustment), + static_assert(IsAligned<kArmCodeAlignment>(kReachableFromOffset2 + kPcAdjustment), "PC for BNE must be aligned."); // If not for the extra NOP, this would allow reaching the thunk from the BNE @@ -1068,8 +1073,8 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddleUnreachableFromLast CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false).size(); size_t filler2_size = 1 * MB - (kReachableFromOffset2 + kPcAdjustment) - - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArmAlignment) - - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArmAlignment) + - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArmCodeAlignment) + - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment) - sizeof(OatQuickMethodHeader); std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 2u); ArrayRef<const uint8_t> filler2_code(raw_filler2_code); @@ -1091,7 +1096,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddleUnreachableFromLast const uint32_t bne_max_forward = kBneWPlus0 | 0x003f2fff; const uint32_t bne_last = - BneWWithOffset(kLiteralOffset2, RoundUp(raw_code2.size(), kArmAlignment)); + BneWWithOffset(kLiteralOffset2, RoundUp(raw_code2.size(), kArmCodeAlignment)); const std::vector<uint8_t> expected_code1 = RawCode({kNopWInsn, kNopInsn, bne_max_forward, kLdrWInsn}); const std::vector<uint8_t> expected_code2 = @@ -1123,7 +1128,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerArray) { Link(); // All thunks are at the end. - uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment); + uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmCodeAlignment); method_idx = 0u; for (uint32_t base_reg : kBakerValidRegs) { ++method_idx; @@ -1177,7 +1182,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerArray) { // Do not check the rest of the implementation. // The next thunk follows on the next aligned offset. - thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment); + thunk_offset += RoundUp(expected_thunk.size(), kArmCodeAlignment); } } @@ -1200,7 +1205,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootWide) { Link(); // All thunks are at the end. - uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment); + uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmCodeAlignment); method_idx = 0u; for (uint32_t root_reg : kBakerValidRegs) { ++method_idx; @@ -1232,7 +1237,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootWide) { // Do not check the rest of the implementation. // The next thunk follows on the next aligned offset. - thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment); + thunk_offset += RoundUp(expected_thunk.size(), kArmCodeAlignment); } } @@ -1255,7 +1260,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootNarrow) { Link(); // All thunks are at the end. - uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment); + uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmCodeAlignment); method_idx = 0u; for (uint32_t root_reg : kBakerValidRegsNarrow) { ++method_idx; @@ -1281,7 +1286,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootNarrow) { // Do not check the rest of the implementation. // The next thunk follows on the next aligned offset. - thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment); + thunk_offset += RoundUp(expected_thunk.size(), kArmCodeAlignment); } } @@ -1309,7 +1314,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootOffsetBits) { Link(); // The thunk is right after the method code. - DCHECK_ALIGNED(1 * MB, kArmAlignment); + DCHECK_ALIGNED(1 * MB, kArmCodeAlignment); std::vector<uint8_t> expected_code; for (size_t i = 0; i != num_patches; ++i) { PushBackInsn(&expected_code, ldr); @@ -1343,7 +1348,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerAndMethodCallInteraction) { // Add a method with the right size that the method code for the next one starts 1MiB // after code for method 1. size_t filler_size = - 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment) + 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment) - sizeof(OatQuickMethodHeader); std::vector<uint8_t> filler_code = GenNops(filler_size / 2u); ++method_idx; @@ -1358,16 +1363,16 @@ TEST_F(Thumb2RelativePatcherTest, BakerAndMethodCallInteraction) { } // Add 2 Baker GC root patches to the last method, one that would allow the thunk at - // 1MiB + kArmAlignment, i.e. kArmAlignment after the method call thunk, and the - // second that needs it kArmAlignment after that. Given the size of the GC root thunk - // is more than the space required by the method call thunk plus kArmAlignment, + // 1MiB + kArmCodeAlignment, i.e. kArmCodeAlignment after the method call thunk, and the + // second that needs it kArmCodeAlignment after that. Given the size of the GC root thunk + // is more than the space required by the method call thunk plus kArmCodeAlignment, // this pushes the first GC root thunk's pending MaxNextOffset() before the method call // thunk's pending MaxNextOffset() which needs to be adjusted. - ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArmAlignment) + kArmAlignment, + ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArmCodeAlignment) + kArmCodeAlignment, CompileBakerGcRootThunk(/* root_reg */ 0, /* narrow */ false).size()); - static_assert(kArmAlignment == 8, "Code below assumes kArmAlignment == 8"); - constexpr size_t kBakerLiteralOffset1 = kArmAlignment + 2u - kPcAdjustment; - constexpr size_t kBakerLiteralOffset2 = kBakerLiteralOffset1 + kArmAlignment; + static_assert(kArmCodeAlignment == 8, "Code below assumes kArmCodeAlignment == 8"); + constexpr size_t kBakerLiteralOffset1 = kArmCodeAlignment + 2u - kPcAdjustment; + constexpr size_t kBakerLiteralOffset2 = kBakerLiteralOffset1 + kArmCodeAlignment; // Use offset = 0, base_reg = 0, the LDR is simply `kLdrWInsn | (root_reg << 12)`. const uint32_t ldr1 = kLdrWInsn | (/* root_reg */ 1 << 12); const uint32_t ldr2 = kLdrWInsn | (/* root_reg */ 2 << 12); diff --git a/dex2oat/linker/arm64/relative_patcher_arm64.cc b/dex2oat/linker/arm64/relative_patcher_arm64.cc index 4028f758b9..6b8447223b 100644 --- a/dex2oat/linker/arm64/relative_patcher_arm64.cc +++ b/dex2oat/linker/arm64/relative_patcher_arm64.cc @@ -21,7 +21,7 @@ #include "art_method.h" #include "base/bit_utils.h" #include "base/malloc_arena_pool.h" -#include "compiled_method-inl.h" +#include "driver/compiled_method-inl.h" #include "driver/compiler_driver.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "heap_poisoning.h" @@ -251,7 +251,7 @@ void Arm64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, } else { if ((insn & 0xfffffc00) == 0x91000000) { // ADD immediate, 64-bit with imm12 == 0 (unset). - if (!kEmitCompilerReadBarrier) { + if (!gUseReadBarrier) { DCHECK(patch.GetType() == LinkerPatch::Type::kIntrinsicReference || patch.GetType() == LinkerPatch::Type::kMethodRelative || patch.GetType() == LinkerPatch::Type::kTypeRelative || diff --git a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc index 8bae5d47f1..ce61f43b6e 100644 --- a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc +++ b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc @@ -112,7 +112,7 @@ class Arm64RelativePatcherTest : public RelativePatcherTest { const ArrayRef<const uint8_t>& last_method_code, const ArrayRef<const LinkerPatch>& last_method_patches, uint32_t distance_without_thunks) { - CHECK_EQ(distance_without_thunks % kArm64Alignment, 0u); + CHECK_EQ(distance_without_thunks % kArm64CodeAlignment, 0u); uint32_t method1_offset = kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader); AddCompiledMethod(MethodRef(1u), method1_code, method1_patches); @@ -120,7 +120,7 @@ class Arm64RelativePatcherTest : public RelativePatcherTest { // We want to put the last method at a very precise offset. const uint32_t last_method_offset = method1_offset + distance_without_thunks; - CHECK_ALIGNED(last_method_offset, kArm64Alignment); + CHECK_ALIGNED(last_method_offset, kArm64CodeAlignment); const uint32_t gap_end = last_method_offset - sizeof(OatQuickMethodHeader); // Fill the gap with intermediate methods in chunks of 2MiB and the first in [2MiB, 4MiB). @@ -733,24 +733,26 @@ TEST_F(Arm64RelativePatcherTestDefault, CallOtherJustTooFarAfter) { bl_offset_in_method1 + just_over_max_positive_disp); ASSERT_EQ(kExpectedLastMethodIdx, last_method_idx); uint32_t method_after_thunk_idx = last_method_idx; - if (sizeof(OatQuickMethodHeader) < kArm64Alignment) { - // The thunk needs to start on a kArm64Alignment-aligned address before the address where the - // last method would have been if there was no thunk. If the size of the OatQuickMethodHeader - // is at least kArm64Alignment, the thunk start shall fit between the previous filler method - // and that address. Otherwise, it shall be inserted before that filler method. + if (sizeof(OatQuickMethodHeader) < kArm64CodeAlignment) { + // The thunk needs to start on a kArm64CodeAlignment-aligned address before the address where + // the last method would have been if there was no thunk. If the size of the + // OatQuickMethodHeader is at least kArm64CodeAlignment, the thunk start shall fit between the + // previous filler method and that address. Otherwise, it shall be inserted before that filler + // method. method_after_thunk_idx -= 1u; } uint32_t method1_offset = GetMethodOffset(1u); uint32_t method_after_thunk_offset = GetMethodOffset(method_after_thunk_idx); - ASSERT_TRUE(IsAligned<kArm64Alignment>(method_after_thunk_offset)); + ASSERT_TRUE(IsAligned<kArm64CodeAlignment>(method_after_thunk_offset)); uint32_t method_after_thunk_header_offset = method_after_thunk_offset - sizeof(OatQuickMethodHeader); uint32_t thunk_size = MethodCallThunkSize(); - uint32_t thunk_offset = RoundDown(method_after_thunk_header_offset - thunk_size, kArm64Alignment); + uint32_t thunk_offset = RoundDown( + method_after_thunk_header_offset - thunk_size, kArm64CodeAlignment); DCHECK_EQ(thunk_offset + thunk_size + CodeAlignmentSize(thunk_offset + thunk_size), method_after_thunk_header_offset); - ASSERT_TRUE(IsAligned<kArm64Alignment>(thunk_offset)); + ASSERT_TRUE(IsAligned<kArm64CodeAlignment>(thunk_offset)); uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1); ASSERT_TRUE(IsAligned<4u>(diff)); ASSERT_LT(diff, 128 * MB); @@ -1065,7 +1067,8 @@ void Arm64RelativePatcherTest::TestBakerField(uint32_t offset, uint32_t ref_reg) Link(); // All thunks are at the end. - uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment); + uint32_t thunk_offset = + GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64CodeAlignment); method_idx = 0u; for (uint32_t base_reg : valid_regs) { for (uint32_t holder_reg : valid_regs) { @@ -1118,7 +1121,7 @@ void Arm64RelativePatcherTest::TestBakerField(uint32_t offset, uint32_t ref_reg) // Do not check the rest of the implementation. // The next thunk follows on the next aligned offset. - thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment); + thunk_offset += RoundUp(expected_thunk.size(), kArm64CodeAlignment); } } } @@ -1155,7 +1158,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddle) { // Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4 // allows the branch to reach that thunk. size_t filler1_size = - 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment); + 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment); std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u); ArrayRef<const uint8_t> filler1_code(raw_filler1_code); AddCompiledMethod(MethodRef(2u), filler1_code); @@ -1170,8 +1173,8 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddle) { // - method 4 header (let there be no padding between method 4 code and method 5 pre-header). size_t thunk_size = CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0).size(); size_t filler2_size = - 1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64Alignment) - - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64Alignment) + 1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64CodeAlignment) + - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment) - sizeof(OatQuickMethodHeader); std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 4u); ArrayRef<const uint8_t> filler2_code(raw_filler2_code); @@ -1215,14 +1218,14 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkBeforeFiller) { // Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4 // allows the branch to reach that thunk. size_t filler1_size = - 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment); + 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment); std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u); ArrayRef<const uint8_t> filler1_code(raw_filler1_code); AddCompiledMethod(MethodRef(2u), filler1_code); Link(); - const uint32_t cbnz_offset = RoundUp(raw_code1.size(), kArm64Alignment) - kLiteralOffset1; + const uint32_t cbnz_offset = RoundUp(raw_code1.size(), kArm64CodeAlignment) - kLiteralOffset1; const uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2)); const std::vector<uint8_t> expected_code1 = RawCode({cbnz, kLdrWInsn, kNopInsn}); ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1))); @@ -1244,7 +1247,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddleUnreachableFr // Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4 // allows the branch to reach that thunk. size_t filler1_size = - 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment); + 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment); std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u); ArrayRef<const uint8_t> filler1_code(raw_filler1_code); AddCompiledMethod(MethodRef(2u), filler1_code); @@ -1259,8 +1262,8 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddleUnreachableFr // - method 4 header (let there be no padding between method 4 code and method 5 pre-header). size_t thunk_size = CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0).size(); size_t filler2_size = - 1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64Alignment) - - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64Alignment) + 1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64CodeAlignment) + - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment) - sizeof(OatQuickMethodHeader); std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 4u); ArrayRef<const uint8_t> filler2_code(raw_filler2_code); @@ -1278,7 +1281,8 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddleUnreachableFr Link(); const uint32_t cbnz_max_forward = kCbnzIP1Plus0Insn | 0x007fffe0; - const uint32_t cbnz_last_offset = RoundUp(raw_code2.size(), kArm64Alignment) - kLiteralOffset2; + const uint32_t cbnz_last_offset = + RoundUp(raw_code2.size(), kArm64CodeAlignment) - kLiteralOffset2; const uint32_t cbnz_last = kCbnzIP1Plus0Insn | (cbnz_last_offset << (5 - 2)); const std::vector<uint8_t> expected_code1 = RawCode({kNopInsn, cbnz_max_forward, kLdrWInsn}); const std::vector<uint8_t> expected_code2 = RawCode({kNopInsn, cbnz_last, kLdrWInsn}); @@ -1315,7 +1319,8 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerArray) { Link(); // All thunks are at the end. - uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment); + uint32_t thunk_offset = + GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64CodeAlignment); method_idx = 0u; for (uint32_t base_reg : valid_regs) { ++method_idx; @@ -1363,7 +1368,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerArray) { // Do not check the rest of the implementation. // The next thunk follows on the next aligned offset. - thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment); + thunk_offset += RoundUp(expected_thunk.size(), kArm64CodeAlignment); } } @@ -1392,7 +1397,8 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerGcRoot) { Link(); // All thunks are at the end. - uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment); + uint32_t thunk_offset = + GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64CodeAlignment); method_idx = 0u; for (uint32_t root_reg : valid_regs) { ++method_idx; @@ -1419,7 +1425,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerGcRoot) { // Do not check the rest of the implementation. // The next thunk follows on the next aligned offset. - thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment); + thunk_offset += RoundUp(expected_thunk.size(), kArm64CodeAlignment); } } @@ -1447,7 +1453,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerAndMethodCallInteraction) { // Add a method with the right size that the method code for the next one starts 1MiB // after code for method 1. size_t filler_size = - 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment) + 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment) - sizeof(OatQuickMethodHeader); std::vector<uint8_t> filler_code = GenNops(filler_size / 4u); ++method_idx; @@ -1462,16 +1468,16 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerAndMethodCallInteraction) { } // Add 2 Baker GC root patches to the last method, one that would allow the thunk at - // 1MiB + kArm64Alignment, i.e. kArm64Alignment after the method call thunk, and the - // second that needs it kArm64Alignment after that. Given the size of the GC root thunk - // is more than the space required by the method call thunk plus kArm64Alignment, + // 1MiB + kArm64CodeAlignment, i.e. kArm64CodeAlignment after the method call thunk, and the + // second that needs it kArm64CodeAlignment after that. Given the size of the GC root thunk + // is more than the space required by the method call thunk plus kArm64CodeAlignment, // this pushes the first GC root thunk's pending MaxNextOffset() before the method call // thunk's pending MaxNextOffset() which needs to be adjusted. - ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArm64Alignment) + kArm64Alignment, + ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArm64CodeAlignment) + kArm64CodeAlignment, CompileBakerGcRootThunk(/* root_reg */ 0).size()); - static_assert(kArm64Alignment == 16, "Code below assumes kArm64Alignment == 16"); - constexpr size_t kBakerLiteralOffset1 = 4u + kArm64Alignment; - constexpr size_t kBakerLiteralOffset2 = 4u + 2 * kArm64Alignment; + static_assert(kArm64CodeAlignment == 16, "Code below assumes kArm64CodeAlignment == 16"); + constexpr size_t kBakerLiteralOffset1 = 4u + kArm64CodeAlignment; + constexpr size_t kBakerLiteralOffset2 = 4u + 2 * kArm64CodeAlignment; // Use offset = 0, base_reg = 0, the LDR is simply `kLdrWInsn | root_reg`. const uint32_t ldr1 = kLdrWInsn | /* root_reg */ 1; const uint32_t ldr2 = kLdrWInsn | /* root_reg */ 2; diff --git a/dex2oat/linker/code_info_table_deduper_test.cc b/dex2oat/linker/code_info_table_deduper_test.cc index 8913b07a51..54b7dd5940 100644 --- a/dex2oat/linker/code_info_table_deduper_test.cc +++ b/dex2oat/linker/code_info_table_deduper_test.cc @@ -35,7 +35,12 @@ TEST(StackMapTest, TestDedupeBitTables) { ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); - stream.BeginMethod(32, 0, 0, 2); + stream.BeginMethod(/* frame_size_in_bytes= */ 32, + /* core_spill_mask= */ 0, + /* fp_spill_mask= */ 0, + /* num_dex_registers= */ 2, + /* baseline= */ false, + /* debuggable= */ false); stream.BeginStackMapEntry(0, 64 * kPcAlign); stream.AddDexRegisterEntry(Kind::kInStack, 0); diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc index 424c252e6f..61e578368b 100644 --- a/dex2oat/linker/elf_writer_quick.cc +++ b/dex2oat/linker/elf_writer_quick.cc @@ -25,7 +25,6 @@ #include "base/globals.h" #include "base/leb128.h" #include "base/utils.h" -#include "compiled_method.h" #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" #include "driver/compiler_options.h" diff --git a/dex2oat/linker/elf_writer_test.cc b/dex2oat/linker/elf_writer_test.cc index c1ff8f2988..6fa5f6602d 100644 --- a/dex2oat/linker/elf_writer_test.cc +++ b/dex2oat/linker/elf_writer_test.cc @@ -16,8 +16,6 @@ #include <sys/mman.h> // For the PROT_NONE constant. -#include "elf_file.h" - #include "base/file_utils.h" #include "base/mem_map.h" #include "base/unix_file/fd_file.h" diff --git a/dex2oat/linker/image_test.cc b/dex2oat/linker/image_test.cc index 33d122bdbe..82c87f504d 100644 --- a/dex2oat/linker/image_test.cc +++ b/dex2oat/linker/image_test.cc @@ -176,5 +176,37 @@ TEST_F(ImageTest, TestSoftVerificationFailureDuringClassInitialization) { /*image_classes_failing_aot_clinit=*/ {"LClassToInitialize;"}); } +TEST_F(ImageTest, TestImageClassWithArrayClassWithUnresolvedComponent) { + CompilationHelper helper; + Compile(ImageHeader::kStorageModeUncompressed, + /*max_image_block_size=*/std::numeric_limits<uint32_t>::max(), + helper, + "ArrayClassWithUnresolvedComponent", + /*image_classes=*/ {"LClassWithStatic;", + "LClassWithStaticConst;", + "[LClassWithMissingInterface;", + "[[LClassWithMissingInterface;", + "[LClassWithMissingSuper", + "[[LClassWithMissingSuper"}, + /*image_classes_failing_aot_clinit=*/ { + "LClassWithStatic;", + "LClassWithStaticConst;"}, + /*image_classes_failing_resolution=*/ { + "[LClassWithMissingInterface;", + "[[LClassWithMissingInterface;", + "[LClassWithMissingSuper", + "[[LClassWithMissingSuper"}); +} + +TEST_F(ImageTest, TestSuperWithAccessChecks) { + CompilationHelper helper; + Compile(ImageHeader::kStorageModeUncompressed, + /*max_image_block_size=*/std::numeric_limits<uint32_t>::max(), + helper, + "SuperWithAccessChecks", + /*image_classes=*/ {"LSubClass;", "LImplementsClass;"}, + /*image_classes_failing_aot_clinit=*/ {"LSubClass;", "LImplementsClass;"}); +} + } // namespace linker } // namespace art diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index 5c2d84cc5e..8dea9a6ff8 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -50,6 +50,7 @@ #include "mirror/object-inl.h" #include "oat.h" #include "oat_writer.h" +#include "read_barrier_config.h" #include "scoped_thread_state_change-inl.h" #include "signal_catcher.h" #include "stream/buffered_output_stream.h" @@ -63,6 +64,7 @@ static const uintptr_t kRequestedImageBase = ART_BASE_ADDRESS; struct CompilationHelper { std::vector<std::string> dex_file_locations; std::vector<ScratchFile> image_locations; + std::string extra_dex; std::vector<std::unique_ptr<const DexFile>> extra_dex_files; std::vector<ScratchFile> image_files; std::vector<ScratchFile> oat_files; @@ -78,7 +80,7 @@ class ImageTest : public CommonCompilerDriverTest { protected: void SetUp() override { ReserveImageSpace(); - CommonCompilerTest::SetUp(); + CommonCompilerDriverTest::SetUp(); } void Compile(ImageHeader::StorageMode storage_mode, @@ -86,10 +88,11 @@ class ImageTest : public CommonCompilerDriverTest { /*out*/ CompilationHelper& out_helper, const std::string& extra_dex = "", const std::initializer_list<std::string>& image_classes = {}, - const std::initializer_list<std::string>& image_classes_failing_aot_clinit = {}); + const std::initializer_list<std::string>& image_classes_failing_aot_clinit = {}, + const std::initializer_list<std::string>& image_classes_failing_resolution = {}); void SetUpRuntimeOptions(RuntimeOptions* options) override { - CommonCompilerTest::SetUpRuntimeOptions(options); + CommonCompilerDriverTest::SetUpRuntimeOptions(options); QuickCompilerCallbacks* new_callbacks = new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileBootImage); new_callbacks->SetVerificationResults(verification_results_.get()); @@ -149,18 +152,11 @@ inline std::vector<size_t> CompilationHelper::GetImageObjectSectionSizes() { inline void ImageTest::DoCompile(ImageHeader::StorageMode storage_mode, /*out*/ CompilationHelper& out_helper) { CompilerDriver* driver = compiler_driver_.get(); + Runtime::Current()->AppendToBootClassPath( + out_helper.extra_dex, out_helper.extra_dex, out_helper.extra_dex_files); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); std::vector<const DexFile*> class_path = class_linker->GetBootClassPath(); - for (const std::unique_ptr<const DexFile>& dex_file : out_helper.extra_dex_files) { - { - ScopedObjectAccess soa(Thread::Current()); - // Inject in boot class path so that the compiler driver can see it. - class_linker->AppendToBootClassPath(soa.Self(), dex_file.get()); - } - class_path.push_back(dex_file.get()); - } - // Enable write for dex2dex. for (const DexFile* dex_file : class_path) { out_helper.dex_file_locations.push_back(dex_file->GetLocation()); @@ -228,6 +224,8 @@ inline void ImageTest::DoCompile(ImageHeader::StorageMode storage_mode, key_value_store.Put(OatHeader::kBootClassPathKey, android::base::Join(out_helper.dex_file_locations, ':')); key_value_store.Put(OatHeader::kApexVersionsKey, Runtime::Current()->GetApexVersions()); + key_value_store.Put(OatHeader::kConcurrentCopying, + gUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue); std::vector<std::unique_ptr<ElfWriter>> elf_writers; std::vector<std::unique_ptr<OatWriter>> oat_writers; @@ -235,6 +233,7 @@ inline void ImageTest::DoCompile(ImageHeader::StorageMode storage_mode, elf_writers.emplace_back(CreateElfWriterQuick(*compiler_options_, oat_file.GetFile())); elf_writers.back()->Start(); oat_writers.emplace_back(new OatWriter(*compiler_options_, + verification_results_.get(), &timings, /*profile_compilation_info*/nullptr, CompactDexLevel::kCompactDexLevelNone)); @@ -352,7 +351,8 @@ inline void ImageTest::Compile( CompilationHelper& helper, const std::string& extra_dex, const std::initializer_list<std::string>& image_classes, - const std::initializer_list<std::string>& image_classes_failing_aot_clinit) { + const std::initializer_list<std::string>& image_classes_failing_aot_clinit, + const std::initializer_list<std::string>& image_classes_failing_resolution) { for (const std::string& image_class : image_classes_failing_aot_clinit) { ASSERT_TRUE(ContainsElement(image_classes, image_class)); } @@ -366,6 +366,7 @@ inline void ImageTest::Compile( compiler_options_->SetMaxImageBlockSize(max_image_block_size); image_classes_.clear(); if (!extra_dex.empty()) { + helper.extra_dex = extra_dex; helper.extra_dex_files = OpenTestDexFiles(extra_dex.c_str()); } DoCompile(storage_mode, helper); @@ -375,12 +376,14 @@ inline void ImageTest::Compile( ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); for (const std::string& image_class : image_classes) { ObjPtr<mirror::Class> klass = - class_linker->FindSystemClass(Thread::Current(), image_class.c_str()); - EXPECT_TRUE(klass != nullptr); - EXPECT_TRUE(klass->IsResolved()); - if (ContainsElement(image_classes_failing_aot_clinit, image_class)) { + class_linker->LookupClass(Thread::Current(), image_class.c_str(), nullptr); + if (ContainsElement(image_classes_failing_resolution, image_class)) { + EXPECT_TRUE(klass == nullptr || klass->IsErroneousUnresolved()); + } else if (ContainsElement(image_classes_failing_aot_clinit, image_class)) { + ASSERT_TRUE(klass != nullptr) << image_class; EXPECT_FALSE(klass->IsInitialized()); } else { + ASSERT_TRUE(klass != nullptr) << image_class; EXPECT_TRUE(klass->IsInitialized()); } } diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 56fbe9013b..63ede1b42f 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -21,10 +21,12 @@ #include <sys/stat.h> #include <zlib.h> +#include <charconv> #include <memory> #include <numeric> #include <vector> +#include "android-base/strings.h" #include "art_field-inl.h" #include "art_method-inl.h" #include "base/callee_save_type.h" @@ -35,7 +37,6 @@ #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" #include "class_root-inl.h" -#include "compiled_method.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" #include "driver/compiler_options.h" @@ -83,7 +84,7 @@ #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "subtype_check.h" -#include "well_known_classes.h" +#include "well_known_classes-inl.h" using ::art::mirror::Class; using ::art::mirror::DexCache; @@ -101,7 +102,7 @@ constexpr double kImageClassTableMinLoadFactor = 0.5; // to make them full. We never insert additional elements to them, so we do not want to waste // extra memory. And unlike runtime class tables, we do not want this to depend on runtime // properties (see `Runtime::GetHashTableMaxLoadFactor()` checking for low memory mode). -constexpr double kImageClassTableMaxLoadFactor = 0.7; +constexpr double kImageClassTableMaxLoadFactor = 0.6; // The actual value of `kImageInternTableMinLoadFactor` is irrelevant because image intern tables // are never resized, but we still need to pass a reasonable value to the constructor. @@ -110,61 +111,7 @@ constexpr double kImageInternTableMinLoadFactor = 0.5; // to make them full. We never insert additional elements to them, so we do not want to waste // extra memory. And unlike runtime intern tables, we do not want this to depend on runtime // properties (see `Runtime::GetHashTableMaxLoadFactor()` checking for low memory mode). -constexpr double kImageInternTableMaxLoadFactor = 0.7; - -static ArrayRef<const uint8_t> MaybeCompressData(ArrayRef<const uint8_t> source, - ImageHeader::StorageMode image_storage_mode, - /*out*/ dchecked_vector<uint8_t>* storage) { - const uint64_t compress_start_time = NanoTime(); - - switch (image_storage_mode) { - case ImageHeader::kStorageModeLZ4: { - storage->resize(LZ4_compressBound(source.size())); - size_t data_size = LZ4_compress_default( - reinterpret_cast<char*>(const_cast<uint8_t*>(source.data())), - reinterpret_cast<char*>(storage->data()), - source.size(), - storage->size()); - storage->resize(data_size); - break; - } - case ImageHeader::kStorageModeLZ4HC: { - // Bound is same as non HC. - storage->resize(LZ4_compressBound(source.size())); - size_t data_size = LZ4_compress_HC( - reinterpret_cast<const char*>(const_cast<uint8_t*>(source.data())), - reinterpret_cast<char*>(storage->data()), - source.size(), - storage->size(), - LZ4HC_CLEVEL_MAX); - storage->resize(data_size); - break; - } - case ImageHeader::kStorageModeUncompressed: { - return source; - } - default: { - LOG(FATAL) << "Unsupported"; - UNREACHABLE(); - } - } - - DCHECK(image_storage_mode == ImageHeader::kStorageModeLZ4 || - image_storage_mode == ImageHeader::kStorageModeLZ4HC); - VLOG(compiler) << "Compressed from " << source.size() << " to " << storage->size() << " in " - << PrettyDuration(NanoTime() - compress_start_time); - if (kIsDebugBuild) { - dchecked_vector<uint8_t> decompressed(source.size()); - const size_t decompressed_size = LZ4_decompress_safe( - reinterpret_cast<char*>(storage->data()), - reinterpret_cast<char*>(decompressed.data()), - storage->size(), - decompressed.size()); - CHECK_EQ(decompressed_size, decompressed.size()); - CHECK_EQ(memcmp(source.data(), decompressed.data(), source.size()), 0) << image_storage_mode; - } - return ArrayRef<const uint8_t>(*storage); -} +constexpr double kImageInternTableMaxLoadFactor = 0.6; // Separate objects into multiple bins to optimize dirty memory use. static constexpr bool kBinObjects = true; @@ -265,8 +212,8 @@ static void ClearDexFileCookies() REQUIRES_SHARED(Locks::mutator_lock_) { auto visitor = [](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(obj != nullptr); Class* klass = obj->GetClass(); - if (klass == WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexFile)) { - ArtField* field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); + if (klass == WellKnownClasses::dalvik_system_DexFile) { + ArtField* field = WellKnownClasses::dalvik_system_DexFile_cookie; // Null out the cookie to enable determinism. b/34090128 field->SetObject</*kTransactionActive*/false>(obj, nullptr); } @@ -327,6 +274,13 @@ bool ImageWriter::PrepareImageAddressSpace(TimingLogger* timings) { TimingLogger::ScopedTiming t("CalculateNewObjectOffsets", timings); ScopedObjectAccess soa(self); CalculateNewObjectOffsets(); + + // If dirty_image_objects_ is present - try optimizing object layout. + // It can only be done after the first CalculateNewObjectOffsets, + // because calculated offsets are used to match dirty objects between imgdiag and dex2oat. + if (compiler_options_.IsBootImage() && dirty_image_objects_ != nullptr) { + TryRecalculateOffsetsWithDirtyObjects(); + } } // This needs to happen after CalculateNewObjectOffsets since it relies on intern_table_bytes_ and @@ -375,58 +329,6 @@ bool ImageWriter::IsInternedAppImageStringReference(ObjPtr<mirror::Object> refer IsStronglyInternedString(referred_obj->AsString()); } -// Helper class that erases the image file if it isn't properly flushed and closed. -class ImageWriter::ImageFileGuard { - public: - ImageFileGuard() noexcept = default; - ImageFileGuard(ImageFileGuard&& other) noexcept = default; - ImageFileGuard& operator=(ImageFileGuard&& other) noexcept = default; - - ~ImageFileGuard() { - if (image_file_ != nullptr) { - // Failure, erase the image file. - image_file_->Erase(); - } - } - - void reset(File* image_file) { - image_file_.reset(image_file); - } - - bool operator==(std::nullptr_t) { - return image_file_ == nullptr; - } - - bool operator!=(std::nullptr_t) { - return image_file_ != nullptr; - } - - File* operator->() const { - return image_file_.get(); - } - - bool WriteHeaderAndClose(const std::string& image_filename, const ImageHeader* image_header) { - // The header is uncompressed since it contains whether the image is compressed or not. - if (!image_file_->PwriteFully(image_header, sizeof(ImageHeader), 0)) { - PLOG(ERROR) << "Failed to write image file header " << image_filename; - return false; - } - - // FlushCloseOrErase() takes care of erasing, so the destructor does not need - // to do that whether the FlushCloseOrErase() succeeds or fails. - std::unique_ptr<File> image_file = std::move(image_file_); - if (image_file->FlushCloseOrErase() != 0) { - PLOG(ERROR) << "Failed to flush and close image file " << image_filename; - return false; - } - - return true; - } - - private: - std::unique_ptr<File> image_file_; -}; - bool ImageWriter::Write(int image_fd, const std::vector<std::string>& image_filenames, size_t component_count) { @@ -497,139 +399,37 @@ bool ImageWriter::Write(int image_fd, // Image data size excludes the bitmap and the header. ImageHeader* const image_header = reinterpret_cast<ImageHeader*>(image_info.image_.Begin()); - - // Block sources (from the image). - const bool is_compressed = image_storage_mode_ != ImageHeader::kStorageModeUncompressed; - dchecked_vector<std::pair<uint32_t, uint32_t>> block_sources; - dchecked_vector<ImageHeader::Block> blocks; - - // Add a set of solid blocks such that no block is larger than the maximum size. A solid block - // is a block that must be decompressed all at once. - auto add_blocks = [&](uint32_t offset, uint32_t size) { - while (size != 0u) { - const uint32_t cur_size = std::min(size, compiler_options_.MaxImageBlockSize()); - block_sources.emplace_back(offset, cur_size); - offset += cur_size; - size -= cur_size; - } - }; - - add_blocks(sizeof(ImageHeader), image_header->GetImageSize() - sizeof(ImageHeader)); - - // Checksum of compressed image data and header. - uint32_t image_checksum = adler32(0L, Z_NULL, 0); - image_checksum = adler32(image_checksum, - reinterpret_cast<const uint8_t*>(image_header), - sizeof(ImageHeader)); - // Copy and compress blocks. - size_t out_offset = sizeof(ImageHeader); - for (const std::pair<uint32_t, uint32_t> block : block_sources) { - ArrayRef<const uint8_t> raw_image_data(image_info.image_.Begin() + block.first, - block.second); - dchecked_vector<uint8_t> compressed_data; - ArrayRef<const uint8_t> image_data = - MaybeCompressData(raw_image_data, image_storage_mode_, &compressed_data); - - if (!is_compressed) { - // For uncompressed, preserve alignment since the image will be directly mapped. - out_offset = block.first; - } - - // Fill in the compressed location of the block. - blocks.emplace_back(ImageHeader::Block( - image_storage_mode_, - /*data_offset=*/ out_offset, - /*data_size=*/ image_data.size(), - /*image_offset=*/ block.first, - /*image_size=*/ block.second)); - - // Write out the image + fields + methods. - if (!image_file->PwriteFully(image_data.data(), image_data.size(), out_offset)) { - PLOG(ERROR) << "Failed to write image file data " << image_filename; - image_file->Erase(); - return false; - } - out_offset += image_data.size(); - image_checksum = adler32(image_checksum, image_data.data(), image_data.size()); - } - - // Write the block metadata directly after the image sections. - // Note: This is not part of the mapped image and is not preserved after decompressing, it's - // only used for image loading. For this reason, only write it out for compressed images. - if (is_compressed) { - // Align up since the compressed data is not necessarily aligned. - out_offset = RoundUp(out_offset, alignof(ImageHeader::Block)); - CHECK(!blocks.empty()); - const size_t blocks_bytes = blocks.size() * sizeof(blocks[0]); - if (!image_file->PwriteFully(&blocks[0], blocks_bytes, out_offset)) { - PLOG(ERROR) << "Failed to write image blocks " << image_filename; - image_file->Erase(); - return false; - } - image_header->blocks_offset_ = out_offset; - image_header->blocks_count_ = blocks.size(); - out_offset += blocks_bytes; - } - - // Data size includes everything except the bitmap. - image_header->data_size_ = out_offset - sizeof(ImageHeader); - - // Update and write the bitmap section. Note that the bitmap section is relative to the - // possibly compressed image. - ImageSection& bitmap_section = image_header->GetImageSection(ImageHeader::kSectionImageBitmap); - // Align up since data size may be unaligned if the image is compressed. - out_offset = RoundUp(out_offset, kPageSize); - bitmap_section = ImageSection(out_offset, bitmap_section.Size()); - - if (!image_file->PwriteFully(image_info.image_bitmap_.Begin(), - bitmap_section.Size(), - bitmap_section.Offset())) { - PLOG(ERROR) << "Failed to write image file bitmap " << image_filename; - return false; - } - - int err = image_file->Flush(); - if (err < 0) { - PLOG(ERROR) << "Failed to flush image file " << image_filename << " with result " << err; + std::string error_msg; + if (!image_header->WriteData(image_file, + image_info.image_.Begin(), + reinterpret_cast<const uint8_t*>(image_info.image_bitmap_.Begin()), + image_storage_mode_, + compiler_options_.MaxImageBlockSize(), + /* update_checksum= */ true, + &error_msg)) { + LOG(ERROR) << error_msg; return false; } - // Calculate the image checksum of the remaining data. - image_checksum = adler32(image_checksum, - reinterpret_cast<const uint8_t*>(image_info.image_bitmap_.Begin()), - bitmap_section.Size()); - image_header->SetImageChecksum(image_checksum); - - if (VLOG_IS_ON(compiler)) { - const size_t separately_written_section_size = bitmap_section.Size(); - const size_t total_uncompressed_size = image_info.image_size_ + - separately_written_section_size; - const size_t total_compressed_size = out_offset + separately_written_section_size; - - VLOG(compiler) << "Dex2Oat:uncompressedImageSize = " << total_uncompressed_size; - if (total_uncompressed_size != total_compressed_size) { - VLOG(compiler) << "Dex2Oat:compressedImageSize = " << total_compressed_size; - } - } - - CHECK_EQ(bitmap_section.End(), static_cast<size_t>(image_file->GetLength())) - << "Bitmap should be at the end of the file"; - // Write header last in case the compiler gets killed in the middle of image writing. // We do not want to have a corrupted image with a valid header. // Delay the writing of the primary image header until after writing secondary images. if (i == 0u) { primary_image_file = std::move(image_file); } else { - if (!image_file.WriteHeaderAndClose(image_filename, image_header)) { + if (!image_file.WriteHeaderAndClose(image_filename, image_header, &error_msg)) { + LOG(ERROR) << error_msg; return false; } // Update the primary image checksum with the secondary image checksum. - primary_header->SetImageChecksum(primary_header->GetImageChecksum() ^ image_checksum); + primary_header->SetImageChecksum( + primary_header->GetImageChecksum() ^ image_header->GetImageChecksum()); } } DCHECK(primary_image_file != nullptr); - if (!primary_image_file.WriteHeaderAndClose(image_filenames[0], primary_header)) { + std::string error_msg; + if (!primary_image_file.WriteHeaderAndClose(image_filenames[0], primary_header, &error_msg)) { + LOG(ERROR) << error_msg; return false; } @@ -722,7 +522,13 @@ ImageWriter::Bin ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t // so packing them together will not result in a noticeably tighter dirty-to-clean ratio. // ObjPtr<mirror::Class> klass = object->GetClass<kVerifyNone, kWithoutReadBarrier>(); - if (klass->IsClassClass()) { + if (klass->IsStringClass<kVerifyNone>()) { + // Assign strings to their bin before checking dirty objects, because + // string intern processing expects strings to be in Bin::kString. + bin = Bin::kString; // Strings are almost always immutable (except for object header). + } else if (dirty_objects_.find(object) != dirty_objects_.end()) { + bin = Bin::kKnownDirty; + } else if (klass->IsClassClass()) { bin = Bin::kClassVerified; ObjPtr<mirror::Class> as_klass = object->AsClass<kVerifyNone>(); @@ -759,8 +565,6 @@ ImageWriter::Bin ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t } } } - } else if (klass->IsStringClass<kVerifyNone>()) { - bin = Bin::kString; // Strings are almost always immutable (except for object header). } else if (!klass->HasSuperClass()) { // Only `j.l.Object` and primitive classes lack the superclass and // there are no instances of primitive classes. @@ -1121,7 +925,8 @@ class ImageWriter::PruneClassesVisitor : public ClassVisitor { last_class_set.erase(it); DCHECK(std::none_of(class_table->classes_.begin(), class_table->classes_.end(), - [klass, hash](ClassTable::ClassSet& class_set) { + [klass, hash](ClassTable::ClassSet& class_set) + REQUIRES_SHARED(Locks::mutator_lock_) { ClassTable::TableSlot slot(klass, hash); return class_set.FindWithHash(slot, hash) != class_set.end(); })); @@ -1538,6 +1343,9 @@ class ImageWriter::LayoutHelper { void ProcessDexFileObjects(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); void ProcessRoots(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); void FinalizeInternTables() REQUIRES_SHARED(Locks::mutator_lock_); + // Recreate dirty object offsets (kKnownDirty bin) with objects sorted by sort_key. + void SortDirtyObjects(const HashMap<mirror::Object*, uint32_t>& dirty_objects, size_t oat_index) + REQUIRES_SHARED(Locks::mutator_lock_); void VerifyImageBinSlotsAssigned() REQUIRES_SHARED(Locks::mutator_lock_); @@ -2166,6 +1974,49 @@ void ImageWriter::LayoutHelper::ProcessWorkQueue() { } } +void ImageWriter::LayoutHelper::SortDirtyObjects( + const HashMap<mirror::Object*, uint32_t>& dirty_objects, size_t oat_index) { + constexpr Bin bin = Bin::kKnownDirty; + ImageInfo& image_info = image_writer_->GetImageInfo(oat_index); + + dchecked_vector<mirror::Object*>& known_dirty = bin_objects_[oat_index][enum_cast<size_t>(bin)]; + if (known_dirty.empty()) { + return; + } + + // Collect objects and their combined sort_keys. + // Combined key contains sort_key and original offset to ensure deterministic sorting. + using CombinedKey = std::pair<uint32_t, uint32_t>; + using ObjSortPair = std::pair<mirror::Object*, CombinedKey>; + dchecked_vector<ObjSortPair> objects; + objects.reserve(known_dirty.size()); + for (mirror::Object* obj : known_dirty) { + const BinSlot bin_slot = image_writer_->GetImageBinSlot(obj, oat_index); + const uint32_t original_offset = bin_slot.GetOffset(); + const auto it = dirty_objects.find(obj); + const uint32_t sort_key = (it != dirty_objects.end()) ? it->second : 0; + objects.emplace_back(obj, std::make_pair(sort_key, original_offset)); + } + // Sort by combined sort_key. + std::sort(std::begin(objects), std::end(objects), [&](ObjSortPair& lhs, ObjSortPair& rhs) { + return lhs.second < rhs.second; + }); + + // Fill known_dirty objects in sorted order, update bin offsets. + known_dirty.clear(); + size_t offset = 0; + for (const ObjSortPair& entry : objects) { + mirror::Object* obj = entry.first; + + known_dirty.push_back(obj); + image_writer_->UpdateImageBinSlotOffset(obj, oat_index, offset); + + const size_t aligned_object_size = RoundUp(obj->SizeOf<kVerifyNone>(), kObjectAlignment); + offset += aligned_object_size; + } + DCHECK_EQ(offset, image_info.GetBinSlotSize(bin)); +} + void ImageWriter::LayoutHelper::VerifyImageBinSlotsAssigned() { dchecked_vector<mirror::Object*> carveout; JavaVMExt* vm = nullptr; @@ -2217,12 +2068,11 @@ void ImageWriter::LayoutHelper::VerifyImageBinSlotsAssigned() { CHECK(ref != nullptr); CHECK(image_writer_->IsImageBinSlotAssigned(ref.Ptr())); ObjPtr<mirror::Class> ref_klass = ref->GetClass<kVerifyNone, kWithoutReadBarrier>(); - CHECK(ref_klass == - DecodeGlobalWithoutRB<mirror::Class>(vm, WellKnownClasses::dalvik_system_DexFile)); + CHECK(ref_klass == WellKnownClasses::dalvik_system_DexFile.Get<kWithoutReadBarrier>()); // Note: The app class loader is used only for checking against the runtime // class loader, the dex file cookie is cleared and therefore we do not need // to run the finalizer even if we implement app image objects collection. - ArtField* field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); + ArtField* field = WellKnownClasses::dalvik_system_DexFile_cookie; CHECK(field->GetObject<kWithoutReadBarrier>(ref) == nullptr); return; } @@ -2489,6 +2339,13 @@ void ImageWriter::CalculateNewObjectOffsets() { layout_helper.ProcessRoots(self); layout_helper.FinalizeInternTables(); + // Sort objects in dirty bin. + if (!dirty_objects_.empty()) { + for (size_t oat_index = 0; oat_index < image_infos_.size(); ++oat_index) { + layout_helper.SortDirtyObjects(dirty_objects_, oat_index); + } + } + // Verify that all objects have assigned image bin slots. layout_helper.VerifyImageBinSlotsAssigned(); @@ -2527,6 +2384,149 @@ void ImageWriter::CalculateNewObjectOffsets() { } } +std::optional<HashMap<mirror::Object*, uint32_t>> ImageWriter::MatchDirtyObjectOffsets( + const HashMap<uint32_t, DirtyEntry>& dirty_entries) REQUIRES_SHARED(Locks::mutator_lock_) { + HashMap<mirror::Object*, uint32_t> dirty_objects; + bool mismatch_found = false; + + auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(obj != nullptr); + if (mismatch_found) { + return; + } + if (!IsImageBinSlotAssigned(obj)) { + return; + } + + uint8_t* image_address = reinterpret_cast<uint8_t*>(GetImageAddress(obj)); + uint32_t offset = static_cast<uint32_t>(image_address - global_image_begin_); + + auto entry_it = dirty_entries.find(offset); + if (entry_it == dirty_entries.end()) { + return; + } + + const DirtyEntry& entry = entry_it->second; + + const bool is_class = obj->IsClass(); + const uint32_t descriptor_hash = + is_class ? obj->AsClass()->DescriptorHash() : obj->GetClass()->DescriptorHash(); + + if (is_class != entry.is_class || descriptor_hash != entry.descriptor_hash) { + LOG(WARNING) << "Dirty image objects offset mismatch (outdated file?)"; + mismatch_found = true; + return; + } + + dirty_objects.insert(std::make_pair(obj, entry.sort_key)); + }; + Runtime::Current()->GetHeap()->VisitObjects(visitor); + + // A single mismatch indicates that dirty-image-objects layout differs from + // current ImageWriter layout. In this case any "valid" matches are likely to be accidental, + // so there's no point in optimizing the layout with such data. + if (mismatch_found) { + return {}; + } + if (dirty_objects.size() != dirty_entries.size()) { + LOG(WARNING) << "Dirty image objects missing offsets (outdated file?)"; + return {}; + } + return dirty_objects; +} + +void ImageWriter::ResetObjectOffsets() { + const size_t image_infos_size = image_infos_.size(); + image_infos_.clear(); + image_infos_.resize(image_infos_size); + + native_object_relocations_.clear(); + + // CalculateNewObjectOffsets stores image offsets of the objects in lock words, + // while original lock words are preserved in saved_hashcode_map. + // Restore original lock words. + auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(obj != nullptr); + const auto it = saved_hashcode_map_.find(obj); + obj->SetLockWord(it != saved_hashcode_map_.end() ? LockWord::FromHashCode(it->second, 0u) : + LockWord::Default(), + false); + }; + Runtime::Current()->GetHeap()->VisitObjects(visitor); + + saved_hashcode_map_.clear(); +} + +void ImageWriter::TryRecalculateOffsetsWithDirtyObjects() { + const std::optional<HashMap<uint32_t, ImageWriter::DirtyEntry>> dirty_entries = + ParseDirtyObjectOffsets(*dirty_image_objects_); + if (!dirty_entries || dirty_entries->empty()) { + return; + } + + std::optional<HashMap<mirror::Object*, uint32_t>> dirty_objects = + MatchDirtyObjectOffsets(*dirty_entries); + if (!dirty_objects || dirty_objects->empty()) { + return; + } + // Calculate offsets again, now with dirty object offsets. + LOG(INFO) << "Recalculating object offsets using dirty-image-objects"; + dirty_objects_ = std::move(*dirty_objects); + ResetObjectOffsets(); + CalculateNewObjectOffsets(); +} + +std::optional<HashMap<uint32_t, ImageWriter::DirtyEntry>> ImageWriter::ParseDirtyObjectOffsets( + const HashSet<std::string>& dirty_image_objects) REQUIRES_SHARED(Locks::mutator_lock_) { + HashMap<uint32_t, DirtyEntry> dirty_entries; + + // Go through each dirty-image-object line, parse only lines of the format: + // "dirty_obj: <offset> <type> <descriptor_hash> <sort_key>" + // <offset> -- decimal uint32. + // <type> -- "class" or "instance" (defines if descriptor is referring to a class or an instance). + // <descriptor_hash> -- decimal uint32 (from DescriptorHash() method). + // <sort_key> -- decimal uint32 (defines order of the object inside the dirty bin). + const std::string prefix = "dirty_obj:"; + for (const std::string& entry_str : dirty_image_objects) { + // Skip the lines of old dirty-image-object format. + if (std::strncmp(entry_str.data(), prefix.data(), prefix.size()) != 0) { + continue; + } + + const std::vector<std::string> tokens = android::base::Split(entry_str, " "); + if (tokens.size() != 5) { + LOG(WARNING) << "Invalid dirty image objects format: \"" << entry_str << "\""; + return {}; + } + + uint32_t offset = 0; + std::from_chars_result res = + std::from_chars(tokens[1].data(), tokens[1].data() + tokens[1].size(), offset); + if (res.ec != std::errc()) { + LOG(WARNING) << "Couldn't parse dirty object offset: \"" << entry_str << "\""; + return {}; + } + + DirtyEntry entry; + entry.is_class = (tokens[2] == "class"); + res = std::from_chars( + tokens[3].data(), tokens[3].data() + tokens[3].size(), entry.descriptor_hash); + if (res.ec != std::errc()) { + LOG(WARNING) << "Couldn't parse dirty object descriptor hash: \"" << entry_str << "\""; + return {}; + } + res = std::from_chars(tokens[4].data(), tokens[4].data() + tokens[4].size(), entry.sort_key); + if (res.ec != std::errc()) { + LOG(WARNING) << "Couldn't parse dirty object marker: \"" << entry_str << "\""; + return {}; + } + + dirty_entries.insert(std::make_pair(offset, entry)); + } + + return dirty_entries; +} + std::pair<size_t, dchecked_vector<ImageSection>> ImageWriter::ImageInfo::CreateImageSections() const { dchecked_vector<ImageSection> sections(ImageHeader::kSectionCount); @@ -2610,6 +2610,15 @@ ImageWriter::ImageInfo::CreateImageSections() const { ImageSection(cur_pos, sizeof(string_reference_offsets_[0]) * num_string_references_); /* + * DexCache arrays section + */ + + // Round up to the alignment dex caches arrays expects. + cur_pos = RoundUp(sections[ImageHeader::kSectionStringReferenceOffsets].End(), sizeof(uint32_t)); + // We don't generate dex cache arrays in an image generated by dex2oat. + sections[ImageHeader::kSectionDexCacheArrays] = ImageSection(cur_pos, 0u); + + /* * Metadata section. */ @@ -3288,25 +3297,7 @@ const uint8_t* ImageWriter::GetOatAddress(StubType type) const { const OatFile* oat_file = image_spaces[0]->GetOatFile(); CHECK(oat_file != nullptr); const OatHeader& header = oat_file->GetOatHeader(); - switch (type) { - // TODO: We could maybe clean this up if we stored them in an array in the oat header. - case StubType::kQuickGenericJNITrampoline: - return static_cast<const uint8_t*>(header.GetQuickGenericJniTrampoline()); - case StubType::kJNIDlsymLookupTrampoline: - return static_cast<const uint8_t*>(header.GetJniDlsymLookupTrampoline()); - case StubType::kJNIDlsymLookupCriticalTrampoline: - return static_cast<const uint8_t*>(header.GetJniDlsymLookupCriticalTrampoline()); - case StubType::kQuickIMTConflictTrampoline: - return static_cast<const uint8_t*>(header.GetQuickImtConflictTrampoline()); - case StubType::kQuickResolutionTrampoline: - return static_cast<const uint8_t*>(header.GetQuickResolutionTrampoline()); - case StubType::kQuickToInterpreterBridge: - return static_cast<const uint8_t*>(header.GetQuickToInterpreterBridge()); - case StubType::kNterpTrampoline: - return static_cast<const uint8_t*>(header.GetNterpTrampoline()); - default: - UNREACHABLE(); - } + return header.GetOatAddress(type); } const ImageInfo& primary_image_info = GetImageInfo(0); return GetOatAddressForOffset(primary_image_info.GetStubOffset(type), primary_image_info); @@ -3336,8 +3327,7 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, const ImageInfo& ima quick_code = GetOatAddressForOffset(quick_oat_code_offset, image_info); } - bool needs_clinit_check = NeedsClinitCheckBeforeCall(method) && - !method->GetDeclaringClass<kWithoutReadBarrier>()->IsVisiblyInitialized(); + bool still_needs_clinit_check = method->StillNeedsClinitCheck<kWithoutReadBarrier>(); if (quick_code == nullptr) { // If we don't have code, use generic jni / interpreter. @@ -3347,7 +3337,7 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, const ImageInfo& ima } else if (CanMethodUseNterp(method, compiler_options_.GetInstructionSet())) { // The nterp trampoline doesn't do initialization checks, so install the // resolution stub if needed. - if (needs_clinit_check) { + if (still_needs_clinit_check) { quick_code = GetOatAddress(StubType::kQuickResolutionTrampoline); } else { quick_code = GetOatAddress(StubType::kNterpTrampoline); @@ -3356,7 +3346,7 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, const ImageInfo& ima // The interpreter brige performs class initialization check if needed. quick_code = GetOatAddress(StubType::kQuickToInterpreterBridge); } - } else if (needs_clinit_check) { + } else if (still_needs_clinit_check && !compiler_options_.ShouldCompileWithClinitCheck(method)) { // If we do have code but the method needs a class initialization check before calling // that code, install the resolution stub that will perform the check. quick_code = GetOatAddress(StubType::kQuickResolutionTrampoline); diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h index e5eeacc02d..3f6aa284d2 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -45,6 +45,7 @@ #include "intern_table.h" #include "lock_word.h" #include "mirror/dex_cache.h" +#include "oat.h" #include "oat_file.h" #include "obj_ptr.h" @@ -229,18 +230,6 @@ class ImageWriter final { }; friend std::ostream& operator<<(std::ostream& stream, NativeObjectRelocationType type); - enum class StubType { - kJNIDlsymLookupTrampoline, - kJNIDlsymLookupCriticalTrampoline, - kQuickGenericJNITrampoline, - kQuickIMTConflictTrampoline, - kQuickResolutionTrampoline, - kQuickToInterpreterBridge, - kNterpTrampoline, - kLast = kNterpTrampoline, - }; - friend std::ostream& operator<<(std::ostream& stream, StubType stub_type); - static constexpr size_t kBinBits = MinimumBitsToStore<uint32_t>(static_cast<size_t>(Bin::kMirrorCount) - 1); // uint32 = typeof(lockword_) @@ -462,6 +451,25 @@ class ImageWriter final { REQUIRES_SHARED(Locks::mutator_lock_); void CalculateObjectBinSlots(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_); + // Undo the changes of CalculateNewObjectOffsets. + void ResetObjectOffsets() REQUIRES_SHARED(Locks::mutator_lock_); + // Reset and calculate new offsets with dirty objects optimization. + // Does nothing if dirty object offsets don't match with current offsets. + void TryRecalculateOffsetsWithDirtyObjects() REQUIRES_SHARED(Locks::mutator_lock_); + + // Dirty object data from dirty-image-objects. + struct DirtyEntry { + uint32_t descriptor_hash = 0; + bool is_class = false; + uint32_t sort_key = 0; + }; + // Parse dirty-image-objects into (offset->entry) map. Returns nullopt on parse error. + static std::optional<HashMap<uint32_t, DirtyEntry>> ParseDirtyObjectOffsets( + const HashSet<std::string>& dirty_image_objects) REQUIRES_SHARED(Locks::mutator_lock_); + // Get all objects that match dirty_entries by offset. Returns nullopt if there is a mismatch. + // Map values are sort_keys from DirtyEntry. + std::optional<HashMap<mirror::Object*, uint32_t>> MatchDirtyObjectOffsets( + const HashMap<uint32_t, DirtyEntry>& dirty_entries) REQUIRES_SHARED(Locks::mutator_lock_); // Creates the contiguous image in memory and adjusts pointers. void CopyAndFixupNativeData(size_t oat_index) REQUIRES_SHARED(Locks::mutator_lock_); @@ -685,9 +693,14 @@ class ImageWriter final { // Map of dex files to the indexes of oat files that they were compiled into. const HashMap<const DexFile*, size_t>& dex_file_oat_index_map_; - // Set of objects known to be dirty in the image. Can be nullptr if there are none. + // Set of classes/objects known to be dirty in the image. Can be nullptr if there are none. + // For old dirty-image-objects format this set contains descriptors of dirty classes. + // For new format -- a set of dirty object offsets and descriptor hashes. const HashSet<std::string>* dirty_image_objects_; + // Dirty object instances and their sort keys parsed from dirty_image_object_ + HashMap<mirror::Object*, uint32_t> dirty_objects_; + // Objects are guaranteed to not cross the region size boundary. size_t region_size_ = 0u; @@ -697,7 +710,6 @@ class ImageWriter final { class FixupClassVisitor; class FixupRootVisitor; class FixupVisitor; - class ImageFileGuard; class LayoutHelper; class NativeLocationVisitor; class PruneClassesVisitor; @@ -712,7 +724,6 @@ class ImageWriter final { std::ostream& operator<<(std::ostream& stream, ImageWriter::Bin bin); std::ostream& operator<<(std::ostream& stream, ImageWriter::NativeObjectRelocationType type); -std::ostream& operator<<(std::ostream& stream, ImageWriter::StubType stub_type); } // namespace linker } // namespace art diff --git a/dex2oat/linker/multi_oat_relative_patcher_test.cc b/dex2oat/linker/multi_oat_relative_patcher_test.cc index 2a05816206..a393eb8b1e 100644 --- a/dex2oat/linker/multi_oat_relative_patcher_test.cc +++ b/dex2oat/linker/multi_oat_relative_patcher_test.cc @@ -16,8 +16,8 @@ #include "multi_oat_relative_patcher.h" -#include "compiled_method.h" #include "debug/method_debug_info.h" +#include "driver/compiled_method.h" #include "gtest/gtest.h" #include "linker/linker_patch.h" #include "stream/vector_output_stream.h" diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 19e77de10b..63d2a42af3 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -16,10 +16,13 @@ #include "oat_writer.h" -#include <algorithm> #include <unistd.h> #include <zlib.h> +#include <algorithm> +#include <memory> +#include <vector> + #include "arch/arm64/instruction_set_features_arm64.h" #include "art_method-inl.h" #include "base/allocator.h" @@ -37,18 +40,19 @@ #include "class_linker.h" #include "class_table-inl.h" #include "code_info_table_deduper.h" -#include "compiled_method-inl.h" #include "debug/method_debug_info.h" #include "dex/art_dex_file_loader.h" #include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "dex/dex_file_types.h" +#include "dex/dex_file_verifier.h" #include "dex/standard_dex_file.h" #include "dex/type_lookup_table.h" #include "dex/verification_results.h" #include "dex_container.h" #include "dexlayout.h" +#include "driver/compiled_method-inl.h" #include "driver/compiler_driver-inl.h" #include "driver/compiler_options.h" #include "gc/space/image_space.h" @@ -138,75 +142,6 @@ class OatWriter::ChecksumUpdatingOutputStream : public OutputStream { OatWriter* const writer_; }; -// Defines the location of the raw dex file to write. -class OatWriter::DexFileSource { - public: - enum Type { - kNone, - kZipEntry, - kRawFile, - kRawData, - }; - - explicit DexFileSource(ZipEntry* zip_entry) - : type_(kZipEntry), source_(zip_entry) { - DCHECK(source_ != nullptr); - } - - explicit DexFileSource(File* raw_file) - : type_(kRawFile), source_(raw_file) { - DCHECK(source_ != nullptr); - } - - explicit DexFileSource(const uint8_t* dex_file) - : type_(kRawData), source_(dex_file) { - DCHECK(source_ != nullptr); - } - - Type GetType() const { return type_; } - bool IsZipEntry() const { return type_ == kZipEntry; } - bool IsRawFile() const { return type_ == kRawFile; } - bool IsRawData() const { return type_ == kRawData; } - - ZipEntry* GetZipEntry() const { - DCHECK(IsZipEntry()); - DCHECK(source_ != nullptr); - return static_cast<ZipEntry*>(const_cast<void*>(source_)); - } - - File* GetRawFile() const { - DCHECK(IsRawFile()); - DCHECK(source_ != nullptr); - return static_cast<File*>(const_cast<void*>(source_)); - } - - const uint8_t* GetRawData() const { - DCHECK(IsRawData()); - DCHECK(source_ != nullptr); - return static_cast<const uint8_t*>(source_); - } - - void SetDexLayoutData(std::vector<uint8_t>&& dexlayout_data) { - DCHECK_GE(dexlayout_data.size(), sizeof(DexFile::Header)); - dexlayout_data_ = std::move(dexlayout_data); - type_ = kRawData; - source_ = dexlayout_data_.data(); - } - - void Clear() { - type_ = kNone; - source_ = nullptr; - // Release the memory held by `dexlayout_data_`. - std::vector<uint8_t> temp; - temp.swap(dexlayout_data_); - } - - private: - Type type_; - const void* source_; - std::vector<uint8_t> dexlayout_data_; -}; - // OatClassHeader is the header only part of the oat class that is required even when compilation // is not enabled. class OatWriter::OatClassHeader { @@ -307,12 +242,11 @@ class OatWriter::OatClass { class OatWriter::OatDexFile { public: - OatDexFile(const char* dex_file_location, - DexFileSource source, - uint32_t dex_file_location_checksun, - size_t dex_file_size); + explicit OatDexFile(std::unique_ptr<const DexFile> dex_file); OatDexFile(OatDexFile&& src) = default; + const DexFile* GetDexFile() const { return dex_file_.get(); } + const char* GetLocation() const { return dex_file_location_data_; } @@ -325,8 +259,10 @@ class OatWriter::OatDexFile { return class_offsets_.size() * sizeof(class_offsets_[0]); } - // The source of the dex file. - DexFileSource source_; + std::unique_ptr<const DexFile> dex_file_; + std::unique_ptr<std::string> dex_file_location_; + + std::vector<uint8_t> cdex_main_section_; // Dex file size. Passed in the constructor, but could be // overwritten by LayoutDexFile. @@ -384,115 +320,112 @@ class OatWriter::OatDexFile { << "file_offset=" << file_offset << " offset_=" << offset_ OatWriter::OatWriter(const CompilerOptions& compiler_options, + const VerificationResults* verification_results, TimingLogger* timings, ProfileCompilationInfo* info, CompactDexLevel compact_dex_level) - : write_state_(WriteState::kAddingDexFileSources), - timings_(timings), - raw_dex_files_(), - zip_archives_(), - zipped_dex_files_(), - zipped_dex_file_locations_(), - compiler_driver_(nullptr), - compiler_options_(compiler_options), - image_writer_(nullptr), - extract_dex_files_into_vdex_(true), - vdex_begin_(nullptr), - dex_files_(nullptr), - primary_oat_file_(false), - vdex_size_(0u), - vdex_dex_files_offset_(0u), - vdex_dex_shared_data_offset_(0u), - vdex_verifier_deps_offset_(0u), - vdex_quickening_info_offset_(0u), - vdex_lookup_tables_offset_(0u), - oat_checksum_(adler32(0L, Z_NULL, 0)), - code_size_(0u), - oat_size_(0u), - data_bimg_rel_ro_start_(0u), - data_bimg_rel_ro_size_(0u), - bss_start_(0u), - bss_size_(0u), - bss_methods_offset_(0u), - bss_roots_offset_(0u), - data_bimg_rel_ro_entries_(), - bss_method_entry_references_(), - bss_method_entries_(), - bss_type_entries_(), - bss_public_type_entries_(), - bss_package_type_entries_(), - bss_string_entries_(), - oat_data_offset_(0u), - oat_header_(nullptr), - size_vdex_header_(0), - size_vdex_checksums_(0), - size_dex_file_alignment_(0), - size_quickening_table_offset_(0), - size_executable_offset_alignment_(0), - size_oat_header_(0), - size_oat_header_key_value_store_(0), - size_dex_file_(0), - size_verifier_deps_(0), - size_verifier_deps_alignment_(0), - size_quickening_info_(0), - size_quickening_info_alignment_(0), - size_vdex_lookup_table_alignment_(0), - size_vdex_lookup_table_(0), - size_interpreter_to_interpreter_bridge_(0), - size_interpreter_to_compiled_code_bridge_(0), - size_jni_dlsym_lookup_trampoline_(0), - size_jni_dlsym_lookup_critical_trampoline_(0), - size_quick_generic_jni_trampoline_(0), - size_quick_imt_conflict_trampoline_(0), - size_quick_resolution_trampoline_(0), - size_quick_to_interpreter_bridge_(0), - size_nterp_trampoline_(0), - size_trampoline_alignment_(0), - size_method_header_(0), - size_code_(0), - size_code_alignment_(0), - size_data_bimg_rel_ro_(0), - size_data_bimg_rel_ro_alignment_(0), - size_relative_call_thunks_(0), - size_misc_thunks_(0), - size_vmap_table_(0), - size_method_info_(0), - size_oat_dex_file_location_size_(0), - size_oat_dex_file_location_data_(0), - size_oat_dex_file_location_checksum_(0), - size_oat_dex_file_offset_(0), - size_oat_dex_file_class_offsets_offset_(0), - size_oat_dex_file_lookup_table_offset_(0), - size_oat_dex_file_dex_layout_sections_offset_(0), - size_oat_dex_file_dex_layout_sections_(0), - size_oat_dex_file_dex_layout_sections_alignment_(0), - size_oat_dex_file_method_bss_mapping_offset_(0), - size_oat_dex_file_type_bss_mapping_offset_(0), - size_oat_dex_file_public_type_bss_mapping_offset_(0), - size_oat_dex_file_package_type_bss_mapping_offset_(0), - size_oat_dex_file_string_bss_mapping_offset_(0), - size_bcp_bss_info_size_(0), - size_bcp_bss_info_method_bss_mapping_offset_(0), - size_bcp_bss_info_type_bss_mapping_offset_(0), - size_bcp_bss_info_public_type_bss_mapping_offset_(0), - size_bcp_bss_info_package_type_bss_mapping_offset_(0), - size_bcp_bss_info_string_bss_mapping_offset_(0), - size_oat_class_offsets_alignment_(0), - size_oat_class_offsets_(0), - size_oat_class_type_(0), - size_oat_class_status_(0), - size_oat_class_num_methods_(0), - size_oat_class_method_bitmaps_(0), - size_oat_class_method_offsets_(0), - size_method_bss_mappings_(0u), - size_type_bss_mappings_(0u), - size_public_type_bss_mappings_(0u), - size_package_type_bss_mappings_(0u), - size_string_bss_mappings_(0u), - relative_patcher_(nullptr), - profile_compilation_info_(info), - compact_dex_level_(compact_dex_level) { -} + : write_state_(WriteState::kAddingDexFileSources), + timings_(timings), + compiler_driver_(nullptr), + compiler_options_(compiler_options), + verification_results_(verification_results), + image_writer_(nullptr), + extract_dex_files_into_vdex_(true), + vdex_begin_(nullptr), + dex_files_(nullptr), + primary_oat_file_(false), + vdex_size_(0u), + vdex_dex_files_offset_(0u), + vdex_dex_shared_data_offset_(0u), + vdex_verifier_deps_offset_(0u), + vdex_quickening_info_offset_(0u), + vdex_lookup_tables_offset_(0u), + oat_checksum_(adler32(0L, Z_NULL, 0)), + code_size_(0u), + oat_size_(0u), + data_bimg_rel_ro_start_(0u), + data_bimg_rel_ro_size_(0u), + bss_start_(0u), + bss_size_(0u), + bss_methods_offset_(0u), + bss_roots_offset_(0u), + data_bimg_rel_ro_entries_(), + bss_method_entry_references_(), + bss_method_entries_(), + bss_type_entries_(), + bss_public_type_entries_(), + bss_package_type_entries_(), + bss_string_entries_(), + oat_data_offset_(0u), + oat_header_(nullptr), + size_vdex_header_(0), + size_vdex_checksums_(0), + size_dex_file_alignment_(0), + size_quickening_table_offset_(0), + size_executable_offset_alignment_(0), + size_oat_header_(0), + size_oat_header_key_value_store_(0), + size_dex_file_(0), + size_verifier_deps_(0), + size_verifier_deps_alignment_(0), + size_quickening_info_(0), + size_quickening_info_alignment_(0), + size_vdex_lookup_table_alignment_(0), + size_vdex_lookup_table_(0), + size_interpreter_to_interpreter_bridge_(0), + size_interpreter_to_compiled_code_bridge_(0), + size_jni_dlsym_lookup_trampoline_(0), + size_jni_dlsym_lookup_critical_trampoline_(0), + size_quick_generic_jni_trampoline_(0), + size_quick_imt_conflict_trampoline_(0), + size_quick_resolution_trampoline_(0), + size_quick_to_interpreter_bridge_(0), + size_nterp_trampoline_(0), + size_trampoline_alignment_(0), + size_method_header_(0), + size_code_(0), + size_code_alignment_(0), + size_data_bimg_rel_ro_(0), + size_data_bimg_rel_ro_alignment_(0), + size_relative_call_thunks_(0), + size_misc_thunks_(0), + size_vmap_table_(0), + size_method_info_(0), + size_oat_dex_file_location_size_(0), + size_oat_dex_file_location_data_(0), + size_oat_dex_file_location_checksum_(0), + size_oat_dex_file_offset_(0), + size_oat_dex_file_class_offsets_offset_(0), + size_oat_dex_file_lookup_table_offset_(0), + size_oat_dex_file_dex_layout_sections_offset_(0), + size_oat_dex_file_dex_layout_sections_(0), + size_oat_dex_file_dex_layout_sections_alignment_(0), + size_oat_dex_file_method_bss_mapping_offset_(0), + size_oat_dex_file_type_bss_mapping_offset_(0), + size_oat_dex_file_public_type_bss_mapping_offset_(0), + size_oat_dex_file_package_type_bss_mapping_offset_(0), + size_oat_dex_file_string_bss_mapping_offset_(0), + size_bcp_bss_info_size_(0), + size_bcp_bss_info_method_bss_mapping_offset_(0), + size_bcp_bss_info_type_bss_mapping_offset_(0), + size_bcp_bss_info_public_type_bss_mapping_offset_(0), + size_bcp_bss_info_package_type_bss_mapping_offset_(0), + size_bcp_bss_info_string_bss_mapping_offset_(0), + size_oat_class_offsets_alignment_(0), + size_oat_class_offsets_(0), + size_oat_class_type_(0), + size_oat_class_status_(0), + size_oat_class_num_methods_(0), + size_oat_class_method_bitmaps_(0), + size_oat_class_method_offsets_(0), + size_method_bss_mappings_(0u), + size_type_bss_mappings_(0u), + size_public_type_bss_mappings_(0u), + size_package_type_bss_mappings_(0u), + size_string_bss_mappings_(0u), + relative_patcher_(nullptr), + profile_compilation_info_(info), + compact_dex_level_(compact_dex_level) {} static bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location) { const bool valid_standard_dex_magic = DexFileLoader::IsMagicValid(raw_header); @@ -513,22 +446,6 @@ static bool ValidateDexFileHeader(const uint8_t* raw_header, const char* locatio return true; } -static const UnalignedDexFileHeader* GetDexFileHeader(File* file, - uint8_t* raw_header, - const char* location) { - // Read the dex file header and perform minimal verification. - if (!file->ReadFully(raw_header, sizeof(DexFile::Header))) { - PLOG(ERROR) << "Failed to read dex file header. Actual: " - << " File: " << location << " Output: " << file->GetPath(); - return nullptr; - } - if (!ValidateDexFileHeader(raw_header, location)) { - return nullptr; - } - - return AsUnalignedDexFileHeader(raw_header); -} - bool OatWriter::AddDexFileSource(const char* filename, const char* location) { DCHECK(write_state_ == WriteState::kAddingDexFileSources); File fd(filename, O_RDONLY, /* check_usage= */ false); @@ -545,58 +462,21 @@ bool OatWriter::AddDexFileSource(const char* filename, const char* location) { bool OatWriter::AddDexFileSource(File&& dex_file_fd, const char* location) { DCHECK(write_state_ == WriteState::kAddingDexFileSources); std::string error_msg; - uint32_t magic; - if (!ReadMagicAndReset(dex_file_fd.Fd(), &magic, &error_msg)) { - LOG(ERROR) << "Failed to read magic number from dex file '" << location << "': " << error_msg; + ArtDexFileLoader loader(dex_file_fd.Release(), location); + std::vector<std::unique_ptr<const DexFile>> dex_files; + if (!loader.Open(/*verify=*/false, + /*verify_checksum=*/false, + &error_msg, + &dex_files)) { + LOG(ERROR) << "Failed to open dex file '" << location << "': " << error_msg; return false; } - if (DexFileLoader::IsMagicValid(magic)) { - uint8_t raw_header[sizeof(DexFile::Header)]; - const UnalignedDexFileHeader* header = GetDexFileHeader(&dex_file_fd, raw_header, location); - if (header == nullptr) { - LOG(ERROR) << "Failed to get DexFileHeader from file descriptor for '" - << location << "': " << error_msg; - return false; - } - // The file is open for reading, not writing, so it's OK to let the File destructor - // close it without checking for explicit Close(), so pass checkUsage = false. - raw_dex_files_.emplace_back(new File(dex_file_fd.Release(), location, /* checkUsage */ false)); - oat_dex_files_.emplace_back(/* OatDexFile */ - location, - DexFileSource(raw_dex_files_.back().get()), - header->checksum_, - header->file_size_); - } else if (IsZipMagic(magic)) { - zip_archives_.emplace_back(ZipArchive::OpenFromFd(dex_file_fd.Release(), location, &error_msg)); - ZipArchive* zip_archive = zip_archives_.back().get(); - if (zip_archive == nullptr) { - LOG(ERROR) << "Failed to open zip from file descriptor for '" << location << "': " - << error_msg; - return false; - } - for (size_t i = 0; ; ++i) { - std::string entry_name = DexFileLoader::GetMultiDexClassesDexName(i); - std::unique_ptr<ZipEntry> entry(zip_archive->Find(entry_name.c_str(), &error_msg)); - if (entry == nullptr) { - break; - } - zipped_dex_files_.push_back(std::move(entry)); - zipped_dex_file_locations_.push_back(DexFileLoader::GetMultiDexLocation(i, location)); - const char* full_location = zipped_dex_file_locations_.back().c_str(); - // We override the checksum from header with the CRC from ZIP entry. - oat_dex_files_.emplace_back(/* OatDexFile */ - full_location, - DexFileSource(zipped_dex_files_.back().get()), - zipped_dex_files_.back()->GetCrc32(), - zipped_dex_files_.back()->GetUncompressedLength()); - } - if (zipped_dex_file_locations_.empty()) { - LOG(ERROR) << "No dex files in zip file '" << location << "': " << error_msg; + for (auto& dex_file : dex_files) { + if (dex_file->IsCompactDexFile()) { + LOG(ERROR) << "Compact dex is only supported from vdex: " << location; return false; } - } else { - LOG(ERROR) << "Expected valid zip or dex file: '" << location << "'"; - return false; + oat_dex_files_.emplace_back(std::move(dex_file)); } return true; } @@ -619,14 +499,13 @@ bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file, const char* loc return false; } // We used `zipped_dex_file_locations_` to keep the strings in memory. - zipped_dex_file_locations_.push_back(DexFileLoader::GetMultiDexLocation(i, location)); - const char* full_location = zipped_dex_file_locations_.back().c_str(); + std::string multidex_location = DexFileLoader::GetMultiDexLocation(i, location); const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(current_dex_data); - oat_dex_files_.emplace_back(/* OatDexFile */ - full_location, - DexFileSource(current_dex_data), - vdex_file.GetLocationChecksum(i), - header->file_size_); + if (!AddRawDexFileSource({current_dex_data, header->file_size_}, + multidex_location.c_str(), + vdex_file.GetLocationChecksum(i))) { + return false; + } } if (vdex_file.GetNextDexFileData(current_dex_data, i) != nullptr) { @@ -646,26 +525,18 @@ bool OatWriter::AddRawDexFileSource(const ArrayRef<const uint8_t>& data, const char* location, uint32_t location_checksum) { DCHECK(write_state_ == WriteState::kAddingDexFileSources); - if (data.size() < sizeof(DexFile::Header)) { - LOG(ERROR) << "Provided data is shorter than dex file header. size: " - << data.size() << " File: " << location; - return false; - } - if (!ValidateDexFileHeader(data.data(), location)) { - return false; - } - const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(data.data()); - if (data.size() < header->file_size_) { - LOG(ERROR) << "Truncated dex file data. Data size: " << data.size() - << " file size from header: " << header->file_size_ << " File: " << location; + std::string error_msg; + ArtDexFileLoader loader(data.data(), data.size(), location); + auto dex_file = loader.Open(location_checksum, + nullptr, + /*verify=*/false, + /*verify_checksum=*/false, + &error_msg); + if (dex_file == nullptr) { + LOG(ERROR) << "Failed to open dex file '" << location << "': " << error_msg; return false; } - - oat_dex_files_.emplace_back(/* OatDexFile */ - location, - DexFileSource(data.data()), - location_checksum, - header->file_size_); + oat_dex_files_.emplace_back(std::move(dex_file)); return true; } @@ -700,8 +571,8 @@ bool OatWriter::WriteAndOpenDexFiles( // Write DEX files into VDEX, mmap and open them. std::vector<MemMap> dex_files_map; std::vector<std::unique_ptr<const DexFile>> dex_files; - if (!WriteDexFiles(vdex_file, use_existing_vdex, copy_dex_files, &dex_files_map) || - !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) { + if (!WriteDexFiles(vdex_file, verify, use_existing_vdex, copy_dex_files, &dex_files_map) || + !OpenDexFiles(vdex_file, &dex_files_map, &dex_files)) { return false; } @@ -1013,7 +884,7 @@ class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor { ClassStatus status; bool found = writer_->compiler_driver_->GetCompiledClass(class_ref, &status); if (!found) { - const VerificationResults* results = writer_->compiler_options_.GetVerificationResults(); + const VerificationResults* results = writer_->verification_results_; if (results != nullptr && results->IsClassRejected(class_ref)) { // The oat class status is used only for verification of resolved classes, // so use ClassStatus::kErrorResolved whether the class was resolved or unresolved @@ -1351,7 +1222,7 @@ class OatWriter::LayoutReserveOffsetCodeMethodVisitor : public OrderedMethodVisi ArrayRef<const uint8_t> quick_code = compiled_method->GetQuickCode(); uint32_t code_size = quick_code.size() * sizeof(uint8_t); - uint32_t thumb_offset = compiled_method->CodeDelta(); + uint32_t thumb_offset = compiled_method->GetEntryPointAdjustment(); // Deduplicate code arrays if we are not producing debuggable code. bool deduped = true; @@ -1486,7 +1357,7 @@ class OatWriter::LayoutReserveOffsetCodeMethodVisitor : public OrderedMethodVisi offset_ = relative_patcher_->ReserveSpace(offset_, compiled_method, method_ref); offset_ += CodeAlignmentSize(offset_, *compiled_method); DCHECK_ALIGNED_PARAM(offset_ + sizeof(OatQuickMethodHeader), - GetInstructionSetAlignment(compiled_method->GetInstructionSet())); + GetInstructionSetCodeAlignment(compiled_method->GetInstructionSet())); return offset_ + sizeof(OatQuickMethodHeader) + thumb_offset; } @@ -1797,9 +1668,10 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { DCHECK_OFFSET_(); } DCHECK_ALIGNED_PARAM(offset_ + sizeof(OatQuickMethodHeader), - GetInstructionSetAlignment(compiled_method->GetInstructionSet())); - DCHECK_EQ(method_offsets.code_offset_, - offset_ + sizeof(OatQuickMethodHeader) + compiled_method->CodeDelta()) + GetInstructionSetCodeAlignment(compiled_method->GetInstructionSet())); + DCHECK_EQ( + method_offsets.code_offset_, + offset_ + sizeof(OatQuickMethodHeader) + compiled_method->GetEntryPointAdjustment()) << dex_file_->PrettyMethod(method_ref.index); const OatQuickMethodHeader& method_header = oat_class->method_headers_[method_offsets_index]; @@ -2391,27 +2263,29 @@ size_t OatWriter::InitOatCode(size_t offset) { offset = RoundUp(offset, kPageSize); oat_header_->SetExecutableOffset(offset); size_executable_offset_alignment_ = offset - old_offset; - if (GetCompilerOptions().IsBootImage() && primary_oat_file_) { - InstructionSet instruction_set = compiler_options_.GetInstructionSet(); + InstructionSet instruction_set = compiler_options_.GetInstructionSet(); + if (GetCompilerOptions().IsBootImage() && primary_oat_file_ && + // TODO(riscv64): remove this when we have compiler support for RISC-V. + instruction_set != InstructionSet::kRiscv64) { const bool generate_debug_info = GetCompilerOptions().GenerateAnyDebugInfo(); size_t adjusted_offset = offset; - #define DO_TRAMPOLINE(field, fn_name) \ - /* Pad with at least four 0xFFs so we can do DCHECKs in OatQuickMethodHeader */ \ - offset = CompiledCode::AlignCode(offset + 4, instruction_set); \ - adjusted_offset = offset + CompiledCode::CodeDelta(instruction_set); \ - oat_header_->Set ## fn_name ## Offset(adjusted_offset); \ - (field) = compiler_driver_->Create ## fn_name(); \ - if (generate_debug_info) { \ - debug::MethodDebugInfo info = {}; \ - info.custom_name = #fn_name; \ - info.isa = instruction_set; \ - info.is_code_address_text_relative = true; \ - /* Use the code offset rather than the `adjusted_offset`. */ \ - info.code_address = offset - oat_header_->GetExecutableOffset(); \ - info.code_size = (field)->size(); \ - method_info_.push_back(std::move(info)); \ - } \ + #define DO_TRAMPOLINE(field, fn_name) \ + /* Pad with at least four 0xFFs so we can do DCHECKs in OatQuickMethodHeader */ \ + offset = CompiledCode::AlignCode(offset + 4, instruction_set); \ + adjusted_offset = offset + GetInstructionSetEntryPointAdjustment(instruction_set); \ + oat_header_->Set ## fn_name ## Offset(adjusted_offset); \ + (field) = compiler_driver_->Create ## fn_name(); \ + if (generate_debug_info) { \ + debug::MethodDebugInfo info = {}; \ + info.custom_name = #fn_name; \ + info.isa = instruction_set; \ + info.is_code_address_text_relative = true; \ + /* Use the code offset rather than the `adjusted_offset`. */ \ + info.code_address = offset - oat_header_->GetExecutableOffset(); \ + info.code_size = (field)->size(); \ + method_info_.push_back(std::move(info)); \ + } \ offset += (field)->size(); DO_TRAMPOLINE(jni_dlsym_lookup_trampoline_, JniDlsymLookupTrampoline); @@ -3197,9 +3071,10 @@ size_t OatWriter::WriteBcpBssInfo(OutputStream* out, size_t file_offset, size_t } size_t OatWriter::WriteCode(OutputStream* out, size_t file_offset, size_t relative_offset) { - if (GetCompilerOptions().IsBootImage() && primary_oat_file_) { - InstructionSet instruction_set = compiler_options_.GetInstructionSet(); - + InstructionSet instruction_set = compiler_options_.GetInstructionSet(); + if (GetCompilerOptions().IsBootImage() && primary_oat_file_ && + // TODO(riscv64): remove this when we have compiler support for RISC-V. + instruction_set != InstructionSet::kRiscv64) { #define DO_TRAMPOLINE(field) \ do { \ /* Pad with at least four 0xFFs so we can do DCHECKs in OatQuickMethodHeader */ \ @@ -3304,6 +3179,7 @@ bool OatWriter::RecordOatDataOffset(OutputStream* out) { } bool OatWriter::WriteDexFiles(File* file, + bool verify, bool use_existing_vdex, CopyOption copy_dex_files, /*out*/ std::vector<MemMap>* opened_dex_files_map) { @@ -3313,12 +3189,8 @@ bool OatWriter::WriteDexFiles(File* file, if (copy_dex_files == CopyOption::kOnlyIfCompressed) { extract_dex_files_into_vdex_ = false; for (OatDexFile& oat_dex_file : oat_dex_files_) { - if (!oat_dex_file.source_.IsZipEntry()) { - extract_dex_files_into_vdex_ = true; - break; - } - ZipEntry* entry = oat_dex_file.source_.GetZipEntry(); - if (!entry->IsUncompressed() || !entry->IsAlignedTo(alignof(DexFile::Header))) { + const DexFileContainer* container = oat_dex_file.GetDexFile()->GetContainer(); + if (!(container->IsZip() && container->IsFileMap())) { extract_dex_files_into_vdex_ = true; break; } @@ -3330,6 +3202,24 @@ bool OatWriter::WriteDexFiles(File* file, extract_dex_files_into_vdex_ = false; } + if (verify) { + TimingLogger::ScopedTiming split2("Verify input Dex files", timings_); + for (OatDexFile& oat_dex_file : oat_dex_files_) { + const DexFile* dex_file = oat_dex_file.GetDexFile(); + if (dex_file->IsCompactDexFile()) { + continue; // Compact dex files can not be verified. + } + std::string error_msg; + if (!dex::Verify(dex_file, + dex_file->GetLocation().c_str(), + /*verify_checksum=*/true, + &error_msg)) { + LOG(ERROR) << "Failed to verify " << dex_file->GetLocation() << ": " << error_msg; + return false; + } + } + } + if (extract_dex_files_into_vdex_) { vdex_dex_files_offset_ = vdex_size_; @@ -3370,16 +3260,13 @@ bool OatWriter::WriteDexFiles(File* file, // Dex files from input vdex are represented as raw dex files and they can be // compact dex files. These need to specify the same shared data section if any. for (const OatDexFile& oat_dex_file : oat_dex_files_) { - if (!oat_dex_file.source_.IsRawData()) { - continue; - } - const uint8_t* raw_data = oat_dex_file.source_.GetRawData(); - const UnalignedDexFileHeader& header = *AsUnalignedDexFileHeader(raw_data); - if (!CompactDexFile::IsMagicValid(header.magic_) || header.data_size_ == 0u) { + const DexFile* dex_file = oat_dex_file.GetDexFile(); + auto& header = dex_file->GetHeader(); + if (!dex_file->IsCompactDexFile() || header.data_size_ == 0u) { // Non compact dex does not have shared data section. continue; } - const uint8_t* cur_data_begin = raw_data + header.data_off_; + const uint8_t* cur_data_begin = dex_file->Begin() + header.data_off_; if (raw_dex_file_shared_data_begin == nullptr) { raw_dex_file_shared_data_begin = cur_data_begin; } else if (raw_dex_file_shared_data_begin != cur_data_begin) { @@ -3434,9 +3321,28 @@ bool OatWriter::WriteDexFiles(File* file, vdex_size_ = RoundUp(vdex_size_, 4u); size_dex_file_alignment_ += vdex_size_ - old_vdex_size; // Write the actual dex file. - if (!WriteDexFile(file, &oat_dex_file, use_existing_vdex)) { - return false; + DCHECK_EQ(vdex_size_, oat_dex_file.dex_file_offset_); + uint8_t* out = vdex_begin_ + oat_dex_file.dex_file_offset_; + const std::vector<uint8_t>& cdex_data = oat_dex_file.cdex_main_section_; + if (!cdex_data.empty()) { + CHECK(!use_existing_vdex); + // Use the compact dex version instead of the original dex file. + DCHECK_EQ(oat_dex_file.dex_file_size_, cdex_data.size()); + memcpy(out, cdex_data.data(), cdex_data.size()); + } else { + const DexFile* dex_file = oat_dex_file.GetDexFile(); + DCHECK_EQ(oat_dex_file.dex_file_size_, dex_file->Size()); + if (use_existing_vdex) { + // The vdex already contains the data. + DCHECK_EQ(memcmp(out, dex_file->Begin(), dex_file->Size()), 0); + } else { + memcpy(out, dex_file->Begin(), dex_file->Size()); + } } + + // Update current size and account for the written data. + vdex_size_ += oat_dex_file.dex_file_size_; + size_dex_file_ += oat_dex_file.dex_file_size_; } // Write shared dex file data section and fix up the dex file headers. @@ -3496,102 +3402,15 @@ bool OatWriter::WriteDexFiles(File* file, void OatWriter::CloseSources() { for (OatDexFile& oat_dex_file : oat_dex_files_) { - oat_dex_file.source_.Clear(); // Get rid of the reference, it's about to be invalidated. + oat_dex_file.dex_file_.reset(); } - zipped_dex_files_.clear(); - zip_archives_.clear(); - raw_dex_files_.clear(); -} - -bool OatWriter::WriteDexFile(File* file, - OatDexFile* oat_dex_file, - bool use_existing_vdex) { - DCHECK_EQ(vdex_size_, oat_dex_file->dex_file_offset_); - if (oat_dex_file->source_.IsZipEntry()) { - DCHECK(!use_existing_vdex); - if (!WriteDexFile(file, oat_dex_file, oat_dex_file->source_.GetZipEntry())) { - return false; - } - } else if (oat_dex_file->source_.IsRawFile()) { - DCHECK(!use_existing_vdex); - if (!WriteDexFile(file, oat_dex_file, oat_dex_file->source_.GetRawFile())) { - return false; - } - } else { - DCHECK(oat_dex_file->source_.IsRawData()); - const uint8_t* raw_data = oat_dex_file->source_.GetRawData(); - if (!WriteDexFile(oat_dex_file, raw_data, use_existing_vdex)) { - return false; - } - } - - // Update current size and account for the written data. - vdex_size_ += oat_dex_file->dex_file_size_; - size_dex_file_ += oat_dex_file->dex_file_size_; - return true; } bool OatWriter::LayoutDexFile(OatDexFile* oat_dex_file) { TimingLogger::ScopedTiming split("Dex Layout", timings_); std::string error_msg; std::string location(oat_dex_file->GetLocation()); - std::unique_ptr<const DexFile> dex_file; - const ArtDexFileLoader dex_file_loader; - if (oat_dex_file->source_.IsZipEntry()) { - ZipEntry* zip_entry = oat_dex_file->source_.GetZipEntry(); - MemMap mem_map; - { - TimingLogger::ScopedTiming extract("Unzip", timings_); - mem_map = zip_entry->ExtractToMemMap(location.c_str(), "classes.dex", &error_msg); - } - if (!mem_map.IsValid()) { - LOG(ERROR) << "Failed to extract dex file to mem map for layout: " << error_msg; - return false; - } - TimingLogger::ScopedTiming extract("Open", timings_); - dex_file = dex_file_loader.Open(location, - zip_entry->GetCrc32(), - std::move(mem_map), - /*verify=*/ true, - /*verify_checksum=*/ true, - &error_msg); - } else if (oat_dex_file->source_.IsRawFile()) { - File* raw_file = oat_dex_file->source_.GetRawFile(); - int dup_fd = DupCloexec(raw_file->Fd()); - if (dup_fd < 0) { - PLOG(ERROR) << "Failed to dup dex file descriptor (" << raw_file->Fd() << ") at " << location; - return false; - } - TimingLogger::ScopedTiming extract("Open", timings_); - dex_file = dex_file_loader.OpenDex(dup_fd, location, - /*verify=*/ true, - /*verify_checksum=*/ true, - /*mmap_shared=*/ false, - &error_msg); - } else { - // The source data is a vdex file. - CHECK(oat_dex_file->source_.IsRawData()) - << static_cast<size_t>(oat_dex_file->source_.GetType()); - const uint8_t* raw_dex_file = oat_dex_file->source_.GetRawData(); - // Note: The raw data has already been checked to contain the header - // and all the data that the header specifies as the file size. - DCHECK(raw_dex_file != nullptr); - DCHECK(ValidateDexFileHeader(raw_dex_file, oat_dex_file->GetLocation())); - const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file); - // Since the source may have had its layout changed, or may be quickened, don't verify it. - dex_file = dex_file_loader.Open(raw_dex_file, - header->file_size_, - location, - oat_dex_file->dex_file_location_checksum_, - nullptr, - /*verify=*/ false, - /*verify_checksum=*/ false, - &error_msg); - } - if (dex_file == nullptr) { - LOG(ERROR) << "Failed to open dex file for layout: " << error_msg; - return false; - } + std::unique_ptr<const DexFile>& dex_file = oat_dex_file->dex_file_; Options options; options.compact_dex_level_ = compact_dex_level_; options.update_checksum_ = true; @@ -3604,11 +3423,11 @@ bool OatWriter::LayoutDexFile(OatDexFile* oat_dex_file) { &dex_container_, &error_msg)) { oat_dex_file->dex_sections_layout_ = dex_layout.GetSections(); - oat_dex_file->source_.SetDexLayoutData(dex_container_->GetMainSection()->ReleaseData()); + oat_dex_file->cdex_main_section_ = dex_container_->GetMainSection()->ReleaseData(); // Dex layout can affect the size of the dex file, so we update here what we have set // when adding the dex file as a source. const UnalignedDexFileHeader* header = - AsUnalignedDexFileHeader(oat_dex_file->source_.GetRawData()); + AsUnalignedDexFileHeader(oat_dex_file->cdex_main_section_.data()); oat_dex_file->dex_file_size_ = header->file_size_; } else { LOG(WARNING) << "Failed to run dex layout, reason:" << error_msg; @@ -3623,57 +3442,8 @@ bool OatWriter::LayoutDexFile(OatDexFile* oat_dex_file) { return true; } -bool OatWriter::WriteDexFile(File* file, - OatDexFile* oat_dex_file, - ZipEntry* dex_file) { - uint8_t* raw_output = vdex_begin_ + oat_dex_file->dex_file_offset_; - - // Extract the dex file. - std::string error_msg; - if (!dex_file->ExtractToMemory(raw_output, &error_msg)) { - LOG(ERROR) << "Failed to extract dex file from ZIP entry: " << error_msg - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - - return true; -} - -bool OatWriter::WriteDexFile(File* file, - OatDexFile* oat_dex_file, - File* dex_file) { - uint8_t* raw_output = vdex_begin_ + oat_dex_file->dex_file_offset_; - - if (!dex_file->PreadFully(raw_output, oat_dex_file->dex_file_size_, /*offset=*/ 0u)) { - PLOG(ERROR) << "Failed to copy dex file to vdex file." - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - - return true; -} - -bool OatWriter::WriteDexFile(OatDexFile* oat_dex_file, - const uint8_t* dex_file, - bool use_existing_vdex) { - // Note: The raw data has already been checked to contain the header - // and all the data that the header specifies as the file size. - DCHECK(dex_file != nullptr); - DCHECK(ValidateDexFileHeader(dex_file, oat_dex_file->GetLocation())); - DCHECK_EQ(oat_dex_file->dex_file_size_, AsUnalignedDexFileHeader(dex_file)->file_size_); - - if (use_existing_vdex) { - // The vdex already contains the dex code, no need to write it again. - } else { - uint8_t* raw_output = vdex_begin_ + oat_dex_file->dex_file_offset_; - memcpy(raw_output, dex_file, oat_dex_file->dex_file_size_); - } - return true; -} - bool OatWriter::OpenDexFiles( File* file, - bool verify, /*inout*/ std::vector<MemMap>* opened_dex_files_map, /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) { TimingLogger::ScopedTiming split("OpenDexFiles", timings_); @@ -3686,37 +3456,11 @@ bool OatWriter::OpenDexFiles( if (!extract_dex_files_into_vdex_) { DCHECK_EQ(opened_dex_files_map->size(), 0u); std::vector<std::unique_ptr<const DexFile>> dex_files; - std::vector<MemMap> maps; for (OatDexFile& oat_dex_file : oat_dex_files_) { - std::string error_msg; - maps.emplace_back(oat_dex_file.source_.GetZipEntry()->MapDirectlyOrExtract( - oat_dex_file.dex_file_location_data_, - "zipped dex", - &error_msg, - alignof(DexFile::Header))); - MemMap* map = &maps.back(); - if (!map->IsValid()) { - LOG(ERROR) << error_msg; - return false; - } - // Now, open the dex file. - const ArtDexFileLoader dex_file_loader; - dex_files.emplace_back(dex_file_loader.Open(map->Begin(), - map->Size(), - oat_dex_file.GetLocation(), - oat_dex_file.dex_file_location_checksum_, - /* oat_dex_file */ nullptr, - verify, - verify, - &error_msg)); - if (dex_files.back() == nullptr) { - LOG(ERROR) << "Failed to open dex file from oat file. File: " << oat_dex_file.GetLocation() - << " Error: " << error_msg; - return false; - } + // The dex file is already open, release the reference. + dex_files.emplace_back(std::move(oat_dex_file.dex_file_)); oat_dex_file.class_offsets_.resize(dex_files.back()->GetHeader().class_defs_size_); } - *opened_dex_files_map = std::move(maps); *opened_dex_files = std::move(dex_files); CloseSources(); return true; @@ -3727,7 +3471,6 @@ bool OatWriter::OpenDexFiles( DCHECK_EQ(opened_dex_files_map->size(), 1u); DCHECK(vdex_begin_ == opened_dex_files_map->front().Begin()); - const ArtDexFileLoader dex_file_loader; std::vector<std::unique_ptr<const DexFile>> dex_files; for (OatDexFile& oat_dex_file : oat_dex_files_) { const uint8_t* raw_dex_file = vdex_begin_ + oat_dex_file.dex_file_offset_; @@ -3749,13 +3492,13 @@ bool OatWriter::OpenDexFiles( // Now, open the dex file. std::string error_msg; - dex_files.emplace_back(dex_file_loader.Open(raw_dex_file, - oat_dex_file.dex_file_size_, - oat_dex_file.GetLocation(), - oat_dex_file.dex_file_location_checksum_, - /* oat_dex_file */ nullptr, - verify, - verify, + ArtDexFileLoader dex_file_loader( + raw_dex_file, oat_dex_file.dex_file_size_, oat_dex_file.GetLocation()); + // All dex files have been already verified in WriteDexFiles before we copied them. + dex_files.emplace_back(dex_file_loader.Open(oat_dex_file.dex_file_location_checksum_, + /*oat_dex_file=*/nullptr, + /*verify=*/false, + /*verify_checksum=*/false, &error_msg)); if (dex_files.back() == nullptr) { LOG(ERROR) << "Failed to open dex file from oat file. File: " << oat_dex_file.GetLocation() @@ -4066,16 +3809,14 @@ void OatWriter::SetMultiOatRelativePatcherAdjustment() { } } -OatWriter::OatDexFile::OatDexFile(const char* dex_file_location, - DexFileSource source, - uint32_t dex_file_location_checksum, - size_t dex_file_size) - : source_(std::move(source)), - dex_file_size_(dex_file_size), +OatWriter::OatDexFile::OatDexFile(std::unique_ptr<const DexFile> dex_file) + : dex_file_(std::move(dex_file)), + dex_file_location_(std::make_unique<std::string>(dex_file_->GetLocation())), + dex_file_size_(dex_file_->Size()), offset_(0), - dex_file_location_size_(strlen(dex_file_location)), - dex_file_location_data_(dex_file_location), - dex_file_location_checksum_(dex_file_location_checksum), + dex_file_location_size_(strlen(dex_file_location_->c_str())), + dex_file_location_data_(dex_file_location_->c_str()), + dex_file_location_checksum_(dex_file_->GetLocationChecksum()), dex_file_offset_(0u), lookup_table_offset_(0u), class_offsets_offset_(0u), @@ -4085,8 +3826,7 @@ OatWriter::OatDexFile::OatDexFile(const char* dex_file_location, package_type_bss_mapping_offset_(0u), string_bss_mapping_offset_(0u), dex_sections_layout_offset_(0u), - class_offsets_() { -} + class_offsets_() {} size_t OatWriter::OatDexFile::SizeOf() const { return sizeof(dex_file_location_size_) diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index ebff7a1b5e..79ec47e37f 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -49,6 +49,7 @@ class ProfileCompilationInfo; class TimingLogger; class TypeLookupTable; class VdexFile; +class VerificationResults; class ZipEntry; namespace debug { @@ -115,6 +116,7 @@ enum class CopyOption { class OatWriter { public: OatWriter(const CompilerOptions& compiler_options, + const VerificationResults* verification_results, TimingLogger* timings, ProfileCompilationInfo* info, CompactDexLevel compact_dex_level); @@ -246,7 +248,6 @@ class OatWriter { private: struct BssMappingInfo; class ChecksumUpdatingOutputStream; - class DexFileSource; class OatClassHeader; class OatClass; class OatDexFile; @@ -277,24 +278,12 @@ class OatWriter { // If `update_input_vdex` is true, then this method won't actually write the dex files, // and the compiler will just re-use the existing vdex file. bool WriteDexFiles(File* file, + bool verify, bool use_existing_vdex, CopyOption copy_dex_files, /*out*/ std::vector<MemMap>* opened_dex_files_map); - bool WriteDexFile(File* file, - OatDexFile* oat_dex_file, - bool use_existing_vdex); bool LayoutDexFile(OatDexFile* oat_dex_file); - bool WriteDexFile(File* file, - OatDexFile* oat_dex_file, - ZipEntry* dex_file); - bool WriteDexFile(File* file, - OatDexFile* oat_dex_file, - File* dex_file); - bool WriteDexFile(OatDexFile* oat_dex_file, - const uint8_t* dex_file, - bool use_existing_vdex); bool OpenDexFiles(File* file, - bool verify, /*inout*/ std::vector<MemMap>* opened_dex_files_map, /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files); void WriteQuickeningInfo(/*out*/std::vector<uint8_t>* buffer); @@ -377,20 +366,13 @@ class OatWriter { WriteState write_state_; TimingLogger* timings_; - std::vector<std::unique_ptr<File>> raw_dex_files_; - std::vector<std::unique_ptr<ZipArchive>> zip_archives_; - std::vector<std::unique_ptr<ZipEntry>> zipped_dex_files_; - - // Using std::list<> which doesn't move elements around on push/emplace_back(). - // We need this because we keep plain pointers to the strings' c_str(). - std::list<std::string> zipped_dex_file_locations_; - dchecked_vector<debug::MethodDebugInfo> method_info_; std::vector<uint8_t> code_info_data_; const CompilerDriver* compiler_driver_; const CompilerOptions& compiler_options_; + const VerificationResults* const verification_results_; ImageWriter* image_writer_; // Whether the dex files being compiled are going to be extracted to the vdex. bool extract_dex_files_into_vdex_; diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 6b2198d9b2..6742cf77a0 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -24,7 +24,6 @@ #include "base/unix_file/fd_file.h" #include "class_linker.h" #include "common_compiler_driver_test.h" -#include "compiled_method-inl.h" #include "compiler.h" #include "debug/method_debug_info.h" #include "dex/class_accessor-inl.h" @@ -32,6 +31,7 @@ #include "dex/quick_compiler_callbacks.h" #include "dex/test_dex_file_builder.h" #include "dex/verification_results.h" +#include "driver/compiled_method-inl.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "entrypoints/quick/quick_entrypoints.h" @@ -107,6 +107,7 @@ class OatTest : public CommonCompilerDriverTest { TimingLogger timings("WriteElf", false, false); ClearBootImageOption(); OatWriter oat_writer(*compiler_options_, + verification_results_.get(), &timings, /*profile_compilation_info*/nullptr, CompactDexLevel::kCompactDexLevelNone); @@ -134,6 +135,7 @@ class OatTest : public CommonCompilerDriverTest { TimingLogger timings("WriteElf", false, false); ClearBootImageOption(); OatWriter oat_writer(*compiler_options_, + verification_results_.get(), &timings, profile_compilation_info, CompactDexLevel::kCompactDexLevelNone); @@ -156,6 +158,7 @@ class OatTest : public CommonCompilerDriverTest { TimingLogger timings("WriteElf", false, false); ClearBootImageOption(); OatWriter oat_writer(*compiler_options_, + verification_results_.get(), &timings, profile_compilation_info, CompactDexLevel::kCompactDexLevelNone); @@ -181,7 +184,7 @@ class OatTest : public CommonCompilerDriverTest { if (!oat_writer.WriteAndOpenDexFiles( vdex_file, verify, - /*update_input_vdex=*/ false, + /*use_existing_vdex=*/ false, copy, &opened_dex_files_maps, &opened_dex_files)) { @@ -505,7 +508,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(68U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(4U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(167 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), + EXPECT_EQ(170 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), sizeof(QuickEntryPoints)); } diff --git a/dex2oat/linker/relative_patcher_test.h b/dex2oat/linker/relative_patcher_test.h index 56e0dc19a7..2900523703 100644 --- a/dex2oat/linker/relative_patcher_test.h +++ b/dex2oat/linker/relative_patcher_test.h @@ -24,9 +24,9 @@ #include "base/array_ref.h" #include "base/globals.h" #include "base/macros.h" -#include "compiled_method-inl.h" #include "dex/method_reference.h" #include "dex/string_reference.h" +#include "driver/compiled_method-inl.h" #include "driver/compiled_method_storage.h" #include "linker/relative_patcher.h" #include "oat_quick_method_header.h" @@ -132,7 +132,7 @@ class RelativePatcherTest : public testing::Test { offset += alignment_size; offset += sizeof(OatQuickMethodHeader); - uint32_t quick_code_offset = offset + compiled_method->CodeDelta(); + uint32_t quick_code_offset = offset + compiled_method->GetEntryPointAdjustment(); const auto code = compiled_method->GetQuickCode(); offset += code.size(); @@ -172,7 +172,8 @@ class RelativePatcherTest : public testing::Test { if (patch.GetType() == LinkerPatch::Type::kCallRelative) { auto result = method_offset_map_.FindMethodOffset(patch.TargetMethod()); uint32_t target_offset = - result.first ? result.second : kTrampolineOffset + compiled_method->CodeDelta(); + result.first ? result.second + : kTrampolineOffset + compiled_method->GetEntryPointAdjustment(); patcher_->PatchCall(&patched_code_, patch.LiteralOffset(), offset + patch.LiteralOffset(), @@ -227,7 +228,7 @@ class RelativePatcherTest : public testing::Test { auto result = method_offset_map_.FindMethodOffset(method_ref); CHECK(result.first); // Must have been linked. - size_t offset = result.second - compiled_methods_[idx]->CodeDelta(); + size_t offset = result.second - compiled_methods_[idx]->GetEntryPointAdjustment(); CHECK_LT(offset, output_.size()); CHECK_LE(offset + expected_code.size(), output_.size()); ArrayRef<const uint8_t> linked_code(&output_[offset], expected_code.size()); diff --git a/dex2oat/linker/x86/relative_patcher_x86.cc b/dex2oat/linker/x86/relative_patcher_x86.cc index e3b94b0453..a4444461a3 100644 --- a/dex2oat/linker/x86/relative_patcher_x86.cc +++ b/dex2oat/linker/x86/relative_patcher_x86.cc @@ -16,7 +16,6 @@ #include "linker/x86/relative_patcher_x86.h" -#include "compiled_method.h" #include "linker/linker_patch.h" namespace art { diff --git a/dex2oat/linker/x86_64/relative_patcher_x86_64.cc b/dex2oat/linker/x86_64/relative_patcher_x86_64.cc index 0b9d07e9cf..629affcb99 100644 --- a/dex2oat/linker/x86_64/relative_patcher_x86_64.cc +++ b/dex2oat/linker/x86_64/relative_patcher_x86_64.cc @@ -16,7 +16,6 @@ #include "linker/x86_64/relative_patcher_x86_64.h" -#include "compiled_method.h" #include "linker/linker_patch.h" namespace art { diff --git a/dex2oat/utils/swap_space.cc b/dex2oat/utils/swap_space.cc new file mode 100644 index 0000000000..6e0773bba4 --- /dev/null +++ b/dex2oat/utils/swap_space.cc @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "swap_space.h" + +#include <sys/mman.h> + +#include <algorithm> +#include <numeric> + +#include "base/bit_utils.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "thread-current-inl.h" + +namespace art { + +// The chunk size by which the swap file is increased and mapped. +static constexpr size_t kMininumMapSize = 16 * MB; + +static constexpr bool kCheckFreeMaps = false; + +template <typename FreeBySizeSet> +static void DumpFreeMap(const FreeBySizeSet& free_by_size) { + size_t last_size = static_cast<size_t>(-1); + for (const auto& entry : free_by_size) { + if (last_size != entry.size) { + last_size = entry.size; + LOG(INFO) << "Size " << last_size; + } + LOG(INFO) << " 0x" << std::hex << entry.free_by_start_entry->Start() + << " size=" << std::dec << entry.free_by_start_entry->size; + } +} + +void SwapSpace::RemoveChunk(FreeBySizeSet::const_iterator free_by_size_pos) { + auto free_by_start_pos = free_by_size_pos->free_by_start_entry; + free_by_size_.erase(free_by_size_pos); + free_by_start_.erase(free_by_start_pos); +} + +inline void SwapSpace::InsertChunk(const SpaceChunk& chunk) { + DCHECK_NE(chunk.size, 0u); + auto insert_result = free_by_start_.insert(chunk); + DCHECK(insert_result.second); + free_by_size_.emplace(chunk.size, insert_result.first); +} + +SwapSpace::SwapSpace(int fd, size_t initial_size) + : fd_(fd), + size_(0), + lock_("SwapSpace lock", static_cast<LockLevel>(LockLevel::kDefaultMutexLevel - 1)) { + // Assume that the file is unlinked. + + InsertChunk(NewFileChunk(initial_size)); +} + +SwapSpace::~SwapSpace() { + // Unmap all mmapped chunks. Nothing should be allocated anymore at + // this point, so there should be only full size chunks in free_by_start_. + for (const SpaceChunk& chunk : free_by_start_) { + if (munmap(chunk.ptr, chunk.size) != 0) { + PLOG(ERROR) << "Failed to unmap swap space chunk at " + << static_cast<const void*>(chunk.ptr) << " size=" << chunk.size; + } + } + // All arenas are backed by the same file. Just close the descriptor. + close(fd_); +} + +template <typename FreeByStartSet, typename FreeBySizeSet> +static size_t CollectFree(const FreeByStartSet& free_by_start, const FreeBySizeSet& free_by_size) { + if (free_by_start.size() != free_by_size.size()) { + LOG(FATAL) << "Size: " << free_by_start.size() << " vs " << free_by_size.size(); + } + + // Calculate over free_by_size. + size_t sum1 = 0; + for (const auto& entry : free_by_size) { + sum1 += entry.free_by_start_entry->size; + } + + // Calculate over free_by_start. + size_t sum2 = 0; + for (const auto& entry : free_by_start) { + sum2 += entry.size; + } + + if (sum1 != sum2) { + LOG(FATAL) << "Sum: " << sum1 << " vs " << sum2; + } + return sum1; +} + +void* SwapSpace::Alloc(size_t size) { + MutexLock lock(Thread::Current(), lock_); + size = RoundUp(size, 8U); + + // Check the free list for something that fits. + // TODO: Smarter implementation. Global biggest chunk, ... + auto it = free_by_start_.empty() + ? free_by_size_.end() + : free_by_size_.lower_bound(FreeBySizeEntry { size, free_by_start_.begin() }); + if (it != free_by_size_.end()) { + SpaceChunk old_chunk = *it->free_by_start_entry; + if (old_chunk.size == size) { + RemoveChunk(it); + } else { + // Avoid deallocating and allocating the std::set<> nodes. + // This would be much simpler if we could use replace() from Boost.Bimap. + + // The free_by_start_ map contains disjoint intervals ordered by the `ptr`. + // Shrinking the interval does not affect the ordering. + it->free_by_start_entry->ptr += size; + it->free_by_start_entry->size -= size; + + auto node = free_by_size_.extract(it); + node.value().size -= size; + free_by_size_.insert(std::move(node)); + } + return old_chunk.ptr; + } else { + // Not a big enough free chunk, need to increase file size. + SpaceChunk new_chunk = NewFileChunk(size); + if (new_chunk.size != size) { + // Insert the remainder. + SpaceChunk remainder = { new_chunk.ptr + size, new_chunk.size - size }; + InsertChunk(remainder); + } + return new_chunk.ptr; + } +} + +SwapSpace::SpaceChunk SwapSpace::NewFileChunk(size_t min_size) { +#if !defined(__APPLE__) + size_t next_part = std::max(RoundUp(min_size, kPageSize), RoundUp(kMininumMapSize, kPageSize)); + int result = TEMP_FAILURE_RETRY(ftruncate64(fd_, size_ + next_part)); + if (result != 0) { + PLOG(FATAL) << "Unable to increase swap file."; + } + uint8_t* ptr = reinterpret_cast<uint8_t*>( + mmap(nullptr, next_part, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, size_)); + if (ptr == MAP_FAILED) { + LOG(ERROR) << "Unable to mmap new swap file chunk."; + LOG(ERROR) << "Current size: " << size_ << " requested: " << next_part << "/" << min_size; + LOG(ERROR) << "Free list:"; + DumpFreeMap(free_by_size_); + LOG(ERROR) << "In free list: " << CollectFree(free_by_start_, free_by_size_); + PLOG(FATAL) << "Unable to mmap new swap file chunk."; + } + size_ += next_part; + SpaceChunk new_chunk = {ptr, next_part}; + return new_chunk; +#else + UNUSED(min_size, kMininumMapSize); + LOG(FATAL) << "No swap file support on the Mac."; + UNREACHABLE(); +#endif +} + +// TODO: Full coalescing. +void SwapSpace::Free(void* ptr, size_t size) { + MutexLock lock(Thread::Current(), lock_); + size = RoundUp(size, 8U); + + size_t free_before = 0; + if (kCheckFreeMaps) { + free_before = CollectFree(free_by_start_, free_by_size_); + } + + SpaceChunk chunk = { reinterpret_cast<uint8_t*>(ptr), size }; + auto it = free_by_start_.lower_bound(chunk); + if (it != free_by_start_.begin()) { + auto prev = it; + --prev; + CHECK_LE(prev->End(), chunk.Start()); + if (prev->End() == chunk.Start()) { + // Merge *prev with this chunk. + chunk.size += prev->size; + chunk.ptr -= prev->size; + auto erase_pos = free_by_size_.find(FreeBySizeEntry { prev->size, prev }); + DCHECK(erase_pos != free_by_size_.end()); + RemoveChunk(erase_pos); + // "prev" is invalidated but "it" remains valid. + } + } + if (it != free_by_start_.end()) { + CHECK_LE(chunk.End(), it->Start()); + if (chunk.End() == it->Start()) { + // Merge *it with this chunk. + chunk.size += it->size; + auto erase_pos = free_by_size_.find(FreeBySizeEntry { it->size, it }); + DCHECK(erase_pos != free_by_size_.end()); + RemoveChunk(erase_pos); + // "it" is invalidated but we don't need it anymore. + } + } + InsertChunk(chunk); + + if (kCheckFreeMaps) { + size_t free_after = CollectFree(free_by_start_, free_by_size_); + + if (free_after != free_before + size) { + DumpFreeMap(free_by_size_); + CHECK_EQ(free_after, free_before + size) << "Should be " << size << " difference from " << free_before; + } + } +} + +} // namespace art diff --git a/dex2oat/utils/swap_space.h b/dex2oat/utils/swap_space.h new file mode 100644 index 0000000000..aba6485c81 --- /dev/null +++ b/dex2oat/utils/swap_space.h @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_DEX2OAT_UTILS_SWAP_SPACE_H_ +#define ART_DEX2OAT_UTILS_SWAP_SPACE_H_ + +#include <stddef.h> +#include <stdint.h> +#include <cstdlib> +#include <list> +#include <set> +#include <vector> + +#include <android-base/logging.h> + +#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" + +namespace art { + +// An arena pool that creates arenas backed by an mmaped file. +class SwapSpace { + public: + SwapSpace(int fd, size_t initial_size); + ~SwapSpace(); + void* Alloc(size_t size) REQUIRES(!lock_); + void Free(void* ptr, size_t size) REQUIRES(!lock_); + + size_t GetSize() { + return size_; + } + + private: + // Chunk of space. + struct SpaceChunk { + // We need mutable members as we keep these objects in a std::set<> (providing only const + // access) but we modify these members while carefully preserving the std::set<> ordering. + mutable uint8_t* ptr; + mutable size_t size; + + uintptr_t Start() const { + return reinterpret_cast<uintptr_t>(ptr); + } + uintptr_t End() const { + return reinterpret_cast<uintptr_t>(ptr) + size; + } + }; + + class SortChunkByPtr { + public: + bool operator()(const SpaceChunk& a, const SpaceChunk& b) const { + return reinterpret_cast<uintptr_t>(a.ptr) < reinterpret_cast<uintptr_t>(b.ptr); + } + }; + + using FreeByStartSet = std::set<SpaceChunk, SortChunkByPtr>; + + // Map size to an iterator to free_by_start_'s entry. + struct FreeBySizeEntry { + FreeBySizeEntry(size_t sz, FreeByStartSet::const_iterator entry) + : size(sz), free_by_start_entry(entry) { } + + // We need mutable members as we keep these objects in a std::set<> (providing only const + // access) but we modify these members while carefully preserving the std::set<> ordering. + mutable size_t size; + mutable FreeByStartSet::const_iterator free_by_start_entry; + }; + struct FreeBySizeComparator { + bool operator()(const FreeBySizeEntry& lhs, const FreeBySizeEntry& rhs) const { + if (lhs.size != rhs.size) { + return lhs.size < rhs.size; + } else { + return lhs.free_by_start_entry->Start() < rhs.free_by_start_entry->Start(); + } + } + }; + using FreeBySizeSet = std::set<FreeBySizeEntry, FreeBySizeComparator>; + + SpaceChunk NewFileChunk(size_t min_size) REQUIRES(lock_); + + void RemoveChunk(FreeBySizeSet::const_iterator free_by_size_pos) REQUIRES(lock_); + void InsertChunk(const SpaceChunk& chunk) REQUIRES(lock_); + + int fd_; + size_t size_; + + // NOTE: Boost.Bimap would be useful for the two following members. + + // Map start of a free chunk to its size. + FreeByStartSet free_by_start_ GUARDED_BY(lock_); + // Free chunks ordered by size. + FreeBySizeSet free_by_size_ GUARDED_BY(lock_); + + mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + DISALLOW_COPY_AND_ASSIGN(SwapSpace); +}; + +template <typename T> class SwapAllocator; + +template <> +class SwapAllocator<void> { + public: + using value_type = void; + using pointer = void*; + using const_pointer = const void*; + + template <typename U> + struct rebind { + using other = SwapAllocator<U>; + }; + + explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {} + + template <typename U> + SwapAllocator(const SwapAllocator<U>& other) + : swap_space_(other.swap_space_) {} + + SwapAllocator(const SwapAllocator& other) = default; + SwapAllocator& operator=(const SwapAllocator& other) = default; + ~SwapAllocator() = default; + + private: + SwapSpace* swap_space_; + + template <typename U> + friend class SwapAllocator; + + template <typename U> + friend bool operator==(const SwapAllocator<U>& lhs, const SwapAllocator<U>& rhs); +}; + +template <typename T> +class SwapAllocator { + public: + using value_type = T; + using pointer = T*; + using reference = T&; + using const_pointer = const T*; + using const_reference = const T&; + using size_type = size_t; + using difference_type = ptrdiff_t; + + template <typename U> + struct rebind { + using other = SwapAllocator<U>; + }; + + explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {} + + template <typename U> + SwapAllocator(const SwapAllocator<U>& other) + : swap_space_(other.swap_space_) {} + + SwapAllocator(const SwapAllocator& other) = default; + SwapAllocator& operator=(const SwapAllocator& other) = default; + ~SwapAllocator() = default; + + size_type max_size() const { + return static_cast<size_type>(-1) / sizeof(T); + } + + pointer address(reference x) const { return &x; } + const_pointer address(const_reference x) const { return &x; } + + pointer allocate(size_type n, SwapAllocator<void>::pointer hint ATTRIBUTE_UNUSED = nullptr) { + DCHECK_LE(n, max_size()); + if (swap_space_ == nullptr) { + T* result = reinterpret_cast<T*>(malloc(n * sizeof(T))); + CHECK_IMPLIES(result == nullptr, n == 0u); // Abort if malloc() fails. + return result; + } else { + return reinterpret_cast<T*>(swap_space_->Alloc(n * sizeof(T))); + } + } + void deallocate(pointer p, size_type n) { + if (swap_space_ == nullptr) { + free(p); + } else { + swap_space_->Free(p, n * sizeof(T)); + } + } + + void construct(pointer p, const_reference val) { + new (static_cast<void*>(p)) value_type(val); + } + template <class U, class... Args> + void construct(U* p, Args&&... args) { + ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...); + } + void destroy(pointer p) { + p->~value_type(); + } + + inline bool operator==(SwapAllocator const& other) { + return swap_space_ == other.swap_space_; + } + inline bool operator!=(SwapAllocator const& other) { + return !operator==(other); + } + + private: + SwapSpace* swap_space_; + + template <typename U> + friend class SwapAllocator; + + template <typename U> + friend bool operator==(const SwapAllocator<U>& lhs, const SwapAllocator<U>& rhs); +}; + +template <typename T> +inline bool operator==(const SwapAllocator<T>& lhs, const SwapAllocator<T>& rhs) { + return lhs.swap_space_ == rhs.swap_space_; +} + +template <typename T> +inline bool operator!=(const SwapAllocator<T>& lhs, const SwapAllocator<T>& rhs) { + return !(lhs == rhs); +} + +template <typename T> +using SwapVector = std::vector<T, SwapAllocator<T>>; +template <typename T, typename Comparator> +using SwapSet = std::set<T, Comparator, SwapAllocator<T>>; + +} // namespace art + +#endif // ART_DEX2OAT_UTILS_SWAP_SPACE_H_ diff --git a/dex2oat/utils/swap_space_test.cc b/dex2oat/utils/swap_space_test.cc new file mode 100644 index 0000000000..eb79cfd13e --- /dev/null +++ b/dex2oat/utils/swap_space_test.cc @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils/swap_space.h" + +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <cstdio> + +#include "gtest/gtest.h" + +#include "base/common_art_test.h" +#include "base/os.h" +#include "base/unix_file/fd_file.h" + +namespace art { + +class SwapSpaceTest : public CommonArtTest {}; + +static void SwapTest(bool use_file) { + ScratchFile scratch; + int fd = scratch.GetFd(); + unlink(scratch.GetFilename().c_str()); + + SwapSpace pool(fd, 1 * MB); + SwapAllocator<void> alloc(use_file ? &pool : nullptr); + + SwapVector<int32_t> v(alloc); + v.reserve(1000000); + for (int32_t i = 0; i < 1000000; ++i) { + v.push_back(i); + EXPECT_EQ(i, v[i]); + } + + SwapVector<int32_t> v2(alloc); + v2.reserve(1000000); + for (int32_t i = 0; i < 1000000; ++i) { + v2.push_back(i); + EXPECT_EQ(i, v2[i]); + } + + SwapVector<int32_t> v3(alloc); + v3.reserve(500000); + for (int32_t i = 0; i < 1000000; ++i) { + v3.push_back(i); + EXPECT_EQ(i, v2[i]); + } + + // Verify contents. + for (int32_t i = 0; i < 1000000; ++i) { + EXPECT_EQ(i, v[i]); + EXPECT_EQ(i, v2[i]); + EXPECT_EQ(i, v3[i]); + } + + scratch.Close(); +} + +TEST_F(SwapSpaceTest, Memory) { + SwapTest(false); +} + +TEST_F(SwapSpaceTest, Swap) { + SwapTest(true); +} + +} // namespace art diff --git a/dex2oat/verifier_deps_test.cc b/dex2oat/verifier_deps_test.cc index 708af04390..00593f5708 100644 --- a/dex2oat/verifier_deps_test.cc +++ b/dex2oat/verifier_deps_test.cc @@ -47,6 +47,7 @@ class VerifierDepsCompilerCallbacks : public CompilerCallbacks { deps_(nullptr) {} void AddUncompilableMethod(MethodReference ref ATTRIBUTE_UNUSED) override {} + void AddUncompilableClass(ClassReference ref ATTRIBUTE_UNUSED) override {} void ClassRejected(ClassReference ref ATTRIBUTE_UNUSED) override {} verifier::VerifierDeps* GetVerifierDeps() const override { return deps_; } @@ -501,6 +502,7 @@ TEST_F(VerifierDepsTest, EncodeDecodeMulti) { std::vector<std::unique_ptr<const DexFile>> first_dex_files = OpenTestDexFiles("VerifierDeps"); std::vector<std::unique_ptr<const DexFile>> second_dex_files = OpenTestDexFiles("MultiDex"); std::vector<const DexFile*> dex_files; + dex_files.reserve(first_dex_files.size() + second_dex_files.size()); for (auto& dex_file : first_dex_files) { dex_files.push_back(dex_file.get()); } @@ -630,12 +632,5 @@ TEST_F(VerifierDepsTest, MultiDexVerification) { ASSERT_FALSE(buffer.empty()); } -TEST_F(VerifierDepsTest, Assignable_Arrays) { - ASSERT_TRUE(TestAssignabilityRecording(/* dst= */ "[LIface;", - /* src= */ "[LMyClassExtendingInterface;")); - ASSERT_FALSE(HasAssignable( - "LIface;", "LMyClassExtendingInterface;")); -} - } // namespace verifier } // namespace art |