xref: /netbsd-src/external/gpl3/gcc/dist/libgcc/libgcov.h (revision d16b7486a53dcb8072b60ec6fcb4373a2d0c27b7)
1 /* Header file for libgcov-*.c.
2    Copyright (C) 1996-2022 Free Software Foundation, Inc.
3 
4    This file is part of GCC.
5 
6    GCC is free software; you can redistribute it and/or modify it under
7    the terms of the GNU General Public License as published by the Free
8    Software Foundation; either version 3, or (at your option) any later
9    version.
10 
11    GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12    WARRANTY; without even the implied warranty of MERCHANTABILITY or
13    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14    for more details.
15 
16    Under Section 7 of GPL version 3, you are granted additional
17    permissions described in the GCC Runtime Library Exception, version
18    3.1, as published by the Free Software Foundation.
19 
20    You should have received a copy of the GNU General Public License and
21    a copy of the GCC Runtime Library Exception along with this program;
22    see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23    <http://www.gnu.org/licenses/>.  */
24 
25 #ifndef GCC_LIBGCOV_H
26 #define GCC_LIBGCOV_H
27 
28 /* work around the poisoned malloc/calloc in system.h.  */
29 #ifndef xmalloc
30 #define xmalloc malloc
31 #endif
32 #ifndef xcalloc
33 #define xcalloc calloc
34 #endif
35 
36 #ifndef IN_GCOV_TOOL
37 /* About the target.  */
38 /* This path will be used by libgcov runtime.  */
39 
40 #include "tconfig.h"
41 #include "auto-target.h"
42 #include "tsystem.h"
43 #include "coretypes.h"
44 #include "tm.h"
45 #include "libgcc_tm.h"
46 #include "gcov.h"
47 
48 #if HAVE_SYS_MMAN_H
49 #include <sys/mman.h>
50 #endif
51 
52 #if __CHAR_BIT__ == 8
53 typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
54 typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
55 #if __LIBGCC_GCOV_TYPE_SIZE > 32
56 typedef signed gcov_type __attribute__ ((mode (DI)));
57 typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
58 #else
59 typedef signed gcov_type __attribute__ ((mode (SI)));
60 typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
61 #endif
62 #else
63 #if __CHAR_BIT__ == 16
64 typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
65 typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
66 #if __LIBGCC_GCOV_TYPE_SIZE > 32
67 typedef signed gcov_type __attribute__ ((mode (SI)));
68 typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
69 #else
70 typedef signed gcov_type __attribute__ ((mode (HI)));
71 typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
72 #endif
73 #else
74 typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
75 typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
76 #if __LIBGCC_GCOV_TYPE_SIZE > 32
77 typedef signed gcov_type __attribute__ ((mode (HI)));
78 typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
79 #else
80 typedef signed gcov_type __attribute__ ((mode (QI)));
81 typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
82 #endif
83 #endif
84 #endif
85 
86 #if defined (TARGET_POSIX_IO)
87 #define GCOV_LOCKED 1
88 #else
89 #define GCOV_LOCKED 0
90 #endif
91 
92 #if defined (__MSVCRT__)
93 #define GCOV_LOCKED_WITH_LOCKING 1
94 #else
95 #define GCOV_LOCKED_WITH_LOCKING 0
96 #endif
97 
98 #ifndef GCOV_SUPPORTS_ATOMIC
99 /* Detect whether target can support atomic update of profilers.  */
100 #if __SIZEOF_LONG_LONG__ == 4 && __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4
101 #define GCOV_SUPPORTS_ATOMIC 1
102 #else
103 #if __SIZEOF_LONG_LONG__ == 8 && __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8
104 #define GCOV_SUPPORTS_ATOMIC 1
105 #else
106 #define GCOV_SUPPORTS_ATOMIC 0
107 #endif
108 #endif
109 #endif
110 
111 /* In libgcov we need these functions to be extern, so prefix them with
112    __gcov.  In libgcov they must also be hidden so that the instance in
113    the executable is not also used in a DSO.  */
114 #define gcov_var __gcov_var
115 #define gcov_open __gcov_open
116 #define gcov_close __gcov_close
117 #define gcov_position __gcov_position
118 #define gcov_seek __gcov_seek
119 #define gcov_rewrite __gcov_rewrite
120 #define gcov_is_error __gcov_is_error
121 #define gcov_write_unsigned __gcov_write_unsigned
122 #define gcov_write_summary __gcov_write_summary
123 #define gcov_read_unsigned __gcov_read_unsigned
124 #define gcov_read_counter __gcov_read_counter
125 #define gcov_read_summary __gcov_read_summary
126 
127 #else /* IN_GCOV_TOOL */
128 /* About the host.  */
129 /* This path will be compiled for the host and linked into
130    gcov-tool binary.  */
131 
132 #include "config.h"
133 #include "system.h"
134 #include "coretypes.h"
135 #include "tm.h"
136 
137 typedef unsigned gcov_unsigned_t;
138 typedef unsigned gcov_position_t;
139 /* gcov_type is typedef'd elsewhere for the compiler */
140 
141 #if defined (HOST_HAS_F_SETLKW)
142 #define GCOV_LOCKED 1
143 #else
144 #define GCOV_LOCKED 0
145 #endif
146 
147 #if defined (HOST_HAS_LK_LOCK)
148 #define GCOV_LOCKED_WITH_LOCKING 1
149 #else
150 #define GCOV_LOCKED_WITH_LOCKING 0
151 #endif
152 
153 /* Some Macros specific to gcov-tool.  */
154 
155 #define L_gcov 1
156 #define L_gcov_merge_add 1
157 #define L_gcov_merge_topn 1
158 #define L_gcov_merge_ior 1
159 #define L_gcov_merge_time_profile 1
160 
161 extern gcov_type gcov_read_counter_mem ();
162 extern unsigned gcov_get_merge_weight ();
163 extern struct gcov_info *gcov_list;
164 
165 #endif /* !IN_GCOV_TOOL */
166 
167 #if defined(inhibit_libc)
168 #define IN_LIBGCOV (-1)
169 #else
170 #define IN_LIBGCOV 1
171 #if defined(L_gcov)
172 #define GCOV_LINKAGE /* nothing */
173 #endif
174 #endif
175 
176 /* Poison these, so they don't accidentally slip in.  */
177 #pragma GCC poison gcov_write_string gcov_write_tag gcov_write_length
178 #pragma GCC poison gcov_time
179 
180 #ifdef HAVE_GAS_HIDDEN
181 #define ATTRIBUTE_HIDDEN  __attribute__ ((__visibility__ ("hidden")))
182 #else
183 #define ATTRIBUTE_HIDDEN
184 #endif
185 
186 #if HAVE_SYS_MMAN_H
187 #ifndef MAP_FAILED
188 #define MAP_FAILED ((void *)-1)
189 #endif
190 
191 #if !defined (MAP_ANONYMOUS) && defined (MAP_ANON)
192 #define MAP_ANONYMOUS MAP_ANON
193 #endif
194 #endif
195 
196 #include "gcov-io.h"
197 
198 /* Structures embedded in coveraged program.  The structures generated
199    by write_profile must match these.  */
200 
201 /* Information about counters for a single function.  */
202 struct gcov_ctr_info
203 {
204   gcov_unsigned_t num;		/* number of counters.  */
205   gcov_type *values;		/* their values.  */
206 };
207 
208 /* Information about a single function.  This uses the trailing array
209    idiom. The number of counters is determined from the merge pointer
210    array in gcov_info.  The key is used to detect which of a set of
211    comdat functions was selected -- it points to the gcov_info object
212    of the object file containing the selected comdat function.  */
213 
214 struct gcov_fn_info
215 {
216   const struct gcov_info *key;		/* comdat key */
217   gcov_unsigned_t ident;		/* unique ident of function */
218   gcov_unsigned_t lineno_checksum;	/* function lineo_checksum */
219   gcov_unsigned_t cfg_checksum;		/* function cfg checksum */
220   struct gcov_ctr_info ctrs[1];		/* instrumented counters */
221 };
222 
223 /* Type of function used to merge counters.  */
224 typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
225 
226 /* Information about a single object file.  */
227 struct gcov_info
228 {
229   gcov_unsigned_t version;	/* expected version number */
230   struct gcov_info *next;	/* link to next, used by libgcov */
231 
232   gcov_unsigned_t stamp;	/* uniquifying time stamp */
233   gcov_unsigned_t checksum;	/* unique object checksum */
234   const char *filename;		/* output file name */
235 
236   gcov_merge_fn merge[GCOV_COUNTERS];  /* merge functions (null for
237 					  unused) */
238 
239   gcov_unsigned_t n_functions;		/* number of functions */
240 
241 #ifndef IN_GCOV_TOOL
242   const struct gcov_fn_info *const *functions; /* pointer to pointers
243                                                   to function information  */
244 #else
245   struct gcov_fn_info **functions;
246   struct gcov_summary summary;
247 #endif /* !IN_GCOV_TOOL */
248 };
249 
250 /* Root of a program/shared-object state */
251 struct gcov_root
252 {
253   struct gcov_info *list;
254   unsigned dumped : 1;	/* counts have been dumped.  */
255   unsigned run_counted : 1;  /* run has been accounted for.  */
256   struct gcov_root *next;
257   struct gcov_root *prev;
258 };
259 
260 extern struct gcov_root __gcov_root ATTRIBUTE_HIDDEN;
261 
262 struct gcov_master
263 {
264   gcov_unsigned_t version;
265   struct gcov_root *root;
266 };
267 
268 struct indirect_call_tuple
269 {
270   /* Callee function.  */
271   void *callee;
272 
273   /* Pointer to counters.  */
274   gcov_type *counters;
275 };
276 
277 /* Exactly one of these will be active in the process.  */
278 extern struct gcov_master __gcov_master;
279 extern struct gcov_kvp *__gcov_kvp_dynamic_pool;
280 extern unsigned __gcov_kvp_dynamic_pool_index;
281 extern unsigned __gcov_kvp_dynamic_pool_size;
282 
283 /* Dump a set of gcov objects.  */
284 extern void __gcov_dump_one (struct gcov_root *) ATTRIBUTE_HIDDEN;
285 
286 /* Register a new object file module.  */
287 extern void __gcov_init (struct gcov_info *) ATTRIBUTE_HIDDEN;
288 
289 /* GCOV exit function registered via a static destructor.  */
290 extern void __gcov_exit (void) ATTRIBUTE_HIDDEN;
291 
292 /* Function to reset all counters to 0.  Both externally visible (and
293    overridable) and internal version.  */
294 extern void __gcov_reset_int (void) ATTRIBUTE_HIDDEN;
295 
296 /* User function to enable early write of profile information so far.  */
297 extern void __gcov_dump_int (void) ATTRIBUTE_HIDDEN;
298 
299 /* Lock critical section for __gcov_dump and __gcov_reset functions.  */
300 extern void __gcov_lock (void) ATTRIBUTE_HIDDEN;
301 
302 /* Unlock critical section for __gcov_dump and __gcov_reset functions.  */
303 extern void __gcov_unlock (void) ATTRIBUTE_HIDDEN;
304 
305 /* The merge function that just sums the counters.  */
306 extern void __gcov_merge_add (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
307 
308 /* The merge function to select the minimum valid counter value.  */
309 extern void __gcov_merge_time_profile (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
310 
311 /* The merge function to choose the most common N values.  */
312 extern void __gcov_merge_topn (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
313 
314 /* The merge function that just ors the counters together.  */
315 extern void __gcov_merge_ior (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
316 
317 /* The profiler functions.  */
318 extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
319 extern void __gcov_interval_profiler_atomic (gcov_type *, gcov_type, int,
320 					     unsigned);
321 extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
322 extern void __gcov_pow2_profiler_atomic (gcov_type *, gcov_type);
323 extern void __gcov_topn_values_profiler (gcov_type *, gcov_type);
324 extern void __gcov_topn_values_profiler_atomic (gcov_type *, gcov_type);
325 extern void __gcov_indirect_call_profiler_v4 (gcov_type, void *);
326 extern void __gcov_indirect_call_profiler_v4_atomic (gcov_type, void *);
327 extern void __gcov_time_profiler (gcov_type *);
328 extern void __gcov_time_profiler_atomic (gcov_type *);
329 extern void __gcov_average_profiler (gcov_type *, gcov_type);
330 extern void __gcov_average_profiler_atomic (gcov_type *, gcov_type);
331 extern void __gcov_ior_profiler (gcov_type *, gcov_type);
332 extern void __gcov_ior_profiler_atomic (gcov_type *, gcov_type);
333 
334 #ifndef inhibit_libc
335 /* The wrappers around some library functions..  */
336 extern pid_t __gcov_fork (void) ATTRIBUTE_HIDDEN;
337 extern int __gcov_execl (const char *, char *, ...) ATTRIBUTE_HIDDEN;
338 extern int __gcov_execlp (const char *, char *, ...) ATTRIBUTE_HIDDEN;
339 extern int __gcov_execle (const char *, char *, ...) ATTRIBUTE_HIDDEN;
340 extern int __gcov_execv (const char *, char *const []) ATTRIBUTE_HIDDEN;
341 extern int __gcov_execvp (const char *, char *const []) ATTRIBUTE_HIDDEN;
342 extern int __gcov_execve (const char *, char  *const [], char *const [])
343   ATTRIBUTE_HIDDEN;
344 
345 /* Functions that only available in libgcov.  */
346 GCOV_LINKAGE int gcov_open (const char */*name*/) ATTRIBUTE_HIDDEN;
347 GCOV_LINKAGE void gcov_write_summary (gcov_unsigned_t /*tag*/,
348                                       const struct gcov_summary *)
349     ATTRIBUTE_HIDDEN;
350 GCOV_LINKAGE void gcov_seek (gcov_position_t /*position*/) ATTRIBUTE_HIDDEN;
351 GCOV_LINKAGE void gcov_rewrite (void) ATTRIBUTE_HIDDEN;
352 
353 /* "Counts" stored in gcda files can be a real counter value, or
354    an target address. When differentiate these two types because
355    when manipulating counts, we should only change real counter values,
356    rather target addresses.  */
357 
358 static inline gcov_type
359 gcov_get_counter (void)
360 {
361 #ifndef IN_GCOV_TOOL
362   /* This version is for reading count values in libgcov runtime:
363      we read from gcda files.  */
364 
365   return gcov_read_counter ();
366 #else
367   /* This version is for gcov-tool. We read the value from memory and
368      multiply it by the merge weight.  */
369 
370   return gcov_read_counter_mem () * gcov_get_merge_weight ();
371 #endif
372 }
373 
374 /* Similar function as gcov_get_counter(), but do not scale
375    when read value is equal to IGNORE_SCALING.  */
376 
377 static inline gcov_type
378 gcov_get_counter_ignore_scaling (gcov_type ignore_scaling ATTRIBUTE_UNUSED)
379 {
380 #ifndef IN_GCOV_TOOL
381   /* This version is for reading count values in libgcov runtime:
382      we read from gcda files.  */
383 
384   return gcov_read_counter ();
385 #else
386   /* This version is for gcov-tool. We read the value from memory and
387      multiply it by the merge weight.  */
388 
389   gcov_type v = gcov_read_counter_mem ();
390   if (v != ignore_scaling)
391     v *= gcov_get_merge_weight ();
392 
393   return v;
394 #endif
395 }
396 
397 /* Similar function as gcov_get_counter(), but handles target address
398    counters.  */
399 
400 static inline gcov_type
401 gcov_get_counter_target (void)
402 {
403 #ifndef IN_GCOV_TOOL
404   /* This version is for reading count target values in libgcov runtime:
405      we read from gcda files.  */
406 
407   return gcov_read_counter ();
408 #else
409   /* This version is for gcov-tool.  We read the value from memory and we do NOT
410      multiply it by the merge weight.  */
411 
412   return gcov_read_counter_mem ();
413 #endif
414 }
415 
416 /* Add VALUE to *COUNTER and make it with atomic operation
417    if USE_ATOMIC is true.  */
418 
419 static inline void
420 gcov_counter_add (gcov_type *counter, gcov_type value,
421 		  int use_atomic ATTRIBUTE_UNUSED)
422 {
423 #if GCOV_SUPPORTS_ATOMIC
424   if (use_atomic)
425     __atomic_fetch_add (counter, value, __ATOMIC_RELAXED);
426   else
427 #endif
428     *counter += value;
429 }
430 
431 #if HAVE_SYS_MMAN_H
432 
433 /* Allocate LENGTH with mmap function.  */
434 
435 static inline void *
436 malloc_mmap (size_t length)
437 {
438   return mmap (NULL, length, PROT_READ | PROT_WRITE,
439 	       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
440 }
441 
442 #endif
443 
444 /* Allocate gcov_kvp from statically pre-allocated pool,
445    or use heap otherwise.  */
446 
447 static inline struct gcov_kvp *
448 allocate_gcov_kvp (void)
449 {
450 #define MMAP_CHUNK_SIZE	(128 * 1024)
451   struct gcov_kvp *new_node = NULL;
452   unsigned kvp_sizeof = sizeof(struct gcov_kvp);
453 
454   /* Try mmaped pool if available.  */
455 #if !defined(IN_GCOV_TOOL) && !defined(L_gcov_merge_topn) && HAVE_SYS_MMAN_H
456   if (__gcov_kvp_dynamic_pool == NULL
457       || __gcov_kvp_dynamic_pool_index >= __gcov_kvp_dynamic_pool_size)
458     {
459       void *ptr = malloc_mmap (MMAP_CHUNK_SIZE);
460       if (ptr != MAP_FAILED)
461 	{
462 	  __gcov_kvp_dynamic_pool = ptr;
463 	  __gcov_kvp_dynamic_pool_size = MMAP_CHUNK_SIZE / kvp_sizeof;
464 	  __gcov_kvp_dynamic_pool_index = 0;
465 	}
466     }
467 
468   if (__gcov_kvp_dynamic_pool != NULL)
469     {
470       unsigned index;
471 #if GCOV_SUPPORTS_ATOMIC
472       index
473 	= __atomic_fetch_add (&__gcov_kvp_dynamic_pool_index, 1,
474 			      __ATOMIC_RELAXED);
475 #else
476       index = __gcov_kvp_dynamic_pool_index++;
477 #endif
478       if (index < __gcov_kvp_dynamic_pool_size)
479 	new_node = __gcov_kvp_dynamic_pool + index;
480     }
481 #endif
482 
483   /* Fallback to malloc.  */
484   if (new_node == NULL)
485     new_node = (struct gcov_kvp *)xcalloc (1, kvp_sizeof);
486 
487   return new_node;
488 }
489 
490 /* Add key value pair VALUE:COUNT to a top N COUNTERS.  When INCREMENT_TOTAL
491    is true, add COUNT to total of the TOP counter.  If USE_ATOMIC is true,
492    do it in atomic way.  Return true when the counter is full, otherwise
493    return false.  */
494 
495 static inline unsigned
496 gcov_topn_add_value (gcov_type *counters, gcov_type value, gcov_type count,
497 		     int use_atomic, int increment_total)
498 {
499   if (increment_total)
500     {
501       /* In the multi-threaded mode, we can have an already merged profile
502 	 with a negative total value.  In that case, we should bail out.  */
503       if (counters[0] < 0)
504 	return 0;
505       gcov_counter_add (&counters[0], 1, use_atomic);
506     }
507 
508   struct gcov_kvp *prev_node = NULL;
509   struct gcov_kvp *minimal_node = NULL;
510   struct gcov_kvp *current_node  = (struct gcov_kvp *)(intptr_t)counters[2];
511 
512   while (current_node)
513     {
514       if (current_node->value == value)
515 	{
516 	  gcov_counter_add (&current_node->count, count, use_atomic);
517 	  return 0;
518 	}
519 
520       if (minimal_node == NULL
521 	  || current_node->count < minimal_node->count)
522 	minimal_node = current_node;
523 
524       prev_node = current_node;
525       current_node = current_node->next;
526     }
527 
528   if (counters[1] == GCOV_TOPN_MAXIMUM_TRACKED_VALUES)
529     {
530       if (--minimal_node->count < count)
531 	{
532 	  minimal_node->value = value;
533 	  minimal_node->count = count;
534 	}
535 
536       return 1;
537     }
538   else
539     {
540       struct gcov_kvp *new_node = allocate_gcov_kvp ();
541       if (new_node == NULL)
542 	return 0;
543 
544       new_node->value = value;
545       new_node->count = count;
546 
547       int success = 0;
548       if (!counters[2])
549 	{
550 #if GCOV_SUPPORTS_ATOMIC
551 	  if (use_atomic)
552 	    {
553 	      struct gcov_kvp **ptr = (struct gcov_kvp **)(intptr_t)&counters[2];
554 	      success = !__sync_val_compare_and_swap (ptr, 0, new_node);
555 	    }
556 	  else
557 #endif
558 	    {
559 	      counters[2] = (intptr_t)new_node;
560 	      success = 1;
561 	    }
562 	}
563       else if (prev_node && !prev_node->next)
564 	{
565 #if GCOV_SUPPORTS_ATOMIC
566 	  if (use_atomic)
567 	    success = !__sync_val_compare_and_swap (&prev_node->next, 0,
568 						    new_node);
569 	  else
570 #endif
571 	    {
572 	      prev_node->next = new_node;
573 	      success = 1;
574 	    }
575 	}
576 
577       /* Increment number of nodes.  */
578       if (success)
579 	gcov_counter_add (&counters[1], 1, use_atomic);
580     }
581 
582   return 0;
583 }
584 
585 #endif /* !inhibit_libc */
586 
587 #endif /* GCC_LIBGCOV_H */
588