[Bug292967] Second part of request coalescing + unit tests + minor fixes.
[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
9f584e4c 15import java.util.Collections;
8c8bf09f
ASL
16import java.util.Vector;
17
e31e01e8
FC
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;
fc6ccf6f 22import org.eclipse.linuxtools.tmf.component.TmfEventProvider;
8c8bf09f
ASL
23import org.eclipse.linuxtools.tmf.event.TmfEvent;
24import org.eclipse.linuxtools.tmf.event.TmfTimeRange;
25import org.eclipse.linuxtools.tmf.event.TmfTimestamp;
9aae0442
ASL
26import org.eclipse.linuxtools.tmf.request.TmfDataRequest;
27import org.eclipse.linuxtools.tmf.request.TmfEventRequest;
9f584e4c 28import org.eclipse.linuxtools.tmf.signal.TmfRangeSynchSignal;
8c8bf09f 29import org.eclipse.linuxtools.tmf.signal.TmfSignalHandler;
9f584e4c
FC
30import org.eclipse.linuxtools.tmf.trace.ITmfContext;
31import org.eclipse.linuxtools.tmf.trace.ITmfLocation;
8c8bf09f 32import org.eclipse.linuxtools.tmf.trace.ITmfTrace;
9f584e4c
FC
33import org.eclipse.linuxtools.tmf.trace.TmfCheckpoint;
34import org.eclipse.linuxtools.tmf.trace.TmfContext;
e31e01e8 35import org.eclipse.linuxtools.tmf.trace.TmfTraceUpdatedSignal;
8c8bf09f
ASL
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 */
fc6ccf6f 44public class TmfExperiment<T extends TmfEvent> extends TmfEventProvider<T> implements ITmfTrace {
8c8bf09f
ASL
45
46 // ------------------------------------------------------------------------
47 // Attributes
48 // ------------------------------------------------------------------------
49
50 // The currently selected experiment
e31e01e8
FC
51 private static TmfExperiment<?> fCurrentExperiment;
52
53 // The experiment ID
54 private String fExperimentId;
8c8bf09f 55
9f584e4c
FC
56 // The set of traces that constitute the experiment
57 private ITmfTrace[] fTraces;
8c8bf09f
ASL
58
59 // The total number of events
9f584e4c 60 private long fNbEvents;
8c8bf09f
ASL
61
62 // The experiment time range
63 private TmfTimeRange fTimeRange;
64
65 // The experiment reference timestamp (default: BigBang)
66 private TmfTimestamp fEpoch;
67
9f584e4c
FC
68 // The experiment index
69 private Vector<TmfCheckpoint> fCheckpoints = new Vector<TmfCheckpoint>();
70
8c8bf09f
ASL
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) {
e31e01e8 83 super(type);
8c8bf09f 84
e31e01e8 85 fExperimentId = id;
9f584e4c 86 fTraces = traces;
8c8bf09f
ASL
87 fEpoch = epoch;
88 fIndexPageSize = indexPageSize;
89
90 updateNbEvents();
91 updateTimeRange();
e31e01e8 92 }
8c8bf09f
ASL
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 }
e31e01e8 112
8c8bf09f 113 /**
e31e01e8 114 *
8c8bf09f
ASL
115 */
116 @Override
e31e01e8 117 public void deregister() {
9f584e4c
FC
118 fTraces = null;
119 fCheckpoints.clear();
e31e01e8
FC
120 fCurrentExperiment= null;
121 super.deregister();
8c8bf09f
ASL
122 }
123
9f584e4c
FC
124 // ------------------------------------------------------------------------
125 // ITmfTrace accessors
126 // ------------------------------------------------------------------------
127
128 public String getPath() {
129 return null;
130 }
131
fc6ccf6f 132 @Override
9f584e4c
FC
133 public String getName() {
134 return fExperimentId;
135 }
136
137 public long getNbEvents() {
138 return fNbEvents;
139 }
140
141 public TmfTimeRange getTimeRange() {
142 return fTimeRange;
143 }
144
145 public TmfTimestamp getStartTime() {
146 return fTimeRange.getStartTime();
147 }
148
149 public TmfTimestamp getEndTime() {
150 return fTimeRange.getEndTime();
151 }
152
8c8bf09f 153 // ------------------------------------------------------------------------
e31e01e8 154 // Accessors
8c8bf09f
ASL
155 // ------------------------------------------------------------------------
156
e31e01e8
FC
157 public static TmfExperiment<?> getCurrentExperiment() {
158 return fCurrentExperiment;
8c8bf09f
ASL
159 }
160
8c8bf09f
ASL
161 public TmfTimestamp getEpoch() {
162 return fEpoch;
163 }
164
9f584e4c
FC
165 public ITmfTrace[] getTraces() {
166 return fTraces;
8c8bf09f
ASL
167 }
168
169 /**
170 * Returns the rank of the first event with the requested timestamp.
171 * If none, returns the index of the next event (if any).
172 *
e31e01e8 173 * @param ts
8c8bf09f
ASL
174 * @return
175 */
e31e01e8
FC
176 public long getRank(TmfTimestamp ts) {
177 // FIXME: Go over all the traces
9f584e4c
FC
178 ITmfTrace trace = fTraces[0];
179 TmfContext context = trace.seekEvent(ts);
8c8bf09f
ASL
180 return context.getRank();
181 }
182
183 /**
184 * Returns the timestamp of the event at the requested index.
185 * If none, returns null.
186 *
187 * @param index
188 * @return
189 */
190 public TmfTimestamp getTimestamp(int index) {
e31e01e8 191 // FIXME: Go over all the traces
9f584e4c
FC
192 ITmfTrace trace = fTraces[0];
193 TmfContext context = trace.seekEvent(index);
e31e01e8 194 TmfEvent event = trace.getNextEvent(context);
9f584e4c
FC
195 TmfTimestamp timestamp = (event != null) ? event.getTimestamp() : null;
196 return timestamp;
8c8bf09f
ASL
197 }
198
199 // ------------------------------------------------------------------------
200 // Operators
201 // ------------------------------------------------------------------------
202
9f584e4c
FC
203// /**
204// * Add a trace to the experiment trace set
205// *
206// * @param trace
207// */
208// public void addTrace(ITmfTrace trace) {
209// fTraces.add(trace);
210// synchronized(this) {
211// updateNbEvents();
212// updateTimeRange();
213// }
214// }
e31e01e8 215
8c8bf09f
ASL
216 /**
217 * Update the total number of events
218 */
219 private void updateNbEvents() {
220 int nbEvents = 0;
221 for (ITmfTrace trace : fTraces) {
222 nbEvents += trace.getNbEvents();
223 }
224 fNbEvents = nbEvents;
225 }
226
227 /**
228 * Update the global time range
229 */
230 private void updateTimeRange() {
231 TmfTimestamp startTime = fTimeRange != null ? fTimeRange.getStartTime() : TmfTimestamp.BigCrunch;
232 TmfTimestamp endTime = fTimeRange != null ? fTimeRange.getEndTime() : TmfTimestamp.BigBang;
233
234 for (ITmfTrace trace : fTraces) {
e31e01e8
FC
235 TmfTimestamp traceStartTime = trace.getStartTime();
236 if (traceStartTime.compareTo(startTime, true) < 0)
237 startTime = traceStartTime;
238
239 TmfTimestamp traceEndTime = trace.getEndTime();
240 if (traceEndTime.compareTo(endTime, true) > 0)
241 endTime = traceEndTime;
8c8bf09f
ASL
242 }
243 fTimeRange = new TmfTimeRange(startTime, endTime);
244 }
245
246 // ------------------------------------------------------------------------
247 // TmfProvider
248 // ------------------------------------------------------------------------
249
250 @Override
fc6ccf6f 251 public ITmfContext armRequest(TmfDataRequest<T> request) {
9f584e4c
FC
252 TmfTimestamp timestamp = (request instanceof TmfEventRequest<?>) ?
253 ((TmfEventRequest<T>) request).getRange().getStartTime() : null;
254
255 TmfExperimentContext context = (timestamp != null) ?
256 seekEvent(timestamp) : seekEvent(request.getIndex());
257
8c8bf09f
ASL
258 return context;
259 }
260
261 @SuppressWarnings("unchecked")
262 @Override
263 public T getNext(ITmfContext context) {
264 if (context instanceof TmfExperimentContext) {
265 return (T) getNextEvent((TmfExperimentContext) context);
266 }
267 return null;
268 }
269
9f584e4c
FC
270 // ------------------------------------------------------------------------
271 // ITmfTrace trace positioning
272 // ------------------------------------------------------------------------
273
274 // Returns a brand new context based on the location provided
275 // Arms the event queues
276 // NOTE: This is a fine example of pathological coupling...
452ad365 277 public TmfExperimentContext seekLocation(ITmfLocation<?> location) {
9f584e4c 278 if (location instanceof TmfExperimentLocation || location == null) {
452ad365
FC
279 ITmfLocation<?>[] oldloc = (location != null) ? ((TmfExperimentLocation) location).getLocation() : new TmfExperimentLocation[fTraces.length];
280 ITmfLocation<?>[] newloc = new ITmfLocation[fTraces.length];
9f584e4c
FC
281 TmfContext[] contexts = new TmfContext[fTraces.length];
282
283 TmfExperimentContext context = new TmfExperimentContext(fTraces, contexts);
284 TmfEvent[] events = context.getEvents();
285
286 long rank = 0;
287 for (int i = 0; i < fTraces.length; i++) {
288 contexts[i] = fTraces[i].seekLocation(oldloc[i]);
289 newloc[i] = contexts[i].getLocation(); // No clone here
290 events[i] = fTraces[i].parseEvent(contexts[i]);
291 rank += contexts[i].getRank();
292 }
293 context.setLocation(new TmfExperimentLocation(newloc));
294 context.setRank(rank);
295 return context;
296 }
297 return null;
298 }
299
300 public TmfExperimentContext seekEvent(TmfTimestamp timestamp) {
8c8bf09f 301
9f584e4c
FC
302 if (timestamp == null) {
303 timestamp = TmfTimestamp.BigBang;
304 }
305
306 // First, find the right checkpoint
307 int index = Collections.binarySearch(fCheckpoints, new TmfCheckpoint(timestamp, null));
308
309 // In the very likely case that the checkpoint was not found, bsearch
310 // returns its negated would-be location (not an offset...). From that
311 // index, we can then position the stream and get the event.
312 if (index < 0) {
313 index = Math.max(0, -(index + 2));
314 }
315
316 // Position the experiment at the checkpoint
452ad365 317 ITmfLocation<?> location;
9f584e4c
FC
318 synchronized (fCheckpoints) {
319 if (fCheckpoints.size() > 0) {
320 if (index >= fCheckpoints.size()) {
321 index = fCheckpoints.size() - 1;
322 }
323 location = fCheckpoints.elementAt(index).getLocation();
324 }
325 else {
326 location = null;
327 }
328 }
329
330 TmfExperimentContext nextEventContext = seekLocation(location);
331 nextEventContext.setRank(index * fIndexPageSize);
332 TmfExperimentContext currentEventContext = new TmfExperimentContext(nextEventContext);
333
334 // And get the event
335 TmfEvent event = getNextEvent(nextEventContext);
336 while (event != null && event.getTimestamp().compareTo(timestamp, false) < 0) {
337 currentEventContext = new TmfExperimentContext(nextEventContext);
338// currentEventContext.setLocation(nextEventContext.getLocation());
339// currentEventContext.updateRank(1);
340 event = getNextEvent(nextEventContext);
341 }
342
343 return currentEventContext;
344 }
8c8bf09f 345
9f584e4c
FC
346 public TmfExperimentContext seekEvent(long rank) {
347
348 TmfExperimentContext context;
e31e01e8
FC
349 int page = 0; // The checkpoint page
350 int current = 0; // The current event index (rank)
9f584e4c 351
e31e01e8 352 // If there is no checkpoint created yet, start from the beginning
9f584e4c
FC
353 if (fCheckpoints.size() == 0) {
354 context = seekLocation(null);
e31e01e8
FC
355 }
356 else {
9f584e4c
FC
357 page = (int) rank / fIndexPageSize;
358 if (page >= fCheckpoints.size()) {
359 page = fCheckpoints.size() - 1;
e31e01e8 360 }
9f584e4c 361 context = seekLocation(fCheckpoints.elementAt(page).getLocation());
e31e01e8
FC
362 current = page * fIndexPageSize;
363 }
364
365 // Position the traces at the requested index
9f584e4c 366 while (current++ < rank) {
e31e01e8
FC
367 getNextEvent(context);
368 }
9f584e4c
FC
369
370 return context;
8c8bf09f
ASL
371 }
372
373 /**
374 * Scan the next events from all traces and return the next one
375 * in chronological order.
376 *
377 * @param context
378 * @return
379 */
9f584e4c
FC
380 public synchronized TmfEvent getNextEvent(TmfContext context) {
381 if (context instanceof TmfExperimentContext) {
382 TmfExperimentContext expContext = (TmfExperimentContext) context;
383 int trace = 0;
384 TmfTimestamp timestamp = TmfTimestamp.BigCrunch;
385 if (expContext.getEvents()[trace] != null) {
386 timestamp = expContext.getEvents()[trace].getTimestamp();
387 }
388 for (int i = 1; i < expContext.getTraces().length; i++) {
389 if (expContext.getEvents()[i].getTimestamp() != null) {
390 TmfTimestamp otherTS = expContext.getEvents()[i].getTimestamp();
391 if (otherTS.compareTo(timestamp, true) < 0) {
392 trace = i;
393 timestamp = otherTS;
394 }
8c8bf09f
ASL
395 }
396 }
9f584e4c
FC
397 TmfContext trcloc = expContext.getContexts()[trace];
398 TmfEvent event = expContext.getTraces()[trace].parseEvent(trcloc);
399 TmfExperimentLocation exploc = (TmfExperimentLocation) expContext.getLocation();
452ad365 400 exploc.getLocation()[trace] = trcloc.getLocation().clone();
9f584e4c
FC
401 expContext.updateRank(1);
402 expContext.getEvents()[trace] = expContext.getTraces()[trace].getNextEvent(trcloc);
403 return event;
8c8bf09f 404 }
9f584e4c 405 return null;
8c8bf09f
ASL
406 }
407
9f584e4c
FC
408 public TmfEvent parseEvent(TmfContext context) {
409 // TODO Auto-generated method stub
410 return null;
8c8bf09f
ASL
411 }
412
413 /* (non-Javadoc)
414 * @see java.lang.Object#toString()
415 */
416 @Override
417 public String toString() {
e31e01e8 418 return "[TmfExperiment (" + fExperimentId + ")]";
8c8bf09f
ASL
419 }
420
421 // ------------------------------------------------------------------------
422 // Indexing
423 // ------------------------------------------------------------------------
424
425 /*
426 * The experiment holds the globally ordered events of its set of traces.
427 * It is expected to provide access to each individual event by index i.e.
9f584e4c 428 * it must be possible to request the Nth event of the experiment.
8c8bf09f
ASL
429 *
430 * The purpose of the index is to keep the information needed to rapidly
431 * restore the traces contexts at regular intervals (every INDEX_PAGE_SIZE
432 * event).
433 */
434
435 // The index page size
436 private static final int DEFAULT_INDEX_PAGE_SIZE = 1000;
e31e01e8 437 private final int fIndexPageSize;
8c8bf09f 438
e31e01e8
FC
439 // Indicates that an indexing job is already running
440 private Boolean fIndexing = false;
441 private Boolean fIndexed = false;
8c8bf09f 442
e31e01e8
FC
443 // The indexing job
444 private IndexingJob job;
445
446 /**
447 * indexExperiment
448 *
449 * Creates the experiment index.
450 */
451 public void indexExperiment(boolean waitForCompletion) {
452
453 synchronized(fIndexing) {
454 if (fIndexed || fIndexing) {
455 // An indexing job is already running but a new request came
456 // in (probably due to a change in the trace set). The index
457 // being currently built is therefore already invalid.
458 // TODO: Cancel and restart the job
459 // TODO: Add support for dynamically adding/removing traces
460 return;
9aae0442 461 }
e31e01e8
FC
462 fIndexing = true;
463 }
8c8bf09f 464
e31e01e8
FC
465 job = new IndexingJob(fExperimentId);
466 job.schedule();
467
468 if (waitForCompletion) {
469 try {
470 job.join();
471 } catch (InterruptedException e) {
472 e.printStackTrace();
473 }
474 }
8c8bf09f 475 }
e31e01e8
FC
476
477 private class IndexingJob extends Job {
478
479 public IndexingJob(String name) {
480 super(name);
481 }
482
483 /* (non-Javadoc)
484 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
485 */
486 @Override
487 protected IStatus run(IProgressMonitor monitor) {
488
489 // Minimal check
9f584e4c 490 if (fTraces.length == 0) {
e31e01e8
FC
491 fIndexing = false;
492 return Status.OK_STATUS;
493 }
494
495 monitor.beginTask("Indexing " + fExperimentId, IProgressMonitor.UNKNOWN);
496
497 int nbEvents = 0;
498 TmfTimestamp startTime = null;
499 TmfTimestamp lastTime = null;
500
9f584e4c
FC
501 // Reset the index
502 fCheckpoints = new Vector<TmfCheckpoint>();
e31e01e8
FC
503
504 try {
9f584e4c
FC
505 // Position the trace at the beginning
506 TmfExperimentContext context = seekLocation(null);
507 TmfExperimentLocation location = (TmfExperimentLocation) context.getLocation();
508
509 // Get the first event
510 TmfEvent event = getNextEvent(context);
511 if (event != null) {
512 startTime = new TmfTimestamp(event.getTimestamp());
513 }
514
515 // Index the experiment
516 while (event != null) {
517 lastTime = event.getTimestamp();
e31e01e8 518 if ((nbEvents++ % fIndexPageSize) == 0) {
9f584e4c 519 fCheckpoints.add(new TmfCheckpoint(lastTime, location.clone()));
e31e01e8
FC
520 fNbEvents = nbEvents;
521 fTimeRange = new TmfTimeRange(startTime, lastTime);
9f584e4c 522 notifyListeners(new TmfTimeRange(startTime, lastTime));
e31e01e8
FC
523
524 monitor.worked(1);
525
526 // Check monitor *after* fCheckpoints has been updated
527 if (monitor.isCanceled()) {
528 monitor.done();
529 return Status.CANCEL_STATUS;
530 }
531 }
532
533 // We will need the contexts at the next iteration
534 if ((nbEvents % fIndexPageSize) == 0) {
9f584e4c 535 location = (TmfExperimentLocation) context.getLocation();
e31e01e8
FC
536 }
537
538 event = getNextEvent(context);
539 }
540
541 }
542 finally {
543 synchronized(this) {
544 fNbEvents = nbEvents;
545 fTimeRange = new TmfTimeRange(startTime, lastTime);
546 fIndexing = false;
547 fIndexed = true;
548 }
549 monitor.done();
550 }
551
9f584e4c 552// dumpCheckpoints();
e31e01e8
FC
553
554 return Status.OK_STATUS;
555 }
556 }
557
9f584e4c
FC
558 protected void notifyListeners(TmfTimeRange range) {
559 broadcast(new TmfRangeSynchSignal(this, range, null));
560 }
561
562 // ========================================================================
563 // Toubleshooting code
564 // ========================================================================
565
566// private void dumpCheckpoints() {
e31e01e8
FC
567// System.out.println("-----");
568// System.out.println("Checkpoints of " + fExperimentId);
9f584e4c 569// for (int i = 0; i < fCheckpoints.size(); i++) {
e31e01e8 570// System.out.println("Entry:" + i);
9f584e4c
FC
571// TmfCheckpoint checkpoint = fCheckpoints.get(i);
572// long rank = 0;
573// for (int j = 0; j < fTraces.length; j++) {
574// ITmfTrace trace = fTraces[j];
575// TmfExperimentContext context = seekLocation(checkpoint.getLocation());
576// TmfContext[] traces = context.getContexts();
577// rank += context.getRank();
578// TmfEvent event = fTraces[j].getNextEvent(new TmfContext(traces[j]));
e31e01e8
FC
579// System.out.println(" [" + trace.getName() + "] rank: " + context.getRank() + ", timestamp: " + event.getTimestamp());
580// assert (checkpoint.getTimestamp().compareTo(event.getTimestamp(), false) == 0);
581// }
9f584e4c 582// System.out.println("Sum of ranks: " + rank + " (expected: " + i * fIndexPageSize + ")");
e31e01e8
FC
583// }
584// }
585
8c8bf09f
ASL
586 // ------------------------------------------------------------------------
587 // Signal handlers
588 // ------------------------------------------------------------------------
589
590 @TmfSignalHandler
951d134a 591 public void experimentSelected(TmfExperimentSelectedSignal<T> signal) {
e31e01e8
FC
592 fCurrentExperiment = signal.getExperiment();
593// if (signal.getExperiment() == this) {
594// indexExperiment(true);
595// }
8c8bf09f
ASL
596 }
597
598 @TmfSignalHandler
599 public void experimentUpdated(TmfExperimentUpdatedSignal signal) {
e31e01e8 600// indexExperiment(true);
8c8bf09f
ASL
601 }
602
603 @TmfSignalHandler
604 public void traceUpdated(TmfTraceUpdatedSignal signal) {
605 // TODO: Incremental index update
606 synchronized(this) {
607 updateNbEvents();
608 updateTimeRange();
609 }
610 broadcast(new TmfExperimentUpdatedSignal(this, this, signal.getTrace()));
611 }
612
613}
This page took 0.054062 seconds and 5 git commands to generate.