blob: 4a62e696e6f4b61b2a2b9c02a06aca90a1d15217 [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* 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.
*/
package com.google.gwt.soyc;
import com.google.gwt.core.ext.linker.CompilationMetricsArtifact;
import com.google.gwt.core.ext.linker.ModuleMetricsArtifact;
import com.google.gwt.core.ext.linker.PrecompilationMetricsArtifact;
import com.google.gwt.core.ext.soyc.impl.SizeMapRecorder;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.collect.Lists;
import com.google.gwt.dev.util.collect.Sets;
import com.google.gwt.soyc.io.OutputDirectory;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A utility to make all the HTML files for one permutation.
*/
public class MakeTopLevelHtmlForPerm {
/**
* A dependency linker for the initial code download. It links to the
* dependencies for the initial download.
*/
public class DependencyLinkerForInitialCode implements DependencyLinker {
@Override
public String dependencyLinkForClass(String className) {
String packageName = globalInformation.getClassToPackage().get(className);
assert packageName != null;
return dependenciesFileName("initial") + "#" + className;
}
}
/**
* A dependency linker for the leftovers fragment. It links to leftovers
* status pages.
*/
public class DependencyLinkerForLeftoversFragment implements DependencyLinker {
@Override
public String dependencyLinkForClass(String className) {
return leftoversStatusFileName() + "#"
+ hashedFilenameFragment(className);
}
}
/**
* A dependency linker for the total program breakdown. It links to a split
* status page.
*
*/
public class DependencyLinkerForTotalBreakdown implements DependencyLinker {
@Override
public String dependencyLinkForClass(String className) {
return splitStatusFileName() + "#" + hashedFilenameFragment(className);
}
}
/**
* A dependency linker that never links to anything.
*/
public static class NullDependencyLinker implements DependencyLinker {
@Override
public String dependencyLinkForClass(String className) {
return null;
}
}
interface DependencyLinker {
String dependencyLinkForClass(String className);
}
/**
* Use this class to intern strings to save space in the generated HTML. After
* populating the map, call getJs to create a JS array of all possible methods
* in this report.
*/
@SuppressWarnings("serial")
private class HtmlInterner {
// Hashes the interned string to the number of times this string is referenced.
Map<String, Integer> builder = new HashMap<String, Integer>();
// Hashes the interned string to its position in the final array of interned strings.
// Populated after the call to {@link #freeze()}
Map<String, Integer> frozen = null;
/**
* Call this method after all calls to {@link #intern(String)} are complete.
* This routine then re-orders the interned calls in order of the number of
* times each string was referenced by intern() so that lower numbered index
* values represent more frequently referenced strings.
*/
public void freeze() {
final int maxDigits = 9;
assert (frozen == null);
assert (builder.size() < Math.pow(10, maxDigits));
// order the interned values with the most referenced first.
String[] temp = new String[builder.size()];
int index = 0;
for (String key : builder.keySet()) {
temp[index++] = key.format("%0" + maxDigits + "d%s", builder.get(key), key);
}
builder = null;
Arrays.sort(temp);
// strip off the numeric prefix on the key to build the frozen hash table
index = 0;
frozen = new LinkedHashMap<String, Integer>();
for (int i = temp.length - 1; i >= 0; i--) {
frozen.put(temp[i].substring(maxDigits), index++);
}
}
/**
* Stores a string for later interning. Keeps track of the number of times a
* particular string is interned which will be used by {@link #freeze()} to
* place the most frequently used strings at the beginning of the
* dictionary. After a call to {@link #freeze()}, it is no longer valid to
* call this method.
*
* @param key string to be added to the intern dictionary.
*/
public void intern(String key) {
if (builder == null) {
throw new RuntimeException("freeze() already called.");
}
if (!builder.containsKey(key)) {
builder.put(key, 1);
} else {
int value = builder.get(key) + 1;
builder.put(key, value);
}
}
/**
* Displays a link for a split point that contains this code.
*/
public void printHasCodeInSplitPoint(PrintWriter outFile, String className,
int sp) {
outFile.print("h(" + frozen.get(getPackageSubstring(className)) + ","
+ frozen.get(getClassSubstring(className)) + "," + sp + ");");
}
/**
* Non specific message that there is code in an initial fragment.
*/
public void printHasInitialFragment(PrintWriter outFile) {
outFile.print("f();");
}
/**
* Displays a link for code in an initial fragment.
*/
public void printHasInitialFragment(PrintWriter outFile, String className) {
String packageName = getPackageSubstring(className);
outFile.print("g(" + frozen.get(packageName) + ","
+ frozen.get(getClassSubstring(className)) + ","
+ frozen.get(hashedFilenameFragment(packageName)) + ");");
}
public void printSomeCodeLeftover(PrintWriter outFile) {
outFile.print("i();");
}
/**
* Prints an h3 element with the class name and an anchor.
*/
private void printClassHeader(PrintWriter outFile, String className) {
outFile.print("e(" + frozen.get(getPackageSubstring(className)) + ","
+ frozen.get(getClassSubstring(className)) + ",'"
+ hashedFilenameFragment(className) + "');");
}
/**
* Print out a single class dependency stack in the methodDependencies
* report.
*/
private void printDependency(PrintWriter outFile,
Map<String, String> dependencies, String method, String depMethod) {
String nameArray = "[" + frozen.get(getPackageSubstring(method)) + ","
+ frozen.get(getClassSubstring(method)) + ","
+ frozen.get(getMethodSubstring(method)) + "]";
outFile.print("b(" + nameArray + ",");
outFile.print("[");
while (depMethod != null) {
String nextDep = dependencies.get(depMethod);
// The bottom of the stack frame is not interesting.
if (nextDep != null) {
String packageString = getPackageSubstring(depMethod);
String classString = getClassSubstring(depMethod);
String methodString = getMethodSubstring(depMethod);
outFile.print("[" + frozen.get(packageString) + ","
+ frozen.get(classString) + "," + frozen.get(methodString) + "]");
}
depMethod = nextDep;
if (nextDep != null && dependencies.get(nextDep) != null) {
outFile.print(",");
}
}
outFile.print("]);");
}
/**
* Prints out a class header for the methodDependendies report.
*/
private void printDependencyClassHeader(PrintWriter outFile,
String className) {
outFile.print("a(" + frozen.get(getPackageSubstring(className)) + ","
+ frozen.get(getClassSubstring(className)) + ");");
}
/**
* Prints a JavaScript snippet that includes the dictionary of interned
* strings and methods to use interned strings to create lines in the
* report.
*
* Call this method after invoking {@link #freeze()}.
*
* @param outFile open file to write the data to.
*/
private void printInternedDataAsJs(PrintWriter outFile) {
if (frozen == null) {
throw new RuntimeException("freeze() not called.");
}
outFile.println(" var internedStrings = [");
for (String key : frozen.keySet()) {
outFile.print("\"" + key + "\",");
}
outFile.println("];");
// array of split point descriptions
outFile.println(" var spl = [");
for (int sp = 1; sp <= globalInformation.getNumSplitPoints(); sp++) {
outFile.println(" '"
+ globalInformation.getSplitPointToLocation().get(sp) + "',");
}
outFile.println(" ];");
// object/dictionary containing method sizes
outFile.println(" var methodSizes = {");
for (SizeBreakdown breakdown : globalInformation.allSizeBreakdowns()) {
for (Entry<String, Integer> methodEntry : breakdown.methodToSize.entrySet()) {
String methodSignature = methodEntry.getKey();
String method = methodSignature.substring(0, methodSignature.indexOf("("));
outFile.println(" \"" + method + "\" : " + methodEntry.getValue() + ",");
}
}
outFile.println("};");
// dropdown button image srcs
outFile.println(" var images = {");
outFile.println(" \"closed\" : \"images/play-g16.png\",");
outFile.println(" \"open\" : \"images/play-g16-down.png\",");
outFile.println(" };");
// TODO(zundel): Most of this below is just inserting a fixed string into the code. It would
// be easier to read and maintain if we could store the fixed part in a flat file. Use some
// kind of HTML template?
// function to print a class header in the methodDependencies report
// see printDependencyClassHeader()
outFile.println(" function a(packageRef, classRef) {");
outFile.println(" var className = internedStrings[packageRef] + \".\" + "
+ "internedStrings[classRef];");
outFile.println(" document.write(\"<div class='main'>\");");
outFile.println(" document.write(\"<table class='soyc-table'>\");");
outFile.println(" document.write(\"<thead>\");");
outFile.println(" document.write(\"<th><a class='soyc-class-name' "
+ "name='\" + className + \"'>"
+ "Class: \" + className + \"</a></th>\");");
outFile.println(" document.write(\"<th class='soyc-numerical-col-header'>Size "
+ "<span class='soyc-th-units'>(bytes)</span></th>\");");
outFile.println(" document.write(\"</thead>\");");
outFile.println(" }");
outFile.println(" function swapShowHide(elementName) {");
outFile.println(" hp = document.getElementById(elementName);");
outFile.println(" arrow = document.getElementById(\"dropdown-\" + elementName);");
outFile.println(" if (hp.style.display !== \"none\" && hp.style.display "
+ "!== \"inline\") {");
outFile.println(" hp.style.display = \"inline\";");
outFile.println(" arrow.src = images[\"open\"];");
outFile.println(" } else if (hp.style.display === \"none\") {");
outFile.println(" hp.style.display = \"inline\";");
outFile.println(" arrow.src = images[\"open\"];");
outFile.println(" } else {");
outFile.println(" hp.style.display = \"none\";");
outFile.println(" arrow.src = images[\"closed\"];");
outFile.println(" }");
outFile.println(" }");
// function to print a single dependency in the methodDependencies report
// see printDependency()
outFile.println(" function b(c, deps) {");
outFile.println(" var methodName = internedStrings[c[0]] + \".\" + internedStrings[c[1]] "
+ "+ \"::\" + internedStrings[c[2]];");
outFile.println(" var methodSize = methodSizes[methodName];");
outFile.println(" if (methodSize === undefined) methodSize = \"--\";");
outFile.println(" var callstackId = \"callstack-\" + methodName;");
outFile.println(" document.write(\"<tr>\");");
outFile.println(" document.write(\"<td>\");");
outFile.println(" document.write(\"<img onclick='swapShowHide(\\\"\" + "
+ "callstackId + \"\\\")'"
+ "id='dropdown-\" + callstackId + \"' "
+ "class='dropdown-img' "
+ "src=\" + images[\"closed\"] + \">\");");
outFile.println(" document.write(\"<a class='toggle soyc-call-stack-link' "
+ "onclick='swapShowHide(\\\"\" + callstackId + \"\\\")'>\" + methodName + \"</a>\");");
outFile.println(" document.write(\"<ul id='\" + callstackId + \"' "
+ "class='soyc-call-stack-list'>\");");
outFile.println(" for (var i = 0; i < deps.length ; i++) {");
outFile.println(" var s = deps[i];");
outFile.println(" document.write(\"<li>\" + internedStrings[s[0]] + \".\" + "
+ "internedStrings[s[1]] + \"::\" + internedStrings[s[2]] + \"</li>\");");
outFile.println(" }");
outFile.println(" document.write(\"</ul>\");");
outFile.println(" document.write(\"</td>\");");
outFile.println(" document.write(\"<td class='soyc-numerical-col'>\" + "
+ "methodSize + \"</td>\");");
outFile.println(" document.write(\"</tr>\");");
outFile.println(" }");
// follows all method dependency stacks
outFile.println(" function j() {");
outFile.println(" document.write(\"</div></table>\");");
outFile.println(" }");
// leftovers status line
outFile.println(" function c(packageRef,classRef,packageHashRef) {");
outFile.println(" var packageName = internedStrings[packageRef];");
outFile.println(" var className = packageName + \".\" + internedStrings[classRef];");
outFile.println(" var d1 = 'methodDependencies-total-" + getPermutationId() + ".html';");
outFile.println(" document.write(\"<ul class='soyc-excl'>\");");
outFile.println(" document.write(\"<li><a href='\" + d1 + \"#\" + className + \"'>"
+ "See why it's live</a></li>\");");
outFile.println(" for (var sp = 1; sp <= "
+ globalInformation.getNumSplitPoints() + "; sp++) {");
outFile.println(" var d2 = 'methodDependencies-sp' + sp + '-" + getPermutationId() + ".html';");
outFile.println(" document.write(\"<li><a href='\" + d2 + \"#\" + className +\"'>"
+ " See why it's not exclusive to s.p. #\" + sp + \" (\" + spl[sp - 1] + \")"
+ "</a></li>\");");
outFile.println(" }");
outFile.println(" document.write(\"</ul>\");");
outFile.println(" }");
// leftovers status package header line
outFile.println(" function d(packageRef) {");
outFile.println(" document.write(\"<div class='soyc-pkg-break'>Package: \" + "
+ "internedStrings[packageRef] + \"</div>\");");
outFile.println(" }");
// leftovers status class header line
outFile.println(" function e(packageRef,classRef,classHashRef) {");
outFile.println(" document.write(\"<a name='\" + classHashRef + \"'></a><h3>\" + "
+ "internedStrings[packageRef] + \".\" + internedStrings[classRef] + \"</h3>\");");
outFile.println(" }");
// split point has a class with code in the initial fragment - no link
outFile.println(" function f() {");
outFile.println(" document.write(\"<p>Some code is included in the initial fragment"
+ "</p>\");");
outFile.println(" }");
// split point has a class with code in the initial fragment
outFile.println(" function g(packageRef, classRef, packageHashRef) {");
outFile.println(" document.write(\"<p>Some code is included in the initial fragment "
+ "(<a href='methodDependencies-initial-\" + internedStrings[packageHashRef] + \"-"
+ getPermutationId()
+ ".html#\" + internedStrings[packageRef] + \".\" + "
+ "internedStrings[classRef] + \"'> See why</a>)</p>\");");
outFile.println(" }");
// split point has code from class
outFile.println(" function h(packageRef, classRef, sp) {");
outFile.println(" document.write(\"<p>Some code downloads with split point \" + sp + "
+ "\": \" + spl[sp - 1] + \"</p>\");");
outFile.println(" }");
// some code is left over
outFile.println(" function i() {");
outFile.println(" document.write(\"<p>Some code is left over:</p>\");");
outFile.println(" }");
}
/**
* Prints links to each split point showing why a leftover fragment isn't
* exclusive.
*/
private void printLeftoversStatus(PrintWriter outFile, String packageName,
String className) {
outFile.println("c(" + frozen.get(packageName) + ","
+ frozen.get(getClassSubstring(className)) + ","
+ frozen.get(hashedFilenameFragment(packageName)) + ");");
}
/**
* Prints a div containing the package name in a blue block.
*/
private void printPackageHeader(PrintWriter outFile, String packageName) {
outFile.print("d(" + frozen.get(packageName) + ");");
}
}
/**
* By a convention shared with the compiler, the initial download is fragment
* number 0.
*/
private static final int FRAGMENT_NUMBER_INITIAL_DOWNLOAD = 0;
/**
* Just within this file, the convention is that the total program is fragment
* number -1.
*/
private static final int FRAGMENT_NUMBER_TOTAL_PROGRAM = -1;
/**
* A pattern describing the name of dependency graphs for code fragments
* corresponding to a specific split point. These can be either exclusive
* fragments or fragments of code for split points in the initial load
* sequence.
*/
private static final Pattern PATTERN_SP_INT = Pattern.compile("sp([0-9]+)");
public static void makeTopLevelHtmlForAllPerms(
Map<String, List<String>> allPermsInfo, OutputDirectory outDir)
throws IOException {
PrintWriter outFile = new PrintWriter(outDir.getOutputStream("index.html"));
addStandardHtmlProlog(outFile, "Compile report", "Compile report",
"Overview of permutations");
outFile.println("<ul>");
// in order to print these in ascending order, we have to sort by
// integers
SortedSet<Integer> sortedPermIds = new TreeSet<Integer>();
for (String permutationId : allPermsInfo.keySet()) {
sortedPermIds.add(Integer.parseInt(permutationId));
}
for (Integer sortedPermId : sortedPermIds) {
String permutationId = Integer.toString(sortedPermId);
List<String> permutationInfoList = allPermsInfo.get(permutationId);
outFile.print("<li>Permutation " + permutationId);
for (String desc : permutationInfoList) {
outFile.println(" (" + desc + ")");
}
outFile.println("<ul>");
outFile.println("<li>");
outFile.println("<a href=\"SoycDashboard-" + permutationId
+ "-index.html\">Split Point Report</a>");
outFile.println("</li>");
outFile.println("<li>");
outFile.println("<a href=\"CompilerMetrics-" + permutationId
+ "-index.html\">Compiler Metrics</a>");
outFile.println("</li>");
outFile.println("</ul>");
outFile.println("</li>");
}
outFile.println("</ul>");
addStandardHtmlEnding(outFile);
outFile.close();
}
/**
* @return given "com.foo.myClass" or "com.foo.myClass::myMethod" returns
* "myClass"
*/
static String getClassSubstring(String fullMethodName) {
if (fullMethodName.length() == 0) {
return "";
}
int startIndex = getPackageSubstring(fullMethodName).length() + 1;
int endIndex = fullMethodName.indexOf("::");
if (endIndex == -1) {
endIndex = fullMethodName.length();
}
if (startIndex > endIndex || startIndex > fullMethodName.length()) {
return "";
}
return fullMethodName.substring(startIndex, endIndex);
}
/**
* @return given "com.foo.myClass::myMethod" returns "myMethod"
*/
static String getMethodSubstring(String fullMethodName) {
int index = fullMethodName.indexOf("::");
if (index == -1) {
return "";
}
index += 2;
if (index >= fullMethodName.length()) {
return "";
}
return fullMethodName.substring(index);
}
/**
* @return given "com.foo.myClass" or "com.foo.myClass::myMethod" returns
* "com.foo"
*/
static String getPackageSubstring(String fullMethodName) {
int endIndex = fullMethodName.lastIndexOf('.');
if (endIndex == -1) {
endIndex = fullMethodName.length();
}
return fullMethodName.substring(0, endIndex);
}
private static void addSmallHtmlProlog(final PrintWriter outFile, String title) {
outFile.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"");
outFile.println("\"http://www.w3.org/TR/html4/strict.dtd\">");
outFile.println("<html>");
outFile.println("<head>");
outFile.println("<title>");
outFile.println(title);
outFile.println("</title>");
outFile.println("<style type=\"text/css\" media=\"screen\">");
outFile.println("@import url('goog.css');");
outFile.println("@import url('inlay.css');");
outFile.println("@import url('soyc.css');");
outFile.println("</style>");
outFile.println("</head>");
}
private static void addStandardHtmlEnding(final PrintWriter out) {
out.println("</div>");
out.println("</body>");
out.println("</html>");
}
private static void addStandardHtmlProlog(final PrintWriter outFile,
String title, String header1, String header2) {
outFile.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"");
outFile.println("\"http://www.w3.org/TR/html4/strict.dtd\">");
outFile.println("<html>");
outFile.println("<head>");
outFile.println("<script type=\"text/javascript\">");
outFile.println("function show(elementName)");
outFile.println("{");
outFile.println("hp = document.getElementById(elementName);");
outFile.println("hp.style.visibility = \"Visible\";");
outFile.println("}");
outFile.println("function hide(elementName)");
outFile.println("{");
outFile.println("hp = document.getElementById(elementName);");
outFile.println("hp.style.visibility = \"Hidden\";");
outFile.println("}");
outFile.println("</script>");
outFile.println("<title>");
outFile.println(title);
outFile.println("</title>");
outFile.println("<style type=\"text/css\" media=\"screen\">");
outFile.println("@import url('goog.css');");
outFile.println("@import url('inlay.css');");
outFile.println("@import url('soyc.css');");
outFile.println("</style>");
outFile.println("</head>");
outFile.println("<body>");
outFile.println("<div class=\"g-doc\">");
outFile.println("<div id=\"hd\" class=\"g-section g-tpl-50-50 g-split\">");
outFile.println("<div class=\"g-unit g-first\">");
outFile.println("<p>");
outFile.println("<a href=\"index.html\" id=\"gwt-logo\" class=\"soyc-ir\">");
outFile.println("<span>Google Web Toolkit</span>");
outFile.println("</a>");
outFile.println("</p>");
outFile.println("</div>");
outFile.println("<div class=\"g-unit\">");
outFile.println("</div>");
outFile.println("</div>");
outFile.println("<div id=\"soyc-appbar-lrg\">");
outFile.println("<div class=\"g-section g-tpl-75-25 g-split\">");
outFile.println("<div class=\"g-unit g-first\">");
outFile.println("<h1>" + header1 + "</h1>");
outFile.println("</div>");
outFile.println("<div class=\"g-unit\"></div>");
outFile.println("</div>");
outFile.println("</div>");
outFile.println("<div id=\"bd\">");
if (header2 != null && header2.length() > 0) {
outFile.println("<h2>" + header2 + "</h2>");
}
}
private static String classesInPackageFileName(SizeBreakdown breakdown,
String permutationId) {
return breakdown.getId() + "_" + permutationId + "_Classes.html";
}
private static String escapeXml(String unescaped) {
return SizeMapRecorder.escapeXml(unescaped);
}
/**
* Convert a potentially long string into a short file name. The current
* implementation simply hashes the long name.
*/
private static String hashedFilenameFragment(String longFileName) {
try {
return Util.computeStrongName(longFileName.getBytes(Util.DEFAULT_ENCODING));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
private static String headerLineForBreakdown(SizeBreakdown breakdown) {
return "(Analyzing code subset: " + breakdown.getDescription() + ")";
}
private static String shellFileName(SizeBreakdown breakdown,
String permutationId) {
return breakdown.getId() + "-" + permutationId + "-overallBreakdown.html";
}
/**
* Global information for this permutation.
*/
private final GlobalInformation globalInformation;
private final OutputDirectory outDir;
MakeTopLevelHtmlForPerm(GlobalInformation globalInformation,
OutputDirectory outDir) {
this.globalInformation = globalInformation;
this.outDir = outDir;
}
public void makeBreakdownShell(SizeBreakdown breakdown) throws IOException {
Map<String, CodeCollection> nameToCodeColl = breakdown.nameToCodeColl;
Map<String, LiteralsCollection> nameToLitColl = breakdown.nameToLitColl;
String packageBreakdownFileName = makePackageHtml(breakdown);
String codeTypeBreakdownFileName = makeCodeTypeHtml(breakdown,
nameToCodeColl, nameToLitColl);
PrintWriter outFile = new PrintWriter(getOutFile(shellFileName(breakdown,
getPermutationId())));
addStandardHtmlProlog(outFile, "Application breakdown analysis",
"Application breakdown analysis", "");
String popupName = "packageBreakdownPopup";
String popupTitle = "Package breakdown";
String popupBody = "The package breakdown blames pieces of JavaScript "
+ "code on Java packages wherever possible. Note that this is not possible for all "
+ "code, so the sizes of the packages here will not normally add up to the full code "
+ "size. More specifically, the sum will exclude strings, whitespace, and a few pieces "
+ "of JavaScript code that are produced during compilation but cannot be attributed to "
+ "any Java package.";
outFile.println("<h2>");
addPopupLink(outFile, popupName, popupTitle, null);
outFile.println("</h2></div>");
addPopup(outFile, popupName, popupTitle, popupBody);
outFile.println("<iframe class='soyc-iframe-package' src=\""
+ packageBreakdownFileName + "\" scrolling=auto></iframe>");
popupName = "codeTypeBreakdownPopup";
popupTitle = "Code Type Breakdown";
popupBody = "The code type breakdown breaks down the JavaScript code according to its "
+ "type or function. For example, it tells you how much of your code can be attributed to "
+ "JRE, GWT-RPC, etc. As above, strings and some other JavaScript snippets are not "
+ "included in the breakdown.";
outFile.println("<h2>");
addPopupLink(outFile, popupName, popupTitle, null);
outFile.println("</h2>");
addPopup(outFile, popupName, popupTitle, popupBody);
outFile.println("<iframe class='soyc-iframe-code' src=\""
+ codeTypeBreakdownFileName + "\" scrolling=auto></iframe>");
outFile.println("</div>");
addStandardHtmlEnding(outFile);
outFile.close();
}
public void makeCodeTypeClassesHtmls(SizeBreakdown breakdown)
throws IOException {
HashMap<String, CodeCollection> nameToCodeColl = breakdown.nameToCodeColl;
for (String codeType : nameToCodeColl.keySet()) {
// construct file name
String outFileName = breakdown.getId() + "_" + codeType + "-"
+ getPermutationId() + "Classes.html";
float sumSize = 0f;
TreeMap<Integer, Set<String>> sortedClasses = new TreeMap<Integer, Set<String>>(
Collections.reverseOrder());
for (String className : nameToCodeColl.get(codeType).classes) {
if (breakdown.classToSize.containsKey(className)) {
int curSize = 0;
if (breakdown.classToSize.containsKey(className)) {
curSize = breakdown.classToSize.get(className);
}
if (curSize != 0) {
if (sortedClasses.containsKey(curSize)) {
Set<String> existingSet = sortedClasses.get(curSize);
existingSet.add(className);
sortedClasses.put(curSize, existingSet);
} else {
Set<String> newSet = new TreeSet<String>();
newSet.add(className);
sortedClasses.put(curSize, newSet);
}
sumSize += curSize;
}
}
}
final PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
addStandardHtmlProlog(outFile, "Classes in package " + codeType,
"Classes in package " + codeType, headerLineForBreakdown(breakdown));
outFile.println("<table class=\"soyc-table\">");
outFile.println("<colgroup>");
outFile.println("<col id=\"soyc-splitpoint-type-col\">");
outFile.println("<col id=\"soyc-splitpoint-size-col\">");
outFile.println("</colgroup>");
outFile.println("<thead>");
outFile.println("<th>Code type</th>");
outFile.println("<th>");
outFile.println("<th class=\"soyc-numerical-col-header\">");
outFile.println("Size <span class=\"soyc-th-units\">(Bytes)</span>");
outFile.println("</th>");
outFile.println("<th class=\"soyc-numerical-col-header\">% of total</th>");
outFile.println("</thead>");
NumberFormat bytesFormatter = NumberFormat.getInstance();
bytesFormatter.setGroupingUsed(true);
NumberFormat percentFormatter = NumberFormat.getPercentInstance();
percentFormatter.setMinimumFractionDigits(1);
percentFormatter.setMaximumFractionDigits(1);
for (Integer size : sortedClasses.keySet()) {
Set<String> classNames = sortedClasses.get(size);
for (String className : classNames) {
float perc = (float) size / sumSize;
outFile.println("<tr>");
outFile.println("<td>" + className + "</a></td>");
outFile.println("<td class=\"soyc-bargraph-col\">");
outFile.println("<div class=\"soyc-bar-graph goog-inline-block\">");
// CHECKSTYLE_OFF
outFile.println("<div style=\"width:" + (perc * 100.0)
+ "%;\" class=\"soyc-bar-graph-fill goog-inline-block\"></div>");
// CHECKSTYLE_ON
outFile.println("</div>");
outFile.println("</td>");
outFile.println("<td class=\"soyc-numerical-col\">");
outFile.println(bytesFormatter.format(size));
outFile.println("</td>");
outFile.println("<td class=\"soyc-percent-col\">" +
percentFormatter.format(perc) + "</td>");
outFile.println("</tr>");
}
}
addStandardHtmlEnding(outFile);
outFile.close();
}
}
public void makeCompilerMetricsPermFiles(ModuleMetricsArtifact moduleMetrics,
PrecompilationMetricsArtifact precompilationMetrics,
CompilationMetricsArtifact compilationMetrics) throws IOException {
String outFileName = "CompilerMetrics-"
+ precompilationMetrics.getPermuationBase() + "-index.html";
PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
String title = "Compiler Metrics for Permutation "
+ compilationMetrics.getPermuationId();
addStandardHtmlProlog(outFile, title, title, "Build Time Metrics");
NumberFormat elapsedFormatter = NumberFormat.getInstance();
elapsedFormatter.setGroupingUsed(true);
elapsedFormatter.setMinimumFractionDigits(3);
elapsedFormatter.setMaximumFractionDigits(3);
outFile.println("<div id=\"bd\">");
int permutationId = compilationMetrics.getPermuationId();
// Build Time Metrics
outFile.println("<table class=\"soyc-table\">");
outFile.println("<colgroup>");
outFile.println("<col id=\"soyc-buildTimePhase-col\">");
outFile.println("<col id=\"soyc-buildTimeElapsed-col\">");
outFile.println("</colgroup>");
outFile.println("<thead>");
outFile.println("<th>Phase</th>");
outFile.println("<th class=\"soyc-numerical-col-header\">Elapsed Time</th>");
outFile.println("</thead>");
outFile.println("<tr>");
outFile.println("<td>");
outFile.println("Module Analysis");
outFile.println("</td>");
outFile.println("<td class=\"soyc-numerical-col\">"
+ elapsedFormatter.format(moduleMetrics.getElapsedMilliseconds() / 1000.0) + " s");
outFile.println("</td>");
outFile.println("</tr>");
outFile.println("<tr>");
outFile.println("<td>");
outFile.println("Precompile (may include Module Analysis)");
outFile.println("</td>");
outFile.println("<td class=\"soyc-numerical-col\">"
+ elapsedFormatter.format(precompilationMetrics.getElapsedMilliseconds() / 1000.0)
+ " s");
outFile.println("</td>");
outFile.println("</tr>");
outFile.println("<tr>");
outFile.println("<td>");
outFile.println("Compile");
outFile.println("</td>");
outFile.println("<td class=\"soyc-numerical-col\">"
+ elapsedFormatter.format(compilationMetrics.getElapsedMilliseconds() / 1000.0)
+ " s");
outFile.println("</td>");
outFile.println("</tr>");
outFile.println("</table>");
NumberFormat referencesFormatter = NumberFormat.getInstance();
referencesFormatter.setGroupingUsed(true);
outFile.println("<p></p>");
outFile.println("<h2>Source/Type Metrics</h2>");
outFile.println("<table class=\"soyc-table\">");
outFile.println("<colgroup>");
outFile.println("<col id=\"soyc-typeList-col\">");
outFile.println("<col id=\"soyc-typeReferences-col\">");
outFile.println("</colgroup>");
outFile.println("<thead>");
outFile.println("<th>Description</th>");
outFile.println("<th class=\"soyc-numerical-col-header\">References</th>");
outFile.println("</thead>");
String sourcesFileName = "CompilerMetrics-sources.html";
outFile.println("<tr>");
outFile.println("<td>");
String popupName = "compilerMetricsSourceFiles";
String popupTitle = "Source files";
String popupBody = "All source files on the module source path.";
addPopupLink(outFile, popupName, popupTitle, sourcesFileName);
addPopup(outFile, popupName, popupTitle, popupBody);
outFile.println("</td>");
outFile.println("<td class=\"soyc-numerical-col\">");
outFile.println(referencesFormatter.format(moduleMetrics.getSourceFiles().length));
outFile.println("</td>");
outFile.println("</tr>");
makeCompilerMetricsSources(sourcesFileName, moduleMetrics, popupBody);
String initialTypesFileName = "CompilerMetrics-initialTypes-"
+ permutationId + ".html";
outFile.println("<tr>");
outFile.println("<td>");
popupName = "compilerMetricsInitialTypes";
popupTitle = "Initial Type Oracle Types";
popupBody = "All types in the type oracle after compiling sources on the source path.";
addPopupLink(outFile, popupName, popupTitle, initialTypesFileName);
addPopup(outFile, popupName, popupTitle, popupBody);
outFile.println("</td>");
outFile.println("<td class=\"soyc-numerical-col\">");
outFile.println(referencesFormatter.format(moduleMetrics.getInitialTypes().length));
outFile.println("</td>");
outFile.println("</tr>");
makeCompilerMetricsInitialTypeOracleTypes(initialTypesFileName,
moduleMetrics, popupBody);
String finalTypesFileName = "CompilerMetrics-finalTypes-" + permutationId
+ ".html";
outFile.println("<tr>");
outFile.println("<td>");
popupName = "compilerMetricsFinalTypes";
popupTitle = "Final Type Oracle Types";
popupBody = "All types in the type oracle after constructing the Java AST.";
addPopupLink(outFile, popupName, popupTitle, finalTypesFileName);
addPopup(outFile, popupName, popupTitle, popupBody);
outFile.println("</td>");
outFile.println("<td class=\"soyc-numerical-col\">");
outFile.println(referencesFormatter.format(
precompilationMetrics.getFinalTypeOracleTypes().length));
outFile.println("</td>");
outFile.println("</tr>");
makeCompilerMetricsFinalTypeOracleTypes(finalTypesFileName,
precompilationMetrics, popupBody);
String[] generatedTypes = getGeneratedTypes(moduleMetrics,
precompilationMetrics);
String generatedTypesFileName = "CompilerMetrics-generatedTypes-"
+ permutationId + ".html";
outFile.println("<tr>");
outFile.println("<td>");
popupName = "compilerMetricsGeneratedTypes";
popupTitle = "GeneratedTypes";
popupBody = "Types that were added to the type oracle while running generators.";
addPopupLink(outFile, popupName, popupTitle, generatedTypesFileName);
addPopup(outFile, popupName, popupTitle, popupBody);
outFile.println("</td>");
outFile.println("<td class=\"soyc-numerical-col\">");
outFile.println(referencesFormatter.format(generatedTypes.length));
outFile.println("</td>");
outFile.println("</tr>");
makeCompilerMetricsGeneratedTypes(generatedTypesFileName, generatedTypes,
popupBody);
String astFileName = "CompilerMetrics-ast-" + permutationId + ".html";
outFile.println("<tr>");
outFile.println("<td>");
popupName = "compilerMetricsAstTypes";
popupTitle = "AST Referenced Types";
popupBody = "All types referenced by the Java AST after performing "
+ "reachability analysis from the module EntryPoint.";
addPopupLink(outFile, popupName, popupTitle, astFileName);
addPopup(outFile, popupName, popupTitle, popupBody);
outFile.println("</td>");
outFile.println("<td class=\"soyc-numerical-col\">");
outFile.println(referencesFormatter.format(precompilationMetrics.getAstTypes().length));
outFile.println("</td>");
outFile.println("</tr>");
makeCompilerMetricsAstTypes(astFileName, precompilationMetrics, popupBody);
String[] unreferencedTypes = getUnreferencedTypes(precompilationMetrics);
String unreferencedFileName = "CompilerMetrics-unreferencedTypes-"
+ permutationId + ".html";
outFile.println("<tr>");
outFile.println("<td>");
popupName = "compilerMetricsUnreferenceTypes";
popupTitle = "Unreferenced Types";
popupBody = "Types that were on the initial source path but never referenced in "
+ "the Java AST.";
addPopupLink(outFile, popupName, popupTitle, unreferencedFileName);
addPopup(outFile, popupName, popupTitle, popupBody);
outFile.println("</td>");
outFile.println("<td class=\"soyc-numerical-col\">");
outFile.println(referencesFormatter.format(unreferencedTypes.length));
outFile.println("</td>");
outFile.println("</tr>");
makeCompilerMetricsUnreferencedTypes(unreferencedFileName,
unreferencedTypes, popupBody);
outFile.println("</table>");
addStandardHtmlEnding(outFile);
outFile.close();
}
public void makeDependenciesHtml() throws IOException {
for (String depGraphName : globalInformation.dependencies.keySet()) {
makeDependenciesHtml(depGraphName,
globalInformation.dependencies.get(depGraphName));
}
}
public void makeLeftoverStatusPages() throws IOException {
PrintWriter outFile = new PrintWriter(getOutFile(leftoversStatusFileName()));
addStandardHtmlProlog(outFile, "Leftovers page", "Leftovers page", "");
outFile.println("<div id=\"bd\">");
outFile.println("<p>These classes have some leftover code, neither initial nor "
+ "exclusive to any split point:</p>");
String curPackageName = "";
HtmlInterner interner = new HtmlInterner();
for (String className : globalInformation.getClassToPackage().keySet()) {
String packageName = globalInformation.getClassToPackage().get(className);
interner.intern(packageName);
interner.intern(getClassSubstring(className));
interner.intern(hashedFilenameFragment(packageName));
}
interner.freeze();
outFile.println("<script language=\"javascript\">");
interner.printInternedDataAsJs(outFile);
for (String className : globalInformation.getClassToPackage().keySet()) {
String packageName = globalInformation.getClassToPackage().get(className);
if (packageName.compareTo("") == 0
|| packageName.compareTo(curPackageName) != 0) {
curPackageName = packageName;
interner.printPackageHeader(outFile, packageName);
}
interner.printClassHeader(outFile, className);
if (globalInformation.dependencies != null) {
interner.printLeftoversStatus(outFile, packageName, className);
}
}
outFile.println("</script>");
addStandardHtmlEnding(outFile);
outFile.close();
}
public void makeLiteralsClassesTableHtmls(SizeBreakdown breakdown)
throws IOException {
Map<String, LiteralsCollection> nameToLitColl = breakdown.nameToLitColl;
for (String literalType : nameToLitColl.keySet()) {
String outFileName = literalType + "-" + getPermutationId() + "Lits.html";
final PrintWriter outFile = new PrintWriter(getOutFile(breakdown.getId()
+ "_" + outFileName));
addStandardHtmlProlog(outFile, "Literals of type " + literalType,
"Literals of type " + literalType, headerLineForBreakdown(breakdown));
outFile.println("<table width=\"80%\" style=\"font-size: 11pt;\" bgcolor=\"white\">");
for (String literal : nameToLitColl.get(literalType).literals) {
if (literal.trim().length() == 0) {
literal = "[whitespace only string]";
}
String escliteral = escapeXml(literal);
outFile.println("<tr>");
outFile.println("<td>" + escliteral + "</td>");
outFile.println("</tr>");
}
outFile.println("</table>");
outFile.println("<center>");
addStandardHtmlEnding(outFile);
outFile.close();
}
}
/**
* Make size breakdowns for each package for one code collection.
*/
public void makePackageClassesHtmls(SizeBreakdown breakdown,
DependencyLinker depLinker) throws IOException {
PrintWriter outFile = new PrintWriter(getOutFile(classesInPackageFileName(
breakdown, getPermutationId())));
addStandardHtmlProlog(outFile, "Classes in " + breakdown.getDescription(),
"Classes in " + breakdown.getDescription(),
headerLineForBreakdown(breakdown));
String[] packageNames = globalInformation.getPackageToClasses().keySet().toArray(
new String[0]);
Arrays.sort(packageNames);
for (String packageName : packageNames) {
TreeMap<Integer, Set<String>> sortedClasses = new TreeMap<Integer, Set<String>>(
Collections.reverseOrder());
int sumSize = 0;
for (String className : globalInformation.getPackageToClasses().get(
packageName)) {
int curSize = 0;
if (!breakdown.classToSize.containsKey(className)) {
// This class not present in this code collection
} else {
curSize = breakdown.classToSize.get(className);
}
if (curSize != 0f) {
if (sortedClasses.containsKey(curSize)) {
Set<String> existingSet = sortedClasses.get(curSize);
existingSet.add(className);
sortedClasses.put(curSize, existingSet);
} else {
Set<String> newSet = new TreeSet<String>();
newSet.add(className);
sortedClasses.put(curSize, newSet);
}
sumSize += curSize;
}
}
if (sortedClasses.size() > 0) {
outFile.println("<p>");
outFile.println("<table class=\"soyc-table\">");
outFile.print("<colgroup>");
outFile.print("<col id=\"soyc-splitpoint-type-col\">");
outFile.print("<col id=\"soyc-splitpoint-size-col\">");
outFile.println("</colgroup>");
outFile.print("<thead>");
outFile.print("<a name=\"" + hashedFilenameFragment(packageName)
+ "\"></a><th>Package: " + packageName + "</th>");
outFile.println("<th></th>");
outFile.println("<th class=\"soyc-numerical-col-header\">");
outFile.println("Size <span class=\"soyc-th-units\">(Bytes)</span>");
outFile.println("</th>");
outFile.println("<th class=\"soyc-numerical-col-header\">% of total</th>");
outFile.print("</thead>");
NumberFormat bytesFormatter = NumberFormat.getInstance();
bytesFormatter.setGroupingUsed(true);
NumberFormat percentFormatter = NumberFormat.getPercentInstance();
percentFormatter.setMinimumFractionDigits(1);
percentFormatter.setMaximumFractionDigits(1);
for (Integer size : sortedClasses.keySet()) {
Set<String> classNames = sortedClasses.get(size);
for (String className : classNames) {
String drillDownFileName = depLinker.dependencyLinkForClass(className);
float perc = (float) size / (float) sumSize;
outFile.println("<tr>");
if (drillDownFileName == null) {
outFile.println("<td>" + className + "</td>");
} else {
outFile.println("<td><a href=\"" + drillDownFileName
+ "\" target=\"_top\">" + className + "</a></td>");
}
outFile.println("<td class=\"soyc-bargraph-col\">");
outFile.println("<div class=\"soyc-bar-graph goog-inline-block\">");
// CHECKSTYLE_OFF
outFile.println("<div style=\"width:" + (perc * 100.0)
+ "%;\" class=\"soyc-bar-graph-fill goog-inline-block\"></div>");
// CHECKSTYLE_ON
outFile.println("</div>");
outFile.println("</td>");
outFile.println("<td class=\"soyc-numerical-col\">");
outFile.println(bytesFormatter.format(size));
outFile.println("</td>");
outFile.println("<td class=\"soyc-percent-col\">" +
percentFormatter.format(perc) + "</td>");
outFile.println("</tr>");
}
}
outFile.println("</table>");
outFile.println("</p>");
}
}
addStandardHtmlEnding(outFile);
outFile.close();
}
public void makeSplitStatusPages() throws IOException {
PrintWriter outFile = new PrintWriter(getOutFile(splitStatusFileName()));
addStandardHtmlProlog(outFile, "Split point status", "Split point status",
"");
outFile.println("<div id=\"bd\">");
HtmlInterner interner = new HtmlInterner();
for (String className : globalInformation.getClassToPackage().keySet()) {
String packageName = globalInformation.getClassToPackage().get(className);
interner.intern(packageName);
interner.intern(getClassSubstring(className));
interner.intern(hashedFilenameFragment(packageName));
}
interner.freeze();
outFile.println("<script language=\"javascript\">");
interner.printInternedDataAsJs(outFile);
String curPackageName = "";
for (String className : globalInformation.getClassToPackage().keySet()) {
String packageName = globalInformation.getClassToPackage().get(className);
if (packageName.compareTo("") == 0
|| packageName.compareTo(curPackageName) != 0) {
curPackageName = packageName;
interner.printPackageHeader(outFile, packageName);
}
interner.printClassHeader(outFile, className);
if (globalInformation.getInitialCodeBreakdown().classToSize.containsKey(className)) {
if (globalInformation.dependencies != null) {
interner.printHasInitialFragment(outFile, className);
} else {
interner.printHasInitialFragment(outFile);
}
}
for (int sp : splitPointsWithClass(className)) {
interner.printHasCodeInSplitPoint(outFile, className, sp);
}
if (globalInformation.getLeftoversBreakdown().classToSize.containsKey(className)) {
interner.printSomeCodeLeftover(outFile);
interner.printLeftoversStatus(outFile, packageName, className);
}
}
outFile.println("</script>");
addStandardHtmlEnding(outFile);
outFile.close();
}
public void makeTopLevelShell() throws IOException {
String permutationId = getPermutationId();
PrintWriter outFile = new PrintWriter(getOutFile("SoycDashboard" + "-"
+ getPermutationId() + "-index.html"));
addStandardHtmlProlog(outFile, "Compile report: Permutation "
+ permutationId, "Compile report: Permutation " + permutationId, "");
NumberFormat bytesFormatter = NumberFormat.getInstance();
bytesFormatter.setGroupingUsed(true);
outFile.println("<div id=\"bd\">");
outFile.println("<div id=\"soyc-summary\" class=\"g-section\">");
outFile.println("<dl>");
outFile.println("<dt>Full code size</dt>");
outFile.println("<dd class=\"value\">"
+ bytesFormatter.format(globalInformation.getTotalCodeBreakdown().sizeAllCode)
+ " Bytes</dd>");
outFile.println("<dd class=\"report\"><a href=\"total-" + permutationId
+ "-overallBreakdown.html\">Report</a></dd>");
outFile.println("</dl>");
outFile.println("<dl>");
outFile.println("<dt>Initial download size</dt>");
// TODO(kprobst) -- add percentage here: (48%)</dd>");
outFile.println("<dd class=\"value\">"
+ bytesFormatter.format(globalInformation.getInitialCodeBreakdown().sizeAllCode)
+ " Bytes</dd>");
outFile.println("<dd class=\"report\"><a href=\"initial-" + permutationId
+ "-overallBreakdown.html\">Report</a></dd>");
outFile.println("</dl>");
outFile.println("<dl>");
outFile.println("<dt>Left over code</dt>");
outFile.println("<dd class=\"value\">"
+ bytesFormatter.format(globalInformation.getLeftoversBreakdown().sizeAllCode)
+ " Bytes</dd>");
outFile.println("<dd class=\"report\"><a href=\"leftovers-" + permutationId
+ "-overallBreakdown.html\">Report</a></dd>");
outFile.println("</dl>");
outFile.println("</div>");
outFile.println("<table id=\"soyc-table-splitpoints\" class=\"soyc-table\">");
outFile.println("<caption>");
outFile.println("<strong>Split Points</strong>");
outFile.println("</caption>");
outFile.println("<colgroup>");
outFile.println("<col id=\"soyc-splitpoint-number-col\">");
outFile.println("<col id=\"soyc-splitpoint-location-col\">");
outFile.println("<col id=\"soyc-splitpoint-size-col\">");
outFile.println("</colgroup>");
outFile.println("<thead>");
outFile.println("<th>#</th>");
outFile.println("<th>Location</th>");
outFile.println("<th></th>");
outFile.println("<th class=\"soyc-numerical-col-header\">");
outFile.println("Size <span class=\"soyc-th-units\">(Bytes)</span>");
outFile.println("</th>");
outFile.println("<th class=\"soyc-numerical-col-header\">% of total</th>");
outFile.println("</thead>");
outFile.println("<tbody>");
NumberFormat percentFormatter = NumberFormat.getPercentInstance();
percentFormatter.setMinimumFractionDigits(1);
percentFormatter.setMaximumFractionDigits(1);
if (globalInformation.getSplitPointToLocation().size() >= 1) {
int numSplitPoints = globalInformation.getSplitPointToLocation().size();
int maxSize = globalInformation.getTotalCodeBreakdown().sizeAllCode;
for (int i = FRAGMENT_NUMBER_TOTAL_PROGRAM; i <= numSplitPoints + 1; i++) {
SizeBreakdown breakdown;
if (i == FRAGMENT_NUMBER_TOTAL_PROGRAM) {
continue;
} else if (i == numSplitPoints + 1) { // leftovers
continue;
} else if (i == FRAGMENT_NUMBER_INITIAL_DOWNLOAD) {
continue;
} else {
breakdown = globalInformation.splitPointCodeBreakdown(i);
}
String drillDownFileName = shellFileName(breakdown, getPermutationId());
String splitPointDescription = globalInformation.getSplitPointToLocation().get(
i);
int size = breakdown.sizeAllCode;
float perc = (float) size / (float) maxSize;
outFile.println("<tr>");
outFile.println("<td>" + i + "</td>");
outFile.println("<td><a href=\"" + drillDownFileName + "\">"
+ splitPointDescription + "</a></td>");
outFile.println("<td class=\"soyc-bargraph-col\">");
outFile.println("<div class=\"soyc-bar-graph goog-inline-block\">");
// CHECKSTYLE_OFF
outFile.println("<div style=\"width:" + (perc * 100.0)
+ "%;\" class=\"soyc-bar-graph-fill goog-inline-block\"></div>");
// CHECKSTYLE_ON
outFile.println("</div>");
outFile.println("</td>");
outFile.println("<td class=\"soyc-numerical-col\">");
outFile.println(bytesFormatter.format(size));
outFile.println("</td>");
outFile.println("<td class=\"soyc-percent-col\">" +
percentFormatter.format(perc) + "</td>");
outFile.println("</tr>");
}
}
outFile.println("</tbody>");
outFile.println("</table>");
outFile.println("</div>");
addStandardHtmlEnding(outFile);
outFile.close();
}
private void addPopup(PrintWriter outFile, String popupName,
String popupTitle, String popupBody) {
outFile.println("<div class=\"soyc-popup\" id=\"" + popupName + "\">");
outFile.println("<table>");
outFile.println("<tr><th><b>" + popupTitle + "</b></th></tr>");
outFile.println("<tr><td>" + popupBody + "</td></tr>");
outFile.println("</table>");
outFile.println("</div>");
}
private void addPopupLink(PrintWriter outFile, String popupName,
String popupTitle, String href) {
outFile.println("<a ");
if (href != null) {
outFile.println("href=\"" + href + "\"");
}
outFile.println("style=\"cursor:default;\" onMouseOver=\"show('"
+ popupName + "');\" " + "onMouseOut=\"hide('" + popupName + "');\">"
+ popupTitle + "</a>");
}
/**
* Returns a file name for the dependencies list.
*/
private String dependenciesFileName(String depGraphName) {
return "methodDependencies-" + depGraphName + "-" + getPermutationId() + ".html";
}
private String[] getGeneratedTypes(ModuleMetricsArtifact moduleMetrics,
PrecompilationMetricsArtifact precompilationMetrics) {
List<String> initialTypes = Lists.create(moduleMetrics.getInitialTypes());
Set<String> generatedTypes = Sets.create(precompilationMetrics.getFinalTypeOracleTypes());
generatedTypes.removeAll(initialTypes);
String[] results = generatedTypes.toArray(new String[generatedTypes.size()]);
Arrays.sort(results);
return results;
}
/**
* Return a {@link java.io.File} object for a file to be emitted into the
* output directory.
*/
private OutputStream getOutFile(String localFileName) throws IOException {
return outDir.getOutputStream(localFileName);
}
/**
* @return the ID for the current permutation
*/
private String getPermutationId() {
return globalInformation.getPermutationId();
}
private String[] getUnreferencedTypes(
PrecompilationMetricsArtifact precompilationMetrics) {
List<String> astTypes = Lists.create(precompilationMetrics.getAstTypes());
Set<String> unreferencedTypes = Sets.create(precompilationMetrics.getFinalTypeOracleTypes());
unreferencedTypes.removeAll(astTypes);
String[] results = unreferencedTypes.toArray(new String[unreferencedTypes.size()]);
Arrays.sort(results);
return results;
}
/**
* Describe the code covered by the dependency graph with the supplied name.
*/
private String inferDepGraphDescription(String depGraphName) {
if (depGraphName.equals("initial")) {
return "Initially Live Code";
}
if (depGraphName.equals("total")) {
return "All Code";
}
Matcher matcher = PATTERN_SP_INT.matcher(depGraphName);
if (matcher.matches()) {
int splitPoint = Integer.valueOf(matcher.group(1));
if (isInitialSplitPoint(splitPoint)) {
return "Code Becoming Live at Split Point " + splitPoint;
} else {
return "Code not Exclusive to Split Point " + splitPoint;
}
}
throw new RuntimeException("Unexpected dependency graph name: "
+ depGraphName);
}
/**
* Returns whether a split point is initial or not.
*
* @param splitPoint
* @returns true of the split point is initial, false otherwise
*/
private boolean isInitialSplitPoint(int splitPoint) {
return globalInformation.getSplitPointInitialLoadSequence().contains(
splitPoint);
}
/**
* Makes a file name for a leftovers status file.
*
* @return the file name of the leftovers status file
*/
private String leftoversStatusFileName() {
return "leftoverStatus-" + getPermutationId() + ".html";
}
/**
* Produces an HTML file that breaks down by code type.
*
* @param breakdown
* @param nameToCodeColl
* @param nameToLitColl
* @return the name of the produced file
* @throws IOException
*/
private String makeCodeTypeHtml(SizeBreakdown breakdown,
Map<String, CodeCollection> nameToCodeColl,
Map<String, LiteralsCollection> nameToLitColl) throws IOException {
String outFileName = breakdown.getId() + "-" + getPermutationId()
+ "-codeTypeBreakdown.html";
int sumSize = 0;
TreeMap<Integer, Set<String>> sortedCodeTypes = new TreeMap<Integer, Set<String>>(
Collections.reverseOrder());
for (String codeType : nameToCodeColl.keySet()) {
int curSize = nameToCodeColl.get(codeType).getCumSize(breakdown);
sumSize += curSize;
if (curSize != 0) {
if (sortedCodeTypes.containsKey(curSize)) {
Set<String> existingSet = sortedCodeTypes.get(curSize);
existingSet.add(codeType);
sortedCodeTypes.put(curSize, existingSet);
} else {
Set<String> newSet = new TreeSet<String>();
newSet.add(codeType);
sortedCodeTypes.put(curSize, newSet);
}
}
}
final PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
addSmallHtmlProlog(outFile, "Code breakdown");
outFile.println("<body class=\"soyc-breakdown\">");
outFile.println("<div class=\"g-doc\">");
outFile.println("<table class=\"soyc-table\">");
outFile.println("<colgroup>");
outFile.println("<col id=\"soyc-splitpoint-type-col\">");
outFile.println("<col id=\"soyc-splitpoint-size-col\">");
outFile.println("</colgroup>");
outFile.println("<thead>");
outFile.println("<th>Type</th>");
outFile.println("<th></th>");
outFile.println("<th class=\"soyc-numerical-col-header\">");
outFile.println("Size <span class=\"soyc-th-units\">(Bytes)</span>");
outFile.println("</th>");
outFile.println("<th class=\"soyc-numerical-col-header\">% of total</th>");
outFile.println("</thead>");
NumberFormat bytesFormatter = NumberFormat.getInstance();
bytesFormatter.setGroupingUsed(true);
NumberFormat percentFormatter = NumberFormat.getPercentInstance();
percentFormatter.setMinimumFractionDigits(1);
percentFormatter.setMaximumFractionDigits(1);
for (Integer size : sortedCodeTypes.keySet()) {
Set<String> codeTypes = sortedCodeTypes.get(size);
for (String codeType : codeTypes) {
String drillDownFileName = breakdown.getId() + "_" + codeType + "-"
+ getPermutationId() + "Classes.html";
float perc = (float) size / (float) sumSize;
outFile.println("<tr>");
outFile.println("<td><a href=\"" + drillDownFileName
+ "\" target=\"_top\">" + codeType + "</a></td>");
outFile.println("<td class=\"soyc-bargraph-col\">");
outFile.println("<div class=\"soyc-bar-graph goog-inline-block\">");
// CHECKSTYLE_OFF
outFile.println("<div style=\"width:" + (perc * 100.0)
+ "%;\" class=\"soyc-bar-graph-fill goog-inline-block\"></div>");
// CHECKSTYLE_ON
outFile.println("</div>");
outFile.println("</td>");
outFile.println("<td class=\"soyc-numerical-col\">");
outFile.println(bytesFormatter.format(size));
outFile.println("</td>");
outFile.println("<td class=\"soyc-percent-col\">" +
percentFormatter.format(perc) + "</td>");
outFile.println("</tr>");
}
}
outFile.println("</table>");
int stringSize = nameToLitColl.get("string").size;
String drillDownFileName = breakdown.getId() + "_string-"
+ getPermutationId() + "Lits.html";
outFile.println("<p class=\"soyc-breakdown-strings\">" + stringSize
+ " bytes occupied by <a href=\"" + drillDownFileName
+ "\" target=\"_top\">Strings</a></p>");
int unaccountedForSize = breakdown.sizeAllCode - sumSize - stringSize;
outFile.println("<p class=\"soyc-breakdown-strings\">"
+ unaccountedForSize
+ " bytes of the JavaScript output cannot be attributed to any package or code type.</p>");
addStandardHtmlEnding(outFile);
outFile.close();
return outFileName;
}
private void makeCompilerMetricsAstTypes(String outFileName,
PrecompilationMetricsArtifact precompilationMetrics, String helpText)
throws IOException {
PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
String title = "AST Types";
addStandardHtmlProlog(outFile, title, title, "");
outFile.println("<p>");
outFile.println(helpText);
outFile.println("</p>");
outFile.println("<pre>");
String[] types = precompilationMetrics.getAstTypes();
Arrays.sort(types);
for (String type : types) {
outFile.println(type);
}
outFile.println("</pre>");
addStandardHtmlEnding(outFile);
outFile.close();
}
private void makeCompilerMetricsFinalTypeOracleTypes(String outFileName,
PrecompilationMetricsArtifact precompilationMetrics, String helpText)
throws IOException {
PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
String title = "Final Type Oracle Types";
addStandardHtmlProlog(outFile, title, title, "");
outFile.println("<p>");
outFile.println(helpText);
outFile.println("</p>");
outFile.println("<pre>");
String[] types = precompilationMetrics.getFinalTypeOracleTypes();
Arrays.sort(types);
for (String type : types) {
outFile.println(type);
}
outFile.println("</pre>");
addStandardHtmlEnding(outFile);
outFile.close();
}
private void makeCompilerMetricsGeneratedTypes(String outFileName,
String[] generatedTypes, String helpText) throws IOException {
PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
String title = "Generated Types";
addStandardHtmlProlog(outFile, title, title, "");
outFile.println("<p>");
outFile.println(helpText);
outFile.println("</p>");
outFile.println("<pre>");
for (String type : generatedTypes) {
outFile.println(type);
}
outFile.println("</pre>");
addStandardHtmlEnding(outFile);
outFile.close();
}
private void makeCompilerMetricsInitialTypeOracleTypes(String outFileName,
ModuleMetricsArtifact moduleMetrics, String helpText) throws IOException {
PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
String title = "Initial Type Oracle Types (built from source path)";
addStandardHtmlProlog(outFile, title, title, "");
outFile.println("<p>");
outFile.println(helpText);
outFile.println("</p>");
outFile.println("<pre>");
String[] types = moduleMetrics.getInitialTypes();
Arrays.sort(types);
for (String type : types) {
outFile.println(type);
}
outFile.println("</pre>");
addStandardHtmlEnding(outFile);
outFile.close();
}
private void makeCompilerMetricsSources(String outFileName,
ModuleMetricsArtifact moduleMetrics, String helpText) throws IOException {
PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
String title = "Sources on Source Path";
addStandardHtmlProlog(outFile, title, title, "");
outFile.println("<p>");
outFile.println(helpText);
outFile.println("</p>");
outFile.println("<pre>");
String[] sources = moduleMetrics.getSourceFiles();
Arrays.sort(sources);
for (String source : sources) {
outFile.println(source);
}
outFile.println("</pre>");
addStandardHtmlEnding(outFile);
outFile.close();
}
private void makeCompilerMetricsUnreferencedTypes(String outFileName,
String[] unreferencedTypes, String helpText) throws IOException {
PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
String title = "Unreferenced Types";
addStandardHtmlProlog(outFile, title, title, "");
outFile.println("<p>");
outFile.println(helpText);
outFile.println("</p>");
outFile.println("<pre>");
for (String type : unreferencedTypes) {
outFile.println(type);
}
outFile.println("</pre>");
addStandardHtmlEnding(outFile);
outFile.close();
}
/**
* Produces an HTML file that displays dependencies.
*
* @param depGraphName name of dependency graph
* @param dependencies map of dependencies
* @throws IOException
*/
private void makeDependenciesHtml(String depGraphName,
Map<String, String> dependencies) throws IOException {
String curPackageName = "";
HtmlInterner interner = new HtmlInterner();
for (String reportMethod : dependencies.keySet()) {
interner.intern(getPackageSubstring(reportMethod));
interner.intern(getClassSubstring(reportMethod));
interner.intern(getMethodSubstring(reportMethod));
String depMethod = dependencies.get(reportMethod);
while (depMethod != null) {
interner.intern(getPackageSubstring(depMethod));
interner.intern(getClassSubstring(depMethod));
interner.intern(getMethodSubstring(depMethod));
depMethod = dependencies.get(depMethod);
}
}
interner.freeze();
// Write out the interned data values as a script element
String jsFileName = "methodDependencies-" + depGraphName + "-"
+ getPermutationId() + ".js";
PrintWriter outFile = new PrintWriter(getOutFile(jsFileName));
interner.printInternedDataAsJs(outFile);
outFile.close();
List<String> classesInPackage = new ArrayList<String>();
for (String method : dependencies.keySet()) {
classesInPackage.add(method);
}
makeDependenciesInternedHtml(depGraphName,
classesInPackage, dependencies, interner, jsFileName);
}
/**
* Produces an HTML file that displays dependencies.
*
* @param depGraphName name of dependency graph
* @param dependencies map of dependencies
* @throws IOException
*/
private void makeDependenciesInternedHtml(String depGraphName,
List<String> classesInSplitPoint,
Map<String, String> dependencies, HtmlInterner interner, String jsFileName)
throws IOException {
String depGraphDescription = inferDepGraphDescription(depGraphName);
PrintWriter outFile = null;
String curClassName = "";
String outFileName = dependenciesFileName(depGraphName);
outFile = new PrintWriter(getOutFile(outFileName));
addStandardHtmlProlog(outFile, "Method Dependencies for "
+ depGraphDescription,
"Method Dependencies for " + depGraphDescription, null);
outFile.print("<script src=\"" + jsFileName
+ "\" language=\"javascript\" ></script>");
// Write out the HTML
outFile.print("<script>");
for (String method : classesInSplitPoint) {
// this key set is already in alphabetical order
// get the package of this method, i.e., everything up to .[A-Z]
String className = method.replaceAll("::.*", "");
String depMethod = dependencies.get(method);
if (curClassName.compareTo(className) != 0) {
curClassName = className;
if (method != classesInSplitPoint.get(0)) {
outFile.print("j();"); // close the previous table if not the first
}
interner.printDependencyClassHeader(outFile, className);
}
interner.printDependency(outFile, dependencies, method, depMethod);
}
outFile.println("</script>");
addStandardHtmlEnding(outFile);
outFile.close();
}
/**
* Produces an HTML file that shows information about a package.
*
* @param breakdown
* @return the name of the HTML file
*/
private String makePackageHtml(SizeBreakdown breakdown) throws IOException {
String outFileName = breakdown.getId() + "-" + getPermutationId() + "-"
+ "packageBreakdown.html";
Map<String, Integer> packageToPartialSize = breakdown.packageToSize;
TreeMap<Integer, Set<String>> sortedPackages = new TreeMap<Integer, Set<String>>(
Collections.reverseOrder());
float sumSize = 0f;
for (String packageName : packageToPartialSize.keySet()) {
Integer curSize = packageToPartialSize.get(packageName);
if (sortedPackages.containsKey(curSize)) {
Set<String> existingSet = sortedPackages.get(curSize);
existingSet.add(packageName);
sortedPackages.put(curSize, existingSet);
} else {
Set<String> newSet = new TreeSet<String>();
newSet.add(packageName);
sortedPackages.put(curSize, newSet);
}
sumSize += curSize;
}
final PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
addSmallHtmlProlog(outFile, "Package breakdown");
outFile.println("<body class=\"soyc-breakdown\">");
outFile.println("<div class=\"g-doc\">");
outFile.println("<table class=\"soyc-table\">");
outFile.println("<colgroup>");
outFile.println("<col id=\"soyc-splitpoint-type-col\">");
outFile.println("<col id=\"soyc-splitpoint-size-col\">");
outFile.println("</colgroup>");
outFile.println("<thead>");
outFile.println("<th>Packages (Sorted by size)</th>");
outFile.println("<th></th>");
outFile.println("<th class=\"soyc-numerical-col-header\">");
outFile.println("Size <span class=\"soyc-th-units\">(Bytes)</span>");
outFile.println("</th>");
outFile.println("<th class=\"soyc-numerical-col-header\">% of total</th>");
outFile.println("</thead>");
NumberFormat bytesFormatter = NumberFormat.getInstance();
bytesFormatter.setGroupingUsed(true);
NumberFormat percentFormatter = NumberFormat.getPercentInstance();
percentFormatter.setMinimumFractionDigits(1);
percentFormatter.setMaximumFractionDigits(1);
for (int size : sortedPackages.keySet()) {
if (size == 0) {
continue;
}
Set<String> packageNames = sortedPackages.get(size);
for (String packageName : packageNames) {
String drillDownFileName = classesInPackageFileName(breakdown,
getPermutationId())
+ "#" + hashedFilenameFragment(packageName);
float perc = size / sumSize;
outFile.println("<tr>");
outFile.println("<td><a href=\"" + drillDownFileName
+ "\" target=\"_top\">" + packageName + "</a></td>");
outFile.println("<td class=\"soyc-bargraph-col\">");
outFile.println("<div class=\"soyc-bar-graph goog-inline-block\">");
// CHECKSTYLE_OFF
outFile.println("<div style=\"width:" + (perc * 100.0)
+ "%;\" class=\"soyc-bar-graph-fill goog-inline-block\"></div>");
// CHECKSTYLE_ON
outFile.println("</div>");
outFile.println("</td>");
outFile.println("<td class=\"soyc-numerical-col\">");
outFile.println(bytesFormatter.format(size));
outFile.println("</td>");
outFile.println("<td class=\"soyc-percent-col\">" +
percentFormatter.format(perc) + "</td>");
outFile.println("</tr>");
}
}
outFile.println("</table>");
addStandardHtmlEnding(outFile);
outFile.close();
return outFileName;
}
/**
* Find which split points include code belonging to <code>className</code>.
*/
private Iterable<Integer> splitPointsWithClass(String className) {
List<Integer> sps = new ArrayList<Integer>();
for (int sp = 1; sp <= globalInformation.getNumSplitPoints(); sp++) {
Map<String, Integer> classToSize = globalInformation.splitPointCodeBreakdown(sp).classToSize;
if (classToSize.containsKey(className)) {
sps.add(sp);
}
}
return sps;
}
private String splitStatusFileName() {
return "splitStatus-" + getPermutationId() + ".html";
}
}