1/* 2 * Copyright (C) 2016 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 "errno.h" 18#include <stdio.h> 19#include <stdlib.h> 20#include <sys/file.h> 21#include <sys/stat.h> 22#include <unistd.h> 23 24#include <iostream> 25#include <string> 26#include <vector> 27 28#include "base/dumpable.h" 29#include "base/scoped_flock.h" 30#include "base/stringpiece.h" 31#include "base/stringprintf.h" 32#include "base/time_utils.h" 33#include "base/unix_file/fd_file.h" 34#include "dex_file.h" 35#include "jit/offline_profiling_info.h" 36#include "utils.h" 37#include "zip_archive.h" 38#include "profile_assistant.h" 39 40namespace art { 41 42static int original_argc; 43static char** original_argv; 44 45static std::string CommandLine() { 46 std::vector<std::string> command; 47 for (int i = 0; i < original_argc; ++i) { 48 command.push_back(original_argv[i]); 49 } 50 return Join(command, ' '); 51} 52 53static constexpr int kInvalidFd = -1; 54 55static bool FdIsValid(int fd) { 56 return fd != kInvalidFd; 57} 58 59static void UsageErrorV(const char* fmt, va_list ap) { 60 std::string error; 61 StringAppendV(&error, fmt, ap); 62 LOG(ERROR) << error; 63} 64 65static void UsageError(const char* fmt, ...) { 66 va_list ap; 67 va_start(ap, fmt); 68 UsageErrorV(fmt, ap); 69 va_end(ap); 70} 71 72NO_RETURN static void Usage(const char *fmt, ...) { 73 va_list ap; 74 va_start(ap, fmt); 75 UsageErrorV(fmt, ap); 76 va_end(ap); 77 78 UsageError("Command: %s", CommandLine().c_str()); 79 UsageError("Usage: profman [options]..."); 80 UsageError(""); 81 UsageError(" --dump-only: dumps the content of the specified profile files"); 82 UsageError(" to standard output (default) in a human readable form."); 83 UsageError(""); 84 UsageError(" --dump-output-to-fd=<number>: redirects --dump-info-for output to a file"); 85 UsageError(" descriptor."); 86 UsageError(""); 87 UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation."); 88 UsageError(" Can be specified multiple time, in which case the data from the different"); 89 UsageError(" profiles will be aggregated."); 90 UsageError(""); 91 UsageError(" --profile-file-fd=<number>: same as --profile-file but accepts a file descriptor."); 92 UsageError(" Cannot be used together with --profile-file."); 93 UsageError(""); 94 UsageError(" --reference-profile-file=<filename>: specify a reference profile."); 95 UsageError(" The data in this file will be compared with the data obtained by merging"); 96 UsageError(" all the files specified with --profile-file or --profile-file-fd."); 97 UsageError(" If the exit code is EXIT_COMPILE then all --profile-file will be merged into"); 98 UsageError(" --reference-profile-file. "); 99 UsageError(""); 100 UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but"); 101 UsageError(" accepts a file descriptor. Cannot be used together with"); 102 UsageError(" --reference-profile-file."); 103 UsageError(""); 104 UsageError(" --dex-location=<string>: location string to use with corresponding"); 105 UsageError(" apk-fd to find dex files"); 106 UsageError(""); 107 UsageError(" --apk-fd=<number>: file descriptor containing an open APK to"); 108 UsageError(" search for dex files"); 109 UsageError(""); 110 111 exit(EXIT_FAILURE); 112} 113 114class ProfMan FINAL { 115 public: 116 ProfMan() : 117 reference_profile_file_fd_(kInvalidFd), 118 dump_only_(false), 119 dump_output_to_fd_(kInvalidFd), 120 start_ns_(NanoTime()) {} 121 122 ~ProfMan() { 123 LogCompletionTime(); 124 } 125 126 void ParseArgs(int argc, char **argv) { 127 original_argc = argc; 128 original_argv = argv; 129 130 InitLogging(argv); 131 132 // Skip over the command name. 133 argv++; 134 argc--; 135 136 if (argc == 0) { 137 Usage("No arguments specified"); 138 } 139 140 for (int i = 0; i < argc; ++i) { 141 const StringPiece option(argv[i]); 142 const bool log_options = false; 143 if (log_options) { 144 LOG(INFO) << "profman: option[" << i << "]=" << argv[i]; 145 } 146 if (option == "--dump-only") { 147 dump_only_ = true; 148 } else if (option.starts_with("--dump-output-to-fd=")) { 149 ParseUintOption(option, "--dump-output-to-fd", &dump_output_to_fd_, Usage); 150 } else if (option.starts_with("--profile-file=")) { 151 profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString()); 152 } else if (option.starts_with("--profile-file-fd=")) { 153 ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_); 154 } else if (option.starts_with("--reference-profile-file=")) { 155 reference_profile_file_ = option.substr(strlen("--reference-profile-file=")).ToString(); 156 } else if (option.starts_with("--reference-profile-file-fd=")) { 157 ParseUintOption(option, "--reference-profile-file-fd", &reference_profile_file_fd_, Usage); 158 } else if (option.starts_with("--dex-location=")) { 159 dex_locations_.push_back(option.substr(strlen("--dex-location=")).ToString()); 160 } else if (option.starts_with("--apk-fd=")) { 161 ParseFdForCollection(option, "--apk-fd", &apks_fd_); 162 } else { 163 Usage("Unknown argument '%s'", option.data()); 164 } 165 } 166 167 bool has_profiles = !profile_files_.empty() || !profile_files_fd_.empty(); 168 bool has_reference_profile = !reference_profile_file_.empty() || 169 FdIsValid(reference_profile_file_fd_); 170 171 // --dump-only may be specified with only --reference-profiles present. 172 if (!dump_only_ && !has_profiles) { 173 Usage("No profile files specified."); 174 } 175 if (!profile_files_.empty() && !profile_files_fd_.empty()) { 176 Usage("Profile files should not be specified with both --profile-file-fd and --profile-file"); 177 } 178 if (!dump_only_ && !has_reference_profile) { 179 Usage("No reference profile file specified."); 180 } 181 if (!reference_profile_file_.empty() && FdIsValid(reference_profile_file_fd_)) { 182 Usage("Reference profile should not be specified with both " 183 "--reference-profile-file-fd and --reference-profile-file"); 184 } 185 if ((!profile_files_.empty() && FdIsValid(reference_profile_file_fd_)) || 186 (!dump_only_ && !profile_files_fd_.empty() && !FdIsValid(reference_profile_file_fd_))) { 187 Usage("Options --profile-file-fd and --reference-profile-file-fd " 188 "should only be used together"); 189 } 190 } 191 192 ProfileAssistant::ProcessingResult ProcessProfiles() { 193 ProfileAssistant::ProcessingResult result; 194 if (profile_files_.empty()) { 195 // The file doesn't need to be flushed here (ProcessProfiles will do it) 196 // so don't check the usage. 197 File file(reference_profile_file_fd_, false); 198 result = ProfileAssistant::ProcessProfiles(profile_files_fd_, reference_profile_file_fd_); 199 CloseAllFds(profile_files_fd_, "profile_files_fd_"); 200 } else { 201 result = ProfileAssistant::ProcessProfiles(profile_files_, reference_profile_file_); 202 } 203 return result; 204 } 205 206 int DumpOneProfile(const std::string& banner, const std::string& filename, int fd, 207 const std::vector<const DexFile*>* dex_files, std::string* dump) { 208 if (!filename.empty()) { 209 fd = open(filename.c_str(), O_RDWR); 210 if (fd < 0) { 211 std::cerr << "Cannot open " << filename << strerror(errno); 212 return -1; 213 } 214 } 215 ProfileCompilationInfo info; 216 if (!info.Load(fd)) { 217 std::cerr << "Cannot load profile info from fd=" << fd << "\n"; 218 return -1; 219 } 220 std::string this_dump = banner + "\n" + info.DumpInfo(dex_files) + "\n"; 221 *dump += this_dump; 222 if (close(fd) < 0) { 223 PLOG(WARNING) << "Failed to close descriptor"; 224 } 225 return 0; 226 } 227 228 int DumpProfileInfo() { 229 static const char* kEmptyString = ""; 230 static const char* kOrdinaryProfile = "=== profile ==="; 231 static const char* kReferenceProfile = "=== reference profile ==="; 232 233 // Open apk/zip files and and read dex files. 234 MemMap::Init(); // for ZipArchive::OpenFromFd 235 std::vector<const DexFile*> dex_files; 236 assert(dex_locations_.size() == apks_fd_.size()); 237 for (size_t i = 0; i < dex_locations_.size(); ++i) { 238 std::string error_msg; 239 std::vector<std::unique_ptr<const DexFile>> dex_files_for_location; 240 std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(apks_fd_[i], 241 dex_locations_[i].c_str(), 242 &error_msg)); 243 if (zip_archive == nullptr) { 244 LOG(WARNING) << "OpenFromFd failed for '" << dex_locations_[i] << "' " << error_msg; 245 continue; 246 } 247 if (DexFile::OpenFromZip(*zip_archive, 248 dex_locations_[i], 249 &error_msg, 250 &dex_files_for_location)) { 251 } else { 252 LOG(WARNING) << "OpenFromZip failed for '" << dex_locations_[i] << "' " << error_msg; 253 continue; 254 } 255 for (std::unique_ptr<const DexFile>& dex_file : dex_files_for_location) { 256 dex_files.push_back(dex_file.release()); 257 } 258 } 259 260 std::string dump; 261 // Dump individual profile files. 262 if (!profile_files_fd_.empty()) { 263 for (int profile_file_fd : profile_files_fd_) { 264 int ret = DumpOneProfile(kOrdinaryProfile, 265 kEmptyString, 266 profile_file_fd, 267 &dex_files, 268 &dump); 269 if (ret != 0) { 270 return ret; 271 } 272 } 273 } 274 if (!profile_files_.empty()) { 275 for (const std::string& profile_file : profile_files_) { 276 int ret = DumpOneProfile(kOrdinaryProfile, profile_file, kInvalidFd, &dex_files, &dump); 277 if (ret != 0) { 278 return ret; 279 } 280 } 281 } 282 // Dump reference profile file. 283 if (FdIsValid(reference_profile_file_fd_)) { 284 int ret = DumpOneProfile(kReferenceProfile, 285 kEmptyString, 286 reference_profile_file_fd_, 287 &dex_files, 288 &dump); 289 if (ret != 0) { 290 return ret; 291 } 292 } 293 if (!reference_profile_file_.empty()) { 294 int ret = DumpOneProfile(kReferenceProfile, 295 reference_profile_file_, 296 kInvalidFd, 297 &dex_files, 298 &dump); 299 if (ret != 0) { 300 return ret; 301 } 302 } 303 if (!FdIsValid(dump_output_to_fd_)) { 304 std::cout << dump; 305 } else { 306 unix_file::FdFile out_fd(dump_output_to_fd_, false /*check_usage*/); 307 if (!out_fd.WriteFully(dump.c_str(), dump.length())) { 308 return -1; 309 } 310 } 311 return 0; 312 } 313 314 bool ShouldOnlyDumpProfile() { 315 return dump_only_; 316 } 317 318 private: 319 static void ParseFdForCollection(const StringPiece& option, 320 const char* arg_name, 321 std::vector<int>* fds) { 322 int fd; 323 ParseUintOption(option, arg_name, &fd, Usage); 324 fds->push_back(fd); 325 } 326 327 static void CloseAllFds(const std::vector<int>& fds, const char* descriptor) { 328 for (size_t i = 0; i < fds.size(); i++) { 329 if (close(fds[i]) < 0) { 330 PLOG(WARNING) << "Failed to close descriptor for " << descriptor << " at index " << i; 331 } 332 } 333 } 334 335 void LogCompletionTime() { 336 static constexpr uint64_t kLogThresholdTime = MsToNs(100); // 100ms 337 uint64_t time_taken = NanoTime() - start_ns_; 338 if (time_taken > kLogThresholdTime) { 339 LOG(WARNING) << "profman took " << PrettyDuration(time_taken); 340 } 341 } 342 343 std::vector<std::string> profile_files_; 344 std::vector<int> profile_files_fd_; 345 std::vector<std::string> dex_locations_; 346 std::vector<int> apks_fd_; 347 std::string reference_profile_file_; 348 int reference_profile_file_fd_; 349 bool dump_only_; 350 int dump_output_to_fd_; 351 uint64_t start_ns_; 352}; 353 354// See ProfileAssistant::ProcessingResult for return codes. 355static int profman(int argc, char** argv) { 356 ProfMan profman; 357 358 // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. 359 profman.ParseArgs(argc, argv); 360 361 if (profman.ShouldOnlyDumpProfile()) { 362 return profman.DumpProfileInfo(); 363 } 364 // Process profile information and assess if we need to do a profile guided compilation. 365 // This operation involves I/O. 366 return profman.ProcessProfiles(); 367} 368 369} // namespace art 370 371int main(int argc, char **argv) { 372 return art::profman(argc, argv); 373} 374 375