import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.Serializable;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
-import org.eclipse.linuxtools.ctf.core.CtfCorePlugin;
import org.eclipse.linuxtools.ctf.core.event.CTFClock;
-import org.eclipse.linuxtools.ctf.core.event.io.BitBuffer;
-import org.eclipse.linuxtools.ctf.core.event.metadata.exceptions.ParseException;
+import org.eclipse.linuxtools.ctf.core.event.EventDeclaration;
+import org.eclipse.linuxtools.ctf.core.event.EventDefinition;
import org.eclipse.linuxtools.ctf.core.event.types.ArrayDefinition;
import org.eclipse.linuxtools.ctf.core.event.types.Definition;
import org.eclipse.linuxtools.ctf.core.event.types.IDefinitionScope;
import org.eclipse.linuxtools.ctf.core.event.types.IntegerDefinition;
import org.eclipse.linuxtools.ctf.core.event.types.StructDeclaration;
import org.eclipse.linuxtools.ctf.core.event.types.StructDefinition;
+import org.eclipse.linuxtools.internal.ctf.core.Activator;
+import org.eclipse.linuxtools.internal.ctf.core.event.io.BitBuffer;
+import org.eclipse.linuxtools.internal.ctf.core.event.metadata.exceptions.ParseException;
+import org.eclipse.linuxtools.internal.ctf.core.trace.Stream;
+import org.eclipse.linuxtools.internal.ctf.core.trace.StreamInput;
+import org.eclipse.linuxtools.internal.ctf.core.trace.StreamInputPacketIndex;
/**
* <b><u>CTFTrace</u></b>
/**
* Packet header structure declaration
*/
- private StructDeclaration packetHeaderDecl;
+ private StructDeclaration packetHeaderDecl = null;
/**
* Packet header structure definition
/**
* Collection of streams contained in the trace.
*/
- private final HashMap<Long, Stream> streams = new HashMap<Long, Stream>();
+ private final HashMap<Long, Stream> streams;
/**
* Collection of environment variables set by the tracer
*/
- private final HashMap<String, String> environment = new HashMap<String, String>();
+ private final HashMap<String, String> environment;
/**
* Collection of all the clocks in a system.
*/
- private final HashMap<String, CTFClock> clocks = new HashMap<String, CTFClock>();
+ private final HashMap<String, CTFClock> clocks;
+
+ /** FileChannels to the streams */
+ private final List<FileChannel> streamFileChannels;
+
+ /** Handlers for the metadata files */
+ private final static FileFilter metadataFileFilter = new MetadataFileFilter();
+ private final static Comparator<File> metadataComparator = new MetadataComparator(); // $codepro.audit.disable
+ // fieldJavadoc
+
+ /** map of all the event types */
+ private final HashMap<Long,HashMap<Long, EventDeclaration>> eventDecs;
+ /** map of all the event types */
+ private final HashMap<StreamInput,HashMap<Long, EventDefinition>> eventDefs;
+ /** map of all the indexes */
+ private final HashMap<StreamInput, StreamInputPacketIndex> indexes;
+
// ------------------------------------------------------------------------
* Trace constructor.
*
* @param path
- * Filesystem path of the trace directory.
- * @throws IOException
+ * Filesystem path of the trace directory
+ * @throws CTFReaderException
+ * If no CTF trace was found at the path
*/
public CTFTrace(String path) throws CTFReaderException {
this(new File(path));
+
}
/**
* @param path
* Filesystem path of the trace directory.
* @throws CTFReaderException
+ * If no CTF trace was found at the path
*/
- @SuppressWarnings("unqualified-field-access")
public CTFTrace(File path) throws CTFReaderException {
this.path = path;
+ this.metadata = new Metadata(this);
- metadata = new Metadata(this);
+ /* Set up the internal containers for this trace */
+ streams = new HashMap<Long, Stream>();
+ environment = new HashMap<String, String>();
+ clocks = new HashMap<String, CTFClock>();
+ streamFileChannels = new LinkedList<FileChannel>();
+ eventDecs = new HashMap<Long, HashMap<Long, EventDeclaration>>();
+ eventDefs = new HashMap<StreamInput, HashMap<Long, EventDefinition>>();
if (!this.path.isDirectory()) {
throw new CTFReaderException("Path must be a valid directory"); //$NON-NLS-1$
}
- this.open();
+ /* Open and parse the metadata file */
+ metadata.parse();
+
+ if (Activator.getDefault() != null) {
+ Activator.getDefault().log(metadata.toString());
+ }
+
+ /* Open all the trace files */
+ /* Create the definitions needed to read things from the files */
+ if (packetHeaderDecl != null) {
+ packetHeaderDef = packetHeaderDecl.createDefinition(this,
+ "packet.header"); //$NON-NLS-1$
+ }
+
+ /* List files not called metadata and not hidden. */
+ File[] files = path.listFiles(metadataFileFilter);
+ Arrays.sort(files, metadataComparator);
+ indexes = new HashMap<StreamInput, StreamInputPacketIndex>();
+ /* Try to open each file */
+ for (File streamFile : files) {
+ openStreamInput(streamFile);
+ }
+
+ /* Create their index */
+ for (Map.Entry<Long, Stream> stream : streams.entrySet()) {
+ Set<StreamInput> inputs = stream.getValue().getStreamInputs();
+ for (StreamInput s : inputs) {
+ /*
+ * Copy the events
+ */
+ Iterator<Entry<Long, EventDeclaration>> it = s.getStream()
+ .getEvents().entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<Long, EventDeclaration> pairs = it.next();
+ Long eventNum = pairs.getKey();
+ EventDeclaration eventDec = pairs.getValue();
+ getEvents(s.getStream().getId()).put(eventNum, eventDec);
+ }
+
+ /*
+ * index the trace
+ */
+ s.setupIndex();
+ }
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ /* If this trace gets closed, release the descriptors to the streams */
+ for (FileChannel fc : streamFileChannels) {
+ if (fc != null) {
+ try {
+ fc.close();
+ } catch (IOException e) {
+ // do nothing it's ok, we tried to close it.
+ }
+ }
+ }
+ super.finalize();
+
}
// ------------------------------------------------------------------------
// Getters/Setters/Predicates
// ------------------------------------------------------------------------
+ /**
+ * Gets an event declaration hash map for a given streamID
+ *
+ * @param streamId
+ * The ID of the stream from which to read
+ * @return The Hash map with the event declarations
+ */
+ public HashMap<Long, EventDeclaration> getEvents(Long streamId) {
+ return eventDecs.get(streamId);
+ }
+
+ /**
+ * Gets an index for a given StreamInput
+ * @param id the StreamInput
+ * @return The index
+ */
+ public StreamInputPacketIndex getIndex(StreamInput id){
+ if(! indexes.containsKey(id)){
+ indexes.put(id, new StreamInputPacketIndex());
+ }
+ return indexes.get(id);
+ }
+
+ /**
+ * Gets an event Declaration hashmap for a given StreamInput
+ * @param id the StreamInput
+ * @return the hashmap with the event definitions
+ */
+ public HashMap<Long, EventDefinition> getEventDefs(StreamInput id) {
+ if(! eventDefs.containsKey(id)){
+ eventDefs.put(id, new HashMap<Long, EventDefinition>());
+ }
+ return eventDefs.get(id);
+ }
+
+ /**
+ * Get an event by it's ID
+ *
+ * @param streamId
+ * The ID of the stream from which to read
+ * @param id
+ * the ID of the event
+ * @return the event declaration
+ */
+ public EventDeclaration getEventType(long streamId, long id) {
+ return getEvents(streamId).get(id);
+ }
+
/**
* Method getStream gets the stream for a given id
*
// Operations
// ------------------------------------------------------------------------
- /**
- * Opens the trace and creates the index.
- *
- * @throws CTFReaderException
- */
- private void open() throws CTFReaderException {
- /* Open and parse the metadata file */
- openTraceMetadata();
-
- if (CtfCorePlugin.getDefault() != null) {
- CtfCorePlugin.getDefault().log(metadata.toString());
- }
- /* Open all the trace files */
- openStreamInputs();
-
- /* Create their index */
- createStreamInputIndexes();
- }
-
- /**
- * Parses the metadata
- *
- * @throws CTFReaderException
- */
- private void openTraceMetadata() throws CTFReaderException {
- metadata.parse();
- }
-
- /**
- * Creates the definitions needed by the Trace class to open the trace
- * files.
- */
- private void createDefinitions() {
- if (packetHeaderDecl != null) {
- packetHeaderDef = packetHeaderDecl.createDefinition(this,
- "packet.header"); //$NON-NLS-1$
- }
- }
-
- /**
- * Creates the indexes of all the trace files.
- *
- * @throws CTFReaderException
- */
- private void createStreamInputIndexes() throws CTFReaderException {
- for (Map.Entry<Long, Stream> stream : streams.entrySet()) {
- Set<StreamInput> inputs = stream.getValue().getStreamInputs();
- for (StreamInput s : inputs) {
- s.createIndex();
- }
- }
- }
-
- /**
- * Tries to open every file in the trace directory (except metadata).
- *
- * @throws CTFReaderException
- */
- private void openStreamInputs() throws CTFReaderException {
- /* Create the definitions needed to read things from the files */
- createDefinitions();
-
- /* List files not called metadata and not hidden. */
- File[] files = path.listFiles(new FileFilter() {
-
- @Override
- public boolean accept(File pathname) {
-
- if (pathname.isDirectory()) {
- return false;
- }
-
- if (pathname.isHidden()) {
- return false;
- }
-
- if (pathname.getName().equals("metadata")) { //$NON-NLS-1$
- return false;
- }
-
- return true;
- }
- });
- Arrays.sort(files, new Comparator<File>() {
-
- @Override
- public int compare(File o1, File o2) {
-
- return o1.getName().compareTo(o2.getName());
-
- }
- });
-
- /* Try to open each file */
- for (File s : files) {
- openStreamInput(s);
- }
- }
-
/**
* Tries to open the given file, reads the first packet header of the file
* and check its validity.
*
* @param streamFile
* A trace file in the trace directory.
+ * @param index
+ * Which index in the class' streamFileChannel array this file
+ * must use
* @throws CTFReaderException
*/
private void openStreamInput(File streamFile) throws CTFReaderException {
- FileChannel streamFileChannel;
MappedByteBuffer byteBuffer;
BitBuffer streamBitBuffer;
+ Stream stream;
+ FileChannel fc;
if (!streamFile.canRead()) {
throw new CTFReaderException("Unreadable file : " //$NON-NLS-1$
try {
/* Open the file and get the FileChannel */
- streamFileChannel = new FileInputStream(streamFile).getChannel();
+ fc = new FileInputStream(streamFile).getChannel();
+ streamFileChannels.add(fc);
/* Map one memory page of 4 kiB */
- byteBuffer = streamFileChannel.map(MapMode.READ_ONLY, 0, 4096);
+ byteBuffer = fc.map(MapMode.READ_ONLY, 0, 4096);
} catch (IOException e) {
/* Shouldn't happen at this stage if every other check passed */
throw new CTFReaderException();
packetHeaderDef.read(streamBitBuffer);
/* Check the magic number */
- IntegerDefinition magicDef = (IntegerDefinition) packetHeaderDef.lookupDefinition("magic"); //$NON-NLS-1$
+ IntegerDefinition magicDef = (IntegerDefinition) packetHeaderDef
+ .lookupDefinition("magic"); //$NON-NLS-1$
int magic = (int) magicDef.getValue();
if (magic != Utils.CTF_MAGIC) {
throw new CTFReaderException("CTF magic mismatch"); //$NON-NLS-1$
}
/* Check UUID */
- ArrayDefinition uuidDef = (ArrayDefinition) packetHeaderDef.lookupDefinition("uuid"); //$NON-NLS-1$
- assert ((uuidDef != null) && (uuidDef.getDeclaration().getLength() == Utils.UUID_LEN));
+ ArrayDefinition uuidDef = (ArrayDefinition) packetHeaderDef
+ .lookupDefinition("uuid"); //$NON-NLS-1$
if (uuidDef != null) {
byte[] uuidArray = new byte[Utils.UUID_LEN];
for (int i = 0; i < Utils.UUID_LEN; i++) {
- IntegerDefinition uuidByteDef = (IntegerDefinition) uuidDef.getElem(i);
+ IntegerDefinition uuidByteDef = (IntegerDefinition) uuidDef
+ .getElem(i);
uuidArray[i] = (byte) uuidByteDef.getValue();
}
// TODO: it hasn't been checked that the stream_id field exists and
// is an unsigned
// integer
- IntegerDefinition streamIDDef = (IntegerDefinition) packetHeaderDef.lookupDefinition("stream_id"); //$NON-NLS-1$
+ IntegerDefinition streamIDDef = (IntegerDefinition) packetHeaderDef
+ .lookupDefinition("stream_id"); //$NON-NLS-1$
assert (streamIDDef != null);
long streamID = streamIDDef.getValue();
/* Get the stream to which this trace file belongs to */
- Stream stream = streams.get(streamID);
-
- /* Create the stream input */
- StreamInput streamInput = new StreamInput(stream,
- streamFileChannel, streamFile);
-
- /* Add a reference to the streamInput in the stream */
- stream.addInput(streamInput);
+ stream = streams.get(streamID);
} else {
/* No packet header, we suppose there is only one stream */
- Stream stream = streams.get(null);
+ stream = streams.get(null);
+ }
- /* Create the stream input */
- StreamInput streamInput = new StreamInput(stream,
- streamFileChannel, streamFile);
+ /* Create the stream input */
+ StreamInput streamInput = new StreamInput(stream, fc, streamFile);
- /* Add a reference to the streamInput in the stream */
- stream.addInput(streamInput);
- }
+ /* Add a reference to the streamInput in the stream */
+ stream.addInput(streamInput);
}
/**
*
* @param stream
* A stream object.
- *
* @throws ParseException
+ * If there was some problem reading the metadata
*/
public void addStream(Stream stream) throws ParseException {
}
/*
- * If the stream we try to add has the null key, it must be the only
- * one. Thus, if the streams container is not empty, it is not valid.
+ * 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.
*/
if ((stream.getId() == null) && (streams.size() != 0)) {
throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$
/* It should be ok now. */
streams.put(stream.getId(), stream);
+ eventDecs.put(stream.getId(), new HashMap<Long,EventDeclaration>());
}
+ /**
+ * gets the Environment variables from the trace metadata (See CTF spec)
+ * @return the environment variables in a hashmap form (key value)
+ */
public HashMap<String, String> getEnvironment() {
return environment;
}
- public String LookupEnvironment( String key )
- {
+ /**
+ * Look up a specific environment variable
+ * @param key the key to look for
+ * @return the value of the variable, can be null.
+ */
+ public String lookupEnvironment(String key) {
return environment.get(key);
}
- public void addEnvironmentVar( String varName, String varValue)
- {
+ /**
+ * Add a variable to the environment variables
+ * @param varName the name of the variable
+ * @param varValue the value of the variable
+ */
+ public void addEnvironmentVar(String varName, String varValue) {
environment.put(varName, varValue);
}
+ /**
+ * Add a clock to the clock list
+ * @param nameValue the name of the clock (full name with scope)
+ * @param ctfClock the clock
+ */
public void addClock(String nameValue, CTFClock ctfClock) {
- clocks.put(nameValue, ctfClock);
+ clocks.put(nameValue, ctfClock);
}
- public CTFClock getClock(String name){
+ /**
+ * gets the clock with a specific name
+ * @param name the name of the clock.
+ * @return the clock
+ */
+ public CTFClock getClock(String name) {
return clocks.get(name);
}
- public CTFClock getClock(){
- if( clocks.size() == 1 )
- {
- String key = (String) clocks.keySet().toArray()[0];
- return clocks.get(key);
+ private CTFClock singleClock;
+ private long singleOffset;
+
+ /**
+ * gets the clock if there is only one. (this is 100% of the use cases as of June 2012)
+ * @return the clock
+ */
+ public final CTFClock getClock() {
+ if (clocks.size() == 1) {
+ if (singleClock == null) {
+ singleClock = clocks.get(clocks.keySet().toArray()[0]);
+ singleOffset = (Long) getClock().getProperty("offset"); //$NON-NLS-1$
+ }
+ return singleClock;
}
return null;
}
+ /**
+ * gets the time offset of a clock with respect to UTC in nanoseconds
+ * @return the time offset of a clock with respect to UTC in nanoseconds
+ */
+ public final long getOffset() {
+ if (getClock() == null) {
+ return 0;
+ }
+ return singleOffset;
+ }
+
+ /**
+ * Does a given stream contain any events?
+ * @param id the stream ID
+ * @return true if the stream has events.
+ */
+ public boolean hasEvents(Long id){
+ return eventDecs.containsKey(id);
+ }
+
+ /**
+ * Add an event declaration map to the events map.
+ * @param id the id of a stream
+ * @return the hashmap containing events.
+ */
+ public HashMap<Long, EventDeclaration> createEvents(Long id){
+ HashMap<Long, EventDeclaration> value = eventDecs.get(id);
+ if( value == null ) {
+ value = new HashMap<Long, EventDeclaration>();
+ eventDecs.put(id, value);
+ }
+ return value;
+ }
+
+}
+
+class MetadataFileFilter implements FileFilter {
+
+ @Override
+ public boolean accept(File pathname) {
+ if (pathname.isDirectory()) {
+ return false;
+ }
+ if (pathname.isHidden()) {
+ return false;
+ }
+ if (pathname.getName().equals("metadata")) { //$NON-NLS-1$
+ return false;
+ }
+ return true;
+ }
+
+}
+
+class MetadataComparator implements Comparator<File>, Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public int compare(File o1, File o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
}