LLVM CFG/DFG控制流图和数据流图可视化

article/2025/9/3 13:59:17

1.引言        

由于最近在学习数据流分析的相关知识,记录一下利用LLVM生成CFG和DFG的学习过程,参考文献和网址放在文章末尾。

2.实验环境

操作系统:Ubuntu 20.04.3 LTS 64bit;

硬件设备:Intel® Celeron(R) CPU N3450 @ 1.10GHz × 4

                  AMD® Hainan / Mesa Intel® HD Graphics 500 (APL 2)

                  LLVM 10.0.0-4

                  Clang version 10.0.0-4ubuntu1

3.实验记录

3.1 实验步骤

     1.首先利用LLVM的编译前端Clang将高级语言程序(C/C++)转换成LLVM IR中间表示结果;

     2.利用事先写好的分析Pass(分析Pass写好后需要进行重新编译LLVM,然后在LLVM的lib库中生成运行时的库.so文件),具体的CFG生成Pass的伪代码如下所示,主要流程是遍历整个函数,找到基本块然后对每个基本块进行遍历,找到目标操作符,锁定目标操作符的行号上下级信息完成控制流信息的获取;

DFG的分析Pass类似CFG分析Pass,基本步骤一样,但是根据我在网上看到一些调用命令,发现CFG的生成好像可以直接利用LLVM的opt工具获得dot文件,但是DFG的似乎没有,因为本人刚接触这个工具,所以不是很熟悉,知道的朋友可以在评论区补充一下,关于CFG/DFG的分析Pass放在文章的附录部分,有需要的可以自行查看。

     3.利用该Pass分析程序的控制流信息和程序调用信息,并通过LLVM的opt工具调用分析Pass生成的.so文件生成.dot文件;

     4.由于LLVM没有可视化工具,需要借助第三方可视化平台graphviz,利用其dot文件转化工具将.dot转化为.png或.pdf文件,安装命令如下:

sudo apt-get install -y graphviz-doc libgraphviz-dev graphviz

3.2 实验结果

本文实验程序如下所示:

//file test.c
#include<srdio.h>
int add(int c,int e){return c+e;
}
int main(){int a = 10;int b = a;return add(a,b);
}

首先通过Clang生成LLVM IR文件:

Clang -S -emit-llvm test.c -o test.ll

生成调用的控制流图:

opt -dot-callgraph test.ll

根据生成的dot文件名称,使用以下命令来生成图片:

dot callgraph.dot -Tpng -o testgraph.png #生成调用图片

得到的控制流图如下:

 接下来获取函数内部指令调度的命令如下:

opt -dot-cfg test.ll #生成dot文件

运行后,会在当前的文件夹目录下得到一个.dot文件,不同版本的LLVM得到的.dot文件可能不一样,LLVM10.0.0得到的文件名为.main.dot,然后输入以下命令生成图片:

dot .main.dot -Tpng -o cc.png

得到以下IR指令的控制流图:

 接下来获取程序的DFG图,同样是在程序转换成中间代码LLVM IR的基础上进行操作,首先将自己写好的分析Pass进行编译,生成.so文件放在build文件夹中。使用如下命令:

opt -load /home/lwq/Desktop/LLVM-CFG-DFG-pass-master/CDFG/DFGPass/build/DFG/libLLVMDFG.so -DFGPass<test.ll> /dev/null

其中load指令后面为编译后文件所在位置,接下来会得到不同函数的.dot文件,选择all.dot文件,输入以下命令:

dot -Tpng all.dot -o ccc.png

则得到下面的DFG图

 4.总结

这些简单的CFG/DFG图生成只是博主简单调用了一些相关工具,在此基础上可以进行代码优化和改进,或者写出一些效率更好的Pass,并且由于LLVM安装过程中的编译问题,似乎只有Debug版本的LLVM才可以使用可视化的命令,本来还想利用LLC工具中的llc -view-combine1-dags test.ll输出程序的DAG图,但是本人是直接使用sudo apt install llvm命令下载的LLVM工具,据说是release版本,所以我调用LLC工具的时候发现并没有上述的DAG指令,因此感兴趣的朋友可以自己试试生成DAG图,成功的话可以在评论区告诉一下我。

最后DAGpass分析的程序如下:

#include"graph.h"using namespace llvm;
namespace {struct DFGPass : public ModulePass {public:static char ID;map<string, Graph*> DFGs;map<string, Graph*> CFGs;DFGPass() : ModulePass(ID) {}bool runOnModule(Module &M) override {for (Module::iterator iter_F = M.begin(), FEnd = M.end(); iter_F != FEnd; ++iter_F) {Function *F = &*iter_F;Graph* control_flow_G = new Graph(F);Graph* data_flow_G = new Graph(F);// F->viewCFG();DFGs.insert(pair<string, Graph*>(F->getName().str(), data_flow_G));CFGs.insert(pair<string, Graph*>(F->getName().str(), control_flow_G));control_flow_G->head.push_back(pair<Value*, Value*>(&*(F->begin())->begin(), &*(F->begin())->begin()));for (Function::iterator BB = F->begin(), BEnd = F->end(); BB != BEnd; ++BB) {BasicBlock *curBB = &*BB;for (BasicBlock::iterator II = curBB->begin(), IEnd = curBB->end(); II != IEnd; ++II) {Instruction* curII = &*II;switch (curII->getOpcode()){// for the case of load operation, we should save the value of itcase llvm::Instruction::Load:{LoadInst* linst = dyn_cast<LoadInst>(curII);Value* loadValPtr = linst->getPointerOperand();insert(data_flow_G, pair<Value*, Value*>(loadValPtr, curII));break;}// for the case of store operation, both of the pointer and value should be recodedcase llvm::Instruction::Store: {StoreInst* sinst = dyn_cast<StoreInst>(curII);Value* storeValPtr = sinst->getPointerOperand();Value* storeVal = sinst->getValueOperand();insert(data_flow_G, pair<Value*, Value*>(storeVal, curII));insert(data_flow_G, pair<Value*, Value*>(curII, storeValPtr));data_flow_G->head.push_back(pair<Value*, Value*>(storeValPtr, storeVal));break;}case llvm::Instruction::Call: {CallInst* cinst = dyn_cast<CallInst>(curII);string f_name = cinst->getCalledFunction()->getName();for(auto iter = DFGs[f_name]->F->arg_begin(), iter_end = DFGs[f_name]->F->arg_end(); iter != iter_end; iter++){data_flow_G->link.push_back(pair<Value*, Value*>(cinst, iter));errs()<<*cinst<<cinst<<"->"<<*iter<<iter<<"\n";// insert(data_flow_G, pair<Value*, Value*>(cinst, iter));}if(!DFGs[f_name]->F->doesNotReturn()){Value* ret_i = &*(--(--DFGs[f_name]->F->end())->end());data_flow_G->link.push_back(pair<Value*, Value*>(ret_i, cinst));// insert(data_flow_G, pair<Value*, Value*>(ret_i, cinst));}}// for other operation, we get all the operand point to the current instructiondefault: {for (Instruction::op_iterator op = curII->op_begin(), opEnd = curII->op_end(); op != opEnd; ++op){Instruction* tempIns;if (dyn_cast<Instruction>(*op)){insert(data_flow_G, pair<Value*, Value*>(op->get(), curII));}}break;}}BasicBlock::iterator next = II;++next;if (next != IEnd) {insert(control_flow_G, pair<Value*, Value*>(curII, &*next));}}Instruction* terminator = curBB->getTerminator();for (BasicBlock* sucBB : successors(curBB)) {Instruction* first = &*(sucBB->begin());insert(control_flow_G, pair<Value*, Value*>(terminator, first));}}writeFileByGraph(F);}// NOTWITHCFHG indicate the fianl graph represents no CFG informationwriteFileByGraphGloble(NOTWITHCFG);errs()<<"end\n";return false;}void DFS_plot(Edge* v, Graph* G, raw_fd_ostream& file){Edge* p = v;while (p){if (mark.find(pair<int, int>(p->v_from, p->v_to)) == mark.end()) {mark.insert(pair<int, int>(p->v_from, p->v_to));file << "\tNode" << G->v[p->v_from]->va << " -> Node" << G->v[p->v_to]->va << "\n";DFS_plot(G->v[p->v_to]->first_out, G, file);}p = p->out_edge;}}void writeFileByGraph(Function *F){std::error_code error;enum sys::fs::OpenFlags F_None;StringRef fileName(F->getName().str() + ".dot");raw_fd_ostream file(fileName, error, F_None);Graph* data_flow_G =  DFGs[F->getName().str()];Graph* control_flow_G = CFGs[F->getName().str()];file << "digraph \"DFG for'" + F->getName() + "\' function\" {\n";for (auto node_iter = DFGs[F->getName()]->v.begin(), node_end = DFGs[F->getName()]->v.end(); node_iter != node_end; ++node_iter) {Value* p = (*node_iter)->va;if(isa<Instruction>(*p)){file << "\tNode" << p << "[shape=record, label=\"" << *p << "\"];\n";}else{file << "\tNode" << p << "[shape=ellipse, label=\"" << *p << "\\l" << p << "\"];\n";}}// plot the instruction flow edgemark.clear();for(auto iter = control_flow_G->head.begin(), iter_end = control_flow_G->head.end(); iter != iter_end; iter++){DFS_plot(control_flow_G->v[find(control_flow_G->v, iter->second)]->first_out, control_flow_G, file);}// plot the data flow edgefile << "edge [color=red]" << "\n";mark.clear();for(auto iter = data_flow_G->head.begin(), iter_end = data_flow_G->head.end(); iter != iter_end; iter++){DFS_plot(data_flow_G->v[find(data_flow_G->v, iter->second)]->first_out, data_flow_G, file);}file << "}\n";file.close();}void writeFileByGraphGloble(Mode m){std::error_code error;enum sys::fs::OpenFlags F_None;StringRef fileName("all.dot");raw_fd_ostream file(fileName, error, F_None);file << "digraph \"DFG for all\" {\n";for(auto F_iter = DFGs.begin(), F_iter_end = DFGs.end(); F_iter != F_iter_end; F_iter++){Graph* data_flow_G =  DFGs[F_iter->first];Graph* control_flow_G = CFGs[F_iter->first];auto nodes = F_iter->second->v;for (auto node_iter = nodes.begin(), node_end =  nodes.end(); node_iter != node_end; ++node_iter) {Value* p = (*node_iter)->va;if(isa<Instruction>(*p)){file << "\tNode" << p << "[shape=record, label=\"" << *p << "\"];\n";}else{file << "\tNode" << p << "[shape=ellipse, label=\"" << *p << "\\l" << p << "\"];\n";}}// plot the instruction flow edgeif(m != NOTWITHCFG){file << "edge [color=black]" << "\n";mark.clear();for(auto iter = control_flow_G->head.begin(), iter_end = control_flow_G->head.end(); iter != iter_end; iter++){DFS_plot(control_flow_G->v[find(control_flow_G->v, iter->second)]->first_out, control_flow_G, file);}}// plot the data flow edgevector<string> color_set = {"red", "blue", "cyan", "orange", "yellow"};mark.clear();int count = 0;for(auto iter = data_flow_G->head.begin(), iter_end = data_flow_G->head.end(); iter != iter_end; iter++){file << "edge [color=" << color_set[count++] << "]" << "\n";DFS_plot(data_flow_G->v[find(data_flow_G->v, iter->second)]->first_out, data_flow_G, file);}for(auto iter = data_flow_G->link.begin(), iter_end = data_flow_G->link.end(); iter != iter_end; iter++){file << "edge [color=grey]" << "\n";file << "\tNode" << iter->first << " -> Node" << iter->second << "\n";errs() << *iter->first << *iter->second << "\n";}}file << "}\n";file.close();}};
}char DFGPass::ID = 0;
static RegisterPass<DFGPass> X("DFGPass", "DFG Pass Analyse",false, false
);

5.参考

在LLVM中可视化代码结构_梦在哪里的博客-CSDN博客_llvm 可视化

LLVM CFG控制流图可视化_ronnie88597的博客-CSDN博客_llvm 控制流图

[1]赵德硕. 面向LLVM编译器的线程级推测执行模型的研究[D].西北农林科技大学,2021.DOI:10.27409/d.cnki.gxbnu.2021.001026.


http://chatgpt.dhexx.cn/article/OgTuHTcU.shtml

相关文章

控制流图、圈复杂度

继续上次的测试作业&#xff0c;学习完程序插装的概念&#xff0c;今天学习测试的静态分析方法&#xff1a;绘制控制流图与计算圈复杂度。 一、控制流图&#xff1a; 一个过程或程序的抽象表现&#xff0c;常以数据结构链的形式表示。 二、圈复杂度&#xff1a; 复杂度越高&…

软件评测师必考题-控制流图

控制流图的基本知识 首先我们得清楚控制流图中的几个判断循环是如何表示的&#xff1a; 判断节点的嵌套 清楚了上面表示方法&#xff0c;你还是很难画出复杂的控制流图&#xff0c;而软考的控制流图往往是2个或多个判断节点嵌套在一起。其实只要把嵌套的节点想象成被嵌套节点…

软件中级-控制流图基本知识

软件中级-控制流图基本知识 什么是控制流图&#xff1f; 控制流图(Control Flow Graph, CFG)也叫控制流程图&#xff0c;是一个过程或程序的抽象表现&#xff0c;是用在编译器中的一个抽象数据结构&#xff0c;代表了一个程序执行过程中会遍历到的所有路径。 控制流图中包含…

程序控制流图

基本符号 ps&#xff1a;请将线看成弧线[doge] 顺序结构 if选择结构 while循环结构 case多分支结构 控制流图由节点和控制流线&#xff08;弧&#xff09;两种符号组成。 结点以标有编号的圆圈表示&#xff0c;用于表示程序流程图中矩形框、菱形框的功能&#xff0c;是一…

控制流图怎么画

一、什么是控制流图&#xff1f; 控制流图(Control Flow Graph, CFG)也叫控制流程图&#xff0c;是一个过程或程序的抽象表现&#xff0c;是用在编译器中的一个抽象数据结构&#xff0c;由编译器在内部维护&#xff0c;代表了一个程序执行过程中会遍历到的所有路径。它用图的形…

软工——各种图

目录 一.因果图二.控制流图三.程序流程图四.数据流图数据流数据流图的画法&#xff1a;由简入繁父图子图平衡保持数据守恒数据字典 五.N-S盒图六.PAD盒图七.操作状态图八.用例图、活动图、顺序图九.类图十.Jackson图十一.IPO图 一.因果图 因果图法&#xff1a;是一种利用图解法…

软件测试之控制流图

为了应对软件工程考试&#xff0c;本文对控制流图常见考法进行整理&#xff0c;主要是针对软件评测师的题型来整理。 什么是控制流图 控制流图是一个过程或程序的抽象表现&#xff0c;常以数据结构链的形式表示。简称流图&#xff0c;是对程序流程图进行简化后得到的&#xf…

控制流图(Control Flow Graph)-(CFG)

1.定义 百度百科&#xff1a; 控制流图(Control Flow Graph, CFG)也叫控制流程图&#xff0c;是一个过程或程序的抽象表现&#xff0c;是用在编译器中的一个抽象数据结构&#xff0c;由编译器在内部维护&#xff0c;代表了一个程序执行过程中会遍历到的所有路径。它用图的形式…

【大学生软件测试基础】白盒测试 - 控制流图 - 01

任务1、画出程序流程图&#xff1b; 任务2、画出控制流图&#xff1b; 任务3、根据程序环形复杂度的计算公式&#xff0c;求出程序路径集合中的独立路径数目&#xff1b; 任务4、根据环形复杂度的计算结果&#xff0c;源程序的基本路径集合中有多少条独立路径&#xff1b; …

控制流图(Control Flow Graph, CFG)

The if Statement if (x < y) {y 0;x x 1; } else {x y; } if (x < y) {y 0;x x 1; } The if-return Statement if (x < y) {return; } print (x); return; 注意&#xff1a;2到3 没有边 while and for Loops x 0; while (x < y) {y f (x, y);x x …

C语言之美——平方根倒数快速计算

C语言之美——平方根倒数快速计算 前言 由于特殊原因&#xff0c;陆陆续续接触陀螺仪很长一段时间&#xff0c;对于各种解析算法的运算速率有了切身体会&#xff0c;不断追求更快、更准。最近&#xff0c;发现了一份比较特殊的平方根倒数速算法&#xff0c;一下子来了兴趣&am…

【C语言求素数(质数)的三种方法】

失踪人口回归&#xff0c;假期因为太懒&#xff0c;刚开学的这几天又真的忙&#xff0c;所以好长时间没有发文章了&#xff0c;马上我们要进行C语言考试了&#xff0c;我发现学的东西好多都不太懂&#xff0c;所以慢慢要在进行一次复习了&#xff1b;上周数据结构课上老师让写程…

平方根求和c语言编程,计算并输出3到100(包括3和100)之间所有素数之和的平方根。 C语言,求救...

满意答案 zh19951006 2014.07.02 采纳率&#xff1a;56% 等级&#xff1a;12 已帮助&#xff1a;6562人 //先求3到100间的所有素数,存在数组中; //求该数组的所有为素数的元素之和,然后开方求平方根 #include <stdio.h> #include <math.h> int sushu[98];//开辟…

word封面下划线怎么对齐(非常简单+彻底解决)

之前遇到这个问题&#xff0c;也在网上百度了很多次&#xff0c;有的说段落调整对齐方式为两端对齐&#xff0c;对称什么的&#xff0c;试了一下都不管用&#xff0c;也有的说是加一个表格&#xff0c;但是并没有解决根本问题&#xff0c;最后突然想起&#xff0c;有一次打印东…

【Office】Word空格无法添加下划线

缘由 在弄文档封面的时候&#xff0c;需要在项目名称后面加一条下划线 于是出现了这个情况&#xff1a; 1.在没有内容的一行&#xff0c;带下划线的空格直接不显示下划线了&#xff08;下图第一行&#xff09; 2.空格应用下划线格式&#xff0c;内容前面的是能显示的&#…

Word公式居中,公式编号右对齐

1. 选择开始-样式-新建样式。编辑名称为公式 2. 左下角选择格式-制表位 打开Word标尺&#xff0c;找到中间和最右边的位置。比如我这里是17和34&#xff0c;输入位置分别设置居中对齐和右对齐 3. 在样式中选择公式 4. 在公式右侧键入编号&#xff0c;然后将光标放在公式左侧&am…

php输入域无法对齐,下划线输入内容后怎么对齐

下划线输入内容后设置对齐的方法&#xff1a;首先打开Word&#xff1b;然后依次选择“菜单栏->插入->表格”&#xff1b;接着将内容分别输入表格中&#xff0c;并调整表格的列宽等格式&#xff1b;最后右键选择“边框和底纹”&#xff0c;并设置只保留底部边框和中间横线…

Word 里文字对齐的4种方法

转自&#xff1a;微点阅读 https://www.weidianyuedu.com 我们在用Word写论文、制作简历的时候&#xff0c;通常会遇到把word中某些特定文字对齐的情况。那么问题来了&#xff0c;你平时都是怎么对齐文字的&#xff1f;傻傻的用空格来对齐吗&#xff1f; 在字符数不等的情况下&…

Word 里文字对齐4种方法推荐

我们在用Word写论文、制作简历的时候&#xff0c;通常会遇到把word中某些特定文字对齐的情况。那么问题来了&#xff0c;你平时都是怎么对齐文字的&#xff1f;傻傻的用空格来对齐吗&#xff1f; 在字符数不等的情况下&#xff0c;加空格不仅麻烦而且不准确&#xff0c;下面传…

Windows系统中Word文档中文字后面的空格下的下划线不显示的解决办法

笔者最近在做课程设计&#xff0c;需要写Word文档&#xff0c;在写文档的时候遇到了一堆bug&#xff0c;现在课程设计肝完了&#xff0c;终于有时间梳理梳理这些bug然后发布出来做个备忘录。    第一个bug是Windows系统中Word文档中文字后面的空格下的下划线不显示&#xff0…