| /******************************************************************************* |
| * 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(); |
| } |
| } |
| |
| } |