ss: Add benchmarks for the threaded history tree
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / trace / TmfTraceManager.java
CommitLineData
fc526aef 1/*******************************************************************************
ed902a2b 2 * Copyright (c) 2013, 2015 Ericsson
fc526aef
AM
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
0fcf3b09 11 * Patrick Tasse - Support selection range
d3de0920 12 * Xavier Raynaud - Support filters tracking
fc526aef
AM
13 *******************************************************************************/
14
2bdf0193 15package org.eclipse.tracecompass.tmf.core.trace;
fc526aef 16
5db5a3a4
AM
17import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
18
e1385db9 19import java.io.File;
3ec38c4c 20import java.net.URISyntaxException;
c14c0757 21import java.util.Collection;
fab18d20 22import java.util.Collections;
fc526aef 23import java.util.LinkedHashMap;
0f8687b4 24import java.util.LinkedHashSet;
e2239aea 25import java.util.List;
fc526aef 26import java.util.Map;
fab18d20 27import java.util.Set;
fc526aef 28
deaae6e1 29import org.eclipse.core.resources.IFile;
b5e8ee95
GB
30import org.eclipse.core.resources.IFolder;
31import org.eclipse.core.resources.IProject;
e1385db9
AM
32import org.eclipse.core.resources.IResource;
33import org.eclipse.core.runtime.CoreException;
1ac53e54 34import org.eclipse.core.runtime.URIUtil;
0f8687b4 35import org.eclipse.jdt.annotation.NonNull;
21852dfa 36import org.eclipse.jdt.annotation.Nullable;
2bdf0193
AM
37import org.eclipse.tracecompass.internal.tmf.core.Activator;
38import org.eclipse.tracecompass.tmf.core.TmfCommonConstants;
2bdf0193 39import org.eclipse.tracecompass.tmf.core.signal.TmfEventFilterAppliedSignal;
16801c72 40import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
2bdf0193
AM
41import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
42import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
2bdf0193
AM
43import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
44import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
45import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
16801c72 46import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal;
2bdf0193
AM
47import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
48import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
49import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
5c5fa260 50import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment;
fc526aef 51
c14c0757
GB
52import com.google.common.collect.ImmutableSet;
53
fc526aef
AM
54/**
55 * Central trace manager for TMF. It tracks the currently opened traces and
0fcf3b09 56 * experiment, as well as the currently-selected time or time range and the
d3de0920
XR
57 * current window time range for each one of those. It also tracks filters
58 * applied for each trace.
fc526aef
AM
59 *
60 * It's a singleton class, so only one instance should exist (available via
61 * {@link #getInstance()}).
62 *
63 * @author Alexandre Montplaisir
fc526aef
AM
64 */
65public final class TmfTraceManager {
66
67 // ------------------------------------------------------------------------
68 // Attributes
69 // ------------------------------------------------------------------------
70
71 private final Map<ITmfTrace, TmfTraceContext> fTraces;
72
73 /** The currently-selected trace. Should always be part of the trace map */
74 private ITmfTrace fCurrentTrace = null;
75
3ec38c4c
MAL
76 private static final String TEMP_DIR_NAME = ".temp"; //$NON-NLS-1$
77
fc526aef
AM
78 // ------------------------------------------------------------------------
79 // Constructor
80 // ------------------------------------------------------------------------
81
82 private TmfTraceManager() {
a4524c1b 83 fTraces = new LinkedHashMap<>();
fc526aef
AM
84 TmfSignalManager.registerVIP(this);
85 }
86
87 /** Singleton instance */
88 private static TmfTraceManager tm = null;
89
90 /**
91 * Get an instance of the trace manager.
92 *
93 * @return The trace manager
94 */
95 public static synchronized TmfTraceManager getInstance() {
96 if (tm == null) {
97 tm = new TmfTraceManager();
98 }
99 return tm;
100 }
101
102 // ------------------------------------------------------------------------
103 // Accessors
104 // ------------------------------------------------------------------------
105
fc526aef
AM
106 /**
107 * Get the currently selected trace (normally, the focused editor).
108 *
109 * @return The active trace
110 */
111 public synchronized ITmfTrace getActiveTrace() {
112 return fCurrentTrace;
113 }
114
115 /**
a6fc3a28 116 * Get the trace set of the currently active trace.
fc526aef
AM
117 *
118 * @return The active trace set
a6fc3a28 119 * @see #getTraceSet(ITmfTrace)
fc526aef 120 */
c14c0757 121 public synchronized @NonNull Collection<ITmfTrace> getActiveTraceSet() {
fc526aef 122 final ITmfTrace trace = fCurrentTrace;
a6fc3a28 123 return getTraceSet(trace);
fc526aef
AM
124 }
125
fab18d20
AM
126 /**
127 * Get the currently-opened traces, as an unmodifiable set.
128 *
129 * @return A set containing the opened traces
130 */
131 public synchronized Set<ITmfTrace> getOpenedTraces() {
132 return Collections.unmodifiableSet(fTraces.keySet());
133 }
134
deaae6e1
PT
135 /**
136 * Get the editor file for an opened trace.
137 *
138 * @param trace
139 * the trace
140 * @return the editor file or null if the trace is not opened
deaae6e1
PT
141 */
142 public synchronized IFile getTraceEditorFile(ITmfTrace trace) {
143 TmfTraceContext ctx = fTraces.get(trace);
144 if (ctx != null) {
145 return ctx.getEditorFile();
146 }
147 return null;
148 }
149
21852dfa
AM
150 /**
151 * Get the {@link TmfTraceContext} of the current active trace. This can be
152 * used to retrieve the current active/selected time ranges and such.
153 *
154 * @return The trace's context.
155 * @since 1.0
156 */
157 public synchronized TmfTraceContext getCurrentTraceContext() {
fc526aef
AM
158 TmfTraceContext curCtx = fTraces.get(fCurrentTrace);
159 if (curCtx == null) {
160 /* There are no traces opened at the moment. */
161 return TmfTraceContext.NULL_CONTEXT;
162 }
163 return curCtx;
164 }
165
e1385db9
AM
166 // ------------------------------------------------------------------------
167 // Public utility methods
168 // ------------------------------------------------------------------------
169
a6fc3a28
AM
170 /**
171 * Get the trace set of a given trace. For a standard trace, this is simply
172 * an array with only that trace in it. For experiments, this is an array of
173 * all the traces contained in this experiment.
174 *
175 * @param trace
176 * The trace or experiment
c14c0757 177 * @return The corresponding trace set.
a6fc3a28 178 */
df2597e0 179 public static @NonNull Collection<@NonNull ITmfTrace> getTraceSet(ITmfTrace trace) {
a6fc3a28 180 if (trace == null) {
0e4f957e 181 return ImmutableSet.of();
a6fc3a28 182 }
df2597e0 183 List<@NonNull ITmfTrace> traces = trace.getChildren(ITmfTrace.class);
e2239aea 184 if (traces.size() > 0) {
0e4f957e 185 return ImmutableSet.copyOf(traces);
a6fc3a28 186 }
0e4f957e 187 return ImmutableSet.of(trace);
a6fc3a28
AM
188 }
189
0f8687b4
GB
190 /**
191 * Get the trace set of a given trace or experiment, including the
192 * experiment. For a standard trace, this is simply a set containing only
193 * that trace. For experiments, it is the set of all the traces contained in
194 * this experiment, along with the experiment.
195 *
196 * @param trace
197 * The trace or experiment
c14c0757 198 * @return The corresponding trace set, including the experiment.
0f8687b4 199 */
c14c0757 200 public static @NonNull Collection<ITmfTrace> getTraceSetWithExperiment(ITmfTrace trace) {
0f8687b4 201 if (trace == null) {
0e4f957e 202 return ImmutableSet.of();
0f8687b4
GB
203 }
204 if (trace instanceof TmfExperiment) {
205 TmfExperiment exp = (TmfExperiment) trace;
fa62dc1d
BH
206 List<ITmfTrace> traces = exp.getTraces();
207 Set<ITmfTrace> alltraces = new LinkedHashSet<>(traces);
0f8687b4 208 alltraces.add(exp);
0e4f957e 209 return ImmutableSet.copyOf(alltraces);
0f8687b4 210 }
0e4f957e 211 return Collections.singleton(trace);
0f8687b4
GB
212 }
213
e1385db9
AM
214 /**
215 * Return the path (as a string) to the directory for supplementary files to
216 * use with a given trace. If no supplementary file directory has been
217 * configured, a temporary directory based on the trace's name will be
218 * provided.
219 *
220 * @param trace
221 * The trace
222 * @return The path to the supplementary file directory (trailing slash is
223 * INCLUDED!)
224 */
225 public static String getSupplementaryFileDir(ITmfTrace trace) {
226 IResource resource = trace.getResource();
227 if (resource == null) {
228 return getTemporaryDir(trace);
229 }
230
231 String supplDir = null;
232 try {
233 supplDir = resource.getPersistentProperty(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER);
234 } catch (CoreException e) {
235 return getTemporaryDir(trace);
236 }
237 return supplDir + File.separator;
238 }
239
b5e8ee95
GB
240 /**
241 * Refresh the supplementary files resources for a trace, so it can pick up
242 * new files that got created.
243 *
244 * @param trace
245 * The trace for which to refresh the supplementary files
b5e8ee95
GB
246 */
247 public static void refreshSupplementaryFiles(ITmfTrace trace) {
248 IResource resource = trace.getResource();
88567ed2 249 if (resource != null && resource.exists()) {
b5e8ee95
GB
250 String supplFolderPath = getSupplementaryFileDir(trace);
251 IProject project = resource.getProject();
252 /* Remove the project's path from the supplementary path dir */
cdb43c16 253 if (!supplFolderPath.startsWith(project.getLocation().toOSString())) {
b5e8ee95
GB
254 Activator.logWarning(String.format("Supplementary files folder for trace %s is not within the project.", trace.getName())); //$NON-NLS-1$
255 return;
256 }
257 IFolder supplFolder = project.getFolder(supplFolderPath.substring(project.getLocationURI().getPath().length()));
258 if (supplFolder.exists()) {
259 try {
260 supplFolder.refreshLocal(IResource.DEPTH_INFINITE, null);
261 } catch (CoreException e) {
262 Activator.logError("Error refreshing resources", e); //$NON-NLS-1$
263 }
264 }
265 }
266 }
267
fc526aef
AM
268 // ------------------------------------------------------------------------
269 // Signal handlers
270 // ------------------------------------------------------------------------
271
272 /**
273 * Signal handler for the traceOpened signal.
274 *
275 * @param signal
276 * The incoming signal
277 */
278 @TmfSignalHandler
279 public synchronized void traceOpened(final TmfTraceOpenedSignal signal) {
280 final ITmfTrace trace = signal.getTrace();
deaae6e1 281 final IFile editorFile = signal.getEditorFile();
fc526aef
AM
282 final ITmfTimestamp startTs = trace.getStartTime();
283
16801c72
MK
284 long offset = trace.getInitialRangeOffset().toNanos();
285 long endTime = startTs.toNanos() + offset;
21852dfa 286 final TmfTimeRange selectionRange = new TmfTimeRange(startTs, startTs);
16801c72 287 final TmfTimeRange windowRange = new TmfTimeRange(startTs, new TmfTimestamp(endTime, ITmfTimestamp.NANOSECOND_SCALE));
fc526aef 288
21852dfa 289 final TmfTraceContext startCtx = new TmfTraceContext(selectionRange, windowRange, editorFile, null);
fc526aef
AM
290
291 fTraces.put(trace, startCtx);
292
293 /* We also want to set the newly-opened trace as the active trace */
294 fCurrentTrace = trace;
295 }
296
fc526aef
AM
297 /**
298 * Handler for the TmfTraceSelectedSignal.
299 *
300 * @param signal
301 * The incoming signal
302 */
303 @TmfSignalHandler
304 public synchronized void traceSelected(final TmfTraceSelectedSignal signal) {
305 final ITmfTrace newTrace = signal.getTrace();
306 if (!fTraces.containsKey(newTrace)) {
307 throw new RuntimeException();
308 }
309 fCurrentTrace = newTrace;
310 }
311
d3de0920
XR
312 /**
313 * Signal handler for the filterApplied signal.
314 *
315 * @param signal
316 * The incoming signal
d3de0920
XR
317 */
318 @TmfSignalHandler
319 public synchronized void filterApplied(TmfEventFilterAppliedSignal signal) {
320 final ITmfTrace newTrace = signal.getTrace();
321 TmfTraceContext context = fTraces.get(newTrace);
322 if (context == null) {
323 throw new RuntimeException();
324 }
21852dfa
AM
325 fTraces.put(newTrace, new TmfTraceContext(context.getSelectionRange(),
326 context.getWindowRange(),
327 context.getEditorFile(),
328 signal.getEventFilter()));
d3de0920
XR
329 }
330
fc526aef
AM
331 /**
332 * Signal handler for the traceClosed signal.
333 *
334 * @param signal
335 * The incoming signal
336 */
337 @TmfSignalHandler
338 public synchronized void traceClosed(final TmfTraceClosedSignal signal) {
3fcf269e 339 fTraces.remove(signal.getTrace());
fc526aef
AM
340 if (fTraces.size() == 0) {
341 fCurrentTrace = null;
342 /*
343 * In other cases, we should receive a traceSelected signal that
344 * will indicate which trace is the new one.
345 */
346 }
347 }
348
349 /**
97c71024 350 * Signal handler for the selection range signal.
fc526aef
AM
351 *
352 * The current time of *all* traces whose range contains the requested new
0fcf3b09 353 * selection time range will be updated.
fc526aef
AM
354 *
355 * @param signal
356 * The incoming signal
97c71024 357 * @since 1.0
fc526aef
AM
358 */
359 @TmfSignalHandler
97c71024 360 public synchronized void selectionRangeUpdated(final TmfSelectionRangeUpdatedSignal signal) {
0fcf3b09
PT
361 final ITmfTimestamp beginTs = signal.getBeginTime();
362 final ITmfTimestamp endTs = signal.getEndTime();
fc526aef
AM
363
364 for (Map.Entry<ITmfTrace, TmfTraceContext> entry : fTraces.entrySet()) {
365 final ITmfTrace trace = entry.getKey();
0fcf3b09 366 if (beginTs.intersects(getValidTimeRange(trace)) || endTs.intersects(getValidTimeRange(trace))) {
21852dfa
AM
367 TmfTraceContext prevCtx = checkNotNull(entry.getValue());
368
369 /*
370 * We want to update the selection range, but keep everything
371 * else the same as the previous trace context.
372 */
373 TmfTimeRange newSelectionRange = new TmfTimeRange(beginTs, endTs);
374 TmfTraceContext newCtx = new TmfTraceContext(newSelectionRange,
375 prevCtx.getWindowRange(),
376 prevCtx.getEditorFile(),
377 prevCtx.getFilter());
fc526aef
AM
378 entry.setValue(newCtx);
379 }
380 }
381 }
382
383 /**
97c71024 384 * Signal handler for the window range signal.
fc526aef 385 *
5db5a3a4
AM
386 * The current window time range of *all* valid traces will be updated to
387 * the new requested times.
fc526aef
AM
388 *
389 * @param signal
390 * The incoming signal
97c71024 391 * @since 1.0
fc526aef
AM
392 */
393 @TmfSignalHandler
97c71024 394 public synchronized void windowRangeUpdated(final TmfWindowRangeUpdatedSignal signal) {
fc526aef
AM
395 for (Map.Entry<ITmfTrace, TmfTraceContext> entry : fTraces.entrySet()) {
396 final ITmfTrace trace = entry.getKey();
21852dfa 397 final TmfTraceContext prevCtx = checkNotNull(entry.getValue());
fc526aef
AM
398
399 final TmfTimeRange validTr = getValidTimeRange(trace);
21852dfa
AM
400 if (validTr == null) {
401 return;
402 }
fc526aef 403
fc526aef 404 /* Determine the new time range */
21852dfa
AM
405 TmfTimeRange targetTr = signal.getCurrentRange().getIntersection(validTr);
406 TmfTimeRange newWindowTr = (targetTr == null ? prevCtx.getWindowRange() : targetTr);
fc526aef 407
21852dfa
AM
408 /* Keep the values from the old context, except for the window range */
409 TmfTraceContext newCtx = new TmfTraceContext(prevCtx.getSelectionRange(),
410 newWindowTr, prevCtx.getEditorFile(), prevCtx.getFilter());
fc526aef
AM
411 entry.setValue(newCtx);
412 }
413 }
414
415 // ------------------------------------------------------------------------
e1385db9 416 // Private utility methods
fc526aef
AM
417 // ------------------------------------------------------------------------
418
419 /**
0fcf3b09
PT
420 * Return the valid time range of a trace (not the current window time
421 * range, but the range of all possible valid timestamps).
fc526aef
AM
422 *
423 * For a real trace this is the whole range of the trace. For an experiment,
424 * it goes from the start time of the earliest trace to the end time of the
425 * latest one.
426 *
427 * @param trace
428 * The trace to check for
429 * @return The valid time span, or 'null' if the trace is not valid
430 */
21852dfa 431 private @Nullable TmfTimeRange getValidTimeRange(ITmfTrace trace) {
fc526aef
AM
432 if (!fTraces.containsKey(trace)) {
433 /* Trace is not part of the currently opened traces */
434 return null;
435 }
e2239aea
BH
436
437 List<ITmfTrace> traces = trace.getChildren(ITmfTrace.class);
438
439 if (traces.isEmpty()) {
fc526aef
AM
440 /* "trace" is a single trace, return its time range directly */
441 return trace.getTimeRange();
442 }
e2239aea
BH
443
444 if (traces.size() == 1) {
fc526aef 445 /* Trace is an experiment with only 1 trace */
e2239aea 446 return traces.get(0).getTimeRange();
fc526aef 447 }
e2239aea 448
fc526aef 449 /*
e2239aea 450 * Trace is an trace set with 2+ traces, so get the earliest start and
fc526aef
AM
451 * the latest end.
452 */
e2239aea
BH
453 ITmfTimestamp start = traces.get(0).getStartTime();
454 ITmfTimestamp end = traces.get(0).getEndTime();
455
456 for (int i = 1; i < traces.size(); i++) {
457 ITmfTrace curTrace = traces.get(i);
fc526aef
AM
458 if (curTrace.getStartTime().compareTo(start) < 0) {
459 start = curTrace.getStartTime();
460 }
461 if (curTrace.getEndTime().compareTo(end) > 0) {
462 end = curTrace.getEndTime();
463 }
464 }
465 return new TmfTimeRange(start, end);
466 }
e1385db9 467
3ec38c4c
MAL
468 /**
469 * Get the temporary directory path. If there is an instance of Eclipse
470 * running, the temporary directory will reside under the workspace.
471 *
472 * @return the temporary directory path suitable to be passed to the
473 * java.io.File constructor without a trailing separator
3ec38c4c
MAL
474 */
475 public static String getTemporaryDirPath() {
476 // Get the workspace path from the properties
477 String property = System.getProperty("osgi.instance.area"); //$NON-NLS-1$
478 if (property != null) {
479 try {
1ac53e54 480 File dir = URIUtil.toFile(URIUtil.fromString(property));
3ec38c4c
MAL
481 dir = new File(dir.getAbsolutePath() + File.separator + TEMP_DIR_NAME);
482 if (!dir.exists()) {
483 dir.mkdirs();
484 }
485 return dir.getAbsolutePath();
486 } catch (URISyntaxException e) {
487 Activator.logError(e.getLocalizedMessage(), e);
488 }
489 }
490 return System.getProperty("java.io.tmpdir"); //$NON-NLS-1$
491 }
492
e1385db9 493 /**
6e4358bd
AM
494 * Get a temporary directory based on a trace's name. We will create the
495 * directory if it doesn't exist, so that it's ready to be used.
e1385db9
AM
496 */
497 private static String getTemporaryDir(ITmfTrace trace) {
3ec38c4c 498 String pathName = getTemporaryDirPath() +
fa62dc1d
BH
499 File.separator +
500 trace.getName() +
501 File.separator;
6e4358bd
AM
502 File dir = new File(pathName);
503 if (!dir.exists()) {
504 dir.mkdirs();
505 }
506 return pathName;
e1385db9 507 }
fc526aef 508}
This page took 0.099347 seconds and 5 git commands to generate.