diff options
author | 2016-02-01 12:06:18 +0000 | |
---|---|---|
committer | 2016-02-22 13:55:18 +0000 | |
commit | 6caefd983a800a063b219f1d3ed71b1416cecd70 (patch) | |
tree | 7fef29d5fcdb997950fd1a2524ae853b4dcfac3d | |
parent | c017318a7c5a9142b1fff9f57eb4105b1d397bbd (diff) |
Add profman tool: responsible to process profiles
This pulls out profile parsing from dex2oat into a separate tool.
Some additional refactoring:
- better return codes
- dex2oat now accepts only one profile file
This is the first step towards support secondary dex files and
extracting profiles out of the code_cache directory.
Bug: 26719109
Bug: 26563023
(cherry picked from commit d81c289e58f4ced8ec7674fd377c356669f97227)
Change-Id: I34d408faa318e866e1a23d4d04c369131ee5012b
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | build/Android.gtest.mk | 8 | ||||
-rw-r--r-- | compiler/Android.mk | 3 | ||||
-rw-r--r-- | compiler/profile_assistant.h | 72 | ||||
-rw-r--r-- | dex2oat/dex2oat.cc | 112 | ||||
-rw-r--r-- | profman/Android.mk | 45 | ||||
-rw-r--r-- | profman/profile_assistant.cc (renamed from compiler/profile_assistant.cc) | 120 | ||||
-rw-r--r-- | profman/profile_assistant.h | 72 | ||||
-rw-r--r-- | profman/profile_assistant_test.cc (renamed from compiler/profile_assistant_test.cc) | 204 | ||||
-rw-r--r-- | profman/profman.cc | 208 | ||||
-rw-r--r-- | runtime/base/scoped_flock.cc | 10 | ||||
-rw-r--r-- | runtime/base/unix_file/fd_file.cc | 31 | ||||
-rw-r--r-- | runtime/base/unix_file/fd_file.h | 4 | ||||
-rw-r--r-- | runtime/jit/offline_profiling_info.cc | 10 | ||||
-rw-r--r-- | runtime/jit/offline_profiling_info.h | 7 | ||||
-rw-r--r-- | runtime/utils.cc | 27 | ||||
-rw-r--r-- | runtime/utils.h | 3 |
17 files changed, 584 insertions, 353 deletions
diff --git a/Android.mk b/Android.mk index 2e05d33209..e762814385 100644 --- a/Android.mk +++ b/Android.mk @@ -86,6 +86,7 @@ include $(art_path)/disassembler/Android.mk include $(art_path)/oatdump/Android.mk include $(art_path)/imgdiag/Android.mk include $(art_path)/patchoat/Android.mk +include $(art_path)/profman/Android.mk include $(art_path)/dalvikvm/Android.mk include $(art_path)/tools/Android.mk include $(art_path)/tools/ahat/Android.mk diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 704d69a37a..22c0e8d7d0 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -144,6 +144,12 @@ ART_GTEST_oatdump_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_default_no-pic_32) \ oatdump +# Profile assistant tests requires profman utility. +ART_GTEST_profile_assistant_test_HOST_DEPS := \ + $(HOST_OUT_EXECUTABLES)/profmand +ART_GTEST_profile_assistant_test_TARGET_DEPS := \ + profman + # The path for which all the source files are relative, not actually the current directory. LOCAL_PATH := art @@ -153,6 +159,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \ dexlist/dexlist_test.cc \ imgdiag/imgdiag_test.cc \ oatdump/oatdump_test.cc \ + profman/profile_assistant_test.cc \ runtime/arch/arch_test.cc \ runtime/arch/instruction_set_test.cc \ runtime/arch/instruction_set_features_test.cc \ @@ -270,7 +277,6 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ compiler/optimizing/ssa_test.cc \ compiler/optimizing/stack_map_test.cc \ compiler/optimizing/suspend_check_test.cc \ - compiler/profile_assistant_test.cc \ compiler/utils/arena_allocator_test.cc \ compiler/utils/dedupe_set_test.cc \ compiler/utils/swap_space_test.cc \ diff --git a/compiler/Android.mk b/compiler/Android.mk index b16494248f..3dfb4b1fd4 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -108,8 +108,7 @@ LIBART_COMPILER_SRC_FILES := \ elf_writer.cc \ elf_writer_quick.cc \ image_writer.cc \ - oat_writer.cc \ - profile_assistant.cc + oat_writer.cc LIBART_COMPILER_SRC_FILES_arm := \ dex/quick/arm/assemble_arm.cc \ diff --git a/compiler/profile_assistant.h b/compiler/profile_assistant.h deleted file mode 100644 index ad5e2163cf..0000000000 --- a/compiler/profile_assistant.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_PROFILE_ASSISTANT_H_ -#define ART_COMPILER_PROFILE_ASSISTANT_H_ - -#include <string> -#include <vector> - -#include "base/scoped_flock.h" -#include "jit/offline_profiling_info.cc" - -namespace art { - -class ProfileAssistant { - public: - // Process the profile information present in the given files. Returns true - // if the analysis ended up successfully (i.e. no errors during reading, - // merging or writing of profile files). - // - // If the returned value is true and there is a significant difference between - // profile_files and reference_profile_files: - // - profile_compilation_info is set to a not null object that - // can be used to drive compilation. It will be the merge of all the data - // found in profile_files and reference_profile_files. - // - the data from profile_files[i] is merged into - // reference_profile_files[i] and the corresponding backing file is - // updated. - // - // If the returned value is false or the difference is insignificant, - // profile_compilation_info will be set to null. - // - // Additional notes: - // - as mentioned above, this function may update the content of the files - // passed with the reference_profile_files. - // - if reference_profile_files is not empty it must be the same size as - // profile_files. - static bool ProcessProfiles( - const std::vector<std::string>& profile_files, - const std::vector<std::string>& reference_profile_files, - /*out*/ ProfileCompilationInfo** profile_compilation_info); - - static bool ProcessProfiles( - const std::vector<uint32_t>& profile_files_fd_, - const std::vector<uint32_t>& reference_profile_files_fd_, - /*out*/ ProfileCompilationInfo** profile_compilation_info); - - private: - static bool ProcessProfilesInternal( - const std::vector<ScopedFlock>& profile_files, - const std::vector<ScopedFlock>& reference_profile_files, - /*out*/ ProfileCompilationInfo** profile_compilation_info); - - DISALLOW_COPY_AND_ASSIGN(ProfileAssistant); -}; - -} // namespace art - -#endif // ART_COMPILER_PROFILE_ASSISTANT_H_ diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 541fb5a423..f7efdc5bac 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -40,6 +40,7 @@ #include "art_method-inl.h" #include "base/dumpable.h" #include "base/macros.h" +#include "base/scoped_flock.h" #include "base/stl_util.h" #include "base/stringpiece.h" #include "base/time_utils.h" @@ -71,7 +72,6 @@ #include "mirror/object_array-inl.h" #include "oat_writer.h" #include "os.h" -#include "profile_assistant.h" #include "runtime.h" #include "runtime_options.h" #include "ScopedLocalRef.h" @@ -339,23 +339,10 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" Example: --runtime-arg -Xms256m"); UsageError(""); UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation."); - UsageError(" Can be specified multiple time, in which case the data from the different"); - UsageError(" profiles will be aggregated."); - UsageError(""); - UsageError(" --reference-profile-file=<filename>: specify a reference profile file to use when"); - UsageError(" compiling. The data in this file will be compared with the data in the"); - UsageError(" associated --profile-file and the compilation will proceed only if there is"); - UsageError(" a significant difference (--reference-profile-file is paired with"); - UsageError(" --profile-file in the natural order). If the compilation was attempted then"); - UsageError(" --profile-file will be merged into --reference-profile-file. Valid only when"); - UsageError(" specified together with --profile-file."); UsageError(""); UsageError(" --profile-file-fd=<number>: same as --profile-file but accepts a file descriptor."); UsageError(" Cannot be used together with --profile-file."); UsageError(""); - UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but"); - UsageError(" accepts a file descriptor. Cannot be used together with"); - UsageError(" --reference-profile-file."); UsageError(" --print-pass-names: print a list of pass names"); UsageError(""); UsageError(" --disable-passes=<pass-names>: disable one or more passes separated by comma."); @@ -517,14 +504,6 @@ static bool UseSwap(bool is_image, std::vector<const DexFile*>& dex_files) { return dex_files_size >= kMinDexFileCumulativeSizeForSwap; } -static void CloseAllFds(const std::vector<uint32_t>& fds, const char* descriptor) { - for (size_t i = 0; i < fds.size(); i++) { - if (close(fds[i]) < 0) { - PLOG(WARNING) << "Failed to close descriptor for " << descriptor << " at index " << i; - } - } -} - class Dex2Oat FINAL { public: explicit Dex2Oat(TimingLogger* timings) : @@ -567,10 +546,12 @@ class Dex2Oat FINAL { dump_passes_(false), dump_timing_(false), dump_slow_timing_(kIsDebugBuild), - swap_fd_(-1), + swap_fd_(kInvalidFd), app_image_fd_(kInvalidFd), + profile_file_fd_(kInvalidFd), timings_(timings), - force_determinism_(false) {} + force_determinism_(false) + {} ~Dex2Oat() { // Log completion time before deleting the runtime_, because this accesses @@ -824,25 +805,8 @@ class Dex2Oat FINAL { } } - if (!profile_files_.empty() && !profile_files_fd_.empty()) { - Usage("Profile files should not be specified with both --profile-file-fd and --profile-file"); - } - if (!profile_files_.empty()) { - if (!reference_profile_files_.empty() && - (reference_profile_files_.size() != profile_files_.size())) { - Usage("If specified, --reference-profile-file should match the number of --profile-file."); - } - } else if (!reference_profile_files_.empty()) { - Usage("--reference-profile-file should only be supplied with --profile-file"); - } - if (!profile_files_fd_.empty()) { - if (!reference_profile_files_fd_.empty() && - (reference_profile_files_fd_.size() != profile_files_fd_.size())) { - Usage("If specified, --reference-profile-file-fd should match the number", - " of --profile-file-fd."); - } - } else if (!reference_profile_files_fd_.empty()) { - Usage("--reference-profile-file-fd should only be supplied with --profile-file-fd"); + if (!profile_file_.empty() && (profile_file_fd_ != kInvalidFd)) { + Usage("Profile file should not be specified with both --profile-file-fd and --profile-file"); } if (!parser_options->oat_symbols.empty()) { @@ -1153,16 +1117,9 @@ class Dex2Oat FINAL { } else if (option.starts_with("--compiler-backend=")) { ParseCompilerBackend(option, parser_options.get()); } else if (option.starts_with("--profile-file=")) { - profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString()); - } else if (option.starts_with("--reference-profile-file=")) { - reference_profile_files_.push_back( - option.substr(strlen("--reference-profile-file=")).ToString()); + profile_file_ = option.substr(strlen("--profile-file=")).ToString(); } else if (option.starts_with("--profile-file-fd=")) { - ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_); - } else if (option.starts_with("--reference-profile-file-fd=")) { - ParseFdForCollection(option, "--reference_profile-file-fd", &reference_profile_files_fd_); - } else if (option == "--no-profile-file") { - // No profile + ParseUintOption(option, "--profile-file-fd", &profile_file_fd_, Usage); } else if (option == "--host") { is_host_ = true; } else if (option == "--runtime-arg") { @@ -1865,33 +1822,44 @@ class Dex2Oat FINAL { } bool UseProfileGuidedCompilation() const { - return !profile_files_.empty() || !profile_files_fd_.empty(); + return !profile_file_.empty() || (profile_file_fd_ != kInvalidFd); } - bool ProcessProfiles() { + bool LoadProfile() { DCHECK(UseProfileGuidedCompilation()); - ProfileCompilationInfo* info = nullptr; - bool result = false; - if (profile_files_.empty()) { - DCHECK(!profile_files_fd_.empty()); - result = ProfileAssistant::ProcessProfiles( - profile_files_fd_, reference_profile_files_fd_, &info); - CloseAllFds(profile_files_fd_, "profile_files_fd_"); - CloseAllFds(reference_profile_files_fd_, "reference_profile_files_fd_"); + + profile_compilation_info_.reset(new ProfileCompilationInfo()); + ScopedFlock flock; + bool success = false; + std::string error; + if (profile_file_fd_ != -1) { + // The file doesn't need to be flushed so don't check the usage. + // Pass a bogus path so that we can easily attribute any reported error. + File file(profile_file_fd_, "profile", /*check_usage*/ false, /*read_only_mode*/ true); + if (flock.Init(&file, &error)) { + success = profile_compilation_info_->Load(profile_file_fd_); + } } else { - result = ProfileAssistant::ProcessProfiles( - profile_files_, reference_profile_files_, &info); + if (flock.Init(profile_file_.c_str(), O_RDONLY, /* block */ true, &error)) { + success = profile_compilation_info_->Load(flock.GetFile()->Fd()); + } + } + if (!error.empty()) { + LOG(WARNING) << "Cannot lock profiles: " << error; } - profile_compilation_info_.reset(info); + if (!success) { + profile_compilation_info_.reset(nullptr); + } - return result; + return success; } bool ShouldCompileBasedOnProfiles() const { DCHECK(UseProfileGuidedCompilation()); - // If we are given profiles, compile only if we have new information. - return profile_compilation_info_ != nullptr; + // If we are given a profile, compile only if we have some data in it. + return (profile_compilation_info_ != nullptr) && + (profile_compilation_info_->GetNumberOfMethods() != 0); } private: @@ -2460,10 +2428,8 @@ class Dex2Oat FINAL { int swap_fd_; std::string app_image_file_name_; int app_image_fd_; - std::vector<std::string> profile_files_; - std::vector<std::string> reference_profile_files_; - std::vector<uint32_t> profile_files_fd_; - std::vector<uint32_t> reference_profile_files_fd_; + std::string profile_file_; + int profile_file_fd_; std::unique_ptr<ProfileCompilationInfo> profile_compilation_info_; TimingLogger* timings_; std::unique_ptr<CumulativeLogger> compiler_phases_timings_; @@ -2592,7 +2558,7 @@ static int dex2oat(int argc, char** argv) { // Process profile information and assess if we need to do a profile guided compilation. // This operation involves I/O. if (dex2oat.UseProfileGuidedCompilation()) { - if (dex2oat.ProcessProfiles()) { + if (dex2oat.LoadProfile()) { if (!dex2oat.ShouldCompileBasedOnProfiles()) { LOG(INFO) << "Skipped compilation because of insignificant profile delta"; return EXIT_SUCCESS; diff --git a/profman/Android.mk b/profman/Android.mk new file mode 100644 index 0000000000..d38d107d21 --- /dev/null +++ b/profman/Android.mk @@ -0,0 +1,45 @@ +# +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +include art/build/Android.executable.mk + +PROFMAN_SRC_FILES := \ + profman.cc \ + profile_assistant.cc + +# TODO: Remove this when the framework (installd) supports pushing the +# right instruction-set parameter for the primary architecture. +ifneq ($(filter ro.zygote=zygote64,$(PRODUCT_DEFAULT_PROPERTY_OVERRIDES)),) + profman_arch := 64 +else + profman_arch := 32 +endif + +ifeq ($(ART_BUILD_TARGET_NDEBUG),true) + $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,target,ndebug,$(profman_arch))) +endif +ifeq ($(ART_BUILD_TARGET_DEBUG),true) + $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,target,debug,$(profman_arch))) +endif + +ifeq ($(ART_BUILD_HOST_NDEBUG),true) + $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,host,ndebug)) +endif +ifeq ($(ART_BUILD_HOST_DEBUG),true) + $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,host,debug)) +endif diff --git a/compiler/profile_assistant.cc b/profman/profile_assistant.cc index 85335efcc4..58e8a3a7d1 100644 --- a/compiler/profile_assistant.cc +++ b/profman/profile_assistant.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,13 +24,10 @@ namespace art { // Minimum number of new methods that profiles must contain to enable recompilation. static constexpr const uint32_t kMinNewMethodsForCompilation = 10; -bool ProfileAssistant::ProcessProfilesInternal( +ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfilesInternal( const std::vector<ScopedFlock>& profile_files, - const std::vector<ScopedFlock>& reference_profile_files, - /*out*/ ProfileCompilationInfo** profile_compilation_info) { + const ScopedFlock& reference_profile_file) { DCHECK(!profile_files.empty()); - DCHECK(!reference_profile_files.empty() || - (profile_files.size() == reference_profile_files.size())); std::vector<ProfileCompilationInfo> new_info(profile_files.size()); bool should_compile = false; @@ -38,51 +35,53 @@ bool ProfileAssistant::ProcessProfilesInternal( for (size_t i = 0; i < new_info.size(); i++) { if (!new_info[i].Load(profile_files[i].GetFile()->Fd())) { LOG(WARNING) << "Could not load profile file at index " << i; - return false; + return kErrorBadProfiles; } // Do we have enough new profiled methods that will make the compilation worthwhile? should_compile |= (new_info[i].GetNumberOfMethods() > kMinNewMethodsForCompilation); } if (!should_compile) { - return true; + return kSkipCompilation; } - std::unique_ptr<ProfileCompilationInfo> result(new ProfileCompilationInfo()); // Merge information. + ProfileCompilationInfo info; + if (!info.Load(reference_profile_file.GetFile()->Fd())) { + LOG(WARNING) << "Could not load reference profile file"; + return kErrorBadProfiles; + } + for (size_t i = 0; i < new_info.size(); i++) { - if (!reference_profile_files.empty()) { - if (!new_info[i].Load(reference_profile_files[i].GetFile()->Fd())) { - LOG(WARNING) << "Could not load reference profile file at index " << i; - return false; - } - } // Merge all data into a single object. - if (!result->Load(new_info[i])) { + if (!info.Load(new_info[i])) { LOG(WARNING) << "Could not merge profile data at index " << i; - return false; + return kErrorBadProfiles; } } - // We were successful in merging all profile information. Update the files. - for (size_t i = 0; i < new_info.size(); i++) { - if (!reference_profile_files.empty()) { - if (!reference_profile_files[i].GetFile()->ClearContent()) { - PLOG(WARNING) << "Could not clear reference profile file at index " << i; - return false; - } - if (!new_info[i].Save(reference_profile_files[i].GetFile()->Fd())) { - LOG(WARNING) << "Could not save reference profile file at index " << i; - return false; - } - if (!profile_files[i].GetFile()->ClearContent()) { - PLOG(WARNING) << "Could not clear profile file at index " << i; - return false; - } - } + // We were successful in merging all profile information. Update the reference profile. + if (!reference_profile_file.GetFile()->ClearContent()) { + PLOG(WARNING) << "Could not clear reference profile file"; + return kErrorIO; } + if (!info.Save(reference_profile_file.GetFile()->Fd())) { + LOG(WARNING) << "Could not save reference profile file"; + return kErrorIO; + } + + return kCompile; +} - *profile_compilation_info = result.release(); - return true; +static bool InitFlock(const std::string& filename, ScopedFlock& flock, std::string* error) { + return flock.Init(filename.c_str(), O_RDWR, /* block */ true, error); +} + +static bool InitFlock(int fd, ScopedFlock& flock, std::string* error) { + DCHECK_GE(fd, 0); + // We do not own the descriptor, so disable auto-close and don't check usage. + File file(fd, false); + file.DisableAutoClose(); + return flock.Init(&file, error); } class ScopedCollectionFlock { @@ -92,7 +91,7 @@ class ScopedCollectionFlock { // Will block until all the locks are acquired. bool Init(const std::vector<std::string>& filenames, /* out */ std::string* error) { for (size_t i = 0; i < filenames.size(); i++) { - if (!flocks_[i].Init(filenames[i].c_str(), O_RDWR, /* block */ true, error)) { + if (!InitFlock(filenames[i], flocks_[i], error)) { *error += " (index=" + std::to_string(i) + ")"; return false; } @@ -101,12 +100,10 @@ class ScopedCollectionFlock { } // Will block until all the locks are acquired. - bool Init(const std::vector<uint32_t>& fds, /* out */ std::string* error) { + bool Init(const std::vector<int>& fds, /* out */ std::string* error) { for (size_t i = 0; i < fds.size(); i++) { - // We do not own the descriptor, so disable auto-close and don't check usage. - File file(fds[i], false); - file.DisableAutoClose(); - if (!flocks_[i].Init(&file, error)) { + DCHECK_GE(fds[i], 0); + if (!InitFlock(fds[i], flocks_[i], error)) { *error += " (index=" + std::to_string(i) + ")"; return false; } @@ -120,50 +117,43 @@ class ScopedCollectionFlock { std::vector<ScopedFlock> flocks_; }; -bool ProfileAssistant::ProcessProfiles( - const std::vector<uint32_t>& profile_files_fd, - const std::vector<uint32_t>& reference_profile_files_fd, - /*out*/ ProfileCompilationInfo** profile_compilation_info) { - *profile_compilation_info = nullptr; - +ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfiles( + const std::vector<int>& profile_files_fd, + int reference_profile_file_fd) { + DCHECK_GE(reference_profile_file_fd, 0); std::string error; ScopedCollectionFlock profile_files_flocks(profile_files_fd.size()); if (!profile_files_flocks.Init(profile_files_fd, &error)) { LOG(WARNING) << "Could not lock profile files: " << error; - return false; + return kErrorCannotLock; } - ScopedCollectionFlock reference_profile_files_flocks(reference_profile_files_fd.size()); - if (!reference_profile_files_flocks.Init(reference_profile_files_fd, &error)) { - LOG(WARNING) << "Could not lock reference profile files: " << error; - return false; + ScopedFlock reference_profile_file_flock; + if (!InitFlock(reference_profile_file_fd, reference_profile_file_flock, &error)) { + LOG(WARNING) << "Could not lock reference profiled files: " << error; + return kErrorCannotLock; } return ProcessProfilesInternal(profile_files_flocks.Get(), - reference_profile_files_flocks.Get(), - profile_compilation_info); + reference_profile_file_flock); } -bool ProfileAssistant::ProcessProfiles( +ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfiles( const std::vector<std::string>& profile_files, - const std::vector<std::string>& reference_profile_files, - /*out*/ ProfileCompilationInfo** profile_compilation_info) { - *profile_compilation_info = nullptr; - + const std::string& reference_profile_file) { std::string error; ScopedCollectionFlock profile_files_flocks(profile_files.size()); if (!profile_files_flocks.Init(profile_files, &error)) { LOG(WARNING) << "Could not lock profile files: " << error; - return false; + return kErrorCannotLock; } - ScopedCollectionFlock reference_profile_files_flocks(reference_profile_files.size()); - if (!reference_profile_files_flocks.Init(reference_profile_files, &error)) { + ScopedFlock reference_profile_file_flock; + if (!InitFlock(reference_profile_file, reference_profile_file_flock, &error)) { LOG(WARNING) << "Could not lock reference profile files: " << error; - return false; + return kErrorCannotLock; } return ProcessProfilesInternal(profile_files_flocks.Get(), - reference_profile_files_flocks.Get(), - profile_compilation_info); + reference_profile_file_flock); } } // namespace art diff --git a/profman/profile_assistant.h b/profman/profile_assistant.h new file mode 100644 index 0000000000..d3c75b817a --- /dev/null +++ b/profman/profile_assistant.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_PROFMAN_PROFILE_ASSISTANT_H_ +#define ART_PROFMAN_PROFILE_ASSISTANT_H_ + +#include <string> +#include <vector> + +#include "base/scoped_flock.h" +#include "jit/offline_profiling_info.h" + +namespace art { + +class ProfileAssistant { + public: + // These also serve as return codes of profman and are processed by installd + // (frameworks/native/cmds/installd/commands.cpp) + enum ProcessingResult { + kCompile = 0, + kSkipCompilation = 1, + kErrorBadProfiles = 2, + kErrorIO = 3, + kErrorCannotLock = 4 + }; + + // Process the profile information present in the given files. Returns one of + // ProcessingResult values depending on profile information and whether or not + // the analysis ended up successfully (i.e. no errors during reading, + // merging or writing of profile files). + // + // When the returned value is kCompile there is a significant difference + // between profile_files and reference_profile_files. In this case + // reference_profile will be updated with the profiling info obtain after + // merging all profiles. + // + // When the returned value is kSkipCompilation, the difference between the + // merge of the current profiles and the reference one is insignificant. In + // this case no file will be updated. + // + static ProcessingResult ProcessProfiles( + const std::vector<std::string>& profile_files, + const std::string& reference_profile_file); + + static ProcessingResult ProcessProfiles( + const std::vector<int>& profile_files_fd_, + int reference_profile_file_fd); + + private: + static ProcessingResult ProcessProfilesInternal( + const std::vector<ScopedFlock>& profile_files, + const ScopedFlock& reference_profile_file); + + DISALLOW_COPY_AND_ASSIGN(ProfileAssistant); +}; + +} // namespace art + +#endif // ART_PROFMAN_PROFILE_ASSISTANT_H_ diff --git a/compiler/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 58b7513377..543be5d181 100644 --- a/compiler/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -18,8 +18,9 @@ #include "base/unix_file/fd_file.h" #include "common_runtime_test.h" -#include "compiler/profile_assistant.h" +#include "profile_assistant.h" #include "jit/offline_profiling_info.h" +#include "utils.h" namespace art { @@ -44,23 +45,51 @@ class ProfileAssistantTest : public CommonRuntimeTest { ASSERT_TRUE(profile.GetFile()->ResetOffset()); } - uint32_t GetFd(const ScratchFile& file) const { - return static_cast<uint32_t>(file.GetFd()); + int GetFd(const ScratchFile& file) const { + return static_cast<int>(file.GetFd()); + } + + void CheckProfileInfo(ScratchFile& file, const ProfileCompilationInfo& info) { + ProfileCompilationInfo file_info; + ASSERT_TRUE(file.GetFile()->ResetOffset()); + ASSERT_TRUE(file_info.Load(GetFd(file))); + ASSERT_TRUE(file_info.Equals(info)); + } + + // Runs test with given arguments. + int ProcessProfiles(const std::vector<int>& profiles_fd, int reference_profile_fd) { + std::string file_path = GetTestAndroidRoot(); + 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_profile1; - ScratchFile reference_profile2; + ScratchFile reference_profile; - std::vector<uint32_t> profile_fds({ + std::vector<int> profile_fds({ GetFd(profile1), GetFd(profile2)}); - std::vector<uint32_t> reference_profile_fds({ - GetFd(reference_profile1), - GetFd(reference_profile2)}); + int reference_profile_fd = GetFd(reference_profile); const uint16_t kNumberOfMethodsToEnableCompilation = 100; ProfileCompilationInfo info1; @@ -69,44 +98,32 @@ TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) { SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2); // We should advise compilation. - ProfileCompilationInfo* result; - ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); - ASSERT_TRUE(result != nullptr); - + ASSERT_EQ(ProfileAssistant::kCompile, + ProcessProfiles(profile_fds, reference_profile_fd)); // The resulting compilation info must be equal to the merge of the inputs. + ProfileCompilationInfo result; + ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); + ASSERT_TRUE(result.Load(reference_profile_fd)); + ProfileCompilationInfo expected; ASSERT_TRUE(expected.Load(info1)); ASSERT_TRUE(expected.Load(info2)); - ASSERT_TRUE(expected.Equals(*result)); + ASSERT_TRUE(expected.Equals(result)); - // The information from profiles must be transfered to the reference profiles. - ProfileCompilationInfo file_info1; - ASSERT_TRUE(reference_profile1.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info1.Load(GetFd(reference_profile1))); - ASSERT_TRUE(file_info1.Equals(info1)); - - ProfileCompilationInfo file_info2; - ASSERT_TRUE(reference_profile2.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info2.Load(GetFd(reference_profile2))); - ASSERT_TRUE(file_info2.Equals(info2)); - - // Initial profiles must be cleared. - ASSERT_EQ(0, profile1.GetFile()->GetLength()); - ASSERT_EQ(0, profile2.GetFile()->GetLength()); + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); + CheckProfileInfo(profile2, info2); } TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) { ScratchFile profile1; ScratchFile profile2; - ScratchFile reference_profile1; - ScratchFile reference_profile2; + ScratchFile reference_profile; - std::vector<uint32_t> profile_fds({ + std::vector<int> profile_fds({ GetFd(profile1), GetFd(profile2)}); - std::vector<uint32_t> reference_profile_fds({ - GetFd(reference_profile1), - GetFd(reference_profile2)}); + int reference_profile_fd = GetFd(reference_profile); // The new profile info will contain the methods with indices 0-100. const uint16_t kNumberOfMethodsToEnableCompilation = 100; @@ -118,60 +135,39 @@ TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) { // The reference profile info will contain the methods with indices 50-150. const uint16_t kNumberOfMethodsAlreadyCompiled = 100; - ProfileCompilationInfo reference_info1; - SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, reference_profile1, - &reference_info1, kNumberOfMethodsToEnableCompilation / 2); - ProfileCompilationInfo reference_info2; - SetupProfile("p2", 2, kNumberOfMethodsAlreadyCompiled, reference_profile2, - &reference_info2, kNumberOfMethodsToEnableCompilation / 2); + ProfileCompilationInfo reference_info; + SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, reference_profile, + &reference_info, kNumberOfMethodsToEnableCompilation / 2); // We should advise compilation. - ProfileCompilationInfo* result; - ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); - ASSERT_TRUE(result != nullptr); + ASSERT_EQ(ProfileAssistant::kCompile, + ProcessProfiles(profile_fds, reference_profile_fd)); // The resulting compilation info must be equal to the merge of the inputs + ProfileCompilationInfo result; + ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); + ASSERT_TRUE(result.Load(reference_profile_fd)); + ProfileCompilationInfo expected; ASSERT_TRUE(expected.Load(info1)); ASSERT_TRUE(expected.Load(info2)); - ASSERT_TRUE(expected.Load(reference_info1)); - ASSERT_TRUE(expected.Load(reference_info2)); - ASSERT_TRUE(expected.Equals(*result)); - - // The information from profiles must be transfered to the reference profiles. - ProfileCompilationInfo file_info1; - ProfileCompilationInfo merge1; - ASSERT_TRUE(merge1.Load(info1)); - ASSERT_TRUE(merge1.Load(reference_info1)); - ASSERT_TRUE(reference_profile1.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info1.Load(GetFd(reference_profile1))); - ASSERT_TRUE(file_info1.Equals(merge1)); + ASSERT_TRUE(expected.Load(reference_info)); + ASSERT_TRUE(expected.Equals(result)); - ProfileCompilationInfo file_info2; - ProfileCompilationInfo merge2; - ASSERT_TRUE(merge2.Load(info2)); - ASSERT_TRUE(merge2.Load(reference_info2)); - ASSERT_TRUE(reference_profile2.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info2.Load(GetFd(reference_profile2))); - ASSERT_TRUE(file_info2.Equals(merge2)); - - // Initial profiles must be cleared. - ASSERT_EQ(0, profile1.GetFile()->GetLength()); - ASSERT_EQ(0, profile2.GetFile()->GetLength()); + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); + CheckProfileInfo(profile2, info2); } TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) { ScratchFile profile1; ScratchFile profile2; - ScratchFile reference_profile1; - ScratchFile reference_profile2; + ScratchFile reference_profile; - std::vector<uint32_t> profile_fds({ + std::vector<int> profile_fds({ GetFd(profile1), GetFd(profile2)}); - std::vector<uint32_t> reference_profile_fds({ - GetFd(reference_profile1), - GetFd(reference_profile2)}); + int reference_profile_fd = GetFd(reference_profile); const uint16_t kNumberOfMethodsToSkipCompilation = 1; ProfileCompilationInfo info1; @@ -180,9 +176,8 @@ TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) { SetupProfile("p2", 2, kNumberOfMethodsToSkipCompilation, profile2, &info2); // We should not advise compilation. - ProfileCompilationInfo* result = nullptr; - ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); - ASSERT_TRUE(result == nullptr); + ASSERT_EQ(ProfileAssistant::kSkipCompilation, + ProcessProfiles(profile_fds, reference_profile_fd)); // The information from profiles must remain the same. ProfileCompilationInfo file_info1; @@ -196,22 +191,22 @@ TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) { ASSERT_TRUE(file_info2.Equals(info2)); // Reference profile files must remain empty. - ASSERT_EQ(0, reference_profile1.GetFile()->GetLength()); - ASSERT_EQ(0, reference_profile2.GetFile()->GetLength()); + ASSERT_EQ(0, reference_profile.GetFile()->GetLength()); + + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); + CheckProfileInfo(profile2, info2); } TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) { ScratchFile profile1; ScratchFile profile2; - ScratchFile reference_profile1; - ScratchFile reference_profile2; + ScratchFile reference_profile; - std::vector<uint32_t> profile_fds({ + std::vector<int> profile_fds({ GetFd(profile1), GetFd(profile2)}); - std::vector<uint32_t> reference_profile_fds({ - GetFd(reference_profile1), - GetFd(reference_profile2)}); + int reference_profile_fd = GetFd(reference_profile); const uint16_t kNumberOfMethodsToEnableCompilation = 100; // Assign different hashes for the same dex file. This will make merging of information to fail. @@ -221,34 +216,24 @@ TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) { SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2); // We should fail processing. - ProfileCompilationInfo* result = nullptr; - ASSERT_FALSE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); - ASSERT_TRUE(result == nullptr); + ASSERT_EQ(ProfileAssistant::kErrorBadProfiles, + ProcessProfiles(profile_fds, reference_profile_fd)); - // The information from profiles must still remain the same. - ProfileCompilationInfo file_info1; - ASSERT_TRUE(profile1.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info1.Load(GetFd(profile1))); - ASSERT_TRUE(file_info1.Equals(info1)); - - ProfileCompilationInfo file_info2; - ASSERT_TRUE(profile2.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info2.Load(GetFd(profile2))); - ASSERT_TRUE(file_info2.Equals(info2)); + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); + CheckProfileInfo(profile2, info2); // Reference profile files must still remain empty. - ASSERT_EQ(0, reference_profile1.GetFile()->GetLength()); - ASSERT_EQ(0, reference_profile2.GetFile()->GetLength()); + ASSERT_EQ(0, reference_profile.GetFile()->GetLength()); } TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) { ScratchFile profile1; ScratchFile reference_profile; - std::vector<uint32_t> profile_fds({ + std::vector<int> profile_fds({ GetFd(profile1)}); - std::vector<uint32_t> reference_profile_fds({ - GetFd(reference_profile)}); + int reference_profile_fd = GetFd(reference_profile); const uint16_t kNumberOfMethodsToEnableCompilation = 100; // Assign different hashes for the same dex file. This will make merging of information to fail. @@ -258,22 +243,13 @@ TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) { SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, reference_profile, &reference_info); // We should not advise compilation. - ProfileCompilationInfo* result = nullptr; ASSERT_TRUE(profile1.GetFile()->ResetOffset()); ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); - ASSERT_FALSE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); - ASSERT_TRUE(result == nullptr); - - // The information from profiles must still remain the same. - ProfileCompilationInfo file_info1; - ASSERT_TRUE(profile1.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info1.Load(GetFd(profile1))); - ASSERT_TRUE(file_info1.Equals(info1)); + ASSERT_EQ(ProfileAssistant::kErrorBadProfiles, + ProcessProfiles(profile_fds, reference_profile_fd)); - ProfileCompilationInfo file_info2; - ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info2.Load(GetFd(reference_profile))); - ASSERT_TRUE(file_info2.Equals(reference_info)); + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); } } // namespace art diff --git a/profman/profman.cc b/profman/profman.cc new file mode 100644 index 0000000000..7c9e449ed5 --- /dev/null +++ b/profman/profman.cc @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <string> +#include <vector> + +#include "base/dumpable.h" +#include "base/scoped_flock.h" +#include "base/stringpiece.h" +#include "base/stringprintf.h" +#include "base/time_utils.h" +#include "base/unix_file/fd_file.h" +#include "jit/offline_profiling_info.h" +#include "utils.h" +#include "profile_assistant.h" + +namespace art { + +static int original_argc; +static char** original_argv; + +static std::string CommandLine() { + std::vector<std::string> command; + for (int i = 0; i < original_argc; ++i) { + command.push_back(original_argv[i]); + } + return Join(command, ' '); +} + +static void UsageErrorV(const char* fmt, va_list ap) { + std::string error; + StringAppendV(&error, fmt, ap); + LOG(ERROR) << error; +} + +static void UsageError(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + UsageErrorV(fmt, ap); + va_end(ap); +} + +NO_RETURN static void Usage(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + UsageErrorV(fmt, ap); + va_end(ap); + + UsageError("Command: %s", CommandLine().c_str()); + UsageError("Usage: profman [options]..."); + UsageError(""); + UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation."); + UsageError(" Can be specified multiple time, in which case the data from the different"); + UsageError(" profiles will be aggregated."); + UsageError(""); + UsageError(" --profile-file-fd=<number>: same as --profile-file but accepts a file descriptor."); + UsageError(" Cannot be used together with --profile-file."); + UsageError(""); + UsageError(" --reference-profile-file=<filename>: specify a reference profile."); + UsageError(" The data in this file will be compared with the data obtained by merging"); + UsageError(" all the files specified with --profile-file or --profile-file-fd."); + UsageError(" If the exit code is EXIT_COMPILE then all --profile-file will be merged into"); + UsageError(" --reference-profile-file. "); + UsageError(""); + UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but"); + UsageError(" accepts a file descriptor. Cannot be used together with"); + UsageError(" --reference-profile-file."); + UsageError(""); + + exit(EXIT_FAILURE); +} + +class ProfMan FINAL { + public: + ProfMan() : + reference_profile_file_fd_(-1), + start_ns_(NanoTime()) {} + + ~ProfMan() { + LogCompletionTime(); + } + + void ParseArgs(int argc, char **argv) { + original_argc = argc; + original_argv = argv; + + InitLogging(argv); + + // Skip over the command name. + argv++; + argc--; + + if (argc == 0) { + Usage("No arguments specified"); + } + + for (int i = 0; i < argc; ++i) { + const StringPiece option(argv[i]); + const bool log_options = false; + if (log_options) { + LOG(INFO) << "patchoat: option[" << i << "]=" << argv[i]; + } + if (option.starts_with("--profile-file=")) { + profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString()); + } else if (option.starts_with("--profile-file-fd=")) { + ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_); + } else if (option.starts_with("--reference-profile-file=")) { + reference_profile_file_ = option.substr(strlen("--reference-profile-file=")).ToString(); + } else if (option.starts_with("--reference-profile-file-fd=")) { + ParseUintOption(option, "--reference-profile-file-fd", &reference_profile_file_fd_, Usage); + } else { + Usage("Unknown argument %s", option.data()); + } + } + + if (profile_files_.empty() && profile_files_fd_.empty()) { + Usage("No profile files specified."); + } + if (!profile_files_.empty() && !profile_files_fd_.empty()) { + Usage("Profile files should not be specified with both --profile-file-fd and --profile-file"); + } + if (!reference_profile_file_.empty() && (reference_profile_file_fd_ != -1)) { + Usage("--reference-profile-file-fd should only be supplied with --profile-file-fd"); + } + if (reference_profile_file_.empty() && (reference_profile_file_fd_ == -1)) { + Usage("Reference profile file not specified"); + } + } + + ProfileAssistant::ProcessingResult ProcessProfiles() { + ProfileAssistant::ProcessingResult result; + if (profile_files_.empty()) { + // The file doesn't need to be flushed here (ProcessProfiles will do it) + // so don't check the usage. + File file(reference_profile_file_fd_, false); + result = ProfileAssistant::ProcessProfiles(profile_files_fd_, reference_profile_file_fd_); + CloseAllFds(profile_files_fd_, "profile_files_fd_"); + } else { + result = ProfileAssistant::ProcessProfiles(profile_files_, reference_profile_file_); + } + return result; + } + + private: + static void ParseFdForCollection(const StringPiece& option, + const char* arg_name, + std::vector<int>* fds) { + int fd; + ParseUintOption(option, arg_name, &fd, Usage); + fds->push_back(fd); + } + + static void CloseAllFds(const std::vector<int>& fds, const char* descriptor) { + for (size_t i = 0; i < fds.size(); i++) { + if (close(fds[i]) < 0) { + PLOG(WARNING) << "Failed to close descriptor for " << descriptor << " at index " << i; + } + } + } + + void LogCompletionTime() { + LOG(INFO) << "profman took " << PrettyDuration(NanoTime() - start_ns_); + } + + std::vector<std::string> profile_files_; + std::vector<int> profile_files_fd_; + std::string reference_profile_file_; + int reference_profile_file_fd_; + uint64_t start_ns_; +}; + +// See ProfileAssistant::ProcessingResult for return codes. +static int profman(int argc, char** argv) { + ProfMan profman; + + // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. + profman.ParseArgs(argc, argv); + + // Process profile information and assess if we need to do a profile guided compilation. + // This operation involves I/O. + return profman.ProcessProfiles(); +} + +} // namespace art + +int main(int argc, char **argv) { + return art::profman(argc, argv); +} + diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc index 814cbd093a..0e8031f4f2 100644 --- a/runtime/base/scoped_flock.cc +++ b/runtime/base/scoped_flock.cc @@ -83,7 +83,7 @@ bool ScopedFlock::Init(const char* filename, int flags, bool block, std::string* } bool ScopedFlock::Init(File* file, std::string* error_msg) { - file_.reset(new File(dup(file->Fd()), true)); + file_.reset(new File(dup(file->Fd()), file->GetPath(), file->CheckUsage(), file->ReadOnlyMode())); if (file_->Fd() == -1) { file_.reset(); *error_msg = StringPrintf("Failed to duplicate open file '%s': %s", @@ -114,7 +114,13 @@ ScopedFlock::~ScopedFlock() { if (file_.get() != nullptr) { int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN)); CHECK_EQ(0, flock_result); - if (file_->FlushCloseOrErase() != 0) { + int close_result = -1; + if (file_->ReadOnlyMode()) { + close_result = file_->Close(); + } else { + close_result = file_->FlushCloseOrErase(); + } + if (close_result != 0) { PLOG(WARNING) << "Could not close scoped file lock file."; } } diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index e17bebb4fb..4672948f31 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -35,18 +35,22 @@ namespace unix_file { -FdFile::FdFile() : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true) { +FdFile::FdFile() + : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true), read_only_mode_(false) { } FdFile::FdFile(int fd, bool check_usage) : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck), - fd_(fd), auto_close_(true) { + fd_(fd), auto_close_(true), read_only_mode_(false) { } FdFile::FdFile(int fd, const std::string& path, bool check_usage) + : FdFile(fd, path, check_usage, false) { +} + +FdFile::FdFile(int fd, const std::string& path, bool check_usage, bool read_only_mode) : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck), - fd_(fd), file_path_(path), auto_close_(true) { - CHECK_NE(0U, path.size()); + fd_(fd), file_path_(path), auto_close_(true), read_only_mode_(read_only_mode) { } FdFile::~FdFile() { @@ -99,6 +103,7 @@ bool FdFile::Open(const std::string& path, int flags) { bool FdFile::Open(const std::string& path, int flags, mode_t mode) { CHECK_EQ(fd_, -1) << path; + read_only_mode_ = (flags & O_RDONLY) != 0; fd_ = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)); if (fd_ == -1) { return false; @@ -136,6 +141,7 @@ int FdFile::Close() { } int FdFile::Flush() { + DCHECK(!read_only_mode_); #ifdef __linux__ int rc = TEMP_FAILURE_RETRY(fdatasync(fd_)); #else @@ -155,6 +161,7 @@ int64_t FdFile::Read(char* buf, int64_t byte_count, int64_t offset) const { } int FdFile::SetLength(int64_t new_length) { + DCHECK(!read_only_mode_); #ifdef __linux__ int rc = TEMP_FAILURE_RETRY(ftruncate64(fd_, new_length)); #else @@ -171,6 +178,7 @@ int64_t FdFile::GetLength() const { } int64_t FdFile::Write(const char* buf, int64_t byte_count, int64_t offset) { + DCHECK(!read_only_mode_); #ifdef __linux__ int rc = TEMP_FAILURE_RETRY(pwrite64(fd_, buf, byte_count, offset)); #else @@ -184,6 +192,14 @@ int FdFile::Fd() const { return fd_; } +bool FdFile::ReadOnlyMode() const { + return read_only_mode_; +} + +bool FdFile::CheckUsage() const { + return guard_state_ != GuardState::kNoCheck; +} + bool FdFile::IsOpened() const { return fd_ >= 0; } @@ -219,6 +235,7 @@ bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) { } bool FdFile::WriteFully(const void* buffer, size_t byte_count) { + DCHECK(!read_only_mode_); const char* ptr = static_cast<const char*>(buffer); moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file."); while (byte_count > 0) { @@ -233,6 +250,7 @@ bool FdFile::WriteFully(const void* buffer, size_t byte_count) { } bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) { + DCHECK(!read_only_mode_); off_t off = static_cast<off_t>(offset); off_t sz = static_cast<off_t>(size); if (offset < 0 || static_cast<int64_t>(off) != offset || @@ -279,12 +297,14 @@ bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) { } void FdFile::Erase() { + DCHECK(!read_only_mode_); TEMP_FAILURE_RETRY(SetLength(0)); TEMP_FAILURE_RETRY(Flush()); TEMP_FAILURE_RETRY(Close()); } int FdFile::FlushCloseOrErase() { + DCHECK(!read_only_mode_); int flush_result = TEMP_FAILURE_RETRY(Flush()); if (flush_result != 0) { LOG(::art::ERROR) << "CloseOrErase failed while flushing a file."; @@ -301,6 +321,7 @@ int FdFile::FlushCloseOrErase() { } int FdFile::FlushClose() { + DCHECK(!read_only_mode_); int flush_result = TEMP_FAILURE_RETRY(Flush()); if (flush_result != 0) { LOG(::art::ERROR) << "FlushClose failed while flushing a file."; @@ -317,6 +338,7 @@ void FdFile::MarkUnchecked() { } bool FdFile::ClearContent() { + DCHECK(!read_only_mode_); if (SetLength(0) < 0) { PLOG(art::ERROR) << "Failed to reset the length"; return false; @@ -325,6 +347,7 @@ bool FdFile::ClearContent() { } bool FdFile::ResetOffset() { + DCHECK(!read_only_mode_); off_t rc = TEMP_FAILURE_RETRY(lseek(fd_, 0, SEEK_SET)); if (rc == static_cast<off_t>(-1)) { PLOG(art::ERROR) << "Failed to reset the offset"; diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h index 1e2d8af151..8040afe9b7 100644 --- a/runtime/base/unix_file/fd_file.h +++ b/runtime/base/unix_file/fd_file.h @@ -37,6 +37,7 @@ class FdFile : public RandomAccessFile { // file descriptor. (Use DisableAutoClose to retain ownership.) FdFile(int fd, bool checkUsage); FdFile(int fd, const std::string& path, bool checkUsage); + FdFile(int fd, const std::string& path, bool checkUsage, bool read_only_mode); // Destroys an FdFile, closing the file descriptor if Close hasn't already // been called. (If you care about the return value of Close, call it @@ -68,6 +69,8 @@ class FdFile : public RandomAccessFile { // Bonus API. int Fd() const; + bool ReadOnlyMode() const; + bool CheckUsage() const; bool IsOpened() const; const std::string& GetPath() const { return file_path_; @@ -119,6 +122,7 @@ class FdFile : public RandomAccessFile { int fd_; std::string file_path_; bool auto_close_; + bool read_only_mode_; DISALLOW_COPY_AND_ASSIGN(FdFile); }; diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc index 0aff1f7ec3..747b112f57 100644 --- a/runtime/jit/offline_profiling_info.cc +++ b/runtime/jit/offline_profiling_info.cc @@ -125,8 +125,8 @@ static constexpr const char kLineSeparator = '\n'; * app.apk,131232145,11,23,454,54 * app.apk:classes5.dex,218490184,39,13,49,1 **/ -bool ProfileCompilationInfo::Save(uint32_t fd) { - DCHECK_GE(fd, 0u); +bool ProfileCompilationInfo::Save(int fd) { + DCHECK_GE(fd, 0); // TODO(calin): Profile this and see how much memory it takes. If too much, // write to file directly. std::ostringstream os; @@ -232,8 +232,8 @@ static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& l return new_line_pos == -1 ? new_line_pos : new_line_pos + 1; } -bool ProfileCompilationInfo::Load(uint32_t fd) { - DCHECK_GE(fd, 0u); +bool ProfileCompilationInfo::Load(int fd) { + DCHECK_GE(fd, 0); std::string current_line; const int kBufferSize = 1024; @@ -343,7 +343,7 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* return os.str(); } -bool ProfileCompilationInfo::Equals(ProfileCompilationInfo& other) { +bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) { return info_.Equals(other.info_); } diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h index c388c4a42f..edc591c2eb 100644 --- a/runtime/jit/offline_profiling_info.h +++ b/runtime/jit/offline_profiling_info.h @@ -46,11 +46,11 @@ class ProfileCompilationInfo { const std::vector<ArtMethod*>& methods); // Loads profile information from the given file descriptor. - bool Load(uint32_t fd); + bool Load(int fd); // Loads the data from another ProfileCompilationInfo object. bool Load(const ProfileCompilationInfo& info); // Saves the profile data to the given file descriptor. - bool Save(uint32_t fd); + bool Save(int fd); // Returns the number of methods that were profiled. uint32_t GetNumberOfMethods() const; @@ -65,8 +65,7 @@ class ProfileCompilationInfo { bool print_full_dex_location = true) const; // For testing purposes. - bool Equals(ProfileCompilationInfo& other); - // Exposed for testing purpose. + bool Equals(const ProfileCompilationInfo& other); static std::string GetProfileDexFileKey(const std::string& dex_location); private: diff --git a/runtime/utils.cc b/runtime/utils.cc index 07f94c0766..13564a6a0f 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1392,9 +1392,8 @@ std::string GetSystemImageFilename(const char* location, const InstructionSet is return filename; } -bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) { +int ExecAndReturnCode(std::vector<std::string>& arg_vector, std::string* error_msg) { const std::string command_line(Join(arg_vector, ' ')); - CHECK_GE(arg_vector.size(), 1U) << command_line; // Convert the args to char pointers. @@ -1417,7 +1416,6 @@ bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) { setpgid(0, 0); execv(program, &args[0]); - PLOG(ERROR) << "Failed to execv(" << command_line << ")"; // _exit to avoid atexit handlers in child. _exit(1); @@ -1425,23 +1423,32 @@ bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) { if (pid == -1) { *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s", command_line.c_str(), strerror(errno)); - return false; + return -1; } // wait for subprocess to finish - int status; + int status = -1; pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); if (got_pid != pid) { *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: " "wanted %d, got %d: %s", command_line.c_str(), pid, got_pid, strerror(errno)); - return false; + return -1; } - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status", - command_line.c_str()); - return false; + if (WIFEXITED(status)) { + return WEXITSTATUS(status); } + return -1; + } +} + +bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) { + int status = ExecAndReturnCode(arg_vector, error_msg); + if (status != 0) { + const std::string command_line(Join(arg_vector, ' ')); + *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status", + command_line.c_str()); + return false; } return true; } diff --git a/runtime/utils.h b/runtime/utils.h index c00db11c16..36f9abfe7b 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -287,6 +287,7 @@ std::string GetSystemImageFilename(const char* location, InstructionSet isa); // Wrapper on fork/execv to run a command in a subprocess. bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg); +int ExecAndReturnCode(std::vector<std::string>& arg_vector, std::string* error_msg); // Returns true if the file exists. bool FileExists(const std::string& filename); @@ -343,7 +344,7 @@ static void ParseUintOption(const StringPiece& option, UsageFn Usage, bool is_long_option = true) { std::string option_prefix = option_name + (is_long_option ? "=" : ""); - DCHECK(option.starts_with(option_prefix)); + DCHECK(option.starts_with(option_prefix)) << option << " " << option_prefix; const char* value_string = option.substr(option_prefix.size()).data(); int64_t parsed_integer_value = 0; if (!ParseInt(value_string, &parsed_integer_value)) { |