#include "tconfig.h"
#include "tsystem.h"
#include "coretypes.h"
#include "tm.h"
#if defined(inhibit_libc)
#define IN_LIBGCOV (-1)
#else
#undef NULL
#include <stdio.h>
#define IN_LIBGCOV 1
#if defined(L_gcov)
#define GCOV_LINKAGE
#endif
#endif
#include "gcov-io.h"
#if defined(inhibit_libc)
#ifdef L_gcov
void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
void __gcov_flush (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
#ifdef L_gcov
#include "gcov-io.c"
static struct gcov_info *gcov_list;
static gcov_unsigned_t gcov_crc32;
static int
gcov_version (struct gcov_info *ptr, gcov_unsigned_t version)
{
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",
ptr->filename, e, v);
return 0;
}
return 1;
}
static void
gcov_exit (void)
{
struct gcov_info *gi_ptr;
struct gcov_summary this_program;
struct gcov_summary all;
struct gcov_ctr_summary *cs_ptr;
const struct gcov_ctr_info *ci_ptr;
unsigned t_ix;
gcov_unsigned_t c_num;
memset (&all, 0, sizeof (all));
memset (&this_program, 0, sizeof (this_program));
for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
{
ci_ptr = gi_ptr->counts;
for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
{
if (!((1 << t_ix) & gi_ptr->ctr_mask))
continue;
cs_ptr = &this_program.ctrs[t_ix];
cs_ptr->num += 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++;
}
}
for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
{
struct gcov_summary this_object;
struct gcov_summary object, program;
gcov_type *values[GCOV_COUNTERS];
const struct gcov_fn_info *fi_ptr;
unsigned fi_stride;
unsigned c_ix, f_ix, n_counts;
struct gcov_ctr_summary *cs_obj, *cs_tobj, *cs_prg, *cs_tprg, *cs_all;
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));
ci_ptr = gi_ptr->counts;
for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
{
if (!((1 << t_ix) & gi_ptr->ctr_mask))
continue;
cs_ptr = &this_object.ctrs[t_ix];
cs_ptr->num += 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++;
}
c_ix = 0;
for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
if ((1 << t_ix) & gi_ptr->ctr_mask)
{
values[c_ix] = gi_ptr->counts[c_ix].values;
c_ix++;
}
fi_stride = sizeof (struct gcov_fn_info) + c_ix * sizeof (unsigned);
if (__alignof__ (struct gcov_fn_info) > sizeof (unsigned))
{
fi_stride += __alignof__ (struct gcov_fn_info) - 1;
fi_stride &= ~(__alignof__ (struct gcov_fn_info) - 1);
}
if (!gcov_open (gi_ptr->filename))
{
fprintf (stderr, "profiling:%s:Cannot open\n", gi_ptr->filename);
continue;
}
tag = gcov_read_unsigned ();
if (tag)
{
if (tag != GCOV_DATA_MAGIC)
{
fprintf (stderr, "profiling:%s:Not a gcov data file\n",
gi_ptr->filename);
goto read_fatal;
}
length = gcov_read_unsigned ();
if (!gcov_version (gi_ptr, length))
goto read_fatal;
length = gcov_read_unsigned ();
if (length != gi_ptr->stamp)
goto rewrite;
for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
{
fi_ptr = (const struct gcov_fn_info *)
((const char *) gi_ptr->functions + f_ix * fi_stride);
tag = gcov_read_unsigned ();
length = gcov_read_unsigned ();
if (tag != GCOV_TAG_FUNCTION
|| length != GCOV_TAG_FUNCTION_LENGTH
|| gcov_read_unsigned () != fi_ptr->ident
|| gcov_read_unsigned () != fi_ptr->checksum)
{
read_mismatch:;
fprintf (stderr, "profiling:%s:Merge mismatch for %s\n",
gi_ptr->filename,
f_ix + 1 ? "function" : "summaries");
goto read_fatal;
}
c_ix = 0;
for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
{
gcov_merge_fn merge;
if (!((1 << t_ix) & gi_ptr->ctr_mask))
continue;
n_counts = fi_ptr->n_ctrs[c_ix];
merge = gi_ptr->counts[c_ix].merge;
tag = gcov_read_unsigned ();
length = gcov_read_unsigned ();
if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
|| length != GCOV_TAG_COUNTER_LENGTH (n_counts))
goto read_mismatch;
(*merge) (values[c_ix], n_counts);
values[c_ix] += n_counts;
c_ix++;
}
if ((error = gcov_is_error ()))
goto read_error;
}
f_ix = ~0u;
while (1)
{
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
|| (!is_program && tag != GCOV_TAG_OBJECT_SUMMARY))
goto read_mismatch;
gcov_read_summary (is_program ? &program : &object);
if ((error = gcov_is_error ()))
goto read_error;
if (is_program && program.checksum == gcov_crc32)
{
summary_pos = eof_pos;
goto rewrite;
}
}
}
goto rewrite;
read_error:;
fprintf (stderr, error < 0 ? "profiling:%s:Overflow merging\n"
: "profiling:%s:Error merging\n", gi_ptr->filename);
read_fatal:;
gcov_close ();
continue;
rewrite:;
gcov_rewrite ();
if (!summary_pos)
memset (&program, 0, sizeof (program));
f_ix = ~0u;
for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
{
cs_obj = &object.ctrs[t_ix];
cs_tobj = &this_object.ctrs[t_ix];
cs_prg = &program.ctrs[t_ix];
cs_tprg = &this_program.ctrs[t_ix];
cs_all = &all.ctrs[t_ix];
if ((1 << t_ix) & gi_ptr->ctr_mask)
{
if (!cs_obj->runs++)
cs_obj->num = cs_tobj->num;
else if (cs_obj->num != cs_tobj->num)
goto read_mismatch;
cs_obj->sum_all += cs_tobj->sum_all;
if (cs_obj->run_max < cs_tobj->run_max)
cs_obj->run_max = cs_tobj->run_max;
cs_obj->sum_max += cs_tobj->run_max;
if (!cs_prg->runs++)
cs_prg->num = cs_tprg->num;
else if (cs_prg->num != cs_tprg->num)
goto read_mismatch;
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;
}
else if (cs_obj->num || cs_prg->num)
goto read_mismatch;
if (!cs_all->runs && cs_prg->runs)
memcpy (cs_all, cs_prg, sizeof (*cs_all));
else if (!all.checksum
&& (!GCOV_LOCKED || cs_all->runs == cs_prg->runs)
&& 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
? "" : " or concurrent update without locking support");
all.checksum = ~0u;
}
}
c_ix = 0;
for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
if ((1 << t_ix) & gi_ptr->ctr_mask)
{
values[c_ix] = gi_ptr->counts[c_ix].values;
c_ix++;
}
program.checksum = gcov_crc32;
gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
gcov_write_unsigned (gi_ptr->stamp);
for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
{
fi_ptr = (const struct gcov_fn_info *)
((const char *) gi_ptr->functions + f_ix * fi_stride);
gcov_write_tag_length (GCOV_TAG_FUNCTION, GCOV_TAG_FUNCTION_LENGTH);
gcov_write_unsigned (fi_ptr->ident);
gcov_write_unsigned (fi_ptr->checksum);
c_ix = 0;
for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
{
gcov_type *c_ptr;
if (!((1 << t_ix) & gi_ptr->ctr_mask))
continue;
n_counts = fi_ptr->n_ctrs[c_ix];
gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
GCOV_TAG_COUNTER_LENGTH (n_counts));
c_ptr = values[c_ix];
while (n_counts--)
gcov_write_counter (*c_ptr++);
values[c_ix] = c_ptr;
c_ix++;
}
}
gcov_write_summary (GCOV_TAG_OBJECT_SUMMARY, &object);
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);
}
}
void
__gcov_init (struct gcov_info *info)
{
if (!info->version)
return;
if (gcov_version (info, info->version))
{
const char *ptr = info->filename;
gcov_unsigned_t crc32 = gcov_crc32;
do
{
unsigned ix;
gcov_unsigned_t value = *ptr << 24;
for (ix = 8; ix--; value <<= 1)
{
gcov_unsigned_t feedback;
feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0;
crc32 <<= 1;
crc32 ^= feedback;
}
}
while (*ptr++);
gcov_crc32 = crc32;
if (!gcov_list)
atexit (gcov_exit);
info->next = gcov_list;
gcov_list = info;
}
info->version = 0;
}
void
__gcov_flush (void)
{
const struct gcov_info *gi_ptr;
gcov_exit ();
for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
{
unsigned t_ix;
const struct gcov_ctr_info *ci_ptr;
for (t_ix = 0, ci_ptr = gi_ptr->counts; t_ix != GCOV_COUNTERS; t_ix++)
if ((1 << t_ix) & gi_ptr->ctr_mask)
{
memset (ci_ptr->values, 0, sizeof (gcov_type) * ci_ptr->num);
ci_ptr++;
}
}
}
#endif
#ifdef L_gcov_merge_add
void
__gcov_merge_add (gcov_type *counters, unsigned n_counters)
{
for (; n_counters; counters++, n_counters--)
*counters += gcov_read_counter ();
}
#endif
#ifdef L_gcov_merge_single
void
__gcov_merge_single (gcov_type *counters, unsigned n_counters)
{
unsigned i, n_measures;
gcov_type value, counter, all;
GCOV_CHECK (!(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
#ifdef L_gcov_merge_delta
void
__gcov_merge_delta (gcov_type *counters, unsigned n_counters)
{
unsigned i, n_measures;
gcov_type last, value, counter, all;
GCOV_CHECK (!(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
#ifdef L_gcov_fork
pid_t
__gcov_fork (void)
{
__gcov_flush ();
return fork ();
}
#endif
#ifdef L_gcov_execl
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 = 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
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 = 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
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 = 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
int
__gcov_execv (const char *path, char *const argv[])
{
__gcov_flush ();
return execv (path, argv);
}
#endif
#ifdef L_gcov_execvp
int
__gcov_execvp (const char *path, char *const argv[])
{
__gcov_flush ();
return execvp (path, argv);
}
#endif
#ifdef L_gcov_execve
int
__gcov_execve (const char *path, char *const argv[], char *const envp[])
{
__gcov_flush ();
return execve (path, argv, envp);
}
#endif
#endif