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