为带有clang的文件创建调用图

为带有clang的文件创建调用图,clang,graphviz,call-graph,Clang,Graphviz,Call Graph,有没有一种方法可以创建一个带有叮当声的调用图,它可以合理地放在一个页面上 i、 e.鉴于: #include<iostream> using namespace std; int main() { int a; cin>>a; cout<<a; cout<<a; return 0; } #包括 使用名称空间std; int main() { INTA; cin>>a; cout我认为首先要做的是通过插入以下

有没有一种方法可以创建一个带有叮当声的调用图,它可以合理地放在一个页面上

i、 e.鉴于:

#include<iostream>
using namespace std;
int main()
{
    int a;
    cin>>a;
    cout<<a;
    cout<<a;
    return 0;
}
#包括
使用名称空间std;
int main()
{
INTA;
cin>>a;

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

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
文件,在该组的一半节点之前(比如)插入一个额外的空白节点,这样该组就分成两个级别,而不是一个级别。不过,我自己还没有机会这样做

*见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
}
…生成所示的图形

…运行
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,并且使用了默认的从下到上的顺序定义的列组,而不是我建议的从左到右的顺序e、 我不知道
clang
输出的是哪种类型的
.dot
文件,因此可能需要对该示例进行一些修改以将其考虑在内

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
…将以下内容放入
测试\u 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
运行此操作,现在可以得到:


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

这肯定比我的好得多,但是有没有一种方法可以随机划分一个等级,并使其显示为两个等级(无需显式插入虚拟节点)有可能得到一个列组上的节点列表吗?我想如果可以的话,脚本编写会更容易。不幸的是,我不知道如何在不插入虚拟节点的情况下分割列组。我尝试过不均匀地对边进行加权,但这并不能调整节点的列组,如
点所示,也就是(看起来)严格按照拓扑定义。如果使用其他一些布局包(例如
neato
),使用差异加权边可以对某些节点进行聚类,并分离其他节点。但是,您将失去使用
dot
获得的层次顺序。如果有办法让dot输出列组中的节点列表,则可以为虚拟节点编写脚本否?检查
-Tplain中输出的节点的X和Y坐标
格式是(对我来说)找到节点排名的最明显的方法。另一种方法是使用自定义图形分析代码分析节点排名,但我认为这比我在回答中建议的方法更难做到。
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
}