xref: /aosp_15_r20/art/profman/boot_image_profile.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "boot_image_profile.h"
18 
19 #include <memory>
20 #include <set>
21 
22 #include "android-base/file.h"
23 #include "dex/class_accessor-inl.h"
24 #include "dex/descriptors_names.h"
25 #include "dex/dex_file-inl.h"
26 #include "dex/method_reference.h"
27 #include "dex/type_reference.h"
28 #include "profile/profile_compilation_info.h"
29 #include "inline_cache_format_util.h"
30 
31 namespace art {
32 
33 using Hotness = ProfileCompilationInfo::MethodHotness;
34 
35 static const std::string kMethodSep = "->";  // NOLINT [runtime/string] [4]
36 static const std::string kPackageUseDelim = "@";  // NOLINT [runtime/string] [4]
37 static constexpr char kMethodFlagStringHot = 'H';
38 static constexpr char kMethodFlagStringStartup = 'S';
39 static constexpr char kMethodFlagStringPostStartup = 'P';
40 
41 // Returns the type descriptor of the given reference.
GetTypeDescriptor(const TypeReference & ref)42 static std::string GetTypeDescriptor(const TypeReference& ref) {
43   const dex::TypeId& type_id = ref.dex_file->GetTypeId(ref.TypeIndex());
44   return ref.dex_file->GetTypeDescriptor(type_id);
45 }
46 
47 // Returns the method representation used in the text format of the boot image profile.
BootImageRepresentation(const MethodReference & ref)48 static std::string BootImageRepresentation(const MethodReference& ref) {
49   const DexFile* dex_file = ref.dex_file;
50   const dex::MethodId& id = ref.GetMethodId();
51   std::string signature_string(dex_file->GetMethodSignature(id).ToString());
52   std::string type_string(dex_file->GetTypeDescriptor(dex_file->GetTypeId(id.class_idx_)));
53   std::string method_name(dex_file->GetMethodName(id));
54   return type_string +
55         kMethodSep +
56         method_name +
57         signature_string;
58 }
59 
60 // Returns the class representation used in the text format of the boot image profile.
BootImageRepresentation(const TypeReference & ref)61 static std::string BootImageRepresentation(const TypeReference& ref) {
62   return GetTypeDescriptor(ref);
63 }
64 
65 // Returns the class representation used in preloaded classes.
PreloadedClassesRepresentation(const TypeReference & ref)66 static std::string PreloadedClassesRepresentation(const TypeReference& ref) {
67   std::string descriptor = GetTypeDescriptor(ref);
68   return DescriptorToDot(descriptor.c_str());
69 }
70 
71 // Formats the list of packages from the item metadata as a debug string.
GetPackageUseString(const FlattenProfileData::ItemMetadata & metadata)72 static std::string GetPackageUseString(const FlattenProfileData::ItemMetadata& metadata) {
73   std::string result;
74   for (const auto& it : metadata.GetAnnotations()) {
75     result += it.GetOriginPackageName() + ",";
76   }
77 
78   return metadata.GetAnnotations().empty()
79       ? result
80       : result.substr(0, result.size() - 1);
81 }
82 
83 // Converts a method representation to its final profile format.
MethodToProfileFormat(const std::string & method,const FlattenProfileData::ItemMetadata & metadata,bool output_package_use)84 static std::string MethodToProfileFormat(
85     const std::string& method,
86     const FlattenProfileData::ItemMetadata& metadata,
87     bool output_package_use) {
88   std::string flags_string;
89   if (metadata.HasFlagSet(Hotness::kFlagHot)) {
90     flags_string += kMethodFlagStringHot;
91   }
92   if (metadata.HasFlagSet(Hotness::kFlagStartup)) {
93     flags_string += kMethodFlagStringStartup;
94   }
95   if (metadata.HasFlagSet(Hotness::kFlagPostStartup)) {
96     flags_string += kMethodFlagStringPostStartup;
97   }
98   std::string extra;
99   if (output_package_use) {
100     extra = kPackageUseDelim + GetPackageUseString(metadata);
101   }
102 
103   std::string inline_cache_string = GetInlineCacheLine(metadata.GetInlineCache());
104   return flags_string + method + extra + inline_cache_string;
105 }
106 
107 // Converts a class representation to its final profile or preloaded classes format.
ClassToProfileFormat(const std::string & classString,const FlattenProfileData::ItemMetadata & metadata,bool output_package_use)108 static std::string ClassToProfileFormat(
109     const std::string& classString,
110     const FlattenProfileData::ItemMetadata& metadata,
111     bool output_package_use) {
112   std::string extra;
113   if (output_package_use) {
114     extra = kPackageUseDelim + GetPackageUseString(metadata);
115   }
116 
117   return classString + extra;
118 }
119 
120 // Tries to asses if the given type reference is a clean class.
MaybeIsClassClean(const TypeReference & ref)121 static bool MaybeIsClassClean(const TypeReference& ref) {
122   const dex::ClassDef* class_def = ref.dex_file->FindClassDef(ref.TypeIndex());
123   if (class_def == nullptr) {
124     return false;
125   }
126 
127   ClassAccessor accessor(*ref.dex_file, *class_def);
128   for (auto& it : accessor.GetStaticFields()) {
129     if (!it.IsFinal()) {
130       // Not final static field will probably dirty the class.
131       return false;
132     }
133   }
134   for (auto& it : accessor.GetMethods()) {
135     uint32_t flags = it.GetAccessFlags();
136     if ((flags & kAccNative) != 0) {
137       // Native method will get dirtied.
138       return false;
139     }
140     if ((flags & kAccConstructor) != 0 && (flags & kAccStatic) != 0) {
141       // Class initializer, may get dirtied (not sure).
142       return false;
143     }
144   }
145 
146   return true;
147 }
148 
149 // Returns true iff the item should be included in the profile.
150 // (i.e. it passes the given aggregation thresholds)
IncludeItemInProfile(uint32_t max_aggregation_count,uint32_t item_threshold,const FlattenProfileData::ItemMetadata & metadata,const BootImageOptions & options)151 static bool IncludeItemInProfile(uint32_t max_aggregation_count,
152                                  uint32_t item_threshold,
153                                  const FlattenProfileData::ItemMetadata& metadata,
154                                  const BootImageOptions& options) {
155   CHECK_NE(max_aggregation_count, 0u);
156   float item_percent = metadata.GetAnnotations().size() / static_cast<float>(max_aggregation_count);
157   for (const auto& annotIt : metadata.GetAnnotations()) {
158     const auto&thresholdIt =
159         options.special_packages_thresholds.find(annotIt.GetOriginPackageName());
160     if (thresholdIt != options.special_packages_thresholds.end()) {
161       if (item_percent >= (thresholdIt->second / 100.f)) {
162         return true;
163       }
164     }
165   }
166   return item_percent >= (item_threshold / 100.f);
167 }
168 
169 // Returns true iff a method with the given metada should be included in the profile.
IncludeMethodInProfile(uint32_t max_aggregation_count,const FlattenProfileData::ItemMetadata & metadata,const BootImageOptions & options)170 static bool IncludeMethodInProfile(uint32_t max_aggregation_count,
171                                    const FlattenProfileData::ItemMetadata& metadata,
172                                    const BootImageOptions& options) {
173   return IncludeItemInProfile(max_aggregation_count, options.method_threshold, metadata, options);
174 }
175 
176 // Returns true iff a class with the given metada should be included in the profile.
IncludeClassInProfile(const TypeReference & type_ref,uint32_t max_aggregation_count,const FlattenProfileData::ItemMetadata & metadata,const BootImageOptions & options)177 static bool IncludeClassInProfile(const TypeReference& type_ref,
178                                   uint32_t max_aggregation_count,
179                                   const FlattenProfileData::ItemMetadata& metadata,
180                                   const BootImageOptions& options) {
181   uint32_t threshold = MaybeIsClassClean(type_ref)
182       ? options.image_class_clean_threshold
183       : options.image_class_threshold;
184   return IncludeItemInProfile(max_aggregation_count, threshold, metadata, options);
185 }
186 
187 // Returns true iff a class with the given metada should be included in the list of
188 // prelaoded classes.
IncludeInPreloadedClasses(const std::string & class_name,uint32_t max_aggregation_count,const FlattenProfileData::ItemMetadata & metadata,const BootImageOptions & options)189 static bool IncludeInPreloadedClasses(const std::string& class_name,
190                                       uint32_t max_aggregation_count,
191                                       const FlattenProfileData::ItemMetadata& metadata,
192                                       const BootImageOptions& options) {
193   bool denylisted = options.preloaded_classes_denylist.find(class_name) !=
194       options.preloaded_classes_denylist.end();
195   return !denylisted && IncludeItemInProfile(
196       max_aggregation_count, options.preloaded_class_threshold, metadata, options);
197 }
198 
GenerateBootImageProfile(const std::vector<std::unique_ptr<const DexFile>> & dex_files,const std::vector<std::string> & profile_files,const BootImageOptions & options,const std::string & boot_profile_out_path,const std::string & preloaded_classes_out_path)199 bool GenerateBootImageProfile(
200     const std::vector<std::unique_ptr<const DexFile>>& dex_files,
201     const std::vector<std::string>& profile_files,
202     const BootImageOptions& options,
203     const std::string& boot_profile_out_path,
204     const std::string& preloaded_classes_out_path) {
205   if (boot_profile_out_path.empty()) {
206     LOG(ERROR) << "No output file specified";
207     return false;
208   }
209 
210   bool generate_preloaded_classes = !preloaded_classes_out_path.empty();
211 
212   std::unique_ptr<FlattenProfileData> flattend_data(new FlattenProfileData());
213   for (const std::string& profile_file : profile_files) {
214     ProfileCompilationInfo profile(/*for_boot_image=*/ true);
215     if (!profile.Load(profile_file, /*clear_if_invalid=*/ false)) {
216       LOG(ERROR) << "Profile is not a valid: " << profile_file;
217       return false;
218     }
219     std::unique_ptr<FlattenProfileData> currentData = profile.ExtractProfileData(dex_files);
220     flattend_data->MergeData(*currentData);
221   }
222 
223   // We want the output sorted by the method/class name.
224   // So we use an intermediate map for that.
225   // There's no attempt to optimize this as it's not part of any critical path,
226   // and mostly executed on hosts.
227   SafeMap<std::string, FlattenProfileData::ItemMetadata> profile_methods;
228   SafeMap<std::string, FlattenProfileData::ItemMetadata> profile_classes;
229   SafeMap<std::string, FlattenProfileData::ItemMetadata> preloaded_classes;
230 
231   for (const auto& it : flattend_data->GetMethodData()) {
232     if (IncludeMethodInProfile(flattend_data->GetMaxAggregationForMethods(), it.second, options)) {
233       FlattenProfileData::ItemMetadata metadata(it.second);
234       if (options.upgrade_startup_to_hot
235           && ((metadata.GetFlags() & Hotness::Flag::kFlagStartup) != 0)) {
236         metadata.AddFlag(Hotness::Flag::kFlagHot);
237       }
238       profile_methods.Put(BootImageRepresentation(it.first), metadata);
239     }
240   }
241 
242   for (const auto& it : flattend_data->GetClassData()) {
243     const TypeReference& type_ref = it.first;
244     const FlattenProfileData::ItemMetadata& metadata = it.second;
245     if (IncludeClassInProfile(type_ref,
246             flattend_data->GetMaxAggregationForClasses(),
247             metadata,
248             options)) {
249       profile_classes.Put(BootImageRepresentation(it.first), it.second);
250     }
251     std::string preloaded_class_representation = PreloadedClassesRepresentation(it.first);
252     if (generate_preloaded_classes && IncludeInPreloadedClasses(
253             preloaded_class_representation,
254             flattend_data->GetMaxAggregationForClasses(),
255             metadata,
256             options)) {
257       preloaded_classes.Put(preloaded_class_representation, it.second);
258     }
259   }
260 
261   // Create the output content
262   std::string profile_content;
263   std::string preloaded_content;
264   for (const auto& it : profile_classes) {
265     profile_content += ClassToProfileFormat(it.first, it.second, options.append_package_use_list)
266         + "\n";
267   }
268   for (const auto& it : profile_methods) {
269     profile_content += MethodToProfileFormat(it.first, it.second, options.append_package_use_list)
270         + "\n";
271   }
272 
273   if (generate_preloaded_classes) {
274     for (const auto& it : preloaded_classes) {
275       preloaded_content +=
276           ClassToProfileFormat(it.first, it.second, options.append_package_use_list) + "\n";
277     }
278   }
279 
280   return android::base::WriteStringToFile(profile_content, boot_profile_out_path)
281       && (!generate_preloaded_classes
282           || android::base::WriteStringToFile(preloaded_content, preloaded_classes_out_path));
283 }
284 
285 }  // namespace art
286