OSDN Git Service

Merge "Support profile guided compilation for secondary dex files" am: cb2e477f14...
authorCalin Juravle <calin@google.com>
Fri, 17 Mar 2017 00:09:58 +0000 (00:09 +0000)
committerandroid-build-merger <android-build-merger@google.com>
Fri, 17 Mar 2017 00:09:58 +0000 (00:09 +0000)
am: 95c5f4b85d

Change-Id: I432fd0a8e7056b5d41e6beb9a71d8fd74c3a34b8

cmds/installd/InstalldNativeService.cpp
cmds/installd/dexopt.cpp
cmds/installd/dexopt.h
cmds/installd/tests/installd_utils_test.cpp
cmds/installd/utils.cpp
cmds/installd/utils.h

index 754ba4c..9006330 100644 (file)
@@ -359,17 +359,20 @@ binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::s
         }
 
         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);
@@ -433,10 +436,10 @@ binder::Status InstalldNativeService::clearAppProfiles(const std::string& packag
     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;
@@ -484,7 +487,7 @@ binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::st
             }
         }
         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);
             }
         }
@@ -494,13 +497,13 @@ binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::st
 
 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);
 }
 
@@ -727,7 +730,7 @@ binder::Status InstalldNativeService::destroyUserData(const std::unique_ptr<std:
             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);
             }
@@ -1237,9 +1240,9 @@ binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::stri
             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();
 
@@ -1257,7 +1260,7 @@ binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::stri
             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();
     }
@@ -1332,9 +1335,9 @@ binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::str
         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();
 
@@ -1356,7 +1359,7 @@ binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::str
         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();
 
@@ -1389,9 +1392,9 @@ binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::str
         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();
 
@@ -1406,7 +1409,7 @@ binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::str
 
         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();
     }
 
@@ -1577,8 +1580,7 @@ binder::Status InstalldNativeService::mergeProfiles(int32_t uid, const std::stri
     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();
 }
 
index 0d5652f..5bb2ce7 100644 (file)
@@ -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<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 @@ 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<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 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<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);
+
+    // 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);
     }
-
-    std::vector<userid_t> 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<unique_fd>& 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<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 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<unique_fd>& profile_fds,
                              const unique_fd& reference_profile_fd,
                              const std::vector<std::string>& 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<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 @@ 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;
     }
 
index df6d176..dbf3fae 100644 (file)
@@ -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);
index 850d257..49605be 100644 (file)
@@ -525,29 +525,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
index 5a08c32..97298e5 100644 (file)
@@ -217,20 +217,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());
 }
@@ -239,11 +241,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<userid_t> get_known_users(const char* volume_uuid) {
@@ -1182,13 +1211,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;
@@ -1198,7 +1229,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;
 }
 
 /**
index 1c36a54..abe6830 100644 (file)
@@ -96,15 +96,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<userid_t> get_known_users(const char* volume_uuid);
 
@@ -151,7 +155,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);