blob: 74bf237c31ff42e572480c79e2f3ce0858972946 [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"
Andreas Gampe57943812017-12-06 21:39:13 -080038#include "base/logging.h" // For VLOG.
Calin Juravle31f2c152015-10-23 17:56:15 +010039#include "base/mutex.h"
Calin Juravle877fd962016-01-05 14:29:29 +000040#include "base/scoped_flock.h"
Calin Juravle66f55232015-12-08 15:09:10 +000041#include "base/stl_util.h"
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -080042#include "base/systrace.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070043#include "base/time_utils.h"
Calin Juravle877fd962016-01-05 14:29:29 +000044#include "base/unix_file/fd_file.h"
David Sehr9e734c72018-01-04 17:56:19 -080045#include "dex/dex_file_loader.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010046#include "jit/profiling_info.h"
Calin Juravle877fd962016-01-05 14:29:29 +000047#include "os.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010048#include "safe_map.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080049#include "utils.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010050
51namespace art {
52
Calin Juravle64142952016-03-21 14:37:55 +000053const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' };
Shubham Ajmeraafbbf182017-08-04 14:33:34 -070054// Last profile version: merge profiles directly from the file without creating
55// profile_compilation_info object. All the profile line headers are now placed together
56// before corresponding method_encodings and class_ids.
57const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '1', '0', '\0' };
Calin Juravle64142952016-03-21 14:37:55 +000058
59static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
60
Calin Juravlec4588572016-06-08 14:24:13 +010061// 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 +010062// Used to facilitate testing profile guided compilation across a large number of apps
Calin Juravlec4588572016-06-08 14:24:13 +010063// using the same test profile.
64static constexpr bool kDebugIgnoreChecksum = false;
65
Calin Juravle589e71e2017-03-03 16:05:05 -080066static constexpr uint8_t kIsMissingTypesEncoding = 6;
67static constexpr uint8_t kIsMegamorphicEncoding = 7;
Calin Juravle940eb0c2017-01-30 19:30:44 -080068
69static_assert(sizeof(InlineCache::kIndividualCacheSize) == sizeof(uint8_t),
70 "InlineCache::kIndividualCacheSize does not have the expect type size");
Calin Juravle589e71e2017-03-03 16:05:05 -080071static_assert(InlineCache::kIndividualCacheSize < kIsMegamorphicEncoding,
72 "InlineCache::kIndividualCacheSize is larger than expected");
73static_assert(InlineCache::kIndividualCacheSize < kIsMissingTypesEncoding,
Calin Juravle940eb0c2017-01-30 19:30:44 -080074 "InlineCache::kIndividualCacheSize is larger than expected");
75
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -070076static bool ChecksumMatch(uint32_t dex_file_checksum, uint32_t checksum) {
77 return kDebugIgnoreChecksum || dex_file_checksum == checksum;
78}
79
Calin Juravlecc3171a2017-05-19 16:47:53 -070080ProfileCompilationInfo::ProfileCompilationInfo(ArenaPool* custom_arena_pool)
Calin Juravlee6f87cc2017-05-24 17:41:05 -070081 : default_arena_pool_(),
Vladimir Markoca6fff82017-10-03 14:49:14 +010082 allocator_(custom_arena_pool),
83 info_(allocator_.Adapter(kArenaAllocProfile)),
84 profile_key_map_(std::less<const std::string>(), allocator_.Adapter(kArenaAllocProfile)) {
Calin Juravlecc3171a2017-05-19 16:47:53 -070085}
86
87ProfileCompilationInfo::ProfileCompilationInfo()
Calin Juravlee6f87cc2017-05-24 17:41:05 -070088 : default_arena_pool_(/*use_malloc*/true, /*low_4gb*/false, "ProfileCompilationInfo"),
Vladimir Markoca6fff82017-10-03 14:49:14 +010089 allocator_(&default_arena_pool_),
90 info_(allocator_.Adapter(kArenaAllocProfile)),
91 profile_key_map_(std::less<const std::string>(), allocator_.Adapter(kArenaAllocProfile)) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -070092}
93
94ProfileCompilationInfo::~ProfileCompilationInfo() {
Vladimir Markoca6fff82017-10-03 14:49:14 +010095 VLOG(profiler) << Dumpable<MemStats>(allocator_.GetMemStats());
Calin Juravle798ba162017-05-23 23:01:53 -070096 for (DexFileData* data : info_) {
97 delete data;
98 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -070099}
100
Calin Juravle940eb0c2017-01-30 19:30:44 -0800101void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx,
102 const dex::TypeIndex& type_idx) {
Calin Juravle589e71e2017-03-03 16:05:05 -0800103 if (is_megamorphic || is_missing_types) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800104 return;
105 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700106
107 // Perform an explicit lookup for the type instead of directly emplacing the
108 // element. We do this because emplace() allocates the node before doing the
109 // lookup and if it then finds an identical element, it shall deallocate the
110 // node. For Arena allocations, that's essentially a leak.
111 ClassReference ref(dex_profile_idx, type_idx);
112 auto it = classes.find(ref);
113 if (it != classes.end()) {
114 // The type index exists.
115 return;
116 }
117
118 // Check if the adding the type will cause the cache to become megamorphic.
119 if (classes.size() + 1 >= InlineCache::kIndividualCacheSize) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800120 is_megamorphic = true;
121 classes.clear();
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700122 return;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800123 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700124
125 // The type does not exist and the inline cache will not be megamorphic.
126 classes.insert(ref);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800127}
128
Calin Juravle34900cc2016-02-05 16:19:19 +0000129// Transform the actual dex location into relative paths.
130// Note: this is OK because we don't store profiles of different apps into the same file.
131// Apps with split apks don't cause trouble because each split has a different name and will not
132// collide with other entries.
Calin Juravle31708b72016-02-05 19:44:05 +0000133std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_location) {
Calin Juravle34900cc2016-02-05 16:19:19 +0000134 DCHECK(!dex_location.empty());
135 size_t last_sep_index = dex_location.find_last_of('/');
136 if (last_sep_index == std::string::npos) {
137 return dex_location;
138 } else {
Calin Juravle31708b72016-02-05 19:44:05 +0000139 DCHECK(last_sep_index < dex_location.size());
140 return dex_location.substr(last_sep_index + 1);
Calin Juravle34900cc2016-02-05 16:19:19 +0000141 }
142}
143
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700144bool ProfileCompilationInfo::AddMethodIndex(MethodHotness::Flag flags, const MethodReference& ref) {
145 DexFileData* data = GetOrAddDexFileData(ref.dex_file);
146 if (data == nullptr) {
147 return false;
148 }
Calin Juravle1ad1e3f2017-09-19 18:20:37 -0700149 return data->AddMethod(flags, ref.index);
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700150}
151
152bool ProfileCompilationInfo::AddMethodIndex(MethodHotness::Flag flags,
153 const std::string& dex_location,
154 uint32_t checksum,
155 uint16_t method_idx,
156 uint32_t num_method_ids) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700157 DexFileData* data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location),
158 checksum,
159 num_method_ids);
160 if (data == nullptr) {
161 return false;
162 }
Calin Juravle1ad1e3f2017-09-19 18:20:37 -0700163 return data->AddMethod(flags, method_idx);
Mathieu Chartierea650f32017-05-24 12:04:13 -0700164}
165
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700166bool ProfileCompilationInfo::AddMethods(const std::vector<ProfileMethodInfo>& methods) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800167 for (const ProfileMethodInfo& method : methods) {
168 if (!AddMethod(method)) {
Calin Juravle67265462016-03-18 16:23:40 +0000169 return false;
170 }
171 }
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700172 return true;
173}
174
175bool ProfileCompilationInfo::AddClasses(const std::set<DexCacheResolvedClasses>& resolved_classes) {
Calin Juravle67265462016-03-18 16:23:40 +0000176 for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
177 if (!AddResolvedClasses(dex_cache)) {
178 return false;
179 }
180 }
181 return true;
182}
183
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700184bool ProfileCompilationInfo::MergeWith(const std::string& filename) {
185 std::string error;
186 int flags = O_RDONLY | O_NOFOLLOW | O_CLOEXEC;
187 ScopedFlock profile_file = LockedFile::Open(filename.c_str(), flags,
188 /*block*/false, &error);
189
190 if (profile_file.get() == nullptr) {
191 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
192 return false;
193 }
194
195 int fd = profile_file->Fd();
196
197 ProfileLoadSatus status = LoadInternal(fd, &error);
198 if (status == kProfileLoadSuccess) {
199 return true;
200 }
201
202 LOG(WARNING) << "Could not load profile data from file " << filename << ": " << error;
203 return false;
204}
205
Calin Juravledcab1902017-05-12 19:18:47 -0700206bool ProfileCompilationInfo::Load(const std::string& filename, bool clear_if_invalid) {
Calin Juravle67265462016-03-18 16:23:40 +0000207 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravle67265462016-03-18 16:23:40 +0000208 std::string error;
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700209
210 if (!IsEmpty()) {
211 return kProfileLoadWouldOverwiteData;
212 }
213
Calin Juravledf674c42017-04-27 19:30:16 -0700214 int flags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
215 // There's no need to fsync profile data right away. We get many chances
216 // to write it again in case something goes wrong. We can rely on a simple
217 // close(), no sync, and let to the kernel decide when to write to disk.
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100218 ScopedFlock profile_file = LockedFile::Open(filename.c_str(), flags,
219 /*block*/false, &error);
220
221 if (profile_file.get() == nullptr) {
Calin Juravle67265462016-03-18 16:23:40 +0000222 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
223 return false;
224 }
225
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100226 int fd = profile_file->Fd();
Calin Juravle67265462016-03-18 16:23:40 +0000227
Calin Juravledcab1902017-05-12 19:18:47 -0700228 ProfileLoadSatus status = LoadInternal(fd, &error);
Calin Juravle5d1bd0a2016-03-24 20:33:22 +0000229 if (status == kProfileLoadSuccess) {
Calin Juravledcab1902017-05-12 19:18:47 -0700230 return true;
231 }
232
233 if (clear_if_invalid &&
234 ((status == kProfileLoadVersionMismatch) || (status == kProfileLoadBadData))) {
235 LOG(WARNING) << "Clearing bad or obsolete profile data from file "
236 << filename << ": " << error;
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100237 if (profile_file->ClearContent()) {
Calin Juravledcab1902017-05-12 19:18:47 -0700238 return true;
Calin Juravle5d1bd0a2016-03-24 20:33:22 +0000239 } else {
Calin Juravledcab1902017-05-12 19:18:47 -0700240 PLOG(WARNING) << "Could not clear profile file: " << filename;
241 return false;
Calin Juravle5d1bd0a2016-03-24 20:33:22 +0000242 }
Calin Juravledcab1902017-05-12 19:18:47 -0700243 }
244
245 LOG(WARNING) << "Could not load profile data from file " << filename << ": " << error;
246 return false;
247}
248
249bool ProfileCompilationInfo::Save(const std::string& filename, uint64_t* bytes_written) {
250 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravledcab1902017-05-12 19:18:47 -0700251 std::string error;
252 int flags = O_WRONLY | O_NOFOLLOW | O_CLOEXEC;
253 // There's no need to fsync profile data right away. We get many chances
254 // to write it again in case something goes wrong. We can rely on a simple
255 // close(), no sync, and let to the kernel decide when to write to disk.
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100256 ScopedFlock profile_file = LockedFile::Open(filename.c_str(), flags,
257 /*block*/false, &error);
258 if (profile_file.get() == nullptr) {
Calin Juravledcab1902017-05-12 19:18:47 -0700259 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
Calin Juravle67265462016-03-18 16:23:40 +0000260 return false;
261 }
262
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100263 int fd = profile_file->Fd();
Calin Juravledcab1902017-05-12 19:18:47 -0700264
Calin Juravle5d1bd0a2016-03-24 20:33:22 +0000265 // We need to clear the data because we don't support appending to the profiles yet.
Narayan Kamatha3d27eb2017-05-11 13:50:59 +0100266 if (!profile_file->ClearContent()) {
Calin Juravle67265462016-03-18 16:23:40 +0000267 PLOG(WARNING) << "Could not clear profile file: " << filename;
268 return false;
269 }
270
271 // This doesn't need locking because we are trying to lock the file for exclusive
272 // access and fail immediately if we can't.
273 bool result = Save(fd);
274 if (result) {
Calin Juravledcab1902017-05-12 19:18:47 -0700275 int64_t size = GetFileSizeBytes(filename);
276 if (size != -1) {
277 VLOG(profiler)
278 << "Successfully saved profile info to " << filename << " Size: "
279 << size;
280 if (bytes_written != nullptr) {
281 *bytes_written = static_cast<uint64_t>(size);
282 }
Calin Juravle67265462016-03-18 16:23:40 +0000283 }
284 } else {
285 VLOG(profiler) << "Failed to save profile info to " << filename;
286 }
287 return result;
288}
289
Calin Juravle64142952016-03-21 14:37:55 +0000290// Returns true if all the bytes were successfully written to the file descriptor.
291static bool WriteBuffer(int fd, const uint8_t* buffer, size_t byte_count) {
292 while (byte_count > 0) {
293 int bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer, byte_count));
294 if (bytes_written == -1) {
Calin Juravle877fd962016-01-05 14:29:29 +0000295 return false;
296 }
Calin Juravle64142952016-03-21 14:37:55 +0000297 byte_count -= bytes_written; // Reduce the number of remaining bytes.
298 buffer += bytes_written; // Move the buffer forward.
299 }
Calin Juravle877fd962016-01-05 14:29:29 +0000300 return true;
Calin Juravle31f2c152015-10-23 17:56:15 +0100301}
302
Calin Juravle64142952016-03-21 14:37:55 +0000303// Add the string bytes to the buffer.
304static void AddStringToBuffer(std::vector<uint8_t>* buffer, const std::string& value) {
305 buffer->insert(buffer->end(), value.begin(), value.end());
306}
307
308// Insert each byte, from low to high into the buffer.
309template <typename T>
310static void AddUintToBuffer(std::vector<uint8_t>* buffer, T value) {
311 for (size_t i = 0; i < sizeof(T); i++) {
312 buffer->push_back((value >> (i * kBitsPerByte)) & 0xff);
313 }
314}
315
316static constexpr size_t kLineHeaderSize =
Calin Juravle940eb0c2017-01-30 19:30:44 -0800317 2 * sizeof(uint16_t) + // class_set.size + dex_location.size
Mathieu Chartierea650f32017-05-24 12:04:13 -0700318 3 * sizeof(uint32_t); // method_map.size + checksum + num_method_ids
Calin Juravle31f2c152015-10-23 17:56:15 +0100319
320/**
321 * Serialization format:
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700322 * [profile_header, zipped[[profile_line_header1, profile_line_header2...],[profile_line_data1,
323 * profile_line_data2...]]]
324 * profile_header:
325 * magic,version,number_of_dex_files,uncompressed_size_of_zipped_data,compressed_data_size
326 * profile_line_header:
327 * dex_location,number_of_classes,methods_region_size,dex_location_checksum,num_method_ids
328 * profile_line_data:
329 * method_encoding_1,method_encoding_2...,class_id1,class_id2...,startup/post startup bitmap
Calin Juravle940eb0c2017-01-30 19:30:44 -0800330 * The method_encoding is:
331 * method_id,number_of_inline_caches,inline_cache1,inline_cache2...
332 * The inline_cache is:
333 * dex_pc,[M|dex_map_size], dex_profile_index,class_id1,class_id2...,dex_profile_index2,...
334 * dex_map_size is the number of dex_indeces that follows.
335 * Classes are grouped per their dex files and the line
336 * `dex_profile_index,class_id1,class_id2...,dex_profile_index2,...` encodes the
337 * mapping from `dex_profile_index` to the set of classes `class_id1,class_id2...`
Calin Juravle589e71e2017-03-03 16:05:05 -0800338 * M stands for megamorphic or missing types and it's encoded as either
339 * the byte kIsMegamorphicEncoding or kIsMissingTypesEncoding.
Calin Juravle940eb0c2017-01-30 19:30:44 -0800340 * When present, there will be no class ids following.
Calin Juravle31f2c152015-10-23 17:56:15 +0100341 **/
Calin Juravle2e2db782016-02-23 12:00:03 +0000342bool ProfileCompilationInfo::Save(int fd) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000343 uint64_t start = NanoTime();
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -0800344 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravle2e2db782016-02-23 12:00:03 +0000345 DCHECK_GE(fd, 0);
Calin Juravle64142952016-03-21 14:37:55 +0000346
Calin Juravle64142952016-03-21 14:37:55 +0000347 // Use a vector wrapper to avoid keeping track of offsets when we add elements.
348 std::vector<uint8_t> buffer;
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000349 if (!WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic))) {
350 return false;
351 }
352 if (!WriteBuffer(fd, kProfileVersion, sizeof(kProfileVersion))) {
353 return false;
354 }
Calin Juravle940eb0c2017-01-30 19:30:44 -0800355 DCHECK_LE(info_.size(), std::numeric_limits<uint8_t>::max());
356 AddUintToBuffer(&buffer, static_cast<uint8_t>(info_.size()));
Calin Juravle64142952016-03-21 14:37:55 +0000357
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000358 uint32_t required_capacity = 0;
359 for (const DexFileData* dex_data_ptr : info_) {
360 const DexFileData& dex_data = *dex_data_ptr;
361 uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
362 required_capacity += kLineHeaderSize +
363 dex_data.profile_key.size() +
364 sizeof(uint16_t) * dex_data.class_set.size() +
Mathieu Chartierea650f32017-05-24 12:04:13 -0700365 methods_region_size +
366 dex_data.bitmap_storage.size();
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000367 }
Mathieu Chartierf2e2af82017-07-12 21:09:57 -0700368 // Allow large profiles for non target builds for the case where we are merging many profiles
369 // to generate a boot image profile.
370 if (kIsTargetBuild && required_capacity > kProfileSizeErrorThresholdInBytes) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000371 LOG(ERROR) << "Profile data size exceeds "
372 << std::to_string(kProfileSizeErrorThresholdInBytes)
373 << " bytes. Profile will not be written to disk.";
374 return false;
375 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000376 AddUintToBuffer(&buffer, required_capacity);
377 if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
378 return false;
379 }
380 // Make sure that the buffer has enough capacity to avoid repeated resizings
381 // while we add data.
382 buffer.reserve(required_capacity);
383 buffer.clear();
384
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700385 // Dex files must be written in the order of their profile index. This
Calin Juravlee0ac1152017-02-13 19:03:47 -0800386 // avoids writing the index in the output file and simplifies the parsing logic.
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700387 // Write profile line headers.
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700388 for (const DexFileData* dex_data_ptr : info_) {
389 const DexFileData& dex_data = *dex_data_ptr;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800390
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700391 if (dex_data.profile_key.size() >= kMaxDexFileKeyLength) {
Calin Juravle64142952016-03-21 14:37:55 +0000392 LOG(WARNING) << "DexFileKey exceeds allocated limit";
393 return false;
394 }
395
Calin Juravle940eb0c2017-01-30 19:30:44 -0800396 uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
Calin Juravle64142952016-03-21 14:37:55 +0000397
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700398 DCHECK_LE(dex_data.profile_key.size(), std::numeric_limits<uint16_t>::max());
Calin Juravle64142952016-03-21 14:37:55 +0000399 DCHECK_LE(dex_data.class_set.size(), std::numeric_limits<uint16_t>::max());
Mathieu Chartierea650f32017-05-24 12:04:13 -0700400 // Write profile line header.
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700401 AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.profile_key.size()));
Calin Juravle64142952016-03-21 14:37:55 +0000402 AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.class_set.size()));
Calin Juravle940eb0c2017-01-30 19:30:44 -0800403 AddUintToBuffer(&buffer, methods_region_size); // uint32_t
Calin Juravle64142952016-03-21 14:37:55 +0000404 AddUintToBuffer(&buffer, dex_data.checksum); // uint32_t
Mathieu Chartierea650f32017-05-24 12:04:13 -0700405 AddUintToBuffer(&buffer, dex_data.num_method_ids); // uint32_t
Calin Juravle64142952016-03-21 14:37:55 +0000406
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700407 AddStringToBuffer(&buffer, dex_data.profile_key);
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700408 }
409
410 for (const DexFileData* dex_data_ptr : info_) {
411 const DexFileData& dex_data = *dex_data_ptr;
412
413 // Note that we allow dex files without any methods or classes, so that
414 // inline caches can refer valid dex files.
Calin Juravle64142952016-03-21 14:37:55 +0000415
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000416 uint16_t last_method_index = 0;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800417 for (const auto& method_it : dex_data.method_map) {
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000418 // Store the difference between the method indices. The SafeMap is ordered by
419 // method_id, so the difference will always be non negative.
420 DCHECK_GE(method_it.first, last_method_index);
421 uint16_t diff_with_last_method_index = method_it.first - last_method_index;
422 last_method_index = method_it.first;
423 AddUintToBuffer(&buffer, diff_with_last_method_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800424 AddInlineCacheToBuffer(&buffer, method_it.second);
Calin Juravle31f2c152015-10-23 17:56:15 +0100425 }
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000426
427 uint16_t last_class_index = 0;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800428 for (const auto& class_id : dex_data.class_set) {
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000429 // Store the difference between the class indices. The set is ordered by
430 // class_id, so the difference will always be non negative.
431 DCHECK_GE(class_id.index_, last_class_index);
432 uint16_t diff_with_last_class_index = class_id.index_ - last_class_index;
433 last_class_index = class_id.index_;
434 AddUintToBuffer(&buffer, diff_with_last_class_index);
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800435 }
Mathieu Chartierea650f32017-05-24 12:04:13 -0700436
437 buffer.insert(buffer.end(),
438 dex_data.bitmap_storage.begin(),
439 dex_data.bitmap_storage.end());
Calin Juravle31f2c152015-10-23 17:56:15 +0100440 }
441
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000442 uint32_t output_size = 0;
443 std::unique_ptr<uint8_t[]> compressed_buffer = DeflateBuffer(buffer.data(),
444 required_capacity,
445 &output_size);
446
Shubham Ajmerad704f0b2017-07-26 16:33:35 -0700447 if (output_size > kProfileSizeWarningThresholdInBytes) {
448 LOG(WARNING) << "Profile data size exceeds "
449 << std::to_string(kProfileSizeWarningThresholdInBytes);
450 }
451
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000452 buffer.clear();
453 AddUintToBuffer(&buffer, output_size);
454
455 if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
456 return false;
457 }
458 if (!WriteBuffer(fd, compressed_buffer.get(), output_size)) {
459 return false;
460 }
461 uint64_t total_time = NanoTime() - start;
462 VLOG(profiler) << "Compressed from "
463 << std::to_string(required_capacity)
464 << " to "
465 << std::to_string(output_size);
466 VLOG(profiler) << "Time to save profile: " << std::to_string(total_time);
467 return true;
Calin Juravle226501b2015-12-11 14:41:31 +0000468}
469
Calin Juravle940eb0c2017-01-30 19:30:44 -0800470void ProfileCompilationInfo::AddInlineCacheToBuffer(std::vector<uint8_t>* buffer,
471 const InlineCacheMap& inline_cache_map) {
472 // Add inline cache map size.
473 AddUintToBuffer(buffer, static_cast<uint16_t>(inline_cache_map.size()));
474 if (inline_cache_map.size() == 0) {
475 return;
476 }
477 for (const auto& inline_cache_it : inline_cache_map) {
478 uint16_t dex_pc = inline_cache_it.first;
479 const DexPcData dex_pc_data = inline_cache_it.second;
480 const ClassSet& classes = dex_pc_data.classes;
481
482 // Add the dex pc.
483 AddUintToBuffer(buffer, dex_pc);
484
Calin Juravle589e71e2017-03-03 16:05:05 -0800485 // Add the megamorphic/missing_types encoding if needed and continue.
486 // In either cases we don't add any classes to the profiles and so there's
487 // no point to continue.
488 // TODO(calin): in case we miss types there is still value to add the
489 // rest of the classes. They can be added without bumping the profile version.
490 if (dex_pc_data.is_missing_types) {
491 DCHECK(!dex_pc_data.is_megamorphic); // at this point the megamorphic flag should not be set.
492 DCHECK_EQ(classes.size(), 0u);
493 AddUintToBuffer(buffer, kIsMissingTypesEncoding);
494 continue;
495 } else if (dex_pc_data.is_megamorphic) {
496 DCHECK_EQ(classes.size(), 0u);
497 AddUintToBuffer(buffer, kIsMegamorphicEncoding);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800498 continue;
499 }
500
501 DCHECK_LT(classes.size(), InlineCache::kIndividualCacheSize);
502 DCHECK_NE(classes.size(), 0u) << "InlineCache contains a dex_pc with 0 classes";
503
504 SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
505 // Group the classes by dex. We expect that most of the classes will come from
506 // the same dex, so this will be more efficient than encoding the dex index
507 // for each class reference.
508 GroupClassesByDex(classes, &dex_to_classes_map);
509 // Add the dex map size.
510 AddUintToBuffer(buffer, static_cast<uint8_t>(dex_to_classes_map.size()));
511 for (const auto& dex_it : dex_to_classes_map) {
512 uint8_t dex_profile_index = dex_it.first;
513 const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
514 // Add the dex profile index.
515 AddUintToBuffer(buffer, dex_profile_index);
516 // Add the the number of classes for each dex profile index.
517 AddUintToBuffer(buffer, static_cast<uint8_t>(dex_classes.size()));
518 for (size_t i = 0; i < dex_classes.size(); i++) {
519 // Add the type index of the classes.
520 AddUintToBuffer(buffer, dex_classes[i].index_);
521 }
522 }
523 }
524}
525
526uint32_t ProfileCompilationInfo::GetMethodsRegionSize(const DexFileData& dex_data) {
527 // ((uint16_t)method index + (uint16_t)inline cache size) * number of methods
528 uint32_t size = 2 * sizeof(uint16_t) * dex_data.method_map.size();
529 for (const auto& method_it : dex_data.method_map) {
530 const InlineCacheMap& inline_cache = method_it.second;
531 size += sizeof(uint16_t) * inline_cache.size(); // dex_pc
532 for (const auto& inline_cache_it : inline_cache) {
533 const ClassSet& classes = inline_cache_it.second.classes;
534 SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
535 GroupClassesByDex(classes, &dex_to_classes_map);
536 size += sizeof(uint8_t); // dex_to_classes_map size
537 for (const auto& dex_it : dex_to_classes_map) {
538 size += sizeof(uint8_t); // dex profile index
539 size += sizeof(uint8_t); // number of classes
540 const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
541 size += sizeof(uint16_t) * dex_classes.size(); // the actual classes
542 }
543 }
544 }
545 return size;
546}
547
548void ProfileCompilationInfo::GroupClassesByDex(
549 const ClassSet& classes,
550 /*out*/SafeMap<uint8_t, std::vector<dex::TypeIndex>>* dex_to_classes_map) {
551 for (const auto& classes_it : classes) {
552 auto dex_it = dex_to_classes_map->FindOrAdd(classes_it.dex_profile_index);
553 dex_it->second.push_back(classes_it.type_index);
554 }
555}
556
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800557ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData(
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700558 const std::string& profile_key,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700559 uint32_t checksum,
560 uint32_t num_method_ids) {
Vladimir Marko3bada4b2017-05-19 12:32:47 +0100561 const auto profile_index_it = profile_key_map_.FindOrAdd(profile_key, profile_key_map_.size());
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700562 if (profile_key_map_.size() > std::numeric_limits<uint8_t>::max()) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800563 // Allow only 255 dex files to be profiled. This allows us to save bytes
564 // when encoding. The number is well above what we expect for normal applications.
565 if (kIsDebugBuild) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700566 LOG(ERROR) << "Exceeded the maximum number of dex files (255). Something went wrong";
Calin Juravle940eb0c2017-01-30 19:30:44 -0800567 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700568 profile_key_map_.erase(profile_key);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800569 return nullptr;
Calin Juravle998c2162015-12-21 15:39:33 +0200570 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700571
572 uint8_t profile_index = profile_index_it->second;
573 if (info_.size() <= profile_index) {
574 // This is a new addition. Add it to the info_ array.
Vladimir Markoca6fff82017-10-03 14:49:14 +0100575 DexFileData* dex_file_data = new (&allocator_) DexFileData(
576 &allocator_,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700577 profile_key,
578 checksum,
579 profile_index,
580 num_method_ids);
Calin Juravlecc3171a2017-05-19 16:47:53 -0700581 info_.push_back(dex_file_data);
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700582 }
583 DexFileData* result = info_[profile_index];
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700584
585 // Check that the checksum matches.
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700586 // 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 -0700587 if (result->checksum != checksum) {
588 LOG(WARNING) << "Checksum mismatch for dex " << profile_key;
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800589 return nullptr;
590 }
Shubham Ajmera61200a02017-08-30 16:29:41 -0700591
592 // DCHECK that profile info map key is consistent with the one stored in the dex file data.
593 // This should always be the case since since the cache map is managed by ProfileCompilationInfo.
594 DCHECK_EQ(profile_key, result->profile_key);
595 DCHECK_EQ(profile_index, result->profile_index);
Calin Juravle1ad1e3f2017-09-19 18:20:37 -0700596
597 if (num_method_ids != result->num_method_ids) {
598 // This should not happen... added to help investigating b/65812889.
599 LOG(ERROR) << "num_method_ids mismatch for dex " << profile_key
600 << ", expected=" << num_method_ids
601 << ", actual=" << result->num_method_ids;
602 return nullptr;
603 }
Shubham Ajmera61200a02017-08-30 16:29:41 -0700604
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700605 return result;
606}
607
608const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData(
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700609 const std::string& profile_key,
610 uint32_t checksum,
611 bool verify_checksum) const {
Vladimir Marko3bada4b2017-05-19 12:32:47 +0100612 const auto profile_index_it = profile_key_map_.find(profile_key);
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700613 if (profile_index_it == profile_key_map_.end()) {
614 return nullptr;
615 }
616
617 uint8_t profile_index = profile_index_it->second;
618 const DexFileData* result = info_[profile_index];
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700619 if (verify_checksum && !ChecksumMatch(result->checksum, checksum)) {
620 return nullptr;
621 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700622 DCHECK_EQ(profile_key, result->profile_key);
623 DCHECK_EQ(profile_index, result->profile_index);
624 return result;
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800625}
626
627bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& classes) {
628 const std::string dex_location = GetProfileDexFileKey(classes.GetDexLocation());
629 const uint32_t checksum = classes.GetLocationChecksum();
Mathieu Chartierea650f32017-05-24 12:04:13 -0700630 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum, classes.NumMethodIds());
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800631 if (data == nullptr) {
Calin Juravle998c2162015-12-21 15:39:33 +0200632 return false;
633 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800634 data->class_set.insert(classes.GetClasses().begin(), classes.GetClasses().end());
635 return true;
636}
637
Calin Juravle940eb0c2017-01-30 19:30:44 -0800638bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
639 uint32_t dex_checksum,
640 uint16_t method_index,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700641 uint32_t num_method_ids,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800642 const OfflineProfileMethodInfo& pmi) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700643 DexFileData* const data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location),
644 dex_checksum,
645 num_method_ids);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800646 if (data == nullptr) { // checksum mismatch
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800647 return false;
648 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700649 // Add the method.
Calin Juravlecc3171a2017-05-19 16:47:53 -0700650 InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700651
652 if (pmi.inline_caches == nullptr) {
653 // If we don't have inline caches return success right away.
654 return true;
655 }
656 for (const auto& pmi_inline_cache_it : *pmi.inline_caches) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800657 uint16_t pmi_ic_dex_pc = pmi_inline_cache_it.first;
658 const DexPcData& pmi_ic_dex_pc_data = pmi_inline_cache_it.second;
Calin Juravlecc3171a2017-05-19 16:47:53 -0700659 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, pmi_ic_dex_pc);
660 if (dex_pc_data->is_missing_types || dex_pc_data->is_megamorphic) {
Calin Juravle589e71e2017-03-03 16:05:05 -0800661 // We are already megamorphic or we are missing types; no point in going forward.
Calin Juravle940eb0c2017-01-30 19:30:44 -0800662 continue;
663 }
Calin Juravle589e71e2017-03-03 16:05:05 -0800664
665 if (pmi_ic_dex_pc_data.is_missing_types) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700666 dex_pc_data->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -0800667 continue;
668 }
669 if (pmi_ic_dex_pc_data.is_megamorphic) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700670 dex_pc_data->SetIsMegamorphic();
Calin Juravle589e71e2017-03-03 16:05:05 -0800671 continue;
672 }
673
Calin Juravle940eb0c2017-01-30 19:30:44 -0800674 for (const ClassReference& class_ref : pmi_ic_dex_pc_data.classes) {
675 const DexReference& dex_ref = pmi.dex_references[class_ref.dex_profile_index];
676 DexFileData* class_dex_data = GetOrAddDexFileData(
677 GetProfileDexFileKey(dex_ref.dex_location),
Mathieu Chartierea650f32017-05-24 12:04:13 -0700678 dex_ref.dex_checksum,
679 dex_ref.num_method_ids);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800680 if (class_dex_data == nullptr) { // checksum mismatch
681 return false;
682 }
Calin Juravlecc3171a2017-05-19 16:47:53 -0700683 dex_pc_data->AddClass(class_dex_data->profile_index, class_ref.type_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800684 }
685 }
686 return true;
687}
688
689bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700690 DexFileData* const data = GetOrAddDexFileData(pmi.ref.dex_file);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800691 if (data == nullptr) { // checksum mismatch
692 return false;
693 }
Mathieu Chartierfc8b4222017-09-17 13:44:24 -0700694 InlineCacheMap* inline_cache = data->FindOrAddMethod(pmi.ref.index);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800695
696 for (const ProfileMethodInfo::ProfileInlineCache& cache : pmi.inline_caches) {
Calin Juravle589e71e2017-03-03 16:05:05 -0800697 if (cache.is_missing_types) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700698 FindOrAddDexPc(inline_cache, cache.dex_pc)->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -0800699 continue;
700 }
Mathieu Chartierdbddc222017-05-24 12:04:13 -0700701 for (const TypeReference& class_ref : cache.classes) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700702 DexFileData* class_dex_data = GetOrAddDexFileData(class_ref.dex_file);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800703 if (class_dex_data == nullptr) { // checksum mismatch
704 return false;
705 }
Calin Juravlecc3171a2017-05-19 16:47:53 -0700706 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, cache.dex_pc);
707 if (dex_pc_data->is_missing_types) {
Calin Juravle589e71e2017-03-03 16:05:05 -0800708 // Don't bother adding classes if we are missing types.
709 break;
710 }
Mathieu Chartierfc8b4222017-09-17 13:44:24 -0700711 dex_pc_data->AddClass(class_dex_data->profile_index, class_ref.TypeIndex());
Calin Juravle940eb0c2017-01-30 19:30:44 -0800712 }
713 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800714 return true;
715}
716
717bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location,
718 uint32_t checksum,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700719 dex::TypeIndex type_idx,
720 uint32_t num_method_ids) {
721 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum, num_method_ids);
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800722 if (data == nullptr) {
723 return false;
724 }
Jeff Hao54b58552016-11-16 15:15:04 -0800725 data->class_set.insert(type_idx);
Calin Juravle998c2162015-12-21 15:39:33 +0200726 return true;
727}
728
Andreas Gampe37c58462017-03-27 15:14:27 -0700729#define READ_UINT(type, buffer, dest, error) \
730 do { \
731 if (!(buffer).ReadUintAndAdvance<type>(&(dest))) { \
732 *(error) = "Could not read "#dest; \
733 return false; \
734 } \
735 } \
Calin Juravle940eb0c2017-01-30 19:30:44 -0800736 while (false)
737
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700738bool ProfileCompilationInfo::ReadInlineCache(
739 SafeBuffer& buffer,
740 uint8_t number_of_dex_files,
741 const SafeMap<uint8_t, uint8_t>& dex_profile_index_remap,
742 /*out*/ InlineCacheMap* inline_cache,
743 /*out*/ std::string* error) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800744 uint16_t inline_cache_size;
745 READ_UINT(uint16_t, buffer, inline_cache_size, error);
746 for (; inline_cache_size > 0; inline_cache_size--) {
747 uint16_t dex_pc;
748 uint8_t dex_to_classes_map_size;
749 READ_UINT(uint16_t, buffer, dex_pc, error);
750 READ_UINT(uint8_t, buffer, dex_to_classes_map_size, error);
Calin Juravlecc3171a2017-05-19 16:47:53 -0700751 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, dex_pc);
Calin Juravle589e71e2017-03-03 16:05:05 -0800752 if (dex_to_classes_map_size == kIsMissingTypesEncoding) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700753 dex_pc_data->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -0800754 continue;
755 }
756 if (dex_to_classes_map_size == kIsMegamorphicEncoding) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700757 dex_pc_data->SetIsMegamorphic();
Calin Juravle940eb0c2017-01-30 19:30:44 -0800758 continue;
759 }
760 for (; dex_to_classes_map_size > 0; dex_to_classes_map_size--) {
761 uint8_t dex_profile_index;
762 uint8_t dex_classes_size;
763 READ_UINT(uint8_t, buffer, dex_profile_index, error);
764 READ_UINT(uint8_t, buffer, dex_classes_size, error);
765 if (dex_profile_index >= number_of_dex_files) {
766 *error = "dex_profile_index out of bounds ";
767 *error += std::to_string(dex_profile_index) + " " + std::to_string(number_of_dex_files);
768 return false;
769 }
770 for (; dex_classes_size > 0; dex_classes_size--) {
771 uint16_t type_index;
772 READ_UINT(uint16_t, buffer, type_index, error);
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700773 dex_pc_data->AddClass(dex_profile_index_remap.Get(dex_profile_index),
774 dex::TypeIndex(type_index));
Calin Juravle940eb0c2017-01-30 19:30:44 -0800775 }
776 }
777 }
778 return true;
779}
780
781bool ProfileCompilationInfo::ReadMethods(SafeBuffer& buffer,
782 uint8_t number_of_dex_files,
783 const ProfileLineHeader& line_header,
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700784 const SafeMap<uint8_t, uint8_t>& dex_profile_index_remap,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800785 /*out*/std::string* error) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000786 uint32_t unread_bytes_before_operation = buffer.CountUnreadBytes();
787 if (unread_bytes_before_operation < line_header.method_region_size_bytes) {
788 *error += "Profile EOF reached prematurely for ReadMethod";
789 return kProfileLoadBadData;
790 }
791 size_t expected_unread_bytes_after_operation = buffer.CountUnreadBytes()
792 - line_header.method_region_size_bytes;
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000793 uint16_t last_method_index = 0;
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000794 while (buffer.CountUnreadBytes() > expected_unread_bytes_after_operation) {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700795 DexFileData* const data = GetOrAddDexFileData(line_header.dex_location,
796 line_header.checksum,
797 line_header.num_method_ids);
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000798 uint16_t diff_with_last_method_index;
799 READ_UINT(uint16_t, buffer, diff_with_last_method_index, error);
800 uint16_t method_index = last_method_index + diff_with_last_method_index;
801 last_method_index = method_index;
Calin Juravlecc3171a2017-05-19 16:47:53 -0700802 InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
Shubham Ajmeraafbbf182017-08-04 14:33:34 -0700803 if (!ReadInlineCache(buffer,
804 number_of_dex_files,
805 dex_profile_index_remap,
806 inline_cache,
807 error)) {
Calin Juravle877fd962016-01-05 14:29:29 +0000808 return false;
809 }
Calin Juravle226501b2015-12-11 14:41:31 +0000810 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000811 uint32_t total_bytes_read = unread_bytes_before_operation - buffer.CountUnreadBytes();
812 if (total_bytes_read != line_header.method_region_size_bytes) {
813 *error += "Profile data inconsistent for ReadMethods";
814 return false;
815 }
Calin Juravle940eb0c2017-01-30 19:30:44 -0800816 return true;
817}
818
819bool ProfileCompilationInfo::ReadClasses(SafeBuffer& buffer,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800820 const ProfileLineHeader& line_header,
821 /*out*/std::string* error) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000822 size_t unread_bytes_before_op = buffer.CountUnreadBytes();
823 if (unread_bytes_before_op < line_header.class_set_size) {
824 *error += "Profile EOF reached prematurely for ReadClasses";
825 return kProfileLoadBadData;
826 }
827
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000828 uint16_t last_class_index = 0;
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000829 for (uint16_t i = 0; i < line_header.class_set_size; i++) {
Shubham Ajmera4b8a96b2017-05-12 18:00:14 +0000830 uint16_t diff_with_last_class_index;
831 READ_UINT(uint16_t, buffer, diff_with_last_class_index, error);
832 uint16_t type_index = last_class_index + diff_with_last_class_index;
833 last_class_index = type_index;
Calin Juravle940eb0c2017-01-30 19:30:44 -0800834 if (!AddClassIndex(line_header.dex_location,
835 line_header.checksum,
Mathieu Chartierea650f32017-05-24 12:04:13 -0700836 dex::TypeIndex(type_index),
837 line_header.num_method_ids)) {
Calin Juravle64142952016-03-21 14:37:55 +0000838 return false;
839 }
840 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000841 size_t total_bytes_read = unread_bytes_before_op - buffer.CountUnreadBytes();
842 uint32_t expected_bytes_read = line_header.class_set_size * sizeof(uint16_t);
843 if (total_bytes_read != expected_bytes_read) {
844 *error += "Profile data inconsistent for ReadClasses";
845 return false;
846 }
Calin Juravle226501b2015-12-11 14:41:31 +0000847 return true;
848}
849
Calin Juravle64142952016-03-21 14:37:55 +0000850// Tests for EOF by trying to read 1 byte from the descriptor.
851// Returns:
852// 0 if the descriptor is at the EOF,
853// -1 if there was an IO error
854// 1 if the descriptor has more content to read
855static int testEOF(int fd) {
856 uint8_t buffer[1];
857 return TEMP_FAILURE_RETRY(read(fd, buffer, 1));
858}
859
860// Reads an uint value previously written with AddUintToBuffer.
861template <typename T>
Calin Juravle940eb0c2017-01-30 19:30:44 -0800862bool ProfileCompilationInfo::SafeBuffer::ReadUintAndAdvance(/*out*/T* value) {
Calin Juravle64142952016-03-21 14:37:55 +0000863 static_assert(std::is_unsigned<T>::value, "Type is not unsigned");
Calin Juravle940eb0c2017-01-30 19:30:44 -0800864 if (ptr_current_ + sizeof(T) > ptr_end_) {
865 return false;
866 }
867 *value = 0;
Calin Juravle64142952016-03-21 14:37:55 +0000868 for (size_t i = 0; i < sizeof(T); i++) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800869 *value += ptr_current_[i] << (i * kBitsPerByte);
Calin Juravle226501b2015-12-11 14:41:31 +0000870 }
Calin Juravle64142952016-03-21 14:37:55 +0000871 ptr_current_ += sizeof(T);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800872 return true;
Calin Juravle64142952016-03-21 14:37:55 +0000873}
874
875bool ProfileCompilationInfo::SafeBuffer::CompareAndAdvance(const uint8_t* data, size_t data_size) {
876 if (ptr_current_ + data_size > ptr_end_) {
877 return false;
878 }
879 if (memcmp(ptr_current_, data, data_size) == 0) {
880 ptr_current_ += data_size;
881 return true;
882 }
883 return false;
884}
885
886ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::SafeBuffer::FillFromFd(
887 int fd,
888 const std::string& source,
889 /*out*/std::string* error) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000890 size_t byte_count = (ptr_end_ - ptr_current_) * sizeof(*ptr_current_);
Calin Juravle64142952016-03-21 14:37:55 +0000891 uint8_t* buffer = ptr_current_;
892 while (byte_count > 0) {
893 int bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, byte_count));
894 if (bytes_read == 0) {
895 *error += "Profile EOF reached prematurely for " + source;
896 return kProfileLoadBadData;
897 } else if (bytes_read < 0) {
898 *error += "Profile IO error for " + source + strerror(errno);
899 return kProfileLoadIOError;
Calin Juravle226501b2015-12-11 14:41:31 +0000900 }
Calin Juravle64142952016-03-21 14:37:55 +0000901 byte_count -= bytes_read;
902 buffer += bytes_read;
Calin Juravle226501b2015-12-11 14:41:31 +0000903 }
Calin Juravle64142952016-03-21 14:37:55 +0000904 return kProfileLoadSuccess;
905}
906
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000907size_t ProfileCompilationInfo::SafeBuffer::CountUnreadBytes() {
908 return (ptr_end_ - ptr_current_) * sizeof(*ptr_current_);
909}
910
911const uint8_t* ProfileCompilationInfo::SafeBuffer::GetCurrentPtr() {
912 return ptr_current_;
913}
914
915void ProfileCompilationInfo::SafeBuffer::Advance(size_t data_size) {
916 ptr_current_ += data_size;
917}
918
Calin Juravle64142952016-03-21 14:37:55 +0000919ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileHeader(
920 int fd,
Calin Juravle940eb0c2017-01-30 19:30:44 -0800921 /*out*/uint8_t* number_of_dex_files,
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000922 /*out*/uint32_t* uncompressed_data_size,
923 /*out*/uint32_t* compressed_data_size,
Calin Juravle64142952016-03-21 14:37:55 +0000924 /*out*/std::string* error) {
925 // Read magic and version
926 const size_t kMagicVersionSize =
927 sizeof(kProfileMagic) +
928 sizeof(kProfileVersion) +
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000929 sizeof(uint8_t) + // number of dex files
930 sizeof(uint32_t) + // size of uncompressed profile data
931 sizeof(uint32_t); // size of compressed profile data
Calin Juravle64142952016-03-21 14:37:55 +0000932
933 SafeBuffer safe_buffer(kMagicVersionSize);
934
935 ProfileLoadSatus status = safe_buffer.FillFromFd(fd, "ReadProfileHeader", error);
936 if (status != kProfileLoadSuccess) {
937 return status;
938 }
939
940 if (!safe_buffer.CompareAndAdvance(kProfileMagic, sizeof(kProfileMagic))) {
941 *error = "Profile missing magic";
942 return kProfileLoadVersionMismatch;
943 }
944 if (!safe_buffer.CompareAndAdvance(kProfileVersion, sizeof(kProfileVersion))) {
945 *error = "Profile version mismatch";
946 return kProfileLoadVersionMismatch;
947 }
Calin Juravle940eb0c2017-01-30 19:30:44 -0800948 if (!safe_buffer.ReadUintAndAdvance<uint8_t>(number_of_dex_files)) {
949 *error = "Cannot read the number of dex files";
950 return kProfileLoadBadData;
951 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000952 if (!safe_buffer.ReadUintAndAdvance<uint32_t>(uncompressed_data_size)) {
953 *error = "Cannot read the size of uncompressed data";
954 return kProfileLoadBadData;
955 }
956 if (!safe_buffer.ReadUintAndAdvance<uint32_t>(compressed_data_size)) {
957 *error = "Cannot read the size of compressed data";
958 return kProfileLoadBadData;
959 }
Calin Juravle64142952016-03-21 14:37:55 +0000960 return kProfileLoadSuccess;
961}
962
Calin Juravle940eb0c2017-01-30 19:30:44 -0800963bool ProfileCompilationInfo::ReadProfileLineHeaderElements(SafeBuffer& buffer,
964 /*out*/uint16_t* dex_location_size,
965 /*out*/ProfileLineHeader* line_header,
966 /*out*/std::string* error) {
967 READ_UINT(uint16_t, buffer, *dex_location_size, error);
968 READ_UINT(uint16_t, buffer, line_header->class_set_size, error);
969 READ_UINT(uint32_t, buffer, line_header->method_region_size_bytes, error);
970 READ_UINT(uint32_t, buffer, line_header->checksum, error);
Mathieu Chartierea650f32017-05-24 12:04:13 -0700971 READ_UINT(uint32_t, buffer, line_header->num_method_ids, error);
Calin Juravle940eb0c2017-01-30 19:30:44 -0800972 return true;
973}
974
Calin Juravle64142952016-03-21 14:37:55 +0000975ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLineHeader(
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000976 SafeBuffer& buffer,
977 /*out*/ProfileLineHeader* line_header,
978 /*out*/std::string* error) {
979 if (buffer.CountUnreadBytes() < kLineHeaderSize) {
980 *error += "Profile EOF reached prematurely for ReadProfileLineHeader";
981 return kProfileLoadBadData;
Calin Juravle64142952016-03-21 14:37:55 +0000982 }
983
Calin Juravle940eb0c2017-01-30 19:30:44 -0800984 uint16_t dex_location_size;
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000985 if (!ReadProfileLineHeaderElements(buffer, &dex_location_size, line_header, error)) {
Calin Juravle940eb0c2017-01-30 19:30:44 -0800986 return kProfileLoadBadData;
987 }
Calin Juravle64142952016-03-21 14:37:55 +0000988
989 if (dex_location_size == 0 || dex_location_size > kMaxDexFileKeyLength) {
Goran Jakovljevic4eb6fbf2016-04-25 19:14:17 +0200990 *error = "DexFileKey has an invalid size: " +
991 std::to_string(static_cast<uint32_t>(dex_location_size));
Calin Juravle64142952016-03-21 14:37:55 +0000992 return kProfileLoadBadData;
993 }
994
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000995 if (buffer.CountUnreadBytes() < dex_location_size) {
996 *error += "Profile EOF reached prematurely for ReadProfileHeaderDexLocation";
997 return kProfileLoadBadData;
Calin Juravle64142952016-03-21 14:37:55 +0000998 }
Shubham Ajmera4d198e02017-05-12 17:45:29 +0000999 const uint8_t* base_ptr = buffer.GetCurrentPtr();
Calin Juravle64142952016-03-21 14:37:55 +00001000 line_header->dex_location.assign(
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001001 reinterpret_cast<const char*>(base_ptr), dex_location_size);
1002 buffer.Advance(dex_location_size);
Calin Juravle64142952016-03-21 14:37:55 +00001003 return kProfileLoadSuccess;
1004}
1005
1006ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine(
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001007 SafeBuffer& buffer,
Calin Juravle940eb0c2017-01-30 19:30:44 -08001008 uint8_t number_of_dex_files,
Calin Juravle64142952016-03-21 14:37:55 +00001009 const ProfileLineHeader& line_header,
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001010 const SafeMap<uint8_t, uint8_t>& dex_profile_index_remap,
1011 bool merge_classes,
Calin Juravle64142952016-03-21 14:37:55 +00001012 /*out*/std::string* error) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001013 DexFileData* data = GetOrAddDexFileData(line_header.dex_location,
1014 line_header.checksum,
1015 line_header.num_method_ids);
1016 if (data == nullptr) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001017 *error = "Error when reading profile file line header: checksum mismatch for "
1018 + line_header.dex_location;
1019 return kProfileLoadBadData;
1020 }
Calin Juravle64142952016-03-21 14:37:55 +00001021
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001022 if (!ReadMethods(buffer, number_of_dex_files, line_header, dex_profile_index_remap, error)) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001023 return kProfileLoadBadData;
Calin Juravle64142952016-03-21 14:37:55 +00001024 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001025
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001026 if (merge_classes) {
1027 if (!ReadClasses(buffer, line_header, error)) {
1028 return kProfileLoadBadData;
1029 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001030 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001031
1032 const size_t bytes = data->bitmap_storage.size();
1033 if (buffer.CountUnreadBytes() < bytes) {
1034 *error += "Profile EOF reached prematurely for ReadProfileHeaderDexLocation";
1035 return kProfileLoadBadData;
1036 }
1037 const uint8_t* base_ptr = buffer.GetCurrentPtr();
Andreas Gampe693bfbf2017-11-10 12:23:31 -08001038 std::copy_n(base_ptr, bytes, data->bitmap_storage.data());
Mathieu Chartierea650f32017-05-24 12:04:13 -07001039 buffer.Advance(bytes);
1040 // Read method bitmap.
Calin Juravle64142952016-03-21 14:37:55 +00001041 return kProfileLoadSuccess;
Calin Juravle226501b2015-12-11 14:41:31 +00001042}
1043
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001044// TODO(calin): Fix this API. ProfileCompilationInfo::Load should be static and
1045// return a unique pointer to a ProfileCompilationInfo upon success.
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001046bool ProfileCompilationInfo::Load(int fd, bool merge_classes) {
Calin Juravle64142952016-03-21 14:37:55 +00001047 std::string error;
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001048
1049 ProfileLoadSatus status = LoadInternal(fd, &error, merge_classes);
Calin Juravle64142952016-03-21 14:37:55 +00001050
1051 if (status == kProfileLoadSuccess) {
1052 return true;
1053 } else {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001054 LOG(WARNING) << "Error when reading profile: " << error;
Calin Juravle64142952016-03-21 14:37:55 +00001055 return false;
1056 }
1057}
1058
Shubham Ajmera188b2bf2017-09-20 15:53:35 -07001059bool ProfileCompilationInfo::VerifyProfileData(const std::vector<const DexFile*>& dex_files) {
1060 std::unordered_map<std::string, const DexFile*> key_to_dex_file;
1061 for (const DexFile* dex_file : dex_files) {
1062 key_to_dex_file.emplace(GetProfileDexFileKey(dex_file->GetLocation()), dex_file);
1063 }
1064 for (const DexFileData* dex_data : info_) {
1065 const auto it = key_to_dex_file.find(dex_data->profile_key);
1066 if (it == key_to_dex_file.end()) {
1067 // It is okay if profile contains data for additional dex files.
1068 continue;
1069 }
1070 const DexFile* dex_file = it->second;
1071 const std::string& dex_location = dex_file->GetLocation();
1072 if (!ChecksumMatch(dex_data->checksum, dex_file->GetLocationChecksum())) {
1073 LOG(ERROR) << "Dex checksum mismatch while verifying profile "
1074 << "dex location " << dex_location << " (checksum="
1075 << dex_file->GetLocationChecksum() << ", profile checksum="
1076 << dex_data->checksum;
1077 return false;
1078 }
Shubham Ajmera460ab792017-09-21 13:44:07 -07001079
1080 if (dex_data->num_method_ids != dex_file->NumMethodIds()) {
1081 LOG(ERROR) << "Number of method ids in dex file and profile don't match."
1082 << "dex location " << dex_location << " NumMethodId in DexFile"
1083 << dex_file->NumMethodIds() << ", NumMethodId in profile"
1084 << dex_data->num_method_ids;
1085 return false;
1086 }
1087
Shubham Ajmera188b2bf2017-09-20 15:53:35 -07001088 // Verify method_encoding.
1089 for (const auto& method_it : dex_data->method_map) {
1090 size_t method_id = (size_t)(method_it.first);
1091 if (method_id >= dex_file->NumMethodIds()) {
1092 LOG(ERROR) << "Invalid method id in profile file. dex location="
1093 << dex_location << " method_id=" << method_id << " NumMethodIds="
1094 << dex_file->NumMethodIds();
1095 return false;
1096 }
1097
1098 // Verify class indices of inline caches.
1099 const InlineCacheMap &inline_cache_map = method_it.second;
1100 for (const auto& inline_cache_it : inline_cache_map) {
1101 const DexPcData dex_pc_data = inline_cache_it.second;
1102 if (dex_pc_data.is_missing_types || dex_pc_data.is_megamorphic) {
1103 // No class indices to verify.
1104 continue;
1105 }
1106
1107 const ClassSet &classes = dex_pc_data.classes;
1108 SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
1109 // Group the classes by dex. We expect that most of the classes will come from
1110 // the same dex, so this will be more efficient than encoding the dex index
1111 // for each class reference.
1112 GroupClassesByDex(classes, &dex_to_classes_map);
1113 for (const auto &dex_it : dex_to_classes_map) {
1114 uint8_t dex_profile_index = dex_it.first;
1115 const auto dex_file_inline_cache_it = key_to_dex_file.find(
1116 info_[dex_profile_index]->profile_key);
1117 if (dex_file_inline_cache_it == key_to_dex_file.end()) {
1118 // It is okay if profile contains data for additional dex files.
1119 continue;
1120 }
1121 const DexFile *dex_file_for_inline_cache_check = dex_file_inline_cache_it->second;
1122 const std::vector<dex::TypeIndex> &dex_classes = dex_it.second;
1123 for (size_t i = 0; i < dex_classes.size(); i++) {
1124 if (dex_classes[i].index_ >= dex_file_for_inline_cache_check->NumTypeIds()) {
1125 LOG(ERROR) << "Invalid inline cache in profile file. dex location="
1126 << dex_location << " method_id=" << method_id
1127 << " dex_profile_index="
1128 << static_cast<uint16_t >(dex_profile_index) << " type_index="
1129 << dex_classes[i].index_
1130 << " NumTypeIds="
1131 << dex_file_for_inline_cache_check->NumTypeIds();
1132 return false;
1133 }
1134 }
1135 }
1136 }
1137 }
1138 // Verify class_ids.
1139 for (const auto& class_id : dex_data->class_set) {
1140 if (class_id.index_ >= dex_file->NumTypeIds()) {
1141 LOG(ERROR) << "Invalid class id in profile file. dex_file location "
1142 << dex_location << " class_id=" << class_id.index_ << " NumClassIds="
1143 << dex_file->NumClassDefs();
1144 return false;
1145 }
1146 }
1147 }
1148 return true;
1149}
1150
Calin Juravledcab1902017-05-12 19:18:47 -07001151// TODO(calin): fail fast if the dex checksums don't match.
Calin Juravle64142952016-03-21 14:37:55 +00001152ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal(
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001153 int fd, std::string* error, bool merge_classes) {
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -08001154 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravle2e2db782016-02-23 12:00:03 +00001155 DCHECK_GE(fd, 0);
Calin Juravle226501b2015-12-11 14:41:31 +00001156
Calin Juravle64142952016-03-21 14:37:55 +00001157 struct stat stat_buffer;
1158 if (fstat(fd, &stat_buffer) != 0) {
1159 return kProfileLoadIOError;
Calin Juravle226501b2015-12-11 14:41:31 +00001160 }
Calin Juravle64142952016-03-21 14:37:55 +00001161 // We allow empty profile files.
1162 // Profiles may be created by ActivityManager or installd before we manage to
1163 // process them in the runtime or profman.
1164 if (stat_buffer.st_size == 0) {
1165 return kProfileLoadSuccess;
1166 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001167 // Read profile header: magic + version + number_of_dex_files.
1168 uint8_t number_of_dex_files;
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001169 uint32_t uncompressed_data_size;
1170 uint32_t compressed_data_size;
1171 ProfileLoadSatus status = ReadProfileHeader(fd,
1172 &number_of_dex_files,
1173 &uncompressed_data_size,
1174 &compressed_data_size,
1175 error);
1176
Calin Juravle64142952016-03-21 14:37:55 +00001177 if (status != kProfileLoadSuccess) {
1178 return status;
1179 }
Mathieu Chartierf2e2af82017-07-12 21:09:57 -07001180 // Allow large profiles for non target builds for the case where we are merging many profiles
1181 // to generate a boot image profile.
1182 if (kIsTargetBuild && uncompressed_data_size > kProfileSizeErrorThresholdInBytes) {
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001183 LOG(ERROR) << "Profile data size exceeds "
1184 << std::to_string(kProfileSizeErrorThresholdInBytes)
1185 << " bytes";
1186 return kProfileLoadBadData;
1187 }
1188 if (uncompressed_data_size > kProfileSizeWarningThresholdInBytes) {
1189 LOG(WARNING) << "Profile data size exceeds "
1190 << std::to_string(kProfileSizeWarningThresholdInBytes)
1191 << " bytes";
1192 }
1193
1194 std::unique_ptr<uint8_t[]> compressed_data(new uint8_t[compressed_data_size]);
1195 bool bytes_read_success =
1196 android::base::ReadFully(fd, compressed_data.get(), compressed_data_size);
1197
1198 if (testEOF(fd) != 0) {
1199 *error += "Unexpected data in the profile file.";
1200 return kProfileLoadBadData;
1201 }
1202
1203 if (!bytes_read_success) {
1204 *error += "Unable to read compressed profile data";
1205 return kProfileLoadBadData;
1206 }
1207
1208 SafeBuffer uncompressed_data(uncompressed_data_size);
1209
1210 int ret = InflateBuffer(compressed_data.get(),
1211 compressed_data_size,
1212 uncompressed_data_size,
1213 uncompressed_data.Get());
1214
1215 if (ret != Z_STREAM_END) {
1216 *error += "Error reading uncompressed profile data";
1217 return kProfileLoadBadData;
1218 }
1219
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001220 std::vector<ProfileLineHeader> profile_line_headers;
1221 // Read profile line headers.
Calin Juravle940eb0c2017-01-30 19:30:44 -08001222 for (uint8_t k = 0; k < number_of_dex_files; k++) {
Calin Juravle64142952016-03-21 14:37:55 +00001223 ProfileLineHeader line_header;
Calin Juravle940eb0c2017-01-30 19:30:44 -08001224
Calin Juravle64142952016-03-21 14:37:55 +00001225 // First, read the line header to get the amount of data we need to read.
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001226 status = ReadProfileLineHeader(uncompressed_data, &line_header, error);
Calin Juravle64142952016-03-21 14:37:55 +00001227 if (status != kProfileLoadSuccess) {
1228 return status;
1229 }
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001230 profile_line_headers.push_back(line_header);
1231 }
Calin Juravle64142952016-03-21 14:37:55 +00001232
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001233 SafeMap<uint8_t, uint8_t> dex_profile_index_remap;
1234 if (!RemapProfileIndex(profile_line_headers, &dex_profile_index_remap)) {
1235 return kProfileLoadBadData;
1236 }
1237
1238 for (uint8_t k = 0; k < number_of_dex_files; k++) {
Calin Juravle64142952016-03-21 14:37:55 +00001239 // Now read the actual profile line.
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001240 status = ReadProfileLine(uncompressed_data,
1241 number_of_dex_files,
1242 profile_line_headers[k],
1243 dex_profile_index_remap,
1244 merge_classes,
1245 error);
Calin Juravle64142952016-03-21 14:37:55 +00001246 if (status != kProfileLoadSuccess) {
1247 return status;
1248 }
Calin Juravle64142952016-03-21 14:37:55 +00001249 }
1250
1251 // Check that we read everything and that profiles don't contain junk data.
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001252 if (uncompressed_data.CountUnreadBytes() > 0) {
Calin Juravle64142952016-03-21 14:37:55 +00001253 *error = "Unexpected content in the profile file";
1254 return kProfileLoadBadData;
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001255 } else {
1256 return kProfileLoadSuccess;
Calin Juravle64142952016-03-21 14:37:55 +00001257 }
Calin Juravle998c2162015-12-21 15:39:33 +02001258}
1259
Shubham Ajmeraafbbf182017-08-04 14:33:34 -07001260bool ProfileCompilationInfo::RemapProfileIndex(
1261 const std::vector<ProfileLineHeader>& profile_line_headers,
1262 /*out*/SafeMap<uint8_t, uint8_t>* dex_profile_index_remap) {
1263 // First verify that all checksums match. This will avoid adding garbage to
1264 // the current profile info.
1265 // Note that the number of elements should be very small, so this should not
1266 // be a performance issue.
1267 for (const ProfileLineHeader other_profile_line_header : profile_line_headers) {
1268 // verify_checksum is false because we want to differentiate between a missing dex data and
1269 // a mismatched checksum.
1270 const DexFileData* dex_data = FindDexData(other_profile_line_header.dex_location,
1271 0u,
1272 false /* verify_checksum */);
1273 if ((dex_data != nullptr) && (dex_data->checksum != other_profile_line_header.checksum)) {
1274 LOG(WARNING) << "Checksum mismatch for dex " << other_profile_line_header.dex_location;
1275 return false;
1276 }
1277 }
1278 // All checksums match. Import the data.
1279 uint32_t num_dex_files = static_cast<uint32_t>(profile_line_headers.size());
1280 for (uint32_t i = 0; i < num_dex_files; i++) {
1281 const DexFileData* dex_data = GetOrAddDexFileData(profile_line_headers[i].dex_location,
1282 profile_line_headers[i].checksum,
1283 profile_line_headers[i].num_method_ids);
1284 if (dex_data == nullptr) {
1285 return false; // Could happen if we exceed the number of allowed dex files.
1286 }
1287 dex_profile_index_remap->Put(i, dex_data->profile_index);
1288 }
1289 return true;
1290}
Shubham Ajmera61200a02017-08-30 16:29:41 -07001291
Shubham Ajmera4d198e02017-05-12 17:45:29 +00001292std::unique_ptr<uint8_t[]> ProfileCompilationInfo::DeflateBuffer(const uint8_t* in_buffer,
1293 uint32_t in_size,
1294 uint32_t* compressed_data_size) {
1295 z_stream strm;
1296 strm.zalloc = Z_NULL;
1297 strm.zfree = Z_NULL;
1298 strm.opaque = Z_NULL;
1299 int ret = deflateInit(&strm, 1);
1300 if (ret != Z_OK) {
1301 return nullptr;
1302 }
1303
1304 uint32_t out_size = deflateBound(&strm, in_size);
1305
1306 std::unique_ptr<uint8_t[]> compressed_buffer(new uint8_t[out_size]);
1307 strm.avail_in = in_size;
1308 strm.next_in = const_cast<uint8_t*>(in_buffer);
1309 strm.avail_out = out_size;
1310 strm.next_out = &compressed_buffer[0];
1311 ret = deflate(&strm, Z_FINISH);
1312 if (ret == Z_STREAM_ERROR) {
1313 return nullptr;
1314 }
1315 *compressed_data_size = out_size - strm.avail_out;
1316 deflateEnd(&strm);
1317 return compressed_buffer;
1318}
1319
1320int ProfileCompilationInfo::InflateBuffer(const uint8_t* in_buffer,
1321 uint32_t in_size,
1322 uint32_t expected_uncompressed_data_size,
1323 uint8_t* out_buffer) {
1324 z_stream strm;
1325
1326 /* allocate inflate state */
1327 strm.zalloc = Z_NULL;
1328 strm.zfree = Z_NULL;
1329 strm.opaque = Z_NULL;
1330 strm.avail_in = in_size;
1331 strm.next_in = const_cast<uint8_t*>(in_buffer);
1332 strm.avail_out = expected_uncompressed_data_size;
1333 strm.next_out = out_buffer;
1334
1335 int ret;
1336 inflateInit(&strm);
1337 ret = inflate(&strm, Z_NO_FLUSH);
1338
1339 if (strm.avail_in != 0 || strm.avail_out != 0) {
1340 return Z_DATA_ERROR;
1341 }
1342 inflateEnd(&strm);
1343 return ret;
1344}
1345
Mathieu Chartier2f794552017-06-19 10:58:08 -07001346bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other,
1347 bool merge_classes) {
Calin Juravle5d1bd0a2016-03-24 20:33:22 +00001348 // First verify that all checksums match. This will avoid adding garbage to
1349 // the current profile info.
1350 // Note that the number of elements should be very small, so this should not
1351 // be a performance issue.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001352 for (const DexFileData* other_dex_data : other.info_) {
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001353 // verify_checksum is false because we want to differentiate between a missing dex data and
1354 // a mismatched checksum.
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001355 const DexFileData* dex_data = FindDexData(other_dex_data->profile_key,
1356 0u,
1357 /* verify_checksum */ false);
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001358 if ((dex_data != nullptr) && (dex_data->checksum != other_dex_data->checksum)) {
1359 LOG(WARNING) << "Checksum mismatch for dex " << other_dex_data->profile_key;
Calin Juravle5d1bd0a2016-03-24 20:33:22 +00001360 return false;
1361 }
1362 }
1363 // All checksums match. Import the data.
Calin Juravle940eb0c2017-01-30 19:30:44 -08001364
1365 // The other profile might have a different indexing of dex files.
1366 // That is because each dex files gets a 'dex_profile_index' on a first come first served basis.
1367 // That means that the order in with the methods are added to the profile matters for the
1368 // actual indices.
1369 // The reason we cannot rely on the actual multidex index is that a single profile may store
1370 // data from multiple splits. This means that a profile may contain a classes2.dex from split-A
1371 // and one from split-B.
1372
1373 // First, build a mapping from other_dex_profile_index to this_dex_profile_index.
1374 // This will make sure that the ClassReferences will point to the correct dex file.
1375 SafeMap<uint8_t, uint8_t> dex_profile_index_remap;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001376 for (const DexFileData* other_dex_data : other.info_) {
1377 const DexFileData* dex_data = GetOrAddDexFileData(other_dex_data->profile_key,
Mathieu Chartierea650f32017-05-24 12:04:13 -07001378 other_dex_data->checksum,
1379 other_dex_data->num_method_ids);
Calin Juravlee0ac1152017-02-13 19:03:47 -08001380 if (dex_data == nullptr) {
1381 return false; // Could happen if we exceed the number of allowed dex files.
1382 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001383 dex_profile_index_remap.Put(other_dex_data->profile_index, dex_data->profile_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -08001384 }
1385
1386 // Merge the actual profile data.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001387 for (const DexFileData* other_dex_data : other.info_) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001388 DexFileData* dex_data = const_cast<DexFileData*>(FindDexData(other_dex_data->profile_key,
1389 other_dex_data->checksum));
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001390 DCHECK(dex_data != nullptr);
Calin Juravle940eb0c2017-01-30 19:30:44 -08001391
1392 // Merge the classes.
Mathieu Chartier2f794552017-06-19 10:58:08 -07001393 if (merge_classes) {
1394 dex_data->class_set.insert(other_dex_data->class_set.begin(),
1395 other_dex_data->class_set.end());
1396 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001397
1398 // Merge the methods and the inline caches.
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001399 for (const auto& other_method_it : other_dex_data->method_map) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001400 uint16_t other_method_index = other_method_it.first;
Calin Juravlecc3171a2017-05-19 16:47:53 -07001401 InlineCacheMap* inline_cache = dex_data->FindOrAddMethod(other_method_index);
Calin Juravle940eb0c2017-01-30 19:30:44 -08001402 const auto& other_inline_cache = other_method_it.second;
1403 for (const auto& other_ic_it : other_inline_cache) {
1404 uint16_t other_dex_pc = other_ic_it.first;
1405 const ClassSet& other_class_set = other_ic_it.second.classes;
Calin Juravlecc3171a2017-05-19 16:47:53 -07001406 DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, other_dex_pc);
Calin Juravle589e71e2017-03-03 16:05:05 -08001407 if (other_ic_it.second.is_missing_types) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001408 dex_pc_data->SetIsMissingTypes();
Calin Juravle589e71e2017-03-03 16:05:05 -08001409 } else if (other_ic_it.second.is_megamorphic) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001410 dex_pc_data->SetIsMegamorphic();
Calin Juravle0def68d2017-02-21 19:00:33 -08001411 } else {
1412 for (const auto& class_it : other_class_set) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001413 dex_pc_data->AddClass(dex_profile_index_remap.Get(
Calin Juravle0def68d2017-02-21 19:00:33 -08001414 class_it.dex_profile_index), class_it.type_index);
1415 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001416 }
1417 }
1418 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001419
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001420 // Merge the method bitmaps.
Mathieu Chartierea650f32017-05-24 12:04:13 -07001421 dex_data->MergeBitmap(*other_dex_data);
Calin Juravle998c2162015-12-21 15:39:33 +02001422 }
1423 return true;
Calin Juravle226501b2015-12-11 14:41:31 +00001424}
1425
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001426const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData(
1427 const DexFile* dex_file) const {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001428 return FindDexData(GetProfileDexFileKey(dex_file->GetLocation()),
1429 dex_file->GetLocationChecksum());
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001430}
1431
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001432ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::GetMethodHotness(
1433 const MethodReference& method_ref) const {
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001434 const DexFileData* dex_data = FindDexData(method_ref.dex_file);
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001435 return dex_data != nullptr
Mathieu Chartierfc8b4222017-09-17 13:44:24 -07001436 ? dex_data->GetHotnessInfo(method_ref.index)
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001437 : MethodHotness();
Mathieu Chartierdb40eac2017-06-09 18:34:11 -07001438}
1439
Mathieu Chartier2f794552017-06-19 10:58:08 -07001440bool ProfileCompilationInfo::AddMethodHotness(const MethodReference& method_ref,
1441 const MethodHotness& hotness) {
1442 DexFileData* dex_data = GetOrAddDexFileData(method_ref.dex_file);
1443 if (dex_data != nullptr) {
1444 // TODO: Add inline caches.
Calin Juravle1ad1e3f2017-09-19 18:20:37 -07001445 return dex_data->AddMethod(
1446 static_cast<MethodHotness::Flag>(hotness.GetFlags()), method_ref.index);
Mathieu Chartier2f794552017-06-19 10:58:08 -07001447 }
1448 return false;
1449}
1450
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001451ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::GetMethodHotness(
1452 const std::string& dex_location,
1453 uint32_t dex_checksum,
1454 uint16_t dex_method_index) const {
1455 const DexFileData* dex_data = FindDexData(GetProfileDexFileKey(dex_location), dex_checksum);
1456 return dex_data != nullptr ? dex_data->GetHotnessInfo(dex_method_index) : MethodHotness();
Calin Juravle226501b2015-12-11 14:41:31 +00001457}
1458
Calin Juravle940eb0c2017-01-30 19:30:44 -08001459
Calin Juravlecc3171a2017-05-19 16:47:53 -07001460std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> ProfileCompilationInfo::GetMethod(
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001461 const std::string& dex_location,
1462 uint32_t dex_checksum,
1463 uint16_t dex_method_index) const {
1464 MethodHotness hotness(GetMethodHotness(dex_location, dex_checksum, dex_method_index));
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001465 if (!hotness.IsHot()) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001466 return nullptr;
Calin Juravle940eb0c2017-01-30 19:30:44 -08001467 }
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001468 const InlineCacheMap* inline_caches = hotness.GetInlineCacheMap();
1469 DCHECK(inline_caches != nullptr);
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001470 std::unique_ptr<OfflineProfileMethodInfo> pmi(new OfflineProfileMethodInfo(inline_caches));
Calin Juravle940eb0c2017-01-30 19:30:44 -08001471
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001472 pmi->dex_references.resize(info_.size());
1473 for (const DexFileData* dex_data : info_) {
1474 pmi->dex_references[dex_data->profile_index].dex_location = dex_data->profile_key;
1475 pmi->dex_references[dex_data->profile_index].dex_checksum = dex_data->checksum;
Mathieu Chartierea650f32017-05-24 12:04:13 -07001476 pmi->dex_references[dex_data->profile_index].num_method_ids = dex_data->num_method_ids;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001477 }
1478
Calin Juravlecc3171a2017-05-19 16:47:53 -07001479 return pmi;
Calin Juravle940eb0c2017-01-30 19:30:44 -08001480}
1481
1482
Andreas Gampea5b09a62016-11-17 15:21:22 -08001483bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001484 const DexFileData* dex_data = FindDexData(&dex_file);
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001485 if (dex_data != nullptr) {
Calin Juravlecc3171a2017-05-19 16:47:53 -07001486 const ArenaSet<dex::TypeIndex>& classes = dex_data->class_set;
Jeff Hao54b58552016-11-16 15:15:04 -08001487 return classes.find(type_idx) != classes.end();
Mathieu Chartiera8077802016-03-16 19:08:31 -07001488 }
1489 return false;
1490}
1491
Calin Juravle998c2162015-12-21 15:39:33 +02001492uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
1493 uint32_t total = 0;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001494 for (const DexFileData* dex_data : info_) {
1495 total += dex_data->method_map.size();
Calin Juravle998c2162015-12-21 15:39:33 +02001496 }
1497 return total;
1498}
1499
Calin Juravle67265462016-03-18 16:23:40 +00001500uint32_t ProfileCompilationInfo::GetNumberOfResolvedClasses() const {
1501 uint32_t total = 0;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001502 for (const DexFileData* dex_data : info_) {
1503 total += dex_data->class_set.size();
Calin Juravle67265462016-03-18 16:23:40 +00001504 }
1505 return total;
1506}
1507
David Sehrb18991b2017-02-08 20:58:10 -08001508// Produce a non-owning vector from a vector.
1509template<typename T>
1510const std::vector<T*>* MakeNonOwningVector(const std::vector<std::unique_ptr<T>>* owning_vector) {
1511 auto non_owning_vector = new std::vector<T*>();
1512 for (auto& element : *owning_vector) {
1513 non_owning_vector->push_back(element.get());
1514 }
1515 return non_owning_vector;
1516}
1517
1518std::string ProfileCompilationInfo::DumpInfo(
1519 const std::vector<std::unique_ptr<const DexFile>>* dex_files,
1520 bool print_full_dex_location) const {
1521 std::unique_ptr<const std::vector<const DexFile*>> non_owning_dex_files(
1522 MakeNonOwningVector(dex_files));
1523 return DumpInfo(non_owning_dex_files.get(), print_full_dex_location);
1524}
1525
Calin Juravle998c2162015-12-21 15:39:33 +02001526std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
1527 bool print_full_dex_location) const {
Calin Juravle226501b2015-12-11 14:41:31 +00001528 std::ostringstream os;
1529 if (info_.empty()) {
1530 return "ProfileInfo: empty";
1531 }
1532
1533 os << "ProfileInfo:";
1534
Calin Juravlea308a322017-07-18 16:51:51 -07001535 const std::string kFirstDexFileKeySubstitute = "!classes.dex";
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001536
1537 for (const DexFileData* dex_data : info_) {
Calin Juravle226501b2015-12-11 14:41:31 +00001538 os << "\n";
Calin Juravle226501b2015-12-11 14:41:31 +00001539 if (print_full_dex_location) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001540 os << dex_data->profile_key;
Calin Juravle226501b2015-12-11 14:41:31 +00001541 } else {
1542 // Replace the (empty) multidex suffix of the first key with a substitute for easier reading.
Mathieu Chartier79c87da2017-10-10 11:54:29 -07001543 std::string multidex_suffix = DexFileLoader::GetMultiDexSuffix(dex_data->profile_key);
Calin Juravle226501b2015-12-11 14:41:31 +00001544 os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
1545 }
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001546 os << " [index=" << static_cast<uint32_t>(dex_data->profile_index) << "]";
Calin Juravle876f3502016-03-24 16:16:34 +00001547 const DexFile* dex_file = nullptr;
1548 if (dex_files != nullptr) {
1549 for (size_t i = 0; i < dex_files->size(); i++) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001550 if (dex_data->profile_key == (*dex_files)[i]->GetLocation()) {
Calin Juravle876f3502016-03-24 16:16:34 +00001551 dex_file = (*dex_files)[i];
Calin Juravle998c2162015-12-21 15:39:33 +02001552 }
Calin Juravle226501b2015-12-11 14:41:31 +00001553 }
Calin Juravle876f3502016-03-24 16:16:34 +00001554 }
Mathieu Chartier28b5c582017-06-06 14:12:50 -07001555 os << "\n\thot methods: ";
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001556 for (const auto& method_it : dex_data->method_map) {
Calin Juravle876f3502016-03-24 16:16:34 +00001557 if (dex_file != nullptr) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001558 os << "\n\t\t" << dex_file->PrettyMethod(method_it.first, true);
Calin Juravle876f3502016-03-24 16:16:34 +00001559 } else {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001560 os << method_it.first;
Calin Juravle876f3502016-03-24 16:16:34 +00001561 }
Calin Juravle940eb0c2017-01-30 19:30:44 -08001562
1563 os << "[";
1564 for (const auto& inline_cache_it : method_it.second) {
1565 os << "{" << std::hex << inline_cache_it.first << std::dec << ":";
Calin Juravle589e71e2017-03-03 16:05:05 -08001566 if (inline_cache_it.second.is_missing_types) {
1567 os << "MT";
1568 } else if (inline_cache_it.second.is_megamorphic) {
1569 os << "MM";
Calin Juravle940eb0c2017-01-30 19:30:44 -08001570 } else {
1571 for (const ClassReference& class_ref : inline_cache_it.second.classes) {
1572 os << "(" << static_cast<uint32_t>(class_ref.dex_profile_index)
1573 << "," << class_ref.type_index.index_ << ")";
1574 }
1575 }
1576 os << "}";
1577 }
1578 os << "], ";
Calin Juravle876f3502016-03-24 16:16:34 +00001579 }
Mathieu Chartier28b5c582017-06-06 14:12:50 -07001580 bool startup = true;
1581 while (true) {
1582 os << "\n\t" << (startup ? "startup methods: " : "post startup methods: ");
1583 for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001584 MethodHotness hotness_info(dex_data->GetHotnessInfo(method_idx));
1585 if (startup ? hotness_info.IsStartup() : hotness_info.IsPostStartup()) {
Calin Juravle0545d4a2017-12-01 13:36:26 -08001586 if (dex_file != nullptr) {
1587 os << "\n\t\t" << dex_file->PrettyMethod(method_idx, true);
1588 } else {
1589 os << method_idx << ", ";
1590 }
Mathieu Chartier28b5c582017-06-06 14:12:50 -07001591 }
1592 }
1593 if (startup == false) {
1594 break;
1595 }
1596 startup = false;
1597 }
Calin Juravle876f3502016-03-24 16:16:34 +00001598 os << "\n\tclasses: ";
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001599 for (const auto class_it : dex_data->class_set) {
Calin Juravle876f3502016-03-24 16:16:34 +00001600 if (dex_file != nullptr) {
Jeff Hao54b58552016-11-16 15:15:04 -08001601 os << "\n\t\t" << dex_file->PrettyType(class_it);
Calin Juravle876f3502016-03-24 16:16:34 +00001602 } else {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001603 os << class_it.index_ << ",";
Calin Juravle876f3502016-03-24 16:16:34 +00001604 }
Calin Juravle226501b2015-12-11 14:41:31 +00001605 }
1606 }
1607 return os.str();
1608}
1609
Mathieu Chartierea650f32017-05-24 12:04:13 -07001610bool ProfileCompilationInfo::GetClassesAndMethods(
1611 const DexFile& dex_file,
1612 /*out*/std::set<dex::TypeIndex>* class_set,
1613 /*out*/std::set<uint16_t>* hot_method_set,
1614 /*out*/std::set<uint16_t>* startup_method_set,
1615 /*out*/std::set<uint16_t>* post_startup_method_method_set) const {
Mathieu Chartier34067262017-04-06 13:55:46 -07001616 std::set<std::string> ret;
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001617 const DexFileData* dex_data = FindDexData(&dex_file);
1618 if (dex_data == nullptr) {
Mathieu Chartier34067262017-04-06 13:55:46 -07001619 return false;
David Sehr7c80f2d2017-02-07 16:47:58 -08001620 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001621 for (const auto& it : dex_data->method_map) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001622 hot_method_set->insert(it.first);
1623 }
1624 for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001625 MethodHotness hotness = dex_data->GetHotnessInfo(method_idx);
1626 if (hotness.IsStartup()) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001627 startup_method_set->insert(method_idx);
1628 }
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001629 if (hotness.IsPostStartup()) {
Mathieu Chartierea650f32017-05-24 12:04:13 -07001630 post_startup_method_method_set->insert(method_idx);
1631 }
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001632 }
Calin Juravlecc3171a2017-05-19 16:47:53 -07001633 for (const dex::TypeIndex& type_index : dex_data->class_set) {
1634 class_set->insert(type_index);
1635 }
Mathieu Chartier34067262017-04-06 13:55:46 -07001636 return true;
David Sehr7c80f2d2017-02-07 16:47:58 -08001637}
1638
Calin Juravle2e2db782016-02-23 12:00:03 +00001639bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001640 // No need to compare profile_key_map_. That's only a cache for fast search.
1641 // All the information is already in the info_ vector.
1642 if (info_.size() != other.info_.size()) {
1643 return false;
1644 }
1645 for (size_t i = 0; i < info_.size(); i++) {
1646 const DexFileData& dex_data = *info_[i];
1647 const DexFileData& other_dex_data = *other.info_[i];
1648 if (!(dex_data == other_dex_data)) {
1649 return false;
1650 }
1651 }
1652 return true;
Calin Juravle877fd962016-01-05 14:29:29 +00001653}
1654
Mathieu Chartier046854b2017-03-01 17:16:22 -08001655std::set<DexCacheResolvedClasses> ProfileCompilationInfo::GetResolvedClasses(
Calin Juravle08556882017-05-26 16:40:45 -07001656 const std::vector<const DexFile*>& dex_files) const {
1657 std::unordered_map<std::string, const DexFile* > key_to_dex_file;
1658 for (const DexFile* dex_file : dex_files) {
1659 key_to_dex_file.emplace(GetProfileDexFileKey(dex_file->GetLocation()), dex_file);
Mathieu Chartier046854b2017-03-01 17:16:22 -08001660 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -08001661 std::set<DexCacheResolvedClasses> ret;
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001662 for (const DexFileData* dex_data : info_) {
Calin Juravle08556882017-05-26 16:40:45 -07001663 const auto it = key_to_dex_file.find(dex_data->profile_key);
1664 if (it != key_to_dex_file.end()) {
1665 const DexFile* dex_file = it->second;
1666 const std::string& dex_location = dex_file->GetLocation();
1667 if (dex_data->checksum != it->second->GetLocationChecksum()) {
1668 LOG(ERROR) << "Dex checksum mismatch when getting resolved classes from profile for "
1669 << "location " << dex_location << " (checksum=" << dex_file->GetLocationChecksum()
1670 << ", profile checksum=" << dex_data->checksum;
1671 return std::set<DexCacheResolvedClasses>();
1672 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001673 DexCacheResolvedClasses classes(dex_location,
1674 dex_location,
1675 dex_data->checksum,
1676 dex_data->num_method_ids);
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001677 classes.AddClasses(dex_data->class_set.begin(), dex_data->class_set.end());
Mathieu Chartier046854b2017-03-01 17:16:22 -08001678 ret.insert(classes);
1679 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -08001680 }
1681 return ret;
1682}
1683
Calin Juravle7bcdb532016-06-07 16:14:47 +01001684// Naive implementation to generate a random profile file suitable for testing.
1685bool ProfileCompilationInfo::GenerateTestProfile(int fd,
1686 uint16_t number_of_dex_files,
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001687 uint16_t method_percentage,
1688 uint16_t class_percentage,
Jeff Haof0a31f82017-03-27 15:50:37 -07001689 uint32_t random_seed) {
Calin Juravle7bcdb532016-06-07 16:14:47 +01001690 const std::string base_dex_location = "base.apk";
1691 ProfileCompilationInfo info;
1692 // The limits are defined by the dex specification.
Mathieu Chartierea650f32017-05-24 12:04:13 -07001693 const uint16_t max_method = std::numeric_limits<uint16_t>::max();
1694 const uint16_t max_classes = std::numeric_limits<uint16_t>::max();
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001695 uint16_t number_of_methods = max_method * method_percentage / 100;
1696 uint16_t number_of_classes = max_classes * class_percentage / 100;
Calin Juravle7bcdb532016-06-07 16:14:47 +01001697
Jeff Haof0a31f82017-03-27 15:50:37 -07001698 std::srand(random_seed);
Calin Juravle7bcdb532016-06-07 16:14:47 +01001699
1700 // Make sure we generate more samples with a low index value.
1701 // This makes it more likely to hit valid method/class indices in small apps.
1702 const uint16_t kFavorFirstN = 10000;
1703 const uint16_t kFavorSplit = 2;
1704
1705 for (uint16_t i = 0; i < number_of_dex_files; i++) {
Mathieu Chartier79c87da2017-10-10 11:54:29 -07001706 std::string dex_location = DexFileLoader::GetMultiDexLocation(i, base_dex_location.c_str());
Calin Juravle7bcdb532016-06-07 16:14:47 +01001707 std::string profile_key = GetProfileDexFileKey(dex_location);
1708
1709 for (uint16_t m = 0; m < number_of_methods; m++) {
1710 uint16_t method_idx = rand() % max_method;
1711 if (m < (number_of_methods / kFavorSplit)) {
1712 method_idx %= kFavorFirstN;
1713 }
Mathieu Chartierc46cf802017-09-28 11:52:19 -07001714 // Alternate between startup and post startup.
1715 uint32_t flags = MethodHotness::kFlagHot;
1716 flags |= ((m & 1) != 0) ? MethodHotness::kFlagPostStartup : MethodHotness::kFlagStartup;
1717 info.AddMethodIndex(static_cast<MethodHotness::Flag>(flags),
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -07001718 profile_key,
1719 /*method_idx*/ 0,
1720 method_idx,
1721 max_method);
Calin Juravle7bcdb532016-06-07 16:14:47 +01001722 }
1723
1724 for (uint16_t c = 0; c < number_of_classes; c++) {
Jeff Hao54b58552016-11-16 15:15:04 -08001725 uint16_t type_idx = rand() % max_classes;
Calin Juravle7bcdb532016-06-07 16:14:47 +01001726 if (c < (number_of_classes / kFavorSplit)) {
Jeff Hao54b58552016-11-16 15:15:04 -08001727 type_idx %= kFavorFirstN;
Calin Juravle7bcdb532016-06-07 16:14:47 +01001728 }
Mathieu Chartierea650f32017-05-24 12:04:13 -07001729 info.AddClassIndex(profile_key, 0, dex::TypeIndex(type_idx), max_method);
Calin Juravle7bcdb532016-06-07 16:14:47 +01001730 }
1731 }
1732 return info.Save(fd);
1733}
1734
Jeff Haof0a31f82017-03-27 15:50:37 -07001735// Naive implementation to generate a random profile file suitable for testing.
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001736// Description of random selection:
1737// * Select a random starting point S.
1738// * For every index i, add (S+i) % (N - total number of methods/classes) to profile with the
1739// probably of 1/(N - i - number of methods/classes needed to add in profile).
Jeff Haof0a31f82017-03-27 15:50:37 -07001740bool ProfileCompilationInfo::GenerateTestProfile(
1741 int fd,
1742 std::vector<std::unique_ptr<const DexFile>>& dex_files,
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001743 uint16_t method_percentage,
1744 uint16_t class_percentage,
Jeff Haof0a31f82017-03-27 15:50:37 -07001745 uint32_t random_seed) {
1746 std::srand(random_seed);
1747 ProfileCompilationInfo info;
1748 for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
1749 const std::string& location = dex_file->GetLocation();
1750 uint32_t checksum = dex_file->GetLocationChecksum();
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001751
1752 uint32_t number_of_classes = dex_file->NumClassDefs();
1753 uint32_t classes_required_in_profile = (number_of_classes * class_percentage) / 100;
1754 uint32_t class_start_index = rand() % number_of_classes;
1755 for (uint32_t i = 0; i < number_of_classes && classes_required_in_profile; ++i) {
1756 if (number_of_classes - i == classes_required_in_profile ||
1757 std::rand() % (number_of_classes - i - classes_required_in_profile) == 0) {
1758 uint32_t class_index = (i + class_start_index) % number_of_classes;
Mathieu Chartierea650f32017-05-24 12:04:13 -07001759 info.AddClassIndex(location,
1760 checksum,
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001761 dex_file->GetClassDef(class_index).class_idx_,
Mathieu Chartierea650f32017-05-24 12:04:13 -07001762 dex_file->NumMethodIds());
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001763 classes_required_in_profile--;
Jeff Haof0a31f82017-03-27 15:50:37 -07001764 }
1765 }
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001766
1767 uint32_t number_of_methods = dex_file->NumMethodIds();
1768 uint32_t methods_required_in_profile = (number_of_methods * method_percentage) / 100;
1769 uint32_t method_start_index = rand() % number_of_methods;
1770 for (uint32_t i = 0; i < number_of_methods && methods_required_in_profile; ++i) {
1771 if (number_of_methods - i == methods_required_in_profile ||
1772 std::rand() % (number_of_methods - i - methods_required_in_profile) == 0) {
1773 uint32_t method_index = (method_start_index + i) % number_of_methods;
Mathieu Chartierc46cf802017-09-28 11:52:19 -07001774 // Alternate between startup and post startup.
1775 uint32_t flags = MethodHotness::kFlagHot;
1776 flags |= ((method_index & 1) != 0)
1777 ? MethodHotness::kFlagPostStartup
1778 : MethodHotness::kFlagStartup;
1779 info.AddMethodIndex(static_cast<MethodHotness::Flag>(flags),
1780 MethodReference(dex_file.get(), method_index));
Shubham Ajmerad704f0b2017-07-26 16:33:35 -07001781 methods_required_in_profile--;
Jeff Haof0a31f82017-03-27 15:50:37 -07001782 }
1783 }
1784 }
1785 return info.Save(fd);
1786}
1787
Calin Juravle940eb0c2017-01-30 19:30:44 -08001788bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==(
1789 const OfflineProfileMethodInfo& other) const {
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001790 if (inline_caches->size() != other.inline_caches->size()) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001791 return false;
1792 }
1793
1794 // We can't use a simple equality test because we need to match the dex files
Calin Juravle589e71e2017-03-03 16:05:05 -08001795 // of the inline caches which might have different profile indexes.
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001796 for (const auto& inline_cache_it : *inline_caches) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001797 uint16_t dex_pc = inline_cache_it.first;
1798 const DexPcData dex_pc_data = inline_cache_it.second;
Calin Juravlee6f87cc2017-05-24 17:41:05 -07001799 const auto& other_it = other.inline_caches->find(dex_pc);
1800 if (other_it == other.inline_caches->end()) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001801 return false;
1802 }
1803 const DexPcData& other_dex_pc_data = other_it->second;
Calin Juravle589e71e2017-03-03 16:05:05 -08001804 if (dex_pc_data.is_megamorphic != other_dex_pc_data.is_megamorphic ||
1805 dex_pc_data.is_missing_types != other_dex_pc_data.is_missing_types) {
Calin Juravle940eb0c2017-01-30 19:30:44 -08001806 return false;
1807 }
1808 for (const ClassReference& class_ref : dex_pc_data.classes) {
1809 bool found = false;
1810 for (const ClassReference& other_class_ref : other_dex_pc_data.classes) {
1811 CHECK_LE(class_ref.dex_profile_index, dex_references.size());
1812 CHECK_LE(other_class_ref.dex_profile_index, other.dex_references.size());
1813 const DexReference& dex_ref = dex_references[class_ref.dex_profile_index];
1814 const DexReference& other_dex_ref = other.dex_references[other_class_ref.dex_profile_index];
1815 if (class_ref.type_index == other_class_ref.type_index &&
1816 dex_ref == other_dex_ref) {
1817 found = true;
1818 break;
1819 }
1820 }
1821 if (!found) {
1822 return false;
1823 }
1824 }
1825 }
1826 return true;
1827}
1828
Calin Juravlecea9e9d2017-03-23 19:04:59 -07001829bool ProfileCompilationInfo::IsEmpty() const {
1830 DCHECK_EQ(info_.empty(), profile_key_map_.empty());
1831 return info_.empty();
1832}
1833
Calin Juravlecc3171a2017-05-19 16:47:53 -07001834ProfileCompilationInfo::InlineCacheMap*
1835ProfileCompilationInfo::DexFileData::FindOrAddMethod(uint16_t method_index) {
1836 return &(method_map.FindOrAdd(
1837 method_index,
Vladimir Marko69d310e2017-10-09 14:12:23 +01001838 InlineCacheMap(std::less<uint16_t>(), allocator_->Adapter(kArenaAllocProfile)))->second);
Calin Juravlecc3171a2017-05-19 16:47:53 -07001839}
1840
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001841// Mark a method as executed at least once.
Calin Juravle1ad1e3f2017-09-19 18:20:37 -07001842bool ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, size_t index) {
1843 if (index >= num_method_ids) {
1844 LOG(ERROR) << "Invalid method index " << index << ". num_method_ids=" << num_method_ids;
1845 return false;
1846 }
1847
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001848 if ((flags & MethodHotness::kFlagStartup) != 0) {
1849 method_bitmap.StoreBit(MethodBitIndex(/*startup*/ true, index), /*value*/ true);
1850 }
1851 if ((flags & MethodHotness::kFlagPostStartup) != 0) {
1852 method_bitmap.StoreBit(MethodBitIndex(/*startup*/ false, index), /*value*/ true);
1853 }
1854 if ((flags & MethodHotness::kFlagHot) != 0) {
1855 method_map.FindOrAdd(
1856 index,
Vladimir Marko69d310e2017-10-09 14:12:23 +01001857 InlineCacheMap(std::less<uint16_t>(), allocator_->Adapter(kArenaAllocProfile)));
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001858 }
Calin Juravle1ad1e3f2017-09-19 18:20:37 -07001859 return true;
Mathieu Chartiere46f3a82017-06-19 19:54:12 -07001860}
1861
1862ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::DexFileData::GetHotnessInfo(
1863 uint32_t dex_method_index) const {
1864 MethodHotness ret;
1865 if (method_bitmap.LoadBit(MethodBitIndex(/*startup*/ true, dex_method_index))) {
1866 ret.AddFlag(MethodHotness::kFlagStartup);
1867 }
1868 if (method_bitmap.LoadBit(MethodBitIndex(/*startup*/ false, dex_method_index))) {
1869 ret.AddFlag(MethodHotness::kFlagPostStartup);
1870 }
1871 auto it = method_map.find(dex_method_index);
1872 if (it != method_map.end()) {
1873 ret.SetInlineCacheMap(&it->second);
1874 ret.AddFlag(MethodHotness::kFlagHot);
1875 }
1876 return ret;
1877}
1878
Calin Juravlecc3171a2017-05-19 16:47:53 -07001879ProfileCompilationInfo::DexPcData*
1880ProfileCompilationInfo::FindOrAddDexPc(InlineCacheMap* inline_cache, uint32_t dex_pc) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001881 return &(inline_cache->FindOrAdd(dex_pc, DexPcData(&allocator_))->second);
Calin Juravlecc3171a2017-05-19 16:47:53 -07001882}
1883
Mathieu Chartier4f342b02017-07-21 17:12:39 -07001884std::unordered_set<std::string> ProfileCompilationInfo::GetClassDescriptors(
1885 const std::vector<const DexFile*>& dex_files) {
1886 std::unordered_set<std::string> ret;
1887 for (const DexFile* dex_file : dex_files) {
1888 const DexFileData* data = FindDexData(dex_file);
1889 if (data != nullptr) {
1890 for (dex::TypeIndex type_idx : data->class_set) {
1891 if (!dex_file->IsTypeIndexValid(type_idx)) {
1892 // Something went bad. The profile is probably corrupted. Abort and return an emtpy set.
1893 LOG(WARNING) << "Corrupted profile: invalid type index "
1894 << type_idx.index_ << " in dex " << dex_file->GetLocation();
1895 return std::unordered_set<std::string>();
1896 }
1897 const DexFile::TypeId& type_id = dex_file->GetTypeId(type_idx);
1898 ret.insert(dex_file->GetTypeDescriptor(type_id));
1899 }
1900 } else {
1901 VLOG(compiler) << "Failed to find profile data for " << dex_file->GetLocation();
1902 }
1903 }
1904 return ret;
1905}
1906
Calin Juravle31f2c152015-10-23 17:56:15 +01001907} // namespace art