From 2a727d7bd52555ff2493ffdbc1f421b3228d04cc Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Fri, 15 Apr 2016 19:33:46 +0100 Subject: [PATCH] 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 --- core/java/android/app/ActivityThread.java | 73 +----------------- core/java/android/app/LoadedApk.java | 91 +++++++++++++++++++++++ 2 files changed, 93 insertions(+), 71 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 97bc570aff4cf..1d9a582d2fe10 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -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 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 diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index e090aa442d8ea..849636f5b5dd6 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -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 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