1 /*******************************************************************************
2 * Copyright (c) 2011, 2013 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
10 * Matthew Khouzam - Initial API and implementation
11 * Alexandre Montplaisir - Initial API and implementation
12 * Simon Delisle - Replace LinkedList by TreeSet in callsitesByName attribute
13 *******************************************************************************/
15 package org
.eclipse
.linuxtools
.ctf
.core
.trace
;
18 import java
.io
.FileFilter
;
19 import java
.io
.FileInputStream
;
20 import java
.io
.IOException
;
21 import java
.io
.Serializable
;
22 import java
.nio
.ByteOrder
;
23 import java
.nio
.MappedByteBuffer
;
24 import java
.nio
.channels
.FileChannel
;
25 import java
.nio
.channels
.FileChannel
.MapMode
;
26 import java
.util
.Arrays
;
27 import java
.util
.Comparator
;
28 import java
.util
.HashMap
;
29 import java
.util
.Iterator
;
30 import java
.util
.LinkedList
;
31 import java
.util
.List
;
33 import java
.util
.Map
.Entry
;
35 import java
.util
.TreeSet
;
36 import java
.util
.UUID
;
38 import org
.eclipse
.linuxtools
.ctf
.core
.event
.CTFCallsite
;
39 import org
.eclipse
.linuxtools
.ctf
.core
.event
.CTFClock
;
40 import org
.eclipse
.linuxtools
.ctf
.core
.event
.EventDefinition
;
41 import org
.eclipse
.linuxtools
.ctf
.core
.event
.IEventDeclaration
;
42 import org
.eclipse
.linuxtools
.ctf
.core
.event
.io
.BitBuffer
;
43 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.ArrayDefinition
;
44 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.Definition
;
45 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.IDefinitionScope
;
46 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.IntegerDefinition
;
47 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StructDeclaration
;
48 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StructDefinition
;
49 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.event
.CTFCallsiteComparator
;
50 import org
.eclipse
.linuxtools
.internal
.ctf
.core
.event
.metadata
.exceptions
.ParseException
;
53 * A CTF trace on the file system.
55 * Represents a trace on the filesystem. It is responsible of parsing the
56 * metadata, creating declarations data structures, indexing the event packets
57 * (in other words, all the work that can be shared between readers), but the
58 * actual reading of events is left to TraceReader.
60 * @author Matthew Khouzam
61 * @version $Revision: 1.0 $
63 public class CTFTrace
implements IDefinitionScope
{
65 @SuppressWarnings("nls")
67 public String
toString() {
68 /* Only for debugging, shouldn't be externalized */
69 return "CTFTrace [path=" + path
+ ", major=" + major
+ ", minor="
70 + minor
+ ", uuid=" + uuid
+ "]";
74 * The trace directory on the filesystem.
76 private final File path
;
79 * Major CTF version number
84 * Minor CTF version number
96 private ByteOrder byteOrder
;
99 * Packet header structure declaration
101 private StructDeclaration packetHeaderDecl
= null;
104 * The clock of the trace
106 private CTFClock singleClock
;
109 * Packet header structure definition
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.
114 private StructDefinition packetHeaderDef
;
117 * Collection of streams contained in the trace.
119 private final Map
<Long
, Stream
> streams
= new HashMap
<Long
, Stream
>();
122 * Collection of environment variables set by the tracer
124 private final Map
<String
, String
> environment
= new HashMap
<String
, String
>();
127 * Collection of all the clocks in a system.
129 private final Map
<String
, CTFClock
> clocks
= new HashMap
<String
, CTFClock
>();
131 /** FileInputStreams to the streams */
132 private final List
<FileInputStream
> fileInputStreams
= new LinkedList
<FileInputStream
>();
134 /** Handlers for the metadata files */
135 private static final FileFilter METADATA_FILE_FILTER
= new MetadataFileFilter();
136 private static final Comparator
<File
> METADATA_COMPARATOR
= new MetadataComparator();
138 /** Callsite helpers */
139 private CTFCallsiteComparator ctfCallsiteComparator
= new CTFCallsiteComparator();
141 private Map
<String
, TreeSet
<CTFCallsite
>> callsitesByName
= new HashMap
<String
, TreeSet
<CTFCallsite
>>();
143 /** Callsite helpers */
144 private TreeSet
<CTFCallsite
> callsitesByIP
= new TreeSet
<CTFCallsite
>();
146 // ------------------------------------------------------------------------
148 // ------------------------------------------------------------------------
154 * Filesystem path of the trace directory
155 * @throws CTFReaderException
156 * If no CTF trace was found at the path
158 public CTFTrace(String path
) throws CTFReaderException
{
159 this(new File(path
));
167 * Filesystem path of the trace directory.
168 * @throws CTFReaderException
169 * If no CTF trace was found at the path
171 public CTFTrace(File path
) throws CTFReaderException
{
173 final Metadata metadata
= new Metadata(this);
175 /* Set up the internal containers for this trace */
176 if (!this.path
.exists()) {
177 throw new CTFReaderException("Trace (" + path
.getPath() + ") doesn't exist. Deleted or moved?"); //$NON-NLS-1$ //$NON-NLS-2$
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 /* Open all the trace files */
188 /* Create the definitions needed to read things from the files */
189 if (packetHeaderDecl
!= null) {
190 packetHeaderDef
= packetHeaderDecl
.createDefinition(this, "packet.header"); //$NON-NLS-1$
193 /* List files not called metadata and not hidden. */
194 File
[] files
= path
.listFiles(METADATA_FILE_FILTER
);
195 Arrays
.sort(files
, METADATA_COMPARATOR
);
196 /* Try to open each file */
197 for (File streamFile
: files
) {
198 openStreamInput(streamFile
);
201 /* Create their index */
202 for (Map
.Entry
<Long
, Stream
> stream
: streams
.entrySet()) {
203 Set
<StreamInput
> inputs
= stream
.getValue().getStreamInputs();
204 for (StreamInput s
: inputs
) {
208 Iterator
<Entry
<Long
, IEventDeclaration
>> it
= s
.getStream()
209 .getEvents().entrySet().iterator();
210 while (it
.hasNext()) {
211 Entry
<Long
, IEventDeclaration
> pairs
= it
.next();
212 Long eventNum
= pairs
.getKey();
213 IEventDeclaration eventDec
= pairs
.getValue();
214 getEvents(s
.getStream().getId()).put(eventNum
, eventDec
);
230 public void dispose() {
231 for (FileInputStream fis
: fileInputStreams
) {
235 } catch (IOException e
) {
236 // do nothing it's ok, we tried to close it.
240 // Invoke GC to release MappedByteBuffer objects (Java bug JDK-4724038)
244 // ------------------------------------------------------------------------
245 // Getters/Setters/Predicates
246 // ------------------------------------------------------------------------
249 * Gets an event declaration hash map for a given streamID
252 * The ID of the stream from which to read
253 * @return The Hash map with the event declarations
256 public Map
<Long
, IEventDeclaration
> getEvents(Long streamId
) {
257 return streams
.get(streamId
).getEvents();
261 * Gets an event Declaration hashmap for a given StreamInput
265 * @return an empty hashmap, please see deprecated
267 * @deprecated You should be using
268 * {@link StreamInputReader#getEventDefinitions()} instead.
271 public Map
<Long
, EventDefinition
> getEventDefs(StreamInput id
) {
272 return new HashMap
<Long
, EventDefinition
>();
276 * Get an event by it's ID
279 * The ID of the stream from which to read
281 * the ID of the event
282 * @return the event declaration
285 public IEventDeclaration
getEventType(long streamId
, long id
) {
286 return getEvents(streamId
).get(id
);
290 * Method getStream gets the stream for a given id
293 * Long the id of the stream
294 * @return Stream the stream that we need
297 public Stream
getStream(Long id
) {
298 return streams
.get(id
);
302 * Method nbStreams gets the number of available streams
304 * @return int the number of streams
306 public int nbStreams() {
307 return streams
.size();
311 * Method setMajor sets the major version of the trace (DO NOT USE)
314 * long the major version
316 public void setMajor(long major
) {
321 * Method setMinor sets the minor version of the trace (DO NOT USE)
324 * long the minor version
326 public void setMinor(long minor
) {
331 * Method setUUID sets the UUID of a trace
336 public void setUUID(UUID uuid
) {
341 * Method setByteOrder sets the byte order
344 * ByteOrder of the trace, can be little-endian or big-endian
346 public void setByteOrder(ByteOrder byteOrder
) {
347 this.byteOrder
= byteOrder
;
351 * Method setPacketHeader sets the packet header of a trace (DO NOT USE)
353 * @param packetHeader
354 * StructDeclaration the header in structdeclaration form
356 public void setPacketHeader(StructDeclaration packetHeader
) {
357 this.packetHeaderDecl
= packetHeader
;
361 * Method majorIsSet is the major version number set?
363 * @return boolean is the major set?
366 public boolean majorIsSet() {
367 return major
!= null;
371 * Method minorIsSet. is the minor version number set?
373 * @return boolean is the minor set?
375 public boolean minorIsSet() {
376 return minor
!= null;
380 * Method UUIDIsSet is the UUID set?
382 * @return boolean is the UUID set?
385 public boolean uuidIsSet() {
390 * Method byteOrderIsSet is the byteorder set?
392 * @return boolean is the byteorder set?
394 public boolean byteOrderIsSet() {
395 return byteOrder
!= null;
399 * Method packetHeaderIsSet is the packet header set?
401 * @return boolean is the packet header set?
403 public boolean packetHeaderIsSet() {
404 return packetHeaderDecl
!= null;
408 * Method getUUID gets the trace UUID
410 * @return UUID gets the trace UUID
412 public UUID
getUUID() {
417 * Method getMajor gets the trace major version
419 * @return long gets the trace major version
421 public long getMajor() {
426 * Method getMinor gets the trace minor version
428 * @return long gets the trace minor version
430 public long getMinor() {
435 * Method getByteOrder gets the trace byte order
437 * @return ByteOrder gets the trace byte order
439 public final ByteOrder
getByteOrder() {
444 * Method getPacketHeader gets the trace packet header
446 * @return StructDeclaration gets the trace packet header
448 public StructDeclaration
getPacketHeader() {
449 return packetHeaderDecl
;
453 * Method getTraceDirectory gets the trace directory
455 * @return File the path in "File" format.
457 public File
getTraceDirectory() {
462 * Method getStreams get all the streams in a map format.
464 * @return Map<Long,Stream> a map of all the streams.
466 public Map
<Long
, Stream
> getStreams() {
471 * Method getPath gets the path of the trace directory
473 * @return String the path of the trace directory, in string format.
474 * @see java.io.File#getPath()
477 public String
getPath() {
478 return path
.getPath();
481 // ------------------------------------------------------------------------
483 // ------------------------------------------------------------------------
486 * Tries to open the given file, reads the first packet header of the file
487 * and check its validity.
490 * A trace file in the trace directory.
492 * Which index in the class' streamFileChannel array this file
494 * @throws CTFReaderException
496 private void openStreamInput(File streamFile
) throws CTFReaderException
{
497 MappedByteBuffer byteBuffer
;
498 BitBuffer streamBitBuffer
;
502 if (!streamFile
.canRead()) {
503 throw new CTFReaderException("Unreadable file : " //$NON-NLS-1$
504 + streamFile
.getPath());
508 /* Open the file and get the FileChannel */
509 FileInputStream fis
= new FileInputStream(streamFile
);
510 fileInputStreams
.add(fis
);
511 fc
= fis
.getChannel();
513 /* Map one memory page of 4 kiB */
514 byteBuffer
= fc
.map(MapMode
.READ_ONLY
, 0, (int) Math
.min(fc
.size(), 4096L));
515 } catch (IOException e
) {
516 /* Shouldn't happen at this stage if every other check passed */
517 throw new CTFReaderException(e
);
520 /* Create a BitBuffer with this mapping and the trace byte order */
521 streamBitBuffer
= new BitBuffer(byteBuffer
, this.getByteOrder());
523 if (packetHeaderDef
!= null) {
524 /* Read the packet header */
525 packetHeaderDef
.read(streamBitBuffer
);
527 /* Check the magic number */
528 IntegerDefinition magicDef
= (IntegerDefinition
) packetHeaderDef
529 .lookupDefinition("magic"); //$NON-NLS-1$
530 int magic
= (int) magicDef
.getValue();
531 if (magic
!= Utils
.CTF_MAGIC
) {
532 throw new CTFReaderException("CTF magic mismatch"); //$NON-NLS-1$
536 ArrayDefinition uuidDef
= (ArrayDefinition
) packetHeaderDef
537 .lookupDefinition("uuid"); //$NON-NLS-1$
538 if (uuidDef
!= null) {
539 byte[] uuidArray
= new byte[Utils
.UUID_LEN
];
541 for (int i
= 0; i
< Utils
.UUID_LEN
; i
++) {
542 IntegerDefinition uuidByteDef
= (IntegerDefinition
) uuidDef
544 uuidArray
[i
] = (byte) uuidByteDef
.getValue();
547 UUID otheruuid
= Utils
.makeUUID(uuidArray
);
549 if (!this.uuid
.equals(otheruuid
)) {
550 throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$
554 /* Read the stream ID */
555 Definition streamIDDef
= packetHeaderDef
.lookupDefinition("stream_id"); //$NON-NLS-1$
557 if (streamIDDef
instanceof IntegerDefinition
) { // this doubles as a
559 long streamID
= ((IntegerDefinition
) streamIDDef
).getValue();
560 stream
= streams
.get(streamID
);
562 /* No stream_id in the packet header */
563 stream
= streams
.get(null);
567 /* No packet header, we suppose there is only one stream */
568 stream
= streams
.get(null);
571 if (stream
== null) {
572 throw new CTFReaderException("Unexpected end of stream"); //$NON-NLS-1$
575 /* Create the stream input */
576 StreamInput streamInput
= new StreamInput(stream
, fc
, streamFile
);
578 /* Add a reference to the streamInput in the stream */
579 stream
.addInput(streamInput
);
583 * Looks up a definition from packet
588 * @see org.eclipse.linuxtools.ctf.core.event.types.IDefinitionScope#lookupDefinition(String)
591 public Definition
lookupDefinition(String lookupPath
) {
592 if (lookupPath
.equals("trace.packet.header")) { //$NON-NLS-1$
593 return packetHeaderDef
;
599 * Adds a new stream to the trace.
603 * @throws ParseException
604 * If there was some problem reading the metadata
607 public void addStream(Stream stream
) throws ParseException
{
610 * If there is already a stream without id (the null key), it must be
613 if (streams
.get(null) != null) {
614 throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$
618 * If the stream we try to add has the null key, it must be the only
619 * one. Thus, if the streams container is not empty, it is not valid.
621 if ((stream
.getId() == null) && (streams
.size() != 0)) {
622 throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$
625 /* If a stream with the same ID already exists, it is not valid. */
626 if (streams
.get(stream
.getId()) != null) {
627 throw new ParseException("Stream id already exists"); //$NON-NLS-1$
630 /* This stream is valid and has a unique id. */
631 streams
.put(stream
.getId(), stream
);
635 * Gets the Environment variables from the trace metadata (See CTF spec)
637 * @return the environment variables in a map form (key value)
640 public Map
<String
, String
> getEnvironment() {
645 * Look up a specific environment variable
648 * the key to look for
649 * @return the value of the variable, can be null.
651 public String
lookupEnvironment(String key
) {
652 return environment
.get(key
);
656 * Add a variable to the environment variables
659 * the name of the variable
661 * the value of the variable
663 public void addEnvironmentVar(String varName
, String varValue
) {
664 environment
.put(varName
, varValue
);
668 * Add a clock to the clock list
671 * the name of the clock (full name with scope)
675 public void addClock(String nameValue
, CTFClock ctfClock
) {
676 clocks
.put(nameValue
, ctfClock
);
680 * gets the clock with a specific name
683 * the name of the clock.
686 public CTFClock
getClock(String name
) {
687 return clocks
.get(name
);
691 * gets the clock if there is only one. (this is 100% of the use cases as of
696 public final CTFClock
getClock() {
697 if (clocks
.size() == 1) {
698 singleClock
= clocks
.get(clocks
.keySet().iterator().next());
705 * gets the time offset of a clock with respect to UTC in nanoseconds
707 * @return the time offset of a clock with respect to UTC in nanoseconds
709 public final long getOffset() {
710 if (getClock() == null) {
713 return singleClock
.getClockOffset();
717 * gets the time offset of a clock with respect to UTC in nanoseconds
719 * @return the time offset of a clock with respect to UTC in nanoseconds
721 private double getTimeScale() {
722 if (getClock() == null) {
725 return singleClock
.getClockScale();
729 * Does the trace need to time scale?
731 * @return if the trace is in ns or cycles.
733 private boolean clockNeedsScale() {
734 if (getClock() == null) {
737 return singleClock
.isClockScaled();
741 * the inverse clock for returning to a scale.
743 * @return 1.0 / scale
745 private double getInverseTimeScale() {
746 if (getClock() == null) {
749 return singleClock
.getClockAntiScale();
754 * clock cycles since boot
755 * @return time in nanoseconds UTC offset
758 public long timestampCyclesToNanos(long cycles
) {
759 long retVal
= cycles
+ getOffset();
761 * this fix is since quite often the offset will be > than 53 bits and
762 * therefore the conversion will be lossy
764 if (clockNeedsScale()) {
765 retVal
= (long) (retVal
* getTimeScale());
772 * time in nanoseconds UTC offset
773 * @return clock cycles since boot.
776 public long timestampNanoToCycles(long nanos
) {
779 * this fix is since quite often the offset will be > than 53 bits and
780 * therefore the conversion will be lossy
782 if (clockNeedsScale()) {
783 retVal
= (long) (nanos
* getInverseTimeScale());
787 return retVal
- getOffset();
794 * the event name of the callsite
796 * the name of the callsite function
798 * the ip of the callsite
800 * the filename of the callsite
802 * the line number of the callsite
804 public void addCallsite(String eventName
, String funcName
, long ip
,
805 String fileName
, long lineNumber
) {
806 final CTFCallsite cs
= new CTFCallsite(eventName
, funcName
, ip
,
807 fileName
, lineNumber
);
808 TreeSet
<CTFCallsite
> csl
= callsitesByName
.get(eventName
);
810 csl
= new TreeSet
<CTFCallsite
>(ctfCallsiteComparator
);
811 callsitesByName
.put(eventName
, csl
);
816 callsitesByIP
.add(cs
);
820 * Gets the set of callsites associated to an event name. O(1)
824 * @return the callsite set can be empty
827 public TreeSet
<CTFCallsite
> getCallsiteCandidates(String eventName
) {
828 TreeSet
<CTFCallsite
> retVal
= callsitesByName
.get(eventName
);
829 if (retVal
== null) {
830 retVal
= new TreeSet
<CTFCallsite
>(ctfCallsiteComparator
);
836 * The I'm feeling lucky of getCallsiteCandidates O(1)
840 * @return the first callsite that has that event name, can be null
843 public CTFCallsite
getCallsite(String eventName
) {
844 TreeSet
<CTFCallsite
> callsites
= callsitesByName
.get(eventName
);
845 if (callsites
!= null) {
846 return callsites
.first();
852 * Gets a callsite from the instruction pointer O(log(n))
855 * the instruction pointer to lookup
856 * @return the callsite just before that IP in the list remember the IP is
857 * backwards on X86, can be null if no callsite is before the IP.
860 public CTFCallsite
getCallsite(long ip
) {
861 CTFCallsite cs
= new CTFCallsite(null, null, ip
, null, 0L);
862 return callsitesByIP
.ceiling(cs
);
866 * Gets a callsite using the event name and instruction pointer O(log(n))
869 * the name of the event
871 * the instruction pointer
872 * @return the closest matching callsite, can be null
874 public CTFCallsite
getCallsite(String eventName
, long ip
) {
875 final TreeSet
<CTFCallsite
> candidates
= callsitesByName
.get(eventName
);
876 final CTFCallsite dummyCs
= new CTFCallsite(null, null, ip
, null, -1);
877 final CTFCallsite callsite
= candidates
.ceiling(dummyCs
);
878 if (callsite
== null) {
879 return candidates
.floor(dummyCs
);
885 class MetadataFileFilter
implements FileFilter
{
888 public boolean accept(File pathname
) {
889 if (pathname
.isDirectory()) {
892 if (pathname
.isHidden()) {
895 if (pathname
.getName().equals("metadata")) { //$NON-NLS-1$
903 class MetadataComparator
implements Comparator
<File
>, Serializable
{
905 private static final long serialVersionUID
= 1L;
908 public int compare(File o1
, File o2
) {
909 return o1
.getName().compareTo(o2
.getName());