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
.Vector
;
17 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
18 import org
.eclipse
.core
.runtime
.IStatus
;
19 import org
.eclipse
.core
.runtime
.Status
;
20 import org
.eclipse
.core
.runtime
.jobs
.Job
;
21 import org
.eclipse
.linuxtools
.tmf
.component
.ITmfContext
;
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
.TmfSignalHandler
;
29 import org
.eclipse
.linuxtools
.tmf
.trace
.ITmfTrace
;
30 import org
.eclipse
.linuxtools
.tmf
.trace
.TmfTraceContext
;
31 import org
.eclipse
.linuxtools
.tmf
.trace
.TmfTraceUpdatedSignal
;
34 * <b><u>TmfExperiment</u></b>
36 * TmfExperiment presents a time-ordered, unified view of a set of TmfTraces
37 * that are part of a tracing experiment.
40 public class TmfExperiment
<T
extends TmfEvent
> extends TmfProvider
<T
> {
42 // TODO: Complete multi-trace experiment
43 // TODO: Add support for dynamic addition/removal of traces
44 // TODO: Add support for live streaming (notifications, incremental indexing, ...)
45 // TODO: Implement indexing-on-demand
47 // ------------------------------------------------------------------------
49 // ------------------------------------------------------------------------
51 // The currently selected experiment
52 private static TmfExperiment
<?
> fCurrentExperiment
;
55 private String fExperimentId
;
57 // The set of trace sthat constitute the experiment
58 private Vector
<ITmfTrace
> fTraces
;
60 // The total number of events
61 private int fNbEvents
;
63 // The experiment time range
64 private TmfTimeRange fTimeRange
;
66 // The experiment reference timestamp (default: BigBang)
67 private TmfTimestamp fEpoch
;
69 // ------------------------------------------------------------------------
71 // ------------------------------------------------------------------------
78 * @param indexPageSize
80 public TmfExperiment(Class
<T
> type
, String id
, ITmfTrace
[] traces
, TmfTimestamp epoch
, int indexPageSize
) {
84 fTraces
= new Vector
<ITmfTrace
>();
85 for (ITmfTrace trace
: traces
) {
89 fIndexPageSize
= indexPageSize
;
100 public TmfExperiment(Class
<T
> type
, String id
, ITmfTrace
[] traces
) {
101 this(type
, id
, traces
, TmfTimestamp
.Zero
, DEFAULT_INDEX_PAGE_SIZE
);
108 * @param indexPageSize
110 public TmfExperiment(Class
<T
> type
, String id
, ITmfTrace
[] traces
, int indexPageSize
) {
111 this(type
, id
, traces
, TmfTimestamp
.Zero
, indexPageSize
);
118 public void deregister() {
120 fCurrentExperiment
= null;
124 // ------------------------------------------------------------------------
126 // ------------------------------------------------------------------------
128 public static TmfExperiment
<?
> getCurrentExperiment() {
129 return fCurrentExperiment
;
132 public String
getExperimentId() {
133 return fExperimentId
;
136 public ITmfTrace
[] getTraces() {
137 ITmfTrace
[] result
= new ITmfTrace
[fTraces
.size()];
138 return fTraces
.toArray(result
);
141 public TmfTimestamp
getEpoch() {
145 public TmfTimeRange
getTimeRange() {
149 public int getNbEvents() {
154 * Returns the rank of the first event with the requested timestamp.
155 * If none, returns the index of the next event (if any).
160 public long getRank(TmfTimestamp ts
) {
161 // FIXME: Go over all the traces
162 ITmfTrace trace
= fTraces
.firstElement();
163 TmfTraceContext context
= trace
.seekEvent(ts
);
164 return context
.getRank();
168 * Returns the timestamp of the event at the requested index.
169 * If none, returns null.
174 public TmfTimestamp
getTimestamp(int index
) {
175 // FIXME: Go over all the traces
176 ITmfTrace trace
= fTraces
.firstElement();
177 TmfTraceContext context
= trace
.seekEvent(index
);
178 TmfEvent event
= trace
.getNextEvent(context
);
179 TmfTimestamp ts
= (event
!= null) ? event
.getTimestamp() : null;
183 // ------------------------------------------------------------------------
185 // ------------------------------------------------------------------------
188 * Add a trace to the experiment trace set
192 public void addTrace(ITmfTrace trace
) {
201 * Update the total number of events
203 private void updateNbEvents() {
205 for (ITmfTrace trace
: fTraces
) {
206 nbEvents
+= trace
.getNbEvents();
208 fNbEvents
= nbEvents
;
212 * Update the global time range
214 private void updateTimeRange() {
215 TmfTimestamp startTime
= fTimeRange
!= null ? fTimeRange
.getStartTime() : TmfTimestamp
.BigCrunch
;
216 TmfTimestamp endTime
= fTimeRange
!= null ? fTimeRange
.getEndTime() : TmfTimestamp
.BigBang
;
218 for (ITmfTrace trace
: fTraces
) {
219 TmfTimestamp traceStartTime
= trace
.getStartTime();
220 if (traceStartTime
.compareTo(startTime
, true) < 0)
221 startTime
= traceStartTime
;
223 TmfTimestamp traceEndTime
= trace
.getEndTime();
224 if (traceEndTime
.compareTo(endTime
, true) > 0)
225 endTime
= traceEndTime
;
227 fTimeRange
= new TmfTimeRange(startTime
, endTime
);
230 // ------------------------------------------------------------------------
232 // ------------------------------------------------------------------------
235 public ITmfContext
setContext(TmfDataRequest
<T
> request
) {
236 TmfExperimentContext context
= new TmfExperimentContext(fTraces
);
237 positionTraces(request
.getIndex(), context
);
241 @SuppressWarnings("unchecked")
243 public T
getNext(ITmfContext context
) {
244 if (context
instanceof TmfExperimentContext
) {
245 return (T
) getNextEvent((TmfExperimentContext
) context
);
251 public boolean isCompleted(TmfDataRequest
<T
> request
, T data
) {
252 if (request
instanceof TmfEventRequest
<?
> && data
!= null) {
253 return data
.getTimestamp().compareTo(((TmfEventRequest
<T
>) request
).getRange().getEndTime(), false) > 0;
259 * Given an experiment event index, position the set of traces so a call
260 * to getNextEvent() will retrieve the corresponding event.
265 private synchronized void positionTraces(long index
, TmfExperimentContext context
) {
267 // Extract the relevant information
268 ITmfTrace
[] traces
= context
.getTraces();
269 TmfEvent
[] events
= context
.getEvents();
270 TmfTraceContext
[] contexts
= context
.getContexts();
272 int page
= 0; // The checkpoint page
273 int current
= 0; // The current event index (rank)
275 // If there is no checkpoint created yet, start from the beginning
276 if (fExperimentIndex
.size() == 0) {
277 for (int i
= 0; i
< contexts
.length
; i
++) {
278 contexts
[i
] = traces
[i
].seekLocation(null).clone();
279 events
[i
] = traces
[i
].parseEvent(contexts
[i
]);
283 page
= (int) index
/ fIndexPageSize
;
284 if (page
>= fExperimentIndex
.size()) {
285 page
= fExperimentIndex
.size() - 1;
288 TmfTraceContext
[] checkpoint
= fExperimentIndex
.elementAt(page
).getContexts();
289 for (int i
= 0; i
< contexts
.length
; i
++) {
290 contexts
[i
] = checkpoint
[i
].clone();
291 events
[i
] = traces
[i
].parseEvent(contexts
[i
]);
293 current
= page
* fIndexPageSize
;
296 // Position the traces at the requested index
297 while (current
++ < index
) {
298 getNextEvent(context
);
303 * Scan the next events from all traces and return the next one
304 * in chronological order.
309 private TmfEvent
getNextEvent(TmfExperimentContext context
) {
310 // TODO: Consider the time adjustment
312 TmfTimestamp timestamp
= TmfTimestamp
.BigCrunch
;
313 if (context
.getEvents()[trace
] != null) {
314 timestamp
= context
.getEvents()[trace
].getTimestamp();
316 for (int i
= 1; i
< context
.getTraces().length
; i
++) {
317 if (context
.getEvents()[i
].getTimestamp() != null) {
318 TmfTimestamp otherTS
= context
.getEvents()[i
].getTimestamp();
319 if (otherTS
.compareTo(timestamp
, true) < 0) {
325 TmfEvent event
= context
.getTraces()[trace
].getNextEvent(context
.getContexts()[trace
]);
326 context
.getEvents()[trace
] = context
.getTraces()[trace
].parseEvent(context
.getContexts()[trace
]);
331 * Scan the next events from all traces and return the next one
332 * in chronological order.
337 private TmfTimestamp
getNextEventTimestamp(TmfExperimentContext context
) {
338 // TODO: Consider the time adjustment
340 TmfTimestamp timestamp
= TmfTimestamp
.BigCrunch
;
341 if (context
.getEvents()[trace
] != null) {
342 timestamp
= context
.getEvents()[trace
].getTimestamp();
344 for (int i
= 1; i
< context
.getTraces().length
; i
++) {
345 if (context
.getEvents()[i
].getTimestamp() != null) {
346 TmfTimestamp otherTS
= context
.getEvents()[i
].getTimestamp();
347 if (otherTS
.compareTo(timestamp
, true) < 0) {
357 * @see java.lang.Object#toString()
360 public String
toString() {
361 return "[TmfExperiment (" + fExperimentId
+ ")]";
364 // ------------------------------------------------------------------------
366 // ------------------------------------------------------------------------
369 * The experiment holds the globally ordered events of its set of traces.
370 * It is expected to provide access to each individual event by index i.e.
371 * it must be possible to request the nth event of the experiment.
373 * The purpose of the index is to keep the information needed to rapidly
374 * restore the traces contexts at regular intervals (every INDEX_PAGE_SIZE
378 // The index page size
379 private static final int DEFAULT_INDEX_PAGE_SIZE
= 1000;
380 private final int fIndexPageSize
;
382 // The experiment index
383 private Vector
<TmfExperimentCheckpoint
> fExperimentIndex
= new Vector
<TmfExperimentCheckpoint
>();
385 // Indicates that an indexing job is already running
386 private Boolean fIndexing
= false;
387 private Boolean fIndexed
= false;
390 private IndexingJob job
;
395 * Creates the experiment index.
397 public void indexExperiment(boolean waitForCompletion
) {
399 synchronized(fIndexing
) {
400 if (fIndexed
|| fIndexing
) {
401 // An indexing job is already running but a new request came
402 // in (probably due to a change in the trace set). The index
403 // being currently built is therefore already invalid.
404 // TODO: Cancel and restart the job
405 // TODO: Add support for dynamically adding/removing traces
411 job
= new IndexingJob(fExperimentId
);
414 if (waitForCompletion
) {
417 } catch (InterruptedException e
) {
423 private class IndexingJob
extends Job
{
425 public IndexingJob(String name
) {
430 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
433 protected IStatus
run(IProgressMonitor monitor
) {
436 if (fTraces
.size() == 0) {
438 return Status
.OK_STATUS
;
441 monitor
.beginTask("Indexing " + fExperimentId
, IProgressMonitor
.UNKNOWN
);
444 TmfTimestamp startTime
= null;
445 TmfTimestamp lastTime
= null;
447 fExperimentIndex
= new Vector
<TmfExperimentCheckpoint
>();
451 TmfExperimentContext context
= new TmfExperimentContext(fTraces
);
452 positionTraces(0, context
);
453 TmfTraceContext
[] traces
= context
.cloneContexts();
455 TmfTimestamp timestamp
= getNextEventTimestamp(context
);
456 startTime
= new TmfTimestamp(timestamp
);
457 lastTime
= new TmfTimestamp(timestamp
);
458 TmfEvent event
= getNextEvent(context
);
459 while (event
!= null) {
460 if ((nbEvents
++ % fIndexPageSize
) == 0) {
461 fExperimentIndex
.add(new TmfExperimentCheckpoint(lastTime
, traces
));
462 fNbEvents
= nbEvents
;
463 fTimeRange
= new TmfTimeRange(startTime
, lastTime
);
467 // Check monitor *after* fCheckpoints has been updated
468 if (monitor
.isCanceled()) {
470 return Status
.CANCEL_STATUS
;
474 // We will need the contexts at the next iteration
475 if ((nbEvents
% fIndexPageSize
) == 0) {
476 traces
= context
.cloneContexts();
477 lastTime
= new TmfTimestamp(event
.getTimestamp());
480 event
= getNextEvent(context
);
486 fNbEvents
= nbEvents
;
487 fTimeRange
= new TmfTimeRange(startTime
, lastTime
);
494 // dumpExperimentCheckpoints();
496 return Status
.OK_STATUS
;
500 // // ------------------------------------------------------------------------
501 // // Toubleshooting code
502 // // ------------------------------------------------------------------------
504 // private void dumpExperimentCheckpoints() {
505 // System.out.println("-----");
506 // System.out.println("Checkpoints of " + fExperimentId);
507 // for (int i = 0; i < fExperimentIndex.size(); i++) {
508 // System.out.println("Entry:" + i);
509 // TmfExperimentCheckpoint checkpoint = fExperimentIndex.get(i);
510 // TmfTraceContext[] contexts = checkpoint.getContexts();
511 // for (int j = 0; j < contexts.length; j++) {
512 // ITmfTrace trace = fTraces.get(j);
513 // TmfTraceContext context = trace.seekLocation(contexts[j].getLocation());
514 // TmfEvent event = fTraces.get(j).getNextEvent(new TmfTraceContext(context));
515 // System.out.println(" [" + trace.getName() + "] rank: " + context.getRank() + ", timestamp: " + event.getTimestamp());
516 // assert (checkpoint.getTimestamp().compareTo(event.getTimestamp(), false) == 0);
521 // ------------------------------------------------------------------------
523 // ------------------------------------------------------------------------
526 public void experimentSelected(TmfExperimentSelectedSignal signal
) {
527 fCurrentExperiment
= signal
.getExperiment();
528 // if (signal.getExperiment() == this) {
529 // indexExperiment(true);
534 public void experimentUpdated(TmfExperimentUpdatedSignal signal
) {
535 // indexExperiment(true);
539 public void traceUpdated(TmfTraceUpdatedSignal signal
) {
540 // TODO: Incremental index update
545 broadcast(new TmfExperimentUpdatedSignal(this, this, signal
.getTrace()));