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