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