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