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
.tracecompass
.ctf
.core
.trace
;
16 import java
.io
.IOException
;
17 import java
.nio
.ByteBuffer
;
18 import java
.nio
.channels
.FileChannel
;
19 import java
.nio
.channels
.FileChannel
.MapMode
;
20 import java
.nio
.file
.StandardOpenOption
;
21 import java
.util
.UUID
;
23 import org
.eclipse
.jdt
.annotation
.NonNullByDefault
;
24 import org
.eclipse
.jdt
.annotation
.Nullable
;
25 import org
.eclipse
.tracecompass
.ctf
.core
.CTFException
;
26 import org
.eclipse
.tracecompass
.ctf
.core
.event
.io
.BitBuffer
;
27 import org
.eclipse
.tracecompass
.ctf
.core
.event
.scope
.IDefinitionScope
;
28 import org
.eclipse
.tracecompass
.ctf
.core
.event
.scope
.ILexicalScope
;
29 import org
.eclipse
.tracecompass
.ctf
.core
.event
.scope
.LexicalScope
;
30 import org
.eclipse
.tracecompass
.ctf
.core
.event
.types
.AbstractArrayDefinition
;
31 import org
.eclipse
.tracecompass
.ctf
.core
.event
.types
.Definition
;
32 import org
.eclipse
.tracecompass
.ctf
.core
.event
.types
.IntegerDefinition
;
33 import org
.eclipse
.tracecompass
.ctf
.core
.event
.types
.StructDeclaration
;
34 import org
.eclipse
.tracecompass
.ctf
.core
.event
.types
.StructDefinition
;
35 import org
.eclipse
.tracecompass
.internal
.ctf
.core
.SafeMappedByteBuffer
;
36 import org
.eclipse
.tracecompass
.internal
.ctf
.core
.trace
.StreamInputPacketIndex
;
37 import org
.eclipse
.tracecompass
.internal
.ctf
.core
.trace
.StreamInputPacketIndexEntry
;
38 import org
.eclipse
.tracecompass
.internal
.ctf
.core
.trace
.Utils
;
41 * <b><u>StreamInput</u></b>
43 * Represents a trace file that belongs to a certain stream.
46 public class CTFStreamInput
implements IDefinitionScope
{
48 // ------------------------------------------------------------------------
50 // ------------------------------------------------------------------------
52 private static final int MAP_SIZE
= 4096;
55 * The associated Stream
57 private final CTFStream fStream
;
60 * Information on the file (used for debugging)
62 private final File fFile
;
67 private final String fFileName
;
70 * The packet index of this input
72 private final StreamInputPacketIndex fIndex
;
74 private long fTimestampEnd
;
77 * Definition of trace packet header
79 private final StructDeclaration fTracePacketHeaderDecl
;
82 * Definition of trace stream packet context
84 private final StructDeclaration fStreamPacketContextDecl
;
87 * Total number of lost events in this stream
89 private long fLostSoFar
= 0;
91 // ------------------------------------------------------------------------
93 // ------------------------------------------------------------------------
96 * Constructs a StreamInput.
99 * The stream to which this StreamInput belongs to.
101 * Information about the trace file (for debugging purposes).
103 public CTFStreamInput(CTFStream stream
, File file
) {
106 String name
= fFile
.getName();
108 throw new IllegalStateException("File cannot have a null name"); //$NON-NLS-1$
112 fIndex
= new StreamInputPacketIndex();
114 * Create the definitions we need to read the packet headers + contexts
116 StructDeclaration packetHeader
= getStream().getTrace().getPacketHeader();
117 if (packetHeader
!= null) {
118 fTracePacketHeaderDecl
= packetHeader
;
120 fTracePacketHeaderDecl
= new StructDeclaration(1);
122 StructDeclaration packetContextDecl
= getStream().getPacketContextDecl();
123 if (packetContextDecl
!= null) {
124 fStreamPacketContextDecl
= packetContextDecl
;
126 fStreamPacketContextDecl
= new StructDeclaration(1);
130 // ------------------------------------------------------------------------
131 // Getters/Setters/Predicates
132 // ------------------------------------------------------------------------
135 * Gets the stream the streamInput wrapper is wrapping
137 * @return the stream the streamInput wrapper is wrapping
139 public CTFStream
getStream() {
144 * The common streamInput Index
146 * @return the stream input Index
148 StreamInputPacketIndex
getIndex() {
153 * Gets the filename of the streamInput file.
155 * @return the filename of the streaminput file.
157 public String
getFilename() {
162 * Gets the last read timestamp of a stream. (this is not necessarily the
163 * last time in the stream.)
165 * @return the last read timestamp
167 public long getTimestampEnd() {
168 return fTimestampEnd
;
172 * Sets the last read timestamp of a stream. (this is not necessarily the
173 * last time in the stream.)
175 * @param timestampEnd
176 * the last read timestamp
178 public void setTimestampEnd(long timestampEnd
) {
179 fTimestampEnd
= timestampEnd
;
183 * Useless for streaminputs
186 public LexicalScope
getScopePath() {
187 return ILexicalScope
.STREAM
;
190 // ------------------------------------------------------------------------
192 // ------------------------------------------------------------------------
195 public @Nullable Definition
lookupDefinition(@Nullable String lookupPath
) {
196 /* TODO: lookup in different dynamic scopes is not supported yet. */
201 * Create the index for this trace file.
203 public void setupIndex() {
206 * The BitBuffer to extract data from the StreamInput
208 BitBuffer bitBuffer
= new BitBuffer();
209 bitBuffer
.setByteOrder(getStream().getTrace().getByteOrder());
214 * Adds the next packet header index entry to the index of a stream input.
216 * <strong>This method is slow and can corrupt data if not used
219 * @return true if there are more packets to add
220 * @throws CTFException
221 * If there was a problem reading the packed header
223 public boolean addPacketHeaderIndex() throws CTFException
{
224 long currentPosBits
= 0L;
225 if (!fIndex
.isEmpty()) {
226 ICTFPacketDescriptor pos
= fIndex
.lastElement();
228 throw new IllegalStateException("Index contains null packet entries"); //$NON-NLS-1$
230 currentPosBits
= pos
.getOffsetBits() + pos
.getPacketSizeBits();
232 if (currentPosBits
< getStreamSizeBits()) {
233 fIndex
.append(createPacketIndexEntry(currentPosBits
));
239 private long getStreamSizeBits() {
240 return fFile
.length() * Byte
.SIZE
;
243 private ICTFPacketDescriptor
createPacketIndexEntry(long dataOffsetbits
)
244 throws CTFException
{
246 try (FileChannel fc
= FileChannel
.open(fFile
.toPath(), StandardOpenOption
.READ
)) {
248 throw new IOException("Failed to create FileChannel"); //$NON-NLS-1$
250 BitBuffer bitBuffer
= createBitBufferForPacketHeader(fc
, dataOffsetbits
);
252 * Read the trace packet header if it exists.
254 parseTracePacketHeader(bitBuffer
);
257 * Read the stream packet context if it exists.
259 long size
= fc
.size();
260 ICTFPacketDescriptor packetIndex
= parsePacketContext(dataOffsetbits
, size
, bitBuffer
);
262 /* Basic validation */
263 if (packetIndex
.getContentSizeBits() > packetIndex
.getPacketSizeBits()) {
264 throw new CTFException("Content size > packet size"); //$NON-NLS-1$
267 if (packetIndex
.getPacketSizeBits() > ((size
* Byte
.SIZE
- packetIndex
.getOffsetBits()))) {
268 throw new CTFException("Not enough data remaining in the file for the size of this packet"); //$NON-NLS-1$
271 } catch (IOException e
) {
272 throw new CTFException("Failed to create packet index entry", e
); //$NON-NLS-1$
276 private BitBuffer
createBitBufferForPacketHeader(FileChannel fc
, long dataOffsetbits
) throws CTFException
, IOException
{
278 * create a packet bit buffer to read the packet header
280 int maximumSize
= fStreamPacketContextDecl
.getMaximumSize() + fTracePacketHeaderDecl
.getMaximumSize();
281 BitBuffer bitBuffer
= new BitBuffer(createPacketBitBuffer(fc
, dataOffsetbits
/Byte
.SIZE
, maximumSize
));
282 bitBuffer
.setByteOrder(getStream().getTrace().getByteOrder());
286 private static ByteBuffer
getByteBufferAt(FileChannel fc
, long position
, long size
) throws CTFException
, IOException
{
287 ByteBuffer map
= SafeMappedByteBuffer
.map(fc
, MapMode
.READ_ONLY
, position
, size
);
289 throw new CTFException("Failed to allocate mapped byte buffer"); //$NON-NLS-1$
294 private static ByteBuffer
createPacketBitBuffer(FileChannel fc
,
295 long packetOffsetBytes
, long maxSize
) throws CTFException
, IOException
{
297 * If there is less data remaining than what we want to map, reduce the
300 long remain
= fc
.size() - packetOffsetBytes
;
302 * Initial size, it is the minimum of the the file size and the maximum
303 * possible size of the
305 long mapSize
= Math
.min(remain
, MAP_SIZE
);
306 if (maxSize
< mapSize
) {
314 return getByteBufferAt(fc
, packetOffsetBytes
, mapSize
);
315 } catch (IllegalArgumentException
| IOException e
) {
316 throw new CTFException(e
);
320 private StructDefinition
parseTracePacketHeader(
321 BitBuffer bitBuffer
) throws CTFException
{
323 StructDefinition tracePacketHeaderDef
= fTracePacketHeaderDecl
.createDefinition(fStream
.getTrace(), ILexicalScope
.TRACE_PACKET_HEADER
, bitBuffer
);
326 * Check the CTF magic number
328 IntegerDefinition magicDef
= (IntegerDefinition
) tracePacketHeaderDef
329 .lookupDefinition("magic"); //$NON-NLS-1$
330 if (magicDef
!= null) {
331 int magic
= (int) magicDef
.getValue();
332 if (magic
!= Utils
.CTF_MAGIC
) {
333 throw new CTFException(
334 "CTF magic mismatch " + Integer
.toHexString(magic
) + " vs " + Integer
.toHexString(Utils
.CTF_MAGIC
)); //$NON-NLS-1$//$NON-NLS-2$
339 * Check the trace UUID
341 AbstractArrayDefinition uuidDef
=
342 (AbstractArrayDefinition
) tracePacketHeaderDef
.lookupDefinition("uuid"); //$NON-NLS-1$
343 if (uuidDef
!= null) {
344 UUID uuid
= Utils
.getUUIDfromDefinition(uuidDef
);
346 if (!getStream().getTrace().getUUID().equals(uuid
)) {
347 throw new CTFException("UUID mismatch"); //$NON-NLS-1$
352 * Check that the stream id did not change
354 IntegerDefinition streamIDDef
= (IntegerDefinition
) tracePacketHeaderDef
355 .lookupDefinition("stream_id"); //$NON-NLS-1$
356 if (streamIDDef
!= null) {
357 long streamID
= streamIDDef
.getValue();
359 if (streamID
!= getStream().getId()) {
360 throw new CTFException("Stream ID changing within a StreamInput"); //$NON-NLS-1$
363 return tracePacketHeaderDef
;
366 private ICTFPacketDescriptor
parsePacketContext(long dataOffsetBits
, long fileSizeBytes
,
367 BitBuffer bitBuffer
) throws CTFException
{
368 ICTFPacketDescriptor packetIndex
;
369 StructDefinition streamPacketContextDef
= fStreamPacketContextDecl
.createDefinition(this, ILexicalScope
.STREAM_PACKET_CONTEXT
, bitBuffer
);
370 packetIndex
= new StreamInputPacketIndexEntry(dataOffsetBits
, streamPacketContextDef
, fileSizeBytes
, fLostSoFar
);
371 fLostSoFar
= packetIndex
.getLostEvents() + fLostSoFar
;
372 setTimestampEnd(packetIndex
.getTimestampEnd());
382 public File
getFile() {
387 public int hashCode() {
388 final int prime
= 31;
390 result
= (prime
* result
) + fFile
.hashCode();
395 public boolean equals(@Nullable Object obj
) {
402 if (!(obj
instanceof CTFStreamInput
)) {
405 CTFStreamInput other
= (CTFStreamInput
) obj
;
406 if (!fFile
.equals(other
.fFile
)) {