import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Comparator;
import java.util.Deque;
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jface.viewers.Viewer;
-
import org.eclipse.tracecompass.internal.analysis.timing.core.callgraph.AggregatedCalledFunction;
import org.eclipse.tracecompass.internal.analysis.timing.core.callgraph.ThreadNode;
-import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
-import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphContentProvider;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
public class FlameGraphContentProvider implements ITimeGraphContentProvider {
private final List<FlamegraphDepthEntry> fFlameGraphEntries = new ArrayList<>();
- private ITmfTrace fActiveTrace;
private SortOption fSortOption = SortOption.BY_NAME;
private @NonNull Comparator<FlamegraphDepthEntry> fThreadComparator = new ThreadNameComparator();
-
/**
* Parse the aggregated tree created by the callGraphAnalysis and creates
* the event list (functions) for each entry (depth)
public ITimeGraphEntry[] getElements(Object inputElement) {
fFlameGraphEntries.clear();
// Get the root of each thread
- fActiveTrace = TmfTraceManager.getInstance().getActiveTrace();
- if (fActiveTrace == null) {
- return new ITimeGraphEntry[0];
- }
-
- if (inputElement instanceof List<?>) {
- List<?> threadNodes = (List<?>) inputElement;
+ if (inputElement instanceof Collection<?>) {
+ Collection<?> threadNodes = (Collection<?>) inputElement;
for (Object object : threadNodes) {
if (object instanceof ThreadNode) {
buildChildrenEntries((ThreadNode) object);
}
}
+ } else {
+ return new ITimeGraphEntry[0];
}
+
// Sort the threads
fFlameGraphEntries.sort(fThreadComparator);
return fFlameGraphEntries.toArray(new ITimeGraphEntry[fFlameGraphEntries.size()]);
// Do nothing
}
-
/**
* Get the sort option
*
* Set the sort option for sorting the thread entries
*
* @param sortOption
- * the sort option to set
+ * the sort option to set
*
*/
public void setSortOption(SortOption sortOption) {
*******************************************************************************/
package org.eclipse.tracecompass.internal.analysis.timing.ui.flamegraph;
+import java.util.concurrent.Semaphore;
+
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
-import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchActionConstants;
+import com.google.common.annotations.VisibleForTesting;
+
/**
* View to display the flame graph .This uses the flameGraphNode tree generated
* by CallGraphAnalysisUI.
private final @NonNull MenuManager fEventMenuManager = new MenuManager();
private Action fSortByNameAction;
private Action fSortByIdAction;
+ /**
+ * A plain old semaphore is used since different threads will be competing
+ * for the same resource.
+ */
+ private final Semaphore fLock = new Semaphore(1);
/**
* Constructor
});
}
- private TimeGraphViewer getTimeGraphViewer() {
- return fTimeGraphViewer;
- }
/**
- * Handler for the trace opened signal
+ * Get the time graph viewer
*
- * @param signal
- * The incoming signal
+ * @return the time graph viewer
*/
- @TmfSignalHandler
- public void TraceOpened(TmfTraceOpenedSignal signal) {
- fTrace = signal.getTrace();
- if (fTrace != null) {
- CallGraphAnalysis flamegraphModule = TmfTraceUtils.getAnalysisModuleOfClass(fTrace, CallGraphAnalysis.class, CallGraphAnalysisUI.ID);
- buildFlameGraph(flamegraphModule);
- }
+ @VisibleForTesting
+ public TimeGraphViewer getTimeGraphViewer() {
+ return fTimeGraphViewer;
}
/**
/**
* Get the necessary data for the flame graph and display it
*
- * @param flamegraphModule
+ * @param callGraphAnalysis
* the callGraphAnalysis
*/
- private void buildFlameGraph(CallGraphAnalysis callGraphAnalysis) {
- fTimeGraphViewer.setInput(null);
+ @VisibleForTesting
+ public void buildFlameGraph(CallGraphAnalysis callGraphAnalysis) {
+ /*
+ * Note for synchronization:
+ *
+ * Acquire the lock at entry. then we have 4 places to release it
+ *
+ * 1- if the lock failed
+ *
+ * 2- if the data is null and we have no UI to update
+ *
+ * 3- if the request is cancelled before it gets to the display
+ *
+ * 4- on a clean execution
+ */
+ try {
+ fLock.acquire();
+ } catch (InterruptedException e) {
+ Activator.getDefault().logError(e.getMessage(), e);
+ fLock.release();
+ }
+ if (callGraphAnalysis == null) {
+ fTimeGraphViewer.setInput(null);
+ fLock.release();
+ return;
+ }
+ fTimeGraphViewer.setInput(callGraphAnalysis.getSegmentStore());
callGraphAnalysis.schedule();
Job j = new Job(Messages.CallGraphAnalysis_Execution) {
@Override
protected IStatus run(IProgressMonitor monitor) {
if (monitor.isCanceled()) {
+ fLock.release();
return Status.CANCEL_STATUS;
}
callGraphAnalysis.waitForCompletion(monitor);
Display.getDefault().asyncExec(() -> {
fTimeGraphViewer.setInput(callGraphAnalysis.getThreadNodes());
fTimeGraphViewer.resetStartFinishTime();
+ fLock.release();
});
return Status.OK_STATUS;
}
j.schedule();
}
+ /**
+ * Await the next refresh
+ *
+ * @throws InterruptedException
+ * something took too long
+ */
+ @VisibleForTesting
+ public void waitForUpdate() throws InterruptedException {
+ /*
+ * wait for the semaphore to be available, then release it immediately
+ */
+ fLock.acquire();
+ fLock.release();
+ }
+
/**
* Trace is closed: clear the data structures and the view
*