ctf: Actually close the filechannels to the streams
[deliverable/tracecompass.git] / org.eclipse.linuxtools.ctf.core / src / org / eclipse / linuxtools / ctf / core / trace / CTFTrace.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: Alexandre Montplaisir - Initial API and implementation
11 *******************************************************************************/
12
13 package org.eclipse.linuxtools.ctf.core.trace;
14
15 import java.io.File;
16 import java.io.FileFilter;
17 import java.io.FileInputStream;
18 import java.io.IOException;
19 import java.nio.ByteOrder;
20 import java.nio.MappedByteBuffer;
21 import java.nio.channels.FileChannel;
22 import java.nio.channels.FileChannel.MapMode;
23 import java.util.Arrays;
24 import java.util.Comparator;
25 import java.util.HashMap;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.UUID;
29
30 import org.eclipse.linuxtools.ctf.core.event.CTFClock;
31 import org.eclipse.linuxtools.ctf.core.event.types.ArrayDefinition;
32 import org.eclipse.linuxtools.ctf.core.event.types.Definition;
33 import org.eclipse.linuxtools.ctf.core.event.types.IDefinitionScope;
34 import org.eclipse.linuxtools.ctf.core.event.types.IntegerDefinition;
35 import org.eclipse.linuxtools.ctf.core.event.types.StructDeclaration;
36 import org.eclipse.linuxtools.ctf.core.event.types.StructDefinition;
37 import org.eclipse.linuxtools.internal.ctf.core.Activator;
38 import org.eclipse.linuxtools.internal.ctf.core.event.io.BitBuffer;
39 import org.eclipse.linuxtools.internal.ctf.core.event.metadata.exceptions.ParseException;
40 import org.eclipse.linuxtools.internal.ctf.core.trace.Stream;
41 import org.eclipse.linuxtools.internal.ctf.core.trace.StreamInput;
42
43 /**
44 * <b><u>CTFTrace</u></b>
45 * <p>
46 * Represents a trace on the filesystem. It is responsible of parsing the
47 * metadata, creating declarations data structures, indexing the event packets
48 * (in other words, all the work that can be shared between readers), but the
49 * actual reading of events is left to TraceReader.
50 *
51 * @author Matthew Khouzam
52 * @version $Revision: 1.0 $
53 */
54 public class CTFTrace implements IDefinitionScope {
55
56 // ------------------------------------------------------------------------
57 // Attributes
58 // ------------------------------------------------------------------------
59
60 /*
61 * (non-Javadoc)
62 *
63 * @see java.lang.Object#toString()
64 */
65 @SuppressWarnings("nls")
66 @Override
67 public String toString() {
68 /* Only for debugging, shouldn't be externalized */
69 return "CTFTrace [path=" + path + ", major=" + major + ", minor="
70 + minor + ", uuid=" + uuid + "]";
71 }
72
73 /**
74 * The trace directory on the filesystem.
75 */
76 private final File path;
77
78 /**
79 * The metadata parsing object.
80 */
81 private final Metadata metadata;
82
83 /**
84 * Major CTF version number
85 */
86 private Long major;
87
88 /**
89 * Minor CTF version number
90 */
91 private Long minor;
92
93 /**
94 * Trace UUID
95 */
96 private UUID uuid;
97
98 /**
99 * Trace byte order
100 */
101 private ByteOrder byteOrder;
102
103 /**
104 * Packet header structure declaration
105 */
106 private StructDeclaration packetHeaderDecl;
107
108 /**
109 * Packet header structure definition
110 *
111 * This is only used when opening the trace files, to read the first packet
112 * header and see if they are valid trace files.
113 */
114 private StructDefinition packetHeaderDef;
115
116 /**
117 * Collection of streams contained in the trace.
118 */
119 private final HashMap<Long, Stream> streams;
120
121 /**
122 * Collection of environment variables set by the tracer
123 */
124 private final HashMap<String, String> environment;
125
126 /**
127 * Collection of all the clocks in a system.
128 */
129 private final HashMap<String, CTFClock> clocks;
130
131 /** FileChannels to the streams */
132 private final FileChannel[] streamFileChannels;
133
134 /** Handlers for the metadata files */
135 private final static FileFilter metadataFileFilter = new MetadataFileFilter();
136 private final static Comparator<File> metadataComparator = new MetadataComparator();
137
138 // ------------------------------------------------------------------------
139 // Constructors
140 // ------------------------------------------------------------------------
141
142 /**
143 * Trace constructor.
144 *
145 * @param path
146 * Filesystem path of the trace directory.
147 * @throws IOException
148 */
149 public CTFTrace(String path) throws CTFReaderException {
150 this(new File(path));
151 }
152
153 /**
154 * Trace constructor.
155 *
156 * @param path
157 * Filesystem path of the trace directory.
158 * @throws CTFReaderException
159 */
160 public CTFTrace(File path) throws CTFReaderException {
161 this.path = path;
162 this.metadata = new Metadata(this);
163
164 if (!this.path.isDirectory()) {
165 throw new CTFReaderException("Path must be a valid directory"); //$NON-NLS-1$
166 }
167
168 /* Set up the internal containers for this trace */
169 streams = new HashMap<Long, Stream>();
170 environment = new HashMap<String, String>();
171 clocks = new HashMap<String, CTFClock>();
172
173 /* Open and parse the metadata file */
174 metadata.parse();
175
176 if (Activator.getDefault() != null) {
177 Activator.getDefault().log(metadata.toString());
178 }
179
180 /* Open all the trace files */
181 /* Create the definitions needed to read things from the files */
182 if (packetHeaderDecl != null) {
183 packetHeaderDef = packetHeaderDecl.createDefinition(this,
184 "packet.header"); //$NON-NLS-1$
185 }
186
187 /* List files not called metadata and not hidden. */
188 File[] files = path.listFiles(metadataFileFilter);
189 Arrays.sort(files, metadataComparator);
190
191 /* Try to open each file */
192 streamFileChannels = new FileChannel[files.length];
193 for (int i = 0; i < files.length; i++) {
194 openStreamInput(files[i], i);
195 }
196
197 /* Create their index */
198 for (Map.Entry<Long, Stream> stream : streams.entrySet()) {
199 Set<StreamInput> inputs = stream.getValue().getStreamInputs();
200 for (StreamInput s : inputs) {
201 s.createIndex();
202 }
203 }
204 }
205
206 @Override
207 protected void finalize() {
208 /* If this trace gets closed, release the descriptors to the streams */
209 for (FileChannel fc : streamFileChannels) {
210 if (fc != null) {
211 try {
212 fc.close();
213 } catch (IOException e) {
214 }
215 }
216 }
217 }
218
219 // ------------------------------------------------------------------------
220 // Getters/Setters/Predicates
221 // ------------------------------------------------------------------------
222
223 /**
224 * Method getStream gets the stream for a given id
225 *
226 * @param id
227 * Long the id of the stream
228 * @return Stream the stream that we need
229 */
230 public Stream getStream(Long id) {
231 return streams.get(id);
232 }
233
234 /**
235 * Method nbStreams gets the number of available streams
236 *
237 * @return int the number of streams
238 */
239 public int nbStreams() {
240 return streams.size();
241 }
242
243 /**
244 * Method setMajor sets the major version of the trace (DO NOT USE)
245 *
246 * @param major
247 * long the major version
248 */
249 public void setMajor(long major) {
250 this.major = major;
251 }
252
253 /**
254 * Method setMinor sets the minor version of the trace (DO NOT USE)
255 *
256 * @param minor
257 * long the minor version
258 */
259 public void setMinor(long minor) {
260 this.minor = minor;
261 }
262
263 /**
264 * Method setUUID sets the UUID of a trace
265 *
266 * @param uuid
267 * UUID
268 */
269 public void setUUID(UUID uuid) {
270 this.uuid = uuid;
271 }
272
273 /**
274 * Method setByteOrder sets the byte order
275 *
276 * @param byteOrder
277 * ByteOrder of the trace, can be little-endian or big-endian
278 */
279 public void setByteOrder(ByteOrder byteOrder) {
280 this.byteOrder = byteOrder;
281 }
282
283 /**
284 * Method setPacketHeader sets the packet header of a trace (DO NOT USE)
285 *
286 * @param packetHeader
287 * StructDeclaration the header in structdeclaration form
288 */
289 public void setPacketHeader(StructDeclaration packetHeader) {
290 this.packetHeaderDecl = packetHeader;
291 }
292
293 /**
294 * Method majortIsSet is the major version number set?
295 *
296 * @return boolean is the major set?
297 */
298 public boolean majortIsSet() {
299 return major != null;
300 }
301
302 /**
303 * Method minorIsSet. is the minor version number set?
304 *
305 * @return boolean is the minor set?
306 */
307 public boolean minorIsSet() {
308 return minor != null;
309 }
310
311 /**
312 * Method UUIDIsSet is the UUID set?
313 *
314 * @return boolean is the UUID set?
315 */
316 public boolean UUIDIsSet() {
317 return uuid != null;
318 }
319
320 /**
321 * Method byteOrderIsSet is the byteorder set?
322 *
323 * @return boolean is the byteorder set?
324 */
325 public boolean byteOrderIsSet() {
326 return byteOrder != null;
327 }
328
329 /**
330 * Method packetHeaderIsSet is the packet header set?
331 *
332 * @return boolean is the packet header set?
333 */
334 public boolean packetHeaderIsSet() {
335 return packetHeaderDecl != null;
336 }
337
338 /**
339 * Method getUUID gets the trace UUID
340 *
341 * @return UUID gets the trace UUID
342 */
343 public UUID getUUID() {
344 return uuid;
345 }
346
347 /**
348 * Method getMajor gets the trace major version
349 *
350 * @return long gets the trace major version
351 */
352 public long getMajor() {
353 return major;
354 }
355
356 /**
357 * Method getMinor gets the trace minor version
358 *
359 * @return long gets the trace minor version
360 */
361 public long getMinor() {
362 return minor;
363 }
364
365 /**
366 * Method getByteOrder gets the trace byte order
367 *
368 * @return ByteOrder gets the trace byte order
369 */
370 public ByteOrder getByteOrder() {
371 return byteOrder;
372 }
373
374 /**
375 * Method getPacketHeader gets the trace packet header
376 *
377 * @return StructDeclaration gets the trace packet header
378 */
379 public StructDeclaration getPacketHeader() {
380 return packetHeaderDecl;
381 }
382
383 /**
384 * Method getTraceDirectory gets the trace directory
385 *
386 * @return File the path in "File" format.
387 */
388 public File getTraceDirectory() {
389 return path;
390 }
391
392 /**
393 * Method getStreams get all the streams in a map format.
394 *
395 * @return Map<Long,Stream> a map of all the streams.
396 */
397 public Map<Long, Stream> getStreams() {
398 return streams;
399 }
400
401 /**
402 * Method getPath gets the path of the trace directory
403 *
404 * @return String the path of the trace directory, in string format.
405 * @see java.io.File#getPath()
406 */
407 @Override
408 public String getPath() {
409 return path.getPath();
410 }
411
412 // ------------------------------------------------------------------------
413 // Operations
414 // ------------------------------------------------------------------------
415
416 /**
417 * Tries to open the given file, reads the first packet header of the file
418 * and check its validity.
419 *
420 * @param streamFile
421 * A trace file in the trace directory.
422 * @param index
423 * Which index in the class' streamFileChannel array this file
424 * must use
425 * @throws CTFReaderException
426 */
427 private void openStreamInput(File streamFile, int index)
428 throws CTFReaderException {
429 MappedByteBuffer byteBuffer;
430 BitBuffer streamBitBuffer;
431
432 if (!streamFile.canRead()) {
433 throw new CTFReaderException("Unreadable file : " //$NON-NLS-1$
434 + streamFile.getPath());
435 }
436
437 try {
438 /* Open the file and get the FileChannel */
439 streamFileChannels[index] = new FileInputStream(streamFile).getChannel();
440
441 /* Map one memory page of 4 kiB */
442 byteBuffer = streamFileChannels[index].map(MapMode.READ_ONLY, 0,
443 4096);
444 } catch (IOException e) {
445 /* Shouldn't happen at this stage if every other check passed */
446 throw new CTFReaderException();
447 }
448
449 /* Create a BitBuffer with this mapping and the trace byte order */
450 streamBitBuffer = new BitBuffer(byteBuffer, this.getByteOrder());
451
452 if (packetHeaderDef != null) {
453 /* Read the packet header */
454 packetHeaderDef.read(streamBitBuffer);
455
456 /* Check the magic number */
457 IntegerDefinition magicDef = (IntegerDefinition) packetHeaderDef.lookupDefinition("magic"); //$NON-NLS-1$
458 int magic = (int) magicDef.getValue();
459 if (magic != Utils.CTF_MAGIC) {
460 throw new CTFReaderException("CTF magic mismatch"); //$NON-NLS-1$
461 }
462
463 /* Check UUID */
464 ArrayDefinition uuidDef = (ArrayDefinition) packetHeaderDef.lookupDefinition("uuid"); //$NON-NLS-1$
465 assert ((uuidDef != null) && (uuidDef.getDeclaration().getLength() == Utils.UUID_LEN));
466 if (uuidDef != null) {
467 byte[] uuidArray = new byte[Utils.UUID_LEN];
468
469 for (int i = 0; i < Utils.UUID_LEN; i++) {
470 IntegerDefinition uuidByteDef = (IntegerDefinition) uuidDef.getElem(i);
471 uuidArray[i] = (byte) uuidByteDef.getValue();
472 }
473
474 UUID otheruuid = Utils.makeUUID(uuidArray);
475
476 if (!this.uuid.equals(otheruuid)) {
477 throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$
478 }
479 }
480
481 /* Read stream ID */
482 // TODO: it hasn't been checked that the stream_id field exists and
483 // is an unsigned
484 // integer
485 IntegerDefinition streamIDDef = (IntegerDefinition) packetHeaderDef.lookupDefinition("stream_id"); //$NON-NLS-1$
486 assert (streamIDDef != null);
487
488 long streamID = streamIDDef.getValue();
489
490 /* Get the stream to which this trace file belongs to */
491 Stream stream = streams.get(streamID);
492
493 /* Create the stream input */
494 StreamInput streamInput = new StreamInput(stream,
495 streamFileChannels[index], streamFile);
496
497 /* Add a reference to the streamInput in the stream */
498 stream.addInput(streamInput);
499 } else {
500 /* No packet header, we suppose there is only one stream */
501 Stream stream = streams.get(null);
502
503 /* Create the stream input */
504 StreamInput streamInput = new StreamInput(stream,
505 streamFileChannels[index], streamFile);
506
507 /* Add a reference to the streamInput in the stream */
508 stream.addInput(streamInput);
509 }
510 }
511
512 /**
513 * Looks up a definition from packet
514 *
515 * @param lookupPath
516 * String
517 * @return Definition
518 * @see org.eclipse.linuxtools.ctf.core.event.types.IDefinitionScope#lookupDefinition(String)
519 */
520 @Override
521 public Definition lookupDefinition(String lookupPath) {
522 if (lookupPath.equals("trace.packet.header")) { //$NON-NLS-1$
523 return packetHeaderDef;
524 }
525 return null;
526 }
527
528 /**
529 * Adds a new stream to the trace.
530 *
531 * @param stream
532 * A stream object.
533 *
534 * @throws ParseException
535 */
536 public void addStream(Stream stream) throws ParseException {
537
538 /*
539 * If there is already a stream without id (the null key), it must be
540 * the only one
541 */
542 if (streams.get(null) != null) {
543 throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$
544 }
545
546 /*
547 * If the stream we try to add has the null key, it must be the only
548 * one. Thus, if the streams container is not empty, it is not valid.
549 */
550 if ((stream.getId() == null) && (streams.size() != 0)) {
551 throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$
552 }
553
554 /* If a stream with the same ID already exists, it is not valid. */
555 if (streams.get(stream.getId()) != null) {
556 throw new ParseException("Stream id already exists"); //$NON-NLS-1$
557 }
558
559 /* It should be ok now. */
560 streams.put(stream.getId(), stream);
561 }
562
563 public HashMap<String, String> getEnvironment() {
564 return environment;
565 }
566
567 public String lookupEnvironment(String key) {
568 return environment.get(key);
569 }
570
571 public void addEnvironmentVar(String varName, String varValue) {
572 environment.put(varName, varValue);
573 }
574
575 public void addClock(String nameValue, CTFClock ctfClock) {
576 clocks.put(nameValue, ctfClock);
577 }
578
579 public CTFClock getClock(String name) {
580 return clocks.get(name);
581 }
582
583 public CTFClock getClock() {
584 if (clocks.size() == 1) {
585 String key = (String) clocks.keySet().toArray()[0];
586 return clocks.get(key);
587 }
588 return null;
589 }
590
591 public long getOffset() {
592 if (getClock() == null) {
593 return 0;
594 }
595 return (Long) getClock().getProperty("offset"); //$NON-NLS-1$
596 }
597
598 }
599
600 class MetadataFileFilter implements FileFilter {
601
602 @Override
603 public boolean accept(File pathname) {
604 if (pathname.isDirectory()) {
605 return false;
606 }
607 if (pathname.isHidden()) {
608 return false;
609 }
610 if (pathname.getName().equals("metadata")) { //$NON-NLS-1$
611 return false;
612 }
613 return true;
614 }
615
616 }
617
618 class MetadataComparator implements Comparator<File> {
619
620 @Override
621 public int compare(File o1, File o2) {
622 return o1.getName().compareTo(o2.getName());
623 }
624 }
This page took 0.054086 seconds and 6 git commands to generate.