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
ComponentResults(AbstractResults parent, String name)42 public ComponentResults(AbstractResults parent, String name) {
43 super(parent, name);
44 this.printStream = parent.printStream;
45 }
46
getAllBuildNames()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 */
getAllSortedBuildNames()63 public String[] getAllSortedBuildNames() {
64 return getAllSortedBuildNames(false/*ascending order*/);
65 }
66
getAllSortedBuildNames(final boolean reverse)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
getComponentResults()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 */
getConfigNumbers(String configName, boolean fingerprints, List differences)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
getScenarioResults(List scenarios, int searchedId)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 */
getSummaryScenarios(boolean global, String config)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
lastBuildName(int kind)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 */
readLocalFile(File dir, List scenarios)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 */
updateBuild(String buildName, List scenarios, boolean force, File dataDir, SubMonitor subMonitor, PerformanceResults.RemainingTimeGuess timeGuess)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 */
writeData(String buildName, File dir, boolean temp, boolean dirty)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