2013-03-17 8 views
10

Есть ли способ создать граф вызовов с clang, который может удобно поместиться на странице?Создать граф вызовов для файла с clang

т.е. Дано:

#include<iostream> 
using namespace std; 
int main() 
{ 
    int a; 
    cin>>a; 
    cout<<a; 
    cout<<a; 
    return 0; 
} 

Я тока получить enter image description here

с помощью:

$ clang++ main.cpp -S -emit-llvm -o - | 
opt -analyze -std-link-opts -dot-callgraph 
$ cat callgraph.dot | c++filt | 
sed 's,>,\\>,g; s,-\\>,->,g; s,<,\\<,g' | 
gawk '/external node/{id=$1}$1!=id' | dot -Tpng -ocallgraph.png 

(который, кажется, как много усилий, чтобы сделать что-то, что я бы не ожидал быть настолько трудным). Я хотел бы получить что-то более разумное на горизонтальной оси. Unflatten, похоже, не оказывает никакого влияния (по крайней мере, на этот файл, на другие файлы он, кажется, имеет минимальный эффект).

Есть ли способ гарантировать, что создаваемый файл png может удобно размещаться на странице (любой стандартный размер)?

Примечание: Код для выше взяты из Generate calling graph for C++ code

Обновление: Настройка страницы = "8.5,11" дает следующее:

enter image description here

ответ

6

Я думаю, что первое, что нужно do должно было бы установить направление графика от нижнего к верхнему ранжированию по умолчанию слева направо, вставив:

rankdir=LR; 

... в верхней части файла .dot, после первого {. Это должно ориентировать график слева направо и тем самым сделать его гораздо более компактным для такого случая, который имеет длинные метки узлов. Точно, как это могло быть сделано, будет зависеть от формата callgraph.dot но, предполагая, что это выглядит примерно так:

digraph G { 
    node [shape=rectangle]; 
    ... 

... потом что-то вроде:

sed 's/digraph G {/digraph G { \n rankdir=LR;/' 

... будет делать работу ,

Другой подход, который я использовал в прошлом, заключается в том, чтобы вставить фиктивные узлы в ребра, чтобы уменьшить количество узлов с одинаковым рангом (и поэтому будет рисоваться в одной строке (с rankdir=TB, который является значением по умолчанию) или столбец (с rankdir=LR). Это просто сделать при написании .dot файлов вручную, но сложнее сценария.

Если вы хотите, чтобы скрипт вставки дополнительных узлов в некоторых краях распространяться узлы, которые, как правило, на том же звании в нескольких разрядах вы можете сделать это, выполнив dot -Tplain для вывода текстового файла *, который включает (среди прочего) список узлов с координатами X и Y центра каждого узла. Затем вы можете использовать gawk, чтобы прочитать этот список, найдите любую большую группу узлов с той же координатой X (если rankdir=TB) или координатой Y (если rankdir=LR), а затем обработайте исходный файл .dot, чтобы вставить дополнительный пустой узел до (скажем) половины узлов этой группы, чтобы группа была распределена по двум рангом, а не по одной. Однако у меня не было случая сделать это сам.

* См Эмден Gansner, Элефтериоса Koutsofios и Стивен Север (2006) Drawing graphs with dot, Приложение B.

EDIT: Как автоматически вставить дополнительные узлы.

Учитывая .dot файл test1.dot следующим образом:

digraph G { 
    n1 -> n20 
    n1 -> n21 
    n1 -> n22 
    n20 -> n3 
    n21 -> n3 
    n22 -> n3 
} 

... который дает график, показанный.

enter image description here

... работает dot -Tplain test1.dot >test1.plain дает файл test1.plain:

graph 1 2.75 2.5 
node n1 1.375 2.25 0.75 0.5 n1 solid ellipse black lightgrey 
node n20 0.375 1.25 0.75 0.5 n20 solid ellipse black lightgrey 
node n21 1.375 1.25 0.75 0.5 n21 solid ellipse black lightgrey 
node n22 2.375 1.25 0.75 0.5 n22 solid ellipse black lightgrey 
node n3 1.375 0.25 0.75 0.5 n3 solid ellipse black lightgrey 
edge n1 n20 4 1.1726 2.0394 1.0313 1.9019 0.83995 1.7159 0.68013 1.5605 solid black 
edge n1 n21 4 1.375 1.9958 1.375 1.8886 1.375 1.7599 1.375 1.6405 solid black 
edge n1 n22 4 1.5774 2.0394 1.7187 1.9019 1.9101 1.7159 2.0699 1.5605 solid black 
edge n20 n3 4 0.57736 1.0394 0.71875 0.90191 0.91005 0.71592 1.0699 0.56054 solid black 
edge n21 n3 4 1.375 0.99579 1.375 0.88865 1.375 0.7599 1.375 0.64045 solid black 
edge n22 n3 4 2.1726 1.0394 2.0313 0.90191 1.8399 0.71592 1.6801 0.56054 solid black 
stop 

Таким образом, теперь мы можем обрабатывать два файла вместе. Я буду использовать Python для этого, потому что это немного проще сделать в Python, чем в Awk. Для этого примера я ограничил количество узлов в ранге равным 2, и я использовал ранжирование, как определено по умолчанию для верхнего порядка, а не слева направо, что я ' вышесказанное выше. Я точно не знаю, какой файл .dot выводится на clang, поэтому может потребоваться немного изменить этот пример, чтобы принять это во внимание.

import sys,re; 

plain = open(sys.argv[2]) 
nodesInRank = {} 
for line in plain: 
    x = line.split() 
    rankloc = 3 # rank is in the y column for the vertical case. 
        # Change this to rankloc = 2 for the horizontal case 
    if len(x) > 0 and x[0] == "node": 
     nodesInRank[x[rankloc]] = nodesInRank.get(x[rankloc],[]) + [x[1]] 

maxNodesInRank = 2 
dummies = set() 
for n in nodesInRank.values(): 
    if len(n) > maxNodesInRank: 
     dummies = dummies | set(n[:len(n)//2]) 

dot = open(sys.argv[1]) 
for line in dot: 
    line = line.rstrip() 
    line2 = "" 
    for d in dummies: 
     m = "-> +%s" % (d) 
     if re.search(m,line): 
      line = re.sub(m,"-> dummy_%s [dir = none]\n dummy_%s -> %s" % (d,d,d),line) 
      line2 = '\tdummy_%s [shape=none, width=0, height=0, label=""];' % (d) 
    print (line) 
    if len(line2) > 0: 
     print (line2) 

Учитывая это Python скрипт, который я назвал breakrank.py, теперь я могу запустить его как:

python breakrank.py test1.dot test1.plain >test_dummy.dot 

... который ставит следующие в test_dummy.dot:

digraph G { 
    n1 -> dummy_n20 [dir = none] 
dummy_n20 -> n20 
    dummy_n20 [shape=none, width=0, height=0, label=""]; 
    n1 -> n21 
    n1 -> n22 
    n20 -> n3 
    n21 -> n3 
    n22 -> n3 
} 

Если мы проведем это через dot, мы получим:

enter image description here

... который дает нам то, что мы хотим, я думаю.

+0

Это, безусловно, намного лучше, чем у меня было, но есть способ случайным образом разбить ранг и заставить его отображаться, как будто это два уровня (без явной вставки фиктивных узлов). – soandos

+0

Можно ли получить список узлов, находящихся на ранге? Я думаю, что если это можно сделать, скрипты будут проще – soandos

+0

К сожалению, я не знаю способ разбить ранг без вставки фиктивных узлов. Я пробовал взвешивающие края неравномерно, но это не корректирует ранг узла, как это определено 'dot', который (как представляется) строго определен топологией. Если вы используете некоторые другие пакеты макета (например, 'neato'), использование дифференциально взвешенных ребер может группировать некоторые узлы и разделять другие. Однако вы теряете иерархическое упорядочение, которое вы получаете с помощью 'dot'. – Simon

Смежные вопросы