Merge "Save profile information in a separate thread." am: 58b2329de7
am: da6e49016d
* commit 'da6e49016dc37704f45b13217a8c6f8e726ab8e1':
Save profile information in a separate thread.
diff --git a/Android.mk b/Android.mk
index 9cf2a30..34022ae 100644
--- a/Android.mk
+++ b/Android.mk
@@ -98,16 +98,26 @@
ART_HOST_DEPENDENCIES := \
$(ART_HOST_EXECUTABLES) \
$(HOST_OUT_JAVA_LIBRARIES)/core-libart-hostdex.jar \
- $(ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION)
+ $(HOST_OUT_JAVA_LIBRARIES)/core-oj-hostdex.jar \
+ $(ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \
+ $(ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdk$(ART_HOST_SHLIB_EXTENSION) \
+ $(ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkjvm$(ART_HOST_SHLIB_EXTENSION)
ART_TARGET_DEPENDENCIES := \
$(ART_TARGET_EXECUTABLES) \
$(TARGET_OUT_JAVA_LIBRARIES)/core-libart.jar \
- $(TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so
+ $(TARGET_OUT_JAVA_LIBRARIES)/core-oj.jar \
+ $(TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so \
+ $(TARGET_OUT_SHARED_LIBRARIES)/libopenjdk.so \
+ $(TARGET_OUT_SHARED_LIBRARIES)/libopenjdkjvm.so
ifdef TARGET_2ND_ARCH
ART_TARGET_DEPENDENCIES += $(2ND_TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so
+ART_TARGET_DEPENDENCIES += $(2ND_TARGET_OUT_SHARED_LIBRARIES)/libopenjdk.so
+ART_TARGET_DEPENDENCIES += $(2ND_TARGET_OUT_SHARED_LIBRARIES)/libopenjdkjvm.so
endif
ifdef HOST_2ND_ARCH
ART_HOST_DEPENDENCIES += $(2ND_HOST_OUT_SHARED_LIBRARIES)/libjavacore.so
+ART_HOST_DEPENDENCIES += $(2ND_HOST_OUT_SHARED_LIBRARIES)/libopenjdk.so
+ART_HOST_DEPENDENCIES += $(2ND_HOST_OUT_SHARED_LIBRARIES)/libopenjdkjvm.so
endif
########################################################################
diff --git a/NOTICE b/NOTICE
index d27f6a6..d79b004 100644
--- a/NOTICE
+++ b/NOTICE
@@ -262,5 +262,3 @@
pyyaml tests llvm/test/YAMLParser/{*.data, LICENSE.TXT}
ARM contributions llvm/lib/Target/ARM/LICENSE.TXT
md5 contributions llvm/lib/Support/MD5.cpp llvm/include/llvm/Support/MD5.h
-
--------------------------------------------------------------------
diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk
index c53479c..7fd2a5a 100644
--- a/build/Android.common_path.mk
+++ b/build/Android.common_path.mk
@@ -80,7 +80,7 @@
TARGET_CORE_IMG_LOCATION := $(ART_TARGET_TEST_OUT)/core.art
# Jar files for core.art.
-TARGET_CORE_JARS := core-libart conscrypt okhttp bouncycastle
+TARGET_CORE_JARS := core-oj core-libart conscrypt okhttp bouncycastle
HOST_CORE_JARS := $(addsuffix -hostdex,$(TARGET_CORE_JARS))
HOST_CORE_DEX_LOCATIONS := $(foreach jar,$(HOST_CORE_JARS), $(HOST_OUT_JAVA_LIBRARIES)/$(jar).jar)
@@ -91,9 +91,9 @@
ifeq ($(ANDROID_COMPILE_WITH_JACK),true)
# Classpath for Jack compilation: we only need core-libart.
-HOST_JACK_CLASSPATH_DEPENDENCIES := $(call intermediates-dir-for,JAVA_LIBRARIES,core-libart-hostdex,t,COMMON)/classes.jack
-HOST_JACK_CLASSPATH := $(foreach dep,$(HOST_JACK_CLASSPATH_DEPENDENCIES),$(abspath $(dep)))
-TARGET_JACK_CLASSPATH_DEPENDENCIES := $(call intermediates-dir-for,JAVA_LIBRARIES,core-libart, ,COMMON)/classes.jack
-TARGET_JACK_CLASSPATH := $(foreach dep,$(TARGET_JACK_CLASSPATH_DEPENDENCIES),$(abspath $(dep)))
+HOST_JACK_CLASSPATH_DEPENDENCIES := $(call intermediates-dir-for,JAVA_LIBRARIES,core-oj-hostdex,t,COMMON)/classes.jack $(call intermediates-dir-for,JAVA_LIBRARIES,core-libart-hostdex,t,COMMON)/classes.jack
+HOST_JACK_CLASSPATH := $(abspath $(call intermediates-dir-for,JAVA_LIBRARIES,core-oj-hostdex,t,COMMON)/classes.jack):$(abspath $(call intermediates-dir-for,JAVA_LIBRARIES,core-libart-hostdex,t,COMMON)/classes.jack)
+TARGET_JACK_CLASSPATH_DEPENDENCIES := $(call intermediates-dir-for,JAVA_LIBRARIES,core-oj, ,COMMON)/classes.jack $(call intermediates-dir-for,JAVA_LIBRARIES,core-libart, ,COMMON)/classes.jack
+TARGET_JACK_CLASSPATH := $(abspath $(call intermediates-dir-for,JAVA_LIBRARIES,core-oj, ,COMMON)/classes.jack):$(abspath $(call intermediates-dir-for,JAVA_LIBRARIES,core-libart, ,COMMON)/classes.jack)
endif
endif # ART_ANDROID_COMMON_PATH_MK
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index edf107e..c9af1c6 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -114,6 +114,9 @@
# Do you want run-tests with the --debuggable flag
ART_TEST_RUN_TEST_DEBUGGABLE ?= $(ART_TEST_FULL)
+# Do you want to test multi-part boot-image functionality?
+ART_TEST_RUN_TEST_MULTI_IMAGE ?= $(ART_TEST_FULL)
+
# Define the command run on test failure. $(1) is the name of the test. Executed by the shell.
define ART_TEST_FAILED
( [ -f $(ART_HOST_TEST_DIR)/skipped/$(1) ] || \
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 1c23929..99f7a2a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -441,7 +441,9 @@
$(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_TEST_TARGET_GTEST_$(file)_DEX)) \
$$(ART_TARGET_NATIVETEST_OUT)/$$(TARGET_$(2)ARCH)/$(1) \
$$($(2)TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so \
- $$(TARGET_OUT_JAVA_LIBRARIES)/core-libart.jar
+ $$($(2)TARGET_OUT_SHARED_LIBRARIES)/libopenjdk.so \
+ $$(TARGET_OUT_JAVA_LIBRARIES)/core-libart.jar \
+ $$(TARGET_OUT_JAVA_LIBRARIES)/core-oj.jar
.PHONY: $$(gtest_rule)
$$(gtest_rule): test-art-target-sync
@@ -483,6 +485,7 @@
# Dependencies for all host gtests.
gtest_deps := $$(HOST_CORE_DEX_LOCATIONS) \
$$($(2)ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$$(ART_HOST_SHLIB_EXTENSION) \
+ $$($(2)ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdk$$(ART_HOST_SHLIB_EXTENSION) \
$$(gtest_exe) \
$$(ART_GTEST_$(1)_HOST_DEPS) \
$(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_TEST_HOST_GTEST_$(file)_DEX))
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 50600ef..884f698 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -42,6 +42,7 @@
# $(3): 2ND_ or undefined, 2ND_ for 32-bit host builds.
# $(4): wrapper, e.g., valgrind.
# $(5): dex2oat suffix, e.g, valgrind requires 32 right now.
+# $(6): multi-image.
# NB depending on HOST_CORE_DEX_LOCATIONS so we are sure to have the dex files in frameworks for
# run-test --no-image
define create-core-oat-host-rules
@@ -92,14 +93,25 @@
$$(error found $(2) expected pic or no-pic)
endif
- core_image_name := $($(3)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(4)$(CORE_IMG_SUFFIX)
- core_oat_name := $($(3)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(4)$(CORE_OAT_SUFFIX)
+ # If $(6) is true, generate a multi-image.
+ ifeq ($(6),true)
+ core_multi_infix := -multi
+ core_multi_param := --multi-image --no-inline-from=core-oj-hostdex.jar
+ core_multi_group := _multi
+ else
+ core_multi_infix :=
+ core_multi_param :=
+ core_multi_group :=
+ endif
+
+ core_image_name := $($(3)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$$(core_multi_infix)$(4)$(CORE_IMG_SUFFIX)
+ core_oat_name := $($(3)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$$(core_multi_infix)$(4)$(CORE_OAT_SUFFIX)
# Using the bitness suffix makes it easier to add as a dependency for the run-test mk.
ifeq ($(3),)
- $(4)HOST_CORE_IMAGE_$(1)_$(2)_64 := $$(core_image_name)
+ $(4)HOST_CORE_IMAGE_$(1)_$(2)$$(core_multi_group)_64 := $$(core_image_name)
else
- $(4)HOST_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
+ $(4)HOST_CORE_IMAGE_$(1)_$(2)$$(core_multi_group)_32 := $$(core_image_name)
endif
$(4)HOST_CORE_IMG_OUTS += $$(core_image_name)
$(4)HOST_CORE_OAT_OUTS += $$(core_oat_name)
@@ -111,6 +123,7 @@
$$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
$$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
$$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
+$$(core_image_name): PRIVATE_CORE_MULTI_PARAM := $$(core_multi_param)
$$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency)
@echo "host dex2oat: $$@"
@mkdir -p $$(dir $$@)
@@ -122,7 +135,7 @@
--base=$$(LIBART_IMG_HOST_BASE_ADDRESS) --instruction-set=$$($(3)ART_HOST_ARCH) \
$$(LOCAL_$(3)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES_OPTION) \
--host --android-root=$$(HOST_OUT) --include-patch-information --generate-debug-info \
- $$(PRIVATE_CORE_COMPILE_OPTIONS)
+ $$(PRIVATE_CORE_MULTI_PARAM) $$(PRIVATE_CORE_COMPILE_OPTIONS)
$$(core_oat_name): $$(core_image_name)
@@ -138,32 +151,40 @@
# $(1): compiler - default, optimizing, jit, interpreter or interpreter-access-checks.
# $(2): wrapper.
# $(3): dex2oat suffix.
+# $(4): multi-image.
define create-core-oat-host-rule-combination
- $(call create-core-oat-host-rules,$(1),no-pic,,$(2),$(3))
- $(call create-core-oat-host-rules,$(1),pic,,$(2),$(3))
+ $(call create-core-oat-host-rules,$(1),no-pic,,$(2),$(3),$(4))
+ $(call create-core-oat-host-rules,$(1),pic,,$(2),$(3),$(4))
ifneq ($(HOST_PREFER_32_BIT),true)
- $(call create-core-oat-host-rules,$(1),no-pic,2ND_,$(2),$(3))
- $(call create-core-oat-host-rules,$(1),pic,2ND_,$(2),$(3))
+ $(call create-core-oat-host-rules,$(1),no-pic,2ND_,$(2),$(3),$(4))
+ $(call create-core-oat-host-rules,$(1),pic,2ND_,$(2),$(3),$(4))
endif
endef
-$(eval $(call create-core-oat-host-rule-combination,default,,))
-$(eval $(call create-core-oat-host-rule-combination,optimizing,,))
-$(eval $(call create-core-oat-host-rule-combination,interpreter,,))
-$(eval $(call create-core-oat-host-rule-combination,interp-ac,,))
-$(eval $(call create-core-oat-host-rule-combination,jit,,))
+$(eval $(call create-core-oat-host-rule-combination,default,,,false))
+$(eval $(call create-core-oat-host-rule-combination,optimizing,,,false))
+$(eval $(call create-core-oat-host-rule-combination,interpreter,,,false))
+$(eval $(call create-core-oat-host-rule-combination,interp-ac,,,false))
+$(eval $(call create-core-oat-host-rule-combination,jit,,,false))
+$(eval $(call create-core-oat-host-rule-combination,default,,,true))
+$(eval $(call create-core-oat-host-rule-combination,optimizing,,,true))
+$(eval $(call create-core-oat-host-rule-combination,interpreter,,,true))
+$(eval $(call create-core-oat-host-rule-combination,interp-ac,,,true))
+$(eval $(call create-core-oat-host-rule-combination,jit,,,true))
valgrindHOST_CORE_IMG_OUTS :=
valgrindHOST_CORE_OAT_OUTS :=
-$(eval $(call create-core-oat-host-rule-combination,default,valgrind,32))
-$(eval $(call create-core-oat-host-rule-combination,optimizing,valgrind,32))
-$(eval $(call create-core-oat-host-rule-combination,interpreter,valgrind,32))
-$(eval $(call create-core-oat-host-rule-combination,interp-ac,valgrind,32))
-$(eval $(call create-core-oat-host-rule-combination,jit,valgrind,32))
+$(eval $(call create-core-oat-host-rule-combination,default,valgrind,32,false))
+$(eval $(call create-core-oat-host-rule-combination,optimizing,valgrind,32,false))
+$(eval $(call create-core-oat-host-rule-combination,interpreter,valgrind,32,false))
+$(eval $(call create-core-oat-host-rule-combination,interp-ac,valgrind,32,false))
+$(eval $(call create-core-oat-host-rule-combination,jit,valgrind,32,false))
valgrind-test-art-host-dex2oat-host: $(valgrindHOST_CORE_IMG_OUTS)
+test-art-host-dex2oat-host: $(HOST_CORE_IMG_OUTS)
+
define create-core-oat-target-rules
core_compile_options :=
core_image_name :=
diff --git a/cmdline/cmdline.h b/cmdline/cmdline.h
index 4aced5b..4dcaf80 100644
--- a/cmdline/cmdline.h
+++ b/cmdline/cmdline.h
@@ -80,8 +80,7 @@
}
}
-static Runtime* StartRuntime(const char* boot_image_location,
- InstructionSet instruction_set) {
+static Runtime* StartRuntime(const char* boot_image_location, InstructionSet instruction_set) {
CHECK(boot_image_location != nullptr);
RuntimeOptions options;
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index c7c1907..278c490 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -208,7 +208,8 @@
false,
timer_.get(),
-1,
- ""));
+ /* profile_file */ "",
+ /* dex_to_oat_map */ nullptr));
// We typically don't generate an image in unit tests, disable this optimization by default.
compiler_driver_->SetSupportBootImageFixup(false);
}
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index f48947d..32d7518 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -22,6 +22,7 @@
#include "base/macros.h"
#include "base/mutex-inl.h"
#include "dex/compiler_ir.h"
+#include "driver/compiler_driver.h"
#include "thread-inl.h"
#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir.h"
@@ -777,6 +778,17 @@
bool DexFileMethodInliner::GenInline(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
uint32_t method_idx) {
+ // Check that we're allowed to inline.
+ {
+ CompilationUnit* cu = mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit();
+ if (!cu->compiler_driver->MayInline(dex_file_, cu->dex_file)) {
+ VLOG(compiler) << "Won't inline " << method_idx << " in "
+ << cu->dex_file->GetLocation() << " from "
+ << dex_file_->GetLocation();
+ return false;
+ }
+ }
+
InlineMethod method;
{
ReaderMutexLock mu(Thread::Current(), lock_);
diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc
index 24daf2f..bcf20c7 100644
--- a/compiler/dex/quick/quick_cfi_test.cc
+++ b/compiler/dex/quick/quick_cfi_test.cc
@@ -58,6 +58,7 @@
CompilerOptions::kDefaultNumDexMethodsThreshold,
CompilerOptions::kDefaultInlineDepthLimit,
CompilerOptions::kDefaultInlineMaxCodeUnits,
+ nullptr,
false,
CompilerOptions::kDefaultTopKProfileThreshold,
false,
@@ -74,9 +75,25 @@
std::unique_ptr<const InstructionSetFeatures> isa_features;
std::string error;
isa_features.reset(InstructionSetFeatures::FromVariant(isa, "default", &error));
- CompilerDriver driver(&compiler_options, &verification_results, &method_inliner_map,
- Compiler::kQuick, isa, isa_features.get(),
- false, nullptr, nullptr, nullptr, 0, false, false, "", false, 0, -1, "");
+ CompilerDriver driver(&compiler_options,
+ &verification_results,
+ &method_inliner_map,
+ Compiler::kQuick,
+ isa,
+ isa_features.get(),
+ false,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ false,
+ false,
+ "",
+ false,
+ 0,
+ -1,
+ "",
+ nullptr);
ClassLinker* linker = nullptr;
CompilationUnit cu(&pool, isa, &driver, linker);
DexFile::CodeItem code_item { 0, 0, 0, 0, 0, 0, { 0 } }; // NOLINT
diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
index e977ebf..9deabc0 100644
--- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc
+++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
@@ -41,6 +41,7 @@
CompilerOptions::kDefaultNumDexMethodsThreshold,
CompilerOptions::kDefaultInlineDepthLimit,
CompilerOptions::kDefaultInlineMaxCodeUnits,
+ nullptr,
false,
CompilerOptions::kDefaultTopKProfileThreshold,
false,
@@ -72,7 +73,8 @@
false,
0,
-1,
- ""));
+ "",
+ nullptr));
cu_.reset(new CompilationUnit(pool_.get(), isa_, compiler_driver_.get(), nullptr));
DexFile::CodeItem* code_item = static_cast<DexFile::CodeItem*>(
cu_->arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc));
diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc
index c6dbd24..84fb432 100644
--- a/compiler/driver/compiled_method_storage_test.cc
+++ b/compiler/driver/compiled_method_storage_test.cc
@@ -45,7 +45,8 @@
false,
nullptr,
-1,
- "");
+ "",
+ nullptr);
CompiledMethodStorage* storage = driver.GetCompiledMethodStorage();
ASSERT_TRUE(storage->DedupeEnabled()); // The default.
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 5630b08..afb4b71 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -334,19 +334,21 @@
DISALLOW_COPY_AND_ASSIGN(AOTCompilationStats);
};
-CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options,
- VerificationResults* verification_results,
- DexFileToMethodInlinerMap* method_inliner_map,
- Compiler::Kind compiler_kind,
- InstructionSet instruction_set,
- const InstructionSetFeatures* instruction_set_features,
- bool boot_image, std::unordered_set<std::string>* image_classes,
- std::unordered_set<std::string>* compiled_classes,
- std::unordered_set<std::string>* compiled_methods,
- size_t thread_count, bool dump_stats, bool dump_passes,
- const std::string& dump_cfg_file_name, bool dump_cfg_append,
- CumulativeLogger* timer, int swap_fd,
- const std::string& profile_file)
+CompilerDriver::CompilerDriver(
+ const CompilerOptions* compiler_options,
+ VerificationResults* verification_results,
+ DexFileToMethodInlinerMap* method_inliner_map,
+ Compiler::Kind compiler_kind,
+ InstructionSet instruction_set,
+ const InstructionSetFeatures* instruction_set_features,
+ bool boot_image, std::unordered_set<std::string>* image_classes,
+ std::unordered_set<std::string>* compiled_classes,
+ std::unordered_set<std::string>* compiled_methods,
+ size_t thread_count, bool dump_stats, bool dump_passes,
+ const std::string& dump_cfg_file_name, bool dump_cfg_append,
+ CumulativeLogger* timer, int swap_fd,
+ const std::string& profile_file,
+ const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map)
: compiler_options_(compiler_options),
verification_results_(verification_results),
method_inliner_map_(method_inliner_map),
@@ -374,6 +376,7 @@
compiler_context_(nullptr),
support_boot_image_fixup_(instruction_set != kMips && instruction_set != kMips64),
dex_files_for_oat_file_(nullptr),
+ dex_file_oat_filename_map_(dex_to_oat_map),
compiled_method_storage_(swap_fd) {
DCHECK(compiler_options_ != nullptr);
DCHECK(verification_results_ != nullptr);
@@ -1538,6 +1541,12 @@
use_dex_cache = true;
}
}
+ if (!use_dex_cache && IsBootImage()) {
+ if (!AreInSameOatFile(&(const_cast<mirror::Class*>(referrer_class)->GetDexFile()),
+ &declaring_class->GetDexFile())) {
+ use_dex_cache = true;
+ }
+ }
// The method is defined not within this dex file. We need a dex cache slot within the current
// dex file or direct pointers.
bool must_use_direct_pointers = false;
@@ -1571,12 +1580,14 @@
*type = sharp_type;
}
} else {
- auto* image_space = heap->GetBootImageSpace();
bool method_in_image = false;
- if (image_space != nullptr) {
+ const std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces();
+ for (gc::space::ImageSpace* image_space : image_spaces) {
const auto& method_section = image_space->GetImageHeader().GetMethodsSection();
- method_in_image = method_section.Contains(
- reinterpret_cast<uint8_t*>(method) - image_space->Begin());
+ if (method_section.Contains(reinterpret_cast<uint8_t*>(method) - image_space->Begin())) {
+ method_in_image = true;
+ break;
+ }
}
if (method_in_image || compiling_boot || runtime->UseJit()) {
// We know we must be able to get to the method in the image, so use that pointer.
@@ -2572,4 +2583,15 @@
return inliner->IsStringInitMethodIndex(method_index);
}
+bool CompilerDriver::MayInlineInternal(const DexFile* inlined_from,
+ const DexFile* inlined_into) const {
+ // We're not allowed to inline across dex files if we're the no-inline-from dex file.
+ if (inlined_from != inlined_into &&
+ compiler_options_->GetNoInlineFromDexFile() == inlined_from) {
+ return false;
+ }
+
+ return true;
+}
+
} // namespace art
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index f0360ce..fa0cb9a 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -97,7 +97,8 @@
size_t thread_count, bool dump_stats, bool dump_passes,
const std::string& dump_cfg_file_name, bool dump_cfg_append,
CumulativeLogger* timer, int swap_fd,
- const std::string& profile_file);
+ const std::string& profile_file,
+ const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map);
~CompilerDriver();
@@ -113,6 +114,18 @@
: ArrayRef<const DexFile* const>();
}
+ // Are the given dex files compiled into the same oat file? Should only be called after
+ // GetDexFilesForOatFile, as the conservative answer (when we don't have a map) is true.
+ bool AreInSameOatFile(const DexFile* d1, const DexFile* d2) {
+ if (dex_file_oat_filename_map_ == nullptr) {
+ // TODO: Check for this wrt/ apps and boot image calls.
+ return true;
+ }
+ auto it1 = dex_file_oat_filename_map_->find(d1);
+ auto it2 = dex_file_oat_filename_map_->find(d2);
+ return it1 == it2;
+ }
+
void CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings)
@@ -471,6 +484,13 @@
bool CanAssumeClassIsLoaded(mirror::Class* klass)
SHARED_REQUIRES(Locks::mutator_lock_);
+ bool MayInline(const DexFile* inlined_from, const DexFile* inlined_into) const {
+ if (!kIsTargetBuild) {
+ return MayInlineInternal(inlined_from, inlined_into);
+ }
+ return true;
+ }
+
private:
// Return whether the declaring class of `resolved_member` is
// available to `referrer_class` for read or write access using two
@@ -587,6 +607,8 @@
ThreadPool* thread_pool, TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
+ bool MayInlineInternal(const DexFile* inlined_from, const DexFile* inlined_into) const;
+
const CompilerOptions* const compiler_options_;
VerificationResults* const verification_results_;
DexFileToMethodInlinerMap* const method_inliner_map_;
@@ -621,9 +643,8 @@
const bool boot_image_;
- // If image_ is true, specifies the classes that will be included in
- // the image. Note if image_classes_ is null, all classes are
- // included in the image.
+ // If image_ is true, specifies the classes that will be included in the image.
+ // Note if image_classes_ is null, all classes are included in the image.
std::unique_ptr<std::unordered_set<std::string>> image_classes_;
// Specifies the classes that will be compiled. Note that if classes_to_compile_ is null,
@@ -663,6 +684,9 @@
// List of dex files that will be stored in the oat file.
const std::vector<const DexFile*>* dex_files_for_oat_file_;
+ // Map from dex files to the oat file (name) they will be compiled into.
+ const std::unordered_map<const DexFile*, const char*>* dex_file_oat_filename_map_;
+
CompiledMethodStorage compiled_method_storage_;
friend class CompileClassVisitor;
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index b6abc6e..82c0e86 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -142,7 +142,7 @@
// TODO: check that all Method::GetCode() values are non-null
}
-TEST_F(CompilerDriverTest, AbstractMethodErrorStub) {
+TEST_F(CompilerDriverTest, DISABLED_AbstractMethodErrorStub) {
TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING_WITH_QUICK();
TEST_DISABLED_FOR_READ_BARRIER_WITH_QUICK();
TEST_DISABLED_FOR_READ_BARRIER_WITH_OPTIMIZING_FOR_UNSUPPORTED_INSTRUCTION_SETS();
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 8c38cf2..209bb5a 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -31,6 +31,7 @@
num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold),
inline_depth_limit_(kUnsetInlineDepthLimit),
inline_max_code_units_(kUnsetInlineMaxCodeUnits),
+ no_inline_from_(nullptr),
include_patch_information_(kDefaultIncludePatchInformation),
top_k_profile_threshold_(kDefaultTopKProfileThreshold),
debuggable_(false),
@@ -59,6 +60,7 @@
size_t num_dex_methods_threshold,
size_t inline_depth_limit,
size_t inline_max_code_units,
+ const DexFile* no_inline_from,
bool include_patch_information,
double top_k_profile_threshold,
bool debuggable,
@@ -79,6 +81,7 @@
num_dex_methods_threshold_(num_dex_methods_threshold),
inline_depth_limit_(inline_depth_limit),
inline_max_code_units_(inline_max_code_units),
+ no_inline_from_(no_inline_from),
include_patch_information_(include_patch_information),
top_k_profile_threshold_(top_k_profile_threshold),
debuggable_(debuggable),
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 2b047a2..9ad1bee 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -72,6 +72,7 @@
size_t num_dex_methods_threshold,
size_t inline_depth_limit,
size_t inline_max_code_units,
+ const DexFile* no_inline_from,
bool include_patch_information,
double top_k_profile_threshold,
bool debuggable,
@@ -217,6 +218,10 @@
return abort_on_hard_verifier_failure_;
}
+ const DexFile* GetNoInlineFromDexFile() const {
+ return no_inline_from_;
+ }
+
bool ParseCompilerOption(const StringPiece& option, UsageFn Usage);
private:
@@ -241,6 +246,10 @@
size_t num_dex_methods_threshold_;
size_t inline_depth_limit_;
size_t inline_max_code_units_;
+
+ // A dex file from which we should not inline code.
+ const DexFile* no_inline_from_;
+
bool include_patch_information_;
// When using a profile file only the top K% of the profiled samples will be compiled.
double top_k_profile_threshold_;
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 15812dc..6859605 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -72,11 +72,18 @@
ScratchFile oat_file(OS::CreateEmptyFile(oat_filename.c_str()));
const uintptr_t requested_image_base = ART_BASE_ADDRESS;
+ std::unordered_map<const DexFile*, const char*> dex_file_to_oat_filename_map;
+ std::vector<const char*> oat_filename_vector(1, oat_filename.c_str());
+ for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
+ dex_file_to_oat_filename_map.emplace(dex_file, oat_filename.c_str());
+ }
std::unique_ptr<ImageWriter> writer(new ImageWriter(*compiler_driver_,
requested_image_base,
/*compile_pic*/false,
/*compile_app_image*/false,
- storage_mode));
+ storage_mode,
+ oat_filename_vector,
+ dex_file_to_oat_filename_map));
// TODO: compile_pic should be a test argument.
{
{
@@ -131,12 +138,12 @@
ASSERT_TRUE(dup_oat.get() != nullptr);
{
- bool success_image = writer->Write(kInvalidImageFd,
- image_file.GetFilename(),
- dup_oat->GetPath(),
- dup_oat->GetPath());
+ std::vector<const char*> dup_oat_filename(1, dup_oat->GetPath().c_str());
+ std::vector<const char*> dup_image_filename(1, image_file.GetFilename().c_str());
+ bool success_image = writer->Write(kInvalidImageFd, dup_image_filename, dup_oat_filename);
ASSERT_TRUE(success_image);
- bool success_fixup = ElfWriter::Fixup(dup_oat.get(), writer->GetOatDataBegin());
+ bool success_fixup = ElfWriter::Fixup(dup_oat.get(),
+ writer->GetOatDataBegin(dup_oat_filename[0]));
ASSERT_TRUE(success_fixup);
ASSERT_EQ(dup_oat->FlushCloseOrErase(), 0) << "Could not flush and close oat file "
@@ -181,7 +188,7 @@
java_lang_dex_file_ = nullptr;
MemMap::Init();
- std::unique_ptr<const DexFile> dex(LoadExpectSingleDexFile(GetLibCoreDexFileName().c_str()));
+ std::unique_ptr<const DexFile> dex(LoadExpectSingleDexFile(GetLibCoreDexFileNames()[0].c_str()));
RuntimeOptions options;
std::string image("-Ximage:");
@@ -203,10 +210,11 @@
class_linker_ = runtime_->GetClassLinker();
gc::Heap* heap = Runtime::Current()->GetHeap();
- ASSERT_TRUE(heap->HasImageSpace());
+ ASSERT_TRUE(heap->HasBootImageSpace());
ASSERT_TRUE(heap->GetNonMovingSpace()->IsMallocSpace());
- gc::space::ImageSpace* image_space = heap->GetBootImageSpace();
+ // We loaded the runtime with an explicit image, so it must exist.
+ gc::space::ImageSpace* image_space = heap->GetBootImageSpaces()[0];
ASSERT_TRUE(image_space != nullptr);
if (storage_mode == ImageHeader::kStorageModeUncompressed) {
// Uncompressed, image should be smaller than file.
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 9545c83..17d0f61 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -154,147 +154,174 @@
}
bool ImageWriter::Write(int image_fd,
- const std::string& image_filename,
- const std::string& oat_filename,
- const std::string& oat_location) {
- CHECK(!image_filename.empty());
+ const std::vector<const char*>& image_filenames,
+ const std::vector<const char*>& oat_filenames) {
+ CHECK(!image_filenames.empty());
+ CHECK(!oat_filenames.empty());
+ CHECK_EQ(image_filenames.size(), oat_filenames.size());
- std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str()));
- if (oat_file.get() == nullptr) {
- PLOG(ERROR) << "Failed to open oat file " << oat_filename << " for " << oat_location;
- return false;
- }
- std::string error_msg;
- oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_location, nullptr, &error_msg);
- if (oat_file_ == nullptr) {
- PLOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location
- << ": " << error_msg;
- oat_file->Erase();
- return false;
- }
- Runtime::Current()->GetOatFileManager().RegisterOatFile(
+ size_t oat_file_offset = 0;
+
+ for (size_t i = 0; i < oat_filenames.size(); ++i) {
+ const char* oat_filename = oat_filenames[i];
+ std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename));
+ if (oat_file.get() == nullptr) {
+ PLOG(ERROR) << "Failed to open oat file " << oat_filename;
+ return false;
+ }
+ std::string error_msg;
+ oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_filename, nullptr, &error_msg);
+ if (oat_file_ == nullptr) {
+ PLOG(ERROR) << "Failed to open writable oat file " << oat_filename;
+ oat_file->Erase();
+ return false;
+ }
+ Runtime::Current()->GetOatFileManager().RegisterOatFile(
std::unique_ptr<const OatFile>(oat_file_));
- const OatHeader& oat_header = oat_file_->GetOatHeader();
- oat_address_offsets_[kOatAddressInterpreterToInterpreterBridge] =
- oat_header.GetInterpreterToInterpreterBridgeOffset();
- oat_address_offsets_[kOatAddressInterpreterToCompiledCodeBridge] =
- oat_header.GetInterpreterToCompiledCodeBridgeOffset();
- oat_address_offsets_[kOatAddressJNIDlsymLookup] =
- oat_header.GetJniDlsymLookupOffset();
- oat_address_offsets_[kOatAddressQuickGenericJNITrampoline] =
- oat_header.GetQuickGenericJniTrampolineOffset();
- oat_address_offsets_[kOatAddressQuickIMTConflictTrampoline] =
- oat_header.GetQuickImtConflictTrampolineOffset();
- oat_address_offsets_[kOatAddressQuickResolutionTrampoline] =
- oat_header.GetQuickResolutionTrampolineOffset();
- oat_address_offsets_[kOatAddressQuickToInterpreterBridge] =
- oat_header.GetQuickToInterpreterBridgeOffset();
+ const OatHeader& oat_header = oat_file_->GetOatHeader();
+ ImageInfo& image_info = GetImageInfo(oat_filename);
- size_t oat_loaded_size = 0;
- size_t oat_data_offset = 0;
- ElfWriter::GetOatElfInformation(oat_file.get(), &oat_loaded_size, &oat_data_offset);
+ size_t oat_loaded_size = 0;
+ size_t oat_data_offset = 0;
+ ElfWriter::GetOatElfInformation(oat_file.get(), &oat_loaded_size, &oat_data_offset);
+
+ DCHECK_EQ(image_info.oat_offset_, oat_file_offset);
+ oat_file_offset += oat_loaded_size;
+
+ if (i == 0) {
+ // Primary oat file, read the trampolines.
+ image_info.oat_address_offsets_[kOatAddressInterpreterToInterpreterBridge] =
+ oat_header.GetInterpreterToInterpreterBridgeOffset();
+ image_info.oat_address_offsets_[kOatAddressInterpreterToCompiledCodeBridge] =
+ oat_header.GetInterpreterToCompiledCodeBridgeOffset();
+ image_info.oat_address_offsets_[kOatAddressJNIDlsymLookup] =
+ oat_header.GetJniDlsymLookupOffset();
+ image_info.oat_address_offsets_[kOatAddressQuickGenericJNITrampoline] =
+ oat_header.GetQuickGenericJniTrampolineOffset();
+ image_info.oat_address_offsets_[kOatAddressQuickIMTConflictTrampoline] =
+ oat_header.GetQuickImtConflictTrampolineOffset();
+ image_info.oat_address_offsets_[kOatAddressQuickResolutionTrampoline] =
+ oat_header.GetQuickResolutionTrampolineOffset();
+ image_info.oat_address_offsets_[kOatAddressQuickToInterpreterBridge] =
+ oat_header.GetQuickToInterpreterBridgeOffset();
+ } else {
+ // Other oat files use the primary trampolines.
+ // TODO: Dummy values to protect usage? b/26317072
+ }
+
+
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ CreateHeader(oat_loaded_size, oat_data_offset);
+ CopyAndFixupNativeData();
+ }
+
+ SetOatChecksumFromElfFile(oat_file.get());
+
+ if (oat_file->FlushCloseOrErase() != 0) {
+ LOG(ERROR) << "Failed to flush and close oat file " << oat_filename;
+ return false;
+ }
+ }
{
- ScopedObjectAccess soa(Thread::Current());
- CreateHeader(oat_loaded_size, oat_data_offset);
- CopyAndFixupNativeData();
// TODO: heap validation can't handle these fix up passes.
+ ScopedObjectAccess soa(Thread::Current());
Runtime::Current()->GetHeap()->DisableObjectValidation();
CopyAndFixupObjects();
}
- SetOatChecksumFromElfFile(oat_file.get());
-
- if (oat_file->FlushCloseOrErase() != 0) {
- LOG(ERROR) << "Failed to flush and close oat file " << oat_filename << " for " << oat_location;
- return false;
- }
- std::unique_ptr<File> image_file;
- if (image_fd != kInvalidImageFd) {
- image_file.reset(new File(image_fd, image_filename, unix_file::kCheckSafeUsage));
- } else {
- image_file.reset(OS::CreateEmptyFile(image_filename.c_str()));
- }
- if (image_file == nullptr) {
- LOG(ERROR) << "Failed to open image file " << image_filename;
- return false;
- }
- if (fchmod(image_file->Fd(), 0644) != 0) {
- PLOG(ERROR) << "Failed to make image file world readable: " << image_filename;
- image_file->Erase();
- return EXIT_FAILURE;
- }
-
- std::unique_ptr<char[]> compressed_data;
- // Image data size excludes the bitmap and the header.
- ImageHeader* const image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
- const size_t image_data_size = image_header->GetImageSize() - sizeof(ImageHeader);
- char* image_data = reinterpret_cast<char*>(image_->Begin()) + sizeof(ImageHeader);
- size_t data_size;
- const char* image_data_to_write;
-
- CHECK_EQ(image_header->storage_mode_, image_storage_mode_);
- switch (image_storage_mode_) {
- case ImageHeader::kStorageModeLZ4: {
- size_t compressed_max_size = LZ4_compressBound(image_data_size);
- compressed_data.reset(new char[compressed_max_size]);
- data_size = LZ4_compress(
- reinterpret_cast<char*>(image_->Begin()) + sizeof(ImageHeader),
- &compressed_data[0],
- image_data_size);
- image_data_to_write = &compressed_data[0];
- VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size;
- break;
+ for (size_t i = 0; i < image_filenames.size(); ++i) {
+ const char* image_filename = image_filenames[i];
+ const char* oat_filename = oat_filenames[i];
+ ImageInfo& image_info = GetImageInfo(oat_filename);
+ std::unique_ptr<File> image_file;
+ if (image_fd != kInvalidImageFd) {
+ image_file.reset(new File(image_fd, image_filename, unix_file::kCheckSafeUsage));
+ } else {
+ image_file.reset(OS::CreateEmptyFile(image_filename));
}
- case ImageHeader::kStorageModeUncompressed: {
- data_size = image_data_size;
- image_data_to_write = image_data;
- break;
+ if (image_file == nullptr) {
+ LOG(ERROR) << "Failed to open image file " << image_filename;
+ return false;
}
- default: {
- LOG(FATAL) << "Unsupported";
- UNREACHABLE();
+ if (fchmod(image_file->Fd(), 0644) != 0) {
+ PLOG(ERROR) << "Failed to make image file world readable: " << image_filename;
+ image_file->Erase();
+ return EXIT_FAILURE;
}
- }
- // Write header first, as uncompressed.
- image_header->data_size_ = data_size;
- if (!image_file->WriteFully(image_->Begin(), sizeof(ImageHeader))) {
- PLOG(ERROR) << "Failed to write image file header " << image_filename;
- image_file->Erase();
- return false;
- }
+ std::unique_ptr<char[]> compressed_data;
+ // Image data size excludes the bitmap and the header.
+ ImageHeader* const image_header = reinterpret_cast<ImageHeader*>(image_info.image_->Begin());
+ const size_t image_data_size = image_header->GetImageSize() - sizeof(ImageHeader);
+ char* image_data = reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader);
+ size_t data_size;
+ const char* image_data_to_write;
- // Write out the image + fields + methods.
- const bool is_compressed = compressed_data != nullptr;
- if (!image_file->WriteFully(image_data_to_write, data_size)) {
- PLOG(ERROR) << "Failed to write image file data " << image_filename;
- image_file->Erase();
- return false;
- }
+ CHECK_EQ(image_header->storage_mode_, image_storage_mode_);
+ switch (image_storage_mode_) {
+ case ImageHeader::kStorageModeLZ4: {
+ size_t compressed_max_size = LZ4_compressBound(image_data_size);
+ compressed_data.reset(new char[compressed_max_size]);
+ data_size = LZ4_compress(
+ reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader),
+ &compressed_data[0],
+ image_data_size);
+ image_data_to_write = &compressed_data[0];
+ VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size;
+ break;
+ }
+ case ImageHeader::kStorageModeUncompressed: {
+ data_size = image_data_size;
+ image_data_to_write = image_data;
+ break;
+ }
+ default: {
+ LOG(FATAL) << "Unsupported";
+ UNREACHABLE();
+ }
+ }
- // Write out the image bitmap at the page aligned start of the image end, also uncompressed for
- // convenience.
- const ImageSection& bitmap_section = image_header->GetImageSection(
- ImageHeader::kSectionImageBitmap);
- // Align up since data size may be unaligned if the image is compressed.
- size_t bitmap_position_in_file = RoundUp(sizeof(ImageHeader) + data_size, kPageSize);
- if (!is_compressed) {
- CHECK_EQ(bitmap_position_in_file, bitmap_section.Offset());
- }
- if (!image_file->Write(reinterpret_cast<char*>(image_bitmap_->Begin()),
- bitmap_section.Size(),
- bitmap_position_in_file)) {
- PLOG(ERROR) << "Failed to write image file " << image_filename;
- image_file->Erase();
- return false;
- }
- CHECK_EQ(bitmap_position_in_file + bitmap_section.Size(),
- static_cast<size_t>(image_file->GetLength()));
- if (image_file->FlushCloseOrErase() != 0) {
- PLOG(ERROR) << "Failed to flush and close image file " << image_filename;
- return false;
+ // Write header first, as uncompressed.
+ image_header->data_size_ = data_size;
+ if (!image_file->WriteFully(image_info.image_->Begin(), sizeof(ImageHeader))) {
+ PLOG(ERROR) << "Failed to write image file header " << image_filename;
+ image_file->Erase();
+ return false;
+ }
+
+ // Write out the image + fields + methods.
+ const bool is_compressed = compressed_data != nullptr;
+ if (!image_file->WriteFully(image_data_to_write, data_size)) {
+ PLOG(ERROR) << "Failed to write image file data " << image_filename;
+ image_file->Erase();
+ return false;
+ }
+
+ // Write out the image bitmap at the page aligned start of the image end, also uncompressed for
+ // convenience.
+ const ImageSection& bitmap_section = image_header->GetImageSection(
+ ImageHeader::kSectionImageBitmap);
+ // Align up since data size may be unaligned if the image is compressed.
+ size_t bitmap_position_in_file = RoundUp(sizeof(ImageHeader) + data_size, kPageSize);
+ if (!is_compressed) {
+ CHECK_EQ(bitmap_position_in_file, bitmap_section.Offset());
+ }
+ if (!image_file->Write(reinterpret_cast<char*>(image_info.image_bitmap_->Begin()),
+ bitmap_section.Size(),
+ bitmap_position_in_file)) {
+ PLOG(ERROR) << "Failed to write image file " << image_filename;
+ image_file->Erase();
+ return false;
+ }
+ CHECK_EQ(bitmap_position_in_file + bitmap_section.Size(),
+ static_cast<size_t>(image_file->GetLength()));
+ if (image_file->FlushCloseOrErase() != 0) {
+ PLOG(ERROR) << "Failed to flush and close image file " << image_filename;
+ return false;
+ }
}
return true;
}
@@ -319,12 +346,14 @@
DCHECK(object != nullptr);
DCHECK_NE(image_objects_offset_begin_, 0u);
- size_t bin_slot_offset = bin_slot_offsets_[bin_slot.GetBin()];
+ const char* oat_filename = GetOatFilename(object);
+ ImageInfo& image_info = GetImageInfo(oat_filename);
+ size_t bin_slot_offset = image_info.bin_slot_offsets_[bin_slot.GetBin()];
size_t new_offset = bin_slot_offset + bin_slot.GetIndex();
DCHECK_ALIGNED(new_offset, kObjectAlignment);
SetImageOffset(object, new_offset);
- DCHECK_LT(new_offset, image_end_);
+ DCHECK_LT(new_offset, image_info.image_end_);
}
bool ImageWriter::IsImageOffsetAssigned(mirror::Object* object) const {
@@ -338,7 +367,9 @@
DCHECK(IsImageOffsetAssigned(object));
LockWord lock_word = object->GetLockWord(false);
size_t offset = lock_word.ForwardingAddress();
- DCHECK_LT(offset, image_end_);
+ const char* oat_filename = GetOatFilename(object);
+ const ImageInfo& image_info = GetConstImageInfo(oat_filename);
+ DCHECK_LT(offset, image_info.image_end_);
return offset;
}
@@ -377,15 +408,16 @@
void ImageWriter::PrepareDexCacheArraySlots() {
// Prepare dex cache array starts based on the ordering specified in the CompilerDriver.
- uint32_t size = 0u;
- for (const DexFile* dex_file : compiler_driver_.GetDexFilesForOatFile()) {
- dex_cache_array_starts_.Put(dex_file, size);
- DexCacheArraysLayout layout(target_ptr_size_, dex_file);
- size += layout.Size();
- }
// Set the slot size early to avoid DCHECK() failures in IsImageBinSlotAssigned()
// when AssignImageBinSlot() assigns their indexes out or order.
- bin_slot_sizes_[kBinDexCacheArray] = size;
+ for (const DexFile* dex_file : compiler_driver_.GetDexFilesForOatFile()) {
+ auto it = dex_file_oat_filename_map_.find(dex_file);
+ DCHECK(it != dex_file_oat_filename_map_.end()) << dex_file->GetLocation();
+ ImageInfo& image_info = GetImageInfo(it->second);
+ image_info.dex_cache_array_starts_.Put(dex_file, image_info.bin_slot_sizes_[kBinDexCacheArray]);
+ DexCacheArraysLayout layout(target_ptr_size_, dex_file);
+ image_info.bin_slot_sizes_[kBinDexCacheArray] += layout.Size();
+ }
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Thread* const self = Thread::Current();
@@ -399,24 +431,32 @@
const DexFile* dex_file = dex_cache->GetDexFile();
DexCacheArraysLayout layout(target_ptr_size_, dex_file);
DCHECK(layout.Valid());
- uint32_t start = dex_cache_array_starts_.Get(dex_file);
+ const char* oat_filename = GetOatFilenameForDexCache(dex_cache);
+ ImageInfo& image_info = GetImageInfo(oat_filename);
+ uint32_t start = image_info.dex_cache_array_starts_.Get(dex_file);
DCHECK_EQ(dex_file->NumTypeIds() != 0u, dex_cache->GetResolvedTypes() != nullptr);
- AddDexCacheArrayRelocation(dex_cache->GetResolvedTypes(), start + layout.TypesOffset());
+ AddDexCacheArrayRelocation(dex_cache->GetResolvedTypes(),
+ start + layout.TypesOffset(),
+ dex_cache);
DCHECK_EQ(dex_file->NumMethodIds() != 0u, dex_cache->GetResolvedMethods() != nullptr);
- AddDexCacheArrayRelocation(dex_cache->GetResolvedMethods(), start + layout.MethodsOffset());
+ AddDexCacheArrayRelocation(dex_cache->GetResolvedMethods(),
+ start + layout.MethodsOffset(),
+ dex_cache);
DCHECK_EQ(dex_file->NumFieldIds() != 0u, dex_cache->GetResolvedFields() != nullptr);
- AddDexCacheArrayRelocation(dex_cache->GetResolvedFields(), start + layout.FieldsOffset());
+ AddDexCacheArrayRelocation(dex_cache->GetResolvedFields(),
+ start + layout.FieldsOffset(),
+ dex_cache);
DCHECK_EQ(dex_file->NumStringIds() != 0u, dex_cache->GetStrings() != nullptr);
- AddDexCacheArrayRelocation(dex_cache->GetStrings(), start + layout.StringsOffset());
+ AddDexCacheArrayRelocation(dex_cache->GetStrings(), start + layout.StringsOffset(), dex_cache);
}
}
-void ImageWriter::AddDexCacheArrayRelocation(void* array, size_t offset) {
+void ImageWriter::AddDexCacheArrayRelocation(void* array, size_t offset, DexCache* dex_cache) {
if (array != nullptr) {
DCHECK(!IsInBootImage(array));
- native_object_relocations_.emplace(
- array,
- NativeObjectRelocation { offset, kNativeObjectRelocationTypeDexCacheArray });
+ const char* oat_filename = GetOatFilenameForDexCache(dex_cache);
+ native_object_relocations_.emplace(array,
+ NativeObjectRelocation { oat_filename, offset, kNativeObjectRelocationTypeDexCacheArray });
}
}
@@ -531,18 +571,21 @@
} // else bin = kBinRegular
}
+ const char* oat_filename = GetOatFilename(object);
+ ImageInfo& image_info = GetImageInfo(oat_filename);
+
size_t offset_delta = RoundUp(object_size, kObjectAlignment); // 64-bit alignment
- current_offset = bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned).
- // Move the current bin size up to accomodate the object we just assigned a bin slot.
- bin_slot_sizes_[bin] += offset_delta;
+ current_offset = image_info.bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned).
+ // Move the current bin size up to accommodate the object we just assigned a bin slot.
+ image_info.bin_slot_sizes_[bin] += offset_delta;
BinSlot new_bin_slot(bin, current_offset);
SetImageBinSlot(object, new_bin_slot);
- ++bin_slot_count_[bin];
+ ++image_info.bin_slot_count_[bin];
// Grow the image closer to the end by the object we just assigned.
- image_end_ += offset_delta;
+ image_info.image_end_ += offset_delta;
}
bool ImageWriter::WillMethodBeDirty(ArtMethod* m) const {
@@ -565,7 +608,9 @@
LockWord lock_word = object->GetLockWord(false);
size_t offset = lock_word.ForwardingAddress();
BinSlot bin_slot(offset);
- DCHECK_LT(bin_slot.GetIndex(), bin_slot_sizes_[bin_slot.GetBin()])
+ const char* oat_filename = GetOatFilename(object);
+ const ImageInfo& image_info = GetConstImageInfo(oat_filename);
+ DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()])
<< "bin slot offset should not exceed the size of that bin";
}
return true;
@@ -580,39 +625,42 @@
DCHECK_LE(offset, std::numeric_limits<uint32_t>::max());
BinSlot bin_slot(static_cast<uint32_t>(offset));
- DCHECK_LT(bin_slot.GetIndex(), bin_slot_sizes_[bin_slot.GetBin()]);
+ const char* oat_filename = GetOatFilename(object);
+ const ImageInfo& image_info = GetConstImageInfo(oat_filename);
+ DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()]);
return bin_slot;
}
bool ImageWriter::AllocMemory() {
- const size_t length = RoundUp(image_objects_offset_begin_ +
- GetBinSizeSum() +
- intern_table_bytes_ +
- class_table_bytes_,
- kPageSize);
- std::string error_msg;
- image_.reset(MemMap::MapAnonymous("image writer image",
- nullptr,
- length,
- PROT_READ | PROT_WRITE,
- false,
- false,
- &error_msg));
- if (UNLIKELY(image_.get() == nullptr)) {
- LOG(ERROR) << "Failed to allocate memory for image file generation: " << error_msg;
- return false;
- }
+ for (const char* oat_filename : oat_filenames_) {
+ ImageInfo& image_info = GetImageInfo(oat_filename);
+ const size_t length = RoundUp(image_objects_offset_begin_ +
+ GetBinSizeSum(image_info) +
+ intern_table_bytes_ +
+ class_table_bytes_,
+ kPageSize);
+ std::string error_msg;
+ image_info.image_.reset(MemMap::MapAnonymous("image writer image",
+ nullptr,
+ length,
+ PROT_READ | PROT_WRITE,
+ false,
+ false,
+ &error_msg));
+ if (UNLIKELY(image_info.image_.get() == nullptr)) {
+ LOG(ERROR) << "Failed to allocate memory for image file generation: " << error_msg;
+ return false;
+ }
- // Create the image bitmap, only needs to cover mirror object section which is up to image_end_.
- CHECK_LE(image_end_, length);
- image_bitmap_.reset(gc::accounting::ContinuousSpaceBitmap::Create(
- "image bitmap",
- image_->Begin(),
- RoundUp(image_end_, kPageSize)));
- if (image_bitmap_.get() == nullptr) {
- LOG(ERROR) << "Failed to allocate memory for image bitmap";
- return false;
+ // Create the image bitmap, only needs to cover mirror object section which is up to image_end_.
+ CHECK_LE(image_info.image_end_, length);
+ image_info.image_bitmap_.reset(gc::accounting::ContinuousSpaceBitmap::Create(
+ "image bitmap", image_info.image_->Begin(), RoundUp(image_info.image_end_, kPageSize)));
+ if (image_info.image_bitmap_.get() == nullptr) {
+ LOG(ERROR) << "Failed to allocate memory for image bitmap";
+ return false;
+ }
}
return true;
}
@@ -885,7 +933,7 @@
AssignImageBinSlot(obj);
}
-ObjectArray<Object>* ImageWriter::CreateImageRoots() const {
+ObjectArray<Object>* ImageWriter::CreateImageRoots(const char* oat_filename) const {
Runtime* runtime = Runtime::Current();
ClassLinker* class_linker = runtime->GetClassLinker();
Thread* self = Thread::Current();
@@ -893,6 +941,15 @@
Handle<Class> object_array_class(hs.NewHandle(
class_linker->FindSystemClass(self, "[Ljava/lang/Object;")));
+ std::unordered_set<const DexFile*> image_dex_files;
+ for (auto& pair : dex_file_oat_filename_map_) {
+ const DexFile* image_dex_file = pair.first;
+ const char* image_oat_filename = pair.second;
+ if (strcmp(oat_filename, image_oat_filename) == 0) {
+ image_dex_files.insert(image_dex_file);
+ }
+ }
+
// build an Object[] of all the DexCaches used in the source_space_.
// Since we can't hold the dex lock when allocating the dex_caches
// ObjectArray, we lock the dex lock twice, first to get the number
@@ -905,7 +962,10 @@
for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
mirror::DexCache* dex_cache =
down_cast<mirror::DexCache*>(self->DecodeJObject(data.weak_root));
- dex_cache_count += IsInBootImage(dex_cache) ? 0u : 1u;
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ if (!IsInBootImage(dex_cache)) {
+ dex_cache_count += image_dex_files.find(dex_file) != image_dex_files.end() ? 1u : 0u;
+ }
}
}
Handle<ObjectArray<Object>> dex_caches(
@@ -918,7 +978,10 @@
for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
mirror::DexCache* dex_cache =
down_cast<mirror::DexCache*>(self->DecodeJObject(data.weak_root));
- non_image_dex_caches += IsInBootImage(dex_cache) ? 0u : 1u;
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ if (!IsInBootImage(dex_cache)) {
+ non_image_dex_caches += image_dex_files.find(dex_file) != image_dex_files.end() ? 1u : 0u;
+ }
}
CHECK_EQ(dex_cache_count, non_image_dex_caches)
<< "The number of non-image dex caches changed.";
@@ -926,7 +989,8 @@
for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
mirror::DexCache* dex_cache =
down_cast<mirror::DexCache*>(self->DecodeJObject(data.weak_root));
- if (!IsInBootImage(dex_cache)) {
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ if (!IsInBootImage(dex_cache) && image_dex_files.find(dex_file) != image_dex_files.end()) {
dex_caches->Set<false>(i, dex_cache);
++i;
}
@@ -997,9 +1061,12 @@
}
// Visit and assign offsets for fields and field arrays.
auto* as_klass = h_obj->AsClass();
+ mirror::DexCache* dex_cache = as_klass->GetDexCache();
LengthPrefixedArray<ArtField>* fields[] = {
as_klass->GetSFieldsPtr(), as_klass->GetIFieldsPtr(),
};
+ const char* oat_file = GetOatFilenameForDexCache(dex_cache);
+ ImageInfo& image_info = GetImageInfo(oat_file);
for (LengthPrefixedArray<ArtField>* cur_fields : fields) {
// Total array length including header.
if (cur_fields != nullptr) {
@@ -1008,11 +1075,10 @@
auto it = native_object_relocations_.find(cur_fields);
CHECK(it == native_object_relocations_.end()) << "Field array " << cur_fields
<< " already forwarded";
- size_t& offset = bin_slot_sizes_[kBinArtField];
+ size_t& offset = image_info.bin_slot_sizes_[kBinArtField];
DCHECK(!IsInBootImage(cur_fields));
- native_object_relocations_.emplace(
- cur_fields,
- NativeObjectRelocation {offset, kNativeObjectRelocationTypeArtFieldArray });
+ native_object_relocations_.emplace(cur_fields,
+ NativeObjectRelocation {oat_file, offset, kNativeObjectRelocationTypeArtFieldArray });
offset += header_size;
// Forward individual fields so that we can quickly find where they belong.
for (size_t i = 0, count = cur_fields->size(); i < count; ++i) {
@@ -1022,9 +1088,8 @@
CHECK(it2 == native_object_relocations_.end()) << "Field at index=" << i
<< " already assigned " << PrettyField(field) << " static=" << field->IsStatic();
DCHECK(!IsInBootImage(field));
- native_object_relocations_.emplace(
- field,
- NativeObjectRelocation {offset, kNativeObjectRelocationTypeArtField });
+ native_object_relocations_.emplace(field,
+ NativeObjectRelocation {oat_file, offset, kNativeObjectRelocationTypeArtField });
offset += sizeof(ArtField);
}
}
@@ -1053,17 +1118,17 @@
auto it = native_object_relocations_.find(array);
CHECK(it == native_object_relocations_.end())
<< "Method array " << array << " already forwarded";
- size_t& offset = bin_slot_sizes_[bin_type];
+ size_t& offset = image_info.bin_slot_sizes_[bin_type];
DCHECK(!IsInBootImage(array));
- native_object_relocations_.emplace(
- array, NativeObjectRelocation {
- offset,
- any_dirty ? kNativeObjectRelocationTypeArtMethodArrayDirty
- : kNativeObjectRelocationTypeArtMethodArrayClean
- });
+ native_object_relocations_.emplace(array,
+ NativeObjectRelocation {
+ oat_file,
+ offset,
+ any_dirty ? kNativeObjectRelocationTypeArtMethodArrayDirty
+ : kNativeObjectRelocationTypeArtMethodArrayClean });
offset += header_size;
for (auto& m : as_klass->GetMethods(target_ptr_size_)) {
- AssignMethodOffset(&m, type);
+ AssignMethodOffset(&m, type, oat_file);
}
(any_dirty ? dirty_methods_ : clean_methods_) += num_methods;
}
@@ -1089,13 +1154,16 @@
}
}
-void ImageWriter::AssignMethodOffset(ArtMethod* method, NativeObjectRelocationType type) {
+void ImageWriter::AssignMethodOffset(ArtMethod* method,
+ NativeObjectRelocationType type,
+ const char* oat_filename) {
DCHECK(!IsInBootImage(method));
auto it = native_object_relocations_.find(method);
CHECK(it == native_object_relocations_.end()) << "Method " << method << " already assigned "
<< PrettyMethod(method);
- size_t& offset = bin_slot_sizes_[BinTypeForNativeRelocationType(type)];
- native_object_relocations_.emplace(method, NativeObjectRelocation { offset, type });
+ ImageInfo& image_info = GetImageInfo(oat_filename);
+ size_t& offset = image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(type)];
+ native_object_relocations_.emplace(method, NativeObjectRelocation { oat_filename, offset, type });
offset += ArtMethod::Size(target_ptr_size_);
}
@@ -1128,18 +1196,20 @@
void ImageWriter::CalculateNewObjectOffsets() {
Thread* const self = Thread::Current();
- StackHandleScope<1> hs(self);
- Handle<ObjectArray<Object>> image_roots(hs.NewHandle(CreateImageRoots()));
+ StackHandleScopeCollection handles(self);
+ std::vector<Handle<ObjectArray<Object>>> image_roots;
+ for (const char* oat_filename : oat_filenames_) {
+ std::string image_filename = oat_filename;
+ image_roots.push_back(handles.NewHandle(CreateImageRoots(image_filename.c_str())));
+ }
auto* runtime = Runtime::Current();
auto* heap = runtime->GetHeap();
- DCHECK_EQ(0U, image_end_);
// Leave space for the header, but do not write it yet, we need to
// know where image_roots is going to end up
- image_end_ += RoundUp(sizeof(ImageHeader), kObjectAlignment); // 64-bit-alignment
+ image_objects_offset_begin_ = RoundUp(sizeof(ImageHeader), kObjectAlignment); // 64-bit-alignment
- image_objects_offset_begin_ = image_end_;
// Clear any pre-existing monitors which may have been in the monitor words, assign bin slots.
heap->VisitObjects(WalkFieldsCallback, this);
// Write the image runtime methods.
@@ -1156,10 +1226,12 @@
const auto image_method_type = kNativeObjectRelocationTypeArtMethodArrayClean;
auto it = native_object_relocations_.find(&image_method_array_);
CHECK(it == native_object_relocations_.end());
- size_t& offset = bin_slot_sizes_[BinTypeForNativeRelocationType(image_method_type)];
+ ImageInfo& default_image_info = GetImageInfo(default_oat_filename_);
+ size_t& offset =
+ default_image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(image_method_type)];
if (!compile_app_image_) {
native_object_relocations_.emplace(&image_method_array_,
- NativeObjectRelocation { offset, image_method_type });
+ NativeObjectRelocation { default_oat_filename_, offset, image_method_type });
}
size_t method_alignment = ArtMethod::Alignment(target_ptr_size_);
const size_t array_size = LengthPrefixedArray<ArtMethod>::ComputeSize(
@@ -1171,43 +1243,74 @@
CHECK(m->IsRuntimeMethod());
DCHECK_EQ(compile_app_image_, IsInBootImage(m)) << "Trampolines should be in boot image";
if (!IsInBootImage(m)) {
- AssignMethodOffset(m, kNativeObjectRelocationTypeArtMethodClean);
+ AssignMethodOffset(m, kNativeObjectRelocationTypeArtMethodClean, default_oat_filename_);
}
}
// Calculate size of the dex cache arrays slot and prepare offsets.
PrepareDexCacheArraySlots();
// Calculate bin slot offsets.
- size_t bin_offset = image_objects_offset_begin_;
- for (size_t i = 0; i != kBinSize; ++i) {
- bin_slot_offsets_[i] = bin_offset;
- bin_offset += bin_slot_sizes_[i];
- if (i == kBinArtField) {
- static_assert(kBinArtField + 1 == kBinArtMethodClean, "Methods follow fields.");
- static_assert(alignof(ArtField) == 4u, "ArtField alignment is 4.");
- DCHECK_ALIGNED(bin_offset, 4u);
- DCHECK(method_alignment == 4u || method_alignment == 8u);
- bin_offset = RoundUp(bin_offset, method_alignment);
+ for (const char* oat_filename : oat_filenames_) {
+ ImageInfo& image_info = GetImageInfo(oat_filename);
+ size_t bin_offset = image_objects_offset_begin_;
+ for (size_t i = 0; i != kBinSize; ++i) {
+ image_info.bin_slot_offsets_[i] = bin_offset;
+ bin_offset += image_info.bin_slot_sizes_[i];
+ if (i == kBinArtField) {
+ static_assert(kBinArtField + 1 == kBinArtMethodClean, "Methods follow fields.");
+ static_assert(alignof(ArtField) == 4u, "ArtField alignment is 4.");
+ DCHECK_ALIGNED(bin_offset, 4u);
+ DCHECK(method_alignment == 4u || method_alignment == 8u);
+ bin_offset = RoundUp(bin_offset, method_alignment);
+ }
}
+ // NOTE: There may be additional padding between the bin slots and the intern table.
+ DCHECK_EQ(image_info.image_end_,
+ GetBinSizeSum(image_info, kBinMirrorCount) + image_objects_offset_begin_);
}
- // NOTE: There may be additional padding between the bin slots and the intern table.
- DCHECK_EQ(image_end_, GetBinSizeSum(kBinMirrorCount) + image_objects_offset_begin_);
+ // Calculate image offsets.
+ size_t image_offset = 0;
+ for (const char* oat_filename : oat_filenames_) {
+ ImageInfo& image_info = GetImageInfo(oat_filename);
+ image_info.image_begin_ = global_image_begin_ + image_offset;
+ image_info.image_offset_ = image_offset;
+ size_t native_sections_size = image_info.bin_slot_sizes_[kBinArtField] +
+ image_info.bin_slot_sizes_[kBinArtMethodDirty] +
+ image_info.bin_slot_sizes_[kBinArtMethodClean] +
+ image_info.bin_slot_sizes_[kBinDexCacheArray] +
+ intern_table_bytes_ +
+ class_table_bytes_;
+ size_t image_objects = RoundUp(image_info.image_end_, kPageSize);
+ size_t bitmap_size =
+ RoundUp(gc::accounting::ContinuousSpaceBitmap::ComputeBitmapSize(image_objects), kPageSize);
+ size_t heap_size = gc::accounting::ContinuousSpaceBitmap::ComputeHeapSize(bitmap_size);
+ size_t max = std::max(heap_size, image_info.image_end_ + native_sections_size + bitmap_size);
+ image_info.image_size_ = RoundUp(max, kPageSize);
+ image_offset += image_info.image_size_;
+ }
// Transform each object's bin slot into an offset which will be used to do the final copy.
heap->VisitObjects(UnbinObjectsIntoOffsetCallback, this);
- DCHECK_EQ(image_end_, GetBinSizeSum(kBinMirrorCount) + image_objects_offset_begin_);
+ // DCHECK_EQ(image_end_, GetBinSizeSum(kBinMirrorCount) + image_objects_offset_begin_);
- image_roots_address_ = PointerToLowMemUInt32(GetImageAddress(image_roots.Get()));
+ size_t i = 0;
+ for (const char* oat_filename : oat_filenames_) {
+ ImageInfo& image_info = GetImageInfo(oat_filename);
+ image_info.image_roots_address_ = PointerToLowMemUInt32(GetImageAddress(image_roots[i].Get()));
+ i++;
+ }
// Update the native relocations by adding their bin sums.
for (auto& pair : native_object_relocations_) {
NativeObjectRelocation& relocation = pair.second;
Bin bin_type = BinTypeForNativeRelocationType(relocation.type);
- relocation.offset += bin_slot_offsets_[bin_type];
+ ImageInfo& image_info = GetImageInfo(relocation.oat_filename);
+ relocation.offset += image_info.bin_slot_offsets_[bin_type];
}
+ /* TODO: Reenable the intern table and class table. b/26317072
// Calculate how big the intern table will be after being serialized.
InternTable* const intern_table = runtime->GetInternTable();
CHECK_EQ(intern_table->WeakSize(), 0u) << " should have strong interned all the strings";
@@ -1231,41 +1334,45 @@
class_table_bytes_ += table->WriteToMemory(nullptr);
}
}
+ */
- // Note that image_end_ is left at end of used mirror object section.
+ // Note that image_info.image_end_ is left at end of used mirror object section.
}
void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) {
CHECK_NE(0U, oat_loaded_size);
- const uint8_t* oat_file_begin = GetOatFileBegin();
+ const char* oat_filename = oat_file_->GetLocation().c_str();
+ ImageInfo& image_info = GetImageInfo(oat_filename);
+ const uint8_t* oat_file_begin = GetOatFileBegin(oat_filename);
const uint8_t* oat_file_end = oat_file_begin + oat_loaded_size;
- oat_data_begin_ = oat_file_begin + oat_data_offset;
- const uint8_t* oat_data_end = oat_data_begin_ + oat_file_->Size();
+ image_info.oat_data_begin_ = const_cast<uint8_t*>(oat_file_begin) + oat_data_offset;
+ const uint8_t* oat_data_end = image_info.oat_data_begin_ + oat_file_->Size();
+ image_info.oat_size_ = oat_file_->Size();
// Create the image sections.
ImageSection sections[ImageHeader::kSectionCount];
// Objects section
auto* objects_section = §ions[ImageHeader::kSectionObjects];
- *objects_section = ImageSection(0u, image_end_);
+ *objects_section = ImageSection(0u, image_info.image_end_);
size_t cur_pos = objects_section->End();
// Add field section.
auto* field_section = §ions[ImageHeader::kSectionArtFields];
- *field_section = ImageSection(cur_pos, bin_slot_sizes_[kBinArtField]);
- CHECK_EQ(bin_slot_offsets_[kBinArtField], field_section->Offset());
+ *field_section = ImageSection(cur_pos, image_info.bin_slot_sizes_[kBinArtField]);
+ CHECK_EQ(image_info.bin_slot_offsets_[kBinArtField], field_section->Offset());
cur_pos = field_section->End();
// Round up to the alignment the required by the method section.
cur_pos = RoundUp(cur_pos, ArtMethod::Alignment(target_ptr_size_));
// Add method section.
auto* methods_section = §ions[ImageHeader::kSectionArtMethods];
*methods_section = ImageSection(cur_pos,
- bin_slot_sizes_[kBinArtMethodClean] +
- bin_slot_sizes_[kBinArtMethodDirty]);
- CHECK_EQ(bin_slot_offsets_[kBinArtMethodClean], methods_section->Offset());
+ image_info.bin_slot_sizes_[kBinArtMethodClean] +
+ image_info.bin_slot_sizes_[kBinArtMethodDirty]);
+ CHECK_EQ(image_info.bin_slot_offsets_[kBinArtMethodClean], methods_section->Offset());
cur_pos = methods_section->End();
// Add dex cache arrays section.
auto* dex_cache_arrays_section = §ions[ImageHeader::kSectionDexCacheArrays];
- *dex_cache_arrays_section = ImageSection(cur_pos, bin_slot_sizes_[kBinDexCacheArray]);
- CHECK_EQ(bin_slot_offsets_[kBinDexCacheArray], dex_cache_arrays_section->Offset());
+ *dex_cache_arrays_section = ImageSection(cur_pos, image_info.bin_slot_sizes_[kBinDexCacheArray]);
+ CHECK_EQ(image_info.bin_slot_offsets_[kBinDexCacheArray], dex_cache_arrays_section->Offset());
cur_pos = dex_cache_arrays_section->End();
// Round up to the alignment the string table expects. See HashSet::WriteToMemory.
cur_pos = RoundUp(cur_pos, sizeof(uint64_t));
@@ -1282,42 +1389,51 @@
// Image end goes right before the start of the image bitmap.
const size_t image_end = static_cast<uint32_t>(cur_pos);
// Finally bitmap section.
- const size_t bitmap_bytes = image_bitmap_->Size();
+ const size_t bitmap_bytes = image_info.image_bitmap_->Size();
auto* bitmap_section = §ions[ImageHeader::kSectionImageBitmap];
*bitmap_section = ImageSection(RoundUp(cur_pos, kPageSize), RoundUp(bitmap_bytes, kPageSize));
cur_pos = bitmap_section->End();
- if (kIsDebugBuild) {
+ if (VLOG_IS_ON(compiler)) {
+ LOG(INFO) << "Creating header for " << oat_filename;
size_t idx = 0;
for (const ImageSection& section : sections) {
LOG(INFO) << static_cast<ImageHeader::ImageSections>(idx) << " " << section;
++idx;
}
LOG(INFO) << "Methods: clean=" << clean_methods_ << " dirty=" << dirty_methods_;
+ LOG(INFO) << "Image roots address=" << std::hex << image_info.image_roots_address_ << std::dec;
+ LOG(INFO) << "Image begin=" << std::hex << reinterpret_cast<uintptr_t>(global_image_begin_)
+ << " Image offset=" << image_info.image_offset_ << std::dec;
+ LOG(INFO) << "Oat file begin=" << std::hex << reinterpret_cast<uintptr_t>(oat_file_begin)
+ << " Oat data begin=" << reinterpret_cast<uintptr_t>(image_info.oat_data_begin_)
+ << " Oat data end=" << reinterpret_cast<uintptr_t>(oat_data_end)
+ << " Oat file end=" << reinterpret_cast<uintptr_t>(oat_file_end);
}
- CHECK_EQ(AlignUp(image_begin_ + image_end, kPageSize), oat_file_begin) <<
- "Oat file should be right after the image.";
+
// Create the header, leave 0 for data size since we will fill this in as we are writing the
// image.
- new (image_->Begin()) ImageHeader(PointerToLowMemUInt32(image_begin_),
- image_end,
- sections,
- image_roots_address_,
- oat_file_->GetOatHeader().GetChecksum(),
- PointerToLowMemUInt32(oat_file_begin),
- PointerToLowMemUInt32(oat_data_begin_),
- PointerToLowMemUInt32(oat_data_end),
- PointerToLowMemUInt32(oat_file_end),
- target_ptr_size_,
- compile_pic_,
- image_storage_mode_,
- /*data_size*/0u);
+ new (image_info.image_->Begin()) ImageHeader(PointerToLowMemUInt32(image_info.image_begin_),
+ image_end,
+ sections,
+ image_info.image_roots_address_,
+ oat_file_->GetOatHeader().GetChecksum(),
+ PointerToLowMemUInt32(oat_file_begin),
+ PointerToLowMemUInt32(image_info.oat_data_begin_),
+ PointerToLowMemUInt32(oat_data_end),
+ PointerToLowMemUInt32(oat_file_end),
+ target_ptr_size_,
+ compile_pic_,
+ image_storage_mode_,
+ /*data_size*/0u);
}
ArtMethod* ImageWriter::GetImageMethodAddress(ArtMethod* method) {
auto it = native_object_relocations_.find(method);
CHECK(it != native_object_relocations_.end()) << PrettyMethod(method) << " @ " << method;
- CHECK_GE(it->second.offset, image_end_) << "ArtMethods should be after Objects";
- return reinterpret_cast<ArtMethod*>(image_begin_ + it->second.offset);
+ const char* oat_filename = GetOatFilename(method->GetDexCache());
+ ImageInfo& image_info = GetImageInfo(oat_filename);
+ CHECK_GE(it->second.offset, image_info.image_end_) << "ArtMethods should be after Objects";
+ return reinterpret_cast<ArtMethod*>(image_info.image_begin_ + it->second.offset);
}
class FixupRootVisitor : public RootVisitor {
@@ -1345,18 +1461,24 @@
mirror::Object* ImageAddress(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_) {
const size_t offset = image_writer_->GetImageOffset(obj);
- auto* const dest = reinterpret_cast<Object*>(image_writer_->image_begin_ + offset);
+ auto* const dest = reinterpret_cast<Object*>(image_writer_->global_image_begin_ + offset);
VLOG(compiler) << "Update root from " << obj << " to " << dest;
return dest;
}
};
void ImageWriter::CopyAndFixupNativeData() {
+ const char* oat_filename = oat_file_->GetLocation().c_str();
+ ImageInfo& image_info = GetImageInfo(oat_filename);
// Copy ArtFields and methods to their locations and update the array for convenience.
for (auto& pair : native_object_relocations_) {
NativeObjectRelocation& relocation = pair.second;
- auto* dest = image_->Begin() + relocation.offset;
- DCHECK_GE(dest, image_->Begin() + image_end_);
+ // Only work with fields and methods that are in the current oat file.
+ if (strcmp(relocation.oat_filename, oat_filename) != 0) {
+ continue;
+ }
+ auto* dest = image_info.image_->Begin() + relocation.offset;
+ DCHECK_GE(dest, image_info.image_->Begin() + image_info.image_end_);
DCHECK(!IsInBootImage(pair.first));
switch (relocation.type) {
case kNativeObjectRelocationTypeArtField: {
@@ -1368,7 +1490,8 @@
case kNativeObjectRelocationTypeArtMethodClean:
case kNativeObjectRelocationTypeArtMethodDirty: {
CopyAndFixupMethod(reinterpret_cast<ArtMethod*>(pair.first),
- reinterpret_cast<ArtMethod*>(dest));
+ reinterpret_cast<ArtMethod*>(dest),
+ image_info);
break;
}
// For arrays, copy just the header since the elements will get copied by their corresponding
@@ -1391,30 +1514,36 @@
}
}
// Fixup the image method roots.
- auto* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
+ auto* image_header = reinterpret_cast<ImageHeader*>(image_info.image_->Begin());
const ImageSection& methods_section = image_header->GetMethodsSection();
for (size_t i = 0; i < ImageHeader::kImageMethodsCount; ++i) {
ArtMethod* method = image_methods_[i];
CHECK(method != nullptr);
+ // Only place runtime methods in the image of the default oat file.
+ if (method->IsRuntimeMethod() && strcmp(default_oat_filename_, oat_filename) != 0) {
+ continue;
+ }
if (!IsInBootImage(method)) {
auto it = native_object_relocations_.find(method);
- CHECK(it != native_object_relocations_.end()) << "No fowarding for " << PrettyMethod(method);
+ CHECK(it != native_object_relocations_.end()) << "No forwarding for " << PrettyMethod(method);
NativeObjectRelocation& relocation = it->second;
CHECK(methods_section.Contains(relocation.offset)) << relocation.offset << " not in "
<< methods_section;
CHECK(relocation.IsArtMethodRelocation()) << relocation.type;
- method = reinterpret_cast<ArtMethod*>(image_begin_ + it->second.offset);
+ method = reinterpret_cast<ArtMethod*>(global_image_begin_ + it->second.offset);
}
image_header->SetImageMethod(static_cast<ImageHeader::ImageMethod>(i), method);
}
FixupRootVisitor root_visitor(this);
+ /* TODO: Reenable the intern table and class table
// Write the intern table into the image.
const ImageSection& intern_table_section = image_header->GetImageSection(
ImageHeader::kSectionInternedStrings);
Runtime* const runtime = Runtime::Current();
InternTable* const intern_table = runtime->GetInternTable();
- uint8_t* const intern_table_memory_ptr = image_->Begin() + intern_table_section.Offset();
+ uint8_t* const intern_table_memory_ptr =
+ image_info.image_->Begin() + intern_table_section.Offset();
const size_t intern_table_bytes = intern_table->WriteToMemory(intern_table_memory_ptr);
CHECK_EQ(intern_table_bytes, intern_table_bytes_);
// Fixup the pointers in the newly written intern table to contain image addresses.
@@ -1430,10 +1559,11 @@
// Write the class table(s) into the image. class_table_bytes_ may be 0 if there are multiple
// class loaders. Writing multiple class tables into the image is currently unsupported.
if (class_table_bytes_ > 0u) {
- ClassLinker* const class_linker = runtime->GetClassLinker();
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
const ImageSection& class_table_section = image_header->GetImageSection(
ImageHeader::kSectionClassTable);
- uint8_t* const class_table_memory_ptr = image_->Begin() + class_table_section.Offset();
+ uint8_t* const class_table_memory_ptr =
+ image_info.image_->Begin() + class_table_section.Offset();
ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
size_t class_table_bytes = 0;
for (mirror::ClassLoader* loader : class_loaders_) {
@@ -1453,6 +1583,7 @@
}
CHECK_EQ(class_table_bytes, class_table_bytes_);
}
+ */
}
void ImageWriter::CopyAndFixupObjects() {
@@ -1500,7 +1631,8 @@
}
UNREACHABLE();
} else {
- elem = image_begin_ + it->second.offset;
+ ImageInfo& image_info = GetImageInfo(it->second.oat_filename);
+ elem = image_info.image_begin_ + it->second.offset;
}
}
dest_array->SetElementPtrSize<false, true>(i, elem, target_ptr_size_);
@@ -1512,14 +1644,16 @@
return;
}
size_t offset = GetImageOffset(obj);
- auto* dst = reinterpret_cast<Object*>(image_->Begin() + offset);
- DCHECK_LT(offset, image_end_);
+ const char* oat_filename = GetOatFilename(obj);
+ ImageInfo& image_info = GetImageInfo(oat_filename);
+ auto* dst = reinterpret_cast<Object*>(image_info.image_->Begin() + offset);
+ DCHECK_LT(offset, image_info.image_end_);
const auto* src = reinterpret_cast<const uint8_t*>(obj);
- image_bitmap_->Set(dst); // Mark the obj as live.
+ image_info.image_bitmap_->Set(dst); // Mark the obj as live.
const size_t n = obj->SizeOf();
- DCHECK_LE(offset + n, image_->Size());
+ DCHECK_LE(offset + n, image_info.image_->Size());
memcpy(dst, src, n);
// Write in a hash code of objects which have inflated monitors or a hash code in their monitor
@@ -1595,34 +1729,55 @@
}
template <typename T>
-T* ImageWriter::NativeLocationInImage(T* obj) {
- return (obj == nullptr || IsInBootImage(obj))
- ? obj
- : reinterpret_cast<T*>(image_begin_ + NativeOffsetInImage(obj));
+T* ImageWriter::NativeLocationInImage(T* obj, const char* oat_filename) {
+ if (obj == nullptr || IsInBootImage(obj)) {
+ return obj;
+ } else {
+ ImageInfo& image_info = GetImageInfo(oat_filename);
+ return reinterpret_cast<T*>(image_info.image_begin_ + NativeOffsetInImage(obj));
+ }
}
template <typename T>
-T* ImageWriter::NativeCopyLocation(T* obj) {
- return (obj == nullptr || IsInBootImage(obj))
- ? obj
- : reinterpret_cast<T*>(image_->Begin() + NativeOffsetInImage(obj));
+T* ImageWriter::NativeCopyLocation(T* obj, mirror::DexCache* dex_cache) {
+ if (obj == nullptr || IsInBootImage(obj)) {
+ return obj;
+ } else {
+ const char* oat_filename = GetOatFilenameForDexCache(dex_cache);
+ ImageInfo& image_info = GetImageInfo(oat_filename);
+ return reinterpret_cast<T*>(image_info.image_->Begin() + NativeOffsetInImage(obj));
+ }
}
class NativeLocationVisitor {
public:
- explicit NativeLocationVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {}
+ explicit NativeLocationVisitor(ImageWriter* image_writer, const char* oat_filename)
+ : image_writer_(image_writer), oat_filename_(oat_filename) {}
template <typename T>
- T* operator()(T* ptr) const {
- return image_writer_->NativeLocationInImage(ptr);
+ T* operator()(T* ptr) const SHARED_REQUIRES(Locks::mutator_lock_) {
+ return image_writer_->NativeLocationInImage(ptr, oat_filename_);
+ }
+
+ ArtMethod* operator()(ArtMethod* method) const SHARED_REQUIRES(Locks::mutator_lock_) {
+ const char* oat_filename = method->IsRuntimeMethod() ? image_writer_->GetDefaultOatFilename() :
+ image_writer_->GetOatFilenameForDexCache(method->GetDexCache());
+ return image_writer_->NativeLocationInImage(method, oat_filename);
+ }
+
+ ArtField* operator()(ArtField* field) const SHARED_REQUIRES(Locks::mutator_lock_) {
+ const char* oat_filename = image_writer_->GetOatFilenameForDexCache(field->GetDexCache());
+ return image_writer_->NativeLocationInImage(field, oat_filename);
}
private:
ImageWriter* const image_writer_;
+ const char* oat_filename_;
};
void ImageWriter::FixupClass(mirror::Class* orig, mirror::Class* copy) {
- orig->FixupNativePointers(copy, target_ptr_size_, NativeLocationVisitor(this));
+ const char* oat_filename = GetOatFilename(orig);
+ orig->FixupNativePointers(copy, target_ptr_size_, NativeLocationVisitor(this, oat_filename));
FixupClassVisitor visitor(this, copy);
static_cast<mirror::Object*>(orig)->VisitReferences(visitor, visitor);
}
@@ -1661,7 +1816,7 @@
CHECK(it != native_object_relocations_.end())
<< "Missing relocation for AbstractMethod.artMethod " << PrettyMethod(src_method);
dest->SetArtMethod(
- reinterpret_cast<ArtMethod*>(image_begin_ + it->second.offset));
+ reinterpret_cast<ArtMethod*>(global_image_begin_ + it->second.offset));
} else if (!klass->IsArrayClass()) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
if (klass == class_linker->GetClassRoot(ClassLinker::kJavaLangDexCache)) {
@@ -1702,41 +1857,52 @@
// 64-bit values here, clearing the top 32 bits for 32-bit targets. The zero-extension is
// done by casting to the unsigned type uintptr_t before casting to int64_t, i.e.
// static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + offset))).
+ const char* oat_filename = GetOatFilenameForDexCache(orig_dex_cache);
GcRoot<mirror::String>* orig_strings = orig_dex_cache->GetStrings();
if (orig_strings != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::StringsOffset(),
- NativeLocationInImage(orig_strings),
+ NativeLocationInImage(orig_strings, oat_filename),
/*pointer size*/8u);
- orig_dex_cache->FixupStrings(NativeCopyLocation(orig_strings), ImageAddressVisitor(this));
+ orig_dex_cache->FixupStrings(NativeCopyLocation(orig_strings, orig_dex_cache),
+ ImageAddressVisitor(this));
}
GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes();
if (orig_types != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedTypesOffset(),
- NativeLocationInImage(orig_types),
+ NativeLocationInImage(orig_types, oat_filename),
/*pointer size*/8u);
- orig_dex_cache->FixupResolvedTypes(NativeCopyLocation(orig_types), ImageAddressVisitor(this));
+ orig_dex_cache->FixupResolvedTypes(NativeCopyLocation(orig_types, orig_dex_cache),
+ ImageAddressVisitor(this));
}
ArtMethod** orig_methods = orig_dex_cache->GetResolvedMethods();
if (orig_methods != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedMethodsOffset(),
- NativeLocationInImage(orig_methods),
+ NativeLocationInImage(orig_methods, oat_filename),
/*pointer size*/8u);
- ArtMethod** copy_methods = NativeCopyLocation(orig_methods);
+ ArtMethod** copy_methods = NativeCopyLocation(orig_methods, orig_dex_cache);
for (size_t i = 0, num = orig_dex_cache->NumResolvedMethods(); i != num; ++i) {
ArtMethod* orig = mirror::DexCache::GetElementPtrSize(orig_methods, i, target_ptr_size_);
- ArtMethod* copy = NativeLocationInImage(orig);
+ const char* method_oat_filename;
+ if (orig == nullptr || orig->IsRuntimeMethod()) {
+ method_oat_filename = default_oat_filename_;
+ } else {
+ method_oat_filename = GetOatFilenameForDexCache(orig->GetDexCache());
+ }
+ ArtMethod* copy = NativeLocationInImage(orig, method_oat_filename);
mirror::DexCache::SetElementPtrSize(copy_methods, i, copy, target_ptr_size_);
}
}
ArtField** orig_fields = orig_dex_cache->GetResolvedFields();
if (orig_fields != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedFieldsOffset(),
- NativeLocationInImage(orig_fields),
+ NativeLocationInImage(orig_fields, oat_filename),
/*pointer size*/8u);
- ArtField** copy_fields = NativeCopyLocation(orig_fields);
+ ArtField** copy_fields = NativeCopyLocation(orig_fields, orig_dex_cache);
for (size_t i = 0, num = orig_dex_cache->NumResolvedFields(); i != num; ++i) {
ArtField* orig = mirror::DexCache::GetElementPtrSize(orig_fields, i, target_ptr_size_);
- ArtField* copy = NativeLocationInImage(orig);
+ const char* field_oat_filename =
+ orig == nullptr ? default_oat_filename_ : GetOatFilenameForDexCache(orig->GetDexCache());
+ ArtField* copy = NativeLocationInImage(orig, field_oat_filename);
mirror::DexCache::SetElementPtrSize(copy_fields, i, copy, target_ptr_size_);
}
}
@@ -1747,9 +1913,10 @@
// If we are compiling an app image, we need to use the stubs of the boot image.
if (compile_app_image_) {
// Use the current image pointers.
- gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetBootImageSpace();
- DCHECK(image_space != nullptr);
- const OatFile* oat_file = image_space->GetOatFile();
+ std::vector<gc::space::ImageSpace*> image_spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ DCHECK(!image_spaces.empty());
+ const OatFile* oat_file = image_spaces[0]->GetOatFile();
CHECK(oat_file != nullptr);
const OatHeader& header = oat_file->GetOatHeader();
switch (type) {
@@ -1772,10 +1939,13 @@
UNREACHABLE();
}
}
- return GetOatAddressForOffset(oat_address_offsets_[type]);
+ const ImageInfo& primary_image_info = GetImageInfo(0);
+ return GetOatAddressForOffset(primary_image_info.oat_address_offsets_[type], primary_image_info);
}
-const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, bool* quick_is_interpreted) {
+const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method,
+ const ImageInfo& image_info,
+ bool* quick_is_interpreted) {
DCHECK(!method->IsResolutionMethod()) << PrettyMethod(method);
DCHECK(!method->IsImtConflictMethod()) << PrettyMethod(method);
DCHECK(!method->IsImtUnimplementedMethod()) << PrettyMethod(method);
@@ -1788,7 +1958,7 @@
// Quick entrypoint:
uint32_t quick_oat_code_offset = PointerToLowMemUInt32(
method->GetEntryPointFromQuickCompiledCodePtrSize(target_ptr_size_));
- const uint8_t* quick_code = GetOatAddressForOffset(quick_oat_code_offset);
+ const uint8_t* quick_code = GetOatAddressForOffset(quick_oat_code_offset, image_info);
*quick_is_interpreted = false;
if (quick_code != nullptr && (!method->IsStatic() || method->IsConstructor() ||
method->GetDeclaringClass()->IsInitialized())) {
@@ -1808,42 +1978,32 @@
quick_code = GetOatAddress(kOatAddressQuickResolutionTrampoline);
}
if (!IsInBootOatFile(quick_code)) {
- DCHECK_GE(quick_code, oat_data_begin_);
+ // DCHECK_GE(quick_code, oat_data_begin_);
}
return quick_code;
}
-const uint8_t* ImageWriter::GetQuickEntryPoint(ArtMethod* method) {
- // Calculate the quick entry point following the same logic as FixupMethod() below.
- // The resolution method has a special trampoline to call.
- Runtime* runtime = Runtime::Current();
- if (UNLIKELY(method == runtime->GetResolutionMethod())) {
- return GetOatAddress(kOatAddressQuickResolutionTrampoline);
- } else if (UNLIKELY(method == runtime->GetImtConflictMethod() ||
- method == runtime->GetImtUnimplementedMethod())) {
- return GetOatAddress(kOatAddressQuickIMTConflictTrampoline);
- } else {
- // We assume all methods have code. If they don't currently then we set them to the use the
- // resolution trampoline. Abstract methods never have code and so we need to make sure their
- // use results in an AbstractMethodError. We use the interpreter to achieve this.
- if (UNLIKELY(!method->IsInvokable())) {
- return GetOatAddress(kOatAddressQuickToInterpreterBridge);
- } else {
- bool quick_is_interpreted;
- return GetQuickCode(method, &quick_is_interpreted);
- }
- }
-}
-
-void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy) {
+void ImageWriter::CopyAndFixupMethod(ArtMethod* orig,
+ ArtMethod* copy,
+ const ImageInfo& image_info) {
memcpy(copy, orig, ArtMethod::Size(target_ptr_size_));
copy->SetDeclaringClass(GetImageAddress(orig->GetDeclaringClassUnchecked()));
+ const char* oat_filename;
+ if (orig->IsRuntimeMethod()) {
+ oat_filename = default_oat_filename_;
+ } else {
+ auto it = dex_file_oat_filename_map_.find(orig->GetDexFile());
+ DCHECK(it != dex_file_oat_filename_map_.end()) << orig->GetDexFile()->GetLocation();
+ oat_filename = it->second;
+ }
ArtMethod** orig_resolved_methods = orig->GetDexCacheResolvedMethods(target_ptr_size_);
- copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods), target_ptr_size_);
+ copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods, oat_filename),
+ target_ptr_size_);
GcRoot<mirror::Class>* orig_resolved_types = orig->GetDexCacheResolvedTypes(target_ptr_size_);
- copy->SetDexCacheResolvedTypes(NativeLocationInImage(orig_resolved_types), target_ptr_size_);
+ copy->SetDexCacheResolvedTypes(NativeLocationInImage(orig_resolved_types, oat_filename),
+ target_ptr_size_);
// OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to
// oat_begin_
@@ -1877,7 +2037,7 @@
GetOatAddress(kOatAddressQuickToInterpreterBridge), target_ptr_size_);
} else {
bool quick_is_interpreted;
- const uint8_t* quick_code = GetQuickCode(orig, &quick_is_interpreted);
+ const uint8_t* quick_code = GetQuickCode(orig, image_info, &quick_is_interpreted);
copy->SetEntryPointFromQuickCompiledCodePtrSize(quick_code, target_ptr_size_);
// JNI entrypoint:
@@ -1914,13 +2074,16 @@
CHECK(oat_header != nullptr);
CHECK(oat_header->IsValid());
- ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
+ ImageInfo& image_info = GetImageInfo(oat_file_->GetLocation().c_str());
+ ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_info.image_->Begin());
image_header->SetOatChecksum(oat_header->GetChecksum());
}
-size_t ImageWriter::GetBinSizeSum(ImageWriter::Bin up_to) const {
+size_t ImageWriter::GetBinSizeSum(ImageWriter::ImageInfo& image_info, ImageWriter::Bin up_to) const {
DCHECK_LE(up_to, kBinSize);
- return std::accumulate(&bin_slot_sizes_[0], &bin_slot_sizes_[up_to], /*init*/0);
+ return std::accumulate(&image_info.bin_slot_sizes_[0],
+ &image_info.bin_slot_sizes_[up_to],
+ /*init*/0);
}
ImageWriter::BinSlot::BinSlot(uint32_t lockword) : lockword_(lockword) {
@@ -1946,15 +2109,18 @@
return lockword_ & ~kBinMask;
}
-uint8_t* ImageWriter::GetOatFileBegin() const {
- DCHECK_GT(intern_table_bytes_, 0u);
- size_t native_sections_size = bin_slot_sizes_[kBinArtField] +
- bin_slot_sizes_[kBinArtMethodDirty] +
- bin_slot_sizes_[kBinArtMethodClean] +
- bin_slot_sizes_[kBinDexCacheArray] +
- intern_table_bytes_ +
- class_table_bytes_;
- return image_begin_ + RoundUp(image_end_ + native_sections_size, kPageSize);
+uint8_t* ImageWriter::GetOatFileBegin(const char* oat_filename) const {
+ // DCHECK_GT(intern_table_bytes_, 0u); TODO: Reenable intern table and class table.
+ uintptr_t last_image_end = 0;
+ for (const char* oat_fn : oat_filenames_) {
+ const ImageInfo& image_info = GetConstImageInfo(oat_fn);
+ DCHECK(image_info.image_begin_ != nullptr);
+ uintptr_t this_end = reinterpret_cast<uintptr_t>(image_info.image_begin_) +
+ image_info.image_size_;
+ last_image_end = std::max(this_end, last_image_end);
+ }
+ const ImageInfo& image_info = GetConstImageInfo(oat_filename);
+ return reinterpret_cast<uint8_t*>(last_image_end) + image_info.oat_offset_;
}
ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocationType type) {
@@ -1974,4 +2140,61 @@
UNREACHABLE();
}
+const char* ImageWriter::GetOatFilename(mirror::Object* obj) const {
+ if (compile_app_image_) {
+ return default_oat_filename_;
+ } else {
+ return GetOatFilenameForDexCache(obj->IsDexCache() ? obj->AsDexCache() :
+ obj->IsClass() ? obj->AsClass()->GetDexCache() : obj->GetClass()->GetDexCache());
+ }
+}
+
+const char* ImageWriter::GetOatFilenameForDexCache(mirror::DexCache* dex_cache) const {
+ if (compile_app_image_ || dex_cache == nullptr) {
+ return default_oat_filename_;
+ } else {
+ auto it = dex_file_oat_filename_map_.find(dex_cache->GetDexFile());
+ DCHECK(it != dex_file_oat_filename_map_.end()) << dex_cache->GetDexFile()->GetLocation();
+ return it->second;
+ }
+}
+
+ImageWriter::ImageInfo& ImageWriter::GetImageInfo(const char* oat_filename) {
+ auto it = image_info_map_.find(oat_filename);
+ DCHECK(it != image_info_map_.end());
+ return it->second;
+}
+
+const ImageWriter::ImageInfo& ImageWriter::GetConstImageInfo(const char* oat_filename) const {
+ auto it = image_info_map_.find(oat_filename);
+ DCHECK(it != image_info_map_.end());
+ return it->second;
+}
+
+const ImageWriter::ImageInfo& ImageWriter::GetImageInfo(size_t index) const {
+ DCHECK_LT(index, oat_filenames_.size());
+ return GetConstImageInfo(oat_filenames_[index]);
+}
+
+void ImageWriter::UpdateOatFile(const char* oat_filename) {
+ std::unique_ptr<File> oat_file(OS::OpenFileForReading(oat_filename));
+ DCHECK(oat_file != nullptr);
+ size_t oat_loaded_size = 0;
+ size_t oat_data_offset = 0;
+ ElfWriter::GetOatElfInformation(oat_file.get(), &oat_loaded_size, &oat_data_offset);
+
+ ImageInfo& cur_image_info = GetImageInfo(oat_filename);
+
+ // Update the oat_offset of the next image info.
+ auto it = std::find(oat_filenames_.begin(), oat_filenames_.end(), oat_filename);
+ DCHECK(it != oat_filenames_.end());
+
+ it++;
+ if (it != oat_filenames_.end()) {
+ // There is a following one.
+ ImageInfo& next_image_info = GetImageInfo(*it);
+ next_image_info.oat_offset_ = cur_image_info.oat_offset_ + oat_loaded_size;
+ }
+}
+
} // namespace art
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index f1b2965..78297ae 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -56,30 +56,31 @@
uintptr_t image_begin,
bool compile_pic,
bool compile_app_image,
- ImageHeader::StorageMode image_storage_mode)
+ ImageHeader::StorageMode image_storage_mode,
+ const std::vector<const char*> oat_filenames,
+ const std::unordered_map<const DexFile*, const char*>& dex_file_oat_filename_map)
: compiler_driver_(compiler_driver),
- image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
- image_end_(0),
+ global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
image_objects_offset_begin_(0),
- image_roots_address_(0),
oat_file_(nullptr),
- oat_data_begin_(nullptr),
compile_pic_(compile_pic),
compile_app_image_(compile_app_image),
boot_image_space_(nullptr),
target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())),
- bin_slot_sizes_(),
- bin_slot_offsets_(),
- bin_slot_count_(),
intern_table_bytes_(0u),
image_method_array_(ImageHeader::kImageMethodsCount),
dirty_methods_(0u),
clean_methods_(0u),
class_table_bytes_(0u),
- image_storage_mode_(image_storage_mode) {
+ image_storage_mode_(image_storage_mode),
+ dex_file_oat_filename_map_(dex_file_oat_filename_map),
+ oat_filenames_(oat_filenames),
+ default_oat_filename_(oat_filenames[0]) {
CHECK_NE(image_begin, 0U);
+ for (const char* oat_filename : oat_filenames) {
+ image_info_map_.emplace(oat_filename, ImageInfo());
+ }
std::fill_n(image_methods_, arraysize(image_methods_), nullptr);
- std::fill_n(oat_address_offsets_, arraysize(oat_address_offsets_), 0);
}
~ImageWriter() {
@@ -88,14 +89,25 @@
bool PrepareImageAddressSpace();
bool IsImageAddressSpaceReady() const {
- return image_roots_address_ != 0u;
+ bool ready = !image_info_map_.empty();
+ for (auto& pair : image_info_map_) {
+ const ImageInfo& image_info = pair.second;
+ if (image_info.image_roots_address_ == 0u) {
+ return false;
+ }
+ }
+ return ready;
}
template <typename T>
T* GetImageAddress(T* object) const SHARED_REQUIRES(Locks::mutator_lock_) {
- return (object == nullptr || IsInBootImage(object))
- ? object
- : reinterpret_cast<T*>(image_begin_ + GetImageOffset(object));
+ if (object == nullptr || IsInBootImage(object)) {
+ return object;
+ } else {
+ const char* oat_filename = GetOatFilename(object);
+ const ImageInfo& image_info = GetConstImageInfo(oat_filename);
+ return reinterpret_cast<T*>(image_info.image_begin_ + GetImageOffset(object));
+ }
}
ArtMethod* GetImageMethodAddress(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
@@ -103,26 +115,36 @@
template <typename PtrType>
PtrType GetDexCacheArrayElementImageAddress(const DexFile* dex_file, uint32_t offset)
const SHARED_REQUIRES(Locks::mutator_lock_) {
- auto it = dex_cache_array_starts_.find(dex_file);
- DCHECK(it != dex_cache_array_starts_.end());
+ auto oat_it = dex_file_oat_filename_map_.find(dex_file);
+ DCHECK(oat_it != dex_file_oat_filename_map_.end());
+ const ImageInfo& image_info = GetConstImageInfo(oat_it->second);
+ auto it = image_info.dex_cache_array_starts_.find(dex_file);
+ DCHECK(it != image_info.dex_cache_array_starts_.end());
return reinterpret_cast<PtrType>(
- image_begin_ + bin_slot_offsets_[kBinDexCacheArray] + it->second + offset);
+ image_info.image_begin_ + image_info.bin_slot_offsets_[kBinDexCacheArray] +
+ it->second + offset);
}
- uint8_t* GetOatFileBegin() const;
+ uint8_t* GetOatFileBegin(const char* oat_filename) const;
// If image_fd is not kInvalidImageFd, then we use that for the file. Otherwise we open
- // image_filename.
+ // the names in image_filenames.
bool Write(int image_fd,
- const std::string& image_filename,
- const std::string& oat_filename,
- const std::string& oat_location)
+ const std::vector<const char*>& image_filenames,
+ const std::vector<const char*>& oat_filenames)
REQUIRES(!Locks::mutator_lock_);
- uintptr_t GetOatDataBegin() {
- return reinterpret_cast<uintptr_t>(oat_data_begin_);
+ uintptr_t GetOatDataBegin(const char* oat_filename) {
+ return reinterpret_cast<uintptr_t>(GetImageInfo(oat_filename).oat_data_begin_);
}
+ const char* GetOatFilenameForDexCache(mirror::DexCache* dex_cache) const
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Update the oat size for the given oat file. This will make the oat_offset for the next oat
+ // file valid.
+ void UpdateOatFile(const char* oat_filename);
+
private:
bool AllocMemory();
@@ -214,6 +236,58 @@
const uint32_t lockword_;
};
+ struct ImageInfo {
+ explicit ImageInfo()
+ : image_begin_(nullptr),
+ image_end_(RoundUp(sizeof(ImageHeader), kObjectAlignment)),
+ image_roots_address_(0),
+ image_offset_(0),
+ image_size_(0),
+ oat_offset_(0),
+ bin_slot_sizes_(),
+ bin_slot_offsets_(),
+ bin_slot_count_() {}
+
+ std::unique_ptr<MemMap> image_; // Memory mapped for generating the image.
+
+ // Target begin of this image. Notes: It is not valid to write here, this is the address
+ // of the target image, not necessarily where image_ is mapped. The address is only valid
+ // after layouting (otherwise null).
+ uint8_t* image_begin_;
+
+ size_t image_end_; // Offset to the free space in image_, initially size of image header.
+ uint32_t image_roots_address_; // The image roots address in the image.
+ size_t image_offset_; // Offset of this image from the start of the first image.
+
+ // Image size is the *address space* covered by this image. As the live bitmap is aligned
+ // to the page size, the live bitmap will cover more address space than necessary. But live
+ // bitmaps may not overlap, so an image has a "shadow," which is accounted for in the size.
+ // The next image may only start at image_begin_ + image_size_ (which is guaranteed to be
+ // page-aligned).
+ size_t image_size_;
+
+ // Oat data.
+ size_t oat_offset_; // Offset of the oat file for this image from start of oat files. This is
+ // valid when the previous oat file has been written.
+ uint8_t* oat_data_begin_; // Start of oatdata in the corresponding oat file. This is
+ // valid when the images have been layed out.
+ size_t oat_size_; // Size of the corresponding oat data.
+
+ // Image bitmap which lets us know where the objects inside of the image reside.
+ std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> image_bitmap_;
+
+ // The start offsets of the dex cache arrays.
+ SafeMap<const DexFile*, size_t> dex_cache_array_starts_;
+
+ // Offset from oat_data_begin_ to the stubs.
+ uint32_t oat_address_offsets_[kOatAddressCount];
+
+ // Bin slot tracking for dirty object packing.
+ size_t bin_slot_sizes_[kBinSize]; // Number of bytes in a bin.
+ size_t bin_slot_offsets_[kBinSize]; // Number of bytes in previous bins.
+ size_t bin_slot_count_[kBinSize]; // Number of objects in a bin.
+ };
+
// We use the lock word to store the offset of the object in the image.
void AssignImageOffset(mirror::Object* object, BinSlot bin_slot)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -233,7 +307,8 @@
SHARED_REQUIRES(Locks::mutator_lock_);
BinSlot GetImageBinSlot(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_);
- void AddDexCacheArrayRelocation(void* array, size_t offset) SHARED_REQUIRES(Locks::mutator_lock_);
+ void AddDexCacheArrayRelocation(void* array, size_t offset, mirror::DexCache* dex_cache)
+ SHARED_REQUIRES(Locks::mutator_lock_);
void AddMethodPointerArray(mirror::PointerArray* arr) SHARED_REQUIRES(Locks::mutator_lock_);
static void* GetImageAddressCallback(void* writer, mirror::Object* obj)
@@ -244,19 +319,21 @@
mirror::Object* GetLocalAddress(mirror::Object* object) const
SHARED_REQUIRES(Locks::mutator_lock_) {
size_t offset = GetImageOffset(object);
- uint8_t* dst = image_->Begin() + offset;
+ const char* oat_filename = GetOatFilename(object);
+ const ImageInfo& image_info = GetConstImageInfo(oat_filename);
+ uint8_t* dst = image_info.image_->Begin() + offset;
return reinterpret_cast<mirror::Object*>(dst);
}
// Returns the address in the boot image if we are compiling the app image.
const uint8_t* GetOatAddress(OatAddress type) const;
- const uint8_t* GetOatAddressForOffset(uint32_t offset) const {
+ const uint8_t* GetOatAddressForOffset(uint32_t offset, const ImageInfo& image_info) const {
// With Quick, code is within the OatFile, as there are all in one
- // .o ELF object.
- DCHECK_LE(offset, oat_file_->Size());
- DCHECK(oat_data_begin_ != nullptr);
- return offset == 0u ? nullptr : oat_data_begin_ + offset;
+ // .o ELF object. But interpret it as signed.
+ DCHECK_LE(static_cast<int32_t>(offset), static_cast<int32_t>(image_info.oat_size_));
+ DCHECK(image_info.oat_data_begin_ != nullptr);
+ return offset == 0u ? nullptr : image_info.oat_data_begin_ + static_cast<int32_t>(offset);
}
// Returns true if the class was in the original requested image classes list.
@@ -282,7 +359,7 @@
SHARED_REQUIRES(Locks::mutator_lock_);
void CreateHeader(size_t oat_loaded_size, size_t oat_data_offset)
SHARED_REQUIRES(Locks::mutator_lock_);
- mirror::ObjectArray<mirror::Object>* CreateImageRoots() const
+ mirror::ObjectArray<mirror::Object>* CreateImageRoots(const char* oat_filename) const
SHARED_REQUIRES(Locks::mutator_lock_);
void CalculateObjectBinSlots(mirror::Object* obj)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -304,7 +381,7 @@
static void CopyAndFixupObjectsCallback(mirror::Object* obj, void* arg)
SHARED_REQUIRES(Locks::mutator_lock_);
void CopyAndFixupObject(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_);
- void CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy)
+ void CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy, const ImageInfo& image_info)
SHARED_REQUIRES(Locks::mutator_lock_);
void FixupClass(mirror::Class* orig, mirror::Class* copy)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -319,23 +396,24 @@
SHARED_REQUIRES(Locks::mutator_lock_);
// Get quick code for non-resolution/imt_conflict/abstract method.
- const uint8_t* GetQuickCode(ArtMethod* method, bool* quick_is_interpreted)
- SHARED_REQUIRES(Locks::mutator_lock_);
-
- const uint8_t* GetQuickEntryPoint(ArtMethod* method)
+ const uint8_t* GetQuickCode(ArtMethod* method,
+ const ImageInfo& image_info,
+ bool* quick_is_interpreted)
SHARED_REQUIRES(Locks::mutator_lock_);
// Patches references in OatFile to expect runtime addresses.
void SetOatChecksumFromElfFile(File* elf_file);
// Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins.
- size_t GetBinSizeSum(Bin up_to = kBinSize) const;
+ size_t GetBinSizeSum(ImageInfo& image_info, Bin up_to = kBinSize) const;
// Return true if a method is likely to be dirtied at runtime.
bool WillMethodBeDirty(ArtMethod* m) const SHARED_REQUIRES(Locks::mutator_lock_);
// Assign the offset for an ArtMethod.
- void AssignMethodOffset(ArtMethod* method, NativeObjectRelocationType type)
+ void AssignMethodOffset(ArtMethod* method,
+ NativeObjectRelocationType type,
+ const char* oat_filename)
SHARED_REQUIRES(Locks::mutator_lock_);
// Return true if klass is loaded by the boot class loader but not in the boot image.
@@ -359,11 +437,11 @@
// Location of where the object will be when the image is loaded at runtime.
template <typename T>
- T* NativeLocationInImage(T* obj);
+ T* NativeLocationInImage(T* obj, const char* oat_filename) SHARED_REQUIRES(Locks::mutator_lock_);
// Location of where the temporary copy of the object currently is.
template <typename T>
- T* NativeCopyLocation(T* obj);
+ T* NativeCopyLocation(T* obj, mirror::DexCache* dex_cache) SHARED_REQUIRES(Locks::mutator_lock_);
// Return true of obj is inside of the boot image space. This may only return true if we are
// compiling an app image.
@@ -372,46 +450,35 @@
// Return true if ptr is within the boot oat file.
bool IsInBootOatFile(const void* ptr) const;
+ const char* GetOatFilename(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_);
+
+ const char* GetDefaultOatFilename() const {
+ return default_oat_filename_;
+ }
+
+ ImageInfo& GetImageInfo(const char* oat_filename);
+ const ImageInfo& GetConstImageInfo(const char* oat_filename) const;
+ const ImageInfo& GetImageInfo(size_t index) const;
+
const CompilerDriver& compiler_driver_;
- // Beginning target image address for the output image.
- uint8_t* image_begin_;
-
- // Offset to the free space in image_.
- size_t image_end_;
+ // Beginning target image address for the first image.
+ uint8_t* global_image_begin_;
// Offset from image_begin_ to where the first object is in image_.
size_t image_objects_offset_begin_;
- // The image roots address in the image.
- uint32_t image_roots_address_;
-
// oat file with code for this image
OatFile* oat_file_;
- // Memory mapped for generating the image.
- std::unique_ptr<MemMap> image_;
-
// Pointer arrays that need to be updated. Since these are only some int and long arrays, we need
// to keep track. These include vtable arrays, iftable arrays, and dex caches.
std::unordered_map<mirror::PointerArray*, Bin> pointer_arrays_;
- // The start offsets of the dex cache arrays.
- SafeMap<const DexFile*, size_t> dex_cache_array_starts_;
-
// Saved hash codes. We use these to restore lockwords which were temporarily used to have
// forwarding addresses as well as copying over hash codes.
std::unordered_map<mirror::Object*, uint32_t> saved_hashcode_map_;
- // Beginning target oat address for the pointers from the output image to its oat file.
- const uint8_t* oat_data_begin_;
-
- // Image bitmap which lets us know where the objects inside of the image reside.
- std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> image_bitmap_;
-
- // Offset from oat_data_begin_ to the stubs.
- uint32_t oat_address_offsets_[kOatAddressCount];
-
// Boolean flags.
const bool compile_pic_;
const bool compile_app_image_;
@@ -422,10 +489,8 @@
// Size of pointers on the target architecture.
size_t target_ptr_size_;
- // Bin slot tracking for dirty object packing
- size_t bin_slot_sizes_[kBinSize]; // Number of bytes in a bin
- size_t bin_slot_offsets_[kBinSize]; // Number of bytes in previous bins.
- size_t bin_slot_count_[kBinSize]; // Number of objects in a bin
+ // Mapping of oat filename to image data.
+ std::unordered_map<std::string, ImageInfo> image_info_map_;
// Cached size of the intern table for when we allocate memory.
size_t intern_table_bytes_;
@@ -434,6 +499,7 @@
// have one entry per art field for convenience. ArtFields are placed right after the end of the
// image objects (aka sum of bin_slot_sizes_). ArtMethods are placed right after the ArtFields.
struct NativeObjectRelocation {
+ const char* oat_filename;
uintptr_t offset;
NativeObjectRelocationType type;
@@ -468,6 +534,11 @@
// Which mode the image is stored as, see image.h
const ImageHeader::StorageMode image_storage_mode_;
+ // Map of dex files to the oat filenames that they were compiled into.
+ const std::unordered_map<const DexFile*, const char*>& dex_file_oat_filename_map_;
+ const std::vector<const char*> oat_filenames_;
+ const char* default_oat_filename_;
+
friend class ContainsBootClassLoaderNonImageClassVisitor;
friend class FixupClassVisitor;
friend class FixupRootVisitor;
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index d001495..b323d24 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -84,6 +84,7 @@
CompilerOptions::kDefaultNumDexMethodsThreshold,
CompilerOptions::kDefaultInlineDepthLimit,
CompilerOptions::kDefaultInlineMaxCodeUnits,
+ /* no_inline_from */ nullptr,
/* include_patch_information */ false,
CompilerOptions::kDefaultTopKProfileThreshold,
Runtime::Current()->IsDebuggable(),
@@ -154,7 +155,8 @@
/* dump_cfg_append */ false,
cumulative_logger_.get(),
/* swap_fd */ -1,
- /* profile_file */ ""));
+ /* profile_file */ "",
+ /* dex to oat map */ nullptr));
// Disable dedupe so we can remove compiled methods.
compiler_driver_->SetDedupeEnabled(false);
compiler_driver_->SetSupportBootImageFixup(false);
diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
index 92cf8ca..877a674 100644
--- a/compiler/linker/relative_patcher_test.h
+++ b/compiler/linker/relative_patcher_test.h
@@ -47,7 +47,7 @@
driver_(&compiler_options_, &verification_results_, &inliner_map_,
Compiler::kQuick, instruction_set, nullptr,
false, nullptr, nullptr, nullptr, 1u,
- false, false, "", false, nullptr, -1, ""),
+ false, false, "", false, nullptr, -1, "", nullptr),
error_msg_(),
instruction_set_(instruction_set),
features_(InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg_)),
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 7b7d46c..58f46d6 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -121,7 +121,8 @@
false,
timer_.get(),
-1,
- ""));
+ "",
+ nullptr));
}
bool WriteElf(File* file,
@@ -199,7 +200,7 @@
ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
const OatHeader& oat_header = oat_file->GetOatHeader();
ASSERT_TRUE(oat_header.IsValid());
- ASSERT_EQ(1U, oat_header.GetDexFileCount()); // core
+ ASSERT_EQ(class_linker->GetBootClassPath().size(), oat_header.GetDexFileCount()); // core
ASSERT_EQ(42U, oat_header.GetImageFileLocationOatChecksum());
ASSERT_EQ(4096U, oat_header.GetImageFileLocationOatDataBegin());
ASSERT_EQ("lue.art", std::string(oat_header.GetStoreValueByKey(OatHeader::kImageLocationKey)));
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 53ac77b..025e35e 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -716,6 +716,14 @@
bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
SHARED_REQUIRES(Locks::mutator_lock_) {
+ const DexFile::TypeId& type_id =
+ dex_file_->GetTypeId(dex_file_->GetClassDef(class_def_index_).class_idx_);
+ const char* class_descriptor = dex_file_->GetTypeDescriptor(type_id);
+ // Skip methods that are not in the image.
+ if (!writer_->GetCompilerDriver()->IsImageClass(class_descriptor)) {
+ return true;
+ }
+
OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
@@ -958,7 +966,9 @@
if (writer_->HasBootImage()) {
auto* element = writer_->image_writer_->GetDexCacheArrayElementImageAddress<const uint8_t*>(
patch.TargetDexCacheDexFile(), patch.TargetDexCacheElementOffset());
- const uint8_t* oat_data = writer_->image_writer_->GetOatFileBegin() + file_offset_;
+ const char* oat_filename = writer_->image_writer_->GetOatFilenameForDexCache(dex_cache_);
+ const uint8_t* oat_data =
+ writer_->image_writer_->GetOatFileBegin(oat_filename) + file_offset_;
return element - oat_data;
} else {
size_t start = writer_->dex_cache_arrays_offsets_.Get(patch.TargetDexCacheDexFile());
@@ -994,9 +1004,15 @@
// NOTE: We're using linker patches for app->boot references when the image can
// be relocated and therefore we need to emit .oat_patches. We're not using this
// for app->app references, so check that the method is an image method.
- gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetBootImageSpace();
- size_t method_offset = reinterpret_cast<const uint8_t*>(method) - image_space->Begin();
- CHECK(image_space->GetImageHeader().GetMethodsSection().Contains(method_offset));
+ std::vector<gc::space::ImageSpace*> image_spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ bool contains_method = false;
+ for (gc::space::ImageSpace* image_space : image_spaces) {
+ size_t method_offset = reinterpret_cast<const uint8_t*>(method) - image_space->Begin();
+ contains_method |=
+ image_space->GetImageHeader().GetMethodsSection().Contains(method_offset);
+ }
+ CHECK(contains_method);
}
// Note: We only patch targeting ArtMethods in image which is in the low 4gb.
uint32_t address = PointerToLowMemUInt32(method);
@@ -1012,7 +1028,8 @@
SHARED_REQUIRES(Locks::mutator_lock_) {
uint32_t address = target_offset;
if (writer_->HasBootImage()) {
- address = PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin() +
+ const char* oat_filename = writer_->image_writer_->GetOatFilenameForDexCache(dex_cache_);
+ address = PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin(oat_filename) +
writer_->oat_data_offset_ + target_offset);
}
DCHECK_LE(offset + 4, code->size());
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index a4dcb3a..9842137 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -372,6 +372,18 @@
bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do_rtp) {
const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
+
+ // Check whether we're allowed to inline. The outermost compilation unit is the relevant
+ // dex file here (though the transitivity of an inline chain would allow checking the calller).
+ if (!compiler_driver_->MayInline(method->GetDexFile(),
+ outer_compilation_unit_.GetDexFile())) {
+ VLOG(compiler) << "Won't inline " << PrettyMethod(method) << " in "
+ << outer_compilation_unit_.GetDexFile()->GetLocation() << " ("
+ << caller_compilation_unit_.GetDexFile()->GetLocation() << ") from "
+ << method->GetDexFile()->GetLocation();
+ return false;
+ }
+
uint32_t method_index = FindMethodIndexIn(
method, caller_dex_file, invoke_instruction->GetDexMethodIndex());
if (method_index == DexFile::kDexNoIndex) {
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 831b626..dd1d193 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -880,7 +880,11 @@
bool IsCompilingWithCoreImage() {
const std::string& image = Runtime::Current()->GetImageLocation();
- return EndsWith(image, "core.art") || EndsWith(image, "core-optimizing.art");
+ // TODO: This is under-approximating...
+ if (EndsWith(image, "core.art") || EndsWith(image, "core-optimizing.art")) {
+ return true;
+ }
+ return false;
}
bool OptimizingCompiler::JitCompile(Thread* self,
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 21ce73c..bb4224b 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -193,7 +193,7 @@
UsageError(" corresponding to the file descriptor specified by --zip-fd.");
UsageError(" Example: --zip-location=/system/app/Calculator.apk");
UsageError("");
- UsageError(" --oat-file=<file.oat>: specifies the oat output destination via a filename.");
+ UsageError(" --oat-file=<file.oat>: specifies an oat output destination via a filename.");
UsageError(" Example: --oat-file=/system/framework/boot.oat");
UsageError("");
UsageError(" --oat-fd=<number>: specifies the oat output destination via a file descriptor.");
@@ -203,10 +203,10 @@
UsageError(" to the file descriptor specified by --oat-fd.");
UsageError(" Example: --oat-location=/data/dalvik-cache/system@app@Calculator.apk.oat");
UsageError("");
- UsageError(" --oat-symbols=<file.oat>: specifies the oat output destination with full symbols.");
+ UsageError(" --oat-symbols=<file.oat>: specifies an oat output destination with full symbols.");
UsageError(" Example: --oat-symbols=/symbols/system/framework/boot.oat");
UsageError("");
- UsageError(" --image=<file.art>: specifies the output image filename.");
+ UsageError(" --image=<file.art>: specifies an output image filename.");
UsageError(" Example: --image=/system/framework/boot.art");
UsageError("");
UsageError(" --image-format=(uncompressed|lz4):");
@@ -355,6 +355,9 @@
UsageError(" --app-image-file=<file-name>: specify a file name for app image.");
UsageError(" Example: --app-image-file=/data/dalvik-cache/system@app@Calculator.apk.art");
UsageError("");
+ UsageError(" --multi-image: specify that separate oat and image files be generated for each "
+ "input dex file.");
+ UsageError("");
std::cerr << "See log for usage error information\n";
exit(EXIT_FAILURE);
}
@@ -536,7 +539,9 @@
for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files_) {
dex_file.release();
}
- oat_file_.release();
+ for (std::unique_ptr<File>& oat_file : oat_files_) {
+ oat_file.release();
+ }
runtime_.release();
verification_results_.release();
key_value_store_.release();
@@ -544,7 +549,7 @@
}
struct ParserOptions {
- std::string oat_symbols;
+ std::vector<const char*> oat_symbols;
std::string boot_image_filename;
bool watch_dog_enabled = true;
bool requested_specific_compiler = false;
@@ -644,8 +649,8 @@
}
}
- void ProcessOptions(ParserOptions* parser_options) {
- boot_image_ = !image_filename_.empty();
+ void ProcessOptions(ParserOptions* parser_options, bool multi_image) {
+ boot_image_ = !image_filenames_.empty();
app_image_ = app_image_fd_ != -1 || !app_image_file_name_.empty();
if (IsAppImage() && IsBootImage()) {
@@ -657,11 +662,11 @@
compiler_options_->debuggable_ = true;
}
- if (oat_filename_.empty() && oat_fd_ == -1) {
+ if (oat_filenames_.empty() && oat_fd_ == -1) {
Usage("Output must be supplied with either --oat-file or --oat-fd");
}
- if (!oat_filename_.empty() && oat_fd_ != -1) {
+ if (!oat_filenames_.empty() && oat_fd_ != -1) {
Usage("--oat-file should not be used with --oat-fd");
}
@@ -673,10 +678,19 @@
Usage("--oat-symbols should not be used with --host");
}
- if (oat_fd_ != -1 && !image_filename_.empty()) {
+ if (oat_fd_ != -1 && !image_filenames_.empty()) {
Usage("--oat-fd should not be used with --image");
}
+ if (!parser_options->oat_symbols.empty() &&
+ parser_options->oat_symbols.size() != oat_filenames_.size()) {
+ Usage("--oat-file arguments do not match --oat-symbols arguments");
+ }
+
+ if (!image_filenames_.empty() && image_filenames_.size() != oat_filenames_.size()) {
+ Usage("--oat-file arguments do not match --image arguments");
+ }
+
if (android_root_.empty()) {
const char* android_root_env_var = getenv("ANDROID_ROOT");
if (android_root_env_var == nullptr) {
@@ -737,6 +751,12 @@
Usage("--dex-location arguments do not match --dex-file arguments");
}
+ if (!dex_filenames_.empty() && !oat_filenames_.empty()) {
+ if (oat_filenames_.size() != 1 && oat_filenames_.size() != dex_filenames_.size()) {
+ Usage("--oat-file arguments must be singular or match --dex-file arguments");
+ }
+ }
+
if (zip_fd_ != -1 && zip_location_.empty()) {
Usage("--zip-location should be supplied with --zip-fd");
}
@@ -747,11 +767,8 @@
}
}
- oat_stripped_ = oat_filename_;
if (!parser_options->oat_symbols.empty()) {
- oat_unstripped_ = parser_options->oat_symbols;
- } else {
- oat_unstripped_ = oat_filename_;
+ oat_unstripped_ = std::move(parser_options->oat_symbols);
}
// If no instruction set feature was given, use the default one for the target
@@ -816,6 +833,68 @@
compiler_options_->verbose_methods_ = verbose_methods_.empty() ? nullptr : &verbose_methods_;
+ if (!IsBootImage() && multi_image) {
+ Usage("--multi-image can only be used when creating boot images");
+ }
+ if (IsBootImage() && multi_image && image_filenames_.size() > 1) {
+ Usage("--multi-image cannot be used with multiple image names");
+ }
+
+ // For now, if we're on the host and compile the boot image, *always* use multiple image files.
+ if (!kIsTargetBuild && IsBootImage()) {
+ if (image_filenames_.size() == 1) {
+ multi_image = true;
+ }
+ }
+
+ if (IsBootImage() && multi_image) {
+ // Expand the oat and image filenames.
+ std::string base_oat = oat_filenames_[0];
+ size_t last_oat_slash = base_oat.rfind('/');
+ if (last_oat_slash == std::string::npos) {
+ Usage("--multi-image used with unusable oat filename %s", base_oat.c_str());
+ }
+ base_oat = base_oat.substr(0, last_oat_slash + 1);
+
+ std::string base_img = image_filenames_[0];
+ size_t last_img_slash = base_img.rfind('/');
+ if (last_img_slash == std::string::npos) {
+ Usage("--multi-image used with unusable image filename %s", base_img.c_str());
+ }
+ base_img = base_img.substr(0, last_img_slash + 1);
+
+ // Now create the other names.
+ // Note: we have some special case here for our testing. We have to inject the differentiating
+ // parts for the different core images.
+ std::string infix; // Empty infix by default.
+ {
+ // Check the first name.
+ std::string dex_file = oat_filenames_[0];
+ size_t last_dex_slash = dex_file.rfind('/');
+ if (last_dex_slash != std::string::npos) {
+ dex_file = dex_file.substr(last_dex_slash + 1);
+ }
+ size_t last_dex_dot = dex_file.rfind('.');
+ if (last_dex_dot != std::string::npos) {
+ dex_file = dex_file.substr(0, last_dex_dot);
+ }
+ if (StartsWith(dex_file, "core-")) {
+ infix = dex_file.substr(strlen("core"));
+ }
+ }
+ // Use a counted loop to skip the first one.
+ for (size_t i = 1; i < dex_locations_.size(); ++i) {
+ // TODO: Make everything properly std::string.
+ std::string image_name = CreateMultiImageName(dex_locations_[i], infix, ".art");
+ char_backing_storage_.push_back(base_img + image_name);
+ image_filenames_.push_back((char_backing_storage_.end() - 1)->c_str());
+
+ std::string oat_name = CreateMultiImageName(dex_locations_[i], infix, ".oat");
+ char_backing_storage_.push_back(base_oat + oat_name);
+ oat_filenames_.push_back((char_backing_storage_.end() - 1)->c_str());
+ }
+ }
+
// Done with usage checks, enable watchdog if requested
if (parser_options->watch_dog_enabled) {
watchdog_.reset(new WatchDog(true));
@@ -825,6 +904,26 @@
key_value_store_.reset(new SafeMap<std::string, std::string>());
}
+ static std::string CreateMultiImageName(std::string in,
+ const std::string& infix,
+ const char* suffix) {
+ size_t last_dex_slash = in.rfind('/');
+ if (last_dex_slash != std::string::npos) {
+ in = in.substr(last_dex_slash + 1);
+ }
+ if (!infix.empty()) {
+ // Inject infix.
+ size_t last_dot = in.rfind('.');
+ if (last_dot != std::string::npos) {
+ in.insert(last_dot, infix);
+ }
+ }
+ if (EndsWith(in, ".jar")) {
+ in = in.substr(0, in.length() - strlen(".jar")) + (suffix != nullptr ? suffix : "");
+ }
+ return in;
+ }
+
void InsertCompileOptions(int argc, char** argv) {
std::ostringstream oss;
for (int i = 0; i < argc; ++i) {
@@ -865,6 +964,8 @@
std::unique_ptr<ParserOptions> parser_options(new ParserOptions());
compiler_options_.reset(new CompilerOptions());
+ bool multi_image = false;
+
for (int i = 0; i < argc; i++) {
const StringPiece option(argv[i]);
const bool log_options = false;
@@ -880,9 +981,9 @@
} else if (option.starts_with("--zip-location=")) {
zip_location_ = option.substr(strlen("--zip-location=")).data();
} else if (option.starts_with("--oat-file=")) {
- oat_filename_ = option.substr(strlen("--oat-file=")).data();
+ oat_filenames_.push_back(option.substr(strlen("--oat-file=")).data());
} else if (option.starts_with("--oat-symbols=")) {
- parser_options->oat_symbols = option.substr(strlen("--oat-symbols=")).data();
+ parser_options->oat_symbols.push_back(option.substr(strlen("--oat-symbols=")).data());
} else if (option.starts_with("--oat-fd=")) {
ParseOatFd(option);
} else if (option == "--watch-dog") {
@@ -894,7 +995,7 @@
} else if (option.starts_with("--oat-location=")) {
oat_location_ = option.substr(strlen("--oat-location=")).data();
} else if (option.starts_with("--image=")) {
- image_filename_ = option.substr(strlen("--image=")).data();
+ image_filenames_.push_back(option.substr(strlen("--image=")).data());
} else if (option.starts_with("--image-classes=")) {
image_classes_filename_ = option.substr(strlen("--image-classes=")).data();
} else if (option.starts_with("--image-classes-zip=")) {
@@ -961,41 +1062,56 @@
// conditional on having verbost methods.
gLogVerbosity.compiler = false;
Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods_);
+ } else if (option == "--multi-image") {
+ multi_image = true;
+ } else if (option.starts_with("--no-inline-from=")) {
+ no_inline_from_string_ = option.substr(strlen("--no-inline-from=")).data();
} else if (!compiler_options_->ParseCompilerOption(option, Usage)) {
Usage("Unknown argument %s", option.data());
}
}
- ProcessOptions(parser_options.get());
+ ProcessOptions(parser_options.get(), multi_image);
// Insert some compiler things.
InsertCompileOptions(argc, argv);
}
- // Check whether the oat output file is writable, and open it for later. Also open a swap file,
- // if a name is given.
+ // Check whether the oat output files are writable, and open them for later. Also open a swap
+ // file, if a name is given.
bool OpenFile() {
- bool create_file = !oat_unstripped_.empty(); // as opposed to using open file descriptor
+ bool create_file = oat_fd_ == -1; // as opposed to using open file descriptor
if (create_file) {
- oat_file_.reset(OS::CreateEmptyFile(oat_unstripped_.c_str()));
- if (oat_location_.empty()) {
- oat_location_ = oat_filename_;
+ for (const char* oat_filename : oat_filenames_) {
+ std::unique_ptr<File> oat_file(OS::CreateEmptyFile(oat_filename));
+ if (oat_file.get() == nullptr) {
+ PLOG(ERROR) << "Failed to create oat file: " << oat_filename;
+ return false;
+ }
+ if (create_file && fchmod(oat_file->Fd(), 0644) != 0) {
+ PLOG(ERROR) << "Failed to make oat file world readable: " << oat_filename;
+ oat_file->Erase();
+ return false;
+ }
+ oat_files_.push_back(std::move(oat_file));
}
} else {
- oat_file_.reset(new File(oat_fd_, oat_location_, true));
- oat_file_->DisableAutoClose();
- if (oat_file_->SetLength(0) != 0) {
+ std::unique_ptr<File> oat_file(new File(oat_fd_, oat_location_, true));
+ oat_file->DisableAutoClose();
+ if (oat_file->SetLength(0) != 0) {
PLOG(WARNING) << "Truncating oat file " << oat_location_ << " failed.";
}
- }
- if (oat_file_.get() == nullptr) {
- PLOG(ERROR) << "Failed to create oat file: " << oat_location_;
- return false;
- }
- if (create_file && fchmod(oat_file_->Fd(), 0644) != 0) {
- PLOG(ERROR) << "Failed to make oat file world readable: " << oat_location_;
- oat_file_->Erase();
- return false;
+ if (oat_file.get() == nullptr) {
+ PLOG(ERROR) << "Failed to create oat file: " << oat_location_;
+ return false;
+ }
+ if (create_file && fchmod(oat_file->Fd(), 0644) != 0) {
+ PLOG(ERROR) << "Failed to make oat file world readable: " << oat_location_;
+ oat_file->Erase();
+ return false;
+ }
+ oat_filenames_.push_back(oat_location_.c_str());
+ oat_files_.push_back(std::move(oat_file));
}
// Swap file handling.
@@ -1020,10 +1136,12 @@
return true;
}
- void EraseOatFile() {
- DCHECK(oat_file_.get() != nullptr);
- oat_file_->Erase();
- oat_file_.reset();
+ void EraseOatFiles() {
+ for (size_t i = 0; i < oat_files_.size(); ++i) {
+ DCHECK(oat_files_[i].get() != nullptr);
+ oat_files_[i]->Erase();
+ oat_files_[i].reset();
+ }
}
void Shutdown() {
@@ -1158,9 +1276,35 @@
}
}
+ // Organize inputs, handling multi-dex and multiple oat file outputs.
+ CreateDexOatMappings();
+
return true;
}
+ void CreateDexOatMappings() {
+ if (oat_files_.size() > 1) {
+ size_t index = 0;
+ for (size_t i = 0; i < oat_files_.size(); ++i) {
+ std::vector<const DexFile*> dex_files = { 1, dex_files_[index] };
+ dex_file_oat_filename_map_.emplace(dex_files_[index], oat_filenames_[i]);
+ index++;
+ while (index < dex_files_.size() &&
+ (dex_files_[index]->GetBaseLocation() == dex_files_[index - 1]->GetBaseLocation())) {
+ dex_file_oat_filename_map_.emplace(dex_files_[index], oat_filenames_[i]);
+ dex_files.push_back(dex_files_[index]);
+ index++;
+ }
+ dex_files_per_oat_file_.push_back(std::move(dex_files));
+ }
+ } else {
+ dex_files_per_oat_file_.push_back(dex_files_);
+ for (const DexFile* dex_file : dex_files_) {
+ dex_file_oat_filename_map_.emplace(dex_file, oat_filenames_[0]);
+ }
+ }
+ }
+
// Create and invoke the compiler driver. This will compile all the dex files.
void Compile() {
TimingLogger::ScopedTiming t("dex2oat Compile", timings_);
@@ -1191,6 +1335,85 @@
class_loader = class_linker->CreatePathClassLoader(self, dex_files_, class_path_class_loader);
}
+ // Find the dex file we should not inline from.
+
+ // For now, on the host always have core-oj removed.
+ if (!kIsTargetBuild && no_inline_from_string_.empty()) {
+ no_inline_from_string_ = "core-oj";
+ }
+
+ if (!no_inline_from_string_.empty()) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_);
+ std::vector<const std::vector<const DexFile*>*> dex_file_vectors = {
+ &class_linker->GetBootClassPath(),
+ &class_path_files,
+ &dex_files_
+ };
+ for (const std::vector<const DexFile*>* dex_file_vector : dex_file_vectors) {
+ if (dex_file_vector == nullptr) {
+ continue;
+ }
+
+ bool found = false;
+
+ for (const DexFile* dex_file : *dex_file_vector) {
+ // Try the complete location first.
+ found = no_inline_from_string_ == dex_file->GetLocation();
+ // The try just the name.
+ if (!found) {
+ size_t last_slash = dex_file->GetLocation().rfind('/');
+ if (last_slash != std::string::npos) {
+ found = StartsWith(dex_file->GetLocation().substr(last_slash + 1),
+ no_inline_from_string_.c_str());
+ }
+ }
+
+ if (found) {
+ VLOG(compiler) << "Disabling inlining from " << dex_file->GetLocation();
+ compiler_options_->no_inline_from_ = dex_file;
+ break;
+ }
+ }
+
+ if (found) {
+ break;
+ }
+ }
+ }
+
+ if (IsBootImage() && image_filenames_.size() > 1) {
+ // If we're compiling the boot image, store the boot classpath into the Key-Value store. If
+ // the image filename was adapted (e.g., for our tests), we need to change this here, too. We
+ // need this for the multi-image case.
+ std::ostringstream bootcp_oss;
+ bool first_bootcp = true;
+ for (size_t i = 0; i < dex_locations_.size(); ++i) {
+ if (!first_bootcp) {
+ bootcp_oss << ":";
+ }
+
+ std::string dex_loc = dex_locations_[i];
+ std::string image_filename = image_filenames_[i];
+
+ // Use the dex_loc path, but the image_filename name.
+ size_t dex_last_slash = dex_loc.rfind('/');
+ size_t image_last_slash = image_filename.rfind('/');
+ if (dex_last_slash == std::string::npos) {
+ dex_loc = image_filename.substr(image_last_slash + 1);
+ } else {
+ dex_loc = dex_loc.substr(0, dex_last_slash + 1) +
+ image_filename.substr(image_last_slash + 1);
+ }
+
+ // Image filenames already end with .art, no need to replace.
+
+ bootcp_oss << dex_loc;
+ first_bootcp = false;
+ }
+ key_value_store_->Put(OatHeader::kBootClassPath, bootcp_oss.str());
+ }
+
driver_.reset(new CompilerDriver(compiler_options_.get(),
verification_results_.get(),
&method_inliner_map_,
@@ -1208,12 +1431,14 @@
dump_cfg_append_,
compiler_phases_timings_.get(),
swap_fd_,
- profile_file_));
+ profile_file_,
+ &dex_file_oat_filename_map_));
driver_->SetDexFilesForOatFile(dex_files_);
driver_->CompileAll(class_loader, dex_files_, timings_);
}
+ // TODO: Update comments about how this works for multi image. b/26317072
// Notes on the interleaving of creating the image and oat file to
// ensure the references between the two are correct.
//
@@ -1275,17 +1500,16 @@
// Steps 1.-3. are done by the CreateOatFile() above, steps 4.-5.
// are done by the CreateImageFile() below.
-
// Write out the generated code part. Calls the OatWriter and ElfBuilder. Also prepares the
// ImageWriter, if necessary.
// Note: Flushing (and closing) the file is the caller's responsibility, except for the failure
// case (when the file will be explicitly erased).
- bool CreateOatFile() {
+ bool CreateOatFiles() {
CHECK(key_value_store_.get() != nullptr);
TimingLogger::ScopedTiming t("dex2oat Oat", timings_);
- std::unique_ptr<OatWriter> oat_writer;
+ std::vector<std::unique_ptr<OatWriter>> oat_writers;
{
TimingLogger::ScopedTiming t2("dex2oat OatWriter", timings_);
std::string image_file_location;
@@ -1294,10 +1518,14 @@
int32_t image_patch_delta = 0;
if (app_image_ && image_base_ == 0) {
- gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetBootImageSpace();
- image_base_ = RoundUp(
- reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatFileEnd()),
- kPageSize);
+ std::vector<gc::space::ImageSpace*> image_spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ for (gc::space::ImageSpace* image_space : image_spaces) {
+ // TODO: IS THIS IN ORDER? JUST TAKE THE LAST ONE?
+ image_base_ = std::max(image_base_, RoundUp(
+ reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatFileEnd()),
+ kPageSize));
+ }
VLOG(compiler) << "App image base=" << reinterpret_cast<void*>(image_base_);
}
@@ -1307,27 +1535,36 @@
if (!IsBootImage()) {
TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
- gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetBootImageSpace();
- image_file_location_oat_checksum = image_space->GetImageHeader().GetOatChecksum();
+ std::vector<gc::space::ImageSpace*> image_spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ image_file_location_oat_checksum = image_spaces[0]->GetImageHeader().GetOatChecksum();
image_file_location_oat_data_begin =
- reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatDataBegin());
- image_file_location = image_space->GetImageFilename();
- image_patch_delta = image_space->GetImageHeader().GetPatchDelta();
+ reinterpret_cast<uintptr_t>(image_spaces[0]->GetImageHeader().GetOatDataBegin());
+ image_patch_delta = image_spaces[0]->GetImageHeader().GetPatchDelta();
+ std::vector<std::string> image_filenames;
+ for (const gc::space::ImageSpace* image_space : image_spaces) {
+ image_filenames.push_back(image_space->GetImageFilename());
+ }
+ image_file_location = Join(image_filenames, ':');
}
if (!image_file_location.empty()) {
key_value_store_->Put(OatHeader::kImageLocationKey, image_file_location);
}
- oat_writer.reset(new OatWriter(dex_files_,
- image_file_location_oat_checksum,
- image_file_location_oat_data_begin,
- image_patch_delta,
- driver_.get(),
- image_writer_.get(),
- IsBootImage(),
- timings_,
- key_value_store_.get()));
+ for (size_t i = 0; i < oat_files_.size(); ++i) {
+ std::vector<const DexFile*>& dex_files = dex_files_per_oat_file_[i];
+ std::unique_ptr<OatWriter> oat_writer(new OatWriter(dex_files,
+ image_file_location_oat_checksum,
+ image_file_location_oat_data_begin,
+ image_patch_delta,
+ driver_.get(),
+ image_writer_.get(),
+ IsBootImage(),
+ timings_,
+ key_value_store_.get()));
+ oat_writers.push_back(std::move(oat_writer));
+ }
}
if (IsImage()) {
@@ -1342,37 +1579,56 @@
{
TimingLogger::ScopedTiming t2("dex2oat Write ELF", timings_);
- std::unique_ptr<ElfWriter> elf_writer =
- CreateElfWriterQuick(instruction_set_, compiler_options_.get(), oat_file_.get());
+ for (size_t i = 0; i < oat_files_.size(); ++i) {
+ std::unique_ptr<File>& oat_file = oat_files_[i];
+ std::unique_ptr<OatWriter>& oat_writer = oat_writers[i];
+ std::unique_ptr<ElfWriter> elf_writer =
+ CreateElfWriterQuick(instruction_set_, compiler_options_.get(), oat_file.get());
- elf_writer->Start();
+ elf_writer->Start();
- OutputStream* rodata = elf_writer->StartRoData();
- if (!oat_writer->WriteRodata(rodata)) {
- LOG(ERROR) << "Failed to write .rodata section to the ELF file " << oat_file_->GetPath();
- return false;
- }
- elf_writer->EndRoData(rodata);
+ OutputStream* rodata = elf_writer->StartRoData();
+ if (!oat_writer->WriteRodata(rodata)) {
+ LOG(ERROR) << "Failed to write .rodata section to the ELF file " << oat_file->GetPath();
+ return false;
+ }
+ elf_writer->EndRoData(rodata);
- OutputStream* text = elf_writer->StartText();
- if (!oat_writer->WriteCode(text)) {
- LOG(ERROR) << "Failed to write .text section to the ELF file " << oat_file_->GetPath();
- return false;
- }
- elf_writer->EndText(text);
+ OutputStream* text = elf_writer->StartText();
+ if (!oat_writer->WriteCode(text)) {
+ LOG(ERROR) << "Failed to write .text section to the ELF file " << oat_file->GetPath();
+ return false;
+ }
+ elf_writer->EndText(text);
- elf_writer->SetBssSize(oat_writer->GetBssSize());
- elf_writer->WriteDynamicSection();
- elf_writer->WriteDebugInfo(oat_writer->GetMethodDebugInfo());
- elf_writer->WritePatchLocations(oat_writer->GetAbsolutePatchLocations());
+ elf_writer->SetBssSize(oat_writer->GetBssSize());
+ elf_writer->WriteDynamicSection();
+ elf_writer->WriteDebugInfo(oat_writer->GetMethodDebugInfo());
+ elf_writer->WritePatchLocations(oat_writer->GetAbsolutePatchLocations());
- if (!elf_writer->End()) {
- LOG(ERROR) << "Failed to write ELF file " << oat_file_->GetPath();
- return false;
+ if (!elf_writer->End()) {
+ LOG(ERROR) << "Failed to write ELF file " << oat_file->GetPath();
+ return false;
+ }
+
+ // Flush the oat file.
+ if (oat_files_[i] != nullptr) {
+ if (oat_files_[i]->Flush() != 0) {
+ PLOG(ERROR) << "Failed to flush oat file: " << oat_filenames_[i];
+ oat_files_[i]->Erase();
+ return false;
+ }
+ }
+
+ if (IsImage()) {
+ // Update oat estimates.
+ UpdateImageWriter(i);
+ }
+
+ VLOG(compiler) << "Oat file written successfully: " << oat_filenames_[i];
}
}
- VLOG(compiler) << "Oat file written successfully (unstripped): " << oat_location_;
return true;
}
@@ -1383,70 +1639,80 @@
if (!CreateImageFile()) {
return false;
}
- VLOG(compiler) << "Image written successfully: " << image_filename_;
+ VLOG(compiler) << "Images written successfully";
}
return true;
}
- // Create a copy from unstripped to stripped.
- bool CopyUnstrippedToStripped() {
- // If we don't want to strip in place, copy from unstripped location to stripped location.
- // We need to strip after image creation because FixupElf needs to use .strtab.
- if (oat_unstripped_ != oat_stripped_) {
- // If the oat file is still open, flush it.
- if (oat_file_.get() != nullptr && oat_file_->IsOpened()) {
- if (!FlushCloseOatFile()) {
+ // Create a copy from stripped to unstripped.
+ bool CopyStrippedToUnstripped() {
+ for (size_t i = 0; i < oat_unstripped_.size(); ++i) {
+ // If we don't want to strip in place, copy from stripped location to unstripped location.
+ // We need to strip after image creation because FixupElf needs to use .strtab.
+ if (strcmp(oat_unstripped_[i], oat_filenames_[i]) != 0) {
+ // If the oat file is still open, flush it.
+ if (oat_files_[i].get() != nullptr && oat_files_[i]->IsOpened()) {
+ if (!FlushCloseOatFile(i)) {
+ return false;
+ }
+ }
+
+ TimingLogger::ScopedTiming t("dex2oat OatFile copy", timings_);
+ std::unique_ptr<File> in(OS::OpenFileForReading(oat_filenames_[i]));
+ std::unique_ptr<File> out(OS::CreateEmptyFile(oat_unstripped_[i]));
+ size_t buffer_size = 8192;
+ std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
+ while (true) {
+ int bytes_read = TEMP_FAILURE_RETRY(read(in->Fd(), buffer.get(), buffer_size));
+ if (bytes_read <= 0) {
+ break;
+ }
+ bool write_ok = out->WriteFully(buffer.get(), bytes_read);
+ CHECK(write_ok);
+ }
+ if (out->FlushCloseOrErase() != 0) {
+ PLOG(ERROR) << "Failed to flush and close copied oat file: " << oat_unstripped_[i];
+ return false;
+ }
+ VLOG(compiler) << "Oat file copied successfully (unstripped): " << oat_unstripped_[i];
+ }
+ }
+ return true;
+ }
+
+ bool FlushOatFiles() {
+ TimingLogger::ScopedTiming t2("dex2oat Flush ELF", timings_);
+ for (size_t i = 0; i < oat_files_.size(); ++i) {
+ if (oat_files_[i].get() != nullptr) {
+ if (oat_files_[i]->Flush() != 0) {
+ PLOG(ERROR) << "Failed to flush oat file: " << oat_filenames_[i];
+ oat_files_[i]->Erase();
return false;
}
}
-
- TimingLogger::ScopedTiming t("dex2oat OatFile copy", timings_);
- std::unique_ptr<File> in(OS::OpenFileForReading(oat_unstripped_.c_str()));
- std::unique_ptr<File> out(OS::CreateEmptyFile(oat_stripped_.c_str()));
- size_t buffer_size = 8192;
- std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
- while (true) {
- int bytes_read = TEMP_FAILURE_RETRY(read(in->Fd(), buffer.get(), buffer_size));
- if (bytes_read <= 0) {
- break;
- }
- bool write_ok = out->WriteFully(buffer.get(), bytes_read);
- CHECK(write_ok);
- }
- if (out->FlushCloseOrErase() != 0) {
- PLOG(ERROR) << "Failed to flush and close copied oat file: " << oat_stripped_;
- return false;
- }
- VLOG(compiler) << "Oat file copied successfully (stripped): " << oat_stripped_;
}
return true;
}
- bool FlushOatFile() {
- if (oat_file_.get() != nullptr) {
- TimingLogger::ScopedTiming t2("dex2oat Flush ELF", timings_);
- if (oat_file_->Flush() != 0) {
- PLOG(ERROR) << "Failed to flush oat file: " << oat_location_ << " / "
- << oat_filename_;
- oat_file_->Erase();
- return false;
- }
- }
- return true;
- }
-
- bool FlushCloseOatFile() {
- if (oat_file_.get() != nullptr) {
- std::unique_ptr<File> tmp(oat_file_.release());
+ bool FlushCloseOatFile(size_t i) {
+ if (oat_files_[i].get() != nullptr) {
+ std::unique_ptr<File> tmp(oat_files_[i].release());
if (tmp->FlushCloseOrErase() != 0) {
- PLOG(ERROR) << "Failed to flush and close oat file: " << oat_location_ << " / "
- << oat_filename_;
+ PLOG(ERROR) << "Failed to flush and close oat file: " << oat_filenames_[i];
return false;
}
}
return true;
}
+ bool FlushCloseOatFiles() {
+ bool result = true;
+ for (size_t i = 0; i < oat_files_.size(); ++i) {
+ result &= FlushCloseOatFile(i);
+ }
+ return result;
+ }
+
void DumpTiming() {
if (dump_timing_ || (dump_slow_timing_ && timings_->GetTotalNs() > MsToNs(1000))) {
LOG(INFO) << Dumpable<TimingLogger>(*timings_);
@@ -1707,42 +1973,52 @@
image_base,
compiler_options_->GetCompilePic(),
IsAppImage(),
- image_storage_mode_));
+ image_storage_mode_,
+ oat_filenames_,
+ dex_file_oat_filename_map_));
}
- // Let the ImageWriter write the image file. If we do not compile PIC, also fix up the oat file.
+ // Let the ImageWriter write the image files. If we do not compile PIC, also fix up the oat files.
bool CreateImageFile()
REQUIRES(!Locks::mutator_lock_) {
CHECK(image_writer_ != nullptr);
- if (!image_writer_->Write(app_image_fd_,
- IsBootImage() ? image_filename_ : app_image_file_name_,
- oat_unstripped_,
- oat_location_)) {
- LOG(ERROR) << "Failed to create image file " << image_filename_;
+ if (!IsBootImage()) {
+ image_filenames_.push_back(app_image_file_name_.c_str());
+ }
+ if (!image_writer_->Write(app_image_fd_, image_filenames_, oat_filenames_)) {
+ LOG(ERROR) << "Failure during image file creation";
return false;
}
- uintptr_t oat_data_begin = image_writer_->GetOatDataBegin();
+ // We need the OatDataBegin entries.
+ std::map<const char*, uintptr_t> oat_data_begins;
+ for (const char* oat_filename : oat_filenames_) {
+ oat_data_begins.emplace(oat_filename, image_writer_->GetOatDataBegin(oat_filename));
+ }
// Destroy ImageWriter before doing FixupElf.
image_writer_.reset();
- // Do not fix up the ELF file if we are --compile-pic or compiing the app image
- if (!compiler_options_->GetCompilePic() && IsBootImage()) {
- std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_unstripped_.c_str()));
- if (oat_file.get() == nullptr) {
- PLOG(ERROR) << "Failed to open ELF file: " << oat_unstripped_;
- return false;
- }
+ for (const char* oat_filename : oat_filenames_) {
+ // Do not fix up the ELF file if we are --compile-pic or compiling the app image
+ if (!compiler_options_->GetCompilePic() && IsBootImage()) {
+ std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename));
+ if (oat_file.get() == nullptr) {
+ PLOG(ERROR) << "Failed to open ELF file: " << oat_filename;
+ return false;
+ }
- if (!ElfWriter::Fixup(oat_file.get(), oat_data_begin)) {
- oat_file->Erase();
- LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
- return false;
- }
+ uintptr_t oat_data_begin = oat_data_begins.find(oat_filename)->second;
- if (oat_file->FlushCloseOrErase()) {
- PLOG(ERROR) << "Failed to flush and close fixed ELF file " << oat_file->GetPath();
- return false;
+ if (!ElfWriter::Fixup(oat_file.get(), oat_data_begin)) {
+ oat_file->Erase();
+ LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
+ return false;
+ }
+
+ if (oat_file->FlushCloseOrErase()) {
+ PLOG(ERROR) << "Failed to flush and close fixed ELF file " << oat_file->GetPath();
+ return false;
+ }
}
}
@@ -1845,6 +2121,33 @@
"");
}
+ std::string StripIsaFrom(const char* image_filename, InstructionSet isa) {
+ std::string res(image_filename);
+ size_t last_slash = res.rfind('/');
+ if (last_slash == std::string::npos || last_slash == 0) {
+ return res;
+ }
+ size_t penultimate_slash = res.rfind('/', last_slash - 1);
+ if (penultimate_slash == std::string::npos) {
+ return res;
+ }
+ // Check that the string in-between is the expected one.
+ if (res.substr(penultimate_slash + 1, last_slash - penultimate_slash - 1) !=
+ GetInstructionSetString(isa)) {
+ LOG(WARNING) << "Unexpected string when trying to strip isa: " << res;
+ return res;
+ }
+ return res.substr(0, penultimate_slash) + res.substr(last_slash);
+ }
+
+ // Update the estimate for the oat file with the given index.
+ void UpdateImageWriter(size_t index) {
+ DCHECK(image_writer_ != nullptr);
+ DCHECK_LT(index, oat_filenames_.size());
+
+ image_writer_->UpdateOatFile(oat_filenames_[index]);
+ }
+
std::unique_ptr<CompilerOptions> compiler_options_;
Compiler::Kind compiler_kind_;
@@ -1866,11 +2169,10 @@
size_t thread_count_;
uint64_t start_ns_;
std::unique_ptr<WatchDog> watchdog_;
- std::unique_ptr<File> oat_file_;
- std::string oat_stripped_;
- std::string oat_unstripped_;
+ std::vector<std::unique_ptr<File>> oat_files_;
std::string oat_location_;
- std::string oat_filename_;
+ std::vector<const char*> oat_filenames_;
+ std::vector<const char*> oat_unstripped_;
int oat_fd_;
std::vector<const char*> dex_filenames_;
std::vector<const char*> dex_locations_;
@@ -1878,7 +2180,7 @@
std::string zip_location_;
std::string boot_image_filename_;
std::vector<const char*> runtime_args_;
- std::string image_filename_;
+ std::vector<const char*> image_filenames_;
uintptr_t image_base_;
const char* image_classes_zip_filename_;
const char* image_classes_filename_;
@@ -1895,6 +2197,7 @@
bool is_host_;
std::string android_root_;
std::vector<const DexFile*> dex_files_;
+ std::string no_inline_from_string_;
std::vector<jobject> dex_caches_;
std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
@@ -1915,6 +2218,11 @@
std::string profile_file_; // Profile file to use
TimingLogger* timings_;
std::unique_ptr<CumulativeLogger> compiler_phases_timings_;
+ std::vector<std::vector<const DexFile*>> dex_files_per_oat_file_;
+ std::unordered_map<const DexFile*, const char*> dex_file_oat_filename_map_;
+
+ // Backing storage.
+ std::vector<std::string> char_backing_storage_;
DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
};
@@ -1942,19 +2250,18 @@
static int CompileImage(Dex2Oat& dex2oat) {
dex2oat.Compile();
- // Create the boot.oat.
- if (!dex2oat.CreateOatFile()) {
- dex2oat.EraseOatFile();
+ if (!dex2oat.CreateOatFiles()) {
+ dex2oat.EraseOatFiles();
return EXIT_FAILURE;
}
- // Flush and close the boot.oat. We always expect the output file by name, and it will be
- // re-opened from the unstripped name.
- if (!dex2oat.FlushCloseOatFile()) {
+ // Close the image oat files. We always expect the output file by name, and it will be
+ // re-opened from the unstripped name. Note: it's easier to *flush* and close...
+ if (!dex2oat.FlushCloseOatFiles()) {
return EXIT_FAILURE;
}
- // Creates the boot.art and patches the boot.oat.
+ // Creates the boot.art and patches the oat files.
if (!dex2oat.HandleImage()) {
return EXIT_FAILURE;
}
@@ -1965,13 +2272,13 @@
return EXIT_SUCCESS;
}
- // Copy unstripped to stripped location, if necessary.
- if (!dex2oat.CopyUnstrippedToStripped()) {
+ // Copy stripped to unstripped location, if necessary.
+ if (!dex2oat.CopyStrippedToUnstripped()) {
return EXIT_FAILURE;
}
- // FlushClose again, as stripping might have re-opened the oat file.
- if (!dex2oat.FlushCloseOatFile()) {
+ // FlushClose again, as stripping might have re-opened the oat files.
+ if (!dex2oat.FlushCloseOatFiles()) {
return EXIT_FAILURE;
}
@@ -1982,21 +2289,17 @@
static int CompileApp(Dex2Oat& dex2oat) {
dex2oat.Compile();
- // Create the app oat.
- if (!dex2oat.CreateOatFile()) {
- dex2oat.EraseOatFile();
+ if (!dex2oat.CreateOatFiles()) {
+ dex2oat.EraseOatFiles();
return EXIT_FAILURE;
}
- // Do not close the oat file here. We might haven gotten the output file by file descriptor,
+ // Do not close the oat files here. We might have gotten the output file by file descriptor,
// which we would lose.
- if (!dex2oat.FlushOatFile()) {
- return EXIT_FAILURE;
- }
// When given --host, finish early without stripping.
if (dex2oat.IsHost()) {
- if (!dex2oat.FlushCloseOatFile()) {
+ if (!dex2oat.FlushCloseOatFiles()) {
return EXIT_FAILURE;
}
@@ -2004,14 +2307,14 @@
return EXIT_SUCCESS;
}
- // Copy unstripped to stripped location, if necessary. This will implicitly flush & close the
- // unstripped version. If this is given, we expect to be able to open writable files by name.
- if (!dex2oat.CopyUnstrippedToStripped()) {
+ // Copy stripped to unstripped location, if necessary. This will implicitly flush & close the
+ // stripped versions. If this is given, we expect to be able to open writable files by name.
+ if (!dex2oat.CopyStrippedToUnstripped()) {
return EXIT_FAILURE;
}
- // Flush and close the file.
- if (!dex2oat.FlushCloseOatFile()) {
+ // Flush and close the files.
+ if (!dex2oat.FlushCloseOatFiles()) {
return EXIT_FAILURE;
}
@@ -2047,7 +2350,7 @@
}
if (!dex2oat.Setup()) {
- dex2oat.EraseOatFile();
+ dex2oat.EraseOatFiles();
return EXIT_FAILURE;
}
diff --git a/dexdump/dexdump_test.cc b/dexdump/dexdump_test.cc
index 4230cb2..9819233 100644
--- a/dexdump/dexdump_test.cc
+++ b/dexdump/dexdump_test.cc
@@ -37,7 +37,7 @@
virtual void SetUp() {
CommonRuntimeTest::SetUp();
// Dogfood our own lib core dex file.
- dex_file_ = GetLibCoreDexFileName();
+ dex_file_ = GetLibCoreDexFileNames()[0];
}
// Runs test with given arguments.
diff --git a/dexlist/dexlist_test.cc b/dexlist/dexlist_test.cc
index 82179de..9a65ba6 100644
--- a/dexlist/dexlist_test.cc
+++ b/dexlist/dexlist_test.cc
@@ -37,7 +37,7 @@
virtual void SetUp() {
CommonRuntimeTest::SetUp();
// Dogfood our own lib core dex file.
- dex_file_ = GetLibCoreDexFileName();
+ dex_file_ = GetLibCoreDexFileNames()[0];
}
// Runs test with given arguments.
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index 5e71053..b8a72af 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -814,9 +814,9 @@
static const ImageHeader& GetBootImageHeader() {
gc::Heap* heap = Runtime::Current()->GetHeap();
- gc::space::ImageSpace* image_space = heap->GetBootImageSpace();
- CHECK(image_space != nullptr);
- const ImageHeader& image_header = image_space->GetImageHeader();
+ std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces();
+ CHECK(!image_spaces.empty());
+ const ImageHeader& image_header = image_spaces[0]->GetImageHeader();
return image_header;
}
@@ -834,22 +834,25 @@
DISALLOW_COPY_AND_ASSIGN(ImgDiagDumper);
};
-static int DumpImage(Runtime* runtime, const char* image_location,
- std::ostream* os, pid_t image_diff_pid) {
+static int DumpImage(Runtime* runtime, std::ostream* os, pid_t image_diff_pid) {
ScopedObjectAccess soa(Thread::Current());
gc::Heap* heap = runtime->GetHeap();
- gc::space::ImageSpace* image_space = heap->GetBootImageSpace();
- CHECK(image_space != nullptr);
- const ImageHeader& image_header = image_space->GetImageHeader();
- if (!image_header.IsValid()) {
- fprintf(stderr, "Invalid image header %s\n", image_location);
- return EXIT_FAILURE;
+ std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces();
+ CHECK(!image_spaces.empty());
+ for (gc::space::ImageSpace* image_space : image_spaces) {
+ const ImageHeader& image_header = image_space->GetImageHeader();
+ if (!image_header.IsValid()) {
+ fprintf(stderr, "Invalid image header %s\n", image_space->GetImageLocation().c_str());
+ return EXIT_FAILURE;
+ }
+
+ ImgDiagDumper img_diag_dumper(
+ os, image_header, image_space->GetImageLocation().c_str(), image_diff_pid);
+ if (!img_diag_dumper.Dump()) {
+ return EXIT_FAILURE;
+ }
}
-
- ImgDiagDumper img_diag_dumper(os, image_header, image_location, image_diff_pid);
-
- bool success = img_diag_dumper.Dump();
- return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
+ return EXIT_SUCCESS;
}
struct ImgDiagArgs : public CmdlineArgs {
@@ -935,7 +938,6 @@
CHECK(args_ != nullptr);
return DumpImage(runtime,
- args_->boot_image_location_,
args_->os_,
args_->image_diff_pid_) == EXIT_SUCCESS;
}
diff --git a/imgdiag/imgdiag_test.cc b/imgdiag/imgdiag_test.cc
index a926ca5..dc101e5 100644
--- a/imgdiag/imgdiag_test.cc
+++ b/imgdiag/imgdiag_test.cc
@@ -47,9 +47,10 @@
CommonRuntimeTest::SetUp();
// We loaded the runtime with an explicit image. Therefore the image space must exist.
- gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetBootImageSpace();
- ASSERT_TRUE(image_space != nullptr);
- boot_image_location_ = image_space->GetImageLocation();
+ std::vector<gc::space::ImageSpace*> image_spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ ASSERT_TRUE(!image_spaces.empty());
+ boot_image_location_ = image_spaces[0]->GetImageLocation();
}
virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index a3ef38d..5c75f20 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -74,14 +74,14 @@
.PHONY: dump-oat-boot-$(TARGET_ARCH)
ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
dump-oat-boot-$(TARGET_ARCH): $(DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME) $(OATDUMP)
- $(OATDUMP) --image=$(DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION) \
+ $(OATDUMP) $(addprefix --image=,$(DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION)) \
--output=$(ART_DUMP_OAT_PATH)/boot.$(TARGET_ARCH).oatdump.txt --instruction-set=$(TARGET_ARCH)
@echo Output in $(ART_DUMP_OAT_PATH)/boot.$(TARGET_ARCH).oatdump.txt
endif
ifdef TARGET_2ND_ARCH
dump-oat-boot-$(TARGET_2ND_ARCH): $(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME) $(OATDUMP)
- $(OATDUMP) --image=$(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION) \
+ $(OATDUMP) $(addprefix --image=,$(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION)) \
--output=$(ART_DUMP_OAT_PATH)/boot.$(TARGET_2ND_ARCH).oatdump.txt --instruction-set=$(TARGET_2ND_ARCH)
@echo Output in $(ART_DUMP_OAT_PATH)/boot.$(TARGET_2ND_ARCH).oatdump.txt
endif
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index bad928e..52c6524 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1492,6 +1492,8 @@
os << "MAGIC: " << image_header_.GetMagic() << "\n\n";
+ os << "IMAGE LOCATION: " << image_space_.GetImageLocation() << "\n\n";
+
os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n";
os << "IMAGE SIZE: " << image_header_.GetImageSize() << "\n\n";
@@ -1599,9 +1601,8 @@
os << "OBJECTS:\n" << std::flush;
- // Loop through all the image spaces and dump their objects.
+ // Loop through the image space and dump its objects.
gc::Heap* heap = runtime->GetHeap();
- const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces();
Thread* self = Thread::Current();
{
{
@@ -1629,21 +1630,16 @@
}
}
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
- for (const auto& space : spaces) {
- if (space->IsImageSpace()) {
- auto* image_space = space->AsImageSpace();
- // Dump the normal objects before ArtMethods.
- image_space->GetLiveBitmap()->Walk(ImageDumper::Callback, this);
- indent_os << "\n";
- // TODO: Dump fields.
- // Dump methods after.
- const auto& methods_section = image_header_.GetMethodsSection();
- const size_t pointer_size =
- InstructionSetPointerSize(oat_dumper_->GetOatInstructionSet());
- DumpArtMethodVisitor visitor(this);
- methods_section.VisitPackedArtMethods(&visitor, image_space->Begin(), pointer_size);
- }
- }
+ // Dump the normal objects before ArtMethods.
+ image_space_.GetLiveBitmap()->Walk(ImageDumper::Callback, this);
+ indent_os << "\n";
+ // TODO: Dump fields.
+ // Dump methods after.
+ const auto& methods_section = image_header_.GetMethodsSection();
+ const size_t pointer_size =
+ InstructionSetPointerSize(oat_dumper_->GetOatInstructionSet());
+ DumpArtMethodVisitor visitor(this);
+ methods_section.VisitPackedArtMethods(&visitor, image_space_.Begin(), pointer_size);
// Dump the large objects separately.
heap->GetLargeObjectsSpace()->GetLiveBitmap()->Walk(ImageDumper::Callback, this);
indent_os << "\n";
@@ -2163,6 +2159,9 @@
size_t sum_of_expansion = 0;
size_t sum_of_expansion_squared = 0;
size_t n = method_outlier_size.size();
+ if (n == 0) {
+ return;
+ }
for (size_t i = 0; i < n; i++) {
size_t cur_size = method_outlier_size[i];
sum_of_sizes += cur_size;
@@ -2377,26 +2376,28 @@
DISALLOW_COPY_AND_ASSIGN(ImageDumper);
};
-static int DumpImage(Runtime* runtime, const char* image_location, OatDumperOptions* options,
- std::ostream* os) {
+static int DumpImage(Runtime* runtime, OatDumperOptions* options, std::ostream* os) {
// Dumping the image, no explicit class loader.
ScopedNullHandle<mirror::ClassLoader> null_class_loader;
options->class_loader_ = &null_class_loader;
ScopedObjectAccess soa(Thread::Current());
gc::Heap* heap = runtime->GetHeap();
- gc::space::ImageSpace* image_space = heap->GetBootImageSpace();
- CHECK(image_space != nullptr);
- const ImageHeader& image_header = image_space->GetImageHeader();
- if (!image_header.IsValid()) {
- fprintf(stderr, "Invalid image header %s\n", image_location);
- return EXIT_FAILURE;
+ std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces();
+ CHECK(!image_spaces.empty());
+ for (gc::space::ImageSpace* image_space : image_spaces) {
+ const ImageHeader& image_header = image_space->GetImageHeader();
+ if (!image_header.IsValid()) {
+ fprintf(stderr, "Invalid image header %s\n", image_space->GetImageLocation().c_str());
+ return EXIT_FAILURE;
+ }
+
+ ImageDumper image_dumper(os, *image_space, image_header, options);
+ if (!image_dumper.Dump()) {
+ return EXIT_FAILURE;
+ }
}
-
- ImageDumper image_dumper(os, *image_space, image_header, options);
-
- bool success = image_dumper.Dump();
- return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
+ return EXIT_SUCCESS;
}
static int DumpOatWithRuntime(Runtime* runtime, OatFile* oat_file, OatDumperOptions* options,
@@ -2689,8 +2690,7 @@
args_->os_) == EXIT_SUCCESS;
}
- return DumpImage(runtime, args_->image_location_, oat_dumper_options_.get(), args_->os_)
- == EXIT_SUCCESS;
+ return DumpImage(runtime, oat_dumper_options_.get(), args_->os_) == EXIT_SUCCESS;
}
std::unique_ptr<OatDumperOptions> oat_dumper_options_;
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 46ab34b..b403abd 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -118,6 +118,38 @@
return true;
}
+static File* CreateOrOpen(const char* name, bool* created) {
+ if (OS::FileExists(name)) {
+ *created = false;
+ return OS::OpenFileReadWrite(name);
+ } else {
+ *created = true;
+ std::unique_ptr<File> f(OS::CreateEmptyFile(name));
+ if (f.get() != nullptr) {
+ if (fchmod(f->Fd(), 0644) != 0) {
+ PLOG(ERROR) << "Unable to make " << name << " world readable";
+ TEMP_FAILURE_RETRY(unlink(name));
+ return nullptr;
+ }
+ }
+ return f.release();
+ }
+}
+
+// Either try to close the file (close=true), or erase it.
+static bool FinishFile(File* file, bool close) {
+ if (close) {
+ if (file->FlushCloseOrErase() != 0) {
+ PLOG(ERROR) << "Failed to flush and close file.";
+ return false;
+ }
+ return true;
+ } else {
+ file->Erase();
+ return false;
+ }
+}
+
bool PatchOat::Patch(const std::string& image_location, off_t delta,
File* output_image, InstructionSet isa,
TimingLogger* timings) {
@@ -195,11 +227,12 @@
LOG(ERROR) << "unable to map image file " << input_image->GetPath() << " : " << error_msg;
return false;
}
- gc::space::ImageSpace* ispc = Runtime::Current()->GetHeap()->GetBootImageSpace();
+ // TODO: Support multi-image when patchoat is only patching images. Ever used? b/26317072
+ gc::space::ImageSpace* ispc = Runtime::Current()->GetHeap()->GetBootImageSpaces()[0];
PatchOat p(isa, image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(), delta, timings);
t.NewTiming("Patching files");
- if (!p.PatchImage()) {
+ if (!p.PatchImage(true)) {
LOG(ERROR) << "Failed to patch image file " << input_image->GetPath();
return false;
}
@@ -214,7 +247,7 @@
bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t delta,
File* output_oat, File* output_image, InstructionSet isa,
TimingLogger* timings,
- bool output_oat_opened_from_fd,
+ bool output_oat_opened_from_fd ATTRIBUTE_UNUSED,
bool new_oat_out) {
CHECK(Runtime::Current() == nullptr);
CHECK(output_image != nullptr);
@@ -236,31 +269,6 @@
isa = GetInstructionSetFromELF(elf_hdr.e_machine, elf_hdr.e_flags);
}
const char* isa_name = GetInstructionSetString(isa);
- std::string image_filename;
- if (!LocationToFilename(image_location, isa, &image_filename)) {
- LOG(ERROR) << "Unable to find image at location " << image_location;
- return false;
- }
- std::unique_ptr<File> input_image(OS::OpenFileForReading(image_filename.c_str()));
- if (input_image.get() == nullptr) {
- LOG(ERROR) << "unable to open input image file at " << image_filename
- << " for location " << image_location;
- return false;
- }
- int64_t image_len = input_image->GetLength();
- if (image_len < 0) {
- LOG(ERROR) << "Error while getting image length";
- return false;
- }
- ImageHeader image_header;
- if (sizeof(image_header) != input_image->Read(reinterpret_cast<char*>(&image_header),
- sizeof(image_header), 0)) {
- LOG(ERROR) << "Unable to read image header from image file " << input_image->GetPath();
- }
-
- /*bool is_image_pic = */IsImagePic(image_header, input_image->GetPath());
- // Nothing special to do right now since the image always needs to get patched.
- // Perhaps in some far-off future we may have images with relative addresses that are true-PIC.
// Set up the runtime
RuntimeOptions options;
@@ -279,70 +287,169 @@
Thread::Current()->TransitionFromRunnableToSuspended(kNative);
ScopedObjectAccess soa(Thread::Current());
+ std::string output_directory =
+ output_image->GetPath().substr(0, output_image->GetPath().find_last_of("/"));
t.NewTiming("Image and oat Patching setup");
- // Create the map where we will write the image patches to.
- std::string error_msg;
- std::unique_ptr<MemMap> image(MemMap::MapFile(image_len,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE,
- input_image->Fd(),
- 0,
- /*low_4gb*/false,
- input_image->GetPath().c_str(),
- &error_msg));
- if (image.get() == nullptr) {
- LOG(ERROR) << "unable to map image file " << input_image->GetPath() << " : " << error_msg;
- return false;
- }
- gc::space::ImageSpace* ispc = Runtime::Current()->GetHeap()->GetBootImageSpace();
+ std::vector<gc::space::ImageSpace*> spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ std::map<gc::space::ImageSpace*, std::unique_ptr<File>> space_to_file_map;
+ std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>> space_to_memmap_map;
+ std::map<gc::space::ImageSpace*, PatchOat> space_to_patchoat_map;
+ std::map<gc::space::ImageSpace*, bool> space_to_skip_patching_map;
- std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat,
- PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg));
- if (elf.get() == nullptr) {
- LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
- return false;
- }
-
- bool skip_patching_oat = false;
- MaybePic is_oat_pic = IsOatPic(elf.get());
- if (is_oat_pic >= ERROR_FIRST) {
- // Error logged by IsOatPic
- return false;
- } else if (is_oat_pic == PIC) {
- // Do not need to do ELF-file patching. Create a symlink and skip the ELF patching.
- if (!ReplaceOatFileWithSymlink(input_oat->GetPath(),
- output_oat->GetPath(),
- output_oat_opened_from_fd,
- new_oat_out)) {
- // Errors already logged by above call.
+ for (size_t i = 0; i < spaces.size(); ++i) {
+ gc::space::ImageSpace* space = spaces[i];
+ std::string input_image_filename = space->GetImageFilename();
+ std::unique_ptr<File> input_image(OS::OpenFileForReading(input_image_filename.c_str()));
+ if (input_image.get() == nullptr) {
+ LOG(ERROR) << "Unable to open input image file at " << input_image_filename;
return false;
}
- // Don't patch the OAT, since we just symlinked it. Image still needs patching.
- skip_patching_oat = true;
- } else {
- CHECK(is_oat_pic == NOT_PIC);
+
+ int64_t image_len = input_image->GetLength();
+ if (image_len < 0) {
+ LOG(ERROR) << "Error while getting image length";
+ return false;
+ }
+ ImageHeader image_header;
+ if (sizeof(image_header) != input_image->Read(reinterpret_cast<char*>(&image_header),
+ sizeof(image_header), 0)) {
+ LOG(ERROR) << "Unable to read image header from image file " << input_image->GetPath();
+ }
+
+ /*bool is_image_pic = */IsImagePic(image_header, input_image->GetPath());
+ // Nothing special to do right now since the image always needs to get patched.
+ // Perhaps in some far-off future we may have images with relative addresses that are true-PIC.
+
+ // Create the map where we will write the image patches to.
+ std::string error_msg;
+ std::unique_ptr<MemMap> image(MemMap::MapFile(image_len,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE,
+ input_image->Fd(),
+ 0,
+ /*low_4gb*/false,
+ input_image->GetPath().c_str(),
+ &error_msg));
+ if (image.get() == nullptr) {
+ LOG(ERROR) << "Unable to map image file " << input_image->GetPath() << " : " << error_msg;
+ return false;
+ }
+ space_to_file_map.emplace(space, std::move(input_image));
+ space_to_memmap_map.emplace(space, std::move(image));
}
- PatchOat p(isa, elf.release(), image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(),
- delta, timings);
- t.NewTiming("Patching files");
- if (!skip_patching_oat && !p.PatchElf()) {
- LOG(ERROR) << "Failed to patch oat file " << input_oat->GetPath();
- return false;
- }
- if (!p.PatchImage()) {
- LOG(ERROR) << "Failed to patch image file " << input_image->GetPath();
- return false;
+ for (size_t i = 0; i < spaces.size(); ++i) {
+ gc::space::ImageSpace* space = spaces[i];
+ std::string input_image_filename = space->GetImageFilename();
+ std::string input_oat_filename =
+ ImageHeader::GetOatLocationFromImageLocation(input_image_filename);
+ std::unique_ptr<File> input_oat_file(OS::OpenFileForReading(input_oat_filename.c_str()));
+ if (input_oat_file.get() == nullptr) {
+ LOG(ERROR) << "Unable to open input oat file at " << input_oat_filename;
+ return false;
+ }
+ std::string error_msg;
+ std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat_file.get(),
+ PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg));
+ if (elf.get() == nullptr) {
+ LOG(ERROR) << "Unable to open oat file " << input_oat_file->GetPath() << " : " << error_msg;
+ return false;
+ }
+
+ bool skip_patching_oat = false;
+ MaybePic is_oat_pic = IsOatPic(elf.get());
+ if (is_oat_pic >= ERROR_FIRST) {
+ // Error logged by IsOatPic
+ return false;
+ } else if (is_oat_pic == PIC) {
+ // Do not need to do ELF-file patching. Create a symlink and skip the ELF patching.
+
+ std::string converted_image_filename = space->GetImageLocation();
+ std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@');
+ std::string output_image_filename = output_directory +
+ (StartsWith(converted_image_filename, "/") ? "" : "/") +
+ converted_image_filename;
+ std::string output_oat_filename =
+ ImageHeader::GetOatLocationFromImageLocation(output_image_filename);
+
+ if (!ReplaceOatFileWithSymlink(input_oat_file->GetPath(),
+ output_oat_filename,
+ false,
+ true)) {
+ // Errors already logged by above call.
+ return false;
+ }
+ // Don't patch the OAT, since we just symlinked it. Image still needs patching.
+ skip_patching_oat = true;
+ } else {
+ CHECK(is_oat_pic == NOT_PIC);
+ }
+
+ PatchOat& p = space_to_patchoat_map.emplace(space,
+ PatchOat(
+ isa,
+ elf.release(),
+ space_to_memmap_map.find(space)->second.get(),
+ space->GetLiveBitmap(),
+ space->GetMemMap(),
+ delta,
+ &space_to_memmap_map,
+ timings)).first->second;
+
+ t.NewTiming("Patching files");
+ if (!skip_patching_oat && !p.PatchElf()) {
+ LOG(ERROR) << "Failed to patch oat file " << input_oat_file->GetPath();
+ return false;
+ }
+ if (!p.PatchImage(i == 0)) {
+ LOG(ERROR) << "Failed to patch image file " << input_image_filename;
+ return false;
+ }
+
+ space_to_skip_patching_map.emplace(space, skip_patching_oat);
}
- t.NewTiming("Writing files");
- if (!skip_patching_oat && !p.WriteElf(output_oat)) {
- LOG(ERROR) << "Failed to write oat file " << input_oat->GetPath();
- return false;
- }
- if (!p.WriteImage(output_image)) {
- LOG(ERROR) << "Failed to write image file " << input_image->GetPath();
- return false;
+ for (size_t i = 0; i < spaces.size(); ++i) {
+ gc::space::ImageSpace* space = spaces[i];
+ std::string input_image_filename = space->GetImageFilename();
+
+ t.NewTiming("Writing files");
+ std::string converted_image_filename = space->GetImageLocation();
+ std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@');
+ std::string output_image_filename = output_directory +
+ (StartsWith(converted_image_filename, "/") ? "" : "/") +
+ converted_image_filename;
+ std::unique_ptr<File>
+ output_image_file(CreateOrOpen(output_image_filename.c_str(), &new_oat_out));
+ if (output_image_file.get() == nullptr) {
+ LOG(ERROR) << "Failed to open output image file at " << output_image_filename;
+ return false;
+ }
+
+ PatchOat& p = space_to_patchoat_map.find(space)->second;
+
+ if (!p.WriteImage(output_image_file.get())) {
+ LOG(ERROR) << "Failed to write image file " << output_image_file->GetPath();
+ return false;
+ }
+ FinishFile(output_image_file.get(), true);
+
+ bool skip_patching_oat = space_to_skip_patching_map.find(space)->second;
+ if (!skip_patching_oat) {
+ std::string output_oat_filename =
+ ImageHeader::GetOatLocationFromImageLocation(output_image_filename);
+ std::unique_ptr<File>
+ output_oat_file(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
+ if (output_oat_file.get() == nullptr) {
+ LOG(ERROR) << "Failed to open output oat file at " << output_oat_filename;
+ return false;
+ }
+ if (!p.WriteElf(output_oat_file.get())) {
+ LOG(ERROR) << "Failed to write oat file " << output_oat_file->GetPath();
+ return false;
+ }
+ FinishFile(output_oat_file.get(), true);
+ }
}
return true;
}
@@ -616,7 +723,7 @@
}
}
-bool PatchOat::PatchImage() {
+bool PatchOat::PatchImage(bool primary_image) {
ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
CHECK_GT(image_->Size(), sizeof(ImageHeader));
// These are the roots from the original file.
@@ -630,9 +737,12 @@
// Patch dex file int/long arrays which point to ArtFields.
PatchDexFileArrays(img_roots);
- VisitObject(img_roots);
+ if (primary_image) {
+ VisitObject(img_roots);
+ }
+
if (!image_header->IsValid()) {
- LOG(ERROR) << "reloction renders image header invalid";
+ LOG(ERROR) << "relocation renders image header invalid";
return false;
}
@@ -655,7 +765,8 @@
void PatchOat::PatchVisitor::operator() (mirror::Object* obj, MemberOffset off,
bool is_static_unused ATTRIBUTE_UNUSED) const {
mirror::Object* referent = obj->GetFieldObject<mirror::Object, kVerifyNone>(off);
- DCHECK(patcher_->InHeap(referent)) << "Referent is not in the heap.";
+ // TODO: Modify check for multi-image support? b/26317072
+ // DCHECK(patcher_->InHeap(referent)) << "Referent is not in the heap.";
mirror::Object* moved_object = patcher_->RelocatedAddressOfPointer(referent);
copy_->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(off, moved_object);
}
@@ -664,7 +775,8 @@
mirror::Reference* ref) const {
MemberOffset off = mirror::Reference::ReferentOffset();
mirror::Object* referent = ref->GetReferent();
- DCHECK(patcher_->InHeap(referent)) << "Referent is not in the heap.";
+ // TODO: Modify check for multi-image support? b/26317072
+ // DCHECK(patcher_->InHeap(referent)) << "Referent is not in the heap.";
mirror::Object* moved_object = patcher_->RelocatedAddressOfPointer(referent);
copy_->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(off, moved_object);
}
@@ -691,7 +803,7 @@
klass->FixupNativePointers(copy_klass, pointer_size, native_visitor);
auto* vtable = klass->GetVTable();
if (vtable != nullptr) {
- vtable->Fixup(RelocatedCopyOf(vtable), pointer_size, native_visitor);
+ vtable->Fixup(RelocatedCopyOfFollowImages(vtable), pointer_size, native_visitor);
}
auto* iftable = klass->GetIfTable();
if (iftable != nullptr) {
@@ -699,7 +811,9 @@
if (iftable->GetMethodArrayCount(i) > 0) {
auto* method_array = iftable->GetMethodArray(i);
CHECK(method_array != nullptr);
- method_array->Fixup(RelocatedCopyOf(method_array), pointer_size, native_visitor);
+ method_array->Fixup(RelocatedCopyOfFollowImages(method_array),
+ pointer_size,
+ native_visitor);
}
}
}
@@ -972,38 +1086,6 @@
return true;
}
-static File* CreateOrOpen(const char* name, bool* created) {
- if (OS::FileExists(name)) {
- *created = false;
- return OS::OpenFileReadWrite(name);
- } else {
- *created = true;
- std::unique_ptr<File> f(OS::CreateEmptyFile(name));
- if (f.get() != nullptr) {
- if (fchmod(f->Fd(), 0644) != 0) {
- PLOG(ERROR) << "Unable to make " << name << " world readable";
- TEMP_FAILURE_RETRY(unlink(name));
- return nullptr;
- }
- }
- return f.release();
- }
-}
-
-// Either try to close the file (close=true), or erase it.
-static bool FinishFile(File* file, bool close) {
- if (close) {
- if (file->FlushCloseOrErase() != 0) {
- PLOG(ERROR) << "Failed to flush and close file.";
- return false;
- }
- return true;
- } else {
- file->Erase();
- return false;
- }
-}
-
static int patchoat(int argc, char **argv) {
InitLogging(argv);
MemMap::Init();
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 38bd865..cb0d14b 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -23,8 +23,10 @@
#include "elf_file.h"
#include "elf_utils.h"
#include "gc/accounting/space_bitmap.h"
+#include "gc/space/image_space.h"
#include "gc/heap.h"
#include "os.h"
+#include "runtime.h"
namespace art {
@@ -57,21 +59,23 @@
bool output_oat_opened_from_fd, // Was this using --oatput-oat-fd ?
bool new_oat_out); // Output oat was a new file created by us?
+ ~PatchOat() {}
+ PatchOat(PatchOat&&) = default;
+
private:
// Takes ownership only of the ElfFile. All other pointers are only borrowed.
PatchOat(ElfFile* oat_file, off_t delta, TimingLogger* timings)
: oat_file_(oat_file), image_(nullptr), bitmap_(nullptr), heap_(nullptr), delta_(delta),
- isa_(kNone), timings_(timings) {}
+ isa_(kNone), space_map_(nullptr), timings_(timings) {}
PatchOat(InstructionSet isa, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap,
MemMap* heap, off_t delta, TimingLogger* timings)
: image_(image), bitmap_(bitmap), heap_(heap),
- delta_(delta), isa_(isa), timings_(timings) {}
+ delta_(delta), isa_(isa), space_map_(nullptr), timings_(timings) {}
PatchOat(InstructionSet isa, ElfFile* oat_file, MemMap* image,
gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta,
- TimingLogger* timings)
+ std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>>* map, TimingLogger* timings)
: oat_file_(oat_file), image_(image), bitmap_(bitmap), heap_(heap),
- delta_(delta), isa_(isa), timings_(timings) {}
- ~PatchOat() {}
+ delta_(delta), isa_(isa), space_map_(map), timings_(timings) {}
// Was the .art image at image_path made with --compile-pic ?
static bool IsImagePic(const ImageHeader& image_header, const std::string& image_path);
@@ -111,7 +115,7 @@
template <typename ElfFileImpl>
bool PatchOatHeader(ElfFileImpl* oat_file);
- bool PatchImage() SHARED_REQUIRES(Locks::mutator_lock_);
+ bool PatchImage(bool primary_image) SHARED_REQUIRES(Locks::mutator_lock_);
void PatchArtFields(const ImageHeader* image_header) SHARED_REQUIRES(Locks::mutator_lock_);
void PatchArtMethods(const ImageHeader* image_header) SHARED_REQUIRES(Locks::mutator_lock_);
void PatchInternedStrings(const ImageHeader* image_header)
@@ -129,15 +133,34 @@
if (obj == nullptr) {
return nullptr;
}
- DCHECK_GT(reinterpret_cast<uintptr_t>(obj), reinterpret_cast<uintptr_t>(heap_->Begin()));
- DCHECK_LT(reinterpret_cast<uintptr_t>(obj), reinterpret_cast<uintptr_t>(heap_->End()));
+ // TODO: Fix these checks for multi-image. Some may still be valid. b/26317072
+ // DCHECK_GT(reinterpret_cast<uintptr_t>(obj), reinterpret_cast<uintptr_t>(heap_->Begin()));
+ // DCHECK_LT(reinterpret_cast<uintptr_t>(obj), reinterpret_cast<uintptr_t>(heap_->End()));
uintptr_t heap_off =
reinterpret_cast<uintptr_t>(obj) - reinterpret_cast<uintptr_t>(heap_->Begin());
- DCHECK_LT(heap_off, image_->Size());
+ // DCHECK_LT(heap_off, image_->Size());
return reinterpret_cast<T*>(image_->Begin() + heap_off);
}
template <typename T>
+ T* RelocatedCopyOfFollowImages(T* obj) const {
+ if (obj == nullptr) {
+ return nullptr;
+ }
+ // Find ImageSpace this belongs to.
+ auto image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ for (gc::space::ImageSpace* image_space : image_spaces) {
+ if (image_space->Contains(obj)) {
+ uintptr_t heap_off = reinterpret_cast<uintptr_t>(obj) -
+ reinterpret_cast<uintptr_t>(image_space->GetMemMap()->Begin());
+ return reinterpret_cast<T*>(space_map_->find(image_space)->second->Begin() + heap_off);
+ }
+ }
+ LOG(FATAL) << "Did not find object in boot image space " << obj;
+ UNREACHABLE();
+ }
+
+ template <typename T>
T* RelocatedAddressOfPointer(T* obj) const {
if (obj == nullptr) {
return obj;
@@ -197,6 +220,8 @@
// Active instruction set, used to know the entrypoint size.
const InstructionSet isa_;
+ const std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>>* space_map_;
+
TimingLogger* timings_;
friend class FixupRootVisitor;
diff --git a/runtime/Android.mk b/runtime/Android.mk
index a854650..de4314c 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -364,6 +364,8 @@
thread_state.h \
verifier/method_verifier.h
+LIBOPENJDKJVM_SRC_FILES := openjdkjvm/OpenjdkJvm.cc
+
LIBART_CFLAGS := -DBUILDING_LIBART=1
LIBART_TARGET_CFLAGS :=
@@ -400,8 +402,9 @@
# $(1): target or host
# $(2): ndebug or debug
-# $(3): static or shared (empty means shared, applies only for host)
-define build-libart
+# $(3): static or shared (note that static only applies for host)
+# $(4): module name : either libart or libopenjdkjvm
+define build-runtime-library
ifneq ($(1),target)
ifneq ($(1),host)
$$(error expected target or host for argument 1, received $(1))
@@ -412,6 +415,11 @@
$$(error expected ndebug or debug for argument 2, received $(2))
endif
endif
+ ifneq ($(4),libart)
+ ifneq ($(4),libopenjdkjvm)
+ $$(error expected libart of libopenjdkjvm for argument 4, received $(4))
+ endif
+ endif
art_target_or_host := $(1)
art_ndebug_or_debug := $(2)
@@ -420,12 +428,12 @@
include $$(CLEAR_VARS)
LOCAL_CPP_EXTENSION := $$(ART_CPP_EXTENSION)
ifeq ($$(art_ndebug_or_debug),ndebug)
- LOCAL_MODULE := libart
+ LOCAL_MODULE := $(4)
ifeq ($$(art_target_or_host),target)
LOCAL_FDO_SUPPORT := true
endif
else # debug
- LOCAL_MODULE := libartd
+ LOCAL_MODULE := $(4)d
endif
LOCAL_MODULE_TAGS := optional
@@ -436,17 +444,25 @@
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
endif
- ifeq ($$(art_target_or_host),target)
- LOCAL_SRC_FILES := $$(LIBART_TARGET_SRC_FILES)
- $$(foreach arch,$$(ART_TARGET_SUPPORTED_ARCH), \
- $$(eval LOCAL_SRC_FILES_$$(arch) := $$$$(LIBART_TARGET_SRC_FILES_$$(arch))))
- else # host
- LOCAL_SRC_FILES := $$(LIBART_HOST_SRC_FILES)
- LOCAL_SRC_FILES_32 := $$(LIBART_HOST_SRC_FILES_32)
- LOCAL_SRC_FILES_64 := $$(LIBART_HOST_SRC_FILES_64)
- LOCAL_IS_HOST_MODULE := true
+ ifeq ($(4),libart)
+ ifeq ($$(art_target_or_host),target)
+ LOCAL_SRC_FILES := $$(LIBART_TARGET_SRC_FILES)
+ $$(foreach arch,$$(ART_TARGET_SUPPORTED_ARCH), \
+ $$(eval LOCAL_SRC_FILES_$$(arch) := $$$$(LIBART_TARGET_SRC_FILES_$$(arch))))
+ else # host
+ LOCAL_SRC_FILES := $$(LIBART_HOST_SRC_FILES)
+ LOCAL_SRC_FILES_32 := $$(LIBART_HOST_SRC_FILES_32)
+ LOCAL_SRC_FILES_64 := $$(LIBART_HOST_SRC_FILES_64)
+ LOCAL_IS_HOST_MODULE := true
+ endif
+ else # libopenjdkjvm
+ LOCAL_SRC_FILES := $$(LIBOPENJDKJVM_SRC_FILES)
+ ifeq ($$(art_target_or_host),host)
+ LOCAL_IS_HOST_MODULE := true
+ endif
endif
+ifeq ($(4),libart)
GENERATED_SRC_DIR := $$(call local-generated-sources-dir)
ENUM_OPERATOR_OUT_CC_FILES := $$(patsubst %.h,%_operator_out.cc,$$(LIBART_ENUM_OPERATOR_OUT_HEADER_FILES))
ENUM_OPERATOR_OUT_GEN := $$(addprefix $$(GENERATED_SRC_DIR)/,$$(ENUM_OPERATOR_OUT_CC_FILES))
@@ -457,6 +473,7 @@
$$(transform-generated-source)
LOCAL_GENERATED_SOURCES += $$(ENUM_OPERATOR_OUT_GEN)
+endif
LOCAL_CFLAGS := $$(LIBART_CFLAGS)
LOCAL_LDFLAGS := $$(LIBART_LDFLAGS)
@@ -538,6 +555,15 @@
LOCAL_SHARED_LIBRARIES += libcutils
endif
endif
+
+ ifeq ($(4),libopenjdkjvm)
+ ifeq ($$(art_ndebug_or_debug),ndebug)
+ LOCAL_SHARED_LIBRARIES += libart
+ else
+ LOCAL_SHARED_LIBRARIES += libartd
+ endif
+ LOCAL_NOTICE_FILE := $(LOCAL_PATH)/openjdkjvm/NOTICE
+ endif
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
LOCAL_ADDITIONAL_DEPENDENCIES += $$(LOCAL_PATH)/Android.mk
@@ -574,24 +600,30 @@
# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since
# they are used to cross compile for the target.
ifeq ($(ART_BUILD_HOST_NDEBUG),true)
- $(eval $(call build-libart,host,ndebug))
+ $(eval $(call build-runtime-library,host,ndebug,shared,libart))
+ $(eval $(call build-runtime-library,host,ndebug,shared,libopenjdkjvm))
ifeq ($(ART_BUILD_HOST_STATIC),true)
- $(eval $(call build-libart,host,ndebug,static))
+ $(eval $(call build-runtime-library,host,ndebug,static,libart))
+ $(eval $(call build-runtime-library,host,ndebug,static,libopenjdkjvm))
endif
endif
ifeq ($(ART_BUILD_HOST_DEBUG),true)
- $(eval $(call build-libart,host,debug))
+ $(eval $(call build-runtime-library,host,debug,shared,libart))
+ $(eval $(call build-runtime-library,host,debug,shared,libopenjdkjvm))
ifeq ($(ART_BUILD_HOST_STATIC),true)
- $(eval $(call build-libart,host,debug,static))
+ $(eval $(call build-runtime-library,host,debug,static,libart))
+ $(eval $(call build-runtime-library,host,debug,static,libopenjdkjvm))
endif
endif
ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
-# $(error $(call build-libart,target,ndebug))
- $(eval $(call build-libart,target,ndebug))
+# $(error $(call build-runtime-library,target,ndebug))
+ $(eval $(call build-runtime-library,target,ndebug,shared,libart))
+ $(eval $(call build-runtime-library,target,ndebug,shared,libopenjdkjvm))
endif
ifeq ($(ART_BUILD_TARGET_DEBUG),true)
- $(eval $(call build-libart,target,debug))
+ $(eval $(call build-runtime-library,target,debug,shared,libart))
+ $(eval $(call build-runtime-library,target,debug,shared,libopenjdkjvm))
endif
# Clear locally defined variables.
@@ -622,4 +654,4 @@
LIBART_CFLAGS :=
LIBART_TARGET_CFLAGS :=
LIBART_HOST_CFLAGS :=
-build-libart :=
+build-runtime-library :=
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 2bc6c79..2b4826e 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -160,16 +160,16 @@
ADD_TEST_EQ(size_t(MIRROR_OBJECT_HEADER_SIZE), sizeof(art::mirror::Object))
// Offsets within java.lang.Class.
-#define MIRROR_CLASS_COMPONENT_TYPE_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_COMPONENT_TYPE_OFFSET (8 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_COMPONENT_TYPE_OFFSET,
art::mirror::Class::ComponentTypeOffset().Int32Value())
-#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (64 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (36 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_ACCESS_FLAGS_OFFSET,
art::mirror::Class::AccessFlagsOffset().Int32Value())
-#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (96 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (100 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_OBJECT_SIZE_OFFSET,
art::mirror::Class::ObjectSizeOffset().Int32Value())
-#define MIRROR_CLASS_STATUS_OFFSET (108 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_STATUS_OFFSET (112 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_STATUS_OFFSET,
art::mirror::Class::StatusOffset().Int32Value())
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d998d99..0518911 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -330,7 +330,7 @@
Runtime* const runtime = Runtime::Current();
gc::Heap* const heap = runtime->GetHeap();
- CHECK(!heap->HasImageSpace()) << "Runtime has image. We should use it.";
+ CHECK(!heap->HasBootImageSpace()) << "Runtime has image. We should use it.";
CHECK(!init_done_);
// Use the pointer size from the runtime since we are probably creating the image.
@@ -736,7 +736,7 @@
static void SanityCheckArtMethod(ArtMethod* m,
mirror::Class* expected_class,
- gc::space::ImageSpace* space)
+ std::vector<gc::space::ImageSpace*> spaces)
SHARED_REQUIRES(Locks::mutator_lock_) {
if (m->IsRuntimeMethod()) {
CHECK(m->GetDeclaringClass() == nullptr) << PrettyMethod(m);
@@ -745,18 +745,22 @@
} else if (expected_class != nullptr) {
CHECK_EQ(m->GetDeclaringClassUnchecked(), expected_class) << PrettyMethod(m);
}
- if (space != nullptr) {
- auto& header = space->GetImageHeader();
- auto& methods = header.GetMethodsSection();
- auto offset = reinterpret_cast<uint8_t*>(m) - space->Begin();
- CHECK(methods.Contains(offset)) << m << " not in " << methods;
+ if (!spaces.empty()) {
+ bool contains = false;
+ for (gc::space::ImageSpace* space : spaces) {
+ auto& header = space->GetImageHeader();
+ auto& methods = header.GetMethodsSection();
+ auto offset = reinterpret_cast<uint8_t*>(m) - space->Begin();
+ contains |= methods.Contains(offset);
+ }
+ CHECK(contains) << m << " not found";
}
}
static void SanityCheckArtMethodPointerArray(mirror::PointerArray* arr,
mirror::Class* expected_class,
size_t pointer_size,
- gc::space::ImageSpace* space)
+ std::vector<gc::space::ImageSpace*> spaces)
SHARED_REQUIRES(Locks::mutator_lock_) {
CHECK(arr != nullptr);
for (int32_t j = 0; j < arr->GetLength(); ++j) {
@@ -766,11 +770,12 @@
CHECK(method != nullptr);
}
if (method != nullptr) {
- SanityCheckArtMethod(method, expected_class, space);
+ SanityCheckArtMethod(method, expected_class, spaces);
}
}
}
+/* TODO: Modify check to support multiple image spaces and reenable. b/26317072
static void SanityCheckArtMethodPointerArray(
ArtMethod** arr,
size_t size,
@@ -790,6 +795,7 @@
}
}
}
+*/
static void SanityCheckObjectsCallback(mirror::Object* obj, void* arg ATTRIBUTE_UNUSED)
SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -805,29 +811,30 @@
CHECK_EQ(field.GetDeclaringClass(), klass);
}
auto* runtime = Runtime::Current();
- auto* image_space = runtime->GetHeap()->GetBootImageSpace();
+ auto image_spaces = runtime->GetHeap()->GetBootImageSpaces();
auto pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
for (auto& m : klass->GetMethods(pointer_size)) {
- SanityCheckArtMethod(&m, klass, image_space);
+ SanityCheckArtMethod(&m, klass, image_spaces);
}
auto* vtable = klass->GetVTable();
if (vtable != nullptr) {
- SanityCheckArtMethodPointerArray(vtable, nullptr, pointer_size, image_space);
+ SanityCheckArtMethodPointerArray(vtable, nullptr, pointer_size, image_spaces);
}
if (klass->ShouldHaveEmbeddedImtAndVTable()) {
for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
- SanityCheckArtMethod(klass->GetEmbeddedImTableEntry(i, pointer_size), nullptr, image_space);
+ SanityCheckArtMethod(
+ klass->GetEmbeddedImTableEntry(i, pointer_size), nullptr, image_spaces);
}
for (int32_t i = 0; i < klass->GetEmbeddedVTableLength(); ++i) {
- SanityCheckArtMethod(klass->GetEmbeddedVTableEntry(i, pointer_size), nullptr, image_space);
+ SanityCheckArtMethod(klass->GetEmbeddedVTableEntry(i, pointer_size), nullptr, image_spaces);
}
}
auto* iftable = klass->GetIfTable();
if (iftable != nullptr) {
for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) {
if (iftable->GetMethodArrayCount(i) > 0) {
- SanityCheckArtMethodPointerArray(iftable->GetMethodArray(i), nullptr, pointer_size,
- image_space);
+ SanityCheckArtMethodPointerArray(
+ iftable->GetMethodArray(i), nullptr, pointer_size, image_spaces);
}
}
}
@@ -856,6 +863,33 @@
DISALLOW_COPY_AND_ASSIGN(SetInterpreterEntrypointArtMethodVisitor);
};
+struct TrampolineCheckData {
+ const void* quick_resolution_trampoline;
+ const void* quick_imt_conflict_trampoline;
+ const void* quick_generic_jni_trampoline;
+ const void* quick_to_interpreter_bridge_trampoline;
+ size_t pointer_size;
+ ArtMethod* m;
+ bool error;
+};
+static void CheckTrampolines(mirror::Object* obj, void* arg) NO_THREAD_SAFETY_ANALYSIS {
+ if (obj->IsClass()) {
+ mirror::Class* klass = obj->AsClass();
+ TrampolineCheckData* d = reinterpret_cast<TrampolineCheckData*>(arg);
+ for (ArtMethod& m : klass->GetMethods(d->pointer_size)) {
+ const void* entrypoint = m.GetEntryPointFromQuickCompiledCodePtrSize(d->pointer_size);
+ if (entrypoint == d->quick_resolution_trampoline ||
+ entrypoint == d->quick_imt_conflict_trampoline ||
+ entrypoint == d->quick_generic_jni_trampoline ||
+ entrypoint == d->quick_to_interpreter_bridge_trampoline) {
+ d->m = &m;
+ d->error = true;
+ return;
+ }
+ }
+ }
+}
+
bool ClassLinker::InitFromImage(std::string* error_msg) {
VLOG(startup) << "ClassLinker::InitFromImage entering";
CHECK(!init_done_);
@@ -863,28 +897,71 @@
Runtime* const runtime = Runtime::Current();
Thread* const self = Thread::Current();
gc::Heap* const heap = runtime->GetHeap();
- gc::space::ImageSpace* const space = heap->GetBootImageSpace();
- CHECK(space != nullptr);
- image_pointer_size_ = space->GetImageHeader().GetPointerSize();
+ std::vector<gc::space::ImageSpace*> spaces = heap->GetBootImageSpaces();
+ CHECK(!spaces.empty());
+ image_pointer_size_ = spaces[0]->GetImageHeader().GetPointerSize();
dex_cache_boot_image_class_lookup_required_ = true;
- const OatFile* oat_file = runtime->GetOatFileManager().RegisterImageOatFile(space);
- DCHECK(oat_file != nullptr);
- CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0U);
- CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0U);
- const char* image_file_location = oat_file->GetOatHeader().
+ std::vector<const OatFile*> oat_files =
+ runtime->GetOatFileManager().RegisterImageOatFiles(spaces);
+ DCHECK(!oat_files.empty());
+ const OatHeader& default_oat_header = oat_files[0]->GetOatHeader();
+ CHECK_EQ(default_oat_header.GetImageFileLocationOatChecksum(), 0U);
+ CHECK_EQ(default_oat_header.GetImageFileLocationOatDataBegin(), 0U);
+ const char* image_file_location = oat_files[0]->GetOatHeader().
GetStoreValueByKey(OatHeader::kImageLocationKey);
CHECK(image_file_location == nullptr || *image_file_location == 0);
- quick_resolution_trampoline_ = oat_file->GetOatHeader().GetQuickResolutionTrampoline();
- quick_imt_conflict_trampoline_ = oat_file->GetOatHeader().GetQuickImtConflictTrampoline();
- quick_generic_jni_trampoline_ = oat_file->GetOatHeader().GetQuickGenericJniTrampoline();
- quick_to_interpreter_bridge_trampoline_ = oat_file->GetOatHeader().GetQuickToInterpreterBridge();
- StackHandleScope<2> hs(self);
- mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
- Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(
- hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>()));
+ quick_resolution_trampoline_ = default_oat_header.GetQuickResolutionTrampoline();
+ quick_imt_conflict_trampoline_ = default_oat_header.GetQuickImtConflictTrampoline();
+ quick_generic_jni_trampoline_ = default_oat_header.GetQuickGenericJniTrampoline();
+ quick_to_interpreter_bridge_trampoline_ = default_oat_header.GetQuickToInterpreterBridge();
+ if (kIsDebugBuild) {
+ // Check that the other images use the same trampoline.
+ for (size_t i = 1; i < oat_files.size(); ++i) {
+ const OatHeader& ith_oat_header = oat_files[i]->GetOatHeader();
+ const void* ith_quick_resolution_trampoline =
+ ith_oat_header.GetQuickResolutionTrampoline();
+ const void* ith_quick_imt_conflict_trampoline =
+ ith_oat_header.GetQuickImtConflictTrampoline();
+ const void* ith_quick_generic_jni_trampoline =
+ ith_oat_header.GetQuickGenericJniTrampoline();
+ const void* ith_quick_to_interpreter_bridge_trampoline =
+ ith_oat_header.GetQuickToInterpreterBridge();
+ if (ith_quick_resolution_trampoline != quick_resolution_trampoline_ ||
+ ith_quick_imt_conflict_trampoline != quick_imt_conflict_trampoline_ ||
+ ith_quick_generic_jni_trampoline != quick_generic_jni_trampoline_ ||
+ ith_quick_to_interpreter_bridge_trampoline != quick_to_interpreter_bridge_trampoline_) {
+ // Make sure that all methods in this image do not contain those trampolines as
+ // entrypoints. Otherwise the class-linker won't be able to work with a single set.
+ TrampolineCheckData data;
+ data.error = false;
+ data.pointer_size = GetImagePointerSize();
+ data.quick_resolution_trampoline = ith_quick_resolution_trampoline;
+ data.quick_imt_conflict_trampoline = ith_quick_imt_conflict_trampoline;
+ data.quick_generic_jni_trampoline = ith_quick_generic_jni_trampoline;
+ data.quick_to_interpreter_bridge_trampoline = ith_quick_to_interpreter_bridge_trampoline;
+ ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ spaces[i]->GetLiveBitmap()->Walk(CheckTrampolines, &data);
+ if (data.error) {
+ ArtMethod* m = data.m;
+ LOG(ERROR) << "Found a broken ArtMethod: " << PrettyMethod(m);
+ *error_msg = "Found an ArtMethod with a bad entrypoint";
+ return false;
+ }
+ }
+ }
+ }
- Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
- space->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)->
+ StackHandleScopeCollection handles(self);
+ std::vector<Handle<mirror::ObjectArray<mirror::DexCache>>> dex_caches_vector;
+ for (gc::space::ImageSpace* space : spaces) {
+ Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(handles.NewHandle(
+ space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches)->
+ AsObjectArray<mirror::DexCache>()));
+ dex_caches_vector.push_back(dex_caches);
+ }
+
+ Handle<mirror::ObjectArray<mirror::Class>> class_roots(handles.NewHandle(
+ spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)->
AsObjectArray<mirror::Class>()));
class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get());
@@ -896,56 +973,70 @@
java_lang_Object->SetObjectSize(sizeof(mirror::Object));
// Allocate in non-movable so that it's possible to check if a JNI weak global ref has been
// cleared without triggering the read barrier and unintentionally mark the sentinel alive.
- runtime->SetSentinel(heap->AllocNonMovableObject<true>(self,
- java_lang_Object,
- java_lang_Object->GetObjectSize(),
- VoidFunctor()));
+ runtime->SetSentinel(heap->AllocNonMovableObject<true>(
+ self, java_lang_Object, java_lang_Object->GetObjectSize(), VoidFunctor()));
- if (oat_file->GetOatHeader().GetDexFileCount() !=
- static_cast<uint32_t>(dex_caches->GetLength())) {
+ uint32_t dex_file_count = 0;
+ for (const OatFile* oat_file : oat_files) {
+ dex_file_count += oat_file->GetOatHeader().GetDexFileCount();
+ }
+ uint32_t dex_caches_count = 0;
+ for (auto dex_caches : dex_caches_vector) {
+ dex_caches_count += dex_caches->GetLength();
+ }
+ if (dex_file_count != dex_caches_count) {
*error_msg = "Dex cache count and dex file count mismatch while trying to initialize from "
"image";
return false;
}
- for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
- StackHandleScope<1> hs2(self);
- Handle<mirror::DexCache> dex_cache(hs2.NewHandle(dex_caches->Get(i)));
- const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file_location.c_str(),
- nullptr);
- if (oat_dex_file == nullptr) {
- *error_msg = StringPrintf("Failed finding oat dex file for %s %s",
- oat_file->GetLocation().c_str(),
- dex_file_location.c_str());
- return false;
- }
- std::string inner_error_msg;
- std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&inner_error_msg);
- if (dex_file == nullptr) {
- *error_msg = StringPrintf("Failed to open dex file %s from within oat file %s error '%s'",
- dex_file_location.c_str(),
- oat_file->GetLocation().c_str(),
- inner_error_msg.c_str());
- return false;
- }
+ for (auto dex_caches : dex_caches_vector) {
+ for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
+ StackHandleScope<1> hs2(self);
+ Handle<mirror::DexCache> dex_cache(hs2.NewHandle(dex_caches->Get(i)));
+ const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
+ const OatFile::OatDexFile* oat_dex_file = nullptr;
+ for (const OatFile* oat_file : oat_files) {
+ const OatFile::OatDexFile* oat_dex =
+ oat_file->GetOatDexFile(dex_file_location.c_str(), nullptr, false);
+ if (oat_dex != nullptr) {
+ DCHECK(oat_dex_file == nullptr);
+ oat_dex_file = oat_dex;
+ }
+ }
- if (kSanityCheckObjects) {
- SanityCheckArtMethodPointerArray(dex_cache->GetResolvedMethods(),
- dex_cache->NumResolvedMethods(),
- image_pointer_size_,
- space);
- }
+ if (oat_dex_file == nullptr) {
+ *error_msg = StringPrintf("Failed finding oat dex file for %s",
+ dex_file_location.c_str());
+ return false;
+ }
+ std::string inner_error_msg;
+ std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&inner_error_msg);
+ if (dex_file == nullptr) {
+ *error_msg = StringPrintf("Failed to open dex file %s error '%s'",
+ dex_file_location.c_str(),
+ inner_error_msg.c_str());
+ return false;
+ }
- if (dex_file->GetLocationChecksum() != oat_dex_file->GetDexFileLocationChecksum()) {
- *error_msg = StringPrintf("Checksums do not match for %s: %x vs %x",
- dex_file_location.c_str(),
- dex_file->GetLocationChecksum(),
- oat_dex_file->GetDexFileLocationChecksum());
- return false;
- }
+ // TODO: Modify check to support multiple image spaces and reenable.
+// if (kSanityCheckObjects) {
+// SanityCheckArtMethodPointerArray(dex_cache->GetResolvedMethods(),
+// dex_cache->NumResolvedMethods(),
+// image_pointer_size_,
+// spaces);
+// }
- AppendToBootClassPath(*dex_file.get(), dex_cache);
- opened_dex_files_.push_back(std::move(dex_file));
+ if (dex_file->GetLocationChecksum() != oat_dex_file->GetDexFileLocationChecksum()) {
+ *error_msg = StringPrintf("Checksums do not match for %s: %x vs %x",
+ dex_file_location.c_str(),
+ dex_file->GetLocationChecksum(),
+ oat_dex_file->GetDexFileLocationChecksum());
+ return false;
+ }
+
+ AppendToBootClassPath(*dex_file.get(), dex_cache);
+ opened_dex_files_.push_back(std::move(dex_file));
+ }
}
if (!ValidPointerSize(image_pointer_size_)) {
@@ -968,12 +1059,14 @@
}
if (kSanityCheckObjects) {
- for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
- auto* dex_cache = dex_caches->Get(i);
- for (size_t j = 0; j < dex_cache->NumResolvedFields(); ++j) {
- auto* field = dex_cache->GetResolvedField(j, image_pointer_size_);
- if (field != nullptr) {
- CHECK(field->GetDeclaringClass()->GetClass() != nullptr);
+ for (auto dex_caches : dex_caches_vector) {
+ for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
+ auto* dex_cache = dex_caches->Get(i);
+ for (size_t j = 0; j < dex_cache->NumResolvedFields(); ++j) {
+ auto* field = dex_cache->GetResolvedField(j, image_pointer_size_);
+ if (field != nullptr) {
+ CHECK(field->GetDeclaringClass()->GetClass() != nullptr);
+ }
}
}
}
@@ -982,10 +1075,12 @@
// Set entry point to interpreter if in InterpretOnly mode.
if (!runtime->IsAotCompiler() && runtime->GetInstrumentation()->InterpretOnly()) {
- const ImageHeader& header = space->GetImageHeader();
- const ImageSection& methods = header.GetMethodsSection();
- SetInterpreterEntrypointArtMethodVisitor visitor(image_pointer_size_);
- methods.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_);
+ for (gc::space::ImageSpace* space : spaces) {
+ const ImageHeader& header = space->GetImageHeader();
+ const ImageSection& methods = header.GetMethodsSection();
+ SetInterpreterEntrypointArtMethodVisitor visitor(image_pointer_size_);
+ methods.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_);
+ }
}
// reinit class_roots_
@@ -1014,13 +1109,15 @@
mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable));
mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
- const ImageHeader& header = space->GetImageHeader();
- const ImageSection& section = header.GetImageSection(ImageHeader::kSectionClassTable);
- if (section.Size() > 0u) {
- WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
- ClassTable* const class_table = InsertClassTableForClassLoader(nullptr);
- class_table->ReadFromMemory(space->Begin() + section.Offset());
- dex_cache_boot_image_class_lookup_required_ = false;
+ for (gc::space::ImageSpace* space : spaces) {
+ const ImageHeader& header = space->GetImageHeader();
+ const ImageSection& section = header.GetImageSection(ImageHeader::kSectionClassTable);
+ if (section.Size() > 0u) {
+ WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
+ ClassTable* const class_table = InsertClassTableForClassLoader(nullptr);
+ class_table->ReadFromMemory(space->Begin() + section.Offset());
+ dex_cache_boot_image_class_lookup_required_ = false;
+ }
}
FinishInit(self);
@@ -1974,7 +2071,7 @@
}
Runtime* runtime = Runtime::Current();
if (!runtime->IsStarted()) {
- if (runtime->IsAotCompiler() || runtime->GetHeap()->HasImageSpace()) {
+ if (runtime->IsAotCompiler() || runtime->GetHeap()->HasBootImageSpace()) {
return; // OAT file unavailable.
}
}
@@ -2783,23 +2880,27 @@
return result;
}
-static mirror::ObjectArray<mirror::DexCache>* GetImageDexCaches(gc::space::ImageSpace* image_space)
- SHARED_REQUIRES(Locks::mutator_lock_) {
- CHECK(image_space != nullptr);
- mirror::Object* root = image_space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
- DCHECK(root != nullptr);
- return root->AsObjectArray<mirror::DexCache>();
+static std::vector<mirror::ObjectArray<mirror::DexCache>*> GetImageDexCaches(
+ std::vector<gc::space::ImageSpace*> image_spaces) SHARED_REQUIRES(Locks::mutator_lock_) {
+ CHECK(!image_spaces.empty());
+ std::vector<mirror::ObjectArray<mirror::DexCache>*> dex_caches_vector;
+ for (gc::space::ImageSpace* image_space : image_spaces) {
+ mirror::Object* root = image_space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
+ DCHECK(root != nullptr);
+ dex_caches_vector.push_back(root->AsObjectArray<mirror::DexCache>());
+ }
+ return dex_caches_vector;
}
void ClassLinker::AddBootImageClassesToClassTable() {
if (dex_cache_boot_image_class_lookup_required_) {
- AddImageClassesToClassTable(Runtime::Current()->GetHeap()->GetBootImageSpace(),
+ AddImageClassesToClassTable(Runtime::Current()->GetHeap()->GetBootImageSpaces(),
/*class_loader*/nullptr);
dex_cache_boot_image_class_lookup_required_ = false;
}
}
-void ClassLinker::AddImageClassesToClassTable(gc::space::ImageSpace* image_space,
+void ClassLinker::AddImageClassesToClassTable(std::vector<gc::space::ImageSpace*> image_spaces,
mirror::ClassLoader* class_loader) {
Thread* self = Thread::Current();
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
@@ -2807,25 +2908,28 @@
ClassTable* const class_table = InsertClassTableForClassLoader(class_loader);
- mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches(image_space);
std::string temp;
- for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
- mirror::DexCache* dex_cache = dex_caches->Get(i);
- GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes();
- for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
- mirror::Class* klass = types[j].Read();
- if (klass != nullptr) {
- DCHECK_EQ(klass->GetClassLoader(), class_loader);
- const char* descriptor = klass->GetDescriptor(&temp);
- size_t hash = ComputeModifiedUtf8Hash(descriptor);
- mirror::Class* existing = class_table->Lookup(descriptor, hash);
- if (existing != nullptr) {
- CHECK_EQ(existing, klass) << PrettyClassAndClassLoader(existing) << " != "
- << PrettyClassAndClassLoader(klass);
- } else {
- class_table->Insert(klass);
- if (log_new_class_table_roots_) {
- new_class_roots_.push_back(GcRoot<mirror::Class>(klass));
+ std::vector<mirror::ObjectArray<mirror::DexCache>*> dex_caches_vector =
+ GetImageDexCaches(image_spaces);
+ for (mirror::ObjectArray<mirror::DexCache>* dex_caches : dex_caches_vector) {
+ for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
+ mirror::DexCache* dex_cache = dex_caches->Get(i);
+ GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes();
+ for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
+ mirror::Class* klass = types[j].Read();
+ if (klass != nullptr) {
+ DCHECK_EQ(klass->GetClassLoader(), class_loader);
+ const char* descriptor = klass->GetDescriptor(&temp);
+ size_t hash = ComputeModifiedUtf8Hash(descriptor);
+ mirror::Class* existing = class_table->Lookup(descriptor, hash);
+ if (existing != nullptr) {
+ CHECK_EQ(existing, klass) << PrettyClassAndClassLoader(existing) << " != "
+ << PrettyClassAndClassLoader(klass);
+ } else {
+ class_table->Insert(klass);
+ if (log_new_class_table_roots_) {
+ new_class_roots_.push_back(GcRoot<mirror::Class>(klass));
+ }
}
}
}
@@ -2856,18 +2960,20 @@
mirror::Class* ClassLinker::LookupClassFromBootImage(const char* descriptor) {
ScopedAssertNoThreadSuspension ants(Thread::Current(), "Image class lookup");
- mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches(
- Runtime::Current()->GetHeap()->GetBootImageSpace());
- for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
- mirror::DexCache* dex_cache = dex_caches->Get(i);
- const DexFile* dex_file = dex_cache->GetDexFile();
- // Try binary searching the type index by descriptor.
- const DexFile::TypeId* type_id = dex_file->FindTypeId(descriptor);
- if (type_id != nullptr) {
- uint16_t type_idx = dex_file->GetIndexForTypeId(*type_id);
- mirror::Class* klass = dex_cache->GetResolvedType(type_idx);
- if (klass != nullptr) {
- return klass;
+ std::vector<mirror::ObjectArray<mirror::DexCache>*> dex_caches_vector =
+ GetImageDexCaches(Runtime::Current()->GetHeap()->GetBootImageSpaces());
+ for (mirror::ObjectArray<mirror::DexCache>* dex_caches : dex_caches_vector) {
+ for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
+ mirror::DexCache* dex_cache = dex_caches->Get(i);
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ // Try binary searching the type index by descriptor.
+ const DexFile::TypeId* type_id = dex_file->FindTypeId(descriptor);
+ if (type_id != nullptr) {
+ uint16_t type_idx = dex_file->GetIndexForTypeId(*type_id);
+ mirror::Class* klass = dex_cache->GetResolvedType(type_idx);
+ if (klass != nullptr) {
+ return klass;
+ }
}
}
}
@@ -3167,7 +3273,7 @@
// the runtime isn't started. On the other hand, app classes can be re-verified even if they are
// already pre-opted, as then the runtime is started.
if (!Runtime::Current()->IsAotCompiler() &&
- !Runtime::Current()->GetHeap()->HasImageSpace() &&
+ !Runtime::Current()->GetHeap()->HasBootImageSpace() &&
klass->GetClassLoader() != nullptr) {
return false;
}
@@ -3441,7 +3547,7 @@
void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* out) {
// Create constructor for Proxy that must initialize the method.
- CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 16u);
+ CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 19u);
ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)->GetDirectMethodUnchecked(
2, image_pointer_size_);
// Ensure constructor is in dex cache so that we can use the dex cache to look up the overridden
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index f16fe92..9d432c6 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -505,7 +505,7 @@
SHARED_REQUIRES(Locks::mutator_lock_);
// Add image classes to the class table.
- void AddImageClassesToClassTable(gc::space::ImageSpace* image_space,
+ void AddImageClassesToClassTable(std::vector<gc::space::ImageSpace*> image_spaces,
mirror::ClassLoader* class_loader)
REQUIRES(!Locks::classlinker_classes_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 59a43ee..99353c5 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -501,6 +501,7 @@
struct ClassOffsets : public CheckOffsets<mirror::Class> {
ClassOffsets() : CheckOffsets<mirror::Class>(false, "Ljava/lang/Class;") {
addOffset(OFFSETOF_MEMBER(mirror::Class, access_flags_), "accessFlags");
+ addOffset(OFFSETOF_MEMBER(mirror::Class, annotation_type_), "annotationType");
addOffset(OFFSETOF_MEMBER(mirror::Class, class_flags_), "classFlags");
addOffset(OFFSETOF_MEMBER(mirror::Class, class_loader_), "classLoader");
addOffset(OFFSETOF_MEMBER(mirror::Class, class_size_), "classSize");
@@ -535,15 +536,15 @@
struct StringOffsets : public CheckOffsets<mirror::String> {
StringOffsets() : CheckOffsets<mirror::String>(false, "Ljava/lang/String;") {
addOffset(OFFSETOF_MEMBER(mirror::String, count_), "count");
- addOffset(OFFSETOF_MEMBER(mirror::String, hash_code_), "hashCode");
+ addOffset(OFFSETOF_MEMBER(mirror::String, hash_code_), "hash");
};
};
struct ThrowableOffsets : public CheckOffsets<mirror::Throwable> {
ThrowableOffsets() : CheckOffsets<mirror::Throwable>(false, "Ljava/lang/Throwable;") {
+ addOffset(OFFSETOF_MEMBER(mirror::Throwable, backtrace_), "backtrace");
addOffset(OFFSETOF_MEMBER(mirror::Throwable, cause_), "cause");
addOffset(OFFSETOF_MEMBER(mirror::Throwable, detail_message_), "detailMessage");
- addOffset(OFFSETOF_MEMBER(mirror::Throwable, stack_state_), "stackState");
addOffset(OFFSETOF_MEMBER(mirror::Throwable, stack_trace_), "stackTrace");
addOffset(OFFSETOF_MEMBER(mirror::Throwable, suppressed_exceptions_), "suppressedExceptions");
};
@@ -612,7 +613,7 @@
struct AccessibleObjectOffsets : public CheckOffsets<mirror::AccessibleObject> {
AccessibleObjectOffsets() : CheckOffsets<mirror::AccessibleObject>(
false, "Ljava/lang/reflect/AccessibleObject;") {
- addOffset(mirror::AccessibleObject::FlagOffset().Uint32Value(), "flag");
+ addOffset(mirror::AccessibleObject::FlagOffset().Uint32Value(), "override");
};
};
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 2640a6e..403dd4c 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -303,7 +303,12 @@
RuntimeOptions options;
- std::string boot_class_path_string = "-Xbootclasspath:" + GetLibCoreDexFileName();
+ std::string boot_class_path_string = "-Xbootclasspath";
+ for (const std::string &core_dex_file_name : GetLibCoreDexFileNames()) {
+ boot_class_path_string += ":";
+ boot_class_path_string += core_dex_file_name;
+ }
+
options.push_back(std::make_pair(boot_class_path_string, nullptr));
options.push_back(std::make_pair("-Xcheck:jni", nullptr));
options.push_back(std::make_pair(min_heap_string, nullptr));
@@ -409,10 +414,30 @@
(*icu_cleanup_fn)();
Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test
+
+ // Manually closing the JNI libraries.
+ // Runtime does not support repeatedly doing JNI->CreateVM, thus we need to manually clean up the
+ // dynamic linking loader so that gtests would not fail.
+ // Bug: 25785594
+ if (runtime_->IsStarted()) {
+ {
+ // We retrieve the handle by calling dlopen on the library. To close it, we need to call
+ // dlclose twice, the first time to undo our dlopen and the second time to actually unload it.
+ // See man dlopen.
+ void* handle = dlopen("libjavacore.so", RTLD_LAZY);
+ dlclose(handle);
+ CHECK_EQ(0, dlclose(handle));
+ }
+ {
+ void* handle = dlopen("libopenjdk.so", RTLD_LAZY);
+ dlclose(handle);
+ CHECK_EQ(0, dlclose(handle));
+ }
+ }
}
-std::string CommonRuntimeTest::GetLibCoreDexFileName() {
- return GetDexFileName("core-libart");
+std::vector<std::string> CommonRuntimeTest::GetLibCoreDexFileNames() {
+ return std::vector<std::string>({GetDexFileName("core-oj"), GetDexFileName("core-libart")});
}
std::string CommonRuntimeTest::GetDexFileName(const std::string& jar_prefix) {
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index f318457..8d9e628 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -77,8 +77,8 @@
CommonRuntimeTest();
~CommonRuntimeTest();
- // Gets the path of the libcore dex file.
- static std::string GetLibCoreDexFileName();
+ // Gets the paths of the libcore dex files.
+ static std::vector<std::string> GetLibCoreDexFileNames();
// Returns bin directory which contains host's prebuild tools.
static std::string GetAndroidHostToolsDir();
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index c32331f..e0211f5 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -2033,29 +2033,28 @@
SHARED_REQUIRES(Locks::mutator_lock_) {
CHECK(thread_group != nullptr);
- // Get the ArrayList<ThreadGroup> "groups" out of this thread group...
- ArtField* groups_field = soa.DecodeField(WellKnownClasses::java_lang_ThreadGroup_groups);
- mirror::Object* groups_array_list = groups_field->GetObject(thread_group);
- {
- // The "groups" field is declared as a java.util.List: check it really is
- // an instance of java.util.ArrayList.
- CHECK(groups_array_list != nullptr);
- mirror::Class* java_util_ArrayList_class =
- soa.Decode<mirror::Class*>(WellKnownClasses::java_util_ArrayList);
- CHECK(groups_array_list->InstanceOf(java_util_ArrayList_class));
+ // Get the int "ngroups" count of this thread group...
+ ArtField* ngroups_field = soa.DecodeField(WellKnownClasses::java_lang_ThreadGroup_ngroups);
+ CHECK(ngroups_field != nullptr);
+ const int32_t size = ngroups_field->GetInt(thread_group);
+ if (size == 0) {
+ return;
}
- // Get the array and size out of the ArrayList<ThreadGroup>...
- ArtField* array_field = soa.DecodeField(WellKnownClasses::java_util_ArrayList_array);
- ArtField* size_field = soa.DecodeField(WellKnownClasses::java_util_ArrayList_size);
- mirror::ObjectArray<mirror::Object>* groups_array =
- array_field->GetObject(groups_array_list)->AsObjectArray<mirror::Object>();
- const int32_t size = size_field->GetInt(groups_array_list);
+ // Get the ThreadGroup[] "groups" out of this thread group...
+ ArtField* groups_field = soa.DecodeField(WellKnownClasses::java_lang_ThreadGroup_groups);
+ mirror::Object* groups_array = groups_field->GetObject(thread_group);
+
+ CHECK(groups_array != nullptr);
+ CHECK(groups_array->IsObjectArray());
+
+ mirror::ObjectArray<mirror::Object>* groups_array_as_array =
+ groups_array->AsObjectArray<mirror::Object>();
// Copy the first 'size' elements out of the array into the result.
ObjectRegistry* registry = Dbg::GetObjectRegistry();
for (int32_t i = 0; i < size; ++i) {
- child_thread_group_ids->push_back(registry->Add(groups_array->Get(i)));
+ child_thread_group_ids->push_back(registry->Add(groups_array_as_array->Get(i)));
}
}
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 0a167bb..796701d 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -206,7 +206,7 @@
uint32_t checksum;
ScopedObjectAccess soa(Thread::Current());
std::string error_msg;
- EXPECT_TRUE(DexFile::GetChecksum(GetLibCoreDexFileName().c_str(), &checksum, &error_msg))
+ EXPECT_TRUE(DexFile::GetChecksum(GetLibCoreDexFileNames()[0].c_str(), &checksum, &error_msg))
<< error_msg;
EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksum);
}
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index 8f7bb94..d16afd9 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -487,7 +487,9 @@
// Mark all references to the alloc space(s).
void ModUnionTableCardCache::UpdateAndMarkReferences(MarkObjectVisitor* visitor) {
- auto* image_space = heap_->GetBootImageSpace();
+ // TODO: Needs better support for multi-images? b/26317072
+ space::ImageSpace* image_space =
+ heap_->GetBootImageSpaces().empty() ? nullptr : heap_->GetBootImageSpaces()[0];
// If we don't have an image space, just pass in space_ as the immune space. Pass in the same
// space_ instead of image_space to avoid a null check in ModUnionUpdateObjectReferencesVisitor.
CardBitVisitor bit_visitor(visitor, space_, image_space != nullptr ? image_space : space_,
diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h
index 3be7181..61c67f8 100644
--- a/runtime/gc/accounting/space_bitmap-inl.h
+++ b/runtime/gc/accounting/space_bitmap-inl.h
@@ -167,7 +167,10 @@
uintptr_t* address = &bitmap_begin_[index];
uintptr_t old_word = *address;
if (kSetBit) {
- *address = old_word | mask;
+ if ((old_word & mask) == 0) {
+ // Avoid dirtying the page if possible.
+ *address = old_word | mask;
+ }
} else {
*address = old_word & ~mask;
}
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 6d72f31..3e432c7 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -233,8 +233,7 @@
backtrace_lock_(nullptr),
seen_backtrace_count_(0u),
unique_backtrace_count_(0u),
- gc_disabled_for_shutdown_(false),
- boot_image_space_(nullptr) {
+ gc_disabled_for_shutdown_(false) {
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
LOG(INFO) << "Heap() entering";
}
@@ -260,23 +259,107 @@
CHECK_GE(300 * MB, non_moving_space_capacity);
requested_alloc_space_begin = reinterpret_cast<uint8_t*>(300 * MB) - non_moving_space_capacity;
}
+
+ // Load image space(s).
if (!image_file_name.empty()) {
- ATRACE_BEGIN("ImageSpace::Create");
- std::string error_msg;
- boot_image_space_ = space::ImageSpace::Create(image_file_name.c_str(),
- image_instruction_set,
- &error_msg);
- ATRACE_END();
- if (boot_image_space_ != nullptr) {
- AddSpace(boot_image_space_);
- // Oat files referenced by image files immediately follow them in memory, ensure alloc space
- // isn't going to get in the middle
- uint8_t* oat_file_end_addr = boot_image_space_->GetImageHeader().GetOatFileEnd();
- CHECK_GT(oat_file_end_addr, boot_image_space_->End());
- requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize);
- } else {
- LOG(ERROR) << "Could not create image space with image file '" << image_file_name << "'. "
- << "Attempting to fall back to imageless running. Error was: " << error_msg;
+ // For code reuse, handle this like a work queue.
+ std::vector<std::string> image_file_names;
+ image_file_names.push_back(image_file_name);
+
+ for (size_t index = 0; index < image_file_names.size(); ++index) {
+ std::string& image_name = image_file_names[index];
+ ATRACE_BEGIN("ImageSpace::Create");
+ std::string error_msg;
+ space::ImageSpace* boot_image_space = space::ImageSpace::Create(image_name.c_str(),
+ image_instruction_set,
+ index > 0,
+ &error_msg);
+ ATRACE_END();
+ if (boot_image_space != nullptr) {
+ AddSpace(boot_image_space);
+ // Oat files referenced by image files immediately follow them in memory, ensure alloc space
+ // isn't going to get in the middle
+ uint8_t* oat_file_end_addr = boot_image_space->GetImageHeader().GetOatFileEnd();
+ CHECK_GT(oat_file_end_addr, boot_image_space->End());
+ requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize);
+ boot_image_spaces_.push_back(boot_image_space);
+
+ if (index == 0) {
+ // If this was the first space, check whether there are more images to load.
+ const OatFile* boot_oat_file = boot_image_space->GetOatFile();
+ if (boot_oat_file == nullptr) {
+ continue;
+ }
+
+ const OatHeader& boot_oat_header = boot_oat_file->GetOatHeader();
+ const char* boot_classpath =
+ boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPath);
+ if (boot_classpath == nullptr) {
+ continue;
+ }
+
+ std::vector<std::string> images;
+ Split(boot_classpath, ':', &images);
+
+ // Add the rest into the list. We have to adjust locations, possibly:
+ //
+ // For example, image_file_name is /a/b/c/d/e.art
+ // images[0] is f/c/d/e.art
+ // ----------------------------------------------
+ // images[1] is g/h/i/j.art -> /a/b/h/i/j.art
+
+ // Derive pattern.
+ std::vector<std::string> left;
+ Split(image_file_name, '/', &left);
+ std::vector<std::string> right;
+ Split(images[0], '/', &right);
+
+ size_t common = 1;
+ while (common < left.size() && common < right.size()) {
+ if (left[left.size() - common - 1] != right[right.size() - common - 1]) {
+ break;
+ }
+ common++;
+ }
+
+ std::vector<std::string> prefix_vector(left.begin(), left.end() - common);
+ std::string common_prefix = Join(prefix_vector, '/');
+ if (!common_prefix.empty() && common_prefix[0] != '/' && image_file_name[0] == '/') {
+ common_prefix = "/" + common_prefix;
+ }
+
+ // Apply pattern to images[1] .. images[n].
+ for (size_t i = 1; i < images.size(); ++i) {
+ std::string image = images[i];
+
+ size_t rslash = std::string::npos;
+ for (size_t j = 0; j < common; ++j) {
+ if (rslash != std::string::npos) {
+ rslash--;
+ }
+
+ rslash = image.rfind('/', rslash);
+ if (rslash == std::string::npos) {
+ rslash = 0;
+ }
+ if (rslash == 0) {
+ break;
+ }
+ }
+ std::string image_part = image.substr(rslash);
+
+ std::string new_image = common_prefix + (StartsWith(image_part, "/") ? "" : "/") +
+ image_part;
+ image_file_names.push_back(new_image);
+ }
+ }
+ } else {
+ LOG(ERROR) << "Could not create image space with image file '" << image_file_name << "'. "
+ << "Attempting to fall back to imageless running. Error was: " << error_msg
+ << "\nAttempted image: " << image_name;
+ // TODO: Remove already loaded spaces.
+ break;
+ }
}
}
/*
@@ -456,13 +539,15 @@
rb_table_.reset(new accounting::ReadBarrierTable());
DCHECK(rb_table_->IsAllCleared());
}
- if (GetBootImageSpace() != nullptr) {
+ if (HasBootImageSpace()) {
// Don't add the image mod union table if we are running without an image, this can crash if
// we use the CardCache implementation.
- accounting::ModUnionTable* mod_union_table = new accounting::ModUnionTableToZygoteAllocspace(
- "Image mod-union table", this, GetBootImageSpace());
- CHECK(mod_union_table != nullptr) << "Failed to create image mod-union table";
- AddModUnionTable(mod_union_table);
+ for (space::ImageSpace* image_space : GetBootImageSpaces()) {
+ accounting::ModUnionTable* mod_union_table = new accounting::ModUnionTableToZygoteAllocspace(
+ "Image mod-union table", this, image_space);
+ CHECK(mod_union_table != nullptr) << "Failed to create image mod-union table";
+ AddModUnionTable(mod_union_table);
+ }
}
if (collector::SemiSpace::kUseRememberedSet && non_moving_space_ != main_space_) {
accounting::RememberedSet* non_moving_space_rem_set =
@@ -525,11 +610,12 @@
garbage_collectors_.push_back(mark_compact_collector_);
}
}
- if (GetBootImageSpace() != nullptr && non_moving_space_ != nullptr &&
+ if (!GetBootImageSpaces().empty() && non_moving_space_ != nullptr &&
(is_zygote || separate_non_moving_space || foreground_collector_type_ == kCollectorTypeGSS)) {
// Check that there's no gap between the image space and the non moving space so that the
// immune region won't break (eg. due to a large object allocated in the gap). This is only
// required when we're the zygote or using GSS.
+ /* TODO: Modify this check to support multi-images. b/26317072
bool no_gap = MemMap::CheckNoGaps(GetBootImageSpace()->GetMemMap(),
non_moving_space_->GetMemMap());
if (!no_gap) {
@@ -537,6 +623,7 @@
MemMap::DumpMaps(LOG(ERROR), true);
LOG(FATAL) << "There's a gap between the image space and the non-moving space";
}
+ */
}
instrumentation::Instrumentation* const instrumentation = runtime->GetInstrumentation();
if (gc_stress_mode_) {
@@ -1202,8 +1289,8 @@
return FindDiscontinuousSpaceFromObject(obj, fail_ok);
}
-space::ImageSpace* Heap::GetBootImageSpace() const {
- return boot_image_space_;
+std::vector<space::ImageSpace*> Heap::GetBootImageSpaces() const {
+ return boot_image_spaces_;
}
void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index e23b1a3..e7ea983 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -580,9 +580,8 @@
// Unbind any bound bitmaps.
void UnBindBitmaps() REQUIRES(Locks::heap_bitmap_lock_);
- // Returns the boot image space. There may be multiple image spaces, but there is only one boot
- // image space.
- space::ImageSpace* GetBootImageSpace() const;
+ // Returns the boot image spaces. There may be multiple boot image spaces.
+ std::vector<space::ImageSpace*> GetBootImageSpaces() const;
// Permenantly disable moving garbage collection.
void DisableMovingGc() REQUIRES(!*gc_complete_lock_);
@@ -660,8 +659,8 @@
void RemoveRememberedSet(space::Space* space);
bool IsCompilingBoot() const;
- bool HasImageSpace() const {
- return boot_image_space_ != nullptr;
+ bool HasBootImageSpace() const {
+ return !boot_image_spaces_.empty();
}
ReferenceProcessor* GetReferenceProcessor() {
@@ -1322,8 +1321,8 @@
// allocating.
bool gc_disabled_for_shutdown_ GUARDED_BY(gc_complete_lock_);
- // Boot image space.
- space::ImageSpace* boot_image_space_;
+ // Boot image spaces.
+ std::vector<space::ImageSpace*> boot_image_spaces_;
friend class CollectorTransitionTask;
friend class collector::GarbageCollector;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 8f67c21..952759c 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -43,12 +43,17 @@
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,
- uint8_t* end)
+ImageSpace::ImageSpace(const std::string& image_filename,
+ const char* image_location,
+ MemMap* mem_map,
+ accounting::ContinuousSpaceBitmap* live_bitmap,
+ uint8_t* end,
+ MemMap* shadow_map)
: MemMapSpace(image_filename, mem_map, mem_map->Begin(), end, end,
kGcRetentionPolicyNeverCollect),
- image_location_(image_location) {
+ oat_file_non_owned_(nullptr),
+ image_location_(image_location),
+ shadow_map_(shadow_map) {
DCHECK(live_bitmap != nullptr);
live_bitmap_.reset(live_bitmap);
}
@@ -470,6 +475,7 @@
ImageSpace* ImageSpace::Create(const char* image_location,
const InstructionSet image_isa,
+ bool secondary_image,
std::string* error_msg) {
std::string system_filename;
bool has_system = false;
@@ -481,7 +487,7 @@
&has_system, &cache_filename, &dalvik_cache_exists,
&has_cache, &is_global_cache);
- if (Runtime::Current()->IsZygote()) {
+ if (Runtime::Current()->IsZygote() && !secondary_image) {
MarkZygoteStart(image_isa, Runtime::Current()->GetZygoteMaxFailedBoots());
}
@@ -686,7 +692,7 @@
return nullptr;
}
- if (kIsDebugBuild) {
+ if (VLOG_IS_ON(startup)) {
LOG(INFO) << "Dumping image sections";
for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
const auto section_idx = static_cast<ImageHeader::ImageSections>(i);
@@ -799,11 +805,52 @@
return nullptr;
}
+ // In case of multi-images, the images are spaced apart so that the bitmaps don't overlap. We
+ // need to reserve the slack, as otherwise the large object space might allocate in there.
+ // TODO: Reconsider the multi-image layout. b/26317072
+ std::unique_ptr<MemMap> shadow_map;
+ {
+ uintptr_t image_begin = reinterpret_cast<uintptr_t>(image_header.GetImageBegin());
+ uintptr_t image_end = RoundUp(image_begin + image_header.GetImageSize(), kPageSize);
+ uintptr_t oat_begin = reinterpret_cast<uintptr_t>(image_header.GetOatFileBegin());
+ if (image_end < oat_begin) {
+ // There's a gap. Could be multi-image, could be the oat file spaced apart. Go ahead and
+ // dummy-reserve the space covered by the bitmap (which will be a shadow that introduces
+ // a gap to the next image).
+ uintptr_t heap_size = bitmap->HeapSize();
+ uintptr_t bitmap_coverage_end = RoundUp(image_begin + heap_size, kPageSize);
+ if (bitmap_coverage_end > image_end) {
+ VLOG(startup) << "Reserving bitmap shadow ["
+ << std::hex << image_end << ";"
+ << std::hex << bitmap_coverage_end << ";] (oat file begins at "
+ << std::hex << oat_begin;
+ // Note: we cannot use MemMap::Dummy here, as that won't reserve the space in 32-bit mode.
+ shadow_map.reset(MemMap::MapAnonymous("Image bitmap shadow",
+ reinterpret_cast<uint8_t*>(image_end),
+ bitmap_coverage_end - image_end,
+ PROT_NONE,
+ false,
+ false,
+ error_msg));
+ if (shadow_map == nullptr) {
+ return nullptr;
+ }
+ // madvise it away, we don't really want it, just reserve the address space.
+ // TODO: Should we use MadviseDontNeedAndZero? b/26317072
+ madvise(shadow_map->BaseBegin(), shadow_map->BaseSize(), MADV_DONTNEED);
+ }
+ }
+ }
+
// We only want the mirror object, not the ArtFields and ArtMethods.
uint8_t* const image_end =
map->Begin() + image_header.GetImageSection(ImageHeader::kSectionObjects).End();
- std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename, image_location,
- map.release(), bitmap.release(), image_end));
+ std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename,
+ image_location,
+ map.release(),
+ bitmap.release(),
+ image_end,
+ shadow_map.release()));
// VerifyImageAllocations() will be called later in Runtime::Init()
// as some class roots like ArtMethod::java_lang_reflect_ArtMethod_
@@ -826,16 +873,18 @@
Runtime* runtime = Runtime::Current();
runtime->SetInstructionSet(space->oat_file_->GetOatHeader().GetInstructionSet());
- runtime->SetResolutionMethod(image_header.GetImageMethod(ImageHeader::kResolutionMethod));
- runtime->SetImtConflictMethod(image_header.GetImageMethod(ImageHeader::kImtConflictMethod));
- runtime->SetImtUnimplementedMethod(
- image_header.GetImageMethod(ImageHeader::kImtUnimplementedMethod));
- runtime->SetCalleeSaveMethod(
- image_header.GetImageMethod(ImageHeader::kCalleeSaveMethod), Runtime::kSaveAll);
- runtime->SetCalleeSaveMethod(
- image_header.GetImageMethod(ImageHeader::kRefsOnlySaveMethod), Runtime::kRefsOnly);
- runtime->SetCalleeSaveMethod(
- image_header.GetImageMethod(ImageHeader::kRefsAndArgsSaveMethod), Runtime::kRefsAndArgs);
+ if (!runtime->HasResolutionMethod()) {
+ runtime->SetResolutionMethod(image_header.GetImageMethod(ImageHeader::kResolutionMethod));
+ runtime->SetImtConflictMethod(image_header.GetImageMethod(ImageHeader::kImtConflictMethod));
+ runtime->SetImtUnimplementedMethod(
+ image_header.GetImageMethod(ImageHeader::kImtUnimplementedMethod));
+ runtime->SetCalleeSaveMethod(
+ image_header.GetImageMethod(ImageHeader::kCalleeSaveMethod), Runtime::kSaveAll);
+ runtime->SetCalleeSaveMethod(
+ image_header.GetImageMethod(ImageHeader::kRefsOnlySaveMethod), Runtime::kRefsOnly);
+ runtime->SetCalleeSaveMethod(
+ image_header.GetImageMethod(ImageHeader::kRefsAndArgsSaveMethod), Runtime::kRefsAndArgs);
+ }
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
LOG(INFO) << "ImageSpace::Init exiting (" << PrettyDuration(NanoTime() - start_time)
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index babd672..a54358a 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -43,7 +43,10 @@
// creation of the alloc space. The ReleaseOatFile will later be
// used to transfer ownership of the OatFile to the ClassLinker when
// it is initialized.
- static ImageSpace* Create(const char* image, InstructionSet image_isa, std::string* error_msg)
+ static ImageSpace* Create(const char* image,
+ InstructionSet image_isa,
+ bool secondary_image,
+ std::string* error_msg)
SHARED_REQUIRES(Locks::mutator_lock_);
// Reads the image header from the specified image location for the
@@ -158,8 +161,12 @@
std::unique_ptr<accounting::ContinuousSpaceBitmap> live_bitmap_;
- ImageSpace(const std::string& name, const char* image_location,
- MemMap* mem_map, accounting::ContinuousSpaceBitmap* live_bitmap, uint8_t* end);
+ ImageSpace(const std::string& name,
+ const char* image_location,
+ MemMap* mem_map,
+ accounting::ContinuousSpaceBitmap* live_bitmap,
+ uint8_t* end,
+ MemMap* shadow_map = nullptr);
// The OatFile associated with the image during early startup to
// reserve space contiguous to the image. It is later released to
@@ -172,6 +179,10 @@
const std::string image_location_;
+ // A MemMap reserving the space of the bitmap "shadow," so that we don't allocate into it. Only
+ // used in the multi-image case.
+ std::unique_ptr<MemMap> shadow_map_;
+
private:
DISALLOW_COPY_AND_ASSIGN(ImageSpace);
};
diff --git a/runtime/image.cc b/runtime/image.cc
index f8f930b..3856787 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -55,7 +55,6 @@
CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize));
CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize));
CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kPageSize));
- CHECK_LT(image_begin, image_roots);
CHECK_LT(image_roots, oat_file_begin);
CHECK_LE(oat_file_begin, oat_data_begin);
CHECK_LT(oat_data_begin, oat_data_end);
@@ -100,9 +99,6 @@
if (oat_file_begin_ >= oat_data_begin_) {
return false;
}
- if (image_roots_ <= image_begin_ || oat_file_begin_ <= image_roots_) {
- return false;
- }
if (!IsAligned<kPageSize>(patch_delta_)) {
return false;
}
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index e2e4782..d035f5d 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -187,24 +187,27 @@
if (image_added_to_intern_table_) {
return nullptr;
}
- gc::space::ImageSpace* image = Runtime::Current()->GetHeap()->GetBootImageSpace();
- if (image == nullptr) {
+ std::vector<gc::space::ImageSpace*> image_spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ if (image_spaces.empty()) {
return nullptr; // No image present.
}
- mirror::Object* root = image->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
- mirror::ObjectArray<mirror::DexCache>* dex_caches = root->AsObjectArray<mirror::DexCache>();
const std::string utf8 = s->ToModifiedUtf8();
- for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
- mirror::DexCache* dex_cache = dex_caches->Get(i);
- const DexFile* dex_file = dex_cache->GetDexFile();
- // Binary search the dex file for the string index.
- const DexFile::StringId* string_id = dex_file->FindStringId(utf8.c_str());
- if (string_id != nullptr) {
- uint32_t string_idx = dex_file->GetIndexForStringId(*string_id);
- // GetResolvedString() contains a RB.
- mirror::String* image_string = dex_cache->GetResolvedString(string_idx);
- if (image_string != nullptr) {
- return image_string;
+ for (gc::space::ImageSpace* image_space : image_spaces) {
+ mirror::Object* root = image_space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
+ mirror::ObjectArray<mirror::DexCache>* dex_caches = root->AsObjectArray<mirror::DexCache>();
+ for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
+ mirror::DexCache* dex_cache = dex_caches->Get(i);
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ // Binary search the dex file for the string index.
+ const DexFile::StringId* string_id = dex_file->FindStringId(utf8.c_str());
+ if (string_id != nullptr) {
+ uint32_t string_idx = dex_file->GetIndexForStringId(*string_id);
+ // GetResolvedString() contains a RB.
+ mirror::String* image_string = dex_cache->GetResolvedString(string_idx);
+ if (image_string != nullptr) {
+ return image_string;
+ }
}
}
}
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index b41d16b..c718466 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -2091,8 +2091,7 @@
MakeExecutable(nullptr, "java.lang.Class");
MakeExecutable(nullptr, "java.lang.Object");
MakeExecutable(nullptr, "java.nio.DirectByteBuffer");
- MakeExecutable(nullptr, "java.nio.MemoryBlock");
- MakeExecutable(nullptr, "java.nio.MemoryBlock$UnmanagedBlock");
+ MakeExecutable(nullptr, "java.nio.Bits");
MakeExecutable(nullptr, "java.nio.MappedByteBuffer");
MakeExecutable(nullptr, "java.nio.ByteBuffer");
MakeExecutable(nullptr, "java.nio.Buffer");
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index ce879ba..489c269 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -560,8 +560,8 @@
// The size of java.lang.Class.class.
static uint32_t ClassClassSize(size_t pointer_size) {
// The number of vtable entries in java.lang.Class.
- uint32_t vtable_entries = Object::kVTableLength + 65;
- return ComputeClassSize(true, vtable_entries, 0, 0, 0, 1, 0, pointer_size);
+ uint32_t vtable_entries = Object::kVTableLength + 69;
+ return ComputeClassSize(true, vtable_entries, 0, 0, 4, 1, 0, pointer_size);
}
// The size of a java.lang.Class representing a primitive such as int.class.
@@ -1275,6 +1275,8 @@
// 'Class' Object Fields
// Order governed by java field ordering. See art::ClassLinker::LinkFields.
+ HeapReference<Object> annotation_type_;
+
// Defining class loader, or null for the "bootstrap" system loader.
HeapReference<ClassLoader> class_loader_;
@@ -1320,6 +1322,9 @@
// virtual_ methods_ for miranda methods.
HeapReference<PointerArray> vtable_;
+ // Access flags; low 16 bits are defined by VM spec.
+ uint32_t access_flags_;
+
// Short cuts to dex_cache_ member for fast compiled code access.
uint64_t dex_cache_strings_;
@@ -1352,9 +1357,6 @@
// Static fields length-prefixed array.
uint64_t sfields_;
- // Access flags; low 16 bits are defined by VM spec.
- uint32_t access_flags_;
-
// Class flags to help speed up visiting object references.
uint32_t class_flags_;
diff --git a/runtime/mirror/reference-inl.h b/runtime/mirror/reference-inl.h
index 01e99b9..bd4a9c1 100644
--- a/runtime/mirror/reference-inl.h
+++ b/runtime/mirror/reference-inl.h
@@ -23,7 +23,7 @@
namespace mirror {
inline uint32_t Reference::ClassSize(size_t pointer_size) {
- uint32_t vtable_entries = Object::kVTableLength + 5;
+ uint32_t vtable_entries = Object::kVTableLength + 4;
return Class::ComputeClassSize(false, vtable_entries, 2, 0, 0, 0, 0, pointer_size);
}
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 28a830d..cdf468c 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -33,8 +33,8 @@
namespace mirror {
inline uint32_t String::ClassSize(size_t pointer_size) {
- uint32_t vtable_entries = Object::kVTableLength + 52;
- return Class::ComputeClassSize(true, vtable_entries, 0, 1, 0, 1, 2, pointer_size);
+ uint32_t vtable_entries = Object::kVTableLength + 53;
+ return Class::ComputeClassSize(true, vtable_entries, 0, 2, 0, 1, 2, pointer_size);
}
// Sets string count in the allocation code path to ensure it is guarded by a CAS.
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index e215994..f068b3e 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -56,9 +56,9 @@
void Throwable::SetStackState(Object* state) SHARED_REQUIRES(Locks::mutator_lock_) {
CHECK(state != nullptr);
if (Runtime::Current()->IsActiveTransaction()) {
- SetFieldObjectVolatile<true>(OFFSET_OF_OBJECT_MEMBER(Throwable, stack_state_), state);
+ SetFieldObjectVolatile<true>(OFFSET_OF_OBJECT_MEMBER(Throwable, backtrace_), state);
} else {
- SetFieldObjectVolatile<false>(OFFSET_OF_OBJECT_MEMBER(Throwable, stack_state_), state);
+ SetFieldObjectVolatile<false>(OFFSET_OF_OBJECT_MEMBER(Throwable, backtrace_), state);
}
}
diff --git a/runtime/mirror/throwable.h b/runtime/mirror/throwable.h
index 0f488dc..6aacc8d 100644
--- a/runtime/mirror/throwable.h
+++ b/runtime/mirror/throwable.h
@@ -60,16 +60,16 @@
private:
Object* GetStackState() SHARED_REQUIRES(Locks::mutator_lock_) {
- return GetFieldObjectVolatile<Object>(OFFSET_OF_OBJECT_MEMBER(Throwable, stack_state_));
+ return GetFieldObjectVolatile<Object>(OFFSET_OF_OBJECT_MEMBER(Throwable, backtrace_));
}
Object* GetStackTrace() SHARED_REQUIRES(Locks::mutator_lock_) {
- return GetFieldObjectVolatile<Object>(OFFSET_OF_OBJECT_MEMBER(Throwable, stack_trace_));
+ return GetFieldObjectVolatile<Object>(OFFSET_OF_OBJECT_MEMBER(Throwable, backtrace_));
}
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
+ HeapReference<Object> backtrace_; // Note this is Java volatile:
HeapReference<Throwable> cause_;
HeapReference<String> detail_message_;
- HeapReference<Object> stack_state_; // Note this is Java volatile:
HeapReference<Object> stack_trace_;
HeapReference<Object> suppressed_exceptions_;
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index ae1a4d7..67d825e 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -129,7 +129,11 @@
return reinterpret_cast<jlong>(ThreadForEnv(env));
}
-static void ZygoteHooks_nativePostForkChild(JNIEnv* env, jclass, jlong token, jint debug_flags,
+static void ZygoteHooks_nativePostForkChild(JNIEnv* env,
+ jclass,
+ jlong token,
+ jint debug_flags,
+ jboolean is_system_server,
jstring instruction_set) {
Thread* thread = reinterpret_cast<Thread*>(token);
// Our system thread ID, etc, has changed so reset Thread state.
@@ -174,22 +178,24 @@
}
}
- if (instruction_set != nullptr) {
+ if (instruction_set != nullptr && !is_system_server) {
ScopedUtfChars isa_string(env, instruction_set);
InstructionSet isa = GetInstructionSetFromString(isa_string.c_str());
Runtime::NativeBridgeAction action = Runtime::NativeBridgeAction::kUnload;
if (isa != kNone && isa != kRuntimeISA) {
action = Runtime::NativeBridgeAction::kInitialize;
}
- Runtime::Current()->InitNonZygoteOrPostFork(env, action, isa_string.c_str());
+ Runtime::Current()->InitNonZygoteOrPostFork(
+ env, is_system_server, action, isa_string.c_str());
} else {
- Runtime::Current()->InitNonZygoteOrPostFork(env, Runtime::NativeBridgeAction::kUnload, nullptr);
+ Runtime::Current()->InitNonZygoteOrPostFork(
+ env, is_system_server, Runtime::NativeBridgeAction::kUnload, nullptr);
}
}
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(ZygoteHooks, nativePreFork, "()J"),
- NATIVE_METHOD(ZygoteHooks, nativePostForkChild, "(JILjava/lang/String;)V"),
+ NATIVE_METHOD(ZygoteHooks, nativePostForkChild, "(JIZLjava/lang/String;)V"),
};
void register_dalvik_system_ZygoteHooks(JNIEnv* env) {
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index 45b9484..ddcaade 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -86,7 +86,7 @@
* with an interface, array, or primitive class. If this is coming from
* native, it is OK to avoid access checks since JNI does not enforce them.
*/
-static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) {
+static jobject Constructor_newInstance0(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) {
ScopedFastNativeObjectAccess soa(env);
mirror::Constructor* m = soa.Decode<mirror::Constructor*>(javaMethod);
StackHandleScope<1> hs(soa.Self());
@@ -99,7 +99,9 @@
}
// Verify that we can access the class.
if (!m->IsAccessible() && !c->IsPublic()) {
- auto* caller = GetCallingClass(soa.Self(), 1);
+ // Go 2 frames back, this method is always called from newInstance0, which is called from
+ // Constructor.newInstance(Object... args).
+ auto* caller = GetCallingClass(soa.Self(), 2);
// If caller is null, then we called from JNI, just avoid the check since JNI avoids most
// access checks anyways. TODO: Investigate if this the correct behavior.
if (caller != nullptr && !caller->CanAccess(c.Get())) {
@@ -127,7 +129,7 @@
// String constructor is replaced by a StringFactory method in InvokeMethod.
if (c->IsStringClass()) {
- return InvokeMethod(soa, javaMethod, nullptr, javaArgs, 1);
+ return InvokeMethod(soa, javaMethod, nullptr, javaArgs, 2);
}
mirror::Object* receiver =
@@ -136,11 +138,18 @@
return nullptr;
}
jobject javaReceiver = soa.AddLocalReference<jobject>(receiver);
- InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, 1);
+ InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, 2);
// Constructors are ()V methods, so we shouldn't touch the result of InvokeMethod.
return javaReceiver;
}
+static jobject Constructor_newInstanceFromSerialization(JNIEnv* env, jclass unused ATTRIBUTE_UNUSED,
+ jclass ctorClass, jclass allocClass) {
+ jmethodID ctor = env->GetMethodID(ctorClass, "<init>", "()V");
+ DCHECK(ctor != NULL);
+ return env->NewObject(allocClass, ctor);
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Constructor, getAnnotationNative,
"!(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"),
@@ -149,7 +158,8 @@
NATIVE_METHOD(Constructor, getParameterAnnotationsNative,
"!()[[Ljava/lang/annotation/Annotation;"),
NATIVE_METHOD(Constructor, isAnnotationPresentNative, "!(Ljava/lang/Class;)Z"),
- NATIVE_METHOD(Constructor, newInstance, "!([Ljava/lang/Object;)Ljava/lang/Object;"),
+ NATIVE_METHOD(Constructor, newInstance0, "!([Ljava/lang/Object;)Ljava/lang/Object;"),
+ NATIVE_METHOD(Constructor, newInstanceFromSerialization, "!(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Object;"),
};
void register_java_lang_reflect_Constructor(JNIEnv* env) {
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 83125ce..8a2c7e4 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -15,7 +15,7 @@
*/
#include "sun_misc_Unsafe.h"
-
+#include "common_throws.h"
#include "gc/accounting/card_table-inl.h"
#include "jni_internal.h"
#include "mirror/array.h"
@@ -23,6 +23,10 @@
#include "mirror/object-inl.h"
#include "scoped_fast_native_object_access.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
namespace art {
static jboolean Unsafe_compareAndSwapInt(JNIEnv* env, jobject, jobject javaObj, jlong offset,
@@ -196,6 +200,279 @@
return Primitive::ComponentSize(primitive_type);
}
+static jint Unsafe_addressSize(JNIEnv* env ATTRIBUTE_UNUSED, jobject ob ATTRIBUTE_UNUSED) {
+ return sizeof(void*);
+}
+
+static jint Unsafe_pageSize(JNIEnv* env ATTRIBUTE_UNUSED, jobject ob ATTRIBUTE_UNUSED) {
+ return sysconf(_SC_PAGESIZE);
+}
+
+static jlong Unsafe_allocateMemory(JNIEnv* env, jobject, jlong bytes) {
+ ScopedFastNativeObjectAccess soa(env);
+ // bytes is nonnegative and fits into size_t
+ if (bytes < 0 || bytes != (jlong)(size_t) bytes) {
+ ThrowIllegalAccessException("wrong number of bytes");
+ return 0;
+ }
+ void* mem = malloc(bytes);
+ if (mem == nullptr) {
+ soa.Self()->ThrowOutOfMemoryError("native alloc");
+ return 0;
+ }
+ return (uintptr_t) mem;
+}
+
+static void Unsafe_freeMemory(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
+ free(reinterpret_cast<void*>(static_cast<uintptr_t>(address)));
+}
+
+static void Unsafe_setMemory(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jlong bytes, jbyte value) {
+ memset(reinterpret_cast<void*>(static_cast<uintptr_t>(address)), value, bytes);
+}
+
+static jbyte Unsafe_getByte$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
+ return *reinterpret_cast<jbyte*>(address);
+}
+
+static void Unsafe_putByte$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jbyte value) {
+ *reinterpret_cast<jbyte*>(address) = value;
+}
+
+static jshort Unsafe_getShort$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
+ return *reinterpret_cast<jshort*>(address);
+}
+
+static void Unsafe_putShort$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jshort value) {
+ *reinterpret_cast<jshort*>(address) = value;
+}
+
+static jchar Unsafe_getChar$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
+ return *reinterpret_cast<jchar*>(address);
+}
+
+static void Unsafe_putChar$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jchar value) {
+ *reinterpret_cast<jchar*>(address) = value;
+}
+
+static jint Unsafe_getInt$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
+ return *reinterpret_cast<jint*>(address);
+}
+
+static void Unsafe_putInt$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jint value) {
+ *reinterpret_cast<jint*>(address) = value;
+}
+
+static jlong Unsafe_getLong$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
+ return *reinterpret_cast<jlong*>(address);
+}
+
+static void Unsafe_putLong$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jlong value) {
+ *reinterpret_cast<jlong*>(address) = value;
+}
+
+static jfloat Unsafe_getFloat$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
+ return *reinterpret_cast<jfloat*>(address);
+}
+
+static void Unsafe_putFloat$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jfloat value) {
+ *reinterpret_cast<jfloat*>(address) = value;
+}
+static jdouble Unsafe_getDouble$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
+ return *reinterpret_cast<jdouble*>(address);
+}
+
+static void Unsafe_putDouble$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jdouble value) {
+ *reinterpret_cast<jdouble*>(address) = value;
+}
+
+static void Unsafe_copyMemory(JNIEnv *env, jobject unsafe ATTRIBUTE_UNUSED, jlong src,
+ jlong dst, jlong size) {
+ if (size == 0) {
+ return;
+ }
+ // size is nonnegative and fits into size_t
+ if (size < 0 || size != (jlong)(size_t) size) {
+ ScopedFastNativeObjectAccess soa(env);
+ ThrowIllegalAccessException("wrong number of bytes");
+ }
+ size_t sz = (size_t)size;
+ memcpy(reinterpret_cast<void *>(dst), reinterpret_cast<void *>(src), sz);
+}
+
+template<typename T>
+static void copyToArray(jlong srcAddr, mirror::PrimitiveArray<T>* array,
+ size_t array_offset,
+ size_t size)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ const T* src = reinterpret_cast<T*>(srcAddr);
+ size_t sz = size / sizeof(T);
+ size_t of = array_offset / sizeof(T);
+ for (size_t i = 0; i < sz; ++i) {
+ array->Set(i + of, *(src + i));
+ }
+}
+
+template<typename T>
+static void copyFromArray(jlong dstAddr, mirror::PrimitiveArray<T>* array,
+ size_t array_offset,
+ size_t size)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ T* dst = reinterpret_cast<T*>(dstAddr);
+ size_t sz = size / sizeof(T);
+ size_t of = array_offset / sizeof(T);
+ for (size_t i = 0; i < sz; ++i) {
+ *(dst + i) = array->Get(i + of);
+ }
+}
+
+static void Unsafe_copyMemoryToPrimitiveArray(JNIEnv *env,
+ jobject unsafe ATTRIBUTE_UNUSED,
+ jlong srcAddr,
+ jobject dstObj,
+ jlong dstOffset,
+ jlong size) {
+ ScopedObjectAccess soa(env);
+ if (size == 0) {
+ return;
+ }
+ // size is nonnegative and fits into size_t
+ if (size < 0 || size != (jlong)(size_t) size) {
+ ThrowIllegalAccessException("wrong number of bytes");
+ }
+ size_t sz = (size_t)size;
+ size_t dst_offset = (size_t)dstOffset;
+ mirror::Object* dst = soa.Decode<mirror::Object*>(dstObj);
+ mirror::Class* component_type = dst->GetClass()->GetComponentType();
+ if (component_type->IsPrimitiveByte() || component_type->IsPrimitiveBoolean()) {
+ copyToArray(srcAddr, dst->AsByteSizedArray(), dst_offset, sz);
+ } else if (component_type->IsPrimitiveShort() || component_type->IsPrimitiveChar()) {
+ copyToArray(srcAddr, dst->AsShortSizedArray(), dst_offset, sz);
+ } else if (component_type->IsPrimitiveInt() || component_type->IsPrimitiveFloat()) {
+ copyToArray(srcAddr, dst->AsIntArray(), dst_offset, sz);
+ } else if (component_type->IsPrimitiveLong() || component_type->IsPrimitiveDouble()) {
+ copyToArray(srcAddr, dst->AsLongArray(), dst_offset, sz);
+ } else {
+ ThrowIllegalAccessException("not a primitive array");
+ }
+}
+
+static void Unsafe_copyMemoryFromPrimitiveArray(JNIEnv *env,
+ jobject unsafe ATTRIBUTE_UNUSED,
+ jobject srcObj,
+ jlong srcOffset,
+ jlong dstAddr,
+ jlong size) {
+ ScopedObjectAccess soa(env);
+ if (size == 0) {
+ return;
+ }
+ // size is nonnegative and fits into size_t
+ if (size < 0 || size != (jlong)(size_t) size) {
+ ThrowIllegalAccessException("wrong number of bytes");
+ }
+ size_t sz = (size_t)size;
+ size_t src_offset = (size_t)srcOffset;
+ mirror::Object* src = soa.Decode<mirror::Object*>(srcObj);
+ mirror::Class* component_type = src->GetClass()->GetComponentType();
+ if (component_type->IsPrimitiveByte() || component_type->IsPrimitiveBoolean()) {
+ copyFromArray(dstAddr, src->AsByteSizedArray(), src_offset, sz);
+ } else if (component_type->IsPrimitiveShort() || component_type->IsPrimitiveChar()) {
+ copyFromArray(dstAddr, src->AsShortSizedArray(), src_offset, sz);
+ } else if (component_type->IsPrimitiveInt() || component_type->IsPrimitiveFloat()) {
+ copyFromArray(dstAddr, src->AsIntArray(), src_offset, sz);
+ } else if (component_type->IsPrimitiveLong() || component_type->IsPrimitiveDouble()) {
+ copyFromArray(dstAddr, src->AsLongArray(), src_offset, sz);
+ } else {
+ ThrowIllegalAccessException("not a primitive array");
+ }
+}
+static jboolean Unsafe_getBoolean(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
+ ScopedFastNativeObjectAccess soa(env);
+ mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+ return obj->GetFieldBoolean(MemberOffset(offset));
+}
+
+static void Unsafe_putBoolean(JNIEnv* env, jobject, jobject javaObj, jlong offset, jboolean newValue) {
+ ScopedFastNativeObjectAccess soa(env);
+ mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+ // JNI must use non transactional mode (SetField8 is non-transactional).
+ obj->SetFieldBoolean<false>(MemberOffset(offset), newValue);
+}
+
+static jbyte Unsafe_getByte(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
+ ScopedFastNativeObjectAccess soa(env);
+ mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+ return obj->GetFieldByte(MemberOffset(offset));
+}
+
+static void Unsafe_putByte(JNIEnv* env, jobject, jobject javaObj, jlong offset, jbyte newValue) {
+ ScopedFastNativeObjectAccess soa(env);
+ mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+ // JNI must use non transactional mode.
+ obj->SetFieldByte<false>(MemberOffset(offset), newValue);
+}
+
+static jchar Unsafe_getChar(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
+ ScopedFastNativeObjectAccess soa(env);
+ mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+ return obj->GetFieldChar(MemberOffset(offset));
+}
+
+static void Unsafe_putChar(JNIEnv* env, jobject, jobject javaObj, jlong offset, jchar newValue) {
+ ScopedFastNativeObjectAccess soa(env);
+ mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+ // JNI must use non transactional mode.
+ obj->SetFieldChar<false>(MemberOffset(offset), newValue);
+}
+
+static jshort Unsafe_getShort(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
+ ScopedFastNativeObjectAccess soa(env);
+ mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+ return obj->GetFieldShort(MemberOffset(offset));
+}
+
+static void Unsafe_putShort(JNIEnv* env, jobject, jobject javaObj, jlong offset, jshort newValue) {
+ ScopedFastNativeObjectAccess soa(env);
+ mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+ // JNI must use non transactional mode.
+ obj->SetFieldShort<false>(MemberOffset(offset), newValue);
+}
+
+static jfloat Unsafe_getFloat(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
+ ScopedFastNativeObjectAccess soa(env);
+ mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+ union {int32_t val; jfloat converted;} conv;
+ conv.val = obj->GetField32(MemberOffset(offset));
+ return conv.converted;
+}
+
+static void Unsafe_putFloat(JNIEnv* env, jobject, jobject javaObj, jlong offset, jfloat newValue) {
+ ScopedFastNativeObjectAccess soa(env);
+ mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+ union {int32_t converted; jfloat val;} conv;
+ conv.val = newValue;
+ // JNI must use non transactional mode.
+ obj->SetField32<false>(MemberOffset(offset), conv.converted);
+}
+
+static jdouble Unsafe_getDouble(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
+ ScopedFastNativeObjectAccess soa(env);
+ mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+ union {int64_t val; jdouble converted;} conv;
+ conv.val = obj->GetField64(MemberOffset(offset));
+ return conv.converted;
+}
+
+static void Unsafe_putDouble(JNIEnv* env, jobject, jobject javaObj, jlong offset, jdouble newValue) {
+ ScopedFastNativeObjectAccess soa(env);
+ mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+ union {int64_t converted; jdouble val;} conv;
+ conv.val = newValue;
+ // JNI must use non transactional mode.
+ obj->SetField64<false>(MemberOffset(offset), conv.converted);
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Unsafe, compareAndSwapInt, "!(Ljava/lang/Object;JII)Z"),
NATIVE_METHOD(Unsafe, compareAndSwapLong, "!(Ljava/lang/Object;JJJ)Z"),
@@ -217,6 +494,40 @@
NATIVE_METHOD(Unsafe, putOrderedObject, "!(Ljava/lang/Object;JLjava/lang/Object;)V"),
NATIVE_METHOD(Unsafe, getArrayBaseOffsetForComponentType, "!(Ljava/lang/Class;)I"),
NATIVE_METHOD(Unsafe, getArrayIndexScaleForComponentType, "!(Ljava/lang/Class;)I"),
+ NATIVE_METHOD(Unsafe, addressSize, "!()I"),
+ NATIVE_METHOD(Unsafe, pageSize, "!()I"),
+ NATIVE_METHOD(Unsafe, allocateMemory, "!(J)J"),
+ NATIVE_METHOD(Unsafe, freeMemory, "!(J)V"),
+ NATIVE_METHOD(Unsafe, setMemory, "!(JJB)V"),
+ NATIVE_METHOD(Unsafe, getByte$, "!(J)B"),
+ NATIVE_METHOD(Unsafe, putByte$, "!(JB)V"),
+ NATIVE_METHOD(Unsafe, getShort$, "!(J)S"),
+ NATIVE_METHOD(Unsafe, putShort$, "!(JS)V"),
+ NATIVE_METHOD(Unsafe, getChar$, "!(J)C"),
+ NATIVE_METHOD(Unsafe, putChar$, "!(JC)V"),
+ NATIVE_METHOD(Unsafe, getInt$, "!(J)I"),
+ NATIVE_METHOD(Unsafe, putInt$, "!(JI)V"),
+ NATIVE_METHOD(Unsafe, getLong$, "!(J)J"),
+ NATIVE_METHOD(Unsafe, putLong$, "!(JJ)V"),
+ NATIVE_METHOD(Unsafe, getFloat$, "!(J)F"),
+ NATIVE_METHOD(Unsafe, putFloat$, "!(JF)V"),
+ NATIVE_METHOD(Unsafe, getDouble$, "!(J)D"),
+ NATIVE_METHOD(Unsafe, putDouble$, "!(JD)V"),
+ NATIVE_METHOD(Unsafe, copyMemory, "!(JJJ)V"),
+ NATIVE_METHOD(Unsafe, copyMemoryToPrimitiveArray, "!(JLjava/lang/Object;JJ)V"),
+ NATIVE_METHOD(Unsafe, copyMemoryFromPrimitiveArray, "!(Ljava/lang/Object;JJJ)V"),
+ NATIVE_METHOD(Unsafe, getBoolean, "!(Ljava/lang/Object;J)Z"),
+ NATIVE_METHOD(Unsafe, getByte, "!(Ljava/lang/Object;J)B"),
+ NATIVE_METHOD(Unsafe, getChar, "!(Ljava/lang/Object;J)C"),
+ NATIVE_METHOD(Unsafe, getShort, "!(Ljava/lang/Object;J)S"),
+ NATIVE_METHOD(Unsafe, getFloat, "!(Ljava/lang/Object;J)F"),
+ NATIVE_METHOD(Unsafe, getDouble, "!(Ljava/lang/Object;J)D"),
+ NATIVE_METHOD(Unsafe, putBoolean, "!(Ljava/lang/Object;JZ)V"),
+ NATIVE_METHOD(Unsafe, putByte, "!(Ljava/lang/Object;JB)V"),
+ NATIVE_METHOD(Unsafe, putChar, "!(Ljava/lang/Object;JC)V"),
+ NATIVE_METHOD(Unsafe, putShort, "!(Ljava/lang/Object;JS)V"),
+ NATIVE_METHOD(Unsafe, putFloat, "!(Ljava/lang/Object;JF)V"),
+ NATIVE_METHOD(Unsafe, putDouble, "!(Ljava/lang/Object;JD)V"),
};
void register_sun_misc_Unsafe(JNIEnv* env) {
diff --git a/runtime/oat.h b/runtime/oat.h
index 5ed1977..13fd6a4 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -39,6 +39,7 @@
static constexpr const char* kPicKey = "pic";
static constexpr const char* kDebuggableKey = "debuggable";
static constexpr const char* kClassPathKey = "classpath";
+ static constexpr const char* kBootClassPath = "bootclasspath";
static constexpr const char kTrueValue[] = "true";
static constexpr const char kFalseValue[] = "false";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 83e594b..e3de14b 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -983,6 +983,7 @@
LOG(WARNING) << "Failed to find OatDexFile for DexFile " << dex_location
<< " ( canonical path " << dex_canonical_location << ")"
<< " with checksum " << checksum << " in OatFile " << GetLocation();
+ /* TODO: Modify for multi-image support and reenable. b/26317072
if (kIsDebugBuild) {
for (const OatDexFile* odf : oat_dex_files_storage_) {
LOG(WARNING) << "OatFile " << GetLocation()
@@ -991,6 +992,7 @@
<< " with checksum 0x" << std::hex << odf->GetDexFileLocationChecksum();
}
}
+ */
}
return nullptr;
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 0f3a013..cb80cc9 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -846,11 +846,7 @@
std::string OatFileAssistant::ImageLocation() {
Runtime* runtime = Runtime::Current();
- const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetBootImageSpace();
- if (image_space == nullptr) {
- return "";
- }
- return image_space->GetImageLocation();
+ return runtime->GetHeap()->GetBootImageSpaces()[0]->GetImageLocation();
}
const uint32_t* OatFileAssistant::GetRequiredDexChecksum() {
@@ -949,12 +945,13 @@
image_info_load_attempted_ = true;
Runtime* runtime = Runtime::Current();
- const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetBootImageSpace();
- if (image_space != nullptr) {
- cached_image_info_.location = image_space->GetImageLocation();
+ std::vector<gc::space::ImageSpace*> image_spaces = runtime->GetHeap()->GetBootImageSpaces();
+ if (!image_spaces.empty()) {
+ // TODO: Better support multi-images? b/26317072
+ cached_image_info_.location = image_spaces[0]->GetImageLocation();
if (isa_ == kRuntimeISA) {
- const ImageHeader& image_header = image_space->GetImageHeader();
+ const ImageHeader& image_header = image_spaces[0]->GetImageHeader();
cached_image_info_.oat_checksum = image_header.GetOatChecksum();
cached_image_info_.oat_data_begin = reinterpret_cast<uintptr_t>(
image_header.GetOatDataBegin());
@@ -969,7 +966,7 @@
cached_image_info_.patch_delta = image_header->GetPatchDelta();
}
}
- image_info_load_succeeded_ = (image_space != nullptr);
+ image_info_load_succeeded_ = (!image_spaces.empty());
}
return image_info_load_succeeded_ ? &cached_image_info_ : nullptr;
}
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 8c7efb2..f994f0c 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -223,9 +223,10 @@
false, dex_location.c_str(), &error_msg));
ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
- const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetBootImageSpace();
- ASSERT_TRUE(image_space != nullptr);
- const ImageHeader& image_header = image_space->GetImageHeader();
+ const std::vector<gc::space::ImageSpace*> image_spaces =
+ runtime->GetHeap()->GetBootImageSpaces();
+ ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr);
+ const ImageHeader& image_header = image_spaces[0]->GetImageHeader();
const OatHeader& oat_header = odex_file->GetOatHeader();
EXPECT_FALSE(odex_file->IsPic());
EXPECT_EQ(image_header.GetOatChecksum(), oat_header.GetImageFileLocationOatChecksum());
@@ -1025,7 +1026,7 @@
// We use the lib core dex file, because it's large, and hopefully should
// take a while to generate.
- Copy(GetLibCoreDexFileName(), dex_location);
+ Copy(GetLibCoreDexFileNames()[0], dex_location);
const int kNumThreads = 32;
Thread* self = Thread::Current();
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index ea6d3ff..36a967f 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -78,17 +78,23 @@
return nullptr;
}
-const OatFile* OatFileManager::GetBootOatFile() const {
- gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetBootImageSpace();
- return (image_space == nullptr) ? nullptr : image_space->GetOatFile();
+std::vector<const OatFile*> OatFileManager::GetBootOatFiles() const {
+ std::vector<const OatFile*> oat_files;
+ std::vector<gc::space::ImageSpace*> image_spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ for (gc::space::ImageSpace* image_space : image_spaces) {
+ oat_files.push_back(image_space->GetOatFile());
+ }
+ return oat_files;
}
const OatFile* OatFileManager::GetPrimaryOatFile() const {
ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
- const OatFile* boot_oat_file = GetBootOatFile();
- if (boot_oat_file != nullptr) {
+ std::vector<const OatFile*> boot_oat_files = GetBootOatFiles();
+ if (!boot_oat_files.empty()) {
for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) {
- if (oat_file.get() != boot_oat_file) {
+ if (std::find(boot_oat_files.begin(), boot_oat_files.end(), oat_file.get()) ==
+ boot_oat_files.end()) {
return oat_file.get();
}
}
@@ -102,8 +108,13 @@
oat_files_.clear();
}
-const OatFile* OatFileManager::RegisterImageOatFile(gc::space::ImageSpace* space) {
- return RegisterOatFile(space->ReleaseOatFile());
+std::vector<const OatFile*> OatFileManager::RegisterImageOatFiles(
+ std::vector<gc::space::ImageSpace*> spaces) {
+ std::vector<const OatFile*> oat_files;
+ for (gc::space::ImageSpace* space : spaces) {
+ oat_files.push_back(RegisterOatFile(space->ReleaseOatFile()));
+ }
+ return oat_files;
}
class DexFileAndClassPair : ValueObject {
@@ -213,7 +224,7 @@
std::priority_queue<DexFileAndClassPair> queue;
// Add dex files from already loaded oat files, but skip boot.
- const OatFile* boot_oat = GetBootOatFile();
+ std::vector<const OatFile*> boot_oat_files = GetBootOatFiles();
// The same OatFile can be loaded multiple times at different addresses. In this case, we don't
// need to check both against each other since they would have resolved the same way at compile
// time.
@@ -221,8 +232,8 @@
for (const std::unique_ptr<const OatFile>& loaded_oat_file : oat_files_) {
DCHECK_NE(loaded_oat_file.get(), oat_file);
const std::string& location = loaded_oat_file->GetLocation();
- if (loaded_oat_file.get() != boot_oat &&
- location != oat_file->GetLocation() &&
+ if (std::find(boot_oat_files.begin(), boot_oat_files.end(), loaded_oat_file.get()) ==
+ boot_oat_files.end() && location != oat_file->GetLocation() &&
unique_locations.find(location) == unique_locations.end()) {
unique_locations.insert(location);
AddDexFilesFromOat(loaded_oat_file.get(), /*already_loaded*/true, &queue);
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index af7efb4..4690e45 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -73,15 +73,15 @@
return have_non_pic_oat_file_;
}
- // Returns the boot image oat file.
- const OatFile* GetBootOatFile() const;
+ // Returns the boot image oat files.
+ std::vector<const OatFile*> GetBootOatFiles() const;
// Returns the first non-image oat file in the class path.
const OatFile* GetPrimaryOatFile() const REQUIRES(!Locks::oat_file_manager_lock_);
- // Return the oat file for an image, registers the oat file. Takes ownership of the imagespace's
- // underlying oat file.
- const OatFile* RegisterImageOatFile(gc::space::ImageSpace* space)
+ // Returns the oat files for the images, registers the oat files.
+ // Takes ownership of the imagespace's underlying oat files.
+ std::vector<const OatFile*> RegisterImageOatFiles(std::vector<gc::space::ImageSpace*> spaces)
REQUIRES(!Locks::oat_file_manager_lock_);
// Finds or creates the oat file holding dex_location. Then loads and returns
diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h
index 03cad08..5643739 100644
--- a/runtime/oat_quick_method_header.h
+++ b/runtime/oat_quick_method_header.h
@@ -44,7 +44,8 @@
uintptr_t code = reinterpret_cast<uintptr_t>(code_ptr);
uintptr_t header = code - OFFSETOF_MEMBER(OatQuickMethodHeader, code_);
DCHECK(IsAlignedParam(code, GetInstructionSetAlignment(kRuntimeISA)) ||
- IsAlignedParam(header, GetInstructionSetAlignment(kRuntimeISA)));
+ IsAlignedParam(header, GetInstructionSetAlignment(kRuntimeISA)))
+ << std::hex << code << " " << std::hex << header;
return reinterpret_cast<OatQuickMethodHeader*>(header);
}
diff --git a/runtime/openjdkjvm/NOTICE b/runtime/openjdkjvm/NOTICE
new file mode 100644
index 0000000..700a206
--- /dev/null
+++ b/runtime/openjdkjvm/NOTICE
@@ -0,0 +1,29 @@
+Copyright (C) 2014 The Android Open Source Project
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+This file implements interfaces from the file jvm.h. This implementation
+is licensed under the same terms as the file jvm.h. The
+copyright and license information for the file jvm.h follows.
+
+Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+This code is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 2 only, as
+published by the Free Software Foundation. Oracle designates this
+particular file as subject to the "Classpath" exception as provided
+by Oracle in the LICENSE file that accompanied this code.
+
+This code is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+version 2 for more details (a copy is included in the LICENSE file that
+accompanied this code).
+
+You should have received a copy of the GNU General Public License version
+2 along with this work; if not, write to the Free Software Foundation,
+Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+or visit www.oracle.com if you need additional information or have any
+questions.
diff --git a/runtime/openjdkjvm/OpenjdkJvm.cc b/runtime/openjdkjvm/OpenjdkJvm.cc
new file mode 100644
index 0000000..ab0d934
--- /dev/null
+++ b/runtime/openjdkjvm/OpenjdkJvm.cc
@@ -0,0 +1,540 @@
+/* Copyright (C) 2014 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvm.h. This implementation
+ * is licensed under the same terms as the file jvm.h. The
+ * copyright and license information for the file jvm.h follows.
+ *
+ * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * Services that OpenJDK expects the VM to provide.
+ */
+#include<stdio.h>
+#include <dlfcn.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include "common_throws.h"
+#include "gc/heap.h"
+#include "thread.h"
+#include "thread_list.h"
+#include "runtime.h"
+#include "handle_scope-inl.h"
+#include "scoped_thread_state_change.h"
+#include "ScopedUtfChars.h"
+#include "mirror/class_loader.h"
+#include "verify_object-inl.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "../../libcore/ojluni/src/main/native/jvm.h" // TODO(narayan): fix it
+#include "jni_internal.h"
+#include "mirror/string-inl.h"
+#include "native/scoped_fast_native_object_access.h"
+#include "ScopedLocalRef.h"
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#ifdef __ANDROID__
+// This function is provided by android linker.
+extern "C" void android_update_LD_LIBRARY_PATH(const char* ld_library_path);
+#endif // __ANDROID__
+
+#undef LOG_TAG
+#define LOG_TAG "artopenjdk"
+
+using art::DEBUG;
+using art::WARNING;
+using art::VERBOSE;
+using art::INFO;
+using art::ERROR;
+using art::FATAL;
+
+/* posix open() with extensions; used by e.g. ZipFile */
+JNIEXPORT jint JVM_Open(const char* fname, jint flags, jint mode) {
+ LOG(DEBUG) << "JVM_Open fname='" << fname << "', flags=" << flags << ", mode=" << mode;
+
+ /*
+ * The call is expected to handle JVM_O_DELETE, which causes the file
+ * to be removed after it is opened. Also, some code seems to
+ * want the special return value JVM_EEXIST if the file open fails
+ * due to O_EXCL.
+ */
+ int fd = TEMP_FAILURE_RETRY(open(fname, flags & ~JVM_O_DELETE, mode));
+ if (fd < 0) {
+ int err = errno;
+ LOG(DEBUG) << "open(" << fname << ") failed: " << strerror(errno);
+ if (err == EEXIST) {
+ return JVM_EEXIST;
+ } else {
+ return -1;
+ }
+ }
+
+ if (flags & JVM_O_DELETE) {
+ LOG(DEBUG) << "Deleting '" << fname << "' after open\n";
+ if (unlink(fname) != 0) {
+ LOG(WARNING) << "Post-open deletion of '" << fname << "' failed: " << strerror(errno);
+ }
+ /* ignore */
+ }
+
+ LOG(VERBOSE) << "open(" << fname << ") --> " << fd;
+ return fd;
+}
+
+/* posix close() */
+JNIEXPORT jint JVM_Close(jint fd) {
+ LOG(DEBUG) << "JVM_Close fd=" << fd;
+ // don't want TEMP_FAILURE_RETRY here -- file is closed even if EINTR
+ return close(fd);
+}
+
+/* posix read() */
+JNIEXPORT jint JVM_Read(jint fd, char* buf, jint nbytes) {
+ LOG(DEBUG) << "JVM_Read fd=" << fd << ", buf='" << buf << "', nbytes=" << nbytes;
+ return TEMP_FAILURE_RETRY(read(fd, buf, nbytes));
+}
+
+/* posix write(); is used to write messages to stderr */
+JNIEXPORT jint JVM_Write(jint fd, char* buf, jint nbytes) {
+ LOG(DEBUG) << "JVM_Write fd=" << fd << ", buf='" << buf << "', nbytes=" << nbytes;
+ return TEMP_FAILURE_RETRY(write(fd, buf, nbytes));
+}
+
+/* posix lseek() */
+JNIEXPORT jlong JVM_Lseek(jint fd, jlong offset, jint whence) {
+ LOG(DEBUG) << "JVM_Lseek fd=" << fd << ", offset=" << offset << ", whence=" << whence;
+ return TEMP_FAILURE_RETRY(lseek(fd, offset, whence));
+}
+
+/*
+ * "raw monitors" seem to be expected to behave like non-recursive pthread
+ * mutexes. They're used by ZipFile.
+ */
+JNIEXPORT void* JVM_RawMonitorCreate(void) {
+ LOG(DEBUG) << "JVM_RawMonitorCreate";
+ pthread_mutex_t* newMutex =
+ reinterpret_cast<pthread_mutex_t*>(malloc(sizeof(pthread_mutex_t)));
+ pthread_mutex_init(newMutex, NULL);
+ return newMutex;
+}
+
+JNIEXPORT void JVM_RawMonitorDestroy(void* mon) {
+ LOG(DEBUG) << "JVM_RawMonitorDestroy mon=" << mon;
+ pthread_mutex_destroy(reinterpret_cast<pthread_mutex_t*>(mon));
+}
+
+JNIEXPORT jint JVM_RawMonitorEnter(void* mon) {
+ LOG(DEBUG) << "JVM_RawMonitorEnter mon=" << mon;
+ return pthread_mutex_lock(reinterpret_cast<pthread_mutex_t*>(mon));
+}
+
+JNIEXPORT void JVM_RawMonitorExit(void* mon) {
+ LOG(DEBUG) << "JVM_RawMonitorExit mon=" << mon;
+ pthread_mutex_unlock(reinterpret_cast<pthread_mutex_t*>(mon));
+}
+
+JNIEXPORT char* JVM_NativePath(char* path) {
+ LOG(DEBUG) << "JVM_NativePath path='" << path << "'";
+ return path;
+}
+
+JNIEXPORT jint JVM_GetLastErrorString(char* buf, int len) {
+#if defined(__GLIBC__) || defined(__BIONIC__)
+ int err = errno; // grab before JVM_TRACE can trash it
+ LOG(DEBUG) << "JVM_GetLastErrorString buf=" << buf << ", len=" << len;
+
+ if (len == 0) {
+ return 0;
+ }
+
+ char* result = strerror_r(err, buf, len);
+ if (result != buf) {
+ strncpy(buf, result, len);
+ buf[len - 1] = '\0';
+ }
+
+ return strlen(buf);
+#else
+ UNUSED(buf);
+ UNUSED(len);
+ return -1;
+#endif
+}
+
+JNIEXPORT int jio_fprintf(FILE* fp, const char* fmt, ...) {
+ va_list args;
+
+ va_start(args, fmt);
+ int len = jio_vfprintf(fp, fmt, args);
+ va_end(args);
+
+ return len;
+}
+
+JNIEXPORT int jio_vfprintf(FILE* fp, const char* fmt, va_list args) {
+ assert(fp != NULL);
+ return vfprintf(fp, fmt, args);
+}
+
+/* posix fsync() */
+JNIEXPORT jint JVM_Sync(jint fd) {
+ LOG(DEBUG) << "JVM_Sync fd=" << fd;
+ return TEMP_FAILURE_RETRY(fsync(fd));
+}
+
+JNIEXPORT void* JVM_FindLibraryEntry(void* handle, const char* name) {
+ LOG(DEBUG) << "JVM_FindLibraryEntry handle=" << handle << " name=" << name;
+ return dlsym(handle, name);
+}
+
+JNIEXPORT jlong JVM_CurrentTimeMillis(JNIEnv* env, jclass clazz ATTRIBUTE_UNUSED) {
+ LOG(DEBUG) << "JVM_CurrentTimeMillis env=" << env;
+ struct timeval tv;
+
+ gettimeofday(&tv, (struct timezone *) NULL);
+ jlong when = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
+ return when;
+}
+
+JNIEXPORT jint JVM_Socket(jint domain, jint type, jint protocol) {
+ LOG(DEBUG) << "JVM_Socket domain=" << domain << ", type=" << type << ", protocol=" << protocol;
+
+ return TEMP_FAILURE_RETRY(socket(domain, type, protocol));
+}
+
+JNIEXPORT jint JVM_InitializeSocketLibrary() {
+ return 0;
+}
+
+int jio_vsnprintf(char *str, size_t count, const char *fmt, va_list args) {
+ if ((intptr_t)count <= 0) return -1;
+ return vsnprintf(str, count, fmt, args);
+}
+
+int jio_snprintf(char *str, size_t count, const char *fmt, ...) {
+ va_list args;
+ int len;
+ va_start(args, fmt);
+ len = jio_vsnprintf(str, count, fmt, args);
+ va_end(args);
+ return len;
+}
+
+JNIEXPORT jint JVM_SetSockOpt(jint fd, int level, int optname,
+ const char* optval, int optlen) {
+ LOG(DEBUG) << "JVM_SetSockOpt fd=" << fd << ", level=" << level << ", optname=" << optname
+ << ", optval=" << optval << ", optlen=" << optlen;
+ return TEMP_FAILURE_RETRY(setsockopt(fd, level, optname, optval, optlen));
+}
+
+JNIEXPORT jint JVM_SocketShutdown(jint fd, jint howto) {
+ LOG(DEBUG) << "JVM_SocketShutdown fd=" << fd << ", howto=" << howto;
+ return TEMP_FAILURE_RETRY(shutdown(fd, howto));
+}
+
+JNIEXPORT jint JVM_GetSockOpt(jint fd, int level, int optname, char* optval,
+ int* optlen) {
+ LOG(DEBUG) << "JVM_GetSockOpt fd=" << fd << ", level=" << level << ", optname=" << optname
+ << ", optval=" << optval << ", optlen=" << optlen;
+
+ socklen_t len = *optlen;
+ int cc = TEMP_FAILURE_RETRY(getsockopt(fd, level, optname, optval, &len));
+ *optlen = len;
+ return cc;
+}
+
+JNIEXPORT jint JVM_GetSockName(jint fd, struct sockaddr* addr, int* addrlen) {
+ LOG(DEBUG) << "JVM_GetSockName fd=" << fd << ", addr=" << addr << ", addrlen=" << addrlen;
+
+ socklen_t len = *addrlen;
+ int cc = TEMP_FAILURE_RETRY(getsockname(fd, addr, &len));
+ *addrlen = len;
+ return cc;
+}
+
+JNIEXPORT jint JVM_SocketAvailable(jint fd, jint* result) {
+ LOG(DEBUG) << "JVM_SocketAvailable fd=" << fd << ", result=" << result;
+
+ if (TEMP_FAILURE_RETRY(ioctl(fd, FIONREAD, result)) < 0) {
+ LOG(DEBUG) << "ioctl(" << fd << ", FIONREAD) failed: " << strerror(errno);
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+JNIEXPORT jint JVM_Send(jint fd, char* buf, jint nBytes, jint flags) {
+ LOG(DEBUG) << "JVM_Send fd=" << fd << ", buf=" << buf << ", nBytes="
+ << nBytes << ", flags=" << flags;
+
+ return TEMP_FAILURE_RETRY(send(fd, buf, nBytes, flags));
+}
+
+JNIEXPORT jint JVM_SocketClose(jint fd) {
+ LOG(DEBUG) << "JVM_SocketClose fd=" << fd;
+
+ // don't want TEMP_FAILURE_RETRY here -- file is closed even if EINTR
+ return close(fd);
+}
+
+JNIEXPORT jint JVM_Listen(jint fd, jint count) {
+ LOG(DEBUG) << "JVM_Listen fd=" << fd << ", count=" << count;
+
+ return TEMP_FAILURE_RETRY(listen(fd, count));
+}
+
+JNIEXPORT jint JVM_Connect(jint fd, struct sockaddr* addr, jint addrlen) {
+ LOG(DEBUG) << "JVM_Connect fd=" << fd << ", addr=" << addr << ", addrlen=" << addrlen;
+
+ return TEMP_FAILURE_RETRY(connect(fd, addr, addrlen));
+}
+
+JNIEXPORT int JVM_GetHostName(char* name, int namelen) {
+ LOG(DEBUG) << "JVM_GetHostName name=" << name << ", namelen=" << namelen;
+
+ return TEMP_FAILURE_RETRY(gethostname(name, namelen));
+}
+
+JNIEXPORT jstring JVM_InternString(JNIEnv* env, jstring jstr) {
+ LOG(DEBUG) << "JVM_InternString env=" << env << ", jstr=" << jstr;
+ art::ScopedFastNativeObjectAccess soa(env);
+ art::mirror::String* s = soa.Decode<art::mirror::String*>(jstr);
+ art::mirror::String* result = s->Intern();
+ return soa.AddLocalReference<jstring>(result);
+}
+
+JNIEXPORT jlong JVM_FreeMemory(void) {
+ return art::Runtime::Current()->GetHeap()->GetFreeMemory();
+}
+
+JNIEXPORT jlong JVM_TotalMemory(void) {
+ return art::Runtime::Current()->GetHeap()->GetTotalMemory();
+}
+
+JNIEXPORT jlong JVM_MaxMemory(void) {
+ return art::Runtime::Current()->GetHeap()->GetMaxMemory();
+}
+
+JNIEXPORT void JVM_GC(void) {
+ if (art::Runtime::Current()->IsExplicitGcDisabled()) {
+ LOG(INFO) << "Explicit GC skipped.";
+ return;
+ }
+ art::Runtime::Current()->GetHeap()->CollectGarbage(false);
+}
+
+JNIEXPORT __attribute__((noreturn)) void JVM_Exit(jint status) {
+ LOG(INFO) << "System.exit called, status: " << status;
+ art::Runtime::Current()->CallExitHook(status);
+ exit(status);
+}
+
+static void SetLdLibraryPath(JNIEnv* env, jstring javaLdLibraryPath) {
+#ifdef __ANDROID__
+ if (javaLdLibraryPath != nullptr) {
+ ScopedUtfChars ldLibraryPath(env, javaLdLibraryPath);
+ if (ldLibraryPath.c_str() != nullptr) {
+ android_update_LD_LIBRARY_PATH(ldLibraryPath.c_str());
+ }
+ }
+
+#else
+ LOG(WARNING) << "android_update_LD_LIBRARY_PATH not found; .so dependencies will not work!";
+ UNUSED(javaLdLibraryPath, env);
+#endif
+}
+
+
+JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env, jstring javaFilename, jobject javaLoader,
+ jboolean isSharedNamespace, jstring javaLibrarySearchPath,
+ jstring javaLibraryPermittedPath) {
+ ScopedUtfChars filename(env, javaFilename);
+ if (filename.c_str() == NULL) {
+ return NULL;
+ }
+
+ int32_t target_sdk_version = art::Runtime::Current()->GetTargetSdkVersion();
+
+ // Starting with N nativeLoad uses classloader local
+ // linker namespace instead of global LD_LIBRARY_PATH
+ // (23 is Marshmallow)
+ if (target_sdk_version <= 23) {
+ SetLdLibraryPath(env, javaLibrarySearchPath);
+ }
+
+ std::string error_msg;
+ {
+ art::ScopedObjectAccess soa(env);
+ art::StackHandleScope<1> hs(soa.Self());
+ art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
+ bool success = vm->LoadNativeLibrary(env,
+ filename.c_str(),
+ javaLoader,
+ isSharedNamespace == JNI_TRUE,
+ javaLibrarySearchPath,
+ javaLibraryPermittedPath,
+ &error_msg);
+ if (success) {
+ return nullptr;
+ }
+ }
+
+ // Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
+ env->ExceptionClear();
+ return env->NewStringUTF(error_msg.c_str());
+}
+
+JNIEXPORT void JVM_StartThread(JNIEnv* env, jobject jthread, jlong stack_size, jboolean daemon) {
+ art::Thread::CreateNativeThread(env, jthread, stack_size, daemon == JNI_TRUE);
+}
+
+JNIEXPORT void JVM_SetThreadPriority(JNIEnv* env, jobject jthread, jint prio) {
+ art::ScopedObjectAccess soa(env);
+ art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
+ art::Thread* thread = art::Thread::FromManagedThread(soa, jthread);
+ if (thread != NULL) {
+ thread->SetNativePriority(prio);
+ }
+}
+
+JNIEXPORT void JVM_Yield(JNIEnv* env ATTRIBUTE_UNUSED, jclass threadClass ATTRIBUTE_UNUSED) {
+ sched_yield();
+}
+
+JNIEXPORT void JVM_Sleep(JNIEnv* env, jclass threadClass ATTRIBUTE_UNUSED,
+ jobject java_lock, jlong millis) {
+ art::ScopedFastNativeObjectAccess soa(env);
+ art::mirror::Object* lock = soa.Decode<art::mirror::Object*>(java_lock);
+ art::Monitor::Wait(art::Thread::Current(), lock, millis, 0, true, art::kSleeping);
+}
+
+JNIEXPORT jobject JVM_CurrentThread(JNIEnv* env, jclass unused ATTRIBUTE_UNUSED) {
+ art::ScopedFastNativeObjectAccess soa(env);
+ return soa.AddLocalReference<jobject>(soa.Self()->GetPeer());
+}
+
+JNIEXPORT void JVM_Interrupt(JNIEnv* env, jobject jthread) {
+ art::ScopedFastNativeObjectAccess soa(env);
+ art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
+ art::Thread* thread = art::Thread::FromManagedThread(soa, jthread);
+ if (thread != nullptr) {
+ thread->Interrupt(soa.Self());
+ }
+}
+
+JNIEXPORT jboolean JVM_IsInterrupted(JNIEnv* env, jobject jthread, jboolean clearInterrupted) {
+ if (clearInterrupted) {
+ return static_cast<art::JNIEnvExt*>(env)->self->Interrupted() ? JNI_TRUE : JNI_FALSE;
+ } else {
+ art::ScopedFastNativeObjectAccess soa(env);
+ art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
+ art::Thread* thread = art::Thread::FromManagedThread(soa, jthread);
+ return (thread != nullptr) ? thread->IsInterrupted() : JNI_FALSE;
+ }
+}
+
+JNIEXPORT jboolean JVM_HoldsLock(JNIEnv* env, jclass unused ATTRIBUTE_UNUSED, jobject jobj) {
+ art::ScopedObjectAccess soa(env);
+ art::mirror::Object* object = soa.Decode<art::mirror::Object*>(jobj);
+ if (object == NULL) {
+ art::ThrowNullPointerException("object == null");
+ return JNI_FALSE;
+ }
+ return soa.Self()->HoldsLock(object);
+}
+
+JNIEXPORT void JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring java_name) {
+ ScopedUtfChars name(env, java_name);
+ {
+ art::ScopedObjectAccess soa(env);
+ if (soa.Decode<art::mirror::Object*>(jthread) == soa.Self()->GetPeer()) {
+ soa.Self()->SetThreadName(name.c_str());
+ return;
+ }
+ }
+ // Suspend thread to avoid it from killing itself while we set its name. We don't just hold the
+ // thread list lock to avoid this, as setting the thread name causes mutator to lock/unlock
+ // in the DDMS send code.
+ art::ThreadList* thread_list = art::Runtime::Current()->GetThreadList();
+ bool timed_out;
+ // Take suspend thread lock to avoid races with threads trying to suspend this one.
+ art::Thread* thread;
+ {
+ thread = thread_list->SuspendThreadByPeer(jthread, true, false, &timed_out);
+ }
+ if (thread != NULL) {
+ {
+ art::ScopedObjectAccess soa(env);
+ thread->SetThreadName(name.c_str());
+ }
+ thread_list->Resume(thread, false);
+ } else if (timed_out) {
+ LOG(ERROR) << "Trying to set thread name to '" << name.c_str() << "' failed as the thread "
+ "failed to suspend within a generous timeout.";
+ }
+}
+
+JNIEXPORT jint JVM_IHashCode(JNIEnv* env ATTRIBUTE_UNUSED,
+ jobject javaObject ATTRIBUTE_UNUSED) {
+ UNIMPLEMENTED(FATAL) << "JVM_IHashCode is not implemented";
+ return 0;
+}
+
+JNIEXPORT jlong JVM_NanoTime(JNIEnv* env ATTRIBUTE_UNUSED, jclass unused ATTRIBUTE_UNUSED) {
+ UNIMPLEMENTED(FATAL) << "JVM_NanoTime is not implemented";
+ return 0L;
+}
+
+JNIEXPORT void JVM_ArrayCopy(JNIEnv* /* env */, jclass /* unused */, jobject /* javaSrc */,
+ jint /* srcPos */, jobject /* javaDst */, jint /* dstPos */,
+ jint /* length */) {
+ UNIMPLEMENTED(FATAL) << "JVM_ArrayCopy is not implemented";
+}
+
+JNIEXPORT jint JVM_FindSignal(const char* name ATTRIBUTE_UNUSED) {
+ LOG(FATAL) << "JVM_FindSignal is not implemented";
+ return 0;
+}
+
+JNIEXPORT void* JVM_RegisterSignal(jint signum ATTRIBUTE_UNUSED, void* handler ATTRIBUTE_UNUSED) {
+ LOG(FATAL) << "JVM_RegisterSignal is not implemented";
+ return nullptr;
+}
+
+JNIEXPORT jboolean JVM_RaiseSignal(jint signum ATTRIBUTE_UNUSED) {
+ LOG(FATAL) << "JVM_RaiseSignal is not implemented";
+ return JNI_FALSE;
+}
+
+JNIEXPORT __attribute__((noreturn)) void JVM_Halt(jint code) {
+ exit(code);
+}
+
+JNIEXPORT jboolean JVM_IsNaN(jdouble d) {
+ return isnan(d);
+}
diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc
index c32d76c..5b90c6a 100644
--- a/runtime/parsed_options_test.cc
+++ b/runtime/parsed_options_test.cc
@@ -36,18 +36,28 @@
void* test_abort = reinterpret_cast<void*>(0xb);
void* test_exit = reinterpret_cast<void*>(0xc);
- std::string lib_core(CommonRuntimeTest::GetLibCoreDexFileName());
-
std::string boot_class_path;
+ std::string class_path;
boot_class_path += "-Xbootclasspath:";
- boot_class_path += lib_core;
+
+ bool first_dex_file = true;
+ for (const std::string &dex_file_name :
+ CommonRuntimeTest::GetLibCoreDexFileNames()) {
+ if (!first_dex_file) {
+ class_path += ":";
+ } else {
+ first_dex_file = false;
+ }
+ class_path += dex_file_name;
+ }
+ boot_class_path += class_path;
RuntimeOptions options;
options.push_back(std::make_pair(boot_class_path.c_str(), nullptr));
options.push_back(std::make_pair("-classpath", nullptr));
- options.push_back(std::make_pair(lib_core.c_str(), nullptr));
+ options.push_back(std::make_pair(class_path.c_str(), nullptr));
options.push_back(std::make_pair("-cp", nullptr));
- options.push_back(std::make_pair(lib_core.c_str(), nullptr));
+ options.push_back(std::make_pair(class_path.c_str(), nullptr));
options.push_back(std::make_pair("-Ximage:boot_image", nullptr));
options.push_back(std::make_pair("-Xcheck:jni", nullptr));
options.push_back(std::make_pair("-Xms2048", nullptr));
@@ -71,8 +81,8 @@
#define EXPECT_PARSED_EQ(expected, actual_key) EXPECT_EQ(expected, map.GetOrDefault(actual_key))
#define EXPECT_PARSED_EXISTS(actual_key) EXPECT_TRUE(map.Exists(actual_key))
- EXPECT_PARSED_EQ(lib_core, Opt::BootClassPath);
- EXPECT_PARSED_EQ(lib_core, Opt::ClassPath);
+ EXPECT_PARSED_EQ(class_path, Opt::BootClassPath);
+ EXPECT_PARSED_EQ(class_path, Opt::ClassPath);
EXPECT_PARSED_EQ(std::string("boot_image"), Opt::Image);
EXPECT_PARSED_EXISTS(Opt::CheckJni);
EXPECT_PARSED_EQ(2048U, Opt::MemoryInitialSize);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index d241112..98aa8d8 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -543,15 +543,15 @@
// Use !IsAotCompiler so that we get test coverage, tests are never the zygote.
if (!IsAotCompiler()) {
ScopedObjectAccess soa(self);
- gc::space::ImageSpace* image_space = heap_->GetBootImageSpace();
- if (image_space != nullptr) {
+ std::vector<gc::space::ImageSpace*> image_spaces = heap_->GetBootImageSpaces();
+ for (gc::space::ImageSpace* image_space : image_spaces) {
ATRACE_BEGIN("AddImageStringsToTable");
GetInternTable()->AddImageStringsToTable(image_space);
ATRACE_END();
- ATRACE_BEGIN("MoveImageClassesToClassTable");
- GetClassLinker()->AddBootImageClassesToClassTable();
- ATRACE_END();
}
+ ATRACE_BEGIN("MoveImageClassesToClassTable");
+ GetClassLinker()->AddBootImageClassesToClassTable();
+ ATRACE_END();
}
// If we are the zygote then we need to wait until after forking to create the code cache
@@ -560,7 +560,7 @@
CreateJit();
}
- if (!IsImageDex2OatEnabled() || !GetHeap()->HasImageSpace()) {
+ if (!IsImageDex2OatEnabled() || !GetHeap()->HasBootImageSpace()) {
ScopedObjectAccess soa(self);
StackHandleScope<1> hs(soa.Self());
auto klass(hs.NewHandle<mirror::Class>(mirror::Class::GetJavaLangClass()));
@@ -589,6 +589,7 @@
PreInitializeNativeBridge(".");
}
InitNonZygoteOrPostFork(self->GetJniEnv(),
+ /* is_system_server */ false,
NativeBridgeAction::kInitialize,
GetInstructionSetString(kRuntimeISA));
}
@@ -677,7 +678,8 @@
#endif
}
-void Runtime::InitNonZygoteOrPostFork(JNIEnv* env, NativeBridgeAction action, const char* isa) {
+void Runtime::InitNonZygoteOrPostFork(
+ JNIEnv* env, bool is_system_server, NativeBridgeAction action, const char* isa) {
is_zygote_ = false;
if (is_native_bridge_loaded_) {
@@ -699,7 +701,7 @@
// before fork aren't attributed to an app.
heap_->ResetGcPerformanceInfo();
- if (!safe_mode_ && jit_options_->UseJIT() && jit_.get() == nullptr) {
+ if (!is_system_server && !safe_mode_ && jit_options_->UseJIT() && jit_.get() == nullptr) {
// Note that when running ART standalone (not zygote, nor zygote fork),
// the jit may have already been created.
CreateJit();
@@ -747,61 +749,96 @@
VLOG(startup) << "Runtime::StartDaemonThreads exiting";
}
+// Attempts to open dex files from image(s). Given the image location, try to find the oat file
+// and open it to get the stored dex file. If the image is the first for a multi-image boot
+// classpath, go on and also open the other images.
static bool OpenDexFilesFromImage(const std::string& image_location,
std::vector<std::unique_ptr<const DexFile>>* dex_files,
size_t* failures) {
DCHECK(dex_files != nullptr) << "OpenDexFilesFromImage: out-param is nullptr";
- std::string system_filename;
- bool has_system = false;
- std::string cache_filename_unused;
- bool dalvik_cache_exists_unused;
- bool has_cache_unused;
- bool is_global_cache_unused;
- bool found_image = gc::space::ImageSpace::FindImageFilename(image_location.c_str(),
- kRuntimeISA,
- &system_filename,
- &has_system,
- &cache_filename_unused,
- &dalvik_cache_exists_unused,
- &has_cache_unused,
- &is_global_cache_unused);
- *failures = 0;
- if (!found_image || !has_system) {
- return false;
- }
- std::string error_msg;
- // We are falling back to non-executable use of the oat file because patching failed, presumably
- // due to lack of space.
- std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(system_filename.c_str());
- std::string oat_location = ImageHeader::GetOatLocationFromImageLocation(image_location.c_str());
- std::unique_ptr<File> file(OS::OpenFileForReading(oat_filename.c_str()));
- if (file.get() == nullptr) {
- return false;
- }
- std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file.release(), false, false, &error_msg));
- if (elf_file.get() == nullptr) {
- return false;
- }
- std::unique_ptr<const OatFile> oat_file(
- OatFile::OpenWithElfFile(elf_file.release(), oat_location, nullptr, &error_msg));
- if (oat_file == nullptr) {
- LOG(WARNING) << "Unable to use '" << oat_filename << "' because " << error_msg;
- return false;
- }
- for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
- if (oat_dex_file == nullptr) {
- *failures += 1;
- continue;
+ // Use a work-list approach, so that we can easily reuse the opening code.
+ std::vector<std::string> image_locations;
+ image_locations.push_back(image_location);
+
+ for (size_t index = 0; index < image_locations.size(); ++index) {
+ std::string system_filename;
+ bool has_system = false;
+ std::string cache_filename_unused;
+ bool dalvik_cache_exists_unused;
+ bool has_cache_unused;
+ bool is_global_cache_unused;
+ bool found_image = gc::space::ImageSpace::FindImageFilename(image_locations[index].c_str(),
+ kRuntimeISA,
+ &system_filename,
+ &has_system,
+ &cache_filename_unused,
+ &dalvik_cache_exists_unused,
+ &has_cache_unused,
+ &is_global_cache_unused);
+
+ if (!found_image || !has_system) {
+ return false;
}
- std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
- if (dex_file.get() == nullptr) {
- *failures += 1;
- } else {
- dex_files->push_back(std::move(dex_file));
+
+ // We are falling back to non-executable use of the oat file because patching failed, presumably
+ // due to lack of space.
+ std::string oat_filename =
+ ImageHeader::GetOatLocationFromImageLocation(system_filename.c_str());
+ std::string oat_location =
+ ImageHeader::GetOatLocationFromImageLocation(image_locations[index].c_str());
+ // Note: in the multi-image case, the image location may end in ".jar," and not ".art." Handle
+ // that here.
+ if (EndsWith(oat_location, ".jar")) {
+ oat_location.replace(oat_location.length() - 3, 3, "oat");
}
+
+ std::unique_ptr<File> file(OS::OpenFileForReading(oat_filename.c_str()));
+ if (file.get() == nullptr) {
+ return false;
+ }
+ std::string error_msg;
+ std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file.release(), false, false, &error_msg));
+ if (elf_file.get() == nullptr) {
+ return false;
+ }
+ std::unique_ptr<const OatFile> oat_file(
+ OatFile::OpenWithElfFile(elf_file.release(), oat_location, nullptr, &error_msg));
+ if (oat_file == nullptr) {
+ LOG(WARNING) << "Unable to use '" << oat_filename << "' because " << error_msg;
+ return false;
+ }
+
+ for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
+ if (oat_dex_file == nullptr) {
+ *failures += 1;
+ continue;
+ }
+ std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+ if (dex_file.get() == nullptr) {
+ *failures += 1;
+ } else {
+ dex_files->push_back(std::move(dex_file));
+ }
+ }
+
+ if (index == 0) {
+ // First file. See if this is a multi-image environment, and if so, enqueue the other images.
+ const OatHeader& boot_oat_header = oat_file->GetOatHeader();
+ const char* boot_cp = boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPath);
+ if (boot_cp != nullptr) {
+ std::vector<std::string> cp;
+ Split(boot_cp, ':', &cp);
+
+ if (cp.size() > 1) {
+ // More images, enqueue (skipping the first).
+ image_locations.insert(image_locations.end(), cp.begin() + 1, cp.end());
+ }
+ }
+ }
+
+ Runtime::Current()->GetOatFileManager().RegisterOatFile(std::move(oat_file));
}
- Runtime::Current()->GetOatFileManager().RegisterOatFile(std::move(oat_file));
return true;
}
@@ -939,7 +976,7 @@
runtime_options.GetOrDefault(Opt::HSpaceCompactForOOMMinIntervalsMs));
ATRACE_END();
- if (heap_->GetBootImageSpace() == nullptr && !allow_dex_file_fallback_) {
+ if (!heap_->HasBootImageSpace() && !allow_dex_file_fallback_) {
LOG(ERROR) << "Dex file fallback disabled, cannot continue without image.";
ATRACE_END();
return false;
@@ -1047,7 +1084,7 @@
CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U);
class_linker_ = new ClassLinker(intern_table_);
- if (GetHeap()->HasImageSpace()) {
+ if (GetHeap()->HasBootImageSpace()) {
ATRACE_BEGIN("InitFromImage");
std::string error_msg;
bool result = class_linker_->InitFromImage(&error_msg);
@@ -1056,9 +1093,13 @@
LOG(ERROR) << "Could not initialize from image: " << error_msg;
return false;
}
+ /* TODO: Modify check to support multiple image spaces and reenable. b/26317072
if (kIsDebugBuild) {
- GetHeap()->GetBootImageSpace()->VerifyImageAllocations();
+ for (auto image_space : GetHeap()->GetBootImageSpaces()) {
+ image_space->VerifyImageAllocations();
+ }
}
+ */
if (boot_class_path_string_.empty()) {
// The bootclasspath is not explicitly specified: construct it from the loaded dex files.
const std::vector<const DexFile*>& boot_class_path = GetClassLinker()->GetBootClassPath();
@@ -1204,14 +1245,17 @@
// First set up JniConstants, which is used by both the runtime's built-in native
// methods and libcore.
JniConstants::init(env);
- WellKnownClasses::Init(env);
// Then set up the native methods provided by the runtime itself.
RegisterRuntimeNativeMethods(env);
- // Then set up libcore, which is just a regular JNI library with a regular JNI_OnLoad.
- // Most JNI libraries can just use System.loadLibrary, but libcore can't because it's
- // the library that implements System.loadLibrary!
+ // Initialize classes used in JNI. The initialization requires runtime native
+ // methods to be loaded first.
+ WellKnownClasses::Init(env);
+
+ // Then set up libjavacore / libopenjdk, which are just a regular JNI libraries with
+ // a regular JNI_OnLoad. Most JNI libraries can just use System.loadLibrary, but
+ // libcore can't because it's the library that implements System.loadLibrary!
{
std::string error_msg;
if (!java_vm_->LoadNativeLibrary(env, "libjavacore.so", nullptr,
@@ -1220,6 +1264,14 @@
LOG(FATAL) << "LoadNativeLibrary failed for \"libjavacore.so\": " << error_msg;
}
}
+ {
+ std::string error_msg;
+ if (!java_vm_->LoadNativeLibrary(env, "libopenjdk.so", nullptr,
+ /* is_shared_namespace */ false,
+ nullptr, nullptr, &error_msg)) {
+ LOG(FATAL) << "LoadNativeLibrary failed for \"libopenjdk.so\": " << error_msg;
+ }
+ }
// Initialize well known classes that may invoke runtime native methods.
WellKnownClasses::LateInit(env);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index c2ee577..20acffb 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -457,7 +457,8 @@
void PreZygoteFork();
bool InitZygote();
- void InitNonZygoteOrPostFork(JNIEnv* env, NativeBridgeAction action, const char* isa);
+ void InitNonZygoteOrPostFork(
+ JNIEnv* env, bool is_system_server, NativeBridgeAction action, const char* isa);
const instrumentation::Instrumentation* GetInstrumentation() const {
return &instrumentation_;
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index a47a08e..8300921 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -140,6 +140,7 @@
jfieldID WellKnownClasses::java_lang_Thread_uncaughtHandler;
jfieldID WellKnownClasses::java_lang_Thread_nativePeer;
jfieldID WellKnownClasses::java_lang_ThreadGroup_groups;
+jfieldID WellKnownClasses::java_lang_ThreadGroup_ngroups;
jfieldID WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup;
jfieldID WellKnownClasses::java_lang_ThreadGroup_name;
jfieldID WellKnownClasses::java_lang_ThreadGroup_parent;
@@ -268,7 +269,7 @@
java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
java_lang_Thread_run = CacheMethod(env, java_lang_Thread, false, "run", "()V");
java_lang_Thread__UncaughtExceptionHandler_uncaughtException = CacheMethod(env, java_lang_Thread__UncaughtExceptionHandler, false, "uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
- java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "removeThread", "(Ljava/lang/Thread;)V");
+ java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "threadTerminated", "(Ljava/lang/Thread;)V");
java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "<init>", "(JI)V");
libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(env, libcore_reflect_AnnotationFactory, true, "createAnnotation", "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;");
libcore_reflect_AnnotationMember_init = CacheMethod(env, libcore_reflect_AnnotationMember, false, "<init>", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V");
@@ -340,9 +341,10 @@
java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;");
java_lang_Thread_name = CacheField(env, java_lang_Thread, false, "name", "Ljava/lang/String;");
java_lang_Thread_priority = CacheField(env, java_lang_Thread, false, "priority", "I");
- java_lang_Thread_uncaughtHandler = CacheField(env, java_lang_Thread, false, "uncaughtHandler", "Ljava/lang/Thread$UncaughtExceptionHandler;");
+ java_lang_Thread_uncaughtHandler = CacheField(env, java_lang_Thread, false, "uncaughtExceptionHandler", "Ljava/lang/Thread$UncaughtExceptionHandler;");
java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J");
- java_lang_ThreadGroup_groups = CacheField(env, java_lang_ThreadGroup, false, "groups", "Ljava/util/List;");
+ java_lang_ThreadGroup_groups = CacheField(env, java_lang_ThreadGroup, false, "groups", "[Ljava/lang/ThreadGroup;");
+ java_lang_ThreadGroup_ngroups = CacheField(env, java_lang_ThreadGroup, false, "ngroups", "I");
java_lang_ThreadGroup_mainThreadGroup = CacheField(env, java_lang_ThreadGroup, true, "mainThreadGroup", "Ljava/lang/ThreadGroup;");
java_lang_ThreadGroup_name = CacheField(env, java_lang_ThreadGroup, false, "name", "Ljava/lang/String;");
java_lang_ThreadGroup_parent = CacheField(env, java_lang_ThreadGroup, false, "parent", "Ljava/lang/ThreadGroup;");
@@ -350,13 +352,13 @@
java_lang_Throwable_cause = CacheField(env, java_lang_Throwable, false, "cause", "Ljava/lang/Throwable;");
java_lang_Throwable_detailMessage = CacheField(env, java_lang_Throwable, false, "detailMessage", "Ljava/lang/String;");
java_lang_Throwable_stackTrace = CacheField(env, java_lang_Throwable, false, "stackTrace", "[Ljava/lang/StackTraceElement;");
- java_lang_Throwable_stackState = CacheField(env, java_lang_Throwable, false, "stackState", "Ljava/lang/Object;");
+ java_lang_Throwable_stackState = CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;");
java_lang_Throwable_suppressedExceptions = CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;");
java_lang_reflect_AbstractMethod_artMethod = CacheField(env, java_lang_reflect_AbstractMethod, false, "artMethod", "J");
java_lang_reflect_Proxy_h = CacheField(env, java_lang_reflect_Proxy, false, "h", "Ljava/lang/reflect/InvocationHandler;");
java_nio_DirectByteBuffer_capacity = CacheField(env, java_nio_DirectByteBuffer, false, "capacity", "I");
- java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "effectiveDirectAddress", "J");
- java_util_ArrayList_array = CacheField(env, java_util_ArrayList, false, "array", "[Ljava/lang/Object;");
+ java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "address", "J");
+ java_util_ArrayList_array = CacheField(env, java_util_ArrayList, false, "elementData", "[Ljava/lang/Object;");
java_util_ArrayList_size = CacheField(env, java_util_ArrayList, false, "size", "I");
java_util_Collections_EMPTY_LIST = CacheField(env, java_util_Collections, true, "EMPTY_LIST", "Ljava/util/List;");
libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(env, libcore_util_EmptyArray, true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index c856291..55158a7 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -153,6 +153,7 @@
static jfieldID java_lang_Thread_uncaughtHandler;
static jfieldID java_lang_Thread_nativePeer;
static jfieldID java_lang_ThreadGroup_groups;
+ static jfieldID java_lang_ThreadGroup_ngroups;
static jfieldID java_lang_ThreadGroup_mainThreadGroup;
static jfieldID java_lang_ThreadGroup_name;
static jfieldID java_lang_ThreadGroup_parent;
diff --git a/runtime/zip_archive_test.cc b/runtime/zip_archive_test.cc
index aded30c..4fc7ee2 100644
--- a/runtime/zip_archive_test.cc
+++ b/runtime/zip_archive_test.cc
@@ -32,7 +32,7 @@
TEST_F(ZipArchiveTest, FindAndExtract) {
std::string error_msg;
- std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(GetLibCoreDexFileName().c_str(), &error_msg));
+ std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(GetLibCoreDexFileNames()[0].c_str(), &error_msg));
ASSERT_TRUE(zip_archive.get() != nullptr) << error_msg;
ASSERT_TRUE(error_msg.empty());
std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find("classes.dex", &error_msg));
diff --git a/test/003-omnibus-opcodes/expected.txt b/test/003-omnibus-opcodes/expected.txt
index b591a7a..ee25ec1 100644
--- a/test/003-omnibus-opcodes/expected.txt
+++ b/test/003-omnibus-opcodes/expected.txt
@@ -31,15 +31,7 @@
FloatMath.checkConvI
FloatMath.checkConvL
FloatMath.checkConvF
- 0: -2.0054409E9
- 1: -8.613303E18
- 2: -3.1415927
--2.0054409E9, -8.6133031E18, -3.1415927
FloatMath.checkConvD
- 0: -2.005440939E9
- 1: -8.613303245920329E18
- 2: 123.45600128173828
--2.005440939E9, -8.6133032459203287E18, 123.4560012817382
FloatMath.checkConsts
FloatMath.jlmTests
IntMath.testIntCompare
diff --git a/test/003-omnibus-opcodes/src/FloatMath.java b/test/003-omnibus-opcodes/src/FloatMath.java
index a0bc9f4..96befe9 100644
--- a/test/003-omnibus-opcodes/src/FloatMath.java
+++ b/test/003-omnibus-opcodes/src/FloatMath.java
@@ -245,10 +245,9 @@
}
static void checkConvF(float[] results) {
System.out.println("FloatMath.checkConvF");
- // TODO: Main.assertTrue values
- for (int i = 0; i < results.length; i++)
- System.out.println(" " + i + ": " + results[i]);
- System.out.println("-2.0054409E9, -8.6133031E18, -3.1415927");
+ Main.assertTrue(results[0] == -2.0054409E9f);
+ Main.assertTrue(results[1] == -8.613303E18f);
+ Main.assertTrue(results[2] == -3.1415927f);
}
static double[] convD(int i, long l, float f) {
@@ -260,10 +259,9 @@
}
static void checkConvD(double[] results) {
System.out.println("FloatMath.checkConvD");
- // TODO: Main.assertTrue values
- for (int i = 0; i < results.length; i++)
- System.out.println(" " + i + ": " + results[i]);
- System.out.println("-2.005440939E9, -8.6133032459203287E18, 123.4560012817382");
+ Main.assertTrue(results[0] == -2.005440939E9);
+ Main.assertTrue(results[1] == -8.6133032459203287E18);
+ Main.assertTrue(results[2] == 123.45600128173828);
}
static void checkConsts() {
diff --git a/test/031-class-attributes/expected.txt b/test/031-class-attributes/expected.txt
index de99872..72656ae 100644
--- a/test/031-class-attributes/expected.txt
+++ b/test/031-class-attributes/expected.txt
@@ -84,7 +84,7 @@
enclosingCon: null
enclosingMeth: null
modifiers: 1
- package: package otherpackage
+ package: package otherpackage, Unknown, version 0.0
declaredClasses: [0]
member classes: [0]
isAnnotation: false
diff --git a/test/034-call-null/expected.txt b/test/034-call-null/expected.txt
index 343226f..4e0281e 100644
--- a/test/034-call-null/expected.txt
+++ b/test/034-call-null/expected.txt
@@ -1,2 +1,2 @@
-java.lang.NullPointerException: Attempt to invoke direct method 'void Main.doStuff(int, int[][], java.lang.String, java.lang.String[][])' on a null object reference
+Exception in thread "main" java.lang.NullPointerException: Attempt to invoke direct method 'void Main.doStuff(int, int[][], java.lang.String, java.lang.String[][])' on a null object reference
at Main.main(Main.java:26)
diff --git a/test/038-inner-null/expected.txt b/test/038-inner-null/expected.txt
index ba411f0..2e92564 100644
--- a/test/038-inner-null/expected.txt
+++ b/test/038-inner-null/expected.txt
@@ -1,4 +1,4 @@
new Special()
-java.lang.NullPointerException: Attempt to invoke virtual method 'void Main$Blort.repaint()' on a null object reference
+Exception in thread "main" java.lang.NullPointerException: Attempt to invoke virtual method 'void Main$Blort.repaint()' on a null object reference
at Main$Special.callInner(Main.java:31)
at Main.main(Main.java:20)
diff --git a/test/042-new-instance/expected.txt b/test/042-new-instance/expected.txt
index 7d843d1..c5de313 100644
--- a/test/042-new-instance/expected.txt
+++ b/test/042-new-instance/expected.txt
@@ -9,3 +9,4 @@
Cons got expected PackageAccess complaint
Cons got expected InstantationException
Cons got expected PackageAccess2 complaint
+Cons ConstructorAccess succeeded
diff --git a/test/042-new-instance/src/Main.java b/test/042-new-instance/src/Main.java
index b0a5fd4..8cd6b2e 100644
--- a/test/042-new-instance/src/Main.java
+++ b/test/042-new-instance/src/Main.java
@@ -156,6 +156,14 @@
ex.printStackTrace();
}
+ // should succeed
+ try {
+ otherpackage.ConstructorAccess.newConstructorInstance();
+ System.out.println("Cons ConstructorAccess succeeded");
+ } catch (Exception ex) {
+ System.err.println("Cons ConstructorAccess failed");
+ ex.printStackTrace();
+ }
}
class InnerClass {
@@ -173,7 +181,6 @@
public LocalClass2() {}
}
-
class LocalClass3 {
public static void main() {
try {
diff --git a/test/042-new-instance/src/otherpackage/ConstructorAccess.java b/test/042-new-instance/src/otherpackage/ConstructorAccess.java
new file mode 100644
index 0000000..a74e9a0
--- /dev/null
+++ b/test/042-new-instance/src/otherpackage/ConstructorAccess.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package otherpackage;
+
+import java.lang.reflect.Constructor;
+
+public class ConstructorAccess {
+
+ static class Inner {
+ Inner() {}
+ }
+
+ // Test for regression in b/25817515. Inner class constructor should
+ // be accessible from this static method, but if we over-shoot and check
+ // accessibility using the frame below (in Main class), we will see an
+ // IllegalAccessException from #newInstance
+ static public void newConstructorInstance() throws Exception {
+ Class c = Inner.class;
+ Constructor cons = c.getDeclaredConstructor((Class[]) null);
+ Object obj = cons.newInstance();
+ }
+}
diff --git a/test/046-reflect/expected.txt b/test/046-reflect/expected.txt
index d657d44..06932b9 100644
--- a/test/046-reflect/expected.txt
+++ b/test/046-reflect/expected.txt
@@ -96,8 +96,8 @@
got expected exception for Constructor.newInstance
ReflectTest done!
public method
-static java.lang.Object java.util.Collections.checkType(java.lang.Object,java.lang.Class) accessible=false
-static java.lang.Object java.util.Collections.checkType(java.lang.Object,java.lang.Class) accessible=true
+private static void java.util.Collections.swap(java.lang.Object[],int,int) accessible=false
+private static void java.util.Collections.swap(java.lang.Object[],int,int) accessible=true
checkType invoking null
checkType got expected exception
calling const-class FieldNoisyInitUser.class
diff --git a/test/046-reflect/src/Main.java b/test/046-reflect/src/Main.java
index 0c90109..67a0d11 100644
--- a/test/046-reflect/src/Main.java
+++ b/test/046-reflect/src/Main.java
@@ -407,12 +407,13 @@
System.out.println("ReflectTest done!");
}
- public static void checkType() {
+ public static void checkSwap() {
Method m;
+ final Object[] objects = new Object[2];
try {
- m = Collections.class.getDeclaredMethod("checkType",
- Object.class, Class.class);
+ m = Collections.class.getDeclaredMethod("swap",
+ Object[].class, int.class, int.class);
} catch (NoSuchMethodException nsme) {
nsme.printStackTrace();
return;
@@ -421,7 +422,7 @@
m.setAccessible(true);
System.out.println(m + " accessible=" + m.isAccessible());
try {
- m.invoke(null, new Object(), Object.class);
+ m.invoke(null, objects, 0, 1);
} catch (IllegalAccessException iae) {
iae.printStackTrace();
return;
@@ -432,7 +433,7 @@
try {
String s = "Should be ignored";
- m.invoke(s, new Object(), Object.class);
+ m.invoke(s, objects, 0, 1);
} catch (IllegalAccessException iae) {
iae.printStackTrace();
return;
@@ -443,7 +444,8 @@
try {
System.out.println("checkType invoking null");
- m.invoke(null, new Object(), int.class);
+ // Trigger an NPE at the target.
+ m.invoke(null, null, 0, 1);
System.out.println("ERROR: should throw InvocationTargetException");
} catch (InvocationTargetException ite) {
System.out.println("checkType got expected exception");
@@ -710,27 +712,27 @@
private static void checkGetDeclaredConstructor() {
try {
Method.class.getDeclaredConstructor().setAccessible(true);
- System.out.print("Didn't get an exception from Method.class.getDeclaredConstructor().setAccessible");
+ System.out.println("Didn't get an exception from Method.class.getDeclaredConstructor().setAccessible");
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
} catch (Exception e) {
- System.out.print(e);
+ System.out.println(e);
}
try {
Field.class.getDeclaredConstructor().setAccessible(true);
- System.out.print("Didn't get an exception from Field.class.getDeclaredConstructor().setAccessible");
+ System.out.println("Didn't get an exception from Field.class.getDeclaredConstructor().setAccessible");
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
} catch (Exception e) {
- System.out.print(e);
+ System.out.println(e);
}
try {
Class.class.getDeclaredConstructor().setAccessible(true);
- System.out.print("Didn't get an exception from Class.class.getDeclaredConstructor().setAccessible");
+ System.out.println("Didn't get an exception from Class.class.getDeclaredConstructor().setAccessible");
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
} catch (Exception e) {
- System.out.print(e);
+ System.out.println(e);
}
}
@@ -744,7 +746,7 @@
checkGetDeclaredConstructor();
checkAccess();
- checkType();
+ checkSwap();
checkClinitForFields();
checkClinitForMethods();
checkGeneric();
diff --git a/test/055-enum-performance/src/Main.java b/test/055-enum-performance/src/Main.java
index d5903af..d6bb211 100644
--- a/test/055-enum-performance/src/Main.java
+++ b/test/055-enum-performance/src/Main.java
@@ -20,7 +20,7 @@
throw new AssertionError();
} catch (InvocationTargetException expected) {
IllegalArgumentException iae = (IllegalArgumentException) expected.getCause();
- if (!iae.getMessage().equals("class java.lang.String is not an enum type")) {
+ if (!iae.getMessage().equals("class java.lang.String is not an enum type.")) {
throw new AssertionError();
}
}
diff --git a/test/063-process-manager/expected.txt b/test/063-process-manager/expected.txt
index 8360239..8c01bf0 100644
--- a/test/063-process-manager/expected.txt
+++ b/test/063-process-manager/expected.txt
@@ -4,12 +4,12 @@
spawning child
process manager: RUNNABLE
child died
-process manager: WAITING
+process manager: TIMED_WAITING
spawning child #2
spawning child
process manager: RUNNABLE
child died
-process manager: WAITING
+process manager: TIMED_WAITING
done!
diff --git a/test/063-process-manager/src/Main.java b/test/063-process-manager/src/Main.java
index 68bf878..e9e522c 100644
--- a/test/063-process-manager/src/Main.java
+++ b/test/063-process-manager/src/Main.java
@@ -30,7 +30,7 @@
traces.entrySet()) {
Thread t = entry.getKey();
String name = t.getName();
- if (name.equals("java.lang.ProcessManager")) {
+ if (name.indexOf("process reaper") >= 0) {
System.out.println("process manager: " + t.getState());
found = true;
}
diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java
index 5913c40..af25d9b 100644
--- a/test/082-inline-execute/src/Main.java
+++ b/test/082-inline-execute/src/Main.java
@@ -803,7 +803,7 @@
Assert.assertEquals(Math.round(-2.5d), -2l);
Assert.assertEquals(Math.round(-2.9d), -3l);
Assert.assertEquals(Math.round(-3.0d), -3l);
- Assert.assertEquals(Math.round(0.49999999999999994d), 1l);
+ Assert.assertEquals(Math.round(0.49999999999999994d), 0l);
Assert.assertEquals(Math.round(Double.NaN), (long)+0.0d);
Assert.assertEquals(Math.round(Long.MAX_VALUE + 1.0d), Long.MAX_VALUE);
Assert.assertEquals(Math.round(Long.MIN_VALUE - 1.0d), Long.MIN_VALUE);
@@ -1034,7 +1034,7 @@
Assert.assertEquals(StrictMath.round(-2.5d), -2l);
Assert.assertEquals(StrictMath.round(-2.9d), -3l);
Assert.assertEquals(StrictMath.round(-3.0d), -3l);
- Assert.assertEquals(StrictMath.round(0.49999999999999994d), 1l);
+ Assert.assertEquals(StrictMath.round(0.49999999999999994d), 0l);
Assert.assertEquals(StrictMath.round(Double.NaN), (long)+0.0d);
Assert.assertEquals(StrictMath.round(Long.MAX_VALUE + 1.0d), Long.MAX_VALUE);
Assert.assertEquals(StrictMath.round(Long.MIN_VALUE - 1.0d), Long.MIN_VALUE);
diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt
index 0b87a4f..e4988c9 100644
--- a/test/100-reflect2/expected.txt
+++ b/test/100-reflect2/expected.txt
@@ -32,8 +32,8 @@
62 (class java.lang.Long)
14 (class java.lang.Short)
[java.lang.String(int,int,char[]), public java.lang.String(), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder)]
-[private final int java.lang.String.count, private int java.lang.String.hashCode, private static final char java.lang.String.REPLACEMENT_CHAR, private static final char[] java.lang.String.ASCII, private static final long java.lang.String.serialVersionUID, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER]
-[native void java.lang.String.getCharsNoCheck(int,int,char[],int), native void java.lang.String.setCharAt(int,char), private char java.lang.String.foldCase(char), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.failedBoundsCheck(int,int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.indexAndLength(int), private java.lang.StringIndexOutOfBoundsException java.lang.String.startEndAndLength(int,int), private native int java.lang.String.fastIndexOf(int,int), private native java.lang.String java.lang.String.fastSubstring(int,int), private static int java.lang.String.indexOf(java.lang.String,java.lang.String,int,int,char), public [B java.lang.String.getBytes(), public [B java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public [B java.lang.String.getBytes(java.nio.charset.Charset), public [Ljava.lang.String; java.lang.String.split(java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public native [C java.lang.String.toCharArray(), public native char java.lang.String.charAt(int), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int)]
+[private final int java.lang.String.count, private int java.lang.String.hash, private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields, private static final long java.lang.String.serialVersionUID, private static int java.lang.String.HASHING_SEED, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER]
+[int java.lang.String.hash32(), native void java.lang.String.getCharsNoCheck(int,int,char[],int), native void java.lang.String.setCharAt(int,char), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native int java.lang.String.fastIndexOf(int,int), private native java.lang.String java.lang.String.fastSubstring(int,int), private static int java.lang.String.getHashingSeed(), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int)]
[]
[interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
0
diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc
index b6b1c43..1337442 100644
--- a/test/117-nopatchoat/nopatchoat.cc
+++ b/test/117-nopatchoat/nopatchoat.cc
@@ -35,8 +35,9 @@
}
static bool isRelocationDeltaZero() {
- gc::space::ImageSpace* space = Runtime::Current()->GetHeap()->GetBootImageSpace();
- return space != nullptr && space->GetImageHeader().GetPatchDelta() == 0;
+ std::vector<gc::space::ImageSpace*> spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ return !spaces.empty() && spaces[0]->GetImageHeader().GetPatchDelta() == 0;
}
static bool hasExecutableOat(jclass cls) {
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 7762b2d..9bfe429 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -92,9 +92,10 @@
// detecting this.
#if __linux__
static bool IsPicImage() {
- gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetBootImageSpace();
- CHECK(image_space != nullptr); // We should be running with an image.
- const OatFile* oat_file = image_space->GetOatFile();
+ std::vector<gc::space::ImageSpace*> image_spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ CHECK(!image_spaces.empty()); // We should be running with an image.
+ const OatFile* oat_file = image_spaces[0]->GetOatFile();
CHECK(oat_file != nullptr); // We should have an oat file to go with the image.
return oat_file->IsPic();
}
diff --git a/test/137-cfi/src/Main.java b/test/137-cfi/src/Main.java
index dc3ef7e..5474c9b 100644
--- a/test/137-cfi/src/Main.java
+++ b/test/137-cfi/src/Main.java
@@ -117,7 +117,7 @@
// Could do reflection for the private pid field, but String parsing is easier.
String s = p.toString();
if (s.startsWith("Process[pid=")) {
- return Integer.parseInt(s.substring("Process[pid=".length(), s.length() - 1));
+ return Integer.parseInt(s.substring("Process[pid=".length(), s.indexOf(",")));
} else {
return -1;
}
diff --git a/test/464-checker-inline-sharpen-calls/src/Main.java b/test/464-checker-inline-sharpen-calls/src/Main.java
index 5080f142..2222e0f 100644
--- a/test/464-checker-inline-sharpen-calls/src/Main.java
+++ b/test/464-checker-inline-sharpen-calls/src/Main.java
@@ -16,6 +16,14 @@
public final class Main {
+ public final static class Helper {
+ private int foo = 3;
+
+ public int getFoo() {
+ return foo;
+ }
+ }
+
public void invokeVirtual() {
}
@@ -31,25 +39,25 @@
m.invokeVirtual();
}
- /// CHECK-START: int Main.inlineSharpenStringInvoke() ssa_builder (after)
- /// CHECK-DAG: <<Invoke:i\d+>> InvokeVirtual
+ /// CHECK-START: int Main.inlineSharpenHelperInvoke() ssa_builder (after)
+ /// CHECK-DAG: <<Invoke:i\d+>> InvokeVirtual {{.*\.getFoo.*}}
/// CHECK-DAG: Return [<<Invoke>>]
- /// CHECK-START: int Main.inlineSharpenStringInvoke() inliner (after)
- /// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: InvokeVirtual
+ /// CHECK-START: int Main.inlineSharpenHelperInvoke() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect {{.*\.getFoo.*}}
+ /// CHECK-NOT: InvokeVirtual {{.*\.getFoo.*}}
- /// CHECK-START: int Main.inlineSharpenStringInvoke() inliner (after)
+ /// CHECK-START: int Main.inlineSharpenHelperInvoke() inliner (after)
/// CHECK-DAG: <<Field:i\d+>> InstanceFieldGet
/// CHECK-DAG: Return [<<Field>>]
- public static int inlineSharpenStringInvoke() {
- return "Foo".length();
+ public static int inlineSharpenHelperInvoke() {
+ return new Helper().getFoo();
}
public static void main(String[] args) {
inlineSharpenInvokeVirtual(new Main());
- if (inlineSharpenStringInvoke() != 3) {
+ if (inlineSharpenHelperInvoke() != 3) {
throw new Error("Expected 3");
}
}
diff --git a/test/490-checker-inline/src/Main.java b/test/490-checker-inline/src/Main.java
index 21a0189..2e2deea 100644
--- a/test/490-checker-inline/src/Main.java
+++ b/test/490-checker-inline/src/Main.java
@@ -39,7 +39,7 @@
/// CHECK-DAG: InvokeInterface
/// CHECK-START: void Main.testMethod() inliner (after)
- /// CHECK-NOT: Invoke{{.*}}
+ /// CHECK-NOT: Invoke{{.*Object\.<init>.*}}
public static void testMethod() {
createMain().invokeVirtual();
diff --git a/test/492-checker-inline-invoke-interface/src/Main.java b/test/492-checker-inline-invoke-interface/src/Main.java
index a8b6307..3106ce4 100644
--- a/test/492-checker-inline-invoke-interface/src/Main.java
+++ b/test/492-checker-inline-invoke-interface/src/Main.java
@@ -32,14 +32,14 @@
}
/// CHECK-START: void Main.main(java.lang.String[]) ssa_builder (after)
- /// CHECK: InvokeStaticOrDirect
+ /// CHECK: InvokeStaticOrDirect {{.*Main.<init>.*}}
/// CHECK: InvokeInterface
/// CHECK-START: void Main.main(java.lang.String[]) inliner (before)
/// CHECK-NOT: ClinitCheck
/// CHECK-START: void Main.main(java.lang.String[]) inliner (after)
- /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: InvokeStaticOrDirect {{.*Main.<init>.*}}
/// CHECK-NOT: InvokeVirtual
/// CHECK-NOT: InvokeInterface
diff --git a/test/493-checker-inline-invoke-interface/src/Main.java b/test/493-checker-inline-invoke-interface/src/Main.java
index 44b727f..171405c 100644
--- a/test/493-checker-inline-invoke-interface/src/Main.java
+++ b/test/493-checker-inline-invoke-interface/src/Main.java
@@ -36,7 +36,7 @@
/// CHECK: InvokeInterface
/// CHECK-START: void Main.main(java.lang.String[]) inliner (after)
- /// CHECK-NOT: Invoke{{.*}}
+ /// CHECK-NOT: Invoke{{.*Object\.<init>.*}}
public static void main(String[] args) {
Itf itf = bar();
itf.foo();
diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java
index 3f65d5a..be666e9 100644
--- a/test/536-checker-intrinsic-optimization/src/Main.java
+++ b/test/536-checker-intrinsic-optimization/src/Main.java
@@ -47,7 +47,7 @@
}
/// CHECK-START-X86: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after)
- /// CHECK: InvokeVirtual
+ /// CHECK: InvokeVirtual {{.*\.equals.*}}
/// CHECK-NOT: test
public static boolean stringArgumentNotNull(Object obj) {
obj.getClass();
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index a616395..81cfb70 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -155,8 +155,14 @@
ifeq ($(ART_TEST_RUN_TEST_NO_IMAGE),true)
IMAGE_TYPES += no-image
endif
+ifeq ($(ART_TEST_RUN_TEST_MULTI_IMAGE),true)
+ IMAGE_TYPES := multiimage
+endif
ifeq ($(ART_TEST_PIC_IMAGE),true)
IMAGE_TYPES += picimage
+ ifeq ($(ART_TEST_RUN_TEST_MULTI_IMAGE),true)
+ IMAGE_TYPES := multipicimage
+ endif
endif
PICTEST_TYPES := npictest
ifeq ($(ART_TEST_PIC_TEST),true)
@@ -581,6 +587,19 @@
TEST_ART_BROKEN_DEFAULT_HEAP_POISONING_RUN_TESTS :=
TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS :=
+
+# Tests broken by multi-image. b/26317072
+TEST_ART_BROKEN_MULTI_IMAGE_RUN_TESTS := \
+ 476-checker-ctor-memory-barrier \
+ 530-checker-lse
+
+ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), \
+ $(TEST_ART_BROKEN_MULTI_IMAGE_RUN_TESTS), $(ALL_ADDRESS_SIZES))
+
+TEST_ART_BROKEN_MULTI_IMAGE_RUN_TESTS :=
+
# Clear variables ahead of appending to them when defining tests.
$(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=))
$(foreach target, $(TARGET_TYPES), \
@@ -642,14 +661,16 @@
$(ART_HOST_OUT_SHARED_LIBRARIES)/libarttest$(ART_HOST_SHLIB_EXTENSION) \
$(ART_HOST_OUT_SHARED_LIBRARIES)/libarttestd$(ART_HOST_SHLIB_EXTENSION) \
$(ART_HOST_OUT_SHARED_LIBRARIES)/libnativebridgetest$(ART_HOST_SHLIB_EXTENSION) \
- $(ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION)
+ $(ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \
+ $(ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdk$(ART_HOST_SHLIB_EXTENSION)
ifneq ($(HOST_PREFER_32_BIT),true)
ART_TEST_HOST_RUN_TEST_DEPENDENCIES += \
$(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libarttest$(ART_HOST_SHLIB_EXTENSION) \
$(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libarttestd$(ART_HOST_SHLIB_EXTENSION) \
$(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libnativebridgetest$(ART_HOST_SHLIB_EXTENSION) \
- $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION)
+ $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \
+ $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdk$(ART_HOST_SHLIB_EXTENSION)
endif
# Create a rule to build and run a tests following the form:
@@ -837,7 +858,27 @@
prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_pic_$(13))
endif
else
- $$(error found $(9) expected $(IMAGE_TYPES))
+ ifeq ($(9),multiimage)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_IMAGE_RULES
+ run_test_options += --multi-image
+ ifeq ($(1),host)
+ prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_no-pic_multi_$(13))
+ else
+ prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_no-pic_multi_$(13))
+ endif
+ else
+ ifeq ($(9),multipicimage)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_PICIMAGE_RULES
+ run_test_options += --pic-image --multi-image
+ ifeq ($(1),host)
+ prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_pic_multi_$(13))
+ else
+ prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_pic_multi_$(13))
+ endif
+ else
+ $$(error found $(9) expected $(IMAGE_TYPES))
+ endif
+ endif
endif
endif
endif
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 082c9b3..fd41fd2 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -56,7 +56,7 @@
extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasImage(JNIEnv* env ATTRIBUTE_UNUSED,
jclass cls ATTRIBUTE_UNUSED) {
- return Runtime::Current()->GetHeap()->HasImageSpace();
+ return Runtime::Current()->GetHeap()->HasBootImageSpace();
}
// public static native boolean isImageDex2OatEnabled();
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 3efa6ff..6ff356f 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -361,13 +361,17 @@
dex2oat_cmdline="true"
mkdir_cmdline="mkdir -p ${DEX_LOCATION}/dalvik-cache/$ISA"
+# TODO: allow app-image to work with multi-image. b/26317072
+app_image=""
+# app_image="--app-image-file=$DEX_LOCATION/dalvik-cache/$ISA/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.art | cut -d/ -f 2- | sed "s:/:@:g")"
+
if [ "$PREBUILD" = "y" ]; then
dex2oat_cmdline="$INVOKE_WITH $ANDROID_ROOT/bin/dex2oatd \
$COMPILE_FLAGS \
--boot-image=${BOOT_IMAGE} \
--dex-file=$DEX_LOCATION/$TEST_NAME.jar \
--oat-file=$DEX_LOCATION/dalvik-cache/$ISA/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex | cut -d/ -f 2- | sed "s:/:@:g") \
- --app-image-file=$DEX_LOCATION/dalvik-cache/$ISA/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.art | cut -d/ -f 2- | sed "s:/:@:g") \
+ ${app_image} \
--instruction-set=$ISA"
if [ "x$INSTRUCTION_SET_FEATURES" != "x" ] ; then
dex2oat_cmdline="${dex2oat_cmdline} --instruction-set-features=${INSTRUCTION_SET_FEATURES}"
diff --git a/test/run-test b/test/run-test
index 60e008c..d076687 100755
--- a/test/run-test
+++ b/test/run-test
@@ -85,7 +85,7 @@
# If JACK_CLASSPATH is not set, assume it only contains core-libart.
if [ -z "$JACK_CLASSPATH" ]; then
- export JACK_CLASSPATH="${OUT_DIR:-$ANDROID_BUILD_TOP/out}/host/common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack"
+ export JACK_CLASSPATH="${OUT_DIR:-$ANDROID_BUILD_TOP/out}/host/common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:${OUT_DIR:-$ANDROID_BUILD_TOP/out}/host/common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack"
fi
# If JILL_JAR is not set, assume it is located in the prebuilts directory.
@@ -135,6 +135,7 @@
have_image="yes"
image_suffix=""
pic_image_suffix=""
+multi_image_suffix=""
android_root="/system"
while true; do
@@ -184,6 +185,9 @@
elif [ "x$1" = "x--pic-image" ]; then
pic_image_suffix="-pic"
shift
+ elif [ "x$1" = "x--multi-image" ]; then
+ multi_image_suffix="-multi"
+ shift
elif [ "x$1" = "x--pic-test" ]; then
run_args="${run_args} --pic-test"
shift
@@ -458,7 +462,7 @@
if [ "$runtime" = "dalvik" ]; then
if [ "$target_mode" = "no" ]; then
framework="${ANDROID_PRODUCT_OUT}/system/framework"
- bpath="${framework}/core.jar:${framework}/conscrypt.jar:${framework}/okhttp.jar:${framework}/core-junit.jar:${framework}/bouncycastle.jar:${framework}/ext.jar"
+ bpath="${framework}/core-libart.jar:${framework}/core-oj.jar:${framework}/conscrypt.jar:${framework}/okhttp.jar:${framework}/core-junit.jar:${framework}/bouncycastle.jar:${framework}/ext.jar"
run_args="${run_args} --boot -Xbootclasspath:${bpath}"
else
true # defaults to using target BOOTCLASSPATH
@@ -470,12 +474,12 @@
export ANDROID_HOST_OUT=${OUT_DIR:-$ANDROID_BUILD_TOP/out/}host/linux-x86
fi
guess_host_arch_name
- run_args="${run_args} --boot ${ANDROID_HOST_OUT}/framework/core${image_suffix}${pic_image_suffix}.art"
+ run_args="${run_args} --boot ${ANDROID_HOST_OUT}/framework/core${image_suffix}${pic_image_suffix}${multi_image_suffix}.art"
run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}"
else
guess_target_arch_name
run_args="${run_args} --runtime-option -Djava.library.path=/data/art-test/${target_arch_name}"
- run_args="${run_args} --boot /data/art-test/core${image_suffix}${pic_image_suffix}.art"
+ run_args="${run_args} --boot /data/art-test/core${image_suffix}${pic_image_suffix}${multi_image_suffix}.art"
fi
if [ "$relocate" = "yes" ]; then
run_args="${run_args} --relocate"
@@ -502,6 +506,7 @@
# TODO If the target was compiled WITH_DEXPREOPT=true then these tests will
# fail since these jar files will be stripped.
bpath="${framework}/core-libart${bpath_suffix}.jar"
+ bpath="${bpath}:${framework}/core-oj${bpath_suffix}.jar"
bpath="${bpath}:${framework}/conscrypt${bpath_suffix}.jar"
bpath="${bpath}:${framework}/okhttp${bpath_suffix}.jar"
bpath="${bpath}:${framework}/core-junit${bpath_suffix}.jar"
@@ -611,6 +616,8 @@
echo " Set instruction-set-features for compilation."
echo " --pic-image Use an image compiled with position independent code for the"
echo " boot class path."
+ echo " --multi-image Use a set of images compiled with dex2oat multi-image for"
+ echo " the boot class path."
echo " --pic-test Compile the test code position independent."
echo " --quiet Don't print anything except failure messages"
) 1>&2 # Direct to stderr so usage is not printed if --quiet is set.