Added tool to generate application-specific reports from class load profiling data. Generated new profiling data. Deleted old data. Generated new preloaded-classes file.
This commit is contained in:
1613
preloaded-classes
1613
preloaded-classes
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@@ -8,6 +8,7 @@ LOCAL_SRC_FILES := \
|
||||
MemoryUsage.java \
|
||||
Operation.java \
|
||||
Policy.java \
|
||||
PrintBugReports.java \
|
||||
PrintCsv.java \
|
||||
PrintHtmlDiff.java \
|
||||
PrintPsTree.java \
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
*/
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A loaded class.
|
||||
@@ -50,6 +54,30 @@ class LoadedClass implements Serializable, Comparable<LoadedClass> {
|
||||
this.systemClass = systemClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this class was loaded by more than one proc.
|
||||
*/
|
||||
boolean isSharable() {
|
||||
Set<String> procNames = new HashSet<String>();
|
||||
for (Operation load : loads) {
|
||||
if (load.process.fromZygote()) {
|
||||
procNames.add(load.process.name);
|
||||
if (procNames.size() > 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Operation init : initializations) {
|
||||
if (init.process.fromZygote()) {
|
||||
procNames.add(init.process.name);
|
||||
if (procNames.size() > 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void measureMemoryUsage() {
|
||||
this.memoryUsage = MemoryUsage.forClass(name);
|
||||
}
|
||||
|
||||
272
tools/preload/PrintBugReports.java
Normal file
272
tools/preload/PrintBugReports.java
Normal file
@@ -0,0 +1,272 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,8 @@ public class Root implements Serializable {
|
||||
final Map<String, LoadedClass> loadedClasses
|
||||
= new HashMap<String, LoadedClass>();
|
||||
|
||||
MemoryUsage baseline = MemoryUsage.baseline();
|
||||
// MemoryUsage baseline = MemoryUsage.baseline();
|
||||
MemoryUsage baseline = MemoryUsage.NOT_AVAILABLE;
|
||||
|
||||
/**
|
||||
* Records class loads and initializations.
|
||||
@@ -73,7 +74,7 @@ public class Root implements Serializable {
|
||||
if (loadedClass.systemClass) {
|
||||
// Only measure memory for classes in the boot
|
||||
// classpath.
|
||||
loadedClass.measureMemoryUsage();
|
||||
// loadedClass.measureMemoryUsage();
|
||||
}
|
||||
loadedClasses.put(name, loadedClass);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public class WritePreloadedClassFile {
|
||||
/**
|
||||
* Preload any class that take longer to load than MIN_LOAD_TIME_MICROS us.
|
||||
*/
|
||||
static final int MIN_LOAD_TIME_MICROS = 1250;
|
||||
static final int MIN_LOAD_TIME_MICROS = 1000;
|
||||
|
||||
public static void main(String[] args) throws IOException,
|
||||
ClassNotFoundException {
|
||||
|
||||
@@ -364,7 +364,7 @@
|
||||
</component>
|
||||
<component name="ProjectFileVersion" converted="true" />
|
||||
<component name="ProjectKey">
|
||||
<option name="state" value="project:///Volumes/Android/donut/frameworks/base/tools/preload/preload.ipr" />
|
||||
<option name="state" value="project:///Volumes/Android/eclair/frameworks/base/tools/preload/preload.ipr" />
|
||||
</component>
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
Instructions:
|
||||
Download this file
|
||||
Add <script src="sorttable.js"></script> to your HTML
|
||||
Add <script src="sorttable.js"> to your HTML
|
||||
Add class="sortable" to any table you'd like to make sortable
|
||||
Click on the headers to sort
|
||||
|
||||
@@ -88,6 +88,7 @@ sorttable = {
|
||||
}
|
||||
// make it clickable to sort
|
||||
headrow[i].sorttable_columnindex = i;
|
||||
headrow[i].style.cursor = "pointer";
|
||||
headrow[i].sorttable_tbody = table.tBodies[0];
|
||||
dean_addEvent(headrow[i],"click", function(e) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user