Как я могу повлиять на Graphviz/dot, чтобы сделать более приятные графы управления потоками, удаляя змею и улучшая пересечение границ?
Я рисую графы управления потоками для программ на Python и хотел бы повлиять на то, какие края не должны пересекаться. Есть ли способ сделать это?
Рассмотрим эту простую программу на Python:
try:
a += 1
except:
a += 2
else:
a = 3
И точечная программа для представления потока управления для этого сгенерированного через 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];
}
Изображение, которое производит точка для вышеупомянутого, является
Обратите внимание, как одна линия обходит вокруг и пересекает прямую стрелку вниз. Вместо этого я бы предпочел, чтобы ни одна из прямых стрелок вниз не была пересечена. Зубчатые края сделали бы лучшие места пересечения.
Если вы посмотрите на точку, у меня есть два невидимых нисходящих края, которые я использую для выравнивания. (В байт-коде они следуют линейной последовательности инструкций).
Поэтому, если необходимо пересечь прямую линию вниз (а здесь нет), невидимые края предпочтительнее тех, которые видны.
Мысли?
редактировать
Один отличный ответ до сих пор предлагает изменить порядок определения ребер и указать в некоторых ситуациях, где должны быть сделаны ребра.
В этом приложении у меня есть информация иерархического вложения из дерева доминант, и я могу классифицировать ребра: те, которые зацикливаются, те, которые переходят к концу составной структуры, те, которые выходят из цикла, и так далее.
Так что теперь проблема заключается именно в том, как использовать эту информацию, чтобы избежать этих змеиных стрелок, и гарантирует, что разрывы циклов предпочтительнее пересекать края, скажем, "если"/"еще" переходные края.
Это похоже на проектирование СБИС: придумать набор шаблонов, которые работают для каждого типа структуры (потока управления), и тогда они будут правильно вложены и упорядочены.
Я экспериментировал с упорядочением и размещением краев, и у меня просто нет интуитивного ощущения, когда нужно что-то ставить раньше или позже.
Руководство, или, что лучше, правило проектирования для структурированных границ потока управления будет очень цениться.
1 ответ
Вам нужно сделать две вещи, чтобы улучшить ситуацию:
- нарисовать (один из) ребер, которые вы хотите контролировать раньше, чем другие,
- скажи графвизу, где ты хочешь, чтобы они были прикреплены (север, восток...)
Я отредактировал ваш код соответственно
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 */
}
что дает вам
Здесь есть немного проб и ошибок, но я уверен, что это должно помочь.