babc8ad04d942ce383ea32a0eb04f91ea5250ff3
[deliverable/tracecompass.git] / ctf / org.eclipse.tracecompass.ctf.core / src / org / eclipse / tracecompass / ctf / core / trace / CTFStreamInput.java
1 /*******************************************************************************
2 * Copyright (c) 2011, 2014 Ericsson, Ecole Polytechnique de Montreal and others
3 *
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
8 *
9 * Contributors: Matthew Khouzam - Initial API and implementation
10 * Contributors: Simon Marchi - Initial API and implementation
11 *******************************************************************************/
12
13 package org.eclipse.tracecompass.ctf.core.trace;
14
15 import java.io.File;
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;
22
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;
39
40 /**
41 * <b><u>StreamInput</u></b>
42 * <p>
43 * Represents a trace file that belongs to a certain stream.
44 */
45 @NonNullByDefault
46 public class CTFStreamInput implements IDefinitionScope {
47
48 // ------------------------------------------------------------------------
49 // Attributes
50 // ------------------------------------------------------------------------
51
52 private static final int MAP_SIZE = 4096;
53
54 /**
55 * The associated Stream
56 */
57 private final ICTFStream fStream;
58
59 /**
60 * Information on the file (used for debugging)
61 */
62 private final File fFile;
63
64 /**
65 * The file name
66 */
67 private final String fFileName;
68
69 /**
70 * The packet index of this input
71 */
72 private final StreamInputPacketIndex fIndex;
73
74 private long fTimestampEnd;
75
76 /**
77 * Definition of trace packet header
78 */
79 private final StructDeclaration fTracePacketHeaderDecl;
80
81 /**
82 * Definition of trace stream packet context
83 */
84 private final StructDeclaration fStreamPacketContextDecl;
85
86 /**
87 * Total number of lost events in this stream
88 */
89 private long fLostSoFar = 0;
90
91 // ------------------------------------------------------------------------
92 // Constructors
93 // ------------------------------------------------------------------------
94
95 /**
96 * Constructs a StreamInput.
97 *
98 * @param stream
99 * The stream to which this StreamInput belongs to.
100 * @param file
101 * Information about the trace file (for debugging purposes).
102 * @since 2.0
103 */
104 public CTFStreamInput(ICTFStream stream, File file) {
105 fStream = stream;
106 fFile = file;
107 String name = fFile.getName();
108 if (name == null) {
109 throw new IllegalStateException("File cannot have a null name"); //$NON-NLS-1$
110 }
111 fFileName = name;
112
113 fIndex = new StreamInputPacketIndex();
114 /*
115 * Create the definitions we need to read the packet headers + contexts
116 */
117 StructDeclaration packetHeader = getStream().getTrace().getPacketHeader();
118 if (packetHeader != null) {
119 fTracePacketHeaderDecl = packetHeader;
120 } else {
121 fTracePacketHeaderDecl = new StructDeclaration(1);
122 }
123 StructDeclaration packetContextDecl = getStream().getPacketContextDecl();
124 if (packetContextDecl != null) {
125 fStreamPacketContextDecl = packetContextDecl;
126 } else {
127 fStreamPacketContextDecl = new StructDeclaration(1);
128 }
129 }
130
131 // ------------------------------------------------------------------------
132 // Getters/Setters/Predicates
133 // ------------------------------------------------------------------------
134
135 /**
136 * Gets the stream the streamInput wrapper is wrapping
137 *
138 * @return the stream the streamInput wrapper is wrapping
139 * @since 2.0
140 */
141 public ICTFStream getStream() {
142 return fStream;
143 }
144
145 /**
146 * The common streamInput Index
147 *
148 * @return the stream input Index
149 */
150 StreamInputPacketIndex getIndex() {
151 return fIndex;
152 }
153
154 /**
155 * Gets the filename of the streamInput file.
156 *
157 * @return the filename of the streaminput file.
158 */
159 public String getFilename() {
160 return fFileName;
161 }
162
163 /**
164 * Gets the last read timestamp of a stream. (this is not necessarily the
165 * last time in the stream.)
166 *
167 * @return the last read timestamp
168 */
169 public long getTimestampEnd() {
170 return fTimestampEnd;
171 }
172
173 /**
174 * Sets the last read timestamp of a stream. (this is not necessarily the
175 * last time in the stream.)
176 *
177 * @param timestampEnd
178 * the last read timestamp
179 */
180 public void setTimestampEnd(long timestampEnd) {
181 fTimestampEnd = timestampEnd;
182 }
183
184 /**
185 * Useless for streaminputs
186 */
187 @Override
188 public LexicalScope getScopePath() {
189 return ILexicalScope.STREAM;
190 }
191
192 // ------------------------------------------------------------------------
193 // Operations
194 // ------------------------------------------------------------------------
195
196 @Override
197 public @Nullable Definition lookupDefinition(@Nullable String lookupPath) {
198 /* TODO: lookup in different dynamic scopes is not supported yet. */
199 return null;
200 }
201
202 /**
203 * Create the index for this trace file.
204 */
205 public void setupIndex() {
206
207 /*
208 * The BitBuffer to extract data from the StreamInput
209 */
210 BitBuffer bitBuffer = new BitBuffer();
211 bitBuffer.setByteOrder(getStream().getTrace().getByteOrder());
212
213 }
214
215 /**
216 * Adds the next packet header index entry to the index of a stream input.
217 *
218 * <strong>This method is slow and can corrupt data if not used
219 * properly</strong>
220 *
221 * @return true if there are more packets to add
222 * @throws CTFException
223 * If there was a problem reading the packed header
224 */
225 public boolean addPacketHeaderIndex() throws CTFException {
226 long currentPosBits = 0L;
227 if (!fIndex.isEmpty()) {
228 ICTFPacketDescriptor pos = fIndex.lastElement();
229 if (pos == null) {
230 throw new IllegalStateException("Index contains null packet entries"); //$NON-NLS-1$
231 }
232 currentPosBits = pos.getOffsetBits() + pos.getPacketSizeBits();
233 }
234 if (currentPosBits < getStreamSizeBits()) {
235 fIndex.append(createPacketIndexEntry(currentPosBits));
236 return true;
237 }
238 return false;
239 }
240
241 private long getStreamSizeBits() {
242 return fFile.length() * Byte.SIZE;
243 }
244
245 private ICTFPacketDescriptor createPacketIndexEntry(long dataOffsetbits)
246 throws CTFException {
247
248 try (FileChannel fc = FileChannel.open(fFile.toPath(), StandardOpenOption.READ)) {
249 if (fc == null) {
250 throw new IOException("Failed to create FileChannel"); //$NON-NLS-1$
251 }
252 BitBuffer bitBuffer = createBitBufferForPacketHeader(fc, dataOffsetbits);
253 /*
254 * Read the trace packet header if it exists.
255 */
256 parseTracePacketHeader(bitBuffer);
257
258 /*
259 * Read the stream packet context if it exists.
260 */
261 long size = fc.size();
262 ICTFPacketDescriptor packetIndex = parsePacketContext(dataOffsetbits, size, bitBuffer);
263
264 /* Basic validation */
265 if (packetIndex.getContentSizeBits() > packetIndex.getPacketSizeBits()) {
266 throw new CTFException("Content size > packet size"); //$NON-NLS-1$
267 }
268
269 if (packetIndex.getPacketSizeBits() > ((size * Byte.SIZE - packetIndex.getOffsetBits()))) {
270 throw new CTFException("Not enough data remaining in the file for the size of this packet"); //$NON-NLS-1$
271 }
272 return packetIndex;
273 } catch (IOException e) {
274 throw new CTFException("Failed to create packet index entry", e); //$NON-NLS-1$
275 }
276 }
277
278 private BitBuffer createBitBufferForPacketHeader(FileChannel fc, long dataOffsetbits) throws CTFException, IOException {
279 /*
280 * create a packet bit buffer to read the packet header
281 */
282 int maximumSize = fStreamPacketContextDecl.getMaximumSize() + fTracePacketHeaderDecl.getMaximumSize();
283 BitBuffer bitBuffer = new BitBuffer(createPacketBitBuffer(fc, dataOffsetbits/Byte.SIZE, maximumSize));
284 bitBuffer.setByteOrder(getStream().getTrace().getByteOrder());
285 return bitBuffer;
286 }
287
288 private static ByteBuffer getByteBufferAt(FileChannel fc, long position, long size) throws CTFException, IOException {
289 ByteBuffer map = SafeMappedByteBuffer.map(fc, MapMode.READ_ONLY, position, size);
290 if (map == null) {
291 throw new CTFException("Failed to allocate mapped byte buffer"); //$NON-NLS-1$
292 }
293 return map;
294 }
295
296 private static ByteBuffer createPacketBitBuffer(FileChannel fc,
297 long packetOffsetBytes, long maxSize) throws CTFException, IOException {
298 /*
299 * If there is less data remaining than what we want to map, reduce the
300 * map size.
301 */
302 long remain = fc.size() - packetOffsetBytes;
303 /*
304 * Initial size, it is the minimum of the the file size and the maximum
305 * possible size of the
306 */
307 long mapSize = Math.min(remain, MAP_SIZE);
308 if (maxSize < mapSize) {
309 mapSize = maxSize;
310 }
311
312 /*
313 * Map the packet.
314 */
315 try {
316 return getByteBufferAt(fc, packetOffsetBytes, mapSize);
317 } catch (IllegalArgumentException | IOException e) {
318 throw new CTFException(e);
319 }
320 }
321
322 private StructDefinition parseTracePacketHeader(
323 BitBuffer bitBuffer) throws CTFException {
324
325 StructDefinition tracePacketHeaderDef = fTracePacketHeaderDecl.createDefinition(fStream.getTrace(), ILexicalScope.TRACE_PACKET_HEADER, bitBuffer);
326
327 /*
328 * Check the CTF magic number
329 */
330 IntegerDefinition magicDef = (IntegerDefinition) tracePacketHeaderDef
331 .lookupDefinition("magic"); //$NON-NLS-1$
332 if (magicDef != null) {
333 int magic = (int) magicDef.getValue();
334 if (magic != Utils.CTF_MAGIC) {
335 throw new CTFException(
336 "CTF magic mismatch " + Integer.toHexString(magic) + " vs " + Integer.toHexString(Utils.CTF_MAGIC)); //$NON-NLS-1$//$NON-NLS-2$
337 }
338 }
339
340 /*
341 * Check the trace UUID
342 */
343 AbstractArrayDefinition uuidDef =
344 (AbstractArrayDefinition) tracePacketHeaderDef.lookupDefinition("uuid"); //$NON-NLS-1$
345 if (uuidDef != null) {
346 UUID uuid = Utils.getUUIDfromDefinition(uuidDef);
347
348 if (!getStream().getTrace().getUUID().equals(uuid)) {
349 throw new CTFException("UUID mismatch"); //$NON-NLS-1$
350 }
351 }
352
353 /*
354 * Check that the stream id did not change
355 */
356 IntegerDefinition streamIDDef = (IntegerDefinition) tracePacketHeaderDef
357 .lookupDefinition("stream_id"); //$NON-NLS-1$
358 if (streamIDDef != null) {
359 long streamID = streamIDDef.getValue();
360
361 if (streamID != getStream().getId()) {
362 throw new CTFException("Stream ID changing within a StreamInput"); //$NON-NLS-1$
363 }
364 }
365 return tracePacketHeaderDef;
366 }
367
368 private ICTFPacketDescriptor parsePacketContext(long dataOffsetBits, long fileSizeBytes,
369 BitBuffer bitBuffer) throws CTFException {
370 ICTFPacketDescriptor packetIndex;
371 StructDefinition streamPacketContextDef = fStreamPacketContextDecl.createDefinition(this, ILexicalScope.STREAM_PACKET_CONTEXT, bitBuffer);
372 packetIndex = new StreamInputPacketIndexEntry(dataOffsetBits, streamPacketContextDef, fileSizeBytes, fLostSoFar, bitBuffer.position());
373 fLostSoFar = packetIndex.getLostEvents() + fLostSoFar;
374 setTimestampEnd(packetIndex.getTimestampEnd());
375 return packetIndex;
376 }
377
378 /**
379 * Get the file
380 *
381 * @return the file
382 * @since 1.0
383 */
384 public File getFile() {
385 return fFile;
386 }
387
388 @Override
389 public int hashCode() {
390 final int prime = 31;
391 int result = 1;
392 result = (prime * result) + fFile.hashCode();
393 return result;
394 }
395
396 @Override
397 public boolean equals(@Nullable Object obj) {
398 if (this == obj) {
399 return true;
400 }
401 if (obj == null) {
402 return false;
403 }
404 if (!(obj instanceof CTFStreamInput)) {
405 return false;
406 }
407 CTFStreamInput other = (CTFStreamInput) obj;
408 if (!fFile.equals(other.fFile)) {
409 return false;
410 }
411 return true;
412 }
413 }
This page took 0.040191 seconds and 4 git commands to generate.