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 <fstream>
25 #include <iostream>
26 #include <set>
27 #include <string>
28 #include <string_view>
29 #include <unordered_set>
30 #include <vector>
31
32 #include "android-base/parsebool.h"
33 #include "android-base/stringprintf.h"
34 #include "android-base/strings.h"
35
36 #include "base/dumpable.h"
37 #include "base/logging.h" // For InitLogging.
38 #include "base/mem_map.h"
39 #include "base/scoped_flock.h"
40 #include "base/stl_util.h"
41 #include "base/string_view_cpp20.h"
42 #include "base/time_utils.h"
43 #include "base/unix_file/fd_file.h"
44 #include "base/utils.h"
45 #include "base/zip_archive.h"
46 #include "boot_image_profile.h"
47 #include "dex/art_dex_file_loader.h"
48 #include "dex/bytecode_utils.h"
49 #include "dex/class_accessor-inl.h"
50 #include "dex/code_item_accessors-inl.h"
51 #include "dex/dex_file.h"
52 #include "dex/dex_file_loader.h"
53 #include "dex/dex_file_types.h"
54 #include "dex/type_reference.h"
55 #include "profile/profile_boot_info.h"
56 #include "profile/profile_compilation_info.h"
57 #include "profile_assistant.h"
58
59 namespace art {
60
61 using ProfileSampleAnnotation = ProfileCompilationInfo::ProfileSampleAnnotation;
62
63 static int original_argc;
64 static char** original_argv;
65
CommandLine()66 static std::string CommandLine() {
67 std::vector<std::string> command;
68 command.reserve(original_argc);
69 for (int i = 0; i < original_argc; ++i) {
70 command.push_back(original_argv[i]);
71 }
72 return android::base::Join(command, ' ');
73 }
74
75 static constexpr int kInvalidFd = -1;
76
FdIsValid(int fd)77 static bool FdIsValid(int fd) {
78 return fd != kInvalidFd;
79 }
80
UsageErrorV(const char * fmt,va_list ap)81 static void UsageErrorV(const char* fmt, va_list ap) {
82 std::string error;
83 android::base::StringAppendV(&error, fmt, ap);
84 LOG(ERROR) << error;
85 }
86
UsageError(const char * fmt,...)87 static void UsageError(const char* fmt, ...) {
88 va_list ap;
89 va_start(ap, fmt);
90 UsageErrorV(fmt, ap);
91 va_end(ap);
92 }
93
Usage(const char * fmt,...)94 NO_RETURN static void Usage(const char *fmt, ...) {
95 va_list ap;
96 va_start(ap, fmt);
97 UsageErrorV(fmt, ap);
98 va_end(ap);
99
100 UsageError("Command: %s", CommandLine().c_str());
101 UsageError("Usage: profman [options]...");
102 UsageError("");
103 UsageError(" --dump-only: dumps the content of the specified profile files");
104 UsageError(" to standard output (default) in a human readable form.");
105 UsageError("");
106 UsageError(" --dump-output-to-fd=<number>: redirects --dump-only output to a file descriptor.");
107 UsageError("");
108 UsageError(" --dump-classes-and-methods: dumps a sorted list of classes and methods that are");
109 UsageError(" in the specified profile file to standard output (default) in a human");
110 UsageError(" readable form. The output is valid input for --create-profile-from");
111 UsageError("");
112 UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation.");
113 UsageError(" Can be specified multiple time, in which case the data from the different");
114 UsageError(" profiles will be aggregated.");
115 UsageError("");
116 UsageError(" --profile-file-fd=<number>: same as --profile-file but accepts a file descriptor.");
117 UsageError(" Cannot be used together with --profile-file.");
118 UsageError("");
119 UsageError(" --reference-profile-file=<filename>: specify a reference profile.");
120 UsageError(" The data in this file will be compared with the data obtained by merging");
121 UsageError(" all the files specified with --profile-file or --profile-file-fd.");
122 UsageError(" If the exit code is EXIT_COMPILE then all --profile-file will be merged into");
123 UsageError(" --reference-profile-file. ");
124 UsageError("");
125 UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but");
126 UsageError(" accepts a file descriptor. Cannot be used together with");
127 UsageError(" --reference-profile-file.");
128 UsageError("");
129 UsageError(" --generate-test-profile=<filename>: generates a random profile file for testing.");
130 UsageError(" --generate-test-profile-num-dex=<number>: number of dex files that should be");
131 UsageError(" included in the generated profile. Defaults to 20.");
132 UsageError(" --generate-test-profile-method-percentage=<number>: the percentage from the maximum");
133 UsageError(" number of methods that should be generated. Defaults to 5.");
134 UsageError(" --generate-test-profile-class-percentage=<number>: the percentage from the maximum");
135 UsageError(" number of classes that should be generated. Defaults to 5.");
136 UsageError(" --generate-test-profile-seed=<number>: seed for random number generator used when");
137 UsageError(" generating random test profiles. Defaults to using NanoTime.");
138 UsageError("");
139 UsageError(" --create-profile-from=<filename>: creates a profile from a list of classes and");
140 UsageError(" methods.");
141 UsageError("");
142 UsageError(" --dex-location=<string>: location string to use with corresponding");
143 UsageError(" apk-fd to find dex files");
144 UsageError("");
145 UsageError(" --apk-fd=<number>: file descriptor containing an open APK to");
146 UsageError(" search for dex files");
147 UsageError(" --apk-=<filename>: an APK to search for dex files");
148 UsageError(" --skip-apk-verification: do not attempt to verify APKs");
149 UsageError("");
150 UsageError(" --generate-boot-image-profile: Generate a boot image profile based on input");
151 UsageError(" profiles. Requires passing in dex files to inspect properties of classes.");
152 UsageError(" --method-threshold=percentage between 0 and 100");
153 UsageError(" what threshold to apply to the methods when deciding whether or not to");
154 UsageError(" include it in the final profile.");
155 UsageError(" --class-threshold=percentage between 0 and 100");
156 UsageError(" what threshold to apply to the classes when deciding whether or not to");
157 UsageError(" include it in the final profile.");
158 UsageError(" --clean-class-threshold=percentage between 0 and 100");
159 UsageError(" what threshold to apply to the clean classes when deciding whether or not to");
160 UsageError(" include it in the final profile.");
161 UsageError(" --preloaded-class-threshold=percentage between 0 and 100");
162 UsageError(" what threshold to apply to the classes when deciding whether or not to");
163 UsageError(" include it in the final preloaded classes.");
164 UsageError(" --preloaded-classes-blacklist=file");
165 UsageError(" a file listing the classes that should not be preloaded in Zygote");
166 UsageError(" --upgrade-startup-to-hot=true|false:");
167 UsageError(" whether or not to upgrade startup methods to hot");
168 UsageError(" --special-package=pkg_name:percentage between 0 and 100");
169 UsageError(" what threshold to apply to the methods/classes that are used by the given");
170 UsageError(" package when deciding whether or not to include it in the final profile.");
171 UsageError(" --debug-append-uses=bool: whether or not to append package use as debug info.");
172 UsageError(" --out-profile-path=path: boot image profile output path");
173 UsageError(" --out-preloaded-classes-path=path: preloaded classes output path");
174 UsageError(" --copy-and-update-profile-key: if present, profman will copy the profile from");
175 UsageError(" the file passed with --profile-fd(file) to the profile passed with");
176 UsageError(" --reference-profile-fd(file) and update at the same time the profile-key");
177 UsageError(" of entries corresponding to the apks passed with --apk(-fd).");
178 UsageError(" --boot-image-merge: indicates that this merge is for a boot image profile.");
179 UsageError(" In this case, the reference profile must have a boot profile version.");
180 UsageError(" --force-merge: performs a forced merge, without analyzing if there is a");
181 UsageError(" significant difference between the current profile and the reference profile.");
182 UsageError("");
183
184 exit(EXIT_FAILURE);
185 }
186
187 // Note: make sure you update the Usage if you change these values.
188 static constexpr uint16_t kDefaultTestProfileNumDex = 20;
189 static constexpr uint16_t kDefaultTestProfileMethodPercentage = 5;
190 static constexpr uint16_t kDefaultTestProfileClassPercentage = 5;
191
192 // Separators used when parsing human friendly representation of profiles.
193 static const std::string kMethodSep = "->"; // NOLINT [runtime/string] [4]
194 static const std::string kMissingTypesMarker = "missing_types"; // NOLINT [runtime/string] [4]
195 static const std::string kInvalidClassDescriptor = "invalid_class"; // NOLINT [runtime/string] [4]
196 static const std::string kInvalidMethod = "invalid_method"; // NOLINT [runtime/string] [4]
197 static const std::string kClassAllMethods = "*"; // NOLINT [runtime/string] [4]
198 static constexpr char kAnnotationStart = '{';
199 static constexpr char kAnnotationEnd = '}';
200 static constexpr char kProfileParsingInlineChacheSep = '+';
201 static constexpr char kProfileParsingTypeSep = ',';
202 static constexpr char kProfileParsingFirstCharInSignature = '(';
203 static constexpr char kMethodFlagStringHot = 'H';
204 static constexpr char kMethodFlagStringStartup = 'S';
205 static constexpr char kMethodFlagStringPostStartup = 'P';
206
Abort(const char * msg)207 NO_RETURN static void Abort(const char* msg) {
208 LOG(ERROR) << msg;
209 exit(1);
210 }
211 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 ())212 static void ParseUintValue(const std::string& option_name,
213 const std::string& value,
214 T* out,
215 T min = std::numeric_limits<T>::min(),
216 T max = std::numeric_limits<T>::max()) {
217 int64_t parsed_integer_value = 0;
218 if (!android::base::ParseInt(
219 value,
220 &parsed_integer_value,
221 static_cast<int64_t>(min),
222 static_cast<int64_t>(max))) {
223 Usage("Failed to parse %s '%s' as an integer", option_name.c_str(), value.c_str());
224 }
225 if (parsed_integer_value < 0) {
226 Usage("%s passed a negative value %" PRId64, option_name.c_str(), parsed_integer_value);
227 }
228 if (static_cast<uint64_t>(parsed_integer_value) >
229 static_cast<std::make_unsigned_t<T>>(std::numeric_limits<T>::max())) {
230 Usage("%s passed a value %" PRIu64 " above max (%" PRIu64 ")",
231 option_name.c_str(),
232 static_cast<uint64_t>(parsed_integer_value),
233 static_cast<uint64_t>(std::numeric_limits<T>::max()));
234 }
235 *out = dchecked_integral_cast<T>(parsed_integer_value);
236 }
237
238 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 ())239 static void ParseUintOption(const char* raw_option,
240 std::string_view option_prefix,
241 T* out,
242 T min = std::numeric_limits<T>::min(),
243 T max = std::numeric_limits<T>::max()) {
244 DCHECK(EndsWith(option_prefix, "="));
245 DCHECK(StartsWith(raw_option, option_prefix)) << raw_option << " " << option_prefix;
246 std::string option_name(option_prefix.substr(option_prefix.size() - 1u));
247 const char* value_string = raw_option + option_prefix.size();
248
249 ParseUintValue(option_name, value_string, out, min, max);
250 }
251
ParseBoolOption(const char * raw_option,std::string_view option_prefix,bool * out)252 static void ParseBoolOption(const char* raw_option,
253 std::string_view option_prefix,
254 bool* out) {
255 DCHECK(EndsWith(option_prefix, "="));
256 DCHECK(StartsWith(raw_option, option_prefix)) << raw_option << " " << option_prefix;
257 const char* value_string = raw_option + option_prefix.size();
258 android::base::ParseBoolResult result = android::base::ParseBool(value_string);
259 if (result == android::base::ParseBoolResult::kError) {
260 std::string option_name(option_prefix.substr(option_prefix.size() - 1u));
261 Usage("Failed to parse %s '%s' as an integer", option_name.c_str(), value_string);
262 }
263
264 *out = result == android::base::ParseBoolResult::kTrue;
265 }
266
267 // TODO(calin): This class has grown too much from its initial design. Split the functionality
268 // into smaller, more contained pieces.
269 class ProfMan final {
270 public:
ProfMan()271 ProfMan() :
272 reference_profile_file_fd_(kInvalidFd),
273 dump_only_(false),
274 dump_classes_and_methods_(false),
275 generate_boot_image_profile_(false),
276 generate_boot_profile_(false),
277 dump_output_to_fd_(kInvalidFd),
278 test_profile_num_dex_(kDefaultTestProfileNumDex),
279 test_profile_method_percerntage_(kDefaultTestProfileMethodPercentage),
280 test_profile_class_percentage_(kDefaultTestProfileClassPercentage),
281 test_profile_seed_(NanoTime()),
282 start_ns_(NanoTime()),
283 copy_and_update_profile_key_(false),
284 profile_assistant_options_(ProfileAssistant::Options()) {}
285
~ProfMan()286 ~ProfMan() {
287 LogCompletionTime();
288 }
289
ParseArgs(int argc,char ** argv)290 void ParseArgs(int argc, char **argv) {
291 original_argc = argc;
292 original_argv = argv;
293
294 MemMap::Init();
295 InitLogging(argv, Abort);
296
297 // Skip over the command name.
298 argv++;
299 argc--;
300
301 if (argc == 0) {
302 Usage("No arguments specified");
303 }
304
305 for (int i = 0; i < argc; ++i) {
306 const char* raw_option = argv[i];
307 const std::string_view option(raw_option);
308 const bool log_options = false;
309 if (log_options) {
310 LOG(INFO) << "profman: option[" << i << "]=" << argv[i];
311 }
312 if (option == "--dump-only") {
313 dump_only_ = true;
314 } else if (option == "--dump-classes-and-methods") {
315 dump_classes_and_methods_ = true;
316 } else if (StartsWith(option, "--create-profile-from=")) {
317 create_profile_from_file_ = std::string(option.substr(strlen("--create-profile-from=")));
318 } else if (StartsWith(option, "--dump-output-to-fd=")) {
319 ParseUintOption(raw_option, "--dump-output-to-fd=", &dump_output_to_fd_);
320 } else if (option == "--generate-boot-profile") {
321 generate_boot_profile_ = true;
322 } else if (option == "--generate-boot-image-profile") {
323 generate_boot_image_profile_ = true;
324 } else if (StartsWith(option, "--method-threshold=")) {
325 ParseUintOption(raw_option,
326 "--method-threshold=",
327 &boot_image_options_.method_threshold,
328 0u,
329 100u);
330 } else if (StartsWith(option, "--class-threshold=")) {
331 ParseUintOption(raw_option,
332 "--class-threshold=",
333 &boot_image_options_.image_class_threshold,
334 0u,
335 100u);
336 } else if (StartsWith(option, "--clean-class-threshold=")) {
337 ParseUintOption(raw_option,
338 "--clean-class-threshold=",
339 &boot_image_options_.image_class_clean_threshold,
340 0u,
341 100u);
342 } else if (StartsWith(option, "--preloaded-class-threshold=")) {
343 ParseUintOption(raw_option,
344 "--preloaded-class-threshold=",
345 &boot_image_options_.preloaded_class_threshold,
346 0u,
347 100u);
348 } else if (StartsWith(option, "--preloaded-classes-blacklist=")) {
349 std::string preloaded_classes_blacklist =
350 std::string(option.substr(strlen("--preloaded-classes-blacklist=")));
351 // Read the user-specified list of methods.
352 std::unique_ptr<std::set<std::string>>
353 blacklist(ReadCommentedInputFromFile<std::set<std::string>>(
354 preloaded_classes_blacklist.c_str(), nullptr)); // No post-processing.
355 boot_image_options_.preloaded_classes_blacklist.insert(
356 blacklist->begin(), blacklist->end());
357 } else if (StartsWith(option, "--upgrade-startup-to-hot=")) {
358 ParseBoolOption(raw_option,
359 "--upgrade-startup-to-hot=",
360 &boot_image_options_.upgrade_startup_to_hot);
361 } else if (StartsWith(option, "--special-package=")) {
362 std::vector<std::string> values;
363 Split(std::string(option.substr(strlen("--special-package="))), ':', &values);
364 if (values.size() != 2) {
365 Usage("--special-package needs to be specified as pkg_name:threshold");
366 }
367 uint32_t threshold;
368 ParseUintValue("special-package", values[1], &threshold, 0u, 100u);
369 boot_image_options_.special_packages_thresholds.Overwrite(values[0], threshold);
370 } else if (StartsWith(option, "--debug-append-uses=")) {
371 ParseBoolOption(raw_option,
372 "--debug-append-uses=",
373 &boot_image_options_.append_package_use_list);
374 } else if (StartsWith(option, "--out-profile-path=")) {
375 boot_profile_out_path_ = std::string(option.substr(strlen("--out-profile-path=")));
376 } else if (StartsWith(option, "--out-preloaded-classes-path=")) {
377 preloaded_classes_out_path_ = std::string(
378 option.substr(strlen("--out-preloaded-classes-path=")));
379 } else if (StartsWith(option, "--profile-file=")) {
380 profile_files_.push_back(std::string(option.substr(strlen("--profile-file="))));
381 } else if (StartsWith(option, "--profile-file-fd=")) {
382 ParseFdForCollection(raw_option, "--profile-file-fd=", &profile_files_fd_);
383 } else if (StartsWith(option, "--reference-profile-file=")) {
384 reference_profile_file_ = std::string(option.substr(strlen("--reference-profile-file=")));
385 } else if (StartsWith(option, "--reference-profile-file-fd=")) {
386 ParseUintOption(raw_option, "--reference-profile-file-fd=", &reference_profile_file_fd_);
387 } else if (StartsWith(option, "--dex-location=")) {
388 dex_locations_.push_back(std::string(option.substr(strlen("--dex-location="))));
389 } else if (StartsWith(option, "--apk-fd=")) {
390 ParseFdForCollection(raw_option, "--apk-fd=", &apks_fd_);
391 } else if (StartsWith(option, "--apk=")) {
392 apk_files_.push_back(std::string(option.substr(strlen("--apk="))));
393 } else if (StartsWith(option, "--generate-test-profile=")) {
394 test_profile_ = std::string(option.substr(strlen("--generate-test-profile=")));
395 } else if (StartsWith(option, "--generate-test-profile-num-dex=")) {
396 ParseUintOption(raw_option,
397 "--generate-test-profile-num-dex=",
398 &test_profile_num_dex_);
399 } else if (StartsWith(option, "--generate-test-profile-method-percentage=")) {
400 ParseUintOption(raw_option,
401 "--generate-test-profile-method-percentage=",
402 &test_profile_method_percerntage_);
403 } else if (StartsWith(option, "--generate-test-profile-class-percentage=")) {
404 ParseUintOption(raw_option,
405 "--generate-test-profile-class-percentage=",
406 &test_profile_class_percentage_);
407 } else if (StartsWith(option, "--generate-test-profile-seed=")) {
408 ParseUintOption(raw_option, "--generate-test-profile-seed=", &test_profile_seed_);
409 } else if (option == "--copy-and-update-profile-key") {
410 copy_and_update_profile_key_ = true;
411 } else if (option == "--boot-image-merge") {
412 profile_assistant_options_.SetBootImageMerge(true);
413 } else if (option == "--force-merge") {
414 profile_assistant_options_.SetForceMerge(true);
415 } else {
416 Usage("Unknown argument '%s'", raw_option);
417 }
418 }
419
420 // Validate global consistency between file/fd options.
421 if (!profile_files_.empty() && !profile_files_fd_.empty()) {
422 Usage("Profile files should not be specified with both --profile-file-fd and --profile-file");
423 }
424 if (!reference_profile_file_.empty() && FdIsValid(reference_profile_file_fd_)) {
425 Usage("Reference profile should not be specified with both "
426 "--reference-profile-file-fd and --reference-profile-file");
427 }
428 if (!apk_files_.empty() && !apks_fd_.empty()) {
429 Usage("APK files should not be specified with both --apk-fd and --apk");
430 }
431 }
432
433 struct ProfileFilterKey {
ProfileFilterKeyart::ProfMan::ProfileFilterKey434 ProfileFilterKey(const std::string& dex_location, uint32_t checksum)
435 : dex_location_(dex_location), checksum_(checksum) {}
436 const std::string dex_location_;
437 uint32_t checksum_;
438
operator ==art::ProfMan::ProfileFilterKey439 bool operator==(const ProfileFilterKey& other) const {
440 return checksum_ == other.checksum_ && dex_location_ == other.dex_location_;
441 }
operator <art::ProfMan::ProfileFilterKey442 bool operator<(const ProfileFilterKey& other) const {
443 return checksum_ == other.checksum_
444 ? dex_location_ < other.dex_location_
445 : checksum_ < other.checksum_;
446 }
447 };
448
ProcessProfiles()449 ProfileAssistant::ProcessingResult ProcessProfiles() {
450 // Validate that at least one profile file was passed, as well as a reference profile.
451 if (profile_files_.empty() && profile_files_fd_.empty()) {
452 Usage("No profile files specified.");
453 }
454 if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
455 Usage("No reference profile file specified.");
456 }
457 if ((!profile_files_.empty() && FdIsValid(reference_profile_file_fd_)) ||
458 (!profile_files_fd_.empty() && !FdIsValid(reference_profile_file_fd_))) {
459 Usage("Options --profile-file-fd and --reference-profile-file-fd "
460 "should only be used together");
461 }
462
463 // Check if we have any apks which we should use to filter the profile data.
464 std::set<ProfileFilterKey> profile_filter_keys;
465 if (!GetProfileFilterKeyFromApks(&profile_filter_keys)) {
466 return ProfileAssistant::kErrorIO;
467 }
468
469 // Build the profile filter function. If the set of keys is empty it means we
470 // don't have any apks; as such we do not filter anything.
471 const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn =
472 [profile_filter_keys](const std::string& profile_key, uint32_t checksum) {
473 if (profile_filter_keys.empty()) {
474 // No --apk was specified. Accept all dex files.
475 return true;
476 } else {
477 // Remove any annotations from the profile key before comparing with the keys we get from apks.
478 std::string base_key = ProfileCompilationInfo::GetBaseKeyFromAugmentedKey(profile_key);
479 return profile_filter_keys.find(ProfileFilterKey(base_key, checksum)) !=
480 profile_filter_keys.end();
481 }
482 };
483
484 ProfileAssistant::ProcessingResult result;
485
486 if (profile_files_.empty()) {
487 // The file doesn't need to be flushed here (ProcessProfiles will do it)
488 // so don't check the usage.
489 File file(reference_profile_file_fd_, false);
490 result = ProfileAssistant::ProcessProfiles(profile_files_fd_,
491 reference_profile_file_fd_,
492 filter_fn,
493 profile_assistant_options_);
494 CloseAllFds(profile_files_fd_, "profile_files_fd_");
495 } else {
496 result = ProfileAssistant::ProcessProfiles(profile_files_,
497 reference_profile_file_,
498 filter_fn,
499 profile_assistant_options_);
500 }
501 return result;
502 }
503
GetProfileFilterKeyFromApks(std::set<ProfileFilterKey> * profile_filter_keys)504 bool GetProfileFilterKeyFromApks(std::set<ProfileFilterKey>* profile_filter_keys) {
505 auto process_fn = [profile_filter_keys](std::unique_ptr<const DexFile>&& dex_file) {
506 // Store the profile key of the location instead of the location itself.
507 // This will make the matching in the profile filter method much easier.
508 profile_filter_keys->emplace(ProfileCompilationInfo::GetProfileDexFileBaseKey(
509 dex_file->GetLocation()), dex_file->GetLocationChecksum());
510 };
511 return OpenApkFilesFromLocations(process_fn);
512 }
513
OpenApkFilesFromLocations(std::vector<std::unique_ptr<const DexFile>> * dex_files)514 bool OpenApkFilesFromLocations(std::vector<std::unique_ptr<const DexFile>>* dex_files) {
515 auto process_fn = [dex_files](std::unique_ptr<const DexFile>&& dex_file) {
516 dex_files->emplace_back(std::move(dex_file));
517 };
518 return OpenApkFilesFromLocations(process_fn);
519 }
520
OpenApkFilesFromLocations(const std::function<void (std::unique_ptr<const DexFile> &&)> & process_fn)521 bool OpenApkFilesFromLocations(
522 const std::function<void(std::unique_ptr<const DexFile>&&)>& process_fn) {
523 bool use_apk_fd_list = !apks_fd_.empty();
524 if (use_apk_fd_list) {
525 // Get the APKs from the collection of FDs.
526 if (dex_locations_.empty()) {
527 // Try to compute the dex locations from the file paths of the descriptions.
528 // This will make it easier to invoke profman with --apk-fd and without
529 // being force to pass --dex-location when the location would be the apk path.
530 if (!ComputeDexLocationsFromApkFds()) {
531 return false;
532 }
533 } else {
534 if (dex_locations_.size() != apks_fd_.size()) {
535 Usage("The number of apk-fds must match the number of dex-locations.");
536 }
537 }
538 } else if (!apk_files_.empty()) {
539 if (dex_locations_.empty()) {
540 // If no dex locations are specified use the apk names as locations.
541 dex_locations_ = apk_files_;
542 } else if (dex_locations_.size() != apk_files_.size()) {
543 Usage("The number of apk-fds must match the number of dex-locations.");
544 }
545 } else {
546 // No APKs were specified.
547 CHECK(dex_locations_.empty());
548 return true;
549 }
550 static constexpr bool kVerifyChecksum = true;
551 for (size_t i = 0; i < dex_locations_.size(); ++i) {
552 std::string error_msg;
553 const ArtDexFileLoader dex_file_loader;
554 std::vector<std::unique_ptr<const DexFile>> dex_files_for_location;
555 // We do not need to verify the apk for processing profiles.
556 if (use_apk_fd_list) {
557 if (dex_file_loader.OpenZip(apks_fd_[i],
558 dex_locations_[i],
559 /* verify= */ false,
560 kVerifyChecksum,
561 &error_msg,
562 &dex_files_for_location)) {
563 } else {
564 LOG(ERROR) << "OpenZip failed for '" << dex_locations_[i] << "' " << error_msg;
565 return false;
566 }
567 } else {
568 if (dex_file_loader.Open(apk_files_[i].c_str(),
569 dex_locations_[i],
570 /* verify= */ false,
571 kVerifyChecksum,
572 &error_msg,
573 &dex_files_for_location)) {
574 } else {
575 LOG(ERROR) << "Open failed for '" << dex_locations_[i] << "' " << error_msg;
576 return false;
577 }
578 }
579 for (std::unique_ptr<const DexFile>& dex_file : dex_files_for_location) {
580 process_fn(std::move(dex_file));
581 }
582 }
583 return true;
584 }
585
586 // Get the dex locations from the apk fds.
587 // The methods reads the links from /proc/self/fd/ to find the original apk paths
588 // and puts them in the dex_locations_ vector.
ComputeDexLocationsFromApkFds()589 bool ComputeDexLocationsFromApkFds() {
590 #ifdef _WIN32
591 PLOG(ERROR) << "ComputeDexLocationsFromApkFds is unsupported on Windows.";
592 return false;
593 #else
594 // We can't use a char array of PATH_MAX size without exceeding the frame size.
595 // So we use a vector as the buffer for the path.
596 std::vector<char> buffer(PATH_MAX, 0);
597 for (size_t i = 0; i < apks_fd_.size(); ++i) {
598 std::string fd_path = "/proc/self/fd/" + std::to_string(apks_fd_[i]);
599 ssize_t len = readlink(fd_path.c_str(), buffer.data(), buffer.size() - 1);
600 if (len == -1) {
601 PLOG(ERROR) << "Could not open path from fd";
602 return false;
603 }
604
605 buffer[len] = '\0';
606 dex_locations_.push_back(buffer.data());
607 }
608 return true;
609 #endif
610 }
611
LoadProfile(const std::string & filename,int fd)612 std::unique_ptr<const ProfileCompilationInfo> LoadProfile(const std::string& filename, int fd) {
613 if (!filename.empty()) {
614 #ifdef _WIN32
615 int flags = O_RDWR;
616 #else
617 int flags = O_RDWR | O_CLOEXEC;
618 #endif
619 fd = open(filename.c_str(), flags);
620 if (fd < 0) {
621 PLOG(ERROR) << "Cannot open " << filename;
622 return nullptr;
623 }
624 }
625 std::unique_ptr<ProfileCompilationInfo> info(new ProfileCompilationInfo);
626 if (!info->Load(fd)) {
627 LOG(ERROR) << "Cannot load profile info from fd=" << fd << "\n";
628 return nullptr;
629 }
630 return info;
631 }
632
DumpOneProfile(const std::string & banner,const std::string & filename,int fd,const std::vector<std::unique_ptr<const DexFile>> * dex_files,std::string * dump)633 int DumpOneProfile(const std::string& banner,
634 const std::string& filename,
635 int fd,
636 const std::vector<std::unique_ptr<const DexFile>>* dex_files,
637 std::string* dump) {
638 std::unique_ptr<const ProfileCompilationInfo> info(LoadProfile(filename, fd));
639 if (info == nullptr) {
640 LOG(ERROR) << "Cannot load profile info from filename=" << filename << " fd=" << fd;
641 return -1;
642 }
643 *dump += banner + "\n" + info->DumpInfo(MakeNonOwningPointerVector(*dex_files)) + "\n";
644 return 0;
645 }
646
DumpProfileInfo()647 int DumpProfileInfo() {
648 // Validate that at least one profile file or reference was specified.
649 if (profile_files_.empty() && profile_files_fd_.empty() &&
650 reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
651 Usage("No profile files or reference profile specified.");
652 }
653 static const char* kEmptyString = "";
654 static const char* kOrdinaryProfile = "=== profile ===";
655 static const char* kReferenceProfile = "=== reference profile ===";
656 static const char* kDexFiles = "=== Dex files ===";
657
658 std::vector<std::unique_ptr<const DexFile>> dex_files;
659 OpenApkFilesFromLocations(&dex_files);
660
661 std::string dump;
662
663 // Dump checkfiles and corresponding checksums.
664 dump += kDexFiles;
665 dump += "\n";
666 for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
667 std::ostringstream oss;
668 oss << dex_file->GetLocation()
669 << " [checksum=" << std::hex << dex_file->GetLocationChecksum() << "]\n";
670 dump += oss.str();
671 }
672
673 // Dump individual profile files.
674 if (!profile_files_fd_.empty()) {
675 for (int profile_file_fd : profile_files_fd_) {
676 int ret = DumpOneProfile(kOrdinaryProfile,
677 kEmptyString,
678 profile_file_fd,
679 &dex_files,
680 &dump);
681 if (ret != 0) {
682 return ret;
683 }
684 }
685 }
686 for (const std::string& profile_file : profile_files_) {
687 int ret = DumpOneProfile(kOrdinaryProfile, profile_file, kInvalidFd, &dex_files, &dump);
688 if (ret != 0) {
689 return ret;
690 }
691 }
692 // Dump reference profile file.
693 if (FdIsValid(reference_profile_file_fd_)) {
694 int ret = DumpOneProfile(kReferenceProfile,
695 kEmptyString,
696 reference_profile_file_fd_,
697 &dex_files,
698 &dump);
699 if (ret != 0) {
700 return ret;
701 }
702 }
703 if (!reference_profile_file_.empty()) {
704 int ret = DumpOneProfile(kReferenceProfile,
705 reference_profile_file_,
706 kInvalidFd,
707 &dex_files,
708 &dump);
709 if (ret != 0) {
710 return ret;
711 }
712 }
713 if (!FdIsValid(dump_output_to_fd_)) {
714 std::cout << dump;
715 } else {
716 unix_file::FdFile out_fd(dump_output_to_fd_, /*check_usage=*/ false);
717 if (!out_fd.WriteFully(dump.c_str(), dump.length())) {
718 return -1;
719 }
720 }
721 return 0;
722 }
723
ShouldOnlyDumpProfile()724 bool ShouldOnlyDumpProfile() {
725 return dump_only_;
726 }
727
GetClassNamesAndMethods(int fd,std::vector<std::unique_ptr<const DexFile>> * dex_files,std::set<std::string> * out_lines)728 bool GetClassNamesAndMethods(int fd,
729 std::vector<std::unique_ptr<const DexFile>>* dex_files,
730 std::set<std::string>* out_lines) {
731 ProfileCompilationInfo profile_info;
732 if (!profile_info.Load(fd)) {
733 LOG(ERROR) << "Cannot load profile info";
734 return false;
735 }
736 for (const std::unique_ptr<const DexFile>& dex_file : *dex_files) {
737 std::set<dex::TypeIndex> class_types;
738 std::set<uint16_t> hot_methods;
739 std::set<uint16_t> startup_methods;
740 std::set<uint16_t> post_startup_methods;
741 std::set<uint16_t> combined_methods;
742 if (profile_info.GetClassesAndMethods(*dex_file.get(),
743 &class_types,
744 &hot_methods,
745 &startup_methods,
746 &post_startup_methods)) {
747 for (const dex::TypeIndex& type_index : class_types) {
748 const dex::TypeId& type_id = dex_file->GetTypeId(type_index);
749 out_lines->insert(std::string(dex_file->GetTypeDescriptor(type_id)));
750 }
751 combined_methods = hot_methods;
752 combined_methods.insert(startup_methods.begin(), startup_methods.end());
753 combined_methods.insert(post_startup_methods.begin(), post_startup_methods.end());
754 for (uint16_t dex_method_idx : combined_methods) {
755 const dex::MethodId& id = dex_file->GetMethodId(dex_method_idx);
756 std::string signature_string(dex_file->GetMethodSignature(id).ToString());
757 std::string type_string(dex_file->GetTypeDescriptor(dex_file->GetTypeId(id.class_idx_)));
758 std::string method_name(dex_file->GetMethodName(id));
759 std::string flags_string;
760 if (hot_methods.find(dex_method_idx) != hot_methods.end()) {
761 flags_string += kMethodFlagStringHot;
762 }
763 if (startup_methods.find(dex_method_idx) != startup_methods.end()) {
764 flags_string += kMethodFlagStringStartup;
765 }
766 if (post_startup_methods.find(dex_method_idx) != post_startup_methods.end()) {
767 flags_string += kMethodFlagStringPostStartup;
768 }
769 out_lines->insert(flags_string +
770 type_string +
771 kMethodSep +
772 method_name +
773 signature_string);
774 }
775 }
776 }
777 return true;
778 }
779
GetClassNamesAndMethods(const std::string & profile_file,std::vector<std::unique_ptr<const DexFile>> * dex_files,std::set<std::string> * out_lines)780 bool GetClassNamesAndMethods(const std::string& profile_file,
781 std::vector<std::unique_ptr<const DexFile>>* dex_files,
782 std::set<std::string>* out_lines) {
783 #ifdef _WIN32
784 int flags = O_RDONLY;
785 #else
786 int flags = O_RDONLY | O_CLOEXEC;
787 #endif
788 int fd = open(profile_file.c_str(), flags);
789 if (!FdIsValid(fd)) {
790 PLOG(ERROR) << "Cannot open " << profile_file;
791 return false;
792 }
793 if (!GetClassNamesAndMethods(fd, dex_files, out_lines)) {
794 return false;
795 }
796 if (close(fd) < 0) {
797 PLOG(WARNING) << "Failed to close descriptor";
798 }
799 return true;
800 }
801
DumpClassesAndMethods()802 int DumpClassesAndMethods() {
803 // Validate that at least one profile file or reference was specified.
804 if (profile_files_.empty() && profile_files_fd_.empty() &&
805 reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
806 Usage("No profile files or reference profile specified.");
807 }
808
809 // Open the dex files to get the names for classes.
810 std::vector<std::unique_ptr<const DexFile>> dex_files;
811 OpenApkFilesFromLocations(&dex_files);
812 // Build a vector of class names from individual profile files.
813 std::set<std::string> class_names;
814 if (!profile_files_fd_.empty()) {
815 for (int profile_file_fd : profile_files_fd_) {
816 if (!GetClassNamesAndMethods(profile_file_fd, &dex_files, &class_names)) {
817 return -1;
818 }
819 }
820 }
821 if (!profile_files_.empty()) {
822 for (const std::string& profile_file : profile_files_) {
823 if (!GetClassNamesAndMethods(profile_file, &dex_files, &class_names)) {
824 return -1;
825 }
826 }
827 }
828 // Concatenate class names from reference profile file.
829 if (FdIsValid(reference_profile_file_fd_)) {
830 if (!GetClassNamesAndMethods(reference_profile_file_fd_, &dex_files, &class_names)) {
831 return -1;
832 }
833 }
834 if (!reference_profile_file_.empty()) {
835 if (!GetClassNamesAndMethods(reference_profile_file_, &dex_files, &class_names)) {
836 return -1;
837 }
838 }
839 // Dump the class names.
840 std::string dump;
841 for (const std::string& class_name : class_names) {
842 dump += class_name + std::string("\n");
843 }
844 if (!FdIsValid(dump_output_to_fd_)) {
845 std::cout << dump;
846 } else {
847 unix_file::FdFile out_fd(dump_output_to_fd_, /*check_usage=*/ false);
848 if (!out_fd.WriteFully(dump.c_str(), dump.length())) {
849 return -1;
850 }
851 }
852 return 0;
853 }
854
ShouldOnlyDumpClassesAndMethods()855 bool ShouldOnlyDumpClassesAndMethods() {
856 return dump_classes_and_methods_;
857 }
858
859 // Read lines from the given file, dropping comments and empty lines. Post-process each line with
860 // the given function.
861 template <typename T>
ReadCommentedInputFromFile(const char * input_filename,std::function<std::string (const char *)> * process)862 static T* ReadCommentedInputFromFile(
863 const char* input_filename, std::function<std::string(const char*)>* process) {
864 std::unique_ptr<std::ifstream> input_file(new std::ifstream(input_filename, std::ifstream::in));
865 if (input_file.get() == nullptr) {
866 LOG(ERROR) << "Failed to open input file " << input_filename;
867 return nullptr;
868 }
869 std::unique_ptr<T> result(
870 ReadCommentedInputStream<T>(*input_file, process));
871 input_file->close();
872 return result.release();
873 }
874
875 // Read lines from the given stream, dropping comments and empty lines. Post-process each line
876 // with the given function.
877 template <typename T>
ReadCommentedInputStream(std::istream & in_stream,std::function<std::string (const char *)> * process)878 static T* ReadCommentedInputStream(
879 std::istream& in_stream,
880 std::function<std::string(const char*)>* process) {
881 std::unique_ptr<T> output(new T());
882 while (in_stream.good()) {
883 std::string dot;
884 std::getline(in_stream, dot);
885 if (android::base::StartsWith(dot, "#") || dot.empty()) {
886 continue;
887 }
888 if (process != nullptr) {
889 std::string descriptor((*process)(dot.c_str()));
890 output->insert(output->end(), descriptor);
891 } else {
892 output->insert(output->end(), dot);
893 }
894 }
895 return output.release();
896 }
897
898 // Find class klass_descriptor in the given dex_files and store its reference
899 // in the out parameter class_ref.
900 // Return true if the definition or a reference of the class was found in any
901 // of the dex_files.
FindClass(const std::vector<std::unique_ptr<const DexFile>> & dex_files,const std::string & klass_descriptor,TypeReference * class_ref)902 bool FindClass(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
903 const std::string& klass_descriptor,
904 /*out*/TypeReference* class_ref) {
905 constexpr uint16_t kInvalidTypeIndex = std::numeric_limits<uint16_t>::max() - 1;
906 for (const std::unique_ptr<const DexFile>& dex_file_ptr : dex_files) {
907 const DexFile* dex_file = dex_file_ptr.get();
908 if (klass_descriptor == kInvalidClassDescriptor) {
909 if (kInvalidTypeIndex >= dex_file->NumTypeIds()) {
910 // The dex file does not contain all possible type ids which leaves us room
911 // to add an "invalid" type id.
912 *class_ref = TypeReference(dex_file, dex::TypeIndex(kInvalidTypeIndex));
913 return true;
914 } else {
915 // The dex file contains all possible type ids. We don't have any free type id
916 // that we can use as invalid.
917 continue;
918 }
919 }
920
921 const dex::TypeId* type_id = dex_file->FindTypeId(klass_descriptor.c_str());
922 if (type_id == nullptr) {
923 continue;
924 }
925 dex::TypeIndex type_index = dex_file->GetIndexForTypeId(*type_id);
926 *class_ref = TypeReference(dex_file, type_index);
927
928 if (dex_file->FindClassDef(type_index) == nullptr) {
929 // Class is only referenced in the current dex file but not defined in it.
930 // We use its current type reference, but keep looking for its
931 // definition.
932 // Note that array classes fall into that category, as they do not have
933 // a class definition.
934 continue;
935 }
936 return true;
937 }
938 // If we arrive here, we haven't found a class definition. If the dex file
939 // of the class reference is not null, then we have found a type reference,
940 // and we return that to the caller.
941 return (class_ref->dex_file != nullptr);
942 }
943
944 // Find the method specified by method_spec in the class class_ref.
FindMethodIndex(const TypeReference & class_ref,const std::string & method_spec)945 uint32_t FindMethodIndex(const TypeReference& class_ref,
946 const std::string& method_spec) {
947 const DexFile* dex_file = class_ref.dex_file;
948 if (method_spec == kInvalidMethod) {
949 constexpr uint16_t kInvalidMethodIndex = std::numeric_limits<uint16_t>::max() - 1;
950 return kInvalidMethodIndex >= dex_file->NumMethodIds()
951 ? kInvalidMethodIndex
952 : dex::kDexNoIndex;
953 }
954
955 std::vector<std::string> name_and_signature;
956 Split(method_spec, kProfileParsingFirstCharInSignature, &name_and_signature);
957 if (name_and_signature.size() != 2) {
958 LOG(ERROR) << "Invalid method name and signature " << method_spec;
959 return dex::kDexNoIndex;
960 }
961
962 const std::string& name = name_and_signature[0];
963 const std::string& signature = kProfileParsingFirstCharInSignature + name_and_signature[1];
964
965 const dex::StringId* name_id = dex_file->FindStringId(name.c_str());
966 if (name_id == nullptr) {
967 LOG(WARNING) << "Could not find name: " << name;
968 return dex::kDexNoIndex;
969 }
970 dex::TypeIndex return_type_idx;
971 std::vector<dex::TypeIndex> param_type_idxs;
972 if (!dex_file->CreateTypeList(signature, &return_type_idx, ¶m_type_idxs)) {
973 LOG(WARNING) << "Could not create type list" << signature;
974 return dex::kDexNoIndex;
975 }
976 const dex::ProtoId* proto_id = dex_file->FindProtoId(return_type_idx, param_type_idxs);
977 if (proto_id == nullptr) {
978 LOG(WARNING) << "Could not find proto_id: " << name;
979 return dex::kDexNoIndex;
980 }
981 const dex::MethodId* method_id = dex_file->FindMethodId(
982 dex_file->GetTypeId(class_ref.TypeIndex()), *name_id, *proto_id);
983 if (method_id == nullptr) {
984 LOG(WARNING) << "Could not find method_id: " << name;
985 return dex::kDexNoIndex;
986 }
987
988 return dex_file->GetIndexForMethodId(*method_id);
989 }
990
991 // Given a method, return true if the method has a single INVOKE_VIRTUAL in its byte code.
992 // Upon success it returns true and stores the method index and the invoke dex pc
993 // in the output parameters.
994 // The format of the method spec is "inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;".
995 //
996 // TODO(calin): support INVOKE_INTERFACE and the range variants.
HasSingleInvoke(const TypeReference & class_ref,uint16_t method_index,uint32_t * dex_pc)997 bool HasSingleInvoke(const TypeReference& class_ref,
998 uint16_t method_index,
999 /*out*/uint32_t* dex_pc) {
1000 const DexFile* dex_file = class_ref.dex_file;
1001 uint32_t offset = dex_file->FindCodeItemOffset(
1002 *dex_file->FindClassDef(class_ref.TypeIndex()),
1003 method_index);
1004 const dex::CodeItem* code_item = dex_file->GetCodeItem(offset);
1005
1006 bool found_invoke = false;
1007 for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(*dex_file, code_item)) {
1008 if (inst->Opcode() == Instruction::INVOKE_VIRTUAL ||
1009 inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) {
1010 if (found_invoke) {
1011 LOG(ERROR) << "Multiple invoke INVOKE_VIRTUAL found: "
1012 << dex_file->PrettyMethod(method_index);
1013 return false;
1014 }
1015 found_invoke = true;
1016 *dex_pc = inst.DexPc();
1017 }
1018 }
1019 if (!found_invoke) {
1020 LOG(ERROR) << "Could not find any INVOKE_VIRTUAL: " << dex_file->PrettyMethod(method_index);
1021 }
1022 return found_invoke;
1023 }
1024
1025 // Process a line defining a class or a method and its inline caches.
1026 // Upon success return true and add the class or the method info to profile.
1027 // The possible line formats are:
1028 // "LJustTheClass;".
1029 // "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;".
1030 // "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,invalid_class".
1031 // "LTestInline;->inlineMissingTypes(LSuper;)I+missing_types".
1032 // "{annotation}LTestInline;->inlineNoInlineCaches(LSuper;)I".
1033 // "LTestInline;->*".
1034 // "invalid_class".
1035 // "LTestInline;->invalid_method".
1036 // The method and classes are searched only in the given dex files.
ProcessLine(const std::vector<std::unique_ptr<const DexFile>> & dex_files,const std::string & maybe_annotated_line,ProfileCompilationInfo * profile)1037 bool ProcessLine(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
1038 const std::string& maybe_annotated_line,
1039 /*out*/ProfileCompilationInfo* profile) {
1040 // First, process the annotation.
1041 if (maybe_annotated_line.empty()) {
1042 return true;
1043 }
1044 // Working line variable which will contain the user input without the annotations.
1045 std::string line = maybe_annotated_line;
1046
1047 std::string annotation_string;
1048 if (maybe_annotated_line[0] == kAnnotationStart) {
1049 size_t end_pos = maybe_annotated_line.find(kAnnotationEnd, 0);
1050 if (end_pos == std::string::npos || end_pos == 0) {
1051 LOG(ERROR) << "Invalid line: " << maybe_annotated_line;
1052 return false;
1053 }
1054 annotation_string = maybe_annotated_line.substr(1, end_pos - 1);
1055 // Update the working line.
1056 line = maybe_annotated_line.substr(end_pos + 1);
1057 }
1058
1059 ProfileSampleAnnotation annotation = annotation_string.empty()
1060 ? ProfileSampleAnnotation::kNone
1061 : ProfileSampleAnnotation(annotation_string);
1062
1063 // Now process the rest of the lines.
1064 std::string klass;
1065 std::string method_str;
1066 bool is_hot = false;
1067 bool is_startup = false;
1068 bool is_post_startup = false;
1069 const size_t method_sep_index = line.find(kMethodSep, 0);
1070 if (method_sep_index == std::string::npos) {
1071 klass = line.substr(0);
1072 } else {
1073 // The method prefix flags are only valid for method strings.
1074 size_t start_index = 0;
1075 while (start_index < line.size() && line[start_index] != 'L') {
1076 const char c = line[start_index];
1077 if (c == kMethodFlagStringHot) {
1078 is_hot = true;
1079 } else if (c == kMethodFlagStringStartup) {
1080 is_startup = true;
1081 } else if (c == kMethodFlagStringPostStartup) {
1082 is_post_startup = true;
1083 } else {
1084 LOG(WARNING) << "Invalid flag " << c;
1085 return false;
1086 }
1087 ++start_index;
1088 }
1089 klass = line.substr(start_index, method_sep_index - start_index);
1090 method_str = line.substr(method_sep_index + kMethodSep.size());
1091 }
1092
1093 uint32_t flags = 0;
1094 if (is_hot) {
1095 flags |= ProfileCompilationInfo::MethodHotness::kFlagHot;
1096 }
1097 if (is_startup) {
1098 flags |= ProfileCompilationInfo::MethodHotness::kFlagStartup;
1099 }
1100 if (is_post_startup) {
1101 flags |= ProfileCompilationInfo::MethodHotness::kFlagPostStartup;
1102 }
1103
1104 TypeReference class_ref(/* dex_file= */ nullptr, dex::TypeIndex());
1105 if (!FindClass(dex_files, klass, &class_ref)) {
1106 LOG(WARNING) << "Could not find class: " << klass;
1107 return false;
1108 }
1109
1110 if (method_str.empty() || method_str == kClassAllMethods) {
1111 // Start by adding the class.
1112 const DexFile* dex_file = class_ref.dex_file;
1113 std::vector<ProfileMethodInfo> methods;
1114 if (method_str == kClassAllMethods) {
1115 ClassAccessor accessor(
1116 *dex_file,
1117 dex_file->GetIndexForClassDef(*dex_file->FindClassDef(class_ref.TypeIndex())));
1118 for (const ClassAccessor::Method& method : accessor.GetMethods()) {
1119 if (method.GetCodeItemOffset() != 0) {
1120 // Add all of the methods that have code to the profile.
1121 methods.push_back(ProfileMethodInfo(method.GetReference()));
1122 }
1123 }
1124 }
1125 // TODO: Check return values?
1126 profile->AddMethods(
1127 methods, static_cast<ProfileCompilationInfo::MethodHotness::Flag>(flags), annotation);
1128 std::set<dex::TypeIndex> classes;
1129 classes.insert(class_ref.TypeIndex());
1130 profile->AddClassesForDex(dex_file, classes.begin(), classes.end(), annotation);
1131 return true;
1132 }
1133
1134 // Process the method.
1135 std::string method_spec;
1136 std::vector<std::string> inline_cache_elems;
1137
1138 // If none of the flags are set, default to hot.
1139 is_hot = is_hot || (!is_hot && !is_startup && !is_post_startup);
1140
1141 std::vector<std::string> method_elems;
1142 bool is_missing_types = false;
1143 Split(method_str, kProfileParsingInlineChacheSep, &method_elems);
1144 if (method_elems.size() == 2) {
1145 method_spec = method_elems[0];
1146 is_missing_types = method_elems[1] == kMissingTypesMarker;
1147 if (!is_missing_types) {
1148 Split(method_elems[1], kProfileParsingTypeSep, &inline_cache_elems);
1149 }
1150 } else if (method_elems.size() == 1) {
1151 method_spec = method_elems[0];
1152 } else {
1153 LOG(ERROR) << "Invalid method line: " << line;
1154 return false;
1155 }
1156
1157 const uint32_t method_index = FindMethodIndex(class_ref, method_spec);
1158 if (method_index == dex::kDexNoIndex) {
1159 return false;
1160 }
1161
1162 std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches;
1163 if (is_missing_types || !inline_cache_elems.empty()) {
1164 uint32_t dex_pc;
1165 if (!HasSingleInvoke(class_ref, method_index, &dex_pc)) {
1166 return false;
1167 }
1168 std::vector<TypeReference> classes(inline_cache_elems.size(),
1169 TypeReference(/* dex_file= */ nullptr, dex::TypeIndex()));
1170 size_t class_it = 0;
1171 for (const std::string& ic_class : inline_cache_elems) {
1172 if (!FindClass(dex_files, ic_class, &(classes[class_it++]))) {
1173 LOG(ERROR) << "Could not find class: " << ic_class;
1174 return false;
1175 }
1176 }
1177 inline_caches.emplace_back(dex_pc, is_missing_types, classes);
1178 }
1179 MethodReference ref(class_ref.dex_file, method_index);
1180 if (is_hot) {
1181 profile->AddMethod(ProfileMethodInfo(ref, inline_caches),
1182 static_cast<ProfileCompilationInfo::MethodHotness::Flag>(flags),
1183 annotation);
1184 }
1185 if (flags != 0) {
1186 if (!profile->AddMethod(ProfileMethodInfo(ref),
1187 static_cast<ProfileCompilationInfo::MethodHotness::Flag>(flags),
1188 annotation)) {
1189 return false;
1190 }
1191 DCHECK(profile->GetMethodHotness(ref, annotation).IsInProfile()) << method_spec;
1192 }
1193 return true;
1194 }
1195
ProcessBootLine(const std::vector<std::unique_ptr<const DexFile>> & dex_files,const std::string & line,ProfileBootInfo * boot_profiling_info)1196 bool ProcessBootLine(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
1197 const std::string& line,
1198 ProfileBootInfo* boot_profiling_info) {
1199 const size_t method_sep_index = line.find(kMethodSep, 0);
1200 std::string klass_str = line.substr(0, method_sep_index);
1201 std::string method_str = line.substr(method_sep_index + kMethodSep.size());
1202
1203 TypeReference class_ref(/* dex_file= */ nullptr, dex::TypeIndex());
1204 if (!FindClass(dex_files, klass_str, &class_ref)) {
1205 LOG(WARNING) << "Could not find class: " << klass_str;
1206 return false;
1207 }
1208
1209 const uint32_t method_index = FindMethodIndex(class_ref, method_str);
1210 if (method_index == dex::kDexNoIndex) {
1211 LOG(WARNING) << "Could not find method: " << line;
1212 return false;
1213 }
1214 boot_profiling_info->Add(class_ref.dex_file, method_index);
1215 return true;
1216 }
1217
OpenReferenceProfile() const1218 int OpenReferenceProfile() const {
1219 int fd = reference_profile_file_fd_;
1220 if (!FdIsValid(fd)) {
1221 CHECK(!reference_profile_file_.empty());
1222 #ifdef _WIN32
1223 int flags = O_CREAT | O_TRUNC | O_WRONLY;
1224 #else
1225 int flags = O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC;
1226 #endif
1227 fd = open(reference_profile_file_.c_str(), flags, 0644);
1228 if (fd < 0) {
1229 PLOG(ERROR) << "Cannot open " << reference_profile_file_;
1230 return kInvalidFd;
1231 }
1232 }
1233 return fd;
1234 }
1235
1236 // Create and store a ProfileBootInfo.
CreateBootProfile()1237 int CreateBootProfile() {
1238 // Validate parameters for this command.
1239 if (apk_files_.empty() && apks_fd_.empty()) {
1240 Usage("APK files must be specified");
1241 }
1242 if (dex_locations_.empty()) {
1243 Usage("DEX locations must be specified");
1244 }
1245 if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
1246 Usage("Reference profile must be specified with --reference-profile-file or "
1247 "--reference-profile-file-fd");
1248 }
1249 if (!profile_files_.empty() || !profile_files_fd_.empty()) {
1250 Usage("Profile must be specified with --reference-profile-file or "
1251 "--reference-profile-file-fd");
1252 }
1253 // Open the profile output file if needed.
1254 int fd = OpenReferenceProfile();
1255 if (!FdIsValid(fd)) {
1256 return -1;
1257 }
1258 // Read the user-specified list of methods.
1259 std::unique_ptr<std::vector<std::string>>
1260 user_lines(ReadCommentedInputFromFile<std::vector<std::string>>(
1261 create_profile_from_file_.c_str(), nullptr)); // No post-processing.
1262
1263 // Open the dex files to look up classes and methods.
1264 std::vector<std::unique_ptr<const DexFile>> dex_files;
1265 OpenApkFilesFromLocations(&dex_files);
1266
1267 // Process the lines one by one and add the successful ones to the profile.
1268 ProfileBootInfo info;
1269
1270 for (const auto& line : *user_lines) {
1271 ProcessBootLine(dex_files, line, &info);
1272 }
1273
1274 // Write the profile file.
1275 CHECK(info.Save(fd));
1276
1277 if (close(fd) < 0) {
1278 PLOG(WARNING) << "Failed to close descriptor";
1279 }
1280
1281 return 0;
1282 }
1283
1284 // Creates a profile from a human friendly textual representation.
1285 // The expected input format is:
1286 // # Classes
1287 // Ljava/lang/Comparable;
1288 // Ljava/lang/Math;
1289 // # Methods with inline caches
1290 // LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;
1291 // LTestInline;->noInlineCache(LSuper;)I
CreateProfile()1292 int CreateProfile() {
1293 // Validate parameters for this command.
1294 if (apk_files_.empty() && apks_fd_.empty()) {
1295 Usage("APK files must be specified");
1296 }
1297 if (dex_locations_.empty()) {
1298 Usage("DEX locations must be specified");
1299 }
1300 if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
1301 Usage("Reference profile must be specified with --reference-profile-file or "
1302 "--reference-profile-file-fd");
1303 }
1304 if (!profile_files_.empty() || !profile_files_fd_.empty()) {
1305 Usage("Profile must be specified with --reference-profile-file or "
1306 "--reference-profile-file-fd");
1307 }
1308 // Open the profile output file if needed.
1309 int fd = OpenReferenceProfile();
1310 if (!FdIsValid(fd)) {
1311 return -1;
1312 }
1313 // Read the user-specified list of classes and methods.
1314 std::unique_ptr<std::unordered_set<std::string>>
1315 user_lines(ReadCommentedInputFromFile<std::unordered_set<std::string>>(
1316 create_profile_from_file_.c_str(), nullptr)); // No post-processing.
1317
1318 // Open the dex files to look up classes and methods.
1319 std::vector<std::unique_ptr<const DexFile>> dex_files;
1320 OpenApkFilesFromLocations(&dex_files);
1321
1322 // Process the lines one by one and add the successful ones to the profile.
1323 ProfileCompilationInfo info;
1324
1325 for (const auto& line : *user_lines) {
1326 ProcessLine(dex_files, line, &info);
1327 }
1328
1329 // Write the profile file.
1330 CHECK(info.Save(fd));
1331 if (close(fd) < 0) {
1332 PLOG(WARNING) << "Failed to close descriptor";
1333 }
1334 return 0;
1335 }
1336
ShouldCreateBootImageProfile() const1337 bool ShouldCreateBootImageProfile() const {
1338 return generate_boot_image_profile_;
1339 }
1340
ShouldCreateBootProfile() const1341 bool ShouldCreateBootProfile() const {
1342 return generate_boot_profile_;
1343 }
1344
1345 // Create and store a ProfileCompilationInfo for the boot image.
CreateBootImageProfile()1346 int CreateBootImageProfile() {
1347 // Open the input profile file.
1348 if (profile_files_.size() < 1) {
1349 LOG(ERROR) << "At least one --profile-file must be specified.";
1350 return -1;
1351 }
1352 // Open the dex files.
1353 std::vector<std::unique_ptr<const DexFile>> dex_files;
1354 OpenApkFilesFromLocations(&dex_files);
1355 if (dex_files.empty()) {
1356 PLOG(ERROR) << "Expected dex files for creating boot profile";
1357 return -2;
1358 }
1359
1360 if (!GenerateBootImageProfile(dex_files,
1361 profile_files_,
1362 boot_image_options_,
1363 boot_profile_out_path_,
1364 preloaded_classes_out_path_)) {
1365 LOG(ERROR) << "There was an error when generating the boot image profiles";
1366 return -4;
1367 }
1368 return 0;
1369 }
1370
ShouldCreateProfile()1371 bool ShouldCreateProfile() {
1372 return !create_profile_from_file_.empty();
1373 }
1374
GenerateTestProfile()1375 int GenerateTestProfile() {
1376 // Validate parameters for this command.
1377 if (test_profile_method_percerntage_ > 100) {
1378 Usage("Invalid percentage for --generate-test-profile-method-percentage");
1379 }
1380 if (test_profile_class_percentage_ > 100) {
1381 Usage("Invalid percentage for --generate-test-profile-class-percentage");
1382 }
1383 // If given APK files or DEX locations, check that they're ok.
1384 if (!apk_files_.empty() || !apks_fd_.empty() || !dex_locations_.empty()) {
1385 if (apk_files_.empty() && apks_fd_.empty()) {
1386 Usage("APK files must be specified when passing DEX locations to --generate-test-profile");
1387 }
1388 if (dex_locations_.empty()) {
1389 Usage("DEX locations must be specified when passing APK files to --generate-test-profile");
1390 }
1391 }
1392 // ShouldGenerateTestProfile confirms !test_profile_.empty().
1393 #ifdef _WIN32
1394 int flags = O_CREAT | O_TRUNC | O_WRONLY;
1395 #else
1396 int flags = O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC;
1397 #endif
1398 int profile_test_fd = open(test_profile_.c_str(), flags, 0644);
1399 if (profile_test_fd < 0) {
1400 PLOG(ERROR) << "Cannot open " << test_profile_;
1401 return -1;
1402 }
1403 bool result;
1404 if (apk_files_.empty() && apks_fd_.empty() && dex_locations_.empty()) {
1405 result = ProfileCompilationInfo::GenerateTestProfile(profile_test_fd,
1406 test_profile_num_dex_,
1407 test_profile_method_percerntage_,
1408 test_profile_class_percentage_,
1409 test_profile_seed_);
1410 } else {
1411 // Open the dex files to look up classes and methods.
1412 std::vector<std::unique_ptr<const DexFile>> dex_files;
1413 OpenApkFilesFromLocations(&dex_files);
1414 // Create a random profile file based on the set of dex files.
1415 result = ProfileCompilationInfo::GenerateTestProfile(profile_test_fd,
1416 dex_files,
1417 test_profile_method_percerntage_,
1418 test_profile_class_percentage_,
1419 test_profile_seed_);
1420 }
1421 close(profile_test_fd); // ignore close result.
1422 return result ? 0 : -1;
1423 }
1424
ShouldGenerateTestProfile()1425 bool ShouldGenerateTestProfile() {
1426 return !test_profile_.empty();
1427 }
1428
ShouldCopyAndUpdateProfileKey() const1429 bool ShouldCopyAndUpdateProfileKey() const {
1430 return copy_and_update_profile_key_;
1431 }
1432
CopyAndUpdateProfileKey()1433 int32_t CopyAndUpdateProfileKey() {
1434 // Validate that at least one profile file was passed, as well as a reference profile.
1435 if (!(profile_files_.size() == 1 ^ profile_files_fd_.size() == 1)) {
1436 Usage("Only one profile file should be specified.");
1437 }
1438 if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
1439 Usage("No reference profile file specified.");
1440 }
1441
1442 if (apk_files_.empty() && apks_fd_.empty()) {
1443 Usage("No apk files specified");
1444 }
1445
1446 static constexpr int32_t kErrorFailedToUpdateProfile = -1;
1447 static constexpr int32_t kErrorFailedToSaveProfile = -2;
1448 static constexpr int32_t kErrorFailedToLoadProfile = -3;
1449
1450 bool use_fds = profile_files_fd_.size() == 1;
1451
1452 ProfileCompilationInfo profile;
1453 // Do not clear if invalid. The input might be an archive.
1454 bool load_ok = use_fds
1455 ? profile.Load(profile_files_fd_[0])
1456 : profile.Load(profile_files_[0], /*clear_if_invalid=*/ false);
1457 if (load_ok) {
1458 // Open the dex files to look up classes and methods.
1459 std::vector<std::unique_ptr<const DexFile>> dex_files;
1460 OpenApkFilesFromLocations(&dex_files);
1461 if (!profile.UpdateProfileKeys(dex_files)) {
1462 return kErrorFailedToUpdateProfile;
1463 }
1464 bool result = use_fds
1465 ? profile.Save(reference_profile_file_fd_)
1466 : profile.Save(reference_profile_file_, /*bytes_written=*/ nullptr);
1467 return result ? 0 : kErrorFailedToSaveProfile;
1468 } else {
1469 return kErrorFailedToLoadProfile;
1470 }
1471 }
1472
1473 private:
ParseFdForCollection(const char * raw_option,std::string_view option_prefix,std::vector<int> * fds)1474 static void ParseFdForCollection(const char* raw_option,
1475 std::string_view option_prefix,
1476 std::vector<int>* fds) {
1477 int fd;
1478 ParseUintOption(raw_option, option_prefix, &fd);
1479 fds->push_back(fd);
1480 }
1481
CloseAllFds(const std::vector<int> & fds,const char * descriptor)1482 static void CloseAllFds(const std::vector<int>& fds, const char* descriptor) {
1483 for (size_t i = 0; i < fds.size(); i++) {
1484 if (close(fds[i]) < 0) {
1485 PLOG(WARNING) << "Failed to close descriptor for "
1486 << descriptor << " at index " << i << ": " << fds[i];
1487 }
1488 }
1489 }
1490
LogCompletionTime()1491 void LogCompletionTime() {
1492 static constexpr uint64_t kLogThresholdTime = MsToNs(100); // 100ms
1493 uint64_t time_taken = NanoTime() - start_ns_;
1494 if (time_taken > kLogThresholdTime) {
1495 LOG(WARNING) << "profman took " << PrettyDuration(time_taken);
1496 }
1497 }
1498
1499 std::vector<std::string> profile_files_;
1500 std::vector<int> profile_files_fd_;
1501 std::vector<std::string> dex_locations_;
1502 std::vector<std::string> apk_files_;
1503 std::vector<int> apks_fd_;
1504 std::string reference_profile_file_;
1505 int reference_profile_file_fd_;
1506 bool dump_only_;
1507 bool dump_classes_and_methods_;
1508 bool generate_boot_image_profile_;
1509 bool generate_boot_profile_;
1510 int dump_output_to_fd_;
1511 BootImageOptions boot_image_options_;
1512 std::string test_profile_;
1513 std::string create_profile_from_file_;
1514 uint16_t test_profile_num_dex_;
1515 uint16_t test_profile_method_percerntage_;
1516 uint16_t test_profile_class_percentage_;
1517 uint32_t test_profile_seed_;
1518 uint64_t start_ns_;
1519 bool copy_and_update_profile_key_;
1520 ProfileAssistant::Options profile_assistant_options_;
1521 std::string boot_profile_out_path_;
1522 std::string preloaded_classes_out_path_;
1523 };
1524
1525 // See ProfileAssistant::ProcessingResult for return codes.
profman(int argc,char ** argv)1526 static int profman(int argc, char** argv) {
1527 ProfMan profman;
1528
1529 // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
1530 profman.ParseArgs(argc, argv);
1531
1532 // Initialize MemMap for ZipArchive::OpenFromFd.
1533 MemMap::Init();
1534
1535 if (profman.ShouldGenerateTestProfile()) {
1536 return profman.GenerateTestProfile();
1537 }
1538 if (profman.ShouldOnlyDumpProfile()) {
1539 return profman.DumpProfileInfo();
1540 }
1541 if (profman.ShouldOnlyDumpClassesAndMethods()) {
1542 return profman.DumpClassesAndMethods();
1543 }
1544 if (profman.ShouldCreateBootProfile()) {
1545 return profman.CreateBootProfile();
1546 }
1547 if (profman.ShouldCreateProfile()) {
1548 return profman.CreateProfile();
1549 }
1550
1551 if (profman.ShouldCreateBootImageProfile()) {
1552 return profman.CreateBootImageProfile();
1553 }
1554
1555 if (profman.ShouldCopyAndUpdateProfileKey()) {
1556 return profman.CopyAndUpdateProfileKey();
1557 }
1558
1559 // Process profile information and assess if we need to do a profile guided compilation.
1560 // This operation involves I/O.
1561 return profman.ProcessProfiles();
1562 }
1563
1564 } // namespace art
1565
main(int argc,char ** argv)1566 int main(int argc, char **argv) {
1567 return art::profman(argc, argv);
1568 }
1569