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