blob: 1eba3067181679521564e81173ea1ce1b40c5859 [file] [log] [blame]
Calin Juravle31f2c152015-10-23 17:56:15 +01001/*
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
Calin Juravle33083d62017-01-18 15:29:12 -080017#include "profile_compilation_info.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010018
Calin Juravle31f2c152015-10-23 17:56:15 +010019#include <sys/file.h>
20#include <sys/stat.h>
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070021#include <sys/types.h>
Calin Juravle31f2c152015-10-23 17:56:15 +010022#include <sys/uio.h>
Shubham Ajmera4d198e02017-05-12 17:45:29 +000023#include <unistd.h>
24#include <zlib.h>
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070025
26#include <cerrno>
27#include <climits>
28#include <cstdlib>
29#include <string>
30#include <vector>
Shubham Ajmeraafbbf182017-08-04 14:33:34 -070031#include <iostream>
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070032
33#include "android-base/file.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010034
Calin Juravlecc3171a2017-05-19 16:47:53 -070035#include "base/arena_allocator.h"
36#include "base/dumpable.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010037#include "base/mutex.h"
Calin Juravle877fd962016-01-05 14:29:29 +000038#include "base/scoped_flock.h"
Calin Juravle66f55232015-12-08 15:09:10 +000039#include "base/stl_util.h"
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -080040#include "base/systrace.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070041#include "base/time_utils.h"
Calin Juravle877fd962016-01-05 14:29:29 +000042#include "base/unix_file/fd_file.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010043#include "jit/profiling_info.h"
Calin Juravle877fd962016-01-05 14:29:29 +000044#include "os.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010045#include "safe_map.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080046#include "utils.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010047
48namespace art {
49
Calin Juravle64142952016-03-21 14:37:55 +000050const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' };
Shubham Ajmeraafbbf182017-08-04 14:33:34 -070051// Last profile version: merge profiles directly from the file without creating
52// profile_compilation_info object. All the profile line headers are now placed together
53// before corresponding method_encodings and class_ids.
54const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '1', '0', '\0' };
Calin Juravle64142952016-03-21 14:37:55 +000055
56static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
57
Calin Juravlec4588572016-06-08 14:24:13 +010058// Debug flag to ignore checksums when testing if a method or a class is present in the profile.
Calin Juravle7bcdb532016-06-07 16:14:47 +010059// Used to facilitate testing profile guided compilation across a large number of apps
Calin Juravlec4588572016-06-08 14:24:13 +010060// using the same test profile.
61static constexpr bool kDebugIgnoreChecksum = false;
62
Calin Juravle589e71e2017-03-03 16:05:05 -080063static constexpr uint8_t kIsMissingTypesEncoding = 6;
64static constexpr uint8_t kIsMegamorphicEncoding = 7;
Calin Juravle940eb0c2017-01-30 19:30:44 -080065
66static_assert(sizeof(InlineCache::kIndividualCacheSize) == sizeof(uint8_t),
67 "InlineCache::kIndividualCacheSize does not have the expect type size");
Calin Juravle589e71e2017-03-03 16:05:05 -080068static_assert(InlineCache::kIndividualCacheSize < kIsMegamorphicEncoding,
69 "InlineCache::kIndividualCacheSize is larger than expected");
70static_assert(InlineCache::kIndividualCacheSize < kIsMissingTypesEncoding,
Calin Juravle940eb0c2017-01-30 19:30:44 -080071 "InlineCache::kIndividualCacheSize is larger than expected");
72
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -070073static bool ChecksumMatch(uint32_t dex_file_checksum, uint32_t checksum) {
74 return kDebugIgnoreChecksum || dex_file_checksum == checksum;
75}
76
Calin Juravlecc3171a2017-05-19 16:47:53 -070077ProfileCompilationInfo::ProfileCompilationInfo(ArenaPool* custom_arena_pool)
Calin Juravlee6f87cc2017-05-24 17:41:05 -070078 : default_arena_pool_(),
79 arena_(custom_arena_pool),
80 info_(arena_.Adapter(kArenaAllocProfile)),
81 profile_key_map_(std::less<const std::string>(), arena_.Adapter(kArenaAllocProfile)) {
Calin Juravlecc3171a2017-05-19 16:47:53 -070082}
83
84ProfileCompilationInfo::ProfileCompilationInfo()
Calin Juravlee6f87cc2017-05-24 17:41:05 -070085 : default_arena_pool_(/*use_malloc*/true, /*low_4gb*/false, "ProfileCompilationInfo"),
86 arena_(&default_arena_pool_),
87 info_(arena_.Adapter(kArenaAllocProfile)),
88 profile_key_map_(std::less<const std::string>(), arena_.Adapter(kArenaAllocProfile)) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -070089}
90
91ProfileCompilationInfo::~ProfileCompilationInfo() {
Calin Juravlee6f87cc2017-05-24 17:41:05 -070092 VLOG(profiler) << Dumpable<MemStats>(arena_.GetMemStats());
Calin Juravle798ba162017-05-23 23:01:53 -070093 for (DexFileData* data : info_) {
94 delete data;
95 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -070096}
97
Calin Juravle940eb0c2017-01-30 19:30:44 -080098void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx,
99 const dex::TypeIndex& type_idx) {
Calin Juravle589e71e2017-03-03 16:05:05 -0800100 if (is_megamorphic || is_missing_types) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800101 return;
102 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700103
104 // Perform an explicit lookup for the type instead of directly emplacing the
105 // element. We do this because emplace() allocates the node before doing the
106 // lookup and if it then finds an identical element, it shall deallocate the
107 // node. For Arena allocations, that's essentially a leak.
108 ClassReference ref(dex_profile_idx, type_idx);
109 auto it = classes.find(ref);
110 if (it != classes.end()) {
111 // The type index exists.
112 return;
113 }
114
115 // Check if the adding the type will cause the cache to become megamorphic.
116 if (classes.size() + 1 >= InlineCache::kIndividualCacheSize) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800117 is_megamorphic = true;
118 classes.clear();
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700119 return;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800120 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700121
122 // The type does not exist and the inline cache will not be megamorphic.
123 classes.insert(ref);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800124}
125
Calin Juravle34900cc2016-02-05 16:19:19 +0000126// Transform the actual dex location into relative paths.
127// Note: this is OK because we don't store profiles of different apps into the same file.
128// Apps with split apks don't cause trouble because each split has a different name and will not
129// collide with other entries.
Calin Juravle31708b72016-02-05 19:44:05 +0000130std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_location) {
Calin Juravle34900cc2016-02-05 16:19:19 +0000131 DCHECK(!dex_location.empty());
132 size_t last_sep_index = dex_location.find_last_of('/');
133 if (last_sep_index == std::string::npos) {
134 return dex_location;
135 } else {
Calin Juravle31708b72016-02-05 19:44:05 +0000136 DCHECK(last_sep_index < dex_location.size());
137 return dex_location.substr(last_sep_index + 1);
Calin Juravle34900cc2016-02-05 16:19:19 +0000138 }
139}
140
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700141bool ProfileCompilationInfo::AddMethodIndex(MethodHotness::Flag flags, const MethodReference& ref) {
142 DexFileData* data = GetOrAddDexFileData(ref.dex_file);
143 if (data == nullptr) {
144 return false;
145 }
Calin Juravle1ad1e3f2017-09-19 18:20:37 -0700146 return data->AddMethod(flags, ref.index);
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700147}
148
149bool ProfileCompilationInfo::AddMethodIndex(MethodHotness::Flag flags,
150 const std::string& dex_location,
151 uint32_t checksum,
152 uint16_t method_idx,
153 uint32_t num_method_ids) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700154 DexFileData* data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location),
155 checksum,
156 num_method_ids);
157 if (data == nullptr) {
158 return false;
159 }
Calin Juravle1ad1e3f2017-09-19 18:20:37 -0700160 return data->AddMethod(flags, method_idx);
Mathieu Chartierea650f32017-05-24 12:04:13 -0700161}
162
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700163bool ProfileCompilationInfo::AddMethods(const std::vector<ProfileMethodInfo>& methods) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800164 for (const ProfileMethodInfo& method : methods) {
165 if (!AddMethod(method)) {
Calin Juravle67265462016-03-18 16:23:40 +0000166 return false;
167 }
168 }
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700169 return true;
170}
171
172bool ProfileCompilationInfo::AddClasses(const std::set<DexCacheResolvedClasses>& resolved_classes) {
Calin Juravle67265462016-03-18 16:23:40 +0000173 for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
174 if (!AddResolvedClasses(dex_cache)) {
175 return false;
176 }
177 }
178 return true;
179}
180
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700181bool ProfileCompilationInfo::MergeWith(const std::string& filename) {
182 std::string error;
183 int flags = O_RDONLY | O_NOFOLLOW | O_CLOEXEC;
184 ScopedFlock profile_file = LockedFile::Open(filename.c_str(), flags,
185 /*block*/false, &error);
186
187 if (profile_file.get() == nullptr) {
188 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
189 return false;
190 }
191
192 int fd = profile_file->Fd();
193
194 ProfileLoadSatus status = LoadInternal(fd, &error);
195 if (status == kProfileLoadSuccess) {
196 return true;
197 }
198
199 LOG(WARNING) << "Could not load profile data from file " << filename << ": " << error;
200 return false;
201}
202
Calin Juravledcab1902017-05-12 19:18:47 -0700203bool ProfileCompilationInfo::Load(const std::string& filename, bool clear_if_invalid) {
Calin Juravle67265462016-03-18 16:23:40 +0000204 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravle67265462016-03-18 16:23:40 +0000205 std::string error;
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700206
207 if (!IsEmpty()) {
208 return kProfileLoadWouldOverwiteData;
209 }
210
Calin Juravledf674c42017-04-27 19:30:16 -0700211 int flags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
212 // There's no need to fsync profile data right away. We get many chances
213 // to write it again in case something goes wrong. We can rely on a simple
214 // close(), no sync, and let to the kernel decide when to write to disk.
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100215 ScopedFlock profile_file = LockedFile::Open(filename.c_str(), flags,
216 /*block*/false, &error);
217
218 if (profile_file.get() == nullptr) {
Calin Juravle67265462016-03-18 16:23:40 +0000219 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
220 return false;
221 }
222
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100223 int fd = profile_file->Fd();
Calin Juravle67265462016-03-18 16:23:40 +0000224
Calin Juravledcab1902017-05-12 19:18:47 -0700225 ProfileLoadSatus status = LoadInternal(fd, &error);
Calin Juravle5d1bd0a2016-03-24 20:33:22 +0000226 if (status == kProfileLoadSuccess) {
Calin Juravledcab1902017-05-12 19:18:47 -0700227 return true;
228 }
229
230 if (clear_if_invalid &&
231 ((status == kProfileLoadVersionMismatch) || (status == kProfileLoadBadData))) {
232 LOG(WARNING) << "Clearing bad or obsolete profile data from file "
233 << filename << ": " << error;
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100234 if (profile_file->ClearContent()) {
Calin Juravledcab1902017-05-12 19:18:47 -0700235 return true;
Calin Juravle5d1bd0a2016-03-24 20:33:22 +0000236 } else {
Calin Juravledcab1902017-05-12 19:18:47 -0700237 PLOG(WARNING) << "Could not clear profile file: " << filename;
238 return false;
Calin Juravle5d1bd0a2016-03-24 20:33:22 +0000239 }
Calin Juravledcab1902017-05-12 19:18:47 -0700240 }
241
242 LOG(WARNING) << "Could not load profile data from file " << filename << ": " << error;
243 return false;
244}
245
246bool ProfileCompilationInfo::Save(const std::string& filename, uint64_t* bytes_written) {
247 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravledcab1902017-05-12 19:18:47 -0700248 std::string error;
249 int flags = O_WRONLY | O_NOFOLLOW | O_CLOEXEC;
250 // There's no need to fsync profile data right away. We get many chances
251 // to write it again in case something goes wrong. We can rely on a simple
252 // close(), no sync, and let to the kernel decide when to write to disk.
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100253 ScopedFlock profile_file = LockedFile::Open(filename.c_str(), flags,
254 /*block*/false, &error);
255 if (profile_file.get() == nullptr) {
Calin Juravledcab1902017-05-12 19:18:47 -0700256 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
Calin Juravle67265462016-03-18 16:23:40 +0000257 return false;
258 }
259
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100260 int fd = profile_file->Fd();
Calin Juravledcab1902017-05-12 19:18:47 -0700261
Calin Juravle5d1bd0a2016-03-24 20:33:22 +0000262 // We need to clear the data because we don't support appending to the profiles yet.
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100263 if (!profile_file->ClearContent()) {
Calin Juravle67265462016-03-18 16:23:40 +0000264 PLOG(WARNING) << "Could not clear profile file: " << filename;
265 return false;
266 }
267
268 // This doesn't need locking because we are trying to lock the file for exclusive
269 // access and fail immediately if we can't.
270 bool result = Save(fd);
271 if (result) {
Calin Juravledcab1902017-05-12 19:18:47 -0700272 int64_t size = GetFileSizeBytes(filename);
273 if (size != -1) {
274 VLOG(profiler)
275 << "Successfully saved profile info to " << filename << " Size: "
276 << size;
277 if (bytes_written != nullptr) {
278 *bytes_written = static_cast<uint64_t>(size);
279 }
Calin Juravle67265462016-03-18 16:23:40 +0000280 }
281 } else {
282 VLOG(profiler) << "Failed to save profile info to " << filename;
283 }
284 return result;
285}
286
Calin Juravle64142952016-03-21 14:37:55 +0000287// Returns true if all the bytes were successfully written to the file descriptor.
288static bool WriteBuffer(int fd, const uint8_t* buffer, size_t byte_count) {
289 while (byte_count > 0) {
290 int bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer, byte_count));
291 if (bytes_written == -1) {
Calin Juravle877fd962016-01-05 14:29:29 +0000292 return false;
293 }
Calin Juravle64142952016-03-21 14:37:55 +0000294 byte_count -= bytes_written; // Reduce the number of remaining bytes.
295 buffer += bytes_written; // Move the buffer forward.
296 }
Calin Juravle877fd962016-01-05 14:29:29 +0000297 return true;
Calin Juravle31f2c152015-10-23 17:56:15 +0100298}
299
Calin Juravle64142952016-03-21 14:37:55 +0000300// Add the string bytes to the buffer.
301static void AddStringToBuffer(std::vector<uint8_t>* buffer, const std::string& value) {
302 buffer->insert(buffer->end(), value.begin(), value.end());
303}
304
305// Insert each byte, from low to high into the buffer.
306template <typename T>
307static void AddUintToBuffer(std::vector<uint8_t>* buffer, T value) {
308 for (size_t i = 0; i < sizeof(T); i++) {
309 buffer->push_back((value >> (i * kBitsPerByte)) & 0xff);
310 }
311}
312
313static constexpr size_t kLineHeaderSize =
Calin Juravle940eb0c2017-01-30 19:30:44 -0800314 2 * sizeof(uint16_t) + // class_set.size + dex_location.size
Mathieu Chartierea650f32017-05-24 12:04:13 -0700315 3 * sizeof(uint32_t); // method_map.size + checksum + num_method_ids
Calin Juravle31f2c152015-10-23 17:56:15 +0100316
317/**
318 * Serialization format:
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700319 * [profile_header, zipped[[profile_line_header1, profile_line_header2...],[profile_line_data1,
320 * profile_line_data2...]]]
321 * profile_header:
322 * magic,version,number_of_dex_files,uncompressed_size_of_zipped_data,compressed_data_size
323 * profile_line_header:
324 * dex_location,number_of_classes,methods_region_size,dex_location_checksum,num_method_ids
325 * profile_line_data:
326 * method_encoding_1,method_encoding_2...,class_id1,class_id2...,startup/post startup bitmap
Calin Juravle940eb0c2017-01-30 19:30:44 -0800327 * The method_encoding is:
328 * method_id,number_of_inline_caches,inline_cache1,inline_cache2...
329 * The inline_cache is:
330 * dex_pc,[M|dex_map_size], dex_profile_index,class_id1,class_id2...,dex_profile_index2,...
331 * dex_map_size is the number of dex_indeces that follows.
332 * Classes are grouped per their dex files and the line
333 * `dex_profile_index,class_id1,class_id2...,dex_profile_index2,...` encodes the
334 * mapping from `dex_profile_index` to the set of classes `class_id1,class_id2...`
Calin Juravle589e71e2017-03-03 16:05:05 -0800335 * M stands for megamorphic or missing types and it's encoded as either
336 * the byte kIsMegamorphicEncoding or kIsMissingTypesEncoding.
Calin Juravle940eb0c2017-01-30 19:30:44 -0800337 * When present, there will be no class ids following.
Calin Juravle31f2c152015-10-23 17:56:15 +0100338 **/
Calin Juravle2e2db782016-02-23 12:00:03 +0000339bool ProfileCompilationInfo::Save(int fd) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000340 uint64_t start = NanoTime();
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -0800341 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravle2e2db782016-02-23 12:00:03 +0000342 DCHECK_GE(fd, 0);
Calin Juravle64142952016-03-21 14:37:55 +0000343
Calin Juravle64142952016-03-21 14:37:55 +0000344 // Use a vector wrapper to avoid keeping track of offsets when we add elements.
345 std::vector<uint8_t> buffer;
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000346 if (!WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic))) {
347 return false;
348 }
349 if (!WriteBuffer(fd, kProfileVersion, sizeof(kProfileVersion))) {
350 return false;
351 }
Calin Juravle940eb0c2017-01-30 19:30:44 -0800352 DCHECK_LE(info_.size(), std::numeric_limits<uint8_t>::max());
353 AddUintToBuffer(&buffer, static_cast<uint8_t>(info_.size()));
Calin Juravle64142952016-03-21 14:37:55 +0000354
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000355 uint32_t required_capacity = 0;
356 for (const DexFileData* dex_data_ptr : info_) {
357 const DexFileData& dex_data = *dex_data_ptr;
358 uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
359 required_capacity += kLineHeaderSize +
360 dex_data.profile_key.size() +
361 sizeof(uint16_t) * dex_data.class_set.size() +
Mathieu Chartierea650f32017-05-24 12:04:13 -0700362 methods_region_size +
363 dex_data.bitmap_storage.size();
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000364 }
Mathieu Chartierf2e2af82017-07-12 21:09:57 -0700365 // Allow large profiles for non target builds for the case where we are merging many profiles
366 // to generate a boot image profile.
367 if (kIsTargetBuild && required_capacity > kProfileSizeErrorThresholdInBytes) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000368 LOG(ERROR) << "Profile data size exceeds "
369 << std::to_string(kProfileSizeErrorThresholdInBytes)
370 << " bytes. Profile will not be written to disk.";
371 return false;
372 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000373 AddUintToBuffer(&buffer, required_capacity);
374 if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
375 return false;
376 }
377 // Make sure that the buffer has enough capacity to avoid repeated resizings
378 // while we add data.
379 buffer.reserve(required_capacity);
380 buffer.clear();
381
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700382 // Dex files must be written in the order of their profile index. This
Calin Juravlee0ac1152017-02-13 19:03:47 -0800383 // avoids writing the index in the output file and simplifies the parsing logic.
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700384 // Write profile line headers.
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700385 for (const DexFileData* dex_data_ptr : info_) {
386 const DexFileData& dex_data = *dex_data_ptr;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800387
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700388 if (dex_data.profile_key.size() >= kMaxDexFileKeyLength) {
Calin Juravle64142952016-03-21 14:37:55 +0000389 LOG(WARNING) << "DexFileKey exceeds allocated limit";
390 return false;
391 }
392
Calin Juravle940eb0c2017-01-30 19:30:44 -0800393 uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
Calin Juravle64142952016-03-21 14:37:55 +0000394
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700395 DCHECK_LE(dex_data.profile_key.size(), std::numeric_limits<uint16_t>::max());
Calin Juravle64142952016-03-21 14:37:55 +0000396 DCHECK_LE(dex_data.class_set.size(), std::numeric_limits<uint16_t>::max());
Mathieu Chartierea650f32017-05-24 12:04:13 -0700397 // Write profile line header.
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700398 AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.profile_key.size()));
Calin Juravle64142952016-03-21 14:37:55 +0000399 AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.class_set.size()));
Calin Juravle940eb0c2017-01-30 19:30:44 -0800400 AddUintToBuffer(&buffer, methods_region_size); // uint32_t
Calin Juravle64142952016-03-21 14:37:55 +0000401 AddUintToBuffer(&buffer, dex_data.checksum); // uint32_t
Mathieu Chartierea650f32017-05-24 12:04:13 -0700402 AddUintToBuffer(&buffer, dex_data.num_method_ids); // uint32_t
Calin Juravle64142952016-03-21 14:37:55 +0000403
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700404 AddStringToBuffer(&buffer, dex_data.profile_key);
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700405 }
406
407 for (const DexFileData* dex_data_ptr : info_) {
408 const DexFileData& dex_data = *dex_data_ptr;
409
410 // Note that we allow dex files without any methods or classes, so that
411 // inline caches can refer valid dex files.
Calin Juravle64142952016-03-21 14:37:55 +0000412
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000413 uint16_t last_method_index = 0;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800414 for (const auto& method_it : dex_data.method_map) {
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000415 // Store the difference between the method indices. The SafeMap is ordered by
416 // method_id, so the difference will always be non negative.
417 DCHECK_GE(method_it.first, last_method_index);
418 uint16_t diff_with_last_method_index = method_it.first - last_method_index;
419 last_method_index = method_it.first;
420 AddUintToBuffer(&buffer, diff_with_last_method_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800421 AddInlineCacheToBuffer(&buffer, method_it.second);
Calin Juravle31f2c152015-10-23 17:56:15 +0100422 }
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000423
424 uint16_t last_class_index = 0;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800425 for (const auto& class_id : dex_data.class_set) {
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000426 // Store the difference between the class indices. The set is ordered by
427 // class_id, so the difference will always be non negative.
428 DCHECK_GE(class_id.index_, last_class_index);
429 uint16_t diff_with_last_class_index = class_id.index_ - last_class_index;
430 last_class_index = class_id.index_;
431 AddUintToBuffer(&buffer, diff_with_last_class_index);
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800432 }
Mathieu Chartierea650f32017-05-24 12:04:13 -0700433
434 buffer.insert(buffer.end(),
435 dex_data.bitmap_storage.begin(),
436 dex_data.bitmap_storage.end());
Calin Juravle31f2c152015-10-23 17:56:15 +0100437 }
438
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000439 uint32_t output_size = 0;
440 std::unique_ptr<uint8_t[]> compressed_buffer = DeflateBuffer(buffer.data(),
441 required_capacity,
442 &output_size);
443
Shubham Ajmerad704f0b2017-07-26 16:33:35 -0700444 if (output_size > kProfileSizeWarningThresholdInBytes) {
445 LOG(WARNING) << "Profile data size exceeds "
446 << std::to_string(kProfileSizeWarningThresholdInBytes);
447 }
448
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000449 buffer.clear();
450 AddUintToBuffer(&buffer, output_size);
451
452 if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
453 return false;
454 }
455 if (!WriteBuffer(fd, compressed_buffer.get(), output_size)) {
456 return false;
457 }
458 uint64_t total_time = NanoTime() - start;
459 VLOG(profiler) << "Compressed from "
460 << std::to_string(required_capacity)
461 << " to "
462 << std::to_string(output_size);
463 VLOG(profiler) << "Time to save profile: " << std::to_string(total_time);
464 return true;
Calin Juravle226501b2015-12-11 14:41:31 +0000465}
466
Calin Juravle940eb0c2017-01-30 19:30:44 -0800467void ProfileCompilationInfo::AddInlineCacheToBuffer(std::vector<uint8_t>* buffer,
468 const InlineCacheMap& inline_cache_map) {
469 // Add inline cache map size.
470 AddUintToBuffer(buffer, static_cast<uint16_t>(inline_cache_map.size()));
471 if (inline_cache_map.size() == 0) {
472 return;
473 }
474 for (const auto& inline_cache_it : inline_cache_map) {
475 uint16_t dex_pc = inline_cache_it.first;
476 const DexPcData dex_pc_data = inline_cache_it.second;
477 const ClassSet& classes = dex_pc_data.classes;
478
479 // Add the dex pc.
480 AddUintToBuffer(buffer, dex_pc);
481
Calin Juravle589e71e2017-03-03 16:05:05 -0800482 // Add the megamorphic/missing_types encoding if needed and continue.
483 // In either cases we don't add any classes to the profiles and so there's
484 // no point to continue.
485 // TODO(calin): in case we miss types there is still value to add the
486 // rest of the classes. They can be added without bumping the profile version.
487 if (dex_pc_data.is_missing_types) {
488 DCHECK(!dex_pc_data.is_megamorphic); // at this point the megamorphic flag should not be set.
489 DCHECK_EQ(classes.size(), 0u);
490 AddUintToBuffer(buffer, kIsMissingTypesEncoding);
491 continue;
492 } else if (dex_pc_data.is_megamorphic) {
493 DCHECK_EQ(classes.size(), 0u);
494 AddUintToBuffer(buffer, kIsMegamorphicEncoding);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800495 continue;
496 }
497
498 DCHECK_LT(classes.size(), InlineCache::kIndividualCacheSize);
499 DCHECK_NE(classes.size(), 0u) << "InlineCache contains a dex_pc with 0 classes";
500
501 SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
502 // Group the classes by dex. We expect that most of the classes will come from
503 // the same dex, so this will be more efficient than encoding the dex index
504 // for each class reference.
505 GroupClassesByDex(classes, &dex_to_classes_map);
506 // Add the dex map size.
507 AddUintToBuffer(buffer, static_cast<uint8_t>(dex_to_classes_map.size()));
508 for (const auto& dex_it : dex_to_classes_map) {
509 uint8_t dex_profile_index = dex_it.first;
510 const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
511 // Add the dex profile index.
512 AddUintToBuffer(buffer, dex_profile_index);
513 // Add the the number of classes for each dex profile index.
514 AddUintToBuffer(buffer, static_cast<uint8_t>(dex_classes.size()));
515 for (size_t i = 0; i < dex_classes.size(); i++) {
516 // Add the type index of the classes.
517 AddUintToBuffer(buffer, dex_classes[i].index_);
518 }
519 }
520 }
521}
522
523uint32_t ProfileCompilationInfo::GetMethodsRegionSize(const DexFileData& dex_data) {
524 // ((uint16_t)method index + (uint16_t)inline cache size) * number of methods
525 uint32_t size = 2 * sizeof(uint16_t) * dex_data.method_map.size();
526 for (const auto& method_it : dex_data.method_map) {
527 const InlineCacheMap& inline_cache = method_it.second;
528 size += sizeof(uint16_t) * inline_cache.size(); // dex_pc
529 for (const auto& inline_cache_it : inline_cache) {
530 const ClassSet& classes = inline_cache_it.second.classes;
531 SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
532 GroupClassesByDex(classes, &dex_to_classes_map);
533 size += sizeof(uint8_t); // dex_to_classes_map size
534 for (const auto& dex_it : dex_to_classes_map) {
535 size += sizeof(uint8_t); // dex profile index
536 size += sizeof(uint8_t); // number of classes
537 const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
538 size += sizeof(uint16_t) * dex_classes.size(); // the actual classes
539 }
540 }
541 }
542 return size;
543}
544
545void ProfileCompilationInfo::GroupClassesByDex(
546 const ClassSet& classes,
547 /*out*/SafeMap<uint8_t, std::vector<dex::TypeIndex>>* dex_to_classes_map) {
548 for (const auto& classes_it : classes) {
549 auto dex_it = dex_to_classes_map->FindOrAdd(classes_it.dex_profile_index);
550 dex_it->second.push_back(classes_it.type_index);
551 }
552}
553
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800554ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData(
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700555 const std::string& profile_key,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700556 uint32_t checksum,
557 uint32_t num_method_ids) {
Vladimir Marko3bada4b2017-05-19 12:32:47 +0100558 const auto profile_index_it = profile_key_map_.FindOrAdd(profile_key, profile_key_map_.size());
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700559 if (profile_key_map_.size() > std::numeric_limits<uint8_t>::max()) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800560 // Allow only 255 dex files to be profiled. This allows us to save bytes
561 // when encoding. The number is well above what we expect for normal applications.
562 if (kIsDebugBuild) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700563 LOG(ERROR) << "Exceeded the maximum number of dex files (255). Something went wrong";
Calin Juravle940eb0c2017-01-30 19:30:44 -0800564 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700565 profile_key_map_.erase(profile_key);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800566 return nullptr;
Calin Juravle998c2162015-12-21 15:39:33 +0200567 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700568
569 uint8_t profile_index = profile_index_it->second;
570 if (info_.size() <= profile_index) {
571 // This is a new addition. Add it to the info_ array.
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700572 DexFileData* dex_file_data = new (&arena_) DexFileData(
Mathieu Chartierea650f32017-05-24 12:04:13 -0700573 &arena_,
574 profile_key,
575 checksum,
576 profile_index,
577 num_method_ids);
Calin Juravlecc3171a2017-05-19 16:47:53 -0700578 info_.push_back(dex_file_data);
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700579 }
580 DexFileData* result = info_[profile_index];
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700581
582 // Check that the checksum matches.
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700583 // This may different if for example the dex file was updated and we had a record of the old one.
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700584 if (result->checksum != checksum) {
585 LOG(WARNING) << "Checksum mismatch for dex " << profile_key;
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800586 return nullptr;
587 }
Shubham Ajmera61200a02017-08-30 16:29:41 -0700588
589 // DCHECK that profile info map key is consistent with the one stored in the dex file data.
590 // This should always be the case since since the cache map is managed by ProfileCompilationInfo.
591 DCHECK_EQ(profile_key, result->profile_key);
592 DCHECK_EQ(profile_index, result->profile_index);
Calin Juravle1ad1e3f2017-09-19 18:20:37 -0700593
594 if (num_method_ids != result->num_method_ids) {
595 // This should not happen... added to help investigating b/65812889.
596 LOG(ERROR) << "num_method_ids mismatch for dex " << profile_key
597 << ", expected=" << num_method_ids
598 << ", actual=" << result->num_method_ids;
599 return nullptr;
600 }
Shubham Ajmera61200a02017-08-30 16:29:41 -0700601
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700602 return result;
603}
604
605const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData(
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700606 const std::string& profile_key,
607 uint32_t checksum,
608 bool verify_checksum) const {
Vladimir Marko3bada4b2017-05-19 12:32:47 +0100609 const auto profile_index_it = profile_key_map_.find(profile_key);
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700610 if (profile_index_it == profile_key_map_.end()) {
611 return nullptr;
612 }
613
614 uint8_t profile_index = profile_index_it->second;
615 const DexFileData* result = info_[profile_index];
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700616 if (verify_checksum && !ChecksumMatch(result->checksum, checksum)) {
617 return nullptr;
618 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700619 DCHECK_EQ(profile_key, result->profile_key);
620 DCHECK_EQ(profile_index, result->profile_index);
621 return result;
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800622}
623
624bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& classes) {
625 const std::string dex_location = GetProfileDexFileKey(classes.GetDexLocation());
626 const uint32_t checksum = classes.GetLocationChecksum();
Mathieu Chartierea650f32017-05-24 12:04:13 -0700627 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum, classes.NumMethodIds());
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800628 if (data == nullptr) {
Calin Juravle998c2162015-12-21 15:39:33 +0200629 return false;
630 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800631 data->class_set.insert(classes.GetClasses().begin(), classes.GetClasses().end());
632 return true;
633}
634
Calin Juravle940eb0c2017-01-30 19:30:44 -0800635bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
636 uint32_t dex_checksum,
637 uint16_t method_index,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700638 uint32_t num_method_ids,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800639 const OfflineProfileMethodInfo& pmi) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700640 DexFileData* const data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location),
641 dex_checksum,
642 num_method_ids);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800643 if (data == nullptr) { // checksum mismatch
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800644 return false;
645 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700646 // Add the method.
Calin Juravlecc3171a2017-05-19 16:47:53 -0700647 InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700648
649 if (pmi.inline_caches == nullptr) {
650 // If we don't have inline caches return success right away.
651 return true;
652 }
653 for (const auto& pmi_inline_cache_it : *pmi.inline_caches) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800654 uint16_t pmi_ic_dex_pc = pmi_inline_cache_it.first;
655 const DexPcData& pmi_ic_dex_pc_data = pmi_inline_cache_it.second;
Calin Juravlecc3171a2017-05-19 16:47:53 -0700656 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, pmi_ic_dex_pc);
657 if (dex_pc_data->is_missing_types || dex_pc_data->is_megamorphic) {
Calin Juravle589e71e2017-03-03 16:05:05 -0800658 // We are already megamorphic or we are missing types; no point in going forward.
Calin Juravle940eb0c2017-01-30 19:30:44 -0800659 continue;
660 }
Calin Juravle589e71e2017-03-03 16:05:05 -0800661
662 if (pmi_ic_dex_pc_data.is_missing_types) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700663 dex_pc_data->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -0800664 continue;
665 }
666 if (pmi_ic_dex_pc_data.is_megamorphic) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700667 dex_pc_data->SetIsMegamorphic();
Calin Juravle589e71e2017-03-03 16:05:05 -0800668 continue;
669 }
670
Calin Juravle940eb0c2017-01-30 19:30:44 -0800671 for (const ClassReference& class_ref : pmi_ic_dex_pc_data.classes) {
672 const DexReference& dex_ref = pmi.dex_references[class_ref.dex_profile_index];
673 DexFileData* class_dex_data = GetOrAddDexFileData(
674 GetProfileDexFileKey(dex_ref.dex_location),
Mathieu Chartierea650f32017-05-24 12:04:13 -0700675 dex_ref.dex_checksum,
676 dex_ref.num_method_ids);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800677 if (class_dex_data == nullptr) { // checksum mismatch
678 return false;
679 }
Calin Juravlecc3171a2017-05-19 16:47:53 -0700680 dex_pc_data->AddClass(class_dex_data->profile_index, class_ref.type_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800681 }
682 }
683 return true;
684}
685
686bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700687 DexFileData* const data = GetOrAddDexFileData(pmi.ref.dex_file);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800688 if (data == nullptr) { // checksum mismatch
689 return false;
690 }
Mathieu Chartierfc8b4222017-09-17 13:44:24 -0700691 InlineCacheMap* inline_cache = data->FindOrAddMethod(pmi.ref.index);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800692
693 for (const ProfileMethodInfo::ProfileInlineCache& cache : pmi.inline_caches) {
Calin Juravle589e71e2017-03-03 16:05:05 -0800694 if (cache.is_missing_types) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700695 FindOrAddDexPc(inline_cache, cache.dex_pc)->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -0800696 continue;
697 }
Mathieu Chartierdbddc222017-05-24 12:04:13 -0700698 for (const TypeReference& class_ref : cache.classes) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700699 DexFileData* class_dex_data = GetOrAddDexFileData(class_ref.dex_file);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800700 if (class_dex_data == nullptr) { // checksum mismatch
701 return false;
702 }
Calin Juravlecc3171a2017-05-19 16:47:53 -0700703 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, cache.dex_pc);
704 if (dex_pc_data->is_missing_types) {
Calin Juravle589e71e2017-03-03 16:05:05 -0800705 // Don't bother adding classes if we are missing types.
706 break;
707 }
Mathieu Chartierfc8b4222017-09-17 13:44:24 -0700708 dex_pc_data->AddClass(class_dex_data->profile_index, class_ref.TypeIndex());
Calin Juravle940eb0c2017-01-30 19:30:44 -0800709 }
710 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800711 return true;
712}
713
714bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location,
715 uint32_t checksum,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700716 dex::TypeIndex type_idx,
717 uint32_t num_method_ids) {
718 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum, num_method_ids);
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800719 if (data == nullptr) {
720 return false;
721 }
Jeff Hao54b58552016-11-16 15:15:04 -0800722 data->class_set.insert(type_idx);
Calin Juravle998c2162015-12-21 15:39:33 +0200723 return true;
724}
725
Andreas Gampe37c58462017-03-27 15:14:27 -0700726#define READ_UINT(type, buffer, dest, error) \
727 do { \
728 if (!(buffer).ReadUintAndAdvance<type>(&(dest))) { \
729 *(error) = "Could not read "#dest; \
730 return false; \
731 } \
732 } \
Calin Juravle940eb0c2017-01-30 19:30:44 -0800733 while (false)
734
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700735bool ProfileCompilationInfo::ReadInlineCache(
736 SafeBuffer& buffer,
737 uint8_t number_of_dex_files,
738 const SafeMap<uint8_t, uint8_t>& dex_profile_index_remap,
739 /*out*/ InlineCacheMap* inline_cache,
740 /*out*/ std::string* error) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800741 uint16_t inline_cache_size;
742 READ_UINT(uint16_t, buffer, inline_cache_size, error);
743 for (; inline_cache_size > 0; inline_cache_size--) {
744 uint16_t dex_pc;
745 uint8_t dex_to_classes_map_size;
746 READ_UINT(uint16_t, buffer, dex_pc, error);
747 READ_UINT(uint8_t, buffer, dex_to_classes_map_size, error);
Calin Juravlecc3171a2017-05-19 16:47:53 -0700748 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, dex_pc);
Calin Juravle589e71e2017-03-03 16:05:05 -0800749 if (dex_to_classes_map_size == kIsMissingTypesEncoding) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700750 dex_pc_data->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -0800751 continue;
752 }
753 if (dex_to_classes_map_size == kIsMegamorphicEncoding) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700754 dex_pc_data->SetIsMegamorphic();
Calin Juravle940eb0c2017-01-30 19:30:44 -0800755 continue;
756 }
757 for (; dex_to_classes_map_size > 0; dex_to_classes_map_size--) {
758 uint8_t dex_profile_index;
759 uint8_t dex_classes_size;
760 READ_UINT(uint8_t, buffer, dex_profile_index, error);
761 READ_UINT(uint8_t, buffer, dex_classes_size, error);
762 if (dex_profile_index >= number_of_dex_files) {
763 *error = "dex_profile_index out of bounds ";
764 *error += std::to_string(dex_profile_index) + " " + std::to_string(number_of_dex_files);
765 return false;
766 }
767 for (; dex_classes_size > 0; dex_classes_size--) {
768 uint16_t type_index;
769 READ_UINT(uint16_t, buffer, type_index, error);
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700770 dex_pc_data->AddClass(dex_profile_index_remap.Get(dex_profile_index),
771 dex::TypeIndex(type_index));
Calin Juravle940eb0c2017-01-30 19:30:44 -0800772 }
773 }
774 }
775 return true;
776}
777
778bool ProfileCompilationInfo::ReadMethods(SafeBuffer& buffer,
779 uint8_t number_of_dex_files,
780 const ProfileLineHeader& line_header,
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700781 const SafeMap<uint8_t, uint8_t>& dex_profile_index_remap,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800782 /*out*/std::string* error) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000783 uint32_t unread_bytes_before_operation = buffer.CountUnreadBytes();
784 if (unread_bytes_before_operation < line_header.method_region_size_bytes) {
785 *error += "Profile EOF reached prematurely for ReadMethod";
786 return kProfileLoadBadData;
787 }
788 size_t expected_unread_bytes_after_operation = buffer.CountUnreadBytes()
789 - line_header.method_region_size_bytes;
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000790 uint16_t last_method_index = 0;
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000791 while (buffer.CountUnreadBytes() > expected_unread_bytes_after_operation) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700792 DexFileData* const data = GetOrAddDexFileData(line_header.dex_location,
793 line_header.checksum,
794 line_header.num_method_ids);
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000795 uint16_t diff_with_last_method_index;
796 READ_UINT(uint16_t, buffer, diff_with_last_method_index, error);
797 uint16_t method_index = last_method_index + diff_with_last_method_index;
798 last_method_index = method_index;
Calin Juravlecc3171a2017-05-19 16:47:53 -0700799 InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700800 if (!ReadInlineCache(buffer,
801 number_of_dex_files,
802 dex_profile_index_remap,
803 inline_cache,
804 error)) {
Calin Juravle877fd962016-01-05 14:29:29 +0000805 return false;
806 }
Calin Juravle226501b2015-12-11 14:41:31 +0000807 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000808 uint32_t total_bytes_read = unread_bytes_before_operation - buffer.CountUnreadBytes();
809 if (total_bytes_read != line_header.method_region_size_bytes) {
810 *error += "Profile data inconsistent for ReadMethods";
811 return false;
812 }
Calin Juravle940eb0c2017-01-30 19:30:44 -0800813 return true;
814}
815
816bool ProfileCompilationInfo::ReadClasses(SafeBuffer& buffer,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800817 const ProfileLineHeader& line_header,
818 /*out*/std::string* error) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000819 size_t unread_bytes_before_op = buffer.CountUnreadBytes();
820 if (unread_bytes_before_op < line_header.class_set_size) {
821 *error += "Profile EOF reached prematurely for ReadClasses";
822 return kProfileLoadBadData;
823 }
824
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000825 uint16_t last_class_index = 0;
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000826 for (uint16_t i = 0; i < line_header.class_set_size; i++) {
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000827 uint16_t diff_with_last_class_index;
828 READ_UINT(uint16_t, buffer, diff_with_last_class_index, error);
829 uint16_t type_index = last_class_index + diff_with_last_class_index;
830 last_class_index = type_index;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800831 if (!AddClassIndex(line_header.dex_location,
832 line_header.checksum,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700833 dex::TypeIndex(type_index),
834 line_header.num_method_ids)) {
Calin Juravle64142952016-03-21 14:37:55 +0000835 return false;
836 }
837 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000838 size_t total_bytes_read = unread_bytes_before_op - buffer.CountUnreadBytes();
839 uint32_t expected_bytes_read = line_header.class_set_size * sizeof(uint16_t);
840 if (total_bytes_read != expected_bytes_read) {
841 *error += "Profile data inconsistent for ReadClasses";
842 return false;
843 }
Calin Juravle226501b2015-12-11 14:41:31 +0000844 return true;
845}
846
Calin Juravle64142952016-03-21 14:37:55 +0000847// Tests for EOF by trying to read 1 byte from the descriptor.
848// Returns:
849// 0 if the descriptor is at the EOF,
850// -1 if there was an IO error
851// 1 if the descriptor has more content to read
852static int testEOF(int fd) {
853 uint8_t buffer[1];
854 return TEMP_FAILURE_RETRY(read(fd, buffer, 1));
855}
856
857// Reads an uint value previously written with AddUintToBuffer.
858template <typename T>
Calin Juravle940eb0c2017-01-30 19:30:44 -0800859bool ProfileCompilationInfo::SafeBuffer::ReadUintAndAdvance(/*out*/T* value) {
Calin Juravle64142952016-03-21 14:37:55 +0000860 static_assert(std::is_unsigned<T>::value, "Type is not unsigned");
Calin Juravle940eb0c2017-01-30 19:30:44 -0800861 if (ptr_current_ + sizeof(T) > ptr_end_) {
862 return false;
863 }
864 *value = 0;
Calin Juravle64142952016-03-21 14:37:55 +0000865 for (size_t i = 0; i < sizeof(T); i++) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800866 *value += ptr_current_[i] << (i * kBitsPerByte);
Calin Juravle226501b2015-12-11 14:41:31 +0000867 }
Calin Juravle64142952016-03-21 14:37:55 +0000868 ptr_current_ += sizeof(T);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800869 return true;
Calin Juravle64142952016-03-21 14:37:55 +0000870}
871
872bool ProfileCompilationInfo::SafeBuffer::CompareAndAdvance(const uint8_t* data, size_t data_size) {
873 if (ptr_current_ + data_size > ptr_end_) {
874 return false;
875 }
876 if (memcmp(ptr_current_, data, data_size) == 0) {
877 ptr_current_ += data_size;
878 return true;
879 }
880 return false;
881}
882
883ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::SafeBuffer::FillFromFd(
884 int fd,
885 const std::string& source,
886 /*out*/std::string* error) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000887 size_t byte_count = (ptr_end_ - ptr_current_) * sizeof(*ptr_current_);
Calin Juravle64142952016-03-21 14:37:55 +0000888 uint8_t* buffer = ptr_current_;
889 while (byte_count > 0) {
890 int bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, byte_count));
891 if (bytes_read == 0) {
892 *error += "Profile EOF reached prematurely for " + source;
893 return kProfileLoadBadData;
894 } else if (bytes_read < 0) {
895 *error += "Profile IO error for " + source + strerror(errno);
896 return kProfileLoadIOError;
Calin Juravle226501b2015-12-11 14:41:31 +0000897 }
Calin Juravle64142952016-03-21 14:37:55 +0000898 byte_count -= bytes_read;
899 buffer += bytes_read;
Calin Juravle226501b2015-12-11 14:41:31 +0000900 }
Calin Juravle64142952016-03-21 14:37:55 +0000901 return kProfileLoadSuccess;
902}
903
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000904size_t ProfileCompilationInfo::SafeBuffer::CountUnreadBytes() {
905 return (ptr_end_ - ptr_current_) * sizeof(*ptr_current_);
906}
907
908const uint8_t* ProfileCompilationInfo::SafeBuffer::GetCurrentPtr() {
909 return ptr_current_;
910}
911
912void ProfileCompilationInfo::SafeBuffer::Advance(size_t data_size) {
913 ptr_current_ += data_size;
914}
915
Calin Juravle64142952016-03-21 14:37:55 +0000916ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileHeader(
917 int fd,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800918 /*out*/uint8_t* number_of_dex_files,
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000919 /*out*/uint32_t* uncompressed_data_size,
920 /*out*/uint32_t* compressed_data_size,
Calin Juravle64142952016-03-21 14:37:55 +0000921 /*out*/std::string* error) {
922 // Read magic and version
923 const size_t kMagicVersionSize =
924 sizeof(kProfileMagic) +
925 sizeof(kProfileVersion) +
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000926 sizeof(uint8_t) + // number of dex files
927 sizeof(uint32_t) + // size of uncompressed profile data
928 sizeof(uint32_t); // size of compressed profile data
Calin Juravle64142952016-03-21 14:37:55 +0000929
930 SafeBuffer safe_buffer(kMagicVersionSize);
931
932 ProfileLoadSatus status = safe_buffer.FillFromFd(fd, "ReadProfileHeader", error);
933 if (status != kProfileLoadSuccess) {
934 return status;
935 }
936
937 if (!safe_buffer.CompareAndAdvance(kProfileMagic, sizeof(kProfileMagic))) {
938 *error = "Profile missing magic";
939 return kProfileLoadVersionMismatch;
940 }
941 if (!safe_buffer.CompareAndAdvance(kProfileVersion, sizeof(kProfileVersion))) {
942 *error = "Profile version mismatch";
943 return kProfileLoadVersionMismatch;
944 }
Calin Juravle940eb0c2017-01-30 19:30:44 -0800945 if (!safe_buffer.ReadUintAndAdvance<uint8_t>(number_of_dex_files)) {
946 *error = "Cannot read the number of dex files";
947 return kProfileLoadBadData;
948 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000949 if (!safe_buffer.ReadUintAndAdvance<uint32_t>(uncompressed_data_size)) {
950 *error = "Cannot read the size of uncompressed data";
951 return kProfileLoadBadData;
952 }
953 if (!safe_buffer.ReadUintAndAdvance<uint32_t>(compressed_data_size)) {
954 *error = "Cannot read the size of compressed data";
955 return kProfileLoadBadData;
956 }
Calin Juravle64142952016-03-21 14:37:55 +0000957 return kProfileLoadSuccess;
958}
959
Calin Juravle940eb0c2017-01-30 19:30:44 -0800960bool ProfileCompilationInfo::ReadProfileLineHeaderElements(SafeBuffer& buffer,
961 /*out*/uint16_t* dex_location_size,
962 /*out*/ProfileLineHeader* line_header,
963 /*out*/std::string* error) {
964 READ_UINT(uint16_t, buffer, *dex_location_size, error);
965 READ_UINT(uint16_t, buffer, line_header->class_set_size, error);
966 READ_UINT(uint32_t, buffer, line_header->method_region_size_bytes, error);
967 READ_UINT(uint32_t, buffer, line_header->checksum, error);
Mathieu Chartierea650f32017-05-24 12:04:13 -0700968 READ_UINT(uint32_t, buffer, line_header->num_method_ids, error);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800969 return true;
970}
971
Calin Juravle64142952016-03-21 14:37:55 +0000972ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLineHeader(
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000973 SafeBuffer& buffer,
974 /*out*/ProfileLineHeader* line_header,
975 /*out*/std::string* error) {
976 if (buffer.CountUnreadBytes() < kLineHeaderSize) {
977 *error += "Profile EOF reached prematurely for ReadProfileLineHeader";
978 return kProfileLoadBadData;
Calin Juravle64142952016-03-21 14:37:55 +0000979 }
980
Calin Juravle940eb0c2017-01-30 19:30:44 -0800981 uint16_t dex_location_size;
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000982 if (!ReadProfileLineHeaderElements(buffer, &dex_location_size, line_header, error)) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800983 return kProfileLoadBadData;
984 }
Calin Juravle64142952016-03-21 14:37:55 +0000985
986 if (dex_location_size == 0 || dex_location_size > kMaxDexFileKeyLength) {
Goran Jakovljevic4eb6fbf2016-04-25 19:14:17 +0200987 *error = "DexFileKey has an invalid size: " +
988 std::to_string(static_cast<uint32_t>(dex_location_size));
Calin Juravle64142952016-03-21 14:37:55 +0000989 return kProfileLoadBadData;
990 }
991
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000992 if (buffer.CountUnreadBytes() < dex_location_size) {
993 *error += "Profile EOF reached prematurely for ReadProfileHeaderDexLocation";
994 return kProfileLoadBadData;
Calin Juravle64142952016-03-21 14:37:55 +0000995 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000996 const uint8_t* base_ptr = buffer.GetCurrentPtr();
Calin Juravle64142952016-03-21 14:37:55 +0000997 line_header->dex_location.assign(
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000998 reinterpret_cast<const char*>(base_ptr), dex_location_size);
999 buffer.Advance(dex_location_size);
Calin Juravle64142952016-03-21 14:37:55 +00001000 return kProfileLoadSuccess;
1001}
1002
1003ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine(
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001004 SafeBuffer& buffer,
Calin Juravle940eb0c2017-01-30 19:30:44 -08001005 uint8_t number_of_dex_files,
Calin Juravle64142952016-03-21 14:37:55 +00001006 const ProfileLineHeader& line_header,
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001007 const SafeMap<uint8_t, uint8_t>& dex_profile_index_remap,
1008 bool merge_classes,
Calin Juravle64142952016-03-21 14:37:55 +00001009 /*out*/std::string* error) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001010 DexFileData* data = GetOrAddDexFileData(line_header.dex_location,
1011 line_header.checksum,
1012 line_header.num_method_ids);
1013 if (data == nullptr) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001014 *error = "Error when reading profile file line header: checksum mismatch for "
1015 + line_header.dex_location;
1016 return kProfileLoadBadData;
1017 }
Calin Juravle64142952016-03-21 14:37:55 +00001018
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001019 if (!ReadMethods(buffer, number_of_dex_files, line_header, dex_profile_index_remap, error)) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001020 return kProfileLoadBadData;
Calin Juravle64142952016-03-21 14:37:55 +00001021 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001022
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001023 if (merge_classes) {
1024 if (!ReadClasses(buffer, line_header, error)) {
1025 return kProfileLoadBadData;
1026 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001027 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001028
1029 const size_t bytes = data->bitmap_storage.size();
1030 if (buffer.CountUnreadBytes() < bytes) {
1031 *error += "Profile EOF reached prematurely for ReadProfileHeaderDexLocation";
1032 return kProfileLoadBadData;
1033 }
1034 const uint8_t* base_ptr = buffer.GetCurrentPtr();
1035 std::copy_n(base_ptr, bytes, &data->bitmap_storage[0]);
1036 buffer.Advance(bytes);
1037 // Read method bitmap.
Calin Juravle64142952016-03-21 14:37:55 +00001038 return kProfileLoadSuccess;
Calin Juravle226501b2015-12-11 14:41:31 +00001039}
1040
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001041// TODO(calin): Fix this API. ProfileCompilationInfo::Load should be static and
1042// return a unique pointer to a ProfileCompilationInfo upon success.
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001043bool ProfileCompilationInfo::Load(int fd, bool merge_classes) {
Calin Juravle64142952016-03-21 14:37:55 +00001044 std::string error;
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001045
1046 ProfileLoadSatus status = LoadInternal(fd, &error, merge_classes);
Calin Juravle64142952016-03-21 14:37:55 +00001047
1048 if (status == kProfileLoadSuccess) {
1049 return true;
1050 } else {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001051 LOG(WARNING) << "Error when reading profile: " << error;
Calin Juravle64142952016-03-21 14:37:55 +00001052 return false;
1053 }
1054}
1055
Shubham Ajmera188b2bf2017-09-20 15:53:35 -07001056bool ProfileCompilationInfo::VerifyProfileData(const std::vector<const DexFile*>& dex_files) {
1057 std::unordered_map<std::string, const DexFile*> key_to_dex_file;
1058 for (const DexFile* dex_file : dex_files) {
1059 key_to_dex_file.emplace(GetProfileDexFileKey(dex_file->GetLocation()), dex_file);
1060 }
1061 for (const DexFileData* dex_data : info_) {
1062 const auto it = key_to_dex_file.find(dex_data->profile_key);
1063 if (it == key_to_dex_file.end()) {
1064 // It is okay if profile contains data for additional dex files.
1065 continue;
1066 }
1067 const DexFile* dex_file = it->second;
1068 const std::string& dex_location = dex_file->GetLocation();
1069 if (!ChecksumMatch(dex_data->checksum, dex_file->GetLocationChecksum())) {
1070 LOG(ERROR) << "Dex checksum mismatch while verifying profile "
1071 << "dex location " << dex_location << " (checksum="
1072 << dex_file->GetLocationChecksum() << ", profile checksum="
1073 << dex_data->checksum;
1074 return false;
1075 }
1076 // Verify method_encoding.
1077 for (const auto& method_it : dex_data->method_map) {
1078 size_t method_id = (size_t)(method_it.first);
1079 if (method_id >= dex_file->NumMethodIds()) {
1080 LOG(ERROR) << "Invalid method id in profile file. dex location="
1081 << dex_location << " method_id=" << method_id << " NumMethodIds="
1082 << dex_file->NumMethodIds();
1083 return false;
1084 }
1085
1086 // Verify class indices of inline caches.
1087 const InlineCacheMap &inline_cache_map = method_it.second;
1088 for (const auto& inline_cache_it : inline_cache_map) {
1089 const DexPcData dex_pc_data = inline_cache_it.second;
1090 if (dex_pc_data.is_missing_types || dex_pc_data.is_megamorphic) {
1091 // No class indices to verify.
1092 continue;
1093 }
1094
1095 const ClassSet &classes = dex_pc_data.classes;
1096 SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
1097 // Group the classes by dex. We expect that most of the classes will come from
1098 // the same dex, so this will be more efficient than encoding the dex index
1099 // for each class reference.
1100 GroupClassesByDex(classes, &dex_to_classes_map);
1101 for (const auto &dex_it : dex_to_classes_map) {
1102 uint8_t dex_profile_index = dex_it.first;
1103 const auto dex_file_inline_cache_it = key_to_dex_file.find(
1104 info_[dex_profile_index]->profile_key);
1105 if (dex_file_inline_cache_it == key_to_dex_file.end()) {
1106 // It is okay if profile contains data for additional dex files.
1107 continue;
1108 }
1109 const DexFile *dex_file_for_inline_cache_check = dex_file_inline_cache_it->second;
1110 const std::vector<dex::TypeIndex> &dex_classes = dex_it.second;
1111 for (size_t i = 0; i < dex_classes.size(); i++) {
1112 if (dex_classes[i].index_ >= dex_file_for_inline_cache_check->NumTypeIds()) {
1113 LOG(ERROR) << "Invalid inline cache in profile file. dex location="
1114 << dex_location << " method_id=" << method_id
1115 << " dex_profile_index="
1116 << static_cast<uint16_t >(dex_profile_index) << " type_index="
1117 << dex_classes[i].index_
1118 << " NumTypeIds="
1119 << dex_file_for_inline_cache_check->NumTypeIds();
1120 return false;
1121 }
1122 }
1123 }
1124 }
1125 }
1126 // Verify class_ids.
1127 for (const auto& class_id : dex_data->class_set) {
1128 if (class_id.index_ >= dex_file->NumTypeIds()) {
1129 LOG(ERROR) << "Invalid class id in profile file. dex_file location "
1130 << dex_location << " class_id=" << class_id.index_ << " NumClassIds="
1131 << dex_file->NumClassDefs();
1132 return false;
1133 }
1134 }
1135 }
1136 return true;
1137}
1138
Calin Juravledcab1902017-05-12 19:18:47 -07001139// TODO(calin): fail fast if the dex checksums don't match.
Calin Juravle64142952016-03-21 14:37:55 +00001140ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal(
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001141 int fd, std::string* error, bool merge_classes) {
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -08001142 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravle2e2db782016-02-23 12:00:03 +00001143 DCHECK_GE(fd, 0);
Calin Juravle226501b2015-12-11 14:41:31 +00001144
Calin Juravle64142952016-03-21 14:37:55 +00001145 struct stat stat_buffer;
1146 if (fstat(fd, &stat_buffer) != 0) {
1147 return kProfileLoadIOError;
Calin Juravle226501b2015-12-11 14:41:31 +00001148 }
Calin Juravle64142952016-03-21 14:37:55 +00001149 // We allow empty profile files.
1150 // Profiles may be created by ActivityManager or installd before we manage to
1151 // process them in the runtime or profman.
1152 if (stat_buffer.st_size == 0) {
1153 return kProfileLoadSuccess;
1154 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001155 // Read profile header: magic + version + number_of_dex_files.
1156 uint8_t number_of_dex_files;
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001157 uint32_t uncompressed_data_size;
1158 uint32_t compressed_data_size;
1159 ProfileLoadSatus status = ReadProfileHeader(fd,
1160 &number_of_dex_files,
1161 &uncompressed_data_size,
1162 &compressed_data_size,
1163 error);
1164
Calin Juravle64142952016-03-21 14:37:55 +00001165 if (status != kProfileLoadSuccess) {
1166 return status;
1167 }
Mathieu Chartierf2e2af82017-07-12 21:09:57 -07001168 // Allow large profiles for non target builds for the case where we are merging many profiles
1169 // to generate a boot image profile.
1170 if (kIsTargetBuild && uncompressed_data_size > kProfileSizeErrorThresholdInBytes) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001171 LOG(ERROR) << "Profile data size exceeds "
1172 << std::to_string(kProfileSizeErrorThresholdInBytes)
1173 << " bytes";
1174 return kProfileLoadBadData;
1175 }
1176 if (uncompressed_data_size > kProfileSizeWarningThresholdInBytes) {
1177 LOG(WARNING) << "Profile data size exceeds "
1178 << std::to_string(kProfileSizeWarningThresholdInBytes)
1179 << " bytes";
1180 }
1181
1182 std::unique_ptr<uint8_t[]> compressed_data(new uint8_t[compressed_data_size]);
1183 bool bytes_read_success =
1184 android::base::ReadFully(fd, compressed_data.get(), compressed_data_size);
1185
1186 if (testEOF(fd) != 0) {
1187 *error += "Unexpected data in the profile file.";
1188 return kProfileLoadBadData;
1189 }
1190
1191 if (!bytes_read_success) {
1192 *error += "Unable to read compressed profile data";
1193 return kProfileLoadBadData;
1194 }
1195
1196 SafeBuffer uncompressed_data(uncompressed_data_size);
1197
1198 int ret = InflateBuffer(compressed_data.get(),
1199 compressed_data_size,
1200 uncompressed_data_size,
1201 uncompressed_data.Get());
1202
1203 if (ret != Z_STREAM_END) {
1204 *error += "Error reading uncompressed profile data";
1205 return kProfileLoadBadData;
1206 }
1207
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001208 std::vector<ProfileLineHeader> profile_line_headers;
1209 // Read profile line headers.
Calin Juravle940eb0c2017-01-30 19:30:44 -08001210 for (uint8_t k = 0; k < number_of_dex_files; k++) {
Calin Juravle64142952016-03-21 14:37:55 +00001211 ProfileLineHeader line_header;
Calin Juravle940eb0c2017-01-30 19:30:44 -08001212
Calin Juravle64142952016-03-21 14:37:55 +00001213 // First, read the line header to get the amount of data we need to read.
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001214 status = ReadProfileLineHeader(uncompressed_data, &line_header, error);
Calin Juravle64142952016-03-21 14:37:55 +00001215 if (status != kProfileLoadSuccess) {
1216 return status;
1217 }
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001218 profile_line_headers.push_back(line_header);
1219 }
Calin Juravle64142952016-03-21 14:37:55 +00001220
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001221 SafeMap<uint8_t, uint8_t> dex_profile_index_remap;
1222 if (!RemapProfileIndex(profile_line_headers, &dex_profile_index_remap)) {
1223 return kProfileLoadBadData;
1224 }
1225
1226 for (uint8_t k = 0; k < number_of_dex_files; k++) {
Calin Juravle64142952016-03-21 14:37:55 +00001227 // Now read the actual profile line.
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001228 status = ReadProfileLine(uncompressed_data,
1229 number_of_dex_files,
1230 profile_line_headers[k],
1231 dex_profile_index_remap,
1232 merge_classes,
1233 error);
Calin Juravle64142952016-03-21 14:37:55 +00001234 if (status != kProfileLoadSuccess) {
1235 return status;
1236 }
Calin Juravle64142952016-03-21 14:37:55 +00001237 }
1238
1239 // Check that we read everything and that profiles don't contain junk data.
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001240 if (uncompressed_data.CountUnreadBytes() > 0) {
Calin Juravle64142952016-03-21 14:37:55 +00001241 *error = "Unexpected content in the profile file";
1242 return kProfileLoadBadData;
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001243 } else {
1244 return kProfileLoadSuccess;
Calin Juravle64142952016-03-21 14:37:55 +00001245 }
Calin Juravle998c2162015-12-21 15:39:33 +02001246}
1247
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001248bool ProfileCompilationInfo::RemapProfileIndex(
1249 const std::vector<ProfileLineHeader>& profile_line_headers,
1250 /*out*/SafeMap<uint8_t, uint8_t>* dex_profile_index_remap) {
1251 // First verify that all checksums match. This will avoid adding garbage to
1252 // the current profile info.
1253 // Note that the number of elements should be very small, so this should not
1254 // be a performance issue.
1255 for (const ProfileLineHeader other_profile_line_header : profile_line_headers) {
1256 // verify_checksum is false because we want to differentiate between a missing dex data and
1257 // a mismatched checksum.
1258 const DexFileData* dex_data = FindDexData(other_profile_line_header.dex_location,
1259 0u,
1260 false /* verify_checksum */);
1261 if ((dex_data != nullptr) && (dex_data->checksum != other_profile_line_header.checksum)) {
1262 LOG(WARNING) << "Checksum mismatch for dex " << other_profile_line_header.dex_location;
1263 return false;
1264 }
1265 }
1266 // All checksums match. Import the data.
1267 uint32_t num_dex_files = static_cast<uint32_t>(profile_line_headers.size());
1268 for (uint32_t i = 0; i < num_dex_files; i++) {
1269 const DexFileData* dex_data = GetOrAddDexFileData(profile_line_headers[i].dex_location,
1270 profile_line_headers[i].checksum,
1271 profile_line_headers[i].num_method_ids);
1272 if (dex_data == nullptr) {
1273 return false; // Could happen if we exceed the number of allowed dex files.
1274 }
1275 dex_profile_index_remap->Put(i, dex_data->profile_index);
1276 }
1277 return true;
1278}
Shubham Ajmera61200a02017-08-30 16:29:41 -07001279
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001280std::unique_ptr<uint8_t[]> ProfileCompilationInfo::DeflateBuffer(const uint8_t* in_buffer,
1281 uint32_t in_size,
1282 uint32_t* compressed_data_size) {
1283 z_stream strm;
1284 strm.zalloc = Z_NULL;
1285 strm.zfree = Z_NULL;
1286 strm.opaque = Z_NULL;
1287 int ret = deflateInit(&strm, 1);
1288 if (ret != Z_OK) {
1289 return nullptr;
1290 }
1291
1292 uint32_t out_size = deflateBound(&strm, in_size);
1293
1294 std::unique_ptr<uint8_t[]> compressed_buffer(new uint8_t[out_size]);
1295 strm.avail_in = in_size;
1296 strm.next_in = const_cast<uint8_t*>(in_buffer);
1297 strm.avail_out = out_size;
1298 strm.next_out = &compressed_buffer[0];
1299 ret = deflate(&strm, Z_FINISH);
1300 if (ret == Z_STREAM_ERROR) {
1301 return nullptr;
1302 }
1303 *compressed_data_size = out_size - strm.avail_out;
1304 deflateEnd(&strm);
1305 return compressed_buffer;
1306}
1307
1308int ProfileCompilationInfo::InflateBuffer(const uint8_t* in_buffer,
1309 uint32_t in_size,
1310 uint32_t expected_uncompressed_data_size,
1311 uint8_t* out_buffer) {
1312 z_stream strm;
1313
1314 /* allocate inflate state */
1315 strm.zalloc = Z_NULL;
1316 strm.zfree = Z_NULL;
1317 strm.opaque = Z_NULL;
1318 strm.avail_in = in_size;
1319 strm.next_in = const_cast<uint8_t*>(in_buffer);
1320 strm.avail_out = expected_uncompressed_data_size;
1321 strm.next_out = out_buffer;
1322
1323 int ret;
1324 inflateInit(&strm);
1325 ret = inflate(&strm, Z_NO_FLUSH);
1326
1327 if (strm.avail_in != 0 || strm.avail_out != 0) {
1328 return Z_DATA_ERROR;
1329 }
1330 inflateEnd(&strm);
1331 return ret;
1332}
1333
Mathieu Chartier2f794552017-06-19 10:58:08 -07001334bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other,
1335 bool merge_classes) {
Calin Juravle5d1bd0a2016-03-24 20:33:22 +00001336 // First verify that all checksums match. This will avoid adding garbage to
1337 // the current profile info.
1338 // Note that the number of elements should be very small, so this should not
1339 // be a performance issue.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001340 for (const DexFileData* other_dex_data : other.info_) {
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001341 // verify_checksum is false because we want to differentiate between a missing dex data and
1342 // a mismatched checksum.
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001343 const DexFileData* dex_data = FindDexData(other_dex_data->profile_key,
1344 0u,
1345 /* verify_checksum */ false);
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001346 if ((dex_data != nullptr) && (dex_data->checksum != other_dex_data->checksum)) {
1347 LOG(WARNING) << "Checksum mismatch for dex " << other_dex_data->profile_key;
Calin Juravle5d1bd0a2016-03-24 20:33:22 +00001348 return false;
1349 }
1350 }
1351 // All checksums match. Import the data.
Calin Juravle940eb0c2017-01-30 19:30:44 -08001352
1353 // The other profile might have a different indexing of dex files.
1354 // That is because each dex files gets a 'dex_profile_index' on a first come first served basis.
1355 // That means that the order in with the methods are added to the profile matters for the
1356 // actual indices.
1357 // The reason we cannot rely on the actual multidex index is that a single profile may store
1358 // data from multiple splits. This means that a profile may contain a classes2.dex from split-A
1359 // and one from split-B.
1360
1361 // First, build a mapping from other_dex_profile_index to this_dex_profile_index.
1362 // This will make sure that the ClassReferences will point to the correct dex file.
1363 SafeMap<uint8_t, uint8_t> dex_profile_index_remap;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001364 for (const DexFileData* other_dex_data : other.info_) {
1365 const DexFileData* dex_data = GetOrAddDexFileData(other_dex_data->profile_key,
Mathieu Chartierea650f32017-05-24 12:04:13 -07001366 other_dex_data->checksum,
1367 other_dex_data->num_method_ids);
Calin Juravlee0ac1152017-02-13 19:03:47 -08001368 if (dex_data == nullptr) {
1369 return false; // Could happen if we exceed the number of allowed dex files.
1370 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001371 dex_profile_index_remap.Put(other_dex_data->profile_index, dex_data->profile_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -08001372 }
1373
1374 // Merge the actual profile data.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001375 for (const DexFileData* other_dex_data : other.info_) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001376 DexFileData* dex_data = const_cast<DexFileData*>(FindDexData(other_dex_data->profile_key,
1377 other_dex_data->checksum));
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001378 DCHECK(dex_data != nullptr);
Calin Juravle940eb0c2017-01-30 19:30:44 -08001379
1380 // Merge the classes.
Mathieu Chartier2f794552017-06-19 10:58:08 -07001381 if (merge_classes) {
1382 dex_data->class_set.insert(other_dex_data->class_set.begin(),
1383 other_dex_data->class_set.end());
1384 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001385
1386 // Merge the methods and the inline caches.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001387 for (const auto& other_method_it : other_dex_data->method_map) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001388 uint16_t other_method_index = other_method_it.first;
Calin Juravlecc3171a2017-05-19 16:47:53 -07001389 InlineCacheMap* inline_cache = dex_data->FindOrAddMethod(other_method_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -08001390 const auto& other_inline_cache = other_method_it.second;
1391 for (const auto& other_ic_it : other_inline_cache) {
1392 uint16_t other_dex_pc = other_ic_it.first;
1393 const ClassSet& other_class_set = other_ic_it.second.classes;
Calin Juravlecc3171a2017-05-19 16:47:53 -07001394 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, other_dex_pc);
Calin Juravle589e71e2017-03-03 16:05:05 -08001395 if (other_ic_it.second.is_missing_types) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001396 dex_pc_data->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -08001397 } else if (other_ic_it.second.is_megamorphic) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001398 dex_pc_data->SetIsMegamorphic();
Calin Juravle0def68d2017-02-21 19:00:33 -08001399 } else {
1400 for (const auto& class_it : other_class_set) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001401 dex_pc_data->AddClass(dex_profile_index_remap.Get(
Calin Juravle0def68d2017-02-21 19:00:33 -08001402 class_it.dex_profile_index), class_it.type_index);
1403 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001404 }
1405 }
1406 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001407
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001408 // Merge the method bitmaps.
Mathieu Chartierea650f32017-05-24 12:04:13 -07001409 dex_data->MergeBitmap(*other_dex_data);
Calin Juravle998c2162015-12-21 15:39:33 +02001410 }
1411 return true;
Calin Juravle226501b2015-12-11 14:41:31 +00001412}
1413
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001414const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData(
1415 const DexFile* dex_file) const {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001416 return FindDexData(GetProfileDexFileKey(dex_file->GetLocation()),
1417 dex_file->GetLocationChecksum());
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001418}
1419
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001420ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::GetMethodHotness(
1421 const MethodReference& method_ref) const {
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001422 const DexFileData* dex_data = FindDexData(method_ref.dex_file);
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001423 return dex_data != nullptr
Mathieu Chartierfc8b4222017-09-17 13:44:24 -07001424 ? dex_data->GetHotnessInfo(method_ref.index)
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001425 : MethodHotness();
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001426}
1427
Mathieu Chartier2f794552017-06-19 10:58:08 -07001428bool ProfileCompilationInfo::AddMethodHotness(const MethodReference& method_ref,
1429 const MethodHotness& hotness) {
1430 DexFileData* dex_data = GetOrAddDexFileData(method_ref.dex_file);
1431 if (dex_data != nullptr) {
1432 // TODO: Add inline caches.
Calin Juravle1ad1e3f2017-09-19 18:20:37 -07001433 return dex_data->AddMethod(
1434 static_cast<MethodHotness::Flag>(hotness.GetFlags()), method_ref.index);
Mathieu Chartier2f794552017-06-19 10:58:08 -07001435 }
1436 return false;
1437}
1438
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001439ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::GetMethodHotness(
1440 const std::string& dex_location,
1441 uint32_t dex_checksum,
1442 uint16_t dex_method_index) const {
1443 const DexFileData* dex_data = FindDexData(GetProfileDexFileKey(dex_location), dex_checksum);
1444 return dex_data != nullptr ? dex_data->GetHotnessInfo(dex_method_index) : MethodHotness();
Calin Juravle226501b2015-12-11 14:41:31 +00001445}
1446
Calin Juravle940eb0c2017-01-30 19:30:44 -08001447
Calin Juravlecc3171a2017-05-19 16:47:53 -07001448std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> ProfileCompilationInfo::GetMethod(
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001449 const std::string& dex_location,
1450 uint32_t dex_checksum,
1451 uint16_t dex_method_index) const {
1452 MethodHotness hotness(GetMethodHotness(dex_location, dex_checksum, dex_method_index));
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001453 if (!hotness.IsHot()) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001454 return nullptr;
Calin Juravle940eb0c2017-01-30 19:30:44 -08001455 }
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001456 const InlineCacheMap* inline_caches = hotness.GetInlineCacheMap();
1457 DCHECK(inline_caches != nullptr);
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001458 std::unique_ptr<OfflineProfileMethodInfo> pmi(new OfflineProfileMethodInfo(inline_caches));
Calin Juravle940eb0c2017-01-30 19:30:44 -08001459
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001460 pmi->dex_references.resize(info_.size());
1461 for (const DexFileData* dex_data : info_) {
1462 pmi->dex_references[dex_data->profile_index].dex_location = dex_data->profile_key;
1463 pmi->dex_references[dex_data->profile_index].dex_checksum = dex_data->checksum;
Mathieu Chartierea650f32017-05-24 12:04:13 -07001464 pmi->dex_references[dex_data->profile_index].num_method_ids = dex_data->num_method_ids;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001465 }
1466
Calin Juravlecc3171a2017-05-19 16:47:53 -07001467 return pmi;
Calin Juravle940eb0c2017-01-30 19:30:44 -08001468}
1469
1470
Andreas Gampea5b09a62016-11-17 15:21:22 -08001471bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001472 const DexFileData* dex_data = FindDexData(&dex_file);
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001473 if (dex_data != nullptr) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001474 const ArenaSet<dex::TypeIndex>& classes = dex_data->class_set;
Jeff Hao54b58552016-11-16 15:15:04 -08001475 return classes.find(type_idx) != classes.end();
Mathieu Chartiera8077802016-03-16 19:08:31 -07001476 }
1477 return false;
1478}
1479
Calin Juravle998c2162015-12-21 15:39:33 +02001480uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
1481 uint32_t total = 0;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001482 for (const DexFileData* dex_data : info_) {
1483 total += dex_data->method_map.size();
Calin Juravle998c2162015-12-21 15:39:33 +02001484 }
1485 return total;
1486}
1487
Calin Juravle67265462016-03-18 16:23:40 +00001488uint32_t ProfileCompilationInfo::GetNumberOfResolvedClasses() const {
1489 uint32_t total = 0;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001490 for (const DexFileData* dex_data : info_) {
1491 total += dex_data->class_set.size();
Calin Juravle67265462016-03-18 16:23:40 +00001492 }
1493 return total;
1494}
1495
David Sehrb18991b2017-02-08 20:58:10 -08001496// Produce a non-owning vector from a vector.
1497template<typename T>
1498const std::vector<T*>* MakeNonOwningVector(const std::vector<std::unique_ptr<T>>* owning_vector) {
1499 auto non_owning_vector = new std::vector<T*>();
1500 for (auto& element : *owning_vector) {
1501 non_owning_vector->push_back(element.get());
1502 }
1503 return non_owning_vector;
1504}
1505
1506std::string ProfileCompilationInfo::DumpInfo(
1507 const std::vector<std::unique_ptr<const DexFile>>* dex_files,
1508 bool print_full_dex_location) const {
1509 std::unique_ptr<const std::vector<const DexFile*>> non_owning_dex_files(
1510 MakeNonOwningVector(dex_files));
1511 return DumpInfo(non_owning_dex_files.get(), print_full_dex_location);
1512}
1513
Calin Juravle998c2162015-12-21 15:39:33 +02001514std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
1515 bool print_full_dex_location) const {
Calin Juravle226501b2015-12-11 14:41:31 +00001516 std::ostringstream os;
1517 if (info_.empty()) {
1518 return "ProfileInfo: empty";
1519 }
1520
1521 os << "ProfileInfo:";
1522
Calin Juravlea308a322017-07-18 16:51:51 -07001523 const std::string kFirstDexFileKeySubstitute = "!classes.dex";
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001524
1525 for (const DexFileData* dex_data : info_) {
Calin Juravle226501b2015-12-11 14:41:31 +00001526 os << "\n";
Calin Juravle226501b2015-12-11 14:41:31 +00001527 if (print_full_dex_location) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001528 os << dex_data->profile_key;
Calin Juravle226501b2015-12-11 14:41:31 +00001529 } else {
1530 // Replace the (empty) multidex suffix of the first key with a substitute for easier reading.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001531 std::string multidex_suffix = DexFile::GetMultiDexSuffix(dex_data->profile_key);
Calin Juravle226501b2015-12-11 14:41:31 +00001532 os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
1533 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001534 os << " [index=" << static_cast<uint32_t>(dex_data->profile_index) << "]";
Calin Juravle876f3502016-03-24 16:16:34 +00001535 const DexFile* dex_file = nullptr;
1536 if (dex_files != nullptr) {
1537 for (size_t i = 0; i < dex_files->size(); i++) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001538 if (dex_data->profile_key == (*dex_files)[i]->GetLocation()) {
Calin Juravle876f3502016-03-24 16:16:34 +00001539 dex_file = (*dex_files)[i];
Calin Juravle998c2162015-12-21 15:39:33 +02001540 }
Calin Juravle226501b2015-12-11 14:41:31 +00001541 }
Calin Juravle876f3502016-03-24 16:16:34 +00001542 }
Mathieu Chartier28b5c582017-06-06 14:12:50 -07001543 os << "\n\thot methods: ";
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001544 for (const auto& method_it : dex_data->method_map) {
Calin Juravle876f3502016-03-24 16:16:34 +00001545 if (dex_file != nullptr) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001546 os << "\n\t\t" << dex_file->PrettyMethod(method_it.first, true);
Calin Juravle876f3502016-03-24 16:16:34 +00001547 } else {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001548 os << method_it.first;
Calin Juravle876f3502016-03-24 16:16:34 +00001549 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001550
1551 os << "[";
1552 for (const auto& inline_cache_it : method_it.second) {
1553 os << "{" << std::hex << inline_cache_it.first << std::dec << ":";
Calin Juravle589e71e2017-03-03 16:05:05 -08001554 if (inline_cache_it.second.is_missing_types) {
1555 os << "MT";
1556 } else if (inline_cache_it.second.is_megamorphic) {
1557 os << "MM";
Calin Juravle940eb0c2017-01-30 19:30:44 -08001558 } else {
1559 for (const ClassReference& class_ref : inline_cache_it.second.classes) {
1560 os << "(" << static_cast<uint32_t>(class_ref.dex_profile_index)
1561 << "," << class_ref.type_index.index_ << ")";
1562 }
1563 }
1564 os << "}";
1565 }
1566 os << "], ";
Calin Juravle876f3502016-03-24 16:16:34 +00001567 }
Mathieu Chartier28b5c582017-06-06 14:12:50 -07001568 bool startup = true;
1569 while (true) {
1570 os << "\n\t" << (startup ? "startup methods: " : "post startup methods: ");
1571 for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001572 MethodHotness hotness_info(dex_data->GetHotnessInfo(method_idx));
1573 if (startup ? hotness_info.IsStartup() : hotness_info.IsPostStartup()) {
Mathieu Chartier28b5c582017-06-06 14:12:50 -07001574 os << method_idx << ", ";
1575 }
1576 }
1577 if (startup == false) {
1578 break;
1579 }
1580 startup = false;
1581 }
Calin Juravle876f3502016-03-24 16:16:34 +00001582 os << "\n\tclasses: ";
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001583 for (const auto class_it : dex_data->class_set) {
Calin Juravle876f3502016-03-24 16:16:34 +00001584 if (dex_file != nullptr) {
Jeff Hao54b58552016-11-16 15:15:04 -08001585 os << "\n\t\t" << dex_file->PrettyType(class_it);
Calin Juravle876f3502016-03-24 16:16:34 +00001586 } else {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001587 os << class_it.index_ << ",";
Calin Juravle876f3502016-03-24 16:16:34 +00001588 }
Calin Juravle226501b2015-12-11 14:41:31 +00001589 }
1590 }
1591 return os.str();
1592}
1593
Mathieu Chartierea650f32017-05-24 12:04:13 -07001594bool ProfileCompilationInfo::GetClassesAndMethods(
1595 const DexFile& dex_file,
1596 /*out*/std::set<dex::TypeIndex>* class_set,
1597 /*out*/std::set<uint16_t>* hot_method_set,
1598 /*out*/std::set<uint16_t>* startup_method_set,
1599 /*out*/std::set<uint16_t>* post_startup_method_method_set) const {
Mathieu Chartier34067262017-04-06 13:55:46 -07001600 std::set<std::string> ret;
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001601 const DexFileData* dex_data = FindDexData(&dex_file);
1602 if (dex_data == nullptr) {
Mathieu Chartier34067262017-04-06 13:55:46 -07001603 return false;
David Sehr7c80f2d2017-02-07 16:47:58 -08001604 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001605 for (const auto& it : dex_data->method_map) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001606 hot_method_set->insert(it.first);
1607 }
1608 for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001609 MethodHotness hotness = dex_data->GetHotnessInfo(method_idx);
1610 if (hotness.IsStartup()) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001611 startup_method_set->insert(method_idx);
1612 }
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001613 if (hotness.IsPostStartup()) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001614 post_startup_method_method_set->insert(method_idx);
1615 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001616 }
Calin Juravlecc3171a2017-05-19 16:47:53 -07001617 for (const dex::TypeIndex& type_index : dex_data->class_set) {
1618 class_set->insert(type_index);
1619 }
Mathieu Chartier34067262017-04-06 13:55:46 -07001620 return true;
David Sehr7c80f2d2017-02-07 16:47:58 -08001621}
1622
Calin Juravle2e2db782016-02-23 12:00:03 +00001623bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001624 // No need to compare profile_key_map_. That's only a cache for fast search.
1625 // All the information is already in the info_ vector.
1626 if (info_.size() != other.info_.size()) {
1627 return false;
1628 }
1629 for (size_t i = 0; i < info_.size(); i++) {
1630 const DexFileData& dex_data = *info_[i];
1631 const DexFileData& other_dex_data = *other.info_[i];
1632 if (!(dex_data == other_dex_data)) {
1633 return false;
1634 }
1635 }
1636 return true;
Calin Juravle877fd962016-01-05 14:29:29 +00001637}
1638
Mathieu Chartier046854b2017-03-01 17:16:22 -08001639std::set<DexCacheResolvedClasses> ProfileCompilationInfo::GetResolvedClasses(
Calin Juravle08556882017-05-26 16:40:45 -07001640 const std::vector<const DexFile*>& dex_files) const {
1641 std::unordered_map<std::string, const DexFile* > key_to_dex_file;
1642 for (const DexFile* dex_file : dex_files) {
1643 key_to_dex_file.emplace(GetProfileDexFileKey(dex_file->GetLocation()), dex_file);
Mathieu Chartier046854b2017-03-01 17:16:22 -08001644 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -08001645 std::set<DexCacheResolvedClasses> ret;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001646 for (const DexFileData* dex_data : info_) {
Calin Juravle08556882017-05-26 16:40:45 -07001647 const auto it = key_to_dex_file.find(dex_data->profile_key);
1648 if (it != key_to_dex_file.end()) {
1649 const DexFile* dex_file = it->second;
1650 const std::string& dex_location = dex_file->GetLocation();
1651 if (dex_data->checksum != it->second->GetLocationChecksum()) {
1652 LOG(ERROR) << "Dex checksum mismatch when getting resolved classes from profile for "
1653 << "location " << dex_location << " (checksum=" << dex_file->GetLocationChecksum()
1654 << ", profile checksum=" << dex_data->checksum;
1655 return std::set<DexCacheResolvedClasses>();
1656 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001657 DexCacheResolvedClasses classes(dex_location,
1658 dex_location,
1659 dex_data->checksum,
1660 dex_data->num_method_ids);
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001661 classes.AddClasses(dex_data->class_set.begin(), dex_data->class_set.end());
Mathieu Chartier046854b2017-03-01 17:16:22 -08001662 ret.insert(classes);
1663 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -08001664 }
1665 return ret;
1666}
1667
Calin Juravle7bcdb532016-06-07 16:14:47 +01001668// Naive implementation to generate a random profile file suitable for testing.
1669bool ProfileCompilationInfo::GenerateTestProfile(int fd,
1670 uint16_t number_of_dex_files,
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001671 uint16_t method_percentage,
1672 uint16_t class_percentage,
Jeff Haof0a31f82017-03-27 15:50:37 -07001673 uint32_t random_seed) {
Calin Juravle7bcdb532016-06-07 16:14:47 +01001674 const std::string base_dex_location = "base.apk";
1675 ProfileCompilationInfo info;
1676 // The limits are defined by the dex specification.
Mathieu Chartierea650f32017-05-24 12:04:13 -07001677 const uint16_t max_method = std::numeric_limits<uint16_t>::max();
1678 const uint16_t max_classes = std::numeric_limits<uint16_t>::max();
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001679 uint16_t number_of_methods = max_method * method_percentage / 100;
1680 uint16_t number_of_classes = max_classes * class_percentage / 100;
Calin Juravle7bcdb532016-06-07 16:14:47 +01001681
Jeff Haof0a31f82017-03-27 15:50:37 -07001682 std::srand(random_seed);
Calin Juravle7bcdb532016-06-07 16:14:47 +01001683
1684 // Make sure we generate more samples with a low index value.
1685 // This makes it more likely to hit valid method/class indices in small apps.
1686 const uint16_t kFavorFirstN = 10000;
1687 const uint16_t kFavorSplit = 2;
1688
1689 for (uint16_t i = 0; i < number_of_dex_files; i++) {
1690 std::string dex_location = DexFile::GetMultiDexLocation(i, base_dex_location.c_str());
1691 std::string profile_key = GetProfileDexFileKey(dex_location);
1692
1693 for (uint16_t m = 0; m < number_of_methods; m++) {
1694 uint16_t method_idx = rand() % max_method;
1695 if (m < (number_of_methods / kFavorSplit)) {
1696 method_idx %= kFavorFirstN;
1697 }
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001698 info.AddMethodIndex(MethodHotness::kFlagHot,
1699 profile_key,
1700 /*method_idx*/ 0,
1701 method_idx,
1702 max_method);
Calin Juravle7bcdb532016-06-07 16:14:47 +01001703 }
1704
1705 for (uint16_t c = 0; c < number_of_classes; c++) {
Jeff Hao54b58552016-11-16 15:15:04 -08001706 uint16_t type_idx = rand() % max_classes;
Calin Juravle7bcdb532016-06-07 16:14:47 +01001707 if (c < (number_of_classes / kFavorSplit)) {
Jeff Hao54b58552016-11-16 15:15:04 -08001708 type_idx %= kFavorFirstN;
Calin Juravle7bcdb532016-06-07 16:14:47 +01001709 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001710 info.AddClassIndex(profile_key, 0, dex::TypeIndex(type_idx), max_method);
Calin Juravle7bcdb532016-06-07 16:14:47 +01001711 }
1712 }
1713 return info.Save(fd);
1714}
1715
Jeff Haof0a31f82017-03-27 15:50:37 -07001716// Naive implementation to generate a random profile file suitable for testing.
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001717// Description of random selection:
1718// * Select a random starting point S.
1719// * For every index i, add (S+i) % (N - total number of methods/classes) to profile with the
1720// probably of 1/(N - i - number of methods/classes needed to add in profile).
Jeff Haof0a31f82017-03-27 15:50:37 -07001721bool ProfileCompilationInfo::GenerateTestProfile(
1722 int fd,
1723 std::vector<std::unique_ptr<const DexFile>>& dex_files,
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001724 uint16_t method_percentage,
1725 uint16_t class_percentage,
Jeff Haof0a31f82017-03-27 15:50:37 -07001726 uint32_t random_seed) {
1727 std::srand(random_seed);
1728 ProfileCompilationInfo info;
1729 for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
1730 const std::string& location = dex_file->GetLocation();
1731 uint32_t checksum = dex_file->GetLocationChecksum();
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001732
1733 uint32_t number_of_classes = dex_file->NumClassDefs();
1734 uint32_t classes_required_in_profile = (number_of_classes * class_percentage) / 100;
1735 uint32_t class_start_index = rand() % number_of_classes;
1736 for (uint32_t i = 0; i < number_of_classes && classes_required_in_profile; ++i) {
1737 if (number_of_classes - i == classes_required_in_profile ||
1738 std::rand() % (number_of_classes - i - classes_required_in_profile) == 0) {
1739 uint32_t class_index = (i + class_start_index) % number_of_classes;
Mathieu Chartierea650f32017-05-24 12:04:13 -07001740 info.AddClassIndex(location,
1741 checksum,
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001742 dex_file->GetClassDef(class_index).class_idx_,
Mathieu Chartierea650f32017-05-24 12:04:13 -07001743 dex_file->NumMethodIds());
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001744 classes_required_in_profile--;
Jeff Haof0a31f82017-03-27 15:50:37 -07001745 }
1746 }
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001747
1748 uint32_t number_of_methods = dex_file->NumMethodIds();
1749 uint32_t methods_required_in_profile = (number_of_methods * method_percentage) / 100;
1750 uint32_t method_start_index = rand() % number_of_methods;
1751 for (uint32_t i = 0; i < number_of_methods && methods_required_in_profile; ++i) {
1752 if (number_of_methods - i == methods_required_in_profile ||
1753 std::rand() % (number_of_methods - i - methods_required_in_profile) == 0) {
1754 uint32_t method_index = (method_start_index + i) % number_of_methods;
1755 info.AddMethodIndex(MethodHotness::kFlagHot, MethodReference(dex_file.get(),
1756 method_index));
1757 methods_required_in_profile--;
Jeff Haof0a31f82017-03-27 15:50:37 -07001758 }
1759 }
1760 }
1761 return info.Save(fd);
1762}
1763
Calin Juravle940eb0c2017-01-30 19:30:44 -08001764bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==(
1765 const OfflineProfileMethodInfo& other) const {
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001766 if (inline_caches->size() != other.inline_caches->size()) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001767 return false;
1768 }
1769
1770 // We can't use a simple equality test because we need to match the dex files
Calin Juravle589e71e2017-03-03 16:05:05 -08001771 // of the inline caches which might have different profile indexes.
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001772 for (const auto& inline_cache_it : *inline_caches) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001773 uint16_t dex_pc = inline_cache_it.first;
1774 const DexPcData dex_pc_data = inline_cache_it.second;
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001775 const auto& other_it = other.inline_caches->find(dex_pc);
1776 if (other_it == other.inline_caches->end()) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001777 return false;
1778 }
1779 const DexPcData& other_dex_pc_data = other_it->second;
Calin Juravle589e71e2017-03-03 16:05:05 -08001780 if (dex_pc_data.is_megamorphic != other_dex_pc_data.is_megamorphic ||
1781 dex_pc_data.is_missing_types != other_dex_pc_data.is_missing_types) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001782 return false;
1783 }
1784 for (const ClassReference& class_ref : dex_pc_data.classes) {
1785 bool found = false;
1786 for (const ClassReference& other_class_ref : other_dex_pc_data.classes) {
1787 CHECK_LE(class_ref.dex_profile_index, dex_references.size());
1788 CHECK_LE(other_class_ref.dex_profile_index, other.dex_references.size());
1789 const DexReference& dex_ref = dex_references[class_ref.dex_profile_index];
1790 const DexReference& other_dex_ref = other.dex_references[other_class_ref.dex_profile_index];
1791 if (class_ref.type_index == other_class_ref.type_index &&
1792 dex_ref == other_dex_ref) {
1793 found = true;
1794 break;
1795 }
1796 }
1797 if (!found) {
1798 return false;
1799 }
1800 }
1801 }
1802 return true;
1803}
1804
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001805bool ProfileCompilationInfo::IsEmpty() const {
1806 DCHECK_EQ(info_.empty(), profile_key_map_.empty());
1807 return info_.empty();
1808}
1809
Calin Juravlecc3171a2017-05-19 16:47:53 -07001810ProfileCompilationInfo::InlineCacheMap*
1811ProfileCompilationInfo::DexFileData::FindOrAddMethod(uint16_t method_index) {
1812 return &(method_map.FindOrAdd(
1813 method_index,
1814 InlineCacheMap(std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile)))->second);
1815}
1816
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001817// Mark a method as executed at least once.
Calin Juravle1ad1e3f2017-09-19 18:20:37 -07001818bool ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, size_t index) {
1819 if (index >= num_method_ids) {
1820 LOG(ERROR) << "Invalid method index " << index << ". num_method_ids=" << num_method_ids;
1821 return false;
1822 }
1823
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001824 if ((flags & MethodHotness::kFlagStartup) != 0) {
1825 method_bitmap.StoreBit(MethodBitIndex(/*startup*/ true, index), /*value*/ true);
1826 }
1827 if ((flags & MethodHotness::kFlagPostStartup) != 0) {
1828 method_bitmap.StoreBit(MethodBitIndex(/*startup*/ false, index), /*value*/ true);
1829 }
1830 if ((flags & MethodHotness::kFlagHot) != 0) {
1831 method_map.FindOrAdd(
1832 index,
1833 InlineCacheMap(std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile)));
1834 }
Calin Juravle1ad1e3f2017-09-19 18:20:37 -07001835 return true;
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001836}
1837
1838ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::DexFileData::GetHotnessInfo(
1839 uint32_t dex_method_index) const {
1840 MethodHotness ret;
1841 if (method_bitmap.LoadBit(MethodBitIndex(/*startup*/ true, dex_method_index))) {
1842 ret.AddFlag(MethodHotness::kFlagStartup);
1843 }
1844 if (method_bitmap.LoadBit(MethodBitIndex(/*startup*/ false, dex_method_index))) {
1845 ret.AddFlag(MethodHotness::kFlagPostStartup);
1846 }
1847 auto it = method_map.find(dex_method_index);
1848 if (it != method_map.end()) {
1849 ret.SetInlineCacheMap(&it->second);
1850 ret.AddFlag(MethodHotness::kFlagHot);
1851 }
1852 return ret;
1853}
1854
Calin Juravlecc3171a2017-05-19 16:47:53 -07001855ProfileCompilationInfo::DexPcData*
1856ProfileCompilationInfo::FindOrAddDexPc(InlineCacheMap* inline_cache, uint32_t dex_pc) {
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001857 return &(inline_cache->FindOrAdd(dex_pc, DexPcData(&arena_))->second);
Calin Juravlecc3171a2017-05-19 16:47:53 -07001858}
1859
Mathieu Chartier4f342b02017-07-21 17:12:39 -07001860std::unordered_set<std::string> ProfileCompilationInfo::GetClassDescriptors(
1861 const std::vector<const DexFile*>& dex_files) {
1862 std::unordered_set<std::string> ret;
1863 for (const DexFile* dex_file : dex_files) {
1864 const DexFileData* data = FindDexData(dex_file);
1865 if (data != nullptr) {
1866 for (dex::TypeIndex type_idx : data->class_set) {
1867 if (!dex_file->IsTypeIndexValid(type_idx)) {
1868 // Something went bad. The profile is probably corrupted. Abort and return an emtpy set.
1869 LOG(WARNING) << "Corrupted profile: invalid type index "
1870 << type_idx.index_ << " in dex " << dex_file->GetLocation();
1871 return std::unordered_set<std::string>();
1872 }
1873 const DexFile::TypeId& type_id = dex_file->GetTypeId(type_idx);
1874 ret.insert(dex_file->GetTypeDescriptor(type_id));
1875 }
1876 } else {
1877 VLOG(compiler) << "Failed to find profile data for " << dex_file->GetLocation();
1878 }
1879 }
1880 return ret;
1881}
1882
Calin Juravle31f2c152015-10-23 17:56:15 +01001883} // namespace art