diff options
9 files changed, 124 insertions, 81 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 4e55c89fa739..5ce13634a578 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4720,6 +4720,9 @@ public final class ActivityThread { } private static void setupJitProfileSupport(LoadedApk loadedApk, File cacheDir) { + if (!SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) { + return; + } final ApplicationInfo appInfo = loadedApk.getApplicationInfo(); if (isSharingRuntime(appInfo)) { // If sharing is enabled we do not have a unique application diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java index b3222f013b3e..ed4722d985af 100644 --- a/core/java/com/android/internal/os/InstallerConnection.java +++ b/core/java/com/android/internal/os/InstallerConnection.java @@ -21,6 +21,7 @@ import android.net.LocalSocketAddress; import android.os.SystemClock; import android.text.TextUtils; import android.util.Slog; +import android.text.TextUtils; import com.android.internal.util.Preconditions; @@ -139,14 +140,24 @@ public class InstallerConnection { } public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded, - int dexFlags) throws InstallerException { - dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded, null /* outputPath */, dexFlags); + int dexFlags, String volumeUuid, boolean useProfiles) throws InstallerException { + dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded, + null /*outputPath*/, dexFlags, volumeUuid, useProfiles); } public void dexopt(String apkPath, int uid, String pkgName, String instructionSet, - int dexoptNeeded, String outputPath, int dexFlags) throws InstallerException { - execute("dexopt", apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath, - dexFlags); + int dexoptNeeded, String outputPath, int dexFlags, String volumeUuid, + boolean useProfiles) throws InstallerException { + execute("dexopt", + apkPath, + uid, + pkgName, + instructionSet, + dexoptNeeded, + outputPath, + dexFlags, + volumeUuid, + useProfiles ? '1' : '0'); } private boolean connect() { diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index eecc0eeab062..6ad9e20360e0 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -500,8 +500,11 @@ public class ZygoteInit { final int dexoptNeeded = DexFile.getDexOptNeeded( classPathElement, "*", instructionSet, false /* defer */); if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { + // System server is fully AOTed and never profiled + // for profile guided compilation. installer.dexopt(classPathElement, Process.SYSTEM_UID, instructionSet, - dexoptNeeded, 0 /*dexFlags*/); + dexoptNeeded, 0 /*dexFlags*/, null /*volumeUuid*/, + false /*useProfiles*/); } } } catch (IOException | InstallerException e) { diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 63f193d57419..cc74de1652de 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -586,6 +586,7 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) char jitmaxsizeOptsBuf[sizeof("-Xjitmaxsize:")-1 + PROPERTY_VALUE_MAX]; char jitinitialsizeOptsBuf[sizeof("-Xjitinitialsize:")-1 + PROPERTY_VALUE_MAX]; char jitthresholdOptsBuf[sizeof("-Xjitthreshold:")-1 + PROPERTY_VALUE_MAX]; + char useJitProfilesOptsBuf[sizeof("-Xjitsaveprofilinginfo:")-1 + PROPERTY_VALUE_MAX]; char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX]; char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX]; char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX]; @@ -691,6 +692,10 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) parseRuntimeOption("dalvik.vm.jitmaxsize", jitmaxsizeOptsBuf, "-Xjitmaxsize:"); parseRuntimeOption("dalvik.vm.jitinitialsize", jitinitialsizeOptsBuf, "-Xjitinitialsize:"); parseRuntimeOption("dalvik.vm.jitthreshold", jitthresholdOptsBuf, "-Xjitthreshold:"); + property_get("dalvik.vm.usejitprofiles", useJitProfilesOptsBuf, ""); + if (strcmp(useJitProfilesOptsBuf, "true") == 0) { + addOption("-Xjitsaveprofilinginfo"); + } property_get("ro.config.low_ram", propBuf, ""); if (strcmp(propBuf, "true") == 0) { diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index af20679fc250..06f828e4b4b7 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -24,6 +24,7 @@ import android.app.job.JobService; import android.content.ComponentName; import android.content.Context; import android.os.ServiceManager; +import android.os.SystemProperties; import android.util.ArraySet; import android.util.Log; @@ -50,12 +51,13 @@ public class BackgroundDexOptService extends JobService { final AtomicBoolean mIdleTime = new AtomicBoolean(false); - public static void schedule(Context context, long minLatency) { + private boolean useJitProfiles = SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false); + + public static void schedule(Context context) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo job = new JobInfo.Builder(BACKGROUND_DEXOPT_JOB, sDexoptServiceName) .setRequiresDeviceIdle(true) .setRequiresCharging(true) - .setMinimumLatency(minLatency) .setPeriodic(TimeUnit.DAYS.toMillis(1)) .build(); js.schedule(job); @@ -63,16 +65,17 @@ public class BackgroundDexOptService extends JobService { @Override public boolean onStartJob(JobParameters params) { - Log.i(TAG, "onIdleStart"); + Log.i(TAG, "onStartJob"); final PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package"); if (pm.isStorageLow()) { - schedule(BackgroundDexOptService.this, RETRY_LATENCY); + Log.i(TAG, "Low storage, skipping this run"); return false; } - final ArraySet<String> pkgs = pm.getPackagesThatNeedDexOpt(); - if (pkgs == null) { + final ArraySet<String> pkgs = pm.getOptimizablePackages(); + if (pkgs == null || pkgs.isEmpty()) { + Log.i(TAG, "No packages to optimize"); return false; } @@ -83,15 +86,14 @@ public class BackgroundDexOptService extends JobService { public void run() { for (String pkg : pkgs) { if (!mIdleTime.get()) { - // stopped while still working, so we need to reschedule - schedule(BackgroundDexOptService.this, 0); + // Out of the idle state. Stop the compilation. return; } if (sFailedPackageNames.contains(pkg)) { // skip previously failing package continue; } - if (!pm.performDexOpt(pkg, null /* instruction set */)) { + if (!pm.performDexOpt(pkg, /* instruction set */ null, useJitProfiles)) { // there was a problem running dexopt, // remember this so we do not keep retrying. sFailedPackageNames.add(pkg); diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 190eca63b183..0babdba6584e 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -128,17 +128,19 @@ public final class Installer extends SystemService { } public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded, - int dexFlags) throws InstallerException { + int dexFlags, String volumeUuid, boolean useProfiles) throws InstallerException { assertValidInstructionSet(instructionSet); - mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags); + mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags, + volumeUuid, useProfiles); } public void dexopt(String apkPath, int uid, String pkgName, String instructionSet, - int dexoptNeeded, @Nullable String outputPath, int dexFlags) + int dexoptNeeded, @Nullable String outputPath, int dexFlags, + String volumeUuid, boolean useProfiles) throws InstallerException { assertValidInstructionSet(instructionSet); mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, - outputPath, dexFlags); + outputPath, dexFlags, volumeUuid, useProfiles); } public void idmap(String targetApkPath, String overlayApkPath, int uid) diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index b45a922460d3..44b51d4f50be 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -23,6 +23,7 @@ import android.content.pm.PackageParser; import android.os.PowerManager; import android.os.UserHandle; import android.os.WorkSource; +import android.os.storage.StorageManager; import android.util.ArraySet; import android.util.Log; import android.util.Slog; @@ -68,6 +69,11 @@ final class PackageDexOptimizer { mDexoptWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*dexopt*"); } + static boolean canOptimizePackage(PackageParser.Package pkg) { + return pkg.canHaveOatDir() && + ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0); + } + /** * Performs dexopt on all code paths and libraries of the specified package for specified * instruction sets. @@ -76,7 +82,7 @@ final class PackageDexOptimizer { * {@link PackageManagerService#mInstallLock}. */ int performDexOpt(PackageParser.Package pkg, String[] instructionSets, - boolean inclDependencies) { + boolean inclDependencies, String volumeUuid, boolean useProfiles) { ArraySet<String> done; if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) { done = new ArraySet<String>(); @@ -91,7 +97,7 @@ final class PackageDexOptimizer { mDexoptWakeLock.acquire(); } try { - return performDexOptLI(pkg, instructionSets, done); + return performDexOptLI(pkg, instructionSets, done, volumeUuid, useProfiles); } finally { if (useLock) { mDexoptWakeLock.release(); @@ -101,7 +107,7 @@ final class PackageDexOptimizer { } private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets, - ArraySet<String> done) { + ArraySet<String> done, String volumeUuid, boolean useProfiles) { final String[] instructionSets = targetInstructionSets != null ? targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo); @@ -115,7 +121,7 @@ final class PackageDexOptimizer { } } - if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) { + if (!canOptimizePackage(pkg)) { return DEX_OPT_SKIPPED; } @@ -124,18 +130,15 @@ final class PackageDexOptimizer { final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly(); boolean performedDexOpt = false; - // There are three basic cases here: - // 1.) we need to dexopt, either because we are forced or it is needed - // 2.) we are deferring a needed dexopt - // 3.) we are skipping an unneeded dexopt final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); for (String dexCodeInstructionSet : dexCodeInstructionSets) { - if (pkg.mDexOptPerformed.contains(dexCodeInstructionSet)) { + if (!useProfiles && pkg.mDexOptPerformed.contains(dexCodeInstructionSet)) { + // Skip only if we do not use profiles since they might trigger a recompilation. continue; } for (String path : paths) { - final int dexoptNeeded; + int dexoptNeeded; try { dexoptNeeded = DexFile.getDexOptNeeded(path, pkg.packageName, dexCodeInstructionSet, /* defer */false); @@ -144,38 +147,46 @@ final class PackageDexOptimizer { return DEX_OPT_FAILED; } - if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { - final String dexoptType; - String oatDir = null; - if (dexoptNeeded == DexFile.DEX2OAT_NEEDED) { - dexoptType = "dex2oat"; - oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet); - } else if (dexoptNeeded == DexFile.PATCHOAT_NEEDED) { - dexoptType = "patchoat"; - } else if (dexoptNeeded == DexFile.SELF_PATCHOAT_NEEDED) { - dexoptType = "self patchoat"; + if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) { + if (useProfiles) { + // If we do a profile guided compilation then we might recompile the same + // package if more profile information is available. + dexoptNeeded = DexFile.DEX2OAT_NEEDED; } else { - throw new IllegalStateException("Invalid dexopt needed: " + dexoptNeeded); + // No dexopt needed and we don't use profiles. Nothing to do. + continue; } + } + final String dexoptType; + String oatDir = null; + if (dexoptNeeded == DexFile.DEX2OAT_NEEDED) { + dexoptType = "dex2oat"; + oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet); + } else if (dexoptNeeded == DexFile.PATCHOAT_NEEDED) { + dexoptType = "patchoat"; + } else if (dexoptNeeded == DexFile.SELF_PATCHOAT_NEEDED) { + dexoptType = "self patchoat"; + } else { + throw new IllegalStateException("Invalid dexopt needed: " + dexoptNeeded); + } - Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg=" - + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet - + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable - + " oatDir = " + oatDir); - final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); - final int dexFlags = - (!pkg.isForwardLocked() ? DEXOPT_PUBLIC : 0) - | (vmSafeMode ? DEXOPT_SAFEMODE : 0) - | (debuggable ? DEXOPT_DEBUGGABLE : 0) - | DEXOPT_BOOTCOMPLETE; - try { - mPackageManagerService.mInstaller.dexopt(path, sharedGid, - pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir, - dexFlags); - performedDexOpt = true; - } catch (InstallerException e) { - Slog.w(TAG, "Failed to dexopt", e); - } + Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg=" + + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet + + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable + + " oatDir = " + oatDir); + final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); + final int dexFlags = + (!pkg.isForwardLocked() ? DEXOPT_PUBLIC : 0) + | (vmSafeMode ? DEXOPT_SAFEMODE : 0) + | (debuggable ? DEXOPT_DEBUGGABLE : 0) + | DEXOPT_BOOTCOMPLETE; + try { + mPackageManagerService.mInstaller.dexopt(path, sharedGid, + pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir, + dexFlags, volumeUuid, useProfiles); + performedDexOpt = true; + } catch (InstallerException e) { + Slog.w(TAG, "Failed to dexopt", e); } } @@ -235,7 +246,10 @@ final class PackageDexOptimizer { PackageParser.Package libPkg = mPackageManagerService.findSharedNonSystemLibrary( libName); if (libPkg != null && !done.contains(libName)) { - performDexOptLI(libPkg, instructionSets, done); + // TODO: Analyze and investigate if we (should) profile libraries. + // Currently this will do a full compilation of the library. + performDexOptLI(libPkg, instructionSets, done, + StorageManager.UUID_PRIVATE_INTERNAL, /*useProfiles*/ false); } } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index f777faf3c6c0..6f2804bdac74 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2063,8 +2063,12 @@ public class PackageManagerService extends IPackageManager.Stub { try { int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false); if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { + // Shared libraries do not have profiles so we perform a full + // AOT compilation. mInstaller.dexopt(lib, Process.SYSTEM_UID, dexCodeInstructionSet, - dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/); + dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/, + StorageManager.UUID_PRIVATE_INTERNAL, + false /*useProfiles*/); } } catch (FileNotFoundException e) { Slog.w(TAG, "Library not found: " + lib); @@ -6606,25 +6610,28 @@ public class PackageManagerService extends IPackageManager.Stub { } } + // TODO: this is not used nor needed. Delete it. @Override public boolean performDexOptIfNeeded(String packageName, String instructionSet) { - return performDexOptTraced(packageName, instructionSet); + return performDexOptTraced(packageName, instructionSet, false); } - public boolean performDexOpt(String packageName, String instructionSet) { - return performDexOptTraced(packageName, instructionSet); + public boolean performDexOpt(String packageName, String instructionSet, boolean useProfiles) { + return performDexOptTraced(packageName, instructionSet, useProfiles); } - private boolean performDexOptTraced(String packageName, String instructionSet) { + private boolean performDexOptTraced(String packageName, String instructionSet, + boolean useProfiles) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); try { - return performDexOptInternal(packageName, instructionSet); + return performDexOptInternal(packageName, instructionSet, useProfiles); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } - private boolean performDexOptInternal(String packageName, String instructionSet) { + private boolean performDexOptInternal(String packageName, String instructionSet, + boolean useProfiles) { PackageParser.Package p; final String targetInstructionSet; synchronized (mPackages) { @@ -6636,7 +6643,8 @@ public class PackageManagerService extends IPackageManager.Stub { targetInstructionSet = instructionSet != null ? instructionSet : getPrimaryInstructionSet(p.applicationInfo); - if (p.mDexOptPerformed.contains(targetInstructionSet)) { + if (!useProfiles && p.mDexOptPerformed.contains(targetInstructionSet)) { + // Skip only if we do not use profiles since they might trigger a recompilation. return false; } } @@ -6645,7 +6653,7 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mInstallLock) { final String[] instructionSets = new String[] { targetInstructionSet }; int result = mPackageDexOptimizer.performDexOpt(p, instructionSets, - true /* inclDependencies */); + true /* inclDependencies */, p.volumeUuid, useProfiles); return result == PackageDexOptimizer.DEX_OPT_PERFORMED; } } finally { @@ -6653,20 +6661,13 @@ public class PackageManagerService extends IPackageManager.Stub { } } - public ArraySet<String> getPackagesThatNeedDexOpt() { - ArraySet<String> pkgs = null; + public ArraySet<String> getOptimizablePackages() { + ArraySet<String> pkgs = new ArraySet<String>(); synchronized (mPackages) { for (PackageParser.Package p : mPackages.values()) { - if (DEBUG_DEXOPT) { - Log.i(TAG, p.packageName + " mDexOptPerformed=" + p.mDexOptPerformed.toArray()); + if (PackageDexOptimizer.canOptimizePackage(p)) { + pkgs.add(p.packageName); } - if (!p.mDexOptPerformed.isEmpty()) { - continue; - } - if (pkgs == null) { - pkgs = new ArraySet<String>(); - } - pkgs.add(p.packageName); } } return pkgs; @@ -6694,8 +6695,10 @@ public class PackageManagerService extends IPackageManager.Stub { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); + // Whoever is calling forceDexOpt wants a fully compiled package. + // Don't use profiles since that may cause compilation to be skipped. final int res = mPackageDexOptimizer.performDexOpt(pkg, instructionSets, - true /* inclDependencies */); + true /* inclDependencies */, pkg.volumeUuid, false /* useProfiles */); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 287b39c235c3..9bdad1aa6ef3 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1041,7 +1041,7 @@ public final class SystemServer { traceBeginAndSlog("StartBackgroundDexOptService"); try { - BackgroundDexOptService.schedule(context, 0); + BackgroundDexOptService.schedule(context); } catch (Throwable e) { reportWtf("starting BackgroundDexOptService", e); } |