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

Commit f18cec26 authored by Calin Juravle's avatar Calin Juravle Committed by Android (Google) Code Review
Browse files

Merge "Register secondary dex files for JIT profiling"

parents 321c5a19 f5a7bfc8
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -5579,7 +5579,7 @@ public final class ActivityThread {
        // Make sure we do this before calling onCreate so that we can capture the
        // complete application startup.
        if (SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) {
            BaseDexClassLoader.setReporter(DexLoadReporter.INSTANCE);
            BaseDexClassLoader.setReporter(DexLoadReporter.getInstance());
        }

        // Install the Network Security Config Provider. This must happen before the application
+113 −2
Original line number Diff line number Diff line
@@ -16,13 +16,21 @@

package android.app;

import android.os.FileUtils;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;

import dalvik.system.BaseDexClassLoader;
import dalvik.system.VMRuntime;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * A dex load reporter which will notify package manager of any dex file loaded
@@ -37,15 +45,60 @@ import java.util.List;
/*package*/ class DexLoadReporter implements BaseDexClassLoader.Reporter {
    private static final String TAG = "DexLoadReporter";

    /*package*/ static final DexLoadReporter INSTANCE = new DexLoadReporter();
    private static final DexLoadReporter INSTANCE = new DexLoadReporter();

    private static final boolean DEBUG = false;

    // We must guard the access to the list of data directories because
    // we might have concurrent accesses. Apps might load dex files while
    // new data dirs are registered (due to creation of LoadedApks via
    // create createApplicationContext).
    @GuardedBy("mDataDirs")
    private final Set<String> mDataDirs;

    private DexLoadReporter() {
        mDataDirs = new HashSet<>();
    }

    private DexLoadReporter() {}
    /*package*/ static DexLoadReporter getInstance() {
        return INSTANCE;
    }

    /**
     * Register an application data directory with the reporter.
     * The data directories are used to determine if a dex file is secondary dex or not.
     * Note that this method may be called multiple times for the same app, registering
     * different data directories. This may happen when apps share the same user id
     * ({@code android:sharedUserId}). For example, if app1 and app2 share the same user
     * id, and app1 loads app2 apk, then both data directories will be registered.
     */
    /*package*/ void registerAppDataDir(String packageName, String dataDir) {
        if (DEBUG) {
            Slog.i(TAG, "Package " + packageName + " registering data dir: " + dataDir);
        }
        // TODO(calin): A few code paths imply that the data dir
        // might be null. Investigate when that can happen.
        if (dataDir != null) {
            synchronized (mDataDirs) {
                mDataDirs.add(dataDir);
            }
        }
    }

    @Override
    public void report(List<String> dexPaths) {
        if (dexPaths.isEmpty()) {
            return;
        }
        // Notify the package manager about the dex loads unconditionally.
        // The load might be for either a primary or secondary dex file.
        notifyPackageManager(dexPaths);
        // Check for secondary dex files and register them for profiling if
        // possible.
        registerSecondaryDexForProfiling(dexPaths);
    }

    private void notifyPackageManager(List<String> dexPaths) {
        String packageName = ActivityThread.currentPackageName();
        try {
            ActivityThread.getPackageManager().notifyDexLoad(
@@ -54,4 +107,62 @@ import java.util.List;
            Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
        }
    }

    private void registerSecondaryDexForProfiling(List<String> dexPaths) {
        if (!SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) {
            return;
        }
        // Make a copy of the current data directories so that we don't keep the lock
        // while registering for profiling. The registration will perform I/O to
        // check for or create the profile.
        String[] dataDirs;
        synchronized (mDataDirs) {
            dataDirs = mDataDirs.toArray(new String[0]);
        }
        for (String dexPath : dexPaths) {
            registerSecondaryDexForProfiling(dexPath, dataDirs);
        }
    }

    private void registerSecondaryDexForProfiling(String dexPath, String[] dataDirs) {
        if (!isSecondaryDexFile(dexPath, dataDirs)) {
            // The dex path is not a secondary dex file. Nothing to do.
            return;
        }
        File secondaryProfile = getSecondaryProfileFile(dexPath);
        try {
            // Create the profile if not already there.
            // Returns true if the file was created, false if the file already exists.
            // or throws exceptions in case of errors.
            boolean created = secondaryProfile.createNewFile();
            if (DEBUG && created) {
                Slog.i(TAG, "Created profile for secondary dex: " + secondaryProfile);
            }
        } catch (IOException ex) {
            Slog.e(TAG, "Failed to create profile for secondary dex " + secondaryProfile +
                    ":" + ex.getMessage());
            // Don't move forward with the registration if we failed to create the profile.
            return;
        }

        VMRuntime.registerAppInfo(secondaryProfile.getPath(), new String[] { dexPath });
    }

    // A dex file is a secondary dex file if it is in any of the registered app
    // data directories.
    private boolean isSecondaryDexFile(String dexPath, String[] dataDirs) {
        for (String dataDir : dataDirs) {
            if (FileUtils.contains(dataDir, dexPath)) {
                return true;
            }
        }
        return false;
    }

    // Secondary dex profiles are stored next to the dex file and have the same
    // name with '.prof' appended.
    // NOTE: Keep in sync with installd.
    private File getSecondaryProfileFile(String dexPath) {
        return new File(dexPath + ".prof");
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -750,6 +750,11 @@ public final class LoadedApk {

        VMRuntime.registerAppInfo(profileFile.getPath(),
                codePaths.toArray(new String[codePaths.size()]));

        // Register the app data directory with the reporter. It will
        // help deciding whether or not a dex file is the primary apk or a
        // secondary dex.
        DexLoadReporter.getInstance().registerAppDataDir(mPackageName, mDataDir);
    }

    /**