aboutsummaryrefslogtreecommitdiff
path: root/contrib/gcc/libgcov.c
diff options
context:
space:
mode:
authorAlexander Kabaev <kan@FreeBSD.org>2007-05-19 01:19:51 +0000
committerAlexander Kabaev <kan@FreeBSD.org>2007-05-19 01:19:51 +0000
commit6b834ef156bcf24dcf0e281f57ee5bde03ca07cf (patch)
tree0cb530c9c38af219e6dda2994c078b6b2b9ad853 /contrib/gcc/libgcov.c
parent9ba78bf6b1135ae200742b2a97ae5bc71c9fd265 (diff)
downloadsrc-6b834ef156bcf24dcf0e281f57ee5bde03ca07cf.tar.gz
src-6b834ef156bcf24dcf0e281f57ee5bde03ca07cf.zip
GCC 4.2.0 release.
Notes
Notes: svn path=/vendor/gcc/dist/; revision=169689
Diffstat (limited to 'contrib/gcc/libgcov.c')
-rw-r--r--contrib/gcc/libgcov.c395
1 files changed, 355 insertions, 40 deletions
diff --git a/contrib/gcc/libgcov.c b/contrib/gcc/libgcov.c
index d5c97a2854d6..494759e6bed1 100644
--- a/contrib/gcc/libgcov.c
+++ b/contrib/gcc/libgcov.c
@@ -1,7 +1,7 @@
/* Routines required for instrumenting a program. */
/* Compile this one with gcc. */
/* Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
This file is part of GCC.
@@ -26,12 +26,8 @@ for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
-
-/* It is incorrect to include config.h here, because this file is being
- compiled for the target, and hence definitions concerning only the host
- do not apply. */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA. */
#include "tconfig.h"
#include "tsystem.h"
@@ -92,8 +88,51 @@ static struct gcov_info *gcov_list;
object file included in multiple programs. */
static gcov_unsigned_t gcov_crc32;
+/* Size of the longest file name. */
+static size_t gcov_max_filename = 0;
+
+#ifdef TARGET_POSIX_IO
+/* 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)
+{
+ char *s;
+
+ for (s = filename + 1; *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
+ && mkdir (filename, 0755) == -1
+ /* 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
+
+/* 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)
+gcov_version (struct gcov_info *ptr, gcov_unsigned_t version,
+ const char *filename)
{
if (version != GCOV_VERSION)
{
@@ -104,7 +143,7 @@ gcov_version (struct gcov_info *ptr, gcov_unsigned_t version)
fprintf (stderr,
"profiling:%s:Version mismatch - expected %.4s got %.4s\n",
- ptr->filename, e, v);
+ filename? filename : ptr->filename, e, v);
return 0;
}
return 1;
@@ -127,6 +166,10 @@ gcov_exit (void)
const struct gcov_ctr_info *ci_ptr;
unsigned t_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;
memset (&all, 0, sizeof (all));
/* Find the totals for this execution. */
@@ -151,6 +194,35 @@ gcov_exit (void)
}
}
+ /* Get file name relocation prefix. Non-absolute values are ignored. */
+ gcov_prefix = getenv("GCOV_PREFIX");
+ if (gcov_prefix && IS_ABSOLUTE_PATH (gcov_prefix))
+ {
+ /* 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;
+ }
+
+ prefix_length = strlen(gcov_prefix);
+
+ /* Remove an unnecessary trailing '/' */
+ if (IS_DIR_SEPARATOR (gcov_prefix[prefix_length - 1]))
+ prefix_length--;
+ }
+ else
+ prefix_length = 0;
+
+ /* Allocate and initialize the filename scratch space. */
+ gi_filename = (char *) alloca (prefix_length + gcov_max_filename + 1);
+ 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)
{
@@ -164,10 +236,33 @@ gcov_exit (void)
int error = 0;
gcov_unsigned_t tag, length;
gcov_position_t summary_pos = 0;
+ gcov_position_t eof_pos = 0;
memset (&this_object, 0, sizeof (this_object));
memset (&object, 0, sizeof (object));
+ /* Build relocated filename, stripping off leading
+ directories from the initial filename if requested. */
+ if (gcov_prefix_strip > 0)
+ {
+ int level = 0;
+ const char *fname = gi_ptr->filename;
+ const char *s;
+
+ /* Skip selected directory levels. */
+ for (s = fname + 1; (*s != '\0') && (level < gcov_prefix_strip); s++)
+ if (IS_DIR_SEPARATOR(*s))
+ {
+ fname = s;
+ level++;
+ };
+
+ /* Update complete filename with stripped original. */
+ strcpy (gi_filename_up, fname);
+ }
+ else
+ strcpy (gi_filename_up, gi_ptr->filename);
+
/* Totals for this object file. */
ci_ptr = gi_ptr->counts;
for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
@@ -204,10 +299,22 @@ gcov_exit (void)
fi_stride &= ~(__alignof__ (struct gcov_fn_info) - 1);
}
- if (!gcov_open (gi_ptr->filename))
+ if (!gcov_open (gi_filename))
{
- fprintf (stderr, "profiling:%s:Cannot open\n", gi_ptr->filename);
- continue;
+#ifdef TARGET_POSIX_IO
+ /* 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;
+ }
+#endif
+ if (!gcov_open (gi_filename))
+ {
+ fprintf (stderr, "profiling:%s:Cannot open\n", gi_filename);
+ continue;
+ }
}
tag = gcov_read_unsigned ();
@@ -217,23 +324,17 @@ gcov_exit (void)
if (tag != GCOV_DATA_MAGIC)
{
fprintf (stderr, "profiling:%s:Not a gcov data file\n",
- gi_ptr->filename);
- read_fatal:;
- gcov_close ();
- continue;
+ gi_filename);
+ goto read_fatal;
}
length = gcov_read_unsigned ();
- if (!gcov_version (gi_ptr, length))
+ 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. */
- gcov_truncate ();
- goto rewrite;
- }
+ /* Read from a different compilation. Overwrite the file. */
+ goto rewrite;
/* Merge execution counts for each function. */
for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
@@ -251,7 +352,7 @@ gcov_exit (void)
{
read_mismatch:;
fprintf (stderr, "profiling:%s:Merge mismatch for %s\n",
- gi_ptr->filename,
+ gi_filename,
f_ix + 1 ? "function" : "summaries");
goto read_fatal;
}
@@ -284,12 +385,13 @@ gcov_exit (void)
/* Check program & object summary */
while (1)
{
- gcov_position_t base = gcov_position ();
int is_program;
+ eof_pos = gcov_position ();
tag = gcov_read_unsigned ();
if (!tag)
break;
+
length = gcov_read_unsigned ();
is_program = tag == GCOV_TAG_PROGRAM_SUMMARY;
if (length != GCOV_TAG_SUMMARY_LENGTH
@@ -300,19 +402,21 @@ gcov_exit (void)
goto read_error;
if (is_program && program.checksum == gcov_crc32)
{
- summary_pos = base;
+ summary_pos = eof_pos;
goto rewrite;
}
}
}
+ goto rewrite;
- if (!gcov_is_eof ())
- {
- read_error:;
- fprintf (stderr, error < 0 ? "profiling:%s:Overflow merging\n"
- : "profiling:%s:Error merging\n", gi_ptr->filename);
- goto read_fatal;
- }
+ read_error:;
+ fprintf (stderr, error < 0 ? "profiling:%s:Overflow merging\n"
+ : "profiling:%s:Error merging\n", gi_filename);
+
+ read_fatal:;
+ gcov_close ();
+ continue;
+
rewrite:;
gcov_rewrite ();
if (!summary_pos)
@@ -358,7 +462,7 @@ gcov_exit (void)
&& memcmp (cs_all, cs_prg, sizeof (*cs_all)))
{
fprintf (stderr, "profiling:%s:Invocation mismatch - some data files may have been removed%s",
- gi_ptr->filename, GCOV_LOCKED
+ gi_filename, GCOV_LOCKED
? "" : " or concurrent update without locking support");
all.checksum = ~0u;
}
@@ -414,13 +518,16 @@ gcov_exit (void)
gcov_write_summary (GCOV_TAG_OBJECT_SUMMARY, &object);
/* Generate whole program statistics. */
- gcov_seek (summary_pos);
+ if (eof_pos)
+ gcov_seek (eof_pos);
gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &program);
+ if (!summary_pos)
+ gcov_write_unsigned (0);
if ((error = gcov_close ()))
fprintf (stderr, error < 0 ?
"profiling:%s:Overflow writing\n" :
"profiling:%s:Error writing\n",
- gi_ptr->filename);
+ gi_filename);
}
}
@@ -432,11 +539,16 @@ __gcov_init (struct gcov_info *info)
{
if (!info->version)
return;
- if (gcov_version (info, info->version))
+ if (gcov_version (info, info->version, 0))
{
const char *ptr = info->filename;
gcov_unsigned_t crc32 = gcov_crc32;
-
+ size_t filename_length = strlen(info->filename);
+
+ /* Refresh the longest file name information */
+ if (filename_length > gcov_max_filename)
+ gcov_max_filename = filename_length;
+
do
{
unsigned ix;
@@ -518,7 +630,7 @@ __gcov_merge_single (gcov_type *counters, unsigned n_counters)
unsigned i, n_measures;
gcov_type value, counter, all;
- GCOV_CHECK (!(n_counters % 3));
+ gcc_assert (!(n_counters % 3));
n_measures = n_counters / 3;
for (i = 0; i < n_measures; i++, counters += 3)
{
@@ -557,7 +669,7 @@ __gcov_merge_delta (gcov_type *counters, unsigned n_counters)
unsigned i, n_measures;
gcov_type last, value, counter, all;
- GCOV_CHECK (!(n_counters % 4));
+ gcc_assert (!(n_counters % 4));
n_measures = n_counters / 4;
for (i = 0; i < n_measures; i++, counters += 4)
{
@@ -580,4 +692,207 @@ __gcov_merge_delta (gcov_type *counters, unsigned n_counters)
}
#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
+
+#ifdef L_gcov_one_value_profiler
+/* 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. */
+
+void
+__gcov_one_value_profiler (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]++;
+}
+#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)
+{
+ __gcov_flush ();
+ return fork ();
+}
+#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, const 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] = (char *) 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, const 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] = (char *) 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, const 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] = (char *) 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 */