1 /*******************************************************************************
2 * Copyright (c) 2009, 2010 Ericsson
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
10 * Francois Chouinard - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.linuxtools
.tmf
.experiment
;
15 import java
.util
.Collections
;
16 import java
.util
.Vector
;
18 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
19 import org
.eclipse
.core
.runtime
.IStatus
;
20 import org
.eclipse
.core
.runtime
.Status
;
21 import org
.eclipse
.core
.runtime
.jobs
.Job
;
22 import org
.eclipse
.linuxtools
.tmf
.component
.TmfProvider
;
23 import org
.eclipse
.linuxtools
.tmf
.event
.TmfEvent
;
24 import org
.eclipse
.linuxtools
.tmf
.event
.TmfTimeRange
;
25 import org
.eclipse
.linuxtools
.tmf
.event
.TmfTimestamp
;
26 import org
.eclipse
.linuxtools
.tmf
.request
.TmfDataRequest
;
27 import org
.eclipse
.linuxtools
.tmf
.request
.TmfEventRequest
;
28 import org
.eclipse
.linuxtools
.tmf
.signal
.TmfRangeSynchSignal
;
29 import org
.eclipse
.linuxtools
.tmf
.signal
.TmfSignalHandler
;
30 import org
.eclipse
.linuxtools
.tmf
.trace
.ITmfContext
;
31 import org
.eclipse
.linuxtools
.tmf
.trace
.ITmfLocation
;
32 import org
.eclipse
.linuxtools
.tmf
.trace
.ITmfTrace
;
33 import org
.eclipse
.linuxtools
.tmf
.trace
.TmfCheckpoint
;
34 import org
.eclipse
.linuxtools
.tmf
.trace
.TmfContext
;
35 import org
.eclipse
.linuxtools
.tmf
.trace
.TmfTraceUpdatedSignal
;
38 * <b><u>TmfExperiment</u></b>
40 * TmfExperiment presents a time-ordered, unified view of a set of TmfTraces
41 * that are part of a tracing experiment.
44 public class TmfExperiment
<T
extends TmfEvent
> extends TmfProvider
<T
> implements ITmfTrace
{
46 // ------------------------------------------------------------------------
48 // ------------------------------------------------------------------------
50 // The currently selected experiment
51 private static TmfExperiment
<?
> fCurrentExperiment
;
54 private String fExperimentId
;
56 // The set of traces that constitute the experiment
57 private ITmfTrace
[] fTraces
;
59 // The total number of events
60 private long fNbEvents
;
62 // The experiment time range
63 private TmfTimeRange fTimeRange
;
65 // The experiment reference timestamp (default: BigBang)
66 private TmfTimestamp fEpoch
;
68 // The experiment index
69 private Vector
<TmfCheckpoint
> fCheckpoints
= new Vector
<TmfCheckpoint
>();
71 // ------------------------------------------------------------------------
73 // ------------------------------------------------------------------------
80 * @param indexPageSize
82 public TmfExperiment(Class
<T
> type
, String id
, ITmfTrace
[] traces
, TmfTimestamp epoch
, int indexPageSize
) {
88 fIndexPageSize
= indexPageSize
;
99 public TmfExperiment(Class
<T
> type
, String id
, ITmfTrace
[] traces
) {
100 this(type
, id
, traces
, TmfTimestamp
.Zero
, DEFAULT_INDEX_PAGE_SIZE
);
107 * @param indexPageSize
109 public TmfExperiment(Class
<T
> type
, String id
, ITmfTrace
[] traces
, int indexPageSize
) {
110 this(type
, id
, traces
, TmfTimestamp
.Zero
, indexPageSize
);
117 public void deregister() {
119 fCheckpoints
.clear();
120 fCurrentExperiment
= null;
124 // ------------------------------------------------------------------------
125 // ITmfTrace accessors
126 // ------------------------------------------------------------------------
128 public String
getPath() {
132 public String
getName() {
133 return fExperimentId
;
136 public long getNbEvents() {
140 public TmfTimeRange
getTimeRange() {
144 public TmfTimestamp
getStartTime() {
145 return fTimeRange
.getStartTime();
148 public TmfTimestamp
getEndTime() {
149 return fTimeRange
.getEndTime();
152 // ------------------------------------------------------------------------
154 // ------------------------------------------------------------------------
156 public static TmfExperiment
<?
> getCurrentExperiment() {
157 return fCurrentExperiment
;
160 public TmfTimestamp
getEpoch() {
164 public ITmfTrace
[] getTraces() {
169 * Returns the rank of the first event with the requested timestamp.
170 * If none, returns the index of the next event (if any).
175 public long getRank(TmfTimestamp ts
) {
176 // FIXME: Go over all the traces
177 ITmfTrace trace
= fTraces
[0];
178 TmfContext context
= trace
.seekEvent(ts
);
179 return context
.getRank();
183 * Returns the timestamp of the event at the requested index.
184 * If none, returns null.
189 public TmfTimestamp
getTimestamp(int index
) {
190 // FIXME: Go over all the traces
191 ITmfTrace trace
= fTraces
[0];
192 TmfContext context
= trace
.seekEvent(index
);
193 TmfEvent event
= trace
.getNextEvent(context
);
194 TmfTimestamp timestamp
= (event
!= null) ? event
.getTimestamp() : null;
198 // ------------------------------------------------------------------------
200 // ------------------------------------------------------------------------
203 // * Add a trace to the experiment trace set
207 // public void addTrace(ITmfTrace trace) {
208 // fTraces.add(trace);
209 // synchronized(this) {
211 // updateTimeRange();
216 * Update the total number of events
218 private void updateNbEvents() {
220 for (ITmfTrace trace
: fTraces
) {
221 nbEvents
+= trace
.getNbEvents();
223 fNbEvents
= nbEvents
;
227 * Update the global time range
229 private void updateTimeRange() {
230 TmfTimestamp startTime
= fTimeRange
!= null ? fTimeRange
.getStartTime() : TmfTimestamp
.BigCrunch
;
231 TmfTimestamp endTime
= fTimeRange
!= null ? fTimeRange
.getEndTime() : TmfTimestamp
.BigBang
;
233 for (ITmfTrace trace
: fTraces
) {
234 TmfTimestamp traceStartTime
= trace
.getStartTime();
235 if (traceStartTime
.compareTo(startTime
, true) < 0)
236 startTime
= traceStartTime
;
238 TmfTimestamp traceEndTime
= trace
.getEndTime();
239 if (traceEndTime
.compareTo(endTime
, true) > 0)
240 endTime
= traceEndTime
;
242 fTimeRange
= new TmfTimeRange(startTime
, endTime
);
245 // ------------------------------------------------------------------------
247 // ------------------------------------------------------------------------
250 public ITmfContext
setContext(TmfDataRequest
<T
> request
) {
251 TmfTimestamp timestamp
= (request
instanceof TmfEventRequest
<?
>) ?
252 ((TmfEventRequest
<T
>) request
).getRange().getStartTime() : null;
254 TmfExperimentContext context
= (timestamp
!= null) ?
255 seekEvent(timestamp
) : seekEvent(request
.getIndex());
260 @SuppressWarnings("unchecked")
262 public T
getNext(ITmfContext context
) {
263 if (context
instanceof TmfExperimentContext
) {
264 return (T
) getNextEvent((TmfExperimentContext
) context
);
270 public boolean isCompleted(TmfDataRequest
<T
> request
, T data
) {
271 if (request
instanceof TmfEventRequest
<?
> && data
!= null) {
272 return data
.getTimestamp().compareTo(((TmfEventRequest
<T
>) request
).getRange().getEndTime(), false) > 0;
277 // ------------------------------------------------------------------------
278 // ITmfTrace trace positioning
279 // ------------------------------------------------------------------------
281 // Returns a brand new context based on the location provided
282 // Arms the event queues
283 // NOTE: This is a fine example of pathological coupling...
284 public TmfExperimentContext
seekLocation(ITmfLocation
<?
> location
) {
285 if (location
instanceof TmfExperimentLocation
|| location
== null) {
286 ITmfLocation
<?
>[] oldloc
= (location
!= null) ?
((TmfExperimentLocation
) location
).getLocation() : new TmfExperimentLocation
[fTraces
.length
];
287 ITmfLocation
<?
>[] newloc
= new ITmfLocation
[fTraces
.length
];
288 TmfContext
[] contexts
= new TmfContext
[fTraces
.length
];
290 TmfExperimentContext context
= new TmfExperimentContext(fTraces
, contexts
);
291 TmfEvent
[] events
= context
.getEvents();
294 for (int i
= 0; i
< fTraces
.length
; i
++) {
295 contexts
[i
] = fTraces
[i
].seekLocation(oldloc
[i
]);
296 newloc
[i
] = contexts
[i
].getLocation(); // No clone here
297 events
[i
] = fTraces
[i
].parseEvent(contexts
[i
]);
298 rank
+= contexts
[i
].getRank();
300 context
.setLocation(new TmfExperimentLocation(newloc
));
301 context
.setRank(rank
);
307 public TmfExperimentContext
seekEvent(TmfTimestamp timestamp
) {
309 if (timestamp
== null) {
310 timestamp
= TmfTimestamp
.BigBang
;
313 // First, find the right checkpoint
314 int index
= Collections
.binarySearch(fCheckpoints
, new TmfCheckpoint(timestamp
, null));
316 // In the very likely case that the checkpoint was not found, bsearch
317 // returns its negated would-be location (not an offset...). From that
318 // index, we can then position the stream and get the event.
320 index
= Math
.max(0, -(index
+ 2));
323 // Position the experiment at the checkpoint
324 ITmfLocation
<?
> location
;
325 synchronized (fCheckpoints
) {
326 if (fCheckpoints
.size() > 0) {
327 if (index
>= fCheckpoints
.size()) {
328 index
= fCheckpoints
.size() - 1;
330 location
= fCheckpoints
.elementAt(index
).getLocation();
337 TmfExperimentContext nextEventContext
= seekLocation(location
);
338 nextEventContext
.setRank(index
* fIndexPageSize
);
339 TmfExperimentContext currentEventContext
= new TmfExperimentContext(nextEventContext
);
342 TmfEvent event
= getNextEvent(nextEventContext
);
343 while (event
!= null && event
.getTimestamp().compareTo(timestamp
, false) < 0) {
344 currentEventContext
= new TmfExperimentContext(nextEventContext
);
345 // currentEventContext.setLocation(nextEventContext.getLocation());
346 // currentEventContext.updateRank(1);
347 event
= getNextEvent(nextEventContext
);
350 return currentEventContext
;
353 public TmfExperimentContext
seekEvent(long rank
) {
355 TmfExperimentContext context
;
356 int page
= 0; // The checkpoint page
357 int current
= 0; // The current event index (rank)
359 // If there is no checkpoint created yet, start from the beginning
360 if (fCheckpoints
.size() == 0) {
361 context
= seekLocation(null);
364 page
= (int) rank
/ fIndexPageSize
;
365 if (page
>= fCheckpoints
.size()) {
366 page
= fCheckpoints
.size() - 1;
368 context
= seekLocation(fCheckpoints
.elementAt(page
).getLocation());
369 current
= page
* fIndexPageSize
;
372 // Position the traces at the requested index
373 while (current
++ < rank
) {
374 getNextEvent(context
);
381 // * Given an experiment event index, position the set of traces so a call
382 // * to getNextEvent() will retrieve the corresponding event.
387 // private synchronized void positionTraces(long index, TmfExperimentContext context) {
389 // // Extract the relevant information
390 // ITmfTrace[] traces = context.getTraces();
391 // TmfEvent[] events = context.getEvents();
392 // TmfContext[] contexts = context.getContexts();
394 // int page = 0; // The checkpoint page
395 // int current = 0; // The current event index (rank)
397 // // If there is no checkpoint created yet, start from the beginning
398 // if (fCheckpoints.size() == 0) {
399 // for (int i = 0; i < contexts.length; i++) {
400 // contexts[i] = traces[i].seekLocation(null).clone();
401 // events[i] = traces[i].parseEvent(contexts[i]);
405 // page = (int) index / fIndexPageSize;
406 // if (page >= fCheckpoints.size()) {
407 // page = fCheckpoints.size() - 1;
410 //// TmfContext[] checkpoint = fCheckpoints.elementAt(page).getContexts();
411 // for (int i = 0; i < contexts.length; i++) {
412 // contexts[i] = checkpoint[i].clone();
413 // events[i] = traces[i].parseEvent(contexts[i]);
415 // current = page * fIndexPageSize;
418 // // Position the traces at the requested index
419 // while (current++ < index) {
420 // getNextEvent(context);
425 * Scan the next events from all traces and return the next one
426 * in chronological order.
431 public synchronized TmfEvent
getNextEvent(TmfContext context
) {
432 if (context
instanceof TmfExperimentContext
) {
433 TmfExperimentContext expContext
= (TmfExperimentContext
) context
;
435 TmfTimestamp timestamp
= TmfTimestamp
.BigCrunch
;
436 if (expContext
.getEvents()[trace
] != null) {
437 timestamp
= expContext
.getEvents()[trace
].getTimestamp();
439 for (int i
= 1; i
< expContext
.getTraces().length
; i
++) {
440 if (expContext
.getEvents()[i
].getTimestamp() != null) {
441 TmfTimestamp otherTS
= expContext
.getEvents()[i
].getTimestamp();
442 if (otherTS
.compareTo(timestamp
, true) < 0) {
448 TmfContext trcloc
= expContext
.getContexts()[trace
];
449 TmfEvent event
= expContext
.getTraces()[trace
].parseEvent(trcloc
);
450 TmfExperimentLocation exploc
= (TmfExperimentLocation
) expContext
.getLocation();
451 exploc
.getLocation()[trace
] = trcloc
.getLocation().clone();
452 expContext
.updateRank(1);
453 expContext
.getEvents()[trace
] = expContext
.getTraces()[trace
].getNextEvent(trcloc
);
459 // public TmfEvent getNextEvent(TmfExperimentContext context) {
460 // // TODO: Consider the time adjustment
462 // TmfTimestamp timestamp = TmfTimestamp.BigCrunch;
463 // if (context.getEvents()[trace] != null) {
464 // timestamp = context.getEvents()[trace].getTimestamp();
466 // for (int i = 1; i < context.getTraces().length; i++) {
467 // if (context.getEvents()[i].getTimestamp() != null) {
468 // TmfTimestamp otherTS = context.getEvents()[i].getTimestamp();
469 // if (otherTS.compareTo(timestamp, true) < 0) {
471 // timestamp = otherTS;
475 // TmfEvent event = context.getTraces()[trace].getNextEvent(context.getContexts()[trace]);
476 // context.getEvents()[trace] = context.getTraces()[trace].parseEvent(context.getContexts()[trace]);
480 public TmfEvent
parseEvent(TmfContext context
) {
481 // TODO Auto-generated method stub
486 // * Scan the next events from all traces and return the next one
487 // * in chronological order.
492 // private TmfTimestamp getNextEventTimestamp(TmfExperimentContext context) {
493 // // TODO: Consider the time adjustment
495 // TmfTimestamp timestamp = TmfTimestamp.BigCrunch;
496 // if (context.getEvents()[trace] != null) {
497 // timestamp = context.getEvents()[trace].getTimestamp();
499 // for (int i = 1; i < context.getTraces().length; i++) {
500 // if (context.getEvents()[i].getTimestamp() != null) {
501 // TmfTimestamp otherTS = context.getEvents()[i].getTimestamp();
502 // if (otherTS.compareTo(timestamp, true) < 0) {
504 // timestamp = otherTS;
512 * @see java.lang.Object#toString()
515 public String
toString() {
516 return "[TmfExperiment (" + fExperimentId
+ ")]";
519 // ------------------------------------------------------------------------
521 // ------------------------------------------------------------------------
524 * The experiment holds the globally ordered events of its set of traces.
525 * It is expected to provide access to each individual event by index i.e.
526 * it must be possible to request the Nth event of the experiment.
528 * The purpose of the index is to keep the information needed to rapidly
529 * restore the traces contexts at regular intervals (every INDEX_PAGE_SIZE
533 // The index page size
534 private static final int DEFAULT_INDEX_PAGE_SIZE
= 1000;
535 private final int fIndexPageSize
;
537 // Indicates that an indexing job is already running
538 private Boolean fIndexing
= false;
539 private Boolean fIndexed
= false;
542 private IndexingJob job
;
547 * Creates the experiment index.
549 public void indexExperiment(boolean waitForCompletion
) {
551 synchronized(fIndexing
) {
552 if (fIndexed
|| fIndexing
) {
553 // An indexing job is already running but a new request came
554 // in (probably due to a change in the trace set). The index
555 // being currently built is therefore already invalid.
556 // TODO: Cancel and restart the job
557 // TODO: Add support for dynamically adding/removing traces
563 job
= new IndexingJob(fExperimentId
);
566 if (waitForCompletion
) {
569 } catch (InterruptedException e
) {
575 private class IndexingJob
extends Job
{
577 public IndexingJob(String name
) {
582 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
585 protected IStatus
run(IProgressMonitor monitor
) {
588 if (fTraces
.length
== 0) {
590 return Status
.OK_STATUS
;
593 monitor
.beginTask("Indexing " + fExperimentId
, IProgressMonitor
.UNKNOWN
);
596 TmfTimestamp startTime
= null;
597 TmfTimestamp lastTime
= null;
600 fCheckpoints
= new Vector
<TmfCheckpoint
>();
603 // Position the trace at the beginning
604 TmfExperimentContext context
= seekLocation(null);
605 TmfExperimentLocation location
= (TmfExperimentLocation
) context
.getLocation();
607 // Get the first event
608 TmfEvent event
= getNextEvent(context
);
610 startTime
= new TmfTimestamp(event
.getTimestamp());
613 // Index the experiment
614 while (event
!= null) {
615 lastTime
= event
.getTimestamp();
616 if ((nbEvents
++ % fIndexPageSize
) == 0) {
617 fCheckpoints
.add(new TmfCheckpoint(lastTime
, location
.clone()));
618 fNbEvents
= nbEvents
;
619 fTimeRange
= new TmfTimeRange(startTime
, lastTime
);
620 notifyListeners(new TmfTimeRange(startTime
, lastTime
));
624 // Check monitor *after* fCheckpoints has been updated
625 if (monitor
.isCanceled()) {
627 return Status
.CANCEL_STATUS
;
631 // We will need the contexts at the next iteration
632 if ((nbEvents
% fIndexPageSize
) == 0) {
633 location
= (TmfExperimentLocation
) context
.getLocation();
636 event
= getNextEvent(context
);
642 fNbEvents
= nbEvents
;
643 fTimeRange
= new TmfTimeRange(startTime
, lastTime
);
650 // dumpCheckpoints();
652 return Status
.OK_STATUS
;
656 protected void notifyListeners(TmfTimeRange range
) {
657 broadcast(new TmfRangeSynchSignal(this, range
, null));
660 // ========================================================================
661 // Toubleshooting code
662 // ========================================================================
664 // private void dumpCheckpoints() {
665 // System.out.println("-----");
666 // System.out.println("Checkpoints of " + fExperimentId);
667 // for (int i = 0; i < fCheckpoints.size(); i++) {
668 // System.out.println("Entry:" + i);
669 // TmfCheckpoint checkpoint = fCheckpoints.get(i);
671 // for (int j = 0; j < fTraces.length; j++) {
672 // ITmfTrace trace = fTraces[j];
673 // TmfExperimentContext context = seekLocation(checkpoint.getLocation());
674 // TmfContext[] traces = context.getContexts();
675 // rank += context.getRank();
676 // TmfEvent event = fTraces[j].getNextEvent(new TmfContext(traces[j]));
677 // System.out.println(" [" + trace.getName() + "] rank: " + context.getRank() + ", timestamp: " + event.getTimestamp());
678 // assert (checkpoint.getTimestamp().compareTo(event.getTimestamp(), false) == 0);
680 // System.out.println("Sum of ranks: " + rank + " (expected: " + i * fIndexPageSize + ")");
684 // ------------------------------------------------------------------------
686 // ------------------------------------------------------------------------
689 public void experimentSelected(TmfExperimentSelectedSignal signal
) {
690 fCurrentExperiment
= signal
.getExperiment();
691 // if (signal.getExperiment() == this) {
692 // indexExperiment(true);
697 public void experimentUpdated(TmfExperimentUpdatedSignal signal
) {
698 // indexExperiment(true);
702 public void traceUpdated(TmfTraceUpdatedSignal signal
) {
703 // TODO: Incremental index update
708 broadcast(new TmfExperimentUpdatedSignal(this, this, signal
.getTrace()));