Commit | Line | Data |
---|---|---|
b8585c7c AM |
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 | ||
aa353506 AM |
15 | import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; |
16 | ||
d26d67f5 BH |
17 | import java.io.BufferedInputStream; |
18 | import java.io.File; | |
19 | import java.io.FileInputStream; | |
20 | import java.io.IOException; | |
daf3c7eb | 21 | import java.util.ArrayList; |
cf392b73 | 22 | import java.util.HashSet; |
daf3c7eb | 23 | import java.util.List; |
c93f91d9 | 24 | import java.util.Optional; |
cf392b73 | 25 | import java.util.Set; |
dba71686 | 26 | import java.util.function.Predicate; |
b8585c7c | 27 | |
f8a23812 | 28 | import org.eclipse.core.runtime.IProgressMonitor; |
df2597e0 | 29 | import org.eclipse.jdt.annotation.NonNull; |
1d83ed07 | 30 | import org.eclipse.jdt.annotation.NonNullByDefault; |
b8585c7c | 31 | import org.eclipse.jdt.annotation.Nullable; |
c15e897d | 32 | import org.eclipse.tracecompass.common.core.StreamUtils; |
b8585c7c | 33 | import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule; |
daf3c7eb | 34 | import org.eclipse.tracecompass.tmf.core.component.ITmfEventProvider; |
b1aad44e | 35 | import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; |
35f39420 | 36 | import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect; |
dba71686 | 37 | import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest; |
b8585c7c | 38 | |
daf3c7eb | 39 | import com.google.common.collect.Iterables; |
c93f91d9 | 40 | import com.google.common.collect.Lists; |
daf3c7eb | 41 | |
b8585c7c AM |
42 | /** |
43 | * Utility methods for ITmfTrace's. | |
44 | * | |
45 | * @author Alexandre Montplaisir | |
46 | */ | |
1d83ed07 | 47 | @NonNullByDefault |
b8585c7c AM |
48 | public final class TmfTraceUtils { |
49 | ||
d26d67f5 | 50 | private static final int MAX_NB_BINARY_BYTES = 2048; |
cf392b73 | 51 | private static final Set<ITmfEventAspect<?>> EXTRA_ASPECTS = new HashSet<>(); |
d26d67f5 | 52 | |
b1aad44e GB |
53 | private TmfTraceUtils() { |
54 | } | |
b8585c7c AM |
55 | |
56 | /** | |
daf3c7eb MK |
57 | * Return the first result of the first analysis module belonging to this trace or its children, |
58 | * with the specified ID and class. | |
b8585c7c AM |
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 | ||
cf392b73 GB |
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 | |
4b40a764 | 89 | * @since 3.0 |
cf392b73 GB |
90 | */ |
91 | public static void registerEventAspect(ITmfEventAspect<?> aspect) { | |
92 | EXTRA_ASPECTS.add(aspect); | |
93 | } | |
94 | ||
b8585c7c | 95 | /** |
daf3c7eb MK |
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. | |
b8585c7c AM |
99 | * |
100 | * @param trace | |
daf3c7eb MK |
101 | * The trace for which you want the modules, the children trace modules |
102 | * are added as well. | |
b8585c7c AM |
103 | * @param moduleClass |
104 | * Returned modules must extend this class | |
105 | * @return List of modules of class moduleClass | |
106 | */ | |
df2597e0 | 107 | public static <T> Iterable<@NonNull T> getAnalysisModulesOfClass(ITmfTrace trace, Class<T> moduleClass) { |
b8585c7c | 108 | Iterable<IAnalysisModule> analysisModules = trace.getAnalysisModules(); |
daf3c7eb | 109 | List<@NonNull T> modules = new ArrayList<>(); |
b1aad44e | 110 | for (IAnalysisModule module : analysisModules) { |
b8585c7c | 111 | if (moduleClass.isAssignableFrom(module.getClass())) { |
aa353506 | 112 | modules.add(checkNotNull(moduleClass.cast(module))); |
b8585c7c AM |
113 | } |
114 | } | |
daf3c7eb MK |
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 | } | |
b8585c7c AM |
121 | return modules; |
122 | } | |
35f39420 AM |
123 | |
124 | /** | |
b1aad44e GB |
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. | |
35f39420 AM |
128 | * |
129 | * @param trace | |
130 | * The trace for which you want the event aspects | |
131 | * @param aspectClass | |
b1aad44e GB |
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 | |
35f39420 | 138 | */ |
c15e897d | 139 | public static <T extends ITmfEventAspect<?>> @Nullable Object resolveEventAspectOfClassForEvent( |
1d83ed07 | 140 | ITmfTrace trace, Class<T> aspectClass, ITmfEvent event) { |
cf392b73 GB |
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); | |
35f39420 | 156 | } |
d26d67f5 | 157 | |
f75806c1 GB |
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 | |
4b40a764 | 172 | * @since 3.0 |
f75806c1 GB |
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 | ||
b3867ecc | 196 | /** |
c15e897d AM |
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. | |
b3867ecc MAL |
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 | */ | |
c15e897d | 212 | public static <T extends ITmfEventAspect<Integer>> @Nullable Integer resolveIntEventAspectOfClassForEvent( |
b3867ecc | 213 | ITmfTrace trace, Class<T> aspectClass, ITmfEvent event) { |
c15e897d AM |
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); | |
b3867ecc MAL |
220 | } |
221 | ||
d26d67f5 BH |
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 | |
066b02aa | 233 | * @since 1.2 |
d26d67f5 BH |
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 | } | |
dba71686 AM |
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 | |
f8a23812 AM |
265 | * @param monitor |
266 | * Optional progress monitor that can be used to cancel the | |
267 | * operation | |
dba71686 AM |
268 | * @return The first event matching the predicate, or null if the end of the |
269 | * trace was reached and no event was found | |
dc281431 | 270 | * @since 2.1 |
dba71686 | 271 | */ |
f8a23812 AM |
272 | public static @Nullable ITmfEvent getNextEventMatching(ITmfTrace trace, long startRank, |
273 | Predicate<ITmfEvent> predicate, @Nullable IProgressMonitor monitor) { | |
1353a51a AM |
274 | if (monitor != null && monitor.isCanceled()) { |
275 | return null; | |
276 | } | |
277 | ||
dba71686 AM |
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 { | |
f8a23812 AM |
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 | } | |
dba71686 AM |
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 | |
f8a23812 AM |
309 | * @param monitor Optional progress monitor that can be used to cancel the |
310 | * operation | |
dba71686 AM |
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 | |
dc281431 | 313 | * @since 2.1 |
dba71686 | 314 | */ |
f8a23812 AM |
315 | public static @Nullable ITmfEvent getPreviousEventMatching(ITmfTrace trace, long startRank, |
316 | Predicate<ITmfEvent> predicate, @Nullable IProgressMonitor monitor) { | |
1353a51a AM |
317 | if (monitor != null && monitor.isCanceled()) { |
318 | return null; | |
319 | } | |
dba71686 | 320 | /* |
c93f91d9 MK |
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. | |
dba71686 | 324 | */ |
c93f91d9 | 325 | int step = trace.getCacheSize(); |
dba71686 AM |
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 | */ | |
c93f91d9 MK |
331 | if (startRank < step) { |
332 | step = (int) startRank; | |
333 | } | |
dba71686 AM |
334 | |
335 | long currentRank = startRank; | |
336 | try { | |
337 | while (currentRank > 0) { | |
338 | currentRank = Math.max(currentRank - step, 0); | |
339 | ||
c93f91d9 MK |
340 | List<ITmfEvent> list = new ArrayList<>(step); |
341 | ArrayFillingRequest req = new ArrayFillingRequest(currentRank, step, list); | |
dba71686 | 342 | trace.sendRequest(req); |
f8a23812 AM |
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 | } | |
dba71686 AM |
353 | req.waitForCompletion(); |
354 | ||
c93f91d9 MK |
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(); | |
dba71686 AM |
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 | } | |
c93f91d9 MK |
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 | } | |
b8585c7c | 465 | } |