(no commit message)
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf / src / org / eclipse / linuxtools / tmf / experiment / TmfExperiment.java
CommitLineData
8c8bf09f
ASL
1/*******************************************************************************
2 * Copyright (c) 2009, 2010 Ericsson
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 *******************************************************************************/
12
13package org.eclipse.linuxtools.tmf.experiment;
14
15import java.util.Collections;
16import java.util.Vector;
17
18import org.eclipse.core.runtime.IProgressMonitor;
19import org.eclipse.core.runtime.IStatus;
20import org.eclipse.core.runtime.Status;
21import org.eclipse.core.runtime.jobs.Job;
22import org.eclipse.linuxtools.tmf.component.TmfEventProvider;
23import org.eclipse.linuxtools.tmf.event.TmfEvent;
24import org.eclipse.linuxtools.tmf.event.TmfTimeRange;
25import org.eclipse.linuxtools.tmf.event.TmfTimestamp;
26import org.eclipse.linuxtools.tmf.request.ITmfDataRequest;
27import org.eclipse.linuxtools.tmf.request.ITmfEventRequest;
28import org.eclipse.linuxtools.tmf.signal.TmfExperimentSelectedSignal;
29import org.eclipse.linuxtools.tmf.signal.TmfExperimentUpdatedSignal;
30import org.eclipse.linuxtools.tmf.signal.TmfRangeSynchSignal;
31import org.eclipse.linuxtools.tmf.signal.TmfSignalHandler;
32import org.eclipse.linuxtools.tmf.signal.TmfTraceUpdatedSignal;
33import org.eclipse.linuxtools.tmf.trace.ITmfContext;
34import org.eclipse.linuxtools.tmf.trace.ITmfLocation;
35import org.eclipse.linuxtools.tmf.trace.ITmfTrace;
36import org.eclipse.linuxtools.tmf.trace.TmfCheckpoint;
37import org.eclipse.linuxtools.tmf.trace.TmfContext;
38
39/**
40 * <b><u>TmfExperiment</u></b>
41 * <p>
42 * TmfExperiment presents a time-ordered, unified view of a set of TmfTraces
43 * that are part of a tracing experiment.
44 * <p>
45 */
46public class TmfExperiment<T extends TmfEvent> extends TmfEventProvider<T> implements ITmfTrace {
47
48 // ------------------------------------------------------------------------
49 // Attributes
50 // ------------------------------------------------------------------------
51
52 // The currently selected experiment
53 private static TmfExperiment<?> fCurrentExperiment = null;
54
55 // The set of traces that constitute the experiment
56 private ITmfTrace[] fTraces;
57
58 // The total number of events
59 private long fNbEvents;
60
61 // The experiment time range
62 private TmfTimeRange fTimeRange;
63
64 // The experiment reference timestamp (default: BigBang)
65 private TmfTimestamp fEpoch;
66
67 // The experiment index
68 private Vector<TmfCheckpoint> fCheckpoints = new Vector<TmfCheckpoint>();
69
70 // ------------------------------------------------------------------------
71 // Constructors
72 // ------------------------------------------------------------------------
73
74 /**
75 * @param type
76 * @param id
77 * @param traces
78 * @param epoch
79 * @param indexPageSize
80 */
81 public TmfExperiment(Class<T> type, String id, ITmfTrace[] traces, TmfTimestamp epoch, int indexPageSize) {
82 super(id, type);
83
84 fTraces = traces;
85 fEpoch = epoch;
86 fIndexPageSize = indexPageSize;
87
88 updateNbEvents();
89 updateTimeRange();
90 }
91
92 /**
93 * @param type
94 * @param id
95 * @param traces
96 */
97 public TmfExperiment(Class<T> type, String id, ITmfTrace[] traces) {
98 this(type, id, traces, TmfTimestamp.Zero, DEFAULT_INDEX_PAGE_SIZE);
99 }
100
101 /**
102 * @param type
103 * @param id
104 * @param traces
105 * @param indexPageSize
106 */
107 public TmfExperiment(Class<T> type, String id, ITmfTrace[] traces, int indexPageSize) {
108 this(type, id, traces, TmfTimestamp.Zero, indexPageSize);
109 }
110
111
112 public TmfExperiment(TmfExperiment<T> other) {
113 super(other.getName(), other.fType);
114
115 fEpoch = other.fEpoch;
116 fIndexPageSize = other.fIndexPageSize;
117
118 fTraces = new ITmfTrace[other.fTraces.length];
119 for (int trace = 0; trace < other.fTraces.length; trace++) {
120 fTraces[trace] = other.fTraces[trace].createTraceCopy();
121 }
122
123 fNbEvents = other.fNbEvents;
124 fTimeRange = other.fTimeRange;
125 }
126
127 public TmfExperiment<T> createTraceCopy() {
128 return new TmfExperiment<T>(this);
129 }
130
131 /**
132 * Clears the experiment
133 */
134 @Override
135 public void dispose() {
136 for (ITmfTrace trace : fTraces) {
137 trace.dispose();
138 }
139 fTraces = null;
140 fCheckpoints.clear();
141 super.dispose();
142 }
143
144 private static void setCurrentExperiment(TmfExperiment<?> experiment) {
145 fCurrentExperiment = experiment;
146 }
147
148 // ------------------------------------------------------------------------
149 // ITmfTrace
150 // ------------------------------------------------------------------------
151
152 public String getPath() {
153 return null;
154 }
155
156 public long getNbEvents() {
157 return fNbEvents;
158 }
159
160 public int getCacheSize() {
161 return fIndexPageSize;
162 }
163
164 public TmfTimeRange getTimeRange() {
165 return fTimeRange;
166 }
167
168 public TmfTimestamp getStartTime() {
169 return fTimeRange.getStartTime();
170 }
171
172 public TmfTimestamp getEndTime() {
173 return fTimeRange.getEndTime();
174 }
175
176 public Vector<TmfCheckpoint> getCheckpoints() {
177 return fCheckpoints;
178 }
179
180 // ------------------------------------------------------------------------
181 // Accessors
182 // ------------------------------------------------------------------------
183
184 public static TmfExperiment<?> getCurrentExperiment() {
185 return fCurrentExperiment;
186 }
187
188 public TmfTimestamp getEpoch() {
189 return fEpoch;
190 }
191
192 public ITmfTrace[] getTraces() {
193 return fTraces;
194 }
195
196 /**
197 * Returns the rank of the first event with the requested timestamp.
198 * If none, returns the index of the next event (if any).
199 *
200 * @param timestamp
201 * @return
202 */
203 public long getRank(TmfTimestamp timestamp) {
204 TmfExperimentContext context = seekEvent(timestamp);
205 return context.getRank();
206 }
207
208 /**
209 * Returns the timestamp of the event at the requested index.
210 * If none, returns null.
211 *
212 * @param index
213 * @return
214 */
215 public TmfTimestamp getTimestamp(int index) {
216 TmfExperimentContext context = seekEvent(index);
217 TmfEvent event = getNextEvent(context);
218 return (event != null) ? event.getTimestamp() : null;
219 }
220
221 // ------------------------------------------------------------------------
222 // Operators
223 // ------------------------------------------------------------------------
224
225 /**
226 * Update the total number of events
227 */
228 private void updateNbEvents() {
229 int nbEvents = 0;
230 for (ITmfTrace trace : fTraces) {
231 nbEvents += trace.getNbEvents();
232 }
233 fNbEvents = nbEvents;
234 }
235
236 /**
237 * Update the global time range
238 */
239 private void updateTimeRange() {
240 TmfTimestamp startTime = fTimeRange != null ? fTimeRange.getStartTime() : TmfTimestamp.BigCrunch;
241 TmfTimestamp endTime = fTimeRange != null ? fTimeRange.getEndTime() : TmfTimestamp.BigBang;
242
243 for (ITmfTrace trace : fTraces) {
244 if (trace.getNbEvents() > 0) {
245 TmfTimestamp traceStartTime = trace.getStartTime();
246 if (traceStartTime.compareTo(startTime, true) < 0)
247 startTime = traceStartTime;
248
249 TmfTimestamp traceEndTime = trace.getEndTime();
250 if (traceEndTime.compareTo(endTime, true) > 0)
251 endTime = traceEndTime;
252 }
253 }
254 fTimeRange = new TmfTimeRange(startTime, endTime);
255 }
256
257 // ------------------------------------------------------------------------
258 // TmfProvider
259 // ------------------------------------------------------------------------
260
261 @Override
262 public ITmfContext armRequest(ITmfDataRequest<T> request) {
263 TmfTimestamp timestamp = (request instanceof ITmfEventRequest<?>) ?
264 ((ITmfEventRequest<T>) request).getRange().getStartTime() : null;
265 TmfExperimentContext context = (timestamp != null) ?
266 seekEvent(timestamp) : seekEvent(request.getIndex());
267 return context;
268 }
269
270 @SuppressWarnings("unchecked")
271 @Override
272 public T getNext(ITmfContext context) {
273 if (context instanceof TmfExperimentContext) {
274 return (T) getNextEvent((TmfExperimentContext) context);
275 }
276 return null;
277 }
278
279 // ------------------------------------------------------------------------
280 // ITmfTrace trace positioning
281 // ------------------------------------------------------------------------
282
283 // Returns a brand new context based on the location provided
284 // and initializes the event queues
285 public TmfExperimentContext seekLocation(ITmfLocation<?> location) {
286
287 // Validate the location
288 if (location != null && !(location instanceof TmfExperimentLocation)) {
289 return null; // Throw an exception?
290 }
291
292 // Instantiate the location
293 TmfExperimentLocation expLocation = (location == null)
294 ? new TmfExperimentLocation(new ITmfLocation<?>[fTraces.length], new long[fTraces.length])
295 : (TmfExperimentLocation) location.clone();
296
297 // Create and populate the context's traces contexts
298 TmfExperimentContext context = new TmfExperimentContext(fTraces, new TmfContext[fTraces.length]);
299 long rank = 0;
300 for (int i = 0; i < fTraces.length; i++) {
301 // Get the relevant trace attributes
302 ITmfLocation<?> traceLocation = expLocation.getLocation()[i];
303 long traceRank = expLocation.getRanks()[i];
304
305 // Set the corresponding sub-context
306 context.getContexts()[i] = fTraces[i].seekLocation(traceLocation);
307 context.getContexts()[i].setRank(traceRank);
308 rank += traceRank;
309
310 // Set the trace location and read the corresponding event
311 expLocation.getLocation()[i] = context.getContexts()[i].getLocation();
312 context.getEvents()[i] = fTraces[i].getNextEvent(context.getContexts()[i]);
313 }
314
315 // Finalize context
316 context.setLocation(expLocation);
317 context.setRank(rank);
318 context.setLastTrace(TmfExperimentContext.NO_TRACE);
319 return context;
320 }
321
322 /* (non-Javadoc)
323 * @see org.eclipse.linuxtools.tmf.trace.ITmfTrace#seekEvent(org.eclipse.linuxtools.tmf.event.TmfTimestamp)
324 */
325 public TmfExperimentContext seekEvent(TmfTimestamp timestamp) {
326
327 if (timestamp == null) {
328 timestamp = TmfTimestamp.BigBang;
329 }
330
331 // First, find the right checkpoint
332 int index = Collections.binarySearch(fCheckpoints, new TmfCheckpoint(timestamp, null));
333
334 // In the very likely case that the checkpoint was not found, bsearch
335 // returns its negated would-be location (not an offset...). From that
336 // index, we can then position the stream and get the event.
337 if (index < 0) {
338 index = Math.max(0, -(index + 2));
339 }
340
341 // Position the experiment at the checkpoint
342 ITmfLocation<?> location;
343 synchronized (fCheckpoints) {
344 if (fCheckpoints.size() > 0) {
345 if (index >= fCheckpoints.size()) {
346 index = fCheckpoints.size() - 1;
347 }
348 location = fCheckpoints.elementAt(index).getLocation();
349 }
350 else {
351 location = null;
352 }
353 }
354
355 TmfExperimentContext context = seekLocation(location);
356 context.setRank((long) index * fIndexPageSize);
357
358 // And locate the event
359 TmfExperimentContext nextEventContext = new TmfExperimentContext(context);
360 TmfEvent event = getNextEvent(nextEventContext);
361 while (event != null && event.getTimestamp().compareTo(timestamp, false) < 0) {
362 context = new TmfExperimentContext(nextEventContext);
363 event = getNextEvent(nextEventContext);
364 }
365 context.setLastTrace(TmfExperimentContext.NO_TRACE);
366
367 return context;
368 }
369
370 /* (non-Javadoc)
371 * @see org.eclipse.linuxtools.tmf.trace.ITmfTrace#seekEvent(long)
372 */
373 public TmfExperimentContext seekEvent(long rank) {
374
375 // Position the stream at the previous checkpoint
376 int index = (int) rank / fIndexPageSize;
377 ITmfLocation<?> location;
378 synchronized (fCheckpoints) {
379 if (fCheckpoints.size() == 0) {
380 location = null;
381 }
382 else {
383 if (index >= fCheckpoints.size()) {
384 index = fCheckpoints.size() - 1;
385 }
386 location = fCheckpoints.elementAt(index).getLocation();
387 }
388 }
389
390 TmfExperimentContext context = seekLocation(location);
391 long pos = (long) index * fIndexPageSize;
392 context.setRank(pos);
393
394 // And locate the event
395 TmfExperimentContext nextEventContext = new TmfExperimentContext(context);
396 TmfEvent event = getNextEvent(nextEventContext);
397 while (event != null && pos++ < rank) {
398 event = getNextEvent(nextEventContext);
399 context = new TmfExperimentContext(nextEventContext);
400 if (event != null) context.updateRank(-1);
401 }
402 context.setLastTrace(TmfExperimentContext.NO_TRACE);
403
404 return context;
405 }
406
407 /**
408 * Scan the next events from all traces and return the next one
409 * in chronological order.
410 *
411 * @param context
412 * @return
413 */
414 public synchronized TmfEvent getNextEvent(TmfContext context) {
415
416 // Validate the context
417 if (!(context instanceof TmfExperimentContext)) {
418 return null; // Throw an exception?
419 }
420
421 TmfExperimentContext expContext = (TmfExperimentContext) context;
422
423 // If an event was consumed previously, get the next one from that trace
424 int lastTrace = expContext.getLastTrace();
425 if (lastTrace != TmfExperimentContext.NO_TRACE) {
426 TmfContext traceContext = expContext.getContexts()[lastTrace];
427 expContext.getEvents()[lastTrace] = expContext.getTraces()[lastTrace].getNextEvent(traceContext);
428 }
429
430 // Scan the candidate events and identify the "next" trace to read from
431 int trace = TmfExperimentContext.NO_TRACE;
432 TmfTimestamp timestamp = TmfTimestamp.BigCrunch;
433 for (int i = 0; i < expContext.getTraces().length; i++) {
434 TmfEvent event = expContext.getEvents()[i];
435 if (event != null && event.getTimestamp() != null) {
436 TmfTimestamp otherTS = event.getTimestamp();
437 if (otherTS.compareTo(timestamp, true) < 0) {
438 trace = i;
439 timestamp = otherTS;
440 }
441 }
442 }
443
444 // Update the experiment context and set the "next" event
445 TmfEvent event = null;
446 if (trace >= 0) {
447 expContext.setLastTrace(trace);
448 expContext.updateRank(1);
449 TmfExperimentLocation expLocation = (TmfExperimentLocation) expContext.getLocation();
450 TmfContext traceContext = expContext.getContexts()[trace];
451 expLocation.getLocation()[trace] = traceContext.getLocation().clone();
452 expLocation.getRanks()[trace] = traceContext.getRank();
453 event = expContext.getEvents()[trace];
454 }
455
456 return event;
457 }
458
459 /* (non-Javadoc)
460 * @see org.eclipse.linuxtools.tmf.trace.ITmfTrace#parseEvent(org.eclipse.linuxtools.tmf.trace.TmfContext)
461 */
462 public TmfEvent parseEvent(TmfContext context) {
463
464 if (context instanceof TmfExperimentContext) {
465 TmfExperimentContext expContext = (TmfExperimentContext) context;
466 int lastTrace = expContext.getLastTrace();
467 if (lastTrace != -1) {
468 TmfContext traceContext = expContext.getContexts()[lastTrace];
469 expContext.getEvents()[lastTrace] = expContext.getTraces()[lastTrace].getNextEvent(traceContext);
470 expContext.updateRank(1);
471 TmfExperimentLocation expLocation = (TmfExperimentLocation) expContext.getLocation();
472 expLocation.getLocation()[lastTrace] = traceContext.getLocation().clone();
473 }
474
475 int trace = -1;
476 TmfTimestamp timestamp = TmfTimestamp.BigCrunch;
477 for (int i = 0; i < expContext.getTraces().length; i++) {
478 if (expContext.getEvents()[i] != null) {
479 if (expContext.getEvents()[i].getTimestamp() != null) {
480 TmfTimestamp otherTS = expContext.getEvents()[i].getTimestamp();
481 if (otherTS.compareTo(timestamp, true) < 0) {
482 trace = i;
483 timestamp = otherTS;
484 }
485 }
486 }
487 }
488 if (trace >= 0) {
489 expContext.setLastTrace(TmfExperimentContext.NO_TRACE);
490 return expContext.getEvents()[trace];
491 }
492 }
493
494 return null;
495 }
496
497 /* (non-Javadoc)
498 * @see java.lang.Object#toString()
499 */
500 @Override
501 public String toString() {
502 return "[TmfExperiment (" + getName() + ")]";
503 }
504
505 // ------------------------------------------------------------------------
506 // Indexing
507 // ------------------------------------------------------------------------
508
509 /*
510 * The experiment holds the globally ordered events of its set of traces.
511 * It is expected to provide access to each individual event by index i.e.
512 * it must be possible to request the Nth event of the experiment.
513 *
514 * The purpose of the index is to keep the information needed to rapidly
515 * restore the traces contexts at regular intervals (every INDEX_PAGE_SIZE
516 * event).
517 */
518
519 // The index page size
520 private static final int DEFAULT_INDEX_PAGE_SIZE = 1000;
521 private final int fIndexPageSize;
522
523 // Indicates that an indexing job is already running
524 private Boolean fIndexing = false;
525 private Boolean fIndexed = false;
526
527 // The indexing job
528 private IndexingJob job;
529
530 /**
531 * indexExperiment
532 *
533 * Creates the experiment index.
534 */
535 public void indexExperiment(boolean waitForCompletion) {
536
537 synchronized(this) {
538 if (fIndexed || fIndexing) {
539 // An indexing job is already running but a new request came
540 // in (probably due to a change in the trace set). The index
541 // being currently built is therefore already invalid.
542 // TODO: Cancel and restart the job
543 // TODO: Add support for dynamically adding/removing traces
544 return;
545 }
546 fIndexing = true;
547 }
548
549 job = new IndexingJob(new TmfExperiment<T>(this));
550 job.schedule();
551
552 if (waitForCompletion) {
553 try {
554 job.join();
555 } catch (InterruptedException e) {
556 e.printStackTrace();
557 }
558 }
559 }
560
561 private class IndexingJob extends Job {
562
563 private final String experimentId;
564 private final ITmfTrace[] traces;
565 private Vector<TmfCheckpoint> checkpoints;
566
567 public IndexingJob(TmfExperiment<T> experiment) {
568 super(experiment.getName());
569 experimentId = experiment.getName();
570 traces = experiment.getTraces();
571 }
572
573 /* (non-Javadoc)
574 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
575 */
576 @Override
577 protected IStatus run(IProgressMonitor monitor) {
578
579 // Minimal check
580 if (traces.length == 0) {
581 fIndexing = false;
582 return Status.OK_STATUS;
583 }
584
585 monitor.beginTask("Indexing " + experimentId, IProgressMonitor.UNKNOWN);
586
587 int nbEvents = 0;
588 TmfTimestamp startTime = null;
589 TmfTimestamp lastTime = null;
590
591 // Reset the index
592 checkpoints = new Vector<TmfCheckpoint>();
593
594 try {
595 // Position the trace at the beginning
596 TmfExperimentContext context = seekLocation(null);
597 TmfExperimentLocation location = (TmfExperimentLocation) context.getLocation().clone();
598
599 // Get the first event
600 TmfEvent event = getNextEvent(context);
601 if (event != null) {
602 startTime = new TmfTimestamp(event.getTimestamp());
603 }
604
605 // Index the experiment
606 while (event != null) {
607 lastTime = event.getTimestamp();
608 if ((nbEvents++ % fIndexPageSize) == 0) {
609 checkpoints.add(new TmfCheckpoint(lastTime, location));
610
611 monitor.worked(1);
612
613 // Check monitor *after* fCheckpoints has been updated
614 if (monitor.isCanceled()) {
615 monitor.done();
616 return Status.CANCEL_STATUS;
617 }
618 }
619
620 // We will need the contexts at the next iteration
621 if ((nbEvents % fIndexPageSize) == 0) {
622 location = (TmfExperimentLocation) context.getLocation().clone();
623 }
624
625 event = getNextEvent(context);
626 }
627
628 }
629 finally {
630 synchronized(this) {
631 fCheckpoints = checkpoints;
632 fNbEvents = nbEvents;
633 fTimeRange = new TmfTimeRange(startTime, lastTime);
634 fIndexing = false;
635 fIndexed = true;
636 }
637// notifyListeners(fTimeRange);
638 monitor.done();
639 }
640
641 return Status.OK_STATUS;
642 }
643 }
644
645 protected void notifyListeners(TmfTimeRange range) {
646 broadcast(new TmfRangeSynchSignal(this, range, range.getStartTime()));
647 }
648
649 // ------------------------------------------------------------------------
650 // Signal handlers
651 // ------------------------------------------------------------------------
652
653 @TmfSignalHandler
654 public void experimentSelected(TmfExperimentSelectedSignal<T> signal) {
655 TmfExperiment<?> experiment = signal.getExperiment();
656 if (experiment == this) {
657 setCurrentExperiment(experiment);
658 }
659 else {
660 dispose();
661 }
662// if (signal.getExperiment() == this) {
663// indexExperiment(true);
664// }
665 }
666
667 @TmfSignalHandler
668 public void experimentUpdated(TmfExperimentUpdatedSignal signal) {
669// indexExperiment(true);
670 }
671
672 @TmfSignalHandler
673 public void traceUpdated(TmfTraceUpdatedSignal signal) {
674 // TODO: Incremental index update
675 synchronized(this) {
676 updateNbEvents();
677 updateTimeRange();
678 }
679 broadcast(new TmfExperimentUpdatedSignal(this, this, signal.getTrace()));
680 }
681
682}
This page took 0.049359 seconds and 5 git commands to generate.