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
;
65 * The packet index of this input
67 private final StreamInputPacketIndex fIndex
;
69 private long fTimestampEnd
;
72 * Definition of trace packet header
74 private final StructDeclaration fTracePacketHeaderDecl
;
77 * Definition of trace stream packet context
79 private final StructDeclaration fStreamPacketContextDecl
;
82 * Total number of lost events in this stream
84 private long fLostSoFar
= 0;
86 // ------------------------------------------------------------------------
88 // ------------------------------------------------------------------------
91 * Constructs a StreamInput.
94 * The stream to which this StreamInput belongs to.
96 * Information about the trace file (for debugging purposes).
98 public CTFStreamInput(CTFStream stream
, File file
) {
101 fIndex
= new StreamInputPacketIndex();
103 * Create the definitions we need to read the packet headers + contexts
105 StructDeclaration packetHeader
= getStream().getTrace().getPacketHeader();
106 if (packetHeader
!= null) {
107 fTracePacketHeaderDecl
= packetHeader
;
109 fTracePacketHeaderDecl
= new StructDeclaration(1);
111 StructDeclaration packetContextDecl
= getStream().getPacketContextDecl();
112 if (packetContextDecl
!= null) {
113 fStreamPacketContextDecl
= packetContextDecl
;
115 fStreamPacketContextDecl
= new StructDeclaration(1);
119 // ------------------------------------------------------------------------
120 // Getters/Setters/Predicates
121 // ------------------------------------------------------------------------
124 * Gets the stream the streamInput wrapper is wrapping
126 * @return the stream the streamInput wrapper is wrapping
128 public CTFStream
getStream() {
133 * The common streamInput Index
135 * @return the stream input Index
137 StreamInputPacketIndex
getIndex() {
142 * Gets the filename of the streamInput file.
144 * @return the filename of the streaminput file.
146 public String
getFilename() {
147 String name
= fFile
.getName();
149 throw new IllegalStateException("File cannot have a null name"); //$NON-NLS-1$
155 * Gets the last read timestamp of a stream. (this is not necessarily the
156 * last time in the stream.)
158 * @return the last read timestamp
160 public long getTimestampEnd() {
161 return fTimestampEnd
;
165 * Sets the last read timestamp of a stream. (this is not necessarily the
166 * last time in the stream.)
168 * @param timestampEnd
169 * the last read timestamp
171 public void setTimestampEnd(long timestampEnd
) {
172 fTimestampEnd
= timestampEnd
;
176 * Useless for streaminputs
179 public LexicalScope
getScopePath() {
180 return ILexicalScope
.STREAM
;
183 // ------------------------------------------------------------------------
185 // ------------------------------------------------------------------------
188 public @Nullable Definition
lookupDefinition(@Nullable String lookupPath
) {
189 /* TODO: lookup in different dynamic scopes is not supported yet. */
194 * Create the index for this trace file.
196 public void setupIndex() {
199 * The BitBuffer to extract data from the StreamInput
201 BitBuffer bitBuffer
= new BitBuffer();
202 bitBuffer
.setByteOrder(getStream().getTrace().getByteOrder());
207 * Adds the next packet header index entry to the index of a stream input.
209 * <strong>This method is slow and can corrupt data if not used
212 * @return true if there are more packets to add
213 * @throws CTFException
214 * If there was a problem reading the packed header
216 public boolean addPacketHeaderIndex() throws CTFException
{
217 long currentPosBits
= 0L;
218 if (!fIndex
.isEmpty()) {
219 ICTFPacketDescriptor pos
= fIndex
.lastElement();
221 throw new IllegalStateException("Index contains null packet entries"); //$NON-NLS-1$
223 currentPosBits
= pos
.getOffsetBits() + pos
.getPacketSizeBits();
225 if (currentPosBits
< getStreamSizeBits()) {
226 fIndex
.append(createPacketIndexEntry(currentPosBits
));
232 private long getStreamSizeBits() {
233 return fFile
.length() * Byte
.SIZE
;
236 private ICTFPacketDescriptor
createPacketIndexEntry(long dataOffsetbits
)
237 throws CTFException
{
239 try (FileChannel fc
= FileChannel
.open(fFile
.toPath(), StandardOpenOption
.READ
)) {
241 throw new IOException("Failed to create FileChannel"); //$NON-NLS-1$
243 BitBuffer bitBuffer
= createBitBufferForPacketHeader(fc
, dataOffsetbits
);
245 * Read the trace packet header if it exists.
247 parseTracePacketHeader(bitBuffer
);
250 * Read the stream packet context if it exists.
252 long size
= fc
.size();
253 ICTFPacketDescriptor packetIndex
= parsePacketContext(dataOffsetbits
, size
, bitBuffer
);
255 /* Basic validation */
256 if (packetIndex
.getContentSizeBits() > packetIndex
.getPacketSizeBits()) {
257 throw new CTFException("Content size > packet size"); //$NON-NLS-1$
260 if (packetIndex
.getPacketSizeBits() > ((size
* Byte
.SIZE
- packetIndex
.getOffsetBits()))) {
261 throw new CTFException("Not enough data remaining in the file for the size of this packet"); //$NON-NLS-1$
264 } catch (IOException e
) {
265 throw new CTFException("Failed to create packet index entry", e
); //$NON-NLS-1$
269 private BitBuffer
createBitBufferForPacketHeader(FileChannel fc
, long dataOffsetbits
) throws CTFException
, IOException
{
271 * create a packet bit buffer to read the packet header
273 int maximumSize
= fStreamPacketContextDecl
.getMaximumSize() + fTracePacketHeaderDecl
.getMaximumSize();
274 BitBuffer bitBuffer
= new BitBuffer(createPacketBitBuffer(fc
, dataOffsetbits
/Byte
.SIZE
, maximumSize
));
275 bitBuffer
.setByteOrder(getStream().getTrace().getByteOrder());
279 private static ByteBuffer
getByteBufferAt(FileChannel fc
, long position
, long size
) throws CTFException
, IOException
{
280 ByteBuffer map
= SafeMappedByteBuffer
.map(fc
, MapMode
.READ_ONLY
, position
, size
);
282 throw new CTFException("Failed to allocate mapped byte buffer"); //$NON-NLS-1$
287 private static ByteBuffer
createPacketBitBuffer(FileChannel fc
,
288 long packetOffsetBytes
, long maxSize
) throws CTFException
, IOException
{
290 * If there is less data remaining than what we want to map, reduce the
293 long remain
= fc
.size() - packetOffsetBytes
;
295 * Initial size, it is the minimum of the the file size and the maximum
296 * possible size of the
298 long mapSize
= Math
.min(remain
, MAP_SIZE
);
299 if (maxSize
< mapSize
) {
307 return getByteBufferAt(fc
, packetOffsetBytes
, mapSize
);
308 } catch (IllegalArgumentException
| IOException e
) {
309 throw new CTFException(e
);
313 private StructDefinition
parseTracePacketHeader(
314 BitBuffer bitBuffer
) throws CTFException
{
316 StructDefinition tracePacketHeaderDef
= fTracePacketHeaderDecl
.createDefinition(fStream
.getTrace(), ILexicalScope
.TRACE_PACKET_HEADER
, bitBuffer
);
319 * Check the CTF magic number
321 IntegerDefinition magicDef
= (IntegerDefinition
) tracePacketHeaderDef
322 .lookupDefinition("magic"); //$NON-NLS-1$
323 if (magicDef
!= null) {
324 int magic
= (int) magicDef
.getValue();
325 if (magic
!= Utils
.CTF_MAGIC
) {
326 throw new CTFException(
327 "CTF magic mismatch " + Integer
.toHexString(magic
) + " vs " + Integer
.toHexString(Utils
.CTF_MAGIC
)); //$NON-NLS-1$//$NON-NLS-2$
332 * Check the trace UUID
334 AbstractArrayDefinition uuidDef
=
335 (AbstractArrayDefinition
) tracePacketHeaderDef
.lookupDefinition("uuid"); //$NON-NLS-1$
336 if (uuidDef
!= null) {
337 UUID uuid
= Utils
.getUUIDfromDefinition(uuidDef
);
339 if (!getStream().getTrace().getUUID().equals(uuid
)) {
340 throw new CTFException("UUID mismatch"); //$NON-NLS-1$
345 * Check that the stream id did not change
347 IntegerDefinition streamIDDef
= (IntegerDefinition
) tracePacketHeaderDef
348 .lookupDefinition("stream_id"); //$NON-NLS-1$
349 if (streamIDDef
!= null) {
350 long streamID
= streamIDDef
.getValue();
352 if (streamID
!= getStream().getId()) {
353 throw new CTFException("Stream ID changing within a StreamInput"); //$NON-NLS-1$
356 return tracePacketHeaderDef
;
359 private ICTFPacketDescriptor
parsePacketContext(long dataOffsetBits
, long fileSizeBytes
,
360 BitBuffer bitBuffer
) throws CTFException
{
361 ICTFPacketDescriptor packetIndex
;
362 StructDefinition streamPacketContextDef
= fStreamPacketContextDecl
.createDefinition(this, ILexicalScope
.STREAM_PACKET_CONTEXT
, bitBuffer
);
363 packetIndex
= new StreamInputPacketIndexEntry(dataOffsetBits
, streamPacketContextDef
, fileSizeBytes
, fLostSoFar
);
364 fLostSoFar
= packetIndex
.getLostEvents() + fLostSoFar
;
365 setTimestampEnd(packetIndex
.getTimestampEnd());
375 public File
getFile() {
380 public int hashCode() {
381 final int prime
= 31;
383 result
= (prime
* result
) + fFile
.hashCode();
388 public boolean equals(@Nullable Object obj
) {
395 if (!(obj
instanceof CTFStreamInput
)) {
398 CTFStreamInput other
= (CTFStreamInput
) obj
;
399 if (!fFile
.equals(other
.fFile
)) {