offline_profiling_info.cc revision 2e2db786b8fbaa4dceb37603a4296b0b2aea4e9e
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(const std::string& filename, 52 const std::vector<ArtMethod*>& methods) { 53 if (methods.empty()) { 54 VLOG(profiler) << "No info to save to " << filename; 55 return true; 56 } 57 58 ScopedFlock flock; 59 std::string error; 60 if (!flock.Init(filename.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC, /* block */ false, &error)) { 61 LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error; 62 return false; 63 } 64 65 int fd = flock.GetFile()->Fd(); 66 67 ProfileCompilationInfo info; 68 if (!info.Load(fd)) { 69 LOG(WARNING) << "Could not load previous profile data from file " << filename; 70 return false; 71 } 72 { 73 ScopedObjectAccess soa(Thread::Current()); 74 for (auto it = methods.begin(); it != methods.end(); it++) { 75 const DexFile* dex_file = (*it)->GetDexFile(); 76 if (!info.AddData(GetProfileDexFileKey(dex_file->GetLocation()), 77 dex_file->GetLocationChecksum(), 78 (*it)->GetDexMethodIndex())) { 79 return false; 80 } 81 } 82 } 83 84 if (!flock.GetFile()->ClearContent()) { 85 PLOG(WARNING) << "Could not clear profile file: " << filename; 86 return false; 87 } 88 89 // This doesn't need locking because we are trying to lock the file for exclusive 90 // access and fail immediately if we can't. 91 bool result = info.Save(fd); 92 if (result) { 93 VLOG(profiler) << "Successfully saved profile info to " << filename 94 << " Size: " << GetFileSizeBytes(filename); 95 } else { 96 VLOG(profiler) << "Failed to save profile info to " << filename; 97 } 98 return result; 99} 100 101static bool WriteToFile(int fd, const std::ostringstream& os) { 102 std::string data(os.str()); 103 const char *p = data.c_str(); 104 size_t length = data.length(); 105 do { 106 int n = TEMP_FAILURE_RETRY(write(fd, p, length)); 107 if (n < 0) { 108 PLOG(WARNING) << "Failed to write to descriptor: " << fd; 109 return false; 110 } 111 p += n; 112 length -= n; 113 } while (length > 0); 114 return true; 115} 116 117static constexpr const char kFieldSeparator = ','; 118static constexpr const char kLineSeparator = '\n'; 119 120/** 121 * Serialization format: 122 * dex_location1,dex_location_checksum1,method_id11,method_id12... 123 * dex_location2,dex_location_checksum2,method_id21,method_id22... 124 * e.g. 125 * app.apk,131232145,11,23,454,54 126 * app.apk:classes5.dex,218490184,39,13,49,1 127 **/ 128bool ProfileCompilationInfo::Save(int fd) { 129 DCHECK_GE(fd, 0); 130 // TODO(calin): Profile this and see how much memory it takes. If too much, 131 // write to file directly. 132 std::ostringstream os; 133 for (const auto& it : info_) { 134 const std::string& dex_location = it.first; 135 const DexFileData& dex_data = it.second; 136 137 os << dex_location << kFieldSeparator << dex_data.checksum; 138 for (auto method_it : dex_data.method_set) { 139 os << kFieldSeparator << method_it; 140 } 141 os << kLineSeparator; 142 } 143 144 return WriteToFile(fd, os); 145} 146 147// TODO(calin): This a duplicate of Utils::Split fixing the case where the first character 148// is the separator. Merge the fix into Utils::Split once verified that it doesn't break its users. 149static void SplitString(const std::string& s, char separator, std::vector<std::string>* result) { 150 const char* p = s.data(); 151 const char* end = p + s.size(); 152 // Check if the first character is the separator. 153 if (p != end && *p ==separator) { 154 result->push_back(""); 155 ++p; 156 } 157 // Process the rest of the characters. 158 while (p != end) { 159 if (*p == separator) { 160 ++p; 161 } else { 162 const char* start = p; 163 while (++p != end && *p != separator) { 164 // Skip to the next occurrence of the separator. 165 } 166 result->push_back(std::string(start, p - start)); 167 } 168 } 169} 170 171bool ProfileCompilationInfo::AddData(const std::string& dex_location, 172 uint32_t checksum, 173 uint16_t method_idx) { 174 auto info_it = info_.find(dex_location); 175 if (info_it == info_.end()) { 176 info_it = info_.Put(dex_location, DexFileData(checksum)); 177 } 178 if (info_it->second.checksum != checksum) { 179 LOG(WARNING) << "Checksum mismatch for dex " << dex_location; 180 return false; 181 } 182 info_it->second.method_set.insert(method_idx); 183 return true; 184} 185 186bool ProfileCompilationInfo::ProcessLine(const std::string& line) { 187 std::vector<std::string> parts; 188 SplitString(line, kFieldSeparator, &parts); 189 if (parts.size() < 3) { 190 LOG(WARNING) << "Invalid line: " << line; 191 return false; 192 } 193 194 const std::string& dex_location = parts[0]; 195 uint32_t checksum; 196 if (!ParseInt(parts[1].c_str(), &checksum)) { 197 return false; 198 } 199 200 for (size_t i = 2; i < parts.size(); i++) { 201 uint32_t method_idx; 202 if (!ParseInt(parts[i].c_str(), &method_idx)) { 203 LOG(WARNING) << "Cannot parse method_idx " << parts[i]; 204 return false; 205 } 206 if (!AddData(dex_location, checksum, method_idx)) { 207 return false; 208 } 209 } 210 return true; 211} 212 213// Parses the buffer (of length n) starting from start_from and identify new lines 214// based on kLineSeparator marker. 215// Returns the first position after kLineSeparator in the buffer (starting from start_from), 216// or -1 if the marker doesn't appear. 217// The processed characters are appended to the given line. 218static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& line) { 219 if (start_from >= n) { 220 return -1; 221 } 222 int new_line_pos = -1; 223 for (int i = start_from; i < n; i++) { 224 if (buffer[i] == kLineSeparator) { 225 new_line_pos = i; 226 break; 227 } 228 } 229 int append_limit = new_line_pos == -1 ? n : new_line_pos; 230 line.append(buffer + start_from, append_limit - start_from); 231 // Jump over kLineSeparator and return the position of the next character. 232 return new_line_pos == -1 ? new_line_pos : new_line_pos + 1; 233} 234 235bool ProfileCompilationInfo::Load(int fd) { 236 DCHECK_GE(fd, 0); 237 238 std::string current_line; 239 const int kBufferSize = 1024; 240 char buffer[kBufferSize]; 241 242 while (true) { 243 int n = TEMP_FAILURE_RETRY(read(fd, buffer, kBufferSize)); 244 if (n < 0) { 245 PLOG(WARNING) << "Error when reading profile file"; 246 return false; 247 } else if (n == 0) { 248 break; 249 } 250 // Detect the new lines from the buffer. If we manage to complete a line, 251 // process it. Otherwise append to the current line. 252 int current_start_pos = 0; 253 while (current_start_pos < n) { 254 current_start_pos = GetLineFromBuffer(buffer, n, current_start_pos, current_line); 255 if (current_start_pos == -1) { 256 break; 257 } 258 if (!ProcessLine(current_line)) { 259 return false; 260 } 261 // Reset the current line (we just processed it). 262 current_line.clear(); 263 } 264 } 265 return true; 266} 267 268bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) { 269 for (const auto& other_it : other.info_) { 270 const std::string& other_dex_location = other_it.first; 271 const DexFileData& other_dex_data = other_it.second; 272 273 auto info_it = info_.find(other_dex_location); 274 if (info_it == info_.end()) { 275 info_it = info_.Put(other_dex_location, DexFileData(other_dex_data.checksum)); 276 } 277 if (info_it->second.checksum != other_dex_data.checksum) { 278 LOG(WARNING) << "Checksum mismatch for dex " << other_dex_location; 279 return false; 280 } 281 info_it->second.method_set.insert(other_dex_data.method_set.begin(), 282 other_dex_data.method_set.end()); 283 } 284 return true; 285} 286 287bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const { 288 auto info_it = info_.find(GetProfileDexFileKey(method_ref.dex_file->GetLocation())); 289 if (info_it != info_.end()) { 290 if (method_ref.dex_file->GetLocationChecksum() != info_it->second.checksum) { 291 return false; 292 } 293 const std::set<uint16_t>& methods = info_it->second.method_set; 294 return methods.find(method_ref.dex_method_index) != methods.end(); 295 } 296 return false; 297} 298 299uint32_t ProfileCompilationInfo::GetNumberOfMethods() const { 300 uint32_t total = 0; 301 for (const auto& it : info_) { 302 total += it.second.method_set.size(); 303 } 304 return total; 305} 306 307std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files, 308 bool print_full_dex_location) const { 309 std::ostringstream os; 310 if (info_.empty()) { 311 return "ProfileInfo: empty"; 312 } 313 314 os << "ProfileInfo:"; 315 316 const std::string kFirstDexFileKeySubstitute = ":classes.dex"; 317 for (const auto& it : info_) { 318 os << "\n"; 319 const std::string& location = it.first; 320 const DexFileData& dex_data = it.second; 321 if (print_full_dex_location) { 322 os << location; 323 } else { 324 // Replace the (empty) multidex suffix of the first key with a substitute for easier reading. 325 std::string multidex_suffix = DexFile::GetMultiDexSuffix(location); 326 os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix); 327 } 328 for (const auto method_it : dex_data.method_set) { 329 if (dex_files != nullptr) { 330 const DexFile* dex_file = nullptr; 331 for (size_t i = 0; i < dex_files->size(); i++) { 332 if (location == (*dex_files)[i]->GetLocation()) { 333 dex_file = (*dex_files)[i]; 334 } 335 } 336 if (dex_file != nullptr) { 337 os << "\n " << PrettyMethod(method_it, *dex_file, true); 338 } 339 } 340 os << "\n " << method_it; 341 } 342 } 343 return os.str(); 344} 345 346bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) { 347 return info_.Equals(other.info_); 348} 349 350} // namespace art 351