blob: f619c8a2441e3108f74d921ce9f420332756179e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.test.internal.performance.results.db;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.test.internal.performance.results.utils.Util;
/**
* Class to handle performance results of an eclipse component
* (for example 'org.eclipse.jdt.core').
*
* It gives access to results for each scenario run for this component.
*
* @see ScenarioResults
*/
public class ComponentResults extends AbstractResults {
public static final double[] INVALID_RESULTS = new double[] {2};
public static final double[] NO_BUILD_RESULTS = new double[0];
public static final int BUILD_VALUE_INDEX = 0;
public static final int BASELINE_VALUE_INDEX = 1;
public static final int DELTA_VALUE_INDEX = 2;
public static final int DELTA_ERROR_INDEX = 3;
public static final int BUILD_ERROR_INDEX = 4;
public static final int BASELINE_ERROR_INDEX = 5;
private static final int NUMBERS_LENGTH = 6;
public ComponentResults(AbstractResults parent, String name) {
super(parent, name);
this.printStream = parent.printStream;
}
Set getAllBuildNames() {
Set buildNames = new HashSet();
int size = size();
for (int i=0; i<size; i++) {
ScenarioResults scenarioResults = (ScenarioResults) this.children.get(i);
Set builds = scenarioResults.getAllBuildNames();
buildNames.addAll(builds);
}
return buildNames;
}
/**
* Return all the build names for this component sorted by ascending order.
*
* @return An array of names
*/
public String[] getAllSortedBuildNames() {
return getAllSortedBuildNames(false/*ascending order*/);
}
String[] getAllSortedBuildNames(final boolean reverse) {
Set allBuildNames = getAllBuildNames();
String[] sortedNames = new String[allBuildNames.size()];
allBuildNames.toArray(sortedNames);
Arrays.sort(sortedNames, new Comparator() {
public int compare(Object o1, Object o2) {
String s1 = (String) (reverse ? o2 : o1);
String s2 = (String) (reverse ? o1 : o2);
return Util.getBuildDate(s1).compareTo(Util.getBuildDate(s2));
}
});
return sortedNames;
}
ComponentResults getComponentResults() {
return this;
}
/**
* Get all results numbers for a given machine of the current component.
*
* @param configName The name of the configuration to get numbers
* @param fingerprints Set whether only fingerprints scenario should be taken into account
* @return A list of lines. Each line represent a build and is a list of either strings or values.
* Values are an array of double:
* <ul>
* <li>{@link #BUILD_VALUE_INDEX}: the build value in milliseconds</li>
* <li>{@link #BASELINE_VALUE_INDEX}: the baseline value in milliseconds</li>
* <li>{@link #DELTA_VALUE_INDEX}: the difference between the build value and its more recent baseline</li>
* <li>{@link #DELTA_ERROR_INDEX}: the error made while computing the difference</li>
* <li>{@link #BUILD_ERROR_INDEX}: the error made while measuring the build value</li>
* <li>{@link #BASELINE_ERROR_INDEX}: the error made while measuring the baseline value</li>
* </ul>
*/
public List getConfigNumbers(String configName, boolean fingerprints, List differences) {
// Initialize lists
AbstractResults[] scenarios = getChildren();
int length = scenarios.length;
// Print scenario names line
List firstLine = new ArrayList();
for (int i=0; i<length; i++) {
ScenarioResults scenarioResults = (ScenarioResults) scenarios[i];
if (!fingerprints || scenarioResults.hasSummary()) {
firstLine.add(scenarioResults.getName());
}
}
// Print each build line
String[] builds = getAllSortedBuildNames(true/*descending order*/);
// int milestoneIndex = 0;
// String milestoneDate = Util.getMilestoneDate(milestoneIndex);
String currentBuildName = null;
int buildsLength= builds.length;
firstLine.add(0, new Integer(buildsLength));
differences.add(firstLine);
for (int i=0; i<buildsLength; i++) {
List line = new ArrayList();
String buildName = builds[i];
line.add(buildName);
if (!buildName.startsWith(DB_Results.getDbBaselinePrefix())) {
for (int j=0; j<length; j++) {
ScenarioResults scenarioResults = (ScenarioResults) scenarios[j];
if (!fingerprints || scenarioResults.hasSummary()) {
ConfigResults configResults = scenarioResults.getConfigResults(configName);
BuildResults buildResults = configResults == null ? null : configResults.getBuildResults(buildName);
if (buildResults == null) {
// no result for this scenario in this build
line.add(NO_BUILD_RESULTS);
} else {
getConfigNumbers(buildResults, configResults.getBaselineBuildResults(buildName), line);
}
}
}
differences.add(line);
if (currentBuildName != null && currentBuildName.charAt(0) != 'N') {
}
currentBuildName = buildName;
}
// if (milestoneDate != null) { // update previous builds
// int dateComparison = milestoneDate.compareTo(Util.getBuildDate(buildName));
// if (dateComparison <= 0) {
// if (dateComparison == 0) {
// }
// if (++milestoneIndex == Util.MILESTONES.length) {
// milestoneDate = null;
// } else {
// milestoneDate = Util.getMilestoneDate(milestoneIndex);
// }
// }
// }
}
// Write differences lines
int last = buildsLength-1;
String lastBuildName = builds[last];
while (last > 0 && lastBuildName.startsWith(DB_Results.getDbBaselinePrefix())) {
lastBuildName = builds[--last];
}
// appendDifferences(lastBuildName, configName, previousMilestoneName, differences, fingerprints);
// appendDifferences(lastBuildName, configName, previousBuildName, differences, fingerprints);
// Return the computed differences
return differences;
}
private void getConfigNumbers(BuildResults buildResults, BuildResults baselineResults, List line) {
if (baselineResults == null) {
line.add(INVALID_RESULTS);
return;
}
double[] values = new double[NUMBERS_LENGTH];
for (int i=0 ;i<NUMBERS_LENGTH; i++) {
values[i] = Double.NaN;
}
double buildValue = buildResults.getValue();
values[BUILD_VALUE_INDEX] = buildValue;;
double baselineValue = baselineResults.getValue();
values[BASELINE_VALUE_INDEX] = baselineValue;
double delta = (baselineValue - buildValue) / baselineValue;
values[DELTA_VALUE_INDEX] = delta;
if (Double.isNaN(delta)) {
line.add(values);
return;
}
long baselineCount = baselineResults.getCount();
long currentCount = buildResults.getCount();
if (baselineCount > 1 && currentCount > 1) {
double baselineError = baselineResults.getError();
double currentError = buildResults.getError();
values[BASELINE_ERROR_INDEX] = baselineError;
values[BUILD_ERROR_INDEX] = currentError;
values[DELTA_ERROR_INDEX] = Double.isNaN(baselineError)
? currentError / baselineValue
: Math.sqrt(baselineError*baselineError + currentError*currentError) / baselineValue;
}
line.add(values);
}
private ScenarioResults getScenarioResults(List scenarios, int searchedId) {
int size = scenarios.size();
for (int i=0; i<size; i++) {
ScenarioResults scenarioResults = (ScenarioResults) scenarios.get(i);
if (scenarioResults.id == searchedId) {
return scenarioResults;
}
}
return null;
}
/**
* Returns a list of scenario results which have a summary
*
* @param global Indicates whether the summary must be global or not.
* @param config Configuration name
* @return A list of {@link ScenarioResults scenario results} which have a summary
*/
public List getSummaryScenarios(boolean global, String config) {
int size= size();
List scenarios = new ArrayList(size);
for (int i=0; i<size; i++) {
ScenarioResults scenarioResults = (ScenarioResults) this.children.get(i);
ConfigResults configResults = scenarioResults.getConfigResults(config);
if (configResults != null) {
BuildResults buildResults = configResults.getCurrentBuildResults();
if ((global && buildResults.summaryKind == 1) || (!global && buildResults.summaryKind >= 0)) {
scenarios.add(scenarioResults);
}
}
}
return scenarios;
}
private String lastBuildName(int kind) {
String[] builds = getAllSortedBuildNames();
int idx = builds.length-1;
String lastBuildName = builds[idx--];
switch (kind) {
case 1: // no ref
while (lastBuildName.startsWith(DB_Results.getDbBaselinePrefix())) {
lastBuildName = builds[idx--];
}
break;
case 2: // only I-build or M-build
char ch = lastBuildName.charAt(0);
while (ch != 'I' && ch != 'M') {
lastBuildName = builds[idx--];
ch = lastBuildName.charAt(0);
}
break;
default:
break;
}
return lastBuildName;
}
/*
* Read local file contents and populate the results model with the collected
* information.
*/
String readLocalFile(File dir, List scenarios) throws FileNotFoundException {
// if (!dir.exists()) return null;
File dataFile = new File(dir, getName()+".dat"); //$NON-NLS-1$
if (!dataFile.exists()) throw new FileNotFoundException();
DataInputStream stream = null;
try {
// Read local file info
stream = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile)));
print(" - read local files info"); //$NON-NLS-1$
String lastBuildName = stream.readUTF(); // first string is the build name
// Next field is the number of scenarios for the component
int size = stream.readInt();
// Then follows all the scenario information
for (int i=0; i<size; i++) {
// ... which starts with the scenario id
int scenario_id = stream.readInt();
ScenarioResults scenarioResults = scenarios == null ? null : getScenarioResults(scenarios, scenario_id);
if (scenarioResults == null) {
// this can happen if scenario pattern does not cover all those stored in local data file
// hence, creates a fake scenario to read the numbers and skip to the next scenario
scenarioResults = new ScenarioResults(-1, null, null);
// scenarioResults.parent = this;
// scenarioResults.readData(stream);
// Should no longer occur as we get all scenarios from database now
// throw new RuntimeException("Unexpected unfound scenario!"); //$NON-NLS-1$
}
scenarioResults.parent = this;
scenarioResults.printStream = this.printStream;
scenarioResults.readData(stream);
addChild(scenarioResults, true);
if (this.printStream != null) this.printStream.print('.');
}
println();
println(" => "+size+" scenarios data were read from file "+dataFile); //$NON-NLS-1$ //$NON-NLS-2$
// Return last build name stored in the local files
return lastBuildName;
} catch (IOException ioe) {
println(" !!! "+dataFile+" should be deleted as it contained invalid data !!!"); //$NON-NLS-1$ //$NON-NLS-2$
} finally {
try {
stream.close();
} catch (IOException e) {
// nothing else to do!
}
}
return null;
}
/*
* Read the database values for a build name and a list of scenarios.
* The database is read only if the components does not already knows the
* given build (i.e. if it has not been already read) or if the force arguments is set.
*/
void updateBuild(String buildName, List scenarios, boolean force, File dataDir, SubMonitor subMonitor, PerformanceResults.RemainingTimeGuess timeGuess) {
// Read all variations
println("Component '"+this.name+"':"); //$NON-NLS-1$ //$NON-NLS-2$
PerformanceResults performanceResults = getPerformance();
DB_Results.queryAllVariations(performanceResults.getConfigurationsPattern());
// manage monitor
int size = scenarios.size();
subMonitor.setWorkRemaining(size+1);
StringBuffer buffer = new StringBuffer("Component "); //$NON-NLS-1$
buffer.append(this.name);
buffer.append("..."); //$NON-NLS-1$
String title = buffer.toString();
subMonitor.subTask(title+timeGuess.display());
timeGuess.count++;
subMonitor.worked(1);
if (subMonitor.isCanceled()) return;
// Read new values for the local result
boolean dirty = false;
long readTime = System.currentTimeMillis();
String log = " - read scenarios from DB:"; //$NON-NLS-1$
if (size > 0) {
for (int i=0; i<size; i++) {
// manage monitor
subMonitor.subTask(title+timeGuess.display());
timeGuess.count++;
if (log != null) {
println(log);
log = null;
}
// read results
ScenarioResults nextScenarioResults= (ScenarioResults) scenarios.get(i);
ScenarioResults scenarioResults = (ScenarioResults) getResults(nextScenarioResults.id);
if (scenarioResults == null) {
// Scenario is not known yet, force an update
scenarioResults = nextScenarioResults;
scenarioResults.parent = this;
scenarioResults.printStream = this.printStream;
scenarioResults.updateBuild(buildName, true);
dirty = true;
addChild(scenarioResults, true);
} else {
if (scenarioResults.updateBuild(buildName, force)) {
dirty = true;
}
}
if (dataDir != null && dirty && (System.currentTimeMillis() - readTime) > 300000) { // save every 5mn
writeData(buildName, dataDir, true, true);
dirty = false;
readTime = System.currentTimeMillis();
}
// manage monitor
subMonitor.worked(1);
if (subMonitor.isCanceled()) return;
}
}
// Write local files
if (dataDir != null) {
writeData(buildName, dataDir, false, dirty);
}
// Print global time
printGlobalTime(readTime);
}
/*
* Write the component results data to the file '<component name>.dat' in the given directory.
*/
void writeData(String buildName, File dir, boolean temp, boolean dirty) {
// if (!dir.exists() && !dir.mkdirs()) {
// System.err.println("can't create directory "+dir); //$NON-NLS-1$
// }
File tmpFile = new File(dir, getName()+".tmp"); //$NON-NLS-1$
File dataFile = new File(dir, getName()+".dat"); //$NON-NLS-1$
if (!dirty) { // only possible on final write
if (tmpFile.exists()) {
if (dataFile.exists()) dataFile.delete();
tmpFile.renameTo(dataFile);
println(" => rename temporary file to "+dataFile); //$NON-NLS-1$
}
return;
}
if (tmpFile.exists()) {
tmpFile.delete();
}
File file;
if (temp) {
file = tmpFile;
} else {
if (dataFile.exists()) {
dataFile.delete();
}
file = dataFile;
}
try {
DataOutputStream stream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
int size = this.children.size();
stream.writeUTF(lastBuildName(0));
stream.writeInt(size);
for (int i=0; i<size; i++) {
ScenarioResults scenarioResults = (ScenarioResults) this.children.get(i);
scenarioResults.write(stream);
}
stream.close();
println(" => extracted data "+(temp?"temporarily ":"")+"written in file "+file); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
} catch (FileNotFoundException e) {
System.err.println("can't create output file"+file); //$NON-NLS-1$
} catch (IOException e) {
e.printStackTrace();
}
}
}