Home | History | Annotate | Line # | Download | only in libgcc
libgcov-driver.c revision 1.6
      1 /* Routines required for instrumenting a program.  */
      2 /* Compile this one with gcc.  */
      3 /* Copyright (C) 1989-2020 Free Software Foundation, Inc.
      4 
      5 This file is part of GCC.
      6 
      7 GCC is free software; you can redistribute it and/or modify it under
      8 the terms of the GNU General Public License as published by the Free
      9 Software Foundation; either version 3, or (at your option) any later
     10 version.
     11 
     12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
     13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15 for more details.
     16 
     17 Under Section 7 of GPL version 3, you are granted additional
     18 permissions described in the GCC Runtime Library Exception, version
     19 3.1, as published by the Free Software Foundation.
     20 
     21 You should have received a copy of the GNU General Public License and
     22 a copy of the GCC Runtime Library Exception along with this program;
     23 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     24 <http://www.gnu.org/licenses/>.  */
     25 
     26 #include "libgcov.h"
     27 #include "gcov-io.h"
     28 
     29 #if defined(inhibit_libc)
     30 /* If libc and its header files are not available, provide dummy functions.  */
     31 
     32 #if defined(L_gcov)
     33 void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
     34 #endif
     35 
     36 #else /* inhibit_libc */
     37 
     38 #include <string.h>
     39 #if GCOV_LOCKED
     40 #include <fcntl.h>
     41 #include <errno.h>
     42 #include <sys/stat.h>
     43 #endif
     44 
     45 #ifdef L_gcov
     46 
     47 /* A utility function for outputting errors.  */
     48 static int gcov_error (const char *, ...);
     49 
     50 #if !IN_GCOV_TOOL
     51 static void gcov_error_exit (void);
     52 #endif
     53 
     54 #include "gcov-io.c"
     55 
     56 #define GCOV_PROF_PREFIX "libgcov profiling error:%s:"
     57 
     58 struct gcov_fn_buffer
     59 {
     60   struct gcov_fn_buffer *next;
     61   unsigned fn_ix;
     62   struct gcov_fn_info info;
     63   /* note gcov_fn_info ends in a trailing array.  */
     64 };
     65 
     66 struct gcov_summary_buffer
     67 {
     68   struct gcov_summary_buffer *next;
     69   struct gcov_summary summary;
     70 };
     71 
     72 /* A struct that bundles all the related information about the
     73    gcda filename.  */
     74 
     75 struct gcov_filename
     76 {
     77   char *filename;  /* filename buffer */
     78   int strip; /* leading chars to strip from filename */
     79   char *prefix; /* prefix string */
     80 };
     81 
     82 static struct gcov_fn_buffer *
     83 free_fn_data (const struct gcov_info *gi_ptr, struct gcov_fn_buffer *buffer,
     84               unsigned limit)
     85 {
     86   struct gcov_fn_buffer *next;
     87   unsigned ix, n_ctr = 0;
     88 
     89   if (!buffer)
     90     return 0;
     91   next = buffer->next;
     92 
     93   for (ix = 0; ix != limit; ix++)
     94     if (gi_ptr->merge[ix])
     95       free (buffer->info.ctrs[n_ctr++].values);
     96   free (buffer);
     97   return next;
     98 }
     99 
    100 static struct gcov_fn_buffer **
    101 buffer_fn_data (const char *filename, const struct gcov_info *gi_ptr,
    102                 struct gcov_fn_buffer **end_ptr, unsigned fn_ix)
    103 {
    104   unsigned n_ctrs = 0, ix = 0;
    105   struct gcov_fn_buffer *fn_buffer;
    106   unsigned len;
    107 
    108   for (ix = GCOV_COUNTERS; ix--;)
    109     if (gi_ptr->merge[ix])
    110       n_ctrs++;
    111 
    112   len = sizeof (*fn_buffer) + sizeof (fn_buffer->info.ctrs[0]) * n_ctrs;
    113   fn_buffer = (struct gcov_fn_buffer *) xmalloc (len);
    114 
    115   if (!fn_buffer)
    116     goto fail;
    117 
    118   fn_buffer->next = 0;
    119   fn_buffer->fn_ix = fn_ix;
    120   fn_buffer->info.ident = gcov_read_unsigned ();
    121   fn_buffer->info.lineno_checksum = gcov_read_unsigned ();
    122   fn_buffer->info.cfg_checksum = gcov_read_unsigned ();
    123 
    124   for (n_ctrs = ix = 0; ix != GCOV_COUNTERS; ix++)
    125     {
    126       gcov_unsigned_t length;
    127       gcov_type *values;
    128 
    129       if (!gi_ptr->merge[ix])
    130         continue;
    131 
    132       if (gcov_read_unsigned () != GCOV_TAG_FOR_COUNTER (ix))
    133         {
    134           len = 0;
    135           goto fail;
    136         }
    137 
    138       length = GCOV_TAG_COUNTER_NUM (gcov_read_unsigned ());
    139       len = length * sizeof (gcov_type);
    140       values = (gcov_type *) xmalloc (len);
    141       if (!values)
    142         goto fail;
    143 
    144       fn_buffer->info.ctrs[n_ctrs].num = length;
    145       fn_buffer->info.ctrs[n_ctrs].values = values;
    146 
    147       while (length--)
    148         *values++ = gcov_read_counter ();
    149       n_ctrs++;
    150     }
    151 
    152   *end_ptr = fn_buffer;
    153   return &fn_buffer->next;
    154 
    155 fail:
    156   gcov_error (GCOV_PROF_PREFIX "Function %u %s %u \n", filename, fn_ix,
    157               len ? "cannot allocate" : "counter mismatch", len ? len : ix);
    158 
    159   return (struct gcov_fn_buffer **)free_fn_data (gi_ptr, fn_buffer, ix);
    160 }
    161 
    162 /* Convert VERSION into a string description and return the it.
    163    BUFFER is used for storage of the string.  The code should be
    164    aligned wit gcov-iov.c.  */
    165 
    166 static char *
    167 gcov_version_string (char *buffer, char version[4])
    168 {
    169   if (version[0] < 'A' || version[0] > 'Z'
    170       || version[1] < '0' || version[1] > '9'
    171       || version[2] < '0' || version[2] > '9')
    172     sprintf (buffer, "(unknown)");
    173   else
    174     {
    175       unsigned major = 10 * (version[0] - 'A') + (version[1] - '0');
    176       unsigned minor = version[2] - '0';
    177       sprintf (buffer, "%u.%u (%s)", major, minor,
    178 	       version[3] == '*' ? "release" : "experimental");
    179     }
    180   return buffer;
    181 }
    182 
    183 /* Check if VERSION of the info block PTR matches libgcov one.
    184    Return 1 on success, or zero in case of versions mismatch.
    185    If FILENAME is not NULL, its value used for reporting purposes
    186    instead of value from the info block.  */
    187 
    188 static int
    189 gcov_version (struct gcov_info *ptr, gcov_unsigned_t version,
    190               const char *filename)
    191 {
    192   if (version != GCOV_VERSION)
    193     {
    194       char v[4], e[4];
    195       char version_string[128], expected_string[128];
    196 
    197       GCOV_UNSIGNED2STRING (v, version);
    198       GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
    199 
    200       gcov_error (GCOV_PROF_PREFIX "Version mismatch - expected %s (%.4s) "
    201 		  "got %s (%.4s)\n",
    202 		  filename? filename : ptr->filename,
    203 		  gcov_version_string (expected_string, e), e,
    204 		  gcov_version_string (version_string, v), v);
    205       return 0;
    206     }
    207   return 1;
    208 }
    209 
    210 /* buffer for the fn_data from another program.  */
    211 static struct gcov_fn_buffer *fn_buffer;
    212 
    213 /* Including system dependent components. */
    214 #include "libgcov-driver-system.c"
    215 
    216 /* Prune TOP N value COUNTERS.  It's needed in order to preserve
    217    reproducibility of builds.  */
    218 
    219 static void
    220 prune_topn_counter (gcov_type *counters, gcov_type all)
    221 {
    222   for (unsigned i = 0; i < GCOV_TOPN_VALUES; i++)
    223     if (counters[2 * i + 1] < all)
    224       {
    225 	counters[2 * i] = 0;
    226 	counters[2 * i + 1] = 0;
    227       }
    228 }
    229 
    230 /* Prune counters so that they are ready to store or merge.  */
    231 
    232 static void
    233 prune_counters (struct gcov_info *gi)
    234 {
    235   for (unsigned i = 0; i < gi->n_functions; i++)
    236     {
    237       const struct gcov_fn_info *gfi = gi->functions[i];
    238       const struct gcov_ctr_info *ci = gfi->ctrs;
    239 
    240       for (unsigned j = 0; j < GCOV_COUNTERS; j++)
    241 	{
    242 	  if (gi->merge[j] == NULL)
    243 	    continue;
    244 
    245 	  if (j == GCOV_COUNTER_V_TOPN || j == GCOV_COUNTER_V_INDIR)
    246 	    {
    247 	      gcc_assert (!(ci->num % GCOV_TOPN_VALUES_COUNTERS));
    248 	      for (unsigned k = 0; k < (ci->num / GCOV_TOPN_VALUES_COUNTERS);
    249 		   k++)
    250 		{
    251 		  gcov_type *counters
    252 		    = ci->values + (k * GCOV_TOPN_VALUES_COUNTERS);
    253 		  prune_topn_counter (counters + 1, *counters);
    254 		}
    255 	    }
    256 	  ci++;
    257 	}
    258     }
    259 }
    260 
    261 /* This function merges counters in GI_PTR to an existing gcda file.
    262    Return 0 on success.
    263    Return -1 on error. In this case, caller will goto read_fatal.  */
    264 
    265 static int
    266 merge_one_data (const char *filename,
    267 		struct gcov_info *gi_ptr,
    268 		struct gcov_summary *summary)
    269 {
    270   gcov_unsigned_t tag, length;
    271   unsigned t_ix;
    272   int f_ix = -1;
    273   int error = 0;
    274   struct gcov_fn_buffer **fn_tail = &fn_buffer;
    275 
    276   length = gcov_read_unsigned ();
    277   if (!gcov_version (gi_ptr, length, filename))
    278     return -1;
    279 
    280   length = gcov_read_unsigned ();
    281   if (length != gi_ptr->stamp)
    282     {
    283       /* Read from a different compilation.  Overwrite the file.  */
    284       gcov_error (GCOV_PROF_PREFIX "overwriting an existing profile data "
    285 		  "with a different timestamp\n", filename);
    286       return 0;
    287     }
    288 
    289   tag = gcov_read_unsigned ();
    290   if (tag != GCOV_TAG_OBJECT_SUMMARY)
    291     goto read_mismatch;
    292   length = gcov_read_unsigned ();
    293   gcc_assert (length > 0);
    294   gcov_read_summary (summary);
    295 
    296   tag = gcov_read_unsigned ();
    297   /* Merge execution counts for each function.  */
    298   for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
    299        f_ix++, tag = gcov_read_unsigned ())
    300     {
    301       const struct gcov_ctr_info *ci_ptr;
    302       const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
    303 
    304       if (tag != GCOV_TAG_FUNCTION)
    305         goto read_mismatch;
    306 
    307       length = gcov_read_unsigned ();
    308       if (!length)
    309         /* This function did not appear in the other program.
    310            We have nothing to merge.  */
    311         continue;
    312 
    313       if (length != GCOV_TAG_FUNCTION_LENGTH)
    314         goto read_mismatch;
    315 
    316       if (!gfi_ptr || gfi_ptr->key != gi_ptr)
    317         {
    318           /* This function appears in the other program.  We
    319              need to buffer the information in order to write
    320              it back out -- we'll be inserting data before
    321              this point, so cannot simply keep the data in the
    322              file.  */
    323           fn_tail = buffer_fn_data (filename, gi_ptr, fn_tail, f_ix);
    324           if (!fn_tail)
    325             goto read_mismatch;
    326           continue;
    327         }
    328 
    329       length = gcov_read_unsigned ();
    330       if (length != gfi_ptr->ident)
    331         goto read_mismatch;
    332 
    333       length = gcov_read_unsigned ();
    334       if (length != gfi_ptr->lineno_checksum)
    335         goto read_mismatch;
    336 
    337       length = gcov_read_unsigned ();
    338       if (length != gfi_ptr->cfg_checksum)
    339         goto read_mismatch;
    340 
    341       ci_ptr = gfi_ptr->ctrs;
    342       for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
    343         {
    344           gcov_merge_fn merge = gi_ptr->merge[t_ix];
    345 
    346           if (!merge)
    347             continue;
    348 
    349           tag = gcov_read_unsigned ();
    350           length = gcov_read_unsigned ();
    351           if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
    352               || length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num))
    353             goto read_mismatch;
    354           (*merge) (ci_ptr->values, ci_ptr->num);
    355           ci_ptr++;
    356         }
    357       if ((error = gcov_is_error ()))
    358         goto read_error;
    359     }
    360 
    361   if (tag)
    362     {
    363     read_mismatch:;
    364       gcov_error (GCOV_PROF_PREFIX "Merge mismatch for %s %u\n",
    365                   filename, f_ix >= 0 ? "function" : "summary",
    366                   f_ix < 0 ? -1 - f_ix : f_ix);
    367       return -1;
    368     }
    369   return 0;
    370 
    371 read_error:
    372   gcov_error (GCOV_PROF_PREFIX "%s merging\n", filename,
    373               error < 0 ? "Overflow": "Error");
    374   return -1;
    375 }
    376 
    377 /* Write counters in GI_PTR and the summary in PRG to a gcda file. In
    378    the case of appending to an existing file, SUMMARY_POS will be non-zero.
    379    We will write the file starting from SUMMAY_POS.  */
    380 
    381 static void
    382 write_one_data (const struct gcov_info *gi_ptr,
    383 		const struct gcov_summary *prg_p)
    384 {
    385   unsigned f_ix;
    386 
    387   gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
    388   gcov_write_unsigned (gi_ptr->stamp);
    389 
    390   /* Generate whole program statistics.  */
    391   gcov_write_summary (GCOV_TAG_OBJECT_SUMMARY, prg_p);
    392 
    393   /* Write execution counts for each function.  */
    394   for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
    395     {
    396       unsigned buffered = 0;
    397       const struct gcov_fn_info *gfi_ptr;
    398       const struct gcov_ctr_info *ci_ptr;
    399       gcov_unsigned_t length;
    400       unsigned t_ix;
    401 
    402       if (fn_buffer && fn_buffer->fn_ix == f_ix)
    403         {
    404           /* Buffered data from another program.  */
    405           buffered = 1;
    406           gfi_ptr = &fn_buffer->info;
    407           length = GCOV_TAG_FUNCTION_LENGTH;
    408         }
    409       else
    410         {
    411           gfi_ptr = gi_ptr->functions[f_ix];
    412           if (gfi_ptr && gfi_ptr->key == gi_ptr)
    413             length = GCOV_TAG_FUNCTION_LENGTH;
    414           else
    415                 length = 0;
    416         }
    417 
    418       gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
    419       if (!length)
    420         continue;
    421 
    422       gcov_write_unsigned (gfi_ptr->ident);
    423       gcov_write_unsigned (gfi_ptr->lineno_checksum);
    424       gcov_write_unsigned (gfi_ptr->cfg_checksum);
    425 
    426       ci_ptr = gfi_ptr->ctrs;
    427       for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
    428         {
    429           gcov_unsigned_t n_counts;
    430           gcov_type *c_ptr;
    431 
    432           if (!gi_ptr->merge[t_ix])
    433             continue;
    434 
    435           n_counts = ci_ptr->num;
    436           gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
    437                                  GCOV_TAG_COUNTER_LENGTH (n_counts));
    438           c_ptr = ci_ptr->values;
    439           while (n_counts--)
    440             gcov_write_counter (*c_ptr++);
    441           ci_ptr++;
    442         }
    443       if (buffered)
    444         fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
    445     }
    446 
    447   gcov_write_unsigned (0);
    448 }
    449 
    450 /* Helper function for merging summary.  */
    451 
    452 static void
    453 merge_summary (int run_counted, struct gcov_summary *summary,
    454 	      gcov_type run_max)
    455 {
    456   if (!run_counted)
    457     {
    458       summary->runs++;
    459       summary->sum_max += run_max;
    460     }
    461 }
    462 
    463 /* Dump the coverage counts for one gcov_info object. We merge with existing
    464    counts when possible, to avoid growing the .da files ad infinitum. We use
    465    this program's checksum to make sure we only accumulate whole program
    466    statistics to the correct summary. An object file might be embedded
    467    in two separate programs, and we must keep the two program
    468    summaries separate.  */
    469 
    470 static void
    471 dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf,
    472 	       unsigned run_counted, gcov_type run_max)
    473 {
    474   struct gcov_summary summary = {};
    475   int error;
    476   gcov_unsigned_t tag;
    477   fn_buffer = 0;
    478 
    479   /* Prune current counters before we merge them.  */
    480   prune_counters (gi_ptr);
    481 
    482   error = gcov_exit_open_gcda_file (gi_ptr, gf);
    483   if (error == -1)
    484     return;
    485 
    486   tag = gcov_read_unsigned ();
    487   if (tag)
    488     {
    489       /* Merge data from file.  */
    490       if (tag != GCOV_DATA_MAGIC)
    491         {
    492 	  gcov_error (GCOV_PROF_PREFIX "Not a gcov data file\n",
    493 		      gf->filename);
    494           goto read_fatal;
    495         }
    496       error = merge_one_data (gf->filename, gi_ptr, &summary);
    497       if (error == -1)
    498         goto read_fatal;
    499     }
    500 
    501   gcov_rewrite ();
    502 
    503   merge_summary (run_counted, &summary, run_max);
    504 
    505   write_one_data (gi_ptr, &summary);
    506   /* fall through */
    507 
    508 read_fatal:;
    509   while (fn_buffer)
    510     fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
    511 
    512   if ((error = gcov_close ()))
    513     gcov_error (error  < 0 ?
    514 		GCOV_PROF_PREFIX "Overflow writing\n" :
    515 		GCOV_PROF_PREFIX "Error writing\n",
    516                 gf->filename);
    517 }
    518 
    519 
    520 /* Dump all the coverage counts for the program. It first computes program
    521    summary and then traverses gcov_list list and dumps the gcov_info
    522    objects one by one.  */
    523 
    524 #if !IN_GCOV_TOOL
    525 static
    526 #endif
    527 void
    528 gcov_do_dump (struct gcov_info *list, int run_counted)
    529 {
    530   struct gcov_info *gi_ptr;
    531   struct gcov_filename gf;
    532 
    533   /* Compute run_max of this program run.  */
    534   gcov_type run_max = 0;
    535   for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next)
    536     for (unsigned f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
    537       {
    538 	const struct gcov_ctr_info *cinfo
    539 	  = &gi_ptr->functions[f_ix]->ctrs[GCOV_COUNTER_ARCS];
    540 
    541 	for (unsigned i = 0; i < cinfo->num; i++)
    542 	  if (run_max < cinfo->values[i])
    543 	    run_max = cinfo->values[i];
    544       }
    545 
    546   allocate_filename_struct (&gf);
    547 
    548   /* Now merge each file.  */
    549   for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next)
    550     {
    551       dump_one_gcov (gi_ptr, &gf, run_counted, run_max);
    552       free (gf.filename);
    553     }
    554 
    555   free (gf.prefix);
    556 }
    557 
    558 #if IN_GCOV_TOOL
    559 const char *
    560 __attribute__ ((unused))
    561 gcov_get_filename (struct gcov_info *list)
    562 {
    563   return list->filename;
    564 }
    565 #endif
    566 
    567 #if !IN_GCOV_TOOL
    568 void
    569 __gcov_dump_one (struct gcov_root *root)
    570 {
    571   if (root->dumped)
    572     return;
    573 
    574   gcov_do_dump (root->list, root->run_counted);
    575 
    576   root->dumped = 1;
    577   root->run_counted = 1;
    578 }
    579 
    580 /* Per-dynamic-object gcov state.  */
    581 struct gcov_root __gcov_root;
    582 
    583 /* Exactly one of these will be live in the process image.  */
    584 struct gcov_master __gcov_master =
    585   {GCOV_VERSION, 0};
    586 
    587 void
    588 __gcov_exit (void)
    589 {
    590   __gcov_dump_one (&__gcov_root);
    591   if (__gcov_root.next)
    592     __gcov_root.next->prev = __gcov_root.prev;
    593   if (__gcov_root.prev)
    594     __gcov_root.prev->next = __gcov_root.next;
    595   else
    596     __gcov_master.root = __gcov_root.next;
    597 
    598   gcov_error_exit ();
    599 }
    600 
    601 /* Add a new object file onto the bb chain.  Invoked automatically
    602   when running an object file's global ctors.  */
    603 
    604 void
    605 __gcov_init (struct gcov_info *info)
    606 {
    607   if (!info->version || !info->n_functions)
    608     return;
    609   if (gcov_version (info, info->version, 0))
    610     {
    611       if (!__gcov_root.list)
    612 	{
    613 	  /* Add to master list and at exit function.  */
    614 	  if (gcov_version (NULL, __gcov_master.version, "<master>"))
    615 	    {
    616 	      __gcov_root.next = __gcov_master.root;
    617 	      if (__gcov_master.root)
    618 		__gcov_master.root->prev = &__gcov_root;
    619 	      __gcov_master.root = &__gcov_root;
    620 	    }
    621 	}
    622 
    623       info->next = __gcov_root.list;
    624       __gcov_root.list = info;
    625     }
    626 }
    627 #endif /* !IN_GCOV_TOOL */
    628 #endif /* L_gcov */
    629 #endif /* inhibit_libc */
    630