sphinx.addnodesdocument)}( rawsourcechildren]( translations LanguagesNode)}(hhh](h pending_xref)}(hhh]docutils.nodesTextEnglish}parenthsba attributes}(ids]classes]names]dupnames]backrefs] refdomainstdreftypedoc reftarget/dev-tools/kcovmodnameN classnameN refexplicitutagnamehhh ubh)}(hhh]hChinese (Traditional)}hh2sbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget"/translations/zh_TW/dev-tools/kcovmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hItalian}hhFsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget"/translations/it_IT/dev-tools/kcovmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hJapanese}hhZsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget"/translations/ja_JP/dev-tools/kcovmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hKorean}hhnsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget"/translations/ko_KR/dev-tools/kcovmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hSpanish}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget"/translations/sp_SP/dev-tools/kcovmodnameN classnameN refexplicituh1hhh ubeh}(h]h ]h"]h$]h&]current_languageChinese (Simplified)uh1h hh _documenthsourceNlineNubhcomment)}(h SPDX-License-Identifier: GPL-2.0h]h SPDX-License-Identifier: GPL-2.0}hhsbah}(h]h ]h"]h$]h&] xml:spacepreserveuh1hhhhhhO/var/lib/git/docbuild/linux/Documentation/translations/zh_CN/dev-tools/kcov.rsthKubhnote)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]h paragraph)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hh5Documentation/translations/zh_CN/disclaimer-zh_CN.rsthKhhubah}(h]h ]h"]h$]h&]uh1hhhhhhhhNubh field_list)}(hhh](hfield)}(hhh](h field_name)}(hOriginalh]hOriginal}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhhhKubh field_body)}(h Documentation/dev-tools/kcov.rsth]h)}(hhh]h Documentation/dev-tools/kcov.rst}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhhubah}(h]h ]h"]h$]h&]uh1hhhubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hhh](h)}(h Translatorh]h Translator}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj hhhKubh)}(h2刘浩阳 Haoyang Liu h]h)}(h1刘浩阳 Haoyang Liu h](h刘浩阳 Haoyang Liu <}(hjhhhNhNubh reference)}(htttturtleruss@hust.edu.cnh]htttturtleruss@hust.edu.cn}(hj)hhhNhNubah}(h]h ]h"]h$]h&]refuri mailto:tttturtleruss@hust.edu.cnuh1j'hjubh>}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1hhj ubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubeh}(h]h ]h"]h$]h&]uh1hhhhhhhhKubhsection)}(hhh](htitle)}(h*KCOV: 用于模糊测试的代码覆盖率h]h*KCOV: 用于模糊测试的代码覆盖率}(hj\hhhNhNubah}(h]h ]h"]h$]h&]uh1jZhjWhhhhhK ubh)}(hX1KCOV 以一种适用于覆盖率引导的模糊测试的形式收集和暴露内核代码覆盖率信息。 一个正在运行的内核的覆盖率数据可以通过 ``kcov`` 调试文件导出。覆盖率的收集是基 于任务启用的,因此 KCOV 可以精确捕获单个系统调用的覆盖率。h](hKCOV 以一种适用于覆盖率引导的模糊测试的形式收集和暴露内核代码覆盖率信息。 一个正在运行的内核的覆盖率数据可以通过 }(hjjhhhNhNubhliteral)}(h``kcov``h]hkcov}(hjthhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjjubh 调试文件导出。覆盖率的收集是基 于任务启用的,因此 KCOV 可以精确捕获单个系统调用的覆盖率。}(hjjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK hjWhhubh)}(hX^要注意的是 KCOV 不是为了收集尽可能多的覆盖率数据。而是为了收集相对稳定的覆盖率 ,这是系统调用输入的函数。为了完成这个目标,它不收集软硬中断的覆盖率(除非移除 覆盖率收集被启用,见下文)以及内核中固有的不确定部分的覆盖率(如调度器,锁定)h]hX^要注意的是 KCOV 不是为了收集尽可能多的覆盖率数据。而是为了收集相对稳定的覆盖率 ,这是系统调用输入的函数。为了完成这个目标,它不收集软硬中断的覆盖率(除非移除 覆盖率收集被启用,见下文)以及内核中固有的不确定部分的覆盖率(如调度器,锁定)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjWhhubh)}(h除了收集代码覆盖率,KCOV 还收集操作数比较的覆盖率。见 "操作数比较收集" 一节 查看详细信息。h]h除了收集代码覆盖率,KCOV 还收集操作数比较的覆盖率。见 “操作数比较收集” 一节 查看详细信息。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjWhhubh)}(h除了从系统调用处理器收集覆盖率数据,KCOV 还从后台内核或软中断任务中执行的内核 被标注的部分收集覆盖率。见 "远程覆盖率收集" 一节查看详细信息。h]h除了从系统调用处理器收集覆盖率数据,KCOV 还从后台内核或软中断任务中执行的内核 被标注的部分收集覆盖率。见 “远程覆盖率收集” 一节查看详细信息。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjWhhubjV)}(hhh](j[)}(h 先决条件h]h 先决条件}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jZhjhhhhhKubh)}(hjKCOV 依赖编译器插桩,要求 GCC 6.1.0 及更高版本或者内核支持的任意版本的 Clang。h]hjKCOV 依赖编译器插桩,要求 GCC 6.1.0 及更高版本或者内核支持的任意版本的 Clang。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h>收集操作数比较的覆盖率需要 GCC 8+ 或者 Clang。h]h>收集操作数比较的覆盖率需要 GCC 8+ 或者 Clang。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h:为了启用 KCOV,需要使用如下参数配置内核::h]h9为了启用 KCOV,需要使用如下参数配置内核:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK hjhhubh literal_block)}(h CONFIG_KCOV=yh]h CONFIG_KCOV=y}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhK"hjhhubh)}(hD为了启用操作数比较覆盖率的收集,使用如下参数::h]hC为了启用操作数比较覆盖率的收集,使用如下参数:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK$hjhhubj)}(h CONFIG_KCOV_ENABLE_COMPARISONS=yh]h CONFIG_KCOV_ENABLE_COMPARISONS=y}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhK&hjhhubh)}(hG覆盖率数据只会在调试文件系统被挂载后才可以获取::h]hF覆盖率数据只会在调试文件系统被挂载后才可以获取:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK(hjhhubj)}(h'mount -t debugfs none /sys/kernel/debugh]h'mount -t debugfs none /sys/kernel/debug}hj+sbah}(h]h ]h"]h$]h&]hhuh1jhhhK*hjhhubeh}(h]id1ah ]h"] 先决条件ah$]h&]uh1jUhjWhhhhhKubjV)}(hhh](j[)}(h覆盖率收集h]h覆盖率收集}(hjDhhhNhNubah}(h]h ]h"]h$]h&]uh1jZhjAhhhhhK-ubh)}(hi下面的程序演示了如何使用 KCOV 在一个测试程序中收集单个系统调用的覆盖率:h]hi下面的程序演示了如何使用 KCOV 在一个测试程序中收集单个系统调用的覆盖率:}(hjRhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK/hjAhhubj)}(hX#include #include #include #include #include #include #include #include #include #include #include #define KCOV_INIT_TRACE _IOR('c', 1, unsigned long) #define KCOV_ENABLE _IO('c', 100) #define KCOV_DISABLE _IO('c', 101) #define COVER_SIZE (64<<10) #define KCOV_TRACE_PC 0 #define KCOV_TRACE_CMP 1 int main(int argc, char **argv) { int fd; unsigned long *cover, n, i; /* 单个文件描述符允许 * 在单线程上收集覆盖率。 */ fd = open("/sys/kernel/debug/kcov", O_RDWR); if (fd == -1) perror("open"), exit(1); /* 设置跟踪模式和跟踪大小。 */ if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE)) perror("ioctl"), exit(1); /* 映射内核空间和用户空间共享的缓冲区。 */ cover = (unsigned long*)mmap(NULL, COVER_SIZE * sizeof(unsigned long), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if ((void*)cover == MAP_FAILED) perror("mmap"), exit(1); /* 在当前线程中启用覆盖率收集。 */ if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_PC)) perror("ioctl"), exit(1); /* 在调用 ioctl() 之后重置覆盖率。 */ __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED); /* 调用目标系统调用。 */ read(-1, NULL, 0); /* 读取收集到的 PC 的数目。 */ n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED); for (i = 0; i < n; i++) printf("0x%lx\n", cover[i + 1]); /* 在当前线程上禁用覆盖率收集。在这之后 * 可以在其他线程上收集覆盖率 */ if (ioctl(fd, KCOV_DISABLE, 0)) perror("ioctl"), exit(1); /* 释放资源 */ if (munmap(cover, COVER_SIZE * sizeof(unsigned long))) perror("munmap"), exit(1); if (close(fd)) perror("close"), exit(1); return 0; }h]hX#include #include #include #include #include #include #include #include #include #include #include #define KCOV_INIT_TRACE _IOR('c', 1, unsigned long) #define KCOV_ENABLE _IO('c', 100) #define KCOV_DISABLE _IO('c', 101) #define COVER_SIZE (64<<10) #define KCOV_TRACE_PC 0 #define KCOV_TRACE_CMP 1 int main(int argc, char **argv) { int fd; unsigned long *cover, n, i; /* 单个文件描述符允许 * 在单线程上收集覆盖率。 */ fd = open("/sys/kernel/debug/kcov", O_RDWR); if (fd == -1) perror("open"), exit(1); /* 设置跟踪模式和跟踪大小。 */ if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE)) perror("ioctl"), exit(1); /* 映射内核空间和用户空间共享的缓冲区。 */ cover = (unsigned long*)mmap(NULL, COVER_SIZE * sizeof(unsigned long), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if ((void*)cover == MAP_FAILED) perror("mmap"), exit(1); /* 在当前线程中启用覆盖率收集。 */ if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_PC)) perror("ioctl"), exit(1); /* 在调用 ioctl() 之后重置覆盖率。 */ __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED); /* 调用目标系统调用。 */ read(-1, NULL, 0); /* 读取收集到的 PC 的数目。 */ n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED); for (i = 0; i < n; i++) printf("0x%lx\n", cover[i + 1]); /* 在当前线程上禁用覆盖率收集。在这之后 * 可以在其他线程上收集覆盖率 */ if (ioctl(fd, KCOV_DISABLE, 0)) perror("ioctl"), exit(1); /* 释放资源 */ if (munmap(cover, COVER_SIZE * sizeof(unsigned long))) perror("munmap"), exit(1); if (close(fd)) perror("close"), exit(1); return 0; }}hj`sbah}(h]h ]h"]h$]h&]hhforcelanguagechighlight_args}uh1jhhhK1hjAhhubh)}(hD在使用 ``addr2line`` 传输后,程序输出应该如下所示::h](h 在使用 }(hjshhhNhNubjs)}(h ``addr2line``h]h addr2line}(hj{hhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjsubh, 传输后,程序输出应该如下所示:}(hjshhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKrhjAhhubj)}(hSyS_read fs/read_write.c:562 __fdget_pos fs/file.c:774 __fget_light fs/file.c:746 __fget_light fs/file.c:750 __fget_light fs/file.c:760 __fdget_pos fs/file.c:784 SyS_read fs/read_write.c:562h]hSyS_read fs/read_write.c:562 __fdget_pos fs/file.c:774 __fget_light fs/file.c:746 __fget_light fs/file.c:750 __fget_light fs/file.c:760 __fdget_pos fs/file.c:784 SyS_read fs/read_write.c:562}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKthjAhhubh)}(h如果一个程序需要从多个线程收集覆盖率(独立地)。那么每个线程都需要单独打开 ``/sys/kernel/debug/kcov``。h](hp如果一个程序需要从多个线程收集覆盖率(独立地)。那么每个线程都需要单独打开 }(hjhhhNhNubjs)}(h``/sys/kernel/debug/kcov``h]h/sys/kernel/debug/kcov}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjAhhubh)}(hXI接口的细粒度允许高效的创建测试进程。即,一个父进程打开了 ``/sys/kernel/debug/kcov``,启用了追踪模式,映射了覆盖率缓冲区,然后在一个循 环中创建了子进程。这个子进程只需要启用覆盖率收集即可(当一个线程退出时将自动禁 用覆盖率收集)。h](hU接口的细粒度允许高效的创建测试进程。即,一个父进程打开了 }(hjhhhNhNubjs)}(h``/sys/kernel/debug/kcov``h]h/sys/kernel/debug/kcov}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh,启用了追踪模式,映射了覆盖率缓冲区,然后在一个循 环中创建了子进程。这个子进程只需要启用覆盖率收集即可(当一个线程退出时将自动禁 用覆盖率收集)。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjAhhubeh}(h]id2ah ]h"]覆盖率收集ah$]h&]uh1jUhjWhhhhhK-ubjV)}(hhh](j[)}(h操作数比较收集h]h操作数比较收集}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jZhjhhhhhKubh)}(h0操作数比较收集和覆盖率收集类似:h]h0操作数比较收集和覆盖率收集类似:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hX /* 包含和上文一样的头文件和宏定义。 */ /* 每次记录的 64 位字的数量。 */ #define KCOV_WORDS_PER_CMP 4 /* * 收集的比较种类的格式。 * * 0 比特表示是否是一个编译时常量。 * 1 & 2 比特包含参数大小的 log2 值,最大 8 字节。 */ #define KCOV_CMP_CONST (1 << 0) #define KCOV_CMP_SIZE(n) ((n) << 1) #define KCOV_CMP_MASK KCOV_CMP_SIZE(3) int main(int argc, char **argv) { int fd; uint64_t *cover, type, arg1, arg2, is_const, size; unsigned long n, i; fd = open("/sys/kernel/debug/kcov", O_RDWR); if (fd == -1) perror("open"), exit(1); if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE)) perror("ioctl"), exit(1); /* * 注意缓冲区指针的类型是 uint64_t*,因为所有的 * 比较操作数都被提升为 uint64_t 类型。 */ cover = (uint64_t *)mmap(NULL, COVER_SIZE * sizeof(unsigned long), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if ((void*)cover == MAP_FAILED) perror("mmap"), exit(1); /* 注意这里是 KCOV_TRACE_CMP 而不是 KCOV_TRACE_PC。 */ if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_CMP)) perror("ioctl"), exit(1); __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED); read(-1, NULL, 0); /* 读取收集到的比较操作数的数量。 */ n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED); for (i = 0; i < n; i++) { uint64_t ip; type = cover[i * KCOV_WORDS_PER_CMP + 1]; /* arg1 和 arg2 - 比较的两个操作数。 */ arg1 = cover[i * KCOV_WORDS_PER_CMP + 2]; arg2 = cover[i * KCOV_WORDS_PER_CMP + 3]; /* ip - 调用者的地址。 */ ip = cover[i * KCOV_WORDS_PER_CMP + 4]; /* 操作数的大小。 */ size = 1 << ((type & KCOV_CMP_MASK) >> 1); /* is_const - 当操作数是一个编译时常量时为真。*/ is_const = type & KCOV_CMP_CONST; printf("ip: 0x%lx type: 0x%lx, arg1: 0x%lx, arg2: 0x%lx, " "size: %lu, %s\n", ip, type, arg1, arg2, size, is_const ? "const" : "non-const"); } if (ioctl(fd, KCOV_DISABLE, 0)) perror("ioctl"), exit(1); /* 释放资源。 */ if (munmap(cover, COVER_SIZE * sizeof(unsigned long))) perror("munmap"), exit(1); if (close(fd)) perror("close"), exit(1); return 0; }h]hX /* 包含和上文一样的头文件和宏定义。 */ /* 每次记录的 64 位字的数量。 */ #define KCOV_WORDS_PER_CMP 4 /* * 收集的比较种类的格式。 * * 0 比特表示是否是一个编译时常量。 * 1 & 2 比特包含参数大小的 log2 值,最大 8 字节。 */ #define KCOV_CMP_CONST (1 << 0) #define KCOV_CMP_SIZE(n) ((n) << 1) #define KCOV_CMP_MASK KCOV_CMP_SIZE(3) int main(int argc, char **argv) { int fd; uint64_t *cover, type, arg1, arg2, is_const, size; unsigned long n, i; fd = open("/sys/kernel/debug/kcov", O_RDWR); if (fd == -1) perror("open"), exit(1); if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE)) perror("ioctl"), exit(1); /* * 注意缓冲区指针的类型是 uint64_t*,因为所有的 * 比较操作数都被提升为 uint64_t 类型。 */ cover = (uint64_t *)mmap(NULL, COVER_SIZE * sizeof(unsigned long), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if ((void*)cover == MAP_FAILED) perror("mmap"), exit(1); /* 注意这里是 KCOV_TRACE_CMP 而不是 KCOV_TRACE_PC。 */ if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_CMP)) perror("ioctl"), exit(1); __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED); read(-1, NULL, 0); /* 读取收集到的比较操作数的数量。 */ n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED); for (i = 0; i < n; i++) { uint64_t ip; type = cover[i * KCOV_WORDS_PER_CMP + 1]; /* arg1 和 arg2 - 比较的两个操作数。 */ arg1 = cover[i * KCOV_WORDS_PER_CMP + 2]; arg2 = cover[i * KCOV_WORDS_PER_CMP + 3]; /* ip - 调用者的地址。 */ ip = cover[i * KCOV_WORDS_PER_CMP + 4]; /* 操作数的大小。 */ size = 1 << ((type & KCOV_CMP_MASK) >> 1); /* is_const - 当操作数是一个编译时常量时为真。*/ is_const = type & KCOV_CMP_CONST; printf("ip: 0x%lx type: 0x%lx, arg1: 0x%lx, arg2: 0x%lx, " "size: %lu, %s\n", ip, type, arg1, arg2, size, is_const ? "const" : "non-const"); } if (ioctl(fd, KCOV_DISABLE, 0)) perror("ioctl"), exit(1); /* 释放资源。 */ if (munmap(cover, COVER_SIZE * sizeof(unsigned long))) perror("munmap"), exit(1); if (close(fd)) perror("close"), exit(1); return 0; }}hjsbah}(h]h ]h"]h$]h&]hhjnjojpjq}uh1jhhhKhjhhubh)}(hW注意 KCOV 的模式(代码覆盖率收集或操作数比较收集)是互斥的。h]hW注意 KCOV 的模式(代码覆盖率收集或操作数比较收集)是互斥的。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]id3ah ]h"]操作数比较收集ah$]h&]uh1jUhjWhhhhhKubjV)}(hhh](j[)}(h远程覆盖率收集h]h远程覆盖率收集}(hj0hhhNhNubah}(h]h ]h"]h$]h&]uh1jZhj-hhhhhKubh)}(h除了从用户空间进程发布的系统调用句柄收集覆盖率数据以外,KCOV 也可以从部分在其 他上下文中执行的内核中收集覆盖率 - 称为“远程”覆盖率。h]h除了从用户空间进程发布的系统调用句柄收集覆盖率数据以外,KCOV 也可以从部分在其 他上下文中执行的内核中收集覆盖率 - 称为“远程”覆盖率。}(hj>hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj-hhubh)}(h*使用 KCOV 收集远程覆盖率要求:h]h*使用 KCOV 收集远程覆盖率要求:}(hjLhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj-hhubhenumerated_list)}(hhh](h list_item)}(hz修改内核源码并使用 ``kcov_remote_start`` 和 ``kcov_remote_stop`` 来标注要收集 覆盖率的代码片段。 h]h)}(hy修改内核源码并使用 ``kcov_remote_start`` 和 ``kcov_remote_stop`` 来标注要收集 覆盖率的代码片段。h](h修改内核源码并使用 }(hjehhhNhNubjs)}(h``kcov_remote_start``h]hkcov_remote_start}(hjmhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjeubh 和 }(hjehhhNhNubjs)}(h``kcov_remote_stop``h]hkcov_remote_stop}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjeubh/ 来标注要收集 覆盖率的代码片段。}(hjehhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjaubah}(h]h ]h"]h$]h&]uh1j_hj\hhhhhNubj`)}(hh在用户空间的收集覆盖率的进程应使用 ``KCOV_REMOTE_ENABLE`` 而不是 ``KCOV_ENABLE``。 h]h)}(hg在用户空间的收集覆盖率的进程应使用 ``KCOV_REMOTE_ENABLE`` 而不是 ``KCOV_ENABLE``。h](h4在用户空间的收集覆盖率的进程应使用 }(hjhhhNhNubjs)}(h``KCOV_REMOTE_ENABLE``h]hKCOV_REMOTE_ENABLE}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh 而不是 }(hjhhhNhNubjs)}(h``KCOV_ENABLE``h]h KCOV_ENABLE}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1j_hj\hhhhhNubeh}(h]h ]h"]h$]h&]enumtypearabicprefixhsuffix.uh1jZhj-hhhhhKubh)}(h``kcov_remote_start`` 和 ``kcov_remote_stop`` 的标注以及 ``KCOV_REMOTE_ENABLE`` ioctl 都接受可以识别特定覆盖率收集片段的句柄。句柄的使用方式取决于匹配代码片段执 行的上下文。h](js)}(h``kcov_remote_start``h]hkcov_remote_start}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh 和 }(hjhhhNhNubjs)}(h``kcov_remote_stop``h]hkcov_remote_stop}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh 的标注以及 }(hjhhhNhNubjs)}(h``KCOV_REMOTE_ENABLE``h]hKCOV_REMOTE_ENABLE}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh ioctl 都接受可以识别特定覆盖率收集片段的句柄。句柄的使用方式取决于匹配代码片段执 行的上下文。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj-hhubh)}(h8KCOV 支持在如下上下文中收集远程覆盖率:h]h8KCOV 支持在如下上下文中收集远程覆盖率:}(hj$hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj-hhubj[)}(hhh](j`)}(h全局内核后台任务。这些任务是内核启动时创建的数量有限的实例(如,每一个 USB HCD 产生一个 USB ``hub_event`` 工作器)。 h]h)}(h全局内核后台任务。这些任务是内核启动时创建的数量有限的实例(如,每一个 USB HCD 产生一个 USB ``hub_event`` 工作器)。h](h全局内核后台任务。这些任务是内核启动时创建的数量有限的实例(如,每一个 USB HCD 产生一个 USB }(hj9hhhNhNubjs)}(h ``hub_event``h]h hub_event}(hjAhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhj9ubh 工作器)。}(hj9hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj5ubah}(h]h ]h"]h$]h&]uh1j_hj2hhhhhNubj`)}(h局部内核后台任务。这些任务通常是由于用户空间进程与某些内核接口进行交互时产 生的,并且通常在进程退出时会被停止(如,vhost 工作器)。 h]h)}(h局部内核后台任务。这些任务通常是由于用户空间进程与某些内核接口进行交互时产 生的,并且通常在进程退出时会被停止(如,vhost 工作器)。h]h局部内核后台任务。这些任务通常是由于用户空间进程与某些内核接口进行交互时产 生的,并且通常在进程退出时会被停止(如,vhost 工作器)。}(hjchhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj_ubah}(h]h ]h"]h$]h&]uh1j_hj2hhhhhNubj`)}(h 软中断。 h]h)}(h 软中断。h]h 软中断。}(hj{hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjwubah}(h]h ]h"]h$]h&]uh1j_hj2hhhhhNubeh}(h]h ]h"]h$]h&]jjjhjjuh1jZhj-hhhhhKubh)}(hX对于 #1 和 #3,必须选择一个独特的全局句柄并将其传递给对应的 ``kcov_remote_start`` 调用。一个用户空间进程必须将该句柄存储在 ``kcov_remote_arg`` 结构体的 ``handle`` 数组字段中并将其传递给 ``KCOV_REMOTE_ENABLE``。这会将使用的 KCOV 设备附加到由此句柄引用的代码片段。多个全局 句柄标识的不同代码片段可以一次性传递。h](hV对于 #1 和 #3,必须选择一个独特的全局句柄并将其传递给对应的 }(hjhhhNhNubjs)}(h``kcov_remote_start``h]hkcov_remote_start}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh> 调用。一个用户空间进程必须将该句柄存储在 }(hjhhhNhNubjs)}(h``kcov_remote_arg``h]hkcov_remote_arg}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh 结构体的 }(hjhhhNhNubjs)}(h ``handle``h]hhandle}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh# 数组字段中并将其传递给 }(hjhhhNhNubjs)}(h``KCOV_REMOTE_ENABLE``h]hKCOV_REMOTE_ENABLE}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh。这会将使用的 KCOV 设备附加到由此句柄引用的代码片段。多个全局 句柄标识的不同代码片段可以一次性传递。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj-hhubh)}(hX对于 #2,用户空间进程必须通过 ``kcov_remote_arg`` 结构体的 ``common_handle`` 字段 传递一个非零句柄。这个通用句柄将会被保存在当前 ``task_struct`` 结构体的 ``kcov_handle`` 字段中并且需要通过自定义内核代码的修改来传递给新创建的本地任务 。这些任务需要在 ``kcov_remote_start`` 和 ``kcov_remote_stop`` 标注中依次使用传递过来的 句柄。h](h+对于 #2,用户空间进程必须通过 }(hjhhhNhNubjs)}(h``kcov_remote_arg``h]hkcov_remote_arg}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh 结构体的 }(hjhhhNhNubjs)}(h``common_handle``h]h common_handle}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubhN 字段 传递一个非零句柄。这个通用句柄将会被保存在当前 }(hjhhhNhNubjs)}(h``task_struct``h]h task_struct}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh 结构体的 }(hjhhhNhNubjs)}(h``kcov_handle``h]h kcov_handle}(hj)hhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubhx 字段中并且需要通过自定义内核代码的修改来传递给新创建的本地任务 。这些任务需要在 }(hjhhhNhNubjs)}(h``kcov_remote_start``h]hkcov_remote_start}(hj;hhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh 和 }(hjhhhNhNubjs)}(h``kcov_remote_stop``h]hkcov_remote_stop}(hjMhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh/ 标注中依次使用传递过来的 句柄。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj-hhubh)}(hKCOV 对全局句柄和通用句柄均遵循一个预定义的格式。每一个句柄都是一个 ``u64`` 整形 。当前,只有最高位和低四位字节被使用。第 4-7 字节是保留位并且值必须为 0。h](hcKCOV 对全局句柄和通用句柄均遵循一个预定义的格式。每一个句柄都是一个 }(hjehhhNhNubjs)}(h``u64``h]hu64}(hjmhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjeubhr 整形 。当前,只有最高位和低四位字节被使用。第 4-7 字节是保留位并且值必须为 0。}(hjehhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj-hhubh)}(hX.对于全局句柄,最高位的字节表示该句柄属于的子系统的标识。比如,KCOV 使用 ``1`` 表示 USB 子系统类型。全局句柄的低 4 字节表示子系统中任务实例的标识。比如,每一 个 ``hub_event`` 工作器使用 USB 总线号作为任务实例的标识。h](hi对于全局句柄,最高位的字节表示该句柄属于的子系统的标识。比如,KCOV 使用 }(hjhhhNhNubjs)}(h``1``h]h1}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubhw 表示 USB 子系统类型。全局句柄的低 4 字节表示子系统中任务实例的标识。比如,每一 个 }(hjhhhNhNubjs)}(h ``hub_event``h]h hub_event}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh< 工作器使用 USB 总线号作为任务实例的标识。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj-hhubh)}(hX对于通用句柄,使用一个保留值 ``0`` 作为子系统标识,因为这些句柄不属于一个特定 的子系统。通用句柄的低 4 字节用于识别有用户进程生成的所有本地句柄的集合实例, 该进程将通用句柄传递给 ``KCOV_REMOTE_ENABLE``。h](h+对于通用句柄,使用一个保留值 }(hjhhhNhNubjs)}(h``0``h]h0}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh 作为子系统标识,因为这些句柄不属于一个特定 的子系统。通用句柄的低 4 字节用于识别有用户进程生成的所有本地句柄的集合实例, 该进程将通用句柄传递给 }(hjhhhNhNubjs)}(h``KCOV_REMOTE_ENABLE``h]hKCOV_REMOTE_ENABLE}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jrhjubh。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj-hhubh)}(hXC实际上,如果只从系统中的单个用户空间进程收集覆盖率,那么可以使用任意值作为通用 句柄的实例标识。然而,如果通用句柄被多个用户空间进程使用,每个进程必须使用唯一 的实例标识。一个选择是使用进程标识作为通用句柄实例的标识。h]hXC实际上,如果只从系统中的单个用户空间进程收集覆盖率,那么可以使用任意值作为通用 句柄的实例标识。然而,如果通用句柄被多个用户空间进程使用,每个进程必须使用唯一 的实例标识。一个选择是使用进程标识作为通用句柄实例的标识。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM hj-hhubh)}(h下面的程序演示了如何使用 KCOV 从一个由进程产生的本地任务和处理 USB 总线的全局 任务 #1 收集覆盖率:h]h下面的程序演示了如何使用 KCOV 从一个由进程产生的本地任务和处理 USB 总线的全局 任务 #1 收集覆盖率:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj-hhubj)}(hX /* 包含和上文一样的头文件和宏定义。 */ struct kcov_remote_arg { __u32 trace_mode; __u32 area_size; __u32 num_handles; __aligned_u64 common_handle; __aligned_u64 handles[0]; }; #define KCOV_INIT_TRACE _IOR('c', 1, unsigned long) #define KCOV_DISABLE _IO('c', 101) #define KCOV_REMOTE_ENABLE _IOW('c', 102, struct kcov_remote_arg) #define COVER_SIZE (64 << 10) #define KCOV_TRACE_PC 0 #define KCOV_SUBSYSTEM_COMMON (0x00ull << 56) #define KCOV_SUBSYSTEM_USB (0x01ull << 56) #define KCOV_SUBSYSTEM_MASK (0xffull << 56) #define KCOV_INSTANCE_MASK (0xffffffffull) static inline __u64 kcov_remote_handle(__u64 subsys, __u64 inst) { if (subsys & ~KCOV_SUBSYSTEM_MASK || inst & ~KCOV_INSTANCE_MASK) return 0; return subsys | inst; } #define KCOV_COMMON_ID 0x42 #define KCOV_USB_BUS_NUM 1 int main(int argc, char **argv) { int fd; unsigned long *cover, n, i; struct kcov_remote_arg *arg; fd = open("/sys/kernel/debug/kcov", O_RDWR); if (fd == -1) perror("open"), exit(1); if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE)) perror("ioctl"), exit(1); cover = (unsigned long*)mmap(NULL, COVER_SIZE * sizeof(unsigned long), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if ((void*)cover == MAP_FAILED) perror("mmap"), exit(1); /* 通过通用句柄和 USB 总线 #1 启用代码覆盖率收集。 */ arg = calloc(1, sizeof(*arg) + sizeof(uint64_t)); if (!arg) perror("calloc"), exit(1); arg->trace_mode = KCOV_TRACE_PC; arg->area_size = COVER_SIZE; arg->num_handles = 1; arg->common_handle = kcov_remote_handle(KCOV_SUBSYSTEM_COMMON, KCOV_COMMON_ID); arg->handles[0] = kcov_remote_handle(KCOV_SUBSYSTEM_USB, KCOV_USB_BUS_NUM); if (ioctl(fd, KCOV_REMOTE_ENABLE, arg)) perror("ioctl"), free(arg), exit(1); free(arg); /* * 在这里用户需要触发执行一个内核代码段 * 该代码段要么使用通用句柄标识 * 要么触发了一些 USB 总线 #1 上的一些活动。 */ sleep(2); n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED); for (i = 0; i < n; i++) printf("0x%lx\n", cover[i + 1]); if (ioctl(fd, KCOV_DISABLE, 0)) perror("ioctl"), exit(1); if (munmap(cover, COVER_SIZE * sizeof(unsigned long))) perror("munmap"), exit(1); if (close(fd)) perror("close"), exit(1); return 0; }h]hX /* 包含和上文一样的头文件和宏定义。 */ struct kcov_remote_arg { __u32 trace_mode; __u32 area_size; __u32 num_handles; __aligned_u64 common_handle; __aligned_u64 handles[0]; }; #define KCOV_INIT_TRACE _IOR('c', 1, unsigned long) #define KCOV_DISABLE _IO('c', 101) #define KCOV_REMOTE_ENABLE _IOW('c', 102, struct kcov_remote_arg) #define COVER_SIZE (64 << 10) #define KCOV_TRACE_PC 0 #define KCOV_SUBSYSTEM_COMMON (0x00ull << 56) #define KCOV_SUBSYSTEM_USB (0x01ull << 56) #define KCOV_SUBSYSTEM_MASK (0xffull << 56) #define KCOV_INSTANCE_MASK (0xffffffffull) static inline __u64 kcov_remote_handle(__u64 subsys, __u64 inst) { if (subsys & ~KCOV_SUBSYSTEM_MASK || inst & ~KCOV_INSTANCE_MASK) return 0; return subsys | inst; } #define KCOV_COMMON_ID 0x42 #define KCOV_USB_BUS_NUM 1 int main(int argc, char **argv) { int fd; unsigned long *cover, n, i; struct kcov_remote_arg *arg; fd = open("/sys/kernel/debug/kcov", O_RDWR); if (fd == -1) perror("open"), exit(1); if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE)) perror("ioctl"), exit(1); cover = (unsigned long*)mmap(NULL, COVER_SIZE * sizeof(unsigned long), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if ((void*)cover == MAP_FAILED) perror("mmap"), exit(1); /* 通过通用句柄和 USB 总线 #1 启用代码覆盖率收集。 */ arg = calloc(1, sizeof(*arg) + sizeof(uint64_t)); if (!arg) perror("calloc"), exit(1); arg->trace_mode = KCOV_TRACE_PC; arg->area_size = COVER_SIZE; arg->num_handles = 1; arg->common_handle = kcov_remote_handle(KCOV_SUBSYSTEM_COMMON, KCOV_COMMON_ID); arg->handles[0] = kcov_remote_handle(KCOV_SUBSYSTEM_USB, KCOV_USB_BUS_NUM); if (ioctl(fd, KCOV_REMOTE_ENABLE, arg)) perror("ioctl"), free(arg), exit(1); free(arg); /* * 在这里用户需要触发执行一个内核代码段 * 该代码段要么使用通用句柄标识 * 要么触发了一些 USB 总线 #1 上的一些活动。 */ sleep(2); n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED); for (i = 0; i < n; i++) printf("0x%lx\n", cover[i + 1]); if (ioctl(fd, KCOV_DISABLE, 0)) perror("ioctl"), exit(1); if (munmap(cover, COVER_SIZE * sizeof(unsigned long))) perror("munmap"), exit(1); if (close(fd)) perror("close"), exit(1); return 0; }}hjsbah}(h]h ]h"]h$]h&]hhjnjojpjq}uh1jhhhMhj-hhubeh}(h]id4ah ]h"]远程覆盖率收集ah$]h&]uh1jUhjWhhhhhKubeh}(h]kcovah ]h"]*kcov: 用于模糊测试的代码覆盖率ah$]h&]uh1jUhhhhhhhK ubeh}(h]h ]h"]h$]h&]sourcehuh1hcurrent_sourceN current_lineNsettingsdocutils.frontendValues)}(jZN generatorN datestampN source_linkN source_urlN toc_backlinksentryfootnote_backlinksK sectnum_xformKstrip_commentsNstrip_elements_with_classesN strip_classesN report_levelK halt_levelKexit_status_levelKdebugNwarning_streamN tracebackinput_encoding utf-8-siginput_encoding_error_handlerstrictoutput_encodingutf-8output_encoding_error_handlerjGerror_encodingutf-8error_encoding_error_handlerbackslashreplace language_codeenrecord_dependenciesNconfigN id_prefixhauto_id_prefixid dump_settingsNdump_internalsNdump_transformsNdump_pseudo_xmlNexpose_internalsNstrict_visitorN_disable_configN_sourceh _destinationN _config_files]7/var/lib/git/docbuild/linux/Documentation/docutils.confafile_insertion_enabled raw_enabledKline_length_limitM'pep_referencesN pep_base_urlhttps://peps.python.org/pep_file_url_templatepep-%04drfc_referencesN rfc_base_url&https://datatracker.ietf.org/doc/html/ tab_widthKtrim_footnote_reference_spacesyntax_highlightlong smart_quotessmartquotes_locales]character_level_inline_markupdoctitle_xform docinfo_xformKsectsubtitle_xform image_loadinglinkembed_stylesheetcloak_email_addressessection_self_linkenvNubreporterNindirect_targets]substitution_defs}substitution_names}refnames}refids}nameids}(j!jj>j;jjj*j'jju nametypes}(j!j>jj*juh}(jjWj;jjjAj'jjj-u footnote_refs} citation_refs} autofootnotes]autofootnote_refs]symbol_footnotes]symbol_footnote_refs] footnotes] citations]autofootnote_startKsymbol_footnote_startK id_counter collectionsCounter}jUKsRparse_messages]transform_messages] transformerN include_log]3Documentation/translations/zh_CN/dev-tools/kcov.rst(NNNNta decorationNhhub.