1 .. SPDX-License-Identifier: GPL-2.0 2 3 .. include:: ../disclaimer-zh_CN.rst 4 5 :Original: Documentation/dev-tools/kcov.rst 6 :Translator: 刘浩阳 Haoyang Liu <tttturtleru 7 8 KCOV: 用于模糊测试的代码覆盖率 9 ============================== 10 11 KCOV 以一种适用于覆盖率引导的模 12 一个正在运行的内核的覆盖率数据 13 于任务启用的,因此 KCOV 可以精确 14 15 要注意的是 KCOV 不是为了收集尽可 16 ,这是系统调用输入的函数。为了 17 覆盖率收集被启用,见下文)以及 18 19 除了收集代码覆盖率,KCOV 还收集 20 查看详细信息。 21 22 除了从系统调用处理器收集覆盖率 23 被标注的部分收集覆盖率。见 "远 24 25 先决条件 26 -------- 27 28 KCOV 依赖编译器插桩,要求 GCC 6.1.0 29 30 收集操作数比较的覆盖率需要 GCC 8+ 31 32 为了启用 KCOV,需要使用如下参数 33 34 CONFIG_KCOV=y 35 36 为了启用操作数比较覆盖率的收集 37 38 CONFIG_KCOV_ENABLE_COMPARISONS=y 39 40 覆盖率数据只会在调试文件系统被 41 42 mount -t debugfs none /sys/kernel/debu 43 44 覆盖率收集 45 ---------- 46 47 下面的程序演示了如何使用 KCOV 在 48 49 .. code-block:: c 50 51 #include <stdio.h> 52 #include <stddef.h> 53 #include <stdint.h> 54 #include <stdlib.h> 55 #include <sys/types.h> 56 #include <sys/stat.h> 57 #include <sys/ioctl.h> 58 #include <sys/mman.h> 59 #include <unistd.h> 60 #include <fcntl.h> 61 #include <linux/types.h> 62 63 #define KCOV_INIT_TRACE 64 #define KCOV_ENABLE _IO('c 65 #define KCOV_DISABLE 66 #define COVER_SIZE (64<<1 67 68 #define KCOV_TRACE_PC 0 69 #define KCOV_TRACE_CMP 1 70 71 int main(int argc, char **argv) 72 { 73 int fd; 74 unsigned long *cover, n, i; 75 76 /* 单个文件描述符允许 77 * 在单线程上收集覆盖率。 78 */ 79 fd = open("/sys/kernel/debug/kcov", O_ 80 if (fd == -1) 81 perror("open"), exit(1); 82 /* 设置跟踪模式和跟踪大小 83 if (ioctl(fd, KCOV_INIT_TRACE, COVER_S 84 perror("ioctl"), exit(1); 85 /* 映射内核空间和用户空间 86 cover = (unsigned long*)mmap(NULL, COV 87 PROT_READ 88 if ((void*)cover == MAP_FAILED) 89 perror("mmap"), exit(1); 90 /* 在当前线程中启用覆盖率 91 if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_ 92 perror("ioctl"), exit(1); 93 /* 在调用 ioctl() 之后重置覆 94 __atomic_store_n(&cover[0], 0, __ATOMI 95 /* 调用目标系统调用。 */ 96 read(-1, NULL, 0); 97 /* 读取收集到的 PC 的数目。 98 n = __atomic_load_n(&cover[0], __ATOMI 99 for (i = 0; i < n; i++) 100 printf("0x%lx\n", cover[i + 1] 101 /* 在当前线程上禁用覆盖率 102 * 可以在其他线程上收集覆 103 */ 104 if (ioctl(fd, KCOV_DISABLE, 0)) 105 perror("ioctl"), exit(1); 106 /* 释放资源 */ 107 if (munmap(cover, COVER_SIZE * sizeof( 108 perror("munmap"), exit(1); 109 if (close(fd)) 110 perror("close"), exit(1); 111 return 0; 112 } 113 114 在使用 ``addr2line`` 传输后,程序输 115 116 SyS_read 117 fs/read_write.c:562 118 __fdget_pos 119 fs/file.c:774 120 __fget_light 121 fs/file.c:746 122 __fget_light 123 fs/file.c:750 124 __fget_light 125 fs/file.c:760 126 __fdget_pos 127 fs/file.c:784 128 SyS_read 129 fs/read_write.c:562 130 131 如果一个程序需要从多个线程收集 132 ``/sys/kernel/debug/kcov``。 133 134 接口的细粒度允许高效的创建测试 135 ``/sys/kernel/debug/kcov``,启用了追踪 136 环中创建了子进程。这个子进程只 137 用覆盖率收集)。 138 139 操作数比较收集 140 -------------- 141 142 操作数比较收集和覆盖率收集类似 143 144 .. code-block:: c 145 146 /* 包含和上文一样的头文件和宏 147 148 /* 每次记录的 64 位字的数量。 * 149 #define KCOV_WORDS_PER_CMP 4 150 151 /* 152 * 收集的比较种类的格式。 153 * 154 * 0 比特表示是否是一个编译时 155 * 1 & 2 比特包含参数大小的 log2 156 */ 157 158 #define KCOV_CMP_CONST (1 << 0) 159 #define KCOV_CMP_SIZE(n) ((n) << 1) 160 #define KCOV_CMP_MASK KCOV_CMP_S 161 162 int main(int argc, char **argv) 163 { 164 int fd; 165 uint64_t *cover, type, arg1, arg2, is_ 166 unsigned long n, i; 167 168 fd = open("/sys/kernel/debug/kcov", O_ 169 if (fd == -1) 170 perror("open"), exit(1); 171 if (ioctl(fd, KCOV_INIT_TRACE, COVER_S 172 perror("ioctl"), exit(1); 173 /* 174 * 注意缓冲区指针的类型是 ui 175 * 比较操作数都被提升为 uint6 176 */ 177 cover = (uint64_t *)mmap(NULL, COVER_S 178 PROT_READ 179 if ((void*)cover == MAP_FAILED) 180 perror("mmap"), exit(1); 181 /* 注意这里是 KCOV_TRACE_CMP 而 182 if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_ 183 perror("ioctl"), exit(1); 184 __atomic_store_n(&cover[0], 0, __ATOMI 185 read(-1, NULL, 0); 186 /* 读取收集到的比较操作数 187 n = __atomic_load_n(&cover[0], __ATOMI 188 for (i = 0; i < n; i++) { 189 uint64_t ip; 190 191 type = cover[i * KCOV_WORDS_PE 192 /* arg1 和 arg2 - 比较的 193 arg1 = cover[i * KCOV_WORDS_PE 194 arg2 = cover[i * KCOV_WORDS_PE 195 /* ip - 调用者的地址。 196 ip = cover[i * KCOV_WORDS_PER_ 197 /* 操作数的大小。 */ 198 size = 1 << ((type & KCOV_CMP_ 199 /* is_const - 当操作数是 200 is_const = type & KCOV_CMP_CON 201 printf("ip: 0x%lx type: 0x%lx, 202 "size: %lu, %s\n", 203 ip, type, arg1, arg2, 204 is_const ? "const" : "non-cons 205 } 206 if (ioctl(fd, KCOV_DISABLE, 0)) 207 perror("ioctl"), exit(1); 208 /* 释放资源。 */ 209 if (munmap(cover, COVER_SIZE * sizeof( 210 perror("munmap"), exit(1); 211 if (close(fd)) 212 perror("close"), exit(1); 213 return 0; 214 } 215 216 注意 KCOV 的模式(代码覆盖率收集 217 218 远程覆盖率收集 219 -------------- 220 221 除了从用户空间进程发布的系统调 222 他上下文中执行的内核中收集覆盖 223 224 使用 KCOV 收集远程覆盖率要求: 225 226 1. 修改内核源码并使用 ``kcov_remote_s 227 覆盖率的代码片段。 228 229 2. 在用户空间的收集覆盖率的进程 230 231 ``kcov_remote_start`` 和 ``kcov_remote_stop`` 232 ioctl 都接受可以识别特定覆盖率收 233 行的上下文。 234 235 KCOV 支持在如下上下文中收集远程 236 237 1. 全局内核后台任务。这些任务是 238 USB HCD 产生一个 USB ``hub_event`` 工 239 240 2. 局部内核后台任务。这些任务通 241 生的,并且通常在进程退出时会 242 243 3. 软中断。 244 245 对于 #1 和 #3,必须选择一个独特的 246 ``kcov_remote_start`` 调用。一个用户空 247 ``kcov_remote_arg`` 结构体的 ``handle`` 248 ``KCOV_REMOTE_ENABLE``。这会将使用的 KC 249 句柄标识的不同代码片段可以一次 250 251 对于 #2,用户空间进程必须通过 ``k 252 传递一个非零句柄。这个通用句柄 253 ``kcov_handle`` 字段中并且需要通过自 254 。这些任务需要在 ``kcov_remote_start`` 255 句柄。 256 257 KCOV 对全局句柄和通用句柄均遵循 258 。当前,只有最高位和低四位字节 259 260 对于全局句柄,最高位的字节表示 261 表示 USB 子系统类型。全局句柄的 262 个 ``hub_event`` 工作器使用 USB 总线 263 264 对于通用句柄,使用一个保留值 ``0 265 的子系统。通用句柄的低 4 字节用 266 该进程将通用句柄传递给 ``KCOV_REMOT 267 268 实际上,如果只从系统中的单个用 269 句柄的实例标识。然而,如果通用 270 的实例标识。一个选择是使用进程 271 272 下面的程序演示了如何使用 KCOV 从 273 任务 #1 收集覆盖率: 274 275 .. code-block:: c 276 277 /* 包含和上文一样的头文件和宏 278 279 struct kcov_remote_arg { 280 __u32 trace_mode; 281 __u32 area_size; 282 __u32 num_handles; 283 __aligned_u64 common_handle; 284 __aligned_u64 handles[0]; 285 }; 286 287 #define KCOV_INIT_TRACE 288 #define KCOV_DISABLE 289 #define KCOV_REMOTE_ENABLE _IOW(' 290 291 #define COVER_SIZE (64 << 10) 292 293 #define KCOV_TRACE_PC 0 294 295 #define KCOV_SUBSYSTEM_COMMON (0x00u 296 #define KCOV_SUBSYSTEM_USB (0x01ull << 56 297 298 #define KCOV_SUBSYSTEM_MASK (0xffull << 56 299 #define KCOV_INSTANCE_MASK (0xffffffffull 300 301 static inline __u64 kcov_remote_handle(__u 302 { 303 if (subsys & ~KCOV_SUBSYSTEM_MASK || i 304 return 0; 305 return subsys | inst; 306 } 307 308 #define KCOV_COMMON_ID 0x42 309 #define KCOV_USB_BUS_NUM 1 310 311 int main(int argc, char **argv) 312 { 313 int fd; 314 unsigned long *cover, n, i; 315 struct kcov_remote_arg *arg; 316 317 fd = open("/sys/kernel/debug/kcov", O_ 318 if (fd == -1) 319 perror("open"), exit(1); 320 if (ioctl(fd, KCOV_INIT_TRACE, COVER_S 321 perror("ioctl"), exit(1); 322 cover = (unsigned long*)mmap(NULL, COV 323 PROT_READ 324 if ((void*)cover == MAP_FAILED) 325 perror("mmap"), exit(1); 326 327 /* 通过通用句柄和 USB 总线 #1 328 arg = calloc(1, sizeof(*arg) + sizeof( 329 if (!arg) 330 perror("calloc"), exit(1); 331 arg->trace_mode = KCOV_TRACE_PC; 332 arg->area_size = COVER_SIZE; 333 arg->num_handles = 1; 334 arg->common_handle = kcov_remote_handl 335 336 arg->handles[0] = kcov_remote_handle(K 337 338 if (ioctl(fd, KCOV_REMOTE_ENABLE, arg) 339 perror("ioctl"), free(arg), ex 340 free(arg); 341 342 /* 343 * 在这里用户需要触发执行 344 * 该代码段要么使用通用句 345 * 要么触发了一些 USB 总线 #1 346 */ 347 sleep(2); 348 349 n = __atomic_load_n(&cover[0], __ATOMI 350 for (i = 0; i < n; i++) 351 printf("0x%lx\n", cover[i + 1] 352 if (ioctl(fd, KCOV_DISABLE, 0)) 353 perror("ioctl"), exit(1); 354 if (munmap(cover, COVER_SIZE * sizeof( 355 perror("munmap"), exit(1); 356 if (close(fd)) 357 perror("close"), exit(1); 358 return 0; 359 }
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.