import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.action.Action;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.tracecompass.common.core.NonNullUtils;
+import org.eclipse.tracecompass.common.core.log.TraceCompassLog;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.tmf.core.resources.ITmfMarker;
+import org.eclipse.tracecompass.tmf.core.signal.TmfMarkerEventSourceUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfTimestampFormatUpdateSignal;
private static final Pattern RGBA_PATTERN = Pattern.compile("RGBA \\{(\\d+), (\\d+), (\\d+), (\\d+)\\}"); //$NON-NLS-1$
+ private static final Logger LOGGER = TraceCompassLog.getLogger(AbstractTimeGraphView.class);
+ private static final String LOG_STRING_WITH_PARAM = "[TimeGraphView:%s] viewId=%s, %s"; //$NON-NLS-1$
+ private static final String LOG_STRING = "[TimeGraphView:%s] viewId=%s"; //$NON-NLS-1$
+
/**
* Redraw state enum
*/
private final Map<ITmfTrace, List<IMarkerEventSource>> fMarkerEventSourcesMap = new HashMap<>();
/** The trace to build thread hash map */
- private final Map<ITmfTrace, BuildThread> fBuildThreadMap = new HashMap<>();
+ private final Map<ITmfTrace, Job> fBuildJobMap = new HashMap<>();
/** The start time */
private long fStartTime = SWT.DEFAULT;
}
- private class BuildThread extends Thread {
+ // TODO: This can implement ICoreRunnable once support for Eclipse 4.5. is not necessary anymore.
+ private class BuildRunnable {
private final @NonNull ITmfTrace fBuildTrace;
private final @NonNull ITmfTrace fParentTrace;
- private final @NonNull IProgressMonitor fMonitor;
- public BuildThread(final @NonNull ITmfTrace trace, final @NonNull ITmfTrace parentTrace, final String name) {
- super(name + " build"); //$NON-NLS-1$
+ public BuildRunnable(final @NonNull ITmfTrace trace, final @NonNull ITmfTrace parentTrace) {
fBuildTrace = trace;
fParentTrace = parentTrace;
- fMonitor = new NullProgressMonitor();
}
- @Override
- public void run() {
- buildEntryList(fBuildTrace, fParentTrace, fMonitor);
- synchronized (fBuildThreadMap) {
- fBuildThreadMap.remove(fBuildTrace);
+ public void run(IProgressMonitor monitor) {
+ LOGGER.info(() -> getLogMessage("BuildThreadStart", "trace=" + fBuildTrace.getName())); //$NON-NLS-1$ //$NON-NLS-2$
+
+ buildEntryList(fBuildTrace, fParentTrace, NonNullUtils.checkNotNull(monitor));
+ synchronized (fBuildJobMap) {
+ fBuildJobMap.remove(fBuildTrace);
}
- }
- public void cancel() {
- fMonitor.setCanceled(true);
+ LOGGER.info(() -> getLogMessage("BuildThreadEnd", null)); //$NON-NLS-1$
}
}
@Override
public final void run() {
+ LOGGER.info(() -> getLogMessage("ZoomThreadStart", "start=" + fZoomStartTime + ", end=" + fZoomEndTime)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
doRun();
fDirty.decrementAndGet();
+
+ LOGGER.info(() -> getLogMessage("ZoomThreadEnd", null)); //$NON-NLS-1$
}
/**
@Override
public void doRun() {
+ LOGGER.config(() -> getLogMessage("ZoomThreadGettingStates", null)); //$NON-NLS-1$
+
for (TimeGraphEntry entry : fZoomEntryList) {
if (getMonitor().isCanceled()) {
+ LOGGER.info(() -> getLogMessage("ZoomThreadCanceled", null)); //$NON-NLS-1$
return;
}
if (entry == null) {
zoom(entry, getMonitor());
}
/* Refresh the arrows when zooming */
+ LOGGER.config(() -> getLogMessage("ZoomThreadGettingLinks", null)); //$NON-NLS-1$
List<ILinkEvent> events = getLinkList(getZoomStartTime(), getZoomEndTime(), getResolution(), getMonitor());
+
/* Refresh the view-specific markers when zooming */
+ LOGGER.config(() -> getLogMessage("ZoomThreadGettingMarkers", null)); //$NON-NLS-1$
List<IMarkerEvent> markers = new ArrayList<>(getViewMarkerList(getZoomStartTime(), getZoomEndTime(), getResolution(), getMonitor()));
/* Refresh the trace-specific markers when zooming */
markers.addAll(getTraceMarkerList(getZoomStartTime(), getZoomEndTime(), getResolution(), getMonitor()));
if (events != null) {
fTimeGraphWrapper.getTimeGraphViewer().setLinks(events);
}
+ fTimeGraphWrapper.getTimeGraphViewer().setMarkerCategories(getMarkerCategories());
fTimeGraphWrapper.getTimeGraphViewer().setMarkers(markers);
redraw();
});
return fFindTarget;
}
+ /**
+ * Formats a log message for this class
+ *
+ * @param event
+ * The event to log, that will be appended to the class name to
+ * make the full event name
+ * @param parameters
+ * The string of extra parameters to add to the log message, in
+ * the format name=value[, name=value]*, or <code>null</code> for
+ * no params
+ * @return The complete log message for this class
+ */
+ private String getLogMessage(String event, @Nullable String parameters) {
+ if (parameters == null) {
+ return String.format(LOG_STRING, event, getViewId());
+ }
+ return String.format(LOG_STRING_WITH_PARAM, event, getViewId(), parameters);
+ }
+
// ------------------------------------------------------------------------
// ViewPart
// ------------------------------------------------------------------------
@Override
public void dispose() {
super.dispose();
- synchronized (fBuildThreadMap) {
- fBuildThreadMap.values().forEach(buildThread -> {
- buildThread.cancel();
+ synchronized (fBuildJobMap) {
+ fBuildJobMap.values().forEach(buildJob -> {
+ buildJob.cancel();
});
}
if (fZoomThread != null) {
if (signal.getTrace() == fTrace) {
fTrace = null;
fEditorFile = null;
- fStartTime = SWT.DEFAULT;
- fEndTime = SWT.DEFAULT;
+ setStartTime(SWT.DEFAULT);
+ setEndTime(SWT.DEFAULT);
refresh();
}
}
fTimeGraphWrapper.refresh();
}
+ /**
+ * A marker event source has been updated
+ *
+ * @param signal
+ * the signal
+ * @since 2.1
+ */
+ @TmfSignalHandler
+ public void markerEventSourceUpdated(final TmfMarkerEventSourceUpdatedSignal signal) {
+ getTimeGraphViewer().setMarkerCategories(getMarkerCategories());
+ getTimeGraphViewer().setMarkers(null);
+ refresh();
+ }
+
// ------------------------------------------------------------------------
// Internal
// ------------------------------------------------------------------------
fViewContext.put(fTrace, new ViewContext(fCurrentSortColumn, fSortDirection, fTimeGraphWrapper.getSelection()));
}
fTrace = trace;
+
+ LOGGER.info(() -> getLogMessage("LoadingTrace", "trace=" + trace.getName())); //$NON-NLS-1$ //$NON-NLS-2$
+
restoreViewContext();
fEditorFile = TmfTraceManager.getInstance().getTraceEditorFile(trace);
synchronized (fEntryListMap) {
if (fEntryList == null) {
rebuild();
} else {
- fStartTime = fTrace.getStartTime().toNanos();
- fEndTime = fTrace.getEndTime().toNanos();
+ setStartTime(fTrace.getStartTime().toNanos());
+ setEndTime(fTrace.getEndTime().toNanos());
refresh();
}
}
resetView(viewTrace);
List<IMarkerEventSource> markerEventSources = new ArrayList<>();
- synchronized (fBuildThreadMap) {
+ synchronized (fBuildJobMap) {
for (ITmfTrace trace : getTracesToBuild(viewTrace)) {
if (trace == null) {
break;
}
markerEventSources.addAll(TmfTraceAdapterManager.getAdapters(trace, IMarkerEventSource.class));
- BuildThread buildThread = new BuildThread(trace, viewTrace, getName());
- fBuildThreadMap.put(trace, buildThread);
- buildThread.start();
+ Job buildJob = new Job(getTitle() + Messages.AbstractTimeGraphView_BuildJob) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ new BuildRunnable(trace, viewTrace).run(monitor);
+ monitor.done();
+ return Status.OK_STATUS;
+ }
+ };
+ fBuildJobMap.put(trace, buildJob);
+ buildJob.schedule();
}
}
fMarkerEventSourcesMap.put(viewTrace, markerEventSources);
* some of which may receive events in live streaming mode.
*
* @param trace
- * The trace associated with this view
+ * The trace associated with this view, can be null
* @return List of traces with data to display
*/
- protected @NonNull Iterable<ITmfTrace> getTracesToBuild(@NonNull ITmfTrace trace) {
+ protected @NonNull Iterable<ITmfTrace> getTracesToBuild(@Nullable ITmfTrace trace) {
return TmfTraceManager.getTraceSet(trace);
}
/**
* Build the entry list to show in this time graph view.
* <p>
- * Called from the BuildThread for each trace returned by
- * {@link #getTracesToBuild(ITmfTrace)}. The full event list is also
- * normally computed for every entry that is created.
+ * Called from the BuildJob for each trace returned by
+ * {@link #getTracesToBuild(ITmfTrace)}.
+ * <p>
+ * Root entries must be added to the entry list by calling the
+ * {@link #addToEntryList(ITmfTrace, List)} method with the list of entries
+ * to add and where the trace in parameter should be the parentTrace.
+ * Entries that are children of other entries will be automatically picked
+ * up after refreshing the root entries.
+ * <p>
+ * The full event list is also normally computed for every entry that is
+ * created. It should be set for each entry by calling the
+ * {@link TimeGraphEntry#setEventList(List)}. These full event lists will be
+ * used to display something while the zoomed event lists are being
+ * calculated when the window range is updated. Also, when fully zoomed out,
+ * it is this list of events that is displayed.
+ * <p>
+ * Also, when all the entries have been added and their events set, this
+ * method can finish by calling the refresh() method like this:
+ *
+ * <pre>
+ * if (parentTrace.equals(getTrace())) {
+ * refresh();
+ * }
+ * </pre>
*
* @param trace
* The trace being built
* Get the list of current marker categories.
*
* @return The list of marker categories
- * @since 2.0
+ * @since 2.1
*/
- private @NonNull List<String> getMarkerCategories() {
+ protected @NonNull List<String> getMarkerCategories() {
Set<String> categories = new LinkedHashSet<>(getViewMarkerCategories());
for (IMarkerEventSource markerEventSource : getMarkerEventSources(fTrace)) {
categories.addAll(markerEventSource.getMarkerCategories());
* Refresh the display
*/
protected void refresh() {
+ LOGGER.info(() -> getLogMessage("RefreshRequested", null)); //$NON-NLS-1$
final boolean zoomThread = Thread.currentThread() instanceof ZoomThread;
TmfUiRefreshHandler.getInstance().queueUpdate(this, new Runnable() {
@Override
public void run() {
+ LOGGER.info(() -> getLogMessage("RefreshStart", null)); //$NON-NLS-1$
if (fTimeGraphWrapper.isDisposed()) {
return;
}
startZoomThread(startTime, endTime);
}
fDirty.decrementAndGet();
+ LOGGER.info(() -> getLogMessage("RefreshEnd", null)); //$NON-NLS-1$
}
});
}
return;
}
}
+ LOGGER.info(() -> getLogMessage("RedrawRequested", null)); //$NON-NLS-1$
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
+ LOGGER.info(() -> getLogMessage("RedrawStart", null)); //$NON-NLS-1$
if (fTimeGraphWrapper.isDisposed()) {
return;
}
fRedrawState = State.IDLE;
}
}
+ LOGGER.info(() -> getLogMessage("RedrawEnd", null)); //$NON-NLS-1$
}
});
}
* @since 2.0
*/
protected final void startZoomThread(long startTime, long endTime) {
- long clampedStartTime = Math.min(Math.max(startTime, fStartTime), fEndTime);
- long clampedEndTime = Math.max(Math.min(endTime, fEndTime), fStartTime);
+ long clampedStartTime = (fStartTime == Long.MAX_VALUE ? startTime : Math.min(Math.max(startTime, fStartTime), fEndTime));
+ long clampedEndTime = (fEndTime == Long.MIN_VALUE ? endTime : Math.max(Math.min(endTime, fEndTime), fStartTime));
fDirty.incrementAndGet();
boolean restart = false;
if (fZoomThread != null) {
if (viewTrace == null) {
return;
}
- synchronized (fBuildThreadMap) {
+ synchronized (fBuildJobMap) {
for (ITmfTrace trace : getTracesToBuild(viewTrace)) {
- BuildThread buildThread = fBuildThreadMap.remove(trace);
- if (buildThread != null) {
- buildThread.cancel();
+ Job buildJob = fBuildJobMap.remove(trace);
+ if (buildJob != null) {
+ buildJob.cancel();
}
}
}