Embedded 通过QEMU/GDB生成.gcda覆盖率文件
执行摘要:我想使用GDB提取存储在嵌入式目标内存中的覆盖执行计数,并使用它们创建Embedded 通过QEMU/GDB生成.gcda覆盖率文件,embedded,code-coverage,gcov,Embedded,Code Coverage,Gcov,执行摘要:我想使用GDB提取存储在嵌入式目标内存中的覆盖执行计数,并使用它们创建.gcda文件(用于馈送到gcov/lcov) 设置: 我可以成功地交叉编译我的二进制文件,以我的特定嵌入式目标为目标,然后在QEMU下执行它 我还可以使用QEMU的GDB支持调试二进制文件(即使用tar扩展远程本地主机:…连接到正在运行的QEMU GDB服务器,并完全控制二进制文件的执行) 覆盖范围: 现在,为了执行“目标”覆盖率分析,我交叉编译 -fprofile arcs-ftest覆盖范围。GCC然后发出
.gcda
文件(用于馈送到gcov/lcov)
设置:
- 我可以成功地交叉编译我的二进制文件,以我的特定嵌入式目标为目标,然后在QEMU下执行它李>
- 我还可以使用QEMU的GDB支持调试二进制文件(即使用
连接到正在运行的QEMU GDB服务器,并完全控制二进制文件的执行)tar扩展远程本地主机:…
-fprofile arcs-ftest覆盖范围
。GCC然后发出64位计数器来跟踪特定代码块的执行计数
在正常(即基于主机的,非交叉编译)执行情况下,当应用程序完成\uuuu gcov\u exit
时,将调用-并将所有这些执行计数收集到.gcda
文件中(gcov然后使用该文件报告覆盖详细信息)
然而,在我的嵌入式target中,并没有文件系统,libgcov基本上包含了所有\uugcov\uu…
函数的空存根
通过QEMU/GDB解决方法:为了解决这个问题,并以GCC版本不可知的方式进行,我可以通过MYPLATFORM readelf
在二进制文件中列出覆盖范围相关的符号,并grep
列出相关的符号(例如\uu gcov0.Task1\u EntryPoint
,\uu gcov0.worker
等):
然后,我可以使用报告的偏移量/大小自动创建一个GDB脚本——一个通过简单内存转储(从偏移量、转储长度字节到本地文件)提取计数器数据的脚本
我不知道(也找不到任何相关信息/工具)如何将结果对(内存偏移量、内存数据)转换为.gcda
文件。如果存在这样的工具/脚本,我会有一种可移植的(平台无关的)方法来覆盖任何QEMU支持的平台
有这样的工具/脚本吗
如有任何建议/建议,将不胜感激
更新:我自己解决了这个问题,正如你在下面看到的,我写了一篇文章。结果是有一个(更好)的方法来做我想做的事
Linux内核,通过提供以下端点抽象出GCC版本特定的细节:
size_t convert_to_gcda(char *buffer, struct gcov_info *info)
因此,基本上,我可以通过以下步骤进行目标覆盖:
步骤1
我在我的项目中添加了三个稍加修改的linux gcov文件版本:,和。我不得不替换其中的一些linux ISM,比如vmalloc
,kfree
等等,以使代码可移植(因此,在我的嵌入式平台上可编译,这与linux无关)
步骤2
然后我提供了我自己的\uugcov\uinit
typedef struct tagGcovInfo {
struct gcov_info *info;
struct tagGcovInfo *next;
} GcovInfo;
GcovInfo *headGcov = NULL;
void __gcov_init(struct gcov_info *info)
{
printf(
"__gcov_init called for %s!\n",
gcov_info_filename(info));
fflush(stdout);
GcovInfo *newHead = malloc(sizeof(GcovInfo));
if (!newHead) {
puts("Out of memory!");
exit(1);
}
newHead->info = info;
newHead->next = headGcov;
headGcov = newHead;
}
…和退出
void __gcov_exit()
{
GcovInfo *tmp = headGcov;
while(tmp) {
char *buffer;
int bytesNeeded = convert_to_gcda(NULL, tmp->info);
buffer = malloc(bytesNeeded);
if (!buffer) {
puts("Out of memory!");
exit(1);
}
convert_to_gcda(buffer, tmp->info);
printf("Emitting %6d bytes for %s\n", bytesNeeded, gcov_info_filename(tmp->info));
free(buffer);
tmp = tmp->next;
}
}
步骤3
最后,我通过以下方式编写了我的GDB(远程驱动QEMU):
$ cat coverage.gdb
tar extended-remote :9976
file bin.debug/fputest
b base.c:88 <================= This breaks on the "Emitting" printf in __gcov_exit
commands 1
silent
set $filename = tmp->info->filename
set $dataBegin = buffer
set $dataEnd = buffer + bytesNeeded
eval "dump binary memory %s 0x%lx 0x%lx", $filename, $dataBegin, $dataEnd
c
end
c
quit
…就是这样-我能够在本地文件系统中生成.gcda文件,然后通过gcov
和lcov
查看覆盖率结果
更新:我写了一篇文章
$ cat coverage.gdb
tar extended-remote :9976
file bin.debug/fputest
b base.c:88 <================= This breaks on the "Emitting" printf in __gcov_exit
commands 1
silent
set $filename = tmp->info->filename
set $dataBegin = buffer
set $dataEnd = buffer + bytesNeeded
eval "dump binary memory %s 0x%lx 0x%lx", $filename, $dataBegin, $dataEnd
c
end
c
quit
$ # In terminal 1:
qemu-system-MYPLATFORM ... -kernel bin.debug/fputest -gdb tcp::9976 -S
$ # In terminal 2:
MYPLATFORM-gdb -x coverage.gdb