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
.EventDefinition
;
38 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.ArrayDefinition
;
39 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.Definition
;
40 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.IDefinitionScope
;
41 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.IntegerDefinition
;
42 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StructDeclaration
;
43 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StructDefinition
;
44 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.Activator
;
45 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.event
.io
.BitBuffer
;
46 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.event
.metadata
.exceptions
.ParseException
;
47 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.trace
.Stream
;
48 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.trace
.StreamInput
;
49 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.trace
.StreamInputPacketIndex
;
52 * A CTF trace on the file system.
54 * Represents a trace on the filesystem. It is responsible of parsing the
55 * metadata, creating declarations data structures, indexing the event packets
56 * (in other words, all the work that can be shared between readers), but the
57 * actual reading of events is left to TraceReader.
59 * @author Matthew Khouzam
60 * @version $Revision: 1.0 $
62 public class CTFTrace
implements IDefinitionScope
{
64 // ------------------------------------------------------------------------
66 // ------------------------------------------------------------------------
71 * @see java.lang.Object#toString()
73 @SuppressWarnings("nls")
75 public String
toString() {
76 /* Only for debugging, shouldn't be externalized */
77 return "CTFTrace [path=" + path
+ ", major=" + major
+ ", minor="
78 + minor
+ ", uuid=" + uuid
+ "]";
82 * The trace directory on the filesystem.
84 private final File path
;
87 * The metadata parsing object.
89 private final Metadata metadata
;
92 * Major CTF version number
97 * Minor CTF version number
109 private ByteOrder byteOrder
;
112 * Packet header structure declaration
114 private StructDeclaration packetHeaderDecl
= null;
117 * Packet header structure definition
119 * This is only used when opening the trace files, to read the first packet
120 * header and see if they are valid trace files.
122 private StructDefinition packetHeaderDef
;
125 * Collection of streams contained in the trace.
127 private final HashMap
<Long
, Stream
> streams
;
130 * Collection of environment variables set by the tracer
132 private final HashMap
<String
, String
> environment
;
135 * Collection of all the clocks in a system.
137 private final HashMap
<String
, CTFClock
> clocks
;
139 /** FileChannels to the streams */
140 private final List
<FileChannel
> streamFileChannels
;
142 /** Handlers for the metadata files */
143 private final static FileFilter metadataFileFilter
= new MetadataFileFilter();
144 private final static Comparator
<File
> metadataComparator
= new MetadataComparator(); // $codepro.audit.disable
147 /** map of all the event types */
148 private final HashMap
<Long
,HashMap
<Long
, EventDeclaration
>> eventDecs
;
149 /** map of all the event types */
150 private final HashMap
<StreamInput
,HashMap
<Long
, EventDefinition
>> eventDefs
;
151 /** map of all the indexes */
152 private final HashMap
<StreamInput
, StreamInputPacketIndex
> indexes
;
156 // ------------------------------------------------------------------------
158 // ------------------------------------------------------------------------
164 * Filesystem path of the trace directory
165 * @throws CTFReaderException
166 * If no CTF trace was found at the path
168 public CTFTrace(String path
) throws CTFReaderException
{
169 this(new File(path
));
177 * Filesystem path of the trace directory.
178 * @throws CTFReaderException
179 * If no CTF trace was found at the path
181 public CTFTrace(File path
) throws CTFReaderException
{
183 this.metadata
= new Metadata(this);
185 /* Set up the internal containers for this trace */
186 streams
= new HashMap
<Long
, Stream
>();
187 environment
= new HashMap
<String
, String
>();
188 clocks
= new HashMap
<String
, CTFClock
>();
189 streamFileChannels
= new LinkedList
<FileChannel
>();
190 eventDecs
= new HashMap
<Long
, HashMap
<Long
, EventDeclaration
>>();
191 eventDefs
= new HashMap
<StreamInput
, HashMap
<Long
, EventDefinition
>>();
193 if (!this.path
.isDirectory()) {
194 throw new CTFReaderException("Path must be a valid directory"); //$NON-NLS-1$
197 /* Open and parse the metadata file */
200 if (Activator
.getDefault() != null) {
201 Activator
.getDefault().log(metadata
.toString());
204 /* Open all the trace files */
205 /* Create the definitions needed to read things from the files */
206 if (packetHeaderDecl
!= null) {
207 packetHeaderDef
= packetHeaderDecl
.createDefinition(this,
208 "packet.header"); //$NON-NLS-1$
211 /* List files not called metadata and not hidden. */
212 File
[] files
= path
.listFiles(metadataFileFilter
);
213 Arrays
.sort(files
, metadataComparator
);
214 indexes
= new HashMap
<StreamInput
, StreamInputPacketIndex
>();
215 /* Try to open each file */
216 for (File streamFile
: files
) {
217 openStreamInput(streamFile
);
220 /* Create their index */
221 for (Map
.Entry
<Long
, Stream
> stream
: streams
.entrySet()) {
222 Set
<StreamInput
> inputs
= stream
.getValue().getStreamInputs();
223 for (StreamInput s
: inputs
) {
227 Iterator
<Entry
<Long
, EventDeclaration
>> it
= s
.getStream()
228 .getEvents().entrySet().iterator();
229 while (it
.hasNext()) {
230 Map
.Entry
<Long
, EventDeclaration
> pairs
= it
.next();
231 Long eventNum
= pairs
.getKey();
232 EventDeclaration eventDec
= pairs
.getValue();
233 getEvents(s
.getStream().getId()).put(eventNum
, eventDec
);
245 protected void finalize() throws Throwable
{
246 /* If this trace gets closed, release the descriptors to the streams */
247 for (FileChannel fc
: streamFileChannels
) {
251 } catch (IOException e
) {
252 // do nothing it's ok, we tried to close it.
260 // ------------------------------------------------------------------------
261 // Getters/Setters/Predicates
262 // ------------------------------------------------------------------------
265 * Gets an event declaration hash map for a given streamID
268 * The ID of the stream from which to read
269 * @return The Hash map with the event declarations
271 public HashMap
<Long
, EventDeclaration
> getEvents(Long streamId
) {
272 return eventDecs
.get(streamId
);
276 * Gets an index for a given StreamInput
277 * @param id the StreamInput
280 public StreamInputPacketIndex
getIndex(StreamInput id
){
281 if(! indexes
.containsKey(id
)){
282 indexes
.put(id
, new StreamInputPacketIndex());
284 return indexes
.get(id
);
288 * Gets an event Declaration hashmap for a given StreamInput
289 * @param id the StreamInput
290 * @return the hashmap with the event definitions
292 public HashMap
<Long
, EventDefinition
> getEventDefs(StreamInput id
) {
293 if(! eventDefs
.containsKey(id
)){
294 eventDefs
.put(id
, new HashMap
<Long
, EventDefinition
>());
296 return eventDefs
.get(id
);
300 * Get an event by it's ID
303 * The ID of the stream from which to read
305 * the ID of the event
306 * @return the event declaration
308 public EventDeclaration
getEventType(long streamId
, long id
) {
309 return getEvents(streamId
).get(id
);
313 * Method getStream gets the stream for a given id
316 * Long the id of the stream
317 * @return Stream the stream that we need
319 public Stream
getStream(Long id
) {
320 return streams
.get(id
);
324 * Method nbStreams gets the number of available streams
326 * @return int the number of streams
328 public int nbStreams() {
329 return streams
.size();
333 * Method setMajor sets the major version of the trace (DO NOT USE)
336 * long the major version
338 public void setMajor(long major
) {
343 * Method setMinor sets the minor version of the trace (DO NOT USE)
346 * long the minor version
348 public void setMinor(long minor
) {
353 * Method setUUID sets the UUID of a trace
358 public void setUUID(UUID uuid
) {
363 * Method setByteOrder sets the byte order
366 * ByteOrder of the trace, can be little-endian or big-endian
368 public void setByteOrder(ByteOrder byteOrder
) {
369 this.byteOrder
= byteOrder
;
373 * Method setPacketHeader sets the packet header of a trace (DO NOT USE)
375 * @param packetHeader
376 * StructDeclaration the header in structdeclaration form
378 public void setPacketHeader(StructDeclaration packetHeader
) {
379 this.packetHeaderDecl
= packetHeader
;
383 * Method majortIsSet is the major version number set?
385 * @return boolean is the major set?
387 public boolean majortIsSet() {
388 return major
!= null;
392 * Method minorIsSet. is the minor version number set?
394 * @return boolean is the minor set?
396 public boolean minorIsSet() {
397 return minor
!= null;
401 * Method UUIDIsSet is the UUID set?
403 * @return boolean is the UUID set?
405 public boolean UUIDIsSet() {
410 * Method byteOrderIsSet is the byteorder set?
412 * @return boolean is the byteorder set?
414 public boolean byteOrderIsSet() {
415 return byteOrder
!= null;
419 * Method packetHeaderIsSet is the packet header set?
421 * @return boolean is the packet header set?
423 public boolean packetHeaderIsSet() {
424 return packetHeaderDecl
!= null;
428 * Method getUUID gets the trace UUID
430 * @return UUID gets the trace UUID
432 public UUID
getUUID() {
437 * Method getMajor gets the trace major version
439 * @return long gets the trace major version
441 public long getMajor() {
446 * Method getMinor gets the trace minor version
448 * @return long gets the trace minor version
450 public long getMinor() {
455 * Method getByteOrder gets the trace byte order
457 * @return ByteOrder gets the trace byte order
459 public ByteOrder
getByteOrder() {
464 * Method getPacketHeader gets the trace packet header
466 * @return StructDeclaration gets the trace packet header
468 public StructDeclaration
getPacketHeader() {
469 return packetHeaderDecl
;
473 * Method getTraceDirectory gets the trace directory
475 * @return File the path in "File" format.
477 public File
getTraceDirectory() {
482 * Method getStreams get all the streams in a map format.
484 * @return Map<Long,Stream> a map of all the streams.
486 public Map
<Long
, Stream
> getStreams() {
491 * Method getPath gets the path of the trace directory
493 * @return String the path of the trace directory, in string format.
494 * @see java.io.File#getPath()
497 public String
getPath() {
498 return path
.getPath();
501 // ------------------------------------------------------------------------
503 // ------------------------------------------------------------------------
506 * Tries to open the given file, reads the first packet header of the file
507 * and check its validity.
510 * A trace file in the trace directory.
512 * Which index in the class' streamFileChannel array this file
514 * @throws CTFReaderException
516 private void openStreamInput(File streamFile
) throws CTFReaderException
{
517 MappedByteBuffer byteBuffer
;
518 BitBuffer streamBitBuffer
;
522 if (!streamFile
.canRead()) {
523 throw new CTFReaderException("Unreadable file : " //$NON-NLS-1$
524 + streamFile
.getPath());
528 /* Open the file and get the FileChannel */
529 fc
= new FileInputStream(streamFile
).getChannel();
530 streamFileChannels
.add(fc
);
532 /* Map one memory page of 4 kiB */
533 byteBuffer
= fc
.map(MapMode
.READ_ONLY
, 0, 4096);
534 } catch (IOException e
) {
535 /* Shouldn't happen at this stage if every other check passed */
536 throw new CTFReaderException();
539 /* Create a BitBuffer with this mapping and the trace byte order */
540 streamBitBuffer
= new BitBuffer(byteBuffer
, this.getByteOrder());
542 if (packetHeaderDef
!= null) {
543 /* Read the packet header */
544 packetHeaderDef
.read(streamBitBuffer
);
546 /* Check the magic number */
547 IntegerDefinition magicDef
= (IntegerDefinition
) packetHeaderDef
548 .lookupDefinition("magic"); //$NON-NLS-1$
549 int magic
= (int) magicDef
.getValue();
550 if (magic
!= Utils
.CTF_MAGIC
) {
551 throw new CTFReaderException("CTF magic mismatch"); //$NON-NLS-1$
555 ArrayDefinition uuidDef
= (ArrayDefinition
) packetHeaderDef
556 .lookupDefinition("uuid"); //$NON-NLS-1$
557 if (uuidDef
!= null) {
558 byte[] uuidArray
= new byte[Utils
.UUID_LEN
];
560 for (int i
= 0; i
< Utils
.UUID_LEN
; i
++) {
561 IntegerDefinition uuidByteDef
= (IntegerDefinition
) uuidDef
563 uuidArray
[i
] = (byte) uuidByteDef
.getValue();
566 UUID otheruuid
= Utils
.makeUUID(uuidArray
);
568 if (!this.uuid
.equals(otheruuid
)) {
569 throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$
574 // TODO: it hasn't been checked that the stream_id field exists and
577 IntegerDefinition streamIDDef
= (IntegerDefinition
) packetHeaderDef
578 .lookupDefinition("stream_id"); //$NON-NLS-1$
579 assert (streamIDDef
!= null);
581 long streamID
= streamIDDef
.getValue();
583 /* Get the stream to which this trace file belongs to */
584 stream
= streams
.get(streamID
);
586 /* No packet header, we suppose there is only one stream */
587 stream
= streams
.get(null);
590 /* Create the stream input */
591 StreamInput streamInput
= new StreamInput(stream
, fc
, streamFile
);
593 /* Add a reference to the streamInput in the stream */
594 stream
.addInput(streamInput
);
598 * Looks up a definition from packet
603 * @see org.eclipse.linuxtools.ctf.core.event.types.IDefinitionScope#lookupDefinition(String)
606 public Definition
lookupDefinition(String lookupPath
) {
607 if (lookupPath
.equals("trace.packet.header")) { //$NON-NLS-1$
608 return packetHeaderDef
;
614 * Adds a new stream to the trace.
618 * @throws ParseException
619 * If there was some problem reading the metadata
621 public void addStream(Stream stream
) throws ParseException
{
624 * If there is already a stream without id (the null key), it must be
627 if (streams
.get(null) != null) {
628 throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$
632 * If the stream we try to add has the null key, it must be the onl * one. Thus, if the streams container is not empty, it is not valid.
634 if ((stream
.getId() == null) && (streams
.size() != 0)) {
635 throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$
638 /* If a stream with the same ID already exists, it is not valid. */
639 if (streams
.get(stream
.getId()) != null) {
640 throw new ParseException("Stream id already exists"); //$NON-NLS-1$
643 /* It should be ok now. */
644 streams
.put(stream
.getId(), stream
);
645 eventDecs
.put(stream
.getId(), new HashMap
<Long
,EventDeclaration
>());
649 * gets the Environment variables from the trace metadata (See CTF spec)
650 * @return the environment variables in a hashmap form (key value)
652 public HashMap
<String
, String
> getEnvironment() {
657 * Look up a specific environment variable
658 * @param key the key to look for
659 * @return the value of the variable, can be null.
661 public String
lookupEnvironment(String key
) {
662 return environment
.get(key
);
666 * Add a variable to the environment variables
667 * @param varName the name of the variable
668 * @param varValue the value of the variable
670 public void addEnvironmentVar(String varName
, String varValue
) {
671 environment
.put(varName
, varValue
);
675 * Add a clock to the clock list
676 * @param nameValue the name of the clock (full name with scope)
677 * @param ctfClock the clock
679 public void addClock(String nameValue
, CTFClock ctfClock
) {
680 clocks
.put(nameValue
, ctfClock
);
684 * gets the clock with a specific name
685 * @param name the name of the clock.
688 public CTFClock
getClock(String name
) {
689 return clocks
.get(name
);
692 private CTFClock singleClock
;
693 private long singleOffset
;
696 * gets the clock if there is only one. (this is 100% of the use cases as of June 2012)
699 public final CTFClock
getClock() {
700 if (clocks
.size() == 1) {
701 if (singleClock
== null) {
702 singleClock
= clocks
.get(clocks
.keySet().toArray()[0]);
703 singleOffset
= (Long
) getClock().getProperty("offset"); //$NON-NLS-1$
711 * gets the time offset of a clock with respect to UTC in nanoseconds
712 * @return the time offset of a clock with respect to UTC in nanoseconds
714 public final long getOffset() {
715 if (getClock() == null) {
722 * Does a given stream contain any events?
723 * @param id the stream ID
724 * @return true if the stream has events.
726 public boolean hasEvents(Long id
){
727 return eventDecs
.containsKey(id
);
731 * Add an event declaration map to the events map.
732 * @param id the id of a stream
733 * @return the hashmap containing events.
735 public HashMap
<Long
, EventDeclaration
> createEvents(Long id
){
736 HashMap
<Long
, EventDeclaration
> value
= eventDecs
.get(id
);
737 if( value
== null ) {
738 value
= new HashMap
<Long
, EventDeclaration
>();
739 eventDecs
.put(id
, value
);
746 class MetadataFileFilter
implements FileFilter
{
749 public boolean accept(File pathname
) {
750 if (pathname
.isDirectory()) {
753 if (pathname
.isHidden()) {
756 if (pathname
.getName().equals("metadata")) { //$NON-NLS-1$
764 class MetadataComparator
implements Comparator
<File
>, Serializable
{
766 private static final long serialVersionUID
= 1L;
769 public int compare(File o1
, File o2
) {
770 return o1
.getName().compareTo(o2
.getName());