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