perf_34x - 186549+201920
diff --git a/bundles/org.eclipse.test.performance.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.test.performance.ui/META-INF/MANIFEST.MF
index 1af5e3f..90da67c 100644
--- a/bundles/org.eclipse.test.performance.ui/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.test.performance.ui/META-INF/MANIFEST.MF
@@ -12,6 +12,7 @@
  org.eclipse.test.performance,
  org.eclipse.swt,
  org.apache.derby;resolution:=optional,
+ Cloudscape;resolution:=optional,
  org.junit
 Bundle-RequiredExecutionEnvironment: J2SE-1.4
 Export-Package: org.eclipse.test.performance.ui
diff --git a/bundles/org.eclipse.test.performance.ui/doc/help.html b/bundles/org.eclipse.test.performance.ui/doc/help.html
new file mode 100644
index 0000000..03eefba
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/help.html
@@ -0,0 +1,102 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+<style type="text/css">p, table, td, th {  font-family: arial, helvetica, geneva; font-size: 10pt}
+pre {  font-family: "Courier New", Courier, mono; font-size: 10pt}
+h2 { font-family: arial, helvetica, geneva; font-size: 18pt; font-weight: bold ; line-height: 14px}
+code {  font-family: "Courier New", Courier, mono; font-size: 10pt}
+sup {  font-family: arial,helvetica,geneva; font-size: 10px}
+h3 {  font-family: arial, helvetica, geneva; font-size: 14pt; font-weight: bold}
+li {  font-family: arial, helvetica, geneva; font-size: 10pt}
+h1 {  font-family: arial, helvetica, geneva; font-size: 28px; font-weight: bold}
+body {  font-family: arial, helvetica, geneva; font-size: 10pt; clip:   rect(   ); margin-top: 5mm; margin-left: 3mm}
+.indextop { font-size: x-large;; font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: bold}
+.indexsub { font-size: xx-small;; font-family: Arial, Helvetica, sans-serif; color: #8080FF}
+</style>
+<body>
+<p>
+Since 3.5, it's possible to see results in fingerprints with three different
+kinds of scale:
+<ul>
+<li><a href="#percent">Percentage scale</a></li>
+<li><a href="#time">Time linear scale</a></li>
+<li><a href="#time">Time logarithmic scale</a></li>
+<li><a href="#tips">Tips for time scales</a></li>
+</ul>
+</p>
+<h1><a name="percent">Percentage scale</h1>
+<p>The X axis represents percentage of the variation vs. the given baseline</p>
+<p>This is the way fingerprints were displayed since the beginning:
+<p><img src="images/percentage.png">
+<ul>
+<li>Red bar means a regression, even if it's less than the 10% threshold.</li>
+<li>Green bar means an improvement</li>
+<li>Gray bar means an 'explained' regression.</li>
+</ul>
+</p>
+<h1><a name="time">Linear and logarithmic time scales</h1>
+<p>For these scales, the X axis represents the duration time of the test.<br>
+The colors meanings are the same than for the percentage scale.<br>
+These kind of graphs give a better idea of time duration for each test.</p>
+<p>Typically use linear scale if you want to see the tests relativeness for all the component tests:</p>
+<p><img src="images/linear.png">
+<p>But the logarithmic scale is more appropriate when there are a strong duration differences between tests, hence makes short duration tests easier to survey:</p>
+<p><img src="images/log.png">
+<p>Each test have two bars: the former is white and shows the baseline result, the latter is colored (red, green or gray) and shows the current build result.<br>
+The variation between the baseline and the build is displayed as a percentage on top of both bars.</p>
+<h1><a name="tips">Tips for time scales</h1>
+<p>Tips are almost the same for linear and logarithmic scales:</p>
+<table border="0">
+  <tr>
+    <td valign="top"><img src="images/light.gif"></td>
+    <td><b>Flying over a bar displays its time value<b>:</td></tr>
+  </tr>
+  <tr>
+    <td></td>
+    <td><img src="images/help_time_baseline.png"></td>
+  </tr>
+  <tr>
+    <td></td>
+    <td><img src="images/help_time_current.png"></td>
+  </tr>
+  <tr><td><br></td></tr>
+  <tr>
+    <td valign="top"><img src="images/light.gif"></td>
+    <td><b>For <u>linear scale only</u>, when the error on the time result is noticeable,
+		then the measurement uncertainty is shown in yellow at the end of the bar<b>:</td></tr>
+  </tr>
+  <tr>
+    <td></td>
+    <td><img src="images/help_time_error.png"></td>
+  </tr>
+  <tr><td><br></td></tr>
+  <tr>
+    <td valign="top"><img src="images/light.gif"></td>
+    <td><b>A performance regression may sometimes have a known good reason</b>.<br>
+    In this case, the current build bar is grayed and flying over it also shows the given explanation:</td></tr>
+  </tr>
+  <tr>
+    <td></td>
+    <td><img src="images/help_explained_regression.png"></td>
+  </tr>
+  <tr><td><br></td></tr>
+  <tr>
+    <td valign="top"><img src="images/light.gif"></td>
+    <td><b>Test result may have big error which can make the test result not fully reliable</b>.<br>
+  In this case, a warning icon is shown after the variation value and flying over it gives the offending error value:</td></tr>
+  </tr>
+  <tr>
+    <td></td>
+    <td><img src="images/help_error_warning.png"></td>
+  </tr>
+  <tr><td><br></td></tr>
+  <tr>
+    <td valign="top"><img src="images/light.gif"></td>
+    <td><b>Test may have no result for the used baseline, hence the first available build is used as a reference</b>.<br>
+  In this case, a warning icon is shown after the scenario title and flying over it gives the build ID used to compute the variation:</td></tr>
+  </tr>
+  <tr>
+    <td></td>
+    <td><img src="images/help_no_baseline.png"></td>
+  </tr>
+</table>
+</body>
+</html>
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/help_error_warning.png b/bundles/org.eclipse.test.performance.ui/doc/images/help_error_warning.png
new file mode 100644
index 0000000..399acaf
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/help_error_warning.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/help_explained_regression.png b/bundles/org.eclipse.test.performance.ui/doc/images/help_explained_regression.png
new file mode 100644
index 0000000..a7b7c84
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/help_explained_regression.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/help_no_baseline.png b/bundles/org.eclipse.test.performance.ui/doc/images/help_no_baseline.png
new file mode 100644
index 0000000..f06762e
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/help_no_baseline.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/help_time_baseline.png b/bundles/org.eclipse.test.performance.ui/doc/images/help_time_baseline.png
new file mode 100644
index 0000000..594fdf2
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/help_time_baseline.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/help_time_current.png b/bundles/org.eclipse.test.performance.ui/doc/images/help_time_current.png
new file mode 100644
index 0000000..5e913cb
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/help_time_current.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/help_time_error.png b/bundles/org.eclipse.test.performance.ui/doc/images/help_time_error.png
new file mode 100644
index 0000000..2ce272d
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/help_time_error.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/light.gif b/bundles/org.eclipse.test.performance.ui/doc/images/light.gif
new file mode 100644
index 0000000..11af180
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/light.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/linear.png b/bundles/org.eclipse.test.performance.ui/doc/images/linear.png
new file mode 100644
index 0000000..cd276a9
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/linear.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/log.png b/bundles/org.eclipse.test.performance.ui/doc/images/log.png
new file mode 100644
index 0000000..7f2e84a
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/log.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/doc/images/percentage.png b/bundles/org.eclipse.test.performance.ui/doc/images/percentage.png
new file mode 100644
index 0000000..6f965da
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/doc/images/percentage.png
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/images/light.gif b/bundles/org.eclipse.test.performance.ui/images/light.gif
new file mode 100644
index 0000000..11af180
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/images/light.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/images/warning_obj.gif b/bundles/org.eclipse.test.performance.ui/images/warning_obj.gif
new file mode 100644
index 0000000..2b2e50f
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/images/warning_obj.gif
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/performanceui.jar b/bundles/org.eclipse.test.performance.ui/performanceui.jar
index 7760695..9e8eddc 100644
--- a/bundles/org.eclipse.test.performance.ui/performanceui.jar
+++ b/bundles/org.eclipse.test.performance.ui/performanceui.jar
Binary files differ
diff --git a/bundles/org.eclipse.test.performance.ui/scripts/Fingerprints.js b/bundles/org.eclipse.test.performance.ui/scripts/Fingerprints.js
new file mode 100644
index 0000000..d1914e9
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/scripts/Fingerprints.js
@@ -0,0 +1,32 @@
+function toggleFingerprints() {
+	var formSelect=document.forms[0].elements[0];
+	var type=formSelect.selectedIndex;
+	var idx=document.URL.indexOf("php?");
+	if (idx==-1) {
+		window.open(document.URL+"?fp_type="+type, "_self");
+	} else {
+		window.open(document.URL.substring(0,idx)+"php?fp_type="+type, "_self");
+	}
+}
+
+function setFingerprintsType() {
+	var idx=document.URL.indexOf("?");
+	var type=0;
+	if (idx != -1) {
+		var typeStr=document.URL.substring(idx+1, document.URL.length);
+		idx=typeStr.indexOf("=");
+		if (idx != -1) {
+			var ch=typeStr.substring(idx+1, idx+2)
+			switch (ch) {
+				case '1':
+					type=1;
+					break;
+				case '2':
+					type=2;
+					break;
+			}
+		}
+	}
+	var formSelect=document.forms[0].elements[0];
+	formSelect.selectedIndex=type;
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/BarGraph.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/BarGraph.java
index 2dc79d0..a5cf5a1 100644
--- a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/BarGraph.java
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/BarGraph.java
@@ -273,7 +273,7 @@
 			if (hasURL) {
 				if (fAreaBuffer == null)
 					fAreaBuffer= new StringBuffer();
-				fAreaBuffer.append("<area shape=\"RECT\" coords=\"0," + y0 + ',' + width + ',' + y + "\" href=\"" + bar.url + "\">\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+				fAreaBuffer.append("		echo '<area shape=\"RECT\" coords=\"0," + y0 + ',' + width + ',' + y + "\" href=\"" + bar.url + "\">';\n");
 			}
 		}
 
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/FingerPrint.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/FingerPrint.java
index 054f65a..3ada494 100644
--- a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/FingerPrint.java
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/FingerPrint.java
@@ -17,6 +17,7 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintStream;
+import java.util.ArrayList;
 import java.util.List;
 
 import org.eclipse.swt.SWT;
@@ -42,7 +43,7 @@
 	File outputDir;
 
 public FingerPrint(String name, PrintStream ps, File outputDir) {
-	if (!name.equals("global")) this.component = name;
+	if (!name.startsWith("global")) this.component = name;
 	this.stream = ps;
 	this.outputDir = outputDir;
 }
@@ -74,8 +75,35 @@
 	buffer.append(baselinePrefix);
 	buffer.append('_');
 	buffer.append(buildName);
-	buffer.append('.');
 	String filePrefix = buffer.toString();
+	
+	// Print the legend
+	this.stream.print("The following fingerprints show results for the most representative tests of the ");
+	if (this.component == null) {
+		this.stream.print("current build.<br>\n");
+	} else {
+		this.stream.print(component);
+		this.stream.print(" component.<br>\n");
+	}
+	this.stream.print("<table border=\"0\">\n");
+	this.stream.print("<tr><td valign=\"top\">Select which kind of scale you want to use:</td>\n");
+	this.stream.print("<td valign=\"top\">\n");
+	this.stream.print("  <form>\n");
+	this.stream.print("    <select onChange=\"toggleFingerprints();\">\n");
+	this.stream.print("      <option>percentage</option>\n");
+	this.stream.print("      <option>time (linear)</option>\n");
+	this.stream.print("      <option>time (log)</option>\n");
+	this.stream.print("    </select>\n");
+	this.stream.print("  </form>\n");
+	this.stream.print("</td>\n");
+	this.stream.print("<td valign=\"top\">\n");
+	this.stream.print("<a href=\"help.html\"><img hspace=\"10\" border=\"0\" src=\"light.gif\" title=\"Some tips on fingerprints\"/></a>\n");
+	this.stream.print("</td></tr></table>\n");
+
+	// Print script to reset dropdown list selection
+	this.stream.print("<script type=\"text/javascript\">\n");
+	this.stream.print("	setFingerprintsType();\n");
+	this.stream.print("</script>\n");
 
 	// Create each fingerprint and save it
 	String[] configNames = performanceResults.getConfigNames(false/* not sorted*/);
@@ -87,17 +115,18 @@
 		if (scenarios == null) continue;
 
 		// Create BarGraph
-//		BarGraph barGraph = new BarGraph(null);
+		// TODO use FingerPrintGraph instead
 		BarGraph barGraph = null;
+		List allResults = new ArrayList();
+		String defaultDimName = AbstractResults.DEFAULT_DIM.getName();
 		for (int i=0, size=scenarios.size(); i<size; i++) {
 			ScenarioResults scenarioResults = (ScenarioResults) scenarios.get(i);
 			ConfigResults configResults = scenarioResults.getConfigResults(configName);
 			if (configResults == null || !configResults.isValid()) continue;
-			double[] results = configResults.getCurrentBuildDeviation();
+			double[] results = configResults.getCurrentBuildDeltaInfo();
 			double percent = -results[0] * 100.0;
 			if (results != null && Math.abs(percent) < 200) {
-				String defaultDimensionName = AbstractResults.SUPPORTED_DIMS[0].getName();
-				String name = scenarioResults.getLabel() + " (" + defaultDimensionName + ")";
+				String name = scenarioResults.getLabel() + " (" + defaultDimName + ")";
 				if (!configResults.getCurrentBuildName().equals(buildName)) {
 					continue; // the test didn't run on last build, skip it
 				}
@@ -109,15 +138,18 @@
 				}
 				barGraph.addItem(name,
 				    results,
-				    configName + "/" + scenarioResults.getFileName() + ".html#" + defaultDimensionName,
+				    configName + "/" + scenarioResults.getFileName() + ".html",
 				    configResults.getCurrentBuildResults().getComment(),
 				    (Utils.confidenceLevel(results) & Utils.ERR) == 0);
+
+				// add results
+				allResults.add(configResults);
 			}
 		}
-		 if (barGraph == null) continue;
+		if (barGraph == null) continue;
 
 		// Save image file
-		String fileName = filePrefix + configName ;
+		String fileName = filePrefix + '.' + configName ;
 		File outputFile = new File(this.outputDir, fileName+".gif");
 		save(barGraph, outputFile);
 
@@ -128,21 +160,32 @@
 			if (areas == null) areas = "";
 			this.stream.print("<h4>");
 			this.stream.print(boxName);
-			this.stream.print("</h4>");
-			this.stream.print("<img src=\"");
+			this.stream.print("</h4>\n");
+			this.stream.print("<?php\n");
+			this.stream.print("	if ($QUERY_STRING==\"\" || $QUERY_STRING==\"fp_type=0\") {\n");
+			this.stream.print("		echo '<img src=\"");
 			this.stream.print(fileName);
 			this.stream.print(".gif\" usemap=\"#");
 			this.stream.print(fileName);
-			this.stream.print("\"><map name=\"");
+			this.stream.print("\" name=\"");
+			this.stream.print(configName);
+			this.stream.print("\">';\n");
+			this.stream.print("		echo '<map name=\"");
 			this.stream.print(fileName);
-			this.stream.print("\">");
+			this.stream.print("\">';\n");
 			this.stream.print(areas);
-			this.stream.print("</map>\n");
+			this.stream.print("		echo '</map>';\n");
+			this.stream.print("	}\n");
 		} else {
 			this.stream.print("<br><br>There is no fingerprint for ");
 			this.stream.print(boxName);
 			this.stream.print("<br><br>\n");
 		}
+
+		// Create, paint and print the time bars graph
+		FingerPrintGraph graph = new FingerPrintGraph(this.outputDir, fileName, GRAPH_WIDTH, allResults);
+		graph.paint(this.stream);
+		this.stream.print("?>\n");
 	}
 }
 
@@ -159,6 +202,14 @@
 	barGraph.paint(display, GRAPH_WIDTH, height, gc);
 	gc.dispose();
 
+	saveImage(outputFile, image);
+}
+
+/**
+ * @param outputFile
+ * @param image
+ */
+private void saveImage(File outputFile, Image image) {
 	// Save image
 	ImageData data = Utils.downSample(image);
 	ImageLoader imageLoader = new ImageLoader();
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/FingerPrintGraph.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/FingerPrintGraph.java
new file mode 100644
index 0000000..36d4d8c
--- /dev/null
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/FingerPrintGraph.java
@@ -0,0 +1,671 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.test.performance.ui;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.Resource;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.test.internal.performance.results.AbstractResults;
+import org.eclipse.test.internal.performance.results.BuildResults;
+import org.eclipse.test.internal.performance.results.ConfigResults;
+import org.eclipse.test.internal.performance.results.ScenarioResults;
+
+/**
+ * Abstract class to build graph with bars
+ */
+public class FingerPrintGraph {
+
+	// Dimensions
+	static final String DEFAULT_DIM_NAME = AbstractResults.DEFAULT_DIM.getName();
+
+	// Sizes
+	static final int MARGIN= 5; // margin on all four sides
+	static final int BAR_HEIGHT= 6; // height of bar
+	static final int GAP= 10; // gap between bars
+	static final int TGAP= 5; // gap between lines and labels
+	static final int LINE_HEIGHT = 2*BAR_HEIGHT + GAP;
+
+	// fraction of width reserved for bar graph
+	static final double RATIO= 0.6;
+
+	// Formatting constants
+	static final NumberFormat NUMBER_FORMAT;
+	static {
+		NUMBER_FORMAT = NumberFormat.getInstance();
+		NUMBER_FORMAT.setMaximumFractionDigits(1);
+	}
+
+	// Graphic constants
+	static final Display DEFAULT_DISPLAY = Display.getDefault();
+	static final Color BLACK= DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_BLACK);
+	static final Color BLUE= DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_BLUE);
+	static final Color GREEN= DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_GREEN);
+	static final Color RED = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_RED);
+	static final Color GRAY = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_GRAY);
+	static final Color DARK_GRAY = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_DARK_GRAY);
+	static final Color YELLOW = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_YELLOW);
+	static final Color WHITE = DEFAULT_DISPLAY.getSystemColor(SWT.COLOR_WHITE);
+
+	// Bar graph kinds
+	static final int NO_TIME = 0; // i.e. percentage
+	static final int TIME_LINEAR = 1;
+	static final int TIME_LOG = 2;
+	static final int[] SUPPORTED_GRAPHS = {
+//		NO_TIME,
+		TIME_LINEAR,
+		TIME_LOG,
+	};
+
+	// Graphic fields
+	GC gc;
+	Image image;
+	int imageWidth;
+	int imageHeight;
+	int graphWidth;
+	int graphHeight;
+	Map resources = new HashMap();
+
+	// Data fields
+	int count = 0;
+	ConfigResults[] results = new ConfigResults[10];
+	BarGraphArea[] areas;
+
+	// Values
+	double maxValue = 0.0;
+	double minValue = Double.MAX_VALUE;
+	
+	// File info
+	File outputDir;
+	String imageName;
+
+	/*
+	 * Member class defining a bar graph area.
+	 * This area applies to a configuration results and is made of several zones.
+	 */
+	class BarGraphArea {
+		List zones;
+		private ConfigResults configResults;
+
+		/*
+		 * Member class defining a zone inside a bar graph area.
+		 * Typically made of a rectangle and an associated text used as tooltip.
+		 */
+		class AreaZone {
+			Rectangle zone;
+			String title;
+
+			AreaZone(Rectangle zone, String tooltip) {
+	            super();
+	            this.zone = zone;
+	            this.title = tooltip;
+            }
+
+			void print(String url, PrintStream stream) {
+				stream.print("		echo '<area shape=\"RECT\"");
+				if (this.title != null) {
+					stream.print(" title=\""+this.title+"\"");
+				}
+				stream.print("coords=\"");
+				stream.print(this.zone.x);
+				stream.print(',');
+				stream.print(this.zone.y);
+				stream.print(',');
+				stream.print(this.zone.x+this.zone.width);
+				stream.print(',');
+				stream.print(this.zone.y+this.zone.height);
+				stream.print('"');
+				if (url != null) {
+					stream.print(" href=\"");
+					stream.print(url);
+					stream.print('"');
+				}
+				stream.print(">';\n");
+			}
+		}
+
+		 BarGraphArea(ConfigResults results) {
+			this.configResults = results;
+			this.zones = new ArrayList();
+        }
+
+		void print(PrintStream stream) {
+			String url = this.configResults.getName() + "/" + ((ScenarioResults) this.configResults.getParent()).getFileName() + ".html";
+			int size = this.zones.size();
+			for (int i=0; i<size; i++) {
+				AreaZone zone = (AreaZone) this.zones.get(i);
+				zone.print(url, stream);
+			}
+		}
+
+		void addArea(Rectangle rec, String tooltip) {
+			AreaZone zone = new AreaZone(rec, tooltip);
+			this.zones.add(zone);
+		}
+
+	}
+
+
+FingerPrintGraph(File dir, String fileName, int width, List results) {
+    super();
+    this.imageWidth = width;
+    this.count = results.size();
+    this.results = new ConfigResults[this.count];
+    results.toArray(this.results);
+    this.outputDir = dir;
+    this.imageName = fileName;
+}
+
+/**
+ */
+void drawBars(int kind) {
+
+	// Get/Set graphical resources
+	Font italicFont = (Font) this.resources.get("italicFont");
+	if (italicFont == null) {
+		String fontDataName = this.gc.getFont().getFontData()[0].toString();
+		FontData fdItalic = new FontData(fontDataName);
+		fdItalic.setStyle(SWT.ITALIC);
+		italicFont = new Font(DEFAULT_DISPLAY, fdItalic);
+		this.resources.put("italicFont", italicFont);
+	}
+	Color blueref = (Color) this.resources.get("blueref");
+	if (blueref == null) {
+		blueref = new Color(DEFAULT_DISPLAY, 200, 200, 255);
+		this.resources.put("blueref", blueref);
+	}
+	Color lightyellow= (Color) this.resources.get("lightyellow");
+	if (lightyellow == null) {
+		lightyellow = new Color(DEFAULT_DISPLAY, 255, 255, 160);
+		this.resources.put("lightyellow", lightyellow);
+	}
+	Color darkyellow= (Color) this.resources.get("darkyellow");
+	if (darkyellow == null) {
+		darkyellow = new Color(DEFAULT_DISPLAY, 160, 160, 0);
+		this.resources.put("darkyellow", darkyellow);
+	}
+	Color lightgreen= (Color) this.resources.get("lightgreen");
+	if (lightgreen == null) {
+		lightgreen = new Color(DEFAULT_DISPLAY, 160, 255, 160);
+		this.resources.put("lightgreen", lightgreen);
+	}
+	Color lightred = (Color) this.resources.get("lightred");
+	if (lightred == null) {
+		lightred = new Color(DEFAULT_DISPLAY, 255, 160, 160);
+		this.resources.put("lightred", lightred);
+	}
+
+	// Build each scenario bar graph
+	this.areas = new BarGraphArea[this.count];
+	double max = kind == TIME_LOG ? Math.log(this.maxValue) : this.maxValue;
+	for (int i=0, y=MARGIN; i < this.count; i++, y+=LINE_HEIGHT) {
+
+		// get builds info
+		ConfigResults configResults = this.results[i];
+		this.areas[i] = new BarGraphArea(configResults);
+		BarGraphArea graphArea = this.areas[i];
+		BuildResults currentBuildResults = configResults.getCurrentBuildResults();
+		double currentValue = currentBuildResults.getValue();
+		double currentError = currentBuildResults.getError();
+		double error = configResults.getError();
+		boolean singleTest = Double.isNaN(error);
+		boolean isSignificant = singleTest || error < Utils.STANDARD_ERROR_THRESHOLD;
+		boolean isCommented = currentBuildResults.getComment() != null;
+		BuildResults baselineBuildResults = configResults.getBaselineBuildResults();
+		double baselineValue = baselineBuildResults.getValue();
+		double baselineError = baselineBuildResults.getError();
+
+		// draw baseline build bar
+		Color whiteref = (Color) this.resources.get("whiteref");
+		if (whiteref == null) {
+			whiteref = new Color(DEFAULT_DISPLAY, 240, 240, 248);
+			this.resources.put("whiteref", whiteref);
+		}
+		this.gc.setBackground(whiteref);
+		double baselineGraphValue = kind == TIME_LOG ? Math.log(baselineValue) : baselineValue;
+		int baselineBarLength= (int) (baselineGraphValue / max * this.graphWidth);
+		int baselineErrorLength= (int) (baselineError / max * this.graphWidth / 2);
+		int labelxpos = MARGIN + baselineBarLength;
+		if (kind == TIME_LOG || baselineErrorLength <= 1) {
+			this.gc.fillRectangle(MARGIN, y + (GAP/2), baselineBarLength, BAR_HEIGHT);
+			Rectangle rec = new Rectangle(MARGIN, y + (GAP/2), baselineBarLength, BAR_HEIGHT);
+			this.gc.drawRectangle(rec);
+			graphArea.addArea(rec, "Time for baseline build "+baselineBuildResults.getName()+": "+AbstractResults.timeString((long)baselineValue));
+		} else {
+			int wr = baselineBarLength - baselineErrorLength;
+			Rectangle recValue = new Rectangle(MARGIN, y + (GAP/2), wr, BAR_HEIGHT);
+			this.gc.fillRectangle(recValue);
+			this.gc.setBackground(YELLOW);
+			Rectangle recError = new Rectangle(MARGIN+wr, y + (GAP/2), baselineErrorLength*2, BAR_HEIGHT);
+			this.gc.fillRectangle(recError);
+			Rectangle rec = new Rectangle(MARGIN, y + (GAP/2), baselineBarLength+baselineErrorLength, BAR_HEIGHT);
+			this.gc.drawRectangle(rec);
+			StringBuffer tooltip = new StringBuffer("Time for baseline build ");
+			tooltip.append(baselineBuildResults.getName());
+			tooltip.append(": ");
+			tooltip.append(AbstractResults.timeString((long)baselineValue));
+			tooltip.append(" [&#177;");
+			tooltip.append(AbstractResults.timeString((long)baselineError));
+			tooltip.append(']');
+			graphArea.addArea(rec, tooltip.toString());
+			labelxpos += baselineErrorLength;
+		}
+
+		// set current build bar color
+		if (baselineValue < currentValue) {
+			if (isCommented) {
+				this.gc.setBackground(GRAY);
+			} else  {
+				this.gc.setBackground(RED);
+			}
+		} else {
+			this.gc.setBackground(GREEN);
+		}
+
+		// draw current build bar
+		double currentGraphValue = kind == TIME_LOG ? Math.log(currentValue) : currentValue;
+		int currentBarLength= (int) (currentGraphValue / max * this.graphWidth);
+		int currentErrorLength= (int) (currentError / max * this.graphWidth / 2);
+		if (kind == TIME_LOG || currentErrorLength <= 1) {
+			this.gc.fillRectangle(MARGIN, y + (GAP/2) + BAR_HEIGHT, currentBarLength, BAR_HEIGHT);
+			Rectangle rec = new Rectangle(MARGIN, y + (GAP/2) + BAR_HEIGHT, currentBarLength, BAR_HEIGHT);
+			this.gc.drawRectangle(rec);
+			String tooltip = "Time for current build "+currentBuildResults.getName()+": "+AbstractResults.timeString((long)currentValue);
+			if (isCommented) {
+				tooltip += ".		" + currentBuildResults.getComment();
+			}
+			graphArea.addArea(rec, tooltip);
+			if (labelxpos < (MARGIN+currentBarLength)) {
+				labelxpos = MARGIN + currentBarLength;
+			}
+		} else {
+			int wr = currentBarLength - currentErrorLength;
+			Rectangle recValue = new Rectangle(MARGIN, y + (GAP/2) + BAR_HEIGHT, wr, BAR_HEIGHT);
+			this.gc.fillRectangle(recValue);
+			this.gc.setBackground(YELLOW);
+			Rectangle recError = new Rectangle(MARGIN+wr, y + (GAP/2) + BAR_HEIGHT, currentErrorLength*2, BAR_HEIGHT);
+			this.gc.fillRectangle(recError);
+			Rectangle rec = new Rectangle(MARGIN, y + (GAP/2) + BAR_HEIGHT, currentBarLength+currentErrorLength, BAR_HEIGHT);
+			this.gc.drawRectangle(rec);
+			StringBuffer tooltip = new StringBuffer("Time for current build ");
+			tooltip.append(currentBuildResults.getName());
+			tooltip.append(": ");
+			tooltip.append(AbstractResults.timeString((long)currentValue));
+			tooltip.append(" [&#177;");
+			tooltip.append(AbstractResults.timeString((long)currentError));
+			tooltip.append(']');
+			if (isCommented) {
+				tooltip.append(".		");
+				tooltip.append(currentBuildResults.getComment());
+			}
+			graphArea.addArea(rec, tooltip.toString());
+			if (labelxpos < (MARGIN+currentBarLength+currentErrorLength)) {
+				labelxpos = MARGIN + currentBarLength+currentErrorLength;
+			}
+		}
+
+		// set delta value style and color
+		boolean hasFailure = currentBuildResults.getFailure() != null;
+		if (hasFailure) {
+			if (isCommented) {
+				this.gc.setForeground(DARK_GRAY);
+			} else  {
+				this.gc.setForeground(RED);
+			}
+		} else {
+			this.gc.setForeground(BLACK);
+		}
+
+		// draw delta value
+		double delta = -configResults.getDelta();
+		String label = delta > 0 ? "+" : "";
+		label += NUMBER_FORMAT.format(delta*100) + "%";
+		Point labelExtent= this.gc.stringExtent(label);
+		int labelvpos= y + (LINE_HEIGHT - labelExtent.y) / 2;
+		this.gc.drawString(label, labelxpos+TGAP, labelvpos, true);
+		this.gc.setForeground(BLACK);
+		this.gc.setFont(null);
+		int titleStart = (int) (RATIO * this.imageWidth);
+		if (singleTest || !isSignificant) {
+			String deltaTooltip = null;
+			if (singleTest) {
+				deltaTooltip = "This test performed only one iteration; hence its reliability cannot be assessed";
+			} else if (!isSignificant) {
+				deltaTooltip = "This test has a bad reliability: error is "+NUMBER_FORMAT.format(error*100)+"% (> 3%)!";
+			}
+			Image warning = (Image) this.resources.get("warning");
+			int xi = labelxpos+TGAP+labelExtent.x;
+			this.gc.drawImage(warning, xi, labelvpos);
+			ImageData imageData = warning.getImageData();
+			// Set zones
+			// - first one is between end of bar and warning image beginning
+			Rectangle deltaZone = new Rectangle(labelxpos, labelvpos-2, xi-labelxpos, labelExtent.y+4);
+			graphArea.addArea(deltaZone, null);
+			// - second one is the warning image
+			Rectangle warningZone = new Rectangle(xi, labelvpos, imageData.width, imageData.height);
+			graphArea.addArea(warningZone, deltaTooltip);
+			// - last one is between end of the warning image and the scenario title beginning
+			int warningImageEnd = xi+imageData.width;
+			Rectangle emptyZone = new Rectangle(warningImageEnd, labelvpos, titleStart-warningImageEnd, imageData.height);
+			graphArea.addArea(emptyZone, deltaTooltip);
+		} else {
+			// No tooltip => delta zone is between end of bar and the scenario title beginning
+			Rectangle deltaZone = new Rectangle(labelxpos, labelvpos-2, titleStart-labelxpos, labelExtent.y+4);
+			graphArea.addArea(deltaZone, null);
+		}
+
+		// set title style
+		Color oldfg= this.gc.getForeground();
+		this.gc.setForeground(BLUE);
+
+		// draw scenario title
+		int x= titleStart;
+		ScenarioResults scenarioResults = (ScenarioResults) configResults.getParent();
+		String title = scenarioResults.getLabel() + " (" + DEFAULT_DIM_NAME + ")";
+		Point e= this.gc.stringExtent(title);
+		this.gc.drawLine(x, labelvpos + e.y - 1, x + e.x, labelvpos + e.y - 1);
+		this.gc.drawString(title, x, labelvpos, true);
+		this.gc.setForeground(oldfg);
+		this.gc.setFont(null);
+		Rectangle titleZone = new Rectangle(x, labelvpos, e.x, e.y);
+		graphArea.addArea(titleZone, null/*no tooltip*/);
+		if (!configResults.isBaselined()) {
+			Image warning = (Image) this.resources.get("warning");
+			this.gc.drawImage(warning, x+e.x, labelvpos);
+			ImageData imageData = warning.getImageData();
+			Rectangle warningZone = new Rectangle(x+e.x, labelvpos, imageData.width, imageData.height);
+			String titleTooltip =  "This test has no baseline result, hence use build "+configResults.getBaselineBuildName()+" for reference!";
+			graphArea.addArea(warningZone, titleTooltip);
+		}
+	}
+}
+
+void drawLinearScale() {
+
+	// Draw scale background
+	drawScaleBackground();
+
+	// Draw scale grid lines
+	int gridValue = 100;
+	int n = (int) (this.maxValue / gridValue);
+	while (n > 10) {
+		switch (gridValue) {
+			case 100:
+				gridValue = 200;
+				break;
+			case 200:
+				gridValue = 500;
+				break;
+			case 500:
+				gridValue = 1000;
+				break;
+			default:
+				gridValue += 1000;
+				break;
+		}
+		n = (int) (this.maxValue / gridValue);
+	}
+	int gridWidth = (int) (this.graphWidth * gridValue / this.maxValue);
+	int x = MARGIN;
+	long value = 0; // TODO use minValue instead
+	while (x < this.graphWidth) {
+
+		// draw line
+		this.gc.setForeground(GRAY);
+		if (x > 0) {
+			this.gc.setLineStyle(SWT.LINE_DOT);
+			this.gc.drawLine(x, MARGIN, x, this.graphHeight + TGAP);
+		}
+
+		// draw value
+		this.gc.setForeground(BLACK);
+		String val= AbstractResults.timeString(value);
+		Point point= this.gc.stringExtent(val);
+		this.gc.drawString(val, x - point.x / 2, this.graphHeight + TGAP, true);
+
+		// compute next grid position
+		x += gridWidth;
+		value += gridValue; // value is expressed in seconds
+	}
+	this.gc.setLineStyle(SWT.LINE_SOLID);
+	this.gc.drawLine(0, this.graphHeight, this.graphWidth, this.graphHeight);
+}
+
+void drawLogarithmScale() {
+
+	// Draw scale background
+	drawScaleBackground();
+
+	// Draw scale grid lines
+	double max = Math.log(this.maxValue);
+	int gridValue = 100;
+	int x = MARGIN;
+	long value = 0; // TODO use minValue instead
+	while (x < this.graphWidth) {
+
+		// draw line
+		this.gc.setForeground(GRAY);
+		if (x > MARGIN) {
+			this.gc.setLineStyle(SWT.LINE_DOT);
+			this.gc.drawLine(x, MARGIN, x, this.graphHeight + TGAP);
+		}
+
+		// draw value
+		this.gc.setForeground(BLACK);
+		String str = AbstractResults.timeString(value);
+		Point point= this.gc.stringExtent(str);
+		this.gc.drawString(str, x - point.x / 2, this.graphHeight + TGAP, true);
+
+		// compute next grid position
+		value += gridValue;
+		int v = (int) (value / 100);
+		int c = 1;
+		while (v > 10) {
+			v = v / 10;
+			c *= 10;
+		}
+		switch (v) {
+			case 3:
+				gridValue = 200*c;
+				break;
+			case 5:
+				gridValue = 500*c;
+				break;
+			case 10:
+				gridValue = 1000*c;
+				break;
+		}
+		x = MARGIN + (int) (this.graphWidth * Math.log(value) / max);
+	}
+	this.gc.setLineStyle(SWT.LINE_SOLID);
+	this.gc.drawLine(0, this.graphHeight, this.graphWidth, this.graphHeight);
+}
+
+/**
+ * Draw the scale depending on the bar time graph kind.
+ */
+void drawScale(int kind) {
+	switch (kind) {
+		case TIME_LINEAR:
+			drawLinearScale();
+			break;
+		case TIME_LOG:
+			drawLogarithmScale();
+			break;
+	}
+}
+
+private void drawScaleBackground() {
+
+	// Draw striped background
+	Color lightblue = (Color) this.resources.get("lightblue");
+	if (lightblue == null) {
+		lightblue = new Color(DEFAULT_DISPLAY, 237, 243, 254);
+		this.resources.put("lightblue", lightblue);
+	}
+	this.gc.setBackground(lightblue);
+	for (int i= 0; i<this.count; i++) {
+		if (i % 2 == 0) {
+	        this.gc.fillRectangle(0, MARGIN + i * LINE_HEIGHT, this.imageWidth, LINE_HEIGHT);
+        }
+	}
+
+	// Draw bottom vertical line
+	int yy= MARGIN + this.count * LINE_HEIGHT;
+	this.gc.drawLine(MARGIN, MARGIN, MARGIN, yy + TGAP);
+}
+
+String getImageName(int kind) {
+	switch (kind) {
+		case TIME_LINEAR:
+			return this.imageName+"_linear";
+		case TIME_LOG:
+			return this.imageName+"_log";
+	}
+	return this.imageName;
+}
+
+void paint(int kind) {
+
+	// Set image
+	this.graphHeight = MARGIN + this.count * LINE_HEIGHT;
+	this.imageHeight = this.graphHeight + GAP + 16 + MARGIN;
+	this.image = new Image(DEFAULT_DISPLAY, this.imageWidth, this.imageHeight);
+	this.gc = new GC(this.image);
+
+	// draw white background
+	this.gc.setBackground(WHITE);
+	this.gc.fillRectangle(0, 0, this.imageWidth, this.imageHeight);
+
+	// Set widths and heights
+	int width= (int) (RATIO * this.imageWidth); // width for results bar
+	this.graphWidth= width - this.gc.stringExtent("-999.9%").x - TGAP - MARGIN; // reserve space //$NON-NLS-1$
+
+	// Get warning image width
+	Image warning = (Image) this.resources.get("warning");
+	if (warning == null) {
+		warning = new Image(this.gc.getDevice(), new File(this.outputDir, Utils.WARNING_OBJ).toString());
+		this.resources.put("warning", warning);
+	}
+	this.graphWidth -= warning.getImageData().width;
+
+	// Set maximum of values
+	for (int i= 0; i<this.count; i++) {
+		BuildResults baselineBuildResults = this.results[i].getBaselineBuildResults();
+		double value = baselineBuildResults.getValue();
+//		double error = baselineBuildResults.getError();
+//		value += error;
+		if (value > this.maxValue) {
+			this.maxValue = value;
+		}
+		if (value < this.minValue) {
+			this.minValue = value;
+		}
+		BuildResults currentBuildResults = this.results[i].getCurrentBuildResults();
+		value = currentBuildResults.getValue();
+//		error = currentBuildResults.getError();
+//		value += error;
+		if (value > this.maxValue) {
+			this.maxValue = value;
+		}
+		if (value < this.minValue) {
+			this.minValue = value;
+		}
+	}
+	this.minValue = 0; // do not use minValue for now...
+
+	// Draw the scale
+	drawScale(kind);
+
+	// Draw the bars
+	drawBars(kind);
+	
+	// Dispose
+	this.gc.dispose();
+}
+
+/**
+ * Create, paint and save all supported bar graphs and add the corresponding
+ * image and map references in the given stream.
+ * 
+ * @param stream
+ */
+final public void paint(PrintStream stream) {
+	
+	// Paint supported graphs
+	int length = SUPPORTED_GRAPHS.length;
+	for (int i=0; i<length; i++) {
+		int kind = SUPPORTED_GRAPHS[i];
+		paint(kind);
+		save(kind, stream);
+	}
+
+	// Dispose created graphic resources
+	Iterator iterator = this.resources.values().iterator();
+	while (iterator.hasNext()) {
+		Resource resource = (Resource) iterator.next();
+		resource.dispose();
+	}
+	this.resources.clear();
+}
+
+void print(int kind, PrintStream stream) {
+	String imgName = getImageName(kind);
+	stream.print("	if ($QUERY_STRING==\"fp_type="+kind+"\") {\n");
+	stream.print("		echo '<img src=\"");
+	stream.print(imgName);
+	stream.print(".gif\" usemap=\"#");
+	stream.print(imgName);
+	stream.print("\" name=\"");
+	stream.print(imgName.substring(imgName.lastIndexOf('.')));
+	stream.print("\">';\n");
+	stream.print("		echo '<map name=\"");
+	stream.print(imgName);
+	stream.print("\">';\n");
+	if (this.areas != null) {
+		for (int i=0; i<this.count; i++) {
+			this.areas[i].print(stream);
+		}
+	}
+	stream.print("		echo '</map>';\n");
+	stream.print("	}\n");
+}
+
+void save(int kind, PrintStream stream) {
+	File file = new File(this.outputDir, getImageName(kind)+".gif");
+	Utils.saveImage(file, this.image);
+	if (file.exists()) {
+		print(kind, stream);
+	} else {
+		stream.print("<br><br>There is no fingerprint for ");
+		stream.print(imageName);
+		stream.print(" (kind=");
+		stream.print(kind);
+		stream.print(")<br><br>\n");
+	}
+}
+}
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Main.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Main.java
index c9992d8..90258e3 100644
--- a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Main.java
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Main.java
@@ -18,7 +18,6 @@
 import org.eclipse.core.runtime.FileLocator;
 import org.eclipse.equinox.app.IApplication;
 import org.eclipse.equinox.app.IApplicationContext;
-import org.eclipse.test.internal.performance.PerformanceTestPlugin;
 import org.eclipse.test.internal.performance.results.AbstractResults;
 import org.eclipse.test.internal.performance.results.ConfigResults;
 import org.eclipse.test.internal.performance.results.DB_Results;
@@ -36,7 +35,7 @@
 
 /**
  * Prefix of baseline builds displayed in data graphs.
- * This field is set using <b>-baselinePrefix</b> argument.
+ * This field is set using <b>-baseline.prefix</b> argument.
  * <p>
  * Example:
  *		<pre>-baseline.prefix 3.2_200606291905</pre>
@@ -165,6 +164,13 @@
  */
 private boolean print = false;
 
+/**
+ * Tells what should be the failure percentage threshold.
+ * <p>
+ * Default is 10%.
+ */
+private int failure_threshold = 10; // PerformanceTestPlugin.getDBLocation().startsWith("net://");
+
 /*
  * Parse the command arguments and create corresponding performance
  * results object.
@@ -197,7 +203,7 @@
 		if (arg.equals("-baseline")) {
 			baseline = args[i + 1];
 			if (baseline.startsWith("-")) {
-				System.out.println("Missing value for -baseline parameter");
+				System.out.println("Missing value for "+arg+" parameter");
 				printUsage();
 			}
 			buffer.append("	-baseline = "+baseline+'\n');
@@ -207,20 +213,20 @@
 		if (arg.equals("-baseline.prefix")) {
 			this.baselinePrefix = args[i + 1];
 			if (this.baselinePrefix.startsWith("-")) {
-				System.out.println("Missing value for -baseline.prefix parameter");
+				System.out.println("Missing value for "+arg+" parameter");
 				printUsage();
 			}
-			buffer.append("	-baselinePrefix = "+this.baselinePrefix+'\n');
+			buffer.append("	").append(arg).append(" = ").append(this.baselinePrefix).append('\n');
 			i++;
 			continue;
 		}
 		if (arg.equals("-current.prefix")) {
 			String idPrefixList = args[i + 1];
 			if (idPrefixList.startsWith("-")) {
-				System.out.println("Missing value for -current.prefix parameter");
+				System.out.println("Missing value for "+arg+" parameter");
 				printUsage();
 			}
-			buffer.append("	-current.prefix = ");
+			buffer.append("	").append(arg).append(" = ");
 			String[] ids = idPrefixList.split(",");
 			this.currentBuildPrefixes = new ArrayList();
 			for (int j = 0; j < ids.length; j++) {
@@ -233,10 +239,10 @@
 		}
 		if (arg.equals("-highlight") || arg.equals("-highlight.latest")) {
 			if (args[i + 1].startsWith("-")) {
-				System.out.println("Missing value for -highlight parameter");
+				System.out.println("Missing value for "+arg+" parameter");
 				printUsage();
 			}
-			buffer.append("	"+arg+" = ");
+			buffer.append("	").append(arg).append(" = ");
 			String[] ids = args[i + 1].split(",");
 			this.pointsOfInterest = new ArrayList();
 			for (int j = 0; j < ids.length; j++) {
@@ -250,27 +256,27 @@
 		if (arg.equals("-current")) {
 			currentBuildId  = args[i + 1];
 			if (currentBuildId.startsWith("-")) {
-				System.out.println("Missing value for -current parameter");
+				System.out.println("Missing value for "+arg+" parameter");
 				printUsage();
 			}
-			buffer.append("	-current = "+currentBuildId+'\n');
+			buffer.append("	").append(arg).append(" = ").append(currentBuildId).append('\n');
 			i++;
 			continue;
 		}
 		if (arg.equals("-jvm")) {
 			jvm = args[i + 1];
 			if (jvm.startsWith("-")) {
-				System.out.println("Missing value for -jvm parameter");
+				System.out.println("Missing value for "+arg+" parameter");
 				printUsage();
 			}
-			buffer.append("	-jvm = "+jvm+'\n');
+			buffer.append("	").append(arg).append(" = ").append(jvm).append('\n');
 			i++;
 			continue;
 		}
 		if (arg.equals("-output")) {
 			String dir = args[++i];
 			if (dir.startsWith("-")) {
-				System.out.println("Missing value for -output parameter");
+				System.out.println("Missing value for "+arg+" parameter");
 				printUsage();
 			}
 			this.outputDir = new File(dir);
@@ -278,13 +284,13 @@
 				System.err.println("Cannot create directory "+dir+" to write results in!");
 				System.exit(2);
 			}
-			buffer.append("	-output = "+dir+'\n');
+			buffer.append("	").append(arg).append(" = ").append(dir).append('\n');
 			continue;
 		}
 		if (arg.equals("-dataDir")) {
 			String dir = args[++i];
 			if (dir.startsWith("-")) {
-				System.out.println("Missing value for -output parameter");
+				System.out.println("Missing value for "+arg+" parameter");
 				printUsage();
 			}
 			this.dataDir = new File(dir);
@@ -292,18 +298,18 @@
 				System.err.println("Cannot create directory "+dir+" to save data locally!");
 				System.exit(2);
 			}
-			buffer.append("	-dataDir = "+dir+'\n');
+			buffer.append("	").append(arg).append(" = ").append(dir).append('\n');
 			continue;
 		}
 		if (arg.equals("-config")) {
 			String configs = args[i + 1];
 			if (configs.startsWith("-")) {
-				System.out.println("Missing value for -config parameter");
+				System.out.println("Missing value for "+arg+" parameter");
 				printUsage();
 			}
 			String[] names = configs.split(",");
 			int length = names.length;
-			buffer.append("	-config = ");
+			buffer.append("	").append(arg).append(" = ");
 			for (int j=0; j<length; j++) {
 				if (j>0) buffer.append(',');
 				buffer.append(names[j]);
@@ -347,7 +353,7 @@
 		if (arg.equals("-config.properties")) {
 			String configProperties = args[i + 1];
 			if (configProperties.startsWith("-")) {
-				System.out.println("Missing value for -config.properties parameter");
+				System.out.println("Missing value for "+arg+" parameter");
 				printUsage();
 			}
 			if (this.configDescriptors == null) {
@@ -356,7 +362,7 @@
 			}
 			int length = this.configDescriptors.length;
 			StringTokenizer tokenizer = new StringTokenizer(configProperties, ";");
-			buffer.append("	-config.properties = ");
+			buffer.append("	").append(arg).append(" = ");
 			while (tokenizer.hasMoreTokens()) {
 				String labelDescriptor = tokenizer.nextToken();
 				String[] elements = labelDescriptor.trim().split(",");
@@ -377,47 +383,73 @@
 		if (arg.equals("-scenario.filter") || arg.equals("-scenario.pattern")) {
 			this.scenarioPattern= args[i + 1];
 			if (this.scenarioPattern.startsWith("-")) {
-				System.out.println("Missing value for -baseline parameter");
+				System.out.println("Missing value for "+arg+" parameter");
 				printUsage();
 			}
-			buffer.append("	"+arg+" = "+this.scenarioPattern+'\n');
+			buffer.append("	").append(arg).append(" = ").append(this.scenarioPattern).append('\n');
 			i++;
 			continue;
 		}
 		if (arg.equals("-fingerprints")) {
 			this.genFingerPrints = true;
 			this.genAll = false;
-			buffer.append("	-fingerprints\n");
+			buffer.append("	").append(arg).append('\n');
 			i++;
 			continue;
 		}
 		if (arg.equals("-data")) {
 			this.genData = true;
 			this.genAll = false;
-			buffer.append("	-data\n");
+			buffer.append("	").append(arg).append('\n');
 			i++;
 			continue;
 		}
 		if (arg.equals("-print")) {
 			this.print = true;
-			buffer.append("	-print\n");
+			buffer.append("	").append(arg).append('\n');
+			i++;
+			continue;
+		}
+		if (arg.equals("-failure.threshold")) {
+			String value = args[i + 1];
+			try {
+				this.failure_threshold = Integer.parseInt(value);
+				if (this.failure_threshold < 0) {
+					System.out.println("Value for "+arg+" parameter must be positive.");
+					printUsage();
+				}
+			}
+			catch (NumberFormatException nfe) {
+				System.out.println("Invalid value for "+arg+" parameter");
+				printUsage();
+			}
+			buffer.append("	").append(arg).append(" = ").append(value).append('\n');
 			i++;
 			continue;
 		}
 		i++;
 	}
 	if (this.print) System.out.println(buffer.toString());
+	
+	// Stop if some mandatory parameters are missing
 	if (baseline == null || this.outputDir == null || this.configDescriptors == null || jvm == null || currentBuildId == null) {
 		printUsage();
 	}
+	
+	// Init baseline prefix if not set
 	if (this.baselinePrefix == null) {
 		// Assume that baseline name format is *always* x.y_yyyyMMddhhmm_yyyyMMddhhmm
 		this.baselinePrefix = baseline.substring(0, baseline.lastIndexOf('_'));
 	}
 
+	// Init currnt build prefixes if not set
 	if (this.currentBuildPrefixes == null) {
 		this.currentBuildPrefixes = new ArrayList();
-		this.currentBuildPrefixes.add("N");
+		if (currentBuildId.charAt(0) == 'M') {
+			this.currentBuildPrefixes.add("M");
+		} else {
+			this.currentBuildPrefixes.add("N");
+		}
 		this.currentBuildPrefixes.add("I");
 	}
 	return new PerformanceResults(currentBuildId, baseline, this.print);
@@ -430,28 +462,49 @@
 	if (this.print) System.out.print(".");
 	File outputFile = new File(this.outputDir, component + ".php");
 	PrintStream stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
-	stream.println(Utils.HTML_OPEN);
-	stream.println("<link href=\"ToolTip.css\" rel=\"stylesheet\" type=\"text/css\"><script src=\"ToolTip.js\"></script>");
-	stream.println(Utils.HTML_DEFAULT_CSS);
-	stream.println("<body>");
-
-	String baselineName = performanceResults.getBaselineName();
-	String currentName = performanceResults.getName();
-	boolean isGlobal = component.equals("global");
-	StringBuffer title = new StringBuffer("<h3>Performance of ");
-	if (!isGlobal) {
-		title.append(component);
-		title.append(": ");
+	
+	// Print header
+	boolean isGlobal = component.startsWith("global");
+	if (isGlobal) {
+		File globalFile = new File(this.outputDir, "global.php");
+		PrintStream gStream = new PrintStream(new BufferedOutputStream(new FileOutputStream(globalFile)));
+		gStream.print(Utils.HTML_OPEN);
+		gStream.print("</head>\n");
+		gStream.print("<body>\n");
+		gStream.print("<?php\n");
+		gStream.print("	include(\"global_fp.php\");\n");
+		gStream.print("?>\n");
+		gStream.print("<table border=0 cellpadding=2 cellspacing=5 width=\"100%\">\n");
+		gStream.print("<tbody><tr> <td colspan=3 align=\"left\" bgcolor=\"#0080c0\" valign=\"top\"><b><font color=\"#ffffff\" face=\"Arial,Helvetica\">\n");
+		gStream.print("Detailed performance data grouped by scenario prefix</font></b></td></tr></tbody></table>\n");
+		gStream.print("<a href=\"org.eclipse.ant.php?\">org.eclipse.ant*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.compare.php?\">org.eclipse.compare*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.core.php?\">org.eclipse.core*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.jdt.core.php?\">org.eclipse.jdt.core*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.jdt.debug.php?\">org.eclipse.jdt.debug*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.jdt.text.php?\">org.eclipse.jdt.text*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.jdt.ui.php?\">org.eclipse.jdt.ui*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.jface.php?\">org.eclipse.jface*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.osgi.php?\">org.eclipse.osgi*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.pde.ui.php?\">org.eclipse.pde.ui*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.swt.php?\">org.eclipse.swt*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.team.php?\">org.eclipse.team*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.ua.php?\">org.eclipse.ua*</a><br>\n");
+		gStream.print("<a href=\"org.eclipse.ui.php?\">org.eclipse.ui*</a><br><p><br><br>\n");
+		gStream.print("</body>\n");
+		gStream.print(Utils.HTML_CLOSE);
+		gStream.close();
+	} else {
+		stream.print(Utils.HTML_OPEN);
 	}
-	title.append(currentName);
-	title.append(" relative to ");
-	int index = baselineName.indexOf('_');
-	title.append(baselineName.substring(0, index));
-	title.append(" (");
-	index = baselineName.lastIndexOf('_');
-	title.append(baselineName.substring(index+1, baselineName.length()));
-	title.append(")</h3>");
-	stream.println(title.toString());
+	stream.print("<link href=\"ToolTip.css\" rel=\"stylesheet\" type=\"text/css\">\n");
+	stream.print("<script src=\"ToolTip.js\"></script>\n");
+	stream.print("<script src=\"Fingerprints.js\"></script>\n");
+	stream.print(Utils.HTML_DEFAULT_CSS);
+	
+	// Print title
+	stream.print("<body>");
+	printComponentTitle(performanceResults, component, isGlobal, stream);
 
 	// print the html representation of fingerprint for each config
 	if (genFingerPrints || genAll) {
@@ -464,27 +517,7 @@
 	}
 
 	// print scenario status table
-	if (isGlobal) {
-		if (!PerformanceTestPlugin.getDBLocation().startsWith("net://")) {
-			stream.println("<table border=0 cellpadding=2 cellspacing=5 width=\"100%\">");
-			stream.println("<tbody><tr> <td colspan=3 align=\"left\" bgcolor=\"#0080c0\" valign=\"top\"><b><font color=\"#ffffff\" face=\"Arial,Helvetica\">");
-			stream.println("Detailed performance data grouped by scenario prefix</font></b></td></tr></tbody></table>");
-			stream.println("<a href=\"org.eclipse.ant.php?\">org.eclipse.ant*</a><br>");
-			stream.println("<a href=\"org.eclipse.compare.php?\">org.eclipse.compare*</a><br>");
-			stream.println("<a href=\"org.eclipse.core.php?\">org.eclipse.core*</a><br>");
-			stream.println("<a href=\"org.eclipse.jdt.core.php?\">org.eclipse.jdt.core*</a><br>");
-			stream.println("<a href=\"org.eclipse.jdt.debug.php?\">org.eclipse.jdt.debug*</a><br>");
-			stream.println("<a href=\"org.eclipse.jdt.text.php?\">org.eclipse.jdt.text*</a><br>");
-			stream.println("<a href=\"org.eclipse.jdt.ui.php?\">org.eclipse.jdt.ui*</a><br>");
-			stream.println("<a href=\"org.eclipse.jface.php?\">org.eclipse.jface*</a><br>");
-			stream.println("<a href=\"org.eclipse.osgi.php?\">org.eclipse.osgi*</a><br>");
-			stream.println("<a href=\"org.eclipse.pde.ui.php?\">org.eclipse.pde.ui*</a><br>");
-			stream.println("<a href=\"org.eclipse.swt.php?\">org.eclipse.swt*</a><br>");
-			stream.println("<a href=\"org.eclipse.team.php?\">org.eclipse.team*</a><br>");
-			stream.println("<a href=\"org.eclipse.ua.php?\">org.eclipse.ua*</a><br>");
-			stream.println("<a href=\"org.eclipse.ui.php?\">org.eclipse.ui*</a><br><p><br><br>");
-		}
-	} else if (component.length() > 0) {
+	if (!isGlobal) {
 		// print the component scenario status table beneath the fingerprint
 		ScenarioStatusTable sst = new ScenarioStatusTable(component, stream);
 		try {
@@ -494,10 +527,43 @@
 		}
 	}
 
-	stream.println(Utils.HTML_CLOSE);
+	stream.print(Utils.HTML_CLOSE);
 	stream.close();
 }
 
+private void printComponentTitle(PerformanceResults performanceResults, String component, boolean isGlobal, PrintStream stream) {
+	String baselineName = performanceResults.getBaselineName();
+	String currentName = performanceResults.getName();
+	
+	// Print title line
+	stream.print("<h3>Performance of ");
+	if (!isGlobal) {
+		stream.print(component);
+		stream.print(": ");
+	}
+	stream.print(currentName);
+	stream.print(" relative to ");
+	int index = baselineName.indexOf('_');
+	stream.print(baselineName.substring(0, index));
+	stream.print(" (");
+	index = baselineName.lastIndexOf('_');
+	stream.print(baselineName.substring(index+1, baselineName.length()));
+	stream.print(")</h3>\n");
+	
+	// Print reference to global results
+	if (!isGlobal) {
+		stream.print("<?php\n");
+		stream.print("	$type=$QUERY_STRING;\n");
+		stream.print("	if ($type==\"\") {\n");
+		stream.print("        $type=\"fp_type=0\";\n");
+		stream.print("	}\n");
+		stream.print("	$href=\"<a href=\\\"performance.php?\";\n");
+		stream.print("	$href=$href . $type . \"\\\">Back to global results</a><br><br>\";\n");
+		stream.print("	echo $href;\n");
+		stream.print("?>\n");
+	}
+}
+
 /*
  * Print summary of coefficient of variation for each scenario of the given pattern
  * both for baseline and current builds.
@@ -510,7 +576,7 @@
 	try {
 		stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
 		printSummaryPresentation(stream);
-		List scenarioNames = DB_Results.getScenariosNames();
+		List scenarioNames = DB_Results.getScenarios();
 		int size = scenarioNames.size();
 		printSummaryColumnsTitle(stream, performanceResults);
 		String[] configs = performanceResults.getConfigNames(true/*sorted*/);
@@ -520,7 +586,7 @@
 			if (scenarioName == null) continue;
 			ScenarioResults scenarioResults = performanceResults.getScenarioResults(scenarioName);
 			if (scenarioResults != null) {
-				stream.println("<tr>");
+				stream.print("<tr>\n");
 				for (int j=0; j<2; j++) {
 					for (int c=0; c<configsLength; c++) {
 						printSummaryScenarioLine(j, configs[c], scenarioResults, stream);
@@ -528,13 +594,13 @@
 				}
 				stream.print("<td>");
 				stream.print(scenarioName);
-				stream.println("</td></tr>");
+				stream.print("</td></tr>\n");
 			}
 		}
 	} catch (Exception e) {
 		e.printStackTrace();
 	} finally {
-		stream.println("</table></body></html>");
+		stream.print("</table></body></html>\n");
 		stream.flush();
 		stream.close();
 	}
@@ -545,31 +611,31 @@
  * Print summary presentation (eg. file start and text presenting the purpose of this file contents)..
  */
 private void printSummaryPresentation(PrintStream stream) {
-	stream.println(Utils.HTML_OPEN);
+	stream.print(Utils.HTML_OPEN);
 	stream.print(Utils.HTML_DEFAULT_CSS);
-	stream.println("<title>Summary of Elapsed Process Variation Coefficients</title></head>");
-	stream.println("<body><h3>Summary of Elapsed Process Variation Coefficients</h3>\n");
-	stream.println("<p> This table provides a bird's eye view of variability in elapsed process times\n");
+	stream.print("<title>Summary of Elapsed Process Variation Coefficients</title></head>\n");
+	stream.print("<body><h3>Summary of Elapsed Process Variation Coefficients</h3>\n");
+	stream.print("<p> This table provides a bird's eye view of variability in elapsed process times\n");
 	stream.print("for baseline and current build stream performance scenarios.");
 	stream.print(" This summary is provided to facilitate the identification of scenarios that should be examined due to high variability.");
-	stream.println("The variability for each scenario is expressed as a <a href=\"http://en.wikipedia.org/wiki/Coefficient_of_variation\">coefficient\n");
-	stream.println("of variation</a> (CV). The CV is calculated by dividing the <b>standard deviation\n");
-	stream.println("of the elapse process time over builds</b> by the <b>average elapsed process\n");
-	stream.println("time over builds</b> and multiplying by 100.\n");
-	stream.println("</p><p>High CV values may be indicative of any of the following:<br></p>\n");
-	stream.println("<ol><li> an unstable performance test. </li>\n");
-	stream.println("<ul><li>may be evidenced by an erratic elapsed process line graph.<br><br></li></ul>\n");
-	stream.println("<li>performance regressions or improvements at some time in the course of builds.</li>\n");
-	stream.println("<ul><li>may be evidenced by plateaus in elapsed process line graphs.<br><br></li></ul>\n");
-	stream.println("<li>unstable testing hardware.\n");
+	stream.print("The variability for each scenario is expressed as a <a href=\"http://en.wikipedia.org/wiki/Coefficient_of_variation\">coefficient\n");
+	stream.print("of variation</a> (CV). The CV is calculated by dividing the <b>standard deviation\n");
+	stream.print("of the elapse process time over builds</b> by the <b>average elapsed process\n");
+	stream.print("time over builds</b> and multiplying by 100.\n");
+	stream.print("</p><p>High CV values may be indicative of any of the following:<br></p>\n");
+	stream.print("<ol><li> an unstable performance test. </li>\n");
+	stream.print("<ul><li>may be evidenced by an erratic elapsed process line graph.<br><br></li></ul>\n");
+	stream.print("<li>performance regressions or improvements at some time in the course of builds.</li>\n");
+	stream.print("<ul><li>may be evidenced by plateaus in elapsed process line graphs.<br><br></li></ul>\n");
+	stream.print("<li>unstable testing hardware.\n");
 	stream.print("<ul><li>consistent higher CV values for one test configuration as compared to others across");
-	stream.println(" scenarios may be related to hardward problems.</li></ul></li></ol>\n");
-	stream.println("<p> Scenarios are listed in alphabetical order in the far right column. A scenario's\n");
-	stream.println("variation coefficients (CVs) are in columns to the left for baseline and current\n");
-	stream.println("build streams for each test configuration. Scenarios with CVs > 10% are highlighted\n");
-	stream.println("in yellow (10%<CV>&lt;CV<20%) and orange(CV>20%). </p>\n");
-	stream.println("<p> Each CV value links to the scenario's detailed results to allow viewers to\n");
-	stream.println("investigate the variability.</p>\n");
+	stream.print(" scenarios may be related to hardward problems.</li></ul></li></ol>\n");
+	stream.print("<p> Scenarios are listed in alphabetical order in the far right column. A scenario's\n");
+	stream.print("variation coefficients (CVs) are in columns to the left for baseline and current\n");
+	stream.print("build streams for each test configuration. Scenarios with CVs > 10% are highlighted\n");
+	stream.print("in yellow (10%<CV>&lt;CV<20%) and orange(CV>20%). </p>\n");
+	stream.print("<p> Each CV value links to the scenario's detailed results to allow viewers to\n");
+	stream.print("investigate the variability.</p>\n");
 }
 
 /*
@@ -582,7 +648,7 @@
 	stream.print(length);
 	stream.print("\"><b>Baseline CVs</b></td><td colspan=\"");
 	stream.print(length);
-	stream.println("\"><b>Current Build Stream CVs</b></td><td rowspan=\"2\"><b>Scenario Name</b></td></tr>");
+	stream.print("\"><b>Current Build Stream CVs</b></td><td rowspan=\"2\"><b>Scenario Name</b></td></tr>\n");
 	stream.print("<tr>");
 	for (int n=0; n<2; n++) {
 		for (int c=0; c<length; c++) {
@@ -591,7 +657,7 @@
 			stream.print("</td>");
 		}
 	}
-	stream.println("</tr>\n");
+	stream.print("</tr>\n");
 }
 
 /*
@@ -605,13 +671,12 @@
 	}
 	String url = config + "/" + scenarioResults.getFileName()+".html";
 	double[] stats = null;
-	int dim_id = AbstractResults.SUPPORTED_DIMS[0].getId();
 	if (i==0) { // baseline results
 		List baselinePrefixes = new ArrayList();
 		baselinePrefixes.add(this.baselinePrefix);
-		stats = configResults.getStatistics(baselinePrefixes, dim_id);
+		stats = configResults.getStatistics(baselinePrefixes);
 	} else {
-		stats = configResults.getStatistics(this.currentBuildPrefixes, dim_id);
+		stats = configResults.getStatistics(this.currentBuildPrefixes);
 	}
 	double variation = stats[3];
 	if (variation > 10 && variation < 20) {
@@ -683,7 +748,13 @@
 		"	Optional.  Generates table of scenario reference and current data with line graphs.\n\n" +
 
 		"[-print]\n" +
-		"	Optional.  Display output in the console while generating.\n"
+		"	Optional.  Display output in the console while generating.\n" +
+
+		"[-nophp]\n" +
+		"	Optional.  Generate files for non-php server.\n" +
+
+		"[-failure.threshold]\n" +
+		"	Optional.  Set the failure percentage threshold (default is 10%).\n"
 	);
 
 	System.exit(1);
@@ -706,67 +777,89 @@
 
 	// Parse arguments and read DB info
 	PerformanceResults performanceResults = parse(context.getArguments().get("application.args"));
-	performanceResults.read(this.configDescriptors, this.scenarioPattern, this.dataDir);
+	performanceResults.read(this.configDescriptors, this.scenarioPattern, this.dataDir, this.failure_threshold);
 
 	// Print whole scenarios summary
+	if (this.print) System.out.println();
 	printSummary(performanceResults);
 
 	// Copy images and scripts to output dir
 	Bundle bundle = UiPlugin.getDefault().getBundle();
 	URL images = bundle.getEntry("images");
-	URL scripts = bundle.getEntry("scripts");
 	if (images != null) {
 		images = FileLocator.resolve(images);
 		Utils.copyImages(new File(images.getPath()), this.outputDir);
 	}
+	URL scripts = bundle.getEntry("scripts");
 	if (scripts != null) {
 		scripts = FileLocator.resolve(scripts);
 		Utils.copyScripts(new File(scripts.getPath()), this.outputDir);
 	}
+	URL doc = bundle.getEntry("doc");
+	if (doc != null) {
+		doc = FileLocator.resolve(doc);
+		File docDir = new File(doc.getPath());
+		FileFilter filter = new FileFilter() {
+			public boolean accept(File pathname) {
+	            return !pathname.getName().equals("CVS");
+            }
+		};
+		File[] docFiles = docDir.listFiles(filter);
+		for (int i=0; i<docFiles.length; i++) {
+			File file = docFiles[i];
+			if (file.isDirectory()) {
+				File subdir = new File(this.outputDir, file.getName());
+				subdir.mkdir();
+				File[] subdirFiles = file.listFiles(filter);
+				for (int j=0; j<subdirFiles.length; j++) {
+					if (subdirFiles[i].isDirectory()) {
+						// expect only one sub-directory
+					} else {
+						AbstractResults.copyFile(subdirFiles[j], new File(subdir, subdirFiles[j].getName()));
+					}
+				}
+			} else {
+				AbstractResults.copyFile(file, new File(this.outputDir, file.getName()));
+			}
+		}
+	}
 
 	// Print HTML pages and all linked files
 	if (this.print) {
 		System.out.println("Print performance results HTML pages:");
-		System.out.print("	- all components");
+		System.out.print("	- components main page");
 	}
 	long start = System.currentTimeMillis();
-	printComponent(performanceResults, "global");
+	printComponent(performanceResults, "global_fp");
 	Iterator components = performanceResults.getComponents().iterator();
 	while (components.hasNext()) {
 		printComponent(performanceResults, (String) components.next());
 	}
-	if (this.print) System.out.println("done in "+(System.currentTimeMillis()-start)+"ms");
+	if (this.print) {
+		String duration = AbstractResults.timeString(System.currentTimeMillis()-start);
+		System.out.println(" done in "+duration);
+	}
 
 	// Print the scenarios data
 	if (genData || genAll) {
 		start = System.currentTimeMillis();
-		if (this.print) System.out.print("	- all scenarios data...");
+		if (this.print) System.out.println("	- all scenarios data:");
 		ScenarioData data = new ScenarioData(this.baselinePrefix, this.pointsOfInterest, this.currentBuildPrefixes, this.outputDir);
 		try {
-			data.print(performanceResults);
+			data.print(performanceResults, this.print);
 		} catch (Exception ex) {
 			ex.printStackTrace();
 		}
-		if (this.print) System.out.println("done in "+(System.currentTimeMillis()-start)+"ms");
+		if (this.print) {
+			String duration = AbstractResults.timeString(System.currentTimeMillis()-start);
+			System.out.println("	=> done in "+duration);
+		}
 	}
 	if (this.print) {
 		long time = System.currentTimeMillis();
 		System.out.println("End of generation: "+new SimpleDateFormat("H:mm:ss").format(new Date(time)));
-		long ms = System.currentTimeMillis() - begin;
-		int sec = (int) (ms / 1000L);
-		if ((ms - (sec*1000)) >= 500) sec++;
-		if (sec < 60) {
-			System.out.println("=> done in "+sec+" second"+(sec==1?"":"s"));
-		} else if (sec < 3600) {
-			int m = sec / 60;
-			int s = sec % 60;
-			System.out.println("=> done in "+m+" minute"+(m==1?"":"s")+" and "+s+" second"+(s==1?"":"s"));
-		} else {
-			int h = sec / 3600;
-			int m = (sec-h*3600) / 60;
-			int s = (sec-h*3600)  % 60;
-			System.out.println("=> done in "+h+" hour"+(h==1?"":"s")+", "+m+" minute"+(m==1?"":"s")+" and "+s+" second"+(s==1?"":"s"));
-		}
+		String duration = AbstractResults.timeString(System.currentTimeMillis()-begin);
+		System.out.println("=> done in "+duration);
 	}
 	return null;
 }
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/RawDataTable.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/RawDataTable.java
index c18861e..bac35c3 100644
--- a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/RawDataTable.java
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/RawDataTable.java
@@ -54,7 +54,7 @@
 	stream.print("<table border=\"1\">");
 	printSummary();
 	printDetails();
-	stream.println("</table>");
+	stream.print("</table>\n");
 }
 
 /*
@@ -77,7 +77,7 @@
 private void printDetails() {
 	stream.print("<tr><td><b>Build ID</b></td>");
 	printColumnHeaders();
-	stream.println("</tr>");
+	stream.print("</tr>\n");
 
 	List builds = this.configResults.getBuildsMatchingPrefixes(this.buildPrefixes);
 	Collections.reverse(builds);
@@ -98,7 +98,7 @@
 			stream.print("</td>");
 		}
 		if (debug) System.out.println();
-		stream.println("</tr>");
+		stream.print("</tr>\n");
 	}
 	if (debug) System.out.println("\n");
 }
@@ -109,7 +109,7 @@
 private void printSummary() {
 	stream.print("<tr><td><b>Stats</b></td>");
 	printColumnHeaders();
-	stream.println("</tr>");
+	stream.print("</tr>\n");
 
 	int length = this.dimensions.length;
 	double[][] dimStats = new double[2][];
@@ -124,23 +124,23 @@
 		stream.print((int)dimStats[i][0]);
 		stream.print("</td>");
 	}
-	stream.println("</tr>");
+	stream.print("</tr>\n");
 	stream.print("<tr><td>MEAN</td>");
 	printRowDoubles(dimStats, 1);
-	stream.println("</tr>");
+	stream.print("</tr>\n");
 	stream.print("<tr><td>STD DEV</td>");
 	printRowDoubles(dimStats, 2);
-	stream.println("</tr>");
+	stream.print("</tr>\n");
 	stream.print("<tr><td>COEF. VAR</td>");
 	printRowDoubles(dimStats, 3);
-	stream.println("</tr>");
+	stream.print("</tr>\n");
 
 	// Blank line
 	stream.print("<tr>");
 	for (int i=0; i<length+1;	i++){
 		stream.print("<td>&nbsp;</td>");
 	}
-	stream.println("</tr>");
+	stream.print("</tr>\n");
 }
 
 /*
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/ScenarioData.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/ScenarioData.java
index d41a12a..8ee20a4 100644
--- a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/ScenarioData.java
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/ScenarioData.java
@@ -30,7 +30,6 @@
 import org.eclipse.swt.graphics.ImageLoader;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.test.internal.performance.data.Dim;
-import org.eclipse.test.internal.performance.data.DimensionMessages;
 import org.eclipse.test.internal.performance.results.AbstractResults;
 import org.eclipse.test.internal.performance.results.BuildResults;
 import org.eclipse.test.internal.performance.results.ComponentResults;
@@ -66,211 +65,19 @@
 	this.rootDir = outputDir;
 }
 
-/**
- * Print the scenario all builds data from the given performance results.
- * 
- * @param performanceResults The needed information to generate scenario data
- */
-public void print(PerformanceResults performanceResults) {
-	String[] configNames = performanceResults.getConfigNames(false/*not sorted*/);
-	String[] configBoxes = performanceResults.getConfigBoxes(false/*not sorted*/);
-	int length = configNames.length;
-	for (int i=0; i<length; i++) {
-		File outputDir = new File(this.rootDir, configNames[i]);
-		outputDir.mkdir();
-		Iterator components = performanceResults.getResults();
-		while (components.hasNext()) {
-			ComponentResults componentResults = (ComponentResults) components.next();
-			printSummary(configNames[i], configBoxes[i], componentResults, outputDir);
-			printDetails(configNames[i], configBoxes[i], componentResults, outputDir);
-		}
-	}
-}
-
 /*
- * Print the summary file of the builds data.
+ * Create a file handle verifying that its name does not go over
+ * the maximum authorized length.
  */
-private void printSummary(String configName, String configBox, ComponentResults componentResults, File outputDir) {
-	Iterator scenarios = componentResults.getResults();
-	while (scenarios.hasNext()) {
-		List highlightedPoints = new ArrayList();
-		ScenarioResults scenarioResults = (ScenarioResults) scenarios.next();
-		ConfigResults configResults = scenarioResults.getConfigResults(configName);
-		if (configResults == null || !configResults.isValid()) continue;
-
-		// get latest points of interest matching
-		if (this.pointsOfInterest != null) {
-			Iterator buildPrefixes = this.pointsOfInterest.iterator();
-			while (buildPrefixes.hasNext()) {
-				String buildPrefix = (String) buildPrefixes.next();
-				List builds = configResults.getBuilds(buildPrefix);
-				if (buildPrefix.indexOf('*') <0 && buildPrefix.indexOf('?') < 0) {
-					if (builds.size() > 0) {
-						highlightedPoints.add(builds.get(builds.size()-1));
-					}
-				} else {
-					highlightedPoints.addAll(builds);
-				}
-			}
-		}
-
-		String scenarioFileName = scenarioResults.getFileName();
-		File outFile = new File(outputDir, scenarioFileName + ".html");
-		PrintStream stream = null;
-		try {
-			stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outFile)));
-		} catch (FileNotFoundException e) {
-			System.err.println("can't create output file" + outFile); //$NON-NLS-1$
-		}
-		if (stream == null) {
-			stream = System.out;
-		}
-		stream.println(Utils.HTML_OPEN);
-		stream.println(Utils.HTML_DEFAULT_CSS);
-
-		stream.println("<title>" + scenarioResults.getName() + "(" + configBox + ")" + "</title></head>"); //$NON-NLS-1$
-		stream.println("<h4>Scenario: " + scenarioResults.getName() + " (" + configBox + ")</h4><br>"); //$NON-NLS-1$ //$NON-NLS-2$
-
-		String failureMessage = Utils.failureMessage(configResults.getCurrentBuildDeviation(), true);
- 		if (failureMessage != null){
-   			stream.println("<table><tr><td><b>"+failureMessage+"</td></tr></table>\n");
- 		}
-
- 		BuildResults currentBuildResults = configResults.getCurrentBuildResults();
- 		String comment = currentBuildResults.getComment();
-		if (comment != null) {
-			stream.println("<p><b>Note:</b><br>\n");
-			stream.println(comment + "</p>");
-		}
-
-		// Print link to raw data.
-		String rawDataFile = scenarioFileName+"_raw.html";
-		stream.println("<br><br><b><a href=\""+rawDataFile+"\">Raw data and Stats</a></b><br><br>\n");
-		stream.println("<b>Click measurement name to view line graph of measured values over builds.</b><br><br>\n");
-
-		try {
-			// Print build result table
-			stream.println("<table border=\"1\">"); //$NON-NLS-1$
-			stream.print("<tr><td><b>Build Id</b></td>"); //$NON-NLS-1$
-			Dim[] dimensions = AbstractResults.SUPPORTED_DIMS;
-			int dimLength = dimensions.length;
-			for (int d=0; d<dimLength; d++) {
-				String dimName = dimensions[d].getName();
-				stream.print("<td><a href=\"#" + configName + "_" + scenarioFileName + "_" + dimName + "\"><b>" + dimName + "</b></a></td>");
-			}
-			stream.println("</tr>\n");
-
-			// Write build lines
-			printTableLine(stream, currentBuildResults);
-			printTableLine(stream, configResults.getBaselineBuildResults());
-
-			// Write difference line
-			printDifferenceLine(stream, configResults);
-
-			// End of table
-			stream.println("</table>");
-			stream.println("*Delta values in red and green indicate degradation > 10% and improvement > 10%,respectively.<br><br>");
-			stream.println("<br><hr>\n\n");
-
-			// print text legend.
-			stream.println("Black and yellow points plot values measured in integration and last seven nightly builds.<br>\n" + "Magenta points plot the repeated baseline measurement over time.<br>\n"
-					+ "Boxed points represent previous releases, milestone builds, current reference and current build.<br><br>\n"
-					+ "Hover over any point for build id and value.\n");
-
-			// print image maps of historical
-			for (int d=0; d<dimLength; d++) {
-				String dimName = dimensions[d].getName();
-				int dim_id = dimensions[d].getId();
-				TimeLineGraph lineGraph = getLineGraph(scenarioResults, configResults, dimensions[d], highlightedPoints, this.buildIDStreamPatterns);
-
-				File graphsDir = new File(outputDir, "graphs");
-				graphsDir.mkdir();
-				File imgFile = new File(graphsDir, scenarioFileName + "_" + dimName + ".gif");
-				saveGraph(lineGraph, imgFile);
-				stream.println("<br><a name=\"" + configName + "_" + scenarioFileName + "_" + dimName + "\"></a>");
-				stream.println("<br><b>" + dimName + "</b><br>");
-				stream.println(DimensionMessages.getDescription(dim_id) + "<br><br>\n");
-				stream.print("<img src=\"graphs/");
-				stream.print(imgFile.getName());
-				stream.print("\" usemap=\"#" + lineGraph.fTitle + "\">");
-				stream.print("<map name=\"" + lineGraph.fTitle + "\">");
-				stream.print(lineGraph.getAreas());
-				stream.println("</map>");
-			}
-			stream.println("<br><br></body>");
-			stream.println(Utils.HTML_CLOSE);
-			if (stream != System.out)
-				stream.close();
-
-		} catch (AssertionFailedError e) {
-			e.printStackTrace();
-			continue;
+private File createFile(File outputDir, String subdir, String name, String extension) {
+	File dir = outputDir;
+	if (subdir != null) {
+		dir = new File(outputDir, subdir);
+		if (!dir.exists()) {
+			dir.mkdir();
 		}
 	}
-}
-
-/*
- * Print the data for a build results.
- */
-private void printTableLine(PrintStream stream, BuildResults buildResults) {
-	stream.print("<tr><td>");
-	stream.print(buildResults.getName());
-	if (buildResults.isBaseline()) stream.print(" (reference)");
-	stream.print("</td>");
-	Dim[] dimensions = AbstractResults.SUPPORTED_DIMS;
-	int dimLength = dimensions.length;
-	for (int d=0; d<dimLength; d++) {
-		int dim_id = dimensions[d].getId();
-		double stddev = buildResults.getDeviation(dim_id);
-		String displayValue = dimensions[d].getDisplayValue(buildResults.getValue(dim_id));
-		stream.print("<td>");
-		stream.print(displayValue);
-		if (stddev < 0) {
-			stream.println(" [n/a]");
-		} else if (stddev > 0) {
-			stream.print(" [");
-			stream.print(dimensions[d].getDisplayValue(stddev));
-			stream.print("]");
-		}
-		stream.print( "</td>");
-	}
-	stream.println("</tr>");
-}
-
-/*
- * Print the line showing the difference between current and baseline builds.
- */
-private void printDifferenceLine(PrintStream stream, ConfigResults configResults) {
-	stream.print("<tr><td>*Delta</td>");
-	Dim[] dimensions = AbstractResults.SUPPORTED_DIMS;
-	int dimLength = dimensions.length;
-	for (int d=0; d<dimLength; d++) {
-		Dim currentDim = dimensions[d];
-		int dim_id = currentDim.getId();
-		BuildResults currentBuild = configResults.getCurrentBuildResults();
-		BuildResults baselineBuild = configResults.getBaselineBuildResults();
-
-		double baselineValue = baselineBuild.getValue(dim_id);
-		double diffValue = currentBuild.getValue(dim_id) - baselineValue;
-		double diffPercentage =  baselineValue == 0 ? 0 : Math.round(diffValue / baselineValue * 1000) / 10.0;
-		String diffDisplayValue = currentDim.getDisplayValue(diffValue);
-		// green
-		String fontColor = "";
-		if ((diffPercentage < -10 && !currentDim.largerIsBetter()) || (diffPercentage > 10 && currentDim.largerIsBetter()))
-			fontColor = "#006600";
-		if ((diffPercentage < -10 && currentDim.largerIsBetter()) || (diffPercentage > 10 && !currentDim.largerIsBetter()))
-			fontColor = "#FF0000";
-
-		diffPercentage = Math.abs(diffPercentage);
-		String percentage = (diffPercentage == 0) ? "" : "<br>" + diffPercentage + " %";
-
-		if (diffPercentage > 10 || diffPercentage < -10) {
-			stream.print("<td><FONT COLOR=\"" + fontColor + "\"><b>" + diffDisplayValue + percentage + "</b></FONT></td>");
-		} else {
-			stream.print("<td>" + diffDisplayValue + percentage + "</td>");
-		}
-	}
-	stream.print("</tr></font>");
+	return new File(dir, name + '.' + extension);
 }
 
 /*
@@ -337,6 +144,221 @@
 	return graph;
 }
 
+/**
+ * Print the scenario all builds data from the given performance results.
+ * 
+ * @param performanceResults The needed information to generate scenario data
+ */
+public void print(PerformanceResults performanceResults, boolean print) {
+	String[] configNames = performanceResults.getConfigNames(false/*not sorted*/);
+	String[] configBoxes = performanceResults.getConfigBoxes(false/*not sorted*/);
+	int length = configNames.length;
+	for (int i=0; i<length; i++) {
+		String configName = configNames[i];
+		long start = System.currentTimeMillis();
+		if (print) System.out.print("		+ "+configName);
+		File outputDir = new File(this.rootDir, configName);
+		outputDir.mkdir();
+		Iterator components = performanceResults.getResults();
+		while (components.hasNext()) {
+			if (print) System.out.print(".");
+			ComponentResults componentResults = (ComponentResults) components.next();
+			printSummary(configName, configBoxes[i], componentResults, outputDir);
+			printDetails(configName, configBoxes[i], componentResults, outputDir);
+		}
+		if (print) {
+			String duration = AbstractResults.timeString(System.currentTimeMillis()-start);
+			System.out.println(" done in "+duration);
+		}
+	}
+}
+
+/*
+ * Print the summary file of the builds data.
+ */
+private void printSummary(String configName, String configBox, ComponentResults componentResults, File outputDir) {
+	Iterator scenarios = componentResults.getResults();
+	while (scenarios.hasNext()) {
+		List highlightedPoints = new ArrayList();
+		ScenarioResults scenarioResults = (ScenarioResults) scenarios.next();
+		ConfigResults configResults = scenarioResults.getConfigResults(configName);
+		if (configResults == null || !configResults.isValid()) continue;
+
+		// get latest points of interest matching
+		if (this.pointsOfInterest != null) {
+			Iterator buildPrefixes = this.pointsOfInterest.iterator();
+			while (buildPrefixes.hasNext()) {
+				String buildPrefix = (String) buildPrefixes.next();
+				List builds = configResults.getBuilds(buildPrefix);
+				if (buildPrefix.indexOf('*') <0 && buildPrefix.indexOf('?') < 0) {
+					if (builds.size() > 0) {
+						highlightedPoints.add(builds.get(builds.size()-1));
+					}
+				} else {
+					highlightedPoints.addAll(builds);
+				}
+			}
+		}
+
+		String scenarioFileName = scenarioResults.getFileName();
+		File outputFile = new File(outputDir, scenarioFileName+".html");
+		PrintStream stream = null;
+		try {
+			stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
+		} catch (FileNotFoundException e) {
+			System.err.println("can't create output file" + outputFile); //$NON-NLS-1$
+		}
+		if (stream == null) {
+			stream = System.out;
+		}
+		stream.print(Utils.HTML_OPEN);
+		stream.print(Utils.HTML_DEFAULT_CSS);
+
+		stream.print("<title>" + scenarioResults.getName() + "(" + configBox + ")" + "</title></head>\n"); //$NON-NLS-1$
+		stream.print("<h4>Scenario: " + scenarioResults.getName() + " (" + configBox + ")</h4><br>\n"); //$NON-NLS-1$ //$NON-NLS-2$
+
+		String failureMessage = Utils.failureMessage(configResults.getCurrentBuildDeltaInfo(), true);
+ 		if (failureMessage != null){
+   			stream.print("<table><tr><td><b>"+failureMessage+"</td></tr></table>\n");
+ 		}
+
+ 		BuildResults currentBuildResults = configResults.getCurrentBuildResults();
+ 		String comment = currentBuildResults.getComment();
+		if (comment != null) {
+			stream.print("<p><b>Note:</b><br>\n");
+			stream.print(comment + "</p>\n");
+		}
+
+		// Print link to raw data.
+		String rawDataFile = "raw/" + scenarioFileName+".html";
+		stream.print("<br><br><b><a href=\""+rawDataFile+"\">Raw data and Stats</a></b><br><br>\n");
+		stream.print("<b>Click measurement name to view line graph of measured values over builds.</b><br><br>\n");
+
+		try {
+			// Print build result table
+			stream.print("<table border=\"1\">\n"); //$NON-NLS-1$
+			stream.print("<tr><td><b>Build Id</b></td>"); //$NON-NLS-1$
+			Dim[] dimensions = AbstractResults.SUPPORTED_DIMS;
+			int dimLength = dimensions.length;
+			for (int d=0; d<dimLength; d++) {
+				stream.print("<td><a href=\"#" + dimensions[d].getShortName() + "\"><b>" + dimensions[d].getName() + "</b></a></td>");
+			}
+			stream.print("</tr>\n");
+
+			// Write build lines
+			printTableLine(stream, currentBuildResults);
+			printTableLine(stream, configResults.getBaselineBuildResults());
+
+			// Write difference line
+			printDifferenceLine(stream, configResults);
+
+			// End of table
+			stream.print("</table>\n");
+			stream.print("*Delta values in red and green indicate degradation > 10% and improvement > 10%,respectively.<br><br>\n");
+			stream.print("<br><hr>\n\n");
+
+			// print text legend.
+			stream.print("Black and yellow points plot values measured in integration and last seven nightly builds.<br>\n" + "Magenta points plot the repeated baseline measurement over time.<br>\n"
+					+ "Boxed points represent previous releases, milestone builds, current reference and current build.<br><br>\n"
+					+ "Hover over any point for build id and value.\n");
+
+			// print image maps of historical
+			for (int d=0; d<dimLength; d++) {
+				TimeLineGraph lineGraph = getLineGraph(scenarioResults, configResults, dimensions[d], highlightedPoints, this.buildIDStreamPatterns);
+
+				String dimShortName = dimensions[d].getShortName();
+				String imgFileName = scenarioFileName + "_" + dimShortName;
+				File imgFile = createFile(outputDir, "graphs", imgFileName, "gif");
+				saveGraph(lineGraph, imgFile);
+				stream.print("<br><a name=\"" + dimShortName + "\"></a>\n");
+				stream.print("<br><b>" + dimensions[d].getName() + "</b><br>\n");
+				stream.print(dimensions[d].getDescription() + "<br><br>\n");
+				stream.print("<img src=\"graphs/");
+				stream.print(imgFile.getName());
+				stream.print("\" usemap=\"#" + lineGraph.fTitle + "\">");
+				stream.print("<map name=\"" + lineGraph.fTitle + "\">");
+				stream.print(lineGraph.getAreas());
+				stream.print("</map>\n");
+			}
+			stream.print("<br><br></body>\n");
+			stream.print(Utils.HTML_CLOSE);
+			if (stream != System.out)
+				stream.close();
+
+		} catch (AssertionFailedError e) {
+			e.printStackTrace();
+			continue;
+		}
+	}
+}
+
+/*
+ * Print the data for a build results.
+ */
+private void printTableLine(PrintStream stream, BuildResults buildResults) {
+	stream.print("<tr><td>");
+	stream.print(buildResults.getName());
+	if (buildResults.isBaseline()) stream.print(" (reference)");
+	stream.print("</td>");
+	Dim[] dimensions = AbstractResults.SUPPORTED_DIMS;
+	int dimLength = dimensions.length;
+	for (int d=0; d<dimLength; d++) {
+		int dim_id = dimensions[d].getId();
+		double stddev = buildResults.getDeviation(dim_id);
+		String displayValue = dimensions[d].getDisplayValue(buildResults.getValue(dim_id));
+		stream.print("<td>");
+		stream.print(displayValue);
+		if (stddev < 0) {
+			stream.print(" [n/a]\n");
+		} else if (stddev > 0) {
+			stream.print(" [");
+			stream.print(dimensions[d].getDisplayValue(stddev));
+			stream.print("]");
+		}
+		stream.print( "</td>");
+	}
+	stream.print("</tr>\n");
+}
+
+/*
+ * Print the line showing the difference between current and baseline builds.
+ */
+private void printDifferenceLine(PrintStream stream, ConfigResults configResults) {
+	stream.print("<tr><td>*Delta</td>");
+	Dim[] dimensions = AbstractResults.SUPPORTED_DIMS;
+	int dimLength = dimensions.length;
+	for (int d=0; d<dimLength; d++) {
+		Dim currentDim = dimensions[d];
+		int dim_id = currentDim.getId();
+		BuildResults currentBuild = configResults.getCurrentBuildResults();
+		BuildResults baselineBuild = configResults.getBaselineBuildResults();
+
+		// Compute difference values
+		double baselineValue = baselineBuild.getValue(dim_id);
+		double diffValue = baselineValue - currentBuild.getValue(dim_id);
+		double diffPercentage =  baselineValue == 0 ? 0 : Math.round(diffValue / baselineValue * 1000) / 10.0;
+		String diffDisplayValue = currentDim.getDisplayValue(diffValue);
+
+		// Set colors
+		String fontColor = "";
+		if (diffPercentage > 10) {
+			fontColor = "#006600";	// green
+		}
+		if (diffPercentage < -10) {
+			fontColor = "#FF0000";	// red
+		}
+
+		// Print line
+		String percentage = (diffPercentage == 0) ? "" : "<br>" + diffPercentage + " %";
+		if (diffPercentage > 10 || diffPercentage < -10) {
+			stream.print("<td><FONT COLOR=\"" + fontColor + "\"><b>" + diffDisplayValue + percentage + "</b></FONT></td>");
+		} else {
+			stream.print("<td>" + diffDisplayValue + percentage + "</td>");
+		}
+	}
+	stream.print("</tr></font>");
+}
+
 /*
  * Print details file of the scenario builds data.
  */
@@ -348,31 +370,31 @@
 		if (configResults == null || !configResults.isValid()) continue;
 		String scenarioName= scenarioResults.getName();
 		String scenarioFileName = scenarioResults.getFileName();
-		File outFile = new File(outputDir, scenarioFileName + "_raw.html");
+		File outputFile = createFile(outputDir, "raw", scenarioFileName, "html");
 		PrintStream stream = null;
 		try {
-			stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outFile)));
+			stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
 		} catch (FileNotFoundException e) {
-			System.err.println("can't create output file" + outFile); //$NON-NLS-1$
+			System.err.println("can't create output file" + outputFile); //$NON-NLS-1$
 		}
 		if (stream == null) stream = System.out;
 		RawDataTable currentResultsTable = new RawDataTable(configResults, this.buildIDStreamPatterns, stream);
 		RawDataTable baselineResultsTable = new RawDataTable(configResults, this.baselinePrefix, stream);
-		stream.println(Utils.HTML_OPEN);
-		stream.println(Utils.HTML_DEFAULT_CSS);
-		stream.println("<title>" + scenarioName + "(" + configBox + ")" + " - Details</title></head>"); //$NON-NLS-1$
-		stream.println("<h4>Scenario: " + scenarioName + " (" + configBox + ")</h4>"); //$NON-NLS-1$
-		stream.println("<a href=\""+scenarioFileName+".html\">VIEW GRAPH</a><br><br>"); //$NON-NLS-1$
-		stream.println("<table><td><b>Current Stream Test Runs</b></td><td><b>Baseline Test Runs</b></td></tr>\n");
-		stream.println("<tr valign=\"top\">");
+		stream.print(Utils.HTML_OPEN);
+		stream.print(Utils.HTML_DEFAULT_CSS);
+		stream.print("<title>" + scenarioName + "(" + configBox + ")" + " - Details</title></head>\n"); //$NON-NLS-1$
+		stream.print("<h4>Scenario: " + scenarioName + " (" + configBox + ")</h4>\n"); //$NON-NLS-1$
+		stream.print("<a href=\"../"+scenarioFileName+".html\">VIEW GRAPH</a><br><br>\n"); //$NON-NLS-1$
+		stream.print("<table><td><b>Current Stream Test Runs</b></td><td><b>Baseline Test Runs</b></td></tr>\n");
+		stream.print("<tr valign=\"top\">\n");
 		stream.print("<td>");
 		currentResultsTable.print();
-		stream.println("</td>");
+		stream.print("</td>\n");
 		stream.print("<td>");
 		baselineResultsTable.print();
-		stream.println("</td>");
-		stream.println("</tr>");
-		stream.println("</table>");
+		stream.print("</td>\n");
+		stream.print("</tr>\n");
+		stream.print("</table>\n");
 		stream.close();
 	}
 }
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/ScenarioStatusTable.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/ScenarioStatusTable.java
index 3ddf979..ec257e2 100644
--- a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/ScenarioStatusTable.java
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/ScenarioStatusTable.java
@@ -49,7 +49,7 @@
 	this.jsIdCount = 0;
 	for (int i=0; i<size; i++) {
 		ScenarioResults scenarioResults = (ScenarioResults) scenarios.get(i);
-		this.stream.println("<tr>");
+		this.stream.print("<tr>\n");
 		this.stream.print("<td>");
 		boolean hasSummary = scenarioResults.hasSummary();
 		if (hasSummary) this.stream.print("<b>");
@@ -65,25 +65,25 @@
 			this.stream.print(scenarioResults.getShortName());
 		}
 		if (hasSummary) this.stream.print("</b>");
-		this.stream.println();
+		this.stream.print("\n");
 		String[] configs = performanceResults.getConfigNames(true/*sort*/);
 		int length = configs.length;
 		for (int j=0; j<length; j++) {
 			printConfigStats(scenarioResults, configs[j]);
 		}
 	}
-	this.stream.println("</table>");
+	this.stream.print("</table>\n");
 }
 
 /*
  * Print the table columns title.
  */
 private void printColumnsTitle(int size, PerformanceResults performanceResults) {
-	this.stream.println("<table border=\"1\">");
-	this.stream.println("<tr>");
+	this.stream.print("<table border=\"1\">\n");
+	this.stream.print("<tr>\n");
 	this.stream.print("<td><h4>All ");
 	this.stream.print(size);
-	this.stream.println(" scenarios</h4></td>");
+	this.stream.print(" scenarios</h4></td>\n");
 	String[] configNames = performanceResults.getConfigNames(true/*sort*/);
 	String[] configBoxes = performanceResults.getConfigBoxes(true/*sort*/);
 	int length = configNames.length;
@@ -113,7 +113,7 @@
 		}
 		this.stream.print("<td><h5>");
 		this.stream.print(columnTitle);
-		this.stream.println("</h5>");
+		this.stream.print("</h5>\n");
 	}
 }
 
@@ -128,7 +128,7 @@
 	}
 	BuildResults currentBuildResults = configResults.getCurrentBuildResults();
 	String failure = currentBuildResults.getFailure();
-	double[] deviation = configResults.getCurrentBuildDeviation();
+	double[] deviation = configResults.getCurrentBuildDeltaInfo();
 	int confidence = Utils.confidenceLevel(deviation);
 	boolean hasFailure = failure != null;
 	String comment = currentBuildResults.getComment();
@@ -140,10 +140,10 @@
 		this.stream.print(configResults.getName());
 		this.stream.print('/');
 		this.stream.print(scenarioResults.getFileName());
-		this.stream.println(".html\">");
+		this.stream.print(".html\">\n");
 		this.stream.print("<img hspace=\"10\" border=\"0\" src=\"");
 		this.stream.print(image);
-		this.stream.println("\"/></a>");
+		this.stream.print("\"/></a>\n");
 	} else {
 		// create message with tooltip text including deviation with error plus failure message
 		this.jsIdCount+=1;
@@ -155,62 +155,63 @@
 		this.stream.print(configResults.getName());
 		this.stream.print('/');
 		this.stream.print(scenarioResults.getFileName());
-		this.stream.println(".html\">");
+		this.stream.print(".html\">\n");
 		this.stream.print("<img hspace=\"10\" border=\"0\" src=\"");
 		this.stream.print(image);
-		this.stream.println("\"/>");
+		this.stream.print("\"/>\n");
 		this.stream.print("<span class=\"hidden_tooltip\" id=\"toolTip");
 		this.stream.print(jsIdCount);
 		this.stream.print("\">");
 		this.stream.print(failure);
-		this.stream.println("</span></a>");
+		this.stream.print("</span></a>\n");
 	}
 	String result = Utils.failureMessage(deviation, false);
-	this.stream.println(result);
+	this.stream.print(result);
+	this.stream.print("\n");
 }
 
 /*
  * Print the status table explanationtitle.
  */
 private void printTitle() {
-	this.stream.println("<br><h4>Scenario Status</h4>");
-	this.stream.println("The following table gives a complete but compact view of performance results for the component.<br>");
-	this.stream.println("Each line of the table shows the results for one scenario on all machines.<br><br>");
-	this.stream.println("The name of the scenario is in <b>bold</b> when its results are also displayed in the fingerprints<br>");
-	this.stream.println("and starts with an '*' when the scenario has no results in the last baseline run.<br><br>");
-	this.stream.println("Here are information displayed for each test (ie. in each cell):");
-	this.stream.println("<ul>");
-	this.stream.println("<li>an icon showing whether the test fails or passes and whether it's reliable or not.<br>");
-	this.stream.println("The legend for this icon is:");
-	this.stream.println("<ul>");
+	this.stream.print("<br><h4>Scenario Status</h4>\n");
+	this.stream.print("The following table gives a complete but compact view of performance results for the component.<br>\n");
+	this.stream.print("Each line of the table shows the results for one scenario on all machines.<br><br>\n");
+	this.stream.print("The name of the scenario is in <b>bold</b> when its results are also displayed in the fingerprints<br>\n");
+	this.stream.print("and starts with an '*' when the scenario has no results in the last baseline run.<br><br>\n");
+	this.stream.print("Here are information displayed for each test (ie. in each cell):\n");
+	this.stream.print("<ul>\n");
+	this.stream.print("<li>an icon showing whether the test fails or passes and whether it's reliable or not.<br>\n");
+	this.stream.print("The legend for this icon is:\n");
+	this.stream.print("<ul>\n");
 	this.stream.print("<li>Green (<img src=\"");
 	this.stream.print(Utils.OK_IMAGE);
 	this.stream.print("\">): mark a <b>successful result</b>, which means this test has neither significant performance regression nor significant standard error</li>");
 	this.stream.print("<li>Red (<img src=\"");
 	this.stream.print(Utils.FAIL_IMAGE);
-	this.stream.println("\">): mark a <b>failing result</b>, which means this test shows a significant performance regression (more than 10%)</li>");
+	this.stream.print("\">): mark a <b>failing result</b>, which means this test shows a significant performance regression (more than 10%)</li>\n");
 	this.stream.print("<li>Gray (<img src=\"");
 	this.stream.print(Utils.FAIL_IMAGE_EXPLAINED);
-	this.stream.println("\">): mark a <b>failing result</b> (see above) with a comment explaining this degradation.</li>");
+	this.stream.print("\">): mark a <b>failing result</b> (see above) with a comment explaining this degradation.</li>\n");
 	this.stream.print("<li>Yellow (<img src=\"");
 	this.stream.print(Utils.FAIL_IMAGE_WARN);
 	this.stream.print("\"> or <img src=\"");
 	this.stream.print(Utils.OK_IMAGE_WARN);
 	this.stream.print("\">): mark a <b>failing or successful result</b> with a significant standard error (more than ");
 	this.stream.print(Utils.STANDARD_ERROR_THRESHOLD_STRING);
-	this.stream.println(")</li>");
+	this.stream.print(")</li>\n");
 	this.stream.print("<li>Black (<img src=\"");
 	this.stream.print(Utils.UNKNOWN_IMAGE);
 	this.stream.print("\">): mark an <b>undefined result</b>, which means that deviation on this test is not a number (<code>NaN</code>) or is infinite (happens when the reference value is equals to 0!)</li>");
-	this.stream.println("<li>\"n/a\": mark a test for with <b>no</b> performance results</li>");
-	this.stream.println("</ul></li>");
-	this.stream.println("<li>the value of the deviation from the baseline as a percentage (ie. formula is: <code>(build_test_time - baseline_test_time) / baseline_test_time</code>)</li>");
-	this.stream.println("<li>the value of the standard error of this deviation as a percentage (ie. formula is: <code>sqrt(build_test_stddev^2 / N + baseline_test_stddev^2 / N) / baseline_test_time</code>)<br>");
-	this.stream.println("When test only has one measure, the standard error cannot be computed and is replaced with a '<font color=\"#CCCC00\">[n/a]</font>'.</li>");
-	this.stream.println("</ul>");
-	this.stream.println("<u>Hints</u>:<ul>");
-	this.stream.println("<li>fly over image of failing tests to see the complete error message</li>");
-	this.stream.println("<li>to look at the complete and detailed test results, click on its image</li>");
-	this.stream.println("</ul>");
+	this.stream.print("<li>\"n/a\": mark a test for with <b>no</b> performance results</li>\n");
+	this.stream.print("</ul></li>\n");
+	this.stream.print("<li>the value of the deviation from the baseline as a percentage (ie. formula is: <code>(build_test_time - baseline_test_time) / baseline_test_time</code>)</li>\n");
+	this.stream.print("<li>the value of the standard error of this deviation as a percentage (ie. formula is: <code>sqrt(build_test_stddev^2 / N + baseline_test_stddev^2 / N) / baseline_test_time</code>)<br>\n");
+	this.stream.print("When test only has one measure, the standard error cannot be computed and is replaced with a '<font color=\"#CCCC00\">[n/a]</font>'.</li>\n");
+	this.stream.print("</ul>\n");
+	this.stream.print("<u>Hints</u>:<ul>\n");
+	this.stream.print("<li>fly over image of failing tests to see the complete error message</li>\n");
+	this.stream.print("<li>to look at the complete and detailed test results, click on its image</li>\n");
+	this.stream.print("</ul>\n");
 }
 }
diff --git a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Utils.java b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Utils.java
index 795b63e..94fe523 100644
--- a/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Utils.java
+++ b/bundles/org.eclipse.test.performance.ui/src/org/eclipse/test/performance/ui/Utils.java
@@ -10,12 +10,11 @@
  *******************************************************************************/
 package org.eclipse.test.performance.ui;
 
+import java.io.BufferedOutputStream;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.OutputStream;
 import java.text.DecimalFormat;
 import java.text.NumberFormat;
@@ -23,12 +22,15 @@
 import java.util.Calendar;
 import java.util.HashMap;
 
+import org.eclipse.swt.SWT;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
 import org.eclipse.swt.graphics.PaletteData;
 import org.eclipse.swt.graphics.RGB;
 import org.eclipse.test.internal.performance.PerformanceTestPlugin;
 import org.eclipse.test.internal.performance.db.Variations;
+import org.eclipse.test.internal.performance.results.AbstractResults;
 
 
 public class Utils {
@@ -58,6 +60,8 @@
 	public final static String FAIL_IMAGE="FAIL.gif";
 	public final static String FAIL_IMAGE_WARN="FAIL_caution.gif";
 	public final static String FAIL_IMAGE_EXPLAINED="FAIL_greyed.gif";
+	public final static String LIGHT="light.gif";
+	public final static String WARNING_OBJ="warning_obj.gif";
 	public final static int OK = 0;
 	public final static int NAN = 0x1;
 	public final static int ERR = 0x2;
@@ -66,24 +70,24 @@
 	 * Return &lt;html&gt;&lt;head&gt;&lt;meta http-equiv="Content-Type"
 	 *         content="text/html; charset=iso-8859-1"&gt;
 	 */
-	public static String HTML_OPEN = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">";
+	public final static String HTML_OPEN = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\n";
 
 	/**
 	 * Return "&lt;/html&gt;".
 	 */
-	public static String HTML_CLOSE = "</html>";
+	public final static String HTML_CLOSE = "</html>\n";
 
 	/**
 	 * Default style-sheet used on eclipse.org
 	 */
-	public static String HTML_DEFAULT_CSS = "<style type=\"text/css\">" + "p, table, td, th {  font-family: arial, helvetica, geneva; font-size: 10pt}\n"
+	public final static String HTML_DEFAULT_CSS = "<style type=\"text/css\">" + "p, table, td, th {  font-family: arial, helvetica, geneva; font-size: 10pt}\n"
 			+ "pre {  font-family: \"Courier New\", Courier, mono; font-size: 10pt}\n" + "h2 { font-family: arial, helvetica, geneva; font-size: 18pt; font-weight: bold ; line-height: 14px}\n"
 			+ "code {  font-family: \"Courier New\", Courier, mono; font-size: 10pt}\n" + "sup {  font-family: arial,helvetica,geneva; font-size: 10px}\n"
 			+ "h3 {  font-family: arial, helvetica, geneva; font-size: 14pt; font-weight: bold}\n" + "li {  font-family: arial, helvetica, geneva; font-size: 10pt}\n"
 			+ "h1 {  font-family: arial, helvetica, geneva; font-size: 28px; font-weight: bold}\n"
 			+ "body {  font-family: arial, helvetica, geneva; font-size: 10pt; clip:   rect(   ); margin-top: 5mm; margin-left: 3mm}\n"
 			+ ".indextop { font-size: x-large;; font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: bold}\n"
-			+ ".indexsub { font-size: xx-small;; font-family: Arial, Helvetica, sans-serif; color: #8080FF}\n" + "</style>";
+			+ ".indexsub { font-size: xx-small;; font-family: Arial, Helvetica, sans-serif; color: #8080FF}\n" + "</style>\n\n";
 
 	/**
 	 * Creates a Variations object using build id pattern, config and jvm.
@@ -102,41 +106,43 @@
 	}
 
 	/**
-	 * Utility method to copy a file.
-	 *
-	 * @param src the source file.
-	 * @param dest the destination.
+	 * Copy all image files.
 	 */
-	private static void copyFile(File src, File dest) {
-
-		try {
-			InputStream in = new FileInputStream(src);
-			OutputStream out = new FileOutputStream(dest);
-			byte[] buf = new byte[1024];
-			int len;
-			while ((len = in.read(buf)) > 0) {
-				out.write(buf, 0, len);
-			}
-			in.close();
-			out.close();
-
-		} catch (FileNotFoundException e) {
-			e.printStackTrace();
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
-	}
 	public static void copyImages(File images, File output) {
-		copyFile(new File(images, FAIL_IMAGE), new File(output, FAIL_IMAGE));
-		copyFile(new File(images, FAIL_IMAGE_EXPLAINED), new File(output, FAIL_IMAGE_EXPLAINED));
-		copyFile(new File(images, FAIL_IMAGE_WARN), new File(output, FAIL_IMAGE_WARN));
-		copyFile(new File(images, OK_IMAGE), new File(output, OK_IMAGE));
-		copyFile(new File(images, OK_IMAGE_WARN), new File(output, OK_IMAGE_WARN));
-		copyFile(new File(images, UNKNOWN_IMAGE), new File(output, UNKNOWN_IMAGE));
+		AbstractResults.copyFile(new File(images, FAIL_IMAGE), new File(output, FAIL_IMAGE));
+		AbstractResults.copyFile(new File(images, FAIL_IMAGE_EXPLAINED), new File(output, FAIL_IMAGE_EXPLAINED));
+		AbstractResults.copyFile(new File(images, FAIL_IMAGE_WARN), new File(output, FAIL_IMAGE_WARN));
+		AbstractResults.copyFile(new File(images, OK_IMAGE), new File(output, OK_IMAGE));
+		AbstractResults.copyFile(new File(images, OK_IMAGE_WARN), new File(output, OK_IMAGE_WARN));
+		AbstractResults.copyFile(new File(images, UNKNOWN_IMAGE), new File(output, UNKNOWN_IMAGE));
+		AbstractResults.copyFile(new File(images, LIGHT), new File(output, LIGHT));
+		AbstractResults.copyFile(new File(images, WARNING_OBJ), new File(output, WARNING_OBJ));
 	}
+
+	/**
+	 * Copy all scripts files.
+	 */
 	public static void copyScripts(File scripts, File output) {
-		copyFile(new File(scripts, "ToolTip.css"), new File(output, "ToolTip.css"));
-		copyFile(new File(scripts, "ToolTip.js"), new File(output, "ToolTip.js"));
+		AbstractResults.copyFile(new File(scripts, "ToolTip.css"), new File(output, "ToolTip.css"));
+		AbstractResults.copyFile(new File(scripts, "ToolTip.js"), new File(output, "ToolTip.js"));
+		AbstractResults.copyFile(new File(scripts, "Fingerprints.js"), new File(output, "Fingerprints.js"));
+	}
+
+	/**
+	 * Copy all doc files.
+	 */
+	public static void copyDoc(File docDir, File output) {
+		File[] docFiles = docDir.listFiles();
+		for (int i=0; i<docFiles.length; i++) {
+			File file = docFiles[i];
+			if (file.isDirectory()) {
+				File subdir = new File(output, file.getName());
+				subdir.mkdir();
+				copyDoc(file, subdir);
+			} else {
+				AbstractResults.copyFile(file, new File(output, file.getName()));
+			}
+		}
 	}
 
 	/**
@@ -384,4 +390,31 @@
 	    return image;
     }
 
+/**
+ * @param outputFile
+ * @param image
+ */
+public static void saveImage(File outputFile, Image image) {
+	// Save image
+	ImageData data = downSample(image);
+	ImageLoader imageLoader = new ImageLoader();
+	imageLoader.data = new ImageData[] { data };
+
+	OutputStream out = null;
+	try {
+		out = new BufferedOutputStream(new FileOutputStream(outputFile));
+		imageLoader.save(out, SWT.IMAGE_GIF);
+	} catch (FileNotFoundException e) {
+		e.printStackTrace();
+	} finally {
+		image.dispose();
+		if (out != null) {
+			try {
+				out.close();
+			} catch (IOException e1) {
+				// silently ignored
+			}
+		}
+	}
+}
 }
\ No newline at end of file