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