| 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); |
| } |
| } |
| } |