summaryrefslogtreecommitdiff
path: root/dex2oat
diff options
context:
space:
mode:
Diffstat (limited to 'dex2oat')
-rw-r--r--dex2oat/Android.bp50
-rw-r--r--dex2oat/art_standalone_dex2oat_cts_tests.xml1
-rw-r--r--dex2oat/art_standalone_dex2oat_tests.xml8
-rw-r--r--dex2oat/common_compiler_driver_test.cc6
-rw-r--r--dex2oat/common_compiler_driver_test.h2
-rw-r--r--dex2oat/dex/quick_compiler_callbacks.cc6
-rw-r--r--dex2oat/dex/quick_compiler_callbacks.h1
-rw-r--r--dex2oat/dex/verification_results.cc76
-rw-r--r--dex2oat/dex/verification_results.h61
-rw-r--r--dex2oat/dex2oat.cc136
-rw-r--r--dex2oat/dex2oat_image_test.cc1
-rw-r--r--dex2oat/dex2oat_options.cc20
-rw-r--r--dex2oat/dex2oat_test.cc969
-rw-r--r--dex2oat/dex2oat_vdex_test.cc86
-rw-r--r--dex2oat/driver/compiled_method-inl.h55
-rw-r--r--dex2oat/driver/compiled_method.cc105
-rw-r--r--dex2oat/driver/compiled_method.h161
-rw-r--r--dex2oat/driver/compiled_method_storage.cc303
-rw-r--r--dex2oat/driver/compiled_method_storage.h144
-rw-r--r--dex2oat/driver/compiled_method_storage_test.cc101
-rw-r--r--dex2oat/driver/compiler_driver.cc184
-rw-r--r--dex2oat/driver/compiler_driver.h6
-rw-r--r--dex2oat/driver/compiler_driver_test.cc24
-rw-r--r--dex2oat/linker/arm/relative_patcher_arm_base.cc9
-rw-r--r--dex2oat/linker/arm/relative_patcher_thumb2.cc2
-rw-r--r--dex2oat/linker/arm/relative_patcher_thumb2_test.cc87
-rw-r--r--dex2oat/linker/arm64/relative_patcher_arm64.cc4
-rw-r--r--dex2oat/linker/arm64/relative_patcher_arm64_test.cc72
-rw-r--r--dex2oat/linker/code_info_table_deduper_test.cc7
-rw-r--r--dex2oat/linker/elf_writer_quick.cc1
-rw-r--r--dex2oat/linker/elf_writer_test.cc2
-rw-r--r--dex2oat/linker/image_test.cc32
-rw-r--r--dex2oat/linker/image_test.h37
-rw-r--r--dex2oat/linker/image_writer.cc510
-rw-r--r--dex2oat/linker/image_writer.h41
-rw-r--r--dex2oat/linker/multi_oat_relative_patcher_test.cc2
-rw-r--r--dex2oat/linker/oat_writer.cc746
-rw-r--r--dex2oat/linker/oat_writer.h26
-rw-r--r--dex2oat/linker/oat_writer_test.cc9
-rw-r--r--dex2oat/linker/relative_patcher_test.h9
-rw-r--r--dex2oat/linker/x86/relative_patcher_x86.cc1
-rw-r--r--dex2oat/linker/x86_64/relative_patcher_x86_64.cc1
-rw-r--r--dex2oat/utils/swap_space.cc223
-rw-r--r--dex2oat/utils/swap_space.h242
-rw-r--r--dex2oat/utils/swap_space_test.cc82
-rw-r--r--dex2oat/verifier_deps_test.cc9
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