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