C++源码的调用图生成
前言
之前受知乎用户mailto1587启发,写了个C++源码的调用图生成器,可以以图示法显示C++函数的调用关系,
代码放在了github仓库里,仅供参考:
CodeSnippet/python/SRCGraphviz/c++ at master · Cheukyin/CodeSnippet · GitHub
主要思路
利用gcc/g++的-finstrument-functions的注入选项,
得到每个函数的调用地址信息,生成一个trace文件,
然后利用addr2line和c++filt将函数名及其所在源码位置从地址中解析出来,
从而得到程序的Call Stack,
然后用pygraphviz画出来
使用示例
比如我现在有A.hpp、B.hpp、C.hpp、ABCTest.cpp这几个文件,
我想看他们的Call Graph
源码如下:

然后按下面编译(instrument.c在上面github地址中可以下载,用于注入地址信息):
g++ -g -finstrument-functions -O0 instrument.c ABCTest.cpp -o test
然后运行程序,得到trace.txt
输入shell命令./test
最后
输入shell命令python CallGraph.py trace.txt test
弹出一张Call Graph

图上标注含义:
- 绿线表示程序启动后的第一次调用
- 红线表示进入当前上下文的最后一次调用
- 每一条线表示一次调用,
#符号后面的数字是序号,at XXX表示该次调用发生在这个文件(文件路径在框上方)的第几行 - 在圆圈里,
XXX:YYY,YYY是调用的函数名,XXX表示这个函数是在该文件的第几行被定义的
获取C/C++调用关系
利用-finstrument-functions编译选项,
可以让编译器在每个函数的开头和结尾注入__cyg_profile_func_enter和 __cyg_profile_func_exit
这两个函数的实现由用户定义
在本例中,只用到__cyg_profile_func_enter,定义在instrument.c中,
其函数原型如下:
void __cyg_profile_func_enter (void *this_fn, void *call_site);
其中this_fn为 被调用的地址,call_site为 调用方的地址
显然,假如我们把所有的 调用方和被调用方的地址 都打印出来,
就可以得到一张完整的运行时Call Graph
因此,我们的instrument.c实现如下:
/* Function prototypes with attributes */
void main_constructor( void )
__attribute__ ((no_instrument_function, constructor));
void main_destructor( void )
__attribute__ ((no_instrument_function, destructor));
void __cyg_profile_func_enter( void *, void * )
__attribute__ ((no_instrument_function));
void __cyg_profile_func_exit( void *, void * )
__attribute__ ((no_instrument_function));
static FILE *fp;
void main_constructor( void )
{
fp = fopen( "trace.txt", "w" );
if (fp == NULL) exit(-1);
}
void main_deconstructor( void )
{
fclose( fp );
}
void __cyg_profile_func_enter( void *this_fn, void *call_site )
{
/* fprintf(fp, "E %p %p\n", (int *)this_fn, (int *)call_site); */
fprintf(fp, "%p %p\n", (int *)this_fn, (int *)call_site);
}
其中main_constructor在 调用main 前执行,main_deconstructor在调用main后执行,
以上几个函数的作用就是 将所有的 调用方和被调用方的地址 写入trace.txt中
然而,现在有一个问题,就是trace.txt中保存的是地址,我们如何将地址翻译成源码中的符号?
答案就是用addr2line
以上面ABCTest.cpp工程为例,比如我们现在有地址0x400974,输入以下命令
addr2line 0x400aa4 -e a.out -f
结果为
_ZN1A4AOneEv
/home/cheukyin/PersonalProjects/CodeSnippet/python/SRCGraphviz/c++/A.hpp:11
第一行该地址所在的函数名,第二行为函数所在的源码位置
然而,你一定会问,_ZN1A4AOneEv是什么鬼?
为实现重载、命名空间等功能,因此C++有name mangling,因此函数名是不可读的
我们需要利用c++filt作进一步解析:
输入shell命令 addr2line 0x400aa4 -e a.out -f | c++filt
结果是不是就清晰很多:
A::AOne()
/home/cheukyin/PersonalProjects/CodeSnippet/python/SRCGraphviz/c++/A.hpp:11
注意这个结果中包含了函数名、函数所在文件和行号
调用图渲染
经过上面的步骤,我们已经可以把所有的(调用方, 被调用方)对分析出来了,相当于获取到调用图所有的节点和边,
最后可以用pygraphviz将 每一条调用关系 画出来即可,代码用python实现在 CallGraph.py 中
C++源码的调用图生成的更多相关文章
- Vue2.0源码思维导图-------------Vue 初始化
上一节看完<Vue源码思维导图-------------Vue 构造函数.原型.静态属性和方法>,这节将会以new Vue()为入口,大体看下 this._init()要做的事情. fun ...
- 十大基础排序算法[java源码+动静双图解析+性能分析]
一.概述 作为一个合格的程序员,算法是必备技能,特此总结十大基础排序算法.java版源码实现,强烈推荐<算法第四版>非常适合入手,所有算法网上可以找到源码下载. PS:本文讲解算法分三步: ...
- 图书管理系统(Java实现,十个数据表,含源码、ER图,超详细报告解释,2020.7.11更新)
图书管理系统数据库设计实验报告 文章目录 更新日志 1.概述 2.需求分析 2.1需要实现的功能 2.2业务流程图 2.2.1学生流程图 2.2.2管理员流程图 2.2.3超级管理员流程图 2.3功能 ...
- Spark Streaming源码解读之Receiver生成全生命周期彻底研究和思考
本期内容 : Receiver启动的方式设想 Receiver启动源码彻底分析 多个输入源输入启动,Receiver启动失败,只要我们的集群存在就希望Receiver启动成功,运行过程中基于每个Tea ...
- valgrind 内存检测与调用图生成
http://blog.csdn.net/destina/article/details/6198443 感谢作者的分享! 一 valgrind是什么? Valgrind是一套Linux下,开放源 ...
- 项目源码--Android美图秀秀源码
下载源码 技术要点: 1. 多种分类分享高清图片 2. 图片缓存技术 3. 图片缩图显示 4. 图片实时加载技术 5. 多点触控技术 6. HTTP网络数据搜索技术 7. 精美UI图片显示 ...
- 大白话Vue源码系列(03):生成AST
阅读目录 AST 节点定义 标签的正则匹配 解析用到的工具方法 解析开始标签 解析结束标签 解析文本 解析整块 HTML 模板 未提及的细节 本篇探讨 Vue 根据 html 模板片段构建出 AST ...
- 大白话Vue源码系列(03):生成render函数
阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...
- 大白话Vue源码系列(04):生成render函数
阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...
随机推荐
- Laravel Cache 使用
在项目中使用 laravel 的 cache 时,使用下面形式方法: $value = Cache::remember('users', $minutes, function() { return D ...
- Java加密解密字符串
http://www.cnblogs.com/vwpolo/archive/2012/07/18/2597232.html Java加密解密字符串 旧文重发:http://www.blogjava ...
- svn第一篇----入门指南
摘要:trunk存放的是主代码,不修改,branch,tag,milestone均是从trunk中衍生的.branch复制trunk中代码用于开发,tag用于存放比较重要的发行版,存放release版 ...
- 系统里有Courier New字体 Eclipse没有这个字体选项
问题状态: 系统里有Courier New字体 Eclipse没有这个字体选项问题原因: windows(xp)中的系统字体分为"显示"和"隐藏"两种状态,当为 ...
- js 截取字符串里的ip
var ip_reg = /([\d\.]*)/ig; ip = ip_reg.exec(str); return ip; ip_reg会截取 '(' 开始的字符串,中间包含数字和 '.' .
- hdu 2614
#include<stdio.h> int map[99][99]; int vist[99]; int sum=1; int maxsum=1; int max=0; int N; vo ...
- S3C2440触摸屏驱动详解
2440的触摸屏转换接口搭载在ADC接口之上,使用上比ADC接口多了一些花样,首先,触摸屏接口有几种转换模式 1. 普通转换模式 单转换模式是最合适的通用ADC转换.此模式可以通过设置ADCCON(A ...
- S3C2440时钟系统详解
在讲述系统时钟之前,因为这些设备都是挂靠在系统时钟上的,所以必须先说系统时钟,S3C2440的时钟系统如下 外部时钟源分两种,晶振或者外部频率,由om3-2选择,时钟电路根据两种选择也有两种 我们来分 ...
- Java Web EL JSTL的用法
1.导入包 fastjson-1.2.2.jar 2.JSP文件加入 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" p ...
- SQL Server数据库备份方法
数据库备份,是在数据丢失的情况下,能及时恢复重要数据,防止数据丢失的一种重要手段.一个合理的数据库备份方案,应该能够在数据丢失时,有效地恢复重要数据,同时需要考虑技术实现难度和有效地利用资源. 数据库 ...