Merge "Make JNI work correctly with default methods."
diff --git a/Android.mk b/Android.mk
index 2e05d33..e762814 100644
--- a/Android.mk
+++ b/Android.mk
@@ -86,6 +86,7 @@
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 ab70367..c9af1c6 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -205,7 +205,7 @@
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 @@
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 704d69a..22c0e8d 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -144,6 +144,12 @@
$(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 @@
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/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 159e9cf..3f61e8e 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -107,8 +107,7 @@
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 9ed5ec8..8ef1f28 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2490,6 +2490,7 @@
parallel_thread_pool_.get(),
parallel_thread_count_,
timings);
+ Runtime::Current()->ReclaimArenaPoolMemory();
}
VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
}
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index ba1b168..a7a1c0f 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -67,20 +67,28 @@
static bool IsAddOrSubAConstant(HInstruction* instruction,
/* out */ HInstruction** left_instruction,
/* out */ int32_t* right_constant) {
- if (instruction->IsAdd() || instruction->IsSub()) {
+ HInstruction* left_so_far = nullptr;
+ int32_t right_so_far = 0;
+ while (instruction->IsAdd() || instruction->IsSub()) {
HBinaryOperation* bin_op = instruction->AsBinaryOperation();
HInstruction* left = bin_op->GetLeft();
HInstruction* right = bin_op->GetRight();
if (right->IsIntConstant()) {
- *left_instruction = left;
- int32_t c = right->AsIntConstant()->GetValue();
- *right_constant = instruction->IsAdd() ? c : -c;
- return true;
+ int32_t v = right->AsIntConstant()->GetValue();
+ int32_t c = instruction->IsAdd() ? v : -v;
+ if (!WouldAddOverflowOrUnderflow(right_so_far, c)) {
+ instruction = left;
+ left_so_far = left;
+ right_so_far += c;
+ continue;
+ }
}
+ break;
}
- *left_instruction = nullptr;
- *right_constant = 0;
- return false;
+ // Return result: either false and "null+0" or true and "instr+constant".
+ *left_instruction = left_so_far;
+ *right_constant = right_so_far;
+ return left_so_far != nullptr;
}
// Expresses any instruction as a value bound.
diff --git a/compiler/profile_assistant.cc b/compiler/profile_assistant.cc
deleted file mode 100644
index 85335ef..0000000
--- a/compiler/profile_assistant.cc
+++ /dev/null
@@ -1,169 +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.
- */
-
-#include "profile_assistant.h"
-
-#include "base/unix_file/fd_file.h"
-#include "os.h"
-
-namespace art {
-
-// Minimum number of new methods that profiles must contain to enable recompilation.
-static constexpr const uint32_t kMinNewMethodsForCompilation = 10;
-
-bool ProfileAssistant::ProcessProfilesInternal(
- const std::vector<ScopedFlock>& profile_files,
- const std::vector<ScopedFlock>& reference_profile_files,
- /*out*/ ProfileCompilationInfo** profile_compilation_info) {
- 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;
- // Read the main profile files.
- 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;
- }
- // 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;
- }
-
- std::unique_ptr<ProfileCompilationInfo> result(new ProfileCompilationInfo());
- // Merge information.
- 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])) {
- LOG(WARNING) << "Could not merge profile data at index " << i;
- return false;
- }
- }
- // 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;
- }
- }
- }
-
- *profile_compilation_info = result.release();
- return true;
-}
-
-class ScopedCollectionFlock {
- public:
- explicit ScopedCollectionFlock(size_t size) : flocks_(size) {}
-
- // 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)) {
- *error += " (index=" + std::to_string(i) + ")";
- return false;
- }
- }
- return true;
- }
-
- // Will block until all the locks are acquired.
- bool Init(const std::vector<uint32_t>& 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)) {
- *error += " (index=" + std::to_string(i) + ")";
- return false;
- }
- }
- return true;
- }
-
- const std::vector<ScopedFlock>& Get() const { return flocks_; }
-
- private:
- 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;
-
- 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;
- }
- 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;
- }
-
- return ProcessProfilesInternal(profile_files_flocks.Get(),
- reference_profile_files_flocks.Get(),
- profile_compilation_info);
-}
-
-bool 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;
-
- 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;
- }
- ScopedCollectionFlock reference_profile_files_flocks(reference_profile_files.size());
- if (!reference_profile_files_flocks.Init(reference_profile_files, &error)) {
- LOG(WARNING) << "Could not lock reference profile files: " << error;
- return false;
- }
-
- return ProcessProfilesInternal(profile_files_flocks.Get(),
- reference_profile_files_flocks.Get(),
- profile_compilation_info);
-}
-
-} // namespace art
diff --git a/compiler/profile_assistant.h b/compiler/profile_assistant.h
deleted file mode 100644
index ad5e216..0000000
--- 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/compiler/profile_assistant_test.cc b/compiler/profile_assistant_test.cc
deleted file mode 100644
index 58b7513..0000000
--- a/compiler/profile_assistant_test.cc
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * 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 <gtest/gtest.h>
-
-#include "base/unix_file/fd_file.h"
-#include "common_runtime_test.h"
-#include "compiler/profile_assistant.h"
-#include "jit/offline_profiling_info.h"
-
-namespace art {
-
-class ProfileAssistantTest : public CommonRuntimeTest {
- protected:
- void SetupProfile(const std::string& id,
- uint32_t checksum,
- uint16_t number_of_methods,
- const ScratchFile& profile,
- ProfileCompilationInfo* info,
- uint16_t start_method_index = 0) {
- std::string dex_location1 = "location1" + id;
- uint32_t dex_location_checksum1 = checksum;
- std::string dex_location2 = "location2" + id;
- uint32_t dex_location_checksum2 = 10 * checksum;
- for (uint16_t i = start_method_index; i < start_method_index + number_of_methods; i++) {
- ASSERT_TRUE(info->AddData(dex_location1, dex_location_checksum1, i));
- ASSERT_TRUE(info->AddData(dex_location2, dex_location_checksum2, i));
- }
- ASSERT_TRUE(info->Save(GetFd(profile)));
- ASSERT_EQ(0, profile.GetFile()->Flush());
- ASSERT_TRUE(profile.GetFile()->ResetOffset());
- }
-
- uint32_t GetFd(const ScratchFile& file) const {
- return static_cast<uint32_t>(file.GetFd());
- }
-};
-
-TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) {
- ScratchFile profile1;
- ScratchFile profile2;
- ScratchFile reference_profile1;
- ScratchFile reference_profile2;
-
- std::vector<uint32_t> profile_fds({
- GetFd(profile1),
- GetFd(profile2)});
- std::vector<uint32_t> reference_profile_fds({
- GetFd(reference_profile1),
- GetFd(reference_profile2)});
-
- const uint16_t kNumberOfMethodsToEnableCompilation = 100;
- ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
- ProfileCompilationInfo info2;
- 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);
-
- // The resulting compilation info must be equal to the merge of the inputs.
- ProfileCompilationInfo expected;
- ASSERT_TRUE(expected.Load(info1));
- ASSERT_TRUE(expected.Load(info2));
- 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());
-}
-
-TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) {
- ScratchFile profile1;
- ScratchFile profile2;
- ScratchFile reference_profile1;
- ScratchFile reference_profile2;
-
- std::vector<uint32_t> profile_fds({
- GetFd(profile1),
- GetFd(profile2)});
- std::vector<uint32_t> reference_profile_fds({
- GetFd(reference_profile1),
- GetFd(reference_profile2)});
-
- // The new profile info will contain the methods with indices 0-100.
- const uint16_t kNumberOfMethodsToEnableCompilation = 100;
- ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
- ProfileCompilationInfo info2;
- SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2);
-
-
- // 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);
-
- // We should advise compilation.
- ProfileCompilationInfo* result;
- ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result));
- ASSERT_TRUE(result != nullptr);
-
- // The resulting compilation info must be equal to the merge of the inputs
- 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));
-
- 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());
-}
-
-TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) {
- ScratchFile profile1;
- ScratchFile profile2;
- ScratchFile reference_profile1;
- ScratchFile reference_profile2;
-
- std::vector<uint32_t> profile_fds({
- GetFd(profile1),
- GetFd(profile2)});
- std::vector<uint32_t> reference_profile_fds({
- GetFd(reference_profile1),
- GetFd(reference_profile2)});
-
- const uint16_t kNumberOfMethodsToSkipCompilation = 1;
- ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToSkipCompilation, profile1, &info1);
- ProfileCompilationInfo info2;
- 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);
-
- // The information from profiles must 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));
-
- // Reference profile files must remain empty.
- ASSERT_EQ(0, reference_profile1.GetFile()->GetLength());
- ASSERT_EQ(0, reference_profile2.GetFile()->GetLength());
-}
-
-TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) {
- ScratchFile profile1;
- ScratchFile profile2;
- ScratchFile reference_profile1;
- ScratchFile reference_profile2;
-
- std::vector<uint32_t> profile_fds({
- GetFd(profile1),
- GetFd(profile2)});
- std::vector<uint32_t> reference_profile_fds({
- GetFd(reference_profile1),
- GetFd(reference_profile2)});
-
- const uint16_t kNumberOfMethodsToEnableCompilation = 100;
- // Assign different hashes for the same dex file. This will make merging of information to fail.
- ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
- ProfileCompilationInfo info2;
- 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);
-
- // 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));
-
- // Reference profile files must still remain empty.
- ASSERT_EQ(0, reference_profile1.GetFile()->GetLength());
- ASSERT_EQ(0, reference_profile2.GetFile()->GetLength());
-}
-
-TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) {
- ScratchFile profile1;
- ScratchFile reference_profile;
-
- std::vector<uint32_t> profile_fds({
- GetFd(profile1)});
- std::vector<uint32_t> reference_profile_fds({
- GetFd(reference_profile)});
-
- const uint16_t kNumberOfMethodsToEnableCompilation = 100;
- // Assign different hashes for the same dex file. This will make merging of information to fail.
- ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
- ProfileCompilationInfo reference_info;
- 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));
-
- ProfileCompilationInfo file_info2;
- ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info2.Load(GetFd(reference_profile)));
- ASSERT_TRUE(file_info2.Equals(reference_info));
-}
-
-} // namespace art
diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk
index 77f8d6c..dfc379f 100644
--- a/dex2oat/Android.mk
+++ b/dex2oat/Android.mk
@@ -55,20 +55,42 @@
$(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 541fb5a..f7efdc5 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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
} 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 @@
}
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 @@
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 @@
// 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 0000000..d38d107
--- /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/profman/profile_assistant.cc b/profman/profile_assistant.cc
new file mode 100644
index 0000000..58e8a3a
--- /dev/null
+++ b/profman/profile_assistant.cc
@@ -0,0 +1,159 @@
+/*
+ * 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 "profile_assistant.h"
+
+#include "base/unix_file/fd_file.h"
+#include "os.h"
+
+namespace art {
+
+// Minimum number of new methods that profiles must contain to enable recompilation.
+static constexpr const uint32_t kMinNewMethodsForCompilation = 10;
+
+ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfilesInternal(
+ const std::vector<ScopedFlock>& profile_files,
+ const ScopedFlock& reference_profile_file) {
+ DCHECK(!profile_files.empty());
+
+ std::vector<ProfileCompilationInfo> new_info(profile_files.size());
+ bool should_compile = false;
+ // Read the main profile files.
+ 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 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 kSkipCompilation;
+ }
+
+ // 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++) {
+ // Merge all data into a single object.
+ if (!info.Load(new_info[i])) {
+ LOG(WARNING) << "Could not merge profile data at index " << i;
+ return kErrorBadProfiles;
+ }
+ }
+ // 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;
+}
+
+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 {
+ public:
+ explicit ScopedCollectionFlock(size_t size) : flocks_(size) {}
+
+ // 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 (!InitFlock(filenames[i], flocks_[i], error)) {
+ *error += " (index=" + std::to_string(i) + ")";
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Will block until all the locks are acquired.
+ bool Init(const std::vector<int>& fds, /* out */ std::string* error) {
+ for (size_t i = 0; i < fds.size(); i++) {
+ DCHECK_GE(fds[i], 0);
+ if (!InitFlock(fds[i], flocks_[i], error)) {
+ *error += " (index=" + std::to_string(i) + ")";
+ return false;
+ }
+ }
+ return true;
+ }
+
+ const std::vector<ScopedFlock>& Get() const { return flocks_; }
+
+ private:
+ std::vector<ScopedFlock> flocks_;
+};
+
+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 kErrorCannotLock;
+ }
+ 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_file_flock);
+}
+
+ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfiles(
+ const std::vector<std::string>& profile_files,
+ 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 kErrorCannotLock;
+ }
+ 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 kErrorCannotLock;
+ }
+
+ return ProcessProfilesInternal(profile_files_flocks.Get(),
+ reference_profile_file_flock);
+}
+
+} // namespace art
diff --git a/profman/profile_assistant.h b/profman/profile_assistant.h
new file mode 100644
index 0000000..d3c75b8
--- /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/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
new file mode 100644
index 0000000..3faa8eb
--- /dev/null
+++ b/profman/profile_assistant_test.cc
@@ -0,0 +1,251 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include "base/unix_file/fd_file.h"
+#include "common_runtime_test.h"
+#include "profile_assistant.h"
+#include "jit/offline_profiling_info.h"
+#include "utils.h"
+
+namespace art {
+
+class ProfileAssistantTest : public CommonRuntimeTest {
+ protected:
+ void SetupProfile(const std::string& id,
+ uint32_t checksum,
+ uint16_t number_of_methods,
+ const ScratchFile& profile,
+ ProfileCompilationInfo* info,
+ uint16_t start_method_index = 0) {
+ std::string dex_location1 = "location1" + id;
+ uint32_t dex_location_checksum1 = checksum;
+ std::string dex_location2 = "location2" + id;
+ uint32_t dex_location_checksum2 = 10 * checksum;
+ for (uint16_t i = start_method_index; i < start_method_index + number_of_methods; i++) {
+ ASSERT_TRUE(info->AddData(dex_location1, dex_location_checksum1, i));
+ ASSERT_TRUE(info->AddData(dex_location2, dex_location_checksum2, i));
+ }
+ ASSERT_TRUE(info->Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ }
+
+ 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_profile;
+
+ std::vector<int> profile_fds({
+ GetFd(profile1),
+ GetFd(profile2)});
+ int reference_profile_fd = GetFd(reference_profile);
+
+ const uint16_t kNumberOfMethodsToEnableCompilation = 100;
+ ProfileCompilationInfo info1;
+ SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
+ ProfileCompilationInfo info2;
+ SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2);
+
+ // We should advise compilation.
+ 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));
+
+ // The information from profiles must remain the same.
+ CheckProfileInfo(profile1, info1);
+ CheckProfileInfo(profile2, info2);
+}
+
+TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) {
+ ScratchFile profile1;
+ ScratchFile profile2;
+ ScratchFile reference_profile;
+
+ std::vector<int> profile_fds({
+ GetFd(profile1),
+ GetFd(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;
+ ProfileCompilationInfo info1;
+ SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
+ ProfileCompilationInfo info2;
+ SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2);
+
+
+ // The reference profile info will contain the methods with indices 50-150.
+ const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
+ ProfileCompilationInfo reference_info;
+ SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, reference_profile,
+ &reference_info, kNumberOfMethodsToEnableCompilation / 2);
+
+ // We should advise compilation.
+ 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_info));
+ ASSERT_TRUE(expected.Equals(result));
+
+ // The information from profiles must remain the same.
+ CheckProfileInfo(profile1, info1);
+ CheckProfileInfo(profile2, info2);
+}
+
+TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) {
+ ScratchFile profile1;
+ ScratchFile profile2;
+ ScratchFile reference_profile;
+
+ std::vector<int> profile_fds({
+ GetFd(profile1),
+ GetFd(profile2)});
+ int reference_profile_fd = GetFd(reference_profile);
+
+ const uint16_t kNumberOfMethodsToSkipCompilation = 1;
+ ProfileCompilationInfo info1;
+ SetupProfile("p1", 1, kNumberOfMethodsToSkipCompilation, profile1, &info1);
+ ProfileCompilationInfo info2;
+ SetupProfile("p2", 2, kNumberOfMethodsToSkipCompilation, profile2, &info2);
+
+ // We should not advise compilation.
+ ASSERT_EQ(ProfileAssistant::kSkipCompilation,
+ ProcessProfiles(profile_fds, reference_profile_fd));
+
+ // The information from profiles must 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));
+
+ // Reference profile files must remain empty.
+ 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_profile;
+
+ std::vector<int> profile_fds({
+ GetFd(profile1),
+ GetFd(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.
+ ProfileCompilationInfo info1;
+ SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
+ ProfileCompilationInfo info2;
+ SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2);
+
+ // We should fail processing.
+ ASSERT_EQ(ProfileAssistant::kErrorBadProfiles,
+ ProcessProfiles(profile_fds, reference_profile_fd));
+
+ // 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_profile.GetFile()->GetLength());
+}
+
+TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) {
+ ScratchFile profile1;
+ ScratchFile reference_profile;
+
+ std::vector<int> profile_fds({
+ GetFd(profile1)});
+ 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.
+ ProfileCompilationInfo info1;
+ SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
+ ProfileCompilationInfo reference_info;
+ SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, reference_profile, &reference_info);
+
+ // We should not advise compilation.
+ ASSERT_TRUE(profile1.GetFile()->ResetOffset());
+ ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
+ ASSERT_EQ(ProfileAssistant::kErrorBadProfiles,
+ ProcessProfiles(profile_fds, reference_profile_fd));
+
+ // 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 0000000..7c9e449
--- /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 771b2d0..a4b38ea 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -222,6 +222,10 @@
}
ArenaPool::~ArenaPool() {
+ ReclaimMemory();
+}
+
+void ArenaPool::ReclaimMemory() {
while (free_arenas_ != nullptr) {
auto* arena = free_arenas_;
free_arenas_ = free_arenas_->next_;
@@ -229,6 +233,11 @@
}
}
+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 36334c4..8a96571 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -276,6 +276,8 @@
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 814cbd0..0e8031f 100644
--- a/runtime/base/scoped_flock.cc
+++ b/runtime/base/scoped_flock.cc
@@ -83,7 +83,7 @@
}
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 @@
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 e17bebb..4672948 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, 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::Flush() {
+ DCHECK(!read_only_mode_);
#ifdef __linux__
int rc = TEMP_FAILURE_RETRY(fdatasync(fd_));
#else
@@ -155,6 +161,7 @@
}
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::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 @@
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::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::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 @@
}
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::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 @@
}
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::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 1e2d8af..8040afe 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -37,6 +37,7 @@
// 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 @@
// 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 @@
int fd_;
std::string file_path_;
bool auto_close_;
+ bool read_only_mode_;
DISALLOW_COPY_AND_ASSIGN(FdFile);
};
diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc
index 369e408..4de5388 100644
--- a/runtime/gc/allocation_record.cc
+++ b/runtime/gc/allocation_record.cc
@@ -34,11 +34,7 @@
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 @@
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 @@
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 @@
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 @@
}
AllocRecordStackTrace* trace;
- size_t depth;
+ size_t depth = 0u;
const size_t max_depth;
};
@@ -237,9 +238,12 @@
<< records->max_stack_depth_ << " frames, taking up to "
<< PrettySize(sz * records->alloc_record_max_) << ")";
heap->SetAllocationRecords(records);
- heap->SetAllocTrackingEnabled(true);
}
Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints();
+ {
+ MutexLock mu(self, *Locks::alloc_tracker_lock_);
+ heap->SetAllocTrackingEnabled(true);
+ }
} else {
{
MutexLock mu(self, *Locks::alloc_tracker_lock_);
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 71d16e0..d5a9d66 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -542,7 +542,7 @@
// we hold the lock.
{
MutexLock mu(self, lock_);
- if (!garbage_collect_code_) {
+ if (!garbage_collect_code_ || current_capacity_ < kReservedCapacity) {
IncreaseCodeCacheCapacity();
NotifyCollectionDone(self);
return;
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index ac53532..74ce7b5 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -49,7 +49,10 @@
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.
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index 0aff1f7..747b112 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -125,8 +125,8 @@
* 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 @@
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 @@
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 c388c4a..edc591c 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -46,11 +46,11 @@
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 @@
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 861bd85..eb5455a 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1300,6 +1300,10 @@
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 cbb3e89..8aac4ce 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -564,6 +564,9 @@
const ArenaPool* GetArenaPool() const {
return arena_pool_.get();
}
+
+ void ReclaimArenaPoolMemory();
+
LinearAlloc* GetLinearAlloc() {
return linear_alloc_.get();
}
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 07f94c0..13564a6 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1392,9 +1392,8 @@
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 @@
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 @@
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 79e4da1..83ac0b8 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -292,6 +292,7 @@
// 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 @@
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/130-hprof/src-ex/Allocator.java b/test/130-hprof/src-ex/Allocator.java
new file mode 100644
index 0000000..ee75a14
--- /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 67e5232..9868c61 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 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 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/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index 31bb94c..32bbc5b 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -391,6 +391,36 @@
array[base + 1] = 1;
}
+ /// CHECK-START: void Main.constantIndexing10(int[], int) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+
+ /// CHECK-START: void Main.constantIndexing10(int[], int) BCE (after)
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+
+ static void constantIndexing10(int[] array, int base) {
+ // Offset hidden in incremented base.
+ array[base] = 1;
+ array[++base] = 2;
+ array[++base] = 3;
+ array[++base] = 4;
+ }
+
static void runAllConstantIndices() {
int[] a1 = { 0 };
int[] a6 = { 0, 0, 0, 0, 0, 0 };
@@ -502,6 +532,12 @@
a6[3] != 3 || a6[4] != 40 || a6[5] != 10) {
System.out.println("constant indices 9 failed!");
}
+
+ constantIndexing10(a6, 0);
+ if (a6[0] != 1 || a6[1] != 2 || a6[2] != 3 ||
+ a6[3] != 4 || a6[4] != 40 || a6[5] != 10) {
+ System.out.println("constant indices 10 failed!");
+ }
}
// A helper into which the actual throwing function should be inlined.
diff --git a/tools/libcore_failures_concurrent_collector.txt b/tools/libcore_failures_concurrent_collector.txt
index f347429..19a61dc 100644
--- a/tools/libcore_failures_concurrent_collector.txt
+++ b/tools/libcore_failures_concurrent_collector.txt
@@ -24,20 +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.GZIPInputStreamTest#testLongMessage",
- "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],