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