Bob Lee | fcc3ccb | 2009-09-23 11:21:22 -0700 | [diff] [blame] | 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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); |
| 267 | in.close(); |
| 268 | } catch (IOException e) { |
| 269 | throw new RuntimeException(e); |
| 270 | } |
| 271 | } |
| 272 | } |