1  /* Routines required for instrumenting a program.  */
       2  /* Compile this one with gcc.  */
       3  /* Copyright (C) 1989-2023 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  #if !defined(inhibit_libc)
      28  
      29  #ifdef L_gcov_interval_profiler
      30  /* If VALUE is in interval <START, START + STEPS - 1>, then increases the
      31     corresponding counter in COUNTERS.  If the VALUE is above or below
      32     the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased
      33     instead.  */
      34  
      35  void
      36  __gcov_interval_profiler (gcov_type *counters, gcov_type value,
      37                            int start, unsigned steps)
      38  {
      39    gcov_type delta = value - start;
      40    if (delta < 0)
      41      counters[steps + 1]++;
      42    else if (delta >= steps)
      43      counters[steps]++;
      44    else
      45      counters[delta]++;
      46  }
      47  #endif
      48  
      49  #if defined(L_gcov_interval_profiler_atomic) && GCOV_SUPPORTS_ATOMIC
      50  /* If VALUE is in interval <START, START + STEPS - 1>, then increases the
      51     corresponding counter in COUNTERS.  If the VALUE is above or below
      52     the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased
      53     instead.  Function is thread-safe.  */
      54  
      55  void
      56  __gcov_interval_profiler_atomic (gcov_type *counters, gcov_type value,
      57  				 int start, unsigned steps)
      58  {
      59    gcov_type delta = value - start;
      60    if (delta < 0)
      61      __atomic_fetch_add (&counters[steps + 1], 1, __ATOMIC_RELAXED);
      62    else if (delta >= steps)
      63      __atomic_fetch_add (&counters[steps], 1, __ATOMIC_RELAXED);
      64    else
      65      __atomic_fetch_add (&counters[delta], 1, __ATOMIC_RELAXED);
      66  }
      67  #endif
      68  
      69  #ifdef L_gcov_pow2_profiler
      70  /* If VALUE is a power of two, COUNTERS[1] is incremented.  Otherwise
      71     COUNTERS[0] is incremented.  */
      72  
      73  void
      74  __gcov_pow2_profiler (gcov_type *counters, gcov_type value)
      75  {
      76    if (value == 0 || (value & (value - 1)))
      77      counters[0]++;
      78    else
      79      counters[1]++;
      80  }
      81  #endif
      82  
      83  #if defined(L_gcov_pow2_profiler_atomic) && GCOV_SUPPORTS_ATOMIC
      84  /* If VALUE is a power of two, COUNTERS[1] is incremented.  Otherwise
      85     COUNTERS[0] is incremented.  Function is thread-safe.  */
      86  
      87  void
      88  __gcov_pow2_profiler_atomic (gcov_type *counters, gcov_type value)
      89  {
      90    if (value == 0 || (value & (value - 1)))
      91      __atomic_fetch_add (&counters[0], 1, __ATOMIC_RELAXED);
      92    else
      93      __atomic_fetch_add (&counters[1], 1, __ATOMIC_RELAXED);
      94  }
      95  #endif
      96  
      97  /* Tries to determine N most commons value among its inputs.  */
      98  
      99  static inline void
     100  __gcov_topn_values_profiler_body (gcov_type *counters, gcov_type value,
     101  				  int use_atomic)
     102  {
     103    gcov_topn_add_value (counters, value, 1, use_atomic, 1);
     104  }
     105  
     106  #ifdef L_gcov_topn_values_profiler
     107  void
     108  __gcov_topn_values_profiler (gcov_type *counters, gcov_type value)
     109  {
     110    __gcov_topn_values_profiler_body (counters, value, 0);
     111  }
     112  #endif
     113  
     114  #if defined(L_gcov_topn_values_profiler_atomic) && GCOV_SUPPORTS_ATOMIC
     115  
     116  /* Update one value profilers (COUNTERS) for a given VALUE.
     117  
     118     CAVEAT: Following function is not thread-safe, only total number
     119     of executions (COUNTERS[2]) is update with an atomic instruction.
     120     Problem is that one cannot atomically update two counters
     121     (COUNTERS[0] and COUNTERS[1]), for more information please read
     122     following email thread:
     123     https://gcc.gnu.org/ml/gcc-patches/2016-08/msg00024.html.  */
     124  
     125  void
     126  __gcov_topn_values_profiler_atomic (gcov_type *counters, gcov_type value)
     127  {
     128    __gcov_topn_values_profiler_body (counters, value, 1);
     129  }
     130  #endif
     131  
     132  #ifdef L_gcov_indirect_call_profiler_v4
     133  
     134  /* These two variables are used to actually track caller and callee.  Keep
     135     them in TLS memory so races are not common (they are written to often).
     136     The variables are set directly by GCC instrumented code, so declaration
     137     here must match one in tree-profile.c  */
     138  
     139  #if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
     140  __thread
     141  #endif
     142  struct indirect_call_tuple __gcov_indirect_call;
     143  
     144  /* By default, the C++ compiler will use function addresses in the
     145     vtable entries.  Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero
     146     tells the compiler to use function descriptors instead.  The value
     147     of this macro says how many words wide the descriptor is (normally 2).
     148  
     149     It is assumed that the address of a function descriptor may be treated
     150     as a pointer to a function.  */
     151  
     152  /* Tries to determine the most common value among its inputs. */
     153  static inline void
     154  __gcov_indirect_call_profiler_body (gcov_type value, void *cur_func,
     155  				    int use_atomic)
     156  {
     157    /* If the C++ virtual tables contain function descriptors then one
     158       function may have multiple descriptors and we need to dereference
     159       the descriptors to see if they point to the same function.  */
     160    if (cur_func == __gcov_indirect_call.callee
     161        || (__LIBGCC_VTABLE_USES_DESCRIPTORS__
     162  	  && *(void **) cur_func == *(void **) __gcov_indirect_call.callee))
     163      __gcov_topn_values_profiler_body (__gcov_indirect_call.counters, value,
     164  				      use_atomic);
     165  
     166    __gcov_indirect_call.callee = NULL;
     167  }
     168  
     169  void
     170  __gcov_indirect_call_profiler_v4 (gcov_type value, void *cur_func)
     171  {
     172    __gcov_indirect_call_profiler_body (value, cur_func, 0);
     173  }
     174  
     175  #if GCOV_SUPPORTS_ATOMIC
     176  void
     177  __gcov_indirect_call_profiler_v4_atomic (gcov_type value, void *cur_func)
     178  {
     179    __gcov_indirect_call_profiler_body (value, cur_func, 1);
     180  }
     181  #endif
     182  
     183  #endif
     184  
     185  #ifdef L_gcov_time_profiler
     186  
     187  /* Counter for first visit of each function.  */
     188  gcov_type __gcov_time_profiler_counter ATTRIBUTE_HIDDEN;
     189  
     190  #endif
     191  
     192  #ifdef L_gcov_average_profiler
     193  /* Increase corresponding COUNTER by VALUE.  FIXME: Perhaps we want
     194     to saturate up.  */
     195  
     196  void
     197  __gcov_average_profiler (gcov_type *counters, gcov_type value)
     198  {
     199    counters[0] += value;
     200    counters[1] ++;
     201  }
     202  #endif
     203  
     204  #if defined(L_gcov_average_profiler_atomic) && GCOV_SUPPORTS_ATOMIC
     205  /* Increase corresponding COUNTER by VALUE.  FIXME: Perhaps we want
     206     to saturate up.  Function is thread-safe.  */
     207  
     208  void
     209  __gcov_average_profiler_atomic (gcov_type *counters, gcov_type value)
     210  {
     211    __atomic_fetch_add (&counters[0], value, __ATOMIC_RELAXED);
     212    __atomic_fetch_add (&counters[1], 1, __ATOMIC_RELAXED);
     213  }
     214  #endif
     215  
     216  #ifdef L_gcov_ior_profiler
     217  /* Bitwise-OR VALUE into COUNTER.  */
     218  
     219  void
     220  __gcov_ior_profiler (gcov_type *counters, gcov_type value)
     221  {
     222    *counters |= value;
     223  }
     224  #endif
     225  
     226  #if defined(L_gcov_ior_profiler_atomic) && GCOV_SUPPORTS_ATOMIC
     227  /* Bitwise-OR VALUE into COUNTER.  Function is thread-safe.  */
     228  
     229  void
     230  __gcov_ior_profiler_atomic (gcov_type *counters, gcov_type value)
     231  {
     232    __atomic_fetch_or (&counters[0], value, __ATOMIC_RELAXED);
     233  }
     234  #endif
     235  
     236  
     237  #endif /* inhibit_libc */