===================================================================
@@ -0,0 +1,800 @@
+/* GCC instrumentation plugin for ThreadSanitizer.
+ Copyright (C) 2011-2013 Free Software Foundation, Inc.
+ Contributed by Dmitry Vyukov <dvyukov@google.com>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 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/>. */
+
+/* work around the poisoned malloc/calloc in system.h. */
+#ifndef xmalloc
+#define xmalloc malloc
+#define xcalloc calloc
+#endif
+
+#define PROFILE_TOOL 1
+#define L_gcov 1
+#define L_gcov_merge_add 1
+#define L_gcov_merge_single 1
+#define L_gcov_merge_delta 1
+#define L_gcov_merge_ior 1
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "intl.h"
+#include "diagnostic.h"
+#include "version.h"
+#include "demangle.h"
+
+/* We need the dumping and merge part of code in libgcov. */
+#include "libgcov-driver.c"
+#include "libgcov-merge.c"
+
+/* Verbose mode for debug. */
+static int verbose;
+
+/* Set verbose flag. */
+void gcov_tool_set_verbose (void)
+{
+ verbose = 1;
+}
+
+/* -------- Read Gcda and Reconstruct GCOV_INFO ----------- */
+
+#include "obstack.h"
+#include <unistd.h>
+#include <ftw.h>
+
+static void tag_function (const char *, unsigned, unsigned);
+static void tag_blocks (const char *, unsigned, unsigned);
+static void tag_arcs (const char *, unsigned, unsigned);
+static void tag_lines (const char *, unsigned, unsigned);
+static void tag_counters (const char *, unsigned, unsigned);
+static void tag_summary (const char *, unsigned, unsigned);
+
+/* The gcov_info for the first module. */
+static struct gcov_info *curr_gcov_info;
+/* The gcov_info being processed. */
+static struct gcov_info *gcov_info_head;
+/* This variable contains all the functions in current module. */
+static struct obstack fn_info;
+/* The function being processed. */
+static struct gcov_fn_info *curr_fn_info;
+/* The number of functions seen so far. */
+static unsigned num_fn_info;
+/* This variable contains all the counters for current module. */
+static int k_ctrs_mask[GCOV_COUNTERS];
+/* The kind of counters that have been seen. */
+static struct gcov_ctr_info k_ctrs[GCOV_COUNTERS];
+/* Number of kind of counters that have been seen. */
+static int k_ctrs_types;
+/* The longest length of all the filenames. */
+static int max_filename_len;
+
+/* Merge functions for counters. */
+static gcov_merge_fn ctr_merge_functions[GCOV_COUNTERS] = {
+ __gcov_merge_add,
+ __gcov_merge_add,
+ __gcov_merge_add,
+ __gcov_merge_single,
+ __gcov_merge_delta,
+ __gcov_merge_single,
+ __gcov_merge_add,
+ __gcov_merge_ior,
+};
+
+/* Set the fn_ctrs structure in fn_info. */
+
+static void
+set_fn_ctrs (struct gcov_fn_info *fn_info)
+{
+ int j = 0, i;
+
+ for (i = 0; i < GCOV_COUNTERS; i++)
+ {
+ if (k_ctrs_mask[i] == 0)
+ continue;
+ fn_info->ctrs[j].num = k_ctrs[i].num;
+ fn_info->ctrs[j].values = k_ctrs[i].values;
+ j++;
+ }
+ if (k_ctrs_types == 0)
+ k_ctrs_types = j;
+ else
+ gcc_assert (j == k_ctrs_types);
+}
+
+typedef struct tag_format
+{
+ unsigned tag;
+ char const *name;
+ void (*proc) (const char *, unsigned, unsigned);
+} tag_format_t;
+
+static const tag_format_t tag_table[] =
+{
+ {0, "NOP", NULL},
+ {0, "UNKNOWN", NULL},
+ {0, "COUNTERS", tag_counters},
+ {GCOV_TAG_FUNCTION, "FUNCTION", tag_function},
+ {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks},
+ {GCOV_TAG_ARCS, "ARCS", tag_arcs},
+ {GCOV_TAG_LINES, "LINES", tag_lines},
+ {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
+ {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
+ {0, NULL, NULL}
+};
+
+/* Handler for reading funtion tag. */
+
+static void
+tag_function (const char *filename ATTRIBUTE_UNUSED,
+ unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+ int i;
+
+ /* write out previous fn_info. */
+ if (num_fn_info)
+ {
+ set_fn_ctrs (curr_fn_info);
+ /* obstack_grow (&fn_info, &curr_fn_info, sizeof (struct gcov_fn_info*)); */
+ obstack_ptr_grow (&fn_info, curr_fn_info);
+ }
+ /* first time. */
+ {
+ /* Here we over allocate a bit, using GCOV_COUNTERS instead of the actual active
+ counter types. */
+ curr_fn_info = (struct gcov_fn_info *) xcalloc (sizeof (struct gcov_fn_info)
+ + GCOV_COUNTERS * sizeof (struct gcov_ctr_info), 1);
+ }
+
+ for (i = 0; i < GCOV_COUNTERS; i++)
+ k_ctrs[i].num = 0;
+ k_ctrs_types = 0;
+
+ curr_fn_info->key = curr_gcov_info;
+ curr_fn_info->ident = gcov_read_unsigned ();
+ curr_fn_info->lineno_checksum = gcov_read_unsigned ();
+ curr_fn_info->cfg_checksum = gcov_read_unsigned ();
+ num_fn_info++;
+
+ if (verbose)
+ fprintf (stderr, "tag one function id=%d \n", curr_fn_info->ident);
+}
+
+/* Handler for reading block tag. */
+
+static void
+tag_blocks (const char *filename ATTRIBUTE_UNUSED,
+ unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+ gcc_assert (0);
+}
+
+/* Handler for reading flow arc tag. */
+
+static void
+tag_arcs (const char *filename ATTRIBUTE_UNUSED,
+ unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+ gcc_assert (0);
+}
+
+/* Handler for reading line tag. */
+
+static void
+tag_lines (const char *filename ATTRIBUTE_UNUSED,
+ unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+ gcc_assert (0);
+}
+
+/* Handler for reading counters array tag. */
+
+static void
+tag_counters (const char *filename ATTRIBUTE_UNUSED,
+ unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+ unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
+ gcov_type *values;
+ unsigned ix;
+ unsigned tag_ix;
+
+ tag_ix = GCOV_COUNTER_FOR_TAG (tag);
+ gcc_assert (tag_ix < GCOV_COUNTERS);
+ k_ctrs_mask [tag_ix] = 1;
+ gcc_assert (k_ctrs[tag_ix].num == 0);
+ k_ctrs[tag_ix].num = n_counts;
+
+ /* fprintf(stderr, "read %d\n", n_counts); */
+ k_ctrs[tag_ix].values = values = (gcov_type *)xmalloc (n_counts * sizeof (gcov_type));
+ gcc_assert (values);
+
+ for (ix = 0; ix != n_counts; ix++)
+ values[ix] = gcov_read_counter ();
+}
+
+/* Handler for reading summary tag. */
+
+static void
+tag_summary (const char *filename ATTRIBUTE_UNUSED,
+ unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+{
+ struct gcov_summary summary;
+
+ gcov_read_summary (&summary);
+ if (!saved_summary_checksum)
+ saved_summary_checksum = summary.checksum;
+}
+
+/* This function is called at the end of reading a gcda file.
+ It flushes the contents in curr_fn_info to the gcov_info. */
+
+static void
+read_gcda_finalize (struct gcov_info *obj_info)
+{
+ int i;
+
+ set_fn_ctrs (curr_fn_info);
+ obstack_ptr_grow (&fn_info, curr_fn_info);
+
+ /* We set the following fields: merge, n_functions, and functions. */
+ obj_info->n_functions = num_fn_info;
+ obj_info->functions = (const struct gcov_fn_info**) obstack_finish (&fn_info);
+
+ /* wrap all the counter array. */
+ for (i=0; i< GCOV_COUNTERS; i++)
+ {
+ if (k_ctrs_mask[i])
+ obj_info->merge[i] = ctr_merge_functions[i];
+ }
+}
+
+/* Read the content of a gcda file, and return a gcov_info data structure.
+ Program level summary CURRENT_SUMMARY will also be updated. */
+
+static struct gcov_info *
+read_gcda_file (const char *filename)
+{
+ unsigned tags[4];
+ unsigned depth = 0;
+ unsigned magic, version;
+ struct gcov_info *obj_info;
+ int i;
+
+ for (i=0; i< GCOV_COUNTERS; i++)
+ k_ctrs_mask[i] = 0;
+ k_ctrs_types = 0;
+
+ if (!gcov_open (filename))
+ {
+ fprintf (stderr, "%s:cannot open\n", filename);
+ return NULL;
+ }
+
+ /* read magic. */
+ magic = gcov_read_unsigned ();
+ if (magic != GCOV_DATA_MAGIC)
+ {
+ fprintf (stderr, "%s:Not a gcov data file.\n", filename);
+ gcov_close ();
+ return NULL;
+ }
+
+ /* read version. */
+ version = gcov_read_unsigned ();
+ if (version != GCOV_VERSION)
+ {
+ fprintf (stderr, "%s:Incorrect gcov version. %d vs %d \n", filename, version, GCOV_VERSION);
+ gcov_close ();
+ return NULL;
+ }
+
+ /* Instantiate a gcov_info object. */
+ curr_gcov_info = obj_info = (struct gcov_info *) xcalloc (sizeof (struct gcov_info) +
+ sizeof (struct gcov_ctr_info) * GCOV_COUNTERS, 1);
+
+ obj_info->version = version;
+ obstack_init (&fn_info);
+ num_fn_info = 0;
+ curr_fn_info = 0;
+ {
+ char *str_dup = (char*) xmalloc (strlen (filename) + 1);
+ int len;
+
+ strcpy (str_dup, filename);
+ obj_info->filename = str_dup;
+ if ((len = strlen (filename)) > max_filename_len)
+ max_filename_len = len;
+ }
+
+ /* read stamp. */
+ obj_info->stamp = gcov_read_unsigned ();
+
+ while (1)
+ {
+ gcov_position_t base;
+ unsigned tag, length;
+ tag_format_t const *format;
+ unsigned tag_depth;
+ int error;
+ unsigned mask;
+
+ tag = gcov_read_unsigned ();
+ if (!tag)
+ break;
+ length = gcov_read_unsigned ();
+ base = gcov_position ();
+ mask = GCOV_TAG_MASK (tag) >> 1;
+ for (tag_depth = 4; mask; mask >>= 8)
+ {
+ if (((mask & 0xff) != 0xff) /*&& ((mask & 0xff) != 0x3)*/)
+ {
+ /* fprintf (stderr, "Warning: %s:tag `%08x' is invalid\n", filename, tag); */
+ break;
+ }
+ tag_depth--;
+ }
+ for (format = tag_table; format->name; format++)
+ if (format->tag == tag)
+ goto found;
+ format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1];
+ found:;
+ if (tag)
+ {
+ if (depth && depth < tag_depth)
+ {
+ if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag))
+ fprintf (stderr, "Warning: %s:tag `%08x' is incorrectly nested\n",
+ filename, tag);
+ }
+ depth = tag_depth;
+ tags[depth - 1] = tag;
+ }
+
+ if (format->proc)
+ (*format->proc) (filename, tag, length);
+
+ if (format->proc)
+ {
+ unsigned long actual_length = gcov_position () - base;
+
+ if (actual_length > length)
+ fprintf (stderr,"Warning: %s:record size mismatch %lu bytes overread\n",
+ filename, actual_length - length);
+ else if (length > actual_length)
+ fprintf (stderr,"Warning: %s:record size mismatch %lu bytes unread\n",
+ filename, length - actual_length);
+ }
+ gcov_sync (base, length);
+ if ((error = gcov_is_error ()))
+ {
+ fprintf (stderr,error < 0 ? "Warning:%s:counter overflow at %lu\n" :
+ "Warning:%s:read error at %lu\n", filename,
+ (long unsigned) gcov_position ());
+ break;
+ }
+ }
+
+ read_gcda_finalize (obj_info);
+ gcov_close ();
+
+ return obj_info;
+}
+
+#define GCOV_SUFFIX ".gcda"
+
+/* This will be called by ftw. Return a non-zero value
+ to stop the tree walk. */
+
+static int
+ftw_read_file ( const char *filename,
+ const struct stat *status ATTRIBUTE_UNUSED,
+ int type)
+{
+ int filename_len;
+ int suffix_len;
+ struct gcov_info *obj_info;
+
+ /* Only read regular files. */
+ if (type != FTW_F)
+ return 0;
+
+ filename_len = strlen (filename);
+ suffix_len = strlen (GCOV_SUFFIX);
+
+ if (filename_len <= suffix_len)
+ return 0;
+
+ if (strcmp(filename + filename_len - suffix_len, GCOV_SUFFIX))
+ return 0;
+
+ if (verbose)
+ printf ("Read file: %s \n", filename);
+
+ obj_info = read_gcda_file (filename);
+
+ obj_info->next = gcov_info_head;
+ gcov_info_head = obj_info;
+
+ return 0;
+}
+
+static inline void
+read_profile_dir_init (void)
+{
+ gcov_info_head = 0;
+}
+
+/* Driver for read a profile directory and convert into gcvo_info list in memory.
+ Return NULL on error,
+ Return the head of gcov_info list on success.
+ Note the file static variable GCOV_MAX_FILENAME is also set. */
+
+struct gcov_info *
+gcov_read_profile_dir (const char* dir_name, int recompute_summary ATTRIBUTE_UNUSED)
+{
+ char *pwd;
+ int ret;
+
+ read_profile_dir_init ();
+
+ if (access (dir_name, R_OK) != 0)
+ {
+ fprintf (stderr, "Cannot access directory %s.\n", dir_name);
+ return NULL;
+ }
+ pwd = get_current_dir_name ();
+ gcc_assert (pwd);
+ ret = chdir (dir_name);
+ if (ret !=0)
+ {
+ fprintf (stderr, "%s is not a directory.\n", dir_name);
+ return NULL;
+ }
+ ftw (".", ftw_read_file, 50);
+ ret = chdir (pwd);
+ free (pwd);
+
+
+ /* gcov_max_filename is defined in libgcov.c that recored
+ max filename len. We need to set it here to allocate the
+ array for dumping. */
+ gcov_max_filename = max_filename_len;
+
+ return gcov_info_head;;
+}
+
+/* -------------- Merge Profile Counters ------------------ */
+
+/* Offline tool to manipulate profile data.
+ This tool targets on matched profiles. But it has some tolerance on
+ unmatched profiles.
+ When merging p1 to p2 (p2 is the dst),
+ * m.gcda in p1 but not in p2: append m.gcda to p2 with specified weight; emit warning
+ * m.gcda in p2 but not in p1: keep m.gcda in p2 and multiplying specified weight; emit warning.
+ * m.gcda in both p1 and p2:
+ ** p1->m.gcda->f checksum matches p2->m.gcda->f: simple merge.
+ ** p1->m.gcda->f checksum does not matches p2->m.gcda->f: keep p2->m.gcda->f and
+ drop p1->m.gcda->f. A warning is emitted. */
+
+/* Add INFO2's counter to INFO1, multiplying weight W. */
+
+static int
+gcov_merge (struct gcov_info *info1, struct gcov_info *info2, int w)
+{
+ unsigned f_ix;
+ unsigned n_functions = info1->n_functions;
+ int has_mismatch = 0;
+
+ gcc_assert (info2->n_functions == n_functions);
+ for (f_ix = 0; f_ix < n_functions; f_ix++)
+ {
+ unsigned t_ix;
+ const struct gcov_fn_info *gfi_ptr1 = info1->functions[f_ix];
+ const struct gcov_fn_info *gfi_ptr2 = info2->functions[f_ix];
+ const struct gcov_ctr_info *ci_ptr1, *ci_ptr2;
+
+ if (!gfi_ptr1 || gfi_ptr1->key != info1)
+ continue;
+ if (!gfi_ptr2 || gfi_ptr2->key != info2)
+ continue;
+
+ if (gfi_ptr1->cfg_checksum != gfi_ptr2->cfg_checksum)
+ {
+ fprintf (stderr, "In %s, cfg_checksum mismatch, skipping ...\n",
+ info1->filename);
+ has_mismatch = 1;
+ continue;
+ }
+ ci_ptr1 = gfi_ptr1->ctrs;
+ ci_ptr2 = gfi_ptr2->ctrs;
+ for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
+ {
+ gcov_merge_fn merge1 = info1->merge[t_ix];
+ gcov_merge_fn merge2 = info2->merge[t_ix];
+
+ gcc_assert (merge1 == merge2);
+ if (!merge1)
+ continue;
+ gcc_assert (ci_ptr1->num == ci_ptr2->num);
+ (*merge1) (ci_ptr1->values, ci_ptr1->num, ci_ptr2->values, w);
+ ci_ptr1++;
+ ci_ptr2++;
+ }
+ }
+
+ return has_mismatch;
+}
+
+static struct gcov_info *
+find_match_gcov_info (struct gcov_info **array, int size, struct gcov_info *info)
+{
+ struct gcov_info *gi_ptr;
+ struct gcov_info *ret = NULL;
+ int i;
+
+ for (i = 0; i < size; i++)
+ {
+ gi_ptr = array[i];
+ if (gi_ptr == 0)
+ continue;
+ if (!strcmp (gi_ptr->filename, info->filename))
+ {
+ ret = gi_ptr;
+ array[i] = 0;
+ break;
+ }
+ }
+
+ if (ret && ret->n_functions != info->n_functions)
+ {
+ fprintf (stderr, "Mismatched profiles in %s (%d functions"
+ " vs %d functions).\n",
+ ret->filename,
+ ret->n_functions,
+ info->n_functions);
+ ret = NULL;
+ }
+ return ret;
+}
+
+/* return 0 on success: without mismatch.
+ reutrn 1 on error. */
+
+int
+gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile,
+ int w1, int w2)
+{
+ struct gcov_info *gi_ptr;
+ struct gcov_info **tgt_infos;
+ struct gcov_info *tgt_tail;
+ struct gcov_info **in_src_not_tgt;
+ unsigned tgt_cnt = 0, src_cnt = 0;
+ unsigned unmatch_info_cnt = 0;
+ unsigned int i;
+
+ for (gi_ptr = tgt_profile; gi_ptr; gi_ptr = gi_ptr->next)
+ tgt_cnt++;
+ for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next)
+ src_cnt++;
+ tgt_infos = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) * tgt_cnt);
+ gcc_assert (tgt_infos);
+ in_src_not_tgt = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) * src_cnt);
+ gcc_assert (in_src_not_tgt);
+
+ for (gi_ptr = tgt_profile, i = 0; gi_ptr; gi_ptr = gi_ptr->next, i++)
+ tgt_infos[i] = gi_ptr;
+
+ tgt_tail = tgt_infos[tgt_cnt - 1];
+
+ /* First pass on tgt_profile, we multiply w1 to all counters. */
+ if (w1 > 1)
+ {
+ for (i = 0; i < tgt_cnt; i++)
+ gcov_merge (tgt_infos[i], tgt_infos[i], w1-1);
+ }
+
+ /* Second pass, add src_profile to the tgt_profile. */
+ for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next)
+ {
+ struct gcov_info *gi_ptr1;
+
+ gi_ptr1 = find_match_gcov_info (tgt_infos, tgt_cnt, gi_ptr);
+ if (gi_ptr1 == NULL)
+ {
+ in_src_not_tgt[unmatch_info_cnt++] = gi_ptr;
+ continue;
+ }
+ gcov_merge (gi_ptr1, gi_ptr, w2);
+ }
+
+#if 0
+ /* For modules left in the array. They are not in src_prfile. */
+ for (i = 0; i < tgt_cnt; i++)
+ {
+ gi_ptr = tgt_infos[i];
+ if (!gi_ptr)
+ continue;
+ gcov_merge (gi_ptr, gi_ptr, w2);
+ }
+
+ /* For modules in src but not in tgt. We adjust the counter and append. */
+ for (i = 0; i < unmatch_info_cnt; i++)
+ {
+ gi_ptr = in_src_not_tgt[i];
+ gcov_merge (gi_ptr, gi_ptr, w1+w2-1);
+ }
+#else
+ /* For modules in src but not in tgt. We adjust the counter and append. */
+ for (i = 0; i < unmatch_info_cnt; i++)
+ {
+ gi_ptr = in_src_not_tgt[i];
+ gcov_merge (gi_ptr, gi_ptr, w2 - 1);
+ tgt_tail->next = gi_ptr;
+ tgt_tail = gi_ptr;
+ }
+#endif
+
+ return 0;
+}
+
+/* -------------- Scale Profile Counters ------------------ */
+
+/* Type of function used to normalize counters. */
+typedef void (*gcov_scale_fn) (gcov_type *, gcov_unsigned_t, double);
+
+static void
+__gcov_scale_add (gcov_type *counters, unsigned n_counters, double f)
+{
+ for (; n_counters; counters++, n_counters--)
+ {
+ gcov_type val = *counters;
+ *counters = val * f;
+ }
+}
+
+/* Scale ior counters. */
+
+static void
+__gcov_scale_ior (gcov_type *counters ATTRIBUTE_UNUSED,
+ unsigned n_counters ATTRIBUTE_UNUSED,
+ double f ATTRIBUTE_UNUSED)
+{
+ /* do nothing. */
+}
+
+/* Scale delta counters. */
+
+static void
+__gcov_scale_delta (gcov_type *counters, unsigned n_counters, double f)
+{
+ unsigned i, n_measures;
+
+ gcc_assert (!(n_counters % 4));
+ n_measures = n_counters / 4;
+ for (i = 0; i < n_measures; i++, counters += 4)
+ {
+ counters[2] *= f;
+ counters[3] *= f;
+ }
+}
+
+/* Scale single counters. */
+
+static void
+__gcov_scale_single (gcov_type *counters, unsigned n_counters, double f)
+{
+ unsigned i, n_measures;
+
+ gcc_assert (!(n_counters % 3));
+ n_measures = n_counters / 3;
+ for (i = 0; i < n_measures; i++, counters += 3)
+ {
+ counters[1] *= f;
+ counters[2] *= f;
+ }
+}
+
+/* Scaling functions for counters. */
+static gcov_scale_fn ctr_scale_functions[GCOV_COUNTERS] = {
+ __gcov_scale_add,
+ __gcov_scale_add,
+ __gcov_scale_add,
+ __gcov_scale_single,
+ __gcov_scale_delta,
+ __gcov_scale_single,
+ __gcov_scale_add,
+ __gcov_scale_ior,
+};
+
+
+/* Driver for scale profile counters. */
+
+int
+gcov_profile_scale (struct gcov_info *profile, float scale_factor)
+{
+ struct gcov_info *gi_ptr;
+ unsigned f_ix;
+
+ if (verbose)
+ fprintf (stderr, "scale_factor is %f\n", scale_factor);
+
+ /* scale the counters. */
+ for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next)
+ for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+ {
+ unsigned t_ix;
+ const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
+ const struct gcov_ctr_info *ci_ptr;
+
+ if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+ continue;
+
+ ci_ptr = gfi_ptr->ctrs;
+ for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
+ {
+ gcov_merge_fn merge = gi_ptr->merge[t_ix];
+
+ if (!merge)
+ continue;
+ (*ctr_scale_functions[t_ix]) (ci_ptr->values, ci_ptr->num, scale_factor);
+ ci_ptr++;
+ }
+ }
+
+ return 0;
+}
+
+/* Driver for normalize profile counters. */
+
+int
+gcov_profile_normalize (struct gcov_info *profile, gcov_type max_val)
+{
+ struct gcov_info *gi_ptr;
+ gcov_type curr_max_val = 0;
+ unsigned f_ix;
+ unsigned int i;
+ float scale_factor;
+
+ /* get the larest count value. */
+ for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next)
+ for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+ {
+ unsigned t_ix;
+ const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
+ const struct gcov_ctr_info *ci_ptr;
+
+ if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+ continue;
+
+ ci_ptr = gfi_ptr->ctrs;
+ for (t_ix = 0; t_ix < 1; t_ix++)
+ {
+ for (i = 0; i < ci_ptr->num; i++)
+ if (ci_ptr->values[i] > curr_max_val)
+ curr_max_val = ci_ptr->values[i];
+ ci_ptr++;
+ }
+ }
+
+ scale_factor = (float)max_val / curr_max_val;
+ if (verbose)
+ fprintf (stderr, "max_val is %lld\n", (long long) curr_max_val);
+
+ return gcov_profile_scale (profile, scale_factor);
+}
===================================================================
@@ -0,0 +1,282 @@
+/* Routines required for instrumenting a program. */
+/* Compile this one with gcc. */
+/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
+
+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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+#include "tconfig.h"
+#include "tsystem.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "libgcc_tm.h"
+#include "gthr.h"
+
+#if defined(inhibit_libc)
+#define IN_LIBGCOV (-1)
+#else
+#define IN_LIBGCOV 1
+#if defined(L_gcov_flush)
+#define GCOV_LINKAGE /* nothing */
+#endif
+#endif
+#include "gcov-io.h"
+
+#if defined(inhibit_libc)
+
+#ifdef L_gcov_flush
+void __gcov_flush (void) {}
+#endif
+
+#ifdef L_gcov_reset
+void __gcov_reset (void) {}
+#endif
+
+#ifdef L_gcov_dump
+void __gcov_dump (void) {}
+#endif
+
+#else
+
+extern void gcov_clear (void) ATTRIBUTE_HIDDEN;
+extern void gcov_exit (void) ATTRIBUTE_HIDDEN;
+extern void set_gcov_dump_complete (void) ATTRIBUTE_HIDDEN;
+extern void reset_gcov_dump_complete (void) ATTRIBUTE_HIDDEN;
+
+#ifdef L_gcov_flush
+
+#ifdef __GTHREAD_MUTEX_INIT
+ATTRIBUTE_HIDDEN __gthread_mutex_t __gcov_flush_mx = __GTHREAD_MUTEX_INIT;
+#define init_mx_once()
+#else
+__gthread_mutex_t __gcov_flush_mx ATTRIBUTE_HIDDEN;
+
+static void
+init_mx (void)
+{
+ __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
+}
+static void
+init_mx_once (void)
+{
+ static __gthread_once_t once = __GTHREAD_ONCE_INIT;
+ __gthread_once (&once, init_mx);
+}
+#endif
+
+/* Called before fork or exec - write out profile information gathered so
+ far and reset it to zero. This avoids duplication or loss of the
+ profile information gathered so far. */
+
+void
+__gcov_flush (void)
+{
+ init_mx_once ();
+ __gthread_mutex_lock (&__gcov_flush_mx);
+
+ gcov_exit ();
+ gcov_clear ();
+
+ __gthread_mutex_unlock (&__gcov_flush_mx);
+}
+
+#endif /* L_gcov_flush */
+
+#ifdef L_gcov_reset
+
+/* Function that can be called from application to reset counters to zero,
+ in order to collect profile in region of interest. */
+
+void
+__gcov_reset (void)
+{
+ gcov_clear ();
+ /* Re-enable dumping to support collecting profile in multiple regions
+ of interest. */
+ reset_gcov_dump_complete ();
+}
+
+#endif /* L_gcov_reset */
+
+#ifdef L_gcov_dump
+
+/* Function that can be called from application to write profile collected
+ so far, in order to collect profile in region of interest. */
+
+void
+__gcov_dump (void)
+{
+ gcov_exit ();
+ /* Prevent profile from being dumped a second time on application exit. */
+ set_gcov_dump_complete ();
+}
+
+#endif /* L_gcov_dump */
+
+
+#ifdef L_gcov_fork
+/* A wrapper for the fork function. Flushes the accumulated profiling data, so
+ that they are not counted twice. */
+
+pid_t
+__gcov_fork (void)
+{
+ pid_t pid;
+ extern __gthread_mutex_t __gcov_flush_mx;
+ __gcov_flush ();
+ pid = fork ();
+ if (pid == 0)
+ __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
+ return pid;
+}
+#endif
+
+#ifdef L_gcov_execl
+/* A wrapper for the execl function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execl (const char *path, char *arg, ...)
+{
+ va_list ap, aq;
+ unsigned i, length;
+ char **args;
+
+ __gcov_flush ();
+
+ va_start (ap, arg);
+ va_copy (aq, ap);
+
+ length = 2;
+ while (va_arg (ap, char *))
+ length++;
+ va_end (ap);
+
+ args = (char **) alloca (length * sizeof (void *));
+ args[0] = arg;
+ for (i = 1; i < length; i++)
+ args[i] = va_arg (aq, char *);
+ va_end (aq);
+
+ return execv (path, args);
+}
+#endif
+
+#ifdef L_gcov_execlp
+/* A wrapper for the execlp function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execlp (const char *path, char *arg, ...)
+{
+ va_list ap, aq;
+ unsigned i, length;
+ char **args;
+
+ __gcov_flush ();
+
+ va_start (ap, arg);
+ va_copy (aq, ap);
+
+ length = 2;
+ while (va_arg (ap, char *))
+ length++;
+ va_end (ap);
+
+ args = (char **) alloca (length * sizeof (void *));
+ args[0] = arg;
+ for (i = 1; i < length; i++)
+ args[i] = va_arg (aq, char *);
+ va_end (aq);
+
+ return execvp (path, args);
+}
+#endif
+
+#ifdef L_gcov_execle
+/* A wrapper for the execle function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execle (const char *path, char *arg, ...)
+{
+ va_list ap, aq;
+ unsigned i, length;
+ char **args;
+ char **envp;
+
+ __gcov_flush ();
+
+ va_start (ap, arg);
+ va_copy (aq, ap);
+
+ length = 2;
+ while (va_arg (ap, char *))
+ length++;
+ va_end (ap);
+
+ args = (char **) alloca (length * sizeof (void *));
+ args[0] = arg;
+ for (i = 1; i < length; i++)
+ args[i] = va_arg (aq, char *);
+ envp = va_arg (aq, char **);
+ va_end (aq);
+
+ return execve (path, args, envp);
+}
+#endif
+
+#ifdef L_gcov_execv
+/* A wrapper for the execv function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execv (const char *path, char *const argv[])
+{
+ __gcov_flush ();
+ return execv (path, argv);
+}
+#endif
+
+#ifdef L_gcov_execvp
+/* A wrapper for the execvp function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execvp (const char *path, char *const argv[])
+{
+ __gcov_flush ();
+ return execvp (path, argv);
+}
+#endif
+
+#ifdef L_gcov_execve
+/* A wrapper for the execve function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execve (const char *path, char *const argv[], char *const envp[])
+{
+ __gcov_flush ();
+ return execve (path, argv, envp);
+}
+#endif
+#endif /* inhibit_libc */
===================================================================
@@ -1,1374 +0,0 @@
-/* Routines required for instrumenting a program. */
-/* Compile this one with gcc. */
-/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
-
-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.
-
-Under Section 7 of GPL version 3, you are granted additional
-permissions described in the GCC Runtime Library Exception, version
-3.1, as published by the Free Software Foundation.
-
-You should have received a copy of the GNU General Public License and
-a copy of the GCC Runtime Library Exception along with this program;
-see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
-<http://www.gnu.org/licenses/>. */
-
-#include "tconfig.h"
-#include "tsystem.h"
-#include "coretypes.h"
-#include "tm.h"
-#include "libgcc_tm.h"
-#include "gthr.h"
-
-#if defined(inhibit_libc)
-#define IN_LIBGCOV (-1)
-#else
-#define IN_LIBGCOV 1
-#if defined(L_gcov)
-#define GCOV_LINKAGE /* nothing */
-#endif
-#endif
-#include "gcov-io.h"
-
-#if defined(inhibit_libc)
-/* If libc and its header files are not available, provide dummy functions. */
-
-#ifdef L_gcov
-void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
-void __gcov_flush (void) {}
-#endif
-
-#ifdef L_gcov_reset
-void __gcov_reset (void) {}
-#endif
-
-#ifdef L_gcov_dump
-void __gcov_dump (void) {}
-#endif
-
-#ifdef L_gcov_merge_add
-void __gcov_merge_add (gcov_type *counters __attribute__ ((unused)),
- unsigned n_counters __attribute__ ((unused))) {}
-#endif
-
-#ifdef L_gcov_merge_single
-void __gcov_merge_single (gcov_type *counters __attribute__ ((unused)),
- unsigned n_counters __attribute__ ((unused))) {}
-#endif
-
-#ifdef L_gcov_merge_delta
-void __gcov_merge_delta (gcov_type *counters __attribute__ ((unused)),
- unsigned n_counters __attribute__ ((unused))) {}
-#endif
-
-#else
-
-#include <string.h>
-#if GCOV_LOCKED
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/stat.h>
-#endif
-
-extern void gcov_clear (void) ATTRIBUTE_HIDDEN;
-extern void gcov_exit (void) ATTRIBUTE_HIDDEN;
-extern int gcov_dump_complete ATTRIBUTE_HIDDEN;
-
-#ifdef L_gcov
-#include "gcov-io.c"
-
-struct gcov_fn_buffer
-{
- struct gcov_fn_buffer *next;
- unsigned fn_ix;
- struct gcov_fn_info info;
- /* note gcov_fn_info ends in a trailing array. */
-};
-
-struct gcov_summary_buffer
-{
- struct gcov_summary_buffer *next;
- struct gcov_summary summary;
-};
-
-/* Chain of per-object gcov structures. */
-static struct gcov_info *gcov_list;
-
-/* Size of the longest file name. */
-static size_t gcov_max_filename = 0;
-
-/* Flag when the profile has already been dumped via __gcov_dump(). */
-int gcov_dump_complete = 0;
-
-/* Make sure path component of the given FILENAME exists, create
- missing directories. FILENAME must be writable.
- Returns zero on success, or -1 if an error occurred. */
-
-static int
-create_file_directory (char *filename)
-{
-#if !defined(TARGET_POSIX_IO) && !defined(_WIN32)
- (void) filename;
- return -1;
-#else
- char *s;
-
- s = filename;
-
- if (HAS_DRIVE_SPEC(s))
- s += 2;
- if (IS_DIR_SEPARATOR(*s))
- ++s;
- for (; *s != '\0'; s++)
- if (IS_DIR_SEPARATOR(*s))
- {
- char sep = *s;
- *s = '\0';
-
- /* Try to make directory if it doesn't already exist. */
- if (access (filename, F_OK) == -1
-#ifdef TARGET_POSIX_IO
- && mkdir (filename, 0755) == -1
-#else
- && mkdir (filename) == -1
-#endif
- /* The directory might have been made by another process. */
- && errno != EEXIST)
- {
- fprintf (stderr, "profiling:%s:Cannot create directory\n",
- filename);
- *s = sep;
- return -1;
- };
-
- *s = sep;
- };
- return 0;
-#endif
-}
-
-static struct gcov_fn_buffer *
-free_fn_data (const struct gcov_info *gi_ptr, struct gcov_fn_buffer *buffer,
- unsigned limit)
-{
- struct gcov_fn_buffer *next;
- unsigned ix, n_ctr = 0;
-
- if (!buffer)
- return 0;
- next = buffer->next;
-
- for (ix = 0; ix != limit; ix++)
- if (gi_ptr->merge[ix])
- free (buffer->info.ctrs[n_ctr++].values);
- free (buffer);
- return next;
-}
-
-static struct gcov_fn_buffer **
-buffer_fn_data (const char *filename, const struct gcov_info *gi_ptr,
- struct gcov_fn_buffer **end_ptr, unsigned fn_ix)
-{
- unsigned n_ctrs = 0, ix = 0;
- struct gcov_fn_buffer *fn_buffer;
- unsigned len;
-
- for (ix = GCOV_COUNTERS; ix--;)
- if (gi_ptr->merge[ix])
- n_ctrs++;
-
- len = sizeof (*fn_buffer) + sizeof (fn_buffer->info.ctrs[0]) * n_ctrs;
- fn_buffer = (struct gcov_fn_buffer *)malloc (len);
-
- if (!fn_buffer)
- goto fail;
-
- fn_buffer->next = 0;
- fn_buffer->fn_ix = fn_ix;
- fn_buffer->info.ident = gcov_read_unsigned ();
- fn_buffer->info.lineno_checksum = gcov_read_unsigned ();
- fn_buffer->info.cfg_checksum = gcov_read_unsigned ();
-
- for (n_ctrs = ix = 0; ix != GCOV_COUNTERS; ix++)
- {
- gcov_unsigned_t length;
- gcov_type *values;
-
- if (!gi_ptr->merge[ix])
- continue;
-
- if (gcov_read_unsigned () != GCOV_TAG_FOR_COUNTER (ix))
- {
- len = 0;
- goto fail;
- }
-
- length = GCOV_TAG_COUNTER_NUM (gcov_read_unsigned ());
- len = length * sizeof (gcov_type);
- values = (gcov_type *)malloc (len);
- if (!values)
- goto fail;
-
- fn_buffer->info.ctrs[n_ctrs].num = length;
- fn_buffer->info.ctrs[n_ctrs].values = values;
-
- while (length--)
- *values++ = gcov_read_counter ();
- n_ctrs++;
- }
-
- *end_ptr = fn_buffer;
- return &fn_buffer->next;
-
- fail:
- fprintf (stderr, "profiling:%s:Function %u %s %u \n", filename, fn_ix,
- len ? "cannot allocate" : "counter mismatch", len ? len : ix);
-
- return (struct gcov_fn_buffer **)free_fn_data (gi_ptr, fn_buffer, ix);
-}
-
-/* Add an unsigned value to the current crc */
-
-static gcov_unsigned_t
-crc32_unsigned (gcov_unsigned_t crc32, gcov_unsigned_t value)
-{
- unsigned ix;
-
- for (ix = 32; ix--; value <<= 1)
- {
- unsigned feedback;
-
- feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0;
- crc32 <<= 1;
- crc32 ^= feedback;
- }
-
- return crc32;
-}
-
-/* Check if VERSION of the info block PTR matches libgcov one.
- Return 1 on success, or zero in case of versions mismatch.
- If FILENAME is not NULL, its value used for reporting purposes
- instead of value from the info block. */
-
-static int
-gcov_version (struct gcov_info *ptr, gcov_unsigned_t version,
- const char *filename)
-{
- if (version != GCOV_VERSION)
- {
- char v[4], e[4];
-
- GCOV_UNSIGNED2STRING (v, version);
- GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
-
- fprintf (stderr,
- "profiling:%s:Version mismatch - expected %.4s got %.4s\n",
- filename? filename : ptr->filename, e, v);
- return 0;
- }
- return 1;
-}
-
-/* Insert counter VALUE into HISTOGRAM. */
-
-static void
-gcov_histogram_insert(gcov_bucket_type *histogram, gcov_type value)
-{
- unsigned i;
-
- i = gcov_histo_index(value);
- histogram[i].num_counters++;
- histogram[i].cum_value += value;
- if (value < histogram[i].min_value)
- histogram[i].min_value = value;
-}
-
-/* Computes a histogram of the arc counters to place in the summary SUM. */
-
-static void
-gcov_compute_histogram (struct gcov_summary *sum)
-{
- struct gcov_info *gi_ptr;
- const struct gcov_fn_info *gfi_ptr;
- const struct gcov_ctr_info *ci_ptr;
- struct gcov_ctr_summary *cs_ptr;
- unsigned t_ix, f_ix, ctr_info_ix, ix;
- int h_ix;
-
- /* This currently only applies to arc counters. */
- t_ix = GCOV_COUNTER_ARCS;
-
- /* First check if there are any counts recorded for this counter. */
- cs_ptr = &(sum->ctrs[t_ix]);
- if (!cs_ptr->num)
- return;
-
- for (h_ix = 0; h_ix < GCOV_HISTOGRAM_SIZE; h_ix++)
- {
- cs_ptr->histogram[h_ix].num_counters = 0;
- cs_ptr->histogram[h_ix].min_value = cs_ptr->run_max;
- cs_ptr->histogram[h_ix].cum_value = 0;
- }
-
- /* Walk through all the per-object structures and record each of
- the count values in histogram. */
- for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
- {
- if (!gi_ptr->merge[t_ix])
- continue;
-
- /* Find the appropriate index into the gcov_ctr_info array
- for the counter we are currently working on based on the
- existence of the merge function pointer for this object. */
- for (ix = 0, ctr_info_ix = 0; ix < t_ix; ix++)
- {
- if (gi_ptr->merge[ix])
- ctr_info_ix++;
- }
- for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
- {
- gfi_ptr = gi_ptr->functions[f_ix];
-
- if (!gfi_ptr || gfi_ptr->key != gi_ptr)
- continue;
-
- ci_ptr = &gfi_ptr->ctrs[ctr_info_ix];
- for (ix = 0; ix < ci_ptr->num; ix++)
- gcov_histogram_insert (cs_ptr->histogram, ci_ptr->values[ix]);
- }
- }
-}
-
-/* Dump the coverage counts. We merge with existing counts when
- possible, to avoid growing the .da files ad infinitum. We use this
- program's checksum to make sure we only accumulate whole program
- statistics to the correct summary. An object file might be embedded
- in two separate programs, and we must keep the two program
- summaries separate. */
-
-void
-gcov_exit (void)
-{
- struct gcov_info *gi_ptr;
- const struct gcov_fn_info *gfi_ptr;
- struct gcov_summary this_prg; /* summary for program. */
-#if !GCOV_LOCKED
- struct gcov_summary all_prg; /* summary for all instances of program. */
-#endif
- struct gcov_ctr_summary *cs_ptr;
- const struct gcov_ctr_info *ci_ptr;
- unsigned t_ix;
- int f_ix;
- gcov_unsigned_t c_num;
- const char *gcov_prefix;
- int gcov_prefix_strip = 0;
- size_t prefix_length;
- char *gi_filename, *gi_filename_up;
- gcov_unsigned_t crc32 = 0;
-
- /* Prevent the counters from being dumped a second time on exit when the
- application already wrote out the profile using __gcov_dump(). */
- if (gcov_dump_complete)
- return;
-
-#if !GCOV_LOCKED
- memset (&all_prg, 0, sizeof (all_prg));
-#endif
- /* Find the totals for this execution. */
- memset (&this_prg, 0, sizeof (this_prg));
- for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
- {
- crc32 = crc32_unsigned (crc32, gi_ptr->stamp);
- crc32 = crc32_unsigned (crc32, gi_ptr->n_functions);
-
- for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
- {
- gfi_ptr = gi_ptr->functions[f_ix];
-
- if (gfi_ptr && gfi_ptr->key != gi_ptr)
- gfi_ptr = 0;
-
- crc32 = crc32_unsigned (crc32, gfi_ptr ? gfi_ptr->cfg_checksum : 0);
- crc32 = crc32_unsigned (crc32,
- gfi_ptr ? gfi_ptr->lineno_checksum : 0);
- if (!gfi_ptr)
- continue;
-
- ci_ptr = gfi_ptr->ctrs;
- for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++)
- {
- if (!gi_ptr->merge[t_ix])
- continue;
-
- cs_ptr = &this_prg.ctrs[t_ix];
- cs_ptr->num += ci_ptr->num;
- crc32 = crc32_unsigned (crc32, ci_ptr->num);
-
- for (c_num = 0; c_num < ci_ptr->num; c_num++)
- {
- cs_ptr->sum_all += ci_ptr->values[c_num];
- if (cs_ptr->run_max < ci_ptr->values[c_num])
- cs_ptr->run_max = ci_ptr->values[c_num];
- }
- ci_ptr++;
- }
- }
- }
- gcov_compute_histogram (&this_prg);
-
- {
- /* Check if the level of dirs to strip off specified. */
- char *tmp = getenv("GCOV_PREFIX_STRIP");
- if (tmp)
- {
- gcov_prefix_strip = atoi (tmp);
- /* Do not consider negative values. */
- if (gcov_prefix_strip < 0)
- gcov_prefix_strip = 0;
- }
- }
-
- /* Get file name relocation prefix. Non-absolute values are ignored. */
- gcov_prefix = getenv("GCOV_PREFIX");
- if (gcov_prefix)
- {
- prefix_length = strlen(gcov_prefix);
-
- /* Remove an unnecessary trailing '/' */
- if (IS_DIR_SEPARATOR (gcov_prefix[prefix_length - 1]))
- prefix_length--;
- }
- else
- prefix_length = 0;
-
- /* If no prefix was specified and a prefix stip, then we assume
- relative. */
- if (gcov_prefix_strip != 0 && prefix_length == 0)
- {
- gcov_prefix = ".";
- prefix_length = 1;
- }
- /* Allocate and initialize the filename scratch space plus one. */
- gi_filename = (char *) alloca (prefix_length + gcov_max_filename + 2);
- if (prefix_length)
- memcpy (gi_filename, gcov_prefix, prefix_length);
- gi_filename_up = gi_filename + prefix_length;
-
- /* Now merge each file. */
- for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
- {
- unsigned n_counts;
- struct gcov_summary prg; /* summary for this object over all
- program. */
- struct gcov_ctr_summary *cs_prg, *cs_tprg;
-#if !GCOV_LOCKED
- struct gcov_ctr_summary *cs_all;
-#endif
- int error = 0;
- gcov_unsigned_t tag, length;
- gcov_position_t summary_pos = 0;
- gcov_position_t eof_pos = 0;
- const char *fname, *s;
- struct gcov_fn_buffer *fn_buffer = 0;
- struct gcov_fn_buffer **fn_tail = &fn_buffer;
- struct gcov_summary_buffer *next_sum_buffer, *sum_buffer = 0;
- struct gcov_summary_buffer **sum_tail = &sum_buffer;
-
- fname = gi_ptr->filename;
-
- /* Avoid to add multiple drive letters into combined path. */
- if (prefix_length != 0 && HAS_DRIVE_SPEC(fname))
- fname += 2;
-
- /* Build relocated filename, stripping off leading
- directories from the initial filename if requested. */
- if (gcov_prefix_strip > 0)
- {
- int level = 0;
- s = fname;
- if (IS_DIR_SEPARATOR(*s))
- ++s;
-
- /* Skip selected directory levels. */
- for (; (*s != '\0') && (level < gcov_prefix_strip); s++)
- if (IS_DIR_SEPARATOR(*s))
- {
- fname = s;
- level++;
- }
- }
-
- /* Update complete filename with stripped original. */
- if (prefix_length != 0 && !IS_DIR_SEPARATOR (*fname))
- {
- /* If prefix is given, add directory separator. */
- strcpy (gi_filename_up, "/");
- strcpy (gi_filename_up + 1, fname);
- }
- else
- strcpy (gi_filename_up, fname);
-
- if (!gcov_open (gi_filename))
- {
- /* Open failed likely due to missed directory.
- Create directory and retry to open file. */
- if (create_file_directory (gi_filename))
- {
- fprintf (stderr, "profiling:%s:Skip\n", gi_filename);
- continue;
- }
- if (!gcov_open (gi_filename))
- {
- fprintf (stderr, "profiling:%s:Cannot open\n", gi_filename);
- continue;
- }
- }
-
- tag = gcov_read_unsigned ();
- if (tag)
- {
- /* Merge data from file. */
- if (tag != GCOV_DATA_MAGIC)
- {
- fprintf (stderr, "profiling:%s:Not a gcov data file\n",
- gi_filename);
- goto read_fatal;
- }
- length = gcov_read_unsigned ();
- if (!gcov_version (gi_ptr, length, gi_filename))
- goto read_fatal;
-
- length = gcov_read_unsigned ();
- if (length != gi_ptr->stamp)
- /* Read from a different compilation. Overwrite the file. */
- goto rewrite;
-
- /* Look for program summary. */
- for (f_ix = 0;;)
- {
- struct gcov_summary tmp;
-
- eof_pos = gcov_position ();
- tag = gcov_read_unsigned ();
- if (tag != GCOV_TAG_PROGRAM_SUMMARY)
- break;
-
- f_ix--;
- length = gcov_read_unsigned ();
- gcov_read_summary (&tmp);
- if ((error = gcov_is_error ()))
- goto read_error;
- if (summary_pos)
- {
- /* Save all summaries after the one that will be
- merged into below. These will need to be rewritten
- as histogram merging may change the number of non-zero
- histogram entries that will be emitted, and thus the
- size of the merged summary. */
- (*sum_tail) = (struct gcov_summary_buffer *)
- malloc (sizeof(struct gcov_summary_buffer));
- (*sum_tail)->summary = tmp;
- (*sum_tail)->next = 0;
- sum_tail = &((*sum_tail)->next);
- goto next_summary;
- }
- if (tmp.checksum != crc32)
- goto next_summary;
-
- for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++)
- if (tmp.ctrs[t_ix].num != this_prg.ctrs[t_ix].num)
- goto next_summary;
- prg = tmp;
- summary_pos = eof_pos;
-
- next_summary:;
- }
-
- /* Merge execution counts for each function. */
- for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
- f_ix++, tag = gcov_read_unsigned ())
- {
- gfi_ptr = gi_ptr->functions[f_ix];
-
- if (tag != GCOV_TAG_FUNCTION)
- goto read_mismatch;
-
- length = gcov_read_unsigned ();
- if (!length)
- /* This function did not appear in the other program.
- We have nothing to merge. */
- continue;
-
- if (length != GCOV_TAG_FUNCTION_LENGTH)
- goto read_mismatch;
-
- if (!gfi_ptr || gfi_ptr->key != gi_ptr)
- {
- /* This function appears in the other program. We
- need to buffer the information in order to write
- it back out -- we'll be inserting data before
- this point, so cannot simply keep the data in the
- file. */
- fn_tail = buffer_fn_data (gi_filename,
- gi_ptr, fn_tail, f_ix);
- if (!fn_tail)
- goto read_mismatch;
- continue;
- }
-
- length = gcov_read_unsigned ();
- if (length != gfi_ptr->ident)
- goto read_mismatch;
-
- length = gcov_read_unsigned ();
- if (length != gfi_ptr->lineno_checksum)
- goto read_mismatch;
-
- length = gcov_read_unsigned ();
- if (length != gfi_ptr->cfg_checksum)
- goto read_mismatch;
-
- ci_ptr = gfi_ptr->ctrs;
- for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
- {
- gcov_merge_fn merge = gi_ptr->merge[t_ix];
-
- if (!merge)
- continue;
-
- tag = gcov_read_unsigned ();
- length = gcov_read_unsigned ();
- if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
- || length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num))
- goto read_mismatch;
- (*merge) (ci_ptr->values, ci_ptr->num);
- ci_ptr++;
- }
- if ((error = gcov_is_error ()))
- goto read_error;
- }
-
- if (tag)
- {
- read_mismatch:;
- fprintf (stderr, "profiling:%s:Merge mismatch for %s %u\n",
- gi_filename, f_ix >= 0 ? "function" : "summary",
- f_ix < 0 ? -1 - f_ix : f_ix);
- goto read_fatal;
- }
- }
- goto rewrite;
-
- read_error:;
- fprintf (stderr, "profiling:%s:%s merging\n", gi_filename,
- error < 0 ? "Overflow": "Error");
-
- goto read_fatal;
-
- rewrite:;
- gcov_rewrite ();
- if (!summary_pos)
- {
- memset (&prg, 0, sizeof (prg));
- summary_pos = eof_pos;
- }
-
- /* Merge the summaries. */
- for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
- {
- cs_prg = &prg.ctrs[t_ix];
- cs_tprg = &this_prg.ctrs[t_ix];
-
- if (gi_ptr->merge[t_ix])
- {
- if (!cs_prg->runs++)
- cs_prg->num = cs_tprg->num;
- cs_prg->sum_all += cs_tprg->sum_all;
- if (cs_prg->run_max < cs_tprg->run_max)
- cs_prg->run_max = cs_tprg->run_max;
- cs_prg->sum_max += cs_tprg->run_max;
- if (cs_prg->runs == 1)
- memcpy (cs_prg->histogram, cs_tprg->histogram,
- sizeof (gcov_bucket_type) * GCOV_HISTOGRAM_SIZE);
- else
- gcov_histogram_merge (cs_prg->histogram, cs_tprg->histogram);
- }
- else if (cs_prg->runs)
- goto read_mismatch;
-
-#if !GCOV_LOCKED
- cs_all = &all_prg.ctrs[t_ix];
- if (!cs_all->runs && cs_prg->runs)
- {
- cs_all->num = cs_prg->num;
- cs_all->runs = cs_prg->runs;
- cs_all->sum_all = cs_prg->sum_all;
- cs_all->run_max = cs_prg->run_max;
- cs_all->sum_max = cs_prg->sum_max;
- }
- else if (!all_prg.checksum
- /* Don't compare the histograms, which may have slight
- variations depending on the order they were updated
- due to the truncating integer divides used in the
- merge. */
- && (cs_all->num != cs_prg->num
- || cs_all->runs != cs_prg->runs
- || cs_all->sum_all != cs_prg->sum_all
- || cs_all->run_max != cs_prg->run_max
- || cs_all->sum_max != cs_prg->sum_max))
- {
- fprintf (stderr,
- "profiling:%s:Data file mismatch - some data files may "
- "have been concurrently updated without locking support\n",
- gi_filename);
- all_prg.checksum = ~0u;
- }
-#endif
- }
-
- prg.checksum = crc32;
-
- /* Write out the data. */
- if (!eof_pos)
- {
- gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
- gcov_write_unsigned (gi_ptr->stamp);
- }
-
- if (summary_pos)
- gcov_seek (summary_pos);
-
- /* Generate whole program statistics. */
- gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &prg);
-
- /* Rewrite all the summaries that were after the summary we merged
- into. This is necessary as the merged summary may have a different
- size due to the number of non-zero histogram entries changing after
- merging. */
-
- while (sum_buffer)
- {
- gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &sum_buffer->summary);
- next_sum_buffer = sum_buffer->next;
- free (sum_buffer);
- sum_buffer = next_sum_buffer;
- }
-
- /* Write execution counts for each function. */
- for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
- {
- unsigned buffered = 0;
-
- if (fn_buffer && fn_buffer->fn_ix == (unsigned)f_ix)
- {
- /* Buffered data from another program. */
- buffered = 1;
- gfi_ptr = &fn_buffer->info;
- length = GCOV_TAG_FUNCTION_LENGTH;
- }
- else
- {
- gfi_ptr = gi_ptr->functions[f_ix];
- if (gfi_ptr && gfi_ptr->key == gi_ptr)
- length = GCOV_TAG_FUNCTION_LENGTH;
- else
- length = 0;
- }
-
- gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
- if (!length)
- continue;
-
- gcov_write_unsigned (gfi_ptr->ident);
- gcov_write_unsigned (gfi_ptr->lineno_checksum);
- gcov_write_unsigned (gfi_ptr->cfg_checksum);
-
- ci_ptr = gfi_ptr->ctrs;
- for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
- {
- if (!gi_ptr->merge[t_ix])
- continue;
-
- n_counts = ci_ptr->num;
- gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
- GCOV_TAG_COUNTER_LENGTH (n_counts));
- gcov_type *c_ptr = ci_ptr->values;
- while (n_counts--)
- gcov_write_counter (*c_ptr++);
- ci_ptr++;
- }
- if (buffered)
- fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
- }
-
- gcov_write_unsigned (0);
-
- read_fatal:;
- while (fn_buffer)
- fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
-
- if ((error = gcov_close ()))
- fprintf (stderr, error < 0 ?
- "profiling:%s:Overflow writing\n" :
- "profiling:%s:Error writing\n",
- gi_filename);
- }
-}
-
-/* Reset all counters to zero. */
-
-void
-gcov_clear (void)
-{
- const struct gcov_info *gi_ptr;
-
- for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
- {
- unsigned f_ix;
-
- for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
- {
- unsigned t_ix;
- const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
-
- if (!gfi_ptr || gfi_ptr->key != gi_ptr)
- continue;
- const struct gcov_ctr_info *ci_ptr = gfi_ptr->ctrs;
- for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
- {
- if (!gi_ptr->merge[t_ix])
- continue;
-
- memset (ci_ptr->values, 0, sizeof (gcov_type) * ci_ptr->num);
- ci_ptr++;
- }
- }
- }
-}
-
-/* Add a new object file onto the bb chain. Invoked automatically
- when running an object file's global ctors. */
-
-void
-__gcov_init (struct gcov_info *info)
-{
- if (!info->version || !info->n_functions)
- return;
- if (gcov_version (info, info->version, 0))
- {
- size_t filename_length = strlen(info->filename);
-
- /* Refresh the longest file name information */
- if (filename_length > gcov_max_filename)
- gcov_max_filename = filename_length;
-
- if (!gcov_list)
- atexit (gcov_exit);
-
- info->next = gcov_list;
- gcov_list = info;
- }
- info->version = 0;
-}
-
-#ifdef __GTHREAD_MUTEX_INIT
-ATTRIBUTE_HIDDEN __gthread_mutex_t __gcov_flush_mx = __GTHREAD_MUTEX_INIT;
-#define init_mx_once()
-#else
-__gthread_mutex_t __gcov_flush_mx ATTRIBUTE_HIDDEN;
-
-static void
-init_mx (void)
-{
- __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
-}
-static void
-init_mx_once (void)
-{
- static __gthread_once_t once = __GTHREAD_ONCE_INIT;
- __gthread_once (&once, init_mx);
-}
-#endif
-
-/* Called before fork or exec - write out profile information gathered so
- far and reset it to zero. This avoids duplication or loss of the
- profile information gathered so far. */
-
-void
-__gcov_flush (void)
-{
- init_mx_once ();
- __gthread_mutex_lock (&__gcov_flush_mx);
-
- gcov_exit ();
- gcov_clear ();
-
- __gthread_mutex_unlock (&__gcov_flush_mx);
-}
-
-#endif /* L_gcov */
-
-#ifdef L_gcov_reset
-
-/* Function that can be called from application to reset counters to zero,
- in order to collect profile in region of interest. */
-
-void
-__gcov_reset (void)
-{
- gcov_clear ();
- /* Re-enable dumping to support collecting profile in multiple regions
- of interest. */
- gcov_dump_complete = 0;
-}
-
-#endif /* L_gcov_reset */
-
-#ifdef L_gcov_dump
-
-/* Function that can be called from application to write profile collected
- so far, in order to collect profile in region of interest. */
-
-void
-__gcov_dump (void)
-{
- gcov_exit ();
- /* Prevent profile from being dumped a second time on application exit. */
- gcov_dump_complete = 1;
-}
-
-#endif /* L_gcov_dump */
-
-#ifdef L_gcov_merge_add
-/* The profile merging function that just adds the counters. It is given
- an array COUNTERS of N_COUNTERS old counters and it reads the same number
- of counters from the gcov file. */
-void
-__gcov_merge_add (gcov_type *counters, unsigned n_counters)
-{
- for (; n_counters; counters++, n_counters--)
- *counters += gcov_read_counter ();
-}
-#endif /* L_gcov_merge_add */
-
-#ifdef L_gcov_merge_ior
-/* The profile merging function that just adds the counters. It is given
- an array COUNTERS of N_COUNTERS old counters and it reads the same number
- of counters from the gcov file. */
-void
-__gcov_merge_ior (gcov_type *counters, unsigned n_counters)
-{
- for (; n_counters; counters++, n_counters--)
- *counters |= gcov_read_counter ();
-}
-#endif
-
-#ifdef L_gcov_merge_single
-/* The profile merging function for choosing the most common value.
- It is given an array COUNTERS of N_COUNTERS old counters and it
- reads the same number of counters from the gcov file. The counters
- are split into 3-tuples where the members of the tuple have
- meanings:
-
- -- the stored candidate on the most common value of the measured entity
- -- counter
- -- total number of evaluations of the value */
-void
-__gcov_merge_single (gcov_type *counters, unsigned n_counters)
-{
- unsigned i, n_measures;
- gcov_type value, counter, all;
-
- gcc_assert (!(n_counters % 3));
- n_measures = n_counters / 3;
- for (i = 0; i < n_measures; i++, counters += 3)
- {
- value = gcov_read_counter ();
- counter = gcov_read_counter ();
- all = gcov_read_counter ();
-
- if (counters[0] == value)
- counters[1] += counter;
- else if (counter > counters[1])
- {
- counters[0] = value;
- counters[1] = counter - counters[1];
- }
- else
- counters[1] -= counter;
- counters[2] += all;
- }
-}
-#endif /* L_gcov_merge_single */
-
-#ifdef L_gcov_merge_delta
-/* The profile merging function for choosing the most common
- difference between two consecutive evaluations of the value. It is
- given an array COUNTERS of N_COUNTERS old counters and it reads the
- same number of counters from the gcov file. The counters are split
- into 4-tuples where the members of the tuple have meanings:
-
- -- the last value of the measured entity
- -- the stored candidate on the most common difference
- -- counter
- -- total number of evaluations of the value */
-void
-__gcov_merge_delta (gcov_type *counters, unsigned n_counters)
-{
- unsigned i, n_measures;
- gcov_type value, counter, all;
-
- gcc_assert (!(n_counters % 4));
- n_measures = n_counters / 4;
- for (i = 0; i < n_measures; i++, counters += 4)
- {
- /* last = */ gcov_read_counter ();
- value = gcov_read_counter ();
- counter = gcov_read_counter ();
- all = gcov_read_counter ();
-
- if (counters[1] == value)
- counters[2] += counter;
- else if (counter > counters[2])
- {
- counters[1] = value;
- counters[2] = counter - counters[2];
- }
- else
- counters[2] -= counter;
- counters[3] += all;
- }
-}
-#endif /* L_gcov_merge_delta */
-
-#ifdef L_gcov_interval_profiler
-/* If VALUE is in interval <START, START + STEPS - 1>, then increases the
- corresponding counter in COUNTERS. If the VALUE is above or below
- the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased
- instead. */
-
-void
-__gcov_interval_profiler (gcov_type *counters, gcov_type value,
- int start, unsigned steps)
-{
- gcov_type delta = value - start;
- if (delta < 0)
- counters[steps + 1]++;
- else if (delta >= steps)
- counters[steps]++;
- else
- counters[delta]++;
-}
-#endif
-
-#ifdef L_gcov_pow2_profiler
-/* If VALUE is a power of two, COUNTERS[1] is incremented. Otherwise
- COUNTERS[0] is incremented. */
-
-void
-__gcov_pow2_profiler (gcov_type *counters, gcov_type value)
-{
- if (value & (value - 1))
- counters[0]++;
- else
- counters[1]++;
-}
-#endif
-
-/* Tries to determine the most common value among its inputs. Checks if the
- value stored in COUNTERS[0] matches VALUE. If this is the case, COUNTERS[1]
- is incremented. If this is not the case and COUNTERS[1] is not zero,
- COUNTERS[1] is decremented. Otherwise COUNTERS[1] is set to one and
- VALUE is stored to COUNTERS[0]. This algorithm guarantees that if this
- function is called more than 50% of the time with one value, this value
- will be in COUNTERS[0] in the end.
-
- In any case, COUNTERS[2] is incremented. */
-
-static inline void
-__gcov_one_value_profiler_body (gcov_type *counters, gcov_type value)
-{
- if (value == counters[0])
- counters[1]++;
- else if (counters[1] == 0)
- {
- counters[1] = 1;
- counters[0] = value;
- }
- else
- counters[1]--;
- counters[2]++;
-}
-
-#ifdef L_gcov_one_value_profiler
-void
-__gcov_one_value_profiler (gcov_type *counters, gcov_type value)
-{
- __gcov_one_value_profiler_body (counters, value);
-}
-#endif
-
-#ifdef L_gcov_indirect_call_profiler
-/* This function exist only for workaround of binutils bug 14342.
- Once this compatibility hack is obsolette, it can be removed. */
-
-/* By default, the C++ compiler will use function addresses in the
- vtable entries. Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero
- tells the compiler to use function descriptors instead. The value
- of this macro says how many words wide the descriptor is (normally 2),
- but it may be dependent on target flags. Since we do not have access
- to the target flags here we just check to see if it is set and use
- that to set VTABLE_USES_DESCRIPTORS to 0 or 1.
-
- It is assumed that the address of a function descriptor may be treated
- as a pointer to a function. */
-
-#ifdef TARGET_VTABLE_USES_DESCRIPTORS
-#define VTABLE_USES_DESCRIPTORS 1
-#else
-#define VTABLE_USES_DESCRIPTORS 0
-#endif
-
-/* Tries to determine the most common value among its inputs. */
-void
-__gcov_indirect_call_profiler (gcov_type* counter, gcov_type value,
- void* cur_func, void* callee_func)
-{
- /* If the C++ virtual tables contain function descriptors then one
- function may have multiple descriptors and we need to dereference
- the descriptors to see if they point to the same function. */
- if (cur_func == callee_func
- || (VTABLE_USES_DESCRIPTORS && callee_func
- && *(void **) cur_func == *(void **) callee_func))
- __gcov_one_value_profiler_body (counter, value);
-}
-
-#endif
-#ifdef L_gcov_indirect_call_profiler_v2
-
-/* These two variables are used to actually track caller and callee. Keep
- them in TLS memory so races are not common (they are written to often).
- The variables are set directly by GCC instrumented code, so declaration
- here must match one in tree-profile.c */
-
-#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
-__thread
-#endif
-void * __gcov_indirect_call_callee;
-#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
-__thread
-#endif
-gcov_type * __gcov_indirect_call_counters;
-
-/* By default, the C++ compiler will use function addresses in the
- vtable entries. Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero
- tells the compiler to use function descriptors instead. The value
- of this macro says how many words wide the descriptor is (normally 2),
- but it may be dependent on target flags. Since we do not have access
- to the target flags here we just check to see if it is set and use
- that to set VTABLE_USES_DESCRIPTORS to 0 or 1.
-
- It is assumed that the address of a function descriptor may be treated
- as a pointer to a function. */
-
-#ifdef TARGET_VTABLE_USES_DESCRIPTORS
-#define VTABLE_USES_DESCRIPTORS 1
-#else
-#define VTABLE_USES_DESCRIPTORS 0
-#endif
-
-/* Tries to determine the most common value among its inputs. */
-void
-__gcov_indirect_call_profiler_v2 (gcov_type value, void* cur_func)
-{
- /* If the C++ virtual tables contain function descriptors then one
- function may have multiple descriptors and we need to dereference
- the descriptors to see if they point to the same function. */
- if (cur_func == __gcov_indirect_call_callee
- || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee
- && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
- __gcov_one_value_profiler_body (__gcov_indirect_call_counters, value);
-}
-#endif
-
-#ifdef L_gcov_average_profiler
-/* Increase corresponding COUNTER by VALUE. FIXME: Perhaps we want
- to saturate up. */
-
-void
-__gcov_average_profiler (gcov_type *counters, gcov_type value)
-{
- counters[0] += value;
- counters[1] ++;
-}
-#endif
-
-#ifdef L_gcov_ior_profiler
-/* Bitwise-OR VALUE into COUNTER. */
-
-void
-__gcov_ior_profiler (gcov_type *counters, gcov_type value)
-{
- *counters |= value;
-}
-#endif
-
-#ifdef L_gcov_fork
-/* A wrapper for the fork function. Flushes the accumulated profiling data, so
- that they are not counted twice. */
-
-pid_t
-__gcov_fork (void)
-{
- pid_t pid;
- extern __gthread_mutex_t __gcov_flush_mx;
- __gcov_flush ();
- pid = fork ();
- if (pid == 0)
- __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
- return pid;
-}
-#endif
-
-#ifdef L_gcov_execl
-/* A wrapper for the execl function. Flushes the accumulated profiling data, so
- that they are not lost. */
-
-int
-__gcov_execl (const char *path, char *arg, ...)
-{
- va_list ap, aq;
- unsigned i, length;
- char **args;
-
- __gcov_flush ();
-
- va_start (ap, arg);
- va_copy (aq, ap);
-
- length = 2;
- while (va_arg (ap, char *))
- length++;
- va_end (ap);
-
- args = (char **) alloca (length * sizeof (void *));
- args[0] = arg;
- for (i = 1; i < length; i++)
- args[i] = va_arg (aq, char *);
- va_end (aq);
-
- return execv (path, args);
-}
-#endif
-
-#ifdef L_gcov_execlp
-/* A wrapper for the execlp function. Flushes the accumulated profiling data, so
- that they are not lost. */
-
-int
-__gcov_execlp (const char *path, char *arg, ...)
-{
- va_list ap, aq;
- unsigned i, length;
- char **args;
-
- __gcov_flush ();
-
- va_start (ap, arg);
- va_copy (aq, ap);
-
- length = 2;
- while (va_arg (ap, char *))
- length++;
- va_end (ap);
-
- args = (char **) alloca (length * sizeof (void *));
- args[0] = arg;
- for (i = 1; i < length; i++)
- args[i] = va_arg (aq, char *);
- va_end (aq);
-
- return execvp (path, args);
-}
-#endif
-
-#ifdef L_gcov_execle
-/* A wrapper for the execle function. Flushes the accumulated profiling data, so
- that they are not lost. */
-
-int
-__gcov_execle (const char *path, char *arg, ...)
-{
- va_list ap, aq;
- unsigned i, length;
- char **args;
- char **envp;
-
- __gcov_flush ();
-
- va_start (ap, arg);
- va_copy (aq, ap);
-
- length = 2;
- while (va_arg (ap, char *))
- length++;
- va_end (ap);
-
- args = (char **) alloca (length * sizeof (void *));
- args[0] = arg;
- for (i = 1; i < length; i++)
- args[i] = va_arg (aq, char *);
- envp = va_arg (aq, char **);
- va_end (aq);
-
- return execve (path, args, envp);
-}
-#endif
-
-#ifdef L_gcov_execv
-/* A wrapper for the execv function. Flushes the accumulated profiling data, so
- that they are not lost. */
-
-int
-__gcov_execv (const char *path, char *const argv[])
-{
- __gcov_flush ();
- return execv (path, argv);
-}
-#endif
-
-#ifdef L_gcov_execvp
-/* A wrapper for the execvp function. Flushes the accumulated profiling data, so
- that they are not lost. */
-
-int
-__gcov_execvp (const char *path, char *const argv[])
-{
- __gcov_flush ();
- return execvp (path, argv);
-}
-#endif
-
-#ifdef L_gcov_execve
-/* A wrapper for the execve function. Flushes the accumulated profiling data, so
- that they are not lost. */
-
-int
-__gcov_execve (const char *path, char *const argv[], char *const envp[])
-{
- __gcov_flush ();
- return execve (path, argv, envp);
-}
-#endif
-#endif /* inhibit_libc */
===================================================================
@@ -0,0 +1,208 @@
+/* Routines required for instrumenting a program. */
+/* Compile this one with gcc. */
+/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
+
+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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+#include "tconfig.h"
+#include "tsystem.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "libgcc_tm.h"
+
+#if !defined(inhibit_libc)
+#define IN_LIBGCOV 1
+#include "gcov-io.h"
+
+#ifdef L_gcov_interval_profiler
+/* If VALUE is in interval <START, START + STEPS - 1>, then increases the
+ corresponding counter in COUNTERS. If the VALUE is above or below
+ the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased
+ instead. */
+
+void
+__gcov_interval_profiler (gcov_type *counters, gcov_type value,
+ int start, unsigned steps)
+{
+ gcov_type delta = value - start;
+ if (delta < 0)
+ counters[steps + 1]++;
+ else if (delta >= steps)
+ counters[steps]++;
+ else
+ counters[delta]++;
+}
+#endif
+
+#ifdef L_gcov_pow2_profiler
+/* If VALUE is a power of two, COUNTERS[1] is incremented. Otherwise
+ COUNTERS[0] is incremented. */
+
+void
+__gcov_pow2_profiler (gcov_type *counters, gcov_type value)
+{
+ if (value & (value - 1))
+ counters[0]++;
+ else
+ counters[1]++;
+}
+#endif
+
+/* Tries to determine the most common value among its inputs. Checks if the
+ value stored in COUNTERS[0] matches VALUE. If this is the case, COUNTERS[1]
+ is incremented. If this is not the case and COUNTERS[1] is not zero,
+ COUNTERS[1] is decremented. Otherwise COUNTERS[1] is set to one and
+ VALUE is stored to COUNTERS[0]. This algorithm guarantees that if this
+ function is called more than 50% of the time with one value, this value
+ will be in COUNTERS[0] in the end.
+
+ In any case, COUNTERS[2] is incremented. */
+
+static inline void
+__gcov_one_value_profiler_body (gcov_type *counters, gcov_type value)
+{
+ if (value == counters[0])
+ counters[1]++;
+ else if (counters[1] == 0)
+ {
+ counters[1] = 1;
+ counters[0] = value;
+ }
+ else
+ counters[1]--;
+ counters[2]++;
+}
+
+#ifdef L_gcov_one_value_profiler
+void
+__gcov_one_value_profiler (gcov_type *counters, gcov_type value)
+{
+ __gcov_one_value_profiler_body (counters, value);
+}
+#endif
+
+#ifdef L_gcov_indirect_call_profiler
+/* This function exist only for workaround of binutils bug 14342.
+ Once this compatibility hack is obsolette, it can be removed. */
+
+/* By default, the C++ compiler will use function addresses in the
+ vtable entries. Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero
+ tells the compiler to use function descriptors instead. The value
+ of this macro says how many words wide the descriptor is (normally 2),
+ but it may be dependent on target flags. Since we do not have access
+ to the target flags here we just check to see if it is set and use
+ that to set VTABLE_USES_DESCRIPTORS to 0 or 1.
+
+ It is assumed that the address of a function descriptor may be treated
+ as a pointer to a function. */
+
+#ifdef TARGET_VTABLE_USES_DESCRIPTORS
+#define VTABLE_USES_DESCRIPTORS 1
+#else
+#define VTABLE_USES_DESCRIPTORS 0
+#endif
+
+/* Tries to determine the most common value among its inputs. */
+void
+__gcov_indirect_call_profiler (gcov_type* counter, gcov_type value,
+ void* cur_func, void* callee_func)
+{
+ /* If the C++ virtual tables contain function descriptors then one
+ function may have multiple descriptors and we need to dereference
+ the descriptors to see if they point to the same function. */
+ if (cur_func == callee_func
+ || (VTABLE_USES_DESCRIPTORS && callee_func
+ && *(void **) cur_func == *(void **) callee_func))
+ __gcov_one_value_profiler_body (counter, value);
+}
+
+#endif
+#ifdef L_gcov_indirect_call_profiler_v2
+
+/* These two variables are used to actually track caller and callee. Keep
+ them in TLS memory so races are not common (they are written to often).
+ The variables are set directly by GCC instrumented code, so declaration
+ here must match one in tree-profile.c */
+
+#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
+__thread
+#endif
+void * __gcov_indirect_call_callee;
+#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
+__thread
+#endif
+gcov_type * __gcov_indirect_call_counters;
+
+/* By default, the C++ compiler will use function addresses in the
+ vtable entries. Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero
+ tells the compiler to use function descriptors instead. The value
+ of this macro says how many words wide the descriptor is (normally 2),
+ but it may be dependent on target flags. Since we do not have access
+ to the target flags here we just check to see if it is set and use
+ that to set VTABLE_USES_DESCRIPTORS to 0 or 1.
+
+ It is assumed that the address of a function descriptor may be treated
+ as a pointer to a function. */
+
+#ifdef TARGET_VTABLE_USES_DESCRIPTORS
+#define VTABLE_USES_DESCRIPTORS 1
+#else
+#define VTABLE_USES_DESCRIPTORS 0
+#endif
+
+/* Tries to determine the most common value among its inputs. */
+void
+__gcov_indirect_call_profiler_v2 (gcov_type value, void* cur_func)
+{
+ /* If the C++ virtual tables contain function descriptors then one
+ function may have multiple descriptors and we need to dereference
+ the descriptors to see if they point to the same function. */
+ if (cur_func == __gcov_indirect_call_callee
+ || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee
+ && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
+ __gcov_one_value_profiler_body (__gcov_indirect_call_counters, value);
+}
+#endif
+
+#ifdef L_gcov_average_profiler
+/* Increase corresponding COUNTER by VALUE. FIXME: Perhaps we want
+ to saturate up. */
+
+void
+__gcov_average_profiler (gcov_type *counters, gcov_type value)
+{
+ counters[0] += value;
+ counters[1] ++;
+}
+#endif
+
+#ifdef L_gcov_ior_profiler
+/* Bitwise-OR VALUE into COUNTER. */
+
+void
+__gcov_ior_profiler (gcov_type *counters, gcov_type value)
+{
+ *counters |= value;
+}
+#endif
+
+#endif /* inhibit_libc */
===================================================================
@@ -0,0 +1,851 @@
+/* Routines required for instrumenting a program. */
+/* Compile this one with gcc. */
+/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
+
+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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef xmalloc
+#define xmalloc malloc
+#endif
+
+#ifndef PROFILE_TOOL
+#include "tconfig.h"
+#include "tsystem.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "libgcc_tm.h"
+#endif
+
+#if defined(inhibit_libc)
+#define IN_LIBGCOV (-1)
+#else
+#define IN_LIBGCOV 1
+#if defined(L_gcov)
+#define GCOV_LINKAGE /* nothing */
+#endif
+#endif
+#include "gcov-io.h"
+
+#if defined(inhibit_libc)
+/* If libc and its header files are not available, provide dummy functions. */
+
+#if defined(L_gcov)
+void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
+#endif
+
+#else /* inhibit_libc */
+
+#include <string.h>
+#if GCOV_LOCKED
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#endif
+
+extern void gcov_clear (void) ATTRIBUTE_HIDDEN;
+extern void gcov_exit (void) ATTRIBUTE_HIDDEN;
+
+#ifdef L_gcov
+#include "gcov-io.c"
+
+struct gcov_fn_buffer
+{
+ struct gcov_fn_buffer *next;
+ unsigned fn_ix;
+ struct gcov_fn_info info;
+ /* note gcov_fn_info ends in a trailing array. */
+};
+
+struct gcov_summary_buffer
+{
+ struct gcov_summary_buffer *next;
+ struct gcov_summary summary;
+};
+
+/* Chain of per-object gcov structures. */
+static struct gcov_info *gcov_list;
+
+/* Set the head of gcov_list. */
+void
+set_gcov_list (struct gcov_info *head)
+{
+ gcov_list = head;
+}
+
+/* Use this summary checksum rather the computed one if the value is
+ non-zero. */
+static gcov_unsigned_t saved_summary_checksum;
+
+/* Size of the longest file name. */
+static size_t gcov_max_filename = 0;
+
+/* Flag when the profile has already been dumped via __gcov_dump(). */
+static int gcov_dump_complete;
+
+inline void
+set_gcov_dump_complete (void)
+{
+ gcov_dump_complete = 1;
+}
+
+inline void
+reset_gcov_dump_complete (void)
+{
+ gcov_dump_complete = 0;
+}
+
+/* A utility function for outputing errors. */
+static int gcov_error (const char *, ...);
+
+static struct gcov_fn_buffer *
+free_fn_data (const struct gcov_info *gi_ptr, struct gcov_fn_buffer *buffer,
+ unsigned limit)
+{
+ struct gcov_fn_buffer *next;
+ unsigned ix, n_ctr = 0;
+
+ if (!buffer)
+ return 0;
+ next = buffer->next;
+
+ for (ix = 0; ix != limit; ix++)
+ if (gi_ptr->merge[ix])
+ free (buffer->info.ctrs[n_ctr++].values);
+ free (buffer);
+ return next;
+}
+
+static struct gcov_fn_buffer **
+buffer_fn_data (const char *filename, const struct gcov_info *gi_ptr,
+ struct gcov_fn_buffer **end_ptr, unsigned fn_ix)
+{
+ unsigned n_ctrs = 0, ix = 0;
+ struct gcov_fn_buffer *fn_buffer;
+ unsigned len;
+
+ for (ix = GCOV_COUNTERS; ix--;)
+ if (gi_ptr->merge[ix])
+ n_ctrs++;
+
+ len = sizeof (*fn_buffer) + sizeof (fn_buffer->info.ctrs[0]) * n_ctrs;
+ fn_buffer = (struct gcov_fn_buffer *)xmalloc (len);
+
+ if (!fn_buffer)
+ goto fail;
+
+ fn_buffer->next = 0;
+ fn_buffer->fn_ix = fn_ix;
+ fn_buffer->info.ident = gcov_read_unsigned ();
+ fn_buffer->info.lineno_checksum = gcov_read_unsigned ();
+ fn_buffer->info.cfg_checksum = gcov_read_unsigned ();
+
+ for (n_ctrs = ix = 0; ix != GCOV_COUNTERS; ix++)
+ {
+ gcov_unsigned_t length;
+ gcov_type *values;
+
+ if (!gi_ptr->merge[ix])
+ continue;
+
+ if (gcov_read_unsigned () != GCOV_TAG_FOR_COUNTER (ix))
+ {
+ len = 0;
+ goto fail;
+ }
+
+ length = GCOV_TAG_COUNTER_NUM (gcov_read_unsigned ());
+ len = length * sizeof (gcov_type);
+ values = (gcov_type *)xmalloc (len);
+ if (!values)
+ goto fail;
+
+ fn_buffer->info.ctrs[n_ctrs].num = length;
+ fn_buffer->info.ctrs[n_ctrs].values = values;
+
+ while (length--)
+ *values++ = gcov_read_counter ();
+ n_ctrs++;
+ }
+
+ *end_ptr = fn_buffer;
+ return &fn_buffer->next;
+
+fail:
+ gcov_error ("profiling:%s:Function %u %s %u \n", filename, fn_ix,
+ len ? "cannot allocate" : "counter mismatch", len ? len : ix);
+
+ return (struct gcov_fn_buffer **)free_fn_data (gi_ptr, fn_buffer, ix);
+}
+
+/* Add an unsigned value to the current crc */
+
+static gcov_unsigned_t
+crc32_unsigned (gcov_unsigned_t crc32, gcov_unsigned_t value)
+{
+ unsigned ix;
+
+ for (ix = 32; ix--; value <<= 1)
+ {
+ unsigned feedback;
+
+ feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0;
+ crc32 <<= 1;
+ crc32 ^= feedback;
+ }
+
+ return crc32;
+}
+
+/* Check if VERSION of the info block PTR matches libgcov one.
+ Return 1 on success, or zero in case of versions mismatch.
+ If FILENAME is not NULL, its value used for reporting purposes
+ instead of value from the info block. */
+
+static int
+gcov_version (struct gcov_info *ptr, gcov_unsigned_t version,
+ const char *filename)
+{
+ if (version != GCOV_VERSION)
+ {
+ char v[4], e[4];
+
+ GCOV_UNSIGNED2STRING (v, version);
+ GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
+
+ gcov_error ("profiling:%s:Version mismatch - expected %.4s got %.4s\n",
+ filename? filename : ptr->filename, e, v);
+ return 0;
+ }
+ return 1;
+}
+
+/* Insert counter VALUE into HISTOGRAM. */
+
+static void
+gcov_histogram_insert(gcov_bucket_type *histogram, gcov_type value)
+{
+ unsigned i;
+
+ i = gcov_histo_index(value);
+ histogram[i].num_counters++;
+ histogram[i].cum_value += value;
+ if (value < histogram[i].min_value)
+ histogram[i].min_value = value;
+}
+
+/* Computes a histogram of the arc counters to place in the summary SUM. */
+
+static void
+gcov_compute_histogram (struct gcov_summary *sum)
+{
+ struct gcov_info *gi_ptr;
+ const struct gcov_fn_info *gfi_ptr;
+ const struct gcov_ctr_info *ci_ptr;
+ struct gcov_ctr_summary *cs_ptr;
+ unsigned t_ix, f_ix, ctr_info_ix, ix;
+ int h_ix;
+
+ /* This currently only applies to arc counters. */
+ t_ix = GCOV_COUNTER_ARCS;
+
+ /* First check if there are any counts recorded for this counter. */
+ cs_ptr = &(sum->ctrs[t_ix]);
+ if (!cs_ptr->num)
+ return;
+
+ for (h_ix = 0; h_ix < GCOV_HISTOGRAM_SIZE; h_ix++)
+ {
+ cs_ptr->histogram[h_ix].num_counters = 0;
+ cs_ptr->histogram[h_ix].min_value = cs_ptr->run_max;
+ cs_ptr->histogram[h_ix].cum_value = 0;
+ }
+
+ /* Walk through all the per-object structures and record each of
+ the count values in histogram. */
+ for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
+ {
+ if (!gi_ptr->merge[t_ix])
+ continue;
+
+ /* Find the appropriate index into the gcov_ctr_info array
+ for the counter we are currently working on based on the
+ existence of the merge function pointer for this object. */
+ for (ix = 0, ctr_info_ix = 0; ix < t_ix; ix++)
+ {
+ if (gi_ptr->merge[ix])
+ ctr_info_ix++;
+ }
+ for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
+ {
+ gfi_ptr = gi_ptr->functions[f_ix];
+
+ if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+ continue;
+
+ ci_ptr = &gfi_ptr->ctrs[ctr_info_ix];
+ for (ix = 0; ix < ci_ptr->num; ix++)
+ gcov_histogram_insert (cs_ptr->histogram, ci_ptr->values[ix]);
+ }
+ }
+}
+
+/* summary for program. */
+static struct gcov_summary this_prg;
+#if !GCOV_LOCKED
+/* summary for all instances of program. */
+static struct gcov_summary all_prg;
+#endif
+/* crc32 for this program. */
+static gcov_unsigned_t crc32;
+/* gcda filename. */
+static char *gi_filename;
+/* buffer for the fn_data from another program. */
+static struct gcov_fn_buffer *fn_buffer;
+/* buffer for summary from other programs to be written out. */
+static struct gcov_summary_buffer *sum_buffer;
+
+/* This funtions computes the program level summary and the histo-gram.
+ It initializes ALL_PRG, computes CRC32, and stores the summary in
+ THIS_PRG. All these three variables are file statics. */
+
+static void
+gcov_exit_compute_summary (void)
+{
+ struct gcov_info *gi_ptr;
+ const struct gcov_fn_info *gfi_ptr;
+ struct gcov_ctr_summary *cs_ptr;
+ const struct gcov_ctr_info *ci_ptr;
+ int f_ix;
+ unsigned t_ix;
+ gcov_unsigned_t c_num;
+
+#if !GCOV_LOCKED
+ memset (&all_prg, 0, sizeof (all_prg));
+#endif
+ /* Find the totals for this execution. */
+ memset (&this_prg, 0, sizeof (this_prg));
+ for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
+ {
+ crc32 = crc32_unsigned (crc32, gi_ptr->stamp);
+ crc32 = crc32_unsigned (crc32, gi_ptr->n_functions);
+
+ for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
+ {
+ gfi_ptr = gi_ptr->functions[f_ix];
+
+ if (gfi_ptr && gfi_ptr->key != gi_ptr)
+ gfi_ptr = 0;
+
+ crc32 = crc32_unsigned (crc32, gfi_ptr ? gfi_ptr->cfg_checksum : 0);
+ crc32 = crc32_unsigned (crc32,
+ gfi_ptr ? gfi_ptr->lineno_checksum : 0);
+ if (!gfi_ptr)
+ continue;
+
+ ci_ptr = gfi_ptr->ctrs;
+ for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++)
+ {
+ if (!gi_ptr->merge[t_ix])
+ continue;
+
+ cs_ptr = &this_prg.ctrs[t_ix];
+ cs_ptr->num += ci_ptr->num;
+ crc32 = crc32_unsigned (crc32, ci_ptr->num);
+
+ for (c_num = 0; c_num < ci_ptr->num; c_num++)
+ {
+ cs_ptr->sum_all += ci_ptr->values[c_num];
+ if (cs_ptr->run_max < ci_ptr->values[c_num])
+ cs_ptr->run_max = ci_ptr->values[c_num];
+ }
+ ci_ptr++;
+ }
+ }
+ }
+ gcov_compute_histogram (&this_prg);
+}
+
+/* A struct that bundles all the related information about the
+ gcda filename. */
+struct gcov_filename_aux{
+ char *gi_filename_up;
+ int gcov_prefix_strip;
+ size_t prefix_length;
+};
+
+/* Including system dependent components. */
+#include "libgcov-driver-system.c"
+
+/* This function merges counters in GI_PTR to an existing gcda file.
+ Return 0 on success.
+ Return -1 on error. In this case, caller will goto read_fatal. */
+
+static int
+gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
+ struct gcov_summary *prg_p,
+ gcov_position_t *summary_pos_p,
+ gcov_position_t *eof_pos_p)
+{
+ gcov_unsigned_t tag, length;
+ unsigned t_ix;
+ int f_ix;
+ int error = 0;
+ struct gcov_fn_buffer **fn_tail = &fn_buffer;
+ struct gcov_summary_buffer **sum_tail = &sum_buffer;
+
+ length = gcov_read_unsigned ();
+ if (!gcov_version (gi_ptr, length, gi_filename))
+ return -1;
+
+ length = gcov_read_unsigned ();
+ if (length != gi_ptr->stamp)
+ /* Read from a different compilation. Overwrite the file. */
+ return 0;
+
+ /* Look for program summary. */
+ for (f_ix = 0;;)
+ {
+ struct gcov_summary tmp;
+
+ *eof_pos_p = gcov_position ();
+ tag = gcov_read_unsigned ();
+ if (tag != GCOV_TAG_PROGRAM_SUMMARY)
+ break;
+
+ f_ix--;
+ length = gcov_read_unsigned ();
+ gcov_read_summary (&tmp);
+ if ((error = gcov_is_error ()))
+ goto read_error;
+ if (*summary_pos_p)
+ {
+ /* Save all summaries after the one that will be
+ merged into below. These will need to be rewritten
+ as histogram merging may change the number of non-zero
+ histogram entries that will be emitted, and thus the
+ size of the merged summary. */
+ (*sum_tail) = (struct gcov_summary_buffer *)
+ xmalloc (sizeof(struct gcov_summary_buffer));
+ (*sum_tail)->summary = tmp;
+ (*sum_tail)->next = 0;
+ sum_tail = &((*sum_tail)->next);
+ goto next_summary;
+ }
+ if (tmp.checksum != crc32)
+ goto next_summary;
+
+ for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++)
+ if (tmp.ctrs[t_ix].num != this_prg.ctrs[t_ix].num)
+ goto next_summary;
+ *prg_p = tmp;
+ *summary_pos_p = *eof_pos_p;
+
+ next_summary:;
+ }
+
+ /* Merge execution counts for each function. */
+ for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
+ f_ix++, tag = gcov_read_unsigned ())
+ {
+ const struct gcov_ctr_info *ci_ptr;
+ const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
+
+ if (tag != GCOV_TAG_FUNCTION)
+ goto read_mismatch;
+
+ length = gcov_read_unsigned ();
+ if (!length)
+ /* This function did not appear in the other program.
+ We have nothing to merge. */
+ continue;
+
+ if (length != GCOV_TAG_FUNCTION_LENGTH)
+ goto read_mismatch;
+
+ if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+ {
+ /* This function appears in the other program. We
+ need to buffer the information in order to write
+ it back out -- we'll be inserting data before
+ this point, so cannot simply keep the data in the
+ file. */
+ fn_tail = buffer_fn_data (gi_filename,
+ gi_ptr, fn_tail, f_ix);
+ if (!fn_tail)
+ goto read_mismatch;
+ continue;
+ }
+
+ length = gcov_read_unsigned ();
+ if (length != gfi_ptr->ident)
+ goto read_mismatch;
+
+ length = gcov_read_unsigned ();
+ if (length != gfi_ptr->lineno_checksum)
+ goto read_mismatch;
+
+ length = gcov_read_unsigned ();
+ if (length != gfi_ptr->cfg_checksum)
+ goto read_mismatch;
+
+ ci_ptr = gfi_ptr->ctrs;
+ for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
+ {
+ gcov_merge_fn merge = gi_ptr->merge[t_ix];
+
+ if (!merge)
+ continue;
+
+ tag = gcov_read_unsigned ();
+ length = gcov_read_unsigned ();
+ if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
+ || length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num))
+ goto read_mismatch;
+ (*merge) (ci_ptr->values, ci_ptr->num, NULL, 1);
+ ci_ptr++;
+ }
+ if ((error = gcov_is_error ()))
+ goto read_error;
+ }
+
+ if (tag)
+ {
+ read_mismatch:;
+ gcov_error ("profiling:%s:Merge mismatch for %s %u\n",
+ gi_filename, f_ix >= 0 ? "function" : "summary",
+ f_ix < 0 ? -1 - f_ix : f_ix);
+ return -1;
+ }
+ return 0;
+
+read_error:
+ gcov_error ("profiling:%s:%s merging\n", gi_filename,
+ error < 0 ? "Overflow": "Error");
+ return -1;
+}
+
+/* Write counters in GI_PTR and the summary in PRG to a gcda file. In
+ the case of appending to an existing file, SUMMARY_POS will be non-zero.
+ We will write the file starting from SUMMAY_POS. */
+
+static void
+gcov_exit_write_gcda (const struct gcov_info *gi_ptr,
+ const struct gcov_summary *prg_p,
+ const gcov_position_t summary_pos)
+{
+ unsigned f_ix;
+ struct gcov_summary_buffer *next_sum_buffer;
+
+ /* Write out the data. */
+ if (!summary_pos)
+ {
+ gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
+ gcov_write_unsigned (gi_ptr->stamp);
+ }
+ else
+ gcov_seek (summary_pos);
+
+ /* Generate whole program statistics. */
+ gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, prg_p);
+
+ /* Rewrite all the summaries that were after the summary we merged
+ into. This is necessary as the merged summary may have a different
+ size due to the number of non-zero histogram entries changing after
+ merging. */
+
+ while (sum_buffer)
+ {
+ gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &sum_buffer->summary);
+ next_sum_buffer = sum_buffer->next;
+ free (sum_buffer);
+ sum_buffer = next_sum_buffer;
+ }
+
+ /* Write execution counts for each function. */
+ for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
+ {
+ unsigned buffered = 0;
+ const struct gcov_fn_info *gfi_ptr;
+ const struct gcov_ctr_info *ci_ptr;
+ gcov_unsigned_t length;
+ unsigned t_ix;
+
+ if (fn_buffer && fn_buffer->fn_ix == f_ix)
+ {
+ /* Buffered data from another program. */
+ buffered = 1;
+ gfi_ptr = &fn_buffer->info;
+ length = GCOV_TAG_FUNCTION_LENGTH;
+ }
+ else
+ {
+ gfi_ptr = gi_ptr->functions[f_ix];
+ if (gfi_ptr && gfi_ptr->key == gi_ptr)
+ length = GCOV_TAG_FUNCTION_LENGTH;
+ else
+ length = 0;
+ }
+
+ gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
+ if (!length)
+ continue;
+
+ gcov_write_unsigned (gfi_ptr->ident);
+ gcov_write_unsigned (gfi_ptr->lineno_checksum);
+ gcov_write_unsigned (gfi_ptr->cfg_checksum);
+
+ ci_ptr = gfi_ptr->ctrs;
+ for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
+ {
+ gcov_unsigned_t n_counts;
+ gcov_type *c_ptr;
+
+ if (!gi_ptr->merge[t_ix])
+ continue;
+
+ n_counts = ci_ptr->num;
+ gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
+ GCOV_TAG_COUNTER_LENGTH (n_counts));
+ c_ptr = ci_ptr->values;
+ while (n_counts--)
+ gcov_write_counter (*c_ptr++);
+ ci_ptr++;
+ }
+ if (buffered)
+ fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
+ }
+
+ gcov_write_unsigned (0);
+}
+
+/* Dump the coverage counts for one gcov_info object. We merge with existing
+ counts when possible, to avoid growing the .da files ad infinitum. We use
+ this program's checksum to make sure we only accumulate whole program
+ statistics to the correct summary. An object file might be embedded
+ in two separate programs, and we must keep the two program
+ summaries separate. */
+
+static void
+gcov_exit_dump_gcov (struct gcov_info *gi_ptr, struct gcov_filename_aux *gf)
+{
+ struct gcov_summary prg; /* summary for this object over all
+ program. */
+ struct gcov_ctr_summary *cs_prg, *cs_tprg;
+#if !GCOV_LOCKED
+ struct gcov_ctr_summary *cs_all;
+#endif
+ int error = 0, ret;
+ gcov_unsigned_t tag;
+ gcov_position_t summary_pos = 0;
+ gcov_position_t eof_pos = 0;
+ unsigned t_ix;
+
+ fn_buffer = 0;
+ sum_buffer = 0;
+
+ ret = gcov_exit_open_gcda_file (gi_ptr, gf);
+ if (ret != 0)
+ return;
+
+ tag = gcov_read_unsigned ();
+ if (tag)
+ {
+ /* Merge data from file. */
+ if (tag != GCOV_DATA_MAGIC)
+ {
+ gcov_error ("profiling:%s:Not a gcov data file\n", gi_filename);
+ goto read_fatal;
+ }
+ ret = gcov_exit_merge_gcda (gi_ptr, &prg, &summary_pos, &eof_pos);
+ if (ret == -1)
+ goto read_fatal;
+ }
+
+ gcov_rewrite ();
+
+ if (!summary_pos)
+ {
+ memset (&prg, 0, sizeof (prg));
+ summary_pos = eof_pos;
+ }
+
+ /* Merge the summaries. */
+ for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
+ {
+ cs_prg = &prg.ctrs[t_ix];
+ cs_tprg = &this_prg.ctrs[t_ix];
+
+ if (gi_ptr->merge[t_ix])
+ {
+ if (!cs_prg->runs++)
+ cs_prg->num = cs_tprg->num;
+ cs_prg->sum_all += cs_tprg->sum_all;
+ if (cs_prg->run_max < cs_tprg->run_max)
+ cs_prg->run_max = cs_tprg->run_max;
+ cs_prg->sum_max += cs_tprg->run_max;
+ if (cs_prg->runs == 1)
+ memcpy (cs_prg->histogram, cs_tprg->histogram,
+ sizeof (gcov_bucket_type) * GCOV_HISTOGRAM_SIZE);
+ else
+ gcov_histogram_merge (cs_prg->histogram, cs_tprg->histogram);
+ }
+ else if (cs_prg->runs)
+ {
+ gcov_error ("profiling:%s:Merge mismatch for summary.\n",
+ gi_filename);
+ goto read_fatal;
+ }
+
+#if !GCOV_LOCKED
+ cs_all = &all_prg.ctrs[t_ix];
+ if (!cs_all->runs && cs_prg->runs)
+ {
+ cs_all->num = cs_prg->num;
+ cs_all->runs = cs_prg->runs;
+ cs_all->sum_all = cs_prg->sum_all;
+ cs_all->run_max = cs_prg->run_max;
+ cs_all->sum_max = cs_prg->sum_max;
+ }
+ else if (!all_prg.checksum
+ /* Don't compare the histograms, which may have slight
+ variations depending on the order they were updated
+ due to the truncating integer divides used in the
+ merge. */
+ && (cs_all->num != cs_prg->num
+ || cs_all->runs != cs_prg->runs
+ || cs_all->sum_all != cs_prg->sum_all
+ || cs_all->run_max != cs_prg->run_max
+ || cs_all->sum_max != cs_prg->sum_max))
+ {
+ gcov_error ("profiling:%s:Data file mismatch - some "
+ "data files may have been concurrently "
+ "updated without locking support\n", gi_filename);
+ all_prg.checksum = ~0u;
+ }
+#endif
+ }
+
+ if (saved_summary_checksum)
+ prg.checksum = saved_summary_checksum;
+ else
+ prg.checksum = crc32;
+
+ gcov_exit_write_gcda (gi_ptr, &prg, summary_pos);
+ /* fall through */
+
+read_fatal:;
+ while (fn_buffer)
+ fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
+
+ if ((error = gcov_close ()))
+ gcov_error (error < 0 ?
+ "profiling:%s:Overflow writing\n" :
+ "profiling:%s:Error writing\n",
+ gi_filename);
+}
+
+
+/* Dump all the coverage counts for the program. It first computes program
+ summary and then traverses gcov_list list and dumps the gcov_info
+ objects one by one. */
+
+void
+gcov_exit (void)
+{
+ struct gcov_info *gi_ptr;
+ struct gcov_filename_aux gf;
+
+ /* Prevent the counters from being dumped a second time on exit when the
+ application already wrote out the profile using __gcov_dump(). */
+ if (gcov_dump_complete)
+ return;
+
+ gcov_exit_compute_summary ();
+
+ allocate_filename_struct (&gf);
+
+ /* Now merge each file. */
+ for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
+ gcov_exit_dump_gcov (gi_ptr, &gf);
+
+ if (gi_filename)
+ free (gi_filename);
+}
+
+/* Reset all counters to zero. */
+
+void
+gcov_clear (void)
+{
+ const struct gcov_info *gi_ptr;
+
+ for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
+ {
+ unsigned f_ix;
+
+ for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
+ {
+ unsigned t_ix;
+ const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
+
+ if (!gfi_ptr || gfi_ptr->key != gi_ptr)
+ continue;
+ const struct gcov_ctr_info *ci_ptr = gfi_ptr->ctrs;
+ for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
+ {
+ if (!gi_ptr->merge[t_ix])
+ continue;
+
+ memset (ci_ptr->values, 0, sizeof (gcov_type) * ci_ptr->num);
+ ci_ptr++;
+ }
+ }
+ }
+}
+
+/* Add a new object file onto the bb chain. Invoked automatically
+ when running an object file's global ctors. */
+
+void
+__gcov_init (struct gcov_info *info)
+{
+ if (!info->version || !info->n_functions)
+ return;
+ if (gcov_version (info, info->version, 0))
+ {
+ size_t filename_length = strlen(info->filename);
+
+ /* Refresh the longest file name information */
+ if (filename_length > gcov_max_filename)
+ gcov_max_filename = filename_length;
+
+ if (!gcov_list)
+ atexit (gcov_exit);
+
+ info->next = gcov_list;
+ gcov_list = info;
+ }
+ info->version = 0;
+}
+
+#endif /* L_gcov */
+#endif /* inhibit_libc */
===================================================================
@@ -0,0 +1,217 @@
+/* Routines required for instrumenting a program. */
+/* Compile this one with gcc. */
+/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
+
+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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef PROFILE_TOOL
+#include "tconfig.h"
+#include "tsystem.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "libgcc_tm.h"
+#endif
+
+#if defined(inhibit_libc)
+#define IN_LIBGCOV (-1)
+#else
+#define IN_LIBGCOV 1
+#endif
+
+#include "gcov-io.h"
+
+#if defined(inhibit_libc)
+/* If libc and its header files are not available, provide dummy functions. */
+
+#ifdef L_gcov_merge_add
+void __gcov_merge_add (gcov_type *counters __attribute__ ((unused)),
+ unsigned n_counters __attribute__ ((unused)),
+ unsigned gcov_type *src __attribute__ ((unused)),
+ unsigned w __attribute__ ((unused))) {}
+#endif
+
+#ifdef L_gcov_merge_single
+void __gcov_merge_single (gcov_type *counters __attribute__ ((unused)),
+ unsigned n_counters __attribute__ ((unused)),
+ unsigned gcov_type *src __attribute__ ((unused)),
+ unsigned w __attribute__ ((unused))) {}
+#endif
+
+#ifdef L_gcov_merge_delta
+void __gcov_merge_delta (gcov_type *counters __attribute__ ((unused)),
+ unsigned n_counters __attribute__ ((unused)),
+ unsigned gcov_type *src __attribute__ ((unused)),
+ unsigned w __attribute__ ((unused))) {}
+#endif
+
+#else
+
+#ifdef L_gcov_merge_add
+/* The profile merging function that just adds the counters. It is given
+ an array COUNTERS of N_COUNTERS old counters and it reads the same number
+ of counters from the gcov file. */
+void
+__gcov_merge_add (gcov_type *counters, unsigned n_counters,
+ gcov_type *src, unsigned w)
+{
+ int in_mem = (src != 0);
+
+ for (; n_counters; counters++, n_counters--)
+ {
+ gcov_type value;
+
+ if (in_mem)
+ value = *(src++);
+ else
+ value = gcov_read_counter ();
+
+ *counters += w * value;
+ }
+}
+#endif /* L_gcov_merge_add */
+
+#ifdef L_gcov_merge_ior
+/* The profile merging function that just adds the counters. It is given
+ an array COUNTERS of N_COUNTERS old counters and it reads the same number
+ of counters from the gcov file. */
+void
+__gcov_merge_ior (gcov_type *counters, unsigned n_counters,
+ gcov_type *src, unsigned w __attribute__ ((unused)))
+{
+ int in_mem = (src != 0);
+
+ for (; n_counters; counters++, n_counters--)
+ {
+ gcov_type value;
+
+ if (in_mem)
+ value = *(src++);
+ else
+ value = gcov_read_counter ();
+
+ *counters |= value;
+ }
+}
+#endif
+
+#ifdef L_gcov_merge_single
+/* The profile merging function for choosing the most common value.
+ It is given an array COUNTERS of N_COUNTERS old counters and it
+ reads the same number of counters from the gcov file. The counters
+ are split into 3-tuples where the members of the tuple have
+ meanings:
+
+ -- the stored candidate on the most common value of the measured entity
+ -- counter
+ -- total number of evaluations of the value */
+void
+__gcov_merge_single (gcov_type *counters, unsigned n_counters,
+ gcov_type *src, unsigned w)
+{
+ unsigned i, n_measures;
+ gcov_type value, counter, all;
+ int in_mem = (src != 0);
+
+ gcc_assert (!(n_counters % 3));
+ n_measures = n_counters / 3;
+ for (i = 0; i < n_measures; i++, counters += 3, src += 3)
+ {
+ if (in_mem)
+ {
+ value = src[0];
+ counter = src[1];
+ all = src[2];
+ }
+ else
+ {
+ value = gcov_read_counter ();
+ counter = gcov_read_counter ();
+ all = gcov_read_counter ();
+ }
+ counter *= w;
+
+ if (counters[0] == value)
+ counters[1] += counter;
+ else if (counter > counters[1])
+ {
+ counters[0] = value;
+ counters[1] = counter - counters[1];
+ }
+ else
+ counters[1] -= counter;
+ counters[2] += all;
+ }
+}
+#endif /* L_gcov_merge_single */
+
+#ifdef L_gcov_merge_delta
+/* The profile merging function for choosing the most common
+ difference between two consecutive evaluations of the value. It is
+ given an array COUNTERS of N_COUNTERS old counters and it reads the
+ same number of counters from the gcov file. The counters are split
+ into 4-tuples where the members of the tuple have meanings:
+
+ -- the last value of the measured entity
+ -- the stored candidate on the most common difference
+ -- counter
+ -- total number of evaluations of the value */
+void
+__gcov_merge_delta (gcov_type *counters, unsigned n_counters,
+ gcov_type *src, unsigned w)
+{
+ unsigned i, n_measures;
+ gcov_type value, counter, all;
+ int in_mem = (src != 0);
+
+ gcc_assert (!(n_counters % 4));
+ n_measures = n_counters / 4;
+ for (i = 0; i < n_measures; i++, counters += 4, src += 4)
+ {
+ if (in_mem)
+ {
+ value = src[1];
+ counter = src[2];
+ all = src[3];
+ }
+ else
+ {
+ /* last = */ gcov_read_counter ();
+ value = gcov_read_counter ();
+ counter = gcov_read_counter ();
+ all = gcov_read_counter ();
+ }
+ counter *= w;
+
+ if (counters[1] == value)
+ counters[2] += counter;
+ else if (counter > counters[2])
+ {
+ counters[1] = value;
+ counters[2] = counter - counters[2];
+ }
+ else
+ counters[2] -= counter;
+ counters[3] += all;
+ }
+}
+#endif /* L_gcov_merge_delta */
+#endif /* inhibit_libc */
===================================================================
@@ -853,17 +853,37 @@ include $(iterator)
# Build libgcov components.
# Defined in libgcov.c, included only in gcov library
-LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
- _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
- _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
- _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
+##LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
+## _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
+## _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \
+## _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
+## _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
+## _gcov_merge_ior _gcov_indirect_call_profiler_v2
+
+LIBGCOV_MERGE = _gcov_merge_add _gcov_merge_single _gcov_merge_delta _gcov_merge_ior
+LIBGCOV_PROFILER = _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
_gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \
- _gcov_merge_ior _gcov_indirect_call_profiler_v2
+ _gcov_indirect_call_profiler_v2
+LIBGCOV_INTERFACE = _gcov_flush _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
+ _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump
+LIBGCOV_DRIVER = _gcov
-libgcov-objects = $(patsubst %,%$(objext),$(LIBGCOV))
+libgcov-merge-objects = $(patsubst %,%$(objext),$(LIBGCOV_MERGE))
+libgcov-profiler-objects = $(patsubst %,%$(objext),$(LIBGCOV_PROFILER))
+libgcov-interface-objects = $(patsubst %,%$(objext),$(LIBGCOV_INTERFACE))
+libgcov-driver-objects = $(patsubst %,%$(objext),$(LIBGCOV_DRIVER))
+libgcov-objects = $(libgcov-merge-objects) $(libgcov-profiler-objects) \
+ $(libgcov-interface-objects) $(libgcov-driver-objects)
-$(libgcov-objects): %$(objext): $(srcdir)/libgcov.c
- $(gcc_compile) -DL$* -c $(srcdir)/libgcov.c
+$(libgcov-merge-objects): %$(objext): $(srcdir)/libgcov-merge.c
+ $(gcc_compile) -DL$* -c $(srcdir)/libgcov-merge.c
+$(libgcov-profiler-objects): %$(objext): $(srcdir)/libgcov-profiler.c
+ $(gcc_compile) -DL$* -c $(srcdir)/libgcov-profiler.c
+$(libgcov-interface-objects): %$(objext): $(srcdir)/libgcov-interface.c
+ $(gcc_compile) -DL$* -c $(srcdir)/libgcov-interface.c
+$(libgcov-driver-objects): %$(objext): $(srcdir)/libgcov-driver.c \
+ $(srcdir)/libgcov-driver-system.c
+ $(gcc_compile) -DL$* -c $(srcdir)/libgcov-driver.c
# Static libraries.
===================================================================
@@ -27,7 +27,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
/* Routines declared in gcov-io.h. This file should be #included by
another source file, after having #included gcov-io.h. */
-#if !IN_GCOV
+#if (!IN_GCOV)
static void gcov_write_block (unsigned);
static gcov_unsigned_t *gcov_write_words (unsigned);
#endif
@@ -36,6 +36,10 @@ static const gcov_unsigned_t *gcov_read_words (uns
static void gcov_allocate (unsigned);
#endif
+#if IN_LIBGCOV >= 0
+/*GCOV_LINKAGE*/ struct gcov_var gcov_var ATTRIBUTE_HIDDEN;
+#endif
+
static inline gcov_unsigned_t from_file (gcov_unsigned_t value)
{
#if !IN_LIBGCOV
@@ -236,7 +240,9 @@ gcov_write_words (unsigned words)
{
gcov_unsigned_t *result;
+#ifndef PROFILE_TOOL
gcc_assert (gcov_var.mode < 0);
+#endif
#if IN_LIBGCOV
if (gcov_var.offset >= GCOV_BLOCK_SIZE)
{
@@ -503,7 +509,7 @@ gcov_read_counter (void)
buffer, or NULL on empty string. You must copy the string before
calling another gcov function. */
-#if !IN_LIBGCOV
+#if !IN_LIBGCOV || PROFILE_TOOL
GCOV_LINKAGE const char *
gcov_read_string (void)
{
@@ -580,7 +586,7 @@ gcov_read_summary (struct gcov_summary *summary)
}
}
-#if !IN_LIBGCOV
+#if !IN_LIBGCOV || PROFILE_TOOL
/* Reset to a known position. BASE should have been obtained from
gcov_position, LENGTH should be a record length. */
@@ -606,7 +612,9 @@ gcov_sync (gcov_position_t base, gcov_unsigned_t l
GCOV_LINKAGE void
gcov_seek (gcov_position_t base)
{
+#ifndef PROFILE_TOOL
gcc_assert (gcov_var.mode < 0);
+#endif
if (gcov_var.offset)
gcov_write_block (gcov_var.offset);
fseek (gcov_var.file, base << 2, SEEK_SET);
===================================================================
@@ -164,7 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect
#ifndef GCC_GCOV_IO_H
#define GCC_GCOV_IO_H
-#if IN_LIBGCOV
+#if (IN_LIBGCOV && !PROFILE_TOOL)
/* About the target */
#if BITS_PER_UNIT == 8
@@ -214,7 +214,7 @@ typedef unsigned gcov_type_unsigned __attribute__
typedef unsigned gcov_unsigned_t;
typedef unsigned gcov_position_t;
/* gcov_type is typedef'd elsewhere for the compiler */
-#if IN_GCOV
+#if IN_GCOV || PROfILE_TOOL
#define GCOV_LINKAGE static
typedef HOST_WIDEST_INT gcov_type;
typedef unsigned HOST_WIDEST_INT gcov_type_unsigned;
@@ -259,7 +259,9 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
/* Poison these, so they don't accidentally slip in. */
#pragma GCC poison gcov_write_string gcov_write_tag gcov_write_length
+#ifndef PROFILE_TOOL
#pragma GCC poison gcov_read_string gcov_sync gcov_time gcov_magic
+#endif
#ifdef HAVE_GAS_HIDDEN
#define ATTRIBUTE_HIDDEN __attribute__ ((__visibility__ ("hidden")))
@@ -467,7 +469,8 @@ struct gcov_fn_info
};
/* Type of function used to merge counters. */
-typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
+typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t,
+ gcov_type *, unsigned);
/* Information about a single object file. */
struct gcov_info
@@ -482,8 +485,12 @@ struct gcov_info
unused) */
unsigned n_functions; /* number of functions */
+#ifndef PROFILE_TOOL
const struct gcov_fn_info *const *functions; /* pointer to pointers
to function information */
+#else
+ const struct gcov_fn_info **functions;
+#endif
};
/* Register a new object file module. */
@@ -499,22 +506,28 @@ extern void __gcov_reset (void);
extern void __gcov_dump (void);
/* The merge function that just sums the counters. */
-extern void __gcov_merge_add (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+extern void __gcov_merge_add (gcov_type *, unsigned, gcov_type *,
+ unsigned) ATTRIBUTE_HIDDEN;
/* The merge function to choose the most common value. */
-extern void __gcov_merge_single (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+extern void __gcov_merge_single (gcov_type *, unsigned, gcov_type *,
+ unsigned) ATTRIBUTE_HIDDEN;
/* The merge function to choose the most common difference between
consecutive values. */
-extern void __gcov_merge_delta (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+extern void __gcov_merge_delta (gcov_type *, unsigned, gcov_type *,
+ unsigned) ATTRIBUTE_HIDDEN;
/* The merge function that just ors the counters together. */
-extern void __gcov_merge_ior (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+extern void __gcov_merge_ior (gcov_type *, unsigned, gcov_type *,
+ unsigned) ATTRIBUTE_HIDDEN;
/* The profiler functions. */
extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
+extern void __gcov_indirect_call_profiler (gcov_type*, gcov_type,
+ void*, void*);
extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *);
extern void __gcov_average_profiler (gcov_type *, gcov_type);
extern void __gcov_ior_profiler (gcov_type *, gcov_type);
@@ -538,7 +551,7 @@ extern int __gcov_execve (const char *, char *con
/* Optimum number of gcov_unsigned_t's read from or written to disk. */
#define GCOV_BLOCK_SIZE (1 << 10)
-GCOV_LINKAGE struct gcov_var
+struct gcov_var
{
FILE *file;
gcov_position_t start; /* Position of first byte of block */
@@ -560,7 +573,8 @@ extern int __gcov_execve (const char *, char *con
size_t alloc;
gcov_unsigned_t *buffer;
#endif
-} gcov_var ATTRIBUTE_HIDDEN;
+};
+extern struct gcov_var gcov_var ATTRIBUTE_HIDDEN;
/* Functions for reading and writing gcov files. In libgcov you can
open the file for reading then writing. Elsewhere you can open the
@@ -587,7 +601,7 @@ GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (v
GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN;
GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN;
-#if IN_LIBGCOV
+#if (IN_LIBGCOV)
/* Available only in libgcov */
GCOV_LINKAGE void gcov_write_counter (gcov_type) ATTRIBUTE_HIDDEN;
GCOV_LINKAGE void gcov_write_tag_length (gcov_unsigned_t, gcov_unsigned_t)
@@ -597,7 +611,9 @@ GCOV_LINKAGE void gcov_write_summary (gcov_unsigne
ATTRIBUTE_HIDDEN;
static void gcov_rewrite (void);
GCOV_LINKAGE void gcov_seek (gcov_position_t /*position*/) ATTRIBUTE_HIDDEN;
-#else
+#endif
+
+#if (!IN_LIBGCOV || PROFILE_TOOL)
/* Available outside libgcov */
GCOV_LINKAGE const char *gcov_read_string (void);
GCOV_LINKAGE void gcov_sync (gcov_position_t /*base*/,
===================================================================
@@ -123,7 +123,8 @@ SUBDIRS =@subdirs@ build
# Selection of languages to be made.
CONFIG_LANGUAGES = @all_selected_languages@
-LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) $(CONFIG_LANGUAGES)
+LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) profile-tool$(exeext) \
+ $(CONFIG_LANGUAGES)
# Default values for variables overridden in Makefile fragments.
# CFLAGS is for the user to override to, e.g., do a cross build with -O2.
@@ -196,6 +197,8 @@ GCC_WARN_CXXFLAGS = $(LOOSE_WARN) $($(@D)-warn) $(
# flex output may yield harmless "no previous prototype" warnings
build/gengtype-lex.o-warn = -Wno-error
gengtype-lex.o-warn = -Wno-error
+profile-tool.o-warn = -Wno-error
+libgcov-tool.o-warn = -Wno-error
# All warnings have to be shut off in stage1 if the compiler used then
# isn't gcc; configure determines that. WARN_CFLAGS will be either
@@ -1480,7 +1483,7 @@ ALL_HOST_FRONTEND_OBJS = $(foreach v,$(CONFIG_LANG
ALL_HOST_BACKEND_OBJS = $(GCC_OBJS) $(OBJS) $(OBJS-libcommon) \
$(OBJS-libcommon-target) @TREEBROWSER@ main.o c-family/cppspec.o \
$(COLLECT2_OBJS) $(EXTRA_GCC_OBJS) $(GCOV_OBJS) $(GCOV_DUMP_OBJS) \
- lto-wrapper.o
+ $(PROFILE_TOOL_OBJS) libgcov-tool.o lto-wrapper.o
# This lists all host object files, whether they are included in this
# compilation or not.
@@ -1505,6 +1508,7 @@ MOSTLYCLEANFILES = insn-flags.h insn-config.h insn
$(SPECS) collect2$(exeext) gcc-ar$(exeext) gcc-nm$(exeext) \
gcc-ranlib$(exeext) \
gcov-iov$(build_exeext) gcov$(exeext) gcov-dump$(exeext) \
+ profile-tool$(exeect) \
gengtype$(exeext) *.[0-9][0-9].* *.[si] *-checksum.c libbackend.a \
libcommon-target.a libcommon.a libgcc.mk
@@ -2560,6 +2564,16 @@ GCOV_DUMP_OBJS = gcov-dump.o
gcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS)
+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_DUMP_OBJS) \
$(LIBS) -o $@
+libgcov-tool.o: $(srcdir)/../libgcc/libgcov-tool.c gcov-io.c $(GCOV_IO_H) \
+ $(srcdir)/../libgcc/libgcov-driver.c $(srcdir)/../libgcc/libgcov-driver-system.c \
+ $(srcdir)/../libgcc/libgcov-merge.c \
+ $(SYSTEM_H) coretypes.h $(TM_H) $(CONFIG_H) version.h intl.h $(DIAGNOSTIC_H)
+ +$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -o $@ $<
+
+PROFILE_TOOL_OBJS = profile-tool.o libgcov-tool.o
+profile-tool$(exeext): $(PROFILE_TOOL_OBJS) $(LIBDEPS)
+ +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(PROFILE_TOOL_OBJS) \
+ $(LIBS) -o $@
#
# Build the include directories. The stamp files are stmp-* rather than
# s-* so that mostlyclean does not force the include directory to
@@ -3183,6 +3197,13 @@ install-common: native lang.install-common install
rm -f $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \
$(INSTALL_PROGRAM) gcov$(exeext) $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \
fi
+# Install profile-tool if it was compiled.
+ -if [ -f profile-tool$(exeext) ]; \
+ then \
+ rm -f $(DESTDIR)$(bindir)/$(PROFILE_TOOL_INSTALL_NAME)$(exeext); \
+ $(INSTALL_PROGRAM) \
+ profile-tool$(exeext) $(DESTDIR)$(bindir)/$(PROFILE_TOOL_INSTALL_NAME)$(exeext); \
+ fi
# Install the driver program as $(target_noncanonical)-gcc,
# $(target_noncanonical)-gcc-$(version), and also as gcc if native.
===================================================================
@@ -0,0 +1,340 @@
+/* GCC instrumentation plugin for ThreadSanitizer.
+ Copyright (C) 2011-2013 Free Software Foundation, Inc.
+ Contributed by Dmitry Vyukov <dvyukov@google.com>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 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 PROFILE_TOOL 1
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ftw.h>
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "intl.h"
+#include "diagnostic.h"
+#include "version.h"
+#include "gcov-io.h"
+
+extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int);
+extern int gcov_profile_normalize (struct gcov_info*, gcov_type);
+extern int gcov_profile_scale (struct gcov_info*, float);
+extern struct gcov_info* gcov_read_profile_dir (const char*, int);
+extern void gcov_exit (void);
+extern void set_gcov_list (struct gcov_info *);
+extern void gcov_tool_set_verbose (void);
+
+
+static int verbose;
+
+static void print_merge_usage_message (void);
+
+static int
+unlink_file (const char *name,
+ const struct stat *status ATTRIBUTE_UNUSED,
+ int type ATTRIBUTE_UNUSED,
+ struct FTW *ftwbuf ATTRIBUTE_UNUSED)
+{
+ int ret = remove (name);
+
+ if (ret)
+ {
+ fprintf (stderr, "Error in removing %s.\n", name);
+ exit (-1);
+ }
+
+ return ret;
+}
+
+static int
+unlink_dir (const char *path)
+{
+ return nftw(path, unlink_file, 64, FTW_DEPTH | FTW_PHYS);
+}
+
+#define COUNT_MAX_VAL 1000000
+static int
+profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2)
+{
+ char *pwd;
+ int ret;
+ struct gcov_info * d1_profile;
+ struct gcov_info * d2_profile;
+
+
+ d1_profile = gcov_read_profile_dir (d1, 0);
+ if (!d1_profile)
+ return 1;
+
+ if (d2)
+ {
+ d2_profile = gcov_read_profile_dir (d2, 0);
+ if (!d2_profile)
+ return 1;
+
+ /* the actual merge: we overwrite to d1_profile. */
+ ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2);
+
+ if (ret)
+ return ret;
+ }
+
+ /* output */
+ unlink_dir (out);
+ mkdir (out, 0755);
+ pwd = get_current_dir_name ();
+ gcc_assert (pwd);
+ ret = chdir (out);
+ gcc_assert (ret == 0);
+
+ set_gcov_list (d1_profile);
+ gcov_exit ();
+
+ ret = chdir (pwd);
+ free (pwd);
+ return 0;
+}
+
+static void
+print_merge_usage_message (void)
+{
+ puts ("\n Merge sub-command: merge coverage file contents.");
+ puts (" merge [options] <dir1> <dir2>");
+ puts (" -v, --verbose");
+ puts (" \t Verbose mode.");
+ puts (" -o <dir>, --output <dir>");
+ puts (" \t Output directory.");
+ puts (" -w <w1,w2>, --weight <w1,w2>");
+ puts (" \t Set weights (float point values).");
+}
+
+static const struct option merge_options[] =
+{
+ { "verbose", no_argument, NULL, 'v' },
+ { "output", required_argument, NULL, 'o' },
+ { "weight", required_argument, NULL, 'w' },
+ { 0, 0, 0, 0 }
+};
+
+static void
+merge_usage (void)
+{
+ puts ("Usage:");
+ print_merge_usage_message ();
+ exit (-1);
+}
+
+static int
+do_merge (int argc, char **argv)
+{
+ int opt;
+ int ret;
+ const char *output_dir = 0;
+ int w1 = 1, w2 = 1;
+
+ while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1)
+ {
+ switch (opt)
+ {
+ case 'v':
+ verbose = 1;
+ gcov_tool_set_verbose ();
+ break;
+ case 'o':
+ output_dir = optarg;
+ break;
+ case 'w':
+ sscanf (optarg, "%d,%d", &w1, &w2);
+ if (w1 < 0 || w2 < 0)
+ {
+ fprintf (stderr, "Weights need to be non-negative. \n");
+ exit (-1);
+ }
+ break;
+ default:
+ merge_usage ();
+ }
+ }
+
+ if (output_dir == NULL)
+ output_dir = "merged_profile";
+
+ if (argc - optind == 2)
+ ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2);
+ else
+ merge_usage ();
+
+ return ret;
+}
+
+#define COUNT_MAX_VAL 1000000
+static int
+profile_rewrite (const char *d1, const char *out, long long n_val, float scale)
+{
+ char *pwd;
+ int ret;
+ struct gcov_info * d1_profile;
+
+
+ d1_profile = gcov_read_profile_dir (d1, 0);
+ if (!d1_profile)
+ return 1;
+
+ /* output */
+ unlink_dir (out);
+ mkdir (out, 0755);
+ pwd = get_current_dir_name ();
+ gcc_assert (pwd);
+ ret = chdir (out);
+ gcc_assert (ret == 0);
+
+ if (n_val)
+ gcov_profile_normalize (d1_profile, (gcov_type) n_val);
+ else
+ gcov_profile_scale (d1_profile, scale);
+
+ set_gcov_list (d1_profile);
+ gcov_exit ();
+
+ ret = chdir (pwd);
+ free (pwd);
+ return 0;
+}
+
+static void
+print_rewrite_usage_message (void)
+{
+ puts ("\n Rewrite sub-command: rewrite coverage file contents.");
+ puts (" rewrite [options] <dir>");
+ puts (" -v, --verbose");
+ puts (" \t Verbose mode.");
+ puts (" -o <dir>, --output <dir>");
+ puts (" \t Output directory.");
+ puts (" -s <float>, --scale <float>");
+ puts (" \t Set weights (float point values).");
+ puts (" -n <long long>, --normalize <long long>");
+ puts (" \t Normalize the profile.");
+}
+
+static const struct option rewrite_options[] =
+{
+ { "verbose", no_argument, NULL, 'v' },
+ { "output", required_argument, NULL, 'o' },
+ { "scale", required_argument, NULL, 's' },
+ { "normalize", required_argument, NULL, 'n' },
+ { 0, 0, 0, 0 }
+};
+
+static void
+rewrite_usage (void)
+{
+ puts ("Usage:");
+ print_merge_usage_message ();
+ exit (-1);
+}
+
+static int
+do_rewrite (int argc, char **argv)
+{
+ int opt;
+ int ret;
+ const char *output_dir = 0;
+ long long normalize_val = 0;
+ float scale = 1.0;
+
+ while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1)
+ {
+ switch (opt)
+ {
+ case 'v':
+ verbose = 1;
+ gcov_tool_set_verbose ();
+ break;
+ case 'o':
+ output_dir = optarg;
+ break;
+ case 'n':
+ if (scale != 1.0)
+ {
+ fprintf (stderr, "Scaling cannot co-exist with normalization.\n");
+ scale = 1.0;
+ }
+ normalize_val = atoll (optarg);
+ break;
+ case 's':
+ sscanf (optarg, "%f", &scale);
+ if (scale < 0.0)
+ {
+ fprintf (stderr, "Scale needs to be non-negative. \n");
+ exit (-1);
+ }
+ if (normalize_val != 0)
+ {
+ fprintf (stderr, "Normalization cannot co-exist with scaling.\n");
+ normalize_val = 0;
+ }
+ break;
+ default:
+ rewrite_usage ();
+ }
+ }
+
+ if (output_dir == NULL)
+ output_dir = "rewrite_profile";
+
+ if (argc - optind == 1)
+ ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale);
+ else
+ rewrite_usage ();
+
+ return ret;
+}
+static void
+print_usage (const char* cmd)
+{
+ if (cmd)
+ printf ("Usage: %s <sub_command> [options]\n", cmd);
+ print_merge_usage_message ();
+ print_rewrite_usage_message ();
+}
+
+int
+main (int argc, char **argv)
+{
+ int ret = -1;
+ const char *sub_command;
+
+ if (argc < 2)
+ {
+ print_usage (argv[0]);
+ return ret;
+ }
+
+ sub_command = argv[1];
+
+ if (!strcmp (sub_command, "merge"))
+ ret = do_merge (argc - 1, argv + 1);
+ else if (!strcmp (sub_command, "rewrite"))
+ ret = do_rewrite (argc - 1, argv + 1);
+ else
+ print_usage (argv[0]);
+
+ return ret;
+}
+