首先,我目前见到的最常见的编辑器是vscode和neovim,本文所介绍的方法完美适配这两个编辑器~
阅读代码的痛点
- 难以正确跳转
读代码应该都遇到过一个问题就是,有时候很多的同名函数,在编译链接的过程你不知道具体链接到了哪个,虽然vscode和neovim的ctag都有全局的搜索,但是满屏的同名函数一个一个去找,实在是不优雅~
- 对复杂的结构嵌套没有全局视角
例如如果遇到复杂的驱动,以v4l2举例,一个struct不仅包含很多个struct,而且为了减少
->
的使用,还会将里面包含的struct的一些成员提到顶层,以及还会有指向父struct(暂且这么叫)的指针,如果第一次梳理源码,这种复杂的结构会给阅读代码带来了极大麻烦
- 不同版本之间代码变动,接口弃用,但不知道原因
在版本更新的过程中,经常遇见一些情况导致以前使用的接口在新版中弃用,那么为了更好的使用新版本,就需要对搞懂这些原因
ok,uu们如果你也苦于上面的情况,希望这篇文章能帮大家更好的阅读源码~
解决方法
优雅跳转
主要使用的是compile_commands.json
这个文件,这个是clangd的lsp使用到的,其中记录了一个文件编译过程中使用到的文件,如果有这个文件,那么完成正确的跳转,就不在话下了~
- 首先需要编译linux源码,不管是clangd,还是交叉编译链都可以。
2.1 linux kernel源码,如果用clangd构建指定好LLVM还是可以使用下面的指令得到 compile_commands.json
这个文件的。
./scripts/gen_compile_commands.py
2.2 但是对于 aarch64-linux-gnu-gcc
这个交叉编译链,会生成clangd无法识别的指令,所以需要在linux kernel源码目录下
touch .clangd
并且在 .clangd
中输入下面的内容
CompileFlags:
Remove: -mabi=lp64
- 再启动lsp,就可以发现可以直接跳转啦~(vscode是clangd的插件,nvim如果使用的是lazyvim,不需要额外配置)
ps:这里使用的clangd,可能在执行过程中缺少一些程序包,apt install就好
复杂的嵌套结构可视化
原理
内核每个源文件编译会生成 .xx.o.cmd 的隐藏文件,里面包含了依赖源文件和头文件列表
如imx219.c对应的 .imx219.o.cmd 里
3 source_drivers/media/i2c/imx219.o := ~/linux/vender/nvidia/drivers/media/i2c/imx219.c
4
5 deps_drivers/media/i2c/imx219.o := \
6 $(wildcard include/config/of.h) \
7 include/linux/slab.h \
8 $(wildcard include/config/debug/slab.h) \
9 $(wildcard include/config/debug/objects.h) \
主要就是使用doxygen来生成,并且可以配合一下graph软件,生成可视化的图
步骤
- 安装软件
$ sudo -H pip3 install filetype pygraphviz
$ sudo apt-get install build-essential python3-dev libssl-dev libffi-dev libxml2 libxml2-dev libxslt1-dev zlib1g-dev
$ sudo apt-get install python3-pygraphviz
$ sudo apt-get install doxygen-latex doxygen-doc doxygen-gui graphviz
$ doxywizard //doxygen启动
配置
- 内核源码中实际编译的文件列表
//kernel_src_dump.py
#!/usr/bin/env python
import sys
import os
import os.path
import time
import re
import argparse
KERNEL_BASE = os.getcwd()
KERNEL_SRC = os.getcwd()
KERNEL_OUT = os.getcwd()
def kernel_depfile_2_flist(depfile, filelist) :
fobj = open(depfile, "r")
for line in fobj :
txt = line.strip()
if 0 == len(txt) :
continue
if txt.startswith("cmd_") :
continue
if txt.startswith("deps_") :
continue
if txt.startswith("$(deps_") :
continue
if txt.endswith(".o)") :
continue
if txt.endswith(" \\") :
txt = txt[:-2]
if txt.startswith("$(wildcard ") and txt.endswith(")"):
idx_start = len("$(wildcard ")
tmp_txt = txt[idx_start : -1]
pre_path = KERNEL_OUT
relative_path = os.path.join(pre_path, tmp_txt)
abs_path = relative_path
if KERNEL_BASE in KERNEL_OUT:
pre_path = KERNEL_OUT[len(KERNEL_BASE) + 1::]
relative_path = os.path.join(pre_path, tmp_txt)
abs_path = os.path.join(KERNEL_BASE, relative_path)
if os.path.exists(abs_path) :
print(relative_path)
pass
else :
tmp_txt = re.sub(r".*:= *","", txt)
if len(tmp_txt) == 0 :
continue
if tmp_txt.find(KERNEL_BASE) >= 0 :
idx_start = len(KERNEL_BASE) + 1
full_txt = tmp_txt[idx_start:]
else :
if tmp_txt.endswith(".mod.c") :
continue
pre_path = KERNEL_OUT
relative_path = os.path.join(pre_path, tmp_txt)
if KERNEL_BASE in KERNEL_OUT:
pre_path = KERNEL_OUT[len(KERNEL_BASE) + 1::]
relative_path = os.path.join(pre_path, tmp_txt)
full_txt = relative_path
print(full_txt)
fobj.close()
def depfile_find(path, callback, args):
for root, dirs, files in os.walk(path): # path 为根目录
for fname in files :
rootname = str(root)
if rootname.startswith(os.path.join(KERNEL_OUT,"scripts")) :
continue
full_name = os.path.join(rootname, fname)
matchObj = re.search( r'.*\.o\.cmd', full_name, re.M|re.I)
if matchObj :
# print(full_name)
# print("callback type: %s" % str(type(callback)))
# if "<class 'function'>" == str(type(callback)) :
# py_ver = sys.version[:3]
if "function" in str(type(callback)) :
callback(full_name, args)
if __name__ == '__main__' :
path = KERNEL_OUT
depfile_find(path, kernel_depfile_2_flist, "undefined")
# dump all files in path: include/generated
path = os.path.join(KERNEL_OUT,"include", "generated")
for root, dirs, files in os.walk(path): # path 为根目录
rootname = str(root)
for fname in files :
full_txt = os.path.join(rootname, fname)
strip_path = full_txt
if KERNEL_BASE in full_txt:
strip_path = full_txt[len(KERNEL_BASE) + 1::]
print(strip_path)
$ python3 kernel_dump.py -s $PWD | uniq -u |sort |uniq> kernel.txt //递归查找子目录中 .o.cmd文件提取出依赖的源文件和头文件列表
//加sort 和uniq 是去除隔行重复的行
//运行时间要几分钟,可以进到drivers/media下测试 内容少比较快
- 按文件列表拷贝文件
$ python3 copy_file.py kernel.txt //运行,可成功看到生成out目录,里面文件有拷贝过来,
//copy_file.py
# coding:utf-8
import os
import sys
import shutil
src_kernel = '~/kernel-4.9/'
out_dir = 'out/'
abs_src_dir = '~/Linux_for_Tegra/source/public/kernel'
self_out_dir = 'out/self_nvidia/'
def copyFile(src):
if os.path.isabs(src):
dst = src.replace(abs_src_dir, self_out_dir)
else:
dst = os.path.join(out_dir, src)
src = os.path.join(src_kernel, src
dirs = os.path.dirname(dst)
#print("dst dir:"+dirs)
if not(os.path.exists(dirs) and os.path.isdir(dirs)):
try:
os.makedirs(dirs)
except OSError:
print("dirs already exists"+dirs)
try:
shutil.copyfile(src,dst)
print("copy ok:"+src)
except:
print("copy err:"+src)
if __name__ == '__main__':
if len(sys.argv) != 2: #//检测参数个数,不满足报错
print('params must be 1,the params is the file of contain the list of cutAndPastFile List')
exit(0)
file_pa1 = sys.argv[1] #//第二个参数,为包含文件列表的文件
f = open(file_pa1, 'r') #//打开文件
lst_file = f.readlines() #//读取文件里的一行(一行就是一个待拷贝的文件路径)
f.close()
for filename in lst_file: #//循环读取文件的每一行,进行拷贝
filename = filename.replace('\n', '') #//去除换行符的影响
if filename != '':
copyFile(filename) #//拷贝指定文件
- 用
doxywizard
启动doxygen构建 - windows下可视化
查看linux邮件
事情的起因是发现 gpio_request
被标记成弃用了,想找一下改这个patch和邮件,查看一下原因。
首先
# gpiolib-legacy.c是该函数存在的文件
git blame drivers/gpio/gpiolib-legacy.c
然后得到该change的commit
git show f402886
得到了该commit的标题,如红框所示。
最后在这个网址进行搜索,找到邮件 List archives on lore.kernel.org 搜索 lore.kernel -> listing of currently hosted archives -> 搜索标题)
全部评论
(2) 回帖