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 ICTFStream 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).
104 public CTFStreamInput(ICTFStream stream
, File file
) {
107 String name
= fFile
.getName();
109 throw new IllegalStateException("File cannot have a null name"); //$NON-NLS-1$
113 fIndex
= new StreamInputPacketIndex();
115 * Create the definitions we need to read the packet headers + contexts
117 StructDeclaration packetHeader
= getStream().getTrace().getPacketHeader();
118 if (packetHeader
!= null) {
119 fTracePacketHeaderDecl
= packetHeader
;
121 fTracePacketHeaderDecl
= new StructDeclaration(1);
123 StructDeclaration packetContextDecl
= getStream().getPacketContextDecl();
124 if (packetContextDecl
!= null) {
125 fStreamPacketContextDecl
= packetContextDecl
;
127 fStreamPacketContextDecl
= new StructDeclaration(1);
131 // ------------------------------------------------------------------------
132 // Getters/Setters/Predicates
133 // ------------------------------------------------------------------------
136 * Gets the stream the streamInput wrapper is wrapping
138 * @return the stream the streamInput wrapper is wrapping
141 public ICTFStream
getStream() {
146 * The common streamInput Index
148 * @return the stream input Index
150 StreamInputPacketIndex
getIndex() {
155 * Gets the filename of the streamInput file.
157 * @return the filename of the streaminput file.
159 public String
getFilename() {
164 * Gets the last read timestamp of a stream. (this is not necessarily the
165 * last time in the stream.)
167 * @return the last read timestamp
169 public long getTimestampEnd() {
170 return fTimestampEnd
;
174 * Sets the last read timestamp of a stream. (this is not necessarily the
175 * last time in the stream.)
177 * @param timestampEnd
178 * the last read timestamp
180 public void setTimestampEnd(long timestampEnd
) {
181 fTimestampEnd
= timestampEnd
;
185 * Useless for streaminputs
188 public LexicalScope
getScopePath() {
189 return ILexicalScope
.STREAM
;
192 // ------------------------------------------------------------------------
194 // ------------------------------------------------------------------------
197 public @Nullable Definition
lookupDefinition(@Nullable String lookupPath
) {
198 /* TODO: lookup in different dynamic scopes is not supported yet. */
203 * Create the index for this trace file.
205 public void setupIndex() {
208 * The BitBuffer to extract data from the StreamInput
210 BitBuffer bitBuffer
= new BitBuffer();
211 bitBuffer
.setByteOrder(getStream().getTrace().getByteOrder());
216 * Adds the next packet header index entry to the index of a stream input.
218 * <strong>This method is slow and can corrupt data if not used
221 * @return true if there are more packets to add
222 * @throws CTFException
223 * If there was a problem reading the packed header
225 public boolean addPacketHeaderIndex() throws CTFException
{
226 long currentPosBits
= 0L;
227 if (!fIndex
.isEmpty()) {
228 ICTFPacketDescriptor pos
= fIndex
.lastElement();
230 throw new IllegalStateException("Index contains null packet entries"); //$NON-NLS-1$
232 currentPosBits
= pos
.getOffsetBits() + pos
.getPacketSizeBits();
234 if (currentPosBits
< getStreamSizeBits()) {
235 fIndex
.append(createPacketIndexEntry(currentPosBits
));
241 private long getStreamSizeBits() {
242 return fFile
.length() * Byte
.SIZE
;
245 private ICTFPacketDescriptor
createPacketIndexEntry(long dataOffsetbits
)
246 throws CTFException
{
248 try (FileChannel fc
= FileChannel
.open(fFile
.toPath(), StandardOpenOption
.READ
)) {
250 throw new IOException("Failed to create FileChannel"); //$NON-NLS-1$
252 BitBuffer bitBuffer
= createBitBufferForPacketHeader(fc
, dataOffsetbits
);
254 * Read the trace packet header if it exists.
256 parseTracePacketHeader(bitBuffer
);
259 * Read the stream packet context if it exists.
261 long size
= fc
.size();
262 ICTFPacketDescriptor packetIndex
= parsePacketContext(dataOffsetbits
, size
, bitBuffer
);
264 /* Basic validation */
265 if (packetIndex
.getContentSizeBits() > packetIndex
.getPacketSizeBits()) {
266 throw new CTFException("Content size > packet size"); //$NON-NLS-1$
269 if (packetIndex
.getPacketSizeBits() > ((size
* Byte
.SIZE
- packetIndex
.getOffsetBits()))) {
270 throw new CTFException("Not enough data remaining in the file for the size of this packet"); //$NON-NLS-1$
273 } catch (IOException e
) {
274 throw new CTFException("Failed to create packet index entry", e
); //$NON-NLS-1$
278 private BitBuffer
createBitBufferForPacketHeader(FileChannel fc
, long dataOffsetbits
) throws CTFException
, IOException
{
280 * create a packet bit buffer to read the packet header
282 int maximumSize
= fStreamPacketContextDecl
.getMaximumSize() + fTracePacketHeaderDecl
.getMaximumSize();
283 BitBuffer bitBuffer
= new BitBuffer(createPacketBitBuffer(fc
, dataOffsetbits
/Byte
.SIZE
, maximumSize
));
284 bitBuffer
.setByteOrder(getStream().getTrace().getByteOrder());
288 private static ByteBuffer
getByteBufferAt(FileChannel fc
, long position
, long size
) throws CTFException
, IOException
{
289 ByteBuffer map
= SafeMappedByteBuffer
.map(fc
, MapMode
.READ_ONLY
, position
, size
);
291 throw new CTFException("Failed to allocate mapped byte buffer"); //$NON-NLS-1$
296 private static ByteBuffer
createPacketBitBuffer(FileChannel fc
,
297 long packetOffsetBytes
, long maxSize
) throws CTFException
, IOException
{
299 * If there is less data remaining than what we want to map, reduce the
302 long remain
= fc
.size() - packetOffsetBytes
;
304 * Initial size, it is the minimum of the the file size and the maximum
305 * possible size of the
307 long mapSize
= Math
.min(remain
, MAP_SIZE
);
308 if (maxSize
< mapSize
) {
316 return getByteBufferAt(fc
, packetOffsetBytes
, mapSize
);
317 } catch (IllegalArgumentException
| IOException e
) {
318 throw new CTFException(e
);
322 private StructDefinition
parseTracePacketHeader(
323 BitBuffer bitBuffer
) throws CTFException
{
325 StructDefinition tracePacketHeaderDef
= fTracePacketHeaderDecl
.createDefinition(fStream
.getTrace(), ILexicalScope
.TRACE_PACKET_HEADER
, bitBuffer
);
328 * Check the CTF magic number
330 IntegerDefinition magicDef
= (IntegerDefinition
) tracePacketHeaderDef
331 .lookupDefinition("magic"); //$NON-NLS-1$
332 if (magicDef
!= null) {
333 int magic
= (int) magicDef
.getValue();
334 if (magic
!= Utils
.CTF_MAGIC
) {
335 throw new CTFException(
336 "CTF magic mismatch " + Integer
.toHexString(magic
) + " vs " + Integer
.toHexString(Utils
.CTF_MAGIC
)); //$NON-NLS-1$//$NON-NLS-2$
341 * Check the trace UUID
343 AbstractArrayDefinition uuidDef
=
344 (AbstractArrayDefinition
) tracePacketHeaderDef
.lookupDefinition("uuid"); //$NON-NLS-1$
345 if (uuidDef
!= null) {
346 UUID uuid
= Utils
.getUUIDfromDefinition(uuidDef
);
348 if (!getStream().getTrace().getUUID().equals(uuid
)) {
349 throw new CTFException("UUID mismatch"); //$NON-NLS-1$
354 * Check that the stream id did not change
356 IntegerDefinition streamIDDef
= (IntegerDefinition
) tracePacketHeaderDef
357 .lookupDefinition("stream_id"); //$NON-NLS-1$
358 if (streamIDDef
!= null) {
359 long streamID
= streamIDDef
.getValue();
361 if (streamID
!= getStream().getId()) {
362 throw new CTFException("Stream ID changing within a StreamInput"); //$NON-NLS-1$
365 return tracePacketHeaderDef
;
368 private ICTFPacketDescriptor
parsePacketContext(long dataOffsetBits
, long fileSizeBytes
,
369 BitBuffer bitBuffer
) throws CTFException
{
370 ICTFPacketDescriptor packetIndex
;
371 StructDefinition streamPacketContextDef
= fStreamPacketContextDecl
.createDefinition(this, ILexicalScope
.STREAM_PACKET_CONTEXT
, bitBuffer
);
372 packetIndex
= new StreamInputPacketIndexEntry(dataOffsetBits
, streamPacketContextDef
, fileSizeBytes
, fLostSoFar
, bitBuffer
.position());
373 fLostSoFar
= packetIndex
.getLostEvents() + fLostSoFar
;
374 setTimestampEnd(packetIndex
.getTimestampEnd());
384 public File
getFile() {
389 public int hashCode() {
390 final int prime
= 31;
392 result
= (prime
* result
) + fFile
.hashCode();
397 public boolean equals(@Nullable Object obj
) {
404 if (!(obj
instanceof CTFStreamInput
)) {
407 CTFStreamInput other
= (CTFStreamInput
) obj
;
408 if (!fFile
.equals(other
.fFile
)) {