/* * 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(\"</table></div>\");"); 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"; } }