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
.HashSet
;
23 import java
.util
.List
;
24 import java
.util
.Optional
;
26 import java
.util
.function
.Predicate
;
28 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
29 import org
.eclipse
.jdt
.annotation
.NonNull
;
30 import org
.eclipse
.jdt
.annotation
.NonNullByDefault
;
31 import org
.eclipse
.jdt
.annotation
.Nullable
;
32 import org
.eclipse
.tracecompass
.common
.core
.StreamUtils
;
33 import org
.eclipse
.tracecompass
.tmf
.core
.analysis
.IAnalysisModule
;
34 import org
.eclipse
.tracecompass
.tmf
.core
.component
.ITmfEventProvider
;
35 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEvent
;
36 import org
.eclipse
.tracecompass
.tmf
.core
.event
.aspect
.ITmfEventAspect
;
37 import org
.eclipse
.tracecompass
.tmf
.core
.request
.TmfEventRequest
;
39 import com
.google
.common
.collect
.Iterables
;
40 import com
.google
.common
.collect
.Lists
;
43 * Utility methods for ITmfTrace's.
45 * @author Alexandre Montplaisir
48 public final class TmfTraceUtils
{
50 private static final int MAX_NB_BINARY_BYTES
= 2048;
51 private static final Set
<ITmfEventAspect
<?
>> EXTRA_ASPECTS
= new HashSet
<>();
53 private TmfTraceUtils() {
57 * Return the first result of the first analysis module belonging to this trace or its children,
58 * with the specified ID and class.
61 * The trace for which you want the modules
63 * Returned modules must extend this class
65 * The ID of the analysis module
66 * @return The analysis module with specified class and ID, or null if no
69 public static @Nullable <T
extends IAnalysisModule
> T
getAnalysisModuleOfClass(ITmfTrace trace
,
70 Class
<T
> moduleClass
, String id
) {
71 Iterable
<T
> modules
= getAnalysisModulesOfClass(trace
, moduleClass
);
72 for (T module
: modules
) {
73 if (id
.equals(module
.getId())) {
81 * Registers an extra event aspect that may apply to all traces, not
82 * directly linked to a trace type. These can be used to retrieve values
84 * {@link #resolveEventAspectOfClassForEvent(ITmfTrace, Class, ITmfEvent)}
85 * method, but will not appear where trace aspects are being displayed.
88 * The event aspect to register
91 public static void registerEventAspect(ITmfEventAspect
<?
> aspect
) {
92 EXTRA_ASPECTS
.add(aspect
);
96 * Return the analysis modules that are of a given class. The modules will be
97 * cast to the requested class. If the trace has children, the childrens modules
101 * The trace for which you want the modules, the children trace modules
104 * Returned modules must extend this class
105 * @return List of modules of class moduleClass
107 public static <T
> Iterable
<@NonNull T
> getAnalysisModulesOfClass(ITmfTrace trace
, Class
<T
> moduleClass
) {
108 Iterable
<IAnalysisModule
> analysisModules
= trace
.getAnalysisModules();
109 List
<@NonNull T
> modules
= new ArrayList
<>();
110 for (IAnalysisModule module
: analysisModules
) {
111 if (moduleClass
.isAssignableFrom(module
.getClass())) {
112 modules
.add(checkNotNull(moduleClass
.cast(module
)));
115 for (ITmfEventProvider child
: trace
.getChildren()) {
116 if (child
instanceof ITmfTrace
) {
117 ITmfTrace childTrace
= (ITmfTrace
) child
;
118 Iterables
.addAll(modules
, getAnalysisModulesOfClass(childTrace
, moduleClass
));
125 * Return the first result of the first aspect that resolves as non null for
126 * the event received in parameter. If the returned value is not null, it
127 * can be safely cast to the aspect's class proper return type.
130 * The trace for which you want the event aspects
132 * The class of the aspect(s) to resolve
134 * The event for which to get the aspect
135 * @return The first result of the
136 * {@link ITmfEventAspect#resolve(ITmfEvent)} that returns non null
137 * for the event or {@code null} otherwise
139 public static <T
extends ITmfEventAspect
<?
>> @Nullable Object
resolveEventAspectOfClassForEvent(
140 ITmfTrace trace
, Class
<T
> aspectClass
, ITmfEvent event
) {
141 // First look in the trace aspects
142 Object value
= StreamUtils
.getStream(trace
.getEventAspects())
143 .filter(aspect
-> aspectClass
.isAssignableFrom(aspect
.getClass()))
144 .map(aspect
-> aspect
.resolve(event
))
145 .filter(obj
-> obj
!= null)
146 .findFirst().orElse(null);
150 // If the value is not found, look at the global aspects
151 return EXTRA_ASPECTS
.stream()
152 .filter(aspect
-> aspectClass
.isAssignableFrom(aspect
.getClass()))
153 .map(aspect
-> aspect
.resolve(event
))
154 .filter(obj
-> obj
!= null)
155 .findFirst().orElse(null);
159 * Return the first result of the first aspect that resolves as non null for
160 * the event received in parameter. If the returned value is not null, it
161 * can be safely cast to the aspect's class proper return type.
164 * The trace for which you want the event aspects
166 * The class of the aspect(s) to resolve
168 * The event for which to get the aspect
169 * @return The first result of the
170 * {@link ITmfEventAspect#resolve(ITmfEvent)} that returns non null
171 * for the event or {@code null} otherwise
174 public static @Nullable Object
resolveAspectOfNameForEvent(ITmfTrace trace
, String aspectName
, ITmfEvent event
) {
175 // First look in the trace aspects
176 Object value
= StreamUtils
.getStream(trace
.getEventAspects())
177 .filter(aspect
-> aspectName
.equalsIgnoreCase(aspect
.getName()))
178 .map(aspect
-> aspect
.resolve(event
))
179 .filter(obj
-> obj
!= null)
180 .findFirst().orElse(null);
184 // If the value is not found, look at the global aspects
185 for (ITmfEventAspect
<?
> aspect
: EXTRA_ASPECTS
) {
186 if (aspectName
.equalsIgnoreCase(aspect
.getName())) {
187 value
= aspect
.resolve(event
);
197 * Return the first result of the first aspect that resolves as a non-null
198 * Integer for the event received in parameter. If no matching aspects are
199 * found then null is returned.
202 * The trace for which you want the event aspects
204 * The class of the aspect(s) to resolve
206 * The event for which to get the aspect
207 * @return Integer of the first result of the
208 * {@link ITmfEventAspect#resolve(ITmfEvent)} that returns non null
209 * for the event or {@code null} otherwise
212 public static <T
extends ITmfEventAspect
<Integer
>> @Nullable Integer
resolveIntEventAspectOfClassForEvent(
213 ITmfTrace trace
, Class
<T
> aspectClass
, ITmfEvent event
) {
214 return StreamUtils
.getStream(trace
.getEventAspects())
215 .filter(aspect
-> aspectClass
.isAssignableFrom(aspect
.getClass()))
216 /* Enforced by the T parameter bounding */
217 .map(aspect
-> (Integer
) aspect
.resolve(event
))
218 .filter(obj
-> obj
!= null)
219 .findFirst().orElse(null);
223 * Checks for text file.
225 * Note that it checks for binary value 0 in the first MAX_NB_BINARY_BYTES
226 * bytes to determine if the file is text.
229 * the file to check. Caller has to make sure that file exists.
230 * @return true if it is binary else false
231 * @throws IOException
232 * if IOException occurs
235 public static boolean isText(File file
) throws IOException
{
236 try (BufferedInputStream bufferedInputStream
= new BufferedInputStream(new FileInputStream(file
))) {
238 int val
= bufferedInputStream
.read();
239 while ((count
< MAX_NB_BINARY_BYTES
) && (val
>= 0)) {
244 val
= bufferedInputStream
.read();
250 // ------------------------------------------------------------------------
251 // Event matching methods
252 // ------------------------------------------------------------------------
255 * Retrieve from a trace the next event, from a starting rank, matching the
261 * The rank of the event at which to start searching. Use
262 * <code>0</code> to search from the start of the trace.
264 * The predicate to test events against
266 * Optional progress monitor that can be used to cancel the
268 * @return The first event matching the predicate, or null if the end of the
269 * trace was reached and no event was found
272 public static @Nullable ITmfEvent
getNextEventMatching(ITmfTrace trace
, long startRank
,
273 Predicate
<ITmfEvent
> predicate
, @Nullable IProgressMonitor monitor
) {
274 if (monitor
!= null && monitor
.isCanceled()) {
278 /* rank + 1 because we do not want to include the start event itself in the search */
279 EventMatchingRequest req
= new EventMatchingRequest(startRank
+ 1, predicate
, false);
280 trace
.sendRequest(req
);
282 /* Check periodically if the job was cancelled */
284 while (req
.isRunning()) {
286 if (monitor
!= null && monitor
.isCanceled()) {
291 req
.waitForCompletion();
292 } catch (InterruptedException e
) {
296 return req
.getFoundEvent();
300 * Retrieve from a trace the previous event, from a given rank, matching the
306 * The rank of the event at which to start searching backwards.
308 * The predicate to test events against
309 * @param monitor Optional progress monitor that can be used to cancel the
311 * @return The first event found matching the predicate, or null if the
312 * beginning of the trace was reached and no event was found
315 public static @Nullable ITmfEvent
getPreviousEventMatching(ITmfTrace trace
, long startRank
,
316 Predicate
<ITmfEvent
> predicate
, @Nullable IProgressMonitor monitor
) {
317 if (monitor
!= null && monitor
.isCanceled()) {
321 * We are going to do a series of queries matching the trace's cache
322 * size in length (which should minimize on-disk seeks), then iterate on
323 * the found events in reverse order until we find a match.
325 int step
= trace
.getCacheSize();
328 * If we are close to the beginning of the trace, make sure we only look
329 * for the events before the startRank.
331 if (startRank
< step
) {
332 step
= (int) startRank
;
335 long currentRank
= startRank
;
337 while (currentRank
> 0) {
338 currentRank
= Math
.max(currentRank
- step
, 0);
340 List
<ITmfEvent
> list
= new ArrayList
<>(step
);
341 ArrayFillingRequest req
= new ArrayFillingRequest(currentRank
, step
, list
);
342 trace
.sendRequest(req
);
344 /* Check periodically if the job was cancelled */
346 while (req
.isRunning()) {
348 if (monitor
!= null && monitor
.isCanceled()) {
353 req
.waitForCompletion();
355 Optional
<ITmfEvent
> matchingEvent
= Lists
.reverse(list
).stream()
359 if (matchingEvent
.isPresent()) {
360 /* We found an event matching, return it! */
361 return matchingEvent
.get();
363 /* Keep searching, next loop */
366 } catch (InterruptedException e
) {
371 * We searched up to the beginning of the trace and didn't find
379 * Event request looking for an event matching a Predicate.
381 private static class EventMatchingRequest
extends TmfEventRequest
{
383 private final Predicate
<ITmfEvent
> fPredicate
;
384 private final boolean fReturnLast
;
386 private @Nullable ITmfEvent fFoundEvent
= null;
389 * Basic constructor, will query the trace until the end.
392 * The rank at which to start, use 0 for the beginning
394 * The predicate to test against each event
396 * Should we return the last or first event found. If false,
397 * the request ends as soon as a matching event is found. If
398 * false, we will go through all events to find a possible
401 public EventMatchingRequest(long startRank
, Predicate
<ITmfEvent
> predicate
, boolean returnLast
) {
402 super(ITmfEvent
.class, startRank
, ALL_DATA
, ExecutionType
.FOREGROUND
);
403 fPredicate
= predicate
;
404 fReturnLast
= returnLast
;
408 * Basic constructor, will query the trace the limit is reached.
411 * The rank at which to start, use 0 for the beginning
413 * The limit on the number of events
415 * The predicate to test against each event
417 * Should we return the last or first event found. If false,
418 * the request ends as soon as a matching event is found. If
419 * false, we will go through all events to find a possible
422 public EventMatchingRequest(long startRank
, int limit
, Predicate
<ITmfEvent
> predicate
, boolean returnLast
) {
423 super(ITmfEvent
.class, startRank
, limit
, ExecutionType
.FOREGROUND
);
424 fPredicate
= predicate
;
425 fReturnLast
= returnLast
;
428 public @Nullable ITmfEvent
getFoundEvent() {
433 public void handleData(ITmfEvent event
) {
434 super.handleData(event
);
436 if (fPredicate
.test(event
)) {
446 * Event request that simply puts all returned events into a list passed in
449 private static class ArrayFillingRequest
extends TmfEventRequest
{
451 private final List
<ITmfEvent
> fList
;
453 public ArrayFillingRequest(long startRank
, int limit
, List
<ITmfEvent
> listToFill
) {
454 super(ITmfEvent
.class, startRank
, limit
, ExecutionType
.FOREGROUND
);
459 public void handleData(ITmfEvent event
) {
460 super.handleData(event
);