1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <gtest/gtest.h>
18 #include <stdio.h>
19
20 #include "art_method-inl.h"
21 #include "base/unix_file/fd_file.h"
22 #include "class_linker-inl.h"
23 #include "common_runtime_test.h"
24 #include "dex/dex_file.h"
25 #include "dex/dex_file_loader.h"
26 #include "dex/method_reference.h"
27 #include "dex/type_reference.h"
28 #include "handle_scope-inl.h"
29 #include "jit/profile_compilation_info.h"
30 #include "linear_alloc.h"
31 #include "mirror/class-inl.h"
32 #include "mirror/class_loader.h"
33 #include "scoped_thread_state_change-inl.h"
34 #include "ziparchive/zip_writer.h"
35
36 namespace art {
37
38 using Hotness = ProfileCompilationInfo::MethodHotness;
39
40 static constexpr size_t kMaxMethodIds = 65535;
41
42 class ProfileCompilationInfoTest : public CommonRuntimeTest {
43 public:
PostRuntimeCreate()44 void PostRuntimeCreate() OVERRIDE {
45 allocator_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
46 }
47
48 protected:
GetVirtualMethods(jobject class_loader,const std::string & clazz)49 std::vector<ArtMethod*> GetVirtualMethods(jobject class_loader,
50 const std::string& clazz) {
51 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
52 Thread* self = Thread::Current();
53 ScopedObjectAccess soa(self);
54 StackHandleScope<1> hs(self);
55 Handle<mirror::ClassLoader> h_loader(
56 hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader()));
57 mirror::Class* klass = class_linker->FindClass(self, clazz.c_str(), h_loader);
58
59 const auto pointer_size = class_linker->GetImagePointerSize();
60 std::vector<ArtMethod*> methods;
61 for (auto& m : klass->GetVirtualMethods(pointer_size)) {
62 methods.push_back(&m);
63 }
64 return methods;
65 }
66
AddMethod(const std::string & dex_location,uint32_t checksum,uint16_t method_index,ProfileCompilationInfo * info)67 bool AddMethod(const std::string& dex_location,
68 uint32_t checksum,
69 uint16_t method_index,
70 ProfileCompilationInfo* info) {
71 return info->AddMethodIndex(Hotness::kFlagHot,
72 dex_location,
73 checksum,
74 method_index,
75 kMaxMethodIds);
76 }
77
AddMethod(const std::string & dex_location,uint32_t checksum,uint16_t method_index,const ProfileCompilationInfo::OfflineProfileMethodInfo & pmi,ProfileCompilationInfo * info)78 bool AddMethod(const std::string& dex_location,
79 uint32_t checksum,
80 uint16_t method_index,
81 const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi,
82 ProfileCompilationInfo* info) {
83 return info->AddMethod(
84 dex_location, checksum, method_index, kMaxMethodIds, pmi, Hotness::kFlagPostStartup);
85 }
86
AddClass(const std::string & dex_location,uint32_t checksum,dex::TypeIndex type_index,ProfileCompilationInfo * info)87 bool AddClass(const std::string& dex_location,
88 uint32_t checksum,
89 dex::TypeIndex type_index,
90 ProfileCompilationInfo* info) {
91 DexCacheResolvedClasses classes(dex_location, dex_location, checksum, kMaxMethodIds);
92 classes.AddClass(type_index);
93 return info->AddClasses({classes});
94 }
95
GetFd(const ScratchFile & file)96 uint32_t GetFd(const ScratchFile& file) {
97 return static_cast<uint32_t>(file.GetFd());
98 }
99
SaveProfilingInfo(const std::string & filename,const std::vector<ArtMethod * > & methods,const std::set<DexCacheResolvedClasses> & resolved_classes,Hotness::Flag flags)100 bool SaveProfilingInfo(
101 const std::string& filename,
102 const std::vector<ArtMethod*>& methods,
103 const std::set<DexCacheResolvedClasses>& resolved_classes,
104 Hotness::Flag flags) {
105 ProfileCompilationInfo info;
106 std::vector<ProfileMethodInfo> profile_methods;
107 ScopedObjectAccess soa(Thread::Current());
108 for (ArtMethod* method : methods) {
109 profile_methods.emplace_back(
110 MethodReference(method->GetDexFile(), method->GetDexMethodIndex()));
111 }
112 if (!info.AddMethods(profile_methods, flags) || !info.AddClasses(resolved_classes)) {
113 return false;
114 }
115 if (info.GetNumberOfMethods() != profile_methods.size()) {
116 return false;
117 }
118 ProfileCompilationInfo file_profile;
119 if (!file_profile.Load(filename, false)) {
120 return false;
121 }
122 if (!info.MergeWith(file_profile)) {
123 return false;
124 }
125
126 return info.Save(filename, nullptr);
127 }
128
129 // Saves the given art methods to a profile backed by 'filename' and adds
130 // some fake inline caches to it. The added inline caches are returned in
131 // the out map `profile_methods_map`.
SaveProfilingInfoWithFakeInlineCaches(const std::string & filename,const std::vector<ArtMethod * > & methods,Hotness::Flag flags,SafeMap<ArtMethod *,ProfileMethodInfo> * profile_methods_map)132 bool SaveProfilingInfoWithFakeInlineCaches(
133 const std::string& filename,
134 const std::vector<ArtMethod*>& methods,
135 Hotness::Flag flags,
136 /*out*/ SafeMap<ArtMethod*, ProfileMethodInfo>* profile_methods_map) {
137 ProfileCompilationInfo info;
138 std::vector<ProfileMethodInfo> profile_methods;
139 ScopedObjectAccess soa(Thread::Current());
140 for (ArtMethod* method : methods) {
141 std::vector<ProfileMethodInfo::ProfileInlineCache> caches;
142 // Monomorphic
143 for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
144 std::vector<TypeReference> classes;
145 classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0));
146 caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
147 }
148 // Polymorphic
149 for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
150 std::vector<TypeReference> classes;
151 for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) {
152 classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
153 }
154 caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
155 }
156 // Megamorphic
157 for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
158 std::vector<TypeReference> classes;
159 for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) {
160 classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
161 }
162 caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
163 }
164 // Missing types
165 for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
166 std::vector<TypeReference> classes;
167 caches.emplace_back(dex_pc, /*is_missing_types*/true, classes);
168 }
169 ProfileMethodInfo pmi(MethodReference(method->GetDexFile(),
170 method->GetDexMethodIndex()),
171 caches);
172 profile_methods.push_back(pmi);
173 profile_methods_map->Put(method, pmi);
174 }
175
176 if (!info.AddMethods(profile_methods, flags)
177 || info.GetNumberOfMethods() != profile_methods.size()) {
178 return false;
179 }
180 return info.Save(filename, nullptr);
181 }
182
183 // Creates an inline cache which will be destructed at the end of the test.
CreateInlineCacheMap()184 ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() {
185 used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap(
186 std::less<uint16_t>(), allocator_->Adapter(kArenaAllocProfile)));
187 return used_inline_caches.back().get();
188 }
189
ConvertProfileMethodInfo(const ProfileMethodInfo & pmi)190 ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo(
191 const ProfileMethodInfo& pmi) {
192 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
193 ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi(ic_map);
194 SafeMap<DexFile*, uint8_t> dex_map; // dex files to profile index
195 for (const auto& inline_cache : pmi.inline_caches) {
196 ProfileCompilationInfo::DexPcData& dex_pc_data =
197 ic_map->FindOrAdd(
198 inline_cache.dex_pc, ProfileCompilationInfo::DexPcData(allocator_.get()))->second;
199 if (inline_cache.is_missing_types) {
200 dex_pc_data.SetIsMissingTypes();
201 }
202 for (const auto& class_ref : inline_cache.classes) {
203 uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast<DexFile*>(class_ref.dex_file),
204 static_cast<uint8_t>(dex_map.size()))->second;
205 dex_pc_data.AddClass(dex_profile_index, class_ref.TypeIndex());
206 if (dex_profile_index >= offline_pmi.dex_references.size()) {
207 // This is a new dex.
208 const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey(
209 class_ref.dex_file->GetLocation());
210 offline_pmi.dex_references.emplace_back(dex_key,
211 class_ref.dex_file->GetLocationChecksum(),
212 class_ref.dex_file->NumMethodIds());
213 }
214 }
215 }
216 return offline_pmi;
217 }
218
219 // Creates an offline profile used for testing inline caches.
GetOfflineProfileMethodInfo()220 ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() {
221 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
222
223 // Monomorphic
224 for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
225 ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
226 dex_pc_data.AddClass(0, dex::TypeIndex(0));
227 ic_map->Put(dex_pc, dex_pc_data);
228 }
229 // Polymorphic
230 for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
231 ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
232 dex_pc_data.AddClass(0, dex::TypeIndex(0));
233 dex_pc_data.AddClass(1, dex::TypeIndex(1));
234 dex_pc_data.AddClass(2, dex::TypeIndex(2));
235
236 ic_map->Put(dex_pc, dex_pc_data);
237 }
238 // Megamorphic
239 for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
240 ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
241 dex_pc_data.SetIsMegamorphic();
242 ic_map->Put(dex_pc, dex_pc_data);
243 }
244 // Missing types
245 for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
246 ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
247 dex_pc_data.SetIsMissingTypes();
248 ic_map->Put(dex_pc, dex_pc_data);
249 }
250
251 ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
252
253 pmi.dex_references.emplace_back("dex_location1", /* checksum */1, kMaxMethodIds);
254 pmi.dex_references.emplace_back("dex_location2", /* checksum */2, kMaxMethodIds);
255 pmi.dex_references.emplace_back("dex_location3", /* checksum */3, kMaxMethodIds);
256
257 return pmi;
258 }
259
MakeMegamorphic(ProfileCompilationInfo::OfflineProfileMethodInfo * pmi)260 void MakeMegamorphic(/*out*/ProfileCompilationInfo::OfflineProfileMethodInfo* pmi) {
261 ProfileCompilationInfo::InlineCacheMap* ic_map =
262 const_cast<ProfileCompilationInfo::InlineCacheMap*>(pmi->inline_caches);
263 for (auto it : *ic_map) {
264 for (uint16_t k = 0; k <= 2 * InlineCache::kIndividualCacheSize; k++) {
265 it.second.AddClass(0, dex::TypeIndex(k));
266 }
267 }
268 }
269
SetIsMissingTypes(ProfileCompilationInfo::OfflineProfileMethodInfo * pmi)270 void SetIsMissingTypes(/*out*/ProfileCompilationInfo::OfflineProfileMethodInfo* pmi) {
271 ProfileCompilationInfo::InlineCacheMap* ic_map =
272 const_cast<ProfileCompilationInfo::InlineCacheMap*>(pmi->inline_caches);
273 for (auto it : *ic_map) {
274 it.second.SetIsMissingTypes();
275 }
276 }
277
TestProfileLoadFromZip(const char * zip_entry,size_t zip_flags,bool should_succeed,bool should_succeed_with_empty_profile=false)278 void TestProfileLoadFromZip(const char* zip_entry,
279 size_t zip_flags,
280 bool should_succeed,
281 bool should_succeed_with_empty_profile = false) {
282 // Create a valid profile.
283 ScratchFile profile;
284 ProfileCompilationInfo saved_info;
285 for (uint16_t i = 0; i < 10; i++) {
286 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
287 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
288 }
289 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
290 ASSERT_EQ(0, profile.GetFile()->Flush());
291
292 // Prepare the profile content for zipping.
293 ASSERT_TRUE(profile.GetFile()->ResetOffset());
294 std::vector<uint8_t> data(profile.GetFile()->GetLength());
295 ASSERT_TRUE(profile.GetFile()->ReadFully(data.data(), data.size()));
296
297 // Zip the profile content.
298 ScratchFile zip;
299 FILE* file = fopen(zip.GetFile()->GetPath().c_str(), "wb");
300 ZipWriter writer(file);
301 writer.StartEntry(zip_entry, zip_flags);
302 writer.WriteBytes(data.data(), data.size());
303 writer.FinishEntry();
304 writer.Finish();
305 fflush(file);
306 fclose(file);
307
308 // Verify loading from the zip archive.
309 ProfileCompilationInfo loaded_info;
310 ASSERT_TRUE(zip.GetFile()->ResetOffset());
311 ASSERT_EQ(should_succeed, loaded_info.Load(zip.GetFile()->GetPath(), false));
312 if (should_succeed) {
313 if (should_succeed_with_empty_profile) {
314 ASSERT_TRUE(loaded_info.IsEmpty());
315 } else {
316 ASSERT_TRUE(loaded_info.Equals(saved_info));
317 }
318 }
319 }
320
IsEmpty(const ProfileCompilationInfo & info)321 bool IsEmpty(const ProfileCompilationInfo& info) {
322 return info.IsEmpty();
323 }
324
325 // Cannot sizeof the actual arrays so hard code the values here.
326 // They should not change anyway.
327 static constexpr int kProfileMagicSize = 4;
328 static constexpr int kProfileVersionSize = 4;
329
330 std::unique_ptr<ArenaAllocator> allocator_;
331
332 // Cache of inline caches generated during tests.
333 // This makes it easier to pass data between different utilities and ensure that
334 // caches are destructed at the end of the test.
335 std::vector<std::unique_ptr<ProfileCompilationInfo::InlineCacheMap>> used_inline_caches;
336 };
337
TEST_F(ProfileCompilationInfoTest,SaveArtMethods)338 TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
339 ScratchFile profile;
340
341 Thread* self = Thread::Current();
342 jobject class_loader;
343 {
344 ScopedObjectAccess soa(self);
345 class_loader = LoadDex("ProfileTestMultiDex");
346 }
347 ASSERT_NE(class_loader, nullptr);
348
349 // Save virtual methods from Main.
350 std::set<DexCacheResolvedClasses> resolved_classes;
351 std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
352 ASSERT_TRUE(SaveProfilingInfo(
353 profile.GetFilename(), main_methods, resolved_classes, Hotness::kFlagPostStartup));
354
355 // Check that what we saved is in the profile.
356 ProfileCompilationInfo info1;
357 ASSERT_TRUE(info1.Load(GetFd(profile)));
358 ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size());
359 {
360 ScopedObjectAccess soa(self);
361 for (ArtMethod* m : main_methods) {
362 Hotness h = info1.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
363 ASSERT_TRUE(h.IsHot());
364 ASSERT_TRUE(h.IsPostStartup());
365 }
366 }
367
368 // Save virtual methods from Second.
369 std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;");
370 ASSERT_TRUE(SaveProfilingInfo(
371 profile.GetFilename(), second_methods, resolved_classes, Hotness::kFlagStartup));
372
373 // Check that what we saved is in the profile (methods form Main and Second).
374 ProfileCompilationInfo info2;
375 ASSERT_TRUE(profile.GetFile()->ResetOffset());
376 ASSERT_TRUE(info2.Load(GetFd(profile)));
377 ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size());
378 {
379 ScopedObjectAccess soa(self);
380 for (ArtMethod* m : main_methods) {
381 Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
382 ASSERT_TRUE(h.IsHot());
383 ASSERT_TRUE(h.IsPostStartup());
384 }
385 for (ArtMethod* m : second_methods) {
386 Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
387 ASSERT_TRUE(h.IsHot());
388 ASSERT_TRUE(h.IsStartup());
389 }
390 }
391 }
392
TEST_F(ProfileCompilationInfoTest,SaveFd)393 TEST_F(ProfileCompilationInfoTest, SaveFd) {
394 ScratchFile profile;
395
396 ProfileCompilationInfo saved_info;
397 // Save a few methods.
398 for (uint16_t i = 0; i < 10; i++) {
399 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
400 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
401 }
402 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
403 ASSERT_EQ(0, profile.GetFile()->Flush());
404
405 // Check that we get back what we saved.
406 ProfileCompilationInfo loaded_info;
407 ASSERT_TRUE(profile.GetFile()->ResetOffset());
408 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
409 ASSERT_TRUE(loaded_info.Equals(saved_info));
410
411 // Save more methods.
412 for (uint16_t i = 0; i < 100; i++) {
413 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
414 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
415 ASSERT_TRUE(AddMethod("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info));
416 }
417 ASSERT_TRUE(profile.GetFile()->ResetOffset());
418 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
419 ASSERT_EQ(0, profile.GetFile()->Flush());
420
421 // Check that we get back everything we saved.
422 ProfileCompilationInfo loaded_info2;
423 ASSERT_TRUE(profile.GetFile()->ResetOffset());
424 ASSERT_TRUE(loaded_info2.Load(GetFd(profile)));
425 ASSERT_TRUE(loaded_info2.Equals(saved_info));
426 }
427
TEST_F(ProfileCompilationInfoTest,AddMethodsAndClassesFail)428 TEST_F(ProfileCompilationInfoTest, AddMethodsAndClassesFail) {
429 ScratchFile profile;
430
431 ProfileCompilationInfo info;
432 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info));
433 // Trying to add info for an existing file but with a different checksum.
434 ASSERT_FALSE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info));
435 }
436
TEST_F(ProfileCompilationInfoTest,MergeFail)437 TEST_F(ProfileCompilationInfoTest, MergeFail) {
438 ScratchFile profile;
439
440 ProfileCompilationInfo info1;
441 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1));
442 // Use the same file, change the checksum.
443 ProfileCompilationInfo info2;
444 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2));
445
446 ASSERT_FALSE(info1.MergeWith(info2));
447 }
448
449
TEST_F(ProfileCompilationInfoTest,MergeFdFail)450 TEST_F(ProfileCompilationInfoTest, MergeFdFail) {
451 ScratchFile profile;
452
453 ProfileCompilationInfo info1;
454 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1));
455 // Use the same file, change the checksum.
456 ProfileCompilationInfo info2;
457 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2));
458
459 ASSERT_TRUE(info1.Save(profile.GetFd()));
460 ASSERT_EQ(0, profile.GetFile()->Flush());
461 ASSERT_TRUE(profile.GetFile()->ResetOffset());
462
463 ASSERT_FALSE(info2.Load(profile.GetFd()));
464 }
465
TEST_F(ProfileCompilationInfoTest,SaveMaxMethods)466 TEST_F(ProfileCompilationInfoTest, SaveMaxMethods) {
467 ScratchFile profile;
468
469 ProfileCompilationInfo saved_info;
470 // Save the maximum number of methods
471 for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) {
472 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
473 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
474 }
475 // Save the maximum number of classes
476 for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) {
477 ASSERT_TRUE(AddClass("dex_location1", /* checksum */ 1, dex::TypeIndex(i), &saved_info));
478 ASSERT_TRUE(AddClass("dex_location2", /* checksum */ 2, dex::TypeIndex(i), &saved_info));
479 }
480
481 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
482 ASSERT_EQ(0, profile.GetFile()->Flush());
483
484 // Check that we get back what we saved.
485 ProfileCompilationInfo loaded_info;
486 ASSERT_TRUE(profile.GetFile()->ResetOffset());
487 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
488 ASSERT_TRUE(loaded_info.Equals(saved_info));
489 }
490
TEST_F(ProfileCompilationInfoTest,SaveEmpty)491 TEST_F(ProfileCompilationInfoTest, SaveEmpty) {
492 ScratchFile profile;
493
494 ProfileCompilationInfo saved_info;
495 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
496 ASSERT_EQ(0, profile.GetFile()->Flush());
497
498 // Check that we get back what we saved.
499 ProfileCompilationInfo loaded_info;
500 ASSERT_TRUE(profile.GetFile()->ResetOffset());
501 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
502 ASSERT_TRUE(loaded_info.Equals(saved_info));
503 }
504
TEST_F(ProfileCompilationInfoTest,LoadEmpty)505 TEST_F(ProfileCompilationInfoTest, LoadEmpty) {
506 ScratchFile profile;
507
508 ProfileCompilationInfo empty_info;
509
510 ProfileCompilationInfo loaded_info;
511 ASSERT_TRUE(profile.GetFile()->ResetOffset());
512 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
513 ASSERT_TRUE(loaded_info.Equals(empty_info));
514 }
515
TEST_F(ProfileCompilationInfoTest,BadMagic)516 TEST_F(ProfileCompilationInfoTest, BadMagic) {
517 ScratchFile profile;
518 uint8_t buffer[] = { 1, 2, 3, 4 };
519 ASSERT_TRUE(profile.GetFile()->WriteFully(buffer, sizeof(buffer)));
520 ProfileCompilationInfo loaded_info;
521 ASSERT_TRUE(profile.GetFile()->ResetOffset());
522 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
523 }
524
TEST_F(ProfileCompilationInfoTest,BadVersion)525 TEST_F(ProfileCompilationInfoTest, BadVersion) {
526 ScratchFile profile;
527
528 ASSERT_TRUE(profile.GetFile()->WriteFully(
529 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
530 uint8_t version[] = { 'v', 'e', 'r', 's', 'i', 'o', 'n' };
531 ASSERT_TRUE(profile.GetFile()->WriteFully(version, sizeof(version)));
532 ASSERT_EQ(0, profile.GetFile()->Flush());
533
534 ProfileCompilationInfo loaded_info;
535 ASSERT_TRUE(profile.GetFile()->ResetOffset());
536 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
537 }
538
TEST_F(ProfileCompilationInfoTest,Incomplete)539 TEST_F(ProfileCompilationInfoTest, Incomplete) {
540 ScratchFile profile;
541 ASSERT_TRUE(profile.GetFile()->WriteFully(
542 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
543 ASSERT_TRUE(profile.GetFile()->WriteFully(
544 ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
545 // Write that we have at least one line.
546 uint8_t line_number[] = { 0, 1 };
547 ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
548 ASSERT_EQ(0, profile.GetFile()->Flush());
549
550 ProfileCompilationInfo loaded_info;
551 ASSERT_TRUE(profile.GetFile()->ResetOffset());
552 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
553 }
554
TEST_F(ProfileCompilationInfoTest,TooLongDexLocation)555 TEST_F(ProfileCompilationInfoTest, TooLongDexLocation) {
556 ScratchFile profile;
557 ASSERT_TRUE(profile.GetFile()->WriteFully(
558 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
559 ASSERT_TRUE(profile.GetFile()->WriteFully(
560 ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
561 // Write that we have at least one line.
562 uint8_t line_number[] = { 0, 1 };
563 ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
564
565 // dex_location_size, methods_size, classes_size, checksum.
566 // Dex location size is too big and should be rejected.
567 uint8_t line[] = { 255, 255, 0, 1, 0, 1, 0, 0, 0, 0 };
568 ASSERT_TRUE(profile.GetFile()->WriteFully(line, sizeof(line)));
569 ASSERT_EQ(0, profile.GetFile()->Flush());
570
571 ProfileCompilationInfo loaded_info;
572 ASSERT_TRUE(profile.GetFile()->ResetOffset());
573 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
574 }
575
TEST_F(ProfileCompilationInfoTest,UnexpectedContent)576 TEST_F(ProfileCompilationInfoTest, UnexpectedContent) {
577 ScratchFile profile;
578
579 ProfileCompilationInfo saved_info;
580 // Save the maximum number of methods
581 for (uint16_t i = 0; i < 10; i++) {
582 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
583 }
584 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
585
586 uint8_t random_data[] = { 1, 2, 3};
587 ASSERT_TRUE(profile.GetFile()->WriteFully(random_data, sizeof(random_data)));
588
589 ASSERT_EQ(0, profile.GetFile()->Flush());
590
591 // Check that we fail because of unexpected data at the end of the file.
592 ProfileCompilationInfo loaded_info;
593 ASSERT_TRUE(profile.GetFile()->ResetOffset());
594 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
595 }
596
TEST_F(ProfileCompilationInfoTest,SaveInlineCaches)597 TEST_F(ProfileCompilationInfoTest, SaveInlineCaches) {
598 ScratchFile profile;
599
600 ProfileCompilationInfo saved_info;
601 ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
602
603 // Add methods with inline caches.
604 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
605 // Add a method which is part of the same dex file as one of the
606 // class from the inline caches.
607 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
608 // Add a method which is outside the set of dex files.
609 ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info));
610 }
611
612 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
613 ASSERT_EQ(0, profile.GetFile()->Flush());
614
615 // Check that we get back what we saved.
616 ProfileCompilationInfo loaded_info;
617 ASSERT_TRUE(profile.GetFile()->ResetOffset());
618 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
619
620 ASSERT_TRUE(loaded_info.Equals(saved_info));
621
622 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
623 loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ 3);
624 ASSERT_TRUE(loaded_pmi1 != nullptr);
625 ASSERT_TRUE(*loaded_pmi1 == pmi);
626 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi2 =
627 loaded_info.GetMethod("dex_location4", /* checksum */ 4, /* method_idx */ 3);
628 ASSERT_TRUE(loaded_pmi2 != nullptr);
629 ASSERT_TRUE(*loaded_pmi2 == pmi);
630 }
631
TEST_F(ProfileCompilationInfoTest,MegamorphicInlineCaches)632 TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCaches) {
633 ScratchFile profile;
634
635 ProfileCompilationInfo saved_info;
636 ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
637
638 // Add methods with inline caches.
639 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
640 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
641 }
642
643 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
644 ASSERT_EQ(0, profile.GetFile()->Flush());
645
646 // Make the inline caches megamorphic and add them to the profile again.
647 ProfileCompilationInfo saved_info_extra;
648 ProfileCompilationInfo::OfflineProfileMethodInfo pmi_extra = GetOfflineProfileMethodInfo();
649 MakeMegamorphic(&pmi_extra);
650 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
651 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra));
652 }
653
654 ASSERT_TRUE(profile.GetFile()->ResetOffset());
655 ASSERT_TRUE(saved_info_extra.Save(GetFd(profile)));
656 ASSERT_EQ(0, profile.GetFile()->Flush());
657
658 // Merge the profiles so that we have the same view as the file.
659 ASSERT_TRUE(saved_info.MergeWith(saved_info_extra));
660
661 // Check that we get back what we saved.
662 ProfileCompilationInfo loaded_info;
663 ASSERT_TRUE(profile.GetFile()->ResetOffset());
664 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
665
666 ASSERT_TRUE(loaded_info.Equals(saved_info));
667
668 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
669 loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ 3);
670
671 ASSERT_TRUE(loaded_pmi1 != nullptr);
672 ASSERT_TRUE(*loaded_pmi1 == pmi_extra);
673 }
674
TEST_F(ProfileCompilationInfoTest,MissingTypesInlineCaches)675 TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCaches) {
676 ScratchFile profile;
677
678 ProfileCompilationInfo saved_info;
679 ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
680
681 // Add methods with inline caches.
682 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
683 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
684 }
685
686 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
687 ASSERT_EQ(0, profile.GetFile()->Flush());
688
689 // Make some inline caches megamorphic and add them to the profile again.
690 ProfileCompilationInfo saved_info_extra;
691 ProfileCompilationInfo::OfflineProfileMethodInfo pmi_extra = GetOfflineProfileMethodInfo();
692 MakeMegamorphic(&pmi_extra);
693 for (uint16_t method_idx = 5; method_idx < 10; method_idx++) {
694 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra));
695 }
696
697 // Mark all inline caches with missing types and add them to the profile again.
698 // This will verify that all inline caches (megamorphic or not) should be marked as missing types.
699 ProfileCompilationInfo::OfflineProfileMethodInfo missing_types = GetOfflineProfileMethodInfo();
700 SetIsMissingTypes(&missing_types);
701 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
702 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra));
703 }
704
705 ASSERT_TRUE(profile.GetFile()->ResetOffset());
706 ASSERT_TRUE(saved_info_extra.Save(GetFd(profile)));
707 ASSERT_EQ(0, profile.GetFile()->Flush());
708
709 // Merge the profiles so that we have the same view as the file.
710 ASSERT_TRUE(saved_info.MergeWith(saved_info_extra));
711
712 // Check that we get back what we saved.
713 ProfileCompilationInfo loaded_info;
714 ASSERT_TRUE(profile.GetFile()->ResetOffset());
715 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
716
717 ASSERT_TRUE(loaded_info.Equals(saved_info));
718
719 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
720 loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ 3);
721 ASSERT_TRUE(loaded_pmi1 != nullptr);
722 ASSERT_TRUE(*loaded_pmi1 == pmi_extra);
723 }
724
TEST_F(ProfileCompilationInfoTest,SaveArtMethodsWithInlineCaches)725 TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) {
726 ScratchFile profile;
727
728 Thread* self = Thread::Current();
729 jobject class_loader;
730 {
731 ScopedObjectAccess soa(self);
732 class_loader = LoadDex("ProfileTestMultiDex");
733 }
734 ASSERT_NE(class_loader, nullptr);
735
736 // Save virtual methods from Main.
737 std::set<DexCacheResolvedClasses> resolved_classes;
738 std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
739
740 SafeMap<ArtMethod*, ProfileMethodInfo> profile_methods_map;
741 ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches(
742 profile.GetFilename(), main_methods, Hotness::kFlagStartup, &profile_methods_map));
743
744 // Check that what we saved is in the profile.
745 ProfileCompilationInfo info;
746 ASSERT_TRUE(info.Load(GetFd(profile)));
747 ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size());
748 {
749 ScopedObjectAccess soa(self);
750 for (ArtMethod* m : main_methods) {
751 Hotness h = info.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
752 ASSERT_TRUE(h.IsHot());
753 ASSERT_TRUE(h.IsStartup());
754 const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second;
755 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> offline_pmi =
756 info.GetMethod(m->GetDexFile()->GetLocation(),
757 m->GetDexFile()->GetLocationChecksum(),
758 m->GetDexMethodIndex());
759 ASSERT_TRUE(offline_pmi != nullptr);
760 ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi =
761 ConvertProfileMethodInfo(pmi);
762 ASSERT_EQ(converted_pmi, *offline_pmi);
763 }
764 }
765 }
766
TEST_F(ProfileCompilationInfoTest,InvalidChecksumInInlineCache)767 TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCache) {
768 ScratchFile profile;
769
770 ProfileCompilationInfo info;
771 ProfileCompilationInfo::OfflineProfileMethodInfo pmi1 = GetOfflineProfileMethodInfo();
772 ProfileCompilationInfo::OfflineProfileMethodInfo pmi2 = GetOfflineProfileMethodInfo();
773 // Modify the checksum to trigger a mismatch.
774 pmi2.dex_references[0].dex_checksum++;
775
776 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /*method_idx*/ 0, pmi1, &info));
777 ASSERT_FALSE(AddMethod("dex_location2", /* checksum */ 2, /*method_idx*/ 0, pmi2, &info));
778 }
779
780 // Verify that profiles behave correctly even if the methods are added in a different
781 // order and with a different dex profile indices for the dex files.
TEST_F(ProfileCompilationInfoTest,MergeInlineCacheTriggerReindex)782 TEST_F(ProfileCompilationInfoTest, MergeInlineCacheTriggerReindex) {
783 ScratchFile profile;
784
785 ProfileCompilationInfo info;
786 ProfileCompilationInfo info_reindexed;
787
788 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
789 ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
790 pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1, kMaxMethodIds);
791 pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2, kMaxMethodIds);
792 for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) {
793 ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
794 dex_pc_data.AddClass(0, dex::TypeIndex(0));
795 dex_pc_data.AddClass(1, dex::TypeIndex(1));
796 ic_map->Put(dex_pc, dex_pc_data);
797 }
798
799 ProfileCompilationInfo::InlineCacheMap* ic_map_reindexed = CreateInlineCacheMap();
800 ProfileCompilationInfo::OfflineProfileMethodInfo pmi_reindexed(ic_map_reindexed);
801 pmi_reindexed.dex_references.emplace_back("dex_location2", /* checksum */ 2, kMaxMethodIds);
802 pmi_reindexed.dex_references.emplace_back("dex_location1", /* checksum */ 1, kMaxMethodIds);
803 for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) {
804 ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
805 dex_pc_data.AddClass(1, dex::TypeIndex(0));
806 dex_pc_data.AddClass(0, dex::TypeIndex(1));
807 ic_map_reindexed->Put(dex_pc, dex_pc_data);
808 }
809
810 // Profile 1 and Profile 2 get the same methods but in different order.
811 // This will trigger a different dex numbers.
812 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
813 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &info));
814 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, method_idx, pmi, &info));
815 }
816
817 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
818 ASSERT_TRUE(AddMethod(
819 "dex_location2", /* checksum */ 2, method_idx, pmi_reindexed, &info_reindexed));
820 ASSERT_TRUE(AddMethod(
821 "dex_location1", /* checksum */ 1, method_idx, pmi_reindexed, &info_reindexed));
822 }
823
824 ProfileCompilationInfo info_backup;
825 info_backup.MergeWith(info);
826 ASSERT_TRUE(info.MergeWith(info_reindexed));
827 // Merging should have no effect as we're adding the exact same stuff.
828 ASSERT_TRUE(info.Equals(info_backup));
829 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
830 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
831 info.GetMethod("dex_location1", /* checksum */ 1, method_idx);
832 ASSERT_TRUE(loaded_pmi1 != nullptr);
833 ASSERT_TRUE(*loaded_pmi1 == pmi);
834 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi2 =
835 info.GetMethod("dex_location2", /* checksum */ 2, method_idx);
836 ASSERT_TRUE(loaded_pmi2 != nullptr);
837 ASSERT_TRUE(*loaded_pmi2 == pmi);
838 }
839 }
840
TEST_F(ProfileCompilationInfoTest,AddMoreDexFileThanLimit)841 TEST_F(ProfileCompilationInfoTest, AddMoreDexFileThanLimit) {
842 ProfileCompilationInfo info;
843 // Save a few methods.
844 for (uint16_t i = 0; i < std::numeric_limits<uint8_t>::max(); i++) {
845 std::string dex_location = std::to_string(i);
846 ASSERT_TRUE(AddMethod(dex_location, /* checksum */ 1, /* method_idx */ i, &info));
847 }
848 // We only support at most 255 dex files.
849 ASSERT_FALSE(AddMethod(
850 /*dex_location*/ "256", /* checksum */ 1, /* method_idx */ 0, &info));
851 }
852
TEST_F(ProfileCompilationInfoTest,MegamorphicInlineCachesMerge)853 TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCachesMerge) {
854 // Create a megamorphic inline cache.
855 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
856 ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
857 pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1, kMaxMethodIds);
858 ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
859 dex_pc_data.SetIsMegamorphic();
860 ic_map->Put(/*dex_pc*/ 0, dex_pc_data);
861
862 ProfileCompilationInfo info_megamorphic;
863 ASSERT_TRUE(AddMethod("dex_location1",
864 /*checksum*/ 1,
865 /*method_idx*/ 0,
866 pmi,
867 &info_megamorphic));
868
869 // Create a profile with no inline caches (for the same method).
870 ProfileCompilationInfo info_no_inline_cache;
871 ASSERT_TRUE(AddMethod("dex_location1",
872 /*checksum*/ 1,
873 /*method_idx*/ 0,
874 &info_no_inline_cache));
875
876 // Merge the megamorphic cache into the empty one.
877 ASSERT_TRUE(info_no_inline_cache.MergeWith(info_megamorphic));
878 ScratchFile profile;
879 // Saving profile should work without crashing (b/35644850).
880 ASSERT_TRUE(info_no_inline_cache.Save(GetFd(profile)));
881 }
882
TEST_F(ProfileCompilationInfoTest,MissingTypesInlineCachesMerge)883 TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCachesMerge) {
884 // Create an inline cache with missing types
885 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
886 ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
887 pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1, kMaxMethodIds);
888 ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
889 dex_pc_data.SetIsMissingTypes();
890 ic_map->Put(/*dex_pc*/ 0, dex_pc_data);
891
892 ProfileCompilationInfo info_megamorphic;
893 ASSERT_TRUE(AddMethod("dex_location1",
894 /*checksum*/ 1,
895 /*method_idx*/ 0,
896 pmi,
897 &info_megamorphic));
898
899 // Create a profile with no inline caches (for the same method).
900 ProfileCompilationInfo info_no_inline_cache;
901 ASSERT_TRUE(AddMethod("dex_location1",
902 /*checksum*/ 1,
903 /*method_idx*/ 0,
904 &info_no_inline_cache));
905
906 // Merge the missing type cache into the empty one.
907 // Everything should be saved without errors.
908 ASSERT_TRUE(info_no_inline_cache.MergeWith(info_megamorphic));
909 ScratchFile profile;
910 ASSERT_TRUE(info_no_inline_cache.Save(GetFd(profile)));
911 }
912
TEST_F(ProfileCompilationInfoTest,SampledMethodsTest)913 TEST_F(ProfileCompilationInfoTest, SampledMethodsTest) {
914 ProfileCompilationInfo test_info;
915 static constexpr size_t kNumMethods = 1000;
916 static constexpr size_t kChecksum1 = 1234;
917 static constexpr size_t kChecksum2 = 4321;
918 static const std::string kDex1 = "dex1";
919 static const std::string kDex2 = "dex2";
920 test_info.AddMethodIndex(Hotness::kFlagStartup, kDex1, kChecksum1, 1, kNumMethods);
921 test_info.AddMethodIndex(Hotness::kFlagPostStartup, kDex1, kChecksum1, 5, kNumMethods);
922 test_info.AddMethodIndex(Hotness::kFlagStartup, kDex2, kChecksum2, 2, kNumMethods);
923 test_info.AddMethodIndex(Hotness::kFlagPostStartup, kDex2, kChecksum2, 4, kNumMethods);
924 auto run_test = [](const ProfileCompilationInfo& info) {
925 EXPECT_FALSE(info.GetMethodHotness(kDex1, kChecksum1, 2).IsInProfile());
926 EXPECT_FALSE(info.GetMethodHotness(kDex1, kChecksum1, 4).IsInProfile());
927 EXPECT_TRUE(info.GetMethodHotness(kDex1, kChecksum1, 1).IsStartup());
928 EXPECT_FALSE(info.GetMethodHotness(kDex1, kChecksum1, 3).IsStartup());
929 EXPECT_TRUE(info.GetMethodHotness(kDex1, kChecksum1, 5).IsPostStartup());
930 EXPECT_FALSE(info.GetMethodHotness(kDex1, kChecksum1, 6).IsStartup());
931 EXPECT_TRUE(info.GetMethodHotness(kDex2, kChecksum2, 2).IsStartup());
932 EXPECT_TRUE(info.GetMethodHotness(kDex2, kChecksum2, 4).IsPostStartup());
933 };
934 run_test(test_info);
935
936 // Save the profile.
937 ScratchFile profile;
938 ASSERT_TRUE(test_info.Save(GetFd(profile)));
939 ASSERT_EQ(0, profile.GetFile()->Flush());
940 ASSERT_TRUE(profile.GetFile()->ResetOffset());
941
942 // Load the profile and make sure we can read the data and it matches what we expect.
943 ProfileCompilationInfo loaded_info;
944 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
945 run_test(loaded_info);
946
947 // Test that the bitmap gets merged properly.
948 EXPECT_FALSE(test_info.GetMethodHotness(kDex1, kChecksum1, 11).IsStartup());
949 {
950 ProfileCompilationInfo merge_info;
951 merge_info.AddMethodIndex(Hotness::kFlagStartup, kDex1, kChecksum1, 11, kNumMethods);
952 test_info.MergeWith(merge_info);
953 }
954 EXPECT_TRUE(test_info.GetMethodHotness(kDex1, kChecksum1, 11).IsStartup());
955
956 // Test bulk adding.
957 {
958 std::unique_ptr<const DexFile> dex(OpenTestDexFile("ManyMethods"));
959 ProfileCompilationInfo info;
960 std::vector<uint16_t> hot_methods = {1, 3, 5};
961 std::vector<uint16_t> startup_methods = {1, 2};
962 std::vector<uint16_t> post_methods = {0, 2, 6};
963 ASSERT_GE(dex->NumMethodIds(), 7u);
964 info.AddMethodsForDex(static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagStartup),
965 dex.get(),
966 hot_methods.begin(),
967 hot_methods.end());
968 info.AddMethodsForDex(Hotness::kFlagStartup,
969 dex.get(),
970 startup_methods.begin(),
971 startup_methods.end());
972 info.AddMethodsForDex(Hotness::kFlagPostStartup,
973 dex.get(),
974 post_methods.begin(),
975 post_methods.end());
976 for (uint16_t id : hot_methods) {
977 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsHot());
978 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsStartup());
979 }
980 for (uint16_t id : startup_methods) {
981 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsStartup());
982 }
983 for (uint16_t id : post_methods) {
984 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsPostStartup());
985 }
986 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), 6)).IsPostStartup());
987 // Check that methods that shouldn't have been touched are OK.
988 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), 0)).IsInProfile());
989 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex.get(), 4)).IsInProfile());
990 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex.get(), 7)).IsInProfile());
991 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex.get(), 1)).IsPostStartup());
992 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex.get(), 4)).IsStartup());
993 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex.get(), 6)).IsStartup());
994 }
995 }
996
TEST_F(ProfileCompilationInfoTest,LoadFromZipCompress)997 TEST_F(ProfileCompilationInfoTest, LoadFromZipCompress) {
998 TestProfileLoadFromZip("primary.prof",
999 ZipWriter::kCompress | ZipWriter::kAlign32,
1000 /*should_succeed*/true);
1001 }
1002
TEST_F(ProfileCompilationInfoTest,LoadFromZipUnCompress)1003 TEST_F(ProfileCompilationInfoTest, LoadFromZipUnCompress) {
1004 TestProfileLoadFromZip("primary.prof",
1005 ZipWriter::kAlign32,
1006 /*should_succeed*/true);
1007 }
1008
TEST_F(ProfileCompilationInfoTest,LoadFromZipUnAligned)1009 TEST_F(ProfileCompilationInfoTest, LoadFromZipUnAligned) {
1010 TestProfileLoadFromZip("primary.prof",
1011 0,
1012 /*should_succeed*/true);
1013 }
1014
TEST_F(ProfileCompilationInfoTest,LoadFromZipFailBadZipEntry)1015 TEST_F(ProfileCompilationInfoTest, LoadFromZipFailBadZipEntry) {
1016 TestProfileLoadFromZip("invalid.profile.entry",
1017 0,
1018 /*should_succeed*/true,
1019 /*should_succeed_with_empty_profile*/true);
1020 }
1021
TEST_F(ProfileCompilationInfoTest,LoadFromZipFailBadProfile)1022 TEST_F(ProfileCompilationInfoTest, LoadFromZipFailBadProfile) {
1023 // Create a bad profile.
1024 ScratchFile profile;
1025 ASSERT_TRUE(profile.GetFile()->WriteFully(
1026 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
1027 ASSERT_TRUE(profile.GetFile()->WriteFully(
1028 ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
1029 // Write that we have at least one line.
1030 uint8_t line_number[] = { 0, 1 };
1031 ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
1032 ASSERT_EQ(0, profile.GetFile()->Flush());
1033
1034 // Prepare the profile content for zipping.
1035 ASSERT_TRUE(profile.GetFile()->ResetOffset());
1036 std::vector<uint8_t> data(profile.GetFile()->GetLength());
1037 ASSERT_TRUE(profile.GetFile()->ReadFully(data.data(), data.size()));
1038
1039 // Zip the profile content.
1040 ScratchFile zip;
1041 FILE* file = fopen(zip.GetFile()->GetPath().c_str(), "wb");
1042 ZipWriter writer(file);
1043 writer.StartEntry("primary.prof", ZipWriter::kAlign32);
1044 writer.WriteBytes(data.data(), data.size());
1045 writer.FinishEntry();
1046 writer.Finish();
1047 fflush(file);
1048 fclose(file);
1049
1050 // Check that we failed to load.
1051 ProfileCompilationInfo loaded_info;
1052 ASSERT_TRUE(zip.GetFile()->ResetOffset());
1053 ASSERT_FALSE(loaded_info.Load(GetFd(zip)));
1054 }
1055
TEST_F(ProfileCompilationInfoTest,UpdateProfileKeyOk)1056 TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOk) {
1057 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("MultiDex");
1058
1059 ProfileCompilationInfo info;
1060 for (const std::unique_ptr<const DexFile>& dex : dex_files) {
1061 // Create the profile with a different location so that we can update it to the
1062 // real dex location later.
1063 std::string base_location = DexFileLoader::GetBaseLocation(dex->GetLocation());
1064 std::string multidex_suffix = DexFileLoader::GetMultiDexSuffix(dex->GetLocation());
1065 std::string old_name = base_location + "-old" + multidex_suffix;
1066 info.AddMethodIndex(Hotness::kFlagHot,
1067 old_name,
1068 dex->GetLocationChecksum(),
1069 /* method_idx */ 0,
1070 dex->NumMethodIds());
1071 }
1072
1073 // Update the profile keys based on the original dex files
1074 ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
1075
1076 // Verify that we find the methods when searched with the original dex files.
1077 for (const std::unique_ptr<const DexFile>& dex : dex_files) {
1078 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi =
1079 info.GetMethod(dex->GetLocation(), dex->GetLocationChecksum(), /* method_idx */ 0);
1080 ASSERT_TRUE(loaded_pmi != nullptr);
1081 }
1082 }
1083
TEST_F(ProfileCompilationInfoTest,UpdateProfileKeyOkButNoUpdate)1084 TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOkButNoUpdate) {
1085 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("MultiDex");
1086
1087 ProfileCompilationInfo info;
1088 info.AddMethodIndex(Hotness::kFlagHot,
1089 "my.app",
1090 /* checksum */ 123,
1091 /* method_idx */ 0,
1092 /* num_method_ids */ 10);
1093
1094 // Update the profile keys based on the original dex files
1095 ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
1096
1097 // Verify that we did not perform any update and that we cannot find anything with the new
1098 // location.
1099 for (const std::unique_ptr<const DexFile>& dex : dex_files) {
1100 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi =
1101 info.GetMethod(dex->GetLocation(), dex->GetLocationChecksum(), /* method_idx */ 0);
1102 ASSERT_TRUE(loaded_pmi == nullptr);
1103 }
1104
1105 // Verify that we can find the original entry.
1106 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi =
1107 info.GetMethod("my.app", /* checksum */ 123, /* method_idx */ 0);
1108 ASSERT_TRUE(loaded_pmi != nullptr);
1109 }
1110
TEST_F(ProfileCompilationInfoTest,UpdateProfileKeyFail)1111 TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyFail) {
1112 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("MultiDex");
1113
1114
1115 ProfileCompilationInfo info;
1116 // Add all dex
1117 for (const std::unique_ptr<const DexFile>& dex : dex_files) {
1118 // Create the profile with a different location so that we can update it to the
1119 // real dex location later.
1120 std::string base_location = DexFileLoader::GetBaseLocation(dex->GetLocation());
1121 std::string multidex_suffix = DexFileLoader::GetMultiDexSuffix(dex->GetLocation());
1122 std::string old_name = base_location + "-old" + multidex_suffix;
1123 info.AddMethodIndex(Hotness::kFlagHot,
1124 old_name,
1125 dex->GetLocationChecksum(),
1126 /* method_idx */ 0,
1127 dex->NumMethodIds());
1128 }
1129
1130 // Add a method index using the location we want to rename to.
1131 // This will cause the rename to fail because an existing entry would already have that name.
1132 info.AddMethodIndex(Hotness::kFlagHot,
1133 dex_files[0]->GetLocation(),
1134 /* checksum */ 123,
1135 /* method_idx */ 0,
1136 dex_files[0]->NumMethodIds());
1137
1138 ASSERT_FALSE(info.UpdateProfileKeys(dex_files));
1139 }
1140
TEST_F(ProfileCompilationInfoTest,FilteredLoading)1141 TEST_F(ProfileCompilationInfoTest, FilteredLoading) {
1142 ScratchFile profile;
1143
1144 ProfileCompilationInfo saved_info;
1145 ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
1146
1147 // Add methods with inline caches.
1148 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1149 // Add a method which is part of the same dex file as one of the class from the inline caches.
1150 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
1151 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, method_idx, pmi, &saved_info));
1152 // Add a method which is outside the set of dex files.
1153 ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info));
1154 }
1155
1156 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
1157 ASSERT_EQ(0, profile.GetFile()->Flush());
1158
1159 // Check that we get back what we saved.
1160 ProfileCompilationInfo loaded_info;
1161 ASSERT_TRUE(profile.GetFile()->ResetOffset());
1162
1163 // Filter out dex locations. Keep only dex_location1 and dex_location3.
1164 ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
1165 [](const std::string& dex_location, uint32_t checksum) -> bool {
1166 return (dex_location == "dex_location1" && checksum == 1)
1167 || (dex_location == "dex_location3" && checksum == 3);
1168 };
1169 ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
1170
1171 // Verify that we filtered out locations during load.
1172
1173 // Dex location 2 and 4 should have been filtered out
1174 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1175 ASSERT_TRUE(nullptr == loaded_info.GetMethod("dex_location2", /* checksum */ 2, method_idx));
1176 ASSERT_TRUE(nullptr == loaded_info.GetMethod("dex_location4", /* checksum */ 4, method_idx));
1177 }
1178
1179 // Dex location 1 should have all all the inline caches referencing dex location 2 set to
1180 // missing types.
1181 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1182 // The methods for dex location 1 should be in the profile data.
1183 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
1184 loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ method_idx);
1185 ASSERT_TRUE(loaded_pmi1 != nullptr);
1186
1187 // Verify the inline cache.
1188 // Everything should be as constructed by GetOfflineProfileMethodInfo with the exception
1189 // of the inline caches referring types from dex_location2.
1190 // These should be set to IsMissingType.
1191 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
1192
1193 // Monomorphic types should remain the same as dex_location1 was kept.
1194 for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
1195 ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
1196 dex_pc_data.AddClass(0, dex::TypeIndex(0));
1197 ic_map->Put(dex_pc, dex_pc_data);
1198 }
1199 // Polymorphic inline cache should have been transformed to IsMissingType due to
1200 // the removal of dex_location2.
1201 for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
1202 ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
1203 dex_pc_data.SetIsMissingTypes();
1204 ic_map->Put(dex_pc, dex_pc_data);
1205 }
1206
1207 // Megamorphic are not affected by removal of dex files.
1208 for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
1209 ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
1210 dex_pc_data.SetIsMegamorphic();
1211 ic_map->Put(dex_pc, dex_pc_data);
1212 }
1213 // Missing types are not affected be removal of dex files.
1214 for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
1215 ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
1216 dex_pc_data.SetIsMissingTypes();
1217 ic_map->Put(dex_pc, dex_pc_data);
1218 }
1219
1220 ProfileCompilationInfo::OfflineProfileMethodInfo expected_pmi(ic_map);
1221
1222 // The dex references should not have dex_location2 in the list.
1223 expected_pmi.dex_references.emplace_back("dex_location1", /* checksum */1, kMaxMethodIds);
1224 expected_pmi.dex_references.emplace_back("dex_location3", /* checksum */3, kMaxMethodIds);
1225
1226 // Now check that we get back what we expect.
1227 ASSERT_TRUE(*loaded_pmi1 == expected_pmi);
1228 }
1229 }
1230
TEST_F(ProfileCompilationInfoTest,FilteredLoadingRemoveAll)1231 TEST_F(ProfileCompilationInfoTest, FilteredLoadingRemoveAll) {
1232 ScratchFile profile;
1233
1234 ProfileCompilationInfo saved_info;
1235 ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
1236
1237 // Add methods with inline caches.
1238 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1239 // Add a method which is part of the same dex file as one of the class from the inline caches.
1240 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
1241 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, method_idx, pmi, &saved_info));
1242 // Add a method which is outside the set of dex files.
1243 ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info));
1244 }
1245
1246 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
1247 ASSERT_EQ(0, profile.GetFile()->Flush());
1248
1249 // Check that we get back what we saved.
1250 ProfileCompilationInfo loaded_info;
1251 ASSERT_TRUE(profile.GetFile()->ResetOffset());
1252
1253 // Remove all elements.
1254 ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
1255 [](const std::string&, uint32_t) -> bool { return false; };
1256 ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
1257
1258 // Verify that we filtered out everything.
1259 ASSERT_TRUE(IsEmpty(loaded_info));
1260 }
1261
TEST_F(ProfileCompilationInfoTest,FilteredLoadingKeepAll)1262 TEST_F(ProfileCompilationInfoTest, FilteredLoadingKeepAll) {
1263 ScratchFile profile;
1264
1265 ProfileCompilationInfo saved_info;
1266 ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
1267
1268 // Add methods with inline caches.
1269 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1270 // Add a method which is part of the same dex file as one of the
1271 // class from the inline caches.
1272 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
1273 // Add a method which is outside the set of dex files.
1274 ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info));
1275 }
1276
1277 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
1278 ASSERT_EQ(0, profile.GetFile()->Flush());
1279
1280 // Check that we get back what we saved.
1281 ProfileCompilationInfo loaded_info;
1282 ASSERT_TRUE(profile.GetFile()->ResetOffset());
1283
1284 // Keep all elements.
1285 ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
1286 [](const std::string&, uint32_t) -> bool { return true; };
1287 ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
1288
1289
1290 ASSERT_TRUE(loaded_info.Equals(saved_info));
1291
1292 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1293 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
1294 loaded_info.GetMethod("dex_location1", /* checksum */ 1, method_idx);
1295 ASSERT_TRUE(loaded_pmi1 != nullptr);
1296 ASSERT_TRUE(*loaded_pmi1 == pmi);
1297 }
1298 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1299 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi2 =
1300 loaded_info.GetMethod("dex_location4", /* checksum */ 4, method_idx);
1301 ASSERT_TRUE(loaded_pmi2 != nullptr);
1302 ASSERT_TRUE(*loaded_pmi2 == pmi);
1303 }
1304 }
1305
1306 // Regression test: we were failing to do a filtering loading when the filtered dex file
1307 // contained profiled classes.
TEST_F(ProfileCompilationInfoTest,FilteredLoadingWithClasses)1308 TEST_F(ProfileCompilationInfoTest, FilteredLoadingWithClasses) {
1309 ScratchFile profile;
1310
1311 // Save a profile with 2 dex files containing just classes.
1312 ProfileCompilationInfo saved_info;
1313 uint16_t item_count = 1000;
1314 for (uint16_t i = 0; i < item_count; i++) {
1315 ASSERT_TRUE(AddClass("dex_location1", /* checksum */ 1, dex::TypeIndex(i), &saved_info));
1316 ASSERT_TRUE(AddClass("dex_location2", /* checksum */ 2, dex::TypeIndex(i), &saved_info));
1317 }
1318
1319 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
1320 ASSERT_EQ(0, profile.GetFile()->Flush());
1321
1322
1323 // Filter out dex locations: kepp only dex_location2.
1324 ProfileCompilationInfo loaded_info;
1325 ASSERT_TRUE(profile.GetFile()->ResetOffset());
1326 ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
1327 [](const std::string& dex_location, uint32_t checksum) -> bool {
1328 return (dex_location == "dex_location2" && checksum == 2);
1329 };
1330 ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
1331
1332 // Compute the expectation.
1333 ProfileCompilationInfo expected_info;
1334 for (uint16_t i = 0; i < item_count; i++) {
1335 ASSERT_TRUE(AddClass("dex_location2", /* checksum */ 2, dex::TypeIndex(i), &expected_info));
1336 }
1337
1338 // Validate the expectation.
1339 ASSERT_TRUE(loaded_info.Equals(expected_info));
1340 }
1341
1342
TEST_F(ProfileCompilationInfoTest,ClearData)1343 TEST_F(ProfileCompilationInfoTest, ClearData) {
1344 ProfileCompilationInfo info;
1345 for (uint16_t i = 0; i < 10; i++) {
1346 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &info));
1347 }
1348 ASSERT_FALSE(IsEmpty(info));
1349 info.ClearData();
1350 ASSERT_TRUE(IsEmpty(info));
1351 }
1352
TEST_F(ProfileCompilationInfoTest,ClearDataAndSave)1353 TEST_F(ProfileCompilationInfoTest, ClearDataAndSave) {
1354 ProfileCompilationInfo info;
1355 for (uint16_t i = 0; i < 10; i++) {
1356 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &info));
1357 }
1358 info.ClearData();
1359
1360 ScratchFile profile;
1361 ASSERT_TRUE(info.Save(GetFd(profile)));
1362 ASSERT_EQ(0, profile.GetFile()->Flush());
1363
1364 // Check that we get back what we saved.
1365 ProfileCompilationInfo loaded_info;
1366 ASSERT_TRUE(profile.GetFile()->ResetOffset());
1367 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
1368 ASSERT_TRUE(loaded_info.Equals(info));
1369 }
1370
1371 } // namespace art
1372