1 /*******************************************************************************
2 * Copyright (c) 2011-2012 Ericsson, Ecole Polytechnique de Montreal and others
4 * All rights reserved. This program and the accompanying materials are made
5 * 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
9 * Contributors: Matthew Khouzam - Initial API and implementation
10 * Contributors: Alexandre Montplaisir - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.linuxtools
.ctf
.core
.trace
;
15 import java
.util
.Collection
;
16 import java
.util
.HashMap
;
17 import java
.util
.PriorityQueue
;
19 import java
.util
.Vector
;
21 import org
.eclipse
.linuxtools
.ctf
.core
.event
.EventDefinition
;
22 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.Activator
;
23 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.trace
.Stream
;
24 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.trace
.StreamInput
;
25 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.trace
.StreamInputPacketIndexEntry
;
26 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.trace
.StreamInputReaderTimestampComparator
;
29 * Reads the events of a trace.
32 public class CTFTraceReader
{
34 // ------------------------------------------------------------------------
36 // ------------------------------------------------------------------------
39 * The trace to read from.
41 private final CTFTrace trace
;
44 * Vector of all the trace file readers.
46 private final Vector
<StreamInputReader
> streamInputReaders
= new Vector
<StreamInputReader
>();
49 * Priority queue to order the trace file readers by timestamp.
51 protected PriorityQueue
<StreamInputReader
> prio
;
54 * Array to count the number of event per trace file.
56 private int[] eventCountPerTraceFile
;
59 * Timestamp of the first event in the trace
61 private long startTime
;
64 * Timestamp of the last event read so far
69 protected void setEndTime(long endTime
) {
70 this.endTime
= endTime
;
78 private final HashMap
<Integer
, Long
> startIndex
;
80 // ------------------------------------------------------------------------
82 // ------------------------------------------------------------------------
85 * Constructs a TraceReader to read a trace.
88 * The trace to read from.
89 * @throws CTFReaderException
91 public CTFTraceReader(CTFTrace trace
) {
95 * Create the trace file readers.
97 createStreamInputReaders();
100 * Populate the timestamp-based priority queue.
102 populateStreamInputReaderHeap();
105 * Get the start Time of this trace
106 * bear in mind that the trace could be empty.
108 this.startTime
= 0;// prio.peek().getPacketReader().getCurrentPacket().getTimestampBegin();
109 if (hasMoreEvents()) {
110 this.startTime
= prio
.peek().getCurrentEvent().getTimestamp();
111 this.setEndTime(this.startTime
);
114 startIndex
= new HashMap
<Integer
, Long
>();
120 public CTFTraceReader
copyFrom() {
121 CTFTraceReader newReader
= null;
123 newReader
= new CTFTraceReader(this.trace
);
124 newReader
.startTime
= this.startTime
;
125 newReader
.setEndTime(this.endTime
);
129 // ------------------------------------------------------------------------
130 // Getters/Setters/Predicates
131 // ------------------------------------------------------------------------
134 * Return the start time of this trace (== timestamp of the first event)
136 * @return the trace start time
138 public long getStartTime() {
139 return this.startTime
;
145 public long getIndex() {
149 // ------------------------------------------------------------------------
151 // ------------------------------------------------------------------------
154 * Creates one trace file reader per trace file contained in the trace.
156 private void createStreamInputReaders() {
157 Collection
<Stream
> streams
= this.trace
.getStreams().values();
162 for (Stream stream
: streams
) {
163 Set
<StreamInput
> streamInputs
= stream
.getStreamInputs();
166 * For each trace file of the stream.
168 for (StreamInput streamInput
: streamInputs
) {
172 StreamInputReader streamInputReader
= new StreamInputReader(
176 * Add it to the group.
178 this.streamInputReaders
.add(streamInputReader
);
183 * Create the array to count the number of event per trace file.
185 this.eventCountPerTraceFile
= new int[this.streamInputReaders
.size()];
189 * Initializes the priority queue used to choose the trace file with the
190 * lower next event timestamp.
192 private void populateStreamInputReaderHeap() {
194 * Create the priority queue with a size twice as bigger as the number
195 * of reader in order to avoid constant resizing.
197 this.prio
= new PriorityQueue
<StreamInputReader
>(
198 this.streamInputReaders
.size() * 2,
199 new StreamInputReaderTimestampComparator());
203 for (StreamInputReader reader
: this.streamInputReaders
) {
205 * Add each trace file reader in the priority queue, if we are able
206 * to read an event from it.
208 if (reader
.readNextEvent()) {
209 this.prio
.add(reader
);
211 this.eventCountPerTraceFile
[pos
] = 0;
220 * Get the current event, which is the current event of the trace file
221 * reader with the lowest timestamp.
223 * @return An event definition, or null of the trace reader reached the end
226 public EventDefinition
getCurrentEventDef() {
227 StreamInputReader top
= getTopStream();
229 return (top
!= null) ? top
.getCurrentEvent() : null;
233 * Go to the next event.
235 * @return True if an event was read.
237 public boolean advance() {
242 * Remove the reader from the top of the priority queue.
244 StreamInputReader top
= this.prio
.poll();
247 * If the queue was empty.
255 if (hasMoreEvents()) {
256 StreamInputPacketReader packetReader
= top
.getPacketReader();
257 boolean packetHasMoreEvents
= packetReader
.hasMoreEvents();
258 StreamInputPacketIndexEntry currentPacket
= packetReader
260 if (!packetHasMoreEvents
) {
261 int n
= this.streamInputReaders
.indexOf(top
);
262 if (!startIndex
.containsKey(n
)) {
263 startIndex
.put(n
, 0L);
265 currentPacket
.setIndexBegin(startIndex
.get(n
));
266 currentPacket
.setIndexEnd(fIndex
);
267 startIndex
.put(n
, fIndex
+ 1);
271 * Read the next event of this reader.
273 if (top
.readNextEvent()) {
275 * Add it back in the queue.
278 final long topEnd
= top
.getCurrentEvent().getTimestamp() + this.getTrace().getOffset();
279 this.setEndTime( Math
.max(topEnd
, this.getEndTime()));
280 this.eventCountPerTraceFile
[top
.getName()]++;
282 * increment the index
286 boolean hasMoreEvents
= hasMoreEvents();
289 * If there is no reader in the queue, it means the trace reader reached
290 * the end of the trace.
292 return hasMoreEvents
;
296 * Go to the last event in the trace.
298 * @throws CTFReaderException
300 public void goToLastEvent() throws CTFReaderException
{
302 for (StreamInputReader streamInputReader
: this.streamInputReaders
) {
304 * Seek the trace reader.
306 streamInputReader
.goToLastEvent();
308 int count
= prio
.size();
309 for (int i
= 0; i
< (count
- 1); i
++) {
311 * Cull all the streams aside from the last one
318 * Seeks to a given timestamp It will go to the event just after the
319 * timestamp or the timestamp itself. if a if a trace is 10 20 30 40 and
320 * you're looking for 19, it'll give you 20, it you want 20, you'll get 20,
321 * if you want 21, you'll get 30. You want -inf, you'll get the first
322 * element, you want +inf, you'll get the end of the file with no events.
325 * the timestamp to seek to
326 * @return true if the trace has more events following the timestamp
328 public boolean seek(long timestamp
) {
330 * Remove all the trace readers from the priority queue
335 for (StreamInputReader streamInputReader
: this.streamInputReaders
) {
337 * Seek the trace reader.
339 offset
+= streamInputReader
.seek(timestamp
);
342 * Add it to the priority queue if there is a current event.
346 for (StreamInputReader streamInputReader
: this.streamInputReaders
) {
347 if (streamInputReader
.getCurrentEvent() != null) {
348 this.prio
.add(streamInputReader
);
349 fIndex
= Math
.max(fIndex
, streamInputReader
.getPacketReader()
350 .getCurrentPacket().getIndexBegin()
354 return hasMoreEvents();
357 public boolean seekIndex(long index
) {
360 long tempIndex
= Long
.MIN_VALUE
;
361 long tempTimestamp
= Long
.MIN_VALUE
;
363 for (StreamInputReader streamInputReader
: this.streamInputReaders
) {
365 * Seek the trace reader.
367 final long streamIndex
= streamInputReader
.seekIndex(index
);
368 if (streamInputReader
.getCurrentEvent() != null) {
369 tempIndex
= Math
.max(tempIndex
, streamIndex
);
370 EventDefinition currentEvent
= streamInputReader
373 * Maybe we're at the beginning of a trace.
375 if (currentEvent
== null) {
376 streamInputReader
.readNextEvent();
377 currentEvent
= streamInputReader
.getCurrentEvent();
379 if (currentEvent
!= null) {
380 tempTimestamp
= Math
.max(tempTimestamp
,
381 currentEvent
.getTimestamp());
384 * probably beyond the last event
386 tempIndex
= goToZero();
391 } catch (CTFReaderException e
) {
393 * Important, if it failed, it's because it's not yet indexed, so we
394 * have to manually advance to the right value.
396 tempIndex
= goToZero();
398 for (StreamInputReader streamInputReader
: this.streamInputReaders
) {
400 * Add it to the priority queue if there is a current event.
403 if (streamInputReader
.getCurrentEvent() != null) {
404 this.prio
.add(streamInputReader
);
407 if (tempIndex
== Long
.MAX_VALUE
) {
410 long pos
= tempIndex
;
411 if (index
> tempIndex
) {
415 while ((prio
.peek().getCurrentEvent().getTimestamp() < tempTimestamp
)
416 && hasMoreEvents()) {
420 for (pos
= tempIndex
; (pos
< index
) && hasMoreEvents(); pos
++) {
425 return hasMoreEvents();
429 * Go to the first entry of a trace
431 * @return 0, the first index.
433 private long goToZero() {
435 for (StreamInputReader streamInputReader
: this.streamInputReaders
) {
437 * Seek the trace reader.
439 streamInputReader
.seek(0);
445 public StreamInputReader
getTopStream() {
446 return this.prio
.peek();
450 * Does the trace have more events?
452 * @return true if yes.
454 public boolean hasMoreEvents() {
455 return this.prio
.size() > 0;
459 * Prints the event count stats.
461 public void printStats() {
466 * Prints the event count stats.
469 * Width of the display.
471 public void printStats(int width
) {
477 for (int i
: this.eventCountPerTraceFile
) {
481 for (int j
= 0; j
< this.eventCountPerTraceFile
.length
; j
++) {
482 StreamInputReader se
= this.streamInputReaders
.get(j
);
484 int len
= (width
* this.eventCountPerTraceFile
[se
.getName()])
487 StringBuilder sb
= new StringBuilder(se
.getFilename() + "\t["); //$NON-NLS-1$
489 for (int i
= 0; i
< len
; i
++) {
493 for (int i
= len
; i
< width
; i
++) {
497 sb
.append("]\t" + this.eventCountPerTraceFile
[se
.getName()] + " Events"); //$NON-NLS-1$//$NON-NLS-2$
498 Activator
.getDefault().log(sb
.toString());
502 public long getEndTime() {
507 public int hashCode() {
508 final int prime
= 31;
510 result
= (prime
* result
) + (int) (endTime ^
(endTime
>>> 32));
511 result
= (prime
* result
) + (int) (startTime ^
(startTime
>>> 32));
512 result
= (prime
* result
)
513 + ((streamInputReaders
== null) ?
0 : streamInputReaders
515 result
= (prime
* result
) + ((trace
== null) ?
0 : trace
.hashCode());
520 public boolean equals(Object obj
) {
527 if (!(obj
instanceof CTFTraceReader
)) {
530 CTFTraceReader other
= (CTFTraceReader
) obj
;
531 if (endTime
!= other
.endTime
) {
534 if (startTime
!= other
.startTime
) {
537 if (streamInputReaders
== null) {
538 if (other
.streamInputReaders
!= null) {
541 } else if (!streamInputReaders
.equals(other
.streamInputReaders
)) {
545 if (other
.trace
!= null) {
548 } else if (!trace
.equals(other
.trace
)) {
557 * @see java.lang.Object#toString()
560 public String
toString() {
561 /* Only for debugging, shouldn't be externalized */
562 return "CTFTraceReader [trace=" + trace
+ ']'; //$NON-NLS-1$
565 public CTFTrace
getTrace() {