blob: 805b9c185a9b6def0df014382d44272ccbac0dbc [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"
David Sehr891a50e2017-10-27 17:01:07 -070037#include "base/file_utils.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010038#include "base/mutex.h"
Calin Juravle877fd962016-01-05 14:29:29 +000039#include "base/scoped_flock.h"
Calin Juravle66f55232015-12-08 15:09:10 +000040#include "base/stl_util.h"
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -080041#include "base/systrace.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070042#include "base/time_utils.h"
Calin Juravle877fd962016-01-05 14:29:29 +000043#include "base/unix_file/fd_file.h"
Mathieu Chartier79c87da2017-10-10 11:54:29 -070044#include "dex_file_loader.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010045#include "jit/profiling_info.h"
Calin Juravle877fd962016-01-05 14:29:29 +000046#include "os.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010047#include "safe_map.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080048#include "utils.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010049
50namespace art {
51
Calin Juravle64142952016-03-21 14:37:55 +000052const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' };
Shubham Ajmeraafbbf182017-08-04 14:33:34 -070053// Last profile version: merge profiles directly from the file without creating
54// profile_compilation_info object. All the profile line headers are now placed together
55// before corresponding method_encodings and class_ids.
56const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '1', '0', '\0' };
Calin Juravle64142952016-03-21 14:37:55 +000057
58static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
59
Calin Juravlec4588572016-06-08 14:24:13 +010060// 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 +010061// Used to facilitate testing profile guided compilation across a large number of apps
Calin Juravlec4588572016-06-08 14:24:13 +010062// using the same test profile.
63static constexpr bool kDebugIgnoreChecksum = false;
64
Calin Juravle589e71e2017-03-03 16:05:05 -080065static constexpr uint8_t kIsMissingTypesEncoding = 6;
66static constexpr uint8_t kIsMegamorphicEncoding = 7;
Calin Juravle940eb0c2017-01-30 19:30:44 -080067
68static_assert(sizeof(InlineCache::kIndividualCacheSize) == sizeof(uint8_t),
69 "InlineCache::kIndividualCacheSize does not have the expect type size");
Calin Juravle589e71e2017-03-03 16:05:05 -080070static_assert(InlineCache::kIndividualCacheSize < kIsMegamorphicEncoding,
71 "InlineCache::kIndividualCacheSize is larger than expected");
72static_assert(InlineCache::kIndividualCacheSize < kIsMissingTypesEncoding,
Calin Juravle940eb0c2017-01-30 19:30:44 -080073 "InlineCache::kIndividualCacheSize is larger than expected");
74
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -070075static bool ChecksumMatch(uint32_t dex_file_checksum, uint32_t checksum) {
76 return kDebugIgnoreChecksum || dex_file_checksum == checksum;
77}
78
Calin Juravlecc3171a2017-05-19 16:47:53 -070079ProfileCompilationInfo::ProfileCompilationInfo(ArenaPool* custom_arena_pool)
Calin Juravlee6f87cc2017-05-24 17:41:05 -070080 : default_arena_pool_(),
Vladimir Markoca6fff82017-10-03 14:49:14 +010081 allocator_(custom_arena_pool),
82 info_(allocator_.Adapter(kArenaAllocProfile)),
83 profile_key_map_(std::less<const std::string>(), allocator_.Adapter(kArenaAllocProfile)) {
Calin Juravlecc3171a2017-05-19 16:47:53 -070084}
85
86ProfileCompilationInfo::ProfileCompilationInfo()
Calin Juravlee6f87cc2017-05-24 17:41:05 -070087 : default_arena_pool_(/*use_malloc*/true, /*low_4gb*/false, "ProfileCompilationInfo"),
Vladimir Markoca6fff82017-10-03 14:49:14 +010088 allocator_(&default_arena_pool_),
89 info_(allocator_.Adapter(kArenaAllocProfile)),
90 profile_key_map_(std::less<const std::string>(), allocator_.Adapter(kArenaAllocProfile)) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -070091}
92
93ProfileCompilationInfo::~ProfileCompilationInfo() {
Vladimir Markoca6fff82017-10-03 14:49:14 +010094 VLOG(profiler) << Dumpable<MemStats>(allocator_.GetMemStats());
Calin Juravle798ba162017-05-23 23:01:53 -070095 for (DexFileData* data : info_) {
96 delete data;
97 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -070098}
99
Calin Juravle940eb0c2017-01-30 19:30:44 -0800100void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx,
101 const dex::TypeIndex& type_idx) {
Calin Juravle589e71e2017-03-03 16:05:05 -0800102 if (is_megamorphic || is_missing_types) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800103 return;
104 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700105
106 // Perform an explicit lookup for the type instead of directly emplacing the
107 // element. We do this because emplace() allocates the node before doing the
108 // lookup and if it then finds an identical element, it shall deallocate the
109 // node. For Arena allocations, that's essentially a leak.
110 ClassReference ref(dex_profile_idx, type_idx);
111 auto it = classes.find(ref);
112 if (it != classes.end()) {
113 // The type index exists.
114 return;
115 }
116
117 // Check if the adding the type will cause the cache to become megamorphic.
118 if (classes.size() + 1 >= InlineCache::kIndividualCacheSize) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800119 is_megamorphic = true;
120 classes.clear();
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700121 return;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800122 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700123
124 // The type does not exist and the inline cache will not be megamorphic.
125 classes.insert(ref);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800126}
127
Calin Juravle34900cc2016-02-05 16:19:19 +0000128// Transform the actual dex location into relative paths.
129// Note: this is OK because we don't store profiles of different apps into the same file.
130// Apps with split apks don't cause trouble because each split has a different name and will not
131// collide with other entries.
Calin Juravle31708b72016-02-05 19:44:05 +0000132std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_location) {
Calin Juravle34900cc2016-02-05 16:19:19 +0000133 DCHECK(!dex_location.empty());
134 size_t last_sep_index = dex_location.find_last_of('/');
135 if (last_sep_index == std::string::npos) {
136 return dex_location;
137 } else {
Calin Juravle31708b72016-02-05 19:44:05 +0000138 DCHECK(last_sep_index < dex_location.size());
139 return dex_location.substr(last_sep_index + 1);
Calin Juravle34900cc2016-02-05 16:19:19 +0000140 }
141}
142
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700143bool ProfileCompilationInfo::AddMethodIndex(MethodHotness::Flag flags, const MethodReference& ref) {
144 DexFileData* data = GetOrAddDexFileData(ref.dex_file);
145 if (data == nullptr) {
146 return false;
147 }
Calin Juravle1ad1e3f2017-09-19 18:20:37 -0700148 return data->AddMethod(flags, ref.index);
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700149}
150
151bool ProfileCompilationInfo::AddMethodIndex(MethodHotness::Flag flags,
152 const std::string& dex_location,
153 uint32_t checksum,
154 uint16_t method_idx,
155 uint32_t num_method_ids) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700156 DexFileData* data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location),
157 checksum,
158 num_method_ids);
159 if (data == nullptr) {
160 return false;
161 }
Calin Juravle1ad1e3f2017-09-19 18:20:37 -0700162 return data->AddMethod(flags, method_idx);
Mathieu Chartierea650f32017-05-24 12:04:13 -0700163}
164
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700165bool ProfileCompilationInfo::AddMethods(const std::vector<ProfileMethodInfo>& methods) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800166 for (const ProfileMethodInfo& method : methods) {
167 if (!AddMethod(method)) {
Calin Juravle67265462016-03-18 16:23:40 +0000168 return false;
169 }
170 }
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700171 return true;
172}
173
174bool ProfileCompilationInfo::AddClasses(const std::set<DexCacheResolvedClasses>& resolved_classes) {
Calin Juravle67265462016-03-18 16:23:40 +0000175 for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
176 if (!AddResolvedClasses(dex_cache)) {
177 return false;
178 }
179 }
180 return true;
181}
182
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700183bool ProfileCompilationInfo::MergeWith(const std::string& filename) {
184 std::string error;
185 int flags = O_RDONLY | O_NOFOLLOW | O_CLOEXEC;
186 ScopedFlock profile_file = LockedFile::Open(filename.c_str(), flags,
187 /*block*/false, &error);
188
189 if (profile_file.get() == nullptr) {
190 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
191 return false;
192 }
193
194 int fd = profile_file->Fd();
195
196 ProfileLoadSatus status = LoadInternal(fd, &error);
197 if (status == kProfileLoadSuccess) {
198 return true;
199 }
200
201 LOG(WARNING) << "Could not load profile data from file " << filename << ": " << error;
202 return false;
203}
204
Calin Juravledcab1902017-05-12 19:18:47 -0700205bool ProfileCompilationInfo::Load(const std::string& filename, bool clear_if_invalid) {
Calin Juravle67265462016-03-18 16:23:40 +0000206 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravle67265462016-03-18 16:23:40 +0000207 std::string error;
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700208
209 if (!IsEmpty()) {
210 return kProfileLoadWouldOverwiteData;
211 }
212
Calin Juravledf674c42017-04-27 19:30:16 -0700213 int flags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
214 // There's no need to fsync profile data right away. We get many chances
215 // to write it again in case something goes wrong. We can rely on a simple
216 // close(), no sync, and let to the kernel decide when to write to disk.
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100217 ScopedFlock profile_file = LockedFile::Open(filename.c_str(), flags,
218 /*block*/false, &error);
219
220 if (profile_file.get() == nullptr) {
Calin Juravle67265462016-03-18 16:23:40 +0000221 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
222 return false;
223 }
224
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100225 int fd = profile_file->Fd();
Calin Juravle67265462016-03-18 16:23:40 +0000226
Calin Juravledcab1902017-05-12 19:18:47 -0700227 ProfileLoadSatus status = LoadInternal(fd, &error);
Calin Juravle5d1bd0a2016-03-24 20:33:22 +0000228 if (status == kProfileLoadSuccess) {
Calin Juravledcab1902017-05-12 19:18:47 -0700229 return true;
230 }
231
232 if (clear_if_invalid &&
233 ((status == kProfileLoadVersionMismatch) || (status == kProfileLoadBadData))) {
234 LOG(WARNING) << "Clearing bad or obsolete profile data from file "
235 << filename << ": " << error;
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100236 if (profile_file->ClearContent()) {
Calin Juravledcab1902017-05-12 19:18:47 -0700237 return true;
Calin Juravle5d1bd0a2016-03-24 20:33:22 +0000238 } else {
Calin Juravledcab1902017-05-12 19:18:47 -0700239 PLOG(WARNING) << "Could not clear profile file: " << filename;
240 return false;
Calin Juravle5d1bd0a2016-03-24 20:33:22 +0000241 }
Calin Juravledcab1902017-05-12 19:18:47 -0700242 }
243
244 LOG(WARNING) << "Could not load profile data from file " << filename << ": " << error;
245 return false;
246}
247
248bool ProfileCompilationInfo::Save(const std::string& filename, uint64_t* bytes_written) {
249 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravledcab1902017-05-12 19:18:47 -0700250 std::string error;
251 int flags = O_WRONLY | O_NOFOLLOW | O_CLOEXEC;
252 // There's no need to fsync profile data right away. We get many chances
253 // to write it again in case something goes wrong. We can rely on a simple
254 // close(), no sync, and let to the kernel decide when to write to disk.
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100255 ScopedFlock profile_file = LockedFile::Open(filename.c_str(), flags,
256 /*block*/false, &error);
257 if (profile_file.get() == nullptr) {
Calin Juravledcab1902017-05-12 19:18:47 -0700258 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
Calin Juravle67265462016-03-18 16:23:40 +0000259 return false;
260 }
261
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100262 int fd = profile_file->Fd();
Calin Juravledcab1902017-05-12 19:18:47 -0700263
Calin Juravle5d1bd0a2016-03-24 20:33:22 +0000264 // We need to clear the data because we don't support appending to the profiles yet.
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100265 if (!profile_file->ClearContent()) {
Calin Juravle67265462016-03-18 16:23:40 +0000266 PLOG(WARNING) << "Could not clear profile file: " << filename;
267 return false;
268 }
269
270 // This doesn't need locking because we are trying to lock the file for exclusive
271 // access and fail immediately if we can't.
272 bool result = Save(fd);
273 if (result) {
Calin Juravledcab1902017-05-12 19:18:47 -0700274 int64_t size = GetFileSizeBytes(filename);
275 if (size != -1) {
276 VLOG(profiler)
277 << "Successfully saved profile info to " << filename << " Size: "
278 << size;
279 if (bytes_written != nullptr) {
280 *bytes_written = static_cast<uint64_t>(size);
281 }
Calin Juravle67265462016-03-18 16:23:40 +0000282 }
283 } else {
284 VLOG(profiler) << "Failed to save profile info to " << filename;
285 }
286 return result;
287}
288
Calin Juravle64142952016-03-21 14:37:55 +0000289// Returns true if all the bytes were successfully written to the file descriptor.
290static bool WriteBuffer(int fd, const uint8_t* buffer, size_t byte_count) {
291 while (byte_count > 0) {
292 int bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer, byte_count));
293 if (bytes_written == -1) {
Calin Juravle877fd962016-01-05 14:29:29 +0000294 return false;
295 }
Calin Juravle64142952016-03-21 14:37:55 +0000296 byte_count -= bytes_written; // Reduce the number of remaining bytes.
297 buffer += bytes_written; // Move the buffer forward.
298 }
Calin Juravle877fd962016-01-05 14:29:29 +0000299 return true;
Calin Juravle31f2c152015-10-23 17:56:15 +0100300}
301
Calin Juravle64142952016-03-21 14:37:55 +0000302// Add the string bytes to the buffer.
303static void AddStringToBuffer(std::vector<uint8_t>* buffer, const std::string& value) {
304 buffer->insert(buffer->end(), value.begin(), value.end());
305}
306
307// Insert each byte, from low to high into the buffer.
308template <typename T>
309static void AddUintToBuffer(std::vector<uint8_t>* buffer, T value) {
310 for (size_t i = 0; i < sizeof(T); i++) {
311 buffer->push_back((value >> (i * kBitsPerByte)) & 0xff);
312 }
313}
314
315static constexpr size_t kLineHeaderSize =
Calin Juravle940eb0c2017-01-30 19:30:44 -0800316 2 * sizeof(uint16_t) + // class_set.size + dex_location.size
Mathieu Chartierea650f32017-05-24 12:04:13 -0700317 3 * sizeof(uint32_t); // method_map.size + checksum + num_method_ids
Calin Juravle31f2c152015-10-23 17:56:15 +0100318
319/**
320 * Serialization format:
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700321 * [profile_header, zipped[[profile_line_header1, profile_line_header2...],[profile_line_data1,
322 * profile_line_data2...]]]
323 * profile_header:
324 * magic,version,number_of_dex_files,uncompressed_size_of_zipped_data,compressed_data_size
325 * profile_line_header:
326 * dex_location,number_of_classes,methods_region_size,dex_location_checksum,num_method_ids
327 * profile_line_data:
328 * method_encoding_1,method_encoding_2...,class_id1,class_id2...,startup/post startup bitmap
Calin Juravle940eb0c2017-01-30 19:30:44 -0800329 * The method_encoding is:
330 * method_id,number_of_inline_caches,inline_cache1,inline_cache2...
331 * The inline_cache is:
332 * dex_pc,[M|dex_map_size], dex_profile_index,class_id1,class_id2...,dex_profile_index2,...
333 * dex_map_size is the number of dex_indeces that follows.
334 * Classes are grouped per their dex files and the line
335 * `dex_profile_index,class_id1,class_id2...,dex_profile_index2,...` encodes the
336 * mapping from `dex_profile_index` to the set of classes `class_id1,class_id2...`
Calin Juravle589e71e2017-03-03 16:05:05 -0800337 * M stands for megamorphic or missing types and it's encoded as either
338 * the byte kIsMegamorphicEncoding or kIsMissingTypesEncoding.
Calin Juravle940eb0c2017-01-30 19:30:44 -0800339 * When present, there will be no class ids following.
Calin Juravle31f2c152015-10-23 17:56:15 +0100340 **/
Calin Juravle2e2db782016-02-23 12:00:03 +0000341bool ProfileCompilationInfo::Save(int fd) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000342 uint64_t start = NanoTime();
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -0800343 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravle2e2db782016-02-23 12:00:03 +0000344 DCHECK_GE(fd, 0);
Calin Juravle64142952016-03-21 14:37:55 +0000345
Calin Juravle64142952016-03-21 14:37:55 +0000346 // Use a vector wrapper to avoid keeping track of offsets when we add elements.
347 std::vector<uint8_t> buffer;
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000348 if (!WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic))) {
349 return false;
350 }
351 if (!WriteBuffer(fd, kProfileVersion, sizeof(kProfileVersion))) {
352 return false;
353 }
Calin Juravle940eb0c2017-01-30 19:30:44 -0800354 DCHECK_LE(info_.size(), std::numeric_limits<uint8_t>::max());
355 AddUintToBuffer(&buffer, static_cast<uint8_t>(info_.size()));
Calin Juravle64142952016-03-21 14:37:55 +0000356
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000357 uint32_t required_capacity = 0;
358 for (const DexFileData* dex_data_ptr : info_) {
359 const DexFileData& dex_data = *dex_data_ptr;
360 uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
361 required_capacity += kLineHeaderSize +
362 dex_data.profile_key.size() +
363 sizeof(uint16_t) * dex_data.class_set.size() +
Mathieu Chartierea650f32017-05-24 12:04:13 -0700364 methods_region_size +
365 dex_data.bitmap_storage.size();
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000366 }
Mathieu Chartierf2e2af82017-07-12 21:09:57 -0700367 // Allow large profiles for non target builds for the case where we are merging many profiles
368 // to generate a boot image profile.
369 if (kIsTargetBuild && required_capacity > kProfileSizeErrorThresholdInBytes) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000370 LOG(ERROR) << "Profile data size exceeds "
371 << std::to_string(kProfileSizeErrorThresholdInBytes)
372 << " bytes. Profile will not be written to disk.";
373 return false;
374 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000375 AddUintToBuffer(&buffer, required_capacity);
376 if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
377 return false;
378 }
379 // Make sure that the buffer has enough capacity to avoid repeated resizings
380 // while we add data.
381 buffer.reserve(required_capacity);
382 buffer.clear();
383
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700384 // Dex files must be written in the order of their profile index. This
Calin Juravlee0ac1152017-02-13 19:03:47 -0800385 // avoids writing the index in the output file and simplifies the parsing logic.
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700386 // Write profile line headers.
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700387 for (const DexFileData* dex_data_ptr : info_) {
388 const DexFileData& dex_data = *dex_data_ptr;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800389
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700390 if (dex_data.profile_key.size() >= kMaxDexFileKeyLength) {
Calin Juravle64142952016-03-21 14:37:55 +0000391 LOG(WARNING) << "DexFileKey exceeds allocated limit";
392 return false;
393 }
394
Calin Juravle940eb0c2017-01-30 19:30:44 -0800395 uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
Calin Juravle64142952016-03-21 14:37:55 +0000396
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700397 DCHECK_LE(dex_data.profile_key.size(), std::numeric_limits<uint16_t>::max());
Calin Juravle64142952016-03-21 14:37:55 +0000398 DCHECK_LE(dex_data.class_set.size(), std::numeric_limits<uint16_t>::max());
Mathieu Chartierea650f32017-05-24 12:04:13 -0700399 // Write profile line header.
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700400 AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.profile_key.size()));
Calin Juravle64142952016-03-21 14:37:55 +0000401 AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.class_set.size()));
Calin Juravle940eb0c2017-01-30 19:30:44 -0800402 AddUintToBuffer(&buffer, methods_region_size); // uint32_t
Calin Juravle64142952016-03-21 14:37:55 +0000403 AddUintToBuffer(&buffer, dex_data.checksum); // uint32_t
Mathieu Chartierea650f32017-05-24 12:04:13 -0700404 AddUintToBuffer(&buffer, dex_data.num_method_ids); // uint32_t
Calin Juravle64142952016-03-21 14:37:55 +0000405
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700406 AddStringToBuffer(&buffer, dex_data.profile_key);
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700407 }
408
409 for (const DexFileData* dex_data_ptr : info_) {
410 const DexFileData& dex_data = *dex_data_ptr;
411
412 // Note that we allow dex files without any methods or classes, so that
413 // inline caches can refer valid dex files.
Calin Juravle64142952016-03-21 14:37:55 +0000414
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000415 uint16_t last_method_index = 0;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800416 for (const auto& method_it : dex_data.method_map) {
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000417 // Store the difference between the method indices. The SafeMap is ordered by
418 // method_id, so the difference will always be non negative.
419 DCHECK_GE(method_it.first, last_method_index);
420 uint16_t diff_with_last_method_index = method_it.first - last_method_index;
421 last_method_index = method_it.first;
422 AddUintToBuffer(&buffer, diff_with_last_method_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800423 AddInlineCacheToBuffer(&buffer, method_it.second);
Calin Juravle31f2c152015-10-23 17:56:15 +0100424 }
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000425
426 uint16_t last_class_index = 0;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800427 for (const auto& class_id : dex_data.class_set) {
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000428 // Store the difference between the class indices. The set is ordered by
429 // class_id, so the difference will always be non negative.
430 DCHECK_GE(class_id.index_, last_class_index);
431 uint16_t diff_with_last_class_index = class_id.index_ - last_class_index;
432 last_class_index = class_id.index_;
433 AddUintToBuffer(&buffer, diff_with_last_class_index);
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800434 }
Mathieu Chartierea650f32017-05-24 12:04:13 -0700435
436 buffer.insert(buffer.end(),
437 dex_data.bitmap_storage.begin(),
438 dex_data.bitmap_storage.end());
Calin Juravle31f2c152015-10-23 17:56:15 +0100439 }
440
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000441 uint32_t output_size = 0;
442 std::unique_ptr<uint8_t[]> compressed_buffer = DeflateBuffer(buffer.data(),
443 required_capacity,
444 &output_size);
445
Shubham Ajmerad704f0b2017-07-26 16:33:35 -0700446 if (output_size > kProfileSizeWarningThresholdInBytes) {
447 LOG(WARNING) << "Profile data size exceeds "
448 << std::to_string(kProfileSizeWarningThresholdInBytes);
449 }
450
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000451 buffer.clear();
452 AddUintToBuffer(&buffer, output_size);
453
454 if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
455 return false;
456 }
457 if (!WriteBuffer(fd, compressed_buffer.get(), output_size)) {
458 return false;
459 }
460 uint64_t total_time = NanoTime() - start;
461 VLOG(profiler) << "Compressed from "
462 << std::to_string(required_capacity)
463 << " to "
464 << std::to_string(output_size);
465 VLOG(profiler) << "Time to save profile: " << std::to_string(total_time);
466 return true;
Calin Juravle226501b2015-12-11 14:41:31 +0000467}
468
Calin Juravle940eb0c2017-01-30 19:30:44 -0800469void ProfileCompilationInfo::AddInlineCacheToBuffer(std::vector<uint8_t>* buffer,
470 const InlineCacheMap& inline_cache_map) {
471 // Add inline cache map size.
472 AddUintToBuffer(buffer, static_cast<uint16_t>(inline_cache_map.size()));
473 if (inline_cache_map.size() == 0) {
474 return;
475 }
476 for (const auto& inline_cache_it : inline_cache_map) {
477 uint16_t dex_pc = inline_cache_it.first;
478 const DexPcData dex_pc_data = inline_cache_it.second;
479 const ClassSet& classes = dex_pc_data.classes;
480
481 // Add the dex pc.
482 AddUintToBuffer(buffer, dex_pc);
483
Calin Juravle589e71e2017-03-03 16:05:05 -0800484 // Add the megamorphic/missing_types encoding if needed and continue.
485 // In either cases we don't add any classes to the profiles and so there's
486 // no point to continue.
487 // TODO(calin): in case we miss types there is still value to add the
488 // rest of the classes. They can be added without bumping the profile version.
489 if (dex_pc_data.is_missing_types) {
490 DCHECK(!dex_pc_data.is_megamorphic); // at this point the megamorphic flag should not be set.
491 DCHECK_EQ(classes.size(), 0u);
492 AddUintToBuffer(buffer, kIsMissingTypesEncoding);
493 continue;
494 } else if (dex_pc_data.is_megamorphic) {
495 DCHECK_EQ(classes.size(), 0u);
496 AddUintToBuffer(buffer, kIsMegamorphicEncoding);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800497 continue;
498 }
499
500 DCHECK_LT(classes.size(), InlineCache::kIndividualCacheSize);
501 DCHECK_NE(classes.size(), 0u) << "InlineCache contains a dex_pc with 0 classes";
502
503 SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
504 // Group the classes by dex. We expect that most of the classes will come from
505 // the same dex, so this will be more efficient than encoding the dex index
506 // for each class reference.
507 GroupClassesByDex(classes, &dex_to_classes_map);
508 // Add the dex map size.
509 AddUintToBuffer(buffer, static_cast<uint8_t>(dex_to_classes_map.size()));
510 for (const auto& dex_it : dex_to_classes_map) {
511 uint8_t dex_profile_index = dex_it.first;
512 const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
513 // Add the dex profile index.
514 AddUintToBuffer(buffer, dex_profile_index);
515 // Add the the number of classes for each dex profile index.
516 AddUintToBuffer(buffer, static_cast<uint8_t>(dex_classes.size()));
517 for (size_t i = 0; i < dex_classes.size(); i++) {
518 // Add the type index of the classes.
519 AddUintToBuffer(buffer, dex_classes[i].index_);
520 }
521 }
522 }
523}
524
525uint32_t ProfileCompilationInfo::GetMethodsRegionSize(const DexFileData& dex_data) {
526 // ((uint16_t)method index + (uint16_t)inline cache size) * number of methods
527 uint32_t size = 2 * sizeof(uint16_t) * dex_data.method_map.size();
528 for (const auto& method_it : dex_data.method_map) {
529 const InlineCacheMap& inline_cache = method_it.second;
530 size += sizeof(uint16_t) * inline_cache.size(); // dex_pc
531 for (const auto& inline_cache_it : inline_cache) {
532 const ClassSet& classes = inline_cache_it.second.classes;
533 SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
534 GroupClassesByDex(classes, &dex_to_classes_map);
535 size += sizeof(uint8_t); // dex_to_classes_map size
536 for (const auto& dex_it : dex_to_classes_map) {
537 size += sizeof(uint8_t); // dex profile index
538 size += sizeof(uint8_t); // number of classes
539 const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
540 size += sizeof(uint16_t) * dex_classes.size(); // the actual classes
541 }
542 }
543 }
544 return size;
545}
546
547void ProfileCompilationInfo::GroupClassesByDex(
548 const ClassSet& classes,
549 /*out*/SafeMap<uint8_t, std::vector<dex::TypeIndex>>* dex_to_classes_map) {
550 for (const auto& classes_it : classes) {
551 auto dex_it = dex_to_classes_map->FindOrAdd(classes_it.dex_profile_index);
552 dex_it->second.push_back(classes_it.type_index);
553 }
554}
555
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800556ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData(
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700557 const std::string& profile_key,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700558 uint32_t checksum,
559 uint32_t num_method_ids) {
Vladimir Marko3bada4b2017-05-19 12:32:47 +0100560 const auto profile_index_it = profile_key_map_.FindOrAdd(profile_key, profile_key_map_.size());
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700561 if (profile_key_map_.size() > std::numeric_limits<uint8_t>::max()) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800562 // Allow only 255 dex files to be profiled. This allows us to save bytes
563 // when encoding. The number is well above what we expect for normal applications.
564 if (kIsDebugBuild) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700565 LOG(ERROR) << "Exceeded the maximum number of dex files (255). Something went wrong";
Calin Juravle940eb0c2017-01-30 19:30:44 -0800566 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700567 profile_key_map_.erase(profile_key);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800568 return nullptr;
Calin Juravle998c2162015-12-21 15:39:33 +0200569 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700570
571 uint8_t profile_index = profile_index_it->second;
572 if (info_.size() <= profile_index) {
573 // This is a new addition. Add it to the info_ array.
Vladimir Markoca6fff82017-10-03 14:49:14 +0100574 DexFileData* dex_file_data = new (&allocator_) DexFileData(
575 &allocator_,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700576 profile_key,
577 checksum,
578 profile_index,
579 num_method_ids);
Calin Juravlecc3171a2017-05-19 16:47:53 -0700580 info_.push_back(dex_file_data);
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700581 }
582 DexFileData* result = info_[profile_index];
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700583
584 // Check that the checksum matches.
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700585 // 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 -0700586 if (result->checksum != checksum) {
587 LOG(WARNING) << "Checksum mismatch for dex " << profile_key;
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800588 return nullptr;
589 }
Shubham Ajmera61200a02017-08-30 16:29:41 -0700590
591 // DCHECK that profile info map key is consistent with the one stored in the dex file data.
592 // This should always be the case since since the cache map is managed by ProfileCompilationInfo.
593 DCHECK_EQ(profile_key, result->profile_key);
594 DCHECK_EQ(profile_index, result->profile_index);
Calin Juravle1ad1e3f2017-09-19 18:20:37 -0700595
596 if (num_method_ids != result->num_method_ids) {
597 // This should not happen... added to help investigating b/65812889.
598 LOG(ERROR) << "num_method_ids mismatch for dex " << profile_key
599 << ", expected=" << num_method_ids
600 << ", actual=" << result->num_method_ids;
601 return nullptr;
602 }
Shubham Ajmera61200a02017-08-30 16:29:41 -0700603
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700604 return result;
605}
606
607const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData(
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700608 const std::string& profile_key,
609 uint32_t checksum,
610 bool verify_checksum) const {
Vladimir Marko3bada4b2017-05-19 12:32:47 +0100611 const auto profile_index_it = profile_key_map_.find(profile_key);
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700612 if (profile_index_it == profile_key_map_.end()) {
613 return nullptr;
614 }
615
616 uint8_t profile_index = profile_index_it->second;
617 const DexFileData* result = info_[profile_index];
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700618 if (verify_checksum && !ChecksumMatch(result->checksum, checksum)) {
619 return nullptr;
620 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700621 DCHECK_EQ(profile_key, result->profile_key);
622 DCHECK_EQ(profile_index, result->profile_index);
623 return result;
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800624}
625
626bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& classes) {
627 const std::string dex_location = GetProfileDexFileKey(classes.GetDexLocation());
628 const uint32_t checksum = classes.GetLocationChecksum();
Mathieu Chartierea650f32017-05-24 12:04:13 -0700629 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum, classes.NumMethodIds());
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800630 if (data == nullptr) {
Calin Juravle998c2162015-12-21 15:39:33 +0200631 return false;
632 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800633 data->class_set.insert(classes.GetClasses().begin(), classes.GetClasses().end());
634 return true;
635}
636
Calin Juravle940eb0c2017-01-30 19:30:44 -0800637bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
638 uint32_t dex_checksum,
639 uint16_t method_index,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700640 uint32_t num_method_ids,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800641 const OfflineProfileMethodInfo& pmi) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700642 DexFileData* const data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location),
643 dex_checksum,
644 num_method_ids);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800645 if (data == nullptr) { // checksum mismatch
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800646 return false;
647 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700648 // Add the method.
Calin Juravlecc3171a2017-05-19 16:47:53 -0700649 InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700650
651 if (pmi.inline_caches == nullptr) {
652 // If we don't have inline caches return success right away.
653 return true;
654 }
655 for (const auto& pmi_inline_cache_it : *pmi.inline_caches) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800656 uint16_t pmi_ic_dex_pc = pmi_inline_cache_it.first;
657 const DexPcData& pmi_ic_dex_pc_data = pmi_inline_cache_it.second;
Calin Juravlecc3171a2017-05-19 16:47:53 -0700658 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, pmi_ic_dex_pc);
659 if (dex_pc_data->is_missing_types || dex_pc_data->is_megamorphic) {
Calin Juravle589e71e2017-03-03 16:05:05 -0800660 // We are already megamorphic or we are missing types; no point in going forward.
Calin Juravle940eb0c2017-01-30 19:30:44 -0800661 continue;
662 }
Calin Juravle589e71e2017-03-03 16:05:05 -0800663
664 if (pmi_ic_dex_pc_data.is_missing_types) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700665 dex_pc_data->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -0800666 continue;
667 }
668 if (pmi_ic_dex_pc_data.is_megamorphic) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700669 dex_pc_data->SetIsMegamorphic();
Calin Juravle589e71e2017-03-03 16:05:05 -0800670 continue;
671 }
672
Calin Juravle940eb0c2017-01-30 19:30:44 -0800673 for (const ClassReference& class_ref : pmi_ic_dex_pc_data.classes) {
674 const DexReference& dex_ref = pmi.dex_references[class_ref.dex_profile_index];
675 DexFileData* class_dex_data = GetOrAddDexFileData(
676 GetProfileDexFileKey(dex_ref.dex_location),
Mathieu Chartierea650f32017-05-24 12:04:13 -0700677 dex_ref.dex_checksum,
678 dex_ref.num_method_ids);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800679 if (class_dex_data == nullptr) { // checksum mismatch
680 return false;
681 }
Calin Juravlecc3171a2017-05-19 16:47:53 -0700682 dex_pc_data->AddClass(class_dex_data->profile_index, class_ref.type_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800683 }
684 }
685 return true;
686}
687
688bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700689 DexFileData* const data = GetOrAddDexFileData(pmi.ref.dex_file);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800690 if (data == nullptr) { // checksum mismatch
691 return false;
692 }
Mathieu Chartierfc8b4222017-09-17 13:44:24 -0700693 InlineCacheMap* inline_cache = data->FindOrAddMethod(pmi.ref.index);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800694
695 for (const ProfileMethodInfo::ProfileInlineCache& cache : pmi.inline_caches) {
Calin Juravle589e71e2017-03-03 16:05:05 -0800696 if (cache.is_missing_types) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700697 FindOrAddDexPc(inline_cache, cache.dex_pc)->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -0800698 continue;
699 }
Mathieu Chartierdbddc222017-05-24 12:04:13 -0700700 for (const TypeReference& class_ref : cache.classes) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700701 DexFileData* class_dex_data = GetOrAddDexFileData(class_ref.dex_file);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800702 if (class_dex_data == nullptr) { // checksum mismatch
703 return false;
704 }
Calin Juravlecc3171a2017-05-19 16:47:53 -0700705 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, cache.dex_pc);
706 if (dex_pc_data->is_missing_types) {
Calin Juravle589e71e2017-03-03 16:05:05 -0800707 // Don't bother adding classes if we are missing types.
708 break;
709 }
Mathieu Chartierfc8b4222017-09-17 13:44:24 -0700710 dex_pc_data->AddClass(class_dex_data->profile_index, class_ref.TypeIndex());
Calin Juravle940eb0c2017-01-30 19:30:44 -0800711 }
712 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800713 return true;
714}
715
716bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location,
717 uint32_t checksum,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700718 dex::TypeIndex type_idx,
719 uint32_t num_method_ids) {
720 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum, num_method_ids);
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800721 if (data == nullptr) {
722 return false;
723 }
Jeff Hao54b58552016-11-16 15:15:04 -0800724 data->class_set.insert(type_idx);
Calin Juravle998c2162015-12-21 15:39:33 +0200725 return true;
726}
727
Andreas Gampe37c58462017-03-27 15:14:27 -0700728#define READ_UINT(type, buffer, dest, error) \
729 do { \
730 if (!(buffer).ReadUintAndAdvance<type>(&(dest))) { \
731 *(error) = "Could not read "#dest; \
732 return false; \
733 } \
734 } \
Calin Juravle940eb0c2017-01-30 19:30:44 -0800735 while (false)
736
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700737bool ProfileCompilationInfo::ReadInlineCache(
738 SafeBuffer& buffer,
739 uint8_t number_of_dex_files,
740 const SafeMap<uint8_t, uint8_t>& dex_profile_index_remap,
741 /*out*/ InlineCacheMap* inline_cache,
742 /*out*/ std::string* error) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800743 uint16_t inline_cache_size;
744 READ_UINT(uint16_t, buffer, inline_cache_size, error);
745 for (; inline_cache_size > 0; inline_cache_size--) {
746 uint16_t dex_pc;
747 uint8_t dex_to_classes_map_size;
748 READ_UINT(uint16_t, buffer, dex_pc, error);
749 READ_UINT(uint8_t, buffer, dex_to_classes_map_size, error);
Calin Juravlecc3171a2017-05-19 16:47:53 -0700750 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, dex_pc);
Calin Juravle589e71e2017-03-03 16:05:05 -0800751 if (dex_to_classes_map_size == kIsMissingTypesEncoding) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700752 dex_pc_data->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -0800753 continue;
754 }
755 if (dex_to_classes_map_size == kIsMegamorphicEncoding) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700756 dex_pc_data->SetIsMegamorphic();
Calin Juravle940eb0c2017-01-30 19:30:44 -0800757 continue;
758 }
759 for (; dex_to_classes_map_size > 0; dex_to_classes_map_size--) {
760 uint8_t dex_profile_index;
761 uint8_t dex_classes_size;
762 READ_UINT(uint8_t, buffer, dex_profile_index, error);
763 READ_UINT(uint8_t, buffer, dex_classes_size, error);
764 if (dex_profile_index >= number_of_dex_files) {
765 *error = "dex_profile_index out of bounds ";
766 *error += std::to_string(dex_profile_index) + " " + std::to_string(number_of_dex_files);
767 return false;
768 }
769 for (; dex_classes_size > 0; dex_classes_size--) {
770 uint16_t type_index;
771 READ_UINT(uint16_t, buffer, type_index, error);
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700772 dex_pc_data->AddClass(dex_profile_index_remap.Get(dex_profile_index),
773 dex::TypeIndex(type_index));
Calin Juravle940eb0c2017-01-30 19:30:44 -0800774 }
775 }
776 }
777 return true;
778}
779
780bool ProfileCompilationInfo::ReadMethods(SafeBuffer& buffer,
781 uint8_t number_of_dex_files,
782 const ProfileLineHeader& line_header,
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700783 const SafeMap<uint8_t, uint8_t>& dex_profile_index_remap,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800784 /*out*/std::string* error) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000785 uint32_t unread_bytes_before_operation = buffer.CountUnreadBytes();
786 if (unread_bytes_before_operation < line_header.method_region_size_bytes) {
787 *error += "Profile EOF reached prematurely for ReadMethod";
788 return kProfileLoadBadData;
789 }
790 size_t expected_unread_bytes_after_operation = buffer.CountUnreadBytes()
791 - line_header.method_region_size_bytes;
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000792 uint16_t last_method_index = 0;
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000793 while (buffer.CountUnreadBytes() > expected_unread_bytes_after_operation) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700794 DexFileData* const data = GetOrAddDexFileData(line_header.dex_location,
795 line_header.checksum,
796 line_header.num_method_ids);
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000797 uint16_t diff_with_last_method_index;
798 READ_UINT(uint16_t, buffer, diff_with_last_method_index, error);
799 uint16_t method_index = last_method_index + diff_with_last_method_index;
800 last_method_index = method_index;
Calin Juravlecc3171a2017-05-19 16:47:53 -0700801 InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700802 if (!ReadInlineCache(buffer,
803 number_of_dex_files,
804 dex_profile_index_remap,
805 inline_cache,
806 error)) {
Calin Juravle877fd962016-01-05 14:29:29 +0000807 return false;
808 }
Calin Juravle226501b2015-12-11 14:41:31 +0000809 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000810 uint32_t total_bytes_read = unread_bytes_before_operation - buffer.CountUnreadBytes();
811 if (total_bytes_read != line_header.method_region_size_bytes) {
812 *error += "Profile data inconsistent for ReadMethods";
813 return false;
814 }
Calin Juravle940eb0c2017-01-30 19:30:44 -0800815 return true;
816}
817
818bool ProfileCompilationInfo::ReadClasses(SafeBuffer& buffer,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800819 const ProfileLineHeader& line_header,
820 /*out*/std::string* error) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000821 size_t unread_bytes_before_op = buffer.CountUnreadBytes();
822 if (unread_bytes_before_op < line_header.class_set_size) {
823 *error += "Profile EOF reached prematurely for ReadClasses";
824 return kProfileLoadBadData;
825 }
826
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000827 uint16_t last_class_index = 0;
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000828 for (uint16_t i = 0; i < line_header.class_set_size; i++) {
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000829 uint16_t diff_with_last_class_index;
830 READ_UINT(uint16_t, buffer, diff_with_last_class_index, error);
831 uint16_t type_index = last_class_index + diff_with_last_class_index;
832 last_class_index = type_index;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800833 if (!AddClassIndex(line_header.dex_location,
834 line_header.checksum,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700835 dex::TypeIndex(type_index),
836 line_header.num_method_ids)) {
Calin Juravle64142952016-03-21 14:37:55 +0000837 return false;
838 }
839 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000840 size_t total_bytes_read = unread_bytes_before_op - buffer.CountUnreadBytes();
841 uint32_t expected_bytes_read = line_header.class_set_size * sizeof(uint16_t);
842 if (total_bytes_read != expected_bytes_read) {
843 *error += "Profile data inconsistent for ReadClasses";
844 return false;
845 }
Calin Juravle226501b2015-12-11 14:41:31 +0000846 return true;
847}
848
Calin Juravle64142952016-03-21 14:37:55 +0000849// Tests for EOF by trying to read 1 byte from the descriptor.
850// Returns:
851// 0 if the descriptor is at the EOF,
852// -1 if there was an IO error
853// 1 if the descriptor has more content to read
854static int testEOF(int fd) {
855 uint8_t buffer[1];
856 return TEMP_FAILURE_RETRY(read(fd, buffer, 1));
857}
858
859// Reads an uint value previously written with AddUintToBuffer.
860template <typename T>
Calin Juravle940eb0c2017-01-30 19:30:44 -0800861bool ProfileCompilationInfo::SafeBuffer::ReadUintAndAdvance(/*out*/T* value) {
Calin Juravle64142952016-03-21 14:37:55 +0000862 static_assert(std::is_unsigned<T>::value, "Type is not unsigned");
Calin Juravle940eb0c2017-01-30 19:30:44 -0800863 if (ptr_current_ + sizeof(T) > ptr_end_) {
864 return false;
865 }
866 *value = 0;
Calin Juravle64142952016-03-21 14:37:55 +0000867 for (size_t i = 0; i < sizeof(T); i++) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800868 *value += ptr_current_[i] << (i * kBitsPerByte);
Calin Juravle226501b2015-12-11 14:41:31 +0000869 }
Calin Juravle64142952016-03-21 14:37:55 +0000870 ptr_current_ += sizeof(T);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800871 return true;
Calin Juravle64142952016-03-21 14:37:55 +0000872}
873
874bool ProfileCompilationInfo::SafeBuffer::CompareAndAdvance(const uint8_t* data, size_t data_size) {
875 if (ptr_current_ + data_size > ptr_end_) {
876 return false;
877 }
878 if (memcmp(ptr_current_, data, data_size) == 0) {
879 ptr_current_ += data_size;
880 return true;
881 }
882 return false;
883}
884
885ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::SafeBuffer::FillFromFd(
886 int fd,
887 const std::string& source,
888 /*out*/std::string* error) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000889 size_t byte_count = (ptr_end_ - ptr_current_) * sizeof(*ptr_current_);
Calin Juravle64142952016-03-21 14:37:55 +0000890 uint8_t* buffer = ptr_current_;
891 while (byte_count > 0) {
892 int bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, byte_count));
893 if (bytes_read == 0) {
894 *error += "Profile EOF reached prematurely for " + source;
895 return kProfileLoadBadData;
896 } else if (bytes_read < 0) {
897 *error += "Profile IO error for " + source + strerror(errno);
898 return kProfileLoadIOError;
Calin Juravle226501b2015-12-11 14:41:31 +0000899 }
Calin Juravle64142952016-03-21 14:37:55 +0000900 byte_count -= bytes_read;
901 buffer += bytes_read;
Calin Juravle226501b2015-12-11 14:41:31 +0000902 }
Calin Juravle64142952016-03-21 14:37:55 +0000903 return kProfileLoadSuccess;
904}
905
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000906size_t ProfileCompilationInfo::SafeBuffer::CountUnreadBytes() {
907 return (ptr_end_ - ptr_current_) * sizeof(*ptr_current_);
908}
909
910const uint8_t* ProfileCompilationInfo::SafeBuffer::GetCurrentPtr() {
911 return ptr_current_;
912}
913
914void ProfileCompilationInfo::SafeBuffer::Advance(size_t data_size) {
915 ptr_current_ += data_size;
916}
917
Calin Juravle64142952016-03-21 14:37:55 +0000918ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileHeader(
919 int fd,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800920 /*out*/uint8_t* number_of_dex_files,
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000921 /*out*/uint32_t* uncompressed_data_size,
922 /*out*/uint32_t* compressed_data_size,
Calin Juravle64142952016-03-21 14:37:55 +0000923 /*out*/std::string* error) {
924 // Read magic and version
925 const size_t kMagicVersionSize =
926 sizeof(kProfileMagic) +
927 sizeof(kProfileVersion) +
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000928 sizeof(uint8_t) + // number of dex files
929 sizeof(uint32_t) + // size of uncompressed profile data
930 sizeof(uint32_t); // size of compressed profile data
Calin Juravle64142952016-03-21 14:37:55 +0000931
932 SafeBuffer safe_buffer(kMagicVersionSize);
933
934 ProfileLoadSatus status = safe_buffer.FillFromFd(fd, "ReadProfileHeader", error);
935 if (status != kProfileLoadSuccess) {
936 return status;
937 }
938
939 if (!safe_buffer.CompareAndAdvance(kProfileMagic, sizeof(kProfileMagic))) {
940 *error = "Profile missing magic";
941 return kProfileLoadVersionMismatch;
942 }
943 if (!safe_buffer.CompareAndAdvance(kProfileVersion, sizeof(kProfileVersion))) {
944 *error = "Profile version mismatch";
945 return kProfileLoadVersionMismatch;
946 }
Calin Juravle940eb0c2017-01-30 19:30:44 -0800947 if (!safe_buffer.ReadUintAndAdvance<uint8_t>(number_of_dex_files)) {
948 *error = "Cannot read the number of dex files";
949 return kProfileLoadBadData;
950 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000951 if (!safe_buffer.ReadUintAndAdvance<uint32_t>(uncompressed_data_size)) {
952 *error = "Cannot read the size of uncompressed data";
953 return kProfileLoadBadData;
954 }
955 if (!safe_buffer.ReadUintAndAdvance<uint32_t>(compressed_data_size)) {
956 *error = "Cannot read the size of compressed data";
957 return kProfileLoadBadData;
958 }
Calin Juravle64142952016-03-21 14:37:55 +0000959 return kProfileLoadSuccess;
960}
961
Calin Juravle940eb0c2017-01-30 19:30:44 -0800962bool ProfileCompilationInfo::ReadProfileLineHeaderElements(SafeBuffer& buffer,
963 /*out*/uint16_t* dex_location_size,
964 /*out*/ProfileLineHeader* line_header,
965 /*out*/std::string* error) {
966 READ_UINT(uint16_t, buffer, *dex_location_size, error);
967 READ_UINT(uint16_t, buffer, line_header->class_set_size, error);
968 READ_UINT(uint32_t, buffer, line_header->method_region_size_bytes, error);
969 READ_UINT(uint32_t, buffer, line_header->checksum, error);
Mathieu Chartierea650f32017-05-24 12:04:13 -0700970 READ_UINT(uint32_t, buffer, line_header->num_method_ids, error);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800971 return true;
972}
973
Calin Juravle64142952016-03-21 14:37:55 +0000974ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLineHeader(
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000975 SafeBuffer& buffer,
976 /*out*/ProfileLineHeader* line_header,
977 /*out*/std::string* error) {
978 if (buffer.CountUnreadBytes() < kLineHeaderSize) {
979 *error += "Profile EOF reached prematurely for ReadProfileLineHeader";
980 return kProfileLoadBadData;
Calin Juravle64142952016-03-21 14:37:55 +0000981 }
982
Calin Juravle940eb0c2017-01-30 19:30:44 -0800983 uint16_t dex_location_size;
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000984 if (!ReadProfileLineHeaderElements(buffer, &dex_location_size, line_header, error)) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800985 return kProfileLoadBadData;
986 }
Calin Juravle64142952016-03-21 14:37:55 +0000987
988 if (dex_location_size == 0 || dex_location_size > kMaxDexFileKeyLength) {
Goran Jakovljevic4eb6fbf2016-04-25 19:14:17 +0200989 *error = "DexFileKey has an invalid size: " +
990 std::to_string(static_cast<uint32_t>(dex_location_size));
Calin Juravle64142952016-03-21 14:37:55 +0000991 return kProfileLoadBadData;
992 }
993
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000994 if (buffer.CountUnreadBytes() < dex_location_size) {
995 *error += "Profile EOF reached prematurely for ReadProfileHeaderDexLocation";
996 return kProfileLoadBadData;
Calin Juravle64142952016-03-21 14:37:55 +0000997 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000998 const uint8_t* base_ptr = buffer.GetCurrentPtr();
Calin Juravle64142952016-03-21 14:37:55 +0000999 line_header->dex_location.assign(
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001000 reinterpret_cast<const char*>(base_ptr), dex_location_size);
1001 buffer.Advance(dex_location_size);
Calin Juravle64142952016-03-21 14:37:55 +00001002 return kProfileLoadSuccess;
1003}
1004
1005ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine(
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001006 SafeBuffer& buffer,
Calin Juravle940eb0c2017-01-30 19:30:44 -08001007 uint8_t number_of_dex_files,
Calin Juravle64142952016-03-21 14:37:55 +00001008 const ProfileLineHeader& line_header,
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001009 const SafeMap<uint8_t, uint8_t>& dex_profile_index_remap,
1010 bool merge_classes,
Calin Juravle64142952016-03-21 14:37:55 +00001011 /*out*/std::string* error) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001012 DexFileData* data = GetOrAddDexFileData(line_header.dex_location,
1013 line_header.checksum,
1014 line_header.num_method_ids);
1015 if (data == nullptr) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001016 *error = "Error when reading profile file line header: checksum mismatch for "
1017 + line_header.dex_location;
1018 return kProfileLoadBadData;
1019 }
Calin Juravle64142952016-03-21 14:37:55 +00001020
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001021 if (!ReadMethods(buffer, number_of_dex_files, line_header, dex_profile_index_remap, error)) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001022 return kProfileLoadBadData;
Calin Juravle64142952016-03-21 14:37:55 +00001023 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001024
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001025 if (merge_classes) {
1026 if (!ReadClasses(buffer, line_header, error)) {
1027 return kProfileLoadBadData;
1028 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001029 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001030
1031 const size_t bytes = data->bitmap_storage.size();
1032 if (buffer.CountUnreadBytes() < bytes) {
1033 *error += "Profile EOF reached prematurely for ReadProfileHeaderDexLocation";
1034 return kProfileLoadBadData;
1035 }
1036 const uint8_t* base_ptr = buffer.GetCurrentPtr();
1037 std::copy_n(base_ptr, bytes, &data->bitmap_storage[0]);
1038 buffer.Advance(bytes);
1039 // Read method bitmap.
Calin Juravle64142952016-03-21 14:37:55 +00001040 return kProfileLoadSuccess;
Calin Juravle226501b2015-12-11 14:41:31 +00001041}
1042
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001043// TODO(calin): Fix this API. ProfileCompilationInfo::Load should be static and
1044// return a unique pointer to a ProfileCompilationInfo upon success.
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001045bool ProfileCompilationInfo::Load(int fd, bool merge_classes) {
Calin Juravle64142952016-03-21 14:37:55 +00001046 std::string error;
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001047
1048 ProfileLoadSatus status = LoadInternal(fd, &error, merge_classes);
Calin Juravle64142952016-03-21 14:37:55 +00001049
1050 if (status == kProfileLoadSuccess) {
1051 return true;
1052 } else {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001053 LOG(WARNING) << "Error when reading profile: " << error;
Calin Juravle64142952016-03-21 14:37:55 +00001054 return false;
1055 }
1056}
1057
Shubham Ajmera188b2bf2017-09-20 15:53:35 -07001058bool ProfileCompilationInfo::VerifyProfileData(const std::vector<const DexFile*>& dex_files) {
1059 std::unordered_map<std::string, const DexFile*> key_to_dex_file;
1060 for (const DexFile* dex_file : dex_files) {
1061 key_to_dex_file.emplace(GetProfileDexFileKey(dex_file->GetLocation()), dex_file);
1062 }
1063 for (const DexFileData* dex_data : info_) {
1064 const auto it = key_to_dex_file.find(dex_data->profile_key);
1065 if (it == key_to_dex_file.end()) {
1066 // It is okay if profile contains data for additional dex files.
1067 continue;
1068 }
1069 const DexFile* dex_file = it->second;
1070 const std::string& dex_location = dex_file->GetLocation();
1071 if (!ChecksumMatch(dex_data->checksum, dex_file->GetLocationChecksum())) {
1072 LOG(ERROR) << "Dex checksum mismatch while verifying profile "
1073 << "dex location " << dex_location << " (checksum="
1074 << dex_file->GetLocationChecksum() << ", profile checksum="
1075 << dex_data->checksum;
1076 return false;
1077 }
Shubham Ajmera460ab792017-09-21 13:44:07 -07001078
1079 if (dex_data->num_method_ids != dex_file->NumMethodIds()) {
1080 LOG(ERROR) << "Number of method ids in dex file and profile don't match."
1081 << "dex location " << dex_location << " NumMethodId in DexFile"
1082 << dex_file->NumMethodIds() << ", NumMethodId in profile"
1083 << dex_data->num_method_ids;
1084 return false;
1085 }
1086
Shubham Ajmera188b2bf2017-09-20 15:53:35 -07001087 // Verify method_encoding.
1088 for (const auto& method_it : dex_data->method_map) {
1089 size_t method_id = (size_t)(method_it.first);
1090 if (method_id >= dex_file->NumMethodIds()) {
1091 LOG(ERROR) << "Invalid method id in profile file. dex location="
1092 << dex_location << " method_id=" << method_id << " NumMethodIds="
1093 << dex_file->NumMethodIds();
1094 return false;
1095 }
1096
1097 // Verify class indices of inline caches.
1098 const InlineCacheMap &inline_cache_map = method_it.second;
1099 for (const auto& inline_cache_it : inline_cache_map) {
1100 const DexPcData dex_pc_data = inline_cache_it.second;
1101 if (dex_pc_data.is_missing_types || dex_pc_data.is_megamorphic) {
1102 // No class indices to verify.
1103 continue;
1104 }
1105
1106 const ClassSet &classes = dex_pc_data.classes;
1107 SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
1108 // Group the classes by dex. We expect that most of the classes will come from
1109 // the same dex, so this will be more efficient than encoding the dex index
1110 // for each class reference.
1111 GroupClassesByDex(classes, &dex_to_classes_map);
1112 for (const auto &dex_it : dex_to_classes_map) {
1113 uint8_t dex_profile_index = dex_it.first;
1114 const auto dex_file_inline_cache_it = key_to_dex_file.find(
1115 info_[dex_profile_index]->profile_key);
1116 if (dex_file_inline_cache_it == key_to_dex_file.end()) {
1117 // It is okay if profile contains data for additional dex files.
1118 continue;
1119 }
1120 const DexFile *dex_file_for_inline_cache_check = dex_file_inline_cache_it->second;
1121 const std::vector<dex::TypeIndex> &dex_classes = dex_it.second;
1122 for (size_t i = 0; i < dex_classes.size(); i++) {
1123 if (dex_classes[i].index_ >= dex_file_for_inline_cache_check->NumTypeIds()) {
1124 LOG(ERROR) << "Invalid inline cache in profile file. dex location="
1125 << dex_location << " method_id=" << method_id
1126 << " dex_profile_index="
1127 << static_cast<uint16_t >(dex_profile_index) << " type_index="
1128 << dex_classes[i].index_
1129 << " NumTypeIds="
1130 << dex_file_for_inline_cache_check->NumTypeIds();
1131 return false;
1132 }
1133 }
1134 }
1135 }
1136 }
1137 // Verify class_ids.
1138 for (const auto& class_id : dex_data->class_set) {
1139 if (class_id.index_ >= dex_file->NumTypeIds()) {
1140 LOG(ERROR) << "Invalid class id in profile file. dex_file location "
1141 << dex_location << " class_id=" << class_id.index_ << " NumClassIds="
1142 << dex_file->NumClassDefs();
1143 return false;
1144 }
1145 }
1146 }
1147 return true;
1148}
1149
Calin Juravledcab1902017-05-12 19:18:47 -07001150// TODO(calin): fail fast if the dex checksums don't match.
Calin Juravle64142952016-03-21 14:37:55 +00001151ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal(
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001152 int fd, std::string* error, bool merge_classes) {
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -08001153 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravle2e2db782016-02-23 12:00:03 +00001154 DCHECK_GE(fd, 0);
Calin Juravle226501b2015-12-11 14:41:31 +00001155
Calin Juravle64142952016-03-21 14:37:55 +00001156 struct stat stat_buffer;
1157 if (fstat(fd, &stat_buffer) != 0) {
1158 return kProfileLoadIOError;
Calin Juravle226501b2015-12-11 14:41:31 +00001159 }
Calin Juravle64142952016-03-21 14:37:55 +00001160 // We allow empty profile files.
1161 // Profiles may be created by ActivityManager or installd before we manage to
1162 // process them in the runtime or profman.
1163 if (stat_buffer.st_size == 0) {
1164 return kProfileLoadSuccess;
1165 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001166 // Read profile header: magic + version + number_of_dex_files.
1167 uint8_t number_of_dex_files;
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001168 uint32_t uncompressed_data_size;
1169 uint32_t compressed_data_size;
1170 ProfileLoadSatus status = ReadProfileHeader(fd,
1171 &number_of_dex_files,
1172 &uncompressed_data_size,
1173 &compressed_data_size,
1174 error);
1175
Calin Juravle64142952016-03-21 14:37:55 +00001176 if (status != kProfileLoadSuccess) {
1177 return status;
1178 }
Mathieu Chartierf2e2af82017-07-12 21:09:57 -07001179 // Allow large profiles for non target builds for the case where we are merging many profiles
1180 // to generate a boot image profile.
1181 if (kIsTargetBuild && uncompressed_data_size > kProfileSizeErrorThresholdInBytes) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001182 LOG(ERROR) << "Profile data size exceeds "
1183 << std::to_string(kProfileSizeErrorThresholdInBytes)
1184 << " bytes";
1185 return kProfileLoadBadData;
1186 }
1187 if (uncompressed_data_size > kProfileSizeWarningThresholdInBytes) {
1188 LOG(WARNING) << "Profile data size exceeds "
1189 << std::to_string(kProfileSizeWarningThresholdInBytes)
1190 << " bytes";
1191 }
1192
1193 std::unique_ptr<uint8_t[]> compressed_data(new uint8_t[compressed_data_size]);
1194 bool bytes_read_success =
1195 android::base::ReadFully(fd, compressed_data.get(), compressed_data_size);
1196
1197 if (testEOF(fd) != 0) {
1198 *error += "Unexpected data in the profile file.";
1199 return kProfileLoadBadData;
1200 }
1201
1202 if (!bytes_read_success) {
1203 *error += "Unable to read compressed profile data";
1204 return kProfileLoadBadData;
1205 }
1206
1207 SafeBuffer uncompressed_data(uncompressed_data_size);
1208
1209 int ret = InflateBuffer(compressed_data.get(),
1210 compressed_data_size,
1211 uncompressed_data_size,
1212 uncompressed_data.Get());
1213
1214 if (ret != Z_STREAM_END) {
1215 *error += "Error reading uncompressed profile data";
1216 return kProfileLoadBadData;
1217 }
1218
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001219 std::vector<ProfileLineHeader> profile_line_headers;
1220 // Read profile line headers.
Calin Juravle940eb0c2017-01-30 19:30:44 -08001221 for (uint8_t k = 0; k < number_of_dex_files; k++) {
Calin Juravle64142952016-03-21 14:37:55 +00001222 ProfileLineHeader line_header;
Calin Juravle940eb0c2017-01-30 19:30:44 -08001223
Calin Juravle64142952016-03-21 14:37:55 +00001224 // First, read the line header to get the amount of data we need to read.
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001225 status = ReadProfileLineHeader(uncompressed_data, &line_header, error);
Calin Juravle64142952016-03-21 14:37:55 +00001226 if (status != kProfileLoadSuccess) {
1227 return status;
1228 }
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001229 profile_line_headers.push_back(line_header);
1230 }
Calin Juravle64142952016-03-21 14:37:55 +00001231
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001232 SafeMap<uint8_t, uint8_t> dex_profile_index_remap;
1233 if (!RemapProfileIndex(profile_line_headers, &dex_profile_index_remap)) {
1234 return kProfileLoadBadData;
1235 }
1236
1237 for (uint8_t k = 0; k < number_of_dex_files; k++) {
Calin Juravle64142952016-03-21 14:37:55 +00001238 // Now read the actual profile line.
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001239 status = ReadProfileLine(uncompressed_data,
1240 number_of_dex_files,
1241 profile_line_headers[k],
1242 dex_profile_index_remap,
1243 merge_classes,
1244 error);
Calin Juravle64142952016-03-21 14:37:55 +00001245 if (status != kProfileLoadSuccess) {
1246 return status;
1247 }
Calin Juravle64142952016-03-21 14:37:55 +00001248 }
1249
1250 // Check that we read everything and that profiles don't contain junk data.
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001251 if (uncompressed_data.CountUnreadBytes() > 0) {
Calin Juravle64142952016-03-21 14:37:55 +00001252 *error = "Unexpected content in the profile file";
1253 return kProfileLoadBadData;
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001254 } else {
1255 return kProfileLoadSuccess;
Calin Juravle64142952016-03-21 14:37:55 +00001256 }
Calin Juravle998c2162015-12-21 15:39:33 +02001257}
1258
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001259bool ProfileCompilationInfo::RemapProfileIndex(
1260 const std::vector<ProfileLineHeader>& profile_line_headers,
1261 /*out*/SafeMap<uint8_t, uint8_t>* dex_profile_index_remap) {
1262 // First verify that all checksums match. This will avoid adding garbage to
1263 // the current profile info.
1264 // Note that the number of elements should be very small, so this should not
1265 // be a performance issue.
1266 for (const ProfileLineHeader other_profile_line_header : profile_line_headers) {
1267 // verify_checksum is false because we want to differentiate between a missing dex data and
1268 // a mismatched checksum.
1269 const DexFileData* dex_data = FindDexData(other_profile_line_header.dex_location,
1270 0u,
1271 false /* verify_checksum */);
1272 if ((dex_data != nullptr) && (dex_data->checksum != other_profile_line_header.checksum)) {
1273 LOG(WARNING) << "Checksum mismatch for dex " << other_profile_line_header.dex_location;
1274 return false;
1275 }
1276 }
1277 // All checksums match. Import the data.
1278 uint32_t num_dex_files = static_cast<uint32_t>(profile_line_headers.size());
1279 for (uint32_t i = 0; i < num_dex_files; i++) {
1280 const DexFileData* dex_data = GetOrAddDexFileData(profile_line_headers[i].dex_location,
1281 profile_line_headers[i].checksum,
1282 profile_line_headers[i].num_method_ids);
1283 if (dex_data == nullptr) {
1284 return false; // Could happen if we exceed the number of allowed dex files.
1285 }
1286 dex_profile_index_remap->Put(i, dex_data->profile_index);
1287 }
1288 return true;
1289}
Shubham Ajmera61200a02017-08-30 16:29:41 -07001290
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001291std::unique_ptr<uint8_t[]> ProfileCompilationInfo::DeflateBuffer(const uint8_t* in_buffer,
1292 uint32_t in_size,
1293 uint32_t* compressed_data_size) {
1294 z_stream strm;
1295 strm.zalloc = Z_NULL;
1296 strm.zfree = Z_NULL;
1297 strm.opaque = Z_NULL;
1298 int ret = deflateInit(&strm, 1);
1299 if (ret != Z_OK) {
1300 return nullptr;
1301 }
1302
1303 uint32_t out_size = deflateBound(&strm, in_size);
1304
1305 std::unique_ptr<uint8_t[]> compressed_buffer(new uint8_t[out_size]);
1306 strm.avail_in = in_size;
1307 strm.next_in = const_cast<uint8_t*>(in_buffer);
1308 strm.avail_out = out_size;
1309 strm.next_out = &compressed_buffer[0];
1310 ret = deflate(&strm, Z_FINISH);
1311 if (ret == Z_STREAM_ERROR) {
1312 return nullptr;
1313 }
1314 *compressed_data_size = out_size - strm.avail_out;
1315 deflateEnd(&strm);
1316 return compressed_buffer;
1317}
1318
1319int ProfileCompilationInfo::InflateBuffer(const uint8_t* in_buffer,
1320 uint32_t in_size,
1321 uint32_t expected_uncompressed_data_size,
1322 uint8_t* out_buffer) {
1323 z_stream strm;
1324
1325 /* allocate inflate state */
1326 strm.zalloc = Z_NULL;
1327 strm.zfree = Z_NULL;
1328 strm.opaque = Z_NULL;
1329 strm.avail_in = in_size;
1330 strm.next_in = const_cast<uint8_t*>(in_buffer);
1331 strm.avail_out = expected_uncompressed_data_size;
1332 strm.next_out = out_buffer;
1333
1334 int ret;
1335 inflateInit(&strm);
1336 ret = inflate(&strm, Z_NO_FLUSH);
1337
1338 if (strm.avail_in != 0 || strm.avail_out != 0) {
1339 return Z_DATA_ERROR;
1340 }
1341 inflateEnd(&strm);
1342 return ret;
1343}
1344
Mathieu Chartier2f794552017-06-19 10:58:08 -07001345bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other,
1346 bool merge_classes) {
Calin Juravle5d1bd0a2016-03-24 20:33:22 +00001347 // First verify that all checksums match. This will avoid adding garbage to
1348 // the current profile info.
1349 // Note that the number of elements should be very small, so this should not
1350 // be a performance issue.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001351 for (const DexFileData* other_dex_data : other.info_) {
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001352 // verify_checksum is false because we want to differentiate between a missing dex data and
1353 // a mismatched checksum.
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001354 const DexFileData* dex_data = FindDexData(other_dex_data->profile_key,
1355 0u,
1356 /* verify_checksum */ false);
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001357 if ((dex_data != nullptr) && (dex_data->checksum != other_dex_data->checksum)) {
1358 LOG(WARNING) << "Checksum mismatch for dex " << other_dex_data->profile_key;
Calin Juravle5d1bd0a2016-03-24 20:33:22 +00001359 return false;
1360 }
1361 }
1362 // All checksums match. Import the data.
Calin Juravle940eb0c2017-01-30 19:30:44 -08001363
1364 // The other profile might have a different indexing of dex files.
1365 // That is because each dex files gets a 'dex_profile_index' on a first come first served basis.
1366 // That means that the order in with the methods are added to the profile matters for the
1367 // actual indices.
1368 // The reason we cannot rely on the actual multidex index is that a single profile may store
1369 // data from multiple splits. This means that a profile may contain a classes2.dex from split-A
1370 // and one from split-B.
1371
1372 // First, build a mapping from other_dex_profile_index to this_dex_profile_index.
1373 // This will make sure that the ClassReferences will point to the correct dex file.
1374 SafeMap<uint8_t, uint8_t> dex_profile_index_remap;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001375 for (const DexFileData* other_dex_data : other.info_) {
1376 const DexFileData* dex_data = GetOrAddDexFileData(other_dex_data->profile_key,
Mathieu Chartierea650f32017-05-24 12:04:13 -07001377 other_dex_data->checksum,
1378 other_dex_data->num_method_ids);
Calin Juravlee0ac1152017-02-13 19:03:47 -08001379 if (dex_data == nullptr) {
1380 return false; // Could happen if we exceed the number of allowed dex files.
1381 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001382 dex_profile_index_remap.Put(other_dex_data->profile_index, dex_data->profile_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -08001383 }
1384
1385 // Merge the actual profile data.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001386 for (const DexFileData* other_dex_data : other.info_) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001387 DexFileData* dex_data = const_cast<DexFileData*>(FindDexData(other_dex_data->profile_key,
1388 other_dex_data->checksum));
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001389 DCHECK(dex_data != nullptr);
Calin Juravle940eb0c2017-01-30 19:30:44 -08001390
1391 // Merge the classes.
Mathieu Chartier2f794552017-06-19 10:58:08 -07001392 if (merge_classes) {
1393 dex_data->class_set.insert(other_dex_data->class_set.begin(),
1394 other_dex_data->class_set.end());
1395 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001396
1397 // Merge the methods and the inline caches.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001398 for (const auto& other_method_it : other_dex_data->method_map) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001399 uint16_t other_method_index = other_method_it.first;
Calin Juravlecc3171a2017-05-19 16:47:53 -07001400 InlineCacheMap* inline_cache = dex_data->FindOrAddMethod(other_method_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -08001401 const auto& other_inline_cache = other_method_it.second;
1402 for (const auto& other_ic_it : other_inline_cache) {
1403 uint16_t other_dex_pc = other_ic_it.first;
1404 const ClassSet& other_class_set = other_ic_it.second.classes;
Calin Juravlecc3171a2017-05-19 16:47:53 -07001405 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, other_dex_pc);
Calin Juravle589e71e2017-03-03 16:05:05 -08001406 if (other_ic_it.second.is_missing_types) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001407 dex_pc_data->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -08001408 } else if (other_ic_it.second.is_megamorphic) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001409 dex_pc_data->SetIsMegamorphic();
Calin Juravle0def68d2017-02-21 19:00:33 -08001410 } else {
1411 for (const auto& class_it : other_class_set) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001412 dex_pc_data->AddClass(dex_profile_index_remap.Get(
Calin Juravle0def68d2017-02-21 19:00:33 -08001413 class_it.dex_profile_index), class_it.type_index);
1414 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001415 }
1416 }
1417 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001418
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001419 // Merge the method bitmaps.
Mathieu Chartierea650f32017-05-24 12:04:13 -07001420 dex_data->MergeBitmap(*other_dex_data);
Calin Juravle998c2162015-12-21 15:39:33 +02001421 }
1422 return true;
Calin Juravle226501b2015-12-11 14:41:31 +00001423}
1424
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001425const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData(
1426 const DexFile* dex_file) const {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001427 return FindDexData(GetProfileDexFileKey(dex_file->GetLocation()),
1428 dex_file->GetLocationChecksum());
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001429}
1430
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001431ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::GetMethodHotness(
1432 const MethodReference& method_ref) const {
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001433 const DexFileData* dex_data = FindDexData(method_ref.dex_file);
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001434 return dex_data != nullptr
Mathieu Chartierfc8b4222017-09-17 13:44:24 -07001435 ? dex_data->GetHotnessInfo(method_ref.index)
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001436 : MethodHotness();
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001437}
1438
Mathieu Chartier2f794552017-06-19 10:58:08 -07001439bool ProfileCompilationInfo::AddMethodHotness(const MethodReference& method_ref,
1440 const MethodHotness& hotness) {
1441 DexFileData* dex_data = GetOrAddDexFileData(method_ref.dex_file);
1442 if (dex_data != nullptr) {
1443 // TODO: Add inline caches.
Calin Juravle1ad1e3f2017-09-19 18:20:37 -07001444 return dex_data->AddMethod(
1445 static_cast<MethodHotness::Flag>(hotness.GetFlags()), method_ref.index);
Mathieu Chartier2f794552017-06-19 10:58:08 -07001446 }
1447 return false;
1448}
1449
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001450ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::GetMethodHotness(
1451 const std::string& dex_location,
1452 uint32_t dex_checksum,
1453 uint16_t dex_method_index) const {
1454 const DexFileData* dex_data = FindDexData(GetProfileDexFileKey(dex_location), dex_checksum);
1455 return dex_data != nullptr ? dex_data->GetHotnessInfo(dex_method_index) : MethodHotness();
Calin Juravle226501b2015-12-11 14:41:31 +00001456}
1457
Calin Juravle940eb0c2017-01-30 19:30:44 -08001458
Calin Juravlecc3171a2017-05-19 16:47:53 -07001459std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> ProfileCompilationInfo::GetMethod(
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001460 const std::string& dex_location,
1461 uint32_t dex_checksum,
1462 uint16_t dex_method_index) const {
1463 MethodHotness hotness(GetMethodHotness(dex_location, dex_checksum, dex_method_index));
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001464 if (!hotness.IsHot()) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001465 return nullptr;
Calin Juravle940eb0c2017-01-30 19:30:44 -08001466 }
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001467 const InlineCacheMap* inline_caches = hotness.GetInlineCacheMap();
1468 DCHECK(inline_caches != nullptr);
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001469 std::unique_ptr<OfflineProfileMethodInfo> pmi(new OfflineProfileMethodInfo(inline_caches));
Calin Juravle940eb0c2017-01-30 19:30:44 -08001470
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001471 pmi->dex_references.resize(info_.size());
1472 for (const DexFileData* dex_data : info_) {
1473 pmi->dex_references[dex_data->profile_index].dex_location = dex_data->profile_key;
1474 pmi->dex_references[dex_data->profile_index].dex_checksum = dex_data->checksum;
Mathieu Chartierea650f32017-05-24 12:04:13 -07001475 pmi->dex_references[dex_data->profile_index].num_method_ids = dex_data->num_method_ids;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001476 }
1477
Calin Juravlecc3171a2017-05-19 16:47:53 -07001478 return pmi;
Calin Juravle940eb0c2017-01-30 19:30:44 -08001479}
1480
1481
Andreas Gampea5b09a62016-11-17 15:21:22 -08001482bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001483 const DexFileData* dex_data = FindDexData(&dex_file);
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001484 if (dex_data != nullptr) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001485 const ArenaSet<dex::TypeIndex>& classes = dex_data->class_set;
Jeff Hao54b58552016-11-16 15:15:04 -08001486 return classes.find(type_idx) != classes.end();
Mathieu Chartiera8077802016-03-16 19:08:31 -07001487 }
1488 return false;
1489}
1490
Calin Juravle998c2162015-12-21 15:39:33 +02001491uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
1492 uint32_t total = 0;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001493 for (const DexFileData* dex_data : info_) {
1494 total += dex_data->method_map.size();
Calin Juravle998c2162015-12-21 15:39:33 +02001495 }
1496 return total;
1497}
1498
Calin Juravle67265462016-03-18 16:23:40 +00001499uint32_t ProfileCompilationInfo::GetNumberOfResolvedClasses() const {
1500 uint32_t total = 0;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001501 for (const DexFileData* dex_data : info_) {
1502 total += dex_data->class_set.size();
Calin Juravle67265462016-03-18 16:23:40 +00001503 }
1504 return total;
1505}
1506
David Sehrb18991b2017-02-08 20:58:10 -08001507// Produce a non-owning vector from a vector.
1508template<typename T>
1509const std::vector<T*>* MakeNonOwningVector(const std::vector<std::unique_ptr<T>>* owning_vector) {
1510 auto non_owning_vector = new std::vector<T*>();
1511 for (auto& element : *owning_vector) {
1512 non_owning_vector->push_back(element.get());
1513 }
1514 return non_owning_vector;
1515}
1516
1517std::string ProfileCompilationInfo::DumpInfo(
1518 const std::vector<std::unique_ptr<const DexFile>>* dex_files,
1519 bool print_full_dex_location) const {
1520 std::unique_ptr<const std::vector<const DexFile*>> non_owning_dex_files(
1521 MakeNonOwningVector(dex_files));
1522 return DumpInfo(non_owning_dex_files.get(), print_full_dex_location);
1523}
1524
Calin Juravle998c2162015-12-21 15:39:33 +02001525std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
1526 bool print_full_dex_location) const {
Calin Juravle226501b2015-12-11 14:41:31 +00001527 std::ostringstream os;
1528 if (info_.empty()) {
1529 return "ProfileInfo: empty";
1530 }
1531
1532 os << "ProfileInfo:";
1533
Calin Juravlea308a322017-07-18 16:51:51 -07001534 const std::string kFirstDexFileKeySubstitute = "!classes.dex";
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001535
1536 for (const DexFileData* dex_data : info_) {
Calin Juravle226501b2015-12-11 14:41:31 +00001537 os << "\n";
Calin Juravle226501b2015-12-11 14:41:31 +00001538 if (print_full_dex_location) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001539 os << dex_data->profile_key;
Calin Juravle226501b2015-12-11 14:41:31 +00001540 } else {
1541 // Replace the (empty) multidex suffix of the first key with a substitute for easier reading.
Mathieu Chartier79c87da2017-10-10 11:54:29 -07001542 std::string multidex_suffix = DexFileLoader::GetMultiDexSuffix(dex_data->profile_key);
Calin Juravle226501b2015-12-11 14:41:31 +00001543 os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
1544 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001545 os << " [index=" << static_cast<uint32_t>(dex_data->profile_index) << "]";
Calin Juravle876f3502016-03-24 16:16:34 +00001546 const DexFile* dex_file = nullptr;
1547 if (dex_files != nullptr) {
1548 for (size_t i = 0; i < dex_files->size(); i++) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001549 if (dex_data->profile_key == (*dex_files)[i]->GetLocation()) {
Calin Juravle876f3502016-03-24 16:16:34 +00001550 dex_file = (*dex_files)[i];
Calin Juravle998c2162015-12-21 15:39:33 +02001551 }
Calin Juravle226501b2015-12-11 14:41:31 +00001552 }
Calin Juravle876f3502016-03-24 16:16:34 +00001553 }
Mathieu Chartier28b5c582017-06-06 14:12:50 -07001554 os << "\n\thot methods: ";
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001555 for (const auto& method_it : dex_data->method_map) {
Calin Juravle876f3502016-03-24 16:16:34 +00001556 if (dex_file != nullptr) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001557 os << "\n\t\t" << dex_file->PrettyMethod(method_it.first, true);
Calin Juravle876f3502016-03-24 16:16:34 +00001558 } else {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001559 os << method_it.first;
Calin Juravle876f3502016-03-24 16:16:34 +00001560 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001561
1562 os << "[";
1563 for (const auto& inline_cache_it : method_it.second) {
1564 os << "{" << std::hex << inline_cache_it.first << std::dec << ":";
Calin Juravle589e71e2017-03-03 16:05:05 -08001565 if (inline_cache_it.second.is_missing_types) {
1566 os << "MT";
1567 } else if (inline_cache_it.second.is_megamorphic) {
1568 os << "MM";
Calin Juravle940eb0c2017-01-30 19:30:44 -08001569 } else {
1570 for (const ClassReference& class_ref : inline_cache_it.second.classes) {
1571 os << "(" << static_cast<uint32_t>(class_ref.dex_profile_index)
1572 << "," << class_ref.type_index.index_ << ")";
1573 }
1574 }
1575 os << "}";
1576 }
1577 os << "], ";
Calin Juravle876f3502016-03-24 16:16:34 +00001578 }
Mathieu Chartier28b5c582017-06-06 14:12:50 -07001579 bool startup = true;
1580 while (true) {
1581 os << "\n\t" << (startup ? "startup methods: " : "post startup methods: ");
1582 for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001583 MethodHotness hotness_info(dex_data->GetHotnessInfo(method_idx));
1584 if (startup ? hotness_info.IsStartup() : hotness_info.IsPostStartup()) {
Mathieu Chartier28b5c582017-06-06 14:12:50 -07001585 os << method_idx << ", ";
1586 }
1587 }
1588 if (startup == false) {
1589 break;
1590 }
1591 startup = false;
1592 }
Calin Juravle876f3502016-03-24 16:16:34 +00001593 os << "\n\tclasses: ";
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001594 for (const auto class_it : dex_data->class_set) {
Calin Juravle876f3502016-03-24 16:16:34 +00001595 if (dex_file != nullptr) {
Jeff Hao54b58552016-11-16 15:15:04 -08001596 os << "\n\t\t" << dex_file->PrettyType(class_it);
Calin Juravle876f3502016-03-24 16:16:34 +00001597 } else {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001598 os << class_it.index_ << ",";
Calin Juravle876f3502016-03-24 16:16:34 +00001599 }
Calin Juravle226501b2015-12-11 14:41:31 +00001600 }
1601 }
1602 return os.str();
1603}
1604
Mathieu Chartierea650f32017-05-24 12:04:13 -07001605bool ProfileCompilationInfo::GetClassesAndMethods(
1606 const DexFile& dex_file,
1607 /*out*/std::set<dex::TypeIndex>* class_set,
1608 /*out*/std::set<uint16_t>* hot_method_set,
1609 /*out*/std::set<uint16_t>* startup_method_set,
1610 /*out*/std::set<uint16_t>* post_startup_method_method_set) const {
Mathieu Chartier34067262017-04-06 13:55:46 -07001611 std::set<std::string> ret;
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001612 const DexFileData* dex_data = FindDexData(&dex_file);
1613 if (dex_data == nullptr) {
Mathieu Chartier34067262017-04-06 13:55:46 -07001614 return false;
David Sehr7c80f2d2017-02-07 16:47:58 -08001615 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001616 for (const auto& it : dex_data->method_map) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001617 hot_method_set->insert(it.first);
1618 }
1619 for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001620 MethodHotness hotness = dex_data->GetHotnessInfo(method_idx);
1621 if (hotness.IsStartup()) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001622 startup_method_set->insert(method_idx);
1623 }
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001624 if (hotness.IsPostStartup()) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001625 post_startup_method_method_set->insert(method_idx);
1626 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001627 }
Calin Juravlecc3171a2017-05-19 16:47:53 -07001628 for (const dex::TypeIndex& type_index : dex_data->class_set) {
1629 class_set->insert(type_index);
1630 }
Mathieu Chartier34067262017-04-06 13:55:46 -07001631 return true;
David Sehr7c80f2d2017-02-07 16:47:58 -08001632}
1633
Calin Juravle2e2db782016-02-23 12:00:03 +00001634bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001635 // No need to compare profile_key_map_. That's only a cache for fast search.
1636 // All the information is already in the info_ vector.
1637 if (info_.size() != other.info_.size()) {
1638 return false;
1639 }
1640 for (size_t i = 0; i < info_.size(); i++) {
1641 const DexFileData& dex_data = *info_[i];
1642 const DexFileData& other_dex_data = *other.info_[i];
1643 if (!(dex_data == other_dex_data)) {
1644 return false;
1645 }
1646 }
1647 return true;
Calin Juravle877fd962016-01-05 14:29:29 +00001648}
1649
Mathieu Chartier046854b2017-03-01 17:16:22 -08001650std::set<DexCacheResolvedClasses> ProfileCompilationInfo::GetResolvedClasses(
Calin Juravle08556882017-05-26 16:40:45 -07001651 const std::vector<const DexFile*>& dex_files) const {
1652 std::unordered_map<std::string, const DexFile* > key_to_dex_file;
1653 for (const DexFile* dex_file : dex_files) {
1654 key_to_dex_file.emplace(GetProfileDexFileKey(dex_file->GetLocation()), dex_file);
Mathieu Chartier046854b2017-03-01 17:16:22 -08001655 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -08001656 std::set<DexCacheResolvedClasses> ret;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001657 for (const DexFileData* dex_data : info_) {
Calin Juravle08556882017-05-26 16:40:45 -07001658 const auto it = key_to_dex_file.find(dex_data->profile_key);
1659 if (it != key_to_dex_file.end()) {
1660 const DexFile* dex_file = it->second;
1661 const std::string& dex_location = dex_file->GetLocation();
1662 if (dex_data->checksum != it->second->GetLocationChecksum()) {
1663 LOG(ERROR) << "Dex checksum mismatch when getting resolved classes from profile for "
1664 << "location " << dex_location << " (checksum=" << dex_file->GetLocationChecksum()
1665 << ", profile checksum=" << dex_data->checksum;
1666 return std::set<DexCacheResolvedClasses>();
1667 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001668 DexCacheResolvedClasses classes(dex_location,
1669 dex_location,
1670 dex_data->checksum,
1671 dex_data->num_method_ids);
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001672 classes.AddClasses(dex_data->class_set.begin(), dex_data->class_set.end());
Mathieu Chartier046854b2017-03-01 17:16:22 -08001673 ret.insert(classes);
1674 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -08001675 }
1676 return ret;
1677}
1678
Calin Juravle7bcdb532016-06-07 16:14:47 +01001679// Naive implementation to generate a random profile file suitable for testing.
1680bool ProfileCompilationInfo::GenerateTestProfile(int fd,
1681 uint16_t number_of_dex_files,
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001682 uint16_t method_percentage,
1683 uint16_t class_percentage,
Jeff Haof0a31f82017-03-27 15:50:37 -07001684 uint32_t random_seed) {
Calin Juravle7bcdb532016-06-07 16:14:47 +01001685 const std::string base_dex_location = "base.apk";
1686 ProfileCompilationInfo info;
1687 // The limits are defined by the dex specification.
Mathieu Chartierea650f32017-05-24 12:04:13 -07001688 const uint16_t max_method = std::numeric_limits<uint16_t>::max();
1689 const uint16_t max_classes = std::numeric_limits<uint16_t>::max();
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001690 uint16_t number_of_methods = max_method * method_percentage / 100;
1691 uint16_t number_of_classes = max_classes * class_percentage / 100;
Calin Juravle7bcdb532016-06-07 16:14:47 +01001692
Jeff Haof0a31f82017-03-27 15:50:37 -07001693 std::srand(random_seed);
Calin Juravle7bcdb532016-06-07 16:14:47 +01001694
1695 // Make sure we generate more samples with a low index value.
1696 // This makes it more likely to hit valid method/class indices in small apps.
1697 const uint16_t kFavorFirstN = 10000;
1698 const uint16_t kFavorSplit = 2;
1699
1700 for (uint16_t i = 0; i < number_of_dex_files; i++) {
Mathieu Chartier79c87da2017-10-10 11:54:29 -07001701 std::string dex_location = DexFileLoader::GetMultiDexLocation(i, base_dex_location.c_str());
Calin Juravle7bcdb532016-06-07 16:14:47 +01001702 std::string profile_key = GetProfileDexFileKey(dex_location);
1703
1704 for (uint16_t m = 0; m < number_of_methods; m++) {
1705 uint16_t method_idx = rand() % max_method;
1706 if (m < (number_of_methods / kFavorSplit)) {
1707 method_idx %= kFavorFirstN;
1708 }
Mathieu Chartierc46cf802017-09-28 11:52:19 -07001709 // Alternate between startup and post startup.
1710 uint32_t flags = MethodHotness::kFlagHot;
1711 flags |= ((m & 1) != 0) ? MethodHotness::kFlagPostStartup : MethodHotness::kFlagStartup;
1712 info.AddMethodIndex(static_cast<MethodHotness::Flag>(flags),
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001713 profile_key,
1714 /*method_idx*/ 0,
1715 method_idx,
1716 max_method);
Calin Juravle7bcdb532016-06-07 16:14:47 +01001717 }
1718
1719 for (uint16_t c = 0; c < number_of_classes; c++) {
Jeff Hao54b58552016-11-16 15:15:04 -08001720 uint16_t type_idx = rand() % max_classes;
Calin Juravle7bcdb532016-06-07 16:14:47 +01001721 if (c < (number_of_classes / kFavorSplit)) {
Jeff Hao54b58552016-11-16 15:15:04 -08001722 type_idx %= kFavorFirstN;
Calin Juravle7bcdb532016-06-07 16:14:47 +01001723 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001724 info.AddClassIndex(profile_key, 0, dex::TypeIndex(type_idx), max_method);
Calin Juravle7bcdb532016-06-07 16:14:47 +01001725 }
1726 }
1727 return info.Save(fd);
1728}
1729
Jeff Haof0a31f82017-03-27 15:50:37 -07001730// Naive implementation to generate a random profile file suitable for testing.
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001731// Description of random selection:
1732// * Select a random starting point S.
1733// * For every index i, add (S+i) % (N - total number of methods/classes) to profile with the
1734// probably of 1/(N - i - number of methods/classes needed to add in profile).
Jeff Haof0a31f82017-03-27 15:50:37 -07001735bool ProfileCompilationInfo::GenerateTestProfile(
1736 int fd,
1737 std::vector<std::unique_ptr<const DexFile>>& dex_files,
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001738 uint16_t method_percentage,
1739 uint16_t class_percentage,
Jeff Haof0a31f82017-03-27 15:50:37 -07001740 uint32_t random_seed) {
1741 std::srand(random_seed);
1742 ProfileCompilationInfo info;
1743 for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
1744 const std::string& location = dex_file->GetLocation();
1745 uint32_t checksum = dex_file->GetLocationChecksum();
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001746
1747 uint32_t number_of_classes = dex_file->NumClassDefs();
1748 uint32_t classes_required_in_profile = (number_of_classes * class_percentage) / 100;
1749 uint32_t class_start_index = rand() % number_of_classes;
1750 for (uint32_t i = 0; i < number_of_classes && classes_required_in_profile; ++i) {
1751 if (number_of_classes - i == classes_required_in_profile ||
1752 std::rand() % (number_of_classes - i - classes_required_in_profile) == 0) {
1753 uint32_t class_index = (i + class_start_index) % number_of_classes;
Mathieu Chartierea650f32017-05-24 12:04:13 -07001754 info.AddClassIndex(location,
1755 checksum,
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001756 dex_file->GetClassDef(class_index).class_idx_,
Mathieu Chartierea650f32017-05-24 12:04:13 -07001757 dex_file->NumMethodIds());
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001758 classes_required_in_profile--;
Jeff Haof0a31f82017-03-27 15:50:37 -07001759 }
1760 }
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001761
1762 uint32_t number_of_methods = dex_file->NumMethodIds();
1763 uint32_t methods_required_in_profile = (number_of_methods * method_percentage) / 100;
1764 uint32_t method_start_index = rand() % number_of_methods;
1765 for (uint32_t i = 0; i < number_of_methods && methods_required_in_profile; ++i) {
1766 if (number_of_methods - i == methods_required_in_profile ||
1767 std::rand() % (number_of_methods - i - methods_required_in_profile) == 0) {
1768 uint32_t method_index = (method_start_index + i) % number_of_methods;
Mathieu Chartierc46cf802017-09-28 11:52:19 -07001769 // Alternate between startup and post startup.
1770 uint32_t flags = MethodHotness::kFlagHot;
1771 flags |= ((method_index & 1) != 0)
1772 ? MethodHotness::kFlagPostStartup
1773 : MethodHotness::kFlagStartup;
1774 info.AddMethodIndex(static_cast<MethodHotness::Flag>(flags),
1775 MethodReference(dex_file.get(), method_index));
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001776 methods_required_in_profile--;
Jeff Haof0a31f82017-03-27 15:50:37 -07001777 }
1778 }
1779 }
1780 return info.Save(fd);
1781}
1782
Calin Juravle940eb0c2017-01-30 19:30:44 -08001783bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==(
1784 const OfflineProfileMethodInfo& other) const {
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001785 if (inline_caches->size() != other.inline_caches->size()) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001786 return false;
1787 }
1788
1789 // We can't use a simple equality test because we need to match the dex files
Calin Juravle589e71e2017-03-03 16:05:05 -08001790 // of the inline caches which might have different profile indexes.
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001791 for (const auto& inline_cache_it : *inline_caches) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001792 uint16_t dex_pc = inline_cache_it.first;
1793 const DexPcData dex_pc_data = inline_cache_it.second;
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001794 const auto& other_it = other.inline_caches->find(dex_pc);
1795 if (other_it == other.inline_caches->end()) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001796 return false;
1797 }
1798 const DexPcData& other_dex_pc_data = other_it->second;
Calin Juravle589e71e2017-03-03 16:05:05 -08001799 if (dex_pc_data.is_megamorphic != other_dex_pc_data.is_megamorphic ||
1800 dex_pc_data.is_missing_types != other_dex_pc_data.is_missing_types) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001801 return false;
1802 }
1803 for (const ClassReference& class_ref : dex_pc_data.classes) {
1804 bool found = false;
1805 for (const ClassReference& other_class_ref : other_dex_pc_data.classes) {
1806 CHECK_LE(class_ref.dex_profile_index, dex_references.size());
1807 CHECK_LE(other_class_ref.dex_profile_index, other.dex_references.size());
1808 const DexReference& dex_ref = dex_references[class_ref.dex_profile_index];
1809 const DexReference& other_dex_ref = other.dex_references[other_class_ref.dex_profile_index];
1810 if (class_ref.type_index == other_class_ref.type_index &&
1811 dex_ref == other_dex_ref) {
1812 found = true;
1813 break;
1814 }
1815 }
1816 if (!found) {
1817 return false;
1818 }
1819 }
1820 }
1821 return true;
1822}
1823
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001824bool ProfileCompilationInfo::IsEmpty() const {
1825 DCHECK_EQ(info_.empty(), profile_key_map_.empty());
1826 return info_.empty();
1827}
1828
Calin Juravlecc3171a2017-05-19 16:47:53 -07001829ProfileCompilationInfo::InlineCacheMap*
1830ProfileCompilationInfo::DexFileData::FindOrAddMethod(uint16_t method_index) {
1831 return &(method_map.FindOrAdd(
1832 method_index,
Vladimir Marko69d310e2017-10-09 14:12:23 +01001833 InlineCacheMap(std::less<uint16_t>(), allocator_->Adapter(kArenaAllocProfile)))->second);
Calin Juravlecc3171a2017-05-19 16:47:53 -07001834}
1835
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001836// Mark a method as executed at least once.
Calin Juravle1ad1e3f2017-09-19 18:20:37 -07001837bool ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, size_t index) {
1838 if (index >= num_method_ids) {
1839 LOG(ERROR) << "Invalid method index " << index << ". num_method_ids=" << num_method_ids;
1840 return false;
1841 }
1842
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001843 if ((flags & MethodHotness::kFlagStartup) != 0) {
1844 method_bitmap.StoreBit(MethodBitIndex(/*startup*/ true, index), /*value*/ true);
1845 }
1846 if ((flags & MethodHotness::kFlagPostStartup) != 0) {
1847 method_bitmap.StoreBit(MethodBitIndex(/*startup*/ false, index), /*value*/ true);
1848 }
1849 if ((flags & MethodHotness::kFlagHot) != 0) {
1850 method_map.FindOrAdd(
1851 index,
Vladimir Marko69d310e2017-10-09 14:12:23 +01001852 InlineCacheMap(std::less<uint16_t>(), allocator_->Adapter(kArenaAllocProfile)));
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001853 }
Calin Juravle1ad1e3f2017-09-19 18:20:37 -07001854 return true;
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001855}
1856
1857ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::DexFileData::GetHotnessInfo(
1858 uint32_t dex_method_index) const {
1859 MethodHotness ret;
1860 if (method_bitmap.LoadBit(MethodBitIndex(/*startup*/ true, dex_method_index))) {
1861 ret.AddFlag(MethodHotness::kFlagStartup);
1862 }
1863 if (method_bitmap.LoadBit(MethodBitIndex(/*startup*/ false, dex_method_index))) {
1864 ret.AddFlag(MethodHotness::kFlagPostStartup);
1865 }
1866 auto it = method_map.find(dex_method_index);
1867 if (it != method_map.end()) {
1868 ret.SetInlineCacheMap(&it->second);
1869 ret.AddFlag(MethodHotness::kFlagHot);
1870 }
1871 return ret;
1872}
1873
Calin Juravlecc3171a2017-05-19 16:47:53 -07001874ProfileCompilationInfo::DexPcData*
1875ProfileCompilationInfo::FindOrAddDexPc(InlineCacheMap* inline_cache, uint32_t dex_pc) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001876 return &(inline_cache->FindOrAdd(dex_pc, DexPcData(&allocator_))->second);
Calin Juravlecc3171a2017-05-19 16:47:53 -07001877}
1878
Mathieu Chartier4f342b02017-07-21 17:12:39 -07001879std::unordered_set<std::string> ProfileCompilationInfo::GetClassDescriptors(
1880 const std::vector<const DexFile*>& dex_files) {
1881 std::unordered_set<std::string> ret;
1882 for (const DexFile* dex_file : dex_files) {
1883 const DexFileData* data = FindDexData(dex_file);
1884 if (data != nullptr) {
1885 for (dex::TypeIndex type_idx : data->class_set) {
1886 if (!dex_file->IsTypeIndexValid(type_idx)) {
1887 // Something went bad. The profile is probably corrupted. Abort and return an emtpy set.
1888 LOG(WARNING) << "Corrupted profile: invalid type index "
1889 << type_idx.index_ << " in dex " << dex_file->GetLocation();
1890 return std::unordered_set<std::string>();
1891 }
1892 const DexFile::TypeId& type_id = dex_file->GetTypeId(type_idx);
1893 ret.insert(dex_file->GetTypeDescriptor(type_id));
1894 }
1895 } else {
1896 VLOG(compiler) << "Failed to find profile data for " << dex_file->GetLocation();
1897 }
1898 }
1899 return ret;
1900}
1901
Calin Juravle31f2c152015-10-23 17:56:15 +01001902} // namespace art