Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 1 | /* Routines required for instrumenting a program. */ |
| 2 | /* Compile this one with gcc. */ |
Thomas Koenig | b18a97e | 2021-09-13 19:49:49 +0200 | [diff] [blame] | 3 | /* Copyright (C) 1989-2021 Free Software Foundation, Inc. |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 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 | |
Rong Xu | 40d6b75 | 2014-01-08 16:37:08 +0000 | [diff] [blame] | 26 | #include "libgcov.h" |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 27 | #if !defined(inhibit_libc) |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 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 | |
Martin Liska | 7fe76f6 | 2016-09-06 16:13:21 +0200 | [diff] [blame] | 49 | #if defined(L_gcov_interval_profiler_atomic) && GCOV_SUPPORTS_ATOMIC |
Martin Liska | a266236 | 2016-08-10 15:14:56 +0200 | [diff] [blame] | 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) |
Thomas Preud'homme | 4d0cdd0 | 2016-10-13 14:17:52 +0000 | [diff] [blame] | 61 | __atomic_fetch_add (&counters[steps + 1], 1, __ATOMIC_RELAXED); |
Martin Liska | a266236 | 2016-08-10 15:14:56 +0200 | [diff] [blame] | 62 | else if (delta >= steps) |
Thomas Preud'homme | 4d0cdd0 | 2016-10-13 14:17:52 +0000 | [diff] [blame] | 63 | __atomic_fetch_add (&counters[steps], 1, __ATOMIC_RELAXED); |
Martin Liska | a266236 | 2016-08-10 15:14:56 +0200 | [diff] [blame] | 64 | else |
Thomas Preud'homme | 4d0cdd0 | 2016-10-13 14:17:52 +0000 | [diff] [blame] | 65 | __atomic_fetch_add (&counters[delta], 1, __ATOMIC_RELAXED); |
Martin Liska | a266236 | 2016-08-10 15:14:56 +0200 | [diff] [blame] | 66 | } |
| 67 | #endif |
| 68 | |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 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 | { |
Martin Liska | dcb1e13 | 2016-08-09 22:57:14 +0200 | [diff] [blame] | 76 | if (value == 0 || (value & (value - 1))) |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 77 | counters[0]++; |
| 78 | else |
| 79 | counters[1]++; |
| 80 | } |
| 81 | #endif |
| 82 | |
Martin Liska | 7fe76f6 | 2016-09-06 16:13:21 +0200 | [diff] [blame] | 83 | #if defined(L_gcov_pow2_profiler_atomic) && GCOV_SUPPORTS_ATOMIC |
Martin Liska | a266236 | 2016-08-10 15:14:56 +0200 | [diff] [blame] | 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))) |
Thomas Preud'homme | 4d0cdd0 | 2016-10-13 14:17:52 +0000 | [diff] [blame] | 91 | __atomic_fetch_add (&counters[0], 1, __ATOMIC_RELAXED); |
Martin Liska | a266236 | 2016-08-10 15:14:56 +0200 | [diff] [blame] | 92 | else |
Thomas Preud'homme | 4d0cdd0 | 2016-10-13 14:17:52 +0000 | [diff] [blame] | 93 | __atomic_fetch_add (&counters[1], 1, __ATOMIC_RELAXED); |
Martin Liska | a266236 | 2016-08-10 15:14:56 +0200 | [diff] [blame] | 94 | } |
| 95 | #endif |
| 96 | |
Martin Liska | 596341c | 2019-07-03 14:42:23 +0200 | [diff] [blame] | 97 | /* Tries to determine N most commons value among its inputs. */ |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 98 | |
| 99 | static inline void |
Martin Liska | 596341c | 2019-07-03 14:42:23 +0200 | [diff] [blame] | 100 | __gcov_topn_values_profiler_body (gcov_type *counters, gcov_type value, |
| 101 | int use_atomic) |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 102 | { |
Martin Liska | 871e5ad | 2020-01-31 13:10:14 +0100 | [diff] [blame] | 103 | gcov_topn_add_value (counters, value, 1, use_atomic, 1); |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 104 | } |
| 105 | |
Martin Liska | 596341c | 2019-07-03 14:42:23 +0200 | [diff] [blame] | 106 | #ifdef L_gcov_topn_values_profiler |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 107 | void |
Martin Liska | 596341c | 2019-07-03 14:42:23 +0200 | [diff] [blame] | 108 | __gcov_topn_values_profiler (gcov_type *counters, gcov_type value) |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 109 | { |
Martin Liska | 596341c | 2019-07-03 14:42:23 +0200 | [diff] [blame] | 110 | __gcov_topn_values_profiler_body (counters, value, 0); |
Martin Liska | a266236 | 2016-08-10 15:14:56 +0200 | [diff] [blame] | 111 | } |
| 112 | #endif |
| 113 | |
Martin Liska | 596341c | 2019-07-03 14:42:23 +0200 | [diff] [blame] | 114 | #if defined(L_gcov_topn_values_profiler_atomic) && GCOV_SUPPORTS_ATOMIC |
Martin Liska | a266236 | 2016-08-10 15:14:56 +0200 | [diff] [blame] | 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 |
Martin Liska | 596341c | 2019-07-03 14:42:23 +0200 | [diff] [blame] | 126 | __gcov_topn_values_profiler_atomic (gcov_type *counters, gcov_type value) |
Martin Liska | a266236 | 2016-08-10 15:14:56 +0200 | [diff] [blame] | 127 | { |
Martin Liska | 596341c | 2019-07-03 14:42:23 +0200 | [diff] [blame] | 128 | __gcov_topn_values_profiler_body (counters, value, 1); |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 129 | } |
| 130 | #endif |
| 131 | |
Martin Liska | 92d4171 | 2019-06-10 09:38:59 +0200 | [diff] [blame] | 132 | #ifdef L_gcov_indirect_call_profiler_v4 |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 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 |
Martin Liska | 3edbcdb | 2018-10-04 14:41:14 +0200 | [diff] [blame] | 142 | struct indirect_call_tuple __gcov_indirect_call; |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 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 |
Joseph Myers | 53d68b9 | 2014-09-05 13:03:46 +0100 | [diff] [blame] | 147 | of this macro says how many words wide the descriptor is (normally 2). |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 148 | |
| 149 | It is assumed that the address of a function descriptor may be treated |
| 150 | as a pointer to a function. */ |
| 151 | |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 152 | /* Tries to determine the most common value among its inputs. */ |
Martin Liska | 3ae37f9 | 2020-01-27 16:20:10 +0100 | [diff] [blame] | 153 | static inline void |
| 154 | __gcov_indirect_call_profiler_body (gcov_type value, void *cur_func, |
| 155 | int use_atomic) |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 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. */ |
Martin Liska | 3edbcdb | 2018-10-04 14:41:14 +0200 | [diff] [blame] | 160 | if (cur_func == __gcov_indirect_call.callee |
Martin Liska | fd2e1dc | 2018-08-01 12:22:18 +0200 | [diff] [blame] | 161 | || (__LIBGCC_VTABLE_USES_DESCRIPTORS__ |
Martin Liska | 3edbcdb | 2018-10-04 14:41:14 +0200 | [diff] [blame] | 162 | && *(void **) cur_func == *(void **) __gcov_indirect_call.callee)) |
Martin Liska | 3ae37f9 | 2020-01-27 16:20:10 +0100 | [diff] [blame] | 163 | __gcov_topn_values_profiler_body (__gcov_indirect_call.counters, value, |
| 164 | use_atomic); |
Martin Liska | 4f751c5 | 2017-06-09 14:54:53 +0200 | [diff] [blame] | 165 | |
Martin Liska | 3edbcdb | 2018-10-04 14:41:14 +0200 | [diff] [blame] | 166 | __gcov_indirect_call.callee = NULL; |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 167 | } |
Martin Liska | 3ae37f9 | 2020-01-27 16:20:10 +0100 | [diff] [blame] | 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 | |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 183 | #endif |
| 184 | |
| 185 | #ifdef L_gcov_time_profiler |
| 186 | |
| 187 | /* Counter for first visit of each function. */ |
Martin Liska | 3239d72 | 2018-10-25 17:36:12 +0200 | [diff] [blame] | 188 | gcov_type __gcov_time_profiler_counter ATTRIBUTE_HIDDEN; |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 189 | |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 190 | #endif |
Martin Liska | a266236 | 2016-08-10 15:14:56 +0200 | [diff] [blame] | 191 | |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 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 | |
Martin Liska | 7fe76f6 | 2016-09-06 16:13:21 +0200 | [diff] [blame] | 204 | #if defined(L_gcov_average_profiler_atomic) && GCOV_SUPPORTS_ATOMIC |
Martin Liska | a266236 | 2016-08-10 15:14:56 +0200 | [diff] [blame] | 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 | { |
Thomas Preud'homme | 4d0cdd0 | 2016-10-13 14:17:52 +0000 | [diff] [blame] | 211 | __atomic_fetch_add (&counters[0], value, __ATOMIC_RELAXED); |
| 212 | __atomic_fetch_add (&counters[1], 1, __ATOMIC_RELAXED); |
Martin Liska | a266236 | 2016-08-10 15:14:56 +0200 | [diff] [blame] | 213 | } |
| 214 | #endif |
| 215 | |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 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 | |
Martin Liska | 7fe76f6 | 2016-09-06 16:13:21 +0200 | [diff] [blame] | 226 | #if defined(L_gcov_ior_profiler_atomic) && GCOV_SUPPORTS_ATOMIC |
Martin Liska | a266236 | 2016-08-10 15:14:56 +0200 | [diff] [blame] | 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 | { |
Thomas Preud'homme | 4d0cdd0 | 2016-10-13 14:17:52 +0000 | [diff] [blame] | 232 | __atomic_fetch_or (&counters[0], value, __ATOMIC_RELAXED); |
Martin Liska | a266236 | 2016-08-10 15:14:56 +0200 | [diff] [blame] | 233 | } |
| 234 | #endif |
| 235 | |
| 236 | |
Rong Xu | d6d3f03 | 2013-11-13 00:24:49 +0000 | [diff] [blame] | 237 | #endif /* inhibit_libc */ |