ctf: Fix some Sonar warnings
[deliverable/tracecompass.git] / org.eclipse.linuxtools.ctf.core / src / org / eclipse / linuxtools / ctf / core / trace / CTFTrace.java
index 42e596a0b9237930b50301f4a1d41867e287751d..f7b0bad5fa0ec4a1bd8596c28fd5db38384d1bdf 100644 (file)
@@ -1,13 +1,14 @@
 /*******************************************************************************
- * Copyright (c) 2011-2012 Ericsson, Ecole Polytechnique de Montreal and others
+ * Copyright (c) 2011, 2013 Ericsson, Ecole Polytechnique de Montreal and others
  *
  * All rights reserved. This program and the accompanying materials are made
  * available under the terms of the Eclipse Public License v1.0 which
  * accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
- * Contributors: Matthew Khouzam - Initial API and implementation
- * Contributors: Alexandre Montplaisir - Initial API and implementation
+ * Contributors:
+ *     Matthew Khouzam - Initial API and implementation
+ *     Alexandre Montplaisir - Initial API and implementation
  *******************************************************************************/
 
 package org.eclipse.linuxtools.ctf.core.trace;
@@ -22,29 +23,31 @@ import java.nio.MappedByteBuffer;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileChannel.MapMode;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.TreeSet;
 import java.util.UUID;
 
+import org.eclipse.linuxtools.ctf.core.event.CTFCallsite;
 import org.eclipse.linuxtools.ctf.core.event.CTFClock;
-import org.eclipse.linuxtools.ctf.core.event.EventDeclaration;
 import org.eclipse.linuxtools.ctf.core.event.EventDefinition;
+import org.eclipse.linuxtools.ctf.core.event.IEventDeclaration;
+import org.eclipse.linuxtools.ctf.core.event.io.BitBuffer;
 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.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;
 
 /**
@@ -60,15 +63,6 @@ import org.eclipse.linuxtools.internal.ctf.core.trace.StreamInputPacketIndex;
  */
 public class CTFTrace implements IDefinitionScope {
 
-    // ------------------------------------------------------------------------
-    // Attributes
-    // ------------------------------------------------------------------------
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see java.lang.Object#toString()
-     */
     @SuppressWarnings("nls")
     @Override
     public String toString() {
@@ -82,11 +76,6 @@ public class CTFTrace implements IDefinitionScope {
      */
     private final File path;
 
-    /**
-     * The metadata parsing object.
-     */
-    private final Metadata metadata;
-
     /**
      * Major CTF version number
      */
@@ -112,6 +101,11 @@ public class CTFTrace implements IDefinitionScope {
      */
     private StructDeclaration packetHeaderDecl = null;
 
+    /**
+     * The clock of the trace
+     */
+    private CTFClock singleClock;
+
     /**
      * Packet header structure definition
      *
@@ -123,32 +117,37 @@ public class CTFTrace implements IDefinitionScope {
     /**
      * Collection of streams contained in the trace.
      */
-    private final HashMap<Long, Stream> streams;
+    private final Map<Long, Stream> streams =  new HashMap<Long, Stream>();
 
     /**
      * Collection of environment variables set by the tracer
      */
-    private final HashMap<String, String> environment;
+    private final Map<String, String> environment = new HashMap<String, String>();
 
     /**
      * Collection of all the clocks in a system.
      */
-    private final HashMap<String, CTFClock> clocks;
+    private final Map<String, CTFClock> clocks = new HashMap<String, CTFClock>();
 
-    /** FileChannels to the streams */
-    private final List<FileChannel> streamFileChannels;
+    /** FileInputStreams to the streams */
+    private final List<FileInputStream> fileInputStreams = new LinkedList<FileInputStream>();
 
     /** Handlers for the metadata files */
-    private final static FileFilter metadataFileFilter = new MetadataFileFilter();
-    private final static Comparator<File> metadataComparator = new MetadataComparator(); // $codepro.audit.disable
-                                                                                         // fieldJavadoc
+    private static final FileFilter METADATA_FILE_FILTER = new MetadataFileFilter();
+    private static final Comparator<File> METADATA_COMPARATOR = new MetadataComparator();
 
     /** map of all the event types */
-    private final HashMap<Long,HashMap<Long, EventDeclaration>> eventDecs;
+    private final Map<Long,HashMap<Long, IEventDeclaration>> eventDecs = new HashMap<Long, HashMap<Long,IEventDeclaration>>();
     /** map of all the event types */
-    private final HashMap<StreamInput,HashMap<Long, EventDefinition>> eventDefs;
+    private final Map<StreamInput,HashMap<Long, EventDefinition>> eventDefs = new HashMap<StreamInput, HashMap<Long,EventDefinition>>();
     /** map of all the indexes */
-    private final HashMap<StreamInput, StreamInputPacketIndex> indexes;
+    private final Map<StreamInput, StreamInputPacketIndex> indexes = new HashMap<StreamInput, StreamInputPacketIndex>();
+
+    /** Callsite helpers */
+    private Map<String, LinkedList<CTFCallsite>> callsitesByName = new HashMap<String, LinkedList<CTFCallsite>>();
+
+    /** Callsite helpers */
+    private TreeSet<CTFCallsite> callsitesByIP = new TreeSet<CTFCallsite>();
 
 
 
@@ -179,15 +178,12 @@ public class CTFTrace implements IDefinitionScope {
      */
     public CTFTrace(File path) throws CTFReaderException {
         this.path = path;
-        this.metadata = new Metadata(this);
+        final Metadata 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.exists()) {
+            throw new CTFReaderException("Trace (" + path.getPath() + ") doesn't exist. Deleted or moved?"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
 
         if (!this.path.isDirectory()) {
             throw new CTFReaderException("Path must be a valid directory"); //$NON-NLS-1$
@@ -204,9 +200,8 @@ public class CTFTrace implements IDefinitionScope {
         }
 
         /* List files not called metadata and not hidden. */
-        File[] files = path.listFiles(metadataFileFilter);
-        Arrays.sort(files, metadataComparator);
-        indexes = new HashMap<StreamInput, StreamInputPacketIndex>();
+        File[] files = path.listFiles(METADATA_FILE_FILTER);
+        Arrays.sort(files, METADATA_COMPARATOR);
         /* Try to open each file */
         for (File streamFile : files) {
             openStreamInput(streamFile);
@@ -219,12 +214,12 @@ public class CTFTrace implements IDefinitionScope {
                 /*
                  * Copy the events
                  */
-                Iterator<Entry<Long, EventDeclaration>> it = s.getStream()
+                Iterator<Entry<Long, IEventDeclaration>> it = s.getStream()
                         .getEvents().entrySet().iterator();
                 while (it.hasNext()) {
-                    Map.Entry<Long, EventDeclaration> pairs = it.next();
+                    Entry<Long, IEventDeclaration> pairs = it.next();
                     Long eventNum = pairs.getKey();
-                    EventDeclaration eventDec = pairs.getValue();
+                    IEventDeclaration eventDec = pairs.getValue();
                     getEvents(s.getStream().getId()).put(eventNum, eventDec);
                 }
 
@@ -236,20 +231,21 @@ public class CTFTrace implements IDefinitionScope {
         }
     }
 
-    @Override
-    protected void finalize() throws Throwable {
-        /* If this trace gets closed, release the descriptors to the streams */
-        for (FileChannel fc : streamFileChannels) {
-            if (fc != null) {
+    /**
+     * Dispose the trace
+     * @since 2.0
+     */
+    public void dispose() {
+        for (FileInputStream fis : fileInputStreams) {
+            if (fis != null) {
                 try {
-                    fc.close();
+                    fis.close();
                 } catch (IOException e) {
                     // do nothing it's ok, we tried to close it.
                 }
             }
         }
-        super.finalize();
-
+        System.gc(); // Invoke GC to release MappedByteBuffer objects (Java bug JDK-4724038)
     }
 
     // ------------------------------------------------------------------------
@@ -262,8 +258,9 @@ public class CTFTrace implements IDefinitionScope {
      * @param streamId
      *            The ID of the stream from which to read
      * @return The Hash map with the event declarations
+     * @since 2.0
      */
-    public HashMap<Long, EventDeclaration> getEvents(Long streamId) {
+    public Map<Long, IEventDeclaration> getEvents(Long streamId) {
         return eventDecs.get(streamId);
     }
 
@@ -272,7 +269,7 @@ public class CTFTrace implements IDefinitionScope {
      * @param id the StreamInput
      * @return The index
      */
-    public StreamInputPacketIndex getIndex(StreamInput id){
+    StreamInputPacketIndex getIndex(StreamInput id){
         if(! indexes.containsKey(id)){
             indexes.put(id, new StreamInputPacketIndex());
         }
@@ -283,8 +280,9 @@ public class CTFTrace implements IDefinitionScope {
      * Gets an event Declaration hashmap for a given StreamInput
      * @param id the StreamInput
      * @return the hashmap with the event definitions
+     * @since 2.0
      */
-    public HashMap<Long, EventDefinition> getEventDefs(StreamInput id) {
+    public Map<Long, EventDefinition> getEventDefs(StreamInput id) {
         if(! eventDefs.containsKey(id)){
             eventDefs.put(id, new HashMap<Long, EventDefinition>());
         }
@@ -299,8 +297,9 @@ public class CTFTrace implements IDefinitionScope {
      * @param id
      *            the ID of the event
      * @return the event declaration
+     * @since 2.0
      */
-    public EventDeclaration getEventType(long streamId, long id) {
+    public IEventDeclaration getEventType(long streamId, long id) {
         return getEvents(streamId).get(id);
     }
 
@@ -310,6 +309,7 @@ public class CTFTrace implements IDefinitionScope {
      * @param id
      *            Long the id of the stream
      * @return Stream the stream that we need
+     * @since 2.0
      */
     public Stream getStream(Long id) {
         return streams.get(id);
@@ -396,8 +396,9 @@ public class CTFTrace implements IDefinitionScope {
      * Method UUIDIsSet is the UUID set?
      *
      * @return boolean is the UUID set?
+     * @since 2.0
      */
-    public boolean UUIDIsSet() {
+    public boolean uuidIsSet() {
         return uuid != null;
     }
 
@@ -451,7 +452,7 @@ public class CTFTrace implements IDefinitionScope {
      *
      * @return ByteOrder gets the trace byte order
      */
-    public ByteOrder getByteOrder() {
+    public final ByteOrder getByteOrder() {
         return byteOrder;
     }
 
@@ -521,14 +522,15 @@ public class CTFTrace implements IDefinitionScope {
 
         try {
             /* Open the file and get the FileChannel */
-            fc = new FileInputStream(streamFile).getChannel();
-            streamFileChannels.add(fc);
+            FileInputStream fis = new FileInputStream(streamFile);
+            fileInputStreams.add(fis);
+            fc = fis.getChannel();
 
             /* Map one memory page of 4 kiB */
-            byteBuffer = fc.map(MapMode.READ_ONLY, 0, 4096);
+            byteBuffer = fc.map(MapMode.READ_ONLY, 0, Math.min((int)fc.size(), 4096));
         } catch (IOException e) {
             /* Shouldn't happen at this stage if every other check passed */
-            throw new CTFReaderException();
+            throw new CTFReaderException(e);
         }
 
         /* Create a BitBuffer with this mapping and the trace byte order */
@@ -565,23 +567,26 @@ public class CTFTrace implements IDefinitionScope {
                 }
             }
 
-            /* Read stream ID */
-            // 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$
-            assert (streamIDDef != null);
+            /* Read the stream ID */
+            Definition streamIDDef = packetHeaderDef.lookupDefinition("stream_id"); //$NON-NLS-1$
 
-            long streamID = streamIDDef.getValue();
+            if (streamIDDef instanceof IntegerDefinition) { //this doubles as a null check
+                long streamID = ((IntegerDefinition) streamIDDef).getValue();
+                stream = streams.get(streamID);
+            } else {
+                /* No stream_id in the packet header */
+                stream = streams.get(null);
+            }
 
-            /* Get the stream to which this trace file belongs to */
-            stream = streams.get(streamID);
         } else {
             /* No packet header, we suppose there is only one stream */
             stream = streams.get(null);
         }
 
+        if (stream == null) {
+            throw new CTFReaderException("Unexpected end of stream"); //$NON-NLS-1$
+        }
+
         /* Create the stream input */
         StreamInput streamInput = new StreamInput(stream, fc, streamFile);
 
@@ -612,6 +617,7 @@ public class CTFTrace implements IDefinitionScope {
      *            A stream object.
      * @throws ParseException
      *             If there was some problem reading the metadata
+     * @since 2.0
      */
     public void addStream(Stream stream) throws ParseException {
 
@@ -624,7 +630,8 @@ public class CTFTrace implements IDefinitionScope {
         }
 
         /*
-         * 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 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 ((stream.getId() == null) && (streams.size() != 0)) {
             throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$
@@ -637,14 +644,15 @@ public class CTFTrace implements IDefinitionScope {
 
         /* It should be ok now. */
         streams.put(stream.getId(), stream);
-        eventDecs.put(stream.getId(), new HashMap<Long,EventDeclaration>());
+        eventDecs.put(stream.getId(), new HashMap<Long,IEventDeclaration>());
     }
 
     /**
      * gets the Environment variables from the trace metadata (See CTF spec)
-     * @return the environment variables in a hashmap form (key value)
+     * @return the environment variables in a map form (key value)
+     * @since 2.0
      */
-    public HashMap<String, String> getEnvironment() {
+    public Map<String, String> getEnvironment() {
         return environment;
     }
 
@@ -684,19 +692,18 @@ public class CTFTrace implements IDefinitionScope {
         return clocks.get(name);
     }
 
-    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)
+     * 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$
-            }
+            singleClock = clocks.get(clocks.keySet().iterator().next());
             return singleClock;
         }
         return null;
@@ -704,13 +711,88 @@ public class CTFTrace implements IDefinitionScope {
 
     /**
      * 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;
+        return singleClock.getClockOffset();
+    }
+
+    /**
+     * 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
+     */
+    private double getTimeScale() {
+        if (getClock() == null) {
+            return 1.0;
+        }
+        return singleClock.getClockScale();
+    }
+
+    /**
+     * Does the trace need to time scale?
+     *
+     * @return if the trace is in ns or cycles.
+     */
+    private boolean clockNeedsScale() {
+        if (getClock() == null) {
+            return false;
+        }
+        return singleClock.isClockScaled();
+    }
+
+    /**
+     * the inverse clock for returning to a scale.
+     *
+     * @return 1.0 / scale
+     */
+    private double getInverseTimeScale() {
+        if (getClock() == null) {
+            return 1.0;
+        }
+        return singleClock.getClockAntiScale();
+    }
+
+    /**
+     * @param cycles
+     *            clock cycles since boot
+     * @return time in nanoseconds UTC offset
+     * @since 2.0
+     */
+    public long timestampCyclesToNanos(long cycles) {
+        long retVal = cycles + getOffset();
+        /*
+         * this fix is since quite often the offset will be > than 53 bits and
+         * therefore the conversion will be lossy
+         */
+        if (clockNeedsScale()) {
+            retVal = (long) (retVal * getTimeScale());
+        }
+        return retVal;
+    }
+
+    /**
+     * @param nanos
+     *            time in nanoseconds UTC offset
+     * @return clock cycles since boot.
+     * @since 2.0
+     */
+    public long timestampNanoToCycles(long nanos) {
+        long retVal;
+        /*
+         * this fix is since quite often the offset will be > than 53 bits and
+         * therefore the conversion will be lossy
+         */
+        if (clockNeedsScale()) {
+            retVal = (long) (nanos * getInverseTimeScale());
+        } else {
+            retVal = nanos;
+        }
+        return retVal - getOffset();
     }
 
     /**
@@ -726,16 +808,119 @@ public class CTFTrace implements IDefinitionScope {
      * Add an event declaration map to the events map.
      * @param id the id of a stream
      * @return the hashmap containing events.
+     * @since 2.0
      */
-    public HashMap<Long, EventDeclaration> createEvents(Long id){
-        HashMap<Long, EventDeclaration> value = eventDecs.get(id);
+    public Map<Long, IEventDeclaration> createEvents(Long id){
+        HashMap<Long, IEventDeclaration> value = eventDecs.get(id);
         if( value == null ) {
-            value = new HashMap<Long, EventDeclaration>();
+            value = new HashMap<Long, IEventDeclaration>();
             eventDecs.put(id, value);
         }
         return value;
     }
 
+    /**
+     * Adds a callsite
+     *
+     * @param eventName
+     *            the event name of the callsite
+     * @param funcName
+     *            the name of the callsite function
+     * @param ip
+     *            the ip of the callsite
+     * @param fileName
+     *            the filename of the callsite
+     * @param lineNumber
+     *            the line number of the callsite
+     */
+    public void addCallsite(String eventName, String funcName, long ip,
+            String fileName, long lineNumber) {
+        final CTFCallsite cs = new CTFCallsite(eventName, funcName, ip,
+                fileName, lineNumber);
+        LinkedList<CTFCallsite> csl = callsitesByName.get(eventName);
+        if (csl == null) {
+            csl = new LinkedList<CTFCallsite>();
+            callsitesByName.put(eventName, csl);
+        }
+
+        ListIterator<CTFCallsite> iter = csl.listIterator();
+        int index = 0;
+        for (; index < csl.size(); index++) {
+            if (iter.next().compareTo(cs) < 0) {
+                break;
+            }
+        }
+
+        csl.add(index, cs);
+
+        callsitesByIP.add(cs);
+    }
+
+    /**
+     * Gets the list of callsites associated to an event name. O(1)
+     *
+     * @param eventName
+     *            the event name
+     * @return the callsite list can be empty
+     * @since 1.2
+     */
+    public List<CTFCallsite> getCallsiteCandidates(String eventName) {
+        LinkedList<CTFCallsite> retVal = callsitesByName.get(eventName);
+        if( retVal == null ) {
+            retVal = new LinkedList<CTFCallsite>();
+        }
+        return retVal;
+    }
+
+    /**
+     * The I'm feeling lucky of getCallsiteCandidates O(1)
+     *
+     * @param eventName
+     *            the event name
+     * @return the first callsite that has that event name, can be null
+     * @since 1.2
+     */
+    public CTFCallsite getCallsite(String eventName) {
+        LinkedList<CTFCallsite> callsites = callsitesByName.get(eventName);
+        if (callsites != null) {
+            return callsites.getFirst();
+        }
+        return null;
+    }
+
+    /**
+     * Gets a callsite from the instruction pointer O(log(n))
+     *
+     * @param ip
+     *            the instruction pointer to lookup
+     * @return the callsite just before that IP in the list remember the IP is
+     *         backwards on X86, can be null if no callsite is before the IP.
+     * @since 1.2
+     */
+    public CTFCallsite getCallsite(long ip) {
+        CTFCallsite cs = new CTFCallsite(null, null, ip, null, 0L);
+        return callsitesByIP.ceiling(cs);
+    }
+
+    /**
+     * Gets a callsite using the event name and instruction pointer O(log(n))
+     *
+     * @param eventName
+     *            the name of the event
+     * @param ip
+     *            the instruction pointer
+     * @return the closest matching callsite, can be null
+     */
+    public CTFCallsite getCallsite(String eventName, long ip) {
+        final LinkedList<CTFCallsite> candidates = callsitesByName.get(eventName);
+        final CTFCallsite dummyCs = new CTFCallsite(null, null, ip, null, -1);
+        final int pos = Collections.binarySearch(candidates, dummyCs)+1;
+        if( pos >= candidates.size()) {
+            return null;
+        }
+        return candidates.get(pos);
+    }
+
 }
 
 class MetadataFileFilter implements FileFilter {
This page took 0.031311 seconds and 5 git commands to generate.