sphinx.addnodesdocument)}( rawsourcechildren]( translations LanguagesNode)}(hhh](h pending_xref)}(hhh]docutils.nodesTextEnglish}parenthsba attributes}(ids]classes]names]dupnames]backrefs] refdomainstdreftypedoc reftarget/dev-tools/gcovmodnameN classnameN refexplicitutagnamehhh ubh)}(hhh]hChinese (Traditional)}hh2sbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget"/translations/zh_TW/dev-tools/gcovmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hItalian}hhFsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget"/translations/it_IT/dev-tools/gcovmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hJapanese}hhZsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget"/translations/ja_JP/dev-tools/gcovmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hKorean}hhnsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget"/translations/ko_KR/dev-tools/gcovmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hSpanish}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget"/translations/sp_SP/dev-tools/gcovmodnameN classnameN refexplicituh1hhh ubeh}(h]h ]h"]h$]h&]current_languageChinese (Simplified)uh1h hh _documenthsourceNlineNubhnote)}(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&]uh1hhhhO/var/lib/git/docbuild/linux/Documentation/translations/zh_CN/dev-tools/gcov.rsthKubh field_body)}(h Documentation/dev-tools/gcov.rsth]h)}(hhh]h Documentation/dev-tools/gcov.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}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhhhKubh)}(h*赵军奎 Bernard Zhao h]h)}(h)赵军奎 Bernard Zhao h](h赵军奎 Bernard Zhao <}(hj hhhNhNubh reference)}(hbernard@vivo.comh]hbernard@vivo.com}(hjhhhNhNubah}(h]h ]h"]h$]h&]refurimailto:bernard@vivo.comuh1jhj ubh>}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1hhhubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubeh}(h]h ]h"]h$]h&]uh1hhhhhhhhKubhsection)}(hhh](htitle)}(h3在Linux内核里使用gcov做代码覆盖率检查h]h3在Linux内核里使用gcov做代码覆盖率检查}(hjJhhhNhNubah}(h]h ]h"]h$]h&]uh1jHhjEhhhhhKubh)}(hXkgcov分析核心支持在Linux内核中启用GCC的覆盖率测试工具 gcov_ ,Linux内核 运行时的代码覆盖率数据会以gcov兼容的格式导出到“gcov”debugfs目录中,可 以通过gcov的 ``-o`` 选项(如下示例)获得指定文件的代码运行覆盖率统计数据 (需要跳转到内核编译路径下并且要有root权限)::h](hIgcov分析核心支持在Linux内核中启用GCC的覆盖率测试工具 }(hjXhhhNhNubj)}(hgcov_h]hgcov}(hj`hhhNhNubah}(h]h ]h"]h$]h&]namegcovrefuri,https://gcc.gnu.org/onlinedocs/gcc/Gcov.htmluh1jhjXresolvedKubh ,Linux内核 运行时的代码覆盖率数据会以gcov兼容的格式导出到“gcov”debugfs目录中,可 以通过gcov的 }(hjXhhhNhNubhliteral)}(h``-o``h]h-o}(hjyhhhNhNubah}(h]h ]h"]h$]h&]uh1jwhjXubh 选项(如下示例)获得指定文件的代码运行覆盖率统计数据 (需要跳转到内核编译路径下并且要有root权限):}(hjXhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK hjEhhubh literal_block)}(hT# cd /tmp/linux-out # gcov -o /sys/kernel/debug/gcov/tmp/linux-out/kernel spinlock.ch]hT# cd /tmp/linux-out # gcov -o /sys/kernel/debug/gcov/tmp/linux-out/kernel spinlock.c}hjsbah}(h]h ]h"]h$]h&] xml:spacepreserveuh1jhhhKhjEhhubh)}(hX这将在当前目录中创建带有执行计数注释的源代码文件。 在获得这些统计文件后,可以使用图形化的gcov前端工具(比如 lcov_ ),来实现 自动化处理Linux内核的覆盖率运行数据,同时生成易于阅读的HTML格式文件。h](h这将在当前目录中创建带有执行计数注释的源代码文件。 在获得这些统计文件后,可以使用图形化的gcov前端工具(比如 }(hjhhhNhNubj)}(hlcov_h]hlcov}(hjhhhNhNubah}(h]h ]h"]h$]h&]namelcovjp,http://ltp.sourceforge.net/coverage/lcov.phpuh1jhjjrKubht ),来实现 自动化处理Linux内核的覆盖率运行数据,同时生成易于阅读的HTML格式文件。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjEhhubh)}(h可能的用途:h]h可能的用途:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjEhhubh bullet_list)}(hhh](h list_item)}(h?调试(用来判断每一行的代码是否已经运行过)h]h)}(hjh]h?调试(用来判断每一行的代码是否已经运行过)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(hZ测试改进(如何修改测试代码,尽可能地覆盖到没有运行过的代码)h]h)}(hjh]hZ测试改进(如何修改测试代码,尽可能地覆盖到没有运行过的代码)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h内核最小化配置(对于某一个选项配置,如果关联的代码从来没有运行过, 是否还需要这个配置) h]h)}(h内核最小化配置(对于某一个选项配置,如果关联的代码从来没有运行过, 是否还需要这个配置)h]h内核最小化配置(对于某一个选项配置,如果关联的代码从来没有运行过, 是否还需要这个配置)}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]h ]h"]h$]h&]bullet*uh1jhhhKhjEhhubhtarget)}(h6.. _gcov: https://gcc.gnu.org/onlinedocs/gcc/Gcov.htmlh]h}(h]gcovah ]h"]gcovah$]h&]jpjquh1j)hK'hjEhhhh، referencedKubj*)}(h6.. _lcov: http://ltp.sourceforge.net/coverage/lcov.phph]h}(h]lcovah ]h"]lcovah$]h&]jpjuh1j)hK(hjEhhhhj7KubjD)}(hhh](jI)}(h准备h]h准备}(hjGhhhNhNubah}(h]h ]h"]h$]h&]uh1jHhjDhhhhhK!ubh)}(h内核打开如下配置::h]h内核打开如下配置:}(hjUhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK#hjDhhubj)}(h&CONFIG_DEBUG_FS=y CONFIG_GCOV_KERNEL=yh]h&CONFIG_DEBUG_FS=y CONFIG_GCOV_KERNEL=y}hjcsbah}(h]h ]h"]h$]h&]jjuh1jhhhK%hjDhhubh)}(h8获取整个内核的覆盖率数据,还需要打开::h]h7获取整个内核的覆盖率数据,还需要打开:}(hjqhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK(hjDhhubj)}(hCONFIG_GCOV_PROFILE_ALL=yh]hCONFIG_GCOV_PROFILE_ALL=y}hjsbah}(h]h ]h"]h$]h&]jjuh1jhhhK*hjDhhubh)}(h需要注意的是,整个内核开启覆盖率统计会造成内核镜像文件尺寸的增大, 同时内核运行也会变慢一些。 另外,并不是所有的架构都支持整个内核开启覆盖率统计。h]h需要注意的是,整个内核开启覆盖率统计会造成内核镜像文件尺寸的增大, 同时内核运行也会变慢一些。 另外,并不是所有的架构都支持整个内核开启覆盖率统计。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK,hjDhhubh)}(hH代码运行覆盖率数据只在debugfs挂载完成后才可以访问::h]hG代码运行覆盖率数据只在debugfs挂载完成后才可以访问:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK0hjDhhubj)}(h'mount -t debugfs none /sys/kernel/debugh]h'mount -t debugfs none /sys/kernel/debug}hjsbah}(h]h ]h"]h$]h&]jjuh1jhhhK2hjDhhubeh}(h]id1ah ]h"]准备ah$]h&]uh1jChjEhhhhhK!ubjD)}(hhh](jI)}(h 定制化h]h 定制化}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jHhjhhhhhK6ubh)}(h如果要单独针对某一个路径或者文件进行代码覆盖率统计,可以在内核相应路 径的Makefile中增加如下的配置:h]h如果要单独针对某一个路径或者文件进行代码覆盖率统计,可以在内核相应路 径的Makefile中增加如下的配置:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK8hjhhubj)}(hhh](j)}(hI单独统计单个文件(例如main.o):: GCOV_PROFILE_main.o := y h](h)}(h,单独统计单个文件(例如main.o)::h]h+单独统计单个文件(例如main.o):}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK;hjubj)}(hGCOV_PROFILE_main.o := yh]hGCOV_PROFILE_main.o := y}hjsbah}(h]h ]h"]h$]h&]jjuh1jhhhK=hjubeh}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h3单独统计某一个路径:: GCOV_PROFILE := y h](h)}(h单独统计某一个路径::h]h单独统计某一个路径:}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK?hjubj)}(hGCOV_PROFILE := yh]hGCOV_PROFILE := y}hjsbah}(h]h ]h"]h$]h&]jjuh1jhhhKAhjubeh}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]h ]h"]h$]h&]j'-uh1jhhhK;hjhhubh)}(h如果要在整个内核的覆盖率统计(开启CONFIG_GCOV_PROFILE_ALL)中单独排除 某一个文件或者路径,可以使用如下的方法::h]h如果要在整个内核的覆盖率统计(开启CONFIG_GCOV_PROFILE_ALL)中单独排除 某一个文件或者路径,可以使用如下的方法:}(hj4hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKChjhhubj)}(hGCOV_PROFILE_main.o := nh]hGCOV_PROFILE_main.o := n}hjBsbah}(h]h ]h"]h$]h&]jjuh1jhhhKFhjhhubh)}(h和::h]h和:}(hjPhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKHhjhhubj)}(hGCOV_PROFILE := nh]hGCOV_PROFILE := n}hj^sbah}(h]h ]h"]h$]h&]jjuh1jhhhKJhjhhubh)}(hK此机制仅支持链接到内核镜像或编译为内核模块的文件。h]hK此机制仅支持链接到内核镜像或编译为内核模块的文件。}(hjlhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKLhjhhubeh}(h]id2ah ]h"] 定制化ah$]h&]uh1jChjEhhhhhK6ubjD)}(hhh](jI)}(h 相关文件h]h 相关文件}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jHhjhhhhhKPubh)}(h0gcov功能需要在debugfs中创建如下文件:h]h0gcov功能需要在debugfs中创建如下文件:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKRhjhhubhdefinition_list)}(hhh](hdefinition_list_item)}(h8``/sys/kernel/debug/gcov`` gcov相关功能的根路径 h](hterm)}(h``/sys/kernel/debug/gcov``h]jx)}(hjh]h/sys/kernel/debug/gcov}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jwhjubah}(h]h ]h"]h$]h&]uh1jhhhKUhjubh definition)}(hhh]h)}(hgcov相关功能的根路径h]hgcov相关功能的根路径}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKUhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKUhjubj)}(hs``/sys/kernel/debug/gcov/reset`` 全局复位文件:向该文件写入数据后会将所有的gcov统计数据清0 h](j)}(h ``/sys/kernel/debug/gcov/reset``h]jx)}(hjh]h/sys/kernel/debug/gcov/reset}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jwhjubah}(h]h ]h"]h$]h&]uh1jhhhKXhjubj)}(hhh]h)}(hQ全局复位文件:向该文件写入数据后会将所有的gcov统计数据清0h]hQ全局复位文件:向该文件写入数据后会将所有的gcov统计数据清0}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKXhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKXhjhhubj)}(h``/sys/kernel/debug/gcov/path/to/compile/dir/file.gcda`` gcov工具可以识别的覆盖率统计数据文件,向该文件写入数据后 会将本文件的gcov统计数据清0 h](j)}(h8``/sys/kernel/debug/gcov/path/to/compile/dir/file.gcda``h]jx)}(hj"h]h4/sys/kernel/debug/gcov/path/to/compile/dir/file.gcda}(hj$hhhNhNubah}(h]h ]h"]h$]h&]uh1jwhj ubah}(h]h ]h"]h$]h&]uh1jhhhK\hjubj)}(hhh]j)}(hhh]j)}(hzgcov工具可以识别的覆盖率统计数据文件,向该文件写入数据后 会将本文件的gcov统计数据清0 h](j)}(hRgcov工具可以识别的覆盖率统计数据文件,向该文件写入数据后h]hRgcov工具可以识别的覆盖率统计数据文件,向该文件写入数据后}(hjAhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhK\hj=ubj)}(hhh]h)}(h&会将本文件的gcov统计数据清0h]h&会将本文件的gcov统计数据清0}(hjRhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK\hjOubah}(h]h ]h"]h$]h&]uh1jhj=ubeh}(h]h ]h"]h$]h&]uh1jhhhK\hj:ubah}(h]h ]h"]h$]h&]uh1jhj7ubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhK\hjhhubj)}(h``/sys/kernel/debug/gcov/path/to/compile/dir/file.gcno`` gcov工具需要的软连接文件(指向编译时生成的信息统计文件),这个文件是 在gcc编译时如果配置了选项 ``-ftest-coverage`` 时生成的。 h](j)}(h8``/sys/kernel/debug/gcov/path/to/compile/dir/file.gcno``h]jx)}(hjh]h4/sys/kernel/debug/gcov/path/to/compile/dir/file.gcno}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jwhjubah}(h]h ]h"]h$]h&]uh1jhhhKahj~ubj)}(hhh]h)}(hgcov工具需要的软连接文件(指向编译时生成的信息统计文件),这个文件是 在gcc编译时如果配置了选项 ``-ftest-coverage`` 时生成的。h](hgcov工具需要的软连接文件(指向编译时生成的信息统计文件),这个文件是 在gcc编译时如果配置了选项 }(hjhhhNhNubjx)}(h``-ftest-coverage``h]h-ftest-coverage}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jwhjubh 时生成的。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK_hjubah}(h]h ]h"]h$]h&]uh1jhj~ubeh}(h]h ]h"]h$]h&]uh1jhhhKahjhhubeh}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]id3ah ]h"] 相关文件ah$]h&]uh1jChjEhhhhhKPubjD)}(hhh](jI)}(h针对模块的统计h]h针对模块的统计}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jHhjhhhhhKdubh)}(hX}内核中的模块会动态的加载和卸载,模块卸载时对应的数据会被清除掉。 gcov提供了一种机制,通过保留相关数据的副本来收集这部分卸载模块的覆盖率数据。 模块卸载后这些备份数据在debugfs中会继续存在。 一旦这个模块重新加载,模块关联的运行统计会被初始化成debugfs中备份的数据。h]hX}内核中的模块会动态的加载和卸载,模块卸载时对应的数据会被清除掉。 gcov提供了一种机制,通过保留相关数据的副本来收集这部分卸载模块的覆盖率数据。 模块卸载后这些备份数据在debugfs中会继续存在。 一旦这个模块重新加载,模块关联的运行统计会被初始化成debugfs中备份的数据。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKfhjhhubh)}(hW可以通过对内核参数gcov_persist的修改来停用gcov对模块的备份机制::h]hV可以通过对内核参数gcov_persist的修改来停用gcov对模块的备份机制:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKkhjhhubj)}(hgcov_persist = 0h]hgcov_persist = 0}hjsbah}(h]h ]h"]h$]h&]jjuh1jhhhKmhjhhubh)}(h在运行时,用户还可以通过写入模块的数据文件或者写入gcov复位文件来丢弃已卸 载模块的数据。h]h在运行时,用户还可以通过写入模块的数据文件或者写入gcov复位文件来丢弃已卸 载模块的数据。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKohjhhubeh}(h]id4ah ]h"]针对模块的统计ah$]h&]uh1jChjEhhhhhKdubjD)}(hhh](jI)}(h编译机和测试机分离h]h编译机和测试机分离}(hj*hhhNhNubah}(h]h ]h"]h$]h&]uh1jHhj'hhhhhKtubh)}(hXgcov的内核分析插桩支持内核的编译和运行是在同一台机器上,也可以编译和运 行是在不同的机器上。 如果内核编译和运行是不同的机器,那么需要额外的准备工作,这取决于gcov工具 是在哪里使用的:h]hXgcov的内核分析插桩支持内核的编译和运行是在同一台机器上,也可以编译和运 行是在不同的机器上。 如果内核编译和运行是不同的机器,那么需要额外的准备工作,这取决于gcov工具 是在哪里使用的:}(hj8hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKvhj'hhubj*)}(h.. _gcov-test_zh_CN:h]h}(h]h ]h"]h$]h&]refidgcov-test-zh-cnuh1j)hKhj'hhhhubhenumerated_list)}(hhh]j)}(hXa若gcov运行在测试机上 测试机上面gcov工具的版本必须要跟内核编译机器使用的gcc版本相兼容, 同时下面的文件要从编译机拷贝到测试机上: 从源代码中: - 所有的C文件和头文件 从编译目录中: - 所有的C文件和头文件 - 所有的.gcda文件和.gcno文件 - 所有目录的链接 特别需要注意,测试机器上面的目录结构跟编译机器上面的目录机构必须 完全一致。 如果文件是软链接,需要替换成真正的目录文件(这是由make的当前工作 目录变量CURDIR引起的)。 h](h)}(h若gcov运行在测试机上h]h若gcov运行在测试机上}(hj[hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK}hjWubh block_quote)}(hX7测试机上面gcov工具的版本必须要跟内核编译机器使用的gcc版本相兼容, 同时下面的文件要从编译机拷贝到测试机上: 从源代码中: - 所有的C文件和头文件 从编译目录中: - 所有的C文件和头文件 - 所有的.gcda文件和.gcno文件 - 所有目录的链接 特别需要注意,测试机器上面的目录结构跟编译机器上面的目录机构必须 完全一致。 如果文件是软链接,需要替换成真正的目录文件(这是由make的当前工作 目录变量CURDIR引起的)。 h](h)}(h测试机上面gcov工具的版本必须要跟内核编译机器使用的gcc版本相兼容, 同时下面的文件要从编译机拷贝到测试机上:h]h测试机上面gcov工具的版本必须要跟内核编译机器使用的gcc版本相兼容, 同时下面的文件要从编译机拷贝到测试机上:}(hjohhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjkubj)}(hhh](j)}(h0从源代码中: - 所有的C文件和头文件 h](j)}(h从源代码中:h]h从源代码中:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hhh]j)}(hhh]j)}(h所有的C文件和头文件 h]h)}(h所有的C文件和头文件h]h所有的C文件和头文件}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]j'j3uh1jhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhj}ubj)}(hp从编译目录中: - 所有的C文件和头文件 - 所有的.gcda文件和.gcno文件 - 所有目录的链接 h](j)}(h从编译目录中:h]h从编译目录中:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hhh]j)}(hhh](j)}(h所有的C文件和头文件h]h)}(hjh]h所有的C文件和头文件}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h"所有的.gcda文件和.gcno文件h]h)}(hjh]h"所有的.gcda文件和.gcno文件}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h所有目录的链接 h]h)}(h所有目录的链接h]h所有目录的链接}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]j'j3uh1jhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhj}ubeh}(h]h ]h"]h$]h&]uh1jhjkubh)}(h特别需要注意,测试机器上面的目录结构跟编译机器上面的目录机构必须 完全一致。 如果文件是软链接,需要替换成真正的目录文件(这是由make的当前工作 目录变量CURDIR引起的)。h]h特别需要注意,测试机器上面的目录结构跟编译机器上面的目录机构必须 完全一致。 如果文件是软链接,需要替换成真正的目录文件(这是由make的当前工作 目录变量CURDIR引起的)。}(hj8hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjkubeh}(h]h ]h"]h$]h&]uh1jihhhKhjWubeh}(h]h ]h"]h$]h&]uh1jhjThhhhhNubah}(h]jQah ]h"]gcov-test_zh_cnah$]h&]enumtype loweralphaprefixhsuffix)uh1jRhj'hhhhhK}expect_referenced_by_name}jVjFsexpect_referenced_by_id}jQjFsubj*)}(h.. _gcov-build_zh_CN:h]h}(h]h ]h"]h$]h&]jPgcov-build-zh-cnuh1j)hKhj'hhhhubjS)}(hhh]j)}(hXj若gcov运行在编译机上 测试用例运行结束后,如下的文件需要从测试机中拷贝到编译机上: 从sysfs中的gcov目录中: - 所有的.gcda文件 - 所有的.gcno文件软链接 这些文件可以拷贝到编译机的任意目录下,gcov使用-o选项指定拷贝的 目录。 比如一个是示例的目录结构如下:: /tmp/linux: 内核源码目录 /tmp/out: 内核编译文件路径(make O=指定) /tmp/coverage: 从测试机器上面拷贝的数据文件路径 [user@build] cd /tmp/out [user@build] gcov -o /tmp/coverage/tmp/out/init main.c h](h)}(h若gcov运行在编译机上h]h若gcov运行在编译机上}(hjthhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjpubjj)}(hX@测试用例运行结束后,如下的文件需要从测试机中拷贝到编译机上: 从sysfs中的gcov目录中: - 所有的.gcda文件 - 所有的.gcno文件软链接 这些文件可以拷贝到编译机的任意目录下,gcov使用-o选项指定拷贝的 目录。 比如一个是示例的目录结构如下:: /tmp/linux: 内核源码目录 /tmp/out: 内核编译文件路径(make O=指定) /tmp/coverage: 从测试机器上面拷贝的数据文件路径 [user@build] cd /tmp/out [user@build] gcov -o /tmp/coverage/tmp/out/init main.c h](h)}(hX测试用例运行结束后,如下的文件需要从测试机中拷贝到编译机上:h]hX测试用例运行结束后,如下的文件需要从测试机中拷贝到编译机上:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubj)}(hhh]j)}(hT从sysfs中的gcov目录中: - 所有的.gcda文件 - 所有的.gcno文件软链接 h](j)}(h从sysfs中的gcov目录中:h]h从sysfs中的gcov目录中:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hhh]j)}(hhh](j)}(h所有的.gcda文件h]h)}(hjh]h所有的.gcda文件}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h所有的.gcno文件软链接 h]h)}(h所有的.gcno文件软链接h]h所有的.gcno文件软链接}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]j'j3uh1jhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubh)}(hd这些文件可以拷贝到编译机的任意目录下,gcov使用-o选项指定拷贝的 目录。h]hd这些文件可以拷贝到编译机的任意目录下,gcov使用-o选项指定拷贝的 目录。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubh)}(h,比如一个是示例的目录结构如下::h]h+比如一个是示例的目录结构如下:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubj)}(h/tmp/linux: 内核源码目录 /tmp/out: 内核编译文件路径(make O=指定) /tmp/coverage: 从测试机器上面拷贝的数据文件路径 [user@build] cd /tmp/out [user@build] gcov -o /tmp/coverage/tmp/out/init main.ch]h/tmp/linux: 内核源码目录 /tmp/out: 内核编译文件路径(make O=指定) /tmp/coverage: 从测试机器上面拷贝的数据文件路径 [user@build] cd /tmp/out [user@build] gcov -o /tmp/coverage/tmp/out/init main.c}hjsbah}(h]h ]h"]h$]h&]jjuh1jhhhKhjubeh}(h]h ]h"]h$]h&]uh1jihhhKhjpubeh}(h]h ]h"]h$]h&]uh1jhjmhhhhhNubah}(h]jlah ]h"]gcov-build_zh_cnah$]h&]jYjZj[hj\j]startKuh1jRhj'hhhhhKj^}j0jbsj`}jljbsubeh}(h]id5ah ]h"]编译机和测试机分离ah$]h&]uh1jChjEhhhhhKtubjD)}(hhh](jI)}(h关于编译器的注意事项h]h关于编译器的注意事项}(hjAhhhNhNubah}(h]h ]h"]h$]h&]uh1jHhj>hhhhhKubh)}(hGCC和LLVM gcov工具不一定兼容。 如果编译器是GCC,使用 gcov_ 来处理.gcno和.gcda文件,如果是Clang编译器, 则使用 llvm-cov_ 。h](hGGCC和LLVM gcov工具不一定兼容。 如果编译器是GCC,使用 }(hjOhhhNhNubj)}(hgcov_h]hgcov}(hjWhhhNhNubah}(h]h ]h"]h$]h&]namegcovjpjquh1jhjOjrKubhE 来处理.gcno和.gcda文件,如果是Clang编译器, 则使用 }(hjOhhhNhNubj)}(h llvm-cov_h]hllvm-cov}(hjkhhhNhNubah}(h]h ]h"]h$]h&]namellvm-covjp0https://llvm.org/docs/CommandGuide/llvm-cov.htmluh1jhjOjrKubh 。}(hjOhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj>hhubj*)}(h6.. _gcov: https://gcc.gnu.org/onlinedocs/gcc/Gcov.htmlh]h}(h]id7ah ]h"]h$]gcovah&]jp,https://gcc.gnu.org/onlinedocs/gcc/Gcov.htmluh1j)hKj7Khj>hhhhubj*)}(h>.. _llvm-cov: https://llvm.org/docs/CommandGuide/llvm-cov.htmlh]h}(h]llvm-covah ]h"]llvm-covah$]h&]jpj{uh1j)hKhj>hhhhj7Kubh)}(hGCC和Clang gcov之间的版本差异由Kconfig处理的。 kconfig会根据编译工具链的检查自动选择合适的gcov格式。h]hGCC和Clang gcov之间的版本差异由Kconfig处理的。 kconfig会根据编译工具链的检查自动选择合适的gcov格式。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj>hhubeh}(h]id6ah ]h"]关于编译器的注意事项ah$]h&]uh1jChjEhhhhhKubjD)}(hhh](jI)}(h 问题定位h]h 问题定位}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jHhjhhhhhKubj)}(hhh](j)}(h9可能出现的问题1 编译到链接阶段报错终止 h](j)}(h可能出现的问题1h]h可能出现的问题1}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hhh]h)}(h!编译到链接阶段报错终止h]h!编译到链接阶段报错终止}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hn问题原因 分析标志指定在了源文件但是没有链接到主内核,或者客制化了链接程序 h](j)}(h 问题原因h]h 问题原因}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hhh]h)}(h`分析标志指定在了源文件但是没有链接到主内核,或者客制化了链接程序h]h`分析标志指定在了源文件但是没有链接到主内核,或者客制化了链接程序}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjhhubj)}(h解决方法 通过在相应的Makefile中使用 ``GCOV_PROFILE := n`` 或者 ``GCOV_PROFILE_basename.o := n`` 来将链接报错的文件排除掉 h](j)}(h 解决方法h]h 解决方法}(hj+hhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhj'ubj)}(hhh]h)}(h通过在相应的Makefile中使用 ``GCOV_PROFILE := n`` 或者 ``GCOV_PROFILE_basename.o := n`` 来将链接报错的文件排除掉h](h$通过在相应的Makefile中使用 }(hj<hhhNhNubjx)}(h``GCOV_PROFILE := n``h]hGCOV_PROFILE := n}(hjDhhhNhNubah}(h]h ]h"]h$]h&]uh1jwhj<ubh 或者 }(hj<hhhNhNubjx)}(h ``GCOV_PROFILE_basename.o := n``h]hGCOV_PROFILE_basename.o := n}(hjVhhhNhNubah}(h]h ]h"]h$]h&]uh1jwhj<ubh% 来将链接报错的文件排除掉}(hj<hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj9ubah}(h]h ]h"]h$]h&]uh1jhj'ubeh}(h]h ]h"]h$]h&]uh1jhhhKhjhhubj)}(hG可能出现的问题2 从sysfs复制的文件显示为空或不完整 h](j)}(h可能出现的问题2h]h可能出现的问题2}(hj~hhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjzubj)}(hhh]h)}(h/从sysfs复制的文件显示为空或不完整h]h/从sysfs复制的文件显示为空或不完整}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjzubeh}(h]h ]h"]h$]h&]uh1jhhhKhjhhubj)}(h{问题原因 由于seq_file的工作方式,某些工具(例如cp或tar)可能无法正确地从 sysfs复制文件。 h](j)}(h 问题原因h]h 问题原因}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hhh]h)}(hm由于seq_file的工作方式,某些工具(例如cp或tar)可能无法正确地从 sysfs复制文件。h]hm由于seq_file的工作方式,某些工具(例如cp或tar)可能无法正确地从 sysfs复制文件。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjhhubj)}(h解决方法 使用 ``cat`` 读取 ``.gcda`` 文件,使用 ``cp -d`` 复制链接,或者使用附录B 中所示的机制。 h](j)}(h 解决方法h]h 解决方法}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hhh]h)}(hr使用 ``cat`` 读取 ``.gcda`` 文件,使用 ``cp -d`` 复制链接,或者使用附录B 中所示的机制。h](h使用 }(hjhhhNhNubjx)}(h``cat``h]hcat}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jwhjubh 读取 }(hjhhhNhNubjx)}(h ``.gcda``h]h.gcda}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jwhjubh 文件,使用 }(hjhhhNhNubjx)}(h ``cp -d``h]hcp -d}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jwhjubh9 复制链接,或者使用附录B 中所示的机制。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjhhubeh}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]id8ah ]h"] 问题定位ah$]h&]uh1jChjEhhhhhKubjD)}(hhh](jI)}(h附录A:collect_on_build.shh]h附录A:collect_on_build.sh}(hjN hhhNhNubah}(h]h ]h"]h$]h&]uh1jHhjK hhhhhKubh)}(h用于在编译机上收集覆盖率元文件的示例脚本 (见 :ref:`编译机和测试机分离 a. ` )h](hD用于在编译机上收集覆盖率元文件的示例脚本 (见 }(hj\ hhhNhNubh)}(h7:ref:`编译机和测试机分离 a. `h]hinline)}(hjf h]h编译机和测试机分离 a.}(hjj hhhNhNubah}(h]h ](xrefstdstd-refeh"]h$]h&]uh1jh hjd ubah}(h]h ]h"]h$]h&]refdoc!translations/zh_CN/dev-tools/gcov refdomainju reftyperef refexplicitrefwarn reftargetgcov-test_zh_cnuh1hhhhKhj\ ubh )}(hj\ hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjK hhubj)}(hXg#!/bin/bash KSRC=$1 KOBJ=$2 DEST=$3 if [ -z "$KSRC" ] || [ -z "$KOBJ" ] || [ -z "$DEST" ]; then echo "Usage: $0 " >&2 exit 1 fi KSRC=$(cd $KSRC; printf "all:\n\t@echo \${CURDIR}\n" | make -f -) KOBJ=$(cd $KOBJ; printf "all:\n\t@echo \${CURDIR}\n" | make -f -) find $KSRC $KOBJ \( -name '*.gcno' -o -name '*.[ch]' -o -type l \) -a \ -perm /u+r,g+r | tar cfz $DEST -P -T - if [ $? -eq 0 ] ; then echo "$DEST successfully created, copy to test system and unpack with:" echo " tar xfz $DEST -P" else echo "Could not create file $DEST" fih]hXg#!/bin/bash KSRC=$1 KOBJ=$2 DEST=$3 if [ -z "$KSRC" ] || [ -z "$KOBJ" ] || [ -z "$DEST" ]; then echo "Usage: $0 " >&2 exit 1 fi KSRC=$(cd $KSRC; printf "all:\n\t@echo \${CURDIR}\n" | make -f -) KOBJ=$(cd $KOBJ; printf "all:\n\t@echo \${CURDIR}\n" | make -f -) find $KSRC $KOBJ \( -name '*.gcno' -o -name '*.[ch]' -o -type l \) -a \ -perm /u+r,g+r | tar cfz $DEST -P -T - if [ $? -eq 0 ] ; then echo "$DEST successfully created, copy to test system and unpack with:" echo " tar xfz $DEST -P" else echo "Could not create file $DEST" fi}hj sbah}(h]h ]h"]h$]h&]jjforcelanguageshhighlight_args}uh1jhhhKhjK hhubeh}(h]a-collect-on-build-shah ]h"]附录a:collect_on_build.shah$]h&]uh1jChjEhhhhhKubjD)}(hhh](jI)}(h附录B:collect_on_test.shh]h附录B:collect_on_test.sh}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jHhj hhhhhKubh)}(h用于在测试机上收集覆盖率数据文件的示例脚本 (见 :ref:`编译机和测试机分离 b. ` )h](hG用于在测试机上收集覆盖率数据文件的示例脚本 (见 }(hj hhhNhNubh)}(h8:ref:`编译机和测试机分离 b. `h]ji )}(hj h]h编译机和测试机分离 b.}(hj hhhNhNubah}(h]h ](jt stdstd-refeh"]h$]h&]uh1jh hj ubah}(h]h ]h"]h$]h&]refdocj refdomainj reftyperef refexplicitrefwarnj gcov-build_zh_cnuh1hhhhKhj ubh )}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj hhubj)}(hX#!/bin/bash -e DEST=$1 GCDA=/sys/kernel/debug/gcov if [ -z "$DEST" ] ; then echo "Usage: $0 " >&2 exit 1 fi TEMPDIR=$(mktemp -d) echo Collecting data.. find $GCDA -type d -exec mkdir -p $TEMPDIR/\{\} \; find $GCDA -name '*.gcda' -exec sh -c 'cat < $0 > '$TEMPDIR'/$0' {} \; find $GCDA -name '*.gcno' -exec sh -c 'cp -d $0 '$TEMPDIR'/$0' {} \; tar czf $DEST -C $TEMPDIR sys rm -rf $TEMPDIR echo "$DEST successfully created, copy to build system and unpack with:" echo " tar xfz $DEST"h]hX#!/bin/bash -e DEST=$1 GCDA=/sys/kernel/debug/gcov if [ -z "$DEST" ] ; then echo "Usage: $0 " >&2 exit 1 fi TEMPDIR=$(mktemp -d) echo Collecting data.. find $GCDA -type d -exec mkdir -p $TEMPDIR/\{\} \; find $GCDA -name '*.gcda' -exec sh -c 'cat < $0 > '$TEMPDIR'/$0' {} \; find $GCDA -name '*.gcno' -exec sh -c 'cp -d $0 '$TEMPDIR'/$0' {} \; tar czf $DEST -C $TEMPDIR sys rm -rf $TEMPDIR echo "$DEST successfully created, copy to build system and unpack with:" echo " tar xfz $DEST"}hj sbah}(h]h ]h"]h$]h&]jjj j shj }uh1jhhhKhj hhubeh}(h]b-collect-on-test-shah ]h"]附录b:collect_on_test.shah$]h&]uh1jChjEhhhhhKubeh}(h] linuxgcovah ]h"]3在linux内核里使用gcov做代码覆盖率检查ah$]h&]uh1jChhhhhhhKubeh}(h]h ]h"]h$]h&]sourcehuh1hcurrent_sourceN current_lineNsettingsdocutils.frontendValues)}(jHN 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_handlerj4 error_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}(gcov](j`jWelcov]jallvm-cov]jkaurefids}(jQ]jFajl]jbaunameids}(j j j4j1jAj>jjjj|jjj$j!j;j8jVjQj0jljjjjjH jE j j j j u nametypes}(j j4jAjjjj$j;jVj0jjjH j j uh}(j jEj1j+j>j8jjDj|jjjj!jj8j'jQjTjljmjj>jjjjjE jj jK j j u footnote_refs} citation_refs} autofootnotes]autofootnote_refs]symbol_footnotes]symbol_footnote_refs] footnotes] citations]autofootnote_startKsymbol_footnote_startK id_counter collectionsCounter}jB KsRparse_messages](hsystem_message)}(hhh]h)}(h:Enumerated list start value not ordinal-1: "b" (ordinal 2)h]h>Enumerated list start value not ordinal-1: “b” (ordinal 2)}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj ubah}(h]h ]h"]h$]h&]levelKtypeINFOsourceh،lineKuh1j hj'hhhhhKubj )}(hhh]h)}(h'Duplicate explicit target name: "gcov".h]h+Duplicate explicit target name: “gcov”.}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj ubah}(h]h ]h"]h$]h&]jalevelKtypej lineKsourcehuh1j hj>hhhhhKubetransform_messages](j )}(hhh]h)}(hhh]h5Hyperlink target "gcov-test-zh-cn" is not referenced.}hj sbah}(h]h ]h"]h$]h&]uh1hhj ubah}(h]h ]h"]h$]h&]levelKtypej sourceh،lineKuh1j ubj )}(hhh]h)}(hhh]h6Hyperlink target "gcov-build-zh-cn" is not referenced.}hj sbah}(h]h ]h"]h$]h&]uh1hhj ubah}(h]h ]h"]h$]h&]levelKtypej sourceh،lineKuh1j ube transformerN include_log]3Documentation/translations/zh_CN/dev-tools/gcov.rst(NNNNta decorationNhhub.