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
9 * Contributors: Matthew Khouzam - Initial API and implementation
10 * Contributors: Simon Marchi - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.linuxtools
.ctf
.core
.trace
;
16 import java
.io
.FileInputStream
;
17 import java
.io
.FileNotFoundException
;
18 import java
.io
.IOException
;
19 import java
.nio
.ByteBuffer
;
20 import java
.nio
.channels
.FileChannel
;
21 import java
.nio
.channels
.FileChannel
.MapMode
;
22 import java
.util
.UUID
;
24 import org
.eclipse
.jdt
.annotation
.NonNull
;
25 import org
.eclipse
.linuxtools
.ctf
.core
.event
.io
.BitBuffer
;
26 import org
.eclipse
.linuxtools
.ctf
.core
.event
.scope
.IDefinitionScope
;
27 import org
.eclipse
.linuxtools
.ctf
.core
.event
.scope
.LexicalScope
;
28 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.ArrayDefinition
;
29 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.Definition
;
30 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.EnumDefinition
;
31 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.FloatDefinition
;
32 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.IntegerDefinition
;
33 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StringDefinition
;
34 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StructDeclaration
;
35 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StructDefinition
;
36 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.trace
.StreamInputPacketIndex
;
37 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.trace
.StreamInputPacketIndexEntry
;
40 * <b><u>StreamInput</u></b>
42 * Represents a trace file that belongs to a certain stream.
46 public class CTFStreamInput
implements IDefinitionScope
, AutoCloseable
{
48 // ------------------------------------------------------------------------
50 // ------------------------------------------------------------------------
53 * The associated Stream
55 private final CTFStream fStream
;
58 * FileChannel to the trace file
60 private final FileChannel fFileChannel
;
63 * Information on the file (used for debugging)
65 private final File fFile
;
68 * The packet index of this input
70 private final StreamInputPacketIndex fIndex
;
72 private long fTimestampEnd
;
75 * Definition of trace packet header
77 private StructDeclaration fTracePacketHeaderDecl
= null;
80 * Definition of trace stream packet context
82 private StructDeclaration fStreamPacketContextDecl
= null;
85 * Total number of lost events in this stream
87 private long fLostSoFar
= 0;
90 * File input stream, the parent input file
92 private final FileInputStream fFileInputStream
;
94 // ------------------------------------------------------------------------
96 // ------------------------------------------------------------------------
99 * Constructs a StreamInput.
102 * The stream to which this StreamInput belongs to.
104 * Information about the trace file (for debugging purposes).
105 * @throws CTFReaderException
106 * The file must exist
108 public CTFStreamInput(CTFStream stream
, File file
) throws CTFReaderException
{
112 fFileInputStream
= new FileInputStream(file
);
113 } catch (FileNotFoundException e
) {
114 throw new CTFReaderException(e
);
117 fFileChannel
= fFileInputStream
.getChannel();
118 fIndex
= new StreamInputPacketIndex();
122 public void close() throws IOException
{
123 fFileChannel
.close();
124 fFileInputStream
.close();
127 // ------------------------------------------------------------------------
128 // Getters/Setters/Predicates
129 // ------------------------------------------------------------------------
132 * Gets the stream the streamInput wrapper is wrapping
134 * @return the stream the streamInput wrapper is wrapping
136 public CTFStream
getStream() {
141 * The common streamInput Index
143 * @return the stream input Index
145 StreamInputPacketIndex
getIndex() {
150 * Gets the filename of the streamInput file.
152 * @return the filename of the streaminput file.
154 public String
getFilename() {
155 return fFile
.getName();
159 * Gets the last read timestamp of a stream. (this is not necessarily the
160 * last time in the stream.)
162 * @return the last read timestamp
164 public long getTimestampEnd() {
165 return fTimestampEnd
;
169 * Sets the last read timestamp of a stream. (this is not necessarily the
170 * last time in the stream.)
172 * @param timestampEnd
173 * the last read timestamp
175 public void setTimestampEnd(long timestampEnd
) {
176 fTimestampEnd
= timestampEnd
;
180 * Useless for streaminputs
183 public LexicalScope
getScopePath() {
184 return LexicalScope
.STREAM
;
187 // ------------------------------------------------------------------------
189 // ------------------------------------------------------------------------
192 public Definition
lookupDefinition(String lookupPath
) {
193 /* TODO: lookup in different dynamic scopes is not supported yet. */
198 * Create the index for this trace file.
200 public void setupIndex() {
203 * The BitBuffer to extract data from the StreamInput
205 BitBuffer bitBuffer
= new BitBuffer();
206 bitBuffer
.setByteOrder(getStream().getTrace().getByteOrder());
209 * Create the definitions we need to read the packet headers + contexts
211 if (getStream().getTrace().getPacketHeader() != null) {
212 fTracePacketHeaderDecl
= getStream().getTrace().getPacketHeader();
215 if (getStream().getPacketContextDecl() != null) {
216 fStreamPacketContextDecl
= getStream().getPacketContextDecl();
222 * Adds the next packet header index entry to the index of a stream input.
224 * <strong>This method is slow and can corrupt data if not used properly</strong>
225 * @return true if there are more packets to add
226 * @throws CTFReaderException
227 * If there was a problem reading the packed header
229 public boolean addPacketHeaderIndex() throws CTFReaderException
{
230 long currentPos
= 0L;
231 if (!fIndex
.getEntries().isEmpty()) {
232 StreamInputPacketIndexEntry pos
= fIndex
.getEntries().lastElement();
233 currentPos
= computeNextOffset(pos
);
235 long fileSize
= getStreamSize();
236 if (currentPos
< fileSize
) {
237 BitBuffer bitBuffer
= new BitBuffer();
238 bitBuffer
.setByteOrder(getStream().getTrace().getByteOrder());
239 StreamInputPacketIndexEntry packetIndex
= new StreamInputPacketIndexEntry(
241 createPacketIndexEntry(fileSize
, currentPos
, packetIndex
,
242 fTracePacketHeaderDecl
, fStreamPacketContextDecl
, bitBuffer
);
243 fIndex
.addEntry(packetIndex
);
249 private long getStreamSize() {
250 return fFile
.length();
253 private long createPacketIndexEntry(long fileSizeBytes
,
254 long packetOffsetBytes
, StreamInputPacketIndexEntry packetIndex
,
255 StructDeclaration tracePacketHeaderDecl
,
256 StructDeclaration streamPacketContextDecl
, @NonNull BitBuffer bitBuffer
)
257 throws CTFReaderException
{
260 * Ignoring the return value, but this call is needed to initialize the
263 createPacketBitBuffer(fileSizeBytes
, packetOffsetBytes
, packetIndex
, bitBuffer
);
266 * Read the trace packet header if it exists.
268 if (tracePacketHeaderDecl
!= null) {
269 parseTracePacketHeader(tracePacketHeaderDecl
, bitBuffer
);
273 * Read the stream packet context if it exists.
275 if (streamPacketContextDecl
!= null) {
276 parsePacketContext(fileSizeBytes
, streamPacketContextDecl
,
277 bitBuffer
, packetIndex
);
279 setPacketContextNull(fileSizeBytes
, packetIndex
);
282 /* Basic validation */
283 if (packetIndex
.getContentSizeBits() > packetIndex
.getPacketSizeBits()) {
284 throw new CTFReaderException("Content size > packet size"); //$NON-NLS-1$
287 if (packetIndex
.getPacketSizeBits() > ((fileSizeBytes
- packetIndex
288 .getOffsetBytes()) * 8)) {
289 throw new CTFReaderException("Not enough data remaining in the file for the size of this packet"); //$NON-NLS-1$
293 * Offset in the file, in bits
295 packetIndex
.setDataOffsetBits(bitBuffer
.position());
298 * Update the counting packet offset
300 return computeNextOffset(packetIndex
);
307 private static long computeNextOffset(
308 StreamInputPacketIndexEntry packetIndex
) {
309 return packetIndex
.getOffsetBytes()
310 + ((packetIndex
.getPacketSizeBits() + 7) / 8);
313 ByteBuffer
getByteBufferAt(long position
, long size
) throws IOException
{
314 return fFileChannel
.map(MapMode
.READ_ONLY
, position
, size
);
318 * @param fileSizeBytes
319 * @param packetOffsetBytes
323 * @throws CTFReaderException
325 private ByteBuffer
createPacketBitBuffer(long fileSizeBytes
,
326 long packetOffsetBytes
, StreamInputPacketIndexEntry packetIndex
,
327 BitBuffer bitBuffer
) throws CTFReaderException
{
329 * Initial size, it should map at least the packet header + context
332 * TODO: use a less arbitrary size.
336 * If there is less data remaining than what we want to map, reduce the
339 if ((fileSizeBytes
- packetIndex
.getOffsetBytes()) < mapSize
) {
340 mapSize
= fileSizeBytes
- packetIndex
.getOffsetBytes();
349 bb
= getByteBufferAt(packetOffsetBytes
, mapSize
);
350 } catch (IOException e
) {
351 throw new CTFReaderException(e
);
353 bitBuffer
.setByteBuffer(bb
);
357 private void parseTracePacketHeader(StructDeclaration tracePacketHeaderDecl
,
358 @NonNull BitBuffer bitBuffer
) throws CTFReaderException
{
359 StructDefinition tracePacketHeaderDef
= tracePacketHeaderDecl
.createDefinition(fStream
.getTrace(), LexicalScope
.TRACE_PACKET_HEADER
.getName(), bitBuffer
);
362 * Check the CTF magic number
364 IntegerDefinition magicDef
= (IntegerDefinition
) tracePacketHeaderDef
365 .lookupDefinition("magic"); //$NON-NLS-1$
366 if (magicDef
!= null) {
367 int magic
= (int) magicDef
.getValue();
368 if (magic
!= Utils
.CTF_MAGIC
) {
369 throw new CTFReaderException(
370 "CTF magic mismatch " + Integer
.toHexString(magic
) + " vs " + Integer
.toHexString(Utils
.CTF_MAGIC
)); //$NON-NLS-1$//$NON-NLS-2$
375 * Check the trace UUID
377 ArrayDefinition uuidDef
=
378 (ArrayDefinition
) tracePacketHeaderDef
.lookupDefinition("uuid"); //$NON-NLS-1$
379 if (uuidDef
!= null) {
380 UUID uuid
= Utils
.getUUIDfromDefinition(uuidDef
);
382 if (!getStream().getTrace().getUUID().equals(uuid
)) {
383 throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$
388 * Check that the stream id did not change
390 IntegerDefinition streamIDDef
= (IntegerDefinition
) tracePacketHeaderDef
391 .lookupDefinition("stream_id"); //$NON-NLS-1$
392 if (streamIDDef
!= null) {
393 long streamID
= streamIDDef
.getValue();
395 if (streamID
!= getStream().getId()) {
396 throw new CTFReaderException("Stream ID changing within a StreamInput"); //$NON-NLS-1$
401 private static void setPacketContextNull(long fileSizeBytes
,
402 StreamInputPacketIndexEntry packetIndex
) {
404 * If there is no packet context, infer the content and packet size from
405 * the file size (assume that there is only one packet and no padding)
407 packetIndex
.setContentSizeBits(fileSizeBytes
* 8);
408 packetIndex
.setPacketSizeBits(fileSizeBytes
* 8);
411 private void parsePacketContext(long fileSizeBytes
,
412 StructDeclaration streamPacketContextDecl
, @NonNull BitBuffer bitBuffer
,
413 StreamInputPacketIndexEntry packetIndex
) throws CTFReaderException
{
414 StructDefinition streamPacketContextDef
= streamPacketContextDecl
.createDefinition(null, LexicalScope
.STREAM_PACKET_CONTEXT
.getName(), bitBuffer
);
416 for (String field
: streamPacketContextDef
.getDeclaration()
418 Definition id
= streamPacketContextDef
.lookupDefinition(field
);
419 if (id
instanceof IntegerDefinition
) {
420 packetIndex
.addAttribute(field
,
421 ((IntegerDefinition
) id
).getValue());
422 } else if (id
instanceof FloatDefinition
) {
423 packetIndex
.addAttribute(field
,
424 ((FloatDefinition
) id
).getValue());
425 } else if (id
instanceof EnumDefinition
) {
426 packetIndex
.addAttribute(field
,
427 ((EnumDefinition
) id
).getValue());
428 } else if (id
instanceof StringDefinition
) {
429 packetIndex
.addAttribute(field
,
430 ((StringDefinition
) id
).getValue());
434 Long contentSize
= (Long
) packetIndex
.lookupAttribute("content_size"); //$NON-NLS-1$
435 Long packetSize
= (Long
) packetIndex
.lookupAttribute("packet_size"); //$NON-NLS-1$
436 Long tsBegin
= (Long
) packetIndex
.lookupAttribute("timestamp_begin"); //$NON-NLS-1$
437 Long tsEnd
= (Long
) packetIndex
.lookupAttribute("timestamp_end"); //$NON-NLS-1$
438 String device
= (String
) packetIndex
.lookupAttribute("device"); //$NON-NLS-1$
440 Long cpuId
= (Long
) packetIndex
.lookupAttribute("cpu_id"); //$NON-NLS-1$
441 Long lostEvents
= (Long
) packetIndex
.lookupAttribute("events_discarded"); //$NON-NLS-1$
443 /* Read the content size in bits */
444 if (contentSize
!= null) {
445 packetIndex
.setContentSizeBits(contentSize
.intValue());
446 } else if (packetSize
!= null) {
447 packetIndex
.setContentSizeBits(packetSize
.longValue());
449 packetIndex
.setContentSizeBits((int) (fileSizeBytes
* 8));
452 /* Read the packet size in bits */
453 if (packetSize
!= null) {
454 packetIndex
.setPacketSizeBits(packetSize
.intValue());
455 } else if (packetIndex
.getContentSizeBits() != 0) {
456 packetIndex
.setPacketSizeBits(packetIndex
.getContentSizeBits());
458 packetIndex
.setPacketSizeBits((int) (fileSizeBytes
* 8));
461 /* Read the begin timestamp */
462 if (tsBegin
!= null) {
463 packetIndex
.setTimestampBegin(tsBegin
.longValue());
466 /* Read the end timestamp */
469 tsEnd
= Long
.MAX_VALUE
;
471 packetIndex
.setTimestampEnd(tsEnd
.longValue());
472 setTimestampEnd(packetIndex
.getTimestampEnd());
475 if (device
!= null) {
476 packetIndex
.setTarget(device
);
480 packetIndex
.setTarget("CPU" + cpuId
.toString()); //$NON-NLS-1$
483 if (lostEvents
!= null) {
484 packetIndex
.setLostEvents(lostEvents
- fLostSoFar
);
485 fLostSoFar
= lostEvents
;
490 public int hashCode() {
491 final int prime
= 31;
493 result
= (prime
* result
) + ((fFile
== null) ?
0 : fFile
.hashCode());
498 public boolean equals(Object obj
) {
505 if (!(obj
instanceof CTFStreamInput
)) {
508 CTFStreamInput other
= (CTFStreamInput
) obj
;
510 if (other
.fFile
!= null) {
513 } else if (!fFile
.equals(other
.fFile
)) {