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