blob: f181ca3467af81c48c37ccbcba80d18a025550e8 [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"
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -080029#include "base/systrace.h"
Calin Juravle877fd962016-01-05 14:29:29 +000030#include "base/unix_file/fd_file.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010031#include "jit/profiling_info.h"
Calin Juravle877fd962016-01-05 14:29:29 +000032#include "os.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010033#include "safe_map.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010034
35namespace art {
36
Calin Juravle34900cc2016-02-05 16:19:19 +000037// Transform the actual dex location into relative paths.
38// Note: this is OK because we don't store profiles of different apps into the same file.
39// Apps with split apks don't cause trouble because each split has a different name and will not
40// collide with other entries.
Calin Juravle31708b72016-02-05 19:44:05 +000041std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_location) {
Calin Juravle34900cc2016-02-05 16:19:19 +000042 DCHECK(!dex_location.empty());
43 size_t last_sep_index = dex_location.find_last_of('/');
44 if (last_sep_index == std::string::npos) {
45 return dex_location;
46 } else {
Calin Juravle31708b72016-02-05 19:44:05 +000047 DCHECK(last_sep_index < dex_location.size());
48 return dex_location.substr(last_sep_index + 1);
Calin Juravle34900cc2016-02-05 16:19:19 +000049 }
50}
51
Mathieu Chartierc5dd3192015-12-09 16:38:30 -080052bool ProfileCompilationInfo::SaveProfilingInfo(
53 const std::string& filename,
54 const std::vector<ArtMethod*>& methods,
55 const std::set<DexCacheResolvedClasses>& resolved_classes) {
56 if (methods.empty() && resolved_classes.empty()) {
Calin Juravle31f2c152015-10-23 17:56:15 +010057 VLOG(profiler) << "No info to save to " << filename;
Calin Juravle998c2162015-12-21 15:39:33 +020058 return true;
Calin Juravle31f2c152015-10-23 17:56:15 +010059 }
60
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -080061 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravle877fd962016-01-05 14:29:29 +000062 ScopedFlock flock;
63 std::string error;
64 if (!flock.Init(filename.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC, /* block */ false, &error)) {
65 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
66 return false;
67 }
68
69 int fd = flock.GetFile()->Fd();
70
Calin Juravle998c2162015-12-21 15:39:33 +020071 ProfileCompilationInfo info;
Calin Juravle877fd962016-01-05 14:29:29 +000072 if (!info.Load(fd)) {
Calin Juravle998c2162015-12-21 15:39:33 +020073 LOG(WARNING) << "Could not load previous profile data from file " << filename;
74 return false;
75 }
Calin Juravle31f2c152015-10-23 17:56:15 +010076 {
77 ScopedObjectAccess soa(Thread::Current());
Mathieu Chartierc5dd3192015-12-09 16:38:30 -080078 for (ArtMethod* method : methods) {
79 const DexFile* dex_file = method->GetDexFile();
80 if (!info.AddMethodIndex(GetProfileDexFileKey(dex_file->GetLocation()),
81 dex_file->GetLocationChecksum(),
82 method->GetDexMethodIndex())) {
Calin Juravle998c2162015-12-21 15:39:33 +020083 return false;
84 }
Calin Juravle31f2c152015-10-23 17:56:15 +010085 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -080086 for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
87 info.AddResolvedClasses(dex_cache);
88 }
Calin Juravle31f2c152015-10-23 17:56:15 +010089 }
90
Calin Juravle877fd962016-01-05 14:29:29 +000091 if (!flock.GetFile()->ClearContent()) {
92 PLOG(WARNING) << "Could not clear profile file: " << filename;
93 return false;
94 }
95
Calin Juravle31f2c152015-10-23 17:56:15 +010096 // This doesn't need locking because we are trying to lock the file for exclusive
97 // access and fail immediately if we can't.
Calin Juravle877fd962016-01-05 14:29:29 +000098 bool result = info.Save(fd);
Calin Juravle998c2162015-12-21 15:39:33 +020099 if (result) {
Calin Juravle4d77b6a2015-12-01 18:38:09 +0000100 VLOG(profiler) << "Successfully saved profile info to " << filename
101 << " Size: " << GetFileSizeBytes(filename);
Calin Juravle998c2162015-12-21 15:39:33 +0200102 } else {
103 VLOG(profiler) << "Failed to save profile info to " << filename;
Calin Juravle31f2c152015-10-23 17:56:15 +0100104 }
Calin Juravle998c2162015-12-21 15:39:33 +0200105 return result;
Calin Juravle31f2c152015-10-23 17:56:15 +0100106}
107
Calin Juravle877fd962016-01-05 14:29:29 +0000108static bool WriteToFile(int fd, const std::ostringstream& os) {
Calin Juravle31f2c152015-10-23 17:56:15 +0100109 std::string data(os.str());
110 const char *p = data.c_str();
111 size_t length = data.length();
112 do {
Calin Juravle877fd962016-01-05 14:29:29 +0000113 int n = TEMP_FAILURE_RETRY(write(fd, p, length));
114 if (n < 0) {
115 PLOG(WARNING) << "Failed to write to descriptor: " << fd;
116 return false;
117 }
Calin Juravle31f2c152015-10-23 17:56:15 +0100118 p += n;
119 length -= n;
120 } while (length > 0);
Calin Juravle877fd962016-01-05 14:29:29 +0000121 return true;
Calin Juravle31f2c152015-10-23 17:56:15 +0100122}
123
Calin Juravle226501b2015-12-11 14:41:31 +0000124static constexpr const char kFieldSeparator = ',';
125static constexpr const char kLineSeparator = '\n';
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800126static constexpr const char* kClassesMarker = "classes";
Calin Juravle31f2c152015-10-23 17:56:15 +0100127
128/**
129 * Serialization format:
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800130 * dex_location1,dex_location_checksum1,method_id11,method_id12...,classes,class_id1,class_id2...
131 * dex_location2,dex_location_checksum2,method_id21,method_id22...,classes,class_id1,class_id2...
Calin Juravle31f2c152015-10-23 17:56:15 +0100132 * e.g.
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800133 * app.apk,131232145,11,23,454,54,classes,1,2,4,1234
Calin Juravle34900cc2016-02-05 16:19:19 +0000134 * app.apk:classes5.dex,218490184,39,13,49,1
Calin Juravle31f2c152015-10-23 17:56:15 +0100135 **/
Calin Juravle2e2db782016-02-23 12:00:03 +0000136bool ProfileCompilationInfo::Save(int fd) {
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -0800137 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravle2e2db782016-02-23 12:00:03 +0000138 DCHECK_GE(fd, 0);
Calin Juravle31f2c152015-10-23 17:56:15 +0100139 // TODO(calin): Profile this and see how much memory it takes. If too much,
140 // write to file directly.
141 std::ostringstream os;
Calin Juravle998c2162015-12-21 15:39:33 +0200142 for (const auto& it : info_) {
143 const std::string& dex_location = it.first;
144 const DexFileData& dex_data = it.second;
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800145 if (dex_data.method_set.empty() && dex_data.class_set.empty()) {
146 continue;
147 }
Calin Juravle31f2c152015-10-23 17:56:15 +0100148
Calin Juravle998c2162015-12-21 15:39:33 +0200149 os << dex_location << kFieldSeparator << dex_data.checksum;
150 for (auto method_it : dex_data.method_set) {
Calin Juravle31f2c152015-10-23 17:56:15 +0100151 os << kFieldSeparator << method_it;
152 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800153 if (!dex_data.class_set.empty()) {
154 os << kFieldSeparator << kClassesMarker;
155 for (auto class_id : dex_data.class_set) {
156 os << kFieldSeparator << class_id;
157 }
158 }
Calin Juravle31f2c152015-10-23 17:56:15 +0100159 os << kLineSeparator;
160 }
161
Calin Juravle877fd962016-01-05 14:29:29 +0000162 return WriteToFile(fd, os);
Calin Juravle31f2c152015-10-23 17:56:15 +0100163}
Calin Juravle226501b2015-12-11 14:41:31 +0000164
165// TODO(calin): This a duplicate of Utils::Split fixing the case where the first character
166// is the separator. Merge the fix into Utils::Split once verified that it doesn't break its users.
167static void SplitString(const std::string& s, char separator, std::vector<std::string>* result) {
168 const char* p = s.data();
169 const char* end = p + s.size();
170 // Check if the first character is the separator.
171 if (p != end && *p ==separator) {
172 result->push_back("");
173 ++p;
174 }
175 // Process the rest of the characters.
176 while (p != end) {
177 if (*p == separator) {
178 ++p;
179 } else {
180 const char* start = p;
181 while (++p != end && *p != separator) {
182 // Skip to the next occurrence of the separator.
183 }
184 result->push_back(std::string(start, p - start));
185 }
186 }
187}
188
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800189ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData(
190 const std::string& dex_location,
191 uint32_t checksum) {
Calin Juravle998c2162015-12-21 15:39:33 +0200192 auto info_it = info_.find(dex_location);
193 if (info_it == info_.end()) {
194 info_it = info_.Put(dex_location, DexFileData(checksum));
195 }
196 if (info_it->second.checksum != checksum) {
197 LOG(WARNING) << "Checksum mismatch for dex " << dex_location;
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800198 return nullptr;
199 }
200 return &info_it->second;
201}
202
203bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& classes) {
204 const std::string dex_location = GetProfileDexFileKey(classes.GetDexLocation());
205 const uint32_t checksum = classes.GetLocationChecksum();
206 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum);
207 if (data == nullptr) {
Calin Juravle998c2162015-12-21 15:39:33 +0200208 return false;
209 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800210 data->class_set.insert(classes.GetClasses().begin(), classes.GetClasses().end());
211 return true;
212}
213
214bool ProfileCompilationInfo::AddMethodIndex(const std::string& dex_location,
215 uint32_t checksum,
216 uint16_t method_idx) {
217 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum);
218 if (data == nullptr) {
219 return false;
220 }
221 data->method_set.insert(method_idx);
222 return true;
223}
224
225bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location,
226 uint32_t checksum,
227 uint16_t class_idx) {
228 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum);
229 if (data == nullptr) {
230 return false;
231 }
232 data->class_set.insert(class_idx);
Calin Juravle998c2162015-12-21 15:39:33 +0200233 return true;
234}
235
236bool ProfileCompilationInfo::ProcessLine(const std::string& line) {
Calin Juravle226501b2015-12-11 14:41:31 +0000237 std::vector<std::string> parts;
238 SplitString(line, kFieldSeparator, &parts);
239 if (parts.size() < 3) {
240 LOG(WARNING) << "Invalid line: " << line;
241 return false;
242 }
243
Calin Juravle66f55232015-12-08 15:09:10 +0000244 const std::string& dex_location = parts[0];
Calin Juravle226501b2015-12-11 14:41:31 +0000245 uint32_t checksum;
246 if (!ParseInt(parts[1].c_str(), &checksum)) {
247 return false;
248 }
249
Calin Juravle226501b2015-12-11 14:41:31 +0000250 for (size_t i = 2; i < parts.size(); i++) {
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800251 if (parts[i] == kClassesMarker) {
252 ++i;
253 // All of the remaining idx are class def indexes.
254 for (++i; i < parts.size(); ++i) {
255 uint32_t class_def_idx;
256 if (!ParseInt(parts[i].c_str(), &class_def_idx)) {
257 LOG(WARNING) << "Cannot parse class_def_idx " << parts[i];
258 return false;
259 } else if (class_def_idx >= std::numeric_limits<uint16_t>::max()) {
260 LOG(WARNING) << "Class def idx " << class_def_idx << " is larger than uint16_t max";
261 return false;
262 }
263 if (!AddClassIndex(dex_location, checksum, class_def_idx)) {
264 return false;
265 }
266 }
267 break;
268 }
Calin Juravle226501b2015-12-11 14:41:31 +0000269 uint32_t method_idx;
270 if (!ParseInt(parts[i].c_str(), &method_idx)) {
271 LOG(WARNING) << "Cannot parse method_idx " << parts[i];
272 return false;
273 }
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800274 if (!AddMethodIndex(dex_location, checksum, method_idx)) {
Calin Juravle877fd962016-01-05 14:29:29 +0000275 return false;
276 }
Calin Juravle226501b2015-12-11 14:41:31 +0000277 }
278 return true;
279}
280
281// Parses the buffer (of length n) starting from start_from and identify new lines
282// based on kLineSeparator marker.
283// Returns the first position after kLineSeparator in the buffer (starting from start_from),
284// or -1 if the marker doesn't appear.
285// The processed characters are appended to the given line.
286static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& line) {
287 if (start_from >= n) {
288 return -1;
289 }
290 int new_line_pos = -1;
291 for (int i = start_from; i < n; i++) {
292 if (buffer[i] == kLineSeparator) {
293 new_line_pos = i;
294 break;
295 }
296 }
297 int append_limit = new_line_pos == -1 ? n : new_line_pos;
298 line.append(buffer + start_from, append_limit - start_from);
299 // Jump over kLineSeparator and return the position of the next character.
300 return new_line_pos == -1 ? new_line_pos : new_line_pos + 1;
301}
302
Calin Juravle2e2db782016-02-23 12:00:03 +0000303bool ProfileCompilationInfo::Load(int fd) {
Mathieu Chartier32ce2ad2016-03-04 14:58:03 -0800304 ScopedTrace trace(__PRETTY_FUNCTION__);
Calin Juravle2e2db782016-02-23 12:00:03 +0000305 DCHECK_GE(fd, 0);
Calin Juravle226501b2015-12-11 14:41:31 +0000306
307 std::string current_line;
308 const int kBufferSize = 1024;
309 char buffer[kBufferSize];
Calin Juravle226501b2015-12-11 14:41:31 +0000310
Calin Juravle877fd962016-01-05 14:29:29 +0000311 while (true) {
312 int n = TEMP_FAILURE_RETRY(read(fd, buffer, kBufferSize));
Calin Juravle226501b2015-12-11 14:41:31 +0000313 if (n < 0) {
Calin Juravle877fd962016-01-05 14:29:29 +0000314 PLOG(WARNING) << "Error when reading profile file";
315 return false;
Calin Juravle226501b2015-12-11 14:41:31 +0000316 } else if (n == 0) {
317 break;
318 }
319 // Detect the new lines from the buffer. If we manage to complete a line,
320 // process it. Otherwise append to the current line.
321 int current_start_pos = 0;
322 while (current_start_pos < n) {
323 current_start_pos = GetLineFromBuffer(buffer, n, current_start_pos, current_line);
324 if (current_start_pos == -1) {
325 break;
326 }
Calin Juravle998c2162015-12-21 15:39:33 +0200327 if (!ProcessLine(current_line)) {
Calin Juravle877fd962016-01-05 14:29:29 +0000328 return false;
Calin Juravle226501b2015-12-11 14:41:31 +0000329 }
330 // Reset the current line (we just processed it).
331 current_line.clear();
332 }
333 }
Calin Juravle877fd962016-01-05 14:29:29 +0000334 return true;
Calin Juravle998c2162015-12-21 15:39:33 +0200335}
336
337bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) {
338 for (const auto& other_it : other.info_) {
339 const std::string& other_dex_location = other_it.first;
340 const DexFileData& other_dex_data = other_it.second;
341
342 auto info_it = info_.find(other_dex_location);
343 if (info_it == info_.end()) {
344 info_it = info_.Put(other_dex_location, DexFileData(other_dex_data.checksum));
345 }
346 if (info_it->second.checksum != other_dex_data.checksum) {
347 LOG(WARNING) << "Checksum mismatch for dex " << other_dex_location;
348 return false;
349 }
350 info_it->second.method_set.insert(other_dex_data.method_set.begin(),
351 other_dex_data.method_set.end());
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800352 info_it->second.class_set.insert(other_dex_data.class_set.begin(),
353 other_dex_data.class_set.end());
Calin Juravle998c2162015-12-21 15:39:33 +0200354 }
355 return true;
Calin Juravle226501b2015-12-11 14:41:31 +0000356}
357
358bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const {
Calin Juravle34900cc2016-02-05 16:19:19 +0000359 auto info_it = info_.find(GetProfileDexFileKey(method_ref.dex_file->GetLocation()));
Calin Juravle226501b2015-12-11 14:41:31 +0000360 if (info_it != info_.end()) {
Calin Juravle998c2162015-12-21 15:39:33 +0200361 if (method_ref.dex_file->GetLocationChecksum() != info_it->second.checksum) {
362 return false;
Calin Juravle226501b2015-12-11 14:41:31 +0000363 }
Calin Juravle998c2162015-12-21 15:39:33 +0200364 const std::set<uint16_t>& methods = info_it->second.method_set;
365 return methods.find(method_ref.dex_method_index) != methods.end();
Calin Juravle226501b2015-12-11 14:41:31 +0000366 }
367 return false;
368}
369
Mathieu Chartiera8077802016-03-16 19:08:31 -0700370bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, uint16_t class_def_idx) const {
371 auto info_it = info_.find(GetProfileDexFileKey(dex_file.GetLocation()));
372 if (info_it != info_.end()) {
373 if (dex_file.GetLocationChecksum() != info_it->second.checksum) {
374 return false;
375 }
376 const std::set<uint16_t>& classes = info_it->second.class_set;
377 return classes.find(class_def_idx) != classes.end();
378 }
379 return false;
380}
381
Calin Juravle998c2162015-12-21 15:39:33 +0200382uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
383 uint32_t total = 0;
384 for (const auto& it : info_) {
385 total += it.second.method_set.size();
386 }
387 return total;
388}
389
390std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
391 bool print_full_dex_location) const {
Calin Juravle226501b2015-12-11 14:41:31 +0000392 std::ostringstream os;
393 if (info_.empty()) {
394 return "ProfileInfo: empty";
395 }
396
397 os << "ProfileInfo:";
398
Calin Juravle226501b2015-12-11 14:41:31 +0000399 const std::string kFirstDexFileKeySubstitute = ":classes.dex";
Calin Juravle998c2162015-12-21 15:39:33 +0200400 for (const auto& it : info_) {
Calin Juravle226501b2015-12-11 14:41:31 +0000401 os << "\n";
Calin Juravle998c2162015-12-21 15:39:33 +0200402 const std::string& location = it.first;
403 const DexFileData& dex_data = it.second;
Calin Juravle226501b2015-12-11 14:41:31 +0000404 if (print_full_dex_location) {
405 os << location;
406 } else {
407 // Replace the (empty) multidex suffix of the first key with a substitute for easier reading.
408 std::string multidex_suffix = DexFile::GetMultiDexSuffix(location);
409 os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
410 }
Calin Juravle998c2162015-12-21 15:39:33 +0200411 for (const auto method_it : dex_data.method_set) {
412 if (dex_files != nullptr) {
413 const DexFile* dex_file = nullptr;
414 for (size_t i = 0; i < dex_files->size(); i++) {
415 if (location == (*dex_files)[i]->GetLocation()) {
416 dex_file = (*dex_files)[i];
417 }
418 }
419 if (dex_file != nullptr) {
420 os << "\n " << PrettyMethod(method_it, *dex_file, true);
421 }
Calin Juravle226501b2015-12-11 14:41:31 +0000422 }
Calin Juravle998c2162015-12-21 15:39:33 +0200423 os << "\n " << method_it;
Calin Juravle226501b2015-12-11 14:41:31 +0000424 }
425 }
426 return os.str();
427}
428
Calin Juravle2e2db782016-02-23 12:00:03 +0000429bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) {
Calin Juravle877fd962016-01-05 14:29:29 +0000430 return info_.Equals(other.info_);
431}
432
Mathieu Chartierc5dd3192015-12-09 16:38:30 -0800433std::set<DexCacheResolvedClasses> ProfileCompilationInfo::GetResolvedClasses() const {
434 std::set<DexCacheResolvedClasses> ret;
435 for (auto&& pair : info_) {
436 const std::string& profile_key = pair.first;
437 const DexFileData& data = pair.second;
438 DexCacheResolvedClasses classes(profile_key, data.checksum);
439 classes.AddClasses(data.class_set.begin(), data.class_set.end());
440 ret.insert(classes);
441 }
442 return ret;
443}
444
Calin Juravle31f2c152015-10-23 17:56:15 +0100445} // namespace art