Merge "Extend the ArtManager profiling API to cover boot image profiling"

This commit is contained in:
Calin Juravle
2018-01-25 19:10:23 +00:00
committed by Android (Google) Code Review
5 changed files with 176 additions and 85 deletions

View File

@@ -1044,8 +1044,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

View File

@@ -16,18 +16,21 @@
package android.content.pm.dex;
import android.annotation.CallbackExecutor;
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 +49,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 +73,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.
*
* 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}.
* 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}.
*
* @param packageName the target package name
* @param codePath the code path for which the profile should be retrieved
* 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 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}.
*
* @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 @CallbackExecutor 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 +154,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 +196,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");
}
}

View File

@@ -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);
}

View File

@@ -526,11 +526,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);
}

View File

@@ -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);
}
/**