diff options
-rw-r--r-- | compiler/Android.mk | 3 | ||||
-rw-r--r-- | compiler/common_compiler_test.cc | 4 | ||||
-rw-r--r-- | compiler/dex/quick/quick_cfi_test.cc | 2 | ||||
-rw-r--r-- | compiler/dex/quick/x86/quick_assemble_x86_test.cc | 2 | ||||
-rw-r--r-- | compiler/driver/compiled_method_storage_test.cc | 2 | ||||
-rw-r--r-- | compiler/driver/compiler_driver.cc | 27 | ||||
-rw-r--r-- | compiler/driver/compiler_driver.h | 10 | ||||
-rw-r--r-- | compiler/jit/jit_compiler.cc | 4 | ||||
-rw-r--r-- | compiler/linker/relative_patcher_test.h | 2 | ||||
-rw-r--r-- | compiler/oat_test.cc | 2 | ||||
-rw-r--r-- | compiler/profile_assistant.cc | 69 | ||||
-rw-r--r-- | compiler/profile_assistant.h | 61 | ||||
-rw-r--r-- | dex2oat/dex2oat.cc | 68 | ||||
-rw-r--r-- | runtime/jit/offline_profiling_info.cc | 196 | ||||
-rw-r--r-- | runtime/jit/offline_profiling_info.h | 68 | ||||
-rw-r--r-- | runtime/jit/profile_saver.cc | 5 | ||||
-rw-r--r-- | runtime/jit/profile_saver.h | 1 |
17 files changed, 345 insertions, 181 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index f0bf4997c6..458973684e 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -108,7 +108,8 @@ LIBART_COMPILER_SRC_FILES := \ elf_writer_debug.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/common_compiler_test.cc b/compiler/common_compiler_test.cc index 278c49017e..b5fd1e074f 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -208,8 +208,8 @@ void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSe false, timer_.get(), -1, - /* profile_file */ "", - /* dex_to_oat_map */ nullptr)); + /* dex_to_oat_map */ nullptr, + /* profile_compilation_info */ nullptr)); // We typically don't generate an image in unit tests, disable this optimization by default. compiler_driver_->SetSupportBootImageFixup(false); } diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc index bcf20c7efa..12568a4ad4 100644 --- a/compiler/dex/quick/quick_cfi_test.cc +++ b/compiler/dex/quick/quick_cfi_test.cc @@ -92,7 +92,7 @@ class QuickCFITest : public CFITest { false, 0, -1, - "", + nullptr, nullptr); ClassLinker* linker = nullptr; CompilationUnit cu(&pool, isa, &driver, linker); diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc index 9deabc02e9..b39fe4da4f 100644 --- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc +++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc @@ -73,7 +73,7 @@ class QuickAssembleX86TestBase : public testing::Test { false, 0, -1, - "", + nullptr, nullptr)); cu_.reset(new CompilationUnit(pool_.get(), isa_, compiler_driver_.get(), nullptr)); DexFile::CodeItem* code_item = static_cast<DexFile::CodeItem*>( diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc index 84fb4324b5..f18fa67ea5 100644 --- a/compiler/driver/compiled_method_storage_test.cc +++ b/compiler/driver/compiled_method_storage_test.cc @@ -45,7 +45,7 @@ TEST(CompiledMethodStorage, Deduplicate) { false, nullptr, -1, - "", + nullptr, nullptr); CompiledMethodStorage* storage = driver.GetCompiledMethodStorage(); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index afb4b71ccf..043bd93bd7 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -347,8 +347,8 @@ CompilerDriver::CompilerDriver( size_t thread_count, bool dump_stats, bool dump_passes, const std::string& dump_cfg_file_name, bool dump_cfg_append, CumulativeLogger* timer, int swap_fd, - const std::string& profile_file, - const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map) + const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map, + const ProfileCompilationInfo* profile_compilation_info) : compiler_options_(compiler_options), verification_results_(verification_results), method_inliner_map_(method_inliner_map), @@ -377,7 +377,8 @@ CompilerDriver::CompilerDriver( support_boot_image_fixup_(instruction_set != kMips && instruction_set != kMips64), dex_files_for_oat_file_(nullptr), dex_file_oat_filename_map_(dex_to_oat_map), - compiled_method_storage_(swap_fd) { + compiled_method_storage_(swap_fd), + profile_compilation_info_(profile_compilation_info) { DCHECK(compiler_options_ != nullptr); DCHECK(verification_results_ != nullptr); DCHECK(method_inliner_map_ != nullptr); @@ -385,12 +386,6 @@ CompilerDriver::CompilerDriver( compiler_->Init(); CHECK_EQ(boot_image_, image_classes_.get() != nullptr); - - // Read the profile file if one is provided. - if (!profile_file.empty()) { - profile_compilation_info_.reset(new ProfileCompilationInfo(profile_file)); - LOG(INFO) << "Using profile data from file " << profile_file; - } } CompilerDriver::~CompilerDriver() { @@ -2306,15 +2301,11 @@ void CompilerDriver::InitializeClasses(jobject class_loader, void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files, ThreadPool* thread_pool, TimingLogger* timings) { - if (profile_compilation_info_ != nullptr) { - if (!profile_compilation_info_->Load(dex_files)) { - LOG(WARNING) << "Failed to load offline profile info from " - << profile_compilation_info_->GetFilename() - << ". No methods will be compiled"; - } else if (kDebugProfileGuidedCompilation) { - LOG(INFO) << "[ProfileGuidedCompilation] " - << profile_compilation_info_->DumpInfo(); - } + if (kDebugProfileGuidedCompilation) { + LOG(INFO) << "[ProfileGuidedCompilation] " << + ((profile_compilation_info_ == nullptr) + ? "null" + : profile_compilation_info_->DumpInfo(&dex_files)); } for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index fa0cb9a412..3847c8183e 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -97,8 +97,8 @@ class CompilerDriver { size_t thread_count, bool dump_stats, bool dump_passes, const std::string& dump_cfg_file_name, bool dump_cfg_append, CumulativeLogger* timer, int swap_fd, - const std::string& profile_file, - const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map); + const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map, + const ProfileCompilationInfo* profile_compilation_info); ~CompilerDriver(); @@ -657,9 +657,6 @@ class CompilerDriver { // This option may be restricted to the boot image, depending on a flag in the implementation. std::unique_ptr<std::unordered_set<std::string>> methods_to_compile_; - // Info for profile guided compilation. - std::unique_ptr<ProfileCompilationInfo> profile_compilation_info_; - bool had_hard_verifier_failure_; size_t thread_count_; @@ -689,6 +686,9 @@ class CompilerDriver { CompiledMethodStorage compiled_method_storage_; + // Info for profile guided compilation. + const ProfileCompilationInfo* const profile_compilation_info_; + friend class CompileClassVisitor; DISALLOW_COPY_AND_ASSIGN(CompilerDriver); }; diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index b323d24038..85216b7610 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -155,8 +155,8 @@ JitCompiler::JitCompiler() : total_time_(0) { /* dump_cfg_append */ false, cumulative_logger_.get(), /* swap_fd */ -1, - /* profile_file */ "", - /* dex to oat map */ nullptr)); + /* dex to oat map */ nullptr, + /* profile_compilation_info */ nullptr)); // Disable dedupe so we can remove compiled methods. compiler_driver_->SetDedupeEnabled(false); compiler_driver_->SetSupportBootImageFixup(false); diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h index 877a674674..b10cc3534c 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/compiler/linker/relative_patcher_test.h @@ -47,7 +47,7 @@ class RelativePatcherTest : public testing::Test { driver_(&compiler_options_, &verification_results_, &inliner_map_, Compiler::kQuick, instruction_set, nullptr, false, nullptr, nullptr, nullptr, 1u, - false, false, "", false, nullptr, -1, "", nullptr), + false, false, "", false, nullptr, -1, nullptr, nullptr), error_msg_(), instruction_set_(instruction_set), features_(InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg_)), diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 58f46d69a2..9f7ffa5ace 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -121,7 +121,7 @@ class OatTest : public CommonCompilerTest { false, timer_.get(), -1, - "", + nullptr, nullptr)); } diff --git a/compiler/profile_assistant.cc b/compiler/profile_assistant.cc new file mode 100644 index 0000000000..81f2a5692d --- /dev/null +++ b/compiler/profile_assistant.cc @@ -0,0 +1,69 @@ +/* + * 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" + +namespace art { + +// Minimum number of new methods that profiles must contain to enable recompilation. +static constexpr const uint32_t kMinNewMethodsForCompilation = 10; + +bool ProfileAssistant::ProcessProfiles( + const std::vector<std::string>& profile_files, + const std::vector<std::string>& 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 < profile_files.size(); i++) { + if (!new_info[i].Load(profile_files[i])) { + LOG(WARNING) << "Could not load profile file: " << profile_files[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) { + *profile_compilation_info = nullptr; + return true; + } + + std::unique_ptr<ProfileCompilationInfo> result(new ProfileCompilationInfo()); + for (size_t i = 0; i < new_info.size(); i++) { + // Merge all data into a single object. + result->Load(new_info[i]); + // If we have any reference profile information merge their information with + // the current profiles and save them back to disk. + if (!reference_profile_files.empty()) { + if (!new_info[i].Load(reference_profile_files[i])) { + LOG(WARNING) << "Could not load reference profile file: " << reference_profile_files[i]; + return false; + } + if (!new_info[i].Save(reference_profile_files[i])) { + LOG(WARNING) << "Could not save reference profile file: " << reference_profile_files[i]; + return false; + } + } + } + *profile_compilation_info = result.release(); + return true; +} + +} // namespace art diff --git a/compiler/profile_assistant.h b/compiler/profile_assistant.h new file mode 100644 index 0000000000..088c8bd1c7 --- /dev/null +++ b/compiler/profile_assistant.h @@ -0,0 +1,61 @@ +/* + * 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 "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); + + private: + DISALLOW_COPY_AND_ASSIGN(ProfileAssistant); +}; + +} // namespace art + +#endif // ART_COMPILER_PROFILE_ASSISTANT_H_ diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 50480d9043..c4f68ea966 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -63,6 +63,7 @@ #include "gc/space/space-inl.h" #include "image_writer.h" #include "interpreter/unstarted_runtime.h" +#include "jit/offline_profiling_info.h" #include "leb128.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" @@ -70,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" @@ -328,6 +330,16 @@ 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(" --print-pass-names: print a list of pass names"); UsageError(""); @@ -767,6 +779,13 @@ class Dex2Oat FINAL { } } + 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."); + } + } + if (!parser_options->oat_symbols.empty()) { oat_unstripped_ = std::move(parser_options->oat_symbols); } @@ -1057,8 +1076,10 @@ class Dex2Oat FINAL { } 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=")).data(); - VLOG(compiler) << "dex2oat: profile file is " << 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()); } else if (option == "--no-profile-file") { // No profile } else if (option == "--host") { @@ -1479,9 +1500,8 @@ class Dex2Oat FINAL { dump_cfg_append_, compiler_phases_timings_.get(), swap_fd_, - profile_file_, - &dex_file_oat_filename_map_)); - + &dex_file_oat_filename_map_, + profile_compilation_info_.get())); driver_->SetDexFilesForOatFile(dex_files_); driver_->CompileAll(class_loader, dex_files_, timings_); } @@ -1790,6 +1810,26 @@ class Dex2Oat FINAL { return is_host_; } + bool UseProfileGuidedCompilation() const { + return !profile_files_.empty(); + } + + bool ProcessProfiles() { + DCHECK(UseProfileGuidedCompilation()); + ProfileCompilationInfo* info = nullptr; + if (ProfileAssistant::ProcessProfiles(profile_files_, reference_profile_files_, &info)) { + profile_compilation_info_.reset(info); + return true; + } + return false; + } + + bool ShouldCompileBasedOnProfiles() const { + DCHECK(UseProfileGuidedCompilation()); + // If we are given profiles, compile only if we have new information. + return profile_compilation_info_ != nullptr; + } + private: template <typename T> static std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) { @@ -2263,7 +2303,9 @@ class Dex2Oat FINAL { int swap_fd_; std::string app_image_file_name_; int app_image_fd_; - std::string profile_file_; // Profile file to use + std::vector<std::string> profile_files_; + std::vector<std::string> reference_profile_files_; + std::unique_ptr<ProfileCompilationInfo> profile_compilation_info_; TimingLogger* timings_; std::unique_ptr<CumulativeLogger> compiler_phases_timings_; std::vector<std::vector<const DexFile*>> dex_files_per_oat_file_; @@ -2380,6 +2422,20 @@ static int dex2oat(int argc, char** argv) { // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. dex2oat.ParseArgs(argc, 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.ShouldCompileBasedOnProfiles()) { + LOG(INFO) << "Skipped compilation because of insignificant profile delta"; + return EXIT_SUCCESS; + } + } else { + LOG(WARNING) << "Failed to process profile files"; + return EXIT_FAILURE; + } + } + // Check early that the result of compilation can be written if (!dex2oat.OpenFile()) { return EXIT_FAILURE; diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc index 3942b0ba02..a132701796 100644 --- a/runtime/jit/offline_profiling_info.cc +++ b/runtime/jit/offline_profiling_info.cc @@ -30,38 +30,40 @@ namespace art { -void OfflineProfilingInfo::SaveProfilingInfo(const std::string& filename, - const std::vector<ArtMethod*>& methods) { +bool ProfileCompilationInfo::SaveProfilingInfo(const std::string& filename, + const std::vector<ArtMethod*>& methods) { if (methods.empty()) { VLOG(profiler) << "No info to save to " << filename; - return; + return true; } - DexFileToMethodsMap info; + ProfileCompilationInfo info; + if (!info.Load(filename)) { + LOG(WARNING) << "Could not load previous profile data from file " << filename; + return false; + } { ScopedObjectAccess soa(Thread::Current()); for (auto it = methods.begin(); it != methods.end(); it++) { - AddMethodInfo(*it, &info); + const DexFile* dex_file = (*it)->GetDexFile(); + if (!info.AddData(dex_file->GetLocation(), + dex_file->GetLocationChecksum(), + (*it)->GetDexMethodIndex())) { + return false; + } } } // This doesn't need locking because we are trying to lock the file for exclusive // access and fail immediately if we can't. - if (Serialize(filename, info)) { + bool result = info.Save(filename); + if (result) { VLOG(profiler) << "Successfully saved profile info to " << filename << " Size: " << GetFileSizeBytes(filename); + } else { + VLOG(profiler) << "Failed to save profile info to " << filename; } -} - -void OfflineProfilingInfo::AddMethodInfo(ArtMethod* method, DexFileToMethodsMap* info) { - DCHECK(method != nullptr); - const DexFile* dex_file = method->GetDexFile(); - - auto info_it = info->find(dex_file); - if (info_it == info->end()) { - info_it = info->Put(dex_file, std::set<uint32_t>()); - } - info_it->second.insert(method->GetDexMethodIndex()); + return result; } enum OpenMode { @@ -135,8 +137,7 @@ static constexpr const char kLineSeparator = '\n'; * /system/priv-app/app/app.apk,131232145,11,23,454,54 * /system/priv-app/app/app.apk:classes5.dex,218490184,39,13,49,1 **/ -bool OfflineProfilingInfo::Serialize(const std::string& filename, - const DexFileToMethodsMap& info) const { +bool ProfileCompilationInfo::Save(const std::string& filename) { int fd = OpenFile(filename, READ_WRITE); if (fd == -1) { return false; @@ -146,14 +147,12 @@ bool OfflineProfilingInfo::Serialize(const std::string& filename, // TODO(calin): Profile this and see how much memory it takes. If too much, // write to file directly. std::ostringstream os; - for (auto it : info) { - const DexFile* dex_file = it.first; - const std::set<uint32_t>& method_dex_ids = it.second; - - os << dex_file->GetLocation() - << kFieldSeparator - << dex_file->GetLocationChecksum(); - for (auto method_it : method_dex_ids) { + for (const auto& it : info_) { + const std::string& dex_location = it.first; + const DexFileData& dex_data = it.second; + + os << dex_location << kFieldSeparator << dex_data.checksum; + for (auto method_it : dex_data.method_set) { os << kFieldSeparator << method_it; } os << kLineSeparator; @@ -188,8 +187,22 @@ static void SplitString(const std::string& s, char separator, std::vector<std::s } } -bool ProfileCompilationInfo::ProcessLine(const std::string& line, - const std::vector<const DexFile*>& dex_files) { +bool ProfileCompilationInfo::AddData(const std::string& dex_location, + uint32_t checksum, + uint16_t method_idx) { + auto info_it = info_.find(dex_location); + if (info_it == info_.end()) { + info_it = info_.Put(dex_location, DexFileData(checksum)); + } + if (info_it->second.checksum != checksum) { + LOG(WARNING) << "Checksum mismatch for dex " << dex_location; + return false; + } + info_it->second.method_set.insert(method_idx); + return true; +} + +bool ProfileCompilationInfo::ProcessLine(const std::string& line) { std::vector<std::string> parts; SplitString(line, kFieldSeparator, &parts); if (parts.size() < 3) { @@ -203,39 +216,13 @@ bool ProfileCompilationInfo::ProcessLine(const std::string& line, return false; } - const DexFile* current_dex_file = nullptr; - for (auto dex_file : dex_files) { - if (dex_file->GetLocation() == dex_location) { - if (checksum != dex_file->GetLocationChecksum()) { - LOG(WARNING) << "Checksum mismatch for " - << dex_file->GetLocation() << " when parsing " << filename_; - return false; - } - current_dex_file = dex_file; - break; - } - } - if (current_dex_file == nullptr) { - return true; - } - for (size_t i = 2; i < parts.size(); i++) { uint32_t method_idx; if (!ParseInt(parts[i].c_str(), &method_idx)) { LOG(WARNING) << "Cannot parse method_idx " << parts[i]; return false; } - uint16_t class_idx = current_dex_file->GetMethodId(method_idx).class_idx_; - auto info_it = info_.find(current_dex_file); - if (info_it == info_.end()) { - info_it = info_.Put(current_dex_file, ClassToMethodsMap()); - } - ClassToMethodsMap& class_map = info_it->second; - auto class_it = class_map.find(class_idx); - if (class_it == class_map.end()) { - class_it = class_map.Put(class_idx, std::set<uint32_t>()); - } - class_it->second.insert(method_idx); + AddData(dex_location, checksum, method_idx); } return true; } @@ -262,25 +249,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(const std::vector<const DexFile*>& dex_files) { - if (dex_files.empty()) { - return true; - } - if (kIsDebugBuild) { - // In debug builds verify that the locations are unique. - std::set<std::string> locations; - for (auto dex_file : dex_files) { - const std::string& location = dex_file->GetLocation(); - DCHECK(locations.find(location) == locations.end()) - << "DexFiles appear to belong to different apks." - << " There are multiple dex files with the same location: " - << location; - locations.insert(location); - } - } - info_.clear(); - - int fd = OpenFile(filename_, READ); +bool ProfileCompilationInfo::Load(const std::string& filename) { + int fd = OpenFile(filename, READ); if (fd == -1) { return false; } @@ -293,7 +263,7 @@ bool ProfileCompilationInfo::Load(const std::vector<const DexFile*>& dex_files) while (success) { int n = read(fd, buffer, kBufferSize); if (n < 0) { - PLOG(WARNING) << "Error when reading profile file " << filename_; + PLOG(WARNING) << "Error when reading profile file " << filename; success = false; break; } else if (n == 0) { @@ -307,7 +277,7 @@ bool ProfileCompilationInfo::Load(const std::vector<const DexFile*>& dex_files) if (current_start_pos == -1) { break; } - if (!ProcessLine(current_line, dex_files)) { + if (!ProcessLine(current_line)) { success = false; break; } @@ -318,25 +288,50 @@ bool ProfileCompilationInfo::Load(const std::vector<const DexFile*>& dex_files) if (!success) { info_.clear(); } - return CloseDescriptorForFile(fd, filename_) && success; + return CloseDescriptorForFile(fd, filename) && success; +} + +bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) { + for (const auto& other_it : other.info_) { + const std::string& other_dex_location = other_it.first; + const DexFileData& other_dex_data = other_it.second; + + auto info_it = info_.find(other_dex_location); + if (info_it == info_.end()) { + info_it = info_.Put(other_dex_location, DexFileData(other_dex_data.checksum)); + } + if (info_it->second.checksum != other_dex_data.checksum) { + LOG(WARNING) << "Checksum mismatch for dex " << other_dex_location; + return false; + } + info_it->second.method_set.insert(other_dex_data.method_set.begin(), + other_dex_data.method_set.end()); + } + return true; } bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const { - auto info_it = info_.find(method_ref.dex_file); + auto info_it = info_.find(method_ref.dex_file->GetLocation()); if (info_it != info_.end()) { - uint16_t class_idx = method_ref.dex_file->GetMethodId(method_ref.dex_method_index).class_idx_; - const ClassToMethodsMap& class_map = info_it->second; - auto class_it = class_map.find(class_idx); - if (class_it != class_map.end()) { - const std::set<uint32_t>& methods = class_it->second; - return methods.find(method_ref.dex_method_index) != methods.end(); + if (method_ref.dex_file->GetLocationChecksum() != info_it->second.checksum) { + return false; } - return false; + const std::set<uint16_t>& methods = info_it->second.method_set; + return methods.find(method_ref.dex_method_index) != methods.end(); } return false; } -std::string ProfileCompilationInfo::DumpInfo(bool print_full_dex_location) const { +uint32_t ProfileCompilationInfo::GetNumberOfMethods() const { + uint32_t total = 0; + for (const auto& it : info_) { + total += it.second.method_set.size(); + } + return total; +} + +std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files, + bool print_full_dex_location) const { std::ostringstream os; if (info_.empty()) { return "ProfileInfo: empty"; @@ -344,17 +339,11 @@ std::string ProfileCompilationInfo::DumpInfo(bool print_full_dex_location) const os << "ProfileInfo:"; - // Use an additional map to achieve a predefined order based on the dex locations. - SafeMap<const std::string, const DexFile*> dex_locations_map; - for (auto info_it : info_) { - dex_locations_map.Put(info_it.first->GetLocation(), info_it.first); - } - const std::string kFirstDexFileKeySubstitute = ":classes.dex"; - for (auto dex_file_it : dex_locations_map) { + for (const auto& it : info_) { os << "\n"; - const std::string& location = dex_file_it.first; - const DexFile* dex_file = dex_file_it.second; + const std::string& location = it.first; + const DexFileData& dex_data = it.second; if (print_full_dex_location) { os << location; } else { @@ -362,10 +351,19 @@ std::string ProfileCompilationInfo::DumpInfo(bool print_full_dex_location) const std::string multidex_suffix = DexFile::GetMultiDexSuffix(location); os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix); } - for (auto class_it : info_.find(dex_file)->second) { - for (auto method_it : class_it.second) { - os << "\n " << PrettyMethod(method_it, *dex_file, true); + for (const auto method_it : dex_data.method_set) { + if (dex_files != nullptr) { + const DexFile* dex_file = nullptr; + for (size_t i = 0; i < dex_files->size(); i++) { + if (location == (*dex_files)[i]->GetLocation()) { + dex_file = (*dex_files)[i]; + } + } + if (dex_file != nullptr) { + os << "\n " << PrettyMethod(method_it, *dex_file, true); + } } + os << "\n " << method_it; } } return os.str(); diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h index 32d4c5bedc..26e1ac385f 100644 --- a/runtime/jit/offline_profiling_info.h +++ b/runtime/jit/offline_profiling_info.h @@ -29,60 +29,50 @@ namespace art { class ArtMethod; +// TODO: rename file. /** - * Profiling information in a format that can be serialized to disk. - * It is a serialize-friendly format based on information collected - * by the interpreter (ProfileInfo). + * Profile information in a format suitable to be queried by the compiler and + * performing profile guided compilation. + * It is a serialize-friendly format based on information collected by the + * interpreter (ProfileInfo). * Currently it stores only the hot compiled methods. */ -class OfflineProfilingInfo { - public: - void SaveProfilingInfo(const std::string& filename, const std::vector<ArtMethod*>& methods); - - private: - // Map identifying the location of the profiled methods. - // dex_file_ -> [dex_method_index]+ - using DexFileToMethodsMap = SafeMap<const DexFile*, std::set<uint32_t>>; - - void AddMethodInfo(ArtMethod* method, DexFileToMethodsMap* info) - SHARED_REQUIRES(Locks::mutator_lock_); - bool Serialize(const std::string& filename, const DexFileToMethodsMap& info) const; -}; - -/** - * Profile information in a format suitable to be queried by the compiler and performing - * profile guided compilation. - */ class ProfileCompilationInfo { public: - // Constructs a ProfileCompilationInfo backed by the provided file. - explicit ProfileCompilationInfo(const std::string& filename) : filename_(filename) {} - - // Loads profile information corresponding to the provided dex files. - // The dex files' multidex suffixes must be unique. - // This resets the state of the profiling information - // (i.e. all previously loaded info are cleared). - bool Load(const std::vector<const DexFile*>& dex_files); + static bool SaveProfilingInfo(const std::string& filename, + const std::vector<ArtMethod*>& methods); + + // Loads profile information from the given file. + bool Load(const std::string& profile_filename); + // Loads the data from another ProfileCompilationInfo object. + bool Load(const ProfileCompilationInfo& info); + // Saves the profile data to the given file. + bool Save(const std::string& profile_filename); + // Returns the number of methods that were profiled. + uint32_t GetNumberOfMethods() const; // Returns true if the method reference is present in the profiling info. bool ContainsMethod(const MethodReference& method_ref) const; - const std::string& GetFilename() const { return filename_; } - // Dumps all the loaded profile info into a string and returns it. + // If dex_files is not null then the method indices will be resolved to their + // names. // This is intended for testing and debugging. - std::string DumpInfo(bool print_full_dex_location = true) const; + std::string DumpInfo(const std::vector<const DexFile*>* dex_files, + bool print_full_dex_location = true) const; private: - bool ProcessLine(const std::string& line, - const std::vector<const DexFile*>& dex_files); + bool AddData(const std::string& dex_location, uint32_t checksum, uint16_t method_idx); + bool ProcessLine(const std::string& line); + + struct DexFileData { + explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {} + uint32_t checksum; + std::set<uint16_t> method_set; + }; - using ClassToMethodsMap = SafeMap<uint32_t, std::set<uint32_t>>; - // Map identifying the location of the profiled methods. - // dex_file -> class_index -> [dex_method_index]+ - using DexFileToProfileInfoMap = SafeMap<const DexFile*, ClassToMethodsMap>; + using DexFileToProfileInfoMap = SafeMap<const std::string, DexFileData>; - const std::string filename_; DexFileToProfileInfoMap info_; }; diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 0278138d6e..ec289ea2b5 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -106,10 +106,9 @@ bool ProfileSaver::ProcessProfilingInfo() { VLOG(profiler) << "Not enough information to save. Nr of methods: " << methods.size(); return false; } - offline_profiling_info_.SaveProfilingInfo(output_filename_, methods); - - VLOG(profiler) << "Saved profile time: " << PrettyDuration(NanoTime() - start); + ProfileCompilationInfo::SaveProfilingInfo(output_filename_, methods); + VLOG(profiler) << "Profile process time: " << PrettyDuration(NanoTime() - start); return true; } diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h index 88efd41156..d60142b205 100644 --- a/runtime/jit/profile_saver.h +++ b/runtime/jit/profile_saver.h @@ -66,7 +66,6 @@ class ProfileSaver { const std::string output_filename_; jit::JitCodeCache* jit_code_cache_; const std::set<const std::string> tracked_dex_base_locations_; - OfflineProfilingInfo offline_profiling_info_; uint64_t code_cache_last_update_time_ns_; bool shutting_down_ GUARDED_BY(Locks::profiler_lock_); |