Merge "Support dexopting shared libraries."

This commit is contained in:
Nicolas Geoffray
2019-03-01 14:44:49 +00:00
committed by Gerrit Code Review
4 changed files with 117 additions and 50 deletions

View File

@@ -25,10 +25,12 @@ import android.content.pm.dex.ArtManager;
import android.content.pm.dex.DexMetadataHelper; import android.content.pm.dex.DexMetadataHelper;
import android.os.FileUtils; import android.os.FileUtils;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.Process;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.WorkSource; import android.os.WorkSource;
import android.os.storage.StorageManager;
import android.util.Log; import android.util.Log;
import android.util.Slog; import android.util.Slog;
@@ -148,6 +150,51 @@ public class PackageDexOptimizer {
} }
} }
int performDexOpt(SharedLibraryInfo info, String[] instructionSets, DexoptOptions options) {
String classLoaderContext = DexoptUtils.getClassLoaderContext(info);
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
String compilerFilter = PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
PackageManagerService.REASON_SHARED);
int result = DEX_OPT_SKIPPED;
for (String instructionSet : dexCodeInstructionSets) {
int dexoptNeeded = getDexoptNeeded(
info.getPath(), instructionSet, compilerFilter,
classLoaderContext, false /* newProfile */,
false /* downgrade */);
if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
continue;
}
// Special string recognized by installd.
final String packageName = "*";
final String outputPath = null;
int dexFlags = DEXOPT_PUBLIC
| (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
| (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0);
dexFlags = adjustDexoptFlags(dexFlags);
final String uuid = StorageManager.UUID_SYSTEM;
final String seInfo = null;
final int targetSdkVersion = 0; // Builtin libraries targets the system's SDK version
try {
mInstaller.dexopt(info.getPath(), Process.SYSTEM_UID, packageName,
instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
uuid, classLoaderContext, seInfo, false /* downgrade */,
targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null,
getReasonName(options.getCompilationReason()));
// The end result is:
// - FAILED if any path failed,
// - PERFORMED if at least one path needed compilation,
// - SKIPPED when all paths are up to date
if (result != DEX_OPT_FAILED) {
result = DEX_OPT_PERFORMED;
}
} catch (InstallerException e) {
Slog.w(TAG, "Failed to dexopt", e);
result = DEX_OPT_FAILED;
}
}
return result;
}
/** /**
* Performs dexopt on all code paths of the given package. * Performs dexopt on all code paths of the given package.
* It assumes the install lock is held. * It assumes the install lock is held.

View File

@@ -9443,18 +9443,27 @@ public class PackageManagerService extends IPackageManager.Stub
// at boot, or background job), the passed 'targetCompilerFilter' stays the same, // at boot, or background job), the passed 'targetCompilerFilter' stays the same,
// and the first package that uses the library will dexopt it. The // and the first package that uses the library will dexopt it. The
// others will see that the compiled code for the library is up to date. // others will see that the compiled code for the library is up to date.
Collection<PackageParser.Package> deps = findSharedNonSystemLibraries(p); Collection<SharedLibraryInfo> deps = findSharedLibraries(p);
final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo); final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
if (!deps.isEmpty()) { if (!deps.isEmpty()) {
DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(), DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
options.getCompilationReason(), options.getCompilerFilter(), options.getCompilationReason(), options.getCompilerFilter(),
options.getSplitName(), options.getSplitName(),
options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY); options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
for (PackageParser.Package depPackage : deps) { for (SharedLibraryInfo info : deps) {
// TODO: Analyze and investigate if we (should) profile libraries. PackageParser.Package depPackage = null;
pdo.performDexOpt(depPackage, instructionSets, synchronized (mPackages) {
getOrCreateCompilerPackageStats(depPackage), depPackage = mPackages.get(info.getPackageName());
mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions); }
if (depPackage != null) {
// TODO: Analyze and investigate if we (should) profile libraries.
pdo.performDexOpt(depPackage, instructionSets,
getOrCreateCompilerPackageStats(depPackage),
mDexManager.getPackageUseInfoOrDefault(depPackage.packageName),
libraryOptions);
} else {
pdo.performDexOpt(info, instructionSets, libraryOptions);
}
} }
} }
return pdo.performDexOpt(p, instructionSets, return pdo.performDexOpt(p, instructionSets,
@@ -9494,63 +9503,48 @@ public class PackageManagerService extends IPackageManager.Stub
return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames); return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames);
} }
List<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) { private static List<SharedLibraryInfo> findSharedLibraries(PackageParser.Package p) {
if (p.usesLibraries != null || p.usesOptionalLibraries != null if (p.usesLibraryInfos != null) {
|| p.usesStaticLibraries != null) { ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
ArrayList<PackageParser.Package> retValue = new ArrayList<>();
Set<String> collectedNames = new HashSet<>(); Set<String> collectedNames = new HashSet<>();
findSharedNonSystemLibrariesRecursive(p, retValue, collectedNames); for (SharedLibraryInfo info : p.usesLibraryInfos) {
findSharedLibrariesRecursive(info, retValue, collectedNames);
retValue.remove(p); }
return retValue; return retValue;
} else { } else {
return Collections.emptyList(); return Collections.emptyList();
} }
} }
private void findSharedNonSystemLibrariesRecursive(PackageParser.Package p, private static void findSharedLibrariesRecursive(SharedLibraryInfo info,
ArrayList<PackageParser.Package> collected, Set<String> collectedNames) { ArrayList<SharedLibraryInfo> collected, Set<String> collectedNames) {
if (!collectedNames.contains(p.packageName)) { if (!collectedNames.contains(info.getName())) {
collectedNames.add(p.packageName); collectedNames.add(info.getName());
collected.add(p); collected.add(info);
if (p.usesLibraries != null) { if (info.getDependencies() != null) {
findSharedNonSystemLibrariesRecursive(p.usesLibraries, for (SharedLibraryInfo dep : info.getDependencies()) {
null, collected, collectedNames); findSharedLibrariesRecursive(dep, collected, collectedNames);
} }
if (p.usesOptionalLibraries != null) {
findSharedNonSystemLibrariesRecursive(p.usesOptionalLibraries,
null, collected, collectedNames);
}
if (p.usesStaticLibraries != null) {
findSharedNonSystemLibrariesRecursive(p.usesStaticLibraries,
p.usesStaticLibrariesVersions, collected, collectedNames);
} }
} }
} }
private void findSharedNonSystemLibrariesRecursive(ArrayList<String> libs, long[] versions, List<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package pkg) {
ArrayList<PackageParser.Package> collected, Set<String> collectedNames) { List<SharedLibraryInfo> deps = findSharedLibraries(pkg);
final int libNameCount = libs.size(); if (!deps.isEmpty()) {
for (int i = 0; i < libNameCount; i++) { ArrayList<PackageParser.Package> retValue = new ArrayList<>();
String libName = libs.get(i); synchronized (mPackages) {
long version = (versions != null && versions.length == libNameCount) for (SharedLibraryInfo info : deps) {
? versions[i] : PackageManager.VERSION_CODE_HIGHEST; PackageParser.Package depPackage = mPackages.get(info.getPackageName());
PackageParser.Package libPkg = findSharedNonSystemLibrary(libName, version); if (depPackage != null) {
if (libPkg != null) { retValue.add(depPackage);
findSharedNonSystemLibrariesRecursive(libPkg, collected, collectedNames); }
}
} }
} return retValue;
} } else {
return Collections.emptyList();
private PackageParser.Package findSharedNonSystemLibrary(String name, long version) {
synchronized (mPackages) {
SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(name, version);
if (libraryInfo != null) {
return mPackages.get(libraryInfo.getPackageName());
}
return null;
} }
} }

View File

@@ -173,6 +173,18 @@ public final class DexoptUtils {
return classLoaderContexts; return classLoaderContexts;
} }
/**
* Creates the class loader context for the given shared library.
*/
public static String getClassLoaderContext(SharedLibraryInfo info) {
String sharedLibrariesContext = "";
if (info.getDependencies() != null) {
sharedLibrariesContext = encodeSharedLibraries(info.getDependencies());
}
return encodeClassLoader(
"", SHARED_LIBRARY_LOADER_TYPE, sharedLibrariesContext);
}
/** /**
* Recursive method to generate the class loader context dependencies for the split with the * Recursive method to generate the class loader context dependencies for the split with the
* given index. {@param classLoaderContexts} acts as an accumulator. Upton return * given index. {@param classLoaderContexts} acts as an accumulator. Upton return

View File

@@ -331,6 +331,20 @@ public class DexoptUtilsTest {
assertEquals(null, contexts[7]); assertEquals(null, contexts[7]);
} }
@Test
public void testSharedLibraryContext() {
SharedLibraryInfo sharedLibrary =
createMockSharedLibrary(new String[] {"a.dex", "b.dex"}).get(0);
String context = DexoptUtils.getClassLoaderContext(sharedLibrary);
assertEquals("PCL[]", context);
SharedLibraryInfo otherSharedLibrary =
createMockSharedLibrary(new String[] {"c.dex"}).get(0);
otherSharedLibrary.addDependency(sharedLibrary);
context = DexoptUtils.getClassLoaderContext(otherSharedLibrary);
assertEquals("PCL[]{PCL[a.dex:b.dex]}", context);
}
@Test @Test
public void testProcessContextForDexLoad() { public void testProcessContextForDexLoad() {
List<String> classLoaders = Arrays.asList( List<String> classLoaders = Arrays.asList(