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