Home | History | Annotate | Line # | Download | only in libgcc
libgcov-interface.c revision 1.4
      1 /* Routines required for instrumenting a program.  */
      2 /* Compile this one with gcc.  */
      3 /* Copyright (C) 1989-2018 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 "gthr.h"
     28 
     29 #if defined(inhibit_libc)
     30 
     31 #ifdef L_gcov_flush
     32 void __gcov_flush (void) {}
     33 #endif
     34 
     35 #ifdef L_gcov_reset
     36 void __gcov_reset (void) {}
     37 #endif
     38 
     39 #ifdef L_gcov_dump
     40 void __gcov_dump (void) {}
     41 #endif
     42 
     43 #else
     44 
     45 /* Some functions we want to bind in this dynamic object, but have an
     46    overridable global alias.  Unfortunately not all targets support
     47    aliases, so we just have a forwarding function.  That'll be tail
     48    called, so the cost is a single jump instruction.*/
     49 
     50 #define ALIAS_void_fn(src,dst) \
     51   void dst (void)	    \
     52   { src (); }
     53 
     54 extern __gthread_mutex_t __gcov_flush_mx ATTRIBUTE_HIDDEN;
     55 extern __gthread_mutex_t __gcov_flush_mx ATTRIBUTE_HIDDEN;
     56 
     57 #ifdef L_gcov_flush
     58 #ifdef __GTHREAD_MUTEX_INIT
     59 __gthread_mutex_t __gcov_flush_mx = __GTHREAD_MUTEX_INIT;
     60 #define init_mx_once()
     61 #else
     62 __gthread_mutex_t __gcov_flush_mx;
     63 
     64 static void
     65 init_mx (void)
     66 {
     67   __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
     68 }
     69 
     70 static void
     71 init_mx_once (void)
     72 {
     73   static __gthread_once_t once = __GTHREAD_ONCE_INIT;
     74   __gthread_once (&once, init_mx);
     75 }
     76 #endif
     77 
     78 /* Called before fork or exec - write out profile information gathered so
     79    far and reset it to zero.  This avoids duplication or loss of the
     80    profile information gathered so far.  */
     81 
     82 void
     83 __gcov_flush (void)
     84 {
     85   init_mx_once ();
     86   __gthread_mutex_lock (&__gcov_flush_mx);
     87 
     88   __gcov_dump_int ();
     89   __gcov_reset_int ();
     90 
     91   __gthread_mutex_unlock (&__gcov_flush_mx);
     92 }
     93 
     94 #endif /* L_gcov_flush */
     95 
     96 #ifdef L_gcov_reset
     97 
     98 /* Reset all counters to zero.  */
     99 
    100 static void
    101 gcov_clear (const struct gcov_info *list)
    102 {
    103   const struct gcov_info *gi_ptr;
    104 
    105   for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next)
    106     {
    107       unsigned f_ix;
    108 
    109       for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
    110         {
    111           unsigned t_ix;
    112           const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
    113 
    114           if (!gfi_ptr || gfi_ptr->key != gi_ptr)
    115             continue;
    116           const struct gcov_ctr_info *ci_ptr = gfi_ptr->ctrs;
    117           for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
    118             {
    119               if (!gi_ptr->merge[t_ix])
    120                 continue;
    121 
    122               memset (ci_ptr->values, 0, sizeof (gcov_type) * ci_ptr->num);
    123               ci_ptr++;
    124             }
    125         }
    126     }
    127 }
    128 
    129 /* Function that can be called from application to reset counters to zero,
    130    in order to collect profile in region of interest.  */
    131 
    132 void
    133 __gcov_reset_int (void)
    134 {
    135   struct gcov_root *root;
    136 
    137   /* If we're compatible with the master, iterate over everything,
    138      otherise just do us.  */
    139   for (root = __gcov_master.version == GCOV_VERSION
    140 	 ? __gcov_master.root : &__gcov_root; root; root = root->next)
    141     {
    142       gcov_clear (root->list);
    143       root->dumped = 0;
    144     }
    145 }
    146 
    147 ALIAS_void_fn (__gcov_reset_int, __gcov_reset);
    148 
    149 #endif /* L_gcov_reset */
    150 
    151 #ifdef L_gcov_dump
    152 /* Function that can be called from application to write profile collected
    153    so far, in order to collect profile in region of interest.  */
    154 
    155 void
    156 __gcov_dump_int (void)
    157 {
    158   struct gcov_root *root;
    159 
    160   /* If we're compatible with the master, iterate over everything,
    161      otherise just do us.  */
    162   for (root = __gcov_master.version == GCOV_VERSION
    163 	 ? __gcov_master.root : &__gcov_root; root; root = root->next)
    164     __gcov_dump_one (root);
    165 }
    166 
    167 ALIAS_void_fn (__gcov_dump_int, __gcov_dump);
    168 
    169 #endif /* L_gcov_dump */
    170 
    171 #ifdef L_gcov_fork
    172 /* A wrapper for the fork function.  Flushes the accumulated profiling data, so
    173    that they are not counted twice.  */
    174 
    175 pid_t
    176 __gcov_fork (void)
    177 {
    178   pid_t pid;
    179   __gcov_flush ();
    180   pid = fork ();
    181   if (pid == 0)
    182     __GTHREAD_MUTEX_INIT_FUNCTION (&__gcov_flush_mx);
    183   return pid;
    184 }
    185 #endif
    186 
    187 #ifdef L_gcov_execl
    188 /* A wrapper for the execl function.  Flushes the accumulated
    189    profiling data, so that they are not lost.  */
    190 
    191 int
    192 __gcov_execl (const char *path, char *arg, ...)
    193 {
    194   va_list ap, aq;
    195   unsigned i, length;
    196   char **args;
    197 
    198   __gcov_flush ();
    199 
    200   va_start (ap, arg);
    201   va_copy (aq, ap);
    202 
    203   length = 2;
    204   while (va_arg (ap, char *))
    205     length++;
    206   va_end (ap);
    207 
    208   args = (char **) alloca (length * sizeof (void *));
    209   args[0] = arg;
    210   for (i = 1; i < length; i++)
    211     args[i] = va_arg (aq, char *);
    212   va_end (aq);
    213 
    214   return execv (path, args);
    215 }
    216 #endif
    217 
    218 #ifdef L_gcov_execlp
    219 /* A wrapper for the execlp function.  Flushes the accumulated
    220    profiling data, so that they are not lost.  */
    221 
    222 int
    223 __gcov_execlp (const char *path, char *arg, ...)
    224 {
    225   va_list ap, aq;
    226   unsigned i, length;
    227   char **args;
    228 
    229   __gcov_flush ();
    230 
    231   va_start (ap, arg);
    232   va_copy (aq, ap);
    233 
    234   length = 2;
    235   while (va_arg (ap, char *))
    236     length++;
    237   va_end (ap);
    238 
    239   args = (char **) alloca (length * sizeof (void *));
    240   args[0] = arg;
    241   for (i = 1; i < length; i++)
    242     args[i] = va_arg (aq, char *);
    243   va_end (aq);
    244 
    245   return execvp (path, args);
    246 }
    247 #endif
    248 
    249 #ifdef L_gcov_execle
    250 /* A wrapper for the execle function.  Flushes the accumulated
    251    profiling data, so that they are not lost.  */
    252 
    253 int
    254 __gcov_execle (const char *path, char *arg, ...)
    255 {
    256   va_list ap, aq;
    257   unsigned i, length;
    258   char **args;
    259   char **envp;
    260 
    261   __gcov_flush ();
    262 
    263   va_start (ap, arg);
    264   va_copy (aq, ap);
    265 
    266   length = 2;
    267   while (va_arg (ap, char *))
    268     length++;
    269   va_end (ap);
    270 
    271   args = (char **) alloca (length * sizeof (void *));
    272   args[0] = arg;
    273   for (i = 1; i < length; i++)
    274     args[i] = va_arg (aq, char *);
    275   envp = va_arg (aq, char **);
    276   va_end (aq);
    277 
    278   return execve (path, args, envp);
    279 }
    280 #endif
    281 
    282 #ifdef L_gcov_execv
    283 /* A wrapper for the execv function.  Flushes the accumulated
    284    profiling data, so that they are not lost.  */
    285 
    286 int
    287 __gcov_execv (const char *path, char *const argv[])
    288 {
    289   __gcov_flush ();
    290   return execv (path, argv);
    291 }
    292 #endif
    293 
    294 #ifdef L_gcov_execvp
    295 /* A wrapper for the execvp function.  Flushes the accumulated
    296    profiling data, so that they are not lost.  */
    297 
    298 int
    299 __gcov_execvp (const char *path, char *const argv[])
    300 {
    301   __gcov_flush ();
    302   return execvp (path, argv);
    303 }
    304 #endif
    305 
    306 #ifdef L_gcov_execve
    307 /* A wrapper for the execve function.  Flushes the accumulated
    308    profiling data, so that they are not lost.  */
    309 
    310 int
    311 __gcov_execve (const char *path, char *const argv[], char *const envp[])
    312 {
    313   __gcov_flush ();
    314   return execve (path, argv, envp);
    315 }
    316 #endif
    317 #endif /* inhibit_libc */
    318