第 2 页 dump_file 函数分析 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( 稍后介绍该变量 ) ,接着读取文件头信息,包括 magic , version , stamp ,然后 循环 读取每个 tag , length ,并通过函数指针处理该 tag ,直到文件结束 (0x00000000) 。下面介绍各种 tag 的 callback 。