一、StackView是什么?
在工作中我们常常需要阅读一些比较复杂的大型程序,通常我们使用静态阅读代码方式来整理程序执行过程,但这种方法对于分析一些指针函数或者匿名函数时让人非常的迷茫,使我们无法快速的定位到这些指针函数、匿名函数真正调用了什么函数。这种情况下很多人会选择一些辅助方法来帮助整理代码的执行流程,例如使用GDB动态调试的方式来定位一些不太好定位的函数调用。当使用GDB分析源码的时候,我们可以随时使用backtrace命令来回溯函数调用栈,这是一个非常灵活的功能。假如有一个工具能把GDB输出的函数调用栈随时的展示出来,再假如这个工具能够结合VIM的tag功能来实现代码跳转阅读,这样对代码的阅读来说就显得非常的友好了。本文所述的StackView就是为实现上述的功能而诞生。
项目地址: https://github.com/silent-dxx/stackview
二、如何安装StackView?
安装此插件前请检查你的环境是否满足下面所述的情况:
- 确保已经安装了VIM工具(本人只使用7.4和8.0以上的版本可以正常支持本插件)
- 确保已经安装了Python2.7,并在环境变量中可以执行python命令
通用安装方式:
cd ~/.vim/plugin
git clone https://github.com/silent-dxx/stackview.git
Vundle.vim 安装方式:
call vundle#begin()
Plugin 'silent-dxx/stackview'
call vundle#end()
vim-plug 安装方式:
call plug#begin()
Plug 'silent-dxx/stackview'
call plug#end()
三、如何使用StackView?
在使用StackView前,需要我们收集、制作插件所需要的配置文件。这个过程我们可以直接使用GDB或者eclipse+GDB的方式来获取函数调用栈,下面我们将详细的介绍使用eclipse+GDB的方法来使用StackView插件。
1. 获取函数调用栈
首先我们打开eclipse工具,在选择工具菜单中选择: 运行->调试配置选项,通过手动配置一个调试选项(具体方法不再描述,网络中有很多此类文章),随后直接点击调试按键开始调试程序。
当定位到我们所关心的源代码处时,在eclipse中切换到gdb traces窗口,切换方法如下(不同版本的eclipse可能使用方式有所不同):
此时在gdb traces
窗口中记录了函数调用栈
,我们仅仅将所需要的内容拷贝出来(注意是从^done
开始)。
2. 制作StackView配置文件(格式以*.bkpt结尾)
新建一个以bkpt结尾的文件,格式如下所示:
# 这里填写文件目录映射关系
# Windows系统的配置方式: D:/source_path/src (注意使用'/'而不是'')
file_mapping = {
'/orig_path1': '/conv_path1'
}
# 把从GDB获取到的函数调用栈复制到下面
# stack_list可以添加多组
stack_list.append('''
......
''')
以作者获取到的函数调用栈为例,制作的配置文件内容如下:
文件名: Xorg.bkpt
3. 使用StackView加载配置文件
打开VIM,在命令模式中输入:StackView ./Xorg.bkpt
在VIM左侧就会显示出函数调用栈,其中foucs里面的条目指的是所有函数调用栈最顶部的函数,单击foucs里面的函数会在stack_list展示出该函数完整的函数调用栈,单击stack_list里面的条目会在右侧显示对应的源代码,如果单击条目后不能正常显示源码,请检查该文件是否在系统的指定路径下或配置file_mapping来重新映射新的源码路径
下图所示,在配置文件中配置了多条函数调用栈的展示:
四、一些使用提示
1. 如何使用GDB来制作配置文件?
使用GDB制作配置文件的方式与上诉类似,不同的地方就是获取函数调用栈的方法有所不同。GDB需要使用MI接口的方式来获取函数调用栈。在启动GDB的时候,加入几个命令参数: gdb -q -i mi2 ./your_demo, 然后使用MI或者正常的GDB命令调试程序,当定位到关键函数处时使用-stack-list-frames命令来获取函数调用栈。
2. 关于配置文件
配置文件本身其实是一个Python脚本,它不仅仅可以配置file_mapping和stack_list,还很多其他操作,比如它可以指定不同操作系统下file_mapping的映射关系:
import platform
sys_name = platform.system()
if sys_name == 'Windows':
file_mapping = {
'/builddir/build/BUILD': 'D:/package_source/BUILD',
'/home/loongson/rpmbuild/BUILD': 'D:/package_source/BUILD'
}
elif sys_name == 'Linux':
file_mapping = {
'/builddir/build/BUILD': '/home/loongson/rpmbuild/BUILD'
}
stack_list.append('''
......
''')