/* Routines required for instrumenting a program. */
/* Compile this one with gcc. */
-/* Copyright (C) 1989-2014 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2019 Free Software Foundation, Inc.
This file is part of GCC.
<http://www.gnu.org/licenses/>. */
#include "libgcov.h"
+#include "gcov-io.h"
#if defined(inhibit_libc)
/* If libc and its header files are not available, provide dummy functions. */
#ifdef L_gcov
-/* A utility function for outputing errors. */
+/* A utility function for outputting errors. */
static int gcov_error (const char *, ...);
+#if !IN_GCOV_TOOL
+static void gcov_error_exit (void);
+#endif
+
#include "gcov-io.c"
+#define GCOV_PROF_PREFIX "libgcov profiling error:%s:"
+
struct gcov_fn_buffer
{
struct gcov_fn_buffer *next;
struct gcov_filename
{
char *filename; /* filename buffer */
- size_t max_length; /* maximum filename length */
int strip; /* leading chars to strip from filename */
- size_t prefix; /* chars to prepend to filename */
+ char *prefix; /* prefix string */
};
-/* Chain of per-object gcov structures. */
-#ifndef IN_GCOV_TOOL
-/* We need to expose this static variable when compiling for gcov-tool. */
-static
-#endif
-struct gcov_info *gcov_list;
-
-/* Flag when the profile has already been dumped via __gcov_dump(). */
-static int gcov_dump_complete;
-
static struct gcov_fn_buffer *
free_fn_data (const struct gcov_info *gi_ptr, struct gcov_fn_buffer *buffer,
unsigned limit)
return &fn_buffer->next;
fail:
- gcov_error ("profiling:%s:Function %u %s %u \n", filename, fn_ix,
+ gcov_error (GCOV_PROF_PREFIX "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 */
+/* Convert VERSION into a string description and return the it.
+ BUFFER is used for storage of the string. The code should be
+ aligned wit gcov-iov.c. */
-static gcov_unsigned_t
-crc32_unsigned (gcov_unsigned_t crc32, gcov_unsigned_t value)
+static char *
+gcov_version_string (char *buffer, char version[4])
{
- unsigned ix;
-
- for (ix = 32; ix--; value <<= 1)
+ if (version[0] < 'A' || version[0] > 'Z'
+ || version[1] < '0' || version[1] > '9'
+ || version[2] < '0' || version[2] > '9')
+ sprintf (buffer, "(unknown)");
+ else
{
- unsigned feedback;
-
- feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0;
- crc32 <<= 1;
- crc32 ^= feedback;
+ unsigned major = 10 * (version[0] - 'A') + (version[1] - '0');
+ unsigned minor = version[2] - '0';
+ sprintf (buffer, "%u.%u (%s)", major, minor,
+ version[3] == '*' ? "release" : "experimental");
}
-
- return crc32;
+ return buffer;
}
/* Check if VERSION of the info block PTR matches libgcov one.
if (version != GCOV_VERSION)
{
char v[4], e[4];
+ char version_string[128], expected_string[128];
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);
+ gcov_error (GCOV_PROF_PREFIX "Version mismatch - expected %s (%.4s) "
+ "got %s (%.4s)\n",
+ filename? filename : ptr->filename,
+ gcov_version_string (expected_string, e), e,
+ gcov_version_string (version_string, v), 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]);
- }
- }
-}
-
/* 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;
-/* If application calls fork or exec multiple times, we end up storing
- profile repeadely. We should not account this as multiple runs or
- functions executed once may mistakely become cold. */
-static int run_accounted = 0;
-
-/* This function computes the program level summary and the histo-gram.
- It computes and returns CRC32 and stored summary in THIS_PRG.
- Also determines the longest filename length of the info files. */
-
-static gcov_unsigned_t
-compute_summary (struct gcov_info *list, struct gcov_summary *this_prg,
- size_t *max_length)
-{
- 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;
- gcov_unsigned_t crc32 = 0;
-
- /* Find the totals for this execution. */
- memset (this_prg, 0, sizeof (*this_prg));
- *max_length = 0;
- for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next)
- {
- size_t len = strlen (gi_ptr->filename);
- if (len > *max_length)
- *max_length = len;
-
- 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);
- return crc32;
-}
/* Including system dependent components. */
#include "libgcov-driver-system.c"
static int
merge_one_data (const char *filename,
struct gcov_info *gi_ptr,
- struct gcov_summary *prg_p,
- struct gcov_summary *this_prg,
- gcov_position_t *summary_pos_p,
- gcov_position_t *eof_pos_p,
- gcov_unsigned_t crc32)
+ struct gcov_summary *summary)
{
gcov_unsigned_t tag, length;
unsigned t_ix;
- int f_ix;
+ int f_ix = -1;
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, filename))
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:;
+ /* Read from a different compilation. Overwrite the file. */
+ gcov_error (GCOV_PROF_PREFIX "overwriting an existing profile data "
+ "with a different timestamp\n", filename);
+ return 0;
}
+ tag = gcov_read_unsigned ();
+ if (tag != GCOV_TAG_OBJECT_SUMMARY)
+ goto read_mismatch;
+ length = gcov_read_unsigned ();
+ gcc_assert (length > 0);
+ gcov_read_summary (summary);
+
+ tag = gcov_read_unsigned ();
/* Merge execution counts for each function. */
for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
f_ix++, tag = gcov_read_unsigned ())
if (tag)
{
read_mismatch:;
- gcov_error ("profiling:%s:Merge mismatch for %s %u\n",
+ gcov_error (GCOV_PROF_PREFIX "Merge mismatch for %s %u\n",
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", filename,
+ gcov_error (GCOV_PROF_PREFIX "%s merging\n", filename,
error < 0 ? "Overflow": "Error");
return -1;
}
static void
write_one_data (const struct gcov_info *gi_ptr,
- const struct gcov_summary *prg_p,
- const gcov_position_t eof_pos,
- const gcov_position_t summary_pos)
+ const struct gcov_summary *prg_p)
{
unsigned f_ix;
- struct gcov_summary_buffer *next_sum_buffer;
-
- /* 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);
+ gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
+ gcov_write_unsigned (gi_ptr->stamp);
/* 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;
- }
+ gcov_write_summary (GCOV_TAG_OBJECT_SUMMARY, prg_p);
/* Write execution counts for each function. */
for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
gcov_write_unsigned (0);
}
-/* Helper function for merging summary.
- Return -1 on error. Return 0 on success. */
+/* Helper function for merging summary. */
-static int
-merge_summary (const char *filename, int run_counted,
- const struct gcov_info *gi_ptr, struct gcov_summary *prg,
- struct gcov_summary *this_prg, gcov_unsigned_t crc32,
- struct gcov_summary *all_prg __attribute__ ((unused)))
+static void
+merge_summary (int run_counted, struct gcov_summary *summary,
+ gcov_type run_max)
{
- struct gcov_ctr_summary *cs_prg, *cs_tprg;
- unsigned t_ix;
-#if !GCOV_LOCKED
- /* summary for all instances of program. */
- struct gcov_ctr_summary *cs_all;
-#endif
-
- /* Merge the summaries. */
- for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
+ if (!run_counted)
{
- cs_prg = &(prg->ctrs[t_ix]);
- cs_tprg = &(this_prg->ctrs[t_ix]);
-
- if (gi_ptr->merge[t_ix])
- {
- int first = !cs_prg->runs;
-
- if (!run_counted)
- cs_prg->runs++;
- if (first)
- 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 (first)
- 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",
- filename);
- return -1;
- }
-#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", filename);
- all_prg->checksum = ~0u;
- }
-#endif
+ summary->runs++;
+ summary->sum_max += run_max;
}
-
- prg->checksum = crc32;
-
- return 0;
}
/* Dump the coverage counts for one gcov_info object. We merge with existing
static void
dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf,
- unsigned run_counted,
- gcov_unsigned_t crc32, struct gcov_summary *all_prg,
- struct gcov_summary *this_prg)
+ unsigned run_counted, gcov_type run_max)
{
- struct gcov_summary prg; /* summary for this object over all program. */
+ struct gcov_summary summary = {};
int error;
gcov_unsigned_t tag;
- gcov_position_t summary_pos = 0;
- gcov_position_t eof_pos = 0;
fn_buffer = 0;
- sum_buffer = 0;
error = gcov_exit_open_gcda_file (gi_ptr, gf);
if (error == -1)
/* Merge data from file. */
if (tag != GCOV_DATA_MAGIC)
{
- gcov_error ("profiling:%s:Not a gcov data file\n", gf->filename);
+ gcov_error (GCOV_PROF_PREFIX "Not a gcov data file\n",
+ gf->filename);
goto read_fatal;
}
- error = merge_one_data (gf->filename, gi_ptr, &prg, this_prg,
- &summary_pos, &eof_pos, crc32);
+ error = merge_one_data (gf->filename, gi_ptr, &summary);
if (error == -1)
goto read_fatal;
}
gcov_rewrite ();
- if (!summary_pos)
- {
- memset (&prg, 0, sizeof (prg));
- summary_pos = eof_pos;
- }
-
- error = merge_summary (gf->filename, run_counted, gi_ptr, &prg, this_prg,
- crc32, all_prg);
- if (error == -1)
- goto read_fatal;
+ merge_summary (run_counted, &summary, run_max);
- write_one_data (gi_ptr, &prg, eof_pos, summary_pos);
+ write_one_data (gi_ptr, &summary);
/* fall through */
read_fatal:;
if ((error = gcov_close ()))
gcov_error (error < 0 ?
- "profiling:%s:Overflow writing\n" :
- "profiling:%s:Error writing\n",
+ GCOV_PROF_PREFIX "Overflow writing\n" :
+ GCOV_PROF_PREFIX "Error writing\n",
gf->filename);
}
summary and then traverses gcov_list list and dumps the gcov_info
objects one by one. */
-void ATTRIBUTE_HIDDEN
+#if !IN_GCOV_TOOL
+static
+#endif
+void
gcov_do_dump (struct gcov_info *list, int run_counted)
{
struct gcov_info *gi_ptr;
struct gcov_filename gf;
- gcov_unsigned_t crc32;
- struct gcov_summary all_prg;
- struct gcov_summary this_prg;
- crc32 = compute_summary (list, &this_prg, &gf.max_length);
+ /* Compute run_max of this program run. */
+ gcov_type run_max = 0;
+ for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next)
+ for (unsigned f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
+ {
+ const struct gcov_ctr_info *cinfo
+ = &gi_ptr->functions[f_ix]->ctrs[GCOV_COUNTER_ARCS];
+
+ for (unsigned i = 0; i < cinfo->num; i++)
+ if (run_max < cinfo->values[i])
+ run_max = cinfo->values[i];
+ }
allocate_filename_struct (&gf);
-#if !GCOV_LOCKED
- memset (&all_prg, 0, sizeof (all_prg));
-#endif
/* Now merge each file. */
for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next)
- dump_one_gcov (gi_ptr, &gf, run_counted, crc32, &all_prg, &this_prg);
+ {
+ dump_one_gcov (gi_ptr, &gf, run_counted, run_max);
+ free (gf.filename);
+ }
+
+ free (gf.prefix);
+}
- free (gf.filename);
+#if IN_GCOV_TOOL
+const char *
+__attribute__ ((unused))
+gcov_get_filename (struct gcov_info *list)
+{
+ return list->filename;
}
+#endif
+#if !IN_GCOV_TOOL
void
-gcov_exit (void)
+__gcov_dump_one (struct gcov_root *root)
{
- /* 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)
+ if (root->dumped)
return;
- gcov_dump_complete = 1;
-
- gcov_do_dump (gcov_list, run_accounted);
+ gcov_do_dump (root->list, root->run_counted);
- run_accounted = 1;
+ root->dumped = 1;
+ root->run_counted = 1;
}
-/* Reset all counters to zero. */
+/* Per-dynamic-object gcov state. */
+struct gcov_root __gcov_root;
+
+/* Exactly one of these will be live in the process image. */
+struct gcov_master __gcov_master =
+ {GCOV_VERSION, 0};
void
-gcov_clear (void)
+__gcov_exit (void)
{
- const struct gcov_info *gi_ptr;
-
- gcov_dump_complete = 0;
- 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++;
- }
- }
- }
+ __gcov_dump_one (&__gcov_root);
+ if (__gcov_root.next)
+ __gcov_root.next->prev = __gcov_root.prev;
+ if (__gcov_root.prev)
+ __gcov_root.prev->next = __gcov_root.next;
+ else
+ __gcov_master.root = __gcov_root.next;
+
+ gcov_error_exit ();
}
/* Add a new object file onto the bb chain. Invoked automatically
return;
if (gcov_version (info, info->version, 0))
{
- if (!gcov_list)
- atexit (gcov_exit);
-
- info->next = gcov_list;
- gcov_list = info;
+ if (!__gcov_root.list)
+ {
+ /* Add to master list and at exit function. */
+ if (gcov_version (NULL, __gcov_master.version, "<master>"))
+ {
+ __gcov_root.next = __gcov_master.root;
+ if (__gcov_master.root)
+ __gcov_master.root->prev = &__gcov_root;
+ __gcov_master.root = &__gcov_root;
+ }
+ }
+
+ info->next = __gcov_root.list;
+ __gcov_root.list = info;
}
- info->version = 0;
}
-
+#endif /* !IN_GCOV_TOOL */
#endif /* L_gcov */
#endif /* inhibit_libc */