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