ctf: no longer invoke new String() in Metadata.java
[deliverable/tracecompass.git] / ctf / org.eclipse.tracecompass.ctf.core / src / org / eclipse / tracecompass / ctf / core / trace / Metadata.java
CommitLineData
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
51173fa1 13 * Bernd Hufmann - Add method to copy metadata file
866e5b51
FC
14 *******************************************************************************/
15
f357bcd4 16package org.eclipse.tracecompass.ctf.core.trace;
866e5b51 17
d16bb0dd 18import java.io.File;
866e5b51
FC
19import java.io.FileInputStream;
20import java.io.FileNotFoundException;
21import java.io.FileReader;
22import java.io.IOException;
23import java.io.Reader;
24import java.io.StringReader;
25import java.nio.ByteBuffer;
26import java.nio.ByteOrder;
27import java.nio.channels.FileChannel;
d16bb0dd 28import java.nio.charset.Charset;
51173fa1
BH
29import java.nio.file.FileSystems;
30import java.nio.file.Files;
31import java.nio.file.Path;
d16bb0dd 32import java.nio.file.StandardOpenOption;
866e5b51
FC
33import java.util.UUID;
34
35import org.antlr.runtime.ANTLRReaderStream;
36import org.antlr.runtime.CommonTokenStream;
37import org.antlr.runtime.RecognitionException;
38import org.antlr.runtime.tree.CommonTree;
4b825d8b 39import org.antlr.runtime.tree.RewriteCardinalityException;
b1ea73b5 40import org.eclipse.tracecompass.common.core.NonNullUtils;
680f9173 41import org.eclipse.tracecompass.ctf.core.CTFException;
f357bcd4
AM
42import org.eclipse.tracecompass.ctf.parser.CTFLexer;
43import org.eclipse.tracecompass.ctf.parser.CTFParser;
44import org.eclipse.tracecompass.ctf.parser.CTFParser.parse_return;
b1ea73b5 45import org.eclipse.tracecompass.internal.ctf.core.event.metadata.CtfAntlrException;
f357bcd4 46import org.eclipse.tracecompass.internal.ctf.core.event.metadata.IOStructGen;
b1ea73b5 47import org.eclipse.tracecompass.internal.ctf.core.event.metadata.ParseException;
1b638236 48import org.eclipse.tracecompass.internal.ctf.core.trace.Utils;
866e5b51
FC
49
50/**
58129ff7 51 * The CTF trace metadata TSDL file
791072b0 52 *
d37aaa7f
FC
53 * @version 1.0
54 * @author Matthew Khouzam
55 * @author Simon Marchi
866e5b51
FC
56 */
57public class Metadata {
58
59 // ------------------------------------------------------------------------
60 // Constants
61 // ------------------------------------------------------------------------
0f1e85f9 62 private static final String TEXT_ONLY_METADATA_HEADER_PREFIX = "/* CTF"; //$NON-NLS-1$
d16bb0dd
BH
63
64 private static final int PREVALIDATION_SIZE = 8;
866e5b51 65
f068c622
MK
66 private static final int BITS_PER_BYTE = Byte.SIZE;
67
866e5b51
FC
68 /**
69 * Name of the metadata file in the trace directory
70 */
0594c61c 71 private static final String METADATA_FILENAME = "metadata"; //$NON-NLS-1$
866e5b51
FC
72
73 /**
74 * Size of the metadata packet header, in bytes, computed by hand.
75 */
0594c61c 76 private static final int METADATA_PACKET_HEADER_SIZE = 37;
866e5b51
FC
77
78 // ------------------------------------------------------------------------
79 // Attributes
80 // ------------------------------------------------------------------------
81
866e5b51
FC
82 /**
83 * Byte order as detected when reading the TSDL magic number.
84 */
a67ad2a0 85 private ByteOrder fDetectedByteOrder = null;
866e5b51
FC
86
87 /**
88 * The trace file to which belongs this metadata file.
89 */
a67ad2a0 90 private final CTFTrace fTrace;
58129ff7
MK
91
92 private IOStructGen fTreeParser;
866e5b51
FC
93
94 // ------------------------------------------------------------------------
95 // Constructors
96 // ------------------------------------------------------------------------
97
98 /**
99 * Constructs a Metadata object.
aa572e22 100 *
866e5b51
FC
101 * @param trace
102 * The trace to which belongs this metadata file.
103 */
104 public Metadata(CTFTrace trace) {
a67ad2a0 105 fTrace = trace;
58129ff7 106 }
866e5b51 107
58129ff7
MK
108 /**
109 * For network streaming
58129ff7
MK
110 */
111 public Metadata() {
a67ad2a0 112 fTrace = new CTFTrace();
866e5b51
FC
113 }
114
115 // ------------------------------------------------------------------------
116 // Getters/Setters/Predicates
117 // ------------------------------------------------------------------------
118
119 /**
120 * Returns the ByteOrder that was detected while parsing the metadata.
aa572e22 121 *
866e5b51
FC
122 * @return The byte order.
123 */
124 public ByteOrder getDetectedByteOrder() {
a67ad2a0 125 return fDetectedByteOrder;
866e5b51
FC
126 }
127
58129ff7
MK
128 /**
129 * Gets the parent trace
130 *
131 * @return the parent trace
58129ff7
MK
132 */
133 public CTFTrace getTrace() {
a67ad2a0 134 return fTrace;
58129ff7
MK
135 }
136
866e5b51
FC
137 // ------------------------------------------------------------------------
138 // Operations
139 // ------------------------------------------------------------------------
140
141 /**
142 * Parse the metadata file.
aa572e22 143 *
680f9173 144 * @throws CTFException
be6df2d8 145 * If there was a problem parsing the metadata
866e5b51 146 */
680f9173 147 public void parseFile() throws CTFException {
866e5b51
FC
148
149 /*
150 * Reader. It will contain a StringReader if we are using packet-based
151 * metadata and it will contain a FileReader if we have text-based
152 * metadata.
153 */
58129ff7
MK
154
155 try (FileInputStream fis = new FileInputStream(getMetadataPath());
156 FileChannel metadataFileChannel = fis.getChannel();
157 /* Check if metadata is packet-based, if not it is text based */
b1ea73b5 158 Reader metadataTextInput = (isPacketBased(metadataFileChannel) ? readBinaryMetaData(metadataFileChannel) : new FileReader(getMetadataPath()));) {
58129ff7
MK
159
160 readMetaDataText(metadataTextInput);
68c9980d
AM
161
162 } catch (FileNotFoundException e) {
680f9173 163 throw new CTFException("Cannot find metadata file!", e); //$NON-NLS-1$
58129ff7 164 } catch (IOException | ParseException e) {
680f9173 165 throw new CTFException(e);
58129ff7
MK
166 } catch (RecognitionException | RewriteCardinalityException e) {
167 throw new CtfAntlrException(e);
168 }
169 }
170
680f9173 171 private Reader readBinaryMetaData(FileChannel metadataFileChannel) throws CTFException {
58129ff7
MK
172 /* Create StringBuffer to receive metadata text */
173 StringBuffer metadataText = new StringBuffer();
174
175 /*
176 * Read metadata packet one by one, appending the text to the
177 * StringBuffer
178 */
179 MetadataPacketHeader packetHeader = readMetadataPacket(
180 metadataFileChannel, metadataText);
181 while (packetHeader != null) {
182 packetHeader = readMetadataPacket(metadataFileChannel,
183 metadataText);
184 }
185
186 /* Wrap the metadata string with a StringReader */
187 return new StringReader(metadataText.toString());
188 }
189
d16bb0dd 190 /**
b1ea73b5
MK
191 * Executes a weak validation of the metadata. It checks if a file with name
192 * metadata exists and if one of the following conditions are met:
193 * <ul>
194 * <li>For text-only metadata, the file starts with "/* CTF" (without the
195 * quotes)</li>
196 * <li>For packet-based metadata, the file starts with correct magic number
197 * </li>
198 * </ul>
d16bb0dd
BH
199 *
200 * @param path
201 * path to CTF trace directory
202 * @return <code>true</code> if pre-validation is ok else <code>false</code>
203 * @throws CTFException
204 * file channel cannot be created
205 * @since 1.0
206 */
207 public static boolean preValidate(String path) throws CTFException {
208 String metadataPath = path + Utils.SEPARATOR + METADATA_FILENAME;
209 File metadataFile = new File(metadataPath);
210 if (metadataFile.exists() && metadataFile.length() > PREVALIDATION_SIZE) {
211 try (FileChannel fc = FileChannel.open(metadataFile.toPath(), StandardOpenOption.READ)) {
212 ByteBuffer bb = ByteBuffer.allocate(PREVALIDATION_SIZE);
213 bb.clear();
214 fc.read(bb);
215 bb.flip();
216 if (bb.getInt(0) == Utils.TSDL_MAGIC) {
217 return true;
218 }
219 bb.order(ByteOrder.LITTLE_ENDIAN);
220 if (bb.getInt(0) == Utils.TSDL_MAGIC) {
221 return true;
222 }
223 bb.position(0);
224 Charset forName = Charset.forName("ASCII"); //$NON-NLS-1$
225 byte bytes[] = new byte[PREVALIDATION_SIZE];
226 bb.get(bytes);
227 String text = new String(bytes, forName);
228 return text.startsWith(TEXT_ONLY_METADATA_HEADER_PREFIX);
229 } catch (IOException e) {
230 throw new CTFException(e.getMessage(), e);
231 }
232 }
233 return false;
234 }
235
58129ff7
MK
236 /**
237 * Read the metadata from a formatted TSDL string
238 *
239 * @param data
240 * the data to read
680f9173 241 * @throws CTFException
58129ff7
MK
242 * this exception wraps a ParseException, IOException or
243 * CtfAntlrException, three exceptions that can be obtained from
244 * parsing a TSDL file
58129ff7 245 */
680f9173 246 public void parseText(String data) throws CTFException {
58129ff7
MK
247 Reader metadataTextInput = new StringReader(data);
248 try {
249 readMetaDataText(metadataTextInput);
250 } catch (IOException | ParseException e) {
680f9173 251 throw new CTFException(e);
58129ff7 252 } catch (RecognitionException | RewriteCardinalityException e) {
950ee1f5 253 throw new CtfAntlrException(e);
58129ff7
MK
254 }
255
256 }
257
258 private void readMetaDataText(Reader metadataTextInput) throws IOException, RecognitionException, ParseException {
259 CommonTree tree = createAST(metadataTextInput);
260
261 /* Generate IO structures (declarations) */
b1ea73b5 262 fTreeParser = new IOStructGen(tree, NonNullUtils.checkNotNull(fTrace));
58129ff7 263 fTreeParser.generate();
a67ad2a0
MK
264 /* store locally in case of concurrent modification */
265 ByteOrder detectedByteOrder = getDetectedByteOrder();
266 if (detectedByteOrder != null && fTrace.getByteOrder() != detectedByteOrder) {
0f1e85f9
MK
267 throw new ParseException("Metadata byte order and trace byte order inconsistent."); //$NON-NLS-1$
268 }
58129ff7
MK
269 }
270
271 /**
272 * Read a metadata fragment from a formatted TSDL string
273 *
274 * @param dataFragment
275 * the data to read
680f9173 276 * @throws CTFException
58129ff7
MK
277 * this exception wraps a ParseException, IOException or
278 * CtfAntlrException, three exceptions that can be obtained from
279 * parsing a TSDL file
58129ff7 280 */
680f9173 281 public void parseTextFragment(String dataFragment) throws CTFException {
58129ff7
MK
282 Reader metadataTextInput = new StringReader(dataFragment);
283 try {
284 readMetaDataTextFragment(metadataTextInput);
285 } catch (IOException | ParseException e) {
680f9173 286 throw new CTFException(e);
58129ff7 287 } catch (RecognitionException | RewriteCardinalityException e) {
950ee1f5 288 throw new CtfAntlrException(e);
68c9980d 289 }
866e5b51
FC
290 }
291
58129ff7
MK
292 private void readMetaDataTextFragment(Reader metadataTextInput) throws IOException, RecognitionException, ParseException {
293 CommonTree tree = createAST(metadataTextInput);
294 fTreeParser.setTree(tree);
295 fTreeParser.generateFragment();
296 }
297
791072b0
MK
298 private static CommonTree createAST(Reader metadataTextInput) throws IOException,
299 RecognitionException {
300 /* Create an ANTLR reader */
301 ANTLRReaderStream antlrStream;
302 antlrStream = new ANTLRReaderStream(metadataTextInput);
303
304 /* Parse the metadata text and get the AST */
305 CTFLexer ctfLexer = new CTFLexer(antlrStream);
306 CommonTokenStream tokens = new CommonTokenStream(ctfLexer);
307 CTFParser ctfParser = new CTFParser(tokens, false);
791072b0 308
0594c61c 309 parse_return pr = ctfParser.parse();
bbbc7d98 310 return pr.getTree();
791072b0
MK
311 }
312
866e5b51
FC
313 /**
314 * Determines whether the metadata file is packet-based by looking at the
315 * TSDL magic number. If it is packet-based, it also gives information about
316 * the endianness of the trace using the detectedByteOrder attribute.
aa572e22 317 *
866e5b51
FC
318 * @param metadataFileChannel
319 * FileChannel of the metadata file.
320 * @return True if the metadata is packet-based.
680f9173 321 * @throws CTFException
866e5b51
FC
322 */
323 private boolean isPacketBased(FileChannel metadataFileChannel)
680f9173 324 throws CTFException {
866e5b51 325 /*
ecb12461
EB
326 * Create a ByteBuffer to read the TSDL magic number (default is
327 * big-endian)
866e5b51
FC
328 */
329 ByteBuffer magicByteBuffer = ByteBuffer.allocate(Utils.TSDL_MAGIC_LEN);
330
331 /* Read without changing file position */
332 try {
333 metadataFileChannel.read(magicByteBuffer, 0);
334 } catch (IOException e) {
680f9173 335 throw new CTFException("Unable to read metadata file channel.", e); //$NON-NLS-1$
866e5b51
FC
336 }
337
338 /* Get the first int from the file */
339 int magic = magicByteBuffer.getInt(0);
340
341 /* Check if it matches */
342 if (Utils.TSDL_MAGIC == magic) {
a67ad2a0 343 fDetectedByteOrder = ByteOrder.BIG_ENDIAN;
866e5b51
FC
344 return true;
345 }
346
ecb12461 347 /* Try the same thing, but with little-endian */
866e5b51
FC
348 magicByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
349 magic = magicByteBuffer.getInt(0);
350
351 if (Utils.TSDL_MAGIC == magic) {
a67ad2a0 352 fDetectedByteOrder = ByteOrder.LITTLE_ENDIAN;
866e5b51
FC
353 return true;
354 }
355
356 return false;
357 }
358
58129ff7
MK
359 private String getMetadataPath() {
360 /* Path of metadata file = trace directory path + metadata filename */
a67ad2a0 361 if (fTrace.getTraceDirectory() == null) {
d4a0dd35 362 return ""; //$NON-NLS-1$
58129ff7 363 }
a67ad2a0 364 return fTrace.getTraceDirectory().getPath()
58129ff7
MK
365 + Utils.SEPARATOR + METADATA_FILENAME;
366 }
367
866e5b51
FC
368 /**
369 * Reads a metadata packet from the given metadata FileChannel, do some
370 * basic validation and append the text to the StringBuffer.
aa572e22 371 *
866e5b51
FC
372 * @param metadataFileChannel
373 * Metadata FileChannel
374 * @param metadataText
375 * StringBuffer to which the metadata text will be appended.
376 * @return A structure describing the header of the metadata packet, or null
377 * if the end of the file is reached.
680f9173 378 * @throws CTFException
866e5b51
FC
379 */
380 private MetadataPacketHeader readMetadataPacket(
381 FileChannel metadataFileChannel, StringBuffer metadataText)
b1ea73b5 382 throws CTFException {
866e5b51
FC
383 /* Allocate a ByteBuffer for the header */
384 ByteBuffer headerByteBuffer = ByteBuffer.allocate(METADATA_PACKET_HEADER_SIZE);
385
386 /* Read the header */
866e5b51 387 try {
950ee1f5
EB
388 int nbBytesRead = metadataFileChannel.read(headerByteBuffer);
389
390 /* Return null if EOF */
391 if (nbBytesRead < 0) {
392 return null;
393 }
394
395 if (nbBytesRead != METADATA_PACKET_HEADER_SIZE) {
680f9173 396 throw new CTFException("Error reading the metadata header."); //$NON-NLS-1$
950ee1f5
EB
397 }
398
866e5b51 399 } catch (IOException e) {
680f9173 400 throw new CTFException("Error reading the metadata header.", e); //$NON-NLS-1$
866e5b51
FC
401 }
402
866e5b51
FC
403 /* Set ByteBuffer's position to 0 */
404 headerByteBuffer.position(0);
405
406 /* Use byte order that was detected with the magic number */
a67ad2a0 407 headerByteBuffer.order(fDetectedByteOrder);
866e5b51 408
373ab081 409 MetadataPacketHeader header = new MetadataPacketHeader(headerByteBuffer);
866e5b51
FC
410
411 /* Check TSDL magic number */
373ab081 412 if (!header.isMagicValid()) {
680f9173 413 throw new CTFException("TSDL magic number does not match"); //$NON-NLS-1$
866e5b51
FC
414 }
415
416 /* Check UUID */
a67ad2a0
MK
417 if (!fTrace.uuidIsSet()) {
418 fTrace.setUUID(header.getUuid());
419 } else if (!fTrace.getUUID().equals(header.getUuid())) {
680f9173 420 throw new CTFException("UUID mismatch"); //$NON-NLS-1$
866e5b51
FC
421 }
422
423 /* Extract the text from the packet */
f068c622 424 int payloadSize = ((header.getContentSize() / BITS_PER_BYTE) - METADATA_PACKET_HEADER_SIZE);
4311ac8b 425 if (payloadSize < 0) {
680f9173 426 throw new CTFException("Invalid metadata packet payload size."); //$NON-NLS-1$
4311ac8b 427 }
f068c622 428 int skipSize = (header.getPacketSize() - header.getContentSize()) / BITS_PER_BYTE;
866e5b51
FC
429
430 /* Read the payload + the padding in a ByteBuffer */
431 ByteBuffer payloadByteBuffer = ByteBuffer.allocateDirect(payloadSize
432 + skipSize);
433 try {
434 metadataFileChannel.read(payloadByteBuffer);
435 } catch (IOException e) {
680f9173 436 throw new CTFException("Error reading metadata packet payload.", e); //$NON-NLS-1$
866e5b51
FC
437 }
438 payloadByteBuffer.rewind();
439
440 /* Read only the payload from the ByteBuffer into a byte array */
441 byte payloadByteArray[] = new byte[payloadByteBuffer.remaining()];
442 payloadByteBuffer.get(payloadByteArray, 0, payloadSize);
443
444 /* Convert the byte array to a String */
445 String str = new String(payloadByteArray, 0, payloadSize);
446
447 /* Append it to the existing metadata */
448 metadataText.append(str);
449
450 return header;
451 }
452
0594c61c 453 private static class MetadataPacketHeader {
866e5b51 454
f068c622 455 private static final int UUID_SIZE = 16;
373ab081
MK
456 private final int fMagic;
457 private final UUID fUuid;
458 private final int fChecksum;
459 private final int fContentSize;
460 private final int fPacketSize;
461 private final byte fCompressionScheme;
462 private final byte fEncryptionScheme;
463 private final byte fChecksumScheme;
464 private final byte fCtfMajorVersion;
465 private final byte fCtfMinorVersion;
466
467 public MetadataPacketHeader(ByteBuffer headerByteBuffer) {
468 /* Read from the ByteBuffer */
469 fMagic = headerByteBuffer.getInt();
f068c622 470 byte[] uuidBytes = new byte[UUID_SIZE];
373ab081
MK
471 headerByteBuffer.get(uuidBytes);
472 fUuid = Utils.makeUUID(uuidBytes);
473 fChecksum = headerByteBuffer.getInt();
474 fContentSize = headerByteBuffer.getInt();
475 fPacketSize = headerByteBuffer.getInt();
476 fCompressionScheme = headerByteBuffer.get();
477 fEncryptionScheme = headerByteBuffer.get();
478 fChecksumScheme = headerByteBuffer.get();
479 fCtfMajorVersion = headerByteBuffer.get();
480 fCtfMinorVersion = headerByteBuffer.get();
481 }
482
483 public boolean isMagicValid() {
484 return fMagic == Utils.TSDL_MAGIC;
485 }
486
487 public UUID getUuid() {
488 return fUuid;
489 }
490
491 public int getContentSize() {
492 return fContentSize;
493 }
494
495 public int getPacketSize() {
496 return fPacketSize;
497 }
866e5b51
FC
498
499 @Override
500 public String toString() {
501 /* Only for debugging, shouldn't be externalized */
e291b8c8 502 /* Therefore it cannot be covered by test cases */
866e5b51 503 return "MetadataPacketHeader [magic=0x" //$NON-NLS-1$
373ab081
MK
504 + Integer.toHexString(fMagic) + ", uuid=" //$NON-NLS-1$
505 + fUuid.toString() + ", checksum=" + fChecksum //$NON-NLS-1$
506 + ", contentSize=" + fContentSize + ", packetSize=" //$NON-NLS-1$ //$NON-NLS-2$
507 + fPacketSize + ", compressionScheme=" + fCompressionScheme //$NON-NLS-1$
508 + ", encryptionScheme=" + fEncryptionScheme //$NON-NLS-1$
509 + ", checksumScheme=" + fChecksumScheme //$NON-NLS-1$
510 + ", ctfMajorVersion=" + fCtfMajorVersion //$NON-NLS-1$
511 + ", ctfMinorVersion=" + fCtfMinorVersion + ']'; //$NON-NLS-1$
866e5b51
FC
512 }
513
514 }
51173fa1
BH
515
516 /**
517 * Copies the metadata file to a destination directory.
b1ea73b5 518 *
51173fa1 519 * @param path
b1ea73b5 520 * the destination directory
51173fa1
BH
521 * @return the path to the target file
522 * @throws IOException
523 * if an error occurred
524 *
525 * @since 1.0
526 */
527 public Path copyTo(final File path) throws IOException {
a67ad2a0 528 Path source = FileSystems.getDefault().getPath(fTrace.getTraceDirectory().getAbsolutePath(), METADATA_FILENAME);
51173fa1
BH
529 Path destPath = FileSystems.getDefault().getPath(path.getAbsolutePath());
530 return Files.copy(source, destPath.resolve(source.getFileName()));
531 }
866e5b51 532}
This page took 0.089714 seconds and 5 git commands to generate.