diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 989286e074955..a2d3cd80605b9 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -310,6 +310,15 @@ public class ZygoteInit { } else { missingClasses += " " + line; } + } catch (Throwable t) { + Log.e(TAG, "Error preloading " + line + ".", t); + if (t instanceof Error) { + throw (Error) t; + } + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } + throw new RuntimeException(t); } } diff --git a/tools/preload/20090811.compiled b/tools/preload/20090811.compiled new file mode 100644 index 0000000000000..dd61487c2f871 Binary files /dev/null and b/tools/preload/20090811.compiled differ diff --git a/tools/preload/ClassRank.java b/tools/preload/ClassRank.java index 3699b89082b96..c562d5c64a931 100644 --- a/tools/preload/ClassRank.java +++ b/tools/preload/ClassRank.java @@ -26,7 +26,7 @@ class ClassRank implements Comparator { * Increase this number to add more weight to classes which were loaded * earlier. */ - static final int SEQUENCE_WEIGHT = 500; // 5 ms + static final int SEQUENCE_WEIGHT = 500; // 0.5ms static final int BUCKET_SIZE = 5; diff --git a/tools/preload/LoadedClass.java b/tools/preload/LoadedClass.java index 57828071bab8d..9ef17f5643f5e 100644 --- a/tools/preload/LoadedClass.java +++ b/tools/preload/LoadedClass.java @@ -15,10 +15,7 @@ */ import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Set; +import java.util.*; /** * A loaded class. @@ -54,7 +51,7 @@ class LoadedClass implements Serializable, Comparable { } void measureMemoryUsage() { - this.memoryUsage = MemoryUsage.forClass(name); +// this.memoryUsage = MemoryUsage.forClass(name); } int mlt = -1; @@ -102,31 +99,20 @@ class LoadedClass implements Serializable, Comparable { } } - /** - * Counts loads by apps. - */ - int appLoads() { - return operationsByApps(loads); + /** Returns names of apps that loaded this class. */ + Set applicationNames() { + Set appNames = new HashSet(); + addProcessNames(loads, appNames); + addProcessNames(initializations, appNames); + return appNames; } - /** - * Counts inits by apps. - */ - int appInits() { - return operationsByApps(initializations); - } - - /** - * Counts number of app operations in the given list. - */ - private static int operationsByApps(List operations) { - int byApps = 0; - for (Operation operation : operations) { + private void addProcessNames(List ops, Set appNames) { + for (Operation operation : ops) { if (operation.process.isApplication()) { - byApps++; + appNames.add(operation.process.name); } } - return byApps; } public int compareTo(LoadedClass o) { @@ -160,4 +146,8 @@ class LoadedClass implements Serializable, Comparable { return false; } + + public boolean isPreloadable() { + return systemClass && Policy.isPreloadableClass(name); + } } diff --git a/tools/preload/Policy.java b/tools/preload/Policy.java index 554966b88731c..ade889e3e44b6 100644 --- a/tools/preload/Policy.java +++ b/tools/preload/Policy.java @@ -23,48 +23,33 @@ import java.util.Set; */ public class Policy { + /** + * No constructor - use static methods only + */ + private Policy() {} + /** * This location (in the build system) of the preloaded-classes file. */ - private static final String PRELOADED_CLASS_FILE = "frameworks/base/preloaded-classes"; - + private static final String PRELOADED_CLASS_FILE + = "frameworks/base/preloaded-classes"; + /** - * The internal process name of the system process. Note, this also shows up as - * "system_process", e.g. in ddms. - */ - private static final String SYSTEM_SERVER_PROCESS_NAME = "system_server"; - - /** - * Names of non-application processes - these will not be checked for preloaded classes. - * - * TODO: Replace this hardcoded list with a walk up the parent chain looking for zygote. - */ - private static final Set NOT_FROM_ZYGOTE = new HashSet(Arrays.asList( - "zygote", - "dexopt", - "unknown", - SYSTEM_SERVER_PROCESS_NAME, - "com.android.development", - "app_process" // am & other shell commands - )); - - /** - * Long running services. These are restricted in their contribution to the preloader - * because their launch time is less critical. + * Long running services. These are restricted in their contribution to the + * preloader because their launch time is less critical. */ private static final Set SERVICES = new HashSet(Arrays.asList( - SYSTEM_SERVER_PROCESS_NAME, - "com.android.acore", - // Commented out to make sure DefaultTimeZones gets preloaded. - // "com.android.phone", + "system_server", "com.google.process.content", - "android.process.media" + "android.process.media", + "com.google.process.gapps" )); /** * Classes which we shouldn't load from the Zygote. */ - private static final Set EXCLUDED_CLASSES = new HashSet(Arrays.asList( + private static final Set EXCLUDED_CLASSES + = new HashSet(Arrays.asList( // Binders "android.app.AlarmManager", "android.app.SearchManager", @@ -75,14 +60,8 @@ public class Policy { "android.os.AsyncTask", "android.pim.ContactsAsyncHelper", "java.lang.ProcessManager" - )); - /** - * No constructor - use static methods only - */ - private Policy() {} - /** * Returns the path/file name of the preloaded classes file that will be written * by WritePreloadedClassFile. @@ -91,13 +70,6 @@ public class Policy { return PRELOADED_CLASS_FILE; } - /** - * Reports if a given process name was created from zygote - */ - public static boolean isFromZygote(String processName) { - return !NOT_FROM_ZYGOTE.contains(processName); - } - /** * Reports if the given process name is a "long running" process or service */ diff --git a/tools/preload/PrintCsv.java b/tools/preload/PrintCsv.java index 9f2a318d15dd0..62f4271946462 100644 --- a/tools/preload/PrintCsv.java +++ b/tools/preload/PrintCsv.java @@ -18,6 +18,9 @@ import java.io.IOException; import java.io.FileInputStream; import java.io.ObjectInputStream; import java.io.BufferedInputStream; +import java.util.Set; +import java.util.HashSet; +import java.util.TreeSet; /** * Prints raw information in CSV format. @@ -37,13 +40,14 @@ public class PrintCsv { + ",Preloaded" + ",Median Load Time (us)" + ",Median Init Time (us)" + + ",Process Names" + ",Load Count" - + ",Init Count" - + ",Managed Heap (B)" - + ",Native Heap (B)" - + ",Managed Pages (kB)" - + ",Native Pages (kB)" - + ",Other Pages (kB)"); + + ",Init Count"); +// + ",Managed Heap (B)" +// + ",Native Heap (B)" +// + ",Managed Pages (kB)" +// + ",Native Pages (kB)" +// + ",Other Pages (kB)"); MemoryUsage baseline = root.baseline; @@ -60,10 +64,23 @@ public class PrintCsv { System.out.print(','); System.out.print(loadedClass.medianInitTimeMicros()); System.out.print(','); + System.out.print('"'); + + Set procNames = new TreeSet(); + for (Operation op : loadedClass.loads) + procNames.add(op.process.name); + for (Operation op : loadedClass.initializations) + procNames.add(op.process.name); + for (String name : procNames) { + System.out.print(name + "\n"); + } + + System.out.print('"'); + System.out.print(','); System.out.print(loadedClass.loads.size()); System.out.print(','); System.out.print(loadedClass.initializations.size()); - +/* if (loadedClass.memoryUsage.isAvailable()) { MemoryUsage subtracted = loadedClass.memoryUsage.subtract(baseline); @@ -82,7 +99,7 @@ public class PrintCsv { } else { System.out.print(",n/a,n/a,n/a,n/a,n/a"); } - +*/ System.out.println(); } } diff --git a/tools/preload/Proc.java b/tools/preload/Proc.java index 22697f8743156..66e04dc360644 100644 --- a/tools/preload/Proc.java +++ b/tools/preload/Proc.java @@ -14,8 +14,6 @@ * limitations under the License. */ -import java.util.Set; -import java.util.HashSet; import java.util.Arrays; import java.util.List; import java.util.ArrayList; @@ -23,7 +21,6 @@ import java.util.LinkedList; import java.util.Map; import java.util.HashMap; import java.util.Collections; -import java.util.TreeSet; import java.io.Serializable; /** @@ -38,11 +35,6 @@ class Proc implements Serializable { */ static final int PERCENTAGE_TO_PRELOAD = 75; - /** - * Maximum number of classes to preload for a given process. - */ - static final int MAX_TO_PRELOAD = 100; - /** Parent process. */ final Proc parent; @@ -97,11 +89,9 @@ class Proc implements Serializable { /** * Returns a list of classes which should be preloaded. - * - * @param takeAllClasses forces all classes to be taken (irrespective of ranking) */ - List highestRankedClasses(boolean takeAllClasses) { - if (!isApplication()) { + List highestRankedClasses() { + if (!isApplication() || Policy.isService(this.name)) { return Collections.emptyList(); } @@ -114,25 +104,13 @@ class Proc implements Serializable { int timeToSave = totalTimeMicros() * percentageToPreload() / 100; int timeSaved = 0; - boolean service = Policy.isService(this.name); - + int count = 0; List highest = new ArrayList(); for (Operation operation : ranked) { - - // These are actual ranking decisions, which can be overridden - if (!takeAllClasses) { - if (highest.size() >= MAX_TO_PRELOAD) { - System.out.println(name + " got " - + (timeSaved * 100 / timeToSave) + "% through"); - break; - } - - if (timeSaved >= timeToSave) { - break; - } + if (timeSaved >= timeToSave || count++ > 100) { + break; } - // The remaining rules apply even to wired-down processes if (!Policy.isPreloadableClass(operation.loadedClass.name)) { continue; } @@ -140,13 +118,8 @@ class Proc implements Serializable { if (!operation.loadedClass.systemClass) { continue; } - - // Only load java.* class for services. - if (!service || operation.loadedClass.name.startsWith("java.")) { - highest.add(operation.loadedClass); - } - - // For services, still count the time even if it's not in java.* + + highest.add(operation.loadedClass); timeSaved += operation.medianExclusiveTimeMicros(); } @@ -166,11 +139,13 @@ class Proc implements Serializable { /** * Returns true if this process is an app. - * - * TODO: Replace the hardcoded list with a walk up the parent chain looking for zygote. */ public boolean isApplication() { - return Policy.isFromZygote(name); + if (name.equals("com.android.development")) { + return false; + } + + return parent != null && parent.name.equals("zygote"); } /** diff --git a/tools/preload/WritePreloadedClassFile.java b/tools/preload/WritePreloadedClassFile.java index d87b1f056a015..b209af0c25f60 100644 --- a/tools/preload/WritePreloadedClassFile.java +++ b/tools/preload/WritePreloadedClassFile.java @@ -20,8 +20,6 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; import java.util.Set; import java.util.TreeSet; @@ -32,71 +30,85 @@ import java.util.TreeSet; public class WritePreloadedClassFile { public static void main(String[] args) throws IOException, ClassNotFoundException { - - // Process command-line arguments first - List wiredProcesses = new ArrayList(); - String inputFileName = null; - int argOffset = 0; - try { - while ("--preload-all-process".equals(args[argOffset])) { - argOffset++; - wiredProcesses.add(args[argOffset++]); - } - - inputFileName = args[argOffset++]; - } catch (RuntimeException e) { - System.err.println("Usage: WritePreloadedClassFile " + - "[--preload-all-process process-name] " + - "[compiled log file]"); - System.exit(0); + if (args.length != 1) { + System.err.println("Usage: WritePreloadedClassFile [compiled log]"); + System.exit(-1); } + String rootFile = args[0]; + Root root = Root.fromFile(rootFile); - Root root = Root.fromFile(inputFileName); - + // No classes are preloaded to start. for (LoadedClass loadedClass : root.loadedClasses.values()) { loadedClass.preloaded = false; } + // Open preloaded-classes file for output. Writer out = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(Policy.getPreloadedClassFileName()), Charset.forName("US-ASCII"))); out.write("# Classes which are preloaded by com.android.internal.os.ZygoteInit.\n"); out.write("# Automatically generated by /frameworks/base/tools/preload.\n"); - out.write("# percent=" + Proc.PERCENTAGE_TO_PRELOAD + ", weight=" - + ClassRank.SEQUENCE_WEIGHT + out.write("# percent=" + Proc.PERCENTAGE_TO_PRELOAD + + ", weight=" + ClassRank.SEQUENCE_WEIGHT + ", bucket_size=" + ClassRank.BUCKET_SIZE + "\n"); - for (String wiredProcess : wiredProcesses) { - out.write("# forcing classes loaded by: " + wiredProcess + "\n"); - } - Set highestRanked = new TreeSet(); - for (Proc proc : root.processes.values()) { - // test to see if this is one of the wired-down ("take all classes") processes - boolean isWired = wiredProcesses.contains(proc.name); - - List highestForProc = proc.highestRankedClasses(isWired); + Set toPreload = new TreeSet(); - System.out.println(proc.name + ": " + highestForProc.size()); - - for (LoadedClass loadedClass : highestForProc) { - loadedClass.preloaded = true; + // Preload all classes that were loaded by at least 2 apps, if both + // apps run at the same time, they'll share memory. + for (LoadedClass loadedClass : root.loadedClasses.values()) { + if (!loadedClass.isPreloadable()) { + continue; + } + + Set appNames = loadedClass.applicationNames(); + + if (appNames.size() > 3) { + toPreload.add(loadedClass); } - highestRanked.addAll(highestForProc); } - for (LoadedClass loadedClass : highestRanked) { + // Try to make individual apps start faster by preloading slowest + // classes. + for (Proc proc : root.processes.values()) { + toPreload.addAll(proc.highestRankedClasses()); + } + + System.out.println(toPreload.size() + " classes will be preloaded."); + + // Make classes that were already loaded by the zygote explicit. + // This adds minimal overhead but avoid confusion about classes not + // appearing in the list. + addAllClassesFor("zygote", root, toPreload); + + for (LoadedClass loadedClass : toPreload) { out.write(loadedClass.name); out.write('\n'); } out.close(); - System.out.println(highestRanked.size() - + " classes will be preloaded."); - // Update data to reflect LoadedClass.preloaded changes. - root.toFile(inputFileName); + for (LoadedClass loadedClass : toPreload) { + loadedClass.preloaded = true; + } + root.toFile(rootFile); + } + + private static void addAllClassesFor(String packageName, Root root, + Set toPreload) { + for (Proc proc : root.processes.values()) { + if (proc.name.equals(packageName)) { + for (Operation operation : proc.operations) { + // TODO: I'm not sure how the zygote loaded classes that + // aren't supposed to be preloadable... + if (operation.loadedClass.isPreloadable()) { + toPreload.add(operation.loadedClass); + } + } + } + } } } diff --git a/tools/preload/preload.iml b/tools/preload/preload.iml index d1fab57582fe8..2d87c55ae2cac 100644 --- a/tools/preload/preload.iml +++ b/tools/preload/preload.iml @@ -1,15 +1,14 @@ - + + - - diff --git a/tools/preload/preload.ipr b/tools/preload/preload.ipr index c5613ad163146..f78bf76567fb2 100644 --- a/tools/preload/preload.ipr +++ b/tools/preload/preload.ipr @@ -114,6 +114,7 @@