summaryrefslogtreecommitdiffstats
path: root/tools/preload
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-02-10 15:44:00 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-02-10 15:44:00 -0800
commitd24b8183b93e781080b2c16c487e60d51c12da31 (patch)
treefbb89154858984eb8e41556da7e9433040d55cd4 /tools/preload
parentf1e484acb594a726fb57ad0ae4cfe902c7f35858 (diff)
downloadframeworks_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.java27
-rw-r--r--tools/preload/LoadedClass.java6
-rw-r--r--tools/preload/MemoryUsage.java11
-rw-r--r--tools/preload/Operation.java18
-rw-r--r--tools/preload/Policy.java114
-rw-r--r--tools/preload/Proc.java93
-rw-r--r--tools/preload/Record.java46
-rw-r--r--tools/preload/WritePreloadedClassFile.java55
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);
}
}