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