Commit | Line | Data |
---|---|---|
866e5b51 | 1 | /******************************************************************************* |
11252342 | 2 | * Copyright (c) 2011, 2013 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 | |
866e5b51 FC |
12 | *******************************************************************************/ |
13 | ||
14 | package org.eclipse.linuxtools.ctf.core.trace; | |
15 | ||
16 | import java.io.File; | |
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; | |
a94410d9 | 31 | import org.antlr.runtime.MismatchedTokenException; |
866e5b51 FC |
32 | import org.antlr.runtime.RecognitionException; |
33 | import org.antlr.runtime.tree.CommonTree; | |
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 | /** | |
d37aaa7f | 42 | * The CTF trace metadata 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 | */ | |
07002e0a | 57 | final static String METADATA_FILENAME = "metadata"; //$NON-NLS-1$ |
866e5b51 FC |
58 | |
59 | /** | |
60 | * Size of the metadata packet header, in bytes, computed by hand. | |
61 | */ | |
07002e0a | 62 | final static int METADATA_PACKET_HEADER_SIZE = 37; |
866e5b51 FC |
63 | |
64 | // ------------------------------------------------------------------------ | |
65 | // Attributes | |
66 | // ------------------------------------------------------------------------ | |
67 | ||
68 | /** | |
69 | * Reference to the metadata file | |
70 | */ | |
71 | private File metadataFile = null; | |
72 | ||
73 | /** | |
74 | * Byte order as detected when reading the TSDL magic number. | |
75 | */ | |
76 | private ByteOrder detectedByteOrder = null; | |
77 | ||
78 | /** | |
79 | * The trace file to which belongs this metadata file. | |
80 | */ | |
81 | private CTFTrace trace = null; | |
82 | ||
83 | // ------------------------------------------------------------------------ | |
84 | // Constructors | |
85 | // ------------------------------------------------------------------------ | |
86 | ||
87 | /** | |
88 | * Constructs a Metadata object. | |
aa572e22 | 89 | * |
866e5b51 FC |
90 | * @param trace |
91 | * The trace to which belongs this metadata file. | |
92 | */ | |
93 | public Metadata(CTFTrace trace) { | |
94 | this.trace = trace; | |
95 | ||
96 | /* Path of metadata file = trace directory path + metadata filename */ | |
97 | String metadataPath = trace.getTraceDirectory().getPath() | |
98 | + Utils.SEPARATOR + METADATA_FILENAME; | |
99 | ||
100 | /* Create a file reference to the metadata file */ | |
101 | metadataFile = new File(metadataPath); | |
102 | } | |
103 | ||
104 | // ------------------------------------------------------------------------ | |
105 | // Getters/Setters/Predicates | |
106 | // ------------------------------------------------------------------------ | |
107 | ||
108 | /** | |
109 | * Returns the ByteOrder that was detected while parsing the metadata. | |
aa572e22 | 110 | * |
866e5b51 FC |
111 | * @return The byte order. |
112 | */ | |
113 | public ByteOrder getDetectedByteOrder() { | |
114 | return detectedByteOrder; | |
115 | } | |
116 | ||
117 | // ------------------------------------------------------------------------ | |
118 | // Operations | |
119 | // ------------------------------------------------------------------------ | |
120 | ||
121 | /** | |
122 | * Parse the metadata file. | |
aa572e22 | 123 | * |
866e5b51 | 124 | * @throws CTFReaderException |
be6df2d8 | 125 | * If there was a problem parsing the metadata |
866e5b51 FC |
126 | */ |
127 | public void parse() throws CTFReaderException { | |
68c9980d AM |
128 | CTFReaderException tempException = null; |
129 | ||
130 | FileInputStream fis = null; | |
131 | FileChannel metadataFileChannel = null; | |
866e5b51 FC |
132 | |
133 | /* | |
134 | * Reader. It will contain a StringReader if we are using packet-based | |
135 | * metadata and it will contain a FileReader if we have text-based | |
136 | * metadata. | |
137 | */ | |
138 | Reader metadataTextInput = null; | |
139 | ||
68c9980d AM |
140 | try { |
141 | fis = new FileInputStream(metadataFile); | |
142 | metadataFileChannel = fis.getChannel(); | |
866e5b51 | 143 | |
68c9980d AM |
144 | /* Check if metadata is packet-based */ |
145 | if (isPacketBased(metadataFileChannel)) { | |
146 | /* Create StringBuffer to receive metadata text */ | |
147 | StringBuffer metadataText = new StringBuffer(); | |
866e5b51 | 148 | |
866e5b51 | 149 | /* |
68c9980d AM |
150 | * Read metadata packet one by one, appending the text to the |
151 | * StringBuffer | |
866e5b51 | 152 | */ |
68c9980d AM |
153 | MetadataPacketHeader packetHeader = readMetadataPacket( |
154 | metadataFileChannel, metadataText); | |
155 | while (packetHeader != null) { | |
156 | packetHeader = readMetadataPacket(metadataFileChannel, | |
157 | metadataText); | |
158 | } | |
159 | ||
160 | /* Wrap the metadata string with a StringReader */ | |
161 | metadataTextInput = new StringReader(metadataText.toString()); | |
162 | } else { | |
163 | /* Wrap the metadata file with a FileReader */ | |
164 | metadataTextInput = new FileReader(metadataFile); | |
866e5b51 | 165 | } |
866e5b51 | 166 | |
791072b0 | 167 | CommonTree tree = createAST(metadataTextInput); |
68c9980d AM |
168 | |
169 | /* Generate IO structures (declarations) */ | |
170 | IOStructGen gen = new IOStructGen(tree, trace); | |
171 | gen.generate(); | |
172 | ||
173 | } catch (FileNotFoundException e) { | |
174 | tempException = new CTFReaderException("Cannot find metadata file!"); //$NON-NLS-1$ | |
175 | } catch (IOException e) { | |
176 | /* This would indicate a problem with the ANTLR library... */ | |
177 | tempException = new CTFReaderException(e); | |
178 | } catch (ParseException e) { | |
179 | tempException = new CTFReaderException(e); | |
a94410d9 | 180 | } catch (MismatchedTokenException e) { |
c0804cf6 | 181 | tempException = new CtfAntlrException(e); |
866e5b51 | 182 | } catch (RecognitionException e) { |
c0804cf6 | 183 | tempException = new CtfAntlrException(e); |
866e5b51 | 184 | } |
866e5b51 | 185 | |
68c9980d AM |
186 | /* Ghetto resource management. Java 7 will deliver us from this... */ |
187 | if (metadataTextInput != null) { | |
188 | try { | |
189 | metadataTextInput.close(); | |
190 | } catch (IOException e) { | |
aa572e22 | 191 | // Do nothing |
68c9980d AM |
192 | } |
193 | } | |
194 | if (metadataFileChannel != null) { | |
195 | try { | |
196 | metadataFileChannel.close(); | |
197 | } catch (IOException e) { | |
aa572e22 | 198 | // Do nothing |
68c9980d AM |
199 | } |
200 | } | |
201 | if (fis != null) { | |
202 | try { | |
203 | fis.close(); | |
204 | } catch (IOException e) { | |
aa572e22 | 205 | // Do nothing |
68c9980d AM |
206 | } |
207 | } | |
208 | ||
209 | if (tempException != null) { | |
210 | throw tempException; | |
866e5b51 FC |
211 | } |
212 | } | |
213 | ||
791072b0 MK |
214 | private static CommonTree createAST(Reader metadataTextInput) throws IOException, |
215 | RecognitionException { | |
216 | /* Create an ANTLR reader */ | |
217 | ANTLRReaderStream antlrStream; | |
218 | antlrStream = new ANTLRReaderStream(metadataTextInput); | |
219 | ||
220 | /* Parse the metadata text and get the AST */ | |
221 | CTFLexer ctfLexer = new CTFLexer(antlrStream); | |
222 | CommonTokenStream tokens = new CommonTokenStream(ctfLexer); | |
223 | CTFParser ctfParser = new CTFParser(tokens, false); | |
224 | parse_return ret; | |
225 | ||
226 | ret = ctfParser.parse(); | |
227 | ||
228 | CommonTree tree = (CommonTree) ret.getTree(); | |
229 | return tree; | |
230 | } | |
231 | ||
866e5b51 FC |
232 | /** |
233 | * Determines whether the metadata file is packet-based by looking at the | |
234 | * TSDL magic number. If it is packet-based, it also gives information about | |
235 | * the endianness of the trace using the detectedByteOrder attribute. | |
aa572e22 | 236 | * |
866e5b51 FC |
237 | * @param metadataFileChannel |
238 | * FileChannel of the metadata file. | |
239 | * @return True if the metadata is packet-based. | |
240 | * @throws CTFReaderException | |
241 | */ | |
242 | private boolean isPacketBased(FileChannel metadataFileChannel) | |
243 | throws CTFReaderException { | |
244 | /* | |
245 | * Create a ByteBuffer to read the TSDL magic number (default is big | |
246 | * endian) | |
247 | */ | |
248 | ByteBuffer magicByteBuffer = ByteBuffer.allocate(Utils.TSDL_MAGIC_LEN); | |
249 | ||
250 | /* Read without changing file position */ | |
251 | try { | |
252 | metadataFileChannel.read(magicByteBuffer, 0); | |
253 | } catch (IOException e) { | |
a94410d9 | 254 | throw new CTFReaderException("Unable to read metadata file channel."); //$NON-NLS-1$ |
866e5b51 FC |
255 | } |
256 | ||
257 | /* Get the first int from the file */ | |
258 | int magic = magicByteBuffer.getInt(0); | |
259 | ||
260 | /* Check if it matches */ | |
261 | if (Utils.TSDL_MAGIC == magic) { | |
262 | detectedByteOrder = ByteOrder.BIG_ENDIAN; | |
263 | return true; | |
264 | } | |
265 | ||
266 | /* Try the same thing, but with little endian */ | |
267 | magicByteBuffer.order(ByteOrder.LITTLE_ENDIAN); | |
268 | magic = magicByteBuffer.getInt(0); | |
269 | ||
270 | if (Utils.TSDL_MAGIC == magic) { | |
271 | detectedByteOrder = ByteOrder.LITTLE_ENDIAN; | |
272 | return true; | |
273 | } | |
274 | ||
275 | return false; | |
276 | } | |
277 | ||
278 | /** | |
279 | * Reads a metadata packet from the given metadata FileChannel, do some | |
280 | * basic validation and append the text to the StringBuffer. | |
aa572e22 | 281 | * |
866e5b51 FC |
282 | * @param metadataFileChannel |
283 | * Metadata FileChannel | |
284 | * @param metadataText | |
285 | * StringBuffer to which the metadata text will be appended. | |
286 | * @return A structure describing the header of the metadata packet, or null | |
287 | * if the end of the file is reached. | |
288 | * @throws CTFReaderException | |
289 | */ | |
290 | private MetadataPacketHeader readMetadataPacket( | |
291 | FileChannel metadataFileChannel, StringBuffer metadataText) | |
292 | throws CTFReaderException { | |
293 | /* Allocate a ByteBuffer for the header */ | |
294 | ByteBuffer headerByteBuffer = ByteBuffer.allocate(METADATA_PACKET_HEADER_SIZE); | |
295 | ||
296 | /* Read the header */ | |
297 | int nbBytesRead; | |
298 | try { | |
299 | nbBytesRead = metadataFileChannel.read(headerByteBuffer); | |
300 | } catch (IOException e) { | |
301 | throw new CTFReaderException("Error reading the metadata header."); //$NON-NLS-1$ | |
302 | } | |
303 | ||
304 | /* Return null if EOF */ | |
305 | if (nbBytesRead < 0) { | |
306 | return null; | |
307 | } | |
308 | ||
309 | /* Set ByteBuffer's position to 0 */ | |
310 | headerByteBuffer.position(0); | |
311 | ||
312 | /* Use byte order that was detected with the magic number */ | |
313 | headerByteBuffer.order(detectedByteOrder); | |
314 | ||
315 | assert (nbBytesRead == METADATA_PACKET_HEADER_SIZE); | |
316 | ||
317 | MetadataPacketHeader header = new MetadataPacketHeader(); | |
318 | ||
319 | /* Read from the ByteBuffer */ | |
320 | header.magic = headerByteBuffer.getInt(); | |
321 | headerByteBuffer.get(header.uuid); | |
322 | header.checksum = headerByteBuffer.getInt(); | |
323 | header.contentSize = headerByteBuffer.getInt(); | |
324 | header.packetSize = headerByteBuffer.getInt(); | |
325 | header.compressionScheme = headerByteBuffer.get(); | |
326 | header.encryptionScheme = headerByteBuffer.get(); | |
327 | header.checksumScheme = headerByteBuffer.get(); | |
328 | header.ctfMajorVersion = headerByteBuffer.get(); | |
329 | header.ctfMinorVersion = headerByteBuffer.get(); | |
330 | ||
331 | /* Check TSDL magic number */ | |
332 | if (header.magic != Utils.TSDL_MAGIC) { | |
333 | throw new CTFReaderException("TSDL magic number does not match"); //$NON-NLS-1$ | |
334 | } | |
335 | ||
336 | /* Check UUID */ | |
337 | UUID uuid = Utils.makeUUID(header.uuid); | |
338 | if (!trace.UUIDIsSet()) { | |
339 | trace.setUUID(uuid); | |
340 | } else { | |
341 | if (!trace.getUUID().equals(uuid)) { | |
342 | throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$ | |
343 | } | |
344 | } | |
345 | ||
346 | /* Extract the text from the packet */ | |
347 | int payloadSize = ((header.contentSize / 8) - METADATA_PACKET_HEADER_SIZE); | |
4311ac8b MAL |
348 | if (payloadSize < 0) { |
349 | throw new CTFReaderException("Invalid metadata packet payload size."); //$NON-NLS-1$ | |
350 | } | |
866e5b51 FC |
351 | int skipSize = (header.packetSize - header.contentSize) / 8; |
352 | ||
353 | /* Read the payload + the padding in a ByteBuffer */ | |
354 | ByteBuffer payloadByteBuffer = ByteBuffer.allocateDirect(payloadSize | |
355 | + skipSize); | |
356 | try { | |
357 | metadataFileChannel.read(payloadByteBuffer); | |
358 | } catch (IOException e) { | |
a94410d9 | 359 | throw new CTFReaderException("Error reading metadata packet payload."); //$NON-NLS-1$ |
866e5b51 FC |
360 | } |
361 | payloadByteBuffer.rewind(); | |
362 | ||
363 | /* Read only the payload from the ByteBuffer into a byte array */ | |
364 | byte payloadByteArray[] = new byte[payloadByteBuffer.remaining()]; | |
365 | payloadByteBuffer.get(payloadByteArray, 0, payloadSize); | |
366 | ||
367 | /* Convert the byte array to a String */ | |
368 | String str = new String(payloadByteArray, 0, payloadSize); | |
369 | ||
370 | /* Append it to the existing metadata */ | |
371 | metadataText.append(str); | |
372 | ||
373 | return header; | |
374 | } | |
375 | ||
376 | static class MetadataPacketHeader { | |
377 | ||
378 | public int magic; | |
379 | public byte uuid[] = new byte[16]; | |
380 | public int checksum; | |
381 | public int contentSize; | |
382 | public int packetSize; | |
383 | public byte compressionScheme; | |
384 | public byte encryptionScheme; | |
385 | public byte checksumScheme; | |
386 | public byte ctfMajorVersion; | |
387 | public byte ctfMinorVersion; | |
388 | ||
389 | @Override | |
390 | public String toString() { | |
391 | /* Only for debugging, shouldn't be externalized */ | |
e291b8c8 | 392 | /* Therefore it cannot be covered by test cases */ |
866e5b51 FC |
393 | return "MetadataPacketHeader [magic=0x" //$NON-NLS-1$ |
394 | + Integer.toHexString(magic) + ", uuid=" //$NON-NLS-1$ | |
395 | + Arrays.toString(uuid) + ", checksum=" + checksum //$NON-NLS-1$ | |
396 | + ", contentSize=" + contentSize + ", packetSize=" //$NON-NLS-1$ //$NON-NLS-2$ | |
397 | + packetSize + ", compressionScheme=" + compressionScheme //$NON-NLS-1$ | |
398 | + ", encryptionScheme=" + encryptionScheme //$NON-NLS-1$ | |
399 | + ", checksumScheme=" + checksumScheme //$NON-NLS-1$ | |
400 | + ", ctfMajorVersion=" + ctfMajorVersion //$NON-NLS-1$ | |
401 | + ", ctfMinorVersion=" + ctfMinorVersion + ']'; //$NON-NLS-1$ | |
402 | } | |
403 | ||
404 | } | |
405 | } |