From e4bc4695e263b0f4867e9efc10dced5571fd06ff Mon Sep 17 00:00:00 2001 From: Alexandre Montplaisir Date: Tue, 14 Jun 2016 20:33:53 -0400 Subject: [PATCH] tmf: Add utility method to search for events matching a Predicate 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 --- .../META-INF/MANIFEST.MF | 1 + .../trace/TmfTraceUtilsSearchingTest.java | 177 ++++++++++++++++++ .../tmf/core/trace/TmfTraceUtils.java | 155 +++++++++++++++ 3 files changed, 333 insertions(+) create mode 100644 ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/src/org/eclipse/tracecompass/tmf/ctf/core/tests/trace/TmfTraceUtilsSearchingTest.java diff --git a/ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/META-INF/MANIFEST.MF b/ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/META-INF/MANIFEST.MF index 1a1ebb9f3a..fc85e49bfe 100644 --- a/ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/META-INF/MANIFEST.MF +++ b/ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/META-INF/MANIFEST.MF @@ -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 index 0000000000..b220989654 --- /dev/null +++ b/ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/src/org/eclipse/tracecompass/tmf/ctf/core/tests/trace/TmfTraceUtilsSearchingTest.java @@ -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); + } +} diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/TmfTraceUtils.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/TmfTraceUtils.java index 31bb37d8b1..084cf22076 100644 --- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/TmfTraceUtils.java +++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/TmfTraceUtils.java @@ -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 + * 0 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 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 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 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 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 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(); + } + } + } + } } -- 2.34.1