Add profile guided compilation hooks in installd
Bug: 26080105
Change-Id: I6aaddffe79c55b9a6560c2d6542cf87abbd4fb11
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index 6b00429..7674c45 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -592,7 +592,8 @@
static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name,
const char* output_file_name, int swap_fd, const char *instruction_set,
- bool vm_safe_mode, bool debuggable, bool post_bootcomplete, bool use_jit)
+ bool vm_safe_mode, bool debuggable, bool post_bootcomplete, bool use_jit,
+ const std::vector<int>& profile_files_fd, const std::vector<int>& reference_profile_files_fd)
{
static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
@@ -602,6 +603,12 @@
return;
}
+ if (profile_files_fd.size() != reference_profile_files_fd.size()) {
+ ALOGE("Invalid configuration of profile files: pf_size (%zu) != rpf_size (%zu)",
+ profile_files_fd.size(), reference_profile_files_fd.size());
+ return;
+ }
+
char dex2oat_Xms_flag[kPropertyValueMax];
bool have_dex2oat_Xms_flag = get_property("dalvik.vm.dex2oat-Xms", dex2oat_Xms_flag, NULL) > 0;
@@ -712,6 +719,17 @@
(get_property("dalvik.vm.always_debuggable", prop_buf, "0") > 0) &&
(prop_buf[0] == '1');
}
+ std::vector<std::string> profile_file_args(profile_files_fd.size());
+ std::vector<std::string> reference_profile_file_args(profile_files_fd.size());
+ // "reference-profile-file-fd" is longer than "profile-file-fd" so we can
+ // use it to set the max length.
+ char profile_buf[strlen("--reference-profile-file-fd=") + MAX_INT_LEN];
+ for (size_t k = 0; k < profile_files_fd.size(); k++) {
+ sprintf(profile_buf, "--profile-file-fd=%d", profile_files_fd[k]);
+ profile_file_args[k].assign(profile_buf);
+ sprintf(profile_buf, "--reference-profile-file-fd=%d", reference_profile_files_fd[k]);
+ reference_profile_file_args[k].assign(profile_buf);
+ }
ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);
@@ -726,7 +744,9 @@
+ (have_dex2oat_relocation_skip_flag ? 2 : 0)
+ (generate_debug_info ? 1 : 0)
+ (debuggable ? 1 : 0)
- + dex2oat_flags_count];
+ + dex2oat_flags_count
+ + profile_files_fd.size()
+ + reference_profile_files_fd.size()];
int i = 0;
argv[i++] = DEX2OAT_BIN;
argv[i++] = zip_fd_arg;
@@ -770,6 +790,10 @@
argv[i++] = RUNTIME_ARG;
argv[i++] = dex2oat_norelocation;
}
+ for (size_t k = 0; k < profile_file_args.size(); k++) {
+ argv[i++] = profile_file_args[k].c_str();
+ argv[i++] = reference_profile_file_args[k].c_str();
+ }
// Do not add after dex2oat_flags, they should override others for debugging.
argv[i] = NULL;
@@ -836,8 +860,108 @@
}
}
-int dexopt(const char *apk_path, uid_t uid, const char *pkgname, const char *instruction_set,
- int dexopt_needed, const char* oat_dir, int dexopt_flags)
+constexpr const char* PROFILE_FILE_EXTENSION = ".prof";
+constexpr const char* REFERENCE_PROFILE_FILE_EXTENSION = ".prof.ref";
+
+static void close_all_fds(const std::vector<int>& fds, const char* description) {
+ for (size_t i = 0; i < fds.size(); i++) {
+ if (close(fds[i]) != 0) {
+ PLOG(WARNING) << "Failed to close fd for " << description << " at index " << i;
+ }
+ }
+}
+
+static int open_code_cache_for_user(userid_t user, const char* volume_uuid, const char* pkgname) {
+ std::string code_cache_path =
+ create_data_user_package_path(volume_uuid, user, pkgname) + CODE_CACHE_DIR_POSTFIX;
+
+ struct stat buffer;
+ // Check that the code cache exists. If not, return and don't log an error.
+ if (TEMP_FAILURE_RETRY(lstat(code_cache_path.c_str(), &buffer)) == -1) {
+ if (errno != ENOENT) {
+ PLOG(ERROR) << "Failed to lstat code_cache: " << code_cache_path;
+ return -1;
+ }
+ }
+
+ int code_cache_fd = open(code_cache_path.c_str(),
+ O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW);
+ if (code_cache_fd < 0) {
+ PLOG(ERROR) << "Failed to open code_cache: " << code_cache_path;
+ }
+ return code_cache_fd;
+}
+
+// Keep profile paths in sync with ActivityThread.
+static void open_profile_files_for_user(uid_t uid, const char* pkgname, int code_cache_fd,
+ /*out*/ int* profile_fd, /*out*/ int* reference_profile_fd) {
+ *profile_fd = -1;
+ *reference_profile_fd = -1;
+ std::string profile_file(pkgname);
+ profile_file += PROFILE_FILE_EXTENSION;
+
+ // Check if the profile exists. If not, early return and don't log an error.
+ struct stat buffer;
+ if (TEMP_FAILURE_RETRY(fstatat(
+ code_cache_fd, profile_file.c_str(), &buffer, AT_SYMLINK_NOFOLLOW)) == -1) {
+ if (errno != ENOENT) {
+ PLOG(ERROR) << "Failed to fstatat profile file: " << profile_file;
+ return;
+ }
+ }
+
+ // Open in read-write to allow transfer of information from the current profile
+ // to the reference profile.
+ *profile_fd = openat(code_cache_fd, profile_file.c_str(), O_RDWR | O_NOFOLLOW);
+ if (*profile_fd < 0) {
+ PLOG(ERROR) << "Failed to open profile file: " << profile_file;
+ return;
+ }
+
+ std::string reference_profile(pkgname);
+ reference_profile += REFERENCE_PROFILE_FILE_EXTENSION;
+ // Give read-write permissions just for the user (changed with fchown after opening).
+ // We need write permission because dex2oat will update the reference profile files
+ // with the content of the corresponding current profile files.
+ *reference_profile_fd = openat(code_cache_fd, reference_profile.c_str(),
+ O_CREAT | O_RDWR | O_NOFOLLOW, S_IWUSR | S_IRUSR);
+ if (*reference_profile_fd < 0) {
+ close(*profile_fd);
+ return;
+ }
+ if (fchown(*reference_profile_fd, uid, uid) < 0) {
+ PLOG(ERROR) << "Cannot change reference profile file owner: " << reference_profile;
+ close(*profile_fd);
+ *profile_fd = -1;
+ *reference_profile_fd = -1;
+ }
+}
+
+static void open_profile_files(const char* volume_uuid, uid_t uid, const char* pkgname,
+ std::vector<int>* profile_fds, std::vector<int>* reference_profile_fds) {
+ std::vector<userid_t> users = get_known_users(volume_uuid);
+ for (auto user : users) {
+ int code_cache_fd = open_code_cache_for_user(user, volume_uuid, pkgname);
+ if (code_cache_fd < 0) {
+ continue;
+ }
+ int profile_fd = -1;
+ int reference_profile_fd = -1;
+ open_profile_files_for_user(
+ uid, pkgname, code_cache_fd, &profile_fd, &reference_profile_fd);
+ close(code_cache_fd);
+
+ // Add to the lists only if both fds are valid.
+ if ((profile_fd >= 0) && (reference_profile_fd >= 0)) {
+ profile_fds->push_back(profile_fd);
+ reference_profile_fds->push_back(reference_profile_fd);
+ }
+ }
+}
+
+int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* instruction_set,
+ int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* volume_uuid,
+ bool use_profiles)
{
struct utimbuf ut;
struct stat input_stat;
@@ -851,6 +975,16 @@
bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0;
bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0;
bool use_jit = (dexopt_flags & DEXOPT_USEJIT) != 0;
+ std::vector<int> profile_files_fd;
+ std::vector<int> reference_profile_files_fd;
+ if (use_profiles) {
+ open_profile_files(volume_uuid, uid, pkgname,
+ &profile_files_fd, &reference_profile_files_fd);
+ if (profile_files_fd.empty()) {
+ // Skip profile guided compilation because no profiles were found.
+ return 0;
+ }
+ }
if ((dexopt_flags & ~DEXOPT_MASK) != 0) {
LOG_FATAL("dexopt flags contains unknown fields\n");
@@ -982,14 +1116,9 @@
|| dexopt_needed == DEXOPT_SELF_PATCHOAT_NEEDED) {
run_patchoat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set);
} else if (dexopt_needed == DEXOPT_DEX2OAT_NEEDED) {
- const char *input_file_name = strrchr(input_file, '/');
- if (input_file_name == NULL) {
- input_file_name = input_file;
- } else {
- input_file_name++;
- }
- run_dex2oat(input_fd, out_fd, input_file_name, out_path, swap_fd,
- instruction_set, vm_safe_mode, debuggable, boot_complete, use_jit);
+ run_dex2oat(input_fd, out_fd, input_file, out_path, swap_fd,
+ instruction_set, vm_safe_mode, debuggable, boot_complete, use_jit,
+ profile_files_fd, reference_profile_files_fd);
} else {
ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
exit(73);
@@ -1014,6 +1143,10 @@
if (swap_fd != -1) {
close(swap_fd);
}
+ if (use_profiles != 0) {
+ close_all_fds(profile_files_fd, "profile_files_fd");
+ close_all_fds(reference_profile_files_fd, "reference_profile_files_fd");
+ }
return 0;
fail:
@@ -1024,6 +1157,10 @@
if (input_fd >= 0) {
close(input_fd);
}
+ if (use_profiles != 0) {
+ close_all_fds(profile_files_fd, "profile_files_fd");
+ close_all_fds(reference_profile_files_fd, "reference_profile_files_fd");
+ }
return -1;
}
diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h
index 5510e7b..ab24ecf 100644
--- a/cmds/installd/commands.h
+++ b/cmds/installd/commands.h
@@ -48,7 +48,8 @@
int rm_dex(const char *path, const char *instruction_set);
int free_cache(const char *uuid, int64_t free_size);
int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set,
- int dexopt_needed, const char* oat_dir, int dexopt_flags);
+ int dexopt_needed, const char* oat_dir, int dexopt_flags,
+ const char* volume_uuid, bool use_profiles);
int mark_boot_complete(const char *instruction_set);
int movefiles();
int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId);
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index 8542c4a..62b5bb0 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -214,9 +214,10 @@
static int do_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
{
- /* apk_path, uid, pkgname, instruction_set, dexopt_needed, oat_dir, dexopt_flags */
+ /* apk_path, uid, pkgname, instruction_set, dexopt_needed, oat_dir, dexopt_flags, volume_uuid,
+ use_profiles */
return dexopt(arg[0], atoi(arg[1]), arg[2], arg[3], atoi(arg[4]),
- arg[5], atoi(arg[6]));
+ arg[5], atoi(arg[6]), parse_null(arg[7]), (atoi(arg[8]) == 0 ? false : true));
}
static int do_mark_boot_complete(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
@@ -321,7 +322,7 @@
{ "move_complete_app", 6, do_move_complete_app },
{ "get_app_size", 9, do_get_app_size },
- { "dexopt", 7, do_dexopt },
+ { "dexopt", 9, do_dexopt },
{ "markbootcomplete", 1, do_mark_boot_complete },
{ "rmdex", 2, do_rm_dex },
{ "freecache", 2, do_free_cache },