Home | History | Annotate | Download | only in ui
      1 /*******************************************************************************
      2  * Copyright (c) 2000, 2009 IBM Corporation and others.
      3  * All rights reserved. This program and the accompanying materials
      4  * are made available under the terms of the Eclipse Public License v1.0
      5  * which accompanies this distribution, and is available at
      6  * http://www.eclipse.org/legal/epl-v10.html
      7  *
      8  * Contributors:
      9  *     IBM Corporation - initial API and implementation
     10  *******************************************************************************/
     11 package org.eclipse.test.performance.ui;
     12 
     13 import java.io.BufferedOutputStream;
     14 import java.io.File;
     15 import java.io.FileNotFoundException;
     16 import java.io.FileOutputStream;
     17 import java.io.PrintStream;
     18 import java.text.SimpleDateFormat;
     19 import java.util.ArrayList;
     20 import java.util.Arrays;
     21 import java.util.Date;
     22 import java.util.List;
     23 import java.util.StringTokenizer;
     24 
     25 import org.eclipse.core.runtime.IProgressMonitor;
     26 import org.eclipse.core.runtime.IStatus;
     27 import org.eclipse.core.runtime.OperationCanceledException;
     28 import org.eclipse.core.runtime.Status;
     29 import org.eclipse.core.runtime.SubMonitor;
     30 import org.eclipse.swt.widgets.Display;
     31 import org.eclipse.test.internal.performance.results.db.ConfigResults;
     32 import org.eclipse.test.internal.performance.results.db.DB_Results;
     33 import org.eclipse.test.internal.performance.results.db.PerformanceResults;
     34 import org.eclipse.test.internal.performance.results.db.ScenarioResults;
     35 import org.eclipse.test.internal.performance.results.utils.Util;
     36 import org.osgi.framework.Bundle;
     37 
     38 /**
     39  * Main class to generate performance results of all scenarios matching a given pattern
     40  * in one HTML page per component.
     41  *
     42  * @see #printUsage() method to see a detailed parameters usage
     43  */
     44 public class GenerateResults {
     45 
     46 /**
     47  * Prefix of baseline builds displayed in data graphs.
     48  * This field is set using <b>-baseline.prefix</b> argument.
     49  * <p>
     50  * Example:
     51  *		<pre>-baseline.prefix 3.2_200606291905</pre>
     52  *
     53  * @see #currentBuildPrefixes
     54  */
     55 String baselinePrefix = null;
     56 
     57 /**
     58  * Root directory where all files are generated.
     59  * This field is set using <b>-output</b> argument.
     60  * <p>
     61  * Example:
     62  * 	<pre>-output /releng/results/I20070615-1200/performance</pre>
     63  */
     64 File outputDir;
     65 
     66 /**
     67  * Root directory where all data are locally stored to speed-up generation.
     68  * This field is set using <b>-dataDir</b> argument.
     69  * <p>
     70  * Example:
     71  * 	<pre>-dataDir /tmp</pre>
     72  */
     73 File dataDir;
     74 
     75 /**
     76  * Arrays of 2 strings which contains config information: name and description.
     77  * This field is set using <b>-config</b> and/or <b>-config.properties</b> arguments.
     78  * <p>
     79  * Example:
     80  * <pre>
     81  * 	-config eclipseperflnx3_R3.3,eclipseperfwin2_R3.3,eclipseperflnx2_R3.3,eclipseperfwin1_R3.3,eclipseperflnx1_R3.3
     82  * 	-config.properties
     83  * 		"eclipseperfwin1_R3.3,Win XP Sun 1.4.2_08 (2 GHz 512 MB);
     84  * 		eclipseperflnx1_R3.3,RHEL 3.0 Sun 1.4.2_08 (2 GHz 512 MB);
     85  * 		eclipseperfwin2_R3.3,Win XP Sun 1.4.2_08 (3 GHz 2 GB);
     86  * 		eclipseperflnx2_R3.3,RHEL 3.0 Sun 1.4.2_08 (3 GHz 2 GB);
     87  * 		eclipseperflnx3_R3.3,RHEL 4.0 Sun 1.4.2_08 (3 GHz 2.5 GB)"
     88  * </pre>
     89  * Note that:
     90  * <ul>
     91  * <li>if only <b>-config</b> is set, then configuration name is used for description </li>
     92  * <li>if only <b>-config.properties</b> is set, then all configurations defined with this argument are generated
     93  * <li>if both arguments are defined, then only configurations defined by <b>-config</b> argument are generated,
     94  * 		<b>-config.properties</b> argument is only used to set the configuration description.</li>
     95  * </ul>
     96  */
     97 String[][] configDescriptors;
     98 
     99 /**
    100  * Scenario pattern used to generate performance results.
    101  * This field is set using <b>-scenarioPattern</b> argument.
    102  * <p>
    103  * Note that this pattern uses SQL conventions, not RegEx ones,
    104  * which means that '%' is used to match several consecutive characters
    105  * and '_' to match a single character.
    106  * <p>
    107  * Example:
    108  * 	<pre>-scenario.pattern org.eclipse.%.test</pre>
    109  */
    110 String scenarioPattern;
    111 
    112 /**
    113  * A list of prefixes for builds displayed in data graphs.
    114  * This field is set using <b>-currentPrefix</b> argument.
    115  * <p>
    116  * Example:
    117  * 	<pre>-current.prefix N, I</pre>
    118  *
    119  * @see #baselinePrefix
    120  */
    121 List currentBuildPrefixes;
    122 
    123 /**
    124  * A list of prefixes of builds to highlight in displayed data graphs.
    125  * This field is set using <b>-highlight</b> and/or <b>-highlight.latest</b> arguments.
    126  * <p>
    127  * Example:
    128  * 	<pre>-higlight 3_2</pre>
    129  */
    130 List pointsOfInterest;
    131 
    132 /**
    133  * Tells whether only fingerprints has to be generated.
    134  * This field is set to <code>true</code> if <b>-fingerprints</b> argument is specified.
    135  * <p>
    136  * Default is <code>false</code> which means that scenario data
    137  * will also be generated.
    138  *
    139  * @see #genData
    140  * @see #genAll
    141  */
    142 boolean genFingerPrints = false;
    143 
    144 /**
    145  * Tells whether only fingerprints has to be generated.
    146  * This field is set to <code>true</code> if <b>-data</b> argument is specified.
    147  * <p>
    148  * Default is <code>false</code> which means that fingerprints
    149  * will also be generated.
    150  *
    151  * @see #genFingerPrints
    152  * @see #genAll
    153  */
    154 boolean genData = false;
    155 
    156 /**
    157  * Tells whether only fingerprints has to be generated.
    158  * This field is set to <code>false</code>
    159  * if <b>-fingerprints</b> or <b>-data</b> argument is specified.
    160  * <p>
    161  * Default is <code>true</code> which means that scenario data
    162  * will also be generated.
    163  *
    164  * @see #genData
    165  * @see #genFingerPrints
    166  */
    167 boolean genAll = true;
    168 
    169 /**
    170  * Tells whether information should be displayed in the console while generating.
    171  * This field is set to <code>true</code> if <b>-print</b> argument is specified.
    172  * <p>
    173  * Default is <code>false</code> which means that nothing is print during the generation.
    174  */
    175 PrintStream printStream = null;
    176 
    177 /**
    178  * Tells what should be the failure percentage threshold.
    179  * <p>
    180  * Default is 10%.
    181  */
    182 int failure_threshold = 10; // PerformanceTestPlugin.getDBLocation().startsWith("net://");
    183 
    184 PerformanceResults performanceResults;
    185 
    186 public GenerateResults() {
    187 }
    188 
    189 public GenerateResults(PerformanceResults results, String current, String baseline, boolean fingerprints, File data, File output) {
    190 	this.dataDir = data;
    191 	this.outputDir = output;
    192 	this.genFingerPrints = fingerprints;
    193 	this.genAll = !fingerprints;
    194 	this.performanceResults = results;
    195 	this.printStream = System.out;
    196 	setDefaults(current, baseline);
    197 }
    198 
    199 /*
    200  * Parse the command arguments and create corresponding performance
    201  * results object.
    202  */
    203 private void parse(String[] args) {
    204 	StringBuffer buffer = new StringBuffer("Parameters used to generate performance results (");
    205 	buffer.append(new SimpleDateFormat().format(new Date(System.currentTimeMillis())));
    206 	buffer.append("):\n");
    207 	int i = 0;
    208 	int argsLength = args.length;
    209 	if (argsLength == 0) {
    210 		printUsage();
    211 	}
    212 
    213 	String currentBuildId = null;
    214 	String baseline = null;
    215 	String jvm = null;
    216 	this.configDescriptors = null;
    217 
    218 	while (i < argsLength) {
    219 		String arg = args[i];
    220 		if (!arg.startsWith("-")) {
    221 			i++;
    222 			continue;
    223 		}
    224 		if (argsLength == i + 1 && i != argsLength - 1) {
    225 			System.out.println("Missing value for last parameter");
    226 			printUsage();
    227 		}
    228 		if (arg.equals("-baseline")) {
    229 			baseline = args[i + 1];
    230 			if (baseline.startsWith("-")) {
    231 				System.out.println("Missing value for "+arg+" parameter");
    232 				printUsage();
    233 			}
    234 			buffer.append("	-baseline = "+baseline+'\n');
    235 			i++;
    236 			continue;
    237 		}
    238 		if (arg.equals("-baseline.prefix")) {
    239 			this.baselinePrefix = args[i + 1];
    240 			if (this.baselinePrefix.startsWith("-")) {
    241 				System.out.println("Missing value for "+arg+" parameter");
    242 				printUsage();
    243 			}
    244 			buffer.append("	").append(arg).append(" = ").append(this.baselinePrefix).append('\n');
    245 			i++;
    246 			continue;
    247 		}
    248 		if (arg.equals("-current.prefix")) {
    249 			String idPrefixList = args[i + 1];
    250 			if (idPrefixList.startsWith("-")) {
    251 				System.out.println("Missing value for "+arg+" parameter");
    252 				printUsage();
    253 			}
    254 			buffer.append("	").append(arg).append(" = ");
    255 			String[] ids = idPrefixList.split(",");
    256 			this.currentBuildPrefixes = new ArrayList();
    257 			for (int j = 0; j < ids.length; j++) {
    258 				this.currentBuildPrefixes.add(ids[j]);
    259 				buffer.append(ids[j]);
    260 			}
    261 			buffer.append('\n');
    262 			i++;
    263 			continue;
    264 		}
    265 		if (arg.equals("-highlight") || arg.equals("-highlight.latest")) {
    266 			if (args[i + 1].startsWith("-")) {
    267 				System.out.println("Missing value for "+arg+" parameter");
    268 				printUsage();
    269 			}
    270 			buffer.append("	").append(arg).append(" = ");
    271 			String[] ids = args[i + 1].split(",");
    272 			this.pointsOfInterest = new ArrayList();
    273 			for (int j = 0; j < ids.length; j++) {
    274 				this.pointsOfInterest.add(ids[j]);
    275 				buffer.append(ids[j]);
    276 			}
    277 			buffer.append('\n');
    278 			i++;
    279 			continue;
    280 		}
    281 		if (arg.equals("-current")) {
    282 			currentBuildId  = args[i + 1];
    283 			if (currentBuildId.startsWith("-")) {
    284 				System.out.println("Missing value for "+arg+" parameter");
    285 				printUsage();
    286 			}
    287 			buffer.append("	").append(arg).append(" = ").append(currentBuildId).append('\n');
    288 			i++;
    289 			continue;
    290 		}
    291 		if (arg.equals("-jvm")) {
    292 			jvm = args[i + 1];
    293 			if (jvm.startsWith("-")) {
    294 				System.out.println("Missing value for "+arg+" parameter");
    295 				printUsage();
    296 			}
    297 			buffer.append("	").append(arg).append(" = ").append(jvm).append('\n');
    298 			i++;
    299 			continue;
    300 		}
    301 		if (arg.equals("-output")) {
    302 			String dir = args[++i];
    303 			if (dir.startsWith("-")) {
    304 				System.out.println("Missing value for "+arg+" parameter");
    305 				printUsage();
    306 			}
    307 			this.outputDir = new File(dir);
    308 			if (!this.outputDir.exists() && !this.outputDir.mkdirs()) {
    309 				System.err.println("Cannot create directory "+dir+" to write results in!");
    310 				System.exit(2);
    311 			}
    312 			buffer.append("	").append(arg).append(" = ").append(dir).append('\n');
    313 			continue;
    314 		}
    315 		if (arg.equals("-dataDir")) {
    316 			String dir = args[++i];
    317 			if (dir.startsWith("-")) {
    318 				System.out.println("Missing value for "+arg+" parameter");
    319 				printUsage();
    320 			}
    321 			this.dataDir = new File(dir);
    322 			if (!this.dataDir.exists() && !this.dataDir.mkdirs()) {
    323 				System.err.println("Cannot create directory "+dir+" to save data locally!");
    324 				System.exit(2);
    325 			}
    326 			buffer.append("	").append(arg).append(" = ").append(dir).append('\n');
    327 			continue;
    328 		}
    329 		if (arg.equals("-config")) {
    330 			String configs = args[i + 1];
    331 			if (configs.startsWith("-")) {
    332 				System.out.println("Missing value for "+arg+" parameter");
    333 				printUsage();
    334 			}
    335 			String[] names = configs.split(",");
    336 			int length = names.length;
    337 			buffer.append("	").append(arg).append(" = ");
    338 			for (int j=0; j<length; j++) {
    339 				if (j>0) buffer.append(',');
    340 				buffer.append(names[j]);
    341 			}
    342 			if (this.configDescriptors == null) {
    343 				this.configDescriptors = new String[length][2];
    344 				for (int j=0; j<length; j++) {
    345 					this.configDescriptors[j][0] = names[j];
    346 					this.configDescriptors[j][1] = names[j];
    347 				}
    348 			} else {
    349 				int confLength = this.configDescriptors[0].length;
    350 				int newLength = confLength;
    351 				mainLoop: for (int j=0; j<confLength; j++) {
    352 					for (int k=0; k<length; k++) {
    353 						if (this.configDescriptors[j][0].equals(names[k])) {
    354 							continue mainLoop;
    355 						}
    356 					}
    357 					this.configDescriptors[j][0] = null;
    358 					this.configDescriptors[j][1] = null;
    359 					newLength--;
    360 				}
    361 				if (newLength < confLength) {
    362 					String[][] newDescriptors = new String[newLength][2];
    363 					for (int j=0, c=0; j<newLength; j++) {
    364 						if (this.configDescriptors[c] != null) {
    365 							newDescriptors[j][0] = this.configDescriptors[c][0];
    366 							newDescriptors[j][1] = this.configDescriptors[c][1];
    367 						} else {
    368 							c++;
    369 						}
    370 					}
    371 					this.configDescriptors = newDescriptors;
    372 				}
    373 			}
    374 			buffer.append('\n');
    375 			i++;
    376 			continue;
    377 		}
    378 		if (arg.equals("-config.properties")) {
    379 			String configProperties = args[i + 1];
    380 			if (configProperties.startsWith("-")) {
    381 				System.out.println("Missing value for "+arg+" parameter");
    382 				printUsage();
    383 			}
    384 			if (this.configDescriptors == null) {
    385 				System.out.println("Missing -config parameter");
    386 				printUsage();
    387 			}
    388 			int length = this.configDescriptors.length;
    389 			StringTokenizer tokenizer = new StringTokenizer(configProperties, ";");
    390 			buffer.append('\t').append(arg).append(" = '").append(configProperties).append("' splitted in ").append(length).append(" configs:");
    391 			while (tokenizer.hasMoreTokens()) {
    392 				String labelDescriptor = tokenizer.nextToken();
    393 				String[] elements = labelDescriptor.trim().split(",");
    394 				for (int j=0; j<length; j++) {
    395 					if (elements[0].equals(this.configDescriptors[j][0])) {
    396 						this.configDescriptors[j][1] = elements[1];
    397 						buffer.append("\n\t\t+ ");
    398 						buffer.append(elements[0]);
    399 						buffer.append(" -> ");
    400 						buffer.append(elements[1]);
    401 					}
    402 				}
    403 			}
    404 			buffer.append('\n');
    405 			i++;
    406 			continue;
    407 		}
    408 		if (arg.equals("-scenario.filter") || arg.equals("-scenario.pattern")) {
    409 			this.scenarioPattern= args[i + 1];
    410 			if (this.scenarioPattern.startsWith("-")) {
    411 				System.out.println("Missing value for "+arg+" parameter");
    412 				printUsage();
    413 			}
    414 			buffer.append("	").append(arg).append(" = ").append(this.scenarioPattern).append('\n');
    415 			i++;
    416 			continue;
    417 		}
    418 		if (arg.equals("-fingerprints")) {
    419 			this.genFingerPrints = true;
    420 			this.genAll = false;
    421 			buffer.append("	").append(arg).append('\n');
    422 			i++;
    423 			continue;
    424 		}
    425 		if (arg.equals("-data")) {
    426 			this.genData = true;
    427 			this.genAll = false;
    428 			buffer.append("	").append(arg).append('\n');
    429 			i++;
    430 			continue;
    431 		}
    432 		if (arg.equals("-print")) {
    433 			this.printStream = System.out; // default is to print to console
    434 			buffer.append("	").append(arg);
    435 			i++;
    436 			String printFile = i==argsLength ? null : args[i];
    437 			if (printFile==null ||printFile.startsWith("-")) {
    438 				buffer.append(" (to the console)").append('\n');
    439 			} else {
    440 				try {
    441 					this.printStream = new PrintStream(new BufferedOutputStream(new FileOutputStream(printFile)));
    442 				}
    443 				catch (FileNotFoundException fnfe) {
    444 					// use the console if the output file cannot be created
    445 				}
    446 				buffer.append(" (to file: ").append(printFile).append(")\n");
    447 			}
    448 			continue;
    449 		}
    450 		if (arg.equals("-failure.threshold")) {
    451 			String value = args[i + 1];
    452 			try {
    453 				this.failure_threshold = Integer.parseInt(value);
    454 				if (this.failure_threshold < 0) {
    455 					System.out.println("Value for "+arg+" parameter must be positive.");
    456 					printUsage();
    457 				}
    458 			}
    459 			catch (NumberFormatException nfe) {
    460 				System.out.println("Invalid value for "+arg+" parameter");
    461 				printUsage();
    462 			}
    463 			buffer.append("	").append(arg).append(" = ").append(value).append('\n');
    464 			i++;
    465 			continue;
    466 		}
    467 		i++;
    468 	}
    469 	if (this.printStream != null) {
    470 		this.printStream.print(buffer.toString());
    471 	}
    472 
    473 	// Stop if some mandatory parameters are missing
    474 	if (this.outputDir == null || this.configDescriptors == null || jvm == null) {
    475 		printUsage();
    476 	}
    477 
    478 	// Set performance results
    479 	setPerformanceResults(currentBuildId, baseline);
    480 }
    481 
    482 /*
    483  * Print component PHP file
    484  */
    485 private void printComponent(/*PerformanceResults performanceResults, */String component) throws FileNotFoundException {
    486 	if (this.printStream != null) this.printStream.print(".");
    487 	File outputFile = new File(this.outputDir, component + ".php");
    488 	PrintStream stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
    489 
    490 	// Print header
    491 	boolean isGlobal = component.startsWith("global");
    492 	if (isGlobal) {
    493 		File globalFile = new File(this.outputDir, "global.php");
    494 		PrintStream gStream = new PrintStream(new BufferedOutputStream(new FileOutputStream(globalFile)));
    495 		gStream.print(Utils.HTML_OPEN);
    496 		gStream.print("</head>\n");
    497 		gStream.print("<body>\n");
    498 		gStream.print("<?php\n");
    499 		gStream.print("	include(\"global_fp.php\");\n");
    500 		gStream.print("?>\n");
    501 		gStream.print("<table border=0 cellpadding=2 cellspacing=5 width=\"100%\">\n");
    502 		gStream.print("<tbody><tr> <td colspan=3 align=\"left\" bgcolor=\"#0080c0\" valign=\"top\"><b><font color=\"#ffffff\" face=\"Arial,Helvetica\">\n");
    503 		gStream.print("Detailed performance data grouped by scenario prefix</font></b></td></tr></tbody></table>\n");
    504 		gStream.print("<a href=\"org.eclipse.ant.php?\">org.eclipse.ant*</a><br>\n");
    505 		gStream.print("<a href=\"org.eclipse.compare.php?\">org.eclipse.compare*</a><br>\n");
    506 		gStream.print("<a href=\"org.eclipse.core.php?\">org.eclipse.core*</a><br>\n");
    507 		gStream.print("<a href=\"org.eclipse.jdt.core.php?\">org.eclipse.jdt.core*</a><br>\n");
    508 		gStream.print("<a href=\"org.eclipse.jdt.debug.php?\">org.eclipse.jdt.debug*</a><br>\n");
    509 		gStream.print("<a href=\"org.eclipse.jdt.text.php?\">org.eclipse.jdt.text*</a><br>\n");
    510 		gStream.print("<a href=\"org.eclipse.jdt.ui.php?\">org.eclipse.jdt.ui*</a><br>\n");
    511 		gStream.print("<a href=\"org.eclipse.jface.php?\">org.eclipse.jface*</a><br>\n");
    512 		gStream.print("<a href=\"org.eclipse.osgi.php?\">org.eclipse.osgi*</a><br>\n");
    513 		gStream.print("<a href=\"org.eclipse.pde.api.tools.php?\">org.eclipse.pde.api.tools*</a><br>\n");
    514 		gStream.print("<a href=\"org.eclipse.pde.ui.php?\">org.eclipse.pde.ui*</a><br>\n");
    515 		gStream.print("<a href=\"org.eclipse.swt.php?\">org.eclipse.swt*</a><br>\n");
    516 		gStream.print("<a href=\"org.eclipse.team.php?\">org.eclipse.team*</a><br>\n");
    517 		gStream.print("<a href=\"org.eclipse.ua.php?\">org.eclipse.ua*</a><br>\n");
    518 		gStream.print("<a href=\"org.eclipse.ui.php?\">org.eclipse.ui*</a><br><p><br><br>\n");
    519 		gStream.print("</body>\n");
    520 		gStream.print(Utils.HTML_CLOSE);
    521 		gStream.close();
    522 	} else {
    523 		stream.print(Utils.HTML_OPEN);
    524 	}
    525 	stream.print("<link href=\""+Utils.TOOLTIP_STYLE+"\" rel=\"stylesheet\" type=\"text/css\">\n");
    526 	stream.print("<script src=\""+Utils.TOOLTIP_SCRIPT+"\"></script>\n");
    527 	stream.print("<script src=\""+Utils.FINGERPRINT_SCRIPT+"\"></script>\n");
    528 	stream.print(Utils.HTML_DEFAULT_CSS);
    529 
    530 	// Print title
    531 	stream.print("<body>");
    532 	printComponentTitle(/*performanceResults, */component, isGlobal, stream);
    533 
    534 	// print the html representation of fingerprint for each config
    535 	Display display = Display.getDefault();
    536 	if (this.genFingerPrints || this.genAll) {
    537 		final FingerPrint fingerprint = new FingerPrint(component, stream, this.outputDir);
    538 		display.syncExec(
    539 			new Runnable() {
    540 				public void run(){
    541 					try {
    542 						fingerprint.print(GenerateResults.this.performanceResults);
    543 					} catch (Exception ex) {
    544 						ex.printStackTrace();
    545 					}
    546 				}
    547 			}
    548 		);
    549 	}
    550 //	FingerPrint fingerprint = new FingerPrint(component, stream, this.outputDir);
    551 //	fingerprint.print(performanceResults);
    552 
    553 	// print scenario status table
    554 	if (!isGlobal) {
    555 		// print the component scenario status table beneath the fingerprint
    556 		final ScenarioStatusTable sst = new ScenarioStatusTable(component, stream);
    557 		display.syncExec(
    558 			new Runnable() {
    559 				public void run(){
    560 					try {
    561 						sst.print(GenerateResults.this.performanceResults);
    562 					} catch (Exception ex) {
    563 						ex.printStackTrace();
    564 					}
    565 				}
    566 			}
    567 		);
    568 //		ScenarioStatusTable sst = new ScenarioStatusTable(component, stream);
    569 //		sst.print(performanceResults);
    570 	}
    571 
    572 	stream.print(Utils.HTML_CLOSE);
    573 	stream.close();
    574 }
    575 
    576 private void printComponentTitle(/*PerformanceResults performanceResults, */String component, boolean isGlobal, PrintStream stream) {
    577 	String baselineName = this.performanceResults.getBaselineName();
    578 	String currentName = this.performanceResults.getName();
    579 
    580 	// Print title line
    581 	stream.print("<h3>Performance of ");
    582 	if (!isGlobal) {
    583 		stream.print(component);
    584 		stream.print(": ");
    585 	}
    586 	stream.print(currentName);
    587 	stream.print(" relative to ");
    588 	int index = baselineName.indexOf('_');
    589 	if (index > 0) {
    590 		stream.print(baselineName.substring(0, index));
    591 		stream.print(" (");
    592 		index = baselineName.lastIndexOf('_');
    593 		stream.print(baselineName.substring(index+1, baselineName.length()));
    594 		stream.print(')');
    595 	} else {
    596 		stream.print(baselineName);
    597 	}
    598 		stream.print("</h3>\n");
    599 
    600 	// Print reference to global results
    601 	if (!isGlobal) {
    602 		stream.print("<?php\n");
    603 		stream.print("	$type=$_SERVER['QUERY_STRING'];\n");
    604 		stream.print("	if ($type==\"\") {\n");
    605 		stream.print("		$type=\"fp_type=0\";\n");
    606 		stream.print("	}\n");
    607 		stream.print("	$href=\"<a href=\\\"performance.php?\";\n");
    608 		stream.print("	$href=$href . $type . \"\\\">Back to global results</a><br><br>\";\n");
    609 		stream.print("	echo $href;\n");
    610 		stream.print("?>\n");
    611 	}
    612 }
    613 
    614 /*
    615  * Print summary of coefficient of variation for each scenario of the given pattern
    616  * both for baseline and current builds.
    617  */
    618 private void printSummary(/*PerformanceResults performanceResults*/) {
    619 	long start = System.currentTimeMillis();
    620 	if (this.printStream != null) this.printStream.print("Print scenarios variations summary...");
    621 	File outputFile = new File(this.outputDir, "cvsummary.html");
    622 	PrintStream stream = null;
    623 	try {
    624 		stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
    625 		printSummaryPresentation(stream);
    626 //		List scenarioNames = DB_Results.getScenarios();
    627 //		int size = scenarioNames.size();
    628 		String[] components = this.performanceResults.getComponents();
    629 		int componentsLength = components.length;
    630 		printSummaryColumnsTitle(stream/*, performanceResults*/);
    631 		String[] configs = this.performanceResults.getConfigNames(true/*sorted*/);
    632 		int configsLength = configs.length;
    633 		for (int i=0; i<componentsLength; i++) {
    634 			String componentName = components[i];
    635 			List scenarioNames = this.performanceResults.getComponentScenarios(componentName);
    636 			int size = scenarioNames.size();
    637 			for (int s=0; s<size; s++) {
    638 				String scenarioName = ((ScenarioResults) scenarioNames.get(s)).getName();
    639 				if (scenarioName == null) continue;
    640 				ScenarioResults scenarioResults = this.performanceResults.getScenarioResults(scenarioName);
    641 				if (scenarioResults != null) {
    642 					stream.print("<tr>\n");
    643 					for (int j=0; j<2; j++) {
    644 						for (int c=0; c<configsLength; c++) {
    645 							printSummaryScenarioLine(j, configs[c], scenarioResults, stream);
    646 						}
    647 					}
    648 					stream.print("<td>");
    649 					stream.print(scenarioName);
    650 					stream.print("</td></tr>\n");
    651 				}
    652 			}
    653 		}
    654 	} catch (Exception e) {
    655 		e.printStackTrace();
    656 	} finally {
    657 		stream.print("</table></body></html>\n");
    658 		stream.flush();
    659 		stream.close();
    660 	}
    661 	if (this.printStream != null) this.printStream.println("done in "+(System.currentTimeMillis()-start)+"ms");
    662 }
    663 
    664 /*
    665  * Print summary presentation (eg. file start and text presenting the purpose of this file contents)..
    666  */
    667 private void printSummaryPresentation(PrintStream stream) {
    668 	stream.print(Utils.HTML_OPEN);
    669 	stream.print(Utils.HTML_DEFAULT_CSS);
    670 	stream.print("<title>Summary of Elapsed Process Variation Coefficients</title></head>\n");
    671 	stream.print("<body><h3>Summary of Elapsed Process Variation Coefficients</h3>\n");
    672 	stream.print("<p> This table provides a bird's eye view of variability in elapsed process times\n");
    673 	stream.print("for baseline and current build stream performance scenarios.");
    674 	stream.print(" This summary is provided to facilitate the identification of scenarios that should be examined due to high variability.");
    675 	stream.print("The variability for each scenario is expressed as a <a href=\"http://en.wikipedia.org/wiki/Coefficient_of_variation\">coefficient\n");
    676 	stream.print("of variation</a> (CV). The CV is calculated by dividing the <b>standard deviation\n");
    677 	stream.print("of the elapse process time over builds</b> by the <b>average elapsed process\n");
    678 	stream.print("time over builds</b> and multiplying by 100.\n");
    679 	stream.print("</p><p>High CV values may be indicative of any of the following:<br></p>\n");
    680 	stream.print("<ol><li> an unstable performance test. </li>\n");
    681 	stream.print("<ul><li>may be evidenced by an erratic elapsed process line graph.<br><br></li></ul>\n");
    682 	stream.print("<li>performance regressions or improvements at some time in the course of builds.</li>\n");
    683 	stream.print("<ul><li>may be evidenced by plateaus in elapsed process line graphs.<br><br></li></ul>\n");
    684 	stream.print("<li>unstable testing hardware.\n");
    685 	stream.print("<ul><li>consistent higher CV values for one test configuration as compared to others across");
    686 	stream.print(" scenarios may be related to hardward problems.</li></ul></li></ol>\n");
    687 	stream.print("<p> Scenarios are listed in alphabetical order in the far right column. A scenario's\n");
    688 	stream.print("variation coefficients (CVs) are in columns to the left for baseline and current\n");
    689 	stream.print("build streams for each test configuration. Scenarios with CVs > 10% are highlighted\n");
    690 	stream.print("in yellow (10%<CV>&lt;CV<20%) and orange(CV>20%). </p>\n");
    691 	stream.print("<p> Each CV value links to the scenario's detailed results to allow viewers to\n");
    692 	stream.print("investigate the variability.</p>\n");
    693 }
    694 
    695 /*
    696  * Print columns titles of the summary table.
    697  */
    698 private void printSummaryColumnsTitle(PrintStream stream/*, PerformanceResults performanceResults*/) {
    699 	String[] configBoxes = this.performanceResults.getConfigBoxes(true/*sorted*/);
    700 	int length = configBoxes.length;
    701 	stream.print("<table border=\"1\"><tr><td colspan=\"");
    702 	stream.print(length);
    703 	stream.print("\"><b>Baseline CVs</b></td><td colspan=\"");
    704 	stream.print(length);
    705 	stream.print("\"><b>Current Build Stream CVs</b></td><td rowspan=\"2\"><b>Scenario Name</b></td></tr>\n");
    706 	stream.print("<tr>");
    707 	for (int n=0; n<2; n++) {
    708 		for (int c=0; c<length; c++) {
    709 			stream.print("<td>");
    710 			stream.print(configBoxes[c]);
    711 			stream.print("</td>");
    712 		}
    713 	}
    714 	stream.print("</tr>\n");
    715 }
    716 
    717 /*
    718  * Print a scenario line in the summary table.
    719  */
    720 private void printSummaryScenarioLine(int i, String config, ScenarioResults scenarioResults, PrintStream stream) {
    721 	ConfigResults configResults = scenarioResults.getConfigResults(config);
    722 	if (configResults == null || !configResults.isValid()) {
    723 		stream.print("<td>n/a</td>");
    724 		return;
    725 	}
    726 	String url = config + "/" + scenarioResults.getFileName()+".html";
    727 	double[] stats = null;
    728 	if (i==0) { // baseline results
    729 		List baselinePrefixes;
    730 		if (this.baselinePrefix == null) {
    731 			baselinePrefixes = Util.BASELINE_BUILD_PREFIXES;
    732 		} else {
    733 			baselinePrefixes = new ArrayList();
    734 			baselinePrefixes.add(this.baselinePrefix);
    735 		}
    736 		stats = configResults.getStatistics(baselinePrefixes);
    737 	} else {
    738 		stats = configResults.getStatistics(this.currentBuildPrefixes);
    739 	}
    740 	double variation = stats[3];
    741 	if (variation > 0.1 && variation < 0.2) {
    742 		stream.print("<td bgcolor=\"yellow\">");
    743 	} else if (variation >= 0.2) {
    744 		stream.print("<td bgcolor=\"FF9900\">");
    745 	} else {
    746 		stream.print("<td>");
    747 	}
    748 	stream.print("<a href=\"");
    749 	stream.print(url);
    750 	stream.print("\"/>");
    751 	stream.print(Util.PERCENTAGE_FORMAT.format(variation));
    752 	stream.print("</a></td>");
    753 }
    754 
    755 /*
    756  * Print usage in case one of the argument of the line was incorrect.
    757  * Note that calling this method ends the program run due to final System.exit()
    758  */
    759 private void printUsage() {
    760 	System.out.println(
    761 		"Usage:\n\n" +
    762 		"-baseline\n" +
    763 		"	Build id against which to compare results.\n" +
    764 		"	Same as value specified for the \"build\" key in the eclipse.perf.config system property.\n\n" +
    765 
    766 		"[-baseline.prefix]\n" +
    767 		"	Optional.  Build id prefix used in baseline test builds and reruns.  Used to plot baseline historical data.\n" +
    768 		"	A common prefix used for the value of the \"build\" key in the eclipse.perf.config system property when rerunning baseline tests.\n\n" +
    769 
    770 		"-current\n" +
    771 		"	build id for which to generate results.  Compared to build id specified in -baseline parameter above.\n" +
    772 		"	Same as value specified for the \"build\" key in the eclipse.perf.config system property. \n\n" +
    773 
    774 		"[-current.prefix]\n" +
    775 		"	Optional.  Comma separated list of build id prefixes used in current build stream.\n" +
    776 		"	Used to plot current build stream historical data.  Defaults to \"N,I\".\n" +
    777 		"	Prefixes for values specified for the \"build\" key in the eclipse.perf.config system property. \n\n" +
    778 
    779 		"-jvm\n" +
    780 		"	Value specified in \"jvm\" key in eclipse.perf.config system property for current build.\n\n" +
    781 
    782 		"-config\n" +
    783 		"	Comma separated list of config names for which to generate results.\n" +
    784 		"	Same as values specified in \"config\" key in eclipse.perf.config system property.\n\n" +
    785 
    786 		"-output\n" +
    787 		"	Path to default output directory.\n\n" +
    788 
    789 		"[-config.properties]\n" +
    790 		"	Optional.  Used by scenario status table to provide the following:\n" +
    791 		"		alternate descriptions of config values to use in columns.\n" +
    792 		"	The value should be specified in the following format:\n" +
    793 		"	name1,description1;name2,description2;etc..\n\n" +
    794 
    795 		"[-highlight]\n" +
    796 		"	Optional.  Comma-separated list of build Id prefixes used to find most recent matching for each entry.\n" +
    797 		"	Result used to highlight points in line graphs.\n\n" +
    798 
    799 		"[-scenario.pattern]\n" +
    800 		"	Optional.  Scenario prefix pattern to query database.  If not specified,\n" +
    801 		"	default of % used in query.\n\n" +
    802 
    803 		"[-fingerprints]\n" +
    804 		"	Optional.  Use to generate fingerprints only.\n\n" +
    805 
    806 		"[-data]\n" +
    807 		"	Optional.  Generates table of scenario reference and current data with line graphs.\n\n" +
    808 
    809 		"[-print]\n" +
    810 		"	Optional.  Display output in the console while generating.\n" +
    811 
    812 		"[-nophp]\n" +
    813 		"	Optional.  Generate files for non-php server.\n" +
    814 
    815 		"[-failure.threshold]\n" +
    816 		"	Optional.  Set the failure percentage threshold (default is 10%).\n"
    817 	);
    818 
    819 	System.exit(1);
    820 }
    821 
    822 /**
    823  * Run the generation from a list of arguments.
    824  * Typically used to generate results from an application.
    825  */
    826 public IStatus run(String[] args) {
    827 	parse(args);
    828 	return run((IProgressMonitor) null);
    829 }
    830 
    831 /**
    832  * Run the generation using a progress monitor.
    833  * Note that all necessary information to generate properly must be set before
    834  * calling this method
    835  *
    836  * @see #run(String[])
    837  */
    838 public IStatus run(final IProgressMonitor monitor) {
    839 	long begin = System.currentTimeMillis();
    840 	int work = 1100;
    841     int dataWork = 1000 * this.performanceResults.getConfigBoxes(false).length;
    842 	if (this.genAll || this.genData) {
    843 	    work += dataWork;
    844     }
    845 	SubMonitor subMonitor = SubMonitor.convert(monitor, work);
    846 	try {
    847 
    848 		// Print whole scenarios summary
    849 		if (this.printStream != null) this.printStream.println();
    850 		printSummary(/*performanceResults*/);
    851 
    852 		// Copy images and scripts to output dir
    853 		Bundle bundle = UiPlugin.getDefault().getBundle();
    854 //		URL images = bundle.getEntry("images");
    855 //		if (images != null) {
    856 //			images = FileLocator.resolve(images);
    857 //			Utils.copyImages(new File(images.getPath()), this.outputDir);
    858 //		}
    859 		/* New way to get images
    860 		File content = FileLocator.getBundleFile(bundle);
    861 		BundleFile bundleFile;
    862 		if (content.isDirectory()) {
    863 			bundleFile = new DirBundleFile(content);
    864 			Utils.copyImages(bundleFile.getFile("images", true), this.outputDir);
    865 		} else {
    866 			bundleFile = new ZipBundleFile(content, null);
    867 			Enumeration imageFiles = bundle.findEntries("images", "*.gif", false);
    868 			while (imageFiles.hasMoreElements()) {
    869 				URL url = (URL) imageFiles.nextElement();
    870 				Utils.copyFile(bundleFile.getFile("images"+File.separator+, true), this.outputDir);
    871 			}
    872 		}
    873 		*/
    874 		// Copy bundle files
    875 		Utils.copyBundleFiles(bundle, "images", "*.gif", this.outputDir); // images
    876 		Utils.copyBundleFiles(bundle, "scripts", "*.js", this.outputDir); // java scripts
    877 		Utils.copyBundleFiles(bundle, "scripts", "*.css", this.outputDir); // styles
    878 		Utils.copyBundleFiles(bundle, "doc", "*.html", this.outputDir); // doc
    879 		Utils.copyBundleFiles(bundle, "doc/images", "*.png", this.outputDir); // images for doc
    880 		/*
    881 		URL doc = bundle.getEntry("doc");
    882 		if (doc != null) {
    883 			doc = FileLocator.resolve(doc);
    884 			File docDir = new File(doc.getPath());
    885 			FileFilter filter = new FileFilter() {
    886 				public boolean accept(File pathname) {
    887 		            return !pathname.getName().equals("CVS");
    888 	            }
    889 			};
    890 			File[] docFiles = docDir.listFiles(filter);
    891 			for (int i=0; i<docFiles.length; i++) {
    892 				File file = docFiles[i];
    893 				if (file.isDirectory()) {
    894 					File subdir = new File(this.outputDir, file.getName());
    895 					subdir.mkdir();
    896 					File[] subdirFiles = file.listFiles(filter);
    897 					for (int j=0; j<subdirFiles.length; j++) {
    898 						if (subdirFiles[i].isDirectory()) {
    899 							// expect only one sub-directory
    900 						} else {
    901 							Util.copyFile(subdirFiles[j], new File(subdir, subdirFiles[j].getName()));
    902 						}
    903 					}
    904 				} else {
    905 					Util.copyFile(file, new File(this.outputDir, file.getName()));
    906 				}
    907 			}
    908 		}
    909 		*/
    910 
    911 		// Print HTML pages and all linked files
    912 		if (this.printStream != null) {
    913 			this.printStream.println("Print performance results HTML pages:");
    914 			this.printStream.print("	- components main page");
    915 		}
    916 		long start = System.currentTimeMillis();
    917 		subMonitor.setTaskName("Write fingerprints: 0%");
    918 		subMonitor.subTask("Global...");
    919 		printComponent(/*performanceResults, */"global_fp");
    920 		subMonitor.worked(100);
    921 		if (subMonitor.isCanceled()) throw new OperationCanceledException();
    922 		String[] components = this.performanceResults.getComponents();
    923 		int length = components.length;
    924 		int step = 1000 / length;
    925 		int progress = 0;
    926 		for (int i=0; i<length; i++) {
    927 			int percentage = (int) ((progress / ((double) length)) * 100);
    928 			subMonitor.setTaskName("Write fingerprints: "+percentage+"%");
    929 			subMonitor.subTask(components[i]+"...");
    930 			printComponent(/*performanceResults, */components[i]);
    931 			subMonitor.worked(step);
    932 			if (subMonitor.isCanceled()) throw new OperationCanceledException();
    933 			progress++;
    934 		}
    935 		if (this.printStream != null) {
    936 			String duration = Util.timeString(System.currentTimeMillis()-start);
    937 			this.printStream.println(" done in "+duration);
    938 		}
    939 
    940 		// Print the scenarios data
    941 		if (this.genData || this.genAll) {
    942 			start = System.currentTimeMillis();
    943 			if (this.printStream != null) this.printStream.println("	- all scenarios data:");
    944 			ScenarioData data = new ScenarioData(this.baselinePrefix, this.pointsOfInterest, this.currentBuildPrefixes, this.outputDir);
    945 			try {
    946 				data.print(this.performanceResults, this.printStream, subMonitor.newChild(dataWork));
    947 			} catch (Exception ex) {
    948 				ex.printStackTrace();
    949 			}
    950 			if (this.printStream != null) {
    951 				String duration = Util.timeString(System.currentTimeMillis()-start);
    952 				this.printStream.println("	=> done in "+duration);
    953 			}
    954 		}
    955 		if (this.printStream != null) {
    956 			long time = System.currentTimeMillis();
    957 			this.printStream.println("End of generation: "+new SimpleDateFormat("H:mm:ss").format(new Date(time)));
    958 			String duration = Util.timeString(System.currentTimeMillis()-begin);
    959 			this.printStream.println("=> done in "+duration);
    960 		}
    961 		return new Status(IStatus.OK, UiPlugin.getDefault().toString(), "Everything is OK");
    962 	}
    963 	catch (OperationCanceledException oce) {
    964 		return new Status(IStatus.OK, UiPlugin.getDefault().toString(), "Generation was cancelled!");
    965 	}
    966 	catch (Exception ex) {
    967 		return new Status(IStatus.ERROR, UiPlugin.getDefault().toString(), "An unexpected exception occurred!", ex);
    968 	}
    969 	finally {
    970 		if (this.printStream != null) {
    971 			this.printStream.flush();
    972 			if (this.printStream != System.out) {
    973 				this.printStream.close();
    974 			}
    975 		}
    976 	}
    977 }
    978 
    979 private void setDefaults(String buildName, String baseline) {
    980 	if (buildName == null) {
    981 		buildName = this.performanceResults.getName();
    982 	}
    983 
    984 	// Set default output dir if not set
    985 	if (this.outputDir.getPath().indexOf(buildName) == -1) {
    986 		File dir = new File(this.outputDir, buildName);
    987 		if (dir.exists() || dir.mkdir()) {
    988 			this.outputDir = dir;
    989 			if (this.printStream != null) {
    990 				this.printStream.println("	+ changed output dir to: "+dir.getPath());
    991 			}
    992 		}
    993 	}
    994 
    995 	// Verify that build is known
    996 	String[] builds = this.performanceResults.getAllBuildNames();
    997 	if (builds == null || builds.length == 0) {
    998 		System.err.println("Cannot connect to database to generate results build '"+buildName+"'");
    999 		System.exit(1);
   1000 	}
   1001 	if (Arrays.binarySearch(builds, buildName, Util.BUILD_DATE_COMPARATOR) < 0) {
   1002 		throw new RuntimeException("No results in database for build '"+buildName+"'");
   1003 	}
   1004 	if (this.printStream != null) {
   1005 		this.printStream.println();
   1006 		this.printStream.flush();
   1007 	}
   1008 
   1009 	// Init baseline prefix if not set
   1010 	if (this.baselinePrefix == null) {
   1011 		int index = baseline.lastIndexOf('_');
   1012 		if (index > 0) {
   1013 			this.baselinePrefix = baseline.substring(0, index);
   1014 		} else {
   1015 			this.baselinePrefix = DB_Results.getDbBaselinePrefix();
   1016 		}
   1017 	}
   1018 
   1019 	// Init current build prefixes if not set
   1020 	if (this.currentBuildPrefixes == null) {
   1021 		this.currentBuildPrefixes = new ArrayList();
   1022 		if (buildName.charAt(0) == 'M') {
   1023 			this.currentBuildPrefixes.add("M");
   1024 		} else {
   1025 			this.currentBuildPrefixes.add("N");
   1026 		}
   1027 		this.currentBuildPrefixes.add("I");
   1028 	}
   1029 }
   1030 
   1031 private void setPerformanceResults(String buildName, String baselineName) {
   1032 
   1033 	// Set performance results
   1034 	this.performanceResults = new PerformanceResults(buildName, baselineName, this.baselinePrefix, this.printStream);
   1035 
   1036 	// Set defaults
   1037 	setDefaults(buildName, this.performanceResults.getBaselineName());
   1038 
   1039 	// Read performance results data
   1040 	this.performanceResults.readAll(buildName, this.configDescriptors, this.scenarioPattern, this.dataDir, this.failure_threshold, null);
   1041 }
   1042 
   1043 /* (non-Javadoc)
   1044  * @see org.eclipse.equinox.app.IApplication#stop()
   1045  */
   1046 public void stop() {
   1047 	// Do nothing
   1048 }
   1049 
   1050 }