blob: a0586d44e25de1a707258ba8263418a052ce2a5c [file] [log] [blame]
/* Read and write coverage files, and associated functionality.
Copyright (C) 1990-2013 Free Software Foundation, Inc.
Contributed by James E. Wilson, UC Berkeley/Cygnus Support;
based on some ideas from Dain Samples of UC Berkeley.
Further mangling by Bob Manson, Cygnus Support.
Further mangled by Nathan Sidwell, CodeSourcery
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#define GCOV_LINKAGE
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "tree.h"
#include "flags.h"
#include "output.h"
#include "regs.h"
#include "expr.h"
#include "function.h"
#include "basic-block.h"
#include "toplev.h"
#include "tm_p.h"
#include "ggc.h"
#include "coverage.h"
#include "langhooks.h"
#include "hash-table.h"
#include "tree-iterator.h"
#include "cgraph.h"
#include "dumpfile.h"
#include "opts.h"
#include "gcov-io.h"
#include "tree-flow.h"
#include "tree-pass.h"
#include "cpplib.h"
#include "incpath.h"
#include "diagnostic-core.h"
#include "intl.h"
#include "l-ipo.h"
#include "filenames.h"
#include "dwarf2asm.h"
#include "target.h"
#include "auto-profile.h"
#include "gcov-io.h"
#include "gcov-io.c"
#include "params.h"
#include "dbgcnt.h"
#include "input.h"
struct GTY((chain_next ("%h.next"))) coverage_data
{
struct coverage_data *next; /* next function */
unsigned ident; /* function ident */
unsigned lineno_checksum; /* function lineno checksum */
unsigned cfg_checksum; /* function cfg checksum */
tree fn_decl; /* the function decl */
tree ctr_vars[GCOV_COUNTERS]; /* counter variables. */
};
static bool profiling_enabled_p (void);
/* Linked list of -D/-U/-imacro/-include strings for a source module. */
struct str_list
{
char *str;
struct str_list *next;
};
/* Counts information for a function. */
typedef struct counts_entry
{
/* We hash by */
unsigned HOST_WIDEST_INT ident;
unsigned ctr;
/* Store */
unsigned lineno_checksum;
unsigned cfg_checksum;
gcov_type *counts;
struct gcov_ctr_summary summary;
/* hash_table support. */
typedef counts_entry value_type;
typedef counts_entry compare_type;
static inline hashval_t hash (const value_type *);
static int equal (const value_type *, const compare_type *);
static void remove (value_type *);
} counts_entry_t;
static GTY(()) struct coverage_data *functions_head = 0;
static struct coverage_data **functions_tail = &functions_head;
static unsigned no_coverage = 0;
/* Cumulative counter information for whole program. */
static unsigned prg_ctr_mask; /* Mask of counter types generated. */
/* Counter information for current function. */
static unsigned fn_ctr_mask; /* Mask of counters used. */
static GTY(()) tree fn_v_ctrs[GCOV_COUNTERS]; /* counter variables. */
static unsigned fn_n_ctrs[GCOV_COUNTERS]; /* Counters allocated. */
static unsigned fn_b_ctrs[GCOV_COUNTERS]; /* Allocation base. */
/* Coverage info VAR_DECL and function info type nodes. */
static GTY(()) tree gcov_info_var;
static GTY(()) tree gcov_fn_info_type;
static GTY(()) tree gcov_fn_info_ptr_type;
/* Name of the notes (gcno) output file. The "bbg" prefix is for
historical reasons, when the notes file contained only the
basic block graph notes.
If this is NULL we're not writing to the notes file. */
static char *bbg_file_name;
/* File stamp for notes file. */
static unsigned bbg_file_stamp;
/* Name of the count data (gcda) file. */
static char *da_file_name;
static char *da_base_file_name;
static char *main_input_file_name;
/* The names of merge functions for counters. */
static const char *const ctr_merge_functions[GCOV_COUNTERS] = GCOV_MERGE_FUNCTIONS;
static const char *const ctr_names[GCOV_COUNTERS] = GCOV_COUNTER_NAMES;
/* True during the period that counts_hash is being rebuilt. */
static bool rebuilding_counts_hash = false;
struct gcov_module_info **module_infos = NULL;
/* List of -D/-U options. */
static struct str_list *cpp_defines_head = NULL, *cpp_defines_tail = NULL;
static unsigned num_cpp_defines = 0;
/* List of -imcaro/-include options. */
static struct str_list *cpp_includes_head = NULL, *cpp_includes_tail = NULL;
static unsigned num_cpp_includes = 0;
/* True if the current module has any asm statements. */
static bool has_asm_statement;
/* Forward declarations. */
static void read_counts_file (const char *, unsigned);
static tree build_var (tree, tree, int);
static void build_fn_info_type (tree, unsigned, tree);
static void build_info_type (tree, tree);
static tree build_fn_info (const struct coverage_data *, tree, tree);
static tree build_info (tree, tree);
static bool coverage_obj_init (void);
static vec<constructor_elt, va_gc> *coverage_obj_fn
(vec<constructor_elt, va_gc> *, tree, struct coverage_data const *);
static void coverage_obj_finish (vec<constructor_elt, va_gc> *);
static char * get_da_file_name (const char *);
static tree build_gcov_module_info_type (void);
/* Return the type node for gcov_type. */
tree
get_gcov_type (void)
{
enum machine_mode mode = smallest_mode_for_size (GCOV_TYPE_SIZE, MODE_INT);
return lang_hooks.types.type_for_mode (mode, false);
}
/* Return the type node for gcov_unsigned_t. */
tree
get_gcov_unsigned_t (void)
{
enum machine_mode mode = smallest_mode_for_size (32, MODE_INT);
return lang_hooks.types.type_for_mode (mode, true);
}
/* Return the type node for const char *. */
tree
get_const_string_type (void)
{
return build_pointer_type
(build_qualified_type (char_type_node, TYPE_QUAL_CONST));
}
inline hashval_t
counts_entry::hash (const value_type *entry)
{
return entry->ident * GCOV_COUNTERS + entry->ctr;
}
inline int
counts_entry::equal (const value_type *entry1,
const compare_type *entry2)
{
return entry1->ident == entry2->ident && entry1->ctr == entry2->ctr;
}
inline void
counts_entry::remove (value_type *entry)
{
/* When rebuilding counts_hash, we will reuse the entry. */
if (!rebuilding_counts_hash)
{
free (entry->counts);
free (entry);
}
}
/* Hash table of count data. */
static hash_table <counts_entry> counts_hash;
/* Returns true if MOD_ID is the id of the last source module. */
int
is_last_module (unsigned mod_id)
{
return (mod_id == module_infos[num_in_fnames - 1]->ident);
}
/* String hash function */
struct string_hasher
{
/* hash_table support. */
typedef char value_type;
typedef char compare_type;
static inline hashval_t hash (const value_type *);
static int equal (const value_type *, const compare_type *);
static void remove (value_type *) {};
};
hashval_t
string_hasher::hash (const char* s)
{
return htab_hash_string (s);
}
/* String equal function */
int
string_hasher::equal (const char *s1, const char *s2)
{
return !strcmp (s1, s2);
}
/* Command line option descriptor. */
struct opt_desc
{
const char *opt_str;
const char *opt_neg_str;
bool default_val; /* TODO better handling of default */
};
static struct opt_desc force_matching_cg_opts[] =
{
{ "-fexceptions", "-fno-exceptions", true },
{ "-fsized-delete", "-fno-sized-delete", false },
{ "-frtti", "-fno-rtti", true },
{ "-fstrict-aliasing", "-fno-strict-aliasing", true },
{ "-fsigned-char", "-funsigned-char", true },
/* { "-fsigned-char", "-fno-signed-char", true },
{ "-funsigned-char", "-fno-unsigned-char", false }, */
{ "-ansi", "", false },
{ NULL, NULL, false }
};
/* A helper function to check if OPTION_STRING is one of the codegen
options specified in FORCE_MATCHING_CG_ARGS. If yes, set the
corresponding entry in CG_ARG_VAL to the value of the option specified
in OPTION_STRING. */
static void
check_cg_opts (bool *cg_opt_val, const char *option_str)
{
unsigned int i;
for (i = 0; force_matching_cg_opts[i].opt_str; i++)
{
if (!strcmp (force_matching_cg_opts[i].opt_str, option_str))
cg_opt_val[i] = true;
else if (!strcmp (force_matching_cg_opts[i].opt_neg_str, option_str))
cg_opt_val[i] = false;
}
}
/* A helper function to check if CG_OPTS1 and CG_OPTS are identical. It returns
true if yes, false otherwise. */
static bool
has_incompatible_cg_opts (bool *cg_opts1, bool *cg_opts2, unsigned num_cg_opts)
{
unsigned i;
for (i = 0; i < num_cg_opts; i++)
{
if (cg_opts1[i] != cg_opts2[i])
return true;
}
return false;
}
/* Returns true if the command-line arguments stored in the given module-infos
are incompatible. */
bool
incompatible_cl_args (struct gcov_module_info* mod_info1,
struct gcov_module_info* mod_info2)
{
char **warning_opts1 = XNEWVEC (char *, mod_info1->num_cl_args);
char **warning_opts2 = XNEWVEC (char *, mod_info2->num_cl_args);
char **non_warning_opts1 = XNEWVEC (char *, mod_info1->num_cl_args);
char **non_warning_opts2 = XNEWVEC (char *, mod_info2->num_cl_args);
char *std_opts1 = NULL, *std_opts2 = NULL;
unsigned int i, num_warning_opts1 = 0, num_warning_opts2 = 0;
unsigned int num_non_warning_opts1 = 0, num_non_warning_opts2 = 0;
bool warning_mismatch = false;
bool non_warning_mismatch = false;
hash_table <string_hasher> option_tab1, option_tab2;
unsigned int start_index1 = mod_info1->num_quote_paths
+ mod_info1->num_bracket_paths + mod_info1->num_system_paths
+ mod_info1->num_cpp_defines + mod_info1->num_cpp_includes;
unsigned int start_index2 = mod_info2->num_quote_paths
+ mod_info2->num_bracket_paths + mod_info2->num_system_paths
+ mod_info2->num_cpp_defines + mod_info2->num_cpp_includes;
bool *cg_opts1, *cg_opts2, has_any_incompatible_cg_opts, has_incompatible_std;
unsigned int num_cg_opts = 0;
for (i = 0; force_matching_cg_opts[i].opt_str; i++)
num_cg_opts++;
cg_opts1 = XCNEWVEC (bool, num_cg_opts);
cg_opts2 = XCNEWVEC (bool, num_cg_opts);
/* Initialize the array to default value */
for (i = 0; force_matching_cg_opts[i].opt_str; i++)
{
cg_opts1[i] = force_matching_cg_opts[i].default_val;
cg_opts2[i] = force_matching_cg_opts[i].default_val;
}
option_tab1.create (10);
option_tab2.create (10);
/* First, separate the warning and non-warning options. */
for (i = 0; i < mod_info1->num_cl_args; i++)
if (mod_info1->string_array[start_index1 + i][1] == 'W')
warning_opts1[num_warning_opts1++] =
mod_info1->string_array[start_index1 + i];
else
{
char **slot;
char *option_string = mod_info1->string_array[start_index1 + i];
check_cg_opts (cg_opts1, option_string);
if (strstr (option_string, "-std="))
std_opts1 = option_string;
slot = option_tab1.find_slot (option_string, INSERT);
if (!*slot)
{
*slot = option_string;
non_warning_opts1[num_non_warning_opts1++] = option_string;
}
}
for (i = 0; i < mod_info2->num_cl_args; i++)
if (mod_info2->string_array[start_index2 + i][1] == 'W')
warning_opts2[num_warning_opts2++] =
mod_info2->string_array[start_index2 + i];
else
{
char **slot;
char *option_string = mod_info2->string_array[start_index2 + i];
check_cg_opts (cg_opts2, option_string);
if (strstr (option_string, "-std="))
std_opts2 = option_string;
slot = option_tab2.find_slot (option_string, INSERT);
if (!*slot)
{
*slot = option_string;
non_warning_opts2[num_non_warning_opts2++] = option_string;
}
}
has_incompatible_std =
std_opts1 != std_opts2 && (std_opts1 == NULL || std_opts2 == NULL
|| strcmp (std_opts1, std_opts2));
/* Compare warning options. If these mismatch, we emit a warning. */
if (num_warning_opts1 != num_warning_opts2)
warning_mismatch = true;
else
for (i = 0; i < num_warning_opts1 && !warning_mismatch; i++)
warning_mismatch = strcmp (warning_opts1[i], warning_opts2[i]) != 0;
/* Compare non-warning options. If these mismatch, we emit a warning, and if
-fripa-disallow-opt-mismatch is supplied, the two modules are also
incompatible. */
if (num_non_warning_opts1 != num_non_warning_opts2)
non_warning_mismatch = true;
else
for (i = 0; i < num_non_warning_opts1 && !non_warning_mismatch; i++)
non_warning_mismatch =
strcmp (non_warning_opts1[i], non_warning_opts2[i]) != 0;
if (warn_ripa_opt_mismatch && (warning_mismatch || non_warning_mismatch))
warning (OPT_Wripa_opt_mismatch, "command line arguments mismatch for %s "
"and %s", mod_info1->source_filename, mod_info2->source_filename);
if (warn_ripa_opt_mismatch && non_warning_mismatch && dump_enabled_p ())
{
dump_printf_loc (MSG_MISSED_OPTIMIZATION, UNKNOWN_LOCATION,
"Options for %s", mod_info1->source_filename);
for (i = 0; i < num_non_warning_opts1; i++)
dump_printf_loc (MSG_MISSED_OPTIMIZATION, UNKNOWN_LOCATION,
non_warning_opts1[i]);
dump_printf_loc (MSG_MISSED_OPTIMIZATION, UNKNOWN_LOCATION,
"Options for %s", mod_info2->source_filename);
for (i = 0; i < num_non_warning_opts2; i++)
dump_printf_loc (MSG_MISSED_OPTIMIZATION, UNKNOWN_LOCATION,
non_warning_opts2[i]);
}
has_any_incompatible_cg_opts
= has_incompatible_cg_opts (cg_opts1, cg_opts2, num_cg_opts);
XDELETEVEC (warning_opts1);
XDELETEVEC (warning_opts2);
XDELETEVEC (non_warning_opts1);
XDELETEVEC (non_warning_opts2);
XDELETEVEC (cg_opts1);
XDELETEVEC (cg_opts2);
option_tab1.dispose ();
option_tab2.dispose ();
return ((flag_ripa_disallow_opt_mismatch && non_warning_mismatch)
|| has_any_incompatible_cg_opts || has_incompatible_std);
}
/* Support for module sorting based on user specfication. */
struct module_name_entry
{
typedef module_name_entry value_type;
typedef module_name_entry compare_type;
static inline hashval_t hash (const value_type *entry);
static inline int equal (const value_type *entry1, const compare_type *entry2);
static inline void remove (value_type *v);
const char *source_name;
int order;
};
/* Hash function for module name */
hashval_t
module_name_entry::hash (const value_type *s)
{
return htab_hash_string (s->source_name);
}
/* Delete function for module name */
void
module_name_entry::remove (value_type *entry)
{
/* XDELETE (entry->source_name); */
XDELETE (entry);
}
/* Equal function for module name */
int
module_name_entry::equal (const value_type *s1, const compare_type *s2)
{
return !strcmp (s1->source_name, s2->source_name);
}
static hash_table<module_name_entry> module_name_tab;
/* Comparison function for sorting module_infos array. */
static int
cmp_module_name_entry (const void *p1, const void *p2)
{
module_name_entry **slot1, **slot2;
module_name_entry *m_e1, *m_e2;
struct gcov_module_info *const *e1 = (struct gcov_module_info *const *) p1;
struct gcov_module_info *const *e2 = (struct gcov_module_info *const *) p2;
module_name_entry e;
e.source_name = (*e1)->source_filename;
slot1 = module_name_tab.find_slot (&e, NO_INSERT);
e.source_name = (*e2)->source_filename;
slot2 = module_name_tab.find_slot (&e, NO_INSERT);
if (!slot1 || !*slot1)
return 1;
if (!slot2 || !*slot2)
return -1;
gcc_assert (slot1 && *slot1 && slot2 && *slot2);
m_e1 = *slot1;
m_e2 = *slot2;
return m_e1->order - m_e2->order;
}
/* Comparison function for sorting fname array */
static int
cmp_fname_entry (const void *p1, const void *p2)
{
module_name_entry **slot1, **slot2;
module_name_entry *m_e1, *m_e2;
const char *const *e1 = (const char *const *) p1;
const char *const *e2 = (const char *const *) p2;
module_name_entry e;
e.source_name = *e1;
slot1 = module_name_tab.find_slot (&e, NO_INSERT);
e.source_name = *e2;
slot2 = module_name_tab.find_slot (&e, NO_INSERT);
if (!slot1 || !*slot1)
return 1;
if (!slot2 || !*slot2)
return -1;
gcc_assert (slot1 && *slot1 && slot2 && *slot2);
m_e1 = *slot1;
m_e2 = *slot2;
return m_e1->order - m_e2->order;
}
/* Reorder module group according to file IMPORTS_FILE */
static void
reorder_module_groups (const char *imports_file, unsigned max_group)
{
FILE *f;
int n, order = 0;
size_t len;
char *line = NULL;
module_name_tab.create (20);
f = fopen (imports_file, "r");
if (!f)
error ("Can't open file %s", imports_file);
while ((n = getline (&line, &len, f)) != -1)
{
module_name_entry **slot;
module_name_entry *m_e = XCNEW (module_name_entry);
line[n - 1] = '\0';
m_e->source_name = line;
m_e->order = order;
slot = module_name_tab.find_slot (m_e, INSERT);
gcc_assert (!*slot);
*slot = m_e;
line = NULL;
order++;
}
/* Now do the sorting */
qsort (&module_infos[1], num_in_fnames - 1, sizeof (void *),
cmp_module_name_entry);
qsort (&in_fnames[1], num_in_fnames - 1, sizeof (void *),
cmp_fname_entry);
{
unsigned i;
for (i = 0; i < num_in_fnames; i++)
fprintf (stderr, "*** %s (%s)\n", in_fnames[i],
i < max_group ? "Kept":"Skipped");
for (i = 0; i < num_in_fnames; i++)
fprintf (stderr, "### %s (%s)\n", module_infos[i]->source_filename,
i < max_group ? "Kept":"Skipped");
}
if (num_in_fnames > max_group)
num_in_fnames = max_group;
module_name_tab.dispose ();
}
/* Return the module name for module with MOD_ID. */
const char *
get_module_name (unsigned int mod_id)
{
size_t i;
for (i = 0; i < num_in_fnames; i++)
{
if (module_infos[i]->ident == mod_id)
return lbasename (module_infos[i]->source_filename);
}
gcc_assert (0);
return NULL;
}
/* Read in the counts file, if available. DA_FILE_NAME is the
name of the gcda file, and MODULE_ID is the module id of the
associated source module. */
static void
read_counts_file (const char *da_file_name, unsigned module_id)
{
gcov_unsigned_t fn_ident = 0;
struct gcov_summary summary;
unsigned new_summary = 1;
gcov_unsigned_t tag;
int is_error = 0;
unsigned module_infos_read = 0;
struct pointer_set_t *modset = 0;
unsigned max_group = PARAM_VALUE (PARAM_MAX_LIPO_GROUP);
unsigned lineno_checksum = 0;
unsigned cfg_checksum = 0;
const char *imports_filename;
if (max_group == 0)
max_group = (unsigned) -1;
if (!gcov_open (da_file_name, 1))
{
if (PARAM_VALUE (PARAM_GCOV_DEBUG))
{
/* Try to find .gcda file in the current working dir. */
da_file_name = lbasename (da_file_name);
if (!gcov_open (da_file_name, 1))
return;
}
else
return;
}
if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
{
warning (0, "%qs is not a gcov data file", da_file_name);
gcov_close ();
return;
}
else if ((tag = gcov_read_unsigned ()) != GCOV_VERSION)
{
char v[4], e[4];
GCOV_UNSIGNED2STRING (v, tag);
GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
warning (0, "%qs is version %q.*s, expected version %q.*s",
da_file_name, 4, v, 4, e);
gcov_close ();
return;
}
/* Read the stamp, used for creating a generation count. */
tag = gcov_read_unsigned ();
bbg_file_stamp = crc32_unsigned (bbg_file_stamp, tag);
if (!counts_hash.is_created ())
counts_hash.create (10);
while ((tag = gcov_read_unsigned ()))
{
gcov_unsigned_t length;
gcov_position_t offset;
length = gcov_read_unsigned ();
offset = gcov_position ();
if (tag == GCOV_TAG_FUNCTION)
{
if (length)
{
fn_ident = gcov_read_unsigned ();
lineno_checksum = gcov_read_unsigned ();
cfg_checksum = gcov_read_unsigned ();
}
else
fn_ident = lineno_checksum = cfg_checksum = 0;
new_summary = 1;
}
else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
{
struct gcov_summary sum;
unsigned ix;
if (new_summary)
memset (&summary, 0, sizeof (summary));
gcov_read_summary (&sum);
for (ix = 0; ix != GCOV_COUNTERS_SUMMABLE; ix++)
{
summary.ctrs[ix].runs += sum.ctrs[ix].runs;
summary.ctrs[ix].sum_all += sum.ctrs[ix].sum_all;
if (summary.ctrs[ix].run_max < sum.ctrs[ix].run_max)
summary.ctrs[ix].run_max = sum.ctrs[ix].run_max;
summary.ctrs[ix].sum_max += sum.ctrs[ix].sum_max;
}
if (new_summary)
memcpy (summary.ctrs[GCOV_COUNTER_ARCS].histogram,
sum.ctrs[GCOV_COUNTER_ARCS].histogram,
sizeof (gcov_bucket_type) * GCOV_HISTOGRAM_SIZE);
else
gcov_histogram_merge (summary.ctrs[GCOV_COUNTER_ARCS].histogram,
sum.ctrs[GCOV_COUNTER_ARCS].histogram);
new_summary = 0;
}
else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
{
counts_entry_t **slot, *entry, elt;
unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
unsigned ix;
elt.ident = GEN_FUNC_GLOBAL_ID (module_id, fn_ident);
elt.ctr = GCOV_COUNTER_FOR_TAG (tag);
slot = counts_hash.find_slot (&elt, INSERT);
entry = *slot;
if (!entry)
{
*slot = entry = XCNEW (counts_entry_t);
entry->ident = elt.ident;
entry->ctr = elt.ctr;
entry->lineno_checksum = lineno_checksum;
entry->cfg_checksum = cfg_checksum;
if (elt.ctr < GCOV_COUNTERS_SUMMABLE)
entry->summary = summary.ctrs[elt.ctr];
entry->summary.num = n_counts;
entry->counts = XCNEWVEC (gcov_type, n_counts);
}
else if (entry->lineno_checksum != lineno_checksum
|| entry->cfg_checksum != cfg_checksum)
{
error ("Profile data for function %u is corrupted", fn_ident);
error ("checksum is (%x,%x) instead of (%x,%x)",
entry->lineno_checksum, entry->cfg_checksum,
lineno_checksum, cfg_checksum);
counts_hash.dispose ();
break;
}
else if (entry->summary.num != n_counts)
{
error ("Profile data for function %u is corrupted", fn_ident);
error ("number of counters is %d instead of %d", entry->summary.num, n_counts);
counts_hash.dispose ();
break;
}
else if (elt.ctr >= GCOV_COUNTERS_SUMMABLE)
{
error ("cannot merge separate %s counters for function %u",
ctr_names[elt.ctr], fn_ident);
goto skip_merge;
}
else
{
entry->summary.runs += summary.ctrs[elt.ctr].runs;
entry->summary.sum_all += summary.ctrs[elt.ctr].sum_all;
if (entry->summary.run_max < summary.ctrs[elt.ctr].run_max)
entry->summary.run_max = summary.ctrs[elt.ctr].run_max;
entry->summary.sum_max += summary.ctrs[elt.ctr].sum_max;
}
for (ix = 0; ix != n_counts; ix++)
entry->counts[ix] += gcov_read_counter ();
skip_merge:;
}
/* Skip the MODULE_INFO records if not in dyn-ipa mode, or when reading
auxiliary modules. */
else if (tag == GCOV_TAG_MODULE_INFO && flag_dyn_ipa && !module_id)
{
struct gcov_module_info* mod_info;
size_t info_sz;
/* each string has at least 8 bytes, so MOD_INFO's
persistent length >= in core size. */
mod_info
= (struct gcov_module_info *) alloca ((length + 2)
* sizeof (gcov_unsigned_t));
gcov_read_module_info (mod_info, length);
info_sz = (sizeof (struct gcov_module_info) +
sizeof (void *) * (mod_info->num_quote_paths +
mod_info->num_bracket_paths +
mod_info->num_system_paths +
mod_info->num_cpp_defines +
mod_info->num_cpp_includes +
mod_info->num_cl_args));
/* The first MODULE_INFO record must be for the primary module. */
if (module_infos_read == 0)
{
gcc_assert (mod_info->is_primary && !modset);
module_infos_read++;
modset = pointer_set_create ();
pointer_set_insert (modset, (void *)(size_t)mod_info->ident);
primary_module_id = mod_info->ident;
include_all_aux = MODULE_INCLUDE_ALL_AUX_FLAG (mod_info);
module_infos = XCNEWVEC (struct gcov_module_info *, 1);
module_infos[0] = XCNEWVAR (struct gcov_module_info, info_sz);
memcpy (module_infos[0], mod_info, info_sz);
}
else
{
int fd;
char *aux_da_filename = get_da_file_name (mod_info->da_filename);
gcc_assert (!mod_info->is_primary);
if (pointer_set_insert (modset, (void *)(size_t)mod_info->ident))
{
if (dump_enabled_p ())
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
"Not importing %s: already imported",
mod_info->source_filename);
}
else if ((module_infos[0]->lang & GCOV_MODULE_LANG_MASK) !=
(mod_info->lang & GCOV_MODULE_LANG_MASK))
{
if (dump_enabled_p ())
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
"Not importing %s: source language"
" different from primary module's source"
" language",
mod_info->source_filename);
}
else if (module_infos_read == max_group
/* If reordering is specified, delay the cutoff
until after sorting. */
&& !getenv ("LIPO_REORDER_GROUP"))
{
if (dump_enabled_p ())
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
"Not importing %s: maximum group size"
" reached", mod_info->source_filename);
}
else if (incompatible_cl_args (module_infos[0], mod_info))
{
if (dump_enabled_p ())
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
"Not importing %s: command-line"
" arguments not compatible with primary"
" module",
mod_info->source_filename);
}
else if ((fd = open (aux_da_filename, O_RDONLY)) < 0)
{
if (dump_enabled_p ())
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
"Not importing %s: couldn't open %s",
mod_info->source_filename,
aux_da_filename);
}
else if ((mod_info->lang & GCOV_MODULE_ASM_STMTS)
&& flag_ripa_disallow_asm_modules)
{
if (dump_enabled_p ())
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
"Not importing %s: contains assembler"
" statements", mod_info->source_filename);
}
else if (mod_info->is_primary == false
&& MODULE_EXPORTED_FLAG (mod_info) == false)
{
warning (0, "MODULE_ID=%d (%s) is an auxiliary module, "
"but export_bit is not set. \n",
mod_info->ident, mod_info->source_filename);
}
else
{
close (fd);
module_infos_read++;
add_input_filename (mod_info->source_filename);
module_infos = XRESIZEVEC (struct gcov_module_info *,
module_infos, num_in_fnames);
gcc_assert (num_in_fnames == module_infos_read);
module_infos[module_infos_read - 1]
= XCNEWVAR (struct gcov_module_info, info_sz);
memcpy (module_infos[module_infos_read - 1], mod_info,
info_sz);
}
}
if (dump_enabled_p ())
{
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
"MODULE Id=%d, Is_Primary=%s,"
" Is_Exported=%s, Include_all=%s, Name=%s (%s)",
mod_info->ident, mod_info->is_primary?"yes":"no",
MODULE_EXPORTED_FLAG (mod_info)?"yes":"no",
MODULE_INCLUDE_ALL_AUX_FLAG (mod_info)?"yes"
:"no",
mod_info->source_filename,
mod_info->da_filename);
}
}
gcov_sync (offset, length);
if ((is_error = gcov_is_error ()))
{
error (is_error < 0 ? "%qs has overflowed" : "%qs is corrupted",
da_file_name);
counts_hash.dispose ();
break;
}
}
if ((imports_filename = getenv ("LIPO_REORDER_GROUP"))
&& flag_dyn_ipa && !module_id)
{
reorder_module_groups (imports_filename, max_group);
if (module_infos_read != num_in_fnames)
module_infos_read = num_in_fnames;
}
/* TODO: profile based multiple module compilation does not work
together with command line (-combine) based ipo -- add a nice
warning and bail out instead of asserting. */
if (modset)
pointer_set_destroy (modset);
gcc_assert (module_infos_read == 0
|| module_infos_read == num_in_fnames);
if (flag_dyn_ipa)
gcc_assert (primary_module_id && num_in_fnames >= 1);
gcov_close ();
}
/* Returns the coverage data entry for counter type COUNTER of function
FUNC. EXPECTED is the number of expected counter entries. */
static counts_entry_t *
get_coverage_counts_entry (struct function *func, unsigned counter)
{
counts_entry_t *entry, elt;
elt.ident = FUNC_DECL_GLOBAL_ID (func);
elt.ctr = counter;
entry = counts_hash.find (&elt);
return entry;
}
/* Returns the counters for a particular tag. */
gcov_type *
get_coverage_counts (unsigned counter, unsigned expected,
unsigned cfg_checksum, unsigned lineno_checksum,
const struct gcov_ctr_summary **summary)
{
counts_entry_t *entry;
/* No hash table, no counts. */
if (!counts_hash.is_created ())
{
static int warned = 0;
if (!warned++ && dump_enabled_p ())
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
(flag_guess_branch_prob
? "file %s not found, execution counts estimated"
: "file %s not found, execution counts assumed to "
"be zero"),
da_file_name);
return NULL;
}
entry = get_coverage_counts_entry (cfun, counter);
if (!entry || !entry->summary.num)
{
if (!flag_dyn_ipa && dump_enabled_p ())
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
"no coverage for function %s found",
IDENTIFIER_POINTER
(DECL_ASSEMBLER_NAME (current_function_decl)));
return NULL;
}
if (entry->cfg_checksum != cfg_checksum
|| entry->summary.num != expected)
{
static int warned = 0;
bool warning_printed = false;
tree id = DECL_ASSEMBLER_NAME (current_function_decl);
warning_printed =
warning_at (input_location, OPT_Wcoverage_mismatch,
"the control flow of function %qE does not match "
"its profile data (counter %qs)", id, ctr_names[counter]);
if (warning_printed && dump_enabled_p ())
{
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
"use -Wno-error=coverage-mismatch to tolerate "
"the mismatch but performance may drop if the "
"function is hot");
if (!seen_error ()
&& !warned++)
{
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
"coverage mismatch ignored");
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
flag_guess_branch_prob
? G_("execution counts estimated")
: G_("execution counts assumed to be zero"));
if (!flag_guess_branch_prob)
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
"this can result in poorly optimized code");
}
}
return NULL;
}
else if (entry->lineno_checksum != lineno_checksum)
{
warning (OPT_Wripa_opt_mismatch,
"Source location for function %qE have changed,"
" the profile data may be out of date",
DECL_ASSEMBLER_NAME (current_function_decl));
}
if (summary)
*summary = &entry->summary;
return entry->counts;
}
/* Returns the coverage data entry for counter type COUNTER of function
FUNC. On return, *N_COUNTS is set to the number of entries in the counter. */
gcov_type *
get_coverage_counts_no_warn (struct function *f, unsigned counter, unsigned *n_counts)
{
counts_entry_t *entry, elt;
/* No hash table, no counts. */
if (!counts_hash.is_created () || !f)
return NULL;
elt.ident = FUNC_DECL_GLOBAL_ID (f);
elt.ctr = counter;
entry = counts_hash.find (&elt);
if (!entry)
return NULL;
*n_counts = entry->summary.num;
return entry->counts;
}
/* Allocate NUM counters of type COUNTER. Returns nonzero if the
allocation succeeded. */
int
coverage_counter_alloc (unsigned counter, unsigned num)
{
if (no_coverage)
return 0;
if (!num)
return 1;
if (!fn_v_ctrs[counter])
{
tree array_type = build_array_type (get_gcov_type (), NULL_TREE);
fn_v_ctrs[counter]
= build_var (current_function_decl, array_type, counter);
}
fn_b_ctrs[counter] = fn_n_ctrs[counter];
fn_n_ctrs[counter] += num;
fn_ctr_mask |= 1 << counter;
return 1;
}
/* Generate a tree to access COUNTER NO. */
tree
tree_coverage_counter_ref (unsigned counter, unsigned no)
{
tree gcov_type_node = get_gcov_type ();
gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]);
no += fn_b_ctrs[counter];
/* "no" here is an array index, scaled to bytes later. */
return build4 (ARRAY_REF, gcov_type_node, fn_v_ctrs[counter],
build_int_cst (integer_type_node, no), NULL, NULL);
}
/* Generate a tree to access the address of COUNTER NO. */
tree
tree_coverage_counter_addr (unsigned counter, unsigned no)
{
tree gcov_type_node = get_gcov_type ();
gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]);
no += fn_b_ctrs[counter];
/* "no" here is an array index, scaled to bytes later. */
return build_fold_addr_expr (build4 (ARRAY_REF, gcov_type_node,
fn_v_ctrs[counter],
build_int_cst (integer_type_node, no),
NULL, NULL));
}
/* Generate a crc32 of a string with specified STR_LEN when it's not 0.
Non-zero STR_LEN should only be seen in LIPO mode. */
static unsigned
crc32_string_1 (unsigned chksum, const char *string, unsigned str_len)
{
char *dup;
if (!L_IPO_COMP_MODE || str_len == 0)
return crc32_string (chksum, string);
gcc_assert (str_len > 0 && str_len < strlen (string));
dup = xstrdup (string);
dup[str_len] = 0;
chksum = crc32_string (chksum, dup);
free (dup);
return chksum;
}
/* Generate a checksum for a string. CHKSUM is the current
checksum. */
static unsigned
coverage_checksum_string (unsigned chksum, const char *string)
{
int i;
char *dup = NULL;
unsigned lipo_orig_str_len = 0;
/* Strip out the ending ".cmo.[0-9]*" string from function
name. Otherwise we will have lineno checksum mismatch. */
if (L_IPO_COMP_MODE)
{
int len;
i = len = strlen (string);
while (i--)
if ((string[i] < '0' || string[i] > '9'))
break;
if ((i > 5) && (i != len - 1))
{
if (!strncmp (string + i - 4, ".cmo.", 5))
lipo_orig_str_len = i - 4;
}
}
/* Look for everything that looks if it were produced by
get_file_function_name and zero out the second part
that may result from flag_random_seed. This is not critical
as the checksums are used only for sanity checking. */
for (i = 0; string[i]; i++)
{
int offset = 0;
if (!strncmp (string + i, "_GLOBAL__N_", 11))
offset = 11;
if (!strncmp (string + i, "_GLOBAL__", 9))
offset = 9;
/* C++ namespaces do have scheme:
_GLOBAL__N_<filename>_<wrongmagicnumber>_<magicnumber>functionname
since filename might contain extra underscores there seems
to be no better chance then walk all possible offsets looking
for magicnumber. */
if (offset)
{
for (i = i + offset; string[i]; i++)
if (string[i]=='_')
{
int y;
for (y = 1; y < 9; y++)
if (!(string[i + y] >= '0' && string[i + y] <= '9')
&& !(string[i + y] >= 'A' && string[i + y] <= 'F'))
break;
if (y != 9 || string[i + 9] != '_')
continue;
for (y = 10; y < 18; y++)
if (!(string[i + y] >= '0' && string[i + y] <= '9')
&& !(string[i + y] >= 'A' && string[i + y] <= 'F'))
break;
if (y != 18)
continue;
if (!dup)
string = dup = xstrdup (string);
for (y = 10; y < 18; y++)
dup[i + y] = '0';
}
break;
}
}
chksum = crc32_string_1 (chksum, string, lipo_orig_str_len);
if (dup)
free (dup);
return chksum;
}
/* Compute checksum for the current function. We generate a CRC32. */
unsigned
coverage_compute_lineno_checksum (void)
{
tree name;
expanded_location xloc
= expand_location (DECL_SOURCE_LOCATION (current_function_decl));
unsigned chksum = xloc.line;
const char *pathless_filename = xloc.file;
int i;
for (i = strlen (xloc.file); i >= 0; i--)
if (IS_DIR_SEPARATOR (pathless_filename[i]))
{
pathless_filename += i + 1;
break;
}
chksum = coverage_checksum_string (chksum, pathless_filename);
/* Note: it is a bad design that C++ FE associate the convertion function type
with the name of the decl. This leads to cross contamination between different
conversion operators in different modules (If conv_type_names map is cleared
at the end of parsing of each module). */
if (flag_dyn_ipa && lang_hooks.user_conv_function_p (current_function_decl))
name = DECL_ASSEMBLER_NAME (current_function_decl);
else
name = DECL_NAME (current_function_decl);
chksum = coverage_checksum_string
(chksum, IDENTIFIER_POINTER (name));
return chksum;
}
/* Compute cfg checksum for the current function.
The checksum is calculated carefully so that
source code changes that doesn't affect the control flow graph
won't change the checksum.
This is to make the profile data useable across source code change.
The downside of this is that the compiler may use potentially
wrong profile data - that the source code change has non-trivial impact
on the validity of profile data (e.g. the reversed condition)
but the compiler won't detect the change and use the wrong profile data. */
unsigned
coverage_compute_cfg_checksum (void)
{
basic_block bb;
unsigned chksum = n_basic_blocks;
FOR_EACH_BB (bb)
{
edge e;
edge_iterator ei;
chksum = crc32_byte (chksum, bb->index);
FOR_EACH_EDGE (e, ei, bb->succs)
{
chksum = crc32_byte (chksum, e->dest->index);
}
}
return chksum;
}
/* Begin output to the notes file for the current function.
Writes the function header. Returns nonzero if data should be output. */
int
coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum)
{
expanded_location xloc;
unsigned long offset;
/* We don't need to output .gcno file unless we're under -ftest-coverage
(e.g. -fprofile-arcs/generate/use don't need .gcno to work). */
if (no_coverage || !bbg_file_name)
return 0;
xloc = expand_location (DECL_SOURCE_LOCATION (current_function_decl));
/* Announce function */
offset = gcov_write_tag (GCOV_TAG_FUNCTION);
gcov_write_unsigned (FUNC_DECL_FUNC_ID (cfun));
gcov_write_unsigned (lineno_checksum);
gcov_write_unsigned (cfg_checksum);
gcov_write_string (IDENTIFIER_POINTER
(DECL_ASSEMBLER_NAME (current_function_decl)));
gcov_write_string (xloc.file);
gcov_write_unsigned (xloc.line);
gcov_write_length (offset);
return !gcov_is_error ();
}
/* Finish coverage data for the current function. Verify no output
error has occurred. Save function coverage counts. */
void
coverage_end_function (unsigned lineno_checksum, unsigned cfg_checksum)
{
unsigned i;
if (bbg_file_name && gcov_is_error ())
{
warning (0, "error writing %qs", bbg_file_name);
unlink (bbg_file_name);
bbg_file_name = NULL;
}
if (fn_ctr_mask)
{
struct coverage_data *item = 0;
/* If the function is extern (i.e. extern inline), then we won't
be outputting it, so don't chain it onto the function
list. */
if (!DECL_EXTERNAL (current_function_decl))
{
item = ggc_alloc_coverage_data ();
item->ident = FUNC_DECL_FUNC_ID (cfun);
item->lineno_checksum = lineno_checksum;
item->cfg_checksum = cfg_checksum;
item->fn_decl = current_function_decl;
item->next = 0;
*functions_tail = item;
functions_tail = &item->next;
}
for (i = 0; i != GCOV_COUNTERS; i++)
{
tree var = fn_v_ctrs[i];
if (item)
item->ctr_vars[i] = var;
if (var)
{
tree array_type = build_index_type (size_int (fn_n_ctrs[i] - 1));
array_type = build_array_type (get_gcov_type (), array_type);
TREE_TYPE (var) = array_type;
DECL_SIZE (var) = TYPE_SIZE (array_type);
DECL_SIZE_UNIT (var) = TYPE_SIZE_UNIT (array_type);
varpool_finalize_decl (var);
}
fn_b_ctrs[i] = fn_n_ctrs[i] = 0;
fn_v_ctrs[i] = NULL_TREE;
}
prg_ctr_mask |= fn_ctr_mask;
fn_ctr_mask = 0;
}
}
/* True if a function entry corresponding to the given FN_IDENT
is present in the coverage internal data structures. */
bool
coverage_function_present (unsigned fn_ident)
{
struct coverage_data *item = functions_head;
while (item && item->ident != fn_ident)
item = item->next;
return item != NULL;
}
/* Update function and program direct-call coverage counts. */
void
coverage_dc_end_function (void)
{
tree var;
if (fn_ctr_mask)
{
const unsigned idx = GCOV_COUNTER_DIRECT_CALL;
struct coverage_data *item = functions_head;
while (item && item->ident != (unsigned) FUNC_DECL_FUNC_ID (cfun))
item = item->next;
/* If a matching function entry hasn't been found, either this function
has had no coverage counts added in the profile pass, or this
is a new function (function versioning, etc). Create a new entry. */
if (!item)
{
int cnt;
item = ggc_alloc_coverage_data ();
*functions_tail = item;
functions_tail = &item->next;
item->next = 0;
item->ident = FUNC_DECL_FUNC_ID (cfun);
item->fn_decl = current_function_decl;
item->lineno_checksum = coverage_compute_lineno_checksum ();
item->cfg_checksum = coverage_compute_cfg_checksum ();
for (cnt = 0; cnt < GCOV_COUNTERS; cnt++)
item->ctr_vars[cnt] = NULL_TREE;
}
var = fn_v_ctrs[idx];
item->ctr_vars[idx] = var;
if (var)
{
tree array_type = build_index_type (size_int (fn_n_ctrs[idx] - 1));
array_type = build_array_type (get_gcov_type (), array_type);
TREE_TYPE (var) = array_type;
DECL_SIZE (var) = TYPE_SIZE (array_type);
DECL_SIZE_UNIT (var) = TYPE_SIZE_UNIT (array_type);
varpool_finalize_decl (var);
}
fn_n_ctrs[idx] = fn_b_ctrs[idx] = 0;
fn_v_ctrs[idx] = NULL_TREE;
prg_ctr_mask |= fn_ctr_mask;
fn_ctr_mask = 0;
}
}
/* Build a coverage variable of TYPE for function FN_DECL. If COUNTER
>= 0 it is a counter array, otherwise it is the function structure. */
static tree
build_var (tree fn_decl, tree type, int counter)
{
tree var = build_decl (BUILTINS_LOCATION, VAR_DECL, NULL_TREE, type);
const char *fn_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (fn_decl));
char *buf;
size_t fn_name_len, len;
fn_name = targetm.strip_name_encoding (fn_name);
fn_name_len = strlen (fn_name);
buf = XALLOCAVEC (char, fn_name_len + 8 + sizeof (int) * 3);
if (counter < 0)
strcpy (buf, "__gcov__");
else
sprintf (buf, "__gcov%u_", counter);
len = strlen (buf);
#ifndef NO_DOT_IN_LABEL
buf[len - 1] = '.';
#elif !defined NO_DOLLAR_IN_LABEL
buf[len - 1] = '$';
#endif
memcpy (buf + len, fn_name, fn_name_len + 1);
DECL_NAME (var) = get_identifier (buf);
TREE_STATIC (var) = 1;
TREE_ADDRESSABLE (var) = 1;
DECL_ALIGN (var) = TYPE_ALIGN (type);
DECL_ARTIFICIAL (var) = 1;
return var;
}
/* Creates the gcov_fn_info RECORD_TYPE. */
static void
build_fn_info_type (tree type, unsigned counters, tree gcov_info_type)
{
tree ctr_info = lang_hooks.types.make_type (RECORD_TYPE);
tree field, fields;
tree array_type;
gcc_assert (counters);
/* ctr_info::num */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
get_gcov_unsigned_t ());
fields = field;
/* ctr_info::values */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
build_pointer_type (get_gcov_type ()));
DECL_CHAIN (field) = fields;
fields = field;
finish_builtin_struct (ctr_info, "__gcov_ctr_info", fields, NULL_TREE);
/* key */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
build_pointer_type (build_qualified_type
(gcov_info_type, TYPE_QUAL_CONST)));
fields = field;
/* ident */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* lineno_checksum */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* cfg checksum */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
array_type = build_index_type (size_int (counters - 1));
array_type = build_array_type (ctr_info, array_type);
/* counters */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, array_type);
DECL_CHAIN (field) = fields;
fields = field;
finish_builtin_struct (type, "__gcov_fn_info", fields, NULL_TREE);
}
/* Returns a CONSTRUCTOR for a gcov_fn_info. DATA is
the coverage data for the function and TYPE is the gcov_fn_info
RECORD_TYPE. KEY is the object file key. */
static tree
build_fn_info (const struct coverage_data *data, tree type, tree key)
{
tree fields = TYPE_FIELDS (type);
tree ctr_type;
unsigned ix;
vec<constructor_elt, va_gc> *v1 = NULL;
vec<constructor_elt, va_gc> *v2 = NULL;
/* key */
CONSTRUCTOR_APPEND_ELT (v1, fields,
build1 (ADDR_EXPR, TREE_TYPE (fields), key));
fields = DECL_CHAIN (fields);
/* ident */
CONSTRUCTOR_APPEND_ELT (v1, fields,
build_int_cstu (get_gcov_unsigned_t (),
data->ident));
fields = DECL_CHAIN (fields);
/* lineno_checksum */
CONSTRUCTOR_APPEND_ELT (v1, fields,
build_int_cstu (get_gcov_unsigned_t (),
data->lineno_checksum));
fields = DECL_CHAIN (fields);
/* cfg_checksum */
CONSTRUCTOR_APPEND_ELT (v1, fields,
build_int_cstu (get_gcov_unsigned_t (),
data->cfg_checksum));
fields = DECL_CHAIN (fields);
/* counters */
ctr_type = TREE_TYPE (TREE_TYPE (fields));
for (ix = 0; ix != GCOV_COUNTERS; ix++)
if (prg_ctr_mask & (1 << ix))
{
vec<constructor_elt, va_gc> *ctr = NULL;
tree var = data->ctr_vars[ix];
unsigned count = 0;
if (var)
count
= tree_low_cst (TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (var))), 0)
+ 1;
CONSTRUCTOR_APPEND_ELT (ctr, TYPE_FIELDS (ctr_type),
build_int_cstu (get_gcov_unsigned_t (),
count));
if (var)
CONSTRUCTOR_APPEND_ELT (ctr, DECL_CHAIN (TYPE_FIELDS (ctr_type)),
build_fold_addr_expr (var));
CONSTRUCTOR_APPEND_ELT (v2, NULL, build_constructor (ctr_type, ctr));
/* In LIPO mode, coverage_finish is called late when pruning can not be
* done, so we need to force emitting counter variables even for
* eliminated functions to avoid unsat. */
if (flag_dyn_ipa && var)
varpool_finalize_decl (var);
}
CONSTRUCTOR_APPEND_ELT (v1, fields,
build_constructor (TREE_TYPE (fields), v2));
return build_constructor (type, v1);
}
/* Create gcov_info struct. TYPE is the incomplete RECORD_TYPE to be
completed, and FN_INFO_PTR_TYPE is a pointer to the function info type. */
static void
build_info_type (tree type, tree fn_info_ptr_type)
{
tree field, fields = NULL_TREE;
tree merge_fn_type, mod_type;
/* Version ident */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* mod_info */
mod_type = build_gcov_module_info_type ();
mod_type = build_pointer_type (mod_type);
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, mod_type);
DECL_CHAIN (field) = fields;
fields = field;
/* next pointer */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
build_pointer_type (build_qualified_type
(type, TYPE_QUAL_CONST)));
DECL_CHAIN (field) = fields;
fields = field;
/* stamp */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* Filename */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
build_pointer_type (build_qualified_type
(char_type_node, TYPE_QUAL_CONST)));
DECL_CHAIN (field) = fields;
fields = field;
/* eof_pos */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
NULL_TREE, get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* merge fn array */
merge_fn_type
= build_function_type_list (void_type_node,
build_pointer_type (get_gcov_type ()),
get_gcov_unsigned_t (), NULL_TREE);
merge_fn_type
= build_array_type (build_pointer_type (merge_fn_type),
build_index_type (size_int (GCOV_COUNTERS - 1)));
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
merge_fn_type);
DECL_CHAIN (field) = fields;
fields = field;
/* n_functions */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* function_info pointer pointer */
fn_info_ptr_type = build_pointer_type
(build_qualified_type (fn_info_ptr_type, TYPE_QUAL_CONST));
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
fn_info_ptr_type);
DECL_CHAIN (field) = fields;
fields = field;
finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE);
}
/* Compute an array (tree) of include path strings. STRING_TYPE is
the path string type, INC_PATH_VALUE is the initial value of the
path array, PATHS gives raw path string values, and NUM is the
number of paths. */
static void
build_inc_path_array_value (tree string_type, vec<constructor_elt, va_gc> **v,
cpp_dir *paths, int num)
{
int i;
cpp_dir *pdir;
for (i = 0, pdir = paths; i < num; pdir = pdir->next)
{
const char *path_raw_string;
int path_string_length;
tree path_string;
path_raw_string = pdir->name;
path_string_length = strlen (path_raw_string);
path_string = build_string (path_string_length + 1, path_raw_string);
TREE_TYPE (path_string) = build_array_type
(char_type_node, build_index_type
(build_int_cst (NULL_TREE, path_string_length)));
CONSTRUCTOR_APPEND_ELT (*v, NULL,
build1 (ADDR_EXPR, string_type, path_string));
i++;
}
}
/* Compute an array (tree) of strings. STR_TYPE is the string type,
STR_ARRAY_VALUE is the initial value of the string array, and HEAD gives
the list of raw strings. */
static void
build_str_array_value (tree str_type, vec<constructor_elt, va_gc> **v,
struct str_list *head)
{
const char *raw_str;
int str_length;
while (head)
{
tree str;
raw_str = head->str;
str_length = strlen (raw_str);
str = build_string (str_length + 1, raw_str);
TREE_TYPE (str) =
build_array_type (char_type_node,
build_index_type (build_int_cst (NULL_TREE,
str_length)));
CONSTRUCTOR_APPEND_ELT (*v, NULL,
build1 (ADDR_EXPR, str_type, str));
head = head->next;
}
return;
}
/* Compute an array (tree) of command-line argument strings. STRING_TYPE is
the string type, CL_ARGS_VALUE is the initial value of the command-line
args array. */
static void
build_cl_args_array_value (tree string_type, vec<constructor_elt, va_gc> **v)
{
unsigned int i;
for (i = 0; i < num_lipo_cl_args; i++)
{
int arg_length = strlen (lipo_cl_args[i]);
tree arg_string = build_string (arg_length + 1, lipo_cl_args[i]);
TREE_TYPE (arg_string) =
build_array_type (char_type_node,
build_index_type (build_int_cst (NULL_TREE,
arg_length)));
CONSTRUCTOR_APPEND_ELT (*v, NULL,
build1 (ADDR_EXPR, string_type, arg_string));
}
return;
}
/* Returns the type of the module info associated with the
current source module being compiled. */
static tree
build_gcov_module_info_type (void)
{
tree type, field, fields = NULL_TREE;
tree string_type, index_type, string_array_type;
cpp_dir *quote_paths, *bracket_paths, *system_paths, *pdir;
int num_quote_paths = 0, num_bracket_paths = 0, num_system_paths = 0;
type = lang_hooks.types.make_type (RECORD_TYPE);
string_type = build_pointer_type (
build_qualified_type (char_type_node,
TYPE_QUAL_CONST));
/* ident */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
NULL_TREE, get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* is_primary */
/* We also overload this field to store a flag that indicates whether this
module was built in regular FDO or LIPO mode (-fripa). When reading this
field from a GCDA file, it should be used as the IS_PRIMARY flag. When
reading this field from the binary's data section, it should be used
as a FDO/LIPO flag. */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
NULL_TREE, get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* flags: is_exported and include_all_aux flag. */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
NULL_TREE, get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* lang field */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
NULL_TREE, get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* ggc_memory field */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
NULL_TREE, get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* da_filename */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
NULL_TREE, string_type);
DECL_CHAIN (field) = fields;
fields = field;
/* Source name */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
NULL_TREE, string_type);
DECL_CHAIN (field) = fields;
fields = field;
/* Num quote paths */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
NULL_TREE, get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* Num bracket paths */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
NULL_TREE, get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* Num system paths */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
NULL_TREE, get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* Num -D/-U options. */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
NULL_TREE, get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* Num -imacro/-include options. */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* Num command-line args. */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
NULL_TREE, get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
get_include_chains (&quote_paths, &bracket_paths, &system_paths);
for (pdir = quote_paths; pdir; pdir = pdir->next)
{
if (pdir == bracket_paths)
break;
num_quote_paths++;
}
for (pdir = bracket_paths; pdir; pdir = pdir->next)
{
if (pdir == system_paths)
break;
num_bracket_paths++;
}
for (pdir = system_paths; pdir; pdir = pdir->next)
num_system_paths++;
/* string array */
index_type = build_index_type (build_int_cst (NULL_TREE,
num_quote_paths +
num_bracket_paths +
num_system_paths +
num_cpp_defines +
num_cpp_includes +
num_lipo_cl_args));
string_array_type = build_array_type (string_type, index_type);
field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
NULL_TREE, string_array_type);
DECL_CHAIN (field) = fields;
fields = field;
finish_builtin_struct (type, "__gcov_module_info", fields, NULL_TREE);
return type;
}
/* Returns the value of the module info associated with the
current source module being compiled. */
static tree
build_gcov_module_info_value (tree mod_type)
{
tree info_fields, mod_info;
tree value = NULL_TREE;
int file_name_len;
tree filename_string, string_array_type, string_type;
cpp_dir *quote_paths, *bracket_paths, *system_paths, *pdir;
int num_quote_paths = 0, num_bracket_paths = 0, num_system_paths = 0;
unsigned lang;
char name_buf[50];
vec<constructor_elt,va_gc> *v = NULL, *path_v = NULL;
info_fields = TYPE_FIELDS (mod_type);
/* ident */
CONSTRUCTOR_APPEND_ELT (v, info_fields,
build_int_cstu (get_gcov_unsigned_t (), 0));
info_fields = DECL_CHAIN (info_fields);
/* is_primary */
/* We also overload this field to store a flag that indicates whether this
module was built in regular FDO or LIPO mode (-fripa). When reading this
field from a GCDA file, it should be used as the IS_PRIMARY flag. When
reading this field from the binary's data section, it should be used
as a FDO/LIPO flag. */
CONSTRUCTOR_APPEND_ELT (v, info_fields,
build_int_cstu (get_gcov_unsigned_t (),
flag_dyn_ipa ? 1 : 0));
info_fields = DECL_CHAIN (info_fields);
/* flags */
CONSTRUCTOR_APPEND_ELT (v, info_fields,
build_int_cstu (get_gcov_unsigned_t (), 0));
info_fields = DECL_CHAIN (info_fields);
/* lang field */
if (!strcmp (lang_hooks.name, "GNU C"))
lang = GCOV_MODULE_C_LANG;
else if (!strcmp (lang_hooks.name, "GNU C++"))
lang = GCOV_MODULE_CPP_LANG;
else
lang = GCOV_MODULE_UNKNOWN_LANG;
if (has_asm_statement)
lang |= GCOV_MODULE_ASM_STMTS;
CONSTRUCTOR_APPEND_ELT (v, info_fields,
build_int_cstu (get_gcov_unsigned_t (), lang));
info_fields = DECL_CHAIN (info_fields);
/* ggc_memory field */
CONSTRUCTOR_APPEND_ELT (v, info_fields,
build_int_cstu (get_gcov_unsigned_t (), ggc_total_memory));
info_fields = DECL_CHAIN (info_fields);
/* da_filename */
string_type = TREE_TYPE (info_fields);
file_name_len = strlen (da_base_file_name);
filename_string = build_string (file_name_len + 1, da_base_file_name);
TREE_TYPE (filename_string) = build_array_type
(char_type_node, build_index_type
(build_int_cst (NULL_TREE, file_name_len)));
CONSTRUCTOR_APPEND_ELT (v, info_fields,
build1 (ADDR_EXPR, string_type, filename_string));
info_fields = DECL_CHAIN (info_fields);
/* Source name */
file_name_len = strlen (main_input_file_name);
filename_string = build_string (file_name_len + 1, main_input_file_name);
TREE_TYPE (filename_string) = build_array_type
(char_type_node, build_index_type
(build_int_cst (NULL_TREE, file_name_len)));
CONSTRUCTOR_APPEND_ELT (v, info_fields,
build1 (ADDR_EXPR, string_type, filename_string));
info_fields = DECL_CHAIN (info_fields);
get_include_chains (&quote_paths, &bracket_paths, &system_paths);
for (pdir = quote_paths; pdir; pdir = pdir->next)
{
if (pdir == bracket_paths)
break;
num_quote_paths++;
}
for (pdir = bracket_paths; pdir; pdir = pdir->next)
{
if (pdir == system_paths)
break;
num_bracket_paths++;
}
for (pdir = system_paths; pdir; pdir = pdir->next)
num_system_paths++;
/* Num quote paths */
CONSTRUCTOR_APPEND_ELT (v, info_fields,
build_int_cstu (get_gcov_unsigned_t (),
num_quote_paths));
info_fields = DECL_CHAIN (info_fields);
/* Num bracket paths */
CONSTRUCTOR_APPEND_ELT (v, info_fields,
build_int_cstu (get_gcov_unsigned_t (),
num_bracket_paths));
info_fields = DECL_CHAIN (info_fields);
/* Num system paths */
CONSTRUCTOR_APPEND_ELT (v, info_fields,
build_int_cstu (get_gcov_unsigned_t (),
num_system_paths));
info_fields = DECL_CHAIN (info_fields);
/* Num -D/-U options. */
CONSTRUCTOR_APPEND_ELT (v, info_fields,
build_int_cstu (get_gcov_unsigned_t (),
num_cpp_defines));
info_fields = DECL_CHAIN (info_fields);
/* Num -imacro/-include options. */
CONSTRUCTOR_APPEND_ELT (v, info_fields,
build_int_cstu (get_gcov_unsigned_t (),
num_cpp_includes));
info_fields = DECL_CHAIN (info_fields);
/* Num command-line args. */
CONSTRUCTOR_APPEND_ELT (v, info_fields,
build_int_cstu (get_gcov_unsigned_t (),
num_lipo_cl_args));
info_fields = DECL_CHAIN (info_fields);
/* string array */
string_array_type = TREE_TYPE (info_fields);
build_inc_path_array_value (string_type, &path_v,
quote_paths, num_quote_paths);
build_inc_path_array_value (string_type, &path_v,
bracket_paths, num_bracket_paths);
build_inc_path_array_value (string_type, &path_v,
system_paths, num_system_paths);
build_str_array_value (string_type, &path_v,
cpp_defines_head);
build_str_array_value (string_type, &path_v,
cpp_includes_head);
build_cl_args_array_value (string_type, &path_v);
CONSTRUCTOR_APPEND_ELT (v, info_fields,
build_constructor (string_array_type, path_v));
info_fields = DECL_CHAIN (info_fields);
gcc_assert (!info_fields);
value = build_constructor (mod_type, v);
mod_info = build_decl (BUILTINS_LOCATION, VAR_DECL,
NULL_TREE, TREE_TYPE (value));
TREE_STATIC (mod_info) = 1;
ASM_GENERATE_INTERNAL_LABEL (name_buf, "MODINFO", 0);
DECL_NAME (mod_info) = get_identifier (name_buf);
DECL_INITIAL (mod_info) = value;
/* Build structure. */
varpool_finalize_decl (mod_info);
return mod_info;
}
/* Returns a CONSTRUCTOR for the gcov_info object. INFO_TYPE is the
gcov_info structure type, FN_ARY is the array of pointers to
function info objects. */
static tree
build_info (tree info_type, tree fn_ary)
{
tree info_fields = TYPE_FIELDS (info_type);
tree merge_fn_type, n_funcs;
unsigned ix;
tree mod_value = NULL_TREE;
tree filename_string;
int da_file_name_len;
vec<constructor_elt, va_gc> *v1 = NULL;
vec<constructor_elt, va_gc> *v2 = NULL;
/* Version ident */
CONSTRUCTOR_APPEND_ELT (v1, info_fields,
build_int_cstu (TREE_TYPE (info_fields),
GCOV_VERSION));
info_fields = DECL_CHAIN (info_fields);
/* mod_info */
mod_value = build_gcov_module_info_value (TREE_TYPE (TREE_TYPE (info_fields)));
mod_value = build1 (ADDR_EXPR,
build_pointer_type (TREE_TYPE (mod_value)),
mod_value);
CONSTRUCTOR_APPEND_ELT (v1, info_fields, mod_value);
info_fields = DECL_CHAIN (info_fields);
/* next -- NULL */
CONSTRUCTOR_APPEND_ELT (v1, info_fields, null_pointer_node);
info_fields = DECL_CHAIN (info_fields);
/* stamp */
CONSTRUCTOR_APPEND_ELT (v1, info_fields,
build_int_cstu (TREE_TYPE (info_fields),
bbg_file_stamp));
info_fields = DECL_CHAIN (info_fields);
/* Filename */
da_file_name_len = strlen (da_file_name);
filename_string = build_string (da_file_name_len + 1, da_file_name);
TREE_TYPE (filename_string) = build_array_type
(char_type_node, build_index_type (size_int (da_file_name_len)));
CONSTRUCTOR_APPEND_ELT (v1, info_fields,
build1 (ADDR_EXPR, TREE_TYPE (info_fields),
filename_string));
info_fields = DECL_CHAIN (info_fields);
/* eof_pos */
CONSTRUCTOR_APPEND_ELT (v1, info_fields,
build_int_cstu (TREE_TYPE (info_fields), 0));
info_fields = DECL_CHAIN (info_fields);
/* merge fn array -- NULL slots indicate unmeasured counters */
merge_fn_type = TREE_TYPE (TREE_TYPE (info_fields));
for (ix = 0; ix != GCOV_COUNTERS; ix++)
{
tree ptr = null_pointer_node;
if ((1u << ix) & prg_ctr_mask)
{
tree merge_fn = build_decl (BUILTINS_LOCATION,
FUNCTION_DECL,
get_identifier (ctr_merge_functions[ix]),
TREE_TYPE (merge_fn_type));
DECL_EXTERNAL (merge_fn) = 1;
TREE_PUBLIC (merge_fn) = 1;
DECL_ARTIFICIAL (merge_fn) = 1;
TREE_NOTHROW (merge_fn) = 1;
/* Initialize assembler name so we can stream out. */
DECL_ASSEMBLER_NAME (merge_fn);
ptr = build1 (ADDR_EXPR, merge_fn_type, merge_fn);
}
CONSTRUCTOR_APPEND_ELT (v2, NULL, ptr);
}
CONSTRUCTOR_APPEND_ELT (v1, info_fields,
build_constructor (TREE_TYPE (info_fields), v2));
info_fields = DECL_CHAIN (info_fields);
/* n_functions */
n_funcs = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (fn_ary)));
n_funcs = fold_build2 (PLUS_EXPR, TREE_TYPE (info_fields),
n_funcs, size_one_node);
CONSTRUCTOR_APPEND_ELT (v1, info_fields, n_funcs);
info_fields = DECL_CHAIN (info_fields);
/* functions */
CONSTRUCTOR_APPEND_ELT (v1, info_fields,
build1 (ADDR_EXPR, TREE_TYPE (info_fields), fn_ary));
info_fields = DECL_CHAIN (info_fields);
gcc_assert (!info_fields);
return build_constructor (info_type, v1);
}
/* Generate the constructor function to call __gcov_init. */
static void
build_init_ctor (tree gcov_info_type)
{
tree ctor, stmt, init_fn;
/* Build a decl for __gcov_init. */
init_fn = build_pointer_type (gcov_info_type);
init_fn = build_function_type_list (void_type_node, init_fn, NULL);
init_fn = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
get_identifier ("__gcov_init"), init_fn);
TREE_PUBLIC (init_fn) = 1;
DECL_EXTERNAL (init_fn) = 1;
DECL_ASSEMBLER_NAME (init_fn);
/* Generate a call to __gcov_init(&gcov_info). */
ctor = NULL;
stmt = build_fold_addr_expr (gcov_info_var);
stmt = build_call_expr (init_fn, 1, stmt);
append_to_statement_list (stmt, &ctor);
/* Generate a constructor to run it. */
cgraph_build_static_cdtor ('I', ctor, DEFAULT_INIT_PRIORITY);
}
/* Create the gcov_info types and object. Does not generate the initializer
for the object. Returns TRUE if coverage data is being emitted. */
static bool
coverage_obj_init (void)
{
tree gcov_info_type;
unsigned n_counters = 0;
unsigned ix;
struct coverage_data *fn;
struct coverage_data **fn_prev;
char name_buf[32];
no_coverage = 1; /* Disable any further coverage. */
if (!prg_ctr_mask)
return false;
if (cgraph_dump_file)
fprintf (cgraph_dump_file, "Using data file %s\n", da_file_name);
/* Prune functions. */
if (!flag_dyn_ipa)
/* in lipo mode, coverage_finish is called when function struct is cleared,
so pruning code here will skip all functions. */
for (fn_prev = &functions_head; (fn = *fn_prev);)
if (DECL_STRUCT_FUNCTION (fn->fn_decl))
fn_prev = &fn->next;
else
/* The function is not being emitted, remove from list. */
*fn_prev = fn->next;
if (functions_head == NULL)
return false;
for (ix = 0; ix != GCOV_COUNTERS; ix++)
if ((1u << ix) & prg_ctr_mask)
n_counters++;
/* Build the info and fn_info types. These are mutually recursive. */
gcov_info_type = lang_hooks.types.make_type (RECORD_TYPE);
gcov_fn_info_type = lang_hooks.types.make_type (RECORD_TYPE);
gcov_fn_info_ptr_type = build_pointer_type
(build_qualified_type (gcov_fn_info_type, TYPE_QUAL_CONST));
build_fn_info_type (gcov_fn_info_type, n_counters, gcov_info_type);
build_info_type (gcov_info_type, gcov_fn_info_ptr_type);
/* Build the gcov info var, this is referred to in its own
initializer. */
gcov_info_var = build_decl (BUILTINS_LOCATION,
VAR_DECL, NULL_TREE, gcov_info_type);
TREE_STATIC (gcov_info_var) = 1;
ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 0);
DECL_NAME (gcov_info_var) = get_identifier (name_buf);
return true;
}
/* Generate the coverage function info for FN and DATA. Append a
pointer to that object to CTOR and return the appended CTOR. */
static vec<constructor_elt, va_gc> *
coverage_obj_fn (vec<constructor_elt, va_gc> *ctor, tree fn,
struct coverage_data const *data)
{
tree init = build_fn_info (data, gcov_fn_info_type, gcov_info_var);
tree var = build_var (fn, gcov_fn_info_type, -1);
DECL_INITIAL (var) = init;
varpool_finalize_decl (var);
CONSTRUCTOR_APPEND_ELT (ctor, NULL,
build1 (ADDR_EXPR, gcov_fn_info_ptr_type, var));
return ctor;
}
/* Finalize the coverage data. Generates the array of pointers to
function objects from CTOR. Generate the gcov_info initializer.
Generate the constructor function to call __gcov_init. */
static void
coverage_obj_finish (vec<constructor_elt, va_gc> *ctor)
{
unsigned n_functions = vec_safe_length (ctor);
tree fn_info_ary_type = build_array_type
(build_qualified_type (gcov_fn_info_ptr_type, TYPE_QUAL_CONST),
build_index_type (size_int (n_functions - 1)));
tree fn_info_ary = build_decl (BUILTINS_LOCATION, VAR_DECL, NULL_TREE,
fn_info_ary_type);
char name_buf[32];
TREE_STATIC (fn_info_ary) = 1;
ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 1);
DECL_NAME (fn_info_ary) = get_identifier (name_buf);
DECL_INITIAL (fn_info_ary) = build_constructor (fn_info_ary_type, ctor);
varpool_finalize_decl (fn_info_ary);
DECL_INITIAL (gcov_info_var)
= build_info (TREE_TYPE (gcov_info_var), fn_info_ary);
build_init_ctor (TREE_TYPE (gcov_info_var));
varpool_finalize_decl (gcov_info_var);
}
/* Get the da file name, given base file name. */
static char *
get_da_file_name (const char *base_file_name)
{
char *da_file_name;
int len = strlen (base_file_name);
const char *prefix = profile_data_prefix;
int prefix_len = 0;
if (profile_data_prefix == 0 && !IS_ABSOLUTE_PATH(&base_file_name[0]))
{
profile_data_prefix = getpwd ();
prefix = profile_data_prefix;
}
prefix_len = (prefix) ? strlen (prefix) + 1 : 0;
/* Name of da file. */
da_file_name = XNEWVEC (char, len + strlen (GCOV_DATA_SUFFIX)
+ prefix_len + 2);
if (prefix)
{
strcpy (da_file_name, prefix);
da_file_name[prefix_len - 1] = '/';
da_file_name[prefix_len] = 0;
}
else
da_file_name[0] = 0;
strcat (da_file_name, base_file_name);
if (profile_base_name_suffix_to_strip)
{
int base_name_len = strlen (da_file_name);
int suffix_to_strip_len = strlen (profile_base_name_suffix_to_strip);
if (base_name_len > suffix_to_strip_len
&& !strcmp (da_file_name + (base_name_len - suffix_to_strip_len),
profile_base_name_suffix_to_strip))
da_file_name[base_name_len - suffix_to_strip_len] = '\0';
}
strcat (da_file_name, GCOV_DATA_SUFFIX);
return da_file_name;
}
/* Callback to move counts_entry from one hash table to
the target hashtable */
int
move_hash_entry_callback (counts_entry **x,
hash_table <counts_entry> *target_counts_hash)
{
counts_entry *entry = *x;
counts_entry **slot;
slot = target_counts_hash->find_slot (entry, INSERT);
*slot = entry;
return 1;
}
/* Rebuild counts_hash already built the primary module. This hashtable
was built with a module-id of zero. It needs to be rebuilt taking the
correct primary module-id into account. */
int
rehash_callback (counts_entry **x,
hash_table <counts_entry> *target_counts_hash)
{
counts_entry *entry = *x;
counts_entry **slot;
entry->ident = GEN_FUNC_GLOBAL_ID (primary_module_id, entry->ident);
slot = target_counts_hash->find_slot (entry, INSERT);
*slot = entry;
return 1;
}
/* Rebuild counts_hash already built the primary module. This hashtable
was built with a module-id of zero. It needs to be rebuilt taking the
correct primary module-id into account. */
static void
rebuild_counts_hash (void)
{
hash_table <counts_entry> tmp_counts_hash;
tmp_counts_hash.create (10);
gcc_assert (primary_module_id);
rebuilding_counts_hash = true;
/* Move the counts entries to the temporary hashtable. */
counts_hash.traverse_noresize <
hash_table <counts_entry> *,
move_hash_entry_callback> (&tmp_counts_hash);
counts_hash.empty ();
/* Now rehash and copy back. */
tmp_counts_hash.traverse_noresize <
hash_table <counts_entry> *,
rehash_callback> (&counts_hash);
tmp_counts_hash.dispose();
rebuilding_counts_hash = false;
}
/* Add the module information record for the module with id
MODULE_ID. IS_PRIMARY is true if the module is the primary module.
INDEX is the index of the new record in the module info array. */
void
add_module_info (unsigned module_id, bool is_primary, int index)
{
struct gcov_module_info *cur_info;
module_infos = XRESIZEVEC (struct gcov_module_info *,
module_infos, index + 1);
module_infos[index] = XNEW (struct gcov_module_info);
cur_info = module_infos[index];
cur_info->ident = module_id;
SET_MODULE_EXPORTED (cur_info);
cur_info->num_quote_paths = 0;
cur_info->num_bracket_paths = 0;
cur_info->da_filename = NULL;
cur_info->source_filename = NULL;
if (is_primary)
primary_module_id = module_id;
}
/* Process the include paths needed for parsing the aux modules.
The sub_pattern is in the form SUB_PATH:NEW_SUB_PATH. If it is
defined, the SUB_PATH in ORIG_INC_PATH will be replaced with
NEW_SUB_PATH. */
static void
process_include (char **orig_inc_path, char* old_sub, char *new_sub)
{
char *inc_path, *orig_sub;
if (strlen (*orig_inc_path) < strlen (old_sub))
return;
inc_path = (char*) xmalloc (strlen (*orig_inc_path) + strlen (new_sub)
- strlen (old_sub) + 1);
orig_sub = strstr (*orig_inc_path, old_sub);
if (!orig_sub)
{
inform (UNKNOWN_LOCATION, "subpath %s not found in path %s",
old_sub, *orig_inc_path);
free (inc_path);
return;
}
strncpy (inc_path, *orig_inc_path, orig_sub - *orig_inc_path);
inc_path[orig_sub - *orig_inc_path] = '\0';
strcat (inc_path, new_sub);
strcat (inc_path, orig_sub + strlen (old_sub));
free (*orig_inc_path);
*orig_inc_path = inc_path;
}
/* Process include paths for MOD_INFO according to option
-fripa-inc-path-sub=OLD_SUB:NEW_SUB */
static void
process_include_paths_1 (struct gcov_module_info *mod_info,
char* old_sub, char *new_sub)
{
unsigned i, j;
for (i = 0; i < mod_info->num_quote_paths; i++)
process_include (&mod_info->string_array[i], old_sub, new_sub);
for (i = 0, j = mod_info->num_quote_paths;
i < mod_info->num_bracket_paths; i++, j++)
process_include (&mod_info->string_array[j], old_sub, new_sub);
for (i = 0, j = mod_info->num_quote_paths + mod_info->num_bracket_paths +
mod_info->num_cpp_defines; i < mod_info->num_cpp_includes; i++, j++)
process_include (&mod_info->string_array[j], old_sub, new_sub);
}
/* Process include paths for MOD_INFO according to option
-fripa-inc-path-sub=old_sub1:new_sub1[,old_sub2:new_sub2] */
static void
process_include_paths (struct gcov_module_info *mod_info)
{
char *sub_pattern, *cur, *next, *new_sub;
if (!lipo_inc_path_pattern)
return;
sub_pattern = xstrdup (lipo_inc_path_pattern);
cur = sub_pattern;
do
{
next = strchr (cur, ',');
if (next)
*next++ = '\0';
new_sub = strchr (cur, ':');
if (!new_sub)
{
error ("Invalid path substibution pattern %s", sub_pattern);
free (sub_pattern);
return;
}
*new_sub++ = '\0';
process_include_paths_1 (mod_info, cur, new_sub);
cur = next;
} while (cur);
free (sub_pattern);
}
/* Set the prepreprocessing context (include search paths, -D/-U).
PARSE_IN is the preprocessor reader, I is the index of the module,
and VERBOSE is the verbose flag. */
void
set_lipo_c_parsing_context (struct cpp_reader *parse_in, int i, bool verbose)
{
struct gcov_module_info *mod_info;
if (!L_IPO_COMP_MODE)
return;
mod_info = module_infos[i];
gcc_assert (flag_dyn_ipa);
current_module_id = mod_info->ident;
reset_funcdef_no ();
if (current_module_id != primary_module_id)
{
unsigned i, j;
process_include_paths (mod_info);
/* Setup include paths. */
clear_include_chains ();
for (i = 0; i < mod_info->num_quote_paths; i++)
add_path (xstrdup (mod_info->string_array[i]),
QUOTE, 0, 1);
for (i = 0, j = mod_info->num_quote_paths;
i < mod_info->num_bracket_paths; i++, j++)
add_path (xstrdup (mod_info->string_array[j]),
BRACKET, 0, 1);
for (i = 0; i < mod_info->num_system_paths; i++, j++)
add_path (xstrdup (mod_info->string_array[j]),
SYSTEM, 0, 1);
register_include_chains (parse_in, NULL, NULL, NULL,
0, 0, verbose);
/* Setup defines/undefs. */
for (i = 0; i < mod_info->num_cpp_defines; i++, j++)
if (mod_info->string_array[j][0] == 'D')
cpp_define (parse_in, mod_info->string_array[j] + 1);
else
cpp_undef (parse_in, mod_info->string_array[j] + 1);
/* Setup -imacro/-include. */
for (i = 0; i < mod_info->num_cpp_includes; i++, j++)
cpp_push_include (parse_in, mod_info->string_array[j]);
}
}
/* Perform file-level initialization. Read in data file, generate name
of graph file. */
void
coverage_init (const char *filename, const char* source_name)
{
char* src_name_prefix = 0;
int src_name_prefix_len = 0;
int len = strlen (filename);
/* Since coverage_init is invoked very early, before the pass
manager, we need to set up the dumping explicitly. This is
similar to the handling in finish_optimization_passes. */
dump_start (pass_profile.pass.static_pass_number, NULL);
has_asm_statement = false;
da_file_name = get_da_file_name (filename);
da_base_file_name = XNEWVEC (char, strlen (filename) + 1);
strcpy (da_base_file_name, filename);
if (profile_data_prefix == 0 && !IS_ABSOLUTE_PATH (source_name))
{
src_name_prefix = getpwd ();
src_name_prefix_len = strlen (src_name_prefix) + 1;
}
main_input_file_name = XNEWVEC (char, strlen (source_name) + 1
+ src_name_prefix_len);
if (!src_name_prefix)
strcpy (main_input_file_name, source_name);
else
{
strcpy (main_input_file_name, src_name_prefix);
strcat (main_input_file_name, "/");
strcat (main_input_file_name, source_name);
}
bbg_file_stamp = local_tick;
if (flag_branch_probabilities && !flag_auto_profile)
read_counts_file (da_file_name, 0);
/* Rebuild counts_hash and read the auxiliary GCDA files. */
if (flag_profile_use && L_IPO_COMP_MODE)
{
unsigned i;
gcc_assert (flag_dyn_ipa);
rebuild_counts_hash ();
for (i = 1; i < num_in_fnames; i++)
read_counts_file (get_da_file_name (module_infos[i]->da_filename),
module_infos[i]->ident);
}
/* Define variables which are referenced at runtime by libgcov. */
if (profiling_enabled_p ())
{
tree_init_instrumentation ();
tree_init_dyn_ipa_parameters ();
tree_init_instrumentation_sampling ();
}
if (flag_auto_profile)
init_auto_profile ();
/* Name of bbg file. */
if (flag_test_coverage && !flag_compare_debug)
{
bbg_file_name = XNEWVEC (char, len + strlen (GCOV_NOTE_SUFFIX) + 1);
memcpy (bbg_file_name, filename, len);
strcpy (bbg_file_name + len, GCOV_NOTE_SUFFIX);
if (!gcov_open (bbg_file_name, -1))
{
error ("cannot open %s", bbg_file_name);
bbg_file_name = NULL;
}
else
{
gcov_write_unsigned (GCOV_NOTE_MAGIC);
gcov_write_unsigned (GCOV_VERSION);
gcov_write_unsigned (bbg_file_stamp);
}
}
dump_finish (pass_profile.pass.static_pass_number);
}
/* Return True if any type of profiling is enabled which requires linking
in libgcov otherwise return False. */
static bool
profiling_enabled_p (void)
{
return profile_arc_flag
|| flag_profile_generate_sampling;
}
/* Performs file-level cleanup. Close graph file, generate coverage
variables and constructor. */
void
coverage_finish (void)
{
if (bbg_file_name && gcov_close ())
unlink (bbg_file_name);
if (!flag_branch_probabilities && flag_test_coverage
&& (!local_tick || local_tick == (unsigned)-1))
/* Only remove the da file, if we're emitting coverage code and
cannot uniquely stamp it. If we can stamp it, libgcov will DTRT. */
unlink (da_file_name);
if (coverage_obj_init ())
{
vec<constructor_elt, va_gc> *fn_ctor = NULL;
struct coverage_data *fn;
for (fn = functions_head; fn; fn = fn->next)
fn_ctor = coverage_obj_fn (fn_ctor, fn->fn_decl, fn);
coverage_obj_finish (fn_ctor);
}
}
/* Add S to the end of the string-list, the head and tail of which are
pointed-to by HEAD and TAIL, respectively. */
static void
str_list_append (struct str_list **head, struct str_list **tail, const char *s)
{
struct str_list *e = XNEW (struct str_list);
e->str = XNEWVEC (char, strlen (s) + 1);
strcpy (e->str, s);
e->next = NULL;
if (*tail)
(*tail)->next = e;
else
*head = e;
*tail = e;
}
/* Copies the macro def or undef CPP_DEF and saves the copy
in a list. IS_DEF is a flag indicating if CPP_DEF represents
a -D or -U. */
void
coverage_note_define (const char *cpp_def, bool is_def)
{
char *s = XNEWVEC (char, strlen (cpp_def) + 2);
s[0] = is_def ? 'D' : 'U';
strcpy (s + 1, cpp_def);
str_list_append (&cpp_defines_head, &cpp_defines_tail, s);
num_cpp_defines++;
}
/* Copies the -imacro/-include FILENAME and saves the copy in a list. */
void
coverage_note_include (const char *filename)
{
str_list_append (&cpp_includes_head, &cpp_includes_tail, filename);
num_cpp_includes++;
}
/* Mark this module as containing asm statements. */
void
coverage_has_asm_stmt (void)
{
has_asm_statement = flag_ripa_disallow_asm_modules;
}
/* Write compilation info to the .note section. */
void
write_compilation_info_to_asm (void)
{
unsigned lang;
/* Write lang, ggc_memory to ASM section. */
switch_to_section (get_section (".gnu.switches.text.lipo_info",
SECTION_DEBUG, NULL));
if (!strcmp (lang_hooks.name, "GNU C"))
lang = GCOV_MODULE_C_LANG;
else if (!strcmp (lang_hooks.name, "GNU C++"))
lang = GCOV_MODULE_CPP_LANG;
else
lang = GCOV_MODULE_UNKNOWN_LANG;
if (has_asm_statement)
lang |= GCOV_MODULE_ASM_STMTS;
dw2_asm_output_nstring (in_fnames[0], (size_t)-1, NULL);
dw2_asm_output_data_uleb128 (lang, NULL);
dw2_asm_output_data_uleb128 (ggc_total_memory, NULL);
}
/* Write command line options to the .note section. */
void
write_compilation_flags_to_asm (void)
{
size_t i;
cpp_dir *quote_paths, *bracket_paths, *system_paths, *pdir;
struct str_list *pdef, *pinc;
int num_quote_paths = 0;
int num_bracket_paths = 0;
int num_system_paths = 0;
get_include_chains (&quote_paths, &bracket_paths, &system_paths);
/* Write quote_paths to ASM section. */
switch_to_section (get_section (".gnu.switches.text.quote_paths",
SECTION_DEBUG, NULL));
for (pdir = quote_paths; pdir; pdir = pdir->next)
{
if (pdir == bracket_paths)
break;
num_quote_paths++;
}
dw2_asm_output_nstring (in_fnames[0], (size_t)-1, NULL);
dw2_asm_output_data_uleb128 (num_quote_paths, NULL);
for (pdir = quote_paths; pdir; pdir = pdir->next)
{
if (pdir == bracket_paths)
break;
dw2_asm_output_nstring (pdir->name, (size_t)-1, NULL);
}
/* Write bracket_paths to ASM section. */
switch_to_section (get_section (".gnu.switches.text.bracket_paths",
SECTION_DEBUG, NULL));
for (pdir = bracket_paths; pdir; pdir = pdir->next)
{
if (pdir == system_paths)
break;
num_bracket_paths++;
}
dw2_asm_output_nstring (in_fnames[0], (size_t)-1, NULL);
dw2_asm_output_data_uleb128 (num_bracket_paths, NULL);
for (pdir = bracket_paths; pdir; pdir = pdir->next)
{
if (pdir == system_paths)
break;
dw2_asm_output_nstring (pdir->name, (size_t)-1, NULL);
}
/* Write system_paths to ASM section. */
switch_to_section (get_section (".gnu.switches.text.system_paths",
SECTION_DEBUG, NULL));
for (pdir = system_paths; pdir; pdir = pdir->next)
num_system_paths++;
dw2_asm_output_nstring (in_fnames[0], (size_t)-1, NULL);
dw2_asm_output_data_uleb128 (num_system_paths, NULL);
for (pdir = system_paths; pdir; pdir = pdir->next)
dw2_asm_output_nstring (pdir->name, (size_t)-1, NULL);
/* Write cpp_defines to ASM section. */
switch_to_section (get_section (".gnu.switches.text.cpp_defines",
SECTION_DEBUG, NULL));
dw2_asm_output_nstring (in_fnames[0], (size_t)-1, NULL);
dw2_asm_output_data_uleb128 (num_cpp_defines, NULL);
for (pdef = cpp_defines_head; pdef; pdef = pdef->next)
dw2_asm_output_nstring (pdef->str, (size_t)-1, NULL);
/* Write cpp_includes to ASM section. */
switch_to_section (get_section (".gnu.switches.text.cpp_includes",
SECTION_DEBUG, NULL));
dw2_asm_output_nstring (in_fnames[0], (size_t)-1, NULL);
dw2_asm_output_data_uleb128 (num_cpp_includes, NULL);
for (pinc = cpp_includes_head; pinc; pinc = pinc->next)
dw2_asm_output_nstring (pinc->str, (size_t)-1, NULL);
/* Write cl_args to ASM section. */
switch_to_section (get_section (".gnu.switches.text.cl_args",
SECTION_DEBUG, NULL));
dw2_asm_output_nstring (in_fnames[0], (size_t)-1, NULL);
dw2_asm_output_data_uleb128 (num_lipo_cl_args, NULL);
for (i = 0; i < num_lipo_cl_args; i++)
dw2_asm_output_nstring (lipo_cl_args[i], (size_t)-1, NULL);
}
#include "gt-coverage.h"