Use gcov/lcov/gcovr to get code coverage under Android APK


foreword

C/C++ code coverage is often generated using tools such as gcov/lcov/gcovr, which are very convenient to use. You can also quickly build a test environment according to the following reference documents:

In short, you need:

  1. install lcov
  2. Add -fprofile-arcs -ftest-coverage to the c/c++ compilation option to compile the executable file
  3. run the executable
  4. Use lcov to collect code coverage information

After compiling in step 2, a .gcno file will be generated in the .o directory; in step 3, after running the executable file, a .gcda file will be generated; finally, with the source code file, tools such as gcov can generate reports.

Since both lcov and gcovr are tools for secondary development based on gcov, the most important thing is to understand how the gcov tool works. The workflow of gcov will be described in detail next.


1. The location generated by gcno and gcda

When compiling c/c++ code, gcno is generated, and the location generated by gcno is consistent with the location of .o. Take the use code about code coverage lcov as an example. In the author's computer, the location of gcno is as shown in the figure below. You can see that there is a .o A .gcno file

insert image description here

The gcda file is generated after executing the program, so where is its generation path? In fact, this location has been written into the execution program. You can view the string content in the binary file through stringsthe command . Taking mainthe program in the above example as an example, you can see two lines of information about .gcda:

insert image description here

Aha, you see, the position of gcda is hard-coded. This is not good, because we want to get the gcda file under Android, but there is no such fixed path on the android phone, which will cause the gcda file to not be generated.

Fortunately, we can modify the gcda generation path by setting GCOV_PREFIXenvironment variables. For example, specify that gcda is generated under /Users/user/Downloads/gcov_testthe directory , and then run the program:

 export GCOV_PREFIX=/Users/user/Downloads/gcov_test
 ./main

After running, main.cpp.gcda and a.cpp.gcda /Users/user/Downloads/gcov_testare . But gcda still maintains the full path, that is, the actual path is:

  • /Users/user/Downloads/gcov_test/Users/user/Documents/develop/lcov_test/cmake-build-debug/CMakeFiles/main.dir/main.cpp.gcda
  • /Users/user/Downloads/gcov_test/Users/user/Documents/develop/lcov_test/cmake-build-debug/CMakeFiles/main.dir/src/a.cpp.gcda

Man, this path is too long. Fortunately, we can specify GCOV_PREFIX_STRIPto clip the series in the absolute path. For example, if set to , export GCOV_PREFIX_STRIP=8we can get:

  • /Users/user/Downloads/gcov_test/main.cpp.gcda
  • /Users/user/Downloads/gcov_test/src/a.cpp.gcda

Of course, we can also GCOV_PREFIX_STRIPset to a very large number such as 100, so that all gcdas are stored in the same directory.

2. Three elements

Three elements are required to use gcvo to obtain code coverage information:

  • gcno
  • gcda
  • source code

Among them, gcno and gcda should be in one-to-one correspondence, in the same directory level, for example, a.cpp.gcno and a.cpp.gcda should be in the same directory. The location of the source code is written into gcno when compiling, and you can find the location of the source code through stringsthe command :

insert image description here

Great, so all the information can be strung together, to sum up:

  • gcno is generated at compile time, and the generated location is at the same level as the .o file; through stringsthe command , you can find the file path of the source code in gcno
  • gcda is generated after executing the program, and the generated location is injected into the binary file. Through stringsthe command , you can find the path generated by gcda in the binary file; by setting environment variables GCOV_PREFIXand GCOV_PREFIX_STRIPwe can easily modify the generated location of gcda
  • gcno and gcda need to match one by one, the gcov command calculates the code coverage rate through the information of these two files, and generates reports

3. Get coverage under Android

With the above foreshadowing, you already know the working principle of gcov. If you want to get the code coverage of gcov under android apk, the process is roughly the same:

  1. Add coverage-related compilation options in native c/c++ compilation
  2. Compile apk or library files locally, and get c/c++ gcno files in the intermediate product directory
  3. Specify GCOV_PREFIXand GCOV_PREFIX_STRIPspecify the location where gcda is generated on the phone
  4. Pull the gcda file from the mobile phone to the machine
  5. Generate coverage reports using tools such as gcov/lcov/gcovr

The specific demo you can find in AndroidNativeCodeCoverageExample . The specific steps to get code coverage on Android apk are as follows:

  1. Set compile options in app /CMakeLists.txtset(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")

  2. After compiling the apk in Android Studio, you can find the *.gcno file in the compilation directory. Check the source code location
    insert image description here
    through stringsthe command and find that it is a relative path. Please pay attention to this relative path. If you execute the gcov command but it prompts that the source code cannot be found, please ensure gcno The /gcda file is paired with the source code.
    insert image description here
    In addition, you can query the directory generated by *.gcda by viewing the generated .o file through stringsthe command :
    insert image description here
    insert image description here

  3. Specify GCOV_PREFIXand GCOV_PREFIX_STRIP, make sure GCOV_PREFIXthat has permission to write. In the example, GCOV_PREFIXit is designated as the application cache directory; GCOV_PREFIX_STRIP=100the first 100 directories are cut out, so that all gcda files are in the same level directory for easy processing.
    insert image description here

  4. When the apk exits, the call needs to be called __gcov_flushto let gcov generate the gcda file. This article explains why to do this, here I use a simpler way, reload and call it onDestroyon exit __gcov_flush
    insert image description here
    If all goes well, you can find these *.gcda files on your phone:
    insert image description here

  5. Pull all the *.gcda files and store them in a folder at a special location. In this folder, the relative location of the source code recorded by gcno is matched. On my computer, this path is /Users/user/Documents/develop/NativeCodeCoverageTest/app/.cxx/Debug/3g1m3d1m/gcov, store the gcno file here, so that it ../../../../src/main/cpp/a.cppjust matches. At the same time, copy the compiled gcno to this directory:
    insert image description here

  6. Use tools such as gcov/lcov/gcorv to generate reports. Here take gcovr as an example:

gcovr -v -r /Users/user/Documents/develop/NativeCodeCoverageTest/app/src .

insert image description here
If gcovr cannot generate the report correctly, you can add -vthe option to see the debug information; or use gcov --dumpthe command first to try, after all, gcovr calls gcov.


4. Summary

This article introduces the basic process and principle of code coverage generated by gcov, focusing on the relationship between gcno, gcda and source code files; through stringsthe command specified in gcno and the generation location of gcda; through the settings ofGCOV_PREFIX and GCOV_PREFIX_STRIP, you can specify the location where gcda is generated. Finally, through the specific example of AndroidNativeCodeCoverageExample , it explains in detail how to run android apk and obtain the code coverage of c/c++.


5. Reference

Guess you like

Origin blog.csdn.net/weiwei9363/article/details/126002907