前言

之前受知乎用户mailto1587启发,写了个C++源码的调用图生成器,可以以图示法显示C++函数的调用关系,

代码放在了github仓库里,仅供参考:

CodeSnippet/python/SRCGraphviz/c++ at master · Cheukyin/CodeSnippet · GitHub

主要思路

利用gcc/g++-finstrument-functions的注入选项,

得到每个函数的调用地址信息,生成一个trace文件

然后利用addr2linec++filt函数名及其所在源码位置从地址中解析出来,

从而得到程序的Call Stack

然后用pygraphviz画出来

使用示例

比如我现在有A.hppB.hppC.hppABCTest.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:YYYYYY是调用的函数名,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++源码的调用图生成的更多相关文章

  1. Vue2.0源码思维导图-------------Vue 初始化

    上一节看完<Vue源码思维导图-------------Vue 构造函数.原型.静态属性和方法>,这节将会以new Vue()为入口,大体看下 this._init()要做的事情. fun ...

  2. 十大基础排序算法[java源码+动静双图解析+性能分析]

    一.概述 作为一个合格的程序员,算法是必备技能,特此总结十大基础排序算法.java版源码实现,强烈推荐<算法第四版>非常适合入手,所有算法网上可以找到源码下载. PS:本文讲解算法分三步: ...

  3. 图书管理系统(Java实现,十个数据表,含源码、ER图,超详细报告解释,2020.7.11更新)

    图书管理系统数据库设计实验报告 文章目录 更新日志 1.概述 2.需求分析 2.1需要实现的功能 2.2业务流程图 2.2.1学生流程图 2.2.2管理员流程图 2.2.3超级管理员流程图 2.3功能 ...

  4. Spark Streaming源码解读之Receiver生成全生命周期彻底研究和思考

    本期内容 : Receiver启动的方式设想 Receiver启动源码彻底分析 多个输入源输入启动,Receiver启动失败,只要我们的集群存在就希望Receiver启动成功,运行过程中基于每个Tea ...

  5. valgrind 内存检测与调用图生成

    http://blog.csdn.net/destina/article/details/6198443  感谢作者的分享! 一  valgrind是什么? Valgrind是一套Linux下,开放源 ...

  6. 项目源码--Android美图秀秀源码

    下载源码   技术要点:   1. 多种分类分享高清图片 2. 图片缓存技术 3. 图片缩图显示 4. 图片实时加载技术 5. 多点触控技术 6.  HTTP网络数据搜索技术 7.  精美UI图片显示 ...

  7. 大白话Vue源码系列(03):生成AST

    阅读目录 AST 节点定义 标签的正则匹配 解析用到的工具方法 解析开始标签 解析结束标签 解析文本 解析整块 HTML 模板 未提及的细节 本篇探讨 Vue 根据 html 模板片段构建出 AST ...

  8. 大白话Vue源码系列(03):生成render函数

    阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...

  9. 大白话Vue源码系列(04):生成render函数

    阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...

随机推荐

  1. FZU 2087 统计树边

    这题第一直觉就是和CF第三次教育场的E题是一样的, http://codeforces.com/contest/609/problem/E 然后直接拉过来代码改了改,提交返回MLE.FZU内存开的小, ...

  2. UVA 10689 Yet another Number Sequence

    简单矩阵快速幂. if(m==1) MOD=10; if(m==2) MOD=100; if(m==3) MOD=1000; if(m==4) MOD=10000; 剩下的就是矩阵快速幂求斐波那契数列 ...

  3. Zabbix日志错误总结(持续更新)

    no active checks on server [*.*.*.*:10051]: host [*] not found failed to accept an incoming connecti ...

  4. MYSQL最大连接数修改

    MYSQL数据库安装完成后,默认最大连接数是100,一般流量稍微大一点的论坛或网站这个连接数是远远不够的,增加默认MYSQL连接数的方法有两个 方法一:进入MYSQL安装目录 打开MYSQL配置文件 ...

  5. 编译uboot提示libasm-offsets.c10 error bad value (armv5)解决方法

    编译uboot-2016.09提示如下错误: lib/asm-offsets.c:1:0: error: bad value (armv5) for -march= switch 解决方法: 1.在命 ...

  6. xml--笔记

    1.例子 <?xml version="1.0" encoding="utf-8"?> <!--引用css样式文件--> <?xm ...

  7. IOS开发-UI学习-根据URL显示图片,下载图片的练习(button,textfield,image view,url,data)

    编写一个如下界面,实现: 1.在文本输入框中输入一个网址,然后点击显示图片,图片显示到UIImageView中. 2.点击下载,这张显示的图片被下载到手机的Documents文件夹下的Dowmload ...

  8. android 菜单的总结

    安卓菜单有三种菜单. 选项菜单: 点击系统菜单按钮会触发 上下文菜单:长按屏幕触发 子菜单:某一个菜单的下一级菜单 具体的描叙:http://blog.csdn.net/zqiang_55/artic ...

  9. HDU 2830:Matrix Swapping II(思维)

    http://acm.hdu.edu.cn/showproblem.php?pid=2830 题意:-- 思路:对于每一列,它是固定的,用dp[][]处理出连续的长度.例如: 假设我们扫第四列的时候, ...

  10. 二 APPIUM Android自动化 环境搭建

    1.安装JAVA运行环境   2.安装Android开发环境   3.安装nodejs 下载地址:https://nodejs.org/en/ 下载完成之后双击安装.   4.安装APPIUM,App ...