使用vscode+clangd 进行内核代码阅读/开发

开发环境搭建

由于linux内核代码/头文件最好都放在linux环境下开发测试,在Mac下开发不太方便,记录一下使用vscode + remote ssh 进行远程开发

  1. vscode 安装插件 Remote - SSH 方便连接到linux机器,注意,linux机器不要用docker,自己搭一个虚拟机或者物理机器,方便双机调试
  2. vscode 会自动把 远程服务需要的文件传到linux机器下 所以请配置 ssh 的免密码登录,否则每次打开工程都需要输密码,很麻烦,保证机器至少有100m的空间用来存放vscode远程开发的插件/配置等文件
  3. linux 机器安装 clangd ubuntu下使用 sudo apt install clangd,最好也把clang也安装上,因为代码提示都是靠llvm 编译出的IR来进行AST分析,实现代码提示功能的
  4. linux 机器安装 bear ubuntu下使用 sudo apt install bear 这个是用来自动生成 compile_commands.json 这个文件来配置clangd
  5. vscode 配置 linux机器的clangd,给linux机器装上clangd 插件,配置如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    "clangd.arguments": [
    "--background-index",
    "--completion-style=detailed",
    "--header-insertion=never",
    "-log=info",
    "--pretty",
    "--clang-tidy",
    "--header-insertion=iwyu",
    "--compile-commands-dir=${workspaceFolder}"
    ]
  6. ${workspaceFolder} 也就是你使用vscode 打开文件夹的目录下新建 .clangd 文件配置详细工作内容,配置如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    CompileFlags: 
    Add:
    [
    "-I/usr/src/linux-headers-5.15.0-46/", # 配置linux源码目录
    "-I./include", # 配置包含目录
    "-DDEBUG", # 配置宏,方便调试模式切换
    "-Wunused-result", # 配置 clang 参数
    ]
    Remove: # 移除 clang 编译时的参数,因为clangd会使用compile_commands.json这个文件的编译参数,但是这个文件是gcc适用的,所以需要移除clang不适用的参数
    [
    "-mpreferred-stack-boundary*",
    "-mindirect-branch=thunk-extern",
    "-mindirect-branch-register",
    "-mfunction-return=thunk-extern",
    "-fno-allow-store-data-races",
    "-fconserve-stack",
    "-mrecord-mcount",
    ]
    Compiler: clang # 注意,内核使用的是gcc来编译代码的,但是此处配置的是使用clang来编译llvm IR进行语法提示,两者是不一样的

    Diagnostics:
    ClangTidy:
    CheckOptions:
    readability-identifier-naming.VariableCase: CamelCase
  7. 配置好clangd之后 还需要使用bear来自动生成内核编译时的参数,因为编译内核都是用Makefile来管理工程的,需要bear配合clangd才能进行自动完成等提示
  8. 在你的内核工程下执行bear -- make此时会生成一个compile_commands.json文件,里面记录了make命令执行过程中用到的所有参数

配置示例:

Makefile:

用来生成内核文件.ko

1
2
3
4
5
6
7
8
9
10
11
export KERNEL_VERSION = $(shell uname -r)
export KERNEL_DIR = /lib/modules/$(KERNEL_VERSION)/build
export KERNEL_TOOL =/usr/src/linux-headers-$(KERNEL_VERSION)/scripts/

obj-m := protect_openat.o # 和源码名称一致
ccflags-y :=-g -DDEBUG # gcc 参数,注意Makefile的参数和clangd是不一样的,clangd只是用来进行语法提示,实际上编译文件还得靠gcc

all:modules

modules:
@make -C $(KERNEL_DIR) M=$$PWD modules;

.clang-format:

用来格式化/美化代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
Language:        Cpp
BasedOnStyle: Google
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: true
AlignConsecutiveAssignments: true
AlignConsecutiveBitFields: true
AlignConsecutiveDeclarations: true
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Allman
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 0
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
- Regex: '.*'
Priority: 1
SortPriority: 0
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: Auto
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE

注意事项

  1. 执行bear -- make时,如果你的源码没有变动过,会生成出一个空的compile_commands.json 文件,此时clangd的智能提示会失效,所以在提示失效时,只需要改动一下源码,再重新执行一下bear -- make 即可
  2. clangd配置和Makefile是不一样的,clangd只用于vscode的智能代码提示,头文件的自动查找,Makefile才是用来生成内核ko文件的东西