Welcome 微信登录

首页 / 操作系统 / Linux / Linux平台代码覆盖率测试-GCC如何编译生成gcov/gcov-dump程序及其bug分析

Content0. 序 1. 编译 gcov/gcov-dump 2. 额外的话 3. gcov-dump 程序的一个 bug 3.1 bug 描述 3.2 bug 分析与修复 3.3 正确的输出 3.4 gcov-dump 的打印开关 3.5 一个问题:上面红色的 0 是什么?谁打印出来的? 4. 总结   0. 序  某些版本的 Gcc 在默认情况下编译,可能不会产生 gcov-dump 程序,或者不会安装到 /usr/bin 。但 gcov-dump 程序在做覆盖率测试时 dump 相关文件 (.gcda/.gcno) 内容时非常必要和好用。  Gcc 的编译耗时又繁琐,如果某些配置不正确,会导致编译过程中各种莫名其妙的错误。因此,本文主要讲述在不重新编译整个 Gcc 项目的情况下,如何获得 gcov-dump 程序。  本文在 Linux 平台上实验,以 gcc-4.1.2 为例,且 gcc 源代码在 /usr/src/gcc-4.1.2 目录。以下若不做特别说明, . 表示 gcc 源代码目录,即 /usr/src/gcc-4.1.2 。  1. 编译 gcov/gcov-dump  Gcov-dump.c 位于 ./gcc 目录下,因此,可以通过 ./gcc 的 makefile 文件编译生成 gcov-dump 。 gcc 目录下 configure 程序即可生成该 makefile 。  makefile 文件中的 gcov-dump 如下,由 ./gcc/build 下的 Makefile 文件中抽取出来。
exeext =  CPPLIB = ../libcpp/libcpp.aLIBIBERTY = ../libiberty/libiberty.a # Internationalization library.LIBINTL = LIBINTL_DEP =  # Character encoding conversion library.LIBICONV = LIBICONV_DEP =  LIBS =  $(CPPLIB ) $(LIBINTL) $(LIBICONV) $(LIBIBERTY ) gcov.o: gcov.c gcov-io.c $(GCOV_IO_H) intl.h $(SYSTEM_H) coretypes.h $(TM_H)    $(CONFIG_H) version.hgcov-dump.o: gcov-dump.c gcov-io.c $(GCOV_IO_H) $(SYSTEM_H) coretypes.h    $(TM_H) $(CONFIG_H) GCOV_OBJS = gcov.o intl.o version.o errors.ogcov$(exeext): $(GCOV_OBJS) $(LIBDEPS)$(CC) $(ALL_CFLAGS) $(LDFLAGS) $(GCOV_OBJS) $(LIBS ) -o $@ GCOV_DUMP_OBJS = gcov-dump.o version.o errors.ogcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS)$(CC) $(ALL_CFLAGS) $(LDFLAGS) $(GCOV_DUMP_OBJS) $(LIBS ) -o $@
至于其他的定义,非本文重点,不予解释。
# cd /usr/src/ gcc-4.1.2/gcc/build # make clean# make gcov-dump...make: *** No rule to make target `../build-i686-pc-linux-gnu/libiberty/libiberty.a", needed by `build/genmodes".  Stop. # make gcov...make: *** No rule to make target `../build-i686-pc-linux-gnu/libiberty/libiberty.a", needed by `build/genmodes".  Stop.
程序提示在 ../build-i686-pc-linux-gnu/libiberty 目录下没有找到 genmodes 所需的静态库 libiberty.a 。  从 makefile 文件中也了解到, gcov/gcov-dump 实际所需的静态库是 libcpp.a 和 libiberty.a 。实际上,只需在 gcc 目录下的 makefile 文件中指定好这两个静态库的路径 ( 绝对路径和相对路径均可 ) 即可解决问题。例如: /usr/bin/libiberty.a 。  2. 额外的话  不得不指出的一点:事实上,对 libcpp.a 的依赖几乎为 0 ,且对 libiberty.a 的依赖也仅限于以下函数。
FILE *fopen_unlocked (const char *, const char *);void unlock_std_streams (void);
这两个函数的声明在 ./include/libiberty.h 中。  因此,了解这些之后,就可以将 gcov/gcov-dump 相关的文件抽取出来,单独成为一个独立的项目,来编译出 gcov/gcov-dump ,以方便对 gcov/gcov-dump 源代码和原理的学习、调试。 ——将另文讨论。 3. gcov-dump 程序的一个 bug  3.1 bug 描述  用上述生成的 gcov-dump 程序 dump 出某个 .gcda 文件的内容,如下。  
# cd /home/zubo/gcc/test# /usr/src/ gcc-4.1.2/gcc/build/gcov-dump test.gcda test.gcda:data:magic `gcda":version `401p"test.gcda:stamp 1427241144test.gcda: 01000000:   2:FUNCTION ident=3, checksum=0xeb65a768test.gcda:  01a10000:  10:COUNTERS arcs 5 countstest.gcda:              0 10 0 1 0 1test.gcda: a1000000:   9:OBJECT_SUMMARY checksum=0x00000000test.gcda:              counts=5, runs=1, sum_all=12, run_max=10, sum_max=10test.gcda:              counts=1, runs=1, sum_all=577750259318514008 , run_max=-6845471430389142944 , sum_max=577764773093965833 test.gcda:              counts=3214008580, runs=134522083, sum_all=577765013746656483, run_max=-1208884056, sum_max=29051165790196test.gcda:              counts=134527488, runs=1882271796, sum_all=-6845471431969184921, run_max=577765507560701952, sum_max=4429488512test.gcda:              counts=1734567009, runs=875573616, sum_all=4429489327, run_max=91621554360, sum_max=-6845471433603153901test.gcda: a3000000:   9:PROGRAM_SUMMARY checksum=0x51924f98test.gcda:              counts=5, runs=1, sum_all=12, run_max=10, sum_max=10test.gcda:              counts=1, runs=1, sum_all=577750259318514008, run_max=-6701356242313287072, sum_max=577764837518475273test.gcda:              counts=3214008580, runs=134522083, sum_all=577765013746656483, run_max=-1208884056, sum_max=29051165790196test.gcda:              counts=134527488, runs=1882271796, sum_all=-6701356243893329049, run_max=577765507560701952, sum_max=4429488512test.gcda:              counts=1734567009, runs=875573616, sum_all=4429489327, run_max=138866194616, sum_max=-6701356245527298018
dump 出的数据怎么会这么大?会不会有什么问题?使用 od 命令 dump 该文件的内容,看看该文件里到底有没有这些庞大的数据。如下。数据分析可以参考 "Linux 平台代码覆盖率测试工具 GCOV 相关文件分析 " 一文。
# od -t x4 -w16 test.gcda0000000 67636461 34303170 5511f8b8 01000000   //"gcda", "401p", timestamp, tag=0x 01000000 0000020 00000002 00000003 eb65a768 01a10000   //FUNCTION, 0x 01a10000 0000040 0000000a 0000000a 00000000 00000000   //length=0xa=10, counter content: 0xa, 0, 1, 0, 1 0000060 00000000 00000001 00000000 00000000   //8 Bytes for each counter 0000100 00000000 00000001 00000000 a1000000 0000120 00000009 00000000 00000005 00000001   //length=9, checksum=0, counts=5, runs=1 0000140 0000000c 00000000 0000000a 00000000   //sum_all=0xc=12(8 Bytes), run_max=0xa=10(8 Bytes) 0000160 0000000a 00000000 a3000000 00000009   //sum_max=0xa=10 (8 Bytes), tag= a3000000 , length=9 0000200 51924f98 00000005 00000001 0000000c   //same as above 0000220 00000000 0000000a 00000000 0000000a 0000240 00000000 000000000000250
很显然,该文件里并没有这些庞大的数据,也就证实了我们的猜测。基本可以确定, gcov-dump 有 bug 。  3.2 bug 分析与修复  如何分析,自然想到了 gdb 。有问题的数据貌似在 dump Object Summary 和 Program Summary 时出现的。那么在 tag_summary () 函数中设置断点是很自然的事。经一番调试后,发现问题就在 tag_summary() 函数里。如下。
static voidtag_summary (const char *filename ATTRIBUTE_UNUSED,     unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED){  struct gcov_summary summary;  unsigned ix;   unsigned count = gcov_read_summary (&summary); /* 还应该修改该函数的声明和定义 */   printf (" checksum=0x%08x", summary.checksum);   / * for (ix = 0; ix != GCOV_COUNTERS ; ix++) */  /* 原来的代码 */   for (ix = 0; ix < count ; ix++)                  /* 应该如此修改 */     {      printf (" ");      print_prefix (filename, 0, 0);      printf (" counts=%u, runs=%u",      summary.ctrs[ix].num, summary.ctrs[ix].runs);       printf (", sum_all=" HOST_WIDEST_INT_PRINT_DEC ,      (HOST_WIDEST_INT )summary.ctrs[ix].sum_all);      printf (", run_max=" HOST_WIDEST_INT_PRINT_DEC ,      (HOST_WIDEST_INT )summary.ctrs[ix].run_max);      printf (", sum_max=" HOST_WIDEST_INT_PRINT_DEC ,      (HOST_WIDEST_INT )summary.ctrs[ix].sum_max);    }}
其中, GCOV_COUNTERS 的定义如下,其值为 5 ,故每次打印均打印出 5 个 summary 的内容。但实际应该是按照 summary(Object 或者 program) 的个数来打印信息。 <!-- [if gte mso 9]> Zubo.Yu12.00<![endif]--><!-- [if gte mso 9]> Print0 7.8 pt02 falsefalsefalse EN-USZH-CNX-NONEMicrosoftInternetExplorer4 <![endif]--><!-- [if gte mso 9]><![endif]--><!-- [if gte mso 10]> <!-- /* Style Definitions */ table.MsoNormalTable {mso-style-name:"Table Normal"; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-qformat:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.5pt; mso-bidi-font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-font-kerning:1.0pt;} --><!-- [endif]---->
/ * Counters that are collected. * / #define GCOV_COUNTER_ARCS 0 /   * Arc transitions. * / #define GCOV_COUNTERS_SUMMABLE     1 /   * Counters which can be summaried. * / #define GCOV_FIRS T_VALUE_COUNTER 1   /   * The fi rst of counters used for val ue profiling. They must form a consecutive i nterval and thei r order must match the order of HIST_TYPEs i n value- prof.h. * / #define GCOV_COUNTER_V_INTERVAL     1 /   * Hi stogram of val ue i nsi de an i nterval . */ #define GCOV_COUNTER_V_P OW2    2 /   * Hi stogram of exact power2 l ogari thm of a val ue. */ #define GCOV_COUNTER_V_S INGLE 3 /   * The most common value of expression. * / #define GCOV_COUNTER_V_DELTA   4 /   * The most common difference between consecutive values of expression. * / #define GCOV_LAS T_VALUE_COUNTER 4 /   * The last of counters used for value profiling. * / #define GCOV_COUNTERS           5
输出的信息,如,counts=5, runs=1, sum_all=12, run_max=10, sum_max=10即为 gcov_ctr_summary 结构,其定义如下。在 ./gcc/gcov_io.h 文件中。<!-- [if gte mso 9]> Zubo.Yu12.00<![endif]--><!-- [if gte mso 9]> Print0 7.8 磅02 falsefalsefalse EN-USZH-CNX-NONEMicrosoftInternetExplorer4 <![endif]--><!-- [if gte mso 9]><![endif]--><!-- [if gte mso 10]> <!-- /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-qformat:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.5pt; mso-bidi-font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi; mso-font-kerning:1.0pt;} --><!-- [endif]----><!-- [if gte mso 9]> Zubo.Yu12.00<![endif]--><!-- [if gte mso 9]> Print0 7.8 磅02 falsefalsefalse EN-USZH-CNX-NONEMicrosoftInternetExplorer4 <![endif]--><!-- [if gte mso 9]><![endif]--><!-- [if gte mso 10]> <!-- /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-qformat:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.5pt; mso-bidi-font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi; mso-font-kerning:1.0pt;} --><!-- [endif]---->
/ * Cumulative counter data. */ struct   gcov_ctr _su mmar y {     gcov_unsigned_t num ;     / * number of   ounters. */     gcov_unsigned_t runs ;     / * number of program runs */     gcov_type sum_all ;        / * sum of all counters accumulated. * /     gcov_type run_max ;       / * maximum value on a single run. * /     gcov_type sum_max ;       / * sum of i ndi vi dual run max val ues. */ };
/ * Object & program summary record. */ struct   gcov_su mmar y {     gcov_unsigned_t checksum ;     / * checksum of program */      struct gcov_ctr_summary ctrs [ GCOV_COUNTERS_SUMMABLE ]; };
GCOV_COUNTERS_SUMMABLE =1 ,因此, gcov_summary 结构中的 ctrs 数组,实际上是一个指针而已,当 summary 有多个时 ( 不超过 5 个 ) ,应该为其分配空间。