在升级到 Xcode7 后,项目遇到覆盖率测试输出的 GCDA 文件损坏问题。
覆盖率测试原理
- 在 App 运行时调用__gcov_flush() 输出 GCDA 文件, 记录每行代码的执行次数。
- 然后用 lcov 命令从 CGDA GCOV 生成报告文件,可以看到运行过程中每行代码的执行次数。
问题表现
- 终端输出信息后 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) |
---|
- 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
12345678910 | 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 恢复数据虽然有了,基本满足测试组的需求(有没有覆盖到),但是不是完全正确的。