/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package org.visage.tools.framework;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.DefaultCategoryDataset;
/**
*
* @author ksrini
*/
public class Reporter {
final Map<String, ResultData> result12;
final Map<String, ResultData> result13;
final Map<String, ResultData> goals;
final Map<String, ResultData> result;
final Map<String, ResultData> last;
static final String TIMING_RESULT_FILE_NAME = "timing-result.html";
static final String FOOTPRINT_RESULT_FILE_NAME = "footprint-result.html";
static final String COLOR_KEY_FILE_NAME = "color_key.html";
static final String COMBINED_REPORT_FILE_NAME = "index.html";
static final String PLOT_FILE_TAIL = "-plot.png";
static final String TIMING_GOAL_PLOT_FILE_NAME_TAIL =
"-timing-goal" + PLOT_FILE_TAIL;
static final String TIMING_LAST_PLOT_FILE_NAME_TAIL =
"-timing-last" + PLOT_FILE_TAIL;
static final String FOOTPRINT_GOAL_PLOT_FILE_NAME_TAIL =
"-footprint-goal" + PLOT_FILE_TAIL;
static final String FOOTPRINT_LAST_PLOT_FILE_NAME_TAIL =
"-footprint-last" + PLOT_FILE_TAIL;
static final String IMAGE_DIRNAME = "images";
public Reporter() {
result12 = Utils.readResults12Csv();
result13 = Utils.readResults13Csv();
goals = Utils.readGoalsCsv();
result = Utils.readCurrentResultsCsv();
last = Utils.readLastBuildCsv();
}
private String getPerformanceValue(Map<String, ResultData> in, String key) {
return getValue(true, in, key);
}
private String getHeapsizeValue(Map<String, ResultData> in, String key) {
return getValue(false, in, key);
}
private String getValue(boolean performance, Map<String,
ResultData> in, String key) {
if (in == null) {
return ResultData.DEFAULT_VALUE;
}
ResultData rd = in.get(key);
if (rd == null || ((performance) ? rd.getPerformance() : rd.getHeapsize()) == null) {
return ResultData.DEFAULT_VALUE;
} else {
return (performance) ? rd.getPerformance() : rd.getHeapsize();
}
}
private String nameAsHref(String name) {
return "<a href=" + Utils.getBenchmarkSourceLink(name) +
">" + name + "</a>";
}
private Float percentChangeTiming(Map<String, ResultData> in1,
Map<String, ResultData> in2, String key) {
return percentChange0(true, in1, in2, key);
}
private Float percentChangeHeap(Map<String, ResultData> in1,
Map<String, ResultData> in2, String key) {
return percentChange0(false, in1, in2, key);
}
private Float percentChange0(boolean performance,
Map<String, ResultData> in1, Map<String, ResultData> in2, String key) {
float f1 = 0;
float f2 = 0;
ResultData rd = null;
if (in1 != null) {
rd = in1.get(key);
f1 = (float) ((rd == null)
? 0.0
: Float.parseFloat((performance ? rd.getPerformance() : rd.getHeapsize())));
}
if (in2 != null) {
rd = in2.get(key);
f2 = (float) ((rd == null)
? 0.0
: Float.parseFloat((performance ? rd.getPerformance() : rd.getHeapsize())));
}
float change = (f1 - f2) / f1 * 100;
return Float.parseFloat(String.format("%10.2f", change));
}
private String getImageRef(String refName, String name) {
StringBuilder sb = new StringBuilder();
sb.append("<a href=\"" + refName + "\" target=\"_blank\">");
sb.append("<img src=\"" + IMAGE_DIRNAME + "/" + name +
"\" width=\"150\" height=\"50\" border=\"0\" alt=\"\"/>");
sb.append("</a>");
return sb.toString();
}
private String generateImage(String refName, String name, Number changeFactor) {
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
dataset.addValue(changeFactor, 0, 0);
JFreeChart chart = ChartFactory.createBarChart("", "", "%change", dataset, PlotOrientation.HORIZONTAL, false, false, false);
try {
Color bgcolor = null;
double value = changeFactor.doubleValue();
if (value == Double.POSITIVE_INFINITY || value == Double.NEGATIVE_INFINITY) {
bgcolor = Color.YELLOW;
} else if ( value > 5) {
bgcolor = Color.GREEN;
} else if ( value >= -5 && value <= 5) {
bgcolor = Color.WHITE;
} else {
bgcolor = Color.RED;
}
chart.setBackgroundPaint(bgcolor);
File dirFile = new File(IMAGE_DIRNAME);
if (!dirFile.exists()) {
dirFile.mkdirs();
}
File ofile = new File(dirFile, name);
ChartUtilities.saveChartAsPNG(ofile, chart, 300, 100);
return getImageRef(refName, name);
} catch (IOException ioe) {
Utils.logger.severe(ioe.getMessage());
}
return null;
}
String detailsTable(String header) {
StringBuilder sb = new StringBuilder();
sb.append(" <TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0>");
sb.append(" <TR VALIGN=\"TOP\">");
sb.append(" <TH COLSPAN=8><P><B>" + header + "</B></P></TH>");
sb.append(" </TR>");
sb.append(" <TR>");
sb.append(" <TH><P><B>Benchmark</B></P></TH>");
sb.append(" <TD><P><B>Visage 1.2.1</B></P></TD>");
sb.append(" <TD><P><B>Visage 1.3</B></P></TD>");
sb.append(" <TD><P><B>Goal</B></P></TD>");
sb.append(" <TD><P><B>Last</B></P></TD>");
sb.append(" <TD><P><B>Current</B></P></TD>");
sb.append(" <TD><P><B>%change this build to goal</B></P></TD>");
sb.append(" <TD><P><B>%change this build to last build</B></P></TD>");
sb.append(" </TR>");
return sb.toString();
}
String dashboardTable(String header) {
StringBuilder sb = new StringBuilder();
sb.append(legendTable());
sb.append(" <TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0>");
sb.append(" <TR VALIGN=\"TOP\">");
sb.append(" <TH COLSPAN=3><P><B>" + header + "</B></P></TH>");
sb.append(" </TR>");
sb.append(" <TR>");
sb.append(" <TH><P><B>Benchmark</B></P></TH>");
sb.append(" <TD><P><B>Current build vs. Goal</B></P></TD>");
sb.append(" <TD><P><B>Current build vs. Last</B></P></TD>");
sb.append(" </TR>");
return sb.toString();
}
private void printTimingReport() {
HtmlWriter timingReport = null;
HtmlWriter dashboardTimingReport = null;
try {
timingReport = new HtmlWriter(TIMING_RESULT_FILE_NAME,
detailsTable("Timing in milliseconds"));
dashboardTimingReport = new HtmlWriter("timing-dashboard.html",
dashboardTable("Timing"));
Set<String> kset = new TreeSet<String>(result.keySet());
for (String x : kset) {
Float currentToGoal = percentChangeTiming(goals, result, x);
Float currentToLast = percentChangeTiming(last, result, x);
// print to details table
timingReport.writeToHtmlTable(nameAsHref(x),
getPerformanceValue(result12, x),
getPerformanceValue(result13, x),
getPerformanceValue(goals, x),
getPerformanceValue(last, x),
getPerformanceValue(result, x),
currentToGoal.toString(),
currentToLast.toString());
// generate images and print to dashboard
dashboardTimingReport.writeToHtmlTable(nameAsHref(x),
generateImage(TIMING_RESULT_FILE_NAME,
x + TIMING_GOAL_PLOT_FILE_NAME_TAIL, currentToGoal),
generateImage(TIMING_RESULT_FILE_NAME,
x + TIMING_LAST_PLOT_FILE_NAME_TAIL, currentToLast));
}
} catch (IOException ioe) {
Utils.logger.severe(ioe.toString());
} finally {
Utils.close(dashboardTimingReport);
Utils.close(timingReport);
}
}
private void printHeapReport() {
HtmlWriter footprintReport = null;
HtmlWriter dashboardFootprintReport = null;
try {
footprintReport = new HtmlWriter(FOOTPRINT_RESULT_FILE_NAME,
detailsTable("Footprint in MBytes"));
dashboardFootprintReport = new HtmlWriter("footprint-dashboard.html",
dashboardTable("Footprint"));
Set<String> kset = new TreeSet<String>(result.keySet());
for (String x : kset) {
Float currentToGoal = percentChangeHeap(goals, result, x);
Float currentToLast = percentChangeHeap(last, result, x);
footprintReport.writeToHtmlTable(nameAsHref(x),
getHeapsizeValue(result12, x),
getHeapsizeValue(result13, x),
getHeapsizeValue(goals, x),
getHeapsizeValue(last, x),
getHeapsizeValue(result, x),
currentToGoal.toString(),
currentToLast.toString());
// generate images and print to dashboard
dashboardFootprintReport.writeToHtmlTable(nameAsHref(x),
generateImage(FOOTPRINT_RESULT_FILE_NAME,
x + FOOTPRINT_GOAL_PLOT_FILE_NAME_TAIL, currentToGoal),
generateImage(FOOTPRINT_RESULT_FILE_NAME,
x + FOOTPRINT_LAST_PLOT_FILE_NAME_TAIL, currentToLast));
}
} catch (IOException ioe) {
Utils.logger.severe(ioe.toString());
} finally {
Utils.close(footprintReport);
Utils.close(dashboardFootprintReport);
}
}
StringBuilder legendTable() {
StringBuilder sb = new StringBuilder();
sb.append("<TABLE BORDER=1>\n");
sb.append("<TR>\n");
sb.append("<TD><a href=" + COLOR_KEY_FILE_NAME + ">Color Key</a></TD>\n");
sb.append("</TR>\n");
sb.append("<TR>\n");
sb.append("<TD><a href=hg_tip.html>Mercurial Tip pertaining to this result</a></TD>\n");
sb.append("</TR>\n");
sb.append("</TABLE>\n");
sb.append("<P>\n");
return sb;
}
String combinedHeader() {
StringBuilder sb = new StringBuilder();
sb.append(legendTable());
sb.append(" <TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0>\n");
sb.append(" <TR VALIGN=\"TOP\">\n");
sb.append(" <TH COLSPAN=5><P><B>Performance Report</B></P></TH>\n");
sb.append(" </TR>\n");
sb.append(" <TR>\n");
sb.append(" <TH COLSPAN=1 ROWSPAN=2><P><B>Benchmark</B></P></TH>\n");
sb.append(" <TH COLSPAN=2><P><B>Timing</B></P></TH>\n");
sb.append(" <TH COLSPAN=2><P><B>Footprint</B></P></TH>\n");
sb.append(" </TR>\n");
sb.append(" <TR>\n");
sb.append(" <TH COLSPAN=1>Current vs. Goal</TH>\n");
sb.append(" <TH COLSPAN=1>Current vs. Last</TH>\n");
sb.append(" <TH COLSPAN=1>Current vs. Goal</TH>\n");
sb.append(" <TH COLSPAN=1>Current vs. Last</TH>\n");
sb.append(" </TR>\n");
return sb.toString();
}
private void printCombinedReport() {
HtmlWriter combinedReport = null;
try {
combinedReport = new HtmlWriter(COMBINED_REPORT_FILE_NAME,
combinedHeader());
Set<String> kset = new TreeSet<String>(result.keySet());
for (String x : kset) {
// generate images and print to dashboard
combinedReport.writeToHtmlTable(nameAsHref(x),
getImageRef(TIMING_RESULT_FILE_NAME,
x + TIMING_GOAL_PLOT_FILE_NAME_TAIL),
getImageRef(TIMING_RESULT_FILE_NAME,
x + TIMING_LAST_PLOT_FILE_NAME_TAIL),
getImageRef(FOOTPRINT_RESULT_FILE_NAME,
x + FOOTPRINT_GOAL_PLOT_FILE_NAME_TAIL),
getImageRef(FOOTPRINT_RESULT_FILE_NAME,
x + FOOTPRINT_LAST_PLOT_FILE_NAME_TAIL)
);
}
} catch (IOException ioe) {
Utils.logger.severe(ioe.toString());
} finally {
Utils.close(combinedReport);
}
}
private void printColorKey() {
File pkFile = new File(COLOR_KEY_FILE_NAME);
if (pkFile.exists()) return;
HtmlWriter ckWriter = null;
try {
ckWriter = new HtmlWriter(COLOR_KEY_FILE_NAME);
ckWriter.writeToHtml("<TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0><TR VALIGN=\"RIGHT\">");
ckWriter.writeToHtml("<TH COLSPAN=1><P style=\"font-family:times\"><B>Color key</B></P></TH>");
ckWriter.writeToHtml("<TR>");
ckWriter.writeToHtml("<P style=\"font-family:times\">");
ckWriter.writeToHtml("<TD>");
ckWriter.writeToHtml("White: neutral +- 5%<BR>");
ckWriter.writeToHtml("Green: above 5% improvement<BR>");
ckWriter.writeToHtml("Red: below 5% regression<BR>");
ckWriter.writeToHtml("Yellow: data invalid or unavailable<BR>");
ckWriter.writeToHtml("</TD>");
ckWriter.writeToHtml("</P>");
ckWriter.writeToHtml("</TR>");
ckWriter.writeToHtml("</TABLE>");
} catch (IOException ignore) {
} finally {
Utils.close(ckWriter);
}
}
void printToHtml() {
printColorKey();
printTimingReport();
printHeapReport();
printCombinedReport();
}
public static void main(String... args) {
Reporter r = new Reporter();
r.printToHtml();
}
}