]>
Commit | Line | Data |
---|---|---|
d6d3f033 RX |
1 | /* Routines required for instrumenting a program. */ |
2 | /* Compile this one with gcc. */ | |
818ab71a | 3 | /* Copyright (C) 1989-2016 Free Software Foundation, Inc. |
d6d3f033 RX |
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 | ||
40d6b753 | 26 | #include "libgcov.h" |
d6d3f033 | 27 | #if !defined(inhibit_libc) |
d6d3f033 | 28 | |
7fe76f6a ML |
29 | /* Detect whether target can support atomic update of profilers. */ |
30 | #if __SIZEOF_LONG_LONG__ == 4 && __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 | |
31 | #define GCOV_SUPPORTS_ATOMIC 1 | |
32 | #else | |
33 | #if __SIZEOF_LONG_LONG__ == 8 && __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 | |
34 | #define GCOV_SUPPORTS_ATOMIC 1 | |
35 | #else | |
36 | #define GCOV_SUPPORTS_ATOMIC 0 | |
37 | #endif | |
38 | #endif | |
39 | ||
d6d3f033 RX |
40 | #ifdef L_gcov_interval_profiler |
41 | /* If VALUE is in interval <START, START + STEPS - 1>, then increases the | |
42 | corresponding counter in COUNTERS. If the VALUE is above or below | |
43 | the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased | |
44 | instead. */ | |
45 | ||
46 | void | |
47 | __gcov_interval_profiler (gcov_type *counters, gcov_type value, | |
48 | int start, unsigned steps) | |
49 | { | |
50 | gcov_type delta = value - start; | |
51 | if (delta < 0) | |
52 | counters[steps + 1]++; | |
53 | else if (delta >= steps) | |
54 | counters[steps]++; | |
55 | else | |
56 | counters[delta]++; | |
57 | } | |
58 | #endif | |
59 | ||
7fe76f6a | 60 | #if defined(L_gcov_interval_profiler_atomic) && GCOV_SUPPORTS_ATOMIC |
a266236e ML |
61 | /* If VALUE is in interval <START, START + STEPS - 1>, then increases the |
62 | corresponding counter in COUNTERS. If the VALUE is above or below | |
63 | the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased | |
64 | instead. Function is thread-safe. */ | |
65 | ||
66 | void | |
67 | __gcov_interval_profiler_atomic (gcov_type *counters, gcov_type value, | |
68 | int start, unsigned steps) | |
69 | { | |
70 | gcov_type delta = value - start; | |
71 | if (delta < 0) | |
4d0cdd0c | 72 | __atomic_fetch_add (&counters[steps + 1], 1, __ATOMIC_RELAXED); |
a266236e | 73 | else if (delta >= steps) |
4d0cdd0c | 74 | __atomic_fetch_add (&counters[steps], 1, __ATOMIC_RELAXED); |
a266236e | 75 | else |
4d0cdd0c | 76 | __atomic_fetch_add (&counters[delta], 1, __ATOMIC_RELAXED); |
a266236e ML |
77 | } |
78 | #endif | |
79 | ||
d6d3f033 RX |
80 | #ifdef L_gcov_pow2_profiler |
81 | /* If VALUE is a power of two, COUNTERS[1] is incremented. Otherwise | |
82 | COUNTERS[0] is incremented. */ | |
83 | ||
84 | void | |
85 | __gcov_pow2_profiler (gcov_type *counters, gcov_type value) | |
86 | { | |
dcb1e137 | 87 | if (value == 0 || (value & (value - 1))) |
d6d3f033 RX |
88 | counters[0]++; |
89 | else | |
90 | counters[1]++; | |
91 | } | |
92 | #endif | |
93 | ||
7fe76f6a | 94 | #if defined(L_gcov_pow2_profiler_atomic) && GCOV_SUPPORTS_ATOMIC |
a266236e ML |
95 | /* If VALUE is a power of two, COUNTERS[1] is incremented. Otherwise |
96 | COUNTERS[0] is incremented. Function is thread-safe. */ | |
97 | ||
98 | void | |
99 | __gcov_pow2_profiler_atomic (gcov_type *counters, gcov_type value) | |
100 | { | |
101 | if (value == 0 || (value & (value - 1))) | |
4d0cdd0c | 102 | __atomic_fetch_add (&counters[0], 1, __ATOMIC_RELAXED); |
a266236e | 103 | else |
4d0cdd0c | 104 | __atomic_fetch_add (&counters[1], 1, __ATOMIC_RELAXED); |
a266236e ML |
105 | } |
106 | #endif | |
107 | ||
108 | ||
d6d3f033 RX |
109 | /* Tries to determine the most common value among its inputs. Checks if the |
110 | value stored in COUNTERS[0] matches VALUE. If this is the case, COUNTERS[1] | |
111 | is incremented. If this is not the case and COUNTERS[1] is not zero, | |
112 | COUNTERS[1] is decremented. Otherwise COUNTERS[1] is set to one and | |
113 | VALUE is stored to COUNTERS[0]. This algorithm guarantees that if this | |
114 | function is called more than 50% of the time with one value, this value | |
115 | will be in COUNTERS[0] in the end. | |
116 | ||
a266236e ML |
117 | In any case, COUNTERS[2] is incremented. If USE_ATOMIC is set to 1, |
118 | COUNTERS[2] is updated with an atomic instruction. */ | |
d6d3f033 RX |
119 | |
120 | static inline void | |
a266236e ML |
121 | __gcov_one_value_profiler_body (gcov_type *counters, gcov_type value, |
122 | int use_atomic) | |
d6d3f033 RX |
123 | { |
124 | if (value == counters[0]) | |
125 | counters[1]++; | |
126 | else if (counters[1] == 0) | |
127 | { | |
128 | counters[1] = 1; | |
129 | counters[0] = value; | |
130 | } | |
131 | else | |
132 | counters[1]--; | |
a266236e ML |
133 | |
134 | if (use_atomic) | |
4d0cdd0c | 135 | __atomic_fetch_add (&counters[2], 1, __ATOMIC_RELAXED); |
a266236e ML |
136 | else |
137 | counters[2]++; | |
d6d3f033 RX |
138 | } |
139 | ||
140 | #ifdef L_gcov_one_value_profiler | |
141 | void | |
142 | __gcov_one_value_profiler (gcov_type *counters, gcov_type value) | |
143 | { | |
a266236e ML |
144 | __gcov_one_value_profiler_body (counters, value, 0); |
145 | } | |
146 | #endif | |
147 | ||
7fe76f6a | 148 | #if defined(L_gcov_one_value_profiler_atomic) && GCOV_SUPPORTS_ATOMIC |
a266236e ML |
149 | |
150 | /* Update one value profilers (COUNTERS) for a given VALUE. | |
151 | ||
152 | CAVEAT: Following function is not thread-safe, only total number | |
153 | of executions (COUNTERS[2]) is update with an atomic instruction. | |
154 | Problem is that one cannot atomically update two counters | |
155 | (COUNTERS[0] and COUNTERS[1]), for more information please read | |
156 | following email thread: | |
157 | https://gcc.gnu.org/ml/gcc-patches/2016-08/msg00024.html. */ | |
158 | ||
159 | void | |
160 | __gcov_one_value_profiler_atomic (gcov_type *counters, gcov_type value) | |
161 | { | |
162 | __gcov_one_value_profiler_body (counters, value, 1); | |
d6d3f033 RX |
163 | } |
164 | #endif | |
165 | ||
afe0c5ee RX |
166 | #ifdef L_gcov_indirect_call_topn_profiler |
167 | /* Tries to keep track the most frequent N values in the counters where | |
168 | N is specified by parameter TOPN_VAL. To track top N values, 2*N counter | |
169 | entries are used. | |
170 | counter[0] --- the accumative count of the number of times one entry in | |
171 | in the counters gets evicted/replaced due to limited capacity. | |
172 | When this value reaches a threshold, the bottom N values are | |
173 | cleared. | |
174 | counter[1] through counter[2*N] records the top 2*N values collected so far. | |
175 | Each value is represented by two entries: count[2*i+1] is the ith value, and | |
176 | count[2*i+2] is the number of times the value is seen. */ | |
177 | ||
178 | static void | |
179 | __gcov_topn_value_profiler_body (gcov_type *counters, gcov_type value) | |
180 | { | |
181 | unsigned i, found = 0, have_zero_count = 0; | |
182 | gcov_type *entry; | |
183 | gcov_type *lfu_entry = &counters[1]; | |
184 | gcov_type *value_array = &counters[1]; | |
185 | gcov_type *num_eviction = &counters[0]; | |
186 | gcov_unsigned_t topn_val = GCOV_ICALL_TOPN_VAL; | |
187 | ||
188 | /* There are 2*topn_val values tracked, each value takes two slots in the | |
189 | counter array. */ | |
190 | for (i = 0; i < (topn_val << 2); i += 2) | |
191 | { | |
192 | entry = &value_array[i]; | |
193 | if (entry[0] == value) | |
194 | { | |
195 | entry[1]++ ; | |
196 | found = 1; | |
197 | break; | |
198 | } | |
199 | else if (entry[1] == 0) | |
200 | { | |
201 | lfu_entry = entry; | |
202 | have_zero_count = 1; | |
203 | } | |
204 | else if (entry[1] < lfu_entry[1]) | |
205 | lfu_entry = entry; | |
206 | } | |
207 | ||
208 | if (found) | |
209 | return; | |
210 | ||
211 | /* lfu_entry is either an empty entry or an entry | |
212 | with lowest count, which will be evicted. */ | |
213 | lfu_entry[0] = value; | |
214 | lfu_entry[1] = 1; | |
215 | ||
216 | #define GCOV_ICALL_COUNTER_CLEAR_THRESHOLD 3000 | |
217 | ||
218 | /* Too many evictions -- time to clear bottom entries to | |
219 | avoid hot values bumping each other out. */ | |
220 | if (!have_zero_count | |
221 | && ++*num_eviction >= GCOV_ICALL_COUNTER_CLEAR_THRESHOLD) | |
222 | { | |
223 | unsigned i, j; | |
224 | gcov_type *p, minv; | |
225 | gcov_type* tmp_cnts | |
226 | = (gcov_type *)alloca (topn_val * sizeof (gcov_type)); | |
227 | ||
228 | *num_eviction = 0; | |
229 | ||
230 | for (i = 0; i < topn_val; i++) | |
231 | tmp_cnts[i] = 0; | |
232 | ||
233 | /* Find the largest topn_val values from the group of | |
234 | 2*topn_val values and put them into tmp_cnts. */ | |
235 | ||
236 | for (i = 0; i < 2 * topn_val; i += 2) | |
237 | { | |
238 | p = 0; | |
239 | for (j = 0; j < topn_val; j++) | |
240 | { | |
241 | if (!p || tmp_cnts[j] < *p) | |
242 | p = &tmp_cnts[j]; | |
243 | } | |
244 | if (value_array[i + 1] > *p) | |
245 | *p = value_array[i + 1]; | |
246 | } | |
247 | ||
248 | minv = tmp_cnts[0]; | |
249 | for (j = 1; j < topn_val; j++) | |
250 | { | |
251 | if (tmp_cnts[j] < minv) | |
252 | minv = tmp_cnts[j]; | |
253 | } | |
254 | /* Zero out low value entries. */ | |
255 | for (i = 0; i < 2 * topn_val; i += 2) | |
256 | { | |
257 | if (value_array[i + 1] < minv) | |
258 | { | |
259 | value_array[i] = 0; | |
260 | value_array[i + 1] = 0; | |
261 | } | |
262 | } | |
263 | } | |
264 | } | |
265 | ||
266 | /* These two variables are used to actually track caller and callee. Keep | |
267 | them in TLS memory so races are not common (they are written to often). | |
268 | The variables are set directly by GCC instrumented code, so declaration | |
269 | here must match one in tree-profile.c. */ | |
270 | ||
271 | #if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS) | |
272 | __thread | |
273 | #endif | |
274 | gcov_type *__gcov_indirect_call_topn_counters ATTRIBUTE_HIDDEN; | |
275 | ||
276 | #if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS) | |
277 | __thread | |
278 | #endif | |
279 | void *__gcov_indirect_call_topn_callee ATTRIBUTE_HIDDEN; | |
280 | ||
281 | #ifdef TARGET_VTABLE_USES_DESCRIPTORS | |
282 | #define VTABLE_USES_DESCRIPTORS 1 | |
283 | #else | |
284 | #define VTABLE_USES_DESCRIPTORS 0 | |
285 | #endif | |
286 | ||
287 | /* This fucntion is instrumented at function entry to track topn indirect | |
288 | calls to CUR_FUNC. */ | |
289 | ||
290 | void | |
291 | __gcov_indirect_call_topn_profiler (gcov_type value, void* cur_func) | |
292 | { | |
293 | void *callee_func = __gcov_indirect_call_topn_callee; | |
294 | /* If the C++ virtual tables contain function descriptors then one | |
295 | function may have multiple descriptors and we need to dereference | |
296 | the descriptors to see if they point to the same function. */ | |
297 | if (cur_func == callee_func | |
298 | || (VTABLE_USES_DESCRIPTORS && callee_func | |
299 | && *(void **) cur_func == *(void **) callee_func)) | |
300 | __gcov_topn_value_profiler_body (__gcov_indirect_call_topn_counters, value); | |
301 | } | |
302 | #endif | |
303 | ||
d6d3f033 RX |
304 | #ifdef L_gcov_indirect_call_profiler_v2 |
305 | ||
306 | /* These two variables are used to actually track caller and callee. Keep | |
307 | them in TLS memory so races are not common (they are written to often). | |
308 | The variables are set directly by GCC instrumented code, so declaration | |
309 | here must match one in tree-profile.c */ | |
310 | ||
311 | #if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS) | |
312 | __thread | |
313 | #endif | |
314 | void * __gcov_indirect_call_callee; | |
315 | #if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS) | |
316 | __thread | |
317 | #endif | |
318 | gcov_type * __gcov_indirect_call_counters; | |
319 | ||
320 | /* By default, the C++ compiler will use function addresses in the | |
321 | vtable entries. Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero | |
322 | tells the compiler to use function descriptors instead. The value | |
53d68b9f | 323 | of this macro says how many words wide the descriptor is (normally 2). |
d6d3f033 RX |
324 | |
325 | It is assumed that the address of a function descriptor may be treated | |
326 | as a pointer to a function. */ | |
327 | ||
d6d3f033 RX |
328 | /* Tries to determine the most common value among its inputs. */ |
329 | void | |
330 | __gcov_indirect_call_profiler_v2 (gcov_type value, void* cur_func) | |
331 | { | |
332 | /* If the C++ virtual tables contain function descriptors then one | |
333 | function may have multiple descriptors and we need to dereference | |
334 | the descriptors to see if they point to the same function. */ | |
335 | if (cur_func == __gcov_indirect_call_callee | |
53d68b9f | 336 | || (__LIBGCC_VTABLE_USES_DESCRIPTORS__ && __gcov_indirect_call_callee |
d6d3f033 | 337 | && *(void **) cur_func == *(void **) __gcov_indirect_call_callee)) |
a266236e | 338 | __gcov_one_value_profiler_body (__gcov_indirect_call_counters, value, 0); |
d6d3f033 RX |
339 | } |
340 | #endif | |
341 | ||
342 | #ifdef L_gcov_time_profiler | |
343 | ||
344 | /* Counter for first visit of each function. */ | |
345 | static gcov_type function_counter; | |
346 | ||
347 | /* Sets corresponding COUNTERS if there is no value. */ | |
348 | ||
349 | void | |
350 | __gcov_time_profiler (gcov_type* counters) | |
351 | { | |
352 | if (!counters[0]) | |
353 | counters[0] = ++function_counter; | |
354 | } | |
a266236e | 355 | |
7fe76f6a | 356 | #if GCOV_SUPPORTS_ATOMIC |
a266236e ML |
357 | /* Sets corresponding COUNTERS if there is no value. |
358 | Function is thread-safe. */ | |
359 | ||
360 | void | |
361 | __gcov_time_profiler_atomic (gcov_type* counters) | |
362 | { | |
363 | if (!counters[0]) | |
4d0cdd0c | 364 | counters[0] = __atomic_add_fetch (&function_counter, 1, __ATOMIC_RELAXED); |
a266236e | 365 | } |
d6d3f033 | 366 | #endif |
7fe76f6a | 367 | #endif |
d6d3f033 | 368 | |
a266236e | 369 | |
d6d3f033 RX |
370 | #ifdef L_gcov_average_profiler |
371 | /* Increase corresponding COUNTER by VALUE. FIXME: Perhaps we want | |
372 | to saturate up. */ | |
373 | ||
374 | void | |
375 | __gcov_average_profiler (gcov_type *counters, gcov_type value) | |
376 | { | |
377 | counters[0] += value; | |
378 | counters[1] ++; | |
379 | } | |
380 | #endif | |
381 | ||
7fe76f6a | 382 | #if defined(L_gcov_average_profiler_atomic) && GCOV_SUPPORTS_ATOMIC |
a266236e ML |
383 | /* Increase corresponding COUNTER by VALUE. FIXME: Perhaps we want |
384 | to saturate up. Function is thread-safe. */ | |
385 | ||
386 | void | |
387 | __gcov_average_profiler_atomic (gcov_type *counters, gcov_type value) | |
388 | { | |
4d0cdd0c TP |
389 | __atomic_fetch_add (&counters[0], value, __ATOMIC_RELAXED); |
390 | __atomic_fetch_add (&counters[1], 1, __ATOMIC_RELAXED); | |
a266236e ML |
391 | } |
392 | #endif | |
393 | ||
d6d3f033 RX |
394 | #ifdef L_gcov_ior_profiler |
395 | /* Bitwise-OR VALUE into COUNTER. */ | |
396 | ||
397 | void | |
398 | __gcov_ior_profiler (gcov_type *counters, gcov_type value) | |
399 | { | |
400 | *counters |= value; | |
401 | } | |
402 | #endif | |
403 | ||
7fe76f6a | 404 | #if defined(L_gcov_ior_profiler_atomic) && GCOV_SUPPORTS_ATOMIC |
a266236e ML |
405 | /* Bitwise-OR VALUE into COUNTER. Function is thread-safe. */ |
406 | ||
407 | void | |
408 | __gcov_ior_profiler_atomic (gcov_type *counters, gcov_type value) | |
409 | { | |
4d0cdd0c | 410 | __atomic_fetch_or (&counters[0], value, __ATOMIC_RELAXED); |
a266236e ML |
411 | } |
412 | #endif | |
413 | ||
414 | ||
d6d3f033 | 415 | #endif /* inhibit_libc */ |