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 *******************************************************************************/ 11package org.eclipse.test.internal.performance.results.db; 12 13import java.io.BufferedInputStream; 14import java.io.BufferedOutputStream; 15import java.io.DataInputStream; 16import java.io.DataOutputStream; 17import java.io.File; 18import java.io.FileInputStream; 19import java.io.FileNotFoundException; 20import java.io.FileOutputStream; 21import java.io.IOException; 22import java.util.ArrayList; 23import java.util.Arrays; 24import java.util.Comparator; 25import java.util.HashSet; 26import java.util.List; 27import java.util.Set; 28 29import org.eclipse.core.runtime.SubMonitor; 30import 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 */ 40public class ComponentResults extends AbstractResults { 41 42public ComponentResults(AbstractResults parent, String name) { 43 super(parent, name); 44 this.printStream = parent.printStream; 45} 46 47Set 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 */ 63public String[] getAllSortedBuildNames() { 64 return getAllSortedBuildNames(false/*ascending order*/); 65} 66 67String[] 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 81ComponentResults 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*/ 101public 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/* 175double[] 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 207private 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 */ 225public 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 241private 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 */ 268String 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 */ 324void 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 */ 397void 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