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

Commit 2a727d7b authored by Calin Juravle's avatar Calin Juravle
Browse files

Move profile registration from bindApplication to LoadedApk

In order to capture profiles for apps which share the same VM and for
splits loaded without restart we need to move the profile registration
closer to where we create the class loaders (since bindApplication is
not called multiple times).

Moving the registration after class loader creation also allows us to
not profile apks which are already fully compiled.

Bug: 27539488
Bug: 27893309
Bug: 28012567
Change-Id: I6333f026f4f0680ca28e989f1686e3eac1145339
parent 8f5770dc
Loading
Loading
Loading
Loading
+2 −71
Original line number Diff line number Diff line
@@ -4970,70 +4970,6 @@ public final class ActivityThread {
        }
    }

    // Keep in sync with installd (frameworks/native/cmds/installd/commands.cpp).
    private static File getPrimaryProfileFile(String packageName) {
        File profileDir = Environment.getDataProfilesDePackageDirectory(
                UserHandle.myUserId(), packageName);
        return new File(profileDir, "primary.prof");
    }

    private static void setupJitProfileSupport(LoadedApk loadedApk, File cacheDir) {
        if (!SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) {
            return;
        }
        final ApplicationInfo appInfo = loadedApk.getApplicationInfo();
        final List<String> codePaths = new ArrayList<>();
        if ((appInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
            codePaths.add(appInfo.sourceDir);
        }
        if (appInfo.splitSourceDirs != null) {
            Collections.addAll(codePaths, appInfo.splitSourceDirs);
        }

        if (codePaths.isEmpty()) {
            // If there are no code paths there's no need to setup a profile file and register with
            // the runtime,
            return;
        }

        final File profileFile = getPrimaryProfileFile(loadedApk.mPackageName);
        if (!profileFile.exists()) {
            FileDescriptor fd = null;
            try {
                final int permissions = 0600;  // read-write for user.
                fd = Os.open(profileFile.getAbsolutePath(), OsConstants.O_CREAT, permissions);
                Os.fchmod(fd, permissions);
                Os.fchown(fd, appInfo.uid, appInfo.uid);
            } catch (ErrnoException e) {
                Log.v(TAG, "Unable to create jit profile file "
                        + profileFile + ": " + e.getMessage());
                try {
                    Os.unlink(profileFile.getAbsolutePath());
                } catch (ErrnoException unlinkErr) {
                    if (unlinkErr.errno != OsConstants.ENOENT) {
                        Log.v(TAG, "Unable to unlink jit profile file "
                                + profileFile + ": " + unlinkErr.getMessage());
                    }
                }
                return;
            } finally {
                IoUtils.closeQuietly(fd);
            }
        }

        final File foreignDexProfilesFile =
                Environment.getDataProfilesDeForeignDexDirectory(UserHandle.myUserId());
        String foreignDexProfilesPath = null;
        if (!foreignDexProfilesFile.exists()) {
            Log.v(TAG, "ForeignDexProfilesPath does not exists:" +
                    foreignDexProfilesFile.getPath());
        } else {
            foreignDexProfilesPath = foreignDexProfilesFile.getAbsolutePath();
        }
        VMRuntime.registerAppInfo(profileFile.getAbsolutePath(), appInfo.dataDir,
                codePaths.toArray(new String[codePaths.size()]), foreignDexProfilesPath);
    }

    private void updateDefaultDensity() {
        final int densityDpi = mCurDefaultDisplayDpi;
        if (!mDensityCompatMode
@@ -5259,18 +5195,13 @@ public final class ActivityThread {
                        + "due to missing cache directory");
            }

            // Setup a location to store generated/compiled graphics code and
            // JIT profiling data. Note that this data is stored in a
            // device-protected storage area, so these caches must never contain
            // user sensitive user data.
            // Setup a location to store generated/compiled graphics code.
            final Context deviceContext = appContext.createDeviceProtectedStorageContext();
            final File codeCacheDir = deviceContext.getCodeCacheDir();
            if (codeCacheDir != null) {
                setupGraphicsSupport(data.info, codeCacheDir);
                setupJitProfileSupport(data.info, codeCacheDir);
            } else {
                Log.e(TAG, "Unable to setupGraphicsSupport and setupJitProfileSupport " +
                        "due to missing code-cache directory");
                Log.e(TAG, "Unable to setupGraphicsSupport due to missing code-cache directory");
            }

            // Add the lib dir path to hardware renderer so that vulkan layers
+91 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.content.res.AssetManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
@@ -38,6 +39,9 @@ import android.os.StrictMode;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.system.Os;
import android.system.OsConstants;
import android.system.ErrnoException;
import android.text.TextUtils;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
@@ -50,6 +54,7 @@ import android.view.DisplayAdjustments;
import dalvik.system.VMRuntime;

import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
@@ -62,6 +67,8 @@ import java.util.Enumeration;
import java.util.List;
import java.util.Objects;

import libcore.io.IoUtils;

final class IntentReceiverLeaked extends AndroidRuntimeException {
    public IntentReceiverLeaked(String msg) {
        super(msg);
@@ -488,6 +495,13 @@ public final class LoadedApk {
            final String add = TextUtils.join(File.pathSeparator, addedPaths);
            ApplicationLoaders.getDefault().addPath(mClassLoader, add);
        }

        // Setup jit profile support.
        // It is ok to call this multiple times if the application gets updated with new splits.
        // The runtime only keeps track of unique code paths and can handle re-registration of
        // the same code path. There's no need to pass `addedPaths` since any new code paths
        // are already in `mApplicationInfo`.
        setupJitProfileSupport();
    }

    public ClassLoader getClassLoader() {
@@ -499,6 +513,83 @@ public final class LoadedApk {
        }
    }

    // Keep in sync with installd (frameworks/native/cmds/installd/commands.cpp).
    private static File getPrimaryProfileFile(String packageName) {
        File profileDir = Environment.getDataProfilesDePackageDirectory(
                UserHandle.myUserId(), packageName);
        return new File(profileDir, "primary.prof");
    }

    private void setupJitProfileSupport() {
        if (!SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) {
            return;
        }
        final List<String> codePaths = new ArrayList<>();
        if ((mApplicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
            codePaths.add(mApplicationInfo.sourceDir);
        }
        if (mApplicationInfo.splitSourceDirs != null) {
            Collections.addAll(codePaths, mApplicationInfo.splitSourceDirs);
        }

        if (codePaths.isEmpty()) {
            // If there are no code paths there's no need to setup a profile file and register with
            // the runtime,
            return;
        }

        final File profileFile = getPrimaryProfileFile(mPackageName);
        if (profileFile.exists()) {
            if (!profileFile.canRead() || !profileFile.canWrite()) {
                // The apk might be loaded in a context where we don't have permissions
                // to track the profile (e.g. when loaded by another app via
                // createApplicationContext)
                return;
            }
        } else {
            // Profile does not exist. Create it.
            FileDescriptor fd = null;
            try {
                final int permissions = 0600;  // read-write for user.
                fd = Os.open(profileFile.getAbsolutePath(), OsConstants.O_CREAT, permissions);
                Os.fchmod(fd, permissions);
                Os.fchown(fd, mApplicationInfo.uid, mApplicationInfo.uid);
            } catch (ErrnoException e) {
                if (e.errno == OsConstants.EACCES) {
                    // It can happen that the profile file does not exist but the apk is loaded in a
                    // context where we don't have permissions (e.g. when loaded by another app via
                    // createApplicationContext)
                    return;
                }
                Log.v(TAG, "Unable to create jit profile file "
                        + profileFile + ": " + e.getMessage());
                try {
                    Os.unlink(profileFile.getAbsolutePath());
                } catch (ErrnoException unlinkErr) {
                    if (unlinkErr.errno != OsConstants.ENOENT) {
                        Log.v(TAG, "Unable to unlink jit profile file "
                                + profileFile + ": " + unlinkErr.getMessage());
                    }
                }
                return;
            } finally {
                IoUtils.closeQuietly(fd);
            }
        }

        final File foreignDexProfilesFile =
                Environment.getDataProfilesDeForeignDexDirectory(UserHandle.myUserId());
        String foreignDexProfilesPath = null;
        if (!foreignDexProfilesFile.exists()) {
            Log.v(TAG, "ForeignDexProfilesPath does not exists:" +
                    foreignDexProfilesFile.getPath());
        } else {
            foreignDexProfilesPath = foreignDexProfilesFile.getAbsolutePath();
        }
        VMRuntime.registerAppInfo(profileFile.getAbsolutePath(), mApplicationInfo.dataDir,
                codePaths.toArray(new String[codePaths.size()]), foreignDexProfilesPath);
    }

    /**
     * Setup value for Thread.getContextClassLoader(). If the
     * package will not run in in a VM with other packages, we set