Skip to content
Permalink
Browse files
7002: Too large graphs will freeze JMC
Reviewed-by: hirt
  • Loading branch information
cimi authored and thegreystone committed Jan 13, 2021
1 parent deb32e7 commit b6fe7cac8be77cf3c82846adb78a41ee6a8e3453
@@ -261,11 +261,23 @@ public String getColor(Edge edge) {
/**
* Renders a {@link StacktraceGraphModel} in DOT format.
*/
public static String toDot(StacktraceGraphModel model, Map<ConfigurationKey, String> configuration) {
public static String toDot(
StacktraceGraphModel model, int maxNodesRendered, Map<ConfigurationKey, String> configuration) {
StringBuilder builder = new StringBuilder(2048);
String graphName = getConf(configuration, ConfigurationKey.Name, DEFAULT_NAME);
builder.append(String.format("digraph \"%s\" {%n", graphName));

int nodeCount = model.getNodes().size();
if (nodeCount > maxNodesRendered) {
String message = String.format("Too many nodes in current selection%n(max: %d, actual: %d)",
maxNodesRendered, nodeCount);
emitMessage(builder, message, configuration);
builder.append("}");
return builder.toString();
} else if (nodeCount == 0) {
emitEmptyMessage(builder, "No graph data in current selection", configuration);
builder.append("}");
return builder.toString();
}
createDefaultNodeSettingsEntry(builder, configuration);
if (Boolean.valueOf(getConf(configuration, ConfigurationKey.TitleArea, "false"))) {
createSubgraphNode(builder, graphName, configuration, model);
@@ -353,6 +365,31 @@ private static void emitNode(
builder.append("\"]\n");
}

private static void emitMessage(
StringBuilder builder, String message, Map<ConfigurationKey, String> configuration) {
String shape = getConf(configuration, ConfigurationKey.NodeShape, DEFAULT_SHAPE);
String color = getConf(configuration, ConfigurationKey.NodeColor, "#b22b00");
String fillColor = getConf(configuration, ConfigurationKey.NodeFillColor, "#eddbd5");
builder.append("message [label=\"");
builder.append(message);
builder.append("\" id=\"message\"");
builder.append(" shape=");
builder.append(shape);
builder.append(" fontsize=5");
builder.append(" color=\"");
builder.append(color);
builder.append("\" fillcolor=\"");
builder.append(fillColor);
builder.append("\"]\n");
}

private static void emitEmptyMessage(
StringBuilder builder, String message, Map<ConfigurationKey, String> configuration) {
builder.append("label= <<font color='gray' point-size='1'>");
builder.append(message);
builder.append("</font>>");
}

private static void createSubgraphNode(
StringBuilder builder, String graphName, Map<ConfigurationKey, String> configuration,
StacktraceGraphModel model) {
@@ -419,6 +456,6 @@ public static void main(String[] args) throws IOException, CouldNotLoadRecording
StacktraceGraphModel model = new StacktraceGraphModel(frameSeparator, filteredItems, null);
Map<ConfigurationKey, String> configuration = getDefaultConfiguration();
configuration.put(ConfigurationKey.Name, jfrFile.getName());
System.out.println(toDot(model, configuration));
System.out.println(toDot(model, 1000, configuration));
}
}
@@ -39,13 +39,19 @@
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.Base64;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.stream.Stream;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuCreator;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.SWT;
@@ -56,13 +62,16 @@
import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.ViewPart;
import org.openjdk.jmc.common.item.IItemCollection;
import org.openjdk.jmc.common.util.Pair;
import org.openjdk.jmc.common.util.StringToolkit;
import org.openjdk.jmc.flightrecorder.ext.graphview.graph.DotGenerator;
import org.openjdk.jmc.flightrecorder.stacktrace.FrameSeparator;
@@ -99,11 +108,14 @@
private final FrameSeparator separator;
private IItemCollection items;
private volatile boolean isInvalid;
private final int maxNodesRendered;

private ModelRebuildRunnable(GraphView view, FrameSeparator separator, IItemCollection items) {
private ModelRebuildRunnable(GraphView view, FrameSeparator separator, IItemCollection items,
int maxNodesRendered) {
this.view = view;
this.items = items;
this.separator = separator;
this.maxNodesRendered = maxNodesRendered;
}

private void setInvalid() {
@@ -121,12 +133,12 @@ public void run() {
if (isInvalid) {
return;
}
String json = GraphView.toDot(model);
String dotString = GraphView.toDot(model, maxNodesRendered);
if (isInvalid) {
return;
} else {
view.modelState = ModelState.FINISHED;
DisplayToolkit.inDisplayThread().execute(() -> view.setModel(items, json));
DisplayToolkit.inDisplayThread().execute(() -> view.setModel(items, dotString));
}
}
}
@@ -150,11 +162,16 @@ public Thread newThread(Runnable r) {
private IItemCollection currentItems;
private volatile ModelState modelState = ModelState.NONE;
private ModelRebuildRunnable modelRebuildRunnable;
private int maxNodesRendered = 100;

@Override
public void init(IViewSite site, IMemento memento) throws PartInitException {
super.init(site, memento);
frameSeparator = new FrameSeparator(FrameCategorization.METHOD, false);

IToolBarManager toolBar = site.getActionBars().getToolBarManager();
toolBar.add(new NodeThresholdSelection());

getSite().getPage().addSelectionListener(this);
}

@@ -164,6 +181,66 @@ public void dispose() {
super.dispose();
}

private class NodeThresholdSelection extends Action implements IMenuCreator {
private Menu menu;
private final List<Pair<String, Integer>> items = List.of(new Pair<>("100", 100), new Pair<>("500", 500),
new Pair<>("1000", 1000));

NodeThresholdSelection() {
super("Max Nodes", IAction.AS_DROP_DOWN_MENU);
setMenuCreator(this);
}

@Override
public void dispose() {
// do nothing
}

@Override
public Menu getMenu(Control parent) {
if (menu == null) {
menu = new Menu(parent);
populate(menu);
}
return menu;
}

@Override
public Menu getMenu(Menu parent) {
if (menu == null) {
menu = new Menu(parent);
populate(menu);
}
return menu;
}

private void populate(Menu menu) {
for (Pair<String, Integer> item : items) {
ActionContributionItem actionItem = new ActionContributionItem(
new SetNodeThreshold(item, item.right == maxNodesRendered));
actionItem.fill(menu, -1);
}
}
}

private class SetNodeThreshold extends Action {
private int value;

SetNodeThreshold(Pair<String, Integer> item, boolean isSelected) {
super(item.left, IAction.AS_RADIO_BUTTON);
this.value = item.right;
setChecked(isSelected);
}

@Override
public void run() {
if (maxNodesRendered != value) {
maxNodesRendered = value;
triggerRebuildTask(currentItems);
}
}
}

@Override
public void createPartControl(Composite parent) {
container = new SashForm(parent, SWT.HORIZONTAL);
@@ -207,7 +284,7 @@ private void triggerRebuildTask(IItemCollection items) {

currentItems = items;
modelState = ModelState.NOT_STARTED;
modelRebuildRunnable = new ModelRebuildRunnable(this, frameSeparator, items);
modelRebuildRunnable = new ModelRebuildRunnable(this, frameSeparator, items, maxNodesRendered);
if (!modelRebuildRunnable.isInvalid) {
MODEL_EXECUTOR.execute(modelRebuildRunnable);
}
@@ -241,15 +318,15 @@ public void completed(ProgressEvent event) {
});
}

private static String toDot(StacktraceGraphModel model) {
private static String toDot(StacktraceGraphModel model, int maxNodesRendered) {
if (model == null) {
return "\"\"";
}
return render(model);
return render(model, maxNodesRendered);
}

private static String render(StacktraceGraphModel model) {
return DotGenerator.toDot(model, DotGenerator.getDefaultConfiguration());
private static String render(StacktraceGraphModel model, int maxNodesRendered) {
return DotGenerator.toDot(model, maxNodesRendered, DotGenerator.getDefaultConfiguration());
}

private static String loadLibraries(String ... libs) {

0 comments on commit b6fe7ca

Please sign in to comment.