Home | History | Annotate | Download | only in profman
      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 
     40 namespace art {
     41 
     42 static int original_argc;
     43 static char** original_argv;
     44 
     45 static 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 
     53 static constexpr int kInvalidFd = -1;
     54 
     55 static bool FdIsValid(int fd) {
     56   return fd != kInvalidFd;
     57 }
     58 
     59 static void UsageErrorV(const char* fmt, va_list ap) {
     60   std::string error;
     61   StringAppendV(&error, fmt, ap);
     62   LOG(ERROR) << error;
     63 }
     64 
     65 static void UsageError(const char* fmt, ...) {
     66   va_list ap;
     67   va_start(ap, fmt);
     68   UsageErrorV(fmt, ap);
     69   va_end(ap);
     70 }
     71 
     72 NO_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 
    114 class 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.
    355 static 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 
    371 int main(int argc, char **argv) {
    372   return art::profman(argc, argv);
    373 }
    374 
    375