/******************************************************************************* * Copyright (c) 2009, 2014 Ericsson, École Polytechnique de Montréal * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v1.0 which * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Francois Chouinard - Initial API and implementation * Francois Chouinard - Updated as per TMF Trace Model 1.0 * Patrick Tasse - Updated for removal of context clone * Patrick Tasse - Updated for ranks in experiment location * Geneviève Bastien - Added support of experiment synchronization * Added the initExperiment method and default constructor *******************************************************************************/ package fr.inria.linuxtools.tmf.core.trace; import java.io.File; import java.nio.ByteBuffer; import java.util.Arrays; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Status; import fr.inria.linuxtools.internal.tmf.core.Activator; import fr.inria.linuxtools.internal.tmf.core.trace.TmfExperimentContext; import fr.inria.linuxtools.internal.tmf.core.trace.TmfExperimentLocation; import fr.inria.linuxtools.internal.tmf.core.trace.TmfLocationArray; import fr.inria.linuxtools.tmf.core.TmfCommonConstants; import fr.inria.linuxtools.tmf.core.event.ITmfEvent; import fr.inria.linuxtools.tmf.core.exceptions.TmfTraceException; import fr.inria.linuxtools.tmf.core.request.ITmfEventRequest; import fr.inria.linuxtools.tmf.core.signal.TmfSignalHandler; import fr.inria.linuxtools.tmf.core.signal.TmfTraceOpenedSignal; import fr.inria.linuxtools.tmf.core.signal.TmfTraceRangeUpdatedSignal; import fr.inria.linuxtools.tmf.core.signal.TmfTraceSynchronizedSignal; import fr.inria.linuxtools.tmf.core.synchronization.SynchronizationAlgorithm; import fr.inria.linuxtools.tmf.core.synchronization.SynchronizationManager; import fr.inria.linuxtools.tmf.core.timestamp.ITmfTimestamp; import fr.inria.linuxtools.tmf.core.timestamp.TmfTimeRange; import fr.inria.linuxtools.tmf.core.timestamp.TmfTimestamp; import fr.inria.linuxtools.tmf.core.trace.indexer.ITmfPersistentlyIndexable; import fr.inria.linuxtools.tmf.core.trace.indexer.ITmfTraceIndexer; import fr.inria.linuxtools.tmf.core.trace.indexer.TmfBTreeTraceIndexer; import fr.inria.linuxtools.tmf.core.trace.location.ITmfLocation; /** * TmfExperiment presents a time-ordered, unified view of a set of ITmfTrace:s * that are part of a tracing experiment. * * @version 1.0 * @author Francois Chouinard */ public class TmfExperiment extends TmfTrace implements ITmfEventParser, ITmfPersistentlyIndexable { // ------------------------------------------------------------------------ // Constants // ------------------------------------------------------------------------ /** * The file name of the Synchronization * * @since 3.0 */ public final static String SYNCHRONIZATION_FILE_NAME = "synchronization.bin"; //$NON-NLS-1$ /** * The default index page size */ public static final int DEFAULT_INDEX_PAGE_SIZE = 5000; // ------------------------------------------------------------------------ // Attributes // ------------------------------------------------------------------------ /** * The set of traces that constitute the experiment */ protected ITmfTrace[] fTraces; /** * The set of traces that constitute the experiment */ private boolean fInitialized = false; // ------------------------------------------------------------------------ // Construction // ------------------------------------------------------------------------ /** * Default constructor * @since 3.0 */ public TmfExperiment() { super(); } /** * Constructor with parameters * * @param type * the event type * @param id * the experiment id * @param traces * the experiment set of traces */ public TmfExperiment(final Class<? extends ITmfEvent> type, final String id, final ITmfTrace[] traces) { this(type, id, traces, DEFAULT_INDEX_PAGE_SIZE, null); } /** * Constructor of experiment taking type, path, traces and resource * * @param type * the event type * @param id * the experiment id * @param traces * the experiment set of traces * @param resource * the resource associated to the experiment */ public TmfExperiment(final Class<? extends ITmfEvent> type, final String id, final ITmfTrace[] traces, IResource resource) { this(type, id, traces, DEFAULT_INDEX_PAGE_SIZE, resource); } /** * @param type * the event type * @param path * the experiment path * @param traces * the experiment set of traces * @param indexPageSize * the experiment index page size */ public TmfExperiment(final Class<? extends ITmfEvent> type, final String path, final ITmfTrace[] traces, final int indexPageSize) { this(type, path, traces, indexPageSize, null); } /** * Full constructor of an experiment, taking the type, path, traces, * indexPageSize and resource * * @param type * the event type * @param path * the experiment path * @param traces * the experiment set of traces * @param indexPageSize * the experiment index page size * @param resource * the resource associated to the experiment */ public TmfExperiment(final Class<? extends ITmfEvent> type, final String path, final ITmfTrace[] traces, final int indexPageSize, IResource resource) { initExperiment(type, path, traces, indexPageSize, resource); } @Override protected ITmfTraceIndexer createIndexer(int interval) { if (getCheckpointSize() > 0) { return new TmfBTreeTraceIndexer(this, interval); } return super.createIndexer(interval); } /** * Clears the experiment */ @Override public synchronized void dispose() { // Clean up the index if applicable if (getIndexer() != null) { getIndexer().dispose(); } if (fTraces != null) { for (final ITmfTrace trace : fTraces) { trace.dispose(); } fTraces = null; } super.dispose(); } // ------------------------------------------------------------------------ // ITmfTrace - Initializers // ------------------------------------------------------------------------ @Override public void initTrace(final IResource resource, final String path, final Class<? extends ITmfEvent> type) { } /** * Initialization of an experiment, taking the type, path, traces, * indexPageSize and resource * * @param type * the event type * @param path * the experiment path * @param traces * the experiment set of traces * @param indexPageSize * the experiment index page size * @param resource * the resource associated to the experiment * @since 3.0 */ public void initExperiment(final Class<? extends ITmfEvent> type, final String path, final ITmfTrace[] traces, final int indexPageSize, IResource resource) { setCacheSize(indexPageSize); setStreamingInterval(0); setParser(this); // traces have to be set before super.initialize() fTraces = traces; try { super.initialize(resource, path, type); } catch (TmfTraceException e) { Activator.logError("Error initializing experiment", e); //$NON-NLS-1$ } if (resource != null) { try { this.synchronizeTraces(); } catch (TmfTraceException e) { Activator.logError("Error synchronizing experiment", e); //$NON-NLS-1$ } } } /** * @since 2.0 */ @Override public IStatus validate(final IProject project, final String path) { return Status.OK_STATUS; } // ------------------------------------------------------------------------ // Accessors // ------------------------------------------------------------------------ /** * Get the traces contained in this experiment. * * @return The array of contained traces */ public ITmfTrace[] getTraces() { return fTraces; } /** * Returns the timestamp of the event at the requested index. If none, * returns null. * * @param index * the event index (rank) * @return the corresponding event timestamp * @since 2.0 */ public ITmfTimestamp getTimestamp(final int index) { final ITmfContext context = seekEvent(index); final ITmfEvent event = getNext(context); context.dispose(); return (event != null) ? event.getTimestamp() : null; } // ------------------------------------------------------------------------ // Request management // ------------------------------------------------------------------------ /** * @since 2.0 */ @Override public synchronized ITmfContext armRequest(final ITmfEventRequest request) { // Make sure we have something to read from if (fTraces == null) { return null; } if (!TmfTimestamp.BIG_BANG.equals(request.getRange().getStartTime()) && request.getIndex() == 0) { final ITmfContext context = seekEvent(request.getRange().getStartTime()); request.setStartIndex((int) context.getRank()); return context; } return seekEvent(request.getIndex()); } // ------------------------------------------------------------------------ // ITmfTrace trace positioning // ------------------------------------------------------------------------ /** * @since 3.0 */ @Override public synchronized ITmfContext seekEvent(final ITmfLocation location) { // Validate the location if (location != null && !(location instanceof TmfExperimentLocation)) { return null; // Throw an exception? } // Make sure we have something to read from if (fTraces == null) { return null; } // Initialize the location array if necessary TmfLocationArray locationArray = ((location == null) ? new TmfLocationArray(fTraces.length) : ((TmfExperimentLocation) location).getLocationInfo()); ITmfLocation[] locations = locationArray.getLocations(); long[] ranks = locationArray.getRanks(); // Create and populate the context's traces contexts final TmfExperimentContext context = new TmfExperimentContext(fTraces.length); // Position the traces long rank = 0; for (int i = 0; i < fTraces.length; i++) { // Get the relevant trace attributes final ITmfContext traceContext = fTraces[i].seekEvent(locations[i]); context.setContext(i, traceContext); traceContext.setRank(ranks[i]); locations[i] = traceContext.getLocation(); // update location after seek context.setEvent(i, fTraces[i].getNext(traceContext)); rank += ranks[i]; } // Finalize context context.setLocation(new TmfExperimentLocation(new TmfLocationArray(locations, ranks))); context.setLastTrace(TmfExperimentContext.NO_TRACE); context.setRank(rank); return context; } // ------------------------------------------------------------------------ // ITmfTrace - SeekEvent operations (returning a trace context) // ------------------------------------------------------------------------ @Override public ITmfContext seekEvent(final double ratio) { final ITmfContext context = seekEvent(Math.round(ratio * getNbEvents())); return context; } /** * @since 3.0 */ @Override public double getLocationRatio(final ITmfLocation location) { if (location instanceof TmfExperimentLocation) { long rank = 0; TmfLocationArray locationArray = ((TmfExperimentLocation) location).getLocationInfo(); for (int i = 0; i < locationArray.size(); i++) { rank += locationArray.getRank(i); } return (double) rank / getNbEvents(); } return 0.0; } /** * @since 3.0 */ @Override public ITmfLocation getCurrentLocation() { // never used return null; } // ------------------------------------------------------------------------ // ITmfTrace trace positioning // ------------------------------------------------------------------------ @Override public synchronized ITmfEvent parseEvent(final ITmfContext context) { final ITmfContext tmpContext = seekEvent(context.getLocation()); final ITmfEvent event = getNext(tmpContext); return event; } @Override public synchronized ITmfEvent getNext(ITmfContext context) { // Validate the context if (!(context instanceof TmfExperimentContext)) { return null; // Throw an exception? } // Make sure that we have something to read from if (fTraces == null) { return null; } TmfExperimentContext expContext = (TmfExperimentContext) context; // If an event was consumed previously, first get the next one from that // trace final int lastTrace = expContext.getLastTrace(); if (lastTrace != TmfExperimentContext.NO_TRACE) { final ITmfContext traceContext = expContext.getContext(lastTrace); expContext.setEvent(lastTrace, fTraces[lastTrace].getNext(traceContext)); expContext.setLastTrace(TmfExperimentContext.NO_TRACE); } // Scan the candidate events and identify the "next" trace to read from int trace = TmfExperimentContext.NO_TRACE; ITmfTimestamp timestamp = TmfTimestamp.BIG_CRUNCH; for (int i = 0; i < fTraces.length; i++) { final ITmfEvent event = expContext.getEvent(i); if (event != null && event.getTimestamp() != null) { final ITmfTimestamp otherTS = event.getTimestamp(); if (otherTS.compareTo(timestamp, true) < 0) { trace = i; timestamp = otherTS; } } } ITmfEvent event = null; if (trace != TmfExperimentContext.NO_TRACE) { event = expContext.getEvent(trace); if (event != null) { updateAttributes(expContext, event.getTimestamp()); expContext.increaseRank(); expContext.setLastTrace(trace); final ITmfContext traceContext = expContext.getContext(trace); if (traceContext == null) { throw new IllegalStateException(); } // Update the experiment location TmfLocationArray locationArray = new TmfLocationArray( ((TmfExperimentLocation) expContext.getLocation()).getLocationInfo(), trace, traceContext.getLocation(), traceContext.getRank()); expContext.setLocation(new TmfExperimentLocation(locationArray)); processEvent(event); } } return event; } /** * @since 2.0 */ @Override public ITmfTimestamp getInitialRangeOffset() { if ((fTraces == null) || (fTraces.length == 0)) { return super.getInitialRangeOffset(); } ITmfTimestamp initTs = TmfTimestamp.BIG_CRUNCH; for (int i = 0; i < fTraces.length; i++) { ITmfTimestamp ts = fTraces[i].getInitialRangeOffset(); if (ts.compareTo(initTs) < 0) { initTs = ts; } } return initTs; } /** * Synchronizes the traces of an experiment. By default it only tries to * read a synchronization file if it exists * * @return The synchronization object * @throws TmfTraceException * propagate TmfTraceExceptions * @since 3.0 */ public synchronized SynchronizationAlgorithm synchronizeTraces() throws TmfTraceException { return synchronizeTraces(false); } /** * Synchronizes the traces of an experiment. * * @param doSync * Whether to actually synchronize or just try opening a sync * file * @return The synchronization object * @throws TmfTraceException * propagate TmfTraceExceptions * @since 3.0 */ public synchronized SynchronizationAlgorithm synchronizeTraces(boolean doSync) throws TmfTraceException { /* Set up the path to the synchronization file we'll use */ IResource resource = this.getResource(); String supplDirectory = null; try { /* get the directory where the file will be stored. */ if (resource != null) { supplDirectory = resource.getPersistentProperty(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER); } } catch (CoreException e) { throw new TmfTraceException(e.toString(), e); } final File syncFile = (supplDirectory != null) ? new File(supplDirectory + File.separator + SYNCHRONIZATION_FILE_NAME) : null; final SynchronizationAlgorithm syncAlgo = SynchronizationManager.synchronizeTraces(syncFile, Arrays.asList(fTraces), doSync); final TmfTraceSynchronizedSignal signal = new TmfTraceSynchronizedSignal(this, syncAlgo); /* Broadcast in separate thread to prevent deadlock */ new Thread() { @Override public void run() { broadcast(signal); } }.start(); return syncAlgo; } @Override @SuppressWarnings("nls") public synchronized String toString() { return "[TmfExperiment (" + getName() + ")]"; } // ------------------------------------------------------------------------ // Streaming support // ------------------------------------------------------------------------ private synchronized void initializeStreamingMonitor() { if (fInitialized) { return; } fInitialized = true; if (getStreamingInterval() == 0) { final ITmfContext context = seekEvent(0); final ITmfEvent event = getNext(context); context.dispose(); if (event == null) { return; } final TmfTimeRange timeRange = new TmfTimeRange(event.getTimestamp(), TmfTimestamp.BIG_CRUNCH); final TmfTraceRangeUpdatedSignal signal = new TmfTraceRangeUpdatedSignal(this, this, timeRange); // Broadcast in separate thread to prevent deadlock new Thread() { @Override public void run() { broadcast(signal); } }.start(); return; } final Thread thread = new Thread("Streaming Monitor for experiment " + getName()) { //$NON-NLS-1$ private ITmfTimestamp safeTimestamp = null; private ITmfTimestamp lastSafeTimestamp = null; private TmfTimeRange timeRange = null; @Override public void run() { while (!executorIsShutdown()) { if (!getIndexer().isIndexing()) { ITmfTimestamp startTimestamp = TmfTimestamp.BIG_CRUNCH; ITmfTimestamp endTimestamp = TmfTimestamp.BIG_BANG; for (final ITmfTrace trace : fTraces) { if (trace.getStartTime().compareTo(startTimestamp) < 0) { startTimestamp = trace.getStartTime(); } if (trace.getStreamingInterval() != 0 && trace.getEndTime().compareTo(endTimestamp) > 0) { endTimestamp = trace.getEndTime(); } } if (safeTimestamp != null && (lastSafeTimestamp == null || safeTimestamp.compareTo(lastSafeTimestamp, false) > 0)) { timeRange = new TmfTimeRange(startTimestamp, safeTimestamp); lastSafeTimestamp = safeTimestamp; } else { timeRange = null; } safeTimestamp = endTimestamp; if (timeRange != null) { final TmfTraceRangeUpdatedSignal signal = new TmfTraceRangeUpdatedSignal(TmfExperiment.this, TmfExperiment.this, timeRange); broadcast(signal); } } try { Thread.sleep(getStreamingInterval()); } catch (final InterruptedException e) { e.printStackTrace(); } } } }; thread.start(); } @Override public long getStreamingInterval() { long interval = 0; for (final ITmfTrace trace : fTraces) { interval = Math.max(interval, trace.getStreamingInterval()); } return interval; } // ------------------------------------------------------------------------ // Signal handlers // ------------------------------------------------------------------------ @Override @TmfSignalHandler public void traceOpened(TmfTraceOpenedSignal signal) { if (signal.getTrace() == this) { initializeStreamingMonitor(); /* Initialize the analysis */ MultiStatus status = new MultiStatus(Activator.PLUGIN_ID, IStatus.OK, null, null); status.add(executeAnalysis()); if (!status.isOK()) { Activator.log(status); } TmfTraceManager.refreshSupplementaryFiles(this); } } /** * @since 3.0 */ @Override public synchronized int getCheckpointSize() { int totalCheckpointSize = 0; try { if (fTraces != null) { for (final ITmfTrace trace : fTraces) { if (!(trace instanceof ITmfPersistentlyIndexable)) { return 0; } ITmfPersistentlyIndexable persistableIndexTrace = (ITmfPersistentlyIndexable) trace; int currentTraceCheckpointSize = persistableIndexTrace.getCheckpointSize(); if (currentTraceCheckpointSize <= 0) { return 0; } totalCheckpointSize += currentTraceCheckpointSize; totalCheckpointSize += 8; // each entry in the TmfLocationArray has a rank in addition of the location } } } catch (UnsupportedOperationException e) { return 0; } return totalCheckpointSize; } /** * @since 3.0 */ @Override public ITmfLocation restoreLocation(ByteBuffer bufferIn) { ITmfLocation[] locations = new ITmfLocation[fTraces.length]; long[] ranks = new long[fTraces.length]; for (int i = 0; i < fTraces.length; ++i) { final ITmfTrace trace = fTraces[i]; locations[i] = ((ITmfPersistentlyIndexable) trace).restoreLocation(bufferIn); ranks[i] = bufferIn.getLong(); } TmfLocationArray arr = new TmfLocationArray(locations, ranks); TmfExperimentLocation l = new TmfExperimentLocation(arr); return l; } }