• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.internal.performance.results.db;
12 
13 import java.io.DataInputStream;
14 import java.io.DataOutputStream;
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.List;
18 import org.eclipse.test.internal.performance.InternalDimensions;
19 import org.eclipse.test.internal.performance.results.utils.Util;
20 
21 /**
22  * Class to handle results for an Eclipse performance test box
23  * (called a <i>configuration</i>).
24  *
25  * It gives access to results for each build on which this configuration has been run.
26  *
27  * @see BuildResults
28  */
29 public class ConfigResults extends AbstractResults {
30 	BuildResults baseline, current;
31 	boolean baselined = false, valid = false;
32 	double delta, error;
33 
ConfigResults(AbstractResults parent, int id)34 public ConfigResults(AbstractResults parent, int id) {
35 	super(parent, id);
36 	this.name = parent.getPerformance().sortedConfigNames[id];
37 	this.printStream = parent.printStream;
38 }
39 
40 /*
41  * Complete results with additional database information.
42  */
completeResults(String[] builds)43 void completeResults(String[] builds) {
44 	/*if (this.baseline == null || this.current == null) */initialize();
45 	ScenarioResults scenarioResults = (ScenarioResults) this.parent;
46 	DB_Results.queryScenarioSummaries(scenarioResults, this.name, builds);
47 }
48 
49 /**
50  * Returns the baseline build name used to compare results with.
51  *
52  * @return The name of the baseline build
53  * @see #getBaselineBuildResults()
54  */
getBaselineBuildName()55 public String getBaselineBuildName() {
56 	if (this.baseline == null) {
57 	    initialize();
58     }
59 	return this.baseline.getName();
60 }
61 
62 /**
63  * Returns the most recent baseline build results.
64  *
65  * @return The {@link BuildResults baseline build results}.
66  * @see BuildResults
67  */
getBaselineBuildResults()68 public BuildResults getBaselineBuildResults() {
69 	if (this.baseline == null) {
70 	    initialize();
71     }
72 	return this.baseline;
73 }
74 
75 /**
76  * Return the baseline build results run just before the given build name.
77  *
78  * @param buildName The build name
79  * @return The {@link BuildResults baseline results} preceding the given build name
80  * 	or <code>null</code> if none was found.
81  */
getBaselineBuildResults(String buildName)82 public BuildResults getBaselineBuildResults(String buildName) {
83 	if (this.baseline == null) {
84 	    initialize();
85     }
86 	int size = this.children.size();
87 	String buildDate = Util.getBuildDate(buildName);
88 	for (int i=size-1; i>=0; i--) {
89 		BuildResults buildResults = (BuildResults) this.children.get(i);
90 		if (buildResults.isBaseline() && buildResults.getDate().compareTo(buildDate) < 0) {
91 			return buildResults;
92 		}
93 	}
94 	return this.baseline;
95 
96 }
97 
98 /**
99  * Returns the most recent baseline build result value.
100  *
101  * @return The value of the most recent baseline build results.
102  */
getBaselineBuildValue()103 public double getBaselineBuildValue() {
104 	if (this.baseline == null) {
105 	    initialize();
106     }
107 	return this.baseline.getValue();
108 }
109 
110 /**
111  * Returns the configuration description (currently the box name).
112  *
113  * @return The configuration description (currently the box name).
114  */
getDescription()115 public String getDescription() {
116 	return getPerformance().sortedConfigDescriptions[this.id];
117 }
118 
119 /**
120  * Return the results for the given build name.
121  *
122  * @param buildName The build name
123  * @return The {@link BuildResults results} for the given build name
124  * 	or <code>null</code> if none was found.
125  */
getBuildResults(String buildName)126 public BuildResults getBuildResults(String buildName) {
127 	return (BuildResults) getResults(buildName);
128 }
129 
130 /**
131  * Returns the build results matching a given pattern.
132  *
133  * @param buildPattern The pattern of searched builds
134  * @return The list of the builds which names match the given pattern.
135  * 	The list is ordered by build results date.
136  */
getBuilds(String buildPattern)137 public List getBuilds(String buildPattern) {
138 	List builds = new ArrayList();
139 	int size = size();
140 	for (int i=0; i<size; i++) {
141 		BuildResults buildResults = (BuildResults) this.children.get(i);
142 		if (buildPattern == null || buildResults.match(buildPattern)) {
143 			builds.add(buildResults);
144 		}
145 	}
146 	return builds;
147 }
148 
149 /**
150  * Returns the build results before a given build name.
151  *
152  * @param buildName Name of the last build (included)
153  * @return The list of the builds which precedes the given build name.
154  */
getBuildsBefore(String buildName)155 public List getBuildsBefore(String buildName) {
156 	String buildDate = Util.getBuildDate(buildName);
157 	List builds = new ArrayList();
158 	int size = size();
159 	for (int i=0; i<size; i++) {
160 		BuildResults buildResults = (BuildResults) this.children.get(i);
161 		if (buildName == null || buildResults.getDate().compareTo(buildDate) <= 0) {
162 			builds.add(buildResults);
163 		}
164 	}
165 	return builds;
166 }
167 
168 /**
169  * Returns a list of build results which names starts with one of the given prefixes.
170  *
171  * @param prefixes List of expected prefixes
172  * @return A list of builds which names start with one of the given patterns.
173  */
getBuildsMatchingPrefixes(List prefixes)174 public List getBuildsMatchingPrefixes(List prefixes) {
175 	List builds = new ArrayList();
176 	int size = size();
177 	int length = prefixes.size();
178 	for (int i=0; i<size; i++) {
179 		AbstractResults buildResults = (AbstractResults) this.children.get(i);
180 		String buildName = buildResults.getName();
181 		for (int j=0; j<length; j++) {
182 			if (buildName.startsWith((String)prefixes.get(j))) {
183 				builds.add(buildResults);
184 			}
185 		}
186 	}
187 	return builds;
188 }
189 
190 /**
191  * Get all results numbers for the max last builds.
192  *
193  * @param max The number of last builds to get numbers.
194  * @return An 2 dimensions array of doubles. At the first level of the array each slot
195  * 		represents one build. That means that the dimension of the array matches
196  * 		the given numbers as soon as there are enough builds in the database.
197  * <p>
198  * 		The slots of the second level are the numbers values:
199  * 	<ul>
200  * 		<li>{@link #BUILD_VALUE_INDEX}: the build value in milliseconds</li>
201  * 		<li>{@link #BASELINE_VALUE_INDEX}: the baseline value in milliseconds</li>
202  * 		<li>{@link #DELTA_VALUE_INDEX}: the difference between the build value and its more recent baseline</li>
203  * 		<li>{@link #DELTA_ERROR_INDEX}: the error made while computing the difference</li>
204  * 		<li>{@link #BUILD_ERROR_INDEX}: the error made while measuring the build value</li>
205  * 		<li>{@link #BASELINE_ERROR_INDEX}: the error made while measuring the baseline value</li>
206  * 	</ul>
207 */
getLastNumbers(int max)208 public double[][] getLastNumbers(int max) {
209 
210 	// Return null if no previous builds are expected
211 	if (max <= 0) return null;
212 
213 	// Add numbers for each previous build
214 	int size = size();
215 	double[][] numbers = new double[Math.min(max, size)][];
216 	int n = 0;
217 	for (int i=size-1; i>=0 && n<max; i--) {
218 		BuildResults buildResults = (BuildResults) this.children.get(i);
219 		if (!buildResults.isBaseline()) {
220 			numbers[n] = getNumbers(buildResults, getBaselineBuildResults(buildResults.getName()));
221 			n++;
222 		}
223 	}
224 
225 	// Return the numbers
226 	return numbers;
227 }
228 
229 /**
230  * Returns interesting numbers for the current configuration.
231  *
232  * @return Values in an array of double:
233  * 	<ul>
234  * 		<li>{@link AbstractResults#BUILD_VALUE_INDEX}: the build value in milliseconds</li>
235  * 		<li>{@link AbstractResults#BASELINE_VALUE_INDEX}: the baseline value in milliseconds</li>
236  * 		<li>{@link AbstractResults#DELTA_VALUE_INDEX}: the difference between the build value and its more recent baseline</li>
237  * 		<li>{@link AbstractResults#DELTA_ERROR_INDEX}: the error made while computing the difference</li>
238  * 		<li>{@link AbstractResults#BUILD_ERROR_INDEX}: the error made while measuring the build value</li>
239  * 		<li>{@link AbstractResults#BASELINE_ERROR_INDEX}: the error made while measuring the baseline value</li>
240  * 	</ul>
241  */
getNumbers(BuildResults buildResults, BuildResults baselineResults)242 double[] getNumbers(BuildResults buildResults, BuildResults baselineResults) {
243 	if (baselineResults == null) {
244 		return null;
245 	}
246 	double[] values = new double[NUMBERS_LENGTH];
247 	for (int i=0 ;i<NUMBERS_LENGTH; i++) {
248 		values[i] = Double.NaN;
249 	}
250 	double buildValue = buildResults.getValue();
251 	values[BUILD_VALUE_INDEX] = buildValue;
252 	double baselineValue = baselineResults.getValue();
253 	values[BASELINE_VALUE_INDEX] = baselineValue;
254 	double buildDelta = (baselineValue - buildValue) / baselineValue;
255 	values[DELTA_VALUE_INDEX] = buildDelta;
256 	if (Double.isNaN(buildDelta)) {
257 		return values;
258 	}
259 	long baselineCount = baselineResults.getCount();
260 	long currentCount = buildResults.getCount();
261 	if (baselineCount > 1 && currentCount > 1) {
262 		double baselineError = baselineResults.getError();
263 		double currentError = buildResults.getError();
264 		values[BASELINE_ERROR_INDEX] = baselineError;
265 		values[BUILD_ERROR_INDEX] = currentError;
266 		values[DELTA_ERROR_INDEX] = Double.isNaN(baselineError)
267 				? currentError / baselineValue
268 				: Math.sqrt(baselineError*baselineError + currentError*currentError) / baselineValue;
269 	}
270 	return values;
271 }
272 
273 /**
274  * Return the deviation value and its associated standard error for the default dimension
275  * (currently {@link InternalDimensions#ELAPSED_PROCESS}).
276  *
277  * @return an array of double. First number is the deviation itself and
278  * 	the second is the standard error.
279  */
getCurrentBuildDeltaInfo()280 public double[] getCurrentBuildDeltaInfo() {
281 	if (this.baseline == null || this.current == null) {
282 		initialize();
283 	}
284 	return new double[] { this.delta, this.error };
285 }
286 
287 /**
288  * Returns the error of the current build results
289  *
290  * @return the error made during the current build measure
291  */
getCurrentBuildError()292 public double getCurrentBuildError() {
293 	if (this.current == null) {
294 	    initialize();
295     }
296 	return this.current.getError();
297 }
298 
299 /**
300  * Returns the current build name.
301  *
302  * @return The name of the current build
303  * @see #getCurrentBuildResults()
304  */
getCurrentBuildName()305 public String getCurrentBuildName() {
306 	if (this.current == null) {
307 	    initialize();
308     }
309 	return this.current.getName();
310 }
311 
312 /**
313  * Returns the current build results.
314  * <p>
315  * This build is currently the last integration or nightly
316  * build which has performance results in the database.
317  * It may differ from the {@link PerformanceResults#getName()}.
318  *
319  * @return The current build results.
320  * @see BuildResults
321  */
getCurrentBuildResults()322 public BuildResults getCurrentBuildResults() {
323 	if (this.current == null) {
324 	    initialize();
325     }
326 	return this.current;
327 }
328 
329 /**
330  * Returns the current build result value.
331  *
332  * @return The value of the current build results.
333  */
getCurrentBuildValue()334 public double getCurrentBuildValue() {
335 	if (this.current == null) {
336 	    initialize();
337     }
338 	return this.current.getValue();
339 }
340 
341 /**
342  * Returns the delta between current and baseline builds results.
343  *
344  * @return the delta
345  */
getDelta()346 public double getDelta() {
347 	if (this.baseline == null || this.current == null) {
348 		initialize();
349 	}
350 	return this.delta;
351 }
352 
353 /**
354  * Returns the standard error of the delta between current and baseline builds results.
355  *
356  * @return the delta
357  * @see #getDelta()
358  */
getError()359 public double getError() {
360 	if (this.baseline == null || this.current == null) {
361 		initialize();
362 	}
363 	return this.error;
364 }
365 
366 /**
367  * Return the name of the machine associated with the current config.
368  *
369  * @return The name of the machine.
370  */
getLabel()371 public String getLabel() {
372 	return this.parent.getPerformance().sortedConfigDescriptions[this.id];
373 }
374 
375 /**
376  * Get all dimension builds default dimension statistics for all builds.
377  *
378  * @return An array of double built as follows:
379  * <ul>
380  * <li>0:	numbers of values</li>
381  * <li>1:	mean of values</li>
382  * <li>2:	standard deviation of these values</li>
383  * <li>3:	coefficient of variation of these values</li>
384  * </ul>
385  */
getStatistics()386 public double[] getStatistics() {
387 	return getStatistics(Util.ALL_BUILD_PREFIXES, DB_Results.getDefaultDimension().getId());
388 }
389 
390 /**
391  * Get all dimension builds default dimension statistics for a given list of build
392  * prefixes.
393  *
394  * @param prefixes List of prefixes to filter builds. If <code>null</code>
395  * 	then all the builds are taken to compute statistics.
396  * @return An array of double built as follows:
397  * <ul>
398  * <li>0:	numbers of values</li>
399  * <li>1:	mean of values</li>
400  * <li>2:	standard deviation of these values</li>
401  * <li>3:	coefficient of variation of these values</li>
402  * </ul>
403  */
getStatistics(List prefixes)404 public double[] getStatistics(List prefixes) {
405 	return getStatistics(prefixes, DB_Results.getDefaultDimension().getId());
406 }
407 
408 /**
409  * Get all dimension builds statistics for a given list of build prefixes
410  * and a given dimension.
411  *
412  * @param prefixes List of prefixes to filter builds. If <code>null</code>
413  * 	then all the builds are taken to compute statistics.
414  * @param dim_id The id of the dimension on which the statistics must be computed
415  * @return An array of double built as follows:
416  * <ul>
417  * <li>0:	numbers of values</li>
418  * <li>1:	mean of values</li>
419  * <li>2:	standard deviation of these values</li>
420  * <li>3:	coefficient of variation of these values</li>
421  * </ul>
422  */
getStatistics(List prefixes, int dim_id)423 public double[] getStatistics(List prefixes, int dim_id) {
424 	int size = size();
425 	int length = prefixes == null ? 0 : prefixes.size();
426 	int count = 0;
427 	double mean=0, stddev=0, variation=0;
428 	double[] values = new double[size];
429 	count = 0;
430 	mean = 0.0;
431 	for (int i=0; i<size; i++) {
432 		BuildResults buildResults = (BuildResults) this.children.get(i);
433 		String buildName = buildResults.getName();
434 		if (isBuildConcerned(buildResults)) {
435 			if (prefixes == null) {
436 				double value = buildResults.getValue(dim_id);
437 				values[count] = value;
438 				mean += value;
439 				count++;
440 			} else {
441 				for (int j=0; j<length; j++) {
442 					if (buildName.startsWith((String)prefixes.get(j))) {
443 						double value = buildResults.getValue(dim_id);
444 						values[count] = value;
445 						mean += value;
446 						count++;
447 					}
448 				}
449 			}
450 		}
451 	}
452 	mean /= count;
453 	for (int i=0; i<count; i++) {
454 		stddev += Math.pow(values[i] - mean, 2);
455 	}
456 	stddev = Math.sqrt((stddev / (count - 1)));
457 	variation = stddev / mean;
458 	return new double[] { count, mean, stddev, variation };
459 }
460 
initialize()461 private void initialize() {
462 	reset();
463 	// Get performance results builds name
464 	PerformanceResults perfResults = getPerformance();
465 	String baselineBuildName = perfResults.getBaselineName();
466 	String baselineBuildDate = baselineBuildName == null ? null : Util.getBuildDate(baselineBuildName);
467 	String currentBuildName = perfResults.name;
468 	String currentBuildDate = currentBuildName == null ? null : Util.getBuildDate(currentBuildName);
469 
470 	// Set baseline and current builds
471 	BuildResults lastBaseline = null;
472 	int size = size();
473 	if (size == 0) return;
474 	for (int i=0; i<size; i++) {
475 		BuildResults buildResults = (BuildResults) this.children.get(i);
476 		if (buildResults.values != null) {
477 			buildResults.cleanValues();
478 		}
479 		if (buildResults.isBaseline()) {
480 			if (lastBaseline == null || baselineBuildDate == null || baselineBuildDate.compareTo(buildResults.getDate()) >= 0) {
481 				lastBaseline = buildResults;
482 			}
483 			if (baselineBuildName != null && buildResults.getName().equals(baselineBuildName)) {
484 				this.baseline = buildResults;
485 				this.baselined = true;
486 			}
487 		} else if (currentBuildName == null || currentBuildDate == null || (this.current == null && buildResults.getDate().compareTo(currentBuildDate) >= 0)) {
488 			this.current = buildResults;
489 			this.valid = true;
490 		}
491 	}
492 	if (this.baseline == null) {
493 		this.baseline = (lastBaseline == null) ? (BuildResults) this.children.get(0) : lastBaseline;
494 	}
495 	if (this.current == null) {
496 		int idx = size() - 1;
497 		BuildResults previous = (BuildResults) this.children.get(idx--);
498 		while (idx >= 0 && previous.isBaseline()) {
499 			previous = (BuildResults) this.children.get(idx--);
500 		}
501 		this.current = previous;
502 	}
503 
504 	// Set delta between current vs. baseline and the corresponding error
505 	int dim_id = DB_Results.getDefaultDimension().getId();
506 	double baselineValue = this.baseline.getValue();
507 	double currentValue = this.current.getValue();
508 	this.delta = (currentValue - baselineValue) / baselineValue;
509 	if (Double.isNaN(this.delta)) {
510 		this.error = Double.NaN;
511 	} else {
512 		long baselineCount = this.baseline.getCount(dim_id);
513 		long currentCount = this.current.getCount(dim_id);
514 		if (baselineCount == 1 || currentCount == 1) {
515 			this.error = Double.NaN;
516 		} else {
517 			double baselineError = this.baseline.getError(dim_id);
518 			double currentError = this.current.getError(dim_id);
519 			this.error = Double.isNaN(baselineError)
520 					? currentError / baselineValue
521 					: Math.sqrt(baselineError*baselineError + currentError*currentError) / baselineValue;
522 		}
523 	}
524 
525 	// Set the failure on the current build if necessary
526 	int failure_threshold = getPerformance().failure_threshold;
527 	if (this.delta >= (failure_threshold/100.0)) {
528 		StringBuffer buffer = new StringBuffer("Performance criteria not met when compared to '"); //$NON-NLS-1$
529 		buffer.append(this.baseline.getName());
530 		buffer.append("': "); //$NON-NLS-1$
531 		buffer.append(DB_Results.getDefaultDimension().getName());
532 		buffer.append("= "); //$NON-NLS-1$
533 		buffer.append(Util.timeString((long)this.current.getValue()));
534 		buffer.append(" is not within [0%, "); //$NON-NLS-1$
535 		buffer.append(100+failure_threshold);
536 		buffer.append("'%] of "); //$NON-NLS-1$
537 		buffer.append(Util.timeString((long)this.baseline.getValue()));
538 		this.current.setFailure(buffer.toString());
539 	}
540 }
541 
542 /**
543  * Returns whether the configuration has results for the performance
544  * baseline build or not.
545  *
546  * @return <code>true</code> if the configuration has results
547  * 	for the performance baseline build, <code>false</code> otherwise.
548  */
isBaselined()549 public boolean isBaselined() {
550 	if (this.baseline == null || this.current == null) {
551 	    initialize();
552     }
553 	return this.baselined;
554 }
555 
isBuildConcerned(BuildResults buildResults)556 boolean isBuildConcerned(BuildResults buildResults) {
557 	String buildDate = buildResults.getDate();
558 	String currentBuildDate = getCurrentBuildResults() == null ? null : getCurrentBuildResults().getDate();
559 	String baselineBuildDate = getBaselineBuildResults() == null ? null : getBaselineBuildResults().getDate();
560 	return ((currentBuildDate == null || buildDate.compareTo(currentBuildDate) <= 0) &&
561 		(baselineBuildDate == null || buildDate.compareTo(baselineBuildDate) <= 0));
562 }
563 /**
564  * Returns whether the configuration has results for the performance
565  * current build or not.
566  *
567  * @return <code>true</code> if the configuration has results
568  * 	for the performance current build, <code>false</code> otherwise.
569  */
isValid()570 public boolean isValid() {
571 	if (this.baseline == null || this.current == null) {
572 	    initialize();
573     }
574 	return this.valid;
575 }
576 
577 /**
578  * Returns the 'n' last nightly build names.
579  *
580  * @param n Number of last nightly builds to return
581  * @return Last n nightly build names preceding current.
582  */
lastNightlyBuildNames(int n)583 public List lastNightlyBuildNames(int n) {
584 	List labels = new ArrayList();
585 	for (int i=size()-2; i>=0; i--) {
586 		BuildResults buildResults = (BuildResults) this.children.get(i);
587 		if (isBuildConcerned(buildResults)) {
588 			String buildName = buildResults.getName();
589 			if (buildName.startsWith("N")) { //$NON-NLS-1$
590 				labels.add(buildName);
591 				if (labels.size() >= n) {
592 	                break;
593                 }
594 			}
595 		}
596 	}
597 	return labels;
598 }
599 
600 /*
601  * Read all configuration builds results data from the given stream.
602  */
readData(DataInputStream stream)603 void readData(DataInputStream stream) throws IOException {
604 	int size = stream.readInt();
605 	for (int i=0; i<size; i++) {
606 		BuildResults buildResults = new BuildResults(this);
607 		buildResults.readData(stream);
608 		String lastBuildName = getPerformance().lastBuildName;
609 		if (lastBuildName == null || buildResults.getDate().compareTo(Util.getBuildDate(lastBuildName)) <= 0) {
610 			addChild(buildResults, true);
611 		}
612 	}
613 }
614 
reset()615 private void reset() {
616 	this.current = null;
617 	this.baseline = null;
618 	this.baselined = false;
619 	this.valid = false;
620 	this.delta = 0;
621 	this.error = -1;
622 }
623 
624 /*
625  * Set the configuration value from database information
626  */
setInfos(int build_id, int summaryKind, String comment)627 void setInfos(int build_id, int summaryKind, String comment) {
628 	BuildResults buildResults = (BuildResults) getResults(build_id);
629 	if (buildResults == null) {
630 		buildResults = new BuildResults(this, build_id);
631 		addChild(buildResults, true);
632 	}
633 	buildResults.summaryKind = summaryKind;
634 	buildResults.comment = comment;
635 }
636 
637 /*
638  * Set the configuration value from database information
639  */
setValue(int build_id, int dim_id, int step, long value)640 void setValue(int build_id, int dim_id, int step, long value) {
641 //	reset();
642 	BuildResults buildResults = (BuildResults) getResults(build_id);
643 	if (buildResults == null) {
644 		buildResults = new BuildResults(this, build_id);
645 		addChild(buildResults, true);
646 	}
647 	buildResults.setValue(dim_id, step, value);
648 }
649 
650 /*
651  * Write all configuration builds results data into the given stream.
652  */
write(DataOutputStream stream)653 void write(DataOutputStream stream) throws IOException {
654 	int size = size();
655 	stream.writeInt(this.id);
656 	stream.writeInt(size);
657 	for (int i=0; i<size; i++) {
658 		BuildResults buildResults = (BuildResults) this.children.get(i);
659 		buildResults.write(stream);
660 	}
661 }
662 
663 }
664