ctf: make search return the first matching packet in a trace
authorMatthew Khouzam <matthew.khouzam@ericsson.com>
Fri, 28 Apr 2017 17:16:07 +0000 (13:16 -0400)
committerMatthew Khouzam <matthew.khouzam@ericsson.com>
Wed, 3 May 2017 17:04:40 +0000 (13:04 -0400)
The CTF parser would return a random packet in search when all packets
overlap. This patch makes it return the first. Performance impacts on
properly packetized traces should be negligeable.

Change-Id: I4e9470c46b3801779ba874c4d2ccfcae88a65e16
Signed-off-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Reviewed-on: https://git.eclipse.org/r/96055
Reviewed-by: Hudson CI
Reviewed-by: Patrick Tasse <patrick.tasse@gmail.com>
Tested-by: Patrick Tasse <patrick.tasse@gmail.com>
ctf/org.eclipse.tracecompass.ctf.core.tests/src/org/eclipse/tracecompass/ctf/core/tests/trace/CTFStreamInputPacketIndexTest.java
ctf/org.eclipse.tracecompass.ctf.core.tests/src/org/eclipse/tracecompass/ctf/core/tests/trace/PacketStub.java [new file with mode: 0644]
ctf/org.eclipse.tracecompass.ctf.core/src/org/eclipse/tracecompass/internal/ctf/core/trace/StreamInputPacketIndex.java

index b826252ecc53cacff7b69c950b780821a533a099..393ca56b101cf518e1c6c2081786876cc1a56c6b 100644 (file)
 
 package org.eclipse.tracecompass.ctf.core.tests.trace;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import org.eclipse.tracecompass.ctf.core.CTFException;
+import org.eclipse.tracecompass.ctf.core.trace.ICTFPacketDescriptor;
 import org.eclipse.tracecompass.internal.ctf.core.trace.StreamInputPacketIndex;
 import org.eclipse.tracecompass.internal.ctf.core.trace.StreamInputPacketIndexEntry;
 import org.junit.Before;
@@ -28,7 +32,7 @@ import org.junit.Test;
 @SuppressWarnings("javadoc")
 public class CTFStreamInputPacketIndexTest {
 
-    private StreamInputPacketIndex fixture;
+    private StreamInputPacketIndex fFixture;
 
     /**
      * Perform pre-test initialization.
@@ -37,17 +41,102 @@ public class CTFStreamInputPacketIndexTest {
      */
     @Before
     public void setUp() {
-        fixture = new StreamInputPacketIndex();
-        fixture.append(new StreamInputPacketIndexEntry(1L,0L));
+        fFixture = new StreamInputPacketIndex();
+    }
+
+    @Test
+    public void testStreamInputPacketIndex() {
+        assertNotNull(fFixture);
+        assertTrue(fFixture.append(new StreamInputPacketIndexEntry(0, 1L)));
+        assertTrue(fFixture.append(new PacketStub(1, 0, 0)));
     }
 
     /**
      * Run the StreamInputPacketIndex() constructor test.
      */
     @Test
-    public void testStreamInputPacketIndex() {
-        assertNotNull(fixture);
-        assertNotNull(fixture.getElement(0));
+    public void testStreamInputPacketIndexGet() {
+        assertNotNull(fFixture);
+        ICTFPacketDescriptor first = new StreamInputPacketIndexEntry(0, 1L);
+        ICTFPacketDescriptor second = new PacketStub(1, 0, 0);
+        assertTrue(fFixture.append(first));
+        assertTrue(fFixture.append(second));
+
+        assertEquals(first, fFixture.getElement(0));
+        assertEquals(second, fFixture.getElement(1));
+    }
+
+    /**
+     * Test on a contiguous set
+     */
+    @Test
+    public void testStreamInputPacketIndexContiguous() {
+        assertTrue(fFixture.append(new PacketStub(0, 0, 1)));
+        assertTrue(fFixture.append(new PacketStub(1, 2, 3)));
+        assertTrue(fFixture.append(new PacketStub(2, 4, 5)));
+        assertTrue(fFixture.append(new PacketStub(3, 6, 6)));
+
+        assertEquals(2, fFixture.search(5));
+    }
+
+    @Test
+    public void testStreamInputPacketIndexDisjoint() {
+        assertTrue(fFixture.append(new PacketStub(0, 0, 1)));
+        assertTrue(fFixture.append(new PacketStub(1, 2, 3)));
+        assertTrue(fFixture.append(new PacketStub(2, 6, 6)));
+
+        assertEquals(1, fFixture.search(5));
+        assertEquals(1, fFixture.search(3));
+    }
+
+    @Test
+    public void testStreamInputPacketIndexOverlapping() {
+        assertTrue(fFixture.append(new PacketStub(0, 0, 1)));
+        assertTrue(fFixture.append(new PacketStub(1, 2, 3)));
+        assertTrue(fFixture.append(new PacketStub(2, 6, 6)));
+        assertTrue(fFixture.append(new PacketStub(3, 6, 6)));
+        assertTrue(fFixture.append(new PacketStub(4, 6, 6)));
+        assertTrue(fFixture.append(new PacketStub(5, 6, 6)));
+        assertTrue(fFixture.append(new PacketStub(6, 6, 6)));
+        assertTrue(fFixture.append(new PacketStub(7, 6, 6)));
+        assertTrue(fFixture.append(new PacketStub(8, 6, 6)));
+        assertTrue(fFixture.append(new PacketStub(9, 6, 6)));
+
+        assertEquals(1, fFixture.search(5));
+        assertEquals(2, fFixture.search(6));
+    }
+
+    @Test
+    public void testStreamInputPacketIndexOverlappingBothSides() {
+        assertTrue(fFixture.append(new PacketStub(0, 0, 3)));
+        assertTrue(fFixture.append(new PacketStub(1, 0, 3)));
+        assertTrue(fFixture.append(new PacketStub(2, 0, 3)));
+        assertTrue(fFixture.append(new PacketStub(3, 7, 9)));
+        assertTrue(fFixture.append(new PacketStub(4, 7, 9)));
+        assertTrue(fFixture.append(new PacketStub(5, 7, 9)));
+        assertEquals(0, fFixture.search(3));
+        assertEquals(2, fFixture.search(6));
+        assertEquals(3, fFixture.search(8));
+    }
+
+    @Test
+    public void testStreamInputPacketIndexLargeOverlapping() {
+        assertTrue(fFixture.append(new PacketStub(0, Long.MIN_VALUE, Long.MAX_VALUE)));
+        assertTrue(fFixture.append(new PacketStub(1, Long.MIN_VALUE, Long.MAX_VALUE)));
+        assertTrue(fFixture.append(new PacketStub(2, Long.MIN_VALUE, Long.MAX_VALUE)));
+        assertTrue(fFixture.append(new PacketStub(3, Long.MIN_VALUE, Long.MAX_VALUE)));
+        assertTrue(fFixture.append(new PacketStub(4, Long.MIN_VALUE, Long.MAX_VALUE)));
+
+        assertEquals(0, fFixture.search(5));
+        assertEquals(0, fFixture.search(6));
+    }
+
+    @Test
+    public void testStreamInputPacketIndexInvalidAppend() {
+        fFixture = new StreamInputPacketIndex();
+        assertTrue(fFixture.append(new PacketStub(0, 0, -1)));
+        assertFalse(fFixture.append(new PacketStub(0, 0, 0)));
+        assertFalse(fFixture.append(new PacketStub(0, -1, 0)));
     }
 
 }
\ No newline at end of file
diff --git a/ctf/org.eclipse.tracecompass.ctf.core.tests/src/org/eclipse/tracecompass/ctf/core/tests/trace/PacketStub.java b/ctf/org.eclipse.tracecompass.ctf.core.tests/src/org/eclipse/tracecompass/ctf/core/tests/trace/PacketStub.java
new file mode 100644 (file)
index 0000000..45ec8f0
--- /dev/null
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Ericsson
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.ctf.core.tests.trace;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.tracecompass.ctf.core.trace.ICTFPacketDescriptor;
+
+/**
+ * Internal implementation to test packet index
+ *
+ * @author Matthew Khouzam
+ */
+class PacketStub implements ICTFPacketDescriptor {
+
+    private final long fOffsetBytes;
+    private final long fTsStart;
+    private final long fTsEnd;
+
+    public PacketStub(long offset, long start, long end) {
+        fOffsetBytes = offset;
+        fTsStart = start;
+        fTsEnd = end;
+    }
+
+    @Override
+    public boolean includes(long ts) {
+        return ts >= fTsStart && ts <= fTsEnd;
+    }
+
+    @Override
+    public long getOffsetBits() {
+        return fOffsetBytes * 8;
+    }
+
+    @Override
+    public long getPacketSizeBits() {
+        return 0;
+    }
+
+    @Override
+    public long getContentSizeBits() {
+        return 0;
+    }
+
+    @Override
+    public long getTimestampBegin() {
+        return fTsStart;
+    }
+
+    @Override
+    public long getTimestampEnd() {
+        return fTsEnd;
+    }
+
+    @Override
+    public long getLostEvents() {
+        return 0;
+    }
+
+    @Override
+    public @NonNull Map<String, Object> getAttributes() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public String getTarget() {
+        return "";
+    }
+
+    @Override
+    public long getTargetId() {
+        return 0;
+    }
+
+    @Override
+    public long getOffsetBytes() {
+        return fOffsetBytes;
+    }
+
+    @Override
+    public long getPayloadStartBits() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "[" + fOffsetBytes + ", " + fTsStart + " - " + fTsEnd + "]";
+    }
+
+}
\ No newline at end of file
index 91d31649aa622ec70482f1e9a41c2d8bf15688ea..bb6aa86f73e3b67542104b1ae1be9016910c904e 100644 (file)
@@ -22,7 +22,6 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
-import java.util.TreeSet;
 
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jdt.annotation.NonNull;
@@ -95,7 +94,7 @@ public class StreamInputPacketIndex {
         /* Validate consistent entry. */
         if (entryToAdd.getTimestampBegin() > entryToAdd.getTimestampEnd()) {
             Activator.log(IStatus.WARNING, "Packet at offset " + entryToAdd.getOffsetBytes() + //$NON-NLS-1$
-                          " begin timestamp is after end timestamp"); //$NON-NLS-1$
+                    " begin timestamp is after end timestamp"); //$NON-NLS-1$
             entryToAdd = new StreamInputPacketIndexEntry(entryToAdd, Long.MAX_VALUE);
         }
 
@@ -103,7 +102,9 @@ public class StreamInputPacketIndex {
          * Validate entries are inserted in monotonic increasing timestamp
          * order.
          */
-        if (!fEntries.isEmpty() && (entryToAdd.getTimestampBegin() < lastElement().getTimestampBegin())) {
+        if (!fEntries.isEmpty() &&
+                (entryToAdd.getTimestampBegin() < lastElement().getTimestampBegin() ||
+                        entryToAdd.getOffsetBytes() <= lastElement().getOffsetBytes())) {
             return false;
         }
 
@@ -113,7 +114,8 @@ public class StreamInputPacketIndex {
 
     /**
      * Returns the first packet that could include the timestamp, that is the
-     * last packet with a begin timestamp smaller than the given timestamp.
+     * first packet that includes the given timestamp, or if none exist, first
+     * packet that begins before the given timestamp
      *
      * @param timestamp
      *            The timestamp to look for.
@@ -131,7 +133,21 @@ public class StreamInputPacketIndex {
         if (index < 0) {
             index = -index - 1;
         }
-        return index;
+        if (index >= fEntries.size()) {
+            return fEntries.size() - 1;
+        }
+        for (int i = index; i > 0; i--) {
+            ICTFPacketDescriptor entry = fEntries.get(i);
+            ICTFPacketDescriptor nextEntry = fEntries.get(i - 1);
+            if (entry.getTimestampEnd() >= timestamp && nextEntry.getTimestampEnd() < timestamp) {
+                if (entry.getTimestampBegin() <= timestamp) {
+                    return i;
+                }
+                return i - 1;
+            }
+        }
+        return 0;
+
     }
 
     /**
@@ -162,9 +178,12 @@ public class StreamInputPacketIndex {
      * this data structure, or -1 if this data structure does not contain the
      * element. More formally, returns the lowest index {@code i} such that, for
      * an entry {@code o}, {@code (o==null ? get(i)==null : o.equals(get(i)))},
-     * or {@code -1} if there is no such index. This will work in log(n) time
-     * since the data structure contains elements in a non-repeating increasing
-     * manner.
+     * or {@code -1} if there is no such index. This will typically work in
+     * log(n) time since the data structure contains elements in a non-repeating
+     * increasing manner.
+     *
+     * Only the offset is checked in this case as there cannot be more than one
+     * packet with a given offset.
      *
      * @param element
      *            element to search for
@@ -183,40 +202,11 @@ public class StreamInputPacketIndex {
     public int indexOf(ICTFPacketDescriptor element) {
         int indexOf = -1;
         if (element != null) {
-            indexOf = Collections.binarySearch(fEntries, element, new MonotonicComparator());
+            indexOf = Collections.binarySearch(fEntries, element, Comparator.comparingLong(ICTFPacketDescriptor::getOffsetBytes));
         }
         return (indexOf < 0) ? -1 : indexOf;
     }
 
-    /**
-     * Ordering comparator for entering entries into a data structure sorted by
-     * timestamp.
-     */
-    private static class MonotonicComparator implements Comparator<ICTFPacketDescriptor>, Serializable {
-        /**
-         * For {@link Serializable}, that way if we migrate to a {@link TreeSet}
-         * the comparator is serializable too.
-         */
-        private static final long serialVersionUID = -5693064068367242076L;
-
-        @Override
-        public int compare(ICTFPacketDescriptor left, ICTFPacketDescriptor right) {
-            if (left.getTimestampBegin() > right.getTimestampBegin()) {
-                return 1;
-            }
-            if (left.getTimestampBegin() < right.getTimestampBegin()) {
-                return -1;
-            }
-            if (left.getTimestampEnd() > right.getTimestampEnd()) {
-                return 1;
-            }
-            if (left.getTimestampEnd() < right.getTimestampEnd()) {
-                return -1;
-            }
-            return 0;
-        }
-    }
-
     /**
      * Used for search, assumes that the second argument in the comparison is
      * always the key
This page took 0.02823 seconds and 5 git commands to generate.