Revert "Revert "Add profman tool: responsible to process profiles""
This reverts commit 3da74687e42de7d33a8e75df9bd64374e650f75e.
Change-Id: Id005096bd8063c6c602744d4476d5eb7e0d34e90
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..543be5d
--- /dev/null
+++ b/profman/profile_assistant_test.cc
@@ -0,0 +1,255 @@
+/*
+ * 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
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);
+}
+