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
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index f9235e5..b5f0fb2 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -351,17 +351,20 @@
}
if (property_get_bool("dalvik.vm.usejitprofiles", false)) {
- const std::string profile_path = create_data_user_profile_package_path(userId, pkgname);
+ const std::string profile_dir =
+ create_primary_current_profile_package_dir_path(userId, pkgname);
// read-write-execute only for the app user.
- if (fs_prepare_dir_strict(profile_path.c_str(), 0700, uid, uid) != 0) {
- return error("Failed to prepare " + profile_path);
+ if (fs_prepare_dir_strict(profile_dir.c_str(), 0700, uid, uid) != 0) {
+ return error("Failed to prepare " + profile_dir);
}
- std::string profile_file = create_primary_profile(profile_path);
+ const std::string profile_file = create_current_profile_path(userId, pkgname,
+ /*is_secondary_dex*/false);
// read-write only for the app user.
if (fs_prepare_file_strict(profile_file.c_str(), 0600, uid, uid) != 0) {
- return error("Failed to prepare " + profile_path);
+ return error("Failed to prepare " + profile_file);
}
- const std::string ref_profile_path = create_data_ref_profile_package_path(pkgname);
+ const std::string ref_profile_path =
+ create_primary_reference_profile_package_dir_path(pkgname);
// dex2oat/profman runs under the shared app gid and it needs to read/write reference
// profiles.
int shared_app_gid = multiuser_get_shared_gid(0, appId);
@@ -425,10 +428,10 @@
std::lock_guard<std::recursive_mutex> 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 @@
}
}
if (!only_cache) {
- if (!clear_current_profile(packageName, userId)) {
+ if (!clear_primary_current_profile(packageName, userId)) {
res = error("Failed to clear current profile for " + packageName);
}
}
@@ -486,13 +489,13 @@
static int destroy_app_reference_profile(const std::string& pkgname) {
return delete_dir_contents_and_dir(
- create_data_ref_profile_package_path(pkgname),
+ create_primary_reference_profile_package_dir_path(pkgname),
/*ignore_if_missing*/ true);
}
static int destroy_app_current_profiles(const std::string& pkgname, userid_t userid) {
return delete_dir_contents_and_dir(
- create_data_user_profile_package_path(userid, pkgname),
+ create_primary_current_profile_package_dir_path(userid, pkgname),
/*ignore_if_missing*/ true);
}
@@ -719,7 +722,7 @@
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete " + path);
}
- path = create_data_user_profile_path(userId);
+ path = create_primary_cur_profile_dir_path(userId);
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete " + path);
}
@@ -1216,9 +1219,9 @@
ATRACE_END();
ATRACE_BEGIN("profiles");
- auto userProfilePath = create_data_user_profile_package_path(userId, pkgname);
+ auto userProfilePath = create_primary_current_profile_package_dir_path(userId, pkgname);
calculate_tree_size(userProfilePath, &stats.dataSize);
- auto refProfilePath = create_data_ref_profile_package_path(pkgname);
+ auto refProfilePath = create_primary_reference_profile_package_dir_path(pkgname);
calculate_tree_size(refProfilePath, &stats.codeSize);
ATRACE_END();
@@ -1236,7 +1239,7 @@
calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
sharedGid, -1);
}
- calculate_tree_size(create_data_user_profile_path(userId), &stats.dataSize,
+ calculate_tree_size(create_primary_cur_profile_dir_path(userId), &stats.dataSize,
multiuser_get_uid(userId, appId), -1);
ATRACE_END();
}
@@ -1310,9 +1313,9 @@
ATRACE_END();
ATRACE_BEGIN("profile");
- auto userProfilePath = create_data_user_profile_path(userId);
+ auto userProfilePath = create_primary_cur_profile_dir_path(userId);
calculate_tree_size(userProfilePath, &stats.dataSize, -1, -1, true);
- auto refProfilePath = create_data_ref_profile_path();
+ auto refProfilePath = create_primary_ref_profile_dir_path();
calculate_tree_size(refProfilePath, &stats.codeSize, -1, -1, true);
ATRACE_END();
@@ -1334,7 +1337,7 @@
ATRACE_BEGIN("dalvik");
calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
-1, -1, true);
- calculate_tree_size(create_data_user_profile_path(userId), &stats.dataSize,
+ calculate_tree_size(create_primary_cur_profile_dir_path(userId), &stats.dataSize,
-1, -1, true);
ATRACE_END();
@@ -1367,9 +1370,9 @@
ATRACE_END();
ATRACE_BEGIN("profile");
- auto userProfilePath = create_data_user_profile_path(userId);
+ auto userProfilePath = create_primary_cur_profile_dir_path(userId);
calculate_tree_size(userProfilePath, &stats.dataSize);
- auto refProfilePath = create_data_ref_profile_path();
+ auto refProfilePath = create_primary_ref_profile_dir_path();
calculate_tree_size(refProfilePath, &stats.codeSize);
ATRACE_END();
@@ -1384,7 +1387,7 @@
ATRACE_BEGIN("dalvik");
calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize);
- calculate_tree_size(create_data_user_profile_path(userId), &stats.dataSize);
+ calculate_tree_size(create_primary_cur_profile_dir_path(userId), &stats.dataSize);
ATRACE_END();
}
@@ -1549,8 +1552,7 @@
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
std::lock_guard<std::recursive_mutex> 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 0d5652f..5bb2ce7 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -50,6 +50,18 @@
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<void*>(ptr));
+ }
+};
+
+// Alias for std::unique_ptr<> that uses the C function free() to delete objects.
+template <typename T>
+using UniqueCPtr = std::unique_ptr<T, FreeDelete>;
+
static unique_fd invalid_unique_fd() {
return unique_fd(-1);
}
@@ -106,27 +118,40 @@
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<userid_t> 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 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;
- }
- }
- return profile_dir_fd;
-}
-
-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();
- }
-
- 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.
- if (errno != ENOENT) {
- PLOG(ERROR) << "Failed to open profile : " << profile_file;
- }
- }
- return profile_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_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);
+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) {
- return invalid_unique_fd();
+ if (errno == EEXIST) {
+ return true;
+ } else {
+ PLOG(ERROR) << "Failed to create profile " << profile;
+ return false;
+ }
}
+ // 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_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) {
- // Fix the owner.
- if (fchown(fd.get(), uid, uid) < 0) {
+ 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;
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags)));
+ if (fd.get() < 0) {
+ if (errno != ENOENT) {
+ // 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 fd;
}
-static void open_profile_files(uid_t uid, const std::string& pkgname,
+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& 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& location, bool is_secondary_dex,
/*out*/ std::vector<unique_fd>* 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);
- std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
+ // 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<userid_t> users;
+ if (is_secondary_dex){
+ users.push_back(multiuser_get_user_id(uid));
+ } else {
+ 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 @@
}
// 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<unique_fd> 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 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 @@
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 @@
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 @@
}
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<unique_fd>& profile_fds,
const unique_fd& reference_profile_fd,
const std::vector<std::string>& dex_locations,
@@ -729,7 +784,8 @@
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 @@
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 @@
// 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 @@
}
// 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 @@
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 @@
}
// 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 @@
}
// 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 @@
// 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 @@
// 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 @@
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<char> 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 @@
}
// 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 @@
// 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 @@
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 @@
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 @@
// 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 df6d176..dbf3fae 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -32,13 +32,23 @@
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 940046f..d1e5919 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -521,29 +521,50 @@
}
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 a6fa656..bdd62e6 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -213,20 +213,22 @@
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 @@
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_primary_profile(const std::string& profile_dir) {
- return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME);
+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 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<userid_t> get_known_users(const char* volume_uuid) {
@@ -1163,13 +1192,15 @@
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 @@
// 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 8090b18..c540c52 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -94,15 +94,19 @@
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_ref_profile_path();
-std::string create_data_ref_profile_package_path(const std::string& package_name);
-
std::string create_data_dalvik_cache_path();
-std::string create_primary_profile(const std::string& profile_dir);
+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_primary_ref_profile_dir_path();
+std::string create_primary_reference_profile_package_dir_path(const std::string& package_name);
+
+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<userid_t> get_known_users(const char* volume_uuid);
@@ -147,7 +151,7 @@
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);