Welcome 微信登录

首页 / 操作系统 / Linux / Linux平台代码覆盖率测试-gcov-dump原理分析

Content1. 序2. gcov-dump原理分析2.1 gcov-dump程序结构2.2 dump_file函数分析2.3 处理各种tag的callback定义2.4 基本读取函数gcov_read_words2.5 分配空间函数gcov_allocate2.6 重要数据结构gcov_var3. 处理tag的callback分析3.1 FUNCTION tag: tag_function()函数3.2 BLOCKS tag: tag_blocks()函数3.3 ARCS tag: tag_arcs()函数3.4 LINES tag: tag_lines()函数3.5 COUNTER tag: tag_counters()函数3.6 OBJECT/PROGRAM SUMMARY tag: tag_summary()函数4. 小结  1. 序 gcov的相关文件.gcda(data文件)/.gcno(note文件)文件是以二进制方式写入的(fwrite),普通编辑文件打开看到的只是乱码,用ultraedit打开也只是看到十六进制的数据。如果你了解.gcda/.gcno的文件格式(可以参考"Linux平台代码覆盖率测试工具GCOV相关文件分析"),看起来会好些;否则,看起来便不知所云,除非有一种工具或程序能将其内容按照有意义的(文件)格式dump出来,如果再加上一些提示,就更好了。 ——这就是gcov-dump程序。 gcov-dump是一个dump程序,输入是一个gcov的文件,或者.gcda,即gcov的data文件;或者.gcno,即gcov的note文件。 有了"Linux平台代码覆盖率测试工具GCOV相关文件分析"和"Linux平台代码覆盖率测试-GCC如何编译生成gcov/gcov-dump程序及其bug分析"这两篇文章做基础,gcov-dump的原理就很好理解了。本文不予详细叙述,只做一些代码注释和简单记录,便于用到的时候查询。好头脑赶不上烂笔头嘛。 本文例子所用的gcov-dump程序来自"Linux平台代码覆盖率测试-从GCC源码中抽取gcov/gcov-dump程序"一文。 2. gcov-dump原理分析 2.1 gcov-dump程序结构


图中实线表示调用,实线旁边的数字表示tag值。tag的值请参考gcov_io.h文件,或者"Linux平台代码覆盖率测试工具GCOV相关文件分析"。 2.2 dump_file函数分析 gcov-dump程序的主函数main,是靠调用dump_file()函数来完成文件内容的输出。该函数定义如下。其中的注释为笔者所加。
static voiddump_file (const char *filename){    unsigned tags[4];    unsigned depth = 0;     if (! gcov_open (filename, 1))  /* it will open .gcda/.gcno file, and save information into gcov_var */    {        fprintf (stderr, "%s:cannot open ", filename);        return;    }     /* magic */    {        unsigned magic = gcov_read_unsigned ();        unsigned version;        const char *type = NULL;        int endianness = 0;        char m[4], v[4];         /***** compare magic read just now with "gcda" or "gcno" to confirm file type */        if ((endianness = gcov_magic (magic, GCOV_DATA_MAGIC)))            type = "data";        else if ((endianness = gcov_magic (magic, GCOV_NOTE_MAGIC)))            type = "note";        else        {            printf ("%s:not a gcov file ", filename);            gcov_close ();            return;        }        /***** read version, an unsigned word */        version = gcov_read_unsigned ();         /***** Convert a magic or version number to a 4 character string with ASCII */        GCOV_UNSIGNED2STRING (v, version);        GCOV_UNSIGNED2STRING (m, magic);        printf ("%s:%s:magic `%.4s":version `%.4s"%s ", filename, type,                m, v, endianness < 0 ? " (swapped endianness)" : "");        if (version != GCOV_VERSION)        {            char e[4];            GCOV_UNSIGNED2STRING (e, GCOV_VERSION);            printf ("%s:warning:current version is `%.4s" ", filename, e);        }    }     /* stamp */    {        unsigned stamp = gcov_read_unsigned ();        printf ("%s:stamp %lu ", filename, (unsigned long)stamp);    }     while (1)    {        gcov_position_t base, position = gcov_position ();        unsigned tag, length; tag_format_t const *format; unsigned tag_depth;        int error;        unsigned mask;         /***** read a tag, for example, 0x01000000, 0x01a10000, 0xa1000000, etc */        tag = gcov_read_unsigned ();        if (! tag) /***** tag=0x00000000, then, to the end of file, break *****/            break;         /***** read its length tag */        length = gcov_read_unsigned ();        base = gcov_position ();         /***** for example, tag=0x01000000, then, tag- 1=0xFFFFFF,          * then, GCOV_TAG_MASK (tag)=0x1FFFFFF, then, mask = 0x1FFFFFF/ 2 = 0xFFFFFF        */        mask = GCOV_TAG_MASK (tag) >> 1;         /****** validate the tag */        for (tag_depth = 4; mask; mask >>= 8)        {            if ((mask & 0xff) != 0xff)            {                printf ("%s:tag `%08x" is invalid ", filename, tag);                break;            }            tag_depth-- ;        }         /***** find the tag in tag_table, if found, then call its procedure */        for (format = tag_table; format- >name; format++)            if (format- >tag == tag)                goto found;        format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2: 1];found:        ;        if (tag)        {            if (depth && depth < tag_depth)            {                if (! GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag))                printf ("%s:tag `%08x" is incorrectly nested ",                filename, tag);            }            depth = tag_depth;            tags[depth - 1] = tag;        }         /***** print some spaces to represent the depth level */        print_prefix (filename, tag_depth, position);        printf ("%08x:%4u:%s", tag, length, format- >name);        /***** call the procedure of this tag stored in tag_table */        if (format- >proc)            (*format- >proc) (filename, tag, length);  //此处调用相应的tag处理函数          printf (" ");        if (flag_dump_contents && format- >proc)        {            unsigned long actual_length = gcov_position () - base;            if (actual_length > length)                printf ("%s:record size mismatch %lu bytes overread ",                     filename, actual_length - length);            else if (length > actual_length)                printf ("%s:record size mismatch %lu bytes unread ",                     filename, length - actual_length);        }         /***** base stands for the base position of a tag, then, synchronize the pointer */        gcov_sync (base, length);        if ((error = gcov_is_error ()))        {            printf (error < 0 ? "%s:counter overflow at %lu " :                 "%s:read error at %lu ", filename,                 (long unsigned) gcov_position ());            break;        }    }    gcov_close ();}
dump_file函数首先通过gcov_open打开.gcda/.gcno文件,将文件信息保存到全局变量gcov_var(稍后介绍该变量),接着读取文件头信息,包括magic,version,stamp,然后循环读取每个tag,length,并通过函数指针处理该tag,直到文件结束(0x00000000)。下面介绍各种tag的callback。 2.3 处理各种tag的callback定义 处理tag的callback函数定义如下。
static const tag_format_t tag_table[] ={    {0,"NOP", NULL},    {0,"UNKNOWN", NULL},    {0,"COUNTERS", tag_counters},    {GCOV_TAG_FUNCTION, "FUNCTION", tag_function},    {GCOV_TAG_BLOCKS,   "BLOCKS",   tag_blocks},    {GCOV_TAG_ARCS,     "ARCS",     tag_arcs},    {GCOV_TAG_LINES,    "LINES",    tag_lines},    {GCOV_TAG_OBJECT_SUMMARY,  "OBJECT_SUMMARY",  tag_summary},    {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},    {0, NULL, NULL}};
其类型tag_format_t为一个结构,分别由tag本身,tag name和处理该tag的函数指针组成,定义如下。
typedef struct tag_format{    unsigned    tag;    char const *name;    void (*proc) (const char *, unsigned, unsigned);} tag_format_t;