Update dex2oat to accept multiple profiles.
`ProfileCompilationInfo` already supports loading multiple profiles, we
just need to update the commandline flags.
Bug: 203492478
Test: atest art_standalone_dex2oat_tests
Change-Id: Ifd1e614807e4d637529efe86673a691cc8a441be
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 8cf4f6f..3d956e2 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -40,21 +40,21 @@
#endif
#include "android-base/parseint.h"
+#include "android-base/scopeguard.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "android-base/unique_fd.h"
-
#include "aot_class_linker.h"
#include "arch/instruction_set_features.h"
#include "art_method-inl.h"
#include "base/callee_save_type.h"
#include "base/dumpable.h"
+#include "base/fast_exit.h"
#include "base/file_utils.h"
#include "base/leb128.h"
#include "base/macros.h"
#include "base/mutex.h"
#include "base/os.h"
-#include "base/fast_exit.h"
#include "base/scoped_flock.h"
#include "base/stl_util.h"
#include "base/time_utils.h"
@@ -505,49 +505,47 @@
class Dex2Oat final {
public:
- explicit Dex2Oat(TimingLogger* timings) :
- compiler_kind_(Compiler::kOptimizing),
- // Take the default set of instruction features from the build.
- key_value_store_(nullptr),
- verification_results_(nullptr),
- runtime_(nullptr),
- thread_count_(sysconf(_SC_NPROCESSORS_CONF)),
- start_ns_(NanoTime()),
- start_cputime_ns_(ProcessCpuNanoTime()),
- strip_(false),
- oat_fd_(-1),
- input_vdex_fd_(-1),
- output_vdex_fd_(-1),
- input_vdex_file_(nullptr),
- dm_fd_(-1),
- zip_fd_(-1),
- image_fd_(-1),
- have_multi_image_arg_(false),
- multi_image_(false),
- image_base_(0U),
- image_storage_mode_(ImageHeader::kStorageModeUncompressed),
- passes_to_run_filename_(nullptr),
- dirty_image_objects_filename_(nullptr),
- dirty_image_objects_fd_(-1),
- is_host_(false),
- elf_writers_(),
- oat_writers_(),
- rodata_(),
- image_writer_(nullptr),
- driver_(nullptr),
- opened_dex_files_maps_(),
- opened_dex_files_(),
- avoid_storing_invocation_(false),
- swap_fd_(File::kInvalidFd),
- app_image_fd_(File::kInvalidFd),
- profile_file_fd_(File::kInvalidFd),
- timings_(timings),
- force_determinism_(false),
- check_linkage_conditions_(false),
- crash_on_linkage_violation_(false),
- compile_individually_(false),
- profile_load_attempted_(false)
- {}
+ explicit Dex2Oat(TimingLogger* timings)
+ : compiler_kind_(Compiler::kOptimizing),
+ // Take the default set of instruction features from the build.
+ key_value_store_(nullptr),
+ verification_results_(nullptr),
+ runtime_(nullptr),
+ thread_count_(sysconf(_SC_NPROCESSORS_CONF)),
+ start_ns_(NanoTime()),
+ start_cputime_ns_(ProcessCpuNanoTime()),
+ strip_(false),
+ oat_fd_(-1),
+ input_vdex_fd_(-1),
+ output_vdex_fd_(-1),
+ input_vdex_file_(nullptr),
+ dm_fd_(-1),
+ zip_fd_(-1),
+ image_fd_(-1),
+ have_multi_image_arg_(false),
+ multi_image_(false),
+ image_base_(0U),
+ image_storage_mode_(ImageHeader::kStorageModeUncompressed),
+ passes_to_run_filename_(nullptr),
+ dirty_image_objects_filename_(nullptr),
+ dirty_image_objects_fd_(-1),
+ is_host_(false),
+ elf_writers_(),
+ oat_writers_(),
+ rodata_(),
+ image_writer_(nullptr),
+ driver_(nullptr),
+ opened_dex_files_maps_(),
+ opened_dex_files_(),
+ avoid_storing_invocation_(false),
+ swap_fd_(File::kInvalidFd),
+ app_image_fd_(File::kInvalidFd),
+ timings_(timings),
+ force_determinism_(false),
+ check_linkage_conditions_(false),
+ crash_on_linkage_violation_(false),
+ compile_individually_(false),
+ profile_load_attempted_(false) {}
~Dex2Oat() {
// Log completion time before deleting the runtime_, because this accesses
@@ -788,10 +786,10 @@
Usage("--single-image not specified for --image-fd");
}
- const bool have_profile_file = !profile_file_.empty();
- const bool have_profile_fd = profile_file_fd_ != File::kInvalidFd;
+ const bool have_profile_file = !profile_files_.empty();
+ const bool have_profile_fd = !profile_file_fds_.empty();
if (have_profile_file && have_profile_fd) {
- Usage("Profile file should not be specified with both --profile-file-fd and --profile-file");
+ Usage("Profile files should not be specified with both --profile-file-fd and --profile-file");
}
if (!parser_options->oat_symbols.empty()) {
@@ -1069,8 +1067,8 @@
AssignIfExists(args, M::Passes, &passes_to_run_filename_);
AssignIfExists(args, M::BootImage, &parser_options->boot_image_filename);
AssignIfExists(args, M::AndroidRoot, &android_root_);
- AssignIfExists(args, M::Profile, &profile_file_);
- AssignIfExists(args, M::ProfileFd, &profile_file_fd_);
+ AssignIfExists(args, M::Profile, &profile_files_);
+ AssignIfExists(args, M::ProfileFd, &profile_file_fds_);
AssignIfExists(args, M::RuntimeOptions, &runtime_args_);
AssignIfExists(args, M::SwapFile, &swap_file_name_);
AssignIfExists(args, M::SwapFileFd, &swap_fd_);
@@ -2301,9 +2299,7 @@
return is_host_;
}
- bool HasProfileInput() const {
- return profile_file_fd_ != -1 || !profile_file_.empty();
- }
+ bool HasProfileInput() const { return !profile_file_fds_.empty() || !profile_files_.empty(); }
// Must be called after the profile is loaded.
bool DoProfileGuidedOptimizations() const {
@@ -2344,29 +2340,37 @@
// runtime).
bool for_boot_image = IsBootImage() || IsBootImageExtension();
profile_compilation_info_.reset(new ProfileCompilationInfo(for_boot_image));
+
+ // Cleanup profile compilation info if we encounter any error when reading profiles.
+ auto cleanup = android::base::ScopeGuard([&]() { profile_compilation_info_.reset(nullptr); });
+
// Dex2oat only uses the reference profile and that is not updated concurrently by the app or
// other processes. So we don't need to lock (as we have to do in profman or when writing the
// profile info).
- std::unique_ptr<File> profile_file;
- if (profile_file_fd_ != -1) {
- profile_file.reset(new File(DupCloexec(profile_file_fd_),
- "profile",
- /* check_usage= */ false,
- /* read_only_mode= */ true));
- } else if (profile_file_ != "") {
- profile_file.reset(OS::OpenFileForReading(profile_file_.c_str()));
+ if (!profile_file_fds_.empty()) {
+ for (int fd : profile_file_fds_) {
+ std::unique_ptr<File> profile_file(new File(DupCloexec(fd),
+ "profile",
+ /* check_usage= */ false,
+ /* read_only_mode= */ true));
+ if (!profile_compilation_info_->Load(profile_file->Fd())) {
+ return false;
+ }
+ }
+ } else {
+ for (const std::string& file : profile_files_) {
+ std::unique_ptr<File> profile_file(OS::OpenFileForReading(file.c_str()));
+ if (profile_file.get() == nullptr) {
+ PLOG(ERROR) << "Cannot open profiles";
+ return false;
+ }
+ if (!profile_compilation_info_->Load(profile_file->Fd())) {
+ return false;
+ }
+ }
}
- if (profile_file.get() == nullptr) {
- PLOG(ERROR) << "Cannot lock profiles";
- return false;
- }
-
- if (!profile_compilation_info_->Load(profile_file->Fd())) {
- profile_compilation_info_.reset(nullptr);
- return false;
- }
-
+ cleanup.Disable();
return true;
}
@@ -2900,8 +2904,8 @@
size_t very_large_threshold_ = std::numeric_limits<size_t>::max();
std::string app_image_file_name_;
int app_image_fd_;
- std::string profile_file_;
- int profile_file_fd_;
+ std::vector<std::string> profile_files_;
+ std::vector<int> profile_file_fds_;
std::unique_ptr<ProfileCompilationInfo> profile_compilation_info_;
TimingLogger* timings_;
std::vector<std::vector<const DexFile*>> dex_files_per_oat_file_;
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index 0125f9d..8d5296b 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -242,11 +242,11 @@
.WithType<std::string>()
.IntoKey(M::Passes)
.Define("--profile-file=_")
- .WithType<std::string>()
+ .WithType<std::vector<std::string>>().AppendValues()
.WithHelp("Specify profiler output file to use for compilation using a filename.")
.IntoKey(M::Profile)
.Define("--profile-file-fd=_")
- .WithType<int>()
+ .WithType<std::vector<int>>().AppendValues()
.WithHelp("Specify profiler output file to use for compilation using a file-descriptor.")
.IntoKey(M::ProfileFd)
.Define("--no-inline-from=_")
diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def
index 804231c..f153ddf 100644
--- a/dex2oat/dex2oat_options.def
+++ b/dex2oat/dex2oat_options.def
@@ -66,8 +66,8 @@
DEX2OAT_OPTIONS_KEY (std::string, TargetInstructionSetVariant)
DEX2OAT_OPTIONS_KEY (std::string, TargetInstructionSetFeatures)
DEX2OAT_OPTIONS_KEY (Compiler::Kind, Backend)
-DEX2OAT_OPTIONS_KEY (std::string, Profile)
-DEX2OAT_OPTIONS_KEY (int, ProfileFd)
+DEX2OAT_OPTIONS_KEY (std::vector<std::string>, Profile)
+DEX2OAT_OPTIONS_KEY (std::vector<int>, ProfileFd)
DEX2OAT_OPTIONS_KEY (Unit, Host)
DEX2OAT_OPTIONS_KEY (Unit, DumpTiming)
DEX2OAT_OPTIONS_KEY (Unit, DumpPasses)
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 06b3626..ab5f8c6 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -14,27 +14,26 @@
* limitations under the License.
*/
+#include <sys/wait.h>
+#include <unistd.h>
+
#include <algorithm>
+#include <iterator>
#include <regex>
#include <sstream>
#include <string>
#include <vector>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <android-base/macros.h>
-#include <android-base/stringprintf.h>
-
-#include "common_runtime_test.h"
-
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
#include "arch/instruction_set_features.h"
#include "base/macros.h"
#include "base/mutex-inl.h"
#include "base/string_view_cpp20.h"
#include "base/utils.h"
#include "base/zip_archive.h"
+#include "common_runtime_test.h"
#include "dex/art_dex_file_loader.h"
#include "dex/base64_test_util.h"
#include "dex/bytecode_utils.h"
@@ -611,10 +610,20 @@
}
// Emits a profile with a single dex file with the given location and classes ranging
- // from 0 to num_classes.
+ // from `class_offset` to `class_offset + num_classes`.
void GenerateProfile(const std::string& test_profile,
- const DexFile* dex,
- size_t num_classes) {
+ const std::string& dex_location,
+ size_t num_classes,
+ size_t class_offset = 0) {
+ const char* location = dex_location.c_str();
+ std::string error_msg;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ const ArtDexFileLoader dex_file_loader;
+ ASSERT_TRUE(dex_file_loader.Open(
+ location, location, /*verify=*/true, /*verify_checksum=*/true, &error_msg, &dex_files));
+ EXPECT_EQ(dex_files.size(), 1U);
+ std::unique_ptr<const DexFile>& dex_file = dex_files[0];
+
int profile_test_fd = open(test_profile.c_str(),
O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC,
0644);
@@ -623,33 +632,26 @@
ProfileCompilationInfo info;
std::vector<dex::TypeIndex> classes;;
for (size_t i = 0; i < num_classes; ++i) {
- classes.push_back(dex::TypeIndex(1 + i));
+ classes.push_back(dex::TypeIndex(class_offset + 1 + i));
}
- info.AddClassesForDex(dex, classes.begin(), classes.end());
+ info.AddClassesForDex(dex_file.get(), classes.begin(), classes.end());
bool result = info.Save(profile_test_fd);
close(profile_test_fd);
ASSERT_TRUE(result);
}
+ // Compiles a dex file with profiles.
void CompileProfileOdex(const std::string& dex_location,
const std::string& odex_location,
const std::string& app_image_file_name,
bool use_fd,
- size_t num_profile_classes,
+ const std::vector<std::string>& profile_locations,
const std::vector<std::string>& extra_args = {},
bool expect_success = true) {
- const std::string profile_location = GetScratchDir() + "/primary.prof";
- const char* location = dex_location.c_str();
- std::string error_msg;
- std::vector<std::unique_ptr<const DexFile>> dex_files;
- const ArtDexFileLoader dex_file_loader;
- ASSERT_TRUE(dex_file_loader.Open(
- location, location, /*verify=*/ true, /*verify_checksum=*/ true, &error_msg, &dex_files));
- EXPECT_EQ(dex_files.size(), 1U);
- std::unique_ptr<const DexFile>& dex_file = dex_files[0];
- GenerateProfile(profile_location, dex_file.get(), num_profile_classes);
std::vector<std::string> copy(extra_args);
- copy.push_back("--profile-file=" + profile_location);
+ for (const std::string& profile_location : profile_locations) {
+ copy.push_back("--profile-file=" + profile_location);
+ }
std::unique_ptr<File> app_image_file;
if (!app_image_file_name.empty()) {
if (use_fd) {
@@ -670,6 +672,26 @@
}
}
+ // Same as above, but generates the profile internally with classes ranging from 0 to
+ // `num_profile_classes`.
+ void CompileProfileOdex(const std::string& dex_location,
+ const std::string& odex_location,
+ const std::string& app_image_file_name,
+ bool use_fd,
+ size_t num_profile_classes,
+ const std::vector<std::string>& extra_args = {},
+ bool expect_success = true) {
+ const std::string profile_location = GetScratchDir() + "/primary.prof";
+ GenerateProfile(profile_location, dex_location, num_profile_classes);
+ CompileProfileOdex(dex_location,
+ odex_location,
+ app_image_file_name,
+ use_fd,
+ {profile_location},
+ extra_args,
+ expect_success);
+ }
+
uint64_t GetImageObjectSectionSize(const std::string& image_file_name) {
EXPECT_FALSE(image_file_name.empty());
std::unique_ptr<File> file(OS::OpenFileForReading(image_file_name.c_str()));
@@ -850,6 +872,45 @@
RunTest(/*app_image=*/ true);
}
+TEST_F(Dex2oatLayoutTest, TestLayoutMultipleProfiles) {
+ std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
+ std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
+ std::string app_image_file = GetOdexDir() + "/DexOdexNoOat.art";
+ Copy(GetDexSrc2(), dex_location);
+
+ const std::string profile1_location = GetScratchDir() + "/primary.prof";
+ GenerateProfile(profile1_location, dex_location, /*num_classes=*/1, /*class_offset=*/0);
+ CompileProfileOdex(dex_location,
+ odex_location,
+ app_image_file,
+ /*use_fd=*/false,
+ {profile1_location});
+ uint64_t image_file_size_profile1 = GetImageObjectSectionSize(app_image_file);
+
+ const std::string profile2_location = GetScratchDir() + "/secondary.prof";
+ GenerateProfile(profile2_location, dex_location, /*num_classes=*/1, /*class_offset=*/1);
+ CompileProfileOdex(dex_location,
+ odex_location,
+ app_image_file,
+ /*use_fd=*/false,
+ {profile2_location});
+ uint64_t image_file_size_profile2 = GetImageObjectSectionSize(app_image_file);
+
+ CompileProfileOdex(dex_location,
+ odex_location,
+ app_image_file,
+ /*use_fd=*/false,
+ {profile1_location, profile2_location});
+ uint64_t image_file_size_multiple_profiles = GetImageObjectSectionSize(app_image_file);
+
+ CheckCompilerFilter(dex_location, odex_location, CompilerFilter::Filter::kSpeedProfile);
+
+ // The image file generated with multiple profiles should be larger than any image file generated
+ // with each profile.
+ ASSERT_GT(image_file_size_multiple_profiles, image_file_size_profile1);
+ ASSERT_GT(image_file_size_multiple_profiles, image_file_size_profile2);
+}
+
TEST_F(Dex2oatLayoutTest, TestVdexLayout) {
RunTestVDex();
}