blob: 67c9b5f679a6b8286048f22b028d1134abf18a43 [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
17#include "offline_profiling_info.h"
18
19#include <fstream>
Calin Juravle4d77b6a2015-12-01 18:38:09 +000020#include <vector>
Calin Juravle31f2c152015-10-23 17:56:15 +010021#include <sys/file.h>
22#include <sys/stat.h>
23#include <sys/uio.h>
24
25#include "art_method-inl.h"
26#include "base/mutex.h"
Calin Juravle877fd962016-01-05 14:29:29 +000027#include "base/scoped_flock.h"
Calin Juravle66f55232015-12-08 15:09:10 +000028#include "base/stl_util.h"
Calin Juravle877fd962016-01-05 14:29:29 +000029#include "base/unix_file/fd_file.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010030#include "jit/profiling_info.h"
Calin Juravle877fd962016-01-05 14:29:29 +000031#include "os.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010032#include "safe_map.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010033
34namespace art {
35
Calin Juravle34900cc2016-02-05 16:19:19 +000036// Transform the actual dex location into relative paths.
37// Note: this is OK because we don't store profiles of different apps into the same file.
38// Apps with split apks don't cause trouble because each split has a different name and will not
39// collide with other entries.
Calin Juravle31708b72016-02-05 19:44:05 +000040std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_location) {
Calin Juravle34900cc2016-02-05 16:19:19 +000041 DCHECK(!dex_location.empty());
42 size_t last_sep_index = dex_location.find_last_of('/');
43 if (last_sep_index == std::string::npos) {
44 return dex_location;
45 } else {
Calin Juravle31708b72016-02-05 19:44:05 +000046 DCHECK(last_sep_index < dex_location.size());
47 return dex_location.substr(last_sep_index + 1);
Calin Juravle34900cc2016-02-05 16:19:19 +000048 }
49}
50
Mathieu Chartierc5dd3192015-12-09 16:38:30 -080051bool ProfileCompilationInfo::SaveProfilingInfo(
52 const std::string& filename,
53 const std::vector<ArtMethod*>& methods,
54 const std::set<DexCacheResolvedClasses>& resolved_classes) {
55 if (methods.empty() && resolved_classes.empty()) {
Calin Juravle31f2c152015-10-23 17:56:15 +010056 VLOG(profiler) << "No info to save to " << filename;
Calin Juravle998c2162015-12-21 15:39:33 +020057 return true;
Calin Juravle31f2c152015-10-23 17:56:15 +010058 }
59
Calin Juravle877fd962016-01-05 14:29:29 +000060 ScopedFlock flock;
61 std::string error;
62 if (!flock.Init(filename.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC, /* block */ false, &error)) {
63 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
64 return false;
65 }
66
67 int fd = flock.GetFile()->Fd();
68
Calin Juravle998c2162015-12-21 15:39:33 +020069 ProfileCompilationInfo info;
Calin Juravle877fd962016-01-05 14:29:29 +000070 if (!info.Load(fd)) {
Calin Juravle998c2162015-12-21 15:39:33 +020071 LOG(WARNING) << "Could not load previous profile data from file " << filename;
72 return false;
73 }
Calin Juravle31f2c152015-10-23 17:56:15 +010074 {
75 ScopedObjectAccess soa(Thread::Current());
Mathieu Chartierc5dd3192015-12-09 16:38:30 -080076 for (ArtMethod* method : methods) {
77 const DexFile* dex_file = method->GetDexFile();
78 if (!info.AddMethodIndex(GetProfileDexFileKey(dex_file->GetLocation()),
79 dex_file->GetLocationChecksum(),
80 method->GetDexMethodIndex())) {
Calin Juravle998c2162015-12-21 15:39:33 +020081 return false;
82 }
Calin Juravle31f2c152015-10-23 17:56:15 +010083 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -080084 for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
85 info.AddResolvedClasses(dex_cache);
86 }
Calin Juravle31f2c152015-10-23 17:56:15 +010087 }
88
Calin Juravle877fd962016-01-05 14:29:29 +000089 if (!flock.GetFile()->ClearContent()) {
90 PLOG(WARNING) << "Could not clear profile file: " << filename;
91 return false;
92 }
93
Calin Juravle31f2c152015-10-23 17:56:15 +010094 // This doesn't need locking because we are trying to lock the file for exclusive
95 // access and fail immediately if we can't.
Calin Juravle877fd962016-01-05 14:29:29 +000096 bool result = info.Save(fd);
Calin Juravle998c2162015-12-21 15:39:33 +020097 if (result) {
Calin Juravle4d77b6a2015-12-01 18:38:09 +000098 VLOG(profiler) << "Successfully saved profile info to " << filename
99 << " Size: " << GetFileSizeBytes(filename);
Calin Juravle998c2162015-12-21 15:39:33 +0200100 } else {
101 VLOG(profiler) << "Failed to save profile info to " << filename;
Calin Juravle31f2c152015-10-23 17:56:15 +0100102 }
Calin Juravle998c2162015-12-21 15:39:33 +0200103 return result;
Calin Juravle31f2c152015-10-23 17:56:15 +0100104}
105
Calin Juravle877fd962016-01-05 14:29:29 +0000106static bool WriteToFile(int fd, const std::ostringstream& os) {
Calin Juravle31f2c152015-10-23 17:56:15 +0100107 std::string data(os.str());
108 const char *p = data.c_str();
109 size_t length = data.length();
110 do {
Calin Juravle877fd962016-01-05 14:29:29 +0000111 int n = TEMP_FAILURE_RETRY(write(fd, p, length));
112 if (n < 0) {
113 PLOG(WARNING) << "Failed to write to descriptor: " << fd;
114 return false;
115 }
Calin Juravle31f2c152015-10-23 17:56:15 +0100116 p += n;
117 length -= n;
118 } while (length > 0);
Calin Juravle877fd962016-01-05 14:29:29 +0000119 return true;
Calin Juravle31f2c152015-10-23 17:56:15 +0100120}
121
Calin Juravle226501b2015-12-11 14:41:31 +0000122static constexpr const char kFieldSeparator = ',';
123static constexpr const char kLineSeparator = '\n';
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800124static constexpr const char* kClassesMarker = "classes";
Calin Juravle31f2c152015-10-23 17:56:15 +0100125
126/**
127 * Serialization format:
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800128 * dex_location1,dex_location_checksum1,method_id11,method_id12...,classes,class_id1,class_id2...
129 * dex_location2,dex_location_checksum2,method_id21,method_id22...,classes,class_id1,class_id2...
Calin Juravle31f2c152015-10-23 17:56:15 +0100130 * e.g.
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800131 * app.apk,131232145,11,23,454,54,classes,1,2,4,1234
Calin Juravle34900cc2016-02-05 16:19:19 +0000132 * app.apk:classes5.dex,218490184,39,13,49,1
Calin Juravle31f2c152015-10-23 17:56:15 +0100133 **/
Calin Juravle2e2db782016-02-23 12:00:03 +0000134bool ProfileCompilationInfo::Save(int fd) {
135 DCHECK_GE(fd, 0);
Calin Juravle31f2c152015-10-23 17:56:15 +0100136 // TODO(calin): Profile this and see how much memory it takes. If too much,
137 // write to file directly.
138 std::ostringstream os;
Calin Juravle998c2162015-12-21 15:39:33 +0200139 for (const auto& it : info_) {
140 const std::string& dex_location = it.first;
141 const DexFileData& dex_data = it.second;
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800142 if (dex_data.method_set.empty() && dex_data.class_set.empty()) {
143 continue;
144 }
Calin Juravle31f2c152015-10-23 17:56:15 +0100145
Calin Juravle998c2162015-12-21 15:39:33 +0200146 os << dex_location << kFieldSeparator << dex_data.checksum;
147 for (auto method_it : dex_data.method_set) {
Calin Juravle31f2c152015-10-23 17:56:15 +0100148 os << kFieldSeparator << method_it;
149 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800150 if (!dex_data.class_set.empty()) {
151 os << kFieldSeparator << kClassesMarker;
152 for (auto class_id : dex_data.class_set) {
153 os << kFieldSeparator << class_id;
154 }
155 }
Calin Juravle31f2c152015-10-23 17:56:15 +0100156 os << kLineSeparator;
157 }
158
Calin Juravle877fd962016-01-05 14:29:29 +0000159 return WriteToFile(fd, os);
Calin Juravle31f2c152015-10-23 17:56:15 +0100160}
Calin Juravle226501b2015-12-11 14:41:31 +0000161
162// TODO(calin): This a duplicate of Utils::Split fixing the case where the first character
163// is the separator. Merge the fix into Utils::Split once verified that it doesn't break its users.
164static void SplitString(const std::string& s, char separator, std::vector<std::string>* result) {
165 const char* p = s.data();
166 const char* end = p + s.size();
167 // Check if the first character is the separator.
168 if (p != end && *p ==separator) {
169 result->push_back("");
170 ++p;
171 }
172 // Process the rest of the characters.
173 while (p != end) {
174 if (*p == separator) {
175 ++p;
176 } else {
177 const char* start = p;
178 while (++p != end && *p != separator) {
179 // Skip to the next occurrence of the separator.
180 }
181 result->push_back(std::string(start, p - start));
182 }
183 }
184}
185
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800186ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData(
187 const std::string& dex_location,
188 uint32_t checksum) {
Calin Juravle998c2162015-12-21 15:39:33 +0200189 auto info_it = info_.find(dex_location);
190 if (info_it == info_.end()) {
191 info_it = info_.Put(dex_location, DexFileData(checksum));
192 }
193 if (info_it->second.checksum != checksum) {
194 LOG(WARNING) << "Checksum mismatch for dex " << dex_location;
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800195 return nullptr;
196 }
197 return &info_it->second;
198}
199
200bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& classes) {
201 const std::string dex_location = GetProfileDexFileKey(classes.GetDexLocation());
202 const uint32_t checksum = classes.GetLocationChecksum();
203 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum);
204 if (data == nullptr) {
Calin Juravle998c2162015-12-21 15:39:33 +0200205 return false;
206 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800207 data->class_set.insert(classes.GetClasses().begin(), classes.GetClasses().end());
208 return true;
209}
210
211bool ProfileCompilationInfo::AddMethodIndex(const std::string& dex_location,
212 uint32_t checksum,
213 uint16_t method_idx) {
214 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum);
215 if (data == nullptr) {
216 return false;
217 }
218 data->method_set.insert(method_idx);
219 return true;
220}
221
222bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location,
223 uint32_t checksum,
224 uint16_t class_idx) {
225 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum);
226 if (data == nullptr) {
227 return false;
228 }
229 data->class_set.insert(class_idx);
Calin Juravle998c2162015-12-21 15:39:33 +0200230 return true;
231}
232
233bool ProfileCompilationInfo::ProcessLine(const std::string& line) {
Calin Juravle226501b2015-12-11 14:41:31 +0000234 std::vector<std::string> parts;
235 SplitString(line, kFieldSeparator, &parts);
236 if (parts.size() < 3) {
237 LOG(WARNING) << "Invalid line: " << line;
238 return false;
239 }
240
Calin Juravle66f55232015-12-08 15:09:10 +0000241 const std::string& dex_location = parts[0];
Calin Juravle226501b2015-12-11 14:41:31 +0000242 uint32_t checksum;
243 if (!ParseInt(parts[1].c_str(), &checksum)) {
244 return false;
245 }
246
Calin Juravle226501b2015-12-11 14:41:31 +0000247 for (size_t i = 2; i < parts.size(); i++) {
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800248 if (parts[i] == kClassesMarker) {
249 ++i;
250 // All of the remaining idx are class def indexes.
251 for (++i; i < parts.size(); ++i) {
252 uint32_t class_def_idx;
253 if (!ParseInt(parts[i].c_str(), &class_def_idx)) {
254 LOG(WARNING) << "Cannot parse class_def_idx " << parts[i];
255 return false;
256 } else if (class_def_idx >= std::numeric_limits<uint16_t>::max()) {
257 LOG(WARNING) << "Class def idx " << class_def_idx << " is larger than uint16_t max";
258 return false;
259 }
260 if (!AddClassIndex(dex_location, checksum, class_def_idx)) {
261 return false;
262 }
263 }
264 break;
265 }
Calin Juravle226501b2015-12-11 14:41:31 +0000266 uint32_t method_idx;
267 if (!ParseInt(parts[i].c_str(), &method_idx)) {
268 LOG(WARNING) << "Cannot parse method_idx " << parts[i];
269 return false;
270 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800271 if (!AddMethodIndex(dex_location, checksum, method_idx)) {
Calin Juravle877fd962016-01-05 14:29:29 +0000272 return false;
273 }
Calin Juravle226501b2015-12-11 14:41:31 +0000274 }
275 return true;
276}
277
278// Parses the buffer (of length n) starting from start_from and identify new lines
279// based on kLineSeparator marker.
280// Returns the first position after kLineSeparator in the buffer (starting from start_from),
281// or -1 if the marker doesn't appear.
282// The processed characters are appended to the given line.
283static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& line) {
284 if (start_from >= n) {
285 return -1;
286 }
287 int new_line_pos = -1;
288 for (int i = start_from; i < n; i++) {
289 if (buffer[i] == kLineSeparator) {
290 new_line_pos = i;
291 break;
292 }
293 }
294 int append_limit = new_line_pos == -1 ? n : new_line_pos;
295 line.append(buffer + start_from, append_limit - start_from);
296 // Jump over kLineSeparator and return the position of the next character.
297 return new_line_pos == -1 ? new_line_pos : new_line_pos + 1;
298}
299
Calin Juravle2e2db782016-02-23 12:00:03 +0000300bool ProfileCompilationInfo::Load(int fd) {
301 DCHECK_GE(fd, 0);
Calin Juravle226501b2015-12-11 14:41:31 +0000302
303 std::string current_line;
304 const int kBufferSize = 1024;
305 char buffer[kBufferSize];
Calin Juravle226501b2015-12-11 14:41:31 +0000306
Calin Juravle877fd962016-01-05 14:29:29 +0000307 while (true) {
308 int n = TEMP_FAILURE_RETRY(read(fd, buffer, kBufferSize));
Calin Juravle226501b2015-12-11 14:41:31 +0000309 if (n < 0) {
Calin Juravle877fd962016-01-05 14:29:29 +0000310 PLOG(WARNING) << "Error when reading profile file";
311 return false;
Calin Juravle226501b2015-12-11 14:41:31 +0000312 } else if (n == 0) {
313 break;
314 }
315 // Detect the new lines from the buffer. If we manage to complete a line,
316 // process it. Otherwise append to the current line.
317 int current_start_pos = 0;
318 while (current_start_pos < n) {
319 current_start_pos = GetLineFromBuffer(buffer, n, current_start_pos, current_line);
320 if (current_start_pos == -1) {
321 break;
322 }
Calin Juravle998c2162015-12-21 15:39:33 +0200323 if (!ProcessLine(current_line)) {
Calin Juravle877fd962016-01-05 14:29:29 +0000324 return false;
Calin Juravle226501b2015-12-11 14:41:31 +0000325 }
326 // Reset the current line (we just processed it).
327 current_line.clear();
328 }
329 }
Calin Juravle877fd962016-01-05 14:29:29 +0000330 return true;
Calin Juravle998c2162015-12-21 15:39:33 +0200331}
332
333bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) {
334 for (const auto& other_it : other.info_) {
335 const std::string& other_dex_location = other_it.first;
336 const DexFileData& other_dex_data = other_it.second;
337
338 auto info_it = info_.find(other_dex_location);
339 if (info_it == info_.end()) {
340 info_it = info_.Put(other_dex_location, DexFileData(other_dex_data.checksum));
341 }
342 if (info_it->second.checksum != other_dex_data.checksum) {
343 LOG(WARNING) << "Checksum mismatch for dex " << other_dex_location;
344 return false;
345 }
346 info_it->second.method_set.insert(other_dex_data.method_set.begin(),
347 other_dex_data.method_set.end());
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800348 info_it->second.class_set.insert(other_dex_data.class_set.begin(),
349 other_dex_data.class_set.end());
Calin Juravle998c2162015-12-21 15:39:33 +0200350 }
351 return true;
Calin Juravle226501b2015-12-11 14:41:31 +0000352}
353
354bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const {
Calin Juravle34900cc2016-02-05 16:19:19 +0000355 auto info_it = info_.find(GetProfileDexFileKey(method_ref.dex_file->GetLocation()));
Calin Juravle226501b2015-12-11 14:41:31 +0000356 if (info_it != info_.end()) {
Calin Juravle998c2162015-12-21 15:39:33 +0200357 if (method_ref.dex_file->GetLocationChecksum() != info_it->second.checksum) {
358 return false;
Calin Juravle226501b2015-12-11 14:41:31 +0000359 }
Calin Juravle998c2162015-12-21 15:39:33 +0200360 const std::set<uint16_t>& methods = info_it->second.method_set;
361 return methods.find(method_ref.dex_method_index) != methods.end();
Calin Juravle226501b2015-12-11 14:41:31 +0000362 }
363 return false;
364}
365
Calin Juravle998c2162015-12-21 15:39:33 +0200366uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
367 uint32_t total = 0;
368 for (const auto& it : info_) {
369 total += it.second.method_set.size();
370 }
371 return total;
372}
373
374std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
375 bool print_full_dex_location) const {
Calin Juravle226501b2015-12-11 14:41:31 +0000376 std::ostringstream os;
377 if (info_.empty()) {
378 return "ProfileInfo: empty";
379 }
380
381 os << "ProfileInfo:";
382
Calin Juravle226501b2015-12-11 14:41:31 +0000383 const std::string kFirstDexFileKeySubstitute = ":classes.dex";
Calin Juravle998c2162015-12-21 15:39:33 +0200384 for (const auto& it : info_) {
Calin Juravle226501b2015-12-11 14:41:31 +0000385 os << "\n";
Calin Juravle998c2162015-12-21 15:39:33 +0200386 const std::string& location = it.first;
387 const DexFileData& dex_data = it.second;
Calin Juravle226501b2015-12-11 14:41:31 +0000388 if (print_full_dex_location) {
389 os << location;
390 } else {
391 // Replace the (empty) multidex suffix of the first key with a substitute for easier reading.
392 std::string multidex_suffix = DexFile::GetMultiDexSuffix(location);
393 os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
394 }
Calin Juravle998c2162015-12-21 15:39:33 +0200395 for (const auto method_it : dex_data.method_set) {
396 if (dex_files != nullptr) {
397 const DexFile* dex_file = nullptr;
398 for (size_t i = 0; i < dex_files->size(); i++) {
399 if (location == (*dex_files)[i]->GetLocation()) {
400 dex_file = (*dex_files)[i];
401 }
402 }
403 if (dex_file != nullptr) {
404 os << "\n " << PrettyMethod(method_it, *dex_file, true);
405 }
Calin Juravle226501b2015-12-11 14:41:31 +0000406 }
Calin Juravle998c2162015-12-21 15:39:33 +0200407 os << "\n " << method_it;
Calin Juravle226501b2015-12-11 14:41:31 +0000408 }
409 }
410 return os.str();
411}
412
Calin Juravle2e2db782016-02-23 12:00:03 +0000413bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) {
Calin Juravle877fd962016-01-05 14:29:29 +0000414 return info_.Equals(other.info_);
415}
416
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800417std::set<DexCacheResolvedClasses> ProfileCompilationInfo::GetResolvedClasses() const {
418 std::set<DexCacheResolvedClasses> ret;
419 for (auto&& pair : info_) {
420 const std::string& profile_key = pair.first;
421 const DexFileData& data = pair.second;
422 DexCacheResolvedClasses classes(profile_key, data.checksum);
423 classes.AddClasses(data.class_set.begin(), data.class_set.end());
424 ret.insert(classes);
425 }
426 return ret;
427}
428
Calin Juravle31f2c152015-10-23 17:56:15 +0100429} // namespace art