1 /*
2 * Copyright (C) 2016 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 "profile_assistant.h"
18
19 #include <sstream>
20 #include <string>
21
22 #include "android-base/file.h"
23 #include "android-base/strings.h"
24 #include "art_method-inl.h"
25 #include "base/globals.h"
26 #include "base/unix_file/fd_file.h"
27 #include "base/utils.h"
28 #include "common_runtime_test.h"
29 #include "dex/descriptors_names.h"
30 #include "dex/dex_file_structs.h"
31 #include "dex/dex_instruction-inl.h"
32 #include "dex/dex_instruction_iterator.h"
33 #include "dex/type_reference.h"
34 #include "exec_utils.h"
35 #include "gtest/gtest.h"
36 #include "linear_alloc.h"
37 #include "mirror/class-inl.h"
38 #include "obj_ptr-inl.h"
39 #include "profile/profile_compilation_info.h"
40 #include "profile/profile_test_helper.h"
41 #include "profman/profman_result.h"
42 #include "scoped_thread_state_change-inl.h"
43
44 namespace art {
45
46 using TypeReferenceSet = std::set<TypeReference, TypeReferenceValueComparator>;
47
48 // TODO(calin): These tests share a lot with the ProfileCompilationInfo tests.
49 // we should introduce a better abstraction to extract the common parts.
50 class ProfileAssistantTest : public CommonRuntimeTest, public ProfileTestHelper {
51 public:
PostRuntimeCreate()52 void PostRuntimeCreate() override {
53 allocator_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
54
55 dex1 = BuildDex("location1", /*location_checksum=*/ 1, "LUnique1;", /*num_method_ids=*/ 10001);
56 dex2 = BuildDex("location2", /*location_checksum=*/ 2, "LUnique2;", /*num_method_ids=*/ 10002);
57 dex3 = BuildDex("location3", /*location_checksum=*/ 3, "LUnique3;", /*num_method_ids=*/ 10003);
58 dex4 = BuildDex("location4", /*location_checksum=*/ 4, "LUnique4;", /*num_method_ids=*/ 10004);
59
60 dex1_checksum_missmatch =
61 BuildDex("location1", /*location_checksum=*/ 12, "LUnique1;", /*num_method_ids=*/ 10001);
62 }
63
64 protected:
SetupProfile(const DexFile * dex_file1,const DexFile * dex_file2,uint16_t number_of_methods,uint16_t number_of_classes,const ScratchFile & profile,ProfileCompilationInfo * info,uint16_t start_method_index=0,bool reverse_dex_write_order=false)65 void SetupProfile(const DexFile* dex_file1,
66 const DexFile* dex_file2,
67 uint16_t number_of_methods,
68 uint16_t number_of_classes,
69 const ScratchFile& profile,
70 ProfileCompilationInfo* info,
71 uint16_t start_method_index = 0,
72 bool reverse_dex_write_order = false) {
73 for (uint16_t i = start_method_index; i < start_method_index + number_of_methods; i++) {
74 // reverse_dex_write_order controls the order in which the dex files will be added to
75 // the profile and thus written to disk.
76 std::vector<ProfileInlineCache> inline_caches =
77 GetTestInlineCaches(dex_file1, dex_file2, dex3);
78 Hotness::Flag flags =
79 static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagPostStartup);
80 if (reverse_dex_write_order) {
81 ASSERT_TRUE(AddMethod(info, dex_file2, i, inline_caches, flags));
82 ASSERT_TRUE(AddMethod(info, dex_file1, i, inline_caches, flags));
83 } else {
84 ASSERT_TRUE(AddMethod(info, dex_file1, i, inline_caches, flags));
85 ASSERT_TRUE(AddMethod(info, dex_file2, i, inline_caches, flags));
86 }
87 }
88 for (uint16_t i = 0; i < number_of_classes; i++) {
89 ASSERT_TRUE(AddClass(info, dex_file1, dex::TypeIndex(i)));
90 }
91
92 ASSERT_TRUE(info->Save(GetFd(profile)));
93 ASSERT_EQ(0, profile.GetFile()->Flush());
94 }
95
SetupBasicProfile(const DexFile * dex,const std::vector<uint32_t> & hot_methods,const std::vector<uint32_t> & startup_methods,const std::vector<uint32_t> & post_startup_methods,const ScratchFile & profile,ProfileCompilationInfo * info)96 void SetupBasicProfile(const DexFile* dex,
97 const std::vector<uint32_t>& hot_methods,
98 const std::vector<uint32_t>& startup_methods,
99 const std::vector<uint32_t>& post_startup_methods,
100 const ScratchFile& profile,
101 ProfileCompilationInfo* info) {
102 for (uint32_t idx : hot_methods) {
103 AddMethod(info, dex, idx, Hotness::kFlagHot);
104 }
105 for (uint32_t idx : startup_methods) {
106 AddMethod(info, dex, idx, Hotness::kFlagStartup);
107 }
108 for (uint32_t idx : post_startup_methods) {
109 AddMethod(info, dex, idx, Hotness::kFlagPostStartup);
110 }
111 ASSERT_TRUE(info->Save(GetFd(profile)));
112 ASSERT_EQ(0, profile.GetFile()->Flush());
113 }
114
115 // The dex1_substitute can be used to replace the default dex1 file.
GetTestInlineCaches(const DexFile * dex_file1,const DexFile * dex_file2,const DexFile * dex_file3)116 std::vector<ProfileInlineCache> GetTestInlineCaches(
117 const DexFile* dex_file1, const DexFile* dex_file2, const DexFile* dex_file3) {
118 std::vector<ProfileInlineCache> inline_caches;
119 // Monomorphic
120 for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
121 std::vector<TypeReference> types = {TypeReference(dex_file1, dex::TypeIndex(0))};
122 inline_caches.push_back(ProfileInlineCache(dex_pc, /* missing_types*/ false, types));
123 }
124 // Polymorphic
125 for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
126 std::vector<TypeReference> types = {
127 TypeReference(dex_file1, dex::TypeIndex(0)),
128 TypeReference(dex_file2, dex::TypeIndex(1)),
129 TypeReference(dex_file3, dex::TypeIndex(2))};
130 inline_caches.push_back(ProfileInlineCache(dex_pc, /* missing_types*/ false, types));
131 }
132 // Megamorphic
133 for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
134 // we need 5 types to make the cache megamorphic
135 std::vector<TypeReference> types = {
136 TypeReference(dex_file1, dex::TypeIndex(0)),
137 TypeReference(dex_file1, dex::TypeIndex(1)),
138 TypeReference(dex_file1, dex::TypeIndex(2)),
139 TypeReference(dex_file1, dex::TypeIndex(3)),
140 TypeReference(dex_file1, dex::TypeIndex(4))};
141 inline_caches.push_back(ProfileInlineCache(dex_pc, /* missing_types*/ false, types));
142 }
143 // Missing types
144 for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
145 std::vector<TypeReference> types;
146 inline_caches.push_back(ProfileInlineCache(dex_pc, /* missing_types*/ true, types));
147 }
148
149 return inline_caches;
150 }
151
GetFd(const ScratchFile & file) const152 int GetFd(const ScratchFile& file) const {
153 return static_cast<int>(file.GetFd());
154 }
155
CheckProfileInfo(ScratchFile & file,const ProfileCompilationInfo & info)156 void CheckProfileInfo(ScratchFile& file, const ProfileCompilationInfo& info) {
157 ProfileCompilationInfo file_info;
158 ASSERT_TRUE(file_info.Load(GetFd(file)));
159 ASSERT_TRUE(file_info.Equals(info));
160 }
161
GetProfmanCmd()162 std::string GetProfmanCmd() {
163 std::string file_path = GetArtBinDir() + "/profman";
164 if (kIsDebugBuild) {
165 file_path += "d";
166 }
167 EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
168 return file_path;
169 }
170
171 // Runs test with given arguments.
ProcessProfiles(const std::vector<int> & profiles_fd,int reference_profile_fd,const std::vector<const std::string> & extra_args=std::vector<const std::string> ())172 int ProcessProfiles(
173 const std::vector<int>& profiles_fd,
174 int reference_profile_fd,
175 const std::vector<const std::string>& extra_args = std::vector<const std::string>()) {
176 std::string profman_cmd = GetProfmanCmd();
177 std::vector<std::string> argv_str;
178 argv_str.push_back(profman_cmd);
179 for (size_t k = 0; k < profiles_fd.size(); k++) {
180 argv_str.push_back("--profile-file-fd=" + std::to_string(profiles_fd[k]));
181 }
182 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile_fd));
183 argv_str.insert(argv_str.end(), extra_args.begin(), extra_args.end());
184
185 std::string error;
186 return ExecAndReturnCode(argv_str, &error);
187 }
188
GenerateTestProfile(const std::string & filename)189 bool GenerateTestProfile(const std::string& filename) {
190 std::string profman_cmd = GetProfmanCmd();
191 std::vector<std::string> argv_str;
192 argv_str.push_back(profman_cmd);
193 argv_str.push_back("--generate-test-profile=" + filename);
194 std::string error;
195 return ExecAndReturnCode(argv_str, &error);
196 }
197
GenerateTestProfileWithInputDex(const std::string & filename)198 bool GenerateTestProfileWithInputDex(const std::string& filename) {
199 std::string profman_cmd = GetProfmanCmd();
200 std::vector<std::string> argv_str;
201 argv_str.push_back(profman_cmd);
202 argv_str.push_back("--generate-test-profile=" + filename);
203 argv_str.push_back("--generate-test-profile-seed=0");
204 argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]);
205 argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]);
206 std::string error;
207 return ExecAndReturnCode(argv_str, &error);
208 }
209
CreateProfile(const std::string & profile_file_contents,const std::string & filename,const std::string & dex_location,bool for_boot_image=false)210 bool CreateProfile(const std::string& profile_file_contents,
211 const std::string& filename,
212 const std::string& dex_location,
213 bool for_boot_image = false) {
214 ScratchFile class_names_file;
215 File* file = class_names_file.GetFile();
216 EXPECT_TRUE(file->WriteFully(profile_file_contents.c_str(), profile_file_contents.length()));
217 EXPECT_EQ(0, file->Flush());
218 std::string profman_cmd = GetProfmanCmd();
219 std::vector<std::string> argv_str;
220 argv_str.push_back(profman_cmd);
221 argv_str.push_back(for_boot_image ? "--output-profile-type=boot" : "--output-profile-type=app");
222 argv_str.push_back("--create-profile-from=" + class_names_file.GetFilename());
223 argv_str.push_back("--reference-profile-file=" + filename);
224 argv_str.push_back("--apk=" + dex_location);
225 argv_str.push_back("--dex-location=" + dex_location);
226 std::string error;
227 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0) << error;
228 return true;
229 }
230
RunProfman(const std::string & filename,std::vector<std::string> & extra_args,std::string * output,std::string_view target_apk)231 bool RunProfman(const std::string& filename,
232 std::vector<std::string>& extra_args,
233 std::string* output,
234 std::string_view target_apk) {
235 ScratchFile output_file;
236 std::string profman_cmd = GetProfmanCmd();
237 std::vector<std::string> argv_str;
238 argv_str.push_back(profman_cmd);
239 argv_str.insert(argv_str.end(), extra_args.begin(), extra_args.end());
240 argv_str.push_back("--profile-file=" + filename);
241 argv_str.push_back(std::string("--apk=").append(target_apk));
242 argv_str.push_back(std::string("--dex-location=").append(target_apk));
243 argv_str.push_back("--dump-output-to-fd=" + std::to_string(GetFd(output_file)));
244 std::string error;
245 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0) << error;
246 File* file = output_file.GetFile();
247 EXPECT_EQ(0, file->Flush());
248 int64_t length = file->GetLength();
249 std::unique_ptr<char[]> buf(new char[length]);
250 EXPECT_EQ(file->Read(buf.get(), length, 0), length);
251 *output = std::string(buf.get(), length);
252 return true;
253 }
254
DumpClassesAndMethods(const std::string & filename,std::string * file_contents,std::optional<const std::string_view> target=std::nullopt)255 bool DumpClassesAndMethods(const std::string& filename,
256 std::string* file_contents,
257 std::optional<const std::string_view> target = std::nullopt) {
258 std::vector<std::string> extra_args;
259 extra_args.push_back("--dump-classes-and-methods");
260 return RunProfman(
261 filename, extra_args, file_contents, target.value_or(GetLibCoreDexFileNames()[0]));
262 }
263
DumpOnly(const std::string & filename,std::string * file_contents)264 bool DumpOnly(const std::string& filename, std::string* file_contents) {
265 std::vector<std::string> extra_args;
266 extra_args.push_back("--dump-only");
267 return RunProfman(filename, extra_args, file_contents, GetLibCoreDexFileNames()[0]);
268 }
269
CreateAndDump(const std::string & input_file_contents,std::string * output_file_contents,const std::optional<const std::string> & target=std::nullopt)270 bool CreateAndDump(const std::string& input_file_contents,
271 std::string* output_file_contents,
272 const std::optional<const std::string>& target = std::nullopt) {
273 ScratchFile profile_file;
274 EXPECT_TRUE(CreateProfile(input_file_contents,
275 profile_file.GetFilename(),
276 target.value_or(GetLibCoreDexFileNames()[0])));
277 EXPECT_TRUE(DumpClassesAndMethods(profile_file.GetFilename(), output_file_contents, target));
278 return true;
279 }
280
GetClass(ScopedObjectAccess & soa,jobject class_loader,const std::string & clazz)281 ObjPtr<mirror::Class> GetClass(ScopedObjectAccess& soa,
282 jobject class_loader,
283 const std::string& clazz) REQUIRES_SHARED(Locks::mutator_lock_) {
284 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
285 StackHandleScope<1> hs(soa.Self());
286 Handle<mirror::ClassLoader> h_loader(hs.NewHandle(
287 ObjPtr<mirror::ClassLoader>::DownCast(soa.Self()->DecodeJObject(class_loader))));
288 return class_linker->FindClass(soa.Self(), clazz.c_str(), h_loader);
289 }
290
GetVirtualMethod(jobject class_loader,const std::string & clazz,const std::string & name)291 ArtMethod* GetVirtualMethod(jobject class_loader,
292 const std::string& clazz,
293 const std::string& name) {
294 ScopedObjectAccess soa(Thread::Current());
295 ObjPtr<mirror::Class> klass = GetClass(soa, class_loader, clazz);
296 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
297 const auto pointer_size = class_linker->GetImagePointerSize();
298 ArtMethod* method = nullptr;
299 for (auto& m : klass->GetVirtualMethods(pointer_size)) {
300 if (name == m.GetName()) {
301 EXPECT_TRUE(method == nullptr);
302 method = &m;
303 }
304 }
305 return method;
306 }
307
MakeTypeReference(ObjPtr<mirror::Class> klass)308 static TypeReference MakeTypeReference(ObjPtr<mirror::Class> klass)
309 REQUIRES_SHARED(Locks::mutator_lock_) {
310 return TypeReference(&klass->GetDexFile(), klass->GetDexTypeIndex());
311 }
312
313 // Find the first dex-pc in the given method after 'start_pc' (if given) which
314 // contains a call to any method of 'klass'. If 'start_pc' is not given we
315 // will search from the first dex-pc.
GetDexPcOfCallTo(ArtMethod * method,Handle<mirror::Class> klass,std::optional<uint32_t> start_pc=std::nullopt)316 uint16_t GetDexPcOfCallTo(ArtMethod* method,
317 Handle<mirror::Class> klass,
318 std::optional<uint32_t> start_pc = std::nullopt)
319 REQUIRES_SHARED(Locks::mutator_lock_) {
320 const DexFile* dex_file = method->GetDexFile();
321 for (const DexInstructionPcPair& inst :
322 CodeItemInstructionAccessor(*dex_file, method->GetCodeItem())) {
323 if (start_pc && inst.DexPc() <= *start_pc) {
324 continue;
325 } else if (inst->IsInvoke()) {
326 const dex::MethodId& method_id = dex_file->GetMethodId(inst->VRegB());
327 std::string_view desc(
328 dex_file->GetTypeDescriptor(dex_file->GetTypeId(method_id.class_idx_)));
329 std::string scratch;
330 if (desc == klass->GetDescriptor(&scratch)) {
331 return inst.DexPc();
332 }
333 }
334 }
335 EXPECT_TRUE(false) << "Unable to find dex-pc in " << method->PrettyMethod() << " for call to "
336 << klass->PrettyClass()
337 << " after dexpc: " << (start_pc ? static_cast<int64_t>(*start_pc) : -1);
338 return -1;
339 }
340
AssertInlineCaches(ArtMethod * method,uint16_t dex_pc,const TypeReferenceSet & expected_classes,const ProfileCompilationInfo & info,bool is_megamorphic,bool is_missing_types)341 void AssertInlineCaches(ArtMethod* method,
342 uint16_t dex_pc,
343 const TypeReferenceSet& expected_classes,
344 const ProfileCompilationInfo& info,
345 bool is_megamorphic,
346 bool is_missing_types)
347 REQUIRES_SHARED(Locks::mutator_lock_) {
348 ProfileCompilationInfo::MethodHotness hotness =
349 info.GetMethodHotness(MethodReference(method->GetDexFile(), method->GetDexMethodIndex()));
350 ASSERT_TRUE(hotness.IsHot());
351 const ProfileCompilationInfo::InlineCacheMap* inline_caches = hotness.GetInlineCacheMap();
352 ASSERT_TRUE(inline_caches->find(dex_pc) != inline_caches->end());
353 AssertInlineCaches(expected_classes,
354 info,
355 method,
356 inline_caches->find(dex_pc)->second,
357 is_megamorphic,
358 is_missing_types);
359 }
AssertInlineCaches(ArtMethod * method,const TypeReferenceSet & expected_classes,const ProfileCompilationInfo & info,bool is_megamorphic,bool is_missing_types)360 void AssertInlineCaches(ArtMethod* method,
361 const TypeReferenceSet& expected_classes,
362 const ProfileCompilationInfo& info,
363 bool is_megamorphic,
364 bool is_missing_types)
365 REQUIRES_SHARED(Locks::mutator_lock_) {
366 ProfileCompilationInfo::MethodHotness hotness =
367 info.GetMethodHotness(MethodReference(method->GetDexFile(), method->GetDexMethodIndex()));
368 ASSERT_TRUE(hotness.IsHot());
369 const ProfileCompilationInfo::InlineCacheMap* inline_caches = hotness.GetInlineCacheMap();
370 ASSERT_EQ(inline_caches->size(), 1u);
371 AssertInlineCaches(expected_classes,
372 info,
373 method,
374 inline_caches->begin()->second,
375 is_megamorphic,
376 is_missing_types);
377 }
378
AssertInlineCaches(const TypeReferenceSet & expected_clases,const ProfileCompilationInfo & info,ArtMethod * method,const ProfileCompilationInfo::DexPcData & dex_pc_data,bool is_megamorphic,bool is_missing_types)379 void AssertInlineCaches(const TypeReferenceSet& expected_clases,
380 const ProfileCompilationInfo& info,
381 ArtMethod* method,
382 const ProfileCompilationInfo::DexPcData& dex_pc_data,
383 bool is_megamorphic,
384 bool is_missing_types)
385 REQUIRES_SHARED(Locks::mutator_lock_) {
386 ASSERT_EQ(dex_pc_data.is_megamorphic, is_megamorphic);
387 ASSERT_EQ(dex_pc_data.is_missing_types, is_missing_types);
388 ASSERT_EQ(expected_clases.size(), dex_pc_data.classes.size());
389 const DexFile* dex_file = method->GetDexFile();
390 size_t found = 0;
391 for (const TypeReference& type_ref : expected_clases) {
392 if (type_ref.dex_file == dex_file) {
393 CHECK_LT(type_ref.TypeIndex().index_, dex_file->NumTypeIds());
394 for (dex::TypeIndex type_index : dex_pc_data.classes) {
395 ASSERT_TRUE(type_index.IsValid());
396 if (type_ref.TypeIndex() == type_index) {
397 ++found;
398 }
399 }
400 } else {
401 // Match by descriptor.
402 const char* expected_descriptor =
403 type_ref.dex_file->GetTypeDescriptor(type_ref.TypeIndex());
404 for (dex::TypeIndex type_index : dex_pc_data.classes) {
405 ASSERT_TRUE(type_index.IsValid());
406 const char* descriptor = info.GetTypeDescriptor(dex_file, type_index);
407 if (strcmp(expected_descriptor, descriptor) == 0) {
408 ++found;
409 }
410 }
411 }
412 }
413
414 ASSERT_EQ(expected_clases.size(), found);
415 }
416
CheckCompilationMethodPercentChange(uint16_t methods_in_cur_profile,uint16_t methods_in_ref_profile,const std::vector<const std::string> & extra_args=std::vector<const std::string> ())417 int CheckCompilationMethodPercentChange(uint16_t methods_in_cur_profile,
418 uint16_t methods_in_ref_profile,
419 const std::vector<const std::string>& extra_args =
420 std::vector<const std::string>()) {
421 ScratchFile profile;
422 ScratchFile reference_profile;
423 std::vector<int> profile_fds({ GetFd(profile)});
424 int reference_profile_fd = GetFd(reference_profile);
425 std::vector<uint32_t> hot_methods_cur;
426 std::vector<uint32_t> hot_methods_ref;
427 std::vector<uint32_t> empty_vector;
428 for (size_t i = 0; i < methods_in_cur_profile; ++i) {
429 hot_methods_cur.push_back(i);
430 }
431 for (size_t i = 0; i < methods_in_ref_profile; ++i) {
432 hot_methods_ref.push_back(i);
433 }
434 ProfileCompilationInfo info1;
435 SetupBasicProfile(dex1, hot_methods_cur, empty_vector, empty_vector,
436 profile, &info1);
437 ProfileCompilationInfo info2;
438 SetupBasicProfile(dex1, hot_methods_ref, empty_vector, empty_vector,
439 reference_profile, &info2);
440 return ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
441 }
442
CheckCompilationClassPercentChange(uint16_t classes_in_cur_profile,uint16_t classes_in_ref_profile,const std::vector<const std::string> & extra_args=std::vector<const std::string> ())443 int CheckCompilationClassPercentChange(uint16_t classes_in_cur_profile,
444 uint16_t classes_in_ref_profile,
445 const std::vector<const std::string>& extra_args =
446 std::vector<const std::string>()) {
447 uint16_t max_classes = std::max(classes_in_cur_profile, classes_in_ref_profile);
448 const DexFile* dex1_x = BuildDex("location1_x",
449 /*location_checksum=*/ 0x101,
450 "LUnique1_x;",
451 /*num_method_ids=*/ 0,
452 max_classes);
453 const DexFile* dex2_x = BuildDex("location2_x",
454 /*location_checksum=*/ 0x102,
455 "LUnique2_x;",
456 /*num_method_ids=*/ 0,
457 max_classes);
458
459 ScratchFile profile;
460 ScratchFile reference_profile;
461
462 std::vector<int> profile_fds({ GetFd(profile)});
463 int reference_profile_fd = GetFd(reference_profile);
464
465 ProfileCompilationInfo info1;
466 SetupProfile(dex1_x, dex2_x, 0, classes_in_cur_profile, profile, &info1);
467 ProfileCompilationInfo info2;
468 SetupProfile(dex1_x, dex2_x, 0, classes_in_ref_profile, reference_profile, &info2);
469 return ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
470 }
471
472 std::unique_ptr<ArenaAllocator> allocator_;
473
474 const DexFile* dex1;
475 const DexFile* dex2;
476 const DexFile* dex3;
477 const DexFile* dex4;
478 const DexFile* dex1_checksum_missmatch;
479 };
480
TEST_F(ProfileAssistantTest,AdviseCompilationEmptyReferences)481 TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) {
482 ScratchFile profile1;
483 ScratchFile profile2;
484 ScratchFile reference_profile;
485
486 std::vector<int> profile_fds({
487 GetFd(profile1),
488 GetFd(profile2)});
489 int reference_profile_fd = GetFd(reference_profile);
490
491 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
492 ProfileCompilationInfo info1;
493 SetupProfile(dex1, dex2, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
494 ProfileCompilationInfo info2;
495 SetupProfile(dex3, dex4, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
496
497 // We should advise compilation.
498 ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
499 // The resulting compilation info must be equal to the merge of the inputs.
500 ProfileCompilationInfo result;
501 ASSERT_TRUE(result.Load(reference_profile_fd));
502
503 ProfileCompilationInfo expected;
504 ASSERT_TRUE(expected.MergeWith(info1));
505 ASSERT_TRUE(expected.MergeWith(info2));
506 ASSERT_TRUE(expected.Equals(result));
507
508 // The information from profiles must remain the same.
509 CheckProfileInfo(profile1, info1);
510 CheckProfileInfo(profile2, info2);
511 }
512
513 // TODO(calin): Add more tests for classes.
TEST_F(ProfileAssistantTest,AdviseCompilationEmptyReferencesBecauseOfClasses)514 TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferencesBecauseOfClasses) {
515 const uint16_t kNumberOfClassesToEnableCompilation = 100;
516 const DexFile* dex1_100 = BuildDex("location1_100",
517 /*location_checksum=*/ 101,
518 "LUnique1_100;",
519 /*num_method_ids=*/ 0,
520 /*num_class_ids=*/ 100);
521 const DexFile* dex2_100 = BuildDex("location2_100",
522 /*location_checksum=*/ 102,
523 "LUnique2_100;",
524 /*num_method_ids=*/ 0,
525 /*num_class_ids=*/ 100);
526
527 ScratchFile profile1;
528 ScratchFile reference_profile;
529
530 std::vector<int> profile_fds({
531 GetFd(profile1)});
532 int reference_profile_fd = GetFd(reference_profile);
533
534 ProfileCompilationInfo info1;
535 SetupProfile(dex1_100, dex2_100, 0, kNumberOfClassesToEnableCompilation, profile1, &info1);
536
537 // We should advise compilation.
538 ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
539 // The resulting compilation info must be equal to the merge of the inputs.
540 ProfileCompilationInfo result;
541 ASSERT_TRUE(result.Load(reference_profile_fd));
542
543 ProfileCompilationInfo expected;
544 ASSERT_TRUE(expected.MergeWith(info1));
545 ASSERT_TRUE(expected.Equals(result));
546
547 // The information from profiles must remain the same.
548 CheckProfileInfo(profile1, info1);
549 }
550
TEST_F(ProfileAssistantTest,AdviseCompilationNonEmptyReferences)551 TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) {
552 ScratchFile profile1;
553 ScratchFile profile2;
554 ScratchFile reference_profile;
555
556 std::vector<int> profile_fds({
557 GetFd(profile1),
558 GetFd(profile2)});
559 int reference_profile_fd = GetFd(reference_profile);
560
561 // The new profile info will contain the methods with indices 0-100.
562 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
563 ProfileCompilationInfo info1;
564 SetupProfile(dex1, dex2, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
565 ProfileCompilationInfo info2;
566 SetupProfile(dex3, dex4, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
567
568
569 // The reference profile info will contain the methods with indices 50-150.
570 const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
571 ProfileCompilationInfo reference_info;
572 SetupProfile(dex1, dex2, kNumberOfMethodsAlreadyCompiled, 0, reference_profile,
573 &reference_info, kNumberOfMethodsToEnableCompilation / 2);
574
575 // We should advise compilation.
576 ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
577
578 // The resulting compilation info must be equal to the merge of the inputs
579 ProfileCompilationInfo result;
580 ASSERT_TRUE(result.Load(reference_profile_fd));
581
582 ProfileCompilationInfo expected;
583 ASSERT_TRUE(expected.MergeWith(info1));
584 ASSERT_TRUE(expected.MergeWith(info2));
585 ASSERT_TRUE(expected.MergeWith(reference_info));
586 ASSERT_TRUE(expected.Equals(result));
587
588 // The information from profiles must remain the same.
589 CheckProfileInfo(profile1, info1);
590 CheckProfileInfo(profile2, info2);
591 }
592
TEST_F(ProfileAssistantTest,DoNotAdviseCompilationEmptyProfile)593 TEST_F(ProfileAssistantTest, DoNotAdviseCompilationEmptyProfile) {
594 ScratchFile profile1;
595 ScratchFile profile2;
596 ScratchFile reference_profile;
597
598 std::vector<int> profile_fds({
599 GetFd(profile1),
600 GetFd(profile2)});
601 int reference_profile_fd = GetFd(reference_profile);
602
603 ProfileCompilationInfo info1;
604 SetupProfile(dex1, dex2, /*number_of_methods=*/ 0, /*number_of_classes*/ 0, profile1, &info1);
605 ProfileCompilationInfo info2;
606 SetupProfile(dex3, dex4, /*number_of_methods=*/ 0, /*number_of_classes*/ 0, profile2, &info2);
607
608 // We should not advise compilation.
609 ASSERT_EQ(ProfmanResult::kSkipCompilationEmptyProfiles,
610 ProcessProfiles(profile_fds, reference_profile_fd));
611
612 // The information from profiles must remain the same.
613 ProfileCompilationInfo file_info1;
614 ASSERT_TRUE(file_info1.Load(GetFd(profile1)));
615 ASSERT_TRUE(file_info1.Equals(info1));
616
617 ProfileCompilationInfo file_info2;
618 ASSERT_TRUE(file_info2.Load(GetFd(profile2)));
619 ASSERT_TRUE(file_info2.Equals(info2));
620
621 // Reference profile files must remain empty.
622 ASSERT_EQ(0, reference_profile.GetFile()->GetLength());
623
624 // The information from profiles must remain the same.
625 CheckProfileInfo(profile1, info1);
626 CheckProfileInfo(profile2, info2);
627 }
628
TEST_F(ProfileAssistantTest,DoNotAdviseCompilation)629 TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) {
630 ScratchFile profile1;
631 ScratchFile profile2;
632 ScratchFile reference_profile;
633
634 std::vector<int> profile_fds({
635 GetFd(profile1),
636 GetFd(profile2)});
637 int reference_profile_fd = GetFd(reference_profile);
638
639 const uint16_t kNumberOfMethodsToSkipCompilation = 24; // Threshold is 100.
640 ProfileCompilationInfo info1;
641 SetupProfile(dex1, dex2, kNumberOfMethodsToSkipCompilation, 0, profile1, &info1);
642 ProfileCompilationInfo info2;
643 SetupProfile(dex3, dex4, kNumberOfMethodsToSkipCompilation, 0, profile2, &info2);
644
645 // We should not advise compilation.
646 ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
647 ProcessProfiles(profile_fds, reference_profile_fd));
648
649 // The information from profiles must remain the same.
650 ProfileCompilationInfo file_info1;
651 ASSERT_TRUE(file_info1.Load(GetFd(profile1)));
652 ASSERT_TRUE(file_info1.Equals(info1));
653
654 ProfileCompilationInfo file_info2;
655 ASSERT_TRUE(file_info2.Load(GetFd(profile2)));
656 ASSERT_TRUE(file_info2.Equals(info2));
657
658 // Reference profile files must remain empty.
659 ASSERT_EQ(0, reference_profile.GetFile()->GetLength());
660
661 // The information from profiles must remain the same.
662 CheckProfileInfo(profile1, info1);
663 CheckProfileInfo(profile2, info2);
664 }
665
TEST_F(ProfileAssistantTest,DoNotAdviseCompilationMethodPercentage)666 TEST_F(ProfileAssistantTest, DoNotAdviseCompilationMethodPercentage) {
667 const uint16_t kNumberOfMethodsInRefProfile = 6000;
668 const uint16_t kNumberOfMethodsInCurProfile = 6100; // Threshold is 2%.
669 std::vector<const std::string> extra_args({"--min-new-methods-percent-change=2"});
670
671 // We should not advise compilation.
672 ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
673 CheckCompilationMethodPercentChange(
674 kNumberOfMethodsInCurProfile, kNumberOfMethodsInRefProfile, extra_args));
675 }
676
TEST_F(ProfileAssistantTest,ShouldAdviseCompilationMethodPercentage)677 TEST_F(ProfileAssistantTest, ShouldAdviseCompilationMethodPercentage) {
678 const uint16_t kNumberOfMethodsInRefProfile = 6000;
679 const uint16_t kNumberOfMethodsInCurProfile = 6200; // Threshold is 2%.
680 std::vector<const std::string> extra_args({"--min-new-methods-percent-change=2"});
681
682 // We should advise compilation.
683 ASSERT_EQ(ProfmanResult::kCompile,
684 CheckCompilationMethodPercentChange(
685 kNumberOfMethodsInCurProfile, kNumberOfMethodsInRefProfile, extra_args));
686 }
687
TEST_F(ProfileAssistantTest,DoNotAdviseCompilationClassPercentage)688 TEST_F(ProfileAssistantTest, DoNotAdviseCompilationClassPercentage) {
689 const uint16_t kNumberOfClassesInRefProfile = 6000;
690 const uint16_t kNumberOfClassesInCurProfile = 6110; // Threshold is 2%.
691 std::vector<const std::string> extra_args({"--min-new-classes-percent-change=2"});
692
693 // We should not advise compilation.
694 ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
695 CheckCompilationClassPercentChange(
696 kNumberOfClassesInCurProfile, kNumberOfClassesInRefProfile, extra_args));
697 }
698
TEST_F(ProfileAssistantTest,ShouldAdviseCompilationClassPercentage)699 TEST_F(ProfileAssistantTest, ShouldAdviseCompilationClassPercentage) {
700 const uint16_t kNumberOfClassesInRefProfile = 6000;
701 const uint16_t kNumberOfClassesInCurProfile = 6120; // Threshold is 2%.
702 std::vector<const std::string> extra_args({"--min-new-classes-percent-change=2"});
703
704 // We should advise compilation.
705 ASSERT_EQ(ProfmanResult::kCompile,
706 CheckCompilationClassPercentChange(
707 kNumberOfClassesInCurProfile, kNumberOfClassesInRefProfile, extra_args));
708 }
709
TEST_F(ProfileAssistantTest,FailProcessingBecauseOfProfiles)710 TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) {
711 ScratchFile profile1;
712 ScratchFile profile2;
713 ScratchFile reference_profile;
714
715 std::vector<int> profile_fds({
716 GetFd(profile1),
717 GetFd(profile2)});
718 int reference_profile_fd = GetFd(reference_profile);
719
720 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
721 // Assign different hashes for the same dex file. This will make merging of information to fail.
722 ProfileCompilationInfo info1;
723 SetupProfile(dex1, dex2, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
724 ProfileCompilationInfo info2;
725 SetupProfile(
726 dex1_checksum_missmatch, dex2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
727
728 // We should fail processing.
729 ASSERT_EQ(ProfmanResult::kErrorBadProfiles, ProcessProfiles(profile_fds, reference_profile_fd));
730
731 // The information from profiles must remain the same.
732 CheckProfileInfo(profile1, info1);
733 CheckProfileInfo(profile2, info2);
734
735 // Reference profile files must still remain empty.
736 ASSERT_EQ(0, reference_profile.GetFile()->GetLength());
737 }
738
TEST_F(ProfileAssistantTest,FailProcessingBecauseOfReferenceProfiles)739 TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) {
740 ScratchFile profile1;
741 ScratchFile reference_profile;
742
743 std::vector<int> profile_fds({
744 GetFd(profile1)});
745 int reference_profile_fd = GetFd(reference_profile);
746
747 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
748 // Assign different hashes for the same dex file. This will make merging of information to fail.
749 ProfileCompilationInfo info1;
750 SetupProfile(dex1, dex2, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
751 ProfileCompilationInfo reference_info;
752 SetupProfile(dex1_checksum_missmatch,
753 dex2,
754 kNumberOfMethodsToEnableCompilation,
755 0,
756 reference_profile,
757 &reference_info);
758
759 // We should not advise compilation.
760 ASSERT_EQ(ProfmanResult::kErrorBadProfiles, ProcessProfiles(profile_fds, reference_profile_fd));
761
762 // The information from profiles must remain the same.
763 CheckProfileInfo(profile1, info1);
764 }
765
TEST_F(ProfileAssistantTest,TestProfileGeneration)766 TEST_F(ProfileAssistantTest, TestProfileGeneration) {
767 ScratchFile profile;
768 // Generate a test profile.
769 GenerateTestProfile(profile.GetFilename());
770
771 // Verify that the generated profile is valid and can be loaded.
772 ProfileCompilationInfo info;
773 ASSERT_TRUE(info.Load(GetFd(profile)));
774 }
775
TEST_F(ProfileAssistantTest,TestProfileGenerationWithIndexDex)776 TEST_F(ProfileAssistantTest, TestProfileGenerationWithIndexDex) {
777 ScratchFile profile;
778 // Generate a test profile passing in a dex file as reference.
779 GenerateTestProfileWithInputDex(profile.GetFilename());
780
781 // Verify that the generated profile is valid and can be loaded.
782 ProfileCompilationInfo info;
783 ASSERT_TRUE(info.Load(GetFd(profile)));
784 }
785
TEST_F(ProfileAssistantTest,TestProfileCreationAllMatch)786 TEST_F(ProfileAssistantTest, TestProfileCreationAllMatch) {
787 // Class names put here need to be in sorted order.
788 std::vector<std::string> class_names = {
789 "HLjava/lang/Object;-><init>()V",
790 "Ljava/lang/Comparable;",
791 "Ljava/lang/Math;",
792 "Ljava/lang/Object;",
793 "SPLjava/lang/Comparable;->compareTo(Ljava/lang/Object;)I",
794 "[[[[[[[[I", // No `TypeId`s in core-oj with this many array dimensions,
795 "[[[[[[[[Ljava/lang/Object;", // "extra descriptors" shall be used for these array classes.
796 };
797 std::string file_contents;
798 for (std::string& class_name : class_names) {
799 file_contents += class_name + std::string("\n");
800 }
801 std::string output_file_contents;
802 ASSERT_TRUE(CreateAndDump(file_contents, &output_file_contents));
803 ASSERT_EQ(output_file_contents, file_contents);
804 }
805
TEST_F(ProfileAssistantTest,TestArrayClass)806 TEST_F(ProfileAssistantTest, TestArrayClass) {
807 std::vector<std::string> class_names = {
808 "[Ljava/lang/Comparable;",
809 };
810 std::string file_contents;
811 for (std::string& class_name : class_names) {
812 file_contents += class_name + std::string("\n");
813 }
814 std::string output_file_contents;
815 ASSERT_TRUE(CreateAndDump(file_contents, &output_file_contents));
816 ASSERT_EQ(output_file_contents, file_contents);
817 }
818
TEST_F(ProfileAssistantTest,TestProfileCreationGenerateMethods)819 TEST_F(ProfileAssistantTest, TestProfileCreationGenerateMethods) {
820 // Class names put here need to be in sorted order.
821 std::vector<std::string> class_names = {
822 "HLjava/lang/Math;->*",
823 };
824 std::string input_file_contents;
825 std::string expected_contents;
826 for (std::string& class_name : class_names) {
827 input_file_contents += class_name + std::string("\n");
828 expected_contents += DescriptorToDot(class_name.c_str()) +
829 std::string("\n");
830 }
831 std::string output_file_contents;
832 ScratchFile profile_file;
833 EXPECT_TRUE(CreateProfile(input_file_contents,
834 profile_file.GetFilename(),
835 GetLibCoreDexFileNames()[0]));
836 ProfileCompilationInfo info;
837 ASSERT_TRUE(info.Load(GetFd(profile_file)));
838 // Verify that the profile has matching methods.
839 ScopedObjectAccess soa(Thread::Current());
840 ObjPtr<mirror::Class> klass = GetClass(soa, /*class_loader=*/ nullptr, "Ljava/lang/Math;");
841 ASSERT_TRUE(klass != nullptr);
842 size_t method_count = 0;
843 for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) {
844 if (!method.IsCopied() && method.GetCodeItem() != nullptr) {
845 ++method_count;
846 ProfileCompilationInfo::MethodHotness hotness =
847 info.GetMethodHotness(MethodReference(method.GetDexFile(), method.GetDexMethodIndex()));
848 ASSERT_TRUE(hotness.IsHot()) << method.PrettyMethod();
849 }
850 }
851 EXPECT_GT(method_count, 0u);
852 }
853
JoinProfileLines(const std::vector<std::string> & lines)854 static std::string JoinProfileLines(const std::vector<std::string>& lines) {
855 std::string result = android::base::Join(lines, '\n');
856 return result + '\n';
857 }
858
TEST_F(ProfileAssistantTest,TestBootImageProfile)859 TEST_F(ProfileAssistantTest, TestBootImageProfile) {
860 const std::string core_dex = GetLibCoreDexFileNames()[0];
861
862 std::vector<ScratchFile> profiles;
863
864 // In image with enough clean occurrences.
865 const std::string kCleanClass = "Ljava/lang/CharSequence;";
866 // In image with enough dirty occurrences.
867 const std::string kDirtyClass = "Ljava/lang/Object;";
868 // Not in image becauseof not enough occurrences.
869 const std::string kUncommonCleanClass = "Ljava/lang/Process;";
870 const std::string kUncommonDirtyClass = "Ljava/lang/Package;";
871 // Method that is common and hot. Should end up in profile.
872 const std::string kCommonHotMethod = "Ljava/lang/Comparable;->compareTo(Ljava/lang/Object;)I";
873 // Uncommon method, should not end up in profile
874 const std::string kUncommonMethod = "Ljava/util/HashMap;-><init>()V";
875 // Method that gets marked as hot since it's in multiple profile and marked as startup.
876 const std::string kStartupMethodForUpgrade = "Ljava/util/ArrayList;->clear()V";
877 // Startup method used by a special package which will get a different threshold;
878 const std::string kSpecialPackageStartupMethod =
879 "Ljava/lang/Object;->toString()Ljava/lang/String;";
880 // Method used by a special package which will get a different threshold;
881 const std::string kUncommonSpecialPackageMethod = "Ljava/lang/Object;->hashCode()I";
882 // Denylisted class
883 const std::string kPreloadedDenylistedClass = "Ljava/lang/Thread;";
884
885 // Thresholds for this test.
886 static const size_t kDirtyThreshold = 100;
887 static const size_t kCleanThreshold = 50;
888 static const size_t kPreloadedThreshold = 100;
889 static const size_t kMethodThreshold = 75;
890 static const size_t kSpecialThreshold = 50;
891 const std::string kSpecialPackage = "dex4";
892
893 // Create boot profile content, attributing the classes and methods to different dex files.
894 std::vector<std::string> input_data = {
895 "{dex1}" + kCleanClass,
896 "{dex1}" + kDirtyClass,
897 "{dex1}" + kUncommonCleanClass,
898 "{dex1}H" + kCommonHotMethod,
899 "{dex1}P" + kStartupMethodForUpgrade,
900 "{dex1}" + kUncommonDirtyClass,
901 "{dex1}" + kPreloadedDenylistedClass,
902
903 "{dex2}" + kCleanClass,
904 "{dex2}" + kDirtyClass,
905 "{dex2}P" + kCommonHotMethod,
906 "{dex2}P" + kStartupMethodForUpgrade,
907 "{dex2}" + kUncommonDirtyClass,
908 "{dex2}" + kPreloadedDenylistedClass,
909
910 "{dex3}P" + kUncommonMethod,
911 "{dex3}PS" + kStartupMethodForUpgrade,
912 "{dex3}S" + kCommonHotMethod,
913 "{dex3}S" + kSpecialPackageStartupMethod,
914 "{dex3}" + kDirtyClass,
915 "{dex3}" + kPreloadedDenylistedClass,
916
917 "{dex4}" + kDirtyClass,
918 "{dex4}P" + kCommonHotMethod,
919 "{dex4}S" + kSpecialPackageStartupMethod,
920 "{dex4}P" + kUncommonSpecialPackageMethod,
921 "{dex4}" + kPreloadedDenylistedClass,
922 };
923 std::string input_file_contents = JoinProfileLines(input_data);
924
925 ScratchFile preloaded_class_denylist;
926 std::string denylist_content = DescriptorToDot(kPreloadedDenylistedClass.c_str());
927 EXPECT_TRUE(preloaded_class_denylist.GetFile()->WriteFully(
928 denylist_content.c_str(), denylist_content.length()));
929
930 EXPECT_EQ(0, preloaded_class_denylist.GetFile()->Flush());
931 // Expected data
932 std::vector<std::string> expected_data = {
933 kCleanClass,
934 kDirtyClass,
935 kPreloadedDenylistedClass,
936 "HSP" + kCommonHotMethod,
937 "HS" + kSpecialPackageStartupMethod,
938 "HSP" + kStartupMethodForUpgrade
939 };
940 std::string expected_profile_content = JoinProfileLines(expected_data);
941
942 std::vector<std::string> expected_preloaded_data = {
943 DescriptorToDot(kDirtyClass.c_str())
944 };
945 std::string expected_preloaded_content = JoinProfileLines(expected_preloaded_data);
946
947 ScratchFile profile;
948 EXPECT_TRUE(CreateProfile(input_file_contents,
949 profile.GetFilename(),
950 core_dex,
951 /*for_boot_image=*/ true));
952
953 ProfileCompilationInfo bootProfile(/*for_boot_image=*/ true);
954 EXPECT_TRUE(bootProfile.Load(profile.GetFilename(), /*clear_if_invalid=*/ false));
955
956 // Generate the boot profile.
957 ScratchFile out_profile;
958 ScratchFile out_preloaded_classes;
959 std::vector<std::string> args;
960 args.push_back(GetProfmanCmd());
961 args.push_back("--generate-boot-image-profile");
962 args.push_back("--class-threshold=" + std::to_string(kDirtyThreshold));
963 args.push_back("--clean-class-threshold=" + std::to_string(kCleanThreshold));
964 args.push_back("--method-threshold=" + std::to_string(kMethodThreshold));
965 args.push_back("--preloaded-class-threshold=" + std::to_string(kPreloadedThreshold));
966 args.push_back(
967 "--special-package=" + kSpecialPackage + ":" + std::to_string(kSpecialThreshold));
968 args.push_back("--profile-file=" + profile.GetFilename());
969 args.push_back("--out-profile-path=" + out_profile.GetFilename());
970 args.push_back("--out-preloaded-classes-path=" + out_preloaded_classes.GetFilename());
971 args.push_back("--apk=" + core_dex);
972 args.push_back("--dex-location=" + core_dex);
973 args.push_back("--preloaded-classes-denylist=" + preloaded_class_denylist.GetFilename());
974
975 std::string error;
976 ASSERT_EQ(ExecAndReturnCode(args, &error), 0) << error;
977
978 // Verify the boot profile contents.
979 std::string output_profile_contents;
980 ASSERT_TRUE(android::base::ReadFileToString(
981 out_profile.GetFilename(), &output_profile_contents));
982 ASSERT_EQ(output_profile_contents, expected_profile_content);
983
984 // Verify the preloaded classes content.
985 std::string output_preloaded_contents;
986 ASSERT_TRUE(android::base::ReadFileToString(
987 out_preloaded_classes.GetFilename(), &output_preloaded_contents));
988 ASSERT_EQ(output_preloaded_contents, expected_preloaded_content);
989 }
990
TEST_F(ProfileAssistantTest,TestBootImageProfileWith2RawProfiles)991 TEST_F(ProfileAssistantTest, TestBootImageProfileWith2RawProfiles) {
992 const std::string core_dex = GetLibCoreDexFileNames()[0];
993
994 std::vector<ScratchFile> profiles;
995
996 const std::string kCommonClassUsedByDex1 = "Ljava/lang/CharSequence;";
997 const std::string kCommonClassUsedByDex1Dex2 = "Ljava/lang/Object;";
998 const std::string kUncommonClass = "Ljava/lang/Process;";
999 const std::string kCommonHotMethodUsedByDex1 =
1000 "Ljava/lang/Comparable;->compareTo(Ljava/lang/Object;)I";
1001 const std::string kCommonHotMethodUsedByDex1Dex2 = "Ljava/lang/Object;->hashCode()I";
1002 const std::string kUncommonHotMethod = "Ljava/util/HashMap;-><init>()V";
1003
1004
1005 // Thresholds for this test.
1006 static const size_t kDirtyThreshold = 100;
1007 static const size_t kCleanThreshold = 100;
1008 static const size_t kMethodThreshold = 100;
1009
1010 // Create boot profile content, attributing the classes and methods to different dex files.
1011 std::vector<std::string> input_data1 = {
1012 "{dex1}" + kCommonClassUsedByDex1,
1013 "{dex1}" + kCommonClassUsedByDex1Dex2,
1014 "{dex1}" + kUncommonClass,
1015 "{dex1}H" + kCommonHotMethodUsedByDex1Dex2,
1016 "{dex1}" + kCommonHotMethodUsedByDex1,
1017 };
1018 std::vector<std::string> input_data2 = {
1019 "{dex1}" + kCommonClassUsedByDex1,
1020 "{dex2}" + kCommonClassUsedByDex1Dex2,
1021 "{dex1}H" + kCommonHotMethodUsedByDex1,
1022 "{dex2}" + kCommonHotMethodUsedByDex1Dex2,
1023 "{dex1}" + kUncommonHotMethod,
1024 };
1025 std::string input_file_contents1 = JoinProfileLines(input_data1);
1026 std::string input_file_contents2 = JoinProfileLines(input_data2);
1027
1028 // Expected data
1029 std::vector<std::string> expected_data = {
1030 kCommonClassUsedByDex1,
1031 kCommonClassUsedByDex1Dex2,
1032 "H" + kCommonHotMethodUsedByDex1,
1033 "H" + kCommonHotMethodUsedByDex1Dex2
1034 };
1035 std::string expected_profile_content = JoinProfileLines(expected_data);
1036
1037 ScratchFile profile1;
1038 ScratchFile profile2;
1039 EXPECT_TRUE(CreateProfile(input_file_contents1,
1040 profile1.GetFilename(),
1041 core_dex,
1042 /*for_boot_image=*/ true));
1043 EXPECT_TRUE(CreateProfile(input_file_contents2,
1044 profile2.GetFilename(),
1045 core_dex,
1046 /*for_boot_image=*/ true));
1047
1048 ProfileCompilationInfo boot_profile1(/*for_boot_image=*/ true);
1049 ProfileCompilationInfo boot_profile2(/*for_boot_image=*/ true);
1050 EXPECT_TRUE(boot_profile1.Load(profile1.GetFilename(), /*clear_if_invalid=*/ false));
1051 EXPECT_TRUE(boot_profile2.Load(profile2.GetFilename(), /*clear_if_invalid=*/ false));
1052
1053 // Generate the boot profile.
1054 ScratchFile out_profile;
1055 ScratchFile out_preloaded_classes;
1056 std::vector<std::string> args;
1057 args.push_back(GetProfmanCmd());
1058 args.push_back("--generate-boot-image-profile");
1059 args.push_back("--class-threshold=" + std::to_string(kDirtyThreshold));
1060 args.push_back("--clean-class-threshold=" + std::to_string(kCleanThreshold));
1061 args.push_back("--method-threshold=" + std::to_string(kMethodThreshold));
1062 args.push_back("--profile-file=" + profile1.GetFilename());
1063 args.push_back("--profile-file=" + profile2.GetFilename());
1064 args.push_back("--out-profile-path=" + out_profile.GetFilename());
1065 args.push_back("--out-preloaded-classes-path=" + out_preloaded_classes.GetFilename());
1066 args.push_back("--apk=" + core_dex);
1067 args.push_back("--dex-location=" + core_dex);
1068
1069 std::string error;
1070 ASSERT_EQ(ExecAndReturnCode(args, &error), 0) << error;
1071
1072 // Verify the boot profile contents.
1073 std::string output_profile_contents;
1074 ASSERT_TRUE(android::base::ReadFileToString(
1075 out_profile.GetFilename(), &output_profile_contents));
1076 ASSERT_EQ(output_profile_contents, expected_profile_content);
1077 }
1078
TEST_F(ProfileAssistantTest,TestProfileCreationOneNotMatched)1079 TEST_F(ProfileAssistantTest, TestProfileCreationOneNotMatched) {
1080 // Class names put here need to be in sorted order.
1081 std::vector<std::string> class_names = {
1082 "Ldoesnt/match/this/one;",
1083 "Ljava/lang/Comparable;",
1084 "Ljava/lang/Object;"
1085 };
1086 std::string input_file_contents;
1087 for (std::string& class_name : class_names) {
1088 input_file_contents += class_name + std::string("\n");
1089 }
1090 std::string output_file_contents;
1091 ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents));
1092 std::string expected_contents =
1093 class_names[1] + std::string("\n") +
1094 class_names[2] + std::string("\n");
1095 ASSERT_EQ(output_file_contents, expected_contents);
1096 }
1097
TEST_F(ProfileAssistantTest,TestProfileCreationNoneMatched)1098 TEST_F(ProfileAssistantTest, TestProfileCreationNoneMatched) {
1099 // Class names put here need to be in sorted order.
1100 std::vector<std::string> class_names = {
1101 "Ldoesnt/match/this/one;",
1102 "Ldoesnt/match/this/one/either;",
1103 "Lnor/this/one;"
1104 };
1105 std::string input_file_contents;
1106 for (std::string& class_name : class_names) {
1107 input_file_contents += class_name + std::string("\n");
1108 }
1109 std::string output_file_contents;
1110 ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents));
1111 std::string expected_contents("");
1112 ASSERT_EQ(output_file_contents, expected_contents);
1113 }
1114
1115 // Test that we can dump profiles in a way they can be re-constituted.
1116 // Test goes 'txt -> prof -> txt -> prof' and then compares the two profs.
TEST_F(ProfileAssistantTest,TestProfileRoundTrip)1117 TEST_F(ProfileAssistantTest, TestProfileRoundTrip) {
1118 // Create the profile content.
1119 std::vector<std::string_view> methods = {
1120 "HLTestInline;->inlineMonomorphic(LSuper;)I+LSubA;",
1121 "HLTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;",
1122 "HLTestInline;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;",
1123 "HLTestInline;->inlineMissingTypes(LSuper;)I+missing_types",
1124 "HLTestInline;->noInlineCache(LSuper;)I",
1125 "HLTestInline;->inlineMultiMonomorphic(LSuper;LSecret;)I+]LSuper;LSubA;]LSecret;LSubB;",
1126 "HLTestInline;->inlineMultiPolymorphic(LSuper;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;]LSecret;LSubB;,LSubC;",
1127 "HLTestInline;->inlineMultiMegamorphic(LSuper;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;,LSubD;,LSubE;]LSecret;megamorphic_types",
1128 "HLTestInline;->inlineMultiMissingTypes(LSuper;LSecret;)I+]LSuper;missing_types]LSecret;missing_types",
1129 "HLTestInline;->inlineTriplePolymorphic(LSuper;LSecret;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;]LSecret;LSubB;,LSubC;",
1130 "HLTestInline;->noInlineCacheMulti(LSuper;LSecret;)I",
1131 };
1132 std::ostringstream input_file_contents;
1133 for (const std::string_view& m : methods) {
1134 input_file_contents << m << "\n";
1135 }
1136
1137 // Create the profile and save it to disk.
1138 ScratchFile profile_file;
1139 ASSERT_TRUE(CreateProfile(input_file_contents.str(),
1140 profile_file.GetFilename(),
1141 GetTestDexFileName("ProfileTestMultiDex")));
1142
1143 // Dump the file back into text.
1144 std::string text_two;
1145 ASSERT_TRUE(DumpClassesAndMethods(
1146 profile_file.GetFilename(), &text_two, GetTestDexFileName("ProfileTestMultiDex")));
1147
1148 // Create another profile and save it to the disk as well.
1149 ScratchFile profile_two;
1150 ASSERT_TRUE(CreateProfile(
1151 text_two, profile_two.GetFilename(), GetTestDexFileName("ProfileTestMultiDex")));
1152
1153 // These two profiles should be bit-identical.
1154 // TODO We could compare the 'text_two' to the methods but since the order is
1155 // arbitrary for many parts and there are multiple 'correct' dumps we'd need
1156 // to basically parse everything and this is simply easier.
1157 std::string error;
1158 std::vector<std::string> args { kIsTargetBuild ? "/system/bin/cmp" : "/usr/bin/cmp",
1159 "-s",
1160 profile_file.GetFilename(),
1161 profile_two.GetFilename() };
1162 ASSERT_EQ(ExecAndReturnCode(args, &error), 0) << error << " from " << text_two;
1163 }
1164
1165
1166 // Test that we can dump profiles in a way they can be re-constituted and
1167 // annotations don't interfere. Test goes 'txt -> ProfileWithAnnotations -> txt
1168 // -> prof' and then compares that to one that is 'txt ->
1169 // prof_without_annotations'.
TEST_F(ProfileAssistantTest,TestProfileRoundTripWithAnnotations)1170 TEST_F(ProfileAssistantTest, TestProfileRoundTripWithAnnotations) {
1171 // Create the profile content.
1172 std::vector<std::string_view> methods = {
1173 "HLTestInline;->inlineMonomorphic(LSuper;)I+LSubA;",
1174 "HLTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;",
1175 "HLTestInline;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;",
1176 "HLTestInline;->inlineMissingTypes(LSuper;)I+missing_types",
1177 "HLTestInline;->noInlineCache(LSuper;)I",
1178 "HLTestInline;->inlineMultiMonomorphic(LSuper;LSecret;)I+]LSuper;LSubA;]LSecret;LSubB;",
1179 "HLTestInline;->inlineMultiPolymorphic(LSuper;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;]LSecret;LSubB;,LSubC;",
1180 "HLTestInline;->inlineMultiMegamorphic(LSuper;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;,LSubD;,LSubE;]LSecret;megamorphic_types",
1181 "HLTestInline;->inlineMultiMissingTypes(LSuper;LSecret;)I+]LSuper;missing_types]LSecret;missing_types",
1182 "HLTestInline;->inlineTriplePolymorphic(LSuper;LSecret;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;]LSecret;LSubB;,LSubC;",
1183 "HLTestInline;->noInlineCacheMulti(LSuper;LSecret;)I",
1184 };
1185 std::ostringstream no_annotation_input_file_contents;
1186 std::ostringstream with_annotation_input_file_contents;
1187 for (const std::string_view& m : methods) {
1188 no_annotation_input_file_contents << m << "\n";
1189 with_annotation_input_file_contents << "{foobar}" << m << "\n";
1190 }
1191
1192 // Create the profile and save it to disk.
1193 ScratchFile with_annotation_profile_file;
1194 ASSERT_TRUE(CreateProfile(with_annotation_input_file_contents.str(),
1195 with_annotation_profile_file.GetFilename(),
1196 GetTestDexFileName("ProfileTestMultiDex")));
1197
1198 ScratchFile no_annotation_profile_file;
1199 ASSERT_TRUE(CreateProfile(no_annotation_input_file_contents.str(),
1200 no_annotation_profile_file.GetFilename(),
1201 GetTestDexFileName("ProfileTestMultiDex")));
1202
1203 // Dump the file back into text.
1204 std::string text_two;
1205 ASSERT_TRUE(DumpClassesAndMethods(with_annotation_profile_file.GetFilename(),
1206 &text_two,
1207 GetTestDexFileName("ProfileTestMultiDex")));
1208
1209 // Create another profile and save it to the disk as well.
1210 ScratchFile profile_two;
1211 ASSERT_TRUE(CreateProfile(
1212 text_two, profile_two.GetFilename(), GetTestDexFileName("ProfileTestMultiDex")));
1213
1214 // These two profiles should be bit-identical.
1215 // TODO We could compare the 'text_two' to the methods but since the order is
1216 // arbitrary for many parts and there are multiple 'correct' dumps we'd need
1217 // to basically parse everything and this is simply easier.
1218 std::string error;
1219 std::vector<std::string> args { kIsTargetBuild ? "/system/bin/cmp" : "/usr/bin/cmp",
1220 "-s",
1221 no_annotation_profile_file.GetFilename(),
1222 profile_two.GetFilename() };
1223 ASSERT_EQ(ExecAndReturnCode(args, &error), 0) << error << " from " << text_two;
1224 }
1225
TEST_F(ProfileAssistantTest,TestProfileCreateInlineCache)1226 TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) {
1227 // Create the profile content.
1228 std::vector<std::string_view> methods = {
1229 "HLTestInline;->inlineMonomorphic(LSuper;)I+LSubA;",
1230 "HLTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;",
1231 "HLTestInline;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;",
1232 "HLTestInline;->inlineMissingTypes(LSuper;)I+missing_types",
1233 "HLTestInline;->noInlineCache(LSuper;)I",
1234 "HLTestInline;->inlineMultiMonomorphic(LSuper;LSecret;)I+]LSuper;LSubA;]LSecret;LSubB;",
1235 "HLTestInline;->inlineMultiPolymorphic(LSuper;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;]LSecret;LSubB;,LSubC;",
1236 "HLTestInline;->inlineMultiMegamorphic(LSuper;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;,LSubD;,LSubE;]LSecret;LSubA;,LSubB;,LSubC;,LSubD;,LSubE;",
1237 "HLTestInline;->inlineMultiMissingTypes(LSuper;LSecret;)I+]LSuper;missing_types]LSecret;missing_types",
1238 "HLTestInline;->inlineTriplePolymorphic(LSuper;LSecret;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;]LSecret;LSubB;,LSubC;",
1239 "HLTestInline;->noInlineCacheMulti(LSuper;LSecret;)I",
1240 };
1241 std::ostringstream input_file_contents;
1242 for (const std::string_view& m : methods) {
1243 input_file_contents << m << "\n";
1244 }
1245
1246 // Create the profile and save it to disk.
1247 ScratchFile profile_file;
1248 ASSERT_TRUE(CreateProfile(input_file_contents.str(),
1249 profile_file.GetFilename(),
1250 GetTestDexFileName("ProfileTestMultiDex")));
1251
1252 // Load the profile from disk.
1253 ProfileCompilationInfo info;
1254 ASSERT_TRUE(info.Load(GetFd(profile_file)));
1255
1256 // Load the dex files and verify that the profile contains the expected methods info.
1257 ScopedObjectAccess soa(Thread::Current());
1258 jobject class_loader = LoadDex("ProfileTestMultiDex");
1259 ASSERT_NE(class_loader, nullptr);
1260
1261 StackHandleScope<5> hs(soa.Self());
1262 Handle<mirror::Class> super_klass = hs.NewHandle(GetClass(soa, class_loader, "LSuper;"));
1263 Handle<mirror::Class> secret_klass = hs.NewHandle(GetClass(soa, class_loader, "LSecret;"));
1264 Handle<mirror::Class> sub_a = hs.NewHandle(GetClass(soa, class_loader, "LSubA;"));
1265 Handle<mirror::Class> sub_b = hs.NewHandle(GetClass(soa, class_loader, "LSubB;"));
1266 Handle<mirror::Class> sub_c = hs.NewHandle(GetClass(soa, class_loader, "LSubC;"));
1267
1268 ASSERT_TRUE(super_klass != nullptr);
1269 ASSERT_TRUE(secret_klass != nullptr);
1270 ASSERT_TRUE(sub_a != nullptr);
1271 ASSERT_TRUE(sub_b != nullptr);
1272 ASSERT_TRUE(sub_c != nullptr);
1273
1274 {
1275 // Verify that method inlineMonomorphic has the expected inline caches and nothing else.
1276 ArtMethod* inline_monomorphic = GetVirtualMethod(class_loader,
1277 "LTestInline;",
1278 "inlineMonomorphic");
1279 ASSERT_TRUE(inline_monomorphic != nullptr);
1280 TypeReferenceSet expected_monomorphic;
1281 expected_monomorphic.insert(MakeTypeReference(sub_a.Get()));
1282 AssertInlineCaches(inline_monomorphic,
1283 expected_monomorphic,
1284 info,
1285 /*is_megamorphic=*/false,
1286 /*is_missing_types=*/false);
1287 }
1288
1289 {
1290 // Verify that method inlinePolymorphic has the expected inline caches and nothing else.
1291 ArtMethod* inline_polymorhic = GetVirtualMethod(class_loader,
1292 "LTestInline;",
1293 "inlinePolymorphic");
1294 ASSERT_TRUE(inline_polymorhic != nullptr);
1295 TypeReferenceSet expected_polymorphic;
1296 expected_polymorphic.insert(MakeTypeReference(sub_a.Get()));
1297 expected_polymorphic.insert(MakeTypeReference(sub_b.Get()));
1298 expected_polymorphic.insert(MakeTypeReference(sub_c.Get()));
1299 AssertInlineCaches(inline_polymorhic,
1300 expected_polymorphic,
1301 info,
1302 /*is_megamorphic=*/false,
1303 /*is_missing_types=*/false);
1304 }
1305
1306 {
1307 // Verify that method inlineMegamorphic has the expected inline caches and nothing else.
1308 ArtMethod* inline_megamorphic = GetVirtualMethod(class_loader,
1309 "LTestInline;",
1310 "inlineMegamorphic");
1311 ASSERT_TRUE(inline_megamorphic != nullptr);
1312 TypeReferenceSet expected_megamorphic;
1313 AssertInlineCaches(inline_megamorphic,
1314 expected_megamorphic,
1315 info,
1316 /*is_megamorphic=*/true,
1317 /*is_missing_types=*/false);
1318 }
1319
1320 {
1321 // Verify that method inlineMegamorphic has the expected inline caches and nothing else.
1322 ArtMethod* inline_missing_types = GetVirtualMethod(class_loader,
1323 "LTestInline;",
1324 "inlineMissingTypes");
1325 ASSERT_TRUE(inline_missing_types != nullptr);
1326 TypeReferenceSet expected_missing_Types;
1327 AssertInlineCaches(inline_missing_types,
1328 expected_missing_Types,
1329 info,
1330 /*is_megamorphic=*/false,
1331 /*is_missing_types=*/true);
1332 }
1333
1334 {
1335 // Verify that method noInlineCache has no inline caches in the profile.
1336 ArtMethod* no_inline_cache = GetVirtualMethod(class_loader, "LTestInline;", "noInlineCache");
1337 ASSERT_TRUE(no_inline_cache != nullptr);
1338 ProfileCompilationInfo::MethodHotness hotness_no_inline_cache = info.GetMethodHotness(
1339 MethodReference(no_inline_cache->GetDexFile(), no_inline_cache->GetDexMethodIndex()));
1340 ASSERT_TRUE(hotness_no_inline_cache.IsHot());
1341 ASSERT_TRUE(hotness_no_inline_cache.GetInlineCacheMap()->empty());
1342 }
1343
1344 {
1345 // Verify that method inlineMonomorphic has the expected inline caches and nothing else.
1346 ArtMethod* inline_monomorphic = GetVirtualMethod(class_loader,
1347 "LTestInline;",
1348 "inlineMultiMonomorphic");
1349 ASSERT_TRUE(inline_monomorphic != nullptr);
1350 TypeReferenceSet expected_monomorphic_super;
1351 TypeReferenceSet expected_monomorphic_secret;
1352 expected_monomorphic_super.insert(MakeTypeReference(sub_a.Get()));
1353 expected_monomorphic_secret.insert(MakeTypeReference(sub_b.Get()));
1354 AssertInlineCaches(inline_monomorphic,
1355 GetDexPcOfCallTo(inline_monomorphic, super_klass),
1356 expected_monomorphic_super,
1357 info,
1358 /*is_megamorphic=*/false,
1359 /*is_missing_types=*/false);
1360 AssertInlineCaches(inline_monomorphic,
1361 GetDexPcOfCallTo(inline_monomorphic, secret_klass),
1362 expected_monomorphic_secret,
1363 info,
1364 /*is_megamorphic=*/false,
1365 /*is_missing_types=*/false);
1366 }
1367
1368 {
1369 // Verify that method inlinePolymorphic has the expected inline caches and nothing else.
1370 ArtMethod* inline_polymorhic = GetVirtualMethod(class_loader,
1371 "LTestInline;",
1372 "inlineMultiPolymorphic");
1373 ASSERT_TRUE(inline_polymorhic != nullptr);
1374 TypeReferenceSet expected_polymorphic_super;
1375 expected_polymorphic_super.insert(MakeTypeReference(sub_a.Get()));
1376 expected_polymorphic_super.insert(MakeTypeReference(sub_b.Get()));
1377 expected_polymorphic_super.insert(MakeTypeReference(sub_c.Get()));
1378 TypeReferenceSet expected_polymorphic_secret;
1379 expected_polymorphic_secret.insert(MakeTypeReference(sub_b.Get()));
1380 expected_polymorphic_secret.insert(MakeTypeReference(sub_c.Get()));
1381 AssertInlineCaches(inline_polymorhic,
1382 GetDexPcOfCallTo(inline_polymorhic, super_klass),
1383 expected_polymorphic_super,
1384 info,
1385 /*is_megamorphic=*/false,
1386 /*is_missing_types=*/false);
1387 AssertInlineCaches(inline_polymorhic,
1388 GetDexPcOfCallTo(inline_polymorhic, secret_klass),
1389 expected_polymorphic_secret,
1390 info,
1391 /*is_megamorphic=*/false,
1392 /*is_missing_types=*/false);
1393 }
1394
1395 {
1396 // Verify that method inlinePolymorphic has the expected inline caches and nothing else.
1397 ArtMethod* inline_polymorhic = GetVirtualMethod(class_loader,
1398 "LTestInline;",
1399 "inlineTriplePolymorphic");
1400 ASSERT_TRUE(inline_polymorhic != nullptr);
1401 TypeReferenceSet expected_polymorphic_super;
1402 expected_polymorphic_super.insert(MakeTypeReference(sub_a.Get()));
1403 expected_polymorphic_super.insert(MakeTypeReference(sub_b.Get()));
1404 expected_polymorphic_super.insert(MakeTypeReference(sub_c.Get()));
1405 TypeReferenceSet expected_polymorphic_secret;
1406 expected_polymorphic_secret.insert(MakeTypeReference(sub_b.Get()));
1407 expected_polymorphic_secret.insert(MakeTypeReference(sub_c.Get()));
1408 AssertInlineCaches(inline_polymorhic,
1409 GetDexPcOfCallTo(inline_polymorhic, super_klass),
1410 expected_polymorphic_super,
1411 info,
1412 /*is_megamorphic=*/false,
1413 /*is_missing_types=*/false);
1414 uint16_t first_call = GetDexPcOfCallTo(inline_polymorhic, secret_klass);
1415 AssertInlineCaches(inline_polymorhic,
1416 first_call,
1417 expected_polymorphic_secret,
1418 info,
1419 /*is_megamorphic=*/false,
1420 /*is_missing_types=*/false);
1421 uint16_t second_call = GetDexPcOfCallTo(inline_polymorhic, secret_klass, first_call);
1422 ASSERT_LT(first_call, second_call);
1423 AssertInlineCaches(inline_polymorhic,
1424 second_call,
1425 expected_polymorphic_secret,
1426 info,
1427 /*is_megamorphic=*/false,
1428 /*is_missing_types=*/false);
1429 }
1430
1431 {
1432 // Verify that method inlineMegamorphic has the expected inline caches and nothing else.
1433 ArtMethod* inline_megamorphic = GetVirtualMethod(class_loader,
1434 "LTestInline;",
1435 "inlineMultiMegamorphic");
1436 ASSERT_TRUE(inline_megamorphic != nullptr);
1437 TypeReferenceSet expected_megamorphic;
1438 AssertInlineCaches(inline_megamorphic,
1439 GetDexPcOfCallTo(inline_megamorphic, super_klass),
1440 expected_megamorphic,
1441 info,
1442 /*is_megamorphic=*/true,
1443 /*is_missing_types=*/false);
1444 AssertInlineCaches(inline_megamorphic,
1445 GetDexPcOfCallTo(inline_megamorphic, secret_klass),
1446 expected_megamorphic,
1447 info,
1448 /*is_megamorphic=*/true,
1449 /*is_missing_types=*/false);
1450 }
1451
1452 {
1453 // Verify that method inlineMegamorphic has the expected inline caches and nothing else.
1454 ArtMethod* inline_missing_types = GetVirtualMethod(class_loader,
1455 "LTestInline;",
1456 "inlineMultiMissingTypes");
1457 ASSERT_TRUE(inline_missing_types != nullptr);
1458 TypeReferenceSet expected_missing_Types;
1459 AssertInlineCaches(inline_missing_types,
1460 GetDexPcOfCallTo(inline_missing_types, super_klass),
1461 expected_missing_Types,
1462 info,
1463 /*is_megamorphic=*/false,
1464 /*is_missing_types=*/true);
1465 AssertInlineCaches(inline_missing_types,
1466 GetDexPcOfCallTo(inline_missing_types, secret_klass),
1467 expected_missing_Types,
1468 info,
1469 /*is_megamorphic=*/false,
1470 /*is_missing_types=*/true);
1471 }
1472
1473 {
1474 // Verify that method noInlineCacheMulti has no inline caches in the profile.
1475 ArtMethod* no_inline_cache =
1476 GetVirtualMethod(class_loader, "LTestInline;", "noInlineCacheMulti");
1477 ASSERT_TRUE(no_inline_cache != nullptr);
1478 ProfileCompilationInfo::MethodHotness hotness_no_inline_cache = info.GetMethodHotness(
1479 MethodReference(no_inline_cache->GetDexFile(), no_inline_cache->GetDexMethodIndex()));
1480 ASSERT_TRUE(hotness_no_inline_cache.IsHot());
1481 ASSERT_TRUE(hotness_no_inline_cache.GetInlineCacheMap()->empty());
1482 }
1483 }
1484
TEST_F(ProfileAssistantTest,MergeProfilesWithDifferentDexOrder)1485 TEST_F(ProfileAssistantTest, MergeProfilesWithDifferentDexOrder) {
1486 ScratchFile profile1;
1487 ScratchFile reference_profile;
1488
1489 std::vector<int> profile_fds({GetFd(profile1)});
1490 int reference_profile_fd = GetFd(reference_profile);
1491
1492 // The new profile info will contain the methods with indices 0-100.
1493 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
1494 ProfileCompilationInfo info1;
1495 SetupProfile(dex1, dex2, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1,
1496 /*start_method_index=*/0, /*reverse_dex_write_order=*/false);
1497
1498 // The reference profile info will contain the methods with indices 50-150.
1499 // When setting up the profile reverse the order in which the dex files
1500 // are added to the profile. This will verify that profman merges profiles
1501 // with a different dex order correctly.
1502 const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
1503 ProfileCompilationInfo reference_info;
1504 SetupProfile(dex1, dex2, kNumberOfMethodsAlreadyCompiled, 0, reference_profile,
1505 &reference_info, kNumberOfMethodsToEnableCompilation / 2, /*reverse_dex_write_order=*/true);
1506
1507 // We should advise compilation.
1508 ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
1509
1510 // The resulting compilation info must be equal to the merge of the inputs.
1511 ProfileCompilationInfo result;
1512 ASSERT_TRUE(result.Load(reference_profile_fd));
1513
1514 ProfileCompilationInfo expected;
1515 ASSERT_TRUE(expected.MergeWith(reference_info));
1516 ASSERT_TRUE(expected.MergeWith(info1));
1517 ASSERT_TRUE(expected.Equals(result));
1518
1519 // The information from profile must remain the same.
1520 CheckProfileInfo(profile1, info1);
1521 }
1522
TEST_F(ProfileAssistantTest,TestProfileCreateWithSubtype)1523 TEST_F(ProfileAssistantTest, TestProfileCreateWithSubtype) {
1524 // Create the profile content.
1525 std::vector<std::string> profile_methods = {
1526 "HLTestInlineSubtype;->inlineMonomorphic(LSuper;)I+]LSuper;LSubA;",
1527 };
1528 std::string input_file_contents;
1529 for (std::string& m : profile_methods) {
1530 input_file_contents += m + std::string("\n");
1531 }
1532
1533 // Create the profile and save it to disk.
1534 ScratchFile profile_file;
1535 std::string dex_filename = GetTestDexFileName("ProfileTestMultiDex");
1536 ASSERT_TRUE(CreateProfile(input_file_contents, profile_file.GetFilename(), dex_filename));
1537
1538 // Load the profile from disk.
1539 ProfileCompilationInfo info;
1540 ASSERT_TRUE(info.Load(GetFd(profile_file)));
1541 LOG(ERROR) << profile_file.GetFilename();
1542
1543 // Load the dex files and verify that the profile contains the expected
1544 // methods info.
1545 ScopedObjectAccess soa(Thread::Current());
1546 jobject class_loader = LoadDex("ProfileTestMultiDex");
1547 ASSERT_NE(class_loader, nullptr);
1548
1549 // NB This is the supertype of the declared line!
1550 ArtMethod* inline_monomorphic_super =
1551 GetVirtualMethod(class_loader, "LTestInline;", "inlineMonomorphic");
1552 const DexFile* dex_file = inline_monomorphic_super->GetDexFile();
1553
1554 // Verify that the inline cache is present in the superclass
1555 ProfileCompilationInfo::MethodHotness hotness_super = info.GetMethodHotness(
1556 MethodReference(dex_file, inline_monomorphic_super->GetDexMethodIndex()));
1557 ASSERT_TRUE(hotness_super.IsHot());
1558 const ProfileCompilationInfo::InlineCacheMap* inline_caches = hotness_super.GetInlineCacheMap();
1559 ASSERT_EQ(inline_caches->size(), 1u);
1560 const ProfileCompilationInfo::DexPcData& dex_pc_data = inline_caches->begin()->second;
1561 dex::TypeIndex target_type_index(dex_file->GetIndexForTypeId(*dex_file->FindTypeId("LSubA;")));
1562 ASSERT_EQ(1u, dex_pc_data.classes.size());
1563 ASSERT_EQ(target_type_index, *dex_pc_data.classes.begin());
1564
1565 // Verify that the method is present in subclass but there are no
1566 // inline-caches (since there is no code).
1567 const dex::MethodId& super_method_id =
1568 dex_file->GetMethodId(inline_monomorphic_super->GetDexMethodIndex());
1569 uint32_t sub_method_index = dex_file->GetIndexForMethodId(
1570 *dex_file->FindMethodId(*dex_file->FindTypeId("LTestInlineSubtype;"),
1571 dex_file->GetStringId(super_method_id.name_idx_),
1572 dex_file->GetProtoId(super_method_id.proto_idx_)));
1573 ProfileCompilationInfo::MethodHotness hotness_sub =
1574 info.GetMethodHotness(MethodReference(dex_file, sub_method_index));
1575 ASSERT_TRUE(hotness_sub.IsHot());
1576 ASSERT_EQ(hotness_sub.GetInlineCacheMap()->size(), 0u);
1577 }
1578
TEST_F(ProfileAssistantTest,TestProfileCreateWithSubtypeAndDump)1579 TEST_F(ProfileAssistantTest, TestProfileCreateWithSubtypeAndDump) {
1580 // Create the profile content.
1581 std::vector<std::string> profile_methods = {
1582 "HLTestInlineSubtype;->inlineMonomorphic(LSuper;)I+]LSuper;LSubA;",
1583 };
1584 std::string input_file_contents;
1585 for (std::string& m : profile_methods) {
1586 input_file_contents += m + std::string("\n");
1587 }
1588
1589 // Create the profile and save it to disk.
1590 ScratchFile profile_file;
1591 std::string dex_filename = GetTestDexFileName("ProfileTestMultiDex");
1592 ASSERT_TRUE(CreateProfile(input_file_contents, profile_file.GetFilename(), dex_filename));
1593
1594 std::string dump_ic;
1595 ASSERT_TRUE(DumpClassesAndMethods(
1596 profile_file.GetFilename(), &dump_ic, GetTestDexFileName("ProfileTestMultiDex")));
1597
1598 std::vector<std::string> lines;
1599 std::stringstream dump_stream(dump_ic);
1600 std::string cur;
1601 while (std::getline(dump_stream, cur, '\n')) {
1602 lines.push_back(std::move(cur));
1603 }
1604
1605 EXPECT_EQ(lines.size(), 2u);
1606 EXPECT_TRUE(std::find(lines.cbegin(),
1607 lines.cend(),
1608 "HLTestInline;->inlineMonomorphic(LSuper;)I+]LSuper;LSubA;") !=
1609 lines.cend());
1610 EXPECT_TRUE(std::find(lines.cbegin(),
1611 lines.cend(),
1612 "HLTestInlineSubtype;->inlineMonomorphic(LSuper;)I") != lines.cend());
1613 }
1614
TEST_F(ProfileAssistantTest,TestProfileCreateWithInvalidData)1615 TEST_F(ProfileAssistantTest, TestProfileCreateWithInvalidData) {
1616 // Create the profile content.
1617 std::vector<std::string> profile_methods = {
1618 "HLTestInline;->inlineMonomorphic(LSuper;)I+invalid_class", // Invalid descriptor for IC.
1619 "HLTestInline;->invalid_method", // Invalid method spec (no signature).
1620 "invalid_class", // Invalid descriptor.
1621 };
1622 std::string input_file_contents;
1623 for (std::string& m : profile_methods) {
1624 input_file_contents += m + std::string("\n");
1625 }
1626
1627 // Create the profile and save it to disk.
1628 ScratchFile profile_file;
1629 std::string dex_filename = GetTestDexFileName("ProfileTestMultiDex");
1630 ASSERT_TRUE(CreateProfile(input_file_contents,
1631 profile_file.GetFilename(),
1632 dex_filename));
1633
1634 // Load the profile from disk.
1635 ProfileCompilationInfo info;
1636 ASSERT_TRUE(info.Load(GetFd(profile_file)));
1637
1638 // Load the dex files and verify that the profile contains the expected methods info.
1639 ScopedObjectAccess soa(Thread::Current());
1640 jobject class_loader = LoadDex("ProfileTestMultiDex");
1641 ASSERT_NE(class_loader, nullptr);
1642
1643 ArtMethod* inline_monomorphic = GetVirtualMethod(class_loader,
1644 "LTestInline;",
1645 "inlineMonomorphic");
1646 const DexFile* dex_file = inline_monomorphic->GetDexFile();
1647
1648 // Invalid descriptor in IC results in rejection of the entire line.
1649 ProfileCompilationInfo::MethodHotness hotness =
1650 info.GetMethodHotness(MethodReference(dex_file, inline_monomorphic->GetDexMethodIndex()));
1651 ASSERT_FALSE(hotness.IsHot());
1652
1653 // No data was recorded, so the dex file does not appear in the profile.
1654 // TODO: Record all dex files passed to `profman` in the profile. Note that
1655 // this makes sense only if there are no annotations, otherwise we do not
1656 // know what annotation to use with each dex file.
1657 std::set<dex::TypeIndex> classes;
1658 std::set<uint16_t> hot_methods;
1659 std::set<uint16_t> startup_methods;
1660 std::set<uint16_t> post_start_methods;
1661 ASSERT_FALSE(info.GetClassesAndMethods(*dex_file,
1662 &classes,
1663 &hot_methods,
1664 &startup_methods,
1665 &post_start_methods));
1666 }
1667
TEST_F(ProfileAssistantTest,DumpOnly)1668 TEST_F(ProfileAssistantTest, DumpOnly) {
1669 ScratchFile profile;
1670
1671 const uint32_t kNumberOfMethods = 64;
1672 std::vector<uint32_t> hot_methods;
1673 std::vector<uint32_t> startup_methods;
1674 std::vector<uint32_t> post_startup_methods;
1675 for (size_t i = 0; i < kNumberOfMethods; ++i) {
1676 if (i % 2 == 0) {
1677 hot_methods.push_back(i);
1678 }
1679 if (i % 3 == 1) {
1680 startup_methods.push_back(i);
1681 }
1682 if (i % 4 == 2) {
1683 post_startup_methods.push_back(i);
1684 }
1685 }
1686 EXPECT_GT(hot_methods.size(), 0u);
1687 EXPECT_GT(startup_methods.size(), 0u);
1688 EXPECT_GT(post_startup_methods.size(), 0u);
1689 ProfileCompilationInfo info1;
1690 SetupBasicProfile(dex1,
1691 hot_methods,
1692 startup_methods,
1693 post_startup_methods,
1694 profile,
1695 &info1);
1696 std::string output;
1697 DumpOnly(profile.GetFilename(), &output);
1698 const size_t hot_offset = output.find("hot methods:");
1699 const size_t startup_offset = output.find("startup methods:");
1700 const size_t post_startup_offset = output.find("post startup methods:");
1701 const size_t classes_offset = output.find("classes:");
1702 ASSERT_NE(hot_offset, std::string::npos);
1703 ASSERT_NE(startup_offset, std::string::npos);
1704 ASSERT_NE(post_startup_offset, std::string::npos);
1705 ASSERT_LT(hot_offset, startup_offset);
1706 ASSERT_LT(startup_offset, post_startup_offset);
1707 // Check the actual contents of the dump by looking at the offsets of the methods.
1708 for (uint32_t m : hot_methods) {
1709 const size_t pos = output.find(std::to_string(m) + "[],", hot_offset);
1710 ASSERT_NE(pos, std::string::npos) << output;
1711 EXPECT_LT(pos, startup_offset) << output;
1712 }
1713 for (uint32_t m : startup_methods) {
1714 const size_t pos = output.find(std::to_string(m) + ",", startup_offset);
1715 ASSERT_NE(pos, std::string::npos) << output;
1716 EXPECT_LT(pos, post_startup_offset) << output;
1717 }
1718 for (uint32_t m : post_startup_methods) {
1719 const size_t pos = output.find(std::to_string(m) + ",", post_startup_offset);
1720 ASSERT_NE(pos, std::string::npos) << output;
1721 EXPECT_LT(pos, classes_offset) << output;
1722 }
1723 }
1724
TEST_F(ProfileAssistantTest,MergeProfilesWithFilter)1725 TEST_F(ProfileAssistantTest, MergeProfilesWithFilter) {
1726 ScratchFile profile1;
1727 ScratchFile profile2;
1728 ScratchFile reference_profile;
1729
1730 std::vector<int> profile_fds({
1731 GetFd(profile1),
1732 GetFd(profile2)});
1733 int reference_profile_fd = GetFd(reference_profile);
1734
1735 // Use a real dex file to generate profile test data.
1736 // The file will be used during merging to filter unwanted data.
1737 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
1738 const DexFile& d1 = *dex_files[0];
1739 const DexFile& d2 = *dex_files[1];
1740 // The new profile info will contain the methods with indices 0-100.
1741 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
1742 ProfileCompilationInfo info1;
1743 SetupProfile(&d1, dex1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
1744 ProfileCompilationInfo info2;
1745 SetupProfile(&d2, dex2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
1746
1747
1748 // The reference profile info will contain the methods with indices 50-150.
1749 const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
1750 ProfileCompilationInfo reference_info;
1751 SetupProfile(&d1, dex1,
1752 kNumberOfMethodsAlreadyCompiled, 0, reference_profile,
1753 &reference_info, kNumberOfMethodsToEnableCompilation / 2);
1754
1755 // Run profman and pass the dex file with --apk-fd.
1756 android::base::unique_fd apk_fd(
1757 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY)); // NOLINT
1758 ASSERT_GE(apk_fd.get(), 0);
1759
1760 std::string profman_cmd = GetProfmanCmd();
1761 std::vector<std::string> argv_str;
1762 argv_str.push_back(profman_cmd);
1763 argv_str.push_back("--profile-file-fd=" + std::to_string(profile1.GetFd()));
1764 argv_str.push_back("--profile-file-fd=" + std::to_string(profile2.GetFd()));
1765 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
1766 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
1767 std::string error;
1768
1769 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kCompile) << error;
1770
1771 // Verify that we can load the result.
1772
1773 ProfileCompilationInfo result;
1774 ASSERT_TRUE(result.Load(reference_profile_fd));
1775
1776 // Verify that the result filtered out data not belonging to the dex file.
1777 // This is equivalent to checking that the result is equal to the merging of
1778 // all profiles while filtering out data not belonging to the dex file.
1779
1780 ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
1781 [&d1, &d2](const std::string& dex_location, uint32_t checksum) -> bool {
1782 return (dex_location == ProfileCompilationInfo::GetProfileDexFileBaseKey(d1.GetLocation())
1783 && checksum == d1.GetLocationChecksum())
1784 || (dex_location == ProfileCompilationInfo::GetProfileDexFileBaseKey(d2.GetLocation())
1785 && checksum == d2.GetLocationChecksum());
1786 };
1787
1788 ProfileCompilationInfo info1_filter;
1789 ProfileCompilationInfo info2_filter;
1790 ProfileCompilationInfo expected;
1791
1792 info2_filter.Load(profile1.GetFd(), /*merge_classes=*/ true, filter_fn);
1793 info2_filter.Load(profile2.GetFd(), /*merge_classes=*/ true, filter_fn);
1794 expected.Load(reference_profile.GetFd(), /*merge_classes=*/ true, filter_fn);
1795
1796 ASSERT_TRUE(expected.MergeWith(info1_filter));
1797 ASSERT_TRUE(expected.MergeWith(info2_filter));
1798
1799 ASSERT_TRUE(expected.Equals(result));
1800 }
1801
TEST_F(ProfileAssistantTest,MergeProfilesNoProfile)1802 TEST_F(ProfileAssistantTest, MergeProfilesNoProfile) {
1803 ScratchFile reference_profile;
1804
1805 // Use a real dex file to generate profile test data.
1806 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
1807 const DexFile& d1 = *dex_files[0];
1808 const DexFile& d2 = *dex_files[0];
1809
1810 // The reference profile info will contain the methods with indices 0-100.
1811 ProfileCompilationInfo reference_info;
1812 SetupProfile(&d1,
1813 &d2,
1814 /*number_of_methods=*/ 100,
1815 /*number_of_classes=*/ 0,
1816 reference_profile,
1817 &reference_info);
1818
1819 std::string content_before;
1820 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
1821
1822 // Run profman and pass the dex file with --apk-fd.
1823 android::base::unique_fd apk_fd(
1824 // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
1825 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
1826 ASSERT_GE(apk_fd.get(), 0);
1827
1828 std::string profman_cmd = GetProfmanCmd();
1829 std::vector<std::string> argv_str;
1830 argv_str.push_back(profman_cmd);
1831 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
1832 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
1833
1834 // Must return kSkipCompilationSmallDelta.
1835 std::string error;
1836 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationSmallDelta)
1837 << error;
1838
1839 // Verify that the content has not changed.
1840 std::string content_after;
1841 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
1842 EXPECT_EQ(content_before, content_after);
1843 }
1844
TEST_F(ProfileAssistantTest,MergeProfilesNoProfilePassByFilename)1845 TEST_F(ProfileAssistantTest, MergeProfilesNoProfilePassByFilename) {
1846 ScratchFile reference_profile;
1847
1848 // Use a real dex file to generate profile test data.
1849 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
1850 const DexFile& d1 = *dex_files[0];
1851 const DexFile& d2 = *dex_files[0];
1852
1853 // The reference profile info will contain the methods with indices 0-100.
1854 ProfileCompilationInfo reference_info;
1855 SetupProfile(&d1,
1856 &d2,
1857 /*number_of_methods=*/100,
1858 /*number_of_classes=*/0,
1859 reference_profile,
1860 &reference_info);
1861
1862 std::string content_before;
1863 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
1864
1865 // Run profman and pass the dex file with --apk-fd.
1866 android::base::unique_fd apk_fd(
1867 // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
1868 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
1869 ASSERT_GE(apk_fd.get(), 0);
1870
1871 std::string profman_cmd = GetProfmanCmd();
1872 std::vector<std::string> argv_str;
1873 argv_str.push_back(profman_cmd);
1874 argv_str.push_back("--reference-profile-file=" + reference_profile.GetFilename());
1875 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
1876
1877 // Must return kSkipCompilationSmallDelta.
1878 std::string error;
1879 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationSmallDelta)
1880 << error;
1881
1882 // Verify that the content has not changed.
1883 std::string content_after;
1884 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
1885 EXPECT_EQ(content_before, content_after);
1886 }
1887
TEST_F(ProfileAssistantTest,MergeProfilesNoProfileEmptyReferenceProfile)1888 TEST_F(ProfileAssistantTest, MergeProfilesNoProfileEmptyReferenceProfile) {
1889 ScratchFile reference_profile;
1890
1891 // The reference profile info will only contain the header.
1892 ProfileCompilationInfo reference_info;
1893 SetupProfile(/*dex_file1=*/ nullptr,
1894 /*dex_file2=*/ nullptr,
1895 /*number_of_methods=*/ 0,
1896 /*number_of_classes=*/ 0,
1897 reference_profile,
1898 &reference_info);
1899
1900 std::string content_before;
1901 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
1902
1903 // Run profman and pass the dex file with --apk-fd.
1904 android::base::unique_fd apk_fd(
1905 // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
1906 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
1907 ASSERT_GE(apk_fd.get(), 0);
1908
1909 std::string profman_cmd = GetProfmanCmd();
1910 std::vector<std::string> argv_str;
1911 argv_str.push_back(profman_cmd);
1912 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
1913 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
1914
1915 // Must return kSkipCompilationEmptyProfiles.
1916 std::string error;
1917 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationEmptyProfiles)
1918 << error;
1919
1920 // Verify that the content has not changed.
1921 std::string content_after;
1922 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
1923 EXPECT_EQ(content_before, content_after);
1924 }
1925
TEST_F(ProfileAssistantTest,MergeProfilesNoProfileEmptyReferenceProfileAfterFiltering)1926 TEST_F(ProfileAssistantTest, MergeProfilesNoProfileEmptyReferenceProfileAfterFiltering) {
1927 ScratchFile reference_profile;
1928
1929 // Use fake dex files to generate profile test data.
1930 // All the methods will be filtered out during the profman invocation.
1931 ProfileCompilationInfo reference_info;
1932 SetupProfile(dex1,
1933 dex2,
1934 /*number_of_methods=*/ 100,
1935 /*number_of_classes=*/ 0,
1936 reference_profile,
1937 &reference_info);
1938
1939 std::string content_before;
1940 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
1941
1942 // Run profman and pass the real dex file with --apk-fd.
1943 android::base::unique_fd apk_fd(
1944 // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
1945 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
1946 ASSERT_GE(apk_fd.get(), 0);
1947
1948 std::string profman_cmd = GetProfmanCmd();
1949 std::vector<std::string> argv_str;
1950 argv_str.push_back(profman_cmd);
1951 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
1952 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
1953
1954 // Must return kSkipCompilationEmptyProfiles.
1955 std::string error;
1956 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationEmptyProfiles)
1957 << error;
1958
1959 // Verify that the content has not changed.
1960 std::string content_after;
1961 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
1962 EXPECT_EQ(content_before, content_after);
1963 }
1964
TEST_F(ProfileAssistantTest,CopyAndUpdateProfileKey)1965 TEST_F(ProfileAssistantTest, CopyAndUpdateProfileKey) {
1966 ScratchFile profile1;
1967 ScratchFile reference_profile;
1968
1969 // Use a real dex file to generate profile test data. During the copy-and-update the
1970 // matching is done based on checksum so we have to match with the real thing.
1971 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
1972 const DexFile& d1 = *dex_files[0];
1973 const DexFile& d2 = *dex_files[1];
1974
1975 ProfileCompilationInfo info1;
1976 uint16_t num_methods_to_add = std::min(d1.NumMethodIds(), d2.NumMethodIds());
1977
1978 const DexFile* dex_to_be_updated1 = BuildDex(
1979 "fake-location1", d1.GetLocationChecksum(), "LC;", d1.NumMethodIds(), d1.NumTypeIds());
1980 const DexFile* dex_to_be_updated2 = BuildDex(
1981 "fake-location2", d2.GetLocationChecksum(), "LC;", d2.NumMethodIds(), d2.NumTypeIds());
1982 SetupProfile(dex_to_be_updated1,
1983 dex_to_be_updated2,
1984 num_methods_to_add,
1985 /*number_of_classes=*/ 0,
1986 profile1,
1987 &info1);
1988
1989 // Run profman and pass the dex file with --apk-fd.
1990 android::base::unique_fd apk_fd(
1991 // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
1992 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
1993 ASSERT_GE(apk_fd.get(), 0);
1994
1995 std::string profman_cmd = GetProfmanCmd();
1996 std::vector<std::string> argv_str;
1997 argv_str.push_back(profman_cmd);
1998 argv_str.push_back("--profile-file-fd=" + std::to_string(profile1.GetFd()));
1999 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
2000 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
2001 argv_str.push_back("--copy-and-update-profile-key");
2002 std::string error;
2003
2004 // Must return kCopyAndUpdateSuccess.
2005 ASSERT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kCopyAndUpdateSuccess) << error;
2006
2007 // Verify that we can load the result.
2008 ProfileCompilationInfo result;
2009 ASSERT_TRUE(result.Load(reference_profile.GetFd()));
2010
2011 // Verify that the renaming was done.
2012 for (uint16_t i = 0; i < num_methods_to_add; i ++) {
2013 ASSERT_TRUE(result.GetMethodHotness(MethodReference(&d1, i)).IsHot()) << i;
2014 ASSERT_TRUE(result.GetMethodHotness(MethodReference(&d2, i)).IsHot()) << i;
2015
2016 ASSERT_FALSE(result.GetMethodHotness(MethodReference(dex_to_be_updated1, i)).IsHot()) << i;
2017 ASSERT_FALSE(result.GetMethodHotness(MethodReference(dex_to_be_updated2, i)).IsHot()) << i;
2018 }
2019 }
2020
TEST_F(ProfileAssistantTest,CopyAndUpdateProfileKeyNoUpdate)2021 TEST_F(ProfileAssistantTest, CopyAndUpdateProfileKeyNoUpdate) {
2022 ScratchFile profile1;
2023 ScratchFile reference_profile;
2024
2025 // Use fake dex files to generate profile test data.
2026 ProfileCompilationInfo info1;
2027 SetupProfile(dex1,
2028 dex2,
2029 /*number_of_methods=*/ 100,
2030 /*number_of_classes=*/ 0,
2031 profile1,
2032 &info1);
2033
2034 // Run profman and pass the real dex file with --apk-fd. It won't match any entry in the profile.
2035 android::base::unique_fd apk_fd(
2036 // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
2037 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
2038 ASSERT_GE(apk_fd.get(), 0);
2039
2040 std::string profman_cmd = GetProfmanCmd();
2041 std::vector<std::string> argv_str;
2042 argv_str.push_back(profman_cmd);
2043 argv_str.push_back("--profile-file-fd=" + std::to_string(profile1.GetFd()));
2044 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
2045 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
2046 argv_str.push_back("--copy-and-update-profile-key");
2047 std::string error;
2048
2049 // Must return kCopyAndUpdateNoMatch.
2050 ASSERT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kCopyAndUpdateNoMatch) << error;
2051
2052 // Verify that the content is the same.
2053 ProfileCompilationInfo result;
2054 ASSERT_TRUE(result.Load(reference_profile.GetFd()));
2055 EXPECT_TRUE(result.Equals(info1));
2056 }
2057
TEST_F(ProfileAssistantTest,BootImageMerge)2058 TEST_F(ProfileAssistantTest, BootImageMerge) {
2059 ScratchFile profile1;
2060 ScratchFile profile2;
2061 ScratchFile profile3;
2062 ScratchFile output_profile;
2063 std::vector<uint32_t> hot_methods_1;
2064 std::vector<uint32_t> hot_methods_2;
2065 std::vector<uint32_t> hot_methods_3;
2066 for (size_t i = 0; i < 100; ++i) {
2067 hot_methods_1.push_back(i);
2068 }
2069 for (size_t i = 50; i < 150; ++i) {
2070 hot_methods_2.push_back(i);
2071 }
2072 for (size_t i = 100; i < 200; ++i) {
2073 hot_methods_3.push_back(i);
2074 }
2075 ProfileCompilationInfo info1(/*for_boot_image=*/false);
2076 SetupBasicProfile(
2077 dex1, hot_methods_1, /*startup_methods=*/{}, /*post_startup_methods=*/{}, profile1, &info1);
2078 ProfileCompilationInfo info2(/*for_boot_image=*/true);
2079 SetupBasicProfile(
2080 dex1, hot_methods_2, /*startup_methods=*/{}, /*post_startup_methods=*/{}, profile2, &info2);
2081 ProfileCompilationInfo info3(/*for_boot_image=*/true);
2082 SetupBasicProfile(
2083 dex1, hot_methods_3, /*startup_methods=*/{}, /*post_startup_methods=*/{}, profile3, &info3);
2084
2085 {
2086 int return_code = ProcessProfiles({profile1.GetFd(), profile2.GetFd(), profile3.GetFd()},
2087 output_profile.GetFd(),
2088 {"--force-merge-and-analyze", "--boot-image-merge"});
2089 ASSERT_EQ(return_code, ProfmanResult::kCompile);
2090
2091 // Verify the result: it should be equal to info2 union info3 since info1 is a regular profile
2092 // and should be ignored.
2093 ProfileCompilationInfo result(/*for_boot_image=*/true);
2094 ASSERT_TRUE(result.Load(output_profile.GetFd()));
2095 ASSERT_TRUE(info2.MergeWith(info3));
2096 ASSERT_TRUE(result.Equals(info2));
2097 }
2098
2099 // Same for the legacy force merge mode.
2100 {
2101 int return_code = ProcessProfiles({profile1.GetFd(), profile2.GetFd(), profile3.GetFd()},
2102 output_profile.GetFd(),
2103 {"--force-merge", "--boot-image-merge"});
2104 ASSERT_EQ(return_code, ProfmanResult::kSuccess);
2105
2106 // Verify the result: it should be equal to info2 union info3 since info1 is a regular profile
2107 // and should be ignored.
2108 ProfileCompilationInfo result(/*for_boot_image=*/true);
2109 ASSERT_TRUE(result.Load(output_profile.GetFd()));
2110 ASSERT_TRUE(info2.MergeWith(info3));
2111 ASSERT_TRUE(result.Equals(info2));
2112 }
2113 }
2114
2115 // Under default behaviour we should not advice compilation
2116 // and the reference profile should not be updated.
2117 // However we pass --force-merge to force aggregation and in this case
2118 // we should see an update.
TEST_F(ProfileAssistantTest,ForceMerge)2119 TEST_F(ProfileAssistantTest, ForceMerge) {
2120 const uint16_t kNumberOfClassesInRefProfile = 6000;
2121 const uint16_t kNumberOfClassesInCurProfile = 6110; // Threshold is 2%.
2122
2123 const DexFile* dex1_7000 = BuildDex("location1_7000",
2124 /*location_checksum=*/ 7001,
2125 "LUnique1_7000;",
2126 /*num_method_ids=*/ 0,
2127 /*num_class_ids=*/ 7000);
2128 const DexFile* dex2_7000 = BuildDex("location2_7000",
2129 /*location_checksum=*/ 7002,
2130 "LUnique2_7000;",
2131 /*num_method_ids=*/ 0,
2132 /*num_class_ids=*/ 7000);
2133
2134 ScratchFile profile;
2135 ScratchFile reference_profile;
2136
2137 std::vector<int> profile_fds({ GetFd(profile)});
2138 int reference_profile_fd = GetFd(reference_profile);
2139
2140 ProfileCompilationInfo info1;
2141 SetupProfile(dex1_7000, dex2_7000, 0, kNumberOfClassesInRefProfile, profile, &info1);
2142 ProfileCompilationInfo info2;
2143 SetupProfile(dex1_7000, dex2_7000, 0, kNumberOfClassesInCurProfile, reference_profile, &info2);
2144
2145 std::vector<const std::string> extra_args({"--force-merge"});
2146 int return_code = ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
2147
2148 ASSERT_EQ(return_code, ProfmanResult::kSuccess);
2149
2150 // Check that the result is the aggregation.
2151 ProfileCompilationInfo result;
2152 ASSERT_TRUE(result.Load(reference_profile.GetFd()));
2153 ASSERT_TRUE(info1.MergeWith(info2));
2154 ASSERT_TRUE(result.Equals(info1));
2155 }
2156
TEST_F(ProfileAssistantTest,ForceMergeAndAnalyze)2157 TEST_F(ProfileAssistantTest, ForceMergeAndAnalyze) {
2158 const uint16_t kNumberOfMethodsInRefProfile = 600;
2159 const uint16_t kNumberOfMethodsInCurProfile = 601;
2160
2161 ScratchFile ref_profile;
2162 ScratchFile cur_profile;
2163
2164 ProfileCompilationInfo ref_info;
2165 SetupProfile(
2166 dex1, dex2, kNumberOfMethodsInRefProfile, /*number_of_classes=*/0, ref_profile, &ref_info);
2167 ProfileCompilationInfo cur_info;
2168 SetupProfile(
2169 dex1, dex2, kNumberOfMethodsInCurProfile, /*number_of_classes=*/0, cur_profile, &cur_info);
2170
2171 std::vector<const std::string> extra_args({"--force-merge-and-analyze"});
2172 int return_code = ProcessProfiles({cur_profile.GetFd()}, ref_profile.GetFd(), extra_args);
2173
2174 ASSERT_EQ(return_code, ProfmanResult::kCompile);
2175
2176 // Check that the result is the aggregation.
2177 ProfileCompilationInfo result;
2178 ASSERT_TRUE(result.Load(ref_profile.GetFd()));
2179 ASSERT_TRUE(ref_info.MergeWith(cur_info));
2180 ASSERT_TRUE(result.Equals(ref_info));
2181 }
2182
TEST_F(ProfileAssistantTest,ForceMergeAndAnalyzeNoDelta)2183 TEST_F(ProfileAssistantTest, ForceMergeAndAnalyzeNoDelta) {
2184 const uint16_t kNumberOfMethodsInRefProfile = 600;
2185 const uint16_t kNumberOfMethodsInCurProfile = 600;
2186
2187 ScratchFile ref_profile;
2188 ScratchFile cur_profile;
2189
2190 ProfileCompilationInfo ref_info;
2191 SetupProfile(
2192 dex1, dex2, kNumberOfMethodsInRefProfile, /*number_of_classes=*/0, ref_profile, &ref_info);
2193 ProfileCompilationInfo cur_info;
2194 SetupProfile(
2195 dex1, dex2, kNumberOfMethodsInCurProfile, /*number_of_classes=*/0, cur_profile, &cur_info);
2196
2197 std::vector<const std::string> extra_args({"--force-merge-and-analyze"});
2198 int return_code = ProcessProfiles({cur_profile.GetFd()}, ref_profile.GetFd(), extra_args);
2199
2200 ASSERT_EQ(return_code, ProfmanResult::kSkipCompilationSmallDelta);
2201
2202 // Check that the reference profile is unchanged.
2203 ProfileCompilationInfo result;
2204 ASSERT_TRUE(result.Load(ref_profile.GetFd()));
2205 ASSERT_TRUE(result.Equals(ref_info));
2206 }
2207
TEST_F(ProfileAssistantTest,ForceMergeAndAnalyzeEmptyProfiles)2208 TEST_F(ProfileAssistantTest, ForceMergeAndAnalyzeEmptyProfiles) {
2209 const uint16_t kNumberOfMethodsInRefProfile = 0;
2210 const uint16_t kNumberOfMethodsInCurProfile = 0;
2211
2212 ScratchFile ref_profile;
2213 ScratchFile cur_profile;
2214
2215 ProfileCompilationInfo ref_info;
2216 SetupProfile(
2217 dex1, dex2, kNumberOfMethodsInRefProfile, /*number_of_classes=*/0, ref_profile, &ref_info);
2218 ProfileCompilationInfo cur_info;
2219 SetupProfile(
2220 dex1, dex2, kNumberOfMethodsInCurProfile, /*number_of_classes=*/0, cur_profile, &cur_info);
2221
2222 std::vector<const std::string> extra_args({"--force-merge-and-analyze"});
2223 int return_code = ProcessProfiles({cur_profile.GetFd()}, ref_profile.GetFd(), extra_args);
2224
2225 ASSERT_EQ(return_code, ProfmanResult::kSkipCompilationEmptyProfiles);
2226
2227 // Check that the reference profile is unchanged.
2228 ProfileCompilationInfo result;
2229 ASSERT_TRUE(result.Load(ref_profile.GetFd()));
2230 ASSERT_TRUE(result.Equals(ref_info));
2231 }
2232
2233 // Test that we consider the annations when we merge boot image profiles.
TEST_F(ProfileAssistantTest,BootImageMergeWithAnnotations)2234 TEST_F(ProfileAssistantTest, BootImageMergeWithAnnotations) {
2235 ScratchFile profile;
2236 ScratchFile reference_profile;
2237
2238 std::vector<int> profile_fds({GetFd(profile)});
2239 int reference_profile_fd = GetFd(reference_profile);
2240
2241 // Use a real dex file to generate profile test data so that we can pass descriptors to profman.
2242 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
2243 const DexFile& d1 = *dex_files[0];
2244 const DexFile& d2 = *dex_files[1];
2245 // The new profile info will contain the methods with indices 0-100.
2246 ProfileCompilationInfo info(/*for_boot_image=*/ true);
2247 ProfileCompilationInfo::ProfileSampleAnnotation psa1("package1");
2248 ProfileCompilationInfo::ProfileSampleAnnotation psa2("package2");
2249
2250 AddMethod(&info, &d1, 0, Hotness::kFlagHot, psa1);
2251 AddMethod(&info, &d2, 0, Hotness::kFlagHot, psa2);
2252 info.Save(profile.GetFd());
2253
2254 // Run profman and pass the dex file with --apk-fd.
2255 android::base::unique_fd apk_fd(
2256 // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
2257 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
2258 ASSERT_GE(apk_fd.get(), 0);
2259
2260 std::string profman_cmd = GetProfmanCmd();
2261 std::vector<std::string> argv_str;
2262 argv_str.push_back(profman_cmd);
2263 argv_str.push_back("--profile-file-fd=" + std::to_string(profile.GetFd()));
2264 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
2265 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
2266 argv_str.push_back("--force-merge");
2267 argv_str.push_back("--boot-image-merge");
2268 std::string error;
2269
2270 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSuccess) << error;
2271
2272 // Verify that we can load the result and that it equals to what we saved.
2273 ProfileCompilationInfo result(/*for_boot_image=*/ true);
2274 ASSERT_TRUE(result.Load(reference_profile_fd));
2275 ASSERT_TRUE(info.Equals(result));
2276 }
2277
TEST_F(ProfileAssistantTest,DifferentProfileVersions)2278 TEST_F(ProfileAssistantTest, DifferentProfileVersions) {
2279 ScratchFile profile1;
2280 ScratchFile profile2;
2281
2282 ProfileCompilationInfo info1(/*for_boot_image=*/ false);
2283 info1.Save(profile1.GetFd());
2284
2285 ProfileCompilationInfo info2(/*for_boot_image=*/ true);
2286 info2.Save(profile2.GetFd());
2287
2288 std::vector<int> profile_fds({ GetFd(profile1)});
2289 int reference_profile_fd = GetFd(profile2);
2290 std::vector<const std::string> boot_image_args({"--boot-image-merge"});
2291 ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, boot_image_args),
2292 ProfmanResult::kErrorDifferentVersions);
2293 ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd), ProfmanResult::kErrorBadProfiles);
2294
2295 // Reverse the order of the profiles to verify we get the same behaviour.
2296 profile_fds[0] = GetFd(profile2);
2297 reference_profile_fd = GetFd(profile1);
2298 ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, boot_image_args),
2299 ProfmanResult::kErrorBadProfiles);
2300 ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd),
2301 ProfmanResult::kErrorDifferentVersions);
2302 }
2303
2304 // Under default behaviour we will abort if we cannot load a profile during a merge
2305 // operation. However, if we pass --force-merge to force aggregation we should
2306 // ignore files we cannot load
TEST_F(ProfileAssistantTest,ForceMergeIgnoreProfilesItCannotLoad)2307 TEST_F(ProfileAssistantTest, ForceMergeIgnoreProfilesItCannotLoad) {
2308 ScratchFile profile1;
2309 ScratchFile profile2;
2310
2311 // Write corrupt data in the first file.
2312 std::string content = "giberish";
2313 ASSERT_TRUE(profile1.GetFile()->WriteFully(content.c_str(), content.length()));
2314
2315 ProfileCompilationInfo info2(/*for_boot_image=*/true);
2316 info2.Save(profile2.GetFd());
2317
2318 std::vector<int> profile_fds({GetFd(profile1)});
2319 int reference_profile_fd = GetFd(profile2);
2320
2321 // With force-merge we should merge successfully.
2322 {
2323 ASSERT_EQ(
2324 ProcessProfiles(
2325 profile_fds, reference_profile_fd, {"--force-merge-and-analyze", "--boot-image-merge"}),
2326 ProfmanResult::kSkipCompilationEmptyProfiles);
2327
2328 ProfileCompilationInfo result(/*for_boot_image=*/true);
2329 ASSERT_TRUE(result.Load(reference_profile_fd));
2330 ASSERT_TRUE(info2.Equals(result));
2331 }
2332
2333 // Same for the legacy force merge mode.
2334 {
2335 ASSERT_EQ(
2336 ProcessProfiles(profile_fds, reference_profile_fd, {"--force-merge", "--boot-image-merge"}),
2337 ProfmanResult::kSuccess);
2338
2339 ProfileCompilationInfo result(/*for_boot_image=*/true);
2340 ASSERT_TRUE(result.Load(reference_profile_fd));
2341 ASSERT_TRUE(info2.Equals(result));
2342 }
2343
2344 // Without force-merge we should fail.
2345 {
2346 ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, {"--boot-image-merge"}),
2347 ProfmanResult::kErrorBadProfiles);
2348 }
2349 }
2350
2351 } // namespace art
2352