• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CommandLine()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 
FdIsValid(int fd)55 static bool FdIsValid(int fd) {
56   return fd != kInvalidFd;
57 }
58 
UsageErrorV(const char * fmt,va_list ap)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 
UsageError(const char * fmt,...)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 
Usage(const char * fmt,...)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:
ProfMan()116   ProfMan() :
117       reference_profile_file_fd_(kInvalidFd),
118       dump_only_(false),
119       dump_output_to_fd_(kInvalidFd),
120       start_ns_(NanoTime()) {}
121 
~ProfMan()122   ~ProfMan() {
123     LogCompletionTime();
124   }
125 
ParseArgs(int argc,char ** argv)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 
ProcessProfiles()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 
DumpOneProfile(const std::string & banner,const std::string & filename,int fd,const std::vector<const DexFile * > * dex_files,std::string * dump)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 
DumpProfileInfo()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 
ShouldOnlyDumpProfile()314   bool ShouldOnlyDumpProfile() {
315     return dump_only_;
316   }
317 
318  private:
ParseFdForCollection(const StringPiece & option,const char * arg_name,std::vector<int> * fds)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 
CloseAllFds(const std::vector<int> & fds,const char * descriptor)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 
LogCompletionTime()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.
profman(int argc,char ** argv)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 
main(int argc,char ** argv)371 int main(int argc, char **argv) {
372   return art::profman(argc, argv);
373 }
374 
375