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