blob: 0b7063d90f62ef8a0c77ddac3cca7dae5ae51424 [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 Juravle64142952016-03-21 14:37:55 +000019#include "errno.h"
20#include <limits.h>
Shubham Ajmera4d198e02017-05-12 17:45:29 +000021#include <string>
Calin Juravle4d77b6a2015-12-01 18:38:09 +000022#include <vector>
Calin Juravle7bcdb532016-06-07 16:14:47 +010023#include <stdlib.h>
Calin Juravle31f2c152015-10-23 17:56:15 +010024#include <sys/file.h>
25#include <sys/stat.h>
26#include <sys/uio.h>
Shubham Ajmera4d198e02017-05-12 17:45:29 +000027#include <sys/types.h>
28#include <unistd.h>
29#include <sys/types.h>
30#include <unistd.h>
31#include <zlib.h>
32#include <base/time_utils.h>
Calin Juravle31f2c152015-10-23 17:56:15 +010033
Calin Juravlecc3171a2017-05-19 16:47:53 -070034#include "base/arena_allocator.h"
35#include "base/dumpable.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010036#include "base/mutex.h"
Calin Juravle877fd962016-01-05 14:29:29 +000037#include "base/scoped_flock.h"
Calin Juravle66f55232015-12-08 15:09:10 +000038#include "base/stl_util.h"
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -080039#include "base/systrace.h"
Calin Juravle877fd962016-01-05 14:29:29 +000040#include "base/unix_file/fd_file.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010041#include "jit/profiling_info.h"
Calin Juravle877fd962016-01-05 14:29:29 +000042#include "os.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010043#include "safe_map.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080044#include "utils.h"
Shubham Ajmera4d198e02017-05-12 17:45:29 +000045#include "android-base/file.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010046
47namespace art {
48
Calin Juravle64142952016-03-21 14:37:55 +000049const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' };
Calin Juravlea308a322017-07-18 16:51:51 -070050// Last profile version: update the multidex separator.
51const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '9', '\0' };
Calin Juravle64142952016-03-21 14:37:55 +000052
53static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
54
Calin Juravlec4588572016-06-08 14:24:13 +010055// 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 +010056// Used to facilitate testing profile guided compilation across a large number of apps
Calin Juravlec4588572016-06-08 14:24:13 +010057// using the same test profile.
58static constexpr bool kDebugIgnoreChecksum = false;
59
Calin Juravle589e71e2017-03-03 16:05:05 -080060static constexpr uint8_t kIsMissingTypesEncoding = 6;
61static constexpr uint8_t kIsMegamorphicEncoding = 7;
Calin Juravle940eb0c2017-01-30 19:30:44 -080062
63static_assert(sizeof(InlineCache::kIndividualCacheSize) == sizeof(uint8_t),
64 "InlineCache::kIndividualCacheSize does not have the expect type size");
Calin Juravle589e71e2017-03-03 16:05:05 -080065static_assert(InlineCache::kIndividualCacheSize < kIsMegamorphicEncoding,
66 "InlineCache::kIndividualCacheSize is larger than expected");
67static_assert(InlineCache::kIndividualCacheSize < kIsMissingTypesEncoding,
Calin Juravle940eb0c2017-01-30 19:30:44 -080068 "InlineCache::kIndividualCacheSize is larger than expected");
69
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -070070static bool ChecksumMatch(uint32_t dex_file_checksum, uint32_t checksum) {
71 return kDebugIgnoreChecksum || dex_file_checksum == checksum;
72}
73
Calin Juravlecc3171a2017-05-19 16:47:53 -070074ProfileCompilationInfo::ProfileCompilationInfo(ArenaPool* custom_arena_pool)
Calin Juravlee6f87cc2017-05-24 17:41:05 -070075 : default_arena_pool_(),
76 arena_(custom_arena_pool),
77 info_(arena_.Adapter(kArenaAllocProfile)),
78 profile_key_map_(std::less<const std::string>(), arena_.Adapter(kArenaAllocProfile)) {
Calin Juravlecc3171a2017-05-19 16:47:53 -070079}
80
81ProfileCompilationInfo::ProfileCompilationInfo()
Calin Juravlee6f87cc2017-05-24 17:41:05 -070082 : default_arena_pool_(/*use_malloc*/true, /*low_4gb*/false, "ProfileCompilationInfo"),
83 arena_(&default_arena_pool_),
84 info_(arena_.Adapter(kArenaAllocProfile)),
85 profile_key_map_(std::less<const std::string>(), arena_.Adapter(kArenaAllocProfile)) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -070086}
87
88ProfileCompilationInfo::~ProfileCompilationInfo() {
Calin Juravlee6f87cc2017-05-24 17:41:05 -070089 VLOG(profiler) << Dumpable<MemStats>(arena_.GetMemStats());
Calin Juravle798ba162017-05-23 23:01:53 -070090 for (DexFileData* data : info_) {
91 delete data;
92 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -070093}
94
Calin Juravle940eb0c2017-01-30 19:30:44 -080095void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx,
96 const dex::TypeIndex& type_idx) {
Calin Juravle589e71e2017-03-03 16:05:05 -080097 if (is_megamorphic || is_missing_types) {
Calin Juravle940eb0c2017-01-30 19:30:44 -080098 return;
99 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700100
101 // Perform an explicit lookup for the type instead of directly emplacing the
102 // element. We do this because emplace() allocates the node before doing the
103 // lookup and if it then finds an identical element, it shall deallocate the
104 // node. For Arena allocations, that's essentially a leak.
105 ClassReference ref(dex_profile_idx, type_idx);
106 auto it = classes.find(ref);
107 if (it != classes.end()) {
108 // The type index exists.
109 return;
110 }
111
112 // Check if the adding the type will cause the cache to become megamorphic.
113 if (classes.size() + 1 >= InlineCache::kIndividualCacheSize) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800114 is_megamorphic = true;
115 classes.clear();
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700116 return;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800117 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700118
119 // The type does not exist and the inline cache will not be megamorphic.
120 classes.insert(ref);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800121}
122
Calin Juravle34900cc2016-02-05 16:19:19 +0000123// Transform the actual dex location into relative paths.
124// Note: this is OK because we don't store profiles of different apps into the same file.
125// Apps with split apks don't cause trouble because each split has a different name and will not
126// collide with other entries.
Calin Juravle31708b72016-02-05 19:44:05 +0000127std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_location) {
Calin Juravle34900cc2016-02-05 16:19:19 +0000128 DCHECK(!dex_location.empty());
129 size_t last_sep_index = dex_location.find_last_of('/');
130 if (last_sep_index == std::string::npos) {
131 return dex_location;
132 } else {
Calin Juravle31708b72016-02-05 19:44:05 +0000133 DCHECK(last_sep_index < dex_location.size());
134 return dex_location.substr(last_sep_index + 1);
Calin Juravle34900cc2016-02-05 16:19:19 +0000135 }
136}
137
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700138bool ProfileCompilationInfo::AddMethodIndex(MethodHotness::Flag flags, const MethodReference& ref) {
139 DexFileData* data = GetOrAddDexFileData(ref.dex_file);
140 if (data == nullptr) {
141 return false;
142 }
143 data->AddMethod(flags, ref.dex_method_index);
144 return true;
145}
146
147bool ProfileCompilationInfo::AddMethodIndex(MethodHotness::Flag flags,
148 const std::string& dex_location,
149 uint32_t checksum,
150 uint16_t method_idx,
151 uint32_t num_method_ids) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700152 DexFileData* data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location),
153 checksum,
154 num_method_ids);
155 if (data == nullptr) {
156 return false;
157 }
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700158 data->AddMethod(flags, method_idx);
Mathieu Chartierea650f32017-05-24 12:04:13 -0700159 return true;
160}
161
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700162bool ProfileCompilationInfo::AddMethods(const std::vector<ProfileMethodInfo>& methods) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800163 for (const ProfileMethodInfo& method : methods) {
164 if (!AddMethod(method)) {
Calin Juravle67265462016-03-18 16:23:40 +0000165 return false;
166 }
167 }
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700168 return true;
169}
170
171bool ProfileCompilationInfo::AddClasses(const std::set<DexCacheResolvedClasses>& resolved_classes) {
Calin Juravle67265462016-03-18 16:23:40 +0000172 for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
173 if (!AddResolvedClasses(dex_cache)) {
174 return false;
175 }
176 }
177 return true;
178}
179
Calin Juravledcab1902017-05-12 19:18:47 -0700180bool ProfileCompilationInfo::Load(const std::string& filename, bool clear_if_invalid) {
Calin Juravle67265462016-03-18 16:23:40 +0000181 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravle67265462016-03-18 16:23:40 +0000182 std::string error;
Calin Juravledf674c42017-04-27 19:30:16 -0700183 int flags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
184 // There's no need to fsync profile data right away. We get many chances
185 // to write it again in case something goes wrong. We can rely on a simple
186 // close(), no sync, and let to the kernel decide when to write to disk.
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100187 ScopedFlock profile_file = LockedFile::Open(filename.c_str(), flags,
188 /*block*/false, &error);
189
190 if (profile_file.get() == nullptr) {
Calin Juravle67265462016-03-18 16:23:40 +0000191 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
192 return false;
193 }
194
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100195 int fd = profile_file->Fd();
Calin Juravle67265462016-03-18 16:23:40 +0000196
Calin Juravledcab1902017-05-12 19:18:47 -0700197 ProfileLoadSatus status = LoadInternal(fd, &error);
Calin Juravle5d1bd0a2016-03-24 20:33:22 +0000198 if (status == kProfileLoadSuccess) {
Calin Juravledcab1902017-05-12 19:18:47 -0700199 return true;
200 }
201
202 if (clear_if_invalid &&
203 ((status == kProfileLoadVersionMismatch) || (status == kProfileLoadBadData))) {
204 LOG(WARNING) << "Clearing bad or obsolete profile data from file "
205 << filename << ": " << error;
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100206 if (profile_file->ClearContent()) {
Calin Juravledcab1902017-05-12 19:18:47 -0700207 return true;
Calin Juravle5d1bd0a2016-03-24 20:33:22 +0000208 } else {
Calin Juravledcab1902017-05-12 19:18:47 -0700209 PLOG(WARNING) << "Could not clear profile file: " << filename;
210 return false;
Calin Juravle5d1bd0a2016-03-24 20:33:22 +0000211 }
Calin Juravledcab1902017-05-12 19:18:47 -0700212 }
213
214 LOG(WARNING) << "Could not load profile data from file " << filename << ": " << error;
215 return false;
216}
217
218bool ProfileCompilationInfo::Save(const std::string& filename, uint64_t* bytes_written) {
219 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravledcab1902017-05-12 19:18:47 -0700220 std::string error;
221 int flags = O_WRONLY | O_NOFOLLOW | O_CLOEXEC;
222 // There's no need to fsync profile data right away. We get many chances
223 // to write it again in case something goes wrong. We can rely on a simple
224 // close(), no sync, and let to the kernel decide when to write to disk.
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100225 ScopedFlock profile_file = LockedFile::Open(filename.c_str(), flags,
226 /*block*/false, &error);
227 if (profile_file.get() == nullptr) {
Calin Juravledcab1902017-05-12 19:18:47 -0700228 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
Calin Juravle67265462016-03-18 16:23:40 +0000229 return false;
230 }
231
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100232 int fd = profile_file->Fd();
Calin Juravledcab1902017-05-12 19:18:47 -0700233
Calin Juravle5d1bd0a2016-03-24 20:33:22 +0000234 // We need to clear the data because we don't support appending to the profiles yet.
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100235 if (!profile_file->ClearContent()) {
Calin Juravle67265462016-03-18 16:23:40 +0000236 PLOG(WARNING) << "Could not clear profile file: " << filename;
237 return false;
238 }
239
240 // This doesn't need locking because we are trying to lock the file for exclusive
241 // access and fail immediately if we can't.
242 bool result = Save(fd);
243 if (result) {
Calin Juravledcab1902017-05-12 19:18:47 -0700244 int64_t size = GetFileSizeBytes(filename);
245 if (size != -1) {
246 VLOG(profiler)
247 << "Successfully saved profile info to " << filename << " Size: "
248 << size;
249 if (bytes_written != nullptr) {
250 *bytes_written = static_cast<uint64_t>(size);
251 }
Calin Juravle67265462016-03-18 16:23:40 +0000252 }
253 } else {
254 VLOG(profiler) << "Failed to save profile info to " << filename;
255 }
256 return result;
257}
258
Calin Juravle64142952016-03-21 14:37:55 +0000259// Returns true if all the bytes were successfully written to the file descriptor.
260static bool WriteBuffer(int fd, const uint8_t* buffer, size_t byte_count) {
261 while (byte_count > 0) {
262 int bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer, byte_count));
263 if (bytes_written == -1) {
Calin Juravle877fd962016-01-05 14:29:29 +0000264 return false;
265 }
Calin Juravle64142952016-03-21 14:37:55 +0000266 byte_count -= bytes_written; // Reduce the number of remaining bytes.
267 buffer += bytes_written; // Move the buffer forward.
268 }
Calin Juravle877fd962016-01-05 14:29:29 +0000269 return true;
Calin Juravle31f2c152015-10-23 17:56:15 +0100270}
271
Calin Juravle64142952016-03-21 14:37:55 +0000272// Add the string bytes to the buffer.
273static void AddStringToBuffer(std::vector<uint8_t>* buffer, const std::string& value) {
274 buffer->insert(buffer->end(), value.begin(), value.end());
275}
276
277// Insert each byte, from low to high into the buffer.
278template <typename T>
279static void AddUintToBuffer(std::vector<uint8_t>* buffer, T value) {
280 for (size_t i = 0; i < sizeof(T); i++) {
281 buffer->push_back((value >> (i * kBitsPerByte)) & 0xff);
282 }
283}
284
285static constexpr size_t kLineHeaderSize =
Calin Juravle940eb0c2017-01-30 19:30:44 -0800286 2 * sizeof(uint16_t) + // class_set.size + dex_location.size
Mathieu Chartierea650f32017-05-24 12:04:13 -0700287 3 * sizeof(uint32_t); // method_map.size + checksum + num_method_ids
Calin Juravle31f2c152015-10-23 17:56:15 +0100288
289/**
290 * Serialization format:
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000291 * magic,version,number_of_dex_files,uncompressed_size_of_zipped_data,compressed_data_size,
Mathieu Chartiercebf99c2017-06-05 11:06:52 -0700292 * zipped[dex_location1,number_of_classes1,methods_region_size,dex_location_checksum1
293 * num_method_ids,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800294 * method_encoding_11,method_encoding_12...,class_id1,class_id2...
Mathieu Chartiercebf99c2017-06-05 11:06:52 -0700295 * startup/post startup bitmap,
296 * dex_location2,number_of_classes2,methods_region_size,dex_location_checksum2, num_method_ids,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800297 * method_encoding_21,method_encoding_22...,,class_id1,class_id2...
Mathieu Chartiercebf99c2017-06-05 11:06:52 -0700298 * startup/post startup bitmap,
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000299 * .....]
Calin Juravle940eb0c2017-01-30 19:30:44 -0800300 * The method_encoding is:
301 * method_id,number_of_inline_caches,inline_cache1,inline_cache2...
302 * The inline_cache is:
303 * dex_pc,[M|dex_map_size], dex_profile_index,class_id1,class_id2...,dex_profile_index2,...
304 * dex_map_size is the number of dex_indeces that follows.
305 * Classes are grouped per their dex files and the line
306 * `dex_profile_index,class_id1,class_id2...,dex_profile_index2,...` encodes the
307 * mapping from `dex_profile_index` to the set of classes `class_id1,class_id2...`
Calin Juravle589e71e2017-03-03 16:05:05 -0800308 * M stands for megamorphic or missing types and it's encoded as either
309 * the byte kIsMegamorphicEncoding or kIsMissingTypesEncoding.
Calin Juravle940eb0c2017-01-30 19:30:44 -0800310 * When present, there will be no class ids following.
Calin Juravle31f2c152015-10-23 17:56:15 +0100311 **/
Calin Juravle2e2db782016-02-23 12:00:03 +0000312bool ProfileCompilationInfo::Save(int fd) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000313 uint64_t start = NanoTime();
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -0800314 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravle2e2db782016-02-23 12:00:03 +0000315 DCHECK_GE(fd, 0);
Calin Juravle64142952016-03-21 14:37:55 +0000316
Calin Juravle64142952016-03-21 14:37:55 +0000317 // Use a vector wrapper to avoid keeping track of offsets when we add elements.
318 std::vector<uint8_t> buffer;
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000319 if (!WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic))) {
320 return false;
321 }
322 if (!WriteBuffer(fd, kProfileVersion, sizeof(kProfileVersion))) {
323 return false;
324 }
Calin Juravle940eb0c2017-01-30 19:30:44 -0800325 DCHECK_LE(info_.size(), std::numeric_limits<uint8_t>::max());
326 AddUintToBuffer(&buffer, static_cast<uint8_t>(info_.size()));
Calin Juravle64142952016-03-21 14:37:55 +0000327
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000328 uint32_t required_capacity = 0;
329 for (const DexFileData* dex_data_ptr : info_) {
330 const DexFileData& dex_data = *dex_data_ptr;
331 uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
332 required_capacity += kLineHeaderSize +
333 dex_data.profile_key.size() +
334 sizeof(uint16_t) * dex_data.class_set.size() +
Mathieu Chartierea650f32017-05-24 12:04:13 -0700335 methods_region_size +
336 dex_data.bitmap_storage.size();
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000337 }
Mathieu Chartierf2e2af82017-07-12 21:09:57 -0700338 // Allow large profiles for non target builds for the case where we are merging many profiles
339 // to generate a boot image profile.
340 if (kIsTargetBuild && required_capacity > kProfileSizeErrorThresholdInBytes) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000341 LOG(ERROR) << "Profile data size exceeds "
342 << std::to_string(kProfileSizeErrorThresholdInBytes)
343 << " bytes. Profile will not be written to disk.";
344 return false;
345 }
346 if (required_capacity > kProfileSizeWarningThresholdInBytes) {
347 LOG(WARNING) << "Profile data size exceeds "
348 << std::to_string(kProfileSizeWarningThresholdInBytes);
349 }
350 AddUintToBuffer(&buffer, required_capacity);
351 if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
352 return false;
353 }
354 // Make sure that the buffer has enough capacity to avoid repeated resizings
355 // while we add data.
356 buffer.reserve(required_capacity);
357 buffer.clear();
358
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700359 // Dex files must be written in the order of their profile index. This
Calin Juravlee0ac1152017-02-13 19:03:47 -0800360 // avoids writing the index in the output file and simplifies the parsing logic.
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700361 for (const DexFileData* dex_data_ptr : info_) {
362 const DexFileData& dex_data = *dex_data_ptr;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800363
364 // Note that we allow dex files without any methods or classes, so that
365 // inline caches can refer valid dex files.
Calin Juravle31f2c152015-10-23 17:56:15 +0100366
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700367 if (dex_data.profile_key.size() >= kMaxDexFileKeyLength) {
Calin Juravle64142952016-03-21 14:37:55 +0000368 LOG(WARNING) << "DexFileKey exceeds allocated limit";
369 return false;
370 }
371
Calin Juravle940eb0c2017-01-30 19:30:44 -0800372 uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
Calin Juravle64142952016-03-21 14:37:55 +0000373
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700374 DCHECK_LE(dex_data.profile_key.size(), std::numeric_limits<uint16_t>::max());
Calin Juravle64142952016-03-21 14:37:55 +0000375 DCHECK_LE(dex_data.class_set.size(), std::numeric_limits<uint16_t>::max());
Mathieu Chartierea650f32017-05-24 12:04:13 -0700376 // Write profile line header.
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700377 AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.profile_key.size()));
Calin Juravle64142952016-03-21 14:37:55 +0000378 AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.class_set.size()));
Calin Juravle940eb0c2017-01-30 19:30:44 -0800379 AddUintToBuffer(&buffer, methods_region_size); // uint32_t
Calin Juravle64142952016-03-21 14:37:55 +0000380 AddUintToBuffer(&buffer, dex_data.checksum); // uint32_t
Mathieu Chartierea650f32017-05-24 12:04:13 -0700381 AddUintToBuffer(&buffer, dex_data.num_method_ids); // uint32_t
Calin Juravle64142952016-03-21 14:37:55 +0000382
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700383 AddStringToBuffer(&buffer, dex_data.profile_key);
Calin Juravle64142952016-03-21 14:37:55 +0000384
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000385 uint16_t last_method_index = 0;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800386 for (const auto& method_it : dex_data.method_map) {
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000387 // Store the difference between the method indices. The SafeMap is ordered by
388 // method_id, so the difference will always be non negative.
389 DCHECK_GE(method_it.first, last_method_index);
390 uint16_t diff_with_last_method_index = method_it.first - last_method_index;
391 last_method_index = method_it.first;
392 AddUintToBuffer(&buffer, diff_with_last_method_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800393 AddInlineCacheToBuffer(&buffer, method_it.second);
Calin Juravle31f2c152015-10-23 17:56:15 +0100394 }
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000395
396 uint16_t last_class_index = 0;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800397 for (const auto& class_id : dex_data.class_set) {
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000398 // Store the difference between the class indices. The set is ordered by
399 // class_id, so the difference will always be non negative.
400 DCHECK_GE(class_id.index_, last_class_index);
401 uint16_t diff_with_last_class_index = class_id.index_ - last_class_index;
402 last_class_index = class_id.index_;
403 AddUintToBuffer(&buffer, diff_with_last_class_index);
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800404 }
Mathieu Chartierea650f32017-05-24 12:04:13 -0700405
406 buffer.insert(buffer.end(),
407 dex_data.bitmap_storage.begin(),
408 dex_data.bitmap_storage.end());
Calin Juravle31f2c152015-10-23 17:56:15 +0100409 }
410
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000411 uint32_t output_size = 0;
412 std::unique_ptr<uint8_t[]> compressed_buffer = DeflateBuffer(buffer.data(),
413 required_capacity,
414 &output_size);
415
416 buffer.clear();
417 AddUintToBuffer(&buffer, output_size);
418
419 if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
420 return false;
421 }
422 if (!WriteBuffer(fd, compressed_buffer.get(), output_size)) {
423 return false;
424 }
425 uint64_t total_time = NanoTime() - start;
426 VLOG(profiler) << "Compressed from "
427 << std::to_string(required_capacity)
428 << " to "
429 << std::to_string(output_size);
430 VLOG(profiler) << "Time to save profile: " << std::to_string(total_time);
431 return true;
Calin Juravle226501b2015-12-11 14:41:31 +0000432}
433
Calin Juravle940eb0c2017-01-30 19:30:44 -0800434void ProfileCompilationInfo::AddInlineCacheToBuffer(std::vector<uint8_t>* buffer,
435 const InlineCacheMap& inline_cache_map) {
436 // Add inline cache map size.
437 AddUintToBuffer(buffer, static_cast<uint16_t>(inline_cache_map.size()));
438 if (inline_cache_map.size() == 0) {
439 return;
440 }
441 for (const auto& inline_cache_it : inline_cache_map) {
442 uint16_t dex_pc = inline_cache_it.first;
443 const DexPcData dex_pc_data = inline_cache_it.second;
444 const ClassSet& classes = dex_pc_data.classes;
445
446 // Add the dex pc.
447 AddUintToBuffer(buffer, dex_pc);
448
Calin Juravle589e71e2017-03-03 16:05:05 -0800449 // Add the megamorphic/missing_types encoding if needed and continue.
450 // In either cases we don't add any classes to the profiles and so there's
451 // no point to continue.
452 // TODO(calin): in case we miss types there is still value to add the
453 // rest of the classes. They can be added without bumping the profile version.
454 if (dex_pc_data.is_missing_types) {
455 DCHECK(!dex_pc_data.is_megamorphic); // at this point the megamorphic flag should not be set.
456 DCHECK_EQ(classes.size(), 0u);
457 AddUintToBuffer(buffer, kIsMissingTypesEncoding);
458 continue;
459 } else if (dex_pc_data.is_megamorphic) {
460 DCHECK_EQ(classes.size(), 0u);
461 AddUintToBuffer(buffer, kIsMegamorphicEncoding);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800462 continue;
463 }
464
465 DCHECK_LT(classes.size(), InlineCache::kIndividualCacheSize);
466 DCHECK_NE(classes.size(), 0u) << "InlineCache contains a dex_pc with 0 classes";
467
468 SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
469 // Group the classes by dex. We expect that most of the classes will come from
470 // the same dex, so this will be more efficient than encoding the dex index
471 // for each class reference.
472 GroupClassesByDex(classes, &dex_to_classes_map);
473 // Add the dex map size.
474 AddUintToBuffer(buffer, static_cast<uint8_t>(dex_to_classes_map.size()));
475 for (const auto& dex_it : dex_to_classes_map) {
476 uint8_t dex_profile_index = dex_it.first;
477 const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
478 // Add the dex profile index.
479 AddUintToBuffer(buffer, dex_profile_index);
480 // Add the the number of classes for each dex profile index.
481 AddUintToBuffer(buffer, static_cast<uint8_t>(dex_classes.size()));
482 for (size_t i = 0; i < dex_classes.size(); i++) {
483 // Add the type index of the classes.
484 AddUintToBuffer(buffer, dex_classes[i].index_);
485 }
486 }
487 }
488}
489
490uint32_t ProfileCompilationInfo::GetMethodsRegionSize(const DexFileData& dex_data) {
491 // ((uint16_t)method index + (uint16_t)inline cache size) * number of methods
492 uint32_t size = 2 * sizeof(uint16_t) * dex_data.method_map.size();
493 for (const auto& method_it : dex_data.method_map) {
494 const InlineCacheMap& inline_cache = method_it.second;
495 size += sizeof(uint16_t) * inline_cache.size(); // dex_pc
496 for (const auto& inline_cache_it : inline_cache) {
497 const ClassSet& classes = inline_cache_it.second.classes;
498 SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
499 GroupClassesByDex(classes, &dex_to_classes_map);
500 size += sizeof(uint8_t); // dex_to_classes_map size
501 for (const auto& dex_it : dex_to_classes_map) {
502 size += sizeof(uint8_t); // dex profile index
503 size += sizeof(uint8_t); // number of classes
504 const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
505 size += sizeof(uint16_t) * dex_classes.size(); // the actual classes
506 }
507 }
508 }
509 return size;
510}
511
512void ProfileCompilationInfo::GroupClassesByDex(
513 const ClassSet& classes,
514 /*out*/SafeMap<uint8_t, std::vector<dex::TypeIndex>>* dex_to_classes_map) {
515 for (const auto& classes_it : classes) {
516 auto dex_it = dex_to_classes_map->FindOrAdd(classes_it.dex_profile_index);
517 dex_it->second.push_back(classes_it.type_index);
518 }
519}
520
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800521ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData(
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700522 const std::string& profile_key,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700523 uint32_t checksum,
524 uint32_t num_method_ids) {
Vladimir Marko3bada4b2017-05-19 12:32:47 +0100525 const auto profile_index_it = profile_key_map_.FindOrAdd(profile_key, profile_key_map_.size());
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700526 if (profile_key_map_.size() > std::numeric_limits<uint8_t>::max()) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800527 // Allow only 255 dex files to be profiled. This allows us to save bytes
528 // when encoding. The number is well above what we expect for normal applications.
529 if (kIsDebugBuild) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700530 LOG(ERROR) << "Exceeded the maximum number of dex files (255). Something went wrong";
Calin Juravle940eb0c2017-01-30 19:30:44 -0800531 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700532 profile_key_map_.erase(profile_key);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800533 return nullptr;
Calin Juravle998c2162015-12-21 15:39:33 +0200534 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700535
536 uint8_t profile_index = profile_index_it->second;
537 if (info_.size() <= profile_index) {
538 // This is a new addition. Add it to the info_ array.
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700539 DexFileData* dex_file_data = new (&arena_) DexFileData(
Mathieu Chartierea650f32017-05-24 12:04:13 -0700540 &arena_,
541 profile_key,
542 checksum,
543 profile_index,
544 num_method_ids);
Calin Juravlecc3171a2017-05-19 16:47:53 -0700545 info_.push_back(dex_file_data);
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700546 }
547 DexFileData* result = info_[profile_index];
548 // DCHECK that profile info map key is consistent with the one stored in the dex file data.
549 // This should always be the case since since the cache map is managed by ProfileCompilationInfo.
550 DCHECK_EQ(profile_key, result->profile_key);
551 DCHECK_EQ(profile_index, result->profile_index);
Mathieu Chartierea650f32017-05-24 12:04:13 -0700552 DCHECK_EQ(num_method_ids, result->num_method_ids);
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700553
554 // Check that the checksum matches.
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700555 // 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 -0700556 if (result->checksum != checksum) {
557 LOG(WARNING) << "Checksum mismatch for dex " << profile_key;
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800558 return nullptr;
559 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700560 return result;
561}
562
563const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData(
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700564 const std::string& profile_key,
565 uint32_t checksum,
566 bool verify_checksum) const {
Vladimir Marko3bada4b2017-05-19 12:32:47 +0100567 const auto profile_index_it = profile_key_map_.find(profile_key);
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700568 if (profile_index_it == profile_key_map_.end()) {
569 return nullptr;
570 }
571
572 uint8_t profile_index = profile_index_it->second;
573 const DexFileData* result = info_[profile_index];
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700574 if (verify_checksum && !ChecksumMatch(result->checksum, checksum)) {
575 return nullptr;
576 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700577 DCHECK_EQ(profile_key, result->profile_key);
578 DCHECK_EQ(profile_index, result->profile_index);
579 return result;
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800580}
581
582bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& classes) {
583 const std::string dex_location = GetProfileDexFileKey(classes.GetDexLocation());
584 const uint32_t checksum = classes.GetLocationChecksum();
Mathieu Chartierea650f32017-05-24 12:04:13 -0700585 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum, classes.NumMethodIds());
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800586 if (data == nullptr) {
Calin Juravle998c2162015-12-21 15:39:33 +0200587 return false;
588 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800589 data->class_set.insert(classes.GetClasses().begin(), classes.GetClasses().end());
590 return true;
591}
592
Calin Juravle940eb0c2017-01-30 19:30:44 -0800593bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
594 uint32_t dex_checksum,
595 uint16_t method_index,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700596 uint32_t num_method_ids,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800597 const OfflineProfileMethodInfo& pmi) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700598 DexFileData* const data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location),
599 dex_checksum,
600 num_method_ids);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800601 if (data == nullptr) { // checksum mismatch
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800602 return false;
603 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700604 // Add the method.
Calin Juravlecc3171a2017-05-19 16:47:53 -0700605 InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700606
607 if (pmi.inline_caches == nullptr) {
608 // If we don't have inline caches return success right away.
609 return true;
610 }
611 for (const auto& pmi_inline_cache_it : *pmi.inline_caches) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800612 uint16_t pmi_ic_dex_pc = pmi_inline_cache_it.first;
613 const DexPcData& pmi_ic_dex_pc_data = pmi_inline_cache_it.second;
Calin Juravlecc3171a2017-05-19 16:47:53 -0700614 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, pmi_ic_dex_pc);
615 if (dex_pc_data->is_missing_types || dex_pc_data->is_megamorphic) {
Calin Juravle589e71e2017-03-03 16:05:05 -0800616 // We are already megamorphic or we are missing types; no point in going forward.
Calin Juravle940eb0c2017-01-30 19:30:44 -0800617 continue;
618 }
Calin Juravle589e71e2017-03-03 16:05:05 -0800619
620 if (pmi_ic_dex_pc_data.is_missing_types) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700621 dex_pc_data->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -0800622 continue;
623 }
624 if (pmi_ic_dex_pc_data.is_megamorphic) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700625 dex_pc_data->SetIsMegamorphic();
Calin Juravle589e71e2017-03-03 16:05:05 -0800626 continue;
627 }
628
Calin Juravle940eb0c2017-01-30 19:30:44 -0800629 for (const ClassReference& class_ref : pmi_ic_dex_pc_data.classes) {
630 const DexReference& dex_ref = pmi.dex_references[class_ref.dex_profile_index];
631 DexFileData* class_dex_data = GetOrAddDexFileData(
632 GetProfileDexFileKey(dex_ref.dex_location),
Mathieu Chartierea650f32017-05-24 12:04:13 -0700633 dex_ref.dex_checksum,
634 dex_ref.num_method_ids);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800635 if (class_dex_data == nullptr) { // checksum mismatch
636 return false;
637 }
Calin Juravlecc3171a2017-05-19 16:47:53 -0700638 dex_pc_data->AddClass(class_dex_data->profile_index, class_ref.type_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800639 }
640 }
641 return true;
642}
643
644bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700645 DexFileData* const data = GetOrAddDexFileData(pmi.ref.dex_file);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800646 if (data == nullptr) { // checksum mismatch
647 return false;
648 }
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700649 InlineCacheMap* inline_cache = data->FindOrAddMethod(pmi.ref.dex_method_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800650
651 for (const ProfileMethodInfo::ProfileInlineCache& cache : pmi.inline_caches) {
Calin Juravle589e71e2017-03-03 16:05:05 -0800652 if (cache.is_missing_types) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700653 FindOrAddDexPc(inline_cache, cache.dex_pc)->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -0800654 continue;
655 }
Mathieu Chartierdbddc222017-05-24 12:04:13 -0700656 for (const TypeReference& class_ref : cache.classes) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700657 DexFileData* class_dex_data = GetOrAddDexFileData(class_ref.dex_file);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800658 if (class_dex_data == nullptr) { // checksum mismatch
659 return false;
660 }
Calin Juravlecc3171a2017-05-19 16:47:53 -0700661 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, cache.dex_pc);
662 if (dex_pc_data->is_missing_types) {
Calin Juravle589e71e2017-03-03 16:05:05 -0800663 // Don't bother adding classes if we are missing types.
664 break;
665 }
Calin Juravlecc3171a2017-05-19 16:47:53 -0700666 dex_pc_data->AddClass(class_dex_data->profile_index, class_ref.type_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800667 }
668 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800669 return true;
670}
671
672bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location,
673 uint32_t checksum,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700674 dex::TypeIndex type_idx,
675 uint32_t num_method_ids) {
676 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum, num_method_ids);
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800677 if (data == nullptr) {
678 return false;
679 }
Jeff Hao54b58552016-11-16 15:15:04 -0800680 data->class_set.insert(type_idx);
Calin Juravle998c2162015-12-21 15:39:33 +0200681 return true;
682}
683
Andreas Gampe37c58462017-03-27 15:14:27 -0700684#define READ_UINT(type, buffer, dest, error) \
685 do { \
686 if (!(buffer).ReadUintAndAdvance<type>(&(dest))) { \
687 *(error) = "Could not read "#dest; \
688 return false; \
689 } \
690 } \
Calin Juravle940eb0c2017-01-30 19:30:44 -0800691 while (false)
692
693bool ProfileCompilationInfo::ReadInlineCache(SafeBuffer& buffer,
694 uint8_t number_of_dex_files,
695 /*out*/ InlineCacheMap* inline_cache,
696 /*out*/ std::string* error) {
697 uint16_t inline_cache_size;
698 READ_UINT(uint16_t, buffer, inline_cache_size, error);
699 for (; inline_cache_size > 0; inline_cache_size--) {
700 uint16_t dex_pc;
701 uint8_t dex_to_classes_map_size;
702 READ_UINT(uint16_t, buffer, dex_pc, error);
703 READ_UINT(uint8_t, buffer, dex_to_classes_map_size, error);
Calin Juravlecc3171a2017-05-19 16:47:53 -0700704 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, dex_pc);
Calin Juravle589e71e2017-03-03 16:05:05 -0800705 if (dex_to_classes_map_size == kIsMissingTypesEncoding) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700706 dex_pc_data->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -0800707 continue;
708 }
709 if (dex_to_classes_map_size == kIsMegamorphicEncoding) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700710 dex_pc_data->SetIsMegamorphic();
Calin Juravle940eb0c2017-01-30 19:30:44 -0800711 continue;
712 }
713 for (; dex_to_classes_map_size > 0; dex_to_classes_map_size--) {
714 uint8_t dex_profile_index;
715 uint8_t dex_classes_size;
716 READ_UINT(uint8_t, buffer, dex_profile_index, error);
717 READ_UINT(uint8_t, buffer, dex_classes_size, error);
718 if (dex_profile_index >= number_of_dex_files) {
719 *error = "dex_profile_index out of bounds ";
720 *error += std::to_string(dex_profile_index) + " " + std::to_string(number_of_dex_files);
721 return false;
722 }
723 for (; dex_classes_size > 0; dex_classes_size--) {
724 uint16_t type_index;
725 READ_UINT(uint16_t, buffer, type_index, error);
Calin Juravlecc3171a2017-05-19 16:47:53 -0700726 dex_pc_data->AddClass(dex_profile_index, dex::TypeIndex(type_index));
Calin Juravle940eb0c2017-01-30 19:30:44 -0800727 }
728 }
729 }
730 return true;
731}
732
733bool ProfileCompilationInfo::ReadMethods(SafeBuffer& buffer,
734 uint8_t number_of_dex_files,
735 const ProfileLineHeader& line_header,
736 /*out*/std::string* error) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000737 uint32_t unread_bytes_before_operation = buffer.CountUnreadBytes();
738 if (unread_bytes_before_operation < line_header.method_region_size_bytes) {
739 *error += "Profile EOF reached prematurely for ReadMethod";
740 return kProfileLoadBadData;
741 }
742 size_t expected_unread_bytes_after_operation = buffer.CountUnreadBytes()
743 - line_header.method_region_size_bytes;
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000744 uint16_t last_method_index = 0;
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000745 while (buffer.CountUnreadBytes() > expected_unread_bytes_after_operation) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700746 DexFileData* const data = GetOrAddDexFileData(line_header.dex_location,
747 line_header.checksum,
748 line_header.num_method_ids);
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000749 uint16_t diff_with_last_method_index;
750 READ_UINT(uint16_t, buffer, diff_with_last_method_index, error);
751 uint16_t method_index = last_method_index + diff_with_last_method_index;
752 last_method_index = method_index;
Calin Juravlecc3171a2017-05-19 16:47:53 -0700753 InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
754 if (!ReadInlineCache(buffer, number_of_dex_files, inline_cache, error)) {
Calin Juravle877fd962016-01-05 14:29:29 +0000755 return false;
756 }
Calin Juravle226501b2015-12-11 14:41:31 +0000757 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000758 uint32_t total_bytes_read = unread_bytes_before_operation - buffer.CountUnreadBytes();
759 if (total_bytes_read != line_header.method_region_size_bytes) {
760 *error += "Profile data inconsistent for ReadMethods";
761 return false;
762 }
Calin Juravle940eb0c2017-01-30 19:30:44 -0800763 return true;
764}
765
766bool ProfileCompilationInfo::ReadClasses(SafeBuffer& buffer,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800767 const ProfileLineHeader& line_header,
768 /*out*/std::string* error) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000769 size_t unread_bytes_before_op = buffer.CountUnreadBytes();
770 if (unread_bytes_before_op < line_header.class_set_size) {
771 *error += "Profile EOF reached prematurely for ReadClasses";
772 return kProfileLoadBadData;
773 }
774
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000775 uint16_t last_class_index = 0;
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000776 for (uint16_t i = 0; i < line_header.class_set_size; i++) {
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000777 uint16_t diff_with_last_class_index;
778 READ_UINT(uint16_t, buffer, diff_with_last_class_index, error);
779 uint16_t type_index = last_class_index + diff_with_last_class_index;
780 last_class_index = type_index;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800781 if (!AddClassIndex(line_header.dex_location,
782 line_header.checksum,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700783 dex::TypeIndex(type_index),
784 line_header.num_method_ids)) {
Calin Juravle64142952016-03-21 14:37:55 +0000785 return false;
786 }
787 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000788 size_t total_bytes_read = unread_bytes_before_op - buffer.CountUnreadBytes();
789 uint32_t expected_bytes_read = line_header.class_set_size * sizeof(uint16_t);
790 if (total_bytes_read != expected_bytes_read) {
791 *error += "Profile data inconsistent for ReadClasses";
792 return false;
793 }
Calin Juravle226501b2015-12-11 14:41:31 +0000794 return true;
795}
796
Calin Juravle64142952016-03-21 14:37:55 +0000797// Tests for EOF by trying to read 1 byte from the descriptor.
798// Returns:
799// 0 if the descriptor is at the EOF,
800// -1 if there was an IO error
801// 1 if the descriptor has more content to read
802static int testEOF(int fd) {
803 uint8_t buffer[1];
804 return TEMP_FAILURE_RETRY(read(fd, buffer, 1));
805}
806
807// Reads an uint value previously written with AddUintToBuffer.
808template <typename T>
Calin Juravle940eb0c2017-01-30 19:30:44 -0800809bool ProfileCompilationInfo::SafeBuffer::ReadUintAndAdvance(/*out*/T* value) {
Calin Juravle64142952016-03-21 14:37:55 +0000810 static_assert(std::is_unsigned<T>::value, "Type is not unsigned");
Calin Juravle940eb0c2017-01-30 19:30:44 -0800811 if (ptr_current_ + sizeof(T) > ptr_end_) {
812 return false;
813 }
814 *value = 0;
Calin Juravle64142952016-03-21 14:37:55 +0000815 for (size_t i = 0; i < sizeof(T); i++) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800816 *value += ptr_current_[i] << (i * kBitsPerByte);
Calin Juravle226501b2015-12-11 14:41:31 +0000817 }
Calin Juravle64142952016-03-21 14:37:55 +0000818 ptr_current_ += sizeof(T);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800819 return true;
Calin Juravle64142952016-03-21 14:37:55 +0000820}
821
822bool ProfileCompilationInfo::SafeBuffer::CompareAndAdvance(const uint8_t* data, size_t data_size) {
823 if (ptr_current_ + data_size > ptr_end_) {
824 return false;
825 }
826 if (memcmp(ptr_current_, data, data_size) == 0) {
827 ptr_current_ += data_size;
828 return true;
829 }
830 return false;
831}
832
833ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::SafeBuffer::FillFromFd(
834 int fd,
835 const std::string& source,
836 /*out*/std::string* error) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000837 size_t byte_count = (ptr_end_ - ptr_current_) * sizeof(*ptr_current_);
Calin Juravle64142952016-03-21 14:37:55 +0000838 uint8_t* buffer = ptr_current_;
839 while (byte_count > 0) {
840 int bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, byte_count));
841 if (bytes_read == 0) {
842 *error += "Profile EOF reached prematurely for " + source;
843 return kProfileLoadBadData;
844 } else if (bytes_read < 0) {
845 *error += "Profile IO error for " + source + strerror(errno);
846 return kProfileLoadIOError;
Calin Juravle226501b2015-12-11 14:41:31 +0000847 }
Calin Juravle64142952016-03-21 14:37:55 +0000848 byte_count -= bytes_read;
849 buffer += bytes_read;
Calin Juravle226501b2015-12-11 14:41:31 +0000850 }
Calin Juravle64142952016-03-21 14:37:55 +0000851 return kProfileLoadSuccess;
852}
853
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000854size_t ProfileCompilationInfo::SafeBuffer::CountUnreadBytes() {
855 return (ptr_end_ - ptr_current_) * sizeof(*ptr_current_);
856}
857
858const uint8_t* ProfileCompilationInfo::SafeBuffer::GetCurrentPtr() {
859 return ptr_current_;
860}
861
862void ProfileCompilationInfo::SafeBuffer::Advance(size_t data_size) {
863 ptr_current_ += data_size;
864}
865
Calin Juravle64142952016-03-21 14:37:55 +0000866ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileHeader(
867 int fd,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800868 /*out*/uint8_t* number_of_dex_files,
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000869 /*out*/uint32_t* uncompressed_data_size,
870 /*out*/uint32_t* compressed_data_size,
Calin Juravle64142952016-03-21 14:37:55 +0000871 /*out*/std::string* error) {
872 // Read magic and version
873 const size_t kMagicVersionSize =
874 sizeof(kProfileMagic) +
875 sizeof(kProfileVersion) +
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000876 sizeof(uint8_t) + // number of dex files
877 sizeof(uint32_t) + // size of uncompressed profile data
878 sizeof(uint32_t); // size of compressed profile data
Calin Juravle64142952016-03-21 14:37:55 +0000879
880 SafeBuffer safe_buffer(kMagicVersionSize);
881
882 ProfileLoadSatus status = safe_buffer.FillFromFd(fd, "ReadProfileHeader", error);
883 if (status != kProfileLoadSuccess) {
884 return status;
885 }
886
887 if (!safe_buffer.CompareAndAdvance(kProfileMagic, sizeof(kProfileMagic))) {
888 *error = "Profile missing magic";
889 return kProfileLoadVersionMismatch;
890 }
891 if (!safe_buffer.CompareAndAdvance(kProfileVersion, sizeof(kProfileVersion))) {
892 *error = "Profile version mismatch";
893 return kProfileLoadVersionMismatch;
894 }
Calin Juravle940eb0c2017-01-30 19:30:44 -0800895 if (!safe_buffer.ReadUintAndAdvance<uint8_t>(number_of_dex_files)) {
896 *error = "Cannot read the number of dex files";
897 return kProfileLoadBadData;
898 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000899 if (!safe_buffer.ReadUintAndAdvance<uint32_t>(uncompressed_data_size)) {
900 *error = "Cannot read the size of uncompressed data";
901 return kProfileLoadBadData;
902 }
903 if (!safe_buffer.ReadUintAndAdvance<uint32_t>(compressed_data_size)) {
904 *error = "Cannot read the size of compressed data";
905 return kProfileLoadBadData;
906 }
Calin Juravle64142952016-03-21 14:37:55 +0000907 return kProfileLoadSuccess;
908}
909
Calin Juravle940eb0c2017-01-30 19:30:44 -0800910bool ProfileCompilationInfo::ReadProfileLineHeaderElements(SafeBuffer& buffer,
911 /*out*/uint16_t* dex_location_size,
912 /*out*/ProfileLineHeader* line_header,
913 /*out*/std::string* error) {
914 READ_UINT(uint16_t, buffer, *dex_location_size, error);
915 READ_UINT(uint16_t, buffer, line_header->class_set_size, error);
916 READ_UINT(uint32_t, buffer, line_header->method_region_size_bytes, error);
917 READ_UINT(uint32_t, buffer, line_header->checksum, error);
Mathieu Chartierea650f32017-05-24 12:04:13 -0700918 READ_UINT(uint32_t, buffer, line_header->num_method_ids, error);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800919 return true;
920}
921
Calin Juravle64142952016-03-21 14:37:55 +0000922ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLineHeader(
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000923 SafeBuffer& buffer,
924 /*out*/ProfileLineHeader* line_header,
925 /*out*/std::string* error) {
926 if (buffer.CountUnreadBytes() < kLineHeaderSize) {
927 *error += "Profile EOF reached prematurely for ReadProfileLineHeader";
928 return kProfileLoadBadData;
Calin Juravle64142952016-03-21 14:37:55 +0000929 }
930
Calin Juravle940eb0c2017-01-30 19:30:44 -0800931 uint16_t dex_location_size;
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000932 if (!ReadProfileLineHeaderElements(buffer, &dex_location_size, line_header, error)) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800933 return kProfileLoadBadData;
934 }
Calin Juravle64142952016-03-21 14:37:55 +0000935
936 if (dex_location_size == 0 || dex_location_size > kMaxDexFileKeyLength) {
Goran Jakovljevic4eb6fbf2016-04-25 19:14:17 +0200937 *error = "DexFileKey has an invalid size: " +
938 std::to_string(static_cast<uint32_t>(dex_location_size));
Calin Juravle64142952016-03-21 14:37:55 +0000939 return kProfileLoadBadData;
940 }
941
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000942 if (buffer.CountUnreadBytes() < dex_location_size) {
943 *error += "Profile EOF reached prematurely for ReadProfileHeaderDexLocation";
944 return kProfileLoadBadData;
Calin Juravle64142952016-03-21 14:37:55 +0000945 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000946 const uint8_t* base_ptr = buffer.GetCurrentPtr();
Calin Juravle64142952016-03-21 14:37:55 +0000947 line_header->dex_location.assign(
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000948 reinterpret_cast<const char*>(base_ptr), dex_location_size);
949 buffer.Advance(dex_location_size);
Calin Juravle64142952016-03-21 14:37:55 +0000950 return kProfileLoadSuccess;
951}
952
953ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine(
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000954 SafeBuffer& buffer,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800955 uint8_t number_of_dex_files,
Calin Juravle64142952016-03-21 14:37:55 +0000956 const ProfileLineHeader& line_header,
957 /*out*/std::string* error) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700958 DexFileData* data = GetOrAddDexFileData(line_header.dex_location,
959 line_header.checksum,
960 line_header.num_method_ids);
961 if (data == nullptr) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800962 *error = "Error when reading profile file line header: checksum mismatch for "
963 + line_header.dex_location;
964 return kProfileLoadBadData;
965 }
Calin Juravle64142952016-03-21 14:37:55 +0000966
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000967 if (!ReadMethods(buffer, number_of_dex_files, line_header, error)) {
968 return kProfileLoadBadData;
Calin Juravle64142952016-03-21 14:37:55 +0000969 }
Calin Juravle940eb0c2017-01-30 19:30:44 -0800970
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000971 if (!ReadClasses(buffer, line_header, error)) {
972 return kProfileLoadBadData;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800973 }
Mathieu Chartierea650f32017-05-24 12:04:13 -0700974
975 const size_t bytes = data->bitmap_storage.size();
976 if (buffer.CountUnreadBytes() < bytes) {
977 *error += "Profile EOF reached prematurely for ReadProfileHeaderDexLocation";
978 return kProfileLoadBadData;
979 }
980 const uint8_t* base_ptr = buffer.GetCurrentPtr();
981 std::copy_n(base_ptr, bytes, &data->bitmap_storage[0]);
982 buffer.Advance(bytes);
983 // Read method bitmap.
Calin Juravle64142952016-03-21 14:37:55 +0000984 return kProfileLoadSuccess;
Calin Juravle226501b2015-12-11 14:41:31 +0000985}
986
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700987// TODO(calin): Fix this API. ProfileCompilationInfo::Load should be static and
988// return a unique pointer to a ProfileCompilationInfo upon success.
Calin Juravle2e2db782016-02-23 12:00:03 +0000989bool ProfileCompilationInfo::Load(int fd) {
Calin Juravle64142952016-03-21 14:37:55 +0000990 std::string error;
991 ProfileLoadSatus status = LoadInternal(fd, &error);
992
993 if (status == kProfileLoadSuccess) {
994 return true;
995 } else {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800996 LOG(WARNING) << "Error when reading profile: " << error;
Calin Juravle64142952016-03-21 14:37:55 +0000997 return false;
998 }
999}
1000
Calin Juravledcab1902017-05-12 19:18:47 -07001001// TODO(calin): fail fast if the dex checksums don't match.
Calin Juravle64142952016-03-21 14:37:55 +00001002ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal(
1003 int fd, std::string* error) {
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -08001004 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravle2e2db782016-02-23 12:00:03 +00001005 DCHECK_GE(fd, 0);
Calin Juravle226501b2015-12-11 14:41:31 +00001006
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001007 if (!IsEmpty()) {
1008 return kProfileLoadWouldOverwiteData;
1009 }
1010
Calin Juravle64142952016-03-21 14:37:55 +00001011 struct stat stat_buffer;
1012 if (fstat(fd, &stat_buffer) != 0) {
1013 return kProfileLoadIOError;
Calin Juravle226501b2015-12-11 14:41:31 +00001014 }
Calin Juravle64142952016-03-21 14:37:55 +00001015 // We allow empty profile files.
1016 // Profiles may be created by ActivityManager or installd before we manage to
1017 // process them in the runtime or profman.
1018 if (stat_buffer.st_size == 0) {
1019 return kProfileLoadSuccess;
1020 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001021 // Read profile header: magic + version + number_of_dex_files.
1022 uint8_t number_of_dex_files;
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001023 uint32_t uncompressed_data_size;
1024 uint32_t compressed_data_size;
1025 ProfileLoadSatus status = ReadProfileHeader(fd,
1026 &number_of_dex_files,
1027 &uncompressed_data_size,
1028 &compressed_data_size,
1029 error);
1030
Calin Juravle64142952016-03-21 14:37:55 +00001031 if (status != kProfileLoadSuccess) {
1032 return status;
1033 }
Mathieu Chartierf2e2af82017-07-12 21:09:57 -07001034 // Allow large profiles for non target builds for the case where we are merging many profiles
1035 // to generate a boot image profile.
1036 if (kIsTargetBuild && uncompressed_data_size > kProfileSizeErrorThresholdInBytes) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001037 LOG(ERROR) << "Profile data size exceeds "
1038 << std::to_string(kProfileSizeErrorThresholdInBytes)
1039 << " bytes";
1040 return kProfileLoadBadData;
1041 }
1042 if (uncompressed_data_size > kProfileSizeWarningThresholdInBytes) {
1043 LOG(WARNING) << "Profile data size exceeds "
1044 << std::to_string(kProfileSizeWarningThresholdInBytes)
1045 << " bytes";
1046 }
1047
1048 std::unique_ptr<uint8_t[]> compressed_data(new uint8_t[compressed_data_size]);
1049 bool bytes_read_success =
1050 android::base::ReadFully(fd, compressed_data.get(), compressed_data_size);
1051
1052 if (testEOF(fd) != 0) {
1053 *error += "Unexpected data in the profile file.";
1054 return kProfileLoadBadData;
1055 }
1056
1057 if (!bytes_read_success) {
1058 *error += "Unable to read compressed profile data";
1059 return kProfileLoadBadData;
1060 }
1061
1062 SafeBuffer uncompressed_data(uncompressed_data_size);
1063
1064 int ret = InflateBuffer(compressed_data.get(),
1065 compressed_data_size,
1066 uncompressed_data_size,
1067 uncompressed_data.Get());
1068
1069 if (ret != Z_STREAM_END) {
1070 *error += "Error reading uncompressed profile data";
1071 return kProfileLoadBadData;
1072 }
1073
Calin Juravle940eb0c2017-01-30 19:30:44 -08001074 for (uint8_t k = 0; k < number_of_dex_files; k++) {
Calin Juravle64142952016-03-21 14:37:55 +00001075 ProfileLineHeader line_header;
Calin Juravle940eb0c2017-01-30 19:30:44 -08001076
Calin Juravle64142952016-03-21 14:37:55 +00001077 // First, read the line header to get the amount of data we need to read.
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001078 status = ReadProfileLineHeader(uncompressed_data, &line_header, error);
Calin Juravle64142952016-03-21 14:37:55 +00001079 if (status != kProfileLoadSuccess) {
1080 return status;
1081 }
1082
1083 // Now read the actual profile line.
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001084 status = ReadProfileLine(uncompressed_data, number_of_dex_files, line_header, error);
Calin Juravle64142952016-03-21 14:37:55 +00001085 if (status != kProfileLoadSuccess) {
1086 return status;
1087 }
Calin Juravle64142952016-03-21 14:37:55 +00001088 }
1089
1090 // Check that we read everything and that profiles don't contain junk data.
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001091 if (uncompressed_data.CountUnreadBytes() > 0) {
Calin Juravle64142952016-03-21 14:37:55 +00001092 *error = "Unexpected content in the profile file";
1093 return kProfileLoadBadData;
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001094 } else {
1095 return kProfileLoadSuccess;
Calin Juravle64142952016-03-21 14:37:55 +00001096 }
Calin Juravle998c2162015-12-21 15:39:33 +02001097}
1098
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001099std::unique_ptr<uint8_t[]> ProfileCompilationInfo::DeflateBuffer(const uint8_t* in_buffer,
1100 uint32_t in_size,
1101 uint32_t* compressed_data_size) {
1102 z_stream strm;
1103 strm.zalloc = Z_NULL;
1104 strm.zfree = Z_NULL;
1105 strm.opaque = Z_NULL;
1106 int ret = deflateInit(&strm, 1);
1107 if (ret != Z_OK) {
1108 return nullptr;
1109 }
1110
1111 uint32_t out_size = deflateBound(&strm, in_size);
1112
1113 std::unique_ptr<uint8_t[]> compressed_buffer(new uint8_t[out_size]);
1114 strm.avail_in = in_size;
1115 strm.next_in = const_cast<uint8_t*>(in_buffer);
1116 strm.avail_out = out_size;
1117 strm.next_out = &compressed_buffer[0];
1118 ret = deflate(&strm, Z_FINISH);
1119 if (ret == Z_STREAM_ERROR) {
1120 return nullptr;
1121 }
1122 *compressed_data_size = out_size - strm.avail_out;
1123 deflateEnd(&strm);
1124 return compressed_buffer;
1125}
1126
1127int ProfileCompilationInfo::InflateBuffer(const uint8_t* in_buffer,
1128 uint32_t in_size,
1129 uint32_t expected_uncompressed_data_size,
1130 uint8_t* out_buffer) {
1131 z_stream strm;
1132
1133 /* allocate inflate state */
1134 strm.zalloc = Z_NULL;
1135 strm.zfree = Z_NULL;
1136 strm.opaque = Z_NULL;
1137 strm.avail_in = in_size;
1138 strm.next_in = const_cast<uint8_t*>(in_buffer);
1139 strm.avail_out = expected_uncompressed_data_size;
1140 strm.next_out = out_buffer;
1141
1142 int ret;
1143 inflateInit(&strm);
1144 ret = inflate(&strm, Z_NO_FLUSH);
1145
1146 if (strm.avail_in != 0 || strm.avail_out != 0) {
1147 return Z_DATA_ERROR;
1148 }
1149 inflateEnd(&strm);
1150 return ret;
1151}
1152
Mathieu Chartier2f794552017-06-19 10:58:08 -07001153bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other,
1154 bool merge_classes) {
Calin Juravle5d1bd0a2016-03-24 20:33:22 +00001155 // First verify that all checksums match. This will avoid adding garbage to
1156 // the current profile info.
1157 // Note that the number of elements should be very small, so this should not
1158 // be a performance issue.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001159 for (const DexFileData* other_dex_data : other.info_) {
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001160 // verify_checksum is false because we want to differentiate between a missing dex data and
1161 // a mismatched checksum.
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001162 const DexFileData* dex_data = FindDexData(other_dex_data->profile_key,
1163 0u,
1164 /* verify_checksum */ false);
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001165 if ((dex_data != nullptr) && (dex_data->checksum != other_dex_data->checksum)) {
1166 LOG(WARNING) << "Checksum mismatch for dex " << other_dex_data->profile_key;
Calin Juravle5d1bd0a2016-03-24 20:33:22 +00001167 return false;
1168 }
1169 }
1170 // All checksums match. Import the data.
Calin Juravle940eb0c2017-01-30 19:30:44 -08001171
1172 // The other profile might have a different indexing of dex files.
1173 // That is because each dex files gets a 'dex_profile_index' on a first come first served basis.
1174 // That means that the order in with the methods are added to the profile matters for the
1175 // actual indices.
1176 // The reason we cannot rely on the actual multidex index is that a single profile may store
1177 // data from multiple splits. This means that a profile may contain a classes2.dex from split-A
1178 // and one from split-B.
1179
1180 // First, build a mapping from other_dex_profile_index to this_dex_profile_index.
1181 // This will make sure that the ClassReferences will point to the correct dex file.
1182 SafeMap<uint8_t, uint8_t> dex_profile_index_remap;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001183 for (const DexFileData* other_dex_data : other.info_) {
1184 const DexFileData* dex_data = GetOrAddDexFileData(other_dex_data->profile_key,
Mathieu Chartierea650f32017-05-24 12:04:13 -07001185 other_dex_data->checksum,
1186 other_dex_data->num_method_ids);
Calin Juravlee0ac1152017-02-13 19:03:47 -08001187 if (dex_data == nullptr) {
1188 return false; // Could happen if we exceed the number of allowed dex files.
1189 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001190 dex_profile_index_remap.Put(other_dex_data->profile_index, dex_data->profile_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -08001191 }
1192
1193 // Merge the actual profile data.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001194 for (const DexFileData* other_dex_data : other.info_) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001195 DexFileData* dex_data = const_cast<DexFileData*>(FindDexData(other_dex_data->profile_key,
1196 other_dex_data->checksum));
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001197 DCHECK(dex_data != nullptr);
Calin Juravle940eb0c2017-01-30 19:30:44 -08001198
1199 // Merge the classes.
Mathieu Chartier2f794552017-06-19 10:58:08 -07001200 if (merge_classes) {
1201 dex_data->class_set.insert(other_dex_data->class_set.begin(),
1202 other_dex_data->class_set.end());
1203 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001204
1205 // Merge the methods and the inline caches.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001206 for (const auto& other_method_it : other_dex_data->method_map) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001207 uint16_t other_method_index = other_method_it.first;
Calin Juravlecc3171a2017-05-19 16:47:53 -07001208 InlineCacheMap* inline_cache = dex_data->FindOrAddMethod(other_method_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -08001209 const auto& other_inline_cache = other_method_it.second;
1210 for (const auto& other_ic_it : other_inline_cache) {
1211 uint16_t other_dex_pc = other_ic_it.first;
1212 const ClassSet& other_class_set = other_ic_it.second.classes;
Calin Juravlecc3171a2017-05-19 16:47:53 -07001213 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, other_dex_pc);
Calin Juravle589e71e2017-03-03 16:05:05 -08001214 if (other_ic_it.second.is_missing_types) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001215 dex_pc_data->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -08001216 } else if (other_ic_it.second.is_megamorphic) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001217 dex_pc_data->SetIsMegamorphic();
Calin Juravle0def68d2017-02-21 19:00:33 -08001218 } else {
1219 for (const auto& class_it : other_class_set) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001220 dex_pc_data->AddClass(dex_profile_index_remap.Get(
Calin Juravle0def68d2017-02-21 19:00:33 -08001221 class_it.dex_profile_index), class_it.type_index);
1222 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001223 }
1224 }
1225 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001226
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001227 // Merge the method bitmaps.
Mathieu Chartierea650f32017-05-24 12:04:13 -07001228 dex_data->MergeBitmap(*other_dex_data);
Calin Juravle998c2162015-12-21 15:39:33 +02001229 }
1230 return true;
Calin Juravle226501b2015-12-11 14:41:31 +00001231}
1232
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001233const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData(
1234 const DexFile* dex_file) const {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001235 return FindDexData(GetProfileDexFileKey(dex_file->GetLocation()),
1236 dex_file->GetLocationChecksum());
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001237}
1238
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001239ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::GetMethodHotness(
1240 const MethodReference& method_ref) const {
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001241 const DexFileData* dex_data = FindDexData(method_ref.dex_file);
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001242 return dex_data != nullptr
1243 ? dex_data->GetHotnessInfo(method_ref.dex_method_index)
1244 : MethodHotness();
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001245}
1246
Mathieu Chartier2f794552017-06-19 10:58:08 -07001247bool ProfileCompilationInfo::AddMethodHotness(const MethodReference& method_ref,
1248 const MethodHotness& hotness) {
1249 DexFileData* dex_data = GetOrAddDexFileData(method_ref.dex_file);
1250 if (dex_data != nullptr) {
1251 // TODO: Add inline caches.
1252 dex_data->AddMethod(static_cast<MethodHotness::Flag>(hotness.GetFlags()),
1253 method_ref.dex_method_index);
1254 return true;
1255 }
1256 return false;
1257}
1258
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001259ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::GetMethodHotness(
1260 const std::string& dex_location,
1261 uint32_t dex_checksum,
1262 uint16_t dex_method_index) const {
1263 const DexFileData* dex_data = FindDexData(GetProfileDexFileKey(dex_location), dex_checksum);
1264 return dex_data != nullptr ? dex_data->GetHotnessInfo(dex_method_index) : MethodHotness();
Calin Juravle226501b2015-12-11 14:41:31 +00001265}
1266
Calin Juravle940eb0c2017-01-30 19:30:44 -08001267
Calin Juravlecc3171a2017-05-19 16:47:53 -07001268std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> ProfileCompilationInfo::GetMethod(
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001269 const std::string& dex_location,
1270 uint32_t dex_checksum,
1271 uint16_t dex_method_index) const {
1272 MethodHotness hotness(GetMethodHotness(dex_location, dex_checksum, dex_method_index));
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001273 if (!hotness.IsHot()) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001274 return nullptr;
Calin Juravle940eb0c2017-01-30 19:30:44 -08001275 }
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001276 const InlineCacheMap* inline_caches = hotness.GetInlineCacheMap();
1277 DCHECK(inline_caches != nullptr);
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001278 std::unique_ptr<OfflineProfileMethodInfo> pmi(new OfflineProfileMethodInfo(inline_caches));
Calin Juravle940eb0c2017-01-30 19:30:44 -08001279
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001280 pmi->dex_references.resize(info_.size());
1281 for (const DexFileData* dex_data : info_) {
1282 pmi->dex_references[dex_data->profile_index].dex_location = dex_data->profile_key;
1283 pmi->dex_references[dex_data->profile_index].dex_checksum = dex_data->checksum;
Mathieu Chartierea650f32017-05-24 12:04:13 -07001284 pmi->dex_references[dex_data->profile_index].num_method_ids = dex_data->num_method_ids;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001285 }
1286
Calin Juravlecc3171a2017-05-19 16:47:53 -07001287 return pmi;
Calin Juravle940eb0c2017-01-30 19:30:44 -08001288}
1289
1290
Andreas Gampea5b09a62016-11-17 15:21:22 -08001291bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001292 const DexFileData* dex_data = FindDexData(&dex_file);
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001293 if (dex_data != nullptr) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001294 const ArenaSet<dex::TypeIndex>& classes = dex_data->class_set;
Jeff Hao54b58552016-11-16 15:15:04 -08001295 return classes.find(type_idx) != classes.end();
Mathieu Chartiera8077802016-03-16 19:08:31 -07001296 }
1297 return false;
1298}
1299
Calin Juravle998c2162015-12-21 15:39:33 +02001300uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
1301 uint32_t total = 0;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001302 for (const DexFileData* dex_data : info_) {
1303 total += dex_data->method_map.size();
Calin Juravle998c2162015-12-21 15:39:33 +02001304 }
1305 return total;
1306}
1307
Calin Juravle67265462016-03-18 16:23:40 +00001308uint32_t ProfileCompilationInfo::GetNumberOfResolvedClasses() const {
1309 uint32_t total = 0;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001310 for (const DexFileData* dex_data : info_) {
1311 total += dex_data->class_set.size();
Calin Juravle67265462016-03-18 16:23:40 +00001312 }
1313 return total;
1314}
1315
David Sehrb18991b2017-02-08 20:58:10 -08001316// Produce a non-owning vector from a vector.
1317template<typename T>
1318const std::vector<T*>* MakeNonOwningVector(const std::vector<std::unique_ptr<T>>* owning_vector) {
1319 auto non_owning_vector = new std::vector<T*>();
1320 for (auto& element : *owning_vector) {
1321 non_owning_vector->push_back(element.get());
1322 }
1323 return non_owning_vector;
1324}
1325
1326std::string ProfileCompilationInfo::DumpInfo(
1327 const std::vector<std::unique_ptr<const DexFile>>* dex_files,
1328 bool print_full_dex_location) const {
1329 std::unique_ptr<const std::vector<const DexFile*>> non_owning_dex_files(
1330 MakeNonOwningVector(dex_files));
1331 return DumpInfo(non_owning_dex_files.get(), print_full_dex_location);
1332}
1333
Calin Juravle998c2162015-12-21 15:39:33 +02001334std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
1335 bool print_full_dex_location) const {
Calin Juravle226501b2015-12-11 14:41:31 +00001336 std::ostringstream os;
1337 if (info_.empty()) {
1338 return "ProfileInfo: empty";
1339 }
1340
1341 os << "ProfileInfo:";
1342
Calin Juravlea308a322017-07-18 16:51:51 -07001343 const std::string kFirstDexFileKeySubstitute = "!classes.dex";
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001344
1345 for (const DexFileData* dex_data : info_) {
Calin Juravle226501b2015-12-11 14:41:31 +00001346 os << "\n";
Calin Juravle226501b2015-12-11 14:41:31 +00001347 if (print_full_dex_location) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001348 os << dex_data->profile_key;
Calin Juravle226501b2015-12-11 14:41:31 +00001349 } else {
1350 // Replace the (empty) multidex suffix of the first key with a substitute for easier reading.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001351 std::string multidex_suffix = DexFile::GetMultiDexSuffix(dex_data->profile_key);
Calin Juravle226501b2015-12-11 14:41:31 +00001352 os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
1353 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001354 os << " [index=" << static_cast<uint32_t>(dex_data->profile_index) << "]";
Calin Juravle876f3502016-03-24 16:16:34 +00001355 const DexFile* dex_file = nullptr;
1356 if (dex_files != nullptr) {
1357 for (size_t i = 0; i < dex_files->size(); i++) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001358 if (dex_data->profile_key == (*dex_files)[i]->GetLocation()) {
Calin Juravle876f3502016-03-24 16:16:34 +00001359 dex_file = (*dex_files)[i];
Calin Juravle998c2162015-12-21 15:39:33 +02001360 }
Calin Juravle226501b2015-12-11 14:41:31 +00001361 }
Calin Juravle876f3502016-03-24 16:16:34 +00001362 }
Mathieu Chartier28b5c582017-06-06 14:12:50 -07001363 os << "\n\thot methods: ";
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001364 for (const auto& method_it : dex_data->method_map) {
Calin Juravle876f3502016-03-24 16:16:34 +00001365 if (dex_file != nullptr) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001366 os << "\n\t\t" << dex_file->PrettyMethod(method_it.first, true);
Calin Juravle876f3502016-03-24 16:16:34 +00001367 } else {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001368 os << method_it.first;
Calin Juravle876f3502016-03-24 16:16:34 +00001369 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001370
1371 os << "[";
1372 for (const auto& inline_cache_it : method_it.second) {
1373 os << "{" << std::hex << inline_cache_it.first << std::dec << ":";
Calin Juravle589e71e2017-03-03 16:05:05 -08001374 if (inline_cache_it.second.is_missing_types) {
1375 os << "MT";
1376 } else if (inline_cache_it.second.is_megamorphic) {
1377 os << "MM";
Calin Juravle940eb0c2017-01-30 19:30:44 -08001378 } else {
1379 for (const ClassReference& class_ref : inline_cache_it.second.classes) {
1380 os << "(" << static_cast<uint32_t>(class_ref.dex_profile_index)
1381 << "," << class_ref.type_index.index_ << ")";
1382 }
1383 }
1384 os << "}";
1385 }
1386 os << "], ";
Calin Juravle876f3502016-03-24 16:16:34 +00001387 }
Mathieu Chartier28b5c582017-06-06 14:12:50 -07001388 bool startup = true;
1389 while (true) {
1390 os << "\n\t" << (startup ? "startup methods: " : "post startup methods: ");
1391 for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001392 MethodHotness hotness_info(dex_data->GetHotnessInfo(method_idx));
1393 if (startup ? hotness_info.IsStartup() : hotness_info.IsPostStartup()) {
Mathieu Chartier28b5c582017-06-06 14:12:50 -07001394 os << method_idx << ", ";
1395 }
1396 }
1397 if (startup == false) {
1398 break;
1399 }
1400 startup = false;
1401 }
Calin Juravle876f3502016-03-24 16:16:34 +00001402 os << "\n\tclasses: ";
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001403 for (const auto class_it : dex_data->class_set) {
Calin Juravle876f3502016-03-24 16:16:34 +00001404 if (dex_file != nullptr) {
Jeff Hao54b58552016-11-16 15:15:04 -08001405 os << "\n\t\t" << dex_file->PrettyType(class_it);
Calin Juravle876f3502016-03-24 16:16:34 +00001406 } else {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001407 os << class_it.index_ << ",";
Calin Juravle876f3502016-03-24 16:16:34 +00001408 }
Calin Juravle226501b2015-12-11 14:41:31 +00001409 }
1410 }
1411 return os.str();
1412}
1413
Mathieu Chartierea650f32017-05-24 12:04:13 -07001414bool ProfileCompilationInfo::GetClassesAndMethods(
1415 const DexFile& dex_file,
1416 /*out*/std::set<dex::TypeIndex>* class_set,
1417 /*out*/std::set<uint16_t>* hot_method_set,
1418 /*out*/std::set<uint16_t>* startup_method_set,
1419 /*out*/std::set<uint16_t>* post_startup_method_method_set) const {
Mathieu Chartier34067262017-04-06 13:55:46 -07001420 std::set<std::string> ret;
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001421 const DexFileData* dex_data = FindDexData(&dex_file);
1422 if (dex_data == nullptr) {
Mathieu Chartier34067262017-04-06 13:55:46 -07001423 return false;
David Sehr7c80f2d2017-02-07 16:47:58 -08001424 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001425 for (const auto& it : dex_data->method_map) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001426 hot_method_set->insert(it.first);
1427 }
1428 for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001429 MethodHotness hotness = dex_data->GetHotnessInfo(method_idx);
1430 if (hotness.IsStartup()) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001431 startup_method_set->insert(method_idx);
1432 }
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001433 if (hotness.IsPostStartup()) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001434 post_startup_method_method_set->insert(method_idx);
1435 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001436 }
Calin Juravlecc3171a2017-05-19 16:47:53 -07001437 for (const dex::TypeIndex& type_index : dex_data->class_set) {
1438 class_set->insert(type_index);
1439 }
Mathieu Chartier34067262017-04-06 13:55:46 -07001440 return true;
David Sehr7c80f2d2017-02-07 16:47:58 -08001441}
1442
Calin Juravle2e2db782016-02-23 12:00:03 +00001443bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001444 // No need to compare profile_key_map_. That's only a cache for fast search.
1445 // All the information is already in the info_ vector.
1446 if (info_.size() != other.info_.size()) {
1447 return false;
1448 }
1449 for (size_t i = 0; i < info_.size(); i++) {
1450 const DexFileData& dex_data = *info_[i];
1451 const DexFileData& other_dex_data = *other.info_[i];
1452 if (!(dex_data == other_dex_data)) {
1453 return false;
1454 }
1455 }
1456 return true;
Calin Juravle877fd962016-01-05 14:29:29 +00001457}
1458
Mathieu Chartier046854b2017-03-01 17:16:22 -08001459std::set<DexCacheResolvedClasses> ProfileCompilationInfo::GetResolvedClasses(
Calin Juravle08556882017-05-26 16:40:45 -07001460 const std::vector<const DexFile*>& dex_files) const {
1461 std::unordered_map<std::string, const DexFile* > key_to_dex_file;
1462 for (const DexFile* dex_file : dex_files) {
1463 key_to_dex_file.emplace(GetProfileDexFileKey(dex_file->GetLocation()), dex_file);
Mathieu Chartier046854b2017-03-01 17:16:22 -08001464 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -08001465 std::set<DexCacheResolvedClasses> ret;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001466 for (const DexFileData* dex_data : info_) {
Calin Juravle08556882017-05-26 16:40:45 -07001467 const auto it = key_to_dex_file.find(dex_data->profile_key);
1468 if (it != key_to_dex_file.end()) {
1469 const DexFile* dex_file = it->second;
1470 const std::string& dex_location = dex_file->GetLocation();
1471 if (dex_data->checksum != it->second->GetLocationChecksum()) {
1472 LOG(ERROR) << "Dex checksum mismatch when getting resolved classes from profile for "
1473 << "location " << dex_location << " (checksum=" << dex_file->GetLocationChecksum()
1474 << ", profile checksum=" << dex_data->checksum;
1475 return std::set<DexCacheResolvedClasses>();
1476 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001477 DexCacheResolvedClasses classes(dex_location,
1478 dex_location,
1479 dex_data->checksum,
1480 dex_data->num_method_ids);
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001481 classes.AddClasses(dex_data->class_set.begin(), dex_data->class_set.end());
Mathieu Chartier046854b2017-03-01 17:16:22 -08001482 ret.insert(classes);
1483 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -08001484 }
1485 return ret;
1486}
1487
Calin Juravle7bcdb532016-06-07 16:14:47 +01001488// Naive implementation to generate a random profile file suitable for testing.
1489bool ProfileCompilationInfo::GenerateTestProfile(int fd,
1490 uint16_t number_of_dex_files,
1491 uint16_t method_ratio,
Jeff Haof0a31f82017-03-27 15:50:37 -07001492 uint16_t class_ratio,
1493 uint32_t random_seed) {
Calin Juravle7bcdb532016-06-07 16:14:47 +01001494 const std::string base_dex_location = "base.apk";
1495 ProfileCompilationInfo info;
1496 // The limits are defined by the dex specification.
Mathieu Chartierea650f32017-05-24 12:04:13 -07001497 const uint16_t max_method = std::numeric_limits<uint16_t>::max();
1498 const uint16_t max_classes = std::numeric_limits<uint16_t>::max();
Calin Juravle7bcdb532016-06-07 16:14:47 +01001499 uint16_t number_of_methods = max_method * method_ratio / 100;
1500 uint16_t number_of_classes = max_classes * class_ratio / 100;
1501
Jeff Haof0a31f82017-03-27 15:50:37 -07001502 std::srand(random_seed);
Calin Juravle7bcdb532016-06-07 16:14:47 +01001503
1504 // Make sure we generate more samples with a low index value.
1505 // This makes it more likely to hit valid method/class indices in small apps.
1506 const uint16_t kFavorFirstN = 10000;
1507 const uint16_t kFavorSplit = 2;
1508
1509 for (uint16_t i = 0; i < number_of_dex_files; i++) {
1510 std::string dex_location = DexFile::GetMultiDexLocation(i, base_dex_location.c_str());
1511 std::string profile_key = GetProfileDexFileKey(dex_location);
1512
1513 for (uint16_t m = 0; m < number_of_methods; m++) {
1514 uint16_t method_idx = rand() % max_method;
1515 if (m < (number_of_methods / kFavorSplit)) {
1516 method_idx %= kFavorFirstN;
1517 }
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001518 info.AddMethodIndex(MethodHotness::kFlagHot,
1519 profile_key,
1520 /*method_idx*/ 0,
1521 method_idx,
1522 max_method);
Calin Juravle7bcdb532016-06-07 16:14:47 +01001523 }
1524
1525 for (uint16_t c = 0; c < number_of_classes; c++) {
Jeff Hao54b58552016-11-16 15:15:04 -08001526 uint16_t type_idx = rand() % max_classes;
Calin Juravle7bcdb532016-06-07 16:14:47 +01001527 if (c < (number_of_classes / kFavorSplit)) {
Jeff Hao54b58552016-11-16 15:15:04 -08001528 type_idx %= kFavorFirstN;
Calin Juravle7bcdb532016-06-07 16:14:47 +01001529 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001530 info.AddClassIndex(profile_key, 0, dex::TypeIndex(type_idx), max_method);
Calin Juravle7bcdb532016-06-07 16:14:47 +01001531 }
1532 }
1533 return info.Save(fd);
1534}
1535
Jeff Haof0a31f82017-03-27 15:50:37 -07001536// Naive implementation to generate a random profile file suitable for testing.
1537bool ProfileCompilationInfo::GenerateTestProfile(
1538 int fd,
1539 std::vector<std::unique_ptr<const DexFile>>& dex_files,
1540 uint32_t random_seed) {
1541 std::srand(random_seed);
1542 ProfileCompilationInfo info;
1543 for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
1544 const std::string& location = dex_file->GetLocation();
1545 uint32_t checksum = dex_file->GetLocationChecksum();
1546 for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
1547 // Randomly add a class from the dex file (with 50% chance).
1548 if (std::rand() % 2 != 0) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001549 info.AddClassIndex(location,
1550 checksum,
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001551 dex_file->GetClassDef(i).class_idx_,
Mathieu Chartierea650f32017-05-24 12:04:13 -07001552 dex_file->NumMethodIds());
Jeff Haof0a31f82017-03-27 15:50:37 -07001553 }
1554 }
1555 for (uint32_t i = 0; i < dex_file->NumMethodIds(); ++i) {
1556 // Randomly add a method from the dex file (with 50% chance).
1557 if (std::rand() % 2 != 0) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001558 info.AddMethodIndex(MethodHotness::kFlagHot, MethodReference(dex_file.get(), i));
Jeff Haof0a31f82017-03-27 15:50:37 -07001559 }
1560 }
1561 }
1562 return info.Save(fd);
1563}
1564
Calin Juravle940eb0c2017-01-30 19:30:44 -08001565bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==(
1566 const OfflineProfileMethodInfo& other) const {
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001567 if (inline_caches->size() != other.inline_caches->size()) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001568 return false;
1569 }
1570
1571 // We can't use a simple equality test because we need to match the dex files
Calin Juravle589e71e2017-03-03 16:05:05 -08001572 // of the inline caches which might have different profile indexes.
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001573 for (const auto& inline_cache_it : *inline_caches) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001574 uint16_t dex_pc = inline_cache_it.first;
1575 const DexPcData dex_pc_data = inline_cache_it.second;
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001576 const auto& other_it = other.inline_caches->find(dex_pc);
1577 if (other_it == other.inline_caches->end()) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001578 return false;
1579 }
1580 const DexPcData& other_dex_pc_data = other_it->second;
Calin Juravle589e71e2017-03-03 16:05:05 -08001581 if (dex_pc_data.is_megamorphic != other_dex_pc_data.is_megamorphic ||
1582 dex_pc_data.is_missing_types != other_dex_pc_data.is_missing_types) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001583 return false;
1584 }
1585 for (const ClassReference& class_ref : dex_pc_data.classes) {
1586 bool found = false;
1587 for (const ClassReference& other_class_ref : other_dex_pc_data.classes) {
1588 CHECK_LE(class_ref.dex_profile_index, dex_references.size());
1589 CHECK_LE(other_class_ref.dex_profile_index, other.dex_references.size());
1590 const DexReference& dex_ref = dex_references[class_ref.dex_profile_index];
1591 const DexReference& other_dex_ref = other.dex_references[other_class_ref.dex_profile_index];
1592 if (class_ref.type_index == other_class_ref.type_index &&
1593 dex_ref == other_dex_ref) {
1594 found = true;
1595 break;
1596 }
1597 }
1598 if (!found) {
1599 return false;
1600 }
1601 }
1602 }
1603 return true;
1604}
1605
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001606bool ProfileCompilationInfo::IsEmpty() const {
1607 DCHECK_EQ(info_.empty(), profile_key_map_.empty());
1608 return info_.empty();
1609}
1610
Calin Juravlecc3171a2017-05-19 16:47:53 -07001611ProfileCompilationInfo::InlineCacheMap*
1612ProfileCompilationInfo::DexFileData::FindOrAddMethod(uint16_t method_index) {
1613 return &(method_map.FindOrAdd(
1614 method_index,
1615 InlineCacheMap(std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile)))->second);
1616}
1617
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001618// Mark a method as executed at least once.
1619void ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, size_t index) {
1620 if ((flags & MethodHotness::kFlagStartup) != 0) {
1621 method_bitmap.StoreBit(MethodBitIndex(/*startup*/ true, index), /*value*/ true);
1622 }
1623 if ((flags & MethodHotness::kFlagPostStartup) != 0) {
1624 method_bitmap.StoreBit(MethodBitIndex(/*startup*/ false, index), /*value*/ true);
1625 }
1626 if ((flags & MethodHotness::kFlagHot) != 0) {
1627 method_map.FindOrAdd(
1628 index,
1629 InlineCacheMap(std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile)));
1630 }
1631}
1632
1633ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::DexFileData::GetHotnessInfo(
1634 uint32_t dex_method_index) const {
1635 MethodHotness ret;
1636 if (method_bitmap.LoadBit(MethodBitIndex(/*startup*/ true, dex_method_index))) {
1637 ret.AddFlag(MethodHotness::kFlagStartup);
1638 }
1639 if (method_bitmap.LoadBit(MethodBitIndex(/*startup*/ false, dex_method_index))) {
1640 ret.AddFlag(MethodHotness::kFlagPostStartup);
1641 }
1642 auto it = method_map.find(dex_method_index);
1643 if (it != method_map.end()) {
1644 ret.SetInlineCacheMap(&it->second);
1645 ret.AddFlag(MethodHotness::kFlagHot);
1646 }
1647 return ret;
1648}
1649
Calin Juravlecc3171a2017-05-19 16:47:53 -07001650ProfileCompilationInfo::DexPcData*
1651ProfileCompilationInfo::FindOrAddDexPc(InlineCacheMap* inline_cache, uint32_t dex_pc) {
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001652 return &(inline_cache->FindOrAdd(dex_pc, DexPcData(&arena_))->second);
Calin Juravlecc3171a2017-05-19 16:47:53 -07001653}
1654
Mathieu Chartier4f342b02017-07-21 17:12:39 -07001655std::unordered_set<std::string> ProfileCompilationInfo::GetClassDescriptors(
1656 const std::vector<const DexFile*>& dex_files) {
1657 std::unordered_set<std::string> ret;
1658 for (const DexFile* dex_file : dex_files) {
1659 const DexFileData* data = FindDexData(dex_file);
1660 if (data != nullptr) {
1661 for (dex::TypeIndex type_idx : data->class_set) {
1662 if (!dex_file->IsTypeIndexValid(type_idx)) {
1663 // Something went bad. The profile is probably corrupted. Abort and return an emtpy set.
1664 LOG(WARNING) << "Corrupted profile: invalid type index "
1665 << type_idx.index_ << " in dex " << dex_file->GetLocation();
1666 return std::unordered_set<std::string>();
1667 }
1668 const DexFile::TypeId& type_id = dex_file->GetTypeId(type_idx);
1669 ret.insert(dex_file->GetTypeDescriptor(type_id));
1670 }
1671 } else {
1672 VLOG(compiler) << "Failed to find profile data for " << dex_file->GetLocation();
1673 }
1674 }
1675 return ret;
1676}
1677
Calin Juravle31f2c152015-10-23 17:56:15 +01001678} // namespace art