Commit | Line | Data |
---|---|---|
866e5b51 | 1 | /******************************************************************************* |
58129ff7 | 2 | * Copyright (c) 2011, 2014 Ericsson, Ecole Polytechnique de Montreal and others |
866e5b51 FC |
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 | * | |
4311ac8b MAL |
9 | * Contributors: |
10 | * Matthew Khouzam - Initial API and implementation | |
11 | * Simon Marchi - Initial API and implementation | |
58129ff7 | 12 | * Matthew Khouzam - Update for live trace reading support |
866e5b51 FC |
13 | *******************************************************************************/ |
14 | ||
15 | package org.eclipse.linuxtools.ctf.core.trace; | |
16 | ||
866e5b51 FC |
17 | import java.io.FileInputStream; |
18 | import java.io.FileNotFoundException; | |
19 | import java.io.FileReader; | |
20 | import java.io.IOException; | |
21 | import java.io.Reader; | |
22 | import java.io.StringReader; | |
23 | import java.nio.ByteBuffer; | |
24 | import java.nio.ByteOrder; | |
25 | import java.nio.channels.FileChannel; | |
26 | import java.util.Arrays; | |
27 | import java.util.UUID; | |
28 | ||
29 | import org.antlr.runtime.ANTLRReaderStream; | |
30 | import org.antlr.runtime.CommonTokenStream; | |
31 | import org.antlr.runtime.RecognitionException; | |
32 | import org.antlr.runtime.tree.CommonTree; | |
4b825d8b | 33 | import org.antlr.runtime.tree.RewriteCardinalityException; |
866e5b51 FC |
34 | import org.eclipse.linuxtools.ctf.parser.CTFLexer; |
35 | import org.eclipse.linuxtools.ctf.parser.CTFParser; | |
36 | import org.eclipse.linuxtools.ctf.parser.CTFParser.parse_return; | |
ce2388e0 | 37 | import org.eclipse.linuxtools.internal.ctf.core.event.metadata.IOStructGen; |
c0804cf6 | 38 | import org.eclipse.linuxtools.internal.ctf.core.event.metadata.exceptions.CtfAntlrException; |
ce2388e0 | 39 | import org.eclipse.linuxtools.internal.ctf.core.event.metadata.exceptions.ParseException; |
866e5b51 FC |
40 | |
41 | /** | |
58129ff7 | 42 | * The CTF trace metadata TSDL file |
791072b0 | 43 | * |
d37aaa7f FC |
44 | * @version 1.0 |
45 | * @author Matthew Khouzam | |
46 | * @author Simon Marchi | |
866e5b51 FC |
47 | */ |
48 | public class Metadata { | |
49 | ||
50 | // ------------------------------------------------------------------------ | |
51 | // Constants | |
52 | // ------------------------------------------------------------------------ | |
53 | ||
54 | /** | |
55 | * Name of the metadata file in the trace directory | |
56 | */ | |
0594c61c | 57 | private static final String METADATA_FILENAME = "metadata"; //$NON-NLS-1$ |
866e5b51 FC |
58 | |
59 | /** | |
60 | * Size of the metadata packet header, in bytes, computed by hand. | |
61 | */ | |
0594c61c | 62 | private static final int METADATA_PACKET_HEADER_SIZE = 37; |
866e5b51 FC |
63 | |
64 | // ------------------------------------------------------------------------ | |
65 | // Attributes | |
66 | // ------------------------------------------------------------------------ | |
67 | ||
866e5b51 FC |
68 | /** |
69 | * Byte order as detected when reading the TSDL magic number. | |
70 | */ | |
71 | private ByteOrder detectedByteOrder = null; | |
72 | ||
73 | /** | |
74 | * The trace file to which belongs this metadata file. | |
75 | */ | |
58129ff7 MK |
76 | private final CTFTrace trace; |
77 | ||
78 | private IOStructGen fTreeParser; | |
866e5b51 FC |
79 | |
80 | // ------------------------------------------------------------------------ | |
81 | // Constructors | |
82 | // ------------------------------------------------------------------------ | |
83 | ||
84 | /** | |
85 | * Constructs a Metadata object. | |
aa572e22 | 86 | * |
866e5b51 FC |
87 | * @param trace |
88 | * The trace to which belongs this metadata file. | |
89 | */ | |
90 | public Metadata(CTFTrace trace) { | |
91 | this.trace = trace; | |
58129ff7 | 92 | } |
866e5b51 | 93 | |
58129ff7 MK |
94 | /** |
95 | * For network streaming | |
96 | * @since 3.0 | |
97 | */ | |
98 | public Metadata() { | |
99 | trace = new CTFTrace(); | |
866e5b51 FC |
100 | } |
101 | ||
102 | // ------------------------------------------------------------------------ | |
103 | // Getters/Setters/Predicates | |
104 | // ------------------------------------------------------------------------ | |
105 | ||
106 | /** | |
107 | * Returns the ByteOrder that was detected while parsing the metadata. | |
aa572e22 | 108 | * |
866e5b51 FC |
109 | * @return The byte order. |
110 | */ | |
111 | public ByteOrder getDetectedByteOrder() { | |
112 | return detectedByteOrder; | |
113 | } | |
114 | ||
58129ff7 MK |
115 | /** |
116 | * Gets the parent trace | |
117 | * | |
118 | * @return the parent trace | |
119 | * @since 3.0 | |
120 | */ | |
121 | public CTFTrace getTrace() { | |
122 | return trace; | |
123 | } | |
124 | ||
866e5b51 FC |
125 | // ------------------------------------------------------------------------ |
126 | // Operations | |
127 | // ------------------------------------------------------------------------ | |
128 | ||
129 | /** | |
130 | * Parse the metadata file. | |
aa572e22 | 131 | * |
866e5b51 | 132 | * @throws CTFReaderException |
be6df2d8 | 133 | * If there was a problem parsing the metadata |
58129ff7 | 134 | * @since 3.0 |
866e5b51 | 135 | */ |
58129ff7 | 136 | public void parseFile() throws CTFReaderException { |
866e5b51 FC |
137 | |
138 | /* | |
139 | * Reader. It will contain a StringReader if we are using packet-based | |
140 | * metadata and it will contain a FileReader if we have text-based | |
141 | * metadata. | |
142 | */ | |
58129ff7 MK |
143 | |
144 | try (FileInputStream fis = new FileInputStream(getMetadataPath()); | |
145 | FileChannel metadataFileChannel = fis.getChannel(); | |
146 | /* Check if metadata is packet-based, if not it is text based */ | |
147 | Reader metadataTextInput = | |
148 | (isPacketBased(metadataFileChannel) ? | |
ce3b0f16 MK |
149 | readBinaryMetaData(metadataFileChannel) : |
150 | new FileReader(getMetadataPath()));) { | |
58129ff7 MK |
151 | |
152 | readMetaDataText(metadataTextInput); | |
68c9980d AM |
153 | |
154 | } catch (FileNotFoundException e) { | |
950ee1f5 | 155 | throw new CTFReaderException("Cannot find metadata file!"); //$NON-NLS-1$ |
58129ff7 | 156 | } catch (IOException | ParseException e) { |
950ee1f5 | 157 | throw new CTFReaderException(e); |
58129ff7 MK |
158 | } catch (RecognitionException | RewriteCardinalityException e) { |
159 | throw new CtfAntlrException(e); | |
160 | } | |
161 | } | |
162 | ||
163 | private Reader readBinaryMetaData(FileChannel metadataFileChannel) throws CTFReaderException { | |
164 | /* Create StringBuffer to receive metadata text */ | |
165 | StringBuffer metadataText = new StringBuffer(); | |
166 | ||
167 | /* | |
168 | * Read metadata packet one by one, appending the text to the | |
169 | * StringBuffer | |
170 | */ | |
171 | MetadataPacketHeader packetHeader = readMetadataPacket( | |
172 | metadataFileChannel, metadataText); | |
173 | while (packetHeader != null) { | |
174 | packetHeader = readMetadataPacket(metadataFileChannel, | |
175 | metadataText); | |
176 | } | |
177 | ||
178 | /* Wrap the metadata string with a StringReader */ | |
179 | return new StringReader(metadataText.toString()); | |
180 | } | |
181 | ||
182 | /** | |
183 | * Read the metadata from a formatted TSDL string | |
184 | * | |
185 | * @param data | |
186 | * the data to read | |
187 | * @throws CTFReaderException | |
188 | * this exception wraps a ParseException, IOException or | |
189 | * CtfAntlrException, three exceptions that can be obtained from | |
190 | * parsing a TSDL file | |
191 | * @since 3.0 | |
192 | */ | |
193 | public void parseText(String data) throws CTFReaderException { | |
194 | Reader metadataTextInput = new StringReader(data); | |
195 | try { | |
196 | readMetaDataText(metadataTextInput); | |
197 | } catch (IOException | ParseException e) { | |
950ee1f5 | 198 | throw new CTFReaderException(e); |
58129ff7 | 199 | } catch (RecognitionException | RewriteCardinalityException e) { |
950ee1f5 | 200 | throw new CtfAntlrException(e); |
58129ff7 MK |
201 | } |
202 | ||
203 | } | |
204 | ||
205 | private void readMetaDataText(Reader metadataTextInput) throws IOException, RecognitionException, ParseException { | |
206 | CommonTree tree = createAST(metadataTextInput); | |
207 | ||
208 | /* Generate IO structures (declarations) */ | |
209 | fTreeParser = new IOStructGen(tree, trace); | |
210 | fTreeParser.generate(); | |
211 | } | |
212 | ||
213 | /** | |
214 | * Read a metadata fragment from a formatted TSDL string | |
215 | * | |
216 | * @param dataFragment | |
217 | * the data to read | |
218 | * @throws CTFReaderException | |
219 | * this exception wraps a ParseException, IOException or | |
220 | * CtfAntlrException, three exceptions that can be obtained from | |
221 | * parsing a TSDL file | |
222 | * @since 3.0 | |
223 | */ | |
224 | public void parseTextFragment(String dataFragment) throws CTFReaderException { | |
225 | Reader metadataTextInput = new StringReader(dataFragment); | |
226 | try { | |
227 | readMetaDataTextFragment(metadataTextInput); | |
228 | } catch (IOException | ParseException e) { | |
229 | throw new CTFReaderException(e); | |
230 | } catch (RecognitionException | RewriteCardinalityException e) { | |
950ee1f5 | 231 | throw new CtfAntlrException(e); |
68c9980d | 232 | } |
866e5b51 FC |
233 | } |
234 | ||
58129ff7 MK |
235 | private void readMetaDataTextFragment(Reader metadataTextInput) throws IOException, RecognitionException, ParseException { |
236 | CommonTree tree = createAST(metadataTextInput); | |
237 | fTreeParser.setTree(tree); | |
238 | fTreeParser.generateFragment(); | |
239 | } | |
240 | ||
791072b0 MK |
241 | private static CommonTree createAST(Reader metadataTextInput) throws IOException, |
242 | RecognitionException { | |
243 | /* Create an ANTLR reader */ | |
244 | ANTLRReaderStream antlrStream; | |
245 | antlrStream = new ANTLRReaderStream(metadataTextInput); | |
246 | ||
247 | /* Parse the metadata text and get the AST */ | |
248 | CTFLexer ctfLexer = new CTFLexer(antlrStream); | |
249 | CommonTokenStream tokens = new CommonTokenStream(ctfLexer); | |
250 | CTFParser ctfParser = new CTFParser(tokens, false); | |
791072b0 | 251 | |
0594c61c | 252 | parse_return pr = ctfParser.parse(); |
bbbc7d98 | 253 | return pr.getTree(); |
791072b0 MK |
254 | } |
255 | ||
866e5b51 FC |
256 | /** |
257 | * Determines whether the metadata file is packet-based by looking at the | |
258 | * TSDL magic number. If it is packet-based, it also gives information about | |
259 | * the endianness of the trace using the detectedByteOrder attribute. | |
aa572e22 | 260 | * |
866e5b51 FC |
261 | * @param metadataFileChannel |
262 | * FileChannel of the metadata file. | |
263 | * @return True if the metadata is packet-based. | |
264 | * @throws CTFReaderException | |
265 | */ | |
266 | private boolean isPacketBased(FileChannel metadataFileChannel) | |
267 | throws CTFReaderException { | |
268 | /* | |
ecb12461 EB |
269 | * Create a ByteBuffer to read the TSDL magic number (default is |
270 | * big-endian) | |
866e5b51 FC |
271 | */ |
272 | ByteBuffer magicByteBuffer = ByteBuffer.allocate(Utils.TSDL_MAGIC_LEN); | |
273 | ||
274 | /* Read without changing file position */ | |
275 | try { | |
276 | metadataFileChannel.read(magicByteBuffer, 0); | |
277 | } catch (IOException e) { | |
0594c61c | 278 | throw new CTFReaderException("Unable to read metadata file channel.", e); //$NON-NLS-1$ |
866e5b51 FC |
279 | } |
280 | ||
281 | /* Get the first int from the file */ | |
282 | int magic = magicByteBuffer.getInt(0); | |
283 | ||
284 | /* Check if it matches */ | |
285 | if (Utils.TSDL_MAGIC == magic) { | |
286 | detectedByteOrder = ByteOrder.BIG_ENDIAN; | |
287 | return true; | |
288 | } | |
289 | ||
ecb12461 | 290 | /* Try the same thing, but with little-endian */ |
866e5b51 FC |
291 | magicByteBuffer.order(ByteOrder.LITTLE_ENDIAN); |
292 | magic = magicByteBuffer.getInt(0); | |
293 | ||
294 | if (Utils.TSDL_MAGIC == magic) { | |
295 | detectedByteOrder = ByteOrder.LITTLE_ENDIAN; | |
296 | return true; | |
297 | } | |
298 | ||
299 | return false; | |
300 | } | |
301 | ||
58129ff7 MK |
302 | private String getMetadataPath() { |
303 | /* Path of metadata file = trace directory path + metadata filename */ | |
304 | if (trace.getTraceDirectory() == null) { | |
305 | return new String(); | |
306 | } | |
307 | return trace.getTraceDirectory().getPath() | |
308 | + Utils.SEPARATOR + METADATA_FILENAME; | |
309 | } | |
310 | ||
866e5b51 FC |
311 | /** |
312 | * Reads a metadata packet from the given metadata FileChannel, do some | |
313 | * basic validation and append the text to the StringBuffer. | |
aa572e22 | 314 | * |
866e5b51 FC |
315 | * @param metadataFileChannel |
316 | * Metadata FileChannel | |
317 | * @param metadataText | |
318 | * StringBuffer to which the metadata text will be appended. | |
319 | * @return A structure describing the header of the metadata packet, or null | |
320 | * if the end of the file is reached. | |
321 | * @throws CTFReaderException | |
322 | */ | |
323 | private MetadataPacketHeader readMetadataPacket( | |
324 | FileChannel metadataFileChannel, StringBuffer metadataText) | |
325 | throws CTFReaderException { | |
326 | /* Allocate a ByteBuffer for the header */ | |
327 | ByteBuffer headerByteBuffer = ByteBuffer.allocate(METADATA_PACKET_HEADER_SIZE); | |
328 | ||
329 | /* Read the header */ | |
866e5b51 | 330 | try { |
950ee1f5 EB |
331 | int nbBytesRead = metadataFileChannel.read(headerByteBuffer); |
332 | ||
333 | /* Return null if EOF */ | |
334 | if (nbBytesRead < 0) { | |
335 | return null; | |
336 | } | |
337 | ||
338 | if (nbBytesRead != METADATA_PACKET_HEADER_SIZE) { | |
339 | throw new CTFReaderException("Error reading the metadata header."); //$NON-NLS-1$ | |
340 | } | |
341 | ||
866e5b51 | 342 | } catch (IOException e) { |
0594c61c | 343 | throw new CTFReaderException("Error reading the metadata header.", e); //$NON-NLS-1$ |
866e5b51 FC |
344 | } |
345 | ||
866e5b51 FC |
346 | /* Set ByteBuffer's position to 0 */ |
347 | headerByteBuffer.position(0); | |
348 | ||
349 | /* Use byte order that was detected with the magic number */ | |
350 | headerByteBuffer.order(detectedByteOrder); | |
351 | ||
ce3b0f16 MK |
352 | MetadataPacketHeader header = new MetadataPacketHeader(); |
353 | ||
354 | /* Read from the ByteBuffer */ | |
355 | header.magic = headerByteBuffer.getInt(); | |
356 | headerByteBuffer.get(header.uuid); | |
357 | header.checksum = headerByteBuffer.getInt(); | |
358 | header.contentSize = headerByteBuffer.getInt(); | |
359 | header.packetSize = headerByteBuffer.getInt(); | |
360 | header.compressionScheme = headerByteBuffer.get(); | |
361 | header.encryptionScheme = headerByteBuffer.get(); | |
362 | header.checksumScheme = headerByteBuffer.get(); | |
363 | header.ctfMajorVersion = headerByteBuffer.get(); | |
364 | header.ctfMinorVersion = headerByteBuffer.get(); | |
866e5b51 FC |
365 | |
366 | /* Check TSDL magic number */ | |
ce3b0f16 | 367 | if (header.magic != Utils.TSDL_MAGIC) { |
866e5b51 FC |
368 | throw new CTFReaderException("TSDL magic number does not match"); //$NON-NLS-1$ |
369 | } | |
370 | ||
371 | /* Check UUID */ | |
ce3b0f16 | 372 | UUID uuid = Utils.makeUUID(header.uuid); |
0594c61c | 373 | if (!trace.uuidIsSet()) { |
ce3b0f16 MK |
374 | trace.setUUID(uuid); |
375 | } else if (!trace.getUUID().equals(uuid)) { | |
950ee1f5 | 376 | throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$ |
866e5b51 FC |
377 | } |
378 | ||
379 | /* Extract the text from the packet */ | |
ce3b0f16 | 380 | int payloadSize = ((header.contentSize / 8) - METADATA_PACKET_HEADER_SIZE); |
4311ac8b MAL |
381 | if (payloadSize < 0) { |
382 | throw new CTFReaderException("Invalid metadata packet payload size."); //$NON-NLS-1$ | |
383 | } | |
ce3b0f16 | 384 | int skipSize = (header.packetSize - header.contentSize) / 8; |
866e5b51 FC |
385 | |
386 | /* Read the payload + the padding in a ByteBuffer */ | |
387 | ByteBuffer payloadByteBuffer = ByteBuffer.allocateDirect(payloadSize | |
388 | + skipSize); | |
389 | try { | |
390 | metadataFileChannel.read(payloadByteBuffer); | |
391 | } catch (IOException e) { | |
0594c61c | 392 | throw new CTFReaderException("Error reading metadata packet payload.", e); //$NON-NLS-1$ |
866e5b51 FC |
393 | } |
394 | payloadByteBuffer.rewind(); | |
395 | ||
396 | /* Read only the payload from the ByteBuffer into a byte array */ | |
397 | byte payloadByteArray[] = new byte[payloadByteBuffer.remaining()]; | |
398 | payloadByteBuffer.get(payloadByteArray, 0, payloadSize); | |
399 | ||
400 | /* Convert the byte array to a String */ | |
401 | String str = new String(payloadByteArray, 0, payloadSize); | |
402 | ||
403 | /* Append it to the existing metadata */ | |
404 | metadataText.append(str); | |
405 | ||
406 | return header; | |
407 | } | |
408 | ||
0594c61c | 409 | private static class MetadataPacketHeader { |
866e5b51 | 410 | |
ce3b0f16 MK |
411 | public int magic; |
412 | public byte uuid[] = new byte[16]; | |
413 | public int checksum; | |
414 | public int contentSize; | |
415 | public int packetSize; | |
416 | public byte compressionScheme; | |
417 | public byte encryptionScheme; | |
418 | public byte checksumScheme; | |
419 | public byte ctfMajorVersion; | |
420 | public byte ctfMinorVersion; | |
866e5b51 FC |
421 | |
422 | @Override | |
423 | public String toString() { | |
424 | /* Only for debugging, shouldn't be externalized */ | |
e291b8c8 | 425 | /* Therefore it cannot be covered by test cases */ |
866e5b51 | 426 | return "MetadataPacketHeader [magic=0x" //$NON-NLS-1$ |
ce3b0f16 MK |
427 | + Integer.toHexString(magic) + ", uuid=" //$NON-NLS-1$ |
428 | + Arrays.toString(uuid) + ", checksum=" + checksum //$NON-NLS-1$ | |
429 | + ", contentSize=" + contentSize + ", packetSize=" //$NON-NLS-1$ //$NON-NLS-2$ | |
430 | + packetSize + ", compressionScheme=" + compressionScheme //$NON-NLS-1$ | |
431 | + ", encryptionScheme=" + encryptionScheme //$NON-NLS-1$ | |
432 | + ", checksumScheme=" + checksumScheme //$NON-NLS-1$ | |
433 | + ", ctfMajorVersion=" + ctfMajorVersion //$NON-NLS-1$ | |
434 | + ", ctfMinorVersion=" + ctfMinorVersion + ']'; //$NON-NLS-1$ | |
866e5b51 FC |
435 | } |
436 | ||
437 | } | |
438 | } |