tmf: Add utility method to search for events matching a Predicate
authorAlexandre Montplaisir <alexmonthy@efficios.com>
Wed, 15 Jun 2016 00:33:53 +0000 (20:33 -0400)
committerAlexandre Montplaisir <alexmonthy@efficios.com>
Wed, 15 Jun 2016 22:27:48 +0000 (18:27 -0400)
Add methods in TmfUtils to search for events, forwards or
backwards in the trace, until an event matching the given
Predicate is found.

This will be used to implement a "Go to next/previous event of
current time graph entry" option for time graph views.

Change-Id: I19c78a459f1918cc41b0094f2a16bf0172b34804
Signed-off-by: Alexandre Montplaisir <alexmonthy@efficios.com>
ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/META-INF/MANIFEST.MF
ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/src/org/eclipse/tracecompass/tmf/ctf/core/tests/trace/TmfTraceUtilsSearchingTest.java [new file with mode: 0644]
tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/TmfTraceUtils.java

index 1a1ebb9f3ad0de4151d8b332cf7bbbf40d6c981f..fc85e49bfe4c527b08f787150824885a68f97714 100644 (file)
@@ -10,6 +10,7 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Require-Bundle: org.junit;bundle-version="4.0.0",
  org.eclipse.core.runtime,
  org.eclipse.core.resources,
+ org.eclipse.tracecompass.common.core,
  org.eclipse.tracecompass.tmf.core,
  org.eclipse.tracecompass.tmf.core.tests,
  org.eclipse.tracecompass.tmf.ctf.core,
diff --git a/ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/src/org/eclipse/tracecompass/tmf/ctf/core/tests/trace/TmfTraceUtilsSearchingTest.java b/ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/src/org/eclipse/tracecompass/tmf/ctf/core/tests/trace/TmfTraceUtilsSearchingTest.java
new file mode 100644 (file)
index 0000000..b220989
--- /dev/null
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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.tmf.ctf.core.tests.trace;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotEquals;
+
+import java.util.function.Predicate;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.tracecompass.testtraces.ctf.CtfTestTrace;
+import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+import org.eclipse.tracecompass.tmf.ctf.core.tests.shared.CtfTmfTestTraceUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the event-searching methods in {@link TmfTraceUtils}.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class TmfTraceUtilsSearchingTest {
+
+    private static final @NonNull CtfTestTrace TEST_TRACE = CtfTestTrace.TRACE2;
+    private static final long START_RANK = 500L;
+
+    private ITmfTrace fTrace;
+    private ITmfEvent fStartEvent;
+
+    /**
+     * Test setup
+     */
+    @Before
+    public void setUp() {
+        fTrace = CtfTmfTestTraceUtils.getTrace(TEST_TRACE);
+
+        ITmfContext ctx = fTrace.seekEvent(START_RANK);
+        fStartEvent = fTrace.getNext(ctx);
+        assertEquals("softirq_raise", fStartEvent.getName());
+    }
+
+    /**
+     * Test cleanup
+     */
+    @After
+    public void tearDown() {
+        if (fTrace != null) {
+            fTrace.dispose();
+        }
+    }
+
+    /**
+     * Test the {@link TmfTraceUtils#getNextEventMatching} method.
+     */
+    @Test
+    public void testNextMatchingEvent() {
+        ITmfTrace trace = fTrace;
+        assertNotNull(trace);
+
+        Predicate<@NonNull ITmfEvent> predicate = event -> event.getName().equals("sched_switch");
+
+        ITmfEvent actualEvent = TmfTraceUtils.getNextEventMatching(trace, START_RANK, predicate);
+
+        ITmfContext ctx = trace.seekEvent(508L); // following sched_switch event
+        ITmfEvent expectedEvent = trace.getNext(ctx);
+
+        assertEquals(expectedEvent, actualEvent);
+    }
+
+    /**
+     * Test the {@link TmfTraceUtils#getNextEventMatching} method, where the
+     * event from which we start the search already matches the criterion (it
+     * should be correctly skipped so we can advance).
+     */
+    @Test
+    public void testNextMatchingEventStartMatches() {
+        ITmfTrace trace = fTrace;
+        assertNotNull(trace);
+
+        Predicate<@NonNull ITmfEvent> predicate = event -> event.getName().equals("softirq_raise");
+        ITmfEvent foundEvent = TmfTraceUtils.getNextEventMatching(trace, START_RANK, predicate);
+
+        assertNotNull(foundEvent);
+        assertNotEquals(fStartEvent, foundEvent);
+    }
+
+    /**
+     * Test the {@link TmfTraceUtils#getPreviousEventMatching} method.
+     */
+    @Test
+    public void testPreviousMatchingEvent() {
+        ITmfTrace trace = fTrace;
+        assertNotNull(trace);
+
+        Predicate<@NonNull ITmfEvent> predicate = event -> event.getName().equals("sched_switch");
+
+        ITmfEvent actualEvent = TmfTraceUtils.getPreviousEventMatching(trace, START_RANK, predicate);
+
+        ITmfContext ctx = trace.seekEvent(455L); // previous sched_switch event
+        ITmfEvent expectedEvent = trace.getNext(ctx);
+
+        assertEquals(expectedEvent, actualEvent);
+    }
+
+    /**
+     * Test the {@link TmfTraceUtils#getPreviousEventMatching} method, where the
+     * event from which we start the search already matches the criterion (it
+     * should be correctly skipped).
+     */
+    @Test
+    public void testPreviousMatchingEventStartMatches() {
+        ITmfTrace trace = fTrace;
+        assertNotNull(trace);
+
+        Predicate<@NonNull ITmfEvent> predicate = event -> event.getName().equals("softirq_raise");
+        ITmfEvent foundEvent = TmfTraceUtils.getPreviousEventMatching(trace, START_RANK, predicate);
+
+        assertNotNull(foundEvent);
+        assertNotEquals(fStartEvent, foundEvent);
+    }
+
+    /**
+     * Test the {@link TmfTraceUtils#getPreviousEventMatching} method with an
+     * event that is expected to take more than one inner request.
+     */
+    @Test
+    public void testPreviousMatchingEventFar() {
+        ITmfTrace trace = fTrace;
+        assertNotNull(trace);
+
+        Predicate<@NonNull ITmfEvent> predicate = event -> event.getName().equals("sys_write");
+        ITmfEvent actualEvent = TmfTraceUtils.getPreviousEventMatching(trace, START_RANK, predicate);
+
+        ITmfContext ctx = trace.seekEvent(387L); // previous sched_switch event
+        ITmfEvent expectedEvent = trace.getNext(ctx);
+
+        assertEquals(expectedEvent, actualEvent);
+    }
+
+    /**
+     * When doing a backwards search near the beginning of the trace (when
+     * startRank < step), make sure that we do not go beyond the start rank.
+     */
+    @Test
+    public void testPreviousMatchingBeginningOfTrace() {
+        ITmfTrace trace = fTrace;
+        assertNotNull(trace);
+
+        final int startRank = 3;
+        ITmfContext ctx = fTrace.seekEvent(startRank);
+        ITmfEvent startEvent = fTrace.getNext(ctx);
+        assertEquals("exit_syscall", startEvent.getName());
+
+        /* There is a sys_mmap at rank 6, it should not be matched! */
+        Predicate<@NonNull ITmfEvent> predicate = event -> event.getName().equals("sys_mmap");
+        ITmfEvent foundEvent = TmfTraceUtils.getPreviousEventMatching(trace, startRank, predicate);
+        assertNull(foundEvent);
+
+        /* Do not match the event itself either, or any later "exit_syscall" */
+        predicate = event -> event.getName().equals("exit_syscall");
+        foundEvent = TmfTraceUtils.getPreviousEventMatching(trace, startRank, predicate);
+        assertNull(foundEvent);
+    }
+}
index 31bb37d8b1c8f0a6fc78ee58bef1882f0f75a28a..084cf22076af7f6fbc1a1f8a99b8cc950bad29c1 100644 (file)
@@ -20,6 +20,7 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Predicate;
 
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -29,6 +30,7 @@ import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
 import org.eclipse.tracecompass.tmf.core.component.ITmfEventProvider;
 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
 import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect;
+import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
 
 import com.google.common.collect.Iterables;
 
@@ -175,4 +177,157 @@ public final class TmfTraceUtils {
         }
         return true;
     }
+
+    // ------------------------------------------------------------------------
+    // Event matching methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Retrieve from a trace the next event, from a starting rank, matching the
+     * given predicate.
+     *
+     * @param trace
+     *            The trace
+     * @param startRank
+     *            The rank of the event at which to start searching. Use
+     *            <code>0</code> to search from the start of the trace.
+     * @param predicate
+     *            The predicate to test events against
+     * @return The first event matching the predicate, or null if the end of the
+     *         trace was reached and no event was found
+     */
+    public static @Nullable ITmfEvent getNextEventMatching(ITmfTrace trace, long startRank, Predicate<ITmfEvent> predicate) {
+        /* rank + 1 because we do not want to include the start event itself in the search */
+        EventMatchingRequest req = new EventMatchingRequest(startRank + 1, predicate, false);
+        trace.sendRequest(req);
+        try {
+            req.waitForCompletion();
+        } catch (InterruptedException e) {
+            return null;
+        }
+
+        return req.getFoundEvent();
+    }
+
+    /**
+     * Retrieve from a trace the previous event, from a given rank, matching the
+     * given predicate.
+     *
+     * @param trace
+     *            The trace
+     * @param startRank
+     *            The rank of the event at which to start searching backwards.
+     * @param predicate
+     *            The predicate to test events against
+     * @return The first event found matching the predicate, or null if the
+     *         beginning of the trace was reached and no event was found
+     */
+    public static @Nullable ITmfEvent getPreviousEventMatching(ITmfTrace trace, long startRank, Predicate<ITmfEvent> predicate) {
+        /*
+         * Slightly less straightforward since we unfortunately cannot iterate
+         * backwards on events. Do a series of forward-queries.
+         */
+        final int targetStep = 100;
+
+        /*
+         * If we are close to the beginning of the trace, make sure we only look
+         * for the events before the startRank.
+         */
+        int step = (startRank < targetStep ? (int) startRank : targetStep);
+
+        long currentRank = startRank;
+        try {
+            while (currentRank > 0) {
+                currentRank = Math.max(currentRank - step, 0);
+
+                EventMatchingRequest req = new EventMatchingRequest(currentRank, step, predicate, true);
+                trace.sendRequest(req);
+                req.waitForCompletion();
+
+                ITmfEvent event = req.getFoundEvent();
+                if (event != null) {
+                    /* We found an actual event, return it! */
+                    return event;
+                }
+                /* Keep searching, next loop */
+
+            }
+        } catch (InterruptedException e) {
+            return null;
+        }
+
+        /*
+         * We searched up to the beginning of the trace and didn't find
+         * anything.
+         */
+        return null;
+
+    }
+
+    /**
+     * Event request looking for an event matching a Predicate.
+     */
+    private static class EventMatchingRequest extends TmfEventRequest {
+
+        private final Predicate<ITmfEvent> fPredicate;
+        private final boolean fReturnLast;
+
+        private @Nullable ITmfEvent fFoundEvent = null;
+
+        /**
+         * Basic constructor, will query the trace until the end.
+         *
+         * @param startRank
+         *            The rank at which to start, use 0 for the beginning
+         * @param predicate
+         *            The predicate to test against each event
+         * @param returnLast
+         *            Should we return the last or first event found. If false,
+         *            the request ends as soon as a matching event is found. If
+         *            false, we will go through all events to find a possible
+         *            last-match.
+         */
+        public EventMatchingRequest(long startRank, Predicate<ITmfEvent> predicate, boolean returnLast) {
+            super(ITmfEvent.class, startRank, ALL_DATA, ExecutionType.FOREGROUND);
+            fPredicate = predicate;
+            fReturnLast = returnLast;
+        }
+
+        /**
+         * Basic constructor, will query the trace the limit is reached.
+         *
+         * @param startRank
+         *            The rank at which to start, use 0 for the beginning
+         * @param limit
+         *            The limit on the number of events
+         * @param predicate
+         *            The predicate to test against each event
+         * @param returnLast
+         *            Should we return the last or first event found. If false,
+         *            the request ends as soon as a matching event is found. If
+         *            false, we will go through all events to find a possible
+         *            last-match.
+         */
+        public EventMatchingRequest(long startRank, int limit, Predicate<ITmfEvent> predicate, boolean returnLast) {
+            super(ITmfEvent.class, startRank, limit, ExecutionType.FOREGROUND);
+            fPredicate = predicate;
+            fReturnLast = returnLast;
+        }
+
+        public @Nullable ITmfEvent getFoundEvent() {
+            return fFoundEvent;
+        }
+
+        @Override
+        public void handleData(ITmfEvent event) {
+            super.handleData(event);
+
+            if (fPredicate.test(event)) {
+                fFoundEvent = event;
+                if (!fReturnLast) {
+                    this.done();
+                }
+            }
+        }
+    }
 }
This page took 0.030418 seconds and 5 git commands to generate.