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 MK |
21 | import java.util.ArrayList; |
22 | import java.util.List; | |
c93f91d9 | 23 | import java.util.Optional; |
dba71686 | 24 | import java.util.function.Predicate; |
b8585c7c | 25 | |
f8a23812 | 26 | import org.eclipse.core.runtime.IProgressMonitor; |
df2597e0 | 27 | import org.eclipse.jdt.annotation.NonNull; |
1d83ed07 | 28 | import org.eclipse.jdt.annotation.NonNullByDefault; |
b8585c7c | 29 | import org.eclipse.jdt.annotation.Nullable; |
c15e897d | 30 | import org.eclipse.tracecompass.common.core.StreamUtils; |
b8585c7c | 31 | import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule; |
daf3c7eb | 32 | import org.eclipse.tracecompass.tmf.core.component.ITmfEventProvider; |
b1aad44e | 33 | import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; |
35f39420 | 34 | import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect; |
dba71686 | 35 | import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest; |
b8585c7c | 36 | |
daf3c7eb | 37 | import com.google.common.collect.Iterables; |
c93f91d9 | 38 | import com.google.common.collect.Lists; |
daf3c7eb | 39 | |
b8585c7c AM |
40 | /** |
41 | * Utility methods for ITmfTrace's. | |
42 | * | |
43 | * @author Alexandre Montplaisir | |
44 | */ | |
1d83ed07 | 45 | @NonNullByDefault |
b8585c7c AM |
46 | public final class TmfTraceUtils { |
47 | ||
d26d67f5 BH |
48 | private static final int MAX_NB_BINARY_BYTES = 2048; |
49 | ||
b1aad44e GB |
50 | private TmfTraceUtils() { |
51 | } | |
b8585c7c AM |
52 | |
53 | /** | |
daf3c7eb MK |
54 | * Return the first result of the first analysis module belonging to this trace or its children, |
55 | * with the specified ID and class. | |
b8585c7c AM |
56 | * |
57 | * @param trace | |
58 | * The trace for which you want the modules | |
59 | * @param moduleClass | |
60 | * Returned modules must extend this class | |
61 | * @param id | |
62 | * The ID of the analysis module | |
63 | * @return The analysis module with specified class and ID, or null if no | |
64 | * such module exists. | |
65 | */ | |
66 | public static @Nullable <T extends IAnalysisModule> T getAnalysisModuleOfClass(ITmfTrace trace, | |
67 | Class<T> moduleClass, String id) { | |
68 | Iterable<T> modules = getAnalysisModulesOfClass(trace, moduleClass); | |
69 | for (T module : modules) { | |
70 | if (id.equals(module.getId())) { | |
71 | return module; | |
72 | } | |
73 | } | |
74 | return null; | |
75 | } | |
76 | ||
77 | /** | |
daf3c7eb MK |
78 | * Return the analysis modules that are of a given class. The modules will be |
79 | * cast to the requested class. If the trace has children, the childrens modules | |
80 | * are also returned. | |
b8585c7c AM |
81 | * |
82 | * @param trace | |
daf3c7eb MK |
83 | * The trace for which you want the modules, the children trace modules |
84 | * are added as well. | |
b8585c7c AM |
85 | * @param moduleClass |
86 | * Returned modules must extend this class | |
87 | * @return List of modules of class moduleClass | |
88 | */ | |
df2597e0 | 89 | public static <T> Iterable<@NonNull T> getAnalysisModulesOfClass(ITmfTrace trace, Class<T> moduleClass) { |
b8585c7c | 90 | Iterable<IAnalysisModule> analysisModules = trace.getAnalysisModules(); |
daf3c7eb | 91 | List<@NonNull T> modules = new ArrayList<>(); |
b1aad44e | 92 | for (IAnalysisModule module : analysisModules) { |
b8585c7c | 93 | if (moduleClass.isAssignableFrom(module.getClass())) { |
aa353506 | 94 | modules.add(checkNotNull(moduleClass.cast(module))); |
b8585c7c AM |
95 | } |
96 | } | |
daf3c7eb MK |
97 | for (ITmfEventProvider child : trace.getChildren()) { |
98 | if (child instanceof ITmfTrace) { | |
99 | ITmfTrace childTrace = (ITmfTrace) child; | |
100 | Iterables.addAll(modules, getAnalysisModulesOfClass(childTrace, moduleClass)); | |
101 | } | |
102 | } | |
b8585c7c AM |
103 | return modules; |
104 | } | |
35f39420 AM |
105 | |
106 | /** | |
b1aad44e GB |
107 | * Return the first result of the first aspect that resolves as non null for |
108 | * the event received in parameter. If the returned value is not null, it | |
109 | * can be safely cast to the aspect's class proper return type. | |
35f39420 AM |
110 | * |
111 | * @param trace | |
112 | * The trace for which you want the event aspects | |
113 | * @param aspectClass | |
b1aad44e GB |
114 | * The class of the aspect(s) to resolve |
115 | * @param event | |
116 | * The event for which to get the aspect | |
117 | * @return The first result of the | |
118 | * {@link ITmfEventAspect#resolve(ITmfEvent)} that returns non null | |
119 | * for the event or {@code null} otherwise | |
35f39420 | 120 | */ |
c15e897d | 121 | public static <T extends ITmfEventAspect<?>> @Nullable Object resolveEventAspectOfClassForEvent( |
1d83ed07 | 122 | ITmfTrace trace, Class<T> aspectClass, ITmfEvent event) { |
c15e897d AM |
123 | return StreamUtils.getStream(trace.getEventAspects()) |
124 | .filter(aspect -> aspectClass.isAssignableFrom(aspect.getClass())) | |
125 | .map(aspect -> aspect.resolve(event)) | |
126 | .filter(obj -> obj != null) | |
127 | .findFirst().orElse(null); | |
35f39420 | 128 | } |
d26d67f5 | 129 | |
b3867ecc | 130 | /** |
c15e897d AM |
131 | * Return the first result of the first aspect that resolves as a non-null |
132 | * Integer for the event received in parameter. If no matching aspects are | |
133 | * found then null is returned. | |
b3867ecc MAL |
134 | * |
135 | * @param trace | |
136 | * The trace for which you want the event aspects | |
137 | * @param aspectClass | |
138 | * The class of the aspect(s) to resolve | |
139 | * @param event | |
140 | * The event for which to get the aspect | |
141 | * @return Integer of the first result of the | |
142 | * {@link ITmfEventAspect#resolve(ITmfEvent)} that returns non null | |
143 | * for the event or {@code null} otherwise | |
144 | * @since 2.0 | |
145 | */ | |
c15e897d | 146 | public static <T extends ITmfEventAspect<Integer>> @Nullable Integer resolveIntEventAspectOfClassForEvent( |
b3867ecc | 147 | ITmfTrace trace, Class<T> aspectClass, ITmfEvent event) { |
c15e897d AM |
148 | return StreamUtils.getStream(trace.getEventAspects()) |
149 | .filter(aspect -> aspectClass.isAssignableFrom(aspect.getClass())) | |
150 | /* Enforced by the T parameter bounding */ | |
151 | .map(aspect -> (Integer) aspect.resolve(event)) | |
152 | .filter(obj -> obj != null) | |
153 | .findFirst().orElse(null); | |
b3867ecc MAL |
154 | } |
155 | ||
d26d67f5 BH |
156 | /** |
157 | * Checks for text file. | |
158 | * | |
159 | * Note that it checks for binary value 0 in the first MAX_NB_BINARY_BYTES | |
160 | * bytes to determine if the file is text. | |
161 | * | |
162 | * @param file | |
163 | * the file to check. Caller has to make sure that file exists. | |
164 | * @return true if it is binary else false | |
165 | * @throws IOException | |
166 | * if IOException occurs | |
066b02aa | 167 | * @since 1.2 |
d26d67f5 BH |
168 | */ |
169 | public static boolean isText(File file) throws IOException { | |
170 | try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file))) { | |
171 | int count = 0; | |
172 | int val = bufferedInputStream.read(); | |
173 | while ((count < MAX_NB_BINARY_BYTES) && (val >= 0)) { | |
174 | if (val == 0) { | |
175 | return false; | |
176 | } | |
177 | count++; | |
178 | val = bufferedInputStream.read(); | |
179 | } | |
180 | } | |
181 | return true; | |
182 | } | |
dba71686 AM |
183 | |
184 | // ------------------------------------------------------------------------ | |
185 | // Event matching methods | |
186 | // ------------------------------------------------------------------------ | |
187 | ||
188 | /** | |
189 | * Retrieve from a trace the next event, from a starting rank, matching the | |
190 | * given predicate. | |
191 | * | |
192 | * @param trace | |
193 | * The trace | |
194 | * @param startRank | |
195 | * The rank of the event at which to start searching. Use | |
196 | * <code>0</code> to search from the start of the trace. | |
197 | * @param predicate | |
198 | * The predicate to test events against | |
f8a23812 AM |
199 | * @param monitor |
200 | * Optional progress monitor that can be used to cancel the | |
201 | * operation | |
dba71686 AM |
202 | * @return The first event matching the predicate, or null if the end of the |
203 | * trace was reached and no event was found | |
dc281431 | 204 | * @since 2.1 |
dba71686 | 205 | */ |
f8a23812 AM |
206 | public static @Nullable ITmfEvent getNextEventMatching(ITmfTrace trace, long startRank, |
207 | Predicate<ITmfEvent> predicate, @Nullable IProgressMonitor monitor) { | |
1353a51a AM |
208 | if (monitor != null && monitor.isCanceled()) { |
209 | return null; | |
210 | } | |
211 | ||
dba71686 AM |
212 | /* rank + 1 because we do not want to include the start event itself in the search */ |
213 | EventMatchingRequest req = new EventMatchingRequest(startRank + 1, predicate, false); | |
214 | trace.sendRequest(req); | |
215 | try { | |
f8a23812 AM |
216 | /* Check periodically if the job was cancelled */ |
217 | req.waitForStart(); | |
218 | while (req.isRunning()) { | |
219 | Thread.sleep(200); | |
220 | if (monitor != null && monitor.isCanceled()) { | |
221 | req.cancel(); | |
222 | return null; | |
223 | } | |
224 | } | |
dba71686 AM |
225 | req.waitForCompletion(); |
226 | } catch (InterruptedException e) { | |
227 | return null; | |
228 | } | |
229 | ||
230 | return req.getFoundEvent(); | |
231 | } | |
232 | ||
233 | /** | |
234 | * Retrieve from a trace the previous event, from a given rank, matching the | |
235 | * given predicate. | |
236 | * | |
237 | * @param trace | |
238 | * The trace | |
239 | * @param startRank | |
240 | * The rank of the event at which to start searching backwards. | |
241 | * @param predicate | |
242 | * The predicate to test events against | |
f8a23812 AM |
243 | * @param monitor Optional progress monitor that can be used to cancel the |
244 | * operation | |
dba71686 AM |
245 | * @return The first event found matching the predicate, or null if the |
246 | * beginning of the trace was reached and no event was found | |
dc281431 | 247 | * @since 2.1 |
dba71686 | 248 | */ |
f8a23812 AM |
249 | public static @Nullable ITmfEvent getPreviousEventMatching(ITmfTrace trace, long startRank, |
250 | Predicate<ITmfEvent> predicate, @Nullable IProgressMonitor monitor) { | |
1353a51a AM |
251 | if (monitor != null && monitor.isCanceled()) { |
252 | return null; | |
253 | } | |
dba71686 | 254 | /* |
c93f91d9 MK |
255 | * We are going to do a series of queries matching the trace's cache |
256 | * size in length (which should minimize on-disk seeks), then iterate on | |
257 | * the found events in reverse order until we find a match. | |
dba71686 | 258 | */ |
c93f91d9 | 259 | int step = trace.getCacheSize(); |
dba71686 AM |
260 | |
261 | /* | |
262 | * If we are close to the beginning of the trace, make sure we only look | |
263 | * for the events before the startRank. | |
264 | */ | |
c93f91d9 MK |
265 | if (startRank < step) { |
266 | step = (int) startRank; | |
267 | } | |
dba71686 AM |
268 | |
269 | long currentRank = startRank; | |
270 | try { | |
271 | while (currentRank > 0) { | |
272 | currentRank = Math.max(currentRank - step, 0); | |
273 | ||
c93f91d9 MK |
274 | List<ITmfEvent> list = new ArrayList<>(step); |
275 | ArrayFillingRequest req = new ArrayFillingRequest(currentRank, step, list); | |
dba71686 | 276 | trace.sendRequest(req); |
f8a23812 AM |
277 | |
278 | /* Check periodically if the job was cancelled */ | |
279 | req.waitForStart(); | |
280 | while (req.isRunning()) { | |
281 | Thread.sleep(200); | |
282 | if (monitor != null && monitor.isCanceled()) { | |
283 | req.cancel(); | |
284 | return null; | |
285 | } | |
286 | } | |
dba71686 AM |
287 | req.waitForCompletion(); |
288 | ||
c93f91d9 MK |
289 | Optional<ITmfEvent> matchingEvent = Lists.reverse(list).stream() |
290 | .filter(predicate) | |
291 | .findFirst(); | |
292 | ||
293 | if (matchingEvent.isPresent()) { | |
294 | /* We found an event matching, return it! */ | |
295 | return matchingEvent.get(); | |
dba71686 AM |
296 | } |
297 | /* Keep searching, next loop */ | |
298 | ||
299 | } | |
300 | } catch (InterruptedException e) { | |
301 | return null; | |
302 | } | |
303 | ||
304 | /* | |
305 | * We searched up to the beginning of the trace and didn't find | |
306 | * anything. | |
307 | */ | |
308 | return null; | |
309 | ||
310 | } | |
311 | ||
312 | /** | |
313 | * Event request looking for an event matching a Predicate. | |
314 | */ | |
315 | private static class EventMatchingRequest extends TmfEventRequest { | |
316 | ||
317 | private final Predicate<ITmfEvent> fPredicate; | |
318 | private final boolean fReturnLast; | |
319 | ||
320 | private @Nullable ITmfEvent fFoundEvent = null; | |
321 | ||
322 | /** | |
323 | * Basic constructor, will query the trace until the end. | |
324 | * | |
325 | * @param startRank | |
326 | * The rank at which to start, use 0 for the beginning | |
327 | * @param predicate | |
328 | * The predicate to test against each event | |
329 | * @param returnLast | |
330 | * Should we return the last or first event found. If false, | |
331 | * the request ends as soon as a matching event is found. If | |
332 | * false, we will go through all events to find a possible | |
333 | * last-match. | |
334 | */ | |
335 | public EventMatchingRequest(long startRank, Predicate<ITmfEvent> predicate, boolean returnLast) { | |
336 | super(ITmfEvent.class, startRank, ALL_DATA, ExecutionType.FOREGROUND); | |
337 | fPredicate = predicate; | |
338 | fReturnLast = returnLast; | |
339 | } | |
340 | ||
341 | /** | |
342 | * Basic constructor, will query the trace the limit is reached. | |
343 | * | |
344 | * @param startRank | |
345 | * The rank at which to start, use 0 for the beginning | |
346 | * @param limit | |
347 | * The limit on the number of events | |
348 | * @param predicate | |
349 | * The predicate to test against each event | |
350 | * @param returnLast | |
351 | * Should we return the last or first event found. If false, | |
352 | * the request ends as soon as a matching event is found. If | |
353 | * false, we will go through all events to find a possible | |
354 | * last-match. | |
355 | */ | |
356 | public EventMatchingRequest(long startRank, int limit, Predicate<ITmfEvent> predicate, boolean returnLast) { | |
357 | super(ITmfEvent.class, startRank, limit, ExecutionType.FOREGROUND); | |
358 | fPredicate = predicate; | |
359 | fReturnLast = returnLast; | |
360 | } | |
361 | ||
362 | public @Nullable ITmfEvent getFoundEvent() { | |
363 | return fFoundEvent; | |
364 | } | |
365 | ||
366 | @Override | |
367 | public void handleData(ITmfEvent event) { | |
368 | super.handleData(event); | |
369 | ||
370 | if (fPredicate.test(event)) { | |
371 | fFoundEvent = event; | |
372 | if (!fReturnLast) { | |
373 | this.done(); | |
374 | } | |
375 | } | |
376 | } | |
377 | } | |
c93f91d9 MK |
378 | |
379 | /** | |
380 | * Event request that simply puts all returned events into a list passed in | |
381 | * parameter. | |
382 | */ | |
383 | private static class ArrayFillingRequest extends TmfEventRequest { | |
384 | ||
385 | private final List<ITmfEvent> fList; | |
386 | ||
387 | public ArrayFillingRequest(long startRank, int limit, List<ITmfEvent> listToFill) { | |
388 | super(ITmfEvent.class, startRank, limit, ExecutionType.FOREGROUND); | |
389 | fList = listToFill; | |
390 | } | |
391 | ||
392 | @Override | |
393 | public void handleData(ITmfEvent event) { | |
394 | super.handleData(event); | |
395 | fList.add(event); | |
396 | } | |
397 | ||
398 | } | |
b8585c7c | 399 | } |