1 /*******************************************************************************
2 * Copyright (c) 2011, 2014 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
.tracecompass
.ctf
.core
.trace
;
16 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
18 import java
.io
.IOException
;
19 import java
.util
.ArrayList
;
20 import java
.util
.Collections
;
21 import java
.util
.HashSet
;
22 import java
.util
.List
;
23 import java
.util
.PriorityQueue
;
26 import org
.eclipse
.tracecompass
.ctf
.core
.CTFException
;
27 import org
.eclipse
.tracecompass
.ctf
.core
.event
.EventDefinition
;
28 import org
.eclipse
.tracecompass
.ctf
.core
.event
.IEventDeclaration
;
29 import org
.eclipse
.tracecompass
.internal
.ctf
.core
.Activator
;
30 import org
.eclipse
.tracecompass
.internal
.ctf
.core
.trace
.StreamInputReaderTimestampComparator
;
32 import com
.google
.common
.collect
.ImmutableSet
;
33 import com
.google
.common
.collect
.ImmutableSet
.Builder
;
36 * A CTF trace reader. Reads the events of a trace.
39 * @author Matthew Khouzam
40 * @author Alexandre Montplaisir
42 public class CTFTraceReader
implements AutoCloseable
{
44 private static final int LINE_LENGTH
= 60;
46 private static final int MIN_PRIO_SIZE
= 16;
48 // ------------------------------------------------------------------------
50 // ------------------------------------------------------------------------
53 * The trace to read from.
55 private final CTFTrace fTrace
;
58 * Vector of all the trace file readers.
60 private final List
<CTFStreamInputReader
> fStreamInputReaders
= Collections
.synchronizedList(new ArrayList
<CTFStreamInputReader
>());
63 * Priority queue to order the trace file readers by timestamp.
65 private PriorityQueue
<CTFStreamInputReader
> fPrio
;
68 * Array to count the number of event per trace file.
70 private long[] fEventCountPerTraceFile
;
73 * Timestamp of the first event in the trace
75 private long fStartTime
;
78 * Timestamp of the last event read so far
80 private long fEndTime
;
83 * Boolean to indicate if the CTFTraceReader has been closed
85 private boolean fClosed
= false;
87 // ------------------------------------------------------------------------
89 // ------------------------------------------------------------------------
92 * Constructs a TraceReader to read a trace.
95 * The trace to read from.
96 * @throws CTFException
99 public CTFTraceReader(CTFTrace trace
) throws CTFException
{
101 fStreamInputReaders
.clear();
104 * Create the trace file readers.
106 createStreamInputReaders();
109 * Populate the timestamp-based priority queue.
111 populateStreamInputReaderHeap();
114 * Get the start Time of this trace bear in mind that the trace could be
118 if (hasMoreEvents()) {
119 EventDefinition currentEvent
= getTopStream().getCurrentEvent();
120 if (currentEvent
!= null) {
121 fStartTime
= currentEvent
.getTimestamp();
122 setEndTime(fStartTime
);
130 * @return The new CTFTraceReader
131 * @throws CTFException
134 public CTFTraceReader
copyFrom() throws CTFException
{
135 CTFTraceReader newReader
= null;
137 newReader
= new CTFTraceReader(fTrace
);
138 newReader
.fStartTime
= fStartTime
;
139 newReader
.setEndTime(fEndTime
);
144 * Dispose the CTFTraceReader
147 public void close() {
148 synchronized (fStreamInputReaders
) {
149 for (CTFStreamInputReader reader
: fStreamInputReaders
) {
150 if (reader
!= null) {
153 } catch (IOException e
) {
154 Activator
.logError(e
.getMessage(), e
);
158 fStreamInputReaders
.clear();
164 // ------------------------------------------------------------------------
165 // Getters/Setters/Predicates
166 // ------------------------------------------------------------------------
169 * Return the start time of this trace (== timestamp of the first event)
171 * @return the trace start time
173 public long getStartTime() {
178 * Set the trace's end time
181 * The end time to use
183 protected final void setEndTime(long endTime
) {
188 * Get the priority queue of this trace reader.
190 * @return The priority queue of input readers
192 protected PriorityQueue
<CTFStreamInputReader
> getPrio() {
196 // ------------------------------------------------------------------------
198 // ------------------------------------------------------------------------
201 * Creates one trace file reader per trace file contained in the trace.
203 * @throws CTFException
206 private void createStreamInputReaders() throws CTFException
{
210 for (CTFStream stream
: fTrace
.getStreams()) {
211 Set
<CTFStreamInput
> streamInputs
= stream
.getStreamInputs();
214 * For each trace file of the stream.
216 for (CTFStreamInput streamInput
: streamInputs
) {
219 * Create a reader and add it to the group.
221 fStreamInputReaders
.add(new CTFStreamInputReader(checkNotNull(streamInput
)));
226 * Create the array to count the number of event per trace file.
228 fEventCountPerTraceFile
= new long[fStreamInputReaders
.size()];
232 * Returns whether or not this CTFTraceReader has been closed
234 * @return true if it has been closed, false else
237 public boolean isClosed() {
242 * Update the priority queue to make it match the parent trace
244 * @throws CTFException
247 public void update() throws CTFException
{
248 Set
<CTFStreamInputReader
> readers
= new HashSet
<>();
249 for (CTFStream stream
: fTrace
.getStreams()) {
250 Set
<CTFStreamInput
> streamInputs
= stream
.getStreamInputs();
251 for (CTFStreamInput streamInput
: streamInputs
) {
255 CTFStreamInputReader streamInputReader
= new CTFStreamInputReader(checkNotNull(streamInput
));
258 * Add it to the group.
260 if (!fStreamInputReaders
.contains(streamInputReader
)) {
261 streamInputReader
.readNextEvent();
262 fStreamInputReaders
.add(streamInputReader
);
263 readers
.add(streamInputReader
);
267 long[] temp
= fEventCountPerTraceFile
;
268 fEventCountPerTraceFile
= new long[readers
.size() + temp
.length
];
269 for (CTFStreamInputReader reader
: readers
) {
272 for (int i
= 0; i
< temp
.length
; i
++) {
273 fEventCountPerTraceFile
[i
] = temp
[i
];
278 * Gets an iterable of the stream input readers, useful for foreaches
280 * @return the iterable of the stream input readers
282 public Iterable
<IEventDeclaration
> getEventDeclarations() {
283 ImmutableSet
.Builder
<IEventDeclaration
> builder
= new Builder
<>();
284 for (CTFStreamInputReader sir
: fStreamInputReaders
) {
285 builder
.addAll(sir
.getEventDeclarations());
287 return builder
.build();
291 * Initializes the priority queue used to choose the trace file with the
292 * lower next event timestamp.
294 * @throws CTFException
297 private void populateStreamInputReaderHeap() throws CTFException
{
298 if (fStreamInputReaders
.isEmpty()) {
299 fPrio
= new PriorityQueue
<>(MIN_PRIO_SIZE
,
300 new StreamInputReaderTimestampComparator());
305 * Create the priority queue with a size twice as bigger as the number
306 * of reader in order to avoid constant resizing.
308 fPrio
= new PriorityQueue
<>(
309 Math
.max(fStreamInputReaders
.size() * 2, MIN_PRIO_SIZE
),
310 new StreamInputReaderTimestampComparator());
314 for (CTFStreamInputReader reader
: fStreamInputReaders
) {
316 * Add each trace file reader in the priority queue, if we are able
317 * to read an event from it.
319 CTFResponse readNextEvent
= reader
.readNextEvent();
320 if (readNextEvent
== CTFResponse
.OK
|| readNextEvent
== CTFResponse
.WAIT
) {
323 fEventCountPerTraceFile
[pos
] = 0;
332 * Get the current event, which is the current event of the trace file
333 * reader with the lowest timestamp.
335 * @return An event definition, or null of the trace reader reached the end
338 public EventDefinition
getCurrentEventDef() {
339 CTFStreamInputReader top
= getTopStream();
340 return (top
!= null) ? top
.getCurrentEvent() : null;
344 * Go to the next event.
346 * @return True if an event was read.
347 * @throws CTFException
350 public boolean advance() throws CTFException
{
352 * Remove the reader from the top of the priority queue.
354 CTFStreamInputReader top
= fPrio
.poll();
357 * If the queue was empty.
363 * Read the next event of this reader.
365 switch (top
.readNextEvent()) {
368 * Add it back in the queue.
372 * We're in OK, there's a guaranteed top#getCurrentEvent() unless another
373 * thread does something bad.
375 EventDefinition currentEvent
= checkNotNull(top
.getCurrentEvent());
376 final long topEnd
= fTrace
.timestampCyclesToNanos(currentEvent
.getTimestamp());
377 setEndTime(Math
.max(topEnd
, getEndTime()));
378 fEventCountPerTraceFile
[top
.getName()]++;
379 fEndTime
= Math
.max(currentEvent
.getTimestamp(), fEndTime
);
390 // something bad happend
393 * If there is no reader in the queue, it means the trace reader reached
394 * the end of the trace.
396 return hasMoreEvents();
400 * Go to the last event in the trace.
402 * @throws CTFException
405 public void goToLastEvent() throws CTFException
{
407 while (fPrio
.size() > 1) {
413 * Seeks to a given timestamp. It will seek to the nearest event greater or
414 * equal to timestamp. If a trace is [10 20 30 40] and you are looking for
415 * 19, it will give you 20. If you want 20, you will get 20, if you want 21,
416 * you will get 30. The value -inf will seek to the first element and the
417 * value +inf will seek to the end of the file (past the last event).
420 * the timestamp to seek to
421 * @return true if there are events above or equal the seek timestamp, false
422 * if seek at the end of the trace (no valid event).
423 * @throws CTFException
426 public boolean seek(long timestamp
) throws CTFException
{
428 * Remove all the trace readers from the priority queue
431 for (CTFStreamInputReader streamInputReader
: fStreamInputReaders
) {
433 * Seek the trace reader.
435 streamInputReader
.seek(timestamp
);
438 * Add it to the priority queue if there is a current event.
440 if (streamInputReader
.getCurrentEvent() != null) {
441 fPrio
.add(streamInputReader
);
444 return hasMoreEvents();
448 * Gets the stream with the oldest event
450 * @return the stream with the oldest event
452 public CTFStreamInputReader
getTopStream() {
457 * Does the trace have more events?
459 * @return true if yes.
461 public final boolean hasMoreEvents() {
462 return fPrio
.size() > 0;
466 * Prints the event count stats.
468 public void printStats() {
469 printStats(LINE_LENGTH
);
473 * Prints the event count stats.
476 * Width of the display.
478 public void printStats(int width
) {
484 for (long i
: fEventCountPerTraceFile
) {
488 for (int j
= 0; j
< fEventCountPerTraceFile
.length
; j
++) {
489 CTFStreamInputReader se
= fStreamInputReaders
.get(j
);
491 long len
= (width
* fEventCountPerTraceFile
[se
.getName()])
494 StringBuilder sb
= new StringBuilder(se
.getFilename());
495 sb
.append("\t["); //$NON-NLS-1$
497 for (int i
= 0; i
< len
; i
++) {
501 for (long i
= len
; i
< width
; i
++) {
505 sb
.append("]\t" + fEventCountPerTraceFile
[se
.getName()] + " Events"); //$NON-NLS-1$//$NON-NLS-2$
506 Activator
.log(sb
.toString());
511 * Gets the last event timestamp that was read. This is NOT necessarily the
512 * last event in a trace, just the last one read so far.
514 * @return the last event
516 public long getEndTime() {
521 * Sets a trace to be live or not
524 * whether the trace is live
526 public void setLive(boolean live
) {
527 for (CTFStreamInputReader s
: fPrio
) {
533 * Get if the trace is to read live or not
535 * @return whether the trace is live or not
537 public boolean isLive() {
538 return getTopStream().isLive();
542 public int hashCode() {
543 final int prime
= 31;
545 result
= (prime
* result
) + (int) (fStartTime ^
(fStartTime
>>> 32));
546 result
= (prime
* result
) + fStreamInputReaders
.hashCode();
547 result
= (prime
* result
) + ((fTrace
== null) ?
0 : fTrace
.hashCode());
552 public boolean equals(Object obj
) {
559 if (!(obj
instanceof CTFTraceReader
)) {
562 CTFTraceReader other
= (CTFTraceReader
) obj
;
563 if (!fStreamInputReaders
.equals(other
.fStreamInputReaders
)) {
566 if (fTrace
== null) {
567 if (other
.fTrace
!= null) {
570 } else if (!fTrace
.equals(other
.fTrace
)) {
577 public String
toString() {
578 /* Only for debugging, shouldn't be externalized */
579 return "CTFTraceReader [trace=" + fTrace
+ ']'; //$NON-NLS-1$
583 * Gets the parent trace
585 * @return the parent trace
587 public CTFTrace
getTrace() {
592 * This will read the entire trace and populate all the indexes. The reader
593 * will then be reset to the first event in the trace.
595 * Do not call in the fast path.
597 * @throws CTFException
598 * A trace reading error occurred
601 public void populateIndex() throws CTFException
{
602 for (CTFStreamInputReader sir
: fPrio
) {