blob: 1ed7889998c6f72bf9a9826fab226a26fe9a49dd [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 }
Shubham Ajmera460ab792017-09-21 13:44:07 -07001076
1077 if (dex_data->num_method_ids != dex_file->NumMethodIds()) {
1078 LOG(ERROR) << "Number of method ids in dex file and profile don't match."
1079 << "dex location " << dex_location << " NumMethodId in DexFile"
1080 << dex_file->NumMethodIds() << ", NumMethodId in profile"
1081 << dex_data->num_method_ids;
1082 return false;
1083 }
1084
Shubham Ajmera188b2bf2017-09-20 15:53:35 -07001085 // Verify method_encoding.
1086 for (const auto& method_it : dex_data->method_map) {
1087 size_t method_id = (size_t)(method_it.first);
1088 if (method_id >= dex_file->NumMethodIds()) {
1089 LOG(ERROR) << "Invalid method id in profile file. dex location="
1090 << dex_location << " method_id=" << method_id << " NumMethodIds="
1091 << dex_file->NumMethodIds();
1092 return false;
1093 }
1094
1095 // Verify class indices of inline caches.
1096 const InlineCacheMap &inline_cache_map = method_it.second;
1097 for (const auto& inline_cache_it : inline_cache_map) {
1098 const DexPcData dex_pc_data = inline_cache_it.second;
1099 if (dex_pc_data.is_missing_types || dex_pc_data.is_megamorphic) {
1100 // No class indices to verify.
1101 continue;
1102 }
1103
1104 const ClassSet &classes = dex_pc_data.classes;
1105 SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
1106 // Group the classes by dex. We expect that most of the classes will come from
1107 // the same dex, so this will be more efficient than encoding the dex index
1108 // for each class reference.
1109 GroupClassesByDex(classes, &dex_to_classes_map);
1110 for (const auto &dex_it : dex_to_classes_map) {
1111 uint8_t dex_profile_index = dex_it.first;
1112 const auto dex_file_inline_cache_it = key_to_dex_file.find(
1113 info_[dex_profile_index]->profile_key);
1114 if (dex_file_inline_cache_it == key_to_dex_file.end()) {
1115 // It is okay if profile contains data for additional dex files.
1116 continue;
1117 }
1118 const DexFile *dex_file_for_inline_cache_check = dex_file_inline_cache_it->second;
1119 const std::vector<dex::TypeIndex> &dex_classes = dex_it.second;
1120 for (size_t i = 0; i < dex_classes.size(); i++) {
1121 if (dex_classes[i].index_ >= dex_file_for_inline_cache_check->NumTypeIds()) {
1122 LOG(ERROR) << "Invalid inline cache in profile file. dex location="
1123 << dex_location << " method_id=" << method_id
1124 << " dex_profile_index="
1125 << static_cast<uint16_t >(dex_profile_index) << " type_index="
1126 << dex_classes[i].index_
1127 << " NumTypeIds="
1128 << dex_file_for_inline_cache_check->NumTypeIds();
1129 return false;
1130 }
1131 }
1132 }
1133 }
1134 }
1135 // Verify class_ids.
1136 for (const auto& class_id : dex_data->class_set) {
1137 if (class_id.index_ >= dex_file->NumTypeIds()) {
1138 LOG(ERROR) << "Invalid class id in profile file. dex_file location "
1139 << dex_location << " class_id=" << class_id.index_ << " NumClassIds="
1140 << dex_file->NumClassDefs();
1141 return false;
1142 }
1143 }
1144 }
1145 return true;
1146}
1147
Calin Juravledcab1902017-05-12 19:18:47 -07001148// TODO(calin): fail fast if the dex checksums don't match.
Calin Juravle64142952016-03-21 14:37:55 +00001149ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal(
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001150 int fd, std::string* error, bool merge_classes) {
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -08001151 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravle2e2db782016-02-23 12:00:03 +00001152 DCHECK_GE(fd, 0);
Calin Juravle226501b2015-12-11 14:41:31 +00001153
Calin Juravle64142952016-03-21 14:37:55 +00001154 struct stat stat_buffer;
1155 if (fstat(fd, &stat_buffer) != 0) {
1156 return kProfileLoadIOError;
Calin Juravle226501b2015-12-11 14:41:31 +00001157 }
Calin Juravle64142952016-03-21 14:37:55 +00001158 // We allow empty profile files.
1159 // Profiles may be created by ActivityManager or installd before we manage to
1160 // process them in the runtime or profman.
1161 if (stat_buffer.st_size == 0) {
1162 return kProfileLoadSuccess;
1163 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001164 // Read profile header: magic + version + number_of_dex_files.
1165 uint8_t number_of_dex_files;
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001166 uint32_t uncompressed_data_size;
1167 uint32_t compressed_data_size;
1168 ProfileLoadSatus status = ReadProfileHeader(fd,
1169 &number_of_dex_files,
1170 &uncompressed_data_size,
1171 &compressed_data_size,
1172 error);
1173
Calin Juravle64142952016-03-21 14:37:55 +00001174 if (status != kProfileLoadSuccess) {
1175 return status;
1176 }
Mathieu Chartierf2e2af82017-07-12 21:09:57 -07001177 // Allow large profiles for non target builds for the case where we are merging many profiles
1178 // to generate a boot image profile.
1179 if (kIsTargetBuild && uncompressed_data_size > kProfileSizeErrorThresholdInBytes) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001180 LOG(ERROR) << "Profile data size exceeds "
1181 << std::to_string(kProfileSizeErrorThresholdInBytes)
1182 << " bytes";
1183 return kProfileLoadBadData;
1184 }
1185 if (uncompressed_data_size > kProfileSizeWarningThresholdInBytes) {
1186 LOG(WARNING) << "Profile data size exceeds "
1187 << std::to_string(kProfileSizeWarningThresholdInBytes)
1188 << " bytes";
1189 }
1190
1191 std::unique_ptr<uint8_t[]> compressed_data(new uint8_t[compressed_data_size]);
1192 bool bytes_read_success =
1193 android::base::ReadFully(fd, compressed_data.get(), compressed_data_size);
1194
1195 if (testEOF(fd) != 0) {
1196 *error += "Unexpected data in the profile file.";
1197 return kProfileLoadBadData;
1198 }
1199
1200 if (!bytes_read_success) {
1201 *error += "Unable to read compressed profile data";
1202 return kProfileLoadBadData;
1203 }
1204
1205 SafeBuffer uncompressed_data(uncompressed_data_size);
1206
1207 int ret = InflateBuffer(compressed_data.get(),
1208 compressed_data_size,
1209 uncompressed_data_size,
1210 uncompressed_data.Get());
1211
1212 if (ret != Z_STREAM_END) {
1213 *error += "Error reading uncompressed profile data";
1214 return kProfileLoadBadData;
1215 }
1216
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001217 std::vector<ProfileLineHeader> profile_line_headers;
1218 // Read profile line headers.
Calin Juravle940eb0c2017-01-30 19:30:44 -08001219 for (uint8_t k = 0; k < number_of_dex_files; k++) {
Calin Juravle64142952016-03-21 14:37:55 +00001220 ProfileLineHeader line_header;
Calin Juravle940eb0c2017-01-30 19:30:44 -08001221
Calin Juravle64142952016-03-21 14:37:55 +00001222 // First, read the line header to get the amount of data we need to read.
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001223 status = ReadProfileLineHeader(uncompressed_data, &line_header, error);
Calin Juravle64142952016-03-21 14:37:55 +00001224 if (status != kProfileLoadSuccess) {
1225 return status;
1226 }
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001227 profile_line_headers.push_back(line_header);
1228 }
Calin Juravle64142952016-03-21 14:37:55 +00001229
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001230 SafeMap<uint8_t, uint8_t> dex_profile_index_remap;
1231 if (!RemapProfileIndex(profile_line_headers, &dex_profile_index_remap)) {
1232 return kProfileLoadBadData;
1233 }
1234
1235 for (uint8_t k = 0; k < number_of_dex_files; k++) {
Calin Juravle64142952016-03-21 14:37:55 +00001236 // Now read the actual profile line.
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001237 status = ReadProfileLine(uncompressed_data,
1238 number_of_dex_files,
1239 profile_line_headers[k],
1240 dex_profile_index_remap,
1241 merge_classes,
1242 error);
Calin Juravle64142952016-03-21 14:37:55 +00001243 if (status != kProfileLoadSuccess) {
1244 return status;
1245 }
Calin Juravle64142952016-03-21 14:37:55 +00001246 }
1247
1248 // Check that we read everything and that profiles don't contain junk data.
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001249 if (uncompressed_data.CountUnreadBytes() > 0) {
Calin Juravle64142952016-03-21 14:37:55 +00001250 *error = "Unexpected content in the profile file";
1251 return kProfileLoadBadData;
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001252 } else {
1253 return kProfileLoadSuccess;
Calin Juravle64142952016-03-21 14:37:55 +00001254 }
Calin Juravle998c2162015-12-21 15:39:33 +02001255}
1256
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001257bool ProfileCompilationInfo::RemapProfileIndex(
1258 const std::vector<ProfileLineHeader>& profile_line_headers,
1259 /*out*/SafeMap<uint8_t, uint8_t>* dex_profile_index_remap) {
1260 // First verify that all checksums match. This will avoid adding garbage to
1261 // the current profile info.
1262 // Note that the number of elements should be very small, so this should not
1263 // be a performance issue.
1264 for (const ProfileLineHeader other_profile_line_header : profile_line_headers) {
1265 // verify_checksum is false because we want to differentiate between a missing dex data and
1266 // a mismatched checksum.
1267 const DexFileData* dex_data = FindDexData(other_profile_line_header.dex_location,
1268 0u,
1269 false /* verify_checksum */);
1270 if ((dex_data != nullptr) && (dex_data->checksum != other_profile_line_header.checksum)) {
1271 LOG(WARNING) << "Checksum mismatch for dex " << other_profile_line_header.dex_location;
1272 return false;
1273 }
1274 }
1275 // All checksums match. Import the data.
1276 uint32_t num_dex_files = static_cast<uint32_t>(profile_line_headers.size());
1277 for (uint32_t i = 0; i < num_dex_files; i++) {
1278 const DexFileData* dex_data = GetOrAddDexFileData(profile_line_headers[i].dex_location,
1279 profile_line_headers[i].checksum,
1280 profile_line_headers[i].num_method_ids);
1281 if (dex_data == nullptr) {
1282 return false; // Could happen if we exceed the number of allowed dex files.
1283 }
1284 dex_profile_index_remap->Put(i, dex_data->profile_index);
1285 }
1286 return true;
1287}
Shubham Ajmera61200a02017-08-30 16:29:41 -07001288
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001289std::unique_ptr<uint8_t[]> ProfileCompilationInfo::DeflateBuffer(const uint8_t* in_buffer,
1290 uint32_t in_size,
1291 uint32_t* compressed_data_size) {
1292 z_stream strm;
1293 strm.zalloc = Z_NULL;
1294 strm.zfree = Z_NULL;
1295 strm.opaque = Z_NULL;
1296 int ret = deflateInit(&strm, 1);
1297 if (ret != Z_OK) {
1298 return nullptr;
1299 }
1300
1301 uint32_t out_size = deflateBound(&strm, in_size);
1302
1303 std::unique_ptr<uint8_t[]> compressed_buffer(new uint8_t[out_size]);
1304 strm.avail_in = in_size;
1305 strm.next_in = const_cast<uint8_t*>(in_buffer);
1306 strm.avail_out = out_size;
1307 strm.next_out = &compressed_buffer[0];
1308 ret = deflate(&strm, Z_FINISH);
1309 if (ret == Z_STREAM_ERROR) {
1310 return nullptr;
1311 }
1312 *compressed_data_size = out_size - strm.avail_out;
1313 deflateEnd(&strm);
1314 return compressed_buffer;
1315}
1316
1317int ProfileCompilationInfo::InflateBuffer(const uint8_t* in_buffer,
1318 uint32_t in_size,
1319 uint32_t expected_uncompressed_data_size,
1320 uint8_t* out_buffer) {
1321 z_stream strm;
1322
1323 /* allocate inflate state */
1324 strm.zalloc = Z_NULL;
1325 strm.zfree = Z_NULL;
1326 strm.opaque = Z_NULL;
1327 strm.avail_in = in_size;
1328 strm.next_in = const_cast<uint8_t*>(in_buffer);
1329 strm.avail_out = expected_uncompressed_data_size;
1330 strm.next_out = out_buffer;
1331
1332 int ret;
1333 inflateInit(&strm);
1334 ret = inflate(&strm, Z_NO_FLUSH);
1335
1336 if (strm.avail_in != 0 || strm.avail_out != 0) {
1337 return Z_DATA_ERROR;
1338 }
1339 inflateEnd(&strm);
1340 return ret;
1341}
1342
Mathieu Chartier2f794552017-06-19 10:58:08 -07001343bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other,
1344 bool merge_classes) {
Calin Juravle5d1bd0a2016-03-24 20:33:22 +00001345 // First verify that all checksums match. This will avoid adding garbage to
1346 // the current profile info.
1347 // Note that the number of elements should be very small, so this should not
1348 // be a performance issue.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001349 for (const DexFileData* other_dex_data : other.info_) {
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001350 // verify_checksum is false because we want to differentiate between a missing dex data and
1351 // a mismatched checksum.
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001352 const DexFileData* dex_data = FindDexData(other_dex_data->profile_key,
1353 0u,
1354 /* verify_checksum */ false);
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001355 if ((dex_data != nullptr) && (dex_data->checksum != other_dex_data->checksum)) {
1356 LOG(WARNING) << "Checksum mismatch for dex " << other_dex_data->profile_key;
Calin Juravle5d1bd0a2016-03-24 20:33:22 +00001357 return false;
1358 }
1359 }
1360 // All checksums match. Import the data.
Calin Juravle940eb0c2017-01-30 19:30:44 -08001361
1362 // The other profile might have a different indexing of dex files.
1363 // That is because each dex files gets a 'dex_profile_index' on a first come first served basis.
1364 // That means that the order in with the methods are added to the profile matters for the
1365 // actual indices.
1366 // The reason we cannot rely on the actual multidex index is that a single profile may store
1367 // data from multiple splits. This means that a profile may contain a classes2.dex from split-A
1368 // and one from split-B.
1369
1370 // First, build a mapping from other_dex_profile_index to this_dex_profile_index.
1371 // This will make sure that the ClassReferences will point to the correct dex file.
1372 SafeMap<uint8_t, uint8_t> dex_profile_index_remap;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001373 for (const DexFileData* other_dex_data : other.info_) {
1374 const DexFileData* dex_data = GetOrAddDexFileData(other_dex_data->profile_key,
Mathieu Chartierea650f32017-05-24 12:04:13 -07001375 other_dex_data->checksum,
1376 other_dex_data->num_method_ids);
Calin Juravlee0ac1152017-02-13 19:03:47 -08001377 if (dex_data == nullptr) {
1378 return false; // Could happen if we exceed the number of allowed dex files.
1379 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001380 dex_profile_index_remap.Put(other_dex_data->profile_index, dex_data->profile_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -08001381 }
1382
1383 // Merge the actual profile data.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001384 for (const DexFileData* other_dex_data : other.info_) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001385 DexFileData* dex_data = const_cast<DexFileData*>(FindDexData(other_dex_data->profile_key,
1386 other_dex_data->checksum));
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001387 DCHECK(dex_data != nullptr);
Calin Juravle940eb0c2017-01-30 19:30:44 -08001388
1389 // Merge the classes.
Mathieu Chartier2f794552017-06-19 10:58:08 -07001390 if (merge_classes) {
1391 dex_data->class_set.insert(other_dex_data->class_set.begin(),
1392 other_dex_data->class_set.end());
1393 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001394
1395 // Merge the methods and the inline caches.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001396 for (const auto& other_method_it : other_dex_data->method_map) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001397 uint16_t other_method_index = other_method_it.first;
Calin Juravlecc3171a2017-05-19 16:47:53 -07001398 InlineCacheMap* inline_cache = dex_data->FindOrAddMethod(other_method_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -08001399 const auto& other_inline_cache = other_method_it.second;
1400 for (const auto& other_ic_it : other_inline_cache) {
1401 uint16_t other_dex_pc = other_ic_it.first;
1402 const ClassSet& other_class_set = other_ic_it.second.classes;
Calin Juravlecc3171a2017-05-19 16:47:53 -07001403 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, other_dex_pc);
Calin Juravle589e71e2017-03-03 16:05:05 -08001404 if (other_ic_it.second.is_missing_types) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001405 dex_pc_data->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -08001406 } else if (other_ic_it.second.is_megamorphic) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001407 dex_pc_data->SetIsMegamorphic();
Calin Juravle0def68d2017-02-21 19:00:33 -08001408 } else {
1409 for (const auto& class_it : other_class_set) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001410 dex_pc_data->AddClass(dex_profile_index_remap.Get(
Calin Juravle0def68d2017-02-21 19:00:33 -08001411 class_it.dex_profile_index), class_it.type_index);
1412 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001413 }
1414 }
1415 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001416
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001417 // Merge the method bitmaps.
Mathieu Chartierea650f32017-05-24 12:04:13 -07001418 dex_data->MergeBitmap(*other_dex_data);
Calin Juravle998c2162015-12-21 15:39:33 +02001419 }
1420 return true;
Calin Juravle226501b2015-12-11 14:41:31 +00001421}
1422
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001423const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData(
1424 const DexFile* dex_file) const {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001425 return FindDexData(GetProfileDexFileKey(dex_file->GetLocation()),
1426 dex_file->GetLocationChecksum());
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001427}
1428
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001429ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::GetMethodHotness(
1430 const MethodReference& method_ref) const {
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001431 const DexFileData* dex_data = FindDexData(method_ref.dex_file);
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001432 return dex_data != nullptr
Mathieu Chartierfc8b4222017-09-17 13:44:24 -07001433 ? dex_data->GetHotnessInfo(method_ref.index)
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001434 : MethodHotness();
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001435}
1436
Mathieu Chartier2f794552017-06-19 10:58:08 -07001437bool ProfileCompilationInfo::AddMethodHotness(const MethodReference& method_ref,
1438 const MethodHotness& hotness) {
1439 DexFileData* dex_data = GetOrAddDexFileData(method_ref.dex_file);
1440 if (dex_data != nullptr) {
1441 // TODO: Add inline caches.
Calin Juravle1ad1e3f2017-09-19 18:20:37 -07001442 return dex_data->AddMethod(
1443 static_cast<MethodHotness::Flag>(hotness.GetFlags()), method_ref.index);
Mathieu Chartier2f794552017-06-19 10:58:08 -07001444 }
1445 return false;
1446}
1447
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001448ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::GetMethodHotness(
1449 const std::string& dex_location,
1450 uint32_t dex_checksum,
1451 uint16_t dex_method_index) const {
1452 const DexFileData* dex_data = FindDexData(GetProfileDexFileKey(dex_location), dex_checksum);
1453 return dex_data != nullptr ? dex_data->GetHotnessInfo(dex_method_index) : MethodHotness();
Calin Juravle226501b2015-12-11 14:41:31 +00001454}
1455
Calin Juravle940eb0c2017-01-30 19:30:44 -08001456
Calin Juravlecc3171a2017-05-19 16:47:53 -07001457std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> ProfileCompilationInfo::GetMethod(
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001458 const std::string& dex_location,
1459 uint32_t dex_checksum,
1460 uint16_t dex_method_index) const {
1461 MethodHotness hotness(GetMethodHotness(dex_location, dex_checksum, dex_method_index));
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001462 if (!hotness.IsHot()) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001463 return nullptr;
Calin Juravle940eb0c2017-01-30 19:30:44 -08001464 }
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001465 const InlineCacheMap* inline_caches = hotness.GetInlineCacheMap();
1466 DCHECK(inline_caches != nullptr);
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001467 std::unique_ptr<OfflineProfileMethodInfo> pmi(new OfflineProfileMethodInfo(inline_caches));
Calin Juravle940eb0c2017-01-30 19:30:44 -08001468
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001469 pmi->dex_references.resize(info_.size());
1470 for (const DexFileData* dex_data : info_) {
1471 pmi->dex_references[dex_data->profile_index].dex_location = dex_data->profile_key;
1472 pmi->dex_references[dex_data->profile_index].dex_checksum = dex_data->checksum;
Mathieu Chartierea650f32017-05-24 12:04:13 -07001473 pmi->dex_references[dex_data->profile_index].num_method_ids = dex_data->num_method_ids;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001474 }
1475
Calin Juravlecc3171a2017-05-19 16:47:53 -07001476 return pmi;
Calin Juravle940eb0c2017-01-30 19:30:44 -08001477}
1478
1479
Andreas Gampea5b09a62016-11-17 15:21:22 -08001480bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001481 const DexFileData* dex_data = FindDexData(&dex_file);
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001482 if (dex_data != nullptr) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001483 const ArenaSet<dex::TypeIndex>& classes = dex_data->class_set;
Jeff Hao54b58552016-11-16 15:15:04 -08001484 return classes.find(type_idx) != classes.end();
Mathieu Chartiera8077802016-03-16 19:08:31 -07001485 }
1486 return false;
1487}
1488
Calin Juravle998c2162015-12-21 15:39:33 +02001489uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
1490 uint32_t total = 0;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001491 for (const DexFileData* dex_data : info_) {
1492 total += dex_data->method_map.size();
Calin Juravle998c2162015-12-21 15:39:33 +02001493 }
1494 return total;
1495}
1496
Calin Juravle67265462016-03-18 16:23:40 +00001497uint32_t ProfileCompilationInfo::GetNumberOfResolvedClasses() const {
1498 uint32_t total = 0;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001499 for (const DexFileData* dex_data : info_) {
1500 total += dex_data->class_set.size();
Calin Juravle67265462016-03-18 16:23:40 +00001501 }
1502 return total;
1503}
1504
David Sehrb18991b2017-02-08 20:58:10 -08001505// Produce a non-owning vector from a vector.
1506template<typename T>
1507const std::vector<T*>* MakeNonOwningVector(const std::vector<std::unique_ptr<T>>* owning_vector) {
1508 auto non_owning_vector = new std::vector<T*>();
1509 for (auto& element : *owning_vector) {
1510 non_owning_vector->push_back(element.get());
1511 }
1512 return non_owning_vector;
1513}
1514
1515std::string ProfileCompilationInfo::DumpInfo(
1516 const std::vector<std::unique_ptr<const DexFile>>* dex_files,
1517 bool print_full_dex_location) const {
1518 std::unique_ptr<const std::vector<const DexFile*>> non_owning_dex_files(
1519 MakeNonOwningVector(dex_files));
1520 return DumpInfo(non_owning_dex_files.get(), print_full_dex_location);
1521}
1522
Calin Juravle998c2162015-12-21 15:39:33 +02001523std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
1524 bool print_full_dex_location) const {
Calin Juravle226501b2015-12-11 14:41:31 +00001525 std::ostringstream os;
1526 if (info_.empty()) {
1527 return "ProfileInfo: empty";
1528 }
1529
1530 os << "ProfileInfo:";
1531
Calin Juravlea308a322017-07-18 16:51:51 -07001532 const std::string kFirstDexFileKeySubstitute = "!classes.dex";
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001533
1534 for (const DexFileData* dex_data : info_) {
Calin Juravle226501b2015-12-11 14:41:31 +00001535 os << "\n";
Calin Juravle226501b2015-12-11 14:41:31 +00001536 if (print_full_dex_location) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001537 os << dex_data->profile_key;
Calin Juravle226501b2015-12-11 14:41:31 +00001538 } else {
1539 // Replace the (empty) multidex suffix of the first key with a substitute for easier reading.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001540 std::string multidex_suffix = DexFile::GetMultiDexSuffix(dex_data->profile_key);
Calin Juravle226501b2015-12-11 14:41:31 +00001541 os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
1542 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001543 os << " [index=" << static_cast<uint32_t>(dex_data->profile_index) << "]";
Calin Juravle876f3502016-03-24 16:16:34 +00001544 const DexFile* dex_file = nullptr;
1545 if (dex_files != nullptr) {
1546 for (size_t i = 0; i < dex_files->size(); i++) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001547 if (dex_data->profile_key == (*dex_files)[i]->GetLocation()) {
Calin Juravle876f3502016-03-24 16:16:34 +00001548 dex_file = (*dex_files)[i];
Calin Juravle998c2162015-12-21 15:39:33 +02001549 }
Calin Juravle226501b2015-12-11 14:41:31 +00001550 }
Calin Juravle876f3502016-03-24 16:16:34 +00001551 }
Mathieu Chartier28b5c582017-06-06 14:12:50 -07001552 os << "\n\thot methods: ";
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001553 for (const auto& method_it : dex_data->method_map) {
Calin Juravle876f3502016-03-24 16:16:34 +00001554 if (dex_file != nullptr) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001555 os << "\n\t\t" << dex_file->PrettyMethod(method_it.first, true);
Calin Juravle876f3502016-03-24 16:16:34 +00001556 } else {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001557 os << method_it.first;
Calin Juravle876f3502016-03-24 16:16:34 +00001558 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001559
1560 os << "[";
1561 for (const auto& inline_cache_it : method_it.second) {
1562 os << "{" << std::hex << inline_cache_it.first << std::dec << ":";
Calin Juravle589e71e2017-03-03 16:05:05 -08001563 if (inline_cache_it.second.is_missing_types) {
1564 os << "MT";
1565 } else if (inline_cache_it.second.is_megamorphic) {
1566 os << "MM";
Calin Juravle940eb0c2017-01-30 19:30:44 -08001567 } else {
1568 for (const ClassReference& class_ref : inline_cache_it.second.classes) {
1569 os << "(" << static_cast<uint32_t>(class_ref.dex_profile_index)
1570 << "," << class_ref.type_index.index_ << ")";
1571 }
1572 }
1573 os << "}";
1574 }
1575 os << "], ";
Calin Juravle876f3502016-03-24 16:16:34 +00001576 }
Mathieu Chartier28b5c582017-06-06 14:12:50 -07001577 bool startup = true;
1578 while (true) {
1579 os << "\n\t" << (startup ? "startup methods: " : "post startup methods: ");
1580 for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001581 MethodHotness hotness_info(dex_data->GetHotnessInfo(method_idx));
1582 if (startup ? hotness_info.IsStartup() : hotness_info.IsPostStartup()) {
Mathieu Chartier28b5c582017-06-06 14:12:50 -07001583 os << method_idx << ", ";
1584 }
1585 }
1586 if (startup == false) {
1587 break;
1588 }
1589 startup = false;
1590 }
Calin Juravle876f3502016-03-24 16:16:34 +00001591 os << "\n\tclasses: ";
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001592 for (const auto class_it : dex_data->class_set) {
Calin Juravle876f3502016-03-24 16:16:34 +00001593 if (dex_file != nullptr) {
Jeff Hao54b58552016-11-16 15:15:04 -08001594 os << "\n\t\t" << dex_file->PrettyType(class_it);
Calin Juravle876f3502016-03-24 16:16:34 +00001595 } else {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001596 os << class_it.index_ << ",";
Calin Juravle876f3502016-03-24 16:16:34 +00001597 }
Calin Juravle226501b2015-12-11 14:41:31 +00001598 }
1599 }
1600 return os.str();
1601}
1602
Mathieu Chartierea650f32017-05-24 12:04:13 -07001603bool ProfileCompilationInfo::GetClassesAndMethods(
1604 const DexFile& dex_file,
1605 /*out*/std::set<dex::TypeIndex>* class_set,
1606 /*out*/std::set<uint16_t>* hot_method_set,
1607 /*out*/std::set<uint16_t>* startup_method_set,
1608 /*out*/std::set<uint16_t>* post_startup_method_method_set) const {
Mathieu Chartier34067262017-04-06 13:55:46 -07001609 std::set<std::string> ret;
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001610 const DexFileData* dex_data = FindDexData(&dex_file);
1611 if (dex_data == nullptr) {
Mathieu Chartier34067262017-04-06 13:55:46 -07001612 return false;
David Sehr7c80f2d2017-02-07 16:47:58 -08001613 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001614 for (const auto& it : dex_data->method_map) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001615 hot_method_set->insert(it.first);
1616 }
1617 for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001618 MethodHotness hotness = dex_data->GetHotnessInfo(method_idx);
1619 if (hotness.IsStartup()) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001620 startup_method_set->insert(method_idx);
1621 }
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001622 if (hotness.IsPostStartup()) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001623 post_startup_method_method_set->insert(method_idx);
1624 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001625 }
Calin Juravlecc3171a2017-05-19 16:47:53 -07001626 for (const dex::TypeIndex& type_index : dex_data->class_set) {
1627 class_set->insert(type_index);
1628 }
Mathieu Chartier34067262017-04-06 13:55:46 -07001629 return true;
David Sehr7c80f2d2017-02-07 16:47:58 -08001630}
1631
Calin Juravle2e2db782016-02-23 12:00:03 +00001632bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001633 // No need to compare profile_key_map_. That's only a cache for fast search.
1634 // All the information is already in the info_ vector.
1635 if (info_.size() != other.info_.size()) {
1636 return false;
1637 }
1638 for (size_t i = 0; i < info_.size(); i++) {
1639 const DexFileData& dex_data = *info_[i];
1640 const DexFileData& other_dex_data = *other.info_[i];
1641 if (!(dex_data == other_dex_data)) {
1642 return false;
1643 }
1644 }
1645 return true;
Calin Juravle877fd962016-01-05 14:29:29 +00001646}
1647
Mathieu Chartier046854b2017-03-01 17:16:22 -08001648std::set<DexCacheResolvedClasses> ProfileCompilationInfo::GetResolvedClasses(
Calin Juravle08556882017-05-26 16:40:45 -07001649 const std::vector<const DexFile*>& dex_files) const {
1650 std::unordered_map<std::string, const DexFile* > key_to_dex_file;
1651 for (const DexFile* dex_file : dex_files) {
1652 key_to_dex_file.emplace(GetProfileDexFileKey(dex_file->GetLocation()), dex_file);
Mathieu Chartier046854b2017-03-01 17:16:22 -08001653 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -08001654 std::set<DexCacheResolvedClasses> ret;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001655 for (const DexFileData* dex_data : info_) {
Calin Juravle08556882017-05-26 16:40:45 -07001656 const auto it = key_to_dex_file.find(dex_data->profile_key);
1657 if (it != key_to_dex_file.end()) {
1658 const DexFile* dex_file = it->second;
1659 const std::string& dex_location = dex_file->GetLocation();
1660 if (dex_data->checksum != it->second->GetLocationChecksum()) {
1661 LOG(ERROR) << "Dex checksum mismatch when getting resolved classes from profile for "
1662 << "location " << dex_location << " (checksum=" << dex_file->GetLocationChecksum()
1663 << ", profile checksum=" << dex_data->checksum;
1664 return std::set<DexCacheResolvedClasses>();
1665 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001666 DexCacheResolvedClasses classes(dex_location,
1667 dex_location,
1668 dex_data->checksum,
1669 dex_data->num_method_ids);
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001670 classes.AddClasses(dex_data->class_set.begin(), dex_data->class_set.end());
Mathieu Chartier046854b2017-03-01 17:16:22 -08001671 ret.insert(classes);
1672 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -08001673 }
1674 return ret;
1675}
1676
Calin Juravle7bcdb532016-06-07 16:14:47 +01001677// Naive implementation to generate a random profile file suitable for testing.
1678bool ProfileCompilationInfo::GenerateTestProfile(int fd,
1679 uint16_t number_of_dex_files,
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001680 uint16_t method_percentage,
1681 uint16_t class_percentage,
Jeff Haof0a31f82017-03-27 15:50:37 -07001682 uint32_t random_seed) {
Calin Juravle7bcdb532016-06-07 16:14:47 +01001683 const std::string base_dex_location = "base.apk";
1684 ProfileCompilationInfo info;
1685 // The limits are defined by the dex specification.
Mathieu Chartierea650f32017-05-24 12:04:13 -07001686 const uint16_t max_method = std::numeric_limits<uint16_t>::max();
1687 const uint16_t max_classes = std::numeric_limits<uint16_t>::max();
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001688 uint16_t number_of_methods = max_method * method_percentage / 100;
1689 uint16_t number_of_classes = max_classes * class_percentage / 100;
Calin Juravle7bcdb532016-06-07 16:14:47 +01001690
Jeff Haof0a31f82017-03-27 15:50:37 -07001691 std::srand(random_seed);
Calin Juravle7bcdb532016-06-07 16:14:47 +01001692
1693 // Make sure we generate more samples with a low index value.
1694 // This makes it more likely to hit valid method/class indices in small apps.
1695 const uint16_t kFavorFirstN = 10000;
1696 const uint16_t kFavorSplit = 2;
1697
1698 for (uint16_t i = 0; i < number_of_dex_files; i++) {
1699 std::string dex_location = DexFile::GetMultiDexLocation(i, base_dex_location.c_str());
1700 std::string profile_key = GetProfileDexFileKey(dex_location);
1701
1702 for (uint16_t m = 0; m < number_of_methods; m++) {
1703 uint16_t method_idx = rand() % max_method;
1704 if (m < (number_of_methods / kFavorSplit)) {
1705 method_idx %= kFavorFirstN;
1706 }
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001707 info.AddMethodIndex(MethodHotness::kFlagHot,
1708 profile_key,
1709 /*method_idx*/ 0,
1710 method_idx,
1711 max_method);
Calin Juravle7bcdb532016-06-07 16:14:47 +01001712 }
1713
1714 for (uint16_t c = 0; c < number_of_classes; c++) {
Jeff Hao54b58552016-11-16 15:15:04 -08001715 uint16_t type_idx = rand() % max_classes;
Calin Juravle7bcdb532016-06-07 16:14:47 +01001716 if (c < (number_of_classes / kFavorSplit)) {
Jeff Hao54b58552016-11-16 15:15:04 -08001717 type_idx %= kFavorFirstN;
Calin Juravle7bcdb532016-06-07 16:14:47 +01001718 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001719 info.AddClassIndex(profile_key, 0, dex::TypeIndex(type_idx), max_method);
Calin Juravle7bcdb532016-06-07 16:14:47 +01001720 }
1721 }
1722 return info.Save(fd);
1723}
1724
Jeff Haof0a31f82017-03-27 15:50:37 -07001725// Naive implementation to generate a random profile file suitable for testing.
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001726// Description of random selection:
1727// * Select a random starting point S.
1728// * For every index i, add (S+i) % (N - total number of methods/classes) to profile with the
1729// probably of 1/(N - i - number of methods/classes needed to add in profile).
Jeff Haof0a31f82017-03-27 15:50:37 -07001730bool ProfileCompilationInfo::GenerateTestProfile(
1731 int fd,
1732 std::vector<std::unique_ptr<const DexFile>>& dex_files,
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001733 uint16_t method_percentage,
1734 uint16_t class_percentage,
Jeff Haof0a31f82017-03-27 15:50:37 -07001735 uint32_t random_seed) {
1736 std::srand(random_seed);
1737 ProfileCompilationInfo info;
1738 for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
1739 const std::string& location = dex_file->GetLocation();
1740 uint32_t checksum = dex_file->GetLocationChecksum();
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001741
1742 uint32_t number_of_classes = dex_file->NumClassDefs();
1743 uint32_t classes_required_in_profile = (number_of_classes * class_percentage) / 100;
1744 uint32_t class_start_index = rand() % number_of_classes;
1745 for (uint32_t i = 0; i < number_of_classes && classes_required_in_profile; ++i) {
1746 if (number_of_classes - i == classes_required_in_profile ||
1747 std::rand() % (number_of_classes - i - classes_required_in_profile) == 0) {
1748 uint32_t class_index = (i + class_start_index) % number_of_classes;
Mathieu Chartierea650f32017-05-24 12:04:13 -07001749 info.AddClassIndex(location,
1750 checksum,
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001751 dex_file->GetClassDef(class_index).class_idx_,
Mathieu Chartierea650f32017-05-24 12:04:13 -07001752 dex_file->NumMethodIds());
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001753 classes_required_in_profile--;
Jeff Haof0a31f82017-03-27 15:50:37 -07001754 }
1755 }
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001756
1757 uint32_t number_of_methods = dex_file->NumMethodIds();
1758 uint32_t methods_required_in_profile = (number_of_methods * method_percentage) / 100;
1759 uint32_t method_start_index = rand() % number_of_methods;
1760 for (uint32_t i = 0; i < number_of_methods && methods_required_in_profile; ++i) {
1761 if (number_of_methods - i == methods_required_in_profile ||
1762 std::rand() % (number_of_methods - i - methods_required_in_profile) == 0) {
1763 uint32_t method_index = (method_start_index + i) % number_of_methods;
1764 info.AddMethodIndex(MethodHotness::kFlagHot, MethodReference(dex_file.get(),
1765 method_index));
1766 methods_required_in_profile--;
Jeff Haof0a31f82017-03-27 15:50:37 -07001767 }
1768 }
1769 }
1770 return info.Save(fd);
1771}
1772
Calin Juravle940eb0c2017-01-30 19:30:44 -08001773bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==(
1774 const OfflineProfileMethodInfo& other) const {
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001775 if (inline_caches->size() != other.inline_caches->size()) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001776 return false;
1777 }
1778
1779 // We can't use a simple equality test because we need to match the dex files
Calin Juravle589e71e2017-03-03 16:05:05 -08001780 // of the inline caches which might have different profile indexes.
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001781 for (const auto& inline_cache_it : *inline_caches) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001782 uint16_t dex_pc = inline_cache_it.first;
1783 const DexPcData dex_pc_data = inline_cache_it.second;
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001784 const auto& other_it = other.inline_caches->find(dex_pc);
1785 if (other_it == other.inline_caches->end()) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001786 return false;
1787 }
1788 const DexPcData& other_dex_pc_data = other_it->second;
Calin Juravle589e71e2017-03-03 16:05:05 -08001789 if (dex_pc_data.is_megamorphic != other_dex_pc_data.is_megamorphic ||
1790 dex_pc_data.is_missing_types != other_dex_pc_data.is_missing_types) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001791 return false;
1792 }
1793 for (const ClassReference& class_ref : dex_pc_data.classes) {
1794 bool found = false;
1795 for (const ClassReference& other_class_ref : other_dex_pc_data.classes) {
1796 CHECK_LE(class_ref.dex_profile_index, dex_references.size());
1797 CHECK_LE(other_class_ref.dex_profile_index, other.dex_references.size());
1798 const DexReference& dex_ref = dex_references[class_ref.dex_profile_index];
1799 const DexReference& other_dex_ref = other.dex_references[other_class_ref.dex_profile_index];
1800 if (class_ref.type_index == other_class_ref.type_index &&
1801 dex_ref == other_dex_ref) {
1802 found = true;
1803 break;
1804 }
1805 }
1806 if (!found) {
1807 return false;
1808 }
1809 }
1810 }
1811 return true;
1812}
1813
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001814bool ProfileCompilationInfo::IsEmpty() const {
1815 DCHECK_EQ(info_.empty(), profile_key_map_.empty());
1816 return info_.empty();
1817}
1818
Calin Juravlecc3171a2017-05-19 16:47:53 -07001819ProfileCompilationInfo::InlineCacheMap*
1820ProfileCompilationInfo::DexFileData::FindOrAddMethod(uint16_t method_index) {
1821 return &(method_map.FindOrAdd(
1822 method_index,
1823 InlineCacheMap(std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile)))->second);
1824}
1825
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001826// Mark a method as executed at least once.
Calin Juravle1ad1e3f2017-09-19 18:20:37 -07001827bool ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, size_t index) {
1828 if (index >= num_method_ids) {
1829 LOG(ERROR) << "Invalid method index " << index << ". num_method_ids=" << num_method_ids;
1830 return false;
1831 }
1832
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001833 if ((flags & MethodHotness::kFlagStartup) != 0) {
1834 method_bitmap.StoreBit(MethodBitIndex(/*startup*/ true, index), /*value*/ true);
1835 }
1836 if ((flags & MethodHotness::kFlagPostStartup) != 0) {
1837 method_bitmap.StoreBit(MethodBitIndex(/*startup*/ false, index), /*value*/ true);
1838 }
1839 if ((flags & MethodHotness::kFlagHot) != 0) {
1840 method_map.FindOrAdd(
1841 index,
1842 InlineCacheMap(std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile)));
1843 }
Calin Juravle1ad1e3f2017-09-19 18:20:37 -07001844 return true;
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001845}
1846
1847ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::DexFileData::GetHotnessInfo(
1848 uint32_t dex_method_index) const {
1849 MethodHotness ret;
1850 if (method_bitmap.LoadBit(MethodBitIndex(/*startup*/ true, dex_method_index))) {
1851 ret.AddFlag(MethodHotness::kFlagStartup);
1852 }
1853 if (method_bitmap.LoadBit(MethodBitIndex(/*startup*/ false, dex_method_index))) {
1854 ret.AddFlag(MethodHotness::kFlagPostStartup);
1855 }
1856 auto it = method_map.find(dex_method_index);
1857 if (it != method_map.end()) {
1858 ret.SetInlineCacheMap(&it->second);
1859 ret.AddFlag(MethodHotness::kFlagHot);
1860 }
1861 return ret;
1862}
1863
Calin Juravlecc3171a2017-05-19 16:47:53 -07001864ProfileCompilationInfo::DexPcData*
1865ProfileCompilationInfo::FindOrAddDexPc(InlineCacheMap* inline_cache, uint32_t dex_pc) {
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001866 return &(inline_cache->FindOrAdd(dex_pc, DexPcData(&arena_))->second);
Calin Juravlecc3171a2017-05-19 16:47:53 -07001867}
1868
Mathieu Chartier4f342b02017-07-21 17:12:39 -07001869std::unordered_set<std::string> ProfileCompilationInfo::GetClassDescriptors(
1870 const std::vector<const DexFile*>& dex_files) {
1871 std::unordered_set<std::string> ret;
1872 for (const DexFile* dex_file : dex_files) {
1873 const DexFileData* data = FindDexData(dex_file);
1874 if (data != nullptr) {
1875 for (dex::TypeIndex type_idx : data->class_set) {
1876 if (!dex_file->IsTypeIndexValid(type_idx)) {
1877 // Something went bad. The profile is probably corrupted. Abort and return an emtpy set.
1878 LOG(WARNING) << "Corrupted profile: invalid type index "
1879 << type_idx.index_ << " in dex " << dex_file->GetLocation();
1880 return std::unordered_set<std::string>();
1881 }
1882 const DexFile::TypeId& type_id = dex_file->GetTypeId(type_idx);
1883 ret.insert(dex_file->GetTypeDescriptor(type_id));
1884 }
1885 } else {
1886 VLOG(compiler) << "Failed to find profile data for " << dex_file->GetLocation();
1887 }
1888 }
1889 return ret;
1890}
1891
Calin Juravle31f2c152015-10-23 17:56:15 +01001892} // namespace art