diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch) | |
tree | 35051494d2af230dce54d6b31c6af8fc24091316 /tools/preload | |
download | frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2 |
Initial Contribution
Diffstat (limited to 'tools/preload')
-rw-r--r-- | tools/preload/20080522.compiled | bin | 0 -> 11414749 bytes | |||
-rw-r--r-- | tools/preload/Android.mk | 11 | ||||
-rw-r--r-- | tools/preload/ClassRank.java | 53 | ||||
-rw-r--r-- | tools/preload/Compile.java | 70 | ||||
-rw-r--r-- | tools/preload/LoadedClass.java | 163 | ||||
-rw-r--r-- | tools/preload/MemoryUsage.java | 272 | ||||
-rw-r--r-- | tools/preload/Operation.java | 118 | ||||
-rw-r--r-- | tools/preload/PrintCsv.java | 89 | ||||
-rw-r--r-- | tools/preload/PrintPsTree.java | 46 | ||||
-rw-r--r-- | tools/preload/Proc.java | 298 | ||||
-rw-r--r-- | tools/preload/Record.java | 99 | ||||
-rw-r--r-- | tools/preload/Root.java | 163 | ||||
-rw-r--r-- | tools/preload/WritePreloadedClassFile.java | 87 | ||||
-rw-r--r-- | tools/preload/loadclass/Android.mk | 8 | ||||
-rw-r--r-- | tools/preload/loadclass/LoadClass.java | 80 | ||||
-rw-r--r-- | tools/preload/preload.iml | 15 | ||||
-rw-r--r-- | tools/preload/preload.ipr | 467 |
17 files changed, 2039 insertions, 0 deletions
diff --git a/tools/preload/20080522.compiled b/tools/preload/20080522.compiled Binary files differnew file mode 100644 index 0000000..a2af422 --- /dev/null +++ b/tools/preload/20080522.compiled diff --git a/tools/preload/Android.mk b/tools/preload/Android.mk new file mode 100644 index 0000000..d3457fe --- /dev/null +++ b/tools/preload/Android.mk @@ -0,0 +1,11 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := *.java + +LOCAL_MODULE:= preload + +include $(BUILD_HOST_JAVA_LIBRARY) + +include $(call all-subdir-makefiles) diff --git a/tools/preload/ClassRank.java b/tools/preload/ClassRank.java new file mode 100644 index 0000000..3699b89 --- /dev/null +++ b/tools/preload/ClassRank.java @@ -0,0 +1,53 @@ +/* + * 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.Comparator; + +/** + * Ranks classes for preloading based on how long their operations took + * and how early the operations happened. Higher ranked classes come first. + */ +class ClassRank implements Comparator<Operation> { + + /** + * Increase this number to add more weight to classes which were loaded + * earlier. + */ + static final int SEQUENCE_WEIGHT = 500; // 5 ms + + static final int BUCKET_SIZE = 5; + + public int compare(Operation a, Operation b) { + // Higher ranked operations should come first. + int result = rankOf(b) - rankOf(a); + if (result != 0) { + return result; + } + + // Make sure we don't drop one of two classes w/ the same rank. + // If a load and an initialization have the same rank, it's OK + // to treat the operations equally. + return a.loadedClass.name.compareTo(b.loadedClass.name); + } + + /** Ranks the given operation. */ + private static int rankOf(Operation o) { + return o.medianExclusiveTimeMicros() + + SEQUENCE_WEIGHT / (o.index / BUCKET_SIZE + 1); + } +} + + diff --git a/tools/preload/Compile.java b/tools/preload/Compile.java new file mode 100644 index 0000000..8388b12 --- /dev/null +++ b/tools/preload/Compile.java @@ -0,0 +1,70 @@ +/* + * 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.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.util.ArrayList; +import java.lang.reflect.InvocationTargetException; + +/** + * 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. + */ +public class Compile { + + public static void main(String[] args) + throws IOException, NoSuchMethodException, IllegalAccessException, + InvocationTargetException { + if (args.length != 2) { + System.err.println("Usage: Compile [log file] [output file]"); + System.exit(0); + } + + Root root = new Root(); + + List<Record> records = new ArrayList<Record>(); + + BufferedReader in = new BufferedReader(new InputStreamReader( + new FileInputStream(args[0]))); + + String line; + while ((line = in.readLine()) != null) { + if (line.startsWith("I/PRELOAD")) { + line = line.substring(19); + records.add(new Record(line)); + } + } + + for (Record record : records) { + root.indexProcess(record); + } + + for (Record record : records) { + root.indexClassOperation(record); + } + + in.close(); + + root.toFile(args[1]); + } +} diff --git a/tools/preload/LoadedClass.java b/tools/preload/LoadedClass.java new file mode 100644 index 0000000..6b3d345 --- /dev/null +++ b/tools/preload/LoadedClass.java @@ -0,0 +1,163 @@ +/* + * 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.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Set; +import java.util.HashSet; +import java.io.Serializable; + +/** + * A loaded class. + */ +class LoadedClass implements Serializable, Comparable<LoadedClass> { + + private static final long serialVersionUID = 0; + + /** Class name. */ + final String name; + + /** Load operations. */ + final List<Operation> loads = new ArrayList<Operation>(); + + /** Static initialization operations. */ + final List<Operation> initializations = new ArrayList<Operation>(); + + /** Memory usage gathered by loading only this class in its own VM. */ + MemoryUsage memoryUsage = MemoryUsage.NOT_AVAILABLE; + + /** + * Whether or not this class was loaded in the system class loader. + */ + final boolean systemClass; + + /** Whether or not this class will be preloaded. */ + boolean preloaded; + + /** Constructs a new class. */ + LoadedClass(String name, boolean systemClass) { + this.name = name; + this.systemClass = systemClass; + } + + void measureMemoryUsage() { + this.memoryUsage = MemoryUsage.forClass(name); + } + + int mlt = -1; + + /** Median time to load this class. */ + int medianLoadTimeMicros() { + if (mlt != -1) { + return mlt; + } + + return mlt = calculateMedian(loads); + } + + int mit = -1; + + /** Median time to initialize this class. */ + int medianInitTimeMicros() { + if (mit != -1) { + return mit; + } + + return mit = calculateMedian(initializations); + } + + /** Calculates the median duration for a list of operations. */ + private static int calculateMedian(List<Operation> operations) { + int size = operations.size(); + if (size == 0) { + return 0; + } + + int[] times = new int[size]; + for (int i = 0; i < size; i++) { + times[i] = operations.get(i).exclusiveTimeMicros(); + } + + Arrays.sort(times); + int middle = size / 2; + if (size % 2 == 1) { + // Odd + return times[middle]; + } else { + // Even -- average the two. + return (times[middle - 1] + times[middle]) / 2; + } + } + + /** + * Counts loads by apps. + */ + int appLoads() { + return operationsByApps(loads); + } + + /** + * Counts inits by apps. + */ + int appInits() { + return operationsByApps(initializations); + } + + /** + * Counts number of app operations in the given list. + */ + private static int operationsByApps(List<Operation> operations) { + int byApps = 0; + for (Operation operation : operations) { + if (operation.process.isApplication()) { + byApps++; + } + } + return byApps; + } + + public int compareTo(LoadedClass o) { + return name.compareTo(o.name); + } + + public String toString() { + return name; + } + + /** + * Returns true if this class's initialization causes the given class to + * initialize. + */ + public boolean initializes(LoadedClass clazz, Set<LoadedClass> visited) { + // Avoid infinite recursion. + if (!visited.add(this)) { + return false; + } + + if (clazz == this) { + return true; + } + + for (Operation initialization : initializations) { + if (initialization.loadedClass.initializes(clazz, visited)) { + return true; + } + } + + return false; + } +} diff --git a/tools/preload/MemoryUsage.java b/tools/preload/MemoryUsage.java new file mode 100644 index 0000000..89717eb --- /dev/null +++ b/tools/preload/MemoryUsage.java @@ -0,0 +1,272 @@ +/* + * 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.io.Serializable; +import java.io.IOException; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Memory usage information. + */ +class MemoryUsage implements Serializable { + + private static final long serialVersionUID = 0; + + static final MemoryUsage NOT_AVAILABLE = new MemoryUsage(); + + final int nativeSharedPages; + final int javaSharedPages; + final int otherSharedPages; + final int nativePrivatePages; + final int javaPrivatePages; + final int otherPrivatePages; + + final int allocCount; + final int allocSize; + final int freedCount; + final int freedSize; + final long nativeHeapSize; + + public MemoryUsage(String line) { + String[] parsed = line.split(","); + + nativeSharedPages = Integer.parseInt(parsed[1]); + javaSharedPages = Integer.parseInt(parsed[2]); + otherSharedPages = Integer.parseInt(parsed[3]); + nativePrivatePages = Integer.parseInt(parsed[4]); + javaPrivatePages = Integer.parseInt(parsed[5]); + otherPrivatePages = Integer.parseInt(parsed[6]); + allocCount = Integer.parseInt(parsed[7]); + allocSize = Integer.parseInt(parsed[8]); + freedCount = Integer.parseInt(parsed[9]); + freedSize = Integer.parseInt(parsed[10]); + nativeHeapSize = Long.parseLong(parsed[11]); + } + + MemoryUsage() { + nativeSharedPages = -1; + javaSharedPages = -1; + otherSharedPages = -1; + nativePrivatePages = -1; + javaPrivatePages = -1; + otherPrivatePages = -1; + + allocCount = -1; + allocSize = -1; + freedCount = -1; + freedSize = -1; + nativeHeapSize = -1; + } + + MemoryUsage(int nativeSharedPages, + int javaSharedPages, + int otherSharedPages, + int nativePrivatePages, + int javaPrivatePages, + int otherPrivatePages, + int allocCount, + int allocSize, + int freedCount, + int freedSize, + long nativeHeapSize) { + this.nativeSharedPages = nativeSharedPages; + this.javaSharedPages = javaSharedPages; + this.otherSharedPages = otherSharedPages; + this.nativePrivatePages = nativePrivatePages; + this.javaPrivatePages = javaPrivatePages; + this.otherPrivatePages = otherPrivatePages; + this.allocCount = allocCount; + this.allocSize = allocSize; + this.freedCount = freedCount; + this.freedSize = freedSize; + this.nativeHeapSize = nativeHeapSize; + } + + MemoryUsage subtract(MemoryUsage baseline) { + return new MemoryUsage( + nativeSharedPages - baseline.nativeSharedPages, + javaSharedPages - baseline.javaSharedPages, + otherSharedPages - baseline.otherSharedPages, + nativePrivatePages - baseline.nativePrivatePages, + javaPrivatePages - baseline.javaPrivatePages, + otherPrivatePages - baseline.otherPrivatePages, + allocCount - baseline.allocCount, + allocSize - baseline.allocSize, + freedCount - baseline.freedCount, + freedSize - baseline.freedSize, + nativeHeapSize - baseline.nativeHeapSize); + } + + int javaHeapSize() { + return allocSize - freedSize; + } + + int javaPagesInK() { + return (javaSharedPages + javaPrivatePages) * 4; + } + + int nativePagesInK() { + return (nativeSharedPages + nativePrivatePages) * 4; + } + int otherPagesInK() { + return (otherSharedPages + otherPrivatePages) * 4; + } + + /** + * Was this information available? + */ + boolean isAvailable() { + return nativeSharedPages != -1; + } + + /** + * Measures baseline memory usage. + */ + static MemoryUsage baseline() { + return forClass(null); + } + + private static final String CLASS_PATH = "-Xbootclasspath" + + ":/system/framework/core.jar" + + ":/system/framework/ext.jar" + + ":/system/framework/framework.jar" + + ":/system/framework/framework-tests.jar" + + ":/system/framework/services.jar" + + ":/system/framework/loadclass.jar"; + + private static final String[] GET_DIRTY_PAGES = { + "adb", "-e", "shell", "dalvikvm", CLASS_PATH, "LoadClass" }; + + /** + * Measures memory usage for the given class. + */ + static MemoryUsage forClass(String className) { + MeasureWithTimeout measurer = new MeasureWithTimeout(className); + + new Thread(measurer).start(); + + synchronized (measurer) { + if (measurer.memoryUsage == null) { + // Wait up to 10s. + try { + measurer.wait(30000); + } catch (InterruptedException e) { + System.err.println("Interrupted waiting for measurement."); + e.printStackTrace(); + return NOT_AVAILABLE; + } + + // If it's still null. + if (measurer.memoryUsage == null) { + System.err.println("Timed out while measuring " + + className + "."); + return NOT_AVAILABLE; + } + } + + System.err.println("Got memory usage for " + className + "."); + return measurer.memoryUsage; + } + } + + static class MeasureWithTimeout implements Runnable { + + final String className; + MemoryUsage memoryUsage = null; + + MeasureWithTimeout(String className) { + this.className = className; + } + + public void run() { + MemoryUsage measured = measure(); + + synchronized (this) { + memoryUsage = measured; + notifyAll(); + } + } + + private MemoryUsage measure() { + String[] commands = GET_DIRTY_PAGES; + if (className != null) { + List<String> commandList = new ArrayList<String>( + GET_DIRTY_PAGES.length + 1); + commandList.addAll(Arrays.asList(commands)); + commandList.add(className); + commands = commandList.toArray(new String[commandList.size()]); + } + + try { + final Process process = Runtime.getRuntime().exec(commands); + + final InputStream err = process.getErrorStream(); + + // Send error output to stderr. + Thread errThread = new Thread() { + @Override + public void run() { + copy(err, System.err); + } + }; + errThread.setDaemon(true); + errThread.start(); + + BufferedReader in = new BufferedReader( + new InputStreamReader(process.getInputStream())); + String line = in.readLine(); + if (line == null || !line.startsWith("DECAFBAD,")) { + System.err.println("Got bad response for " + className + + ": " + line); + return NOT_AVAILABLE; + } + + in.close(); + err.close(); + process.destroy(); + + return new MemoryUsage(line); + } catch (IOException e) { + System.err.println("Error getting stats for " + + className + "."); + e.printStackTrace(); + return NOT_AVAILABLE; + } + } + + } + + /** + * Copies from one stream to another. + */ + private static void copy(InputStream in, OutputStream out) { + byte[] buffer = new byte[1024]; + int read; + try { + while ((read = in.read(buffer)) > -1) { + out.write(buffer, 0, read); + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/tools/preload/Operation.java b/tools/preload/Operation.java new file mode 100644 index 0000000..65c9a03 --- /dev/null +++ b/tools/preload/Operation.java @@ -0,0 +1,118 @@ +/* + * 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.List; +import java.util.ArrayList; +import java.io.Serializable; + +/** + * An operation with a duration. Could represent a class load or initialization. + */ +class Operation implements Serializable { + + private static final long serialVersionUID = 0; + + /** + * Type of operation. + */ + enum Type { + LOAD, INIT + } + + /** Process this operation occurred in. */ + final Proc process; + + /** Start time for this operation. */ + final long startTimeNanos; + + /** Index of this operation relative to its process. */ + final int index; + + /** Type of operation. */ + final Type type; + + /** End time for this operation. */ + long endTimeNanos = -1; + + /** The class that this operation loaded or initialized. */ + final LoadedClass loadedClass; + + /** Other operations that occurred during this one. */ + final List<Operation> subops = new ArrayList<Operation>(); + + /** Constructs a new operation. */ + Operation(Proc process, LoadedClass loadedClass, long startTimeNanos, + int index, Type type) { + this.process = process; + this.loadedClass = loadedClass; + this.startTimeNanos = startTimeNanos; + this.index = index; + this.type = type; + } + + /** + * Returns how long this class initialization and all the nested class + * initializations took. + */ + private long inclusiveTimeNanos() { + if (endTimeNanos == -1) { + throw new IllegalStateException("End time hasn't been set yet: " + + loadedClass.name); + } + + return endTimeNanos - startTimeNanos; + } + + /** + * Returns how long this class initialization took. + */ + int exclusiveTimeMicros() { + long exclusive = inclusiveTimeNanos(); + + for (Operation child : subops) { + exclusive -= child.inclusiveTimeNanos(); + } + + if (exclusive < 0) { + throw new AssertionError(loadedClass.name); + } + + return nanosToMicros(exclusive); + } + + /** Gets the median time that this operation took across all processes. */ + int medianExclusiveTimeMicros() { + switch (type) { + case LOAD: return loadedClass.medianLoadTimeMicros(); + case INIT: return loadedClass.medianInitTimeMicros(); + default: throw new AssertionError(); + } + } + + /** + * Converts nanoseconds to microseconds. + * + * @throws RuntimeException if overflow occurs + */ + private static int nanosToMicros(long nanos) { + long micros = nanos / 1000; + int microsInt = (int) micros; + if (microsInt != micros) { + throw new RuntimeException("Integer overflow: " + nanos); + } + return microsInt; + } +} diff --git a/tools/preload/PrintCsv.java b/tools/preload/PrintCsv.java new file mode 100644 index 0000000..9f2a318 --- /dev/null +++ b/tools/preload/PrintCsv.java @@ -0,0 +1,89 @@ +/* + * 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.io.IOException; +import java.io.FileInputStream; +import java.io.ObjectInputStream; +import java.io.BufferedInputStream; + +/** + * Prints raw information in CSV format. + */ +public class PrintCsv { + + public static void main(String[] args) + throws IOException, ClassNotFoundException { + if (args.length != 1) { + System.err.println("Usage: PrintCsv [compiled log file]"); + System.exit(0); + } + + Root root = Root.fromFile(args[0]); + + System.out.println("Name" + + ",Preloaded" + + ",Median Load Time (us)" + + ",Median Init Time (us)" + + ",Load Count" + + ",Init Count" + + ",Managed Heap (B)" + + ",Native Heap (B)" + + ",Managed Pages (kB)" + + ",Native Pages (kB)" + + ",Other Pages (kB)"); + + MemoryUsage baseline = root.baseline; + + for (LoadedClass loadedClass : root.loadedClasses.values()) { + if (!loadedClass.systemClass) { + continue; + } + + System.out.print(loadedClass.name); + System.out.print(','); + System.out.print(loadedClass.preloaded); + System.out.print(','); + System.out.print(loadedClass.medianLoadTimeMicros()); + System.out.print(','); + System.out.print(loadedClass.medianInitTimeMicros()); + 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); + + System.out.print(','); + System.out.print(subtracted.javaHeapSize()); + System.out.print(','); + System.out.print(subtracted.nativeHeapSize); + System.out.print(','); + System.out.print(subtracted.javaPagesInK()); + System.out.print(','); + System.out.print(subtracted.nativePagesInK()); + System.out.print(','); + System.out.print(subtracted.otherPagesInK()); + + } else { + System.out.print(",n/a,n/a,n/a,n/a,n/a"); + } + + System.out.println(); + } + } +} diff --git a/tools/preload/PrintPsTree.java b/tools/preload/PrintPsTree.java new file mode 100644 index 0000000..22701fa --- /dev/null +++ b/tools/preload/PrintPsTree.java @@ -0,0 +1,46 @@ +/* + * 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.io.IOException; +import java.io.FileInputStream; +import java.io.ObjectInputStream; +import java.io.BufferedInputStream; + +/** + * Prints raw information in CSV format. + */ +public class PrintPsTree { + + public static void main(String[] args) + throws IOException, ClassNotFoundException { + if (args.length != 1) { + System.err.println("Usage: PrintCsv [compiled log file]"); + System.exit(0); + } + + FileInputStream fin = new FileInputStream(args[0]); + ObjectInputStream oin = new ObjectInputStream( + new BufferedInputStream(fin)); + + Root root = (Root) oin.readObject(); + + for (Proc proc : root.processes.values()) { + if (proc.parent == null) { + proc.print(); + } + } + } +} diff --git a/tools/preload/Proc.java b/tools/preload/Proc.java new file mode 100644 index 0000000..0b27a51 --- /dev/null +++ b/tools/preload/Proc.java @@ -0,0 +1,298 @@ +/* + * 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.Set; +import java.util.HashSet; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Map; +import java.util.HashMap; +import java.util.Collections; +import java.util.TreeSet; +import java.io.Serializable; + +/** + * A Dalvik process. + */ +class Proc implements Serializable { + + private static final long serialVersionUID = 0; + + /** + * Default percentage of time to cut off of app class loading times. + */ + static final int PERCENTAGE_TO_PRELOAD = 75; + + /** + * Maximum number of classes to preload for a given process. + */ + 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; + + /** Process ID. */ + final int id; + + /** + * Name of this process. We may not have the correct name at first, i.e. + * some classes could have been loaded before the process name was set. + */ + String name; + + /** Child processes. */ + final List<Proc> children = new ArrayList<Proc>(); + + /** Maps thread ID to operation stack. */ + transient final Map<Integer, LinkedList<Operation>> stacks + = new HashMap<Integer, LinkedList<Operation>>(); + + /** Number of operations. */ + int operationCount; + + /** Sequential list of operations that happened in this process. */ + final List<Operation> operations = new ArrayList<Operation>(); + + /** List of past process names. */ + final List<String> nameHistory = new ArrayList<String>(); + + /** Constructs a new process. */ + Proc(Proc parent, int id) { + this.parent = parent; + this.id = id; + } + + /** Sets name of this process. */ + void setName(String name) { + if (!name.equals(this.name)) { + if (this.name != null) { + nameHistory.add(this.name); + } + this.name = name; + } + } + + /** + * Returns the percentage of time we should cut by preloading for this + * app. + */ + int percentageToPreload() { + return PERCENTAGE_TO_PRELOAD; + } + + /** + * Is this a long running process? + */ + boolean isService() { + return SERVICES.contains(this.name); + } + + /** + * Returns a list of classes which should be preloaded. + */ + List<LoadedClass> highestRankedClasses() { + if (NOT_FROM_ZYGOTE.contains(this.name)) { + return Collections.emptyList(); + } + + // Sort by rank. + Operation[] ranked = new Operation[operations.size()]; + ranked = operations.toArray(ranked); + Arrays.sort(ranked, new ClassRank()); + + // The percentage of time to save by preloading. + int timeToSave = totalTimeMicros() * percentageToPreload() / 100; + int timeSaved = 0; + + boolean service = isService(); + + 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; + } + + if (timeSaved >= timeToSave) { + break; + } + + if (EXCLUDED_CLASSES.contains(operation.loadedClass.name) + || !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.* + timeSaved += operation.medianExclusiveTimeMicros(); + } + + return highest; + } + + /** + * Total time spent class loading and initializing. + */ + int totalTimeMicros() { + int totalTime = 0; + for (Operation operation : operations) { + totalTime += operation.medianExclusiveTimeMicros(); + } + return totalTime; + } + + /** Returns true if this process is an app. */ + public boolean isApplication() { + return !NOT_FROM_ZYGOTE.contains(name); + } + + /** + * Starts an operation. + * + * @param threadId thread the operation started in + * @param loadedClass class operation happened to + * @param time the operation started + */ + void startOperation(int threadId, LoadedClass loadedClass, long time, + Operation.Type type) { + Operation o = new Operation( + this, loadedClass, time, operationCount++, type); + operations.add(o); + + LinkedList<Operation> stack = stacks.get(threadId); + if (stack == null) { + stack = new LinkedList<Operation>(); + stacks.put(threadId, stack); + } + + if (!stack.isEmpty()) { + stack.getLast().subops.add(o); + } + + stack.add(o); + } + + /** + * Ends an operation. + * + * @param threadId thread the operation ended in + * @param loadedClass class operation happened to + * @param time the operation ended + */ + Operation endOperation(int threadId, String className, + LoadedClass loadedClass, long time) { + LinkedList<Operation> stack = stacks.get(threadId); + + if (stack == null || stack.isEmpty()) { + didNotStart(className); + return null; + } + + Operation o = stack.getLast(); + if (loadedClass != o.loadedClass) { + didNotStart(className); + return null; + } + + stack.removeLast(); + + o.endTimeNanos = time; + return o; + } + + /** + * Prints an error indicating that we saw the end of an operation but not + * the start. A bug in the logging framework which results in dropped logs + * causes this. + */ + private static void didNotStart(String name) { + System.err.println("Warning: An operation ended on " + name + + " but it never started!"); + } + + /** + * Prints this process tree to stdout. + */ + void print() { + print(""); + } + + /** + * Prints a child proc to standard out. + */ + private void print(String prefix) { + System.out.println(prefix + "id=" + id + ", name=" + name); + for (Proc child : children) { + child.print(prefix + " "); + } + } + + @Override + public String toString() { + return this.name; + } +} diff --git a/tools/preload/Record.java b/tools/preload/Record.java new file mode 100644 index 0000000..9ffab46 --- /dev/null +++ b/tools/preload/Record.java @@ -0,0 +1,99 @@ +/* + * 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. + */ + +/** + * One line from the loaded-classes file. + */ +class Record { + + enum Type { + /** Start of initialization. */ + START_LOAD, + + /** End of initialization. */ + END_LOAD, + + /** Start of initialization. */ + START_INIT, + + /** End of initialization. */ + END_INIT + } + + /** Parent process ID. */ + final int ppid; + + /** Process ID. */ + final int pid; + + /** Thread ID. */ + final int tid; + + /** Process name. */ + final String processName; + + /** Class loader pointer. */ + final int classLoader; + + /** Type of record. */ + final Type type; + + /** Name of loaded class. */ + final String className; + + /** Record time (ns). */ + final long time; + + /** + * Parses a line from the loaded-classes file. + */ + Record(String line) { + char typeChar = line.charAt(0); + switch (typeChar) { + case '>': type = Type.START_LOAD; break; + case '<': type = Type.END_LOAD; break; + case '+': type = Type.START_INIT; break; + case '-': type = Type.END_INIT; break; + default: throw new AssertionError("Bad line: " + line); + } + + line = line.substring(1); + String[] parts = line.split(":"); + + ppid = Integer.parseInt(parts[0]); + pid = Integer.parseInt(parts[1]); + tid = Integer.parseInt(parts[2]); + + processName = parts[3].intern(); + + classLoader = Integer.parseInt(parts[4]); + className = vmTypeToLanguage(parts[5]).intern(); + + time = Long.parseLong(parts[6]); + } + + /** + * Converts a VM-style name to a language-style name. + */ + static String vmTypeToLanguage(String typeName) { + if (!typeName.startsWith("L") || !typeName.endsWith(";") ) { + throw new AssertionError("Bad name: " + typeName); + } + + typeName = typeName.substring(1, typeName.length() - 1); + return typeName.replace("/", "."); + } +} diff --git a/tools/preload/Root.java b/tools/preload/Root.java new file mode 100644 index 0000000..949f9b7 --- /dev/null +++ b/tools/preload/Root.java @@ -0,0 +1,163 @@ +/* + * 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.io.Serializable; +import java.io.IOException; +import java.io.Writer; +import java.io.BufferedWriter; +import java.io.OutputStreamWriter; +import java.io.FileOutputStream; +import java.io.FileInputStream; +import java.io.ObjectInputStream; +import java.io.BufferedInputStream; +import java.io.ObjectOutputStream; +import java.io.BufferedOutputStream; +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.TreeSet; +import java.util.Arrays; +import java.nio.charset.Charset; + +/** + * Root of our data model. + */ +public class Root implements Serializable { + + private static final long serialVersionUID = 0; + + /** pid -> Proc */ + final Map<Integer, Proc> processes = new HashMap<Integer, Proc>(); + + /** Class name -> LoadedClass */ + final Map<String, LoadedClass> loadedClasses + = new HashMap<String, LoadedClass>(); + + final MemoryUsage baseline = MemoryUsage.baseline(); + + /** + * Records class loads and initializations. + */ + void indexClassOperation(Record record) { + Proc process = processes.get(record.pid); + + // Ignore dexopt output. It loads applications classes through the + // system class loader and messes us up. + if (record.processName.equals("dexopt")) { + return; + } + + String name = record.className; + LoadedClass loadedClass = loadedClasses.get(name); + Operation o = null; + + switch (record.type) { + case START_LOAD: + case START_INIT: + if (loadedClass == null) { + loadedClass = new LoadedClass( + name, record.classLoader == 0); + if (loadedClass.systemClass) { + // Only measure memory for classes in the boot + // classpath. + loadedClass.measureMemoryUsage(); + } + loadedClasses.put(name, loadedClass); + } + break; + + case END_LOAD: + case END_INIT: + o = process.endOperation(record.tid, record.className, + loadedClass, record.time); + if (o == null) { + return; + } + } + + switch (record.type) { + case START_LOAD: + process.startOperation(record.tid, loadedClass, record.time, + Operation.Type.LOAD); + break; + + case START_INIT: + process.startOperation(record.tid, loadedClass, record.time, + Operation.Type.INIT); + break; + + case END_LOAD: + loadedClass.loads.add(o); + break; + + case END_INIT: + loadedClass.initializations.add(o); + break; + } + } + + /** + * Indexes information about the process from the given record. + */ + void indexProcess(Record record) { + Proc proc = processes.get(record.pid); + + if (proc == null) { + // Create a new process object. + Proc parent = processes.get(record.ppid); + proc = new Proc(parent, record.pid); + processes.put(proc.id, proc); + if (parent != null) { + parent.children.add(proc); + } + } + + proc.setName(record.processName); + } + + /** + * Writes this graph to a file. + */ + void toFile(String fileName) throws IOException { + FileOutputStream out = new FileOutputStream(fileName); + ObjectOutputStream oout = new ObjectOutputStream( + new BufferedOutputStream(out)); + + System.err.println("Writing object model..."); + + oout.writeObject(this); + + oout.close(); + + System.err.println("Done!"); + } + + /** + * Reads Root from a file. + */ + static Root fromFile(String fileName) + throws IOException, ClassNotFoundException { + FileInputStream fin = new FileInputStream(fileName); + ObjectInputStream oin = new ObjectInputStream( + new BufferedInputStream(fin)); + + Root root = (Root) oin.readObject(); + + oin.close(); + + return root; + } +} diff --git a/tools/preload/WritePreloadedClassFile.java b/tools/preload/WritePreloadedClassFile.java new file mode 100644 index 0000000..5596a62 --- /dev/null +++ b/tools/preload/WritePreloadedClassFile.java @@ -0,0 +1,87 @@ +/* + * 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.io.IOException; +import java.io.Writer; +import java.io.BufferedWriter; +import java.io.OutputStreamWriter; +import java.io.FileOutputStream; +import java.nio.charset.Charset; +import java.util.Set; +import java.util.TreeSet; +import java.util.List; + +/** + * Writes /java/android/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]"); + System.exit(0); + } + + Root root = Root.fromFile(args[0]); + + for (LoadedClass loadedClass : root.loadedClasses.values()) { + loadedClass.preloaded = false; + } + + Writer out = new BufferedWriter(new OutputStreamWriter( + new FileOutputStream(PRELOADED_CLASS_FILE), + 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("# percent=" + Proc.PERCENTAGE_TO_PRELOAD + ", weight=" + + ClassRank.SEQUENCE_WEIGHT + + ", bucket_size=" + ClassRank.BUCKET_SIZE + + "\n"); + + Set<LoadedClass> highestRanked = new TreeSet<LoadedClass>(); + for (Proc proc : root.processes.values()) { + List<LoadedClass> highestForProc = proc.highestRankedClasses(); + + System.out.println(proc.name + ": " + highestForProc.size()); + + for (LoadedClass loadedClass : highestForProc) { + loadedClass.preloaded = true; + } + highestRanked.addAll(highestForProc); + } + + for (LoadedClass loadedClass : highestRanked) { + 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(args[0]); + } +} diff --git a/tools/preload/loadclass/Android.mk b/tools/preload/loadclass/Android.mk new file mode 100644 index 0000000..435699d --- /dev/null +++ b/tools/preload/loadclass/Android.mk @@ -0,0 +1,8 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_MODULE := loadclass + +include $(BUILD_JAVA_LIBRARY) diff --git a/tools/preload/loadclass/LoadClass.java b/tools/preload/loadclass/LoadClass.java new file mode 100644 index 0000000..471cc84 --- /dev/null +++ b/tools/preload/loadclass/LoadClass.java @@ -0,0 +1,80 @@ +/* + * 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 android.util.Log; +import android.os.Debug; + +/** + * Loads a class, runs the garbage collector, and prints showmap output. + * + * <p>Usage: dalvikvm LoadClass [class name] + */ +class LoadClass { + + public static void main(String[] args) { + System.loadLibrary("android_runtime"); + + if (registerNatives() < 0) { + throw new RuntimeException("Error registering natives."); + } + + Debug.startAllocCounting(); + + if (args.length > 0) { + try { + Class.forName(args[0]); + } catch (ClassNotFoundException e) { + Log.w("LoadClass", e); + return; + } + } + + System.gc(); + + int allocCount = Debug.getGlobalAllocCount(); + int allocSize = Debug.getGlobalAllocSize(); + int freedCount = Debug.getGlobalFreedCount(); + int freedSize = Debug.getGlobalFreedSize(); + long nativeHeapSize = Debug.getNativeHeapSize(); + + Debug.stopAllocCounting(); + + StringBuilder response = new StringBuilder("DECAFBAD"); + + int[] pages = new int[6]; + Debug.MemoryInfo memoryInfo = new Debug.MemoryInfo(); + Debug.getMemoryInfo(memoryInfo); + response.append(',').append(memoryInfo.nativeSharedDirty); + response.append(',').append(memoryInfo.dalvikSharedDirty); + response.append(',').append(memoryInfo.otherSharedDirty); + response.append(',').append(memoryInfo.nativePrivateDirty); + response.append(',').append(memoryInfo.dalvikPrivateDirty); + response.append(',').append(memoryInfo.otherPrivateDirty); + + response.append(',').append(allocCount); + response.append(',').append(allocSize); + response.append(',').append(freedCount); + response.append(',').append(freedSize); + response.append(',').append(nativeHeapSize); + + System.out.println(response.toString()); + } + + /** + * Registers native functions. See AndroidRuntime.cpp. + */ + static native int registerNatives(); +} diff --git a/tools/preload/preload.iml b/tools/preload/preload.iml new file mode 100644 index 0000000..d1fab57 --- /dev/null +++ b/tools/preload/preload.iml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module relativePaths="true" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file:///tmp/preload/" /> + <exclude-output /> + <output-test url="file:///tmp/preload/" /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$" isTestSource="false" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntryProperties /> + </component> +</module> + diff --git a/tools/preload/preload.ipr b/tools/preload/preload.ipr new file mode 100644 index 0000000..c5613ad --- /dev/null +++ b/tools/preload/preload.ipr @@ -0,0 +1,467 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project relativePaths="false" version="4"> + <component name="AntConfiguration"> + <defaultAnt bundledAnt="true" /> + </component> + <component name="BuildJarProjectSettings"> + <option name="BUILD_JARS_ON_MAKE" value="false" /> + </component> + <component name="ChangeBrowserSettings"> + <option name="MAIN_SPLITTER_PROPORTION" value="0.3" /> + <option name="MESSAGES_SPLITTER_PROPORTION" value="0.8" /> + <option name="USE_DATE_BEFORE_FILTER" value="false" /> + <option name="USE_DATE_AFTER_FILTER" value="false" /> + <option name="USE_CHANGE_BEFORE_FILTER" value="false" /> + <option name="USE_CHANGE_AFTER_FILTER" value="false" /> + <option name="DATE_BEFORE" value="" /> + <option name="DATE_AFTER" value="" /> + <option name="CHANGE_BEFORE" value="" /> + <option name="CHANGE_AFTER" value="" /> + <option name="USE_USER_FILTER" value="false" /> + <option name="USER" value="" /> + </component> + <component name="CodeStyleProjectProfileManger"> + <option name="PROJECT_PROFILE" /> + <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" /> + </component> + <component name="CodeStyleSettingsManager"> + <option name="PER_PROJECT_SETTINGS" /> + <option name="USE_PER_PROJECT_SETTINGS" value="false" /> + </component> + <component name="CompilerConfiguration"> + <option name="DEFAULT_COMPILER" value="Javac" /> + <option name="DEPLOY_AFTER_MAKE" value="0" /> + <resourceExtensions> + <entry name=".+\.(properties|xml|html|dtd|tld)" /> + <entry name=".+\.(gif|png|jpeg|jpg)" /> + </resourceExtensions> + <wildcardResourcePatterns> + <entry name="?*.properties" /> + <entry name="?*.xml" /> + <entry name="?*.gif" /> + <entry name="?*.png" /> + <entry name="?*.jpeg" /> + <entry name="?*.jpg" /> + <entry name="?*.html" /> + <entry name="?*.dtd" /> + <entry name="?*.tld" /> + </wildcardResourcePatterns> + </component> + <component name="Cvs2Configuration"> + <option name="PRUNE_EMPTY_DIRECTORIES" value="true" /> + <option name="MERGING_MODE" value="0" /> + <option name="MERGE_WITH_BRANCH1_NAME" value="HEAD" /> + <option name="MERGE_WITH_BRANCH2_NAME" value="HEAD" /> + <option name="RESET_STICKY" value="false" /> + <option name="CREATE_NEW_DIRECTORIES" value="true" /> + <option name="DEFAULT_TEXT_FILE_SUBSTITUTION" value="kv" /> + <option name="PROCESS_UNKNOWN_FILES" value="false" /> + <option name="PROCESS_DELETED_FILES" value="false" /> + <option name="PROCESS_IGNORED_FILES" value="false" /> + <option name="RESERVED_EDIT" value="false" /> + <option name="CHECKOUT_DATE_OR_REVISION_SETTINGS"> + <value> + <option name="BRANCH" value="" /> + <option name="DATE" value="" /> + <option name="USE_BRANCH" value="false" /> + <option name="USE_DATE" value="false" /> + </value> + </option> + <option name="UPDATE_DATE_OR_REVISION_SETTINGS"> + <value> + <option name="BRANCH" value="" /> + <option name="DATE" value="" /> + <option name="USE_BRANCH" value="false" /> + <option name="USE_DATE" value="false" /> + </value> + </option> + <option name="SHOW_CHANGES_REVISION_SETTINGS"> + <value> + <option name="BRANCH" value="" /> + <option name="DATE" value="" /> + <option name="USE_BRANCH" value="false" /> + <option name="USE_DATE" value="false" /> + </value> + </option> + <option name="SHOW_OUTPUT" value="false" /> + <option name="ADD_WATCH_INDEX" value="0" /> + <option name="REMOVE_WATCH_INDEX" value="0" /> + <option name="UPDATE_KEYWORD_SUBSTITUTION" /> + <option name="MAKE_NEW_FILES_READONLY" value="false" /> + <option name="SHOW_CORRUPTED_PROJECT_FILES" value="0" /> + <option name="TAG_AFTER_PROJECT_COMMIT" value="false" /> + <option name="OVERRIDE_EXISTING_TAG_FOR_PROJECT" value="true" /> + <option name="TAG_AFTER_PROJECT_COMMIT_NAME" value="" /> + <option name="CLEAN_COPY" value="false" /> + </component> + <component name="DependenciesAnalyzeManager"> + <option name="myForwardDirection" value="false" /> + </component> + <component name="DependencyValidationManager"> + <option name="SKIP_IMPORT_STATEMENTS" value="false" /> + </component> + <component name="EclipseCompilerSettings"> + <option name="DEBUGGING_INFO" value="true" /> + <option name="GENERATE_NO_WARNINGS" value="true" /> + <option name="DEPRECATION" value="false" /> + <option name="ADDITIONAL_OPTIONS_STRING" value="" /> + <option name="MAXIMUM_HEAP_SIZE" value="128" /> + </component> + <component name="EclipseEmbeddedCompilerSettings"> + <option name="DEBUGGING_INFO" value="true" /> + <option name="GENERATE_NO_WARNINGS" value="true" /> + <option name="DEPRECATION" value="false" /> + <option name="ADDITIONAL_OPTIONS_STRING" value="" /> + <option name="MAXIMUM_HEAP_SIZE" value="128" /> + </component> + <component name="EntryPointsManager"> + <entry_points version="2.0" /> + </component> + <component name="ExportToHTMLSettings"> + <option name="PRINT_LINE_NUMBERS" value="false" /> + <option name="OPEN_IN_BROWSER" value="false" /> + <option name="OUTPUT_DIRECTORY" /> + </component> + <component name="IdProvider" IDEtalkID="D171F99B9178C1675593DC9A76A5CC7E" /> + <component name="InspectionProjectProfileManager"> + <option name="PROJECT_PROFILE" value="Project Default" /> + <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" /> + <scopes /> + <profiles> + <profile version="1.0" is_locked="false"> + <option name="myName" value="Project Default" /> + <option name="myLocal" value="false" /> + <inspection_tool class="JavaDoc" level="WARNING" enabled="false"> + <option name="TOP_LEVEL_CLASS_OPTIONS"> + <value> + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> + <option name="REQUIRED_TAGS" value="" /> + </value> + </option> + <option name="INNER_CLASS_OPTIONS"> + <value> + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> + <option name="REQUIRED_TAGS" value="" /> + </value> + </option> + <option name="METHOD_OPTIONS"> + <value> + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> + <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" /> + </value> + </option> + <option name="FIELD_OPTIONS"> + <value> + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> + <option name="REQUIRED_TAGS" value="" /> + </value> + </option> + <option name="IGNORE_DEPRECATED" value="false" /> + <option name="IGNORE_JAVADOC_PERIOD" value="true" /> + <option name="myAdditionalJavadocTags" value="" /> + </inspection_tool> + <inspection_tool class="OnDemandImport" level="WARNING" enabled="true" /> + <inspection_tool class="SamePackageImport" level="WARNING" enabled="true" /> + <inspection_tool class="JavaLangImport" level="WARNING" enabled="true" /> + <inspection_tool class="RedundantImport" level="WARNING" enabled="true" /> + <inspection_tool class="UnusedImport" level="WARNING" enabled="true" /> + </profile> + </profiles> + <list size="0" /> + </component> + <component name="JavacSettings"> + <option name="DEBUGGING_INFO" value="true" /> + <option name="GENERATE_NO_WARNINGS" value="false" /> + <option name="DEPRECATION" value="true" /> + <option name="ADDITIONAL_OPTIONS_STRING" value="" /> + <option name="MAXIMUM_HEAP_SIZE" value="128" /> + </component> + <component name="JavadocGenerationManager"> + <option name="OUTPUT_DIRECTORY" /> + <option name="OPTION_SCOPE" value="protected" /> + <option name="OPTION_HIERARCHY" value="true" /> + <option name="OPTION_NAVIGATOR" value="true" /> + <option name="OPTION_INDEX" value="true" /> + <option name="OPTION_SEPARATE_INDEX" value="true" /> + <option name="OPTION_DOCUMENT_TAG_USE" value="false" /> + <option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false" /> + <option name="OPTION_DOCUMENT_TAG_VERSION" value="false" /> + <option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true" /> + <option name="OPTION_DEPRECATED_LIST" value="true" /> + <option name="OTHER_OPTIONS" value="" /> + <option name="HEAP_SIZE" /> + <option name="LOCALE" /> + <option name="OPEN_IN_BROWSER" value="true" /> + </component> + <component name="JikesSettings"> + <option name="JIKES_PATH" value="" /> + <option name="DEBUGGING_INFO" value="true" /> + <option name="DEPRECATION" value="true" /> + <option name="GENERATE_NO_WARNINGS" value="false" /> + <option name="IS_EMACS_ERRORS_MODE" value="true" /> + <option name="ADDITIONAL_OPTIONS_STRING" value="" /> + </component> + <component name="LogConsolePreferences"> + <option name="FILTER_ERRORS" value="false" /> + <option name="FILTER_WARNINGS" value="false" /> + <option name="FILTER_INFO" value="true" /> + <option name="CUSTOM_FILTER" /> + </component> + <component name="Palette2"> + <group name="Swing"> + <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" /> + </item> + <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" /> + </item> + <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true"> + <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" /> + <initial-values> + <property name="text" value="Button" /> + </initial-values> + </item> + <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="RadioButton" /> + </initial-values> + </item> + <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="CheckBox" /> + </initial-values> + </item> + <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="Label" /> + </initial-values> + </item> + <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> + <preferred-size width="200" height="200" /> + </default-constraints> + </item> + <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> + <preferred-size width="200" height="200" /> + </default-constraints> + </item> + <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" /> + </item> + <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1"> + <preferred-size width="-1" height="20" /> + </default-constraints> + </item> + <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" /> + </item> + <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" /> + </item> + </group> + </component> + <component name="PerforceChangeBrowserSettings"> + <option name="USE_CLIENT_FILTER" value="true" /> + <option name="CLIENT" value="" /> + </component> + <component name="ProjectFileVersion" converted="true" /> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/preload.iml" filepath="$PROJECT_DIR$/preload.iml" /> + </modules> + </component> + <component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.5" project-jdk-type="JavaSDK"> + <output url="file:///tmp/preload" /> + </component> + <component name="RmicSettings"> + <option name="IS_EANABLED" value="false" /> + <option name="DEBUGGING_INFO" value="true" /> + <option name="GENERATE_NO_WARNINGS" value="false" /> + <option name="GENERATE_IIOP_STUBS" value="false" /> + <option name="ADDITIONAL_OPTIONS_STRING" value="" /> + </component> + <component name="StarteamConfiguration"> + <option name="SERVER" value="" /> + <option name="PORT" value="49201" /> + <option name="USER" value="" /> + <option name="PASSWORD" value="" /> + <option name="PROJECT" value="" /> + <option name="VIEW" value="" /> + <option name="ALTERNATIVE_WORKING_PATH" value="" /> + <option name="LOCK_ON_CHECKOUT" value="false" /> + <option name="UNLOCK_ON_CHECKIN" value="false" /> + </component> + <component name="Struts Assistant"> + <option name="showInputs" value="true" /> + <option name="resources"> + <value> + <option name="strutsPath" /> + <option name="strutsHelp" /> + </value> + </option> + <option name="selectedTaglibs" /> + <option name="selectedTaglibs" /> + <option name="myStrutsValidationEnabled" value="true" /> + <option name="myTilesValidationEnabled" value="true" /> + <option name="myValidatorValidationEnabled" value="true" /> + <option name="myReportErrorsAsWarnings" value="true" /> + </component> + <component name="SvnChangesBrowserSettings"> + <option name="USE_AUTHOR_FIELD" value="true" /> + <option name="AUTHOR" value="" /> + <option name="LOCATION" value="" /> + <option name="USE_PROJECT_SETTINGS" value="true" /> + <option name="USE_ALTERNATE_LOCATION" value="false" /> + </component> + <component name="SvnConfiguration"> + <option name="USER" value="" /> + <option name="PASSWORD" value="" /> + <option name="PROCESS_UNRESOLVED" value="false" /> + <option name="LAST_MERGED_REVISION" /> + <option name="UPDATE_RUN_STATUS" value="false" /> + <option name="UPDATE_RECURSIVELY" value="true" /> + <option name="MERGE_DRY_RUN" value="false" /> + </component> + <component name="VCS.FileViewConfiguration"> + <option name="SELECTED_STATUSES" value="DEFAULT" /> + <option name="SELECTED_COLUMNS" value="DEFAULT" /> + <option name="SHOW_FILTERS" value="true" /> + <option name="CUSTOMIZE_VIEW" value="true" /> + <option name="SHOW_FILE_HISTORY_AS_TREE" value="true" /> + </component> + <component name="VcsDirectoryMappings"> + <mapping directory="" vcs="Perforce" /> + </component> + <component name="VssConfiguration"> + <option name="CLIENT_PATH" value="" /> + <option name="SRCSAFEINI_PATH" value="" /> + <option name="USER_NAME" value="" /> + <option name="PWD" value="" /> + <option name="VSS_IS_INITIALIZED" value="false" /> + <CheckoutOptions> + <option name="COMMENT" value="" /> + <option name="DO_NOT_GET_LATEST_VERSION" value="false" /> + <option name="REPLACE_WRITABLE" value="false" /> + <option name="RECURSIVE" value="false" /> + </CheckoutOptions> + <CheckinOptions> + <option name="COMMENT" value="" /> + <option name="KEEP_CHECKED_OUT" value="false" /> + <option name="RECURSIVE" value="false" /> + </CheckinOptions> + <AddOptions> + <option name="STORE_ONLY_LATEST_VERSION" value="false" /> + <option name="CHECK_OUT_IMMEDIATELY" value="false" /> + <option name="FILE_TYPE" value="0" /> + </AddOptions> + <UndocheckoutOptions> + <option name="MAKE_WRITABLE" value="false" /> + <option name="REPLACE_LOCAL_COPY" value="0" /> + <option name="RECURSIVE" value="false" /> + </UndocheckoutOptions> + <GetOptions> + <option name="REPLACE_WRITABLE" value="0" /> + <option name="MAKE_WRITABLE" value="false" /> + <option name="ANSWER_NEGATIVELY" value="false" /> + <option name="ANSWER_POSITIVELY" value="false" /> + <option name="RECURSIVE" value="false" /> + <option name="VERSION" /> + </GetOptions> + <VssConfigurableExcludedFilesTag /> + </component> + <component name="antWorkspaceConfiguration"> + <option name="IS_AUTOSCROLL_TO_SOURCE" value="false" /> + <option name="FILTER_TARGETS" value="false" /> + </component> + <component name="com.intellij.ide.util.scopeChooser.ScopeChooserConfigurable" proportions="" version="1"> + <option name="myLastEditedConfigurable" /> + </component> + <component name="com.intellij.jsf.UserDefinedFacesConfigs"> + <option name="USER_DEFINED_CONFIGS"> + <value> + <list size="0" /> + </value> + </option> + </component> + <component name="com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectRootMasterDetailsConfigurable" proportions="" version="1"> + <option name="myPlainMode" value="false" /> + <option name="myLastEditedConfigurable" /> + </component> + <component name="com.intellij.profile.ui.ErrorOptionsConfigurable" proportions="" version="1"> + <option name="myLastEditedConfigurable" /> + </component> + <component name="uidesigner-configuration"> + <option name="INSTRUMENT_CLASSES" value="true" /> + <option name="COPY_FORMS_RUNTIME_TO_OUTPUT" value="true" /> + <option name="DEFAULT_LAYOUT_MANAGER" value="GridLayoutManager" /> + </component> +</project> + |