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
GenerateResults()186 public GenerateResults() {
187 }
188
GenerateResults(PerformanceResults results, String current, String baseline, boolean fingerprints, File data, File output)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 */
parse(String[] args)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 */
printComponent( String component)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
printComponentTitle( String component, boolean isGlobal, PrintStream stream)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 */
printSummary( )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 */
printSummaryPresentation(PrintStream stream)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><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 */
printSummaryColumnsTitle(PrintStream stream )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 */
printSummaryScenarioLine(int i, String config, ScenarioResults scenarioResults, PrintStream stream)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 */
printUsage()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 */
run(String[] args)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 */
run(final IProgressMonitor monitor)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
setDefaults(String buildName, String baseline)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
setPerformanceResults(String buildName, String baselineName)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 */
stop()1046 public void stop() {
1047 // Do nothing
1048 }
1049
1050 }