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 中
转载于:https://www.cnblogs.com/zhaohongtian/p/6801310.html
C++源码的调用图生成相关推荐
- 易语言从html中获取验证码,易语言过腾讯滑块验证码模块源码和调用例程
易语言过腾讯滑块验证码模块源码和调用例程.版本 2 .支持库 BmpOperate .支持库 spec .程序集 程序集1 .子程序 _启动子程序, 整数型, , 请在本子程序中放置易模块初始化代码 ...
- 【小程序源码】llz制作生成装逼工具支持小程序和公众号制作生成
这是一款制作生成小工具 支持小程序主图制作生成 也支持公众号的llz制作生成 每一种制作都包含了所有的模式 用户自己填写选择制作就可以了! 好了下面来看看小编的测试演示图吧! 小程序源码下载地址:[小 ...
- 判定两个tensor维度相同_Tensorflow源码解析5 -- 图的边 - Tensor
1 概述 前文两篇文章分别讲解了TensorFlow核心对象Graph,和Graph的节点Operation.Graph另外一大成员,即为其边Tensor.边用来表示计算的数据,它经过上游节点计算后得 ...
- SharingSphere 源码解析 -- 真实SQL生成探索
SharingSphere 源码解析 – 真实SQL生成探索 简介 在上一篇文章中,我们探索了ShardingSphere JDBC Mybatis示例执行的一个大致的过程,找到了SQL处理的关键节点 ...
- 艾默生充电15kw+台达三相PFC源程序 艾默生充电桩15kw模块+台达三相PFC源码,软件源码加原理 图BOM
艾默生充电15kw+台达三相PFC源程序 艾默生充电桩15kw模块+台达三相PFC源码,软件源码加原理 图BOM 艾默生充电桩15kw模块原版软件源码含核心算法,PFC+DCDC双DSP数字控制,原理 ...
- 雕刻机6轴usb控制卡源码RTCP算法双源码含pcb图
雕刻机6轴usb控制卡源码RTCP算法双源码含pcb图,,可真正使用 ID:1828639828742992你妹说的对
- 找茬小程序源码、看图找不同小程序,前端+后端+教程,微信小程序游戏一起来找茬,全网首发一款可以完美运营的找茬小程序源码
演示小程序搜[最强脑洞I全民烧脑] 一共有2510关, 达到高级后会随机出现关卡: 一共7个能量, 每闯关一次扣除一个能量值, 看激励视频可以获得一个能量值: 金币获取: 段位升级 或者 观看激励视频 ...
- Flink 源码解析2--JobGraph的生成
上一节介绍了StreamGraph的生成,这个实际上只对应 Flink 作业在逻辑上的执行计划图.Flink 会进一步对 StreamGraph 进行转换,得到另一个执行计划图,即JobGraph.然 ...
- Flink 源码解析1--StreamGraph的生成
1. 先来简单看一下入门的WordCount程序 1.首先数据源会产生随机的数字数据流(0-10内的数字)形式,然后通过flink的transformation将数据进行单词计数,再print输出 / ...
最新文章
- 安装 Fedora 22 后要做的事情
- Spring核心AOP(面向切面编程)
- linux raid1 分区表,在 Linux 下使用 RAID(三):用两块磁盘创建 RAID 1(镜像)
- Alpha 冲刺11——总结
- 深入学习jQuery的三种常见动画效果
- php数组循环便利,浅析PHP中for与foreach两个循环结构遍历数组的区别
- SAP ABAP报表依赖设计原理详解
- 使用 C# 9.0 新语法提升 if 语句美感
- eclipse中导入maven项目时pom文件报错
- linux孟庆昌第六章课后题_第六章 参数估计-矩估计:通过课后题理解矩估计
- Tensorflow实现线性回归
- 第三十八篇 pandas模块
- 获取cookie_XSS获取COOKIE
- 5种方法,加密你的Python代码 !
- 两个音轨合并_技能!如何合并两个音频文件?
- VOIP技术发展综述与外呼系统的关系
- 微软发布Windows 10:连Windows 7都能免费升级了
- 使用ArcMap 生成TPK和geodatabase包
- 计算机专业的英文简历范文带翻译,计算机软件专业英文简历范文 英文简历范文带翻译...
- macbook上好用的软件
热门文章
- Spring Cache 缺陷,我好像有解决方案了
- 强软弱虚引用,只有体会过了,才能记住
- Spring 中的 bean 为什么默认单例?
- RocketMQ Consumer 负载均衡算法源码学习 -- AllocateMessageQueueConsistentHash
- Cookie或将被替换!Chrome工程师提议新型HTTP状态管理协议
- 当创建对象时......
- TensorFlow Wide And Deep 模型详解与应用
- 用 golang 1.11 module 做项目版本管理
- Android开发,Error: Failed to find Build Tools revision 24.0.2
- SpringBoot启动过程详解