2013-03-17 29 views
10

有没有办法用铿锵声创建可以合理地放在页面上的调用图?为带有叮当声的文件创建调用图

即给定:使用

#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

我想的第一件事可以通过插入:

将图形的方向从默认的从下到上的排名从左到右设置为从左到右

...靠近.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.

编辑:如何自动插入额外的节点。

给定一个文件.dottest1.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,并且我已经使用默认的从下到上排序而不是从左到右排序定义的排名,上面已经提出过。我不知道clang文件是由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 2013-04-25 23:05:45

+0

是否有可能得到一个排名靠前的节点列表?我认为,如果可以这样做,脚本将更容易 – soandos 2013-04-25 23:14:52

+0

不幸的是,我不知道一种方法来分裂一个排名,而不插入虚拟节点。我尝试过对边缘加权不均匀,但不会调整'dot'布局的节点的等级,这是由拓扑严格定义的。如果您使用某些其他布局包(例如'neato'),则使用差分加权边可以聚集一些节点并分隔其他节点。但是,您失去了使用'dot'获得的等级顺序。 – Simon 2013-04-25 23:26:35

相关问题