offline_profiling_info.cc revision 8913fc1a27df8cf3b37fd99e94d87f290591328e
1/*
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>
20#include <vector>
21#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"
27#include "base/scoped_flock.h"
28#include "base/stl_util.h"
29#include "base/unix_file/fd_file.h"
30#include "jit/profiling_info.h"
31#include "os.h"
32#include "safe_map.h"
33
34namespace art {
35
36// 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.
40std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_location) {
41  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 {
46    DCHECK(last_sep_index < dex_location.size());
47    return dex_location.substr(last_sep_index + 1);
48  }
49}
50
51bool 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()) {
56    VLOG(profiler) << "No info to save to " << filename;
57    return true;
58  }
59
60  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
69  ProfileCompilationInfo info;
70  if (!info.Load(fd)) {
71    LOG(WARNING) << "Could not load previous profile data from file " << filename;
72    return false;
73  }
74  {
75    ScopedObjectAccess soa(Thread::Current());
76    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())) {
81        return false;
82      }
83    }
84    for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
85      info.AddResolvedClasses(dex_cache);
86    }
87  }
88
89  if (!flock.GetFile()->ClearContent()) {
90    PLOG(WARNING) << "Could not clear profile file: " << filename;
91    return false;
92  }
93
94  // This doesn't need locking because we are trying to lock the file for exclusive
95  // access and fail immediately if we can't.
96  bool result = info.Save(fd);
97  if (result) {
98    VLOG(profiler) << "Successfully saved profile info to " << filename
99        << " Size: " << GetFileSizeBytes(filename);
100  } else {
101    VLOG(profiler) << "Failed to save profile info to " << filename;
102  }
103  return result;
104}
105
106static bool WriteToFile(int fd, const std::ostringstream& os) {
107  std::string data(os.str());
108  const char *p = data.c_str();
109  size_t length = data.length();
110  do {
111    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    }
116    p += n;
117    length -= n;
118  } while (length > 0);
119  return true;
120}
121
122static constexpr const char kFieldSeparator = ',';
123static constexpr const char kLineSeparator = '\n';
124static constexpr const char* kClassesMarker = "classes";
125
126/**
127 * Serialization format:
128 *    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...
130 * e.g.
131 *    app.apk,131232145,11,23,454,54,classes,1,2,4,1234
132 *    app.apk:classes5.dex,218490184,39,13,49,1
133 **/
134bool ProfileCompilationInfo::Save(int fd) {
135  DCHECK_GE(fd, 0);
136  // TODO(calin): Profile this and see how much memory it takes. If too much,
137  // write to file directly.
138  std::ostringstream os;
139  for (const auto& it : info_) {
140    const std::string& dex_location = it.first;
141    const DexFileData& dex_data = it.second;
142    if (dex_data.method_set.empty() && dex_data.class_set.empty()) {
143      continue;
144    }
145
146    os << dex_location << kFieldSeparator << dex_data.checksum;
147    for (auto method_it : dex_data.method_set) {
148      os << kFieldSeparator << method_it;
149    }
150    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    }
156    os << kLineSeparator;
157  }
158
159  return WriteToFile(fd, os);
160}
161
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
186ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData(
187    const std::string& dex_location,
188    uint32_t checksum) {
189  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;
195    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) {
205    return false;
206  }
207  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);
230  return true;
231}
232
233bool ProfileCompilationInfo::ProcessLine(const std::string& line) {
234  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
241  const std::string& dex_location = parts[0];
242  uint32_t checksum;
243  if (!ParseInt(parts[1].c_str(), &checksum)) {
244    return false;
245  }
246
247  for (size_t i = 2; i < parts.size(); i++) {
248    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    }
266    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    }
271    if (!AddMethodIndex(dex_location, checksum, method_idx)) {
272      return false;
273    }
274  }
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
300bool ProfileCompilationInfo::Load(int fd) {
301  DCHECK_GE(fd, 0);
302
303  std::string current_line;
304  const int kBufferSize = 1024;
305  char buffer[kBufferSize];
306
307  while (true) {
308    int n = TEMP_FAILURE_RETRY(read(fd, buffer, kBufferSize));
309    if (n < 0) {
310      PLOG(WARNING) << "Error when reading profile file";
311      return false;
312    } 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      }
323      if (!ProcessLine(current_line)) {
324        return false;
325      }
326      // Reset the current line (we just processed it).
327      current_line.clear();
328    }
329  }
330  return true;
331}
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());
348    info_it->second.class_set.insert(other_dex_data.class_set.begin(),
349                                     other_dex_data.class_set.end());
350  }
351  return true;
352}
353
354bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const {
355  auto info_it = info_.find(GetProfileDexFileKey(method_ref.dex_file->GetLocation()));
356  if (info_it != info_.end()) {
357    if (method_ref.dex_file->GetLocationChecksum() != info_it->second.checksum) {
358      return false;
359    }
360    const std::set<uint16_t>& methods = info_it->second.method_set;
361    return methods.find(method_ref.dex_method_index) != methods.end();
362  }
363  return false;
364}
365
366uint32_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 {
376  std::ostringstream os;
377  if (info_.empty()) {
378    return "ProfileInfo: empty";
379  }
380
381  os << "ProfileInfo:";
382
383  const std::string kFirstDexFileKeySubstitute = ":classes.dex";
384  for (const auto& it : info_) {
385    os << "\n";
386    const std::string& location = it.first;
387    const DexFileData& dex_data = it.second;
388    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    }
395    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        }
406      }
407      os << "\n  " << method_it;
408    }
409  }
410  return os.str();
411}
412
413bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) {
414  return info_.Equals(other.info_);
415}
416
417std::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
429}  // namespace art
430