使用clang创建一个文件的调用图 [英] Create a call graph for a file with clang

查看:526
本文介绍了使用clang创建一个文件的调用图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有办法创建一个可以合理放在页面上的俚语的调用图?



给定:

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

I current get



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

(这看起来很大的努力,没有预期会那么难)。我想在水平轴上得到一些更合理的东西。 Unflatten 似乎没有任何影响(至少在这个文件,对其他文件,它似乎影响最小)。



有没有办法确保生成的 png 文件可以舒适地适合页面(任何标准尺寸)?



请注意:上述代码取自绘制图形。



编辑:如何自动插入多个节点。



给出 .dot 文件 test1.dot 如下:

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

...生成所显示的图表。



>



... running dot -Tplain test1.dot> test1.plain 提供文件 test1.plain

 图1 2.75 2.5 
节点n1 1.375 2.25 0.75 0.5 n1实心椭圆黑色lightgrey
节点n20 0.375 1.25 0.75 0.5 n20实心椭圆黑色lightgrey
节点n21 1.375 1.25 0.75 0.5 n21实心椭圆黑色lightgrey
节点n22 2.375 1.25 0.75 0.5 n22实心椭圆黑色lightgrey
节点n3 1.375 0.25 0.75 0.5 n3实心椭圆形黑色lightgrey
边缘n1 n20 4 1.1726 2.0394 1.0313 1.9019 0.83995 1.7159 0.68013 1.5605实心黑色
边缘n1 n21 4 1.375 1.9958 1.375 1.8886 1.375 1.7599 1.375 1.6405实心黑色
边缘n1 n22 4 1.5774 2.0394 1.7187 1.9019 1.9101 1.7159 2.0699 1.5605实心黑色
边缘n20 n3 4 0.57736 1.0394 0.71875 0.90191 0.91005 0.71592 1.0699 0.56054实心黑色
边缘n21 n3 4 1.375 0.99579 1.375 0.88865 1.375 0.7599 1.375 0.64045实心黑色
边缘n22 n3 4 2.1726 1.0394 2.0313 0.90191 1.8399 0.71592 1.6801 0.56054纯黑
stop

现在可以一起处理这两个文件。我将使用Python为此,因为它是一个更容易做到在Python比Awk。为了这个例子,我已经将一个rank中的节点数量限制为2,并且我已经使用由默认的从下到上排序而不是从左到右排序定义的排名, ve建议上面。我不知道由 clang 输出的是什么类型的 .dot 文件,因此可能需要修改这个例子有点考虑到这一点。

  import sys,re; 

plain = open(sys.argv [2])
nodesInRank = {}
在纯文本行中:
x = line.split()
rankloc = 3#rank在垂直情况的y列中。
#对于水平情况,将此更改为rankloc = 2如果len(x)> 0和x [0] ==node:
nodesInRank [x [rankloc]] = nodesInRank.get(x [rankloc],[])+ [x [1]]
$ b b maxNodesInRank = 2
dummies = set()
对于nodesInRank.values()中的n:
如果len(n)> maxNodesInRank:
dummies = dummies | set(n [:len(n)// 2])

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

给定这个Python脚本, code> breakrank.py ,我现在可以运行它:

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

code> 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 ,我们现在得到:





...这给了我们想要的,我想。


Is there a way to create a call graph with clang that can reasonably fit on a page?

i.e. given:

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

I current get

by using:

$ 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

(which seems like a lot of effort to do something that I would not have expected to be so difficult). I would like to get something that is a bit more reasonable on the horizontal axis. Unflatten seems not to have any impact (at least on this file, on other files it seems to have minimal effect).

Is there a way to ensure that the png file that is generated can comfortably fit on a page (any standard size)?

Note: Code for the above taken from Generate calling graph for C++ code

Update: Setting page="8.5,11" gives the following:

解决方案

I think the first thing to do would be to set the direction of the graph from the default bottom-to-top ranking to left-to-right, by inserting:

rankdir=LR;

... near the top of the .dot file, after the first {. That should orient the graph left-to-right and thereby make it much more compact for a case like this that has long node labels. Exactly how this could be done would depend on the format of callgraph.dot but, assuming it looks something like this:

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

... then something like:

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

... would do the job.

Another approach that I've taken in the past is to insert dummy nodes into edges to reduce the number of nodes that have the same rank (and would therefore be drawn in the same row (with rankdir=TB, which is the default) or column (with rankdir=LR). This is straightforward to do when writing .dot files by hand but harder to script.

If you wanted to script inserting extra nodes in some edges to spread nodes that would normally be at the same rank over several ranks, you could do this by running dot -Tplain to output a plain text file* that includes (among other things) a list of nodes with the X and Y co-ordinates of the center of each node. Then, you could use gawk to read that list, find any large group of nodes with the same X co-ordinate (if rankdir=TB) or Y co-ordinate (if rankdir=LR) and then process the original .dot file to insert an extra blank node before (say) half of the nodes in that group so that the group was spread over two ranks rather than one. I haven't had occasion to do this myself, though.

*See Emden Gansner, Eleftherios Koutsofios and Stephen North (2006) Drawing graphs with dot, Appendix B.

EDIT: How to automatically insert extra nodes.

Given a .dot file test1.dot as follows:

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

... which produces the graph shown.

... running dot -Tplain test1.dot >test1.plain gives the file 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

Thus, we can now process the two files together. I'll use Python for this because it is a bit easier to do it in Python than in Awk. For the sake of this example, I've limited the number of nodes in a rank to 2 and I've used the rank as defined by the default bottom-to-top ordering rather than the left-to-right ordering that I've suggested above. I don't know exactly what sort of .dot file is being output by clang, so there may be a need to modify this example a bit to take that into account.

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)

Given this Python script, which I've called breakrank.py, I can now run it as:

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

... which puts the following in 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
}

If we run this through dot, we now get:

... which gives us what we want, I think.

这篇关于使用clang创建一个文件的调用图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆