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

时间:2016-04-25 11:32:35

标签: embedded code-coverage gcov

执行摘要:我想使用GDB提取存储在嵌入式目标内存中的coverage执行计数,并使用它们创建.gcda个文件(用于提供给的gcov / LCOV )。

设置

  • 我可以成功交叉编译我的二进制文件,以我的特定嵌入式目标为目标 - 然后在QEMU下执行它。
  • 我也可以使用QEMU的GDB支持来调试二进制文件(即使用tar extended-remote localhost:...连接到正在运行的QEMU GDB服务器,并完全控制我的二进制文件的执行。)

覆盖范围: 现在,为了执行“目标”覆盖分析,我用交叉编译 -fprofile-arcs -ftest-coverage。然后,GCC发出64位计数器以跟踪特定代码块的执行计数。

在正常(即基于主机,而不是交叉编译)执行时,当应用程序完成__gcov_exit被调用时 - 并将所有这些执行计数收集到.gcda文件中(然后gcov用于报告)报道细节)。

然而,在我的嵌入式目标中,没有文件系统可供使用 - 而libgcov基本上包含所有__gcov_...函数的空存根。

通过QEMU / GDB解决方法:为了解决这个问题,并以GCC版本无关的方式进行解决,我可以通过MYPLATFORM-readelf在我的二进制文件中列出与覆盖相关的符号,和grep - 相关的(例如__gcov0.Task1_EntryPoint__gcov0.worker等):

$ MYPLATFORM-readelf -s binary | grep __gcov
...
46: 40021498  48 OBJECT  LOCAL  DEFAULT 4 __gcov0.Task1_EntryPoint
...

然后我可以使用报告的偏移量/大小来自动创建GDB脚本 - 一个通过简单内存转储(来自偏移,转储长度到本地文件的字节)。

我不知道(并且未能找到任何相关信息/工具),是如何将生成的(内存偏移量,内存数据)对转换为.gcda个文件。如果存在这样的工具/脚本,我将使用便携式(平台无关)的方式在任何支持QEMU的平台上进行覆盖。

是否有这样的工具/脚本?

任何建议/指示都将非常受欢迎。

更新:我自己解决了这个问题,你可以在下面阅读 - 然后写了blog post about it

1 个答案:

答案 0 :(得分:1)

原来有一种(更好的)方法可以做我想做的事。

Linux内核includes portable GCOV related functionality,通过提供此端点抽象出GCC特定于版本的详细信息:

size_t convert_to_gcda(char *buffer, struct gcov_info *info)

基本上,我能够通过以下步骤进行目标覆盖:

第1步

我在我的项目中添加了三个稍微修改过的linux gcov文件版本:base.cgcc_4_7.cgcov.h。我不得不更换其中的一些linux-isms - 比如vmallockfree等 - 以使代码可移植(因此,可在我的嵌入式平台上编译,这与Linux无关)。

第2步

然后我提供了自己的__gcov_init ...

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;
}

...和__gcov_exit

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

最后,执行了QEMU和GDB - 就像这样:

$ # In terminal 1:
qemu-system-MYPLATFORM ... -kernel bin.debug/fputest  -gdb tcp::9976 -S

$ # In terminal 2:
MYPLATFORM-gdb -x coverage.gdb

......那就是它 - 我能够在我的本地文件系统中生成.gcda文件,然后在gcovlcov上查看覆盖率结果。

更新:我写了blog post showing the process in detail