• 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.text.ParseException;
17 import java.util.StringTokenizer;
18 
19 import org.eclipse.test.internal.performance.InternalPerformanceMeter;
20 import org.eclipse.test.internal.performance.PerformanceTestPlugin;
21 import org.eclipse.test.internal.performance.data.Dim;
22 import org.eclipse.test.internal.performance.results.utils.Util;
23 
24 /**
25  * Class providing numbers of a scenario running on a specific configuration
26  * at a specific time (for example 'I20070615-1200').
27  */
28 public class BuildResults extends AbstractResults {
29 
30 	private static final double IMPOSSIBLE_VALUE = -1E6;
31 
32 	// Build information
33 	String date;
34 	String comment;
35 	int summaryKind = -1;
36 
37 	// Dimensions information
38 	Dim[] dimensions;
39 	double[] average, stddev;
40 	long[] count;
41 	double[][] values;
42 	boolean hadValues = false;
43 	private int defaultDimIndex = -1;
44 
45 	// Comparison information
46 	boolean baseline;
47 	String failure;
48 
BuildResults(AbstractResults parent)49 BuildResults(AbstractResults parent) {
50 	super(parent, -1);
51 }
52 
BuildResults(AbstractResults parent, int id)53 BuildResults(AbstractResults parent, int id) {
54 	super(parent, id);
55 	this.name = DB_Results.getBuildName(id);
56 	this.baseline = this.name.startsWith(DB_Results.getDbBaselinePrefix());
57 }
58 
59 /*
60  * Clean values when several measures has been done for the same build.
61  */
cleanValues()62 void cleanValues() {
63 	int length = this.values.length;
64 	for (int dim_id=0; dim_id<length; dim_id++) {
65 		int vLength = this.values[dim_id].length;
66 		/* Log clean operation
67 		if (dim_id == 0) {
68 			IStatus status = new Status(IStatus.WARNING, PerformanceTestPlugin.PLUGIN_ID, "Clean "+vLength+" values for "+this.parent+">"+this.name+" ("+this.count[dim_id]+" measures)...");    //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ //$NON-NLS-5$
69 			PerformanceTestPlugin.log(status);
70 		}
71 		*/
72 		this.average[dim_id] = 0;
73 		for (int i=0; i<vLength; i++) {
74 			this.average[dim_id] += this.values[dim_id][i];
75 		}
76 		this.average[dim_id] /= vLength;
77 		double squaredDeviations= 0;
78 		for (int i=0; i<vLength; i++) {
79 		    double deviation= this.average[dim_id] - this.values[dim_id][i];
80 		    squaredDeviations += deviation * deviation;
81 		}
82 		this.stddev[dim_id] = Math.sqrt(squaredDeviations / (this.count[dim_id] - 1)); // unbiased sample stdev
83 		this.values[dim_id] = null;
84 	}
85 	for (int i=0; i<length; i++) {
86 		if (this.values[i] != null) {
87 			return;
88 		}
89 	}
90 	this.values = null;
91 	this.hadValues = true;
92 }
93 
94 /**
95  * Compare build results using the date of the build.
96  *
97  * @see Comparable#compareTo(Object)
98  */
compareTo(Object obj)99 public int compareTo(Object obj) {
100 	if (obj instanceof BuildResults) {
101 		BuildResults res = (BuildResults) obj;
102 		return getDate().compareTo(res.getDate());
103 	}
104 	return -1;
105 }
106 
107 /**
108  * Returns the most recent baseline build results.
109  *
110  * @return The {@link BuildResults baseline build results}.
111  * @see BuildResults
112  */
getBaselineBuildResults()113 public BuildResults getBaselineBuildResults() {
114 	return ((ConfigResults)this.parent).getBaselineBuildResults();
115 }
116 
117 /**
118  * Returns the comment associated with the scenario for the current build.
119  *
120  * @return The comment associated with the scenario for the current build
121  * 	or <code>null</code> if no comment was stored for it.
122  */
getComment()123 public String getComment() {
124 	return this.comment;
125 }
126 
127 /**
128  * Return the number of stored values for the default dimension
129  *
130  * @return the number of stored values for the default dimension
131  */
getCount()132 public long getCount() {
133 	if (this.defaultDimIndex < 0) {
134 		this.defaultDimIndex = DB_Results.getDefaultDimensionIndex();
135 	}
136 	return this.count[this.defaultDimIndex];
137 }
138 
139 /**
140  * Return the number of stored values for the given dimension.
141  *
142  * @param dim_id The id of the dimension (see {@link Dim#getId()})
143  * @return the number of stored values for the given dimension
144  *
145  */
getCount(int dim_id)146 public long getCount(int dim_id) {
147 	return this.count[getDimIndex(dim_id)];
148 }
149 
150 /**
151  * Returns the date of the build which is a part of its name.
152  *
153  * @return The date of the build as yyyyMMddHHmm
154  */
getDate()155 public String getDate() {
156 	if (this.date == null) {
157 		if (this.baseline) {
158 			int length = this.name.length();
159 			this.date = this.name.substring(length-12, length);
160 		} else {
161 			char first = this.name.charAt(0);
162 			if (first == 'N' || first == 'I' || first == 'M') { // TODO (frederic) should be buildIdPrefixes...
163 				if (this.name.length() == 14) {
164 					this.date = this.name.substring(1, 9)+this.name.substring(10, 14);
165 				} else {
166 					this.date = this.name.substring(1);
167 				}
168 			} else {
169 				int length = this.name.length() - 12 /* length of date */;
170 				for (int i=0; i<=length; i++) {
171 					try {
172 						String substring = i == 0 ? this.name : this.name.substring(i);
173 						Util.DATE_FORMAT.parse(substring);
174 						this.date = substring; // if no exception is raised then the substring has a correct date format => store it
175 						break;
176 					} catch(ParseException ex) {
177 						// skip
178 					}
179 				}
180 			}
181 		}
182 	}
183 	return this.date;
184 }
185 
186 /**
187  * Returns the standard deviation of the default dimension computed
188  * while running the scenario for the current build.
189  *
190  * @return The value of the standard deviation
191  */
getDeviation()192 public double getDeviation() {
193 	if (this.defaultDimIndex < 0) {
194 		this.defaultDimIndex = DB_Results.getDefaultDimensionIndex();
195 	}
196 	return this.stddev[this.defaultDimIndex];
197 }
198 
199 /**
200  * Returns the standard deviation of the given dimension computed
201  * while running the scenario for the current build.
202  *
203  * @param dim_id The id of the dimension (see {@link Dim#getId()})
204  * @return The value of the standard deviation
205  */
getDeviation(int dim_id)206 public double getDeviation(int dim_id) {
207 	final int dimIndex = getDimIndex(dim_id);
208 	return dimIndex < 0 ? 0 : this.stddev[dimIndex];
209 }
210 
211 /**
212  * Returns the dimensions supported for the current build.
213  *
214  * @return An array of dimensions.
215  */
getDimensions()216 public Dim[] getDimensions() {
217 	return this.dimensions;
218 }
219 
220 /**
221  * Returns the kind of summary for the scenario of the current build.
222  *
223  * @return -1 if the scenario has no summary, 1 if it's a global summary, 0 otherwise.
224  */
getSummaryKind()225 public int getSummaryKind() {
226 	return this.summaryKind;
227 }
228 
229 /**
230  * Returns whether the current build had several values stored in database.
231  *
232  * @return <code>true</code> if the measure was committed several times,
233  * <code>false</code> otherwise.
234  */
hadValues()235 public boolean hadValues() {
236 	return this.hadValues;
237 }
238 
239 /*
240  * Return the index of the dimension corresponding to the given
241  * dimension id (see {@link Dim#getId()})
242  */
getDimIndex(int dim_id)243 int getDimIndex(int dim_id) {
244 	if (this.dimensions == null) return -1;
245 	int length = this.dimensions.length;
246 	for (int i=0; i<length; i++) {
247 		if (this.dimensions[i] == null) break;
248 		if (this.dimensions[i].getId() == dim_id) {
249 			return i;
250 		}
251 	}
252 	return -1;
253 }
254 
255 /**
256  * Return the error computed while storing values for the default dimension
257  *
258  * @return the error of the measures stored for the default dimension
259  */
getError()260 public double getError() {
261 	long n = getCount();
262 	if (n == 1) return Double.NaN;
263 	return getDeviation() / Math.sqrt(n);
264 }
265 
266 /**
267  * Return the error computed while storing values for the given dimension.
268  *
269  * @param dim_id The id of the dimension (see {@link Dim#getId()})
270  * @return the error of the measures stored for the given dimension
271  */
getError(int dim_id)272 public double getError(int dim_id) {
273 	long n = getCount(dim_id);
274 	if (n == 1) return Double.NaN;
275 	return getDeviation(dim_id) / Math.sqrt(n);
276 }
277 
278 /**
279  * Return the failure message which may happened on this scenario
280  * while running the current build
281  *
282  * @return The failure message or <code>null</null> if the scenario passed.
283  */
getFailure()284 public String getFailure() {
285 	return this.failure;
286 }
287 
288 /**
289  * Return the value of the performance result stored
290  * for the given dimension of the current build.
291  *
292  * @param dim_id The id of the dimension (see {@link Dim#getId()})
293  * @return The value of the performance result
294  */
getValue(int dim_id)295 public double getValue(int dim_id) {
296 	int idx = getDimIndex(dim_id);
297 	if (idx < 0) return Double.NaN;
298 	return this.average[idx];
299 }
300 
301 /**
302  * Return the value of the performance result stored
303  * for the default dimension of the current build.
304  *
305  * @return The value of the performance result
306  */
getValue()307 public double getValue() {
308 	if (this.defaultDimIndex < 0) {
309 		this.defaultDimIndex = DB_Results.getDefaultDimensionIndex();
310 	}
311 	return this.average[this.defaultDimIndex];
312 }
313 
314 /**
315  * Returns whether the build is a baseline build or not.
316  *
317  * @return <code>true</code> if the build name starts with the baseline prefix
318  * 	(see {@link PerformanceResults#getBaselinePrefix()} or <code>false</code>
319  * 	otherwise.
320  */
isBaseline()321 public boolean isBaseline() {
322 	return this.baseline;
323 }
324 
325 /**
326  * Returns whether the build has a summary or not. Note that the summary
327  * may be global or not.
328  *
329  * @return <code>true</code> if the summary kind is equals to 0 or 1
330  * 	<code>false</code> otherwise.
331  */
hasSummary()332 public boolean hasSummary() {
333 	return this.summaryKind >= 0;
334 }
335 /**
336  * Returns whether the build has a global summary or not.
337  *
338  * @return <code>true</code> if the summary kind is equals to 1
339  * 	<code>false</code> otherwise.
340  */
hasGlobalSummary()341 public boolean hasGlobalSummary() {
342 	return this.summaryKind == 1;
343 }
344 
345 /*
346  * Returns a given pattern match the build name or not.
347  */
match(String pattern)348 boolean match(String pattern) {
349 	if (pattern.equals("*")) return true; //$NON-NLS-1$
350 	if (pattern.indexOf('*') < 0 && pattern.indexOf('?') < 0) {
351 		pattern += "*"; //$NON-NLS-1$
352 	}
353 	StringTokenizer tokenizer = new StringTokenizer(pattern, "*?", true); //$NON-NLS-1$
354 	int start = 0;
355 	String previous = ""; //$NON-NLS-1$
356 	while (tokenizer.hasMoreTokens()) {
357 		String token = tokenizer.nextToken();
358 		if (!token.equals("*") && !token.equals("?")) { //$NON-NLS-1$ //$NON-NLS-2$
359 			if (previous.equals("*")) { //$NON-NLS-1$
360 				int idx = this.name.substring(start).indexOf(token);
361 				if (idx < 0) return false;
362 				start += idx;
363 			} else {
364 				if (previous.equals("?")) start++; //$NON-NLS-1$
365 				if (!this.name.substring(start).startsWith(token)) return false;
366 			}
367 			start += token.length();
368 		}
369 		previous = token;
370 	}
371 	if (previous.equals("*")) { //$NON-NLS-1$
372 		return true;
373 	} else if (previous.equals("?")) { //$NON-NLS-1$
374 		return this.name.length() == start;
375 	}
376 	return this.name.endsWith(previous);
377 }
378 
379 /*
380  * Read the build results data from the given stream.
381  */
readData(DataInputStream stream)382 void readData(DataInputStream stream) throws IOException {
383 	long timeBuild = stream.readLong();
384 	this.date = new Long(timeBuild).toString();
385 	byte kind = stream.readByte();
386 	this.baseline = kind == 0;
387 	if (this.baseline) {
388 		this.name = getPerformance().baselinePrefix + '_' + this.date;
389 	} else {
390 		String suffix = this.date.substring(0, 8) + '-' + this.date.substring(8);
391 		switch (kind) {
392 			case 1:
393 				this.name = "N" + suffix; //$NON-NLS-1$
394 				break;
395 			case 2:
396 				this.name = "I" + suffix; //$NON-NLS-1$
397 				break;
398 			case 3:
399 				this.name = "M" + suffix; //$NON-NLS-1$
400 				break;
401 			default:
402 				this.name = stream.readUTF();
403 				break;
404 		}
405 	}
406 	int length = stream.readInt();
407 	this.dimensions = new Dim[length];
408 	this.average = new double[length];
409 	this.stddev = new double[length];
410 	this.count = new long[length];
411 	for (int i=0; i<length; i++) {
412 		int dimId = stream.readInt();
413 		DB_Results.storeDimension(dimId);
414 		this.dimensions[i] = (Dim) PerformanceTestPlugin.getDimension(dimId);
415 		this.average[i] = stream.readLong();
416 		this.count[i] = stream.readLong();
417 		this.stddev[i] = stream.readDouble();
418 	}
419 	this.id = DB_Results.getBuildId(this.name);
420 
421 	// read summary
422 	this.summaryKind = stream.readInt();
423 
424 	// read comment
425 	String str = stream.readUTF();
426 	if (str.length() > 0) {
427 		this.comment = str;
428 	}
429 }
430 
431 /*
432  * Set the build summary and its associated comment.
433  */
setComment(String comment)434 void setComment(String comment) {
435 	if (comment != null && this.comment == null) {
436 		this.comment = comment;
437 	}
438 }
439 
440 /*
441  * Set the build summary and its associated comment.
442  */
setSummary(int kind, String comment)443 void setSummary(int kind, String comment) {
444 	this.comment = comment;
445 	this.summaryKind = kind;
446 }
447 
448 /*
449  * Set the build failure.
450  */
setFailure(String failure)451 void setFailure(String failure) {
452 	this.failure = failure;
453 }
454 
455 /*
456  * Set the build value from database information.
457  */
setValue(int dim_id, int step, long value)458 void setValue(int dim_id, int step, long value) {
459 	int length = DB_Results.getDimensions().length;
460 	Dim dimension = (Dim) PerformanceTestPlugin.getDimension(dim_id);
461 	int idx = 0;
462 	if (this.dimensions == null){
463 		this.dimensions = new Dim[length];
464 		this.average = new double[length];
465 		this.stddev = new double[length];
466 		this.count = new long[length];
467 		this.dimensions[0] = dimension;
468 		for (int i=0; i<length; i++) {
469 			// init average numbers with an impossible value
470 			// to clearly identify whether it's already set or not
471 			// when several measures are made for the same build
472 			this.average[i] = IMPOSSIBLE_VALUE;
473 		}
474 	} else {
475 		length = this.dimensions.length;
476 		for (int i=0; i<length; i++) {
477 			if (this.dimensions[i] == null) {
478 				this.dimensions[i] = dimension;
479 				idx = i;
480 				break;
481 			}
482 			if (this.dimensions[i].getId() == dim_id) {
483 				idx = i;
484 				break;
485 			}
486 		}
487 	}
488 	switch (step) {
489 		case InternalPerformanceMeter.AVERAGE:
490 			if (this.average[idx] != IMPOSSIBLE_VALUE) {
491 				if (this.values == null) {
492 					this.values = new double[length][];
493 					this.values[idx] = new double[2];
494 					this.values[idx][0] = this.average[idx];
495 					this.values[idx][1] = value;
496 					this.average[idx] = IMPOSSIBLE_VALUE;
497 				} else if (this.values[idx] == null) {
498 					this.values[idx] = new double[2];
499 					this.values[idx][0] = this.average[idx];
500 					this.values[idx][1] = value;
501 					this.average[idx] = IMPOSSIBLE_VALUE;
502 				}
503 			} else if (this.values != null && this.values[idx] != null) {
504 				int vLength = this.values[idx].length;
505 				System.arraycopy(this.values[idx], 0, this.values[idx] = new double[vLength+1], 0, vLength);
506 				this.values[idx][vLength] = value;
507 			} else {
508 				this.average[idx] = value;
509 			}
510 			break;
511 		case InternalPerformanceMeter.STDEV:
512 			this.stddev[idx] += Double.longBitsToDouble(value);
513 			break;
514 		case InternalPerformanceMeter.SIZE:
515 			this.count[idx] += value;
516 			break;
517 	}
518 }
519 
520 /* (non-Javadoc)
521  * @see org.eclipse.test.internal.performance.results.AbstractResults#toString()
522  */
toString()523 public String toString() {
524 	StringBuffer buffer = new StringBuffer(this.name);
525 	buffer.append(": "); //$NON-NLS-1$
526 	int length = this.dimensions.length;
527 	for (int i=0; i<length; i++) {
528 		if (i>0)	buffer.append(", "); //$NON-NLS-1$
529 		buffer.append('[');
530 		buffer.append(this.dimensions[i].getId());
531 		buffer.append("]="); //$NON-NLS-1$
532 		buffer.append(this.average[i]);
533 		buffer.append('/');
534 		buffer.append(this.count[i]);
535 		buffer.append('/');
536 		buffer.append(Math.round(this.stddev[i]*1000)/1000.0);
537 	}
538 	return buffer.toString();
539 }
540 
541 /*
542  * Write the build results data in the given stream.
543  */
write(DataOutputStream stream)544 void write(DataOutputStream stream) throws IOException {
545 	long timeBuild = -1;
546     try {
547 	    timeBuild = Long.parseLong(getDate());
548     } catch (NumberFormatException nfe) {
549 	    // do nothing
550     	nfe.printStackTrace();
551     }
552 	stream.writeLong(timeBuild);
553 	byte kind = 0; // baseline
554 	if (!this.baseline) {
555 		switch (this.name.charAt(0)) {
556 			case 'N':
557 				kind = 1;
558 				break;
559 			case 'I':
560 				kind = 2;
561 				break;
562 			case 'M':
563 				kind = 3;
564 				break;
565 			default:
566 				kind = 4;
567 				break;
568 		}
569 	}
570 	stream.writeByte(kind);
571 	if (kind == 4) {
572 		stream.writeUTF(this.name);
573 	}
574 	int length = this.dimensions == null ? 0 : this.dimensions.length;
575 	stream.writeInt(length);
576 	for (int i=0; i<length; i++) {
577 		stream.writeInt(this.dimensions[i].getId());
578 		stream.writeLong((long)this.average[i]) ;
579 		stream.writeLong(this.count[i]);
580 		stream.writeDouble(this.stddev[i]);
581 	}
582 
583 	// Write extra infos (summary, failure and comment)
584 	stream.writeInt(this.summaryKind);
585 	stream.writeUTF(this.comment == null ? "" : this.comment) ; //$NON-NLS-1$
586 }
587 
588 }
589