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