70fbd5a4dedef75fc9640b4ef449672b55c2a9ec
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / trace / TmfTraceUtils.java
1 /*******************************************************************************
2 * Copyright (c) 2014 Ericsson
3 *
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 *
9 * Contributors:
10 * Alexandre Montplaisir - Initial API and implementation
11 *******************************************************************************/
12
13 package org.eclipse.tracecompass.tmf.core.trace;
14
15 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
16
17 import java.io.BufferedInputStream;
18 import java.io.File;
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;
25 import java.util.Set;
26 import java.util.function.Predicate;
27
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;
38
39 import com.google.common.collect.Iterables;
40 import com.google.common.collect.Lists;
41
42 /**
43 * Utility methods for ITmfTrace's.
44 *
45 * @author Alexandre Montplaisir
46 */
47 @NonNullByDefault
48 public final class TmfTraceUtils {
49
50 private static final int MAX_NB_BINARY_BYTES = 2048;
51 private static final Set<ITmfEventAspect<?>> EXTRA_ASPECTS = new HashSet<>();
52
53 private TmfTraceUtils() {
54 }
55
56 /**
57 * Return the first result of the first analysis module belonging to this trace or its children,
58 * with the specified ID and class.
59 *
60 * @param trace
61 * The trace for which you want the modules
62 * @param moduleClass
63 * Returned modules must extend this class
64 * @param id
65 * The ID of the analysis module
66 * @return The analysis module with specified class and ID, or null if no
67 * such module exists.
68 */
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())) {
74 return module;
75 }
76 }
77 return null;
78 }
79
80 /**
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
83 * using the
84 * {@link #resolveEventAspectOfClassForEvent(ITmfTrace, Class, ITmfEvent)}
85 * method, but will not appear where trace aspects are being displayed.
86 *
87 * @param aspect
88 * The event aspect to register
89 * @since 3.0
90 */
91 public static void registerEventAspect(ITmfEventAspect<?> aspect) {
92 EXTRA_ASPECTS.add(aspect);
93 }
94
95 /**
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
98 * are also returned.
99 *
100 * @param trace
101 * The trace for which you want the modules, the children trace modules
102 * are added as well.
103 * @param moduleClass
104 * Returned modules must extend this class
105 * @return List of modules of class moduleClass
106 */
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)));
113 }
114 }
115 for (ITmfEventProvider child : trace.getChildren()) {
116 if (child instanceof ITmfTrace) {
117 ITmfTrace childTrace = (ITmfTrace) child;
118 Iterables.addAll(modules, getAnalysisModulesOfClass(childTrace, moduleClass));
119 }
120 }
121 return modules;
122 }
123
124 /**
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.
128 *
129 * @param trace
130 * The trace for which you want the event aspects
131 * @param aspectClass
132 * The class of the aspect(s) to resolve
133 * @param event
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
138 */
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);
147 if (value != null) {
148 return value;
149 }
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);
156 }
157
158 /**
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.
162 *
163 * @param trace
164 * The trace for which you want the event aspects
165 * @param aspectName
166 * The class of the aspect(s) to resolve
167 * @param event
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
172 * @since 3.0
173 */
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);
181 if (value != null) {
182 return value;
183 }
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);
188 if (value != null) {
189 return value;
190 }
191 }
192 }
193 return null;
194 }
195
196 /**
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.
200 *
201 * @param trace
202 * The trace for which you want the event aspects
203 * @param aspectClass
204 * The class of the aspect(s) to resolve
205 * @param event
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
210 * @since 2.0
211 */
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);
220 }
221
222 /**
223 * Checks for text file.
224 *
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.
227 *
228 * @param file
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
233 * @since 1.2
234 */
235 public static boolean isText(File file) throws IOException {
236 try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file))) {
237 int count = 0;
238 int val = bufferedInputStream.read();
239 while ((count < MAX_NB_BINARY_BYTES) && (val >= 0)) {
240 if (val == 0) {
241 return false;
242 }
243 count++;
244 val = bufferedInputStream.read();
245 }
246 }
247 return true;
248 }
249
250 // ------------------------------------------------------------------------
251 // Event matching methods
252 // ------------------------------------------------------------------------
253
254 /**
255 * Retrieve from a trace the next event, from a starting rank, matching the
256 * given predicate.
257 *
258 * @param trace
259 * The trace
260 * @param startRank
261 * The rank of the event at which to start searching. Use
262 * <code>0</code> to search from the start of the trace.
263 * @param predicate
264 * The predicate to test events against
265 * @param monitor
266 * Optional progress monitor that can be used to cancel the
267 * operation
268 * @return The first event matching the predicate, or null if the end of the
269 * trace was reached and no event was found
270 * @since 2.1
271 */
272 public static @Nullable ITmfEvent getNextEventMatching(ITmfTrace trace, long startRank,
273 Predicate<ITmfEvent> predicate, @Nullable IProgressMonitor monitor) {
274 if (monitor != null && monitor.isCanceled()) {
275 return null;
276 }
277
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);
281 try {
282 /* Check periodically if the job was cancelled */
283 req.waitForStart();
284 while (req.isRunning()) {
285 Thread.sleep(200);
286 if (monitor != null && monitor.isCanceled()) {
287 req.cancel();
288 return null;
289 }
290 }
291 req.waitForCompletion();
292 } catch (InterruptedException e) {
293 return null;
294 }
295
296 return req.getFoundEvent();
297 }
298
299 /**
300 * Retrieve from a trace the previous event, from a given rank, matching the
301 * given predicate.
302 *
303 * @param trace
304 * The trace
305 * @param startRank
306 * The rank of the event at which to start searching backwards.
307 * @param predicate
308 * The predicate to test events against
309 * @param monitor Optional progress monitor that can be used to cancel the
310 * operation
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
313 * @since 2.1
314 */
315 public static @Nullable ITmfEvent getPreviousEventMatching(ITmfTrace trace, long startRank,
316 Predicate<ITmfEvent> predicate, @Nullable IProgressMonitor monitor) {
317 if (monitor != null && monitor.isCanceled()) {
318 return null;
319 }
320 /*
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.
324 */
325 int step = trace.getCacheSize();
326
327 /*
328 * If we are close to the beginning of the trace, make sure we only look
329 * for the events before the startRank.
330 */
331 if (startRank < step) {
332 step = (int) startRank;
333 }
334
335 long currentRank = startRank;
336 try {
337 while (currentRank > 0) {
338 currentRank = Math.max(currentRank - step, 0);
339
340 List<ITmfEvent> list = new ArrayList<>(step);
341 ArrayFillingRequest req = new ArrayFillingRequest(currentRank, step, list);
342 trace.sendRequest(req);
343
344 /* Check periodically if the job was cancelled */
345 req.waitForStart();
346 while (req.isRunning()) {
347 Thread.sleep(200);
348 if (monitor != null && monitor.isCanceled()) {
349 req.cancel();
350 return null;
351 }
352 }
353 req.waitForCompletion();
354
355 Optional<ITmfEvent> matchingEvent = Lists.reverse(list).stream()
356 .filter(predicate)
357 .findFirst();
358
359 if (matchingEvent.isPresent()) {
360 /* We found an event matching, return it! */
361 return matchingEvent.get();
362 }
363 /* Keep searching, next loop */
364
365 }
366 } catch (InterruptedException e) {
367 return null;
368 }
369
370 /*
371 * We searched up to the beginning of the trace and didn't find
372 * anything.
373 */
374 return null;
375
376 }
377
378 /**
379 * Event request looking for an event matching a Predicate.
380 */
381 private static class EventMatchingRequest extends TmfEventRequest {
382
383 private final Predicate<ITmfEvent> fPredicate;
384 private final boolean fReturnLast;
385
386 private @Nullable ITmfEvent fFoundEvent = null;
387
388 /**
389 * Basic constructor, will query the trace until the end.
390 *
391 * @param startRank
392 * The rank at which to start, use 0 for the beginning
393 * @param predicate
394 * The predicate to test against each event
395 * @param returnLast
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
399 * last-match.
400 */
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;
405 }
406
407 /**
408 * Basic constructor, will query the trace the limit is reached.
409 *
410 * @param startRank
411 * The rank at which to start, use 0 for the beginning
412 * @param limit
413 * The limit on the number of events
414 * @param predicate
415 * The predicate to test against each event
416 * @param returnLast
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
420 * last-match.
421 */
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;
426 }
427
428 public @Nullable ITmfEvent getFoundEvent() {
429 return fFoundEvent;
430 }
431
432 @Override
433 public void handleData(ITmfEvent event) {
434 super.handleData(event);
435
436 if (fPredicate.test(event)) {
437 fFoundEvent = event;
438 if (!fReturnLast) {
439 this.done();
440 }
441 }
442 }
443 }
444
445 /**
446 * Event request that simply puts all returned events into a list passed in
447 * parameter.
448 */
449 private static class ArrayFillingRequest extends TmfEventRequest {
450
451 private final List<ITmfEvent> fList;
452
453 public ArrayFillingRequest(long startRank, int limit, List<ITmfEvent> listToFill) {
454 super(ITmfEvent.class, startRank, limit, ExecutionType.FOREGROUND);
455 fList = listToFill;
456 }
457
458 @Override
459 public void handleData(ITmfEvent event) {
460 super.handleData(event);
461 fList.add(event);
462 }
463
464 }
465 }
This page took 0.039993 seconds and 4 git commands to generate.