使用clang为文件创建调用图

soa*_*dos 11 clang graphviz call-graph

有没有办法创建一个可以合理地适合页面的clang调用图?

即给出:

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

我目前得到 在此输入图像描述

通过使用:

$ 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
Run Code Online (Sandbox Code Playgroud)

(这似乎是很多努力去做一些我不希望这么困难的事情).我想在横轴上得到一些更合理的东西.Unflatten似乎没有任何影响(至少在这个文件上,对其他文件似乎影响很小).

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

注意:上面的代码来自生成C++代码的调用图

更新:设置页面="8.5,11"给出以下内容:

在此输入图像描述

Sim*_*mon 6

我认为首先要做的是通过插入以下内容将图表的方向从默认的从下到上的排名设置为从左到右:

rankdir=LR;
Run Code Online (Sandbox Code Playgroud)

...在.dot文件顶部附近,在第一个之后{.这应该使图形从左到右定向,从而使得它具有更紧凑的情况,例如具有长节点标签的情况.究竟如何做到这一点将取决于格式,callgraph.dot但假设它看起来像这样:

digraph G {
    node [shape=rectangle];
    ...
Run Code Online (Sandbox Code Playgroud)

......那样的话:

sed 's/digraph G {/digraph G { \n rankdir=LR;/'
Run Code Online (Sandbox Code Playgroud)

......会做的.

我过去采用的另一种方法是将虚拟节点插入边缘以减少具有相同等级的节点数量(因此将绘制在同一行(使用rankdir=TB,默认情况下)或列(使用rankdir=LR.dot手工编写文件但脚本更难编写时,这很简单.

如果你想脚本插入一些边缘的额外节点来扩展通常在几个等级上相同等级的节点,你可以通过运行dot -Tplain输出一个纯文本文件*,其中包括(除其他之外)一个节点列表使用每个节点中心的X和Y坐标.然后,您可以使用gawk读取该列表,找到具有相同X坐标(if rankdir=TB)或Y 坐标(if )的任何大型节点组,rankdir=LR然后处理原始.dot文件以插入额外的空白节点(比如说)该组中一半的节点,以便该组分布在两个等级而不是一个等级.不过,我自己没有机会这样做.

*见Emden Gansner,Eleftherios Koutsofios和Stephen North(2006)用点绘制图表,附录B.

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

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

digraph G {
    n1 -> n20 
    n1 -> n21 
    n1 -> n22 
    n20 -> n3 
    n21 -> n3 
    n22 -> n3
}
Run Code Online (Sandbox Code Playgroud)

...生成显示的图表.

在此输入图像描述

...运行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
Run Code Online (Sandbox Code Playgroud)

因此,我们现在可以一起处理这两个文件.我将使用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)
Run Code Online (Sandbox Code Playgroud)

鉴于我称之为Python脚本,breakrank.py我现在可以将其运行为:

python breakrank.py test1.dot test1.plain >test_dummy.dot
Run Code Online (Sandbox Code Playgroud)

...将以下内容放入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
}
Run Code Online (Sandbox Code Playgroud)

如果我们通过这个dot,我们现在得到:

在此输入图像描述

......我想,这给了我们想要的东西.