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