--- /dev/null
+/*******************************************************************************
+ * 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);
+ }
+}
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;
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;
}
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();
+ }
+ }
+ }
+ }
}