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