1/* Routines required for instrumenting a program. */ 2/* Compile this one with gcc. */ 3/* Copyright (C) 1989-2022 Free Software Foundation, Inc. 4 5This file is part of GCC. 6 7GCC is free software; you can redistribute it and/or modify it under 8the terms of the GNU General Public License as published by the Free 9Software Foundation; either version 3, or (at your option) any later 10version. 11 12GCC is distributed in the hope that it will be useful, but WITHOUT ANY 13WARRANTY; without even the implied warranty of MERCHANTABILITY or 14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15for more details. 16 17Under Section 7 of GPL version 3, you are granted additional 18permissions described in the GCC Runtime Library Exception, version 193.1, as published by the Free Software Foundation. 20 21You should have received a copy of the GNU General Public License and 22a copy of the GCC Runtime Library Exception along with this program; 23see 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 35void 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 55void 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 73void 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 87void 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 99static 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 107void 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 125void 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 142struct 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. */ 153static 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 169void 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 176void 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. */ 188gcov_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 196void 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 208void 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 219void 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 229void 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 */ 238