Embedded 通过QEMU/GDB生成.gcda覆盖率文件

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然后发出

执行摘要:我想使用GDB提取存储在嵌入式目标内存中的覆盖执行计数,并使用它们创建
.gcda
文件(用于馈送到gcov/lcov)

设置

  • 我可以成功地交叉编译我的二进制文件,以我的特定嵌入式目标为目标,然后在QEMU下执行它
  • 我还可以使用QEMU的GDB支持调试二进制文件(即使用
    tar扩展远程本地主机:…
    连接到正在运行的QEMU GDB服务器,并完全控制二进制文件的执行)
覆盖范围: 现在,为了执行“目标”覆盖率分析,我交叉编译
-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