af7f964085460cff0fbd36e7ee879cf4d36b001a
[deliverable/tracecompass.git] / org.eclipse.linuxtools.ctf.core / src / org / eclipse / linuxtools / 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.linuxtools.ctf.core.trace;
14
15 import java.io.File;
16 import java.io.FileInputStream;
17 import java.io.FileNotFoundException;
18 import java.io.IOException;
19 import java.nio.ByteBuffer;
20 import java.nio.channels.FileChannel;
21 import java.nio.channels.FileChannel.MapMode;
22 import java.util.UUID;
23
24 import org.eclipse.jdt.annotation.NonNull;
25 import org.eclipse.linuxtools.ctf.core.event.io.BitBuffer;
26 import org.eclipse.linuxtools.ctf.core.event.scope.IDefinitionScope;
27 import org.eclipse.linuxtools.ctf.core.event.scope.LexicalScope;
28 import org.eclipse.linuxtools.ctf.core.event.types.ArrayDefinition;
29 import org.eclipse.linuxtools.ctf.core.event.types.Definition;
30 import org.eclipse.linuxtools.ctf.core.event.types.EnumDefinition;
31 import org.eclipse.linuxtools.ctf.core.event.types.FloatDefinition;
32 import org.eclipse.linuxtools.ctf.core.event.types.IntegerDefinition;
33 import org.eclipse.linuxtools.ctf.core.event.types.StringDefinition;
34 import org.eclipse.linuxtools.ctf.core.event.types.StructDeclaration;
35 import org.eclipse.linuxtools.ctf.core.event.types.StructDefinition;
36 import org.eclipse.linuxtools.internal.ctf.core.trace.StreamInputPacketIndex;
37 import org.eclipse.linuxtools.internal.ctf.core.trace.StreamInputPacketIndexEntry;
38
39 /**
40 * <b><u>StreamInput</u></b>
41 * <p>
42 * Represents a trace file that belongs to a certain stream.
43 *
44 * @since 3.0
45 */
46 public class CTFStreamInput implements IDefinitionScope, AutoCloseable {
47
48 // ------------------------------------------------------------------------
49 // Attributes
50 // ------------------------------------------------------------------------
51
52 /**
53 * The associated Stream
54 */
55 private final CTFStream fStream;
56
57 /**
58 * FileChannel to the trace file
59 */
60 private final FileChannel fFileChannel;
61
62 /**
63 * Information on the file (used for debugging)
64 */
65 private final File fFile;
66
67 /**
68 * The packet index of this input
69 */
70 private final StreamInputPacketIndex fIndex;
71
72 private long fTimestampEnd;
73
74 /**
75 * Definition of trace packet header
76 */
77 private StructDeclaration fTracePacketHeaderDecl = null;
78
79 /**
80 * Definition of trace stream packet context
81 */
82 private StructDeclaration fStreamPacketContextDecl = null;
83
84 /**
85 * Total number of lost events in this stream
86 */
87 private long fLostSoFar = 0;
88
89 /**
90 * File input stream, the parent input file
91 */
92 private final FileInputStream fFileInputStream;
93
94 // ------------------------------------------------------------------------
95 // Constructors
96 // ------------------------------------------------------------------------
97
98 /**
99 * Constructs a StreamInput.
100 *
101 * @param stream
102 * The stream to which this StreamInput belongs to.
103 * @param file
104 * Information about the trace file (for debugging purposes).
105 * @throws CTFReaderException
106 * The file must exist
107 */
108 public CTFStreamInput(CTFStream stream, File file) throws CTFReaderException {
109 fStream = stream;
110 fFile = file;
111 try {
112 fFileInputStream = new FileInputStream(file);
113 } catch (FileNotFoundException e) {
114 throw new CTFReaderException(e);
115 }
116
117 fFileChannel = fFileInputStream.getChannel();
118 fIndex = new StreamInputPacketIndex();
119 }
120
121 @Override
122 public void close() throws IOException {
123 fFileChannel.close();
124 fFileInputStream.close();
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 */
136 public CTFStream getStream() {
137 return fStream;
138 }
139
140 /**
141 * The common streamInput Index
142 *
143 * @return the stream input Index
144 */
145 StreamInputPacketIndex getIndex() {
146 return fIndex;
147 }
148
149 /**
150 * Gets the filename of the streamInput file.
151 *
152 * @return the filename of the streaminput file.
153 */
154 public String getFilename() {
155 return fFile.getName();
156 }
157
158 /**
159 * Gets the last read timestamp of a stream. (this is not necessarily the
160 * last time in the stream.)
161 *
162 * @return the last read timestamp
163 */
164 public long getTimestampEnd() {
165 return fTimestampEnd;
166 }
167
168 /**
169 * Sets the last read timestamp of a stream. (this is not necessarily the
170 * last time in the stream.)
171 *
172 * @param timestampEnd
173 * the last read timestamp
174 */
175 public void setTimestampEnd(long timestampEnd) {
176 fTimestampEnd = timestampEnd;
177 }
178
179 /**
180 * Useless for streaminputs
181 */
182 @Override
183 public LexicalScope getScopePath() {
184 return LexicalScope.STREAM;
185 }
186
187 // ------------------------------------------------------------------------
188 // Operations
189 // ------------------------------------------------------------------------
190
191 @Override
192 public Definition lookupDefinition(String lookupPath) {
193 /* TODO: lookup in different dynamic scopes is not supported yet. */
194 return null;
195 }
196
197 /**
198 * Create the index for this trace file.
199 */
200 public void setupIndex() {
201
202 /*
203 * The BitBuffer to extract data from the StreamInput
204 */
205 BitBuffer bitBuffer = new BitBuffer();
206 bitBuffer.setByteOrder(getStream().getTrace().getByteOrder());
207
208 /*
209 * Create the definitions we need to read the packet headers + contexts
210 */
211 if (getStream().getTrace().getPacketHeader() != null) {
212 fTracePacketHeaderDecl = getStream().getTrace().getPacketHeader();
213 }
214
215 if (getStream().getPacketContextDecl() != null) {
216 fStreamPacketContextDecl = getStream().getPacketContextDecl();
217 }
218
219 }
220
221 /**
222 * Adds the next packet header index entry to the index of a stream input.
223 *
224 * <strong>This method is slow and can corrupt data if not used properly</strong>
225 * @return true if there are more packets to add
226 * @throws CTFReaderException
227 * If there was a problem reading the packed header
228 */
229 public boolean addPacketHeaderIndex() throws CTFReaderException {
230 long currentPos = 0L;
231 if (!fIndex.getEntries().isEmpty()) {
232 StreamInputPacketIndexEntry pos = fIndex.getEntries().lastElement();
233 currentPos = computeNextOffset(pos);
234 }
235 long fileSize = getStreamSize();
236 if (currentPos < fileSize) {
237 BitBuffer bitBuffer = new BitBuffer();
238 bitBuffer.setByteOrder(getStream().getTrace().getByteOrder());
239 StreamInputPacketIndexEntry packetIndex = new StreamInputPacketIndexEntry(
240 currentPos);
241 createPacketIndexEntry(fileSize, currentPos, packetIndex,
242 fTracePacketHeaderDecl, fStreamPacketContextDecl, bitBuffer);
243 fIndex.addEntry(packetIndex);
244 return true;
245 }
246 return false;
247 }
248
249 private long getStreamSize() {
250 return fFile.length();
251 }
252
253 private long createPacketIndexEntry(long fileSizeBytes,
254 long packetOffsetBytes, StreamInputPacketIndexEntry packetIndex,
255 StructDeclaration tracePacketHeaderDecl,
256 StructDeclaration streamPacketContextDecl, @NonNull BitBuffer bitBuffer)
257 throws CTFReaderException {
258
259 /*
260 * Ignoring the return value, but this call is needed to initialize the
261 * input
262 */
263 createPacketBitBuffer(fileSizeBytes, packetOffsetBytes, packetIndex, bitBuffer);
264
265 /*
266 * Read the trace packet header if it exists.
267 */
268 if (tracePacketHeaderDecl != null) {
269 parseTracePacketHeader(tracePacketHeaderDecl, bitBuffer);
270 }
271
272 /*
273 * Read the stream packet context if it exists.
274 */
275 if (streamPacketContextDecl != null) {
276 parsePacketContext(fileSizeBytes, streamPacketContextDecl,
277 bitBuffer, packetIndex);
278 } else {
279 setPacketContextNull(fileSizeBytes, packetIndex);
280 }
281
282 /* Basic validation */
283 if (packetIndex.getContentSizeBits() > packetIndex.getPacketSizeBits()) {
284 throw new CTFReaderException("Content size > packet size"); //$NON-NLS-1$
285 }
286
287 if (packetIndex.getPacketSizeBits() > ((fileSizeBytes - packetIndex
288 .getOffsetBytes()) * 8)) {
289 throw new CTFReaderException("Not enough data remaining in the file for the size of this packet"); //$NON-NLS-1$
290 }
291
292 /*
293 * Offset in the file, in bits
294 */
295 packetIndex.setDataOffsetBits(bitBuffer.position());
296
297 /*
298 * Update the counting packet offset
299 */
300 return computeNextOffset(packetIndex);
301 }
302
303 /**
304 * @param packetIndex
305 * @return
306 */
307 private static long computeNextOffset(
308 StreamInputPacketIndexEntry packetIndex) {
309 return packetIndex.getOffsetBytes()
310 + ((packetIndex.getPacketSizeBits() + 7) / 8);
311 }
312
313 ByteBuffer getByteBufferAt(long position, long size) throws IOException {
314 return fFileChannel.map(MapMode.READ_ONLY, position, size);
315 }
316
317 /**
318 * @param fileSizeBytes
319 * @param packetOffsetBytes
320 * @param packetIndex
321 * @param bitBuffer
322 * @return
323 * @throws CTFReaderException
324 */
325 private ByteBuffer createPacketBitBuffer(long fileSizeBytes,
326 long packetOffsetBytes, StreamInputPacketIndexEntry packetIndex,
327 BitBuffer bitBuffer) throws CTFReaderException {
328 /*
329 * Initial size, it should map at least the packet header + context
330 * size.
331 *
332 * TODO: use a less arbitrary size.
333 */
334 long mapSize = 4096;
335 /*
336 * If there is less data remaining than what we want to map, reduce the
337 * map size.
338 */
339 if ((fileSizeBytes - packetIndex.getOffsetBytes()) < mapSize) {
340 mapSize = fileSizeBytes - packetIndex.getOffsetBytes();
341 }
342
343 /*
344 * Map the packet.
345 */
346 ByteBuffer bb;
347
348 try {
349 bb = getByteBufferAt(packetOffsetBytes, mapSize);
350 } catch (IOException e) {
351 throw new CTFReaderException(e);
352 }
353 bitBuffer.setByteBuffer(bb);
354 return bb;
355 }
356
357 private void parseTracePacketHeader(StructDeclaration tracePacketHeaderDecl,
358 @NonNull BitBuffer bitBuffer) throws CTFReaderException {
359 StructDefinition tracePacketHeaderDef = tracePacketHeaderDecl.createDefinition(fStream.getTrace(), LexicalScope.TRACE_PACKET_HEADER.getName(), bitBuffer);
360
361 /*
362 * Check the CTF magic number
363 */
364 IntegerDefinition magicDef = (IntegerDefinition) tracePacketHeaderDef
365 .lookupDefinition("magic"); //$NON-NLS-1$
366 if (magicDef != null) {
367 int magic = (int) magicDef.getValue();
368 if (magic != Utils.CTF_MAGIC) {
369 throw new CTFReaderException(
370 "CTF magic mismatch " + Integer.toHexString(magic) + " vs " + Integer.toHexString(Utils.CTF_MAGIC)); //$NON-NLS-1$//$NON-NLS-2$
371 }
372 }
373
374 /*
375 * Check the trace UUID
376 */
377 ArrayDefinition uuidDef =
378 (ArrayDefinition) tracePacketHeaderDef.lookupDefinition("uuid"); //$NON-NLS-1$
379 if (uuidDef != null) {
380 UUID uuid = Utils.getUUIDfromDefinition(uuidDef);
381
382 if (!getStream().getTrace().getUUID().equals(uuid)) {
383 throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$
384 }
385 }
386
387 /*
388 * Check that the stream id did not change
389 */
390 IntegerDefinition streamIDDef = (IntegerDefinition) tracePacketHeaderDef
391 .lookupDefinition("stream_id"); //$NON-NLS-1$
392 if (streamIDDef != null) {
393 long streamID = streamIDDef.getValue();
394
395 if (streamID != getStream().getId()) {
396 throw new CTFReaderException("Stream ID changing within a StreamInput"); //$NON-NLS-1$
397 }
398 }
399 }
400
401 private static void setPacketContextNull(long fileSizeBytes,
402 StreamInputPacketIndexEntry packetIndex) {
403 /*
404 * If there is no packet context, infer the content and packet size from
405 * the file size (assume that there is only one packet and no padding)
406 */
407 packetIndex.setContentSizeBits(fileSizeBytes * 8);
408 packetIndex.setPacketSizeBits(fileSizeBytes * 8);
409 }
410
411 private void parsePacketContext(long fileSizeBytes,
412 StructDeclaration streamPacketContextDecl, @NonNull BitBuffer bitBuffer,
413 StreamInputPacketIndexEntry packetIndex) throws CTFReaderException {
414 StructDefinition streamPacketContextDef = streamPacketContextDecl.createDefinition(null, LexicalScope.STREAM_PACKET_CONTEXT.getName(), bitBuffer);
415
416 for (String field : streamPacketContextDef.getDeclaration()
417 .getFieldsList()) {
418 Definition id = streamPacketContextDef.lookupDefinition(field);
419 if (id instanceof IntegerDefinition) {
420 packetIndex.addAttribute(field,
421 ((IntegerDefinition) id).getValue());
422 } else if (id instanceof FloatDefinition) {
423 packetIndex.addAttribute(field,
424 ((FloatDefinition) id).getValue());
425 } else if (id instanceof EnumDefinition) {
426 packetIndex.addAttribute(field,
427 ((EnumDefinition) id).getValue());
428 } else if (id instanceof StringDefinition) {
429 packetIndex.addAttribute(field,
430 ((StringDefinition) id).getValue());
431 }
432 }
433
434 Long contentSize = (Long) packetIndex.lookupAttribute("content_size"); //$NON-NLS-1$
435 Long packetSize = (Long) packetIndex.lookupAttribute("packet_size"); //$NON-NLS-1$
436 Long tsBegin = (Long) packetIndex.lookupAttribute("timestamp_begin"); //$NON-NLS-1$
437 Long tsEnd = (Long) packetIndex.lookupAttribute("timestamp_end"); //$NON-NLS-1$
438 String device = (String) packetIndex.lookupAttribute("device"); //$NON-NLS-1$
439 // LTTng Specific
440 Long cpuId = (Long) packetIndex.lookupAttribute("cpu_id"); //$NON-NLS-1$
441 Long lostEvents = (Long) packetIndex.lookupAttribute("events_discarded"); //$NON-NLS-1$
442
443 /* Read the content size in bits */
444 if (contentSize != null) {
445 packetIndex.setContentSizeBits(contentSize.intValue());
446 } else if (packetSize != null) {
447 packetIndex.setContentSizeBits(packetSize.longValue());
448 } else {
449 packetIndex.setContentSizeBits((int) (fileSizeBytes * 8));
450 }
451
452 /* Read the packet size in bits */
453 if (packetSize != null) {
454 packetIndex.setPacketSizeBits(packetSize.intValue());
455 } else if (packetIndex.getContentSizeBits() != 0) {
456 packetIndex.setPacketSizeBits(packetIndex.getContentSizeBits());
457 } else {
458 packetIndex.setPacketSizeBits((int) (fileSizeBytes * 8));
459 }
460
461 /* Read the begin timestamp */
462 if (tsBegin != null) {
463 packetIndex.setTimestampBegin(tsBegin.longValue());
464 }
465
466 /* Read the end timestamp */
467 if (tsEnd != null) {
468 if (tsEnd == -1) {
469 tsEnd = Long.MAX_VALUE;
470 }
471 packetIndex.setTimestampEnd(tsEnd.longValue());
472 setTimestampEnd(packetIndex.getTimestampEnd());
473 }
474
475 if (device != null) {
476 packetIndex.setTarget(device);
477 }
478
479 if (cpuId != null) {
480 packetIndex.setTarget("CPU" + cpuId.toString()); //$NON-NLS-1$
481 }
482
483 if (lostEvents != null) {
484 packetIndex.setLostEvents(lostEvents - fLostSoFar);
485 fLostSoFar = lostEvents;
486 }
487 }
488
489 @Override
490 public int hashCode() {
491 final int prime = 31;
492 int result = 1;
493 result = (prime * result) + ((fFile == null) ? 0 : fFile.hashCode());
494 return result;
495 }
496
497 @Override
498 public boolean equals(Object obj) {
499 if (this == obj) {
500 return true;
501 }
502 if (obj == null) {
503 return false;
504 }
505 if (!(obj instanceof CTFStreamInput)) {
506 return false;
507 }
508 CTFStreamInput other = (CTFStreamInput) obj;
509 if (fFile == null) {
510 if (other.fFile != null) {
511 return false;
512 }
513 } else if (!fFile.equals(other.fFile)) {
514 return false;
515 }
516 return true;
517 }
518
519 }
This page took 0.040843 seconds and 4 git commands to generate.