/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.io.IOException; import java.io.FileReader; import java.io.BufferedReader; import java.io.PrintStream; import java.util.Set; import java.util.TreeSet; import java.util.HashSet; import java.util.Iterator; /** * Prints HTML containing removed and added files. */ public class PrintHtmlDiff { private static final String OLD_PRELOADED_CLASSES = "old-preloaded-classes"; public static void main(String[] args) throws IOException, ClassNotFoundException { Root root = Root.fromFile(args[0]); BufferedReader oldClasses = new BufferedReader( new FileReader(OLD_PRELOADED_CLASSES)); // Classes loaded implicitly by the zygote. Set<LoadedClass> zygote = new HashSet<LoadedClass>(); for (Proc proc : root.processes.values()) { if (proc.name.equals("zygote")) { for (Operation op : proc.operations) { zygote.add(op.loadedClass); } break; } } Set<LoadedClass> removed = new TreeSet<LoadedClass>(); Set<LoadedClass> added = new TreeSet<LoadedClass>(); for (LoadedClass loadedClass : root.loadedClasses.values()) { if (loadedClass.preloaded && !zygote.contains(loadedClass)) { added.add(loadedClass); } } String line; while ((line = oldClasses.readLine()) != null) { line = line.trim(); LoadedClass clazz = root.loadedClasses.get(line); if (clazz != null) { added.remove(clazz); if (!clazz.preloaded) removed.add(clazz); } } PrintStream out = System.out; out.println("<html><body>"); out.println("<style>"); out.println("a, th, td, h2 { font-family: arial }"); out.println("th, td { font-size: small }"); out.println("</style>"); out.println("<script src=\"sorttable.js\"></script>"); out.println("<p><a href=\"#removed\">Removed</a>"); out.println("<a name=\"added\"/><h2>Added</h2>"); printTable(out, root.baseline, added); out.println("<a name=\"removed\"/><h2>Removed</h2>"); printTable(out, root.baseline, removed); out.println("</body></html>"); } static void printTable(PrintStream out, MemoryUsage baseline, Iterable<LoadedClass> classes) { out.println("<table border=\"1\" cellpadding=\"5\"" + " class=\"sortable\">"); out.println("<thead><tr>"); out.println("<th>Name</th>"); out.println("<th>Load Time (us)</th>"); out.println("<th>Loaded By</th>"); out.println("<th>Heap (B)</th>"); out.println("<th>Pages</th>"); out.println("</tr></thead>"); for (LoadedClass clazz : classes) { out.println("<tr>"); out.println("<td>" + clazz.name + "</td>"); out.println("<td>" + clazz.medianTimeMicros() + "</td>"); 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>"); if (clazz.memoryUsage.isAvailable()) { MemoryUsage subtracted = clazz.memoryUsage.subtract(baseline); out.println("<td>" + (subtracted.javaHeapSize() + subtracted.nativeHeapSize) + "</td>"); out.println("<td>" + subtracted.totalPages() + "</td>"); } else { for (int i = 0; i < 2; i++) { out.println("<td>n/a</td>"); } } out.println("</tr>"); } out.println("</table>"); } }