tmf: Move plugins to their own sub-directory
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / trace / experiment / TmfExperiment.java
1 /*******************************************************************************
2 * Copyright (c) 2009, 2014 Ericsson, École Polytechnique de Montréal
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Francois Chouinard - Initial API and implementation
11 * Francois Chouinard - Updated as per TMF Trace Model 1.0
12 * Patrick Tasse - Updated for removal of context clone
13 * Patrick Tasse - Updated for ranks in experiment location
14 * Geneviève Bastien - Added support of experiment synchronization
15 * Added the initExperiment method and default constructor
16 * Bernd Hufmann - Updated for added interfaces to ITmfEventProvider
17 *******************************************************************************/
18
19 package org.eclipse.tracecompass.tmf.core.trace.experiment;
20
21 import java.io.File;
22 import java.nio.ByteBuffer;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.concurrent.locks.Lock;
26 import java.util.concurrent.locks.ReentrantLock;
27
28 import org.eclipse.core.resources.IProject;
29 import org.eclipse.core.resources.IResource;
30 import org.eclipse.core.runtime.CoreException;
31 import org.eclipse.core.runtime.IStatus;
32 import org.eclipse.core.runtime.MultiStatus;
33 import org.eclipse.core.runtime.Status;
34 import org.eclipse.jdt.annotation.Nullable;
35 import org.eclipse.tracecompass.internal.tmf.core.Activator;
36 import org.eclipse.tracecompass.internal.tmf.core.trace.experiment.TmfExperimentContext;
37 import org.eclipse.tracecompass.internal.tmf.core.trace.experiment.TmfExperimentLocation;
38 import org.eclipse.tracecompass.internal.tmf.core.trace.experiment.TmfLocationArray;
39 import org.eclipse.tracecompass.tmf.core.TmfCommonConstants;
40 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
41 import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
42 import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest;
43 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
44 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
45 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceRangeUpdatedSignal;
46 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSynchronizedSignal;
47 import org.eclipse.tracecompass.tmf.core.synchronization.SynchronizationAlgorithm;
48 import org.eclipse.tracecompass.tmf.core.synchronization.SynchronizationManager;
49 import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
50 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
51 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
52 import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
53 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
54 import org.eclipse.tracecompass.tmf.core.trace.TmfTrace;
55 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
56 import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfPersistentlyIndexable;
57 import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfTraceIndexer;
58 import org.eclipse.tracecompass.tmf.core.trace.indexer.TmfBTreeTraceIndexer;
59 import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
60
61 /**
62 * TmfExperiment presents a time-ordered, unified view of a set of ITmfTrace:s
63 * that are part of a tracing experiment.
64 *
65 * @version 1.0
66 * @author Francois Chouinard
67 */
68 public class TmfExperiment extends TmfTrace implements ITmfPersistentlyIndexable {
69
70 // ------------------------------------------------------------------------
71 // Constants
72 // ------------------------------------------------------------------------
73
74 /**
75 * The file name of the Synchronization
76 *
77 * @deprecated This file name shouldn't be used directly anymore. All
78 * synchronization files have been moved to a folder and you
79 * should use the {@link #getSynchronizationFolder(boolean)}
80 * method to return the path to this folder.
81 */
82 @Deprecated
83 public static final String SYNCHRONIZATION_FILE_NAME = "synchronization.bin"; //$NON-NLS-1$
84
85 /**
86 * The name of the directory containing trace synchronization data. This
87 * directory typically will be preserved when traces are synchronized.
88 * Analysis involved in synchronization can put their supplementary files in
89 * there so they are not deleted when synchronized traces are copied.
90 */
91 private static final String SYNCHRONIZATION_DIRECTORY = "sync_data"; //$NON-NLS-1$
92
93 /**
94 * The default index page size
95 */
96 public static final int DEFAULT_INDEX_PAGE_SIZE = 5000;
97
98 // ------------------------------------------------------------------------
99 // Attributes
100 // ------------------------------------------------------------------------
101
102 /**
103 * The set of traces that constitute the experiment
104 */
105 private boolean fInitialized = false;
106
107 /**
108 * Lock for synchronization methods. These methods cannot be 'synchronized'
109 * since it makes it impossible to use an event request on the experiment
110 * during synchronization (the request thread would block)
111 */
112 private final Lock fSyncLock = new ReentrantLock();
113
114 // ------------------------------------------------------------------------
115 // Construction
116 // ------------------------------------------------------------------------
117
118 /**
119 * Default constructor. Should not be used directly, but is needed for
120 * extension points.
121 *
122 * @deprecated Do not call this directly (but do not remove it either!)
123 */
124 @Deprecated
125 public TmfExperiment() {
126 super();
127 }
128
129 /**
130 * Constructor of an experiment, taking the type, path, traces,
131 * indexPageSize and resource
132 *
133 * @param type
134 * The event type
135 * @param path
136 * The experiment path
137 * @param traces
138 * The experiment set of traces
139 * @param indexPageSize
140 * The experiment index page size. You can use
141 * {@link TmfExperiment#DEFAULT_INDEX_PAGE_SIZE} for a default
142 * value.
143 * @param resource
144 * The resource associated to the experiment. You can use 'null'
145 * for no resources (tests, etc.)
146 */
147 public TmfExperiment(final Class<? extends ITmfEvent> type,
148 final String path,
149 final ITmfTrace[] traces,
150 final int indexPageSize,
151 final @Nullable IResource resource) {
152 initExperiment(type, path, traces, indexPageSize, resource);
153 }
154
155 @Override
156 protected ITmfTraceIndexer createIndexer(int interval) {
157 if (getCheckpointSize() > 0) {
158 return new TmfBTreeTraceIndexer(this, interval);
159 }
160 return super.createIndexer(interval);
161 }
162
163 /**
164 * Clears the experiment
165 */
166 @Override
167 public synchronized void dispose() {
168
169 // Clean up the index if applicable
170 if (getIndexer() != null) {
171 getIndexer().dispose();
172 }
173
174 super.dispose();
175 }
176
177 // ------------------------------------------------------------------------
178 // ITmfTrace - Initializers
179 // ------------------------------------------------------------------------
180
181 @Override
182 public void initTrace(final IResource resource, final String path, final Class<? extends ITmfEvent> type) {
183 /* Do nothing for experiments */
184 }
185
186 /**
187 * Initialization of an experiment, taking the type, path, traces,
188 * indexPageSize and resource
189 *
190 * @param type
191 * the event type
192 * @param path
193 * the experiment path
194 * @param traces
195 * the experiment set of traces
196 * @param indexPageSize
197 * the experiment index page size
198 * @param resource
199 * the resource associated to the experiment
200 */
201 public void initExperiment(final Class<? extends ITmfEvent> type,
202 final String path,
203 final ITmfTrace[] traces,
204 final int indexPageSize,
205 final @Nullable IResource resource) {
206
207 setCacheSize(indexPageSize);
208 setStreamingInterval(0);
209
210 // traces have to be set before super.initialize()
211 if (traces != null) {
212 // initialize
213 for (ITmfTrace trace : traces) {
214 if (trace != null) {
215 addChild(trace);
216 }
217 }
218 }
219
220 try {
221 super.initialize(resource, path, type);
222 } catch (TmfTraceException e) {
223 Activator.logError("Error initializing experiment", e); //$NON-NLS-1$
224 }
225
226 if (resource != null) {
227 this.synchronizeTraces();
228 }
229 }
230
231 @Override
232 public IStatus validate(final IProject project, final String path) {
233 return Status.OK_STATUS;
234 }
235
236 // ------------------------------------------------------------------------
237 // Accessors
238 // ------------------------------------------------------------------------
239
240 /**
241 * Get the traces contained in this experiment.
242 *
243 * @return The array of contained traces
244 */
245 public List<ITmfTrace> getTraces() {
246 return getChildren(ITmfTrace.class);
247 }
248
249 /**
250 * Returns the timestamp of the event at the requested index. If none,
251 * returns null.
252 *
253 * @param index
254 * the event index (rank)
255 * @return the corresponding event timestamp
256 */
257 public ITmfTimestamp getTimestamp(final int index) {
258 final ITmfContext context = seekEvent(index);
259 final ITmfEvent event = getNext(context);
260 context.dispose();
261 return (event != null) ? event.getTimestamp() : null;
262 }
263
264 // ------------------------------------------------------------------------
265 // Request management
266 // ------------------------------------------------------------------------
267
268 @Override
269 public synchronized ITmfContext armRequest(final ITmfEventRequest request) {
270
271 // Make sure we have something to read from
272 if (getChildren().isEmpty()) {
273 return null;
274 }
275
276 if (!TmfTimestamp.BIG_BANG.equals(request.getRange().getStartTime())
277 && request.getIndex() == 0) {
278 final ITmfContext context = seekEvent(request.getRange().getStartTime());
279 request.setStartIndex((int) context.getRank());
280 return context;
281
282 }
283
284 return seekEvent(request.getIndex());
285 }
286
287 // ------------------------------------------------------------------------
288 // ITmfTrace trace positioning
289 // ------------------------------------------------------------------------
290
291 @Override
292 public synchronized ITmfContext seekEvent(final ITmfLocation location) {
293 // Validate the location
294 if (location != null && !(location instanceof TmfExperimentLocation)) {
295 return null; // Throw an exception?
296 }
297
298 int length = getNbChildren();
299
300 // Initialize the location array if necessary
301 TmfLocationArray locationArray = ((location == null) ?
302 new TmfLocationArray(length) :
303 ((TmfExperimentLocation) location).getLocationInfo());
304
305 ITmfLocation[] locations = locationArray.getLocations();
306 long[] ranks = locationArray.getRanks();
307
308 // Create and populate the context's traces contexts
309 final TmfExperimentContext context = new TmfExperimentContext(length);
310
311 // Position the traces
312 long rank = 0;
313 for (int i = 0; i < length; i++) {
314 // Get the relevant trace attributes
315 final ITmfContext traceContext = ((ITmfTrace) getChild(i)).seekEvent(locations[i]);
316 context.setContext(i, traceContext);
317 traceContext.setRank(ranks[i]);
318 // update location after seek
319 locations[i] = traceContext.getLocation();
320 context.setEvent(i, ((ITmfTrace) getChild(i)).getNext(traceContext));
321 rank += ranks[i];
322 }
323
324 // Finalize context
325 context.setLocation(new TmfExperimentLocation(new TmfLocationArray(locations, ranks)));
326 context.setLastTrace(TmfExperimentContext.NO_TRACE);
327 context.setRank(rank);
328
329 return context;
330 }
331
332 // ------------------------------------------------------------------------
333 // ITmfTrace - SeekEvent operations (returning a trace context)
334 // ------------------------------------------------------------------------
335
336 @Override
337 public ITmfContext seekEvent(final double ratio) {
338 final ITmfContext context = seekEvent(Math.round(ratio * getNbEvents()));
339 return context;
340 }
341
342 @Override
343 public double getLocationRatio(final ITmfLocation location) {
344 if (location instanceof TmfExperimentLocation) {
345 long rank = 0;
346 TmfLocationArray locationArray = ((TmfExperimentLocation) location).getLocationInfo();
347 for (int i = 0; i < locationArray.size(); i++) {
348 rank += locationArray.getRank(i);
349 }
350 return (double) rank / getNbEvents();
351 }
352 return 0.0;
353 }
354
355 @Override
356 public ITmfLocation getCurrentLocation() {
357 // never used
358 return null;
359 }
360
361 // ------------------------------------------------------------------------
362 // ITmfTrace trace positioning
363 // ------------------------------------------------------------------------
364
365 @Override
366 public synchronized ITmfEvent parseEvent(final ITmfContext context) {
367 final ITmfContext tmpContext = seekEvent(context.getLocation());
368 final ITmfEvent event = getNext(tmpContext);
369 return event;
370 }
371
372 @Override
373 public synchronized ITmfEvent getNext(ITmfContext context) {
374
375 // Validate the context
376 if (!(context instanceof TmfExperimentContext)) {
377 return null; // Throw an exception?
378 }
379
380 int length = getNbChildren();
381
382 // Make sure that we have something to read from
383 if (length == 0) {
384 return null;
385 }
386
387 TmfExperimentContext expContext = (TmfExperimentContext) context;
388
389 // If an event was consumed previously, first get the next one from that
390 // trace
391 final int lastTrace = expContext.getLastTrace();
392 if (lastTrace != TmfExperimentContext.NO_TRACE) {
393 final ITmfContext traceContext = expContext.getContext(lastTrace);
394 expContext.setEvent(lastTrace, ((ITmfTrace) getChild(lastTrace)).getNext(traceContext));
395 expContext.setLastTrace(TmfExperimentContext.NO_TRACE);
396 }
397
398 // Scan the candidate events and identify the "next" trace to read from
399 int trace = TmfExperimentContext.NO_TRACE;
400 ITmfTimestamp timestamp = TmfTimestamp.BIG_CRUNCH;
401 for (int i = 0; i < length; i++) {
402 final ITmfEvent event = expContext.getEvent(i);
403
404 if (event != null) {
405 final ITmfTimestamp otherTS = event.getTimestamp();
406 if (otherTS.compareTo(timestamp) < 0) {
407 trace = i;
408 timestamp = otherTS;
409 }
410 }
411 }
412
413 ITmfEvent event = null;
414 if (trace != TmfExperimentContext.NO_TRACE) {
415 event = expContext.getEvent(trace);
416 if (event != null) {
417 updateAttributes(expContext, event.getTimestamp());
418 expContext.increaseRank();
419 expContext.setLastTrace(trace);
420 final ITmfContext traceContext = expContext.getContext(trace);
421 if (traceContext == null) {
422 throw new IllegalStateException();
423 }
424
425 // Update the experiment location
426 TmfLocationArray locationArray = new TmfLocationArray(
427 ((TmfExperimentLocation) expContext.getLocation()).getLocationInfo(),
428 trace, traceContext.getLocation(), traceContext.getRank());
429 expContext.setLocation(new TmfExperimentLocation(locationArray));
430 }
431 }
432
433 return event;
434 }
435
436 @Override
437 public ITmfTimestamp getInitialRangeOffset() {
438
439 List<ITmfTrace> children = getChildren(ITmfTrace.class);
440
441 if (children.isEmpty()) {
442 return super.getInitialRangeOffset();
443 }
444
445 ITmfTimestamp initTs = TmfTimestamp.BIG_CRUNCH;
446 for (ITmfTrace trace : children) {
447 ITmfTimestamp ts = (trace).getInitialRangeOffset();
448 if (ts.compareTo(initTs) < 0) {
449 initTs = ts;
450 }
451 }
452 return initTs;
453 }
454
455 /**
456 * Get the path to the folder in the supplementary file where
457 * synchronization-related data can be kept so they are not deleted when the
458 * experiment is synchronized. Analysis involved in synchronization can put
459 * their supplementary files in there so they are preserved after
460 * synchronization.
461 *
462 * If the directory does not exist, it will be created. A return value of
463 * <code>null</code> means either the trace resource does not exist or
464 * supplementary resources cannot be kept.
465 *
466 * @param absolute
467 * If <code>true</code>, it returns the absolute path in the file
468 * system, including the supplementary file path. Otherwise, it
469 * returns only the directory name.
470 * @return The path to the folder where synchronization-related
471 * supplementary files can be kept or <code>null</code> if not
472 * available.
473 */
474 public String getSynchronizationFolder(boolean absolute) {
475 /* Set up the path to the synchronization file we'll use */
476 IResource resource = this.getResource();
477 String syncDirectory = null;
478
479 try {
480 /* get the directory where the file will be stored. */
481 if (resource != null) {
482 String fullDirectory = resource.getPersistentProperty(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER);
483 /* Create the synchronization data directory if not present */
484 if (fullDirectory != null) {
485 fullDirectory = fullDirectory + File.separator + SYNCHRONIZATION_DIRECTORY;
486 File syncDir = new File(fullDirectory);
487 syncDir.mkdirs();
488 }
489 if (absolute) {
490 syncDirectory = fullDirectory;
491 } else {
492 syncDirectory = SYNCHRONIZATION_DIRECTORY;
493 }
494 }
495 } catch (CoreException e) {
496 return null;
497 }
498
499 return syncDirectory;
500 }
501
502 /**
503 * Synchronizes the traces of an experiment. By default it only tries to
504 * read a synchronization file if it exists
505 *
506 * @return The synchronization object
507 */
508 public SynchronizationAlgorithm synchronizeTraces() {
509 return synchronizeTraces(false);
510 }
511
512 /**
513 * Synchronizes the traces of an experiment.
514 *
515 * @param doSync
516 * Whether to actually synchronize or just try opening a sync
517 * file
518 * @return The synchronization object
519 */
520 public SynchronizationAlgorithm synchronizeTraces(boolean doSync) {
521 fSyncLock.lock();
522
523 try {
524 String syncDirectory = getSynchronizationFolder(true);
525
526 final File syncFile = (syncDirectory != null) ? new File(syncDirectory + File.separator + SYNCHRONIZATION_FILE_NAME) : null;
527
528 final SynchronizationAlgorithm syncAlgo = SynchronizationManager.synchronizeTraces(syncFile, Collections.<ITmfTrace> singleton(this), doSync);
529
530 final TmfTraceSynchronizedSignal signal = new TmfTraceSynchronizedSignal(this, syncAlgo);
531
532 /* Broadcast in separate thread to prevent deadlock */
533 new Thread() {
534 @Override
535 public void run() {
536 broadcast(signal);
537 }
538 }.start();
539
540 return syncAlgo;
541 } finally {
542 fSyncLock.unlock();
543 }
544 }
545
546 @Override
547 @SuppressWarnings("nls")
548 public synchronized String toString() {
549 return "[TmfExperiment (" + getName() + ")]";
550 }
551
552 // ------------------------------------------------------------------------
553 // Streaming support
554 // ------------------------------------------------------------------------
555
556 private synchronized void initializeStreamingMonitor() {
557
558 if (fInitialized) {
559 return;
560 }
561 fInitialized = true;
562
563 if (getStreamingInterval() == 0) {
564 final ITmfContext context = seekEvent(0);
565 final ITmfEvent event = getNext(context);
566 context.dispose();
567 if (event == null) {
568 return;
569 }
570 final TmfTimeRange timeRange = new TmfTimeRange(event.getTimestamp(), TmfTimestamp.BIG_CRUNCH);
571 final TmfTraceRangeUpdatedSignal signal = new TmfTraceRangeUpdatedSignal(this, this, timeRange);
572
573 // Broadcast in separate thread to prevent deadlock
574 new Thread() {
575 @Override
576 public void run() {
577 broadcast(signal);
578 }
579 }.start();
580 return;
581 }
582
583 final Thread thread = new Thread("Streaming Monitor for experiment " + getName()) { //$NON-NLS-1$
584 private ITmfTimestamp safeTimestamp = null;
585 private ITmfTimestamp lastSafeTimestamp = null;
586 private TmfTimeRange timeRange = null;
587
588 @Override
589 public void run() {
590 while (!executorIsShutdown()) {
591 if (!getIndexer().isIndexing()) {
592 ITmfTimestamp startTimestamp = TmfTimestamp.BIG_CRUNCH;
593 ITmfTimestamp endTimestamp = TmfTimestamp.BIG_BANG;
594
595 for (final ITmfTrace trace : getChildren(ITmfTrace.class)) {
596 if (trace.getStartTime().compareTo(startTimestamp) < 0) {
597 startTimestamp = trace.getStartTime();
598 }
599 if (trace.getStreamingInterval() != 0 && trace.getEndTime().compareTo(endTimestamp) > 0) {
600 endTimestamp = trace.getEndTime();
601 }
602 }
603 ITmfTimestamp safeTs = safeTimestamp;
604 if (safeTs != null && (lastSafeTimestamp == null || safeTs.compareTo(lastSafeTimestamp) > 0)) {
605 timeRange = new TmfTimeRange(startTimestamp, safeTs);
606 lastSafeTimestamp = safeTs;
607 } else {
608 timeRange = null;
609 }
610 safeTimestamp = endTimestamp;
611 if (timeRange != null) {
612 final TmfTraceRangeUpdatedSignal signal =
613 new TmfTraceRangeUpdatedSignal(TmfExperiment.this, TmfExperiment.this, timeRange);
614 broadcast(signal);
615 }
616 }
617 try {
618 Thread.sleep(getStreamingInterval());
619 } catch (final InterruptedException e) {
620 e.printStackTrace();
621 }
622 }
623 }
624 };
625 thread.start();
626 }
627
628 @Override
629 public long getStreamingInterval() {
630 long interval = 0;
631 for (final ITmfTrace trace : getChildren(ITmfTrace.class)) {
632 interval = Math.max(interval, trace.getStreamingInterval());
633 }
634 return interval;
635 }
636
637 // ------------------------------------------------------------------------
638 // Signal handlers
639 // ------------------------------------------------------------------------
640
641 @Override
642 @TmfSignalHandler
643 public void traceOpened(TmfTraceOpenedSignal signal) {
644 if (signal.getTrace() == this) {
645 initializeStreamingMonitor();
646
647 /* Initialize the analysis */
648 MultiStatus status = new MultiStatus(Activator.PLUGIN_ID, IStatus.OK, null, null);
649 status.add(executeAnalysis());
650 if (!status.isOK()) {
651 Activator.log(status);
652 }
653 TmfTraceManager.refreshSupplementaryFiles(this);
654 }
655 }
656
657 @Override
658 public synchronized int getCheckpointSize() {
659 int totalCheckpointSize = 0;
660 try {
661 List<ITmfTrace> children = getChildren(ITmfTrace.class);
662 for (ITmfTrace trace : children) {
663 if (!(trace instanceof ITmfPersistentlyIndexable)) {
664 return 0;
665 }
666
667 ITmfPersistentlyIndexable persistableIndexTrace = (ITmfPersistentlyIndexable) trace;
668 int currentTraceCheckpointSize = persistableIndexTrace.getCheckpointSize();
669 if (currentTraceCheckpointSize <= 0) {
670 return 0;
671 }
672 totalCheckpointSize += currentTraceCheckpointSize;
673 // each entry in the TmfLocationArray has a rank in addition
674 // of the location
675 totalCheckpointSize += 8;
676 }
677 } catch (UnsupportedOperationException e) {
678 return 0;
679 }
680
681 return totalCheckpointSize;
682 }
683
684 @Override
685 public ITmfLocation restoreLocation(ByteBuffer bufferIn) {
686 List<ITmfTrace> children = getChildren(ITmfTrace.class);
687 int length = children.size();
688 ITmfLocation[] locations = new ITmfLocation[length];
689 long[] ranks = new long[length];
690 for (int i = 0; i < length; ++i) {
691 final ITmfTrace trace = children.get(i);
692 locations[i] = ((ITmfPersistentlyIndexable) trace).restoreLocation(bufferIn);
693 ranks[i] = bufferIn.getLong();
694 }
695 TmfLocationArray arr = new TmfLocationArray(locations, ranks);
696 TmfExperimentLocation l = new TmfExperimentLocation(arr);
697 return l;
698 }
699 }
This page took 0.14072 seconds and 5 git commands to generate.