lttng: Update the TestValues scripts used in tests
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / analysis / TmfAbstractAnalysisModule.java
1 /*******************************************************************************
2 * Copyright (c) 2013, 2014 École Polytechnique de Montréal
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 * Geneviève Bastien - Initial API and implementation
11 *******************************************************************************/
12
13 package org.eclipse.tracecompass.tmf.core.analysis;
14
15 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
16
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Set;
23 import java.util.concurrent.CountDownLatch;
24 import java.util.concurrent.TimeUnit;
25
26 import org.eclipse.core.runtime.IProgressMonitor;
27 import org.eclipse.core.runtime.IStatus;
28 import org.eclipse.core.runtime.NullProgressMonitor;
29 import org.eclipse.core.runtime.Status;
30 import org.eclipse.core.runtime.jobs.Job;
31 import org.eclipse.jdt.annotation.NonNullByDefault;
32 import org.eclipse.jdt.annotation.Nullable;
33 import org.eclipse.osgi.util.NLS;
34 import org.eclipse.tracecompass.common.core.NonNullUtils;
35 import org.eclipse.tracecompass.internal.tmf.core.Activator;
36 import org.eclipse.tracecompass.internal.tmf.core.TmfCoreTracer;
37 import org.eclipse.tracecompass.tmf.core.analysis.TmfAnalysisRequirement.ValuePriorityLevel;
38 import org.eclipse.tracecompass.tmf.core.component.TmfComponent;
39 import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException;
40 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
41 import org.eclipse.tracecompass.tmf.core.signal.TmfStartAnalysisSignal;
42 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
43 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
44 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
45 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
46
47 /**
48 * Base class that analysis modules main class may extend. It provides default
49 * behavior to some methods of the analysis module
50 *
51 * @author Geneviève Bastien
52 * @since 3.0
53 */
54 @NonNullByDefault
55 public abstract class TmfAbstractAnalysisModule extends TmfComponent implements IAnalysisModule {
56
57 private @Nullable String fId;
58 private boolean fAutomatic = false, fStarted = false;
59 private volatile @Nullable ITmfTrace fTrace;
60 private final Map<String, Object> fParameters = new HashMap<>();
61 private final List<String> fParameterNames = new ArrayList<>();
62 private final List<IAnalysisOutput> fOutputs = new ArrayList<>();
63 private List<IAnalysisParameterProvider> fParameterProviders = new ArrayList<>();
64 private @Nullable Job fJob = null;
65
66 private final Object syncObj = new Object();
67
68 /* Latch tracking if the analysis is completed or not */
69 private CountDownLatch fFinishedLatch = new CountDownLatch(0);
70
71 private boolean fAnalysisCancelled = false;
72
73 @Override
74 public boolean isAutomatic() {
75 return fAutomatic;
76 }
77
78 @Override
79 public String getName() {
80 return super.getName();
81 }
82
83 @Override
84 public void setName(String name) {
85 super.setName(name);
86 }
87
88 @Override
89 public void setId(String id) {
90 fId = id;
91 }
92
93 @Override
94 public String getId() {
95 String id = fId;
96 if (id == null) {
97 id = this.getClass().getCanonicalName();
98 if (id == null) {
99 /*
100 * Some types, like anonymous classes, don't have a canonical
101 * name. Just use the default name instead.
102 */
103 id = checkNotNull(this.getClass().getName());
104 }
105 fId = id;
106 }
107 return id;
108 }
109
110 @Override
111 public void setAutomatic(boolean auto) {
112 fAutomatic = auto;
113 }
114
115 @Override
116 public void setTrace(ITmfTrace trace) throws TmfAnalysisException {
117 if (fTrace != null) {
118 throw new TmfAnalysisException(NLS.bind(Messages.TmfAbstractAnalysisModule_TraceSetMoreThanOnce, getName()));
119 }
120
121 TmfCoreTracer.traceAnalysis(getId(), trace, "setting trace for analysis"); //$NON-NLS-1$
122
123 /* Check that analysis can be executed */
124 if (!canExecute(trace)) {
125 throw new TmfAnalysisException(NLS.bind(Messages.TmfAbstractAnalysisModule_AnalysisCannotExecute, getName()));
126 }
127
128 fTrace = trace;
129 /* Get the parameter providers for this trace */
130 fParameterProviders = TmfAnalysisManager.getParameterProviders(this, trace);
131 for (IAnalysisParameterProvider provider : fParameterProviders) {
132 TmfCoreTracer.traceAnalysis(getId(), trace, "registered to parameter provider " + provider.getName()); //$NON-NLS-1$
133 provider.registerModule(this);
134 }
135 resetAnalysis();
136 fStarted = false;
137 }
138
139 /**
140 * Gets the trace
141 *
142 * @return The trace
143 */
144 protected @Nullable ITmfTrace getTrace() {
145 return fTrace;
146 }
147
148 @Override
149 public void addParameter(String name) {
150 fParameterNames.add(name);
151 }
152
153 @Override
154 public synchronized void setParameter(String name, @Nullable Object value) {
155 if (!fParameterNames.contains(name)) {
156 throw new RuntimeException(NLS.bind(Messages.TmfAbstractAnalysisModule_InvalidParameter, name, getName()));
157 }
158 Object oldValue = fParameters.get(name);
159 fParameters.put(name, value);
160 if ((value != null) && !(value.equals(oldValue))) {
161 parameterChanged(name);
162 }
163 }
164
165 @Override
166 public synchronized void notifyParameterChanged(String name) {
167 if (!fParameterNames.contains(name)) {
168 throw new RuntimeException(NLS.bind(Messages.TmfAbstractAnalysisModule_InvalidParameter, name, getName()));
169 }
170 Object oldValue = fParameters.get(name);
171 Object value = getParameter(name);
172 if ((value != null) && !(value.equals(oldValue))) {
173 parameterChanged(name);
174 }
175 }
176
177 /**
178 * Used to indicate that a parameter value has been changed
179 *
180 * @param name
181 * The name of the modified parameter
182 */
183 protected void parameterChanged(String name) {
184
185 }
186
187 @Override
188 public @Nullable Object getParameter(String name) {
189 Object paramValue = fParameters.get(name);
190 /* The parameter is not set, maybe it can be provided by someone else */
191 if ((paramValue == null) && (fTrace != null)) {
192 for (IAnalysisParameterProvider provider : fParameterProviders) {
193 paramValue = provider.getParameter(name);
194 if (paramValue != null) {
195 break;
196 }
197 }
198 }
199 return paramValue;
200 }
201
202 @Override
203 public boolean canExecute(ITmfTrace trace) {
204 for (TmfAnalysisRequirement requirement : getAnalysisRequirements()) {
205 if (!requirement.isFulfilled(trace)) {
206 return false;
207 }
208 }
209 return true;
210 }
211
212 /**
213 * Set the countdown latch back to 1 so the analysis can be executed again
214 */
215 protected void resetAnalysis() {
216 TmfCoreTracer.traceAnalysis(getId(), getTrace(), "reset: ready for execution"); //$NON-NLS-1$
217 fFinishedLatch.countDown();
218 fFinishedLatch = new CountDownLatch(1);
219 }
220
221 /**
222 * Actually executes the analysis itself
223 *
224 * @param monitor
225 * Progress monitor
226 * @return Whether the analysis was completed successfully or not
227 * @throws TmfAnalysisException
228 * Method may throw an analysis exception
229 */
230 protected abstract boolean executeAnalysis(final IProgressMonitor monitor) throws TmfAnalysisException;
231
232 /**
233 * Indicate the analysis has been canceled. It is abstract to force
234 * implementing class to cleanup what they are running. This is called by
235 * the job's canceling. It does not need to be called directly.
236 */
237 protected abstract void canceling();
238
239 /**
240 * To be called when the analysis is completed, whether normally or because
241 * it was cancelled or for any other reason.
242 *
243 * It has to be called inside a synchronized block
244 */
245 private void setAnalysisCompleted() {
246 synchronized (syncObj) {
247 fStarted = false;
248 fJob = null;
249 fFinishedLatch.countDown();
250 }
251 }
252
253 /**
254 * Cancels the analysis if it is executing
255 */
256 @Override
257 public final void cancel() {
258 synchronized (syncObj) {
259 TmfCoreTracer.traceAnalysis(getId(), getTrace(), "cancelled by application"); //$NON-NLS-1$
260 if (fJob != null) {
261 if (fJob.cancel()) {
262 fAnalysisCancelled = true;
263 setAnalysisCompleted();
264 }
265 }
266 fStarted = false;
267 }
268 }
269
270 @Override
271 public void dispose() {
272 super.dispose();
273 cancel();
274 }
275
276 /**
277 * Get an iterable list of all analyzes this analysis depends on. These
278 * analyzes will be scheduled before this analysis starts and the current
279 * analysis will not be considered completed until all the dependent
280 * analyzes are finished.
281 *
282 * @return An iterable list of analysis this analyzes depends on.
283 */
284 protected Iterable<IAnalysisModule> getDependentAnalyses() {
285 return checkNotNull(Collections.EMPTY_LIST);
286 }
287
288 private void execute(final ITmfTrace trace) {
289 /*
290 * TODO: The analysis in a job should be done at the analysis manager
291 * level instead of depending on this abstract class implementation,
292 * otherwise another analysis implementation may block the main thread
293 */
294
295 /* Do not execute if analysis has already run */
296 if (fFinishedLatch.getCount() == 0) {
297 TmfCoreTracer.traceAnalysis(getId(), getTrace(), "already executed"); //$NON-NLS-1$
298 return;
299 }
300
301 /* Do not execute if analysis already running */
302 synchronized (syncObj) {
303 if (fStarted) {
304 TmfCoreTracer.traceAnalysis(getId(), getTrace(), "already started, not starting again"); //$NON-NLS-1$
305 return;
306 }
307 fStarted = true;
308 }
309
310 /* Execute dependent analyses before creating the job for this one */
311 final Iterable<IAnalysisModule> dependentAnalyses = getDependentAnalyses();
312 for (IAnalysisModule module : dependentAnalyses) {
313 module.schedule();
314 }
315
316 /*
317 * Actual analysis will be run on a separate thread
318 */
319 String jobName = checkNotNull(NLS.bind(Messages.TmfAbstractAnalysisModule_RunningAnalysis, getName()));
320 fJob = new Job(jobName) {
321 @Override
322 protected @Nullable IStatus run(final @Nullable IProgressMonitor monitor) {
323 IProgressMonitor mon = monitor;
324 if (mon == null) {
325 mon = new NullProgressMonitor();
326 }
327 try {
328 mon.beginTask("", IProgressMonitor.UNKNOWN); //$NON-NLS-1$
329 broadcast(new TmfStartAnalysisSignal(TmfAbstractAnalysisModule.this, TmfAbstractAnalysisModule.this));
330 TmfCoreTracer.traceAnalysis(TmfAbstractAnalysisModule.this.getId(), TmfAbstractAnalysisModule.this.getTrace(), "started"); //$NON-NLS-1$
331 fAnalysisCancelled = !executeAnalysis(mon);
332 for (IAnalysisModule module : dependentAnalyses) {
333 module.waitForCompletion(mon);
334 }
335 TmfCoreTracer.traceAnalysis(TmfAbstractAnalysisModule.this.getId(), TmfAbstractAnalysisModule.this.getTrace(), "finished"); //$NON-NLS-1$
336 } catch (TmfAnalysisException e) {
337 Activator.logError("Error executing analysis with trace " + trace.getName(), e); //$NON-NLS-1$
338 } finally {
339 synchronized (syncObj) {
340 mon.done();
341 setAnalysisCompleted();
342 }
343 TmfTraceManager.refreshSupplementaryFiles(trace);
344 }
345 if (!fAnalysisCancelled) {
346 return Status.OK_STATUS;
347 }
348 // Reset analysis so that it can be executed again.
349 resetAnalysis();
350 return Status.CANCEL_STATUS;
351 }
352
353 @Override
354 protected void canceling() {
355 TmfCoreTracer.traceAnalysis(getId(), getTrace(), "job cancelled"); //$NON-NLS-1$
356 TmfAbstractAnalysisModule.this.canceling();
357 }
358
359 };
360 fJob.schedule();
361 }
362
363 @Override
364 public IStatus schedule() {
365 synchronized (syncObj) {
366 final ITmfTrace trace = getTrace();
367 if (trace == null) {
368 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, String.format("No trace specified for analysis %s", getName())); //$NON-NLS-1$
369 }
370 TmfCoreTracer.traceAnalysis(getId(), getTrace(), "scheduled"); //$NON-NLS-1$
371 execute(trace);
372 }
373
374 return checkNotNull(Status.OK_STATUS);
375 }
376
377 @Override
378 public Iterable<IAnalysisOutput> getOutputs() {
379 return fOutputs;
380 }
381
382 @Override
383 public void registerOutput(IAnalysisOutput output) {
384 if (!fOutputs.contains(output)) {
385 fOutputs.add(output);
386 }
387 }
388
389 @Override
390 public boolean waitForCompletion() {
391 try {
392 fFinishedLatch.await();
393 } catch (InterruptedException e) {
394 Activator.logError("Error while waiting for module completion", e); //$NON-NLS-1$
395 }
396 return !fAnalysisCancelled;
397 }
398
399 @Override
400 public boolean waitForCompletion(IProgressMonitor monitor) {
401 try {
402 while (!fFinishedLatch.await(500, TimeUnit.MILLISECONDS)) {
403 if (fAnalysisCancelled || monitor.isCanceled()) {
404 fAnalysisCancelled = true;
405 return false;
406 }
407 }
408 } catch (InterruptedException e) {
409 Activator.logError("Error while waiting for module completion", e); //$NON-NLS-1$
410 }
411 return !fAnalysisCancelled;
412 }
413
414 /**
415 * Signal handler for trace closing
416 *
417 * @param signal
418 * Trace closed signal
419 */
420 @TmfSignalHandler
421 public void traceClosed(TmfTraceClosedSignal signal) {
422 /* Is the closing trace the one that was requested? */
423 synchronized (syncObj) {
424 if (signal.getTrace() == fTrace) {
425 cancel();
426 fTrace = null;
427 }
428 }
429 }
430
431 /**
432 * Signal handler for when the trace becomes active
433 *
434 * @param signal
435 * Trace selected signal
436 * @since 3.1
437 */
438 @TmfSignalHandler
439 public void traceSelected(TmfTraceSelectedSignal signal) {
440 /*
441 * Since some parameter providers may handle many traces, we need to
442 * register the current trace to it
443 */
444 if (signal.getTrace() == fTrace) {
445 for (IAnalysisParameterProvider provider : fParameterProviders) {
446 provider.registerModule(this);
447 }
448 }
449 }
450
451 /**
452 * Returns a full help text to display
453 *
454 * @return Full help text for the module
455 */
456 protected String getFullHelpText() {
457 return NonNullUtils.nullToEmptyString(NLS.bind(
458 Messages.TmfAbstractAnalysisModule_AnalysisModule,
459 getName()));
460 }
461
462 /**
463 * Gets a short help text, to display as header to other help text
464 *
465 * @param trace
466 * The trace to show help for
467 *
468 * @return Short help text describing the module
469 */
470 protected String getShortHelpText(ITmfTrace trace) {
471 return NonNullUtils.nullToEmptyString(NLS.bind(
472 Messages.TmfAbstractAnalysisModule_AnalysisForTrace,
473 getName(), trace.getName()));
474 }
475
476 /**
477 * Gets the help text specific for a trace who does not have required
478 * characteristics for module to execute. The default implementation uses
479 * the analysis requirements.
480 *
481 * @param trace
482 * The trace to apply the analysis to
483 * @return Help text
484 */
485 protected String getTraceCannotExecuteHelpText(ITmfTrace trace) {
486 StringBuilder builder = new StringBuilder();
487 builder.append(NLS.bind(Messages.TmfAbstractAnalysisModule_AnalysisCannotExecute, getName()));
488 for (TmfAnalysisRequirement requirement : getAnalysisRequirements()) {
489 if (!requirement.isFulfilled(trace)) {
490 builder.append("\n\n"); //$NON-NLS-1$
491 builder.append(NLS.bind(Messages.TmfAnalysis_RequirementNotFulfilled, requirement.getType()));
492 builder.append("\n"); //$NON-NLS-1$
493 builder.append(NLS.bind(Messages.TmfAnalysis_RequirementMandatoryValues, requirement.getValues(ValuePriorityLevel.MANDATORY)));
494 Set<String> information = requirement.getInformation();
495 if (!information.isEmpty()) {
496 builder.append("\n"); //$NON-NLS-1$
497 builder.append(NLS.bind(Messages.TmfAnalysis_RequirementInformation, information));
498 }
499 }
500 }
501 return checkNotNull(builder.toString());
502 }
503
504 @Override
505 public String getHelpText() {
506 return getFullHelpText();
507 }
508
509 @Override
510 public String getHelpText(ITmfTrace trace) {
511 String text = getShortHelpText(trace);
512 if (!canExecute(trace)) {
513 text = text + "\n\n" + getTraceCannotExecuteHelpText(trace); //$NON-NLS-1$
514 }
515 return text;
516 }
517
518 @Override
519 public Iterable<TmfAnalysisRequirement> getAnalysisRequirements() {
520 return checkNotNull(Collections.EMPTY_SET);
521 }
522 }
This page took 0.04781 seconds and 6 git commands to generate.