尝试解决 Xcode7.1 覆盖率测试 GCDA 文件损坏问题

在升级到 Xcode7 后,项目遇到覆盖率测试输出的 GCDA 文件损坏问题。

覆盖率测试原理

  1. 在 App 运行时调用__gcov_flush() 输出 GCDA 文件, 记录每行代码的执行次数。

  2. 然后用 lcov 命令从 CGDA + GCOV 生成报告文件,可以看到运行过程中每行代码的执行次数。

问题表现

  1. 终端输出信息后 crash:
1
profiling: /Users/username/Library/Developer/CoreSimulator/Devices/DEVICE_ID/data/Containers/Data/Application/APP_ID/Documents/CoverageTest/CoreGraphics.gcda: cannot merge previous GCDA file: corrupt arc tag (0x00000011)
  1. lcov 命令从 GCDA 文件恢复的代码执行次数数据为0。而同样的代码用 Xcode6 得到的 GCDA 文件则没有问题。

问题定位

搜到很多人讨论,尝试文中提到的方法均无效,怀疑是 Xcode7 bug:生成 GCDA 文件的 __gcov_flush() 函数的问题。

https://forums.developer.apple.com/thread/9765
https://github.com/specta/specta/issues/167
http://stackoverflow.com/questions/22519530/dozens-of-profilinginvalid-arc-tag-when-running-code-coverage-in-xcode-5
https://twitter.com/orta/status/595599325675171840

问题解决

由于这个函数在 llvm runtime library 中,较难定位debug,故找到其llvm compiler-rt 开源代码,导入Xcode项目中: GCDAProfiling.c / InstrProfiling.c / InstrProfiling.h

将调用__gcov_flush()的文件声明 extern void __gcov_flush() 改成 #import “GCDAProfiling.c”,就可以自行 debug 了。

问题1:crash of cannot merge previous GCDA file: corrupt arc

1
2
3
4
5
6
7
8
9
10
void llvm_gcda_start_file(const char *orig_filename, const char version[4],
uint32_t checksum) {
const char *mode = "r+b";
filename = mangle_filename(orig_filename);
// fix : cannot merge previous GCDA file: corrupt arc tag
char *skipFileNames[2] = {"QuartzCore.gcda", "CoreGraphics.gcda"};
for (int i = 0; i < 2; i++) {
if (strstr(filename, skipFileNames[i])) return;
}
....

问题2: GCDA 恢复得到计数为0

gcda计数为0 定位到这里,原因是uint64_t 累加溢出了,导致计数为0。加一个溢出保护。

gcda fix 2

未来工作

GCDA 恢复数据虽然有了,基本满足测试组的需求(有没有覆盖到),但是不是完全正确的。

本文有帮助?