1 /*******************************************************************************
2 * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *******************************************************************************/
10 package org
.eclipse
.tracecompass
.tmf
.ctf
.core
.tests
.trim
;
12 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
13 import static org
.junit
.Assert
.assertEquals
;
14 import static org
.junit
.Assert
.assertNotNull
;
15 import static org
.junit
.Assert
.assertTrue
;
16 import static org
.junit
.Assert
.fail
;
18 import java
.io
.IOException
;
19 import java
.nio
.file
.Files
;
20 import java
.nio
.file
.Path
;
21 import java
.util
.Arrays
;
22 import java
.util
.Objects
;
24 import java
.util
.concurrent
.TimeUnit
;
25 import java
.util
.stream
.Collectors
;
27 import org
.apache
.commons
.io
.FileUtils
;
28 import org
.eclipse
.core
.runtime
.CoreException
;
29 import org
.eclipse
.core
.runtime
.IStatus
;
30 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
31 import org
.eclipse
.jdt
.annotation
.NonNull
;
32 import org
.eclipse
.tracecompass
.testtraces
.ctf
.CtfTestTrace
;
33 import org
.eclipse
.tracecompass
.tmf
.core
.event
.TmfEvent
;
34 import org
.eclipse
.tracecompass
.tmf
.core
.exceptions
.TmfTraceException
;
35 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalManager
;
36 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceOpenedSignal
;
37 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimeRange
;
38 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimestamp
;
39 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfContext
;
40 import org
.eclipse
.tracecompass
.tmf
.ctf
.core
.event
.CtfTmfEvent
;
41 import org
.eclipse
.tracecompass
.tmf
.ctf
.core
.tests
.shared
.CtfTmfTestTraceUtils
;
42 import org
.eclipse
.tracecompass
.tmf
.ctf
.core
.trace
.CtfTmfTrace
;
43 import org
.junit
.After
;
44 import org
.junit
.Before
;
45 import org
.junit
.Ignore
;
46 import org
.junit
.Rule
;
47 import org
.junit
.Test
;
48 import org
.junit
.rules
.TestRule
;
49 import org
.junit
.rules
.Timeout
;
50 import org
.junit
.runner
.RunWith
;
51 import org
.junit
.runners
.Parameterized
;
52 import org
.junit
.runners
.Parameterized
.Parameters
;
54 import com
.google
.common
.collect
.ImmutableSet
;
57 * Tests related to the trimming feature of CTF traces
58 * ({@link CtfTmfTrace#trim}).
60 * @author Alexandre Montplaisir
62 @Ignore("Trim command requires Babeltrace 2.0, which is not installed on most CIs. Test can be run manually.")
63 @RunWith(Parameterized
.class)
64 public class CtfTmfTraceTrimmingTest
{
67 @Rule public TestRule globalTimeout
= new Timeout(5, TimeUnit
.MINUTES
);
69 private final @NonNull CtfTestTrace fTestTrace
;
71 private CtfTmfTrace fOriginalTrace
;
72 private long fRequestedTraceCutStart
;
73 private long fRequestedTraceCutEnd
;
75 private CtfTmfTrace fNewTrace
;
76 private Path fNewTracePath
;
78 // ------------------------------------------------------------------------
79 // Test suite definition
80 // ------------------------------------------------------------------------
82 @SuppressWarnings("deprecation")
83 private static final Set
<CtfTestTrace
> BLACKLISTED_TRACES
= ImmutableSet
.of(
85 * Babeltrace 2 currently fails reading the following traces. This is a reader
86 * bug, unrelated to the trace trimming operation.
88 * See https://bugs.lttng.org/issues/1121 .
90 * TODO Un-ignore these once BT2 is fixed.
92 CtfTestTrace
.FUNKY_TRACE
,
93 CtfTestTrace
.DYNSCOPE
,
96 * Ignore the deprecated DEBUG_INFO traces. Those were not generated by a
97 * release version of lttng-ust.
99 CtfTestTrace
.DEBUG_INFO
,
100 CtfTestTrace
.DEBUG_INFO3
,
103 * Ignore hello-lost, most of the trace range is lost events, so cutting would
104 * give an empty trace.
106 CtfTestTrace
.HELLO_LOST
,
108 /* Trimming doesn't work on experiments at the moment */
109 CtfTestTrace
.TRACE_EXPERIMENT
113 * Test parameter generator
115 * @return The list of constructor parameters, one for each test instance.
117 @Parameters(name
= "{index}: {0}")
118 public static Iterable
<Object
[]> getTestTraces() {
119 CtfTestTrace
[] testTraces
= CtfTestTrace
.values();
120 return Arrays
.stream(testTraces
)
121 .filter(testTrace
-> !BLACKLISTED_TRACES
.contains(testTrace
))
122 .map(testTrace
-> new Object
[] { testTrace
})
123 .collect(Collectors
.toList());
127 * Constructor. Receives parameters defined in {@link #getTestTraces()}.
130 * The test trace to use for this test instance.
132 public CtfTmfTraceTrimmingTest(@NonNull CtfTestTrace testTrace
) {
133 fTestTrace
= testTrace
;
136 // ------------------------------------------------------------------------
137 // Test instance maintenance
138 // ------------------------------------------------------------------------
144 public void setup() {
145 fOriginalTrace
= CtfTmfTestTraceUtils
.getTrace(fTestTrace
);
146 openTrace(fOriginalTrace
);
148 fRequestedTraceCutStart
= getTraceCutStart(fOriginalTrace
);
149 fRequestedTraceCutEnd
= getTraceCutEnd(fOriginalTrace
);
151 assertTrue(fRequestedTraceCutStart
>= fOriginalTrace
.getStartTime().toNanos());
152 assertTrue(fRequestedTraceCutEnd
<= fOriginalTrace
.getEndTime().toNanos());
153 assertTrue(fRequestedTraceCutStart
< fRequestedTraceCutEnd
);
155 TmfTimeRange range
= new TmfTimeRange(
156 TmfTimestamp
.fromNanos(fRequestedTraceCutStart
),
157 TmfTimestamp
.fromNanos(fRequestedTraceCutEnd
));
159 /* Perform the trim to create the new trace */
160 fNewTracePath
= checkNotNull(Files
.createTempDirectory("trimmed-trace-test"));
161 fOriginalTrace
.trim(range
, fNewTracePath
, new NullProgressMonitor());
163 /* Initialize the new trace */
164 fNewTrace
= new CtfTmfTrace();
165 fNewTrace
.initTrace(null, fNewTracePath
.toString(), CtfTmfEvent
.class);
166 openTrace(fNewTrace
);
168 } catch (IOException
| TmfTraceException e
) {
169 fail(e
.getMessage());
170 } catch (CoreException e
) {
172 * CoreException are more or less useless, all the interesting stuff
173 * is in their "status" objects.
176 IStatus status
= e
.getStatus();
177 IStatus
[] children
= status
.getChildren();
178 if (children
== null) {
179 msg
= status
.getMessage();
181 msg
= Arrays
.stream(children
)
182 .map(IStatus
::getMessage
)
183 .collect(Collectors
.joining("\n"));
193 public void tearDown() {
194 if (fOriginalTrace
!= null) {
195 fOriginalTrace
.dispose();
197 CtfTmfTestTraceUtils
.dispose(fTestTrace
);
199 if (fNewTrace
!= null) {
203 if (fNewTracePath
!= null) {
204 FileUtils
.deleteQuietly(fNewTracePath
.toFile());
208 /** Simulate a trace being opened */
209 private static void openTrace(CtfTmfTrace trace
) {
210 trace
.indexTrace(true);
211 TmfSignalManager
.dispatchSignal(new TmfTraceOpenedSignal(CtfTmfTraceTrimmingTest
.class, trace
, null));
215 * Get the timestamp at which we should start cutting the trace. It should
216 * be roughly 1/4 into the trace.
218 private static long getTraceCutStart(CtfTmfTrace trace
) {
219 long start
= trace
.getStartTime().toNanos();
220 long end
= trace
.getEndTime().toNanos();
222 return ((end
- start
) / 4) + start
;
226 * Get the timestamp at which we should end the trace cutting. It should be
227 * roughly at half the trace.
229 private static long getTraceCutEnd(CtfTmfTrace trace
) {
230 long start
= trace
.getStartTime().toNanos();
231 long end
= trace
.getEndTime().toNanos();
233 return ((end
- start
) / 2) + start
;
236 // ------------------------------------------------------------------------
237 // Test methods and helpers
238 // ------------------------------------------------------------------------
241 * Test that all expected events are present in the new trace.
244 public void testTrimEvents() {
245 CtfTmfTrace initialTrace
= fOriginalTrace
;
246 CtfTmfTrace trimmedTrace
= fNewTrace
;
247 Path newTracePath
= fNewTracePath
;
248 assertNotNull(initialTrace
);
249 assertNotNull(trimmedTrace
);
250 assertNotNull(newTracePath
);
253 * Verify the bounds of the new trace are fine. The actual trace can be
254 * smaller than what was requested if there are no events exactly at the
255 * bounds, but should not contain events outside of the requested range.
257 final long newTraceStartTime
= trimmedTrace
.getStartTime().toNanos();
258 final long newTraceEndTime
= trimmedTrace
.getEndTime().toNanos();
260 assertTrue("Cut trace start time " + newTraceStartTime
261 + " is earlier than the requested " + fRequestedTraceCutStart
,
262 newTraceStartTime
>= fRequestedTraceCutStart
);
264 assertTrue("Cut trace end time " + newTraceEndTime
265 + " is later than the requested " + fRequestedTraceCutEnd
,
266 newTraceEndTime
<= fRequestedTraceCutEnd
);
269 * Verify that each trace event from the original trace in the given
270 * time range is present in the new one.
272 ITmfContext context1
= initialTrace
.seekEvent(TmfTimestamp
.fromNanos(newTraceStartTime
));
273 CtfTmfEvent event1
= initialTrace
.getNext(context1
);
274 ITmfContext context2
= trimmedTrace
.seekEvent(0L);
275 CtfTmfEvent event2
= trimmedTrace
.getNext(context2
);
278 while (event1
.getTimestamp().toNanos() <= fRequestedTraceCutEnd
) {
279 assertNotNull(event1
);
280 assertNotNull("Expected event not present in trimmed trace: " + eventToString(event1
), event2
);
282 if (!eventsEquals(event1
, event2
)) {
284 * Skip the test for different events of the exact same timestamp. The library
285 * does not guarantee in which order events of the same timestamp are read.
287 if (Objects
.equals(event1
.getTimestamp(), event2
.getTimestamp())) {
288 System
.err
.println("The following events have the exact same timestamp, and may be read in any order:");
289 System
.err
.println(eventToString(event1
));
290 System
.err
.println(eventToString(event2
));
292 fail("The following events are not the same: \n "
293 + eventToString(event1
) + '\n'
294 + eventToString(event2
));
298 event1
= initialTrace
.getNext(context1
);
299 event2
= trimmedTrace
.getNext(context2
);
303 assertEquals(trimmedTrace
.getNbEvents(), count
);
307 * {@link TmfEvent#equals} checks the container trace, among other things.
308 * Here we want to compare events from different traces, so we have to
309 * implement our own equals().
311 private static boolean eventsEquals(CtfTmfEvent event1
, CtfTmfEvent event2
) {
312 return Objects
.equals(event1
.getTimestamp(), event2
.getTimestamp())
313 && Objects
.equals(event1
.getType(), event2
.getType())
314 && Objects
.equals(event1
.getContent(), event2
.getContent())
315 && Objects
.equals(event1
.getCPU(), event2
.getCPU());
317 // FIXME This currently gets renamed, but eventually won't.
318 // && Objects.equals(event1.getChannel(), event2.getChannel());
321 private static String
eventToString(CtfTmfEvent event
) {
322 return com
.google
.common
.base
.Objects
.toStringHelper(event
)
323 .add("Timestamp", event
.getTimestamp())
324 .add("Type", event
.getType())
325 .add("Content", event
.getContent())
326 .add("CPU", event
.getCPU())
327 .add("Channel", event
.getChannel())