Commit 546d2adb by Nathan Sidwell Committed by Nathan Sidwell

Change gcov file interface to single file at a time.

	* gcov-io.h: Replace IN_LIBGCC1 with IN_LIBGCOV. Use IN_GCOV.
	Convert to C89 prototypes.
	(gcov_file, gcov_length, gcov_position, gcov_buffer, gcov_alloc,
	gcov_error, gcov_modified): New static variables.
	(gcov_open, gcov_close, gcov_read_bytes, gcov_write_bytes): New
	functions.
	(gcov_write_unsigned, gcov_write_counter, gcov_write_string,
	gcov_read_unsigned, gcov_read_counter, gcov_read_string): Adjust.
	(gcov_read_summary, gcov_write_summary): Adjust.
	(gcov_save_position, gcov_reserve_length, gcov_write_length):
	Adjust.
	(gcov_resync, gcov_skip, gcov_skip_string): Adjust.
	(da_file_open, da_file_close, da_file_eof, da_file_error): Remove.
	(da_file_position, da_file_seek, da_file_write, da_file_read):
	Remove.
	(gcov_error, gcov_eof, gcov_ok, gcov_time): New functions.
	* gcov.c (gcov_type): Don't typedef here.
	(IN_GCOV): #define
	(read_graph_file, read_count_file): Adjust.
	* gcov-dump.c (gcov_type): Don't typedef here.
	(IN_GCOV): #define.
	(tag_function, tag_blocks, tag_arcs, tag_lines, tag_arc_counts):
	Remove FILE parameter, adjust.
	(struct tag_format): Adjust proc member.
	(dump_file): Adjust.
	* libgcov.c (IN_LIBGCOV): #define.
	(gcov_exit): Adjust.
	* loop-init.c: Don't #include gcov-io.h
	* profile.c (struct counts_entry): New structure to hold counter
	values.
	(struct section_reference, struct da_index_entry): Remove.
	(bbg_file, da_file): Remove.
	(htab_counts_index_hash, htab_counts_index_eq,
	htab_counts_index_del): Replace with ...
	(htab_counts_entry_hash, htab_counts_entry_eq,
	htab_counts_entry_del): ... these.
	(cleanup_counts_index, index_counts_file): Remove.
	(read_counts_file): New function.
	(get_exec_counts): Adjust.
	(compute_branch_probabilities): Don't free the exec counts here.
	(branch_prob): Adjust.
	(init_branch_prob): Adjust.
	(end_branch_prob): Adjust.

From-SVN: r65338
parent 5da702b1
2003-04-07 Nathan Sidwell <nathan@codesourcery.com>
Change gcov file interface to single file at a time.
* gcov-io.h: Replace IN_LIBGCC1 with IN_LIBGCOV. Use IN_GCOV.
Convert to C89 prototypes.
(gcov_file, gcov_length, gcov_position, gcov_buffer, gcov_alloc,
gcov_error, gcov_modified): New static variables.
(gcov_open, gcov_close, gcov_read_bytes, gcov_write_bytes): New
functions.
(gcov_write_unsigned, gcov_write_counter, gcov_write_string,
gcov_read_unsigned, gcov_read_counter, gcov_read_string): Adjust.
(gcov_read_summary, gcov_write_summary): Adjust.
(gcov_save_position, gcov_reserve_length, gcov_write_length):
Adjust.
(gcov_resync, gcov_skip, gcov_skip_string): Adjust.
(da_file_open, da_file_close, da_file_eof, da_file_error): Remove.
(da_file_position, da_file_seek, da_file_write, da_file_read):
Remove.
(gcov_error, gcov_eof, gcov_ok, gcov_time): New functions.
* gcov.c (gcov_type): Don't typedef here.
(IN_GCOV): #define
(read_graph_file, read_count_file): Adjust.
* gcov-dump.c (gcov_type): Don't typedef here.
(IN_GCOV): #define.
(tag_function, tag_blocks, tag_arcs, tag_lines, tag_arc_counts):
Remove FILE parameter, adjust.
(struct tag_format): Adjust proc member.
(dump_file): Adjust.
* libgcov.c (IN_LIBGCOV): #define.
(gcov_exit): Adjust.
* loop-init.c: Don't #include gcov-io.h
* profile.c (struct counts_entry): New structure to hold counter
values.
(struct section_reference, struct da_index_entry): Remove.
(bbg_file, da_file): Remove.
(htab_counts_index_hash, htab_counts_index_eq,
htab_counts_index_del): Replace with ...
(htab_counts_entry_hash, htab_counts_entry_eq,
htab_counts_entry_del): ... these.
(cleanup_counts_index, index_counts_file): Remove.
(read_counts_file): New function.
(get_exec_counts): Adjust.
(compute_branch_probabilities): Don't free the exec counts here.
(branch_prob): Adjust.
(init_branch_prob): Adjust.
(end_branch_prob): Adjust.
2003-04-07 Aldy Hernandez <aldyh@redhat.com> 2003-04-07 Aldy Hernandez <aldyh@redhat.com>
* doc/invoke.texi (RS/6000 and PowerPC Options): Document * doc/invoke.texi (RS/6000 and PowerPC Options): Document
......
...@@ -23,26 +23,26 @@ Boston, MA 02111-1307, USA. */ ...@@ -23,26 +23,26 @@ Boston, MA 02111-1307, USA. */
#include "tm.h" #include "tm.h"
#include "version.h" #include "version.h"
#include <getopt.h> #include <getopt.h>
typedef HOST_WIDEST_INT gcov_type; #define IN_GCOV (-1)
#include "gcov-io.h" #include "gcov-io.h"
static void dump_file PARAMS ((const char *)); static void dump_file PARAMS ((const char *));
static void print_prefix PARAMS ((const char *, unsigned)); static void print_prefix PARAMS ((const char *, unsigned));
static void print_usage PARAMS ((void)); static void print_usage PARAMS ((void));
static void print_version PARAMS ((void)); static void print_version PARAMS ((void));
static int tag_function PARAMS ((const char *, FILE *, unsigned, unsigned)); static int tag_function PARAMS ((const char *, unsigned, unsigned));
static int tag_blocks PARAMS ((const char *, FILE *, unsigned, unsigned)); static int tag_blocks PARAMS ((const char *, unsigned, unsigned));
static int tag_arcs PARAMS ((const char *, FILE *, unsigned, unsigned)); static int tag_arcs PARAMS ((const char *, unsigned, unsigned));
static int tag_lines PARAMS ((const char *, FILE *, unsigned, unsigned)); static int tag_lines PARAMS ((const char *, unsigned, unsigned));
static int tag_arc_counts PARAMS ((const char *, FILE *, unsigned, unsigned)); static int tag_arc_counts PARAMS ((const char *, unsigned, unsigned));
static int tag_summary PARAMS ((const char *, FILE *, unsigned, unsigned)); static int tag_summary PARAMS ((const char *, unsigned, unsigned));
extern int main PARAMS ((int, char **)); extern int main PARAMS ((int, char **));
typedef struct tag_format typedef struct tag_format
{ {
unsigned tag; unsigned tag;
char const *name; char const *name;
int (*proc) (const char *, FILE *, unsigned, unsigned); int (*proc) (const char *, unsigned, unsigned);
} tag_format_t; } tag_format_t;
static int flag_dump_contents = 0; static int flag_dump_contents = 0;
...@@ -138,24 +138,22 @@ static void ...@@ -138,24 +138,22 @@ static void
dump_file (filename) dump_file (filename)
const char *filename; const char *filename;
{ {
FILE *file = fopen (filename, "rb");
unsigned tags[4]; unsigned tags[4];
unsigned depth = 0; unsigned depth = 0;
unsigned magic, version; unsigned magic, version;
unsigned tag, length; unsigned tag, length;
if (!file) if (!gcov_open (filename, 1))
{ {
fprintf (stderr, "%s:cannot open\n", filename); fprintf (stderr, "%s:cannot open\n", filename);
return; return;
} }
if (gcov_read_unsigned (file, &magic) if (gcov_read_unsigned (&magic) || gcov_read_unsigned (&version))
|| gcov_read_unsigned (file, &version))
{ {
read_error:; read_error:;
printf ("%s:read error at %ld\n", filename, ftell (file)); printf ("%s:read error at %lu\n", filename, gcov_save_position ());
fclose (file); gcov_close ();
return; return;
} }
...@@ -174,7 +172,7 @@ dump_file (filename) ...@@ -174,7 +172,7 @@ dump_file (filename)
else else
{ {
printf ("%s:not a gcov file\n", filename); printf ("%s:not a gcov file\n", filename);
fclose (file); gcov_close ();
return; return;
} }
for (ix = 4; ix--; expected >>= 8, version >>= 8, magic >>= 8) for (ix = 4; ix--; expected >>= 8, version >>= 8, magic >>= 8)
...@@ -189,14 +187,13 @@ dump_file (filename) ...@@ -189,14 +187,13 @@ dump_file (filename)
printf ("%s:warning:current version is `%.4s'\n", filename, e); printf ("%s:warning:current version is `%.4s'\n", filename, e);
} }
while (!gcov_read_unsigned (file, &tag) while (!gcov_read_unsigned (&tag) && !gcov_read_unsigned (&length))
&& !gcov_read_unsigned (file, &length))
{ {
tag_format_t const *format; tag_format_t const *format;
unsigned tag_depth; unsigned tag_depth;
long base, end; long base, end;
base = gcov_save_position (file); base = gcov_save_position ();
if (!tag) if (!tag)
tag_depth = depth; tag_depth = depth;
...@@ -234,11 +231,11 @@ dump_file (filename) ...@@ -234,11 +231,11 @@ dump_file (filename)
print_prefix (filename, tag_depth); print_prefix (filename, tag_depth);
printf ("%08x:%4u:%s", tag, length, format->name); printf ("%08x:%4u:%s", tag, length, format->name);
if (format->proc) if (format->proc)
if ((*format->proc) (filename, file, tag, length)) if ((*format->proc) (filename, tag, length))
goto read_error; goto read_error;
printf ("\n"); printf ("\n");
end = gcov_save_position (file); end = gcov_save_position ();
gcov_resync (file, base, length); gcov_resync (base, length);
if (format->proc && end != base + (long)length) if (format->proc && end != base + (long)length)
{ {
if (end > base + (long)length) if (end > base + (long)length)
...@@ -249,35 +246,44 @@ dump_file (filename) ...@@ -249,35 +246,44 @@ dump_file (filename)
filename, length - (end - base)); filename, length - (end - base));
} }
} }
if (!feof (file)) if (!gcov_eof ())
goto read_error; goto read_error;
fclose (file); gcov_close ();
} }
static int static int
tag_function (filename, file, tag, length) tag_function (filename, tag, length)
const char *filename ATTRIBUTE_UNUSED; const char *filename ATTRIBUTE_UNUSED;
FILE *file ATTRIBUTE_UNUSED;
unsigned tag ATTRIBUTE_UNUSED; unsigned tag ATTRIBUTE_UNUSED;
unsigned length ATTRIBUTE_UNUSED; unsigned length ATTRIBUTE_UNUSED;
{ {
char *name = NULL; char *name = NULL;
unsigned checksum; unsigned checksum;
char *src = NULL;
unsigned lineno = 0;
unsigned long pos = gcov_save_position ();
if (gcov_read_string (file, &name, NULL) if (gcov_read_string (&name)
|| gcov_read_unsigned (file, &checksum)) || gcov_read_unsigned (&checksum))
return 1;
if (gcov_save_position () - pos != length
&& (gcov_read_string (&src)
|| gcov_read_unsigned (&lineno)))
return 1; return 1;
printf (" `%s' checksum=0x%08x", name, checksum); printf (" `%s' checksum=0x%08x", name, checksum);
if (src)
printf (" %s:%u", src, lineno);
free (name); free (name);
free (src);
return 0; return 0;
} }
static int static int
tag_blocks (filename, file, tag, length) tag_blocks (filename, tag, length)
const char *filename ATTRIBUTE_UNUSED; const char *filename ATTRIBUTE_UNUSED;
FILE *file ATTRIBUTE_UNUSED;
unsigned tag ATTRIBUTE_UNUSED; unsigned tag ATTRIBUTE_UNUSED;
unsigned length ATTRIBUTE_UNUSED; unsigned length ATTRIBUTE_UNUSED;
{ {
...@@ -292,7 +298,7 @@ tag_blocks (filename, file, tag, length) ...@@ -292,7 +298,7 @@ tag_blocks (filename, file, tag, length)
for (ix = 0; ix != n_blocks; ix++) for (ix = 0; ix != n_blocks; ix++)
{ {
unsigned flags; unsigned flags;
if (gcov_read_unsigned (file, &flags)) if (gcov_read_unsigned (&flags))
return 1; return 1;
if (!(ix & 7)) if (!(ix & 7))
printf ("\n%s:\t\t%u", filename, ix); printf ("\n%s:\t\t%u", filename, ix);
...@@ -301,15 +307,14 @@ tag_blocks (filename, file, tag, length) ...@@ -301,15 +307,14 @@ tag_blocks (filename, file, tag, length)
} }
else else
gcov_skip (file, n_blocks * 4); gcov_skip (n_blocks * 4);
return 0; return 0;
} }
static int static int
tag_arcs (filename, file, tag, length) tag_arcs (filename, tag, length)
const char *filename ATTRIBUTE_UNUSED; const char *filename ATTRIBUTE_UNUSED;
FILE *file ATTRIBUTE_UNUSED;
unsigned tag ATTRIBUTE_UNUSED; unsigned tag ATTRIBUTE_UNUSED;
unsigned length ATTRIBUTE_UNUSED; unsigned length ATTRIBUTE_UNUSED;
{ {
...@@ -321,15 +326,14 @@ tag_arcs (filename, file, tag, length) ...@@ -321,15 +326,14 @@ tag_arcs (filename, file, tag, length)
unsigned ix; unsigned ix;
unsigned blockno; unsigned blockno;
if (gcov_read_unsigned (file, &blockno)) if (gcov_read_unsigned (&blockno))
return 1; return 1;
for (ix = 0; ix != n_arcs; ix++) for (ix = 0; ix != n_arcs; ix++)
{ {
unsigned dst, flags; unsigned dst, flags;
if (gcov_read_unsigned (file, &dst) if (gcov_read_unsigned (&dst) || gcov_read_unsigned (&flags))
|| gcov_read_unsigned (file, &flags))
return 1; return 1;
if (!(ix & 3)) if (!(ix & 3))
printf ("\n%s:\t\t%u:", filename, blockno); printf ("\n%s:\t\t%u:", filename, blockno);
...@@ -337,15 +341,14 @@ tag_arcs (filename, file, tag, length) ...@@ -337,15 +341,14 @@ tag_arcs (filename, file, tag, length)
} }
} }
else else
gcov_skip (file, 4 + n_arcs * 8); gcov_skip (4 + n_arcs * 8);
return 0; return 0;
} }
static int static int
tag_lines (filename, file, tag, length) tag_lines (filename, tag, length)
const char *filename ATTRIBUTE_UNUSED; const char *filename ATTRIBUTE_UNUSED;
FILE *file ATTRIBUTE_UNUSED;
unsigned tag ATTRIBUTE_UNUSED; unsigned tag ATTRIBUTE_UNUSED;
unsigned length ATTRIBUTE_UNUSED; unsigned length ATTRIBUTE_UNUSED;
{ {
...@@ -355,21 +358,21 @@ tag_lines (filename, file, tag, length) ...@@ -355,21 +358,21 @@ tag_lines (filename, file, tag, length)
unsigned blockno; unsigned blockno;
char const *sep = NULL; char const *sep = NULL;
if (gcov_read_unsigned (file, &blockno)) if (gcov_read_unsigned (&blockno))
return 1; return 1;
while (1) while (1)
{ {
unsigned lineno; unsigned lineno;
if (gcov_read_unsigned (file, &lineno)) if (gcov_read_unsigned (&lineno))
{ {
free (source); free (source);
return 1; return 1;
} }
if (!lineno) if (!lineno)
{ {
if (gcov_read_string (file, &source, NULL)) if (gcov_read_string (&source))
return 1; return 1;
if (!source) if (!source)
break; break;
...@@ -394,15 +397,14 @@ tag_lines (filename, file, tag, length) ...@@ -394,15 +397,14 @@ tag_lines (filename, file, tag, length)
} }
} }
else else
gcov_skip (file, length); gcov_skip (length);
return 0; return 0;
} }
static int static int
tag_arc_counts (filename, file, tag, length) tag_arc_counts (filename, tag, length)
const char *filename ATTRIBUTE_UNUSED; const char *filename ATTRIBUTE_UNUSED;
FILE *file ATTRIBUTE_UNUSED;
unsigned tag ATTRIBUTE_UNUSED; unsigned tag ATTRIBUTE_UNUSED;
unsigned length ATTRIBUTE_UNUSED; unsigned length ATTRIBUTE_UNUSED;
{ {
...@@ -417,7 +419,7 @@ tag_arc_counts (filename, file, tag, length) ...@@ -417,7 +419,7 @@ tag_arc_counts (filename, file, tag, length)
{ {
gcov_type count; gcov_type count;
if (gcov_read_counter (file, &count)) if (gcov_read_counter (&count))
return 1; return 1;
if (!(ix & 7)) if (!(ix & 7))
printf ("\n%s:\t\t%u", filename, ix); printf ("\n%s:\t\t%u", filename, ix);
...@@ -426,21 +428,20 @@ tag_arc_counts (filename, file, tag, length) ...@@ -426,21 +428,20 @@ tag_arc_counts (filename, file, tag, length)
} }
} }
else else
gcov_skip (file, n_counts * 8); gcov_skip (n_counts * 8);
return 0; return 0;
} }
static int static int
tag_summary (filename, file, tag, length) tag_summary (filename, tag, length)
const char *filename ATTRIBUTE_UNUSED; const char *filename ATTRIBUTE_UNUSED;
FILE *file ATTRIBUTE_UNUSED;
unsigned tag ATTRIBUTE_UNUSED; unsigned tag ATTRIBUTE_UNUSED;
unsigned length ATTRIBUTE_UNUSED; unsigned length ATTRIBUTE_UNUSED;
{ {
struct gcov_summary summary; struct gcov_summary summary;
if (gcov_read_summary (file, &summary)) if (gcov_read_summary (&summary))
return 1; return 1;
printf (" checksum=0x%08x", summary.checksum); printf (" checksum=0x%08x", summary.checksum);
......
...@@ -135,24 +135,30 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA ...@@ -135,24 +135,30 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
merged. merged.
This file is included by both the compiler, gcov tools and the This file is included by both the compiler, gcov tools and the
library. The IN_LIBGCC2 define distinguishes these cases. When runtime support library libgcov. IN_LIBGCOV and IN_GCOV are used to
IN_LIBGCC2 is nonzero, we're building libgcc2 for the target and distinguish which case is which. If IN_LIBGCOV is non-zero,
know the compiler is (the just built) gcc. Otherwise we're libgcov is being built. If IN_GCOV is non-zero, the gcov tools are
generating code for the host, and the compiler may or may not be being built. Otherwise the compiler is being built. IN_GCOV may be
gcc. In this latter case, you must ensure that 'gcov_type' is positive or negative. If positive, we are compiling a tool that
typedefed to something suitable (unsigned HOST_WIDEST_INT is requires additional functions (see the code for knowledge of what
usually what you want). */ those functions are). */
#ifndef GCC_GCOV_IO_H #ifndef GCC_GCOV_IO_H
#define GCC_GCOV_IO_H #define GCC_GCOV_IO_H
#if IN_LIBGCC2 #if IN_LIBGCOV
#if LONG_TYPE_SIZE == GCOV_TYPE_SIZE #if LONG_TYPE_SIZE == GCOV_TYPE_SIZE
typedef long gcov_type; typedef long gcov_type;
#else #else
typedef long long gcov_type; typedef long long gcov_type;
#endif #endif
#endif /* IN_LIBGCC2 */ #endif /* IN_LIBGCOV */
#if IN_GCOV
typedef HOST_WIDEST_INT gcov_type;
#if IN_GCOV > 0
#include <sys/types.h>
#endif
#endif
/* File suffixes. */ /* File suffixes. */
#define GCOV_DATA_SUFFIX ".da" #define GCOV_DATA_SUFFIX ".da"
...@@ -228,7 +234,7 @@ struct counter_section ...@@ -228,7 +234,7 @@ struct counter_section
unsigned n_counters; /* Number of counters in the section. */ unsigned n_counters; /* Number of counters in the section. */
}; };
#if IN_LIBGCC2 #if IN_LIBGCOV
/* Information about section of counters for an object file. */ /* Information about section of counters for an object file. */
struct counter_section_data struct counter_section_data
{ {
...@@ -272,139 +278,369 @@ extern void __gcov_flush (void); ...@@ -272,139 +278,369 @@ extern void __gcov_flush (void);
/* Since this file is used in both host and target files, and we don't /* 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. */ include ansidecl.h in target files, provide some necessary macros. */
#ifndef PARAMS
# define PARAMS(X) X
#endif
#ifndef ATTRIBUTE_UNUSED #ifndef ATTRIBUTE_UNUSED
# define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
#endif #endif
#endif /* IN_LIBGCC2 */ #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. */ /* Functions for reading and writing gcov files. */
static int gcov_write_unsigned PARAMS((FILE *, unsigned)) static int gcov_open (const char */*name*/, int /*truncate*/);
ATTRIBUTE_UNUSED; static int gcov_close (void);
static int gcov_write_counter PARAMS((FILE *, gcov_type)) #if !IN_GCOV
ATTRIBUTE_UNUSED; static unsigned char *gcov_write_bytes (unsigned);
static int gcov_write_string PARAMS((FILE *, const char *, unsigned)) static int gcov_write_unsigned (unsigned);
ATTRIBUTE_UNUSED; #if IN_LIBGCOV
static int gcov_read_unsigned PARAMS((FILE *, unsigned *)) static int gcov_write_counter (gcov_type);
ATTRIBUTE_UNUSED; #endif
static int gcov_read_counter PARAMS((FILE *, gcov_type *)) static int gcov_write_string (const char *);
ATTRIBUTE_UNUSED; static unsigned long gcov_reserve_length (void);
#if !IN_LIBGCC2 static int gcov_write_length (unsigned long /*position*/);
static int gcov_read_string PARAMS((FILE *, char **, unsigned *)) #if IN_LIBGCOV
ATTRIBUTE_UNUSED; static int gcov_write_summary (unsigned, const struct gcov_summary *);
#endif #endif
static int gcov_read_summary PARAMS ((FILE *, struct gcov_summary *)) #endif /* !IN_GCOV */
ATTRIBUTE_UNUSED; static const unsigned char *gcov_read_bytes (unsigned);
#if IN_LIBGCC2 static int gcov_read_unsigned (unsigned *);
static int gcov_write_summary PARAMS ((FILE *, unsigned, static int gcov_read_counter (gcov_type *);
const struct gcov_summary *)) #if !IN_LIBGCOV
ATTRIBUTE_UNUSED; static int gcov_read_string (char **);
#endif #endif
#define gcov_save_position(STREAM) \ static int gcov_read_summary (struct gcov_summary *);
da_file_position (STREAM) static __inline__ unsigned long gcov_save_position (void);
#define gcov_reserve_length(STREAM) \ static int gcov_resync (unsigned long /*base*/, unsigned /*length */);
(gcov_write_unsigned (STREAM, 0) ? 0 : da_file_position (STREAM) - 4) static unsigned long gcov_seek_end (void);
static int gcov_write_length PARAMS((FILE *, long)) static int gcov_skip (unsigned /*length*/);
ATTRIBUTE_UNUSED; static int gcov_skip_string (unsigned /*length*/);
#define gcov_resync(STREAM, BASE, LENGTH) \ static int gcov_ok (void);
da_file_seek (STREAM, BASE + (long)LENGTH, SEEK_SET) static int gcov_error (void);
#define gcov_skip(STREAM, LENGTH) \ static int gcov_eof (void);
da_file_seek (STREAM, LENGTH, SEEK_CUR) #if IN_GCOV > 0
#define gcov_skip_string(STREAM, LENGTH) \ static time_t gcov_time (void);
da_file_seek (STREAM, (LENGTH) + 4 - ((LENGTH) & 3), SEEK_CUR) #endif
#if IN_LIBGCC2
static FILE *da_file_open PARAMS ((const char *, int *)); /* Open a gcov file. NAME is the name of the file to open and MODE
static int da_file_close PARAMS ((void)); indicates whether a new file should be created, or an existing file
static int da_file_eof PARAMS ((void)); opened for modification. If MODE is >= 0 an existing file will be
static int da_file_error PARAMS ((void)); 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 #endif
static unsigned long da_file_position PARAMS ((FILE *)); gcov_alloc = new_size;
static int da_file_seek PARAMS ((FILE *, long, int)); gcov_buffer = result;
static size_t da_file_write PARAMS ((const void *, size_t, size_t, FILE *)); }
static size_t da_file_read PARAMS ((void *, size_t, size_t, FILE *));
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 FILE. Return nonzero if failed due to /* Write VALUE to coverage file. Return nonzero if failed due to
file i/o error, or value error. */ file i/o error, or value error. */
static int static int
gcov_write_unsigned (file, value) gcov_write_unsigned (unsigned value)
FILE *file;
unsigned value;
{ {
char buffer[4]; unsigned char *buffer = gcov_write_bytes (4);
unsigned ix; unsigned ix;
for (ix = sizeof (buffer); ix--; ) if (!buffer)
return 1;
for (ix = 4; ix--; )
{ {
buffer[ix] = value; buffer[ix] = value;
value >>= 8; value >>= 8;
} }
return ((sizeof (value) > sizeof (buffer) && value) return sizeof (value) > 4 && value;
|| da_file_write (buffer, 1, sizeof (buffer), file) != sizeof (buffer));
} }
/* Write VALUE to coverage file FILE. Return nonzero if failed due to /* Write VALUE to coverage file. Return nonzero if failed due to
file i/o error, or value error. Negative values are not checked file i/o error, or value error. Negative values are not checked
here -- they are checked in gcov_read_counter. */ here -- they are checked in gcov_read_counter. */
#if IN_LIBGCOV
static int static int
gcov_write_counter (file, value) gcov_write_counter (gcov_type value)
FILE *file;
gcov_type value;
{ {
char buffer[8]; unsigned char *buffer = gcov_write_bytes (8);
unsigned ix; unsigned ix;
for (ix = sizeof (buffer); ix--; ) if (!buffer)
return 1;
for (ix = 8; ix--; )
{ {
buffer[ix] = value; buffer[ix] = value;
value >>= 8; value >>= 8;
} }
return ((sizeof (value) > sizeof (buffer) && value != 0 && value != -1) return sizeof (value) > 8 && value;
|| da_file_write (buffer, 1, sizeof (buffer), file) != sizeof (buffer));
} }
#endif /* IN_LIBGCOV */
/* Write VALUE to coverage file FILE. Return nonzero if failed due to /* Write VALUE to coverage file. Return nonzero if failed due to
file i/o error, or value error. */ file i/o error, or value error. */
static int static int
gcov_write_string (file, string, length) gcov_write_string (const char *string)
FILE *file;
unsigned length;
const char *string;
{ {
if (string)
{
unsigned length = strlen (string);
unsigned pad = 0; unsigned pad = 0;
unsigned rem = 4 - (length & 3); unsigned rem = 4 - (length & 3);
unsigned char *buffer;
if (string) if (gcov_write_unsigned (length))
return (gcov_write_unsigned (file, length) return 1;
|| da_file_write (string, 1, length, file) != length buffer = gcov_write_bytes (length + rem);
|| da_file_write (&pad, 1, rem, file) != rem); if (!buffer)
return 1;
memcpy (buffer, string, length);
memcpy (buffer + length, &pad, rem);
return 0;
}
else else
return gcov_write_unsigned (file, 0); return gcov_write_unsigned (0);
} }
/* Read *VALUE_P from coverage file FILE. Return nonzero if failed /* 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. */ due to file i/o error, or range error. */
static int static int
gcov_read_unsigned (file, value_p) gcov_read_unsigned (unsigned *value_p)
FILE *file;
unsigned *value_p;
{ {
unsigned value = 0; unsigned value = 0;
unsigned ix; unsigned ix;
unsigned char buffer[4]; const unsigned char *buffer = gcov_read_bytes (4);
if (da_file_read (buffer, 1, sizeof (buffer), file) != sizeof (buffer)) if (!buffer)
return 1; return 1;
for (ix = sizeof (value); ix < sizeof (buffer); ix++) for (ix = sizeof (value); ix < 4; ix++)
if (buffer[ix]) if (buffer[ix])
return 1; return 1;
for (ix = 0; ix != sizeof (buffer); ix++) for (ix = 0; ix != 4; ix++)
{ {
value <<= 8; value <<= 8;
value |= buffer[ix]; value |= buffer[ix];
...@@ -413,24 +649,22 @@ gcov_read_unsigned (file, value_p) ...@@ -413,24 +649,22 @@ gcov_read_unsigned (file, value_p)
return 0; return 0;
} }
/* Read *VALUE_P from coverage file FILE. Return nonzero if failed /* Read *VALUE_P from coverage file. Return nonzero if failed
due to file i/o error, or range error. */ due to file i/o error, or range error. */
static int static int
gcov_read_counter (file, value_p) gcov_read_counter (gcov_type *value_p)
FILE *file;
gcov_type *value_p;
{ {
gcov_type value = 0; gcov_type value = 0;
unsigned ix; unsigned ix;
unsigned char buffer[8]; const unsigned char *buffer = gcov_read_bytes (8);
if (da_file_read (buffer, 1, sizeof (buffer), file) != sizeof (buffer)) if (!buffer)
return 1; return 1;
for (ix = sizeof (value); ix < sizeof (buffer); ix++) for (ix = sizeof (value); ix < 8; ix++)
if (buffer[ix]) if (buffer[ix])
return 1; return 1;
for (ix = 0; ix != sizeof (buffer); ix++) for (ix = 0; ix != 8; ix++)
{ {
value <<= 8; value <<= 8;
value |= buffer[ix]; value |= buffer[ix];
...@@ -440,377 +674,139 @@ gcov_read_counter (file, value_p) ...@@ -440,377 +674,139 @@ gcov_read_counter (file, value_p)
return value < 0; return value < 0;
} }
#if !IN_LIBGCC2 #if !IN_LIBGCOV
/* Read string from coverage file FILE. Length is stored in *LENGTH_P /* Read string from coverage file. A buffer is allocated and returned
(if non-null), a buffer is allocated and returned in *STRING_P. in *STRING_P. Return nonzero if failed due to file i/o error, or
Return nonzero if failed due to file i/o error, or range range error. Uses xmalloc to allocate the string buffer. */
error. Uses xmalloc to allocate the string buffer. */
static int static int
gcov_read_string (file, string_p, length_p) gcov_read_string (char **string_p)
FILE *file;
char **string_p;
unsigned *length_p;
{ {
unsigned length; unsigned length;
const unsigned char *buffer;
if (gcov_read_unsigned (file, &length)) if (gcov_read_unsigned (&length))
return 1; return 1;
if (length_p)
*length_p = length;
free (*string_p); free (*string_p);
*string_p = NULL; *string_p = NULL;
if (!length) if (!length)
return 0; return 0;
length += 4 - (length & 3); length += 4 - (length & 3);
*string_p = (char *) xmalloc (length); buffer = gcov_read_bytes (length);
if (!buffer)
return 1;
return da_file_read (*string_p, 1, length, file) != length; *string_p = xmalloc (length);
if (!*string_p)
return 1;
memcpy (*string_p, buffer, length);
return 0;
} }
#endif /* !IN_LIBGCC2 */ #endif /* !IN_LIBGCOV */
/* 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 (file, place)
FILE *file;
long place;
{
long here = da_file_position (file);
int result = (!place || da_file_seek (file, place, SEEK_SET)
|| gcov_write_unsigned (file, here - place - 4));
if (da_file_seek (file, here, SEEK_SET))
result = 1;
return result;
}
#define GCOV_SUMMARY_LENGTH 44 #define GCOV_SUMMARY_LENGTH 44
static int static int
gcov_read_summary (da_file, summary) gcov_read_summary (struct gcov_summary *summary)
FILE *da_file;
struct gcov_summary *summary;
{ {
return (gcov_read_unsigned (da_file, &summary->checksum) return (gcov_read_unsigned (&summary->checksum)
|| gcov_read_unsigned (da_file, &summary->runs) || gcov_read_unsigned (&summary->runs)
|| gcov_read_unsigned (da_file, &summary->arcs) || gcov_read_unsigned (&summary->arcs)
|| gcov_read_counter (da_file, &summary->arc_sum) || gcov_read_counter (&summary->arc_sum)
|| gcov_read_counter (da_file, &summary->arc_max_one) || gcov_read_counter (&summary->arc_max_one)
|| gcov_read_counter (da_file, &summary->arc_max_sum) || gcov_read_counter (&summary->arc_max_sum)
|| gcov_read_counter (da_file, &summary->arc_sum_max)); || gcov_read_counter (&summary->arc_sum_max));
} }
#if IN_LIBGCC2 /* Save the current position in the gcov file. */
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
#if IN_LIBGCC2 static inline unsigned long
/* The kernel had problems with managing a lot of small reads/writes we use; gcov_save_position (void)
the functions below are used to buffer whole file in memory, thus reading and
writing it only once. This should be feasible, as we have this amount
of memory for counters allocated anyway. */
static FILE *actual_da_file;
static unsigned long actual_da_file_position;
static unsigned long actual_da_file_length;
static char *actual_da_file_buffer;
static unsigned long actual_da_file_buffer_size;
/* Open the file NAME and return it; in EXISTED return 1 if it existed
already. */
static FILE *
da_file_open (name, existed)
const char *name;
int *existed;
{ {
#if defined (TARGET_HAS_F_SETLKW) return gcov_position;
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 (actual_da_file)
return 0;
actual_da_file_position = 0;
if (!actual_da_file_buffer)
{
actual_da_file_buffer = malloc (1);
actual_da_file_buffer_size = 1;
}
actual_da_file = fopen (name, "r+t");
if (actual_da_file)
*existed = 1;
else
{
actual_da_file = fopen (name, "w+t");
if (actual_da_file)
*existed = 0;
else
return 0;
}
#if defined (TARGET_HAS_F_SETLKW)
/* After a fork, another process might try to read and/or write
the same file simultaneously. So if we can, lock the file to
avoid race conditions. */
while (fcntl (fileno (actual_da_file), F_SETLKW, &s_flock)
&& errno == EINTR)
continue;
#endif
if (*existed) /* Reset to a known position. BASE should have been obtained from
{ gcov_save_position, LENGTH should be a record length, or zero. */
if (fseek (actual_da_file, 0, SEEK_END))
{
fclose (actual_da_file);
actual_da_file = 0;
return 0;
}
actual_da_file_length = ftell (actual_da_file);
rewind (actual_da_file);
}
else
actual_da_file_length = 0;
if (actual_da_file_length > actual_da_file_buffer_size) static inline int
{ gcov_resync (unsigned long base, unsigned length)
actual_da_file_buffer_size = actual_da_file_length; {
actual_da_file_buffer = realloc (actual_da_file_buffer, if (gcov_buffer)
actual_da_file_buffer_size); gcov_position = base + length;
if (!actual_da_file_buffer)
{
fclose (actual_da_file);
actual_da_file = 0;
return 0;
}
}
if (*existed)
{
if (fread (actual_da_file_buffer, actual_da_file_length,
1, actual_da_file) != 1)
{
fclose (actual_da_file);
actual_da_file = 0;
return 0; return 0;
}
rewind (actual_da_file);
}
return actual_da_file;
} }
/* Write changes to the .da file and close it. */ /* Move to the end of the gcov file. */
static int da_file_close ()
{
if (!actual_da_file)
return -1;
if (fwrite (actual_da_file_buffer, actual_da_file_length, static inline unsigned long
1, actual_da_file) != 1) gcov_seek_end ()
return da_file_error (); {
gcov_position = gcov_length;
return gcov_position;
}
if (fclose (actual_da_file)) /* Skip LENGTH bytes in the file. */
{
actual_da_file = 0;
return -1;
}
actual_da_file = 0; static inline int
gcov_skip (unsigned length)
{
if (gcov_length < gcov_position + length)
return 1;
gcov_position += length;
return 0; return 0;
} }
/* Returns current position in .da file. */ /* Skip a string of LENGTH bytes. */
static unsigned long
da_file_position (file) static inline int
FILE *file; gcov_skip_string (unsigned length)
{ {
if (file) return gcov_skip (length + 4 - (length & 3));
return ftell (file);
return actual_da_file_position;
} }
/* Tests whether we have reached end of .da file. */ /* Tests whether we have reached end of .da file. */
static int
da_file_eof ()
{
return actual_da_file_position == actual_da_file_length;
}
/* Change position in the .da file. */ static inline int
static int gcov_eof ()
da_file_seek (file, pos, whence)
FILE *file;
long pos;
int whence;
{ {
if (file) return gcov_position == gcov_length;
return fseek (file, pos, whence);
if (!actual_da_file)
return -1;
switch (whence)
{
case SEEK_CUR:
if (pos < 0 && (unsigned long) -pos > actual_da_file_position)
return da_file_error ();
actual_da_file_position += pos;
break;
case SEEK_SET:
actual_da_file_position = pos;
break;
case SEEK_END:
if ((unsigned long) -pos > actual_da_file_length)
return da_file_error ();
actual_da_file_position = actual_da_file_length + pos;
}
if (actual_da_file_position > actual_da_file_length)
return da_file_error ();
return 0;
} }
/* Write LEN chars of DATA to actual .da file; ELTS is expected to be 1, /* Return non-zero if the error flag is set. */
FILE 0. */
static size_t
da_file_write (data, elts, len, file)
const void *data;
size_t elts;
size_t len;
FILE *file;
{
size_t l = len;
const char *dat = data;
if (file)
return fwrite (data, elts, len, file);
if (elts != 1)
abort ();
if (!actual_da_file)
return -1;
if (actual_da_file_position + len > actual_da_file_buffer_size)
{
actual_da_file_buffer_size = 2 * (actual_da_file_position + len);
actual_da_file_buffer = realloc (actual_da_file_buffer,
actual_da_file_buffer_size);
if (!actual_da_file_buffer)
return da_file_error ();
}
while (len--)
actual_da_file_buffer[actual_da_file_position++] = *dat++;
if (actual_da_file_position > actual_da_file_length)
actual_da_file_length = actual_da_file_position;
return l; static inline int
} gcov_ok ()
/* Read LEN chars of DATA from actual .da file; ELTS is expected to be 1,
FILE 0. */
static size_t
da_file_read (data, elts, len, file)
void *data;
size_t elts;
size_t len;
FILE *file;
{ {
size_t l; return gcov_file != 0 && !gcov_errored;
char *dat = data;
if (file)
return fread (data, elts, len, file);
if (elts != 1)
abort ();
if (!actual_da_file)
return -1;
if (actual_da_file_position + len > actual_da_file_length)
len = actual_da_file_length - actual_da_file_position;
l = len;
while (len--)
*dat++ = actual_da_file_buffer[actual_da_file_position++];
return l;
} }
/* Close the current .da file and report error. */ /* Set the error flag. */
static int static inline int
da_file_error () gcov_error ()
{
if (actual_da_file)
fclose (actual_da_file);
actual_da_file = 0;
return -1;
}
#else /* !IN_LIBGCC2 */
static size_t
da_file_write (data, elts, len, file)
const void *data;
size_t elts;
size_t len;
FILE *file;
{ {
return fwrite (data, elts, len, file); int error = gcov_errored;
}
static size_t gcov_errored = 1;
da_file_read (data, elts, len, file) return error;
void *data;
size_t elts;
size_t len;
FILE *file;
{
return fread (data, elts, len, file);
} }
static unsigned long #if IN_GCOV > 0
da_file_position (file) /* Return the modification time of the current gcov file. */
FILE *file;
{
return ftell (file);
}
static int static time_t
da_file_seek (file, pos, whence) gcov_time ()
FILE *file;
long pos;
int whence;
{ {
return fseek (file, pos, whence); struct stat status;
}
#endif
if (fstat (fileno (gcov_file), &status))
return 0;
else
return status.st_mtime;
}
#endif /* IN_GCOV */
#endif /* GCC_GCOV_IO_H */ #endif /* GCC_GCOV_IO_H */
...@@ -51,7 +51,7 @@ Boston, MA 02111-1307, USA. */ ...@@ -51,7 +51,7 @@ Boston, MA 02111-1307, USA. */
#include <getopt.h> #include <getopt.h>
typedef HOST_WIDEST_INT gcov_type; #define IN_GCOV 1
#include "gcov-io.h" #include "gcov-io.h"
/* The bbg file is generated by -ftest-coverage option. The da file is /* The bbg file is generated by -ftest-coverage option. The da file is
...@@ -706,8 +706,6 @@ find_source (file_name) ...@@ -706,8 +706,6 @@ find_source (file_name)
static int static int
read_graph_file () read_graph_file ()
{ {
FILE *file;
struct stat status;
unsigned magic, version; unsigned magic, version;
unsigned current_tag = 0; unsigned current_tag = 0;
unsigned tag; unsigned tag;
...@@ -715,22 +713,20 @@ read_graph_file () ...@@ -715,22 +713,20 @@ read_graph_file ()
source_t *src = NULL; source_t *src = NULL;
unsigned ix; unsigned ix;
file = fopen (bbg_file_name, "rb"); if (!gcov_open (bbg_file_name, 1))
if (!file)
{ {
fnotice (stderr, "%s:cannot open graph file\n", bbg_file_name); fnotice (stderr, "%s:cannot open graph file\n", bbg_file_name);
return 1; return 1;
} }
if (!fstat (fileno (file), &status)) bbg_file_time = gcov_time ();
bbg_file_time = status.st_mtime; if (gcov_read_unsigned (&magic) || magic != GCOV_GRAPH_MAGIC)
if (gcov_read_unsigned (file, &magic) || magic != GCOV_GRAPH_MAGIC)
{ {
fnotice (stderr, "%s:not a gcov graph file\n", bbg_file_name); fnotice (stderr, "%s:not a gcov graph file\n", bbg_file_name);
fclose (file); gcov_close ();
return 1; return 1;
} }
if (gcov_read_unsigned (file, &version) || version != GCOV_VERSION) if (gcov_read_unsigned (&version) || version != GCOV_VERSION)
{ {
char v[4], e[4]; char v[4], e[4];
...@@ -745,15 +741,15 @@ read_graph_file () ...@@ -745,15 +741,15 @@ read_graph_file ()
bbg_file_name, v, e); bbg_file_name, v, e);
} }
while (!gcov_read_unsigned (file, &tag)) while (!gcov_read_unsigned (&tag))
{ {
unsigned length; unsigned length;
long base; long base;
if (gcov_read_unsigned (file, &length)) if (gcov_read_unsigned (&length))
goto corrupt; goto corrupt;
base = gcov_save_position (file); base = gcov_save_position ();
if (tag == GCOV_TAG_FUNCTION) if (tag == GCOV_TAG_FUNCTION)
{ {
...@@ -763,10 +759,10 @@ read_graph_file () ...@@ -763,10 +759,10 @@ read_graph_file ()
source_t *src; source_t *src;
function_t *probe, *prev; function_t *probe, *prev;
if (gcov_read_string (file, &function_name, NULL) if (gcov_read_string (&function_name)
|| gcov_read_unsigned (file, &checksum) || gcov_read_unsigned (&checksum)
|| gcov_read_string (file, &function_file, NULL) || gcov_read_string (&function_file)
|| gcov_read_unsigned (file, &lineno)) || gcov_read_unsigned (&lineno))
goto corrupt; goto corrupt;
src = find_source (function_file); src = find_source (function_file);
fn = (function_t *)xcalloc (1, sizeof (function_t)); fn = (function_t *)xcalloc (1, sizeof (function_t));
...@@ -810,7 +806,7 @@ read_graph_file () ...@@ -810,7 +806,7 @@ read_graph_file ()
{ {
unsigned flags; unsigned flags;
if (gcov_read_unsigned (file, &flags)) if (gcov_read_unsigned (&flags))
goto corrupt; goto corrupt;
fn->blocks[ix].flags = flags; fn->blocks[ix].flags = flags;
} }
...@@ -822,7 +818,7 @@ read_graph_file () ...@@ -822,7 +818,7 @@ read_graph_file ()
unsigned num_dests = (length - 4) / 8; unsigned num_dests = (length - 4) / 8;
unsigned dest, flags; unsigned dest, flags;
if (gcov_read_unsigned (file, &src) if (gcov_read_unsigned (&src)
|| src >= fn->num_blocks || src >= fn->num_blocks
|| fn->blocks[src].succ) || fn->blocks[src].succ)
goto corrupt; goto corrupt;
...@@ -831,8 +827,8 @@ read_graph_file () ...@@ -831,8 +827,8 @@ read_graph_file ()
{ {
struct arc_info *arc; struct arc_info *arc;
if (gcov_read_unsigned (file, &dest) if (gcov_read_unsigned (&dest)
|| gcov_read_unsigned (file, &flags) || gcov_read_unsigned (&flags)
|| dest >= fn->num_blocks) || dest >= fn->num_blocks)
goto corrupt; goto corrupt;
arc = (arc_t *) xcalloc (1, sizeof (arc_t)); arc = (arc_t *) xcalloc (1, sizeof (arc_t));
...@@ -883,7 +879,7 @@ read_graph_file () ...@@ -883,7 +879,7 @@ read_graph_file ()
unsigned *line_nos unsigned *line_nos
= (unsigned *)xcalloc ((length - 4) / 4, sizeof (unsigned)); = (unsigned *)xcalloc ((length - 4) / 4, sizeof (unsigned));
if (gcov_read_unsigned (file, &blockno) if (gcov_read_unsigned (&blockno)
|| blockno >= fn->num_blocks || blockno >= fn->num_blocks
|| fn->blocks[blockno].u.line.encoding) || fn->blocks[blockno].u.line.encoding)
goto corrupt; goto corrupt;
...@@ -892,7 +888,7 @@ read_graph_file () ...@@ -892,7 +888,7 @@ read_graph_file ()
{ {
unsigned lineno; unsigned lineno;
if (gcov_read_unsigned (file, &lineno)) if (gcov_read_unsigned (&lineno))
goto corrupt; goto corrupt;
if (lineno) if (lineno)
{ {
...@@ -909,7 +905,7 @@ read_graph_file () ...@@ -909,7 +905,7 @@ read_graph_file ()
{ {
char *file_name = NULL; char *file_name = NULL;
if (gcov_read_string (file, &file_name, NULL)) if (gcov_read_string (&file_name))
goto corrupt; goto corrupt;
if (!file_name) if (!file_name)
break; break;
...@@ -928,15 +924,15 @@ read_graph_file () ...@@ -928,15 +924,15 @@ read_graph_file ()
fn = NULL; fn = NULL;
current_tag = 0; current_tag = 0;
} }
if (gcov_resync (file, base, length)) if (gcov_resync (base, length))
{ {
corrupt:; corrupt:;
fnotice (stderr, "%s:corrupted\n", bbg_file_name); fnotice (stderr, "%s:corrupted\n", bbg_file_name);
fclose (file); gcov_close ();
return 1; return 1;
} }
} }
fclose (file); gcov_close ();
/* We built everything backwards, so nreverse them all */ /* We built everything backwards, so nreverse them all */
...@@ -997,27 +993,25 @@ read_graph_file () ...@@ -997,27 +993,25 @@ read_graph_file ()
static int static int
read_count_file () read_count_file ()
{ {
FILE *file;
unsigned ix; unsigned ix;
char *function_name_buffer = NULL; char *function_name_buffer = NULL;
unsigned magic, version; unsigned magic, version;
function_t *fn = NULL; function_t *fn = NULL;
file = fopen (da_file_name, "rb"); if (!gcov_open (da_file_name, 1))
if (!file)
{ {
fnotice (stderr, "%s:cannot open data file\n", da_file_name); fnotice (stderr, "%s:cannot open data file\n", da_file_name);
return 1; return 1;
} }
if (gcov_read_unsigned (file, &magic) || magic != GCOV_DATA_MAGIC) if (gcov_read_unsigned (&magic) || magic != GCOV_DATA_MAGIC)
{ {
fnotice (stderr, "%s:not a gcov data file\n", da_file_name); fnotice (stderr, "%s:not a gcov data file\n", da_file_name);
cleanup:; cleanup:;
free (function_name_buffer); free (function_name_buffer);
fclose (file); gcov_close ();
return 1; return 1;
} }
if (gcov_read_unsigned (file, &version) || version != GCOV_VERSION) if (gcov_read_unsigned (&version) || version != GCOV_VERSION)
{ {
char v[4], e[4]; char v[4], e[4];
...@@ -1036,32 +1030,35 @@ read_count_file () ...@@ -1036,32 +1030,35 @@ read_count_file ()
unsigned tag, length; unsigned tag, length;
long base; long base;
if (gcov_read_unsigned (file, &tag) if (gcov_read_unsigned (&tag)
|| gcov_read_unsigned (file, &length)) || gcov_read_unsigned (&length))
{ {
if (feof (file)) if (gcov_eof ())
break; break;
corrupt:; corrupt:;
fnotice (stderr, "%s:corrupted\n", da_file_name); fnotice (stderr, "%s:corrupted\n", da_file_name);
goto cleanup; goto cleanup;
} }
base = gcov_save_position (file); base = gcov_save_position ();
if (tag == GCOV_TAG_OBJECT_SUMMARY) if (tag == GCOV_TAG_OBJECT_SUMMARY)
{ {
if (gcov_read_summary (file, &object_summary)) if (gcov_read_summary (&object_summary))
goto corrupt; goto corrupt;
} }
else if (tag == GCOV_TAG_PROGRAM_SUMMARY else if (tag == GCOV_TAG_PROGRAM_SUMMARY
|| tag == GCOV_TAG_INCORRECT_SUMMARY) || tag == GCOV_TAG_INCORRECT_SUMMARY)
{
program_count++; program_count++;
gcov_resync (base, length);
}
else if (tag == GCOV_TAG_FUNCTION) else if (tag == GCOV_TAG_FUNCTION)
{ {
unsigned checksum; unsigned checksum;
struct function_info *fn_n = functions; struct function_info *fn_n = functions;
if (gcov_read_string (file, &function_name_buffer, NULL) if (gcov_read_string (&function_name_buffer)
|| gcov_read_unsigned (file, &checksum)) || gcov_read_unsigned (&checksum))
goto corrupt; goto corrupt;
for (fn = fn ? fn->next : NULL; ; fn = fn->next) for (fn = fn ? fn->next : NULL; ; fn = fn->next)
...@@ -1103,15 +1100,16 @@ read_count_file () ...@@ -1103,15 +1100,16 @@ read_count_file ()
{ {
gcov_type count; gcov_type count;
if (gcov_read_counter (file, &count)) if (gcov_read_counter (&count))
goto corrupt; goto corrupt;
fn->counts[ix] += count; fn->counts[ix] += count;
} }
} }
gcov_resync (file, base, length); else
gcov_resync (base, length);
} }
fclose (file); gcov_close ();
free (function_name_buffer); free (function_name_buffer);
return 0; return 0;
} }
......
...@@ -57,6 +57,7 @@ void __gcov_flush (void) { } ...@@ -57,6 +57,7 @@ void __gcov_flush (void) { }
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#endif #endif
#define IN_LIBGCOV 1
#include "gcov-io.h" #include "gcov-io.h"
/* Chain of per-object gcov structures. */ /* Chain of per-object gcov structures. */
...@@ -102,16 +103,6 @@ gcov_exit (void) ...@@ -102,16 +103,6 @@ gcov_exit (void)
gcov_type program_sum = 0; gcov_type program_sum = 0;
unsigned program_arcs = 0; unsigned program_arcs = 0;
#if defined (TARGET_HAS_F_SETLKW)
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
memset (&program, 0, sizeof (program)); memset (&program, 0, sizeof (program));
program.checksum = gcov_crc32; program.checksum = gcov_crc32;
...@@ -119,7 +110,7 @@ gcov_exit (void) ...@@ -119,7 +110,7 @@ gcov_exit (void)
{ {
struct gcov_summary object; struct gcov_summary object;
struct gcov_summary local_prg; struct gcov_summary local_prg;
int merging = 0; int merging;
long base; long base;
const struct function_info *fn_info; const struct function_info *fn_info;
gcov_type **counters; gcov_type **counters;
...@@ -164,27 +155,28 @@ gcov_exit (void) ...@@ -164,27 +155,28 @@ gcov_exit (void)
memset (&object, 0, sizeof (object)); memset (&object, 0, sizeof (object));
/* Open for modification */ /* Open for modification */
if (!da_file_open (ptr->filename, &merging)) merging = gcov_open (ptr->filename, 0);
if (!merging)
{ {
fprintf (stderr, "profiling:%s:Cannot open\n", ptr->filename); fprintf (stderr, "profiling:%s:Cannot open\n", ptr->filename);
ptr->filename = 0; ptr->filename = 0;
continue; continue;
} }
if (merging) if (merging > 0)
{ {
/* Merge data from file. */ /* Merge data from file. */
if (gcov_read_unsigned (&tag) || tag != GCOV_DATA_MAGIC)
if (gcov_read_unsigned (0, &tag) || tag != GCOV_DATA_MAGIC)
{ {
fprintf (stderr, "profiling:%s:Not a gcov data file\n", fprintf (stderr, "profiling:%s:Not a gcov data file\n",
ptr->filename); ptr->filename);
read_fatal:; read_fatal:;
da_file_close (); gcov_close ();
ptr->filename = 0; ptr->filename = 0;
continue; continue;
} }
if (gcov_read_unsigned (0, &length) || length != GCOV_VERSION) if (gcov_read_unsigned (&length) || length != GCOV_VERSION)
{ {
gcov_version_mismatch (ptr, length); gcov_version_mismatch (ptr, length);
goto read_fatal; goto read_fatal;
...@@ -194,8 +186,7 @@ gcov_exit (void) ...@@ -194,8 +186,7 @@ gcov_exit (void)
for (ix = ptr->n_functions, fn_info = ptr->functions; for (ix = ptr->n_functions, fn_info = ptr->functions;
ix--; fn_info++) ix--; fn_info++)
{ {
if (gcov_read_unsigned (0, &tag) if (gcov_read_unsigned (&tag) || gcov_read_unsigned (&length))
|| gcov_read_unsigned (0, &length))
{ {
read_error:; read_error:;
fprintf (stderr, "profiling:%s:Error merging\n", fprintf (stderr, "profiling:%s:Error merging\n",
...@@ -212,9 +203,9 @@ gcov_exit (void) ...@@ -212,9 +203,9 @@ gcov_exit (void)
goto read_fatal; goto read_fatal;
} }
if (gcov_read_unsigned (0, &flength) if (gcov_read_unsigned (&flength)
|| gcov_skip_string (0, flength) || gcov_skip_string (flength)
|| gcov_read_unsigned (0, &checksum)) || gcov_read_unsigned (&checksum))
goto read_error; goto read_error;
if (flength != strlen (fn_info->name) if (flength != strlen (fn_info->name)
|| checksum != fn_info->checksum) || checksum != fn_info->checksum)
...@@ -227,8 +218,8 @@ gcov_exit (void) ...@@ -227,8 +218,8 @@ gcov_exit (void)
{ {
unsigned n_counters; unsigned n_counters;
if (gcov_read_unsigned (0, &tag) if (gcov_read_unsigned (&tag)
|| gcov_read_unsigned (0, &length)) || gcov_read_unsigned (&length))
goto read_error; goto read_error;
for (sect_index = 0; for (sect_index = 0;
sect_index < ptr->n_counter_sections; sect_index < ptr->n_counter_sections;
...@@ -244,7 +235,7 @@ gcov_exit (void) ...@@ -244,7 +235,7 @@ gcov_exit (void)
goto read_mismatch; goto read_mismatch;
for (jx = 0; jx < n_counters; jx++) for (jx = 0; jx < n_counters; jx++)
if (gcov_read_counter (0, &count)) if (gcov_read_counter (&count))
goto read_error; goto read_error;
else else
counters[sect_index][jx] += count; counters[sect_index][jx] += count;
...@@ -253,23 +244,22 @@ gcov_exit (void) ...@@ -253,23 +244,22 @@ gcov_exit (void)
} }
/* Check object summary */ /* Check object summary */
if (gcov_read_unsigned (0, &tag) if (gcov_read_unsigned (&tag) || gcov_read_unsigned (&length))
|| gcov_read_unsigned (0, &length))
goto read_error; goto read_error;
if (tag != GCOV_TAG_OBJECT_SUMMARY) if (tag != GCOV_TAG_OBJECT_SUMMARY)
goto read_mismatch; goto read_mismatch;
if (gcov_read_summary (0, &object)) if (gcov_read_summary (&object))
goto read_error; goto read_error;
/* Check program summary */ /* Check program summary */
while (1) while (1)
{ {
long base = da_file_position (0); long base = gcov_save_position ();
if (gcov_read_unsigned (0, &tag) if (gcov_read_unsigned (&tag)
|| gcov_read_unsigned (0, &length)) || gcov_read_unsigned (&length))
{ {
if (da_file_eof ()) if (gcov_eof ())
break; break;
goto read_error; goto read_error;
} }
...@@ -277,7 +267,7 @@ gcov_exit (void) ...@@ -277,7 +267,7 @@ gcov_exit (void)
&& tag != GCOV_TAG_PLACEHOLDER_SUMMARY && tag != GCOV_TAG_PLACEHOLDER_SUMMARY
&& tag != GCOV_TAG_INCORRECT_SUMMARY) && tag != GCOV_TAG_INCORRECT_SUMMARY)
goto read_mismatch; goto read_mismatch;
if (gcov_read_summary (0, &local_prg)) if (gcov_read_summary (&local_prg))
goto read_error; goto read_error;
if (local_prg.checksum != program.checksum) if (local_prg.checksum != program.checksum)
continue; continue;
...@@ -288,7 +278,7 @@ gcov_exit (void) ...@@ -288,7 +278,7 @@ gcov_exit (void)
ptr->filename); ptr->filename);
goto read_fatal; goto read_fatal;
} }
merging = -1; merging = 0;
if (tag != GCOV_TAG_PROGRAM_SUMMARY) if (tag != GCOV_TAG_PROGRAM_SUMMARY)
break; break;
...@@ -304,7 +294,7 @@ gcov_exit (void) ...@@ -304,7 +294,7 @@ gcov_exit (void)
ptr->wkspc = base; ptr->wkspc = base;
break; break;
} }
da_file_seek (0, 0, SEEK_SET); gcov_resync (0, 0);
} }
object.runs++; object.runs++;
...@@ -316,12 +306,12 @@ gcov_exit (void) ...@@ -316,12 +306,12 @@ gcov_exit (void)
/* Write out the data. */ /* Write out the data. */
if (/* magic */ if (/* magic */
gcov_write_unsigned (0, GCOV_DATA_MAGIC) gcov_write_unsigned (GCOV_DATA_MAGIC)
/* version number */ /* version number */
|| gcov_write_unsigned (0, GCOV_VERSION)) || gcov_write_unsigned (GCOV_VERSION))
{ {
write_error:; write_error:;
da_file_close (); gcov_close ();
fprintf (stderr, "profiling:%s:Error writing\n", ptr->filename); fprintf (stderr, "profiling:%s:Error writing\n", ptr->filename);
ptr->filename = 0; ptr->filename = 0;
continue; continue;
...@@ -333,14 +323,13 @@ gcov_exit (void) ...@@ -333,14 +323,13 @@ gcov_exit (void)
for (ix = ptr->n_functions, fn_info = ptr->functions; ix--; fn_info++) for (ix = ptr->n_functions, fn_info = ptr->functions; ix--; fn_info++)
{ {
/* Announce function. */ /* Announce function. */
if (gcov_write_unsigned (0, GCOV_TAG_FUNCTION) if (gcov_write_unsigned (GCOV_TAG_FUNCTION)
|| !(base = gcov_reserve_length (0)) || !(base = gcov_reserve_length ())
/* function name */ /* function name */
|| gcov_write_string (0, fn_info->name, || gcov_write_string (fn_info->name)
strlen (fn_info->name))
/* function checksum */ /* function checksum */
|| gcov_write_unsigned (0, fn_info->checksum) || gcov_write_unsigned (fn_info->checksum)
|| gcov_write_length (0, base)) || gcov_write_length (base))
goto write_error; goto write_error;
/* counters. */ /* counters. */
...@@ -357,8 +346,8 @@ gcov_exit (void) ...@@ -357,8 +346,8 @@ gcov_exit (void)
if (sect_index == ptr->n_counter_sections) if (sect_index == ptr->n_counter_sections)
abort (); abort ();
if (gcov_write_unsigned (0, tag) if (gcov_write_unsigned (tag)
|| !(base = gcov_reserve_length (0))) || !(base = gcov_reserve_length ()))
goto write_error; goto write_error;
for (jx = fn_info->counter_sections[f_sect_index].n_counters; jx--;) for (jx = fn_info->counter_sections[f_sect_index].n_counters; jx--;)
...@@ -371,41 +360,39 @@ gcov_exit (void) ...@@ -371,41 +360,39 @@ gcov_exit (void)
if (object.arc_max_sum < count) if (object.arc_max_sum < count)
object.arc_max_sum = count; object.arc_max_sum = count;
} }
if (gcov_write_counter (0, count)) if (gcov_write_counter (count))
goto write_error; /* RIP Edsger Dijkstra */ goto write_error; /* RIP Edsger Dijkstra */
} }
if (gcov_write_length (0, base)) if (gcov_write_length (base))
goto write_error; goto write_error;
} }
} }
/* Object file summary. */ /* Object file summary. */
if (gcov_write_summary (0, GCOV_TAG_OBJECT_SUMMARY, &object)) if (gcov_write_summary (GCOV_TAG_OBJECT_SUMMARY, &object))
goto write_error; goto write_error;
if (merging >= 0) if (merging)
{ {
if (da_file_seek (0, 0, SEEK_END)) ptr->wkspc = gcov_seek_end ();
goto write_error; if (gcov_write_summary (GCOV_TAG_PLACEHOLDER_SUMMARY,
ptr->wkspc = da_file_position (0);
if (gcov_write_summary (0, GCOV_TAG_PLACEHOLDER_SUMMARY,
&program)) &program))
goto write_error; goto write_error;
} }
else if (ptr->wkspc) else if (ptr->wkspc)
{ {
/* Zap trailing program summary */ /* Zap trailing program summary */
if (da_file_seek (0, ptr->wkspc, SEEK_SET)) if (gcov_resync (ptr->wkspc, 0))
goto write_error; goto write_error;
if (!local_prg.runs) if (!local_prg.runs)
ptr->wkspc = 0; ptr->wkspc = 0;
if (gcov_write_unsigned (0, local_prg.runs if (gcov_write_unsigned (local_prg.runs
? GCOV_TAG_PLACEHOLDER_SUMMARY ? GCOV_TAG_PLACEHOLDER_SUMMARY
: GCOV_TAG_INCORRECT_SUMMARY)) : GCOV_TAG_INCORRECT_SUMMARY))
goto write_error; goto write_error;
} }
if (da_file_close ()) if (gcov_close ())
{ {
fprintf (stderr, "profiling:%s:Error closing\n", ptr->filename); fprintf (stderr, "profiling:%s:Error closing\n", ptr->filename);
ptr->filename = 0; ptr->filename = 0;
...@@ -434,25 +421,16 @@ gcov_exit (void) ...@@ -434,25 +421,16 @@ gcov_exit (void)
for (ptr = gcov_list; ptr; ptr = ptr->next) for (ptr = gcov_list; ptr; ptr = ptr->next)
if (ptr->filename && ptr->wkspc) if (ptr->filename && ptr->wkspc)
{ {
FILE *da_file; if (!gcov_open (ptr->filename, 1))
da_file = fopen (ptr->filename, "r+b");
if (!da_file)
{ {
fprintf (stderr, "profiling:%s:Cannot open\n", ptr->filename); fprintf (stderr, "profiling:%s:Cannot open\n", ptr->filename);
continue; continue;
} }
#if defined (TARGET_HAS_F_SETLKW) if (gcov_resync (ptr->wkspc, 0)
while (fcntl (fileno (da_file), F_SETLKW, &s_flock) || gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &program))
&& errno == EINTR)
continue;
#endif
if (fseek (da_file, ptr->wkspc, SEEK_SET)
|| gcov_write_summary (da_file, GCOV_TAG_PROGRAM_SUMMARY, &program)
|| fflush (da_file))
fprintf (stderr, "profiling:%s:Error writing\n", ptr->filename); fprintf (stderr, "profiling:%s:Error writing\n", ptr->filename);
if (fclose (da_file)) if (gcov_close ())
fprintf (stderr, "profiling:%s:Error closing\n", ptr->filename); fprintf (stderr, "profiling:%s:Error closing\n", ptr->filename);
} }
} }
......
...@@ -27,7 +27,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA ...@@ -27,7 +27,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "basic-block.h" #include "basic-block.h"
#include "cfgloop.h" #include "cfgloop.h"
#include "cfglayout.h" #include "cfglayout.h"
#include "gcov-io.h"
#include "profile.h" #include "profile.h"
/* Initialize loop optimizer. */ /* Initialize loop optimizer. */
......
...@@ -101,6 +101,27 @@ struct function_list ...@@ -101,6 +101,27 @@ struct function_list
/* the sections */ /* the sections */
}; };
/* Counts information for a function. */
typedef struct counts_entry
{
/* We hash by */
char *function_name;
unsigned section;
/* Store */
unsigned checksum;
unsigned n_counts;
gcov_type *counts;
unsigned merged;
gcov_type max_counter;
gcov_type max_counter_sum;
/* Workspace */
struct counts_entry *chain;
} counts_entry_t;
static struct function_list *functions_head = 0; static struct function_list *functions_head = 0;
static struct function_list **functions_tail = &functions_head; static struct function_list **functions_tail = &functions_head;
...@@ -119,12 +140,10 @@ struct profile_info profile_info; ...@@ -119,12 +140,10 @@ struct profile_info profile_info;
/* Name and file pointer of the output file for the basic block graph. */ /* Name and file pointer of the output file for the basic block graph. */
static FILE *bbg_file;
static char *bbg_file_name; static char *bbg_file_name;
/* Name and file pointer of the input file for the arc count data. */ /* Name and file pointer of the input file for the arc count data. */
static FILE *da_file;
static char *da_file_name; static char *da_file_name;
/* The name of the count table. Used by the edge profiling code. */ /* The name of the count table. Used by the edge profiling code. */
...@@ -149,11 +168,10 @@ static void find_spanning_tree PARAMS ((struct edge_list *)); ...@@ -149,11 +168,10 @@ static void find_spanning_tree PARAMS ((struct edge_list *));
static rtx gen_edge_profiler PARAMS ((int)); static rtx gen_edge_profiler PARAMS ((int));
static void instrument_edges PARAMS ((struct edge_list *)); static void instrument_edges PARAMS ((struct edge_list *));
static void compute_branch_probabilities PARAMS ((void)); static void compute_branch_probabilities PARAMS ((void));
static hashval_t htab_counts_index_hash PARAMS ((const void *)); static hashval_t htab_counts_entry_hash PARAMS ((const void *));
static int htab_counts_index_eq PARAMS ((const void *, const void *)); static int htab_counts_entry_eq PARAMS ((const void *, const void *));
static void htab_counts_index_del PARAMS ((void *)); static void htab_counts_entry_del PARAMS ((void *));
static void cleanup_counts_index PARAMS ((int)); static void read_counts_file PARAMS ((const char *));
static int index_counts_file PARAMS ((void));
static gcov_type * get_exec_counts PARAMS ((void)); static gcov_type * get_exec_counts PARAMS ((void));
static unsigned compute_checksum PARAMS ((void)); static unsigned compute_checksum PARAMS ((void));
static basic_block find_group PARAMS ((basic_block)); static basic_block find_group PARAMS ((basic_block));
...@@ -218,106 +236,61 @@ instrument_edges (el) ...@@ -218,106 +236,61 @@ instrument_edges (el)
fprintf (rtl_dump_file, "%d edges instrumented\n", num_instr_edges); fprintf (rtl_dump_file, "%d edges instrumented\n", num_instr_edges);
} }
struct section_reference
{
long offset;
int owns_summary;
long *summary;
};
struct da_index_entry
{
/* We hash by */
char *function_name;
unsigned section;
/* and store */
unsigned checksum;
unsigned n_offsets;
struct section_reference *offsets;
};
static hashval_t static hashval_t
htab_counts_index_hash (of) htab_counts_entry_hash (of)
const void *of; const void *of;
{ {
const struct da_index_entry *entry = of; const counts_entry_t *entry = of;
return htab_hash_string (entry->function_name) ^ entry->section; return htab_hash_string (entry->function_name) ^ entry->section;
} }
static int static int
htab_counts_index_eq (of1, of2) htab_counts_entry_eq (of1, of2)
const void *of1; const void *of1;
const void *of2; const void *of2;
{ {
const struct da_index_entry *entry1 = of1; const counts_entry_t *entry1 = of1;
const struct da_index_entry *entry2 = of2; const counts_entry_t *entry2 = of2;
return !strcmp (entry1->function_name, entry2->function_name) return !strcmp (entry1->function_name, entry2->function_name)
&& entry1->section == entry2->section; && entry1->section == entry2->section;
} }
static void static void
htab_counts_index_del (what) htab_counts_entry_del (of)
void *what; void *of;
{ {
struct da_index_entry *entry = what; counts_entry_t *entry = of;
unsigned i;
for (i = 0; i < entry->n_offsets; i++)
{
struct section_reference *act = entry->offsets + i;
if (act->owns_summary)
free (act->summary);
}
free (entry->function_name); free (entry->function_name);
free (entry->offsets); free (entry->counts);
free (entry); free (entry);
} }
static char *counts_file_name; static htab_t counts_hash = NULL;
static htab_t counts_file_index = NULL;
static void static void
cleanup_counts_index (close_file) read_counts_file (const char *name)
int close_file;
{
if (da_file && close_file)
{
fclose (da_file);
da_file = NULL;
}
if (counts_file_name)
free (counts_file_name);
counts_file_name = NULL;
if (counts_file_index)
htab_delete (counts_file_index);
counts_file_index = NULL;
}
static int
index_counts_file ()
{ {
char *function_name_buffer = NULL; char *function_name_buffer = NULL;
unsigned magic, version, ix, checksum; unsigned magic, version, ix, checksum;
long *summary; counts_entry_t *summaried = NULL;
unsigned seen_summary = 0;
/* No .da file, no data. */
if (!da_file)
return 0;
counts_file_index = htab_create (10, htab_counts_index_hash, htab_counts_index_eq, htab_counts_index_del);
/* Now index all profile sections. */
rewind (da_file);
summary = NULL; if (!gcov_open (name, 1))
{
warning ("file %s not found, execution counts assumed to be zero", name);
return;
}
if (gcov_read_unsigned (da_file, &magic) || magic != GCOV_DATA_MAGIC) if (gcov_read_unsigned (&magic) || magic != GCOV_DATA_MAGIC)
{ {
warning ("`%s' is not a gcov data file", da_file_name); warning ("`%s' is not a gcov data file", name);
goto cleanup; gcov_close ();
return;
} }
if (gcov_read_unsigned (da_file, &version) || version != GCOV_VERSION) else if (gcov_read_unsigned (&version) || version != GCOV_VERSION)
{ {
char v[4], e[4]; char v[4], e[4];
magic = GCOV_VERSION; magic = GCOV_VERSION;
...@@ -327,97 +300,121 @@ index_counts_file () ...@@ -327,97 +300,121 @@ index_counts_file ()
v[ix] = version; v[ix] = version;
e[ix] = magic; e[ix] = magic;
} }
warning ("`%s' is version `%.4s', expected version `%.4s'", warning ("`%s' is version `%.4s', expected version `%.4s'", name, v, e);
da_file_name, v, e); gcov_close ();
goto cleanup; return;
} }
counts_hash = htab_create (10,
htab_counts_entry_hash, htab_counts_entry_eq,
htab_counts_entry_del);
while (1) while (1)
{ {
unsigned tag, length; unsigned tag, length;
long offset; long offset;
offset = gcov_save_position (da_file); offset = gcov_save_position ();
if (gcov_read_unsigned (da_file, &tag) if (gcov_read_unsigned (&tag) || gcov_read_unsigned (&length))
|| gcov_read_unsigned (da_file, &length))
{ {
if (feof (da_file)) if (gcov_eof ())
break; break;
corrupt:; corrupt:;
warning ("`%s' is corrupted", da_file_name); warning ("`%s' is corrupted", name);
goto cleanup; cleanup:
htab_delete (counts_hash);
break;
} }
if (tag == GCOV_TAG_FUNCTION) if (tag == GCOV_TAG_FUNCTION)
{ {
if (gcov_read_string (da_file, &function_name_buffer, NULL) if (gcov_read_string (&function_name_buffer)
|| gcov_read_unsigned (da_file, &checksum)) || gcov_read_unsigned (&checksum))
goto corrupt; goto corrupt;
continue; if (seen_summary)
{
/* We have already seen a summary, this means that this
new function begins a new set of program runs. We
must unlink the summaried chain. */
counts_entry_t *entry, *chain;
for (entry = summaried; entry; entry = chain)
{
chain = entry->chain;
entry->max_counter_sum += entry->max_counter;
entry->chain = NULL;
}
summaried = NULL;
seen_summary = 0;
}
} }
if (tag == GCOV_TAG_PROGRAM_SUMMARY) else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
{ {
if (length != GCOV_SUMMARY_LENGTH) counts_entry_t *entry;
struct gcov_summary summary;
if (length != GCOV_SUMMARY_LENGTH
|| gcov_read_summary (&summary))
goto corrupt; goto corrupt;
if (summary) seen_summary = 1;
*summary = offset; for (entry = summaried; entry; entry = entry->chain)
summary = NULL;
}
else
{ {
if (function_name_buffer) entry->merged += summary.runs;
if (entry->max_counter < summary.arc_sum_max)
entry->max_counter = summary.arc_sum_max;
}
}
else if (GCOV_TAG_IS_SUBTAG (GCOV_TAG_FUNCTION, tag)
&& function_name_buffer)
{ {
struct da_index_entry **slot, elt; counts_entry_t **slot, *entry, elt;
unsigned n_counts = length / 8;
unsigned ix;
gcov_type count;
elt.function_name = function_name_buffer; elt.function_name = function_name_buffer;
elt.section = tag; elt.section = tag;
slot = (struct da_index_entry **) slot = (counts_entry_t **) htab_find_slot
htab_find_slot (counts_file_index, &elt, INSERT); (counts_hash, &elt, INSERT);
if (*slot) entry = *slot;
if (!entry)
{ {
if ((*slot)->checksum != checksum) *slot = entry = xmalloc (sizeof (counts_entry_t));
entry->function_name = xstrdup (function_name_buffer);
entry->section = tag;
entry->checksum = checksum;
entry->n_counts = n_counts;
entry->counts = xcalloc (n_counts, sizeof (gcov_type));
}
else if (entry->checksum != checksum || entry->n_counts != n_counts)
{ {
warning ("profile mismatch for `%s'", function_name_buffer); warning ("profile mismatch for `%s'", function_name_buffer);
goto cleanup; goto cleanup;
} }
(*slot)->n_offsets++;
(*slot)->offsets = xrealloc ((*slot)->offsets, /* This should always be true for a just allocated entry,
sizeof (struct section_reference) * (*slot)->n_offsets); and always false for an existing one. Check this way, in
} case the gcov file is corrupt. */
else if (!entry->chain || summaried != entry)
{ {
*slot = xmalloc (sizeof (struct da_index_entry)); entry->chain = summaried;
(*slot)->function_name = xstrdup (function_name_buffer); summaried = entry;
(*slot)->section = tag;
(*slot)->checksum = checksum;
(*slot)->n_offsets = 1;
(*slot)->offsets = xmalloc (sizeof (struct section_reference));
} }
(*slot)->offsets[(*slot)->n_offsets - 1].offset = offset; for (ix = 0; ix != n_counts; ix++)
if (summary)
(*slot)->offsets[(*slot)->n_offsets - 1].owns_summary = 0;
else
{ {
summary = xmalloc (sizeof (long)); if (gcov_read_counter (&count))
*summary = -1; goto corrupt;
(*slot)->offsets[(*slot)->n_offsets - 1].owns_summary = 1; entry->counts[ix] += count;
}
(*slot)->offsets[(*slot)->n_offsets - 1].summary = summary;
} }
} }
if (gcov_skip (da_file, length)) else
if (gcov_skip (length))
goto corrupt; goto corrupt;
} }
free (function_name_buffer); free (function_name_buffer);
gcov_close ();
return 1;
cleanup:
cleanup_counts_index (1);
if (function_name_buffer)
free (function_name_buffer);
return 0;
} }
/* Computes hybrid profile for all matching entries in da_file. /* Computes hybrid profile for all matching entries in da_file.
...@@ -428,26 +425,17 @@ get_exec_counts () ...@@ -428,26 +425,17 @@ get_exec_counts ()
{ {
unsigned num_edges = 0; unsigned num_edges = 0;
basic_block bb; basic_block bb;
gcov_type *profile;
gcov_type max_count;
unsigned ix, i, tag, length, num;
const char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)); const char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl));
struct da_index_entry *entry, what; counts_entry_t *entry, elt;
struct section_reference *act;
gcov_type count;
struct gcov_summary summ;
profile_info.max_counter_in_program = 0; profile_info.max_counter_in_program = 0;
profile_info.count_profiles_merged = 0; profile_info.count_profiles_merged = 0;
/* No .da file, no execution counts. */ /* No hash table, no counts. */
if (!da_file) if (!counts_hash)
return NULL; return NULL;
if (!counts_file_index)
abort ();
/* Count the edges to be (possibly) instrumented. */ /* Count the edges to be (possibly) instrumented. */
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb) FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
{ {
edge e; edge e;
...@@ -456,81 +444,24 @@ get_exec_counts () ...@@ -456,81 +444,24 @@ get_exec_counts ()
num_edges++; num_edges++;
} }
/* now read and combine all matching profiles. */ elt.function_name = (char *) name;
elt.section = GCOV_TAG_ARC_COUNTS;
profile = xmalloc (sizeof (gcov_type) * num_edges); entry = htab_find (counts_hash, &elt);
for (ix = 0; ix < num_edges; ix++)
profile[ix] = 0;
what.function_name = (char *) name;
what.section = GCOV_TAG_ARC_COUNTS;
entry = htab_find (counts_file_index, &what);
if (!entry) if (!entry)
{ {
warning ("No profile for function '%s' found.", name); warning ("No profile for function '%s' found.", name);
goto cleanup; return NULL;
}
if (entry->checksum != profile_info.current_function_cfg_checksum)
{
warning ("profile mismatch for `%s'", current_function_name);
goto cleanup;
}
for (i = 0; i < entry->n_offsets; i++)
{
act = entry->offsets + i;
/* Read arc counters. */
max_count = 0;
gcov_resync (da_file, act->offset, 0);
if (gcov_read_unsigned (da_file, &tag)
|| gcov_read_unsigned (da_file, &length)
|| tag != GCOV_TAG_ARC_COUNTS)
{
/* We have already passed through file, so any error means
something is rotten. */
abort ();
} }
num = length / 8;
if (num != num_edges) if (entry->checksum != profile_info.current_function_cfg_checksum
|| num_edges != entry->n_counts)
{ {
warning ("profile mismatch for `%s'", current_function_name); warning ("profile mismatch for `%s'", current_function_name);
goto cleanup; return NULL;
} }
for (ix = 0; ix != num; ix++) profile_info.count_profiles_merged = entry->merged;
{ profile_info.max_counter_in_program = entry->max_counter_sum;
if (gcov_read_counter (da_file, &count))
abort ();
if (count > max_count)
max_count = count;
profile[ix] += count;
}
/* Read program summary. */
if (*act->summary != -1)
{
gcov_resync (da_file, *act->summary, 0);
if (gcov_read_unsigned (da_file, &tag)
|| gcov_read_unsigned (da_file, &length)
|| tag != GCOV_TAG_PROGRAM_SUMMARY
|| gcov_read_summary (da_file, &summ))
abort ();
profile_info.count_profiles_merged += summ.runs;
profile_info.max_counter_in_program += summ.arc_sum_max;
}
else
summ.runs = 0;
if (!summ.runs)
{
profile_info.count_profiles_merged++;
profile_info.max_counter_in_program += max_count;
}
}
if (rtl_dump_file) if (rtl_dump_file)
{ {
...@@ -539,12 +470,7 @@ get_exec_counts () ...@@ -539,12 +470,7 @@ get_exec_counts ()
(int)profile_info.max_counter_in_program); (int)profile_info.max_counter_in_program);
} }
return profile; return entry->counts;
cleanup:;
free (profile);
cleanup_counts_index (1);
return NULL;
} }
...@@ -858,8 +784,6 @@ compute_branch_probabilities () ...@@ -858,8 +784,6 @@ compute_branch_probabilities ()
} }
free_aux_for_blocks (); free_aux_for_blocks ();
if (exec_counts)
free (exec_counts);
find_counters_section (GCOV_TAG_ARC_COUNTS)->present = 1; find_counters_section (GCOV_TAG_ARC_COUNTS)->present = 1;
} }
...@@ -1083,32 +1007,30 @@ branch_prob () ...@@ -1083,32 +1007,30 @@ branch_prob ()
edge output the source and target basic block numbers. edge output the source and target basic block numbers.
NOTE: The format of this file must be compatible with gcov. */ NOTE: The format of this file must be compatible with gcov. */
if (flag_test_coverage && bbg_file) if (gcov_ok ())
{ {
long offset; long offset;
const char *file = DECL_SOURCE_FILE (current_function_decl); const char *file = DECL_SOURCE_FILE (current_function_decl);
unsigned line = DECL_SOURCE_LINE (current_function_decl); unsigned line = DECL_SOURCE_LINE (current_function_decl);
/* Announce function */ /* Announce function */
if (gcov_write_unsigned (bbg_file, GCOV_TAG_FUNCTION) if (gcov_write_unsigned (GCOV_TAG_FUNCTION)
|| !(offset = gcov_reserve_length (bbg_file)) || !(offset = gcov_reserve_length ())
|| gcov_write_string (bbg_file, name, || gcov_write_string (name)
strlen (name)) || gcov_write_unsigned (profile_info.current_function_cfg_checksum)
|| gcov_write_unsigned (bbg_file, || gcov_write_string (file)
profile_info.current_function_cfg_checksum) || gcov_write_unsigned (line)
|| gcov_write_string (bbg_file, file, strlen (file)) || gcov_write_length (offset))
|| gcov_write_unsigned (bbg_file, line)
|| gcov_write_length (bbg_file, offset))
goto bbg_error; goto bbg_error;
/* Basic block flags */ /* Basic block flags */
if (gcov_write_unsigned (bbg_file, GCOV_TAG_BLOCKS) if (gcov_write_unsigned (GCOV_TAG_BLOCKS)
|| !(offset = gcov_reserve_length (bbg_file))) || !(offset = gcov_reserve_length ()))
goto bbg_error; goto bbg_error;
for (i = 0; i != (unsigned) (n_basic_blocks + 2); i++) for (i = 0; i != (unsigned) (n_basic_blocks + 2); i++)
if (gcov_write_unsigned (bbg_file, 0)) if (gcov_write_unsigned (0))
goto bbg_error; goto bbg_error;
if (gcov_write_length (bbg_file, offset)) if (gcov_write_length (offset))
goto bbg_error; goto bbg_error;
/* Arcs */ /* Arcs */
...@@ -1116,9 +1038,9 @@ branch_prob () ...@@ -1116,9 +1038,9 @@ branch_prob ()
{ {
edge e; edge e;
if (gcov_write_unsigned (bbg_file, GCOV_TAG_ARCS) if (gcov_write_unsigned (GCOV_TAG_ARCS)
|| !(offset = gcov_reserve_length (bbg_file)) || !(offset = gcov_reserve_length ())
|| gcov_write_unsigned (bbg_file, BB_TO_GCOV_INDEX (bb))) || gcov_write_unsigned (BB_TO_GCOV_INDEX (bb)))
goto bbg_error; goto bbg_error;
for (e = bb->succ; e; e = e->succ_next) for (e = bb->succ; e; e = e->succ_next)
...@@ -1135,14 +1057,13 @@ branch_prob () ...@@ -1135,14 +1057,13 @@ branch_prob ()
if (e->flags & EDGE_FALLTHRU) if (e->flags & EDGE_FALLTHRU)
flag_bits |= GCOV_ARC_FALLTHROUGH; flag_bits |= GCOV_ARC_FALLTHROUGH;
if (gcov_write_unsigned (bbg_file, if (gcov_write_unsigned (BB_TO_GCOV_INDEX (e->dest))
BB_TO_GCOV_INDEX (e->dest)) || gcov_write_unsigned (flag_bits))
|| gcov_write_unsigned (bbg_file, flag_bits))
goto bbg_error; goto bbg_error;
} }
} }
if (gcov_write_length (bbg_file, offset)) if (gcov_write_length (offset))
goto bbg_error; goto bbg_error;
} }
...@@ -1185,10 +1106,10 @@ branch_prob () ...@@ -1185,10 +1106,10 @@ branch_prob ()
{ {
if (offset) if (offset)
/*NOP*/; /*NOP*/;
else if (gcov_write_unsigned (bbg_file, GCOV_TAG_LINES) else if (gcov_write_unsigned (GCOV_TAG_LINES)
|| !(offset = gcov_reserve_length (bbg_file)) || !(offset = gcov_reserve_length ())
|| gcov_write_unsigned (bbg_file, || (gcov_write_unsigned
BB_TO_GCOV_INDEX (bb))) (BB_TO_GCOV_INDEX (bb))))
goto bbg_error; goto bbg_error;
/* If this is a new source file, then output /* If this is a new source file, then output
the file's name to the .bb file. */ the file's name to the .bb file. */
...@@ -1197,12 +1118,11 @@ branch_prob () ...@@ -1197,12 +1118,11 @@ branch_prob ()
prev_file_name)) prev_file_name))
{ {
prev_file_name = NOTE_SOURCE_FILE (insn); prev_file_name = NOTE_SOURCE_FILE (insn);
if (gcov_write_unsigned (bbg_file, 0) if (gcov_write_unsigned (0)
|| gcov_write_string (bbg_file, prev_file_name, || gcov_write_string (prev_file_name))
strlen (prev_file_name)))
goto bbg_error; goto bbg_error;
} }
if (gcov_write_unsigned (bbg_file, NOTE_LINE_NUMBER (insn))) if (gcov_write_unsigned (NOTE_LINE_NUMBER (insn)))
goto bbg_error; goto bbg_error;
} }
} }
...@@ -1211,14 +1131,13 @@ branch_prob () ...@@ -1211,14 +1131,13 @@ branch_prob ()
if (offset) if (offset)
{ {
if (gcov_write_unsigned (bbg_file, 0) if (gcov_write_unsigned (0)
|| gcov_write_string (bbg_file, NULL, 0) || gcov_write_string (NULL)
|| gcov_write_length (bbg_file, offset)) || gcov_write_length (offset))
{ {
bbg_error:; bbg_error:;
warning ("error writing `%s'", bbg_file_name); warning ("error writing `%s'", bbg_file_name);
fclose (bbg_file); gcov_error ();
bbg_file = NULL;
} }
} }
} }
...@@ -1395,38 +1314,27 @@ init_branch_prob (filename) ...@@ -1395,38 +1314,27 @@ init_branch_prob (filename)
int len = strlen (filename); int len = strlen (filename);
int i; int i;
da_file_name = (char *) xmalloc (len + strlen (GCOV_DATA_SUFFIX) + 1);
strcpy (da_file_name, filename);
strcat (da_file_name, GCOV_DATA_SUFFIX);
if (flag_branch_probabilities)
read_counts_file (da_file_name);
if (flag_test_coverage) if (flag_test_coverage)
{ {
/* Open the bbg output file. */ /* Open the bbg output file. */
bbg_file_name = (char *) xmalloc (len + strlen (GCOV_GRAPH_SUFFIX) + 1); bbg_file_name = (char *) xmalloc (len + strlen (GCOV_GRAPH_SUFFIX) + 1);
strcpy (bbg_file_name, filename); strcpy (bbg_file_name, filename);
strcat (bbg_file_name, GCOV_GRAPH_SUFFIX); strcat (bbg_file_name, GCOV_GRAPH_SUFFIX);
bbg_file = fopen (bbg_file_name, "wb"); if (!gcov_open (bbg_file_name, -1))
if (!bbg_file)
fatal_io_error ("cannot open %s", bbg_file_name);
if (gcov_write_unsigned (bbg_file, GCOV_GRAPH_MAGIC)
|| gcov_write_unsigned (bbg_file, GCOV_VERSION))
{ {
fclose (bbg_file); error ("cannot open %s", bbg_file_name);
fatal_io_error ("cannot write `%s'", bbg_file_name); gcov_error ();
} }
} else if (gcov_write_unsigned (GCOV_GRAPH_MAGIC)
|| gcov_write_unsigned (GCOV_VERSION))
da_file_name = (char *) xmalloc (len + strlen (GCOV_DATA_SUFFIX) + 1); gcov_error ();
strcpy (da_file_name, filename);
strcat (da_file_name, GCOV_DATA_SUFFIX);
if (flag_branch_probabilities)
{
da_file = fopen (da_file_name, "rb");
if (!da_file)
warning ("file %s not found, execution counts assumed to be zero",
da_file_name);
if (counts_file_index && strcmp (da_file_name, counts_file_name))
cleanup_counts_index (0);
if (index_counts_file ())
counts_file_name = xstrdup (da_file_name);
} }
if (profile_arc_flag) if (profile_arc_flag)
...@@ -1459,26 +1367,19 @@ end_branch_prob () ...@@ -1459,26 +1367,19 @@ end_branch_prob ()
{ {
if (flag_test_coverage) if (flag_test_coverage)
{ {
if (bbg_file) int error = gcov_close ();
{
#if !SELF_COVERAGE if (error)
/* If the compiler is instrumented, we should not remove the
counts file, because we might be recompiling
ourselves. The .da files are all removed during copying
the stage1 files. */
unlink (da_file_name);
#endif
fclose (bbg_file);
}
else
{
unlink (bbg_file_name); unlink (bbg_file_name);
#if SELF_COVERAGE
/* If the compiler is instrumented, we should not
unconditionally remove the counts file, because we might be
recompiling ourselves. The .da files are all removed during
copying the stage1 files. */
if (error)
#endif
unlink (da_file_name); unlink (da_file_name);
} }
}
if (da_file)
fclose (da_file);
if (rtl_dump_file) if (rtl_dump_file)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment