812 lines
23 KiB
C
812 lines
23 KiB
C
/* 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.
|
|
|
|
GCC is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free
|
|
Software Foundation; either version 2, or (at your option) any later
|
|
version.
|
|
|
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
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
|
|
string:source int32:lineno
|
|
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,
|
|
but without the source location.
|
|
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
|
|
runtime support library libgcov. IN_LIBGCOV and IN_GCOV are used to
|
|
distinguish which case is which. If IN_LIBGCOV is non-zero,
|
|
libgcov is being built. If IN_GCOV is non-zero, the gcov tools are
|
|
being built. Otherwise the compiler is being built. IN_GCOV may be
|
|
positive or negative. If positive, we are compiling a tool that
|
|
requires additional functions (see the code for knowledge of what
|
|
those functions are). */
|
|
|
|
#ifndef GCC_GCOV_IO_H
|
|
#define GCC_GCOV_IO_H
|
|
|
|
#if IN_LIBGCOV
|
|
#if LONG_TYPE_SIZE == GCOV_TYPE_SIZE
|
|
typedef long gcov_type;
|
|
#else
|
|
typedef long long gcov_type;
|
|
#endif
|
|
#endif /* IN_LIBGCOV */
|
|
#if IN_GCOV
|
|
typedef HOST_WIDEST_INT gcov_type;
|
|
#if IN_GCOV > 0
|
|
#include <sys/types.h>
|
|
#endif
|
|
#endif
|
|
|
|
/* 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 << 1)
|
|
|
|
/* 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
|
|
{
|
|
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 */
|
|
};
|
|
|
|
/* Structures embedded in coveraged program. The structures generated
|
|
by write_profile must match these. */
|
|
|
|
/* Information about section of counters for a function. */
|
|
struct counter_section
|
|
{
|
|
unsigned tag; /* Tag of the section. */
|
|
unsigned n_counters; /* Number of counters in the section. */
|
|
};
|
|
|
|
#if IN_LIBGCOV
|
|
/* Information about section of counters for an object file. */
|
|
struct counter_section_data
|
|
{
|
|
unsigned tag; /* Tag of the section. */
|
|
unsigned n_counters; /* Number of counters in the section. */
|
|
gcov_type *counters; /* The data. */
|
|
};
|
|
|
|
/* Information about a single function. */
|
|
struct function_info
|
|
{
|
|
const char *name; /* (mangled) name of function */
|
|
unsigned checksum; /* function checksum */
|
|
unsigned n_counter_sections; /* Number of types of counters */
|
|
const struct counter_section *counter_sections;
|
|
/* The section descriptions */
|
|
};
|
|
|
|
/* Information about a single object file. */
|
|
struct gcov_info
|
|
{
|
|
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 */
|
|
|
|
unsigned n_functions; /* number of functions */
|
|
const struct function_info *functions; /* table of functions */
|
|
|
|
unsigned n_counter_sections; /* Number of types of counters */
|
|
const struct counter_section_data *counter_sections;
|
|
/* The data to be put into the sections. */
|
|
};
|
|
|
|
/* 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);
|
|
|
|
/* Since this file is used in both host and target files, and we don't
|
|
include ansidecl.h in target files, provide some necessary macros. */
|
|
#ifndef ATTRIBUTE_UNUSED
|
|
# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
|
|
#endif
|
|
|
|
#endif /* IN_LIBGCOV */
|
|
|
|
/* Because small reads and writes, interspersed with seeks cause lots
|
|
of disk activity, we buffer the entire count files. */
|
|
|
|
static FILE *gcov_file;
|
|
static size_t gcov_position;
|
|
static size_t gcov_length;
|
|
static unsigned char *gcov_buffer;
|
|
static size_t gcov_alloc;
|
|
static int gcov_modified;
|
|
static int gcov_errored = 1;
|
|
|
|
/* Functions for reading and writing gcov files. */
|
|
static int gcov_open (const char */*name*/, int /*truncate*/);
|
|
static int gcov_close (void);
|
|
#if !IN_GCOV
|
|
static unsigned char *gcov_write_bytes (unsigned);
|
|
static int gcov_write_unsigned (unsigned);
|
|
#if IN_LIBGCOV
|
|
static int gcov_write_counter (gcov_type);
|
|
#endif
|
|
static int gcov_write_string (const char *);
|
|
static unsigned long gcov_reserve_length (void);
|
|
static int gcov_write_length (unsigned long /*position*/);
|
|
#if IN_LIBGCOV
|
|
static int gcov_write_summary (unsigned, const struct gcov_summary *);
|
|
#endif
|
|
#endif /* !IN_GCOV */
|
|
static const unsigned char *gcov_read_bytes (unsigned);
|
|
static int gcov_read_unsigned (unsigned *);
|
|
static int gcov_read_counter (gcov_type *);
|
|
#if !IN_LIBGCOV
|
|
static int gcov_read_string (char **);
|
|
#endif
|
|
static int gcov_read_summary (struct gcov_summary *);
|
|
static unsigned long gcov_save_position (void);
|
|
static int gcov_resync (unsigned long /*base*/, unsigned /*length */);
|
|
static unsigned long gcov_seek_end (void);
|
|
static int gcov_skip (unsigned /*length*/);
|
|
static int gcov_skip_string (unsigned /*length*/);
|
|
static int gcov_ok (void);
|
|
static int gcov_error (void);
|
|
static int gcov_eof (void);
|
|
#if IN_GCOV > 0
|
|
static time_t gcov_time (void);
|
|
#endif
|
|
|
|
/* Open a gcov file. NAME is the name of the file to open and MODE
|
|
indicates whether a new file should be created, or an existing file
|
|
opened for modification. If MODE is >= 0 an existing file will be
|
|
opened, if possible, and if MODE is <= 0, a new file will be
|
|
created. Use MODE=0 to attempt to reopen an existing file and then
|
|
fall back on creating a new one. Return zero on failure, >0 on
|
|
opening an existing file and <0 on creating a new one. */
|
|
|
|
static int
|
|
gcov_open (const char *name, int mode)
|
|
{
|
|
int result = 1;
|
|
size_t alloc = 1024;
|
|
#if defined (TARGET_HAS_F_SETLKW) && IN_LIBGCOV
|
|
struct flock s_flock;
|
|
|
|
s_flock.l_type = F_WRLCK;
|
|
s_flock.l_whence = SEEK_SET;
|
|
s_flock.l_start = 0;
|
|
s_flock.l_len = 0; /* Until EOF. */
|
|
s_flock.l_pid = getpid ();
|
|
#endif
|
|
|
|
if (gcov_file)
|
|
abort ();
|
|
gcov_position = gcov_length = 0;
|
|
gcov_errored = gcov_modified = 0;
|
|
if (mode >= 0)
|
|
gcov_file = fopen (name, "r+b");
|
|
if (!gcov_file && mode <= 0)
|
|
{
|
|
result = -1;
|
|
gcov_file = fopen (name, "w+b");
|
|
}
|
|
if (!gcov_file)
|
|
return 0;
|
|
|
|
#if defined (TARGET_HAS_F_SETLKW) && IN_LIBGCOV
|
|
while (fcntl (fileno (gcov_file), F_SETLKW, &s_flock)
|
|
&& errno == EINTR)
|
|
continue;
|
|
#endif
|
|
|
|
if (result >= 0)
|
|
{
|
|
if (fseek (gcov_file, 0, SEEK_END))
|
|
{
|
|
fclose (gcov_file);
|
|
gcov_file = 0;
|
|
return 0;
|
|
}
|
|
gcov_length = ftell (gcov_file);
|
|
fseek (gcov_file, 0, SEEK_SET);
|
|
alloc += gcov_length;
|
|
}
|
|
if (alloc > gcov_alloc)
|
|
{
|
|
if (gcov_buffer)
|
|
free (gcov_buffer);
|
|
gcov_alloc = alloc;
|
|
#if IN_LIBGCOV
|
|
gcov_buffer = malloc (gcov_alloc);
|
|
if (!gcov_buffer)
|
|
{
|
|
fclose (gcov_file);
|
|
gcov_file = 0;
|
|
gcov_length = 0;
|
|
gcov_alloc = 0;
|
|
return 0;
|
|
}
|
|
#else
|
|
gcov_buffer = xmalloc (gcov_alloc);
|
|
#endif
|
|
}
|
|
if (result >= 0 && fread (gcov_buffer, gcov_length, 1, gcov_file) != 1)
|
|
{
|
|
fclose (gcov_file);
|
|
gcov_file = 0;
|
|
gcov_length = 0;
|
|
return 0;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* Close the current gcov file. Flushes data to disk. Returns nonzero
|
|
on failure or error flag set. */
|
|
|
|
static int
|
|
gcov_close ()
|
|
{
|
|
int result = 0;
|
|
|
|
if (gcov_file)
|
|
{
|
|
if (gcov_modified
|
|
&& (fseek (gcov_file, 0, SEEK_SET)
|
|
|| fwrite (gcov_buffer, gcov_length, 1, gcov_file) != 1))
|
|
result = -1;
|
|
fclose (gcov_file);
|
|
gcov_file = 0;
|
|
gcov_length = 0;
|
|
}
|
|
return result || gcov_errored;
|
|
}
|
|
|
|
#if !IN_GCOV
|
|
/* Allocate space to write BYTES bytes to the gcov file. Return a
|
|
pointer to those bytes, or NULL on failure. */
|
|
|
|
static unsigned char *
|
|
gcov_write_bytes (unsigned bytes)
|
|
{
|
|
char unsigned *result;
|
|
|
|
if (gcov_position + bytes > gcov_alloc)
|
|
{
|
|
size_t new_size = (gcov_alloc + bytes) * 3 / 2;
|
|
|
|
if (!gcov_buffer)
|
|
return 0;
|
|
#if IN_LIBGCOV
|
|
result = realloc (gcov_buffer, new_size);
|
|
if (!result)
|
|
{
|
|
free (gcov_buffer);
|
|
gcov_buffer = 0;
|
|
gcov_alloc = 0;
|
|
gcov_position = gcov_length = 0;
|
|
return 0;
|
|
}
|
|
#else
|
|
result = xrealloc (gcov_buffer, new_size);
|
|
#endif
|
|
gcov_alloc = new_size;
|
|
gcov_buffer = result;
|
|
}
|
|
|
|
result = &gcov_buffer[gcov_position];
|
|
gcov_position += bytes;
|
|
gcov_modified = 1;
|
|
if (gcov_position > gcov_length)
|
|
gcov_length = gcov_position;
|
|
return result;
|
|
}
|
|
|
|
/* Write VALUE to coverage file. Return nonzero if failed due to
|
|
file i/o error, or value error. */
|
|
|
|
static int
|
|
gcov_write_unsigned (unsigned value)
|
|
{
|
|
unsigned char *buffer = gcov_write_bytes (4);
|
|
unsigned ix;
|
|
|
|
if (!buffer)
|
|
return 1;
|
|
|
|
for (ix = 4; ix--; )
|
|
{
|
|
buffer[ix] = value;
|
|
value >>= 8;
|
|
}
|
|
return sizeof (value) > 4 && value;
|
|
}
|
|
|
|
/* Write VALUE to coverage 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. */
|
|
|
|
#if IN_LIBGCOV
|
|
static int
|
|
gcov_write_counter (gcov_type value)
|
|
{
|
|
unsigned char *buffer = gcov_write_bytes (8);
|
|
unsigned ix;
|
|
|
|
if (!buffer)
|
|
return 1;
|
|
|
|
for (ix = 8; ix--; )
|
|
{
|
|
buffer[ix] = value;
|
|
value >>= 8;
|
|
}
|
|
return sizeof (value) > 8 && value;
|
|
}
|
|
#endif /* IN_LIBGCOV */
|
|
|
|
/* Write VALUE to coverage file. Return nonzero if failed due to
|
|
file i/o error, or value error. */
|
|
|
|
static int
|
|
gcov_write_string (const char *string)
|
|
{
|
|
if (string)
|
|
{
|
|
unsigned length = strlen (string);
|
|
unsigned pad = 0;
|
|
unsigned rem = 4 - (length & 3);
|
|
unsigned char *buffer;
|
|
|
|
if (gcov_write_unsigned (length))
|
|
return 1;
|
|
buffer = gcov_write_bytes (length + rem);
|
|
if (!buffer)
|
|
return 1;
|
|
memcpy (buffer, string, length);
|
|
memcpy (buffer + length, &pad, rem);
|
|
return 0;
|
|
}
|
|
else
|
|
return gcov_write_unsigned (0);
|
|
}
|
|
|
|
/* Allocate space to write a record tag length. Return a value to be
|
|
used for gcov_write_length. */
|
|
|
|
static unsigned long
|
|
gcov_reserve_length (void)
|
|
{
|
|
unsigned long result = gcov_position;
|
|
unsigned char *buffer = gcov_write_bytes (4);
|
|
|
|
if (!buffer)
|
|
return 0;
|
|
memset (buffer, 0, 4);
|
|
return result;
|
|
}
|
|
|
|
/* 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. */
|
|
|
|
static int
|
|
gcov_write_length (unsigned long position)
|
|
{
|
|
unsigned length = gcov_position - position - 4;
|
|
unsigned char *buffer = &gcov_buffer[position];
|
|
unsigned ix;
|
|
|
|
if (!position)
|
|
return 1;
|
|
for (ix = 4; ix--; )
|
|
{
|
|
buffer[ix] = length;
|
|
length >>= 8;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if IN_LIBGCOV
|
|
/* Write a summary structure to the gcov file. */
|
|
|
|
static int
|
|
gcov_write_summary (unsigned tag, const struct gcov_summary *summary)
|
|
{
|
|
volatile unsigned long base; /* volatile is necessary to work around
|
|
a compiler bug. */
|
|
|
|
if (gcov_write_unsigned (tag))
|
|
return 1;
|
|
base = gcov_reserve_length ();
|
|
if (gcov_write_unsigned (summary->checksum))
|
|
return 1;
|
|
if (gcov_write_unsigned (summary->runs)
|
|
|| gcov_write_unsigned (summary->arcs))
|
|
return 1;
|
|
if (gcov_write_counter (summary->arc_sum)
|
|
|| gcov_write_counter (summary->arc_max_one)
|
|
|| gcov_write_counter (summary->arc_max_sum)
|
|
|| gcov_write_counter (summary->arc_sum_max))
|
|
return 1;
|
|
if (gcov_write_length (base))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
#endif /* IN_LIBGCOV */
|
|
|
|
#endif /*!IN_GCOV */
|
|
|
|
/* Return a pointer to read BYTES bytes from the gcov file. Returns
|
|
NULL on failure (read past EOF). */
|
|
|
|
static const unsigned char *
|
|
gcov_read_bytes (unsigned bytes)
|
|
{
|
|
const unsigned char *result;
|
|
|
|
if (gcov_position + bytes > gcov_length)
|
|
return 0;
|
|
result = &gcov_buffer[gcov_position];
|
|
gcov_position += bytes;
|
|
return result;
|
|
}
|
|
|
|
/* Read *VALUE_P from coverage file. Return nonzero if failed
|
|
due to file i/o error, or range error. */
|
|
|
|
static int
|
|
gcov_read_unsigned (unsigned *value_p)
|
|
{
|
|
unsigned value = 0;
|
|
unsigned ix;
|
|
const unsigned char *buffer = gcov_read_bytes (4);
|
|
|
|
if (!buffer)
|
|
return 1;
|
|
for (ix = sizeof (value); ix < 4; ix++)
|
|
if (buffer[ix])
|
|
return 1;
|
|
for (ix = 0; ix != 4; ix++)
|
|
{
|
|
value <<= 8;
|
|
value |= buffer[ix];
|
|
}
|
|
*value_p = value;
|
|
return 0;
|
|
}
|
|
|
|
/* Read *VALUE_P from coverage file. Return nonzero if failed
|
|
due to file i/o error, or range error. */
|
|
|
|
static int
|
|
gcov_read_counter (gcov_type *value_p)
|
|
{
|
|
gcov_type value = 0;
|
|
unsigned ix;
|
|
const unsigned char *buffer = gcov_read_bytes (8);
|
|
|
|
if (!buffer)
|
|
return 1;
|
|
for (ix = sizeof (value); ix < 8; ix++)
|
|
if (buffer[ix])
|
|
return 1;
|
|
for (ix = 0; ix != 8; ix++)
|
|
{
|
|
value <<= 8;
|
|
value |= buffer[ix];
|
|
}
|
|
|
|
*value_p = value;
|
|
return value < 0;
|
|
}
|
|
|
|
#if !IN_LIBGCOV
|
|
|
|
/* Read string from coverage file. 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
|
|
gcov_read_string (char **string_p)
|
|
{
|
|
unsigned length;
|
|
const unsigned char *buffer;
|
|
|
|
if (gcov_read_unsigned (&length))
|
|
return 1;
|
|
|
|
free (*string_p);
|
|
*string_p = NULL;
|
|
if (!length)
|
|
return 0;
|
|
|
|
length += 4 - (length & 3);
|
|
buffer = gcov_read_bytes (length);
|
|
if (!buffer)
|
|
return 1;
|
|
|
|
*string_p = xmalloc (length);
|
|
if (!*string_p)
|
|
return 1;
|
|
|
|
memcpy (*string_p, buffer, length);
|
|
return 0;
|
|
}
|
|
|
|
#endif /* !IN_LIBGCOV */
|
|
|
|
#define GCOV_SUMMARY_LENGTH 44
|
|
static int
|
|
gcov_read_summary (struct gcov_summary *summary)
|
|
{
|
|
return (gcov_read_unsigned (&summary->checksum)
|
|
|| gcov_read_unsigned (&summary->runs)
|
|
|| gcov_read_unsigned (&summary->arcs)
|
|
|| gcov_read_counter (&summary->arc_sum)
|
|
|| gcov_read_counter (&summary->arc_max_one)
|
|
|| gcov_read_counter (&summary->arc_max_sum)
|
|
|| gcov_read_counter (&summary->arc_sum_max));
|
|
}
|
|
|
|
/* Save the current position in the gcov file. */
|
|
|
|
static inline unsigned long
|
|
gcov_save_position (void)
|
|
{
|
|
return gcov_position;
|
|
}
|
|
|
|
/* Reset to a known position. BASE should have been obtained from
|
|
gcov_save_position, LENGTH should be a record length, or zero. */
|
|
|
|
static inline int
|
|
gcov_resync (unsigned long base, unsigned length)
|
|
{
|
|
if (gcov_buffer)
|
|
gcov_position = base + length;
|
|
return 0;
|
|
}
|
|
|
|
/* Move to the end of the gcov file. */
|
|
|
|
static inline unsigned long
|
|
gcov_seek_end ()
|
|
{
|
|
gcov_position = gcov_length;
|
|
return gcov_position;
|
|
}
|
|
|
|
/* Skip LENGTH bytes in the file. */
|
|
|
|
static inline int
|
|
gcov_skip (unsigned length)
|
|
{
|
|
if (gcov_length < gcov_position + length)
|
|
return 1;
|
|
gcov_position += length;
|
|
return 0;
|
|
}
|
|
|
|
/* Skip a string of LENGTH bytes. */
|
|
|
|
static inline int
|
|
gcov_skip_string (unsigned length)
|
|
{
|
|
return gcov_skip (length + 4 - (length & 3));
|
|
}
|
|
|
|
/* Tests whether we have reached end of .da file. */
|
|
|
|
static inline int
|
|
gcov_eof ()
|
|
{
|
|
return gcov_position == gcov_length;
|
|
}
|
|
|
|
/* Return non-zero if the error flag is set. */
|
|
|
|
static inline int
|
|
gcov_ok ()
|
|
{
|
|
return gcov_file != 0 && !gcov_errored;
|
|
}
|
|
|
|
/* Set the error flag. */
|
|
static inline int
|
|
gcov_error ()
|
|
{
|
|
int error = gcov_errored;
|
|
|
|
gcov_errored = 1;
|
|
return error;
|
|
}
|
|
|
|
#if IN_GCOV > 0
|
|
/* Return the modification time of the current gcov file. */
|
|
|
|
static time_t
|
|
gcov_time ()
|
|
{
|
|
struct stat status;
|
|
|
|
if (fstat (fileno (gcov_file), &status))
|
|
return 0;
|
|
else
|
|
return status.st_mtime;
|
|
}
|
|
#endif /* IN_GCOV */
|
|
#endif /* GCC_GCOV_IO_H */
|