xref: /aosp_15_r20/art/profman/profman.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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/param.h>
22 #include <unistd.h>
23 
24 #include <cstdint>
25 #include <fstream>
26 #include <iostream>
27 #include <optional>
28 #include <ostream>
29 #include <set>
30 #include <string>
31 #include <string_view>
32 #include <tuple>
33 #include <unordered_set>
34 #include <vector>
35 
36 #include "android-base/parsebool.h"
37 #include "android-base/stringprintf.h"
38 #include "android-base/strings.h"
39 #include "base/array_ref.h"
40 #include "base/dumpable.h"
41 #include "base/logging.h"  // For InitLogging.
42 #include "base/mem_map.h"
43 #include "base/scoped_flock.h"
44 #include "base/stl_util.h"
45 #include "base/time_utils.h"
46 #include "base/unix_file/fd_file.h"
47 #include "base/utils.h"
48 #include "base/zip_archive.h"
49 #include "boot_image_profile.h"
50 #include "dex/art_dex_file_loader.h"
51 #include "dex/bytecode_utils.h"
52 #include "dex/class_accessor-inl.h"
53 #include "dex/class_reference.h"
54 #include "dex/code_item_accessors-inl.h"
55 #include "dex/descriptors_names.h"
56 #include "dex/dex_file.h"
57 #include "dex/dex_file_loader.h"
58 #include "dex/dex_file_structs.h"
59 #include "dex/dex_file_types.h"
60 #include "dex/method_reference.h"
61 #include "dex/type_reference.h"
62 #include "profile/profile_boot_info.h"
63 #include "profile/profile_compilation_info.h"
64 #include "profile_assistant.h"
65 #include "profman/profman_result.h"
66 #include "inline_cache_format_util.h"
67 
68 namespace art {
69 
70 using ProfileSampleAnnotation = ProfileCompilationInfo::ProfileSampleAnnotation;
71 
72 static int original_argc;
73 static char** original_argv;
74 
CommandLine()75 static std::string CommandLine() {
76   std::vector<std::string> command;
77   command.reserve(original_argc);
78   for (int i = 0; i < original_argc; ++i) {
79     command.push_back(original_argv[i]);
80   }
81   return android::base::Join(command, ' ');
82 }
83 
FdIsValid(int fd)84 static bool FdIsValid(int fd) {
85   return fd != File::kInvalidFd;
86 }
87 
UsageErrorV(const char * fmt,va_list ap)88 static void UsageErrorV(const char* fmt, va_list ap) {
89   std::string error;
90   android::base::StringAppendV(&error, fmt, ap);
91   LOG(ERROR) << error;
92 }
93 
UsageError(const char * fmt,...)94 static void UsageError(const char* fmt, ...) {
95   va_list ap;
96   va_start(ap, fmt);
97   UsageErrorV(fmt, ap);
98   va_end(ap);
99 }
100 
Usage(const char * fmt,...)101 NO_RETURN static void Usage(const char *fmt, ...) {
102   va_list ap;
103   va_start(ap, fmt);
104   UsageErrorV(fmt, ap);
105   va_end(ap);
106 
107   UsageError("Command: %s", CommandLine().c_str());
108   UsageError("Usage: profman [options]...");
109   UsageError("");
110   UsageError("  --dump-only: dumps the content of the specified profile files");
111   UsageError("      to standard output (default) in a human readable form.");
112   UsageError("");
113   UsageError("  --dump-output-to-fd=<number>: redirects --dump-only output to a file descriptor.");
114   UsageError("");
115   UsageError("  --dump-classes-and-methods: dumps a sorted list of classes and methods that are");
116   UsageError("      in the specified profile file to standard output (default) in a human");
117   UsageError("      readable form. The output is valid input for --create-profile-from");
118   UsageError("");
119   UsageError("  --profile-file=<filename>: specify profiler output file to use for compilation.");
120   UsageError("      Can be specified multiple time, in which case the data from the different");
121   UsageError("      profiles will be aggregated. Can also be specified zero times, in which case");
122   UsageError("      profman will still analyze the reference profile against the given --apk and");
123   UsageError("      return exit code based on whether the reference profile is empty and whether");
124   UsageError("      an error occurs, but no merge will happen.");
125   UsageError("");
126   UsageError("  --profile-file-fd=<number>: same as --profile-file but accepts a file descriptor.");
127   UsageError("      Cannot be used together with --profile-file.");
128   UsageError("");
129   UsageError("  --reference-profile-file=<filename>: specify a reference profile.");
130   UsageError("      The data in this file will be compared with the data obtained by merging");
131   UsageError("      all the files specified with --profile-file or --profile-file-fd.");
132   UsageError("      If the exit code is ProfmanResult::kCompile then all --profile-file will be");
133   UsageError("      merged into --reference-profile-file. ");
134   UsageError("");
135   UsageError("  --reference-profile-file-fd=<number>: same as --reference-profile-file but");
136   UsageError("      accepts a file descriptor. Cannot be used together with");
137   UsageError("      --reference-profile-file.");
138   UsageError("");
139   UsageError("  --generate-test-profile=<filename>: generates a random profile file for testing.");
140   UsageError("  --generate-test-profile-num-dex=<number>: number of dex files that should be");
141   UsageError("      included in the generated profile. Defaults to 20.");
142   UsageError("  --generate-test-profile-method-percentage=<number>: the percentage from the maximum");
143   UsageError("      number of methods that should be generated. Defaults to 5.");
144   UsageError("  --generate-test-profile-class-percentage=<number>: the percentage from the maximum");
145   UsageError("      number of classes that should be generated. Defaults to 5.");
146   UsageError("  --generate-test-profile-seed=<number>: seed for random number generator used when");
147   UsageError("      generating random test profiles. Defaults to using NanoTime.");
148   UsageError("");
149   UsageError("  --create-profile-from=<filename>: creates a profile from a list of classes,");
150   UsageError("      methods and inline caches.");
151   UsageError("  --output-profile-type=(app|boot|bprof): Select output profile format for");
152   UsageError("      the --create-profile-from option. Default: app.");
153   UsageError("");
154   UsageError("  --dex-location=<string>: location string to use with corresponding");
155   UsageError("      apk-fd to find dex files");
156   UsageError("");
157   UsageError("  --apk-fd=<number>: file descriptor containing an open APK to");
158   UsageError("      search for dex files");
159   UsageError("  --apk=<filename>: an APK to search for dex files");
160   UsageError("  --skip-apk-verification: do not attempt to verify APKs");
161   UsageError("");
162   UsageError("  --generate-boot-image-profile: Generate a boot image profile based on input");
163   UsageError("      profiles. Requires passing in dex files to inspect properties of classes.");
164   UsageError("  --method-threshold=percentage between 0 and 100");
165   UsageError("      what threshold to apply to the methods when deciding whether or not to");
166   UsageError("      include it in the final profile.");
167   UsageError("  --class-threshold=percentage between 0 and 100");
168   UsageError("      what threshold to apply to the classes when deciding whether or not to");
169   UsageError("      include it in the final profile.");
170   UsageError("  --clean-class-threshold=percentage between 0 and 100");
171   UsageError("      what threshold to apply to the clean classes when deciding whether or not to");
172   UsageError("      include it in the final profile.");
173   UsageError("  --preloaded-class-threshold=percentage between 0 and 100");
174   UsageError("      what threshold to apply to the classes when deciding whether or not to");
175   UsageError("      include it in the final preloaded classes.");
176   UsageError("  --preloaded-classes-denylist=file");
177   UsageError("      a file listing the classes that should not be preloaded in Zygote");
178   UsageError("  --upgrade-startup-to-hot=true|false:");
179   UsageError("      whether or not to upgrade startup methods to hot");
180   UsageError("  --special-package=pkg_name:percentage between 0 and 100");
181   UsageError("      what threshold to apply to the methods/classes that are used by the given");
182   UsageError("      package when deciding whether or not to include it in the final profile.");
183   UsageError("  --debug-append-uses=bool: whether or not to append package use as debug info.");
184   UsageError("  --out-profile-path=path: boot image profile output path");
185   UsageError("  --out-preloaded-classes-path=path: preloaded classes output path");
186   UsageError("  --copy-and-update-profile-key: if present, profman will copy the profile from");
187   UsageError("      the file passed with --profile-fd(file) to the profile passed with");
188   UsageError("      --reference-profile-fd(file) and update at the same time the profile-key");
189   UsageError("      of entries corresponding to the apks passed with --apk(-fd).");
190   UsageError("  --boot-image-merge: indicates that this merge is for a boot image profile.");
191   UsageError("      In this case, the reference profile must have a boot profile version.");
192   UsageError("  --force-merge: performs a forced merge, without analyzing if there is a");
193   UsageError("      significant difference between before and after the merge.");
194   UsageError("      Deprecated. Use --force-merge-and-analyze instead.");
195   UsageError("  --force-merge-and-analyze: performs a forced merge and analyzes if there is any");
196   UsageError("      difference between before and after the merge.");
197   UsageError("  --min-new-methods-percent-change=percentage between 0 and 100 (default 2)");
198   UsageError("      the min percent of new methods to trigger a compilation.");
199   UsageError("  --min-new-classes-percent-change=percentage between 0 and 100 (default 2)");
200   UsageError("      the min percent of new classes to trigger a compilation.");
201   UsageError("");
202 
203   exit(ProfmanResult::kErrorUsage);
204 }
205 
206 // Note: make sure you update the Usage if you change these values.
207 static constexpr uint16_t kDefaultTestProfileNumDex = 20;
208 static constexpr uint16_t kDefaultTestProfileMethodPercentage = 5;
209 static constexpr uint16_t kDefaultTestProfileClassPercentage = 5;
210 
211 // Separators used when parsing human friendly representation of profiles.
212 static const std::string kMethodSep = "->";  // NOLINT [runtime/string] [4]
213 static const std::string kClassAllMethods = "*";  // NOLINT [runtime/string] [4]
214 static constexpr char kAnnotationStart = '{';
215 static constexpr char kAnnotationEnd = '}';
216 static constexpr char kProfileParsingFirstCharInSignature = '(';
217 static constexpr char kMethodFlagStringHot = 'H';
218 static constexpr char kMethodFlagStringStartup = 'S';
219 static constexpr char kMethodFlagStringPostStartup = 'P';
220 
Abort(const char * msg)221 NO_RETURN static void Abort(const char* msg) {
222   LOG(ERROR) << msg;
223   exit(1);
224 }
225 template <typename T>
ParseUintValue(const std::string & option_name,const std::string & value,T * out,T min=std::numeric_limits<T>::min (),T max=std::numeric_limits<T>::max ())226 static void ParseUintValue(const std::string& option_name,
227                            const std::string& value,
228                            T* out,
229                            T min = std::numeric_limits<T>::min(),
230                            T max = std::numeric_limits<T>::max()) {
231   int64_t parsed_integer_value = 0;
232   if (!android::base::ParseInt(
233       value,
234       &parsed_integer_value,
235       static_cast<int64_t>(min),
236       static_cast<int64_t>(max))) {
237     Usage("Failed to parse %s '%s' as an integer", option_name.c_str(), value.c_str());
238   }
239   if (parsed_integer_value < 0) {
240     Usage("%s passed a negative value %" PRId64, option_name.c_str(), parsed_integer_value);
241   }
242   if (static_cast<uint64_t>(parsed_integer_value) >
243       static_cast<std::make_unsigned_t<T>>(std::numeric_limits<T>::max())) {
244     Usage("%s passed a value %" PRIu64 " above max (%" PRIu64 ")",
245           option_name.c_str(),
246           static_cast<uint64_t>(parsed_integer_value),
247           static_cast<uint64_t>(std::numeric_limits<T>::max()));
248   }
249   *out = dchecked_integral_cast<T>(parsed_integer_value);
250 }
251 
252 template <typename T>
ParseUintOption(const char * raw_option,std::string_view option_prefix,T * out,T min=std::numeric_limits<T>::min (),T max=std::numeric_limits<T>::max ())253 static void ParseUintOption(const char* raw_option,
254                             std::string_view option_prefix,
255                             T* out,
256                             T min = std::numeric_limits<T>::min(),
257                             T max = std::numeric_limits<T>::max()) {
258   DCHECK(option_prefix.ends_with("="));
259   DCHECK(std::string_view(raw_option).starts_with(option_prefix))
260       << raw_option << " " << option_prefix;
261   std::string option_name(option_prefix.substr(option_prefix.size() - 1u));
262   const char* value_string = raw_option + option_prefix.size();
263 
264   ParseUintValue(option_name, value_string, out, min, max);
265 }
266 
ParseBoolOption(std::string_view option,std::string_view option_prefix,bool * out)267 static void ParseBoolOption(std::string_view option,
268                             std::string_view option_prefix,
269                             bool* out) {
270   DCHECK(option_prefix.ends_with("="));
271   DCHECK(option.starts_with(option_prefix)) << option << " " << option_prefix;
272   const std::string_view value_string = option.substr(option_prefix.size());
273   android::base::ParseBoolResult result = android::base::ParseBool(value_string);
274   if (result == android::base::ParseBoolResult::kError) {
275     std::string option_name(option_prefix.substr(option_prefix.size() - 1u));
276     Usage("Failed to parse %s '%s' as an integer", option_name.c_str(), value_string);
277   }
278 
279   *out = result == android::base::ParseBoolResult::kTrue;
280 }
281 
282 enum class OutputProfileType {
283   kApp,
284   kBoot,
285   kBprof,
286 };
287 
ParseOutputProfileType(std::string_view option,std::string_view option_prefix,OutputProfileType * out)288 static void ParseOutputProfileType(std::string_view option,
289                                    std::string_view option_prefix,
290                                    OutputProfileType* out) {
291   DCHECK(option_prefix.ends_with("="));
292   DCHECK(option.starts_with(option_prefix)) << option << " " << option_prefix;
293   const std::string_view value_string = option.substr(option_prefix.size());
294   if (value_string == "app") {
295     *out = OutputProfileType::kApp;
296   } else if (value_string == "boot") {
297     *out = OutputProfileType::kBoot;
298   } else if (value_string == "bprof") {
299     *out = OutputProfileType::kBprof;
300   } else {
301     std::string option_name(option_prefix.substr(option_prefix.size() - 1u));
302     Usage("Failed to parse %s '%s' as (app|boot|bprof)", option_name.c_str(), value_string);
303   }
304 }
305 
306 // TODO(calin): This class has grown too much from its initial design. Split the functionality
307 // into smaller, more contained pieces.
308 class ProfMan final {
309  public:
ProfMan()310   ProfMan() :
311       reference_profile_file_fd_(File::kInvalidFd),
312       dump_only_(false),
313       dump_classes_and_methods_(false),
314       generate_boot_image_profile_(false),
315       output_profile_type_(OutputProfileType::kApp),
316       dump_output_to_fd_(File::kInvalidFd),
317       test_profile_num_dex_(kDefaultTestProfileNumDex),
318       test_profile_method_percerntage_(kDefaultTestProfileMethodPercentage),
319       test_profile_class_percentage_(kDefaultTestProfileClassPercentage),
320       test_profile_seed_(NanoTime()),
321       start_ns_(NanoTime()),
322       copy_and_update_profile_key_(false),
323       profile_assistant_options_(ProfileAssistant::Options()) {}
324 
~ProfMan()325   ~ProfMan() {
326     LogCompletionTime();
327   }
328 
ParseArgs(int argc,char ** argv)329   void ParseArgs(int argc, char **argv) {
330     original_argc = argc;
331     original_argv = argv;
332 
333     MemMap::Init();
334     InitLogging(argv, Abort);
335 
336     // Skip over the command name.
337     argv++;
338     argc--;
339 
340     if (argc == 0) {
341       Usage("No arguments specified");
342     }
343 
344     for (int i = 0; i < argc; ++i) {
345       const char* raw_option = argv[i];
346       const std::string_view option(raw_option);
347       const bool log_options = false;
348       if (log_options) {
349         LOG(INFO) << "profman: option[" << i << "]=" << argv[i];
350       }
351       if (option == "--dump-only") {
352         dump_only_ = true;
353       } else if (option == "--dump-classes-and-methods") {
354         dump_classes_and_methods_ = true;
355       } else if (option.starts_with("--create-profile-from=")) {
356         create_profile_from_file_ = std::string(option.substr(strlen("--create-profile-from=")));
357       } else if (option.starts_with("--output-profile-type=")) {
358         ParseOutputProfileType(option, "--output-profile-type=", &output_profile_type_);
359       } else if (option.starts_with("--dump-output-to-fd=")) {
360         ParseUintOption(raw_option, "--dump-output-to-fd=", &dump_output_to_fd_);
361       } else if (option == "--generate-boot-image-profile") {
362         generate_boot_image_profile_ = true;
363       } else if (option.starts_with("--method-threshold=")) {
364         ParseUintOption(raw_option,
365                         "--method-threshold=",
366                         &boot_image_options_.method_threshold,
367                         0u,
368                         100u);
369       } else if (option.starts_with("--class-threshold=")) {
370         ParseUintOption(raw_option,
371                         "--class-threshold=",
372                         &boot_image_options_.image_class_threshold,
373                         0u,
374                         100u);
375       } else if (option.starts_with("--clean-class-threshold=")) {
376         ParseUintOption(raw_option,
377                         "--clean-class-threshold=",
378                         &boot_image_options_.image_class_clean_threshold,
379                         0u,
380                         100u);
381       } else if (option.starts_with("--preloaded-class-threshold=")) {
382         ParseUintOption(raw_option,
383                         "--preloaded-class-threshold=",
384                         &boot_image_options_.preloaded_class_threshold,
385                         0u,
386                         100u);
387       } else if (option.starts_with("--preloaded-classes-denylist=")) {
388         std::string preloaded_classes_denylist =
389             std::string(option.substr(strlen("--preloaded-classes-denylist=")));
390         // Read the user-specified list of methods.
391         std::unique_ptr<std::set<std::string>>
392             denylist(ReadCommentedInputFromFile<std::set<std::string>>(
393                 preloaded_classes_denylist.c_str(), nullptr));  // No post-processing.
394         boot_image_options_.preloaded_classes_denylist.insert(
395             denylist->begin(), denylist->end());
396       } else if (option.starts_with("--upgrade-startup-to-hot=")) {
397         ParseBoolOption(option,
398                         "--upgrade-startup-to-hot=",
399                         &boot_image_options_.upgrade_startup_to_hot);
400       } else if (option.starts_with("--special-package=")) {
401         std::vector<std::string> values;
402         Split(std::string(option.substr(strlen("--special-package="))), ':', &values);
403         if (values.size() != 2) {
404           Usage("--special-package needs to be specified as pkg_name:threshold");
405         }
406         uint32_t threshold;
407         ParseUintValue("special-package", values[1], &threshold, 0u, 100u);
408         boot_image_options_.special_packages_thresholds.Overwrite(values[0], threshold);
409       } else if (option.starts_with("--debug-append-uses=")) {
410         ParseBoolOption(option,
411                         "--debug-append-uses=",
412                         &boot_image_options_.append_package_use_list);
413       } else if (option.starts_with("--out-profile-path=")) {
414         boot_profile_out_path_ = std::string(option.substr(strlen("--out-profile-path=")));
415       } else if (option.starts_with("--out-preloaded-classes-path=")) {
416         preloaded_classes_out_path_ = std::string(
417             option.substr(strlen("--out-preloaded-classes-path=")));
418       } else if (option.starts_with("--profile-file=")) {
419         profile_files_.push_back(std::string(option.substr(strlen("--profile-file="))));
420       } else if (option.starts_with("--profile-file-fd=")) {
421         ParseFdForCollection(raw_option, "--profile-file-fd=", &profile_files_fd_);
422       } else if (option.starts_with("--reference-profile-file=")) {
423         reference_profile_file_ = std::string(option.substr(strlen("--reference-profile-file=")));
424       } else if (option.starts_with("--reference-profile-file-fd=")) {
425         ParseUintOption(raw_option, "--reference-profile-file-fd=", &reference_profile_file_fd_);
426       } else if (option.starts_with("--dex-location=")) {
427         dex_locations_.push_back(std::string(option.substr(strlen("--dex-location="))));
428       } else if (option.starts_with("--apk-fd=")) {
429         ParseFdForCollection(raw_option, "--apk-fd=", &apks_fd_);
430       } else if (option.starts_with("--apk=")) {
431         apk_files_.push_back(std::string(option.substr(strlen("--apk="))));
432       } else if (option.starts_with("--generate-test-profile=")) {
433         test_profile_ = std::string(option.substr(strlen("--generate-test-profile=")));
434       } else if (option.starts_with("--generate-test-profile-num-dex=")) {
435         ParseUintOption(raw_option,
436                         "--generate-test-profile-num-dex=",
437                         &test_profile_num_dex_);
438       } else if (option.starts_with("--generate-test-profile-method-percentage=")) {
439         ParseUintOption(raw_option,
440                         "--generate-test-profile-method-percentage=",
441                         &test_profile_method_percerntage_);
442       } else if (option.starts_with("--generate-test-profile-class-percentage=")) {
443         ParseUintOption(raw_option,
444                         "--generate-test-profile-class-percentage=",
445                         &test_profile_class_percentage_);
446       } else if (option.starts_with("--generate-test-profile-seed=")) {
447         ParseUintOption(raw_option, "--generate-test-profile-seed=", &test_profile_seed_);
448       } else if (option.starts_with("--min-new-methods-percent-change=")) {
449         uint32_t min_new_methods_percent_change;
450         ParseUintOption(raw_option,
451                         "--min-new-methods-percent-change=",
452                         &min_new_methods_percent_change,
453                         0u,
454                         100u);
455         profile_assistant_options_.SetMinNewMethodsPercentChangeForCompilation(
456             min_new_methods_percent_change);
457       } else if (option.starts_with("--min-new-classes-percent-change=")) {
458         uint32_t min_new_classes_percent_change;
459         ParseUintOption(raw_option,
460                         "--min-new-classes-percent-change=",
461                         &min_new_classes_percent_change,
462                         0u,
463                         100u);
464         profile_assistant_options_.SetMinNewClassesPercentChangeForCompilation(
465             min_new_classes_percent_change);
466       } else if (option == "--copy-and-update-profile-key") {
467         copy_and_update_profile_key_ = true;
468       } else if (option == "--boot-image-merge") {
469         profile_assistant_options_.SetBootImageMerge(true);
470       } else if (option == "--force-merge") {
471         // For backward compatibility only.
472         // TODO(jiakaiz): Remove this when S and T are no longer supported.
473         profile_assistant_options_.SetForceMerge(true);
474       } else if (option == "--force-merge-and-analyze") {
475         profile_assistant_options_.SetForceMergeAndAnalyze(true);
476       } else {
477         Usage("Unknown argument '%s'", raw_option);
478       }
479     }
480 
481     // Validate global consistency between file/fd options.
482     if (!profile_files_.empty() && !profile_files_fd_.empty()) {
483       Usage("Profile files should not be specified with both --profile-file-fd and --profile-file");
484     }
485     if (!reference_profile_file_.empty() && FdIsValid(reference_profile_file_fd_)) {
486       Usage("Reference profile should not be specified with both "
487             "--reference-profile-file-fd and --reference-profile-file");
488     }
489     if (!apk_files_.empty() && !apks_fd_.empty()) {
490       Usage("APK files should not be specified with both --apk-fd and --apk");
491     }
492   }
493 
494   struct ProfileFilterKey {
ProfileFilterKeyart::ProfMan::ProfileFilterKey495     ProfileFilterKey(const std::string& dex_location, uint32_t checksum)
496         : dex_location_(dex_location), checksum_(checksum) {}
497     const std::string dex_location_;
498     uint32_t checksum_;
499 
operator ==art::ProfMan::ProfileFilterKey500     bool operator==(const ProfileFilterKey& other) const {
501       return checksum_ == other.checksum_ && dex_location_ == other.dex_location_;
502     }
operator <art::ProfMan::ProfileFilterKey503     bool operator<(const ProfileFilterKey& other) const {
504       return checksum_ == other.checksum_
505           ?  dex_location_ < other.dex_location_
506           : checksum_ < other.checksum_;
507     }
508   };
509 
ProcessProfiles()510   ProfmanResult::ProcessingResult ProcessProfiles() {
511     // Validate that a reference profile was passed, at the very least. It's okay that profiles are
512     // missing, in which case profman will still analyze the reference profile (to check whether
513     // it's empty), but no merge will happen.
514     if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
515       Usage("No reference profile file specified.");
516     }
517     if ((!profile_files_.empty() && FdIsValid(reference_profile_file_fd_)) ||
518         (!profile_files_fd_.empty() && !FdIsValid(reference_profile_file_fd_))) {
519       Usage("Options --profile-file-fd and --reference-profile-file-fd "
520             "should only be used together");
521     }
522 
523     // Check if we have any apks which we should use to filter the profile data.
524     std::set<ProfileFilterKey> profile_filter_keys;
525     if (!GetProfileFilterKeyFromApks(&profile_filter_keys)) {
526       return ProfmanResult::kErrorIO;
527     }
528 
529     // Build the profile filter function. If the set of keys is empty it means we
530     // don't have any apks; as such we do not filter anything.
531     const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn =
532         [profile_filter_keys](const std::string& profile_key, uint32_t checksum) {
533             if (profile_filter_keys.empty()) {
534               // No --apk was specified. Accept all dex files.
535               return true;
536             } else {
537               // Remove any annotations from the profile key before comparing with the keys we get from apks.
538               std::string base_key = ProfileCompilationInfo::GetBaseKeyFromAugmentedKey(profile_key);
539               return profile_filter_keys.find(ProfileFilterKey(base_key, checksum)) !=
540                   profile_filter_keys.end();
541             }
542         };
543 
544     ProfmanResult::ProcessingResult result;
545 
546     if (reference_profile_file_.empty()) {
547       // The file doesn't need to be flushed here (ProcessProfiles will do it)
548       // so don't check the usage.
549       File file(reference_profile_file_fd_, false);
550       result = ProfileAssistant::ProcessProfiles(profile_files_fd_,
551                                                  reference_profile_file_fd_,
552                                                  filter_fn,
553                                                  profile_assistant_options_);
554       CloseAllFds(profile_files_fd_, "profile_files_fd_");
555     } else {
556       result = ProfileAssistant::ProcessProfiles(profile_files_,
557                                                  reference_profile_file_,
558                                                  filter_fn,
559                                                  profile_assistant_options_);
560     }
561     return result;
562   }
563 
GetProfileFilterKeyFromApks(std::set<ProfileFilterKey> * profile_filter_keys)564   bool GetProfileFilterKeyFromApks(std::set<ProfileFilterKey>* profile_filter_keys) {
565     return ForEachApkFile([&](File file, const std::string& location) {
566       ArtDexFileLoader dex_file_loader(&file, location);
567       std::vector<std::pair<std::string, uint32_t>> checksums;
568       std::string error_msg;
569       if (!dex_file_loader.GetMultiDexChecksums(&checksums, &error_msg)) {
570         LOG(ERROR) << "Open failed for '" << location << "' " << error_msg;
571         return false;
572       }
573       for (const auto& [multi_dex_location, checksum] : checksums) {
574         profile_filter_keys->emplace(
575             ProfileCompilationInfo::GetProfileDexFileBaseKey(multi_dex_location), checksum);
576       }
577       return true;
578     });
579   }
580 
OpenApkFilesFromLocations(std::vector<std::unique_ptr<const DexFile>> * dex_files)581   bool OpenApkFilesFromLocations(std::vector<std::unique_ptr<const DexFile>>* dex_files) {
582     auto process_fn = [dex_files](std::unique_ptr<const DexFile>&& dex_file) {
583       dex_files->emplace_back(std::move(dex_file));
584     };
585     return OpenApkFilesFromLocations(process_fn);
586   }
587 
OpenApkFilesFromLocations(const std::function<void (std::unique_ptr<const DexFile> &&)> & process_fn)588   bool OpenApkFilesFromLocations(
589       const std::function<void(std::unique_ptr<const DexFile>&&)>& process_fn) {
590     static constexpr bool kVerifyChecksum = true;
591     std::string error_msg;
592     std::vector<std::unique_ptr<const DexFile>> dex_files_for_location;
593     bool result = ForEachApkFile([&](File file, const std::string& location) {
594       ArtDexFileLoader dex_file_loader(&file, location);
595       if (!dex_file_loader.Open(/*verify=*/false,
596                                 kVerifyChecksum,
597                                 /*allow_no_dex_files=*/true,
598                                 &error_msg,
599                                 &dex_files_for_location)) {
600         LOG(ERROR) << "Open failed for '" << location << "' " << error_msg;
601         return false;
602       }
603       return true;
604     });
605     if (!result) {
606       return false;
607     }
608     for (std::unique_ptr<const DexFile>& dex_file : dex_files_for_location) {
609       process_fn(std::move(dex_file));
610     }
611     return true;
612   }
613 
ForEachApkFile(const std::function<bool (File file,const std::string & location)> & process_fn)614   bool ForEachApkFile(
615       const std::function<bool(File file, const std::string& location)>& process_fn) {
616     bool use_apk_fd_list = !apks_fd_.empty();
617     if (use_apk_fd_list) {
618       // Get the APKs from the collection of FDs.
619       if (dex_locations_.empty()) {
620         // Try to compute the dex locations from the file paths of the descriptions.
621         // This will make it easier to invoke profman with --apk-fd and without
622         // being force to pass --dex-location when the location would be the apk path.
623         if (!ComputeDexLocationsFromApkFds()) {
624           return false;
625         }
626       } else {
627         if (dex_locations_.size() != apks_fd_.size()) {
628           Usage("The number of apk-fds must match the number of dex-locations.");
629         }
630       }
631     } else if (!apk_files_.empty()) {
632       if (dex_locations_.empty()) {
633         // If no dex locations are specified use the apk names as locations.
634         dex_locations_ = apk_files_;
635       } else if (dex_locations_.size() != apk_files_.size()) {
636         Usage("The number of apk-fds must match the number of dex-locations.");
637       }
638     } else {
639       // No APKs were specified.
640       CHECK(dex_locations_.empty());
641       return true;
642     }
643     for (size_t i = 0; i < dex_locations_.size(); ++i) {
644       // We do not need to verify the apk for processing profiles.
645       if (use_apk_fd_list) {
646         File file(apks_fd_[i], /*check_usage=*/false);
647         if (!process_fn(std::move(file), dex_locations_[i])) {
648           return false;
649         }
650       } else {
651         File file(apk_files_[i], O_RDONLY, /*check_usage=*/false);
652         if (file.Fd() < 0) {
653           PLOG(ERROR) << "Unable to open '" << apk_files_[i] << "'";
654           return false;
655         }
656         if (!process_fn(std::move(file), dex_locations_[i])) {
657           return false;
658         }
659       }
660     }
661     return true;
662   }
663 
664   // Get the dex locations from the apk fds.
665   // The methods reads the links from /proc/self/fd/ to find the original apk paths
666   // and puts them in the dex_locations_ vector.
ComputeDexLocationsFromApkFds()667   bool ComputeDexLocationsFromApkFds() {
668 #ifdef _WIN32
669     PLOG(ERROR) << "ComputeDexLocationsFromApkFds is unsupported on Windows.";
670     return false;
671 #else
672     // We can't use a char array of PATH_MAX size without exceeding the frame size.
673     // So we use a vector as the buffer for the path.
674     std::vector<char> buffer(PATH_MAX, 0);
675     for (size_t i = 0; i < apks_fd_.size(); ++i) {
676       std::string fd_path = "/proc/self/fd/" + std::to_string(apks_fd_[i]);
677       ssize_t len = readlink(fd_path.c_str(), buffer.data(), buffer.size() - 1);
678       if (len == -1) {
679         PLOG(ERROR) << "Could not open path from fd";
680         return false;
681       }
682 
683       buffer[len] = '\0';
684       dex_locations_.push_back(buffer.data());
685     }
686     return true;
687 #endif
688   }
689 
LoadProfile(const std::string & filename,int fd,bool for_boot_image)690   std::unique_ptr<const ProfileCompilationInfo> LoadProfile(const std::string& filename,
691                                                             int fd,
692                                                             bool for_boot_image) {
693     if (!filename.empty()) {
694 #ifdef _WIN32
695       int flags = O_RDWR;
696 #else
697       int flags = O_RDWR | O_CLOEXEC;
698 #endif
699       fd = open(filename.c_str(), flags);
700       if (fd < 0) {
701         PLOG(ERROR) << "Cannot open " << filename;
702         return nullptr;
703       }
704     }
705     std::unique_ptr<ProfileCompilationInfo> info(new ProfileCompilationInfo(for_boot_image));
706     if (!info->Load(fd)) {
707       LOG(ERROR) << "Cannot load profile info from fd=" << fd << "\n";
708       return nullptr;
709     }
710     return info;
711   }
712 
DumpOneProfile(const std::string & banner,const std::string & filename,int fd,const std::vector<std::unique_ptr<const DexFile>> * dex_files,std::string * dump)713   int DumpOneProfile(const std::string& banner,
714                      const std::string& filename,
715                      int fd,
716                      const std::vector<std::unique_ptr<const DexFile>>* dex_files,
717                      std::string* dump) {
718     // For dumping, try loading as app profile and if that fails try loading as boot profile.
719     std::unique_ptr<const ProfileCompilationInfo> info =
720         LoadProfile(filename, fd, /*for_boot_image=*/ false);
721     if (info == nullptr) {
722       info = LoadProfile(filename, fd, /*for_boot_image=*/ true);
723     }
724     if (info == nullptr) {
725       LOG(ERROR) << "Cannot load profile info from filename=" << filename << " fd=" << fd;
726       return -1;
727     }
728     *dump += banner + "\n" + info->DumpInfo(MakeNonOwningPointerVector(*dex_files)) + "\n";
729     return 0;
730   }
731 
DumpProfileInfo()732   int DumpProfileInfo() {
733     // Validate that at least one profile file or reference was specified.
734     if (profile_files_.empty() && profile_files_fd_.empty() &&
735         reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
736       Usage("No profile files or reference profile specified.");
737     }
738     static const char* kEmptyString = "";
739     static const char* kOrdinaryProfile = "=== profile ===";
740     static const char* kReferenceProfile = "=== reference profile ===";
741     static const char* kDexFiles = "=== Dex files  ===";
742 
743     std::vector<std::unique_ptr<const DexFile>> dex_files;
744     OpenApkFilesFromLocations(&dex_files);
745 
746     std::string dump;
747 
748     // Dump checkfiles and corresponding checksums.
749     dump += kDexFiles;
750     dump += "\n";
751     for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
752       std::ostringstream oss;
753       oss << dex_file->GetLocation()
754           << " [checksum=" << std::hex << dex_file->GetLocationChecksum() << "]\n";
755       dump += oss.str();
756     }
757 
758     // Dump individual profile files.
759     if (!profile_files_fd_.empty()) {
760       for (int profile_file_fd : profile_files_fd_) {
761         int ret = DumpOneProfile(kOrdinaryProfile,
762                                  kEmptyString,
763                                  profile_file_fd,
764                                  &dex_files,
765                                  &dump);
766         if (ret != 0) {
767           return ret;
768         }
769       }
770     }
771     for (const std::string& profile_file : profile_files_) {
772       int ret = DumpOneProfile(kOrdinaryProfile, profile_file, File::kInvalidFd, &dex_files, &dump);
773       if (ret != 0) {
774         return ret;
775       }
776     }
777     // Dump reference profile file.
778     if (FdIsValid(reference_profile_file_fd_)) {
779       int ret = DumpOneProfile(kReferenceProfile,
780                                kEmptyString,
781                                reference_profile_file_fd_,
782                                &dex_files,
783                                &dump);
784       if (ret != 0) {
785         return ret;
786       }
787     }
788     if (!reference_profile_file_.empty()) {
789       int ret = DumpOneProfile(kReferenceProfile,
790                                reference_profile_file_,
791                                File::kInvalidFd,
792                                &dex_files,
793                                &dump);
794       if (ret != 0) {
795         return ret;
796       }
797     }
798     if (!FdIsValid(dump_output_to_fd_)) {
799       std::cout << dump;
800     } else {
801       unix_file::FdFile out_fd(dump_output_to_fd_, /*check_usage=*/ false);
802       if (!out_fd.WriteFully(dump.c_str(), dump.length())) {
803         return -1;
804       }
805     }
806     return 0;
807   }
808 
ShouldOnlyDumpProfile()809   bool ShouldOnlyDumpProfile() {
810     return dump_only_;
811   }
812 
GetClassNamesAndMethods(const ProfileCompilationInfo & profile_info,std::vector<std::unique_ptr<const DexFile>> * dex_files,std::set<std::string> * out_lines)813   bool GetClassNamesAndMethods(const ProfileCompilationInfo& profile_info,
814                                std::vector<std::unique_ptr<const DexFile>>* dex_files,
815                                std::set<std::string>* out_lines) {
816     for (const std::unique_ptr<const DexFile>& dex_file : *dex_files) {
817       std::set<dex::TypeIndex> class_types;
818       std::set<uint16_t> hot_methods;
819       std::set<uint16_t> startup_methods;
820       std::set<uint16_t> post_startup_methods;
821       std::set<uint16_t> combined_methods;
822       if (profile_info.GetClassesAndMethods(*dex_file.get(),
823                                             &class_types,
824                                             &hot_methods,
825                                             &startup_methods,
826                                             &post_startup_methods)) {
827         for (const dex::TypeIndex& type_index : class_types) {
828           out_lines->insert(profile_info.GetTypeDescriptor(dex_file.get(), type_index));
829         }
830         combined_methods = hot_methods;
831         combined_methods.insert(startup_methods.begin(), startup_methods.end());
832         combined_methods.insert(post_startup_methods.begin(), post_startup_methods.end());
833         for (uint16_t dex_method_idx : combined_methods) {
834           const dex::MethodId& id = dex_file->GetMethodId(dex_method_idx);
835           std::string signature_string(dex_file->GetMethodSignature(id).ToString());
836           std::string type_string(dex_file->GetTypeDescriptor(dex_file->GetTypeId(id.class_idx_)));
837           std::string method_name(dex_file->GetMethodName(id));
838           std::string flags_string;
839           if (hot_methods.find(dex_method_idx) != hot_methods.end()) {
840             flags_string += kMethodFlagStringHot;
841           }
842           if (startup_methods.find(dex_method_idx) != startup_methods.end()) {
843             flags_string += kMethodFlagStringStartup;
844           }
845           if (post_startup_methods.find(dex_method_idx) != post_startup_methods.end()) {
846             flags_string += kMethodFlagStringPostStartup;
847           }
848           FlattenProfileData::ItemMetadata metadata;
849           metadata.ExtractInlineCacheInfo(profile_info, dex_file.get(), dex_method_idx);
850           std::string inline_cache_string = GetInlineCacheLine(metadata.GetInlineCache());
851           out_lines->insert(ART_FORMAT("{}{}{}{}{}{}",
852                                        flags_string,
853                                        type_string,
854                                        kMethodSep,
855                                        method_name,
856                                        signature_string,
857                                        inline_cache_string));
858         }
859       }
860     }
861     return true;
862   }
863 
GetClassNamesAndMethods(int fd,std::vector<std::unique_ptr<const DexFile>> * dex_files,std::set<std::string> * out_lines)864   bool GetClassNamesAndMethods(int fd,
865                                std::vector<std::unique_ptr<const DexFile>>* dex_files,
866                                std::set<std::string>* out_lines) {
867     // For dumping, try loading as app profile and if that fails try loading as boot profile.
868     for (bool for_boot_image : {false, true}) {
869       ProfileCompilationInfo profile_info(for_boot_image);
870       if (profile_info.Load(fd)) {
871         return GetClassNamesAndMethods(profile_info, dex_files, out_lines);
872       }
873     }
874     LOG(ERROR) << "Cannot load profile info";
875     return false;
876   }
877 
GetClassNamesAndMethods(const std::string & profile_file,std::vector<std::unique_ptr<const DexFile>> * dex_files,std::set<std::string> * out_lines)878   bool GetClassNamesAndMethods(const std::string& profile_file,
879                                std::vector<std::unique_ptr<const DexFile>>* dex_files,
880                                std::set<std::string>* out_lines) {
881 #ifdef _WIN32
882     int flags = O_RDONLY;
883 #else
884     int flags = O_RDONLY | O_CLOEXEC;
885 #endif
886     int fd = open(profile_file.c_str(), flags);
887     if (!FdIsValid(fd)) {
888       PLOG(ERROR) << "Cannot open " << profile_file;
889       return false;
890     }
891     if (!GetClassNamesAndMethods(fd, dex_files, out_lines)) {
892       return false;
893     }
894     if (close(fd) < 0) {
895       PLOG(WARNING) << "Failed to close descriptor";
896     }
897     return true;
898   }
899 
DumpClassesAndMethods()900   int DumpClassesAndMethods() {
901     // Validate that at least one profile file or reference was specified.
902     if (profile_files_.empty() && profile_files_fd_.empty() &&
903         reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
904       Usage("No profile files or reference profile specified.");
905     }
906 
907     // Open the dex files to get the names for classes.
908     std::vector<std::unique_ptr<const DexFile>> dex_files;
909     OpenApkFilesFromLocations(&dex_files);
910     // Build a vector of class names from individual profile files.
911     std::set<std::string> class_names;
912     if (!profile_files_fd_.empty()) {
913       for (int profile_file_fd : profile_files_fd_) {
914         if (!GetClassNamesAndMethods(profile_file_fd, &dex_files, &class_names)) {
915           return -1;
916         }
917       }
918     }
919     if (!profile_files_.empty()) {
920       for (const std::string& profile_file : profile_files_) {
921         if (!GetClassNamesAndMethods(profile_file, &dex_files, &class_names)) {
922           return -1;
923         }
924       }
925     }
926     // Concatenate class names from reference profile file.
927     if (FdIsValid(reference_profile_file_fd_)) {
928       if (!GetClassNamesAndMethods(reference_profile_file_fd_, &dex_files, &class_names)) {
929         return -1;
930       }
931     }
932     if (!reference_profile_file_.empty()) {
933       if (!GetClassNamesAndMethods(reference_profile_file_, &dex_files, &class_names)) {
934         return -1;
935       }
936     }
937     // Dump the class names.
938     std::string dump;
939     for (const std::string& class_name : class_names) {
940       dump += class_name + std::string("\n");
941     }
942     if (!FdIsValid(dump_output_to_fd_)) {
943       std::cout << dump;
944     } else {
945       unix_file::FdFile out_fd(dump_output_to_fd_, /*check_usage=*/ false);
946       if (!out_fd.WriteFully(dump.c_str(), dump.length())) {
947         return -1;
948       }
949     }
950     return 0;
951   }
952 
ShouldOnlyDumpClassesAndMethods()953   bool ShouldOnlyDumpClassesAndMethods() {
954     return dump_classes_and_methods_;
955   }
956 
957   // Read lines from the given file, dropping comments and empty lines. Post-process each line with
958   // the given function.
959   template <typename T>
ReadCommentedInputFromFile(const char * input_filename,std::function<std::string (const char *)> * process)960   static T* ReadCommentedInputFromFile(
961       const char* input_filename, std::function<std::string(const char*)>* process) {
962     std::unique_ptr<std::ifstream> input_file(new std::ifstream(input_filename, std::ifstream::in));
963     if (input_file.get() == nullptr) {
964       LOG(ERROR) << "Failed to open input file " << input_filename;
965       return nullptr;
966     }
967     std::unique_ptr<T> result(
968         ReadCommentedInputStream<T>(*input_file, process));
969     input_file->close();
970     return result.release();
971   }
972 
973   // Read lines from the given stream, dropping comments and empty lines. Post-process each line
974   // with the given function.
975   template <typename T>
ReadCommentedInputStream(std::istream & in_stream,std::function<std::string (const char *)> * process)976   static T* ReadCommentedInputStream(
977       std::istream& in_stream,
978       std::function<std::string(const char*)>* process) {
979     std::unique_ptr<T> output(new T());
980     while (in_stream.good()) {
981       std::string dot;
982       std::getline(in_stream, dot);
983       if (dot.starts_with("#") || dot.empty()) {
984         continue;
985       }
986       if (process != nullptr) {
987         std::string descriptor((*process)(dot.c_str()));
988         output->insert(output->end(), descriptor);
989       } else {
990         output->insert(output->end(), dot);
991       }
992     }
993     return output.release();
994   }
995 
996   // Find class definition for a descriptor.
FindClassDef(const std::vector<std::unique_ptr<const DexFile>> & dex_files,std::string_view klass_descriptor,TypeReference * class_ref)997   const dex::ClassDef* FindClassDef(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
998                                     std::string_view klass_descriptor,
999                                     /*out*/ TypeReference* class_ref) {
1000     for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
1001       const dex::TypeId* type_id = dex_file->FindTypeId(klass_descriptor);
1002       if (type_id != nullptr) {
1003         dex::TypeIndex type_index = dex_file->GetIndexForTypeId(*type_id);
1004         const dex::ClassDef* class_def = dex_file->FindClassDef(type_index);
1005         if (class_def != nullptr) {
1006           *class_ref = TypeReference(dex_file.get(), type_index);
1007           return class_def;
1008         }
1009       }
1010     }
1011     return nullptr;
1012   }
1013 
1014   // Find class klass_descriptor in the given dex_files and store its reference
1015   // in the out parameter class_ref.
1016   // Return true if a reference of the class was found in any of the dex_files.
FindClass(const std::vector<std::unique_ptr<const DexFile>> & dex_files,std::string_view klass_descriptor,TypeReference * class_ref)1017   bool FindClass(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
1018                  std::string_view klass_descriptor,
1019                  /*out*/ TypeReference* class_ref) {
1020     for (const std::unique_ptr<const DexFile>& dex_file_ptr : dex_files) {
1021       const DexFile* dex_file = dex_file_ptr.get();
1022       const dex::TypeId* type_id = dex_file->FindTypeId(klass_descriptor);
1023       if (type_id != nullptr) {
1024         *class_ref = TypeReference(dex_file, dex_file->GetIndexForTypeId(*type_id));
1025         return true;
1026       }
1027     }
1028     return false;
1029   }
1030 
1031   // Find the method specified by method_spec in the class class_ref.
FindMethodIndex(const TypeReference & class_ref,std::string_view method_spec)1032   uint32_t FindMethodIndex(const TypeReference& class_ref,
1033                            std::string_view method_spec) {
1034     const DexFile* dex_file = class_ref.dex_file;
1035 
1036     size_t signature_start = method_spec.find(kProfileParsingFirstCharInSignature);
1037     if (signature_start == std::string_view::npos) {
1038       LOG(ERROR) << "Invalid method name and signature: " << method_spec;
1039       return dex::kDexNoIndex;
1040     }
1041 
1042     const std::string_view name = method_spec.substr(0u, signature_start);
1043     const std::string_view signature = method_spec.substr(signature_start);
1044 
1045     const dex::StringId* name_id = dex_file->FindStringId(std::string(name).c_str());
1046     if (name_id == nullptr) {
1047       LOG(WARNING) << "Could not find name: "  << name;
1048       return dex::kDexNoIndex;
1049     }
1050     dex::TypeIndex return_type_idx;
1051     std::vector<dex::TypeIndex> param_type_idxs;
1052     if (!dex_file->CreateTypeList(signature, &return_type_idx, &param_type_idxs)) {
1053       LOG(WARNING) << "Could not create type list: " << signature;
1054       return dex::kDexNoIndex;
1055     }
1056     const dex::ProtoId* proto_id = dex_file->FindProtoId(return_type_idx, param_type_idxs);
1057     if (proto_id == nullptr) {
1058       LOG(WARNING) << "Could not find proto_id: " << name;
1059       return dex::kDexNoIndex;
1060     }
1061     const dex::MethodId* method_id = dex_file->FindMethodId(
1062         dex_file->GetTypeId(class_ref.TypeIndex()), *name_id, *proto_id);
1063     if (method_id == nullptr) {
1064       LOG(WARNING) << "Could not find method_id: " << name;
1065       return dex::kDexNoIndex;
1066     }
1067 
1068     return dex_file->GetIndexForMethodId(*method_id);
1069   }
1070 
1071   template <typename Visitor>
VisitAllInstructions(const TypeReference & class_ref,uint16_t method_idx,Visitor visitor)1072   void VisitAllInstructions(const TypeReference& class_ref, uint16_t method_idx, Visitor visitor) {
1073     const DexFile* dex_file = class_ref.dex_file;
1074     const dex::ClassDef* def = dex_file->FindClassDef(class_ref.TypeIndex());
1075     if (def == nullptr) {
1076       return;
1077     }
1078     std::optional<uint32_t> offset = dex_file->GetCodeItemOffset(*def, method_idx);
1079     if (offset.has_value()) {
1080       for (const DexInstructionPcPair& inst :
1081           CodeItemInstructionAccessor(*dex_file, dex_file->GetCodeItem(*offset))) {
1082         if (!visitor(inst)) {
1083           break;
1084         }
1085       }
1086     } else {
1087       LOG(WARNING) << "Could not find method " << method_idx;
1088     }
1089   }
1090 
1091   // Get dex-pcs of any virtual + interface invokes referencing a method of the
1092   // 'target' type in the given method.
GetAllInvokes(const TypeReference & class_ref,uint16_t method_idx,dex::TypeIndex target,std::vector<uint32_t> * dex_pcs)1093   void GetAllInvokes(const TypeReference& class_ref,
1094                      uint16_t method_idx,
1095                      dex::TypeIndex target,
1096                      /*out*/ std::vector<uint32_t>* dex_pcs) {
1097     const DexFile* dex_file = class_ref.dex_file;
1098     VisitAllInstructions(class_ref, method_idx, [&](const DexInstructionPcPair& inst) -> bool {
1099       switch (inst->Opcode()) {
1100         case Instruction::INVOKE_INTERFACE:
1101         case Instruction::INVOKE_INTERFACE_RANGE:
1102         case Instruction::INVOKE_VIRTUAL:
1103         case Instruction::INVOKE_VIRTUAL_RANGE: {
1104           const dex::MethodId& meth = dex_file->GetMethodId(inst->VRegB());
1105           if (meth.class_idx_ == target) {
1106             dex_pcs->push_back(inst.DexPc());
1107           }
1108           break;
1109         }
1110         default:
1111           break;
1112       }
1113       return true;
1114     });
1115   }
1116 
1117   // Given a method, return true if the method has a single INVOKE_VIRTUAL in its byte code.
1118   // Upon success it returns true and stores the method index and the invoke dex pc
1119   // in the output parameters.
1120   // The format of the method spec is "inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;".
HasSingleInvoke(const TypeReference & class_ref,uint16_t method_index,uint32_t * dex_pc)1121   bool HasSingleInvoke(const TypeReference& class_ref,
1122                        uint16_t method_index,
1123                        /*out*/ uint32_t* dex_pc) {
1124     bool found_invoke = false;
1125     bool found_multiple_invokes = false;
1126     VisitAllInstructions(class_ref, method_index, [&](const DexInstructionPcPair& inst) -> bool {
1127       if (inst->Opcode() == Instruction::INVOKE_VIRTUAL ||
1128           inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE ||
1129           inst->Opcode() == Instruction::INVOKE_INTERFACE ||
1130           inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE) {
1131         if (found_invoke) {
1132           LOG(ERROR) << "Multiple invoke INVOKE_VIRTUAL found: "
1133                      << class_ref.dex_file->PrettyMethod(method_index);
1134           return false;
1135         }
1136         found_invoke = true;
1137         *dex_pc = inst.DexPc();
1138       }
1139       return true;
1140     });
1141     if (!found_invoke) {
1142       LOG(ERROR) << "Could not find any INVOKE_VIRTUAL/INTERFACE: "
1143                  << class_ref.dex_file->PrettyMethod(method_index);
1144     }
1145     return found_invoke && !found_multiple_invokes;
1146   }
1147 
1148   struct InlineCacheSegment {
1149    public:
1150     using IcArray =
1151         std::array<std::string_view, ProfileCompilationInfo::kIndividualInlineCacheSize + 1>;
SplitInlineCacheSegmentart::ProfMan::InlineCacheSegment1152     static void SplitInlineCacheSegment(std::string_view ic_line,
1153                                         /*out*/ std::vector<InlineCacheSegment>* res) {
1154       if (ic_line[0] != kProfileParsingInlineChacheTargetSep) {
1155         // single target
1156         InlineCacheSegment out;
1157         Split(ic_line, kProfileParsingTypeSep, &out.inline_caches_);
1158         res->push_back(out);
1159         return;
1160       }
1161       std::vector<std::string_view> targets_and_resolutions;
1162       // Avoid a zero-length entry.
1163       for (std::string_view t :
1164            SplitString(ic_line.substr(1), kProfileParsingInlineChacheTargetSep)) {
1165         InlineCacheSegment out;
1166         // The target may be an array for methods defined in `j.l.Object`, such as `clone()`.
1167         size_t recv_end;
1168         if (UNLIKELY(t[0] == '[')) {
1169           recv_end = t.find_first_not_of('[', 1u);
1170           DCHECK_NE(recv_end, std::string_view::npos);
1171           if (t[recv_end] == 'L') {
1172             recv_end = t.find_first_of(';', recv_end + 1u);
1173             DCHECK_NE(recv_end, std::string_view::npos);
1174           } else {
1175             // Primitive array.
1176             DCHECK_NE(Primitive::GetType(t[recv_end]), Primitive::kPrimNot);
1177           }
1178         } else {
1179           DCHECK_EQ(t[0], 'L') << "Target is not a class? " << t;
1180           recv_end = t.find_first_of(';', 1u);
1181           DCHECK_NE(recv_end, std::string_view::npos);
1182         }
1183         out.receiver_ = t.substr(0, recv_end + 1);
1184         Split(t.substr(recv_end + 1), kProfileParsingTypeSep, &out.inline_caches_);
1185         res->push_back(out);
1186       }
1187     }
1188 
IsSingleReceiverart::ProfMan::InlineCacheSegment1189     bool IsSingleReceiver() const {
1190       return !receiver_.has_value();
1191     }
1192 
GetReceiverTypeart::ProfMan::InlineCacheSegment1193     std::string_view GetReceiverType() const {
1194       DCHECK(!IsSingleReceiver());
1195       return *receiver_;
1196     }
1197 
GetIcTargetsart::ProfMan::InlineCacheSegment1198     const IcArray& GetIcTargets() const {
1199       return inline_caches_;
1200     }
1201 
NumIcTargetsart::ProfMan::InlineCacheSegment1202     size_t NumIcTargets() const {
1203       return std::count_if(
1204           inline_caches_.begin(), inline_caches_.end(), [](const auto& x) { return !x.empty(); });
1205     }
1206 
Dumpart::ProfMan::InlineCacheSegment1207     std::ostream& Dump(std::ostream& os) const {
1208       if (!IsSingleReceiver()) {
1209         os << "[" << GetReceiverType();
1210       }
1211       bool first = true;
1212       for (std::string_view target : inline_caches_) {
1213         if (target.empty()) {
1214           break;
1215         } else if (!first) {
1216           os << ",";
1217         }
1218         first = false;
1219         os << target;
1220       }
1221       return os;
1222     }
1223 
1224    private:
1225     std::optional<std::string_view> receiver_;
1226     // Max number of ics in the profile file. Don't need to store more than this
1227     // (although internally we can have as many as we want). If we fill this up
1228     // we are megamorphic.
1229     IcArray inline_caches_;
1230 
1231     friend std::ostream& operator<<(std::ostream& os, const InlineCacheSegment& ics);
1232   };
1233 
1234   struct ClassMethodReference {
1235     TypeReference type_;
1236     uint32_t method_index_;
1237 
operator ==art::ProfMan::ClassMethodReference1238     bool operator==(const ClassMethodReference& ref) {
1239       return ref.type_ == type_ && ref.method_index_ == method_index_;
1240     }
operator !=art::ProfMan::ClassMethodReference1241     bool operator!=(const ClassMethodReference& ref) {
1242       return !(*this == ref);
1243     }
1244   };
1245 
1246   // Try to perform simple method resolution to produce a more useful profile.
1247   // This will resolve to the nearest class+method-index which is within the
1248   // same dexfile and in a declared supertype of the starting class. It will
1249   // return nullopt if it cannot find an appropriate method or the nearest
1250   // possibility is private.
1251   // TODO: This should ideally support looking in other dex files. That's getting
1252   // to the point of needing to have a whole class-linker so it's probably not
1253   // worth it.
ResolveMethod(TypeReference class_ref,uint32_t method_index)1254   std::optional<ClassMethodReference> ResolveMethod(TypeReference class_ref,
1255                                                     uint32_t method_index) {
1256     const DexFile* dex = class_ref.dex_file;
1257     const dex::ClassDef* def = dex->FindClassDef(class_ref.TypeIndex());
1258     if (def == nullptr || method_index >= dex->NumMethodIds()) {
1259       // Class not in dex-file.
1260       return std::nullopt;
1261     }
1262     if (dex->GetClassData(*def) == nullptr) {
1263       // Class has no fields or methods.
1264       return std::nullopt;
1265     }
1266     if (LIKELY(dex->GetCodeItemOffset(*def, method_index).has_value())) {
1267       return ClassMethodReference{class_ref, method_index};
1268     }
1269     // What to look for.
1270     const dex::MethodId& method_id = dex->GetMethodId(method_index);
1271     // No going between different dexs so use name and proto directly
1272     const dex::ProtoIndex& method_proto = method_id.proto_idx_;
1273     const dex::StringIndex& method_name = method_id.name_idx_;
1274     // Floyd's algo to prevent infinite loops.
1275     // Slow-iterator position for Floyd's
1276     dex::TypeIndex slow_class_type = def->class_idx_;
1277     // Whether to take a step with the slow iterator.
1278     bool update_slow = false;
1279     for (dex::TypeIndex cur_candidate = def->superclass_idx_;
1280          cur_candidate != dex::TypeIndex::Invalid() && cur_candidate != slow_class_type;) {
1281       const dex::ClassDef* cur_class_def = dex->FindClassDef(cur_candidate);
1282       if (cur_class_def == nullptr) {
1283         // We left the dex file.
1284         return std::nullopt;
1285       }
1286       const dex::MethodId* cur_id =
1287           dex->FindMethodIdByIndex(cur_candidate, method_name, method_proto);
1288       if (cur_id != nullptr) {
1289         if (dex->GetCodeItemOffset(*cur_class_def, dex->GetIndexForMethodId(*cur_id)).has_value()) {
1290           return ClassMethodReference{TypeReference(dex, cur_candidate),
1291                                       dex->GetIndexForMethodId(*cur_id)};
1292         }
1293       }
1294       // Floyd's algo step.
1295       cur_candidate = cur_class_def->superclass_idx_;
1296       slow_class_type =
1297           update_slow ? dex->FindClassDef(slow_class_type)->superclass_idx_ : slow_class_type;
1298       update_slow = !update_slow;
1299     }
1300     return std::nullopt;
1301   }
1302 
1303   // Process a line defining a class or a method and its inline caches.
1304   // Upon success return true and add the class or the method info to profile.
1305   // Inline caches are identified by the type of the declared receiver type.
1306   // The possible line formats are:
1307   // "LJustTheClass;".
1308   // "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;".
1309   // "LTestInline;->inlineMissingTypes(LSuper;)I+missing_types".
1310   // // Note no ',' after [LTarget;
1311   // "LTestInline;->multiInlinePolymorphic(LSuper;)I+]LTarget1;LResA;,LResB;]LTarget2;LResC;,LResD;".
1312   // "LTestInline;->multiInlinePolymorphic(LSuper;)I+]LTarget1;missing_types]LTarget2;LResC;,LResD;".
1313   // "{annotation}LTestInline;->inlineNoInlineCaches(LSuper;)I".
1314   // "LTestInline;->*".
1315   // The method and classes are searched only in the given dex files.
ProcessLine(const std::vector<std::unique_ptr<const DexFile>> & dex_files,std::string_view maybe_annotated_line,ProfileCompilationInfo * profile)1316   bool ProcessLine(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
1317                    std::string_view maybe_annotated_line,
1318                    /*out*/ProfileCompilationInfo* profile) {
1319     // First, process the annotation.
1320     if (maybe_annotated_line.empty()) {
1321       return true;
1322     }
1323     // Working line variable which will contain the user input without the annotations.
1324     std::string_view line = maybe_annotated_line;
1325 
1326     std::string_view annotation_string;
1327     if (maybe_annotated_line[0] == kAnnotationStart) {
1328       size_t end_pos = maybe_annotated_line.find(kAnnotationEnd, 0);
1329       if (end_pos == std::string::npos || end_pos == 0) {
1330         LOG(ERROR) << "Invalid line: " << maybe_annotated_line;
1331         return false;
1332       }
1333       annotation_string = maybe_annotated_line.substr(1, end_pos - 1);
1334       // Update the working line.
1335       line = maybe_annotated_line.substr(end_pos + 1);
1336     }
1337 
1338     ProfileSampleAnnotation annotation = annotation_string.empty()
1339         ? ProfileSampleAnnotation::kNone
1340         : ProfileSampleAnnotation(std::string(annotation_string));
1341 
1342     // Now process the rest of the line.
1343     std::string_view klass;
1344     std::string_view method_str;
1345     bool is_hot = false;
1346     bool is_startup = false;
1347     bool is_post_startup = false;
1348     const size_t method_sep_index = line.find(kMethodSep, 0);
1349     if (method_sep_index == std::string::npos) {
1350       klass = line;
1351     } else {
1352       // The method prefix flags are only valid for method strings.
1353       size_t start_index = 0;
1354       while (start_index < line.size() && line[start_index] != 'L') {
1355         const char c = line[start_index];
1356         if (c == kMethodFlagStringHot) {
1357           is_hot = true;
1358         } else if (c == kMethodFlagStringStartup) {
1359           is_startup = true;
1360         } else if (c == kMethodFlagStringPostStartup) {
1361           is_post_startup = true;
1362         } else {
1363           LOG(WARNING) << "Invalid flag " << c;
1364           return false;
1365         }
1366         ++start_index;
1367       }
1368       klass = line.substr(start_index, method_sep_index - start_index);
1369       method_str = line.substr(method_sep_index + kMethodSep.size());
1370     }
1371 
1372     if (!IsValidDescriptor(std::string(klass).c_str())) {
1373       LOG(ERROR) << "Invalid descriptor: " << klass;
1374       return false;
1375     }
1376 
1377     if (method_str.empty()) {
1378       auto array_it = std::find_if(klass.begin(), klass.end(), [](char c) { return c != '['; });
1379       size_t array_dim = std::distance(klass.begin(), array_it);
1380       if (klass.size() == array_dim + 1u) {
1381         // Attribute primitive types and their arrays to the first dex file.
1382         profile->AddClass(*dex_files[0], klass, annotation);
1383         return true;
1384       }
1385       // Attribute non-primitive classes and their arrays to the dex file with the definition.
1386       TypeReference class_ref(/* dex_file= */ nullptr, dex::TypeIndex());
1387       if (FindClassDef(dex_files, klass.substr(array_dim), &class_ref) == nullptr) {
1388         LOG(WARNING) << "Could not find class definition: " << klass.substr(array_dim);
1389         return false;
1390       }
1391       if (array_dim != 0) {
1392         // Let the ProfileCompilationInfo find the type index or add an extra descriptor.
1393         return profile->AddClass(*class_ref.dex_file, klass, annotation);
1394       } else {
1395         return profile->AddClass(*class_ref.dex_file, class_ref.TypeIndex(), annotation);
1396       }
1397     }
1398 
1399     DCHECK_NE(klass[0], '[');
1400     TypeReference class_ref(/* dex_file= */ nullptr, dex::TypeIndex());
1401     const dex::ClassDef* class_def = FindClassDef(dex_files, klass, &class_ref);
1402     if (class_def == nullptr) {
1403       LOG(WARNING) << "Could not find class definition: " << klass;
1404       return false;
1405     }
1406 
1407     uint32_t flags = 0;
1408     if (is_hot) {
1409       flags |= ProfileCompilationInfo::MethodHotness::kFlagHot;
1410     }
1411     if (is_startup) {
1412       flags |= ProfileCompilationInfo::MethodHotness::kFlagStartup;
1413     }
1414     if (is_post_startup) {
1415       flags |= ProfileCompilationInfo::MethodHotness::kFlagPostStartup;
1416     }
1417 
1418     if (method_str == kClassAllMethods) {
1419       // Start by adding the class.
1420       profile->AddClass(*class_ref.dex_file, class_ref.TypeIndex(), annotation);
1421       uint16_t class_def_index = class_ref.dex_file->GetIndexForClassDef(*class_def);
1422       ClassAccessor accessor(*class_ref.dex_file, class_def_index);
1423       std::vector<ProfileMethodInfo> methods;
1424       for (const ClassAccessor::Method& method : accessor.GetMethods()) {
1425         if (method.GetCodeItemOffset() != 0) {
1426           // Add all of the methods that have code to the profile.
1427           methods.push_back(ProfileMethodInfo(method.GetReference()));
1428         }
1429       }
1430       // TODO: Check return value?
1431       profile->AddMethods(
1432           methods, static_cast<ProfileCompilationInfo::MethodHotness::Flag>(flags), annotation);
1433       return true;
1434     }
1435 
1436     // Process the method.
1437     std::string method_spec;
1438 
1439     // If none of the flags are set, default to hot.
1440     // TODO: Why is this done after we have already calculated `flags`?
1441     is_hot = is_hot || (!is_hot && !is_startup && !is_post_startup);
1442 
1443     // Lifetime of segments is same as method_elems since it contains pointers into the string-data
1444     std::vector<InlineCacheSegment> segments;
1445     std::vector<std::string_view> method_elems;
1446     Split(method_str, kProfileParsingInlineChacheSep, &method_elems);
1447     if (method_elems.size() == 2) {
1448       method_spec = method_elems[0];
1449       InlineCacheSegment::SplitInlineCacheSegment(method_elems[1], &segments);
1450     } else if (method_elems.size() == 1) {
1451       method_spec = method_elems[0];
1452     } else {
1453       LOG(ERROR) << "Invalid method line: " << line;
1454       return false;
1455     }
1456 
1457     const uint32_t method_index = FindMethodIndex(class_ref, method_spec);
1458     if (method_index == dex::kDexNoIndex) {
1459       LOG(WARNING) << "Could not find method " << klass << "->" << method_spec;
1460       return false;
1461     }
1462 
1463     std::optional<ClassMethodReference>
1464         resolved_class_method_ref = ResolveMethod(class_ref, method_index);
1465 
1466     std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches;
1467     // We can only create inline-caches when we actually have code we can
1468     // examine. If we couldn't resolve the method don't bother trying to create
1469     // inline-caches.
1470     if (resolved_class_method_ref) {
1471       for (const InlineCacheSegment& segment : segments) {
1472         std::vector<uint32_t> dex_pcs;
1473         if (segment.IsSingleReceiver()) {
1474           DCHECK_EQ(segments.size(), 1u);
1475           dex_pcs.resize(1, -1);
1476           // TODO This single invoke format should really be phased out and
1477           // removed.
1478           if (!HasSingleInvoke(class_ref, method_index, &dex_pcs[0])) {
1479             return false;
1480           }
1481         } else {
1482           // Get the type-ref the method code will use.
1483           std::string_view receiver_descriptor = segment.GetReceiverType();
1484           const dex::TypeId *type_id = class_ref.dex_file->FindTypeId(receiver_descriptor);
1485           if (type_id == nullptr) {
1486             LOG(WARNING) << "Could not find class: "
1487                          << segment.GetReceiverType() << " in dex-file "
1488                          << class_ref.dex_file << ". Ignoring IC group: '"
1489                          << segment << "'";
1490             continue;
1491           }
1492           dex::TypeIndex target_index =
1493               class_ref.dex_file->GetIndexForTypeId(*type_id);
1494 
1495           GetAllInvokes(resolved_class_method_ref->type_,
1496                         resolved_class_method_ref->method_index_,
1497                         target_index,
1498                         &dex_pcs);
1499         }
1500         bool missing_types = segment.GetIcTargets()[0] == kMissingTypesMarker;
1501         bool megamorphic_types =
1502             segment.GetIcTargets()[0] == kMegamorphicTypesMarker;
1503         std::vector<TypeReference> classes;
1504         if (!missing_types && !megamorphic_types) {
1505           classes.reserve(segment.NumIcTargets());
1506           for (const std::string_view& ic_class : segment.GetIcTargets()) {
1507             if (ic_class.empty()) {
1508               break;
1509             }
1510             if (!IsValidDescriptor(std::string(ic_class).c_str())) {
1511               LOG(ERROR) << "Invalid descriptor for inline cache: " << ic_class;
1512               return false;
1513             }
1514             // TODO: Allow referencing classes without a `dex::TypeId` in any of the dex files.
1515             TypeReference ic_class_ref(/* dex_file= */ nullptr, dex::TypeIndex());
1516             if (!FindClass(dex_files, ic_class, &ic_class_ref)) {
1517               LOG(segment.IsSingleReceiver() ? ERROR : WARNING)
1518                   << "Could not find class: " << ic_class << " in " << segment;
1519               if (segment.IsSingleReceiver()) {
1520                 return false;
1521               } else {
1522                 // Be a bit more forgiving with profiles from servers.
1523                 missing_types = true;
1524                 classes.clear();
1525                 break;
1526               }
1527             }
1528             classes.push_back(ic_class_ref);
1529           }
1530         }
1531         for (size_t dex_pc : dex_pcs) {
1532           inline_caches.emplace_back(dex_pc, missing_types, classes, megamorphic_types);
1533         }
1534       }
1535     }
1536     MethodReference ref(class_ref.dex_file, method_index);
1537     if (is_hot) {
1538       ClassMethodReference orig_cmr { class_ref, method_index };
1539       if (!inline_caches.empty() &&
1540           resolved_class_method_ref &&
1541           orig_cmr != *resolved_class_method_ref) {
1542         // We have inline-caches on a method that doesn't actually exist. We
1543         // want to put the inline caches on the resolved version of the method
1544         // (if we could find one) and just mark the actual method as present.
1545         const DexFile *dex = resolved_class_method_ref->type_.dex_file;
1546         LOG(VERBOSE) << "Adding "
1547                      << dex->PrettyMethod(
1548                             resolved_class_method_ref->method_index_)
1549                      << " as alias for " << dex->PrettyMethod(method_index);
1550         // The inline-cache refers to a supertype of the actual profile line.
1551         // Include this supertype method in the profile as well.
1552         MethodReference resolved_ref(class_ref.dex_file,
1553                                      resolved_class_method_ref->method_index_);
1554         profile->AddMethod(
1555             ProfileMethodInfo(resolved_ref, inline_caches),
1556             static_cast<ProfileCompilationInfo::MethodHotness::Flag>(flags),
1557             annotation);
1558         profile->AddMethod(
1559             ProfileMethodInfo(ref),
1560             static_cast<ProfileCompilationInfo::MethodHotness::Flag>(flags),
1561             annotation);
1562       } else {
1563         profile->AddMethod(
1564             ProfileMethodInfo(ref, inline_caches),
1565             static_cast<ProfileCompilationInfo::MethodHotness::Flag>(flags),
1566             annotation);
1567       }
1568     }
1569     if (flags != 0) {
1570       if (!profile->AddMethod(ProfileMethodInfo(ref),
1571                               static_cast<ProfileCompilationInfo::MethodHotness::Flag>(flags),
1572                               annotation)) {
1573         return false;
1574       }
1575       DCHECK(profile->GetMethodHotness(ref, annotation).IsInProfile()) << method_spec;
1576     }
1577     return true;
1578   }
1579 
ProcessBootLine(const std::vector<std::unique_ptr<const DexFile>> & dex_files,std::string_view line,ProfileBootInfo * boot_profiling_info)1580   bool ProcessBootLine(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
1581                        std::string_view line,
1582                        ProfileBootInfo* boot_profiling_info) {
1583     const size_t method_sep_index = line.find(kMethodSep, 0);
1584     if (method_sep_index == std::string_view::npos) {
1585       LOG(ERROR) << "Invalid boot line: " << line;
1586       return false;
1587     }
1588     std::string_view klass_str = line.substr(0, method_sep_index);
1589     std::string_view method_str = line.substr(method_sep_index + kMethodSep.size());
1590 
1591     TypeReference class_ref(/* dex_file= */ nullptr, dex::TypeIndex());
1592     if (FindClassDef(dex_files, klass_str, &class_ref) == nullptr) {
1593       LOG(WARNING) << "Could not find class definition: " << klass_str;
1594       return false;
1595     }
1596 
1597     const uint32_t method_index = FindMethodIndex(class_ref, method_str);
1598     if (method_index == dex::kDexNoIndex) {
1599       LOG(WARNING) << "Could not find method: " << line;
1600       return false;
1601     }
1602     boot_profiling_info->Add(class_ref.dex_file, method_index);
1603     return true;
1604   }
1605 
OpenReferenceProfile() const1606   int OpenReferenceProfile() const {
1607     int fd = reference_profile_file_fd_;
1608     if (!FdIsValid(fd)) {
1609       CHECK(!reference_profile_file_.empty());
1610 #ifdef _WIN32
1611       int flags = O_CREAT | O_TRUNC | O_WRONLY;
1612 #else
1613       int flags = O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC;
1614 #endif
1615       fd = open(reference_profile_file_.c_str(), flags, 0644);
1616       if (fd < 0) {
1617         PLOG(ERROR) << "Cannot open " << reference_profile_file_;
1618         return File::kInvalidFd;
1619       }
1620     }
1621     return fd;
1622   }
1623 
1624   // Create and store a ProfileBootInfo.
CreateBootProfile()1625   int CreateBootProfile() {
1626     // Validate parameters for this command.
1627     if (apk_files_.empty() && apks_fd_.empty()) {
1628       Usage("APK files must be specified");
1629     }
1630     if (dex_locations_.empty()) {
1631       Usage("DEX locations must be specified");
1632     }
1633     if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
1634       Usage("Reference profile must be specified with --reference-profile-file or "
1635             "--reference-profile-file-fd");
1636     }
1637     if (!profile_files_.empty() || !profile_files_fd_.empty()) {
1638       Usage("Profile must be specified with --reference-profile-file or "
1639             "--reference-profile-file-fd");
1640     }
1641     // Open the profile output file if needed.
1642     int fd = OpenReferenceProfile();
1643     if (!FdIsValid(fd)) {
1644         return -1;
1645     }
1646     // Read the user-specified list of methods.
1647     std::unique_ptr<std::vector<std::string>>
1648         user_lines(ReadCommentedInputFromFile<std::vector<std::string>>(
1649             create_profile_from_file_.c_str(), nullptr));  // No post-processing.
1650 
1651     // Open the dex files to look up classes and methods.
1652     std::vector<std::unique_ptr<const DexFile>> dex_files;
1653     OpenApkFilesFromLocations(&dex_files);
1654 
1655     // Process the lines one by one and add the successful ones to the profile.
1656     ProfileBootInfo info;
1657 
1658     for (const auto& line : *user_lines) {
1659       ProcessBootLine(dex_files, line, &info);
1660     }
1661 
1662     // Write the profile file.
1663     CHECK(info.Save(fd));
1664 
1665     if (close(fd) < 0) {
1666       PLOG(WARNING) << "Failed to close descriptor";
1667     }
1668 
1669     return 0;
1670   }
1671 
1672   // Creates a profile from a human friendly textual representation.
1673   // The expected input format is:
1674   //   # Classes
1675   //   Ljava/lang/Comparable;
1676   //   Ljava/lang/Math;
1677   //   # Methods with inline caches
1678   //   LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;
1679   //   LTestInline;->noInlineCache(LSuper;)I
CreateProfile()1680   int CreateProfile() {
1681     // Validate parameters for this command.
1682     if (apk_files_.empty() && apks_fd_.empty()) {
1683       Usage("APK files must be specified");
1684     }
1685     if (dex_locations_.empty()) {
1686       Usage("DEX locations must be specified");
1687     }
1688     if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
1689       Usage("Reference profile must be specified with --reference-profile-file or "
1690             "--reference-profile-file-fd");
1691     }
1692     if (!profile_files_.empty() || !profile_files_fd_.empty()) {
1693       Usage("Profile must be specified with --reference-profile-file or "
1694             "--reference-profile-file-fd");
1695     }
1696     // Open the profile output file if needed.
1697     int fd = OpenReferenceProfile();
1698     if (!FdIsValid(fd)) {
1699         return -1;
1700     }
1701     // Read the user-specified list of classes and methods.
1702     std::unique_ptr<std::unordered_set<std::string>>
1703         user_lines(ReadCommentedInputFromFile<std::unordered_set<std::string>>(
1704             create_profile_from_file_.c_str(), nullptr));  // No post-processing.
1705 
1706     // Open the dex files to look up classes and methods.
1707     std::vector<std::unique_ptr<const DexFile>> dex_files;
1708     OpenApkFilesFromLocations(&dex_files);
1709 
1710     // Process the lines one by one and add the successful ones to the profile.
1711     bool for_boot_image = GetOutputProfileType() == OutputProfileType::kBoot;
1712     ProfileCompilationInfo info(for_boot_image);
1713 
1714     if (for_boot_image) {
1715       // Add all dex files to the profile. This is needed for jitzygote to indicate
1716       // which dex files are part of the boot image extension to compile in memory.
1717       for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
1718         if (info.FindOrAddDexFile(*dex_file) == info.MaxProfileIndex()) {
1719           LOG(ERROR) << "Failed to add dex file to boot image profile: " << dex_file->GetLocation();
1720           return -1;
1721         }
1722       }
1723     }
1724 
1725     for (const auto& line : *user_lines) {
1726       ProcessLine(dex_files, line, &info);
1727     }
1728 
1729     // Write the profile file.
1730     CHECK(info.Save(fd));
1731     if (close(fd) < 0) {
1732       PLOG(WARNING) << "Failed to close descriptor";
1733     }
1734     return 0;
1735   }
1736 
ShouldCreateBootImageProfile() const1737   bool ShouldCreateBootImageProfile() const {
1738     return generate_boot_image_profile_;
1739   }
1740 
GetOutputProfileType() const1741   OutputProfileType GetOutputProfileType() const {
1742     return output_profile_type_;
1743   }
1744 
1745   // Create and store a ProfileCompilationInfo for the boot image.
CreateBootImageProfile()1746   int CreateBootImageProfile() {
1747     // Open the input profile file.
1748     if (profile_files_.size() < 1) {
1749       LOG(ERROR) << "At least one --profile-file must be specified.";
1750       return -1;
1751     }
1752     // Open the dex files.
1753     std::vector<std::unique_ptr<const DexFile>> dex_files;
1754     OpenApkFilesFromLocations(&dex_files);
1755     if (dex_files.empty()) {
1756       PLOG(ERROR) << "Expected dex files for creating boot profile";
1757       return -2;
1758     }
1759 
1760     if (!GenerateBootImageProfile(dex_files,
1761                                   profile_files_,
1762                                   boot_image_options_,
1763                                   boot_profile_out_path_,
1764                                   preloaded_classes_out_path_)) {
1765       LOG(ERROR) << "There was an error when generating the boot image profiles";
1766       return -4;
1767     }
1768     return 0;
1769   }
1770 
ShouldCreateProfile()1771   bool ShouldCreateProfile() {
1772     return !create_profile_from_file_.empty();
1773   }
1774 
GenerateTestProfile()1775   int GenerateTestProfile() {
1776     // Validate parameters for this command.
1777     if (test_profile_method_percerntage_ > 100) {
1778       Usage("Invalid percentage for --generate-test-profile-method-percentage");
1779     }
1780     if (test_profile_class_percentage_ > 100) {
1781       Usage("Invalid percentage for --generate-test-profile-class-percentage");
1782     }
1783     // If given APK files or DEX locations, check that they're ok.
1784     if (!apk_files_.empty() || !apks_fd_.empty() || !dex_locations_.empty()) {
1785       if (apk_files_.empty() && apks_fd_.empty()) {
1786         Usage("APK files must be specified when passing DEX locations to --generate-test-profile");
1787       }
1788       if (dex_locations_.empty()) {
1789         Usage("DEX locations must be specified when passing APK files to --generate-test-profile");
1790       }
1791     }
1792     // ShouldGenerateTestProfile confirms !test_profile_.empty().
1793 #ifdef _WIN32
1794     int flags = O_CREAT | O_TRUNC | O_WRONLY;
1795 #else
1796     int flags = O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC;
1797 #endif
1798     int profile_test_fd = open(test_profile_.c_str(), flags, 0644);
1799     if (profile_test_fd < 0) {
1800       PLOG(ERROR) << "Cannot open " << test_profile_;
1801       return -1;
1802     }
1803     bool result;
1804     if (apk_files_.empty() && apks_fd_.empty() && dex_locations_.empty()) {
1805       result = ProfileCompilationInfo::GenerateTestProfile(profile_test_fd,
1806                                                            test_profile_num_dex_,
1807                                                            test_profile_method_percerntage_,
1808                                                            test_profile_class_percentage_,
1809                                                            test_profile_seed_);
1810     } else {
1811       // Open the dex files to look up classes and methods.
1812       std::vector<std::unique_ptr<const DexFile>> dex_files;
1813       OpenApkFilesFromLocations(&dex_files);
1814       // Create a random profile file based on the set of dex files.
1815       result = ProfileCompilationInfo::GenerateTestProfile(profile_test_fd,
1816                                                            dex_files,
1817                                                            test_profile_method_percerntage_,
1818                                                            test_profile_class_percentage_,
1819                                                            test_profile_seed_);
1820     }
1821     close(profile_test_fd);  // ignore close result.
1822     return result ? 0 : -1;
1823   }
1824 
ShouldGenerateTestProfile()1825   bool ShouldGenerateTestProfile() {
1826     return !test_profile_.empty();
1827   }
1828 
ShouldCopyAndUpdateProfileKey() const1829   bool ShouldCopyAndUpdateProfileKey() const {
1830     return copy_and_update_profile_key_;
1831   }
1832 
CopyAndUpdateProfileKey()1833   ProfmanResult::CopyAndUpdateResult CopyAndUpdateProfileKey() {
1834     // Validate that at least one profile file was passed, as well as a reference profile.
1835     if (!(profile_files_.size() == 1 ^ profile_files_fd_.size() == 1)) {
1836       Usage("Only one profile file should be specified.");
1837     }
1838     if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
1839       Usage("No reference profile file specified.");
1840     }
1841 
1842     if (apk_files_.empty() && apks_fd_.empty()) {
1843       Usage("No apk files specified");
1844     }
1845 
1846     bool use_fds = profile_files_fd_.size() == 1;
1847 
1848     ProfileCompilationInfo profile;
1849     // Do not clear if invalid. The input might be an archive.
1850     bool load_ok = use_fds
1851         ? profile.Load(profile_files_fd_[0])
1852         : profile.Load(profile_files_[0], /*clear_if_invalid=*/ false);
1853     if (load_ok) {
1854       // Open the dex files to look up classes and methods.
1855       std::vector<std::unique_ptr<const DexFile>> dex_files;
1856       OpenApkFilesFromLocations(&dex_files);
1857       bool matched = false;
1858       if (!profile.UpdateProfileKeys(dex_files, &matched)) {
1859         return ProfmanResult::kCopyAndUpdateErrorFailedToUpdateProfile;
1860       }
1861       bool result = use_fds
1862           ? profile.Save(reference_profile_file_fd_)
1863           : profile.Save(reference_profile_file_, /*bytes_written=*/ nullptr);
1864       if (!result) {
1865         return ProfmanResult::kCopyAndUpdateErrorFailedToSaveProfile;
1866       }
1867       return matched ? ProfmanResult::kCopyAndUpdateSuccess : ProfmanResult::kCopyAndUpdateNoMatch;
1868     } else {
1869       return ProfmanResult::kCopyAndUpdateErrorFailedToLoadProfile;
1870     }
1871   }
1872 
1873  private:
ParseFdForCollection(const char * raw_option,std::string_view option_prefix,std::vector<int> * fds)1874   static void ParseFdForCollection(const char* raw_option,
1875                                    std::string_view option_prefix,
1876                                    std::vector<int>* fds) {
1877     int fd;
1878     ParseUintOption(raw_option, option_prefix, &fd);
1879     fds->push_back(fd);
1880   }
1881 
CloseAllFds(const std::vector<int> & fds,const char * descriptor)1882   static void CloseAllFds(const std::vector<int>& fds, const char* descriptor) {
1883     for (size_t i = 0; i < fds.size(); i++) {
1884       if (close(fds[i]) < 0) {
1885         PLOG(WARNING) << "Failed to close descriptor for "
1886             << descriptor << " at index " << i << ": " << fds[i];
1887       }
1888     }
1889   }
1890 
LogCompletionTime()1891   void LogCompletionTime() {
1892     static constexpr uint64_t kLogThresholdTime = MsToNs(100);  // 100ms
1893     uint64_t time_taken = NanoTime() - start_ns_;
1894     if (time_taken > kLogThresholdTime) {
1895       LOG(WARNING) << "profman took " << PrettyDuration(time_taken);
1896     }
1897   }
1898 
1899   std::vector<std::string> profile_files_;
1900   std::vector<int> profile_files_fd_;
1901   std::vector<std::string> dex_locations_;
1902   std::vector<std::string> apk_files_;
1903   std::vector<int> apks_fd_;
1904   std::string reference_profile_file_;
1905   int reference_profile_file_fd_;
1906   bool dump_only_;
1907   bool dump_classes_and_methods_;
1908   bool generate_boot_image_profile_;
1909   OutputProfileType output_profile_type_;
1910   int dump_output_to_fd_;
1911   BootImageOptions boot_image_options_;
1912   std::string test_profile_;
1913   std::string create_profile_from_file_;
1914   uint16_t test_profile_num_dex_;
1915   uint16_t test_profile_method_percerntage_;
1916   uint16_t test_profile_class_percentage_;
1917   uint32_t test_profile_seed_;
1918   uint64_t start_ns_;
1919   bool copy_and_update_profile_key_;
1920   ProfileAssistant::Options profile_assistant_options_;
1921   std::string boot_profile_out_path_;
1922   std::string preloaded_classes_out_path_;
1923 };
1924 
operator <<(std::ostream & os,const ProfMan::InlineCacheSegment & ics)1925 std::ostream& operator<<(std::ostream& os, const ProfMan::InlineCacheSegment& ics) {
1926   return ics.Dump(os);
1927 }
1928 
1929 // See ProfmanResult for return codes.
profman(int argc,char ** argv)1930 static int profman(int argc, char** argv) {
1931   ProfMan profman;
1932 
1933   // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
1934   profman.ParseArgs(argc, argv);
1935 
1936   // Initialize MemMap for ZipArchive::OpenFromFd.
1937   MemMap::Init();
1938 
1939   if (profman.ShouldGenerateTestProfile()) {
1940     return profman.GenerateTestProfile();
1941   }
1942   if (profman.ShouldOnlyDumpProfile()) {
1943     return profman.DumpProfileInfo();
1944   }
1945   if (profman.ShouldOnlyDumpClassesAndMethods()) {
1946     return profman.DumpClassesAndMethods();
1947   }
1948   if (profman.ShouldCreateProfile()) {
1949     if (profman.GetOutputProfileType() == OutputProfileType::kBprof) {
1950       return profman.CreateBootProfile();
1951     } else {
1952       return profman.CreateProfile();
1953     }
1954   }
1955 
1956   if (profman.ShouldCreateBootImageProfile()) {
1957     return profman.CreateBootImageProfile();
1958   }
1959 
1960   if (profman.ShouldCopyAndUpdateProfileKey()) {
1961     return profman.CopyAndUpdateProfileKey();
1962   }
1963 
1964   // Process profile information and assess if we need to do a profile guided compilation.
1965   // This operation involves I/O.
1966   return profman.ProcessProfiles();
1967 }
1968 
1969 }  // namespace art
1970 
main(int argc,char ** argv)1971 int main(int argc, char **argv) {
1972   return art::profman(argc, argv);
1973 }
1974