使用vim+gtags阅读内核源码

使用vim+gtags阅读内核源码

gtags

gtags全称是GNU Global Source Code Tag System
官网简称为global
global这词太普遍,为了不产生歧义,我还是称呼它为gtags吧

gtags定位

简而言之,gtags是替换cscope的,不是代替ctags的

gtags vs cscope

上一篇,我分享过使用vim+cscope来阅读内核源码,cscope已经能很好地满足需求。
那么为什么我还要把cscope升级成gtags呢?
我罗列一下gtags的优劣之处,读者们自行判断。

gtags的优势

  1. 支持C++。这是它很大的一个优势,但是我们都知道内核是纯C写的,这优势用不上。
  2. 增量添加索引比较快。这在内核模块编程的场景下,优势比较大,因为每修改一个文件就要增量添加索引。但在阅读源码的场景下,是无需修改源文件的,所以这优势也用不上。
  3. gtags支持补全,即可以查询部分函数名。这也确实是个优势,但是嘛,cscope也是支持grep功能的,所以补全的功能并不常用。
  4. 内核源码有脚本可以自动生成gtags的索引db。这可是一个方便的东西,但是嘛,手工自己生成索引也没多复杂。

gtags的劣势

  1. 很多linux发行版未必自带gtags,要自己编译安装。似乎有点麻烦,但其实编译一下也很快。
  2. 不支持cscope的“Find functions called by this function”功能,但是这功能一般也不怎么用
  3. C++的支持其实已经deprecated,要使用universal ctags来作为插件完成这功能,好吧,就算用上universal ctags加持,对于C++的新标准,也不是支持得很好,还是得上lsp。

总结一下:
在阅读内核源码场景下,gtags的优势不是很优,劣势也不是太劣。它是比cscope是好一些,但也不多。如果用惯了cscope,又没有心痒痒,就不必上了。
如果你像我一样,就是想玩,愿意折腾,gtags也不会让你失望。

gtags的安装

编译安装

  1. 去下载源码。https://www.gnu.org/software/global/download.html
  2. sh reconf.sh
  3. make
  4. sudo make install

为内核源码产生索引

方法一:使用源码Makefile生成索引db
去到源码目录,执行:make gtags arch=x86

方法二:自行生成索引db

#!/bin/bash

CWD=`pwd`
TMPFILE=${CWD}/list.files

# gen the gtags db
find $CWD -name "*.[chxsS]" -o \( -path "${CWD}/Documentation*" -a -type f \) > $TMPFILE
gtags -f $TMPFILE
rm -f $TMPFILE

去到源码目录,执行一下以上脚本即可

方法三:使用gtags-cscope自动生成
去到源码目录,执行gtags-cscope自动生成

上面三种办法生成的索引其实是有细微差别的,日后用多了就能自行调整

使用gtags

使用gtags有四种办法

  • gtags-cscope -d
  • 在命令行使用global命令
  • globash
  • 在vim中使用

其实四种办法中,用到的gtags的功能都是一样的,只是交互方式不一样罢了,你懂了一种办法也就懂了其他办法。

gtags-cscope -d

对于入门者,或者cscope升级过来的人,推荐用这种方法,它有一个TUI界面,和cscope一模一样,操作直观。
在项目的根目录中,直接输入gtags-cscope -d就可以进入这个界面
gtags-cscope界面

操作办法也和cscope一模一样。
这个屏幕分成上下两个部分,上面的部分显示搜索结果,下面是菜单,可以进行各种搜索。使用tab键进行上下两个部分的切换。
在下面的菜单部分,使用ctrl+pctrl+n上下移动
使用ctrl+d退出界面

它默认vim来打开搜索结果所在的文件,如果要修改,可以设定全局变量EDITOR,比如

EDITOR=nvim gtags-cscope -d #使用neovim编辑器来打开搜索结果所在的文件

在命令行使用global命令

只要在当前目录中含有gtags索引文件,就可以使用global命令了

查找函数定义

这个功能就是gtags-cscope的Find this global definition

❯ global -x xfs_ilock
xfs_ilock         153 fs/xfs/xfs_inode.c xfs_ilock(
查找某个函数被调用的地方

这个功能就是gtags-cscope的Find references of this function

❯ global -rx xfs_ilock
xfs_ilock         270 fs/xfs/libxfs/xfs_attr.c 	xfs_ilock(dp, XFS_ILOCK_EXCL);
xfs_ilock         435 fs/xfs/libxfs/xfs_attr.c 	xfs_ilock(dp, XFS_ILOCK_EXCL);
xfs_ilock        1059 fs/xfs/libxfs/xfs_bmap.c 	xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_ilock        4955 fs/xfs/libxfs/xfs_bmap.c 		xfs_ilock(mp->m_rbmip, XFS_ILOCK_EXCL|XFS_ILOCK_RTBITMAP);
xfs_ilock        4957 fs/xfs/libxfs/xfs_bmap.c 		xfs_ilock(mp->m_rsumip, XFS_ILOCK_EXCL|XFS_ILOCK_RTSUM);
xfs_ilock        5484 fs/xfs/libxfs/xfs_bmap.c 	xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_ilock        5739 fs/xfs/libxfs/xfs_bmap.c 	xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_ilock         249 fs/xfs/xfs_aops.c 	xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_ilock         393 fs/xfs/xfs_aops.c 	xfs_ilock(ip, XFS_ILOCK_SHARED);
xfs_ilock         778 fs/xfs/xfs_aops.c 	xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_ilock        1297 fs/xfs/xfs_aops.c 		xfs_ilock(ip, lockmode);
xfs_ilock        1571 fs/xfs/xfs_aops.c 	xfs_ilock(ip, XFS_ILOCK_EXCL);
...
egrep某个函数,比如查找某个syscall

这个功能就是gtags-cscope的Find this egrep pattern

❯ global -gx "SYSCALL_DEF.*fstatat"
SYSCALL_DEF.*fstatat  272 fs/stat.c        SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
SYSCALL_DEF.*fstatat  416 fs/stat.c        SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,

不过,老实说,这功能和grep命令差别不大,而且速度也不见得快:

grep -Enr "SYSCALL_DEF.*fstatat"
fs/stat.c:272:SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
fs/stat.c:416:SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,

既然和grep差别不大,那么我用rg命令(ripgrep)就能查的更快

❯ rg "SYSCALL_DEF.*fstatat"
fs/stat.c
272:SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
416:SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
查找某个函数在特定路径下被调用的情况

这是内核阅读的时候经常用到的搜索
比如我只想看xfs调用submit_bio()的地方,那么就可以:

❯ global -rx -S fs/xfs submit_bio
submit_bio        540 fs/xfs/xfs_aops.c 	submit_bio(wbc->sync_mode == WB_SYNC_ALL ? WRITE_SYNC : WRITE,
submit_bio        600 fs/xfs/xfs_aops.c 	submit_bio(wbc->sync_mode == WB_SYNC_ALL ? WRITE_SYNC : WRITE,
submit_bio       1345 fs/xfs/xfs_buf.c 		submit_bio(rw, bio);
global命令的优点和缺点

global命令的优势在于:它是个命令。这意味着你可以使用其他任何的linux命令行工具来加工搜索结果,比如grep、sort、awk啥的,随心所欲。
缺点在于:它无法方便地打开某个搜索结果所在的文件。
如果你既要它的优势又要方便地打开搜索结果,那么就可以使用globash

globash

globash是另一种交互式使用gtags的办法
globash首先也是个shell,在它里面可以使用其他命令行工具
globash比bash特殊的地方在于它能记住搜索结果,输入搜索结果的编号,就可以打开该文件。

❯ globash  # 进入globash
Globash started. When you need help, please type 'ghelp <ENTER>'.
[/MyEBook/kernel/kernel-3.10.0-1160/linux-3.10.0-1160.el7]

在这里面drg三个命令分别对应搜索定义reference(被调用)grep三个功能,和前文所述gtags-cscope、global命令的功能一样。

[/MyEBook/kernel/kernel-3.10.0-1160/linux-3.10.0-1160.el7] g "xfs.*inobt" -S fs/xfs  # 在fs/xfs路径下grep "xfs.*inobt"
会列出这么多
>    1  xfs.*inobt        120 fs/xfs/libxfs/xfs_alloc.c         if (xfs_sb_version_hasfinobt(&mp->m_sb))
     2  xfs.*inobt         44 fs/xfs/libxfs/xfs_btree.h         xfs_inobt_key_t         inobt;
     3  xfs.*inobt         51 fs/xfs/libxfs/xfs_btree.h         xfs_inobt_rec_t         inobt;
     4  xfs.*inobt        189 fs/xfs/libxfs/xfs_btree.h                 xfs_inobt_rec_incore_t  i;
     5  xfs.*inobt        520 fs/xfs/libxfs/xfs_format.h static inline int xfs_sb_version_hasfinobt(xfs_sb_t *sbp)
     6  xfs.*inobt       1249 fs/xfs/libxfs/xfs_format.h static inline xfs_inofree_t xfs_inobt_maskn(int i, int n)
     7  xfs.*inobt       1264 fs/xfs/libxfs/xfs_format.h typedef struct xfs_inobt_rec {
     8  xfs.*inobt       1277 fs/xfs/libxfs/xfs_format.h } xfs_inobt_rec_t;
     9  xfs.*inobt       1279 fs/xfs/libxfs/xfs_format.h typedef struct xfs_inobt_rec_incore {
    10  xfs.*inobt       1285 fs/xfs/libxfs/xfs_format.h } xfs_inobt_rec_incore_t;
    11  xfs.*inobt       1287 fs/xfs/libxfs/xfs_format.h static inline bool xfs_inobt_issparse(uint16_t holemask)
    12  xfs.*inobt       1296 fs/xfs/libxfs/xfs_format.h typedef struct xfs_inobt_key {
    13  xfs.*inobt       1298 fs/xfs/libxfs/xfs_format.h } xfs_inobt_key_t;
    14  xfs.*inobt       1301 fs/xfs/libxfs/xfs_format.h typedef __be32 xfs_inobt_ptr_t;
    15  xfs.*inobt       1315 fs/xfs/libxfs/xfs_format.h        (xfs_sb_version_hasfinobt(&((mp)->m_sb)) ? \
...
如果你看到所要的结果,按q可以结束分页输出
按第一列的数字和回车就可以打开该文件,非常方便快捷。

如果要重复查找,只需输入l和回车,就可以又看到上次的输出了。
l其实是list命令,它会列出搜索结果缓存。

ctrl+d或者输出exit命令退出globash

在vim中使用

如果你想在vim中使用gtags,要先做两件事

  • 安装gtags插件
  • 在.vimrc中配置好该插件
安装gtags插件
# 在上面的源码包的根目录中有一个gtags.vim,这就是gtags的vim插件了
# 源码编译安装后,这个文件也可以在/usr/local/share/gtags/gtags.vim找到
# cp到vim的插件目录即可
cp /usr/local/share/gtags/gtags.vim $HOME/.vim/plugin
配置.vimrc

vim ~/.vimrc,增加下面几行,可以看我注释按自己喜好配置

set cscopeprg='gtags-cscope' " 使用 gtags-cscope 代替 cscope
" let Gtags_VerticalWindow = 1 " gtags的搜索结果会在quick fix窗口中显示,设置这个就会把quick fix窗口显示在右侧,我喜欢把它放在下面(默认值)
" let GtagsCscope_Auto_Load = 1 " using default key mapping,是否使用default的快捷键
let Gtags_Close_When_Single = 1 " 如果搜索结果只有一行,就直接跳转,不要打开quick fix窗口
map <C-\><C-]> :GtagsCursor<CR> " 以当前光标所在的单词为关键词,搜索它的定义
" 下面是gtags几个常用搜索的快捷键定义
nmap <C-\>d :Gtags -d <C-R>=expand("<cword>")<CR>
nmap <C-\>r :Gtags -r <C-R>=expand("<cword>")<CR>
nmap <C-\>g :Gtags -g <C-R>=expand("<cword>")<CR>
nmap <C-\>f :Gtags -f <C-R>=expand("<cword>")<CR>

在看懂上面的配置之后,就可以使用最后五行定义的快捷键在vim中来进行搜索了,几种搜索办法还是对应之前说的那几种。
注意我在配置快捷键的时候,最后四种快捷键是故意省略了一个快捷键,这是为了你在搜索开始之前有机会修正搜索的关键字。

使用

进到项目的根目录,打开vim它就会自动加载当前路径下的gtags索引文件。
除了上面定义的快捷键,当然可以直接输入Gtags命令来搜索
比如我输入了Gtags -g bio_alloc命令后,就会跳出这样的搜索结果窗口

vim中调用Gtags

如果你想重复搜索,可以输入命令:@:
如果想跳转到下一条结果的位置,输入命令:cn
如果想跳转到上一条结果的位置,输入命令:cp
如果想跳转到第8条结果的位置,输入命令:cc 8
如果要关闭搜索窗口,输入命令:ccl

写在最后

IT行业,技术更新换代快,人们都喜欢追求最新的技术,对老技术和产品多少有些轻视。
这一点在编辑器中也是这样,cscope、gtags这些以tag为基础的产品,由于不是真的懂得C代码,在现在的编程环境中,已经比不上lsp了。
但是,在C代码阅读的场景中,特别是内核源码阅读的场景中,这些技术还是有一席用武之地,在历史的长河中继续发光发热。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值