1 /*******************************************************************************
2 * Copyright (c) 2011, 2013 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
.MappedByteBuffer
;
18 import java
.nio
.channels
.FileChannel
;
19 import java
.nio
.channels
.FileChannel
.MapMode
;
20 import java
.util
.UUID
;
22 import org
.eclipse
.linuxtools
.ctf
.core
.event
.io
.BitBuffer
;
23 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.ArrayDefinition
;
24 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.Definition
;
25 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.EnumDefinition
;
26 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.FloatDefinition
;
27 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.IDefinitionScope
;
28 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.IntegerDefinition
;
29 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StringDefinition
;
30 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StructDefinition
;
31 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.trace
.StreamInputPacketIndex
;
32 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.trace
.StreamInputPacketIndexEntry
;
35 * <b><u>StreamInput</u></b>
37 * Represents a trace file that belongs to a certain stream.
41 public class StreamInput
implements IDefinitionScope
{
43 // ------------------------------------------------------------------------
45 // ------------------------------------------------------------------------
48 * The associated Stream
50 private final Stream stream
;
53 * FileChannel to the trace file
55 private final FileChannel fileChannel
;
58 * Information on the file (used for debugging)
60 private final File file
;
63 * The packet index of this input
65 private final StreamInputPacketIndex index
;
67 private long timestampEnd
;
70 * Definition of trace packet header
72 private StructDefinition tracePacketHeaderDef
= null;
75 * Definition of trace stream packet context
77 private StructDefinition streamPacketContextDef
= null;
80 * Total number of lost events in this stream
82 private long lostSoFar
= 0;
84 // ------------------------------------------------------------------------
86 // ------------------------------------------------------------------------
89 * Constructs a StreamInput.
92 * The stream to which this StreamInput belongs to.
94 * The FileChannel to the trace file.
96 * Information about the trace file (for debugging purposes).
98 public StreamInput(Stream stream
, FileChannel fileChannel
, File file
) {
100 this.fileChannel
= fileChannel
;
102 this.index
= stream
.getTrace().getIndex(this);
105 // ------------------------------------------------------------------------
106 // Getters/Setters/Predicates
107 // ------------------------------------------------------------------------
110 * Gets the stream the streamInput wrapper is wrapping
112 * @return the stream the streamInput wrapper is wrapping
114 public Stream
getStream() {
119 * The common streamInput Index
121 * @return the stream input Index
123 StreamInputPacketIndex
getIndex() {
128 * Gets the filechannel of the streamInput. This is a limited Java resource.
130 * @return the filechannel
132 public FileChannel
getFileChannel() {
137 * Gets the filename of the streamInput file.
139 * @return the filename of the streaminput file.
141 public String
getFilename() {
142 return file
.getName();
146 * Gets the last read timestamp of a stream. (this is not necessarily the
147 * last time in the stream.)
149 * @return the last read timestamp
151 public long getTimestampEnd() {
156 * Sets the last read timestamp of a stream. (this is not necessarily the
157 * last time in the stream.)
159 * @param timestampEnd
160 * the last read timestamp
162 public void setTimestampEnd(long timestampEnd
) {
163 this.timestampEnd
= timestampEnd
;
167 * Useless for streaminputs
170 public String
getPath() {
171 return ""; //$NON-NLS-1$
174 // ------------------------------------------------------------------------
176 // ------------------------------------------------------------------------
179 public Definition
lookupDefinition(String lookupPath
) {
180 /* TODO: lookup in different dynamic scopes is not supported yet. */
185 * Create the index for this trace file.
187 public void setupIndex() {
190 * The BitBuffer to extract data from the StreamInput
192 BitBuffer bitBuffer
= new BitBuffer();
193 bitBuffer
.setByteOrder(this.getStream().getTrace().getByteOrder());
196 * Create the definitions we need to read the packet headers + contexts
198 if (getStream().getTrace().getPacketHeader() != null) {
199 tracePacketHeaderDef
= getStream().getTrace().getPacketHeader()
200 .createDefinition(this, "trace.packet.header"); //$NON-NLS-1$
203 if (getStream().getPacketContextDecl() != null) {
204 streamPacketContextDef
= getStream().getPacketContextDecl()
205 .createDefinition(this, "stream.packet.context"); //$NON-NLS-1$
211 * Adds the next packet header index entry to the index of a stream input.
213 * @warning slow, can corrupt data if not used properly
214 * @return true if there are more packets to add
215 * @throws CTFReaderException
216 * If there was a problem reading the packed header
218 public boolean addPacketHeaderIndex() throws CTFReaderException
{
219 long currentPos
= 0L;
220 if (!index
.getEntries().isEmpty()) {
221 StreamInputPacketIndexEntry pos
= index
.getEntries().lastElement();
222 currentPos
= computeNextOffset(pos
);
224 long fileSize
= getStreamSize();
225 if (currentPos
< fileSize
) {
226 BitBuffer bitBuffer
= new BitBuffer();
227 bitBuffer
.setByteOrder(this.getStream().getTrace().getByteOrder());
228 StreamInputPacketIndexEntry packetIndex
= new StreamInputPacketIndexEntry(
230 createPacketIndexEntry(fileSize
, currentPos
, packetIndex
,
231 tracePacketHeaderDef
, streamPacketContextDef
, bitBuffer
);
232 index
.addEntry(packetIndex
);
238 private long getStreamSize() {
239 return file
.length();
242 private long createPacketIndexEntry(long fileSizeBytes
,
243 long packetOffsetBytes
, StreamInputPacketIndexEntry packetIndex
,
244 StructDefinition tracePacketHeaderDef
,
245 StructDefinition streamPacketContextDef
, BitBuffer bitBuffer
)
246 throws CTFReaderException
{
249 * Ignoring the return value, but this call is needed to initialize the
252 createPacketBitBuffer(fileSizeBytes
, packetOffsetBytes
, packetIndex
, bitBuffer
);
255 * Read the trace packet header if it exists.
257 if (tracePacketHeaderDef
!= null) {
258 parseTracePacketHeader(tracePacketHeaderDef
, bitBuffer
);
262 * Read the stream packet context if it exists.
264 if (streamPacketContextDef
!= null) {
265 parsePacketContext(fileSizeBytes
, streamPacketContextDef
,
266 bitBuffer
, packetIndex
);
268 setPacketContextNull(fileSizeBytes
, packetIndex
);
271 /* Basic validation */
272 if (packetIndex
.getContentSizeBits() > packetIndex
.getPacketSizeBits()) {
273 throw new CTFReaderException("Content size > packet size"); //$NON-NLS-1$
276 if (packetIndex
.getPacketSizeBits() > ((fileSizeBytes
- packetIndex
277 .getOffsetBytes()) * 8)) {
278 throw new CTFReaderException("Not enough data remaining in the file for the size of this packet"); //$NON-NLS-1$
282 * Offset in the file, in bits
284 packetIndex
.setDataOffsetBits(bitBuffer
.position());
287 * Update the counting packet offset
289 return computeNextOffset(packetIndex
);
296 private static long computeNextOffset(
297 StreamInputPacketIndexEntry packetIndex
) {
298 return packetIndex
.getOffsetBytes()
299 + ((packetIndex
.getPacketSizeBits() + 7) / 8);
303 * @param fileSizeBytes
304 * @param packetOffsetBytes
308 * @throws CTFReaderException
310 private MappedByteBuffer
createPacketBitBuffer(long fileSizeBytes
,
311 long packetOffsetBytes
, StreamInputPacketIndexEntry packetIndex
,
312 BitBuffer bitBuffer
) throws CTFReaderException
{
314 * Initial size, it should map at least the packet header + context
317 * TODO: use a less arbitrary size.
321 * If there is less data remaining than what we want to map, reduce the
324 if ((fileSizeBytes
- packetIndex
.getOffsetBytes()) < mapSize
) {
325 mapSize
= fileSizeBytes
- packetIndex
.getOffsetBytes();
334 bb
= fileChannel
.map(MapMode
.READ_ONLY
, packetOffsetBytes
, mapSize
);
335 } catch (IOException e
) {
336 throw new CTFReaderException(e
);
338 bitBuffer
.setByteBuffer(bb
);
342 private void parseTracePacketHeader(StructDefinition tracePacketHeaderDef
,
343 BitBuffer bitBuffer
) throws CTFReaderException
{
344 tracePacketHeaderDef
.read(bitBuffer
);
347 * Check the CTF magic number
349 IntegerDefinition magicDef
= (IntegerDefinition
) tracePacketHeaderDef
350 .lookupDefinition("magic"); //$NON-NLS-1$
351 if (magicDef
!= null) {
352 int magic
= (int) magicDef
.getValue();
353 if (magic
!= Utils
.CTF_MAGIC
) {
354 throw new CTFReaderException(
355 "CTF magic mismatch " + Integer
.toHexString(magic
) + " vs " + Integer
.toHexString(Utils
.CTF_MAGIC
)); //$NON-NLS-1$//$NON-NLS-2$
360 * Check the trace UUID
362 ArrayDefinition uuidDef
=
363 (ArrayDefinition
) tracePacketHeaderDef
.lookupDefinition("uuid"); //$NON-NLS-1$
364 if (uuidDef
!= null) {
365 byte[] uuidArray
= new byte[16];
367 for (int i
= 0; i
< uuidArray
.length
; i
++) {
368 IntegerDefinition uuidByteDef
= (IntegerDefinition
) uuidDef
.getElem(i
);
369 uuidArray
[i
] = (byte) uuidByteDef
.getValue();
372 UUID uuid
= Utils
.makeUUID(uuidArray
);
374 if (!getStream().getTrace().getUUID().equals(uuid
)) {
375 throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$
380 * Check that the stream id did not change
382 IntegerDefinition streamIDDef
= (IntegerDefinition
) tracePacketHeaderDef
383 .lookupDefinition("stream_id"); //$NON-NLS-1$
384 if (streamIDDef
!= null) {
385 long streamID
= streamIDDef
.getValue();
387 if (streamID
!= getStream().getId()) {
388 throw new CTFReaderException("Stream ID changing within a StreamInput"); //$NON-NLS-1$
393 private static void setPacketContextNull(long fileSizeBytes
,
394 StreamInputPacketIndexEntry packetIndex
) {
396 * If there is no packet context, infer the content and packet size from
397 * the file size (assume that there is only one packet and no padding)
399 packetIndex
.setContentSizeBits(fileSizeBytes
* 8);
400 packetIndex
.setPacketSizeBits(fileSizeBytes
* 8);
403 private void parsePacketContext(long fileSizeBytes
,
404 StructDefinition streamPacketContextDef
, BitBuffer bitBuffer
,
405 StreamInputPacketIndexEntry packetIndex
) throws CTFReaderException
{
406 streamPacketContextDef
.read(bitBuffer
);
408 for (String field
: streamPacketContextDef
.getDeclaration()
410 Definition id
= streamPacketContextDef
.lookupDefinition(field
);
411 if (id
instanceof IntegerDefinition
) {
412 packetIndex
.addAttribute(field
,
413 ((IntegerDefinition
) id
).getValue());
414 } else if (id
instanceof FloatDefinition
) {
415 packetIndex
.addAttribute(field
,
416 ((FloatDefinition
) id
).getValue());
417 } else if (id
instanceof EnumDefinition
) {
418 packetIndex
.addAttribute(field
,
419 ((EnumDefinition
) id
).getValue());
420 } else if (id
instanceof StringDefinition
) {
421 packetIndex
.addAttribute(field
,
422 ((StringDefinition
) id
).getValue());
426 Long contentSize
= (Long
) packetIndex
.lookupAttribute("content_size"); //$NON-NLS-1$
427 Long packetSize
= (Long
) packetIndex
.lookupAttribute("packet_size"); //$NON-NLS-1$
428 Long tsBegin
= (Long
) packetIndex
.lookupAttribute("timestamp_begin"); //$NON-NLS-1$
429 Long tsEnd
= (Long
) packetIndex
.lookupAttribute("timestamp_end"); //$NON-NLS-1$
430 String device
= (String
) packetIndex
.lookupAttribute("device"); //$NON-NLS-1$
432 Long cpuId
= (Long
) packetIndex
.lookupAttribute("cpu_id"); //$NON-NLS-1$
433 Long lostEvents
= (Long
) packetIndex
.lookupAttribute("events_discarded"); //$NON-NLS-1$
435 /* Read the content size in bits */
436 if (contentSize
!= null) {
437 packetIndex
.setContentSizeBits(contentSize
.intValue());
438 } else if (packetSize
!= null) {
439 packetIndex
.setContentSizeBits(packetSize
.longValue());
441 packetIndex
.setContentSizeBits((int) (fileSizeBytes
* 8));
445 /* Read the packet size in bits */
446 if (packetSize
!= null) {
447 packetIndex
.setPacketSizeBits(packetSize
.intValue());
448 } else if (packetIndex
.getContentSizeBits() != 0) {
449 packetIndex
.setPacketSizeBits(packetIndex
.getContentSizeBits());
451 packetIndex
.setPacketSizeBits((int) (fileSizeBytes
* 8));
455 /* Read the begin timestamp */
456 if (tsBegin
!= null) {
457 packetIndex
.setTimestampBegin(tsBegin
.longValue());
460 /* Read the end timestamp */
463 tsEnd
= Long
.MAX_VALUE
;
465 packetIndex
.setTimestampEnd(tsEnd
.longValue());
466 setTimestampEnd(packetIndex
.getTimestampEnd());
469 if (device
!= null) {
470 packetIndex
.setTarget(device
);
474 packetIndex
.setTarget("CPU" + cpuId
.toString()); //$NON-NLS-1$
477 if (lostEvents
!= null) {
478 packetIndex
.setLostEvents(lostEvents
- lostSoFar
);
479 this.lostSoFar
= lostEvents
;
484 public int hashCode() {
485 final int prime
= 31;
487 result
= (prime
* result
) + ((file
== null) ?
0 : file
.hashCode());
492 public boolean equals(Object obj
) {
499 if (!(obj
instanceof StreamInput
)) {
502 StreamInput other
= (StreamInput
) obj
;
504 if (other
.file
!= null) {
507 } else if (!file
.equals(other
.file
)) {