Home | History | Annotate | Download | only in db
      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.BufferedInputStream;
     14 import java.io.BufferedOutputStream;
     15 import java.io.DataInputStream;
     16 import java.io.DataOutputStream;
     17 import java.io.File;
     18 import java.io.FileInputStream;
     19 import java.io.FileNotFoundException;
     20 import java.io.FileOutputStream;
     21 import java.io.IOException;
     22 import java.util.ArrayList;
     23 import java.util.Arrays;
     24 import java.util.Comparator;
     25 import java.util.HashSet;
     26 import java.util.List;
     27 import java.util.Set;
     28 
     29 import org.eclipse.core.runtime.SubMonitor;
     30 import org.eclipse.test.internal.performance.results.utils.Util;
     31 
     32 /**
     33  * Class to handle performance results of an eclipse component
     34  * (for example 'org.eclipse.jdt.core').
     35  *
     36  * It gives access to results for each scenario run for this component.
     37  *
     38  * @see ScenarioResults
     39  */
     40 public class ComponentResults extends AbstractResults {
     41 
     42 public ComponentResults(AbstractResults parent, String name) {
     43 	super(parent, name);
     44 	this.printStream = parent.printStream;
     45 }
     46 
     47 Set getAllBuildNames() {
     48 	Set buildNames = new HashSet();
     49 	int size = size();
     50 	for (int i=0; i<size; i++) {
     51 		ScenarioResults scenarioResults = (ScenarioResults) this.children.get(i);
     52 		Set builds = scenarioResults.getAllBuildNames();
     53 		buildNames.addAll(builds);
     54 	}
     55 	return buildNames;
     56 }
     57 
     58 /**
     59  * Return all the build names for this component sorted by ascending order.
     60  *
     61  * @return An array of names
     62  */
     63 public String[] getAllSortedBuildNames() {
     64 	return getAllSortedBuildNames(false/*ascending order*/);
     65 }
     66 
     67 String[] getAllSortedBuildNames(final boolean reverse) {
     68 	Set allBuildNames = getAllBuildNames();
     69 	String[] sortedNames = new String[allBuildNames.size()];
     70 	allBuildNames.toArray(sortedNames);
     71 	Arrays.sort(sortedNames, new Comparator() {
     72 		public int compare(Object o1, Object o2) {
     73 			String s1 = (String) (reverse ? o2 : o1);
     74 			String s2 = (String) (reverse ? o1 : o2);
     75 			return Util.getBuildDate(s1).compareTo(Util.getBuildDate(s2));
     76 	    }
     77 	});
     78 	return sortedNames;
     79 }
     80 
     81 ComponentResults getComponentResults() {
     82 	return this;
     83 }
     84 
     85 /**
     86  * Get all results numbers for a given machine of the current component.
     87  *
     88  * @param configName The name of the configuration to get numbers
     89  * @param fingerprints Set whether only fingerprints scenario should be taken into account
     90  * @return A list of lines. Each line represent a build and is a list of either strings or values.
     91   * 	Values are an array of double:
     92  * 	<ul>
     93  * 		<li>{@link #BUILD_VALUE_INDEX}: the build value in milliseconds</li>
     94  * 		<li>{@link #BASELINE_VALUE_INDEX}: the baseline value in milliseconds</li>
     95  * 		<li>{@link #DELTA_VALUE_INDEX}: the difference between the build value and its more recent baseline</li>
     96  * 		<li>{@link #DELTA_ERROR_INDEX}: the error made while computing the difference</li>
     97  * 		<li>{@link #BUILD_ERROR_INDEX}: the error made while measuring the build value</li>
     98  * 		<li>{@link #BASELINE_ERROR_INDEX}: the error made while measuring the baseline value</li>
     99  * 	</ul>
    100 */
    101 public List getConfigNumbers(String configName, boolean fingerprints, List differences) {
    102 
    103 	// Initialize lists
    104 	AbstractResults[] scenarios = getChildren();
    105 	int length = scenarios.length;
    106 
    107 	// Print scenario names line
    108 	List firstLine = new ArrayList();
    109 	for (int i=0; i<length; i++) {
    110 		ScenarioResults scenarioResults = (ScenarioResults) scenarios[i];
    111 		if (!fingerprints || scenarioResults.hasSummary()) {
    112 			firstLine.add(scenarioResults.getName());
    113 		}
    114 	}
    115 
    116 	// Print each build line
    117 	String[] builds = getAllSortedBuildNames(true/*descending order*/);
    118 //	int milestoneIndex = 0;
    119 //	String milestoneDate = Util.getMilestoneDate(milestoneIndex);
    120 	String currentBuildName = null;
    121 	int buildsLength= builds.length;
    122 	firstLine.add(0, new Integer(buildsLength));
    123 	differences.add(firstLine);
    124 	for (int i=0; i<buildsLength; i++) {
    125 		List line = new ArrayList();
    126 		String buildName = builds[i];
    127 		line.add(buildName);
    128 		if (!buildName.startsWith(DB_Results.getDbBaselinePrefix())) {
    129 			for (int j=0; j<length; j++) {
    130 				ScenarioResults scenarioResults = (ScenarioResults) scenarios[j];
    131 				if (!fingerprints || scenarioResults.hasSummary()) {
    132 					ConfigResults configResults = scenarioResults.getConfigResults(configName);
    133 					BuildResults buildResults = configResults == null ? null : configResults.getBuildResults(buildName);
    134 					if (buildResults == null) {
    135 						// no result for this scenario in this build
    136 						line.add(NO_BUILD_RESULTS);
    137 					} else {
    138 						line.add(configResults.getNumbers(buildResults, configResults.getBaselineBuildResults(buildName)));
    139 					}
    140 				}
    141 			}
    142 			differences.add(line);
    143 			if (currentBuildName != null && currentBuildName.charAt(0) != 'N') {
    144             }
    145 			currentBuildName = buildName;
    146 		}
    147 //		if (milestoneDate != null) { // update previous builds
    148 //			int dateComparison = milestoneDate.compareTo(Util.getBuildDate(buildName));
    149 //			if (dateComparison <= 0) {
    150 //				if (dateComparison == 0) {
    151 //                }
    152 //				if (++milestoneIndex == Util.MILESTONES.length) {
    153 //					milestoneDate = null;
    154 //				} else {
    155 //					milestoneDate = Util.getMilestoneDate(milestoneIndex);
    156 //				}
    157 //			}
    158 //		}
    159 	}
    160 
    161 	// Write differences lines
    162 	int last = buildsLength-1;
    163 	String lastBuildName = builds[last];
    164 	while (last > 0 && lastBuildName.startsWith(DB_Results.getDbBaselinePrefix())) {
    165 		lastBuildName = builds[--last];
    166 	}
    167 //	appendDifferences(lastBuildName, configName, previousMilestoneName, differences, fingerprints);
    168 //	appendDifferences(lastBuildName, configName, previousBuildName, differences, fingerprints);
    169 
    170 	// Return the computed differences
    171 	return differences;
    172 }
    173 
    174 /*
    175 double[] getConfigNumbers(BuildResults buildResults, BuildResults baselineResults) {
    176 	if (baselineResults == null) {
    177 		return INVALID_RESULTS;
    178 	}
    179 	double[] values = new double[NUMBERS_LENGTH];
    180 	for (int i=0 ;i<NUMBERS_LENGTH; i++) {
    181 		values[i] = Double.NaN;
    182 	}
    183 	double buildValue = buildResults.getValue();
    184 	values[BUILD_VALUE_INDEX] = buildValue;
    185 	double baselineValue = baselineResults.getValue();
    186 	values[BASELINE_VALUE_INDEX] = baselineValue;
    187 	double delta = (baselineValue - buildValue) / baselineValue;
    188 	values[DELTA_VALUE_INDEX] = delta;
    189 	if (Double.isNaN(delta)) {
    190 		return values;
    191 	}
    192 	long baselineCount = baselineResults.getCount();
    193 	long currentCount = buildResults.getCount();
    194 	if (baselineCount > 1 && currentCount > 1) {
    195 		double baselineError = baselineResults.getError();
    196 		double currentError = buildResults.getError();
    197 		values[BASELINE_ERROR_INDEX] = baselineError;
    198 		values[BUILD_ERROR_INDEX] = currentError;
    199 		values[DELTA_ERROR_INDEX] = Double.isNaN(baselineError)
    200 				? currentError / baselineValue
    201 				: Math.sqrt(baselineError*baselineError + currentError*currentError) / baselineValue;
    202 	}
    203 	return values;
    204 }
    205 */
    206 
    207 private ScenarioResults getScenarioResults(List scenarios, int searchedId) {
    208 	int size = scenarios.size();
    209 	for (int i=0; i<size; i++) {
    210 		ScenarioResults scenarioResults = (ScenarioResults) scenarios.get(i);
    211 		if (scenarioResults.id == searchedId) {
    212 			return scenarioResults;
    213 		}
    214 	}
    215 	return null;
    216 }
    217 
    218 /**
    219  * Returns a list of scenario results which have a summary
    220  *
    221  * @param global Indicates whether the summary must be global or not.
    222  * @param config Configuration name
    223  * @return A list of {@link ScenarioResults scenario results} which have a summary
    224  */
    225 public List getSummaryScenarios(boolean global, String config) {
    226 	int size= size();
    227 	List scenarios = new ArrayList(size);
    228 	for (int i=0; i<size; i++) {
    229 		ScenarioResults scenarioResults = (ScenarioResults) this.children.get(i);
    230 		ConfigResults configResults = scenarioResults.getConfigResults(config);
    231 		if (configResults != null) {
    232 			BuildResults buildResults = configResults.getCurrentBuildResults();
    233 			if ((global && buildResults.summaryKind == 1) || (!global && buildResults.summaryKind >= 0)) {
    234 				scenarios.add(scenarioResults);
    235 			}
    236 		}
    237 	}
    238 	return scenarios;
    239 }
    240 
    241 private String lastBuildName(int kind) {
    242 	String[] builds = getAllSortedBuildNames();
    243 	int idx = builds.length-1;
    244 	String lastBuildName = builds[idx--];
    245 	switch (kind) {
    246 		case 1: // no ref
    247 			while (lastBuildName.startsWith(DB_Results.getDbBaselinePrefix())) {
    248 				lastBuildName = builds[idx--];
    249 			}
    250 			break;
    251 		case 2: // only I-build or M-build
    252 			char ch = lastBuildName.charAt(0);
    253 			while (ch != 'I' && ch != 'M') {
    254 				lastBuildName = builds[idx--];
    255 				ch = lastBuildName.charAt(0);
    256 			}
    257 			break;
    258 		default:
    259 			break;
    260 	}
    261 	return lastBuildName;
    262 }
    263 
    264 /*
    265  * Read local file contents and populate the results model with the collected
    266  * information.
    267  */
    268 String readLocalFile(File dir, List scenarios) throws FileNotFoundException {
    269 //	if (!dir.exists()) return null;
    270 	File dataFile = new File(dir, getName()+".dat");	//$NON-NLS-1$
    271 	if (!dataFile.exists()) throw new FileNotFoundException();
    272 	DataInputStream stream = null;
    273 	try {
    274 		// Read local file info
    275 		stream = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile)));
    276 		print(" - read local files info"); //$NON-NLS-1$
    277 		String lastBuildName = stream.readUTF(); // first string is the build name
    278 
    279 		// Next field is the number of scenarios for the component
    280 		int size = stream.readInt();
    281 
    282 		// Then follows all the scenario information
    283 		for (int i=0; i<size; i++) {
    284 			// ... which starts with the scenario id
    285 			int scenario_id = stream.readInt();
    286 			ScenarioResults scenarioResults = scenarios == null ? null : getScenarioResults(scenarios, scenario_id);
    287 			if (scenarioResults == null) {
    288 				// this can happen if scenario pattern does not cover all those stored in local data file
    289 				// hence, creates a fake scenario to read the numbers and skip to the next scenario
    290 				scenarioResults = new ScenarioResults(-1, null, null);
    291 //				scenarioResults.parent = this;
    292 //				scenarioResults.readData(stream);
    293 				// Should no longer occur as we get all scenarios from database now
    294 //				throw new RuntimeException("Unexpected unfound scenario!"); //$NON-NLS-1$
    295 			}
    296 			scenarioResults.parent = this;
    297 			scenarioResults.printStream = this.printStream;
    298 			scenarioResults.readData(stream);
    299 			addChild(scenarioResults, true);
    300 			if (this.printStream != null) this.printStream.print('.');
    301 		}
    302 		println();
    303 		println("	=> "+size+" scenarios data were read from file "+dataFile); //$NON-NLS-1$ //$NON-NLS-2$
    304 
    305 		// Return last build name stored in the local files
    306 		return lastBuildName;
    307 	} catch (IOException ioe) {
    308 		println("	!!! "+dataFile+" should be deleted as it contained invalid data !!!"); //$NON-NLS-1$ //$NON-NLS-2$
    309 	} finally {
    310 		try {
    311 	        stream.close();
    312         } catch (IOException e) {
    313 	        // nothing else to do!
    314         }
    315 	}
    316 	return null;
    317 }
    318 
    319 /*
    320  * Read the database values for a build name and a list of scenarios.
    321  * The database is read only if the components does not already knows the
    322  * given build (i.e. if it has not been already read) or if the force arguments is set.
    323  */
    324 void updateBuild(String buildName, List scenarios, boolean force, File dataDir, SubMonitor subMonitor, PerformanceResults.RemainingTimeGuess timeGuess) {
    325 
    326 	// Read all variations
    327 	println("Component '"+this.name+"':"); //$NON-NLS-1$ //$NON-NLS-2$
    328 
    329 	// manage monitor
    330 	int size = scenarios.size();
    331 	subMonitor.setWorkRemaining(size+1);
    332 	StringBuffer buffer = new StringBuffer("Component "); //$NON-NLS-1$
    333 	buffer.append(this.name);
    334 	buffer.append("..."); //$NON-NLS-1$
    335 	String title = buffer.toString();
    336 	subMonitor.subTask(title+timeGuess.display());
    337 	timeGuess.count++;
    338 	subMonitor.worked(1);
    339 	if (subMonitor.isCanceled()) return;
    340 
    341 	// Read new values for the local result
    342 	boolean dirty = false;
    343 	long readTime = System.currentTimeMillis();
    344 	String log = " - read scenarios from DB:"; //$NON-NLS-1$
    345 	if (size > 0) {
    346 		for (int i=0; i<size; i++) {
    347 
    348 			// manage monitor
    349 			subMonitor.subTask(title+timeGuess.display());
    350 			timeGuess.count++;
    351 			if (log != null) {
    352 				println(log);
    353 				log = null;
    354 			}
    355 
    356 			// read results
    357 			ScenarioResults nextScenarioResults= (ScenarioResults) scenarios.get(i);
    358 			ScenarioResults scenarioResults = (ScenarioResults) getResults(nextScenarioResults.id);
    359 			if (scenarioResults == null) {
    360 				// Scenario is not known yet, force an update
    361 				scenarioResults = nextScenarioResults;
    362 				scenarioResults.parent = this;
    363 				scenarioResults.printStream = this.printStream;
    364 				scenarioResults.updateBuild(buildName, true);
    365 				dirty = true;
    366 				addChild(scenarioResults, true);
    367 			} else {
    368 				if (scenarioResults.updateBuild(buildName, force)) {
    369 					dirty = true;
    370 				}
    371 			}
    372 			if (dataDir != null && dirty && (System.currentTimeMillis() - readTime) > 300000) { // save every 5mn
    373 				writeData(buildName, dataDir, true, true);
    374 				dirty = false;
    375 				readTime = System.currentTimeMillis();
    376 			}
    377 
    378 			// manage monitor
    379 			subMonitor.worked(1);
    380 			if (subMonitor.isCanceled()) return;
    381 		}
    382 	}
    383 
    384 	// Write local files
    385 	if (dataDir != null) {
    386 		writeData(buildName, dataDir, false, dirty);
    387 	}
    388 
    389 	// Print global time
    390 	printGlobalTime(readTime);
    391 
    392 }
    393 
    394 /*
    395  * Write the component results data to the file '<component name>.dat' in the given directory.
    396  */
    397 void writeData(String buildName, File dir, boolean temp, boolean dirty) {
    398 //	if (!dir.exists() && !dir.mkdirs()) {
    399 //		System.err.println("can't create directory "+dir); //$NON-NLS-1$
    400 //	}
    401 	File tmpFile = new File(dir, getName()+".tmp"); //$NON-NLS-1$
    402 	File dataFile = new File(dir, getName()+".dat"); //$NON-NLS-1$
    403 	if (!dirty) { // only possible on final write
    404 		if (tmpFile.exists()) {
    405 			if (dataFile.exists()) dataFile.delete();
    406 			tmpFile.renameTo(dataFile);
    407 			println("	=> rename temporary file to "+dataFile); //$NON-NLS-1$
    408 		}
    409 		return;
    410 	}
    411 	if (tmpFile.exists()) {
    412 		tmpFile.delete();
    413 	}
    414 	File file;
    415 	if (temp) {
    416 		file = tmpFile;
    417 	} else {
    418 		if (dataFile.exists()) {
    419 			dataFile.delete();
    420 		}
    421 		file = dataFile;
    422 	}
    423 	try {
    424 		DataOutputStream stream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
    425 		try {
    426 			int size = this.children.size();
    427 			stream.writeUTF(lastBuildName(0));
    428 			stream.writeInt(size);
    429 			for (int i=0; i<size; i++) {
    430 				ScenarioResults scenarioResults = (ScenarioResults) this.children.get(i);
    431 				scenarioResults.write(stream);
    432 			}
    433 		}
    434 		finally {
    435 			stream.close();
    436 			println("	=> extracted data "+(temp?"temporarily ":"")+"written in file "+file); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
    437 		}
    438 	} catch (FileNotFoundException e) {
    439 		System.err.println("can't create output file"+file); //$NON-NLS-1$
    440 	} catch (IOException e) {
    441 		e.printStackTrace();
    442 	}
    443 }
    444 
    445 }
    446