1 /*******************************************************************************
2 * Copyright (c) 2011, 2013 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
10 * Matthew Khouzam - Initial API and implementation
11 * Alexandre Montplaisir - Initial API and implementation
12 *******************************************************************************/
14 package org
.eclipse
.linuxtools
.ctf
.core
.trace
;
16 import java
.util
.ArrayList
;
17 import java
.util
.Collection
;
18 import java
.util
.List
;
19 import java
.util
.PriorityQueue
;
22 import org
.eclipse
.linuxtools
.ctf
.core
.event
.EventDefinition
;
23 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.Activator
;
24 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.trace
.StreamInputReaderTimestampComparator
;
27 * A CTF trace reader. Reads the events of a trace.
30 * @author Matthew Khouzam
31 * @author Alexandre Montplaisir
33 public class CTFTraceReader
{
35 // ------------------------------------------------------------------------
37 // ------------------------------------------------------------------------
40 * The trace to read from.
42 private final CTFTrace trace
;
45 * Vector of all the trace file readers.
47 private final List
<StreamInputReader
> streamInputReaders
= new ArrayList
<StreamInputReader
>();
50 * Priority queue to order the trace file readers by timestamp.
52 private PriorityQueue
<StreamInputReader
> prio
;
55 * Array to count the number of event per trace file.
57 private long[] eventCountPerTraceFile
;
60 * Timestamp of the first event in the trace
62 private long startTime
;
65 * Timestamp of the last event read so far
69 // ------------------------------------------------------------------------
71 // ------------------------------------------------------------------------
74 * Constructs a TraceReader to read a trace.
77 * The trace to read from.
79 public CTFTraceReader(CTFTrace trace
) {
81 streamInputReaders
.clear();
84 * Create the trace file readers.
86 createStreamInputReaders();
89 * Populate the timestamp-based priority queue.
91 populateStreamInputReaderHeap();
94 * Get the start Time of this trace bear in mind that the trace could be
98 if (hasMoreEvents()) {
99 this.startTime
= prio
.peek().getCurrentEvent().getTimestamp();
100 this.setEndTime(this.startTime
);
107 * @return The new CTFTraceReader
109 public CTFTraceReader
copyFrom() {
110 CTFTraceReader newReader
= null;
112 newReader
= new CTFTraceReader(this.trace
);
113 newReader
.startTime
= this.startTime
;
114 newReader
.setEndTime(this.endTime
);
119 * Dispose the CTFTraceReader
122 public void dispose() {
123 for (StreamInputReader reader
: streamInputReaders
) {
124 if (reader
!= null) {
128 streamInputReaders
.clear();
131 // ------------------------------------------------------------------------
132 // Getters/Setters/Predicates
133 // ------------------------------------------------------------------------
136 * Return the start time of this trace (== timestamp of the first event)
138 * @return the trace start time
140 public long getStartTime() {
141 return this.startTime
;
145 * Set the trace's end time
148 * The end time to use
150 protected final void setEndTime(long endTime
) {
151 this.endTime
= endTime
;
155 * Get the priority queue of this trace reader.
157 * @return The priority queue of input readers
160 protected PriorityQueue
<StreamInputReader
> getPrio() {
165 // ------------------------------------------------------------------------
167 // ------------------------------------------------------------------------
170 * Creates one trace file reader per trace file contained in the trace.
172 private void createStreamInputReaders() {
173 Collection
<Stream
> streams
= this.trace
.getStreams().values();
178 for (Stream stream
: streams
) {
179 Set
<StreamInput
> streamInputs
= stream
.getStreamInputs();
182 * For each trace file of the stream.
184 for (StreamInput streamInput
: streamInputs
) {
188 StreamInputReader streamInputReader
= new StreamInputReader(
192 * Add it to the group.
194 this.streamInputReaders
.add(streamInputReader
);
199 * Create the array to count the number of event per trace file.
201 this.eventCountPerTraceFile
= new long[this.streamInputReaders
.size()];
205 * Initializes the priority queue used to choose the trace file with the
206 * lower next event timestamp.
208 private void populateStreamInputReaderHeap() {
209 if (this.streamInputReaders
.isEmpty()) {
210 this.prio
= new PriorityQueue
<StreamInputReader
>();
215 * Create the priority queue with a size twice as bigger as the number
216 * of reader in order to avoid constant resizing.
218 this.prio
= new PriorityQueue
<StreamInputReader
>(
219 this.streamInputReaders
.size() * 2,
220 new StreamInputReaderTimestampComparator());
224 for (StreamInputReader reader
: this.streamInputReaders
) {
226 * Add each trace file reader in the priority queue, if we are able
227 * to read an event from it.
229 reader
.setParent(this);
230 if (reader
.readNextEvent()) {
231 this.prio
.add(reader
);
233 this.eventCountPerTraceFile
[pos
] = 0;
242 * Get the current event, which is the current event of the trace file
243 * reader with the lowest timestamp.
245 * @return An event definition, or null of the trace reader reached the end
248 public EventDefinition
getCurrentEventDef() {
249 StreamInputReader top
= getTopStream();
251 return (top
!= null) ? top
.getCurrentEvent() : null;
255 * Go to the next event.
257 * @return True if an event was read.
259 public boolean advance() {
264 * Remove the reader from the top of the priority queue.
266 StreamInputReader top
= this.prio
.poll();
269 * If the queue was empty.
275 * Read the next event of this reader.
277 if (top
.readNextEvent()) {
279 * Add it back in the queue.
282 final long topEnd
= this.trace
.timestampCyclesToNanos(top
.getCurrentEvent().getTimestamp());
283 this.setEndTime(Math
.max(topEnd
, this.getEndTime()));
284 this.eventCountPerTraceFile
[top
.getName()]++;
286 if (top
.getCurrentEvent() != null) {
287 this.endTime
= Math
.max(top
.getCurrentEvent().getTimestamp(),
292 * If there is no reader in the queue, it means the trace reader reached
293 * the end of the trace.
295 return hasMoreEvents();
299 * Go to the last event in the trace.
301 public void goToLastEvent() {
302 seek(this.getEndTime());
303 while (this.prio
.size() > 1) {
309 * Seeks to a given timestamp It will go to the event just after the
310 * timestamp or the timestamp itself. if a if a trace is 10 20 30 40 and
311 * you're looking for 19, it'll give you 20, it you want 20, you'll get 20,
312 * if you want 21, you'll get 30. You want -inf, you'll get the first
313 * element, you want +inf, you'll get the end of the file with no events.
316 * the timestamp to seek to
317 * @return true if the trace has more events following the timestamp
319 public boolean seek(long timestamp
) {
321 * Remove all the trace readers from the priority queue
324 for (StreamInputReader streamInputReader
: this.streamInputReaders
) {
326 * Seek the trace reader.
328 streamInputReader
.seek(timestamp
);
331 * Add it to the priority queue if there is a current event.
335 for (StreamInputReader streamInputReader
: this.streamInputReaders
) {
336 if (streamInputReader
.getCurrentEvent() != null) {
337 this.prio
.add(streamInputReader
);
341 return hasMoreEvents();
345 * gets the stream with the oldest event
347 * @return the stream with the oldest event
349 public StreamInputReader
getTopStream() {
350 return this.prio
.peek();
354 * Does the trace have more events?
356 * @return true if yes.
358 public final boolean hasMoreEvents() {
359 return this.prio
.size() > 0;
363 * Prints the event count stats.
365 public void printStats() {
370 * Prints the event count stats.
373 * Width of the display.
375 public void printStats(int width
) {
381 for (long i
: this.eventCountPerTraceFile
) {
385 for (int j
= 0; j
< this.eventCountPerTraceFile
.length
; j
++) {
386 StreamInputReader se
= this.streamInputReaders
.get(j
);
388 long len
= (width
* this.eventCountPerTraceFile
[se
.getName()])
391 StringBuilder sb
= new StringBuilder(se
.getFilename());
392 sb
.append("\t["); //$NON-NLS-1$
394 for (int i
= 0; i
< len
; i
++) {
398 for (long i
= len
; i
< width
; i
++) {
402 sb
.append("]\t" + this.eventCountPerTraceFile
[se
.getName()] + " Events"); //$NON-NLS-1$//$NON-NLS-2$
403 Activator
.log(sb
.toString());
408 * gets the last event timestamp that was read. This is NOT necessarily the
409 * last event in a trace, just the last one read so far.
411 * @return the last event
413 public long getEndTime() {
418 public int hashCode() {
419 final int prime
= 31;
421 result
= (prime
* result
) + (int) (startTime ^
(startTime
>>> 32));
422 result
= (prime
* result
) + streamInputReaders
.hashCode();
423 result
= (prime
* result
) + ((trace
== null) ?
0 : trace
.hashCode());
428 public boolean equals(Object obj
) {
435 if (!(obj
instanceof CTFTraceReader
)) {
438 CTFTraceReader other
= (CTFTraceReader
) obj
;
439 if (!streamInputReaders
.equals(other
.streamInputReaders
)) {
443 if (other
.trace
!= null) {
446 } else if (!trace
.equals(other
.trace
)) {
453 public String
toString() {
454 /* Only for debugging, shouldn't be externalized */
455 return "CTFTraceReader [trace=" + trace
+ ']'; //$NON-NLS-1$
459 * Gets the parent trace
461 * @return the parent trace
463 public CTFTrace
getTrace() {