import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.io.DataInputStream; import java.io.FileInputStream; import java.util.Map; import java.util.List; import java.util.HashMap; import java.util.ArrayList; import java.util.Comparator; import java.util.Set; import java.util.TreeSet; import java.util.Iterator; /** * Prints HTML reports that can be attached to bugs. */ public class PrintBugReports { private static final String DIR = "out/preload"; private static boolean PRINT_MEMORY_USAGE = false; private static final Comparator DEFAULT_ORDER = new Comparator() { public int compare(LoadedClass a, LoadedClass b) { // Longest load time first. int diff = b.medianTimeMicros() - a.medianTimeMicros(); if (diff != 0) { return diff; } return a.name.compareTo(b.name); } }; public static void main(String[] args) throws IOException, ClassNotFoundException { Root root = Root.fromFile(args[0]); String baseUrl = ""; if (args.length > 1) { baseUrl = args[1]; } new File(DIR).mkdirs(); Map> procsByName = new HashMap>(); for (Proc proc : root.processes.values()) { if (proc.fromZygote()) { List procs = procsByName.get(proc.name); if (procs == null) { procs = new ArrayList(); procsByName.put(proc.name, procs); } procs.add(proc); } } Set coreClasses = new TreeSet(DEFAULT_ORDER); Set frameworkClasses = new TreeSet(DEFAULT_ORDER); for (List procs : procsByName.values()) { Proc first = procs.get(0); Set classes = new TreeSet(DEFAULT_ORDER); Set sharedClasses = new TreeSet(DEFAULT_ORDER); for (Proc proc : procs) { for (Operation operation : proc.operations) { LoadedClass clazz = operation.loadedClass; if (clazz.isSharable() && clazz.systemClass) { if (clazz.name.startsWith("dalvik") || clazz.name.startsWith("org") || clazz.name.startsWith("java")) { coreClasses.add(clazz); } else { frameworkClasses.add(clazz); } sharedClasses.add(clazz); } else { classes.add(clazz); } } } printApplicationHtml(first.name, root.baseline, classes, sharedClasses); } printHtml("core", root.baseline, coreClasses); printHtml("framework", root.baseline, frameworkClasses); PrintStream out = new PrintStream(DIR + "/toc.html"); out.println(""); out.println("core
"); out.println("framework
"); for (String s : new TreeSet(procsByName.keySet())) { out.println("" + s + "
"); } out.println(""); out.close(); } static void printApplicationHtml(String name, MemoryUsage baseline, Iterable classes, Iterable sharedClasses) throws IOException { PrintStream out = new PrintStream(DIR + "/" + name + ".html"); printHeader(name, out); out.println(""); out.println("

" + name + "

"); out.println("

Click a column header to sort by that column.

"); out.println("

Shared Classes

"); out.println("

Application-Specific Classes

"); out.println("

These classes were loaded only by " + name + ". If" + " the value of the Preloaded column is yes or " + " no, the class is in the boot classpath; if it's not" + " part of the published API, consider" + " moving it into the APK.

"); printTable(out, baseline, classes, false); out.println("

Top

"); out.println("

Shared Classes

"); out.println("

These classes are in the boot classpath. They are used" + " by " + name + " as well as others."); printTable(out, baseline, sharedClasses, true); out.println(""); out.close(); } static void printHtml(String name, MemoryUsage baseline, Iterable classes) throws IOException { PrintStream out = new PrintStream(DIR + "/" + name + ".html"); printHeader(name, out); out.println(""); out.println("

" + name + "

"); out.println("

Click a column header to sort by that column.

"); printTable(out, baseline, classes, true); out.println(""); out.close(); } private static void printHeader(String name, PrintStream out) throws IOException { out.println(""); out.println("" + name + ""); out.println(""); out.println(""); out.println(""); } static void printTable(PrintStream out, MemoryUsage baseline, Iterable classes, boolean showProcNames) { out.println("

"); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); if (PRINT_MEMORY_USAGE) { out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); } if (showProcNames) { out.println(""); } out.println(""); for (LoadedClass clazz : classes) { out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); if (PRINT_MEMORY_USAGE) { if (clazz.memoryUsage.isAvailable()) { MemoryUsage subtracted = clazz.memoryUsage.subtract(baseline); long totalHeap = subtracted.javaHeapSize() + subtracted.nativeHeapSize; out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); } else { for (int i = 0; i < 7; i++) { out.println(""); } } } if (showProcNames) { out.println(""); } out.println(""); } out.println("
NamePreloadedTotal Time (us)Load Time (us)Init Time (us)Total Heap (B)Dalvik Heap (B)Native Heap (B)Total Pages (kB)Dalvik Pages (kB)Native Pages (kB)Other Pages (kB)Loaded by
" + clazz.name + "" + ((clazz.systemClass) ? ((clazz.preloaded) ? "yes" : "no") : "n/a") + "" + clazz.medianTimeMicros() + "" + clazz.medianLoadTimeMicros() + "" + clazz.medianInitTimeMicros() + "" + totalHeap + "" + subtracted.javaHeapSize() + "" + subtracted.nativeHeapSize + "" + subtracted.totalPages() + "" + subtracted.javaPagesInK() + "" + subtracted.nativePagesInK() + "" + subtracted.otherPagesInK() + " "); Set procNames = new TreeSet(); for (Operation op : clazz.loads) { procNames.add(op.process.name); } for (Operation op : clazz.initializations) { procNames.add(op.process.name); } if (procNames.size() <= 3) { for (String name : procNames) { out.print(name + "
"); } } else { Iterator i = procNames.iterator(); out.print(i.next() + "
"); out.print(i.next() + "
"); out.print("...and " + (procNames.size() - 2) + " others."); } out.println("

"); } static byte[] SCRIPT; static { try { File script = new File( "frameworks/base/tools/preload/sorttable.js"); int length = (int) script.length(); SCRIPT = new byte[length]; DataInputStream in = new DataInputStream( new FileInputStream(script)); in.readFully(SCRIPT); in.close(); } catch (IOException e) { throw new RuntimeException(e); } } }