[Bug304438] Improvement on ITmfLocation
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf / src / org / eclipse / linuxtools / tmf / experiment / TmfExperiment.java
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
13 package org.eclipse.linuxtools.tmf.experiment;
14
15 import java.util.Collections;
16 import java.util.Vector;
17
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;
36
37 /**
38 * <b><u>TmfExperiment</u></b>
39 * <p>
40 * TmfExperiment presents a time-ordered, unified view of a set of TmfTraces
41 * that are part of a tracing experiment.
42 * <p>
43 */
44 public class TmfExperiment<T extends TmfEvent> extends TmfProvider<T> implements ITmfTrace {
45
46 // ------------------------------------------------------------------------
47 // Attributes
48 // ------------------------------------------------------------------------
49
50 // The currently selected experiment
51 private static TmfExperiment<?> fCurrentExperiment;
52
53 // The experiment ID
54 private String fExperimentId;
55
56 // The set of traces that constitute the experiment
57 private ITmfTrace[] fTraces;
58
59 // The total number of events
60 private long fNbEvents;
61
62 // The experiment time range
63 private TmfTimeRange fTimeRange;
64
65 // The experiment reference timestamp (default: BigBang)
66 private TmfTimestamp fEpoch;
67
68 // The experiment index
69 private Vector<TmfCheckpoint> fCheckpoints = new Vector<TmfCheckpoint>();
70
71 // ------------------------------------------------------------------------
72 // Constructors
73 // ------------------------------------------------------------------------
74
75 /**
76 * @param type
77 * @param id
78 * @param traces
79 * @param epoch
80 * @param indexPageSize
81 */
82 public TmfExperiment(Class<T> type, String id, ITmfTrace[] traces, TmfTimestamp epoch, int indexPageSize) {
83 super(type);
84
85 fExperimentId = id;
86 fTraces = traces;
87 fEpoch = epoch;
88 fIndexPageSize = indexPageSize;
89
90 updateNbEvents();
91 updateTimeRange();
92 }
93
94 /**
95 * @param type
96 * @param id
97 * @param traces
98 */
99 public TmfExperiment(Class<T> type, String id, ITmfTrace[] traces) {
100 this(type, id, traces, TmfTimestamp.Zero, DEFAULT_INDEX_PAGE_SIZE);
101 }
102
103 /**
104 * @param type
105 * @param id
106 * @param traces
107 * @param indexPageSize
108 */
109 public TmfExperiment(Class<T> type, String id, ITmfTrace[] traces, int indexPageSize) {
110 this(type, id, traces, TmfTimestamp.Zero, indexPageSize);
111 }
112
113 /**
114 *
115 */
116 @Override
117 public void deregister() {
118 fTraces = null;
119 fCheckpoints.clear();
120 fCurrentExperiment= null;
121 super.deregister();
122 }
123
124 // ------------------------------------------------------------------------
125 // ITmfTrace accessors
126 // ------------------------------------------------------------------------
127
128 public String getPath() {
129 return null;
130 }
131
132 public String getName() {
133 return fExperimentId;
134 }
135
136 public long getNbEvents() {
137 return fNbEvents;
138 }
139
140 public TmfTimeRange getTimeRange() {
141 return fTimeRange;
142 }
143
144 public TmfTimestamp getStartTime() {
145 return fTimeRange.getStartTime();
146 }
147
148 public TmfTimestamp getEndTime() {
149 return fTimeRange.getEndTime();
150 }
151
152 // ------------------------------------------------------------------------
153 // Accessors
154 // ------------------------------------------------------------------------
155
156 public static TmfExperiment<?> getCurrentExperiment() {
157 return fCurrentExperiment;
158 }
159
160 public TmfTimestamp getEpoch() {
161 return fEpoch;
162 }
163
164 public ITmfTrace[] getTraces() {
165 return fTraces;
166 }
167
168 /**
169 * Returns the rank of the first event with the requested timestamp.
170 * If none, returns the index of the next event (if any).
171 *
172 * @param ts
173 * @return
174 */
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();
180 }
181
182 /**
183 * Returns the timestamp of the event at the requested index.
184 * If none, returns null.
185 *
186 * @param index
187 * @return
188 */
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;
195 return timestamp;
196 }
197
198 // ------------------------------------------------------------------------
199 // Operators
200 // ------------------------------------------------------------------------
201
202 // /**
203 // * Add a trace to the experiment trace set
204 // *
205 // * @param trace
206 // */
207 // public void addTrace(ITmfTrace trace) {
208 // fTraces.add(trace);
209 // synchronized(this) {
210 // updateNbEvents();
211 // updateTimeRange();
212 // }
213 // }
214
215 /**
216 * Update the total number of events
217 */
218 private void updateNbEvents() {
219 int nbEvents = 0;
220 for (ITmfTrace trace : fTraces) {
221 nbEvents += trace.getNbEvents();
222 }
223 fNbEvents = nbEvents;
224 }
225
226 /**
227 * Update the global time range
228 */
229 private void updateTimeRange() {
230 TmfTimestamp startTime = fTimeRange != null ? fTimeRange.getStartTime() : TmfTimestamp.BigCrunch;
231 TmfTimestamp endTime = fTimeRange != null ? fTimeRange.getEndTime() : TmfTimestamp.BigBang;
232
233 for (ITmfTrace trace : fTraces) {
234 TmfTimestamp traceStartTime = trace.getStartTime();
235 if (traceStartTime.compareTo(startTime, true) < 0)
236 startTime = traceStartTime;
237
238 TmfTimestamp traceEndTime = trace.getEndTime();
239 if (traceEndTime.compareTo(endTime, true) > 0)
240 endTime = traceEndTime;
241 }
242 fTimeRange = new TmfTimeRange(startTime, endTime);
243 }
244
245 // ------------------------------------------------------------------------
246 // TmfProvider
247 // ------------------------------------------------------------------------
248
249 @Override
250 public ITmfContext setContext(TmfDataRequest<T> request) {
251 TmfTimestamp timestamp = (request instanceof TmfEventRequest<?>) ?
252 ((TmfEventRequest<T>) request).getRange().getStartTime() : null;
253
254 TmfExperimentContext context = (timestamp != null) ?
255 seekEvent(timestamp) : seekEvent(request.getIndex());
256
257 return context;
258 }
259
260 @SuppressWarnings("unchecked")
261 @Override
262 public T getNext(ITmfContext context) {
263 if (context instanceof TmfExperimentContext) {
264 return (T) getNextEvent((TmfExperimentContext) context);
265 }
266 return null;
267 }
268
269 @Override
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;
273 }
274 return false;
275 }
276
277 // ------------------------------------------------------------------------
278 // ITmfTrace trace positioning
279 // ------------------------------------------------------------------------
280
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];
289
290 TmfExperimentContext context = new TmfExperimentContext(fTraces, contexts);
291 TmfEvent[] events = context.getEvents();
292
293 long rank = 0;
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();
299 }
300 context.setLocation(new TmfExperimentLocation(newloc));
301 context.setRank(rank);
302 return context;
303 }
304 return null;
305 }
306
307 public TmfExperimentContext seekEvent(TmfTimestamp timestamp) {
308
309 if (timestamp == null) {
310 timestamp = TmfTimestamp.BigBang;
311 }
312
313 // First, find the right checkpoint
314 int index = Collections.binarySearch(fCheckpoints, new TmfCheckpoint(timestamp, null));
315
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.
319 if (index < 0) {
320 index = Math.max(0, -(index + 2));
321 }
322
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;
329 }
330 location = fCheckpoints.elementAt(index).getLocation();
331 }
332 else {
333 location = null;
334 }
335 }
336
337 TmfExperimentContext nextEventContext = seekLocation(location);
338 nextEventContext.setRank(index * fIndexPageSize);
339 TmfExperimentContext currentEventContext = new TmfExperimentContext(nextEventContext);
340
341 // And get the event
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);
348 }
349
350 return currentEventContext;
351 }
352
353 public TmfExperimentContext seekEvent(long rank) {
354
355 TmfExperimentContext context;
356 int page = 0; // The checkpoint page
357 int current = 0; // The current event index (rank)
358
359 // If there is no checkpoint created yet, start from the beginning
360 if (fCheckpoints.size() == 0) {
361 context = seekLocation(null);
362 }
363 else {
364 page = (int) rank / fIndexPageSize;
365 if (page >= fCheckpoints.size()) {
366 page = fCheckpoints.size() - 1;
367 }
368 context = seekLocation(fCheckpoints.elementAt(page).getLocation());
369 current = page * fIndexPageSize;
370 }
371
372 // Position the traces at the requested index
373 while (current++ < rank) {
374 getNextEvent(context);
375 }
376
377 return context;
378 }
379
380 // /**
381 // * Given an experiment event index, position the set of traces so a call
382 // * to getNextEvent() will retrieve the corresponding event.
383 // *
384 // * @param index
385 // * @param context
386 // */
387 // private synchronized void positionTraces(long index, TmfExperimentContext context) {
388 //
389 // // Extract the relevant information
390 // ITmfTrace[] traces = context.getTraces();
391 // TmfEvent[] events = context.getEvents();
392 // TmfContext[] contexts = context.getContexts();
393 //
394 // int page = 0; // The checkpoint page
395 // int current = 0; // The current event index (rank)
396 //
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]);
402 // }
403 // }
404 // else {
405 // page = (int) index / fIndexPageSize;
406 // if (page >= fCheckpoints.size()) {
407 // page = fCheckpoints.size() - 1;
408 // }
409 //
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]);
414 // }
415 // current = page * fIndexPageSize;
416 // }
417 //
418 // // Position the traces at the requested index
419 // while (current++ < index) {
420 // getNextEvent(context);
421 // }
422 // }
423
424 /**
425 * Scan the next events from all traces and return the next one
426 * in chronological order.
427 *
428 * @param context
429 * @return
430 */
431 public synchronized TmfEvent getNextEvent(TmfContext context) {
432 if (context instanceof TmfExperimentContext) {
433 TmfExperimentContext expContext = (TmfExperimentContext) context;
434 int trace = 0;
435 TmfTimestamp timestamp = TmfTimestamp.BigCrunch;
436 if (expContext.getEvents()[trace] != null) {
437 timestamp = expContext.getEvents()[trace].getTimestamp();
438 }
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) {
443 trace = i;
444 timestamp = otherTS;
445 }
446 }
447 }
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);
454 return event;
455 }
456 return null;
457 }
458
459 // public TmfEvent getNextEvent(TmfExperimentContext context) {
460 // // TODO: Consider the time adjustment
461 // int trace = 0;
462 // TmfTimestamp timestamp = TmfTimestamp.BigCrunch;
463 // if (context.getEvents()[trace] != null) {
464 // timestamp = context.getEvents()[trace].getTimestamp();
465 // }
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) {
470 // trace = i;
471 // timestamp = otherTS;
472 // }
473 // }
474 // }
475 // TmfEvent event = context.getTraces()[trace].getNextEvent(context.getContexts()[trace]);
476 // context.getEvents()[trace] = context.getTraces()[trace].parseEvent(context.getContexts()[trace]);
477 // return event;
478 // }
479
480 public TmfEvent parseEvent(TmfContext context) {
481 // TODO Auto-generated method stub
482 return null;
483 }
484
485 // /**
486 // * Scan the next events from all traces and return the next one
487 // * in chronological order.
488 // *
489 // * @param context
490 // * @return
491 // */
492 // private TmfTimestamp getNextEventTimestamp(TmfExperimentContext context) {
493 // // TODO: Consider the time adjustment
494 // int trace = 0;
495 // TmfTimestamp timestamp = TmfTimestamp.BigCrunch;
496 // if (context.getEvents()[trace] != null) {
497 // timestamp = context.getEvents()[trace].getTimestamp();
498 // }
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) {
503 // trace = i;
504 // timestamp = otherTS;
505 // }
506 // }
507 // }
508 // return timestamp;
509 // }
510
511 /* (non-Javadoc)
512 * @see java.lang.Object#toString()
513 */
514 @Override
515 public String toString() {
516 return "[TmfExperiment (" + fExperimentId + ")]";
517 }
518
519 // ------------------------------------------------------------------------
520 // Indexing
521 // ------------------------------------------------------------------------
522
523 /*
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.
527 *
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
530 * event).
531 */
532
533 // The index page size
534 private static final int DEFAULT_INDEX_PAGE_SIZE = 1000;
535 private final int fIndexPageSize;
536
537 // Indicates that an indexing job is already running
538 private Boolean fIndexing = false;
539 private Boolean fIndexed = false;
540
541 // The indexing job
542 private IndexingJob job;
543
544 /**
545 * indexExperiment
546 *
547 * Creates the experiment index.
548 */
549 public void indexExperiment(boolean waitForCompletion) {
550
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
558 return;
559 }
560 fIndexing = true;
561 }
562
563 job = new IndexingJob(fExperimentId);
564 job.schedule();
565
566 if (waitForCompletion) {
567 try {
568 job.join();
569 } catch (InterruptedException e) {
570 e.printStackTrace();
571 }
572 }
573 }
574
575 private class IndexingJob extends Job {
576
577 public IndexingJob(String name) {
578 super(name);
579 }
580
581 /* (non-Javadoc)
582 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
583 */
584 @Override
585 protected IStatus run(IProgressMonitor monitor) {
586
587 // Minimal check
588 if (fTraces.length == 0) {
589 fIndexing = false;
590 return Status.OK_STATUS;
591 }
592
593 monitor.beginTask("Indexing " + fExperimentId, IProgressMonitor.UNKNOWN);
594
595 int nbEvents = 0;
596 TmfTimestamp startTime = null;
597 TmfTimestamp lastTime = null;
598
599 // Reset the index
600 fCheckpoints = new Vector<TmfCheckpoint>();
601
602 try {
603 // Position the trace at the beginning
604 TmfExperimentContext context = seekLocation(null);
605 TmfExperimentLocation location = (TmfExperimentLocation) context.getLocation();
606
607 // Get the first event
608 TmfEvent event = getNextEvent(context);
609 if (event != null) {
610 startTime = new TmfTimestamp(event.getTimestamp());
611 }
612
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));
621
622 monitor.worked(1);
623
624 // Check monitor *after* fCheckpoints has been updated
625 if (monitor.isCanceled()) {
626 monitor.done();
627 return Status.CANCEL_STATUS;
628 }
629 }
630
631 // We will need the contexts at the next iteration
632 if ((nbEvents % fIndexPageSize) == 0) {
633 location = (TmfExperimentLocation) context.getLocation();
634 }
635
636 event = getNextEvent(context);
637 }
638
639 }
640 finally {
641 synchronized(this) {
642 fNbEvents = nbEvents;
643 fTimeRange = new TmfTimeRange(startTime, lastTime);
644 fIndexing = false;
645 fIndexed = true;
646 }
647 monitor.done();
648 }
649
650 // dumpCheckpoints();
651
652 return Status.OK_STATUS;
653 }
654 }
655
656 protected void notifyListeners(TmfTimeRange range) {
657 broadcast(new TmfRangeSynchSignal(this, range, null));
658 }
659
660 // ========================================================================
661 // Toubleshooting code
662 // ========================================================================
663
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);
670 // long rank = 0;
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);
679 // }
680 // System.out.println("Sum of ranks: " + rank + " (expected: " + i * fIndexPageSize + ")");
681 // }
682 // }
683
684 // ------------------------------------------------------------------------
685 // Signal handlers
686 // ------------------------------------------------------------------------
687
688 @TmfSignalHandler
689 public void experimentSelected(TmfExperimentSelectedSignal signal) {
690 fCurrentExperiment = signal.getExperiment();
691 // if (signal.getExperiment() == this) {
692 // indexExperiment(true);
693 // }
694 }
695
696 @TmfSignalHandler
697 public void experimentUpdated(TmfExperimentUpdatedSignal signal) {
698 // indexExperiment(true);
699 }
700
701 @TmfSignalHandler
702 public void traceUpdated(TmfTraceUpdatedSignal signal) {
703 // TODO: Incremental index update
704 synchronized(this) {
705 updateNbEvents();
706 updateTimeRange();
707 }
708 broadcast(new TmfExperimentUpdatedSignal(this, this, signal.getTrace()));
709 }
710
711 }
This page took 0.046231 seconds and 5 git commands to generate.