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
;
16 import java
.io
.FileNotFoundException
;
17 import java
.util
.Collections
;
18 import java
.util
.Vector
;
20 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
21 import org
.eclipse
.core
.runtime
.IStatus
;
22 import org
.eclipse
.core
.runtime
.Status
;
23 import org
.eclipse
.core
.runtime
.jobs
.Job
;
24 import org
.eclipse
.linuxtools
.tmf
.component
.TmfComponent
;
25 import org
.eclipse
.linuxtools
.tmf
.event
.TmfEvent
;
26 import org
.eclipse
.linuxtools
.tmf
.event
.TmfTimeRange
;
27 import org
.eclipse
.linuxtools
.tmf
.event
.TmfTimestamp
;
28 import org
.eclipse
.linuxtools
.tmf
.request
.ITmfRequestHandler
;
29 import org
.eclipse
.linuxtools
.tmf
.request
.TmfDataRequest
;
32 * <b><u>TmfTrace</u></b>
34 * Abstract implementation of ITmfTrace. It should be sufficient to extend this
35 * class and provide implementation for <code>getCurrentLocation()</code> and
36 * <code>seekLocation()</code>, as well as a proper parser, to have a working
37 * concrete implementation.
39 * TODO: Add support for live streaming (notifications, incremental indexing, ...)
41 public abstract class TmfTrace
extends TmfComponent
implements ITmfTrace
, ITmfRequestHandler
<TmfEvent
> {
43 // ========================================================================
45 // ========================================================================
47 // The default number of events to cache
48 public static final int DEFAULT_CACHE_SIZE
= 1000;
50 // ========================================================================
52 // ========================================================================
55 private final String fPath
;
58 private final String fName
;
60 // The cache page size AND checkpoints interval
61 private final int fCacheSize
;
63 // Indicate if the stream should be pre-indexed
64 private final boolean fWaitForIndexCompletion
;
66 // The set of event stream checkpoints (for random access)
67 protected Vector
<TmfTraceCheckpoint
> fCheckpoints
= new Vector
<TmfTraceCheckpoint
>();
69 // The number of events collected
70 private long fNbEvents
= 0;
72 // The time span of the event stream
73 private TmfTimeRange fTimeRange
= new TmfTimeRange(TmfTimestamp
.BigBang
, TmfTimestamp
.BigBang
);
75 // ========================================================================
77 // ========================================================================
83 * @throws FileNotFoundException
85 protected TmfTrace(String path
, int pageSize
, boolean waitForIndexCompletion
) throws FileNotFoundException
{
87 int sep
= path
.lastIndexOf(File
.separator
);
88 fName
= (sep
>= 0) ? path
.substring(sep
+ 1) : path
;
90 fCacheSize
= (pageSize
> 0) ? pageSize
: DEFAULT_CACHE_SIZE
;
91 fWaitForIndexCompletion
= waitForIndexCompletion
;
97 * @throws FileNotFoundException
99 protected TmfTrace(String name
, boolean waitForIndexCompletion
) throws FileNotFoundException
{
100 this(name
, DEFAULT_CACHE_SIZE
, waitForIndexCompletion
);
106 * @throws FileNotFoundException
108 protected TmfTrace(String name
, int pageSize
) throws FileNotFoundException
{
109 this(name
, pageSize
, false);
114 * @throws FileNotFoundException
116 protected TmfTrace(String name
) throws FileNotFoundException
{
117 this(name
, DEFAULT_CACHE_SIZE
, false);
120 // ========================================================================
122 // ========================================================================
125 * @return the trace path
127 public String
getPath() {
132 * @return the trace name
134 public String
getName() {
139 * @see org.eclipse.linuxtools.tmf.stream.ITmfEventStream#getNbEvents()
141 public long getNbEvents() {
146 * @return the size of the cache
148 public int getCacheSize() {
153 * @see org.eclipse.linuxtools.tmf.stream.ITmfEventStream#getTimeRange()
155 public TmfTimeRange
getTimeRange() {
159 public TmfTimestamp
getStartTime() {
160 return fTimeRange
.getStartTime();
163 public TmfTimestamp
getEndTime() {
164 return fTimeRange
.getEndTime();
167 protected long getIndex(TmfTimestamp timestamp
) {
168 TmfTraceContext context
= seekEvent(timestamp
);
169 return context
.getIndex();
172 protected TmfTimestamp
getTimestamp(int index
) {
173 TmfTraceContext context
= seekEvent(index
);
174 return context
.getTimestamp();
177 // ========================================================================
179 // ========================================================================
181 protected void setTimeRange(TmfTimeRange range
) {
185 protected void setStartTime(TmfTimestamp startTime
) {
186 fTimeRange
= new TmfTimeRange(startTime
, fTimeRange
.getEndTime());
189 protected void setEndTime(TmfTimestamp endTime
) {
190 fTimeRange
= new TmfTimeRange(fTimeRange
.getStartTime(), endTime
);
194 * @see org.eclipse.linuxtools.tmf.trace.ITmfTrace#seekEvent(org.eclipse.linuxtools.tmf.event.TmfTimestamp)
196 public TmfTraceContext
seekEvent(TmfTimestamp timestamp
) {
198 if (timestamp
== null) {
199 timestamp
= TmfTimestamp
.BigBang
;
202 // First, find the right checkpoint
203 int index
= Collections
.binarySearch(fCheckpoints
, new TmfTraceCheckpoint(timestamp
, 0));
205 // In the very likely case that the checkpoint was not found, bsearch
206 // returns its negated would-be location (not an offset...). From that
207 // index, we can then position the stream and get the event.
209 index
= Math
.max(0, -(index
+ 2));
212 // Position the stream at the checkpoint
214 synchronized (fCheckpoints
) { //Just in case we are re-indexing
215 location
= (index
< fCheckpoints
.size()) ? fCheckpoints
.elementAt(index
).getLocation() : null;
217 TmfTraceContext nextEventContext
= seekLocation(location
);
218 nextEventContext
.setIndex(index
* fCacheSize
);
219 TmfTraceContext currentEventContext
= new TmfTraceContext(nextEventContext
);
222 TmfEvent event
= getNextEvent(nextEventContext
);
223 while (event
!= null && event
.getTimestamp().compareTo(timestamp
, false) < 0) {
224 currentEventContext
.setLocation(nextEventContext
.getLocation());
225 currentEventContext
.incrIndex();
226 event
= getNextEvent(nextEventContext
);
229 currentEventContext
.setTimestamp((event
!= null) ? event
.getTimestamp() : null);
230 return currentEventContext
;
234 * @see org.eclipse.linuxtools.tmf.trace.ITmfTrace#seekEvent(int)
236 public TmfTraceContext
seekEvent(long position
) {
238 // Position the stream at the previous checkpoint
239 int index
= (int) position
/ fCacheSize
;
241 synchronized (fCheckpoints
) { //Just in case we are re-indexing
242 location
= (index
< fCheckpoints
.size()) ? fCheckpoints
.elementAt(index
).getLocation() : null;
244 TmfTraceContext nextEventContext
= seekLocation(location
);
245 nextEventContext
.setIndex(index
* fCacheSize
);
246 TmfTraceContext currentEventContext
= new TmfTraceContext(nextEventContext
);
248 // And locate the event (if it exists)
249 TmfEvent event
= getNextEvent(nextEventContext
);
250 while (event
!= null && currentEventContext
.getIndex() < position
) {
251 currentEventContext
.setLocation(nextEventContext
.getLocation());
252 currentEventContext
.setTimestamp(event
.getTimestamp());
253 currentEventContext
.incrIndex();
254 event
= getNextEvent(nextEventContext
);
257 return currentEventContext
;
261 * @see org.eclipse.linuxtools.tmf.trace.ITmfTrace#getNextEvent(org.eclipse.linuxtools.tmf.trace.ITmfTrace.TraceContext)
263 public TmfEvent
getNextEvent(TmfTraceContext context
) {
264 // parseEvent updates the context
265 TmfEvent event
= parseEvent(context
);
273 * To be implemented by the subclass.
275 public abstract Object
getCurrentLocation();
276 public abstract TmfEvent
parseEvent(TmfTraceContext context
);
279 * Hook for "special" processing by the extending class
282 public void processEvent(TmfEvent event
) {
283 // Do nothing by default
286 // ========================================================================
287 // ITmfRequestHandler
288 // ========================================================================
291 * @see org.eclipse.linuxtools.tmf.eventlog.ITmfRequestHandler#processRequest(org.eclipse.linuxtools.tmf.eventlog.TmfDataRequest, boolean)
293 public void processRequest(TmfDataRequest
<TmfEvent
> request
, boolean waitForCompletion
) {
295 // Process the request
296 processDataRequest(request
);
298 // Wait for completion if needed
299 if (waitForCompletion
) {
300 request
.waitForCompletion();
305 * Process a data request
309 private void processDataRequest(final TmfDataRequest
<TmfEvent
> request
) {
311 // Initialize the trace context
312 final TmfTraceContext context
= (request
.getRange() != null) ?
313 seekEvent(request
.getRange().getStartTime()) :
314 seekEvent(request
.getIndex());
316 final TmfTimestamp endTime
= (request
.getRange() != null) ?
317 request
.getRange().getEndTime() :
318 TmfTimestamp
.BigCrunch
;
320 // Process the request
321 Thread thread
= new Thread() {
325 // Extract the general request information
326 int blockSize
= request
.getBlockize();
327 int nbRequestedEvents
= request
.getNbRequestedEvents();
328 if (nbRequestedEvents
== -1) {
329 nbRequestedEvents
= Integer
.MAX_VALUE
;
332 // Create the result buffer
333 Vector
<TmfEvent
> events
= new Vector
<TmfEvent
>();
336 // Get the ordered events
337 TmfEvent event
= getNextEvent(context
);
338 while (!request
.isCancelled() && nbEvents
< nbRequestedEvents
&& event
!= null
339 && event
.getTimestamp().compareTo(endTime
, false) <= 0)
342 if (++nbEvents
% blockSize
== 0) {
343 pushData(request
, events
);
345 // To avoid an unnecessary read passed the last event requested
346 if (nbEvents
< nbRequestedEvents
)
347 event
= getNextEvent(context
);
349 pushData(request
, events
);
357 * Format the result data and notify the requester.
358 * Note: after handling, the data is *removed*.
363 private void pushData(TmfDataRequest
<TmfEvent
> request
, Vector
<TmfEvent
> events
) {
364 TmfEvent
[] result
= new TmfEvent
[events
.size()];
365 events
.toArray(result
);
366 request
.setData(result
);
367 request
.handleData();
368 events
.removeAllElements();
372 * @see java.lang.Object#toString()
375 public String
toString() {
376 return "[TmfTrace (" + fName
+ "]";
379 // ========================================================================
380 // Trace indexing. Essentially, parse the stream asynchronously and build
381 // the checkpoints index. This index is used to quickly find an event based
382 // on a timestamp or an index.
383 // ========================================================================
385 private Boolean fIndexing
= false;
386 public void indexStream() {
387 synchronized (fIndexing
) {
394 final IndexingJob job
= new IndexingJob("Indexing " + fName
);
397 if (fWaitForIndexCompletion
) {
400 } catch (InterruptedException e
) {
401 // TODO Auto-generated catch block
407 private class IndexingJob
extends Job
{
409 public IndexingJob(String name
) {
414 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
417 protected IStatus
run(IProgressMonitor monitor
) {
420 TmfTimestamp startTime
= new TmfTimestamp();
421 TmfTimestamp lastTime
= new TmfTimestamp();
423 monitor
.beginTask("Indexing " + fName
, IProgressMonitor
.UNKNOWN
);
426 // Position the trace at the beginning
427 TmfTraceContext context
= seekLocation(null);
428 if (context
.getTimestamp() == null) {
429 return Status
.OK_STATUS
;
431 // FIXME: LTTng hack - start
432 // fCheckpoints.add(new TmfTraceCheckpoint(context.getTimestamp(), context.getLocation())); // TMF
433 // FIXME: LTTng hack - end
436 startTime
= context
.getTimestamp();
437 lastTime
= context
.getTimestamp();
438 while ((event
= getNextEvent(context
)) != null) {
439 TmfTimestamp timestamp
= context
.getTimestamp();
440 if (timestamp
!= null) {
441 lastTime
= timestamp
;
443 // FIXME: LTTng hack - start
444 // if (((++nbEvents % fCacheSize) == 0) && (timestamp != null)) { // TMF
445 if (((nbEvents
++ % fCacheSize
) == 0) && (timestamp
!= null)) { // LTTng
446 // FIXME: LTTng hack - end
447 fCheckpoints
.add(new TmfTraceCheckpoint(timestamp
, context
.getLocation()));
448 fNbEvents
= nbEvents
- 1;
449 lastTime
= context
.getTimestamp();
450 fTimeRange
= new TmfTimeRange(startTime
, lastTime
);
451 notifyListeners(new TmfTimeRange(startTime
, lastTime
));
455 // Check monitor *after* fCheckpoints has been updated
456 if (monitor
.isCanceled()) {
458 return Status
.CANCEL_STATUS
;
468 fNbEvents
= nbEvents
;
469 fTimeRange
= new TmfTimeRange(startTime
, lastTime
);
472 notifyListeners(new TmfTimeRange(startTime
, lastTime
));
477 // createOffsetsFile();
478 // dumpCheckpoints();
480 return Status
.OK_STATUS
;
484 private void notifyListeners(TmfTimeRange range
) {
485 broadcastSignal(new TmfTraceUpdatedSignal(this, this, range
));
489 // * Dump the trace checkpoints
491 // private void dumpCheckpoints() {
492 // System.out.println("-----");
493 // System.out.println("Checkpoints of " + fName);
494 // for (int i = 0; i < fCheckpoints.size(); i++) {
495 // TmfTraceCheckpoint checkpoint = fCheckpoints.get(i);
496 // TmfTraceContext context = new TmfTraceContext(checkpoint.getLocation());
497 // TmfEvent event = getNextEvent(context);
498 // System.out.println(" Entry: " + i + " timestamp: " + checkpoint.getTimestamp() + ", event: " + event.getTimestamp());
499 // assert((checkpoint.getTimestamp().compareTo(event.getTimestamp(), false) == 0));
501 // System.out.println();
504 // private void createOffsetsFile() {
507 // ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("LTTngOffsets.dat")));
509 // TmfTraceContext context = null;
510 // context = seekLocation(null);
511 // out.writeObject(context.getLocation());
514 // while (getNextEvent(context) != null) {
515 // out.writeObject(context.getLocation());
519 // System.out.println("TmfTrace wrote " + nbEvents + " events");
520 // } catch (FileNotFoundException e) {
521 // // TODO Auto-generated catch block
522 // e.printStackTrace();
523 // } catch (IOException e) {
524 // // TODO Auto-generated catch block
525 // e.printStackTrace();
529 // private void createOffsetsFile() {
532 // DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("LTTngOffsets.dat")));
534 // TmfTraceContext context = null;
535 // context = seekLocation(null);
539 // while ((event = getNextEvent(context)) != null) {
540 // out.writeUTF(event.getTimestamp().toString());
544 // System.out.println("TmfTrace wrote " + nbEvents + " events");
545 // } catch (FileNotFoundException e) {
546 // // TODO Auto-generated catch block
547 // e.printStackTrace();
548 // } catch (IOException e) {
549 // // TODO Auto-generated catch block
550 // e.printStackTrace();