1 /*******************************************************************************
2 * Copyright (c) 2011-2012 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
.internal
.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
.types
.ArrayDefinition
;
23 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.Definition
;
24 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.EnumDefinition
;
25 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.FloatDefinition
;
26 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.IDefinitionScope
;
27 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.IntegerDefinition
;
28 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StringDefinition
;
29 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StructDefinition
;
30 import org
.eclipse
.linuxtools
.ctf
.core
.trace
.CTFReaderException
;
31 import org
.eclipse
.linuxtools
.ctf
.core
.trace
.Utils
;
32 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.event
.io
.BitBuffer
;
35 * <b><u>StreamInput</u></b>
37 * Represents a trace file that belongs to a certain stream.
39 public class StreamInput
implements IDefinitionScope
{
41 // ------------------------------------------------------------------------
43 // ------------------------------------------------------------------------
46 * The associated Stream
48 private final Stream stream
;
51 * FileChannel to the trace file
53 private final FileChannel fileChannel
;
56 * Information on the file (used for debugging)
58 public final File file
;
61 * The packet index of this input
63 private final StreamInputPacketIndex index
;
65 private long timestampEnd
;
68 * Definition of trace packet header
70 StructDefinition tracePacketHeaderDef
= null;
73 * Definition of trace stream packet context
75 StructDefinition streamPacketContextDef
= null;
77 // ------------------------------------------------------------------------
79 // ------------------------------------------------------------------------
82 * Constructs a StreamInput.
85 * The stream to which this StreamInput belongs to.
87 * The FileChannel to the trace file.
89 * Information about the trace file (for debugging purposes).
91 public StreamInput(Stream stream
, FileChannel fileChannel
, File file
) {
93 this.fileChannel
= fileChannel
;
95 index
= stream
.getTrace().getIndex(this);
98 // ------------------------------------------------------------------------
99 // Getters/Setters/Predicates
100 // ------------------------------------------------------------------------
103 * Gets the stream the streamInput wrapper is wrapping
105 * @return the stream the streamInput wrapper is wrapping
107 public Stream
getStream() {
112 * the common streamInput Index
114 * @return the stream input Index
116 public StreamInputPacketIndex
getIndex() {
121 * Gets the filechannel of the streamInput. This is a limited Java
124 * @return the filechannel
126 public FileChannel
getFileChannel() {
131 * Gets the filename of the streamInput file.
133 * @return the filename of the streaminput file.
135 public String
getFilename() {
136 return file
.getName();
140 * gets the last read timestamp of a stream. (this is not necessarily the
141 * last time in the stream.)
143 * @return the last read timestamp
145 public long getTimestampEnd() {
150 * Sets the last read timestamp of a stream. (this is not necessarily the
151 * last time in the stream.)
153 * @param timestampEnd
154 * the last read timestamp
156 public void setTimestampEnd(long timestampEnd
) {
157 this.timestampEnd
= timestampEnd
;
161 * useless for streaminputs
164 public String
getPath() {
165 return ""; //$NON-NLS-1$
168 // ------------------------------------------------------------------------
170 // ------------------------------------------------------------------------
173 public Definition
lookupDefinition(String lookupPath
) {
174 /* TODO: lookup in different dynamic scopes is not supported yet. */
179 * Create the index for this trace file.
181 public void setupIndex() {
184 * The BitBuffer to extract data from the StreamInput
186 BitBuffer bitBuffer
= new BitBuffer();
187 bitBuffer
.order(this.getStream().getTrace().getByteOrder());
190 * Create the definitions we need to read the packet headers + contexts
192 if (getStream().getTrace().getPacketHeader() != null) {
193 tracePacketHeaderDef
= getStream().getTrace().getPacketHeader()
194 .createDefinition(this, "trace.packet.header"); //$NON-NLS-1$
197 if (getStream().getPacketContextDecl() != null) {
198 streamPacketContextDef
= getStream().getPacketContextDecl()
199 .createDefinition(this, "stream.packet.context"); //$NON-NLS-1$
205 * Adds the next packet header index entry to the index of a stream input.
207 * @warning slow, can corrupt data if not used properly
208 * @return true if there are more packets to add
209 * @throws CTFReaderException
210 * If there was a problem reading the packed header
212 public boolean addPacketHeaderIndex() throws CTFReaderException
{
213 long currentPos
= 0L;
214 if (!index
.getEntries().isEmpty()) {
215 StreamInputPacketIndexEntry pos
= index
.getEntries().lastElement();
216 currentPos
= computeNextOffset(pos
);
218 long fileSize
= getStreamSize();
219 if (currentPos
< fileSize
) {
220 BitBuffer bitBuffer
= new BitBuffer();
221 bitBuffer
.order(this.getStream().getTrace().getByteOrder());
222 StreamInputPacketIndexEntry packetIndex
= new StreamInputPacketIndexEntry(
224 createPacketIndexEntry(fileSize
, currentPos
, packetIndex
,
225 tracePacketHeaderDef
, streamPacketContextDef
, bitBuffer
);
226 index
.addEntry(packetIndex
);
232 private long getStreamSize() {
233 return file
.length();
236 private long createPacketIndexEntry(long fileSizeBytes
,
237 long packetOffsetBytes
, StreamInputPacketIndexEntry packetIndex
,
238 StructDefinition tracePacketHeaderDef
,
239 StructDefinition streamPacketContextDef
, BitBuffer bitBuffer
)
240 throws CTFReaderException
{
241 @SuppressWarnings("unused")
242 MappedByteBuffer bb
= createPacketBitBuffer(fileSizeBytes
,
243 packetOffsetBytes
, packetIndex
, bitBuffer
);
246 * Read the trace packet header if it exists.
248 if (tracePacketHeaderDef
!= null) {
249 parseTracePacketHeader(tracePacketHeaderDef
, bitBuffer
);
253 * Read the stream packet context if it exists.
255 if (streamPacketContextDef
!= null) {
256 parsePacketContext(fileSizeBytes
, streamPacketContextDef
,
257 bitBuffer
, packetIndex
);
259 setPacketContextNull(fileSizeBytes
, packetIndex
);
262 /* Basic validation */
263 if (packetIndex
.getContentSizeBits() > packetIndex
.getPacketSizeBits()) {
264 throw new CTFReaderException("Content size > packet size"); //$NON-NLS-1$
267 if (packetIndex
.getPacketSizeBits() > ((fileSizeBytes
- packetIndex
268 .getOffsetBytes()) * 8)) {
269 throw new CTFReaderException(
270 "Not enough data remaining in the file for the size of this packet"); //$NON-NLS-1$
274 * Offset in the file, in bits
276 packetIndex
.setDataOffsetBits(bitBuffer
.position());
279 * Update the counting packet offset
281 return computeNextOffset(packetIndex
);
288 private static long computeNextOffset(
289 StreamInputPacketIndexEntry packetIndex
) {
290 return packetIndex
.getOffsetBytes()
291 + ((packetIndex
.getPacketSizeBits() + 7) / 8);
295 * @param fileSizeBytes
296 * @param packetOffsetBytes
300 * @throws CTFReaderException
302 private MappedByteBuffer
createPacketBitBuffer(long fileSizeBytes
,
303 long packetOffsetBytes
, StreamInputPacketIndexEntry packetIndex
,
304 BitBuffer bitBuffer
) throws CTFReaderException
{
306 * Initial size, it should map at least the packet header + context
309 * TODO: use a less arbitrary size.
313 * If there is less data remaining than what we want to map, reduce the
316 if ((fileSizeBytes
- packetIndex
.getOffsetBytes()) < mapSize
) {
317 mapSize
= fileSizeBytes
- packetIndex
.getOffsetBytes();
326 bb
= fileChannel
.map(MapMode
.READ_ONLY
, packetOffsetBytes
, mapSize
);
327 } catch (IOException e
) {
328 throw new CTFReaderException(e
);
330 bitBuffer
.setByteBuffer(bb
);
334 private void parseTracePacketHeader(StructDefinition tracePacketHeaderDef
,
335 BitBuffer bitBuffer
) throws CTFReaderException
{
336 tracePacketHeaderDef
.read(bitBuffer
);
339 * Check the CTF magic number
341 IntegerDefinition magicDef
= (IntegerDefinition
) tracePacketHeaderDef
342 .lookupDefinition("magic"); //$NON-NLS-1$
343 if (magicDef
!= null) {
344 int magic
= (int) magicDef
.getValue();
345 if (magic
!= Utils
.CTF_MAGIC
) {
346 throw new CTFReaderException(
347 "CTF magic mismatch " + Integer
.toHexString(magic
) + " vs " + Integer
.toHexString(Utils
.CTF_MAGIC
)); //$NON-NLS-1$//$NON-NLS-2$
353 * Check the trace UUID
355 ArrayDefinition uuidDef
= (ArrayDefinition
) tracePacketHeaderDef
356 .lookupDefinition("uuid"); //$NON-NLS-1$
357 if (uuidDef
!= null) {
358 byte[] uuidArray
= new byte[16];
360 for (int i
= 0; i
< 16; i
++) {
361 IntegerDefinition uuidByteDef
= (IntegerDefinition
) uuidDef
363 uuidArray
[i
] = (byte) uuidByteDef
.getValue();
366 UUID uuid
= Utils
.makeUUID(uuidArray
);
368 if (!getStream().getTrace().getUUID().equals(uuid
)) {
369 throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$
374 * Check that the stream id did not change
376 IntegerDefinition streamIDDef
= (IntegerDefinition
) tracePacketHeaderDef
377 .lookupDefinition("stream_id"); //$NON-NLS-1$
378 if (streamIDDef
!= null) {
379 long streamID
= streamIDDef
.getValue();
381 if (streamID
!= getStream().getId()) {
382 throw new CTFReaderException(
383 "Stream ID changing within a StreamInput"); //$NON-NLS-1$
388 private static void setPacketContextNull(long fileSizeBytes
,
389 StreamInputPacketIndexEntry packetIndex
) {
391 * If there is no packet context, infer the content and packet size from
392 * the file size (assume that there is only one packet and no padding)
394 packetIndex
.setContentSizeBits((int) (fileSizeBytes
* 8));
395 packetIndex
.setPacketSizeBits((int) (fileSizeBytes
* 8));
398 private void parsePacketContext(long fileSizeBytes
,
399 StructDefinition streamPacketContextDef
, BitBuffer bitBuffer
,
400 StreamInputPacketIndexEntry packetIndex
) {
401 streamPacketContextDef
.read(bitBuffer
);
403 for (String field
: streamPacketContextDef
.getDeclaration()
405 Definition id
= streamPacketContextDef
.lookupDefinition(field
);
406 if (id
instanceof IntegerDefinition
) {
407 packetIndex
.addAttribute(field
,
408 ((IntegerDefinition
) id
).getValue());
409 } else if (id
instanceof FloatDefinition
) {
410 packetIndex
.addAttribute(field
,
411 ((FloatDefinition
) id
).getValue());
412 } else if (id
instanceof EnumDefinition
) {
413 packetIndex
.addAttribute(field
,
414 ((EnumDefinition
) id
).getValue());
415 } else if (id
instanceof StringDefinition
) {
416 packetIndex
.addAttribute(field
,
417 ((StringDefinition
) id
).getValue());
422 Long contentSize
= (Long
) packetIndex
.lookupAttribute("content_size"); //$NON-NLS-1$
423 Long packetSize
= (Long
) packetIndex
.lookupAttribute("packet_size"); //$NON-NLS-1$
424 Long timestampBegin
= (Long
) packetIndex
425 .lookupAttribute("timestamp_begin"); //$NON-NLS-1$
426 Long timestampEnd
= (Long
) packetIndex
.lookupAttribute("timestamp_end"); //$NON-NLS-1$
427 String device
= (String
) packetIndex
.lookupAttribute("device"); //$NON-NLS-1$
429 Long CPU_ID
= (Long
) packetIndex
.lookupAttribute("cpu_id"); //$NON-NLS-1$
431 * Read the content size in bits
433 if (contentSize
!= null) {
434 packetIndex
.setContentSizeBits(contentSize
.intValue());
436 packetIndex
.setContentSizeBits((int) (fileSizeBytes
* 8));
440 * Read the packet size in bits
443 if (packetSize
!= null) {
444 packetIndex
.setPacketSizeBits(packetSize
.intValue());
446 if (packetIndex
.getContentSizeBits() != 0) {
447 packetIndex
.setPacketSizeBits(packetIndex
.getContentSizeBits());
449 packetIndex
.setPacketSizeBits((int) (fileSizeBytes
* 8));
454 * Read the begin timestamp
457 if (timestampBegin
!= null) {
458 packetIndex
.setTimestampBegin(timestampBegin
.longValue());
462 * Read the end timestamp
464 if (timestampEnd
!= null) {
465 if( timestampEnd
== -1 ) {
466 timestampEnd
= Long
.MAX_VALUE
;
468 packetIndex
.setTimestampEnd(timestampEnd
.longValue());
469 setTimestampEnd(packetIndex
.getTimestampEnd());
472 if (device
!= null) {
473 packetIndex
.setTarget(device
);
476 if (CPU_ID
!= null) {
477 packetIndex
.setTarget("CPU" + CPU_ID
.toString()); //$NON-NLS-1$
484 * @see java.lang.Object#hashCode()
487 public int hashCode() {
488 final int prime
= 31;
490 result
= (prime
* result
) + ((file
== null) ?
0 : file
.hashCode());
497 * @see java.lang.Object#equals(java.lang.Object)
500 public boolean equals(Object obj
) {
507 if (!(obj
instanceof StreamInput
)) {
510 StreamInput other
= (StreamInput
) obj
;
512 if (other
.file
!= null) {
515 } else if (!file
.equals(other
.file
)) {