ctf: Fixed leaked file handle when calling CTFTraceReader.update
[deliverable/tracecompass.git] / ctf / org.eclipse.tracecompass.ctf.core / src / org / eclipse / tracecompass / ctf / core / trace / CTFTraceReader.java
CommitLineData
866e5b51 1/*******************************************************************************
93a45b54 2 * Copyright (c) 2011, 2014 Ericsson, Ecole Polytechnique de Montreal and others
866e5b51
FC
3 *
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
8 *
4311ac8b
MAL
9 * Contributors:
10 * Matthew Khouzam - Initial API and implementation
11 * Alexandre Montplaisir - Initial API and implementation
866e5b51
FC
12 *******************************************************************************/
13
f357bcd4 14package org.eclipse.tracecompass.ctf.core.trace;
866e5b51 15
90cefe9f
MK
16import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
17
b3151232 18import java.io.IOException;
0594c61c 19import java.util.ArrayList;
6a0ec7bc 20import java.util.Collections;
fe75d403 21import java.util.HashSet;
0594c61c 22import java.util.List;
866e5b51
FC
23import java.util.PriorityQueue;
24import java.util.Set;
866e5b51 25
680f9173 26import org.eclipse.tracecompass.ctf.core.CTFException;
f357bcd4
AM
27import org.eclipse.tracecompass.ctf.core.event.EventDefinition;
28import org.eclipse.tracecompass.ctf.core.event.IEventDeclaration;
29import org.eclipse.tracecompass.internal.ctf.core.Activator;
30import org.eclipse.tracecompass.internal.ctf.core.trace.StreamInputReaderTimestampComparator;
ce2388e0 31
866e5b51 32/**
d37aaa7f 33 * A CTF trace reader. Reads the events of a trace.
1d7277f3 34 *
d37aaa7f
FC
35 * @version 1.0
36 * @author Matthew Khouzam
37 * @author Alexandre Montplaisir
866e5b51 38 */
dd9752d5 39public class CTFTraceReader implements AutoCloseable {
866e5b51 40
f068c622
MK
41 private static final int LINE_LENGTH = 60;
42
fe75d403
MK
43 private static final int MIN_PRIO_SIZE = 16;
44
866e5b51
FC
45 // ------------------------------------------------------------------------
46 // Attributes
47 // ------------------------------------------------------------------------
48
49 /**
50 * The trace to read from.
51 */
93a45b54 52 private final CTFTrace fTrace;
866e5b51
FC
53
54 /**
55 * Vector of all the trace file readers.
56 */
90cefe9f 57 private final List<CTFStreamInputReader> fStreamInputReaders = Collections.synchronizedList(new ArrayList<CTFStreamInputReader>());
866e5b51
FC
58
59 /**
60 * Priority queue to order the trace file readers by timestamp.
61 */
d84419e1 62 private PriorityQueue<CTFStreamInputReader> fPrio;
866e5b51
FC
63
64 /**
65 * Array to count the number of event per trace file.
66 */
93a45b54 67 private long[] fEventCountPerTraceFile;
866e5b51
FC
68
69 /**
70 * Timestamp of the first event in the trace
71 */
93a45b54 72 private long fStartTime;
866e5b51
FC
73
74 /**
75 * Timestamp of the last event read so far
76 */
93a45b54 77 private long fEndTime;
866e5b51 78
545d9101
RB
79 /**
80 * Boolean to indicate if the CTFTraceReader has been closed
81 */
82 private boolean fClosed = false;
83
866e5b51
FC
84 // ------------------------------------------------------------------------
85 // Constructors
86 // ------------------------------------------------------------------------
87
88 /**
89 * Constructs a TraceReader to read a trace.
90 *
91 * @param trace
92 * The trace to read from.
680f9173 93 * @throws CTFException
db8e8f7d 94 * if an error occurs
866e5b51 95 */
680f9173 96 public CTFTraceReader(CTFTrace trace) throws CTFException {
93a45b54
MK
97 fTrace = trace;
98 fStreamInputReaders.clear();
866e5b51
FC
99
100 /**
101 * Create the trace file readers.
102 */
103 createStreamInputReaders();
104
105 /**
106 * Populate the timestamp-based priority queue.
107 */
108 populateStreamInputReaderHeap();
109
110 /**
bfe038ff
MK
111 * Get the start Time of this trace bear in mind that the trace could be
112 * empty.
866e5b51 113 */
93a45b54 114 fStartTime = 0;
33656d8e 115 if (hasMoreEvents()) {
42f8feff
MK
116 fStartTime = checkNotNull(getTopStream().getCurrentEvent()).getTimestamp();
117 setEndTime(fStartTime);
33656d8e 118 }
866e5b51
FC
119 }
120
121 /**
122 * Copy constructor
be6df2d8
AM
123 *
124 * @return The new CTFTraceReader
680f9173 125 * @throws CTFException
6a5251eb 126 * if an error occurs
866e5b51 127 */
680f9173 128 public CTFTraceReader copyFrom() throws CTFException {
866e5b51
FC
129 CTFTraceReader newReader = null;
130
93a45b54
MK
131 newReader = new CTFTraceReader(fTrace);
132 newReader.fStartTime = fStartTime;
133 newReader.setEndTime(fEndTime);
866e5b51
FC
134 return newReader;
135 }
136
5d1c6919
PT
137 /**
138 * Dispose the CTFTraceReader
5d1c6919 139 */
dd9752d5
AM
140 @Override
141 public void close() {
6a0ec7bc
AM
142 synchronized (fStreamInputReaders) {
143 for (CTFStreamInputReader reader : fStreamInputReaders) {
144 if (reader != null) {
145 try {
146 reader.close();
147 } catch (IOException e) {
148 Activator.logError(e.getMessage(), e);
149 }
b3151232 150 }
5d1c6919 151 }
6a0ec7bc 152 fStreamInputReaders.clear();
5d1c6919 153 }
74d66aa3 154 fPrio.clear();
545d9101 155 fClosed = true;
5d1c6919
PT
156 }
157
866e5b51
FC
158 // ------------------------------------------------------------------------
159 // Getters/Setters/Predicates
160 // ------------------------------------------------------------------------
161
162 /**
163 * Return the start time of this trace (== timestamp of the first event)
164 *
0d9a6d76 165 * @return the trace start time
866e5b51
FC
166 */
167 public long getStartTime() {
93a45b54 168 return fStartTime;
866e5b51
FC
169 }
170
6f4e8ec0
AM
171 /**
172 * Set the trace's end time
173 *
174 * @param endTime
175 * The end time to use
176 */
0594c61c 177 protected final void setEndTime(long endTime) {
93a45b54 178 fEndTime = endTime;
6f4e8ec0
AM
179 }
180
0594c61c
AM
181 /**
182 * Get the priority queue of this trace reader.
183 *
184 * @return The priority queue of input readers
0594c61c 185 */
d84419e1 186 protected PriorityQueue<CTFStreamInputReader> getPrio() {
93a45b54 187 return fPrio;
0594c61c
AM
188 }
189
866e5b51
FC
190 // ------------------------------------------------------------------------
191 // Operations
192 // ------------------------------------------------------------------------
193
194 /**
195 * Creates one trace file reader per trace file contained in the trace.
db8e8f7d 196 *
680f9173 197 * @throws CTFException
db8e8f7d 198 * if an error occurs
866e5b51 199 */
680f9173 200 private void createStreamInputReaders() throws CTFException {
866e5b51
FC
201 /*
202 * For each stream.
203 */
d84419e1
AM
204 for (CTFStream stream : fTrace.getStreams()) {
205 Set<CTFStreamInput> streamInputs = stream.getStreamInputs();
866e5b51
FC
206
207 /*
208 * For each trace file of the stream.
209 */
d84419e1 210 for (CTFStreamInput streamInput : streamInputs) {
866e5b51
FC
211
212 /*
05ce5fef 213 * Create a reader and add it to the group.
866e5b51 214 */
90cefe9f 215 fStreamInputReaders.add(new CTFStreamInputReader(checkNotNull(streamInput)));
866e5b51
FC
216 }
217 }
218
219 /*
220 * Create the array to count the number of event per trace file.
221 */
93a45b54 222 fEventCountPerTraceFile = new long[fStreamInputReaders.size()];
866e5b51
FC
223 }
224
545d9101
RB
225 /**
226 * Returns whether or not this CTFTraceReader has been closed
227 *
228 * @return true if it has been closed, false else
0336f981 229 * @since 1.1
545d9101
RB
230 */
231 public boolean isClosed() {
232 return fClosed;
233 }
234
fe75d403
MK
235 /**
236 * Update the priority queue to make it match the parent trace
237 *
680f9173 238 * @throws CTFException
fe75d403 239 * An error occured
fe75d403 240 */
680f9173 241 public void update() throws CTFException {
d84419e1
AM
242 Set<CTFStreamInputReader> readers = new HashSet<>();
243 for (CTFStream stream : fTrace.getStreams()) {
244 Set<CTFStreamInput> streamInputs = stream.getStreamInputs();
245 for (CTFStreamInput streamInput : streamInputs) {
fe75d403 246 /*
a3a1a06b 247 * Create a reader to check if it already exists in the list. If it doesn't, add it.
fe75d403 248 */
a3a1a06b
MAL
249 try (CTFStreamInputReader streamInputReader = new CTFStreamInputReader(checkNotNull(streamInput))) {
250 if (!fStreamInputReaders.contains(streamInputReader)) {
251 CTFStreamInputReader streamInputReaderToAdd = new CTFStreamInputReader(checkNotNull(streamInput));
252 streamInputReaderToAdd.readNextEvent();
253 fStreamInputReaders.add(streamInputReaderToAdd);
254 readers.add(streamInputReaderToAdd);
255 }
256 } catch (IOException e) {
257 Activator.logError(e.getMessage(), e);
fe75d403
MK
258 }
259 }
260 }
261 long[] temp = fEventCountPerTraceFile;
262 fEventCountPerTraceFile = new long[readers.size() + temp.length];
d84419e1 263 for (CTFStreamInputReader reader : readers) {
fe75d403
MK
264 fPrio.add(reader);
265 }
266 for (int i = 0; i < temp.length; i++) {
267 fEventCountPerTraceFile[i] = temp[i];
268 }
269 }
270
53359017
MK
271 /**
272 * Gets an iterable of the stream input readers, useful for foreaches
273 *
274 * @return the iterable of the stream input readers
53359017
MK
275 */
276 public Iterable<IEventDeclaration> getEventDeclarations() {
42f8feff 277 Set<IEventDeclaration> retSet = new HashSet<>();
d84419e1 278 for (CTFStreamInputReader sir : fStreamInputReaders) {
42f8feff 279 retSet.addAll(sir.getEventDeclarations());
53359017 280 }
42f8feff
MK
281 retSet.remove(null);
282 return retSet;
53359017
MK
283 }
284
866e5b51
FC
285 /**
286 * Initializes the priority queue used to choose the trace file with the
287 * lower next event timestamp.
db8e8f7d 288 *
680f9173 289 * @throws CTFException
db8e8f7d 290 * if an error occurs
866e5b51 291 */
680f9173 292 private void populateStreamInputReaderHeap() throws CTFException {
93a45b54 293 if (fStreamInputReaders.isEmpty()) {
fe75d403
MK
294 fPrio = new PriorityQueue<>(MIN_PRIO_SIZE,
295 new StreamInputReaderTimestampComparator());
b5354daa
MAL
296 return;
297 }
298
866e5b51
FC
299 /*
300 * Create the priority queue with a size twice as bigger as the number
301 * of reader in order to avoid constant resizing.
302 */
93a45b54 303 fPrio = new PriorityQueue<>(
fe75d403 304 Math.max(fStreamInputReaders.size() * 2, MIN_PRIO_SIZE),
866e5b51
FC
305 new StreamInputReaderTimestampComparator());
306
307 int pos = 0;
308
d84419e1 309 for (CTFStreamInputReader reader : fStreamInputReaders) {
866e5b51
FC
310 /*
311 * Add each trace file reader in the priority queue, if we are able
312 * to read an event from it.
313 */
6a5251eb
MK
314 CTFResponse readNextEvent = reader.readNextEvent();
315 if (readNextEvent == CTFResponse.OK || readNextEvent == CTFResponse.WAIT) {
93a45b54 316 fPrio.add(reader);
866e5b51 317
93a45b54 318 fEventCountPerTraceFile[pos] = 0;
866e5b51
FC
319 reader.setName(pos);
320
321 pos++;
322 }
323 }
324 }
325
326 /**
327 * Get the current event, which is the current event of the trace file
328 * reader with the lowest timestamp.
329 *
330 * @return An event definition, or null of the trace reader reached the end
331 * of the trace.
332 */
333 public EventDefinition getCurrentEventDef() {
d84419e1 334 CTFStreamInputReader top = getTopStream();
866e5b51
FC
335 return (top != null) ? top.getCurrentEvent() : null;
336 }
337
338 /**
339 * Go to the next event.
340 *
341 * @return True if an event was read.
680f9173 342 * @throws CTFException
db8e8f7d 343 * if an error occurs
866e5b51 344 */
680f9173 345 public boolean advance() throws CTFException {
866e5b51
FC
346 /*
347 * Remove the reader from the top of the priority queue.
348 */
d84419e1 349 CTFStreamInputReader top = fPrio.poll();
866e5b51
FC
350
351 /*
352 * If the queue was empty.
353 */
354 if (top == null) {
355 return false;
356 }
866e5b51
FC
357 /*
358 * Read the next event of this reader.
359 */
6a5251eb
MK
360 switch (top.readNextEvent()) {
361 case OK: {
866e5b51
FC
362 /*
363 * Add it back in the queue.
364 */
93a45b54 365 fPrio.add(top);
90cefe9f
MK
366 /*
367 * We're in OK, there's a guaranteed top#getCurrentEvent() unless another
368 * thread does something bad.
369 */
370 EventDefinition currentEvent = checkNotNull(top.getCurrentEvent());
371 final long topEnd = fTrace.timestampCyclesToNanos(currentEvent.getTimestamp());
93a45b54
MK
372 setEndTime(Math.max(topEnd, getEndTime()));
373 fEventCountPerTraceFile[top.getName()]++;
90cefe9f 374 fEndTime = Math.max(currentEvent.getTimestamp(), fEndTime);
6a5251eb
MK
375 break;
376 }
377 case WAIT: {
378 fPrio.add(top);
379 break;
380 }
381 case FINISH:
382 break;
383 case ERROR:
384 default:
385 // something bad happend
bfe038ff 386 }
866e5b51
FC
387 /*
388 * If there is no reader in the queue, it means the trace reader reached
389 * the end of the trace.
390 */
bfe038ff 391 return hasMoreEvents();
866e5b51
FC
392 }
393
394 /**
395 * Go to the last event in the trace.
db8e8f7d 396 *
680f9173 397 * @throws CTFException
db8e8f7d 398 * if an error occurs
866e5b51 399 */
680f9173 400 public void goToLastEvent() throws CTFException {
93a45b54
MK
401 seek(getEndTime());
402 while (fPrio.size() > 1) {
403 advance();
866e5b51
FC
404 }
405 }
406
407 /**
ecb12461
EB
408 * Seeks to a given timestamp. It will seek to the nearest event greater or
409 * equal to timestamp. If a trace is [10 20 30 40] and you are looking for
410 * 19, it will give you 20. If you want 20, you will get 20, if you want 21,
411 * you will get 30. The value -inf will seek to the first element and the
412 * value +inf will seek to the end of the file (past the last event).
866e5b51
FC
413 *
414 * @param timestamp
415 * the timestamp to seek to
6a5251eb
MK
416 * @return true if there are events above or equal the seek timestamp, false
417 * if seek at the end of the trace (no valid event).
680f9173 418 * @throws CTFException
db8e8f7d 419 * if an error occurs
866e5b51 420 */
680f9173 421 public boolean seek(long timestamp) throws CTFException {
866e5b51
FC
422 /*
423 * Remove all the trace readers from the priority queue
424 */
93a45b54 425 fPrio.clear();
d84419e1 426 for (CTFStreamInputReader streamInputReader : fStreamInputReaders) {
866e5b51
FC
427 /*
428 * Seek the trace reader.
429 */
bfe038ff 430 streamInputReader.seek(timestamp);
ce2388e0
FC
431
432 /*
433 * Add it to the priority queue if there is a current event.
434 */
ce2388e0 435 if (streamInputReader.getCurrentEvent() != null) {
93a45b54 436 fPrio.add(streamInputReader);
ce2388e0
FC
437 }
438 }
866e5b51
FC
439 return hasMoreEvents();
440 }
441
9ac2eb62 442 /**
ecb12461 443 * Gets the stream with the oldest event
9ac2eb62
MK
444 *
445 * @return the stream with the oldest event
446 */
d84419e1 447 public CTFStreamInputReader getTopStream() {
93a45b54 448 return fPrio.peek();
ce2388e0
FC
449 }
450
866e5b51
FC
451 /**
452 * Does the trace have more events?
453 *
454 * @return true if yes.
455 */
0594c61c 456 public final boolean hasMoreEvents() {
93a45b54 457 return fPrio.size() > 0;
866e5b51
FC
458 }
459
460 /**
461 * Prints the event count stats.
462 */
463 public void printStats() {
f068c622 464 printStats(LINE_LENGTH);
866e5b51
FC
465 }
466
467 /**
468 * Prints the event count stats.
469 *
470 * @param width
471 * Width of the display.
472 */
473 public void printStats(int width) {
474 int numEvents = 0;
475 if (width == 0) {
476 return;
477 }
478
93a45b54 479 for (long i : fEventCountPerTraceFile) {
866e5b51
FC
480 numEvents += i;
481 }
482
93a45b54 483 for (int j = 0; j < fEventCountPerTraceFile.length; j++) {
d84419e1 484 CTFStreamInputReader se = fStreamInputReaders.get(j);
866e5b51 485
93a45b54 486 long len = (width * fEventCountPerTraceFile[se.getName()])
866e5b51
FC
487 / numEvents;
488
0594c61c
AM
489 StringBuilder sb = new StringBuilder(se.getFilename());
490 sb.append("\t["); //$NON-NLS-1$
866e5b51
FC
491
492 for (int i = 0; i < len; i++) {
493 sb.append('+');
494 }
495
bfe038ff 496 for (long i = len; i < width; i++) {
866e5b51
FC
497 sb.append(' ');
498 }
499
93a45b54 500 sb.append("]\t" + fEventCountPerTraceFile[se.getName()] + " Events"); //$NON-NLS-1$//$NON-NLS-2$
4311ac8b 501 Activator.log(sb.toString());
866e5b51
FC
502 }
503 }
504
9ac2eb62 505 /**
ecb12461 506 * Gets the last event timestamp that was read. This is NOT necessarily the
9ac2eb62
MK
507 * last event in a trace, just the last one read so far.
508 *
509 * @return the last event
510 */
866e5b51 511 public long getEndTime() {
93a45b54 512 return fEndTime;
866e5b51
FC
513 }
514
6a5251eb
MK
515 /**
516 * Sets a trace to be live or not
517 *
518 * @param live
519 * whether the trace is live
6a5251eb
MK
520 */
521 public void setLive(boolean live) {
d84419e1 522 for (CTFStreamInputReader s : fPrio) {
6a5251eb
MK
523 s.setLive(live);
524 }
525 }
526
527 /**
528 * Get if the trace is to read live or not
529 *
530 * @return whether the trace is live or not
6a5251eb
MK
531 */
532 public boolean isLive() {
0af5eb9a 533 return getTopStream().isLive();
6a5251eb
MK
534 }
535
866e5b51
FC
536 @Override
537 public int hashCode() {
538 final int prime = 31;
539 int result = 1;
93a45b54
MK
540 result = (prime * result) + (int) (fStartTime ^ (fStartTime >>> 32));
541 result = (prime * result) + fStreamInputReaders.hashCode();
542 result = (prime * result) + ((fTrace == null) ? 0 : fTrace.hashCode());
866e5b51
FC
543 return result;
544 }
545
546 @Override
547 public boolean equals(Object obj) {
548 if (this == obj) {
549 return true;
550 }
551 if (obj == null) {
552 return false;
553 }
07002e0a 554 if (!(obj instanceof CTFTraceReader)) {
866e5b51
FC
555 return false;
556 }
557 CTFTraceReader other = (CTFTraceReader) obj;
93a45b54 558 if (!fStreamInputReaders.equals(other.fStreamInputReaders)) {
866e5b51
FC
559 return false;
560 }
93a45b54
MK
561 if (fTrace == null) {
562 if (other.fTrace != null) {
866e5b51
FC
563 return false;
564 }
93a45b54 565 } else if (!fTrace.equals(other.fTrace)) {
866e5b51
FC
566 return false;
567 }
568 return true;
569 }
570
866e5b51
FC
571 @Override
572 public String toString() {
573 /* Only for debugging, shouldn't be externalized */
93a45b54 574 return "CTFTraceReader [trace=" + fTrace + ']'; //$NON-NLS-1$
866e5b51
FC
575 }
576
9ac2eb62
MK
577 /**
578 * Gets the parent trace
579 *
580 * @return the parent trace
581 */
866e5b51 582 public CTFTrace getTrace() {
93a45b54 583 return fTrace;
866e5b51 584 }
4a32dd11
MK
585
586 /**
587 * This will read the entire trace and populate all the indexes. The reader
588 * will then be reset to the first event in the trace.
589 *
590 * Do not call in the fast path.
591 *
592 * @throws CTFException
593 * A trace reading error occurred
594 * @since 1.0
595 */
596 public void populateIndex() throws CTFException {
597 for (CTFStreamInputReader sir : fPrio) {
598 sir.goToLastEvent();
599 }
600 seek(0);
601
602 }
866e5b51 603}
This page took 0.112991 seconds and 5 git commands to generate.