Linux平台代码覆盖率测试-gcov-dump原理分析


Content

1.

2. gcov-dump原理分析

2.1 gcov-dump程序结构

2.2 dump_file函数分析

2.3 处理各种tagcallback定义

2.4 基本读取函数gcov_read_words

2.5 分配空间函数gcov_allocate

2.6 重要数据结构gcov_var

3. 处理tagcallback分析

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,即gcovdata文件;或者.gcno,即gcovnote文件。

 

有了"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 void

dump_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\n", 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\n", 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\n", 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'\n", filename, e);

        }

    }

 

    /* stamp */

    {

        unsigned stamp = gcov_read_unsigned ();

        printf ("%s:stamp %lu\n", 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\n", 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\n",

                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 ("\n");

        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\n",

                    filename, actual_length - length);

            else if (length > actual_length)

                printf ("%s:record size mismatch %lu bytes unread\n",

                    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\n" :

                "%s:read error at %lu\n", filename,

                (long unsigned) gcov_position ());

            break;

        }

    }

    gcov_close ();

}

dump_file函数首先通过gcov_open打开.gcda/.gcno文件,将文件信息保存到全局变量gcov_var(稍后介绍该变量),接着读取文件头信息,包括magicversionstamp,然后循环读取每个taglength,并通过函数指针处理该tag,直到文件结束(0x00000000)。下面介绍各种tagcallback

 

2.3 处理各种tagcallback定义

 

处理tagcallback函数定义如下。

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;

  • 1
  • 2
  • 3
  • 下一页

相关内容