blob: a6d418775dc7769d52c0810bdb98f75cac87ca3a [file] [log] [blame]
Bob Leefcc3ccb2009-09-23 11:21:22 -07001import java.io.File;
2import java.io.IOException;
3import java.io.PrintStream;
4import java.io.DataInputStream;
5import java.io.FileInputStream;
6import java.util.Map;
7import java.util.List;
8import java.util.HashMap;
9import java.util.ArrayList;
10import java.util.Comparator;
11import java.util.Set;
12import java.util.TreeSet;
13import java.util.Iterator;
14
15/**
16 * Prints HTML reports that can be attached to bugs.
17 */
18public 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>&nbsp;</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}