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