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