前言

之前受知乎用户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 中

转载于:https://www.cnblogs.com/zhaohongtian/p/6801310.html

C++源码的调用图生成相关推荐

  1. 易语言从html中获取验证码,易语言过腾讯滑块验证码模块源码和调用例程

    易语言过腾讯滑块验证码模块源码和调用例程.版本 2 .支持库 BmpOperate .支持库 spec .程序集 程序集1 .子程序 _启动子程序, 整数型, , 请在本子程序中放置易模块初始化代码 ...

  2. 【小程序源码】llz制作生成装逼工具支持小程序和公众号制作生成

    这是一款制作生成小工具 支持小程序主图制作生成 也支持公众号的llz制作生成 每一种制作都包含了所有的模式 用户自己填写选择制作就可以了! 好了下面来看看小编的测试演示图吧! 小程序源码下载地址:[小 ...

  3. 判定两个tensor维度相同_Tensorflow源码解析5 -- 图的边 - Tensor

    1 概述 前文两篇文章分别讲解了TensorFlow核心对象Graph,和Graph的节点Operation.Graph另外一大成员,即为其边Tensor.边用来表示计算的数据,它经过上游节点计算后得 ...

  4. SharingSphere 源码解析 -- 真实SQL生成探索

    SharingSphere 源码解析 – 真实SQL生成探索 简介 在上一篇文章中,我们探索了ShardingSphere JDBC Mybatis示例执行的一个大致的过程,找到了SQL处理的关键节点 ...

  5. 艾默生充电15kw+台达三相PFC源程序 艾默生充电桩15kw模块+台达三相PFC源码,软件源码加原理 图BOM

    艾默生充电15kw+台达三相PFC源程序 艾默生充电桩15kw模块+台达三相PFC源码,软件源码加原理 图BOM 艾默生充电桩15kw模块原版软件源码含核心算法,PFC+DCDC双DSP数字控制,原理 ...

  6. 雕刻机6轴usb控制卡源码RTCP算法双源码含pcb图

    雕刻机6轴usb控制卡源码RTCP算法双源码含pcb图,,可真正使用 ID:1828639828742992你妹说的对

  7. 找茬小程序源码、看图找不同小程序,前端+后端+教程,微信小程序游戏一起来找茬,全网首发一款可以完美运营的找茬小程序源码

    演示小程序搜[最强脑洞I全民烧脑] 一共有2510关, 达到高级后会随机出现关卡: 一共7个能量, 每闯关一次扣除一个能量值, 看激励视频可以获得一个能量值: 金币获取: 段位升级 或者 观看激励视频 ...

  8. Flink 源码解析2--JobGraph的生成

    上一节介绍了StreamGraph的生成,这个实际上只对应 Flink 作业在逻辑上的执行计划图.Flink 会进一步对 StreamGraph 进行转换,得到另一个执行计划图,即JobGraph.然 ...

  9. Flink 源码解析1--StreamGraph的生成

    1. 先来简单看一下入门的WordCount程序 1.首先数据源会产生随机的数字数据流(0-10内的数字)形式,然后通过flink的transformation将数据进行单词计数,再print输出 / ...

最新文章

  1. 安装 Fedora 22 后要做的事情
  2. Spring核心AOP(面向切面编程)
  3. linux raid1 分区表,在 Linux 下使用 RAID(三):用两块磁盘创建 RAID 1(镜像)
  4. Alpha 冲刺11——总结
  5. 深入学习jQuery的三种常见动画效果
  6. php数组循环便利,浅析PHP中for与foreach两个循环结构遍历数组的区别
  7. SAP ABAP报表依赖设计原理详解
  8. 使用 C# 9.0 新语法提升 if 语句美感
  9. eclipse中导入maven项目时pom文件报错
  10. linux孟庆昌第六章课后题_第六章 参数估计-矩估计:通过课后题理解矩估计
  11. Tensorflow实现线性回归
  12. 第三十八篇 pandas模块
  13. 获取cookie_XSS获取COOKIE
  14. 5种方法,加密你的Python代码 !
  15. 两个音轨合并_技能!如何合并两个音频文件?
  16. VOIP技术发展综述与外呼系统的关系
  17. 微软发布Windows 10:连Windows 7都能免费升级了
  18. 使用ArcMap 生成TPK和geodatabase包
  19. 计算机专业的英文简历范文带翻译,计算机软件专业英文简历范文 英文简历范文带翻译...
  20. macbook上好用的软件

热门文章

  1. Spring Cache 缺陷,我好像有解决方案了
  2. 强软弱虚引用,只有体会过了,才能记住
  3. Spring 中的 bean 为什么默认单例?
  4. RocketMQ Consumer 负载均衡算法源码学习 -- AllocateMessageQueueConsistentHash
  5. Cookie或将被替换!Chrome工程师提议新型HTTP状态管理协议
  6. 当创建对象时......
  7. TensorFlow Wide And Deep 模型详解与应用
  8. 用 golang 1.11 module 做项目版本管理
  9. Android开发,Error: Failed to find Build Tools revision 24.0.2
  10. SpringBoot启动过程详解