offline_profiling_info.cc revision a079e3aa62cceb76c1c1811e6e09bcaf75e20289
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/systrace.h" 30#include "base/unix_file/fd_file.h" 31#include "jit/profiling_info.h" 32#include "os.h" 33#include "safe_map.h" 34 35namespace art { 36 37// Transform the actual dex location into relative paths. 38// Note: this is OK because we don't store profiles of different apps into the same file. 39// Apps with split apks don't cause trouble because each split has a different name and will not 40// collide with other entries. 41std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_location) { 42 DCHECK(!dex_location.empty()); 43 size_t last_sep_index = dex_location.find_last_of('/'); 44 if (last_sep_index == std::string::npos) { 45 return dex_location; 46 } else { 47 DCHECK(last_sep_index < dex_location.size()); 48 return dex_location.substr(last_sep_index + 1); 49 } 50} 51 52bool ProfileCompilationInfo::SaveProfilingInfo( 53 const std::string& filename, 54 const std::vector<ArtMethod*>& methods, 55 const std::set<DexCacheResolvedClasses>& resolved_classes, 56 uint64_t* bytes_written) { 57 if (methods.empty() && resolved_classes.empty()) { 58 VLOG(profiler) << "No info to save to " << filename; 59 if (bytes_written != nullptr) { 60 *bytes_written = 0; 61 } 62 return true; 63 } 64 65 ScopedTrace trace(__PRETTY_FUNCTION__); 66 ScopedFlock flock; 67 std::string error; 68 if (!flock.Init(filename.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC, /* block */ false, &error)) { 69 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error; 70 return false; 71 } 72 73 int fd = flock.GetFile()->Fd(); 74 75 ProfileCompilationInfo info; 76 if (!info.Load(fd)) { 77 LOG(WARNING) << "Could not load previous profile data from file " << filename; 78 return false; 79 } 80 { 81 ScopedObjectAccess soa(Thread::Current()); 82 for (ArtMethod* method : methods) { 83 const DexFile* dex_file = method->GetDexFile(); 84 if (!info.AddMethodIndex(GetProfileDexFileKey(dex_file->GetLocation()), 85 dex_file->GetLocationChecksum(), 86 method->GetDexMethodIndex())) { 87 return false; 88 } 89 } 90 for (const DexCacheResolvedClasses& dex_cache : resolved_classes) { 91 info.AddResolvedClasses(dex_cache); 92 } 93 } 94 95 if (!flock.GetFile()->ClearContent()) { 96 PLOG(WARNING) << "Could not clear profile file: " << filename; 97 return false; 98 } 99 100 // This doesn't need locking because we are trying to lock the file for exclusive 101 // access and fail immediately if we can't. 102 bool result = info.Save(fd); 103 if (result) { 104 VLOG(profiler) << "Successfully saved profile info to " << filename 105 << " Size: " << GetFileSizeBytes(filename); 106 if (bytes_written != nullptr) { 107 *bytes_written = GetFileSizeBytes(filename); 108 } 109 } else { 110 VLOG(profiler) << "Failed to save profile info to " << filename; 111 } 112 return result; 113} 114 115static bool WriteToFile(int fd, const std::ostringstream& os) { 116 std::string data(os.str()); 117 const char *p = data.c_str(); 118 size_t length = data.length(); 119 do { 120 int n = TEMP_FAILURE_RETRY(write(fd, p, length)); 121 if (n < 0) { 122 PLOG(WARNING) << "Failed to write to descriptor: " << fd; 123 return false; 124 } 125 p += n; 126 length -= n; 127 } while (length > 0); 128 return true; 129} 130 131static constexpr const char kFieldSeparator = ','; 132static constexpr const char kLineSeparator = '\n'; 133static constexpr const char* kClassesMarker = "classes"; 134 135/** 136 * Serialization format: 137 * dex_location1,dex_location_checksum1,method_id11,method_id12...,classes,class_id1,class_id2... 138 * dex_location2,dex_location_checksum2,method_id21,method_id22...,classes,class_id1,class_id2... 139 * e.g. 140 * app.apk,131232145,11,23,454,54,classes,1,2,4,1234 141 * app.apk:classes5.dex,218490184,39,13,49,1 142 **/ 143bool ProfileCompilationInfo::Save(int fd) { 144 ScopedTrace trace(__PRETTY_FUNCTION__); 145 DCHECK_GE(fd, 0); 146 // TODO(calin): Profile this and see how much memory it takes. If too much, 147 // write to file directly. 148 std::ostringstream os; 149 for (const auto& it : info_) { 150 const std::string& dex_location = it.first; 151 const DexFileData& dex_data = it.second; 152 if (dex_data.method_set.empty() && dex_data.class_set.empty()) { 153 continue; 154 } 155 156 os << dex_location << kFieldSeparator << dex_data.checksum; 157 for (auto method_it : dex_data.method_set) { 158 os << kFieldSeparator << method_it; 159 } 160 if (!dex_data.class_set.empty()) { 161 os << kFieldSeparator << kClassesMarker; 162 for (auto class_id : dex_data.class_set) { 163 os << kFieldSeparator << class_id; 164 } 165 } 166 os << kLineSeparator; 167 } 168 169 return WriteToFile(fd, os); 170} 171 172// TODO(calin): This a duplicate of Utils::Split fixing the case where the first character 173// is the separator. Merge the fix into Utils::Split once verified that it doesn't break its users. 174static void SplitString(const std::string& s, char separator, std::vector<std::string>* result) { 175 const char* p = s.data(); 176 const char* end = p + s.size(); 177 // Check if the first character is the separator. 178 if (p != end && *p ==separator) { 179 result->push_back(""); 180 ++p; 181 } 182 // Process the rest of the characters. 183 while (p != end) { 184 if (*p == separator) { 185 ++p; 186 } else { 187 const char* start = p; 188 while (++p != end && *p != separator) { 189 // Skip to the next occurrence of the separator. 190 } 191 result->push_back(std::string(start, p - start)); 192 } 193 } 194} 195 196ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData( 197 const std::string& dex_location, 198 uint32_t checksum) { 199 auto info_it = info_.find(dex_location); 200 if (info_it == info_.end()) { 201 info_it = info_.Put(dex_location, DexFileData(checksum)); 202 } 203 if (info_it->second.checksum != checksum) { 204 LOG(WARNING) << "Checksum mismatch for dex " << dex_location; 205 return nullptr; 206 } 207 return &info_it->second; 208} 209 210bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& classes) { 211 const std::string dex_location = GetProfileDexFileKey(classes.GetDexLocation()); 212 const uint32_t checksum = classes.GetLocationChecksum(); 213 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum); 214 if (data == nullptr) { 215 return false; 216 } 217 data->class_set.insert(classes.GetClasses().begin(), classes.GetClasses().end()); 218 return true; 219} 220 221bool ProfileCompilationInfo::AddMethodIndex(const std::string& dex_location, 222 uint32_t checksum, 223 uint16_t method_idx) { 224 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum); 225 if (data == nullptr) { 226 return false; 227 } 228 data->method_set.insert(method_idx); 229 return true; 230} 231 232bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location, 233 uint32_t checksum, 234 uint16_t class_idx) { 235 DexFileData* const data = GetOrAddDexFileData(dex_location, checksum); 236 if (data == nullptr) { 237 return false; 238 } 239 data->class_set.insert(class_idx); 240 return true; 241} 242 243bool ProfileCompilationInfo::ProcessLine(const std::string& line) { 244 std::vector<std::string> parts; 245 SplitString(line, kFieldSeparator, &parts); 246 if (parts.size() < 3) { 247 LOG(WARNING) << "Invalid line: " << line; 248 return false; 249 } 250 251 const std::string& dex_location = parts[0]; 252 uint32_t checksum; 253 if (!ParseInt(parts[1].c_str(), &checksum)) { 254 return false; 255 } 256 257 for (size_t i = 2; i < parts.size(); i++) { 258 if (parts[i] == kClassesMarker) { 259 ++i; 260 // All of the remaining idx are class def indexes. 261 for (++i; i < parts.size(); ++i) { 262 uint32_t class_def_idx; 263 if (!ParseInt(parts[i].c_str(), &class_def_idx)) { 264 LOG(WARNING) << "Cannot parse class_def_idx " << parts[i]; 265 return false; 266 } else if (class_def_idx >= std::numeric_limits<uint16_t>::max()) { 267 LOG(WARNING) << "Class def idx " << class_def_idx << " is larger than uint16_t max"; 268 return false; 269 } 270 if (!AddClassIndex(dex_location, checksum, class_def_idx)) { 271 return false; 272 } 273 } 274 break; 275 } 276 uint32_t method_idx; 277 if (!ParseInt(parts[i].c_str(), &method_idx)) { 278 LOG(WARNING) << "Cannot parse method_idx " << parts[i]; 279 return false; 280 } 281 if (!AddMethodIndex(dex_location, checksum, method_idx)) { 282 return false; 283 } 284 } 285 return true; 286} 287 288// Parses the buffer (of length n) starting from start_from and identify new lines 289// based on kLineSeparator marker. 290// Returns the first position after kLineSeparator in the buffer (starting from start_from), 291// or -1 if the marker doesn't appear. 292// The processed characters are appended to the given line. 293static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& line) { 294 if (start_from >= n) { 295 return -1; 296 } 297 int new_line_pos = -1; 298 for (int i = start_from; i < n; i++) { 299 if (buffer[i] == kLineSeparator) { 300 new_line_pos = i; 301 break; 302 } 303 } 304 int append_limit = new_line_pos == -1 ? n : new_line_pos; 305 line.append(buffer + start_from, append_limit - start_from); 306 // Jump over kLineSeparator and return the position of the next character. 307 return new_line_pos == -1 ? new_line_pos : new_line_pos + 1; 308} 309 310bool ProfileCompilationInfo::Load(int fd) { 311 ScopedTrace trace(__PRETTY_FUNCTION__); 312 DCHECK_GE(fd, 0); 313 314 std::string current_line; 315 const int kBufferSize = 1024; 316 char buffer[kBufferSize]; 317 318 while (true) { 319 int n = TEMP_FAILURE_RETRY(read(fd, buffer, kBufferSize)); 320 if (n < 0) { 321 PLOG(WARNING) << "Error when reading profile file"; 322 return false; 323 } else if (n == 0) { 324 break; 325 } 326 // Detect the new lines from the buffer. If we manage to complete a line, 327 // process it. Otherwise append to the current line. 328 int current_start_pos = 0; 329 while (current_start_pos < n) { 330 current_start_pos = GetLineFromBuffer(buffer, n, current_start_pos, current_line); 331 if (current_start_pos == -1) { 332 break; 333 } 334 if (!ProcessLine(current_line)) { 335 return false; 336 } 337 // Reset the current line (we just processed it). 338 current_line.clear(); 339 } 340 } 341 return true; 342} 343 344bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) { 345 for (const auto& other_it : other.info_) { 346 const std::string& other_dex_location = other_it.first; 347 const DexFileData& other_dex_data = other_it.second; 348 349 auto info_it = info_.find(other_dex_location); 350 if (info_it == info_.end()) { 351 info_it = info_.Put(other_dex_location, DexFileData(other_dex_data.checksum)); 352 } 353 if (info_it->second.checksum != other_dex_data.checksum) { 354 LOG(WARNING) << "Checksum mismatch for dex " << other_dex_location; 355 return false; 356 } 357 info_it->second.method_set.insert(other_dex_data.method_set.begin(), 358 other_dex_data.method_set.end()); 359 info_it->second.class_set.insert(other_dex_data.class_set.begin(), 360 other_dex_data.class_set.end()); 361 } 362 return true; 363} 364 365bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const { 366 auto info_it = info_.find(GetProfileDexFileKey(method_ref.dex_file->GetLocation())); 367 if (info_it != info_.end()) { 368 if (method_ref.dex_file->GetLocationChecksum() != info_it->second.checksum) { 369 return false; 370 } 371 const std::set<uint16_t>& methods = info_it->second.method_set; 372 return methods.find(method_ref.dex_method_index) != methods.end(); 373 } 374 return false; 375} 376 377bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, uint16_t class_def_idx) const { 378 auto info_it = info_.find(GetProfileDexFileKey(dex_file.GetLocation())); 379 if (info_it != info_.end()) { 380 if (dex_file.GetLocationChecksum() != info_it->second.checksum) { 381 return false; 382 } 383 const std::set<uint16_t>& classes = info_it->second.class_set; 384 return classes.find(class_def_idx) != classes.end(); 385 } 386 return false; 387} 388 389uint32_t ProfileCompilationInfo::GetNumberOfMethods() const { 390 uint32_t total = 0; 391 for (const auto& it : info_) { 392 total += it.second.method_set.size(); 393 } 394 return total; 395} 396 397std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files, 398 bool print_full_dex_location) const { 399 std::ostringstream os; 400 if (info_.empty()) { 401 return "ProfileInfo: empty"; 402 } 403 404 os << "ProfileInfo:"; 405 406 const std::string kFirstDexFileKeySubstitute = ":classes.dex"; 407 for (const auto& it : info_) { 408 os << "\n"; 409 const std::string& location = it.first; 410 const DexFileData& dex_data = it.second; 411 if (print_full_dex_location) { 412 os << location; 413 } else { 414 // Replace the (empty) multidex suffix of the first key with a substitute for easier reading. 415 std::string multidex_suffix = DexFile::GetMultiDexSuffix(location); 416 os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix); 417 } 418 for (const auto method_it : dex_data.method_set) { 419 if (dex_files != nullptr) { 420 const DexFile* dex_file = nullptr; 421 for (size_t i = 0; i < dex_files->size(); i++) { 422 if (location == (*dex_files)[i]->GetLocation()) { 423 dex_file = (*dex_files)[i]; 424 } 425 } 426 if (dex_file != nullptr) { 427 os << "\n " << PrettyMethod(method_it, *dex_file, true); 428 } 429 } 430 os << "\n " << method_it; 431 } 432 } 433 return os.str(); 434} 435 436bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) { 437 return info_.Equals(other.info_); 438} 439 440std::set<DexCacheResolvedClasses> ProfileCompilationInfo::GetResolvedClasses() const { 441 std::set<DexCacheResolvedClasses> ret; 442 for (auto&& pair : info_) { 443 const std::string& profile_key = pair.first; 444 const DexFileData& data = pair.second; 445 DexCacheResolvedClasses classes(profile_key, data.checksum); 446 classes.AddClasses(data.class_set.begin(), data.class_set.end()); 447 ret.insert(classes); 448 } 449 return ret; 450} 451 452} // namespace art 453