diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-02-10 15:44:00 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-02-10 15:44:00 -0800 |
commit | d24b8183b93e781080b2c16c487e60d51c12da31 (patch) | |
tree | fbb89154858984eb8e41556da7e9433040d55cd4 /tools/preload | |
parent | f1e484acb594a726fb57ad0ae4cfe902c7f35858 (diff) | |
download | frameworks_base-d24b8183b93e781080b2c16c487e60d51c12da31.zip frameworks_base-d24b8183b93e781080b2c16c487e60d51c12da31.tar.gz frameworks_base-d24b8183b93e781080b2c16c487e60d51c12da31.tar.bz2 |
auto import from //branches/cupcake/...@130745
Diffstat (limited to 'tools/preload')
-rw-r--r-- | tools/preload/Compile.java | 27 | ||||
-rw-r--r-- | tools/preload/LoadedClass.java | 6 | ||||
-rw-r--r-- | tools/preload/MemoryUsage.java | 11 | ||||
-rw-r--r-- | tools/preload/Operation.java | 18 | ||||
-rw-r--r-- | tools/preload/Policy.java | 114 | ||||
-rw-r--r-- | tools/preload/Proc.java | 93 | ||||
-rw-r--r-- | tools/preload/Record.java | 46 | ||||
-rw-r--r-- | tools/preload/WritePreloadedClassFile.java | 55 |
8 files changed, 265 insertions, 105 deletions
diff --git a/tools/preload/Compile.java b/tools/preload/Compile.java index 8388b12..67258ef 100644 --- a/tools/preload/Compile.java +++ b/tools/preload/Compile.java @@ -15,26 +15,22 @@ */ import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.io.IOException; import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; -import java.io.BufferedOutputStream; -import java.util.List; +import java.io.IOException; +import java.io.InputStreamReader; import java.util.ArrayList; -import java.lang.reflect.InvocationTargetException; +import java.util.List; /** * Parses and analyzes a log, pulling our PRELOAD information. If you have * an emulator or device running in the background, this class will use it * to measure and record the memory usage of each class. + * + * TODO: Should analyze lines and select substring dynamically (instead of hardcoded 19) */ public class Compile { - public static void main(String[] args) - throws IOException, NoSuchMethodException, IllegalAccessException, - InvocationTargetException { + public static void main(String[] args) throws IOException { if (args.length != 2) { System.err.println("Usage: Compile [log file] [output file]"); System.exit(0); @@ -48,10 +44,17 @@ public class Compile { new FileInputStream(args[0]))); String line; + int lineNumber = 0; while ((line = in.readLine()) != null) { + lineNumber++; if (line.startsWith("I/PRELOAD")) { - line = line.substring(19); - records.add(new Record(line)); + try { + String clipped = line.substring(19); + records.add(new Record(clipped, lineNumber)); + } catch (RuntimeException e) { + throw new RuntimeException( + "Exception while recording line " + lineNumber + ": " + line, e); + } } } diff --git a/tools/preload/LoadedClass.java b/tools/preload/LoadedClass.java index 6b3d345..5782807 100644 --- a/tools/preload/LoadedClass.java +++ b/tools/preload/LoadedClass.java @@ -14,12 +14,11 @@ * limitations under the License. */ -import java.util.List; +import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Set; -import java.util.HashSet; -import java.io.Serializable; /** * A loaded class. @@ -134,6 +133,7 @@ class LoadedClass implements Serializable, Comparable<LoadedClass> { return name.compareTo(o.name); } + @Override public String toString() { return name; } diff --git a/tools/preload/MemoryUsage.java b/tools/preload/MemoryUsage.java index 89717eb..e5dfb2a 100644 --- a/tools/preload/MemoryUsage.java +++ b/tools/preload/MemoryUsage.java @@ -32,6 +32,9 @@ class MemoryUsage implements Serializable { private static final long serialVersionUID = 0; static final MemoryUsage NOT_AVAILABLE = new MemoryUsage(); + + static int errorCount = 0; + static final int MAXIMUM_ERRORS = 10; // give up after this many fails final int nativeSharedPages; final int javaSharedPages; @@ -160,6 +163,13 @@ class MemoryUsage implements Serializable { * Measures memory usage for the given class. */ static MemoryUsage forClass(String className) { + + // This is a coarse approximation for determining that no device is connected, + // or that the communication protocol has changed, but we'll keep going and stop whining. + if (errorCount >= MAXIMUM_ERRORS) { + return NOT_AVAILABLE; + } + MeasureWithTimeout measurer = new MeasureWithTimeout(className); new Thread(measurer).start(); @@ -237,6 +247,7 @@ class MemoryUsage implements Serializable { if (line == null || !line.startsWith("DECAFBAD,")) { System.err.println("Got bad response for " + className + ": " + line); + errorCount += 1; return NOT_AVAILABLE; } diff --git a/tools/preload/Operation.java b/tools/preload/Operation.java index 65c9a03..4f1938e 100644 --- a/tools/preload/Operation.java +++ b/tools/preload/Operation.java @@ -115,4 +115,22 @@ class Operation implements Serializable { } return microsInt; } + + /** + * Primarily for debugger support + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(type.toString()); + sb.append(' '); + sb.append(loadedClass.toString()); + if (subops.size() > 0) { + sb.append(" ("); + sb.append(subops.size()); + sb.append(" sub ops)"); + } + return sb.toString(); + } + } diff --git a/tools/preload/Policy.java b/tools/preload/Policy.java new file mode 100644 index 0000000..554966b --- /dev/null +++ b/tools/preload/Policy.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * This is not instantiated - we just provide data for other classes to use + */ +public class Policy { + + /** + * This location (in the build system) of the preloaded-classes file. + */ + 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<String> NOT_FROM_ZYGOTE = new HashSet<String>(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. + */ + private static final Set<String> SERVICES = new HashSet<String>(Arrays.asList( + SYSTEM_SERVER_PROCESS_NAME, + "com.android.acore", + // Commented out to make sure DefaultTimeZones gets preloaded. + // "com.android.phone", + "com.google.process.content", + "android.process.media" + )); + + /** + * Classes which we shouldn't load from the Zygote. + */ + private static final Set<String> EXCLUDED_CLASSES = new HashSet<String>(Arrays.asList( + // Binders + "android.app.AlarmManager", + "android.app.SearchManager", + "android.os.FileObserver", + "com.android.server.PackageManagerService$AppDirObserver", + + // Threads + "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. + */ + public static String getPreloadedClassFileName() { + 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 + */ + public static boolean isService(String processName) { + return SERVICES.contains(processName); + } + + /** + * Reports if the given class should never be preloaded + */ + public static boolean isPreloadableClass(String className) { + return !EXCLUDED_CLASSES.contains(className); + } +} diff --git a/tools/preload/Proc.java b/tools/preload/Proc.java index 0b27a51..22697f8 100644 --- a/tools/preload/Proc.java +++ b/tools/preload/Proc.java @@ -43,49 +43,6 @@ class Proc implements Serializable { */ static final int MAX_TO_PRELOAD = 100; - /** Name of system server process. */ - private static final String SYSTEM_SERVER = "system_server"; - - /** Names of non-application processes. */ - private static final Set<String> NOT_FROM_ZYGOTE - = new HashSet<String>(Arrays.asList( - "zygote", - "dexopt", - "unknown", - SYSTEM_SERVER, - "com.android.development", - "app_process" // am - )); - - /** Long running services. */ - private static final Set<String> SERVICES - = new HashSet<String>(Arrays.asList( - SYSTEM_SERVER, - "com.android.home", -// Commented out to make sure DefaultTimeZones gets preloaded. -// "com.android.phone", - "com.google.process.content", - "com.android.process.media" - )); - - /** - * Classes which we shouldn't load from the Zygote. - */ - static final Set<String> EXCLUDED_CLASSES - = new HashSet<String>(Arrays.asList( - // Binders - "android.app.AlarmManager", - "android.app.SearchManager", - "android.os.FileObserver", - "com.android.server.PackageManagerService$AppDirObserver", - - // Threads - "java.lang.ProcessManager", - - // This class was deleted. - "java.math.Elementary" - )); - /** Parent process. */ final Proc parent; @@ -139,17 +96,12 @@ class Proc implements Serializable { } /** - * Is this a long running process? - */ - boolean isService() { - return SERVICES.contains(this.name); - } - - /** * Returns a list of classes which should be preloaded. + * + * @param takeAllClasses forces all classes to be taken (irrespective of ranking) */ - List<LoadedClass> highestRankedClasses() { - if (NOT_FROM_ZYGOTE.contains(this.name)) { + List<LoadedClass> highestRankedClasses(boolean takeAllClasses) { + if (!isApplication()) { return Collections.emptyList(); } @@ -162,23 +114,30 @@ class Proc implements Serializable { int timeToSave = totalTimeMicros() * percentageToPreload() / 100; int timeSaved = 0; - boolean service = isService(); + boolean service = Policy.isService(this.name); List<LoadedClass> highest = new ArrayList<LoadedClass>(); for (Operation operation : ranked) { - if (highest.size() >= MAX_TO_PRELOAD) { - System.out.println(name + " got " - + (timeSaved * 100 / timeToSave) + "% through"); - - break; + + // 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) { - break; + // The remaining rules apply even to wired-down processes + if (!Policy.isPreloadableClass(operation.loadedClass.name)) { + continue; } - - if (EXCLUDED_CLASSES.contains(operation.loadedClass.name) - || !operation.loadedClass.systemClass) { + + if (!operation.loadedClass.systemClass) { continue; } @@ -205,9 +164,13 @@ class Proc implements Serializable { return totalTime; } - /** Returns true if this process is an app. */ + /** + * 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 !NOT_FROM_ZYGOTE.contains(name); + return Policy.isFromZygote(name); } /** diff --git a/tools/preload/Record.java b/tools/preload/Record.java index 9ffab46..b2be4d4 100644 --- a/tools/preload/Record.java +++ b/tools/preload/Record.java @@ -56,11 +56,14 @@ class Record { /** Record time (ns). */ final long time; + + /** Source file line# */ + int sourceLineNumber; /** * Parses a line from the loaded-classes file. */ - Record(String line) { + Record(String line, int lineNum) { char typeChar = line.charAt(0); switch (typeChar) { case '>': type = Type.START_LOAD; break; @@ -70,6 +73,8 @@ class Record { default: throw new AssertionError("Bad line: " + line); } + sourceLineNumber = lineNum; + line = line.substring(1); String[] parts = line.split(":"); @@ -77,20 +82,51 @@ class Record { pid = Integer.parseInt(parts[1]); tid = Integer.parseInt(parts[2]); - processName = parts[3].intern(); + processName = decode(parts[3]).intern(); classLoader = Integer.parseInt(parts[4]); - className = vmTypeToLanguage(parts[5]).intern(); + className = vmTypeToLanguage(decode(parts[5])).intern(); time = Long.parseLong(parts[6]); } + + /** + * Decode any escaping that may have been written to the log line. + * + * Supports unicode-style escaping: \\uXXXX = character in hex + * + * @param rawField the field as it was written into the log + * @result the same field with any escaped characters replaced + */ + String decode(String rawField) { + String result = rawField; + int offset = result.indexOf("\\u"); + while (offset >= 0) { + String before = result.substring(0, offset); + String escaped = result.substring(offset+2, offset+6); + String after = result.substring(offset+6); + + result = String.format("%s%c%s", before, Integer.parseInt(escaped, 16), after); + + // find another but don't recurse + offset = result.indexOf("\\u", offset + 1); + } + return result; + } /** * Converts a VM-style name to a language-style name. */ - static String vmTypeToLanguage(String typeName) { + String vmTypeToLanguage(String typeName) { + // if the typename is (null), just return it as-is. This is probably in dexopt and + // will be discarded anyway. NOTE: This corresponds to the case in dalvik/vm/oo/Class.c + // where dvmLinkClass() returns false and we clean up and exit. + if ("(null)".equals(typeName)) { + return typeName; + } + if (!typeName.startsWith("L") || !typeName.endsWith(";") ) { - throw new AssertionError("Bad name: " + typeName); + throw new AssertionError("Bad name: " + typeName + " in line " + sourceLineNumber); } typeName = typeName.substring(1, typeName.length() - 1); diff --git a/tools/preload/WritePreloadedClassFile.java b/tools/preload/WritePreloadedClassFile.java index 5596a62..d87b1f0 100644 --- a/tools/preload/WritePreloadedClassFile.java +++ b/tools/preload/WritePreloadedClassFile.java @@ -14,54 +14,69 @@ * limitations under the License. */ -import java.io.IOException; -import java.io.Writer; import java.io.BufferedWriter; -import java.io.OutputStreamWriter; import java.io.FileOutputStream; +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; -import java.util.List; /** - * Writes /java/android/preloaded-classes. Also updates LoadedClass.preloaded + * Writes /frameworks/base/preloaded-classes. Also updates LoadedClass.preloaded * fields and writes over compiled log file. */ public class WritePreloadedClassFile { - private static final String PRELOADED_CLASS_FILE - = "java/android/preloaded-classes"; - - public static void main(String[] args) - throws IOException, ClassNotFoundException { - if (args.length != 1) { - System.err.println( - "Usage: WritePreloadedClassFile [compiled log file]"); + public static void main(String[] args) throws IOException, ClassNotFoundException { + + // Process command-line arguments first + List<String> wiredProcesses = new ArrayList<String>(); + 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); } - Root root = Root.fromFile(args[0]); + Root root = Root.fromFile(inputFileName); for (LoadedClass loadedClass : root.loadedClasses.values()) { loadedClass.preloaded = false; } Writer out = new BufferedWriter(new OutputStreamWriter( - new FileOutputStream(PRELOADED_CLASS_FILE), + 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 /tools/preload.\n"); + 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 + ", bucket_size=" + ClassRank.BUCKET_SIZE + "\n"); + for (String wiredProcess : wiredProcesses) { + out.write("# forcing classes loaded by: " + wiredProcess + "\n"); + } Set<LoadedClass> highestRanked = new TreeSet<LoadedClass>(); for (Proc proc : root.processes.values()) { - List<LoadedClass> highestForProc = proc.highestRankedClasses(); + // test to see if this is one of the wired-down ("take all classes") processes + boolean isWired = wiredProcesses.contains(proc.name); + + List<LoadedClass> highestForProc = proc.highestRankedClasses(isWired); System.out.println(proc.name + ": " + highestForProc.size()); @@ -82,6 +97,6 @@ public class WritePreloadedClassFile { + " classes will be preloaded."); // Update data to reflect LoadedClass.preloaded changes. - root.toFile(args[0]); + root.toFile(inputFileName); } } |