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
19 #include "android-base/file.h"
20 #include "android-base/strings.h"
21 #include "art_method-inl.h"
22 #include "base/unix_file/fd_file.h"
23 #include "base/utils.h"
24 #include "common_runtime_test.h"
25 #include "dex/descriptors_names.h"
26 #include "dex/type_reference.h"
27 #include "exec_utils.h"
28 #include "linear_alloc.h"
29 #include "mirror/class-inl.h"
30 #include "obj_ptr-inl.h"
31 #include "profile/profile_compilation_info.h"
32 #include "profile_assistant.h"
33 #include "scoped_thread_state_change-inl.h"
34
35 namespace art {
36
37 using Hotness = ProfileCompilationInfo::MethodHotness;
38 using TypeReferenceSet = std::set<TypeReference, TypeReferenceValueComparator>;
39 using ProfileInlineCache = ProfileMethodInfo::ProfileInlineCache;
40
41 // TODO(calin): These tests share a lot with the ProfileCompilationInfo tests.
42 // we should introduce a better abstraction to extract the common parts.
43 class ProfileAssistantTest : public CommonRuntimeTest {
44 public:
PostRuntimeCreate()45 void PostRuntimeCreate() override {
46 allocator_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
47
48 dex1 = fake_dex_storage.AddFakeDex("location1", /* checksum= */ 1, /* num_method_ids= */ 10001);
49 dex2 = fake_dex_storage.AddFakeDex("location2", /* checksum= */ 2, /* num_method_ids= */ 10002);
50 dex3 = fake_dex_storage.AddFakeDex("location3", /* checksum= */ 3, /* num_method_ids= */ 10003);
51 dex4 = fake_dex_storage.AddFakeDex("location4", /* checksum= */ 4, /* num_method_ids= */ 10004);
52
53 dex1_checksum_missmatch = fake_dex_storage.AddFakeDex(
54 "location1", /* checksum= */ 12, /* num_method_ids= */ 10001);
55 }
56
57 protected:
AddMethod(ProfileCompilationInfo * info,const DexFile * dex,uint16_t method_idx,const std::vector<ProfileInlineCache> & inline_caches,Hotness::Flag flags)58 bool AddMethod(ProfileCompilationInfo* info,
59 const DexFile* dex,
60 uint16_t method_idx,
61 const std::vector<ProfileInlineCache>& inline_caches,
62 Hotness::Flag flags) {
63 return info->AddMethod(
64 ProfileMethodInfo(MethodReference(dex, method_idx), inline_caches), flags);
65 }
66
AddMethod(ProfileCompilationInfo * info,const DexFile * dex,uint16_t method_idx,Hotness::Flag flags,const ProfileCompilationInfo::ProfileSampleAnnotation & annotation=ProfileCompilationInfo::ProfileSampleAnnotation::kNone)67 bool AddMethod(ProfileCompilationInfo* info,
68 const DexFile* dex,
69 uint16_t method_idx,
70 Hotness::Flag flags,
71 const ProfileCompilationInfo::ProfileSampleAnnotation& annotation
72 = ProfileCompilationInfo::ProfileSampleAnnotation::kNone) {
73 return info->AddMethod(ProfileMethodInfo(MethodReference(dex, method_idx)),
74 flags,
75 annotation);
76 }
77
AddClass(ProfileCompilationInfo * info,const DexFile * dex,dex::TypeIndex type_index)78 bool AddClass(ProfileCompilationInfo* info,
79 const DexFile* dex,
80 dex::TypeIndex type_index) {
81 std::vector<dex::TypeIndex> classes = {type_index};
82 return info->AddClassesForDex(dex, classes.begin(), classes.end());
83 }
84
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)85 void SetupProfile(const DexFile* dex_file1,
86 const DexFile* dex_file2,
87 uint16_t number_of_methods,
88 uint16_t number_of_classes,
89 const ScratchFile& profile,
90 ProfileCompilationInfo* info,
91 uint16_t start_method_index = 0,
92 bool reverse_dex_write_order = false) {
93 for (uint16_t i = start_method_index; i < start_method_index + number_of_methods; i++) {
94 // reverse_dex_write_order controls the order in which the dex files will be added to
95 // the profile and thus written to disk.
96 std::vector<ProfileInlineCache> inline_caches = GetTestInlineCaches(dex_file1 , dex_file2, dex3);
97 Hotness::Flag flags = static_cast<Hotness::Flag>(
98 Hotness::kFlagHot | Hotness::kFlagPostStartup);
99 if (reverse_dex_write_order) {
100 ASSERT_TRUE(AddMethod(info, dex_file2, i, inline_caches, flags));
101 ASSERT_TRUE(AddMethod(info, dex_file1, i, inline_caches, flags));
102 } else {
103 ASSERT_TRUE(AddMethod(info, dex_file1, i, inline_caches, flags));
104 ASSERT_TRUE(AddMethod(info, dex_file2, i, inline_caches, flags));
105 }
106 }
107 for (uint16_t i = 0; i < number_of_classes; i++) {
108 ASSERT_TRUE(AddClass(info, dex_file1, dex::TypeIndex(i)));
109 }
110
111 ASSERT_TRUE(info->Save(GetFd(profile)));
112 ASSERT_EQ(0, profile.GetFile()->Flush());
113 ASSERT_TRUE(profile.GetFile()->ResetOffset());
114 }
115
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)116 void SetupBasicProfile(const DexFile* dex,
117 const std::vector<uint32_t>& hot_methods,
118 const std::vector<uint32_t>& startup_methods,
119 const std::vector<uint32_t>& post_startup_methods,
120 const ScratchFile& profile,
121 ProfileCompilationInfo* info) {
122 for (uint32_t idx : hot_methods) {
123 AddMethod(info, dex, idx, Hotness::kFlagHot);
124 }
125 for (uint32_t idx : startup_methods) {
126 AddMethod(info, dex, idx, Hotness::kFlagStartup);
127 }
128 for (uint32_t idx : post_startup_methods) {
129 AddMethod(info, dex, idx, Hotness::kFlagPostStartup);
130 }
131 ASSERT_TRUE(info->Save(GetFd(profile)));
132 ASSERT_EQ(0, profile.GetFile()->Flush());
133 ASSERT_TRUE(profile.GetFile()->ResetOffset());
134 }
135
136 // 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)137 std::vector<ProfileInlineCache> GetTestInlineCaches(
138 const DexFile* dex_file1, const DexFile* dex_file2, const DexFile* dex_file3) {
139 std::vector<ProfileInlineCache> inline_caches;
140 // Monomorphic
141 for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
142 std::vector<TypeReference> types = {TypeReference(dex_file1, dex::TypeIndex(0))};
143 inline_caches.push_back(ProfileInlineCache(dex_pc, /* missing_types*/ false, types));
144 }
145 // Polymorphic
146 for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
147 std::vector<TypeReference> types = {
148 TypeReference(dex_file1, dex::TypeIndex(0)),
149 TypeReference(dex_file2, dex::TypeIndex(1)),
150 TypeReference(dex_file3, dex::TypeIndex(2))};
151 inline_caches.push_back(ProfileInlineCache(dex_pc, /* missing_types*/ false, types));
152 }
153 // Megamorphic
154 for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
155 // we need 5 types to make the cache megamorphic
156 std::vector<TypeReference> types = {
157 TypeReference(dex_file1, dex::TypeIndex(0)),
158 TypeReference(dex_file1, dex::TypeIndex(1)),
159 TypeReference(dex_file1, dex::TypeIndex(2)),
160 TypeReference(dex_file1, dex::TypeIndex(3)),
161 TypeReference(dex_file1, dex::TypeIndex(4))};
162 inline_caches.push_back(ProfileInlineCache(dex_pc, /* missing_types*/ false, types));
163 }
164 // Missing types
165 for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
166 std::vector<TypeReference> types;
167 inline_caches.push_back(ProfileInlineCache(dex_pc, /* missing_types*/ true, types));
168 }
169
170 return inline_caches;
171 }
172
GetFd(const ScratchFile & file) const173 int GetFd(const ScratchFile& file) const {
174 return static_cast<int>(file.GetFd());
175 }
176
CheckProfileInfo(ScratchFile & file,const ProfileCompilationInfo & info)177 void CheckProfileInfo(ScratchFile& file, const ProfileCompilationInfo& info) {
178 ProfileCompilationInfo file_info;
179 ASSERT_TRUE(file.GetFile()->ResetOffset());
180 ASSERT_TRUE(file_info.Load(GetFd(file)));
181 ASSERT_TRUE(file_info.Equals(info));
182 }
183
GetProfmanCmd()184 std::string GetProfmanCmd() {
185 std::string file_path = GetArtBinDir() + "/profman";
186 if (kIsDebugBuild) {
187 file_path += "d";
188 }
189 EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
190 return file_path;
191 }
192
193 // 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> ())194 int ProcessProfiles(
195 const std::vector<int>& profiles_fd,
196 int reference_profile_fd,
197 const std::vector<const std::string>& extra_args = std::vector<const std::string>()) {
198 std::string profman_cmd = GetProfmanCmd();
199 std::vector<std::string> argv_str;
200 argv_str.push_back(profman_cmd);
201 for (size_t k = 0; k < profiles_fd.size(); k++) {
202 argv_str.push_back("--profile-file-fd=" + std::to_string(profiles_fd[k]));
203 }
204 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile_fd));
205 argv_str.insert(argv_str.end(), extra_args.begin(), extra_args.end());
206
207 std::string error;
208 return ExecAndReturnCode(argv_str, &error);
209 }
210
GenerateTestProfile(const std::string & filename)211 bool GenerateTestProfile(const std::string& filename) {
212 std::string profman_cmd = GetProfmanCmd();
213 std::vector<std::string> argv_str;
214 argv_str.push_back(profman_cmd);
215 argv_str.push_back("--generate-test-profile=" + filename);
216 std::string error;
217 return ExecAndReturnCode(argv_str, &error);
218 }
219
GenerateTestProfileWithInputDex(const std::string & filename)220 bool GenerateTestProfileWithInputDex(const std::string& filename) {
221 std::string profman_cmd = GetProfmanCmd();
222 std::vector<std::string> argv_str;
223 argv_str.push_back(profman_cmd);
224 argv_str.push_back("--generate-test-profile=" + filename);
225 argv_str.push_back("--generate-test-profile-seed=0");
226 argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]);
227 argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]);
228 std::string error;
229 return ExecAndReturnCode(argv_str, &error);
230 }
231
CreateProfile(const std::string & profile_file_contents,const std::string & filename,const std::string & dex_location)232 bool CreateProfile(const std::string& profile_file_contents,
233 const std::string& filename,
234 const std::string& dex_location) {
235 ScratchFile class_names_file;
236 File* file = class_names_file.GetFile();
237 EXPECT_TRUE(file->WriteFully(profile_file_contents.c_str(), profile_file_contents.length()));
238 EXPECT_EQ(0, file->Flush());
239 EXPECT_TRUE(file->ResetOffset());
240 std::string profman_cmd = GetProfmanCmd();
241 std::vector<std::string> argv_str;
242 argv_str.push_back(profman_cmd);
243 argv_str.push_back("--create-profile-from=" + class_names_file.GetFilename());
244 argv_str.push_back("--reference-profile-file=" + filename);
245 argv_str.push_back("--apk=" + dex_location);
246 argv_str.push_back("--dex-location=" + dex_location);
247 std::string error;
248 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
249 return true;
250 }
251
RunProfman(const std::string & filename,std::vector<std::string> & extra_args,std::string * output)252 bool RunProfman(const std::string& filename,
253 std::vector<std::string>& extra_args,
254 std::string* output) {
255 ScratchFile output_file;
256 std::string profman_cmd = GetProfmanCmd();
257 std::vector<std::string> argv_str;
258 argv_str.push_back(profman_cmd);
259 argv_str.insert(argv_str.end(), extra_args.begin(), extra_args.end());
260 argv_str.push_back("--profile-file=" + filename);
261 argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]);
262 argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]);
263 argv_str.push_back("--dump-output-to-fd=" + std::to_string(GetFd(output_file)));
264 std::string error;
265 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
266 File* file = output_file.GetFile();
267 EXPECT_EQ(0, file->Flush());
268 EXPECT_TRUE(file->ResetOffset());
269 int64_t length = file->GetLength();
270 std::unique_ptr<char[]> buf(new char[length]);
271 EXPECT_EQ(file->Read(buf.get(), length, 0), length);
272 *output = std::string(buf.get(), length);
273 return true;
274 }
275
DumpClassesAndMethods(const std::string & filename,std::string * file_contents)276 bool DumpClassesAndMethods(const std::string& filename, std::string* file_contents) {
277 std::vector<std::string> extra_args;
278 extra_args.push_back("--dump-classes-and-methods");
279 return RunProfman(filename, extra_args, file_contents);
280 }
281
DumpOnly(const std::string & filename,std::string * file_contents)282 bool DumpOnly(const std::string& filename, std::string* file_contents) {
283 std::vector<std::string> extra_args;
284 extra_args.push_back("--dump-only");
285 return RunProfman(filename, extra_args, file_contents);
286 }
287
CreateAndDump(const std::string & input_file_contents,std::string * output_file_contents)288 bool CreateAndDump(const std::string& input_file_contents,
289 std::string* output_file_contents) {
290 ScratchFile profile_file;
291 EXPECT_TRUE(CreateProfile(input_file_contents,
292 profile_file.GetFilename(),
293 GetLibCoreDexFileNames()[0]));
294 profile_file.GetFile()->ResetOffset();
295 EXPECT_TRUE(DumpClassesAndMethods(profile_file.GetFilename(), output_file_contents));
296 return true;
297 }
298
GetClass(ScopedObjectAccess & soa,jobject class_loader,const std::string & clazz)299 ObjPtr<mirror::Class> GetClass(ScopedObjectAccess& soa,
300 jobject class_loader,
301 const std::string& clazz) REQUIRES_SHARED(Locks::mutator_lock_) {
302 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
303 StackHandleScope<1> hs(soa.Self());
304 Handle<mirror::ClassLoader> h_loader(hs.NewHandle(
305 ObjPtr<mirror::ClassLoader>::DownCast(soa.Self()->DecodeJObject(class_loader))));
306 return class_linker->FindClass(soa.Self(), clazz.c_str(), h_loader);
307 }
308
GetVirtualMethod(jobject class_loader,const std::string & clazz,const std::string & name)309 ArtMethod* GetVirtualMethod(jobject class_loader,
310 const std::string& clazz,
311 const std::string& name) {
312 ScopedObjectAccess soa(Thread::Current());
313 ObjPtr<mirror::Class> klass = GetClass(soa, class_loader, clazz);
314 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
315 const auto pointer_size = class_linker->GetImagePointerSize();
316 ArtMethod* method = nullptr;
317 for (auto& m : klass->GetVirtualMethods(pointer_size)) {
318 if (name == m.GetName()) {
319 EXPECT_TRUE(method == nullptr);
320 method = &m;
321 }
322 }
323 return method;
324 }
325
MakeTypeReference(ObjPtr<mirror::Class> klass)326 static TypeReference MakeTypeReference(ObjPtr<mirror::Class> klass)
327 REQUIRES_SHARED(Locks::mutator_lock_) {
328 return TypeReference(&klass->GetDexFile(), klass->GetDexTypeIndex());
329 }
330
331 // Verify that given method has the expected inline caches and nothing else.
AssertInlineCaches(ArtMethod * method,const TypeReferenceSet & expected_clases,const ProfileCompilationInfo & info,bool is_megamorphic,bool is_missing_types)332 void AssertInlineCaches(ArtMethod* method,
333 const TypeReferenceSet& expected_clases,
334 const ProfileCompilationInfo& info,
335 bool is_megamorphic,
336 bool is_missing_types)
337 REQUIRES_SHARED(Locks::mutator_lock_) {
338 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi =
339 info.GetHotMethodInfo(MethodReference(
340 method->GetDexFile(), method->GetDexMethodIndex()));
341 ASSERT_TRUE(pmi != nullptr);
342 ASSERT_EQ(pmi->inline_caches->size(), 1u);
343 const ProfileCompilationInfo::DexPcData& dex_pc_data = pmi->inline_caches->begin()->second;
344
345 ASSERT_EQ(dex_pc_data.is_megamorphic, is_megamorphic);
346 ASSERT_EQ(dex_pc_data.is_missing_types, is_missing_types);
347 ASSERT_EQ(expected_clases.size(), dex_pc_data.classes.size());
348 size_t found = 0;
349 for (const TypeReference& type_ref : expected_clases) {
350 for (const auto& class_ref : dex_pc_data.classes) {
351 ProfileCompilationInfo::DexReference dex_ref =
352 pmi->dex_references[class_ref.dex_profile_index];
353 if (dex_ref.MatchesDex(type_ref.dex_file) && class_ref.type_index == type_ref.TypeIndex()) {
354 found++;
355 }
356 }
357 }
358
359 ASSERT_EQ(expected_clases.size(), found);
360 }
361
CheckCompilationMethodPercentChange(uint16_t methods_in_cur_profile,uint16_t methods_in_ref_profile)362 int CheckCompilationMethodPercentChange(uint16_t methods_in_cur_profile,
363 uint16_t methods_in_ref_profile) {
364 ScratchFile profile;
365 ScratchFile reference_profile;
366 std::vector<int> profile_fds({ GetFd(profile)});
367 int reference_profile_fd = GetFd(reference_profile);
368 std::vector<uint32_t> hot_methods_cur;
369 std::vector<uint32_t> hot_methods_ref;
370 std::vector<uint32_t> empty_vector;
371 for (size_t i = 0; i < methods_in_cur_profile; ++i) {
372 hot_methods_cur.push_back(i);
373 }
374 for (size_t i = 0; i < methods_in_ref_profile; ++i) {
375 hot_methods_ref.push_back(i);
376 }
377 ProfileCompilationInfo info1;
378 SetupBasicProfile(dex1, hot_methods_cur, empty_vector, empty_vector,
379 profile, &info1);
380 ProfileCompilationInfo info2;
381 SetupBasicProfile(dex1, hot_methods_ref, empty_vector, empty_vector,
382 reference_profile, &info2);
383 return ProcessProfiles(profile_fds, reference_profile_fd);
384 }
385
CheckCompilationClassPercentChange(uint16_t classes_in_cur_profile,uint16_t classes_in_ref_profile)386 int CheckCompilationClassPercentChange(uint16_t classes_in_cur_profile,
387 uint16_t classes_in_ref_profile) {
388 ScratchFile profile;
389 ScratchFile reference_profile;
390
391 std::vector<int> profile_fds({ GetFd(profile)});
392 int reference_profile_fd = GetFd(reference_profile);
393
394 ProfileCompilationInfo info1;
395 SetupProfile(dex1, dex2, 0, classes_in_cur_profile, profile, &info1);
396 ProfileCompilationInfo info2;
397 SetupProfile(dex1, dex2, 0, classes_in_ref_profile, reference_profile, &info2);
398 return ProcessProfiles(profile_fds, reference_profile_fd);
399 }
400
401 std::unique_ptr<ArenaAllocator> allocator_;
402
403 const DexFile* dex1;
404 const DexFile* dex2;
405 const DexFile* dex3;
406 const DexFile* dex4;
407 const DexFile* dex1_checksum_missmatch;
408 FakeDexStorage fake_dex_storage;
409 };
410
TEST_F(ProfileAssistantTest,AdviseCompilationEmptyReferences)411 TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) {
412 ScratchFile profile1;
413 ScratchFile profile2;
414 ScratchFile reference_profile;
415
416 std::vector<int> profile_fds({
417 GetFd(profile1),
418 GetFd(profile2)});
419 int reference_profile_fd = GetFd(reference_profile);
420
421 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
422 ProfileCompilationInfo info1;
423 SetupProfile(dex1, dex2, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
424 ProfileCompilationInfo info2;
425 SetupProfile(dex3, dex4, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
426
427 // We should advise compilation.
428 ASSERT_EQ(ProfileAssistant::kCompile,
429 ProcessProfiles(profile_fds, reference_profile_fd));
430 // The resulting compilation info must be equal to the merge of the inputs.
431 ProfileCompilationInfo result;
432 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
433 ASSERT_TRUE(result.Load(reference_profile_fd));
434
435 ProfileCompilationInfo expected;
436 ASSERT_TRUE(expected.MergeWith(info1));
437 ASSERT_TRUE(expected.MergeWith(info2));
438 ASSERT_TRUE(expected.Equals(result));
439
440 // The information from profiles must remain the same.
441 CheckProfileInfo(profile1, info1);
442 CheckProfileInfo(profile2, info2);
443 }
444
445 // TODO(calin): Add more tests for classes.
TEST_F(ProfileAssistantTest,AdviseCompilationEmptyReferencesBecauseOfClasses)446 TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferencesBecauseOfClasses) {
447 ScratchFile profile1;
448 ScratchFile reference_profile;
449
450 std::vector<int> profile_fds({
451 GetFd(profile1)});
452 int reference_profile_fd = GetFd(reference_profile);
453
454 const uint16_t kNumberOfClassesToEnableCompilation = 100;
455 ProfileCompilationInfo info1;
456 SetupProfile(dex1, dex2, 0, kNumberOfClassesToEnableCompilation, profile1, &info1);
457
458 // We should advise compilation.
459 ASSERT_EQ(ProfileAssistant::kCompile,
460 ProcessProfiles(profile_fds, reference_profile_fd));
461 // The resulting compilation info must be equal to the merge of the inputs.
462 ProfileCompilationInfo result;
463 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
464 ASSERT_TRUE(result.Load(reference_profile_fd));
465
466 ProfileCompilationInfo expected;
467 ASSERT_TRUE(expected.MergeWith(info1));
468 ASSERT_TRUE(expected.Equals(result));
469
470 // The information from profiles must remain the same.
471 CheckProfileInfo(profile1, info1);
472 }
473
TEST_F(ProfileAssistantTest,AdviseCompilationNonEmptyReferences)474 TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) {
475 ScratchFile profile1;
476 ScratchFile profile2;
477 ScratchFile reference_profile;
478
479 std::vector<int> profile_fds({
480 GetFd(profile1),
481 GetFd(profile2)});
482 int reference_profile_fd = GetFd(reference_profile);
483
484 // The new profile info will contain the methods with indices 0-100.
485 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
486 ProfileCompilationInfo info1;
487 SetupProfile(dex1, dex2, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
488 ProfileCompilationInfo info2;
489 SetupProfile(dex3, dex4, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
490
491
492 // The reference profile info will contain the methods with indices 50-150.
493 const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
494 ProfileCompilationInfo reference_info;
495 SetupProfile(dex1, dex2, kNumberOfMethodsAlreadyCompiled, 0, reference_profile,
496 &reference_info, kNumberOfMethodsToEnableCompilation / 2);
497
498 // We should advise compilation.
499 ASSERT_EQ(ProfileAssistant::kCompile,
500 ProcessProfiles(profile_fds, reference_profile_fd));
501
502 // The resulting compilation info must be equal to the merge of the inputs
503 ProfileCompilationInfo result;
504 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
505 ASSERT_TRUE(result.Load(reference_profile_fd));
506
507 ProfileCompilationInfo expected;
508 ASSERT_TRUE(expected.MergeWith(info1));
509 ASSERT_TRUE(expected.MergeWith(info2));
510 ASSERT_TRUE(expected.MergeWith(reference_info));
511 ASSERT_TRUE(expected.Equals(result));
512
513 // The information from profiles must remain the same.
514 CheckProfileInfo(profile1, info1);
515 CheckProfileInfo(profile2, info2);
516 }
517
TEST_F(ProfileAssistantTest,DoNotAdviseCompilation)518 TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) {
519 ScratchFile profile1;
520 ScratchFile profile2;
521 ScratchFile reference_profile;
522
523 std::vector<int> profile_fds({
524 GetFd(profile1),
525 GetFd(profile2)});
526 int reference_profile_fd = GetFd(reference_profile);
527
528 const uint16_t kNumberOfMethodsToSkipCompilation = 24; // Threshold is 100.
529 ProfileCompilationInfo info1;
530 SetupProfile(dex1, dex2, kNumberOfMethodsToSkipCompilation, 0, profile1, &info1);
531 ProfileCompilationInfo info2;
532 SetupProfile(dex3, dex4, kNumberOfMethodsToSkipCompilation, 0, profile2, &info2);
533
534 // We should not advise compilation.
535 ASSERT_EQ(ProfileAssistant::kSkipCompilation,
536 ProcessProfiles(profile_fds, reference_profile_fd));
537
538 // The information from profiles must remain the same.
539 ProfileCompilationInfo file_info1;
540 ASSERT_TRUE(profile1.GetFile()->ResetOffset());
541 ASSERT_TRUE(file_info1.Load(GetFd(profile1)));
542 ASSERT_TRUE(file_info1.Equals(info1));
543
544 ProfileCompilationInfo file_info2;
545 ASSERT_TRUE(profile2.GetFile()->ResetOffset());
546 ASSERT_TRUE(file_info2.Load(GetFd(profile2)));
547 ASSERT_TRUE(file_info2.Equals(info2));
548
549 // Reference profile files must remain empty.
550 ASSERT_EQ(0, reference_profile.GetFile()->GetLength());
551
552 // The information from profiles must remain the same.
553 CheckProfileInfo(profile1, info1);
554 CheckProfileInfo(profile2, info2);
555 }
556
TEST_F(ProfileAssistantTest,DoNotAdviseCompilationMethodPercentage)557 TEST_F(ProfileAssistantTest, DoNotAdviseCompilationMethodPercentage) {
558 const uint16_t kNumberOfMethodsInRefProfile = 6000;
559 const uint16_t kNumberOfMethodsInCurProfile = 6100; // Threshold is 2%.
560 // We should not advise compilation.
561 ASSERT_EQ(ProfileAssistant::kSkipCompilation,
562 CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile,
563 kNumberOfMethodsInRefProfile));
564 }
565
TEST_F(ProfileAssistantTest,ShouldAdviseCompilationMethodPercentage)566 TEST_F(ProfileAssistantTest, ShouldAdviseCompilationMethodPercentage) {
567 const uint16_t kNumberOfMethodsInRefProfile = 6000;
568 const uint16_t kNumberOfMethodsInCurProfile = 6200; // Threshold is 2%.
569 // We should advise compilation.
570 ASSERT_EQ(ProfileAssistant::kCompile,
571 CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile,
572 kNumberOfMethodsInRefProfile));
573 }
574
TEST_F(ProfileAssistantTest,DoNotdviseCompilationClassPercentage)575 TEST_F(ProfileAssistantTest, DoNotdviseCompilationClassPercentage) {
576 const uint16_t kNumberOfClassesInRefProfile = 6000;
577 const uint16_t kNumberOfClassesInCurProfile = 6110; // Threshold is 2%.
578 // We should not advise compilation.
579 ASSERT_EQ(ProfileAssistant::kSkipCompilation,
580 CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile,
581 kNumberOfClassesInRefProfile));
582 }
583
TEST_F(ProfileAssistantTest,ShouldAdviseCompilationClassPercentage)584 TEST_F(ProfileAssistantTest, ShouldAdviseCompilationClassPercentage) {
585 const uint16_t kNumberOfClassesInRefProfile = 6000;
586 const uint16_t kNumberOfClassesInCurProfile = 6120; // Threshold is 2%.
587 // We should advise compilation.
588 ASSERT_EQ(ProfileAssistant::kCompile,
589 CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile,
590 kNumberOfClassesInRefProfile));
591 }
592
TEST_F(ProfileAssistantTest,FailProcessingBecauseOfProfiles)593 TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) {
594 ScratchFile profile1;
595 ScratchFile profile2;
596 ScratchFile reference_profile;
597
598 std::vector<int> profile_fds({
599 GetFd(profile1),
600 GetFd(profile2)});
601 int reference_profile_fd = GetFd(reference_profile);
602
603 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
604 // Assign different hashes for the same dex file. This will make merging of information to fail.
605 ProfileCompilationInfo info1;
606 SetupProfile(dex1, dex2, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
607 ProfileCompilationInfo info2;
608 SetupProfile(
609 dex1_checksum_missmatch, dex2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
610
611 // We should fail processing.
612 ASSERT_EQ(ProfileAssistant::kErrorBadProfiles,
613 ProcessProfiles(profile_fds, reference_profile_fd));
614
615 // The information from profiles must remain the same.
616 CheckProfileInfo(profile1, info1);
617 CheckProfileInfo(profile2, info2);
618
619 // Reference profile files must still remain empty.
620 ASSERT_EQ(0, reference_profile.GetFile()->GetLength());
621 }
622
TEST_F(ProfileAssistantTest,FailProcessingBecauseOfReferenceProfiles)623 TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) {
624 ScratchFile profile1;
625 ScratchFile reference_profile;
626
627 std::vector<int> profile_fds({
628 GetFd(profile1)});
629 int reference_profile_fd = GetFd(reference_profile);
630
631 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
632 // Assign different hashes for the same dex file. This will make merging of information to fail.
633 ProfileCompilationInfo info1;
634 SetupProfile(dex1, dex2, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
635 ProfileCompilationInfo reference_info;
636 SetupProfile(
637 dex1_checksum_missmatch, dex2, kNumberOfMethodsToEnableCompilation, 0, reference_profile, &reference_info);
638
639 // We should not advise compilation.
640 ASSERT_TRUE(profile1.GetFile()->ResetOffset());
641 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
642 ASSERT_EQ(ProfileAssistant::kErrorBadProfiles,
643 ProcessProfiles(profile_fds, reference_profile_fd));
644
645 // The information from profiles must remain the same.
646 CheckProfileInfo(profile1, info1);
647 }
648
TEST_F(ProfileAssistantTest,TestProfileGeneration)649 TEST_F(ProfileAssistantTest, TestProfileGeneration) {
650 ScratchFile profile;
651 // Generate a test profile.
652 GenerateTestProfile(profile.GetFilename());
653
654 // Verify that the generated profile is valid and can be loaded.
655 ASSERT_TRUE(profile.GetFile()->ResetOffset());
656 ProfileCompilationInfo info;
657 ASSERT_TRUE(info.Load(GetFd(profile)));
658 }
659
TEST_F(ProfileAssistantTest,TestProfileGenerationWithIndexDex)660 TEST_F(ProfileAssistantTest, TestProfileGenerationWithIndexDex) {
661 ScratchFile profile;
662 // Generate a test profile passing in a dex file as reference.
663 GenerateTestProfileWithInputDex(profile.GetFilename());
664
665 // Verify that the generated profile is valid and can be loaded.
666 ASSERT_TRUE(profile.GetFile()->ResetOffset());
667 ProfileCompilationInfo info;
668 ASSERT_TRUE(info.Load(GetFd(profile)));
669 }
670
TEST_F(ProfileAssistantTest,TestProfileCreationAllMatch)671 TEST_F(ProfileAssistantTest, TestProfileCreationAllMatch) {
672 // Class names put here need to be in sorted order.
673 std::vector<std::string> class_names = {
674 "HLjava/lang/Object;-><init>()V",
675 "Ljava/lang/Comparable;",
676 "Ljava/lang/Math;",
677 "Ljava/lang/Object;",
678 "SPLjava/lang/Comparable;->compareTo(Ljava/lang/Object;)I",
679 };
680 std::string file_contents;
681 for (std::string& class_name : class_names) {
682 file_contents += class_name + std::string("\n");
683 }
684 std::string output_file_contents;
685 ASSERT_TRUE(CreateAndDump(file_contents, &output_file_contents));
686 ASSERT_EQ(output_file_contents, file_contents);
687 }
688
TEST_F(ProfileAssistantTest,TestArrayClass)689 TEST_F(ProfileAssistantTest, TestArrayClass) {
690 std::vector<std::string> class_names = {
691 "[Ljava/lang/Comparable;",
692 };
693 std::string file_contents;
694 for (std::string& class_name : class_names) {
695 file_contents += class_name + std::string("\n");
696 }
697 std::string output_file_contents;
698 ASSERT_TRUE(CreateAndDump(file_contents, &output_file_contents));
699 ASSERT_EQ(output_file_contents, file_contents);
700 }
701
TEST_F(ProfileAssistantTest,TestProfileCreationGenerateMethods)702 TEST_F(ProfileAssistantTest, TestProfileCreationGenerateMethods) {
703 // Class names put here need to be in sorted order.
704 std::vector<std::string> class_names = {
705 "HLjava/lang/Math;->*",
706 };
707 std::string input_file_contents;
708 std::string expected_contents;
709 for (std::string& class_name : class_names) {
710 input_file_contents += class_name + std::string("\n");
711 expected_contents += DescriptorToDot(class_name.c_str()) +
712 std::string("\n");
713 }
714 std::string output_file_contents;
715 ScratchFile profile_file;
716 EXPECT_TRUE(CreateProfile(input_file_contents,
717 profile_file.GetFilename(),
718 GetLibCoreDexFileNames()[0]));
719 ProfileCompilationInfo info;
720 profile_file.GetFile()->ResetOffset();
721 ASSERT_TRUE(info.Load(GetFd(profile_file)));
722 // Verify that the profile has matching methods.
723 ScopedObjectAccess soa(Thread::Current());
724 ObjPtr<mirror::Class> klass = GetClass(soa, /* class_loader= */ nullptr, "Ljava/lang/Math;");
725 ASSERT_TRUE(klass != nullptr);
726 size_t method_count = 0;
727 for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) {
728 if (!method.IsCopied() && method.GetCodeItem() != nullptr) {
729 ++method_count;
730 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi =
731 info.GetHotMethodInfo(MethodReference(method.GetDexFile(), method.GetDexMethodIndex()));
732 ASSERT_TRUE(pmi != nullptr) << method.PrettyMethod();
733 }
734 }
735 EXPECT_GT(method_count, 0u);
736 }
737
JoinProfileLines(const std::vector<std::string> & lines)738 static std::string JoinProfileLines(const std::vector<std::string>& lines) {
739 std::string result = android::base::Join(lines, '\n');
740 return result + '\n';
741 }
742
TEST_F(ProfileAssistantTest,TestBootImageProfile)743 TEST_F(ProfileAssistantTest, TestBootImageProfile) {
744 const std::string core_dex = GetLibCoreDexFileNames()[0];
745
746 std::vector<ScratchFile> profiles;
747
748 // In image with enough clean occurrences.
749 const std::string kCleanClass = "Ljava/lang/CharSequence;";
750 // In image with enough dirty occurrences.
751 const std::string kDirtyClass = "Ljava/lang/Object;";
752 // Not in image becauseof not enough occurrences.
753 const std::string kUncommonCleanClass = "Ljava/lang/Process;";
754 const std::string kUncommonDirtyClass = "Ljava/lang/Package;";
755 // Method that is common and hot. Should end up in profile.
756 const std::string kCommonHotMethod = "Ljava/lang/Comparable;->compareTo(Ljava/lang/Object;)I";
757 // Uncommon method, should not end up in profile
758 const std::string kUncommonMethod = "Ljava/util/HashMap;-><init>()V";
759 // Method that gets marked as hot since it's in multiple profile and marked as startup.
760 const std::string kStartupMethodForUpgrade = "Ljava/util/ArrayList;->clear()V";
761 // Startup method used by a special package which will get a different threshold;
762 const std::string kSpecialPackageStartupMethod =
763 "Ljava/lang/Object;->toString()Ljava/lang/String;";
764 // Method used by a special package which will get a different threshold;
765 const std::string kUncommonSpecialPackageMethod = "Ljava/lang/Object;->hashCode()I";
766 // Blacklisted class
767 const std::string kPreloadedBlacklistedClass = "Ljava/lang/Thread;";
768
769 // Thresholds for this test.
770 static const size_t kDirtyThreshold = 100;
771 static const size_t kCleanThreshold = 50;
772 static const size_t kPreloadedThreshold = 100;
773 static const size_t kMethodThreshold = 75;
774 static const size_t kSpecialThreshold = 50;
775 const std::string kSpecialPackage = "dex4";
776
777 // Create boot profile content, attributing the classes and methods to different dex files.
778 std::vector<std::string> input_data = {
779 "{dex1}" + kCleanClass,
780 "{dex1}" + kDirtyClass,
781 "{dex1}" + kUncommonCleanClass,
782 "{dex1}H" + kCommonHotMethod,
783 "{dex1}P" + kStartupMethodForUpgrade,
784 "{dex1}" + kUncommonDirtyClass,
785 "{dex1}" + kPreloadedBlacklistedClass,
786
787 "{dex2}" + kCleanClass,
788 "{dex2}" + kDirtyClass,
789 "{dex2}P" + kCommonHotMethod,
790 "{dex2}P" + kStartupMethodForUpgrade,
791 "{dex2}" + kUncommonDirtyClass,
792 "{dex2}" + kPreloadedBlacklistedClass,
793
794 "{dex3}P" + kUncommonMethod,
795 "{dex3}PS" + kStartupMethodForUpgrade,
796 "{dex3}S" + kCommonHotMethod,
797 "{dex3}S" + kSpecialPackageStartupMethod,
798 "{dex3}" + kDirtyClass,
799 "{dex3}" + kPreloadedBlacklistedClass,
800
801 "{dex4}" + kDirtyClass,
802 "{dex4}P" + kCommonHotMethod,
803 "{dex4}S" + kSpecialPackageStartupMethod,
804 "{dex4}P" + kUncommonSpecialPackageMethod,
805 "{dex4}" + kPreloadedBlacklistedClass,
806 };
807 std::string input_file_contents = JoinProfileLines(input_data);
808
809 ScratchFile preloaded_class_blacklist;
810 std::string blacklist_content = DescriptorToDot(kPreloadedBlacklistedClass.c_str());
811 EXPECT_TRUE(preloaded_class_blacklist.GetFile()->WriteFully(
812 blacklist_content.c_str(), blacklist_content.length()));
813
814 EXPECT_EQ(0, preloaded_class_blacklist.GetFile()->Flush());
815 EXPECT_TRUE(preloaded_class_blacklist.GetFile()->ResetOffset());
816 // Expected data
817 std::vector<std::string> expected_data = {
818 kCleanClass,
819 kDirtyClass,
820 kPreloadedBlacklistedClass,
821 "HSP" + kCommonHotMethod,
822 "HS" + kSpecialPackageStartupMethod,
823 "HSP" + kStartupMethodForUpgrade
824 };
825 std::string expected_profile_content = JoinProfileLines(expected_data);
826
827 std::vector<std::string> expected_preloaded_data = {
828 DescriptorToDot(kDirtyClass.c_str())
829 };
830 std::string expected_preloaded_content = JoinProfileLines(expected_preloaded_data);
831
832 ScratchFile profile;
833 EXPECT_TRUE(CreateProfile(input_file_contents, profile.GetFilename(), core_dex));
834
835 ProfileCompilationInfo bootProfile;
836 bootProfile.Load(profile.GetFilename(), /*for_boot_image*/ true);
837
838 // Generate the boot profile.
839 ScratchFile out_profile;
840 ScratchFile out_preloaded_classes;
841 ASSERT_TRUE(out_profile.GetFile()->ResetOffset());
842 ASSERT_TRUE(out_preloaded_classes.GetFile()->ResetOffset());
843 std::vector<std::string> args;
844 args.push_back(GetProfmanCmd());
845 args.push_back("--generate-boot-image-profile");
846 args.push_back("--class-threshold=" + std::to_string(kDirtyThreshold));
847 args.push_back("--clean-class-threshold=" + std::to_string(kCleanThreshold));
848 args.push_back("--method-threshold=" + std::to_string(kMethodThreshold));
849 args.push_back("--preloaded-class-threshold=" + std::to_string(kPreloadedThreshold));
850 args.push_back(
851 "--special-package=" + kSpecialPackage + ":" + std::to_string(kSpecialThreshold));
852 args.push_back("--profile-file=" + profile.GetFilename());
853 args.push_back("--out-profile-path=" + out_profile.GetFilename());
854 args.push_back("--out-preloaded-classes-path=" + out_preloaded_classes.GetFilename());
855 args.push_back("--apk=" + core_dex);
856 args.push_back("--dex-location=" + core_dex);
857 args.push_back("--preloaded-classes-blacklist=" + preloaded_class_blacklist.GetFilename());
858
859 std::string error;
860 ASSERT_EQ(ExecAndReturnCode(args, &error), 0) << error;
861 ASSERT_TRUE(out_profile.GetFile()->ResetOffset());
862
863 // Verify the boot profile contents.
864 std::string output_profile_contents;
865 ASSERT_TRUE(android::base::ReadFileToString(
866 out_profile.GetFilename(), &output_profile_contents));
867 ASSERT_EQ(output_profile_contents, expected_profile_content);
868
869 // Verify the preloaded classes content.
870 std::string output_preloaded_contents;
871 ASSERT_TRUE(android::base::ReadFileToString(
872 out_preloaded_classes.GetFilename(), &output_preloaded_contents));
873 ASSERT_EQ(output_preloaded_contents, expected_preloaded_content);
874 }
875
TEST_F(ProfileAssistantTest,TestBootImageProfileWith2RawProfiles)876 TEST_F(ProfileAssistantTest, TestBootImageProfileWith2RawProfiles) {
877 const std::string core_dex = GetLibCoreDexFileNames()[0];
878
879 std::vector<ScratchFile> profiles;
880
881 const std::string kCommonClassUsedByDex1 = "Ljava/lang/CharSequence;";
882 const std::string kCommonClassUsedByDex1Dex2 = "Ljava/lang/Object;";
883 const std::string kUncommonClass = "Ljava/lang/Process;";
884 const std::string kCommonHotMethodUsedByDex1 =
885 "Ljava/lang/Comparable;->compareTo(Ljava/lang/Object;)I";
886 const std::string kCommonHotMethodUsedByDex1Dex2 = "Ljava/lang/Object;->hashCode()I";
887 const std::string kUncommonHotMethod = "Ljava/util/HashMap;-><init>()V";
888
889
890 // Thresholds for this test.
891 static const size_t kDirtyThreshold = 100;
892 static const size_t kCleanThreshold = 100;
893 static const size_t kMethodThreshold = 100;
894
895 // Create boot profile content, attributing the classes and methods to different dex files.
896 std::vector<std::string> input_data1 = {
897 "{dex1}" + kCommonClassUsedByDex1,
898 "{dex1}" + kCommonClassUsedByDex1Dex2,
899 "{dex1}" + kUncommonClass,
900 "{dex1}H" + kCommonHotMethodUsedByDex1Dex2,
901 "{dex1}" + kCommonHotMethodUsedByDex1,
902 };
903 std::vector<std::string> input_data2 = {
904 "{dex1}" + kCommonClassUsedByDex1,
905 "{dex2}" + kCommonClassUsedByDex1Dex2,
906 "{dex1}H" + kCommonHotMethodUsedByDex1,
907 "{dex2}" + kCommonHotMethodUsedByDex1Dex2,
908 "{dex1}" + kUncommonHotMethod,
909 };
910 std::string input_file_contents1 = JoinProfileLines(input_data1);
911 std::string input_file_contents2 = JoinProfileLines(input_data2);
912
913 // Expected data
914 std::vector<std::string> expected_data = {
915 kCommonClassUsedByDex1,
916 kCommonClassUsedByDex1Dex2,
917 "H" + kCommonHotMethodUsedByDex1,
918 "H" + kCommonHotMethodUsedByDex1Dex2
919 };
920 std::string expected_profile_content = JoinProfileLines(expected_data);
921
922 ScratchFile profile1;
923 ScratchFile profile2;
924 EXPECT_TRUE(CreateProfile(input_file_contents1, profile1.GetFilename(), core_dex));
925 EXPECT_TRUE(CreateProfile(input_file_contents2, profile2.GetFilename(), core_dex));
926
927 ProfileCompilationInfo boot_profile1;
928 ProfileCompilationInfo boot_profile2;
929 boot_profile1.Load(profile1.GetFilename(), /*for_boot_image*/ true);
930 boot_profile2.Load(profile2.GetFilename(), /*for_boot_image*/ true);
931
932 // Generate the boot profile.
933 ScratchFile out_profile;
934 ScratchFile out_preloaded_classes;
935 ASSERT_TRUE(out_profile.GetFile()->ResetOffset());
936 ASSERT_TRUE(out_preloaded_classes.GetFile()->ResetOffset());
937 std::vector<std::string> args;
938 args.push_back(GetProfmanCmd());
939 args.push_back("--generate-boot-image-profile");
940 args.push_back("--class-threshold=" + std::to_string(kDirtyThreshold));
941 args.push_back("--clean-class-threshold=" + std::to_string(kCleanThreshold));
942 args.push_back("--method-threshold=" + std::to_string(kMethodThreshold));
943 args.push_back("--profile-file=" + profile1.GetFilename());
944 args.push_back("--profile-file=" + profile2.GetFilename());
945 args.push_back("--out-profile-path=" + out_profile.GetFilename());
946 args.push_back("--out-preloaded-classes-path=" + out_preloaded_classes.GetFilename());
947 args.push_back("--apk=" + core_dex);
948 args.push_back("--dex-location=" + core_dex);
949
950 std::string error;
951 ASSERT_EQ(ExecAndReturnCode(args, &error), 0) << error;
952 ASSERT_TRUE(out_profile.GetFile()->ResetOffset());
953
954 // Verify the boot profile contents.
955 std::string output_profile_contents;
956 ASSERT_TRUE(android::base::ReadFileToString(
957 out_profile.GetFilename(), &output_profile_contents));
958 ASSERT_EQ(output_profile_contents, expected_profile_content);
959 }
960
TEST_F(ProfileAssistantTest,TestProfileCreationOneNotMatched)961 TEST_F(ProfileAssistantTest, TestProfileCreationOneNotMatched) {
962 // Class names put here need to be in sorted order.
963 std::vector<std::string> class_names = {
964 "Ldoesnt/match/this/one;",
965 "Ljava/lang/Comparable;",
966 "Ljava/lang/Object;"
967 };
968 std::string input_file_contents;
969 for (std::string& class_name : class_names) {
970 input_file_contents += class_name + std::string("\n");
971 }
972 std::string output_file_contents;
973 ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents));
974 std::string expected_contents =
975 class_names[1] + std::string("\n") +
976 class_names[2] + std::string("\n");
977 ASSERT_EQ(output_file_contents, expected_contents);
978 }
979
TEST_F(ProfileAssistantTest,TestProfileCreationNoneMatched)980 TEST_F(ProfileAssistantTest, TestProfileCreationNoneMatched) {
981 // Class names put here need to be in sorted order.
982 std::vector<std::string> class_names = {
983 "Ldoesnt/match/this/one;",
984 "Ldoesnt/match/this/one/either;",
985 "Lnor/this/one;"
986 };
987 std::string input_file_contents;
988 for (std::string& class_name : class_names) {
989 input_file_contents += class_name + std::string("\n");
990 }
991 std::string output_file_contents;
992 ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents));
993 std::string expected_contents("");
994 ASSERT_EQ(output_file_contents, expected_contents);
995 }
996
TEST_F(ProfileAssistantTest,TestProfileCreateInlineCache)997 TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) {
998 // Create the profile content.
999 std::vector<std::string> methods = {
1000 "HLTestInline;->inlineMonomorphic(LSuper;)I+LSubA;",
1001 "HLTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;",
1002 "HLTestInline;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;",
1003 "HLTestInline;->inlineMissingTypes(LSuper;)I+missing_types",
1004 "HLTestInline;->noInlineCache(LSuper;)I"
1005 };
1006 std::string input_file_contents;
1007 for (std::string& m : methods) {
1008 input_file_contents += m + std::string("\n");
1009 }
1010
1011 // Create the profile and save it to disk.
1012 ScratchFile profile_file;
1013 ASSERT_TRUE(CreateProfile(input_file_contents,
1014 profile_file.GetFilename(),
1015 GetTestDexFileName("ProfileTestMultiDex")));
1016
1017 // Load the profile from disk.
1018 ProfileCompilationInfo info;
1019 profile_file.GetFile()->ResetOffset();
1020 ASSERT_TRUE(info.Load(GetFd(profile_file)));
1021
1022 // Load the dex files and verify that the profile contains the expected methods info.
1023 ScopedObjectAccess soa(Thread::Current());
1024 jobject class_loader = LoadDex("ProfileTestMultiDex");
1025 ASSERT_NE(class_loader, nullptr);
1026
1027 StackHandleScope<3> hs(soa.Self());
1028 Handle<mirror::Class> sub_a = hs.NewHandle(GetClass(soa, class_loader, "LSubA;"));
1029 Handle<mirror::Class> sub_b = hs.NewHandle(GetClass(soa, class_loader, "LSubB;"));
1030 Handle<mirror::Class> sub_c = hs.NewHandle(GetClass(soa, class_loader, "LSubC;"));
1031
1032 ASSERT_TRUE(sub_a != nullptr);
1033 ASSERT_TRUE(sub_b != nullptr);
1034 ASSERT_TRUE(sub_c != nullptr);
1035
1036 {
1037 // Verify that method inlineMonomorphic has the expected inline caches and nothing else.
1038 ArtMethod* inline_monomorphic = GetVirtualMethod(class_loader,
1039 "LTestInline;",
1040 "inlineMonomorphic");
1041 ASSERT_TRUE(inline_monomorphic != nullptr);
1042 TypeReferenceSet expected_monomorphic;
1043 expected_monomorphic.insert(MakeTypeReference(sub_a.Get()));
1044 AssertInlineCaches(inline_monomorphic,
1045 expected_monomorphic,
1046 info,
1047 /*is_megamorphic=*/false,
1048 /*is_missing_types=*/false);
1049 }
1050
1051 {
1052 // Verify that method inlinePolymorphic has the expected inline caches and nothing else.
1053 ArtMethod* inline_polymorhic = GetVirtualMethod(class_loader,
1054 "LTestInline;",
1055 "inlinePolymorphic");
1056 ASSERT_TRUE(inline_polymorhic != nullptr);
1057 TypeReferenceSet expected_polymorphic;
1058 expected_polymorphic.insert(MakeTypeReference(sub_a.Get()));
1059 expected_polymorphic.insert(MakeTypeReference(sub_b.Get()));
1060 expected_polymorphic.insert(MakeTypeReference(sub_c.Get()));
1061 AssertInlineCaches(inline_polymorhic,
1062 expected_polymorphic,
1063 info,
1064 /*is_megamorphic=*/false,
1065 /*is_missing_types=*/false);
1066 }
1067
1068 {
1069 // Verify that method inlineMegamorphic has the expected inline caches and nothing else.
1070 ArtMethod* inline_megamorphic = GetVirtualMethod(class_loader,
1071 "LTestInline;",
1072 "inlineMegamorphic");
1073 ASSERT_TRUE(inline_megamorphic != nullptr);
1074 TypeReferenceSet expected_megamorphic;
1075 AssertInlineCaches(inline_megamorphic,
1076 expected_megamorphic,
1077 info,
1078 /*is_megamorphic=*/true,
1079 /*is_missing_types=*/false);
1080 }
1081
1082 {
1083 // Verify that method inlineMegamorphic has the expected inline caches and nothing else.
1084 ArtMethod* inline_missing_types = GetVirtualMethod(class_loader,
1085 "LTestInline;",
1086 "inlineMissingTypes");
1087 ASSERT_TRUE(inline_missing_types != nullptr);
1088 TypeReferenceSet expected_missing_Types;
1089 AssertInlineCaches(inline_missing_types,
1090 expected_missing_Types,
1091 info,
1092 /*is_megamorphic=*/false,
1093 /*is_missing_types=*/true);
1094 }
1095
1096 {
1097 // Verify that method noInlineCache has no inline caches in the profile.
1098 ArtMethod* no_inline_cache = GetVirtualMethod(class_loader, "LTestInline;", "noInlineCache");
1099 ASSERT_TRUE(no_inline_cache != nullptr);
1100 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi_no_inline_cache =
1101 info.GetHotMethodInfo(MethodReference(
1102 no_inline_cache->GetDexFile(), no_inline_cache->GetDexMethodIndex()));
1103 ASSERT_TRUE(pmi_no_inline_cache != nullptr);
1104 ASSERT_TRUE(pmi_no_inline_cache->inline_caches->empty());
1105 }
1106 }
1107
TEST_F(ProfileAssistantTest,MergeProfilesWithDifferentDexOrder)1108 TEST_F(ProfileAssistantTest, MergeProfilesWithDifferentDexOrder) {
1109 ScratchFile profile1;
1110 ScratchFile reference_profile;
1111
1112 std::vector<int> profile_fds({GetFd(profile1)});
1113 int reference_profile_fd = GetFd(reference_profile);
1114
1115 // The new profile info will contain the methods with indices 0-100.
1116 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
1117 ProfileCompilationInfo info1;
1118 SetupProfile(dex1, dex2, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1,
1119 /*start_method_index=*/0, /*reverse_dex_write_order=*/false);
1120
1121 // The reference profile info will contain the methods with indices 50-150.
1122 // When setting up the profile reverse the order in which the dex files
1123 // are added to the profile. This will verify that profman merges profiles
1124 // with a different dex order correctly.
1125 const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
1126 ProfileCompilationInfo reference_info;
1127 SetupProfile(dex1, dex2, kNumberOfMethodsAlreadyCompiled, 0, reference_profile,
1128 &reference_info, kNumberOfMethodsToEnableCompilation / 2, /*reverse_dex_write_order=*/true);
1129
1130 // We should advise compilation.
1131 ASSERT_EQ(ProfileAssistant::kCompile,
1132 ProcessProfiles(profile_fds, reference_profile_fd));
1133
1134 // The resulting compilation info must be equal to the merge of the inputs.
1135 ProfileCompilationInfo result;
1136 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
1137 ASSERT_TRUE(result.Load(reference_profile_fd));
1138
1139 ProfileCompilationInfo expected;
1140 ASSERT_TRUE(expected.MergeWith(reference_info));
1141 ASSERT_TRUE(expected.MergeWith(info1));
1142 ASSERT_TRUE(expected.Equals(result));
1143
1144 // The information from profile must remain the same.
1145 CheckProfileInfo(profile1, info1);
1146 }
1147
TEST_F(ProfileAssistantTest,TestProfileCreateWithInvalidData)1148 TEST_F(ProfileAssistantTest, TestProfileCreateWithInvalidData) {
1149 // Create the profile content.
1150 std::vector<std::string> profile_methods = {
1151 "HLTestInline;->inlineMonomorphic(LSuper;)I+invalid_class",
1152 "HLTestInline;->invalid_method",
1153 "invalid_class"
1154 };
1155 std::string input_file_contents;
1156 for (std::string& m : profile_methods) {
1157 input_file_contents += m + std::string("\n");
1158 }
1159
1160 // Create the profile and save it to disk.
1161 ScratchFile profile_file;
1162 std::string dex_filename = GetTestDexFileName("ProfileTestMultiDex");
1163 ASSERT_TRUE(CreateProfile(input_file_contents,
1164 profile_file.GetFilename(),
1165 dex_filename));
1166
1167 // Load the profile from disk.
1168 ProfileCompilationInfo info;
1169 profile_file.GetFile()->ResetOffset();
1170 ASSERT_TRUE(info.Load(GetFd(profile_file)));
1171
1172 // Load the dex files and verify that the profile contains the expected methods info.
1173 ScopedObjectAccess soa(Thread::Current());
1174 jobject class_loader = LoadDex("ProfileTestMultiDex");
1175 ASSERT_NE(class_loader, nullptr);
1176
1177 ArtMethod* inline_monomorphic = GetVirtualMethod(class_loader,
1178 "LTestInline;",
1179 "inlineMonomorphic");
1180 const DexFile* dex_file = inline_monomorphic->GetDexFile();
1181
1182 // Verify that the inline cache contains the invalid type.
1183 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi =
1184 info.GetHotMethodInfo(MethodReference(dex_file, inline_monomorphic->GetDexMethodIndex()));
1185 ASSERT_TRUE(pmi != nullptr);
1186 ASSERT_EQ(pmi->inline_caches->size(), 1u);
1187 const ProfileCompilationInfo::DexPcData& dex_pc_data = pmi->inline_caches->begin()->second;
1188 dex::TypeIndex invalid_class_index(std::numeric_limits<uint16_t>::max() - 1);
1189 ASSERT_EQ(1u, dex_pc_data.classes.size());
1190 ASSERT_EQ(invalid_class_index, dex_pc_data.classes.begin()->type_index);
1191
1192 // Verify that the start-up classes contain the invalid class.
1193 std::set<dex::TypeIndex> classes;
1194 std::set<uint16_t> hot_methods;
1195 std::set<uint16_t> startup_methods;
1196 std::set<uint16_t> post_start_methods;
1197 ASSERT_TRUE(info.GetClassesAndMethods(*dex_file,
1198 &classes,
1199 &hot_methods,
1200 &startup_methods,
1201 &post_start_methods));
1202 ASSERT_EQ(1u, classes.size());
1203 ASSERT_TRUE(classes.find(invalid_class_index) != classes.end());
1204
1205 // Verify that the invalid method did not get in the profile.
1206 ASSERT_EQ(1u, hot_methods.size());
1207 uint16_t invalid_method_index = std::numeric_limits<uint16_t>::max() - 1;
1208 ASSERT_FALSE(hot_methods.find(invalid_method_index) != hot_methods.end());
1209 }
1210
TEST_F(ProfileAssistantTest,DumpOnly)1211 TEST_F(ProfileAssistantTest, DumpOnly) {
1212 ScratchFile profile;
1213
1214 const uint32_t kNumberOfMethods = 64;
1215 std::vector<uint32_t> hot_methods;
1216 std::vector<uint32_t> startup_methods;
1217 std::vector<uint32_t> post_startup_methods;
1218 for (size_t i = 0; i < kNumberOfMethods; ++i) {
1219 if (i % 2 == 0) {
1220 hot_methods.push_back(i);
1221 }
1222 if (i % 3 == 1) {
1223 startup_methods.push_back(i);
1224 }
1225 if (i % 4 == 2) {
1226 post_startup_methods.push_back(i);
1227 }
1228 }
1229 EXPECT_GT(hot_methods.size(), 0u);
1230 EXPECT_GT(startup_methods.size(), 0u);
1231 EXPECT_GT(post_startup_methods.size(), 0u);
1232 ProfileCompilationInfo info1;
1233 SetupBasicProfile(dex1,
1234 hot_methods,
1235 startup_methods,
1236 post_startup_methods,
1237 profile,
1238 &info1);
1239 std::string output;
1240 DumpOnly(profile.GetFilename(), &output);
1241 const size_t hot_offset = output.find("hot methods:");
1242 const size_t startup_offset = output.find("startup methods:");
1243 const size_t post_startup_offset = output.find("post startup methods:");
1244 const size_t classes_offset = output.find("classes:");
1245 ASSERT_NE(hot_offset, std::string::npos);
1246 ASSERT_NE(startup_offset, std::string::npos);
1247 ASSERT_NE(post_startup_offset, std::string::npos);
1248 ASSERT_LT(hot_offset, startup_offset);
1249 ASSERT_LT(startup_offset, post_startup_offset);
1250 // Check the actual contents of the dump by looking at the offsets of the methods.
1251 for (uint32_t m : hot_methods) {
1252 const size_t pos = output.find(std::to_string(m) + "[],", hot_offset);
1253 ASSERT_NE(pos, std::string::npos) << output;
1254 EXPECT_LT(pos, startup_offset) << output;
1255 }
1256 for (uint32_t m : startup_methods) {
1257 const size_t pos = output.find(std::to_string(m) + ",", startup_offset);
1258 ASSERT_NE(pos, std::string::npos) << output;
1259 EXPECT_LT(pos, post_startup_offset) << output;
1260 }
1261 for (uint32_t m : post_startup_methods) {
1262 const size_t pos = output.find(std::to_string(m) + ",", post_startup_offset);
1263 ASSERT_NE(pos, std::string::npos) << output;
1264 EXPECT_LT(pos, classes_offset) << output;
1265 }
1266 }
1267
TEST_F(ProfileAssistantTest,MergeProfilesWithFilter)1268 TEST_F(ProfileAssistantTest, MergeProfilesWithFilter) {
1269 ScratchFile profile1;
1270 ScratchFile profile2;
1271 ScratchFile reference_profile;
1272
1273 std::vector<int> profile_fds({
1274 GetFd(profile1),
1275 GetFd(profile2)});
1276 int reference_profile_fd = GetFd(reference_profile);
1277
1278 // Use a real dex file to generate profile test data.
1279 // The file will be used during merging to filter unwanted data.
1280 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
1281 const DexFile& d1 = *dex_files[0];
1282 const DexFile& d2 = *dex_files[1];
1283 // The new profile info will contain the methods with indices 0-100.
1284 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
1285 ProfileCompilationInfo info1;
1286 SetupProfile(&d1, dex1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
1287 ProfileCompilationInfo info2;
1288 SetupProfile(&d2, dex2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
1289
1290
1291 // The reference profile info will contain the methods with indices 50-150.
1292 const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
1293 ProfileCompilationInfo reference_info;
1294 SetupProfile(&d1, dex1,
1295 kNumberOfMethodsAlreadyCompiled, 0, reference_profile,
1296 &reference_info, kNumberOfMethodsToEnableCompilation / 2);
1297
1298 // Run profman and pass the dex file with --apk-fd.
1299 android::base::unique_fd apk_fd(
1300 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY)); // NOLINT
1301 ASSERT_GE(apk_fd.get(), 0);
1302
1303 std::string profman_cmd = GetProfmanCmd();
1304 std::vector<std::string> argv_str;
1305 argv_str.push_back(profman_cmd);
1306 argv_str.push_back("--profile-file-fd=" + std::to_string(profile1.GetFd()));
1307 argv_str.push_back("--profile-file-fd=" + std::to_string(profile2.GetFd()));
1308 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
1309 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
1310 std::string error;
1311
1312 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfileAssistant::kCompile) << error;
1313
1314 // Verify that we can load the result.
1315
1316 ProfileCompilationInfo result;
1317 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
1318 ASSERT_TRUE(result.Load(reference_profile_fd));
1319
1320
1321 ASSERT_TRUE(profile1.GetFile()->ResetOffset());
1322 ASSERT_TRUE(profile2.GetFile()->ResetOffset());
1323 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
1324
1325 // Verify that the result filtered out data not belonging to the dex file.
1326 // This is equivalent to checking that the result is equal to the merging of
1327 // all profiles while filtering out data not belonging to the dex file.
1328
1329 ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
1330 [&d1, &d2](const std::string& dex_location, uint32_t checksum) -> bool {
1331 return (dex_location == ProfileCompilationInfo::GetProfileDexFileBaseKey(d1.GetLocation())
1332 && checksum == d1.GetLocationChecksum())
1333 || (dex_location == ProfileCompilationInfo::GetProfileDexFileBaseKey(d2.GetLocation())
1334 && checksum == d2.GetLocationChecksum());
1335 };
1336
1337 ProfileCompilationInfo info1_filter;
1338 ProfileCompilationInfo info2_filter;
1339 ProfileCompilationInfo expected;
1340
1341 info2_filter.Load(profile1.GetFd(), /*merge_classes=*/ true, filter_fn);
1342 info2_filter.Load(profile2.GetFd(), /*merge_classes=*/ true, filter_fn);
1343 expected.Load(reference_profile.GetFd(), /*merge_classes=*/ true, filter_fn);
1344
1345 ASSERT_TRUE(expected.MergeWith(info1_filter));
1346 ASSERT_TRUE(expected.MergeWith(info2_filter));
1347
1348 ASSERT_TRUE(expected.Equals(result));
1349 }
1350
TEST_F(ProfileAssistantTest,CopyAndUpdateProfileKey)1351 TEST_F(ProfileAssistantTest, CopyAndUpdateProfileKey) {
1352 ScratchFile profile1;
1353 ScratchFile reference_profile;
1354
1355 // Use a real dex file to generate profile test data. During the copy-and-update the
1356 // matching is done based on checksum so we have to match with the real thing.
1357 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
1358 const DexFile& d1 = *dex_files[0];
1359 const DexFile& d2 = *dex_files[1];
1360
1361 ProfileCompilationInfo info1;
1362 uint16_t num_methods_to_add = std::min(d1.NumMethodIds(), d2.NumMethodIds());
1363
1364 FakeDexStorage local_storage;
1365 const DexFile* dex_to_be_updated1 = local_storage.AddFakeDex(
1366 "fake-location1", d1.GetLocationChecksum(), d1.NumMethodIds());
1367 const DexFile* dex_to_be_updated2 = local_storage.AddFakeDex(
1368 "fake-location2", d2.GetLocationChecksum(), d2.NumMethodIds());
1369 SetupProfile(dex_to_be_updated1,
1370 dex_to_be_updated2,
1371 num_methods_to_add,
1372 /*number_of_classes=*/ 0,
1373 profile1,
1374 &info1);
1375
1376 // Run profman and pass the dex file with --apk-fd.
1377 android::base::unique_fd apk_fd(
1378 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY)); // NOLINT
1379 ASSERT_GE(apk_fd.get(), 0);
1380
1381 std::string profman_cmd = GetProfmanCmd();
1382 std::vector<std::string> argv_str;
1383 argv_str.push_back(profman_cmd);
1384 argv_str.push_back("--profile-file-fd=" + std::to_string(profile1.GetFd()));
1385 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
1386 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
1387 argv_str.push_back("--copy-and-update-profile-key");
1388 std::string error;
1389
1390 ASSERT_EQ(ExecAndReturnCode(argv_str, &error), 0) << error;
1391
1392 // Verify that we can load the result.
1393 ProfileCompilationInfo result;
1394 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
1395 ASSERT_TRUE(result.Load(reference_profile.GetFd()));
1396
1397 // Verify that the renaming was done.
1398 for (uint16_t i = 0; i < num_methods_to_add; i ++) {
1399 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi;
1400 ASSERT_TRUE(result.GetHotMethodInfo(MethodReference(&d1, i)) != nullptr) << i;
1401 ASSERT_TRUE(result.GetHotMethodInfo(MethodReference(&d2, i)) != nullptr) << i;
1402
1403 ASSERT_TRUE(result.GetHotMethodInfo(MethodReference(dex_to_be_updated1, i)) == nullptr);
1404 ASSERT_TRUE(result.GetHotMethodInfo(MethodReference(dex_to_be_updated2, i)) == nullptr);
1405 }
1406 }
1407
TEST_F(ProfileAssistantTest,BootImageMerge)1408 TEST_F(ProfileAssistantTest, BootImageMerge) {
1409 ScratchFile profile;
1410 ScratchFile reference_profile;
1411 std::vector<int> profile_fds({GetFd(profile)});
1412 int reference_profile_fd = GetFd(reference_profile);
1413 std::vector<uint32_t> hot_methods_cur;
1414 std::vector<uint32_t> hot_methods_ref;
1415 std::vector<uint32_t> empty_vector;
1416 size_t num_methods = 100;
1417 for (size_t i = 0; i < num_methods; ++i) {
1418 hot_methods_cur.push_back(i);
1419 }
1420 for (size_t i = 0; i < num_methods; ++i) {
1421 hot_methods_ref.push_back(i);
1422 }
1423 ProfileCompilationInfo info1;
1424 SetupBasicProfile(dex1, hot_methods_cur, empty_vector, empty_vector,
1425 profile, &info1);
1426 ProfileCompilationInfo info2(/*for_boot_image=*/true);
1427 SetupBasicProfile(dex1, hot_methods_ref, empty_vector, empty_vector,
1428 reference_profile, &info2);
1429
1430 std::vector<const std::string> extra_args({"--force-merge", "--boot-image-merge"});
1431
1432 int return_code = ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
1433
1434 ASSERT_EQ(return_code, ProfileAssistant::kSuccess);
1435
1436 // Verify the result: it should be equal to info2 since info1 is a regular profile
1437 // and should be ignored.
1438 ProfileCompilationInfo result;
1439 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
1440 ASSERT_TRUE(result.Load(reference_profile.GetFd()));
1441 ASSERT_TRUE(result.Equals(info2));
1442 }
1443
1444 // Under default behaviour we should not advice compilation
1445 // and the reference profile should not be updated.
1446 // However we pass --force-merge to force aggregation and in this case
1447 // we should see an update.
TEST_F(ProfileAssistantTest,ForceMerge)1448 TEST_F(ProfileAssistantTest, ForceMerge) {
1449 const uint16_t kNumberOfClassesInRefProfile = 6000;
1450 const uint16_t kNumberOfClassesInCurProfile = 6110; // Threshold is 2%.
1451
1452 ScratchFile profile;
1453 ScratchFile reference_profile;
1454
1455 std::vector<int> profile_fds({ GetFd(profile)});
1456 int reference_profile_fd = GetFd(reference_profile);
1457
1458 ProfileCompilationInfo info1;
1459 SetupProfile(dex1, dex2, 0, kNumberOfClassesInRefProfile, profile, &info1);
1460 ProfileCompilationInfo info2;
1461 SetupProfile(dex1, dex2, 0, kNumberOfClassesInCurProfile, reference_profile, &info2);
1462
1463 std::vector<const std::string> extra_args({"--force-merge"});
1464 int return_code = ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
1465
1466 ASSERT_EQ(return_code, ProfileAssistant::kSuccess);
1467
1468 // Check that the result is the aggregation.
1469 ProfileCompilationInfo result;
1470 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
1471 ASSERT_TRUE(result.Load(reference_profile.GetFd()));
1472 ASSERT_TRUE(info1.MergeWith(info2));
1473 ASSERT_TRUE(result.Equals(info1));
1474 }
1475
1476 // Test that we consider the annations when we merge boot image profiles.
TEST_F(ProfileAssistantTest,BootImageMergeWithAnnotations)1477 TEST_F(ProfileAssistantTest, BootImageMergeWithAnnotations) {
1478 ScratchFile profile;
1479 ScratchFile reference_profile;
1480
1481 std::vector<int> profile_fds({GetFd(profile)});
1482 int reference_profile_fd = GetFd(reference_profile);
1483
1484 // Use a real dex file to generate profile test data so that we can pass descriptors to profman.
1485 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
1486 const DexFile& d1 = *dex_files[0];
1487 const DexFile& d2 = *dex_files[1];
1488 // The new profile info will contain the methods with indices 0-100.
1489 ProfileCompilationInfo info(/*for_boot_image*/ true);
1490 ProfileCompilationInfo::ProfileSampleAnnotation psa1("package1");
1491 ProfileCompilationInfo::ProfileSampleAnnotation psa2("package2");
1492
1493 AddMethod(&info, &d1, 0, Hotness::kFlagHot, psa1);
1494 AddMethod(&info, &d2, 0, Hotness::kFlagHot, psa2);
1495 info.Save(profile.GetFd());
1496 profile.GetFile()->ResetOffset();
1497
1498 // Run profman and pass the dex file with --apk-fd.
1499 android::base::unique_fd apk_fd(
1500 open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY)); // NOLINT
1501 ASSERT_GE(apk_fd.get(), 0);
1502
1503 std::string profman_cmd = GetProfmanCmd();
1504 std::vector<std::string> argv_str;
1505 argv_str.push_back(profman_cmd);
1506 argv_str.push_back("--profile-file-fd=" + std::to_string(profile.GetFd()));
1507 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
1508 argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
1509 argv_str.push_back("--force-merge");
1510 argv_str.push_back("--boot-image-merge");
1511 std::string error;
1512
1513 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfileAssistant::kSuccess) << error;
1514
1515 // Verify that we can load the result and that it equals to what we saved.
1516 ProfileCompilationInfo result;
1517 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
1518 ASSERT_TRUE(result.Load(reference_profile_fd));
1519 ASSERT_TRUE(info.Equals(result));
1520 }
1521
TEST_F(ProfileAssistantTest,DifferentProfileVersions)1522 TEST_F(ProfileAssistantTest, DifferentProfileVersions) {
1523 ScratchFile profile1;
1524 ScratchFile profile2;
1525
1526 ProfileCompilationInfo info1(/*for_boot_image*/ false);
1527 info1.Save(profile1.GetFd());
1528 profile1.GetFile()->ResetOffset();
1529
1530 ProfileCompilationInfo info2(/*for_boot_image*/ true);
1531 info2.Save(profile2.GetFd());
1532 profile2.GetFile()->ResetOffset();
1533
1534 std::vector<int> profile_fds({ GetFd(profile1)});
1535 int reference_profile_fd = GetFd(profile2);
1536 ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd),
1537 ProfileAssistant::kErrorDifferentVersions);
1538
1539 // Reverse the order of the profiles to verify we get the same behaviour.
1540 profile_fds[0] = GetFd(profile2);
1541 reference_profile_fd = GetFd(profile1);
1542 profile1.GetFile()->ResetOffset();
1543 profile2.GetFile()->ResetOffset();
1544 ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd),
1545 ProfileAssistant::kErrorDifferentVersions);
1546 }
1547
1548 // Under default behaviour we will abort if we cannot load a profile during a merge
1549 // operation. However, if we pass --force-merge to force aggregation we should
1550 // ignore files we cannot load
TEST_F(ProfileAssistantTest,ForceMergeIgnoreProfilesItCannotLoad)1551 TEST_F(ProfileAssistantTest, ForceMergeIgnoreProfilesItCannotLoad) {
1552 ScratchFile profile1;
1553 ScratchFile profile2;
1554
1555 // Write corrupt data in the first file.
1556 std::string content = "giberish";
1557 ASSERT_TRUE(profile1.GetFile()->WriteFully(content.c_str(), content.length()));
1558 profile1.GetFile()->ResetOffset();
1559
1560 ProfileCompilationInfo info2(/*for_boot_image*/ true);
1561 info2.Save(profile2.GetFd());
1562 profile2.GetFile()->ResetOffset();
1563
1564 std::vector<int> profile_fds({ GetFd(profile1)});
1565 int reference_profile_fd = GetFd(profile2);
1566
1567 // With force-merge we should merge successfully.
1568 std::vector<const std::string> extra_args({"--force-merge"});
1569 ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, extra_args),
1570 ProfileAssistant::kSuccess);
1571
1572 ProfileCompilationInfo result;
1573 ASSERT_TRUE(profile2.GetFile()->ResetOffset());
1574 ASSERT_TRUE(result.Load(reference_profile_fd));
1575 ASSERT_TRUE(info2.Equals(result));
1576
1577 // Without force-merge we should fail.
1578 ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, extra_args),
1579 ProfileAssistant::kErrorBadProfiles);
1580 }
1581 } // namespace art
1582