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 fFileName
= fFile
.getName();
109 fIndex
= new StreamInputPacketIndex();
111 * Create the definitions we need to read the packet headers + contexts
113 StructDeclaration packetHeader
= getStream().getTrace().getPacketHeader();
114 if (packetHeader
!= null) {
115 fTracePacketHeaderDecl
= packetHeader
;
117 fTracePacketHeaderDecl
= new StructDeclaration(1);
119 StructDeclaration packetContextDecl
= getStream().getPacketContextDecl();
120 if (packetContextDecl
!= null) {
121 fStreamPacketContextDecl
= packetContextDecl
;
123 fStreamPacketContextDecl
= new StructDeclaration(1);
127 // ------------------------------------------------------------------------
128 // Getters/Setters/Predicates
129 // ------------------------------------------------------------------------
132 * Gets the stream the streamInput wrapper is wrapping
134 * @return the stream the streamInput wrapper is wrapping
137 public ICTFStream
getStream() {
142 * The common streamInput Index
144 * @return the stream input Index
146 StreamInputPacketIndex
getIndex() {
151 * Gets the filename of the streamInput file.
153 * @return the filename of the streaminput file.
155 public String
getFilename() {
160 * Gets the last read timestamp of a stream. (this is not necessarily the
161 * last time in the stream.)
163 * @return the last read timestamp
165 public long getTimestampEnd() {
166 return fTimestampEnd
;
170 * Sets the last read timestamp of a stream. (this is not necessarily the
171 * last time in the stream.)
173 * @param timestampEnd
174 * the last read timestamp
176 public void setTimestampEnd(long timestampEnd
) {
177 fTimestampEnd
= timestampEnd
;
181 * Useless for streaminputs
184 public LexicalScope
getScopePath() {
185 return ILexicalScope
.STREAM
;
188 // ------------------------------------------------------------------------
190 // ------------------------------------------------------------------------
193 public @Nullable Definition
lookupDefinition(@Nullable String lookupPath
) {
194 /* TODO: lookup in different dynamic scopes is not supported yet. */
199 * Create the index for this trace file.
201 public void setupIndex() {
204 * The BitBuffer to extract data from the StreamInput
206 BitBuffer bitBuffer
= new BitBuffer();
207 bitBuffer
.setByteOrder(getStream().getTrace().getByteOrder());
212 * Adds the next packet header index entry to the index of a stream input.
214 * <strong>This method is slow and can corrupt data if not used
217 * @return true if there are more packets to add
218 * @throws CTFException
219 * If there was a problem reading the packed header
221 public boolean addPacketHeaderIndex() throws CTFException
{
222 long currentPosBits
= 0L;
223 if (!fIndex
.isEmpty()) {
224 ICTFPacketDescriptor pos
= fIndex
.lastElement();
226 throw new IllegalStateException("Index contains null packet entries"); //$NON-NLS-1$
228 currentPosBits
= pos
.getOffsetBits() + pos
.getPacketSizeBits();
230 if (currentPosBits
< getStreamSizeBits()) {
231 return fIndex
.append(createPacketIndexEntry(currentPosBits
));
236 private long getStreamSizeBits() {
237 return fFile
.length() * Byte
.SIZE
;
240 private ICTFPacketDescriptor
createPacketIndexEntry(long dataOffsetbits
)
241 throws CTFException
{
243 try (FileChannel fc
= FileChannel
.open(fFile
.toPath(), StandardOpenOption
.READ
)) {
245 throw new IOException("Failed to create FileChannel"); //$NON-NLS-1$
247 BitBuffer bitBuffer
= createBitBufferForPacketHeader(fc
, dataOffsetbits
);
249 * Read the trace packet header if it exists.
251 parseTracePacketHeader(bitBuffer
);
254 * Read the stream packet context if it exists.
256 long size
= fc
.size();
257 ICTFPacketDescriptor packetIndex
= parsePacketContext(dataOffsetbits
, size
, bitBuffer
);
259 /* Basic validation */
260 if (packetIndex
.getContentSizeBits() > packetIndex
.getPacketSizeBits()) {
261 throw new CTFException("Content size > packet size"); //$NON-NLS-1$
264 if (packetIndex
.getPacketSizeBits() > ((size
* Byte
.SIZE
- packetIndex
.getOffsetBits()))) {
265 throw new CTFException("Not enough data remaining in the file for the size of this packet"); //$NON-NLS-1$
268 } catch (IOException e
) {
269 throw new CTFException("Failed to create packet index entry", e
); //$NON-NLS-1$
273 private BitBuffer
createBitBufferForPacketHeader(FileChannel fc
, long dataOffsetbits
) throws CTFException
, IOException
{
275 * create a packet bit buffer to read the packet header
277 int maximumSize
= fStreamPacketContextDecl
.getMaximumSize() + fTracePacketHeaderDecl
.getMaximumSize();
278 BitBuffer bitBuffer
= new BitBuffer(createPacketBitBuffer(fc
, dataOffsetbits
/Byte
.SIZE
, maximumSize
));
279 bitBuffer
.setByteOrder(getStream().getTrace().getByteOrder());
283 private static ByteBuffer
getByteBufferAt(FileChannel fc
, long position
, long size
) throws CTFException
, IOException
{
284 ByteBuffer map
= SafeMappedByteBuffer
.map(fc
, MapMode
.READ_ONLY
, position
, size
);
286 throw new CTFException("Failed to allocate mapped byte buffer"); //$NON-NLS-1$
291 private static ByteBuffer
createPacketBitBuffer(FileChannel fc
,
292 long packetOffsetBytes
, long maxSize
) throws CTFException
, IOException
{
294 * If there is less data remaining than what we want to map, reduce the
297 long remain
= fc
.size() - packetOffsetBytes
;
299 * Initial size, it is the minimum of the the file size and the maximum
300 * possible size of the
302 long mapSize
= Math
.min(remain
, MAP_SIZE
);
303 if (maxSize
< mapSize
) {
311 return getByteBufferAt(fc
, packetOffsetBytes
, mapSize
);
312 } catch (IllegalArgumentException
| IOException e
) {
313 throw new CTFException(e
);
317 private StructDefinition
parseTracePacketHeader(
318 BitBuffer bitBuffer
) throws CTFException
{
320 StructDefinition tracePacketHeaderDef
= fTracePacketHeaderDecl
.createDefinition(fStream
.getTrace(), ILexicalScope
.TRACE_PACKET_HEADER
, bitBuffer
);
323 * Check the CTF magic number
325 IntegerDefinition magicDef
= (IntegerDefinition
) tracePacketHeaderDef
326 .lookupDefinition("magic"); //$NON-NLS-1$
327 if (magicDef
!= null) {
328 int magic
= (int) magicDef
.getValue();
329 if (magic
!= Utils
.CTF_MAGIC
) {
330 throw new CTFException(
331 "CTF magic mismatch " + Integer
.toHexString(magic
) + " vs " + Integer
.toHexString(Utils
.CTF_MAGIC
)); //$NON-NLS-1$//$NON-NLS-2$
336 * Check the trace UUID
338 AbstractArrayDefinition uuidDef
=
339 (AbstractArrayDefinition
) tracePacketHeaderDef
.lookupDefinition("uuid"); //$NON-NLS-1$
340 if (uuidDef
!= null) {
341 UUID uuid
= Utils
.getUUIDfromDefinition(uuidDef
);
343 if (!getStream().getTrace().getUUID().equals(uuid
)) {
344 throw new CTFException("UUID mismatch"); //$NON-NLS-1$
349 * Check that the stream id did not change
351 IntegerDefinition streamIDDef
= (IntegerDefinition
) tracePacketHeaderDef
352 .lookupDefinition("stream_id"); //$NON-NLS-1$
353 if (streamIDDef
!= null) {
354 long streamID
= streamIDDef
.getValue();
356 if (streamID
!= getStream().getId()) {
357 throw new CTFException("Stream ID changing within a StreamInput"); //$NON-NLS-1$
360 return tracePacketHeaderDef
;
363 private ICTFPacketDescriptor
parsePacketContext(long dataOffsetBits
, long fileSizeBytes
,
364 BitBuffer bitBuffer
) throws CTFException
{
365 ICTFPacketDescriptor packetIndex
;
366 StructDefinition streamPacketContextDef
= fStreamPacketContextDecl
.createDefinition(this, ILexicalScope
.STREAM_PACKET_CONTEXT
, bitBuffer
);
367 packetIndex
= new StreamInputPacketIndexEntry(dataOffsetBits
, streamPacketContextDef
, fileSizeBytes
, fLostSoFar
, bitBuffer
.position());
368 fLostSoFar
= packetIndex
.getLostEvents() + fLostSoFar
;
369 setTimestampEnd(packetIndex
.getTimestampEnd());
379 public File
getFile() {
384 public int hashCode() {
385 final int prime
= 31;
387 result
= (prime
* result
) + fFile
.hashCode();
392 public boolean equals(@Nullable Object obj
) {
399 if (!(obj
instanceof CTFStreamInput
)) {
402 CTFStreamInput other
= (CTFStreamInput
) obj
;
403 if (!fFile
.equals(other
.fFile
)) {