Revert "Add profman tool: responsible to process profiles"
Needs a profile_assistant_test fix.
Bug: 26719109
Bug: 26563023
This reverts commit 6caefd983a800a063b219f1d3ed71b1416cecd70.
Change-Id: Ibdeb7385737dd7846ed861e0a95f083abb9aa974
diff --git a/Android.mk b/Android.mk
index e762814..2e05d33 100644
--- a/Android.mk
+++ b/Android.mk
@@ -86,7 +86,6 @@
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.gtest.mk b/build/Android.gtest.mk
index 22c0e8d..704d69a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -144,12 +144,6 @@
$(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
@@ -159,7 +153,6 @@
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 \
@@ -277,6 +270,7 @@
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 3dfb4b1..b164942 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -108,7 +108,8 @@
elf_writer.cc \
elf_writer_quick.cc \
image_writer.cc \
- oat_writer.cc
+ oat_writer.cc \
+ profile_assistant.cc
LIBART_COMPILER_SRC_FILES_arm := \
dex/quick/arm/assemble_arm.cc \
diff --git a/compiler/profile_assistant.cc b/compiler/profile_assistant.cc
new file mode 100644
index 0000000..85335ef
--- /dev/null
+++ b/compiler/profile_assistant.cc
@@ -0,0 +1,169 @@
+/*
+ * 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
new file mode 100644
index 0000000..ad5e216
--- /dev/null
+++ b/compiler/profile_assistant.h
@@ -0,0 +1,72 @@
+/*
+ * 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
new file mode 100644
index 0000000..58b7513
--- /dev/null
+++ b/compiler/profile_assistant_test.cc
@@ -0,0 +1,279 @@
+/*
+ * 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/dex2oat.cc b/dex2oat/dex2oat.cc
index f7efdc5..541fb5a 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -40,7 +40,6 @@
#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"
@@ -72,6 +71,7 @@
#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,10 +339,23 @@
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.");
@@ -504,6 +517,14 @@
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) :
@@ -546,12 +567,10 @@
dump_passes_(false),
dump_timing_(false),
dump_slow_timing_(kIsDebugBuild),
- swap_fd_(kInvalidFd),
+ swap_fd_(-1),
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
@@ -805,8 +824,25 @@
}
}
- if (!profile_file_.empty() && (profile_file_fd_ != kInvalidFd)) {
- Usage("Profile file should not be specified with both --profile-file-fd and --profile-file");
+ 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 (!parser_options->oat_symbols.empty()) {
@@ -1117,9 +1153,16 @@
} else if (option.starts_with("--compiler-backend=")) {
ParseCompilerBackend(option, parser_options.get());
} else if (option.starts_with("--profile-file=")) {
- profile_file_ = option.substr(strlen("--profile-file=")).ToString();
+ 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());
} else if (option.starts_with("--profile-file-fd=")) {
- ParseUintOption(option, "--profile-file-fd", &profile_file_fd_, Usage);
+ 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
} else if (option == "--host") {
is_host_ = true;
} else if (option == "--runtime-arg") {
@@ -1822,44 +1865,33 @@
}
bool UseProfileGuidedCompilation() const {
- return !profile_file_.empty() || (profile_file_fd_ != kInvalidFd);
+ return !profile_files_.empty() || !profile_files_fd_.empty();
}
- bool LoadProfile() {
+ bool ProcessProfiles() {
DCHECK(UseProfileGuidedCompilation());
-
- 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_);
- }
+ 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_");
} else {
- 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;
+ result = ProfileAssistant::ProcessProfiles(
+ profile_files_, reference_profile_files_, &info);
}
- if (!success) {
- profile_compilation_info_.reset(nullptr);
- }
+ profile_compilation_info_.reset(info);
- return success;
+ return result;
}
bool ShouldCompileBasedOnProfiles() const {
DCHECK(UseProfileGuidedCompilation());
- // 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);
+ // If we are given profiles, compile only if we have new information.
+ return profile_compilation_info_ != nullptr;
}
private:
@@ -2428,8 +2460,10 @@
int swap_fd_;
std::string app_image_file_name_;
int app_image_fd_;
- std::string profile_file_;
- int profile_file_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::unique_ptr<ProfileCompilationInfo> profile_compilation_info_;
TimingLogger* timings_;
std::unique_ptr<CumulativeLogger> compiler_phases_timings_;
@@ -2558,7 +2592,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.LoadProfile()) {
+ if (dex2oat.ProcessProfiles()) {
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
deleted file mode 100644
index d38d107..0000000
--- a/profman/Android.mk
+++ /dev/null
@@ -1,45 +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.
-#
-
-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
deleted file mode 100644
index 58e8a3a..0000000
--- a/profman/profile_assistant.cc
+++ /dev/null
@@ -1,159 +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 "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
deleted file mode 100644
index d3c75b8..0000000
--- a/profman/profile_assistant.h
+++ /dev/null
@@ -1,72 +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.
- */
-
-#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
deleted file mode 100644
index 543be5d..0000000
--- a/profman/profile_assistant_test.cc
+++ /dev/null
@@ -1,255 +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 "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();
- if (IsHost()) {
- file_path += "/bin/profman";
- } else {
- file_path += "/xbin/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
deleted file mode 100644
index 7c9e449..0000000
--- a/profman/profman.cc
+++ /dev/null
@@ -1,208 +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 <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/scoped_flock.cc b/runtime/base/scoped_flock.cc
index 0e8031f..814cbd0 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()), file->GetPath(), file->CheckUsage(), file->ReadOnlyMode()));
+ file_.reset(new File(dup(file->Fd()), true));
if (file_->Fd() == -1) {
file_.reset();
*error_msg = StringPrintf("Failed to duplicate open file '%s': %s",
@@ -114,13 +114,7 @@
if (file_.get() != nullptr) {
int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN));
CHECK_EQ(0, flock_result);
- int close_result = -1;
- if (file_->ReadOnlyMode()) {
- close_result = file_->Close();
- } else {
- close_result = file_->FlushCloseOrErase();
- }
- if (close_result != 0) {
+ if (file_->FlushCloseOrErase() != 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 4672948..e17bebb 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -35,22 +35,18 @@
namespace unix_file {
-FdFile::FdFile()
- : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true), read_only_mode_(false) {
+FdFile::FdFile() : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true) {
}
FdFile::FdFile(int fd, bool check_usage)
: guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck),
- fd_(fd), auto_close_(true), read_only_mode_(false) {
+ fd_(fd), auto_close_(true) {
}
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), read_only_mode_(read_only_mode) {
+ fd_(fd), file_path_(path), auto_close_(true) {
+ CHECK_NE(0U, path.size());
}
FdFile::~FdFile() {
@@ -103,7 +99,6 @@
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;
@@ -141,7 +136,6 @@
}
int FdFile::Flush() {
- DCHECK(!read_only_mode_);
#ifdef __linux__
int rc = TEMP_FAILURE_RETRY(fdatasync(fd_));
#else
@@ -161,7 +155,6 @@
}
int FdFile::SetLength(int64_t new_length) {
- DCHECK(!read_only_mode_);
#ifdef __linux__
int rc = TEMP_FAILURE_RETRY(ftruncate64(fd_, new_length));
#else
@@ -178,7 +171,6 @@
}
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
@@ -192,14 +184,6 @@
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;
}
@@ -235,7 +219,6 @@
}
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) {
@@ -250,7 +233,6 @@
}
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 ||
@@ -297,14 +279,12 @@
}
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.";
@@ -321,7 +301,6 @@
}
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.";
@@ -338,7 +317,6 @@
}
bool FdFile::ClearContent() {
- DCHECK(!read_only_mode_);
if (SetLength(0) < 0) {
PLOG(art::ERROR) << "Failed to reset the length";
return false;
@@ -347,7 +325,6 @@
}
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 8040afe..1e2d8af 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -37,7 +37,6 @@
// 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
@@ -69,8 +68,6 @@
// Bonus API.
int Fd() const;
- bool ReadOnlyMode() const;
- bool CheckUsage() const;
bool IsOpened() const;
const std::string& GetPath() const {
return file_path_;
@@ -122,7 +119,6 @@
int fd_;
std::string file_path_;
bool auto_close_;
- bool read_only_mode_;
DISALLOW_COPY_AND_ASSIGN(FdFile);
};
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index 747b112..0aff1f7 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(int fd) {
- DCHECK_GE(fd, 0);
+bool ProfileCompilationInfo::Save(uint32_t fd) {
+ DCHECK_GE(fd, 0u);
// 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(int fd) {
- DCHECK_GE(fd, 0);
+bool ProfileCompilationInfo::Load(uint32_t fd) {
+ DCHECK_GE(fd, 0u);
std::string current_line;
const int kBufferSize = 1024;
@@ -343,7 +343,7 @@
return os.str();
}
-bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) {
+bool ProfileCompilationInfo::Equals(ProfileCompilationInfo& other) {
return info_.Equals(other.info_);
}
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h
index edc591c..c388c4a 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(int fd);
+ bool Load(uint32_t fd);
// Loads the data from another ProfileCompilationInfo object.
bool Load(const ProfileCompilationInfo& info);
// Saves the profile data to the given file descriptor.
- bool Save(int fd);
+ bool Save(uint32_t fd);
// Returns the number of methods that were profiled.
uint32_t GetNumberOfMethods() const;
@@ -65,7 +65,8 @@
bool print_full_dex_location = true) const;
// For testing purposes.
- bool Equals(const ProfileCompilationInfo& other);
+ bool Equals(ProfileCompilationInfo& other);
+ // Exposed for testing purpose.
static std::string GetProfileDexFileKey(const std::string& dex_location);
private:
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 13564a6..07f94c0 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1392,8 +1392,9 @@
return filename;
}
-int ExecAndReturnCode(std::vector<std::string>& arg_vector, std::string* error_msg) {
+bool Exec(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.
@@ -1416,6 +1417,7 @@
setpgid(0, 0);
execv(program, &args[0]);
+
PLOG(ERROR) << "Failed to execv(" << command_line << ")";
// _exit to avoid atexit handlers in child.
_exit(1);
@@ -1423,32 +1425,23 @@
if (pid == -1) {
*error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
command_line.c_str(), strerror(errno));
- return -1;
+ return false;
}
// wait for subprocess to finish
- int status = -1;
+ int status;
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 -1;
+ return false;
}
- if (WIFEXITED(status)) {
- return WEXITSTATUS(status);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
+ command_line.c_str());
+ return false;
}
- 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 36f9abf..c00db11 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -287,7 +287,6 @@
// 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);
@@ -344,7 +343,7 @@
UsageFn Usage,
bool is_long_option = true) {
std::string option_prefix = option_name + (is_long_option ? "=" : "");
- DCHECK(option.starts_with(option_prefix)) << option << " " << option_prefix;
+ DCHECK(option.starts_with(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)) {