1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "profile_compilation_info.h"
18 
19 #include "errno.h"
20 #include <limits.h>
21 #include <vector>
22 #include <stdlib.h>
23 #include <sys/file.h>
24 #include <sys/stat.h>
25 #include <sys/uio.h>
26 
27 #include "base/arena_allocator.h"
28 #include "base/dumpable.h"
29 #include "base/mutex.h"
30 #include "base/scoped_flock.h"
31 #include "base/stl_util.h"
32 #include "base/systrace.h"
33 #include "base/unix_file/fd_file.h"
34 #include "jit/profiling_info.h"
35 #include "os.h"
36 #include "safe_map.h"
37 #include "utils.h"
38 
39 namespace art {
40 
41 const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' };
42 // Last profile version: fix profman merges. Update profile version to force
43 // regeneration of possibly faulty profiles.
44 const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '5', '\0' };
45 
46 static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
47 
48 // Debug flag to ignore checksums when testing if a method or a class is present in the profile.
49 // Used to facilitate testing profile guided compilation across a large number of apps
50 // using the same test profile.
51 static constexpr bool kDebugIgnoreChecksum = false;
52 
53 static constexpr uint8_t kIsMissingTypesEncoding = 6;
54 static constexpr uint8_t kIsMegamorphicEncoding = 7;
55 
56 static_assert(sizeof(InlineCache::kIndividualCacheSize) == sizeof(uint8_t),
57               "InlineCache::kIndividualCacheSize does not have the expect type size");
58 static_assert(InlineCache::kIndividualCacheSize < kIsMegamorphicEncoding,
59               "InlineCache::kIndividualCacheSize is larger than expected");
60 static_assert(InlineCache::kIndividualCacheSize < kIsMissingTypesEncoding,
61               "InlineCache::kIndividualCacheSize is larger than expected");
62 
ProfileCompilationInfo(ArenaPool * custom_arena_pool)63 ProfileCompilationInfo::ProfileCompilationInfo(ArenaPool* custom_arena_pool)
64     : default_arena_pool_(),
65       arena_(custom_arena_pool),
66       info_(arena_.Adapter(kArenaAllocProfile)),
67       profile_key_map_(std::less<const std::string>(), arena_.Adapter(kArenaAllocProfile)) {
68 }
69 
ProfileCompilationInfo()70 ProfileCompilationInfo::ProfileCompilationInfo()
71     : default_arena_pool_(/*use_malloc*/true, /*low_4gb*/false, "ProfileCompilationInfo"),
72       arena_(&default_arena_pool_),
73       info_(arena_.Adapter(kArenaAllocProfile)),
74       profile_key_map_(std::less<const std::string>(), arena_.Adapter(kArenaAllocProfile)) {
75 }
76 
~ProfileCompilationInfo()77 ProfileCompilationInfo::~ProfileCompilationInfo() {
78   VLOG(profiler) << Dumpable<MemStats>(arena_.GetMemStats());
79   for (DexFileData* data : info_) {
80     delete data;
81   }
82 }
83 
AddClass(uint16_t dex_profile_idx,const dex::TypeIndex & type_idx)84 void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx,
85                                                  const dex::TypeIndex& type_idx) {
86   if (is_megamorphic || is_missing_types) {
87     return;
88   }
89 
90   // Perform an explicit lookup for the type instead of directly emplacing the
91   // element. We do this because emplace() allocates the node before doing the
92   // lookup and if it then finds an identical element, it shall deallocate the
93   // node. For Arena allocations, that's essentially a leak.
94   ClassReference ref(dex_profile_idx, type_idx);
95   auto it = classes.find(ref);
96   if (it != classes.end()) {
97     // The type index exists.
98     return;
99   }
100 
101   // Check if the adding the type will cause the cache to become megamorphic.
102   if (classes.size() + 1 >= InlineCache::kIndividualCacheSize) {
103     is_megamorphic = true;
104     classes.clear();
105     return;
106   }
107 
108   // The type does not exist and the inline cache will not be megamorphic.
109   classes.insert(ref);
110 }
111 
112 // Transform the actual dex location into relative paths.
113 // Note: this is OK because we don't store profiles of different apps into the same file.
114 // Apps with split apks don't cause trouble because each split has a different name and will not
115 // collide with other entries.
GetProfileDexFileKey(const std::string & dex_location)116 std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_location) {
117   DCHECK(!dex_location.empty());
118   size_t last_sep_index = dex_location.find_last_of('/');
119   if (last_sep_index == std::string::npos) {
120     return dex_location;
121   } else {
122     DCHECK(last_sep_index < dex_location.size());
123     return dex_location.substr(last_sep_index + 1);
124   }
125 }
126 
AddMethodsAndClasses(const std::vector<ProfileMethodInfo> & methods,const std::set<DexCacheResolvedClasses> & resolved_classes)127 bool ProfileCompilationInfo::AddMethodsAndClasses(
128     const std::vector<ProfileMethodInfo>& methods,
129     const std::set<DexCacheResolvedClasses>& resolved_classes) {
130   for (const ProfileMethodInfo& method : methods) {
131     if (!AddMethod(method)) {
132       return false;
133     }
134   }
135   for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
136     if (!AddResolvedClasses(dex_cache)) {
137       return false;
138     }
139   }
140   return true;
141 }
142 
Load(const std::string & filename,bool clear_if_invalid)143 bool ProfileCompilationInfo::Load(const std::string& filename, bool clear_if_invalid) {
144   ScopedTrace trace(__PRETTY_FUNCTION__);
145   ScopedFlock flock;
146   std::string error;
147   int flags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
148   // There's no need to fsync profile data right away. We get many chances
149   // to write it again in case something goes wrong. We can rely on a simple
150   // close(), no sync, and let to the kernel decide when to write to disk.
151   if (!flock.Init(filename.c_str(), flags, /*block*/false, /*flush_on_close*/false, &error)) {
152     LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
153     return false;
154   }
155 
156   int fd = flock.GetFile()->Fd();
157 
158   ProfileLoadSatus status = LoadInternal(fd, &error);
159   if (status == kProfileLoadSuccess) {
160     return true;
161   }
162 
163   if (clear_if_invalid &&
164       ((status == kProfileLoadVersionMismatch) || (status == kProfileLoadBadData))) {
165     LOG(WARNING) << "Clearing bad or obsolete profile data from file "
166                  << filename << ": " << error;
167     if (flock.GetFile()->ClearContent()) {
168       return true;
169     } else {
170       PLOG(WARNING) << "Could not clear profile file: " << filename;
171       return false;
172     }
173   }
174 
175   LOG(WARNING) << "Could not load profile data from file " << filename << ": " << error;
176   return false;
177 }
178 
Save(const std::string & filename,uint64_t * bytes_written)179 bool ProfileCompilationInfo::Save(const std::string& filename, uint64_t* bytes_written) {
180   ScopedTrace trace(__PRETTY_FUNCTION__);
181   ScopedFlock flock;
182   std::string error;
183   int flags = O_WRONLY | O_NOFOLLOW | O_CLOEXEC;
184   // There's no need to fsync profile data right away. We get many chances
185   // to write it again in case something goes wrong. We can rely on a simple
186   // close(), no sync, and let to the kernel decide when to write to disk.
187   if (!flock.Init(filename.c_str(), flags, /*block*/false, /*flush_on_close*/false, &error)) {
188     LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
189     return false;
190   }
191 
192   int fd = flock.GetFile()->Fd();
193 
194   // We need to clear the data because we don't support appending to the profiles yet.
195   if (!flock.GetFile()->ClearContent()) {
196     PLOG(WARNING) << "Could not clear profile file: " << filename;
197     return false;
198   }
199 
200   // This doesn't need locking because we are trying to lock the file for exclusive
201   // access and fail immediately if we can't.
202   bool result = Save(fd);
203   if (result) {
204     int64_t size = GetFileSizeBytes(filename);
205     if (size != -1) {
206       VLOG(profiler)
207         << "Successfully saved profile info to " << filename << " Size: "
208         << size;
209       if (bytes_written != nullptr) {
210         *bytes_written = static_cast<uint64_t>(size);
211       }
212     }
213   } else {
214     VLOG(profiler) << "Failed to save profile info to " << filename;
215   }
216   return result;
217 }
218 
219 // Returns true if all the bytes were successfully written to the file descriptor.
WriteBuffer(int fd,const uint8_t * buffer,size_t byte_count)220 static bool WriteBuffer(int fd, const uint8_t* buffer, size_t byte_count) {
221   while (byte_count > 0) {
222     int bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer, byte_count));
223     if (bytes_written == -1) {
224       return false;
225     }
226     byte_count -= bytes_written;  // Reduce the number of remaining bytes.
227     buffer += bytes_written;  // Move the buffer forward.
228   }
229   return true;
230 }
231 
232 // Add the string bytes to the buffer.
AddStringToBuffer(std::vector<uint8_t> * buffer,const std::string & value)233 static void AddStringToBuffer(std::vector<uint8_t>* buffer, const std::string& value) {
234   buffer->insert(buffer->end(), value.begin(), value.end());
235 }
236 
237 // Insert each byte, from low to high into the buffer.
238 template <typename T>
AddUintToBuffer(std::vector<uint8_t> * buffer,T value)239 static void AddUintToBuffer(std::vector<uint8_t>* buffer, T value) {
240   for (size_t i = 0; i < sizeof(T); i++) {
241     buffer->push_back((value >> (i * kBitsPerByte)) & 0xff);
242   }
243 }
244 
245 static constexpr size_t kLineHeaderSize =
246     2 * sizeof(uint16_t) +  // class_set.size + dex_location.size
247     2 * sizeof(uint32_t);   // method_map.size + checksum
248 
249 /**
250  * Serialization format:
251  *    magic,version,number_of_dex_files
252  *    dex_location1,number_of_classes1,methods_region_size,dex_location_checksum1, \
253  *        method_encoding_11,method_encoding_12...,class_id1,class_id2...
254  *    dex_location2,number_of_classes2,methods_region_size,dex_location_checksum2, \
255  *        method_encoding_21,method_encoding_22...,,class_id1,class_id2...
256  *    .....
257  * The method_encoding is:
258  *    method_id,number_of_inline_caches,inline_cache1,inline_cache2...
259  * The inline_cache is:
260  *    dex_pc,[M|dex_map_size], dex_profile_index,class_id1,class_id2...,dex_profile_index2,...
261  *    dex_map_size is the number of dex_indeces that follows.
262  *       Classes are grouped per their dex files and the line
263  *       `dex_profile_index,class_id1,class_id2...,dex_profile_index2,...` encodes the
264  *       mapping from `dex_profile_index` to the set of classes `class_id1,class_id2...`
265  *    M stands for megamorphic or missing types and it's encoded as either
266  *    the byte kIsMegamorphicEncoding or kIsMissingTypesEncoding.
267  *    When present, there will be no class ids following.
268  **/
Save(int fd)269 bool ProfileCompilationInfo::Save(int fd) {
270   ScopedTrace trace(__PRETTY_FUNCTION__);
271   DCHECK_GE(fd, 0);
272 
273   // Cache at most 50KB before writing.
274   static constexpr size_t kMaxSizeToKeepBeforeWriting = 50 * KB;
275   // Use a vector wrapper to avoid keeping track of offsets when we add elements.
276   std::vector<uint8_t> buffer;
277   WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic));
278   WriteBuffer(fd, kProfileVersion, sizeof(kProfileVersion));
279   DCHECK_LE(info_.size(), std::numeric_limits<uint8_t>::max());
280   AddUintToBuffer(&buffer, static_cast<uint8_t>(info_.size()));
281 
282   // Dex files must be written in the order of their profile index. This
283   // avoids writing the index in the output file and simplifies the parsing logic.
284   for (const DexFileData* dex_data_ptr : info_) {
285     const DexFileData& dex_data = *dex_data_ptr;
286     if (buffer.size() > kMaxSizeToKeepBeforeWriting) {
287       if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
288         return false;
289       }
290       buffer.clear();
291     }
292 
293     // Note that we allow dex files without any methods or classes, so that
294     // inline caches can refer valid dex files.
295 
296     if (dex_data.profile_key.size() >= kMaxDexFileKeyLength) {
297       LOG(WARNING) << "DexFileKey exceeds allocated limit";
298       return false;
299     }
300 
301     // Make sure that the buffer has enough capacity to avoid repeated resizings
302     // while we add data.
303     uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
304     size_t required_capacity = buffer.size() +
305         kLineHeaderSize +
306         dex_data.profile_key.size() +
307         sizeof(uint16_t) * dex_data.class_set.size() +
308         methods_region_size;
309 
310     buffer.reserve(required_capacity);
311     DCHECK_LE(dex_data.profile_key.size(), std::numeric_limits<uint16_t>::max());
312     DCHECK_LE(dex_data.class_set.size(), std::numeric_limits<uint16_t>::max());
313     AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.profile_key.size()));
314     AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.class_set.size()));
315     AddUintToBuffer(&buffer, methods_region_size);  // uint32_t
316     AddUintToBuffer(&buffer, dex_data.checksum);  // uint32_t
317 
318     AddStringToBuffer(&buffer, dex_data.profile_key);
319 
320     for (const auto& method_it : dex_data.method_map) {
321       AddUintToBuffer(&buffer, method_it.first);
322       AddInlineCacheToBuffer(&buffer, method_it.second);
323     }
324     for (const auto& class_id : dex_data.class_set) {
325       AddUintToBuffer(&buffer, class_id.index_);
326     }
327 
328     DCHECK_LE(required_capacity, buffer.size())
329         << "Failed to add the expected number of bytes in the buffer";
330   }
331 
332   return WriteBuffer(fd, buffer.data(), buffer.size());
333 }
334 
AddInlineCacheToBuffer(std::vector<uint8_t> * buffer,const InlineCacheMap & inline_cache_map)335 void ProfileCompilationInfo::AddInlineCacheToBuffer(std::vector<uint8_t>* buffer,
336                                                     const InlineCacheMap& inline_cache_map) {
337   // Add inline cache map size.
338   AddUintToBuffer(buffer, static_cast<uint16_t>(inline_cache_map.size()));
339   if (inline_cache_map.size() == 0) {
340     return;
341   }
342   for (const auto& inline_cache_it : inline_cache_map) {
343     uint16_t dex_pc = inline_cache_it.first;
344     const DexPcData dex_pc_data = inline_cache_it.second;
345     const ClassSet& classes = dex_pc_data.classes;
346 
347     // Add the dex pc.
348     AddUintToBuffer(buffer, dex_pc);
349 
350     // Add the megamorphic/missing_types encoding if needed and continue.
351     // In either cases we don't add any classes to the profiles and so there's
352     // no point to continue.
353     // TODO(calin): in case we miss types there is still value to add the
354     // rest of the classes. They can be added without bumping the profile version.
355     if (dex_pc_data.is_missing_types) {
356       DCHECK(!dex_pc_data.is_megamorphic);  // at this point the megamorphic flag should not be set.
357       DCHECK_EQ(classes.size(), 0u);
358       AddUintToBuffer(buffer, kIsMissingTypesEncoding);
359       continue;
360     } else if (dex_pc_data.is_megamorphic) {
361       DCHECK_EQ(classes.size(), 0u);
362       AddUintToBuffer(buffer, kIsMegamorphicEncoding);
363       continue;
364     }
365 
366     DCHECK_LT(classes.size(), InlineCache::kIndividualCacheSize);
367     DCHECK_NE(classes.size(), 0u) << "InlineCache contains a dex_pc with 0 classes";
368 
369     SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
370     // Group the classes by dex. We expect that most of the classes will come from
371     // the same dex, so this will be more efficient than encoding the dex index
372     // for each class reference.
373     GroupClassesByDex(classes, &dex_to_classes_map);
374     // Add the dex map size.
375     AddUintToBuffer(buffer, static_cast<uint8_t>(dex_to_classes_map.size()));
376     for (const auto& dex_it : dex_to_classes_map) {
377       uint8_t dex_profile_index = dex_it.first;
378       const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
379       // Add the dex profile index.
380       AddUintToBuffer(buffer, dex_profile_index);
381       // Add the the number of classes for each dex profile index.
382       AddUintToBuffer(buffer, static_cast<uint8_t>(dex_classes.size()));
383       for (size_t i = 0; i < dex_classes.size(); i++) {
384         // Add the type index of the classes.
385         AddUintToBuffer(buffer, dex_classes[i].index_);
386       }
387     }
388   }
389 }
390 
GetMethodsRegionSize(const DexFileData & dex_data)391 uint32_t ProfileCompilationInfo::GetMethodsRegionSize(const DexFileData& dex_data) {
392   // ((uint16_t)method index + (uint16_t)inline cache size) * number of methods
393   uint32_t size = 2 * sizeof(uint16_t) * dex_data.method_map.size();
394   for (const auto& method_it : dex_data.method_map) {
395     const InlineCacheMap& inline_cache = method_it.second;
396     size += sizeof(uint16_t) * inline_cache.size();  // dex_pc
397     for (const auto& inline_cache_it : inline_cache) {
398       const ClassSet& classes = inline_cache_it.second.classes;
399       SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
400       GroupClassesByDex(classes, &dex_to_classes_map);
401       size += sizeof(uint8_t);  // dex_to_classes_map size
402       for (const auto& dex_it : dex_to_classes_map) {
403         size += sizeof(uint8_t);  // dex profile index
404         size += sizeof(uint8_t);  // number of classes
405         const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
406         size += sizeof(uint16_t) * dex_classes.size();  // the actual classes
407       }
408     }
409   }
410   return size;
411 }
412 
GroupClassesByDex(const ClassSet & classes,SafeMap<uint8_t,std::vector<dex::TypeIndex>> * dex_to_classes_map)413 void ProfileCompilationInfo::GroupClassesByDex(
414     const ClassSet& classes,
415     /*out*/SafeMap<uint8_t, std::vector<dex::TypeIndex>>* dex_to_classes_map) {
416   for (const auto& classes_it : classes) {
417     auto dex_it = dex_to_classes_map->FindOrAdd(classes_it.dex_profile_index);
418     dex_it->second.push_back(classes_it.type_index);
419   }
420 }
421 
GetOrAddDexFileData(const std::string & profile_key,uint32_t checksum)422 ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData(
423     const std::string& profile_key,
424     uint32_t checksum) {
425   const auto& profile_index_it = profile_key_map_.FindOrAdd(profile_key, profile_key_map_.size());
426   if (profile_key_map_.size() > std::numeric_limits<uint8_t>::max()) {
427     // Allow only 255 dex files to be profiled. This allows us to save bytes
428     // when encoding. The number is well above what we expect for normal applications.
429     if (kIsDebugBuild) {
430       LOG(ERROR) << "Exceeded the maximum number of dex files (255). Something went wrong";
431     }
432     profile_key_map_.erase(profile_key);
433     return nullptr;
434   }
435 
436   uint8_t profile_index = profile_index_it->second;
437   if (info_.size() <= profile_index) {
438     // This is a new addition. Add it to the info_ array.
439     DexFileData* dex_file_data = new (&arena_) DexFileData(
440         &arena_, profile_key, checksum, profile_index);
441     info_.push_back(dex_file_data);
442   }
443   DexFileData* result = info_[profile_index];
444   // DCHECK that profile info map key is consistent with the one stored in the dex file data.
445   // This should always be the case since since the cache map is managed by ProfileCompilationInfo.
446   DCHECK_EQ(profile_key, result->profile_key);
447   DCHECK_EQ(profile_index, result->profile_index);
448 
449   // Check that the checksum matches.
450   // This may different if for example the dex file was updated and
451   // we had a record of the old one.
452   if (result->checksum != checksum) {
453     LOG(WARNING) << "Checksum mismatch for dex " << profile_key;
454     return nullptr;
455   }
456   return result;
457 }
458 
FindDexData(const std::string & profile_key) const459 const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData(
460       const std::string& profile_key) const {
461   const auto& profile_index_it = profile_key_map_.find(profile_key);
462   if (profile_index_it == profile_key_map_.end()) {
463     return nullptr;
464   }
465 
466   uint8_t profile_index = profile_index_it->second;
467   const DexFileData* result = info_[profile_index];
468   DCHECK_EQ(profile_key, result->profile_key);
469   DCHECK_EQ(profile_index, result->profile_index);
470   return result;
471 }
472 
AddResolvedClasses(const DexCacheResolvedClasses & classes)473 bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& classes) {
474   const std::string dex_location = GetProfileDexFileKey(classes.GetDexLocation());
475   const uint32_t checksum = classes.GetLocationChecksum();
476   DexFileData* const data = GetOrAddDexFileData(dex_location, checksum);
477   if (data == nullptr) {
478     return false;
479   }
480   data->class_set.insert(classes.GetClasses().begin(), classes.GetClasses().end());
481   return true;
482 }
483 
AddMethodIndex(const std::string & dex_location,uint32_t dex_checksum,uint16_t method_index)484 bool ProfileCompilationInfo::AddMethodIndex(const std::string& dex_location,
485                                             uint32_t dex_checksum,
486                                             uint16_t method_index) {
487   return AddMethod(dex_location, dex_checksum, method_index, OfflineProfileMethodInfo(nullptr));
488 }
489 
AddMethod(const std::string & dex_location,uint32_t dex_checksum,uint16_t method_index,const OfflineProfileMethodInfo & pmi)490 bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
491                                        uint32_t dex_checksum,
492                                        uint16_t method_index,
493                                        const OfflineProfileMethodInfo& pmi) {
494   DexFileData* const data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location), dex_checksum);
495   if (data == nullptr) {  // checksum mismatch
496     return false;
497   }
498   // Add the method.
499   InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
500 
501   if (pmi.inline_caches == nullptr) {
502     // If we don't have inline caches return success right away.
503     return true;
504   }
505   for (const auto& pmi_inline_cache_it : *pmi.inline_caches) {
506     uint16_t pmi_ic_dex_pc = pmi_inline_cache_it.first;
507     const DexPcData& pmi_ic_dex_pc_data = pmi_inline_cache_it.second;
508     DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, pmi_ic_dex_pc);
509     if (dex_pc_data->is_missing_types || dex_pc_data->is_megamorphic) {
510       // We are already megamorphic or we are missing types; no point in going forward.
511       continue;
512     }
513 
514     if (pmi_ic_dex_pc_data.is_missing_types) {
515       dex_pc_data->SetIsMissingTypes();
516       continue;
517     }
518     if (pmi_ic_dex_pc_data.is_megamorphic) {
519       dex_pc_data->SetIsMegamorphic();
520       continue;
521     }
522 
523     for (const ClassReference& class_ref : pmi_ic_dex_pc_data.classes) {
524       const DexReference& dex_ref = pmi.dex_references[class_ref.dex_profile_index];
525       DexFileData* class_dex_data = GetOrAddDexFileData(
526           GetProfileDexFileKey(dex_ref.dex_location),
527           dex_ref.dex_checksum);
528       if (class_dex_data == nullptr) {  // checksum mismatch
529         return false;
530       }
531       dex_pc_data->AddClass(class_dex_data->profile_index, class_ref.type_index);
532     }
533   }
534   return true;
535 }
536 
AddMethod(const ProfileMethodInfo & pmi)537 bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) {
538   DexFileData* const data = GetOrAddDexFileData(
539       GetProfileDexFileKey(pmi.dex_file->GetLocation()),
540       pmi.dex_file->GetLocationChecksum());
541   if (data == nullptr) {  // checksum mismatch
542     return false;
543   }
544   InlineCacheMap* inline_cache = data->FindOrAddMethod(pmi.dex_method_index);
545 
546   for (const ProfileMethodInfo::ProfileInlineCache& cache : pmi.inline_caches) {
547     if (cache.is_missing_types) {
548       FindOrAddDexPc(inline_cache, cache.dex_pc)->SetIsMissingTypes();
549       continue;
550     }
551     for (const ProfileMethodInfo::ProfileClassReference& class_ref : cache.classes) {
552       DexFileData* class_dex_data = GetOrAddDexFileData(
553           GetProfileDexFileKey(class_ref.dex_file->GetLocation()),
554           class_ref.dex_file->GetLocationChecksum());
555       if (class_dex_data == nullptr) {  // checksum mismatch
556         return false;
557       }
558       DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, cache.dex_pc);
559       if (dex_pc_data->is_missing_types) {
560         // Don't bother adding classes if we are missing types.
561         break;
562       }
563       dex_pc_data->AddClass(class_dex_data->profile_index, class_ref.type_index);
564     }
565   }
566   return true;
567 }
568 
AddClassIndex(const std::string & dex_location,uint32_t checksum,dex::TypeIndex type_idx)569 bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location,
570                                            uint32_t checksum,
571                                            dex::TypeIndex type_idx) {
572   DexFileData* const data = GetOrAddDexFileData(dex_location, checksum);
573   if (data == nullptr) {
574     return false;
575   }
576   data->class_set.insert(type_idx);
577   return true;
578 }
579 
580 #define READ_UINT(type, buffer, dest, error)            \
581   do {                                                  \
582     if (!(buffer).ReadUintAndAdvance<type>(&(dest))) {  \
583       *(error) = "Could not read "#dest;                \
584       return false;                                     \
585     }                                                   \
586   }                                                     \
587   while (false)
588 
ReadInlineCache(SafeBuffer & buffer,uint8_t number_of_dex_files,InlineCacheMap * inline_cache,std::string * error)589 bool ProfileCompilationInfo::ReadInlineCache(SafeBuffer& buffer,
590                                              uint8_t number_of_dex_files,
591                                              /*out*/ InlineCacheMap* inline_cache,
592                                              /*out*/ std::string* error) {
593   uint16_t inline_cache_size;
594   READ_UINT(uint16_t, buffer, inline_cache_size, error);
595   for (; inline_cache_size > 0; inline_cache_size--) {
596     uint16_t dex_pc;
597     uint8_t dex_to_classes_map_size;
598     READ_UINT(uint16_t, buffer, dex_pc, error);
599     READ_UINT(uint8_t, buffer, dex_to_classes_map_size, error);
600     DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, dex_pc);
601     if (dex_to_classes_map_size == kIsMissingTypesEncoding) {
602       dex_pc_data->SetIsMissingTypes();
603       continue;
604     }
605     if (dex_to_classes_map_size == kIsMegamorphicEncoding) {
606       dex_pc_data->SetIsMegamorphic();
607       continue;
608     }
609     for (; dex_to_classes_map_size > 0; dex_to_classes_map_size--) {
610       uint8_t dex_profile_index;
611       uint8_t dex_classes_size;
612       READ_UINT(uint8_t, buffer, dex_profile_index, error);
613       READ_UINT(uint8_t, buffer, dex_classes_size, error);
614       if (dex_profile_index >= number_of_dex_files) {
615         *error = "dex_profile_index out of bounds ";
616         *error += std::to_string(dex_profile_index) + " " + std::to_string(number_of_dex_files);
617         return false;
618       }
619       for (; dex_classes_size > 0; dex_classes_size--) {
620         uint16_t type_index;
621         READ_UINT(uint16_t, buffer, type_index, error);
622         dex_pc_data->AddClass(dex_profile_index, dex::TypeIndex(type_index));
623       }
624     }
625   }
626   return true;
627 }
628 
ReadMethods(SafeBuffer & buffer,uint8_t number_of_dex_files,const ProfileLineHeader & line_header,std::string * error)629 bool ProfileCompilationInfo::ReadMethods(SafeBuffer& buffer,
630                                          uint8_t number_of_dex_files,
631                                          const ProfileLineHeader& line_header,
632                                          /*out*/std::string* error) {
633   while (buffer.HasMoreData()) {
634     DexFileData* const data = GetOrAddDexFileData(line_header.dex_location, line_header.checksum);
635     uint16_t method_index;
636     READ_UINT(uint16_t, buffer, method_index, error);
637 
638     InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
639     if (!ReadInlineCache(buffer, number_of_dex_files, inline_cache, error)) {
640       return false;
641     }
642   }
643 
644   return true;
645 }
646 
ReadClasses(SafeBuffer & buffer,uint16_t classes_to_read,const ProfileLineHeader & line_header,std::string * error)647 bool ProfileCompilationInfo::ReadClasses(SafeBuffer& buffer,
648                                          uint16_t classes_to_read,
649                                          const ProfileLineHeader& line_header,
650                                          /*out*/std::string* error) {
651   for (uint16_t i = 0; i < classes_to_read; i++) {
652     uint16_t type_index;
653     READ_UINT(uint16_t, buffer, type_index, error);
654     if (!AddClassIndex(line_header.dex_location,
655                        line_header.checksum,
656                        dex::TypeIndex(type_index))) {
657       return false;
658     }
659   }
660   return true;
661 }
662 
663 // Tests for EOF by trying to read 1 byte from the descriptor.
664 // Returns:
665 //   0 if the descriptor is at the EOF,
666 //  -1 if there was an IO error
667 //   1 if the descriptor has more content to read
testEOF(int fd)668 static int testEOF(int fd) {
669   uint8_t buffer[1];
670   return TEMP_FAILURE_RETRY(read(fd, buffer, 1));
671 }
672 
673 // Reads an uint value previously written with AddUintToBuffer.
674 template <typename T>
ReadUintAndAdvance(T * value)675 bool ProfileCompilationInfo::SafeBuffer::ReadUintAndAdvance(/*out*/T* value) {
676   static_assert(std::is_unsigned<T>::value, "Type is not unsigned");
677   if (ptr_current_ + sizeof(T) > ptr_end_) {
678     return false;
679   }
680   *value = 0;
681   for (size_t i = 0; i < sizeof(T); i++) {
682     *value += ptr_current_[i] << (i * kBitsPerByte);
683   }
684   ptr_current_ += sizeof(T);
685   return true;
686 }
687 
CompareAndAdvance(const uint8_t * data,size_t data_size)688 bool ProfileCompilationInfo::SafeBuffer::CompareAndAdvance(const uint8_t* data, size_t data_size) {
689   if (ptr_current_ + data_size > ptr_end_) {
690     return false;
691   }
692   if (memcmp(ptr_current_, data, data_size) == 0) {
693     ptr_current_ += data_size;
694     return true;
695   }
696   return false;
697 }
698 
HasMoreData()699 bool ProfileCompilationInfo::SafeBuffer::HasMoreData() {
700   return ptr_current_ < ptr_end_;
701 }
702 
FillFromFd(int fd,const std::string & source,std::string * error)703 ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::SafeBuffer::FillFromFd(
704       int fd,
705       const std::string& source,
706       /*out*/std::string* error) {
707   size_t byte_count = ptr_end_ - ptr_current_;
708   uint8_t* buffer = ptr_current_;
709   while (byte_count > 0) {
710     int bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, byte_count));
711     if (bytes_read == 0) {
712       *error += "Profile EOF reached prematurely for " + source;
713       return kProfileLoadBadData;
714     } else if (bytes_read < 0) {
715       *error += "Profile IO error for " + source + strerror(errno);
716       return kProfileLoadIOError;
717     }
718     byte_count -= bytes_read;
719     buffer += bytes_read;
720   }
721   return kProfileLoadSuccess;
722 }
723 
ReadProfileHeader(int fd,uint8_t * number_of_dex_files,std::string * error)724 ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileHeader(
725       int fd,
726       /*out*/uint8_t* number_of_dex_files,
727       /*out*/std::string* error) {
728   // Read magic and version
729   const size_t kMagicVersionSize =
730     sizeof(kProfileMagic) +
731     sizeof(kProfileVersion) +
732     sizeof(uint8_t);  // number of dex files
733 
734   SafeBuffer safe_buffer(kMagicVersionSize);
735 
736   ProfileLoadSatus status = safe_buffer.FillFromFd(fd, "ReadProfileHeader", error);
737   if (status != kProfileLoadSuccess) {
738     return status;
739   }
740 
741   if (!safe_buffer.CompareAndAdvance(kProfileMagic, sizeof(kProfileMagic))) {
742     *error = "Profile missing magic";
743     return kProfileLoadVersionMismatch;
744   }
745   if (!safe_buffer.CompareAndAdvance(kProfileVersion, sizeof(kProfileVersion))) {
746     *error = "Profile version mismatch";
747     return kProfileLoadVersionMismatch;
748   }
749   if (!safe_buffer.ReadUintAndAdvance<uint8_t>(number_of_dex_files)) {
750     *error = "Cannot read the number of dex files";
751     return kProfileLoadBadData;
752   }
753   return kProfileLoadSuccess;
754 }
755 
ReadProfileLineHeaderElements(SafeBuffer & buffer,uint16_t * dex_location_size,ProfileLineHeader * line_header,std::string * error)756 bool ProfileCompilationInfo::ReadProfileLineHeaderElements(SafeBuffer& buffer,
757                                                            /*out*/uint16_t* dex_location_size,
758                                                            /*out*/ProfileLineHeader* line_header,
759                                                            /*out*/std::string* error) {
760   READ_UINT(uint16_t, buffer, *dex_location_size, error);
761   READ_UINT(uint16_t, buffer, line_header->class_set_size, error);
762   READ_UINT(uint32_t, buffer, line_header->method_region_size_bytes, error);
763   READ_UINT(uint32_t, buffer, line_header->checksum, error);
764   return true;
765 }
766 
ReadProfileLineHeader(int fd,ProfileLineHeader * line_header,std::string * error)767 ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLineHeader(
768       int fd,
769       /*out*/ProfileLineHeader* line_header,
770       /*out*/std::string* error) {
771   SafeBuffer header_buffer(kLineHeaderSize);
772   ProfileLoadSatus status = header_buffer.FillFromFd(fd, "ReadProfileLineHeader", error);
773   if (status != kProfileLoadSuccess) {
774     return status;
775   }
776 
777   uint16_t dex_location_size;
778   if (!ReadProfileLineHeaderElements(header_buffer, &dex_location_size, line_header, error)) {
779     return kProfileLoadBadData;
780   }
781 
782   if (dex_location_size == 0 || dex_location_size > kMaxDexFileKeyLength) {
783     *error = "DexFileKey has an invalid size: " +
784         std::to_string(static_cast<uint32_t>(dex_location_size));
785     return kProfileLoadBadData;
786   }
787 
788   SafeBuffer location_buffer(dex_location_size);
789   status = location_buffer.FillFromFd(fd, "ReadProfileHeaderDexLocation", error);
790   if (status != kProfileLoadSuccess) {
791     return status;
792   }
793   line_header->dex_location.assign(
794       reinterpret_cast<char*>(location_buffer.Get()), dex_location_size);
795   return kProfileLoadSuccess;
796 }
797 
ReadProfileLine(int fd,uint8_t number_of_dex_files,const ProfileLineHeader & line_header,std::string * error)798 ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine(
799       int fd,
800       uint8_t number_of_dex_files,
801       const ProfileLineHeader& line_header,
802       /*out*/std::string* error) {
803   if (GetOrAddDexFileData(line_header.dex_location, line_header.checksum) == nullptr) {
804     *error = "Error when reading profile file line header: checksum mismatch for "
805         + line_header.dex_location;
806     return kProfileLoadBadData;
807   }
808 
809   {
810     SafeBuffer buffer(line_header.method_region_size_bytes);
811     ProfileLoadSatus status = buffer.FillFromFd(fd, "ReadProfileLineMethods", error);
812     if (status != kProfileLoadSuccess) {
813       return status;
814     }
815 
816     if (!ReadMethods(buffer, number_of_dex_files, line_header, error)) {
817       return kProfileLoadBadData;
818     }
819   }
820 
821   {
822     SafeBuffer buffer(sizeof(uint16_t) * line_header.class_set_size);
823     ProfileLoadSatus status = buffer.FillFromFd(fd, "ReadProfileLineClasses", error);
824     if (status != kProfileLoadSuccess) {
825       return status;
826     }
827     if (!ReadClasses(buffer, line_header.class_set_size, line_header, error)) {
828       return kProfileLoadBadData;
829     }
830   }
831 
832   return kProfileLoadSuccess;
833 }
834 
835 // TODO(calin): Fix this API. ProfileCompilationInfo::Load should be static and
836 // return a unique pointer to a ProfileCompilationInfo upon success.
Load(int fd)837 bool ProfileCompilationInfo::Load(int fd) {
838   std::string error;
839   ProfileLoadSatus status = LoadInternal(fd, &error);
840 
841   if (status == kProfileLoadSuccess) {
842     return true;
843   } else {
844     LOG(WARNING) << "Error when reading profile: " << error;
845     return false;
846   }
847 }
848 
849 // TODO(calin): fail fast if the dex checksums don't match.
LoadInternal(int fd,std::string * error)850 ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal(
851       int fd, std::string* error) {
852   ScopedTrace trace(__PRETTY_FUNCTION__);
853   DCHECK_GE(fd, 0);
854 
855   if (!IsEmpty()) {
856     return kProfileLoadWouldOverwiteData;
857   }
858 
859   struct stat stat_buffer;
860   if (fstat(fd, &stat_buffer) != 0) {
861     return kProfileLoadIOError;
862   }
863   // We allow empty profile files.
864   // Profiles may be created by ActivityManager or installd before we manage to
865   // process them in the runtime or profman.
866   if (stat_buffer.st_size == 0) {
867     return kProfileLoadSuccess;
868   }
869   // Read profile header: magic + version + number_of_dex_files.
870   uint8_t number_of_dex_files;
871   ProfileLoadSatus status = ReadProfileHeader(fd, &number_of_dex_files, error);
872   if (status != kProfileLoadSuccess) {
873     return status;
874   }
875 
876   for (uint8_t k = 0; k < number_of_dex_files; k++) {
877     ProfileLineHeader line_header;
878 
879     // First, read the line header to get the amount of data we need to read.
880     status = ReadProfileLineHeader(fd, &line_header, error);
881     if (status != kProfileLoadSuccess) {
882       return status;
883     }
884 
885     // Now read the actual profile line.
886     status = ReadProfileLine(fd, number_of_dex_files, line_header, error);
887     if (status != kProfileLoadSuccess) {
888       return status;
889     }
890   }
891 
892   // Check that we read everything and that profiles don't contain junk data.
893   int result = testEOF(fd);
894   if (result == 0) {
895     return kProfileLoadSuccess;
896   } else if (result < 0) {
897     return kProfileLoadIOError;
898   } else {
899     *error = "Unexpected content in the profile file";
900     return kProfileLoadBadData;
901   }
902 }
903 
MergeWith(const ProfileCompilationInfo & other)904 bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) {
905   // First verify that all checksums match. This will avoid adding garbage to
906   // the current profile info.
907   // Note that the number of elements should be very small, so this should not
908   // be a performance issue.
909   for (const DexFileData* other_dex_data : other.info_) {
910     const DexFileData* dex_data = FindDexData(other_dex_data->profile_key);
911     if ((dex_data != nullptr) && (dex_data->checksum != other_dex_data->checksum)) {
912       LOG(WARNING) << "Checksum mismatch for dex " << other_dex_data->profile_key;
913       return false;
914     }
915   }
916   // All checksums match. Import the data.
917 
918   // The other profile might have a different indexing of dex files.
919   // That is because each dex files gets a 'dex_profile_index' on a first come first served basis.
920   // That means that the order in with the methods are added to the profile matters for the
921   // actual indices.
922   // The reason we cannot rely on the actual multidex index is that a single profile may store
923   // data from multiple splits. This means that a profile may contain a classes2.dex from split-A
924   // and one from split-B.
925 
926   // First, build a mapping from other_dex_profile_index to this_dex_profile_index.
927   // This will make sure that the ClassReferences  will point to the correct dex file.
928   SafeMap<uint8_t, uint8_t> dex_profile_index_remap;
929   for (const DexFileData* other_dex_data : other.info_) {
930     const DexFileData* dex_data = GetOrAddDexFileData(other_dex_data->profile_key,
931                                                       other_dex_data->checksum);
932     if (dex_data == nullptr) {
933       return false;  // Could happen if we exceed the number of allowed dex files.
934     }
935     dex_profile_index_remap.Put(other_dex_data->profile_index, dex_data->profile_index);
936   }
937 
938   // Merge the actual profile data.
939   for (const DexFileData* other_dex_data : other.info_) {
940     DexFileData* dex_data = const_cast<DexFileData*>(FindDexData(other_dex_data->profile_key));
941     DCHECK(dex_data != nullptr);
942 
943     // Merge the classes.
944     dex_data->class_set.insert(other_dex_data->class_set.begin(),
945                                other_dex_data->class_set.end());
946 
947     // Merge the methods and the inline caches.
948     for (const auto& other_method_it : other_dex_data->method_map) {
949       uint16_t other_method_index = other_method_it.first;
950       InlineCacheMap* inline_cache = dex_data->FindOrAddMethod(other_method_index);
951       const auto& other_inline_cache = other_method_it.second;
952       for (const auto& other_ic_it : other_inline_cache) {
953         uint16_t other_dex_pc = other_ic_it.first;
954         const ClassSet& other_class_set = other_ic_it.second.classes;
955         DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, other_dex_pc);
956         if (other_ic_it.second.is_missing_types) {
957           dex_pc_data->SetIsMissingTypes();
958         } else if (other_ic_it.second.is_megamorphic) {
959           dex_pc_data->SetIsMegamorphic();
960         } else {
961           for (const auto& class_it : other_class_set) {
962             dex_pc_data->AddClass(dex_profile_index_remap.Get(
963                 class_it.dex_profile_index), class_it.type_index);
964           }
965         }
966       }
967     }
968   }
969   return true;
970 }
971 
ChecksumMatch(uint32_t dex_file_checksum,uint32_t checksum)972 static bool ChecksumMatch(uint32_t dex_file_checksum, uint32_t checksum) {
973   return kDebugIgnoreChecksum || dex_file_checksum == checksum;
974 }
975 
ChecksumMatch(const DexFile & dex_file,uint32_t checksum)976 static bool ChecksumMatch(const DexFile& dex_file, uint32_t checksum) {
977   return ChecksumMatch(dex_file.GetLocationChecksum(), checksum);
978 }
979 
ContainsMethod(const MethodReference & method_ref) const980 bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const {
981   return FindMethod(method_ref.dex_file->GetLocation(),
982                     method_ref.dex_file->GetLocationChecksum(),
983                     method_ref.dex_method_index) != nullptr;
984 }
985 
986 const ProfileCompilationInfo::InlineCacheMap*
FindMethod(const std::string & dex_location,uint32_t dex_checksum,uint16_t dex_method_index) const987 ProfileCompilationInfo::FindMethod(const std::string& dex_location,
988                                    uint32_t dex_checksum,
989                                    uint16_t dex_method_index) const {
990   const DexFileData* dex_data = FindDexData(GetProfileDexFileKey(dex_location));
991   if (dex_data != nullptr) {
992     if (!ChecksumMatch(dex_checksum, dex_data->checksum)) {
993       return nullptr;
994     }
995     const MethodMap& methods = dex_data->method_map;
996     const auto method_it = methods.find(dex_method_index);
997     return method_it == methods.end() ? nullptr : &(method_it->second);
998   }
999   return nullptr;
1000 }
1001 
GetMethod(const std::string & dex_location,uint32_t dex_checksum,uint16_t dex_method_index) const1002 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> ProfileCompilationInfo::GetMethod(
1003       const std::string& dex_location,
1004       uint32_t dex_checksum,
1005       uint16_t dex_method_index) const {
1006   const InlineCacheMap* inline_caches = FindMethod(dex_location, dex_checksum, dex_method_index);
1007   if (inline_caches == nullptr) {
1008     return nullptr;
1009   }
1010 
1011   std::unique_ptr<OfflineProfileMethodInfo> pmi(new OfflineProfileMethodInfo(inline_caches));
1012 
1013   pmi->dex_references.resize(info_.size());
1014   for (const DexFileData* dex_data : info_) {
1015     pmi->dex_references[dex_data->profile_index].dex_location = dex_data->profile_key;
1016     pmi->dex_references[dex_data->profile_index].dex_checksum = dex_data->checksum;
1017   }
1018 
1019   return pmi;
1020 }
1021 
1022 
ContainsClass(const DexFile & dex_file,dex::TypeIndex type_idx) const1023 bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const {
1024   const DexFileData* dex_data = FindDexData(GetProfileDexFileKey(dex_file.GetLocation()));
1025   if (dex_data != nullptr) {
1026     if (!ChecksumMatch(dex_file, dex_data->checksum)) {
1027       return false;
1028     }
1029     const ArenaSet<dex::TypeIndex>& classes = dex_data->class_set;
1030     return classes.find(type_idx) != classes.end();
1031   }
1032   return false;
1033 }
1034 
GetNumberOfMethods() const1035 uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
1036   uint32_t total = 0;
1037   for (const DexFileData* dex_data : info_) {
1038     total += dex_data->method_map.size();
1039   }
1040   return total;
1041 }
1042 
GetNumberOfResolvedClasses() const1043 uint32_t ProfileCompilationInfo::GetNumberOfResolvedClasses() const {
1044   uint32_t total = 0;
1045   for (const DexFileData* dex_data : info_) {
1046     total += dex_data->class_set.size();
1047   }
1048   return total;
1049 }
1050 
1051 // Produce a non-owning vector from a vector.
1052 template<typename T>
MakeNonOwningVector(const std::vector<std::unique_ptr<T>> * owning_vector)1053 const std::vector<T*>* MakeNonOwningVector(const std::vector<std::unique_ptr<T>>* owning_vector) {
1054   auto non_owning_vector = new std::vector<T*>();
1055   for (auto& element : *owning_vector) {
1056     non_owning_vector->push_back(element.get());
1057   }
1058   return non_owning_vector;
1059 }
1060 
DumpInfo(const std::vector<std::unique_ptr<const DexFile>> * dex_files,bool print_full_dex_location) const1061 std::string ProfileCompilationInfo::DumpInfo(
1062     const std::vector<std::unique_ptr<const DexFile>>* dex_files,
1063     bool print_full_dex_location) const {
1064   std::unique_ptr<const std::vector<const DexFile*>> non_owning_dex_files(
1065       MakeNonOwningVector(dex_files));
1066   return DumpInfo(non_owning_dex_files.get(), print_full_dex_location);
1067 }
1068 
DumpInfo(const std::vector<const DexFile * > * dex_files,bool print_full_dex_location) const1069 std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
1070                                              bool print_full_dex_location) const {
1071   std::ostringstream os;
1072   if (info_.empty()) {
1073     return "ProfileInfo: empty";
1074   }
1075 
1076   os << "ProfileInfo:";
1077 
1078   const std::string kFirstDexFileKeySubstitute = ":classes.dex";
1079 
1080   for (const DexFileData* dex_data : info_) {
1081     os << "\n";
1082     if (print_full_dex_location) {
1083       os << dex_data->profile_key;
1084     } else {
1085       // Replace the (empty) multidex suffix of the first key with a substitute for easier reading.
1086       std::string multidex_suffix = DexFile::GetMultiDexSuffix(dex_data->profile_key);
1087       os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
1088     }
1089     os << " [index=" << static_cast<uint32_t>(dex_data->profile_index) << "]";
1090     const DexFile* dex_file = nullptr;
1091     if (dex_files != nullptr) {
1092       for (size_t i = 0; i < dex_files->size(); i++) {
1093         if (dex_data->profile_key == (*dex_files)[i]->GetLocation()) {
1094           dex_file = (*dex_files)[i];
1095         }
1096       }
1097     }
1098     os << "\n\tmethods: ";
1099     for (const auto& method_it : dex_data->method_map) {
1100       if (dex_file != nullptr) {
1101         os << "\n\t\t" << dex_file->PrettyMethod(method_it.first, true);
1102       } else {
1103         os << method_it.first;
1104       }
1105 
1106       os << "[";
1107       for (const auto& inline_cache_it : method_it.second) {
1108         os << "{" << std::hex << inline_cache_it.first << std::dec << ":";
1109         if (inline_cache_it.second.is_missing_types) {
1110           os << "MT";
1111         } else if (inline_cache_it.second.is_megamorphic) {
1112           os << "MM";
1113         } else {
1114           for (const ClassReference& class_ref : inline_cache_it.second.classes) {
1115             os << "(" << static_cast<uint32_t>(class_ref.dex_profile_index)
1116                << "," << class_ref.type_index.index_ << ")";
1117           }
1118         }
1119         os << "}";
1120       }
1121       os << "], ";
1122     }
1123     os << "\n\tclasses: ";
1124     for (const auto class_it : dex_data->class_set) {
1125       if (dex_file != nullptr) {
1126         os << "\n\t\t" << dex_file->PrettyType(class_it);
1127       } else {
1128         os << class_it.index_ << ",";
1129       }
1130     }
1131   }
1132   return os.str();
1133 }
1134 
GetClassesAndMethods(const DexFile & dex_file,std::set<dex::TypeIndex> * class_set,std::set<uint16_t> * method_set) const1135 bool ProfileCompilationInfo::GetClassesAndMethods(const DexFile& dex_file,
1136                                                   std::set<dex::TypeIndex>* class_set,
1137                                                   std::set<uint16_t>* method_set) const {
1138   std::set<std::string> ret;
1139   std::string profile_key = GetProfileDexFileKey(dex_file.GetLocation());
1140   const DexFileData* dex_data = FindDexData(profile_key);
1141   if (dex_data == nullptr || dex_data->checksum != dex_file.GetLocationChecksum()) {
1142     return false;
1143   }
1144   for (const auto& it : dex_data->method_map) {
1145     method_set->insert(it.first);
1146   }
1147   for (const dex::TypeIndex& type_index : dex_data->class_set) {
1148     class_set->insert(type_index);
1149   }
1150   return true;
1151 }
1152 
Equals(const ProfileCompilationInfo & other)1153 bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) {
1154   // No need to compare profile_key_map_. That's only a cache for fast search.
1155   // All the information is already in the info_ vector.
1156   if (info_.size() != other.info_.size()) {
1157     return false;
1158   }
1159   for (size_t i = 0; i < info_.size(); i++) {
1160     const DexFileData& dex_data = *info_[i];
1161     const DexFileData& other_dex_data = *other.info_[i];
1162     if (!(dex_data == other_dex_data)) {
1163       return false;
1164     }
1165   }
1166   return true;
1167 }
1168 
GetResolvedClasses(const std::vector<const DexFile * > & dex_files) const1169 std::set<DexCacheResolvedClasses> ProfileCompilationInfo::GetResolvedClasses(
1170     const std::vector<const DexFile*>& dex_files) const {
1171   std::unordered_map<std::string, const DexFile* > key_to_dex_file;
1172   for (const DexFile* dex_file : dex_files) {
1173     key_to_dex_file.emplace(GetProfileDexFileKey(dex_file->GetLocation()), dex_file);
1174   }
1175   std::set<DexCacheResolvedClasses> ret;
1176   for (const DexFileData* dex_data : info_) {
1177     const auto it = key_to_dex_file.find(dex_data->profile_key);
1178     if (it != key_to_dex_file.end()) {
1179       const DexFile* dex_file = it->second;
1180       const std::string& dex_location = dex_file->GetLocation();
1181       if (dex_data->checksum != it->second->GetLocationChecksum()) {
1182         LOG(ERROR) << "Dex checksum mismatch when getting resolved classes from profile for "
1183             << "location " << dex_location << " (checksum=" << dex_file->GetLocationChecksum()
1184             << ", profile checksum=" << dex_data->checksum;
1185         return std::set<DexCacheResolvedClasses>();
1186       }
1187       DexCacheResolvedClasses classes(dex_location, dex_location, dex_data->checksum);
1188       classes.AddClasses(dex_data->class_set.begin(), dex_data->class_set.end());
1189       ret.insert(classes);
1190     }
1191   }
1192   return ret;
1193 }
1194 
1195 // Naive implementation to generate a random profile file suitable for testing.
GenerateTestProfile(int fd,uint16_t number_of_dex_files,uint16_t method_ratio,uint16_t class_ratio,uint32_t random_seed)1196 bool ProfileCompilationInfo::GenerateTestProfile(int fd,
1197                                                  uint16_t number_of_dex_files,
1198                                                  uint16_t method_ratio,
1199                                                  uint16_t class_ratio,
1200                                                  uint32_t random_seed) {
1201   const std::string base_dex_location = "base.apk";
1202   ProfileCompilationInfo info;
1203   // The limits are defined by the dex specification.
1204   uint16_t max_method = std::numeric_limits<uint16_t>::max();
1205   uint16_t max_classes = std::numeric_limits<uint16_t>::max();
1206   uint16_t number_of_methods = max_method * method_ratio / 100;
1207   uint16_t number_of_classes = max_classes * class_ratio / 100;
1208 
1209   std::srand(random_seed);
1210 
1211   // Make sure we generate more samples with a low index value.
1212   // This makes it more likely to hit valid method/class indices in small apps.
1213   const uint16_t kFavorFirstN = 10000;
1214   const uint16_t kFavorSplit = 2;
1215 
1216   for (uint16_t i = 0; i < number_of_dex_files; i++) {
1217     std::string dex_location = DexFile::GetMultiDexLocation(i, base_dex_location.c_str());
1218     std::string profile_key = GetProfileDexFileKey(dex_location);
1219 
1220     for (uint16_t m = 0; m < number_of_methods; m++) {
1221       uint16_t method_idx = rand() % max_method;
1222       if (m < (number_of_methods / kFavorSplit)) {
1223         method_idx %= kFavorFirstN;
1224       }
1225       info.AddMethodIndex(profile_key, 0, method_idx);
1226     }
1227 
1228     for (uint16_t c = 0; c < number_of_classes; c++) {
1229       uint16_t type_idx = rand() % max_classes;
1230       if (c < (number_of_classes / kFavorSplit)) {
1231         type_idx %= kFavorFirstN;
1232       }
1233       info.AddClassIndex(profile_key, 0, dex::TypeIndex(type_idx));
1234     }
1235   }
1236   return info.Save(fd);
1237 }
1238 
1239 // Naive implementation to generate a random profile file suitable for testing.
GenerateTestProfile(int fd,std::vector<std::unique_ptr<const DexFile>> & dex_files,uint32_t random_seed)1240 bool ProfileCompilationInfo::GenerateTestProfile(
1241     int fd,
1242     std::vector<std::unique_ptr<const DexFile>>& dex_files,
1243     uint32_t random_seed) {
1244   std::srand(random_seed);
1245   ProfileCompilationInfo info;
1246   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
1247     const std::string& location = dex_file->GetLocation();
1248     uint32_t checksum = dex_file->GetLocationChecksum();
1249     for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
1250       // Randomly add a class from the dex file (with 50% chance).
1251       if (std::rand() % 2 != 0) {
1252         info.AddClassIndex(location, checksum, dex::TypeIndex(dex_file->GetClassDef(i).class_idx_));
1253       }
1254     }
1255     for (uint32_t i = 0; i < dex_file->NumMethodIds(); ++i) {
1256       // Randomly add a method from the dex file (with 50% chance).
1257       if (std::rand() % 2 != 0) {
1258         info.AddMethodIndex(location, checksum, i);
1259       }
1260     }
1261   }
1262   return info.Save(fd);
1263 }
1264 
operator ==(const OfflineProfileMethodInfo & other) const1265 bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==(
1266       const OfflineProfileMethodInfo& other) const {
1267   if (inline_caches->size() != other.inline_caches->size()) {
1268     return false;
1269   }
1270 
1271   // We can't use a simple equality test because we need to match the dex files
1272   // of the inline caches which might have different profile indexes.
1273   for (const auto& inline_cache_it : *inline_caches) {
1274     uint16_t dex_pc = inline_cache_it.first;
1275     const DexPcData dex_pc_data = inline_cache_it.second;
1276     const auto& other_it = other.inline_caches->find(dex_pc);
1277     if (other_it == other.inline_caches->end()) {
1278       return false;
1279     }
1280     const DexPcData& other_dex_pc_data = other_it->second;
1281     if (dex_pc_data.is_megamorphic != other_dex_pc_data.is_megamorphic ||
1282         dex_pc_data.is_missing_types != other_dex_pc_data.is_missing_types) {
1283       return false;
1284     }
1285     for (const ClassReference& class_ref : dex_pc_data.classes) {
1286       bool found = false;
1287       for (const ClassReference& other_class_ref : other_dex_pc_data.classes) {
1288         CHECK_LE(class_ref.dex_profile_index, dex_references.size());
1289         CHECK_LE(other_class_ref.dex_profile_index, other.dex_references.size());
1290         const DexReference& dex_ref = dex_references[class_ref.dex_profile_index];
1291         const DexReference& other_dex_ref = other.dex_references[other_class_ref.dex_profile_index];
1292         if (class_ref.type_index == other_class_ref.type_index &&
1293             dex_ref == other_dex_ref) {
1294           found = true;
1295           break;
1296         }
1297       }
1298       if (!found) {
1299         return false;
1300       }
1301     }
1302   }
1303   return true;
1304 }
1305 
IsEmpty() const1306 bool ProfileCompilationInfo::IsEmpty() const {
1307   DCHECK_EQ(info_.empty(), profile_key_map_.empty());
1308   return info_.empty();
1309 }
1310 
1311 ProfileCompilationInfo::InlineCacheMap*
FindOrAddMethod(uint16_t method_index)1312 ProfileCompilationInfo::DexFileData::FindOrAddMethod(uint16_t method_index) {
1313   return &(method_map.FindOrAdd(
1314       method_index,
1315       InlineCacheMap(std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile)))->second);
1316 }
1317 
1318 ProfileCompilationInfo::DexPcData*
FindOrAddDexPc(InlineCacheMap * inline_cache,uint32_t dex_pc)1319 ProfileCompilationInfo::FindOrAddDexPc(InlineCacheMap* inline_cache, uint32_t dex_pc) {
1320   return &(inline_cache->FindOrAdd(dex_pc, DexPcData(&arena_))->second);
1321 }
1322 
1323 }  // namespace art
1324