diff options
50 files changed, 986 insertions, 532 deletions
diff --git a/Android.mk b/Android.mk index 2e05d33209..e762814385 100644 --- a/Android.mk +++ b/Android.mk @@ -86,6 +86,7 @@ include $(art_path)/disassembler/Android.mk include $(art_path)/oatdump/Android.mk include $(art_path)/imgdiag/Android.mk include $(art_path)/patchoat/Android.mk +include $(art_path)/profman/Android.mk include $(art_path)/dalvikvm/Android.mk include $(art_path)/tools/Android.mk include $(art_path)/tools/ahat/Android.mk diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk index ab7036717a..c9af1c67a4 100644 --- a/build/Android.common_test.mk +++ b/build/Android.common_test.mk @@ -205,7 +205,7 @@ define build-art-test-dex LOCAL_DEX_PREOPT_IMAGE_LOCATION := $(TARGET_CORE_IMG_OUT) ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),) LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex - LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp -D jack.dex.output.multidex.legacy=true + LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp endif include $(BUILD_JAVA_LIBRARY) $(5) := $$(LOCAL_INSTALLED_MODULE) @@ -221,7 +221,7 @@ define build-art-test-dex LOCAL_DEX_PREOPT_IMAGE := $(HOST_CORE_IMG_LOCATION) ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),) LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex - LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp -D jack.dex.output.multidex.legacy=true + LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp endif include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) $(6) := $$(LOCAL_INSTALLED_MODULE) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 704d69a37a..22c0e8d7d0 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -144,6 +144,12 @@ ART_GTEST_oatdump_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_default_no-pic_32) \ oatdump +# Profile assistant tests requires profman utility. +ART_GTEST_profile_assistant_test_HOST_DEPS := \ + $(HOST_OUT_EXECUTABLES)/profmand +ART_GTEST_profile_assistant_test_TARGET_DEPS := \ + profman + # The path for which all the source files are relative, not actually the current directory. LOCAL_PATH := art @@ -153,6 +159,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \ dexlist/dexlist_test.cc \ imgdiag/imgdiag_test.cc \ oatdump/oatdump_test.cc \ + profman/profile_assistant_test.cc \ runtime/arch/arch_test.cc \ runtime/arch/instruction_set_test.cc \ runtime/arch/instruction_set_features_test.cc \ @@ -270,7 +277,6 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ compiler/optimizing/ssa_test.cc \ compiler/optimizing/stack_map_test.cc \ compiler/optimizing/suspend_check_test.cc \ - compiler/profile_assistant_test.cc \ compiler/utils/arena_allocator_test.cc \ compiler/utils/dedupe_set_test.cc \ compiler/utils/swap_space_test.cc \ diff --git a/compiler/Android.mk b/compiler/Android.mk index 159e9cfb5e..3f61e8eb1b 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -107,8 +107,7 @@ LIBART_COMPILER_SRC_FILES := \ elf_writer.cc \ elf_writer_quick.cc \ image_writer.cc \ - oat_writer.cc \ - profile_assistant.cc + oat_writer.cc LIBART_COMPILER_SRC_FILES_arm := \ dex/quick/arm/assemble_arm.cc \ diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index a51dd3209b..db8c3abccf 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -2492,6 +2492,7 @@ void CompilerDriver::Compile(jobject class_loader, parallel_thread_pool_.get(), parallel_thread_count_, timings); + Runtime::Current()->ReclaimArenaPoolMemory(); } VLOG(compiler) << "Compile: " << GetMemoryUsageString(false); } diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index a53a6be3de..f3c40b109f 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -6511,8 +6511,8 @@ void CodeGeneratorX86_64::Load64BitValue(CpuRegister dest, int64_t value) { if (value == 0) { // Clears upper bits too. __ xorl(dest, dest); - } else if (value > 0 && IsInt<32>(value)) { - // We can use a 32 bit move, as it will zero-extend and is one byte shorter. + } else if (IsUint<32>(value)) { + // We can use a 32 bit move, as it will zero-extend and is shorter. __ movl(dest, Immediate(static_cast<int32_t>(value))); } else { __ movq(dest, Immediate(value)); diff --git a/compiler/profile_assistant.h b/compiler/profile_assistant.h deleted file mode 100644 index ad5e2163cf..0000000000 --- a/compiler/profile_assistant.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_PROFILE_ASSISTANT_H_ -#define ART_COMPILER_PROFILE_ASSISTANT_H_ - -#include <string> -#include <vector> - -#include "base/scoped_flock.h" -#include "jit/offline_profiling_info.cc" - -namespace art { - -class ProfileAssistant { - public: - // Process the profile information present in the given files. Returns true - // if the analysis ended up successfully (i.e. no errors during reading, - // merging or writing of profile files). - // - // If the returned value is true and there is a significant difference between - // profile_files and reference_profile_files: - // - profile_compilation_info is set to a not null object that - // can be used to drive compilation. It will be the merge of all the data - // found in profile_files and reference_profile_files. - // - the data from profile_files[i] is merged into - // reference_profile_files[i] and the corresponding backing file is - // updated. - // - // If the returned value is false or the difference is insignificant, - // profile_compilation_info will be set to null. - // - // Additional notes: - // - as mentioned above, this function may update the content of the files - // passed with the reference_profile_files. - // - if reference_profile_files is not empty it must be the same size as - // profile_files. - static bool ProcessProfiles( - const std::vector<std::string>& profile_files, - const std::vector<std::string>& reference_profile_files, - /*out*/ ProfileCompilationInfo** profile_compilation_info); - - static bool ProcessProfiles( - const std::vector<uint32_t>& profile_files_fd_, - const std::vector<uint32_t>& reference_profile_files_fd_, - /*out*/ ProfileCompilationInfo** profile_compilation_info); - - private: - static bool ProcessProfilesInternal( - const std::vector<ScopedFlock>& profile_files, - const std::vector<ScopedFlock>& reference_profile_files, - /*out*/ ProfileCompilationInfo** profile_compilation_info); - - DISALLOW_COPY_AND_ASSIGN(ProfileAssistant); -}; - -} // namespace art - -#endif // ART_COMPILER_PROFILE_ASSISTANT_H_ diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk index 77f8d6cd9f..dfc379fdae 100644 --- a/dex2oat/Android.mk +++ b/dex2oat/Android.mk @@ -55,20 +55,42 @@ ifeq ($(ART_BUILD_TARGET_DEBUG),true) $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler libsigchain,art/compiler,target,debug,$(dex2oat_target_arch))) endif +# Note: the order is important because of static linking resolution. +DEX2OAT_STATIC_DEPENDENCIES := \ + libziparchive-host \ + libnativehelper \ + libnativebridge \ + libnativeloader \ + libsigchain_dummy \ + libvixl \ + liblog \ + libz \ + libbacktrace \ + libLLVMObject \ + libLLVMBitReader \ + libLLVMMC \ + libLLVMMCParser \ + libLLVMCore \ + libLLVMSupport \ + libcutils \ + libunwindbacktrace \ + libutils \ + libbase \ + liblz4 \ + liblzma + # 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-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler libsigchain libziparchive-host liblz4,art/compiler,host,ndebug,$(dex2oat_host_arch))) ifeq ($(ART_BUILD_HOST_STATIC),true) - $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart libart-compiler libart libziparchive-host libnativehelper libnativebridge libsigchain_dummy libvixl liblog libz \ - libbacktrace libLLVMObject libLLVMBitReader libLLVMMC libLLVMMCParser libLLVMCore libLLVMSupport libcutils libunwindbacktrace libutils libbase liblz4,art/compiler,host,ndebug,$(dex2oat_host_arch),static)) + $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart libart-compiler libart $(DEX2OAT_STATIC_DEPENDENCIES),art/compiler,host,ndebug,$(dex2oat_host_arch),static)) endif endif ifeq ($(ART_BUILD_HOST_DEBUG),true) $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler libsigchain libziparchive-host liblz4,art/compiler,host,debug,$(dex2oat_host_arch))) ifeq ($(ART_BUILD_HOST_STATIC),true) - $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd libartd-compiler libartd libziparchive-host libnativehelper libnativebridge libsigchain_dummy libvixld liblog libz \ - libbacktrace libLLVMObject libLLVMBitReader libLLVMMC libLLVMMCParser libLLVMCore libLLVMSupport libcutils libunwindbacktrace libutils libbase liblz4,art/compiler,host,debug,$(dex2oat_host_arch),static)) + $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd libartd-compiler libartd $(DEX2OAT_STATIC_DEPENDENCIES),art/compiler,host,debug,$(dex2oat_host_arch),static)) endif endif diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 541fb5a423..f7efdc5bac 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -40,6 +40,7 @@ #include "art_method-inl.h" #include "base/dumpable.h" #include "base/macros.h" +#include "base/scoped_flock.h" #include "base/stl_util.h" #include "base/stringpiece.h" #include "base/time_utils.h" @@ -71,7 +72,6 @@ #include "mirror/object_array-inl.h" #include "oat_writer.h" #include "os.h" -#include "profile_assistant.h" #include "runtime.h" #include "runtime_options.h" #include "ScopedLocalRef.h" @@ -339,23 +339,10 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" Example: --runtime-arg -Xms256m"); UsageError(""); UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation."); - UsageError(" Can be specified multiple time, in which case the data from the different"); - UsageError(" profiles will be aggregated."); - UsageError(""); - UsageError(" --reference-profile-file=<filename>: specify a reference profile file to use when"); - UsageError(" compiling. The data in this file will be compared with the data in the"); - UsageError(" associated --profile-file and the compilation will proceed only if there is"); - UsageError(" a significant difference (--reference-profile-file is paired with"); - UsageError(" --profile-file in the natural order). If the compilation was attempted then"); - UsageError(" --profile-file will be merged into --reference-profile-file. Valid only when"); - UsageError(" specified together with --profile-file."); UsageError(""); UsageError(" --profile-file-fd=<number>: same as --profile-file but accepts a file descriptor."); UsageError(" Cannot be used together with --profile-file."); UsageError(""); - UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but"); - UsageError(" accepts a file descriptor. Cannot be used together with"); - UsageError(" --reference-profile-file."); UsageError(" --print-pass-names: print a list of pass names"); UsageError(""); UsageError(" --disable-passes=<pass-names>: disable one or more passes separated by comma."); @@ -517,14 +504,6 @@ static bool UseSwap(bool is_image, std::vector<const DexFile*>& dex_files) { return dex_files_size >= kMinDexFileCumulativeSizeForSwap; } -static void CloseAllFds(const std::vector<uint32_t>& fds, const char* descriptor) { - for (size_t i = 0; i < fds.size(); i++) { - if (close(fds[i]) < 0) { - PLOG(WARNING) << "Failed to close descriptor for " << descriptor << " at index " << i; - } - } -} - class Dex2Oat FINAL { public: explicit Dex2Oat(TimingLogger* timings) : @@ -567,10 +546,12 @@ class Dex2Oat FINAL { dump_passes_(false), dump_timing_(false), dump_slow_timing_(kIsDebugBuild), - swap_fd_(-1), + swap_fd_(kInvalidFd), app_image_fd_(kInvalidFd), + profile_file_fd_(kInvalidFd), timings_(timings), - force_determinism_(false) {} + force_determinism_(false) + {} ~Dex2Oat() { // Log completion time before deleting the runtime_, because this accesses @@ -824,25 +805,8 @@ class Dex2Oat FINAL { } } - if (!profile_files_.empty() && !profile_files_fd_.empty()) { - Usage("Profile files should not be specified with both --profile-file-fd and --profile-file"); - } - if (!profile_files_.empty()) { - if (!reference_profile_files_.empty() && - (reference_profile_files_.size() != profile_files_.size())) { - Usage("If specified, --reference-profile-file should match the number of --profile-file."); - } - } else if (!reference_profile_files_.empty()) { - Usage("--reference-profile-file should only be supplied with --profile-file"); - } - if (!profile_files_fd_.empty()) { - if (!reference_profile_files_fd_.empty() && - (reference_profile_files_fd_.size() != profile_files_fd_.size())) { - Usage("If specified, --reference-profile-file-fd should match the number", - " of --profile-file-fd."); - } - } else if (!reference_profile_files_fd_.empty()) { - Usage("--reference-profile-file-fd should only be supplied with --profile-file-fd"); + if (!profile_file_.empty() && (profile_file_fd_ != kInvalidFd)) { + Usage("Profile file should not be specified with both --profile-file-fd and --profile-file"); } if (!parser_options->oat_symbols.empty()) { @@ -1153,16 +1117,9 @@ class Dex2Oat FINAL { } else if (option.starts_with("--compiler-backend=")) { ParseCompilerBackend(option, parser_options.get()); } else if (option.starts_with("--profile-file=")) { - profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString()); - } else if (option.starts_with("--reference-profile-file=")) { - reference_profile_files_.push_back( - option.substr(strlen("--reference-profile-file=")).ToString()); + profile_file_ = option.substr(strlen("--profile-file=")).ToString(); } else if (option.starts_with("--profile-file-fd=")) { - ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_); - } else if (option.starts_with("--reference-profile-file-fd=")) { - ParseFdForCollection(option, "--reference_profile-file-fd", &reference_profile_files_fd_); - } else if (option == "--no-profile-file") { - // No profile + ParseUintOption(option, "--profile-file-fd", &profile_file_fd_, Usage); } else if (option == "--host") { is_host_ = true; } else if (option == "--runtime-arg") { @@ -1865,33 +1822,44 @@ class Dex2Oat FINAL { } bool UseProfileGuidedCompilation() const { - return !profile_files_.empty() || !profile_files_fd_.empty(); + return !profile_file_.empty() || (profile_file_fd_ != kInvalidFd); } - bool ProcessProfiles() { + bool LoadProfile() { DCHECK(UseProfileGuidedCompilation()); - ProfileCompilationInfo* info = nullptr; - bool result = false; - if (profile_files_.empty()) { - DCHECK(!profile_files_fd_.empty()); - result = ProfileAssistant::ProcessProfiles( - profile_files_fd_, reference_profile_files_fd_, &info); - CloseAllFds(profile_files_fd_, "profile_files_fd_"); - CloseAllFds(reference_profile_files_fd_, "reference_profile_files_fd_"); + + profile_compilation_info_.reset(new ProfileCompilationInfo()); + ScopedFlock flock; + bool success = false; + std::string error; + if (profile_file_fd_ != -1) { + // The file doesn't need to be flushed so don't check the usage. + // Pass a bogus path so that we can easily attribute any reported error. + File file(profile_file_fd_, "profile", /*check_usage*/ false, /*read_only_mode*/ true); + if (flock.Init(&file, &error)) { + success = profile_compilation_info_->Load(profile_file_fd_); + } } else { - result = ProfileAssistant::ProcessProfiles( - profile_files_, reference_profile_files_, &info); + if (flock.Init(profile_file_.c_str(), O_RDONLY, /* block */ true, &error)) { + success = profile_compilation_info_->Load(flock.GetFile()->Fd()); + } + } + if (!error.empty()) { + LOG(WARNING) << "Cannot lock profiles: " << error; } - profile_compilation_info_.reset(info); + if (!success) { + profile_compilation_info_.reset(nullptr); + } - return result; + return success; } bool ShouldCompileBasedOnProfiles() const { DCHECK(UseProfileGuidedCompilation()); - // If we are given profiles, compile only if we have new information. - return profile_compilation_info_ != nullptr; + // If we are given a profile, compile only if we have some data in it. + return (profile_compilation_info_ != nullptr) && + (profile_compilation_info_->GetNumberOfMethods() != 0); } private: @@ -2460,10 +2428,8 @@ class Dex2Oat FINAL { int swap_fd_; std::string app_image_file_name_; int app_image_fd_; - std::vector<std::string> profile_files_; - std::vector<std::string> reference_profile_files_; - std::vector<uint32_t> profile_files_fd_; - std::vector<uint32_t> reference_profile_files_fd_; + std::string profile_file_; + int profile_file_fd_; std::unique_ptr<ProfileCompilationInfo> profile_compilation_info_; TimingLogger* timings_; std::unique_ptr<CumulativeLogger> compiler_phases_timings_; @@ -2592,7 +2558,7 @@ static int dex2oat(int argc, char** argv) { // Process profile information and assess if we need to do a profile guided compilation. // This operation involves I/O. if (dex2oat.UseProfileGuidedCompilation()) { - if (dex2oat.ProcessProfiles()) { + if (dex2oat.LoadProfile()) { if (!dex2oat.ShouldCompileBasedOnProfiles()) { LOG(INFO) << "Skipped compilation because of insignificant profile delta"; return EXIT_SUCCESS; diff --git a/profman/Android.mk b/profman/Android.mk new file mode 100644 index 0000000000..d38d107d21 --- /dev/null +++ b/profman/Android.mk @@ -0,0 +1,45 @@ +# +# Copyright (C) 2016 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. +# + +LOCAL_PATH := $(call my-dir) + +include art/build/Android.executable.mk + +PROFMAN_SRC_FILES := \ + profman.cc \ + profile_assistant.cc + +# TODO: Remove this when the framework (installd) supports pushing the +# right instruction-set parameter for the primary architecture. +ifneq ($(filter ro.zygote=zygote64,$(PRODUCT_DEFAULT_PROPERTY_OVERRIDES)),) + profman_arch := 64 +else + profman_arch := 32 +endif + +ifeq ($(ART_BUILD_TARGET_NDEBUG),true) + $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,target,ndebug,$(profman_arch))) +endif +ifeq ($(ART_BUILD_TARGET_DEBUG),true) + $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,target,debug,$(profman_arch))) +endif + +ifeq ($(ART_BUILD_HOST_NDEBUG),true) + $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,host,ndebug)) +endif +ifeq ($(ART_BUILD_HOST_DEBUG),true) + $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,host,debug)) +endif diff --git a/compiler/profile_assistant.cc b/profman/profile_assistant.cc index 85335efcc4..58e8a3a7d1 100644 --- a/compiler/profile_assistant.cc +++ b/profman/profile_assistant.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2016 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. @@ -24,13 +24,10 @@ namespace art { // Minimum number of new methods that profiles must contain to enable recompilation. static constexpr const uint32_t kMinNewMethodsForCompilation = 10; -bool ProfileAssistant::ProcessProfilesInternal( +ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfilesInternal( const std::vector<ScopedFlock>& profile_files, - const std::vector<ScopedFlock>& reference_profile_files, - /*out*/ ProfileCompilationInfo** profile_compilation_info) { + const ScopedFlock& reference_profile_file) { DCHECK(!profile_files.empty()); - DCHECK(!reference_profile_files.empty() || - (profile_files.size() == reference_profile_files.size())); std::vector<ProfileCompilationInfo> new_info(profile_files.size()); bool should_compile = false; @@ -38,51 +35,53 @@ bool ProfileAssistant::ProcessProfilesInternal( for (size_t i = 0; i < new_info.size(); i++) { if (!new_info[i].Load(profile_files[i].GetFile()->Fd())) { LOG(WARNING) << "Could not load profile file at index " << i; - return false; + return kErrorBadProfiles; } // Do we have enough new profiled methods that will make the compilation worthwhile? should_compile |= (new_info[i].GetNumberOfMethods() > kMinNewMethodsForCompilation); } if (!should_compile) { - return true; + return kSkipCompilation; } - std::unique_ptr<ProfileCompilationInfo> result(new ProfileCompilationInfo()); // Merge information. + ProfileCompilationInfo info; + if (!info.Load(reference_profile_file.GetFile()->Fd())) { + LOG(WARNING) << "Could not load reference profile file"; + return kErrorBadProfiles; + } + for (size_t i = 0; i < new_info.size(); i++) { - if (!reference_profile_files.empty()) { - if (!new_info[i].Load(reference_profile_files[i].GetFile()->Fd())) { - LOG(WARNING) << "Could not load reference profile file at index " << i; - return false; - } - } // Merge all data into a single object. - if (!result->Load(new_info[i])) { + if (!info.Load(new_info[i])) { LOG(WARNING) << "Could not merge profile data at index " << i; - return false; + return kErrorBadProfiles; } } - // We were successful in merging all profile information. Update the files. - for (size_t i = 0; i < new_info.size(); i++) { - if (!reference_profile_files.empty()) { - if (!reference_profile_files[i].GetFile()->ClearContent()) { - PLOG(WARNING) << "Could not clear reference profile file at index " << i; - return false; - } - if (!new_info[i].Save(reference_profile_files[i].GetFile()->Fd())) { - LOG(WARNING) << "Could not save reference profile file at index " << i; - return false; - } - if (!profile_files[i].GetFile()->ClearContent()) { - PLOG(WARNING) << "Could not clear profile file at index " << i; - return false; - } - } + // We were successful in merging all profile information. Update the reference profile. + if (!reference_profile_file.GetFile()->ClearContent()) { + PLOG(WARNING) << "Could not clear reference profile file"; + return kErrorIO; } + if (!info.Save(reference_profile_file.GetFile()->Fd())) { + LOG(WARNING) << "Could not save reference profile file"; + return kErrorIO; + } + + return kCompile; +} - *profile_compilation_info = result.release(); - return true; +static bool InitFlock(const std::string& filename, ScopedFlock& flock, std::string* error) { + return flock.Init(filename.c_str(), O_RDWR, /* block */ true, error); +} + +static bool InitFlock(int fd, ScopedFlock& flock, std::string* error) { + DCHECK_GE(fd, 0); + // We do not own the descriptor, so disable auto-close and don't check usage. + File file(fd, false); + file.DisableAutoClose(); + return flock.Init(&file, error); } class ScopedCollectionFlock { @@ -92,7 +91,7 @@ class ScopedCollectionFlock { // Will block until all the locks are acquired. bool Init(const std::vector<std::string>& filenames, /* out */ std::string* error) { for (size_t i = 0; i < filenames.size(); i++) { - if (!flocks_[i].Init(filenames[i].c_str(), O_RDWR, /* block */ true, error)) { + if (!InitFlock(filenames[i], flocks_[i], error)) { *error += " (index=" + std::to_string(i) + ")"; return false; } @@ -101,12 +100,10 @@ class ScopedCollectionFlock { } // Will block until all the locks are acquired. - bool Init(const std::vector<uint32_t>& fds, /* out */ std::string* error) { + bool Init(const std::vector<int>& fds, /* out */ std::string* error) { for (size_t i = 0; i < fds.size(); i++) { - // We do not own the descriptor, so disable auto-close and don't check usage. - File file(fds[i], false); - file.DisableAutoClose(); - if (!flocks_[i].Init(&file, error)) { + DCHECK_GE(fds[i], 0); + if (!InitFlock(fds[i], flocks_[i], error)) { *error += " (index=" + std::to_string(i) + ")"; return false; } @@ -120,50 +117,43 @@ class ScopedCollectionFlock { std::vector<ScopedFlock> flocks_; }; -bool ProfileAssistant::ProcessProfiles( - const std::vector<uint32_t>& profile_files_fd, - const std::vector<uint32_t>& reference_profile_files_fd, - /*out*/ ProfileCompilationInfo** profile_compilation_info) { - *profile_compilation_info = nullptr; - +ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfiles( + const std::vector<int>& profile_files_fd, + int reference_profile_file_fd) { + DCHECK_GE(reference_profile_file_fd, 0); std::string error; ScopedCollectionFlock profile_files_flocks(profile_files_fd.size()); if (!profile_files_flocks.Init(profile_files_fd, &error)) { LOG(WARNING) << "Could not lock profile files: " << error; - return false; + return kErrorCannotLock; } - ScopedCollectionFlock reference_profile_files_flocks(reference_profile_files_fd.size()); - if (!reference_profile_files_flocks.Init(reference_profile_files_fd, &error)) { - LOG(WARNING) << "Could not lock reference profile files: " << error; - return false; + ScopedFlock reference_profile_file_flock; + if (!InitFlock(reference_profile_file_fd, reference_profile_file_flock, &error)) { + LOG(WARNING) << "Could not lock reference profiled files: " << error; + return kErrorCannotLock; } return ProcessProfilesInternal(profile_files_flocks.Get(), - reference_profile_files_flocks.Get(), - profile_compilation_info); + reference_profile_file_flock); } -bool ProfileAssistant::ProcessProfiles( +ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfiles( const std::vector<std::string>& profile_files, - const std::vector<std::string>& reference_profile_files, - /*out*/ ProfileCompilationInfo** profile_compilation_info) { - *profile_compilation_info = nullptr; - + const std::string& reference_profile_file) { std::string error; ScopedCollectionFlock profile_files_flocks(profile_files.size()); if (!profile_files_flocks.Init(profile_files, &error)) { LOG(WARNING) << "Could not lock profile files: " << error; - return false; + return kErrorCannotLock; } - ScopedCollectionFlock reference_profile_files_flocks(reference_profile_files.size()); - if (!reference_profile_files_flocks.Init(reference_profile_files, &error)) { + ScopedFlock reference_profile_file_flock; + if (!InitFlock(reference_profile_file, reference_profile_file_flock, &error)) { LOG(WARNING) << "Could not lock reference profile files: " << error; - return false; + return kErrorCannotLock; } return ProcessProfilesInternal(profile_files_flocks.Get(), - reference_profile_files_flocks.Get(), - profile_compilation_info); + reference_profile_file_flock); } } // namespace art diff --git a/profman/profile_assistant.h b/profman/profile_assistant.h new file mode 100644 index 0000000000..d3c75b817a --- /dev/null +++ b/profman/profile_assistant.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_PROFMAN_PROFILE_ASSISTANT_H_ +#define ART_PROFMAN_PROFILE_ASSISTANT_H_ + +#include <string> +#include <vector> + +#include "base/scoped_flock.h" +#include "jit/offline_profiling_info.h" + +namespace art { + +class ProfileAssistant { + public: + // These also serve as return codes of profman and are processed by installd + // (frameworks/native/cmds/installd/commands.cpp) + enum ProcessingResult { + kCompile = 0, + kSkipCompilation = 1, + kErrorBadProfiles = 2, + kErrorIO = 3, + kErrorCannotLock = 4 + }; + + // Process the profile information present in the given files. Returns one of + // ProcessingResult values depending on profile information and whether or not + // the analysis ended up successfully (i.e. no errors during reading, + // merging or writing of profile files). + // + // When the returned value is kCompile there is a significant difference + // between profile_files and reference_profile_files. In this case + // reference_profile will be updated with the profiling info obtain after + // merging all profiles. + // + // When the returned value is kSkipCompilation, the difference between the + // merge of the current profiles and the reference one is insignificant. In + // this case no file will be updated. + // + static ProcessingResult ProcessProfiles( + const std::vector<std::string>& profile_files, + const std::string& reference_profile_file); + + static ProcessingResult ProcessProfiles( + const std::vector<int>& profile_files_fd_, + int reference_profile_file_fd); + + private: + static ProcessingResult ProcessProfilesInternal( + const std::vector<ScopedFlock>& profile_files, + const ScopedFlock& reference_profile_file); + + DISALLOW_COPY_AND_ASSIGN(ProfileAssistant); +}; + +} // namespace art + +#endif // ART_PROFMAN_PROFILE_ASSISTANT_H_ diff --git a/compiler/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 58b7513377..3faa8eb53f 100644 --- a/compiler/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -18,8 +18,9 @@ #include "base/unix_file/fd_file.h" #include "common_runtime_test.h" -#include "compiler/profile_assistant.h" +#include "profile_assistant.h" #include "jit/offline_profiling_info.h" +#include "utils.h" namespace art { @@ -44,23 +45,47 @@ class ProfileAssistantTest : public CommonRuntimeTest { ASSERT_TRUE(profile.GetFile()->ResetOffset()); } - uint32_t GetFd(const ScratchFile& file) const { - return static_cast<uint32_t>(file.GetFd()); + int GetFd(const ScratchFile& file) const { + return static_cast<int>(file.GetFd()); + } + + void CheckProfileInfo(ScratchFile& file, const ProfileCompilationInfo& info) { + ProfileCompilationInfo file_info; + ASSERT_TRUE(file.GetFile()->ResetOffset()); + ASSERT_TRUE(file_info.Load(GetFd(file))); + ASSERT_TRUE(file_info.Equals(info)); + } + + // Runs test with given arguments. + int ProcessProfiles(const std::vector<int>& profiles_fd, int reference_profile_fd) { + std::string file_path = GetTestAndroidRoot(); + file_path += "/bin/profman"; + if (kIsDebugBuild) { + file_path += "d"; + } + + EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path"; + std::vector<std::string> argv_str; + argv_str.push_back(file_path); + for (size_t k = 0; k < profiles_fd.size(); k++) { + argv_str.push_back("--profile-file-fd=" + std::to_string(profiles_fd[k])); + } + argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile_fd)); + + std::string error; + return ExecAndReturnCode(argv_str, &error); } }; TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) { ScratchFile profile1; ScratchFile profile2; - ScratchFile reference_profile1; - ScratchFile reference_profile2; + ScratchFile reference_profile; - std::vector<uint32_t> profile_fds({ + std::vector<int> profile_fds({ GetFd(profile1), GetFd(profile2)}); - std::vector<uint32_t> reference_profile_fds({ - GetFd(reference_profile1), - GetFd(reference_profile2)}); + int reference_profile_fd = GetFd(reference_profile); const uint16_t kNumberOfMethodsToEnableCompilation = 100; ProfileCompilationInfo info1; @@ -69,44 +94,32 @@ TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) { SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2); // We should advise compilation. - ProfileCompilationInfo* result; - ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); - ASSERT_TRUE(result != nullptr); - + ASSERT_EQ(ProfileAssistant::kCompile, + ProcessProfiles(profile_fds, reference_profile_fd)); // The resulting compilation info must be equal to the merge of the inputs. + ProfileCompilationInfo result; + ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); + ASSERT_TRUE(result.Load(reference_profile_fd)); + ProfileCompilationInfo expected; ASSERT_TRUE(expected.Load(info1)); ASSERT_TRUE(expected.Load(info2)); - ASSERT_TRUE(expected.Equals(*result)); + ASSERT_TRUE(expected.Equals(result)); - // The information from profiles must be transfered to the reference profiles. - ProfileCompilationInfo file_info1; - ASSERT_TRUE(reference_profile1.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info1.Load(GetFd(reference_profile1))); - ASSERT_TRUE(file_info1.Equals(info1)); - - ProfileCompilationInfo file_info2; - ASSERT_TRUE(reference_profile2.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info2.Load(GetFd(reference_profile2))); - ASSERT_TRUE(file_info2.Equals(info2)); - - // Initial profiles must be cleared. - ASSERT_EQ(0, profile1.GetFile()->GetLength()); - ASSERT_EQ(0, profile2.GetFile()->GetLength()); + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); + CheckProfileInfo(profile2, info2); } TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) { ScratchFile profile1; ScratchFile profile2; - ScratchFile reference_profile1; - ScratchFile reference_profile2; + ScratchFile reference_profile; - std::vector<uint32_t> profile_fds({ + std::vector<int> profile_fds({ GetFd(profile1), GetFd(profile2)}); - std::vector<uint32_t> reference_profile_fds({ - GetFd(reference_profile1), - GetFd(reference_profile2)}); + int reference_profile_fd = GetFd(reference_profile); // The new profile info will contain the methods with indices 0-100. const uint16_t kNumberOfMethodsToEnableCompilation = 100; @@ -118,60 +131,39 @@ TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) { // The reference profile info will contain the methods with indices 50-150. const uint16_t kNumberOfMethodsAlreadyCompiled = 100; - ProfileCompilationInfo reference_info1; - SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, reference_profile1, - &reference_info1, kNumberOfMethodsToEnableCompilation / 2); - ProfileCompilationInfo reference_info2; - SetupProfile("p2", 2, kNumberOfMethodsAlreadyCompiled, reference_profile2, - &reference_info2, kNumberOfMethodsToEnableCompilation / 2); + ProfileCompilationInfo reference_info; + SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, reference_profile, + &reference_info, kNumberOfMethodsToEnableCompilation / 2); // We should advise compilation. - ProfileCompilationInfo* result; - ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); - ASSERT_TRUE(result != nullptr); + ASSERT_EQ(ProfileAssistant::kCompile, + ProcessProfiles(profile_fds, reference_profile_fd)); // The resulting compilation info must be equal to the merge of the inputs + ProfileCompilationInfo result; + ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); + ASSERT_TRUE(result.Load(reference_profile_fd)); + ProfileCompilationInfo expected; ASSERT_TRUE(expected.Load(info1)); ASSERT_TRUE(expected.Load(info2)); - ASSERT_TRUE(expected.Load(reference_info1)); - ASSERT_TRUE(expected.Load(reference_info2)); - ASSERT_TRUE(expected.Equals(*result)); - - // The information from profiles must be transfered to the reference profiles. - ProfileCompilationInfo file_info1; - ProfileCompilationInfo merge1; - ASSERT_TRUE(merge1.Load(info1)); - ASSERT_TRUE(merge1.Load(reference_info1)); - ASSERT_TRUE(reference_profile1.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info1.Load(GetFd(reference_profile1))); - ASSERT_TRUE(file_info1.Equals(merge1)); + ASSERT_TRUE(expected.Load(reference_info)); + ASSERT_TRUE(expected.Equals(result)); - ProfileCompilationInfo file_info2; - ProfileCompilationInfo merge2; - ASSERT_TRUE(merge2.Load(info2)); - ASSERT_TRUE(merge2.Load(reference_info2)); - ASSERT_TRUE(reference_profile2.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info2.Load(GetFd(reference_profile2))); - ASSERT_TRUE(file_info2.Equals(merge2)); - - // Initial profiles must be cleared. - ASSERT_EQ(0, profile1.GetFile()->GetLength()); - ASSERT_EQ(0, profile2.GetFile()->GetLength()); + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); + CheckProfileInfo(profile2, info2); } TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) { ScratchFile profile1; ScratchFile profile2; - ScratchFile reference_profile1; - ScratchFile reference_profile2; + ScratchFile reference_profile; - std::vector<uint32_t> profile_fds({ + std::vector<int> profile_fds({ GetFd(profile1), GetFd(profile2)}); - std::vector<uint32_t> reference_profile_fds({ - GetFd(reference_profile1), - GetFd(reference_profile2)}); + int reference_profile_fd = GetFd(reference_profile); const uint16_t kNumberOfMethodsToSkipCompilation = 1; ProfileCompilationInfo info1; @@ -180,9 +172,8 @@ TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) { SetupProfile("p2", 2, kNumberOfMethodsToSkipCompilation, profile2, &info2); // We should not advise compilation. - ProfileCompilationInfo* result = nullptr; - ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); - ASSERT_TRUE(result == nullptr); + ASSERT_EQ(ProfileAssistant::kSkipCompilation, + ProcessProfiles(profile_fds, reference_profile_fd)); // The information from profiles must remain the same. ProfileCompilationInfo file_info1; @@ -196,22 +187,22 @@ TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) { ASSERT_TRUE(file_info2.Equals(info2)); // Reference profile files must remain empty. - ASSERT_EQ(0, reference_profile1.GetFile()->GetLength()); - ASSERT_EQ(0, reference_profile2.GetFile()->GetLength()); + ASSERT_EQ(0, reference_profile.GetFile()->GetLength()); + + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); + CheckProfileInfo(profile2, info2); } TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) { ScratchFile profile1; ScratchFile profile2; - ScratchFile reference_profile1; - ScratchFile reference_profile2; + ScratchFile reference_profile; - std::vector<uint32_t> profile_fds({ + std::vector<int> profile_fds({ GetFd(profile1), GetFd(profile2)}); - std::vector<uint32_t> reference_profile_fds({ - GetFd(reference_profile1), - GetFd(reference_profile2)}); + int reference_profile_fd = GetFd(reference_profile); const uint16_t kNumberOfMethodsToEnableCompilation = 100; // Assign different hashes for the same dex file. This will make merging of information to fail. @@ -221,34 +212,24 @@ TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) { SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2); // We should fail processing. - ProfileCompilationInfo* result = nullptr; - ASSERT_FALSE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); - ASSERT_TRUE(result == nullptr); + ASSERT_EQ(ProfileAssistant::kErrorBadProfiles, + ProcessProfiles(profile_fds, reference_profile_fd)); - // The information from profiles must still remain the same. - ProfileCompilationInfo file_info1; - ASSERT_TRUE(profile1.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info1.Load(GetFd(profile1))); - ASSERT_TRUE(file_info1.Equals(info1)); - - ProfileCompilationInfo file_info2; - ASSERT_TRUE(profile2.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info2.Load(GetFd(profile2))); - ASSERT_TRUE(file_info2.Equals(info2)); + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); + CheckProfileInfo(profile2, info2); // Reference profile files must still remain empty. - ASSERT_EQ(0, reference_profile1.GetFile()->GetLength()); - ASSERT_EQ(0, reference_profile2.GetFile()->GetLength()); + ASSERT_EQ(0, reference_profile.GetFile()->GetLength()); } TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) { ScratchFile profile1; ScratchFile reference_profile; - std::vector<uint32_t> profile_fds({ + std::vector<int> profile_fds({ GetFd(profile1)}); - std::vector<uint32_t> reference_profile_fds({ - GetFd(reference_profile)}); + int reference_profile_fd = GetFd(reference_profile); const uint16_t kNumberOfMethodsToEnableCompilation = 100; // Assign different hashes for the same dex file. This will make merging of information to fail. @@ -258,22 +239,13 @@ TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) { SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, reference_profile, &reference_info); // We should not advise compilation. - ProfileCompilationInfo* result = nullptr; ASSERT_TRUE(profile1.GetFile()->ResetOffset()); ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); - ASSERT_FALSE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); - ASSERT_TRUE(result == nullptr); - - // The information from profiles must still remain the same. - ProfileCompilationInfo file_info1; - ASSERT_TRUE(profile1.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info1.Load(GetFd(profile1))); - ASSERT_TRUE(file_info1.Equals(info1)); + ASSERT_EQ(ProfileAssistant::kErrorBadProfiles, + ProcessProfiles(profile_fds, reference_profile_fd)); - ProfileCompilationInfo file_info2; - ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info2.Load(GetFd(reference_profile))); - ASSERT_TRUE(file_info2.Equals(reference_info)); + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); } } // namespace art diff --git a/profman/profman.cc b/profman/profman.cc new file mode 100644 index 0000000000..7c9e449ed5 --- /dev/null +++ b/profman/profman.cc @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <string> +#include <vector> + +#include "base/dumpable.h" +#include "base/scoped_flock.h" +#include "base/stringpiece.h" +#include "base/stringprintf.h" +#include "base/time_utils.h" +#include "base/unix_file/fd_file.h" +#include "jit/offline_profiling_info.h" +#include "utils.h" +#include "profile_assistant.h" + +namespace art { + +static int original_argc; +static char** original_argv; + +static std::string CommandLine() { + std::vector<std::string> command; + for (int i = 0; i < original_argc; ++i) { + command.push_back(original_argv[i]); + } + return Join(command, ' '); +} + +static void UsageErrorV(const char* fmt, va_list ap) { + std::string error; + StringAppendV(&error, fmt, ap); + LOG(ERROR) << error; +} + +static void UsageError(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + UsageErrorV(fmt, ap); + va_end(ap); +} + +NO_RETURN static void Usage(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + UsageErrorV(fmt, ap); + va_end(ap); + + UsageError("Command: %s", CommandLine().c_str()); + UsageError("Usage: profman [options]..."); + UsageError(""); + UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation."); + UsageError(" Can be specified multiple time, in which case the data from the different"); + UsageError(" profiles will be aggregated."); + UsageError(""); + UsageError(" --profile-file-fd=<number>: same as --profile-file but accepts a file descriptor."); + UsageError(" Cannot be used together with --profile-file."); + UsageError(""); + UsageError(" --reference-profile-file=<filename>: specify a reference profile."); + UsageError(" The data in this file will be compared with the data obtained by merging"); + UsageError(" all the files specified with --profile-file or --profile-file-fd."); + UsageError(" If the exit code is EXIT_COMPILE then all --profile-file will be merged into"); + UsageError(" --reference-profile-file. "); + UsageError(""); + UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but"); + UsageError(" accepts a file descriptor. Cannot be used together with"); + UsageError(" --reference-profile-file."); + UsageError(""); + + exit(EXIT_FAILURE); +} + +class ProfMan FINAL { + public: + ProfMan() : + reference_profile_file_fd_(-1), + start_ns_(NanoTime()) {} + + ~ProfMan() { + LogCompletionTime(); + } + + void ParseArgs(int argc, char **argv) { + original_argc = argc; + original_argv = argv; + + InitLogging(argv); + + // Skip over the command name. + argv++; + argc--; + + if (argc == 0) { + Usage("No arguments specified"); + } + + for (int i = 0; i < argc; ++i) { + const StringPiece option(argv[i]); + const bool log_options = false; + if (log_options) { + LOG(INFO) << "patchoat: option[" << i << "]=" << argv[i]; + } + if (option.starts_with("--profile-file=")) { + profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString()); + } else if (option.starts_with("--profile-file-fd=")) { + ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_); + } else if (option.starts_with("--reference-profile-file=")) { + reference_profile_file_ = option.substr(strlen("--reference-profile-file=")).ToString(); + } else if (option.starts_with("--reference-profile-file-fd=")) { + ParseUintOption(option, "--reference-profile-file-fd", &reference_profile_file_fd_, Usage); + } else { + Usage("Unknown argument %s", option.data()); + } + } + + if (profile_files_.empty() && profile_files_fd_.empty()) { + Usage("No profile files specified."); + } + if (!profile_files_.empty() && !profile_files_fd_.empty()) { + Usage("Profile files should not be specified with both --profile-file-fd and --profile-file"); + } + if (!reference_profile_file_.empty() && (reference_profile_file_fd_ != -1)) { + Usage("--reference-profile-file-fd should only be supplied with --profile-file-fd"); + } + if (reference_profile_file_.empty() && (reference_profile_file_fd_ == -1)) { + Usage("Reference profile file not specified"); + } + } + + ProfileAssistant::ProcessingResult ProcessProfiles() { + ProfileAssistant::ProcessingResult result; + if (profile_files_.empty()) { + // The file doesn't need to be flushed here (ProcessProfiles will do it) + // so don't check the usage. + File file(reference_profile_file_fd_, false); + result = ProfileAssistant::ProcessProfiles(profile_files_fd_, reference_profile_file_fd_); + CloseAllFds(profile_files_fd_, "profile_files_fd_"); + } else { + result = ProfileAssistant::ProcessProfiles(profile_files_, reference_profile_file_); + } + return result; + } + + private: + static void ParseFdForCollection(const StringPiece& option, + const char* arg_name, + std::vector<int>* fds) { + int fd; + ParseUintOption(option, arg_name, &fd, Usage); + fds->push_back(fd); + } + + static void CloseAllFds(const std::vector<int>& fds, const char* descriptor) { + for (size_t i = 0; i < fds.size(); i++) { + if (close(fds[i]) < 0) { + PLOG(WARNING) << "Failed to close descriptor for " << descriptor << " at index " << i; + } + } + } + + void LogCompletionTime() { + LOG(INFO) << "profman took " << PrettyDuration(NanoTime() - start_ns_); + } + + std::vector<std::string> profile_files_; + std::vector<int> profile_files_fd_; + std::string reference_profile_file_; + int reference_profile_file_fd_; + uint64_t start_ns_; +}; + +// See ProfileAssistant::ProcessingResult for return codes. +static int profman(int argc, char** argv) { + ProfMan profman; + + // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. + profman.ParseArgs(argc, argv); + + // Process profile information and assess if we need to do a profile guided compilation. + // This operation involves I/O. + return profman.ProcessProfiles(); +} + +} // namespace art + +int main(int argc, char **argv) { + return art::profman(argc, argv); +} + diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index 771b2d0509..a4b38ea963 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -222,6 +222,10 @@ ArenaPool::ArenaPool(bool use_malloc, bool low_4gb) } ArenaPool::~ArenaPool() { + ReclaimMemory(); +} + +void ArenaPool::ReclaimMemory() { while (free_arenas_ != nullptr) { auto* arena = free_arenas_; free_arenas_ = free_arenas_->next_; @@ -229,6 +233,11 @@ ArenaPool::~ArenaPool() { } } +void ArenaPool::LockReclaimMemory() { + MutexLock lock(Thread::Current(), lock_); + ReclaimMemory(); +} + Arena* ArenaPool::AllocArena(size_t size) { Thread* self = Thread::Current(); Arena* ret = nullptr; diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h index 36334c4129..8a96571e99 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -276,6 +276,8 @@ class ArenaPool { Arena* AllocArena(size_t size) REQUIRES(!lock_); void FreeArenaChain(Arena* first) REQUIRES(!lock_); size_t GetBytesAllocated() const REQUIRES(!lock_); + void ReclaimMemory() NO_THREAD_SAFETY_ANALYSIS; + void LockReclaimMemory() REQUIRES(!lock_); // Trim the maps in arenas by madvising, used by JIT to reduce memory usage. This only works // use_malloc is false. void TrimMaps() REQUIRES(!lock_); diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc index 814cbd093a..0e8031f4f2 100644 --- a/runtime/base/scoped_flock.cc +++ b/runtime/base/scoped_flock.cc @@ -83,7 +83,7 @@ bool ScopedFlock::Init(const char* filename, int flags, bool block, std::string* } bool ScopedFlock::Init(File* file, std::string* error_msg) { - file_.reset(new File(dup(file->Fd()), true)); + file_.reset(new File(dup(file->Fd()), file->GetPath(), file->CheckUsage(), file->ReadOnlyMode())); if (file_->Fd() == -1) { file_.reset(); *error_msg = StringPrintf("Failed to duplicate open file '%s': %s", @@ -114,7 +114,13 @@ ScopedFlock::~ScopedFlock() { if (file_.get() != nullptr) { int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN)); CHECK_EQ(0, flock_result); - if (file_->FlushCloseOrErase() != 0) { + int close_result = -1; + if (file_->ReadOnlyMode()) { + close_result = file_->Close(); + } else { + close_result = file_->FlushCloseOrErase(); + } + if (close_result != 0) { PLOG(WARNING) << "Could not close scoped file lock file."; } } diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index e17bebb4fb..4672948f31 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -35,18 +35,22 @@ namespace unix_file { -FdFile::FdFile() : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true) { +FdFile::FdFile() + : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true), read_only_mode_(false) { } FdFile::FdFile(int fd, bool check_usage) : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck), - fd_(fd), auto_close_(true) { + fd_(fd), auto_close_(true), read_only_mode_(false) { } FdFile::FdFile(int fd, const std::string& path, bool check_usage) + : FdFile(fd, path, check_usage, false) { +} + +FdFile::FdFile(int fd, const std::string& path, bool check_usage, bool read_only_mode) : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck), - fd_(fd), file_path_(path), auto_close_(true) { - CHECK_NE(0U, path.size()); + fd_(fd), file_path_(path), auto_close_(true), read_only_mode_(read_only_mode) { } FdFile::~FdFile() { @@ -99,6 +103,7 @@ bool FdFile::Open(const std::string& path, int flags) { bool FdFile::Open(const std::string& path, int flags, mode_t mode) { CHECK_EQ(fd_, -1) << path; + read_only_mode_ = (flags & O_RDONLY) != 0; fd_ = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)); if (fd_ == -1) { return false; @@ -136,6 +141,7 @@ int FdFile::Close() { } int FdFile::Flush() { + DCHECK(!read_only_mode_); #ifdef __linux__ int rc = TEMP_FAILURE_RETRY(fdatasync(fd_)); #else @@ -155,6 +161,7 @@ int64_t FdFile::Read(char* buf, int64_t byte_count, int64_t offset) const { } int FdFile::SetLength(int64_t new_length) { + DCHECK(!read_only_mode_); #ifdef __linux__ int rc = TEMP_FAILURE_RETRY(ftruncate64(fd_, new_length)); #else @@ -171,6 +178,7 @@ int64_t FdFile::GetLength() const { } int64_t FdFile::Write(const char* buf, int64_t byte_count, int64_t offset) { + DCHECK(!read_only_mode_); #ifdef __linux__ int rc = TEMP_FAILURE_RETRY(pwrite64(fd_, buf, byte_count, offset)); #else @@ -184,6 +192,14 @@ int FdFile::Fd() const { return fd_; } +bool FdFile::ReadOnlyMode() const { + return read_only_mode_; +} + +bool FdFile::CheckUsage() const { + return guard_state_ != GuardState::kNoCheck; +} + bool FdFile::IsOpened() const { return fd_ >= 0; } @@ -219,6 +235,7 @@ bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) { } bool FdFile::WriteFully(const void* buffer, size_t byte_count) { + DCHECK(!read_only_mode_); const char* ptr = static_cast<const char*>(buffer); moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file."); while (byte_count > 0) { @@ -233,6 +250,7 @@ bool FdFile::WriteFully(const void* buffer, size_t byte_count) { } bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) { + DCHECK(!read_only_mode_); off_t off = static_cast<off_t>(offset); off_t sz = static_cast<off_t>(size); if (offset < 0 || static_cast<int64_t>(off) != offset || @@ -279,12 +297,14 @@ bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) { } void FdFile::Erase() { + DCHECK(!read_only_mode_); TEMP_FAILURE_RETRY(SetLength(0)); TEMP_FAILURE_RETRY(Flush()); TEMP_FAILURE_RETRY(Close()); } int FdFile::FlushCloseOrErase() { + DCHECK(!read_only_mode_); int flush_result = TEMP_FAILURE_RETRY(Flush()); if (flush_result != 0) { LOG(::art::ERROR) << "CloseOrErase failed while flushing a file."; @@ -301,6 +321,7 @@ int FdFile::FlushCloseOrErase() { } int FdFile::FlushClose() { + DCHECK(!read_only_mode_); int flush_result = TEMP_FAILURE_RETRY(Flush()); if (flush_result != 0) { LOG(::art::ERROR) << "FlushClose failed while flushing a file."; @@ -317,6 +338,7 @@ void FdFile::MarkUnchecked() { } bool FdFile::ClearContent() { + DCHECK(!read_only_mode_); if (SetLength(0) < 0) { PLOG(art::ERROR) << "Failed to reset the length"; return false; @@ -325,6 +347,7 @@ bool FdFile::ClearContent() { } bool FdFile::ResetOffset() { + DCHECK(!read_only_mode_); off_t rc = TEMP_FAILURE_RETRY(lseek(fd_, 0, SEEK_SET)); if (rc == static_cast<off_t>(-1)) { PLOG(art::ERROR) << "Failed to reset the offset"; diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h index 1e2d8af151..8040afe9b7 100644 --- a/runtime/base/unix_file/fd_file.h +++ b/runtime/base/unix_file/fd_file.h @@ -37,6 +37,7 @@ class FdFile : public RandomAccessFile { // file descriptor. (Use DisableAutoClose to retain ownership.) FdFile(int fd, bool checkUsage); FdFile(int fd, const std::string& path, bool checkUsage); + FdFile(int fd, const std::string& path, bool checkUsage, bool read_only_mode); // Destroys an FdFile, closing the file descriptor if Close hasn't already // been called. (If you care about the return value of Close, call it @@ -68,6 +69,8 @@ class FdFile : public RandomAccessFile { // Bonus API. int Fd() const; + bool ReadOnlyMode() const; + bool CheckUsage() const; bool IsOpened() const; const std::string& GetPath() const { return file_path_; @@ -119,6 +122,7 @@ class FdFile : public RandomAccessFile { int fd_; std::string file_path_; bool auto_close_; + bool read_only_mode_; DISALLOW_COPY_AND_ASSIGN(FdFile); }; diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 4d528ed74b..5c3029a5f8 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -1225,12 +1225,12 @@ TEST_F(ClassLinkerTest, RegisterDexFileName) { dex_cache->SetLocation(location.Get()); const DexFile* old_dex_file = dex_cache->GetDexFile(); - DexFile* dex_file = new DexFile(old_dex_file->Begin(), - old_dex_file->Size(), - location->ToModifiedUtf8(), - 0u, - nullptr, - nullptr); + std::unique_ptr<DexFile> dex_file(new DexFile(old_dex_file->Begin(), + old_dex_file->Size(), + location->ToModifiedUtf8(), + 0u, + nullptr, + nullptr)); { WriterMutexLock mu(soa.Self(), *class_linker->DexLock()); // Check that inserting with a UTF16 name works. diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc index 369e4083a2..83e5bad552 100644 --- a/runtime/gc/allocation_record.cc +++ b/runtime/gc/allocation_record.cc @@ -34,11 +34,7 @@ int32_t AllocRecordStackTraceElement::ComputeLineNumber() const { const char* AllocRecord::GetClassDescriptor(std::string* storage) const { // klass_ could contain null only if we implement class unloading. - if (UNLIKELY(klass_.IsNull())) { - return "null"; - } else { - return klass_.Read()->GetDescriptor(storage); - } + return klass_.IsNull() ? "null" : klass_.Read()->GetDescriptor(storage); } void AllocRecordObjectMap::SetProperties() { @@ -105,8 +101,19 @@ void AllocRecordObjectMap::VisitRoots(RootVisitor* visitor) { size_t count = recent_record_max_; // Only visit the last recent_record_max_ number of allocation records in entries_ and mark the // klass_ fields as strong roots. - for (auto it = entries_.rbegin(), end = entries_.rend(); count > 0 && it != end; count--, ++it) { - buffered_visitor.VisitRootIfNonNull(it->second->GetClassGcRoot()); + for (auto it = entries_.rbegin(), end = entries_.rend(); it != end; ++it) { + AllocRecord* record = it->second; + if (count > 0) { + buffered_visitor.VisitRootIfNonNull(record->GetClassGcRoot()); + --count; + } + // Visit all of the stack frames to make sure no methods in the stack traces get unloaded by + // class unloading. + for (size_t i = 0, depth = record->GetDepth(); i < depth; ++i) { + const AllocRecordStackTraceElement& element = record->StackElement(i); + DCHECK(element.GetMethod() != nullptr); + element.GetMethod()->VisitRoots(buffered_visitor, sizeof(void*)); + } } } @@ -131,12 +138,7 @@ void AllocRecordObjectMap::SweepAllocationRecords(IsMarkedVisitor* visitor) { VLOG(heap) << "Start SweepAllocationRecords()"; size_t count_deleted = 0, count_moved = 0, count = 0; // Only the first (size - recent_record_max_) number of records can be deleted. - size_t delete_bound; - if (entries_.size() <= recent_record_max_) { - delete_bound = 0; - } else { - delete_bound = entries_.size() - recent_record_max_; - } + const size_t delete_bound = std::max(entries_.size(), recent_record_max_) - recent_record_max_; for (auto it = entries_.begin(), end = entries_.end(); it != end;) { ++count; // This does not need a read barrier because this is called by GC. @@ -187,7 +189,6 @@ struct AllocRecordStackVisitor : public StackVisitor { SHARED_REQUIRES(Locks::mutator_lock_) : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), trace(trace_in), - depth(0), max_depth(max) {} // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses @@ -209,7 +210,7 @@ struct AllocRecordStackVisitor : public StackVisitor { } AllocRecordStackTrace* trace; - size_t depth; + size_t depth = 0u; const size_t max_depth; }; diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 3c9312f256..a656fb8faf 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -845,6 +845,13 @@ void Heap::DecrementDisableMovingGC(Thread* self) { void Heap::IncrementDisableThreadFlip(Thread* self) { // Supposed to be called by mutators. If thread_flip_running_ is true, block. Otherwise, go ahead. CHECK(kUseReadBarrier); + bool is_nested = self->GetDisableThreadFlipCount() > 0; + self->IncrementDisableThreadFlipCount(); + if (is_nested) { + // If this is a nested JNI critical section enter, we don't need to wait or increment the global + // counter. The global counter is incremented only once for a thread for the outermost enter. + return; + } ScopedThreadStateChange tsc(self, kWaitingForGcThreadFlip); MutexLock mu(self, *thread_flip_lock_); bool has_waited = false; @@ -867,10 +874,20 @@ void Heap::DecrementDisableThreadFlip(Thread* self) { // Supposed to be called by mutators. Decrement disable_thread_flip_count_ and potentially wake up // the GC waiting before doing a thread flip. CHECK(kUseReadBarrier); + self->DecrementDisableThreadFlipCount(); + bool is_outermost = self->GetDisableThreadFlipCount() == 0; + if (!is_outermost) { + // If this is not an outermost JNI critical exit, we don't need to decrement the global counter. + // The global counter is decremented only once for a thread for the outermost exit. + return; + } MutexLock mu(self, *thread_flip_lock_); CHECK_GT(disable_thread_flip_count_, 0U); --disable_thread_flip_count_; - thread_flip_cond_->Broadcast(self); + if (disable_thread_flip_count_ == 0) { + // Potentially notify the GC thread blocking to begin a thread flip. + thread_flip_cond_->Broadcast(self); + } } void Heap::ThreadFlipBegin(Thread* self) { @@ -882,7 +899,8 @@ void Heap::ThreadFlipBegin(Thread* self) { bool has_waited = false; uint64_t wait_start = NanoTime(); CHECK(!thread_flip_running_); - // Set this to true before waiting so that a new mutator entering a JNI critical won't starve GC. + // Set this to true before waiting so that frequent JNI critical enter/exits won't starve + // GC. This like a writer preference of a reader-writer lock. thread_flip_running_ = true; while (disable_thread_flip_count_ > 0) { has_waited = true; @@ -904,6 +922,7 @@ void Heap::ThreadFlipEnd(Thread* self) { MutexLock mu(self, *thread_flip_lock_); CHECK(thread_flip_running_); thread_flip_running_ = false; + // Potentially notify mutator threads blocking to enter a JNI critical section. thread_flip_cond_->Broadcast(self); } diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index c02e2d3864..a181e23b53 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -1113,6 +1113,8 @@ class Heap { // Used to synchronize between JNI critical calls and the thread flip of the CC collector. Mutex* thread_flip_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; std::unique_ptr<ConditionVariable> thread_flip_cond_ GUARDED_BY(thread_flip_lock_); + // This counter keeps track of how many threads are currently in a JNI critical section. This is + // incremented once per thread even with nested enters. size_t disable_thread_flip_count_ GUARDED_BY(thread_flip_lock_); bool thread_flip_running_ GUARDED_BY(thread_flip_lock_); diff --git a/runtime/interpreter/mterp/arm/op_shl_long.S b/runtime/interpreter/mterp/arm/op_shl_long.S index dc8a679cf7..12ea24883f 100644 --- a/runtime/interpreter/mterp/arm/op_shl_long.S +++ b/runtime/interpreter/mterp/arm/op_shl_long.S @@ -12,16 +12,16 @@ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r2<- r2 & 0x3f add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] - - mov r1, r1, asl r2 @ r1<- r1 << r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 - movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) + mov r1, r1, asl r2 @ r1<- r1 << r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - mov r0, r0, asl r2 @ r0<- r0 << r2 + mov r0, r0, asl r2 @ r0<- r0 << r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_shl_long_2addr.S b/runtime/interpreter/mterp/arm/op_shl_long_2addr.S index fd7668de62..4799e77213 100644 --- a/runtime/interpreter/mterp/arm/op_shl_long_2addr.S +++ b/runtime/interpreter/mterp/arm/op_shl_long_2addr.S @@ -6,17 +6,17 @@ mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs add r9, rFP, r9, lsl #2 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 - - mov r1, r1, asl r2 @ r1<- r1 << r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 + mov r1, r1, asl r2 @ r1<- r1 << r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) - mov r0, r0, asl r2 @ r0<- r0 << r2 + movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) + mov r0, r0, asl r2 @ r0<- r0 << r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_shr_long.S b/runtime/interpreter/mterp/arm/op_shr_long.S index c0edf90f76..88a13d6072 100644 --- a/runtime/interpreter/mterp/arm/op_shr_long.S +++ b/runtime/interpreter/mterp/arm/op_shr_long.S @@ -12,16 +12,16 @@ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r0<- r0 & 0x3f add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] - - mov r0, r0, lsr r2 @ r0<- r2 >> r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 - movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - mov r1, r1, asr r2 @ r1<- r1 >> r2 + mov r1, r1, asr r2 @ r1<- r1 >> r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_shr_long_2addr.S b/runtime/interpreter/mterp/arm/op_shr_long_2addr.S index ffeaf9c865..78d8bb7dba 100644 --- a/runtime/interpreter/mterp/arm/op_shr_long_2addr.S +++ b/runtime/interpreter/mterp/arm/op_shr_long_2addr.S @@ -6,17 +6,17 @@ mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs add r9, rFP, r9, lsl #2 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 - - mov r0, r0, lsr r2 @ r0<- r2 >> r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) - mov r1, r1, asr r2 @ r1<- r1 >> r2 + movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) + mov r1, r1, asr r2 @ r1<- r1 >> r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_ushr_long.S b/runtime/interpreter/mterp/arm/op_ushr_long.S index f64c861ce5..f98ec639fa 100644 --- a/runtime/interpreter/mterp/arm/op_ushr_long.S +++ b/runtime/interpreter/mterp/arm/op_ushr_long.S @@ -12,16 +12,16 @@ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r0<- r0 & 0x3f add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] - - mov r0, r0, lsr r2 @ r0<- r2 >> r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 - movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - mov r1, r1, lsr r2 @ r1<- r1 >>> r2 + mov r1, r1, lsr r2 @ r1<- r1 >>> r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S b/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S index dbab08d817..840283dd58 100644 --- a/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S +++ b/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S @@ -6,17 +6,17 @@ mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs add r9, rFP, r9, lsl #2 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 - - mov r0, r0, lsr r2 @ r0<- r2 >> r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) - mov r1, r1, lsr r2 @ r1<- r1 >>> r2 + movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) + mov r1, r1, lsr r2 @ r1<- r1 >>> r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 1a90acd799..8f4741c3ef 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -149,7 +149,7 @@ extern "C" bool MterpShouldSwitchInterpreters() Runtime::Current()->GetInstrumentation(); bool unhandled_instrumentation; // TODO: enable for other targets after more extensive testing. - if (kRuntimeISA == kArm64) { + if ((kRuntimeISA == kArm64) || (kRuntimeISA == kArm)) { unhandled_instrumentation = instrumentation->NonJitProfilingActive(); } else { unhandled_instrumentation = instrumentation->IsActive(); diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S index 519f8964fb..94cbd2d10e 100644 --- a/runtime/interpreter/mterp/out/mterp_arm.S +++ b/runtime/interpreter/mterp/out/mterp_arm.S @@ -5161,16 +5161,16 @@ constvalop_long_to_double: add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r2<- r2 & 0x3f add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] - - mov r1, r1, asl r2 @ r1<- r1 << r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 - movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) + mov r1, r1, asl r2 @ r1<- r1 << r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - mov r0, r0, asl r2 @ r0<- r0 << r2 + mov r0, r0, asl r2 @ r0<- r0 << r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -5193,16 +5193,16 @@ constvalop_long_to_double: add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r0<- r0 & 0x3f add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] - - mov r0, r0, lsr r2 @ r0<- r2 >> r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 - movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - mov r1, r1, asr r2 @ r1<- r1 >> r2 + mov r1, r1, asr r2 @ r1<- r1 >> r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -5225,16 +5225,16 @@ constvalop_long_to_double: add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r0<- r0 & 0x3f add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] - - mov r0, r0, lsr r2 @ r0<- r2 >> r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 - movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - mov r1, r1, lsr r2 @ r1<- r1 >>> r2 + mov r1, r1, lsr r2 @ r1<- r1 >>> r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -6300,17 +6300,17 @@ constvalop_long_to_double: mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs add r9, rFP, r9, lsl #2 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 - - mov r1, r1, asl r2 @ r1<- r1 << r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 + mov r1, r1, asl r2 @ r1<- r1 << r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) - mov r0, r0, asl r2 @ r0<- r0 << r2 + movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) + mov r0, r0, asl r2 @ r0<- r0 << r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -6327,17 +6327,17 @@ constvalop_long_to_double: mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs add r9, rFP, r9, lsl #2 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 - - mov r0, r0, lsr r2 @ r0<- r2 >> r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) - mov r1, r1, asr r2 @ r1<- r1 >> r2 + movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) + mov r1, r1, asr r2 @ r1<- r1 >> r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -6354,17 +6354,17 @@ constvalop_long_to_double: mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs add r9, rFP, r9, lsl #2 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 - - mov r0, r0, lsr r2 @ r0<- r2 >> r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) - mov r1, r1, lsr r2 @ r1<- r1 >>> r2 + movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) + mov r1, r1, lsr r2 @ r1<- r1 >>> r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 60ad0cbb10..0e175b85eb 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -261,6 +261,16 @@ void UnstartedRuntime::UnstartedClassGetDeclaredField( } } +void UnstartedRuntime::UnstartedClassGetEnclosingClass( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { + StackHandleScope<1> hs(self); + Handle<mirror::Class> klass(hs.NewHandle(shadow_frame->GetVRegReference(arg_offset)->AsClass())); + if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) { + result->SetL(nullptr); + } + result->SetL(klass->GetDexFile().GetEnclosingClass(klass)); +} + void UnstartedRuntime::UnstartedVmClassLoaderFindLoadedClass( Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h index 047e906614..6d4d711645 100644 --- a/runtime/interpreter/unstarted_runtime_list.h +++ b/runtime/interpreter/unstarted_runtime_list.h @@ -24,6 +24,7 @@ V(ClassClassForName, "java.lang.Class java.lang.Class.classForName(java.lang.String, boolean, java.lang.ClassLoader)") \ V(ClassNewInstance, "java.lang.Object java.lang.Class.newInstance()") \ V(ClassGetDeclaredField, "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") \ + V(ClassGetEnclosingClass, "java.lang.Class java.lang.Class.getEnclosingClass()") \ V(VmClassLoaderFindLoadedClass, "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") \ V(VoidLookupType, "java.lang.Class java.lang.Void.lookupType()") \ V(SystemArraycopy, "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)") \ diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 74ff741d93..d5a9d66210 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -232,25 +232,20 @@ static uintptr_t FromCodeToAllocation(const void* code) { void JitCodeCache::FreeCode(const void* code_ptr, ArtMethod* method ATTRIBUTE_UNUSED) { uintptr_t allocation = FromCodeToAllocation(code_ptr); const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - const uint8_t* data = method_header->GetNativeGcMap(); // Notify native debugger that we are about to remove the code. // It does nothing if we are not using native debugger. DeleteJITCodeEntryForAddress(reinterpret_cast<uintptr_t>(code_ptr)); - if (data != nullptr) { - mspace_free(data_mspace_, const_cast<uint8_t*>(data)); - } - data = method_header->GetMappingTable(); - if (data != nullptr) { - mspace_free(data_mspace_, const_cast<uint8_t*>(data)); - } + + FreeData(const_cast<uint8_t*>(method_header->GetNativeGcMap())); + FreeData(const_cast<uint8_t*>(method_header->GetMappingTable())); // Use the offset directly to prevent sanity check that the method is // compiled with optimizing. // TODO(ngeoffray): Clean up. if (method_header->vmap_table_offset_ != 0) { - data = method_header->code_ - method_header->vmap_table_offset_; - mspace_free(data_mspace_, const_cast<uint8_t*>(data)); + const uint8_t* data = method_header->code_ - method_header->vmap_table_offset_; + FreeData(const_cast<uint8_t*>(data)); } - mspace_free(code_mspace_, reinterpret_cast<uint8_t*>(allocation)); + FreeCode(reinterpret_cast<uint8_t*>(allocation)); } void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { @@ -281,7 +276,7 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { ProfilingInfo* info = *it; if (alloc.ContainsUnsafe(info->GetMethod())) { info->GetMethod()->SetProfilingInfo(nullptr); - mspace_free(data_mspace_, reinterpret_cast<uint8_t*>(info)); + FreeData(reinterpret_cast<uint8_t*>(info)); it = profiling_infos_.erase(it); } else { ++it; @@ -307,19 +302,18 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, OatQuickMethodHeader* method_header = nullptr; uint8_t* code_ptr = nullptr; + uint8_t* memory = nullptr; { ScopedThreadSuspension sts(self, kSuspended); MutexLock mu(self, lock_); WaitForPotentialCollectionToComplete(self); { ScopedCodeCacheWrite scc(code_map_.get()); - uint8_t* result = reinterpret_cast<uint8_t*>( - mspace_memalign(code_mspace_, alignment, total_size)); - if (result == nullptr) { + memory = AllocateCode(total_size); + if (memory == nullptr) { return nullptr; } - code_ptr = result + header_size; - DCHECK_ALIGNED_PARAM(reinterpret_cast<uintptr_t>(code_ptr), alignment); + code_ptr = memory + header_size; std::copy(code, code + code_size, code_ptr); method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); @@ -376,9 +370,7 @@ size_t JitCodeCache::CodeCacheSize() { } size_t JitCodeCache::CodeCacheSizeLocked() { - size_t bytes_allocated = 0; - mspace_inspect_all(code_mspace_, DlmallocBytesAllocatedCallback, &bytes_allocated); - return bytes_allocated; + return used_memory_for_code_; } size_t JitCodeCache::DataCacheSize() { @@ -387,9 +379,7 @@ size_t JitCodeCache::DataCacheSize() { } size_t JitCodeCache::DataCacheSizeLocked() { - size_t bytes_allocated = 0; - mspace_inspect_all(data_mspace_, DlmallocBytesAllocatedCallback, &bytes_allocated); - return bytes_allocated; + return used_memory_for_data_; } size_t JitCodeCache::NumberOfCompiledCode() { @@ -399,7 +389,7 @@ size_t JitCodeCache::NumberOfCompiledCode() { void JitCodeCache::ClearData(Thread* self, void* data) { MutexLock mu(self, lock_); - mspace_free(data_mspace_, data); + FreeData(reinterpret_cast<uint8_t*>(data)); } uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size) { @@ -410,7 +400,7 @@ uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size) { ScopedThreadSuspension sts(self, kSuspended); MutexLock mu(self, lock_); WaitForPotentialCollectionToComplete(self); - result = reinterpret_cast<uint8_t*>(mspace_malloc(data_mspace_, size)); + result = AllocateData(size); } if (result == nullptr) { @@ -419,7 +409,7 @@ uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size) { ScopedThreadSuspension sts(self, kSuspended); MutexLock mu(self, lock_); WaitForPotentialCollectionToComplete(self); - result = reinterpret_cast<uint8_t*>(mspace_malloc(data_mspace_, size)); + result = AllocateData(size); } return result; @@ -552,7 +542,7 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { // we hold the lock. { MutexLock mu(self, lock_); - if (!garbage_collect_code_) { + if (!garbage_collect_code_ || current_capacity_ < kReservedCapacity) { IncreaseCodeCacheCapacity(); NotifyCollectionDone(self); return; @@ -628,12 +618,11 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } } - void* data_mspace = data_mspace_; // Free all profiling infos of methods that were not being compiled. auto profiling_kept_end = std::remove_if(profiling_infos_.begin(), profiling_infos_.end(), - [data_mspace] (ProfilingInfo* info) { + [this] (ProfilingInfo* info) NO_THREAD_SAFETY_ANALYSIS { if (info->GetMethod()->GetProfilingInfo(sizeof(void*)) == nullptr) { - mspace_free(data_mspace, reinterpret_cast<uint8_t*>(info)); + FreeData(reinterpret_cast<uint8_t*>(info)); return true; } return false; @@ -718,7 +707,7 @@ ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self, return info; } - uint8_t* data = reinterpret_cast<uint8_t*>(mspace_malloc(data_mspace_, profile_info_size)); + uint8_t* data = AllocateData(profile_info_size); if (data == nullptr) { return nullptr; } @@ -809,5 +798,32 @@ void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, } } +uint8_t* JitCodeCache::AllocateCode(size_t code_size) { + size_t alignment = GetInstructionSetAlignment(kRuntimeISA); + uint8_t* result = reinterpret_cast<uint8_t*>( + mspace_memalign(code_mspace_, alignment, code_size)); + size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment); + // Ensure the header ends up at expected instruction alignment. + DCHECK_ALIGNED_PARAM(reinterpret_cast<uintptr_t>(result + header_size), alignment); + used_memory_for_code_ += mspace_usable_size(result); + return result; +} + +void JitCodeCache::FreeCode(uint8_t* code) { + used_memory_for_code_ -= mspace_usable_size(code); + mspace_free(code_mspace_, code); +} + +uint8_t* JitCodeCache::AllocateData(size_t data_size) { + void* result = mspace_malloc(data_mspace_, data_size); + used_memory_for_data_ += mspace_usable_size(result); + return reinterpret_cast<uint8_t*>(result); +} + +void JitCodeCache::FreeData(uint8_t* data) { + used_memory_for_data_ -= mspace_usable_size(data); + mspace_free(data_mspace_, data); +} + } // namespace jit } // namespace art diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 71f5cda0c8..74ce7b57fd 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -49,7 +49,10 @@ class JitCodeCache { static constexpr size_t kMaxCapacity = 64 * MB; // Put the default to a very low amount for debug builds to stress the code cache // collection. - static constexpr size_t kInitialCapacity = kIsDebugBuild ? 16 * KB : 64 * KB; + static constexpr size_t kInitialCapacity = kIsDebugBuild ? 8 * KB : 64 * KB; + + // By default, do not GC until reaching 256KB. + static constexpr size_t kReservedCapacity = kInitialCapacity * 4; // Create the code cache with a code + data capacity equal to "capacity", error message is passed // in the out arg error_msg. @@ -276,6 +279,17 @@ class JitCodeCache { // Whether we can do garbage collection. const bool garbage_collect_code_; + // The size in bytes of used memory for the data portion of the code cache. + size_t used_memory_for_data_ GUARDED_BY(lock_); + + // The size in bytes of used memory for the code portion of the code cache. + size_t used_memory_for_code_ GUARDED_BY(lock_); + + void FreeCode(uint8_t* code) REQUIRES(lock_); + uint8_t* AllocateCode(size_t code_size) REQUIRES(lock_); + void FreeData(uint8_t* data) REQUIRES(lock_); + uint8_t* AllocateData(size_t data_size) REQUIRES(lock_); + // Number of compilations done throughout the lifetime of the JIT. size_t number_of_compilations_ GUARDED_BY(lock_); diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc index 0aff1f7ec3..747b112f57 100644 --- a/runtime/jit/offline_profiling_info.cc +++ b/runtime/jit/offline_profiling_info.cc @@ -125,8 +125,8 @@ static constexpr const char kLineSeparator = '\n'; * app.apk,131232145,11,23,454,54 * app.apk:classes5.dex,218490184,39,13,49,1 **/ -bool ProfileCompilationInfo::Save(uint32_t fd) { - DCHECK_GE(fd, 0u); +bool ProfileCompilationInfo::Save(int fd) { + DCHECK_GE(fd, 0); // TODO(calin): Profile this and see how much memory it takes. If too much, // write to file directly. std::ostringstream os; @@ -232,8 +232,8 @@ static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& l return new_line_pos == -1 ? new_line_pos : new_line_pos + 1; } -bool ProfileCompilationInfo::Load(uint32_t fd) { - DCHECK_GE(fd, 0u); +bool ProfileCompilationInfo::Load(int fd) { + DCHECK_GE(fd, 0); std::string current_line; const int kBufferSize = 1024; @@ -343,7 +343,7 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* return os.str(); } -bool ProfileCompilationInfo::Equals(ProfileCompilationInfo& other) { +bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) { return info_.Equals(other.info_); } diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h index c388c4a42f..edc591c2eb 100644 --- a/runtime/jit/offline_profiling_info.h +++ b/runtime/jit/offline_profiling_info.h @@ -46,11 +46,11 @@ class ProfileCompilationInfo { const std::vector<ArtMethod*>& methods); // Loads profile information from the given file descriptor. - bool Load(uint32_t fd); + bool Load(int fd); // Loads the data from another ProfileCompilationInfo object. bool Load(const ProfileCompilationInfo& info); // Saves the profile data to the given file descriptor. - bool Save(uint32_t fd); + bool Save(int fd); // Returns the number of methods that were profiled. uint32_t GetNumberOfMethods() const; @@ -65,8 +65,7 @@ class ProfileCompilationInfo { bool print_full_dex_location = true) const; // For testing purposes. - bool Equals(ProfileCompilationInfo& other); - // Exposed for testing purpose. + bool Equals(const ProfileCompilationInfo& other); static std::string GetProfileDexFileKey(const std::string& dex_location); private: diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 861bd85283..eb5455a4cd 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1300,6 +1300,10 @@ void Runtime::InitNativeMethods() { VLOG(startup) << "Runtime::InitNativeMethods exiting"; } +void Runtime::ReclaimArenaPoolMemory() { + arena_pool_->LockReclaimMemory(); +} + void Runtime::InitThreadGroups(Thread* self) { JNIEnvExt* env = self->GetJniEnv(); ScopedJniEnvLocalRefState env_state(env); diff --git a/runtime/runtime.h b/runtime/runtime.h index cbb3e89444..8aac4ce9b4 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -564,6 +564,9 @@ class Runtime { const ArenaPool* GetArenaPool() const { return arena_pool_.get(); } + + void ReclaimArenaPoolMemory(); + LinearAlloc* GetLinearAlloc() { return linear_alloc_.get(); } diff --git a/runtime/thread.h b/runtime/thread.h index 2726e91130..97c47e1490 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -852,6 +852,22 @@ class Thread { tls32_.weak_ref_access_enabled = enabled; } + uint32_t GetDisableThreadFlipCount() const { + CHECK(kUseReadBarrier); + return tls32_.disable_thread_flip_count; + } + + void IncrementDisableThreadFlipCount() { + CHECK(kUseReadBarrier); + ++tls32_.disable_thread_flip_count; + } + + void DecrementDisableThreadFlipCount() { + CHECK(kUseReadBarrier); + DCHECK_GT(tls32_.disable_thread_flip_count, 0U); + --tls32_.disable_thread_flip_count; + } + // Activates single step control for debugging. The thread takes the // ownership of the given SingleStepControl*. It is deleted by a call // to DeactivateSingleStepControl or upon thread destruction. @@ -1214,7 +1230,8 @@ class Thread { daemon(is_daemon), throwing_OutOfMemoryError(false), no_thread_suspension(0), thread_exit_check_count(0), handling_signal_(false), suspended_at_suspend_check(false), ready_for_debug_invoke(false), - debug_method_entry_(false), is_gc_marking(false), weak_ref_access_enabled(true) { + debug_method_entry_(false), is_gc_marking(false), weak_ref_access_enabled(true), + disable_thread_flip_count(0) { } union StateAndFlags state_and_flags; @@ -1281,6 +1298,11 @@ class Thread { // pause, this is not an issue.) Other collectors use Runtime::DisallowNewSystemWeaks() and // ReferenceProcessor::EnableSlowPath(). bool32_t weak_ref_access_enabled; + + // A thread local version of Heap::disable_thread_flip_count_. This keeps track of how many + // levels of (nested) JNI critical sections the thread is in and is used to detect a nested JNI + // critical section enter. + uint32_t disable_thread_flip_count; } tls32_; struct PACKED(8) tls_64bit_sized_values { diff --git a/runtime/utils.cc b/runtime/utils.cc index 07f94c0766..13564a6a0f 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1392,9 +1392,8 @@ std::string GetSystemImageFilename(const char* location, const InstructionSet is return filename; } -bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) { +int ExecAndReturnCode(std::vector<std::string>& arg_vector, std::string* error_msg) { const std::string command_line(Join(arg_vector, ' ')); - CHECK_GE(arg_vector.size(), 1U) << command_line; // Convert the args to char pointers. @@ -1417,7 +1416,6 @@ bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) { setpgid(0, 0); execv(program, &args[0]); - PLOG(ERROR) << "Failed to execv(" << command_line << ")"; // _exit to avoid atexit handlers in child. _exit(1); @@ -1425,23 +1423,32 @@ bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) { if (pid == -1) { *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s", command_line.c_str(), strerror(errno)); - return false; + return -1; } // wait for subprocess to finish - int status; + int status = -1; pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); if (got_pid != pid) { *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: " "wanted %d, got %d: %s", command_line.c_str(), pid, got_pid, strerror(errno)); - return false; + return -1; } - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status", - command_line.c_str()); - return false; + if (WIFEXITED(status)) { + return WEXITSTATUS(status); } + return -1; + } +} + +bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) { + int status = ExecAndReturnCode(arg_vector, error_msg); + if (status != 0) { + const std::string command_line(Join(arg_vector, ' ')); + *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status", + command_line.c_str()); + return false; } return true; } diff --git a/runtime/utils.h b/runtime/utils.h index 79e4da19c8..83ac0b870e 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -292,6 +292,7 @@ std::string GetSystemImageFilename(const char* location, InstructionSet isa); // Wrapper on fork/execv to run a command in a subprocess. bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg); +int ExecAndReturnCode(std::vector<std::string>& arg_vector, std::string* error_msg); // Returns true if the file exists. bool FileExists(const std::string& filename); @@ -348,7 +349,7 @@ static void ParseUintOption(const StringPiece& option, UsageFn Usage, bool is_long_option = true) { std::string option_prefix = option_name + (is_long_option ? "=" : ""); - DCHECK(option.starts_with(option_prefix)); + DCHECK(option.starts_with(option_prefix)) << option << " " << option_prefix; const char* value_string = option.substr(option_prefix.size()).data(); int64_t parsed_integer_value = 0; if (!ParseInt(value_string, &parsed_integer_value)) { diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc index be7888b04a..70454828f1 100644 --- a/test/004-JniTest/jni_test.cc +++ b/test/004-JniTest/jni_test.cc @@ -639,3 +639,23 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testNewStringObject(JNIEnv* env, jcl extern "C" JNIEXPORT jlong JNICALL Java_Main_testGetMethodID(JNIEnv* env, jclass, jclass c) { return reinterpret_cast<jlong>(env->GetMethodID(c, "a", "()V")); } + +extern "C" JNIEXPORT void JNICALL Java_Main_enterJniCriticalSection(JNIEnv* env, jclass, + jint arraySize, + jbyteArray array0, + jbyteArray array1) { + for (int i = 0; i < 50000; ++i) { + char* data0 = reinterpret_cast<char*>(env->GetPrimitiveArrayCritical(array0, nullptr)); + char* data1 = reinterpret_cast<char*>(env->GetPrimitiveArrayCritical(array1, nullptr)); + bool up = i % 2 == 0; + for (int j = 0; j < arraySize; ++j) { + if (up) { + data1[j] = data0[j] + 1; + } else { + data0[j] = data1[j] + 1; + } + } + env->ReleasePrimitiveArrayCritical(array1, data1, 0); + env->ReleasePrimitiveArrayCritical(array0, data0, 0); + } +} diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java index ee3a3b9830..5c39ede687 100644 --- a/test/004-JniTest/src/Main.java +++ b/test/004-JniTest/src/Main.java @@ -38,6 +38,7 @@ public class Main { testNewStringObject(); testRemoveLocalObject(); testProxyGetMethodID(); + testJniCriticalSectionAndGc(); } private static native void testFindClassOnAttachedNativeThread(); @@ -222,6 +223,35 @@ public class Main { } private static native long testGetMethodID(Class<?> c); + + // Exercise GC and JNI critical sections in parallel. + private static void testJniCriticalSectionAndGc() { + Thread runGcThread = new Thread(new Runnable() { + @Override + public void run() { + for (int i = 0; i < 10; ++i) { + Runtime.getRuntime().gc(); + } + } + }); + Thread jniCriticalThread = new Thread(new Runnable() { + @Override + public void run() { + final int arraySize = 32; + byte[] array0 = new byte[arraySize]; + byte[] array1 = new byte[arraySize]; + enterJniCriticalSection(arraySize, array0, array1); + } + }); + jniCriticalThread.start(); + runGcThread.start(); + try { + jniCriticalThread.join(); + runGcThread.join(); + } catch (InterruptedException ignored) {} + } + + private static native void enterJniCriticalSection(int arraySize, byte[] array0, byte[] array); } class JniCallNonvirtualTest { diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java index af25d9bc54..e5c9dba63f 100644 --- a/test/082-inline-execute/src/Main.java +++ b/test/082-inline-execute/src/Main.java @@ -804,6 +804,7 @@ public class Main { Assert.assertEquals(Math.round(-2.9d), -3l); Assert.assertEquals(Math.round(-3.0d), -3l); Assert.assertEquals(Math.round(0.49999999999999994d), 0l); + Assert.assertEquals(Math.round(9007199254740991.0d), 9007199254740991l); // 2^53 - 1 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); @@ -825,6 +826,7 @@ public class Main { Assert.assertEquals(Math.round(-2.5f), -2); Assert.assertEquals(Math.round(-2.9f), -3); Assert.assertEquals(Math.round(-3.0f), -3); + Assert.assertEquals(Math.round(16777215.0f), 16777215); // 2^24 - 1 Assert.assertEquals(Math.round(Float.NaN), (int)+0.0f); Assert.assertEquals(Math.round(Integer.MAX_VALUE + 1.0f), Integer.MAX_VALUE); Assert.assertEquals(Math.round(Integer.MIN_VALUE - 1.0f), Integer.MIN_VALUE); diff --git a/test/130-hprof/src-ex/Allocator.java b/test/130-hprof/src-ex/Allocator.java new file mode 100644 index 0000000000..ee75a14f30 --- /dev/null +++ b/test/130-hprof/src-ex/Allocator.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2016 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. + */ + +// Simple allocator that returns a boot class path object. +public class Allocator { + public static Object allocObject() { + return new Object(); + } +} diff --git a/test/130-hprof/src/Main.java b/test/130-hprof/src/Main.java index 67e52323dd..9868c617f5 100644 --- a/test/130-hprof/src/Main.java +++ b/test/130-hprof/src/Main.java @@ -16,6 +16,7 @@ import java.io.File; import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; @@ -34,24 +35,21 @@ public class Main { } } - public static void main(String[] args) { - // Create some data. - Object data[] = new Object[TEST_LENGTH]; - for (int i = 0; i < data.length; i++) { - if (makeArray(i)) { - data[i] = new Object[TEST_LENGTH]; - } else { - data[i] = String.valueOf(i); - } + private static Object allocInDifferentLoader() throws Exception { + final String DEX_FILE = System.getenv("DEX_LOCATION") + "/130-hprof-ex.jar"; + Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader"); + if (pathClassLoader == null) { + throw new AssertionError("Couldn't find path class loader class"); } - for (int i = 0; i < data.length; i++) { - if (makeArray(i)) { - Object data2[] = (Object[]) data[i]; - fillArray(data, data2, i); - } - } - System.out.println("Generated data."); + Constructor constructor = + pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class); + ClassLoader loader = (ClassLoader)constructor.newInstance( + DEX_FILE, ClassLoader.getSystemClassLoader()); + Class allocator = loader.loadClass("Allocator"); + return allocator.getDeclaredMethod("allocObject", null).invoke(null); + } + private static void createDumpAndConv() throws RuntimeException { File dumpFile = null; File convFile = null; @@ -88,6 +86,43 @@ public class Main { } } + public static void main(String[] args) throws Exception { + // Create some data. + Object data[] = new Object[TEST_LENGTH]; + for (int i = 0; i < data.length; i++) { + if (makeArray(i)) { + data[i] = new Object[TEST_LENGTH]; + } else { + data[i] = String.valueOf(i); + } + } + for (int i = 0; i < data.length; i++) { + if (makeArray(i)) { + Object data2[] = (Object[]) data[i]; + fillArray(data, data2, i); + } + } + System.out.println("Generated data."); + + createDumpAndConv(); + Class klass = Class.forName("org.apache.harmony.dalvik.ddmc.DdmVmInternal"); + if (klass == null) { + throw new AssertionError("Couldn't find path class loader class"); + } + Method enableMethod = klass.getDeclaredMethod("enableRecentAllocations", + Boolean.TYPE); + if (enableMethod == null) { + throw new AssertionError("Couldn't find path class loader class"); + } + enableMethod.invoke(null, true); + Object o = allocInDifferentLoader(); + // Run GC to cause class unloading. + Runtime.getRuntime().gc(); + createDumpAndConv(); + // TODO: Somehow check contents of hprof file. + enableMethod.invoke(null, false); + } + private static File getHprofConf() { // Use the java.library.path. It points to the lib directory. File libDir = new File(System.getProperty("java.library.path")); diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index 44206df089..e6394a999f 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -272,5 +272,10 @@ "libcore.util.NativeAllocationRegistryTest#testNativeAllocationNoAllocatorAndNoSharedRegistry", "libcore.util.NativeAllocationRegistryTest#testNativeAllocationNoAllocatorAndSharedRegistry", "libcore.util.NativeAllocationRegistryTest#testNullArguments"] +}, +{ + description: "Only work with --mode=activity", + result: EXEC_FAILED, + names: [ "libcore.java.io.FileTest#testJavaIoTmpdirMutable" ] } ] diff --git a/tools/libcore_failures_concurrent_collector.txt b/tools/libcore_failures_concurrent_collector.txt index d8ef9baa64..19a61dc8cb 100644 --- a/tools/libcore_failures_concurrent_collector.txt +++ b/tools/libcore_failures_concurrent_collector.txt @@ -24,19 +24,6 @@ bug: 26155567 }, { - description: "TimeoutException on host-{x86,x86-64}-concurrent-collector", - result: EXEC_FAILED, - modes: [host], - names: ["libcore.java.util.zip.DeflaterOutputStreamTest#testSyncFlushEnabled", - "libcore.java.util.zip.DeflaterOutputStreamTest#testSyncFlushDisabled", - "libcore.java.util.zip.GZIPOutputStreamTest#testSyncFlushEnabled", - "libcore.java.util.zip.OldAndroidGZIPStreamTest#testGZIPStream", - "libcore.java.util.zip.OldAndroidZipStreamTest#testZipStream", - "libcore.java.util.zip.ZipFileTest#testZipFileWithLotsOfEntries", - "libcore.java.util.zip.ZipInputStreamTest#testLongMessage"], - bug: 26507762 -}, -{ description: "TimeoutException on hammerhead-concurrent-collector", result: EXEC_FAILED, modes: [device], diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index fda630fe5d..e4af9fa0d7 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -133,7 +133,7 @@ vogar $vm_command \ --vm-arg -Djpda.settings.transportAddress=127.0.0.1:55107 \ --vm-arg -Djpda.settings.debuggeeJavaPath="$art_debugee $image $debuggee_args" \ --classpath $test_jack \ - --toolchain jack \ + --toolchain jack --language JN \ --vm-arg -Xcompiler-option --vm-arg --debuggable \ $test |