summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk172
-rw-r--r--build/Android.common.mk3
-rw-r--r--build/Android.gtest.mk10
-rw-r--r--cmdline/cmdline_parser_test.cc5
-rw-r--r--compiler/driver/compiler_driver.cc6
-rw-r--r--compiler/optimizing/code_generator.cc7
-rw-r--r--compiler/optimizing/nodes.h46
-rw-r--r--compiler/optimizing/side_effects_test.cc1
-rw-r--r--compiler/verifier_deps_test.cc3
-rw-r--r--dex2oat/dex2oat.cc17
-rw-r--r--dex2oat/dex2oat_test.cc104
-rw-r--r--dex2oat/linker/elf_writer_test.cc45
-rw-r--r--dex2oat/linker/oat_writer_test.cc48
-rw-r--r--dexlayout/dexdiag_test.cc10
-rw-r--r--dexlayout/dexlayout.cc14
-rw-r--r--dexlayout/dexlayout_test.cc16
-rw-r--r--libartbase/base/file_utils.cc8
-rw-r--r--libartbase/base/file_utils.h3
-rw-r--r--libartbase/base/fuchsia_compat.h36
-rw-r--r--libartbase/base/mem_map.cc202
-rw-r--r--libartbase/base/mem_map.h65
-rw-r--r--libartbase/base/mem_map_test.cc106
-rw-r--r--libartbase/base/unix_file/fd_file.cc105
-rw-r--r--libartbase/base/unix_file/fd_file.h44
-rw-r--r--libartbase/base/unix_file/fd_file_test.cc5
-rw-r--r--libartbase/base/zip_archive.cc18
-rw-r--r--libartbase/base/zip_archive_test.cc2
-rw-r--r--libdexfile/dex/art_dex_file_loader.cc2
-rw-r--r--libprofile/profile/profile_compilation_info.cc4
-rw-r--r--oatdump/oatdump.cc44
-rw-r--r--openjdkjvmti/OpenjdkJvmTi.cc12
-rw-r--r--openjdkjvmti/ti_heap.cc64
-rw-r--r--openjdkjvmti/ti_heap.h6
-rw-r--r--openjdkjvmti/ti_redefine.cc1
-rw-r--r--runtime/class_linker.cc1
-rw-r--r--runtime/dexopt_test.cc53
-rw-r--r--runtime/dexopt_test.h2
-rw-r--r--runtime/elf_file.cc127
-rw-r--r--runtime/elf_file.h14
-rw-r--r--runtime/elf_file_impl.h20
-rw-r--r--runtime/gc/collector/concurrent_copying.cc26
-rw-r--r--runtime/gc/collector/immune_spaces_test.cc6
-rw-r--r--runtime/gc/heap.cc114
-rw-r--r--runtime/gc/space/image_space.cc387
-rw-r--r--runtime/gc/space/image_space.h7
-rw-r--r--runtime/gc/space/image_space_test.cc12
-rw-r--r--runtime/interpreter/interpreter_intrinsics.cc4
-rw-r--r--runtime/interpreter/shadow_frame.h8
-rw-r--r--runtime/jit/jit_code_cache.cc11
-rw-r--r--runtime/monitor.cc5
-rw-r--r--runtime/oat_file.cc236
-rw-r--r--runtime/oat_file.h8
-rw-r--r--runtime/oat_file_assistant.cc346
-rw-r--r--runtime/oat_file_assistant.h83
-rw-r--r--runtime/oat_file_assistant_test.cc469
-rw-r--r--runtime/oat_file_manager.cc46
-rw-r--r--runtime/oat_file_test.cc28
-rw-r--r--runtime/parsed_options.cc3
-rw-r--r--runtime/runtime.cc2
-rw-r--r--runtime/runtime.h5
-rw-r--r--runtime/runtime_options.def3
-rw-r--r--runtime/trace.cc7
-rw-r--r--runtime/vdex_file.cc5
-rw-r--r--runtime/verifier/method_verifier.cc34
-rw-r--r--runtime/verifier/method_verifier.h19
-rw-r--r--runtime/verifier/method_verifier_test.cc2
-rwxr-xr-xtest/071-dexfile-map-clean/run10
-rw-r--r--test/071-dexfile-map-clean/src/Main.java13
-rw-r--r--test/116-nodex2oat/expected.txt9
-rwxr-xr-xtest/116-nodex2oat/run16
-rw-r--r--test/116-nodex2oat/src/Main.java11
-rw-r--r--test/117-nopatchoat/expected.txt11
-rwxr-xr-xtest/117-nopatchoat/run16
-rw-r--r--test/117-nopatchoat/src/Main.java11
-rw-r--r--test/118-noimage-dex2oat/run4
-rwxr-xr-xtest/134-nodex2oat-nofallback/run2
-rw-r--r--test/134-nodex2oat-nofallback/src/Main.java11
-rwxr-xr-xtest/138-duplicate-classes-check2/run19
-rwxr-xr-xtest/147-stripped-dex-fallback/run2
-rw-r--r--test/305-other-fault-handler/fault_handler.cc1
-rw-r--r--test/510-checker-try-catch/smali/Runtime.smali70
-rw-r--r--test/510-checker-try-catch/src/Main.java119
-rw-r--r--test/527-checker-array-access-split/src/Main.java27
-rwxr-xr-xtest/667-jit-jni-stub/run2
-rw-r--r--test/667-jit-jni-stub/src/Main.java2
-rw-r--r--test/677-fsi/expected.txt1
-rw-r--r--test/677-fsi2/expected.txt3
-rw-r--r--test/677-fsi2/run8
-rw-r--r--test/706-checker-scheduler/src/Main.java8
-rw-r--r--test/906-iterate-heap/iterate_heap.cc16
-rw-r--r--test/906-iterate-heap/src/art/Test906.java39
-rw-r--r--test/common/runtime_state.cc7
-rwxr-xr-xtest/etc/run-test-jar11
-rw-r--r--test/knownfailures.json14
-rwxr-xr-xtest/run-test13
-rw-r--r--test/testrunner/target_config.py2
-rwxr-xr-xtest/testrunner/testrunner.py4
-rw-r--r--tools/art_verifier/art_verifier.cc7
-rw-r--r--tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java45
-rw-r--r--tools/class2greylist/src/com/android/class2greylist/MemberDumpingVisitor.java47
-rw-r--r--tools/field-null-percent/Android.bp56
-rw-r--r--tools/field-null-percent/README.md51
-rw-r--r--tools/field-null-percent/fieldnull.cc218
-rw-r--r--tools/libcore_gcstress_debug_failures.txt14
104 files changed, 2425 insertions, 1822 deletions
diff --git a/Android.mk b/Android.mk
index 19c65a1e67..7852be519f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -41,18 +41,18 @@ endif
.PHONY: clean-oat-target
clean-oat-target:
- adb root
- adb wait-for-device remount
- adb shell rm -rf $(ART_TARGET_NATIVETEST_DIR)
- adb shell rm -rf $(ART_TARGET_TEST_DIR)
- adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*/*
- adb shell rm -rf $(DEXPREOPT_BOOT_JAR_DIR)/$(DEX2OAT_TARGET_ARCH)
- adb shell rm -rf system/app/$(DEX2OAT_TARGET_ARCH)
+ $(ADB) root
+ $(ADB) wait-for-device remount
+ $(ADB) shell rm -rf $(ART_TARGET_NATIVETEST_DIR)
+ $(ADB) shell rm -rf $(ART_TARGET_TEST_DIR)
+ $(ADB) shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*/*
+ $(ADB) shell rm -rf $(DEXPREOPT_BOOT_JAR_DIR)/$(DEX2OAT_TARGET_ARCH)
+ $(ADB) shell rm -rf system/app/$(DEX2OAT_TARGET_ARCH)
ifdef TARGET_2ND_ARCH
- adb shell rm -rf $(DEXPREOPT_BOOT_JAR_DIR)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)
- adb shell rm -rf system/app/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)
+ $(ADB) shell rm -rf $(DEXPREOPT_BOOT_JAR_DIR)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)
+ $(ADB) shell rm -rf system/app/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)
endif
- adb shell rm -rf data/run-test/test-*/dalvik-cache/*
+ $(ADB) shell rm -rf data/run-test/test-*/dalvik-cache/*
########################################################################
# cpplint rules to style check art source files
@@ -92,7 +92,7 @@ endif
# test rules
# All the dependencies that must be built ahead of sync-ing them onto the target device.
-TEST_ART_TARGET_SYNC_DEPS :=
+TEST_ART_TARGET_SYNC_DEPS := $(ADB_EXECUTABLE)
include $(art_path)/build/Android.common_test.mk
include $(art_path)/build/Android.gtest.mk
@@ -100,14 +100,14 @@ include $(art_path)/test/Android.run-test.mk
# Make sure /system is writable on the device.
TEST_ART_ADB_ROOT_AND_REMOUNT := \
- (adb root && \
- adb wait-for-device remount && \
- ((adb shell touch /system/testfile && \
- (adb shell rm /system/testfile || true)) || \
- (adb disable-verity && \
- adb reboot && \
- adb wait-for-device root && \
- adb wait-for-device remount)))
+ ($(ADB) root && \
+ $(ADB) wait-for-device remount && \
+ (($(ADB) shell touch /system/testfile && \
+ ($(ADB) shell rm /system/testfile || true)) || \
+ ($(ADB) disable-verity && \
+ $(ADB) reboot && \
+ $(ADB) wait-for-device root && \
+ $(ADB) wait-for-device remount)))
# Sync test files to the target, depends upon all things that must be pushed to the target.
.PHONY: test-art-target-sync
@@ -121,25 +121,25 @@ ifeq ($(ART_TEST_ANDROID_ROOT),)
ifeq ($(ART_TEST_CHROOT),)
test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS)
$(TEST_ART_ADB_ROOT_AND_REMOUNT)
- adb sync system && adb sync data
+ $(ADB) sync system && $(ADB) sync data
else
# TEST_ART_ADB_ROOT_AND_REMOUNT is not needed here, as we are only
# pushing things to the chroot dir, which is expected to be under
# /data on the device.
test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS)
- adb wait-for-device
- adb push $(PRODUCT_OUT)/system $(ART_TEST_CHROOT)/
- adb push $(PRODUCT_OUT)/data $(ART_TEST_CHROOT)/
+ $(ADB) wait-for-device
+ $(ADB) push $(PRODUCT_OUT)/system $(ART_TEST_CHROOT)/
+ $(ADB) push $(PRODUCT_OUT)/data $(ART_TEST_CHROOT)/
endif
else
test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS)
$(TEST_ART_ADB_ROOT_AND_REMOUNT)
- adb wait-for-device
- adb push $(PRODUCT_OUT)/system $(ART_TEST_CHROOT)$(ART_TEST_ANDROID_ROOT)
+ $(ADB) wait-for-device
+ $(ADB) push $(PRODUCT_OUT)/system $(ART_TEST_CHROOT)$(ART_TEST_ANDROID_ROOT)
# Push the contents of the `data` dir into `$(ART_TEST_CHROOT)/data` on the device (note
# that $(ART_TEST_CHROOT) can be empty). If `$(ART_TEST_CHROOT)/data` already exists on
# the device, it is not overwritten, but its content is updated.
- adb push $(PRODUCT_OUT)/data $(ART_TEST_CHROOT)/
+ $(ADB) push $(PRODUCT_OUT)/data $(ART_TEST_CHROOT)/
endif
endif
@@ -493,90 +493,90 @@ build-art-target-tests: build-art-target $(TEST_ART_RUN_TEST_DEPENDENCIES) $(T
.PHONY: use-art
use-art:
- adb root
- adb wait-for-device shell stop
- adb shell setprop persist.sys.dalvik.vm.lib.2 libart.so
- adb shell start
+ $(ADB) root
+ $(ADB) wait-for-device shell stop
+ $(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libart.so
+ $(ADB) shell start
.PHONY: use-artd
use-artd:
- adb root
- adb wait-for-device shell stop
- adb shell setprop persist.sys.dalvik.vm.lib.2 libartd.so
- adb shell start
+ $(ADB) root
+ $(ADB) wait-for-device shell stop
+ $(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libartd.so
+ $(ADB) shell start
.PHONY: use-dalvik
use-dalvik:
- adb root
- adb wait-for-device shell stop
- adb shell setprop persist.sys.dalvik.vm.lib.2 libdvm.so
- adb shell start
+ $(ADB) root
+ $(ADB) wait-for-device shell stop
+ $(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libdvm.so
+ $(ADB) shell start
.PHONY: use-art-full
use-art-full:
- adb root
- adb wait-for-device shell stop
- adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
- adb shell setprop dalvik.vm.dex2oat-filter \"\"
- adb shell setprop dalvik.vm.image-dex2oat-filter \"\"
- adb shell setprop persist.sys.dalvik.vm.lib.2 libart.so
- adb shell setprop dalvik.vm.usejit false
- adb shell start
+ $(ADB) root
+ $(ADB) wait-for-device shell stop
+ $(ADB) shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
+ $(ADB) shell setprop dalvik.vm.dex2oat-filter \"\"
+ $(ADB) shell setprop dalvik.vm.image-dex2oat-filter \"\"
+ $(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libart.so
+ $(ADB) shell setprop dalvik.vm.usejit false
+ $(ADB) shell start
.PHONY: use-artd-full
use-artd-full:
- adb root
- adb wait-for-device shell stop
- adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
- adb shell setprop dalvik.vm.dex2oat-filter \"\"
- adb shell setprop dalvik.vm.image-dex2oat-filter \"\"
- adb shell setprop persist.sys.dalvik.vm.lib.2 libartd.so
- adb shell setprop dalvik.vm.usejit false
- adb shell start
+ $(ADB) root
+ $(ADB) wait-for-device shell stop
+ $(ADB) shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
+ $(ADB) shell setprop dalvik.vm.dex2oat-filter \"\"
+ $(ADB) shell setprop dalvik.vm.image-dex2oat-filter \"\"
+ $(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libartd.so
+ $(ADB) shell setprop dalvik.vm.usejit false
+ $(ADB) shell start
.PHONY: use-art-jit
use-art-jit:
- adb root
- adb wait-for-device shell stop
- adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
- adb shell setprop dalvik.vm.dex2oat-filter "verify-at-runtime"
- adb shell setprop dalvik.vm.image-dex2oat-filter "verify-at-runtime"
- adb shell setprop persist.sys.dalvik.vm.lib.2 libart.so
- adb shell setprop dalvik.vm.usejit true
- adb shell start
+ $(ADB) root
+ $(ADB) wait-for-device shell stop
+ $(ADB) shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
+ $(ADB) shell setprop dalvik.vm.dex2oat-filter "verify-at-runtime"
+ $(ADB) shell setprop dalvik.vm.image-dex2oat-filter "verify-at-runtime"
+ $(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libart.so
+ $(ADB) shell setprop dalvik.vm.usejit true
+ $(ADB) shell start
.PHONY: use-art-interpret-only
use-art-interpret-only:
- adb root
- adb wait-for-device shell stop
- adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
- adb shell setprop dalvik.vm.dex2oat-filter "interpret-only"
- adb shell setprop dalvik.vm.image-dex2oat-filter "interpret-only"
- adb shell setprop persist.sys.dalvik.vm.lib.2 libart.so
- adb shell setprop dalvik.vm.usejit false
- adb shell start
+ $(ADB) root
+ $(ADB) wait-for-device shell stop
+ $(ADB) shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
+ $(ADB) shell setprop dalvik.vm.dex2oat-filter "interpret-only"
+ $(ADB) shell setprop dalvik.vm.image-dex2oat-filter "interpret-only"
+ $(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libart.so
+ $(ADB) shell setprop dalvik.vm.usejit false
+ $(ADB) shell start
.PHONY: use-artd-interpret-only
use-artd-interpret-only:
- adb root
- adb wait-for-device shell stop
- adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
- adb shell setprop dalvik.vm.dex2oat-filter "interpret-only"
- adb shell setprop dalvik.vm.image-dex2oat-filter "interpret-only"
- adb shell setprop persist.sys.dalvik.vm.lib.2 libartd.so
- adb shell setprop dalvik.vm.usejit false
- adb shell start
+ $(ADB) root
+ $(ADB) wait-for-device shell stop
+ $(ADB) shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
+ $(ADB) shell setprop dalvik.vm.dex2oat-filter "interpret-only"
+ $(ADB) shell setprop dalvik.vm.image-dex2oat-filter "interpret-only"
+ $(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libartd.so
+ $(ADB) shell setprop dalvik.vm.usejit false
+ $(ADB) shell start
.PHONY: use-art-verify-none
use-art-verify-none:
- adb root
- adb wait-for-device shell stop
- adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
- adb shell setprop dalvik.vm.dex2oat-filter "verify-none"
- adb shell setprop dalvik.vm.image-dex2oat-filter "verify-none"
- adb shell setprop persist.sys.dalvik.vm.lib.2 libart.so
- adb shell setprop dalvik.vm.usejit false
- adb shell start
+ $(ADB) root
+ $(ADB) wait-for-device shell stop
+ $(ADB) shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
+ $(ADB) shell setprop dalvik.vm.dex2oat-filter "verify-none"
+ $(ADB) shell setprop dalvik.vm.image-dex2oat-filter "verify-none"
+ $(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libart.so
+ $(ADB) shell setprop dalvik.vm.usejit false
+ $(ADB) shell start
########################################################################
diff --git a/build/Android.common.mk b/build/Android.common.mk
index a6a9f0fc47..316ce646ab 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -92,4 +92,7 @@ else
2ND_ART_HOST_OUT_SHARED_LIBRARIES := $(2ND_HOST_OUT_SHARED_LIBRARIES)
endif
+ADB_EXECUTABLE := $(HOST_OUT_EXECUTABLES)/adb
+ADB := $(ADB_EXECUTABLE)
+
endif # ART_ANDROID_COMMON_MK
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 2c073fec13..20f20c9f7b 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -461,14 +461,14 @@ $$(gtest_rule): PRIVATE_GTEST_WITNESS := $$(gtest_witness)
.PHONY: $$(gtest_rule)
$$(gtest_rule): test-art-target-sync
- $(hide) adb shell touch $$(PRIVATE_GTEST_WITNESS)
- $(hide) adb shell rm $$(PRIVATE_GTEST_WITNESS)
- $(hide) adb shell $$(PRIVATE_MAYBE_CHROOT_COMMAND) chmod 755 $$(PRIVATE_TARGET_EXE)
+ $(hide) $(ADB) shell touch $$(PRIVATE_GTEST_WITNESS)
+ $(hide) $(ADB) shell rm $$(PRIVATE_GTEST_WITNESS)
+ $(hide) $(ADB) shell $$(PRIVATE_MAYBE_CHROOT_COMMAND) chmod 755 $$(PRIVATE_TARGET_EXE)
$(hide) $$(call ART_TEST_SKIP,$$@) && \
- (adb shell "$$(PRIVATE_MAYBE_CHROOT_COMMAND) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \
+ ($(ADB) shell "$$(PRIVATE_MAYBE_CHROOT_COMMAND) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \
ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) $$(PRIVATE_TARGET_EXE) \
&& touch $$(PRIVATE_GTEST_WITNESS)" \
- && (adb pull $$(PRIVATE_GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \
+ && ($(ADB) pull $$(PRIVATE_GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \
|| $$(call ART_TEST_FAILED,$$@))
$(hide) rm -f /tmp/$$@-$$$$PPID
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index a33d53741c..42c6a5ff59 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -558,13 +558,12 @@ TEST_F(CmdlineParserTest, TestIgnoredArguments) {
TEST_F(CmdlineParserTest, MultipleArguments) {
EXPECT_TRUE(IsResultSuccessful(parser_->Parse(
"-help -XX:ForegroundHeapGrowthMultiplier=0.5 "
- "-Xnodex2oat -Xmethod-trace -XX:LargeObjectSpace=map")));
+ "-Xmethod-trace -XX:LargeObjectSpace=map")));
auto&& map = parser_->ReleaseArgumentsMap();
- EXPECT_EQ(5u, map.Size());
+ EXPECT_EQ(4u, map.Size());
EXPECT_KEY_VALUE(map, M::Help, Unit{});
EXPECT_KEY_VALUE(map, M::ForegroundHeapGrowthMultiplier, 0.5);
- EXPECT_KEY_VALUE(map, M::Dex2Oat, false);
EXPECT_KEY_VALUE(map, M::MethodTrace, Unit{});
EXPECT_KEY_VALUE(map, M::LargeObjectSpace, gc::space::LargeObjectSpaceType::kMap);
} // TEST_F
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 21975dee68..f6afe2c958 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1886,7 +1886,9 @@ void CompilerDriver::Verify(jobject jclass_loader,
class VerifyClassVisitor : public CompilationVisitor {
public:
VerifyClassVisitor(const ParallelCompilationManager* manager, verifier::HardFailLogMode log_level)
- : manager_(manager), log_level_(log_level) {}
+ : manager_(manager),
+ log_level_(log_level),
+ sdk_version_(Runtime::Current()->GetTargetSdkVersion()) {}
void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) override {
ScopedTrace trace(__FUNCTION__);
@@ -1923,6 +1925,7 @@ class VerifyClassVisitor : public CompilationVisitor {
Runtime::Current()->GetCompilerCallbacks(),
true /* allow soft failures */,
log_level_,
+ sdk_version_,
&error_msg);
if (failure_kind == verifier::FailureKind::kHardFailure) {
LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(descriptor)
@@ -1995,6 +1998,7 @@ class VerifyClassVisitor : public CompilationVisitor {
private:
const ParallelCompilationManager* const manager_;
const verifier::HardFailLogMode log_level_;
+ const uint32_t sdk_version_;
};
void CompilerDriver::VerifyDexFile(jobject class_loader,
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index a90ff3f885..e84896b113 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -1489,7 +1489,12 @@ void CodeGenerator::ValidateInvokeRuntime(QuickEntrypointEnum entrypoint,
<< " instruction->GetSideEffects().ToString()="
<< instruction->GetSideEffects().ToString();
} else {
- DCHECK(instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()) ||
+ // 'CanTriggerGC' side effect is used to restrict optimization of instructions which depend
+ // on GC (e.g. IntermediateAddress) - to ensure they are not alive across GC points. However
+ // if execution never returns to the compiled code from a GC point this restriction is
+ // unnecessary - in particular for fatal slow paths which might trigger GC.
+ DCHECK((slow_path->IsFatal() && !instruction->GetLocations()->WillCall()) ||
+ instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()) ||
// When (non-Baker) read barriers are enabled, some instructions
// use a slow path to emit a read barrier, which does not trigger
// GC.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 5feffa0511..68f1a2406a 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1626,6 +1626,21 @@ using HConstInputsRef = TransformArrayRef<const HUserRecord<HInstruction*>, HInp
* the same, and any reference read depends on any reference read without
* further regard of its type).
*
+ * kDependsOnGCBit is defined in the following way: instructions with kDependsOnGCBit must not be
+ * alive across the point where garbage collection might happen.
+ *
+ * Note: Instructions with kCanTriggerGCBit do not depend on each other.
+ *
+ * kCanTriggerGCBit must be used for instructions for which GC might happen on the path across
+ * those instructions from the compiler perspective (between this instruction and the next one
+ * in the IR).
+ *
+ * Note: Instructions which can cause GC only on a fatal slow path do not need
+ * kCanTriggerGCBit as the execution never returns to the instruction next to the exceptional
+ * one. However the execution may return to compiled code if there is a catch block in the
+ * current method; for this purpose the TryBoundary exit instruction has kCanTriggerGCBit
+ * set.
+ *
* The internal representation uses 38-bit and is described in the table below.
* The first line indicates the side effect, and for field/array accesses the
* second line indicates the type of the access (in the order of the
@@ -1698,10 +1713,17 @@ class SideEffects : public ValueObject {
return SideEffects(TypeFlag(type, kArrayReadOffset));
}
+ // Returns whether GC might happen across this instruction from the compiler perspective so
+ // the next instruction in the IR would see that.
+ //
+ // See the SideEffect class comments.
static SideEffects CanTriggerGC() {
return SideEffects(1ULL << kCanTriggerGCBit);
}
+ // Returns whether the instruction must not be alive across a GC point.
+ //
+ // See the SideEffect class comments.
static SideEffects DependsOnGC() {
return SideEffects(1ULL << kDependsOnGCBit);
}
@@ -3136,8 +3158,15 @@ class HTryBoundary final : public HExpression<0> {
kLast = kExit
};
+ // SideEffects::CanTriggerGC prevents instructions with SideEffects::DependOnGC to be alive
+ // across the catch block entering edges as GC might happen during throwing an exception.
+ // TryBoundary with BoundaryKind::kExit is conservatively used for that as there is no
+ // HInstruction which a catch block must start from.
explicit HTryBoundary(BoundaryKind kind, uint32_t dex_pc = kNoDexPc)
- : HExpression(kTryBoundary, SideEffects::None(), dex_pc) {
+ : HExpression(kTryBoundary,
+ (kind == BoundaryKind::kExit) ? SideEffects::CanTriggerGC()
+ : SideEffects::None(),
+ dex_pc) {
SetPackedField<BoundaryKindField>(kind);
}
@@ -5163,9 +5192,10 @@ class HAbs final : public HUnaryOperation {
class HDivZeroCheck final : public HExpression<1> {
public:
// `HDivZeroCheck` can trigger GC, as it may call the `ArithmeticException`
- // constructor.
+ // constructor. However it can only do it on a fatal slow path so execution never returns to the
+ // instruction following the current one; thus 'SideEffects::None()' is used.
HDivZeroCheck(HInstruction* value, uint32_t dex_pc)
- : HExpression(kDivZeroCheck, value->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
+ : HExpression(kDivZeroCheck, value->GetType(), SideEffects::None(), dex_pc) {
SetRawInputAt(0, value);
}
@@ -5642,9 +5672,10 @@ static constexpr uint32_t kNoRegNumber = -1;
class HNullCheck final : public HExpression<1> {
public:
// `HNullCheck` can trigger GC, as it may call the `NullPointerException`
- // constructor.
+ // constructor. However it can only do it on a fatal slow path so execution never returns to the
+ // instruction following the current one; thus 'SideEffects::None()' is used.
HNullCheck(HInstruction* value, uint32_t dex_pc)
- : HExpression(kNullCheck, value->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
+ : HExpression(kNullCheck, value->GetType(), SideEffects::None(), dex_pc) {
SetRawInputAt(0, value);
}
@@ -6071,12 +6102,13 @@ class HArrayLength final : public HExpression<1> {
class HBoundsCheck final : public HExpression<2> {
public:
// `HBoundsCheck` can trigger GC, as it may call the `IndexOutOfBoundsException`
- // constructor.
+ // constructor. However it can only do it on a fatal slow path so execution never returns to the
+ // instruction following the current one; thus 'SideEffects::None()' is used.
HBoundsCheck(HInstruction* index,
HInstruction* length,
uint32_t dex_pc,
bool is_string_char_at = false)
- : HExpression(kBoundsCheck, index->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
+ : HExpression(kBoundsCheck, index->GetType(), SideEffects::None(), dex_pc) {
DCHECK_EQ(DataType::Type::kInt32, DataType::Kind(index->GetType()));
SetPackedFlag<kFlagIsStringCharAt>(is_string_char_at);
SetRawInputAt(0, index);
diff --git a/compiler/optimizing/side_effects_test.cc b/compiler/optimizing/side_effects_test.cc
index 97317124ef..4b0be07f3b 100644
--- a/compiler/optimizing/side_effects_test.cc
+++ b/compiler/optimizing/side_effects_test.cc
@@ -202,6 +202,7 @@ TEST(SideEffectsTest, GC) {
EXPECT_TRUE(depends_on_gc.MayDependOn(all_changes));
EXPECT_TRUE(depends_on_gc.Union(can_trigger_gc).MayDependOn(all_changes));
EXPECT_FALSE(can_trigger_gc.MayDependOn(all_changes));
+ EXPECT_FALSE(can_trigger_gc.MayDependOn(can_trigger_gc));
EXPECT_TRUE(all_changes.Includes(can_trigger_gc));
EXPECT_FALSE(all_changes.Includes(depends_on_gc));
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 136066d074..81932a90ce 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -178,7 +178,8 @@ class VerifierDepsTest : public CommonCompilerTest {
true /* allow_soft_failures */,
true /* need_precise_constants */,
false /* verify to dump */,
- true /* allow_thread_suspension */);
+ true /* allow_thread_suspension */,
+ 0 /* api_level */);
verifier.Verify();
soa.Self()->SetVerifierDeps(nullptr);
has_failures = verifier.HasFailures();
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 5655b3c91d..9406c62a0e 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1348,12 +1348,12 @@ class Dex2Oat final {
}
}
} else {
- std::unique_ptr<File> oat_file(new File(oat_fd_, oat_location_, /* check_usage */ true));
- if (oat_file == nullptr) {
+ std::unique_ptr<File> oat_file(
+ new File(DupCloexec(oat_fd_), oat_location_, /* check_usage */ true));
+ if (!oat_file->IsOpened()) {
PLOG(ERROR) << "Failed to create oat file: " << oat_location_;
return false;
}
- oat_file->DisableAutoClose();
if (oat_file->SetLength(0) != 0) {
PLOG(WARNING) << "Truncating oat file " << oat_location_ << " failed.";
oat_file->Erase();
@@ -1385,12 +1385,12 @@ class Dex2Oat final {
DCHECK_NE(output_vdex_fd_, -1);
std::string vdex_location = ReplaceFileExtension(oat_location_, "vdex");
- std::unique_ptr<File> vdex_file(new File(output_vdex_fd_, vdex_location, /* check_usage */ true));
- if (vdex_file == nullptr) {
+ std::unique_ptr<File> vdex_file(new File(
+ DupCloexec(output_vdex_fd_), vdex_location, /* check_usage */ true));
+ if (!vdex_file->IsOpened()) {
PLOG(ERROR) << "Failed to create vdex file: " << vdex_location;
return false;
}
- vdex_file->DisableAutoClose();
if (input_vdex_file_ != nullptr && output_vdex_fd_ == input_vdex_fd_) {
update_input_vdex_ = true;
} else {
@@ -1472,10 +1472,7 @@ class Dex2Oat final {
PLOG(ERROR) << "Failed to create swap file: " << swap_file_name_;
return false;
}
- swap_fd_ = swap_file->Fd();
- swap_file->MarkUnchecked(); // We don't we to track this, it will be unlinked immediately.
- swap_file->DisableAutoClose(); // We'll handle it ourselves, the File object will be
- // released immediately.
+ swap_fd_ = swap_file->Release();
unlink(swap_file_name_.c_str());
}
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index b2fe441582..a1fed5f6d9 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -139,11 +139,11 @@ class Dex2oatTest : public Dex2oatEnvironmentTest {
std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
odex_location.c_str(),
odex_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
dex_location.c_str(),
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
@@ -159,11 +159,11 @@ class Dex2oatTest : public Dex2oatEnvironmentTest {
std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
odex_location.c_str(),
odex_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
dex_location.c_str(),
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(odex_file.get() == nullptr);
}
@@ -516,11 +516,11 @@ class Dex2oatVeryLargeTest : public Dex2oatTest {
std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
odex_location.c_str(),
odex_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
dex_location.c_str(),
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
EXPECT_GT(app_image_file.length(), 0u);
@@ -787,11 +787,11 @@ class Dex2oatLayoutTest : public Dex2oatTest {
std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
odex_location.c_str(),
odex_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
dex_location.c_str(),
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
@@ -949,11 +949,11 @@ class Dex2oatUnquickenTest : public Dex2oatTest {
std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
odex_location.c_str(),
odex_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
dex_location.c_str(),
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
ASSERT_GE(odex_file->GetOatDexFiles().size(), 1u);
@@ -1329,11 +1329,11 @@ TEST_F(Dex2oatTest, LayoutSections) {
std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
oat_filename.c_str(),
oat_filename.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
dex->GetLocation().c_str(),
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(odex_file != nullptr);
std::vector<const OatDexFile*> oat_dex_files = odex_file->GetOatDexFiles();
@@ -1439,11 +1439,11 @@ TEST_F(Dex2oatTest, GenerateCompactDex) {
std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
oat_filename.c_str(),
oat_filename.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
dex_location.c_str(),
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(odex_file != nullptr);
std::vector<const OatDexFile*> oat_dex_files = odex_file->GetOatDexFiles();
@@ -1684,11 +1684,11 @@ TEST_F(Dex2oatTest, CompactDexGenerationFailure) {
std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
oat_filename.c_str(),
oat_filename.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
temp_dex.GetFilename().c_str(),
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(odex_file != nullptr);
std::vector<const OatDexFile*> oat_dex_files = odex_file->GetOatDexFiles();
@@ -1705,7 +1705,7 @@ TEST_F(Dex2oatTest, CompactDexGenerationFailureMultiDex) {
// Create a multidex file with only one dex that gets rejected for cdex conversion.
ScratchFile apk_file;
{
- FILE* file = fdopen(apk_file.GetFd(), "w+b");
+ FILE* file = fdopen(dup(apk_file.GetFd()), "w+b");
ZipWriter writer(file);
// Add vdex to zip.
writer.StartEntry("classes.dex", ZipWriter::kCompress);
@@ -1762,11 +1762,11 @@ TEST_F(Dex2oatTest, VerifyCompilationReason) {
std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
odex_location.c_str(),
odex_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
dex_location.c_str(),
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(odex_file != nullptr);
ASSERT_STREQ("install", odex_file->GetCompilationReason());
@@ -1788,11 +1788,11 @@ TEST_F(Dex2oatTest, VerifyNoCompilationReason) {
std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
odex_location.c_str(),
odex_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
dex_location.c_str(),
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(odex_file != nullptr);
ASSERT_EQ(nullptr, odex_file->GetCompilationReason());
@@ -1826,11 +1826,11 @@ TEST_F(Dex2oatTest, DontExtract) {
std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
odex_location.c_str(),
odex_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/ false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
dex_location.c_str(),
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(odex_file != nullptr) << dex_location;
std::vector<const OatDexFile*> oat_dex_files = odex_file->GetOatDexFiles();
@@ -1847,7 +1847,7 @@ TEST_F(Dex2oatTest, DontExtract) {
std::unique_ptr<File> vdex_file(OS::OpenFileForReading(vdex_location.c_str()));
ASSERT_TRUE(vdex_file != nullptr);
ASSERT_GT(vdex_file->GetLength(), 0u);
- FILE* file = fdopen(dm_file.GetFd(), "w+b");
+ FILE* file = fdopen(dup(dm_file.GetFd()), "w+b");
ZipWriter writer(file);
auto write_all_bytes = [&](File* file) {
std::unique_ptr<uint8_t[]> bytes(new uint8_t[file->GetLength()]);
@@ -1973,7 +1973,7 @@ TEST_F(Dex2oatTest, QuickenedInput) {
TEST_F(Dex2oatTest, CompactDexInvalidSource) {
ScratchFile invalid_dex;
{
- FILE* file = fdopen(invalid_dex.GetFd(), "w+b");
+ FILE* file = fdopen(dup(invalid_dex.GetFd()), "w+b");
ZipWriter writer(file);
writer.StartEntry("classes.dex", ZipWriter::kAlign32);
DexFile::Header header = {};
@@ -2015,7 +2015,7 @@ TEST_F(Dex2oatTest, CompactDexInZip) {
// Create a zip containing the invalid dex.
ScratchFile invalid_dex_zip;
{
- FILE* file = fdopen(invalid_dex_zip.GetFd(), "w+b");
+ FILE* file = fdopen(dup(invalid_dex_zip.GetFd()), "w+b");
ZipWriter writer(file);
writer.StartEntry("classes.dex", ZipWriter::kCompress);
ASSERT_GE(writer.WriteBytes(&header, sizeof(header)), 0);
@@ -2065,11 +2065,11 @@ TEST_F(Dex2oatTest, AppImageNoProfile) {
std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
odex_location.c_str(),
odex_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
odex_location.c_str(),
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(odex_file != nullptr);
ImageHeader header = {};
diff --git a/dex2oat/linker/elf_writer_test.cc b/dex2oat/linker/elf_writer_test.cc
index b2be003b5d..40495f33ee 100644
--- a/dex2oat/linker/elf_writer_test.cc
+++ b/dex2oat/linker/elf_writer_test.cc
@@ -14,9 +14,12 @@
* limitations under the License.
*/
+#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"
#include "base/utils.h"
#include "common_compiler_test.h"
@@ -65,8 +68,8 @@ TEST_F(ElfWriterTest, dlsym) {
{
std::string error_msg;
std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(),
- false,
- false,
+ /* writable */ false,
+ /* program_header_only */ false,
/*low_4gb*/false,
&error_msg));
CHECK(ef.get() != nullptr) << error_msg;
@@ -77,9 +80,9 @@ TEST_F(ElfWriterTest, dlsym) {
{
std::string error_msg;
std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(),
- false,
- false,
- /*low_4gb*/false,
+ /* writable */ false,
+ /* program_header_only */ false,
+ /* low_4gb */ false,
&error_msg));
CHECK(ef.get() != nullptr) << error_msg;
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", true);
@@ -87,16 +90,28 @@ TEST_F(ElfWriterTest, dlsym) {
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatlastword, "oatlastword", true);
}
{
- uint8_t* base = reinterpret_cast<uint8_t*>(ART_BASE_ADDRESS);
std::string error_msg;
std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(),
- false,
- true,
- /*low_4gb*/false,
- &error_msg,
- base));
+ /* writable */ false,
+ /* program_header_only */ true,
+ /* low_4gb */ false,
+ &error_msg));
CHECK(ef.get() != nullptr) << error_msg;
- CHECK(ef->Load(file.get(), false, /*low_4gb*/false, &error_msg)) << error_msg;
+ size_t size;
+ bool success = ef->GetLoadedSize(&size, &error_msg);
+ CHECK(success) << error_msg;
+ MemMap reservation = MemMap::MapAnonymous("ElfWriterTest#dlsym reservation",
+ /* addr */ nullptr,
+ RoundUp(size, kPageSize),
+ PROT_NONE,
+ /* low_4gb */ true,
+ &error_msg);
+ CHECK(reservation.IsValid()) << error_msg;
+ uint8_t* base = reservation.Begin();
+ success =
+ ef->Load(file.get(), /* executable */ false, /* low_4gb */ false, &reservation, &error_msg);
+ CHECK(success) << error_msg;
+ CHECK(!reservation.IsValid());
EXPECT_EQ(reinterpret_cast<uintptr_t>(dl_oatdata) + reinterpret_cast<uintptr_t>(base),
reinterpret_cast<uintptr_t>(ef->FindDynamicSymbolAddress("oatdata")));
EXPECT_EQ(reinterpret_cast<uintptr_t>(dl_oatexec) + reinterpret_cast<uintptr_t>(base),
@@ -116,9 +131,9 @@ TEST_F(ElfWriterTest, CheckBuildIdPresent) {
{
std::string error_msg;
std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(),
- false,
- false,
- /*low_4gb*/false,
+ /* writable */ false,
+ /* program_header_only */ false,
+ /* low_4gb */ false,
&error_msg));
CHECK(ef.get() != nullptr) << error_msg;
EXPECT_TRUE(ef->HasSection(".note.gnu.build-id"));
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 0264b09eda..bd09f239ba 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -406,11 +406,11 @@ TEST_F(OatTest, WriteRead) {
std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
tmp_oat.GetFilename(),
tmp_oat.GetFilename(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/true,
- nullptr,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ true,
+ /* abs_dex_location */ nullptr,
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
const OatHeader& oat_header = oat_file->GetOatHeader();
@@ -529,11 +529,11 @@ TEST_F(OatTest, EmptyTextSection) {
std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
tmp_oat.GetFilename(),
tmp_oat.GetFilename(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
- nullptr,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
+ /* abs_dex_location */ nullptr,
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(oat_file != nullptr);
EXPECT_LT(static_cast<size_t>(oat_file->Size()),
@@ -607,11 +607,11 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) {
std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(/* zip_fd */ -1,
tmp_oat.GetFilename(),
tmp_oat.GetFilename(),
- nullptr,
- nullptr,
- false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
low_4gb,
- nullptr,
+ /* abs_dex_location */ nullptr,
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(opened_oat_file != nullptr) << error_msg;
if (low_4gb) {
@@ -738,11 +738,11 @@ void OatTest::TestZipFileInput(bool verify) {
std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(/* zip_fd */ -1,
tmp_oat.GetFilename(),
tmp_oat.GetFilename(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
- nullptr,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
+ /* abs_dex_location */ nullptr,
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(opened_oat_file != nullptr) << error_msg;
ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
@@ -788,11 +788,11 @@ void OatTest::TestZipFileInput(bool verify) {
std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(/* zip_fd */ -1,
tmp_oat.GetFilename(),
tmp_oat.GetFilename(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
- nullptr,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
+ /* abs_dex_location */ nullptr,
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(opened_oat_file != nullptr) << error_msg;
ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
diff --git a/dexlayout/dexdiag_test.cc b/dexlayout/dexdiag_test.cc
index 53145c22fa..60dd7e42a4 100644
--- a/dexlayout/dexdiag_test.cc
+++ b/dexlayout/dexdiag_test.cc
@@ -71,11 +71,11 @@ class DexDiagTest : public CommonRuntimeTest {
std::unique_ptr<OatFile> oat(OatFile::Open(/* zip_fd */ -1,
oat_location.c_str(),
oat_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
- nullptr,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
+ /* abs_dex_location */ nullptr,
+ /* reservation */ nullptr,
&error_msg));
EXPECT_TRUE(oat != nullptr) << error_msg;
return oat;
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index bd7a301a56..52d355b570 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -1794,14 +1794,20 @@ bool DexLayout::OutputDexFile(const DexFile* input_dex_file,
// If options_.output_dex_directory_ is non null, we are outputting to a file.
if (options_.output_dex_directory_ != nullptr) {
std::string output_location(options_.output_dex_directory_);
- size_t last_slash = dex_file_location.rfind('/');
+ const size_t last_slash = dex_file_location.rfind('/');
std::string dex_file_directory = dex_file_location.substr(0, last_slash + 1);
if (output_location == dex_file_directory) {
output_location = dex_file_location + ".new";
- } else if (last_slash != std::string::npos) {
- output_location += dex_file_location.substr(last_slash);
} else {
- output_location += "/" + dex_file_location + ".new";
+ if (!output_location.empty() && output_location.back() != '/') {
+ output_location += "/";
+ }
+ const size_t separator = dex_file_location.rfind('!');
+ if (separator != std::string::npos) {
+ output_location += dex_file_location.substr(separator + 1);
+ } else {
+ output_location += "classes.dex";
+ }
}
new_file.reset(OS::CreateEmptyFile(output_location.c_str()));
if (new_file == nullptr) {
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index a20930b28b..187c68790a 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -267,7 +267,7 @@ class DexLayoutTest : public CommonRuntimeTest {
ScratchFile dexlayout_output;
const std::string& dexlayout_filename = dexlayout_output.GetFilename();
- for (const std::string &dex_file : GetLibCoreDexFileNames()) {
+ for (const std::string& dex_file : GetLibCoreDexFileNames()) {
std::vector<std::string> dexdump_exec_argv =
{ dexdump, "-d", "-f", "-h", "-l", "plain", "-o", dexdump_filename, dex_file };
std::vector<std::string> dexlayout_args =
@@ -293,31 +293,33 @@ class DexLayoutTest : public CommonRuntimeTest {
const std::string& tmp_name = tmp_file.GetFilename();
size_t tmp_last_slash = tmp_name.rfind('/');
std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
+ std::string unzip_dir = tmp_dir + "unzip/";
- for (const std::string &dex_file : GetLibCoreDexFileNames()) {
+ for (const std::string& dex_file : GetLibCoreDexFileNames()) {
std::vector<std::string> dexlayout_args =
{ "-w", tmp_dir, "-o", tmp_name, dex_file };
if (!DexLayoutExec(dexlayout_args, error_msg, /*pass_default_cdex_option*/ false)) {
return false;
}
- size_t dex_file_last_slash = dex_file.rfind('/');
- std::string dex_file_name = dex_file.substr(dex_file_last_slash + 1);
+ std::string dex_file_name = "classes.dex";
std::vector<std::string> unzip_exec_argv =
- { "/usr/bin/unzip", dex_file, "classes.dex", "-d", tmp_dir};
+ { "/usr/bin/unzip", dex_file, "classes.dex", "-d", unzip_dir};
if (!::art::Exec(unzip_exec_argv, error_msg)) {
return false;
}
std::vector<std::string> diff_exec_argv =
- { "/usr/bin/diff", tmp_dir + "classes.dex" , tmp_dir + dex_file_name };
+ { "/usr/bin/diff", tmp_dir + "classes.dex" , unzip_dir + dex_file_name };
if (!::art::Exec(diff_exec_argv, error_msg)) {
return false;
}
- if (!UnlinkFile(tmp_dir + "classes.dex")) {
+ if (!UnlinkFile(unzip_dir + "classes.dex")) {
return false;
}
if (!UnlinkFile(tmp_dir + dex_file_name)) {
return false;
}
+ // Remove the unzip temp directory so that unlinking android_data doesn't fail.
+ EXPECT_EQ(rmdir(unzip_dir.c_str()), 0);
}
return true;
}
diff --git a/libartbase/base/file_utils.cc b/libartbase/base/file_utils.cc
index a63f326d5a..1d106b2cdb 100644
--- a/libartbase/base/file_utils.cc
+++ b/libartbase/base/file_utils.cc
@@ -279,4 +279,12 @@ bool LocationIsOnSystemFramework(const char* full_path) {
return android::base::StartsWith(full_path, framework_path);
}
+int DupCloexec(int fd) {
+#if defined(__linux__)
+ return fcntl(fd, F_DUPFD_CLOEXEC, 0);
+#else
+ return dup(fd);
+#endif
+}
+
} // namespace art
diff --git a/libartbase/base/file_utils.h b/libartbase/base/file_utils.h
index 063393bd3b..c249bccc3c 100644
--- a/libartbase/base/file_utils.h
+++ b/libartbase/base/file_utils.h
@@ -78,6 +78,9 @@ bool LocationIsOnSystem(const char* location);
// Return whether the location is on system/framework (i.e. android_root/framework).
bool LocationIsOnSystemFramework(const char* location);
+// dup(2), except setting the O_CLOEXEC flag atomically, when possible.
+int DupCloexec(int fd);
+
} // namespace art
#endif // ART_LIBARTBASE_BASE_FILE_UTILS_H_
diff --git a/libartbase/base/fuchsia_compat.h b/libartbase/base/fuchsia_compat.h
deleted file mode 100644
index 018bac0528..0000000000
--- a/libartbase/base/fuchsia_compat.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2018 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_LIBARTBASE_BASE_FUCHSIA_COMPAT_H_
-#define ART_LIBARTBASE_BASE_FUCHSIA_COMPAT_H_
-
-// stubs for features lacking in Fuchsia
-
-struct rlimit {
- int rlim_cur;
-};
-
-#define RLIMIT_FSIZE (1)
-#define RLIM_INFINITY (-1)
-static int getrlimit(int resource, struct rlimit *rlim) {
- LOG(FATAL) << "getrlimit not available for Fuchsia";
-}
-
-static int ashmem_create_region(const char *name, size_t size) {
- LOG(FATAL) << "ashmem_create_region not available for Fuchsia";
-}
-
-#endif // ART_LIBARTBASE_BASE_FUCHSIA_COMPAT_H_
diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc
index d9760bd574..1bf553d293 100644
--- a/libartbase/base/mem_map.cc
+++ b/libartbase/base/mem_map.cc
@@ -23,6 +23,10 @@
#include <sys/resource.h>
#endif
+#if defined(__linux__)
+#include <sys/prctl.h>
+#endif
+
#include <map>
#include <memory>
#include <sstream>
@@ -30,12 +34,6 @@
#include "android-base/stringprintf.h"
#include "android-base/unique_fd.h"
-#if !defined(__Fuchsia__)
-#include "cutils/ashmem.h"
-#else
-#include "fuchsia_compat.h"
-#endif
-
#include "allocator.h"
#include "bit_utils.h"
#include "globals.h"
@@ -61,6 +59,9 @@ using Maps = AllocationTrackingMultiMap<void*, MemMap*, kAllocatorTagMaps>;
// All the non-empty MemMaps. Use a multimap as we do a reserve-and-divide (eg ElfMap::Load()).
static Maps* gMaps GUARDED_BY(MemMap::GetMemMapsLock()) = nullptr;
+// A map containing unique strings used for indentifying anonymous mappings
+static std::map<std::string, int> debugStrMap GUARDED_BY(MemMap::GetMemMapsLock());
+
// Retrieve iterator to a `gMaps` entry that is known to exist.
Maps::iterator GetGMapsEntry(const MemMap& map) REQUIRES(MemMap::GetMemMapsLock()) {
DCHECK(map.IsValid());
@@ -226,6 +227,33 @@ bool MemMap::CheckMapRequest(uint8_t* expected_ptr, void* actual_ptr, size_t byt
return false;
}
+bool MemMap::CheckReservation(uint8_t* expected_ptr,
+ size_t byte_count,
+ const char* name,
+ const MemMap& reservation,
+ /*out*/std::string* error_msg) {
+ if (!reservation.IsValid()) {
+ *error_msg = StringPrintf("Invalid reservation for %s", name);
+ return false;
+ }
+ DCHECK_ALIGNED(reservation.Begin(), kPageSize);
+ if (reservation.Begin() != expected_ptr) {
+ *error_msg = StringPrintf("Bad image reservation start for %s: %p instead of %p",
+ name,
+ reservation.Begin(),
+ expected_ptr);
+ return false;
+ }
+ if (byte_count > reservation.Size()) {
+ *error_msg = StringPrintf("Insufficient reservation, required %zu, available %zu",
+ byte_count,
+ reservation.Size());
+ return false;
+ }
+ return true;
+}
+
+
#if USE_ART_LOW_4G_ALLOCATOR
void* MemMap::TryMemMapLow4GB(void* ptr,
size_t page_aligned_byte_count,
@@ -246,18 +274,45 @@ void* MemMap::TryMemMapLow4GB(void* ptr,
}
#endif
+void MemMap::SetDebugName(void* map_ptr, const char* name, size_t size) {
+ // Debug naming is only used for Android target builds. For Linux targets,
+ // we'll still call prctl but it wont do anything till we upstream the prctl.
+ if (kIsTargetFuchsia || !kIsTargetBuild) {
+ return;
+ }
+
+ // lock as std::map is not thread-safe
+ std::lock_guard<std::mutex> mu(*mem_maps_lock_);
+
+ std::string debug_friendly_name("dalvik-");
+ debug_friendly_name += name;
+ auto it = debugStrMap.find(debug_friendly_name);
+
+ if (it == debugStrMap.end()) {
+ it = debugStrMap.insert(std::make_pair(std::move(debug_friendly_name), 1)).first;
+ }
+
+ DCHECK(it != debugStrMap.end());
+#if defined(PR_SET_VMA) && defined(__linux__)
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_ptr, size, it->first.c_str());
+#else
+ // Prevent variable unused compiler errors.
+ UNUSED(map_ptr, size);
+#endif
+}
+
MemMap MemMap::MapAnonymous(const char* name,
uint8_t* addr,
size_t byte_count,
int prot,
bool low_4gb,
bool reuse,
- std::string* error_msg,
- bool use_ashmem) {
+ /*inout*/MemMap* reservation,
+ /*out*/std::string* error_msg,
+ bool use_debug_name) {
#ifndef __LP64__
UNUSED(low_4gb);
#endif
- use_ashmem = use_ashmem && !kIsTargetLinux && !kIsTargetFuchsia;
if (byte_count == 0) {
*error_msg = "Empty MemMap requested.";
return Invalid();
@@ -269,46 +324,20 @@ MemMap MemMap::MapAnonymous(const char* name,
// reuse means it is okay that it overlaps an existing page mapping.
// Only use this if you actually made the page reservation yourself.
CHECK(addr != nullptr);
+ DCHECK(reservation == nullptr);
DCHECK(ContainedWithinExistingMap(addr, byte_count, error_msg)) << *error_msg;
flags |= MAP_FIXED;
- }
-
- if (use_ashmem) {
- if (!kIsTargetBuild) {
- // When not on Android (either host or assuming a linux target) ashmem is faked using
- // files in /tmp. Ensure that such files won't fail due to ulimit restrictions. If they
- // will then use a regular mmap.
- struct rlimit rlimit_fsize;
- CHECK_EQ(getrlimit(RLIMIT_FSIZE, &rlimit_fsize), 0);
- use_ashmem = (rlimit_fsize.rlim_cur == RLIM_INFINITY) ||
- (page_aligned_byte_count < rlimit_fsize.rlim_cur);
+ } else if (reservation != nullptr) {
+ CHECK(addr != nullptr);
+ if (!CheckReservation(addr, byte_count, name, *reservation, error_msg)) {
+ return MemMap::Invalid();
}
+ flags |= MAP_FIXED;
}
unique_fd fd;
-
- if (use_ashmem) {
- // android_os_Debug.cpp read_mapinfo assumes all ashmem regions associated with the VM are
- // prefixed "dalvik-".
- std::string debug_friendly_name("dalvik-");
- debug_friendly_name += name;
- fd.reset(ashmem_create_region(debug_friendly_name.c_str(), page_aligned_byte_count));
-
- if (fd.get() == -1) {
- // We failed to create the ashmem region. Print a warning, but continue
- // anyway by creating a true anonymous mmap with an fd of -1. It is
- // better to use an unlabelled anonymous map than to fail to create a
- // map at all.
- PLOG(WARNING) << "ashmem_create_region failed for '" << name << "'";
- } else {
- // We succeeded in creating the ashmem region. Use the created ashmem
- // region as backing for the mmap.
- flags &= ~MAP_ANONYMOUS;
- }
- }
-
// We need to store and potentially set an error number for pretty printing of errors
int saved_errno = 0;
@@ -341,6 +370,16 @@ MemMap MemMap::MapAnonymous(const char* name,
if (!CheckMapRequest(addr, actual, page_aligned_byte_count, error_msg)) {
return Invalid();
}
+
+ if (use_debug_name) {
+ SetDebugName(actual, name, page_aligned_byte_count);
+ }
+
+ if (reservation != nullptr) {
+ // Re-mapping was successful, transfer the ownership of the memory to the new MemMap.
+ DCHECK_EQ(actual, reservation->Begin());
+ reservation->ReleaseReservedMemory(byte_count);
+ }
return MemMap(name,
reinterpret_cast<uint8_t*>(actual),
byte_count,
@@ -446,22 +485,30 @@ MemMap MemMap::MapFileAtAddress(uint8_t* expected_ptr,
int fd,
off_t start,
bool low_4gb,
- bool reuse,
const char* filename,
- std::string* error_msg) {
+ bool reuse,
+ /*inout*/MemMap* reservation,
+ /*out*/std::string* error_msg) {
CHECK_NE(0, prot);
CHECK_NE(0, flags & (MAP_SHARED | MAP_PRIVATE));
- // Note that we do not allow MAP_FIXED unless reuse == true, i.e we
- // expect his mapping to be contained within an existing map.
+ // Note that we do not allow MAP_FIXED unless reuse == true or we have an existing
+ // reservation, i.e we expect this mapping to be contained within an existing map.
if (reuse) {
// reuse means it is okay that it overlaps an existing page mapping.
// Only use this if you actually made the page reservation yourself.
CHECK(expected_ptr != nullptr);
+ DCHECK(reservation == nullptr);
DCHECK(error_msg != nullptr);
DCHECK(ContainedWithinExistingMap(expected_ptr, byte_count, error_msg))
<< ((error_msg != nullptr) ? *error_msg : std::string());
flags |= MAP_FIXED;
+ } else if (reservation != nullptr) {
+ DCHECK(error_msg != nullptr);
+ if (!CheckReservation(expected_ptr, byte_count, filename, *reservation, error_msg)) {
+ return Invalid();
+ }
+ flags |= MAP_FIXED;
} else {
CHECK_EQ(0, flags & MAP_FIXED);
// Don't bother checking for an overlapping region here. We'll
@@ -523,6 +570,11 @@ MemMap MemMap::MapFileAtAddress(uint8_t* expected_ptr,
page_aligned_byte_count -= redzone_size;
}
+ if (reservation != nullptr) {
+ // Re-mapping was successful, transfer the ownership of the memory to the new MemMap.
+ DCHECK_EQ(actual, reservation->Begin());
+ reservation->ReleaseReservedMemory(byte_count);
+ }
return MemMap(filename,
actual + page_offset,
byte_count,
@@ -639,8 +691,7 @@ MemMap MemMap::RemapAtEnd(uint8_t* new_end,
const char* tail_name,
int tail_prot,
std::string* error_msg,
- bool use_ashmem) {
- use_ashmem = use_ashmem && !kIsTargetLinux && !kIsTargetFuchsia;
+ bool use_debug_name) {
DCHECK_GE(new_end, Begin());
DCHECK_LE(new_end, End());
DCHECK_LE(begin_ + size_, reinterpret_cast<uint8_t*>(base_begin_) + base_size_);
@@ -666,19 +717,6 @@ MemMap MemMap::RemapAtEnd(uint8_t* new_end,
unique_fd fd;
int flags = MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS;
- if (use_ashmem) {
- // android_os_Debug.cpp read_mapinfo assumes all ashmem regions associated with the VM are
- // prefixed "dalvik-".
- std::string debug_friendly_name("dalvik-");
- debug_friendly_name += tail_name;
- fd.reset(ashmem_create_region(debug_friendly_name.c_str(), tail_base_size));
- flags = MAP_PRIVATE | MAP_FIXED;
- if (fd.get() == -1) {
- *error_msg = StringPrintf("ashmem_create_region failed for '%s': %s",
- tail_name, strerror(errno));
- return Invalid();
- }
- }
MEMORY_TOOL_MAKE_UNDEFINED(tail_base_begin, tail_base_size);
// Note: Do not explicitly unmap the tail region, mmap() with MAP_FIXED automatically
@@ -703,12 +741,56 @@ MemMap MemMap::RemapAtEnd(uint8_t* new_end,
auto it = GetGMapsEntry(*this);
gMaps->erase(it);
}
+
+ if (use_debug_name) {
+ SetDebugName(actual, tail_name, tail_base_size);
+ }
+
size_ = new_size;
base_size_ = new_base_size;
// Return the new mapping.
return MemMap(tail_name, actual, tail_size, actual, tail_base_size, tail_prot, false);
}
+MemMap MemMap::TakeReservedMemory(size_t byte_count) {
+ uint8_t* begin = Begin();
+ ReleaseReservedMemory(byte_count); // Performs necessary DCHECK()s on this reservation.
+ size_t base_size = RoundUp(byte_count, kPageSize);
+ return MemMap(name_, begin, byte_count, begin, base_size, prot_, /* reuse */ false);
+}
+
+void MemMap::ReleaseReservedMemory(size_t byte_count) {
+ // Check the reservation mapping.
+ DCHECK(IsValid());
+ DCHECK(!reuse_);
+ DCHECK(!already_unmapped_);
+ DCHECK_EQ(redzone_size_, 0u);
+ DCHECK_EQ(begin_, base_begin_);
+ DCHECK_EQ(size_, base_size_);
+ DCHECK_ALIGNED(begin_, kPageSize);
+ DCHECK_ALIGNED(size_, kPageSize);
+
+ // Check and round up the `byte_count`.
+ DCHECK_NE(byte_count, 0u);
+ DCHECK_LE(byte_count, size_);
+ byte_count = RoundUp(byte_count, kPageSize);
+
+ if (byte_count == size_) {
+ Invalidate();
+ } else {
+ // Shrink the reservation MemMap and update its `gMaps` entry.
+ std::lock_guard<std::mutex> mu(*mem_maps_lock_);
+ auto it = GetGMapsEntry(*this);
+ // TODO: When C++17 becomes available, use std::map<>::extract(), modify, insert.
+ gMaps->erase(it);
+ begin_ += byte_count;
+ size_ -= byte_count;
+ base_begin_ = begin_;
+ base_size_ = size_;
+ gMaps->emplace(base_begin_, this);
+ }
+}
+
void MemMap::MadviseDontNeedAndZero() {
if (base_begin_ != nullptr || base_size_ != 0) {
if (!kMadviseZeroes) {
diff --git a/libartbase/base/mem_map.h b/libartbase/base/mem_map.h
index cd7d502796..20eda324e1 100644
--- a/libartbase/base/mem_map.h
+++ b/libartbase/base/mem_map.h
@@ -114,9 +114,15 @@ class MemMap {
// of the source mmap is returned to the caller.
bool ReplaceWith(/*in-out*/MemMap* source, /*out*/std::string* error);
+ // Set a debug friendly name for a map. It will be prefixed with "dalvik-".
+ static void SetDebugName(void* map_ptr, const char* name, size_t size);
+
// Request an anonymous region of length 'byte_count' and a requested base address.
// Use null as the requested base address if you don't care.
- // "reuse" allows re-mapping an address range from an existing mapping.
+ //
+ // `reuse` allows re-mapping an address range from an existing mapping which retains the
+ // ownership of the memory. Alternatively, `reservation` allows re-mapping the start of an
+ // existing reservation mapping, transferring the ownership of the memory to the new MemMap.
//
// The word "anonymous" in this context means "not backed by a file". The supplied
// 'name' will be used -- on systems that support it -- to give the mapping
@@ -129,15 +135,23 @@ class MemMap {
int prot,
bool low_4gb,
bool reuse,
- std::string* error_msg,
- bool use_ashmem = true);
+ /*inout*/MemMap* reservation,
+ /*out*/std::string* error_msg,
+ bool use_debug_name = true);
static MemMap MapAnonymous(const char* name,
uint8_t* addr,
size_t byte_count,
int prot,
bool low_4gb,
- std::string* error_msg) {
- return MapAnonymous(name, addr, byte_count, prot, low_4gb, /* reuse */ false, error_msg);
+ /*out*/std::string* error_msg) {
+ return MapAnonymous(name,
+ addr,
+ byte_count,
+ prot,
+ low_4gb,
+ /* reuse */ false,
+ /* reservation */ nullptr,
+ error_msg);
}
// Create placeholder for a region allocated by direct call to mmap.
@@ -164,18 +178,23 @@ class MemMap {
flags,
fd,
start,
- /*low_4gb*/low_4gb,
- /*reuse*/false,
+ /* low_4gb */ low_4gb,
filename,
+ /* reuse */ false,
+ /* reservation */ nullptr,
error_msg);
}
// Map part of a file, taking care of non-page aligned offsets. The "start" offset is absolute,
// not relative. This version allows requesting a specific address for the base of the mapping.
- // "reuse" allows us to create a view into an existing mapping where we do not take ownership of
- // the memory. If error_msg is null then we do not print /proc/maps to the log if
- // MapFileAtAddress fails. This helps improve performance of the fail case since reading and
- // printing /proc/maps takes several milliseconds in the worst case.
+ //
+ // `reuse` allows re-mapping an address range from an existing mapping which retains the
+ // ownership of the memory. Alternatively, `reservation` allows re-mapping the start of an
+ // existing reservation mapping, transferring the ownership of the memory to the new MemMap.
+ //
+ // If error_msg is null then we do not print /proc/maps to the log if MapFileAtAddress fails.
+ // This helps improve performance of the fail case since reading and printing /proc/maps takes
+ // several milliseconds in the worst case.
//
// On success, returns returns a valid MemMap. On failure, returns an invalid MemMap.
static MemMap MapFileAtAddress(uint8_t* addr,
@@ -185,9 +204,10 @@ class MemMap {
int fd,
off_t start,
bool low_4gb,
- bool reuse,
const char* filename,
- std::string* error_msg);
+ bool reuse,
+ /*inout*/MemMap* reservation,
+ /*out*/std::string* error_msg);
const std::string& GetName() const {
return name_;
@@ -239,7 +259,15 @@ class MemMap {
const char* tail_name,
int tail_prot,
std::string* error_msg,
- bool use_ashmem = true);
+ bool use_debug_name = true);
+
+ // Take ownership of pages at the beginning of the mapping. The mapping must be an
+ // anonymous reservation mapping, owning entire pages. The `byte_count` must not
+ // exceed the size of this reservation.
+ //
+ // Returns a mapping owning `byte_count` bytes rounded up to entire pages
+ // with size set to the passed `byte_count`.
+ MemMap TakeReservedMemory(size_t byte_count);
static bool CheckNoGaps(MemMap& begin_map, MemMap& end_map)
REQUIRES(!MemMap::mem_maps_lock_);
@@ -304,12 +332,21 @@ class MemMap {
off_t offset)
REQUIRES(!MemMap::mem_maps_lock_);
+ // Release memory owned by a reservation mapping.
+ void ReleaseReservedMemory(size_t byte_count);
+
// member function to access real_munmap
static bool CheckMapRequest(uint8_t* expected_ptr,
void* actual_ptr,
size_t byte_count,
std::string* error_msg);
+ static bool CheckReservation(uint8_t* expected_ptr,
+ size_t byte_count,
+ const char* name,
+ const MemMap& reservation,
+ /*out*/std::string* error_msg);
+
std::string name_;
uint8_t* begin_ = nullptr; // Start of data. May be changed by AlignBy.
size_t size_ = 0u; // Length of data.
diff --git a/libartbase/base/mem_map_test.cc b/libartbase/base/mem_map_test.cc
index 396f12b421..ab3d18ff04 100644
--- a/libartbase/base/mem_map_test.cc
+++ b/libartbase/base/mem_map_test.cc
@@ -540,6 +540,7 @@ TEST_F(MemMapTest, MapAnonymousReuse) {
PROT_READ | PROT_WRITE,
/* low_4gb */ false,
/* reuse */ false,
+ /* reservation */ nullptr,
&error_msg);
ASSERT_TRUE(map.IsValid());
ASSERT_TRUE(error_msg.empty());
@@ -549,6 +550,7 @@ TEST_F(MemMapTest, MapAnonymousReuse) {
PROT_READ | PROT_WRITE,
/* low_4gb */ false,
/* reuse */ true,
+ /* reservation */ nullptr,
&error_msg);
ASSERT_TRUE(map2.IsValid());
ASSERT_TRUE(error_msg.empty());
@@ -720,4 +722,108 @@ TEST_F(MemMapTest, AlignBy) {
}
}
+TEST_F(MemMapTest, Reservation) {
+ CommonInit();
+ std::string error_msg;
+ ScratchFile scratch_file;
+ constexpr size_t kMapSize = 5 * kPageSize;
+ std::unique_ptr<uint8_t[]> data(new uint8_t[kMapSize]());
+ ASSERT_TRUE(scratch_file.GetFile()->WriteFully(&data[0], kMapSize));
+
+ MemMap reservation = MemMap::MapAnonymous("Test reservation",
+ /* addr */ nullptr,
+ kMapSize,
+ PROT_NONE,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(reservation.IsValid());
+ ASSERT_TRUE(error_msg.empty());
+
+ // Map first part of the reservation.
+ constexpr size_t kChunk1Size = kPageSize - 1u;
+ static_assert(kChunk1Size < kMapSize, "We want to split the reservation.");
+ uint8_t* addr1 = reservation.Begin();
+ MemMap map1 = MemMap::MapFileAtAddress(addr1,
+ /* byte_count */ kChunk1Size,
+ PROT_READ,
+ MAP_PRIVATE,
+ scratch_file.GetFd(),
+ /* start */ 0,
+ /* low_4gb */ false,
+ scratch_file.GetFilename().c_str(),
+ /* reuse */ false,
+ &reservation,
+ &error_msg);
+ ASSERT_TRUE(map1.IsValid()) << error_msg;
+ ASSERT_TRUE(error_msg.empty());
+ ASSERT_EQ(map1.Size(), kChunk1Size);
+ ASSERT_EQ(addr1, map1.Begin());
+ ASSERT_TRUE(reservation.IsValid());
+ // Entire pages are taken from the `reservation`.
+ ASSERT_LT(map1.End(), map1.BaseEnd());
+ ASSERT_EQ(map1.BaseEnd(), reservation.Begin());
+
+ // Map second part as an anonymous mapping.
+ constexpr size_t kChunk2Size = 2 * kPageSize;
+ DCHECK_LT(kChunk2Size, reservation.Size()); // We want to split the reservation.
+ uint8_t* addr2 = reservation.Begin();
+ MemMap map2 = MemMap::MapAnonymous("MiddleReservation",
+ addr2,
+ /* byte_count */ kChunk2Size,
+ PROT_READ,
+ /* low_4gb */ false,
+ /* reuse */ false,
+ &reservation,
+ &error_msg);
+ ASSERT_TRUE(map2.IsValid()) << error_msg;
+ ASSERT_TRUE(error_msg.empty());
+ ASSERT_EQ(map2.Size(), kChunk2Size);
+ ASSERT_EQ(addr2, map2.Begin());
+ ASSERT_EQ(map2.End(), map2.BaseEnd()); // kChunk2Size is page aligned.
+ ASSERT_EQ(map2.BaseEnd(), reservation.Begin());
+
+ // Map the rest of the reservation except the last byte.
+ const size_t kChunk3Size = reservation.Size() - 1u;
+ uint8_t* addr3 = reservation.Begin();
+ MemMap map3 = MemMap::MapFileAtAddress(addr3,
+ /* byte_count */ kChunk3Size,
+ PROT_READ,
+ MAP_PRIVATE,
+ scratch_file.GetFd(),
+ /* start */ dchecked_integral_cast<size_t>(addr3 - addr1),
+ /* low_4gb */ false,
+ scratch_file.GetFilename().c_str(),
+ /* reuse */ false,
+ &reservation,
+ &error_msg);
+ ASSERT_TRUE(map3.IsValid()) << error_msg;
+ ASSERT_TRUE(error_msg.empty());
+ ASSERT_EQ(map3.Size(), kChunk3Size);
+ ASSERT_EQ(addr3, map3.Begin());
+ // Entire pages are taken from the `reservation`, so it's now exhausted.
+ ASSERT_FALSE(reservation.IsValid());
+
+ // Now split the MiddleReservation.
+ constexpr size_t kChunk2ASize = kPageSize - 1u;
+ DCHECK_LT(kChunk2ASize, map2.Size()); // We want to split the reservation.
+ MemMap map2a = map2.TakeReservedMemory(kChunk2ASize);
+ ASSERT_TRUE(map2a.IsValid()) << error_msg;
+ ASSERT_TRUE(error_msg.empty());
+ ASSERT_EQ(map2a.Size(), kChunk2ASize);
+ ASSERT_EQ(addr2, map2a.Begin());
+ ASSERT_TRUE(map2.IsValid());
+ ASSERT_LT(map2a.End(), map2a.BaseEnd());
+ ASSERT_EQ(map2a.BaseEnd(), map2.Begin());
+
+ // And take the rest of the middle reservation.
+ const size_t kChunk2BSize = map2.Size() - 1u;
+ uint8_t* addr2b = map2.Begin();
+ MemMap map2b = map2.TakeReservedMemory(kChunk2BSize);
+ ASSERT_TRUE(map2b.IsValid()) << error_msg;
+ ASSERT_TRUE(error_msg.empty());
+ ASSERT_EQ(map2b.Size(), kChunk2ASize);
+ ASSERT_EQ(addr2b, map2b.Begin());
+ ASSERT_FALSE(map2.IsValid());
+}
+
} // namespace art
diff --git a/libartbase/base/unix_file/fd_file.cc b/libartbase/base/unix_file/fd_file.cc
index c5313e969d..d715670194 100644
--- a/libartbase/base/unix_file/fd_file.cc
+++ b/libartbase/base/unix_file/fd_file.cc
@@ -21,6 +21,10 @@
#include <sys/types.h>
#include <unistd.h>
+#if defined(__BIONIC__)
+#include <android/fdsan.h>
+#endif
+
#include <limits>
#include <android-base/logging.h>
@@ -36,26 +40,34 @@
namespace unix_file {
-FdFile::FdFile()
- : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true), read_only_mode_(false) {
+#if defined(__BIONIC__)
+static uint64_t GetFdFileOwnerTag(FdFile* fd_file) {
+ return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_ART_FDFILE,
+ reinterpret_cast<uint64_t>(fd_file));
}
+#endif
FdFile::FdFile(int fd, bool check_usage)
- : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck),
- fd_(fd), auto_close_(true), read_only_mode_(false) {
-}
+ : FdFile(fd, std::string(), check_usage) {}
FdFile::FdFile(int fd, const std::string& path, bool check_usage)
- : FdFile(fd, path, check_usage, false) {
-}
+ : FdFile(fd, path, check_usage, false) {}
-FdFile::FdFile(int fd, const std::string& path, bool check_usage, bool read_only_mode)
+FdFile::FdFile(int fd, const std::string& path, bool check_usage,
+ bool read_only_mode)
: guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck),
- fd_(fd), file_path_(path), auto_close_(true), read_only_mode_(read_only_mode) {
+ fd_(fd),
+ file_path_(path),
+ read_only_mode_(read_only_mode) {
+#if defined(__BIONIC__)
+ if (fd >= 0) {
+ android_fdsan_exchange_owner_tag(fd, 0, GetFdFileOwnerTag(this));
+ }
+#endif
}
-FdFile::FdFile(const std::string& path, int flags, mode_t mode, bool check_usage)
- : fd_(-1), auto_close_(true) {
+FdFile::FdFile(const std::string& path, int flags, mode_t mode,
+ bool check_usage) {
Open(path, flags, mode);
if (!check_usage || !IsOpened()) {
guard_state_ = GuardState::kNoCheck;
@@ -72,13 +84,27 @@ void FdFile::Destroy() {
}
DCHECK_GE(guard_state_, GuardState::kClosed);
}
- if (auto_close_ && fd_ != -1) {
+ if (fd_ != -1) {
if (Close() != 0) {
PLOG(WARNING) << "Failed to close file with fd=" << fd_ << " path=" << file_path_;
}
}
}
+FdFile::FdFile(FdFile&& other)
+ : guard_state_(other.guard_state_),
+ fd_(other.fd_),
+ file_path_(std::move(other.file_path_)),
+ read_only_mode_(other.read_only_mode_) {
+#if defined(__BIONIC__)
+ if (fd_ >= 0) {
+ android_fdsan_exchange_owner_tag(fd_, GetFdFileOwnerTag(&other), GetFdFileOwnerTag(this));
+ }
+#endif
+ other.guard_state_ = GuardState::kClosed;
+ other.fd_ = -1;
+}
+
FdFile& FdFile::operator=(FdFile&& other) {
if (this == &other) {
return *this;
@@ -91,10 +117,15 @@ FdFile& FdFile::operator=(FdFile&& other) {
guard_state_ = other.guard_state_;
fd_ = other.fd_;
file_path_ = std::move(other.file_path_);
- auto_close_ = other.auto_close_;
read_only_mode_ = other.read_only_mode_;
- other.Release(); // Release other.
+#if defined(__BIONIC__)
+ if (fd_ >= 0) {
+ android_fdsan_exchange_owner_tag(fd_, GetFdFileOwnerTag(&other), GetFdFileOwnerTag(this));
+ }
+#endif
+ other.guard_state_ = GuardState::kClosed;
+ other.fd_ = -1;
return *this;
}
@@ -102,6 +133,39 @@ FdFile::~FdFile() {
Destroy();
}
+int FdFile::Release() {
+ int tmp_fd = fd_;
+ fd_ = -1;
+ guard_state_ = GuardState::kNoCheck;
+#if defined(__BIONIC__)
+ if (tmp_fd >= 0) {
+ android_fdsan_exchange_owner_tag(tmp_fd, GetFdFileOwnerTag(this), 0);
+ }
+#endif
+ return tmp_fd;
+}
+
+void FdFile::Reset(int fd, bool check_usage) {
+ CHECK_NE(fd, fd_);
+
+ if (fd_ != -1) {
+ Destroy();
+ }
+ fd_ = fd;
+
+#if defined(__BIONIC__)
+ if (fd_ >= 0) {
+ android_fdsan_exchange_owner_tag(fd_, 0, GetFdFileOwnerTag(this));
+ }
+#endif
+
+ if (check_usage) {
+ guard_state_ = fd == -1 ? GuardState::kNoCheck : GuardState::kBase;
+ } else {
+ guard_state_ = GuardState::kNoCheck;
+ }
+}
+
void FdFile::moveTo(GuardState target, GuardState warn_threshold, const char* warning) {
if (kCheckSafeUsage) {
if (guard_state_ < GuardState::kNoCheck) {
@@ -125,10 +189,6 @@ void FdFile::moveUp(GuardState target, const char* warning) {
}
}
-void FdFile::DisableAutoClose() {
- auto_close_ = false;
-}
-
bool FdFile::Open(const std::string& path, int flags) {
return Open(path, flags, 0640);
}
@@ -141,6 +201,11 @@ bool FdFile::Open(const std::string& path, int flags, mode_t mode) {
if (fd_ == -1) {
return false;
}
+
+#if defined(__BIONIC__)
+ android_fdsan_exchange_owner_tag(fd_, 0, GetFdFileOwnerTag(this));
+#endif
+
file_path_ = path;
if (kCheckSafeUsage && (flags & (O_RDWR | O_CREAT | O_WRONLY)) != 0) {
// Start in the base state (not flushed, not closed).
@@ -154,7 +219,11 @@ bool FdFile::Open(const std::string& path, int flags, mode_t mode) {
}
int FdFile::Close() {
+#if defined(__BIONIC__)
+ int result = android_fdsan_close_with_tag(fd_, GetFdFileOwnerTag(this));
+#else
int result = close(fd_);
+#endif
// Test here, so the file is closed and not leaked.
if (kCheckSafeUsage) {
diff --git a/libartbase/base/unix_file/fd_file.h b/libartbase/base/unix_file/fd_file.h
index 19be3ef6f7..e362ed13ce 100644
--- a/libartbase/base/unix_file/fd_file.h
+++ b/libartbase/base/unix_file/fd_file.h
@@ -34,9 +34,9 @@ static constexpr bool kCheckSafeUsage = true;
// Not thread safe.
class FdFile : public RandomAccessFile {
public:
- FdFile();
- // Creates an FdFile using the given file descriptor. Takes ownership of the
- // file descriptor. (Use DisableAutoClose to retain ownership.)
+ FdFile() = default;
+ // Creates an FdFile using the given file descriptor.
+ // Takes ownership of the file descriptor.
FdFile(int fd, bool checkUsage);
FdFile(int fd, const std::string& path, bool checkUsage);
FdFile(int fd, const std::string& path, bool checkUsage, bool read_only_mode);
@@ -46,40 +46,16 @@ class FdFile : public RandomAccessFile {
FdFile(const std::string& path, int flags, mode_t mode, bool checkUsage);
// Move constructor.
- FdFile(FdFile&& other)
- : guard_state_(other.guard_state_),
- fd_(other.fd_),
- file_path_(std::move(other.file_path_)),
- auto_close_(other.auto_close_),
- read_only_mode_(other.read_only_mode_) {
- other.Release(); // Release the src.
- }
+ FdFile(FdFile&& other);
// Move assignment operator.
FdFile& operator=(FdFile&& other);
// Release the file descriptor. This will make further accesses to this FdFile invalid. Disables
// all further state checking.
- int Release() {
- int tmp_fd = fd_;
- fd_ = -1;
- guard_state_ = GuardState::kNoCheck;
- auto_close_ = false;
- return tmp_fd;
- }
+ int Release();
- void Reset(int fd, bool check_usage) {
- if (fd_ != -1 && fd_ != fd) {
- Destroy();
- }
- fd_ = fd;
- if (check_usage) {
- guard_state_ = fd == -1 ? GuardState::kNoCheck : GuardState::kBase;
- } else {
- guard_state_ = GuardState::kNoCheck;
- }
- // Keep the auto_close_ state.
- }
+ void Reset(int fd, bool check_usage);
// Destroys an FdFile, closing the file descriptor if Close hasn't already
// been called. (If you care about the return value of Close, call it
@@ -121,7 +97,6 @@ class FdFile : public RandomAccessFile {
const std::string& GetPath() const {
return file_path_;
}
- void DisableAutoClose();
bool ReadFully(void* buffer, size_t byte_count) WARN_UNUSED;
bool PreadFully(void* buffer, size_t byte_count, size_t offset) WARN_UNUSED;
bool WriteFully(const void* buffer, size_t byte_count) WARN_UNUSED;
@@ -168,7 +143,7 @@ class FdFile : public RandomAccessFile {
}
}
- GuardState guard_state_;
+ GuardState guard_state_ = GuardState::kClosed;
// Opens file 'file_path' using 'flags' and 'mode'.
bool Open(const std::string& file_path, int flags);
@@ -180,10 +155,9 @@ class FdFile : public RandomAccessFile {
void Destroy(); // For ~FdFile and operator=(&&).
- int fd_;
+ int fd_ = -1;
std::string file_path_;
- bool auto_close_;
- bool read_only_mode_;
+ bool read_only_mode_ = false;
DISALLOW_COPY_AND_ASSIGN(FdFile);
};
diff --git a/libartbase/base/unix_file/fd_file_test.cc b/libartbase/base/unix_file/fd_file_test.cc
index 1f731a7a7b..298b2d785f 100644
--- a/libartbase/base/unix_file/fd_file_test.cc
+++ b/libartbase/base/unix_file/fd_file_test.cc
@@ -24,7 +24,10 @@ namespace unix_file {
class FdFileTest : public RandomAccessFileTest {
protected:
virtual RandomAccessFile* MakeTestFile() {
- return new FdFile(fileno(tmpfile()), false);
+ FILE* tmp = tmpfile();
+ int fd = dup(fileno(tmp));
+ fclose(tmp);
+ return new FdFile(fd, false);
}
};
diff --git a/libartbase/base/zip_archive.cc b/libartbase/base/zip_archive.cc
index a841bae8ea..174d22792a 100644
--- a/libartbase/base/zip_archive.cc
+++ b/libartbase/base/zip_archive.cc
@@ -133,16 +133,14 @@ MemMap ZipEntry::MapDirectlyFromFile(const char* zip_filename, std::string* erro
}
MemMap map =
- MemMap::MapFileAtAddress(nullptr, // Expected pointer address
- GetUncompressedLength(), // Byte count
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE,
- zip_fd,
- offset,
- false, // Don't restrict allocation to lower4GB
- false, // Doesn't overlap existing map (reuse=false)
- name.c_str(),
- /*out*/error_msg);
+ MemMap::MapFile(GetUncompressedLength(), // Byte count
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE,
+ zip_fd,
+ offset,
+ /* low_4gb */ false,
+ name.c_str(),
+ error_msg);
if (!map.IsValid()) {
DCHECK(!error_msg->empty());
diff --git a/libartbase/base/zip_archive_test.cc b/libartbase/base/zip_archive_test.cc
index b99a471e04..b9238811ca 100644
--- a/libartbase/base/zip_archive_test.cc
+++ b/libartbase/base/zip_archive_test.cc
@@ -41,7 +41,7 @@ TEST_F(ZipArchiveTest, FindAndExtract) {
ScratchFile tmp;
ASSERT_NE(-1, tmp.GetFd());
- std::unique_ptr<File> file(new File(tmp.GetFd(), tmp.GetFilename(), false));
+ std::unique_ptr<File> file(new File(dup(tmp.GetFd()), tmp.GetFilename(), false));
ASSERT_TRUE(file.get() != nullptr);
bool success = zip_entry->ExtractToFile(*file, &error_msg);
ASSERT_TRUE(success) << error_msg;
diff --git a/libdexfile/dex/art_dex_file_loader.cc b/libdexfile/dex/art_dex_file_loader.cc
index eb7d3d3308..4f73967353 100644
--- a/libdexfile/dex/art_dex_file_loader.cc
+++ b/libdexfile/dex/art_dex_file_loader.cc
@@ -95,7 +95,7 @@ bool ArtDexFileLoader::GetMultiDexChecksums(const char* filename,
File fd;
if (zip_fd != -1) {
if (ReadMagicAndReset(zip_fd, &magic, error_msg)) {
- fd = File(zip_fd, false /* check_usage */);
+ fd = File(DupCloexec(zip_fd), false /* check_usage */);
}
} else {
fd = OpenAndReadMagic(filename, &magic, error_msg);
diff --git a/libprofile/profile/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc
index c7653457aa..9b70e62f0c 100644
--- a/libprofile/profile/profile_compilation_info.cc
+++ b/libprofile/profile/profile_compilation_info.cc
@@ -34,6 +34,7 @@
#include "base/arena_allocator.h"
#include "base/dumpable.h"
+#include "base/file_utils.h"
#include "base/logging.h" // For VLOG.
#include "base/malloc_arena_pool.h"
#include "base/os.h"
@@ -1171,7 +1172,8 @@ ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::OpenSource(
source->reset(ProfileSource::Create(fd));
return kProfileLoadSuccess;
} else {
- std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, "profile", error));
+ std::unique_ptr<ZipArchive> zip_archive(
+ ZipArchive::OpenFromFd(DupCloexec(fd), "profile", error));
if (zip_archive.get() == nullptr) {
*error = "Could not open the profile zip archive";
return kProfileLoadBadData;
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index fdf43d7632..d9be750259 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1474,7 +1474,7 @@ class OatDumper {
}
return verifier::MethodVerifier::VerifyMethodAndDump(
soa.Self(), vios, dex_method_idx, dex_file, dex_cache, *options_.class_loader_,
- class_def, code_item, method, method_access_flags);
+ class_def, code_item, method, method_access_flags, /* api_level */ 0);
}
return nullptr;
@@ -1825,11 +1825,11 @@ class ImageDumper {
oat_file = OatFile::Open(/* zip_fd */ -1,
oat_location,
oat_location,
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
- nullptr,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
+ /* abs_dex_location */ nullptr,
+ /* reservation */ nullptr,
&error_msg);
}
if (oat_file == nullptr) {
@@ -2723,11 +2723,11 @@ static int DumpImages(Runtime* runtime, OatDumperOptions* options, std::ostream*
std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
options->app_oat_,
options->app_oat_,
- nullptr,
- nullptr,
- false,
- /*low_4gb*/true,
- nullptr,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ true,
+ /* abs_dex_location */ nullptr,
+ /* reservation */ nullptr,
&error_msg));
if (oat_file == nullptr) {
LOG(ERROR) << "Failed to open oat file " << options->app_oat_ << " with error " << error_msg;
@@ -2847,11 +2847,11 @@ static int DumpOat(Runtime* runtime,
std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
oat_filename,
oat_filename,
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
dex_filename,
+ /* reservation */ nullptr,
&error_msg));
if (oat_file == nullptr) {
LOG(ERROR) << "Failed to open oat file from '" << oat_filename << "': " << error_msg;
@@ -2873,11 +2873,11 @@ static int SymbolizeOat(const char* oat_filename,
std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
oat_filename,
oat_filename,
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
dex_filename,
+ /* reservation */ nullptr,
&error_msg));
if (oat_file == nullptr) {
LOG(ERROR) << "Failed to open oat file from '" << oat_filename << "': " << error_msg;
@@ -2921,11 +2921,11 @@ class IMTDumper {
std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
oat_filename,
oat_filename,
- nullptr,
- nullptr,
- false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
/*low_4gb*/false,
dex_filename,
+ /* reservation */ nullptr,
&error_msg));
if (oat_file == nullptr) {
LOG(ERROR) << "Failed to open oat file from '" << oat_filename << "': " << error_msg;
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index 59f61e2ee4..3213bbe91a 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -506,13 +506,15 @@ class JvmtiFunctions {
static jvmtiError IterateOverInstancesOfClass(
jvmtiEnv* env,
- jclass klass ATTRIBUTE_UNUSED,
- jvmtiHeapObjectFilter object_filter ATTRIBUTE_UNUSED,
- jvmtiHeapObjectCallback heap_object_callback ATTRIBUTE_UNUSED,
- const void* user_data ATTRIBUTE_UNUSED) {
+ jclass klass,
+ jvmtiHeapObjectFilter object_filter,
+ jvmtiHeapObjectCallback heap_object_callback,
+ const void* user_data) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_tag_objects);
- return ERR(NOT_IMPLEMENTED);
+ HeapUtil heap_util(ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table.get());
+ return heap_util.IterateOverInstancesOfClass(
+ env, klass, object_filter, heap_object_callback, user_data);
}
static jvmtiError GetLocalObject(jvmtiEnv* env,
diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc
index 85aa946356..6c79a602c3 100644
--- a/openjdkjvmti/ti_heap.cc
+++ b/openjdkjvmti/ti_heap.cc
@@ -653,6 +653,70 @@ void HeapUtil::Unregister() {
art::Runtime::Current()->RemoveSystemWeakHolder(&gIndexCachingTable);
}
+jvmtiError HeapUtil::IterateOverInstancesOfClass(jvmtiEnv* env,
+ jclass klass,
+ jvmtiHeapObjectFilter filter,
+ jvmtiHeapObjectCallback cb,
+ const void* user_data) {
+ if (cb == nullptr || klass == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self); // Now we know we have the shared lock.
+ art::StackHandleScope<1> hs(self);
+
+ art::ObjPtr<art::mirror::Object> klass_ptr(soa.Decode<art::mirror::Class>(klass));
+ if (!klass_ptr->IsClass()) {
+ return ERR(INVALID_CLASS);
+ }
+ art::Handle<art::mirror::Class> filter_klass(hs.NewHandle(klass_ptr->AsClass()));
+ if (filter_klass->IsInterface()) {
+ // nothing is an 'instance' of an interface so just return without walking anything.
+ return OK;
+ }
+
+ ObjectTagTable* tag_table = ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table.get();
+ bool stop_reports = false;
+ auto visitor = [&](art::mirror::Object* obj) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ // Early return, as we can't really stop visiting.
+ if (stop_reports) {
+ return;
+ }
+
+ art::ScopedAssertNoThreadSuspension no_suspension("IterateOverInstancesOfClass");
+
+ art::ObjPtr<art::mirror::Class> klass = obj->GetClass();
+
+ if (filter_klass != nullptr && !filter_klass->IsAssignableFrom(klass)) {
+ return;
+ }
+
+ jlong tag = 0;
+ tag_table->GetTag(obj, &tag);
+ if ((filter != JVMTI_HEAP_OBJECT_EITHER) &&
+ ((tag == 0 && filter == JVMTI_HEAP_OBJECT_TAGGED) ||
+ (tag != 0 && filter == JVMTI_HEAP_OBJECT_UNTAGGED))) {
+ return;
+ }
+
+ jlong class_tag = 0;
+ tag_table->GetTag(klass.Ptr(), &class_tag);
+
+ jlong saved_tag = tag;
+ jint ret = cb(class_tag, obj->SizeOf(), &tag, const_cast<void*>(user_data));
+
+ stop_reports = (ret == JVMTI_ITERATION_ABORT);
+
+ if (tag != saved_tag) {
+ tag_table->Set(obj, tag);
+ }
+ };
+ art::Runtime::Current()->GetHeap()->VisitObjects(visitor);
+
+ return OK;
+}
+
template <typename T>
static jvmtiError DoIterateThroughHeap(T fn,
jvmtiEnv* env,
diff --git a/openjdkjvmti/ti_heap.h b/openjdkjvmti/ti_heap.h
index 62761b500c..382d80f576 100644
--- a/openjdkjvmti/ti_heap.h
+++ b/openjdkjvmti/ti_heap.h
@@ -30,6 +30,12 @@ class HeapUtil {
jvmtiError GetLoadedClasses(jvmtiEnv* env, jint* class_count_ptr, jclass** classes_ptr);
+ jvmtiError IterateOverInstancesOfClass(jvmtiEnv* env,
+ jclass klass,
+ jvmtiHeapObjectFilter filter,
+ jvmtiHeapObjectCallback cb,
+ const void* user_data);
+
jvmtiError IterateThroughHeap(jvmtiEnv* env,
jint heap_filter,
jclass klass,
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 2ec2f04e73..db2b143022 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -1122,6 +1122,7 @@ bool Redefiner::ClassRedefinition::CheckVerification(const RedefinitionDataIter&
true, /*allow_soft_failures*/
/*log_level*/
art::verifier::HardFailLogMode::kLogWarning,
+ art::Runtime::Current()->GetTargetSdkVersion(),
&error);
switch (failure) {
case art::verifier::FailureKind::kNoFailure:
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 65f05d9362..ccb42c4ba2 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4169,6 +4169,7 @@ verifier::FailureKind ClassLinker::PerformClassVerification(Thread* self,
runtime->GetCompilerCallbacks(),
runtime->IsAotCompiler(),
log_level,
+ Runtime::Current()->GetTargetSdkVersion(),
error_msg);
}
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index 9e3159d5e7..93af77fe23 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -20,6 +20,8 @@
#include <backtrace/BacktraceMap.h>
#include <gtest/gtest.h>
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
#include "base/file_utils.h"
#include "base/mem_map.h"
#include "common_runtime_test.h"
@@ -27,6 +29,7 @@
#include "dex2oat_environment_test.h"
#include "dexopt_test.h"
#include "gc/space/image_space.h"
+#include "hidden_api.h"
namespace art {
void DexoptTest::SetUp() {
@@ -45,6 +48,46 @@ void DexoptTest::PostRuntimeCreate() {
ReserveImageSpace();
}
+static std::string ImageLocation() {
+ Runtime* runtime = Runtime::Current();
+ const std::vector<gc::space::ImageSpace*>& image_spaces =
+ runtime->GetHeap()->GetBootImageSpaces();
+ if (image_spaces.empty()) {
+ return "";
+ }
+ return image_spaces[0]->GetImageLocation();
+}
+
+bool DexoptTest::Dex2Oat(const std::vector<std::string>& args, std::string* error_msg) {
+ Runtime* runtime = Runtime::Current();
+
+ std::vector<std::string> argv;
+ argv.push_back(runtime->GetCompilerExecutable());
+ if (runtime->IsJavaDebuggable()) {
+ argv.push_back("--debuggable");
+ }
+ runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
+
+ if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kNoChecks) {
+ argv.push_back("--runtime-arg");
+ argv.push_back("-Xhidden-api-checks");
+ }
+
+ if (!kIsTargetBuild) {
+ argv.push_back("--host");
+ }
+
+ argv.push_back("--boot-image=" + ImageLocation());
+
+ std::vector<std::string> compiler_options = runtime->GetCompilerOptions();
+ argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());
+
+ argv.insert(argv.end(), args.begin(), args.end());
+
+ std::string command_line(android::base::Join(argv, ' '));
+ return Exec(argv, error_msg);
+}
+
void DexoptTest::GenerateOatForTest(const std::string& dex_location,
const std::string& oat_location_in,
CompilerFilter::Filter filter,
@@ -96,7 +139,7 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location,
}
std::string error_msg;
- ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+ ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
if (!relocate) {
// Restore the dalvik cache if needed.
@@ -108,11 +151,11 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location,
std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
oat_location.c_str(),
oat_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
dex_location.c_str(),
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
EXPECT_EQ(pic, odex_file->IsPic());
diff --git a/runtime/dexopt_test.h b/runtime/dexopt_test.h
index df7181a1e7..5dff379a32 100644
--- a/runtime/dexopt_test.h
+++ b/runtime/dexopt_test.h
@@ -71,6 +71,8 @@ class DexoptTest : public Dex2oatEnvironmentTest {
// Generate a standard oat file in the oat location.
void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter);
+ static bool Dex2Oat(const std::vector<std::string>& args, std::string* error_msg);
+
private:
// Pre-Relocate the image to a known non-zero offset so we don't have to
// deal with the runtime randomly relocating the image by 0 and messing up
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index d45a689fd6..e7715c4934 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -37,9 +37,7 @@ namespace art {
using android::base::StringPrintf;
template <typename ElfTypes>
-ElfFileImpl<ElfTypes>::ElfFileImpl(File* file, bool writable,
- bool program_header_only,
- uint8_t* requested_base)
+ElfFileImpl<ElfTypes>::ElfFileImpl(File* file, bool writable, bool program_header_only)
: writable_(writable),
program_header_only_(program_header_only),
header_(nullptr),
@@ -54,8 +52,7 @@ ElfFileImpl<ElfTypes>::ElfFileImpl(File* file, bool writable,
dynstr_section_start_(nullptr),
hash_section_start_(nullptr),
symtab_symbol_table_(nullptr),
- dynsym_symbol_table_(nullptr),
- requested_base_(requested_base) {
+ dynsym_symbol_table_(nullptr) {
CHECK(file != nullptr);
}
@@ -64,10 +61,9 @@ ElfFileImpl<ElfTypes>* ElfFileImpl<ElfTypes>::Open(File* file,
bool writable,
bool program_header_only,
bool low_4gb,
- std::string* error_msg,
- uint8_t* requested_base) {
- std::unique_ptr<ElfFileImpl<ElfTypes>> elf_file(new ElfFileImpl<ElfTypes>
- (file, writable, program_header_only, requested_base));
+ std::string* error_msg) {
+ std::unique_ptr<ElfFileImpl<ElfTypes>> elf_file(
+ new ElfFileImpl<ElfTypes>(file, writable, program_header_only));
int prot;
int flags;
if (writable) {
@@ -89,9 +85,8 @@ ElfFileImpl<ElfTypes>* ElfFileImpl<ElfTypes>::Open(File* file,
int flags,
bool low_4gb,
std::string* error_msg) {
- std::unique_ptr<ElfFileImpl<ElfTypes>> elf_file(new ElfFileImpl<ElfTypes>
- (file, (prot & PROT_WRITE) == PROT_WRITE, /*program_header_only*/false,
- /*requested_base*/nullptr));
+ std::unique_ptr<ElfFileImpl<ElfTypes>> elf_file(
+ new ElfFileImpl<ElfTypes>(file, (prot & PROT_WRITE) != 0, /* program_header_only */ false));
if (!elf_file->Setup(file, prot, flags, low_4gb, error_msg)) {
return nullptr;
}
@@ -684,9 +679,7 @@ template <typename ElfTypes>
typename ElfTypes::Phdr* ElfFileImpl<ElfTypes>::GetProgramHeader(Elf_Word i) const {
CHECK_LT(i, GetProgramHeaderNum()) << file_path_; // Sanity check for caller.
uint8_t* program_header = GetProgramHeadersStart() + (i * GetHeader().e_phentsize);
- if (program_header >= End()) {
- return nullptr; // Failure condition.
- }
+ CHECK_LT(program_header, End());
return reinterpret_cast<Elf_Phdr*>(program_header);
}
@@ -1027,9 +1020,17 @@ typename ElfTypes::Rela& ElfFileImpl<ElfTypes>::GetRela(Elf_Shdr& section_header
return *(GetRelaSectionStart(section_header) + i);
}
-// Base on bionic phdr_table_get_load_size
template <typename ElfTypes>
bool ElfFileImpl<ElfTypes>::GetLoadedSize(size_t* size, std::string* error_msg) const {
+ uint8_t* vaddr_begin;
+ return GetLoadedAddressRange(&vaddr_begin, size, error_msg);
+}
+
+// Base on bionic phdr_table_get_load_size
+template <typename ElfTypes>
+bool ElfFileImpl<ElfTypes>::GetLoadedAddressRange(/*out*/uint8_t** vaddr_begin,
+ /*out*/size_t* vaddr_size,
+ /*out*/std::string* error_msg) const {
Elf_Addr min_vaddr = static_cast<Elf_Addr>(-1);
Elf_Addr max_vaddr = 0u;
for (Elf_Word i = 0; i < GetProgramHeaderNum(); i++) {
@@ -1048,7 +1049,8 @@ bool ElfFileImpl<ElfTypes>::GetLoadedSize(size_t* size, std::string* error_msg)
<< program_header->p_vaddr << "+0x" << program_header->p_memsz << "=0x" << end_vaddr
<< " in ELF file \"" << file_path_ << "\"";
*error_msg = oss.str();
- *size = static_cast<size_t>(-1);
+ *vaddr_begin = nullptr;
+ *vaddr_size = static_cast<size_t>(-1);
return false;
}
if (end_vaddr > max_vaddr) {
@@ -1058,17 +1060,19 @@ bool ElfFileImpl<ElfTypes>::GetLoadedSize(size_t* size, std::string* error_msg)
min_vaddr = RoundDown(min_vaddr, kPageSize);
max_vaddr = RoundUp(max_vaddr, kPageSize);
CHECK_LT(min_vaddr, max_vaddr) << file_path_;
- Elf_Addr loaded_size = max_vaddr - min_vaddr;
- // Check that the loaded_size fits in size_t.
- if (UNLIKELY(loaded_size > std::numeric_limits<size_t>::max())) {
+ // Check that the range fits into the runtime address space.
+ if (UNLIKELY(max_vaddr - 1u > std::numeric_limits<size_t>::max())) {
std::ostringstream oss;
- oss << "Loaded size is 0x" << std::hex << loaded_size << " but maximum size_t is 0x"
- << std::numeric_limits<size_t>::max() << " for ELF file \"" << file_path_ << "\"";
+ oss << "Loaded range is 0x" << std::hex << min_vaddr << "-0x" << max_vaddr
+ << " but maximum size_t is 0x" << std::numeric_limits<size_t>::max()
+ << " for ELF file \"" << file_path_ << "\"";
*error_msg = oss.str();
- *size = static_cast<size_t>(-1);
+ *vaddr_begin = nullptr;
+ *vaddr_size = static_cast<size_t>(-1);
return false;
}
- *size = loaded_size;
+ *vaddr_begin = reinterpret_cast<uint8_t*>(min_vaddr);
+ *vaddr_size = dchecked_integral_cast<size_t>(max_vaddr - min_vaddr);
return true;
}
@@ -1099,7 +1103,8 @@ template <typename ElfTypes>
bool ElfFileImpl<ElfTypes>::Load(File* file,
bool executable,
bool low_4gb,
- std::string* error_msg) {
+ /*inout*/MemMap* reservation,
+ /*out*/std::string* error_msg) {
CHECK(program_header_only_) << file->GetPath();
if (executable) {
@@ -1115,11 +1120,6 @@ bool ElfFileImpl<ElfTypes>::Load(File* file,
bool reserved = false;
for (Elf_Word i = 0; i < GetProgramHeaderNum(); i++) {
Elf_Phdr* program_header = GetProgramHeader(i);
- if (program_header == nullptr) {
- *error_msg = StringPrintf("No program header for entry %d in ELF file %s.",
- i, file->GetPath().c_str());
- return false;
- }
// Record .dynamic header information for later use
if (program_header->p_type == PT_DYNAMIC) {
@@ -1150,41 +1150,39 @@ bool ElfFileImpl<ElfTypes>::Load(File* file,
}
size_t file_length = static_cast<size_t>(temp_file_length);
if (!reserved) {
- uint8_t* reserve_base = reinterpret_cast<uint8_t*>(program_header->p_vaddr);
- uint8_t* reserve_base_override = reserve_base;
- // Override the base (e.g. when compiling with --compile-pic)
- if (requested_base_ != nullptr) {
- reserve_base_override = requested_base_;
- }
- std::string reservation_name("ElfFile reservation for ");
- reservation_name += file->GetPath();
- size_t loaded_size;
- if (!GetLoadedSize(&loaded_size, error_msg)) {
+ uint8_t* vaddr_begin;
+ size_t vaddr_size;
+ if (!GetLoadedAddressRange(&vaddr_begin, &vaddr_size, error_msg)) {
DCHECK(!error_msg->empty());
return false;
}
- MemMap reserve = MemMap::MapAnonymous(reservation_name.c_str(),
- reserve_base_override,
- loaded_size,
- PROT_NONE,
- low_4gb,
- error_msg);
- if (!reserve.IsValid()) {
+ std::string reservation_name = "ElfFile reservation for " + file->GetPath();
+ MemMap local_reservation = MemMap::MapAnonymous(
+ reservation_name.c_str(),
+ (reservation != nullptr) ? reservation->Begin() : nullptr,
+ vaddr_size,
+ PROT_NONE,
+ low_4gb,
+ /* reuse */ false,
+ reservation,
+ error_msg);
+ if (!local_reservation.IsValid()) {
*error_msg = StringPrintf("Failed to allocate %s: %s",
- reservation_name.c_str(), error_msg->c_str());
+ reservation_name.c_str(),
+ error_msg->c_str());
return false;
}
reserved = true;
- // Base address is the difference of actual mapped location and the p_vaddr
- base_address_ = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(reserve.Begin())
- - reinterpret_cast<uintptr_t>(reserve_base));
+ // Base address is the difference of actual mapped location and the vaddr_begin.
+ base_address_ = reinterpret_cast<uint8_t*>(
+ static_cast<uintptr_t>(local_reservation.Begin() - vaddr_begin));
// By adding the p_vaddr of a section/symbol to base_address_ we will always get the
// dynamic memory address of where that object is actually mapped
//
// TODO: base_address_ needs to be calculated in ::Open, otherwise
// FindDynamicSymbolAddress returns the wrong values until Load is called.
- segments_.push_back(std::move(reserve));
+ segments_.push_back(std::move(local_reservation));
}
// empty segment, nothing to map
if (program_header->p_memsz == 0) {
@@ -1239,9 +1237,10 @@ bool ElfFileImpl<ElfTypes>::Load(File* file,
flags,
file->Fd(),
program_header->p_offset,
- /*low4_gb*/false,
- /*reuse*/true, // implies MAP_FIXED
+ /* low4_gb */ false,
file->GetPath().c_str(),
+ /* reuse */ true, // implies MAP_FIXED
+ /* reservation */ nullptr,
error_msg);
if (!segment.IsValid()) {
*error_msg = StringPrintf("Failed to map ELF file segment %d from %s: %s",
@@ -1265,6 +1264,7 @@ bool ElfFileImpl<ElfTypes>::Load(File* file,
prot,
/* low_4gb */ false,
/* reuse */ true,
+ /* reservation */ nullptr,
error_msg);
if (!segment.IsValid()) {
*error_msg = StringPrintf("Failed to map zero-initialized ELF file segment %d from %s: %s",
@@ -1704,8 +1704,7 @@ ElfFile* ElfFile::Open(File* file,
bool writable,
bool program_header_only,
bool low_4gb,
- std::string* error_msg,
- uint8_t* requested_base) {
+ /*out*/std::string* error_msg) {
if (file->GetLength() < EI_NIDENT) {
*error_msg = StringPrintf("File %s is too short to be a valid ELF file",
file->GetPath().c_str());
@@ -1728,8 +1727,7 @@ ElfFile* ElfFile::Open(File* file,
writable,
program_header_only,
low_4gb,
- error_msg,
- requested_base);
+ error_msg);
if (elf_file_impl == nullptr) {
return nullptr;
}
@@ -1739,8 +1737,7 @@ ElfFile* ElfFile::Open(File* file,
writable,
program_header_only,
low_4gb,
- error_msg,
- requested_base);
+ error_msg);
if (elf_file_impl == nullptr) {
return nullptr;
}
@@ -1754,7 +1751,7 @@ ElfFile* ElfFile::Open(File* file,
}
}
-ElfFile* ElfFile::Open(File* file, int mmap_prot, int mmap_flags, std::string* error_msg) {
+ElfFile* ElfFile::Open(File* file, int mmap_prot, int mmap_flags, /*out*/std::string* error_msg) {
// low_4gb support not required for this path.
constexpr bool low_4gb = false;
if (file->GetLength() < EI_NIDENT) {
@@ -1811,8 +1808,12 @@ ElfFile* ElfFile::Open(File* file, int mmap_prot, int mmap_flags, std::string* e
return elf32_->func(__VA_ARGS__); \
}
-bool ElfFile::Load(File* file, bool executable, bool low_4gb, std::string* error_msg) {
- DELEGATE_TO_IMPL(Load, file, executable, low_4gb, error_msg);
+bool ElfFile::Load(File* file,
+ bool executable,
+ bool low_4gb,
+ /*inout*/MemMap* reservation,
+ /*out*/std::string* error_msg) {
+ DELEGATE_TO_IMPL(Load, file, executable, low_4gb, reservation, error_msg);
}
const uint8_t* ElfFile::FindDynamicSymbolAddress(const std::string& symbol_name) const {
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index ab9e6fa2ec..8da7e1a29c 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -26,6 +26,9 @@
#include "./elf.h"
namespace art {
+
+class MemMap;
+
template <typename ElfTypes>
class ElfFileImpl;
@@ -42,18 +45,21 @@ class ElfFile {
bool writable,
bool program_header_only,
bool low_4gb,
- std::string* error_msg,
- uint8_t* requested_base = nullptr); // TODO: move arg to before error_msg.
+ /*out*/std::string* error_msg);
// Open with specific mmap flags, Always maps in the whole file, not just the
// program header sections.
static ElfFile* Open(File* file,
int mmap_prot,
int mmap_flags,
- std::string* error_msg);
+ /*out*/std::string* error_msg);
~ElfFile();
// Load segments into memory based on PT_LOAD program headers
- bool Load(File* file, bool executable, bool low_4gb, std::string* error_msg);
+ bool Load(File* file,
+ bool executable,
+ bool low_4gb,
+ /*inout*/MemMap* reservation,
+ /*out*/std::string* error_msg);
const uint8_t* FindDynamicSymbolAddress(const std::string& symbol_name) const;
diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h
index 58c38a4436..b55b60f2dc 100644
--- a/runtime/elf_file_impl.h
+++ b/runtime/elf_file_impl.h
@@ -48,13 +48,12 @@ class ElfFileImpl {
bool writable,
bool program_header_only,
bool low_4gb,
- std::string* error_msg,
- uint8_t* requested_base = nullptr);
+ /*out*/std::string* error_msg);
static ElfFileImpl* Open(File* file,
int mmap_prot,
int mmap_flags,
bool low_4gb,
- std::string* error_msg);
+ /*out*/std::string* error_msg);
~ElfFileImpl();
const std::string& GetFilePath() const {
@@ -115,7 +114,11 @@ class ElfFileImpl {
// Load segments into memory based on PT_LOAD program headers.
// executable is true at run time, false at compile time.
- bool Load(File* file, bool executable, bool low_4gb, std::string* error_msg);
+ bool Load(File* file,
+ bool executable,
+ bool low_4gb,
+ /*inout*/MemMap* reservation,
+ /*out*/std::string* error_msg);
bool Fixup(Elf_Addr base_address);
bool FixupDynamic(Elf_Addr base_address);
@@ -131,7 +134,11 @@ class ElfFileImpl {
bool Strip(File* file, std::string* error_msg);
private:
- ElfFileImpl(File* file, bool writable, bool program_header_only, uint8_t* requested_base);
+ ElfFileImpl(File* file, bool writable, bool program_header_only);
+
+ bool GetLoadedAddressRange(/*out*/uint8_t** vaddr_begin,
+ /*out*/size_t* vaddr_size,
+ /*out*/std::string* error_msg) const;
bool Setup(File* file, int prot, int flags, bool low_4gb, std::string* error_msg);
@@ -217,9 +224,6 @@ class ElfFileImpl {
SymbolTable* symtab_symbol_table_;
SymbolTable* dynsym_symbol_table_;
- // Override the 'base' p_vaddr in the first LOAD segment with this value (if non-null).
- uint8_t* requested_base_;
-
DISALLOW_COPY_AND_ASSIGN(ElfFileImpl);
};
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 3ea88d6ec7..b4453d97b2 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -890,6 +890,21 @@ void ConcurrentCopying::MarkingPhase() {
}
// Scan all of the objects on dirty cards in unevac from space, and non moving space. These
// are from previous GCs and may reference things in the from space.
+ //
+ // Note that we do not need to process the large-object space (the only discontinuous space)
+ // as it contains only large string objects and large primitive array objects, that have no
+ // reference to other objects, except their class. There is no need to scan these large
+ // objects, as the String class and the primitive array classes are expected to never move
+ // during a minor (young-generation) collection:
+ // - In the case where we run with a boot image, these classes are part of the image space,
+ // which is an immune space.
+ // - In the case where we run without a boot image, these classes are allocated in the region
+ // space (main space), but they are not expected to move during a minor collection (this
+ // would only happen if those classes were allocated between a major and a minor
+ // collections, which is unlikely -- we don't expect any GC to happen before these
+ // fundamental classes are initialized). Note that these classes could move during a major
+ // collection though, but this is fine: in that case, the whole heap is traced and the card
+ // table logic below is not used.
Runtime::Current()->GetHeap()->GetCardTable()->Scan<false>(
space->GetMarkBitmap(),
space->Begin(),
@@ -2333,10 +2348,13 @@ void ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace(mirror::Object* o
// If `ref` is on the allocation stack, then it may not be
// marked live, but considered marked/alive (but not
// necessarily on the live stack).
- CHECK(IsOnAllocStack(ref)) << "Unmarked ref that's not on the allocation stack."
- << " obj=" << obj
- << " ref=" << ref
- << " is_los=" << std::boolalpha << is_los << std::noboolalpha;
+ CHECK(IsOnAllocStack(ref))
+ << "Unmarked ref that's not on the allocation stack."
+ << " obj=" << obj
+ << " ref=" << ref
+ << " is_los=" << std::boolalpha << is_los << std::noboolalpha
+ << " is_marking=" << std::boolalpha << is_marking_ << std::noboolalpha
+ << " young_gen=" << std::boolalpha << young_gen_ << std::noboolalpha;
}
}
}
diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc
index 677e3f3a05..7bd87bda7a 100644
--- a/runtime/gc/collector/immune_spaces_test.cc
+++ b/runtime/gc/collector/immune_spaces_test.cc
@@ -41,13 +41,13 @@ class DummyOatFile : public OatFile {
class DummyImageSpace : public space::ImageSpace {
public:
DummyImageSpace(MemMap&& map,
- accounting::ContinuousSpaceBitmap* live_bitmap,
+ std::unique_ptr<accounting::ContinuousSpaceBitmap> live_bitmap,
std::unique_ptr<DummyOatFile>&& oat_file,
MemMap&& oat_map)
: ImageSpace("DummyImageSpace",
/*image_location*/"",
std::move(map),
- live_bitmap,
+ std::move(live_bitmap),
map.End()),
oat_map_(std::move(oat_map)) {
oat_file_ = std::move(oat_file);
@@ -130,7 +130,7 @@ class ImmuneSpacesTest : public CommonRuntimeTest {
ImageHeader::kStorageModeUncompressed,
/*storage_size*/0u);
return new DummyImageSpace(std::move(map),
- live_bitmap.release(),
+ std::move(live_bitmap),
std::move(oat_file),
std::move(oat_map));
}
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index b74071b8aa..589e9a4826 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -312,24 +312,63 @@ Heap::Heap(size_t initial_size,
ChangeCollector(desired_collector_type_);
live_bitmap_.reset(new accounting::HeapBitmap(this));
mark_bitmap_.reset(new accounting::HeapBitmap(this));
- // Requested begin for the alloc space, to follow the mapped image and oat files
- uint8_t* requested_alloc_space_begin = nullptr;
- if (foreground_collector_type_ == kCollectorTypeCC) {
- // Need to use a low address so that we can allocate a contiguous 2 * Xmx space when there's no
- // image (dex2oat for target).
- requested_alloc_space_begin = kPreferredAllocSpaceBegin;
+
+ // We don't have hspace compaction enabled with GSS or CC.
+ if (foreground_collector_type_ == kCollectorTypeGSS ||
+ foreground_collector_type_ == kCollectorTypeCC) {
+ use_homogeneous_space_compaction_for_oom_ = false;
+ }
+ bool support_homogeneous_space_compaction =
+ background_collector_type_ == gc::kCollectorTypeHomogeneousSpaceCompact ||
+ use_homogeneous_space_compaction_for_oom_;
+ // We may use the same space the main space for the non moving space if we don't need to compact
+ // from the main space.
+ // This is not the case if we support homogeneous compaction or have a moving background
+ // collector type.
+ bool separate_non_moving_space = is_zygote ||
+ support_homogeneous_space_compaction || IsMovingGc(foreground_collector_type_) ||
+ IsMovingGc(background_collector_type_);
+ if (foreground_collector_type_ == kCollectorTypeGSS) {
+ separate_non_moving_space = false;
}
+ // Requested begin for the alloc space, to follow the mapped image and oat files
+ uint8_t* request_begin = nullptr;
+ // Calculate the extra space required after the boot image, see allocations below.
+ size_t heap_reservation_size = separate_non_moving_space
+ ? non_moving_space_capacity
+ : ((is_zygote && foreground_collector_type_ != kCollectorTypeCC) ? capacity_ : 0u);
+ heap_reservation_size = RoundUp(heap_reservation_size, kPageSize);
// Load image space(s).
std::vector<std::unique_ptr<space::ImageSpace>> boot_image_spaces;
+ MemMap heap_reservation;
if (space::ImageSpace::LoadBootImage(image_file_name,
image_instruction_set,
+ heap_reservation_size,
&boot_image_spaces,
- &requested_alloc_space_begin)) {
+ &heap_reservation)) {
+ DCHECK_EQ(heap_reservation_size, heap_reservation.IsValid() ? heap_reservation.Size() : 0u);
+ DCHECK(!boot_image_spaces.empty());
+ request_begin = boot_image_spaces.back()->GetImageHeader().GetOatFileEnd();
+ DCHECK(!heap_reservation.IsValid() || request_begin == heap_reservation.Begin())
+ << "request_begin=" << static_cast<const void*>(request_begin)
+ << " heap_reservation.Begin()=" << static_cast<const void*>(heap_reservation.Begin());
for (std::unique_ptr<space::ImageSpace>& space : boot_image_spaces) {
boot_image_spaces_.push_back(space.get());
AddSpace(space.release());
}
+ } else {
+ if (foreground_collector_type_ == kCollectorTypeCC) {
+ // Need to use a low address so that we can allocate a contiguous 2 * Xmx space
+ // when there's no image (dex2oat for target).
+ request_begin = kPreferredAllocSpaceBegin;
+ }
+ // Gross hack to make dex2oat deterministic.
+ if (foreground_collector_type_ == kCollectorTypeMS && Runtime::Current()->IsAotCompiler()) {
+ // Currently only enabled for MS collector since that is what the deterministic dex2oat uses.
+ // b/26849108
+ request_begin = reinterpret_cast<uint8_t*>(kAllocSpaceBeginForDeterministicAoT);
+ }
}
/*
@@ -345,39 +384,10 @@ Heap::Heap(size_t initial_size,
+-main alloc space2 / bump space 2 (capacity_)+-
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
*/
- // We don't have hspace compaction enabled with GSS or CC.
- if (foreground_collector_type_ == kCollectorTypeGSS ||
- foreground_collector_type_ == kCollectorTypeCC) {
- use_homogeneous_space_compaction_for_oom_ = false;
- }
- bool support_homogeneous_space_compaction =
- background_collector_type_ == gc::kCollectorTypeHomogeneousSpaceCompact ||
- use_homogeneous_space_compaction_for_oom_;
- // We may use the same space the main space for the non moving space if we don't need to compact
- // from the main space.
- // This is not the case if we support homogeneous compaction or have a moving background
- // collector type.
- bool separate_non_moving_space = is_zygote ||
- support_homogeneous_space_compaction || IsMovingGc(foreground_collector_type_) ||
- IsMovingGc(background_collector_type_);
- if (foreground_collector_type_ == kCollectorTypeGSS) {
- separate_non_moving_space = false;
- }
+
MemMap main_mem_map_1;
MemMap main_mem_map_2;
- // Gross hack to make dex2oat deterministic.
- if (foreground_collector_type_ == kCollectorTypeMS &&
- requested_alloc_space_begin == nullptr &&
- Runtime::Current()->IsAotCompiler()) {
- // Currently only enabled for MS collector since that is what the deterministic dex2oat uses.
- // b/26849108
- requested_alloc_space_begin = reinterpret_cast<uint8_t*>(kAllocSpaceBeginForDeterministicAoT);
- }
- uint8_t* request_begin = requested_alloc_space_begin;
- if (request_begin != nullptr && separate_non_moving_space) {
- request_begin += non_moving_space_capacity;
- }
std::string error_str;
MemMap non_moving_space_mem_map;
if (separate_non_moving_space) {
@@ -388,9 +398,16 @@ Heap::Heap(size_t initial_size,
const char* space_name = is_zygote ? kZygoteSpaceName : kNonMovingSpaceName;
// Reserve the non moving mem map before the other two since it needs to be at a specific
// address.
- non_moving_space_mem_map = MapAnonymousPreferredAddress(
- space_name, requested_alloc_space_begin, non_moving_space_capacity, &error_str);
+ DCHECK_EQ(heap_reservation.IsValid(), !boot_image_spaces_.empty());
+ if (heap_reservation.IsValid()) {
+ non_moving_space_mem_map = heap_reservation.RemapAtEnd(
+ heap_reservation.Begin(), space_name, PROT_READ | PROT_WRITE, &error_str);
+ } else {
+ non_moving_space_mem_map = MapAnonymousPreferredAddress(
+ space_name, request_begin, non_moving_space_capacity, &error_str);
+ }
CHECK(non_moving_space_mem_map.IsValid()) << error_str;
+ DCHECK(!heap_reservation.IsValid());
// Try to reserve virtual memory at a lower address if we have a separate non moving space.
request_begin = kPreferredAllocSpaceBegin + non_moving_space_capacity;
}
@@ -404,14 +421,19 @@ Heap::Heap(size_t initial_size,
// If no separate non-moving space and we are the zygote, the main space must come right
// after the image space to avoid a gap. This is required since we want the zygote space to
// be adjacent to the image space.
- main_mem_map_1 = MemMap::MapAnonymous(kMemMapSpaceName[0],
- request_begin,
- capacity_,
- PROT_READ | PROT_WRITE,
- /* low_4gb */ true,
- &error_str);
+ DCHECK_EQ(heap_reservation.IsValid(), !boot_image_spaces_.empty());
+ main_mem_map_1 = MemMap::MapAnonymous(
+ kMemMapSpaceName[0],
+ request_begin,
+ capacity_,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ true,
+ /* reuse */ false,
+ heap_reservation.IsValid() ? &heap_reservation : nullptr,
+ &error_str);
}
CHECK(main_mem_map_1.IsValid()) << error_str;
+ DCHECK(!heap_reservation.IsValid());
}
if (support_homogeneous_space_compaction ||
background_collector_type_ == kCollectorTypeSS ||
@@ -437,7 +459,7 @@ Heap::Heap(size_t initial_size,
/* can_move_objects */ false);
non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity());
CHECK(non_moving_space_ != nullptr) << "Failed creating non moving space "
- << requested_alloc_space_begin;
+ << non_moving_space_mem_map.Begin();
AddSpace(non_moving_space_);
}
// Create other spaces based on whether or not we have a moving GC.
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 71786279e2..f308f63386 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -63,7 +63,7 @@ Atomic<uint32_t> ImageSpace::bitmap_index_(0);
ImageSpace::ImageSpace(const std::string& image_filename,
const char* image_location,
MemMap&& mem_map,
- accounting::ContinuousSpaceBitmap* live_bitmap,
+ std::unique_ptr<accounting::ContinuousSpaceBitmap> live_bitmap,
uint8_t* end)
: MemMapSpace(image_filename,
std::move(mem_map),
@@ -71,10 +71,10 @@ ImageSpace::ImageSpace(const std::string& image_filename,
end,
end,
kGcRetentionPolicyNeverCollect),
+ live_bitmap_(std::move(live_bitmap)),
oat_file_non_owned_(nullptr),
image_location_(image_location) {
- DCHECK(live_bitmap != nullptr);
- live_bitmap_.reset(live_bitmap);
+ DCHECK(live_bitmap_ != nullptr);
}
static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) {
@@ -480,52 +480,16 @@ std::ostream& operator<<(std::ostream& os, const RelocationRange& reloc) {
}
// Helper class encapsulating loading, so we can access private ImageSpace members (this is a
-// friend class), but not declare functions in the header.
+// nested class), but not declare functions in the header.
class ImageSpace::Loader {
public:
- static std::unique_ptr<ImageSpace> Load(const std::string& image_location,
- const std::string& image_filename,
- bool is_zygote,
- bool is_global_cache,
- bool validate_oat_file,
- std::string* error_msg)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- // Should this be a RDWR lock? This is only a defensive measure, as at
- // this point the image should exist.
- // However, only the zygote can write into the global dalvik-cache, so
- // restrict to zygote processes, or any process that isn't using
- // /data/dalvik-cache (which we assume to be allowed to write there).
- const bool rw_lock = is_zygote || !is_global_cache;
-
- // Note that we must not use the file descriptor associated with
- // ScopedFlock::GetFile to Init the image file. We want the file
- // descriptor (and the associated exclusive lock) to be released when
- // we leave Create.
- ScopedFlock image = LockedFile::Open(image_filename.c_str(),
- rw_lock ? (O_CREAT | O_RDWR) : O_RDONLY /* flags */,
- true /* block */,
- error_msg);
-
- VLOG(startup) << "Using image file " << image_filename.c_str() << " for image location "
- << image_location;
- // If we are in /system we can assume the image is good. We can also
- // assume this if we are using a relocated image (i.e. image checksum
- // matches) since this is only different by the offset. We need this to
- // make sure that host tests continue to work.
- // Since we are the boot image, pass null since we load the oat file from the boot image oat
- // file name.
- return Init(image_filename.c_str(),
- image_location.c_str(),
- validate_oat_file,
- /* oat_file */nullptr,
- error_msg);
- }
-
static std::unique_ptr<ImageSpace> Init(const char* image_filename,
const char* image_location,
bool validate_oat_file,
const OatFile* oat_file,
- std::string* error_msg)
+ /*inout*/MemMap* image_reservation,
+ /*inout*/MemMap* oat_reservation,
+ /*out*/std::string* error_msg)
REQUIRES_SHARED(Locks::mutator_lock_) {
CHECK(image_filename != nullptr);
CHECK(image_location != nullptr);
@@ -616,22 +580,27 @@ class ImageSpace::Loader {
// image at the image begin, the amount of fixup work required is minimized.
// If it is pic we will retry with error_msg for the failure case. Pass a null error_msg to
// avoid reading proc maps for a mapping failure and slowing everything down.
- map = LoadImageFile(image_filename,
- image_location,
- *image_header,
- image_header->GetImageBegin(),
- file->Fd(),
- logger,
- image_header->IsPic() ? nullptr : error_msg);
+ // For the boot image, we have already reserved the memory and we load the image
+ // into the `image_reservation`.
+ map = LoadImageFile(
+ image_filename,
+ image_location,
+ *image_header,
+ image_header->GetImageBegin(),
+ file->Fd(),
+ logger,
+ image_reservation,
+ (image_reservation == nullptr && image_header->IsPic()) ? nullptr : error_msg);
// If the header specifies PIC mode, we can also map at a random low_4gb address since we can
// relocate in-place.
- if (!map.IsValid() && image_header->IsPic()) {
+ if (!map.IsValid() && image_reservation == nullptr && image_header->IsPic()) {
map = LoadImageFile(image_filename,
image_location,
*image_header,
/* address */ nullptr,
file->Fd(),
logger,
+ /* image_reservation */ nullptr,
error_msg);
}
// Were we able to load something and continue?
@@ -641,15 +610,13 @@ class ImageSpace::Loader {
}
DCHECK_EQ(0, memcmp(image_header, map.Begin(), sizeof(ImageHeader)));
- MemMap image_bitmap_map = MemMap::MapFileAtAddress(nullptr,
- bitmap_section.Size(),
- PROT_READ, MAP_PRIVATE,
- file->Fd(),
- image_bitmap_offset,
- /*low_4gb*/false,
- /*reuse*/false,
- image_filename,
- error_msg);
+ MemMap image_bitmap_map = MemMap::MapFile(bitmap_section.Size(),
+ PROT_READ, MAP_PRIVATE,
+ file->Fd(),
+ image_bitmap_offset,
+ /* low_4gb */ false,
+ image_filename,
+ error_msg);
if (!image_bitmap_map.IsValid()) {
*error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str());
return nullptr;
@@ -694,7 +661,7 @@ class ImageSpace::Loader {
std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename,
image_location,
std::move(map),
- bitmap.release(),
+ std::move(bitmap),
image_end));
// VerifyImageAllocations() will be called later in Runtime::Init()
@@ -704,7 +671,7 @@ class ImageSpace::Loader {
// set yet at this point.
if (oat_file == nullptr) {
TimingLogger::ScopedTiming timing("OpenOatFile", &logger);
- space->oat_file_ = OpenOatFile(*space, image_filename, error_msg);
+ space->oat_file_ = OpenOatFile(*space, image_filename, oat_reservation, error_msg);
if (space->oat_file_ == nullptr) {
DCHECK(!error_msg->empty());
return nullptr;
@@ -787,7 +754,8 @@ class ImageSpace::Loader {
uint8_t* address,
int fd,
TimingLogger& logger,
- std::string* error_msg) {
+ /*inout*/MemMap* image_reservation,
+ /*out*/std::string* error_msg) {
TimingLogger::ScopedTiming timing("MapImageFile", &logger);
const ImageHeader::StorageMode storage_mode = image_header.GetStorageMode();
if (storage_mode == ImageHeader::kStorageModeUncompressed) {
@@ -796,10 +764,11 @@ class ImageSpace::Loader {
PROT_READ | PROT_WRITE,
MAP_PRIVATE,
fd,
- 0,
- /*low_4gb*/true,
- /*reuse*/false,
+ /* start */ 0,
+ /* low_4gb */ true,
image_filename,
+ /* reuse */ false,
+ image_reservation,
error_msg);
}
@@ -817,7 +786,9 @@ class ImageSpace::Loader {
address,
image_header.GetImageSize(),
PROT_READ | PROT_WRITE,
- /*low_4gb*/ true,
+ /* low_4gb */ true,
+ /* reuse */ false,
+ image_reservation,
error_msg);
if (map.IsValid()) {
const size_t stored_size = image_header.GetDataSize();
@@ -826,8 +797,8 @@ class ImageSpace::Loader {
PROT_READ,
MAP_PRIVATE,
fd,
- /*offset*/0,
- /*low_4gb*/false,
+ /* offset */ 0,
+ /* low_4gb */ false,
image_filename,
error_msg);
if (!temp_map.IsValid()) {
@@ -1396,6 +1367,7 @@ class ImageSpace::Loader {
static std::unique_ptr<OatFile> OpenOatFile(const ImageSpace& image,
const char* image_path,
+ /*inout*/MemMap* oat_reservation,
std::string* error_msg) {
const ImageHeader& image_header = image.GetImageHeader();
std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_path);
@@ -1406,10 +1378,10 @@ class ImageSpace::Loader {
oat_filename,
oat_filename,
image_header.GetOatDataBegin(),
- image_header.GetOatFileBegin(),
!Runtime::Current()->IsAotCompiler(),
- /*low_4gb*/false,
- nullptr,
+ /* low_4gb */ false,
+ /* abs_dex_location */ nullptr,
+ oat_reservation,
error_msg));
if (oat_file == nullptr) {
*error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
@@ -1489,29 +1461,61 @@ class ImageSpace::BootImageLoader {
return cache_filename_;
}
- bool LoadFromSystem(/*out*/ std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
- /*out*/ uint8_t** oat_file_end,
- /*out*/ std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool LoadFromSystem(size_t extra_reservation_size,
+ /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/MemMap* extra_reservation,
+ /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
std::string filename = GetSystemImageFilename(image_location_.c_str(), image_isa_);
std::vector<std::string> locations;
if (!GetBootClassPathImageLocations(image_location_, filename, &locations, error_msg)) {
return false;
}
+ uint32_t image_start;
+ uint32_t image_end;
+ uint32_t oat_end;
+ if (!GetBootImageAddressRange(filename, &image_start, &image_end, &oat_end, error_msg)) {
+ return false;
+ }
+ if (locations.size() > 1u) {
+ std::string last_filename = GetSystemImageFilename(locations.back().c_str(), image_isa_);
+ uint32_t dummy;
+ if (!GetBootImageAddressRange(last_filename, &dummy, &image_end, &oat_end, error_msg)) {
+ return false;
+ }
+ }
+ MemMap image_reservation;
+ MemMap oat_reservation;
+ MemMap local_extra_reservation;
+ if (!ReserveBootImageMemory(image_start,
+ image_end,
+ oat_end,
+ extra_reservation_size,
+ &image_reservation,
+ &oat_reservation,
+ &local_extra_reservation,
+ error_msg)) {
+ return false;
+ }
+
std::vector<std::unique_ptr<ImageSpace>> spaces;
spaces.reserve(locations.size());
for (const std::string& location : locations) {
filename = GetSystemImageFilename(location.c_str(), image_isa_);
- spaces.push_back(Loader::Load(location,
- filename,
- is_zygote_,
- is_global_cache_,
- /* validate_oat_file */ false,
- error_msg));
+ spaces.push_back(Load(location,
+ filename,
+ /* validate_oat_file */ false,
+ &image_reservation,
+ &oat_reservation,
+ error_msg));
if (spaces.back() == nullptr) {
return false;
}
}
- *oat_file_end = GetOatFileEnd(spaces);
+ if (!CheckReservationsExhausted(image_reservation, oat_reservation, error_msg)) {
+ return false;
+ }
+
+ *extra_reservation = std::move(local_extra_reservation);
boot_image_spaces->swap(spaces);
return true;
}
@@ -1519,14 +1523,48 @@ class ImageSpace::BootImageLoader {
bool LoadFromDalvikCache(
bool validate_system_checksums,
bool validate_oat_file,
- /*out*/ std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
- /*out*/ uint8_t** oat_file_end,
- /*out*/ std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
+ size_t extra_reservation_size,
+ /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/MemMap* extra_reservation,
+ /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(DalvikCacheExists());
std::vector<std::string> locations;
if (!GetBootClassPathImageLocations(image_location_, cache_filename_, &locations, error_msg)) {
return false;
}
+ uint32_t image_start;
+ uint32_t image_end;
+ uint32_t oat_end;
+ if (!GetBootImageAddressRange(cache_filename_, &image_start, &image_end, &oat_end, error_msg)) {
+ return false;
+ }
+ if (locations.size() > 1u) {
+ std::string last_filename;
+ if (!GetDalvikCacheFilename(locations.back().c_str(),
+ dalvik_cache_.c_str(),
+ &last_filename,
+ error_msg)) {
+ return false;
+ }
+ uint32_t dummy;
+ if (!GetBootImageAddressRange(last_filename, &dummy, &image_end, &oat_end, error_msg)) {
+ return false;
+ }
+ }
+ MemMap image_reservation;
+ MemMap oat_reservation;
+ MemMap local_extra_reservation;
+ if (!ReserveBootImageMemory(image_start,
+ image_end,
+ oat_end,
+ extra_reservation_size,
+ &image_reservation,
+ &oat_reservation,
+ &local_extra_reservation,
+ error_msg)) {
+ return false;
+ }
+
std::vector<std::unique_ptr<ImageSpace>> spaces;
spaces.reserve(locations.size());
for (const std::string& location : locations) {
@@ -1534,12 +1572,12 @@ class ImageSpace::BootImageLoader {
if (!GetDalvikCacheFilename(location.c_str(), dalvik_cache_.c_str(), &filename, error_msg)) {
return false;
}
- spaces.push_back(Loader::Load(location,
- filename,
- is_zygote_,
- is_global_cache_,
- validate_oat_file,
- error_msg));
+ spaces.push_back(Load(location,
+ filename,
+ validate_oat_file,
+ &image_reservation,
+ &oat_reservation,
+ error_msg));
if (spaces.back() == nullptr) {
return false;
}
@@ -1560,12 +1598,56 @@ class ImageSpace::BootImageLoader {
}
}
}
- *oat_file_end = GetOatFileEnd(spaces);
+ if (!CheckReservationsExhausted(image_reservation, oat_reservation, error_msg)) {
+ return false;
+ }
+
+ *extra_reservation = std::move(local_extra_reservation);
boot_image_spaces->swap(spaces);
return true;
}
private:
+ std::unique_ptr<ImageSpace> Load(const std::string& image_location,
+ const std::string& image_filename,
+ bool validate_oat_file,
+ /*inout*/MemMap* image_reservation,
+ /*inout*/MemMap* oat_reservation,
+ /*out*/std::string* error_msg)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Should this be a RDWR lock? This is only a defensive measure, as at
+ // this point the image should exist.
+ // However, only the zygote can write into the global dalvik-cache, so
+ // restrict to zygote processes, or any process that isn't using
+ // /data/dalvik-cache (which we assume to be allowed to write there).
+ const bool rw_lock = is_zygote_ || !is_global_cache_;
+
+ // Note that we must not use the file descriptor associated with
+ // ScopedFlock::GetFile to Init the image file. We want the file
+ // descriptor (and the associated exclusive lock) to be released when
+ // we leave Create.
+ ScopedFlock image = LockedFile::Open(image_filename.c_str(),
+ rw_lock ? (O_CREAT | O_RDWR) : O_RDONLY /* flags */,
+ true /* block */,
+ error_msg);
+
+ VLOG(startup) << "Using image file " << image_filename.c_str() << " for image location "
+ << image_location;
+ // If we are in /system we can assume the image is good. We can also
+ // assume this if we are using a relocated image (i.e. image checksum
+ // matches) since this is only different by the offset. We need this to
+ // make sure that host tests continue to work.
+ // Since we are the boot image, pass null since we load the oat file from the boot image oat
+ // file name.
+ return Loader::Init(image_filename.c_str(),
+ image_location.c_str(),
+ validate_oat_file,
+ /* oat_file */ nullptr,
+ image_reservation,
+ oat_reservation,
+ error_msg);
+ }
+
// Extract boot class path from oat file associated with `image_filename`
// and list all associated image locations.
static bool GetBootClassPathImageLocations(const std::string& image_location,
@@ -1577,10 +1659,10 @@ class ImageSpace::BootImageLoader {
oat_filename,
oat_filename,
/* requested_base */ nullptr,
- /* oat_file_begin */ nullptr,
/* executable */ false,
/* low_4gb */ false,
/* abs_dex_location */ nullptr,
+ /* reservation */ nullptr,
error_msg));
if (oat_file == nullptr) {
*error_msg = StringPrintf("Failed to open oat file '%s' for image file %s: %s",
@@ -1598,14 +1680,87 @@ class ImageSpace::BootImageLoader {
return true;
}
- uint8_t* GetOatFileEnd(const std::vector<std::unique_ptr<ImageSpace>>& spaces) {
- DCHECK(std::is_sorted(
- spaces.begin(),
- spaces.end(),
- [](const std::unique_ptr<ImageSpace>& lhs, const std::unique_ptr<ImageSpace>& rhs) {
- return lhs->GetOatFileEnd() < rhs->GetOatFileEnd();
- }));
- return AlignUp(spaces.back()->GetOatFileEnd(), kPageSize);
+ bool GetBootImageAddressRange(const std::string& filename,
+ /*out*/uint32_t* start,
+ /*out*/uint32_t* end,
+ /*out*/uint32_t* oat_end,
+ /*out*/std::string* error_msg) {
+ ImageHeader system_hdr;
+ if (!ReadSpecificImageHeader(filename.c_str(), &system_hdr)) {
+ *error_msg = StringPrintf("Cannot read header of %s", filename.c_str());
+ return false;
+ }
+ *start = reinterpret_cast32<uint32_t>(system_hdr.GetImageBegin());
+ CHECK_ALIGNED(*start, kPageSize);
+ *end = RoundUp(*start + system_hdr.GetImageSize(), kPageSize);
+ *oat_end = RoundUp(reinterpret_cast32<uint32_t>(system_hdr.GetOatFileEnd()), kPageSize);
+ return true;
+ }
+
+ bool ReserveBootImageMemory(uint32_t image_start,
+ uint32_t image_end,
+ uint32_t oat_end,
+ size_t extra_reservation_size,
+ /*out*/MemMap* image_reservation,
+ /*out*/MemMap* oat_reservation,
+ /*out*/MemMap* extra_reservation,
+ /*out*/std::string* error_msg) {
+ DCHECK(!image_reservation->IsValid());
+ size_t total_size =
+ dchecked_integral_cast<size_t>(oat_end - image_start) + extra_reservation_size;
+ *image_reservation =
+ MemMap::MapAnonymous("Boot image reservation",
+ reinterpret_cast32<uint8_t*>(image_start),
+ total_size,
+ PROT_NONE,
+ /* low_4gb */ true,
+ /* reuse */ false,
+ /* reservation */ nullptr,
+ error_msg);
+ if (!image_reservation->IsValid()) {
+ return false;
+ }
+ DCHECK(!extra_reservation->IsValid());
+ if (extra_reservation_size != 0u) {
+ DCHECK_ALIGNED(extra_reservation_size, kPageSize);
+ DCHECK_LT(extra_reservation_size, image_reservation->Size());
+ uint8_t* split = image_reservation->End() - extra_reservation_size;
+ *extra_reservation = image_reservation->RemapAtEnd(split,
+ "Boot image extra reservation",
+ PROT_NONE,
+ error_msg);
+ if (!extra_reservation->IsValid()) {
+ return false;
+ }
+ }
+ DCHECK(!oat_reservation->IsValid());
+ *oat_reservation = image_reservation->RemapAtEnd(reinterpret_cast32<uint8_t*>(image_end),
+ "Boot image oat reservation",
+ PROT_NONE,
+ error_msg);
+ if (!oat_reservation->IsValid()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool CheckReservationsExhausted(const MemMap& image_reservation,
+ const MemMap& oat_reservation,
+ /*out*/std::string* error_msg) {
+ if (image_reservation.IsValid()) {
+ *error_msg = StringPrintf("Excessive image reservation after loading boot image: %p-%p",
+ image_reservation.Begin(),
+ image_reservation.End());
+ return false;
+ }
+ if (oat_reservation.IsValid()) {
+ *error_msg = StringPrintf("Excessive oat reservation after loading boot image: %p-%p",
+ image_reservation.Begin(),
+ image_reservation.End());
+ return false;
+ }
+ return true;
}
const std::string& image_location_;
@@ -1657,13 +1812,15 @@ static bool CheckSpace(const std::string& cache_filename, std::string* error_msg
bool ImageSpace::LoadBootImage(
const std::string& image_location,
const InstructionSet image_isa,
- /*out*/ std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
- /*out*/ uint8_t** oat_file_end) {
+ size_t extra_reservation_size,
+ /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/MemMap* extra_reservation) {
ScopedTrace trace(__FUNCTION__);
DCHECK(boot_image_spaces != nullptr);
DCHECK(boot_image_spaces->empty());
- DCHECK(oat_file_end != nullptr);
+ DCHECK_ALIGNED(extra_reservation_size, kPageSize);
+ DCHECK(extra_reservation != nullptr);
DCHECK_NE(image_isa, InstructionSet::kNone);
if (image_location.empty()) {
@@ -1720,8 +1877,9 @@ bool ImageSpace::LoadBootImage(
// If we have system image, validate system image checksums, otherwise validate the oat file.
if (loader.LoadFromDalvikCache(/* validate_system_checksums */ loader.HasSystem(),
/* validate_oat_file */ !loader.HasSystem(),
+ extra_reservation_size,
boot_image_spaces,
- oat_file_end,
+ extra_reservation,
&local_error_msg)) {
return true;
}
@@ -1735,7 +1893,10 @@ bool ImageSpace::LoadBootImage(
if (loader.HasSystem() && !relocate) {
std::string local_error_msg;
- if (loader.LoadFromSystem(boot_image_spaces, oat_file_end, &local_error_msg)) {
+ if (loader.LoadFromSystem(extra_reservation_size,
+ boot_image_spaces,
+ extra_reservation,
+ &local_error_msg)) {
return true;
}
error_msgs.push_back(local_error_msg);
@@ -1752,8 +1913,9 @@ bool ImageSpace::LoadBootImage(
if (patch_success) {
if (loader.LoadFromDalvikCache(/* validate_system_checksums */ false,
/* validate_oat_file */ false,
+ extra_reservation_size,
boot_image_spaces,
- oat_file_end,
+ extra_reservation,
&local_error_msg)) {
return true;
}
@@ -1777,8 +1939,9 @@ bool ImageSpace::LoadBootImage(
if (compilation_success) {
if (loader.LoadFromDalvikCache(/* validate_system_checksums */ false,
/* validate_oat_file */ false,
+ extra_reservation_size,
boot_image_spaces,
- oat_file_end,
+ extra_reservation,
&local_error_msg)) {
return true;
}
@@ -1834,7 +1997,13 @@ ImageSpace::~ImageSpace() {
std::unique_ptr<ImageSpace> ImageSpace::CreateFromAppImage(const char* image,
const OatFile* oat_file,
std::string* error_msg) {
- return Loader::Init(image, image, /*validate_oat_file*/false, oat_file, /*out*/error_msg);
+ return Loader::Init(image,
+ image,
+ /* validate_oat_file */ false,
+ oat_file,
+ /* image_reservation */ nullptr,
+ /* oat_reservation */ nullptr,
+ error_msg);
}
const OatFile* ImageSpace::GetOatFile() const {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 93cf947218..a2490acdbb 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -44,8 +44,9 @@ class ImageSpace : public MemMapSpace {
static bool LoadBootImage(
const std::string& image_location,
const InstructionSet image_isa,
- /*out*/ std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
- /*out*/ uint8_t** oat_file_end) REQUIRES_SHARED(Locks::mutator_lock_);
+ size_t extra_reservation_size,
+ /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/MemMap* extra_reservation) REQUIRES_SHARED(Locks::mutator_lock_);
// Try to open an existing app image space.
static std::unique_ptr<ImageSpace> CreateFromAppImage(const char* image,
@@ -183,7 +184,7 @@ class ImageSpace : public MemMapSpace {
ImageSpace(const std::string& name,
const char* image_location,
MemMap&& mem_map,
- accounting::ContinuousSpaceBitmap* live_bitmap,
+ std::unique_ptr<accounting::ContinuousSpaceBitmap> live_bitmap,
uint8_t* end);
// The OatFile associated with the image during early startup to
diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc
index d93385de3a..299a413432 100644
--- a/runtime/gc/space/image_space_test.cc
+++ b/runtime/gc/space/image_space_test.cc
@@ -41,16 +41,16 @@ TEST_F(DexoptTest, ValidateOatFile) {
args.push_back("--dex-file=" + multidex1);
args.push_back("--dex-file=" + dex2);
args.push_back("--oat-file=" + oat_location);
- ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+ ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
std::unique_ptr<OatFile> oat(OatFile::Open(/* zip_fd */ -1,
oat_location.c_str(),
oat_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
- nullptr,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
+ /* abs_dex_location */ nullptr,
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(oat != nullptr) << error_msg;
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
index 69dae31b37..17b3cd45aa 100644
--- a/runtime/interpreter/interpreter_intrinsics.cc
+++ b/runtime/interpreter/interpreter_intrinsics.cc
@@ -116,10 +116,10 @@ UNARY_INTRINSIC(MterpLongNumberOfLeadingZeros, JAVASTYLE_CLZ, GetVRegLong, SetJ)
UNARY_INTRINSIC(MterpLongNumberOfTrailingZeros, JAVASTYLE_CTZ, GetVRegLong, SetJ);
// java.lang.Long.rotateRight(JI)J
-BINARY_JJ_INTRINSIC(MterpLongRotateRight, (Rot<int64_t, false>), SetJ);
+BINARY_JI_INTRINSIC(MterpLongRotateRight, (Rot<int64_t, false>), SetJ);
// java.lang.Long.rotateLeft(JI)J
-BINARY_JJ_INTRINSIC(MterpLongRotateLeft, (Rot<int64_t, true>), SetJ);
+BINARY_JI_INTRINSIC(MterpLongRotateLeft, (Rot<int64_t, true>), SetJ);
// java.lang.Long.signum(J)I
UNARY_INTRINSIC(MterpLongSignum, Signum, GetVRegLong, SetI);
diff --git a/runtime/interpreter/shadow_frame.h b/runtime/interpreter/shadow_frame.h
index f76b86c94f..0e4cf27e50 100644
--- a/runtime/interpreter/shadow_frame.h
+++ b/runtime/interpreter/shadow_frame.h
@@ -159,14 +159,14 @@ class ShadowFrame {
}
int64_t GetVRegLong(size_t i) const {
- DCHECK_LT(i, NumberOfVRegs());
+ DCHECK_LT(i + 1, NumberOfVRegs());
const uint32_t* vreg = &vregs_[i];
typedef const int64_t unaligned_int64 __attribute__ ((aligned (4)));
return *reinterpret_cast<unaligned_int64*>(vreg);
}
double GetVRegDouble(size_t i) const {
- DCHECK_LT(i, NumberOfVRegs());
+ DCHECK_LT(i + 1, NumberOfVRegs());
const uint32_t* vreg = &vregs_[i];
typedef const double unaligned_double __attribute__ ((aligned (4)));
return *reinterpret_cast<unaligned_double*>(vreg);
@@ -220,7 +220,7 @@ class ShadowFrame {
}
void SetVRegLong(size_t i, int64_t val) {
- DCHECK_LT(i, NumberOfVRegs());
+ DCHECK_LT(i + 1, NumberOfVRegs());
uint32_t* vreg = &vregs_[i];
typedef int64_t unaligned_int64 __attribute__ ((aligned (4)));
*reinterpret_cast<unaligned_int64*>(vreg) = val;
@@ -233,7 +233,7 @@ class ShadowFrame {
}
void SetVRegDouble(size_t i, double val) {
- DCHECK_LT(i, NumberOfVRegs());
+ DCHECK_LT(i + 1, NumberOfVRegs());
uint32_t* vreg = &vregs_[i];
typedef double unaligned_double __attribute__ ((aligned (4)));
*reinterpret_cast<unaligned_double*>(vreg) = val;
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 33adc18673..2b2898c195 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -168,11 +168,6 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity,
ScopedTrace trace(__PRETTY_FUNCTION__);
CHECK_GE(max_capacity, initial_capacity);
- // Generating debug information is for using the Linux perf tool on
- // host which does not work with ashmem.
- // Also, targets linux and fuchsia do not support ashmem.
- bool use_ashmem = !generate_debug_info && !kIsTargetLinux && !kIsTargetFuchsia;
-
// With 'perf', we want a 1-1 mapping between an address and a method.
// We aren't able to keep method pointers live during the instrumentation method entry trampoline
// so we will just disable jit-gc if we are doing that.
@@ -212,8 +207,8 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity,
kProtData,
/* low_4gb */ true,
/* reuse */ false,
- &error_str,
- use_ashmem);
+ /* reservation */ nullptr,
+ &error_str);
if (!data_map.IsValid()) {
std::ostringstream oss;
oss << "Failed to create read write cache: " << error_str << " size=" << max_capacity;
@@ -233,7 +228,7 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity,
uint8_t* divider = data_map.Begin() + data_size;
MemMap code_map = data_map.RemapAtEnd(
- divider, "jit-code-cache", memmap_flags_prot_code | PROT_WRITE, &error_str, use_ashmem);
+ divider, "jit-code-cache", memmap_flags_prot_code | PROT_WRITE, &error_str);
if (!code_map.IsValid()) {
std::ostringstream oss;
oss << "Failed to create read write execute cache: " << error_str << " size=" << max_capacity;
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 72eced2333..02aa1a823a 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1401,7 +1401,10 @@ void Monitor::VisitLocks(StackVisitor* stack_visitor, void (*callback)(mirror::O
// Ask the verifier for the dex pcs of all the monitor-enter instructions corresponding to
// the locks held in this stack frame.
std::vector<verifier::MethodVerifier::DexLockInfo> monitor_enter_dex_pcs;
- verifier::MethodVerifier::FindLocksAtDexPc(m, dex_pc, &monitor_enter_dex_pcs);
+ verifier::MethodVerifier::FindLocksAtDexPc(m,
+ dex_pc,
+ &monitor_enter_dex_pcs,
+ Runtime::Current()->GetTargetSdkVersion());
for (verifier::MethodVerifier::DexLockInfo& dex_lock_info : monitor_enter_dex_pcs) {
// As a debug check, check that dex PC corresponds to a monitor-enter.
if (kIsDebugBuild) {
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 4780aea003..8f84a4bc19 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -102,12 +102,12 @@ class OatFileBase : public OatFile {
const std::string& elf_filename,
const std::string& location,
uint8_t* requested_base,
- uint8_t* oat_file_begin,
bool writable,
bool executable,
bool low_4gb,
const char* abs_dex_location,
- std::string* error_msg);
+ /*inout*/MemMap* reservation, // Where to load if not null.
+ /*out*/std::string* error_msg);
template <typename kOatFileBaseSubType>
static OatFileBase* OpenOatFile(int zip_fd,
@@ -116,12 +116,12 @@ class OatFileBase : public OatFile {
const std::string& vdex_filename,
const std::string& oat_filename,
uint8_t* requested_base,
- uint8_t* oat_file_begin,
bool writable,
bool executable,
bool low_4gb,
const char* abs_dex_location,
- std::string* error_msg);
+ /*inout*/MemMap* reservation, // Where to load if not null.
+ /*out*/std::string* error_msg);
protected:
OatFileBase(const std::string& filename, bool executable) : OatFile(filename, executable) {}
@@ -143,18 +143,18 @@ class OatFileBase : public OatFile {
std::string* error_msg);
virtual bool Load(const std::string& elf_filename,
- uint8_t* oat_file_begin,
bool writable,
bool executable,
bool low_4gb,
- std::string* error_msg) = 0;
+ /*inout*/MemMap* reservation, // Where to load if not null.
+ /*out*/std::string* error_msg) = 0;
virtual bool Load(int oat_fd,
- uint8_t* oat_file_begin,
bool writable,
bool executable,
bool low_4gb,
- std::string* error_msg) = 0;
+ /*inout*/MemMap* reservation, // Where to load if not null.
+ /*out*/std::string* error_msg) = 0;
bool ComputeFields(uint8_t* requested_base,
const std::string& file_path,
@@ -188,21 +188,21 @@ OatFileBase* OatFileBase::OpenOatFile(int zip_fd,
const std::string& elf_filename,
const std::string& location,
uint8_t* requested_base,
- uint8_t* oat_file_begin,
bool writable,
bool executable,
bool low_4gb,
const char* abs_dex_location,
- std::string* error_msg) {
+ /*inout*/MemMap* reservation,
+ /*out*/std::string* error_msg) {
std::unique_ptr<OatFileBase> ret(new kOatFileBaseSubType(location, executable));
ret->PreLoad();
if (!ret->Load(elf_filename,
- oat_file_begin,
writable,
executable,
low_4gb,
+ reservation,
error_msg)) {
return nullptr;
}
@@ -231,19 +231,19 @@ OatFileBase* OatFileBase::OpenOatFile(int zip_fd,
const std::string& vdex_location,
const std::string& oat_location,
uint8_t* requested_base,
- uint8_t* oat_file_begin,
bool writable,
bool executable,
bool low_4gb,
const char* abs_dex_location,
- std::string* error_msg) {
+ /*inout*/MemMap* reservation,
+ /*out*/std::string* error_msg) {
std::unique_ptr<OatFileBase> ret(new kOatFileBaseSubType(oat_location, executable));
if (!ret->Load(oat_fd,
- oat_file_begin,
writable,
executable,
low_4gb,
+ reservation,
error_msg)) {
return nullptr;
}
@@ -923,13 +923,18 @@ class DlOpenOatFile final : public OatFileBase {
void PreLoad() override;
bool Load(const std::string& elf_filename,
- uint8_t* oat_file_begin,
bool writable,
bool executable,
bool low_4gb,
- std::string* error_msg) override;
-
- bool Load(int, uint8_t*, bool, bool, bool, std::string*) {
+ /*inout*/MemMap* reservation, // Where to load if not null.
+ /*out*/std::string* error_msg) override;
+
+ bool Load(int oat_fd ATTRIBUTE_UNUSED,
+ bool writable ATTRIBUTE_UNUSED,
+ bool executable ATTRIBUTE_UNUSED,
+ bool low_4gb ATTRIBUTE_UNUSED,
+ /*inout*/MemMap* reservation ATTRIBUTE_UNUSED,
+ /*out*/std::string* error_msg ATTRIBUTE_UNUSED) override {
return false;
}
@@ -938,8 +943,8 @@ class DlOpenOatFile final : public OatFileBase {
private:
bool Dlopen(const std::string& elf_filename,
- uint8_t* oat_file_begin,
- std::string* error_msg);
+ /*inout*/MemMap* reservation, // Where to load if not null.
+ /*out*/std::string* error_msg);
// On the host, if the same library is loaded again with dlopen the same
// file handle is returned. This differs from the behavior of dlopen on the
@@ -952,12 +957,13 @@ class DlOpenOatFile final : public OatFileBase {
// Guarded by host_dlopen_handles_lock_;
static std::unordered_set<void*> host_dlopen_handles_;
+ // Reservation and dummy memory map objects corresponding to the regions mapped by dlopen.
+ // Note: Must be destroyed after dlclose() as it can hold the owning reservation.
+ std::vector<MemMap> dlopen_mmaps_;
+
// dlopen handle during runtime.
void* dlopen_handle_; // TODO: Unique_ptr with custom deleter.
- // Dummy memory map objects corresponding to the regions mapped by dlopen.
- std::vector<MemMap> dlopen_mmaps_;
-
// The number of shared objects the linker told us about before loading. Used to
// (optimistically) optimize the PreSetup stage (see comment there).
size_t shared_objects_before_;
@@ -975,9 +981,9 @@ void DlOpenOatFile::PreLoad() {
#else
// Count the entries in dl_iterate_phdr we get at this point in time.
struct dl_iterate_context {
- static int callback(struct dl_phdr_info *info ATTRIBUTE_UNUSED,
+ static int callback(dl_phdr_info* info ATTRIBUTE_UNUSED,
size_t size ATTRIBUTE_UNUSED,
- void *data) {
+ void* data) {
reinterpret_cast<dl_iterate_context*>(data)->count++;
return 0; // Continue iteration.
}
@@ -990,11 +996,11 @@ void DlOpenOatFile::PreLoad() {
}
bool DlOpenOatFile::Load(const std::string& elf_filename,
- uint8_t* oat_file_begin,
bool writable,
bool executable,
bool low_4gb,
- std::string* error_msg) {
+ /*inout*/MemMap* reservation, // Where to load if not null.
+ /*out*/std::string* error_msg) {
// Use dlopen only when flagged to do so, and when it's OK to load things executable.
// TODO: Also try when not executable? The issue here could be re-mapping as writable (as
// !executable is a sign that we may want to patch), which may not be allowed for
@@ -1027,19 +1033,19 @@ bool DlOpenOatFile::Load(const std::string& elf_filename,
}
}
- bool success = Dlopen(elf_filename, oat_file_begin, error_msg);
+ bool success = Dlopen(elf_filename, reservation, error_msg);
DCHECK(dlopen_handle_ != nullptr || !success);
return success;
}
bool DlOpenOatFile::Dlopen(const std::string& elf_filename,
- uint8_t* oat_file_begin,
- std::string* error_msg) {
+ /*inout*/MemMap* reservation,
+ /*out*/std::string* error_msg) {
#ifdef __APPLE__
// The dl_iterate_phdr syscall is missing. There is similar API on OSX,
// but let's fallback to the custom loading code for the time being.
- UNUSED(elf_filename, oat_file_begin);
+ UNUSED(elf_filename, reservation);
*error_msg = "Dlopen unsupported on Mac.";
return false;
#else
@@ -1056,15 +1062,85 @@ bool DlOpenOatFile::Dlopen(const std::string& elf_filename,
// times).
ANDROID_DLEXT_FORCE_FIXED_VADDR; // Take a non-zero vaddr as absolute
// (non-pic boot image).
- if (oat_file_begin != nullptr) { //
- extinfo.flags |= ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS; // Use the requested addr if
- extinfo.reserved_addr = oat_file_begin; // vaddr = 0.
- } // (pic boot image).
+ if (reservation != nullptr) {
+ if (!reservation->IsValid()) {
+ *error_msg = StringPrintf("Invalid reservation for %s", elf_filename.c_str());
+ return false;
+ }
+ extinfo.flags |= ANDROID_DLEXT_RESERVED_ADDRESS; // Use the reserved memory range.
+ extinfo.reserved_addr = reservation->Begin();
+ extinfo.reserved_size = reservation->Size();
+ }
dlopen_handle_ = android_dlopen_ext(absolute_path.get(), RTLD_NOW, &extinfo);
+ if (reservation != nullptr && dlopen_handle_ != nullptr) {
+ // Find used pages from the reservation.
+ struct dl_iterate_context {
+ static int callback(dl_phdr_info* info, size_t size ATTRIBUTE_UNUSED, void* data) {
+ auto* context = reinterpret_cast<dl_iterate_context*>(data);
+ static_assert(std::is_same<Elf32_Half, Elf64_Half>::value, "Half must match");
+ using Elf_Half = Elf64_Half;
+
+ // See whether this callback corresponds to the file which we have just loaded.
+ uint8_t* reservation_begin = context->reservation->Begin();
+ bool contained_in_reservation = false;
+ for (Elf_Half i = 0; i < info->dlpi_phnum; i++) {
+ if (info->dlpi_phdr[i].p_type == PT_LOAD) {
+ uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +
+ info->dlpi_phdr[i].p_vaddr);
+ size_t memsz = info->dlpi_phdr[i].p_memsz;
+ size_t offset = static_cast<size_t>(vaddr - reservation_begin);
+ if (offset < context->reservation->Size()) {
+ contained_in_reservation = true;
+ DCHECK_LE(memsz, context->reservation->Size() - offset);
+ } else if (vaddr < reservation_begin) {
+ // Check that there's no overlap with the reservation.
+ DCHECK_LE(memsz, static_cast<size_t>(reservation_begin - vaddr));
+ }
+ break; // It is sufficient to check the first PT_LOAD header.
+ }
+ }
+
+ if (contained_in_reservation) {
+ for (Elf_Half i = 0; i < info->dlpi_phnum; i++) {
+ if (info->dlpi_phdr[i].p_type == PT_LOAD) {
+ uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +
+ info->dlpi_phdr[i].p_vaddr);
+ size_t memsz = info->dlpi_phdr[i].p_memsz;
+ size_t offset = static_cast<size_t>(vaddr - reservation_begin);
+ DCHECK_LT(offset, context->reservation->Size());
+ DCHECK_LE(memsz, context->reservation->Size() - offset);
+ context->max_size = std::max(context->max_size, offset + memsz);
+ }
+ }
+
+ return 1; // Stop iteration and return 1 from dl_iterate_phdr.
+ }
+ return 0; // Continue iteration and return 0 from dl_iterate_phdr when finished.
+ }
+
+ const MemMap* const reservation;
+ size_t max_size = 0u;
+ };
+ dl_iterate_context context = { reservation };
+
+ if (dl_iterate_phdr(dl_iterate_context::callback, &context) == 0) {
+ LOG(FATAL) << "Could not find the shared object mmapped to the reservation.";
+ UNREACHABLE();
+ }
+
+ // Take ownership of the memory used by the shared object. dlopen() does not assume
+ // full ownership of this memory and dlclose() shall just remap it as zero pages with
+ // PROT_NONE. We need to unmap the memory when destroying this oat file.
+ dlopen_mmaps_.push_back(reservation->TakeReservedMemory(context.max_size));
+ }
#else
- UNUSED(oat_file_begin);
static_assert(!kIsTargetBuild || kIsTargetLinux || kIsTargetFuchsia,
"host_dlopen_handles_ will leak handles");
+ if (reservation != nullptr) {
+ *error_msg = StringPrintf("dlopen() into reserved memory is unsupported on host for '%s'.",
+ elf_filename.c_str());
+ return false;
+ }
MutexLock mu(Thread::Current(), *Locks::host_dlopen_handles_lock_);
dlopen_handle_ = dlopen(absolute_path.get(), RTLD_NOW);
if (dlopen_handle_ != nullptr) {
@@ -1092,8 +1168,11 @@ void DlOpenOatFile::PreSetup(const std::string& elf_filename) {
UNREACHABLE();
#else
struct dl_iterate_context {
- static int callback(struct dl_phdr_info *info, size_t /* size */, void *data) {
+ static int callback(dl_phdr_info* info, size_t size ATTRIBUTE_UNUSED, void* data) {
auto* context = reinterpret_cast<dl_iterate_context*>(data);
+ static_assert(std::is_same<Elf32_Half, Elf64_Half>::value, "Half must match");
+ using Elf_Half = Elf64_Half;
+
context->shared_objects_seen++;
if (context->shared_objects_seen < context->shared_objects_before) {
// We haven't been called yet for anything we haven't seen before. Just continue.
@@ -1104,7 +1183,7 @@ void DlOpenOatFile::PreSetup(const std::string& elf_filename) {
// See whether this callback corresponds to the file which we have just loaded.
bool contains_begin = false;
- for (int i = 0; i < info->dlpi_phnum; i++) {
+ for (Elf_Half i = 0; i < info->dlpi_phnum; i++) {
if (info->dlpi_phdr[i].p_type == PT_LOAD) {
uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +
info->dlpi_phdr[i].p_vaddr);
@@ -1117,7 +1196,7 @@ void DlOpenOatFile::PreSetup(const std::string& elf_filename) {
}
// Add dummy mmaps for this file.
if (contains_begin) {
- for (int i = 0; i < info->dlpi_phnum; i++) {
+ for (Elf_Half i = 0; i < info->dlpi_phnum; i++) {
if (info->dlpi_phdr[i].p_type == PT_LOAD) {
uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +
info->dlpi_phdr[i].p_vaddr);
@@ -1163,13 +1242,12 @@ class ElfOatFile final : public OatFileBase {
static ElfOatFile* OpenElfFile(int zip_fd,
File* file,
const std::string& location,
- uint8_t* requested_base,
- uint8_t* oat_file_begin, // Override base if not null
bool writable,
bool executable,
bool low_4gb,
const char* abs_dex_location,
- std::string* error_msg);
+ /*inout*/MemMap* reservation, // Where to load if not null.
+ /*out*/std::string* error_msg);
bool InitializeFromElfFile(int zip_fd,
ElfFile* elf_file,
@@ -1191,29 +1269,29 @@ class ElfOatFile final : public OatFileBase {
}
bool Load(const std::string& elf_filename,
- uint8_t* oat_file_begin, // Override where the file is loaded to if not null
bool writable,
bool executable,
bool low_4gb,
- std::string* error_msg) override;
+ /*inout*/MemMap* reservation, // Where to load if not null.
+ /*out*/std::string* error_msg) override;
bool Load(int oat_fd,
- uint8_t* oat_file_begin, // Override where the file is loaded to if not null
bool writable,
bool executable,
bool low_4gb,
- std::string* error_msg) override;
+ /*inout*/MemMap* reservation, // Where to load if not null.
+ /*out*/std::string* error_msg) override;
void PreSetup(const std::string& elf_filename ATTRIBUTE_UNUSED) override {
}
private:
bool ElfFileOpen(File* file,
- uint8_t* oat_file_begin, // Override where the file is loaded to if not null
bool writable,
bool executable,
bool low_4gb,
- std::string* error_msg);
+ /*inout*/MemMap* reservation, // Where to load if not null.
+ /*out*/std::string* error_msg);
private:
// Backing memory map for oat file during cross compilation.
@@ -1225,20 +1303,19 @@ class ElfOatFile final : public OatFileBase {
ElfOatFile* ElfOatFile::OpenElfFile(int zip_fd,
File* file,
const std::string& location,
- uint8_t* requested_base,
- uint8_t* oat_file_begin, // Override base if not null
bool writable,
bool executable,
bool low_4gb,
const char* abs_dex_location,
- std::string* error_msg) {
+ /*inout*/MemMap* reservation, // Where to load if not null.
+ /*out*/std::string* error_msg) {
ScopedTrace trace("Open elf file " + location);
std::unique_ptr<ElfOatFile> oat_file(new ElfOatFile(location, executable));
bool success = oat_file->ElfFileOpen(file,
- oat_file_begin,
writable,
low_4gb,
executable,
+ reservation,
error_msg);
if (!success) {
CHECK(!error_msg->empty());
@@ -1246,7 +1323,7 @@ ElfOatFile* ElfOatFile::OpenElfFile(int zip_fd,
}
// Complete the setup.
- if (!oat_file->ComputeFields(requested_base, file->GetPath(), error_msg)) {
+ if (!oat_file->ComputeFields(/* requested_base */ nullptr, file->GetPath(), error_msg)) {
return nullptr;
}
@@ -1279,11 +1356,11 @@ bool ElfOatFile::InitializeFromElfFile(int zip_fd,
}
bool ElfOatFile::Load(const std::string& elf_filename,
- uint8_t* oat_file_begin, // Override where the file is loaded to if not null
bool writable,
bool executable,
bool low_4gb,
- std::string* error_msg) {
+ /*inout*/MemMap* reservation,
+ /*out*/std::string* error_msg) {
ScopedTrace trace(__PRETTY_FUNCTION__);
std::unique_ptr<File> file(OS::OpenFileForReading(elf_filename.c_str()));
if (file == nullptr) {
@@ -1291,57 +1368,56 @@ bool ElfOatFile::Load(const std::string& elf_filename,
return false;
}
return ElfOatFile::ElfFileOpen(file.get(),
- oat_file_begin,
writable,
executable,
low_4gb,
+ reservation,
error_msg);
}
bool ElfOatFile::Load(int oat_fd,
- uint8_t* oat_file_begin, // Override where the file is loaded to if not null
bool writable,
bool executable,
bool low_4gb,
- std::string* error_msg) {
+ /*inout*/MemMap* reservation,
+ /*out*/std::string* error_msg) {
ScopedTrace trace(__PRETTY_FUNCTION__);
if (oat_fd != -1) {
- std::unique_ptr<File> file = std::make_unique<File>(oat_fd, false);
- file->DisableAutoClose();
+ int duped_fd = DupCloexec(oat_fd);
+ std::unique_ptr<File> file = std::make_unique<File>(duped_fd, false);
if (file == nullptr) {
*error_msg = StringPrintf("Failed to open oat filename for reading: %s",
strerror(errno));
return false;
}
return ElfOatFile::ElfFileOpen(file.get(),
- oat_file_begin,
writable,
executable,
low_4gb,
+ reservation,
error_msg);
}
return false;
}
bool ElfOatFile::ElfFileOpen(File* file,
- uint8_t* oat_file_begin,
bool writable,
bool executable,
bool low_4gb,
- std::string* error_msg) {
+ /*inout*/MemMap* reservation,
+ /*out*/std::string* error_msg) {
ScopedTrace trace(__PRETTY_FUNCTION__);
// TODO: rename requested_base to oat_data_begin
elf_file_.reset(ElfFile::Open(file,
writable,
/*program_header_only*/true,
low_4gb,
- error_msg,
- oat_file_begin));
+ error_msg));
if (elf_file_ == nullptr) {
DCHECK(!error_msg->empty());
return false;
}
- bool loaded = elf_file_->Load(file, executable, low_4gb, error_msg);
+ bool loaded = elf_file_->Load(file, executable, low_4gb, reservation, error_msg);
DCHECK(loaded || !error_msg->empty());
return loaded;
}
@@ -1392,11 +1468,11 @@ OatFile* OatFile::Open(int zip_fd,
const std::string& oat_filename,
const std::string& oat_location,
uint8_t* requested_base,
- uint8_t* oat_file_begin,
bool executable,
bool low_4gb,
const char* abs_dex_location,
- std::string* error_msg) {
+ /*inout*/MemMap* reservation,
+ /*out*/std::string* error_msg) {
ScopedTrace trace("Open oat file " + oat_location);
CHECK(!oat_filename.empty()) << oat_location;
CheckLocation(oat_location);
@@ -1419,11 +1495,11 @@ OatFile* OatFile::Open(int zip_fd,
oat_filename,
oat_location,
requested_base,
- oat_file_begin,
false /* writable */,
executable,
low_4gb,
abs_dex_location,
+ reservation,
error_msg);
if (with_dlopen != nullptr) {
return with_dlopen;
@@ -1449,11 +1525,11 @@ OatFile* OatFile::Open(int zip_fd,
oat_filename,
oat_location,
requested_base,
- oat_file_begin,
false /* writable */,
executable,
low_4gb,
abs_dex_location,
+ reservation,
error_msg);
return with_internal;
}
@@ -1463,11 +1539,11 @@ OatFile* OatFile::Open(int zip_fd,
int oat_fd,
const std::string& oat_location,
uint8_t* requested_base,
- uint8_t* oat_file_begin,
bool executable,
bool low_4gb,
const char* abs_dex_location,
- std::string* error_msg) {
+ /*inout*/MemMap* reservation,
+ /*out*/std::string* error_msg) {
CHECK(!oat_location.empty()) << oat_location;
std::string vdex_location = GetVdexFilename(oat_location);
@@ -1478,11 +1554,11 @@ OatFile* OatFile::Open(int zip_fd,
vdex_location,
oat_location,
requested_base,
- oat_file_begin,
false /* writable */,
executable,
low_4gb,
abs_dex_location,
+ reservation,
error_msg);
return with_internal;
}
@@ -1496,12 +1572,11 @@ OatFile* OatFile::OpenWritable(int zip_fd,
return ElfOatFile::OpenElfFile(zip_fd,
file,
location,
- nullptr,
- nullptr,
- true,
- false,
+ /* writable */ true,
+ /* executable */ false,
/*low_4gb*/false,
abs_dex_location,
+ /* reservation */ nullptr,
error_msg);
}
@@ -1514,12 +1589,11 @@ OatFile* OatFile::OpenReadable(int zip_fd,
return ElfOatFile::OpenElfFile(zip_fd,
file,
location,
- nullptr,
- nullptr,
- false,
- false,
+ /* writable */ false,
+ /* executable */ false,
/*low_4gb*/false,
abs_dex_location,
+ /* reservation */ nullptr,
error_msg);
}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 21e214408d..f20c603bf2 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -86,11 +86,11 @@ class OatFile {
const std::string& filename,
const std::string& location,
uint8_t* requested_base,
- uint8_t* oat_file_begin,
bool executable,
bool low_4gb,
const char* abs_dex_location,
- std::string* error_msg);
+ /*inout*/MemMap* reservation, // Where to load if not null.
+ /*out*/std::string* error_msg);
// Similar to OatFile::Open(const std::string...), but accepts input vdex and
// odex files as file descriptors. We also take zip_fd in case the vdex does not
@@ -100,11 +100,11 @@ class OatFile {
int oat_fd,
const std::string& oat_location,
uint8_t* requested_base,
- uint8_t* oat_file_begin,
bool executable,
bool low_4gb,
const char* abs_dex_location,
- std::string* error_msg);
+ /*inout*/MemMap* reservation, // Where to load if not null.
+ /*out*/std::string* error_msg);
// Open an oat file from an already opened File.
// Does not use dlopen underneath so cannot be used for runtime use
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index f7c74cc23b..4ed7e35eee 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -36,7 +36,6 @@
#include "exec_utils.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
-#include "hidden_api.h"
#include "image.h"
#include "oat.h"
#include "runtime.h"
@@ -182,30 +181,6 @@ bool OatFileAssistant::IsInBootClassPath() {
return false;
}
-bool OatFileAssistant::Lock(std::string* error_msg) {
- CHECK(error_msg != nullptr);
- CHECK(flock_.get() == nullptr) << "OatFileAssistant::Lock already acquired";
-
- // Note the lock will only succeed for secondary dex files and in test
- // environment.
- //
- // The lock *will fail* for all primary apks in a production environment.
- // The app does not have permissions to create locks next to its dex location
- // (be it system, data or vendor parition). We also cannot use the odex or
- // oat location for the same reasoning.
- //
- // This is best effort and if it fails it's unlikely that we will be able
- // to generate oat files anyway.
- std::string lock_file_name = dex_location_ + "." + GetInstructionSetString(isa_) + ".flock";
-
- flock_ = LockedFile::Open(lock_file_name.c_str(), error_msg);
- if (flock_.get() == nullptr) {
- unlink(lock_file_name.c_str());
- return false;
- }
- return true;
-}
-
int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target,
bool profile_changed,
bool downgrade,
@@ -221,72 +196,10 @@ int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target,
return -dexopt_needed;
}
-// Figure out the currently specified compile filter option in the runtime.
-// Returns true on success, false if the compiler filter is invalid, in which
-// case error_msg describes the problem.
-static bool GetRuntimeCompilerFilterOption(CompilerFilter::Filter* filter,
- std::string* error_msg) {
- CHECK(filter != nullptr);
- CHECK(error_msg != nullptr);
-
- *filter = OatFileAssistant::kDefaultCompilerFilterForDexLoading;
- for (StringPiece option : Runtime::Current()->GetCompilerOptions()) {
- if (option.starts_with("--compiler-filter=")) {
- const char* compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
- if (!CompilerFilter::ParseCompilerFilter(compiler_filter_string, filter)) {
- *error_msg = std::string("Unknown --compiler-filter value: ")
- + std::string(compiler_filter_string);
- return false;
- }
- }
- }
- return true;
-}
-
bool OatFileAssistant::IsUpToDate() {
return GetBestInfo().Status() == kOatUpToDate;
}
-OatFileAssistant::ResultOfAttemptToUpdate
-OatFileAssistant::MakeUpToDate(bool profile_changed,
- ClassLoaderContext* class_loader_context,
- std::string* error_msg) {
- // The method doesn't use zip_fd_ and directly opens dex files at dex_locations_.
- CHECK_EQ(-1, zip_fd_) << "MakeUpToDate should not be called with zip_fd";
-
- CompilerFilter::Filter target;
- if (!GetRuntimeCompilerFilterOption(&target, error_msg)) {
- return kUpdateNotAttempted;
- }
-
- OatFileInfo& info = GetBestInfo();
- // TODO(calin, jeffhao): the context should really be passed to GetDexOptNeeded: b/62269291.
- // This is actually not trivial in the current logic as it will interact with the collision
- // check:
- // - currently, if the context does not match but we have no collisions we still accept the
- // oat file.
- // - if GetDexOptNeeded would return kDex2OatFromScratch for a context mismatch and we make
- // the oat code up to date the collision check becomes useless.
- // - however, MakeUpToDate will not always succeed (e.g. for primary apks, or for dex files
- // loaded in other processes). So it boils down to how far do we want to complicate
- // the logic in order to enable the use of oat files. Maybe its time to try simplify it.
- switch (info.GetDexOptNeeded(
- target, profile_changed, /*downgrade*/ false, class_loader_context)) {
- case kNoDexOptNeeded:
- return kUpdateSucceeded;
-
- // TODO: For now, don't bother with all the different ways we can call
- // dex2oat to generate the oat file. Always generate the oat file as if it
- // were kDex2OatFromScratch.
- case kDex2OatFromScratch:
- case kDex2OatForBootImage:
- case kDex2OatForRelocation:
- case kDex2OatForFilter:
- return GenerateOatFileNoChecks(info, target, class_loader_context, error_msg);
- }
- UNREACHABLE();
-}
-
std::unique_ptr<OatFile> OatFileAssistant::GetBestOatFile() {
return GetBestInfo().ReleaseFileForUse();
}
@@ -615,243 +528,6 @@ static bool DexLocationToOdexNames(const std::string& location,
return true;
}
-// Prepare a subcomponent of the odex directory.
-// (i.e. create and set the expected permissions on the path `dir`).
-static bool PrepareDirectory(const std::string& dir, std::string* error_msg) {
- struct stat dir_stat;
- if (TEMP_FAILURE_RETRY(stat(dir.c_str(), &dir_stat)) == 0) {
- // The directory exists. Check if it is indeed a directory.
- if (!S_ISDIR(dir_stat.st_mode)) {
- *error_msg = dir + " is not a dir";
- return false;
- } else {
- // The dir is already on disk.
- return true;
- }
- }
-
- // Failed to stat. We need to create the directory.
- if (errno != ENOENT) {
- *error_msg = "Could not stat isa dir " + dir + ":" + strerror(errno);
- return false;
- }
-
- mode_t mode = S_IRWXU | S_IXGRP | S_IXOTH;
- if (mkdir(dir.c_str(), mode) != 0) {
- *error_msg = "Could not create dir " + dir + ":" + strerror(errno);
- return false;
- }
- if (chmod(dir.c_str(), mode) != 0) {
- *error_msg = "Could not create the oat dir " + dir + ":" + strerror(errno);
- return false;
- }
- return true;
-}
-
-// Prepares the odex directory for the given dex location.
-static bool PrepareOdexDirectories(const std::string& dex_location,
- const std::string& expected_odex_location,
- InstructionSet isa,
- std::string* error_msg) {
- std::string actual_odex_location;
- std::string oat_dir;
- std::string isa_dir;
- if (!DexLocationToOdexNames(
- dex_location, isa, &actual_odex_location, &oat_dir, &isa_dir, error_msg)) {
- return false;
- }
- DCHECK_EQ(expected_odex_location, actual_odex_location);
-
- if (!PrepareDirectory(oat_dir, error_msg)) {
- return false;
- }
- if (!PrepareDirectory(isa_dir, error_msg)) {
- return false;
- }
- return true;
-}
-
-class Dex2oatFileWrapper {
- public:
- explicit Dex2oatFileWrapper(File* file)
- : file_(file),
- unlink_file_at_destruction_(true) {
- }
-
- ~Dex2oatFileWrapper() {
- if (unlink_file_at_destruction_ && (file_ != nullptr)) {
- file_->Erase(/*unlink*/ true);
- }
- }
-
- File* GetFile() { return file_.get(); }
-
- void DisableUnlinkAtDestruction() {
- unlink_file_at_destruction_ = false;
- }
-
- private:
- std::unique_ptr<File> file_;
- bool unlink_file_at_destruction_;
-};
-
-OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChecks(
- OatFileAssistant::OatFileInfo& info,
- CompilerFilter::Filter filter,
- const ClassLoaderContext* class_loader_context,
- std::string* error_msg) {
- CHECK(error_msg != nullptr);
-
- Runtime* runtime = Runtime::Current();
- if (!runtime->IsDex2OatEnabled()) {
- *error_msg = "Generation of oat file for dex location " + dex_location_
- + " not attempted because dex2oat is disabled.";
- return kUpdateNotAttempted;
- }
-
- if (info.Filename() == nullptr) {
- *error_msg = "Generation of oat file for dex location " + dex_location_
- + " not attempted because the oat file name could not be determined.";
- return kUpdateNotAttempted;
- }
- const std::string& oat_file_name = *info.Filename();
- const std::string& vdex_file_name = GetVdexFilename(oat_file_name);
-
- // dex2oat ignores missing dex files and doesn't report an error.
- // Check explicitly here so we can detect the error properly.
- // TODO: Why does dex2oat behave that way?
- struct stat dex_path_stat;
- if (TEMP_FAILURE_RETRY(stat(dex_location_.c_str(), &dex_path_stat)) != 0) {
- *error_msg = "Could not access dex location " + dex_location_ + ":" + strerror(errno);
- return kUpdateNotAttempted;
- }
-
- // If this is the odex location, we need to create the odex file layout (../oat/isa/..)
- if (!info.IsOatLocation()) {
- if (!PrepareOdexDirectories(dex_location_, oat_file_name, isa_, error_msg)) {
- return kUpdateNotAttempted;
- }
- }
-
- // Set the permissions for the oat and the vdex files.
- // The user always gets read and write while the group and others propagate
- // the reading access of the original dex file.
- mode_t file_mode = S_IRUSR | S_IWUSR |
- (dex_path_stat.st_mode & S_IRGRP) |
- (dex_path_stat.st_mode & S_IROTH);
-
- Dex2oatFileWrapper vdex_file_wrapper(OS::CreateEmptyFile(vdex_file_name.c_str()));
- File* vdex_file = vdex_file_wrapper.GetFile();
- if (vdex_file == nullptr) {
- *error_msg = "Generation of oat file " + oat_file_name
- + " not attempted because the vdex file " + vdex_file_name
- + " could not be opened.";
- return kUpdateNotAttempted;
- }
-
- if (fchmod(vdex_file->Fd(), file_mode) != 0) {
- *error_msg = "Generation of oat file " + oat_file_name
- + " not attempted because the vdex file " + vdex_file_name
- + " could not be made world readable.";
- return kUpdateNotAttempted;
- }
-
- Dex2oatFileWrapper oat_file_wrapper(OS::CreateEmptyFile(oat_file_name.c_str()));
- File* oat_file = oat_file_wrapper.GetFile();
- if (oat_file == nullptr) {
- *error_msg = "Generation of oat file " + oat_file_name
- + " not attempted because the oat file could not be created.";
- return kUpdateNotAttempted;
- }
-
- if (fchmod(oat_file->Fd(), file_mode) != 0) {
- *error_msg = "Generation of oat file " + oat_file_name
- + " not attempted because the oat file could not be made world readable.";
- return kUpdateNotAttempted;
- }
-
- std::vector<std::string> args;
- args.push_back("--dex-file=" + dex_location_);
- args.push_back("--output-vdex-fd=" + std::to_string(vdex_file->Fd()));
- args.push_back("--oat-fd=" + std::to_string(oat_file->Fd()));
- args.push_back("--oat-location=" + oat_file_name);
- args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
- const std::string dex2oat_context = class_loader_context == nullptr
- ? OatFile::kSpecialSharedLibrary
- : class_loader_context->EncodeContextForDex2oat(/*base_dir*/ "");
- args.push_back("--class-loader-context=" + dex2oat_context);
-
- if (!Dex2Oat(args, error_msg)) {
- return kUpdateFailed;
- }
-
- if (vdex_file->FlushCloseOrErase() != 0) {
- *error_msg = "Unable to close vdex file " + vdex_file_name;
- return kUpdateFailed;
- }
-
- if (oat_file->FlushCloseOrErase() != 0) {
- *error_msg = "Unable to close oat file " + oat_file_name;
- return kUpdateFailed;
- }
-
- // Mark that the odex file has changed and we should try to reload.
- info.Reset();
- // We have compiled successfully. Disable the auto-unlink.
- vdex_file_wrapper.DisableUnlinkAtDestruction();
- oat_file_wrapper.DisableUnlinkAtDestruction();
-
- return kUpdateSucceeded;
-}
-
-bool OatFileAssistant::Dex2Oat(const std::vector<std::string>& args,
- std::string* error_msg) {
- Runtime* runtime = Runtime::Current();
- std::string image_location = ImageLocation();
- if (image_location.empty()) {
- *error_msg = "No image location found for Dex2Oat.";
- return false;
- }
-
- std::vector<std::string> argv;
- argv.push_back(runtime->GetCompilerExecutable());
- if (runtime->IsJavaDebuggable()) {
- argv.push_back("--debuggable");
- }
- runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
-
- if (!runtime->IsVerificationEnabled()) {
- argv.push_back("--compiler-filter=verify-none");
- }
-
- if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kNoChecks) {
- argv.push_back("--runtime-arg");
- argv.push_back("-Xhidden-api-checks");
- }
-
- if (runtime->MustRelocateIfPossible()) {
- argv.push_back("--runtime-arg");
- argv.push_back("-Xrelocate");
- } else {
- argv.push_back("--runtime-arg");
- argv.push_back("-Xnorelocate");
- }
-
- if (!kIsTargetBuild) {
- argv.push_back("--host");
- }
-
- argv.push_back("--boot-image=" + image_location);
-
- std::vector<std::string> compiler_options = runtime->GetCompilerOptions();
- argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());
-
- argv.insert(argv.end(), args.begin(), args.end());
-
- std::string command_line(android::base::Join(argv, ' '));
- return Exec(argv, error_msg);
-}
-
bool OatFileAssistant::DexLocationToOdexFilename(const std::string& location,
InstructionSet isa,
std::string* odex_filename,
@@ -885,16 +561,6 @@ bool OatFileAssistant::DexLocationToOatFilename(const std::string& location,
return GetDalvikCacheFilename(location.c_str(), cache_dir.c_str(), oat_filename, error_msg);
}
-std::string OatFileAssistant::ImageLocation() {
- Runtime* runtime = Runtime::Current();
- const std::vector<gc::space::ImageSpace*>& image_spaces =
- runtime->GetHeap()->GetBootImageSpaces();
- if (image_spaces.empty()) {
- return "";
- }
- return image_spaces[0]->GetImageLocation();
-}
-
const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() {
if (!required_dex_checksums_attempted_) {
required_dex_checksums_attempted_ = true;
@@ -1165,22 +831,22 @@ const OatFile* OatFileAssistant::OatFileInfo::GetFile() {
vdex_fd_,
oat_fd_,
filename_.c_str(),
- nullptr,
- nullptr,
+ /* requested_base */ nullptr,
executable,
- false /* low_4gb */,
+ /* low_4gb */ false,
oat_file_assistant_->dex_location_.c_str(),
+ /* reservation */ nullptr,
&error_msg));
}
} else {
file_.reset(OatFile::Open(/* zip_fd */ -1,
filename_.c_str(),
filename_.c_str(),
- nullptr,
- nullptr,
+ /* requested_base */ nullptr,
executable,
- false /* low_4gb */,
+ /* low_4gb */ false,
oat_file_assistant_->dex_location_.c_str(),
+ /* reservation */ nullptr,
&error_msg));
}
if (file_.get() == nullptr) {
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index a6d0961835..dbfbdf9fbc 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -48,11 +48,6 @@ class ImageSpace;
// dex location is in the boot class path.
class OatFileAssistant {
public:
- // The default compile filter to use when optimizing dex file at load time if they
- // are out of date.
- static const CompilerFilter::Filter kDefaultCompilerFilterForDexLoading =
- CompilerFilter::kQuicken;
-
enum DexOptNeeded {
// No dexopt should (or can) be done to update the apk/jar.
// Matches Java: dalvik.system.DexFile.NO_DEXOPT_NEEDED = 0
@@ -144,24 +139,6 @@ class OatFileAssistant {
// path.
bool IsInBootClassPath();
- // Obtains a lock on the target oat file.
- // Only one OatFileAssistant object can hold the lock for a target oat file
- // at a time. The Lock is released automatically when the OatFileAssistant
- // object goes out of scope. The Lock() method must not be called if the
- // lock has already been acquired.
- //
- // Returns true on success.
- // Returns false on error, in which case error_msg will contain more
- // information on the error.
- //
- // The 'error_msg' argument must not be null.
- //
- // This is intended to be used to avoid race conditions when multiple
- // processes generate oat files, such as when a foreground Activity and
- // a background Service both use DexClassLoaders pointing to the same dex
- // file.
- bool Lock(std::string* error_msg);
-
// Return what action needs to be taken to produce up-to-date code for this
// dex location. If "downgrade" is set to false, it verifies if the current
// compiler filter is at least as good as an oat file generated with the
@@ -187,33 +164,6 @@ class OatFileAssistant {
// irrespective of the compiler filter of the up-to-date code.
bool IsUpToDate();
- // Return code used when attempting to generate updated code.
- enum ResultOfAttemptToUpdate {
- kUpdateFailed, // We tried making the code up to date, but
- // encountered an unexpected failure.
- kUpdateNotAttempted, // We wanted to update the code, but determined we
- // should not make the attempt.
- kUpdateSucceeded // We successfully made the code up to date
- // (possibly by doing nothing).
- };
-
- // Attempts to generate or relocate the oat file as needed to make it up to
- // date based on the current runtime and compiler options.
- // profile_changed should be true to indicate the profile has recently
- // changed for this dex location.
- //
- // If the dex files need to be made up to date, class_loader_context will be
- // passed to dex2oat.
- //
- // Returns the result of attempting to update the code.
- //
- // If the result is not kUpdateSucceeded, the value of error_msg will be set
- // to a string describing why there was a failure or the update was not
- // attempted. error_msg must not be null.
- ResultOfAttemptToUpdate MakeUpToDate(bool profile_changed,
- ClassLoaderContext* class_loader_context,
- std::string* error_msg);
-
// Returns an oat file that can be used for loading dex files.
// Returns null if no suitable oat file was found.
//
@@ -284,18 +234,6 @@ class OatFileAssistant {
// Returns the status of the oat file for the dex location.
OatStatus OatFileStatus();
- // Executes dex2oat using the current runtime configuration overridden with
- // the given arguments. This does not check to see if dex2oat is enabled in
- // the runtime configuration.
- // Returns true on success.
- //
- // If there is a failure, the value of error_msg will be set to a string
- // describing why there was failure. error_msg must not be null.
- //
- // TODO: The OatFileAssistant probably isn't the right place to have this
- // function.
- static bool Dex2Oat(const std::vector<std::string>& args, std::string* error_msg);
-
// Constructs the odex file name for the given dex location.
// Returns true on success, in which case odex_filename is set to the odex
// file name.
@@ -436,20 +374,6 @@ class OatFileAssistant {
bool file_released_ = false;
};
- // Generate the oat file for the given info from the dex file using the
- // current runtime compiler options, the specified filter and class loader
- // context.
- // This does not check the current status before attempting to generate the
- // oat file.
- //
- // If the result is not kUpdateSucceeded, the value of error_msg will be set
- // to a string describing why there was a failure or the update was not
- // attempted. error_msg must not be null.
- ResultOfAttemptToUpdate GenerateOatFileNoChecks(OatFileInfo& info,
- CompilerFilter::Filter target,
- const ClassLoaderContext* class_loader_context,
- std::string* error_msg);
-
// Return info for the best oat file.
OatFileInfo& GetBestInfo();
@@ -473,13 +397,6 @@ class OatFileAssistant {
// location.
OatStatus GivenOatFileStatus(const OatFile& file);
- // Returns the current image location.
- // Returns an empty string if the image location could not be retrieved.
- //
- // TODO: This method should belong with an image file manager, not
- // the oat file assistant.
- static std::string ImageLocation();
-
// Gets the dex checksums required for an up-to-date oat file.
// Returns cached_required_dex_checksums if the required checksums were
// located. Returns null if the required checksums were not found. The
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 0b3c61d474..5a2997809b 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -41,11 +41,6 @@
namespace art {
-static const std::string kSpecialSharedLibrary = "&"; // NOLINT [runtime/string] [4]
-static ClassLoaderContext* kSpecialSharedLibraryContext = nullptr;
-
-static constexpr char kDex2oatCmdLineHiddenApiArg[] = " --runtime-arg -Xhidden-api-checks";
-
class OatFileAssistantTest : public DexoptTest {
public:
void VerifyOptimizationStatus(const std::string& file,
@@ -68,14 +63,6 @@ class OatFileAssistantTest : public DexoptTest {
}
};
-class OatFileAssistantNoDex2OatTest : public DexoptTest {
- public:
- virtual void SetUpRuntimeOptions(RuntimeOptions* options) {
- DexoptTest::SetUpRuntimeOptions(options);
- options->push_back(std::make_pair("-Xnodex2oat", nullptr));
- }
-};
-
class ScopedNonWritable {
public:
explicit ScopedNonWritable(const std::string& dex_location) {
@@ -109,6 +96,97 @@ static bool IsExecutedAsRoot() {
return geteuid() == 0;
}
+// Case: We have a MultiDEX file and up-to-date ODEX file for it with relative
+// encoded dex locations.
+// Expect: The oat file status is kNoDexOptNeeded.
+TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) {
+ std::string dex_location = GetScratchDir() + "/RelativeEncodedDexLocation.jar";
+ std::string odex_location = GetOdexDir() + "/RelativeEncodedDexLocation.odex";
+
+ // Create the dex file
+ Copy(GetMultiDexSrc1(), dex_location);
+
+ // Create the oat file with relative encoded dex location.
+ std::vector<std::string> args = {
+ "--dex-file=" + dex_location,
+ "--dex-location=" + std::string("RelativeEncodedDexLocation.jar"),
+ "--oat-file=" + odex_location,
+ "--compiler-filter=speed"
+ };
+
+ std::string error_msg;
+ ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
+
+ // Verify we can load both dex files.
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(oat_file.get() != nullptr);
+ EXPECT_TRUE(oat_file->IsExecutable());
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+ EXPECT_EQ(2u, dex_files.size());
+}
+
+TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ std::string context_location = GetScratchDir() + "/ContextDex.jar";
+ Copy(GetDexSrc1(), dex_location);
+ Copy(GetDexSrc2(), context_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ std::string context_str = "PCL[" + context_location + "]";
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str);
+ ASSERT_TRUE(context != nullptr);
+ ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, ""));
+
+ std::string error_msg;
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location);
+ args.push_back("--oat-file=" + odex_location);
+ args.push_back("--class-loader-context=" + context_str);
+ ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
+
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ EXPECT_NE(nullptr, oat_file.get());
+ EXPECT_EQ(context->EncodeContextForOatFile(""),
+ oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
+}
+
+TEST_F(OatFileAssistantTest, GetDexOptNeededWithUpToDateContextRelative) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ std::string context_location = GetScratchDir() + "/ContextDex.jar";
+ Copy(GetDexSrc1(), dex_location);
+ Copy(GetDexSrc2(), context_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ std::string context_str = "PCL[" + context_location + "]";
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str);
+ ASSERT_TRUE(context != nullptr);
+ ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, ""));
+
+ std::string error_msg;
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location);
+ args.push_back("--oat-file=" + odex_location);
+ args.push_back("--class-loader-context=" + context_str);
+ ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
+
+ // A relative context simulates a dependent split context.
+ std::unique_ptr<ClassLoaderContext> relative_context =
+ ClassLoaderContext::Create("PCL[ContextDex.jar]");
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kDefaultCompilerFilter,
+ /* downgrade */ false,
+ /* profile_changed */ false,
+ relative_context.get()));
+}
+
// Case: We have a DEX file, but no OAT file for it.
// Expect: The status is kDex2OatNeeded.
TEST_F(OatFileAssistantTest, DexNoOat) {
@@ -145,11 +223,6 @@ TEST_F(OatFileAssistantTest, NoDexNoOat) {
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
- // Trying to make the oat file up to date should not fail or crash.
- std::string error_msg;
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg));
-
// Trying to get the best oat file should fail, but not crash.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
EXPECT_EQ(nullptr, oat_file.get());
@@ -584,37 +657,6 @@ TEST_F(OatFileAssistantTest, StrippedMultiDexNonMainOutOfDate) {
EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
}
-// Case: We have a MultiDEX file and up-to-date ODEX file for it with relative
-// encoded dex locations.
-// Expect: The oat file status is kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) {
- std::string dex_location = GetScratchDir() + "/RelativeEncodedDexLocation.jar";
- std::string odex_location = GetOdexDir() + "/RelativeEncodedDexLocation.odex";
-
- // Create the dex file
- Copy(GetMultiDexSrc1(), dex_location);
-
- // Create the oat file with relative encoded dex location.
- std::vector<std::string> args;
- args.push_back("--dex-file=" + dex_location);
- args.push_back("--dex-location=" + std::string("RelativeEncodedDexLocation.jar"));
- args.push_back("--oat-file=" + odex_location);
- args.push_back("--compiler-filter=speed");
-
- std::string error_msg;
- ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
-
- // Verify we can load both dex files.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
-
- std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
- ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
- std::vector<std::unique_ptr<const DexFile>> dex_files;
- dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
- EXPECT_EQ(2u, dex_files.size());
-}
-
// Case: We have a DEX file and an OAT file out of date with respect to the
// dex checksum.
TEST_F(OatFileAssistantTest, OatDexOutOfDate) {
@@ -872,13 +914,6 @@ TEST_F(OatFileAssistantTest, ResourceOnlyDex) {
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
- // Make the oat file up to date. This should have no effect.
- std::string error_msg;
- Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)) <<
- error_msg;
-
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
@@ -1037,35 +1072,6 @@ TEST_F(OatFileAssistantTest, LoadNoExecOatUpToDate) {
EXPECT_EQ(1u, dex_files.size());
}
-// Case: We don't have a DEX file and can't write the oat file.
-// Expect: We should fail to generate the oat file without crashing.
-TEST_F(OatFileAssistantTest, GenNoDex) {
- if (IsExecutedAsRoot()) {
- // We cannot simulate non writable locations when executed as root: b/38000545.
- LOG(ERROR) << "Test skipped because it's running as root";
- return;
- }
-
- std::string dex_location = GetScratchDir() + "/GenNoDex.jar";
-
- ScopedNonWritable scoped_non_writable(dex_location);
- ASSERT_TRUE(scoped_non_writable.IsSuccessful());
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
- std::string error_msg;
- Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
- // We should get kUpdateSucceeded from MakeUpToDate since there's nothing
- // that can be done in this situation.
- ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg));
-
- // Verify it didn't create an oat in the default location (dalvik-cache).
- OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_EQ(OatFileAssistant::kOatCannotOpen, ofm.OatFileStatus());
- // Verify it didn't create the odex file in the default location (../oat/isa/...odex)
- EXPECT_EQ(OatFileAssistant::kOatCannotOpen, ofm.OdexFileStatus());
-}
-
// Turn an absolute path into a path relative to the current working
// directory.
static std::string MakePathRelative(const std::string& target) {
@@ -1131,13 +1137,6 @@ TEST_F(OatFileAssistantTest, ShortDexLocation) {
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
-
- // Trying to make it up to date should have no effect.
- std::string error_msg;
- Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg));
- EXPECT_TRUE(error_msg.empty());
}
// Case: Non-standard extension for dex file.
@@ -1156,11 +1155,12 @@ TEST_F(OatFileAssistantTest, LongDexExtension) {
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
}
+
// A task to generate a dex location. Used by the RaceToGenerate test.
class RaceGenerateTask : public Task {
public:
- explicit RaceGenerateTask(const std::string& dex_location, const std::string& oat_location)
- : dex_location_(dex_location), oat_location_(oat_location), loaded_oat_file_(nullptr)
+ RaceGenerateTask(const std::string& dex_location, const std::string& oat_location)
+ : dex_location_(dex_location), oat_location_(oat_location), loaded_oat_file_(nullptr)
{}
void Run(Thread* self ATTRIBUTE_UNUSED) {
@@ -1169,6 +1169,21 @@ class RaceGenerateTask : public Task {
std::vector<std::unique_ptr<const DexFile>> dex_files;
std::vector<std::string> error_msgs;
const OatFile* oat_file = nullptr;
+ {
+ // Create the oat file.
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location_);
+ args.push_back("--oat-file=" + oat_location_);
+ std::string error_msg;
+ if (kIsTargetBuild) {
+ // Don't check whether dex2oat is successful: given we're running kNumThreads in
+ // parallel, low memory killer might just kill some of the dex2oat invocations.
+ DexoptTest::Dex2Oat(args, &error_msg);
+ } else {
+ ASSERT_TRUE(DexoptTest::Dex2Oat(args, &error_msg)) << error_msg;
+ }
+ }
+
dex_files = Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(
dex_location_.c_str(),
Runtime::Current()->GetSystemClassLoader(),
@@ -1176,8 +1191,9 @@ class RaceGenerateTask : public Task {
&oat_file,
&error_msgs);
CHECK(!dex_files.empty()) << android::base::Join(error_msgs, '\n');
- CHECK(dex_files[0]->GetOatDexFile() != nullptr) << dex_files[0]->GetLocation();
- loaded_oat_file_ = dex_files[0]->GetOatDexFile()->GetOatFile();
+ if (dex_files[0]->GetOatDexFile() != nullptr) {
+ loaded_oat_file_ = dex_files[0]->GetOatDexFile()->GetOatFile();
+ }
CHECK_EQ(loaded_oat_file_, oat_file);
}
@@ -1191,12 +1207,8 @@ class RaceGenerateTask : public Task {
const OatFile* loaded_oat_file_;
};
-// Test the case where multiple processes race to generate an oat file.
-// This simulates multiple processes using multiple threads.
-//
-// We want unique Oat files to be loaded even when there is a race to load.
-// TODO: The test case no longer tests locking the way it was intended since we now get multiple
-// copies of the same Oat files mapped at different locations.
+// Test the case where dex2oat invocations race with multiple processes trying to
+// load the oat file.
TEST_F(OatFileAssistantTest, RaceToGenerate) {
std::string dex_location = GetScratchDir() + "/RaceToGenerate.jar";
std::string oat_location = GetOdexDir() + "/RaceToGenerate.oat";
@@ -1209,31 +1221,32 @@ TEST_F(OatFileAssistantTest, RaceToGenerate) {
// take a while to generate.
Copy(GetLibCoreDexFileNames()[0], dex_location);
- const int kNumThreads = 32;
+ const size_t kNumThreads = 32;
Thread* self = Thread::Current();
ThreadPool thread_pool("Oat file assistant test thread pool", kNumThreads);
std::vector<std::unique_ptr<RaceGenerateTask>> tasks;
- for (int i = 0; i < kNumThreads; i++) {
+ for (size_t i = 0; i < kNumThreads; i++) {
std::unique_ptr<RaceGenerateTask> task(new RaceGenerateTask(dex_location, oat_location));
thread_pool.AddTask(self, task.get());
tasks.push_back(std::move(task));
}
thread_pool.StartWorkers(self);
- thread_pool.Wait(self, true, false);
+ thread_pool.Wait(self, /* do_work */ true, /* may_hold_locks */ false);
- // Verify every task got a unique oat file.
+ // Verify that tasks which got an oat file got a unique one.
std::set<const OatFile*> oat_files;
for (auto& task : tasks) {
const OatFile* oat_file = task->GetLoadedOatFile();
- EXPECT_TRUE(oat_files.find(oat_file) == oat_files.end());
- oat_files.insert(oat_file);
+ if (oat_file != nullptr) {
+ EXPECT_TRUE(oat_files.find(oat_file) == oat_files.end());
+ oat_files.insert(oat_file);
+ }
}
}
-// Case: We have a DEX file and an ODEX file, no OAT file, and dex2oat is
-// disabled.
+// Case: We have a DEX file and an ODEX file, and no OAT file,
// Expect: We should load the odex file non-executable.
-TEST_F(OatFileAssistantNoDex2OatTest, LoadDexOdexNoOat) {
+TEST_F(DexoptTest, LoadDexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/LoadDexOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/LoadDexOdexNoOat.odex";
@@ -1252,10 +1265,9 @@ TEST_F(OatFileAssistantNoDex2OatTest, LoadDexOdexNoOat) {
EXPECT_EQ(1u, dex_files.size());
}
-// Case: We have a MultiDEX file and an ODEX file, no OAT file, and dex2oat is
-// disabled.
+// Case: We have a MultiDEX file and an ODEX file, and no OAT file.
// Expect: We should load the odex file non-executable.
-TEST_F(OatFileAssistantNoDex2OatTest, LoadMultiDexOdexNoOat) {
+TEST_F(DexoptTest, LoadMultiDexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/LoadMultiDexOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/LoadMultiDexOdexNoOat.odex";
@@ -1274,36 +1286,6 @@ TEST_F(OatFileAssistantNoDex2OatTest, LoadMultiDexOdexNoOat) {
EXPECT_EQ(2u, dex_files.size());
}
-TEST_F(OatFileAssistantTest, RuntimeCompilerFilterOptionUsed) {
- std::string dex_location = GetScratchDir() + "/RuntimeCompilerFilterOptionUsed.jar";
- Copy(GetDexSrc1(), dex_location);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
-
- std::string error_msg;
- Runtime::Current()->AddCompilerOption("--compiler-filter=quicken");
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)) <<
- error_msg;
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-
- Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg))
- << error_msg;
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-
- Runtime::Current()->AddCompilerOption("--compiler-filter=bogus");
- EXPECT_EQ(OatFileAssistant::kUpdateNotAttempted,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg));
-}
-
TEST(OatFileAssistantUtilsTest, DexLocationToOdexFilename) {
std::string error_msg;
std::string odex_file;
@@ -1350,112 +1332,6 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) {
}
}
-// Verify that when no compiler filter is passed the default one from OatFileAssistant is used.
-TEST_F(OatFileAssistantTest, DefaultMakeUpToDateFilter) {
- std::string dex_location = GetScratchDir() + "/TestDex.jar";
- Copy(GetDexSrc1(), dex_location);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
-
- const CompilerFilter::Filter default_filter =
- OatFileAssistant::kDefaultCompilerFilterForDexLoading;
- std::string error_msg;
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)) <<
- error_msg;
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(default_filter));
- std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
- EXPECT_NE(nullptr, oat_file.get());
- EXPECT_EQ(default_filter, oat_file->GetCompilerFilter());
-}
-
-TEST_F(OatFileAssistantTest, MakeUpToDateWithSpecialSharedLibrary) {
- std::string dex_location = GetScratchDir() + "/TestDex.jar";
- Copy(GetDexSrc1(), dex_location);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
-
- const CompilerFilter::Filter default_filter =
- OatFileAssistant::kDefaultCompilerFilterForDexLoading;
- std::string error_msg;
- int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(default_filter));
- std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
- EXPECT_NE(nullptr, oat_file.get());
- EXPECT_EQ(kSpecialSharedLibrary,
- oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
-}
-
-TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) {
- std::string dex_location = GetScratchDir() + "/TestDex.jar";
- std::string context_location = GetScratchDir() + "/ContextDex.jar";
- Copy(GetDexSrc1(), dex_location);
- Copy(GetDexSrc2(), context_location);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
-
- const CompilerFilter::Filter default_filter =
- OatFileAssistant::kDefaultCompilerFilterForDexLoading;
- std::string error_msg;
- std::string context_str = "PCL[" + context_location + "]";
- std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str);
- ASSERT_TRUE(context != nullptr);
- ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, ""));
-
- int status = oat_file_assistant.MakeUpToDate(false, context.get(), &error_msg);
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(default_filter, false, false, context.get()));
-
- std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
- EXPECT_NE(nullptr, oat_file.get());
- EXPECT_EQ(context->EncodeContextForOatFile(""),
- oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
-}
-
-TEST_F(OatFileAssistantTest, MakeUpToDateWithHiddenApiDisabled) {
- hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
- hiddenapi::EnforcementPolicy::kNoChecks);
-
- std::string dex_location = GetScratchDir() + "/TestDexHiddenApiDisabled.jar";
- Copy(GetDexSrc1(), dex_location);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- std::string error_msg;
- int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
-
- std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
- EXPECT_NE(nullptr, oat_file.get());
-
- const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey);
- EXPECT_NE(nullptr, cmd_line);
- EXPECT_EQ(nullptr, strstr(cmd_line, kDex2oatCmdLineHiddenApiArg));
-}
-
-TEST_F(OatFileAssistantTest, MakeUpToDateWithHiddenApiEnabled) {
- hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
- hiddenapi::EnforcementPolicy::kBlacklistOnly);
-
- std::string dex_location = GetScratchDir() + "/TestDexHiddenApiEnabled.jar";
- Copy(GetDexSrc1(), dex_location);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- std::string error_msg;
- int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
-
- std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
- EXPECT_NE(nullptr, oat_file.get());
-
- const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey);
- EXPECT_NE(nullptr, cmd_line);
- EXPECT_NE(nullptr, strstr(cmd_line, kDex2oatCmdLineHiddenApiArg));
-}
-
TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string context_location = GetScratchDir() + "/ContextDex.jar";
@@ -1464,19 +1340,12 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) {
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- const CompilerFilter::Filter default_filter =
- OatFileAssistant::kDefaultCompilerFilterForDexLoading;
std::string error_msg;
std::string context_str = "PCL[" + context_location + "]";
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str);
ASSERT_TRUE(context != nullptr);
ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, ""));
- int status = oat_file_assistant.MakeUpToDate(false, context.get(), &error_msg);
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(default_filter, false, false, context.get()));
-
// Update the context by overriding the jar file.
Copy(GetMultiDexSrc2(), context_location);
std::unique_ptr<ClassLoaderContext> updated_context = ClassLoaderContext::Create(context_str);
@@ -1484,88 +1353,10 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) {
// DexOptNeeded should advise compilation from scratch.
EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
oat_file_assistant.GetDexOptNeeded(
- default_filter, false, false, updated_context.get()));
-}
-
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithUpToDateContextRelative) {
- std::string dex_location = GetScratchDir() + "/TestDex.jar";
- std::string context_location = GetScratchDir() + "/ContextDex.jar";
- Copy(GetDexSrc1(), dex_location);
- Copy(GetDexSrc2(), context_location);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
-
- const CompilerFilter::Filter default_filter =
- OatFileAssistant::kDefaultCompilerFilterForDexLoading;
- std::string error_msg;
- std::string context_str = "PCL[" + context_location + "]";
- std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str);
- ASSERT_TRUE(context != nullptr);
- ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, ""));
-
- int status = oat_file_assistant.MakeUpToDate(false, context.get(), &error_msg);
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
-
- // A relative context simulates a dependent split context.
- std::unique_ptr<ClassLoaderContext> relative_context =
- ClassLoaderContext::Create("PCL[ContextDex.jar]");
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(
- default_filter, false, false, relative_context.get()));
-}
-
-TEST_F(OatFileAssistantTest, SystemOdex) {
- std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
- std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
- std::string system_location = GetAndroidRoot() + "/OatUpToDate.jar";
-
- std::string error_msg;
-
- Copy(GetDexSrc1(), dex_location);
- EXPECT_FALSE(LocationIsOnSystem(dex_location.c_str()));
-
- {
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- true,
- false);
- int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
- ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
- EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable());
- }
-
- {
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- true,
- true);
- int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
- ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
- EXPECT_FALSE(oat_file_assistant.GetBestOatFile()->IsExecutable());
- }
-
- Copy(GetDexSrc1(), system_location);
- EXPECT_TRUE(LocationIsOnSystem(system_location.c_str()));
-
- {
- OatFileAssistant oat_file_assistant(system_location.c_str(),
- kRuntimeISA,
- true,
- false);
- int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
- ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
- EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable());
- }
-
- {
- OatFileAssistant oat_file_assistant(system_location.c_str(),
- kRuntimeISA,
- true,
- true);
- int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
- ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
- EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable());
- }
+ CompilerFilter::kDefaultCompilerFilter,
+ /* downgrade */ false,
+ /* profile_changed */ false,
+ updated_context.get()));
}
// TODO: More Tests:
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 59a1045ba2..bcad4a3428 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -465,57 +465,15 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
!runtime->IsAotCompiler(),
only_use_system_oat_files_);
- // Lock the target oat location to avoid races generating and loading the
- // oat file.
- std::string error_msg;
- if (!oat_file_assistant.Lock(/*out*/&error_msg)) {
- // Don't worry too much if this fails. If it does fail, it's unlikely we
- // can generate an oat file anyway.
- VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg;
- }
-
- const OatFile* source_oat_file = nullptr;
-
- if (!oat_file_assistant.IsUpToDate()) {
- // Update the oat file on disk if we can, based on the --compiler-filter
- // option derived from the current runtime options.
- // This may fail, but that's okay. Best effort is all that matters here.
- // TODO(calin): b/64530081 b/66984396. Pass a null context to verify and compile
- // secondary dex files in isolation (and avoid to extract/verify the main apk
- // if it's in the class path). Note this trades correctness for performance
- // since the resulting slow down is unacceptable in some cases until b/64530081
- // is fixed.
- // We still pass the class loader context when the classpath string of the runtime
- // is not empty, which is the situation when ART is invoked standalone.
- ClassLoaderContext* actual_context = Runtime::Current()->GetClassPathString().empty()
- ? nullptr
- : context.get();
- switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/ false,
- actual_context,
- /*out*/ &error_msg)) {
- case OatFileAssistant::kUpdateFailed:
- LOG(WARNING) << error_msg;
- break;
-
- case OatFileAssistant::kUpdateNotAttempted:
- // Avoid spamming the logs if we decided not to attempt making the oat
- // file up to date.
- VLOG(oat) << error_msg;
- break;
-
- case OatFileAssistant::kUpdateSucceeded:
- // Nothing to do.
- break;
- }
- }
-
// Get the oat file on disk.
std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
VLOG(oat) << "OatFileAssistant(" << dex_location << ").GetBestOatFile()="
<< reinterpret_cast<uintptr_t>(oat_file.get())
<< " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")";
+ const OatFile* source_oat_file = nullptr;
CheckCollisionResult check_collision_result = CheckCollisionResult::kPerformedHasCollisions;
+ std::string error_msg;
if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
// Prevent oat files from being loaded if no class_loader or dex_elements are provided.
// This can happen when the deprecated DexFile.<init>(String) is called directly, and it
diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc
index 12dfe20d56..51d8fca6c5 100644
--- a/runtime/oat_file_test.cc
+++ b/runtime/oat_file_test.cc
@@ -77,11 +77,11 @@ TEST_F(OatFileTest, LoadOat) {
std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
oat_location.c_str(),
oat_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
dex_location.c_str(),
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(odex_file.get() != nullptr);
@@ -105,11 +105,11 @@ TEST_F(OatFileTest, ChangingMultiDexUncompressed) {
std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
oat_location.c_str(),
oat_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
dex_location.c_str(),
+ /* reservation */ nullptr,
&error_msg));
ASSERT_TRUE(odex_file != nullptr);
ASSERT_EQ(2u, odex_file->GetOatDexFiles().size());
@@ -120,13 +120,13 @@ TEST_F(OatFileTest, ChangingMultiDexUncompressed) {
// And try to load again.
std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1,
- oat_location.c_str(),
- oat_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
+ oat_location,
+ oat_location,
+ /* requested_base */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
dex_location.c_str(),
+ /* reservation */ nullptr,
&error_msg));
EXPECT_TRUE(odex_file == nullptr);
EXPECT_NE(std::string::npos, error_msg.find("expected 2 uncompressed dex files, but found 1"))
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 0d98d90f1e..4d16eb537d 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -220,9 +220,6 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize
.Define({"-Xrelocate", "-Xnorelocate"})
.WithValues({true, false})
.IntoKey(M::Relocate)
- .Define({"-Xdex2oat", "-Xnodex2oat"})
- .WithValues({true, false})
- .IntoKey(M::Dex2Oat)
.Define({"-Ximage-dex2oat", "-Xnoimage-dex2oat"})
.WithValues({true, false})
.IntoKey(M::ImageDex2Oat)
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 243150759b..6878cc08c8 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -216,7 +216,6 @@ Runtime::Runtime()
must_relocate_(false),
is_concurrent_gc_enabled_(true),
is_explicit_gc_disabled_(false),
- dex2oat_enabled_(true),
image_dex2oat_enabled_(true),
default_stack_size_(0),
heap_(nullptr),
@@ -1195,7 +1194,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
must_relocate_ = runtime_options.GetOrDefault(Opt::Relocate);
is_zygote_ = runtime_options.Exists(Opt::Zygote);
is_explicit_gc_disabled_ = runtime_options.Exists(Opt::DisableExplicitGC);
- dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::Dex2Oat);
image_dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::ImageDex2Oat);
dump_native_stack_on_sig_quit_ = runtime_options.GetOrDefault(Opt::DumpNativeStackOnSigQuit);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index f98d7b9829..f0bf7548af 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -142,10 +142,6 @@ class Runtime {
return must_relocate_;
}
- bool IsDex2OatEnabled() const {
- return dex2oat_enabled_ && IsImageDex2OatEnabled();
- }
-
bool IsImageDex2OatEnabled() const {
return image_dex2oat_enabled_;
}
@@ -846,7 +842,6 @@ class Runtime {
bool must_relocate_;
bool is_concurrent_gc_enabled_;
bool is_explicit_gc_disabled_;
- bool dex2oat_enabled_;
bool image_dex2oat_enabled_;
std::string compiler_executable_;
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 5e12bbce05..ae1e08f10b 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -21,7 +21,7 @@
// This file defines the list of keys for RuntimeOptions.
// These can be used with RuntimeOptions.Get/Set/etc, for example:
-// RuntimeOptions opt; bool* dex2oat_enabled = opt.Get(RuntimeOptions::Dex2Oat);
+// RuntimeOptions opt; bool* image_dex2oat_enabled = opt.Get(RuntimeOptions::ImageDex2Oat);
//
// Column Descriptions:
// <<Type>> <<Key Name>> <<Default Value>>
@@ -88,7 +88,6 @@ RUNTIME_OPTIONS_KEY (std::vector<std::string>, \
RUNTIME_OPTIONS_KEY (std::string, JniTrace)
RUNTIME_OPTIONS_KEY (std::string, PatchOat)
RUNTIME_OPTIONS_KEY (bool, Relocate, kDefaultMustRelocate)
-RUNTIME_OPTIONS_KEY (bool, Dex2Oat, true)
RUNTIME_OPTIONS_KEY (bool, ImageDex2Oat, true)
RUNTIME_OPTIONS_KEY (bool, Interpret, false) // -Xint
// Disable the compiler for CC (for now).
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 1986eec353..949fabe7f9 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -383,9 +383,6 @@ void Trace::Start(std::unique_ptr<File>&& trace_file_in,
}
};
std::unique_ptr<File, decltype(deleter)> trace_file(trace_file_in.release(), deleter);
- if (trace_file != nullptr) {
- trace_file->DisableAutoClose();
- }
Thread* self = Thread::Current();
{
@@ -420,7 +417,7 @@ void Trace::Start(std::unique_ptr<File>&& trace_file_in,
if (the_trace_ != nullptr) {
LOG(ERROR) << "Trace already in progress, ignoring this request";
} else {
- enable_stats = (flags && kTraceCountAllocs) != 0;
+ enable_stats = (flags & kTraceCountAllocs) != 0;
the_trace_ = new Trace(trace_file.release(), buffer_size, flags, output_mode, trace_mode);
if (trace_mode == TraceMode::kSampling) {
CHECK_PTHREAD_CALL(pthread_create, (&sampling_pthread_, nullptr, &RunSamplingThread,
@@ -608,7 +605,7 @@ void Trace::Resume() {
Runtime* runtime = Runtime::Current();
// Enable count of allocs if specified in the flags.
- bool enable_stats = (the_trace->flags_ && kTraceCountAllocs) != 0;
+ bool enable_stats = (the_trace->flags_ & kTraceCountAllocs) != 0;
{
gc::ScopedGCCriticalSection gcs(self,
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index a7dc2257f1..452cd8e359 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -150,10 +150,11 @@ std::unique_ptr<VdexFile> VdexFile::OpenAtAddress(uint8_t* mmap_addr,
(writable || unquicken) ? PROT_READ | PROT_WRITE : PROT_READ,
unquicken ? MAP_PRIVATE : MAP_SHARED,
file_fd,
- 0 /* start offset */,
+ /* start */ 0u,
low_4gb,
- mmap_reuse,
vdex_filename.c_str(),
+ mmap_reuse,
+ /* reservation */ nullptr,
error_msg);
if (!mmap.IsValid()) {
*error_msg = "Failed to mmap file " + vdex_filename + " : " + *error_msg;
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index a1b8938eaa..e22afaa72a 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -149,6 +149,7 @@ FailureKind MethodVerifier::VerifyClass(Thread* self,
CompilerCallbacks* callbacks,
bool allow_soft_failures,
HardFailLogMode log_level,
+ uint32_t api_level,
std::string* error) {
if (klass->IsVerified()) {
return FailureKind::kNoFailure;
@@ -188,6 +189,7 @@ FailureKind MethodVerifier::VerifyClass(Thread* self,
callbacks,
allow_soft_failures,
log_level,
+ api_level,
error);
}
@@ -211,6 +213,7 @@ FailureKind MethodVerifier::VerifyClass(Thread* self,
CompilerCallbacks* callbacks,
bool allow_soft_failures,
HardFailLogMode log_level,
+ uint32_t api_level,
std::string* error) {
// A class must not be abstract and final.
if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) {
@@ -261,6 +264,7 @@ FailureKind MethodVerifier::VerifyClass(Thread* self,
allow_soft_failures,
log_level,
/*need_precise_constants*/ false,
+ api_level,
&hard_failure_msg);
if (result.kind == FailureKind::kHardFailure) {
if (failure_data.kind == FailureKind::kHardFailure) {
@@ -322,6 +326,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self,
bool allow_soft_failures,
HardFailLogMode log_level,
bool need_precise_constants,
+ uint32_t api_level,
std::string* hard_failure_msg) {
MethodVerifier::FailureData result;
uint64_t start_ns = kTimeVerifyMethod ? NanoTime() : 0;
@@ -339,7 +344,8 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self,
allow_soft_failures,
need_precise_constants,
false /* verify to dump */,
- true /* allow_thread_suspension */);
+ true /* allow_thread_suspension */,
+ api_level);
if (verifier.Verify()) {
// Verification completed, however failures may be pending that didn't cause the verification
// to hard fail.
@@ -458,7 +464,8 @@ MethodVerifier* MethodVerifier::VerifyMethodAndDump(Thread* self,
const DexFile::ClassDef& class_def,
const DexFile::CodeItem* code_item,
ArtMethod* method,
- uint32_t method_access_flags) {
+ uint32_t method_access_flags,
+ uint32_t api_level) {
MethodVerifier* verifier = new MethodVerifier(self,
dex_file,
dex_cache,
@@ -472,7 +479,8 @@ MethodVerifier* MethodVerifier::VerifyMethodAndDump(Thread* self,
true /* allow_soft_failures */,
true /* need_precise_constants */,
true /* verify_to_dump */,
- true /* allow_thread_suspension */);
+ true /* allow_thread_suspension */,
+ api_level);
verifier->Verify();
verifier->DumpFailures(vios->Stream());
vios->Stream() << verifier->info_messages_.str();
@@ -500,7 +508,8 @@ MethodVerifier::MethodVerifier(Thread* self,
bool allow_soft_failures,
bool need_precise_constants,
bool verify_to_dump,
- bool allow_thread_suspension)
+ bool allow_thread_suspension,
+ uint32_t api_level)
: self_(self),
arena_stack_(Runtime::Current()->GetArenaPool()),
allocator_(&arena_stack_),
@@ -534,7 +543,8 @@ MethodVerifier::MethodVerifier(Thread* self,
verify_to_dump_(verify_to_dump),
allow_thread_suspension_(allow_thread_suspension),
is_constructor_(false),
- link_(nullptr) {
+ link_(nullptr),
+ api_level_(api_level == 0 ? std::numeric_limits<uint32_t>::max() : api_level) {
self->PushVerifier(this);
}
@@ -546,7 +556,8 @@ MethodVerifier::~MethodVerifier() {
void MethodVerifier::FindLocksAtDexPc(
ArtMethod* m,
uint32_t dex_pc,
- std::vector<MethodVerifier::DexLockInfo>* monitor_enter_dex_pcs) {
+ std::vector<MethodVerifier::DexLockInfo>* monitor_enter_dex_pcs,
+ uint32_t api_level) {
StackHandleScope<2> hs(Thread::Current());
Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache()));
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader()));
@@ -563,7 +574,8 @@ void MethodVerifier::FindLocksAtDexPc(
true /* allow_soft_failures */,
false /* need_precise_constants */,
false /* verify_to_dump */,
- false /* allow_thread_suspension */);
+ false /* allow_thread_suspension */,
+ api_level);
verifier.interesting_dex_pc_ = dex_pc;
verifier.monitor_enter_dex_pcs_ = monitor_enter_dex_pcs;
verifier.FindLocksAtDexPc();
@@ -3663,9 +3675,11 @@ const RegType& MethodVerifier::ResolveClass(dex::TypeIndex class_idx) {
// the access-checks interpreter. If result is primitive, skip the access check.
//
// Note: we do this for unresolved classes to trigger re-verification at runtime.
- if (C == CheckAccess::kYes && result->IsNonZeroReferenceTypes()) {
+ if (C == CheckAccess::kYes &&
+ result->IsNonZeroReferenceTypes() &&
+ (api_level_ >= 28u || !result->IsUnresolvedTypes())) {
const RegType& referrer = GetDeclaringClass();
- if (!referrer.CanAccess(*result)) {
+ if ((api_level_ >= 28u || !referrer.IsUnresolvedTypes()) && !referrer.CanAccess(*result)) {
Fail(VERIFY_ERROR_ACCESS_CLASS) << "(possibly) illegal class access: '"
<< referrer << "' -> '" << *result << "'";
}
@@ -4725,7 +4739,7 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType&
DCHECK(!can_load_classes_ || self_->IsExceptionPending());
self_->ClearException();
}
- } else {
+ } else if (api_level_ >= 28u) {
// If we don't have the field (it seems we failed resolution) and this is a PUT, we need to
// redo verification at runtime as the field may be final, unless the field id shows it's in
// the same class.
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 9890af9d95..eef22807ec 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -100,6 +100,7 @@ class MethodVerifier {
CompilerCallbacks* callbacks,
bool allow_soft_failures,
HardFailLogMode log_level,
+ uint32_t api_level,
std::string* error)
REQUIRES_SHARED(Locks::mutator_lock_);
static FailureKind VerifyClass(Thread* self,
@@ -110,6 +111,7 @@ class MethodVerifier {
CompilerCallbacks* callbacks,
bool allow_soft_failures,
HardFailLogMode log_level,
+ uint32_t api_level,
std::string* error)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -121,7 +123,8 @@ class MethodVerifier {
Handle<mirror::ClassLoader> class_loader,
const DexFile::ClassDef& class_def,
const DexFile::CodeItem* code_item, ArtMethod* method,
- uint32_t method_access_flags)
+ uint32_t method_access_flags,
+ uint32_t api_level)
REQUIRES_SHARED(Locks::mutator_lock_);
uint8_t EncodePcToReferenceMapData() const;
@@ -163,8 +166,10 @@ class MethodVerifier {
// Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding
// to the locks held at 'dex_pc' in method 'm'.
// Note: this is the only situation where the verifier will visit quickened instructions.
- static void FindLocksAtDexPc(ArtMethod* m, uint32_t dex_pc,
- std::vector<DexLockInfo>* monitor_enter_dex_pcs)
+ static void FindLocksAtDexPc(ArtMethod* m,
+ uint32_t dex_pc,
+ std::vector<DexLockInfo>* monitor_enter_dex_pcs,
+ uint32_t api_level)
REQUIRES_SHARED(Locks::mutator_lock_);
static void Init() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -242,7 +247,8 @@ class MethodVerifier {
bool allow_soft_failures,
bool need_precise_constants,
bool verify_to_dump,
- bool allow_thread_suspension)
+ bool allow_thread_suspension,
+ uint32_t api_level)
REQUIRES_SHARED(Locks::mutator_lock_);
void UninstantiableError(const char* descriptor);
@@ -299,6 +305,7 @@ class MethodVerifier {
bool allow_soft_failures,
HardFailLogMode log_level,
bool need_precise_constants,
+ uint32_t api_level,
std::string* hard_failure_msg)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -790,6 +797,10 @@ class MethodVerifier {
// Link, for the method verifier root linked list.
MethodVerifier* link_;
+ // API level, for dependent checks. Note: we do not use '0' for unset here, to simplify checks.
+ // Instead, unset level should correspond to max().
+ const uint32_t api_level_;
+
friend class art::Thread;
friend class VerifierDepsTest;
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index d1be9fa6f8..cedc583986 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -42,7 +42,7 @@ class MethodVerifierTest : public CommonRuntimeTest {
// Verify the class
std::string error_msg;
FailureKind failure = MethodVerifier::VerifyClass(
- self, klass, nullptr, true, HardFailLogMode::kLogWarning, &error_msg);
+ self, klass, nullptr, true, HardFailLogMode::kLogWarning, /* api_level */ 0u, &error_msg);
if (android::base::StartsWith(descriptor, "Ljava/lang/invoke")) {
ASSERT_TRUE(failure == FailureKind::kSoftFailure ||
diff --git a/test/071-dexfile-map-clean/run b/test/071-dexfile-map-clean/run
index 9c100ec497..afa2ff7462 100755
--- a/test/071-dexfile-map-clean/run
+++ b/test/071-dexfile-map-clean/run
@@ -14,13 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Run without dex2oat so that we don't create oat/vdex files
-# when trying to load the secondary dex file.
-
-# In this way, the secondary dex file will be forced to be
-# loaded directly.
-#
-# In addition, make sure we call 'sync'
+# Make sure we call 'sync'
# before executing dalvikvm because otherwise
# it's highly likely the pushed JAR files haven't
# been committed to permanent storage yet,
@@ -28,4 +22,4 @@
# the memory is dirty (despite being file-backed).
# (Note: this was reproducible 100% of the time on
# a target angler device).
-./default-run "$@" --no-dex2oat --sync
+./default-run "$@" --sync
diff --git a/test/071-dexfile-map-clean/src/Main.java b/test/071-dexfile-map-clean/src/Main.java
index 8a196dd52e..e784ac601c 100644
--- a/test/071-dexfile-map-clean/src/Main.java
+++ b/test/071-dexfile-map-clean/src/Main.java
@@ -90,19 +90,6 @@ public class Main {
return true;
}
- // This test takes relies on dex2oat being skipped.
- // (enforced in 'run' file by using '--no-dex2oat'
- //
- // This could happen in a non-test situation
- // if a secondary dex file is loaded (but not yet maintenance-mode compiled)
- // with JIT.
- //
- // Or it could also happen if a secondary dex file is loaded and forced
- // into running into the interpreter (e.g. duplicate classes).
- //
- // Rather than relying on those weird fallbacks,
- // we force the runtime not to dex2oat the dex file to ensure
- // this test is repeatable and less brittle.
private static void testDexMemoryMaps() throws Exception {
// Ensure that the secondary dex file is mapped clean (directly from JAR file).
String smaps = new String(Files.readAllBytes(Paths.get("/proc/self/smaps")));
diff --git a/test/116-nodex2oat/expected.txt b/test/116-nodex2oat/expected.txt
index 157dfc4ea4..c6c7daa21d 100644
--- a/test/116-nodex2oat/expected.txt
+++ b/test/116-nodex2oat/expected.txt
@@ -1,9 +1,2 @@
-Run -Xnodex2oat
JNI_OnLoad called
-Has oat is false, is dex2oat enabled is false.
-Run -Xdex2oat
-JNI_OnLoad called
-Has oat is true, is dex2oat enabled is true.
-Run default
-JNI_OnLoad called
-Has oat is true, is dex2oat enabled is true.
+Has oat is false.
diff --git a/test/116-nodex2oat/run b/test/116-nodex2oat/run
index d7984cece2..9063685fe7 100755
--- a/test/116-nodex2oat/run
+++ b/test/116-nodex2oat/run
@@ -24,20 +24,4 @@ if [[ "${flags}" == *--prebuild* || "${flags}" != *--no-prebuild* ]] ; then
exit 1
fi
-# Make sure we can run without an oat file.
-echo "Run -Xnodex2oat"
-${RUN} ${flags} --runtime-option -Xnodex2oat
-return_status1=$?
-
-# Make sure we can run with the oat file.
-echo "Run -Xdex2oat"
-${RUN} ${flags} --runtime-option -Xdex2oat
-return_status2=$?
-
-# Make sure we can run with the default settings.
-echo "Run default"
${RUN} ${flags}
-return_status3=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && (exit $return_status2) && (exit $return_status3)
diff --git a/test/116-nodex2oat/src/Main.java b/test/116-nodex2oat/src/Main.java
index 229735f4b8..5491c492a9 100644
--- a/test/116-nodex2oat/src/Main.java
+++ b/test/116-nodex2oat/src/Main.java
@@ -17,17 +17,8 @@
public class Main {
public static void main(String[] args) {
System.loadLibrary(args[0]);
- System.out.println(
- "Has oat is " + hasOatFile() + ", is dex2oat enabled is " + isDex2OatEnabled() + ".");
-
- if (hasOatFile() && !isDex2OatEnabled()) {
- throw new Error("Application with dex2oat disabled runs with an oat file");
- } else if (!hasOatFile() && isDex2OatEnabled()) {
- throw new Error("Application with dex2oat enabled runs without an oat file");
- }
+ System.out.println("Has oat is " + hasOatFile() + ".");
}
private native static boolean hasOatFile();
-
- private native static boolean isDex2OatEnabled();
}
diff --git a/test/117-nopatchoat/expected.txt b/test/117-nopatchoat/expected.txt
index 0cd4715d09..7a24e31775 100644
--- a/test/117-nopatchoat/expected.txt
+++ b/test/117-nopatchoat/expected.txt
@@ -1,12 +1,3 @@
-Run without dex2oat/patchoat
JNI_OnLoad called
-dex2oat & patchoat are disabled, has oat is true, has executable oat is expected.
-This is a function call
-Run with dexoat/patchoat
-JNI_OnLoad called
-dex2oat & patchoat are enabled, has oat is true, has executable oat is expected.
-This is a function call
-Run default
-JNI_OnLoad called
-dex2oat & patchoat are enabled, has oat is true, has executable oat is expected.
+Has oat is true, has executable oat is expected.
This is a function call
diff --git a/test/117-nopatchoat/run b/test/117-nopatchoat/run
index 0627fe5069..4c33f7a450 100755
--- a/test/117-nopatchoat/run
+++ b/test/117-nopatchoat/run
@@ -34,20 +34,4 @@ if [[ "${flags}" == *--no-relocate* ]] ; then
exit 1
fi
-# Make sure we can run without relocation
-echo "Run without dex2oat/patchoat"
-${RUN} ${flags} --runtime-option -Xnodex2oat
-return_status1=$?
-
-# Make sure we can run with the oat file.
-echo "Run with dexoat/patchoat"
-${RUN} ${flags} --runtime-option -Xdex2oat
-return_status2=$?
-
-# Make sure we can run with the default settings.
-echo "Run default"
${RUN} ${flags}
-return_status3=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && (exit $return_status2) && (exit $return_status3)
diff --git a/test/117-nopatchoat/src/Main.java b/test/117-nopatchoat/src/Main.java
index 816eb171a4..ef47ab9ee4 100644
--- a/test/117-nopatchoat/src/Main.java
+++ b/test/117-nopatchoat/src/Main.java
@@ -23,18 +23,13 @@ public class Main {
// Hitting this condition should be rare and ideally we would prevent it from happening but
// there is no way to do so without major changes to the run-test framework.
boolean executable_correct = (needsRelocation() ?
- hasExecutableOat() == (isDex2OatEnabled() || isRelocationDeltaZero()) :
+ hasExecutableOat() == isRelocationDeltaZero() :
hasExecutableOat() == true);
System.out.println(
- "dex2oat & patchoat are " + ((isDex2OatEnabled()) ? "enabled" : "disabled") +
- ", has oat is " + hasOatFile() + ", has executable oat is " + (
+ "Has oat is " + hasOatFile() + ", has executable oat is " + (
executable_correct ? "expected" : "not expected") + ".");
- if (!hasOatFile() && isDex2OatEnabled()) {
- throw new Error("Application with dex2oat enabled runs without an oat file");
- }
-
System.out.println(functionCall());
}
@@ -47,8 +42,6 @@ public class Main {
return ret.substring(0, ret.length() - 1);
}
- private native static boolean isDex2OatEnabled();
-
private native static boolean needsRelocation();
private native static boolean hasOatFile();
diff --git a/test/118-noimage-dex2oat/run b/test/118-noimage-dex2oat/run
index e1e2577ae3..d68b0a0b2c 100644
--- a/test/118-noimage-dex2oat/run
+++ b/test/118-noimage-dex2oat/run
@@ -47,12 +47,12 @@ bpath_arg="--runtime-option -Xbootclasspath:${bpath}"
# Make sure we can run without an oat file.
echo "Run -Xnoimage-dex2oat"
-${RUN} ${flags} ${bpath_arg} --runtime-option -Xnoimage-dex2oat --runtime-option -Xnodex2oat
+${RUN} ${flags} ${bpath_arg} --runtime-option -Xnoimage-dex2oat
return_status1=$?
# Make sure we cannot run without an oat file without fallback.
echo "Run -Xnoimage-dex2oat -Xno-dex-file-fallback"
-${RUN} ${flags} ${bpath_arg} --runtime-option -Xnoimage-dex2oat --runtime-option -Xnodex2oat \
+${RUN} ${flags} ${bpath_arg} --runtime-option -Xnoimage-dex2oat \
--runtime-option -Xno-dex-file-fallback
return_status2=$?
diff --git a/test/134-nodex2oat-nofallback/run b/test/134-nodex2oat-nofallback/run
index 33265ac471..359d22dfa0 100755
--- a/test/134-nodex2oat-nofallback/run
+++ b/test/134-nodex2oat-nofallback/run
@@ -17,6 +17,6 @@
flags="${@}"
# Make sure we cannot run without an oat file without fallback.
-${RUN} ${flags} --runtime-option -Xnodex2oat --runtime-option -Xno-dex-file-fallback
+${RUN} ${flags} --runtime-option -Xno-dex-file-fallback
# Suppress the exit value. This isn't expected to be successful.
echo "Exit status:" $?
diff --git a/test/134-nodex2oat-nofallback/src/Main.java b/test/134-nodex2oat-nofallback/src/Main.java
index 086ffb9295..73f67c4e38 100644
--- a/test/134-nodex2oat-nofallback/src/Main.java
+++ b/test/134-nodex2oat-nofallback/src/Main.java
@@ -17,17 +17,8 @@
public class Main {
public static void main(String[] args) {
System.loadLibrary(args[0]);
- System.out.println(
- "Has oat is " + hasOat() + ", is dex2oat enabled is " + isDex2OatEnabled() + ".");
-
- if (hasOat() && !isDex2OatEnabled()) {
- throw new Error("Application with dex2oat disabled runs with an oat file");
- } else if (!hasOat() && isDex2OatEnabled()) {
- throw new Error("Application with dex2oat enabled runs without an oat file");
- }
+ System.out.println("Has oat is " + hasOat());
}
private native static boolean hasOat();
-
- private native static boolean isDex2OatEnabled();
}
diff --git a/test/138-duplicate-classes-check2/run b/test/138-duplicate-classes-check2/run
deleted file mode 100755
index 8494ad9aad..0000000000
--- a/test/138-duplicate-classes-check2/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# We want to run as no-dex-file-fallback to confirm that even though the -ex file has a symbolic
-# reference to A, there's no class-def, so we don't detect a collision.
-exec ${RUN} --runtime-option -Xno-dex-file-fallback "${@}"
diff --git a/test/147-stripped-dex-fallback/run b/test/147-stripped-dex-fallback/run
index 37c3e1fd88..1f1d22e64e 100755
--- a/test/147-stripped-dex-fallback/run
+++ b/test/147-stripped-dex-fallback/run
@@ -21,4 +21,4 @@ if [[ "${flags}" == *--no-prebuild* ]] ; then
exit 1
fi
-${RUN} ${flags} --strip-dex --runtime-option -Xnodex2oat
+${RUN} ${flags} --strip-dex
diff --git a/test/305-other-fault-handler/fault_handler.cc b/test/305-other-fault-handler/fault_handler.cc
index 93bb148745..db3f1f4bf7 100644
--- a/test/305-other-fault-handler/fault_handler.cc
+++ b/test/305-other-fault-handler/fault_handler.cc
@@ -40,6 +40,7 @@ class TestFaultHandler final : public FaultHandler {
/* prot */ PROT_NONE,
/* low_4gb */ false,
/* reuse */ false,
+ /* reservation */ nullptr,
/* error_msg */ &map_error_,
/* use_ashmem */ false)),
was_hit_(false) {
diff --git a/test/510-checker-try-catch/smali/Runtime.smali b/test/510-checker-try-catch/smali/Runtime.smali
index 19b43a3f30..d080a0c94b 100644
--- a/test/510-checker-try-catch/smali/Runtime.smali
+++ b/test/510-checker-try-catch/smali/Runtime.smali
@@ -549,6 +549,76 @@
.end array-data
.end method
+
+## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after)
+## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+## CHECK-DAG: <<Offset:i\d+>> IntConstant 12
+## CHECK-DAG: <<IndexParam:i\d+>> ParameterValue
+## CHECK-DAG: <<Array:l\d+>> ParameterValue
+
+## CHECK-DAG: <<NullCh1:l\d+>> NullCheck [<<Array>>]
+## CHECK-DAG: <<Length:i\d+>> ArrayLength
+## CHECK-DAG: <<BoundsCh1:i\d+>> BoundsCheck [<<IndexParam>>,<<Length>>]
+## CHECK-DAG: <<IntAddr1:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>]
+## CHECK-DAG: ArrayGet [<<IntAddr1>>,<<BoundsCh1>>]
+## CHECK-DAG: TryBoundary
+
+## CHECK-DAG: <<Xplus1:i\d+>> Add [<<IndexParam>>,<<Const1>>]
+## CHECK-DAG: <<BoundsCh2:i\d+>> BoundsCheck [<<Xplus1>>,<<Length>>]
+## CHECK-DAG: ArrayGet [<<IntAddr1>>,<<BoundsCh2>>]
+## CHECK-DAG: TryBoundary
+
+## CHECK-DAG: <<Phi:i\d+>> Phi [<<Xplus1>>]
+## CHECK-DAG: <<Phiplus1:i\d+>> Add [<<Phi>>,<<Const1>>]
+## CHECK-DAG: <<BoundsCh3:i\d+>> BoundsCheck [<<Phiplus1>>,<<Length>>]
+## CHECK-DAG: <<IntAddr3:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>]
+## CHECK-DAG: ArrayGet [<<IntAddr3>>,<<BoundsCh3>>]
+
+## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after)
+## CHECK: NullCheck
+## CHECK-NOT: NullCheck
+
+## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after)
+## CHECK: IntermediateAddress
+## CHECK: IntermediateAddress
+## CHECK-NOT: IntermediateAddress
+
+## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after)
+## CHECK: BoundsCheck
+## CHECK: BoundsCheck
+## CHECK: BoundsCheck
+## CHECK-NOT: BoundsCheck
+
+## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after)
+## CHECK: ArrayGet
+## CHECK: ArrayGet
+## CHECK: ArrayGet
+## CHECK-NOT: ArrayGet
+.method public static testIntAddressCatch(I[I)I
+ .registers 4
+ aget v0, p1, p0
+ add-int v1, v0, v0
+
+ :try_start
+ const/4 v0, 0x1
+ add-int p0, p0, v0
+ aget v0, p1, p0
+
+ :try_end
+ .catch Ljava/lang/ArithmException; {:try_start .. :try_end} :catch_block
+
+ :return
+ add-int v1, v1, v0
+ return v1
+
+ :catch_block
+ const/4 v0, 0x1
+ add-int p0, p0, v0
+ aget v0, p1, p0
+
+ goto :return
+.end method
+
.field public static intArray:[I
.field public static longArray:[J
.field public static floatArray:[F
diff --git a/test/510-checker-try-catch/src/Main.java b/test/510-checker-try-catch/src/Main.java
index d6dcd30f3c..18658cd5e0 100644
--- a/test/510-checker-try-catch/src/Main.java
+++ b/test/510-checker-try-catch/src/Main.java
@@ -37,6 +37,114 @@ public class Main {
public int expected;
}
+ // Test that IntermediateAddress instruction is not alive across BoundsCheck which can throw to
+ // a catch block.
+ //
+ /// CHECK-START-{ARM,ARM64}: void Main.boundsCheckAndCatch(int, int[], int[]) GVN$after_arch (before)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ /// CHECK-DAG: <<Offset:i\d+>> IntConstant 12
+ /// CHECK-DAG: <<IndexParam:i\d+>> ParameterValue
+ /// CHECK-DAG: <<ArrayA:l\d+>> ParameterValue
+ /// CHECK-DAG: <<ArrayB:l\d+>> ParameterValue
+ //
+
+ /// CHECK-DAG: <<NullCh1:l\d+>> NullCheck [<<ArrayA>>]
+ /// CHECK-DAG: <<LengthA:i\d+>> ArrayLength
+ /// CHECK-DAG: <<BoundsCh1:i\d+>> BoundsCheck [<<IndexParam>>,<<LengthA>>]
+ /// CHECK-DAG: <<IntAddr1:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>]
+ /// CHECK-DAG: ArraySet [<<IntAddr1>>,<<BoundsCh1>>,<<Const1>>]
+ /// CHECK-DAG: TryBoundary
+ //
+ /// CHECK-DAG: <<IntAddr2:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>]
+ /// CHECK-DAG: ArraySet [<<IntAddr2>>,<<BoundsCh1>>,<<Const2>>]
+ /// CHECK-DAG: <<NullChB:l\d+>> NullCheck [<<ArrayB>>]
+ /// CHECK-DAG: <<LengthB:i\d+>> ArrayLength
+ /// CHECK-DAG: <<BoundsChB:i\d+>> BoundsCheck [<<Const0>>,<<LengthB>>]
+ /// CHECK-DAG: <<GetB:i\d+>> ArrayGet [<<NullChB>>,<<BoundsChB>>]
+ /// CHECK-DAG: <<ZeroCheck:i\d+>> DivZeroCheck [<<IndexParam>>]
+ /// CHECK-DAG: <<Div:i\d+>> Div [<<GetB>>,<<ZeroCheck>>]
+ /// CHECK-DAG: <<Xplus1:i\d+>> Add [<<IndexParam>>,<<Const1>>]
+ /// CHECK-DAG: <<BoundsCh2:i\d+>> BoundsCheck [<<Xplus1>>,<<LengthA>>]
+ /// CHECK-DAG: <<IntAddr3:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>]
+ /// CHECK-DAG: ArraySet [<<IntAddr3>>,<<BoundsCh2>>,<<Div>>]
+ /// CHECK-DAG: TryBoundary
+ //
+ /// CHECK-DAG: ClearException
+ /// CHECK-DAG: <<IntAddr4:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>]
+ /// CHECK-DAG: ArraySet [<<IntAddr4>>,<<BoundsCh1>>,<<Const1>>]
+ //
+ /// CHECK-NOT: NullCheck
+ /// CHECK-NOT: IntermediateAddress
+
+ /// CHECK-START-{ARM,ARM64}: void Main.boundsCheckAndCatch(int, int[], int[]) GVN$after_arch (after)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ /// CHECK-DAG: <<Offset:i\d+>> IntConstant 12
+ /// CHECK-DAG: <<IndexParam:i\d+>> ParameterValue
+ /// CHECK-DAG: <<ArrayA:l\d+>> ParameterValue
+ /// CHECK-DAG: <<ArrayB:l\d+>> ParameterValue
+ //
+ /// CHECK-DAG: <<NullCh1:l\d+>> NullCheck [<<ArrayA>>]
+ /// CHECK-DAG: <<LengthA:i\d+>> ArrayLength
+ /// CHECK-DAG: <<BoundsCh1:i\d+>> BoundsCheck [<<IndexParam>>,<<LengthA>>]
+ /// CHECK-DAG: <<IntAddr1:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>]
+ /// CHECK-DAG: ArraySet [<<IntAddr1>>,<<BoundsCh1>>,<<Const1>>]
+ /// CHECK-DAG: TryBoundary
+ //
+ /// CHECK-DAG: ArraySet [<<IntAddr1>>,<<BoundsCh1>>,<<Const2>>]
+ /// CHECK-DAG: <<NullChB:l\d+>> NullCheck [<<ArrayB>>]
+ /// CHECK-DAG: <<LengthB:i\d+>> ArrayLength
+ /// CHECK-DAG: <<BoundsChB:i\d+>> BoundsCheck [<<Const0>>,<<LengthB>>]
+ /// CHECK-DAG: <<GetB:i\d+>> ArrayGet [<<NullChB>>,<<BoundsChB>>]
+ /// CHECK-DAG: <<ZeroCheck:i\d+>> DivZeroCheck [<<IndexParam>>]
+ /// CHECK-DAG: <<Div:i\d+>> Div [<<GetB>>,<<ZeroCheck>>]
+ /// CHECK-DAG: <<Xplus1:i\d+>> Add [<<IndexParam>>,<<Const1>>]
+ /// CHECK-DAG: <<BoundsCh2:i\d+>> BoundsCheck [<<Xplus1>>,<<LengthA>>]
+ /// CHECK-DAG: ArraySet [<<IntAddr1>>,<<BoundsCh2>>,<<Div>>]
+ /// CHECK-DAG: TryBoundary
+ //
+ /// CHECK-DAG: ClearException
+ /// CHECK-DAG: <<IntAddr4:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>]
+ /// CHECK-DAG: ArraySet [<<IntAddr4>>,<<BoundsCh1>>,<<Const1>>]
+ //
+ /// CHECK-NOT: NullCheck
+ /// CHECK-NOT: IntermediateAddress
+
+ // Make sure that BoundsCheck, DivZeroCheck and NullCheck don't stop IntermediateAddress sharing.
+ public static void boundsCheckAndCatch(int x, int[] a, int[] b) {
+ a[x] = 1;
+ try {
+ a[x] = 2;
+ a[x + 1] = b[0] / x;
+ } catch (Exception e) {
+ a[x] = 1;
+ }
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public final static int ARRAY_SIZE = 128;
+
+ public static void testBoundsCheckAndCatch() {
+ int[] a = new int[ARRAY_SIZE];
+ int[] b = new int[ARRAY_SIZE];
+
+ int index = ARRAY_SIZE - 2;
+ boundsCheckAndCatch(index, a, b);
+ expectEquals(2, a[index]);
+
+ index = ARRAY_SIZE - 1;
+ boundsCheckAndCatch(index, a, b);
+ expectEquals(1, a[index]);
+ }
+
public static void testMethod(String method) throws Exception {
Class<?> c = Class.forName("Runtime");
Method m = c.getMethod(method, boolean.class, boolean.class);
@@ -52,6 +160,14 @@ public class Main {
}
}
+ public static void testIntAddressCatch() throws Exception {
+ int[] a = new int[3];
+
+ Class<?> c = Class.forName("Runtime");
+ Method m = c.getMethod("testIntAddressCatch", int.class, Class.forName("[I"));
+ m.invoke(null, 0, a);
+ }
+
public static void main(String[] args) throws Exception {
testMethod("testUseAfterCatch_int");
testMethod("testUseAfterCatch_long");
@@ -64,5 +180,8 @@ public class Main {
testMethod("testCatchPhi_double");
testMethod("testCatchPhi_singleSlot");
testMethod("testCatchPhi_doubleSlot");
+
+ testBoundsCheckAndCatch();
+ testIntAddressCatch();
}
}
diff --git a/test/527-checker-array-access-split/src/Main.java b/test/527-checker-array-access-split/src/Main.java
index 935b37858d..f83c924de9 100644
--- a/test/527-checker-array-access-split/src/Main.java
+++ b/test/527-checker-array-access-split/src/Main.java
@@ -552,6 +552,28 @@ public class Main {
return (int)s;
}
+ //
+ // Check that IntermediateAddress can be shared across BoundsCheck, DivZeroCheck and NullCheck -
+ // instruction which have fatal slow paths.
+ //
+ /// CHECK-START-{ARM,ARM64}: void Main.checkGVNForFatalChecks(int, int, char[], int[]) GVN$after_arch (before)
+ /// CHECK: IntermediateAddress
+ /// CHECK: IntermediateAddress
+ //
+ /// CHECK-NOT: IntermediateAddress
+
+ /// CHECK-START-{ARM,ARM64}: void Main.checkGVNForFatalChecks(int, int, char[], int[]) GVN$after_arch (after)
+ /// CHECK: IntermediateAddress
+ //
+ /// CHECK-NOT: IntermediateAddress
+ public final static void checkGVNForFatalChecks(int begin, int end, char[] buf1, int[] buf2) {
+ buf1[begin] = 'a';
+ buf2[0] = begin / end;
+ buf1[end] = 'n';
+ }
+
+ public final static int ARRAY_SIZE = 128;
+
public static void main(String[] args) {
int[] array = {123, 456, 789};
@@ -575,5 +597,10 @@ public class Main {
assertIntEquals(2097152, canMergeAfterBCE2());
assertIntEquals(18, checkLongFloatDouble());
+
+ char[] c1 = new char[ARRAY_SIZE];
+ int[] i1 = new int[ARRAY_SIZE];
+ checkGVNForFatalChecks(1, 2, c1, i1);
+ assertIntEquals('n', c1[2]);
}
}
diff --git a/test/667-jit-jni-stub/run b/test/667-jit-jni-stub/run
index f235c6bc90..b7ce9132ab 100755
--- a/test/667-jit-jni-stub/run
+++ b/test/667-jit-jni-stub/run
@@ -16,4 +16,4 @@
# Disable AOT compilation of JNI stubs.
# Ensure this test is not subject to unexpected code collection.
-${RUN} "${@}" --no-prebuild --no-dex2oat --runtime-option -Xjitinitialsize:32M
+${RUN} "${@}" --no-prebuild --runtime-option -Xjitinitialsize:32M
diff --git a/test/667-jit-jni-stub/src/Main.java b/test/667-jit-jni-stub/src/Main.java
index 794308d6e1..234c5daf4c 100644
--- a/test/667-jit-jni-stub/src/Main.java
+++ b/test/667-jit-jni-stub/src/Main.java
@@ -18,7 +18,7 @@ public class Main {
public static void main(String[] args) throws Exception {
System.loadLibrary(args[0]);
if (isAotCompiled(Main.class, "hasJit")) {
- throw new Error("This test must be run with --no-prebuild --no-dex2oat!");
+ throw new Error("This test must be run with --no-prebuild!");
}
if (!hasJit()) {
return;
diff --git a/test/677-fsi/expected.txt b/test/677-fsi/expected.txt
index c7fb8fed77..2b073430b6 100644
--- a/test/677-fsi/expected.txt
+++ b/test/677-fsi/expected.txt
@@ -1,3 +1,2 @@
oat file has dex code, but APK has uncompressed dex code
-oat file has dex code, but APK has uncompressed dex code
Hello World
diff --git a/test/677-fsi2/expected.txt b/test/677-fsi2/expected.txt
index de008470fe..557db03de9 100644
--- a/test/677-fsi2/expected.txt
+++ b/test/677-fsi2/expected.txt
@@ -1,4 +1 @@
-Run default
-Hello World
-Run without dex2oat
Hello World
diff --git a/test/677-fsi2/run b/test/677-fsi2/run
index 039a6a78f0..651f082863 100644
--- a/test/677-fsi2/run
+++ b/test/677-fsi2/run
@@ -14,12 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-echo "Run default"
${RUN} $@ --runtime-option -Xonly-use-system-oat-files
-return_status1=$?
-
-echo "Run without dex2oat"
-${RUN} $@ --no-dex2oat --runtime-option -Xonly-use-system-oat-files
-return_status2=$?
-
-(exit $return_status1) && (exit $return_status2)
diff --git a/test/706-checker-scheduler/src/Main.java b/test/706-checker-scheduler/src/Main.java
index 9f4caecccd..af18193388 100644
--- a/test/706-checker-scheduler/src/Main.java
+++ b/test/706-checker-scheduler/src/Main.java
@@ -292,8 +292,7 @@ public class Main {
/// CHECK: <<ArraySet1:v\d+>> ArraySet [<<Addr1>>,{{i\d+}},{{i\d+}}] loop:<<Loop>> outer_loop:none
/// CHECK: <<ArrayGet2:i\d+>> ArrayGet [<<NullB>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
/// CHECK: Add loop:<<Loop>> outer_loop:none
- /// CHECK: <<Addr2:i\d+>> IntermediateAddress [<<NullA>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
- /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Addr2>>,{{i\d+}},{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Addr1>>,{{i\d+}},{{i\d+}}] loop:<<Loop>> outer_loop:none
/// CHECK: Add loop:<<Loop>> outer_loop:none
/// CHECK-START-ARM64: void Main.CrossOverLoop(int[], int[]) scheduler (after)
@@ -303,13 +302,12 @@ public class Main {
/// CHECK: <<NullA:l\d+>> NullCheck [<<ParamA>>] loop:none
/// CHECK: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK: <<ArrayGet1:i\d+>> ArrayGet [<<NullB>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
- /// CHECK: Add loop:<<Loop>> outer_loop:none
/// CHECK: <<Addr1:i\d+>> IntermediateAddress [<<NullA>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK: Add [<<ArrayGet1>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
/// CHECK: <<ArraySet1:v\d+>> ArraySet [<<Addr1>>,{{i\d+}},{{i\d+}}] loop:<<Loop>> outer_loop:none
/// CHECK: <<ArrayGet2:i\d+>> ArrayGet [<<NullB>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
/// CHECK: Add loop:<<Loop>> outer_loop:none
- /// CHECK: <<Addr2:i\d+>> IntermediateAddress [<<NullA>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
- /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Addr2>>,{{i\d+}},{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Addr1>>,{{i\d+}},{{i\d+}}] loop:<<Loop>> outer_loop:none
/// CHECK: Add loop:<<Loop>> outer_loop:none
private static void CrossOverLoop(int a[], int b[]) {
b[20] = 99;
diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc
index 2a06a7b9d6..521f9a6c72 100644
--- a/test/906-iterate-heap/iterate_heap.cc
+++ b/test/906-iterate-heap/iterate_heap.cc
@@ -418,5 +418,21 @@ extern "C" JNIEXPORT jboolean JNICALL Java_art_Test906_checkInitialized(
return (status & JVMTI_CLASS_STATUS_INITIALIZED) != 0;
}
+extern "C" JNIEXPORT jint JNICALL Java_art_Test906_iterateOverInstancesCount(
+ JNIEnv* env, jclass, jclass target) {
+ jint cnt = 0;
+ auto count_func = [](jlong, jlong, jlong*, void* user_data) -> jvmtiIterationControl {
+ *reinterpret_cast<jint*>(user_data) += 1;
+ return JVMTI_ITERATION_CONTINUE;
+ };
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->IterateOverInstancesOfClass(target,
+ JVMTI_HEAP_OBJECT_EITHER,
+ count_func,
+ &cnt));
+ return cnt;
+}
+
} // namespace Test906IterateHeap
} // namespace art
diff --git a/test/906-iterate-heap/src/art/Test906.java b/test/906-iterate-heap/src/art/Test906.java
index be9663a6d4..190f36f5d4 100644
--- a/test/906-iterate-heap/src/art/Test906.java
+++ b/test/906-iterate-heap/src/art/Test906.java
@@ -18,6 +18,7 @@ package art;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Random;
public class Test906 {
public static void run() throws Exception {
@@ -69,6 +70,40 @@ public class Test906 {
throw lastThrow;
}
+ private static Object[] GenTs(Class<?> k) throws Exception {
+ Object[] ret = new Object[new Random().nextInt(100) + 10];
+ for (int i = 0; i < ret.length; i++) {
+ ret[i] = k.newInstance();
+ }
+ return ret;
+ }
+
+ private static void checkEq(int a, int b) {
+ if (a != b) {
+ Error e = new Error("Failed: Expected equal " + a + " and " + b);
+ System.out.println(e);
+ e.printStackTrace(System.out);
+ }
+ }
+
+ public static class Foo {}
+ public static class Bar extends Foo {}
+ public static class Baz extends Bar {}
+ public static class Alpha extends Bar {}
+ public static class MISSING extends Baz {}
+ private static void testIterateOverInstances() throws Exception {
+ Object[] foos = GenTs(Foo.class);
+ Object[] bars = GenTs(Bar.class);
+ Object[] bazs = GenTs(Baz.class);
+ Object[] alphas = GenTs(Alpha.class);
+ checkEq(0, iterateOverInstancesCount(MISSING.class));
+ checkEq(alphas.length, iterateOverInstancesCount(Alpha.class));
+ checkEq(bazs.length, iterateOverInstancesCount(Baz.class));
+ checkEq(bazs.length + alphas.length + bars.length, iterateOverInstancesCount(Bar.class));
+ checkEq(bazs.length + alphas.length + bars.length + foos.length,
+ iterateOverInstancesCount(Foo.class));
+ }
+
public static void doTest() throws Exception {
A a = new A();
B b = new B();
@@ -86,6 +121,8 @@ public class Test906 {
testHeapCount();
+ testIterateOverInstances();
+
long classTags[] = new long[100];
long sizes[] = new long[100];
long tags[] = new long[100];
@@ -308,6 +345,8 @@ public class Test906 {
return Main.getTag(o);
}
+ private static native int iterateOverInstancesCount(Class<?> klass);
+
private static native boolean checkInitialized(Class<?> klass);
private static native int iterateThroughHeapCount(int heapFilter,
Class<?> klassFilter, int stopAfter);
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 9344b24b5d..da79164f12 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -71,13 +71,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_runtimeIsSoftFail(JNIEnv* env AT
return Runtime::Current()->IsVerificationSoftFail() ? JNI_TRUE : JNI_FALSE;
}
-// public static native boolean isDex2OatEnabled();
-
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_isDex2OatEnabled(JNIEnv* env ATTRIBUTE_UNUSED,
- jclass cls ATTRIBUTE_UNUSED) {
- return Runtime::Current()->IsDex2OatEnabled();
-}
-
// public static native boolean hasImage();
extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasImage(JNIEnv* env ATTRIBUTE_UNUSED,
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 6555b46c0d..bd58ae37ec 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -59,7 +59,7 @@ USE_JVMTI="n"
VERIFY="y" # y=yes,n=no,s=softfail
ZYGOTE=""
DEX_VERIFY=""
-USE_DEX2OAT_AND_PATCHOAT="y"
+USE_PATCHOAT="y"
INSTRUCTION_SET_FEATURES=""
ARGS=""
EXTERNAL_LOG_TAGS="n" # if y respect externally set ANDROID_LOG_TAGS.
@@ -166,14 +166,9 @@ while true; do
shift
BOOT_IMAGE="$1"
shift
- elif [ "x$1" = "x--no-dex2oat" ]; then
- DEX2OAT="-Xcompiler:${FALSE_BIN}"
- USE_DEX2OAT_AND_PATCHOAT="n"
- PREBUILD="n" # Do not use prebuilt odex, either.
- shift
elif [ "x$1" = "x--no-patchoat" ]; then
PATCHOAT="-Xpatchoat:${FALSE_BIN}"
- USE_DEX2OAT_AND_PATCHOAT="n"
+ USE_PATCHOAT="n"
shift
elif [ "x$1" = "x--relocate" ]; then
RELOCATE="y"
@@ -813,7 +808,7 @@ RUN_TEST_ASAN_OPTIONS="${RUN_TEST_ASAN_OPTIONS}detect_leaks=0"
if [ "$EXTERNAL_LOG_TAGS" = "n" ]; then
if [ "$DEV_MODE" = "y" ]; then
export ANDROID_LOG_TAGS='*:d'
- elif [ "$USE_DEX2OAT_AND_PATCHOAT" = "n" ]; then
+ elif [ "$USE_PATCHOAT" = "n" ]; then
# All tests would log the error of failing dex2oat/patchoat. Be silent here and only
# log fatal events.
export ANDROID_LOG_TAGS='*:s'
diff --git a/test/knownfailures.json b/test/knownfailures.json
index f71e9363cb..0a179c7093 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -76,7 +76,7 @@
},
{
"tests" : "629-vdex-speed",
- "variant": "interp-ac | no-dex2oat | interpreter | jit | relocate-npatchoat",
+ "variant": "interp-ac | interpreter | jit | relocate-npatchoat",
"description": "629 requires compilation."
},
{
@@ -163,7 +163,7 @@
},
{
"tests": "147-stripped-dex-fallback",
- "variant": "no-dex2oat | no-image | relocate-npatchoat",
+ "variant": "no-image | relocate-npatchoat",
"description": ["147-stripped-dex-fallback is disabled because it",
"requires --prebuild."]
},
@@ -174,7 +174,7 @@
"119-noimage-patchoat",
"137-cfi",
"138-duplicate-classes-check2"],
- "variant": "no-dex2oat | no-image | relocate-npatchoat",
+ "variant": "no-image | relocate-npatchoat",
"description": ["All these tests check that we have sane behavior if we",
"don't have a patchoat or dex2oat. Therefore we",
"shouldn't run them in situations where we actually",
@@ -649,9 +649,9 @@
},
{
"tests": "660-clinit",
- "variant": "no-image | no-dex2oat | no-prebuild | jvmti-stress | redefine-stress",
+ "variant": "no-image | no-prebuild | jvmti-stress | redefine-stress",
"description": ["Tests <clinit> for app images, which --no-image, --no-prebuild, ",
- "--no-dex2oat, and --redefine-stress do not create"]
+ "and --redefine-stress do not create"]
},
{
"tests": ["961-default-iface-resolution-gen",
@@ -674,7 +674,7 @@
},
{
"tests": "661-oat-writer-layout",
- "variant": "interp-ac | interpreter | jit | no-dex2oat | no-prebuild | no-image | trace | redefine-stress | jvmti-stress",
+ "variant": "interp-ac | interpreter | jit | no-prebuild | no-image | trace | redefine-stress | jvmti-stress",
"description": ["Test is designed to only check --compiler-filter=speed"]
},
{
@@ -1010,7 +1010,7 @@
},
{
"tests": "677-fsi",
- "variant": "no-dex2oat | no-image | no-prebuild | relocate-npatchoat | jvm",
+ "variant": "no-image | no-prebuild | relocate-npatchoat | jvm",
"description": ["Test requires a successful dex2oat invocation"]
},
{
diff --git a/test/run-test b/test/run-test
index d90eccdf75..ef173026c1 100755
--- a/test/run-test
+++ b/test/run-test
@@ -148,7 +148,6 @@ jvmti_redefine_stress="false"
strace="false"
always_clean="no"
never_clean="no"
-have_dex2oat="yes"
have_patchoat="yes"
have_image="yes"
multi_image_suffix=""
@@ -195,9 +194,6 @@ while true; do
lib="libdvm.so"
runtime="dalvik"
shift
- elif [ "x$1" = "x--no-dex2oat" ]; then
- have_dex2oat="no"
- shift
elif [ "x$1" = "x--no-patchoat" ]; then
have_patchoat="no"
shift
@@ -580,10 +576,6 @@ if [ "$have_patchoat" = "no" ]; then
run_args="${run_args} --no-patchoat"
fi
-if [ "$have_dex2oat" = "no" ]; then
- run_args="${run_args} --no-dex2oat"
-fi
-
if [ ! "$runtime" = "jvm" ]; then
run_args="${run_args} --lib $lib"
fi
@@ -639,11 +631,6 @@ if [ "$bisection_search" = "yes" -a "$prebuild_mode" = "yes" ]; then
usage="yes"
fi
-if [ "$bisection_search" = "yes" -a "$have_dex2oat" = "no" ]; then
- err_echo "--bisection-search and --no-dex2oat are mutually exclusive"
- usage="yes"
-fi
-
if [ "$bisection_search" = "yes" -a "$have_patchoat" = "no" ]; then
err_echo "--bisection-search and --no-patchoat are mutually exclusive"
usage="yes"
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index 47b1e4e261..84490bf0e4 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -184,7 +184,7 @@ target_config = {
'run-test' : ['--relocate-npatchoat']
},
'art-no-dex2oat' : {
- 'run-test' : ['--no-dex2oat']
+ # Deprecated configuration.
},
'art-heap-poisoning' : {
'run-test' : ['--interpreter',
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index e8d4290d28..10c8619307 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -145,7 +145,7 @@ def gather_test_info():
VARIANT_TYPE_DICT['image'] = {'picimage', 'no-image', 'multipicimage'}
VARIANT_TYPE_DICT['debuggable'] = {'ndebuggable', 'debuggable'}
VARIANT_TYPE_DICT['gc'] = {'gcstress', 'gcverify', 'cms'}
- VARIANT_TYPE_DICT['prebuild'] = {'no-prebuild', 'no-dex2oat', 'prebuild'}
+ VARIANT_TYPE_DICT['prebuild'] = {'no-prebuild', 'prebuild'}
VARIANT_TYPE_DICT['cdex_level'] = {'cdex-none', 'cdex-fast'}
VARIANT_TYPE_DICT['relocate'] = {'relocate-npatchoat', 'relocate', 'no-relocate'}
VARIANT_TYPE_DICT['jni'] = {'jni', 'forcecopy', 'checkjni'}
@@ -414,8 +414,6 @@ def run_tests(tests):
options_test += ' --prebuild'
elif prebuild == 'no-prebuild':
options_test += ' --no-prebuild'
- elif prebuild == 'no-dex2oat':
- options_test += ' --no-prebuild --no-dex2oat'
if cdex_level:
# Add option and remove the cdex- prefix.
diff --git a/tools/art_verifier/art_verifier.cc b/tools/art_verifier/art_verifier.cc
index bb43e67bb6..8f412bf7a6 100644
--- a/tools/art_verifier/art_verifier.cc
+++ b/tools/art_verifier/art_verifier.cc
@@ -111,6 +111,9 @@ struct MethodVerifierArgs : public CmdlineArgs {
} else if (option.starts_with("--repetitions=")) {
char* end;
repetitions_ = strtoul(option.substr(strlen("--repetitions=")).data(), &end, 10);
+ } else if (option.starts_with("--api-level=")) {
+ char* end;
+ api_level_ = strtoul(option.substr(strlen("--api-level=")).data(), &end, 10);
} else {
return kParseUnknownArgument;
}
@@ -146,6 +149,7 @@ struct MethodVerifierArgs : public CmdlineArgs {
" --verbose: use verbose verifier mode.\n"
" --verbose-debug: use verbose verifier debug mode.\n"
" --repetitions=<count>: repeat the verification count times.\n"
+ " --api-level=<level>: use API level for verification.\n"
"\n";
usage += Base::GetUsage();
@@ -162,6 +166,8 @@ struct MethodVerifierArgs : public CmdlineArgs {
bool method_verifier_verbose_debug_ = false;
size_t repetitions_ = 0u;
+
+ uint32_t api_level_ = 0u;
};
struct MethodVerifierMain : public CmdlineMain<MethodVerifierArgs> {
@@ -241,6 +247,7 @@ struct MethodVerifierMain : public CmdlineMain<MethodVerifierArgs> {
runtime->GetCompilerCallbacks(),
true,
verifier::HardFailLogMode::kLogWarning,
+ args_->api_level_,
&error_msg);
if (args_->repetitions_ == 0) {
LOG(INFO) << descriptor << ": " << res << " " << error_msg;
diff --git a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
index 694a5f4a3f..92620760e7 100644
--- a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
+++ b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
@@ -86,6 +86,12 @@ public class Class2Greylist {
.withDescription("Enable debug")
.create("d"));
options.addOption(OptionBuilder
+ .withLongOpt("dump-all-members")
+ .withDescription("Dump all members from jar files to stdout. Ignore annotations. " +
+ "Do not use in conjunction with any other arguments.")
+ .hasArgs(0)
+ .create('m'));
+ options.addOption(OptionBuilder
.withLongOpt("help")
.hasArgs(0)
.withDescription("Show this help")
@@ -113,16 +119,21 @@ public class Class2Greylist {
}
Status status = new Status(cmd.hasOption('d'));
- try {
- Class2Greylist c2gl = new Class2Greylist(
- status,
- cmd.getOptionValue('p', null),
- cmd.getOptionValues('g'),
- cmd.getOptionValue('w', null),
- jarFiles);
- c2gl.main();
- } catch (IOException e) {
- status.error(e);
+
+ if (cmd.hasOption('m')) {
+ dumpAllMembers(status, jarFiles);
+ } else {
+ try {
+ Class2Greylist c2gl = new Class2Greylist(
+ status,
+ cmd.getOptionValue('p', null),
+ cmd.getOptionValues('g'),
+ cmd.getOptionValue('w', null),
+ jarFiles);
+ c2gl.main();
+ } catch (IOException e) {
+ status.error(e);
+ }
}
if (status.ok()) {
@@ -221,6 +232,20 @@ public class Class2Greylist {
return map;
}
+ private static void dumpAllMembers(Status status, String[] jarFiles) {
+ for (String jarFile : jarFiles) {
+ status.debug("Processing jar file %s", jarFile);
+ try {
+ JarReader reader = new JarReader(status, jarFile);
+ reader.stream().forEach(clazz -> new MemberDumpingVisitor(clazz, status)
+ .visit());
+ reader.close();
+ } catch (IOException e) {
+ status.error(e);
+ }
+ }
+ }
+
private static void help(Options options) {
new HelpFormatter().printHelp(
"class2greylist path/to/classes.jar [classes2.jar ...]",
diff --git a/tools/class2greylist/src/com/android/class2greylist/MemberDumpingVisitor.java b/tools/class2greylist/src/com/android/class2greylist/MemberDumpingVisitor.java
new file mode 100644
index 0000000000..6677a3f3ab
--- /dev/null
+++ b/tools/class2greylist/src/com/android/class2greylist/MemberDumpingVisitor.java
@@ -0,0 +1,47 @@
+package com.android.class2greylist;
+
+import org.apache.bcel.classfile.DescendingVisitor;
+import org.apache.bcel.classfile.EmptyVisitor;
+import org.apache.bcel.classfile.Field;
+import org.apache.bcel.classfile.FieldOrMethod;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.classfile.Method;
+
+/**
+ * A class file visitor that simply prints to stdout the signature of every member within the class.
+ */
+public class MemberDumpingVisitor extends EmptyVisitor {
+
+ private final Status mStatus;
+ private final DescendingVisitor mDescendingVisitor;
+
+ /**
+ * Creates a visitor for a class.
+ *
+ * @param clazz Class to visit
+ */
+ public MemberDumpingVisitor(JavaClass clazz, Status status) {
+ mStatus = status;
+ mDescendingVisitor = new DescendingVisitor(clazz, this);
+ }
+
+ public void visit() {
+ mDescendingVisitor.visit();
+ }
+
+ @Override
+ public void visitMethod(Method method) {
+ visitMember(method, "L%s;->%s%s");
+ }
+
+ @Override
+ public void visitField(Field field) {
+ visitMember(field, "L%s;->%s:%s");
+ }
+
+ private void visitMember(FieldOrMethod member, String signatureFormatString) {
+ AnnotationContext context = new AnnotationContext(mStatus, member,
+ (JavaClass) mDescendingVisitor.predecessor(), signatureFormatString);
+ System.out.println(context.getMemberDescriptor());
+ }
+}
diff --git a/tools/field-null-percent/Android.bp b/tools/field-null-percent/Android.bp
new file mode 100644
index 0000000000..26bb1dc437
--- /dev/null
+++ b/tools/field-null-percent/Android.bp
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2018 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.
+//
+
+// Build variants {target,host} x {debug,ndebug} x {32,64}
+cc_defaults {
+ name: "fieldnull-defaults",
+ host_supported: true,
+ srcs: ["fieldnull.cc"],
+ defaults: ["art_defaults"],
+
+ // Note that this tool needs to be built for both 32-bit and 64-bit since it requires
+ // to be same ISA as what it is attached to.
+ compile_multilib: "both",
+
+ shared_libs: [
+ "libbase",
+ ],
+ header_libs: [
+ "libopenjdkjvmti_headers",
+ ],
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+ symlink_preferred_arch: true,
+}
+
+art_cc_library {
+ name: "libfieldnull",
+ defaults: ["fieldnull-defaults"],
+}
+
+art_cc_library {
+ name: "libfieldnulld",
+ defaults: [
+ "art_debug_defaults",
+ "fieldnull-defaults",
+ ],
+}
diff --git a/tools/field-null-percent/README.md b/tools/field-null-percent/README.md
new file mode 100644
index 0000000000..d8bc65d9cc
--- /dev/null
+++ b/tools/field-null-percent/README.md
@@ -0,0 +1,51 @@
+# fieldnull
+
+fieldnull is a JVMTI agent designed for testing for a given field the number of
+instances with that field set to null. This can be useful for determining what
+fields should be moved into side structures in cases where memory use is
+important.
+
+# Usage
+### Build
+> `make libfieldnull`
+
+The libraries will be built for 32-bit, 64-bit, host and target. Below examples
+assume you want to use the 64-bit version.
+
+### Command Line
+
+The agent is loaded using -agentpath like normal. It takes arguments in the
+following format:
+> `Lname/of/class;.nameOfField:Ltype/of/field;[,...]`
+
+#### ART
+> `art -Xplugin:$ANDROID_HOST_OUT/lib64/libopenjdkjvmti.so '-agentpath:libfieldnull.so=Lname/of/class;.nameOfField:Ltype/of/field;' -cp tmp/java/helloworld.dex -Xint helloworld`
+
+* `-Xplugin` and `-agentpath` need to be used, otherwise the agent will fail during init.
+* If using `libartd.so`, make sure to use the debug version of jvmti.
+
+> `adb shell setenforce 0`
+>
+> `adb push $ANDROID_PRODUCT_OUT/system/lib64/libfieldnull.so /data/local/tmp/`
+>
+> `adb shell am start-activity --attach-agent '/data/local/tmp/libfieldnull.so=Ljava/lang/Class;.name:Ljava/lang/String;' some.debuggable.apps/.the.app.MainActivity`
+
+#### RI
+> `java '-agentpath:libfieldnull.so=Lname/of/class;.nameOfField:Ltype/of/field;' -cp tmp/helloworld/classes helloworld`
+
+### Printing the Results
+All statistics gathered during the trace are printed automatically when the
+program normally exits. In the case of Android applications, they are always
+killed, so we need to manually print the results.
+
+> `kill -SIGQUIT $(pid com.littleinc.orm_benchmark)`
+
+Will initiate a dump of the counts (to logcat).
+
+The dump will look something like this.
+
+> `dalvikvm32 I 08-30 14:51:20 84818 84818 fieldnull.cc:96] Dumping counts of null fields.`
+>
+> `dalvikvm32 I 08-30 14:51:20 84818 84818 fieldnull.cc:97] Field name null count total count`
+>
+> `dalvikvm32 I 08-30 14:51:20 84818 84818 fieldnull.cc:135] Ljava/lang/Class;.name:Ljava/lang/String; 5 2936`
diff --git a/tools/field-null-percent/fieldnull.cc b/tools/field-null-percent/fieldnull.cc
new file mode 100644
index 0000000000..86459d238b
--- /dev/null
+++ b/tools/field-null-percent/fieldnull.cc
@@ -0,0 +1,218 @@
+// Copyright (C) 2018 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 <android-base/logging.h>
+
+#include <atomic>
+#include <iomanip>
+#include <iostream>
+#include <istream>
+#include <jni.h>
+#include <jvmti.h>
+#include <memory>
+#include <sstream>
+#include <string.h>
+#include <string>
+#include <vector>
+
+namespace fieldnull {
+
+#define CHECK_JVMTI(x) CHECK_EQ((x), JVMTI_ERROR_NONE)
+
+// Special art ti-version number. We will use this as a fallback if we cannot get a regular JVMTI
+// env.
+static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000;
+
+static JavaVM* java_vm = nullptr;
+
+// Field is "Lclass/name/here;.field_name:Lfield/type/here;"
+static std::pair<jclass, jfieldID> SplitField(JNIEnv* env, const std::string& field_id) {
+ CHECK_EQ(field_id[0], 'L');
+ env->PushLocalFrame(1);
+ std::istringstream is(field_id);
+ std::string class_name;
+ std::string field_name;
+ std::string field_type;
+
+ std::getline(is, class_name, '.');
+ std::getline(is, field_name, ':');
+ std::getline(is, field_type, '\0');
+
+ jclass klass = reinterpret_cast<jclass>(
+ env->NewGlobalRef(env->FindClass(class_name.substr(1, class_name.size() - 2).c_str())));
+ jfieldID field = env->GetFieldID(klass, field_name.c_str(), field_type.c_str());
+ CHECK(klass != nullptr);
+ CHECK(field != nullptr);
+ LOG(INFO) << "listing field " << field_id;
+ env->PopLocalFrame(nullptr);
+ return std::make_pair(klass, field);
+}
+
+static std::vector<std::pair<jclass, jfieldID>> GetRequestedFields(JNIEnv* env,
+ const std::string& args) {
+ std::vector<std::pair<jclass, jfieldID>> res;
+ std::stringstream args_stream(args);
+ std::string item;
+ while (std::getline(args_stream, item, ',')) {
+ if (item == "") {
+ continue;
+ }
+ res.push_back(SplitField(env, item));
+ }
+ return res;
+}
+
+static jint SetupJvmtiEnv(JavaVM* vm, jvmtiEnv** jvmti) {
+ jint res = 0;
+ res = vm->GetEnv(reinterpret_cast<void**>(jvmti), JVMTI_VERSION_1_1);
+
+ if (res != JNI_OK || *jvmti == nullptr) {
+ LOG(ERROR) << "Unable to access JVMTI, error code " << res;
+ return vm->GetEnv(reinterpret_cast<void**>(jvmti), kArtTiVersion);
+ }
+ return res;
+}
+
+struct RequestList {
+ std::vector<std::pair<jclass, jfieldID>> fields_;
+};
+
+static void DataDumpRequestCb(jvmtiEnv* jvmti) {
+ JNIEnv* env = nullptr;
+ CHECK_EQ(java_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6), JNI_OK);
+ LOG(INFO) << "Dumping counts of null fields.";
+ LOG(INFO) << "\t" << "Field name"
+ << "\t" << "null count"
+ << "\t" << "total count";
+ RequestList* list;
+ CHECK_JVMTI(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&list)));
+ for (std::pair<jclass, jfieldID>& p : list->fields_) {
+ jclass klass = p.first;
+ jfieldID field = p.second;
+ // Make sure all instances of the class are tagged with the klass ptr value. Since this is a
+ // global ref it's guaranteed to be unique.
+ CHECK_JVMTI(jvmti->IterateOverInstancesOfClass(
+ p.first,
+ // We need to do this to all objects every time since we might be looking for multiple
+ // fields in classes that are subtypes of each other.
+ JVMTI_HEAP_OBJECT_EITHER,
+ /* class_tag, size, tag_ptr, user_data*/
+ [](jlong, jlong, jlong* tag_ptr, void* klass) -> jvmtiIterationControl {
+ *tag_ptr = static_cast<jlong>(reinterpret_cast<intptr_t>(klass));
+ return JVMTI_ITERATION_CONTINUE;
+ },
+ klass));
+ jobject* obj_list;
+ jint obj_len;
+ jlong tag = static_cast<jlong>(reinterpret_cast<intptr_t>(klass));
+ CHECK_JVMTI(jvmti->GetObjectsWithTags(1, &tag, &obj_len, &obj_list, nullptr));
+
+ uint64_t null_cnt = 0;
+ for (jint i = 0; i < obj_len; i++) {
+ if (env->GetObjectField(obj_list[i], field) == nullptr) {
+ null_cnt++;
+ }
+ }
+
+ char* field_name;
+ char* field_sig;
+ char* class_name;
+ CHECK_JVMTI(jvmti->GetFieldName(klass, field, &field_name, &field_sig, nullptr));
+ CHECK_JVMTI(jvmti->GetClassSignature(klass, &class_name, nullptr));
+ LOG(INFO) << "\t" << class_name << "." << field_name << ":" << field_sig
+ << "\t" << null_cnt
+ << "\t" << obj_len;
+ CHECK_JVMTI(jvmti->Deallocate(reinterpret_cast<unsigned char*>(field_name)));
+ CHECK_JVMTI(jvmti->Deallocate(reinterpret_cast<unsigned char*>(field_sig)));
+ CHECK_JVMTI(jvmti->Deallocate(reinterpret_cast<unsigned char*>(class_name)));
+ }
+}
+
+static void VMDeathCb(jvmtiEnv* jvmti, JNIEnv* env ATTRIBUTE_UNUSED) {
+ DataDumpRequestCb(jvmti);
+ RequestList* list = nullptr;
+ CHECK_JVMTI(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&list)));
+ delete list;
+}
+
+static void CreateFieldList(jvmtiEnv* jvmti, JNIEnv* env, std::string args) {
+ RequestList* list = nullptr;
+ CHECK_JVMTI(jvmti->Allocate(sizeof(*list), reinterpret_cast<unsigned char**>(&list)));
+ new (list) RequestList { .fields_ = GetRequestedFields(env, args), };
+ CHECK_JVMTI(jvmti->SetEnvironmentLocalStorage(list));
+}
+
+static void VMInitCb(jvmtiEnv* jvmti, JNIEnv* env, jobject thr ATTRIBUTE_UNUSED) {
+ char* args = nullptr;
+ CHECK_JVMTI(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&args)));
+ CHECK_JVMTI(jvmti->SetEnvironmentLocalStorage(nullptr));
+ CreateFieldList(jvmti, env, args);
+ CHECK_JVMTI(jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, nullptr));
+ CHECK_JVMTI(jvmti->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_DATA_DUMP_REQUEST,
+ nullptr));
+ CHECK_JVMTI(jvmti->Deallocate(reinterpret_cast<unsigned char*>(args)));
+}
+
+static jint AgentStart(JavaVM* vm, char* options, bool is_onload) {
+ android::base::InitLogging(/* argv */nullptr);
+ java_vm = vm;
+ jvmtiEnv* jvmti = nullptr;
+ if (SetupJvmtiEnv(vm, &jvmti) != JNI_OK) {
+ LOG(ERROR) << "Could not get JVMTI env or ArtTiEnv!";
+ return JNI_ERR;
+ }
+ jvmtiCapabilities caps { .can_tag_objects = 1, };
+ CHECK_JVMTI(jvmti->AddCapabilities(&caps));
+ jvmtiEventCallbacks cb {
+ .VMInit = VMInitCb,
+ .DataDumpRequest = DataDumpRequestCb,
+ .VMDeath = VMDeathCb,
+ };
+ CHECK_JVMTI(jvmti->SetEventCallbacks(&cb, sizeof(cb)));
+ if (is_onload) {
+ unsigned char* ptr = nullptr;
+ CHECK_JVMTI(jvmti->Allocate(strlen(options) + 1, &ptr));
+ strcpy(reinterpret_cast<char*>(ptr), options);
+ CHECK_JVMTI(jvmti->SetEnvironmentLocalStorage(ptr));
+ CHECK_JVMTI(jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, nullptr));
+ } else {
+ JNIEnv* env = nullptr;
+ CHECK_EQ(vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6), JNI_OK);
+ CreateFieldList(jvmti, env, options);
+ CHECK_JVMTI(jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, nullptr));
+ CHECK_JVMTI(jvmti->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_DATA_DUMP_REQUEST,
+ nullptr));
+ }
+ return JNI_OK;
+}
+
+// Late attachment (e.g. 'am attach-agent').
+extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm,
+ char* options,
+ void* reserved ATTRIBUTE_UNUSED) {
+ return AgentStart(vm, options, /*is_onload*/false);
+}
+
+// Early attachment
+extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm,
+ char* options,
+ void* reserved ATTRIBUTE_UNUSED) {
+ return AgentStart(jvm, options, /*is_onload*/true);
+}
+
+} // namespace fieldnull
+
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index 942a4e0fc6..23533af02b 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -10,14 +10,26 @@
modes: [device],
names: ["jsr166.CompletableFutureTest#testCompleteOnTimeout_completed",
"jsr166.CompletableFutureTest#testDelayedExecutor",
+ "jsr166.ExecutorsTest#testTimedCallable",
+ "jsr166.RecursiveActionTest#testJoinIgnoresInterruptsOutsideForkJoinPool",
"libcore.libcore.icu.TransliteratorTest#testAll",
"libcore.libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045",
"libcore.libcore.icu.RelativeDateTimeFormatterTest#test_bug25883157",
"libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndTimeout",
+ "libcore.java.text.DecimalFormatTest#testWhitespaceError",
+ "libcore.java.text.DecimalFormatTest#testWhitespaceTolerated",
+ "libcore.java.text.DecimalFormatTest#test_exponentSeparator",
+ "libcore.java.text.DecimalFormatTest#test_setMaximumFractionDigitsAffectsRoundingMode",
+ "libcore.java.util.jar.OldJarFileTest#test_ConstructorLjava_io_File",
+ "libcore.java.util.jar.OldJarFileTest#test_ConstructorLjava_lang_StringZ",
+ "libcore.java.util.jar.OldJarInputStreamTest#test_read$ZII",
"libcore.java.util.TimeZoneTest#testSetDefaultDeadlock",
"libcore.javax.crypto.CipherBasicsTest#testBasicEncryption",
+ "org.apache.harmony.tests.java.lang.ref.ReferenceQueueTest#test_removeJ",
"org.apache.harmony.tests.java.text.MessageFormatTest#test_parseLjava_lang_String",
- "org.apache.harmony.tests.java.util.TimerTest#testThrowingTaskKillsTimerThread"]
+ "org.apache.harmony.tests.java.util.ControlTest#test_toBundleName_LStringLLocale",
+ "org.apache.harmony.tests.java.util.TimerTest#testThrowingTaskKillsTimerThread"
+ ]
},
{
description: "Sometimes times out with gcstress and debug.",