1 /*******************************************************************************
2 * Copyright (c) 2013, 2015 École Polytechnique de Montréal
4 * All rights reserved. This program and the accompanying materials are made
5 * 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 * Geneviève Bastien - Initial implementation and API
11 *******************************************************************************/
13 package org
.eclipse
.tracecompass
.tmf
.core
.event
.matching
;
15 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
17 import java
.util
.Collection
;
18 import java
.util
.HashSet
;
21 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
22 import org
.eclipse
.core
.runtime
.IStatus
;
23 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
24 import org
.eclipse
.core
.runtime
.Status
;
25 import org
.eclipse
.core
.runtime
.jobs
.Job
;
26 import org
.eclipse
.jdt
.annotation
.NonNull
;
27 import org
.eclipse
.osgi
.util
.NLS
;
28 import org
.eclipse
.tracecompass
.internal
.tmf
.core
.Activator
;
29 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEvent
;
30 import org
.eclipse
.tracecompass
.tmf
.core
.request
.ITmfEventRequest
;
31 import org
.eclipse
.tracecompass
.tmf
.core
.request
.TmfEventRequest
;
32 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimeRange
;
33 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
34 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceManager
;
35 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.experiment
.TmfExperiment
;
37 import com
.google
.common
.collect
.HashBasedTable
;
38 import com
.google
.common
.collect
.HashMultimap
;
39 import com
.google
.common
.collect
.ImmutableList
;
40 import com
.google
.common
.collect
.Multimap
;
41 import com
.google
.common
.collect
.Table
;
44 * Abstract class to extend to match certain type of events in a trace
46 * @author Geneviève Bastien
48 public class TmfEventMatching
implements ITmfEventMatching
{
50 private static final Set
<ITmfMatchEventDefinition
> MATCH_DEFINITIONS
= new HashSet
<>();
53 * The array of traces to match
55 private final @NonNull Collection
<@NonNull ITmfTrace
> fTraces
;
58 * The class to call once a match is found
60 private final IMatchProcessingUnit fMatches
;
62 private final Multimap
<ITmfTrace
, ITmfMatchEventDefinition
> fMatchMap
= HashMultimap
.create();
65 * Hashtables for unmatches incoming events
67 private final Table
<ITmfTrace
, IEventMatchingKey
, ITmfEvent
> fUnmatchedIn
= HashBasedTable
.create();
70 * Hashtables for unmatches outgoing events
72 private final Table
<ITmfTrace
, IEventMatchingKey
, ITmfEvent
> fUnmatchedOut
= HashBasedTable
.create();
75 * Enum for cause and effect types of event
78 public enum Direction
{
80 * The event is the first event of the match
84 * The event is the second event, the one that matches with the cause
90 * Constructor with multiple traces
93 * The set of traces for which to match events
96 public TmfEventMatching(Collection
<@NonNull ITmfTrace
> traces
) {
97 this(traces
, new TmfEventMatches());
101 * Constructor with multiple traces and a match processing object
104 * The set of traces for which to match events
105 * @param tmfEventMatches
106 * The match processing class
108 public TmfEventMatching(Collection
<@NonNull ITmfTrace
> traces
, IMatchProcessingUnit tmfEventMatches
) {
109 if (tmfEventMatches
== null) {
110 throw new IllegalArgumentException();
112 fTraces
= new HashSet
<>(traces
);
113 fMatches
= tmfEventMatches
;
117 * Returns the traces to synchronize. These are the traces that were
118 * specified in the constructor, they may contain either traces or
121 * @return The traces to synchronize
123 protected Collection
<ITmfTrace
> getTraces() {
124 return new HashSet
<>(fTraces
);
128 * Returns the individual traces to process. If some of the traces specified
129 * to synchronize in the constructor were experiments, only the traces
130 * contained in this experiment will be returned. No {@link TmfExperiment}
131 * are returned by this method.
133 * @return The individual traces to synchronize, no experiments
135 protected Collection
<ITmfTrace
> getIndividualTraces() {
136 Set
<ITmfTrace
> traces
= new HashSet
<>();
137 for (ITmfTrace trace
: fTraces
) {
138 traces
.addAll(TmfTraceManager
.getTraceSet(trace
));
144 * Returns the match processing unit
146 * @return The match processing unit
148 protected IMatchProcessingUnit
getProcessingUnit() {
153 * Returns the match event definitions corresponding to the trace
157 * @return The match event definition object
159 protected Collection
<ITmfMatchEventDefinition
> getEventDefinitions(ITmfTrace trace
) {
160 return ImmutableList
.copyOf(fMatchMap
.get(trace
));
164 * Method that initializes any data structure for the event matching. It
165 * also assigns to each trace an event matching definition instance that
166 * applies to the trace
170 public void initMatching() {
171 // Initialize the matching infrastructure (unmatched event lists)
172 fUnmatchedIn
.clear();
173 fUnmatchedOut
.clear();
175 fMatches
.init(fTraces
);
176 for (ITmfTrace trace
: getIndividualTraces()) {
177 for (ITmfMatchEventDefinition def
: MATCH_DEFINITIONS
) {
178 if (def
.canMatchTrace(trace
)) {
179 fMatchMap
.put(trace
, def
);
186 * Calls any post matching methods of the processing class
188 protected void finalizeMatching() {
189 fMatches
.matchingEnded();
193 * Prints stats from the matching
195 * @return string of statistics
198 public String
toString() {
199 final String cr
= System
.getProperty("line.separator"); //$NON-NLS-1$
200 StringBuilder b
= new StringBuilder();
201 b
.append(getProcessingUnit());
203 for (ITmfTrace trace
: getIndividualTraces()) {
204 b
.append("Trace " + i
++ + ":" + cr
+ //$NON-NLS-1$ //$NON-NLS-2$
205 " " + fUnmatchedIn
.row(trace
).size() + " unmatched incoming events" + cr
+ //$NON-NLS-1$ //$NON-NLS-2$
206 " " + fUnmatchedOut
.row(trace
).size() + " unmatched outgoing events" + cr
); //$NON-NLS-1$ //$NON-NLS-2$
218 * The trace to which this event belongs
220 * The monitor for the synchronization job
223 public void matchEvent(ITmfEvent event
, ITmfTrace trace
, @NonNull IProgressMonitor monitor
) {
224 ITmfMatchEventDefinition def
= null;
225 Direction evType
= null;
226 for (ITmfMatchEventDefinition oneDef
: getEventDefinitions(event
.getTrace())) {
228 evType
= def
.getDirection(event
);
229 if (evType
!= null) {
235 if (def
== null || evType
== null) {
239 /* Get the event's unique fields */
240 IEventMatchingKey eventKey
= def
.getEventKey(event
);
242 if (eventKey
== null) {
245 Table
<ITmfTrace
, IEventMatchingKey
, ITmfEvent
> unmatchedTbl
, companionTbl
;
247 /* Point to the appropriate table */
250 unmatchedTbl
= fUnmatchedIn
;
251 companionTbl
= fUnmatchedOut
;
254 unmatchedTbl
= fUnmatchedOut
;
255 companionTbl
= fUnmatchedIn
;
261 boolean found
= false;
262 TmfEventDependency dep
= null;
263 /* Search for the event in the companion table */
264 for (ITmfTrace mTrace
: getIndividualTraces()) {
265 if (companionTbl
.contains(mTrace
, eventKey
)) {
267 ITmfEvent companionEvent
= companionTbl
.get(mTrace
, eventKey
);
269 /* Remove the element from the companion table */
270 companionTbl
.remove(mTrace
, eventKey
);
272 /* Create the dependency object */
275 dep
= new TmfEventDependency(companionEvent
, event
);
278 dep
= new TmfEventDependency(event
, companionEvent
);
288 * If no companion was found, add the event to the appropriate unMatched
292 getProcessingUnit().addMatch(checkNotNull(dep
));
293 monitor
.subTask(NLS
.bind(Messages
.TmfEventMatching_MatchesFound
, getProcessingUnit().countMatches()));
296 * If an event is already associated with this key, do not add it
297 * again, we keep the first event chronologically, so if its match
298 * is eventually found, it is associated with the first send or
299 * receive event. At best, it is a good guess, at worst, the match
300 * will be too far off to be accurate. Too bad!
302 * TODO: maybe instead of just one event, we could have a list of
303 * events as value for the unmatched table. Not necessary right now
306 if (!unmatchedTbl
.contains(event
.getTrace(), eventKey
)) {
307 unmatchedTbl
.put(event
.getTrace(), eventKey
, event
);
313 * Method that start the process of matching events
315 * @return Whether the match was completed correctly or not
318 public boolean matchEvents() {
320 /* Are there traces to match? If no, return false */
321 if (!(fTraces
.size() > 0)) {
328 * Actual analysis will be run on a separate thread
330 Job job
= new Job(Messages
.TmfEventMatching_MatchingEvents
) {
332 protected IStatus
run(final IProgressMonitor monitor
) {
334 * FIXME For now, we use the experiment strategy: the trace that
335 * is asked to be matched is actually an experiment and the
336 * experiment does the request. But depending on how divergent
337 * the traces' times are and how long it takes to get the first
338 * match, it can use a lot of memory.
340 * Some strategies can help limit the memory usage of this
344 * Other possible matching strategy:
345 * * start with the shortest trace
346 * * take a few events at the beginning and at the end and try
350 for (ITmfTrace trace
: fTraces
) {
351 monitor
.beginTask(NLS
.bind(Messages
.TmfEventMatching_LookingEventsFrom
, trace
.getName()), IProgressMonitor
.UNKNOWN
);
352 setName(NLS
.bind(Messages
.TmfEventMatching_RequestingEventsFrom
, trace
.getName()));
354 /* Send the request to the trace */
355 EventMatchingBuildRequest request
= new EventMatchingBuildRequest(TmfEventMatching
.this, trace
, monitor
);
356 trace
.sendRequest(request
);
358 request
.waitForCompletion();
359 } catch (InterruptedException e
) {
360 Activator
.logInfo(e
.getMessage());
362 if (monitor
.isCanceled()) {
363 return Status
.CANCEL_STATUS
;
366 return Status
.OK_STATUS
;
372 } catch (InterruptedException e
) {
382 * Registers an event match definition
385 * The event matching definition
387 public static void registerMatchObject(ITmfMatchEventDefinition match
) {
388 MATCH_DEFINITIONS
.add(match
);
393 class EventMatchingBuildRequest
extends TmfEventRequest
{
395 private final TmfEventMatching matching
;
396 private final ITmfTrace trace
;
397 private final @NonNull IProgressMonitor fMonitor
;
399 EventMatchingBuildRequest(TmfEventMatching matching
, ITmfTrace trace
, IProgressMonitor monitor
) {
400 super(ITmfEvent
.class,
401 TmfTimeRange
.ETERNITY
,
403 ITmfEventRequest
.ALL_DATA
,
404 ITmfEventRequest
.ExecutionType
.FOREGROUND
);
405 this.matching
= matching
;
407 if (monitor
== null) {
408 fMonitor
= new NullProgressMonitor();
415 public void handleData(final ITmfEvent event
) {
416 super.handleData(event
);
417 if (fMonitor
.isCanceled()) {
420 matching
.matchEvent(event
, trace
, fMonitor
);
424 public void handleSuccess() {
425 super.handleSuccess();
429 public void handleCancel() {
430 super.handleCancel();
434 public void handleFailure() {
435 super.handleFailure();