.. include:: ../disclaimer-zh_CN.rst :Original: Documentation/dev-tools/gcov.rst :Translator: 赵军奎 Bernard Zhao <bernard@vivo.com> 在Linuxå†…æ ¸é‡Œä½¿ç”¨gcovåšä»£ç 覆盖率检查 ===================================== gcov分æžæ ¸å¿ƒæ”¯æŒåœ¨Linuxå†…æ ¸ä¸å¯ç”¨GCC的覆盖率测试工具 gcov_ ,Linuxå†…æ ¸ è¿è¡Œæ—¶çš„代ç 覆盖率数æ®ä¼šä»¥gcovå…¼å®¹çš„æ ¼å¼å¯¼å‡ºåˆ°â€œgcovâ€debugfs目录ä¸ï¼Œå¯ 以通过gcovçš„ ``-o`` 选项(如下示例)获得指定文件的代ç è¿è¡Œè¦†ç›–çŽ‡ç»Ÿè®¡æ•°æ® ï¼ˆéœ€è¦è·³è½¬åˆ°å†…æ ¸ç¼–è¯‘è·¯å¾„ä¸‹å¹¶ä¸”è¦æœ‰rootæƒé™ï¼‰:: # cd /tmp/linux-out # gcov -o /sys/kernel/debug/gcov/tmp/linux-out/kernel spinlock.c 这将在当å‰ç›®å½•ä¸åˆ›å»ºå¸¦æœ‰æ‰§è¡Œè®¡æ•°æ³¨é‡Šçš„æºä»£ç 文件。 在获得这些统计文件åŽï¼Œå¯ä»¥ä½¿ç”¨å›¾å½¢åŒ–çš„gcovå‰ç«¯å·¥å…·ï¼ˆæ¯”如 lcov_ ),æ¥å®žçŽ° 自动化处ç†Linuxå†…æ ¸çš„è¦†ç›–çŽ‡è¿è¡Œæ•°æ®ï¼ŒåŒæ—¶ç”Ÿæˆæ˜“于阅读的HTMLæ ¼å¼æ–‡ä»¶ã€‚ å¯èƒ½çš„用途: * 调试(用æ¥åˆ¤æ–æ¯ä¸€è¡Œçš„代ç 是å¦å·²ç»è¿è¡Œè¿‡ï¼‰ * 测试改进(如何修改测试代ç ,尽å¯èƒ½åœ°è¦†ç›–到没有è¿è¡Œè¿‡çš„代ç ) * å†…æ ¸æœ€å°åŒ–é…置(对于æŸä¸€ä¸ªé€‰é¡¹é…置,如果关è”的代ç 从æ¥æ²¡æœ‰è¿è¡Œè¿‡ï¼Œ 是å¦è¿˜éœ€è¦è¿™ä¸ªé…置) .. _gcov: https://gcc.gnu.org/onlinedocs/gcc/Gcov.html .. _lcov: http://ltp.sourceforge.net/coverage/lcov.php 准备 ---- å†…æ ¸æ‰“å¼€å¦‚ä¸‹é…ç½®:: CONFIG_DEBUG_FS=y CONFIG_GCOV_KERNEL=y 获å–æ•´ä¸ªå†…æ ¸çš„è¦†ç›–çŽ‡æ•°æ®ï¼Œè¿˜éœ€è¦æ‰“å¼€:: CONFIG_GCOV_PROFILE_ALL=y 需è¦æ³¨æ„çš„æ˜¯ï¼Œæ•´ä¸ªå†…æ ¸å¼€å¯è¦†ç›–çŽ‡ç»Ÿè®¡ä¼šé€ æˆå†…æ ¸é•œåƒæ–‡ä»¶å°ºå¯¸çš„增大, åŒæ—¶å†…æ ¸è¿è¡Œä¹Ÿä¼šå˜æ…¢ä¸€äº›ã€‚ å¦å¤–,并ä¸æ˜¯æ‰€æœ‰çš„架构都支æŒæ•´ä¸ªå†…æ ¸å¼€å¯è¦†ç›–率统计。 代ç è¿è¡Œè¦†ç›–率数æ®åªåœ¨debugfs挂载完æˆåŽæ‰å¯ä»¥è®¿é—®:: mount -t debugfs none /sys/kernel/debug 定制化 ------ 如果è¦å•ç‹¬é’ˆå¯¹æŸä¸€ä¸ªè·¯å¾„或者文件进行代ç 覆盖率统计,å¯ä»¥åœ¨å†…æ ¸ç›¸åº”è·¯ 径的Makefileä¸å¢žåŠ 如下的é…ç½®: - å•ç‹¬ç»Ÿè®¡å•ä¸ªæ–‡ä»¶ï¼ˆä¾‹å¦‚main.o):: GCOV_PROFILE_main.o := y - å•ç‹¬ç»Ÿè®¡æŸä¸€ä¸ªè·¯å¾„:: GCOV_PROFILE := y 如果è¦åœ¨æ•´ä¸ªå†…æ ¸çš„è¦†ç›–çŽ‡ç»Ÿè®¡ï¼ˆå¼€å¯CONFIG_GCOV_PROFILE_ALL)ä¸å•ç‹¬æŽ’除 æŸä¸€ä¸ªæ–‡ä»¶æˆ–者路径,å¯ä»¥ä½¿ç”¨å¦‚下的方法:: GCOV_PROFILE_main.o := n å’Œ:: GCOV_PROFILE := n æ¤æœºåˆ¶ä»…支æŒé“¾æŽ¥åˆ°å†…æ ¸é•œåƒæˆ–ç¼–è¯‘ä¸ºå†…æ ¸æ¨¡å—的文件。 相关文件 -------- gcov功能需è¦åœ¨debugfsä¸åˆ›å»ºå¦‚下文件: ``/sys/kernel/debug/gcov`` gcovç›¸å…³åŠŸèƒ½çš„æ ¹è·¯å¾„ ``/sys/kernel/debug/gcov/reset`` 全局å¤ä½æ–‡ä»¶:å‘该文件写入数æ®åŽä¼šå°†æ‰€æœ‰çš„gcov统计数æ®æ¸…0 ``/sys/kernel/debug/gcov/path/to/compile/dir/file.gcda`` gcov工具å¯ä»¥è¯†åˆ«çš„覆盖率统计数æ®æ–‡ä»¶ï¼Œå‘该文件写入数æ®åŽ 会将本文件的gcov统计数æ®æ¸…0 ``/sys/kernel/debug/gcov/path/to/compile/dir/file.gcno`` gcov工具需è¦çš„软连接文件(指å‘编译时生æˆçš„ä¿¡æ¯ç»Ÿè®¡æ–‡ä»¶ï¼‰ï¼Œè¿™ä¸ªæ–‡ä»¶æ˜¯ 在gcc编译时如果é…置了选项 ``-ftest-coverage`` 时生æˆçš„。 针对模å—的统计 -------------- å†…æ ¸ä¸çš„模å—会动æ€çš„åŠ è½½å’Œå¸è½½ï¼Œæ¨¡å—å¸è½½æ—¶å¯¹åº”çš„æ•°æ®ä¼šè¢«æ¸…除掉。 gcovæ供了一ç§æœºåˆ¶ï¼Œé€šè¿‡ä¿ç•™ç›¸å…³æ•°æ®çš„副本æ¥æ”¶é›†è¿™éƒ¨åˆ†å¸è½½æ¨¡å—的覆盖率数æ®ã€‚ 模å—å¸è½½åŽè¿™äº›å¤‡ä»½æ•°æ®åœ¨debugfsä¸ä¼šç»§ç»å˜åœ¨ã€‚ 一旦这个模å—é‡æ–°åŠ 载,模å—å…³è”çš„è¿è¡Œç»Ÿè®¡ä¼šè¢«åˆå§‹åŒ–æˆdebugfsä¸å¤‡ä»½çš„æ•°æ®ã€‚ å¯ä»¥é€šè¿‡å¯¹å†…æ ¸å‚æ•°gcov_persist的修改æ¥åœç”¨gcov对模å—的备份机制:: gcov_persist = 0 在è¿è¡Œæ—¶ï¼Œç”¨æˆ·è¿˜å¯ä»¥é€šè¿‡å†™å…¥æ¨¡å—çš„æ•°æ®æ–‡ä»¶æˆ–者写入gcovå¤ä½æ–‡ä»¶æ¥ä¸¢å¼ƒå·²å¸ 载模å—çš„æ•°æ®ã€‚ 编译机和测试机分离 ------------------ gcovçš„å†…æ ¸åˆ†æžæ’桩支æŒå†…æ ¸çš„ç¼–è¯‘å’Œè¿è¡Œæ˜¯åœ¨åŒä¸€å°æœºå™¨ä¸Šï¼Œä¹Ÿå¯ä»¥ç¼–è¯‘å’Œè¿ è¡Œæ˜¯åœ¨ä¸åŒçš„机器上。 å¦‚æžœå†…æ ¸ç¼–è¯‘å’Œè¿è¡Œæ˜¯ä¸åŒçš„机器,那么需è¦é¢å¤–的准备工作,这å–决于gcov工具 是在哪里使用的: .. _gcov-test_zh: a) è‹¥gcovè¿è¡Œåœ¨æµ‹è¯•æœºä¸Š 测试机上é¢gcov工具的版本必须è¦è·Ÿå†…æ ¸ç¼–è¯‘æœºå™¨ä½¿ç”¨çš„gcc版本相兼容, åŒæ—¶ä¸‹é¢çš„文件è¦ä»Žç¼–译机拷è´åˆ°æµ‹è¯•æœºä¸Š: 从æºä»£ç ä¸: - 所有的C文件和头文件 从编译目录ä¸: - 所有的C文件和头文件 - 所有的.gcda文件和.gcno文件 - 所有目录的链接 特别需è¦æ³¨æ„,测试机器上é¢çš„目录结构跟编译机器上é¢çš„目录机构必须 完全一致。 如果文件是软链接,需è¦æ›¿æ¢æˆçœŸæ£çš„目录文件(这是由make的当å‰å·¥ä½œ 目录å˜é‡CURDIR引起的)。 .. _gcov-build_zh: b) è‹¥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 关于编译器的注æ„事项 -------------------- GCCå’ŒLLVM gcov工具ä¸ä¸€å®šå…¼å®¹ã€‚ 如果编译器是GCC,使用 gcov_ æ¥å¤„ç†.gcnoå’Œ.gcda文件,如果是Clang编译器, 则使用 llvm-cov_ 。 .. _gcov: https://gcc.gnu.org/onlinedocs/gcc/Gcov.html .. _llvm-cov: https://llvm.org/docs/CommandGuide/llvm-cov.html GCCå’ŒClang gcov之间的版本差异由Kconfig处ç†çš„。 kconfigä¼šæ ¹æ®ç¼–译工具链的检查自动选择åˆé€‚çš„gcovæ ¼å¼ã€‚ é—®é¢˜å®šä½ -------- å¯èƒ½å‡ºçŽ°çš„问题1 ç¼–è¯‘åˆ°é“¾æŽ¥é˜¶æ®µæŠ¥é”™ç»ˆæ¢ é—®é¢˜åŽŸå› åˆ†æžæ ‡å¿—指定在了æºæ–‡ä»¶ä½†æ˜¯æ²¡æœ‰é“¾æŽ¥åˆ°ä¸»å†…æ ¸ï¼Œæˆ–è€…å®¢åˆ¶åŒ–äº†é“¾æŽ¥ç¨‹åº è§£å†³æ–¹æ³• 通过在相应的Makefileä¸ä½¿ç”¨ ``GCOV_PROFILE := n`` 或者 ``GCOV_PROFILE_basename.o := n`` æ¥å°†é“¾æŽ¥æŠ¥é”™çš„文件排除掉 å¯èƒ½å‡ºçŽ°çš„问题2 从sysfså¤åˆ¶çš„文件显示为空或ä¸å®Œæ•´ é—®é¢˜åŽŸå› ç”±äºŽseq_file的工作方å¼ï¼ŒæŸäº›å·¥å…·ï¼ˆä¾‹å¦‚cp或tar)å¯èƒ½æ— 法æ£ç¡®åœ°ä»Ž sysfså¤åˆ¶æ–‡ä»¶ã€‚ 解决方法 使用 ``cat`` è¯»å– ``.gcda`` 文件,使用 ``cp -d`` å¤åˆ¶é“¾æŽ¥ï¼Œæˆ–者使用附录B ä¸æ‰€ç¤ºçš„机制。 附录A:collect_on_build.sh -------------------------- 用于在编译机上收集覆盖率元文件的示例脚本 ï¼ˆè§ :ref:`编译机和测试机分离 a. <gcov-test_zh>` ) .. code-block:: sh #!/bin/bash KSRC=$1 KOBJ=$2 DEST=$3 if [ -z "$KSRC" ] || [ -z "$KOBJ" ] || [ -z "$DEST" ]; then echo "Usage: $0 <ksrc directory> <kobj directory> <output.tar.gz>" >&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 附录B:collect_on_test.sh ------------------------- 用于在测试机上收集覆盖率数æ®æ–‡ä»¶çš„示例脚本 ï¼ˆè§ :ref:`编译机和测试机分离 b. <gcov-build_zh>` ) .. code-block:: sh #!/bin/bash -e DEST=$1 GCDA=/sys/kernel/debug/gcov if [ -z "$DEST" ] ; then echo "Usage: $0 <output.tar.gz>" >&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"