ctf: make CtfStreamInput not hold onto any resources
[deliverable/tracecompass.git] / org.eclipse.linuxtools.ctf.core / src / org / eclipse / linuxtools / ctf / core / trace / CTFStreamInput.java
CommitLineData
866e5b51 1/*******************************************************************************
60ae41e1 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 *
9 * Contributors: Matthew Khouzam - Initial API and implementation
10 * Contributors: Simon Marchi - Initial API and implementation
11 *******************************************************************************/
12
486efb2e 13package org.eclipse.linuxtools.ctf.core.trace;
866e5b51
FC
14
15import java.io.File;
aa3b05ef 16import java.io.IOException;
aefc5c83 17import java.nio.MappedByteBuffer;
866e5b51 18import java.nio.channels.FileChannel;
aa3b05ef 19import java.nio.channels.FileChannel.MapMode;
b3151232 20import java.nio.file.StandardOpenOption;
866e5b51
FC
21import java.util.UUID;
22
a4fa4e36 23import org.eclipse.jdt.annotation.NonNull;
486efb2e 24import org.eclipse.linuxtools.ctf.core.event.io.BitBuffer;
a4fa4e36
MK
25import org.eclipse.linuxtools.ctf.core.event.scope.IDefinitionScope;
26import org.eclipse.linuxtools.ctf.core.event.scope.LexicalScope;
866e5b51 27import org.eclipse.linuxtools.ctf.core.event.types.Definition;
21fb02fa
MK
28import org.eclipse.linuxtools.ctf.core.event.types.EnumDefinition;
29import org.eclipse.linuxtools.ctf.core.event.types.FloatDefinition;
cc98c947 30import org.eclipse.linuxtools.ctf.core.event.types.IDefinition;
866e5b51 31import org.eclipse.linuxtools.ctf.core.event.types.IntegerDefinition;
21fb02fa 32import org.eclipse.linuxtools.ctf.core.event.types.StringDefinition;
a4fa4e36 33import org.eclipse.linuxtools.ctf.core.event.types.StructDeclaration;
866e5b51 34import org.eclipse.linuxtools.ctf.core.event.types.StructDefinition;
7b4f13e6 35import org.eclipse.linuxtools.internal.ctf.core.event.types.ArrayDefinition;
486efb2e
AM
36import org.eclipse.linuxtools.internal.ctf.core.trace.StreamInputPacketIndex;
37import org.eclipse.linuxtools.internal.ctf.core.trace.StreamInputPacketIndexEntry;
866e5b51
FC
38
39/**
40 * <b><u>StreamInput</u></b>
41 * <p>
42 * Represents a trace file that belongs to a certain stream.
cf9a28da 43 *
d84419e1 44 * @since 3.0
866e5b51 45 */
b3151232 46// TODO: remove AutoCloseable
d84419e1 47public class CTFStreamInput implements IDefinitionScope, AutoCloseable {
866e5b51
FC
48
49 // ------------------------------------------------------------------------
50 // Attributes
51 // ------------------------------------------------------------------------
52
53 /**
54 * The associated Stream
55 */
d84419e1 56 private final CTFStream fStream;
866e5b51 57
866e5b51
FC
58 /**
59 * Information on the file (used for debugging)
60 */
b3151232 61 @NonNull
f224bebc 62 private final File fFile;
866e5b51
FC
63
64 /**
65 * The packet index of this input
66 */
f224bebc 67 private final StreamInputPacketIndex fIndex;
866e5b51 68
f224bebc 69 private long fTimestampEnd;
866e5b51 70
8e15b929 71 /**
bfe038ff
MK
72 * Definition of trace packet header
73 */
a4fa4e36 74 private StructDeclaration fTracePacketHeaderDecl = null;
bfe038ff 75
8e15b929 76 /**
bfe038ff
MK
77 * Definition of trace stream packet context
78 */
a4fa4e36 79 private StructDeclaration fStreamPacketContextDecl = null;
bfe038ff 80
8e15b929 81 /**
132a02b0
MK
82 * Total number of lost events in this stream
83 */
f224bebc 84 private long fLostSoFar = 0;
132a02b0 85
866e5b51
FC
86 // ------------------------------------------------------------------------
87 // Constructors
88 // ------------------------------------------------------------------------
89
90 /**
91 * Constructs a StreamInput.
92 *
93 * @param stream
94 * The stream to which this StreamInput belongs to.
866e5b51
FC
95 * @param file
96 * Information about the trace file (for debugging purposes).
97 */
b3151232 98 public CTFStreamInput(CTFStream stream, @NonNull File file) {
f224bebc 99 fStream = stream;
f224bebc
MK
100 fFile = file;
101 fIndex = new StreamInputPacketIndex();
866e5b51
FC
102 }
103
8e15b929
MK
104 @Override
105 public void close() throws IOException {
8e15b929
MK
106 }
107
866e5b51
FC
108 // ------------------------------------------------------------------------
109 // Getters/Setters/Predicates
110 // ------------------------------------------------------------------------
111
9ac2eb62
MK
112 /**
113 * Gets the stream the streamInput wrapper is wrapping
21fb02fa 114 *
9ac2eb62
MK
115 * @return the stream the streamInput wrapper is wrapping
116 */
d84419e1 117 public CTFStream getStream() {
f224bebc 118 return fStream;
866e5b51
FC
119 }
120
9ac2eb62 121 /**
ecb12461 122 * The common streamInput Index
21fb02fa 123 *
9ac2eb62
MK
124 * @return the stream input Index
125 */
486efb2e 126 StreamInputPacketIndex getIndex() {
f224bebc 127 return fIndex;
866e5b51
FC
128 }
129
9ac2eb62
MK
130 /**
131 * Gets the filename of the streamInput file.
21fb02fa 132 *
9ac2eb62
MK
133 * @return the filename of the streaminput file.
134 */
866e5b51 135 public String getFilename() {
f224bebc 136 return fFile.getName();
866e5b51
FC
137 }
138
9ac2eb62 139 /**
ecb12461 140 * Gets the last read timestamp of a stream. (this is not necessarily the
21fb02fa
MK
141 * last time in the stream.)
142 *
9ac2eb62
MK
143 * @return the last read timestamp
144 */
866e5b51 145 public long getTimestampEnd() {
f224bebc 146 return fTimestampEnd;
866e5b51
FC
147 }
148
9ac2eb62 149 /**
21fb02fa
MK
150 * Sets the last read timestamp of a stream. (this is not necessarily the
151 * last time in the stream.)
152 *
153 * @param timestampEnd
154 * the last read timestamp
9ac2eb62 155 */
866e5b51 156 public void setTimestampEnd(long timestampEnd) {
f224bebc 157 fTimestampEnd = timestampEnd;
866e5b51
FC
158 }
159
9ac2eb62 160 /**
ecb12461 161 * Useless for streaminputs
9ac2eb62 162 */
866e5b51 163 @Override
a4fa4e36
MK
164 public LexicalScope getScopePath() {
165 return LexicalScope.STREAM;
866e5b51
FC
166 }
167
168 // ------------------------------------------------------------------------
169 // Operations
170 // ------------------------------------------------------------------------
171
172 @Override
173 public Definition lookupDefinition(String lookupPath) {
174 /* TODO: lookup in different dynamic scopes is not supported yet. */
175 return null;
176 }
177
178 /**
179 * Create the index for this trace file.
bfe038ff
MK
180 */
181 public void setupIndex() {
182
bfe038ff
MK
183 /*
184 * The BitBuffer to extract data from the StreamInput
185 */
186 BitBuffer bitBuffer = new BitBuffer();
f224bebc 187 bitBuffer.setByteOrder(getStream().getTrace().getByteOrder());
bfe038ff
MK
188
189 /*
190 * Create the definitions we need to read the packet headers + contexts
191 */
192 if (getStream().getTrace().getPacketHeader() != null) {
a4fa4e36 193 fTracePacketHeaderDecl = getStream().getTrace().getPacketHeader();
bfe038ff
MK
194 }
195
196 if (getStream().getPacketContextDecl() != null) {
a4fa4e36 197 fStreamPacketContextDecl = getStream().getPacketContextDecl();
bfe038ff
MK
198 }
199
200 }
201
9ac2eb62
MK
202 /**
203 * Adds the next packet header index entry to the index of a stream input.
be6df2d8 204 *
733c614c
MK
205 * <strong>This method is slow and can corrupt data if not used
206 * properly</strong>
207 *
9ac2eb62
MK
208 * @return true if there are more packets to add
209 * @throws CTFReaderException
be6df2d8 210 * If there was a problem reading the packed header
9ac2eb62 211 */
bfe038ff
MK
212 public boolean addPacketHeaderIndex() throws CTFReaderException {
213 long currentPos = 0L;
f224bebc
MK
214 if (!fIndex.getEntries().isEmpty()) {
215 StreamInputPacketIndexEntry pos = fIndex.getEntries().lastElement();
bfe038ff
MK
216 currentPos = computeNextOffset(pos);
217 }
218 long fileSize = getStreamSize();
219 if (currentPos < fileSize) {
733c614c 220
bfe038ff
MK
221 StreamInputPacketIndexEntry packetIndex = new StreamInputPacketIndexEntry(
222 currentPos);
b3151232 223 createPacketIndexEntry(fileSize, currentPos, packetIndex);
f224bebc 224 fIndex.addEntry(packetIndex);
bfe038ff
MK
225 return true;
226 }
227 return false;
228 }
229
bfe038ff 230 private long getStreamSize() {
f224bebc 231 return fFile.length();
bfe038ff
MK
232 }
233
b3151232 234 private long createPacketIndexEntry(long fileSizeBytes, long packetOffsetBytes, StreamInputPacketIndexEntry packetIndex)
bfe038ff 235 throws CTFReaderException {
0594c61c 236
b3151232 237 long pos = readPacketHeader(fileSizeBytes, packetOffsetBytes, packetIndex);
bfe038ff
MK
238
239 /* Basic validation */
240 if (packetIndex.getContentSizeBits() > packetIndex.getPacketSizeBits()) {
241 throw new CTFReaderException("Content size > packet size"); //$NON-NLS-1$
242 }
866e5b51 243
bfe038ff
MK
244 if (packetIndex.getPacketSizeBits() > ((fileSizeBytes - packetIndex
245 .getOffsetBytes()) * 8)) {
cf9a28da 246 throw new CTFReaderException("Not enough data remaining in the file for the size of this packet"); //$NON-NLS-1$
bfe038ff
MK
247 }
248
249 /*
250 * Offset in the file, in bits
251 */
b3151232 252 packetIndex.setDataOffsetBits(pos);
bfe038ff
MK
253
254 /*
255 * Update the counting packet offset
256 */
21fb02fa 257 return computeNextOffset(packetIndex);
bfe038ff
MK
258 }
259
260 /**
261 * @param packetIndex
262 * @return
263 */
264 private static long computeNextOffset(
265 StreamInputPacketIndexEntry packetIndex) {
266 return packetIndex.getOffsetBytes()
267 + ((packetIndex.getPacketSizeBits() + 7) / 8);
268 }
269
b3151232 270 private long readPacketHeader(long fileSizeBytes,
733c614c 271 long packetOffsetBytes, StreamInputPacketIndexEntry packetIndex) throws CTFReaderException {
b3151232 272 long position = -1;
aa3b05ef
FC
273 /*
274 * Initial size, it should map at least the packet header + context
275 * size.
276 *
277 * TODO: use a less arbitrary size.
278 */
279 long mapSize = 4096;
280 /*
281 * If there is less data remaining than what we want to map, reduce the
282 * map size.
283 */
284 if ((fileSizeBytes - packetIndex.getOffsetBytes()) < mapSize) {
285 mapSize = fileSizeBytes - packetIndex.getOffsetBytes();
286 }
287
288 /*
289 * Map the packet.
290 */
b3151232
MK
291 try (FileChannel fc = FileChannel.open(fFile.toPath(), StandardOpenOption.READ)) {
292 MappedByteBuffer map = fc.map(MapMode.READ_ONLY, packetOffsetBytes, mapSize);
293 if (map == null) {
294 throw new CTFReaderException("Failed to allocate mapped byte buffer"); //$NON-NLS-1$
295 }
296 /*
297 * create a packet bit buffer to read the packet header
298 */
299 BitBuffer bitBuffer = new BitBuffer(map);
300 bitBuffer.setByteOrder(getStream().getTrace().getByteOrder());
301 /*
302 * Read the trace packet header if it exists.
303 */
304 if (fTracePacketHeaderDecl != null) {
305 parseTracePacketHeader(fTracePacketHeaderDecl, bitBuffer);
306 }
307
308 /*
309 * Read the stream packet context if it exists.
310 */
311 if (fStreamPacketContextDecl != null) {
312 parsePacketContext(fileSizeBytes, fStreamPacketContextDecl,
313 bitBuffer, packetIndex);
314 } else {
315 setPacketContextNull(fileSizeBytes, packetIndex);
316 }
317
318 position = bitBuffer.position();
aa3b05ef
FC
319 } catch (IOException e) {
320 throw new CTFReaderException(e);
321 }
b3151232 322 return position;
aa3b05ef 323 }
bfe038ff 324
a4fa4e36
MK
325 private void parseTracePacketHeader(StructDeclaration tracePacketHeaderDecl,
326 @NonNull BitBuffer bitBuffer) throws CTFReaderException {
70f60307 327 StructDefinition tracePacketHeaderDef = tracePacketHeaderDecl.createDefinition(fStream.getTrace(), LexicalScope.TRACE_PACKET_HEADER, bitBuffer);
866e5b51
FC
328
329 /*
bfe038ff 330 * Check the CTF magic number
866e5b51 331 */
bfe038ff
MK
332 IntegerDefinition magicDef = (IntegerDefinition) tracePacketHeaderDef
333 .lookupDefinition("magic"); //$NON-NLS-1$
334 if (magicDef != null) {
335 int magic = (int) magicDef.getValue();
336 if (magic != Utils.CTF_MAGIC) {
337 throw new CTFReaderException(
338 "CTF magic mismatch " + Integer.toHexString(magic) + " vs " + Integer.toHexString(Utils.CTF_MAGIC)); //$NON-NLS-1$//$NON-NLS-2$
339 }
866e5b51
FC
340 }
341
342 /*
bfe038ff 343 * Check the trace UUID
866e5b51 344 */
0594c61c
AM
345 ArrayDefinition uuidDef =
346 (ArrayDefinition) tracePacketHeaderDef.lookupDefinition("uuid"); //$NON-NLS-1$
bfe038ff 347 if (uuidDef != null) {
a4fa4e36 348 UUID uuid = Utils.getUUIDfromDefinition(uuidDef);
866e5b51 349
bfe038ff
MK
350 if (!getStream().getTrace().getUUID().equals(uuid)) {
351 throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$
866e5b51 352 }
bfe038ff 353 }
866e5b51 354
bfe038ff
MK
355 /*
356 * Check that the stream id did not change
357 */
358 IntegerDefinition streamIDDef = (IntegerDefinition) tracePacketHeaderDef
359 .lookupDefinition("stream_id"); //$NON-NLS-1$
360 if (streamIDDef != null) {
361 long streamID = streamIDDef.getValue();
866e5b51 362
bfe038ff 363 if (streamID != getStream().getId()) {
cf9a28da 364 throw new CTFReaderException("Stream ID changing within a StreamInput"); //$NON-NLS-1$
866e5b51 365 }
bfe038ff
MK
366 }
367 }
866e5b51 368
b3151232
MK
369 /**
370 * Gets the wrapped file
371 *
372 * @return the file
373 */
374 @NonNull
375 File getFile() {
376 return fFile;
377 }
378
bfe038ff
MK
379 private static void setPacketContextNull(long fileSizeBytes,
380 StreamInputPacketIndexEntry packetIndex) {
381 /*
382 * If there is no packet context, infer the content and packet size from
383 * the file size (assume that there is only one packet and no padding)
384 */
47ca6c05
SM
385 packetIndex.setContentSizeBits(fileSizeBytes * 8);
386 packetIndex.setPacketSizeBits(fileSizeBytes * 8);
bfe038ff 387 }
866e5b51 388
bfe038ff 389 private void parsePacketContext(long fileSizeBytes,
a4fa4e36 390 StructDeclaration streamPacketContextDecl, @NonNull BitBuffer bitBuffer,
db8e8f7d 391 StreamInputPacketIndexEntry packetIndex) throws CTFReaderException {
70f60307 392 StructDefinition streamPacketContextDef = streamPacketContextDecl.createDefinition(this, LexicalScope.STREAM_PACKET_CONTEXT, bitBuffer);
866e5b51 393
21fb02fa
MK
394 for (String field : streamPacketContextDef.getDeclaration()
395 .getFieldsList()) {
cc98c947 396 IDefinition id = streamPacketContextDef.lookupDefinition(field);
21fb02fa
MK
397 if (id instanceof IntegerDefinition) {
398 packetIndex.addAttribute(field,
399 ((IntegerDefinition) id).getValue());
400 } else if (id instanceof FloatDefinition) {
401 packetIndex.addAttribute(field,
402 ((FloatDefinition) id).getValue());
403 } else if (id instanceof EnumDefinition) {
404 packetIndex.addAttribute(field,
405 ((EnumDefinition) id).getValue());
406 } else if (id instanceof StringDefinition) {
407 packetIndex.addAttribute(field,
408 ((StringDefinition) id).getValue());
409 }
21fb02fa
MK
410 }
411
412 Long contentSize = (Long) packetIndex.lookupAttribute("content_size"); //$NON-NLS-1$
413 Long packetSize = (Long) packetIndex.lookupAttribute("packet_size"); //$NON-NLS-1$
0594c61c
AM
414 Long tsBegin = (Long) packetIndex.lookupAttribute("timestamp_begin"); //$NON-NLS-1$
415 Long tsEnd = (Long) packetIndex.lookupAttribute("timestamp_end"); //$NON-NLS-1$
21fb02fa
MK
416 String device = (String) packetIndex.lookupAttribute("device"); //$NON-NLS-1$
417 // LTTng Specific
0594c61c 418 Long cpuId = (Long) packetIndex.lookupAttribute("cpu_id"); //$NON-NLS-1$
cf9a28da 419 Long lostEvents = (Long) packetIndex.lookupAttribute("events_discarded"); //$NON-NLS-1$
132a02b0
MK
420
421 /* Read the content size in bits */
21fb02fa
MK
422 if (contentSize != null) {
423 packetIndex.setContentSizeBits(contentSize.intValue());
cf9a28da
MK
424 } else if (packetSize != null) {
425 packetIndex.setContentSizeBits(packetSize.longValue());
bfe038ff
MK
426 } else {
427 packetIndex.setContentSizeBits((int) (fileSizeBytes * 8));
428 }
866e5b51 429
132a02b0 430 /* Read the packet size in bits */
21fb02fa
MK
431 if (packetSize != null) {
432 packetIndex.setPacketSizeBits(packetSize.intValue());
51598b21
MK
433 } else if (packetIndex.getContentSizeBits() != 0) {
434 packetIndex.setPacketSizeBits(packetIndex.getContentSizeBits());
bfe038ff 435 } else {
51598b21 436 packetIndex.setPacketSizeBits((int) (fileSizeBytes * 8));
bfe038ff
MK
437 }
438
132a02b0 439 /* Read the begin timestamp */
0594c61c
AM
440 if (tsBegin != null) {
441 packetIndex.setTimestampBegin(tsBegin.longValue());
bfe038ff
MK
442 }
443
132a02b0 444 /* Read the end timestamp */
0594c61c 445 if (tsEnd != null) {
ecb12461 446 if (tsEnd == -1) {
0594c61c 447 tsEnd = Long.MAX_VALUE;
21fb02fa 448 }
0594c61c 449 packetIndex.setTimestampEnd(tsEnd.longValue());
bfe038ff 450 setTimestampEnd(packetIndex.getTimestampEnd());
866e5b51 451 }
21fb02fa
MK
452
453 if (device != null) {
454 packetIndex.setTarget(device);
455 }
456
0594c61c
AM
457 if (cpuId != null) {
458 packetIndex.setTarget("CPU" + cpuId.toString()); //$NON-NLS-1$
21fb02fa 459 }
132a02b0
MK
460
461 if (lostEvents != null) {
f224bebc
MK
462 packetIndex.setLostEvents(lostEvents - fLostSoFar);
463 fLostSoFar = lostEvents;
132a02b0 464 }
866e5b51
FC
465 }
466
81c8e6f7
MK
467 @Override
468 public int hashCode() {
469 final int prime = 31;
470 int result = 1;
b3151232 471 result = (prime * result) + fFile.hashCode();
81c8e6f7
MK
472 return result;
473 }
474
81c8e6f7
MK
475 @Override
476 public boolean equals(Object obj) {
477 if (this == obj) {
478 return true;
479 }
480 if (obj == null) {
481 return false;
482 }
d84419e1 483 if (!(obj instanceof CTFStreamInput)) {
81c8e6f7
MK
484 return false;
485 }
d84419e1 486 CTFStreamInput other = (CTFStreamInput) obj;
b3151232 487 if (!fFile.equals(other.fFile)) {
81c8e6f7
MK
488 return false;
489 }
490 return true;
491 }
492
866e5b51 493}
This page took 0.113836 seconds and 5 git commands to generate.