diff options
Diffstat (limited to 'gcc/gcov-io.h')
-rw-r--r-- | gcc/gcov-io.h | 622 |
1 files changed, 409 insertions, 213 deletions
diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h index 29a44017fde..79686f5a848 100644 --- a/gcc/gcov-io.h +++ b/gcc/gcov-io.h @@ -1,6 +1,7 @@ -/* Machine-independent I/O routines for gcov. - Copyright (C) 1996, 1997, 1998, 2000 Free Software Foundation, Inc. +/* File format for coverage information + Copyright (C) 1996, 1997, 1998, 2000, 2002 Free Software Foundation, Inc. Contributed by Bob Manson <manson@cygnus.com>. + Completely remangled by Nathan Sidwell <nathan@codesourcery.com>. This file is part of GCC. @@ -19,279 +20,474 @@ along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* Coverage information is held in two files. A basic block graph + file, which is generated by the compiler, and a counter file, which + is generated by the program under test. Both files use a similar + structure. We do not attempt to make these files backwards + compatible with previous versions, as you only need coverage + information when developing a program. We do hold version + information, so that mismatches can be detected, and we use a + format that allows tools to skip information they do not understand + or are not interested in. + + Numbers are recorded in big endian unsigned binary form. Either in + 32 or 64 bits. Strings are stored with a length count and NUL + terminator, and 0 to 3 bytes of zero padding up to the next 4 byte + boundary. Zero length and NULL strings are simply stored as a + length of zero (they have no trailing NUL or padding). + + int32: byte3 byte2 byte1 byte0 + int64: byte7 byte6 byte5 byte4 byte3 byte2 byte1 byte0 + string: int32:0 | int32:length char* char:0 padding + padding: | char:0 | char:0 char:0 | char:0 char:0 char:0 + item: int32 | int64 | string + + The basic format of the files is + + file : int32:magic int32:version record* + + The magic ident is different for the bbg and the counter files. + The version is the same for both files and is derived from gcc's + version number. Although the ident and version are formally 32 bit + numbers, they are derived from 4 character ASCII strings. The + version number consists of the single character major version + number, a two character minor version number (leading zero for + versions less than 10), and a single character indicating the + status of the release. That will be 'e' experimental, 'p' + prerelease and 'r' for release. Because, by good fortune, these are + in alphabetical order, string collating can be used to compare + version strings, and because numbers are stored big endian, numeric + comparison can be used when it is read as a 32 bit value. Be aware + that the 'e' designation will (naturally) be unstable and might be + incompatible with itself. For gcc 3.4 experimental, it would be + '304e' (0x33303465). When the major version reaches 10, the letters + A-Z will be used. Assuming minor increments releases every 6 + months, we have to make a major increment every 50 years. Assuming + major increments releases every 5 years, we're ok for the next 155 + years -- good enough for me. + + A record has a tag, length and variable amount of data. + + record: header data + header: int32:tag int32:length + data: item* + + Records are not nested, but there is a record hierarchy. Tag + numbers reflect this hierarchy. Tags are unique across bbg and da + files. Some record types have a varying amount of data. The LENGTH + is usually used to determine how much data. The tag value is split + into 4 8-bit fields, one for each of four possible levels. The + most significant is allocated first. Unused levels are zero. + Active levels are odd-valued, so that the LSB of the level is one. + A sub-level incorporates the values of its superlevels. This + formatting allows you to determine the tag heirarchy, without + understanding the tags themselves, and is similar to the standard + section numbering used in technical documents. Level values + [1..3f] are used for common tags, values [41..9f] for the graph + file and [a1..ff] for the counter file. + + The basic block graph file contains the following records + bbg: function-graph* + function-graph: announce_function basic_blocks {arcs | lines}* + announce_function: header string:name int32:checksum + basic_block: header int32:flags* + arcs: header int32:block_no arc* + arc: int32:dest_block int32:flags + lines: header int32:block_no line* + int32:0 string:NULL + line: int32:line_no | int32:0 string:filename + + The BASIC_BLOCK record holds per-bb flags. The number of blocks + can be inferred from its data length. There is one ARCS record per + basic block. The number of arcs from a bb is implicit from the + data length. It enumerates the destination bb and per-arc flags. + There is one LINES record per basic block, it enumerates the source + lines which belong to that basic block. Source file names are + introduced by a line number of 0, following lines are from the new + source file. The initial source file for the function is NULL, but + the current source file should be remembered from one LINES record + to the next. The end of a block is indicated by an empty filename + - this does not reset the current source file. Note there is no + ordering of the ARCS and LINES records: they may be in any order, + interleaved in any manner. The current filename follows the order + the LINES records are stored in the file, *not* the ordering of the + blocks they are for. + + The data file contains the following records. + da: {function-data* summary:object summary:program*}* + function-data: announce_function arc_counts + announce_function: header string:name int32:checksum + arc_counts: header int64:count* + summary: in32:checksum int32:runs int32:arcs int64:sum int64:max \ + int64:max_sum int64:sum_max + + The ANNOUNCE_FUNCTION record is the same as that in the BBG file. + The ARC_COUNTS gives the counter values for those arcs that are + instrumented. The SUMMARY records give information about the whole + object file and about the whole program. The checksum is used for + whole program summaries, and disambiguates different programs which + include the same instrumented object file. There may be several + program summaries, each with a unique checksum. The object + summary's checkum is zero. Note that the da file might contain + information from several runs concatenated, or the data might be + merged. + + This file is included by both the compiler, gcov tools and the + library. The IN_LIBGCC2 define distinguishes these cases. When + IN_LIBGCC2 is nonzero, we're building libgcc2 for the target and + know the compiler is (the just built) gcc. Otherwise we're + generating code for the host, and the compiler may or may not be + gcc. In this latter case, you must ensure that 'gcov_type' is + typedefed to something suitable (unsigned HOST_WIDEST_INT is + usually what you want). */ + #ifndef GCC_GCOV_IO_H #define GCC_GCOV_IO_H -#include <stdio.h> -#include <sys/types.h> - -static int __fetch_long PARAMS ((long *, char *, size_t)) - ATTRIBUTE_UNUSED; -static int __read_long PARAMS ((long *, FILE *, size_t)) - ATTRIBUTE_UNUSED; -static int __write_long PARAMS ((long, FILE *, size_t)) - ATTRIBUTE_UNUSED; -static int __fetch_gcov_type PARAMS ((gcov_type *, char *, size_t)) - ATTRIBUTE_UNUSED; -static int __store_gcov_type PARAMS ((gcov_type, char *, size_t)) - ATTRIBUTE_UNUSED; -static int __read_gcov_type PARAMS ((gcov_type *, FILE *, size_t)) - ATTRIBUTE_UNUSED; -static int __write_gcov_type PARAMS ((gcov_type, FILE *, size_t)) - ATTRIBUTE_UNUSED; -static int __write_gcov_string PARAMS ((const char *, size_t, FILE*, long)) - ATTRIBUTE_UNUSED; -static int __read_gcov_string PARAMS ((char *, size_t, FILE*, long)) - ATTRIBUTE_UNUSED; - -/* These routines only work for signed values. */ - -/* Store a portable representation of VALUE in DEST using BYTES*8-1 bits. - Return a nonzero value if VALUE requires more than BYTES*8-1 bits - to store. */ -static int -__store_gcov_type (value, dest, bytes) - gcov_type value; - char *dest; - size_t bytes; +#if IN_LIBGCC2 +#if LONG_TYPE_SIZE == GCOV_TYPE_SIZE +typedef long gcov_type; +#else +typedef long long gcov_type; +#endif +#endif /* IN_LIBGCC2 */ + +/* File suffixes. */ +#define GCOV_DATA_SUFFIX ".da" +#define GCOV_GRAPH_SUFFIX ".bbg" + +/* File magic. */ +#define GCOV_DATA_MAGIC 0x67636f76 /* "gcov" */ +#define GCOV_GRAPH_MAGIC 0x67626267 /* "gbbg" */ + +/* gcov-iov.h is automatically generated by the makefile from + version.c, it looks like + #define GCOV_VERSION ((unsigned)0x89abcdef) +*/ +#include "gcov-iov.h" + +/* The record tags. Values [1..3f] are for tags which may be in either + file. Values [41..9f] for those in the bbg file and [a1..ff] for + the data file. */ + +#define GCOV_TAG_FUNCTION ((unsigned)0x01000000) +#define GCOV_TAG_BLOCKS ((unsigned)0x01410000) +#define GCOV_TAG_ARCS ((unsigned)0x01430000) +#define GCOV_TAG_LINES ((unsigned)0x01450000) +#define GCOV_TAG_ARC_COUNTS ((unsigned)0x01a10000) +#define GCOV_TAG_OBJECT_SUMMARY ((unsigned)0xa1000000) +#define GCOV_TAG_PROGRAM_SUMMARY ((unsigned)0xa3000000) +#define GCOV_TAG_PLACEHOLDER_SUMMARY ((unsigned)0xa5000000) +#define GCOV_TAG_INCORRECT_SUMMARY ((unsigned)0xa7000000) + +/* The tag level mask has 1's in the position of the inner levels, & + the lsb of the current level, and zero on the current and outer + levels. */ +#define GCOV_TAG_MASK(TAG) (((TAG) - 1) ^ (TAG)) + +/* Return nonzero if SUB is an immediate subtag of TAG. */ +#define GCOV_TAG_IS_SUBTAG(TAG,SUB) \ + (GCOV_TAG_MASK (TAG) >> 8 == GCOV_TAG_MASK (SUB) \ + && !(((SUB) ^ (TAG)) & ~GCOV_TAG_MASK(TAG))) + +/* Return nonzero if SUB is at a sublevel to TAG. */ +#define GCOV_TAG_IS_SUBLEVEL(TAG,SUB) \ + (GCOV_TAG_MASK (TAG) > GCOV_TAG_MASK (SUB)) + +/* Basic block flags. */ +#define GCOV_BLOCK_UNEXPECTED (1 << 0) + +/* Arc flags. */ +#define GCOV_ARC_ON_TREE (1 << 0) +#define GCOV_ARC_FAKE (1 << 1) +#define GCOV_ARC_FALLTHROUGH (1 << 2) + +/* Structured records. */ + +/* Object & program summary record. */ +struct gcov_summary { - int upper_bit = (value < 0 ? 128 : 0); - size_t i; - - if (value < 0) - { - gcov_type oldvalue = value; - value = -value; - if (oldvalue != -value) - return 1; - } - - for(i = 0 ; i < (sizeof (value) < bytes ? sizeof (value) : bytes) ; i++) { - dest[i] = value & (i == (bytes - 1) ? 127 : 255); - value = value / 256; - } - - if (value && value != -1) - return 1; - - for(; i < bytes ; i++) - dest[i] = 0; - dest[bytes - 1] |= upper_bit; - return 0; -} - -/* Retrieve a quantity containing BYTES*8-1 bits from SOURCE and store - the result in DEST. Returns a nonzero value if the value in SOURCE - will not fit in DEST. */ - -static int -__fetch_gcov_type (dest, source, bytes) - gcov_type *dest; - char *source; - size_t bytes; + unsigned checksum; /* checksum of program */ + unsigned runs; /* number of program runs */ + unsigned arcs; /* number of instrumented arcs */ + gcov_type arc_sum; /* sum of all arc counters */ + gcov_type arc_max_one; /* max counter on any one run */ + gcov_type arc_max_sum; /* maximum arc_sum */ + gcov_type arc_sum_max; /* sum of max_one */ +}; + +#if IN_LIBGCC2 +/* Structures embedded in coveraged program. The structures generated + by write_profile must match these. */ + +/* Information about a single function. */ +struct function_info { - gcov_type value = 0; - int i; - - for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--) - if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 )) - return 1; - - for (; i >= 0; i--) - value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255)); - - if ((source[bytes - 1] & 128) && (value > 0)) - value = - value; - - *dest = value; - return 0; -} + const char *name; /* (mangled) name of function */ + unsigned checksum; /* function checksum */ + unsigned n_arc_counts; /* number of instrumented arcs */ +}; -static int -__fetch_long (dest, source, bytes) - long *dest; - char *source; - size_t bytes; +/* Information about a single object file. */ +struct gcov_info { - long value = 0; - int i; - - for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--) - if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 )) - return 1; - - for (; i >= 0; i--) - value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255)); - - if ((source[bytes - 1] & 128) && (value > 0)) - value = - value; - - *dest = value; - return 0; -} - -/* Write a BYTES*8-bit quantity to FILE, portably. Returns a nonzero - value if the write fails, or if VALUE can't be stored in BYTES*8 - bits. - - Note that VALUE may not actually be large enough to hold BYTES*8 - bits, but BYTES characters will be written anyway. - - BYTES may be a maximum of 10. */ + unsigned long version; /* expected version number */ + struct gcov_info *next; /* link to next, used by libgcc */ + + const char *filename; /* output file name */ + long wkspc; /* libgcc workspace */ + + const struct function_info *functions; /* table of functions */ + unsigned n_functions; /* number of functions */ + + gcov_type *arc_counts; /* table of arc counts */ + unsigned n_arc_counts; /* number of arc counts */ +}; + +/* Register a new object file module. */ +extern void __gcov_init (struct gcov_info *); + +/* Called before fork, to avoid double counting. */ +extern void __gcov_flush (void); + +#endif /* IN_LIBGCC2 */ + +/* Functions for reading and writing gcov files. */ +static int gcov_write_unsigned PARAMS((FILE *, unsigned)) + ATTRIBUTE_UNUSED; +static int gcov_write_counter PARAMS((FILE *, gcov_type)) + ATTRIBUTE_UNUSED; +static int gcov_write_string PARAMS((FILE *, const char *, unsigned)) + ATTRIBUTE_UNUSED; +static int gcov_read_unsigned PARAMS((FILE *, unsigned *)) + ATTRIBUTE_UNUSED; +static int gcov_read_counter PARAMS((FILE *, gcov_type *)) + ATTRIBUTE_UNUSED; +#if !IN_LIBGCC2 +static int gcov_read_string PARAMS((FILE *, char **, unsigned *)) + ATTRIBUTE_UNUSED; +#endif +static int gcov_read_summary PARAMS ((FILE *, struct gcov_summary *)) + ATTRIBUTE_UNUSED; +#if IN_LIBGCC2 +static int gcov_write_summary PARAMS ((FILE *, unsigned, + const struct gcov_summary *)) + ATTRIBUTE_UNUSED; +#endif +#define gcov_save_position(STREAM) \ + ftell (STREAM) +#define gcov_reserve_length(STREAM) \ + (gcov_write_unsigned (STREAM, 0) ? 0 : ftell (STREAM) - 4) +static int gcov_write_length PARAMS((FILE *, long)) + ATTRIBUTE_UNUSED; +#define gcov_resync(STREAM, BASE, LENGTH) \ + fseek (STREAM, BASE + (long)LENGTH, SEEK_SET) +#define gcov_skip(STREAM, LENGTH) \ + fseek (STREAM, LENGTH, SEEK_CUR) +#define gcov_skip_string(STREAM, LENGTH) \ + fseek (STREAM, (LENGTH) + 4 - ((LENGTH) & 3), SEEK_CUR) + + +/* Write VALUE to coverage file FILE. Return nonzero if failed due to + file i/o error, or value error. */ static int -__write_gcov_type (value, file, bytes) - gcov_type value; +gcov_write_unsigned (file, value) FILE *file; - size_t bytes; + unsigned value; { - char c[10]; + char buffer[4]; + unsigned ix; - if (bytes > 10 || __store_gcov_type (value, c, bytes)) - return 1; - else - return fwrite(c, 1, bytes, file) != bytes; + for (ix = sizeof (buffer); ix--; ) + { + buffer[ix] = value; + value >>= 8; + } + return ((sizeof (value) > sizeof (buffer) && value) + || fwrite (buffer, sizeof (buffer), 1, file) != 1); } +/* Write VALUE to coverage file FILE. Return nonzero if failed due to + file i/o error, or value error. Negative values are not checked + here -- they are checked in gcov_read_counter. */ + static int -__write_long (value, file, bytes) - long value; +gcov_write_counter (file, value) FILE *file; - size_t bytes; + gcov_type value; { - char c[10]; + char buffer[8]; + unsigned ix; - if (bytes > 10 || __store_gcov_type ((gcov_type)value, c, bytes)) - return 1; - else - return fwrite(c, 1, bytes, file) != bytes; + for (ix = sizeof (buffer); ix--; ) + { + buffer[ix] = value; + value >>= 8; + } + return ((sizeof (value) > sizeof (buffer) && value != 0 && value != -1) + || fwrite (buffer, sizeof (buffer), 1, file) != 1); } -/* Read a quantity containing BYTES bytes from FILE, portably. Return - a nonzero value if the read fails or if the value will not fit - in DEST. - - Note that DEST may not be large enough to hold all of the requested - data, but the function will read BYTES characters anyway. - - BYTES may be a maximum of 10. */ +/* Write VALUE to coverage file FILE. Return nonzero if failed due to + file i/o error, or value error. */ static int -__read_gcov_type (dest, file, bytes) - gcov_type *dest; +gcov_write_string (file, string, length) FILE *file; - size_t bytes; + unsigned length; + const char *string; { - char c[10]; + unsigned pad = 0; - if (bytes > 10 || fread(c, 1, bytes, file) != bytes) - return 1; + if (string) + return (gcov_write_unsigned (file, length) + || fwrite (string, length, 1, file) != 1 + || fwrite (&pad, 4 - (length & 3), 1, file) != 1); else - return __fetch_gcov_type (dest, c, bytes); + return gcov_write_unsigned (file, 0); } +/* Read *VALUE_P from coverage file FILE. Return nonzero if failed + due to file i/o error, or range error. */ + static int -__read_long (dest, file, bytes) - long *dest; +gcov_read_unsigned (file, value_p) FILE *file; - size_t bytes; + unsigned *value_p; { - char c[10]; + unsigned value = 0; + unsigned ix; + unsigned char buffer[4]; - if (bytes > 10 || fread(c, 1, bytes, file) != bytes) + if (fread (buffer, sizeof (buffer), 1, file) != 1) return 1; - else - return __fetch_long (dest, c, bytes); + for (ix = sizeof (value); ix < sizeof (buffer); ix++) + if (buffer[ix]) + return 1; + for (ix = 0; ix != sizeof (buffer); ix++) + { + value <<= 8; + value |= buffer[ix]; + } + *value_p = value; + return 0; } - -/* Writes string in gcov format. */ +/* Read *VALUE_P from coverage file FILE. Return nonzero if failed + due to file i/o error, or range error. */ static int -__write_gcov_string (string, length, file, delim) - const char *string; - size_t length; +gcov_read_counter (file, value_p) FILE *file; - long delim; + gcov_type *value_p; { - size_t temp = length + 1; - - /* delimiter */ - if (__write_long (delim, file, 4) != 0) - return 1; - - if (__write_long (length, file, 4) != 0) - return 1; + gcov_type value = 0; + unsigned ix; + unsigned char buffer[8]; - if (fwrite (string, temp, 1, file) != 1) + if (fread (buffer, sizeof (buffer), 1, file) != 1) return 1; - - temp &= 3; - - if (temp) + for (ix = sizeof (value); ix < sizeof (buffer); ix++) + if (buffer[ix]) + return 1; + for (ix = 0; ix != sizeof (buffer); ix++) { - char c[4]; - - c[0] = c[1] = c[2] = c[3] = 0; - - if (fwrite (c, sizeof (char), 4 - temp, file) != 4 - temp) - return 1; + value <<= 8; + value |= buffer[ix]; } - if (__write_long (delim, file, 4) != 0) - return 1; - - return 0; + *value_p = value; + return value < 0; } -/* Reads string in gcov format. */ +#if !IN_LIBGCC2 +/* Read string from coverage file FILE. Length is stored in *LENGTH_P + (if non-null), a buffer is allocated and returned in *STRING_P. + Return nonzero if failed due to file i/o error, or range + error. Uses xmalloc to allocate the string buffer. */ static int -__read_gcov_string (string, max_length, file, delim) - char *string; - size_t max_length; +gcov_read_string (file, string_p, length_p) FILE *file; - long delim; + char **string_p; + unsigned *length_p; { - long delim_from_file; - long length; - long read_length; - long tmp; + unsigned length; - if (__read_long (&delim_from_file, file, 4) != 0) + if (gcov_read_unsigned (file, &length)) return 1; - if (delim_from_file != delim) - return 1; + if (length_p) + *length_p = length; + free (*string_p); - if (__read_long (&length, file, 4) != 0) - return 1; + *string_p = NULL; + if (!length) + return 0; - if (length > (long) max_length) - read_length = max_length; - else - read_length = length; + length += 4 - (length & 3); + *string_p = (char *) xmalloc (length); - tmp = (((length + 1) - 1) / 4 + 1) * 4; - /* This is the size occupied by the string in the file */ + return fread (*string_p, length, 1, file) != 1; - if (fread (string, read_length, 1, file) != 1) - return 1; +} - string[read_length] = 0; +#endif /* !IN_LIBGCC2 */ - if (fseek (file, tmp - read_length, SEEK_CUR) < 0) - return 1; - - if (__read_long (&delim_from_file, file, 4) != 0) - return 1; +/* Write a record length at PLACE. The current file position is the + end of the record, and is restored before returning. Returns + nonzero on failure. */ - if (delim_from_file != delim) - return 1; +static int +gcov_write_length (file, place) + FILE *file; + long place; +{ + long here = ftell (file); + int result = (!place || fseek (file, place, SEEK_SET) + || gcov_write_unsigned (file, here - place - 4)); + if (fseek (file, here, SEEK_SET)) + result = 1; + return result; +} - return 0; +#define GCOV_SUMMARY_LENGTH 44 +static int +gcov_read_summary (da_file, summary) + FILE *da_file; + struct gcov_summary *summary; +{ + return (gcov_read_unsigned (da_file, &summary->checksum) + || gcov_read_unsigned (da_file, &summary->runs) + || gcov_read_unsigned (da_file, &summary->arcs) + || gcov_read_counter (da_file, &summary->arc_sum) + || gcov_read_counter (da_file, &summary->arc_max_one) + || gcov_read_counter (da_file, &summary->arc_max_sum) + || gcov_read_counter (da_file, &summary->arc_sum_max)); } +#if IN_LIBGCC2 +static int +gcov_write_summary (da_file, tag, summary) + FILE *da_file; + unsigned tag; + const struct gcov_summary *summary; +{ + long base; + + return (gcov_write_unsigned (da_file, tag) + || !(base = gcov_reserve_length (da_file)) + || gcov_write_unsigned (da_file, summary->checksum) + || gcov_write_unsigned (da_file, summary->runs) + || gcov_write_unsigned (da_file, summary->arcs) + || gcov_write_counter (da_file, summary->arc_sum) + || gcov_write_counter (da_file, summary->arc_max_one) + || gcov_write_counter (da_file, summary->arc_max_sum) + || gcov_write_counter (da_file, summary->arc_sum_max) + || gcov_write_length (da_file, base)); +} +#endif -#endif /* ! GCC_GCOV_IO_H */ +#endif /* GCC_GCOV_IO_H */ |