Skip to content

Commit 16b3685

Browse files
committed
6542: Improve calls to render in the flame view
Reviewed-by: hirt
1 parent 45449e9 commit 16b3685

File tree

1 file changed

+79
-54
lines changed
  • application/org.openjdk.jmc.flightrecorder.flameview/src/main/java/org/openjdk/jmc/flightrecorder/flameview/views

1 file changed

+79
-54
lines changed

application/org.openjdk.jmc.flightrecorder.flameview/src/main/java/org/openjdk/jmc/flightrecorder/flameview/views/FlameGraphView.java

+79-54
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,13 @@
5656
import java.io.IOException;
5757
import java.text.MessageFormat;
5858
import java.util.Base64;
59+
import java.util.concurrent.Callable;
5960
import java.util.concurrent.CancellationException;
6061
import java.util.concurrent.CompletableFuture;
6162
import java.util.concurrent.ExecutionException;
6263
import java.util.concurrent.ExecutorService;
6364
import java.util.concurrent.Executors;
65+
import java.util.concurrent.Future;
6466
import java.util.logging.Level;
6567
import java.util.stream.Collectors;
6668
import java.util.stream.Stream;
@@ -104,6 +106,7 @@
104106
import org.openjdk.jmc.flightrecorder.stacktrace.FrameSeparator.FrameCategorization;
105107
import org.openjdk.jmc.flightrecorder.stacktrace.StacktraceModel;
106108
import org.openjdk.jmc.flightrecorder.ui.FlightRecorderUI;
109+
import org.openjdk.jmc.flightrecorder.ui.ItemCollectionToolkit;
107110
import org.openjdk.jmc.flightrecorder.ui.common.ImageConstants;
108111
import org.openjdk.jmc.flightrecorder.ui.messages.internal.Messages;
109112
import org.openjdk.jmc.ui.CoreImages;
@@ -157,14 +160,15 @@ public class FlameGraphView extends ViewPart implements ISelectionListener {
157160

158161
private Browser browser;
159162
private SashForm container;
160-
private TraceNode currentRoot;
161-
private CompletableFuture<TraceNode> currentModelCalculator;
162-
private boolean threadRootAtTop = true;
163-
private boolean icicleViewActive = true;
164-
private IItemCollection currentItems;
165163
private GroupByAction[] groupByActions;
166164
private GroupByFlameviewAction[] groupByFlameviewActions;
167165
private ExportAction[] exportActions;
166+
private boolean threadRootAtTop = true;
167+
private boolean icicleViewActive = true;
168+
private IItemCollection currentItems;
169+
private ModelState modelState = ModelState.NONE;
170+
private ModelRebuildCallable modelRebuildCallable;
171+
private Future<Void> modelCalculationFuture;
168172

169173
private enum GroupActionType {
170174
THREAD_ROOT(Messages.STACKTRACE_VIEW_THREAD_ROOT, IAction.AS_RADIO_BUTTON, CoreImages.THREAD),
@@ -186,6 +190,10 @@ private GroupActionType(String message, int action, ImageDescriptor imageDescrip
186190

187191
}
188192

193+
private enum ModelState {
194+
INIT, CALCULATION, READY, ABORTED, NONE;
195+
}
196+
189197
private class GroupByAction extends Action {
190198
private final GroupActionType actionType;
191199

@@ -202,7 +210,7 @@ public void run() {
202210
boolean newValue = isChecked() == GroupActionType.THREAD_ROOT.equals(actionType);
203211
if (newValue != threadRootAtTop) {
204212
threadRootAtTop = newValue;
205-
rebuildModel(currentItems);
213+
triggerRebuildTask(currentItems);
206214
}
207215
}
208216
}
@@ -272,6 +280,38 @@ public void run() {
272280
}
273281
}
274282

283+
private static class ModelRebuildCallable implements Callable<Void> {
284+
285+
private volatile boolean isInvalid;
286+
private FlameGraphView view;
287+
private IItemCollection items;
288+
289+
private ModelRebuildCallable(FlameGraphView view, IItemCollection items) {
290+
this.view = view;
291+
this.items = items;
292+
}
293+
294+
private void setInvalid() {
295+
this.isInvalid = true;
296+
}
297+
298+
@Override
299+
public Void call() throws Exception {
300+
view.modelState = ModelState.CALCULATION;
301+
StacktraceModel model = new StacktraceModel(view.threadRootAtTop, view.frameSeparator, items);
302+
TraceNode root = TraceTreeUtils.createRootWithDescription(items, model.getRootFork().getBranchCount());
303+
TraceNode traceNode = TraceTreeUtils.createTree(root, model);
304+
String jsonModel = view.toJSonModel(traceNode).toString();
305+
if (isInvalid) {
306+
view.modelState = ModelState.ABORTED;
307+
return null;
308+
}
309+
view.modelState = ModelState.READY;
310+
DisplayToolkit.inDisplayThread().execute(() -> view.setModel(items, jsonModel));
311+
return null;
312+
}
313+
}
314+
275315
@Override
276316
public void init(IViewSite site, IMemento memento) throws PartInitException {
277317
super.init(site, memento);
@@ -331,59 +371,58 @@ public void saveState(IMemento memento) {
331371
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
332372
if (selection instanceof IStructuredSelection) {
333373
Object first = ((IStructuredSelection) selection).getFirstElement();
334-
setItems(AdapterUtil.getAdapter(first, IItemCollection.class));
335-
}
336-
}
337-
338-
private void setItems(IItemCollection items) {
339-
if (items != null) {
340-
currentItems = items;
341-
rebuildModel(items);
374+
IItemCollection items = AdapterUtil.getAdapter(first, IItemCollection.class);
375+
if (items == null) {
376+
triggerRebuildTask(ItemCollectionToolkit.build(Stream.empty()));
377+
} else if (!items.equals(currentItems)) {
378+
triggerRebuildTask(items);
379+
}
342380
}
343381
}
344382

345-
private void rebuildModel(IItemCollection items) {
346-
// Release old model before building the new
347-
if (currentModelCalculator != null) {
348-
currentModelCalculator.cancel(true);
383+
private void triggerRebuildTask(IItemCollection items) {
384+
// Release old model calculation before building a new
385+
if (modelCalculationFuture != null) {
386+
modelRebuildCallable.setInvalid();
387+
modelCalculationFuture.cancel(true);
349388
}
350-
currentModelCalculator = getModelPreparer(items, frameSeparator, true);
351-
currentModelCalculator.thenAcceptAsync(this::setModel, DisplayToolkit.inDisplayThread())
352-
.exceptionally(FlameGraphView::handleModelBuildException);
353-
}
354389

355-
private CompletableFuture<TraceNode> getModelPreparer(
356-
final IItemCollection items, final FrameSeparator separator, final boolean materializeSelectedBranches) {
357-
return CompletableFuture.supplyAsync(() -> {
358-
StacktraceModel model = new StacktraceModel(threadRootAtTop, frameSeparator, items);
359-
TraceNode root = TraceTreeUtils.createRootWithDescription(items, model.getRootFork().getBranchCount());
360-
return TraceTreeUtils.createTree(root, model);
361-
}, MODEL_EXECUTOR);
390+
modelState = ModelState.INIT;
391+
currentItems = items;
392+
modelRebuildCallable = new ModelRebuildCallable(this, items);
393+
modelCalculationFuture = MODEL_EXECUTOR.submit(modelRebuildCallable);
362394
}
363395

364-
private void setModel(TraceNode root) {
365-
if (!browser.isDisposed() && !root.equals(currentRoot)) {
366-
currentRoot = root;
367-
setViewerInput(root);
396+
private void setModel(final IItemCollection items, final String json) {
397+
if (ModelState.READY.equals(modelState) && items.equals(currentItems) && !browser.isDisposed()) {
398+
setViewerInput(json);
368399
}
369400
}
370401

371-
private void setViewerInput(TraceNode root) {
402+
private void setViewerInput(String json) {
372403
Stream.of(exportActions).forEach((action) -> action.setEnabled(false));
373404
browser.setText(HTML_PAGE);
374405
browser.addListener(SWT.Resize, event -> {
375406
browser.execute("resizeFlameGraph();");
376407
});
377408

378409
browser.addProgressListener(new ProgressAdapter() {
410+
private boolean loaded = false;
411+
412+
@Override
413+
public void changed(ProgressEvent event) {
414+
if (loaded) {
415+
browser.removeProgressListener(this);
416+
}
417+
}
418+
379419
@Override
380420
public void completed(ProgressEvent event) {
381-
browser.removeProgressListener(this);
382421
browser.execute(String.format("configureTooltipText('%s', '%s', '%s', '%s', '%s');", TABLE_COLUMN_COUNT,
383422
TABLE_COLUMN_EVENT_TYPE, TOOLTIP_PACKAGE, TOOLTIP_SAMPLES, TOOLTIP_DESCRIPTION));
384-
385-
browser.execute(String.format("processGraph(%s, %s);", toJSon(root), icicleViewActive));
423+
browser.execute(String.format("processGraph(%s, %s);", json, icicleViewActive));
386424
Stream.of(exportActions).forEach((action) -> action.setEnabled(true));
425+
loaded = true;
387426
}
388427
});
389428
}
@@ -444,33 +483,19 @@ public Object function(Object[] arguments) {
444483
fos.write(bytes);
445484
fos.close();
446485
} catch (CancellationException e) {
447-
// noop
486+
// noop : model calculation is canceled when is still running
448487
} catch (InterruptedException | ExecutionException | IOException e) {
449488
FlightRecorderUI.getDefault().getLogger().log(Level.SEVERE, "Failed to save flame graph", e); //$NON-NLS-1$
450489
}
451490
}
452491

453-
private static Void handleModelBuildException(Throwable ex) {
454-
if (!(ex.getCause() instanceof CancellationException)) {
455-
FlightRecorderUI.getDefault().getLogger().log(Level.SEVERE, "Failed to build stacktrace view model", ex); //$NON-NLS-1$
456-
}
457-
return null;
458-
}
459-
460-
private static String toJSon(TraceNode root) {
461-
if (root == null) {
462-
return "\"\"";
463-
}
464-
return render(root);
465-
}
466-
467-
private static String render(TraceNode root) {
492+
private StringBuilder toJSonModel(TraceNode root) {
468493
StringBuilder builder = new StringBuilder();
469494
String rootNodeStart = createJsonRootTraceNode(root);
470495
builder.append(rootNodeStart);
471496
renderChildren(builder, root);
472497
builder.append("]}");
473-
return builder.toString();
498+
return builder;
474499
}
475500

476501
private static void render(StringBuilder builder, TraceNode node) {

0 commit comments

Comments
 (0)