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 = type_ref.dex_file->StringByTypeIdx(type_ref.TypeIndex());
403 for (dex::TypeIndex type_index : dex_pc_data.classes) {
404 ASSERT_TRUE(type_index.IsValid());
405 const char* descriptor = info.GetTypeDescriptor(dex_file, type_index);
406 if (strcmp(expected_descriptor, descriptor) == 0) {
407 ++found;
408 }
409 }
410 }
411 }
412
413 ASSERT_EQ(expected_clases.size(), found);
414 }
415
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> ())416 int CheckCompilationMethodPercentChange(uint16_t methods_in_cur_profile,
417 uint16_t methods_in_ref_profile,
418 const std::vector<const std::string>& extra_args =
419 std::vector<const std::string>()) {
420 ScratchFile profile;
421 ScratchFile reference_profile;
422 std::vector<int> profile_fds({ GetFd(profile)});
423 int reference_profile_fd = GetFd(reference_profile);
424 std::vector<uint32_t> hot_methods_cur;
425 std::vector<uint32_t> hot_methods_ref;
426 std::vector<uint32_t> empty_vector;
427 for (size_t i = 0; i < methods_in_cur_profile; ++i) {
428 hot_methods_cur.push_back(i);
429 }
430 for (size_t i = 0; i < methods_in_ref_profile; ++i) {
431 hot_methods_ref.push_back(i);
432 }
433 ProfileCompilationInfo info1;
434 SetupBasicProfile(dex1, hot_methods_cur, empty_vector, empty_vector,
435 profile, &info1);
436 ProfileCompilationInfo info2;
437 SetupBasicProfile(dex1, hot_methods_ref, empty_vector, empty_vector,
438 reference_profile, &info2);
439 return ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
440 }
441
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> ())442 int CheckCompilationClassPercentChange(uint16_t classes_in_cur_profile,
443 uint16_t classes_in_ref_profile,
444 const std::vector<const std::string>& extra_args =
445 std::vector<const std::string>()) {
446 uint16_t max_classes = std::max(classes_in_cur_profile, classes_in_ref_profile);
447 const DexFile* dex1_x = BuildDex("location1_x",
448 /*location_checksum=*/ 0x101,
449 "LUnique1_x;",
450 /*num_method_ids=*/ 0,
451 max_classes);
452 const DexFile* dex2_x = BuildDex("location2_x",
453 /*location_checksum=*/ 0x102,
454 "LUnique2_x;",
455 /*num_method_ids=*/ 0,
456 max_classes);
457
458 ScratchFile profile;
459 ScratchFile reference_profile;
460
461 std::vector<int> profile_fds({ GetFd(profile)});
462 int reference_profile_fd = GetFd(reference_profile);
463
464 ProfileCompilationInfo info1;
465 SetupProfile(dex1_x, dex2_x, 0, classes_in_cur_profile, profile, &info1);
466 ProfileCompilationInfo info2;
467 SetupProfile(dex1_x, dex2_x, 0, classes_in_ref_profile, reference_profile, &info2);
468 return ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
469 }
470
471 std::unique_ptr<ArenaAllocator> allocator_;
472
473 const DexFile* dex1;
474 const DexFile* dex2;
475 const DexFile* dex3;
476 const DexFile* dex4;
477 const DexFile* dex1_checksum_missmatch;
478 };
479
TEST_F(ProfileAssistantTest,AdviseCompilationEmptyReferences)480 TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) {
481 ScratchFile profile1;
482 ScratchFile profile2;
483 ScratchFile reference_profile;
484
485 std::vector<int> profile_fds({
486 GetFd(profile1),
487 GetFd(profile2)});
488 int reference_profile_fd = GetFd(reference_profile);
489
490 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
491 ProfileCompilationInfo info1;
492 SetupProfile(dex1, dex2, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
493 ProfileCompilationInfo info2;
494 SetupProfile(dex3, dex4, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
495
496 // We should advise compilation.
497 ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
498 // The resulting compilation info must be equal to the merge of the inputs.
499 ProfileCompilationInfo result;
500 ASSERT_TRUE(result.Load(reference_profile_fd));
501
502 ProfileCompilationInfo expected;
503 ASSERT_TRUE(expected.MergeWith(info1));
504 ASSERT_TRUE(expected.MergeWith(info2));
505 ASSERT_TRUE(expected.Equals(result));
506
507 // The information from profiles must remain the same.
508 CheckProfileInfo(profile1, info1);
509 CheckProfileInfo(profile2, info2);
510 }
511
512 // TODO(calin): Add more tests for classes.
TEST_F(ProfileAssistantTest,AdviseCompilationEmptyReferencesBecauseOfClasses)513 TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferencesBecauseOfClasses) {
514 const uint16_t kNumberOfClassesToEnableCompilation = 100;
515 const DexFile* dex1_100 = BuildDex("location1_100",
516 /*location_checksum=*/ 101,
517 "LUnique1_100;",
518 /*num_method_ids=*/ 0,
519 /*num_class_ids=*/ 100);
520 const DexFile* dex2_100 = BuildDex("location2_100",
521 /*location_checksum=*/ 102,
522 "LUnique2_100;",
523 /*num_method_ids=*/ 0,
524 /*num_class_ids=*/ 100);
525
526 ScratchFile profile1;
527 ScratchFile reference_profile;
528
529 std::vector<int> profile_fds({
530 GetFd(profile1)});
531 int reference_profile_fd = GetFd(reference_profile);
532
533 ProfileCompilationInfo info1;
534 SetupProfile(dex1_100, dex2_100, 0, kNumberOfClassesToEnableCompilation, profile1, &info1);
535
536 // We should advise compilation.
537 ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
538 // The resulting compilation info must be equal to the merge of the inputs.
539 ProfileCompilationInfo result;
540 ASSERT_TRUE(result.Load(reference_profile_fd));
541
542 ProfileCompilationInfo expected;
543 ASSERT_TRUE(expected.MergeWith(info1));
544 ASSERT_TRUE(expected.Equals(result));
545
546 // The information from profiles must remain the same.
547 CheckProfileInfo(profile1, info1);
548 }
549
TEST_F(ProfileAssistantTest,AdviseCompilationNonEmptyReferences)550 TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) {
551 ScratchFile profile1;
552 ScratchFile profile2;
553 ScratchFile reference_profile;
554
555 std::vector<int> profile_fds({
556 GetFd(profile1),
557 GetFd(profile2)});
558 int reference_profile_fd = GetFd(reference_profile);
559
560 // The new profile info will contain the methods with indices 0-100.
561 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
562 ProfileCompilationInfo info1;
563 SetupProfile(dex1, dex2, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
564 ProfileCompilationInfo info2;
565 SetupProfile(dex3, dex4, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
566
567
568 // The reference profile info will contain the methods with indices 50-150.
569 const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
570 ProfileCompilationInfo reference_info;
571 SetupProfile(dex1, dex2, kNumberOfMethodsAlreadyCompiled, 0, reference_profile,
572 &reference_info, kNumberOfMethodsToEnableCompilation / 2);
573
574 // We should advise compilation.
575 ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
576
577 // The resulting compilation info must be equal to the merge of the inputs
578 ProfileCompilationInfo result;
579 ASSERT_TRUE(result.Load(reference_profile_fd));
580
581 ProfileCompilationInfo expected;
582 ASSERT_TRUE(expected.MergeWith(info1));
583 ASSERT_TRUE(expected.MergeWith(info2));
584 ASSERT_TRUE(expected.MergeWith(reference_info));
585 ASSERT_TRUE(expected.Equals(result));
586
587 // The information from profiles must remain the same.
588 CheckProfileInfo(profile1, info1);
589 CheckProfileInfo(profile2, info2);
590 }
591
TEST_F(ProfileAssistantTest,DoNotAdviseCompilationEmptyProfile)592 TEST_F(ProfileAssistantTest, DoNotAdviseCompilationEmptyProfile) {
593 ScratchFile profile1;
594 ScratchFile profile2;
595 ScratchFile reference_profile;
596
597 std::vector<int> profile_fds({
598 GetFd(profile1),
599 GetFd(profile2)});
600 int reference_profile_fd = GetFd(reference_profile);
601
602 ProfileCompilationInfo info1;
603 SetupProfile(dex1, dex2, /*number_of_methods=*/ 0, /*number_of_classes*/ 0, profile1, &info1);
604 ProfileCompilationInfo info2;
605 SetupProfile(dex3, dex4, /*number_of_methods=*/ 0, /*number_of_classes*/ 0, profile2, &info2);
606
607 // We should not advise compilation.
608 ASSERT_EQ(ProfmanResult::kSkipCompilationEmptyProfiles,
609 ProcessProfiles(profile_fds, reference_profile_fd));
610
611 // The information from profiles must remain the same.
612 ProfileCompilationInfo file_info1;
613 ASSERT_TRUE(file_info1.Load(GetFd(profile1)));
614 ASSERT_TRUE(file_info1.Equals(info1));
615
616 ProfileCompilationInfo file_info2;
617 ASSERT_TRUE(file_info2.Load(GetFd(profile2)));
618 ASSERT_TRUE(file_info2.Equals(info2));
619
620 // Reference profile files must remain empty.
621 ASSERT_EQ(0, reference_profile.GetFile()->GetLength());
622
623 // The information from profiles must remain the same.
624 CheckProfileInfo(profile1, info1);
625 CheckProfileInfo(profile2, info2);
626 }
627
TEST_F(ProfileAssistantTest,DoNotAdviseCompilation)628 TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) {
629 ScratchFile profile1;
630 ScratchFile profile2;
631 ScratchFile reference_profile;
632
633 std::vector<int> profile_fds({
634 GetFd(profile1),
635 GetFd(profile2)});
636 int reference_profile_fd = GetFd(reference_profile);
637
638 const uint16_t kNumberOfMethodsToSkipCompilation = 24; // Threshold is 100.
639 ProfileCompilationInfo info1;
640 SetupProfile(dex1, dex2, kNumberOfMethodsToSkipCompilation, 0, profile1, &info1);
641 ProfileCompilationInfo info2;
642 SetupProfile(dex3, dex4, kNumberOfMethodsToSkipCompilation, 0, profile2, &info2);
643
644 // We should not advise compilation.
645 ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
646 ProcessProfiles(profile_fds, reference_profile_fd));
647
648 // The information from profiles must remain the same.
649 ProfileCompilationInfo file_info1;
650 ASSERT_TRUE(file_info1.Load(GetFd(profile1)));
651 ASSERT_TRUE(file_info1.Equals(info1));
652
653 ProfileCompilationInfo file_info2;
654 ASSERT_TRUE(file_info2.Load(GetFd(profile2)));
655 ASSERT_TRUE(file_info2.Equals(info2));
656
657 // Reference profile files must remain empty.
658 ASSERT_EQ(0, reference_profile.GetFile()->GetLength());
659
660 // The information from profiles must remain the same.
661 CheckProfileInfo(profile1, info1);
662 CheckProfileInfo(profile2, info2);
663 }
664
TEST_F(ProfileAssistantTest,DoNotAdviseCompilationMethodPercentage)665 TEST_F(ProfileAssistantTest, DoNotAdviseCompilationMethodPercentage) {
666 const uint16_t kNumberOfMethodsInRefProfile = 6000;
667 const uint16_t kNumberOfMethodsInCurProfile = 6100; // Threshold is 2%.
668 std::vector<const std::string> extra_args({"--min-new-methods-percent-change=2"});
669
670 // We should not advise compilation.
671 ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
672 CheckCompilationMethodPercentChange(
673 kNumberOfMethodsInCurProfile, kNumberOfMethodsInRefProfile, extra_args));
674 }
675
TEST_F(ProfileAssistantTest,ShouldAdviseCompilationMethodPercentage)676 TEST_F(ProfileAssistantTest, ShouldAdviseCompilationMethodPercentage) {
677 const uint16_t kNumberOfMethodsInRefProfile = 6000;
678 const uint16_t kNumberOfMethodsInCurProfile = 6200; // Threshold is 2%.
679 std::vector<const std::string> extra_args({"--min-new-methods-percent-change=2"});
680
681 // We should advise compilation.
682 ASSERT_EQ(ProfmanResult::kCompile,
683 CheckCompilationMethodPercentChange(
684 kNumberOfMethodsInCurProfile, kNumberOfMethodsInRefProfile, extra_args));
685 }
686
TEST_F(ProfileAssistantTest,DoNotAdviseCompilationMethodPercentageWithNewMin)687 TEST_F(ProfileAssistantTest, DoNotAdviseCompilationMethodPercentageWithNewMin) {
688 const uint16_t kNumberOfMethodsInRefProfile = 6000;
689 const uint16_t kNumberOfMethodsInCurProfile = 6200; // Threshold is 20%.
690
691 // We should not advise compilation.
692 ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
693 CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile,
694 kNumberOfMethodsInRefProfile));
695 }
696
TEST_F(ProfileAssistantTest,DoNotAdviseCompilationClassPercentage)697 TEST_F(ProfileAssistantTest, DoNotAdviseCompilationClassPercentage) {
698 const uint16_t kNumberOfClassesInRefProfile = 6000;
699 const uint16_t kNumberOfClassesInCurProfile = 6110; // Threshold is 2%.
700 std::vector<const std::string> extra_args({"--min-new-classes-percent-change=2"});
701
702 // We should not advise compilation.
703 ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
704 CheckCompilationClassPercentChange(
705 kNumberOfClassesInCurProfile, kNumberOfClassesInRefProfile, extra_args));
706 }
707
TEST_F(ProfileAssistantTest,ShouldAdviseCompilationClassPercentage)708 TEST_F(ProfileAssistantTest, ShouldAdviseCompilationClassPercentage) {
709 const uint16_t kNumberOfClassesInRefProfile = 6000;
710 const uint16_t kNumberOfClassesInCurProfile = 6120; // Threshold is 2%.
711 std::vector<const std::string> extra_args({"--min-new-classes-percent-change=2"});
712
713 // We should advise compilation.
714 ASSERT_EQ(ProfmanResult::kCompile,
715 CheckCompilationClassPercentChange(
716 kNumberOfClassesInCurProfile, kNumberOfClassesInRefProfile, extra_args));
717 }
718
TEST_F(ProfileAssistantTest,DoNotAdviseCompilationClassPercentageWithNewMin)719 TEST_F(ProfileAssistantTest, DoNotAdviseCompilationClassPercentageWithNewMin) {
720 const uint16_t kNumberOfClassesInRefProfile = 6000;
721 const uint16_t kNumberOfClassesInCurProfile = 6200; // Threshold is 20%.
722
723 // We should not advise compilation.
724 ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
725 CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile,
726 kNumberOfClassesInRefProfile));
727 }
728
TEST_F(ProfileAssistantTest,FailProcessingBecauseOfProfiles)729 TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) {
730 ScratchFile profile1;
731 ScratchFile profile2;
732 ScratchFile reference_profile;
733
734 std::vector<int> profile_fds({
735 GetFd(profile1),
736 GetFd(profile2)});
737 int reference_profile_fd = GetFd(reference_profile);
738
739 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
740 // Assign different hashes for the same dex file. This will make merging of information to fail.
741 ProfileCompilationInfo info1;
742 SetupProfile(dex1, dex2, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
743 ProfileCompilationInfo info2;
744 SetupProfile(
745 dex1_checksum_missmatch, dex2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
746
747 // We should fail processing.
748 ASSERT_EQ(ProfmanResult::kErrorBadProfiles, ProcessProfiles(profile_fds, reference_profile_fd));
749
750 // The information from profiles must remain the same.
751 CheckProfileInfo(profile1, info1);
752 CheckProfileInfo(profile2, info2);
753
754 // Reference profile files must still remain empty.
755 ASSERT_EQ(0, reference_profile.GetFile()->GetLength());
756 }
757
TEST_F(ProfileAssistantTest,FailProcessingBecauseOfReferenceProfiles)758 TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) {
759 ScratchFile profile1;
760 ScratchFile reference_profile;
761
762 std::vector<int> profile_fds({
763 GetFd(profile1)});
764 int reference_profile_fd = GetFd(reference_profile);
765
766 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
767 // Assign different hashes for the same dex file. This will make merging of information to fail.
768 ProfileCompilationInfo info1;
769 SetupProfile(dex1, dex2, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
770 ProfileCompilationInfo reference_info;
771 SetupProfile(dex1_checksum_missmatch,
772 dex2,
773 kNumberOfMethodsToEnableCompilation,
774 0,
775 reference_profile,
776 &reference_info);
777
778 // We should not advise compilation.
779 ASSERT_EQ(ProfmanResult::kErrorBadProfiles, ProcessProfiles(profile_fds, reference_profile_fd));
780
781 // The information from profiles must remain the same.
782 CheckProfileInfo(profile1, info1);
783 }
784
TEST_F(ProfileAssistantTest,TestProfileGeneration)785 TEST_F(ProfileAssistantTest, TestProfileGeneration) {
786 ScratchFile profile;
787 // Generate a test profile.
788 GenerateTestProfile(profile.GetFilename());
789
790 // Verify that the generated profile is valid and can be loaded.
791 ProfileCompilationInfo info;
792 ASSERT_TRUE(info.Load(GetFd(profile)));
793 }
794
TEST_F(ProfileAssistantTest,TestProfileGenerationWithIndexDex)795 TEST_F(ProfileAssistantTest, TestProfileGenerationWithIndexDex) {
796 ScratchFile profile;
797 // Generate a test profile passing in a dex file as reference.
798 GenerateTestProfileWithInputDex(profile.GetFilename());
799
800 // Verify that the generated profile is valid and can be loaded.
801 ProfileCompilationInfo info;
802 ASSERT_TRUE(info.Load(GetFd(profile)));
803 }
804
TEST_F(ProfileAssistantTest,TestProfileCreationAllMatch)805 TEST_F(ProfileAssistantTest, TestProfileCreationAllMatch) {
806 // Class names put here need to be in sorted order.
807 std::vector<std::string> class_names = {
808 "HLjava/lang/Object;-><init>()V",
809 "Ljava/lang/Comparable;",
810 "Ljava/lang/Math;",
811 "Ljava/lang/Object;",
812 "SPLjava/lang/Comparable;->compareTo(Ljava/lang/Object;)I",
813 "[[[[[[[[I", // No `TypeId`s in core-oj with this many array dimensions,
814 "[[[[[[[[Ljava/lang/Object;", // "extra descriptors" shall be used for these array classes.
815 };
816 std::string file_contents;
817 for (std::string& class_name : class_names) {
818 file_contents += class_name + std::string("\n");
819 }
820 std::string output_file_contents;
821 ASSERT_TRUE(CreateAndDump(file_contents, &output_file_contents));
822 ASSERT_EQ(output_file_contents, file_contents);
823 }
824
TEST_F(ProfileAssistantTest,TestArrayClass)825 TEST_F(ProfileAssistantTest, TestArrayClass) {
826 std::vector<std::string> class_names = {
827 "[Ljava/lang/Comparable;",
828 };
829 std::string file_contents;
830 for (std::string& class_name : class_names) {
831 file_contents += class_name + std::string("\n");
832 }
833 std::string output_file_contents;
834 ASSERT_TRUE(CreateAndDump(file_contents, &output_file_contents));
835 ASSERT_EQ(output_file_contents, file_contents);
836 }
837
TEST_F(ProfileAssistantTest,TestProfileCreationGenerateMethods)838 TEST_F(ProfileAssistantTest, TestProfileCreationGenerateMethods) {
839 // Class names put here need to be in sorted order.
840 std::vector<std::string> class_names = {
841 "HLjava/lang/Math;->*",
842 };
843 std::string input_file_contents;
844 std::string expected_contents;
845 for (std::string& class_name : class_names) {
846 input_file_contents += class_name + std::string("\n");
847 expected_contents += DescriptorToDot(class_name.c_str()) +
848 std::string("\n");
849 }
850 std::string output_file_contents;
851 ScratchFile profile_file;
852 EXPECT_TRUE(CreateProfile(input_file_contents,
853 profile_file.GetFilename(),
854 GetLibCoreDexFileNames()[0]));
855 ProfileCompilationInfo info;
856 ASSERT_TRUE(info.Load(GetFd(profile_file)));
857 // Verify that the profile has matching methods.
858 ScopedObjectAccess soa(Thread::Current());
859 ObjPtr<mirror::Class> klass = GetClass(soa, /*class_loader=*/ nullptr, "Ljava/lang/Math;");
860 ASSERT_TRUE(klass != nullptr);
861 size_t method_count = 0;
862 for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) {
863 if (!method.IsCopied() && method.GetCodeItem() != nullptr) {
864 ++method_count;
865 ProfileCompilationInfo::MethodHotness hotness =
866 info.GetMethodHotness(MethodReference(method.GetDexFile(), method.GetDexMethodIndex()));
867 ASSERT_TRUE(hotness.IsHot()) << method.PrettyMethod();
868 }
869 }
870 EXPECT_GT(method_count, 0u);
871 }
872
JoinProfileLines(const std::vector<std::string> & lines)873 static std::string JoinProfileLines(const std::vector<std::string>& lines) {
874 std::string result = android::base::Join(lines, '\n');
875 return result + '\n';
876 }
877
TEST_F(ProfileAssistantTest,TestBootImageProfile)878 TEST_F(ProfileAssistantTest, TestBootImageProfile) {
879 const std::string core_dex = GetLibCoreDexFileNames()[0];
880
881 std::vector<ScratchFile> profiles;
882
883 // In image with enough clean occurrences.
884 const std::string kCleanClass = "Ljava/lang/CharSequence;";
885 // In image with enough dirty occurrences.
886 const std::string kDirtyClass = "Ljava/lang/Object;";
887 // Not in image becauseof not enough occurrences.
888 const std::string kUncommonCleanClass = "Ljava/lang/Process;";
889 const std::string kUncommonDirtyClass = "Ljava/lang/Package;";
890 // Method that is common and hot. Should end up in profile.
891 const std::string kCommonHotMethod = "Ljava/lang/Comparable;->compareTo(Ljava/lang/Object;)I";
892 // Uncommon method, should not end up in profile
893 const std::string kUncommonMethod = "Ljava/util/HashMap;-><init>()V";
894 // Method that gets marked as hot since it's in multiple profile and marked as startup.
895 const std::string kStartupMethodForUpgrade = "Ljava/util/ArrayList;->clear()V";
896 // Startup method used by a special package which will get a different threshold;
897 const std::string kSpecialPackageStartupMethod =
898 "Ljava/lang/Object;->toString()Ljava/lang/String;";
899 // Method used by a special package which will get a different threshold;
900 const std::string kUncommonSpecialPackageMethod = "Ljava/lang/Object;->hashCode()I";
901 // Denylisted class
902 const std::string kPreloadedDenylistedClass = "Ljava/lang/Thread;";
903
904 // Thresholds for this test.
905 static const size_t kDirtyThreshold = 100;
906 static const size_t kCleanThreshold = 50;
907 static const size_t kPreloadedThreshold = 100;
908 static const size_t kMethodThreshold = 75;
909 static const size_t kSpecialThreshold = 50;
910 const std::string kSpecialPackage = "dex4";
911
912 // Create boot profile content, attributing the classes and methods to different dex files.
913 std::vector<std::string> input_data = {
914 "{dex1}" + kCleanClass,
915 "{dex1}" + kDirtyClass,
916 "{dex1}" + kUncommonCleanClass,
917 "{dex1}H" + kCommonHotMethod,
918 "{dex1}P" + kStartupMethodForUpgrade,
919 "{dex1}" + kUncommonDirtyClass,
920 "{dex1}" + kPreloadedDenylistedClass,
921
922 "{dex2}" + kCleanClass,
923 "{dex2}" + kDirtyClass,
924 "{dex2}P" + kCommonHotMethod,
925 "{dex2}P" + kStartupMethodForUpgrade,
926 "{dex2}" + kUncommonDirtyClass,
927 "{dex2}" + kPreloadedDenylistedClass,
928
929 "{dex3}P" + kUncommonMethod,
930 "{dex3}PS" + kStartupMethodForUpgrade,
931 "{dex3}S" + kCommonHotMethod,
932 "{dex3}S" + kSpecialPackageStartupMethod,
933 "{dex3}" + kDirtyClass,
934 "{dex3}" + kPreloadedDenylistedClass,
935
936 "{dex4}" + kDirtyClass,
937 "{dex4}P" + kCommonHotMethod,
938 "{dex4}S" + kSpecialPackageStartupMethod,
939 "{dex4}P" + kUncommonSpecialPackageMethod,
940 "{dex4}" + kPreloadedDenylistedClass,
941 };
942 std::string input_file_contents = JoinProfileLines(input_data);
943
944 ScratchFile preloaded_class_denylist;
945 std::string denylist_content = DescriptorToDot(kPreloadedDenylistedClass.c_str());
946 EXPECT_TRUE(preloaded_class_denylist.GetFile()->WriteFully(
947 denylist_content.c_str(), denylist_content.length()));
948
949 EXPECT_EQ(0, preloaded_class_denylist.GetFile()->Flush());
950 // Expected data
951 std::vector<std::string> expected_data = {
952 kCleanClass,
953 kDirtyClass,
954 kPreloadedDenylistedClass,
955 "HSP" + kCommonHotMethod,
956 "HS" + kSpecialPackageStartupMethod,
957 "HSP" + kStartupMethodForUpgrade
958 };
959 std::string expected_profile_content = JoinProfileLines(expected_data);
960
961 std::vector<std::string> expected_preloaded_data = {
962 DescriptorToDot(kDirtyClass.c_str())
963 };
964 std::string expected_preloaded_content = JoinProfileLines(expected_preloaded_data);
965
966 ScratchFile profile;
967 EXPECT_TRUE(CreateProfile(input_file_contents,
968 profile.GetFilename(),
969 core_dex,
970 /*for_boot_image=*/ true));
971
972 ProfileCompilationInfo bootProfile(/*for_boot_image=*/ true);
973 EXPECT_TRUE(bootProfile.Load(profile.GetFilename(), /*clear_if_invalid=*/ false));
974
975 // Generate the boot profile.
976 ScratchFile out_profile;
977 ScratchFile out_preloaded_classes;
978 std::vector<std::string> args;
979 args.push_back(GetProfmanCmd());
980 args.push_back("--generate-boot-image-profile");
981 args.push_back("--class-threshold=" + std::to_string(kDirtyThreshold));
982 args.push_back("--clean-class-threshold=" + std::to_string(kCleanThreshold));
983 args.push_back("--method-threshold=" + std::to_string(kMethodThreshold));
984 args.push_back("--preloaded-class-threshold=" + std::to_string(kPreloadedThreshold));
985 args.push_back(
986 "--special-package=" + kSpecialPackage + ":" + std::to_string(kSpecialThreshold));
987 args.push_back("--profile-file=" + profile.GetFilename());
988 args.push_back("--out-profile-path=" + out_profile.GetFilename());
989 args.push_back("--out-preloaded-classes-path=" + out_preloaded_classes.GetFilename());
990 args.push_back("--apk=" + core_dex);
991 args.push_back("--dex-location=" + core_dex);
992 args.push_back("--preloaded-classes-denylist=" + preloaded_class_denylist.GetFilename());
993
994 std::string error;
995 ASSERT_EQ(ExecAndReturnCode(args, &error), 0) << error;
996
997 // Verify the boot profile contents.
998 std::string output_profile_contents;
999 ASSERT_TRUE(android::base::ReadFileToString(
1000 out_profile.GetFilename(), &output_profile_contents));
1001 ASSERT_EQ(output_profile_contents, expected_profile_content);
1002
1003 // Verify the preloaded classes content.
1004 std::string output_preloaded_contents;
1005 ASSERT_TRUE(android::base::ReadFileToString(
1006 out_preloaded_classes.GetFilename(), &output_preloaded_contents));
1007 ASSERT_EQ(output_preloaded_contents, expected_preloaded_content);
1008 }
1009
TEST_F(ProfileAssistantTest,TestBootImageProfileWith2RawProfiles)1010 TEST_F(ProfileAssistantTest, TestBootImageProfileWith2RawProfiles) {
1011 const std::string core_dex = GetLibCoreDexFileNames()[0];
1012
1013 std::vector<ScratchFile> profiles;
1014
1015 const std::string kCommonClassUsedByDex1 = "Ljava/lang/CharSequence;";
1016 const std::string kCommonClassUsedByDex1Dex2 = "Ljava/lang/Object;";
1017 const std::string kUncommonClass = "Ljava/lang/Process;";
1018 const std::string kCommonHotMethodUsedByDex1 =
1019 "Ljava/lang/Comparable;->compareTo(Ljava/lang/Object;)I";
1020 const std::string kCommonHotMethodUsedByDex1Dex2 = "Ljava/lang/Object;->hashCode()I";
1021 const std::string kUncommonHotMethod = "Ljava/util/HashMap;-><init>()V";
1022
1023
1024 // Thresholds for this test.
1025 static const size_t kDirtyThreshold = 100;
1026 static const size_t kCleanThreshold = 100;
1027 static const size_t kMethodThreshold = 100;
1028
1029 // Create boot profile content, attributing the classes and methods to different dex files.
1030 std::vector<std::string> input_data1 = {
1031 "{dex1}" + kCommonClassUsedByDex1,
1032 "{dex1}" + kCommonClassUsedByDex1Dex2,
1033 "{dex1}" + kUncommonClass,
1034 "{dex1}H" + kCommonHotMethodUsedByDex1Dex2,
1035 "{dex1}" + kCommonHotMethodUsedByDex1,
1036 };
1037 std::vector<std::string> input_data2 = {
1038 "{dex1}" + kCommonClassUsedByDex1,
1039 "{dex2}" + kCommonClassUsedByDex1Dex2,
1040 "{dex1}H" + kCommonHotMethodUsedByDex1,
1041 "{dex2}" + kCommonHotMethodUsedByDex1Dex2,
1042 "{dex1}" + kUncommonHotMethod,
1043 };
1044 std::string input_file_contents1 = JoinProfileLines(input_data1);
1045 std::string input_file_contents2 = JoinProfileLines(input_data2);
1046
1047 // Expected data
1048 std::vector<std::string> expected_data = {
1049 kCommonClassUsedByDex1,
1050 kCommonClassUsedByDex1Dex2,
1051 "H" + kCommonHotMethodUsedByDex1,
1052 "H" + kCommonHotMethodUsedByDex1Dex2
1053 };
1054 std::string expected_profile_content = JoinProfileLines(expected_data);
1055
1056 ScratchFile profile1;
1057 ScratchFile profile2;
1058 EXPECT_TRUE(CreateProfile(input_file_contents1,
1059 profile1.GetFilename(),
1060 core_dex,
1061 /*for_boot_image=*/ true));
1062 EXPECT_TRUE(CreateProfile(input_file_contents2,
1063 profile2.GetFilename(),
1064 core_dex,
1065 /*for_boot_image=*/ true));
1066
1067 ProfileCompilationInfo boot_profile1(/*for_boot_image=*/ true);
1068 ProfileCompilationInfo boot_profile2(/*for_boot_image=*/ true);
1069 EXPECT_TRUE(boot_profile1.Load(profile1.GetFilename(), /*clear_if_invalid=*/ false));
1070 EXPECT_TRUE(boot_profile2.Load(profile2.GetFilename(), /*clear_if_invalid=*/ false));
1071
1072 // Generate the boot profile.
1073 ScratchFile out_profile;
1074 ScratchFile out_preloaded_classes;
1075 std::vector<std::string> args;
1076 args.push_back(GetProfmanCmd());
1077 args.push_back("--generate-boot-image-profile");
1078 args.push_back("--class-threshold=" + std::to_string(kDirtyThreshold));
1079 args.push_back("--clean-class-threshold=" + std::to_string(kCleanThreshold));
1080 args.push_back("--method-threshold=" + std::to_string(kMethodThreshold));
1081 args.push_back("--profile-file=" + profile1.GetFilename());
1082 args.push_back("--profile-file=" + profile2.GetFilename());
1083 args.push_back("--out-profile-path=" + out_profile.GetFilename());
1084 args.push_back("--out-preloaded-classes-path=" + out_preloaded_classes.GetFilename());
1085 args.push_back("--apk=" + core_dex);
1086 args.push_back("--dex-location=" + core_dex);
1087
1088 std::string error;
1089 ASSERT_EQ(ExecAndReturnCode(args, &error), 0) << error;
1090
1091 // Verify the boot profile contents.
1092 std::string output_profile_contents;
1093 ASSERT_TRUE(android::base::ReadFileToString(
1094 out_profile.GetFilename(), &output_profile_contents));
1095 ASSERT_EQ(output_profile_contents, expected_profile_content);
1096 }
1097
TEST_F(ProfileAssistantTest,TestProfileCreationOneNotMatched)1098 TEST_F(ProfileAssistantTest, TestProfileCreationOneNotMatched) {
1099 // Class names put here need to be in sorted order.
1100 std::vector<std::string> class_names = {
1101 "Ldoesnt/match/this/one;",
1102 "Ljava/lang/Comparable;",
1103 "Ljava/lang/Object;"
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 class_names[1] + std::string("\n") +
1113 class_names[2] + std::string("\n");
1114 ASSERT_EQ(output_file_contents, expected_contents);
1115 }
1116
TEST_F(ProfileAssistantTest,TestProfileCreationNoneMatched)1117 TEST_F(ProfileAssistantTest, TestProfileCreationNoneMatched) {
1118 // Class names put here need to be in sorted order.
1119 std::vector<std::string> class_names = {
1120 "Ldoesnt/match/this/one;",
1121 "Ldoesnt/match/this/one/either;",
1122 "Lnor/this/one;"
1123 };
1124 std::string input_file_contents;
1125 for (std::string& class_name : class_names) {
1126 input_file_contents += class_name + std::string("\n");
1127 }
1128 std::string output_file_contents;
1129 ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents));
1130 std::string expected_contents("");
1131 ASSERT_EQ(output_file_contents, expected_contents);
1132 }
1133
1134 // Test that we can dump profiles in a way they can be re-constituted.
1135 // Test goes 'txt -> prof -> txt -> prof' and then compares the two profs.
TEST_F(ProfileAssistantTest,TestProfileRoundTrip)1136 TEST_F(ProfileAssistantTest, TestProfileRoundTrip) {
1137 // Create the profile content.
1138 std::vector<std::string_view> methods = {
1139 "HLTestInline;->inlineMonomorphic(LSuper;)I+LSubA;",
1140 "HLTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;",
1141 "HLTestInline;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;",
1142 "HLTestInline;->inlineMissingTypes(LSuper;)I+missing_types",
1143 "HLTestInline;->noInlineCache(LSuper;)I",
1144 "HLTestInline;->inlineMultiMonomorphic(LSuper;LSecret;)I+]LSuper;LSubA;]LSecret;LSubB;",
1145 "HLTestInline;->inlineMultiPolymorphic(LSuper;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;]LSecret;LSubB;,LSubC;",
1146 "HLTestInline;->inlineMultiMegamorphic(LSuper;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;,LSubD;,LSubE;]LSecret;megamorphic_types",
1147 "HLTestInline;->inlineMultiMissingTypes(LSuper;LSecret;)I+]LSuper;missing_types]LSecret;missing_types",
1148 "HLTestInline;->inlineTriplePolymorphic(LSuper;LSecret;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;]LSecret;LSubB;,LSubC;",
1149 "HLTestInline;->noInlineCacheMulti(LSuper;LSecret;)I",
1150 };
1151 std::ostringstream input_file_contents;
1152 for (const std::string_view& m : methods) {
1153 input_file_contents << m << "\n";
1154 }
1155
1156 // Create the profile and save it to disk.
1157 ScratchFile profile_file;
1158 ASSERT_TRUE(CreateProfile(input_file_contents.str(),
1159 profile_file.GetFilename(),
1160 GetTestDexFileName("ProfileTestMultiDex")));
1161
1162 // Dump the file back into text.
1163 std::string text_two;
1164 ASSERT_TRUE(DumpClassesAndMethods(
1165 profile_file.GetFilename(), &text_two, GetTestDexFileName("ProfileTestMultiDex")));
1166
1167 // Create another profile and save it to the disk as well.
1168 ScratchFile profile_two;
1169 ASSERT_TRUE(CreateProfile(
1170 text_two, profile_two.GetFilename(), GetTestDexFileName("ProfileTestMultiDex")));
1171
1172 // These two profiles should be bit-identical.
1173 // TODO We could compare the 'text_two' to the methods but since the order is
1174 // arbitrary for many parts and there are multiple 'correct' dumps we'd need
1175 // to basically parse everything and this is simply easier.
1176 std::string error;
1177 std::vector<std::string> args { kIsTargetBuild ? "/system/bin/cmp" : "/usr/bin/cmp",
1178 "-s",
1179 profile_file.GetFilename(),
1180 profile_two.GetFilename() };
1181 ASSERT_EQ(ExecAndReturnCode(args, &error), 0) << error << " from " << text_two;
1182 }
1183
1184
1185 // Test that we can dump profiles in a way they can be re-constituted and
1186 // annotations don't interfere. Test goes 'txt -> ProfileWithAnnotations -> txt
1187 // -> prof' and then compares that to one that is 'txt ->
1188 // prof_without_annotations'.
TEST_F(ProfileAssistantTest,TestProfileRoundTripWithAnnotations)1189 TEST_F(ProfileAssistantTest, TestProfileRoundTripWithAnnotations) {
1190 // Create the profile content.
1191 std::vector<std::string_view> methods = {
1192 "HLTestInline;->inlineMonomorphic(LSuper;)I+LSubA;",
1193 "HLTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;",
1194 "HLTestInline;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;",
1195 "HLTestInline;->inlineMissingTypes(LSuper;)I+missing_types",
1196 "HLTestInline;->noInlineCache(LSuper;)I",
1197 "HLTestInline;->inlineMultiMonomorphic(LSuper;LSecret;)I+]LSuper;LSubA;]LSecret;LSubB;",
1198 "HLTestInline;->inlineMultiPolymorphic(LSuper;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;]LSecret;LSubB;,LSubC;",
1199 "HLTestInline;->inlineMultiMegamorphic(LSuper;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;,LSubD;,LSubE;]LSecret;megamorphic_types",
1200 "HLTestInline;->inlineMultiMissingTypes(LSuper;LSecret;)I+]LSuper;missing_types]LSecret;missing_types",
1201 "HLTestInline;->inlineTriplePolymorphic(LSuper;LSecret;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;]LSecret;LSubB;,LSubC;",
1202 "HLTestInline;->noInlineCacheMulti(LSuper;LSecret;)I",
1203 };
1204 std::ostringstream no_annotation_input_file_contents;
1205 std::ostringstream with_annotation_input_file_contents;
1206 for (const std::string_view& m : methods) {
1207 no_annotation_input_file_contents << m << "\n";
1208 with_annotation_input_file_contents << "{foobar}" << m << "\n";
1209 }
1210
1211 // Create the profile and save it to disk.
1212 ScratchFile with_annotation_profile_file;
1213 ASSERT_TRUE(CreateProfile(with_annotation_input_file_contents.str(),
1214 with_annotation_profile_file.GetFilename(),
1215 GetTestDexFileName("ProfileTestMultiDex")));
1216
1217 ScratchFile no_annotation_profile_file;
1218 ASSERT_TRUE(CreateProfile(no_annotation_input_file_contents.str(),
1219 no_annotation_profile_file.GetFilename(),
1220 GetTestDexFileName("ProfileTestMultiDex")));
1221
1222 // Dump the file back into text.
1223 std::string text_two;
1224 ASSERT_TRUE(DumpClassesAndMethods(with_annotation_profile_file.GetFilename(),
1225 &text_two,
1226 GetTestDexFileName("ProfileTestMultiDex")));
1227
1228 // Create another profile and save it to the disk as well.
1229 ScratchFile profile_two;
1230 ASSERT_TRUE(CreateProfile(
1231 text_two, profile_two.GetFilename(), GetTestDexFileName("ProfileTestMultiDex")));
1232
1233 // These two profiles should be bit-identical.
1234 // TODO We could compare the 'text_two' to the methods but since the order is
1235 // arbitrary for many parts and there are multiple 'correct' dumps we'd need
1236 // to basically parse everything and this is simply easier.
1237 std::string error;
1238 std::vector<std::string> args { kIsTargetBuild ? "/system/bin/cmp" : "/usr/bin/cmp",
1239 "-s",
1240 no_annotation_profile_file.GetFilename(),
1241 profile_two.GetFilename() };
1242 ASSERT_EQ(ExecAndReturnCode(args, &error), 0) << error << " from " << text_two;
1243 }
1244
TEST_F(ProfileAssistantTest,TestProfileCreateInlineCache)1245 TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) {
1246 // Create the profile content.
1247 std::vector<std::string_view> methods = {
1248 "HLTestInline;->inlineMonomorphic(LSuper;)I+LSubA;",
1249 "HLTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;",
1250 "HLTestInline;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;",
1251 "HLTestInline;->inlineMissingTypes(LSuper;)I+missing_types",
1252 "HLTestInline;->noInlineCache(LSuper;)I",
1253 "HLTestInline;->inlineMultiMonomorphic(LSuper;LSecret;)I+]LSuper;LSubA;]LSecret;LSubB;",
1254 "HLTestInline;->inlineMultiPolymorphic(LSuper;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;]LSecret;LSubB;,LSubC;",
1255 "HLTestInline;->inlineMultiMegamorphic(LSuper;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;,LSubD;,LSubE;]LSecret;LSubA;,LSubB;,LSubC;,LSubD;,LSubE;",
1256 "HLTestInline;->inlineMultiMissingTypes(LSuper;LSecret;)I+]LSuper;missing_types]LSecret;missing_types",
1257 "HLTestInline;->inlineTriplePolymorphic(LSuper;LSecret;LSecret;)I+]LSuper;LSubA;,LSubB;,LSubC;]LSecret;LSubB;,LSubC;",
1258 "HLTestInline;->noInlineCacheMulti(LSuper;LSecret;)I",
1259 };
1260 std::ostringstream input_file_contents;
1261 for (const std::string_view& m : methods) {
1262 input_file_contents << m << "\n";
1263 }
1264
1265 // Create the profile and save it to disk.
1266 ScratchFile profile_file;
1267 ASSERT_TRUE(CreateProfile(input_file_contents.str(),
1268 profile_file.GetFilename(),
1269 GetTestDexFileName("ProfileTestMultiDex")));
1270
1271 // Load the profile from disk.
1272 ProfileCompilationInfo info;
1273 ASSERT_TRUE(info.Load(GetFd(profile_file)));
1274
1275 // Load the dex files and verify that the profile contains the expected methods info.
1276 ScopedObjectAccess soa(Thread::Current());
1277 jobject class_loader = LoadDex("ProfileTestMultiDex");
1278 ASSERT_NE(class_loader, nullptr);
1279
1280 StackHandleScope<5> hs(soa.Self());
1281 Handle<mirror::Class> super_klass = hs.NewHandle(GetClass(soa, class_loader, "LSuper;"));
1282 Handle<mirror::Class> secret_klass = hs.NewHandle(GetClass(soa, class_loader, "LSecret;"));
1283 Handle<mirror::Class> sub_a = hs.NewHandle(GetClass(soa, class_loader, "LSubA;"));
1284 Handle<mirror::Class> sub_b = hs.NewHandle(GetClass(soa, class_loader, "LSubB;"));
1285 Handle<mirror::Class> sub_c = hs.NewHandle(GetClass(soa, class_loader, "LSubC;"));
1286
1287 ASSERT_TRUE(super_klass != nullptr);
1288 ASSERT_TRUE(secret_klass != nullptr);
1289 ASSERT_TRUE(sub_a != nullptr);
1290 ASSERT_TRUE(sub_b != nullptr);
1291 ASSERT_TRUE(sub_c != nullptr);
1292
1293 {
1294 // Verify that method inlineMonomorphic has the expected inline caches and nothing else.
1295 ArtMethod* inline_monomorphic = GetVirtualMethod(class_loader,
1296 "LTestInline;",
1297 "inlineMonomorphic");
1298 ASSERT_TRUE(inline_monomorphic != nullptr);
1299 TypeReferenceSet expected_monomorphic;
1300 expected_monomorphic.insert(MakeTypeReference(sub_a.Get()));
1301 AssertInlineCaches(inline_monomorphic,
1302 expected_monomorphic,
1303 info,
1304 /*is_megamorphic=*/false,
1305 /*is_missing_types=*/false);
1306 }
1307
1308 {
1309 // Verify that method inlinePolymorphic has the expected inline caches and nothing else.
1310 ArtMethod* inline_polymorhic = GetVirtualMethod(class_loader,
1311 "LTestInline;",
1312 "inlinePolymorphic");
1313 ASSERT_TRUE(inline_polymorhic != nullptr);
1314 TypeReferenceSet expected_polymorphic;
1315 expected_polymorphic.insert(MakeTypeReference(sub_a.Get()));
1316 expected_polymorphic.insert(MakeTypeReference(sub_b.Get()));
1317 expected_polymorphic.insert(MakeTypeReference(sub_c.Get()));
1318 AssertInlineCaches(inline_polymorhic,
1319 expected_polymorphic,
1320 info,
1321 /*is_megamorphic=*/false,
1322 /*is_missing_types=*/false);
1323 }
1324
1325 {
1326 // Verify that method inlineMegamorphic has the expected inline caches and nothing else.
1327 ArtMethod* inline_megamorphic = GetVirtualMethod(class_loader,
1328 "LTestInline;",
1329 "inlineMegamorphic");
1330 ASSERT_TRUE(inline_megamorphic != nullptr);
1331 TypeReferenceSet expected_megamorphic;
1332 AssertInlineCaches(inline_megamorphic,
1333 expected_megamorphic,
1334 info,
1335 /*is_megamorphic=*/true,
1336 /*is_missing_types=*/false);
1337 }
1338
1339 {
1340 // Verify that method inlineMegamorphic has the expected inline caches and nothing else.
1341 ArtMethod* inline_missing_types = GetVirtualMethod(class_loader,
1342 "LTestInline;",
1343 "inlineMissingTypes");
1344 ASSERT_TRUE(inline_missing_types != nullptr);
1345 TypeReferenceSet expected_missing_Types;
1346 AssertInlineCaches(inline_missing_types,
1347 expected_missing_Types,
1348 info,
1349 /*is_megamorphic=*/false,
1350 /*is_missing_types=*/true);
1351 }
1352
1353 {
1354 // Verify that method noInlineCache has no inline caches in the profile.
1355 ArtMethod* no_inline_cache = GetVirtualMethod(class_loader, "LTestInline;", "noInlineCache");
1356 ASSERT_TRUE(no_inline_cache != nullptr);
1357 ProfileCompilationInfo::MethodHotness hotness_no_inline_cache = info.GetMethodHotness(
1358 MethodReference(no_inline_cache->GetDexFile(), no_inline_cache->GetDexMethodIndex()));
1359 ASSERT_TRUE(hotness_no_inline_cache.IsHot());
1360 ASSERT_TRUE(hotness_no_inline_cache.GetInlineCacheMap()->empty());
1361 }
1362
1363 {
1364 // Verify that method inlineMonomorphic has the expected inline caches and nothing else.
1365 ArtMethod* inline_monomorphic = GetVirtualMethod(class_loader,
1366 "LTestInline;",
1367 "inlineMultiMonomorphic");
1368 ASSERT_TRUE(inline_monomorphic != nullptr);
1369 TypeReferenceSet expected_monomorphic_super;
1370 TypeReferenceSet expected_monomorphic_secret;
1371 expected_monomorphic_super.insert(MakeTypeReference(sub_a.Get()));
1372 expected_monomorphic_secret.insert(MakeTypeReference(sub_b.Get()));
1373 AssertInlineCaches(inline_monomorphic,
1374 GetDexPcOfCallTo(inline_monomorphic, super_klass),
1375 expected_monomorphic_super,
1376 info,
1377 /*is_megamorphic=*/false,
1378 /*is_missing_types=*/false);
1379 AssertInlineCaches(inline_monomorphic,
1380 GetDexPcOfCallTo(inline_monomorphic, secret_klass),
1381 expected_monomorphic_secret,
1382 info,
1383 /*is_megamorphic=*/false,
1384 /*is_missing_types=*/false);
1385 }
1386
1387 {
1388 // Verify that method inlinePolymorphic has the expected inline caches and nothing else.
1389 ArtMethod* inline_polymorhic = GetVirtualMethod(class_loader,
1390 "LTestInline;",
1391 "inlineMultiPolymorphic");
1392 ASSERT_TRUE(inline_polymorhic != nullptr);
1393 TypeReferenceSet expected_polymorphic_super;
1394 expected_polymorphic_super.insert(MakeTypeReference(sub_a.Get()));
1395 expected_polymorphic_super.insert(MakeTypeReference(sub_b.Get()));
1396 expected_polymorphic_super.insert(MakeTypeReference(sub_c.Get()));
1397 TypeReferenceSet expected_polymorphic_secret;
1398 expected_polymorphic_secret.insert(MakeTypeReference(sub_b.Get()));
1399 expected_polymorphic_secret.insert(MakeTypeReference(sub_c.Get()));
1400 AssertInlineCaches(inline_polymorhic,
1401 GetDexPcOfCallTo(inline_polymorhic, super_klass),
1402 expected_polymorphic_super,
1403 info,
1404 /*is_megamorphic=*/false,
1405 /*is_missing_types=*/false);
1406 AssertInlineCaches(inline_polymorhic,
1407 GetDexPcOfCallTo(inline_polymorhic, secret_klass),
1408 expected_polymorphic_secret,
1409 info,
1410 /*is_megamorphic=*/false,
1411 /*is_missing_types=*/false);
1412 }
1413
1414 {
1415 // Verify that method inlinePolymorphic has the expected inline caches and nothing else.
1416 ArtMethod* inline_polymorhic = GetVirtualMethod(class_loader,
1417 "LTestInline;",
1418 "inlineTriplePolymorphic");
1419 ASSERT_TRUE(inline_polymorhic != nullptr);
1420 TypeReferenceSet expected_polymorphic_super;
1421 expected_polymorphic_super.insert(MakeTypeReference(sub_a.Get()));
1422 expected_polymorphic_super.insert(MakeTypeReference(sub_b.Get()));
1423 expected_polymorphic_super.insert(MakeTypeReference(sub_c.Get()));
1424 TypeReferenceSet expected_polymorphic_secret;
1425 expected_polymorphic_secret.insert(MakeTypeReference(sub_b.Get()));
1426 expected_polymorphic_secret.insert(MakeTypeReference(sub_c.Get()));
1427 AssertInlineCaches(inline_polymorhic,
1428 GetDexPcOfCallTo(inline_polymorhic, super_klass),
1429 expected_polymorphic_super,
1430 info,
1431 /*is_megamorphic=*/false,
1432 /*is_missing_types=*/false);
1433 uint16_t first_call = GetDexPcOfCallTo(inline_polymorhic, secret_klass);
1434 AssertInlineCaches(inline_polymorhic,
1435 first_call,
1436 expected_polymorphic_secret,
1437 info,
1438 /*is_megamorphic=*/false,
1439 /*is_missing_types=*/false);
1440 uint16_t second_call = GetDexPcOfCallTo(inline_polymorhic, secret_klass, first_call);
1441 ASSERT_LT(first_call, second_call);
1442 AssertInlineCaches(inline_polymorhic,
1443 second_call,
1444 expected_polymorphic_secret,
1445 info,
1446 /*is_megamorphic=*/false,
1447 /*is_missing_types=*/false);
1448 }
1449
1450 {
1451 // Verify that method inlineMegamorphic has the expected inline caches and nothing else.
1452 ArtMethod* inline_megamorphic = GetVirtualMethod(class_loader,
1453 "LTestInline;",
1454 "inlineMultiMegamorphic");
1455 ASSERT_TRUE(inline_megamorphic != nullptr);
1456 TypeReferenceSet expected_megamorphic;
1457 AssertInlineCaches(inline_megamorphic,
1458 GetDexPcOfCallTo(inline_megamorphic, super_klass),
1459 expected_megamorphic,
1460 info,
1461 /*is_megamorphic=*/true,
1462 /*is_missing_types=*/false);
1463 AssertInlineCaches(inline_megamorphic,
1464 GetDexPcOfCallTo(inline_megamorphic, secret_klass),
1465 expected_megamorphic,
1466 info,
1467 /*is_megamorphic=*/true,
1468 /*is_missing_types=*/false);
1469 }
1470
1471 {
1472 // Verify that method inlineMegamorphic has the expected inline caches and nothing else.
1473 ArtMethod* inline_missing_types = GetVirtualMethod(class_loader,
1474 "LTestInline;",
1475 "inlineMultiMissingTypes");
1476 ASSERT_TRUE(inline_missing_types != nullptr);
1477 TypeReferenceSet expected_missing_Types;
1478 AssertInlineCaches(inline_missing_types,
1479 GetDexPcOfCallTo(inline_missing_types, super_klass),
1480 expected_missing_Types,
1481 info,
1482 /*is_megamorphic=*/false,
1483 /*is_missing_types=*/true);
1484 AssertInlineCaches(inline_missing_types,
1485 GetDexPcOfCallTo(inline_missing_types, secret_klass),
1486 expected_missing_Types,
1487 info,
1488 /*is_megamorphic=*/false,
1489 /*is_missing_types=*/true);
1490 }
1491
1492 {
1493 // Verify that method noInlineCacheMulti has no inline caches in the profile.
1494 ArtMethod* no_inline_cache =
1495 GetVirtualMethod(class_loader, "LTestInline;", "noInlineCacheMulti");
1496 ASSERT_TRUE(no_inline_cache != nullptr);
1497 ProfileCompilationInfo::MethodHotness hotness_no_inline_cache = info.GetMethodHotness(
1498 MethodReference(no_inline_cache->GetDexFile(), no_inline_cache->GetDexMethodIndex()));
1499 ASSERT_TRUE(hotness_no_inline_cache.IsHot());
1500 ASSERT_TRUE(hotness_no_inline_cache.GetInlineCacheMap()->empty());
1501 }
1502 }
1503
TEST_F(ProfileAssistantTest,MergeProfilesWithDifferentDexOrder)1504 TEST_F(ProfileAssistantTest, MergeProfilesWithDifferentDexOrder) {
1505 ScratchFile profile1;
1506 ScratchFile reference_profile;
1507
1508 std::vector<int> profile_fds({GetFd(profile1)});
1509 int reference_profile_fd = GetFd(reference_profile);
1510
1511 // The new profile info will contain the methods with indices 0-100.
1512 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
1513 ProfileCompilationInfo info1;
1514 SetupProfile(dex1, dex2, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1,
1515 /*start_method_index=*/0, /*reverse_dex_write_order=*/false);
1516
1517 // The reference profile info will contain the methods with indices 50-150.
1518 // When setting up the profile reverse the order in which the dex files
1519 // are added to the profile. This will verify that profman merges profiles
1520 // with a different dex order correctly.
1521 const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
1522 ProfileCompilationInfo reference_info;
1523 SetupProfile(dex1, dex2, kNumberOfMethodsAlreadyCompiled, 0, reference_profile,
1524 &reference_info, kNumberOfMethodsToEnableCompilation / 2, /*reverse_dex_write_order=*/true);
1525
1526 // We should advise compilation.
1527 ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
1528
1529 // The resulting compilation info must be equal to the merge of the inputs.
1530 ProfileCompilationInfo result;
1531 ASSERT_TRUE(result.Load(reference_profile_fd));
1532
1533 ProfileCompilationInfo expected;
1534 ASSERT_TRUE(expected.MergeWith(reference_info));
1535 ASSERT_TRUE(expected.MergeWith(info1));
1536 ASSERT_TRUE(expected.Equals(result));
1537
1538 // The information from profile must remain the same.
1539 CheckProfileInfo(profile1, info1);
1540 }
1541
TEST_F(ProfileAssistantTest,TestProfileCreateWithSubtype)1542 TEST_F(ProfileAssistantTest, TestProfileCreateWithSubtype) {
1543 // Create the profile content.
1544 std::vector<std::string> profile_methods = {
1545 "HLTestInlineSubtype;->inlineMonomorphic(LSuper;)I+]LSuper;LSubA;",
1546 };
1547 std::string input_file_contents;
1548 for (std::string& m : profile_methods) {
1549 input_file_contents += m + std::string("\n");
1550 }
1551
1552 // Create the profile and save it to disk.
1553 ScratchFile profile_file;
1554 std::string dex_filename = GetTestDexFileName("ProfileTestMultiDex");
1555 ASSERT_TRUE(CreateProfile(input_file_contents, profile_file.GetFilename(), dex_filename));
1556
1557 // Load the profile from disk.
1558 ProfileCompilationInfo info;
1559 ASSERT_TRUE(info.Load(GetFd(profile_file)));
1560 LOG(ERROR) << profile_file.GetFilename();
1561
1562 // Load the dex files and verify that the profile contains the expected
1563 // methods info.
1564 ScopedObjectAccess soa(Thread::Current());
1565 jobject class_loader = LoadDex("ProfileTestMultiDex");
1566 ASSERT_NE(class_loader, nullptr);
1567
1568 // NB This is the supertype of the declared line!
1569 ArtMethod* inline_monomorphic_super =
1570 GetVirtualMethod(class_loader, "LTestInline;", "inlineMonomorphic");
1571 const DexFile* dex_file = inline_monomorphic_super->GetDexFile();
1572
1573 // Verify that the inline cache is present in the superclass
1574 ProfileCompilationInfo::MethodHotness hotness_super = info.GetMethodHotness(
1575 MethodReference(dex_file, inline_monomorphic_super->GetDexMethodIndex()));
1576 ASSERT_TRUE(hotness_super.IsHot());
1577 const ProfileCompilationInfo::InlineCacheMap* inline_caches = hotness_super.GetInlineCacheMap();
1578 ASSERT_EQ(inline_caches->size(), 1u);
1579 const ProfileCompilationInfo::DexPcData& dex_pc_data = inline_caches->begin()->second;
1580 dex::TypeIndex target_type_index(dex_file->GetIndexForTypeId(*dex_file->FindTypeId("LSubA;")));
1581 ASSERT_EQ(1u, dex_pc_data.classes.size());
1582 ASSERT_EQ(target_type_index, *dex_pc_data.classes.begin());
1583
1584 // Verify that the method is present in subclass but there are no
1585 // inline-caches (since there is no code).
1586 const dex::MethodId& super_method_id =
1587 dex_file->GetMethodId(inline_monomorphic_super->GetDexMethodIndex());
1588 uint32_t sub_method_index = dex_file->GetIndexForMethodId(
1589 *dex_file->FindMethodId(*dex_file->FindTypeId("LTestInlineSubtype;"),
1590 dex_file->GetStringId(super_method_id.name_idx_),
1591 dex_file->GetProtoId(super_method_id.proto_idx_)));
1592 ProfileCompilationInfo::MethodHotness hotness_sub =
1593 info.GetMethodHotness(MethodReference(dex_file, sub_method_index));
1594 ASSERT_TRUE(hotness_sub.IsHot());
1595 ASSERT_EQ(hotness_sub.GetInlineCacheMap()->size(), 0u);
1596 }
1597
TEST_F(ProfileAssistantTest,TestProfileCreateWithSubtypeAndDump)1598 TEST_F(ProfileAssistantTest, TestProfileCreateWithSubtypeAndDump) {
1599 // Create the profile content.
1600 std::vector<std::string> profile_methods = {
1601 "HLTestInlineSubtype;->inlineMonomorphic(LSuper;)I+]LSuper;LSubA;",
1602 };
1603 std::string input_file_contents;
1604 for (std::string& m : profile_methods) {
1605 input_file_contents += m + std::string("\n");
1606 }
1607
1608 // Create the profile and save it to disk.
1609 ScratchFile profile_file;
1610 std::string dex_filename = GetTestDexFileName("ProfileTestMultiDex");
1611 ASSERT_TRUE(CreateProfile(input_file_contents, profile_file.GetFilename(), dex_filename));
1612
1613 std::string dump_ic;
1614 ASSERT_TRUE(DumpClassesAndMethods(
1615 profile_file.GetFilename(), &dump_ic, GetTestDexFileName("ProfileTestMultiDex")));
1616
1617 std::vector<std::string> lines;
1618 std::stringstream dump_stream(dump_ic);
1619 std::string cur;
1620 while (std::getline(dump_stream, cur, '\n')) {
1621 lines.push_back(std::move(cur));
1622 }
1623
1624 EXPECT_EQ(lines.size(), 2u);
1625 EXPECT_TRUE(std::find(lines.cbegin(),
1626 lines.cend(),
1627 "HLTestInline;->inlineMonomorphic(LSuper;)I+]LSuper;LSubA;") !=
1628 lines.cend());
1629 EXPECT_TRUE(std::find(lines.cbegin(),
1630 lines.cend(),
1631 "HLTestInlineSubtype;->inlineMonomorphic(LSuper;)I") != lines.cend());
1632 }
1633
TEST_F(ProfileAssistantTest,TestProfileCreateWithInvalidData)1634 TEST_F(ProfileAssistantTest, TestProfileCreateWithInvalidData) {
1635 // Create the profile content.
1636 std::vector<std::string> profile_methods = {
1637 "HLTestInline;->inlineMonomorphic(LSuper;)I+invalid_class", // Invalid descriptor for IC.
1638 "HLTestInline;->invalid_method", // Invalid method spec (no signature).
1639 "invalid_class", // Invalid descriptor.
1640 };
1641 std::string input_file_contents;
1642 for (std::string& m : profile_methods) {
1643 input_file_contents += m + std::string("\n");
1644 }
1645
1646 // Create the profile and save it to disk.
1647 ScratchFile profile_file;
1648 std::string dex_filename = GetTestDexFileName("ProfileTestMultiDex");
1649 ASSERT_TRUE(CreateProfile(input_file_contents,
1650 profile_file.GetFilename(),
1651 dex_filename));
1652
1653 // Load the profile from disk.
1654 ProfileCompilationInfo info;
1655 ASSERT_TRUE(info.Load(GetFd(profile_file)));
1656
1657 // Load the dex files and verify that the profile contains the expected methods info.
1658 ScopedObjectAccess soa(Thread::Current());
1659 jobject class_loader = LoadDex("ProfileTestMultiDex");
1660 ASSERT_NE(class_loader, nullptr);
1661
1662 ArtMethod* inline_monomorphic = GetVirtualMethod(class_loader,
1663 "LTestInline;",
1664 "inlineMonomorphic");
1665 const DexFile* dex_file = inline_monomorphic->GetDexFile();
1666
1667 // Invalid descriptor in IC results in rejection of the entire line.
1668 ProfileCompilationInfo::MethodHotness hotness =
1669 info.GetMethodHotness(MethodReference(dex_file, inline_monomorphic->GetDexMethodIndex()));
1670 ASSERT_FALSE(hotness.IsHot());
1671
1672 // No data was recorded, so the dex file does not appear in the profile.
1673 // TODO: Record all dex files passed to `profman` in the profile. Note that
1674 // this makes sense only if there are no annotations, otherwise we do not
1675 // know what annotation to use with each dex file.
1676 std::set<dex::TypeIndex> classes;
1677 std::set<uint16_t> hot_methods;
1678 std::set<uint16_t> startup_methods;
1679 std::set<uint16_t> post_start_methods;
1680 ASSERT_FALSE(info.GetClassesAndMethods(*dex_file,
1681 &classes,
1682 &hot_methods,
1683 &startup_methods,
1684 &post_start_methods));
1685 }
1686
TEST_F(ProfileAssistantTest,DumpOnly)1687 TEST_F(ProfileAssistantTest, DumpOnly) {
1688 ScratchFile profile;
1689
1690 const uint32_t kNumberOfMethods = 64;
1691 std::vector<uint32_t> hot_methods;
1692 std::vector<uint32_t> startup_methods;
1693 std::vector<uint32_t> post_startup_methods;
1694 for (size_t i = 0; i < kNumberOfMethods; ++i) {
1695 if (i % 2 == 0) {
1696 hot_methods.push_back(i);
1697 }
1698 if (i % 3 == 1) {
1699 startup_methods.push_back(i);
1700 }
1701 if (i % 4 == 2) {
1702 post_startup_methods.push_back(i);
1703 }
1704 }
1705 EXPECT_GT(hot_methods.size(), 0u);
1706 EXPECT_GT(startup_methods.size(), 0u);
1707 EXPECT_GT(post_startup_methods.size(), 0u);
1708 ProfileCompilationInfo info1;
1709 SetupBasicProfile(dex1,
1710 hot_methods,
1711 startup_methods,
1712 post_startup_methods,
1713 profile,
1714 &info1);
1715 std::string output;
1716 DumpOnly(profile.GetFilename(), &output);
1717 const size_t hot_offset = output.find("hot methods:");
1718 const size_t startup_offset = output.find("startup methods:");
1719 const size_t post_startup_offset = output.find("post startup methods:");
1720 const size_t classes_offset = output.find("classes:");
1721 ASSERT_NE(hot_offset, std::string::npos);
1722 ASSERT_NE(startup_offset, std::string::npos);
1723 ASSERT_NE(post_startup_offset, std::string::npos);
1724 ASSERT_LT(hot_offset, startup_offset);
1725 ASSERT_LT(startup_offset, post_startup_offset);
1726 // Check the actual contents of the dump by looking at the offsets of the methods.
1727 for (uint32_t m : hot_methods) {
1728 const size_t pos = output.find(std::to_string(m) + "[],", hot_offset);
1729 ASSERT_NE(pos, std::string::npos) << output;
1730 EXPECT_LT(pos, startup_offset) << output;
1731 }
1732 for (uint32_t m : startup_methods) {
1733 const size_t pos = output.find(std::to_string(m) + ",", startup_offset);
1734 ASSERT_NE(pos, std::string::npos) << output;
1735 EXPECT_LT(pos, post_startup_offset) << output;
1736 }
1737 for (uint32_t m : post_startup_methods) {
1738 const size_t pos = output.find(std::to_string(m) + ",", post_startup_offset);
1739 ASSERT_NE(pos, std::string::npos) << output;
1740 EXPECT_LT(pos, classes_offset) << output;
1741 }
1742 }
1743
TEST_F(ProfileAssistantTest,MergeProfilesWithFilter)1744 TEST_F(ProfileAssistantTest, MergeProfilesWithFilter) {
1745 ScratchFile profile1;
1746 ScratchFile profile2;
1747 ScratchFile reference_profile;
1748
1749 std::vector<int> profile_fds({
1750 GetFd(profile1),
1751 GetFd(profile2)});
1752 int reference_profile_fd = GetFd(reference_profile);
1753
1754 // Use a real dex file to generate profile test data.
1755 // The file will be used during merging to filter unwanted data.
1756 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
1757 const DexFile& d1 = *dex_files[0];
1758 const DexFile& d2 = *dex_files[1];
1759 // The new profile info will contain the methods with indices 0-100.
1760 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
1761 ProfileCompilationInfo info1;
1762 SetupProfile(&d1, dex1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
1763 ProfileCompilationInfo info2;
1764 SetupProfile(&d2, dex2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
1765
1766
1767 // The reference profile info will contain the methods with indices 50-150.
1768 const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
1769 ProfileCompilationInfo reference_info;
1770 SetupProfile(&d1, dex1,
1771 kNumberOfMethodsAlreadyCompiled, 0, reference_profile,
1772 &reference_info, kNumberOfMethodsToEnableCompilation / 2);
1773
1774 // Run profman and pass the dex file with --apk-fd.
1775 android::base::unique_fd apk_fd(
1776 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY)); // NOLINT
1777 ASSERT_GE(apk_fd.get(), 0);
1778
1779 std::string profman_cmd = GetProfmanCmd();
1780 std::vector<std::string> argv_str;
1781 argv_str.push_back(profman_cmd);
1782 argv_str.push_back("--profile-file-fd=" + std::to_string(profile1.GetFd()));
1783 argv_str.push_back("--profile-file-fd=" + std::to_string(profile2.GetFd()));
1784 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
1785 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
1786 std::string error;
1787
1788 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kCompile) << error;
1789
1790 // Verify that we can load the result.
1791
1792 ProfileCompilationInfo result;
1793 ASSERT_TRUE(result.Load(reference_profile_fd));
1794
1795 // Verify that the result filtered out data not belonging to the dex file.
1796 // This is equivalent to checking that the result is equal to the merging of
1797 // all profiles while filtering out data not belonging to the dex file.
1798
1799 ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
1800 [&d1, &d2](const std::string& dex_location, uint32_t checksum) -> bool {
1801 return (dex_location == ProfileCompilationInfo::GetProfileDexFileBaseKey(d1.GetLocation())
1802 && checksum == d1.GetLocationChecksum())
1803 || (dex_location == ProfileCompilationInfo::GetProfileDexFileBaseKey(d2.GetLocation())
1804 && checksum == d2.GetLocationChecksum());
1805 };
1806
1807 ProfileCompilationInfo info1_filter;
1808 ProfileCompilationInfo info2_filter;
1809 ProfileCompilationInfo expected;
1810
1811 info2_filter.Load(profile1.GetFd(), /*merge_classes=*/ true, filter_fn);
1812 info2_filter.Load(profile2.GetFd(), /*merge_classes=*/ true, filter_fn);
1813 expected.Load(reference_profile.GetFd(), /*merge_classes=*/ true, filter_fn);
1814
1815 ASSERT_TRUE(expected.MergeWith(info1_filter));
1816 ASSERT_TRUE(expected.MergeWith(info2_filter));
1817
1818 ASSERT_TRUE(expected.Equals(result));
1819 }
1820
TEST_F(ProfileAssistantTest,MergeProfilesNoProfile)1821 TEST_F(ProfileAssistantTest, MergeProfilesNoProfile) {
1822 ScratchFile reference_profile;
1823
1824 // Use a real dex file to generate profile test data.
1825 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
1826 const DexFile& d1 = *dex_files[0];
1827 const DexFile& d2 = *dex_files[0];
1828
1829 // The reference profile info will contain the methods with indices 0-100.
1830 ProfileCompilationInfo reference_info;
1831 SetupProfile(&d1,
1832 &d2,
1833 /*number_of_methods=*/ 100,
1834 /*number_of_classes=*/ 0,
1835 reference_profile,
1836 &reference_info);
1837
1838 std::string content_before;
1839 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
1840
1841 // Run profman and pass the dex file with --apk-fd.
1842 android::base::unique_fd apk_fd(
1843 // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
1844 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
1845 ASSERT_GE(apk_fd.get(), 0);
1846
1847 std::string profman_cmd = GetProfmanCmd();
1848 std::vector<std::string> argv_str;
1849 argv_str.push_back(profman_cmd);
1850 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
1851 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
1852
1853 // Must return kSkipCompilationSmallDelta.
1854 std::string error;
1855 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationSmallDelta)
1856 << error;
1857
1858 // Verify that the content has not changed.
1859 std::string content_after;
1860 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
1861 EXPECT_EQ(content_before, content_after);
1862 }
1863
TEST_F(ProfileAssistantTest,MergeProfilesNoProfilePassByFilename)1864 TEST_F(ProfileAssistantTest, MergeProfilesNoProfilePassByFilename) {
1865 ScratchFile reference_profile;
1866
1867 // Use a real dex file to generate profile test data.
1868 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
1869 const DexFile& d1 = *dex_files[0];
1870 const DexFile& d2 = *dex_files[0];
1871
1872 // The reference profile info will contain the methods with indices 0-100.
1873 ProfileCompilationInfo reference_info;
1874 SetupProfile(&d1,
1875 &d2,
1876 /*number_of_methods=*/100,
1877 /*number_of_classes=*/0,
1878 reference_profile,
1879 &reference_info);
1880
1881 std::string content_before;
1882 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
1883
1884 // Run profman and pass the dex file with --apk-fd.
1885 android::base::unique_fd apk_fd(
1886 // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
1887 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
1888 ASSERT_GE(apk_fd.get(), 0);
1889
1890 std::string profman_cmd = GetProfmanCmd();
1891 std::vector<std::string> argv_str;
1892 argv_str.push_back(profman_cmd);
1893 argv_str.push_back("--reference-profile-file=" + reference_profile.GetFilename());
1894 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
1895
1896 // Must return kSkipCompilationSmallDelta.
1897 std::string error;
1898 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationSmallDelta)
1899 << error;
1900
1901 // Verify that the content has not changed.
1902 std::string content_after;
1903 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
1904 EXPECT_EQ(content_before, content_after);
1905 }
1906
TEST_F(ProfileAssistantTest,MergeProfilesNoProfileEmptyReferenceProfile)1907 TEST_F(ProfileAssistantTest, MergeProfilesNoProfileEmptyReferenceProfile) {
1908 ScratchFile reference_profile;
1909
1910 // The reference profile info will only contain the header.
1911 ProfileCompilationInfo reference_info;
1912 SetupProfile(/*dex_file1=*/ nullptr,
1913 /*dex_file2=*/ nullptr,
1914 /*number_of_methods=*/ 0,
1915 /*number_of_classes=*/ 0,
1916 reference_profile,
1917 &reference_info);
1918
1919 std::string content_before;
1920 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
1921
1922 // Run profman and pass the dex file with --apk-fd.
1923 android::base::unique_fd apk_fd(
1924 // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
1925 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
1926 ASSERT_GE(apk_fd.get(), 0);
1927
1928 std::string profman_cmd = GetProfmanCmd();
1929 std::vector<std::string> argv_str;
1930 argv_str.push_back(profman_cmd);
1931 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
1932 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
1933
1934 // Must return kSkipCompilationEmptyProfiles.
1935 std::string error;
1936 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationEmptyProfiles)
1937 << error;
1938
1939 // Verify that the content has not changed.
1940 std::string content_after;
1941 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
1942 EXPECT_EQ(content_before, content_after);
1943 }
1944
TEST_F(ProfileAssistantTest,MergeProfilesNoProfileEmptyReferenceProfileAfterFiltering)1945 TEST_F(ProfileAssistantTest, MergeProfilesNoProfileEmptyReferenceProfileAfterFiltering) {
1946 ScratchFile reference_profile;
1947
1948 // Use fake dex files to generate profile test data.
1949 // All the methods will be filtered out during the profman invocation.
1950 ProfileCompilationInfo reference_info;
1951 SetupProfile(dex1,
1952 dex2,
1953 /*number_of_methods=*/ 100,
1954 /*number_of_classes=*/ 0,
1955 reference_profile,
1956 &reference_info);
1957
1958 std::string content_before;
1959 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
1960
1961 // Run profman and pass the real dex file with --apk-fd.
1962 android::base::unique_fd apk_fd(
1963 // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
1964 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
1965 ASSERT_GE(apk_fd.get(), 0);
1966
1967 std::string profman_cmd = GetProfmanCmd();
1968 std::vector<std::string> argv_str;
1969 argv_str.push_back(profman_cmd);
1970 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
1971 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
1972
1973 // Must return kSkipCompilationEmptyProfiles.
1974 std::string error;
1975 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationEmptyProfiles)
1976 << error;
1977
1978 // Verify that the content has not changed.
1979 std::string content_after;
1980 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
1981 EXPECT_EQ(content_before, content_after);
1982 }
1983
TEST_F(ProfileAssistantTest,CopyAndUpdateProfileKey)1984 TEST_F(ProfileAssistantTest, CopyAndUpdateProfileKey) {
1985 ScratchFile profile1;
1986 ScratchFile reference_profile;
1987
1988 // Use a real dex file to generate profile test data. During the copy-and-update the
1989 // matching is done based on checksum so we have to match with the real thing.
1990 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
1991 const DexFile& d1 = *dex_files[0];
1992 const DexFile& d2 = *dex_files[1];
1993
1994 ProfileCompilationInfo info1;
1995 uint16_t num_methods_to_add = std::min(d1.NumMethodIds(), d2.NumMethodIds());
1996
1997 const DexFile* dex_to_be_updated1 = BuildDex(
1998 "fake-location1", d1.GetLocationChecksum(), "LC;", d1.NumMethodIds(), d1.NumTypeIds());
1999 const DexFile* dex_to_be_updated2 = BuildDex(
2000 "fake-location2", d2.GetLocationChecksum(), "LC;", d2.NumMethodIds(), d2.NumTypeIds());
2001 SetupProfile(dex_to_be_updated1,
2002 dex_to_be_updated2,
2003 num_methods_to_add,
2004 /*number_of_classes=*/ 0,
2005 profile1,
2006 &info1);
2007
2008 // Run profman and pass the dex file with --apk-fd.
2009 android::base::unique_fd apk_fd(
2010 // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
2011 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
2012 ASSERT_GE(apk_fd.get(), 0);
2013
2014 std::string profman_cmd = GetProfmanCmd();
2015 std::vector<std::string> argv_str;
2016 argv_str.push_back(profman_cmd);
2017 argv_str.push_back("--profile-file-fd=" + std::to_string(profile1.GetFd()));
2018 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
2019 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
2020 argv_str.push_back("--copy-and-update-profile-key");
2021 std::string error;
2022
2023 // Must return kCopyAndUpdateSuccess.
2024 ASSERT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kCopyAndUpdateSuccess) << error;
2025
2026 // Verify that we can load the result.
2027 ProfileCompilationInfo result;
2028 ASSERT_TRUE(result.Load(reference_profile.GetFd()));
2029
2030 // Verify that the renaming was done.
2031 for (uint16_t i = 0; i < num_methods_to_add; i ++) {
2032 ASSERT_TRUE(result.GetMethodHotness(MethodReference(&d1, i)).IsHot()) << i;
2033 ASSERT_TRUE(result.GetMethodHotness(MethodReference(&d2, i)).IsHot()) << i;
2034
2035 ASSERT_FALSE(result.GetMethodHotness(MethodReference(dex_to_be_updated1, i)).IsHot()) << i;
2036 ASSERT_FALSE(result.GetMethodHotness(MethodReference(dex_to_be_updated2, i)).IsHot()) << i;
2037 }
2038 }
2039
TEST_F(ProfileAssistantTest,CopyAndUpdateProfileKeyNoUpdate)2040 TEST_F(ProfileAssistantTest, CopyAndUpdateProfileKeyNoUpdate) {
2041 ScratchFile profile1;
2042 ScratchFile reference_profile;
2043
2044 // Use fake dex files to generate profile test data.
2045 ProfileCompilationInfo info1;
2046 SetupProfile(dex1,
2047 dex2,
2048 /*number_of_methods=*/ 100,
2049 /*number_of_classes=*/ 0,
2050 profile1,
2051 &info1);
2052
2053 std::string input_content;
2054 ASSERT_TRUE(android::base::ReadFileToString(profile1.GetFilename(), &input_content));
2055
2056 // Run profman and pass the real dex file with --apk-fd. It won't match any entry in the profile.
2057 android::base::unique_fd apk_fd(
2058 // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
2059 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
2060 ASSERT_GE(apk_fd.get(), 0);
2061
2062 std::string profman_cmd = GetProfmanCmd();
2063 std::vector<std::string> argv_str;
2064 argv_str.push_back(profman_cmd);
2065 argv_str.push_back("--profile-file-fd=" + std::to_string(profile1.GetFd()));
2066 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
2067 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
2068 argv_str.push_back("--copy-and-update-profile-key");
2069 std::string error;
2070
2071 // Must return kCopyAndUpdateNoMatch.
2072 ASSERT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kCopyAndUpdateNoMatch) << error;
2073
2074 // Verify that the content is the same.
2075 std::string output_content;
2076 ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &output_content));
2077 EXPECT_EQ(input_content, output_content);
2078 }
2079
TEST_F(ProfileAssistantTest,BootImageMerge)2080 TEST_F(ProfileAssistantTest, BootImageMerge) {
2081 ScratchFile profile;
2082 ScratchFile reference_profile;
2083 std::vector<int> profile_fds({GetFd(profile)});
2084 int reference_profile_fd = GetFd(reference_profile);
2085 std::vector<uint32_t> hot_methods_cur;
2086 std::vector<uint32_t> hot_methods_ref;
2087 std::vector<uint32_t> empty_vector;
2088 size_t num_methods = 100;
2089 for (size_t i = 0; i < num_methods; ++i) {
2090 hot_methods_cur.push_back(i);
2091 }
2092 for (size_t i = 0; i < num_methods; ++i) {
2093 hot_methods_ref.push_back(i);
2094 }
2095 ProfileCompilationInfo info1(/*for_boot_image=*/ true);
2096 SetupBasicProfile(dex1, hot_methods_cur, empty_vector, empty_vector,
2097 profile, &info1);
2098 ProfileCompilationInfo info2(/*for_boot_image=*/true);
2099 SetupBasicProfile(dex1, hot_methods_ref, empty_vector, empty_vector,
2100 reference_profile, &info2);
2101
2102 std::vector<const std::string> extra_args({"--force-merge", "--boot-image-merge"});
2103
2104 int return_code = ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
2105
2106 ASSERT_EQ(return_code, ProfmanResult::kSuccess);
2107
2108 // Verify the result: it should be equal to info2 since info1 is a regular profile
2109 // and should be ignored.
2110 ProfileCompilationInfo result(/*for_boot_image=*/ true);
2111 ASSERT_TRUE(result.Load(reference_profile.GetFd()));
2112 ASSERT_TRUE(result.Equals(info2));
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
2157 // Test that we consider the annations when we merge boot image profiles.
TEST_F(ProfileAssistantTest,BootImageMergeWithAnnotations)2158 TEST_F(ProfileAssistantTest, BootImageMergeWithAnnotations) {
2159 ScratchFile profile;
2160 ScratchFile reference_profile;
2161
2162 std::vector<int> profile_fds({GetFd(profile)});
2163 int reference_profile_fd = GetFd(reference_profile);
2164
2165 // Use a real dex file to generate profile test data so that we can pass descriptors to profman.
2166 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
2167 const DexFile& d1 = *dex_files[0];
2168 const DexFile& d2 = *dex_files[1];
2169 // The new profile info will contain the methods with indices 0-100.
2170 ProfileCompilationInfo info(/*for_boot_image=*/ true);
2171 ProfileCompilationInfo::ProfileSampleAnnotation psa1("package1");
2172 ProfileCompilationInfo::ProfileSampleAnnotation psa2("package2");
2173
2174 AddMethod(&info, &d1, 0, Hotness::kFlagHot, psa1);
2175 AddMethod(&info, &d2, 0, Hotness::kFlagHot, psa2);
2176 info.Save(profile.GetFd());
2177
2178 // Run profman and pass the dex file with --apk-fd.
2179 android::base::unique_fd apk_fd(
2180 // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
2181 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
2182 ASSERT_GE(apk_fd.get(), 0);
2183
2184 std::string profman_cmd = GetProfmanCmd();
2185 std::vector<std::string> argv_str;
2186 argv_str.push_back(profman_cmd);
2187 argv_str.push_back("--profile-file-fd=" + std::to_string(profile.GetFd()));
2188 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
2189 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
2190 argv_str.push_back("--force-merge");
2191 argv_str.push_back("--boot-image-merge");
2192 std::string error;
2193
2194 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSuccess) << error;
2195
2196 // Verify that we can load the result and that it equals to what we saved.
2197 ProfileCompilationInfo result(/*for_boot_image=*/ true);
2198 ASSERT_TRUE(result.Load(reference_profile_fd));
2199 ASSERT_TRUE(info.Equals(result));
2200 }
2201
TEST_F(ProfileAssistantTest,DifferentProfileVersions)2202 TEST_F(ProfileAssistantTest, DifferentProfileVersions) {
2203 ScratchFile profile1;
2204 ScratchFile profile2;
2205
2206 ProfileCompilationInfo info1(/*for_boot_image=*/ false);
2207 info1.Save(profile1.GetFd());
2208
2209 ProfileCompilationInfo info2(/*for_boot_image=*/ true);
2210 info2.Save(profile2.GetFd());
2211
2212 std::vector<int> profile_fds({ GetFd(profile1)});
2213 int reference_profile_fd = GetFd(profile2);
2214 std::vector<const std::string> boot_image_args({"--boot-image-merge"});
2215 ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, boot_image_args),
2216 ProfmanResult::kErrorDifferentVersions);
2217 ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd), ProfmanResult::kErrorBadProfiles);
2218
2219 // Reverse the order of the profiles to verify we get the same behaviour.
2220 profile_fds[0] = GetFd(profile2);
2221 reference_profile_fd = GetFd(profile1);
2222 ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, boot_image_args),
2223 ProfmanResult::kErrorBadProfiles);
2224 ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd),
2225 ProfmanResult::kErrorDifferentVersions);
2226 }
2227
2228 // Under default behaviour we will abort if we cannot load a profile during a merge
2229 // operation. However, if we pass --force-merge to force aggregation we should
2230 // ignore files we cannot load
TEST_F(ProfileAssistantTest,ForceMergeIgnoreProfilesItCannotLoad)2231 TEST_F(ProfileAssistantTest, ForceMergeIgnoreProfilesItCannotLoad) {
2232 ScratchFile profile1;
2233 ScratchFile profile2;
2234
2235 // Write corrupt data in the first file.
2236 std::string content = "giberish";
2237 ASSERT_TRUE(profile1.GetFile()->WriteFully(content.c_str(), content.length()));
2238
2239 ProfileCompilationInfo info2(/*for_boot_image=*/ true);
2240 info2.Save(profile2.GetFd());
2241
2242 std::vector<int> profile_fds({ GetFd(profile1)});
2243 int reference_profile_fd = GetFd(profile2);
2244
2245 // With force-merge we should merge successfully.
2246 std::vector<const std::string> extra_args({"--force-merge", "--boot-image-merge"});
2247 ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, extra_args),
2248 ProfmanResult::kSuccess);
2249
2250 ProfileCompilationInfo result(/*for_boot_image=*/ true);
2251 ASSERT_TRUE(result.Load(reference_profile_fd));
2252 ASSERT_TRUE(info2.Equals(result));
2253
2254 // Without force-merge we should fail.
2255 std::vector<const std::string> extra_args2({"--boot-image-merge"});
2256 ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, extra_args2),
2257 ProfmanResult::kErrorBadProfiles);
2258 }
2259
2260 } // namespace art
2261