Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e61189e0 authored by Calin Juravle's avatar Calin Juravle
Browse files

[installd] Create profile snaphots for boot image

Create the profile snapshot for the boot image by aggregating all primary
profiles. During aggregation data that does not belong to the boot image
is filtered out. The matching is done based on the dex files provided in
the classpath argument.

Test: installd_dexopt_test
Bug: 30934496
Change-Id: Ib980ab3feb9f9838dff81a3861693cd08b1df9ab
parent 562de815
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -1872,12 +1872,13 @@ binder::Status InstalldNativeService::mergeProfiles(int32_t uid, const std::stri
}

binder::Status InstalldNativeService::createProfileSnapshot(int32_t appId,
        const std::string& packageName, const std::string& profileName, bool* _aidl_return) {
        const std::string& packageName, const std::string& profileName,
        const std::string& classpath, bool* _aidl_return) {
    ENFORCE_UID(AID_SYSTEM);
    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
    std::lock_guard<std::recursive_mutex> lock(mLock);

    *_aidl_return = create_profile_snapshot(appId, packageName, profileName);
    *_aidl_return = create_profile_snapshot(appId, packageName, profileName, classpath);
    return ok();
}

+1 −1
Original line number Diff line number Diff line
@@ -100,7 +100,7 @@ public:
    binder::Status destroyAppProfiles(const std::string& packageName);

    binder::Status createProfileSnapshot(int32_t appId, const std::string& packageName,
            const std::string& profileName, bool* _aidl_return);
            const std::string& profileName, const std::string& classpath, bool* _aidl_return);
    binder::Status destroyProfileSnapshot(const std::string& packageName,
            const std::string& profileName);

+1 −1
Original line number Diff line number Diff line
@@ -65,7 +65,7 @@ interface IInstalld {
    void destroyAppProfiles(@utf8InCpp String packageName);

    boolean createProfileSnapshot(int appId, @utf8InCpp String packageName,
            @utf8InCpp String profileName);
            @utf8InCpp String profileName, @utf8InCpp String classpath);
    void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName);

    void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid);
+130 −12
Original line number Diff line number Diff line
@@ -716,27 +716,34 @@ static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3;
static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4;

static void run_profman_merge(const std::vector<unique_fd>& profiles_fd,
        const unique_fd& reference_profile_fd) {
    static const size_t MAX_INT_LEN = 32;
        const unique_fd& reference_profile_fd, const std::vector<unique_fd>* apk_fds = nullptr) {
    const char* profman_bin = is_debug_runtime() ? "/system/bin/profmand" : "/system/bin/profman";

    std::vector<std::string> profile_args(profiles_fd.size());
    char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN];
    for (size_t k = 0; k < profiles_fd.size(); k++) {
        sprintf(profile_buf, "--profile-file-fd=%d", profiles_fd[k].get());
        profile_args[k].assign(profile_buf);
        profile_args[k] = "--profile-file-fd=" + std::to_string(profiles_fd[k].get());
    }
    std::string reference_profile_arg = "--reference-profile-file-fd="
            + std::to_string(reference_profile_fd.get());

    std::vector<std::string> apk_args;
    if (apk_fds != nullptr) {
        for (size_t k = 0; k < apk_fds->size(); k++) {
            apk_args.push_back("--apk-fd=" + std::to_string((*apk_fds)[k].get()));
        }
    }
    char reference_profile_arg[strlen("--reference-profile-file-fd=") + MAX_INT_LEN];
    sprintf(reference_profile_arg, "--reference-profile-file-fd=%d", reference_profile_fd.get());

    // program name, reference profile fd, the final NULL and the profile fds
    const char* argv[3 + profiles_fd.size()];
    const char* argv[3 + profile_args.size() + apk_args.size()];
    int i = 0;
    argv[i++] = profman_bin;
    argv[i++] = reference_profile_arg;
    argv[i++] = reference_profile_arg.c_str();
    for (size_t k = 0; k < profile_args.size(); k++) {
        argv[i++] = profile_args[k].c_str();
    }
    for (size_t k = 0; k < apk_args.size(); k++) {
        argv[i++] = apk_args[k].c_str();
    }
    // Do not add after dex2oat_flags, they should override others for debugging.
    argv[i] = NULL;

@@ -2441,8 +2448,24 @@ bool create_cache_path_default(char path[PKG_PATH_MAX], const char *src,
    }
}

bool create_profile_snapshot(int32_t app_id, const std::string& package_name,
        const std::string& profile_name) {
bool open_classpath_files(const std::string& classpath, std::vector<unique_fd>* apk_fds) {
    std::vector<std::string> classpaths_elems = base::Split(classpath, ":");
    for (const std::string& elem : classpaths_elems) {
        unique_fd fd(TEMP_FAILURE_RETRY(open(elem.c_str(), O_RDONLY)));
        if (fd < 0) {
            PLOG(ERROR) << "Could not open classpath elem " << elem;
            return false;
        } else {
            apk_fds->push_back(std::move(fd));
        }
    }
    return true;
}

static bool create_app_profile_snapshot(int32_t app_id,
                                        const std::string& package_name,
                                        const std::string& profile_name,
                                        const std::string& classpath) {
    int app_shared_gid = multiuser_get_shared_gid(/*user_id*/ 0, app_id);

    unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name);
@@ -2460,11 +2483,18 @@ bool create_profile_snapshot(int32_t app_id, const std::string& package_name,

    profiles_fd.push_back(std::move(reference_profile_fd));

    // Open the class paths elements. These will be used to filter out profile data that does
    // not belong to the classpath during merge.
    std::vector<unique_fd> apk_fds;
    if (!open_classpath_files(classpath, &apk_fds)) {
        return false;
    }

    pid_t pid = fork();
    if (pid == 0) {
        /* child -- drop privileges before continuing */
        drop_capabilities(app_shared_gid);
        run_profman_merge(profiles_fd, snapshot_fd);
        run_profman_merge(profiles_fd, snapshot_fd, &apk_fds);
        exit(42);   /* only get here on exec failure */
    }

@@ -2478,6 +2508,94 @@ bool create_profile_snapshot(int32_t app_id, const std::string& package_name,
    return true;
}

static bool create_boot_image_profile_snapshot(const std::string& package_name,
                                               const std::string& profile_name,
                                               const std::string& classpath) {
    // The reference profile directory for the android package might not be prepared. Do it now.
    const std::string ref_profile_dir =
            create_primary_reference_profile_package_dir_path(package_name);
    if (fs_prepare_dir(ref_profile_dir.c_str(), 0770, AID_SYSTEM, AID_SYSTEM) != 0) {
        PLOG(ERROR) << "Failed to prepare " << ref_profile_dir;
        return false;
    }

    // Open and create the snapshot profile.
    unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name);

    // Collect all non empty profiles.
    // The collection will traverse all applications profiles and find the non empty files.
    // This has the potential of inspecting a large number of files and directories (depending
    // on the number of applications and users). So there is a slight increase in the chance
    // to get get occasionally I/O errors (e.g. for opening the file). When that happens do not
    // fail the snapshot and aggregate whatever profile we could open.
    //
    // The profile snapshot is a best effort based on available data it's ok if some data
    // from some apps is missing. It will be counter productive for the snapshot to fail
    // because we could not open or read some of the files.
    std::vector<std::string> profiles;
    if (!collect_profiles(&profiles)) {
        LOG(WARNING) << "There were errors while collecting the profiles for the boot image.";
    }

    // If we have no profiles return early.
    if (profiles.empty()) {
        return true;
    }

    // Open the classpath elements. These will be used to filter out profile data that does
    // not belong to the classpath during merge.
    std::vector<unique_fd> apk_fds;
    if (!open_classpath_files(classpath, &apk_fds)) {
        return false;
    }

    // If we could not open any files from the classpath return an error.
    if (apk_fds.empty()) {
        LOG(ERROR) << "Could not open any of the classpath elements.";
        return false;
    }

    // Aggregate the profiles in batches of kAggregationBatchSize.
    // We do this to avoid opening a huge a amount of files.
    static constexpr size_t kAggregationBatchSize = 10;

    std::vector<unique_fd> profiles_fd;
    for (size_t i = 0; i < profiles.size(); )  {
        for (size_t k = 0; k < kAggregationBatchSize && i < profiles.size(); k++, i++) {
            unique_fd fd = open_profile(AID_SYSTEM, profiles[i], O_RDONLY);
            if (fd.get() >= 0) {
                profiles_fd.push_back(std::move(fd));
            }
        }
        pid_t pid = fork();
        if (pid == 0) {
            /* child -- drop privileges before continuing */
            drop_capabilities(AID_SYSTEM);

            run_profman_merge(profiles_fd, snapshot_fd, &apk_fds);
            exit(42);   /* only get here on exec failure */
        }

        /* parent */
        int return_code = wait_child(pid);
        if (!WIFEXITED(return_code)) {
            PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name;
            return false;
        }
        return true;
    }
    return true;
}

bool create_profile_snapshot(int32_t app_id, const std::string& package_name,
        const std::string& profile_name, const std::string& classpath) {
    if (app_id == -1) {
        return create_boot_image_profile_snapshot(package_name, profile_name, classpath);
    } else {
        return create_app_profile_snapshot(app_id, package_name, profile_name, classpath);
    }
}

bool prepare_app_profile(const std::string& package_name,
                         userid_t user_id,
                         appid_t app_id,
+9 −2
Original line number Diff line number Diff line
@@ -52,6 +52,8 @@ bool analyze_primary_profiles(uid_t uid,
                              const std::string& profile_name);

// Create a snapshot of the profile information for the given package profile.
// If appId is -1, the method creates the profile snapshot for the boot image.
//
// The profile snapshot is the aggregation of all existing profiles (all current user
// profiles & the reference profile) and is meant to capture the all the profile information
// without performing a merge into the reference profile which might impact future dex2oat
@@ -60,8 +62,13 @@ bool analyze_primary_profiles(uid_t uid,
// ownership is assigned to AID_SYSTEM.
// The snapshot location is reference_profile_location.snapshot. If a snapshot is already
// there, it will be truncated and overwritten.
bool create_profile_snapshot(int32_t app_id, const std::string& package,
        const std::string& profile_name);
//
// The classpath acts as filter: only profiling data belonging to elements of the classpath
// will end up in the snapshot.
bool create_profile_snapshot(int32_t app_id,
                             const std::string& package,
                             const std::string& profile_name,
                             const std::string& classpath);

bool dump_profiles(int32_t uid,
                   const std::string& pkgname,
Loading