From 114f08107be384a3f9cc954bdec2b6b7619354ae Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Wed, 8 Mar 2017 19:05:07 -0800 Subject: [PATCH] Support profile guided compilation for secondary dex files Test: adb shell cmd package bg-dexopt-job works for sercondary dex files Bug: 26719109 Change-Id: I4011a780f65378c23a4631c07c37cdb4f5fee41e --- cmds/installd/InstalldNativeService.cpp | 48 ++--- cmds/installd/dexopt.cpp | 296 ++++++++++++++++++---------- cmds/installd/dexopt.h | 18 +- cmds/installd/tests/installd_utils_test.cpp | 39 +++- cmds/installd/utils.cpp | 57 ++++-- cmds/installd/utils.h | 18 +- 6 files changed, 313 insertions(+), 163 deletions(-) diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index f9235e5d54..b5f0fb2c9c 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -351,17 +351,20 @@ binder::Status InstalldNativeService::createAppData(const std::unique_ptr lock(mLock); binder::Status res = ok(); - if (!clear_reference_profile(packageName)) { + if (!clear_primary_reference_profile(packageName)) { res = error("Failed to clear reference profile for " + packageName); } - if (!clear_current_profiles(packageName)) { + if (!clear_primary_current_profiles(packageName)) { res = error("Failed to clear current profiles for " + packageName); } return res; @@ -476,7 +479,7 @@ binder::Status InstalldNativeService::clearAppData(const std::unique_ptr lock(mLock); - const char* pkgname = packageName.c_str(); - *_aidl_return = analyse_profiles(uid, pkgname); + *_aidl_return = analyze_primary_profiles(uid, packageName); return ok(); } diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index 0d5652fc49..5bb2ce754c 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -50,6 +50,18 @@ using android::base::unique_fd; namespace android { namespace installd { +// Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below. +struct FreeDelete { + // NOTE: Deleting a const object is valid but free() takes a non-const pointer. + void operator()(const void* ptr) const { + free(const_cast(ptr)); + } +}; + +// Alias for std::unique_ptr<> that uses the C function free() to delete objects. +template +using UniqueCPtr = std::unique_ptr; + static unique_fd invalid_unique_fd() { return unique_fd(-1); } @@ -106,27 +118,40 @@ static bool clear_profile(const std::string& profile) { return truncated; } -bool clear_reference_profile(const std::string& pkgname) { - std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname); - std::string reference_profile = create_primary_profile(reference_profile_dir); - return clear_profile(reference_profile); +// Clear the reference profile for the given location. +// The location is the package name for primary apks or the dex path for secondary dex files. +static bool clear_reference_profile(const std::string& location, bool is_secondary_dex) { + return clear_profile(create_reference_profile_path(location, is_secondary_dex)); } -bool clear_current_profile(const std::string& pkgname, userid_t user) { - std::string profile_dir = create_data_user_profile_package_path(user, pkgname); - std::string profile = create_primary_profile(profile_dir); - return clear_profile(profile); +// Clear the reference profile for the given location. +// The location is the package name for primary apks or the dex path for secondary dex files. +static bool clear_current_profile(const std::string& pkgname, userid_t user, + bool is_secondary_dex) { + return clear_profile(create_current_profile_path(user, pkgname, is_secondary_dex)); } -bool clear_current_profiles(const std::string& pkgname) { +// Clear the reference profile for the primary apk of the given package. +bool clear_primary_reference_profile(const std::string& pkgname) { + return clear_reference_profile(pkgname, /*is_secondary_dex*/false); +} + +// Clear all current profile for the primary apk of the given package. +bool clear_primary_current_profiles(const std::string& pkgname) { bool success = true; + // For secondary dex files, we don't really need the user but we use it for sanity checks. std::vector users = get_known_users(/*volume_uuid*/ nullptr); for (auto user : users) { - success &= clear_current_profile(pkgname, user); + success &= clear_current_profile(pkgname, user, /*is_secondary_dex*/false); } return success; } +// Clear the current profile for the primary apk of the given package and user. +bool clear_primary_current_profile(const std::string& pkgname, userid_t user) { + return clear_current_profile(pkgname, user, /*is_secondary_dex*/false); +} + static int split_count(const char *str) { char *ctx; @@ -472,72 +497,86 @@ static void SetDex2OatScheduling(bool set_to_bg) { } } -static unique_fd open_profile_dir(const std::string& profile_dir) { - unique_fd profile_dir_fd(TEMP_FAILURE_RETRY(open(profile_dir.c_str(), - O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW))); - if (profile_dir_fd.get() < 0) { - // In a multi-user environment, these directories can be created at - // different points and it's possible we'll attempt to open a profile - // dir before it exists. - if (errno != ENOENT) { - PLOG(ERROR) << "Failed to open profile_dir: " << profile_dir; +static bool create_profile(int uid, const std::string& profile) { + unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), O_CREAT | O_NOFOLLOW, 0600))); + if (fd.get() < 0) { + if (errno == EEXIST) { + return true; + } else { + PLOG(ERROR) << "Failed to create profile " << profile; + return false; } } - return profile_dir_fd; + // Profiles should belong to the app; make sure of that by giving ownership to + // the app uid. If we cannot do that, there's no point in returning the fd + // since dex2oat/profman will fail with SElinux denials. + if (fchown(fd.get(), uid, uid) < 0) { + PLOG(ERROR) << "Could not chwon profile " << profile; + return false; + } + return true; } -static unique_fd open_primary_profile_file_from_dir(const std::string& profile_dir, - mode_t open_mode) { - unique_fd profile_dir_fd = open_profile_dir(profile_dir); - if (profile_dir_fd.get() < 0) { - return invalid_unique_fd(); +static unique_fd open_profile(int uid, const std::string& profile, bool read_write) { + // Check if we need to open the profile for a read-write operation. If so, we + // might need to create the profile since the file might not be there. Reference + // profiles are created on the fly so they might not exist beforehand. + if (read_write) { + if (!create_profile(uid, profile)) { + return invalid_unique_fd(); + } } + int flags = read_write ? O_RDWR : O_RDONLY; + // Do not follow symlinks when opening a profile: + // - primary profiles should not contain symlinks in their paths + // - secondary dex paths should have been already resolved and validated + flags |= O_NOFOLLOW; - std::string profile_file = create_primary_profile(profile_dir); - unique_fd profile_fd(TEMP_FAILURE_RETRY(open(profile_file.c_str(), - open_mode | O_NOFOLLOW, 0600))); - if (profile_fd == -1) { - // It's not an error if the profile file does not exist. + unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags))); + if (fd.get() < 0) { if (errno != ENOENT) { - PLOG(ERROR) << "Failed to open profile : " << profile_file; + // Profiles might be missing for various reasons. For example, in a + // multi-user environment, the profile directory for one user can be created + // after we start a merge. In this case the current profile for that user + // will not be found. + // Also, the secondary dex profiles might be deleted by the app at any time, + // so we can't we need to prepare if they are missing. + PLOG(ERROR) << "Failed to open profile " << profile; } + return invalid_unique_fd(); } - return profile_fd; + + return fd; } -static unique_fd open_primary_profile_file(userid_t user, const std::string& pkgname) { - std::string profile_dir = create_data_user_profile_package_path(user, pkgname); - return open_primary_profile_file_from_dir(profile_dir, O_RDONLY); +static unique_fd open_current_profile(uid_t uid, userid_t user, const std::string& location, + bool is_secondary_dex) { + std::string profile = create_current_profile_path(user, location, is_secondary_dex); + return open_profile(uid, profile, /*read_write*/false); } -static unique_fd open_reference_profile(uid_t uid, const std::string& pkgname, bool read_write) { - std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname); - int flags = read_write ? O_RDWR | O_CREAT : O_RDONLY; - unique_fd fd = open_primary_profile_file_from_dir(reference_profile_dir, flags); - if (fd.get() < 0) { - return invalid_unique_fd(); - } - if (read_write) { - // Fix the owner. - if (fchown(fd.get(), uid, uid) < 0) { - return invalid_unique_fd(); - } - } - return fd; +static unique_fd open_reference_profile(uid_t uid, const std::string& location, bool read_write, + bool is_secondary_dex) { + std::string profile = create_reference_profile_path(location, is_secondary_dex); + return open_profile(uid, profile, read_write); } -static void open_profile_files(uid_t uid, const std::string& pkgname, +static void open_profile_files(uid_t uid, const std::string& location, bool is_secondary_dex, /*out*/ std::vector* profiles_fd, /*out*/ unique_fd* reference_profile_fd) { // Open the reference profile in read-write mode as profman might need to save the merge. - *reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ true); - if (reference_profile_fd->get() < 0) { - // We can't access the reference profile file. - return; + *reference_profile_fd = open_reference_profile(uid, location, /*read_write*/ true, + is_secondary_dex); + + // For secondary dex files, we don't really need the user but we use it for sanity checks. + // Note: the user owning the dex file should be the current user. + std::vector users; + if (is_secondary_dex){ + users.push_back(multiuser_get_user_id(uid)); + } else { + users = get_known_users(/*volume_uuid*/ nullptr); } - - std::vector users = get_known_users(/*volume_uuid*/ nullptr); for (auto user : users) { - unique_fd profile_fd = open_primary_profile_file(user, pkgname); + unique_fd profile_fd = open_current_profile(uid, user, location, is_secondary_dex); // Add to the lists only if both fds are valid. if (profile_fd.get() >= 0) { profiles_fd->push_back(std::move(profile_fd)); @@ -603,14 +642,15 @@ static void run_profman_merge(const std::vector& profiles_fd, } // Decides if profile guided compilation is needed or not based on existing profiles. -// Returns true if there is enough information in the current profiles that worth -// a re-compilation of the package. +// The location is the package name for primary apks or the dex path for secondary dex files. +// Returns true if there is enough information in the current profiles that makes it +// worth to recompile the given location. // If the return value is true all the current profiles would have been merged into // the reference profiles accessible with open_reference_profile(). -bool analyse_profiles(uid_t uid, const std::string& pkgname) { +static bool analyze_profiles(uid_t uid, const std::string& location, bool is_secondary_dex) { std::vector profiles_fd; unique_fd reference_profile_fd; - open_profile_files(uid, pkgname, &profiles_fd, &reference_profile_fd); + open_profile_files(uid, location, is_secondary_dex, &profiles_fd, &reference_profile_fd); if (profiles_fd.empty() || (reference_profile_fd.get() < 0)) { // Skip profile guided compilation because no profiles were found. // Or if the reference profile info couldn't be opened. @@ -630,7 +670,7 @@ bool analyse_profiles(uid_t uid, const std::string& pkgname) { bool should_clear_current_profiles = false; bool should_clear_reference_profile = false; if (!WIFEXITED(return_code)) { - LOG(WARNING) << "profman failed for package " << pkgname << ": " << return_code; + LOG(WARNING) << "profman failed for location " << location << ": " << return_code; } else { return_code = WEXITSTATUS(return_code); switch (return_code) { @@ -645,7 +685,7 @@ bool analyse_profiles(uid_t uid, const std::string& pkgname) { should_clear_reference_profile = false; break; case PROFMAN_BIN_RETURN_CODE_BAD_PROFILES: - LOG(WARNING) << "Bad profiles for package " << pkgname; + LOG(WARNING) << "Bad profiles for location " << location; need_to_compile = false; should_clear_current_profiles = true; should_clear_reference_profile = true; @@ -653,15 +693,15 @@ bool analyse_profiles(uid_t uid, const std::string& pkgname) { case PROFMAN_BIN_RETURN_CODE_ERROR_IO: // fall-through case PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING: // Temporary IO problem (e.g. locking). Ignore but log a warning. - LOG(WARNING) << "IO error while reading profiles for package " << pkgname; + LOG(WARNING) << "IO error while reading profiles for location " << location; need_to_compile = false; should_clear_current_profiles = false; should_clear_reference_profile = false; break; default: // Unknown return code or error. Unlink profiles. - LOG(WARNING) << "Unknown error code while processing profiles for package " << pkgname - << ": " << return_code; + LOG(WARNING) << "Unknown error code while processing profiles for location " + << location << ": " << return_code; need_to_compile = false; should_clear_current_profiles = true; should_clear_reference_profile = true; @@ -670,14 +710,29 @@ bool analyse_profiles(uid_t uid, const std::string& pkgname) { } if (should_clear_current_profiles) { - clear_current_profiles(pkgname); + if (is_secondary_dex) { + // For secondary dex files, the owning user is the current user. + clear_current_profile(location, multiuser_get_user_id(uid), is_secondary_dex); + } else { + clear_primary_current_profiles(location); + } } if (should_clear_reference_profile) { - clear_reference_profile(pkgname); + clear_reference_profile(location, is_secondary_dex); } return need_to_compile; } +// Decides if profile guided compilation is needed or not based on existing profiles. +// The analysis is done for the primary apks of the given package. +// Returns true if there is enough information in the current profiles that makes it +// worth to recompile the package. +// If the return value is true all the current profiles would have been merged into +// the reference profiles accessible with open_reference_profile(). +bool analyze_primary_profiles(uid_t uid, const std::string& pkgname) { + return analyze_profiles(uid, pkgname, /*is_secondary_dex*/false); +} + static void run_profman_dump(const std::vector& profile_fds, const unique_fd& reference_profile_fd, const std::vector& dex_locations, @@ -729,7 +784,8 @@ bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_pat unique_fd reference_profile_fd; std::string out_file_name = StringPrintf("/data/misc/profman/%s.txt", pkgname.c_str()); - open_profile_files(uid, pkgname, &profile_fds, &reference_profile_fd); + open_profile_files(uid, pkgname, /*is_secondary_dex*/false, + &profile_fds, &reference_profile_fd); const bool has_reference_profile = (reference_profile_fd.get() != -1); const bool has_profiles = !profile_fds.empty(); @@ -739,7 +795,8 @@ bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_pat return false; } - unique_fd output_fd(open(out_file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0644)); + unique_fd output_fd(open(out_file_name.c_str(), + O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0644)); if (fchmod(output_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) { ALOGE("installd cannot chmod '%s' dump_profile\n", out_file_name.c_str()); return false; @@ -1061,23 +1118,22 @@ unique_fd maybe_open_dexopt_swap_file(const char* out_oat_path) { // Opens the reference profiles if needed. // Note that the reference profile might not exist so it's OK if the fd will be -1. -Dex2oatFileWrapper maybe_open_reference_profile(const char* pkgname, bool profile_guided, - bool is_public, int uid, bool is_secondary_dex) { +Dex2oatFileWrapper maybe_open_reference_profile(const std::string& pkgname, + const std::string& dex_path, bool profile_guided, bool is_public, int uid, + bool is_secondary_dex) { // Public apps should not be compiled with profile information ever. Same goes for the special // package '*' used for the system server. - // TODO(calin): add support for writing profiles for secondary dex files - if (profile_guided && !is_secondary_dex && !is_public && (pkgname[0] != '*')) { - // Open reference profile in read only mode as dex2oat does not get write permissions. - const std::string pkgname_str(pkgname); - unique_fd profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ false); - return Dex2oatFileWrapper( - profile_fd.release(), - [pkgname_str]() { - clear_reference_profile(pkgname_str.c_str()); - }); - } else { + if (!profile_guided || is_public || (pkgname[0] == '*')) { return Dex2oatFileWrapper(); } + + // Open reference profile in read only mode as dex2oat does not get write permissions. + const std::string location = is_secondary_dex ? dex_path : pkgname; + unique_fd ufd = open_reference_profile(uid, location, /*read_write*/false, is_secondary_dex); + const auto& cleanup = [location, is_secondary_dex]() { + clear_reference_profile(location.c_str(), is_secondary_dex); + }; + return Dex2oatFileWrapper(ufd.release(), cleanup); } // Opens the vdex files and assigns the input fd to in_vdex_wrapper_fd and the output fd to @@ -1197,8 +1253,11 @@ void update_out_oat_access_times(const char* apk_path, const char* out_oat_path) } // Runs (execv) dexoptanalyzer on the given arguments. -static void exec_dexoptanalyzer(const char* dex_file, const char* instruction_set, - const char* compiler_filter) { +// The analyzer will check if the dex_file needs to be (re)compiled to match the compiler_filter. +// If this is for a profile guided compilation, profile_was_updated will tell whether or not +// the profile has changed. +static void exec_dexoptanalyzer(const std::string& dex_file, const char* instruction_set, + const char* compiler_filter, bool profile_was_updated) { static const char* DEXOPTANALYZER_BIN = "/system/bin/dexoptanalyzer"; static const unsigned int MAX_INSTRUCTION_SET_LEN = 7; @@ -1211,18 +1270,22 @@ static void exec_dexoptanalyzer(const char* dex_file, const char* instruction_se char dex_file_arg[strlen("--dex-file=") + PKG_PATH_MAX]; char isa_arg[strlen("--isa=") + MAX_INSTRUCTION_SET_LEN]; char compiler_filter_arg[strlen("--compiler-filter=") + kPropertyValueMax]; + const char* assume_profile_changed = "--assume-profile-changed"; - sprintf(dex_file_arg, "--dex-file=%s", dex_file); + sprintf(dex_file_arg, "--dex-file=%s", dex_file.c_str()); sprintf(isa_arg, "--isa=%s", instruction_set); sprintf(compiler_filter_arg, "--compiler-filter=%s", compiler_filter); // program name, dex file, isa, filter, the final NULL - const char* argv[5]; + const char* argv[5 + (profile_was_updated ? 1 : 0)]; int i = 0; argv[i++] = DEXOPTANALYZER_BIN; argv[i++] = dex_file_arg; argv[i++] = isa_arg; argv[i++] = compiler_filter_arg; + if (profile_was_updated) { + argv[i++] = assume_profile_changed; + } argv[i] = NULL; execv(DEXOPTANALYZER_BIN, (char * const *)argv); @@ -1230,15 +1293,14 @@ static void exec_dexoptanalyzer(const char* dex_file, const char* instruction_se } // Prepares the oat dir for the secondary dex files. -static bool prepare_secondary_dex_oat_dir(const char* dex_path, int uid, - const char* instruction_set, std::string* oat_dir_out) { - std::string apk_path_str(dex_path); - unsigned long dirIndex = apk_path_str.rfind('/'); +static bool prepare_secondary_dex_oat_dir(const std::string& dex_path, int uid, + const char* instruction_set, std::string* oat_dir_out) { + unsigned long dirIndex = dex_path.rfind('/'); if (dirIndex == std::string::npos) { LOG(ERROR ) << "Unexpected dir structure for secondary dex " << dex_path; return false; } - std::string apk_dir = apk_path_str.substr(0, dirIndex); + std::string dex_dir = dex_path.substr(0, dirIndex); // Assign the gid to the cache gid so that the oat file storage // is counted towards the app cache. @@ -1250,13 +1312,13 @@ static bool prepare_secondary_dex_oat_dir(const char* dex_path, int uid, } // Create oat file output directory. - if (prepare_app_cache_dir(apk_dir, "oat", 02711, uid, cache_gid) != 0) { + if (prepare_app_cache_dir(dex_dir, "oat", 02711, uid, cache_gid) != 0) { LOG(ERROR) << "Could not prepare oat dir for secondary dex: " << dex_path; return false; } char oat_dir[PKG_PATH_MAX]; - snprintf(oat_dir, PKG_PATH_MAX, "%s/oat", apk_dir.c_str()); + snprintf(oat_dir, PKG_PATH_MAX, "%s/oat", dex_dir.c_str()); oat_dir_out->assign(oat_dir); // Create oat/isa output directory. @@ -1273,7 +1335,7 @@ static int constexpr DEXOPTANALYZER_BIN_EXEC_ERROR = 200; // Verifies the result of dexoptanalyzer executed for the apk_path. // If the result is valid returns true and sets dexopt_needed_out to a valid value. // Returns false for errors or unexpected result values. -static bool process_dexoptanalyzer_result(const char* dex_path, int result, +static bool process_dexoptanalyzer_result(const std::string& dex_path, int result, int* dexopt_needed_out) { // The result values are defined in dexoptanalyzer. switch (result) { @@ -1305,10 +1367,11 @@ static bool process_dexoptanalyzer_result(const char* dex_path, int result, // be compiled. Returns false for errors (logged) or true if the secondary dex path was process // successfully. // When returning true, dexopt_needed_out is assigned a valid OatFileAsssitant::DexOptNeeded -// code and aot_dir_out is assigned the oat dir path where the oat file should be stored. -static bool process_secondary_dex_dexopt(const char* dex_path, const char* pkgname, +// code and oat_dir_out is assigned the oat dir path where the oat file should be stored. +static bool process_secondary_dex_dexopt(const char* original_dex_path, const char* pkgname, int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set, - const char* compiler_filter, int* dexopt_needed_out, std::string* aot_dir_out) { + const char* compiler_filter, int* dexopt_needed_out, std::string* oat_dir_out, + std::string* dex_path_out) { int storage_flag; if ((dexopt_flags & DEXOPT_STORAGE_CE) != 0) { @@ -1324,17 +1387,31 @@ static bool process_secondary_dex_dexopt(const char* dex_path, const char* pkgna return false; } + { + // As opposed to the primary apk, secondary dex files might contain symlinks. + // Resolve the path before passing it to the validate method to + // make sure the verification is done on the real location. + UniqueCPtr dex_real_path_cstr(realpath(original_dex_path, nullptr)); + if (dex_real_path_cstr == nullptr) { + PLOG(ERROR) << "Could not get the real path of the secondary dex file " + << original_dex_path; + return false; + } else { + dex_path_out->assign(dex_real_path_cstr.get()); + } + } + const std::string& dex_path = *dex_path_out; if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) { LOG(ERROR) << "Could not validate secondary dex path " << dex_path; return false; } // Check if the path exist. If not, there's nothing to do. - if (access(dex_path, F_OK) != 0) { + if (access(dex_path.c_str(), F_OK) != 0) { if (errno == ENOENT) { // Secondary dex files might be deleted any time by the app. // Nothing to do if that's the case - ALOGV("Secondary dex does not exist %s", dex_path); + ALOGV("Secondary dex does not exist %s", dex_path.c_str()); return NO_DEXOPT_NEEDED; } else { PLOG(ERROR) << "Could not access secondary dex " << dex_path; @@ -1342,16 +1419,19 @@ static bool process_secondary_dex_dexopt(const char* dex_path, const char* pkgna } // Prepare the oat directories. - if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set, aot_dir_out)) { + if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set, oat_dir_out)) { return false; } + // Analyze profiles. + bool profile_was_updated = analyze_profiles(uid, dex_path, /*is_secondary_dex*/true); + pid_t pid = fork(); if (pid == 0) { // child -- drop privileges before continuing. drop_capabilities(uid); // Run dexoptanalyzer to get dexopt_needed code. - exec_dexoptanalyzer(dex_path, instruction_set, compiler_filter); + exec_dexoptanalyzer(dex_path, instruction_set, compiler_filter, profile_was_updated); exit(DEXOPTANALYZER_BIN_EXEC_ERROR); } @@ -1394,10 +1474,12 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins // Check if we're dealing with a secondary dex file and if we need to compile it. std::string oat_dir_str; + std::string dex_real_path; if (is_secondary_dex) { if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid, - instruction_set, compiler_filter, &dexopt_needed, &oat_dir_str)) { + instruction_set, compiler_filter, &dexopt_needed, &oat_dir_str, &dex_real_path)) { oat_dir = oat_dir_str.c_str(); + dex_path = dex_real_path.c_str(); if (dexopt_needed == NO_DEXOPT_NEEDED) { return 0; // Nothing to do, report success. } @@ -1442,8 +1524,8 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins maybe_open_app_image(out_oat_path, profile_guided, is_public, uid, is_secondary_dex); // Open the reference profile if needed. - Dex2oatFileWrapper reference_profile_fd = - maybe_open_reference_profile(pkgname, profile_guided, is_public, uid, is_secondary_dex); + Dex2oatFileWrapper reference_profile_fd = maybe_open_reference_profile( + pkgname, dex_path, profile_guided, is_public, uid, is_secondary_dex); ALOGV("DexInv: --- BEGIN '%s' ---\n", dex_path); @@ -1541,7 +1623,7 @@ static bool create_secondary_dex_oat_layout(const std::string& dex_path, const s snprintf(out_oat_isa_dir, PKG_PATH_MAX, "%s/%s", out_oat_dir, isa.c_str()); if (!create_oat_out_path(dex_path.c_str(), isa.c_str(), out_oat_dir, - /*is_secondary_dex*/ true, out_oat_path)) { + /*is_secondary_dex*/true, out_oat_path)) { LOG(ERROR) << "Could not create oat path for secondary dex " << dex_path; return false; } @@ -1738,7 +1820,7 @@ bool delete_odex(const char* apk_path, const char* instruction_set, const char* // Delete the oat/odex file. char out_path[PKG_PATH_MAX]; if (!create_oat_out_path(apk_path, instruction_set, oat_dir, - /*is_secondary_dex*/ false, out_path)) { + /*is_secondary_dex*/false, out_path)) { return false; } diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h index df6d176823..dbf3faeacd 100644 --- a/cmds/installd/dexopt.h +++ b/cmds/installd/dexopt.h @@ -32,13 +32,23 @@ static constexpr int DEX2OAT_FOR_FILTER = 3; static constexpr int DEX2OAT_FOR_RELOCATION = 4; static constexpr int PATCHOAT_FOR_RELOCATION = 5; -bool clear_reference_profile(const std::string& pkgname); -bool clear_current_profile(const std::string& pkgname, userid_t user); -bool clear_current_profiles(const std::string& pkgname); +// Clear the reference profile for the primary apk of the given package. +bool clear_primary_reference_profile(const std::string& pkgname); +// Clear the current profile for the primary apk of the given package and user. +bool clear_primary_current_profile(const std::string& pkgname, userid_t user); +// Clear all current profile for the primary apk of the given package. +bool clear_primary_current_profiles(const std::string& pkgname); bool move_ab(const char* apk_path, const char* instruction_set, const char* output_path); -bool analyse_profiles(uid_t uid, const std::string& pkgname); +// Decide if profile guided compilation is needed or not based on existing profiles. +// The analysis is done for the primary apks (base + splits) of the given package. +// Returns true if there is enough information in the current profiles that makes it +// worth to recompile the package. +// If the return value is true all the current profiles would have been merged into +// the reference profiles accessible with open_reference_profile(). +bool analyze_primary_profiles(uid_t uid, const std::string& pkgname); + bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths); bool delete_odex(const char* apk_path, const char* instruction_set, const char* output_path); diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index 940046fb75..d1e5919575 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -521,29 +521,50 @@ TEST_F(UtilsTest, IsValidPackageName) { } TEST_F(UtilsTest, CreateDataUserProfilePath) { - EXPECT_EQ("/data/misc/profiles/cur/0", create_data_user_profile_path(0)); - EXPECT_EQ("/data/misc/profiles/cur/1", create_data_user_profile_path(1)); + EXPECT_EQ("/data/misc/profiles/cur/0", create_primary_cur_profile_dir_path(0)); + EXPECT_EQ("/data/misc/profiles/cur/1", create_primary_cur_profile_dir_path(1)); } TEST_F(UtilsTest, CreateDataUserProfilePackagePath) { EXPECT_EQ("/data/misc/profiles/cur/0/com.example", - create_data_user_profile_package_path(0, "com.example")); + create_primary_current_profile_package_dir_path(0, "com.example")); EXPECT_EQ("/data/misc/profiles/cur/1/com.example", - create_data_user_profile_package_path(1, "com.example")); + create_primary_current_profile_package_dir_path(1, "com.example")); } TEST_F(UtilsTest, CreateDataRefProfilePath) { - EXPECT_EQ("/data/misc/profiles/ref", create_data_ref_profile_path()); + EXPECT_EQ("/data/misc/profiles/ref", create_primary_ref_profile_dir_path()); } TEST_F(UtilsTest, CreateDataRefProfilePackagePath) { EXPECT_EQ("/data/misc/profiles/ref/com.example", - create_data_ref_profile_package_path("com.example")); + create_primary_reference_profile_package_dir_path("com.example")); } -TEST_F(UtilsTest, CreatePrimaryProfile) { - EXPECT_EQ("/data/misc/profiles/ref/com.example/primary.prof", - create_primary_profile("/data/misc/profiles/ref/com.example")); +TEST_F(UtilsTest, CreatePrimaryCurrentProfile) { + std::string expected = + create_primary_current_profile_package_dir_path(1, "com.example") + "/primary.prof"; + EXPECT_EQ(expected, + create_current_profile_path(/*user*/0, "com.example", /*is_secondary*/false)); +} + +TEST_F(UtilsTest, CreatePrimaryReferenceProfile) { + std::string expected = + create_primary_reference_profile_package_dir_path("com.example") + "/primary.prof"; + EXPECT_EQ(expected, + create_reference_profile_path("com.example", /*is_secondary*/false)); +} + +TEST_F(UtilsTest, CreateSecondaryCurrentProfile) { + EXPECT_EQ("/data/user/0/com.example/secondary.dex.prof", + create_current_profile_path(/*user*/0, + "/data/user/0/com.example/secondary.dex", /*is_secondary*/true)); +} + +TEST_F(UtilsTest, CreateSecondaryReferenceProfile) { + EXPECT_EQ("/data/user/0/com.example/oat/secondary.dex.prof", + create_reference_profile_path( + "/data/user/0/com.example/secondary.dex", /*is_secondary*/true)); } } // namespace installd diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index a6fa6561c9..bdd62e6554 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -213,20 +213,22 @@ std::string create_data_misc_legacy_path(userid_t userid) { return StringPrintf("%s/misc/user/%u", create_data_path(nullptr).c_str(), userid); } -std::string create_data_user_profile_path(userid_t userid) { +std::string create_primary_cur_profile_dir_path(userid_t userid) { return StringPrintf("%s/cur/%u", android_profiles_dir.path, userid); } -std::string create_data_user_profile_package_path(userid_t user, const std::string& package_name) { +std::string create_primary_current_profile_package_dir_path(userid_t user, + const std::string& package_name) { check_package_name(package_name.c_str()); - return StringPrintf("%s/%s",create_data_user_profile_path(user).c_str(), package_name.c_str()); + return StringPrintf("%s/%s", + create_primary_cur_profile_dir_path(user).c_str(), package_name.c_str()); } -std::string create_data_ref_profile_path() { +std::string create_primary_ref_profile_dir_path() { return StringPrintf("%s/ref", android_profiles_dir.path); } -std::string create_data_ref_profile_package_path(const std::string& package_name) { +std::string create_primary_reference_profile_package_dir_path(const std::string& package_name) { check_package_name(package_name.c_str()); return StringPrintf("%s/ref/%s", android_profiles_dir.path, package_name.c_str()); } @@ -235,11 +237,38 @@ std::string create_data_dalvik_cache_path() { return "/data/dalvik-cache"; } -// Keep profile paths in sync with ActivityThread. -constexpr const char* PRIMARY_PROFILE_NAME = "primary.prof"; +// Keep profile paths in sync with ActivityThread and LoadedApk. +const std::string PROFILE_EXT = ".prof"; +const std::string PRIMARY_PROFILE_NAME = "primary" + PROFILE_EXT; + +std::string create_current_profile_path(userid_t user, const std::string& location, + bool is_secondary_dex) { + if (is_secondary_dex) { + // Secondary dex profiles are stored next to the dex files using .prof extension. + return StringPrintf("%s%s", location.c_str(), PROFILE_EXT.c_str()); + } else { + // Profiles for primary apks are under /data/misc/profiles/cur. + std::string profile_dir = create_primary_current_profile_package_dir_path(user, location); + return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME.c_str()); + } +} + +std::string create_reference_profile_path(const std::string& location, bool is_secondary_dex) { + if (is_secondary_dex) { + // Secondary dex reference profiles are stored next to the dex files under the oat folder. + size_t dirIndex = location.rfind('/'); + CHECK(dirIndex != std::string::npos) + << "Unexpected dir structure for secondary dex " << location; -std::string create_primary_profile(const std::string& profile_dir) { - return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME); + std::string dex_dir = location.substr(0, dirIndex); + std::string dex_name = location.substr(dirIndex +1); + return StringPrintf("%s/oat/%s%s", + dex_dir.c_str(), dex_name.c_str(), PROFILE_EXT.c_str()); + } else { + // Reference profiles for primary apks are stored in /data/misc/profile/ref. + std::string profile_dir = create_primary_reference_profile_package_dir_path(location); + return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME.c_str()); + } } std::vector get_known_users(const char* volume_uuid) { @@ -1163,13 +1192,15 @@ int validate_system_app_path(const char* path) { return -1; } -bool validate_secondary_dex_path(const char* pkgname, const char* path, +bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path, const char* volume_uuid, int uid, int storage_flag) { CHECK(storage_flag == FLAG_STORAGE_CE || storage_flag == FLAG_STORAGE_DE); std::string app_private_dir = storage_flag == FLAG_STORAGE_CE - ? create_data_user_ce_package_path(volume_uuid, multiuser_get_user_id(uid), pkgname) - : create_data_user_de_package_path(volume_uuid, multiuser_get_user_id(uid), pkgname); + ? create_data_user_ce_package_path( + volume_uuid, multiuser_get_user_id(uid), pkgname.c_str()) + : create_data_user_de_package_path( + volume_uuid, multiuser_get_user_id(uid), pkgname.c_str()); dir_rec_t dir; if (get_path_from_string(&dir, app_private_dir.c_str()) != 0) { LOG(WARNING) << "Could not get dir rec for " << app_private_dir; @@ -1179,7 +1210,7 @@ bool validate_secondary_dex_path(const char* pkgname, const char* path, // Pick at most 10 subdirectories when validating (arbitrary value). // If the secondary dex file is >10 directory nested then validation will // fail and the file will not be compiled. - return validate_path(&dir, path, /*max_subdirs*/ 10) == 0; + return validate_path(&dir, dex_path.c_str(), /*max_subdirs*/ 10) == 0; } /** diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 8090b18993..c540c52fcb 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -94,15 +94,19 @@ std::string create_data_media_package_path(const char* volume_uuid, userid_t use std::string create_data_misc_legacy_path(userid_t userid); -std::string create_data_user_profile_path(userid_t userid); -std::string create_data_user_profile_package_path(userid_t user, const std::string& package_name); +std::string create_data_dalvik_cache_path(); -std::string create_data_ref_profile_path(); -std::string create_data_ref_profile_package_path(const std::string& package_name); +std::string create_primary_cur_profile_dir_path(userid_t userid); +std::string create_primary_current_profile_package_dir_path( + userid_t user, const std::string& package_name); -std::string create_data_dalvik_cache_path(); +std::string create_primary_ref_profile_dir_path(); +std::string create_primary_reference_profile_package_dir_path(const std::string& package_name); -std::string create_primary_profile(const std::string& profile_dir); +std::string create_current_profile_path( + userid_t user, const std::string& package_name, bool is_secondary_dex); +std::string create_reference_profile_path( + const std::string& package_name, bool is_secondary_dex); std::vector get_known_users(const char* volume_uuid); @@ -147,7 +151,7 @@ void clear_cache_files(const std::string& data_path, cache_t* cache, int64_t fre void finish_cache_collection(cache_t* cache); int validate_system_app_path(const char* path); -bool validate_secondary_dex_path(const char* pkgname, const char* path, +bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path, const char* volume_uuid, int uid, int storage_flag); int get_path_from_env(dir_rec_t* rec, const char* var); -- 2.11.0