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 <string>
22 #include <vector>
23 #include <stdlib.h>
24 #include <sys/file.h>
25 #include <sys/stat.h>
26 #include <sys/uio.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 #include <zlib.h>
32 #include <base/time_utils.h>
33
34 #include "base/arena_allocator.h"
35 #include "base/dumpable.h"
36 #include "base/mutex.h"
37 #include "base/scoped_flock.h"
38 #include "base/stl_util.h"
39 #include "base/systrace.h"
40 #include "base/unix_file/fd_file.h"
41 #include "jit/profiling_info.h"
42 #include "os.h"
43 #include "safe_map.h"
44 #include "utils.h"
45 #include "android-base/file.h"
46
47 namespace art {
48
49 const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' };
50 // Last profile version: update the multidex separator.
51 const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '9', '\0' };
52
53 static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
54
55 // Debug flag to ignore checksums when testing if a method or a class is present in the profile.
56 // Used to facilitate testing profile guided compilation across a large number of apps
57 // using the same test profile.
58 static constexpr bool kDebugIgnoreChecksum = false;
59
60 static constexpr uint8_t kIsMissingTypesEncoding = 6;
61 static constexpr uint8_t kIsMegamorphicEncoding = 7;
62
63 static_assert(sizeof(InlineCache::kIndividualCacheSize) == sizeof(uint8_t),
64 "InlineCache::kIndividualCacheSize does not have the expect type size");
65 static_assert(InlineCache::kIndividualCacheSize < kIsMegamorphicEncoding,
66 "InlineCache::kIndividualCacheSize is larger than expected");
67 static_assert(InlineCache::kIndividualCacheSize < kIsMissingTypesEncoding,
68 "InlineCache::kIndividualCacheSize is larger than expected");
69
ChecksumMatch(uint32_t dex_file_checksum,uint32_t checksum)70 static bool ChecksumMatch(uint32_t dex_file_checksum, uint32_t checksum) {
71 return kDebugIgnoreChecksum || dex_file_checksum == checksum;
72 }
73
ProfileCompilationInfo(ArenaPool * custom_arena_pool)74 ProfileCompilationInfo::ProfileCompilationInfo(ArenaPool* custom_arena_pool)
75 : default_arena_pool_(),
76 arena_(custom_arena_pool),
77 info_(arena_.Adapter(kArenaAllocProfile)),
78 profile_key_map_(std::less<const std::string>(), arena_.Adapter(kArenaAllocProfile)) {
79 }
80
ProfileCompilationInfo()81 ProfileCompilationInfo::ProfileCompilationInfo()
82 : default_arena_pool_(/*use_malloc*/true, /*low_4gb*/false, "ProfileCompilationInfo"),
83 arena_(&default_arena_pool_),
84 info_(arena_.Adapter(kArenaAllocProfile)),
85 profile_key_map_(std::less<const std::string>(), arena_.Adapter(kArenaAllocProfile)) {
86 }
87
~ProfileCompilationInfo()88 ProfileCompilationInfo::~ProfileCompilationInfo() {
89 VLOG(profiler) << Dumpable<MemStats>(arena_.GetMemStats());
90 for (DexFileData* data : info_) {
91 delete data;
92 }
93 }
94
AddClass(uint16_t dex_profile_idx,const dex::TypeIndex & type_idx)95 void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx,
96 const dex::TypeIndex& type_idx) {
97 if (is_megamorphic || is_missing_types) {
98 return;
99 }
100
101 // Perform an explicit lookup for the type instead of directly emplacing the
102 // element. We do this because emplace() allocates the node before doing the
103 // lookup and if it then finds an identical element, it shall deallocate the
104 // node. For Arena allocations, that's essentially a leak.
105 ClassReference ref(dex_profile_idx, type_idx);
106 auto it = classes.find(ref);
107 if (it != classes.end()) {
108 // The type index exists.
109 return;
110 }
111
112 // Check if the adding the type will cause the cache to become megamorphic.
113 if (classes.size() + 1 >= InlineCache::kIndividualCacheSize) {
114 is_megamorphic = true;
115 classes.clear();
116 return;
117 }
118
119 // The type does not exist and the inline cache will not be megamorphic.
120 classes.insert(ref);
121 }
122
123 // Transform the actual dex location into relative paths.
124 // Note: this is OK because we don't store profiles of different apps into the same file.
125 // Apps with split apks don't cause trouble because each split has a different name and will not
126 // collide with other entries.
GetProfileDexFileKey(const std::string & dex_location)127 std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_location) {
128 DCHECK(!dex_location.empty());
129 size_t last_sep_index = dex_location.find_last_of('/');
130 if (last_sep_index == std::string::npos) {
131 return dex_location;
132 } else {
133 DCHECK(last_sep_index < dex_location.size());
134 return dex_location.substr(last_sep_index + 1);
135 }
136 }
137
AddMethodIndex(MethodHotness::Flag flags,const MethodReference & ref)138 bool ProfileCompilationInfo::AddMethodIndex(MethodHotness::Flag flags, const MethodReference& ref) {
139 DexFileData* data = GetOrAddDexFileData(ref.dex_file);
140 if (data == nullptr) {
141 return false;
142 }
143 return data->AddMethod(flags, ref.dex_method_index);
144 }
145
AddMethodIndex(MethodHotness::Flag flags,const std::string & dex_location,uint32_t checksum,uint16_t method_idx,uint32_t num_method_ids)146 bool ProfileCompilationInfo::AddMethodIndex(MethodHotness::Flag flags,
147 const std::string& dex_location,
148 uint32_t checksum,
149 uint16_t method_idx,
150 uint32_t num_method_ids) {
151 DexFileData* data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location),
152 checksum,
153 num_method_ids);
154 if (data == nullptr) {
155 return false;
156 }
157 return data->AddMethod(flags, method_idx);
158 }
159
AddMethods(const std::vector<ProfileMethodInfo> & methods)160 bool ProfileCompilationInfo::AddMethods(const std::vector<ProfileMethodInfo>& methods) {
161 for (const ProfileMethodInfo& method : methods) {
162 if (!AddMethod(method)) {
163 return false;
164 }
165 }
166 return true;
167 }
168
AddClasses(const std::set<DexCacheResolvedClasses> & resolved_classes)169 bool ProfileCompilationInfo::AddClasses(const std::set<DexCacheResolvedClasses>& resolved_classes) {
170 for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
171 if (!AddResolvedClasses(dex_cache)) {
172 return false;
173 }
174 }
175 return true;
176 }
177
Load(const std::string & filename,bool clear_if_invalid)178 bool ProfileCompilationInfo::Load(const std::string& filename, bool clear_if_invalid) {
179 ScopedTrace trace(__PRETTY_FUNCTION__);
180 std::string error;
181 int flags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
182 // There's no need to fsync profile data right away. We get many chances
183 // to write it again in case something goes wrong. We can rely on a simple
184 // close(), no sync, and let to the kernel decide when to write to disk.
185 ScopedFlock profile_file = LockedFile::Open(filename.c_str(), flags,
186 /*block*/false, &error);
187
188 if (profile_file.get() == nullptr) {
189 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
190 return false;
191 }
192
193 int fd = profile_file->Fd();
194
195 ProfileLoadSatus status = LoadInternal(fd, &error);
196 if (status == kProfileLoadSuccess) {
197 return true;
198 }
199
200 if (clear_if_invalid &&
201 ((status == kProfileLoadVersionMismatch) || (status == kProfileLoadBadData))) {
202 LOG(WARNING) << "Clearing bad or obsolete profile data from file "
203 << filename << ": " << error;
204 if (profile_file->ClearContent()) {
205 return true;
206 } else {
207 PLOG(WARNING) << "Could not clear profile file: " << filename;
208 return false;
209 }
210 }
211
212 LOG(WARNING) << "Could not load profile data from file " << filename << ": " << error;
213 return false;
214 }
215
Save(const std::string & filename,uint64_t * bytes_written)216 bool ProfileCompilationInfo::Save(const std::string& filename, uint64_t* bytes_written) {
217 ScopedTrace trace(__PRETTY_FUNCTION__);
218 std::string error;
219 int flags = O_WRONLY | O_NOFOLLOW | O_CLOEXEC;
220 // There's no need to fsync profile data right away. We get many chances
221 // to write it again in case something goes wrong. We can rely on a simple
222 // close(), no sync, and let to the kernel decide when to write to disk.
223 ScopedFlock profile_file = LockedFile::Open(filename.c_str(), flags,
224 /*block*/false, &error);
225 if (profile_file.get() == nullptr) {
226 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
227 return false;
228 }
229
230 int fd = profile_file->Fd();
231
232 // We need to clear the data because we don't support appending to the profiles yet.
233 if (!profile_file->ClearContent()) {
234 PLOG(WARNING) << "Could not clear profile file: " << filename;
235 return false;
236 }
237
238 // This doesn't need locking because we are trying to lock the file for exclusive
239 // access and fail immediately if we can't.
240 bool result = Save(fd);
241 if (result) {
242 int64_t size = GetFileSizeBytes(filename);
243 if (size != -1) {
244 VLOG(profiler)
245 << "Successfully saved profile info to " << filename << " Size: "
246 << size;
247 if (bytes_written != nullptr) {
248 *bytes_written = static_cast<uint64_t>(size);
249 }
250 }
251 } else {
252 VLOG(profiler) << "Failed to save profile info to " << filename;
253 }
254 return result;
255 }
256
257 // Returns true if all the bytes were successfully written to the file descriptor.
WriteBuffer(int fd,const uint8_t * buffer,size_t byte_count)258 static bool WriteBuffer(int fd, const uint8_t* buffer, size_t byte_count) {
259 while (byte_count > 0) {
260 int bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer, byte_count));
261 if (bytes_written == -1) {
262 return false;
263 }
264 byte_count -= bytes_written; // Reduce the number of remaining bytes.
265 buffer += bytes_written; // Move the buffer forward.
266 }
267 return true;
268 }
269
270 // Add the string bytes to the buffer.
AddStringToBuffer(std::vector<uint8_t> * buffer,const std::string & value)271 static void AddStringToBuffer(std::vector<uint8_t>* buffer, const std::string& value) {
272 buffer->insert(buffer->end(), value.begin(), value.end());
273 }
274
275 // Insert each byte, from low to high into the buffer.
276 template <typename T>
AddUintToBuffer(std::vector<uint8_t> * buffer,T value)277 static void AddUintToBuffer(std::vector<uint8_t>* buffer, T value) {
278 for (size_t i = 0; i < sizeof(T); i++) {
279 buffer->push_back((value >> (i * kBitsPerByte)) & 0xff);
280 }
281 }
282
283 static constexpr size_t kLineHeaderSize =
284 2 * sizeof(uint16_t) + // class_set.size + dex_location.size
285 3 * sizeof(uint32_t); // method_map.size + checksum + num_method_ids
286
287 /**
288 * Serialization format:
289 * magic,version,number_of_dex_files,uncompressed_size_of_zipped_data,compressed_data_size,
290 * zipped[dex_location1,number_of_classes1,methods_region_size,dex_location_checksum1
291 * num_method_ids,
292 * method_encoding_11,method_encoding_12...,class_id1,class_id2...
293 * startup/post startup bitmap,
294 * dex_location2,number_of_classes2,methods_region_size,dex_location_checksum2, num_method_ids,
295 * method_encoding_21,method_encoding_22...,,class_id1,class_id2...
296 * startup/post startup bitmap,
297 * .....]
298 * The method_encoding is:
299 * method_id,number_of_inline_caches,inline_cache1,inline_cache2...
300 * The inline_cache is:
301 * dex_pc,[M|dex_map_size], dex_profile_index,class_id1,class_id2...,dex_profile_index2,...
302 * dex_map_size is the number of dex_indeces that follows.
303 * Classes are grouped per their dex files and the line
304 * `dex_profile_index,class_id1,class_id2...,dex_profile_index2,...` encodes the
305 * mapping from `dex_profile_index` to the set of classes `class_id1,class_id2...`
306 * M stands for megamorphic or missing types and it's encoded as either
307 * the byte kIsMegamorphicEncoding or kIsMissingTypesEncoding.
308 * When present, there will be no class ids following.
309 **/
Save(int fd)310 bool ProfileCompilationInfo::Save(int fd) {
311 uint64_t start = NanoTime();
312 ScopedTrace trace(__PRETTY_FUNCTION__);
313 DCHECK_GE(fd, 0);
314
315 // Use a vector wrapper to avoid keeping track of offsets when we add elements.
316 std::vector<uint8_t> buffer;
317 if (!WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic))) {
318 return false;
319 }
320 if (!WriteBuffer(fd, kProfileVersion, sizeof(kProfileVersion))) {
321 return false;
322 }
323 DCHECK_LE(info_.size(), std::numeric_limits<uint8_t>::max());
324 AddUintToBuffer(&buffer, static_cast<uint8_t>(info_.size()));
325
326 uint32_t required_capacity = 0;
327 for (const DexFileData* dex_data_ptr : info_) {
328 const DexFileData& dex_data = *dex_data_ptr;
329 uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
330 required_capacity += kLineHeaderSize +
331 dex_data.profile_key.size() +
332 sizeof(uint16_t) * dex_data.class_set.size() +
333 methods_region_size +
334 dex_data.bitmap_storage.size();
335 }
336 // Allow large profiles for non target builds for the case where we are merging many profiles
337 // to generate a boot image profile.
338 if (kIsTargetBuild && required_capacity > kProfileSizeErrorThresholdInBytes) {
339 LOG(ERROR) << "Profile data size exceeds "
340 << std::to_string(kProfileSizeErrorThresholdInBytes)
341 << " bytes. Profile will not be written to disk.";
342 return false;
343 }
344 if (required_capacity > kProfileSizeWarningThresholdInBytes) {
345 LOG(WARNING) << "Profile data size exceeds "
346 << std::to_string(kProfileSizeWarningThresholdInBytes);
347 }
348 AddUintToBuffer(&buffer, required_capacity);
349 if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
350 return false;
351 }
352 // Make sure that the buffer has enough capacity to avoid repeated resizings
353 // while we add data.
354 buffer.reserve(required_capacity);
355 buffer.clear();
356
357 // Dex files must be written in the order of their profile index. This
358 // avoids writing the index in the output file and simplifies the parsing logic.
359 for (const DexFileData* dex_data_ptr : info_) {
360 const DexFileData& dex_data = *dex_data_ptr;
361
362 // Note that we allow dex files without any methods or classes, so that
363 // inline caches can refer valid dex files.
364
365 if (dex_data.profile_key.size() >= kMaxDexFileKeyLength) {
366 LOG(WARNING) << "DexFileKey exceeds allocated limit";
367 return false;
368 }
369
370 uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
371
372 DCHECK_LE(dex_data.profile_key.size(), std::numeric_limits<uint16_t>::max());
373 DCHECK_LE(dex_data.class_set.size(), std::numeric_limits<uint16_t>::max());
374 // Write profile line header.
375 AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.profile_key.size()));
376 AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.class_set.size()));
377 AddUintToBuffer(&buffer, methods_region_size); // uint32_t
378 AddUintToBuffer(&buffer, dex_data.checksum); // uint32_t
379 AddUintToBuffer(&buffer, dex_data.num_method_ids); // uint32_t
380
381 AddStringToBuffer(&buffer, dex_data.profile_key);
382
383 uint16_t last_method_index = 0;
384 for (const auto& method_it : dex_data.method_map) {
385 // Store the difference between the method indices. The SafeMap is ordered by
386 // method_id, so the difference will always be non negative.
387 DCHECK_GE(method_it.first, last_method_index);
388 uint16_t diff_with_last_method_index = method_it.first - last_method_index;
389 last_method_index = method_it.first;
390 AddUintToBuffer(&buffer, diff_with_last_method_index);
391 AddInlineCacheToBuffer(&buffer, method_it.second);
392 }
393
394 uint16_t last_class_index = 0;
395 for (const auto& class_id : dex_data.class_set) {
396 // Store the difference between the class indices. The set is ordered by
397 // class_id, so the difference will always be non negative.
398 DCHECK_GE(class_id.index_, last_class_index);
399 uint16_t diff_with_last_class_index = class_id.index_ - last_class_index;
400 last_class_index = class_id.index_;
401 AddUintToBuffer(&buffer, diff_with_last_class_index);
402 }
403
404 buffer.insert(buffer.end(),
405 dex_data.bitmap_storage.begin(),
406 dex_data.bitmap_storage.end());
407 }
408
409 uint32_t output_size = 0;
410 std::unique_ptr<uint8_t[]> compressed_buffer = DeflateBuffer(buffer.data(),
411 required_capacity,
412 &output_size);
413
414 buffer.clear();
415 AddUintToBuffer(&buffer, output_size);
416
417 if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
418 return false;
419 }
420 if (!WriteBuffer(fd, compressed_buffer.get(), output_size)) {
421 return false;
422 }
423 uint64_t total_time = NanoTime() - start;
424 VLOG(profiler) << "Compressed from "
425 << std::to_string(required_capacity)
426 << " to "
427 << std::to_string(output_size);
428 VLOG(profiler) << "Time to save profile: " << std::to_string(total_time);
429 return true;
430 }
431
AddInlineCacheToBuffer(std::vector<uint8_t> * buffer,const InlineCacheMap & inline_cache_map)432 void ProfileCompilationInfo::AddInlineCacheToBuffer(std::vector<uint8_t>* buffer,
433 const InlineCacheMap& inline_cache_map) {
434 // Add inline cache map size.
435 AddUintToBuffer(buffer, static_cast<uint16_t>(inline_cache_map.size()));
436 if (inline_cache_map.size() == 0) {
437 return;
438 }
439 for (const auto& inline_cache_it : inline_cache_map) {
440 uint16_t dex_pc = inline_cache_it.first;
441 const DexPcData dex_pc_data = inline_cache_it.second;
442 const ClassSet& classes = dex_pc_data.classes;
443
444 // Add the dex pc.
445 AddUintToBuffer(buffer, dex_pc);
446
447 // Add the megamorphic/missing_types encoding if needed and continue.
448 // In either cases we don't add any classes to the profiles and so there's
449 // no point to continue.
450 // TODO(calin): in case we miss types there is still value to add the
451 // rest of the classes. They can be added without bumping the profile version.
452 if (dex_pc_data.is_missing_types) {
453 DCHECK(!dex_pc_data.is_megamorphic); // at this point the megamorphic flag should not be set.
454 DCHECK_EQ(classes.size(), 0u);
455 AddUintToBuffer(buffer, kIsMissingTypesEncoding);
456 continue;
457 } else if (dex_pc_data.is_megamorphic) {
458 DCHECK_EQ(classes.size(), 0u);
459 AddUintToBuffer(buffer, kIsMegamorphicEncoding);
460 continue;
461 }
462
463 DCHECK_LT(classes.size(), InlineCache::kIndividualCacheSize);
464 DCHECK_NE(classes.size(), 0u) << "InlineCache contains a dex_pc with 0 classes";
465
466 SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
467 // Group the classes by dex. We expect that most of the classes will come from
468 // the same dex, so this will be more efficient than encoding the dex index
469 // for each class reference.
470 GroupClassesByDex(classes, &dex_to_classes_map);
471 // Add the dex map size.
472 AddUintToBuffer(buffer, static_cast<uint8_t>(dex_to_classes_map.size()));
473 for (const auto& dex_it : dex_to_classes_map) {
474 uint8_t dex_profile_index = dex_it.first;
475 const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
476 // Add the dex profile index.
477 AddUintToBuffer(buffer, dex_profile_index);
478 // Add the the number of classes for each dex profile index.
479 AddUintToBuffer(buffer, static_cast<uint8_t>(dex_classes.size()));
480 for (size_t i = 0; i < dex_classes.size(); i++) {
481 // Add the type index of the classes.
482 AddUintToBuffer(buffer, dex_classes[i].index_);
483 }
484 }
485 }
486 }
487
GetMethodsRegionSize(const DexFileData & dex_data)488 uint32_t ProfileCompilationInfo::GetMethodsRegionSize(const DexFileData& dex_data) {
489 // ((uint16_t)method index + (uint16_t)inline cache size) * number of methods
490 uint32_t size = 2 * sizeof(uint16_t) * dex_data.method_map.size();
491 for (const auto& method_it : dex_data.method_map) {
492 const InlineCacheMap& inline_cache = method_it.second;
493 size += sizeof(uint16_t) * inline_cache.size(); // dex_pc
494 for (const auto& inline_cache_it : inline_cache) {
495 const ClassSet& classes = inline_cache_it.second.classes;
496 SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
497 GroupClassesByDex(classes, &dex_to_classes_map);
498 size += sizeof(uint8_t); // dex_to_classes_map size
499 for (const auto& dex_it : dex_to_classes_map) {
500 size += sizeof(uint8_t); // dex profile index
501 size += sizeof(uint8_t); // number of classes
502 const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
503 size += sizeof(uint16_t) * dex_classes.size(); // the actual classes
504 }
505 }
506 }
507 return size;
508 }
509
GroupClassesByDex(const ClassSet & classes,SafeMap<uint8_t,std::vector<dex::TypeIndex>> * dex_to_classes_map)510 void ProfileCompilationInfo::GroupClassesByDex(
511 const ClassSet& classes,
512 /*out*/SafeMap<uint8_t, std::vector<dex::TypeIndex>>* dex_to_classes_map) {
513 for (const auto& classes_it : classes) {
514 auto dex_it = dex_to_classes_map->FindOrAdd(classes_it.dex_profile_index);
515 dex_it->second.push_back(classes_it.type_index);
516 }
517 }
518
GetOrAddDexFileData(const std::string & profile_key,uint32_t checksum,uint32_t num_method_ids)519 ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData(
520 const std::string& profile_key,
521 uint32_t checksum,
522 uint32_t num_method_ids) {
523 const auto profile_index_it = profile_key_map_.FindOrAdd(profile_key, profile_key_map_.size());
524 if (profile_key_map_.size() > std::numeric_limits<uint8_t>::max()) {
525 // Allow only 255 dex files to be profiled. This allows us to save bytes
526 // when encoding. The number is well above what we expect for normal applications.
527 if (kIsDebugBuild) {
528 LOG(ERROR) << "Exceeded the maximum number of dex files (255). Something went wrong";
529 }
530 profile_key_map_.erase(profile_key);
531 return nullptr;
532 }
533
534 uint8_t profile_index = profile_index_it->second;
535 if (info_.size() <= profile_index) {
536 // This is a new addition. Add it to the info_ array.
537 DexFileData* dex_file_data = new (&arena_) DexFileData(
538 &arena_,
539 profile_key,
540 checksum,
541 profile_index,
542 num_method_ids);
543 info_.push_back(dex_file_data);
544 }
545 DexFileData* result = info_[profile_index];
546
547 // Check that the checksum matches.
548 // This may different if for example the dex file was updated and we had a record of the old one.
549 if (result->checksum != checksum) {
550 LOG(WARNING) << "Checksum mismatch for dex " << profile_key;
551 return nullptr;
552 }
553
554 // DCHECK that profile info map key is consistent with the one stored in the dex file data.
555 // This should always be the case since since the cache map is managed by ProfileCompilationInfo.
556 DCHECK_EQ(profile_key, result->profile_key);
557 DCHECK_EQ(profile_index, result->profile_index);
558
559 if (num_method_ids != result->num_method_ids) {
560 // This should not happen... added to help investigating b/65812889.
561 LOG(ERROR) << "num_method_ids mismatch for dex " << profile_key
562 << ", expected=" << num_method_ids
563 << ", actual=" << result->num_method_ids;
564 return nullptr;
565 }
566
567 return result;
568 }
569
FindDexData(const std::string & profile_key,uint32_t checksum,bool verify_checksum) const570 const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData(
571 const std::string& profile_key,
572 uint32_t checksum,
573 bool verify_checksum) const {
574 const auto profile_index_it = profile_key_map_.find(profile_key);
575 if (profile_index_it == profile_key_map_.end()) {
576 return nullptr;
577 }
578
579 uint8_t profile_index = profile_index_it->second;
580 const DexFileData* result = info_[profile_index];
581 if (verify_checksum && !ChecksumMatch(result->checksum, checksum)) {
582 return nullptr;
583 }
584 DCHECK_EQ(profile_key, result->profile_key);
585 DCHECK_EQ(profile_index, result->profile_index);
586 return result;
587 }
588
AddResolvedClasses(const DexCacheResolvedClasses & classes)589 bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& classes) {
590 const std::string dex_location = GetProfileDexFileKey(classes.GetDexLocation());
591 const uint32_t checksum = classes.GetLocationChecksum();
592 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum, classes.NumMethodIds());
593 if (data == nullptr) {
594 return false;
595 }
596 data->class_set.insert(classes.GetClasses().begin(), classes.GetClasses().end());
597 return true;
598 }
599
AddMethod(const std::string & dex_location,uint32_t dex_checksum,uint16_t method_index,uint32_t num_method_ids,const OfflineProfileMethodInfo & pmi)600 bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
601 uint32_t dex_checksum,
602 uint16_t method_index,
603 uint32_t num_method_ids,
604 const OfflineProfileMethodInfo& pmi) {
605 DexFileData* const data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location),
606 dex_checksum,
607 num_method_ids);
608 if (data == nullptr) { // checksum mismatch
609 return false;
610 }
611 // Add the method.
612 InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
613
614 if (pmi.inline_caches == nullptr) {
615 // If we don't have inline caches return success right away.
616 return true;
617 }
618 for (const auto& pmi_inline_cache_it : *pmi.inline_caches) {
619 uint16_t pmi_ic_dex_pc = pmi_inline_cache_it.first;
620 const DexPcData& pmi_ic_dex_pc_data = pmi_inline_cache_it.second;
621 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, pmi_ic_dex_pc);
622 if (dex_pc_data->is_missing_types || dex_pc_data->is_megamorphic) {
623 // We are already megamorphic or we are missing types; no point in going forward.
624 continue;
625 }
626
627 if (pmi_ic_dex_pc_data.is_missing_types) {
628 dex_pc_data->SetIsMissingTypes();
629 continue;
630 }
631 if (pmi_ic_dex_pc_data.is_megamorphic) {
632 dex_pc_data->SetIsMegamorphic();
633 continue;
634 }
635
636 for (const ClassReference& class_ref : pmi_ic_dex_pc_data.classes) {
637 const DexReference& dex_ref = pmi.dex_references[class_ref.dex_profile_index];
638 DexFileData* class_dex_data = GetOrAddDexFileData(
639 GetProfileDexFileKey(dex_ref.dex_location),
640 dex_ref.dex_checksum,
641 dex_ref.num_method_ids);
642 if (class_dex_data == nullptr) { // checksum mismatch
643 return false;
644 }
645 dex_pc_data->AddClass(class_dex_data->profile_index, class_ref.type_index);
646 }
647 }
648 return true;
649 }
650
AddMethod(const ProfileMethodInfo & pmi)651 bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) {
652 DexFileData* const data = GetOrAddDexFileData(pmi.ref.dex_file);
653 if (data == nullptr) { // checksum mismatch
654 return false;
655 }
656 InlineCacheMap* inline_cache = data->FindOrAddMethod(pmi.ref.dex_method_index);
657
658 for (const ProfileMethodInfo::ProfileInlineCache& cache : pmi.inline_caches) {
659 if (cache.is_missing_types) {
660 FindOrAddDexPc(inline_cache, cache.dex_pc)->SetIsMissingTypes();
661 continue;
662 }
663 for (const TypeReference& class_ref : cache.classes) {
664 DexFileData* class_dex_data = GetOrAddDexFileData(class_ref.dex_file);
665 if (class_dex_data == nullptr) { // checksum mismatch
666 return false;
667 }
668 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, cache.dex_pc);
669 if (dex_pc_data->is_missing_types) {
670 // Don't bother adding classes if we are missing types.
671 break;
672 }
673 dex_pc_data->AddClass(class_dex_data->profile_index, class_ref.type_index);
674 }
675 }
676 return true;
677 }
678
AddClassIndex(const std::string & dex_location,uint32_t checksum,dex::TypeIndex type_idx,uint32_t num_method_ids)679 bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location,
680 uint32_t checksum,
681 dex::TypeIndex type_idx,
682 uint32_t num_method_ids) {
683 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum, num_method_ids);
684 if (data == nullptr) {
685 return false;
686 }
687 data->class_set.insert(type_idx);
688 return true;
689 }
690
691 #define READ_UINT(type, buffer, dest, error) \
692 do { \
693 if (!(buffer).ReadUintAndAdvance<type>(&(dest))) { \
694 *(error) = "Could not read "#dest; \
695 return false; \
696 } \
697 } \
698 while (false)
699
ReadInlineCache(SafeBuffer & buffer,uint8_t number_of_dex_files,InlineCacheMap * inline_cache,std::string * error)700 bool ProfileCompilationInfo::ReadInlineCache(SafeBuffer& buffer,
701 uint8_t number_of_dex_files,
702 /*out*/ InlineCacheMap* inline_cache,
703 /*out*/ std::string* error) {
704 uint16_t inline_cache_size;
705 READ_UINT(uint16_t, buffer, inline_cache_size, error);
706 for (; inline_cache_size > 0; inline_cache_size--) {
707 uint16_t dex_pc;
708 uint8_t dex_to_classes_map_size;
709 READ_UINT(uint16_t, buffer, dex_pc, error);
710 READ_UINT(uint8_t, buffer, dex_to_classes_map_size, error);
711 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, dex_pc);
712 if (dex_to_classes_map_size == kIsMissingTypesEncoding) {
713 dex_pc_data->SetIsMissingTypes();
714 continue;
715 }
716 if (dex_to_classes_map_size == kIsMegamorphicEncoding) {
717 dex_pc_data->SetIsMegamorphic();
718 continue;
719 }
720 for (; dex_to_classes_map_size > 0; dex_to_classes_map_size--) {
721 uint8_t dex_profile_index;
722 uint8_t dex_classes_size;
723 READ_UINT(uint8_t, buffer, dex_profile_index, error);
724 READ_UINT(uint8_t, buffer, dex_classes_size, error);
725 if (dex_profile_index >= number_of_dex_files) {
726 *error = "dex_profile_index out of bounds ";
727 *error += std::to_string(dex_profile_index) + " " + std::to_string(number_of_dex_files);
728 return false;
729 }
730 for (; dex_classes_size > 0; dex_classes_size--) {
731 uint16_t type_index;
732 READ_UINT(uint16_t, buffer, type_index, error);
733 dex_pc_data->AddClass(dex_profile_index, dex::TypeIndex(type_index));
734 }
735 }
736 }
737 return true;
738 }
739
ReadMethods(SafeBuffer & buffer,uint8_t number_of_dex_files,const ProfileLineHeader & line_header,std::string * error)740 bool ProfileCompilationInfo::ReadMethods(SafeBuffer& buffer,
741 uint8_t number_of_dex_files,
742 const ProfileLineHeader& line_header,
743 /*out*/std::string* error) {
744 uint32_t unread_bytes_before_operation = buffer.CountUnreadBytes();
745 if (unread_bytes_before_operation < line_header.method_region_size_bytes) {
746 *error += "Profile EOF reached prematurely for ReadMethod";
747 return kProfileLoadBadData;
748 }
749 size_t expected_unread_bytes_after_operation = buffer.CountUnreadBytes()
750 - line_header.method_region_size_bytes;
751 uint16_t last_method_index = 0;
752 while (buffer.CountUnreadBytes() > expected_unread_bytes_after_operation) {
753 DexFileData* const data = GetOrAddDexFileData(line_header.dex_location,
754 line_header.checksum,
755 line_header.num_method_ids);
756 uint16_t diff_with_last_method_index;
757 READ_UINT(uint16_t, buffer, diff_with_last_method_index, error);
758 uint16_t method_index = last_method_index + diff_with_last_method_index;
759 last_method_index = method_index;
760 InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
761 if (!ReadInlineCache(buffer, number_of_dex_files, inline_cache, error)) {
762 return false;
763 }
764 }
765 uint32_t total_bytes_read = unread_bytes_before_operation - buffer.CountUnreadBytes();
766 if (total_bytes_read != line_header.method_region_size_bytes) {
767 *error += "Profile data inconsistent for ReadMethods";
768 return false;
769 }
770 return true;
771 }
772
ReadClasses(SafeBuffer & buffer,const ProfileLineHeader & line_header,std::string * error)773 bool ProfileCompilationInfo::ReadClasses(SafeBuffer& buffer,
774 const ProfileLineHeader& line_header,
775 /*out*/std::string* error) {
776 size_t unread_bytes_before_op = buffer.CountUnreadBytes();
777 if (unread_bytes_before_op < line_header.class_set_size) {
778 *error += "Profile EOF reached prematurely for ReadClasses";
779 return kProfileLoadBadData;
780 }
781
782 uint16_t last_class_index = 0;
783 for (uint16_t i = 0; i < line_header.class_set_size; i++) {
784 uint16_t diff_with_last_class_index;
785 READ_UINT(uint16_t, buffer, diff_with_last_class_index, error);
786 uint16_t type_index = last_class_index + diff_with_last_class_index;
787 last_class_index = type_index;
788 if (!AddClassIndex(line_header.dex_location,
789 line_header.checksum,
790 dex::TypeIndex(type_index),
791 line_header.num_method_ids)) {
792 return false;
793 }
794 }
795 size_t total_bytes_read = unread_bytes_before_op - buffer.CountUnreadBytes();
796 uint32_t expected_bytes_read = line_header.class_set_size * sizeof(uint16_t);
797 if (total_bytes_read != expected_bytes_read) {
798 *error += "Profile data inconsistent for ReadClasses";
799 return false;
800 }
801 return true;
802 }
803
804 // Tests for EOF by trying to read 1 byte from the descriptor.
805 // Returns:
806 // 0 if the descriptor is at the EOF,
807 // -1 if there was an IO error
808 // 1 if the descriptor has more content to read
testEOF(int fd)809 static int testEOF(int fd) {
810 uint8_t buffer[1];
811 return TEMP_FAILURE_RETRY(read(fd, buffer, 1));
812 }
813
814 // Reads an uint value previously written with AddUintToBuffer.
815 template <typename T>
ReadUintAndAdvance(T * value)816 bool ProfileCompilationInfo::SafeBuffer::ReadUintAndAdvance(/*out*/T* value) {
817 static_assert(std::is_unsigned<T>::value, "Type is not unsigned");
818 if (ptr_current_ + sizeof(T) > ptr_end_) {
819 return false;
820 }
821 *value = 0;
822 for (size_t i = 0; i < sizeof(T); i++) {
823 *value += ptr_current_[i] << (i * kBitsPerByte);
824 }
825 ptr_current_ += sizeof(T);
826 return true;
827 }
828
CompareAndAdvance(const uint8_t * data,size_t data_size)829 bool ProfileCompilationInfo::SafeBuffer::CompareAndAdvance(const uint8_t* data, size_t data_size) {
830 if (ptr_current_ + data_size > ptr_end_) {
831 return false;
832 }
833 if (memcmp(ptr_current_, data, data_size) == 0) {
834 ptr_current_ += data_size;
835 return true;
836 }
837 return false;
838 }
839
FillFromFd(int fd,const std::string & source,std::string * error)840 ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::SafeBuffer::FillFromFd(
841 int fd,
842 const std::string& source,
843 /*out*/std::string* error) {
844 size_t byte_count = (ptr_end_ - ptr_current_) * sizeof(*ptr_current_);
845 uint8_t* buffer = ptr_current_;
846 while (byte_count > 0) {
847 int bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, byte_count));
848 if (bytes_read == 0) {
849 *error += "Profile EOF reached prematurely for " + source;
850 return kProfileLoadBadData;
851 } else if (bytes_read < 0) {
852 *error += "Profile IO error for " + source + strerror(errno);
853 return kProfileLoadIOError;
854 }
855 byte_count -= bytes_read;
856 buffer += bytes_read;
857 }
858 return kProfileLoadSuccess;
859 }
860
CountUnreadBytes()861 size_t ProfileCompilationInfo::SafeBuffer::CountUnreadBytes() {
862 return (ptr_end_ - ptr_current_) * sizeof(*ptr_current_);
863 }
864
GetCurrentPtr()865 const uint8_t* ProfileCompilationInfo::SafeBuffer::GetCurrentPtr() {
866 return ptr_current_;
867 }
868
Advance(size_t data_size)869 void ProfileCompilationInfo::SafeBuffer::Advance(size_t data_size) {
870 ptr_current_ += data_size;
871 }
872
ReadProfileHeader(int fd,uint8_t * number_of_dex_files,uint32_t * uncompressed_data_size,uint32_t * compressed_data_size,std::string * error)873 ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileHeader(
874 int fd,
875 /*out*/uint8_t* number_of_dex_files,
876 /*out*/uint32_t* uncompressed_data_size,
877 /*out*/uint32_t* compressed_data_size,
878 /*out*/std::string* error) {
879 // Read magic and version
880 const size_t kMagicVersionSize =
881 sizeof(kProfileMagic) +
882 sizeof(kProfileVersion) +
883 sizeof(uint8_t) + // number of dex files
884 sizeof(uint32_t) + // size of uncompressed profile data
885 sizeof(uint32_t); // size of compressed profile data
886
887 SafeBuffer safe_buffer(kMagicVersionSize);
888
889 ProfileLoadSatus status = safe_buffer.FillFromFd(fd, "ReadProfileHeader", error);
890 if (status != kProfileLoadSuccess) {
891 return status;
892 }
893
894 if (!safe_buffer.CompareAndAdvance(kProfileMagic, sizeof(kProfileMagic))) {
895 *error = "Profile missing magic";
896 return kProfileLoadVersionMismatch;
897 }
898 if (!safe_buffer.CompareAndAdvance(kProfileVersion, sizeof(kProfileVersion))) {
899 *error = "Profile version mismatch";
900 return kProfileLoadVersionMismatch;
901 }
902 if (!safe_buffer.ReadUintAndAdvance<uint8_t>(number_of_dex_files)) {
903 *error = "Cannot read the number of dex files";
904 return kProfileLoadBadData;
905 }
906 if (!safe_buffer.ReadUintAndAdvance<uint32_t>(uncompressed_data_size)) {
907 *error = "Cannot read the size of uncompressed data";
908 return kProfileLoadBadData;
909 }
910 if (!safe_buffer.ReadUintAndAdvance<uint32_t>(compressed_data_size)) {
911 *error = "Cannot read the size of compressed data";
912 return kProfileLoadBadData;
913 }
914 return kProfileLoadSuccess;
915 }
916
ReadProfileLineHeaderElements(SafeBuffer & buffer,uint16_t * dex_location_size,ProfileLineHeader * line_header,std::string * error)917 bool ProfileCompilationInfo::ReadProfileLineHeaderElements(SafeBuffer& buffer,
918 /*out*/uint16_t* dex_location_size,
919 /*out*/ProfileLineHeader* line_header,
920 /*out*/std::string* error) {
921 READ_UINT(uint16_t, buffer, *dex_location_size, error);
922 READ_UINT(uint16_t, buffer, line_header->class_set_size, error);
923 READ_UINT(uint32_t, buffer, line_header->method_region_size_bytes, error);
924 READ_UINT(uint32_t, buffer, line_header->checksum, error);
925 READ_UINT(uint32_t, buffer, line_header->num_method_ids, error);
926 return true;
927 }
928
ReadProfileLineHeader(SafeBuffer & buffer,ProfileLineHeader * line_header,std::string * error)929 ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLineHeader(
930 SafeBuffer& buffer,
931 /*out*/ProfileLineHeader* line_header,
932 /*out*/std::string* error) {
933 if (buffer.CountUnreadBytes() < kLineHeaderSize) {
934 *error += "Profile EOF reached prematurely for ReadProfileLineHeader";
935 return kProfileLoadBadData;
936 }
937
938 uint16_t dex_location_size;
939 if (!ReadProfileLineHeaderElements(buffer, &dex_location_size, line_header, error)) {
940 return kProfileLoadBadData;
941 }
942
943 if (dex_location_size == 0 || dex_location_size > kMaxDexFileKeyLength) {
944 *error = "DexFileKey has an invalid size: " +
945 std::to_string(static_cast<uint32_t>(dex_location_size));
946 return kProfileLoadBadData;
947 }
948
949 if (buffer.CountUnreadBytes() < dex_location_size) {
950 *error += "Profile EOF reached prematurely for ReadProfileHeaderDexLocation";
951 return kProfileLoadBadData;
952 }
953 const uint8_t* base_ptr = buffer.GetCurrentPtr();
954 line_header->dex_location.assign(
955 reinterpret_cast<const char*>(base_ptr), dex_location_size);
956 buffer.Advance(dex_location_size);
957 return kProfileLoadSuccess;
958 }
959
ReadProfileLine(SafeBuffer & buffer,uint8_t number_of_dex_files,const ProfileLineHeader & line_header,std::string * error)960 ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine(
961 SafeBuffer& buffer,
962 uint8_t number_of_dex_files,
963 const ProfileLineHeader& line_header,
964 /*out*/std::string* error) {
965 DexFileData* data = GetOrAddDexFileData(line_header.dex_location,
966 line_header.checksum,
967 line_header.num_method_ids);
968 if (data == nullptr) {
969 *error = "Error when reading profile file line header: checksum mismatch for "
970 + line_header.dex_location;
971 return kProfileLoadBadData;
972 }
973
974 if (!ReadMethods(buffer, number_of_dex_files, line_header, error)) {
975 return kProfileLoadBadData;
976 }
977
978 if (!ReadClasses(buffer, line_header, error)) {
979 return kProfileLoadBadData;
980 }
981
982 const size_t bytes = data->bitmap_storage.size();
983 if (buffer.CountUnreadBytes() < bytes) {
984 *error += "Profile EOF reached prematurely for ReadProfileHeaderDexLocation";
985 return kProfileLoadBadData;
986 }
987 const uint8_t* base_ptr = buffer.GetCurrentPtr();
988 std::copy_n(base_ptr, bytes, &data->bitmap_storage[0]);
989 buffer.Advance(bytes);
990 // Read method bitmap.
991 return kProfileLoadSuccess;
992 }
993
994 // TODO(calin): Fix this API. ProfileCompilationInfo::Load should be static and
995 // return a unique pointer to a ProfileCompilationInfo upon success.
Load(int fd)996 bool ProfileCompilationInfo::Load(int fd) {
997 std::string error;
998 ProfileLoadSatus status = LoadInternal(fd, &error);
999
1000 if (status == kProfileLoadSuccess) {
1001 return true;
1002 } else {
1003 LOG(WARNING) << "Error when reading profile: " << error;
1004 return false;
1005 }
1006 }
1007
1008 // TODO(calin): fail fast if the dex checksums don't match.
LoadInternal(int fd,std::string * error)1009 ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal(
1010 int fd, std::string* error) {
1011 ScopedTrace trace(__PRETTY_FUNCTION__);
1012 DCHECK_GE(fd, 0);
1013
1014 if (!IsEmpty()) {
1015 return kProfileLoadWouldOverwiteData;
1016 }
1017
1018 struct stat stat_buffer;
1019 if (fstat(fd, &stat_buffer) != 0) {
1020 return kProfileLoadIOError;
1021 }
1022 // We allow empty profile files.
1023 // Profiles may be created by ActivityManager or installd before we manage to
1024 // process them in the runtime or profman.
1025 if (stat_buffer.st_size == 0) {
1026 return kProfileLoadSuccess;
1027 }
1028 // Read profile header: magic + version + number_of_dex_files.
1029 uint8_t number_of_dex_files;
1030 uint32_t uncompressed_data_size;
1031 uint32_t compressed_data_size;
1032 ProfileLoadSatus status = ReadProfileHeader(fd,
1033 &number_of_dex_files,
1034 &uncompressed_data_size,
1035 &compressed_data_size,
1036 error);
1037
1038 if (status != kProfileLoadSuccess) {
1039 return status;
1040 }
1041 // Allow large profiles for non target builds for the case where we are merging many profiles
1042 // to generate a boot image profile.
1043 if (kIsTargetBuild && uncompressed_data_size > kProfileSizeErrorThresholdInBytes) {
1044 LOG(ERROR) << "Profile data size exceeds "
1045 << std::to_string(kProfileSizeErrorThresholdInBytes)
1046 << " bytes";
1047 return kProfileLoadBadData;
1048 }
1049 if (uncompressed_data_size > kProfileSizeWarningThresholdInBytes) {
1050 LOG(WARNING) << "Profile data size exceeds "
1051 << std::to_string(kProfileSizeWarningThresholdInBytes)
1052 << " bytes";
1053 }
1054
1055 std::unique_ptr<uint8_t[]> compressed_data(new uint8_t[compressed_data_size]);
1056 bool bytes_read_success =
1057 android::base::ReadFully(fd, compressed_data.get(), compressed_data_size);
1058
1059 if (testEOF(fd) != 0) {
1060 *error += "Unexpected data in the profile file.";
1061 return kProfileLoadBadData;
1062 }
1063
1064 if (!bytes_read_success) {
1065 *error += "Unable to read compressed profile data";
1066 return kProfileLoadBadData;
1067 }
1068
1069 SafeBuffer uncompressed_data(uncompressed_data_size);
1070
1071 int ret = InflateBuffer(compressed_data.get(),
1072 compressed_data_size,
1073 uncompressed_data_size,
1074 uncompressed_data.Get());
1075
1076 if (ret != Z_STREAM_END) {
1077 *error += "Error reading uncompressed profile data";
1078 return kProfileLoadBadData;
1079 }
1080
1081 for (uint8_t k = 0; k < number_of_dex_files; k++) {
1082 ProfileLineHeader line_header;
1083
1084 // First, read the line header to get the amount of data we need to read.
1085 status = ReadProfileLineHeader(uncompressed_data, &line_header, error);
1086 if (status != kProfileLoadSuccess) {
1087 return status;
1088 }
1089
1090 // Now read the actual profile line.
1091 status = ReadProfileLine(uncompressed_data, number_of_dex_files, line_header, error);
1092 if (status != kProfileLoadSuccess) {
1093 return status;
1094 }
1095 }
1096
1097 // Check that we read everything and that profiles don't contain junk data.
1098 if (uncompressed_data.CountUnreadBytes() > 0) {
1099 *error = "Unexpected content in the profile file";
1100 return kProfileLoadBadData;
1101 } else {
1102 return kProfileLoadSuccess;
1103 }
1104 }
1105
DeflateBuffer(const uint8_t * in_buffer,uint32_t in_size,uint32_t * compressed_data_size)1106 std::unique_ptr<uint8_t[]> ProfileCompilationInfo::DeflateBuffer(const uint8_t* in_buffer,
1107 uint32_t in_size,
1108 uint32_t* compressed_data_size) {
1109 z_stream strm;
1110 strm.zalloc = Z_NULL;
1111 strm.zfree = Z_NULL;
1112 strm.opaque = Z_NULL;
1113 int ret = deflateInit(&strm, 1);
1114 if (ret != Z_OK) {
1115 return nullptr;
1116 }
1117
1118 uint32_t out_size = deflateBound(&strm, in_size);
1119
1120 std::unique_ptr<uint8_t[]> compressed_buffer(new uint8_t[out_size]);
1121 strm.avail_in = in_size;
1122 strm.next_in = const_cast<uint8_t*>(in_buffer);
1123 strm.avail_out = out_size;
1124 strm.next_out = &compressed_buffer[0];
1125 ret = deflate(&strm, Z_FINISH);
1126 if (ret == Z_STREAM_ERROR) {
1127 return nullptr;
1128 }
1129 *compressed_data_size = out_size - strm.avail_out;
1130 deflateEnd(&strm);
1131 return compressed_buffer;
1132 }
1133
InflateBuffer(const uint8_t * in_buffer,uint32_t in_size,uint32_t expected_uncompressed_data_size,uint8_t * out_buffer)1134 int ProfileCompilationInfo::InflateBuffer(const uint8_t* in_buffer,
1135 uint32_t in_size,
1136 uint32_t expected_uncompressed_data_size,
1137 uint8_t* out_buffer) {
1138 z_stream strm;
1139
1140 /* allocate inflate state */
1141 strm.zalloc = Z_NULL;
1142 strm.zfree = Z_NULL;
1143 strm.opaque = Z_NULL;
1144 strm.avail_in = in_size;
1145 strm.next_in = const_cast<uint8_t*>(in_buffer);
1146 strm.avail_out = expected_uncompressed_data_size;
1147 strm.next_out = out_buffer;
1148
1149 int ret;
1150 inflateInit(&strm);
1151 ret = inflate(&strm, Z_NO_FLUSH);
1152
1153 if (strm.avail_in != 0 || strm.avail_out != 0) {
1154 return Z_DATA_ERROR;
1155 }
1156 inflateEnd(&strm);
1157 return ret;
1158 }
1159
MergeWith(const ProfileCompilationInfo & other,bool merge_classes)1160 bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other,
1161 bool merge_classes) {
1162 // First verify that all checksums match. This will avoid adding garbage to
1163 // the current profile info.
1164 // Note that the number of elements should be very small, so this should not
1165 // be a performance issue.
1166 for (const DexFileData* other_dex_data : other.info_) {
1167 // verify_checksum is false because we want to differentiate between a missing dex data and
1168 // a mismatched checksum.
1169 const DexFileData* dex_data = FindDexData(other_dex_data->profile_key,
1170 0u,
1171 /* verify_checksum */ false);
1172 if ((dex_data != nullptr) && (dex_data->checksum != other_dex_data->checksum)) {
1173 LOG(WARNING) << "Checksum mismatch for dex " << other_dex_data->profile_key;
1174 return false;
1175 }
1176 }
1177 // All checksums match. Import the data.
1178
1179 // The other profile might have a different indexing of dex files.
1180 // That is because each dex files gets a 'dex_profile_index' on a first come first served basis.
1181 // That means that the order in with the methods are added to the profile matters for the
1182 // actual indices.
1183 // The reason we cannot rely on the actual multidex index is that a single profile may store
1184 // data from multiple splits. This means that a profile may contain a classes2.dex from split-A
1185 // and one from split-B.
1186
1187 // First, build a mapping from other_dex_profile_index to this_dex_profile_index.
1188 // This will make sure that the ClassReferences will point to the correct dex file.
1189 SafeMap<uint8_t, uint8_t> dex_profile_index_remap;
1190 for (const DexFileData* other_dex_data : other.info_) {
1191 const DexFileData* dex_data = GetOrAddDexFileData(other_dex_data->profile_key,
1192 other_dex_data->checksum,
1193 other_dex_data->num_method_ids);
1194 if (dex_data == nullptr) {
1195 return false; // Could happen if we exceed the number of allowed dex files.
1196 }
1197 dex_profile_index_remap.Put(other_dex_data->profile_index, dex_data->profile_index);
1198 }
1199
1200 // Merge the actual profile data.
1201 for (const DexFileData* other_dex_data : other.info_) {
1202 DexFileData* dex_data = const_cast<DexFileData*>(FindDexData(other_dex_data->profile_key,
1203 other_dex_data->checksum));
1204 DCHECK(dex_data != nullptr);
1205
1206 // Merge the classes.
1207 if (merge_classes) {
1208 dex_data->class_set.insert(other_dex_data->class_set.begin(),
1209 other_dex_data->class_set.end());
1210 }
1211
1212 // Merge the methods and the inline caches.
1213 for (const auto& other_method_it : other_dex_data->method_map) {
1214 uint16_t other_method_index = other_method_it.first;
1215 InlineCacheMap* inline_cache = dex_data->FindOrAddMethod(other_method_index);
1216 const auto& other_inline_cache = other_method_it.second;
1217 for (const auto& other_ic_it : other_inline_cache) {
1218 uint16_t other_dex_pc = other_ic_it.first;
1219 const ClassSet& other_class_set = other_ic_it.second.classes;
1220 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, other_dex_pc);
1221 if (other_ic_it.second.is_missing_types) {
1222 dex_pc_data->SetIsMissingTypes();
1223 } else if (other_ic_it.second.is_megamorphic) {
1224 dex_pc_data->SetIsMegamorphic();
1225 } else {
1226 for (const auto& class_it : other_class_set) {
1227 dex_pc_data->AddClass(dex_profile_index_remap.Get(
1228 class_it.dex_profile_index), class_it.type_index);
1229 }
1230 }
1231 }
1232 }
1233
1234 // Merge the method bitmaps.
1235 dex_data->MergeBitmap(*other_dex_data);
1236 }
1237 return true;
1238 }
1239
FindDexData(const DexFile * dex_file) const1240 const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData(
1241 const DexFile* dex_file) const {
1242 return FindDexData(GetProfileDexFileKey(dex_file->GetLocation()),
1243 dex_file->GetLocationChecksum());
1244 }
1245
GetMethodHotness(const MethodReference & method_ref) const1246 ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::GetMethodHotness(
1247 const MethodReference& method_ref) const {
1248 const DexFileData* dex_data = FindDexData(method_ref.dex_file);
1249 return dex_data != nullptr
1250 ? dex_data->GetHotnessInfo(method_ref.dex_method_index)
1251 : MethodHotness();
1252 }
1253
AddMethodHotness(const MethodReference & method_ref,const MethodHotness & hotness)1254 bool ProfileCompilationInfo::AddMethodHotness(const MethodReference& method_ref,
1255 const MethodHotness& hotness) {
1256 DexFileData* dex_data = GetOrAddDexFileData(method_ref.dex_file);
1257 if (dex_data != nullptr) {
1258 // TODO: Add inline caches.
1259 return dex_data->AddMethod(
1260 static_cast<MethodHotness::Flag>(hotness.GetFlags()),
1261 method_ref.dex_method_index);
1262 }
1263 return false;
1264 }
1265
GetMethodHotness(const std::string & dex_location,uint32_t dex_checksum,uint16_t dex_method_index) const1266 ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::GetMethodHotness(
1267 const std::string& dex_location,
1268 uint32_t dex_checksum,
1269 uint16_t dex_method_index) const {
1270 const DexFileData* dex_data = FindDexData(GetProfileDexFileKey(dex_location), dex_checksum);
1271 return dex_data != nullptr ? dex_data->GetHotnessInfo(dex_method_index) : MethodHotness();
1272 }
1273
1274
GetMethod(const std::string & dex_location,uint32_t dex_checksum,uint16_t dex_method_index) const1275 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> ProfileCompilationInfo::GetMethod(
1276 const std::string& dex_location,
1277 uint32_t dex_checksum,
1278 uint16_t dex_method_index) const {
1279 MethodHotness hotness(GetMethodHotness(dex_location, dex_checksum, dex_method_index));
1280 if (!hotness.IsHot()) {
1281 return nullptr;
1282 }
1283 const InlineCacheMap* inline_caches = hotness.GetInlineCacheMap();
1284 DCHECK(inline_caches != nullptr);
1285 std::unique_ptr<OfflineProfileMethodInfo> pmi(new OfflineProfileMethodInfo(inline_caches));
1286
1287 pmi->dex_references.resize(info_.size());
1288 for (const DexFileData* dex_data : info_) {
1289 pmi->dex_references[dex_data->profile_index].dex_location = dex_data->profile_key;
1290 pmi->dex_references[dex_data->profile_index].dex_checksum = dex_data->checksum;
1291 pmi->dex_references[dex_data->profile_index].num_method_ids = dex_data->num_method_ids;
1292 }
1293
1294 return pmi;
1295 }
1296
1297
ContainsClass(const DexFile & dex_file,dex::TypeIndex type_idx) const1298 bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const {
1299 const DexFileData* dex_data = FindDexData(&dex_file);
1300 if (dex_data != nullptr) {
1301 const ArenaSet<dex::TypeIndex>& classes = dex_data->class_set;
1302 return classes.find(type_idx) != classes.end();
1303 }
1304 return false;
1305 }
1306
GetNumberOfMethods() const1307 uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
1308 uint32_t total = 0;
1309 for (const DexFileData* dex_data : info_) {
1310 total += dex_data->method_map.size();
1311 }
1312 return total;
1313 }
1314
GetNumberOfResolvedClasses() const1315 uint32_t ProfileCompilationInfo::GetNumberOfResolvedClasses() const {
1316 uint32_t total = 0;
1317 for (const DexFileData* dex_data : info_) {
1318 total += dex_data->class_set.size();
1319 }
1320 return total;
1321 }
1322
1323 // Produce a non-owning vector from a vector.
1324 template<typename T>
MakeNonOwningVector(const std::vector<std::unique_ptr<T>> * owning_vector)1325 const std::vector<T*>* MakeNonOwningVector(const std::vector<std::unique_ptr<T>>* owning_vector) {
1326 auto non_owning_vector = new std::vector<T*>();
1327 for (auto& element : *owning_vector) {
1328 non_owning_vector->push_back(element.get());
1329 }
1330 return non_owning_vector;
1331 }
1332
DumpInfo(const std::vector<std::unique_ptr<const DexFile>> * dex_files,bool print_full_dex_location) const1333 std::string ProfileCompilationInfo::DumpInfo(
1334 const std::vector<std::unique_ptr<const DexFile>>* dex_files,
1335 bool print_full_dex_location) const {
1336 std::unique_ptr<const std::vector<const DexFile*>> non_owning_dex_files(
1337 MakeNonOwningVector(dex_files));
1338 return DumpInfo(non_owning_dex_files.get(), print_full_dex_location);
1339 }
1340
DumpInfo(const std::vector<const DexFile * > * dex_files,bool print_full_dex_location) const1341 std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
1342 bool print_full_dex_location) const {
1343 std::ostringstream os;
1344 if (info_.empty()) {
1345 return "ProfileInfo: empty";
1346 }
1347
1348 os << "ProfileInfo:";
1349
1350 const std::string kFirstDexFileKeySubstitute = "!classes.dex";
1351
1352 for (const DexFileData* dex_data : info_) {
1353 os << "\n";
1354 if (print_full_dex_location) {
1355 os << dex_data->profile_key;
1356 } else {
1357 // Replace the (empty) multidex suffix of the first key with a substitute for easier reading.
1358 std::string multidex_suffix = DexFile::GetMultiDexSuffix(dex_data->profile_key);
1359 os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
1360 }
1361 os << " [index=" << static_cast<uint32_t>(dex_data->profile_index) << "]";
1362 const DexFile* dex_file = nullptr;
1363 if (dex_files != nullptr) {
1364 for (size_t i = 0; i < dex_files->size(); i++) {
1365 if (dex_data->profile_key == (*dex_files)[i]->GetLocation()) {
1366 dex_file = (*dex_files)[i];
1367 }
1368 }
1369 }
1370 os << "\n\thot methods: ";
1371 for (const auto& method_it : dex_data->method_map) {
1372 if (dex_file != nullptr) {
1373 os << "\n\t\t" << dex_file->PrettyMethod(method_it.first, true);
1374 } else {
1375 os << method_it.first;
1376 }
1377
1378 os << "[";
1379 for (const auto& inline_cache_it : method_it.second) {
1380 os << "{" << std::hex << inline_cache_it.first << std::dec << ":";
1381 if (inline_cache_it.second.is_missing_types) {
1382 os << "MT";
1383 } else if (inline_cache_it.second.is_megamorphic) {
1384 os << "MM";
1385 } else {
1386 for (const ClassReference& class_ref : inline_cache_it.second.classes) {
1387 os << "(" << static_cast<uint32_t>(class_ref.dex_profile_index)
1388 << "," << class_ref.type_index.index_ << ")";
1389 }
1390 }
1391 os << "}";
1392 }
1393 os << "], ";
1394 }
1395 bool startup = true;
1396 while (true) {
1397 os << "\n\t" << (startup ? "startup methods: " : "post startup methods: ");
1398 for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) {
1399 MethodHotness hotness_info(dex_data->GetHotnessInfo(method_idx));
1400 if (startup ? hotness_info.IsStartup() : hotness_info.IsPostStartup()) {
1401 os << method_idx << ", ";
1402 }
1403 }
1404 if (startup == false) {
1405 break;
1406 }
1407 startup = false;
1408 }
1409 os << "\n\tclasses: ";
1410 for (const auto class_it : dex_data->class_set) {
1411 if (dex_file != nullptr) {
1412 os << "\n\t\t" << dex_file->PrettyType(class_it);
1413 } else {
1414 os << class_it.index_ << ",";
1415 }
1416 }
1417 }
1418 return os.str();
1419 }
1420
GetClassesAndMethods(const DexFile & dex_file,std::set<dex::TypeIndex> * class_set,std::set<uint16_t> * hot_method_set,std::set<uint16_t> * startup_method_set,std::set<uint16_t> * post_startup_method_method_set) const1421 bool ProfileCompilationInfo::GetClassesAndMethods(
1422 const DexFile& dex_file,
1423 /*out*/std::set<dex::TypeIndex>* class_set,
1424 /*out*/std::set<uint16_t>* hot_method_set,
1425 /*out*/std::set<uint16_t>* startup_method_set,
1426 /*out*/std::set<uint16_t>* post_startup_method_method_set) const {
1427 std::set<std::string> ret;
1428 const DexFileData* dex_data = FindDexData(&dex_file);
1429 if (dex_data == nullptr) {
1430 return false;
1431 }
1432 for (const auto& it : dex_data->method_map) {
1433 hot_method_set->insert(it.first);
1434 }
1435 for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) {
1436 MethodHotness hotness = dex_data->GetHotnessInfo(method_idx);
1437 if (hotness.IsStartup()) {
1438 startup_method_set->insert(method_idx);
1439 }
1440 if (hotness.IsPostStartup()) {
1441 post_startup_method_method_set->insert(method_idx);
1442 }
1443 }
1444 for (const dex::TypeIndex& type_index : dex_data->class_set) {
1445 class_set->insert(type_index);
1446 }
1447 return true;
1448 }
1449
Equals(const ProfileCompilationInfo & other)1450 bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) {
1451 // No need to compare profile_key_map_. That's only a cache for fast search.
1452 // All the information is already in the info_ vector.
1453 if (info_.size() != other.info_.size()) {
1454 return false;
1455 }
1456 for (size_t i = 0; i < info_.size(); i++) {
1457 const DexFileData& dex_data = *info_[i];
1458 const DexFileData& other_dex_data = *other.info_[i];
1459 if (!(dex_data == other_dex_data)) {
1460 return false;
1461 }
1462 }
1463 return true;
1464 }
1465
GetResolvedClasses(const std::vector<const DexFile * > & dex_files) const1466 std::set<DexCacheResolvedClasses> ProfileCompilationInfo::GetResolvedClasses(
1467 const std::vector<const DexFile*>& dex_files) const {
1468 std::unordered_map<std::string, const DexFile* > key_to_dex_file;
1469 for (const DexFile* dex_file : dex_files) {
1470 key_to_dex_file.emplace(GetProfileDexFileKey(dex_file->GetLocation()), dex_file);
1471 }
1472 std::set<DexCacheResolvedClasses> ret;
1473 for (const DexFileData* dex_data : info_) {
1474 const auto it = key_to_dex_file.find(dex_data->profile_key);
1475 if (it != key_to_dex_file.end()) {
1476 const DexFile* dex_file = it->second;
1477 const std::string& dex_location = dex_file->GetLocation();
1478 if (dex_data->checksum != it->second->GetLocationChecksum()) {
1479 LOG(ERROR) << "Dex checksum mismatch when getting resolved classes from profile for "
1480 << "location " << dex_location << " (checksum=" << dex_file->GetLocationChecksum()
1481 << ", profile checksum=" << dex_data->checksum;
1482 return std::set<DexCacheResolvedClasses>();
1483 }
1484 DexCacheResolvedClasses classes(dex_location,
1485 dex_location,
1486 dex_data->checksum,
1487 dex_data->num_method_ids);
1488 classes.AddClasses(dex_data->class_set.begin(), dex_data->class_set.end());
1489 ret.insert(classes);
1490 }
1491 }
1492 return ret;
1493 }
1494
1495 // 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)1496 bool ProfileCompilationInfo::GenerateTestProfile(int fd,
1497 uint16_t number_of_dex_files,
1498 uint16_t method_ratio,
1499 uint16_t class_ratio,
1500 uint32_t random_seed) {
1501 const std::string base_dex_location = "base.apk";
1502 ProfileCompilationInfo info;
1503 // The limits are defined by the dex specification.
1504 const uint16_t max_method = std::numeric_limits<uint16_t>::max();
1505 const uint16_t max_classes = std::numeric_limits<uint16_t>::max();
1506 uint16_t number_of_methods = max_method * method_ratio / 100;
1507 uint16_t number_of_classes = max_classes * class_ratio / 100;
1508
1509 std::srand(random_seed);
1510
1511 // Make sure we generate more samples with a low index value.
1512 // This makes it more likely to hit valid method/class indices in small apps.
1513 const uint16_t kFavorFirstN = 10000;
1514 const uint16_t kFavorSplit = 2;
1515
1516 for (uint16_t i = 0; i < number_of_dex_files; i++) {
1517 std::string dex_location = DexFile::GetMultiDexLocation(i, base_dex_location.c_str());
1518 std::string profile_key = GetProfileDexFileKey(dex_location);
1519
1520 for (uint16_t m = 0; m < number_of_methods; m++) {
1521 uint16_t method_idx = rand() % max_method;
1522 if (m < (number_of_methods / kFavorSplit)) {
1523 method_idx %= kFavorFirstN;
1524 }
1525 info.AddMethodIndex(MethodHotness::kFlagHot,
1526 profile_key,
1527 /*method_idx*/ 0,
1528 method_idx,
1529 max_method);
1530 }
1531
1532 for (uint16_t c = 0; c < number_of_classes; c++) {
1533 uint16_t type_idx = rand() % max_classes;
1534 if (c < (number_of_classes / kFavorSplit)) {
1535 type_idx %= kFavorFirstN;
1536 }
1537 info.AddClassIndex(profile_key, 0, dex::TypeIndex(type_idx), max_method);
1538 }
1539 }
1540 return info.Save(fd);
1541 }
1542
1543 // 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)1544 bool ProfileCompilationInfo::GenerateTestProfile(
1545 int fd,
1546 std::vector<std::unique_ptr<const DexFile>>& dex_files,
1547 uint32_t random_seed) {
1548 std::srand(random_seed);
1549 ProfileCompilationInfo info;
1550 for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
1551 const std::string& location = dex_file->GetLocation();
1552 uint32_t checksum = dex_file->GetLocationChecksum();
1553 for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
1554 // Randomly add a class from the dex file (with 50% chance).
1555 if (std::rand() % 2 != 0) {
1556 info.AddClassIndex(location,
1557 checksum,
1558 dex_file->GetClassDef(i).class_idx_,
1559 dex_file->NumMethodIds());
1560 }
1561 }
1562 for (uint32_t i = 0; i < dex_file->NumMethodIds(); ++i) {
1563 // Randomly add a method from the dex file (with 50% chance).
1564 if (std::rand() % 2 != 0) {
1565 info.AddMethodIndex(MethodHotness::kFlagHot, MethodReference(dex_file.get(), i));
1566 }
1567 }
1568 }
1569 return info.Save(fd);
1570 }
1571
operator ==(const OfflineProfileMethodInfo & other) const1572 bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==(
1573 const OfflineProfileMethodInfo& other) const {
1574 if (inline_caches->size() != other.inline_caches->size()) {
1575 return false;
1576 }
1577
1578 // We can't use a simple equality test because we need to match the dex files
1579 // of the inline caches which might have different profile indexes.
1580 for (const auto& inline_cache_it : *inline_caches) {
1581 uint16_t dex_pc = inline_cache_it.first;
1582 const DexPcData dex_pc_data = inline_cache_it.second;
1583 const auto& other_it = other.inline_caches->find(dex_pc);
1584 if (other_it == other.inline_caches->end()) {
1585 return false;
1586 }
1587 const DexPcData& other_dex_pc_data = other_it->second;
1588 if (dex_pc_data.is_megamorphic != other_dex_pc_data.is_megamorphic ||
1589 dex_pc_data.is_missing_types != other_dex_pc_data.is_missing_types) {
1590 return false;
1591 }
1592 for (const ClassReference& class_ref : dex_pc_data.classes) {
1593 bool found = false;
1594 for (const ClassReference& other_class_ref : other_dex_pc_data.classes) {
1595 CHECK_LE(class_ref.dex_profile_index, dex_references.size());
1596 CHECK_LE(other_class_ref.dex_profile_index, other.dex_references.size());
1597 const DexReference& dex_ref = dex_references[class_ref.dex_profile_index];
1598 const DexReference& other_dex_ref = other.dex_references[other_class_ref.dex_profile_index];
1599 if (class_ref.type_index == other_class_ref.type_index &&
1600 dex_ref == other_dex_ref) {
1601 found = true;
1602 break;
1603 }
1604 }
1605 if (!found) {
1606 return false;
1607 }
1608 }
1609 }
1610 return true;
1611 }
1612
IsEmpty() const1613 bool ProfileCompilationInfo::IsEmpty() const {
1614 DCHECK_EQ(info_.empty(), profile_key_map_.empty());
1615 return info_.empty();
1616 }
1617
1618 ProfileCompilationInfo::InlineCacheMap*
FindOrAddMethod(uint16_t method_index)1619 ProfileCompilationInfo::DexFileData::FindOrAddMethod(uint16_t method_index) {
1620 return &(method_map.FindOrAdd(
1621 method_index,
1622 InlineCacheMap(std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile)))->second);
1623 }
1624
1625 // Mark a method as executed at least once.
AddMethod(MethodHotness::Flag flags,size_t index)1626 bool ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, size_t index) {
1627 if (index >= num_method_ids) {
1628 LOG(ERROR) << "Invalid method index " << index << ". num_method_ids=" << num_method_ids;
1629 return false;
1630 }
1631
1632 if ((flags & MethodHotness::kFlagStartup) != 0) {
1633 method_bitmap.StoreBit(MethodBitIndex(/*startup*/ true, index), /*value*/ true);
1634 }
1635 if ((flags & MethodHotness::kFlagPostStartup) != 0) {
1636 method_bitmap.StoreBit(MethodBitIndex(/*startup*/ false, index), /*value*/ true);
1637 }
1638 if ((flags & MethodHotness::kFlagHot) != 0) {
1639 method_map.FindOrAdd(
1640 index,
1641 InlineCacheMap(std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile)));
1642 }
1643 return true;
1644 }
1645
GetHotnessInfo(uint32_t dex_method_index) const1646 ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::DexFileData::GetHotnessInfo(
1647 uint32_t dex_method_index) const {
1648 MethodHotness ret;
1649 if (method_bitmap.LoadBit(MethodBitIndex(/*startup*/ true, dex_method_index))) {
1650 ret.AddFlag(MethodHotness::kFlagStartup);
1651 }
1652 if (method_bitmap.LoadBit(MethodBitIndex(/*startup*/ false, dex_method_index))) {
1653 ret.AddFlag(MethodHotness::kFlagPostStartup);
1654 }
1655 auto it = method_map.find(dex_method_index);
1656 if (it != method_map.end()) {
1657 ret.SetInlineCacheMap(&it->second);
1658 ret.AddFlag(MethodHotness::kFlagHot);
1659 }
1660 return ret;
1661 }
1662
1663 ProfileCompilationInfo::DexPcData*
FindOrAddDexPc(InlineCacheMap * inline_cache,uint32_t dex_pc)1664 ProfileCompilationInfo::FindOrAddDexPc(InlineCacheMap* inline_cache, uint32_t dex_pc) {
1665 return &(inline_cache->FindOrAdd(dex_pc, DexPcData(&arena_))->second);
1666 }
1667
GetClassDescriptors(const std::vector<const DexFile * > & dex_files)1668 std::unordered_set<std::string> ProfileCompilationInfo::GetClassDescriptors(
1669 const std::vector<const DexFile*>& dex_files) {
1670 std::unordered_set<std::string> ret;
1671 for (const DexFile* dex_file : dex_files) {
1672 const DexFileData* data = FindDexData(dex_file);
1673 if (data != nullptr) {
1674 for (dex::TypeIndex type_idx : data->class_set) {
1675 if (!dex_file->IsTypeIndexValid(type_idx)) {
1676 // Something went bad. The profile is probably corrupted. Abort and return an emtpy set.
1677 LOG(WARNING) << "Corrupted profile: invalid type index "
1678 << type_idx.index_ << " in dex " << dex_file->GetLocation();
1679 return std::unordered_set<std::string>();
1680 }
1681 const DexFile::TypeId& type_id = dex_file->GetTypeId(type_idx);
1682 ret.insert(dex_file->GetTypeDescriptor(type_id));
1683 }
1684 } else {
1685 VLOG(compiler) << "Failed to find profile data for " << dex_file->GetLocation();
1686 }
1687 }
1688 return ret;
1689 }
1690
1691 } // namespace art
1692