1 /*******************************************************************************
2 * Copyright (c) 2009 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
.trace
;
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
.TmfComponent
;
22 import org
.eclipse
.linuxtools
.tmf
.event
.TmfEvent
;
23 import org
.eclipse
.linuxtools
.tmf
.event
.TmfTimeRange
;
24 import org
.eclipse
.linuxtools
.tmf
.event
.TmfTimestamp
;
25 import org
.eclipse
.linuxtools
.tmf
.request
.ITmfRequestHandler
;
26 import org
.eclipse
.linuxtools
.tmf
.request
.TmfDataRequest
;
27 import org
.eclipse
.linuxtools
.tmf
.signal
.TmfSignalHandler
;
30 * <b><u>TmfExperiment</u></b>
32 * TmfExperiment presents a time-ordered, unified view of a set of TmfTraces
33 * that are part of a tracing experiment.
36 public class TmfExperiment
extends TmfComponent
implements ITmfRequestHandler
<TmfEvent
> {
38 // TODO: Complete multi-trace experiment
39 // TODO: Add support for dynamic addition/removal of traces
40 // TODO: Add support for live streaming (notifications, incremental indexing, ...)
42 // ========================================================================
44 // ========================================================================
46 // The currently selected experiment
47 private static TmfExperiment fCurrentExperiment
;
50 private String fExperimentId
;
52 // The set of trace sthat constitute the experiment
53 private Vector
<ITmfTrace
> fTraces
;
55 // The total number of events
56 private int fNbEvents
;
58 // The experiment time range
59 private TmfTimeRange fTimeRange
;
61 // The experiment reference timestamp (default: BigBang)
62 private TmfTimestamp fEpoch
;
64 // // Indicates if the stream should be indexed synchronously (default: false)
65 // private final boolean fWaitForIndexCompletion;
67 // ========================================================================
68 // Constructors/Destructor
69 // ========================================================================
75 public TmfExperiment(String id
, ITmfTrace
[] traces
) {
76 this(id
, traces
, TmfTimestamp
.BigBang
, DEFAULT_INDEX_PAGE_SIZE
, false);
82 * @param waitForIndexCompletion
84 public TmfExperiment(String id
, ITmfTrace
[] traces
, boolean waitForIndexCompletion
) {
85 this(id
, traces
, TmfTimestamp
.BigBang
, DEFAULT_INDEX_PAGE_SIZE
, waitForIndexCompletion
);
92 * @param waitForIndexCompletion
94 public TmfExperiment(String id
, ITmfTrace
[] traces
, TmfTimestamp epoch
, int indexPageSize
, boolean waitForIndexCompletion
) {
98 fTraces
= new Vector
<ITmfTrace
>();
99 for (ITmfTrace trace
: traces
) {
103 fIndexPageSize
= indexPageSize
;
104 // fWaitForIndexCompletion = waitForIndexCompletion;
108 // indexExperiment();
115 public void dispose() {
118 fCurrentExperiment
= null;
121 // ========================================================================
123 // ========================================================================
125 public static TmfExperiment
getCurrentExperiment() {
126 return fCurrentExperiment
;
129 public String
getExperimentId() {
130 return fExperimentId
;
133 public ITmfTrace
[] getTraces() {
134 ITmfTrace
[] result
= new ITmfTrace
[fTraces
.size()];
135 return fTraces
.toArray(result
);
138 public TmfTimestamp
getEpoch() {
142 public TmfTimeRange
getTimeRange() {
146 public int getNbEvents() {
151 * Returns the index of the first event with the requested timestamp.
152 * If none, returns the index of the next event (if any).
157 public long getIndex(TmfTimestamp ts
) {
158 // TODO: Go over all the traces
159 ITmfTrace trace
= fTraces
.firstElement();
160 TmfTraceContext context
= trace
.seekEvent(ts
);
161 return context
.getIndex();
165 * Returns the timestamp of the event at the requested index.
166 * If none, returns null.
171 public TmfTimestamp
getTimestamp(int index
) {
172 // TODO: Go over all the traces
173 ITmfTrace trace
= fTraces
.firstElement();
174 TmfTraceContext context
= trace
.seekEvent(index
);
175 return context
.getTimestamp();
178 // ========================================================================
180 // ========================================================================
183 * Add a trace to the experiment trace set
187 public void addTrace(ITmfTrace trace
) {
196 * Update the total number of events
198 private void updateNbEvents() {
200 for (ITmfTrace trace
: fTraces
) {
201 nbEvents
+= trace
.getNbEvents();
203 fNbEvents
= nbEvents
;
207 * Update the global time range
209 private void updateTimeRange() {
210 TmfTimestamp startTime
= fTimeRange
!= null ? fTimeRange
.getStartTime() : TmfTimestamp
.BigCrunch
;
211 TmfTimestamp endTime
= fTimeRange
!= null ? fTimeRange
.getEndTime() : TmfTimestamp
.BigBang
;
213 for (ITmfTrace trace
: fTraces
) {
214 TmfTimestamp traceStartTime
= trace
.getTimeRange().getStartTime();
215 if (traceStartTime
.compareTo(startTime
, true) < 0)
216 startTime
= traceStartTime
;
218 TmfTimestamp traceEndTime
= trace
.getTimeRange().getEndTime();
219 if (traceEndTime
.compareTo(endTime
, true) > 0)
220 endTime
= traceEndTime
;
222 fTimeRange
= new TmfTimeRange(startTime
, endTime
);
225 // ========================================================================
226 // ITmfRequestHandler
227 // ========================================================================
230 * @see org.eclipse.linuxtools.tmf.request.ITmfRequestHandler#processRequest(org.eclipse.linuxtools.tmf.request.TmfDataRequest, boolean)
232 public void processRequest(TmfDataRequest
<TmfEvent
> request
, boolean waitForCompletion
) {
234 // Process the request
235 processDataRequest(request
);
237 // Wait for completion if needed
238 if (waitForCompletion
) {
239 request
.waitForCompletion();
244 * Process a data request
248 private void processDataRequest(final TmfDataRequest
<TmfEvent
> request
) {
250 // General request parameters
251 final TmfTimestamp endTime
;
254 // Initialize request params depending on request type
255 if (request
.getRange() != null) {
256 index
= getIndex(request
.getRange().getStartTime());
257 endTime
= request
.getRange().getEndTime();
259 index
= request
.getIndex();
260 endTime
= TmfTimestamp
.BigCrunch
;
263 // Process the request
264 Thread thread
= new Thread() {
270 ITmfTrace
[] traces
= new ITmfTrace
[0]; // The set of traces
271 TmfTraceContext
[] contexts
; // The set of trace contexts
273 // Extract the general request information
274 int blockSize
= request
.getBlockize();
275 int nbRequestedEvents
= request
.getNbRequestedEvents();
276 if (nbRequestedEvents
== -1) {
277 nbRequestedEvents
= Integer
.MAX_VALUE
;
280 // Create the result buffer
281 Vector
<TmfEvent
> events
= new Vector
<TmfEvent
>();
284 // Initialize the traces array and position the traces
285 // at the first requested event
286 traces
= fTraces
.toArray(traces
);
287 contexts
= new TmfTraceContext
[traces
.length
];
288 positionTraces(index
, traces
, contexts
);
290 // Get the ordered events
291 TmfEvent event
= getNextEvent(traces
, contexts
);
292 while (!request
.isCancelled() && nbEvents
< nbRequestedEvents
&& event
!= null
293 && event
.getTimestamp().compareTo(endTime
, false) < 0)
296 if (++nbEvents
% blockSize
== 0) {
297 pushData(request
, events
);
299 // Avoid an unnecessary read passed the last event requested
300 if (nbEvents
< nbRequestedEvents
)
301 event
= getNextEvent(traces
, contexts
);
304 if (!request
.isCancelled() && !request
.isFailed()) {
305 pushData(request
, events
);
314 * Given an experiment event index, position the set of traces so a call
315 * to getNextEvent() will retrieve the corresponding event.
322 private synchronized void positionTraces(long index
, ITmfTrace
[] traces
, TmfTraceContext
[] contexts
) {
324 // Compute the index page and corresponding index
325 int page
= (int) index
/ fIndexPageSize
;
326 int current
= page
* fIndexPageSize
;
328 // Retrieve the checkpoint and set the contexts (make copies)
329 TmfTraceContext
[] saveContexts
= new TmfTraceContext
[contexts
.length
];
330 if (page
< fExperimentIndex
.size()) {
331 saveContexts
= fExperimentIndex
.elementAt(page
);
332 for (int i
= 0; i
< contexts
.length
; i
++) {
333 contexts
[i
] = new TmfTraceContext(saveContexts
[i
]);
336 // If the page entry doesn't exist (e.g. indexing not completed),
337 // set contexts at the the last entry (if it exists)
338 page
= fExperimentIndex
.size() - 1;
340 saveContexts
= fExperimentIndex
.elementAt(page
);
341 for (int i
= 0; i
< contexts
.length
; i
++) {
342 contexts
[i
] = new TmfTraceContext(saveContexts
[i
]);
344 current
= page
* fIndexPageSize
;
346 // Index is empty... position traces at their beginning
348 for (int i
= 0; i
< contexts
.length
; i
++) {
349 contexts
[i
] = new TmfTraceContext(traces
[i
].seekLocation(null));
355 // Position the traces at the requested index
356 while (current
++ < index
) {
357 getNextEvent(traces
, contexts
);
362 * Scan the next events from all traces and return the next one
363 * in chronological order.
370 private TmfEvent
getNextEvent(ITmfTrace
[] traces
, TmfTraceContext
[] contexts
) {
371 // TODO: Consider the time adjustment
373 TmfTimestamp timestamp
= contexts
[trace
].getTimestamp();
374 if (timestamp
== null) {
375 timestamp
= TmfTimestamp
.BigCrunch
;
377 for (int i
= 1; i
< traces
.length
; i
++) {
378 if (contexts
[i
].getTimestamp() != null) {
379 TmfTimestamp otherTS
= contexts
[i
].getTimestamp();
380 if (otherTS
.compareTo(timestamp
, true) < 0) {
386 TmfEvent event
= traces
[trace
].getNextEvent(contexts
[trace
]);
391 * Format the result data and notify the requester.
392 * Note: after handling, the data is *removed*.
397 private void pushData(TmfDataRequest
<TmfEvent
> request
, Vector
<TmfEvent
> events
) {
398 TmfEvent
[] result
= new TmfEvent
[events
.size()];
399 events
.toArray(result
);
400 request
.setData(result
);
401 request
.handleData();
402 events
.removeAllElements();
406 * @see java.lang.Object#toString()
409 public String
toString() {
410 return "[TmfExperiment (" + fExperimentId
+ ")]";
413 // ========================================================================
415 // ========================================================================
418 * The experiment holds the globally ordered events of its set of traces.
419 * It is expected to provide access to each individual event by index i.e.
420 * it must be possible to request the Nth event of the experiment.
422 * The purpose of the index is to keep the information needed to rapidly
423 * restore the traces contexts at regular intervals (every INDEX_PAGE_SIZE
427 // The index page size
428 private static final int DEFAULT_INDEX_PAGE_SIZE
= 1000;
429 private final int fIndexPageSize
;
431 // The experiment index
432 private Vector
<TmfTraceContext
[]> fExperimentIndex
= new Vector
<TmfTraceContext
[]>();
434 // Indicates that an indexing job is already running
435 private Boolean fIndexing
= false;
438 private IndexingJob job
;
443 * Creates the experiment index.
445 private void indexExperiment() {
447 synchronized(fIndexing
) {
449 // An indexing job is already running but a new request came
450 // in (probably due to a change in the trace set). The index
451 // being currently built is therefore already invalid.
452 // TODO: Cancel and restart the job
453 // TODO: Add support for dynamically adding/removing traces
459 job
= new IndexingJob(fExperimentId
);
463 } catch (InterruptedException e
) {
464 // TODO Auto-generated catch block
467 // if (fWaitForIndexCompletion) {
468 // ProgressMonitorDialog dialog = new ProgressMonitorDialog(null);
470 // // TODO: Handle cancel!
471 // dialog.run(true, true, new IRunnableWithProgress() {
472 // public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
473 // monitor.beginTask("Indexing " + fExperimentId, IProgressMonitor.UNKNOWN);
478 // } catch (InvocationTargetException e) {
479 // e.printStackTrace();
480 // } catch (InterruptedException e) {
481 // e.printStackTrace();
486 private class IndexingJob
extends Job
{
488 public IndexingJob(String name
) {
493 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
496 protected IStatus
run(IProgressMonitor monitor
) {
498 Vector
<TmfTraceContext
[]> indices
= new Vector
<TmfTraceContext
[]>();
501 if (fTraces
.size() == 0) {
503 return Status
.OK_STATUS
;
506 monitor
.beginTask("Indexing " + fExperimentId
, IProgressMonitor
.UNKNOWN
);
508 ITmfTrace
[] traces
= new ITmfTrace
[0];
509 TmfTraceContext
[] contexts
;
510 int nbTraces
= fTraces
.size();
512 // Initialize the traces and contexts arrays
513 traces
= fTraces
.toArray(traces
);
514 contexts
= new TmfTraceContext
[nbTraces
];
515 TmfTraceContext
[] savedContexts
= new TmfTraceContext
[nbTraces
];
517 for (int i
= 0; i
< nbTraces
; i
++) {
518 // Context of the first event of the trace
519 contexts
[i
] = traces
[i
].seekLocation(null);
520 savedContexts
[i
] = new TmfTraceContext(contexts
[i
].getLocation(), contexts
[i
].getTimestamp(), 0);
521 if (contexts
[i
].getTimestamp() == null)
524 // Check if there is anything to index
525 if (nullEvents
>= nbTraces
) {
527 return Status
.OK_STATUS
;
529 // FIXME: LTTng hack - start
530 // indices.add(savedContexts); // TMF
531 // FIXME: LTTng hack - end
533 // Get the ordered events and populate the indices
534 // FIXME: LTTng hack - start
535 // int nbEvents = 0; // TMF
536 int nbEvents
= -1; // LTTng
537 // FIXME: LTTng hack - end
538 while ((getNextEvent(traces
, contexts
)) != null)
540 if (++nbEvents
% fIndexPageSize
== 0) {
541 // Special case: if the total number of events is a multiple of the
542 // DEFAULT_PAGE_SIZE then all the pending events are null. In that
543 // case, we don't store an additional entry in the index array.
545 savedContexts
= new TmfTraceContext
[nbTraces
];
546 for (int i
= 0; i
< nbTraces
; i
++) {
547 savedContexts
[i
] = new TmfTraceContext(contexts
[i
]);
548 if (contexts
[i
].getTimestamp() == null)
551 if (nullEvents
< nbTraces
) {
552 indices
.add(savedContexts
);
557 if (monitor
.isCanceled()) {
559 return Status
.CANCEL_STATUS
;
564 fExperimentIndex
= indices
;
569 return Status
.OK_STATUS
;
574 // * Dump the experiment index
576 // private void dumpIndex() {
577 // System.out.println("-----");
578 // System.out.println("Index of " + fExperimentId);
579 // for (int i = 0; i < fExperimentIndex.size(); i++) {
580 // System.out.println("Entry:" + i);
581 // TmfTraceContext[] contexts = fExperimentIndex.get(i);
583 // for (int j = 0; j < contexts.length; j++) {
584 // ITmfTrace trace = fTraces.get(j);
585 // TmfTraceContext context = trace.seekLocation(contexts[j].getLocation());
586 // TmfEvent event = fTraces.get(j).getNextEvent(new TmfTraceContext(context));
587 // nbEvents += contexts[j].getIndex();
588 // System.out.println(" [" + trace.getName() + "]"
589 // + " index: " + contexts[j].getIndex()
590 // + ", timestamp: " + contexts[j].getTimestamp()
591 // + ", event: " + event.getTimestamp());
592 // assert (contexts[j].getTimestamp().compareTo(event.getTimestamp(), false) == 0);
594 // assert ((i+1) * fIndexPageSize == nbEvents);
599 // ========================================================================
601 // ========================================================================
604 public void experimentSelected(TmfExperimentSelectedSignal signal
) {
605 fCurrentExperiment
= this;
610 public void experimentUpdated(TmfExperimentUpdatedSignal signal
) {
611 // indexExperiment();
615 public void traceUpdated(TmfTraceUpdatedSignal signal
) {
616 // TODO: Incremental index update
621 broadcastSignal(new TmfExperimentUpdatedSignal(this, this, signal
.getTrace()));