gcov lcov进行 android apk项目的 code coverage 代码覆盖率检测

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/hfq0219/article/details/53606820

gcov lcov进行 android apk项目的 code coverage 代码覆盖率检测

最近在做code coverage,看了很多博客,大都说到了最基本的一些要求,
比如在用gcc编译的时候加入
CFLAGS=-fprofile-arcs -ftest-coverageLDFLAGS=-lgcov -lgcc参数,就可生成*.gcno文件,
然后运行生成的可执行文件,就会在*.gcno文件对应目录下输出*.gcda文件,
然后运行gcov *.cpp 会生成*.cpp.gcov文件,
查看可看到该cpp里的语句执行了几次,然后运行
lcov --capture --directory . --output-file testHtml.info --test-name testHtml; genhtml -o result testHtml.info
可生成result文件夹,打开里面的index.html即可看到结果。

但是这是对于一般的c/c++项目,而且是在编译的机器上直接运行可执行文件,如果我是把可执行文件放到别的电脑上运行,又或者我不是运行一个简单的类似于“hello world!”的可直接在终端运行的程序,而是一个需要在android系统里运行的apk,那这样的话,运行可执行文件就无法输出*.gcda文件了,很多博客里并没有提到这一特殊情况,或者说没有讲的很明白,让初学者无从下手,这时该怎么办呢?

一、如果在别的电脑上运行,有两种方法:

①设置环境变量,gcov可设置两个环境变量来指定*.gcda文件的输出路径:GCOV_PREFIX, GCOV_PREFIX_STRIP 。在终端输入export GCOV_PREFIX=/home/$user/a/ 运行程序后将在/home/$user/a/下输出*.gcda文件,并且目录和*.gcno文件保持一致,如果同时在终端输入export GCOV_PREFIX_STRIP=$n ,那么*.gcda的目录就会减少$n层,一般不用设置这个环境变量。
②在编译源码的时候加入另一个参数-fprofile-dir(和-fprofile-arcs -ftest-coverage 放在相同位置),我几乎没在别的博客里看到别人有说到这个参数,但这个参数却非常有用,可参见https://gcc.gnu.org/onlinedocs/gcc/Gcov-Data-Files.html#Gcov-Data-Files
通过指定-fprofile-dir=/your/path/ ,编译运行后(不管是在编译的电脑还是其他电脑上),可在/your/path/(要确保运行的电脑上存在这个目录,不然程序没有权限创建这个目录,需要以root模式运行,否则无法生成*.gcda文件)看到输出的*.gcda文件。

二、如果编译的是android apk:

这时需要把apk安装到手机上运行,手机目录跟编译电脑目录不同,所以可用上面的方法指定输出目录,但还有一个问题,生成*.gcda文件的前提是程序运行结束正常退出,即return 0;,而有的apk没有退出功能,或者不允许退出,比如launcher,这时就无法生成*.gcda文件,那该怎么办呢?

没事,我们可以在源码里加入信号处理函数,通过捕获到(SIGTERM)kill等信号来手动调用__gcov_flush()函数生成*.gcda文件(这个函数很多博客有提到,具体实现我也不清楚)。代码如下(可能需要放在android_main函数入口,即MainActivity):

//add by hfq
#include <signal.h>

extern "C" void __gcov_flush();
void sigfunc(int signo)
{
    __gcov_flush();
}

extern void android_main(struct android_app* state)
{
   signal(SIGTERM,sigfunc); //set siginal
   ......
}

编译运行后,用adb kill $pid将进程杀死(可能需要有root权限),即可在指定目录下找到输出的*.gcda文件,然后将这些文件拷贝到编译的电脑上对应的*.gcno目录下,运行lcov命令,即可。
不过有可能*.gcda的目录和*.gcno目录不再对应了(我的出现了这种情况,不知道为什么),我写了一个脚本,可把*.gcda拷贝到对应的*.gcno目录下:

#!/bin/bash
source /etc/profile
set -x

gcno_path="/disk_build/kodi"
gcda_path="/disk_build/Movies"

find $gcno_path -name "*.gcno" > gcno_path.txt
find $gcda_path -name "*.gcda" > gcda_path.txt
cp gcda_path.txt gcda.txt
sed -i 's/\//\n/g' gcda_path.txt
sed -n '/gcda/w out.txt' gcda_path.txt
mv out.txt gcda_path.txt
sed -i 's/.gcno/.gcda/g' gcno_path.txt
while read da_path
do
    grep $da_path gcno_path.txt > no_path.txt
    if [ $? = 0 ]
    then
        read no_path < no_path.txt
        grep $da_path gcda.txt > da_path.txt
        read da_path < da_path.txt
        cp $da_path $no_path
    fi
done < gcda_path.txt
rm gcda_path.txt gcno_path.txt no_path.txt da_path.txt gcda.txt

这样就算结束了。

猜你喜欢

转载自blog.csdn.net/hfq0219/article/details/53606820