使用 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文件来记录,这个文件中记录了编译中每个源代码的编译命令,通常由编译系统自动生成。

在拥有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

  1. 直接使用VScode打开common目录,这时候只能简单的查看文件,并没有代码提示等功能
  2. Ctrl+Shift+P并输入Open Workspece Settings(JSON) 并选择,VScode会在根目录下生成.vscode/settings.json
  3. 将下面代码复制到.vscode/settings.json,需要注意clangd.path的地址更换为读者本地路径
  4. 保存上面的settings.json,然后Ctrl+Shift+P并输入Reload Window,然后随便点击一个C文件,Clangd就会自动加载了,左下角会展示正在indexing,并在根目录下会生成.cache目录
  5. 索引完成后,代码提示和符号跳转等功能就可以正常使用了。

VSCode调试内核

VSCode代码调试基于上一篇文章-使用GDB调试Kernel,在这里简单列一下步骤,详细信息请参照上一篇文章

VScode调试插件安装

安装C/C++ Extension PackGDB 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可以帮助我们节省许多时间,提高我们的学习体验。

#Kernel #VScode #AOSP