| 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<LoadedClass> DEFAULT_ORDER | 
 |             = new Comparator<LoadedClass>() { | 
 |         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<String, List<Proc>> procsByName = new HashMap<String, List<Proc>>(); | 
 |         for (Proc proc : root.processes.values()) { | 
 |             if (proc.fromZygote()) { | 
 |                 List<Proc> procs = procsByName.get(proc.name); | 
 |                 if (procs == null) { | 
 |                     procs = new ArrayList<Proc>(); | 
 |                     procsByName.put(proc.name, procs); | 
 |                 } | 
 |                 procs.add(proc); | 
 |             } | 
 |         } | 
 |  | 
 |         Set<LoadedClass> coreClasses = new TreeSet<LoadedClass>(DEFAULT_ORDER); | 
 |         Set<LoadedClass> frameworkClasses = new TreeSet<LoadedClass>(DEFAULT_ORDER); | 
 |  | 
 |         for (List<Proc> procs : procsByName.values()) { | 
 |             Proc first = procs.get(0); | 
 |             Set<LoadedClass> classes = new TreeSet<LoadedClass>(DEFAULT_ORDER); | 
 |             Set<LoadedClass> sharedClasses | 
 |                     = new TreeSet<LoadedClass>(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("<html><body>"); | 
 |         out.println("<a href='" + baseUrl | 
 |                 + "/core.html'>core</a><br/>"); | 
 |         out.println("<a href='" + baseUrl | 
 |                 + "/framework.html'>framework</a><br/>"); | 
 |  | 
 |         for (String s : new TreeSet<String>(procsByName.keySet())) { | 
 |             out.println("<a href='" + baseUrl + "/" | 
 |                     + s + ".html'>" + s + "</a><br/>"); | 
 |         } | 
 |         out.println("</body></html>"); | 
 |         out.close(); | 
 |     } | 
 |  | 
 |     static void printApplicationHtml(String name, MemoryUsage baseline, | 
 |             Iterable<LoadedClass> classes, Iterable<LoadedClass> sharedClasses) | 
 |             throws IOException { | 
 |         PrintStream out = new PrintStream(DIR + "/" + name + ".html"); | 
 |  | 
 |         printHeader(name, out); | 
 |         out.println("<body>"); | 
 |         out.println("<h1><tt>" + name + "</tt></h1>"); | 
 |         out.println("<p><i>Click a column header to sort by that column.</i></p>"); | 
 |  | 
 |         out.println("<p><a href=\"#shared\">Shared Classes</a></p>"); | 
 |  | 
 |         out.println("<h3>Application-Specific Classes</h3>"); | 
 |  | 
 |         out.println("<p>These classes were loaded only by " + name + ". If" | 
 |                 + " the value of the <i>Preloaded</i> column is <i>yes</i> or " | 
 |                 + " <i>no</i>, the class is in the boot classpath; if it's not" | 
 |                 + " part of the published API, consider" | 
 |                 + " moving it into the APK.</p>"); | 
 |  | 
 |         printTable(out, baseline, classes, false); | 
 |  | 
 |         out.println("<p><a href=\"#\">Top</a></p>"); | 
 |  | 
 |         out.println("<a name=\"shared\"/><h3>Shared Classes</h3>"); | 
 |  | 
 |         out.println("<p>These classes are in the boot classpath. They are used" | 
 |                 + " by " + name + " as well as others."); | 
 |  | 
 |         printTable(out, baseline, sharedClasses, true); | 
 |  | 
 |         out.println("</body></html>"); | 
 |         out.close(); | 
 |     } | 
 |  | 
 |     static void printHtml(String name, MemoryUsage baseline, | 
 |             Iterable<LoadedClass> classes) | 
 |             throws IOException { | 
 |         PrintStream out = new PrintStream(DIR + "/" + name + ".html"); | 
 |  | 
 |         printHeader(name, out); | 
 |         out.println("<body>"); | 
 |         out.println("<h1><tt>" + name + "</tt></h1>"); | 
 |         out.println("<p><i>Click a column header to sort by that column.</i></p>"); | 
 |  | 
 |         printTable(out, baseline, classes, true); | 
 |  | 
 |         out.println("</body></html>"); | 
 |         out.close(); | 
 |     } | 
 |  | 
 |     private static void printHeader(String name, PrintStream out) | 
 |             throws IOException { | 
 |         out.println("<html><head>"); | 
 |         out.println("<title>" + name + "</title>"); | 
 |         out.println("<style>"); | 
 |         out.println("a, th, td, h1, h3, p { font-family: arial }"); | 
 |         out.println("th, td { font-size: small }"); | 
 |         out.println("</style>"); | 
 |         out.println("<script language=\"javascript\">"); | 
 |         out.write(SCRIPT); | 
 |         out.println("</script>"); | 
 |         out.println("</head>"); | 
 |     } | 
 |  | 
 |     static void printTable(PrintStream out, MemoryUsage baseline, | 
 |             Iterable<LoadedClass> classes, boolean showProcNames) { | 
 |         out.println("<p><table border=\"1\" cellpadding=\"5\"" | 
 |                 + " class=\"sortable\" cellspacing=\"0\">"); | 
 |  | 
 |         out.println("<thead bgcolor=\"#eeeeee\"><tr>"); | 
 |         out.println("<th>Name</th>"); | 
 |         out.println("<th>Preloaded</th>"); | 
 |         out.println("<th>Total Time (us)</th>"); | 
 |         out.println("<th>Load Time (us)</th>"); | 
 |         out.println("<th>Init Time (us)</th>"); | 
 |         if (PRINT_MEMORY_USAGE) { | 
 |             out.println("<th>Total Heap (B)</th>"); | 
 |             out.println("<th>Dalvik Heap (B)</th>"); | 
 |             out.println("<th>Native Heap (B)</th>"); | 
 |             out.println("<th>Total Pages (kB)</th>"); | 
 |             out.println("<th>Dalvik Pages (kB)</th>"); | 
 |             out.println("<th>Native Pages (kB)</th>"); | 
 |             out.println("<th>Other Pages (kB)</th>"); | 
 |         } | 
 |         if (showProcNames) { | 
 |             out.println("<th>Loaded by</th>"); | 
 |         } | 
 |         out.println("</tr></thead>"); | 
 |  | 
 |         for (LoadedClass clazz : classes) { | 
 |             out.println("<tr>"); | 
 |             out.println("<td>" + clazz.name + "</td>"); | 
 |  | 
 |             out.println("<td>" + ((clazz.systemClass) | 
 |                     ? ((clazz.preloaded) ? "yes" : "no") : "n/a") + "</td>"); | 
 |  | 
 |             out.println("<td>" + clazz.medianTimeMicros() + "</td>"); | 
 |             out.println("<td>" + clazz.medianLoadTimeMicros() + "</td>"); | 
 |             out.println("<td>" + clazz.medianInitTimeMicros() + "</td>"); | 
 |  | 
 |             if (PRINT_MEMORY_USAGE) { | 
 |                 if (clazz.memoryUsage.isAvailable()) { | 
 |                     MemoryUsage subtracted | 
 |                             = clazz.memoryUsage.subtract(baseline); | 
 |  | 
 |                     long totalHeap = subtracted.javaHeapSize() | 
 |                             + subtracted.nativeHeapSize; | 
 |                     out.println("<td>" + totalHeap + "</td>"); | 
 |                     out.println("<td>" + subtracted.javaHeapSize() + "</td>"); | 
 |                     out.println("<td>" + subtracted.nativeHeapSize + "</td>"); | 
 |  | 
 |                     out.println("<td>" + subtracted.totalPages() + "</td>"); | 
 |                     out.println("<td>" + subtracted.javaPagesInK() + "</td>"); | 
 |                     out.println("<td>" + subtracted.nativePagesInK() + "</td>"); | 
 |                     out.println("<td>" + subtracted.otherPagesInK() + "</td>"); | 
 |                 } else { | 
 |                     for (int i = 0; i < 7; i++) { | 
 |                         out.println("<td> </td>"); | 
 |                     } | 
 |                 } | 
 |             } | 
 |  | 
 |             if (showProcNames) { | 
 |                 out.println("<td>"); | 
 |                 Set<String> procNames = new TreeSet<String>(); | 
 |                 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 + "<br/>"); | 
 |                     } | 
 |                 } else { | 
 |                     Iterator<String> i = procNames.iterator(); | 
 |                     out.print(i.next() + "<br/>"); | 
 |                     out.print(i.next() + "<br/>"); | 
 |                     out.print("...and " + (procNames.size() - 2) | 
 |                             + " others."); | 
 |                 } | 
 |                 out.println("</td>"); | 
 |             } | 
 |  | 
 |             out.println("</tr>"); | 
 |         } | 
 |  | 
 |         out.println("</table></p>"); | 
 |     } | 
 |  | 
 |     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); | 
 |         } | 
 |     } | 
 | } |