offline_profiling_info.cc revision 1d2760bb6128f1bd623824ebcef5784d81b2ab85
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 <set> 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 "jit/profiling_info.h" 28#include "safe_map.h" 29#include "utils.h" 30 31namespace art { 32 33// An arbitrary value to throttle save requests. Set to 500ms for now. 34static constexpr const uint64_t kMilisecondsToNano = 1000000; 35static constexpr const uint64_t kMinimumTimeBetweenSavesNs = 500 * kMilisecondsToNano; 36 37bool OfflineProfilingInfo::NeedsSaving(uint64_t last_update_time_ns) const { 38 return last_update_time_ns - last_update_time_ns_.LoadRelaxed() > kMinimumTimeBetweenSavesNs; 39} 40 41void OfflineProfilingInfo::SaveProfilingInfo(const std::string& filename, 42 uint64_t last_update_time_ns, 43 const std::set<ArtMethod*>& methods) { 44 if (!NeedsSaving(last_update_time_ns)) { 45 VLOG(profiler) << "No need to saved profile info to " << filename; 46 return; 47 } 48 49 if (methods.empty()) { 50 VLOG(profiler) << "No info to save to " << filename; 51 return; 52 } 53 54 DexFileToMethodsMap info; 55 { 56 ScopedObjectAccess soa(Thread::Current()); 57 for (auto it = methods.begin(); it != methods.end(); it++) { 58 AddMethodInfo(*it, &info); 59 } 60 } 61 62 // This doesn't need locking because we are trying to lock the file for exclusive 63 // access and fail immediately if we can't. 64 if (Serialize(filename, info)) { 65 last_update_time_ns_.StoreRelaxed(last_update_time_ns); 66 VLOG(profiler) << "Successfully saved profile info to " 67 << filename << " with time stamp: " << last_update_time_ns; 68 } 69} 70 71void OfflineProfilingInfo::AddMethodInfo(ArtMethod* method, DexFileToMethodsMap* info) { 72 DCHECK(method != nullptr); 73 const DexFile* dex_file = method->GetDexFile(); 74 75 auto info_it = info->find(dex_file); 76 if (info_it == info->end()) { 77 info_it = info->Put(dex_file, std::set<uint32_t>()); 78 } 79 info_it->second.insert(method->GetDexMethodIndex()); 80} 81 82enum OpenMode { 83 READ, 84 READ_WRITE 85}; 86 87static int OpenFile(const std::string& filename, OpenMode open_mode) { 88 int fd = -1; 89 switch (open_mode) { 90 case READ: 91 fd = open(filename.c_str(), O_RDONLY); 92 break; 93 case READ_WRITE: 94 // TODO(calin) allow the shared uid of the app to access the file. 95 fd = open(filename.c_str(), 96 O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC, 97 S_IRUSR | S_IWUSR); 98 break; 99 } 100 101 if (fd < 0) { 102 PLOG(WARNING) << "Failed to open profile file " << filename; 103 return -1; 104 } 105 106 // Lock the file for exclusive access but don't wait if we can't lock it. 107 int err = flock(fd, LOCK_EX | LOCK_NB); 108 if (err < 0) { 109 PLOG(WARNING) << "Failed to lock profile file " << filename; 110 return -1; 111 } 112 return fd; 113} 114 115static bool CloseDescriptorForFile(int fd, const std::string& filename) { 116 // Now unlock the file, allowing another process in. 117 int err = flock(fd, LOCK_UN); 118 if (err < 0) { 119 PLOG(WARNING) << "Failed to unlock profile file " << filename; 120 return false; 121 } 122 123 // Done, close the file. 124 err = ::close(fd); 125 if (err < 0) { 126 PLOG(WARNING) << "Failed to close descriptor for profile file" << filename; 127 return false; 128 } 129 130 return true; 131} 132 133static void WriteToFile(int fd, const std::ostringstream& os) { 134 std::string data(os.str()); 135 const char *p = data.c_str(); 136 size_t length = data.length(); 137 do { 138 int n = ::write(fd, p, length); 139 p += n; 140 length -= n; 141 } while (length > 0); 142} 143 144static constexpr const char kFieldSeparator = ','; 145static constexpr const char kLineSeparator = '\n'; 146 147/** 148 * Serialization format: 149 * multidex_suffix1,dex_location_checksum1,method_id11,method_id12... 150 * multidex_suffix2,dex_location_checksum2,method_id21,method_id22... 151 * e.g. 152 * ,131232145,11,23,454,54 -> this is the first dex file, it has no multidex suffix 153 * :classes5.dex,218490184,39,13,49,1 -> this is the fifth dex file. 154 **/ 155bool OfflineProfilingInfo::Serialize(const std::string& filename, 156 const DexFileToMethodsMap& info) const { 157 int fd = OpenFile(filename, READ_WRITE); 158 if (fd == -1) { 159 return false; 160 } 161 162 // TODO(calin): Merge with a previous existing profile. 163 // TODO(calin): Profile this and see how much memory it takes. If too much, 164 // write to file directly. 165 std::ostringstream os; 166 for (auto it : info) { 167 const DexFile* dex_file = it.first; 168 const std::set<uint32_t>& method_dex_ids = it.second; 169 170 os << DexFile::GetMultiDexSuffix(dex_file->GetLocation()) 171 << kFieldSeparator 172 << dex_file->GetLocationChecksum(); 173 for (auto method_it : method_dex_ids) { 174 os << kFieldSeparator << method_it; 175 } 176 os << kLineSeparator; 177 } 178 179 WriteToFile(fd, os); 180 181 return CloseDescriptorForFile(fd, filename); 182} 183 184// TODO(calin): This a duplicate of Utils::Split fixing the case where the first character 185// is the separator. Merge the fix into Utils::Split once verified that it doesn't break its users. 186static void SplitString(const std::string& s, char separator, std::vector<std::string>* result) { 187 const char* p = s.data(); 188 const char* end = p + s.size(); 189 // Check if the first character is the separator. 190 if (p != end && *p ==separator) { 191 result->push_back(""); 192 ++p; 193 } 194 // Process the rest of the characters. 195 while (p != end) { 196 if (*p == separator) { 197 ++p; 198 } else { 199 const char* start = p; 200 while (++p != end && *p != separator) { 201 // Skip to the next occurrence of the separator. 202 } 203 result->push_back(std::string(start, p - start)); 204 } 205 } 206} 207 208bool ProfileCompilationInfo::ProcessLine(const std::string& line, 209 const std::vector<const DexFile*>& dex_files) { 210 std::vector<std::string> parts; 211 SplitString(line, kFieldSeparator, &parts); 212 if (parts.size() < 3) { 213 LOG(WARNING) << "Invalid line: " << line; 214 return false; 215 } 216 217 const std::string& multidex_suffix = parts[0]; 218 uint32_t checksum; 219 if (!ParseInt(parts[1].c_str(), &checksum)) { 220 return false; 221 } 222 223 const DexFile* current_dex_file = nullptr; 224 for (auto dex_file : dex_files) { 225 if (DexFile::GetMultiDexSuffix(dex_file->GetLocation()) == multidex_suffix) { 226 if (checksum != dex_file->GetLocationChecksum()) { 227 LOG(WARNING) << "Checksum mismatch for " 228 << dex_file->GetLocation() << " when parsing " << filename_; 229 return false; 230 } 231 current_dex_file = dex_file; 232 break; 233 } 234 } 235 if (current_dex_file == nullptr) { 236 return true; 237 } 238 239 for (size_t i = 2; i < parts.size(); i++) { 240 uint32_t method_idx; 241 if (!ParseInt(parts[i].c_str(), &method_idx)) { 242 LOG(WARNING) << "Cannot parse method_idx " << parts[i]; 243 return false; 244 } 245 uint16_t class_idx = current_dex_file->GetMethodId(method_idx).class_idx_; 246 auto info_it = info_.find(current_dex_file); 247 if (info_it == info_.end()) { 248 info_it = info_.Put(current_dex_file, ClassToMethodsMap()); 249 } 250 ClassToMethodsMap& class_map = info_it->second; 251 auto class_it = class_map.find(class_idx); 252 if (class_it == class_map.end()) { 253 class_it = class_map.Put(class_idx, std::set<uint32_t>()); 254 } 255 class_it->second.insert(method_idx); 256 } 257 return true; 258} 259 260// Parses the buffer (of length n) starting from start_from and identify new lines 261// based on kLineSeparator marker. 262// Returns the first position after kLineSeparator in the buffer (starting from start_from), 263// or -1 if the marker doesn't appear. 264// The processed characters are appended to the given line. 265static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& line) { 266 if (start_from >= n) { 267 return -1; 268 } 269 int new_line_pos = -1; 270 for (int i = start_from; i < n; i++) { 271 if (buffer[i] == kLineSeparator) { 272 new_line_pos = i; 273 break; 274 } 275 } 276 int append_limit = new_line_pos == -1 ? n : new_line_pos; 277 line.append(buffer + start_from, append_limit - start_from); 278 // Jump over kLineSeparator and return the position of the next character. 279 return new_line_pos == -1 ? new_line_pos : new_line_pos + 1; 280} 281 282bool ProfileCompilationInfo::Load(const std::vector<const DexFile*>& dex_files) { 283 if (dex_files.empty()) { 284 return true; 285 } 286 if (kIsDebugBuild) { 287 // In debug builds verify that the multidex suffixes are unique. 288 std::set<std::string> suffixes; 289 for (auto dex_file : dex_files) { 290 std::string multidex_suffix = DexFile::GetMultiDexSuffix(dex_file->GetLocation()); 291 DCHECK(suffixes.find(multidex_suffix) == suffixes.end()) 292 << "DexFiles appear to belong to different apks." 293 << " There are multiple dex files with the same multidex suffix: " 294 << multidex_suffix; 295 suffixes.insert(multidex_suffix); 296 } 297 } 298 info_.clear(); 299 300 int fd = OpenFile(filename_, READ); 301 if (fd == -1) { 302 return false; 303 } 304 305 std::string current_line; 306 const int kBufferSize = 1024; 307 char buffer[kBufferSize]; 308 bool success = true; 309 310 while (success) { 311 int n = read(fd, buffer, kBufferSize); 312 if (n < 0) { 313 PLOG(WARNING) << "Error when reading profile file " << filename_; 314 success = false; 315 break; 316 } else if (n == 0) { 317 break; 318 } 319 // Detect the new lines from the buffer. If we manage to complete a line, 320 // process it. Otherwise append to the current line. 321 int current_start_pos = 0; 322 while (current_start_pos < n) { 323 current_start_pos = GetLineFromBuffer(buffer, n, current_start_pos, current_line); 324 if (current_start_pos == -1) { 325 break; 326 } 327 if (!ProcessLine(current_line, dex_files)) { 328 success = false; 329 break; 330 } 331 // Reset the current line (we just processed it). 332 current_line.clear(); 333 } 334 } 335 if (!success) { 336 info_.clear(); 337 } 338 return CloseDescriptorForFile(fd, filename_) && success; 339} 340 341bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const { 342 auto info_it = info_.find(method_ref.dex_file); 343 if (info_it != info_.end()) { 344 uint16_t class_idx = method_ref.dex_file->GetMethodId(method_ref.dex_method_index).class_idx_; 345 const ClassToMethodsMap& class_map = info_it->second; 346 auto class_it = class_map.find(class_idx); 347 if (class_it != class_map.end()) { 348 const std::set<uint32_t>& methods = class_it->second; 349 return methods.find(method_ref.dex_method_index) != methods.end(); 350 } 351 return false; 352 } 353 return false; 354} 355 356std::string ProfileCompilationInfo::DumpInfo(bool print_full_dex_location) const { 357 std::ostringstream os; 358 if (info_.empty()) { 359 return "ProfileInfo: empty"; 360 } 361 362 os << "ProfileInfo:"; 363 364 // Use an additional map to achieve a predefined ordered based on the dex locations. 365 SafeMap<const std::string, const DexFile*> dex_locations_map; 366 for (auto info_it : info_) { 367 dex_locations_map.Put(info_it.first->GetLocation(), info_it.first); 368 } 369 370 const std::string kFirstDexFileKeySubstitute = ":classes.dex"; 371 for (auto dex_file_it : dex_locations_map) { 372 os << "\n"; 373 const std::string& location = dex_file_it.first; 374 const DexFile* dex_file = dex_file_it.second; 375 if (print_full_dex_location) { 376 os << location; 377 } else { 378 // Replace the (empty) multidex suffix of the first key with a substitute for easier reading. 379 std::string multidex_suffix = DexFile::GetMultiDexSuffix(location); 380 os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix); 381 } 382 for (auto class_it : info_.find(dex_file)->second) { 383 for (auto method_it : class_it.second) { 384 os << "\n " << PrettyMethod(method_it, *dex_file, true); 385 } 386 } 387 } 388 return os.str(); 389} 390 391} // namespace art 392