diff options
| author | 2018-01-21 21:39:18 -0800 | |
|---|---|---|
| committer | 2018-02-14 08:19:42 -0800 | |
| commit | e4ad74e83491818baf8ce74f4081fa9c365a13cc (patch) | |
| tree | c4fb08bd2db7b3490bce231b144f43d1ed9cca8f | |
| parent | ea6c0ffb4a276210b6d971c87a15a7484446d3df (diff) | |
Extend the ArtManager profiling API to cover boot image profiling
The boot image profile is available only on userdebug and eng builds and
combines the boot classpath and system server classpath.
Also, update ArtManager API to use Executors instead of Handlers.
(cherry picked from commit fcbb74a4296fd808e1058ecebd91fac56582e799)
Test: gts GtsAndroidRuntimeManagerHostTestCases
Bug: 30934496
Merged-In: Ie501947a659d644acbde04fb46157dd0c7944e81
Change-Id: Ie501947a659d644acbde04fb46157dd0c7944e81
| -rw-r--r-- | api/system-current.txt | 6 | ||||
| -rw-r--r-- | core/java/android/content/pm/dex/ArtManager.java | 109 | ||||
| -rw-r--r-- | core/java/android/content/pm/dex/IArtManager.aidl | 38 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/Installer.java | 6 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/dex/ArtManagerService.java | 101 |
5 files changed, 175 insertions, 85 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index b6d8142b2ebd..d2c6bd064ac9 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -953,8 +953,10 @@ package android.content.pm { package android.content.pm.dex { public class ArtManager { - method public boolean isRuntimeProfilingEnabled(); - method public void snapshotRuntimeProfile(java.lang.String, java.lang.String, android.content.pm.dex.ArtManager.SnapshotRuntimeProfileCallback, android.os.Handler); + method public boolean isRuntimeProfilingEnabled(int); + method public void snapshotRuntimeProfile(int, java.lang.String, java.lang.String, java.util.concurrent.Executor, android.content.pm.dex.ArtManager.SnapshotRuntimeProfileCallback); + field public static final int PROFILE_APPS = 0; // 0x0 + field public static final int PROFILE_BOOT_IMAGE = 1; // 0x1 field public static final int SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND = 1; // 0x1 field public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2; // 0x2 field public static final int SNAPSHOT_FAILED_PACKAGE_NOT_FOUND = 0; // 0x0 diff --git a/core/java/android/content/pm/dex/ArtManager.java b/core/java/android/content/pm/dex/ArtManager.java index f979576e5315..075306366264 100644 --- a/core/java/android/content/pm/dex/ArtManager.java +++ b/core/java/android/content/pm/dex/ArtManager.java @@ -16,18 +16,20 @@ package android.content.pm.dex; +import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.os.Environment; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Slog; import java.io.File; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; /** * Class for retrieving various kinds of information related to the runtime artifacts of @@ -46,6 +48,20 @@ public class ArtManager { /** The snapshot failed because of an internal error (e.g. error during opening profiles). */ public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2; + /** Constant used for applications profiles. */ + public static final int PROFILE_APPS = 0; + /** Constant used for the boot image profile. */ + public static final int PROFILE_BOOT_IMAGE = 1; + + /** @hide */ + @IntDef(flag = true, prefix = { "PROFILE_" }, value = { + PROFILE_APPS, + PROFILE_BOOT_IMAGE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ProfileType {} + + private IArtManager mArtManager; /** @@ -56,41 +72,59 @@ public class ArtManager { } /** - * Snapshots the runtime profile for an apk belonging to the package {@code packageName}. - * The apk is identified by {@code codePath}. The calling process must have - * {@code android.permission.READ_RUNTIME_PROFILE} permission. + * Snapshots a runtime profile according to the {@code profileType} parameter. + * + * If {@code profileType} is {@link ArtManager#PROFILE_APPS} the method will snapshot + * the profile for for an apk belonging to the package {@code packageName}. + * The apk is identified by {@code codePath}. + * + * If {@code profileType} is {@code ArtManager.PROFILE_BOOT_IMAGE} the method will snapshot + * the profile for the boot image. In this case {@code codePath can be null}. The parameters + * {@code packageName} and {@code codePath} are ignored. + *u + * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission. * - * The result will be posted on {@code handler} using the given {@code callback}. - * The profile being available as a read-only {@link android.os.ParcelFileDescriptor}. + * The result will be posted on the {@code executor} using the given {@code callback}. + * The profile will be available as a read-only {@link android.os.ParcelFileDescriptor}. * - * @param packageName the target package name - * @param codePath the code path for which the profile should be retrieved + * This method will throw {@link IllegalStateException} if + * {@link ArtManager#isRuntimeProfilingEnabled(int)} does not return true for the given + * {@code profileType}. + * + * @param profileType the type of profile that should be snapshot (boot image or app) + * @param packageName the target package name or null if the target is the boot image + * @param codePath the code path for which the profile should be retrieved or null if + * the target is the boot image * @param callback the callback which should be used for the result - * @param handler the handler which should be used to post the result + * @param executor the executor which should be used to post the result */ @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES) - public void snapshotRuntimeProfile(@NonNull String packageName, @NonNull String codePath, - @NonNull SnapshotRuntimeProfileCallback callback, @NonNull Handler handler) { + public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName, + @Nullable String codePath, @NonNull Executor executor, + @NonNull SnapshotRuntimeProfileCallback callback) { Slog.d(TAG, "Requesting profile snapshot for " + packageName + ":" + codePath); SnapshotRuntimeProfileCallbackDelegate delegate = - new SnapshotRuntimeProfileCallbackDelegate(callback, handler.getLooper()); + new SnapshotRuntimeProfileCallbackDelegate(callback, executor); try { - mArtManager.snapshotRuntimeProfile(packageName, codePath, delegate); + mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } } /** - * Returns true if runtime profiles are enabled, false otherwise. + * Returns true if runtime profiles are enabled for the given type, false otherwise. * * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission. + * + * @param profileType can be either {@link ArtManager#PROFILE_APPS} + * or {@link ArtManager#PROFILE_BOOT_IMAGE} */ @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES) - public boolean isRuntimeProfilingEnabled() { + public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) { try { - return mArtManager.isRuntimeProfilingEnabled(); + return mArtManager.isRuntimeProfilingEnabled(profileType); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } @@ -119,41 +153,24 @@ public class ArtManager { } private static class SnapshotRuntimeProfileCallbackDelegate - extends android.content.pm.dex.ISnapshotRuntimeProfileCallback.Stub - implements Handler.Callback { - private static final int MSG_SNAPSHOT_OK = 1; - private static final int MSG_ERROR = 2; + extends android.content.pm.dex.ISnapshotRuntimeProfileCallback.Stub { private final ArtManager.SnapshotRuntimeProfileCallback mCallback; - private final Handler mHandler; + private final Executor mExecutor; private SnapshotRuntimeProfileCallbackDelegate( - ArtManager.SnapshotRuntimeProfileCallback callback, Looper looper) { + ArtManager.SnapshotRuntimeProfileCallback callback, Executor executor) { mCallback = callback; - mHandler = new Handler(looper, this); + mExecutor = executor; } @Override - public void onSuccess(ParcelFileDescriptor profileReadFd) { - mHandler.obtainMessage(MSG_SNAPSHOT_OK, profileReadFd).sendToTarget(); + public void onSuccess(final ParcelFileDescriptor profileReadFd) { + mExecutor.execute(() -> mCallback.onSuccess(profileReadFd)); } @Override public void onError(int errCode) { - mHandler.obtainMessage(MSG_ERROR, errCode, 0).sendToTarget(); - } - - @Override - public boolean handleMessage(Message msg) { - switch (msg.what) { - case MSG_SNAPSHOT_OK: - mCallback.onSuccess((ParcelFileDescriptor) msg.obj); - break; - case MSG_ERROR: - mCallback.onError(msg.arg1); - break; - default: return false; - } - return true; + mExecutor.execute(() -> mCallback.onError(errCode)); } } @@ -178,17 +195,15 @@ public class ArtManager { } /** - * Return the snapshot profile file for the given package and split. + * Return the snapshot profile file for the given package and profile name. * * KEEP in sync with installd dexopt.cpp. * TODO(calin): inject the snapshot profile name from PM to avoid the dependency. * * @hide */ - public static File getProfileSnapshotFile(String packageName, String splitName) { + public static File getProfileSnapshotFileForName(String packageName, String profileName) { File profileDir = Environment.getDataRefProfilesDePackageDirectory(packageName); - String snapshotFile = getProfileName(splitName) + ".snapshot"; - return new File(profileDir, snapshotFile); - + return new File(profileDir, profileName + ".snapshot"); } } diff --git a/core/java/android/content/pm/dex/IArtManager.aidl b/core/java/android/content/pm/dex/IArtManager.aidl index 8cbb452344b2..6abfdbab6e26 100644 --- a/core/java/android/content/pm/dex/IArtManager.aidl +++ b/core/java/android/content/pm/dex/IArtManager.aidl @@ -25,20 +25,34 @@ import android.content.pm.dex.ISnapshotRuntimeProfileCallback; */ interface IArtManager { /** - * Snapshots the runtime profile for an apk belonging to the package {@param packageName}. - * The apk is identified by {@param codePath}. The calling process must have - * {@code android.permission.READ_RUNTIME_PROFILE} permission. + * Snapshots a runtime profile according to the {@code profileType} parameter. * - * The result will be posted on {@param callback} with the profile being available as a - * read-only {@link android.os.ParcelFileDescriptor}. - */ - oneway void snapshotRuntimeProfile(in String packageName, - in String codePath, in ISnapshotRuntimeProfileCallback callback); - - /** - * Returns true if runtime profiles are enabled, false otherwise. + * If {@code profileType} is {@link ArtManager#PROFILE_APPS} the method will snapshot + * the profile for for an apk belonging to the package {@code packageName}. + * The apk is identified by {@code codePath}. + * + * If {@code profileType} is {@code ArtManager.PROFILE_BOOT_IMAGE} the method will snapshot + * the profile for the boot image. In this case {@code codePath can be null}. The parameters + * {@code packageName} and {@code codePath} are ignored. * * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission. + * + * The result will be posted on the {@code executor} using the given {@code callback}. + * The profile will be available as a read-only {@link android.os.ParcelFileDescriptor}. + * + * This method will throw {@link IllegalStateException} if + * {@link ArtManager#isRuntimeProfilingEnabled(int)} does not return true for the given + * {@code profileType}. */ - boolean isRuntimeProfilingEnabled(); + oneway void snapshotRuntimeProfile(int profileType, in String packageName, + in String codePath, in ISnapshotRuntimeProfileCallback callback); + + /** + * Returns true if runtime profiles are enabled for the given type, false otherwise. + * The type can be can be either {@code ArtManager.PROFILE_APPS} + * or {@code ArtManager.PROFILE_BOOT_IMAGE}. + * + * @param profileType + */ + boolean isRuntimeProfilingEnabled(int profileType); } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 7b02ace2c05f..1c1357bb23fa 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -502,11 +502,11 @@ public class Installer extends SystemService { } } - public boolean createProfileSnapshot(int appId, String packageName, String profileName) - throws InstallerException { + public boolean createProfileSnapshot(int appId, String packageName, String profileName, + String classpath) throws InstallerException { if (!checkBeforeRemote()) return false; try { - return mInstalld.createProfileSnapshot(appId, packageName, profileName); + return mInstalld.createProfileSnapshot(appId, packageName, profileName, classpath); } catch (Exception e) { throw InstallerException.from(e); } diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index f15dc2d5f8d3..e29027288e9b 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -23,8 +23,10 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.dex.ArtManager; +import android.content.pm.dex.ArtManager.ProfileType; import android.content.pm.dex.DexMetadataHelper; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -32,6 +34,7 @@ import android.content.pm.IPackageManager; import android.content.pm.dex.ISnapshotRuntimeProfileCallback; import android.os.SystemProperties; import android.os.UserHandle; +import android.system.Os; import android.util.ArrayMap; import android.util.Slog; @@ -43,6 +46,9 @@ import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; import java.io.File; import java.io.FileNotFoundException; +import libcore.io.IoUtils; +import libcore.util.NonNull; +import libcore.util.Nullable; /** * A system service that provides access to runtime and compiler artifacts. @@ -62,6 +68,12 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { private static boolean DEBUG = false; private static boolean DEBUG_IGNORE_PERMISSIONS = false; + // Package name used to create the profile directory layout when + // taking a snapshot of the boot image profile. + private static final String BOOT_IMAGE_ANDROID_PACKAGE = "android"; + // Profile name used for the boot image profile. + private static final String BOOT_IMAGE_PROFILE_NAME = "android.prof"; + private final IPackageManager mPackageManager; private final Object mInstallLock; @GuardedBy("mInstallLock") @@ -77,20 +89,36 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { } @Override - public void snapshotRuntimeProfile(String packageName, String codePath, - ISnapshotRuntimeProfileCallback callback) { + public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName, + @Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback) { // Sanity checks on the arguments. - Preconditions.checkStringNotEmpty(packageName); - Preconditions.checkStringNotEmpty(codePath); Preconditions.checkNotNull(callback); - // Verify that the caller has the right permissions. - checkReadRuntimeProfilePermission(); + boolean bootImageProfile = profileType == ArtManager.PROFILE_BOOT_IMAGE; + if (!bootImageProfile) { + Preconditions.checkStringNotEmpty(codePath); + Preconditions.checkStringNotEmpty(packageName); + } + + // Verify that the caller has the right permissions and that the runtime profiling is + // enabled. The call to isRuntimePermissions will checkReadRuntimeProfilePermission. + if (!isRuntimeProfilingEnabled(profileType)) { + throw new IllegalStateException("Runtime profiling is not enabled for " + profileType); + } if (DEBUG) { Slog.d(TAG, "Requested snapshot for " + packageName + ":" + codePath); } + if (bootImageProfile) { + snapshotBootImageProfile(callback); + } else { + snapshotAppProfile(packageName, codePath, callback); + } + } + + private void snapshotAppProfile(String packageName, String codePath, + ISnapshotRuntimeProfileCallback callback) { PackageInfo info = null; try { // Note that we use the default user 0 to retrieve the package info. @@ -127,18 +155,25 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { } // All good, create the profile snapshot. - createProfileSnapshot(packageName, splitName, callback, info); + int appId = UserHandle.getAppId(info.applicationInfo.uid); + if (appId < 0) { + postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); + Slog.wtf(TAG, "AppId is -1 for package: " + packageName); + return; + } + + createProfileSnapshot(packageName, ArtManager.getProfileName(splitName), codePath, + appId, callback); // Destroy the snapshot, we no longer need it. - destroyProfileSnapshot(packageName, splitName); + destroyProfileSnapshot(packageName, ArtManager.getProfileName(splitName)); } - private void createProfileSnapshot(String packageName, String splitName, - ISnapshotRuntimeProfileCallback callback, PackageInfo info) { + private void createProfileSnapshot(String packageName, String profileName, String classpath, + int appId, ISnapshotRuntimeProfileCallback callback) { // Ask the installer to snapshot the profile. synchronized (mInstallLock) { try { - if (!mInstaller.createProfileSnapshot(UserHandle.getAppId(info.applicationInfo.uid), - packageName, ArtManager.getProfileName(splitName))) { + if (!mInstaller.createProfileSnapshot(appId, packageName, profileName, classpath)) { postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); return; } @@ -149,8 +184,9 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { } // Open the snapshot and invoke the callback. - File snapshotProfile = ArtManager.getProfileSnapshotFile(packageName, splitName); - ParcelFileDescriptor fd; + File snapshotProfile = ArtManager.getProfileSnapshotFileForName(packageName, profileName); + + ParcelFileDescriptor fd = null; try { fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY); postSuccess(packageName, fd, callback); @@ -158,31 +194,54 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":" + snapshotProfile, e); postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); + } finally { + IoUtils.closeQuietly(fd); } } - private void destroyProfileSnapshot(String packageName, String splitName) { + private void destroyProfileSnapshot(String packageName, String profileName) { if (DEBUG) { - Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + splitName); + Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + profileName); } synchronized (mInstallLock) { try { - mInstaller.destroyProfileSnapshot(packageName, - ArtManager.getProfileName(splitName)); + mInstaller.destroyProfileSnapshot(packageName, profileName); } catch (InstallerException e) { Slog.e(TAG, "Failed to destroy profile snapshot for " + - packageName + ":" + splitName, e); + packageName + ":" + profileName, e); } } } @Override - public boolean isRuntimeProfilingEnabled() { + public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) { // Verify that the caller has the right permissions. checkReadRuntimeProfilePermission(); - return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false); + switch (profileType) { + case ArtManager.PROFILE_APPS : + return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false); + case ArtManager.PROFILE_BOOT_IMAGE: + return (Build.IS_USERDEBUG || Build.IS_ENG) && + SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false) && + SystemProperties.getBoolean("dalvik.vm.profilebootimage", false); + default: + throw new IllegalArgumentException("Invalid profile type:" + profileType); + } + } + + private void snapshotBootImageProfile(ISnapshotRuntimeProfileCallback callback) { + // Combine the profiles for boot classpath and system server classpath. + // This avoids having yet another type of profiles and simplifies the processing. + String classpath = String.join(":", Os.getenv("BOOTCLASSPATH"), + Os.getenv("SYSTEMSERVERCLASSPATH")); + + // Create the snapshot. + createProfileSnapshot(BOOT_IMAGE_ANDROID_PACKAGE, BOOT_IMAGE_PROFILE_NAME, classpath, + /*appId*/ -1, callback); + // Destroy the snapshot, we no longer need it. + destroyProfileSnapshot(BOOT_IMAGE_ANDROID_PACKAGE, BOOT_IMAGE_PROFILE_NAME); } /** |