1 /*******************************************************************************
2 * Copyright (c) 2014 Ericsson
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
10 * Alexandre Montplaisir - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.tracecompass
.tmf
.core
.trace
;
15 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
17 import java
.io
.BufferedInputStream
;
19 import java
.io
.FileInputStream
;
20 import java
.io
.IOException
;
21 import java
.util
.ArrayList
;
22 import java
.util
.List
;
23 import java
.util
.Optional
;
24 import java
.util
.function
.Predicate
;
26 import org
.eclipse
.jdt
.annotation
.NonNull
;
27 import org
.eclipse
.jdt
.annotation
.NonNullByDefault
;
28 import org
.eclipse
.jdt
.annotation
.Nullable
;
29 import org
.eclipse
.tracecompass
.common
.core
.StreamUtils
;
30 import org
.eclipse
.tracecompass
.tmf
.core
.analysis
.IAnalysisModule
;
31 import org
.eclipse
.tracecompass
.tmf
.core
.component
.ITmfEventProvider
;
32 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEvent
;
33 import org
.eclipse
.tracecompass
.tmf
.core
.event
.aspect
.ITmfEventAspect
;
34 import org
.eclipse
.tracecompass
.tmf
.core
.request
.TmfEventRequest
;
36 import com
.google
.common
.collect
.Iterables
;
37 import com
.google
.common
.collect
.Lists
;
40 * Utility methods for ITmfTrace's.
42 * @author Alexandre Montplaisir
45 public final class TmfTraceUtils
{
47 private static final int MAX_NB_BINARY_BYTES
= 2048;
49 private TmfTraceUtils() {
53 * Return the first result of the first analysis module belonging to this trace or its children,
54 * with the specified ID and class.
57 * The trace for which you want the modules
59 * Returned modules must extend this class
61 * The ID of the analysis module
62 * @return The analysis module with specified class and ID, or null if no
65 public static @Nullable <T
extends IAnalysisModule
> T
getAnalysisModuleOfClass(ITmfTrace trace
,
66 Class
<T
> moduleClass
, String id
) {
67 Iterable
<T
> modules
= getAnalysisModulesOfClass(trace
, moduleClass
);
68 for (T module
: modules
) {
69 if (id
.equals(module
.getId())) {
77 * Return the analysis modules that are of a given class. The modules will be
78 * cast to the requested class. If the trace has children, the childrens modules
82 * The trace for which you want the modules, the children trace modules
85 * Returned modules must extend this class
86 * @return List of modules of class moduleClass
88 public static <T
> Iterable
<@NonNull T
> getAnalysisModulesOfClass(ITmfTrace trace
, Class
<T
> moduleClass
) {
89 Iterable
<IAnalysisModule
> analysisModules
= trace
.getAnalysisModules();
90 List
<@NonNull T
> modules
= new ArrayList
<>();
91 for (IAnalysisModule module
: analysisModules
) {
92 if (moduleClass
.isAssignableFrom(module
.getClass())) {
93 modules
.add(checkNotNull(moduleClass
.cast(module
)));
96 for (ITmfEventProvider child
: trace
.getChildren()) {
97 if (child
instanceof ITmfTrace
) {
98 ITmfTrace childTrace
= (ITmfTrace
) child
;
99 Iterables
.addAll(modules
, getAnalysisModulesOfClass(childTrace
, moduleClass
));
106 * Return the first result of the first aspect that resolves as non null for
107 * the event received in parameter. If the returned value is not null, it
108 * can be safely cast to the aspect's class proper return type.
111 * The trace for which you want the event aspects
113 * The class of the aspect(s) to resolve
115 * The event for which to get the aspect
116 * @return The first result of the
117 * {@link ITmfEventAspect#resolve(ITmfEvent)} that returns non null
118 * for the event or {@code null} otherwise
120 public static <T
extends ITmfEventAspect
<?
>> @Nullable Object
resolveEventAspectOfClassForEvent(
121 ITmfTrace trace
, Class
<T
> aspectClass
, ITmfEvent event
) {
122 return StreamUtils
.getStream(trace
.getEventAspects())
123 .filter(aspect
-> aspectClass
.isAssignableFrom(aspect
.getClass()))
124 .map(aspect
-> aspect
.resolve(event
))
125 .filter(obj
-> obj
!= null)
126 .findFirst().orElse(null);
130 * Return the first result of the first aspect that resolves as a non-null
131 * Integer for the event received in parameter. If no matching aspects are
132 * found then null is returned.
135 * The trace for which you want the event aspects
137 * The class of the aspect(s) to resolve
139 * The event for which to get the aspect
140 * @return Integer of the first result of the
141 * {@link ITmfEventAspect#resolve(ITmfEvent)} that returns non null
142 * for the event or {@code null} otherwise
145 public static <T
extends ITmfEventAspect
<Integer
>> @Nullable Integer
resolveIntEventAspectOfClassForEvent(
146 ITmfTrace trace
, Class
<T
> aspectClass
, ITmfEvent event
) {
147 return StreamUtils
.getStream(trace
.getEventAspects())
148 .filter(aspect
-> aspectClass
.isAssignableFrom(aspect
.getClass()))
149 /* Enforced by the T parameter bounding */
150 .map(aspect
-> (Integer
) aspect
.resolve(event
))
151 .filter(obj
-> obj
!= null)
152 .findFirst().orElse(null);
156 * Checks for text file.
158 * Note that it checks for binary value 0 in the first MAX_NB_BINARY_BYTES
159 * bytes to determine if the file is text.
162 * the file to check. Caller has to make sure that file exists.
163 * @return true if it is binary else false
164 * @throws IOException
165 * if IOException occurs
168 public static boolean isText(File file
) throws IOException
{
169 try (BufferedInputStream bufferedInputStream
= new BufferedInputStream(new FileInputStream(file
))) {
171 int val
= bufferedInputStream
.read();
172 while ((count
< MAX_NB_BINARY_BYTES
) && (val
>= 0)) {
177 val
= bufferedInputStream
.read();
183 // ------------------------------------------------------------------------
184 // Event matching methods
185 // ------------------------------------------------------------------------
188 * Retrieve from a trace the next event, from a starting rank, matching the
194 * The rank of the event at which to start searching. Use
195 * <code>0</code> to search from the start of the trace.
197 * The predicate to test events against
198 * @return The first event matching the predicate, or null if the end of the
199 * trace was reached and no event was found
202 public static @Nullable ITmfEvent
getNextEventMatching(ITmfTrace trace
, long startRank
, Predicate
<ITmfEvent
> predicate
) {
203 /* rank + 1 because we do not want to include the start event itself in the search */
204 EventMatchingRequest req
= new EventMatchingRequest(startRank
+ 1, predicate
, false);
205 trace
.sendRequest(req
);
207 req
.waitForCompletion();
208 } catch (InterruptedException e
) {
212 return req
.getFoundEvent();
216 * Retrieve from a trace the previous event, from a given rank, matching the
222 * The rank of the event at which to start searching backwards.
224 * The predicate to test events against
225 * @return The first event found matching the predicate, or null if the
226 * beginning of the trace was reached and no event was found
229 public static @Nullable ITmfEvent
getPreviousEventMatching(ITmfTrace trace
, long startRank
, Predicate
<ITmfEvent
> predicate
) {
231 * We are going to do a series of queries matching the trace's cache
232 * size in length (which should minimize on-disk seeks), then iterate on
233 * the found events in reverse order until we find a match.
235 int step
= trace
.getCacheSize();
238 * If we are close to the beginning of the trace, make sure we only look
239 * for the events before the startRank.
241 if (startRank
< step
) {
242 step
= (int) startRank
;
245 long currentRank
= startRank
;
247 while (currentRank
> 0) {
248 currentRank
= Math
.max(currentRank
- step
, 0);
250 List
<ITmfEvent
> list
= new ArrayList
<>(step
);
251 ArrayFillingRequest req
= new ArrayFillingRequest(currentRank
, step
, list
);
252 trace
.sendRequest(req
);
253 req
.waitForCompletion();
255 Optional
<ITmfEvent
> matchingEvent
= Lists
.reverse(list
).stream()
259 if (matchingEvent
.isPresent()) {
260 /* We found an event matching, return it! */
261 return matchingEvent
.get();
263 /* Keep searching, next loop */
266 } catch (InterruptedException e
) {
271 * We searched up to the beginning of the trace and didn't find
279 * Event request looking for an event matching a Predicate.
281 private static class EventMatchingRequest
extends TmfEventRequest
{
283 private final Predicate
<ITmfEvent
> fPredicate
;
284 private final boolean fReturnLast
;
286 private @Nullable ITmfEvent fFoundEvent
= null;
289 * Basic constructor, will query the trace until the end.
292 * The rank at which to start, use 0 for the beginning
294 * The predicate to test against each event
296 * Should we return the last or first event found. If false,
297 * the request ends as soon as a matching event is found. If
298 * false, we will go through all events to find a possible
301 public EventMatchingRequest(long startRank
, Predicate
<ITmfEvent
> predicate
, boolean returnLast
) {
302 super(ITmfEvent
.class, startRank
, ALL_DATA
, ExecutionType
.FOREGROUND
);
303 fPredicate
= predicate
;
304 fReturnLast
= returnLast
;
308 * Basic constructor, will query the trace the limit is reached.
311 * The rank at which to start, use 0 for the beginning
313 * The limit on the number of events
315 * The predicate to test against each event
317 * Should we return the last or first event found. If false,
318 * the request ends as soon as a matching event is found. If
319 * false, we will go through all events to find a possible
322 public EventMatchingRequest(long startRank
, int limit
, Predicate
<ITmfEvent
> predicate
, boolean returnLast
) {
323 super(ITmfEvent
.class, startRank
, limit
, ExecutionType
.FOREGROUND
);
324 fPredicate
= predicate
;
325 fReturnLast
= returnLast
;
328 public @Nullable ITmfEvent
getFoundEvent() {
333 public void handleData(ITmfEvent event
) {
334 super.handleData(event
);
336 if (fPredicate
.test(event
)) {
346 * Event request that simply puts all returned events into a list passed in
349 private static class ArrayFillingRequest
extends TmfEventRequest
{
351 private final List
<ITmfEvent
> fList
;
353 public ArrayFillingRequest(long startRank
, int limit
, List
<ITmfEvent
> listToFill
) {
354 super(ITmfEvent
.class, startRank
, limit
, ExecutionType
.FOREGROUND
);
359 public void handleData(ITmfEvent event
) {
360 super.handleData(event
);