offline_profiling_info.cc revision 35c4e0b2ad573e820d6e9d461a571af300611d36
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(const std::string& filename,
52                                               const std::vector<ArtMethod*>& methods) {
53  if (methods.empty()) {
54    VLOG(profiler) << "No info to save to " << filename;
55    return true;
56  }
57
58  ScopedFlock flock;
59  std::string error;
60  if (!flock.Init(filename.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC, /* block */ false, &error)) {
61    LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
62    return false;
63  }
64
65  int fd = flock.GetFile()->Fd();
66
67  ProfileCompilationInfo info;
68  if (!info.Load(fd)) {
69    LOG(WARNING) << "Could not load previous profile data from file " << filename;
70    return false;
71  }
72  {
73    ScopedObjectAccess soa(Thread::Current());
74    for (auto it = methods.begin(); it != methods.end(); it++) {
75      const DexFile* dex_file = (*it)->GetDexFile();
76      if (!info.AddData(GetProfileDexFileKey(dex_file->GetLocation()),
77                        dex_file->GetLocationChecksum(),
78                        (*it)->GetDexMethodIndex())) {
79        return false;
80      }
81    }
82  }
83
84  if (!flock.GetFile()->ClearContent()) {
85    PLOG(WARNING) << "Could not clear profile file: " << filename;
86    return false;
87  }
88
89  // This doesn't need locking because we are trying to lock the file for exclusive
90  // access and fail immediately if we can't.
91  bool result = info.Save(fd);
92  if (result) {
93    VLOG(profiler) << "Successfully saved profile info to " << filename
94        << " Size: " << GetFileSizeBytes(filename);
95  } else {
96    VLOG(profiler) << "Failed to save profile info to " << filename;
97  }
98  return result;
99}
100
101static bool WriteToFile(int fd, const std::ostringstream& os) {
102  std::string data(os.str());
103  const char *p = data.c_str();
104  size_t length = data.length();
105  do {
106    int n = TEMP_FAILURE_RETRY(write(fd, p, length));
107    if (n < 0) {
108      PLOG(WARNING) << "Failed to write to descriptor: " << fd;
109      return false;
110    }
111    p += n;
112    length -= n;
113  } while (length > 0);
114  return true;
115}
116
117static constexpr const char kFieldSeparator = ',';
118static constexpr const char kLineSeparator = '\n';
119
120/**
121 * Serialization format:
122 *    dex_location1,dex_location_checksum1,method_id11,method_id12...
123 *    dex_location2,dex_location_checksum2,method_id21,method_id22...
124 * e.g.
125 *    app.apk,131232145,11,23,454,54
126 *    app.apk:classes5.dex,218490184,39,13,49,1
127 **/
128bool ProfileCompilationInfo::Save(uint32_t fd) {
129  DCHECK_GE(fd, 0u);
130  // TODO(calin): Profile this and see how much memory it takes. If too much,
131  // write to file directly.
132  std::ostringstream os;
133  for (const auto& it : info_) {
134    const std::string& dex_location = it.first;
135    const DexFileData& dex_data = it.second;
136
137    os << dex_location << kFieldSeparator << dex_data.checksum;
138    for (auto method_it : dex_data.method_set) {
139      os << kFieldSeparator << method_it;
140    }
141    os << kLineSeparator;
142  }
143
144  return WriteToFile(fd, os);
145}
146
147// TODO(calin): This a duplicate of Utils::Split fixing the case where the first character
148// is the separator. Merge the fix into Utils::Split once verified that it doesn't break its users.
149static void SplitString(const std::string& s, char separator, std::vector<std::string>* result) {
150  const char* p = s.data();
151  const char* end = p + s.size();
152  // Check if the first character is the separator.
153  if (p != end && *p ==separator) {
154    result->push_back("");
155    ++p;
156  }
157  // Process the rest of the characters.
158  while (p != end) {
159    if (*p == separator) {
160      ++p;
161    } else {
162      const char* start = p;
163      while (++p != end && *p != separator) {
164        // Skip to the next occurrence of the separator.
165      }
166      result->push_back(std::string(start, p - start));
167    }
168  }
169}
170
171bool ProfileCompilationInfo::AddData(const std::string& dex_location,
172                                     uint32_t checksum,
173                                     uint16_t method_idx) {
174  auto info_it = info_.find(dex_location);
175  if (info_it == info_.end()) {
176    info_it = info_.Put(dex_location, DexFileData(checksum));
177  }
178  if (info_it->second.checksum != checksum) {
179    LOG(WARNING) << "Checksum mismatch for dex " << dex_location;
180    return false;
181  }
182  info_it->second.method_set.insert(method_idx);
183  return true;
184}
185
186bool ProfileCompilationInfo::ProcessLine(const std::string& line) {
187  std::vector<std::string> parts;
188  SplitString(line, kFieldSeparator, &parts);
189  if (parts.size() < 3) {
190    LOG(WARNING) << "Invalid line: " << line;
191    return false;
192  }
193
194  const std::string& dex_location = parts[0];
195  uint32_t checksum;
196  if (!ParseInt(parts[1].c_str(), &checksum)) {
197    return false;
198  }
199
200  for (size_t i = 2; i < parts.size(); i++) {
201    uint32_t method_idx;
202    if (!ParseInt(parts[i].c_str(), &method_idx)) {
203      LOG(WARNING) << "Cannot parse method_idx " << parts[i];
204      return false;
205    }
206    if (!AddData(dex_location, checksum, method_idx)) {
207      return false;
208    }
209  }
210  return true;
211}
212
213// Parses the buffer (of length n) starting from start_from and identify new lines
214// based on kLineSeparator marker.
215// Returns the first position after kLineSeparator in the buffer (starting from start_from),
216// or -1 if the marker doesn't appear.
217// The processed characters are appended to the given line.
218static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& line) {
219  if (start_from >= n) {
220    return -1;
221  }
222  int new_line_pos = -1;
223  for (int i = start_from; i < n; i++) {
224    if (buffer[i] == kLineSeparator) {
225      new_line_pos = i;
226      break;
227    }
228  }
229  int append_limit = new_line_pos == -1 ? n : new_line_pos;
230  line.append(buffer + start_from, append_limit - start_from);
231  // Jump over kLineSeparator and return the position of the next character.
232  return new_line_pos == -1 ? new_line_pos : new_line_pos + 1;
233}
234
235bool ProfileCompilationInfo::Load(uint32_t fd) {
236  DCHECK_GE(fd, 0u);
237
238  std::string current_line;
239  const int kBufferSize = 1024;
240  char buffer[kBufferSize];
241
242  while (true) {
243    int n = TEMP_FAILURE_RETRY(read(fd, buffer, kBufferSize));
244    if (n < 0) {
245      PLOG(WARNING) << "Error when reading profile file";
246      return false;
247    } else if (n == 0) {
248      break;
249    }
250    // Detect the new lines from the buffer. If we manage to complete a line,
251    // process it. Otherwise append to the current line.
252    int current_start_pos = 0;
253    while (current_start_pos < n) {
254      current_start_pos = GetLineFromBuffer(buffer, n, current_start_pos, current_line);
255      if (current_start_pos == -1) {
256        break;
257      }
258      if (!ProcessLine(current_line)) {
259        return false;
260      }
261      // Reset the current line (we just processed it).
262      current_line.clear();
263    }
264  }
265  return true;
266}
267
268bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) {
269  for (const auto& other_it : other.info_) {
270    const std::string& other_dex_location = other_it.first;
271    const DexFileData& other_dex_data = other_it.second;
272
273    auto info_it = info_.find(other_dex_location);
274    if (info_it == info_.end()) {
275      info_it = info_.Put(other_dex_location, DexFileData(other_dex_data.checksum));
276    }
277    if (info_it->second.checksum != other_dex_data.checksum) {
278      LOG(WARNING) << "Checksum mismatch for dex " << other_dex_location;
279      return false;
280    }
281    info_it->second.method_set.insert(other_dex_data.method_set.begin(),
282                                      other_dex_data.method_set.end());
283  }
284  return true;
285}
286
287bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const {
288  auto info_it = info_.find(GetProfileDexFileKey(method_ref.dex_file->GetLocation()));
289  if (info_it != info_.end()) {
290    if (method_ref.dex_file->GetLocationChecksum() != info_it->second.checksum) {
291      return false;
292    }
293    const std::set<uint16_t>& methods = info_it->second.method_set;
294    return methods.find(method_ref.dex_method_index) != methods.end();
295  }
296  return false;
297}
298
299uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
300  uint32_t total = 0;
301  for (const auto& it : info_) {
302    total += it.second.method_set.size();
303  }
304  return total;
305}
306
307std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
308                                             bool print_full_dex_location) const {
309  std::ostringstream os;
310  if (info_.empty()) {
311    return "ProfileInfo: empty";
312  }
313
314  os << "ProfileInfo:";
315
316  const std::string kFirstDexFileKeySubstitute = ":classes.dex";
317  for (const auto& it : info_) {
318    os << "\n";
319    const std::string& location = it.first;
320    const DexFileData& dex_data = it.second;
321    if (print_full_dex_location) {
322      os << location;
323    } else {
324      // Replace the (empty) multidex suffix of the first key with a substitute for easier reading.
325      std::string multidex_suffix = DexFile::GetMultiDexSuffix(location);
326      os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
327    }
328    for (const auto method_it : dex_data.method_set) {
329      if (dex_files != nullptr) {
330        const DexFile* dex_file = nullptr;
331        for (size_t i = 0; i < dex_files->size(); i++) {
332          if (location == (*dex_files)[i]->GetLocation()) {
333            dex_file = (*dex_files)[i];
334          }
335        }
336        if (dex_file != nullptr) {
337          os << "\n  " << PrettyMethod(method_it, *dex_file, true);
338        }
339      }
340      os << "\n  " << method_it;
341    }
342  }
343  return os.str();
344}
345
346bool ProfileCompilationInfo::Equals(ProfileCompilationInfo& other) {
347  return info_.Equals(other.info_);
348}
349
350}  // namespace art
351