Merge "Extend the ArtManager profiling API to cover boot image profiling"
This commit is contained in:
committed by
Android (Google) Code Review
commit
172814085b
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user