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