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