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