【发布时间】:2016-04-09 21:32:01
【问题描述】:
我手头有一个带有 cmocka 测试的 C 项目,它是使用 CMake 构建的。现在我尝试使用 gcov 来确定测试覆盖率并使用这个 CMake 模块:https://github.com/bilke/cmake-modules/blob/master/CodeCoverage.cmake 该模块提供了一个 make 目标,它运行测试目标可执行文件(即运行 gcov),然后运行 lcov 和 genhtml 以生成报告。
现在的问题是,当测试目标被执行时,它创建的 .gcda 文件只设置了所有者的可执行位,即。 e.读取位丢失。随后,lcov 无法读取这些文件并生成覆盖率为 0% 的报告。当我之后手动chmod u+r gcda 文件并手动运行后测 lcov 命令时,报告已成功生成(显示某些内容实际上已被覆盖)。因此 gcda 文件已创建且有效,但它们的权限设置不合适。
问题似乎源于包装(使用 ld --wrap)open 函数以在测试用例中捕获返回的文件描述符。这是一个最小的编译示例:
/* wrapped_open.c */
int main(void)
{
return 0;
}
int __wrap_open(const char *filename, int flags)
{
return __real_open(filename, flags);
}
# CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(gcov-mvce C)
add_executable(wrapped_open wrapped_open.c)
target_link_libraries(wrapped_open
-Wl,--wrap=open
)
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_SOURCE_DIR}/cmake")
include(CodeCoverage)
set_target_properties(wrapped_open PROPERTIES
COMPILE_FLAGS "-g -O0 --coverage -fprofile-arcs -ftest-coverage"
LINK_FLAGS "-lgcov --coverage")
setup_target_for_coverage(wrapped_open_coverage wrapped_open "coverage")
# build like this:
cmake . -DCMAKE_BUILD_TYPE=Debug # in-source build
make
# receive coverage report like this
make wrapped_open_coverage
# simple gcc command line for compiling (no cmake required)
gcc -g -O0 --coverage -fprofile-arcs -ftest-coverage -lgcov -Wl,--wrap=open -o wrapped_open wrapped_open.c
当 open 的包装和包装函数定义分别从链接器标志和代码中删除时,它可以工作。但是对于上面的文件,文件wrapped_open.c.gcda 是使用访问掩码0100 创建的,lcov 报告以下内容:
(bulid-directory)/CMakeFiles/wrapped_open.dir/wrapped_open.c.gcda:cannot open data file, assuming not executed
...导致覆盖 0/4 行和 0/2 个函数。
为什么当 open 函数像上面那样包装时访问位是错误的,即使每个路径仍然使用未修改的参数调用原始函数(至少这是它打算做的)?一个明显的解决方法是修改 cmake 模块为我执行 chmod,但我更想了解 open 被包装时出了什么问题。
请在 cmets 中告诉我是否需要以及需要哪些其他信息来回答这个问题。
【问题讨论】:
-
什么是文件系统(ext4 或其他),在其上创建可执行文件和
.gcda文件?最好提供简化的 CMake 脚本来重现您的问题(请参阅stackoverflow.com/help/mcve)。 -
文件系统是一个 vagrant box 中的 ext4(构建目录不在同步文件夹中)。我创建了一个 mvce,并在过程中发现,当 open 被包装用于测试目的时会出现问题。因此,我相应地调整了问题并删除了 cmake 标记,因为即使不使用 cmake 构建也可以观察到问题。
-
创建文件时,
open接受 3 个参数。如果您只通过包装器传递其中的 2 个,则第三个参数(文件模式)将是垃圾。 -
如果我只想为一个签名包装它,这是否意味着我必须为所有 open 变体提供包装器?根据手册页和测试代码,还有
open(pathname, flags)不带mode参数。 -
一般情况下,
open()接受 3 个参数。如果flags不包含O_CREAT标志,您可以提供其中2 个,因此3d 参数没有意义。但是一般你不知道flags中是否包含`O_CREAT`,所以你需要使用所有3个参数。