1 /*******************************************************************************
2 * Copyright (c) 2011-2012 Ericsson, Ecole Polytechnique de Montreal and others
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
9 * Contributors: Matthew Khouzam - Initial API and implementation
10 * Contributors: Alexandre Montplaisir - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.linuxtools
.ctf
.core
.trace
;
16 import java
.io
.FileFilter
;
17 import java
.io
.FileInputStream
;
18 import java
.io
.IOException
;
19 import java
.io
.Serializable
;
20 import java
.nio
.ByteOrder
;
21 import java
.nio
.MappedByteBuffer
;
22 import java
.nio
.channels
.FileChannel
;
23 import java
.nio
.channels
.FileChannel
.MapMode
;
24 import java
.util
.Arrays
;
25 import java
.util
.Comparator
;
26 import java
.util
.HashMap
;
27 import java
.util
.Iterator
;
28 import java
.util
.LinkedList
;
29 import java
.util
.List
;
31 import java
.util
.Map
.Entry
;
33 import java
.util
.UUID
;
35 import org
.eclipse
.linuxtools
.ctf
.core
.event
.CTFClock
;
36 import org
.eclipse
.linuxtools
.ctf
.core
.event
.EventDeclaration
;
37 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.ArrayDefinition
;
38 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.Definition
;
39 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.IDefinitionScope
;
40 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.IntegerDefinition
;
41 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StructDeclaration
;
42 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StructDefinition
;
43 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.Activator
;
44 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.event
.io
.BitBuffer
;
45 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.event
.metadata
.exceptions
.ParseException
;
46 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.trace
.Stream
;
47 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.trace
.StreamInput
;
50 * <b><u>CTFTrace</u></b>
52 * Represents a trace on the filesystem. It is responsible of parsing the
53 * metadata, creating declarations data structures, indexing the event packets
54 * (in other words, all the work that can be shared between readers), but the
55 * actual reading of events is left to TraceReader.
57 * @author Matthew Khouzam
58 * @version $Revision: 1.0 $
60 public class CTFTrace
implements IDefinitionScope
{
62 // ------------------------------------------------------------------------
64 // ------------------------------------------------------------------------
69 * @see java.lang.Object#toString()
71 @SuppressWarnings("nls")
73 public String
toString() {
74 /* Only for debugging, shouldn't be externalized */
75 return "CTFTrace [path=" + path
+ ", major=" + major
+ ", minor="
76 + minor
+ ", uuid=" + uuid
+ "]";
80 * The trace directory on the filesystem.
82 private final File path
;
85 * The metadata parsing object.
87 private final Metadata metadata
;
90 * Major CTF version number
95 * Minor CTF version number
107 private ByteOrder byteOrder
;
110 * Packet header structure declaration
112 private StructDeclaration packetHeaderDecl
= null;
115 * Packet header structure definition
117 * This is only used when opening the trace files, to read the first packet
118 * header and see if they are valid trace files.
120 private StructDefinition packetHeaderDef
;
123 * Collection of streams contained in the trace.
125 private final HashMap
<Long
, Stream
> streams
;
128 * Collection of environment variables set by the tracer
130 private final HashMap
<String
, String
> environment
;
133 * Collection of all the clocks in a system.
135 private final HashMap
<String
, CTFClock
> clocks
;
137 /** FileChannels to the streams */
138 private final List
<FileChannel
> streamFileChannels
;
140 /** Handlers for the metadata files */
141 private final static FileFilter metadataFileFilter
= new MetadataFileFilter();
142 private final static Comparator
<File
> metadataComparator
= new MetadataComparator(); // $codepro.audit.disable fieldJavadoc
144 /** map of all the event types */
145 private final HashMap
<Long
, EventDeclaration
> events
;
147 // ------------------------------------------------------------------------
149 // ------------------------------------------------------------------------
155 * Filesystem path of the trace directory.
156 * @throws IOException
158 public CTFTrace(String path
) throws CTFReaderException
{
159 this(new File(path
));
167 * Filesystem path of the trace directory.
168 * @throws CTFReaderException
170 public CTFTrace(File path
) throws CTFReaderException
{
172 this.metadata
= new Metadata(this);
174 /* Set up the internal containers for this trace */
175 streams
= new HashMap
<Long
, Stream
>();
176 environment
= new HashMap
<String
, String
>();
177 clocks
= new HashMap
<String
, CTFClock
>();
178 streamFileChannels
= new LinkedList
<FileChannel
>();
180 if (!this.path
.isDirectory()) {
181 throw new CTFReaderException("Path must be a valid directory"); //$NON-NLS-1$
184 /* Open and parse the metadata file */
187 if (Activator
.getDefault() != null) {
188 Activator
.getDefault().log(metadata
.toString());
191 /* Open all the trace files */
192 /* Create the definitions needed to read things from the files */
193 if (packetHeaderDecl
!= null) {
194 packetHeaderDef
= packetHeaderDecl
.createDefinition(this,
195 "packet.header"); //$NON-NLS-1$
198 /* List files not called metadata and not hidden. */
199 File
[] files
= path
.listFiles(metadataFileFilter
);
200 Arrays
.sort(files
, metadataComparator
);
202 /* Try to open each file */
203 for (File streamFile
: files
) {
204 openStreamInput(streamFile
);
206 events
= new HashMap
<Long
, EventDeclaration
>();
207 /* Create their index */
208 for (Map
.Entry
<Long
, Stream
> stream
: streams
.entrySet()) {
209 Set
<StreamInput
> inputs
= stream
.getValue().getStreamInputs();
210 for (StreamInput s
: inputs
) {
214 Iterator
<Entry
<Long
, EventDeclaration
>> it
= s
.getStream()
215 .getEvents().entrySet().iterator();
216 while (it
.hasNext()) {
217 Map
.Entry
<Long
, EventDeclaration
> pairs
= it
.next();
218 Long eventNum
= pairs
.getKey();
219 EventDeclaration eventDec
= pairs
.getValue();
220 events
.put(eventNum
, eventDec
);
232 protected void finalize() {
233 /* If this trace gets closed, release the descriptors to the streams */
234 for (FileChannel fc
: streamFileChannels
) {
238 } catch (IOException e
) {
239 // do nothing it's ok, we tried to close it.
245 } catch (Throwable e
) {
246 // TODO Auto-generated catch block
250 // ------------------------------------------------------------------------
251 // Getters/Setters/Predicates
252 // ------------------------------------------------------------------------
255 * Get an event by it's ID
258 * the ID of the event
259 * @return the event declaration
261 public EventDeclaration
getEventType(long id
) {
262 return events
.get(id
);
266 * Get the number of events in the trace so far.
268 * @return the number of events in the trace
270 public int getNbEventTypes() {
271 return events
.size();
275 * Method getStream gets the stream for a given id
278 * Long the id of the stream
279 * @return Stream the stream that we need
281 public Stream
getStream(Long id
) {
282 return streams
.get(id
);
286 * Method nbStreams gets the number of available streams
288 * @return int the number of streams
290 public int nbStreams() {
291 return streams
.size();
295 * Method setMajor sets the major version of the trace (DO NOT USE)
298 * long the major version
300 public void setMajor(long major
) {
305 * Method setMinor sets the minor version of the trace (DO NOT USE)
308 * long the minor version
310 public void setMinor(long minor
) {
315 * Method setUUID sets the UUID of a trace
320 public void setUUID(UUID uuid
) {
325 * Method setByteOrder sets the byte order
328 * ByteOrder of the trace, can be little-endian or big-endian
330 public void setByteOrder(ByteOrder byteOrder
) {
331 this.byteOrder
= byteOrder
;
335 * Method setPacketHeader sets the packet header of a trace (DO NOT USE)
337 * @param packetHeader
338 * StructDeclaration the header in structdeclaration form
340 public void setPacketHeader(StructDeclaration packetHeader
) {
341 this.packetHeaderDecl
= packetHeader
;
345 * Method majortIsSet is the major version number set?
347 * @return boolean is the major set?
349 public boolean majortIsSet() {
350 return major
!= null;
354 * Method minorIsSet. is the minor version number set?
356 * @return boolean is the minor set?
358 public boolean minorIsSet() {
359 return minor
!= null;
363 * Method UUIDIsSet is the UUID set?
365 * @return boolean is the UUID set?
367 public boolean UUIDIsSet() {
372 * Method byteOrderIsSet is the byteorder set?
374 * @return boolean is the byteorder set?
376 public boolean byteOrderIsSet() {
377 return byteOrder
!= null;
381 * Method packetHeaderIsSet is the packet header set?
383 * @return boolean is the packet header set?
385 public boolean packetHeaderIsSet() {
386 return packetHeaderDecl
!= null;
390 * Method getUUID gets the trace UUID
392 * @return UUID gets the trace UUID
394 public UUID
getUUID() {
399 * Method getMajor gets the trace major version
401 * @return long gets the trace major version
403 public long getMajor() {
408 * Method getMinor gets the trace minor version
410 * @return long gets the trace minor version
412 public long getMinor() {
417 * Method getByteOrder gets the trace byte order
419 * @return ByteOrder gets the trace byte order
421 public ByteOrder
getByteOrder() {
426 * Method getPacketHeader gets the trace packet header
428 * @return StructDeclaration gets the trace packet header
430 public StructDeclaration
getPacketHeader() {
431 return packetHeaderDecl
;
435 * Method getTraceDirectory gets the trace directory
437 * @return File the path in "File" format.
439 public File
getTraceDirectory() {
444 * Method getStreams get all the streams in a map format.
446 * @return Map<Long,Stream> a map of all the streams.
448 public Map
<Long
, Stream
> getStreams() {
453 * Method getPath gets the path of the trace directory
455 * @return String the path of the trace directory, in string format.
456 * @see java.io.File#getPath()
459 public String
getPath() {
460 return path
.getPath();
463 // ------------------------------------------------------------------------
465 // ------------------------------------------------------------------------
468 * Tries to open the given file, reads the first packet header of the file
469 * and check its validity.
472 * A trace file in the trace directory.
474 * Which index in the class' streamFileChannel array this file
476 * @throws CTFReaderException
478 private void openStreamInput(File streamFile
) throws CTFReaderException
{
479 MappedByteBuffer byteBuffer
;
480 BitBuffer streamBitBuffer
;
484 if (!streamFile
.canRead()) {
485 throw new CTFReaderException("Unreadable file : " //$NON-NLS-1$
486 + streamFile
.getPath());
490 /* Open the file and get the FileChannel */
491 fc
= new FileInputStream(streamFile
).getChannel();
492 streamFileChannels
.add(fc
);
494 /* Map one memory page of 4 kiB */
495 byteBuffer
= fc
.map(MapMode
.READ_ONLY
, 0, 4096);
496 } catch (IOException e
) {
497 /* Shouldn't happen at this stage if every other check passed */
498 throw new CTFReaderException();
501 /* Create a BitBuffer with this mapping and the trace byte order */
502 streamBitBuffer
= new BitBuffer(byteBuffer
, this.getByteOrder());
504 if (packetHeaderDef
!= null) {
505 /* Read the packet header */
506 packetHeaderDef
.read(streamBitBuffer
);
508 /* Check the magic number */
509 IntegerDefinition magicDef
= (IntegerDefinition
) packetHeaderDef
510 .lookupDefinition("magic"); //$NON-NLS-1$
511 int magic
= (int) magicDef
.getValue();
512 if (magic
!= Utils
.CTF_MAGIC
) {
513 throw new CTFReaderException("CTF magic mismatch"); //$NON-NLS-1$
517 ArrayDefinition uuidDef
= (ArrayDefinition
) packetHeaderDef
518 .lookupDefinition("uuid"); //$NON-NLS-1$
519 assert ((uuidDef
!= null) && (uuidDef
.getDeclaration().getLength() == Utils
.UUID_LEN
));
520 if (uuidDef
!= null) {
521 byte[] uuidArray
= new byte[Utils
.UUID_LEN
];
523 for (int i
= 0; i
< Utils
.UUID_LEN
; i
++) {
524 IntegerDefinition uuidByteDef
= (IntegerDefinition
) uuidDef
526 uuidArray
[i
] = (byte) uuidByteDef
.getValue();
529 UUID otheruuid
= Utils
.makeUUID(uuidArray
);
531 if (!this.uuid
.equals(otheruuid
)) {
532 throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$
537 // TODO: it hasn't been checked that the stream_id field exists and
540 IntegerDefinition streamIDDef
= (IntegerDefinition
) packetHeaderDef
541 .lookupDefinition("stream_id"); //$NON-NLS-1$
542 assert (streamIDDef
!= null);
544 long streamID
= streamIDDef
.getValue();
546 /* Get the stream to which this trace file belongs to */
547 stream
= streams
.get(streamID
);
549 /* No packet header, we suppose there is only one stream */
550 stream
= streams
.get(null);
553 /* Create the stream input */
554 StreamInput streamInput
= new StreamInput(stream
, fc
, streamFile
);
556 /* Add a reference to the streamInput in the stream */
557 stream
.addInput(streamInput
);
561 * Looks up a definition from packet
566 * @see org.eclipse.linuxtools.ctf.core.event.types.IDefinitionScope#lookupDefinition(String)
569 public Definition
lookupDefinition(String lookupPath
) {
570 if (lookupPath
.equals("trace.packet.header")) { //$NON-NLS-1$
571 return packetHeaderDef
;
577 * Adds a new stream to the trace.
582 * @throws ParseException
584 public void addStream(Stream stream
) throws ParseException
{
587 * If there is already a stream without id (the null key), it must be
590 if (streams
.get(null) != null) {
591 throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$
595 * If the stream we try to add has the null key, it must be the only
596 * one. Thus, if the streams container is not empty, it is not valid.
598 if ((stream
.getId() == null) && (streams
.size() != 0)) {
599 throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$
602 /* If a stream with the same ID already exists, it is not valid. */
603 if (streams
.get(stream
.getId()) != null) {
604 throw new ParseException("Stream id already exists"); //$NON-NLS-1$
607 /* It should be ok now. */
608 streams
.put(stream
.getId(), stream
);
611 public HashMap
<String
, String
> getEnvironment() {
615 public String
lookupEnvironment(String key
) {
616 return environment
.get(key
);
619 public void addEnvironmentVar(String varName
, String varValue
) {
620 environment
.put(varName
, varValue
);
623 public void addClock(String nameValue
, CTFClock ctfClock
) {
624 clocks
.put(nameValue
, ctfClock
);
627 public CTFClock
getClock(String name
) {
628 return clocks
.get(name
);
631 public CTFClock
getClock() {
632 if (clocks
.size() == 1) {
633 String key
= (String
) clocks
.keySet().toArray()[0];
634 return clocks
.get(key
);
639 public long getOffset() {
640 if (getClock() == null) {
643 return (Long
) getClock().getProperty("offset"); //$NON-NLS-1$
648 class MetadataFileFilter
implements FileFilter
{
651 public boolean accept(File pathname
) {
652 if (pathname
.isDirectory()) {
655 if (pathname
.isHidden()) {
658 if (pathname
.getName().equals("metadata")) { //$NON-NLS-1$
666 class MetadataComparator
implements Comparator
<File
>, Serializable
{
669 public int compare(File o1
, File o2
) {
670 return o1
.getName().compareTo(o2
.getName());