diff options
author | 2024-08-09 15:47:02 +0000 | |
---|---|---|
committer | 2024-08-10 03:17:31 +0000 | |
commit | 2e2c1ac96d9ea3fc04a4758912774180ee6bfac7 (patch) | |
tree | f319f6dbd5a4647d02a35cc59546dc39a58a0723 | |
parent | a77898ceddae1a61f169c11747eda08bfeb5d6e3 (diff) |
Support inline cache for boot image profile HRF in Profman.
Refactor `GetInlineCacheLine` at `profman.cc` to be used for normal profiles as well as boot image profiles (using `--generate-boot-image-profile` flag).
With this change, the dumped boot-image profile HRF will contain inline cache for hot methods in the profile.
Bug: 348109271
Change-Id: Ie3645b2f3004c01f15298a524f74f14576b5a255
Test: Locally run `./art/tools/boot-image-profile-generate.sh /usr/local/google/home/islamelbanna/Documents /usr/local/google/home/islamelbanna/Downloads/boot.zip frameworks/base/config/preloaded-classes-denylist /usr/local/google/home/islamelbanna/Downloads/android.prof --profman-arg --upgrade-startup-to-hot=false --profman-arg --generate-boot-image-profile` Partial result(since file is too big): gpaste/4527986827329536
-rw-r--r-- | libprofile/profile/profile_compilation_info.cc | 70 | ||||
-rw-r--r-- | libprofile/profile/profile_compilation_info.h | 25 | ||||
-rw-r--r-- | profman/Android.bp | 1 | ||||
-rw-r--r-- | profman/boot_image_profile.cc | 5 | ||||
-rw-r--r-- | profman/inline_cache_format_util.cc | 53 | ||||
-rw-r--r-- | profman/inline_cache_format_util.h | 40 | ||||
-rw-r--r-- | profman/profman.cc | 91 |
7 files changed, 192 insertions, 93 deletions
diff --git a/libprofile/profile/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc index 631bf1148b..9bc43f69b7 100644 --- a/libprofile/profile/profile_compilation_info.cc +++ b/libprofile/profile/profile_compilation_info.cc @@ -60,6 +60,7 @@ #include "dex/code_item_accessors-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file_loader.h" +#include "dex/dex_instruction-inl.h" #ifdef ART_TARGET_ANDROID #include "android-modules-utils/sdk_level.h" @@ -3030,9 +3031,49 @@ FlattenProfileData::ItemMetadata::ItemMetadata() : flags_(0) { } -FlattenProfileData::ItemMetadata::ItemMetadata(const ItemMetadata& other) : - flags_(other.flags_), - annotations_(other.annotations_) { +void FlattenProfileData::ItemMetadata::ExtractInlineCacheInfo( + const ProfileCompilationInfo& profile_info, + const DexFile* dex_file, + uint16_t dex_method_idx) { + ProfileCompilationInfo::MethodHotness hotness = + profile_info.GetMethodHotness(MethodReference(dex_file, dex_method_idx)); + DCHECK(!hotness.IsHot() || hotness.GetInlineCacheMap() != nullptr); + if (!hotness.IsHot() || hotness.GetInlineCacheMap()->empty()) { + return; + } + const dex::MethodId& id = dex_file->GetMethodId(dex_method_idx); + const ProfileCompilationInfo::InlineCacheMap* inline_caches = hotness.GetInlineCacheMap(); + const dex::ClassDef* class_def = dex_file->FindClassDef(id.class_idx_); + if (class_def == nullptr) { + // No class def found. + return; + } + + CodeItemInstructionAccessor accessor( + *dex_file, dex_file->GetCodeItem(dex_file->FindCodeItemOffset(*class_def, dex_method_idx))); + for (const auto& [pc, ic_data] : *inline_caches) { + if (pc >= accessor.InsnsSizeInCodeUnits()) { + // Inlined inline caches are not supported in AOT, so discard any pc beyond the + // code item size. See also `HInliner::GetInlineCacheAOT`. + continue; + } + const Instruction& inst = accessor.InstructionAt(pc); + const dex::MethodId& target = dex_file->GetMethodId(inst.VRegB()); + if (ic_data.classes.empty() && !ic_data.is_megamorphic && !ic_data.is_missing_types) { + continue; + } + InlineCacheInfo& val = + inline_cache_.FindOrAdd(TypeReference(dex_file, target.class_idx_))->second; + if (ic_data.is_megamorphic) { + val.is_megamorphic_ = true; + } + if (ic_data.is_missing_types) { + val.is_missing_types_ = true; + } + for (dex::TypeIndex type_index : ic_data.classes) { + val.classes_.insert(profile_info.GetTypeDescriptor(dex_file, type_index)); + } + } } std::unique_ptr<FlattenProfileData> ProfileCompilationInfo::ExtractProfileData( @@ -3067,6 +3108,8 @@ std::unique_ptr<FlattenProfileData> ProfileCompilationInfo::ExtractProfileData( result->method_metadata_.GetOrCreate(ref, create_metadata_fn); metadata.flags_ |= hotness.flags_; metadata.annotations_.push_back(annotation); + metadata.ExtractInlineCacheInfo(*this, dex_file.get(), method_idx); + // Update the max aggregation counter for methods. // This is essentially a cache, to avoid traversing all the methods just to find out // this value. @@ -3098,6 +3141,26 @@ std::unique_ptr<FlattenProfileData> ProfileCompilationInfo::ExtractProfileData( return result; } +void FlattenProfileData::ItemMetadata::MergeInlineCacheInfo( + const SafeMap<TypeReference, InlineCacheInfo, TypeReferenceValueComparator>& other) { + for (const auto& [target, inline_cache_data] : other) { + if (!inline_cache_data.is_megamorphic_ && !inline_cache_data.is_missing_types_ && + inline_cache_data.classes_.empty()) { + continue; + } + InlineCacheInfo& val = inline_cache_.FindOrAdd(target)->second; + if (inline_cache_data.is_megamorphic_) { + val.is_megamorphic_ = true; + } + if (inline_cache_data.is_missing_types_) { + val.is_missing_types_ = true; + } + for (const std::string& cls : inline_cache_data.classes_) { + val.classes_.insert(cls); + } + } +} + void FlattenProfileData::MergeData(const FlattenProfileData& other) { auto create_metadata_fn = []() { return FlattenProfileData::ItemMetadata(); }; for (const auto& it : other.method_metadata_) { @@ -3111,6 +3174,7 @@ void FlattenProfileData::MergeData(const FlattenProfileData& other) { metadata.flags_ |= otherData.GetFlags(); metadata.annotations_.insert( metadata.annotations_.end(), other_annotations.begin(), other_annotations.end()); + metadata.MergeInlineCacheInfo(otherData.GetInlineCache()); max_aggregation_for_methods_ = std::max( max_aggregation_for_methods_, diff --git a/libprofile/profile/profile_compilation_info.h b/libprofile/profile/profile_compilation_info.h index 104198f1fa..f6b0105ff6 100644 --- a/libprofile/profile/profile_compilation_info.h +++ b/libprofile/profile/profile_compilation_info.h @@ -1092,8 +1092,14 @@ class FlattenProfileData { public: class ItemMetadata { public: + struct InlineCacheInfo { + bool is_megamorphic_ = false; + bool is_missing_types_ = false; + std::set<std::string> classes_; + }; + ItemMetadata(); - ItemMetadata(const ItemMetadata& other); + ItemMetadata(const ItemMetadata& other) = default; uint16_t GetFlags() const { return flags_; @@ -1111,12 +1117,29 @@ class FlattenProfileData { return (flags_ & flag) != 0; } + // Extracts inline cache info for the given method into this instance. + // Note that this will collapse all ICs with the same receiver type. + void ExtractInlineCacheInfo(const ProfileCompilationInfo& profile_info, + const DexFile* dex_file, + uint16_t dex_method_idx); + + // Merges the inline cache info from the other metadata into this instance. + void MergeInlineCacheInfo( + const SafeMap<TypeReference, InlineCacheInfo, TypeReferenceValueComparator>& other); + + const SafeMap<TypeReference, InlineCacheInfo, TypeReferenceValueComparator>& GetInlineCache() + const { + return inline_cache_; + } + private: // will be 0 for classes and MethodHotness::Flags for methods. uint16_t flags_; // This is a list that may contain duplicates after a merge operation. // It represents that a method was used multiple times across different devices. std::list<ProfileCompilationInfo::ProfileSampleAnnotation> annotations_; + // Inline cache map for methods. + SafeMap<TypeReference, InlineCacheInfo, TypeReferenceValueComparator> inline_cache_; friend class ProfileCompilationInfo; friend class FlattenProfileData; diff --git a/profman/Android.bp b/profman/Android.bp index df8c55fd18..948b6fd23d 100644 --- a/profman/Android.bp +++ b/profman/Android.bp @@ -32,6 +32,7 @@ cc_defaults { "boot_image_profile.cc", "profman.cc", "profile_assistant.cc", + "inline_cache_format_util.cc", ], header_libs: ["profman_headers"], diff --git a/profman/boot_image_profile.cc b/profman/boot_image_profile.cc index fc20f20a65..9c46786df2 100644 --- a/profman/boot_image_profile.cc +++ b/profman/boot_image_profile.cc @@ -20,13 +20,13 @@ #include <set> #include "android-base/file.h" -#include "base/unix_file/fd_file.h" #include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/method_reference.h" #include "dex/type_reference.h" #include "profile/profile_compilation_info.h" +#include "inline_cache_format_util.h" namespace art { @@ -100,7 +100,8 @@ static std::string MethodToProfileFormat( extra = kPackageUseDelim + GetPackageUseString(metadata); } - return flags_string + method + extra; + std::string inline_cache_string = GetInlineCacheLine(metadata.GetInlineCache()); + return flags_string + method + extra + inline_cache_string; } // Converts a class representation to its final profile or preloaded classes format. diff --git a/profman/inline_cache_format_util.cc b/profman/inline_cache_format_util.cc new file mode 100644 index 0000000000..24ed8a09cd --- /dev/null +++ b/profman/inline_cache_format_util.cc @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 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 "inline_cache_format_util.h" + +#include "profile/profile_compilation_info.h" + +namespace art { + +std::string GetInlineCacheLine(const SafeMap<TypeReference, + FlattenProfileData::ItemMetadata::InlineCacheInfo, + TypeReferenceValueComparator>& inline_cache) { + if (inline_cache.empty()) { + return ""; + } + std::ostringstream dump_ic; + dump_ic << kProfileParsingInlineChacheSep; + for (const auto& [target_ref, inline_cache_data] : inline_cache) { + dump_ic << kProfileParsingInlineChacheTargetSep; + dump_ic << target_ref.dex_file->GetTypeDescriptor( + target_ref.dex_file->GetTypeId(target_ref.TypeIndex())); + if (inline_cache_data.is_missing_types_) { + dump_ic << kMissingTypesMarker; + } else if (inline_cache_data.is_megamorphic_) { + dump_ic << kMegamorphicTypesMarker; + } else { + bool first = true; + for (const std::string& cls : inline_cache_data.classes_) { + if (!first) { + dump_ic << kProfileParsingTypeSep; + } + first = false; + dump_ic << cls; + } + } + } + return dump_ic.str(); +} + +} // namespace art diff --git a/profman/inline_cache_format_util.h b/profman/inline_cache_format_util.h new file mode 100644 index 0000000000..ef3d4742f6 --- /dev/null +++ b/profman/inline_cache_format_util.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 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_INLINE_CACHE_FORMAT_UTIL_H_ +#define ART_PROFMAN_INLINE_CACHE_FORMAT_UTIL_H_ + +#include "profile/profile_compilation_info.h" + +namespace art { + +constexpr char kProfileParsingInlineChacheSep = '+'; +constexpr char kProfileParsingInlineChacheTargetSep = ']'; +constexpr char kMissingTypesMarker[] = "missing_types"; +constexpr char kMegamorphicTypesMarker[] = "megamorphic_types"; +constexpr char kProfileParsingTypeSep = ','; + +// Creates the inline-cache portion of a text-profile line. If there is no +// inline-caches this will be an empty string. Otherwise it will be '@' +// followed by an IC description matching the format described by +// Profman::ProcessLine. +std::string GetInlineCacheLine(const SafeMap<TypeReference, + FlattenProfileData::ItemMetadata::InlineCacheInfo, + TypeReferenceValueComparator>& inline_cache); + +} // namespace art + +#endif // ART_PROFMAN_INLINE_CACHE_FORMAT_UTIL_H_ diff --git a/profman/profman.cc b/profman/profman.cc index 1525b476cd..2fcb2ef72b 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -63,6 +63,7 @@ #include "profile/profile_compilation_info.h" #include "profile_assistant.h" #include "profman/profman_result.h" +#include "inline_cache_format_util.h" namespace art { @@ -209,14 +210,9 @@ static constexpr uint16_t kDefaultTestProfileClassPercentage = 5; // Separators used when parsing human friendly representation of profiles. static const std::string kMethodSep = "->"; // NOLINT [runtime/string] [4] -static const std::string kMissingTypesMarker = "missing_types"; // NOLINT [runtime/string] [4] -static const std::string kMegamorphicTypesMarker = "megamorphic_types"; // NOLINT [runtime/string] [4] static const std::string kClassAllMethods = "*"; // NOLINT [runtime/string] [4] static constexpr char kAnnotationStart = '{'; static constexpr char kAnnotationEnd = '}'; -static constexpr char kProfileParsingInlineChacheSep = '+'; -static constexpr char kProfileParsingInlineChacheTargetSep = ']'; -static constexpr char kProfileParsingTypeSep = ','; static constexpr char kProfileParsingFirstCharInSignature = '('; static constexpr char kMethodFlagStringHot = 'H'; static constexpr char kMethodFlagStringStartup = 'S'; @@ -801,86 +797,6 @@ class ProfMan final { return dump_only_; } - // Creates the inline-cache portion of a text-profile line. If the class def can't be found, or if - // there is no inline-caches this will be and empty string. Otherwise it will be '@' followed by - // an IC description matching the format described by ProcessLine below. Note that this will - // collapse all ICs with the same receiver type. - std::string GetInlineCacheLine(const ProfileCompilationInfo& profile_info, - const dex::MethodId& id, - const DexFile* dex_file, - uint16_t dex_method_idx) { - ProfileCompilationInfo::MethodHotness hotness = - profile_info.GetMethodHotness(MethodReference(dex_file, dex_method_idx)); - DCHECK(!hotness.IsHot() || hotness.GetInlineCacheMap() != nullptr); - if (!hotness.IsHot() || hotness.GetInlineCacheMap()->empty()) { - return ""; - } - const ProfileCompilationInfo::InlineCacheMap* inline_caches = hotness.GetInlineCacheMap(); - struct IcLineInfo { - bool is_megamorphic_ = false; - bool is_missing_types_ = false; - std::set<dex::TypeIndex> classes_; - }; - std::unordered_map<dex::TypeIndex, IcLineInfo> ics; - const dex::ClassDef* class_def = dex_file->FindClassDef(id.class_idx_); - if (class_def == nullptr) { - // No class def found. - return ""; - } - - CodeItemInstructionAccessor accessor( - *dex_file, dex_file->GetCodeItem(dex_file->FindCodeItemOffset(*class_def, dex_method_idx))); - for (const auto& [pc, ic_data] : *inline_caches) { - if (pc >= accessor.InsnsSizeInCodeUnits()) { - // Inlined inline caches are not supported in AOT, so discard any pc beyond the - // code item size. See also `HInliner::GetInlineCacheAOT`. - continue; - } - const Instruction& inst = accessor.InstructionAt(pc); - const dex::MethodId& target = dex_file->GetMethodId(inst.VRegB()); - if (ic_data.classes.empty() && !ic_data.is_megamorphic && !ic_data.is_missing_types) { - continue; - } - auto val = ics.find(target.class_idx_); - if (val == ics.end()) { - val = ics.insert({ target.class_idx_, {} }).first; - } - if (ic_data.is_megamorphic) { - val->second.is_megamorphic_ = true; - } - if (ic_data.is_missing_types) { - val->second.is_missing_types_ = true; - } - for (dex::TypeIndex type_index : ic_data.classes) { - val->second.classes_.insert(type_index); - } - } - if (ics.empty()) { - return ""; - } - std::ostringstream dump_ic; - dump_ic << kProfileParsingInlineChacheSep; - for (const auto& [target, dex_data] : ics) { - dump_ic << kProfileParsingInlineChacheTargetSep; - dump_ic << dex_file->GetTypeDescriptor(dex_file->GetTypeId(target)); - if (dex_data.is_missing_types_) { - dump_ic << kMissingTypesMarker; - } else if (dex_data.is_megamorphic_) { - dump_ic << kMegamorphicTypesMarker; - } else { - bool first = true; - for (dex::TypeIndex type_index : dex_data.classes_) { - if (!first) { - dump_ic << kProfileParsingTypeSep; - } - first = false; - dump_ic << profile_info.GetTypeDescriptor(dex_file, type_index); - } - } - } - return dump_ic.str(); - } - bool GetClassNamesAndMethods(const ProfileCompilationInfo& profile_info, std::vector<std::unique_ptr<const DexFile>>* dex_files, std::set<std::string>* out_lines) { @@ -916,8 +832,9 @@ class ProfMan final { if (post_startup_methods.find(dex_method_idx) != post_startup_methods.end()) { flags_string += kMethodFlagStringPostStartup; } - std::string inline_cache_string = - GetInlineCacheLine(profile_info, id, dex_file.get(), dex_method_idx); + FlattenProfileData::ItemMetadata metadata; + metadata.ExtractInlineCacheInfo(profile_info, dex_file.get(), dex_method_idx); + std::string inline_cache_string = GetInlineCacheLine(metadata.GetInlineCache()); out_lines->insert(ART_FORMAT("{}{}{}{}{}{}", flags_string, type_string, |