blob: 5dc0e452340901e44f78ced2bb29773c66bf2da3 [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 Juravle66f55232015-12-08 15:09:10 +000027#include "base/stl_util.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010028#include "jit/profiling_info.h"
29#include "safe_map.h"
Calin Juravle31f2c152015-10-23 17:56:15 +010030
31namespace art {
32
Calin Juravle31f2c152015-10-23 17:56:15 +010033void OfflineProfilingInfo::SaveProfilingInfo(const std::string& filename,
Calin Juravle4d77b6a2015-12-01 18:38:09 +000034 const std::vector<ArtMethod*>& methods) {
Calin Juravle31f2c152015-10-23 17:56:15 +010035 if (methods.empty()) {
36 VLOG(profiler) << "No info to save to " << filename;
37 return;
38 }
39
40 DexFileToMethodsMap info;
41 {
42 ScopedObjectAccess soa(Thread::Current());
43 for (auto it = methods.begin(); it != methods.end(); it++) {
44 AddMethodInfo(*it, &info);
45 }
46 }
47
48 // This doesn't need locking because we are trying to lock the file for exclusive
49 // access and fail immediately if we can't.
50 if (Serialize(filename, info)) {
Calin Juravle4d77b6a2015-12-01 18:38:09 +000051 VLOG(profiler) << "Successfully saved profile info to " << filename
52 << " Size: " << GetFileSizeBytes(filename);
Calin Juravle31f2c152015-10-23 17:56:15 +010053 }
54}
55
Calin Juravle31f2c152015-10-23 17:56:15 +010056void OfflineProfilingInfo::AddMethodInfo(ArtMethod* method, DexFileToMethodsMap* info) {
57 DCHECK(method != nullptr);
58 const DexFile* dex_file = method->GetDexFile();
59
60 auto info_it = info->find(dex_file);
61 if (info_it == info->end()) {
62 info_it = info->Put(dex_file, std::set<uint32_t>());
63 }
64 info_it->second.insert(method->GetDexMethodIndex());
65}
66
Calin Juravle226501b2015-12-11 14:41:31 +000067enum OpenMode {
68 READ,
69 READ_WRITE
70};
71
72static int OpenFile(const std::string& filename, OpenMode open_mode) {
73 int fd = -1;
74 switch (open_mode) {
75 case READ:
76 fd = open(filename.c_str(), O_RDONLY);
77 break;
78 case READ_WRITE:
79 // TODO(calin) allow the shared uid of the app to access the file.
80 fd = open(filename.c_str(),
81 O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
82 S_IRUSR | S_IWUSR);
83 break;
84 }
85
Calin Juravle31f2c152015-10-23 17:56:15 +010086 if (fd < 0) {
87 PLOG(WARNING) << "Failed to open profile file " << filename;
88 return -1;
89 }
90
91 // Lock the file for exclusive access but don't wait if we can't lock it.
92 int err = flock(fd, LOCK_EX | LOCK_NB);
93 if (err < 0) {
94 PLOG(WARNING) << "Failed to lock profile file " << filename;
95 return -1;
96 }
Calin Juravle31f2c152015-10-23 17:56:15 +010097 return fd;
98}
99
100static bool CloseDescriptorForFile(int fd, const std::string& filename) {
101 // Now unlock the file, allowing another process in.
102 int err = flock(fd, LOCK_UN);
103 if (err < 0) {
104 PLOG(WARNING) << "Failed to unlock profile file " << filename;
105 return false;
106 }
107
108 // Done, close the file.
109 err = ::close(fd);
110 if (err < 0) {
111 PLOG(WARNING) << "Failed to close descriptor for profile file" << filename;
112 return false;
113 }
114
115 return true;
116}
117
118static void WriteToFile(int fd, const std::ostringstream& os) {
119 std::string data(os.str());
120 const char *p = data.c_str();
121 size_t length = data.length();
122 do {
123 int n = ::write(fd, p, length);
124 p += n;
125 length -= n;
126 } while (length > 0);
127}
128
Calin Juravle226501b2015-12-11 14:41:31 +0000129static constexpr const char kFieldSeparator = ',';
130static constexpr const char kLineSeparator = '\n';
Calin Juravle31f2c152015-10-23 17:56:15 +0100131
132/**
133 * Serialization format:
Calin Juravle66f55232015-12-08 15:09:10 +0000134 * dex_location1,dex_location_checksum1,method_id11,method_id12...
135 * dex_location2,dex_location_checksum2,method_id21,method_id22...
Calin Juravle31f2c152015-10-23 17:56:15 +0100136 * e.g.
Calin Juravle66f55232015-12-08 15:09:10 +0000137 * /system/priv-app/app/app.apk,131232145,11,23,454,54
138 * /system/priv-app/app/app.apk:classes5.dex,218490184,39,13,49,1
Calin Juravle31f2c152015-10-23 17:56:15 +0100139 **/
140bool OfflineProfilingInfo::Serialize(const std::string& filename,
141 const DexFileToMethodsMap& info) const {
Calin Juravle226501b2015-12-11 14:41:31 +0000142 int fd = OpenFile(filename, READ_WRITE);
Calin Juravle31f2c152015-10-23 17:56:15 +0100143 if (fd == -1) {
144 return false;
145 }
146
147 // TODO(calin): Merge with a previous existing profile.
148 // TODO(calin): Profile this and see how much memory it takes. If too much,
149 // write to file directly.
150 std::ostringstream os;
151 for (auto it : info) {
152 const DexFile* dex_file = it.first;
153 const std::set<uint32_t>& method_dex_ids = it.second;
154
Calin Juravle66f55232015-12-08 15:09:10 +0000155 os << dex_file->GetLocation()
Calin Juravle31f2c152015-10-23 17:56:15 +0100156 << kFieldSeparator
157 << dex_file->GetLocationChecksum();
158 for (auto method_it : method_dex_ids) {
159 os << kFieldSeparator << method_it;
160 }
161 os << kLineSeparator;
162 }
163
164 WriteToFile(fd, os);
165
166 return CloseDescriptorForFile(fd, filename);
167}
Calin Juravle226501b2015-12-11 14:41:31 +0000168
169// TODO(calin): This a duplicate of Utils::Split fixing the case where the first character
170// is the separator. Merge the fix into Utils::Split once verified that it doesn't break its users.
171static void SplitString(const std::string& s, char separator, std::vector<std::string>* result) {
172 const char* p = s.data();
173 const char* end = p + s.size();
174 // Check if the first character is the separator.
175 if (p != end && *p ==separator) {
176 result->push_back("");
177 ++p;
178 }
179 // Process the rest of the characters.
180 while (p != end) {
181 if (*p == separator) {
182 ++p;
183 } else {
184 const char* start = p;
185 while (++p != end && *p != separator) {
186 // Skip to the next occurrence of the separator.
187 }
188 result->push_back(std::string(start, p - start));
189 }
190 }
191}
192
193bool ProfileCompilationInfo::ProcessLine(const std::string& line,
194 const std::vector<const DexFile*>& dex_files) {
195 std::vector<std::string> parts;
196 SplitString(line, kFieldSeparator, &parts);
197 if (parts.size() < 3) {
198 LOG(WARNING) << "Invalid line: " << line;
199 return false;
200 }
201
Calin Juravle66f55232015-12-08 15:09:10 +0000202 const std::string& dex_location = parts[0];
Calin Juravle226501b2015-12-11 14:41:31 +0000203 uint32_t checksum;
204 if (!ParseInt(parts[1].c_str(), &checksum)) {
205 return false;
206 }
207
208 const DexFile* current_dex_file = nullptr;
209 for (auto dex_file : dex_files) {
Calin Juravle66f55232015-12-08 15:09:10 +0000210 if (dex_file->GetLocation() == dex_location) {
Calin Juravle226501b2015-12-11 14:41:31 +0000211 if (checksum != dex_file->GetLocationChecksum()) {
212 LOG(WARNING) << "Checksum mismatch for "
213 << dex_file->GetLocation() << " when parsing " << filename_;
214 return false;
215 }
216 current_dex_file = dex_file;
217 break;
218 }
219 }
220 if (current_dex_file == nullptr) {
221 return true;
222 }
223
224 for (size_t i = 2; i < parts.size(); i++) {
225 uint32_t method_idx;
226 if (!ParseInt(parts[i].c_str(), &method_idx)) {
227 LOG(WARNING) << "Cannot parse method_idx " << parts[i];
228 return false;
229 }
230 uint16_t class_idx = current_dex_file->GetMethodId(method_idx).class_idx_;
231 auto info_it = info_.find(current_dex_file);
232 if (info_it == info_.end()) {
233 info_it = info_.Put(current_dex_file, ClassToMethodsMap());
234 }
235 ClassToMethodsMap& class_map = info_it->second;
236 auto class_it = class_map.find(class_idx);
237 if (class_it == class_map.end()) {
238 class_it = class_map.Put(class_idx, std::set<uint32_t>());
239 }
240 class_it->second.insert(method_idx);
241 }
242 return true;
243}
244
245// Parses the buffer (of length n) starting from start_from and identify new lines
246// based on kLineSeparator marker.
247// Returns the first position after kLineSeparator in the buffer (starting from start_from),
248// or -1 if the marker doesn't appear.
249// The processed characters are appended to the given line.
250static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& line) {
251 if (start_from >= n) {
252 return -1;
253 }
254 int new_line_pos = -1;
255 for (int i = start_from; i < n; i++) {
256 if (buffer[i] == kLineSeparator) {
257 new_line_pos = i;
258 break;
259 }
260 }
261 int append_limit = new_line_pos == -1 ? n : new_line_pos;
262 line.append(buffer + start_from, append_limit - start_from);
263 // Jump over kLineSeparator and return the position of the next character.
264 return new_line_pos == -1 ? new_line_pos : new_line_pos + 1;
265}
266
267bool ProfileCompilationInfo::Load(const std::vector<const DexFile*>& dex_files) {
268 if (dex_files.empty()) {
269 return true;
270 }
271 if (kIsDebugBuild) {
Calin Juravle66f55232015-12-08 15:09:10 +0000272 // In debug builds verify that the locations are unique.
273 std::set<std::string> locations;
Calin Juravle226501b2015-12-11 14:41:31 +0000274 for (auto dex_file : dex_files) {
Calin Juravle66f55232015-12-08 15:09:10 +0000275 const std::string& location = dex_file->GetLocation();
276 DCHECK(locations.find(location) == locations.end())
Calin Juravle226501b2015-12-11 14:41:31 +0000277 << "DexFiles appear to belong to different apks."
Calin Juravle66f55232015-12-08 15:09:10 +0000278 << " There are multiple dex files with the same location: "
279 << location;
280 locations.insert(location);
Calin Juravle226501b2015-12-11 14:41:31 +0000281 }
282 }
283 info_.clear();
284
285 int fd = OpenFile(filename_, READ);
286 if (fd == -1) {
287 return false;
288 }
289
290 std::string current_line;
291 const int kBufferSize = 1024;
292 char buffer[kBufferSize];
293 bool success = true;
294
295 while (success) {
296 int n = read(fd, buffer, kBufferSize);
297 if (n < 0) {
298 PLOG(WARNING) << "Error when reading profile file " << filename_;
299 success = false;
300 break;
301 } else if (n == 0) {
302 break;
303 }
304 // Detect the new lines from the buffer. If we manage to complete a line,
305 // process it. Otherwise append to the current line.
306 int current_start_pos = 0;
307 while (current_start_pos < n) {
308 current_start_pos = GetLineFromBuffer(buffer, n, current_start_pos, current_line);
309 if (current_start_pos == -1) {
310 break;
311 }
312 if (!ProcessLine(current_line, dex_files)) {
313 success = false;
314 break;
315 }
316 // Reset the current line (we just processed it).
317 current_line.clear();
318 }
319 }
320 if (!success) {
321 info_.clear();
322 }
323 return CloseDescriptorForFile(fd, filename_) && success;
324}
325
326bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const {
327 auto info_it = info_.find(method_ref.dex_file);
328 if (info_it != info_.end()) {
329 uint16_t class_idx = method_ref.dex_file->GetMethodId(method_ref.dex_method_index).class_idx_;
330 const ClassToMethodsMap& class_map = info_it->second;
331 auto class_it = class_map.find(class_idx);
332 if (class_it != class_map.end()) {
333 const std::set<uint32_t>& methods = class_it->second;
334 return methods.find(method_ref.dex_method_index) != methods.end();
335 }
336 return false;
337 }
338 return false;
339}
340
341std::string ProfileCompilationInfo::DumpInfo(bool print_full_dex_location) const {
342 std::ostringstream os;
343 if (info_.empty()) {
344 return "ProfileInfo: empty";
345 }
346
347 os << "ProfileInfo:";
348
349 // Use an additional map to achieve a predefined order based on the dex locations.
350 SafeMap<const std::string, const DexFile*> dex_locations_map;
351 for (auto info_it : info_) {
352 dex_locations_map.Put(info_it.first->GetLocation(), info_it.first);
353 }
354
355 const std::string kFirstDexFileKeySubstitute = ":classes.dex";
356 for (auto dex_file_it : dex_locations_map) {
357 os << "\n";
358 const std::string& location = dex_file_it.first;
359 const DexFile* dex_file = dex_file_it.second;
360 if (print_full_dex_location) {
361 os << location;
362 } else {
363 // Replace the (empty) multidex suffix of the first key with a substitute for easier reading.
364 std::string multidex_suffix = DexFile::GetMultiDexSuffix(location);
365 os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
366 }
367 for (auto class_it : info_.find(dex_file)->second) {
368 for (auto method_it : class_it.second) {
369 os << "\n " << PrettyMethod(method_it, *dex_file, true);
370 }
371 }
372 }
373 return os.str();
374}
375
Calin Juravle31f2c152015-10-23 17:56:15 +0100376} // namespace art