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
.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
.NonNull
;
24 import org
.eclipse
.linuxtools
.ctf
.core
.event
.io
.BitBuffer
;
25 import org
.eclipse
.linuxtools
.ctf
.core
.event
.scope
.IDefinitionScope
;
26 import org
.eclipse
.linuxtools
.ctf
.core
.event
.scope
.LexicalScope
;
27 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.Definition
;
28 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.EnumDefinition
;
29 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.FloatDefinition
;
30 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.IDefinition
;
31 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.IntegerDefinition
;
32 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StringDefinition
;
33 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StructDeclaration
;
34 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StructDefinition
;
35 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.SafeMappedByteBuffer
;
36 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.event
.types
.ArrayDefinition
;
37 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.trace
.StreamInputPacketIndex
;
38 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.trace
.StreamInputPacketIndexEntry
;
41 * <b><u>StreamInput</u></b>
43 * Represents a trace file that belongs to a certain stream.
47 // TODO: remove AutoCloseable
48 public class CTFStreamInput
implements IDefinitionScope
, AutoCloseable
{
50 // ------------------------------------------------------------------------
52 // ------------------------------------------------------------------------
55 * The associated Stream
57 private final CTFStream fStream
;
60 * Information on the file (used for debugging)
63 private final File fFile
;
66 * The packet index of this input
68 private final StreamInputPacketIndex fIndex
;
70 private long fTimestampEnd
;
73 * Definition of trace packet header
75 private StructDeclaration fTracePacketHeaderDecl
= null;
78 * Definition of trace stream packet context
80 private StructDeclaration fStreamPacketContextDecl
= null;
83 * Total number of lost events in this stream
85 private long fLostSoFar
= 0;
87 // ------------------------------------------------------------------------
89 // ------------------------------------------------------------------------
92 * Constructs a StreamInput.
95 * The stream to which this StreamInput belongs to.
97 * Information about the trace file (for debugging purposes).
99 public CTFStreamInput(CTFStream stream
, @NonNull File file
) {
102 fIndex
= new StreamInputPacketIndex();
106 public void close() throws IOException
{
109 // ------------------------------------------------------------------------
110 // Getters/Setters/Predicates
111 // ------------------------------------------------------------------------
114 * Gets the stream the streamInput wrapper is wrapping
116 * @return the stream the streamInput wrapper is wrapping
118 public CTFStream
getStream() {
123 * The common streamInput Index
125 * @return the stream input Index
127 StreamInputPacketIndex
getIndex() {
132 * Gets the filename of the streamInput file.
134 * @return the filename of the streaminput file.
136 public String
getFilename() {
137 return fFile
.getName();
141 * Gets the last read timestamp of a stream. (this is not necessarily the
142 * last time in the stream.)
144 * @return the last read timestamp
146 public long getTimestampEnd() {
147 return fTimestampEnd
;
151 * Sets the last read timestamp of a stream. (this is not necessarily the
152 * last time in the stream.)
154 * @param timestampEnd
155 * the last read timestamp
157 public void setTimestampEnd(long timestampEnd
) {
158 fTimestampEnd
= timestampEnd
;
162 * Useless for streaminputs
165 public LexicalScope
getScopePath() {
166 return LexicalScope
.STREAM
;
169 // ------------------------------------------------------------------------
171 // ------------------------------------------------------------------------
174 public Definition
lookupDefinition(String lookupPath
) {
175 /* TODO: lookup in different dynamic scopes is not supported yet. */
180 * Create the index for this trace file.
182 public void setupIndex() {
185 * The BitBuffer to extract data from the StreamInput
187 BitBuffer bitBuffer
= new BitBuffer();
188 bitBuffer
.setByteOrder(getStream().getTrace().getByteOrder());
191 * Create the definitions we need to read the packet headers + contexts
193 if (getStream().getTrace().getPacketHeader() != null) {
194 fTracePacketHeaderDecl
= getStream().getTrace().getPacketHeader();
197 if (getStream().getPacketContextDecl() != null) {
198 fStreamPacketContextDecl
= getStream().getPacketContextDecl();
204 * Adds the next packet header index entry to the index of a stream input.
206 * <strong>This method is slow and can corrupt data if not used
209 * @return true if there are more packets to add
210 * @throws CTFReaderException
211 * If there was a problem reading the packed header
213 public boolean addPacketHeaderIndex() throws CTFReaderException
{
214 long currentPos
= 0L;
215 if (!fIndex
.getEntries().isEmpty()) {
216 StreamInputPacketIndexEntry pos
= fIndex
.getEntries().lastElement();
217 currentPos
= computeNextOffset(pos
);
219 long fileSize
= getStreamSize();
220 if (currentPos
< fileSize
) {
222 StreamInputPacketIndexEntry packetIndex
= new StreamInputPacketIndexEntry(
224 createPacketIndexEntry(fileSize
, currentPos
, packetIndex
);
225 fIndex
.addEntry(packetIndex
);
231 private long getStreamSize() {
232 return fFile
.length();
235 private long createPacketIndexEntry(long fileSizeBytes
, long packetOffsetBytes
, StreamInputPacketIndexEntry packetIndex
)
236 throws CTFReaderException
{
238 long pos
= readPacketHeader(fileSizeBytes
, packetOffsetBytes
, packetIndex
);
240 /* Basic validation */
241 if (packetIndex
.getContentSizeBits() > packetIndex
.getPacketSizeBits()) {
242 throw new CTFReaderException("Content size > packet size"); //$NON-NLS-1$
245 if (packetIndex
.getPacketSizeBits() > ((fileSizeBytes
- packetIndex
246 .getOffsetBytes()) * 8)) {
247 throw new CTFReaderException("Not enough data remaining in the file for the size of this packet"); //$NON-NLS-1$
251 * Offset in the file, in bits
253 packetIndex
.setDataOffsetBits(pos
);
256 * Update the counting packet offset
258 return computeNextOffset(packetIndex
);
265 private static long computeNextOffset(
266 StreamInputPacketIndexEntry packetIndex
) {
267 return packetIndex
.getOffsetBytes()
268 + ((packetIndex
.getPacketSizeBits() + 7) / 8);
271 private long readPacketHeader(long fileSizeBytes
,
272 long packetOffsetBytes
, StreamInputPacketIndexEntry packetIndex
) throws CTFReaderException
{
275 * Initial size, it should map at least the packet header + context
278 * TODO: use a less arbitrary size.
282 * If there is less data remaining than what we want to map, reduce the
285 if ((fileSizeBytes
- packetIndex
.getOffsetBytes()) < mapSize
) {
286 mapSize
= fileSizeBytes
- packetIndex
.getOffsetBytes();
292 try (FileChannel fc
= FileChannel
.open(fFile
.toPath(), StandardOpenOption
.READ
)) {
293 ByteBuffer map
= SafeMappedByteBuffer
.map(fc
, MapMode
.READ_ONLY
, packetOffsetBytes
, mapSize
);
295 throw new CTFReaderException("Failed to allocate mapped byte buffer"); //$NON-NLS-1$
298 * create a packet bit buffer to read the packet header
300 BitBuffer bitBuffer
= new BitBuffer(map
);
301 bitBuffer
.setByteOrder(getStream().getTrace().getByteOrder());
303 * Read the trace packet header if it exists.
305 if (fTracePacketHeaderDecl
!= null) {
306 parseTracePacketHeader(fTracePacketHeaderDecl
, bitBuffer
);
310 * Read the stream packet context if it exists.
312 if (fStreamPacketContextDecl
!= null) {
313 parsePacketContext(fileSizeBytes
, fStreamPacketContextDecl
,
314 bitBuffer
, packetIndex
);
316 setPacketContextNull(fileSizeBytes
, packetIndex
);
319 position
= bitBuffer
.position();
320 } catch (IOException e
) {
321 throw new CTFReaderException(e
);
326 private void parseTracePacketHeader(StructDeclaration tracePacketHeaderDecl
,
327 @NonNull BitBuffer bitBuffer
) throws CTFReaderException
{
328 StructDefinition tracePacketHeaderDef
= tracePacketHeaderDecl
.createDefinition(fStream
.getTrace(), LexicalScope
.TRACE_PACKET_HEADER
, bitBuffer
);
331 * Check the CTF magic number
333 IntegerDefinition magicDef
= (IntegerDefinition
) tracePacketHeaderDef
334 .lookupDefinition("magic"); //$NON-NLS-1$
335 if (magicDef
!= null) {
336 int magic
= (int) magicDef
.getValue();
337 if (magic
!= Utils
.CTF_MAGIC
) {
338 throw new CTFReaderException(
339 "CTF magic mismatch " + Integer
.toHexString(magic
) + " vs " + Integer
.toHexString(Utils
.CTF_MAGIC
)); //$NON-NLS-1$//$NON-NLS-2$
344 * Check the trace UUID
346 ArrayDefinition uuidDef
=
347 (ArrayDefinition
) tracePacketHeaderDef
.lookupDefinition("uuid"); //$NON-NLS-1$
348 if (uuidDef
!= null) {
349 UUID uuid
= Utils
.getUUIDfromDefinition(uuidDef
);
351 if (!getStream().getTrace().getUUID().equals(uuid
)) {
352 throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$
357 * Check that the stream id did not change
359 IntegerDefinition streamIDDef
= (IntegerDefinition
) tracePacketHeaderDef
360 .lookupDefinition("stream_id"); //$NON-NLS-1$
361 if (streamIDDef
!= null) {
362 long streamID
= streamIDDef
.getValue();
364 if (streamID
!= getStream().getId()) {
365 throw new CTFReaderException("Stream ID changing within a StreamInput"); //$NON-NLS-1$
371 * Gets the wrapped file
380 private static void setPacketContextNull(long fileSizeBytes
,
381 StreamInputPacketIndexEntry packetIndex
) {
383 * If there is no packet context, infer the content and packet size from
384 * the file size (assume that there is only one packet and no padding)
386 packetIndex
.setContentSizeBits(fileSizeBytes
* 8);
387 packetIndex
.setPacketSizeBits(fileSizeBytes
* 8);
390 private void parsePacketContext(long fileSizeBytes
,
391 StructDeclaration streamPacketContextDecl
, @NonNull BitBuffer bitBuffer
,
392 StreamInputPacketIndexEntry packetIndex
) throws CTFReaderException
{
393 StructDefinition streamPacketContextDef
= streamPacketContextDecl
.createDefinition(this, LexicalScope
.STREAM_PACKET_CONTEXT
, bitBuffer
);
395 for (String field
: streamPacketContextDef
.getDeclaration()
397 IDefinition id
= streamPacketContextDef
.lookupDefinition(field
);
398 if (id
instanceof IntegerDefinition
) {
399 packetIndex
.addAttribute(field
,
400 ((IntegerDefinition
) id
).getValue());
401 } else if (id
instanceof FloatDefinition
) {
402 packetIndex
.addAttribute(field
,
403 ((FloatDefinition
) id
).getValue());
404 } else if (id
instanceof EnumDefinition
) {
405 packetIndex
.addAttribute(field
,
406 ((EnumDefinition
) id
).getValue());
407 } else if (id
instanceof StringDefinition
) {
408 packetIndex
.addAttribute(field
,
409 ((StringDefinition
) id
).getValue());
413 Long contentSize
= (Long
) packetIndex
.lookupAttribute("content_size"); //$NON-NLS-1$
414 Long packetSize
= (Long
) packetIndex
.lookupAttribute("packet_size"); //$NON-NLS-1$
415 Long tsBegin
= (Long
) packetIndex
.lookupAttribute("timestamp_begin"); //$NON-NLS-1$
416 Long tsEnd
= (Long
) packetIndex
.lookupAttribute("timestamp_end"); //$NON-NLS-1$
417 String device
= (String
) packetIndex
.lookupAttribute("device"); //$NON-NLS-1$
419 Long cpuId
= (Long
) packetIndex
.lookupAttribute("cpu_id"); //$NON-NLS-1$
420 Long lostEvents
= (Long
) packetIndex
.lookupAttribute("events_discarded"); //$NON-NLS-1$
422 /* Read the content size in bits */
423 if (contentSize
!= null) {
424 packetIndex
.setContentSizeBits(contentSize
.intValue());
425 } else if (packetSize
!= null) {
426 packetIndex
.setContentSizeBits(packetSize
.longValue());
428 packetIndex
.setContentSizeBits((int) (fileSizeBytes
* 8));
431 /* Read the packet size in bits */
432 if (packetSize
!= null) {
433 packetIndex
.setPacketSizeBits(packetSize
.intValue());
434 } else if (packetIndex
.getContentSizeBits() != 0) {
435 packetIndex
.setPacketSizeBits(packetIndex
.getContentSizeBits());
437 packetIndex
.setPacketSizeBits((int) (fileSizeBytes
* 8));
440 /* Read the begin timestamp */
441 if (tsBegin
!= null) {
442 packetIndex
.setTimestampBegin(tsBegin
.longValue());
445 /* Read the end timestamp */
448 tsEnd
= Long
.MAX_VALUE
;
450 packetIndex
.setTimestampEnd(tsEnd
.longValue());
451 setTimestampEnd(packetIndex
.getTimestampEnd());
454 if (device
!= null) {
455 packetIndex
.setTarget(device
);
459 packetIndex
.setTarget("CPU" + cpuId
.toString()); //$NON-NLS-1$
462 if (lostEvents
!= null) {
463 packetIndex
.setLostEvents(lostEvents
- fLostSoFar
);
464 fLostSoFar
= lostEvents
;
469 public int hashCode() {
470 final int prime
= 31;
472 result
= (prime
* result
) + fFile
.hashCode();
477 public boolean equals(Object obj
) {
484 if (!(obj
instanceof CTFStreamInput
)) {
487 CTFStreamInput other
= (CTFStreamInput
) obj
;
488 if (!fFile
.equals(other
.fFile
)) {