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
.IDefinitionScope
;
25 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.IntegerDefinition
;
26 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StructDefinition
;
27 import org
.eclipse
.linuxtools
.ctf
.core
.trace
.CTFReaderException
;
28 import org
.eclipse
.linuxtools
.ctf
.core
.trace
.Utils
;
29 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.event
.io
.BitBuffer
;
32 * <b><u>StreamInput</u></b>
34 * Represents a trace file that belongs to a certain stream.
36 public class StreamInput
implements IDefinitionScope
{
38 // ------------------------------------------------------------------------
40 // ------------------------------------------------------------------------
43 * The associated Stream
45 private final Stream stream
;
48 * FileChannel to the trace file
50 private final FileChannel fileChannel
;
53 * Information on the file (used for debugging)
55 public final File file
;
58 * The packet index of this input
60 private final StreamInputPacketIndex index
;
62 private long timestampEnd
;
65 * Definition of trace packet header
67 StructDefinition tracePacketHeaderDef
= null;
70 * Definition of trace stream packet context
72 StructDefinition streamPacketContextDef
= null;
74 // ------------------------------------------------------------------------
76 // ------------------------------------------------------------------------
79 * Constructs a StreamInput.
82 * The stream to which this StreamInput belongs to.
84 * The FileChannel to the trace file.
86 * Information about the trace file (for debugging purposes).
88 public StreamInput(Stream stream
, FileChannel fileChannel
, File file
) {
90 this.fileChannel
= fileChannel
;
92 index
= stream
.getTrace().getIndex(this);
95 // ------------------------------------------------------------------------
96 // Getters/Setters/Predicates
97 // ------------------------------------------------------------------------
99 public Stream
getStream() {
103 public StreamInputPacketIndex
getIndex() {
107 public FileChannel
getFileChannel() {
111 public String
getFilename() {
112 return file
.getName();
115 public long getTimestampEnd() {
119 public void setTimestampEnd(long timestampEnd
) {
120 this.timestampEnd
= timestampEnd
;
124 public String
getPath() {
125 return ""; //$NON-NLS-1$
128 // ------------------------------------------------------------------------
130 // ------------------------------------------------------------------------
133 public Definition
lookupDefinition(String lookupPath
) {
134 /* TODO: lookup in different dynamic scopes is not supported yet. */
139 * Create the index for this trace file.
141 public void setupIndex() {
145 * The BitBuffer to extract data from the StreamInput
147 BitBuffer bitBuffer
= new BitBuffer();
148 bitBuffer
.order(this.getStream().getTrace().getByteOrder());
151 * Create the definitions we need to read the packet headers + contexts
153 if (getStream().getTrace().getPacketHeader() != null) {
154 tracePacketHeaderDef
= getStream().getTrace().getPacketHeader()
155 .createDefinition(this, "trace.packet.header"); //$NON-NLS-1$
158 if (getStream().getPacketContextDecl() != null) {
159 streamPacketContextDef
= getStream().getPacketContextDecl()
160 .createDefinition(this, "stream.packet.context"); //$NON-NLS-1$
165 public boolean addPacketHeaderIndex() throws CTFReaderException
{
166 long currentPos
= 0L;
167 if (!index
.getEntries().isEmpty()) {
168 StreamInputPacketIndexEntry pos
= index
.getEntries().lastElement();
169 currentPos
= computeNextOffset(pos
);
171 long fileSize
= getStreamSize();
172 if (currentPos
< fileSize
) {
173 BitBuffer bitBuffer
= new BitBuffer();
174 bitBuffer
.order(this.getStream().getTrace().getByteOrder());
175 StreamInputPacketIndexEntry packetIndex
= new StreamInputPacketIndexEntry(
177 createPacketIndexEntry(fileSize
, currentPos
, packetIndex
,
178 tracePacketHeaderDef
, streamPacketContextDef
, bitBuffer
);
179 index
.addEntry(packetIndex
);
186 * @param fileSizeBytes
189 private long getStreamSize() {
190 return file
.length();
194 * @param fileSizeBytes
195 * @param packetOffsetBytes
197 * @param tracePacketHeaderDef
198 * @param streamPacketContextDef
201 * @throws CTFReaderException
203 private long createPacketIndexEntry(long fileSizeBytes
,
204 long packetOffsetBytes
, StreamInputPacketIndexEntry packetIndex
,
205 StructDefinition tracePacketHeaderDef
,
206 StructDefinition streamPacketContextDef
, BitBuffer bitBuffer
)
207 throws CTFReaderException
{
208 MappedByteBuffer bb
= createPacketBitBuffer(fileSizeBytes
,
209 packetOffsetBytes
, packetIndex
, bitBuffer
);
212 * Read the trace packet header if it exists.
214 if (tracePacketHeaderDef
!= null) {
215 parseTracePacketHeader(tracePacketHeaderDef
, bitBuffer
);
219 * Read the stream packet context if it exists.
221 if (streamPacketContextDef
!= null) {
222 parsePacketContext(fileSizeBytes
, streamPacketContextDef
,
223 bitBuffer
, packetIndex
);
225 setPacketContextNull(fileSizeBytes
, packetIndex
);
228 /* Basic validation */
229 if (packetIndex
.getContentSizeBits() > packetIndex
.getPacketSizeBits()) {
230 throw new CTFReaderException("Content size > packet size"); //$NON-NLS-1$
233 if (packetIndex
.getPacketSizeBits() > ((fileSizeBytes
- packetIndex
234 .getOffsetBytes()) * 8)) {
235 throw new CTFReaderException(
236 "Not enough data remaining in the file for the size of this packet"); //$NON-NLS-1$
240 * Offset in the file, in bits
242 packetIndex
.setDataOffsetBits(bitBuffer
.position());
245 * Update the counting packet offset
247 packetOffsetBytes
= computeNextOffset(packetIndex
);
248 return packetOffsetBytes
;
255 private static long computeNextOffset(
256 StreamInputPacketIndexEntry packetIndex
) {
257 return packetIndex
.getOffsetBytes()
258 + ((packetIndex
.getPacketSizeBits() + 7) / 8);
262 * @param fileSizeBytes
263 * @param packetOffsetBytes
267 * @throws CTFReaderException
269 private MappedByteBuffer
createPacketBitBuffer(long fileSizeBytes
,
270 long packetOffsetBytes
, StreamInputPacketIndexEntry packetIndex
,
271 BitBuffer bitBuffer
) throws CTFReaderException
{
273 * Initial size, it should map at least the packet header + context
276 * TODO: use a less arbitrary size.
280 * If there is less data remaining than what we want to map, reduce the
283 if ((fileSizeBytes
- packetIndex
.getOffsetBytes()) < mapSize
) {
284 mapSize
= fileSizeBytes
- packetIndex
.getOffsetBytes();
293 bb
= fileChannel
.map(MapMode
.READ_ONLY
, packetOffsetBytes
, mapSize
);
294 } catch (IOException e
) {
295 throw new CTFReaderException(e
);
297 bitBuffer
.setByteBuffer(bb
);
302 * @param tracePacketHeaderDef
304 * @throws CTFReaderException
306 private void parseTracePacketHeader(StructDefinition tracePacketHeaderDef
,
307 BitBuffer bitBuffer
) throws CTFReaderException
{
308 tracePacketHeaderDef
.read(bitBuffer
);
311 * Check the CTF magic number
313 IntegerDefinition magicDef
= (IntegerDefinition
) tracePacketHeaderDef
314 .lookupDefinition("magic"); //$NON-NLS-1$
315 if (magicDef
!= null) {
316 int magic
= (int) magicDef
.getValue();
317 if (magic
!= Utils
.CTF_MAGIC
) {
318 throw new CTFReaderException(
319 "CTF magic mismatch " + Integer
.toHexString(magic
) + " vs " + Integer
.toHexString(Utils
.CTF_MAGIC
)); //$NON-NLS-1$//$NON-NLS-2$
325 * Check the trace UUID
327 ArrayDefinition uuidDef
= (ArrayDefinition
) tracePacketHeaderDef
328 .lookupDefinition("uuid"); //$NON-NLS-1$
329 if (uuidDef
!= null) {
330 byte[] uuidArray
= new byte[16];
332 for (int i
= 0; i
< 16; i
++) {
333 IntegerDefinition uuidByteDef
= (IntegerDefinition
) uuidDef
335 uuidArray
[i
] = (byte) uuidByteDef
.getValue();
338 UUID uuid
= Utils
.makeUUID(uuidArray
);
340 if (!getStream().getTrace().getUUID().equals(uuid
)) {
341 throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$
346 * Check that the stream id did not change
348 IntegerDefinition streamIDDef
= (IntegerDefinition
) tracePacketHeaderDef
349 .lookupDefinition("stream_id"); //$NON-NLS-1$
350 if (streamIDDef
!= null) {
351 long streamID
= streamIDDef
.getValue();
353 if (streamID
!= getStream().getId()) {
354 throw new CTFReaderException(
355 "Stream ID changing within a StreamInput"); //$NON-NLS-1$
361 * @param fileSizeBytes
364 private static void setPacketContextNull(long fileSizeBytes
,
365 StreamInputPacketIndexEntry packetIndex
) {
367 * If there is no packet context, infer the content and packet size from
368 * the file size (assume that there is only one packet and no padding)
370 packetIndex
.setContentSizeBits((int) (fileSizeBytes
* 8));
371 packetIndex
.setPacketSizeBits((int) (fileSizeBytes
* 8));
375 * @param fileSizeBytes
376 * @param streamPacketContextDef
380 private void parsePacketContext(long fileSizeBytes
,
381 StructDefinition streamPacketContextDef
, BitBuffer bitBuffer
,
382 StreamInputPacketIndexEntry packetIndex
) {
383 streamPacketContextDef
.read(bitBuffer
);
386 * Read the content size in bits
388 IntegerDefinition contentSizeDef
= (IntegerDefinition
) streamPacketContextDef
389 .lookupDefinition("content_size"); //$NON-NLS-1$
390 if (contentSizeDef
!= null) {
391 packetIndex
.setContentSizeBits((int) contentSizeDef
.getValue());
393 packetIndex
.setContentSizeBits((int) (fileSizeBytes
* 8));
397 * Read the packet size in bits
399 IntegerDefinition packetSizeDef
= (IntegerDefinition
) streamPacketContextDef
400 .lookupDefinition("packet_size"); //$NON-NLS-1$
401 if (packetSizeDef
!= null) {
402 packetIndex
.setPacketSizeBits((int) packetSizeDef
.getValue());
404 if (packetIndex
.getContentSizeBits() != 0) {
405 packetIndex
.setPacketSizeBits(packetIndex
.getContentSizeBits());
407 packetIndex
.setPacketSizeBits((int) (fileSizeBytes
* 8));
412 * Read the begin timestamp
414 IntegerDefinition timestampBeginDef
= (IntegerDefinition
) streamPacketContextDef
415 .lookupDefinition("timestamp_begin"); //$NON-NLS-1$
416 if (timestampBeginDef
!= null) {
417 packetIndex
.setTimestampBegin(timestampBeginDef
.getValue());
421 * Read the end timestamp
423 IntegerDefinition timestampEndDef
= (IntegerDefinition
) streamPacketContextDef
424 .lookupDefinition("timestamp_end"); //$NON-NLS-1$
425 if (timestampEndDef
!= null) {
426 packetIndex
.setTimestampEnd(timestampEndDef
.getValue());
427 setTimestampEnd(packetIndex
.getTimestampEnd());
434 * @see java.lang.Object#hashCode()
437 public int hashCode() {
438 final int prime
= 31;
440 result
= (prime
* result
) + ((file
== null) ?
0 : file
.hashCode());
447 * @see java.lang.Object#equals(java.lang.Object)
450 public boolean equals(Object obj
) {
457 if (!(obj
instanceof StreamInput
)) {
460 StreamInput other
= (StreamInput
) obj
;
462 if (other
.file
!= null) {
465 } else if (!file
.equals(other
.file
)) {