/* Gcov.c: prepend line execution counts and branch probabilities to a
source file.
Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
Contributed by James E. Wilson of Cygnus Support.
Mangled by Bob Manson of Cygnus Support.
Mangled further by Nathan Sidwell <nathan@codesourcery.com>
You should have received a copy of the GNU General Public License
along with Gcov; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
/* ??? Print a list of the ten blocks with the highest execution counts,
and list the line numbers corresponding to those blocks. Also, perhaps
#include "tm.h"
#include "intl.h"
#include "version.h"
-#undef abort
#include <getopt.h>
-typedef HOST_WIDEST_INT gcov_type;
+#define IN_GCOV 1
#include "gcov-io.h"
+#include "gcov-io.c"
/* The bbg file is generated by -ftest-coverage option. The da file is
generated by a program compiled with -fprofile-arcs. Their formats
/* transition counts. */
gcov_type count;
+ /* used in cycle search, so that we do not clobber original counts. */
+ gcov_type cs_count;
unsigned int count_valid : 1;
unsigned int on_tree : 1;
/* Is an unconditional branch. */
unsigned int is_unconditional : 1;
- /* Arc on the local block spanning tree. */
- unsigned int local_span : 1;
-
+ /* Loop making arc. */
+ unsigned int cycle : 1;
+
/* Next branch on line. */
struct arc_info *line_next;
-
+
/* Links to next arc on src and dst lists. */
struct arc_info *succ_next;
struct arc_info *pred_next;
unsigned invalid_chain : 1;
/* Block is a call instrumenting site. */
- unsigned is_call_site : 1;
+ unsigned is_call_site : 1; /* Does the call. */
+ unsigned is_call_return : 1; /* Is the return. */
/* Block is a landing pad for longjmp or throw. */
unsigned is_nonlocal_return : 1;
} line; /* Valid until blocks are linked onto lines */
struct
{
- /* Single line spanning tree workspace. Used for all-blocks mode. */
- struct block_info *root;
- unsigned siblings;
- } span; /* Used in all-blocks mode, after blocks are linked onto
- lines. */
+ /* Single line graph cycle workspace. Used for all-blocks
+ mode. */
+ arc_t *arc;
+ unsigned ident;
+ } cycle; /* Used in all-blocks mode, after blocks are linked onto
+ lines. */
} u;
/* Temporary chain for solving graph, and for chaining blocks on one
line. */
struct block_info *chain;
-
+
} block_t;
/* Describes a single function. Contains an array of basic blocks. */
{
/* Name of function. */
char *name;
+ unsigned ident;
unsigned checksum;
/* Array of basic blocks. */
/* Next function in same source file. */
struct function_info *line_next;
-
+
/* Next function. */
struct function_info *next;
} function_t;
{
int lines;
int lines_executed;
-
+
int branches;
int branches_executed;
int branches_taken;
-
+
int calls;
int calls_executed;
-
+
char *name;
} coverage_t;
gcov_type count; /* execution count */
union
{
- arc_t *branches; /* branches from blocks that end on this
+ arc_t *branches; /* branches from blocks that end on this
line. Used for branch-counts when not
- all-blocks mode. */
+ all-blocks mode. */
block_t *blocks; /* blocks which start on this line. Used
- in all-blocks mode. */
+ in all-blocks mode. */
} u;
unsigned exists : 1;
} line_t;
/* Functions in this source file. These are in ascending line
number order. */
function_t *functions;
-
+
/* Next source file. */
struct source_info *next;
} source_t;
static char *bbg_file_name;
+/* Stamp of the bbg file */
+static unsigned bbg_stamp;
+
/* Name and file pointer of the input file for the arc count data. */
static char *da_file_name;
static int flag_branches = 0;
-/* Show unconditional branches too. */
+/* Show unconditional branches too. */
static int flag_unconditional = 0;
/* Output a gcov file if this is true. This is on by default, and can
static int flag_counts = 0;
/* Forward declarations. */
-static void fnotice PARAMS ((FILE *, const char *, ...)) ATTRIBUTE_PRINTF_2;
-static int process_args PARAMS ((int, char **));
-static void print_usage PARAMS ((int)) ATTRIBUTE_NORETURN;
-static void print_version PARAMS ((void)) ATTRIBUTE_NORETURN;
-static void process_file PARAMS ((const char *));
-static void create_file_names PARAMS ((const char *));
-static source_t *find_source PARAMS ((char *));
-static int read_graph_file PARAMS ((void));
-static int read_count_file PARAMS ((void));
-static void solve_flow_graph PARAMS ((function_t *));
-static void add_branch_counts PARAMS ((coverage_t *, const arc_t *));
-static void add_line_counts PARAMS ((coverage_t *, function_t *));
-static void function_summary PARAMS ((const coverage_t *, const char *));
-static const char *format_gcov PARAMS ((gcov_type, gcov_type, int));
-static void accumulate_line_counts PARAMS ((source_t *));
-static int output_branch_count PARAMS ((FILE *, int, const arc_t *));
-static void output_lines PARAMS ((FILE *, const source_t *));
-static char *make_gcov_file_name PARAMS ((const char *, const char *));
-static void release_structures PARAMS ((void));
-extern int main PARAMS ((int, char **));
+static void fnotice (FILE *, const char *, ...) ATTRIBUTE_PRINTF_2;
+static int process_args (int, char **);
+static void print_usage (int) ATTRIBUTE_NORETURN;
+static void print_version (void) ATTRIBUTE_NORETURN;
+static void process_file (const char *);
+static void create_file_names (const char *);
+static source_t *find_source (const char *);
+static int read_graph_file (void);
+static int read_count_file (void);
+static void solve_flow_graph (function_t *);
+static void add_branch_counts (coverage_t *, const arc_t *);
+static void add_line_counts (coverage_t *, function_t *);
+static void function_summary (const coverage_t *, const char *);
+static const char *format_gcov (gcov_type, gcov_type, int);
+static void accumulate_line_counts (source_t *);
+static int output_branch_count (FILE *, int, const arc_t *);
+static void output_lines (FILE *, const source_t *);
+static char *make_gcov_file_name (const char *, const char *);
+static void release_structures (void);
+extern int main (int, char **);
int
-main (argc, argv)
- int argc;
- char **argv;
+main (int argc, char **argv)
{
int argno;
-
+
+ /* Unlock the stdio streams. */
+ unlock_std_streams ();
+
gcc_init_libintl ();
argno = process_args (argc, argv);
for (; argno != argc; argno++)
{
release_structures ();
-
+
process_file (argv[argno]);
}
-
+
return 0;
}
static void
-fnotice VPARAMS ((FILE *file, const char *msgid, ...))
+fnotice (FILE *file, const char *cmsgid, ...)
{
- VA_OPEN (ap, msgid);
- VA_FIXEDARG (ap, FILE *, file);
- VA_FIXEDARG (ap, const char *, msgid);
+ va_list ap;
- vfprintf (file, _(msgid), ap);
- VA_CLOSE (ap);
-}
-
-/* More 'friendly' abort that prints the line and file.
- config.h can #define abort fancy_abort if you like that sort of thing. */
-extern void fancy_abort PARAMS ((void)) ATTRIBUTE_NORETURN;
-
-void
-fancy_abort ()
-{
- fnotice (stderr, "Internal gcov abort.\n");
- exit (FATAL_EXIT_CODE);
+ va_start (ap, cmsgid);
+ vfprintf (file, _(cmsgid), ap);
+ va_end (ap);
}
\f
/* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
otherwise the output of --help. */
static void
-print_usage (error_p)
- int error_p;
+print_usage (int error_p)
{
FILE *file = error_p ? stderr : stdout;
int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
-
+
fnotice (file, "Usage: gcov [OPTION]... SOURCEFILE\n\n");
fnotice (file, "Print code coverage information.\n\n");
fnotice (file, " -h, --help Print this help, then exit\n");
/* Print version information and exit. */
static void
-print_version ()
+print_version (void)
{
- char v[4];
- unsigned version = GCOV_VERSION;
- unsigned ix;
-
- for (ix = 4; ix--; version >>= 8)
- v[ix] = version;
- fnotice (stdout, "gcov %.4s (GCC %s)\n", v, version_string);
- fnotice (stdout, "Copyright (C) 2002 Free Software Foundation, Inc.\n");
+ fnotice (stdout, "gcov (GCC) %s\n", version_string);
+ fprintf (stdout, "Copyright %s 2004 Free Software Foundation, Inc.\n",
+ _("(C)"));
fnotice (stdout,
- "This is free software; see the source for copying conditions. There is NO\n\
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
+ _("This is free software; see the source for copying conditions.\n"
+ "There is NO warranty; not even for MERCHANTABILITY or \n"
+ "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
exit (SUCCESS_EXIT_CODE);
}
{ "object-directory", required_argument, NULL, 'o' },
{ "object-file", required_argument, NULL, 'o' },
{ "unconditional-branches", no_argument, NULL, 'u' },
+ { 0, 0, 0, 0 }
};
/* Process args, return index to first non-arg. */
static int
-process_args (argc, argv)
- int argc;
- char **argv;
+process_args (int argc, char **argv)
{
int opt;
/* Process a single source file. */
static void
-process_file (file_name)
- const char *file_name;
+process_file (const char *file_name)
{
source_t *src;
function_t *fn;
-
+
create_file_names (file_name);
if (read_graph_file ())
return;
-
+
if (!functions)
{
fnotice (stderr, "%s:no functions found\n", bbg_file_name);
return;
}
-
+
if (read_count_file ())
return;
-
+
for (fn = functions; fn; fn = fn->next)
solve_flow_graph (fn);
for (src = sources; src; src = src->next)
- src->lines = (line_t *) xcalloc (src->num_lines, sizeof (line_t));
+ src->lines = xcalloc (src->num_lines, sizeof (line_t));
for (fn = functions; fn; fn = fn->next)
{
coverage_t coverage;
-
+
memset (&coverage, 0, sizeof (coverage));
coverage.name = fn->name;
add_line_counts (flag_function_summary ? &coverage : NULL, fn);
fnotice (stdout, "\n");
}
}
-
+
for (src = sources; src; src = src->next)
{
accumulate_line_counts (src);
{
char *gcov_file_name = make_gcov_file_name (file_name, src->name);
FILE *gcov_file = fopen (gcov_file_name, "w");
-
+
if (gcov_file)
{
- fnotice (stdout, "%s:creating `%s'\n",
+ fnotice (stdout, "%s:creating '%s'\n",
src->name, gcov_file_name);
output_lines (gcov_file, src);
if (ferror (gcov_file))
- fnotice (stderr, "%s:error writing output file `%s'\n",
+ fnotice (stderr, "%s:error writing output file '%s'\n",
src->name, gcov_file_name);
fclose (gcov_file);
}
else
- fnotice (stderr, "%s:could not open output file `%s'\n",
+ fnotice (stderr, "%s:could not open output file '%s'\n",
src->name, gcov_file_name);
free (gcov_file_name);
}
/* Release all memory used. */
static void
-release_structures ()
+release_structures (void)
{
function_t *fn;
source_t *src;
-
+
free (bbg_file_name);
free (da_file_name);
da_file_name = bbg_file_name = NULL;
bbg_file_time = 0;
-
+ bbg_stamp = 0;
+
while ((src = sources))
{
sources = src->next;
free (src->name);
free (src->lines);
}
-
+
while ((fn = functions))
{
unsigned ix;
block_t *block;
-
+
functions = fn->next;
for (ix = fn->num_blocks, block = fn->blocks; ix--; block++)
{
the object *file*, and the data files are named from that. */
static void
-create_file_names (file_name)
- const char *file_name;
+create_file_names (const char *file_name)
{
char *cptr;
char *name;
int length = strlen (file_name);
int base;
-
+
if (object_directory && object_directory[0])
{
struct stat status;
length += strlen (object_directory) + 2;
name = xmalloc (length);
name[0] = 0;
-
+
base = !stat (object_directory, &status) && S_ISDIR (status.st_mode);
strcat (name, object_directory);
if (base && name[strlen (name) - 1] != '/')
name[0] = 0;
base = 1;
}
-
+
if (base)
{
- /* Append source file name */
+ /* Append source file name. */
cptr = strrchr (file_name, '/');
strcat (name, cptr ? cptr + 1 : file_name);
}
-
+
/* Remove the extension. */
cptr = strrchr (name, '.');
if (cptr)
*cptr = 0;
-
+
length = strlen (name);
- bbg_file_name = xmalloc (length + strlen (GCOV_GRAPH_SUFFIX) + 1);
+ bbg_file_name = xmalloc (length + strlen (GCOV_NOTE_SUFFIX) + 1);
strcpy (bbg_file_name, name);
- strcpy (bbg_file_name + length, GCOV_GRAPH_SUFFIX);
+ strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX);
da_file_name = xmalloc (length + strlen (GCOV_DATA_SUFFIX) + 1);
strcpy (da_file_name, name);
strcpy (da_file_name + length, GCOV_DATA_SUFFIX);
-
+
return;
}
-/* Find or create a source file structure for FILE_NAME. Free
- FILE_NAME appropriately */
+/* Find or create a source file structure for FILE_NAME. Copies
+ FILE_NAME on creation */
static source_t *
-find_source (file_name)
- char *file_name;
+find_source (const char *file_name)
{
-
source_t *src;
-
+
+ if (!file_name)
+ file_name = "<unknown>";
+
for (src = sources; src; src = src->next)
if (!strcmp (file_name, src->name))
- {
- free (file_name);
- break;
- }
- if (!src)
- {
- src = (source_t *)xcalloc (1, sizeof (source_t));
- src->name = file_name;
- src->coverage.name = file_name;
- src->index = sources ? sources->index + 1 : 1;
- src->next = sources;
- sources = src;
- }
+ return src;
+
+ src = xcalloc (1, sizeof (source_t));
+ src->name = xstrdup (file_name);
+ src->coverage.name = src->name;
+ src->index = sources ? sources->index + 1 : 1;
+ src->next = sources;
+ sources = src;
+
return src;
}
/* Read the graph file. Return nonzero on fatal error. */
static int
-read_graph_file ()
+read_graph_file (void)
{
- FILE *file;
- struct stat status;
- unsigned magic, version;
+ unsigned version;
unsigned current_tag = 0;
- unsigned tag;
struct function_info *fn = NULL;
source_t *src = NULL;
unsigned ix;
+ unsigned tag;
- file = fopen (bbg_file_name, "rb");
- if (!file)
+ if (!gcov_open (bbg_file_name, 1))
{
fnotice (stderr, "%s:cannot open graph file\n", bbg_file_name);
return 1;
}
- if (!fstat (fileno (file), &status))
- bbg_file_time = status.st_mtime;
- if (gcov_read_unsigned (file, &magic) || magic != GCOV_GRAPH_MAGIC)
+ bbg_file_time = gcov_time ();
+ if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC))
{
fnotice (stderr, "%s:not a gcov graph file\n", bbg_file_name);
- fclose (file);
+ gcov_close ();
return 1;
}
- if (gcov_read_unsigned (file, &version) || version != GCOV_VERSION)
+ version = gcov_read_unsigned ();
+ if (version != GCOV_VERSION)
{
char v[4], e[4];
- magic = GCOV_VERSION;
-
- for (ix = 4; ix--; magic >>= 8, version >>= 8)
- {
- v[ix] = version;
- e[ix] = magic;
- }
- fnotice (stderr, "%s:version `%.4s', prefer `%.4s'\n",
+ GCOV_UNSIGNED2STRING (v, version);
+ GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
+
+ fnotice (stderr, "%s:version '%.4s', prefer '%.4s'\n",
bbg_file_name, v, e);
}
-
- while (!gcov_read_unsigned (file, &tag))
- {
- unsigned length;
- long base;
+ bbg_stamp = gcov_read_unsigned ();
- if (gcov_read_unsigned (file, &length))
- goto corrupt;
-
- base = gcov_save_position (file);
+ while ((tag = gcov_read_unsigned ()))
+ {
+ unsigned length = gcov_read_unsigned ();
+ gcov_position_t base = gcov_position ();
if (tag == GCOV_TAG_FUNCTION)
{
- char *function_name = NULL;
- char *function_file = NULL;
- unsigned checksum, lineno;
+ char *function_name;
+ unsigned ident, checksum, lineno;
source_t *src;
function_t *probe, *prev;
- if (gcov_read_string (file, &function_name, NULL)
- || gcov_read_unsigned (file, &checksum)
- || gcov_read_string (file, &function_file, NULL)
- || gcov_read_unsigned (file, &lineno))
- goto corrupt;
- src = find_source (function_file);
- fn = (function_t *)xcalloc (1, sizeof (function_t));
+ ident = gcov_read_unsigned ();
+ checksum = gcov_read_unsigned ();
+ function_name = xstrdup (gcov_read_string ());
+ src = find_source (gcov_read_string ());
+ lineno = gcov_read_unsigned ();
+
+ fn = xcalloc (1, sizeof (function_t));
fn->name = function_name;
+ fn->ident = ident;
fn->checksum = checksum;
fn->src = src;
fn->line = lineno;
fn->next = functions;
functions = fn;
current_tag = tag;
-
+
if (lineno >= src->num_lines)
src->num_lines = lineno + 1;
/* Now insert it into the source file's list of
else if (fn && tag == GCOV_TAG_BLOCKS)
{
if (fn->blocks)
- fnotice (stderr, "%s:already seen blocks for `%s'\n",
+ fnotice (stderr, "%s:already seen blocks for '%s'\n",
bbg_file_name, fn->name);
else
{
- unsigned ix, num_blocks = length / 4;
+ unsigned ix, num_blocks = GCOV_TAG_BLOCKS_NUM (length);
fn->num_blocks = num_blocks;
-
- fn->blocks
- = (block_t *)xcalloc (fn->num_blocks, sizeof (block_t));
+
+ fn->blocks = xcalloc (fn->num_blocks, sizeof (block_t));
for (ix = 0; ix != num_blocks; ix++)
- {
- unsigned flags;
-
- if (gcov_read_unsigned (file, &flags))
- goto corrupt;
- fn->blocks[ix].flags = flags;
- }
+ fn->blocks[ix].flags = gcov_read_unsigned ();
}
}
else if (fn && tag == GCOV_TAG_ARCS)
{
- unsigned src;
- unsigned num_dests = (length - 4) / 8;
- unsigned dest, flags;
+ unsigned src = gcov_read_unsigned ();
+ unsigned num_dests = GCOV_TAG_ARCS_NUM (length);
- if (gcov_read_unsigned (file, &src)
- || src >= fn->num_blocks
- || fn->blocks[src].succ)
+ if (src >= fn->num_blocks || fn->blocks[src].succ)
goto corrupt;
-
+
while (num_dests--)
{
struct arc_info *arc;
-
- if (gcov_read_unsigned (file, &dest)
- || gcov_read_unsigned (file, &flags)
- || dest >= fn->num_blocks)
+ unsigned dest = gcov_read_unsigned ();
+ unsigned flags = gcov_read_unsigned ();
+
+ if (dest >= fn->num_blocks)
goto corrupt;
- arc = (arc_t *) xcalloc (1, sizeof (arc_t));
-
+ arc = xcalloc (1, sizeof (arc_t));
+
arc->dst = &fn->blocks[dest];
arc->src = &fn->blocks[src];
-
+
arc->count = 0;
arc->count_valid = 0;
arc->on_tree = !!(flags & GCOV_ARC_ON_TREE);
arc->fake = !!(flags & GCOV_ARC_FAKE);
arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH);
-
+
arc->succ_next = fn->blocks[src].succ;
fn->blocks[src].succ = arc;
fn->blocks[src].num_succ++;
-
+
arc->pred_next = fn->blocks[dest].pred;
fn->blocks[dest].pred = arc;
fn->blocks[dest].num_pred++;
else
{
/* Non-local return from a callee of this
- function. The destination block is a catch or
- setjmp. */
+ function. The destination block is a catch or
+ setjmp. */
arc->is_nonlocal_return = 1;
fn->blocks[dest].is_nonlocal_return = 1;
}
}
-
+
if (!arc->on_tree)
fn->num_counts++;
}
}
else if (fn && tag == GCOV_TAG_LINES)
{
- unsigned blockno;
- unsigned *line_nos
- = (unsigned *)xcalloc ((length - 4) / 4, sizeof (unsigned));
+ unsigned blockno = gcov_read_unsigned ();
+ unsigned *line_nos = xcalloc (length - 1, sizeof (unsigned));
- if (gcov_read_unsigned (file, &blockno)
- || blockno >= fn->num_blocks
- || fn->blocks[blockno].u.line.encoding)
+ if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding)
goto corrupt;
-
+
for (ix = 0; ; )
{
- unsigned lineno;
-
- if (gcov_read_unsigned (file, &lineno))
- goto corrupt;
+ unsigned lineno = gcov_read_unsigned ();
+
if (lineno)
{
if (!ix)
}
else
{
- char *file_name = NULL;
-
- if (gcov_read_string (file, &file_name, NULL))
- goto corrupt;
+ const char *file_name = gcov_read_string ();
+
if (!file_name)
break;
src = find_source (file_name);
-
+
line_nos[ix++] = 0;
line_nos[ix++] = src->index;
}
}
-
+
fn->blocks[blockno].u.line.encoding = line_nos;
fn->blocks[blockno].u.line.num = ix;
}
fn = NULL;
current_tag = 0;
}
- if (gcov_resync (file, base, length))
+ gcov_sync (base, length);
+ if (gcov_is_error ())
{
corrupt:;
fnotice (stderr, "%s:corrupted\n", bbg_file_name);
- fclose (file);
+ gcov_close ();
return 1;
}
}
- fclose (file);
-
- /* We built everything backwards, so nreverse them all */
-
+ gcov_close ();
+
+ /* We built everything backwards, so nreverse them all. */
+
/* Reverse sources. Not strictly necessary, but we'll then process
them in the 'expected' order. */
{
for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn_n)
{
unsigned ix;
-
+
fn_n = fn->next;
fn->next = fn_p;
- /* Reverse the arcs */
+ /* Reverse the arcs. */
for (ix = fn->num_blocks; ix--;)
{
arc_t *arc, *arc_p, *arc_n;
-
+
for (arc_p = NULL, arc = fn->blocks[ix].succ; arc;
arc_p = arc, arc = arc_n)
{
function. Return nonzero if fatal error. */
static int
-read_count_file ()
+read_count_file (void)
{
- FILE *file;
unsigned ix;
- char *function_name_buffer = NULL;
- unsigned magic, version;
+ unsigned version;
+ unsigned tag;
function_t *fn = NULL;
+ int error = 0;
- file = fopen (da_file_name, "rb");
- if (!file)
+ if (!gcov_open (da_file_name, 1))
{
fnotice (stderr, "%s:cannot open data file\n", da_file_name);
return 1;
}
- if (gcov_read_unsigned (file, &magic) || magic != GCOV_DATA_MAGIC)
+ if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
{
fnotice (stderr, "%s:not a gcov data file\n", da_file_name);
cleanup:;
- free (function_name_buffer);
- fclose (file);
+ gcov_close ();
return 1;
}
- if (gcov_read_unsigned (file, &version) || version != GCOV_VERSION)
+ version = gcov_read_unsigned ();
+ if (version != GCOV_VERSION)
{
char v[4], e[4];
+
+ GCOV_UNSIGNED2STRING (v, version);
+ GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
- magic = GCOV_VERSION;
- for (ix = 4; ix--; magic >>= 8, version >>= 8)
- {
- v[ix] = version;
- e[ix] = magic;
- }
- fnotice (stderr, "%s:version `%.4s', prefer version `%.4s'\n",
+ fnotice (stderr, "%s:version '%.4s', prefer version '%.4s'\n",
da_file_name, v, e);
}
-
- while (1)
+ tag = gcov_read_unsigned ();
+ if (tag != bbg_stamp)
{
- unsigned tag, length;
- long base;
-
- if (gcov_read_unsigned (file, &tag)
- || gcov_read_unsigned (file, &length))
- {
- if (feof (file))
- break;
-
- corrupt:;
- fnotice (stderr, "%s:corrupted\n", da_file_name);
- goto cleanup;
- }
- base = gcov_save_position (file);
+ fnotice (stderr, "%s:stamp mismatch with graph file\n", da_file_name);
+ goto cleanup;
+ }
+
+ while ((tag = gcov_read_unsigned ()))
+ {
+ unsigned length = gcov_read_unsigned ();
+ unsigned long base = gcov_position ();
+
if (tag == GCOV_TAG_OBJECT_SUMMARY)
- {
- if (gcov_read_summary (file, &object_summary))
- goto corrupt;
- }
- else if (tag == GCOV_TAG_PROGRAM_SUMMARY
- || tag == GCOV_TAG_INCORRECT_SUMMARY)
+ gcov_read_summary (&object_summary);
+ else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
program_count++;
else if (tag == GCOV_TAG_FUNCTION)
{
- unsigned checksum;
+ unsigned ident = gcov_read_unsigned ();
struct function_info *fn_n = functions;
-
- if (gcov_read_string (file, &function_name_buffer, NULL)
- || gcov_read_unsigned (file, &checksum))
- goto corrupt;
for (fn = fn ? fn->next : NULL; ; fn = fn->next)
{
fn_n = NULL;
else
{
- fnotice (stderr, "%s:unknown function `%s'\n",
- da_file_name, function_name_buffer);
+ fnotice (stderr, "%s:unknown function '%u'\n",
+ da_file_name, ident);
break;
}
- if (!strcmp (fn->name, function_name_buffer))
+ if (fn->ident == ident)
break;
}
if (!fn)
;
- else if (checksum != fn->checksum)
+ else if (gcov_read_unsigned () != fn->checksum)
{
mismatch:;
- fnotice (stderr, "%s:profile mismatch for `%s'\n",
- da_file_name, function_name_buffer);
+ fnotice (stderr, "%s:profile mismatch for '%s'\n",
+ da_file_name, fn->name);
goto cleanup;
}
}
- else if (tag == GCOV_TAG_ARC_COUNTS && fn)
+ else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
{
- if (length != 8 * fn->num_counts)
+ if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts))
goto mismatch;
-
+
if (!fn->counts)
- fn->counts
- = (gcov_type *)xcalloc (fn->num_counts, sizeof (gcov_type));
-
+ fn->counts = xcalloc (fn->num_counts, sizeof (gcov_type));
+
for (ix = 0; ix != fn->num_counts; ix++)
- {
- gcov_type count;
-
- if (gcov_read_counter (file, &count))
- goto corrupt;
- fn->counts[ix] += count;
- }
+ fn->counts[ix] += gcov_read_counter ();
+ }
+ gcov_sync (base, length);
+ if ((error = gcov_is_error ()))
+ {
+ fnotice (stderr, error < 0 ? "%s:overflowed\n" : "%s:corrupted\n",
+ da_file_name);
+ goto cleanup;
}
- gcov_resync (file, base, length);
}
- fclose (file);
- free (function_name_buffer);
+ gcov_close ();
return 0;
}
to the blocks and the uninstrumented arcs. */
static void
-solve_flow_graph (fn)
- function_t *fn;
+solve_flow_graph (function_t *fn)
{
unsigned ix;
arc_t *arc;
block_t *blk;
block_t *valid_blocks = NULL; /* valid, but unpropagated blocks. */
block_t *invalid_blocks = NULL; /* invalid, but inferable blocks. */
-
+
if (fn->num_blocks < 2)
- fnotice (stderr, "%s:`%s' lacks entry and/or exit blocks\n",
+ fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n",
bbg_file_name, fn->name);
else
{
if (fn->blocks[0].num_pred)
- fnotice (stderr, "%s:`%s' has arcs to entry block\n",
+ fnotice (stderr, "%s:'%s' has arcs to entry block\n",
bbg_file_name, fn->name);
else
/* We can't deduce the entry block counts from the lack of
predecessors. */
fn->blocks[0].num_pred = ~(unsigned)0;
-
+
if (fn->blocks[fn->num_blocks - 1].num_succ)
- fnotice (stderr, "%s:`%s' has arcs from exit block\n",
+ fnotice (stderr, "%s:'%s' has arcs from exit block\n",
bbg_file_name, fn->name);
else
/* Likewise, we can't deduce exit block counts from the lack
block_t const *prev_dst = NULL;
int out_of_order = 0;
int non_fake_succ = 0;
-
+
for (arc = blk->succ; arc; arc = arc->succ_next)
{
if (!arc->fake)
non_fake_succ++;
-
+
if (!arc->on_tree)
{
if (count_ptr)
{
arc->is_unconditional = 1;
/* If this block is instrumenting a call, it might be
- an artifical block. It is not artificial if it has
- a non-fallthrough exit, or the destination of the
- exit has more than one entry. */
- if (!arc->fall_through
- || arc->dst->pred != arc || arc->pred_next)
- blk->is_call_site = 0;
+ an artificial block. It is not artificial if it has
+ a non-fallthrough exit, or the destination of this
+ arc has more than one entry. Mark the destination
+ block as a return site, if none of those conditions
+ hold. */
+ if (blk->is_call_site && arc->fall_through
+ && arc->dst->pred == arc && !arc->pred_next)
+ arc->dst->is_call_return = 1;
}
}
- else
- /* If there is more than one exit, it cannot be an artificial
- call instrumenting site. */
- blk->is_call_site = 0;
-
+
/* Sort the successor arcs into ascending dst order. profile.c
normally produces arcs in the right order, but sometimes with
one or two out of order. We're not using a particularly
{
arc_t *start = blk->succ;
unsigned changes = 1;
-
+
while (changes)
{
arc_t *arc, *arc_p, *arc_n;
-
+
changes = 0;
for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);)
{
}
blk->succ = start;
}
-
+
/* Place it on the invalid chain, it will be ignored if that's
wrong. */
blk->invalid_chain = 1;
{
gcov_type total = 0;
const arc_t *arc;
-
+
invalid_blocks = blk->chain;
blk->invalid_chain = 0;
if (!blk->num_succ)
total += arc->count;
else
continue;
-
+
blk->count = total;
blk->count_valid = 1;
blk->chain = valid_blocks;
if (blk->num_succ == 1)
{
block_t *dst;
-
+
total = blk->count;
inv_arc = NULL;
for (arc = blk->succ; arc; arc = arc->succ_next)
if (blk->num_pred == 1)
{
block_t *src;
-
+
total = blk->count;
inv_arc = NULL;
for (arc = blk->pred; arc; arc = arc->pred_next)
}
}
}
-
+
/* If the graph has been correctly solved, every block will have a
valid count. */
for (ix = 0; ix < fn->num_blocks; ix++)
if (!fn->blocks[ix].count_valid)
{
- fnotice (stderr, "%s:graph is unsolvable for `%s'\n",
+ fnotice (stderr, "%s:graph is unsolvable for '%s'\n",
bbg_file_name, fn->name);
break;
}
/* Increment totals in COVERAGE according to arc ARC. */
static void
-add_branch_counts (coverage, arc)
- coverage_t *coverage;
- const arc_t *arc;
+add_branch_counts (coverage_t *coverage, const arc_t *arc)
{
if (arc->is_call_non_return)
{
format TOP. Return pointer to a static string. */
static char const *
-format_gcov (top, bottom, dp)
- gcov_type top, bottom;
- int dp;
+format_gcov (gcov_type top, gcov_type bottom, int dp)
{
static char buffer[20];
-
+
if (dp >= 0)
{
float ratio = bottom ? (float)top / bottom : 0;
int ix;
unsigned limit = 100;
unsigned percent;
-
+
for (ix = dp; ix--; )
limit *= 10;
-
+
percent = (unsigned) (ratio * limit + (float)0.5);
if (percent <= 0 && top)
percent = 1;
}
else
sprintf (buffer, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)top);
-
+
return buffer;
}
/* Output summary info for a function. */
static void
-function_summary (coverage, title)
- const coverage_t *coverage;
- const char *title;
+function_summary (const coverage_t *coverage, const char *title)
{
- fnotice (stdout, "%s `%s'\n", title, coverage->name);
+ fnotice (stdout, "%s '%s'\n", title, coverage->name);
if (coverage->lines)
fnotice (stdout, "Lines executed:%s of %d\n",
format_gcov (coverage->lines_executed, coverage->lines, 2),
coverage->lines);
else
- fnotice (stdout, "No executable lines");
+ fnotice (stdout, "No executable lines\n");
if (flag_branches)
{
removed and '..' components are renamed to '^'. */
static char *
-make_gcov_file_name (input_name, src_name)
- const char *input_name;
- const char *src_name;
+make_gcov_file_name (const char *input_name, const char *src_name)
{
char *cptr;
char *name = xmalloc (strlen (src_name) + strlen (input_name) + 10);
-
+
name[0] = 0;
if (flag_long_names && strcmp (src_name, input_name))
{
strcat (name, cptr ? cptr + 1 : input_name);
strcat (name, "##");
}
-
+
/* Generate the source filename part. */
cptr = flag_preserve_paths ? NULL : strrchr (src_name, '/');
strcat (name, cptr ? cptr + 1 : src_name);
-
+
if (flag_preserve_paths)
{
/* Convert '/' to '#', remove '/./', convert '/../' to '/^/' */
char *prev;
-
+
for (cptr = name; (cptr = strchr ((prev = cptr), '/'));)
- {
- unsigned shift = 0;
-
- if (prev + 1 == cptr && prev[0] == '.')
- {
- /* Remove '.' */
- shift = 2;
- }
- else if (prev + 2 == cptr && prev[0] == '.' && prev[1] == '.')
- {
- /* Convert '..' */
- shift = 1;
- prev[1] = '^';
- }
- else
- *cptr++ = '#';
- if (shift)
- {
- cptr = prev;
- do
- prev[0] = prev[shift];
+ {
+ unsigned shift = 0;
+
+ if (prev + 1 == cptr && prev[0] == '.')
+ {
+ /* Remove '.' */
+ shift = 2;
+ }
+ else if (prev + 2 == cptr && prev[0] == '.' && prev[1] == '.')
+ {
+ /* Convert '..' */
+ shift = 1;
+ prev[1] = '^';
+ }
+ else
+ *cptr++ = '#';
+ if (shift)
+ {
+ cptr = prev;
+ do
+ prev[0] = prev[shift];
while (*prev++);
- }
- }
+ }
+ }
}
-
+
strcat (name, ".gcov");
return name;
}
the appropriate basic block. */
static void
-add_line_counts (coverage, fn)
- coverage_t *coverage;
- function_t *fn;
+add_line_counts (coverage_t *coverage, function_t *fn)
{
unsigned ix;
- line_t *line = NULL; /* this is propagated from one iteration to the
+ line_t *line = NULL; /* This is propagated from one iteration to the
next. */
/* Scan each basic block. */
unsigned *encoding;
const source_t *src = NULL;
unsigned jx;
- line_t *first_line = NULL;
if (block->count && ix && ix + 1 != fn->num_blocks)
fn->blocks_executed++;
}
line->exists = 1;
line->count += block->count;
- if (!first_line)
- first_line = line;
}
free (block->u.line.encoding);
- block->u.span.root = NULL;
- if (!first_line)
- first_line = line;
-
+ block->u.cycle.arc = NULL;
+ block->u.cycle.ident = ~0U;
+
if (!ix || ix + 1 == fn->num_blocks)
/* Entry or exit block */;
else if (flag_all_blocks)
{
- if (!first_line)
- first_line = &fn->src->lines[fn->line];
-
- block->chain = first_line->u.blocks;
- first_line->u.blocks = block;
+ line_t *block_line = line ? line : &fn->src->lines[fn->line];
+
+ block->chain = block_line->u.blocks;
+ block_line->u.blocks = block;
}
else if (flag_branches)
{
}
}
if (!line)
- fnotice (stderr, "%s:no lines for `%s'\n", bbg_file_name, fn->name);
+ fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name);
}
/* Accumulate the line counts of a file. */
static void
-accumulate_line_counts (src)
- source_t *src;
+accumulate_line_counts (source_t *src)
{
line_t *line;
function_t *fn, *fn_p, *fn_n;
fn->line_next = fn_p;
}
src->functions = fn_p;
-
+
for (ix = src->num_lines, line = src->lines; ix--; line++)
{
if (!flag_all_blocks)
{
arc_t *arc, *arc_p, *arc_n;
-
+
/* Total and reverse the branch information. */
for (arc = line->u.branches, arc_p = NULL; arc;
arc_p = arc, arc = arc_n)
{
arc_n = arc->line_next;
arc->line_next = arc_p;
-
+
add_branch_counts (&src->coverage, arc);
}
line->u.branches = arc_p;
/* The user expects the line count to be the number of times
a line has been executed. Simply summing the block count
will give an artificially high number. The Right Thing
- is to generate the spanning tree of the blocks on this
- line, and the sum the entry arcs to that tree. */
+ is to sum the entry counts to the graph of blocks on this
+ line, then find the elementary cycles of the local graph
+ and add the transition counts of those cycles. */
block_t *block, *block_p, *block_n;
- int changes = 1;
gcov_type count = 0;
-
- /* Reverse the block information */
+
+ /* Reverse the block information. */
for (block = line->u.blocks, block_p = NULL; block;
block_p = block, block = block_n)
{
block_n = block->chain;
block->chain = block_p;
- /* Each block is it's own spanning tree, with no siblings */
- block->u.span.root = block;
- block->u.span.siblings = 0;
+ block->u.cycle.ident = ix;
}
line->u.blocks = block_p;
- while (changes)
+ /* Sum the entry arcs. */
+ for (block = line->u.blocks; block; block = block->chain)
{
- changes = 0;
-
- for (block = line->u.blocks; block; block = block->chain)
+ arc_t *arc;
+
+ for (arc = block->pred; arc; arc = arc->pred_next)
{
- arc_t *arc;
-
- for (arc = block->succ; arc; arc = arc->succ_next)
- {
- block_t *dst = arc->dst;
-
- if (!dst->u.span.root)
- /* Not on this line. */;
- else if (dst->u.span.root == block->u.span.root)
- /* Same spanning tree. */;
- else
- {
- block_t *root = block->u.span.root;
- block_t *dst_root = dst->u.span.root;
-
- /* Join spanning trees */
- if (root->u.span.siblings
- && !dst_root->u.span.siblings)
- {
- root = dst->u.span.root;
- dst_root = block->u.span.root;
- }
-
- dst_root->u.span.root = root;
- root->u.span.siblings
- += 1 + dst_root->u.span.siblings;
-
- if (dst_root->u.span.siblings)
- {
- block_t *dst_sib;
-
- dst_root->u.span.siblings = 0;
- for (dst_sib = line->u.blocks; dst_sib;
- dst_sib = dst_sib->chain)
- if (dst_sib->u.span.root == dst_root)
- dst_sib->u.span.root = root;
- }
- arc->local_span = 1;
- changes = 1;
- }
- }
+ if (arc->src->u.cycle.ident != ix)
+ count += arc->count;
+ if (flag_branches)
+ add_branch_counts (&src->coverage, arc);
}
+
+ /* Initialize the cs_count. */
+ for (arc = block->succ; arc; arc = arc->succ_next)
+ arc->cs_count = arc->count;
}
- /* Now sum the entry counts */
+ /* Find the loops. This uses the algorithm described in
+ Tiernan 'An Efficient Search Algorithm to Find the
+ Elementary Circuits of a Graph', CACM Dec 1970. We hold
+ the P array by having each block point to the arc that
+ connects to the previous block. The H array is implicitly
+ held because of the arc ordering, and the block's
+ previous arc pointer.
+
+ Although the algorithm is O(N^3) for highly connected
+ graphs, at worst we'll have O(N^2), as most blocks have
+ only one or two exits. Most graphs will be small.
+
+ For each loop we find, locate the arc with the smallest
+ transition count, and add that to the cumulative
+ count. Decrease flow over the cycle and remove the arc
+ from consideration. */
for (block = line->u.blocks; block; block = block->chain)
{
+ block_t *head = block;
arc_t *arc;
- for (arc = block->succ; arc; arc = arc->succ_next)
+ next_vertex:;
+ arc = head->succ;
+ current_vertex:;
+ while (arc)
{
- if (!arc->local_span)
- count += arc->count;
- if (flag_branches)
- add_branch_counts (&src->coverage, arc);
+ block_t *dst = arc->dst;
+ if (/* Already used that arc. */
+ arc->cycle
+ /* Not to same graph, or before first vertex. */
+ || dst->u.cycle.ident != ix
+ /* Already in path. */
+ || dst->u.cycle.arc)
+ {
+ arc = arc->succ_next;
+ continue;
+ }
+
+ if (dst == block)
+ {
+ /* Found a closing arc. */
+ gcov_type cycle_count = arc->cs_count;
+ arc_t *cycle_arc = arc;
+ arc_t *probe_arc;
+
+ /* Locate the smallest arc count of the loop. */
+ for (dst = head; (probe_arc = dst->u.cycle.arc);
+ dst = probe_arc->src)
+ if (cycle_count > probe_arc->cs_count)
+ {
+ cycle_count = probe_arc->cs_count;
+ cycle_arc = probe_arc;
+ }
+
+ count += cycle_count;
+ cycle_arc->cycle = 1;
+
+ /* Remove the flow from the cycle. */
+ arc->cs_count -= cycle_count;
+ for (dst = head; (probe_arc = dst->u.cycle.arc);
+ dst = probe_arc->src)
+ probe_arc->cs_count -= cycle_count;
+
+ /* Unwind to the cyclic arc. */
+ while (head != cycle_arc->src)
+ {
+ arc = head->u.cycle.arc;
+ head->u.cycle.arc = NULL;
+ head = arc->src;
+ }
+ /* Move on. */
+ arc = arc->succ_next;
+ continue;
+ }
+
+ /* Add new block to chain. */
+ dst->u.cycle.arc = arc;
+ head = dst;
+ goto next_vertex;
+ }
+ /* We could not add another vertex to the path. Remove
+ the last vertex from the list. */
+ arc = head->u.cycle.arc;
+ if (arc)
+ {
+ /* It was not the first vertex. Move onto next arc. */
+ head->u.cycle.arc = NULL;
+ head = arc->src;
+ arc = arc->succ_next;
+ goto current_vertex;
}
- block->u.span.root = NULL;
+ /* Mark this block as unusable. */
+ block->u.cycle.ident = ~0U;
}
-
+
line->count = count;
}
-
+
if (line->exists)
{
src->coverage.lines++;
}
}
-/* Ouput information about ARC number IX. Returns non-zero if
+/* Output information about ARC number IX. Returns nonzero if
anything is output. */
static int
-output_branch_count (gcov_file, ix, arc)
- FILE *gcov_file;
- int ix;
- const arc_t *arc;
+output_branch_count (FILE *gcov_file, int ix, const arc_t *arc)
{
-
+
if (arc->is_call_non_return)
{
if (arc->src->count)
else
fnotice (gcov_file, "branch %2d never executed\n", ix);
}
- else if (flag_unconditional && !arc->src->is_call_site)
+ else if (flag_unconditional && !arc->dst->is_call_return)
{
if (arc->src->count)
fnotice (gcov_file, "unconditional %2d taken %s\n", ix,
else
return 0;
return 1;
-
+
}
/* Read in the source file one line at a time, and output that line to
information. */
static void
-output_lines (gcov_file, src)
- FILE *gcov_file;
- const source_t *src;
+output_lines (FILE *gcov_file, const source_t *src)
{
FILE *source_file;
- unsigned line_num; /* current line number. */
+ unsigned line_num; /* current line number. */
const line_t *line; /* current line info ptr. */
char string[STRING_SIZE]; /* line buffer. */
char const *retval = ""; /* status of source file reading. */
- function_t *fn = src->functions;
+ function_t *fn = NULL;
fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->name);
fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name);
fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0, da_file_name);
- fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0, object_summary.runs);
+ fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0,
+ object_summary.ctrs[GCOV_COUNTER_ARCS].runs);
fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count);
-
+
source_file = fopen (src->name, "r");
if (!source_file)
{
else
{
struct stat status;
-
+
if (!fstat (fileno (source_file), &status)
&& status.st_mtime > bbg_file_time)
{
- fnotice (stderr, "%s:source file is newer than graph file `%s'\n",
+ fnotice (stderr, "%s:source file is newer than graph file '%s'\n",
src->name, bbg_file_name);
fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n",
"-", 0);
}
}
+ if (flag_branches)
+ fn = src->functions;
+
for (line_num = 1, line = &src->lines[line_num];
line_num < src->num_lines; line_num++, line++)
{
{
arc_t *arc = fn->blocks[fn->num_blocks - 1].pred;
gcov_type return_count = fn->blocks[fn->num_blocks - 1].count;
-
+
for (; arc; arc = arc->pred_next)
if (arc->fake)
return_count -= arc->count;
format_gcov (fn->blocks_executed, fn->num_blocks - 2, 0));
fprintf (gcov_file, "\n");
}
-
+
/* For lines which don't exist in the .bb file, print '-' before
- the source line. For lines which exist but were never
- executed, print '#####' before the source line. Otherwise,
- print the execution count before the source line. There are
- 16 spaces of indentation added before the source line so that
- tabs won't be messed up. */
+ the source line. For lines which exist but were never
+ executed, print '#####' before the source line. Otherwise,
+ print the execution count before the source line. There are
+ 16 spaces of indentation added before the source line so that
+ tabs won't be messed up. */
fprintf (gcov_file, "%9s:%5u:",
!line->exists ? "-" : !line->count ? "#####"
: format_gcov (line->count, 0, -1), line_num);
-
+
if (retval)
{
/* Copy source line. */
{
retval = fgets (string, STRING_SIZE, source_file);
if (!retval)
- {
- fnotice (stderr, "%s:unexpected EOF\n", src->name);
- break;
- }
+ break;
fputs (retval, gcov_file);
}
while (!retval[0] || retval[strlen (retval) - 1] != '\n');
}
if (!retval)
- fputs ("??\n", gcov_file);
+ fputs ("/*EOF*/\n", gcov_file);
if (flag_all_blocks)
{
block_t *block;
+ arc_t *arc;
int ix, jx;
-
+
for (ix = jx = 0, block = line->u.blocks; block;
block = block->chain)
{
- arc_t *arc;
-
- if (!block->is_call_site)
+ if (!block->is_call_return)
fprintf (gcov_file, "%9s:%5u-block %2d\n",
!line->exists ? "-" : !block->count ? "$$$$$"
- : format_gcov (block->count, 0, -1), line_num, ix++);
+ : format_gcov (block->count, 0, -1),
+ line_num, ix++);
if (flag_branches)
for (arc = block->succ; arc; arc = arc->succ_next)
jx += output_branch_count (gcov_file, jx, arc);
{
int ix;
arc_t *arc;
-
+
for (ix = 0, arc = line->u.branches; arc; arc = arc->line_next)
ix += output_branch_count (gcov_file, ix, arc);
}
}
-
+
/* Handle all remaining source lines. There may be lines after the
last line of code. */
if (retval)
for (; (retval = fgets (string, STRING_SIZE, source_file)); line_num++)
{
fprintf (gcov_file, "%9s:%5u:%s", "-", line_num, retval);
-
+
while (!retval[0] || retval[strlen (retval) - 1] != '\n')
{
retval = fgets (string, STRING_SIZE, source_file);
}
}
}
-
+
if (source_file)
fclose (source_file);
}