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.io.PrintStream;
23 import java.util.*;
24
25 import org.eclipse.core.runtime.IProgressMonitor;
26 import org.eclipse.core.runtime.OperationCanceledException;
27 import org.eclipse.core.runtime.SubMonitor;
28 import org.eclipse.test.internal.performance.results.utils.Util;
29
30
31 /**
32 * Root class to handle performance results.
33 *
34 * Usually performance results are built for a current build vs. a baseline build.
35 *
36 * This class allow to read all data from releng performance database for given
37 * configurations and scenario pattern.
38 *
39 * Then it provides easy and speedy access to all stored results.
40 */
41 public class PerformanceResults extends AbstractResults {
42
43 String[] allBuildNames = null;
44 Map allScenarios;
45 String lastBuildName; // Name of the last used build
46 String baselineName; // Name of the baseline build used for comparison
47 String baselinePrefix;
48 private String scenarioPattern = "%"; //$NON-NLS-1$
49 private String[] components;
50 String[] configNames, sortedConfigNames;
51 String[] configDescriptions, sortedConfigDescriptions;
52 private String configPattern;
53
54 boolean dbRequired;
55 boolean needToUpdateLocalFile;
56
57 /*
58 * Local class helping to guess remaining time while reading results from DB
59 */
60 class RemainingTimeGuess {
61 int all, count;
62 long start;
63 double remaining;
RemainingTimeGuess(int all, long start)64 RemainingTimeGuess(int all, long start) {
65 this.all = all;
66 this.start = start;
67 }
display()68 String display() {
69 StringBuffer buffer = new StringBuffer(" [elapsed: "); //$NON-NLS-1$
70 long elapsed = getElapsed();
71 buffer.append(Util.timeChrono(elapsed));
72 if (this.count > 0) {
73 buffer.append(" | left: "); //$NON-NLS-1$
74 long remainingTime = getRemainingTime(elapsed);
75 buffer.append(Util.timeChrono(remainingTime));
76 buffer.append(" | end: "); //$NON-NLS-1$
77 buffer.append(Util.timeEnd(remainingTime));
78 }
79 buffer.append(']');
80 return buffer.toString();
81 }
getRemainingTime(long elapsed)82 private long getRemainingTime(long elapsed) {
83 return (long) ((((double)elapsed) / this.count) * (this.all - this.count));
84 }
getElapsed()85 private long getElapsed() {
86 return System.currentTimeMillis() - this.start;
87 }
88 }
89
90
91 // Failure threshold
92 public static final int DEFAULT_FAILURE_THRESHOLD = 10;
93 int failure_threshold = DEFAULT_FAILURE_THRESHOLD;
94
PerformanceResults(PrintStream stream)95 public PerformanceResults(PrintStream stream) {
96 super(null, null);
97 this.printStream = stream;
98 this.dbRequired = false;
99 setDefaults();
100 }
101
PerformanceResults(String name, String baseline, String baselinePrefix, PrintStream stream)102 public PerformanceResults(String name, String baseline, String baselinePrefix, PrintStream stream) {
103 super(null, name);
104 this.baselineName = baseline;
105 this.baselinePrefix = baselinePrefix;
106 this.printStream = stream;
107 this.dbRequired = true;
108 setDefaults();
109 }
110
111 /**
112 * Returns the list of all builds currently read.
113 *
114 * @return The names list of all currently known builds
115 */
getAllBuildNames()116 public String[] getAllBuildNames() {
117 if (this.allBuildNames == null) {
118 setAllBuildNames();
119 }
120 return this.allBuildNames;
121 }
122
123 /**
124 * Returns the name of the baseline used for extracted results
125 *
126 * @return The build name of the baseline of <code>null</code>
127 * if no specific baseline is used for the extracted results.
128 */
getBaselineName()129 public String getBaselineName() {
130 return this.baselineName;
131 }
132
133 /*
134 * Get the baseline prefix (computed from #baselineName).
135 */
getBaselinePrefix()136 String getBaselinePrefix() {
137 return this.baselinePrefix;
138 }
139
140 /*
141 * Get the build date (see #getBuildDate(String, String)).
142 */
getBuildDate()143 public String getBuildDate() {
144 String buildName = getName();
145 if (buildName == null) return ""; //$NON-NLS-1$
146 return Util.getBuildDate(getName(), getBaselinePrefix());
147 }
148
149 /**
150 * Return the list of components concerned by performance results.
151 *
152 * @return The list of the components
153 */
getComponents()154 public String[] getComponents() {
155 return this.components;
156 }
157
158 /**
159 * Get the scenarios of a given component.
160 *
161 * @param componentName The component name. Should not be <code>null</code>
162 * @return A list of {@link ScenarioResults scenario results}
163 */
getComponentScenarios(String componentName)164 public List getComponentScenarios(String componentName) {
165 ComponentResults componentResults = (ComponentResults) getResults(componentName);
166 if (componentResults == null) return null;
167 return Collections.unmodifiableList(componentResults.children);
168 }
169
170 /**
171 * Get the scenarios which have a summary for a given component.
172 *
173 * @param componentName The component name
174 * @param config Configuration name
175 * @return A list of {@link ScenarioResults scenario results} which have a summary
176 */
getComponentSummaryScenarios(String componentName, String config)177 public List getComponentSummaryScenarios(String componentName, String config) {
178 if (componentName == null) {
179 int size = size();
180 List scenarios = new ArrayList();
181 for (int i=0; i< size; i++) {
182 ComponentResults componentResults = (ComponentResults) this.children.get(i);
183 scenarios.addAll(componentResults.getSummaryScenarios(true, config));
184 }
185 return scenarios;
186 }
187 ComponentResults componentResults = (ComponentResults) getResults(componentName);
188 return componentResults.getSummaryScenarios(false, config);
189 }
190
191 /**
192 * Return the configuration boxes considered for this performance results
193 * sorted or not depending on the given flag.
194 *
195 * @param sort Indicates whether the list must be sorted or not.
196 * The order is defined by the configuration names, not by the box names
197 * @return The list of configuration boxes sorted by configuration names
198 */
getConfigBoxes(boolean sort)199 public String[] getConfigBoxes(boolean sort) {
200 return sort ? this.sortedConfigDescriptions : this.configDescriptions;
201 }
202
203 /**
204 * Return the configuration names considered for this performance results
205 * sorted or not depending on the given flag.
206 *
207 * @param sort Indicates whether the list must be sorted or not
208 * @return The list of configuration names
209 */
getConfigNames(boolean sort)210 public String[] getConfigNames(boolean sort) {
211 return sort ?this.sortedConfigNames : this.configNames;
212 }
213
214 /*
215 * Compute a SQL pattern from all stored configuration names.
216 * For example 'eclipseperflnx1', 'eclipseperflnx2' and 'eclipseperflnx3'
217 * will return 'eclipseperflnx_'.
218 */
getConfigurationsPattern()219 String getConfigurationsPattern() {
220 if (this.configPattern == null) {
221 int length = this.sortedConfigNames == null ? 0 : this.sortedConfigNames.length;
222 if (length == 0) return null;
223 this.configPattern = this.sortedConfigNames[0];
224 int refLength = this.configPattern.length();
225 for (int i=1; i<length; i++) {
226 String config = this.sortedConfigNames[i];
227 StringBuffer newConfig = null;
228 if (refLength != config.length()) return null; // strings have not the same length => cannot find a pattern
229 for (int j=0; j<refLength; j++) {
230 char c = this.configPattern.charAt(j);
231 if (config.charAt(j) != c) {
232 if (newConfig == null) {
233 newConfig = new StringBuffer(refLength);
234 if (j == 0) return null; // first char is already different => cannot find a pattern
235 newConfig.append(this.configPattern.substring(0, j));
236 }
237 newConfig.append('_');
238 } else if (newConfig != null) {
239 newConfig.append(c);
240 }
241 }
242 if (newConfig != null) {
243 this.configPattern = newConfig.toString();
244 }
245 }
246 }
247 return this.configPattern;
248 }
249
250 /**
251 * Return the name of the last build name except baselines.
252 *
253 * @return the name of the last build
254 */
getLastBuildName()255 public String getLastBuildName() {
256 return getLastBuildName(1/*all except baselines*/);
257 }
258 /**
259 * Return the name of the last build name
260 *
261 * @param kind Decide what kind of build is taken into account
262 * 0: all kind of build
263 * 1: all except baseline builds
264 * 2: all except baseline and nightly builds
265 * 3: only integration builds
266 * @return the name of the last build of the selected kind
267 */
getLastBuildName(int kind)268 public String getLastBuildName(int kind) {
269 if (this.name == null) {
270 getAllBuildNames(); // init build names if necessary
271 int idx = this.allBuildNames.length-1;
272 this.name = this.allBuildNames[idx];
273 if (kind > 0) {
274 loop: while (idx-- >= 0) {
275 switch (this.name.charAt(0)) {
276 case 'N':
277 if (kind < 2) break loop;
278 break;
279 case 'M':
280 if (kind < 3) break loop;
281 break;
282 case 'I':
283 if (kind < 4) break loop;
284 break;
285 }
286 this.name = this.allBuildNames[idx];
287 }
288 }
289 }
290 return this.name;
291 }
292
getName()293 public String getName() {
294 if (this.name == null) {
295 setAllBuildNames();
296 }
297 return this.name;
298 }
299
300 /*
301 * (non-Javadoc)
302 * @see org.eclipse.test.internal.performance.results.AbstractResults#getPerformance()
303 */
getPerformance()304 PerformanceResults getPerformance() {
305 return this;
306 }
307
308 /**
309 * Get the results of a given scenario.
310 *
311 * @param scenarioName The scenario name
312 * @return The {@link ScenarioResults scenario results}
313 */
getScenarioResults(String scenarioName)314 public ScenarioResults getScenarioResults(String scenarioName) {
315 ComponentResults componentResults = (ComponentResults) getResults(DB_Results.getComponentNameFromScenario(scenarioName));
316 return componentResults == null ? null : (ScenarioResults) componentResults.getResults(scenarioName);
317 }
318
319 /*
320 * Init configurations from performance results database.
321 */
initConfigs()322 private void initConfigs() {
323 // create config names
324 this.configNames = DB_Results.getConfigs();
325 this.configDescriptions = DB_Results.getConfigDescriptions();
326 int length = this.configNames.length;
327 this.sortedConfigNames = new String[length];
328 for (int i = 0; i < length; i++) {
329 this.sortedConfigNames[i] = this.configNames[i];
330 }
331
332 // Sort the config names
333 Arrays.sort(this.sortedConfigNames);
334 this.sortedConfigDescriptions = new String[length];
335 for (int i=0; i<length; i++) {
336 for (int j=0; j<length; j++) {
337 if (this.sortedConfigNames[i] == this.configNames[j]) { // == is intentional!
338 this.sortedConfigDescriptions[i] = this.configDescriptions[j];
339 break;
340 }
341 }
342 }
343 }
344
345 /*
346 * Read or update data for a build from a directory supposed to have local files.
347 */
read(boolean local, String buildName, String[][] configs, boolean force, File dataDir, String taskName, SubMonitor subMonitor)348 private String[] read(boolean local, String buildName, String[][] configs, boolean force, File dataDir, String taskName, SubMonitor subMonitor) {
349 if (local && dataDir == null) {
350 throw new IllegalArgumentException("Must specify a directory to read local files!"); //$NON-NLS-1$
351 }
352 subMonitor.setWorkRemaining(100);
353
354 // Update info
355 long start = System.currentTimeMillis();
356 int allScenariosSize;
357 if (DB_Results.DB_CONNECTION) {
358 try {
359 // Read all scenarios
360 allScenariosSize = readScenarios(buildName, subMonitor.newChild(10)) ;
361 if (allScenariosSize < 0) {
362 return null;
363 }
364
365 // Read all builds
366 DB_Results.queryAllVariations(getConfigurationsPattern());
367
368 // Refresh configs
369 if (configs == null) {
370 initConfigs();
371 } else {
372 setConfigInfo(configs);
373 }
374 } catch (OperationCanceledException e) {
375 return null;
376 }
377 } else {
378 if (this.allScenarios == null) return null;
379 allScenariosSize = this.allScenarios.size();
380 if (configs != null) {
381 setConfigInfo(configs);
382 }
383 }
384
385 // Create corresponding children
386 int componentsLength = this.components.length;
387 subMonitor.setWorkRemaining(componentsLength);
388 RemainingTimeGuess timeGuess = null;
389 for (int i=0; i<componentsLength; i++) {
390 String componentName = this.components[i];
391 List scenarios = this.allScenarios == null ? null : (List) this.allScenarios.get(componentName);
392
393 // Manage monitor
394 int percentage = (int) ((((double)(i+1)) / (componentsLength+1)) * 100);
395 StringBuffer tnBuffer= taskName==null ? new StringBuffer() : new StringBuffer(taskName);
396 tnBuffer.append(" ("); //$NON-NLS-1$
397 if (buildName != null) {
398 tnBuffer.append(buildName).append(": "); //$NON-NLS-1$
399 }
400 tnBuffer.append(percentage).append("%)"); //$NON-NLS-1$
401 subMonitor.setTaskName(tnBuffer.toString());
402 StringBuffer subTaskBuffer = new StringBuffer("Component "); //$NON-NLS-1$
403 subTaskBuffer.append(componentName);
404 subTaskBuffer.append("..."); //$NON-NLS-1$
405 subMonitor.subTask(subTaskBuffer.toString());
406
407 // Get component results
408 if (scenarios == null && !local) continue;
409 ComponentResults componentResults;
410 if (local || (buildName == null && force)) {
411 componentResults = new ComponentResults(this, componentName);
412 addChild(componentResults, true);
413 } else {
414 componentResults = (ComponentResults) getResults(componentName);
415 if (componentResults == null) {
416 componentResults = new ComponentResults(this, componentName);
417 addChild(componentResults, true);
418 }
419 }
420
421 // Read the component results
422 if (local) {
423 try {
424 componentResults.readLocalFile(dataDir, scenarios);
425 }
426 catch (FileNotFoundException ex) {
427 return null;
428 }
429 subMonitor.worked(1);
430 } else {
431 if (timeGuess == null) {
432 timeGuess = new RemainingTimeGuess(1+componentsLength+allScenariosSize, start);
433 }
434 componentResults.updateBuild(buildName, scenarios, force, dataDir, subMonitor.newChild(1), timeGuess);
435 }
436 if (subMonitor.isCanceled()) return null;
437 }
438
439 // Update names
440 setAllBuildNames();
441 writeData(dataDir);
442
443 // Print time
444 printGlobalTime(start);
445
446 return this.allBuildNames;
447 }
448
449 /**
450 * Read all data from performance database for the given configurations
451 * and scenario pattern.
452 *
453 * This method is typically called when generated performance results
454 * from a non-UI application.
455 *
456 * @param buildName The name of the build
457 * @param configs All configurations to extract results. If <code>null</code>,
458 * then all known configurations ({@link DB_Results#getConfigs()}) are read.
459 * @param pattern The pattern of the concerned scenarios
460 * @param dataDir The directory where data will be read/stored locally.
461 * If <code>null</code>, then database will be read instead and no storage
462 * will be performed
463 * @param threshold The failure percentage threshold over which a build result
464 * value compared to the baseline is considered as failing.
465 * @param monitor The progress monitor
466 *
467 * @return All known build names
468 */
readAll(String buildName, String[][] configs, String pattern, File dataDir, int threshold, IProgressMonitor monitor)469 public String[] readAll(String buildName, String[][] configs, String pattern, File dataDir, int threshold, IProgressMonitor monitor) {
470
471 // Init
472 this.scenarioPattern = pattern == null ? "%" : pattern; //$NON-NLS-1$
473 this.failure_threshold = threshold;
474 SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
475
476 // Set default names
477 setDefaults();
478
479 // Read local data files first
480 reset(dataDir);
481 String[] names = read(true, null, configs, true, dataDir, null, subMonitor.newChild(100));
482 if (names==null) {
483 // if one local files is missing then force a full DB read!
484 // TODO moderate this to force the DB read only for the missing file...
485 return read(false, null, configs, true, dataDir, null, subMonitor.newChild(900));
486 }
487
488 // Search build name in read data
489 boolean buildMissing = true;
490 if (buildName != null) {
491 this.name = buildName;
492 buildMissing = Arrays.binarySearch(names, buildName, Util.BUILD_DATE_COMPARATOR) < 0;
493 }
494
495 // Look for missing builds
496 if (buildMissing) {
497 String[] builds = DB_Results.getBuilds();
498 Arrays.sort(builds, Util.BUILD_DATE_COMPARATOR);
499 for (int i=builds.length-1; i>=0; i--) {
500 if (Arrays.binarySearch(names, builds[i], Util.BUILD_DATE_COMPARATOR) >= 0) {
501 break;
502 }
503 read(false, builds[i], configs, true, dataDir, null, subMonitor.newChild(900));
504 }
505 }
506 return this.allBuildNames;
507 }
508
509 /**
510 * Read all data from performance database for the given configurations
511 * and scenario pattern.
512 *
513 * Note that calling this method flush all previous read data.
514 *
515 * @param dataDir The directory where local files are located
516 * @param monitor The progress monitor
517 */
readLocal(File dataDir, IProgressMonitor monitor)518 public void readLocal(File dataDir, IProgressMonitor monitor) {
519
520 // Print title
521 String taskName = "Read local performance results"; //$NON-NLS-1$
522 println(taskName);
523
524 // Create monitor
525 SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
526 subMonitor.setTaskName(taskName);
527
528 // Read
529 reset(dataDir);
530 read(true, null, null, true, dataDir, taskName, subMonitor);
531 }
532
readLocalFile(File dir)533 void readLocalFile(File dir) {
534 if (!dir.exists()) return;
535 File dataFile = new File(dir, "performances.dat"); //$NON-NLS-1$
536 if (!dataFile.exists()) return;
537 DataInputStream stream = null;
538 try {
539 // Read local file info
540 stream = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile)));
541
542 // Read build info
543 String str = stream.readUTF();
544 this.needToUpdateLocalFile = this.name == null || Util.getBuildDate(this.name).compareTo(Util.getBuildDate(str)) > 0;
545 if (this.name != null && Util.getBuildDate(this.name).compareTo(Util.getBuildDate(str)) >= 0) {
546 return;
547 }
548 println(" - read performance results local files info: "); //$NON-NLS-1$
549 println(" + name : "+str);
550 this.name = str == "" ? null : str;
551 str = stream.readUTF();
552 println(" + baseline : "+str);
553 if (this.baselineName == null) {
554 this.baselineName = str == "" ? null : str;
555 }
556 str = stream.readUTF();
557 println(" + baseline prefix: "+str);
558 this.baselinePrefix = str == "" ? null : str;
559
560 // Write configs info
561 int length = stream.readInt();
562 println(" + "+length+" configs");
563 this.configNames = new String[length];
564 this.sortedConfigNames = new String[length];
565 this.configDescriptions = new String[length];
566 this.sortedConfigDescriptions = new String[length];
567 for (int i = 0; i < length; i++) {
568 this.configNames[i] = stream.readUTF();
569 this.sortedConfigNames[i] = this.configNames[i];
570 this.configDescriptions[i] = stream.readUTF();
571 this.sortedConfigDescriptions[i] = this.configDescriptions[i];
572 }
573 DB_Results.setConfigs(this.configNames);
574 DB_Results.setConfigDescriptions(this.configDescriptions);
575
576 // Write builds info
577 length = stream.readInt();
578 println(" + "+length+" builds");
579 this.allBuildNames = new String[length];
580 for (int i = 0; i < length; i++) {
581 this.allBuildNames[i] = stream.readUTF();
582 }
583
584 // Write scenarios info
585 length = stream.readInt();
586 println(" + "+length+" components");
587 this.components = new String[length];
588 this.allScenarios = new HashMap();
589 for (int i = 0; i < length; i++) {
590 this.components[i] = stream.readUTF();
591 int size = stream.readInt();
592 List scenarios = new ArrayList(size);
593 for (int j=0; j<size; j++) {
594 scenarios.add(new ScenarioResults(stream.readInt(), stream.readUTF(), stream.readUTF()));
595 }
596 this.allScenarios.put(this.components[i], scenarios);
597 }
598 println(" => read from file "+dataFile); //$NON-NLS-1$
599 } catch (IOException ioe) {
600 println(" !!! "+dataFile+" should be deleted as it contained invalid data !!!"); //$NON-NLS-1$ //$NON-NLS-2$
601 } finally {
602 try {
603 stream.close();
604 } catch (IOException e) {
605 // nothing else to do!
606 }
607 }
608 }
609
readScenarios(String buildName, SubMonitor subMonitor)610 private int readScenarios(String buildName, SubMonitor subMonitor) throws OperationCanceledException {
611 subMonitor.setWorkRemaining(10);
612 long start = System.currentTimeMillis();
613 String titleSuffix;
614 if (buildName == null) {
615 titleSuffix = "all database scenarios..."; //$NON-NLS-1$
616 } else {
617 titleSuffix = "all database scenarios for "+buildName+" build..."; //$NON-NLS-1$ //$NON-NLS-2$
618 }
619 print(" + get "+titleSuffix); //$NON-NLS-1$
620 subMonitor.subTask("Get "+titleSuffix); //$NON-NLS-1$
621 this.allScenarios = DB_Results.queryAllScenarios(this.scenarioPattern, buildName);
622 if (this.allScenarios == null) return -1;
623 int allScenariosSize = 0;
624 List componentsSet = new ArrayList(this.allScenarios.keySet());
625 Collections.sort(componentsSet);
626 int componentsSize = componentsSet.size();
627 componentsSet.toArray(this.components = new String[componentsSize]);
628 for (int i=0; i<componentsSize; i++) {
629 String componentName = this.components[i];
630 List scenarios = (List) this.allScenarios.get(componentName);
631 allScenariosSize += scenarios.size();
632 }
633 println(" -> "+allScenariosSize+" found in "+(System.currentTimeMillis()-start)+"ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
634 subMonitor.worked(10);
635 if (subMonitor.isCanceled()) throw new OperationCanceledException();
636 return allScenariosSize;
637 }
638
reset(File dataDir)639 void reset(File dataDir) {
640 this.allBuildNames = null;
641 this.children = new ArrayList();
642 // this.name = null;
643 this.components = null;
644 this.allScenarios = null;
645 readLocalFile(dataDir);
646 }
647
setAllBuildNames()648 private void setAllBuildNames() {
649 SortedSet builds = new TreeSet(Util.BUILD_DATE_COMPARATOR);
650 int size = size();
651 if (size == 0) return;
652 for (int i=0; i<size; i++) {
653 ComponentResults componentResults = (ComponentResults) this.children.get(i);
654 Set names = componentResults.getAllBuildNames();
655 builds.addAll(names);
656 }
657 int buildsSize = builds.size();
658 this.allBuildNames = new String[buildsSize];
659 if (buildsSize > 0) {
660 int n = 0;
661 Iterator buildNames = builds.iterator();
662 while (buildNames.hasNext()) {
663 String buildName = (String) buildNames.next();
664 if (this.lastBuildName == null || Util.getBuildDate(buildName).compareTo(Util.getBuildDate(this.lastBuildName)) <= 0) {
665 this.allBuildNames[n++] = buildName;
666 }
667 }
668 if (n < buildsSize) {
669 System.arraycopy(this.allBuildNames, 0, this.allBuildNames = new String[n], 0, n);
670 }
671 int idx = n-1;
672 String lastBuild = this.allBuildNames[idx--];
673 while (lastBuild.startsWith(DB_Results.getDbBaselinePrefix())) {
674 lastBuild = this.allBuildNames[idx--];
675 }
676 this.needToUpdateLocalFile = this.name == null || Util.getBuildDate(lastBuild).compareTo(Util.getBuildDate(this.name)) > 0;
677 this.name = lastBuild;
678 if (this.baselineName != null) {
679 String lastBuildDate = Util.getBuildDate(lastBuild);
680 if (Util.getBuildDate(this.baselineName).compareTo(lastBuildDate) > 0) {
681 this.baselineName = DB_Results.getLastBaselineBuild(lastBuildDate);
682 }
683 }
684 }
685 }
686
setConfigInfo(String[][] configs)687 private void setConfigInfo(String[][] configs) {
688 if (configs == null) return;
689
690 // Store config information
691 int length = configs.length;
692 this.configNames = new String[length];
693 this.sortedConfigNames = new String[length];
694 this.configDescriptions = new String[length];
695 for (int i=0; i<length; i++) {
696 this.configNames[i] = this.sortedConfigNames[i] = configs[i][0];
697 this.configDescriptions[i] = configs[i][1];
698 }
699
700 // Sort the config names
701 Arrays.sort(this.sortedConfigNames);
702 length = this.sortedConfigNames.length;
703 this.sortedConfigDescriptions = new String[length];
704 for (int i=0; i<length; i++) {
705 for (int j=0; j<length; j++) {
706 if (this.sortedConfigNames[i] == this.configNames[j]) { // == is intentional!
707 this.sortedConfigDescriptions[i] = this.configDescriptions[j];
708 break;
709 }
710 }
711 }
712 }
713
714
715 /**
716 * Set the name of the baseline used for extracted results
717 *
718 * @param buildName The name of the baseline build
719 */
setBaselineName(String buildName)720 public void setBaselineName(String buildName) {
721 this.baselineName = buildName;
722 }
723
setDefaults()724 private void setDefaults() {
725
726 // Set builds if none
727 if (size() == 0 && DB_Results.DB_CONNECTION) {
728 this.allBuildNames = DB_Results.getBuilds();
729 this.components = DB_Results.getComponents();
730 initConfigs();
731 }
732
733 // Set name if null
734 if (this.name == null) {
735 setAllBuildNames();
736 if (this.name == null) { // does not know any build
737 this.name = DB_Results.getLastCurrentBuild();
738 if (this.dbRequired) {
739 if (this.name == null) {
740 throw new RuntimeException("Cannot find any current build!"); //$NON-NLS-1$
741 }
742 this.allBuildNames = DB_Results.getBuilds();
743 this.components = DB_Results.getComponents();
744 initConfigs();
745 }
746 if (this.printStream != null) {
747 this.printStream.println(" + no build specified => use last one: "+this.name); //$NON-NLS-1$
748 }
749 }
750 }
751
752 // Init baseline name if not set
753 if (this.baselineName == null && getName() != null) {
754 String buildDate = Util.getBuildDate(getName());
755 this.baselineName = DB_Results.getLastBaselineBuild(buildDate);
756 if (this.baselineName == null && this.dbRequired) {
757 throw new RuntimeException("Cannot find any baseline to refer!"); //$NON-NLS-1$
758 }
759 if (this.printStream != null) {
760 this.printStream.println(" + no baseline specified => use last one: "+this.baselineName); //$NON-NLS-1$
761 }
762 }
763
764 // Init baseline prefix if not set
765 if (this.baselinePrefix == null && this.baselineName != null) {
766 // Assume that baseline name format is *always* x.y_yyyyMMddhhmm_yyyyMMddhhmm
767 int index = this.baselineName.lastIndexOf('_');
768 if (index > 0) {
769 this.baselinePrefix = this.baselineName.substring(0, index);
770 } else {
771 this.baselinePrefix = DB_Results.getDbBaselinePrefix();
772 }
773 }
774
775 // Set scenario pattern default
776 if (this.scenarioPattern == null) {
777 this.scenarioPattern = "%"; //$NON-NLS-1$
778 }
779
780 // Flush print stream
781 if (this.printStream != null) {
782 this.printStream.println();
783 this.printStream.flush();
784 }
785 }
786
setLastBuildName(String lastBuildName)787 public void setLastBuildName(String lastBuildName) {
788 this.lastBuildName = lastBuildName;
789 // if (lastBuildName == null) {
790 // int idx = this.allBuildNames.length-1;
791 // String lastBuild = this.allBuildNames[idx--];
792 // while (this.name.startsWith(DB_Results.getDbBaselinePrefix())) {
793 // lastBuild = this.allBuildNames[idx--];
794 // }
795 // this.name = lastBuild;
796 // } else {
797 // this.name = lastBuildName;
798 // }
799 }
800
801 /**
802 * Update a given build information with database contents.
803 *
804 * @param builds The builds to read new data
805 * @param force Force the update from the database, even if the build is
806 * already known.
807 * @param dataDir The directory where data should be stored locally if necessary.
808 * If <code>null</code>, then information changes won't be persisted.
809 * @param monitor The progress monitor
810 * @return All known builds
811 */
updateBuilds(String[] builds, boolean force, File dataDir, IProgressMonitor monitor)812 public String[] updateBuilds(String[] builds, boolean force, File dataDir, IProgressMonitor monitor) {
813
814 // Print title
815 StringBuffer buffer = new StringBuffer("Update data for "); //$NON-NLS-1$
816 int length = builds == null ? 0 : builds.length;
817 switch (length) {
818 case 0:
819 buffer.append("all builds"); //$NON-NLS-1$
820 reset(dataDir);
821 break;
822 case 1:
823 buffer.append("one build"); //$NON-NLS-1$
824 break;
825 default:
826 buffer.append("several builds"); //$NON-NLS-1$
827 break;
828 }
829 String taskName = buffer.toString();
830 println(buffer);
831
832 // Create sub-monitor
833 SubMonitor subMonitor = SubMonitor.convert(monitor, 1000*length);
834 subMonitor.setTaskName(taskName);
835
836 // Read
837 for (int i=0; i<length; i++) {
838 read(false, builds[i], null, force, dataDir, taskName, subMonitor.newChild(1000));
839 }
840
841 // Return new builds list
842 return this.allBuildNames;
843 }
844
845 /**
846 * Update a given build information with database contents.
847 *
848 * @param buildName The build name to read new data
849 * @param force Force the update from the database, even if the build is
850 * already known.
851 * @param dataDir The directory where data should be stored locally if necessary.
852 * If <code>null</code>, then information changes won't be persisted.
853 * @param monitor The progress monitor
854 * @return All known builds
855 */
updateBuild(String buildName, boolean force, File dataDir, IProgressMonitor monitor)856 public String[] updateBuild(String buildName, boolean force, File dataDir, IProgressMonitor monitor) {
857
858 // Print title
859 StringBuffer buffer = new StringBuffer("Update data for "); //$NON-NLS-1$
860 if (buildName == null) {
861 buffer.append("all builds"); //$NON-NLS-1$
862 reset(dataDir);
863 } else {
864 buffer.append("one build"); //$NON-NLS-1$
865 }
866 String taskName = buffer.toString();
867 println(buffer);
868
869 // Create sub-monitor
870 SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
871 subMonitor.setTaskName(taskName);
872
873 // Read
874 read(false, buildName, null, force, dataDir, taskName, subMonitor);
875
876 // Refresh name
877 if (buildName != null && !buildName.startsWith(DB_Results.getDbBaselinePrefix())) {
878 this.name = buildName;
879 }
880
881 // Return new list all build names
882 return this.allBuildNames;
883 }
884
885 /*
886 * Write general information.
887 */
writeData(File dir)888 void writeData(File dir) {
889 if (!DB_Results.DB_CONNECTION) {
890 // Only write new local file if there's a database connection
891 // otherwise contents may not be complete...
892 return;
893 }
894 if (dir ==null || (!dir.exists() && !dir.mkdirs())) {
895 System.err.println("can't create directory " + dir); //$NON-NLS-1$
896 return;
897 }
898 File dataFile = new File(dir, "performances.dat"); //$NON-NLS-1$
899 if (dataFile.exists()) {
900 if (!this.needToUpdateLocalFile) {
901 return;
902 }
903 dataFile.delete();
904 }
905 try {
906 DataOutputStream stream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile)));
907
908 // Write build info
909 stream.writeUTF(this.name == null ? DB_Results.getLastCurrentBuild() : this.name);
910 stream.writeUTF(this.baselineName == null ? DB_Results.getLastBaselineBuild(null) : this.baselineName);
911 stream.writeUTF(this.baselinePrefix == null ? "" : this.baselinePrefix);
912
913 // Write configs info
914 int length = this.sortedConfigNames.length;
915 stream.writeInt(length);
916 for (int i = 0; i < length; i++) {
917 stream.writeUTF(this.sortedConfigNames[i]);
918 stream.writeUTF(this.sortedConfigDescriptions[i]);
919 }
920
921 // Write builds info
922 String[] builds = this.allBuildNames == null ? DB_Results.getBuilds() : this.allBuildNames;
923 length = builds.length;
924 stream.writeInt(length);
925 for (int i = 0; i < length; i++) {
926 stream.writeUTF(builds[i]);
927 }
928
929 // Write scenarios info
930 length = this.components.length;
931 stream.writeInt(length);
932 for (int i = 0; i < length; i++) {
933 stream.writeUTF(this.components[i]);
934 List scenarios = (List) this.allScenarios.get(this.components[i]);
935 int size = scenarios.size();
936 stream.writeInt(size);
937 for (int j=0; j<size; j++) {
938 final ScenarioResults scenarioResults = (ScenarioResults)scenarios.get(j);
939 stream.writeInt(scenarioResults.getId());
940 stream.writeUTF(scenarioResults.getName());
941 stream.writeUTF(scenarioResults.getLabel());
942 }
943 }
944
945 // Close
946 stream.close();
947 println(" => performance results general data written in file " + dataFile); //$NON-NLS-1$
948 } catch (FileNotFoundException e) {
949 System.err.println("can't create output file" + dataFile); //$NON-NLS-1$
950 } catch (IOException e) {
951 e.printStackTrace();
952 }
953 }
954
955 }
956