comparison libgcc/libgcov-profiler.c @ 145:1830386684a0

gcc-9.2.0
author anatofuz
date Thu, 13 Feb 2020 11:34:05 +0900
parents 84e7813d76e9
children
comparison
equal deleted inserted replaced
131:84e7813d76e9 145:1830386684a0
1 /* Routines required for instrumenting a program. */ 1 /* Routines required for instrumenting a program. */
2 /* Compile this one with gcc. */ 2 /* Compile this one with gcc. */
3 /* Copyright (C) 1989-2018 Free Software Foundation, Inc. 3 /* Copyright (C) 1989-2020 Free Software Foundation, Inc.
4 4
5 This file is part of GCC. 5 This file is part of GCC.
6 6
7 GCC is free software; you can redistribute it and/or modify it under 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 8 the terms of the GNU General Public License as published by the Free
104 __atomic_fetch_add (&counters[1], 1, __ATOMIC_RELAXED); 104 __atomic_fetch_add (&counters[1], 1, __ATOMIC_RELAXED);
105 } 105 }
106 #endif 106 #endif
107 107
108 108
109 /* Tries to determine the most common value among its inputs. Checks if the 109 /* Tries to determine N most commons value among its inputs. */
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
117 In any case, COUNTERS[2] is incremented. If USE_ATOMIC is set to 1,
118 COUNTERS[2] is updated with an atomic instruction. */
119 110
120 static inline void 111 static inline void
121 __gcov_one_value_profiler_body (gcov_type *counters, gcov_type value, 112 __gcov_topn_values_profiler_body (gcov_type *counters, gcov_type value,
122 int use_atomic) 113 int use_atomic)
123 { 114 {
124 if (value == counters[0]) 115 if (use_atomic)
125 counters[1]++; 116 __atomic_fetch_add (&counters[0], 1, __ATOMIC_RELAXED);
126 else if (counters[1] == 0) 117 else
118 counters[0]++;
119
120 ++counters;
121
122 /* First try to find an existing value. */
123 int empty_counter = -1;
124
125 for (unsigned i = 0; i < GCOV_TOPN_VALUES; i++)
126 if (value == counters[2 * i])
127 {
128 if (use_atomic)
129 __atomic_fetch_add (&counters[2 * i + 1], GCOV_TOPN_VALUES,
130 __ATOMIC_RELAXED);
131 else
132 counters[2 * i + 1] += GCOV_TOPN_VALUES;
133 return;
134 }
135 else if (counters[2 * i + 1] <= 0)
136 empty_counter = i;
137
138 /* Find an empty slot for a new value. */
139 if (empty_counter != -1)
127 { 140 {
128 counters[1] = 1; 141 counters[2 * empty_counter] = value;
129 counters[0] = value; 142 counters[2 * empty_counter + 1] = GCOV_TOPN_VALUES;
143 return;
130 } 144 }
131 else 145
132 counters[1]--; 146 /* We haven't found an empty slot, then decrement all
133 147 counter values by one. */
134 if (use_atomic) 148 for (unsigned i = 0; i < GCOV_TOPN_VALUES; i++)
135 __atomic_fetch_add (&counters[2], 1, __ATOMIC_RELAXED); 149 if (use_atomic)
136 else 150 __atomic_fetch_sub (&counters[2 * i + 1], 1, __ATOMIC_RELAXED);
137 counters[2]++; 151 else
138 } 152 counters[2 * i + 1]--;
139 153 }
140 #ifdef L_gcov_one_value_profiler 154
141 void 155 #ifdef L_gcov_topn_values_profiler
142 __gcov_one_value_profiler (gcov_type *counters, gcov_type value) 156 void
143 { 157 __gcov_topn_values_profiler (gcov_type *counters, gcov_type value)
144 __gcov_one_value_profiler_body (counters, value, 0); 158 {
145 } 159 __gcov_topn_values_profiler_body (counters, value, 0);
146 #endif 160 }
147 161 #endif
148 #if defined(L_gcov_one_value_profiler_atomic) && GCOV_SUPPORTS_ATOMIC 162
163 #if defined(L_gcov_topn_values_profiler_atomic) && GCOV_SUPPORTS_ATOMIC
149 164
150 /* Update one value profilers (COUNTERS) for a given VALUE. 165 /* Update one value profilers (COUNTERS) for a given VALUE.
151 166
152 CAVEAT: Following function is not thread-safe, only total number 167 CAVEAT: Following function is not thread-safe, only total number
153 of executions (COUNTERS[2]) is update with an atomic instruction. 168 of executions (COUNTERS[2]) is update with an atomic instruction.
155 (COUNTERS[0] and COUNTERS[1]), for more information please read 170 (COUNTERS[0] and COUNTERS[1]), for more information please read
156 following email thread: 171 following email thread:
157 https://gcc.gnu.org/ml/gcc-patches/2016-08/msg00024.html. */ 172 https://gcc.gnu.org/ml/gcc-patches/2016-08/msg00024.html. */
158 173
159 void 174 void
160 __gcov_one_value_profiler_atomic (gcov_type *counters, gcov_type value) 175 __gcov_topn_values_profiler_atomic (gcov_type *counters, gcov_type value)
161 { 176 {
162 __gcov_one_value_profiler_body (counters, value, 1); 177 __gcov_topn_values_profiler_body (counters, value, 1);
163 } 178 }
164 #endif 179 #endif
165 180
166 #ifdef L_gcov_indirect_call_topn_profiler 181 #ifdef L_gcov_indirect_call_profiler_v4
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 struct indirect_call_tuple __gcov_indirect_call_topn;
275
276 #ifdef TARGET_VTABLE_USES_DESCRIPTORS
277 #define VTABLE_USES_DESCRIPTORS 1
278 #else
279 #define VTABLE_USES_DESCRIPTORS 0
280 #endif
281
282 /* This fucntion is instrumented at function entry to track topn indirect
283 calls to CUR_FUNC. */
284
285 void
286 __gcov_indirect_call_topn_profiler (gcov_type value, void* cur_func)
287 {
288 void *callee_func = __gcov_indirect_call_topn.callee;
289 /* If the C++ virtual tables contain function descriptors then one
290 function may have multiple descriptors and we need to dereference
291 the descriptors to see if they point to the same function. */
292 if (cur_func == callee_func
293 || (VTABLE_USES_DESCRIPTORS && callee_func
294 && *(void **) cur_func == *(void **) callee_func))
295 __gcov_topn_value_profiler_body (__gcov_indirect_call_topn.counters, value);
296 }
297 #endif
298
299 #ifdef L_gcov_indirect_call_profiler_v2
300 182
301 /* These two variables are used to actually track caller and callee. Keep 183 /* These two variables are used to actually track caller and callee. Keep
302 them in TLS memory so races are not common (they are written to often). 184 them in TLS memory so races are not common (they are written to often).
303 The variables are set directly by GCC instrumented code, so declaration 185 The variables are set directly by GCC instrumented code, so declaration
304 here must match one in tree-profile.c */ 186 here must match one in tree-profile.c */
315 197
316 It is assumed that the address of a function descriptor may be treated 198 It is assumed that the address of a function descriptor may be treated
317 as a pointer to a function. */ 199 as a pointer to a function. */
318 200
319 /* Tries to determine the most common value among its inputs. */ 201 /* Tries to determine the most common value among its inputs. */
320 void 202 static inline void
321 __gcov_indirect_call_profiler_v2 (gcov_type value, void* cur_func) 203 __gcov_indirect_call_profiler_body (gcov_type value, void *cur_func,
204 int use_atomic)
322 { 205 {
323 /* If the C++ virtual tables contain function descriptors then one 206 /* If the C++ virtual tables contain function descriptors then one
324 function may have multiple descriptors and we need to dereference 207 function may have multiple descriptors and we need to dereference
325 the descriptors to see if they point to the same function. */ 208 the descriptors to see if they point to the same function. */
326 if (cur_func == __gcov_indirect_call.callee 209 if (cur_func == __gcov_indirect_call.callee
327 || (__LIBGCC_VTABLE_USES_DESCRIPTORS__ 210 || (__LIBGCC_VTABLE_USES_DESCRIPTORS__
328 && *(void **) cur_func == *(void **) __gcov_indirect_call.callee)) 211 && *(void **) cur_func == *(void **) __gcov_indirect_call.callee))
329 __gcov_one_value_profiler_body (__gcov_indirect_call.counters, value, 0); 212 __gcov_topn_values_profiler_body (__gcov_indirect_call.counters, value,
213 use_atomic);
330 214
331 __gcov_indirect_call.callee = NULL; 215 __gcov_indirect_call.callee = NULL;
332 } 216 }
217
218 void
219 __gcov_indirect_call_profiler_v4 (gcov_type value, void *cur_func)
220 {
221 __gcov_indirect_call_profiler_body (value, cur_func, 0);
222 }
223
224 #if GCOV_SUPPORTS_ATOMIC
225 void
226 __gcov_indirect_call_profiler_v4_atomic (gcov_type value, void *cur_func)
227 {
228 __gcov_indirect_call_profiler_body (value, cur_func, 1);
229 }
230 #endif
231
333 #endif 232 #endif
334 233
335 #ifdef L_gcov_time_profiler 234 #ifdef L_gcov_time_profiler
336 235
337 /* Counter for first visit of each function. */ 236 /* Counter for first visit of each function. */
338 gcov_type __gcov_time_profiler_counter ATTRIBUTE_HIDDEN = 1; 237 gcov_type __gcov_time_profiler_counter ATTRIBUTE_HIDDEN;
339 238
340 #endif 239 #endif
341 240
342 #ifdef L_gcov_average_profiler 241 #ifdef L_gcov_average_profiler
343 /* Increase corresponding COUNTER by VALUE. FIXME: Perhaps we want 242 /* Increase corresponding COUNTER by VALUE. FIXME: Perhaps we want