blob: 75963674c9b047533cd16b05a6748d3c9ab40795 [file] [log] [blame]
Mathieu Chartier2f794552017-06-19 10:58:08 -07001/*
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
Calin Juravle0f7f4fc2020-05-11 20:16:50 -070017#include "boot_image_profile.h"
18
Mathieu Chartier2f794552017-06-19 10:58:08 -070019#include <memory>
20#include <set>
21
Calin Juravle0f7f4fc2020-05-11 20:16:50 -070022#include "android-base/file.h"
23#include "base/unix_file/fd_file.h"
Mathieu Chartier20f49922018-05-24 16:04:17 -070024#include "dex/class_accessor-inl.h"
Calin Juravle0f7f4fc2020-05-11 20:16:50 -070025#include "dex/descriptors_names.h"
David Sehr9e734c72018-01-04 17:56:19 -080026#include "dex/dex_file-inl.h"
David Sehr312f3b22018-03-19 08:39:26 -070027#include "dex/method_reference.h"
28#include "dex/type_reference.h"
David Sehr82d046e2018-04-23 08:14:19 -070029#include "profile/profile_compilation_info.h"
Mathieu Chartier2f794552017-06-19 10:58:08 -070030
31namespace art {
32
33using Hotness = ProfileCompilationInfo::MethodHotness;
34
Calin Juravle0f7f4fc2020-05-11 20:16:50 -070035static const std::string kMethodSep = "->"; // NOLINT [runtime/string] [4]
36static const std::string kPackageUseDelim = "@"; // NOLINT [runtime/string] [4]
37static constexpr char kMethodFlagStringHot = 'H';
38static constexpr char kMethodFlagStringStartup = 'S';
39static constexpr char kMethodFlagStringPostStartup = 'P';
40
41// Returns the type descriptor of the given reference.
42static 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.
48static 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.
61static std::string BootImageRepresentation(const TypeReference& ref) {
62 return GetTypeDescriptor(ref);
63}
64
65// Returns the class representation used in preloaded classes.
66static 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.
72static 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.
84static 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 return flags_string + method + extra;
104}
105
106// Converts a class representation to its final profile or preloaded classes format.
107static std::string ClassToProfileFormat(
108 const std::string& classString,
109 const FlattenProfileData::ItemMetadata& metadata,
110 bool output_package_use) {
111 std::string extra;
112 if (output_package_use) {
113 extra = kPackageUseDelim + GetPackageUseString(metadata);
114 }
115
116 return classString + extra;
117}
118
119// Tries to asses if the given type reference is a clean class.
120static bool MaybeIsClassClean(const TypeReference& ref) {
121 const dex::ClassDef* class_def = ref.dex_file->FindClassDef(ref.TypeIndex());
122 if (class_def == nullptr) {
123 return false;
124 }
125
126 ClassAccessor accessor(*ref.dex_file, *class_def);
127 for (auto& it : accessor.GetStaticFields()) {
128 if (!it.IsFinal()) {
129 // Not final static field will probably dirty the class.
130 return false;
131 }
132 }
133 for (auto& it : accessor.GetMethods()) {
134 uint32_t flags = it.GetAccessFlags();
135 if ((flags & kAccNative) != 0) {
136 // Native method will get dirtied.
137 return false;
138 }
139 if ((flags & kAccConstructor) != 0 && (flags & kAccStatic) != 0) {
140 // Class initializer, may get dirtied (not sure).
141 return false;
142 }
143 }
144
145 return true;
146}
147
148// Returns true iff the item should be included in the profile.
149// (i.e. it passes the given aggregation thresholds)
150static bool IncludeItemInProfile(uint32_t max_aggregation_count,
151 uint32_t item_threshold,
152 const FlattenProfileData::ItemMetadata& metadata,
153 const BootImageOptions& options) {
154 CHECK_NE(max_aggregation_count, 0u);
155 float item_percent = metadata.GetAnnotations().size() / static_cast<float>(max_aggregation_count);
156 for (const auto& annotIt : metadata.GetAnnotations()) {
157 const auto&thresholdIt =
158 options.special_packages_thresholds.find(annotIt.GetOriginPackageName());
159 if (thresholdIt != options.special_packages_thresholds.end()) {
160 if (item_percent >= (thresholdIt->second / 100.f)) {
161 return true;
162 }
163 }
164 }
165 return item_percent >= (item_threshold / 100.f);
166}
167
168// Returns true iff a method with the given metada should be included in the profile.
169static bool IncludeMethodInProfile(uint32_t max_aggregation_count,
170 const FlattenProfileData::ItemMetadata& metadata,
171 const BootImageOptions& options) {
172 return IncludeItemInProfile(max_aggregation_count, options.method_threshold, metadata, options);
173}
174
175// Returns true iff a class with the given metada should be included in the profile.
176static bool IncludeClassInProfile(const TypeReference& type_ref,
177 uint32_t max_aggregation_count,
178 const FlattenProfileData::ItemMetadata& metadata,
179 const BootImageOptions& options) {
180 uint32_t threshold = MaybeIsClassClean(type_ref)
181 ? options.image_class_clean_threshold
182 : options.image_class_threshold;
183 return IncludeItemInProfile(max_aggregation_count, threshold, metadata, options);
184}
185
186// Returns true iff a class with the given metada should be included in the list of
187// prelaoded classes.
Calin Juravle48030c42020-06-17 19:16:01 -0700188static bool IncludeInPreloadedClasses(const std::string& class_name,
189 uint32_t max_aggregation_count,
Calin Juravle0f7f4fc2020-05-11 20:16:50 -0700190 const FlattenProfileData::ItemMetadata& metadata,
191 const BootImageOptions& options) {
Orion Hodson3e8caeb2020-08-07 15:41:24 +0100192 bool denylisted = options.preloaded_classes_denylist.find(class_name) !=
193 options.preloaded_classes_denylist.end();
194 return !denylisted && IncludeItemInProfile(
Calin Juravle0f7f4fc2020-05-11 20:16:50 -0700195 max_aggregation_count, options.preloaded_class_threshold, metadata, options);
196}
197
198bool GenerateBootImageProfile(
Mathieu Chartier2f794552017-06-19 10:58:08 -0700199 const std::vector<std::unique_ptr<const DexFile>>& dex_files,
Calin Juravle0e02d612020-06-18 18:05:29 -0700200 const std::vector<std::string>& profile_files,
Mathieu Chartier2f794552017-06-19 10:58:08 -0700201 const BootImageOptions& options,
Calin Juravle0f7f4fc2020-05-11 20:16:50 -0700202 const std::string& boot_profile_out_path,
203 const std::string& preloaded_classes_out_path) {
Calin Juravled4e69922020-05-18 18:32:22 -0700204 if (boot_profile_out_path.empty()) {
205 LOG(ERROR) << "No output file specified";
206 return false;
207 }
208
209 bool generate_preloaded_classes = !preloaded_classes_out_path.empty();
210
Calin Juravle0e02d612020-06-18 18:05:29 -0700211 std::unique_ptr<FlattenProfileData> flattend_data(new FlattenProfileData());
212 for (const std::string& profile_file : profile_files) {
213 ProfileCompilationInfo profile;
214 if (!profile.Load(profile_file, /*clear_if_invalid=*/ false)) {
215 LOG(ERROR) << "Profile is not a valid: " << profile_file;
216 return false;
217 }
218 std::unique_ptr<FlattenProfileData> currentData = profile.ExtractProfileData(dex_files);
219 flattend_data->MergeData(*currentData);
220 }
Mathieu Chartier2f794552017-06-19 10:58:08 -0700221
Calin Juravle0f7f4fc2020-05-11 20:16:50 -0700222 // We want the output sorted by the method/class name.
223 // So we use an intermediate map for that.
224 // There's no attempt to optimize this as it's not part of any critical path,
225 // and mostly executed on hosts.
226 SafeMap<std::string, FlattenProfileData::ItemMetadata> profile_methods;
227 SafeMap<std::string, FlattenProfileData::ItemMetadata> profile_classes;
228 SafeMap<std::string, FlattenProfileData::ItemMetadata> preloaded_classes;
Mathieu Chartier2f794552017-06-19 10:58:08 -0700229
Calin Juravle0e02d612020-06-18 18:05:29 -0700230 for (const auto& it : flattend_data->GetMethodData()) {
231 if (IncludeMethodInProfile(flattend_data->GetMaxAggregationForMethods(), it.second, options)) {
Calin Juravle0f7f4fc2020-05-11 20:16:50 -0700232 FlattenProfileData::ItemMetadata metadata(it.second);
233 if (options.upgrade_startup_to_hot
234 && ((metadata.GetFlags() & Hotness::Flag::kFlagStartup) != 0)) {
235 metadata.AddFlag(Hotness::Flag::kFlagHot);
Mathieu Chartier2f794552017-06-19 10:58:08 -0700236 }
Calin Juravle0f7f4fc2020-05-11 20:16:50 -0700237 profile_methods.Put(BootImageRepresentation(it.first), metadata);
Mathieu Chartier2f794552017-06-19 10:58:08 -0700238 }
239 }
Calin Juravle0f7f4fc2020-05-11 20:16:50 -0700240
Calin Juravle0e02d612020-06-18 18:05:29 -0700241 for (const auto& it : flattend_data->GetClassData()) {
Calin Juravle0f7f4fc2020-05-11 20:16:50 -0700242 const TypeReference& type_ref = it.first;
243 const FlattenProfileData::ItemMetadata& metadata = it.second;
244 if (IncludeClassInProfile(type_ref,
Calin Juravle0e02d612020-06-18 18:05:29 -0700245 flattend_data->GetMaxAggregationForClasses(),
Calin Juravle0f7f4fc2020-05-11 20:16:50 -0700246 metadata,
247 options)) {
248 profile_classes.Put(BootImageRepresentation(it.first), it.second);
249 }
Calin Juravle48030c42020-06-17 19:16:01 -0700250 std::string preloaded_class_representation = PreloadedClassesRepresentation(it.first);
Calin Juravled4e69922020-05-18 18:32:22 -0700251 if (generate_preloaded_classes && IncludeInPreloadedClasses(
Calin Juravle48030c42020-06-17 19:16:01 -0700252 preloaded_class_representation,
Calin Juravle0e02d612020-06-18 18:05:29 -0700253 flattend_data->GetMaxAggregationForClasses(),
Calin Juravle0f7f4fc2020-05-11 20:16:50 -0700254 metadata,
255 options)) {
Calin Juravle48030c42020-06-17 19:16:01 -0700256 preloaded_classes.Put(preloaded_class_representation, it.second);
Calin Juravle0f7f4fc2020-05-11 20:16:50 -0700257 }
Mathieu Chartier2f794552017-06-19 10:58:08 -0700258 }
Calin Juravle0f7f4fc2020-05-11 20:16:50 -0700259
260 // Create the output content
261 std::string profile_content;
262 std::string preloaded_content;
263 for (const auto& it : profile_classes) {
264 profile_content += ClassToProfileFormat(it.first, it.second, options.append_package_use_list)
265 + "\n";
266 }
267 for (const auto& it : profile_methods) {
268 profile_content += MethodToProfileFormat(it.first, it.second, options.append_package_use_list)
269 + "\n";
270 }
Calin Juravled4e69922020-05-18 18:32:22 -0700271
272 if (generate_preloaded_classes) {
273 for (const auto& it : preloaded_classes) {
274 preloaded_content +=
275 ClassToProfileFormat(it.first, it.second, options.append_package_use_list) + "\n";
276 }
Calin Juravle0f7f4fc2020-05-11 20:16:50 -0700277 }
278
279 return android::base::WriteStringToFile(profile_content, boot_profile_out_path)
Calin Juravled4e69922020-05-18 18:32:22 -0700280 && (!generate_preloaded_classes
281 || android::base::WriteStringToFile(preloaded_content, preloaded_classes_out_path));
Mathieu Chartier2f794552017-06-19 10:58:08 -0700282}
283
284} // namespace art