1 /* Routines required for instrumenting a program.  */
2 /* Compile this one with gcc.  */
3 /* Copyright (C) 1989-2021 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
__gcov_interval_profiler(gcov_type * counters,gcov_type value,int start,unsigned steps)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
__gcov_interval_profiler_atomic(gcov_type * counters,gcov_type value,int start,unsigned steps)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
__gcov_pow2_profiler(gcov_type * counters,gcov_type value)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
__gcov_pow2_profiler_atomic(gcov_type * counters,gcov_type value)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
__gcov_topn_values_profiler_body(gcov_type * counters,gcov_type value,int use_atomic)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
__gcov_topn_values_profiler(gcov_type * counters,gcov_type value)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
__gcov_topn_values_profiler_atomic(gcov_type * counters,gcov_type value)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
__gcov_indirect_call_profiler_body(gcov_type value,void * cur_func,int use_atomic)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
__gcov_indirect_call_profiler_v4(gcov_type value,void * cur_func)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
__gcov_indirect_call_profiler_v4_atomic(gcov_type value,void * cur_func)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
__gcov_average_profiler(gcov_type * counters,gcov_type value)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
__gcov_average_profiler_atomic(gcov_type * counters,gcov_type value)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
__gcov_ior_profiler(gcov_type * counters,gcov_type value)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
__gcov_ior_profiler_atomic(gcov_type * counters,gcov_type value)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