1 import java.io.File; 2 import java.io.IOException; 3 import java.io.PrintStream; 4 import java.io.DataInputStream; 5 import java.io.FileInputStream; 6 import java.util.Map; 7 import java.util.List; 8 import java.util.HashMap; 9 import java.util.ArrayList; 10 import java.util.Comparator; 11 import java.util.Set; 12 import java.util.TreeSet; 13 import java.util.Iterator; 14 15 /** 16 * Prints HTML reports that can be attached to bugs. 17 */ 18 public class PrintBugReports { 19 20 private static final String DIR = "out/preload"; 21 private static boolean PRINT_MEMORY_USAGE = false; 22 23 private static final Comparator<LoadedClass> DEFAULT_ORDER 24 = new Comparator<LoadedClass>() { 25 public int compare(LoadedClass a, LoadedClass b) { 26 // Longest load time first. 27 int diff = b.medianTimeMicros() - a.medianTimeMicros(); 28 if (diff != 0) { 29 return diff; 30 } 31 32 return a.name.compareTo(b.name); 33 } 34 }; 35 main(String[] args)36 public static void main(String[] args) 37 throws IOException, ClassNotFoundException { 38 Root root = Root.fromFile(args[0]); 39 String baseUrl = ""; 40 if (args.length > 1) { 41 baseUrl = args[1]; 42 } 43 44 new File(DIR).mkdirs(); 45 46 Map<String, List<Proc>> procsByName = new HashMap<String, List<Proc>>(); 47 for (Proc proc : root.processes.values()) { 48 if (proc.fromZygote()) { 49 List<Proc> procs = procsByName.get(proc.name); 50 if (procs == null) { 51 procs = new ArrayList<Proc>(); 52 procsByName.put(proc.name, procs); 53 } 54 procs.add(proc); 55 } 56 } 57 58 Set<LoadedClass> coreClasses = new TreeSet<LoadedClass>(DEFAULT_ORDER); 59 Set<LoadedClass> frameworkClasses = new TreeSet<LoadedClass>(DEFAULT_ORDER); 60 61 for (List<Proc> procs : procsByName.values()) { 62 Proc first = procs.get(0); 63 Set<LoadedClass> classes = new TreeSet<LoadedClass>(DEFAULT_ORDER); 64 Set<LoadedClass> sharedClasses 65 = new TreeSet<LoadedClass>(DEFAULT_ORDER); 66 for (Proc proc : procs) { 67 for (Operation operation : proc.operations) { 68 LoadedClass clazz = operation.loadedClass; 69 if (clazz.isSharable() && clazz.systemClass) { 70 if (clazz.name.startsWith("dalvik") 71 || clazz.name.startsWith("org") 72 || clazz.name.startsWith("java")) { 73 coreClasses.add(clazz); 74 } else { 75 frameworkClasses.add(clazz); 76 } 77 sharedClasses.add(clazz); 78 } else { 79 classes.add(clazz); 80 } 81 } 82 } 83 printApplicationHtml(first.name, root.baseline, classes, 84 sharedClasses); 85 } 86 87 printHtml("core", root.baseline, coreClasses); 88 printHtml("framework", root.baseline, frameworkClasses); 89 90 PrintStream out = new PrintStream(DIR + "/toc.html"); 91 out.println("<html><body>"); 92 out.println("<a href='" + baseUrl 93 + "/core.html'>core</a><br/>"); 94 out.println("<a href='" + baseUrl 95 + "/framework.html'>framework</a><br/>"); 96 97 for (String s : new TreeSet<String>(procsByName.keySet())) { 98 out.println("<a href='" + baseUrl + "/" 99 + s + ".html'>" + s + "</a><br/>"); 100 } 101 out.println("</body></html>"); 102 out.close(); 103 } 104 printApplicationHtml(String name, MemoryUsage baseline, Iterable<LoadedClass> classes, Iterable<LoadedClass> sharedClasses)105 static void printApplicationHtml(String name, MemoryUsage baseline, 106 Iterable<LoadedClass> classes, Iterable<LoadedClass> sharedClasses) 107 throws IOException { 108 PrintStream out = new PrintStream(DIR + "/" + name + ".html"); 109 110 printHeader(name, out); 111 out.println("<body>"); 112 out.println("<h1><tt>" + name + "</tt></h1>"); 113 out.println("<p><i>Click a column header to sort by that column.</i></p>"); 114 115 out.println("<p><a href=\"#shared\">Shared Classes</a></p>"); 116 117 out.println("<h3>Application-Specific Classes</h3>"); 118 119 out.println("<p>These classes were loaded only by " + name + ". If" 120 + " the value of the <i>Preloaded</i> column is <i>yes</i> or " 121 + " <i>no</i>, the class is in the boot classpath; if it's not" 122 + " part of the published API, consider" 123 + " moving it into the APK.</p>"); 124 125 printTable(out, baseline, classes, false); 126 127 out.println("<p><a href=\"#\">Top</a></p>"); 128 129 out.println("<a name=\"shared\"/><h3>Shared Classes</h3>"); 130 131 out.println("<p>These classes are in the boot classpath. They are used" 132 + " by " + name + " as well as others."); 133 134 printTable(out, baseline, sharedClasses, true); 135 136 out.println("</body></html>"); 137 out.close(); 138 } 139 printHtml(String name, MemoryUsage baseline, Iterable<LoadedClass> classes)140 static void printHtml(String name, MemoryUsage baseline, 141 Iterable<LoadedClass> classes) 142 throws IOException { 143 PrintStream out = new PrintStream(DIR + "/" + name + ".html"); 144 145 printHeader(name, out); 146 out.println("<body>"); 147 out.println("<h1><tt>" + name + "</tt></h1>"); 148 out.println("<p><i>Click a column header to sort by that column.</i></p>"); 149 150 printTable(out, baseline, classes, true); 151 152 out.println("</body></html>"); 153 out.close(); 154 } 155 printHeader(String name, PrintStream out)156 private static void printHeader(String name, PrintStream out) 157 throws IOException { 158 out.println("<html><head>"); 159 out.println("<title>" + name + "</title>"); 160 out.println("<style>"); 161 out.println("a, th, td, h1, h3, p { font-family: arial }"); 162 out.println("th, td { font-size: small }"); 163 out.println("</style>"); 164 out.println("<script language=\"javascript\">"); 165 out.write(SCRIPT); 166 out.println("</script>"); 167 out.println("</head>"); 168 } 169 printTable(PrintStream out, MemoryUsage baseline, Iterable<LoadedClass> classes, boolean showProcNames)170 static void printTable(PrintStream out, MemoryUsage baseline, 171 Iterable<LoadedClass> classes, boolean showProcNames) { 172 out.println("<p><table border=\"1\" cellpadding=\"5\"" 173 + " class=\"sortable\" cellspacing=\"0\">"); 174 175 out.println("<thead bgcolor=\"#eeeeee\"><tr>"); 176 out.println("<th>Name</th>"); 177 out.println("<th>Preloaded</th>"); 178 out.println("<th>Total Time (us)</th>"); 179 out.println("<th>Load Time (us)</th>"); 180 out.println("<th>Init Time (us)</th>"); 181 if (PRINT_MEMORY_USAGE) { 182 out.println("<th>Total Heap (B)</th>"); 183 out.println("<th>Dalvik Heap (B)</th>"); 184 out.println("<th>Native Heap (B)</th>"); 185 out.println("<th>Total Pages (kB)</th>"); 186 out.println("<th>Dalvik Pages (kB)</th>"); 187 out.println("<th>Native Pages (kB)</th>"); 188 out.println("<th>Other Pages (kB)</th>"); 189 } 190 if (showProcNames) { 191 out.println("<th>Loaded by</th>"); 192 } 193 out.println("</tr></thead>"); 194 195 for (LoadedClass clazz : classes) { 196 out.println("<tr>"); 197 out.println("<td>" + clazz.name + "</td>"); 198 199 out.println("<td>" + ((clazz.systemClass) 200 ? ((clazz.preloaded) ? "yes" : "no") : "n/a") + "</td>"); 201 202 out.println("<td>" + clazz.medianTimeMicros() + "</td>"); 203 out.println("<td>" + clazz.medianLoadTimeMicros() + "</td>"); 204 out.println("<td>" + clazz.medianInitTimeMicros() + "</td>"); 205 206 if (PRINT_MEMORY_USAGE) { 207 if (clazz.memoryUsage.isAvailable()) { 208 MemoryUsage subtracted 209 = clazz.memoryUsage.subtract(baseline); 210 211 long totalHeap = subtracted.javaHeapSize() 212 + subtracted.nativeHeapSize; 213 out.println("<td>" + totalHeap + "</td>"); 214 out.println("<td>" + subtracted.javaHeapSize() + "</td>"); 215 out.println("<td>" + subtracted.nativeHeapSize + "</td>"); 216 217 out.println("<td>" + subtracted.totalPages() + "</td>"); 218 out.println("<td>" + subtracted.javaPagesInK() + "</td>"); 219 out.println("<td>" + subtracted.nativePagesInK() + "</td>"); 220 out.println("<td>" + subtracted.otherPagesInK() + "</td>"); 221 } else { 222 for (int i = 0; i < 7; i++) { 223 out.println("<td> </td>"); 224 } 225 } 226 } 227 228 if (showProcNames) { 229 out.println("<td>"); 230 Set<String> procNames = new TreeSet<String>(); 231 for (Operation op : clazz.loads) { 232 procNames.add(op.process.name); 233 } 234 for (Operation op : clazz.initializations) { 235 procNames.add(op.process.name); 236 } 237 if (procNames.size() <= 3) { 238 for (String name : procNames) { 239 out.print(name + "<br/>"); 240 } 241 } else { 242 Iterator<String> i = procNames.iterator(); 243 out.print(i.next() + "<br/>"); 244 out.print(i.next() + "<br/>"); 245 out.print("...and " + (procNames.size() - 2) 246 + " others."); 247 } 248 out.println("</td>"); 249 } 250 251 out.println("</tr>"); 252 } 253 254 out.println("</table></p>"); 255 } 256 257 static byte[] SCRIPT; 258 static { 259 try { 260 File script = new File( 261 "frameworks/base/tools/preload/sorttable.js"); 262 int length = (int) script.length(); 263 SCRIPT = new byte[length]; 264 DataInputStream in = new DataInputStream( 265 new FileInputStream(script)); 266 in.readFully(SCRIPT); in.close()267 in.close(); 268 } catch (IOException e) { 269 throw new RuntimeException(e); 270 } 271 } 272 } 273