使用 VScode 阅读 Android Kernel 代码并调试
在上一篇文章中介绍了如何编译并调试内核,当读者想阅读代码或者修改代码时会发现,没有任何代码提示和跳转。当调试内核的时候又发现,gdb命令太多了十分不方便,让人不禁怀念有他———IDE的日子,那是多么惬意的时光。
前置知识
语言服务器协议(Language Server Protocol/LSP)
语言服务器协议(LSP)定义了编辑器或IDE与语言服务器之间使用的协议,语言服务器提供了自动完成、跳转定义、查找引用等语言功能。
为什么要提出语言服务器协议并增加语言服务器呢?
很久以前各IDE各自为营,例如Eclipse CDT(主要在Eclipse中提供C/C++支持)是由Java编写,如果一个使用TypeScript编写的IDE(例如:Visual Studio Code)想提供C/C++的支持就需要自己使用TypeScript重新写一套。
为了改善这样的局面,微软提出了LSP,将语言支持和编辑器之间增加一层抽象,编辑器/IDE只要实现LSP协议就拥有了复杂的语言支持功能,而对于语言服务器同样只需要关注实现LSP协议,而不限定使用何种语言。
关于更多语言服务器以及支持LSP的IDE列表,请查看微软LSP官网
“Any problem in computer science can be solved by another layer of indirection.”
Clangd
Clangd是一个C++的语言服务器,他为C++语言提供了代码完成、编译错误、转到定义等功能。
下面是一个带有Clangd插件的Vs code,演示了代码完成功能:
安装 clangd
如果你本地有NDK环境,在NDK目录下已经有了clangd(/toolchains/llvm/prebuilt/linux-x86_64/bin/clangd)可以直接使用,如果想要独立安装,请查看文档
VScode安装clangd插件
可以直接在VScode中选择View->Extensions打开插件管理,然后搜索"clangd“单击安装
官方推荐Make sure the Microsoft C/C++ extension is **not** installed
这里先忽略,在稍后配置文件中禁用。
安装后需要重载窗口,Ctrl+Shift+P并输入Reload Window
compile_commands.json
为了让clangd能够理解你的源代码,clangd需要知道你的构建标志,而这些信息通常由compile_commands.json文件来记录,这个文件中记录了编译中每个源代码的编译命令,通常由编译系统自动生成。
- CMAKE通过传递CMAKE_EXPORT_COMPILE_COMMANDS=1来生成compile_commands.json
- Bazel通过bazel-compile-commands-extractor来生成compile_commands.json
- 对于make编译的项目,可以通过Bear来生成compile_commands.json
在拥有compile_commands.json文件后,可以通过--compile-commands-dir
选项来指定数据库地址。
VScode搭建源码阅读环境
Android Kernel生成compile_commands.json
本章的Android Kernel源码环境搭建参照上一篇文章
经过前置知识的介绍,为了让clangd了解Android Kernel,需要生成一个compile_commands.json文件,上文中介绍了Android Kernel的构建系统为bazel,所以自然想到使用bazel-compile-commands-extractor来生成compile_commands.json文件,Android官方早就想到了开发者有这样的需求,所以早就预制了一个target kernel_compile_commands
并在build/kernel/kleaf/common_kernels.bzl中实现了该target。
在Android Kernel根目录直接运行tools/bazel run //common:kernel_x86_64_compile_commands
就会在根目录下生成compile_commands.json。
源码导入VScode
- 直接使用VScode打开common目录,这时候只能简单的查看文件,并没有代码提示等功能
- Ctrl+Shift+P并输入Open Workspece Settings(JSON) 并选择,VScode会在根目录下生成
.vscode/settings.json
- 将下面代码复制到
.vscode/settings.json
,需要注意clangd.path的地址更换为读者本地路径 - 保存上面的settings.json,然后Ctrl+Shift+P并输入Reload Window,然后随便点击一个C文件,Clangd就会自动加载了,左下角会展示正在indexing,并在根目录下会生成.
cache
目录 - 索引完成后,代码提示和符号跳转等功能就可以正常使用了。
VSCode调试内核
VSCode代码调试基于上一篇文章-使用GDB调试Kernel,在这里简单列一下步骤,详细信息请参照上一篇文章
VScode调试插件安装
安装C/C++ Extension Pack和GDB Debug两个插件
配置VSCode lunch.json调试配置文件
Ctrl+Shift+D调出调试面板,点击create a lunch.json file
选择GDB Debug
目标,这时会在.vscode下生成一个lunch.json文件,将下面的代码全部替换
需要注意的是,由于Cuttlefish底层是由crosvm支持的,对内核的调试支持也是由crosvm中的gdb支持的,这里需要使用硬件断点才能正常Debug,所以使用hardwareBreakpoints
将所有断点设置为硬件断点,否则将导致错误。
配置完成后,再次Reload Windows或者重启VScode
启动Cuttlefish
//切换到AOSP目录执行
source build/envsetup.sh
lunch aosp_cf_x86_64_phone-userdebug
launch_cvd -kernel_path /home/prosixe/ssd/Android/android-kernel/out/bzImage -initramfs_path /home/prosixe/ssd/Android/android-kernel/out/initramfs.img
VScode调试
在你关心的地方设置断点,然后在VScode调试面板点击Start Debugging(F5)
,之后代码就会停在断点处。
总结
配置调试环境的过程中并不是一帆风顺,在调试过程中就遇见VScode增加断点vm异常重启的问题,而gdb命令行就顺利运行,当搜索一大圈一无所获。只能阅读vscode-cpptools源码和GDB Remote Serial Protocol 文档,对比gdb命令行的协议数据。最后发现vscode的断点是break类型的,而crosvm需要hbread的断点才会正常,这和qemu虚拟机是有很大区别的,qemu无论是break还是hbreak都会正常suspend。
“工欲善其事,必先利其器”,功能丰富的IDE可以帮助我们节省许多时间,提高我们的学习体验。