How can I influence Graphviz/dot to make nicer control-flow graphs by removing snaking and better edge crossings?

和自甴很熟 提交于 2020-01-14 03:31:05

问题


I am drawing control-flow graphs for Python programs and would like to influence which kind of edges should not be crossed over. Is there a way to do this?

Consider this simple Python program:

try:
    a += 1
except:
    a += 2
else:
    a = 3

And a dot program to represent the control flow for that generated via https://github.com/rocky/python-control-flow/

digraph G {
  mclimit=1.5;
  rankdir=TD; ordering=out;
  graph[fontsize=10 fontname="Verdana"];
  color="#efefef";
  node[shape=box style=filled fontsize=8 fontname="Verdana" fillcolor="#efefef"];
  edge[fontsize=8 fontname="Verdana"];

  node_0 [shape = "oval"][label="Basic Block 0\loffsets: 0..12\lflags=entry, block, unconditional, try\ljumps=[34]\l"];
  node_1 [label="Basic Block 1\loffsets: 14..30\lflags=except, unconditional\ljumps=[38]\l"];
  node_2 [label="Basic Block 2\loffsets: 32..32\lflags=end finally\l"];
  node_3 [label="Basic Block 3\loffsets: 34..36\l"];
  node_4 [label="Basic Block 4\loffsets: 38..40\lflags=no fallthrough\l"];

  node_0 -> node_2 [weight=1][color="red"];
  node_3 -> node_4 [weight=10];
  node_0 -> node_1 [weight=1][color="red"];
  node_2 -> node_3 [weight=10];
  node_0 -> node_1 [weight=10][style="invis"];
  node_1 -> node_2 [weight=10][style="invis"];
  node_1 -> node_4 [weight=1];
  node_0 -> node_3 [weight=1];
}

The image that dot produces for the above is

Notice how one line snakes around and crosses a straight arrow down. Instead I would prefer if none of the straight downward arrows would be crossed. Splined edges would make better crossing places.

If you look at the dot, I have two invisible downward edges which I use for alignment. (In the bytecode these follow the linear sequence of instructions).

So if a downward straight line needs to be crossed (and here it doesn't), invisible edges would be preferred over those that are visible.

Thoughts?

Edit

The one excellent answer so far suggests changing the order that the edges are defined and specifying in certain situations where edge attachments should be made.

In this application, I do have hierarchical nesting information from a dominator tree, and I can classify the edges: those that loop, those that jump to the end of a compound structure, those that break out a loop, and so on.

So now the problem becomes exactly how one uses this information to avoid those snaking arrows, and ensures that breaks out of loops are preferred to cross over edges over say "if"/"else" jump edges.

This feels like VLSI design: come up with a set of patterns that work for each kind of (control-flow) structure and those will then nest and sequence properly.

I have experimented with the edge ordering and placement, and I just don't have an intuitive sense of when to put something earlier or later.

Guidance, or better, a design rule for structured control flow edges would be greatly appreciated.


回答1:


You need to do two things to improve the situation:

  • draw (one of) the edges that you want to control earlier than the others,
  • tell graphviz where you want them to be attached (North, East... )

I have edited your code accordingly

digraph G {
  mclimit=1.5;
  rankdir=TD; ordering=out;
  graph[fontsize=10 fontname="Verdana"];
  color="#efefef";
  node[shape=box style=filled fontsize=8 fontname="Verdana" fillcolor="#efefef"];
  edge[fontsize=8 fontname="Verdana"];

  node_0 [shape = "oval"][label="Basic Block 0\loffsets: 0..12\lflags=entry, block, unconditional, try\ljumps=[34]\l"];
  node_1 [label="Basic Block 1\loffsets: 14..30\lflags=except, unconditional\ljumps=[38]\l"];
  node_2 [label="Basic Block 2\loffsets: 32..32\lflags=end finally\l"];
  node_3 [label="Basic Block 3\loffsets: 34..36\l"];
  node_4 [label="Basic Block 4\loffsets: 38..40\lflags=no fallthrough\l"];

  node_0 -> node_3:nw [weight=1];           /* moved up and added directions*/
  node_0 -> node_2 [weight=1][color="red"];
  node_3 -> node_4 [weight=10];
  node_0 -> node_1 [weight=1][color="red"];
  node_2 -> node_3 [weight=10];
  node_0 -> node_1 [weight=10][style="invis"];
  node_1 -> node_2 [weight=10][style="invis"];
  node_1:se -> node_4:ne [weight=1];            /* added directions */
}

which gives you

There's a bit of trial and error involved here but I'm sure this should help.



来源:https://stackoverflow.com/questions/53468814/how-can-i-influence-graphviz-dot-to-make-nicer-control-flow-graphs-by-removing-s

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!