【问题标题】:Generating .gcda coverage files via QEMU/GDB通过 QEMU/GDB 生成 .gcda 覆盖文件
【发布时间】:2016-08-18 18:31:53
【问题描述】:

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

设置

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

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

在正常(即基于主机,非交叉编译)执行下,当应用程序完成时,__gcov_exit 被调用 - 并将所有这些执行计数收集到 .gcdafiles(然后 gcov 用于报告覆盖率详细信息)。

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

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

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

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

我不知道(并且未能找到任何相关信息/工具)是如何将生成的 (memory offset,memory data) 对转换为 .gcda 文件。如果存在这样的工具/脚本,我将有一种可移植(与平台无关)的方式来覆盖任何支持 QEMU 的平台。

有这样的工具/脚本吗?

任何建议/指针将不胜感激。

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

【问题讨论】:

    标签: embedded code-coverage gcov


    【解决方案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

    【讨论】:

      猜你喜欢
      • 2011-06-06
      • 2012-07-07
      • 2012-11-01
      • 2012-04-18
      • 2014-07-18
      • 2013-11-26
      • 2018-09-13
      • 2016-08-25
      相关资源
      最近更新 更多