1/* Routines required for instrumenting a program. */ 2/* Compile this one with gcc. */ 3/* Copyright (C) 1989-2022 Free Software Foundation, Inc. 4 5This file is part of GCC. 6 7GCC is free software; you can redistribute it and/or modify it under 8the terms of the GNU General Public License as published by the Free 9Software Foundation; either version 3, or (at your option) any later 10version. 11 12GCC is distributed in the hope that it will be useful, but WITHOUT ANY 13WARRANTY; without even the implied warranty of MERCHANTABILITY or 14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15for more details. 16 17Under Section 7 of GPL version 3, you are granted additional 18permissions described in the GCC Runtime Library Exception, version 193.1, as published by the Free Software Foundation. 20 21You should have received a copy of the GNU General Public License and 22a copy of the GCC Runtime Library Exception along with this program; 23see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 24<http://www.gnu.org/licenses/>. */ 25 26#include "libgcov.h" 27#include "gcov-io.h" 28 29/* Return 1, if all counter values are zero, otherwise 0. */ 30 31static inline int 32are_all_counters_zero (const struct gcov_ctr_info *ci_ptr) 33{ 34 for (unsigned i = 0; i < ci_ptr->num; i++) 35 if (ci_ptr->values[i] != 0) 36 return 0; 37 38 return 1; 39} 40 41#if defined(inhibit_libc) 42/* If libc and its header files are not available, provide dummy functions. */ 43 44#if defined(L_gcov) 45void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {} 46#endif 47 48#else /* inhibit_libc */ 49 50#if GCOV_LOCKED 51#include <fcntl.h> 52#include <errno.h> 53#include <sys/stat.h> 54#elif GCOV_LOCKED_WITH_LOCKING 55#include <fcntl.h> 56#include <sys/locking.h> 57#include <sys/stat.h> 58#endif 59 60#if HAVE_SYS_MMAN_H 61#include <sys/mman.h> 62#endif 63 64#endif /* inhibit_libc */ 65 66#if defined(L_gcov) && !defined(inhibit_libc) 67#define NEED_L_GCOV 68#endif 69 70#if defined(L_gcov_info_to_gcda) && !IN_GCOV_TOOL 71#define NEED_L_GCOV_INFO_TO_GCDA 72#endif 73 74#ifdef NEED_L_GCOV 75/* A utility function for outputting errors. */ 76static int gcov_error (const char *, ...); 77 78#if !IN_GCOV_TOOL 79static void gcov_error_exit (void); 80#endif 81 82#include "gcov-io.cc" 83 84#define GCOV_PROF_PREFIX "libgcov profiling error:%s:" 85 86struct gcov_fn_buffer 87{ 88 struct gcov_fn_buffer *next; 89 unsigned fn_ix; 90 struct gcov_fn_info info; 91 /* note gcov_fn_info ends in a trailing array. */ 92}; 93 94struct gcov_summary_buffer 95{ 96 struct gcov_summary_buffer *next; 97 struct gcov_summary summary; 98}; 99 100/* A struct that bundles all the related information about the 101 gcda filename. */ 102 103struct gcov_filename 104{ 105 char *filename; /* filename buffer */ 106 int strip; /* leading chars to strip from filename */ 107 char *prefix; /* prefix string */ 108}; 109 110static struct gcov_fn_buffer * 111free_fn_data (const struct gcov_info *gi_ptr, struct gcov_fn_buffer *buffer, 112 unsigned limit) 113{ 114 struct gcov_fn_buffer *next; 115 unsigned ix, n_ctr = 0; 116 117 if (!buffer) 118 return 0; 119 next = buffer->next; 120 121 for (ix = 0; ix != limit; ix++) 122 if (gi_ptr->merge[ix]) 123 free (buffer->info.ctrs[n_ctr++].values); 124 free (buffer); 125 return next; 126} 127 128static struct gcov_fn_buffer ** 129buffer_fn_data (const char *filename, const struct gcov_info *gi_ptr, 130 struct gcov_fn_buffer **end_ptr, unsigned fn_ix) 131{ 132 unsigned n_ctrs = 0, ix = 0; 133 struct gcov_fn_buffer *fn_buffer; 134 unsigned len; 135 136 for (ix = GCOV_COUNTERS; ix--;) 137 if (gi_ptr->merge[ix]) 138 n_ctrs++; 139 140 len = sizeof (*fn_buffer) + sizeof (fn_buffer->info.ctrs[0]) * n_ctrs; 141 fn_buffer = (struct gcov_fn_buffer *) xmalloc (len); 142 143 if (!fn_buffer) 144 goto fail; 145 146 fn_buffer->next = 0; 147 fn_buffer->fn_ix = fn_ix; 148 fn_buffer->info.ident = gcov_read_unsigned (); 149 fn_buffer->info.lineno_checksum = gcov_read_unsigned (); 150 fn_buffer->info.cfg_checksum = gcov_read_unsigned (); 151 152 for (n_ctrs = ix = 0; ix != GCOV_COUNTERS; ix++) 153 { 154 gcov_unsigned_t length; 155 gcov_type *values; 156 157 if (!gi_ptr->merge[ix]) 158 continue; 159 160 if (gcov_read_unsigned () != GCOV_TAG_FOR_COUNTER (ix)) 161 { 162 len = 0; 163 goto fail; 164 } 165 166 length = GCOV_TAG_COUNTER_NUM (gcov_read_unsigned ()); 167 len = length * sizeof (gcov_type); 168 values = (gcov_type *) xmalloc (len); 169 if (!values) 170 goto fail; 171 172 fn_buffer->info.ctrs[n_ctrs].num = length; 173 fn_buffer->info.ctrs[n_ctrs].values = values; 174 175 while (length--) 176 *values++ = gcov_read_counter (); 177 n_ctrs++; 178 } 179 180 *end_ptr = fn_buffer; 181 return &fn_buffer->next; 182 183fail: 184 gcov_error (GCOV_PROF_PREFIX "Function %u %s %u \n", filename, fn_ix, 185 len ? "cannot allocate" : "counter mismatch", len ? len : ix); 186 187 return (struct gcov_fn_buffer **)free_fn_data (gi_ptr, fn_buffer, ix); 188} 189 190/* Convert VERSION into a string description and return the it. 191 BUFFER is used for storage of the string. The code should be 192 aligned wit gcov-iov.c. */ 193 194static char * 195gcov_version_string (char *buffer, char version[4]) 196{ 197 if (version[0] < 'A' || version[0] > 'Z' 198 || version[1] < '0' || version[1] > '9' 199 || version[2] < '0' || version[2] > '9') 200 sprintf (buffer, "(unknown)"); 201 else 202 { 203 unsigned major = 10 * (version[0] - 'A') + (version[1] - '0'); 204 unsigned minor = version[2] - '0'; 205 sprintf (buffer, "%u.%u (%s)", major, minor, 206 version[3] == '*' ? "release" : "experimental"); 207 } 208 return buffer; 209} 210 211/* Check if VERSION of the info block PTR matches libgcov one. 212 Return 1 on success, or zero in case of versions mismatch. 213 If FILENAME is not NULL, its value used for reporting purposes 214 instead of value from the info block. */ 215 216static int 217gcov_version (struct gcov_info *ptr, gcov_unsigned_t version, 218 const char *filename) 219{ 220 if (version != GCOV_VERSION) 221 { 222 char v[4], e[4]; 223 char ver_string[128], expected_string[128]; 224 225 GCOV_UNSIGNED2STRING (v, version); 226 GCOV_UNSIGNED2STRING (e, GCOV_VERSION); 227 228 gcov_error (GCOV_PROF_PREFIX "Version mismatch - expected %s (%.4s) " 229 "got %s (%.4s)\n", 230 filename? filename : ptr->filename, 231 gcov_version_string (expected_string, e), e, 232 gcov_version_string (ver_string, v), v); 233 return 0; 234 } 235 return 1; 236} 237 238/* buffer for the fn_data from another program. */ 239static struct gcov_fn_buffer *fn_buffer; 240 241/* Including system dependent components. */ 242#include "libgcov-driver-system.c" 243 244/* This function merges counters in GI_PTR to an existing gcda file. 245 Return 0 on success. 246 Return -1 on error. In this case, caller will goto read_fatal. */ 247 248static int 249merge_one_data (const char *filename, 250 struct gcov_info *gi_ptr, 251 struct gcov_summary *summary) 252{ 253 gcov_unsigned_t tag, length; 254 unsigned t_ix; 255 int f_ix = -1; 256 int error = 0; 257 struct gcov_fn_buffer **fn_tail = &fn_buffer; 258 259 length = gcov_read_unsigned (); 260 if (!gcov_version (gi_ptr, length, filename)) 261 return -1; 262 263 /* Skip timestamp. */ 264 gcov_read_unsigned (); 265 266 length = gcov_read_unsigned (); 267 if (length != gi_ptr->checksum) 268 { 269 /* Read from a different compilation. Overwrite the file. */ 270 gcov_error (GCOV_PROF_PREFIX "overwriting an existing profile data " 271 "with a different checksum\n", filename); 272 return 0; 273 } 274 275 tag = gcov_read_unsigned (); 276 if (tag != GCOV_TAG_OBJECT_SUMMARY) 277 goto read_mismatch; 278 length = gcov_read_unsigned (); 279 gcc_assert (length > 0); 280 gcov_read_summary (summary); 281 282 tag = gcov_read_unsigned (); 283 /* Merge execution counts for each function. */ 284 for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; 285 f_ix++, tag = gcov_read_unsigned ()) 286 { 287 const struct gcov_ctr_info *ci_ptr; 288 const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; 289 290 if (tag != GCOV_TAG_FUNCTION) 291 goto read_mismatch; 292 293 length = gcov_read_unsigned (); 294 if (!length) 295 /* This function did not appear in the other program. 296 We have nothing to merge. */ 297 continue; 298 299 if (length != GCOV_TAG_FUNCTION_LENGTH) 300 goto read_mismatch; 301 302 if (!gfi_ptr || gfi_ptr->key != gi_ptr) 303 { 304 /* This function appears in the other program. We 305 need to buffer the information in order to write 306 it back out -- we'll be inserting data before 307 this point, so cannot simply keep the data in the 308 file. */ 309 fn_tail = buffer_fn_data (filename, gi_ptr, fn_tail, f_ix); 310 if (!fn_tail) 311 goto read_mismatch; 312 continue; 313 } 314 315 length = gcov_read_unsigned (); 316 if (length != gfi_ptr->ident) 317 goto read_mismatch; 318 319 length = gcov_read_unsigned (); 320 if (length != gfi_ptr->lineno_checksum) 321 goto read_mismatch; 322 323 length = gcov_read_unsigned (); 324 if (length != gfi_ptr->cfg_checksum) 325 goto read_mismatch; 326 327 ci_ptr = gfi_ptr->ctrs; 328 for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++) 329 { 330 gcov_merge_fn merge = gi_ptr->merge[t_ix]; 331 332 if (!merge) 333 continue; 334 335 tag = gcov_read_unsigned (); 336 int read_length = (int)gcov_read_unsigned (); 337 length = abs (read_length); 338 if (tag != GCOV_TAG_FOR_COUNTER (t_ix) 339 || (length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num) 340 && t_ix != GCOV_COUNTER_V_TOPN 341 && t_ix != GCOV_COUNTER_V_INDIR)) 342 goto read_mismatch; 343 /* Merging with all zero counters does not make sense. */ 344 if (read_length > 0) 345 (*merge) (ci_ptr->values, ci_ptr->num); 346 ci_ptr++; 347 } 348 if ((error = gcov_is_error ())) 349 goto read_error; 350 } 351 352 if (tag) 353 { 354 read_mismatch:; 355 gcov_error (GCOV_PROF_PREFIX "Merge mismatch for %s %u\n", 356 filename, f_ix >= 0 ? "function" : "summary", 357 f_ix < 0 ? -1 - f_ix : f_ix); 358 return -1; 359 } 360 return 0; 361 362read_error: 363 gcov_error (GCOV_PROF_PREFIX "%s merging\n", filename, 364 error < 0 ? "Overflow": "Error"); 365 return -1; 366} 367 368/* Write the DATA of LENGTH characters to the gcov file. */ 369 370static void 371gcov_dump_handler (const void *data, 372 unsigned length, 373 void *arg ATTRIBUTE_UNUSED) 374{ 375 gcov_write (data, length); 376} 377 378/* Allocate SIZE characters and return the address of the allocated memory. */ 379 380static void * 381gcov_allocate_handler (unsigned size, void *arg ATTRIBUTE_UNUSED) 382{ 383 return xmalloc (size); 384} 385#endif /* NEED_L_GCOV */ 386 387#if defined(NEED_L_GCOV) || defined(NEED_L_GCOV_INFO_TO_GCDA) 388/* Dump the WORD using the DUMP handler called with ARG. */ 389 390static inline void 391dump_unsigned (gcov_unsigned_t word, 392 void (*dump_fn) (const void *, unsigned, void *), 393 void *arg) 394{ 395 (*dump_fn) (&word, sizeof (word), arg); 396} 397 398/* Dump the COUNTER using the DUMP handler called with ARG. */ 399 400static inline void 401dump_counter (gcov_type counter, 402 void (*dump_fn) (const void *, unsigned, void *), 403 void *arg) 404{ 405 dump_unsigned ((gcov_unsigned_t)counter, dump_fn, arg); 406 407 if (sizeof (counter) > sizeof (gcov_unsigned_t)) 408 dump_unsigned ((gcov_unsigned_t)(counter >> 32), dump_fn, arg); 409 else 410 dump_unsigned (0, dump_fn, arg); 411} 412 413#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) 414 415/* Store all TOP N counters where each has a dynamic length. */ 416 417static void 418write_topn_counters (const struct gcov_ctr_info *ci_ptr, 419 unsigned t_ix, 420 gcov_unsigned_t n_counts, 421 void (*dump_fn) (const void *, unsigned, void *), 422 void *(*allocate_fn)(unsigned, void *), 423 void *arg) 424{ 425 unsigned counters = n_counts / GCOV_TOPN_MEM_COUNTERS; 426 gcc_assert (n_counts % GCOV_TOPN_MEM_COUNTERS == 0); 427 428 /* It can happen in a multi-threaded environment that number of counters is 429 different from the size of the corresponding linked lists. */ 430#define LIST_SIZE_MIN_LENGTH 4 * 1024 431 432 static unsigned *list_sizes = NULL; 433 static unsigned list_size_length = 0; 434 435 if (list_sizes == NULL || counters > list_size_length) 436 { 437 list_size_length = MAX (LIST_SIZE_MIN_LENGTH, 2 * counters); 438#if !defined(inhibit_libc) && HAVE_SYS_MMAN_H 439 list_sizes 440 = (unsigned *)malloc_mmap (list_size_length * sizeof (unsigned)); 441#endif 442 443 /* Malloc fallback. */ 444 if (list_sizes == NULL) 445 list_sizes = 446 (unsigned *)(*allocate_fn) (list_size_length * sizeof (unsigned), 447 arg); 448 } 449 450 unsigned pair_total = 0; 451 452 for (unsigned i = 0; i < counters; i++) 453 { 454 gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2]; 455 unsigned sizes = 0; 456 457 for (struct gcov_kvp *node = (struct gcov_kvp *)(__INTPTR_TYPE__)start; 458 node != NULL; node = node->next) 459 ++sizes; 460 461 pair_total += sizes; 462 list_sizes[i] = sizes; 463 } 464 465 unsigned disk_size = GCOV_TOPN_DISK_COUNTERS * counters + 2 * pair_total; 466 dump_unsigned (GCOV_TAG_FOR_COUNTER (t_ix), dump_fn, arg), 467 dump_unsigned (GCOV_TAG_COUNTER_LENGTH (disk_size), dump_fn, arg); 468 469 for (unsigned i = 0; i < counters; i++) 470 { 471 dump_counter (ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i], dump_fn, arg); 472 dump_counter (list_sizes[i], dump_fn, arg); 473 gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2]; 474 475 unsigned j = 0; 476 for (struct gcov_kvp *node = (struct gcov_kvp *)(__INTPTR_TYPE__)start; 477 j < list_sizes[i]; node = node->next, j++) 478 { 479 dump_counter (node->value, dump_fn, arg); 480 dump_counter (node->count, dump_fn, arg); 481 } 482 } 483} 484 485/* Write counters in GI_PTR and the summary in PRG to a gcda file. In 486 the case of appending to an existing file, SUMMARY_POS will be non-zero. 487 We will write the file starting from SUMMAY_POS. */ 488 489static void 490write_one_data (const struct gcov_info *gi_ptr, 491 const struct gcov_summary *prg_p ATTRIBUTE_UNUSED, 492 void (*dump_fn) (const void *, unsigned, void *), 493 void *(*allocate_fn) (unsigned, void *), 494 void *arg) 495{ 496 unsigned f_ix; 497 498 dump_unsigned (GCOV_DATA_MAGIC, dump_fn, arg); 499 dump_unsigned (GCOV_VERSION, dump_fn, arg); 500 dump_unsigned (gi_ptr->stamp, dump_fn, arg); 501 dump_unsigned (gi_ptr->checksum, dump_fn, arg); 502 503#ifdef NEED_L_GCOV 504 /* Generate whole program statistics. */ 505 gcov_write_summary (GCOV_TAG_OBJECT_SUMMARY, prg_p); 506#endif 507 508 /* Write execution counts for each function. */ 509 for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++) 510 { 511#ifdef NEED_L_GCOV 512 unsigned buffered = 0; 513#endif 514 const struct gcov_fn_info *gfi_ptr; 515 const struct gcov_ctr_info *ci_ptr; 516 gcov_unsigned_t length; 517 unsigned t_ix; 518 519#ifdef NEED_L_GCOV 520 if (fn_buffer && fn_buffer->fn_ix == f_ix) 521 { 522 /* Buffered data from another program. */ 523 buffered = 1; 524 gfi_ptr = &fn_buffer->info; 525 length = GCOV_TAG_FUNCTION_LENGTH; 526 } 527 else 528#endif 529 { 530 gfi_ptr = gi_ptr->functions[f_ix]; 531 if (gfi_ptr && gfi_ptr->key == gi_ptr) 532 length = GCOV_TAG_FUNCTION_LENGTH; 533 else 534 length = 0; 535 } 536 537 dump_unsigned (GCOV_TAG_FUNCTION, dump_fn, arg); 538 dump_unsigned (length, dump_fn, arg); 539 if (!length) 540 continue; 541 542 dump_unsigned (gfi_ptr->ident, dump_fn, arg); 543 dump_unsigned (gfi_ptr->lineno_checksum, dump_fn, arg); 544 dump_unsigned (gfi_ptr->cfg_checksum, dump_fn, arg); 545 546 ci_ptr = gfi_ptr->ctrs; 547 for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++) 548 { 549 gcov_position_t n_counts; 550 551 if (!gi_ptr->merge[t_ix]) 552 continue; 553 554 n_counts = ci_ptr->num; 555 556 if (t_ix == GCOV_COUNTER_V_TOPN || t_ix == GCOV_COUNTER_V_INDIR) 557 write_topn_counters (ci_ptr, t_ix, n_counts, dump_fn, allocate_fn, 558 arg); 559 else 560 { 561 dump_unsigned (GCOV_TAG_FOR_COUNTER (t_ix), dump_fn, arg); 562 if (are_all_counters_zero (ci_ptr)) 563 /* Do not stream when all counters are zero. */ 564 dump_unsigned (GCOV_TAG_COUNTER_LENGTH (-n_counts), 565 dump_fn, arg); 566 else 567 { 568 dump_unsigned (GCOV_TAG_COUNTER_LENGTH (n_counts), 569 dump_fn, arg); 570 for (unsigned i = 0; i < n_counts; i++) 571 dump_counter (ci_ptr->values[i], dump_fn, arg); 572 } 573 } 574 575 ci_ptr++; 576 } 577#ifdef NEED_L_GCOV 578 if (buffered) 579 fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS); 580#endif 581 } 582 583 dump_unsigned (0, dump_fn, arg); 584} 585#endif /* NEED_L_GCOV || NEED_L_GCOV_INFO_TO_GCDA */ 586 587#ifdef NEED_L_GCOV 588/* Dump the coverage counts for one gcov_info object. We merge with existing 589 counts when possible, to avoid growing the .da files ad infinitum. We use 590 this program's checksum to make sure we only accumulate whole program 591 statistics to the correct summary. An object file might be embedded 592 in two separate programs, and we must keep the two program 593 summaries separate. */ 594 595static void 596dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf, 597 unsigned run_counted ATTRIBUTE_UNUSED, 598 gcov_type run_max ATTRIBUTE_UNUSED) 599{ 600 struct gcov_summary summary = {}; 601 int error; 602 gcov_unsigned_t tag; 603 fn_buffer = 0; 604 605 error = gcov_exit_open_gcda_file (gi_ptr, gf); 606 if (error == -1) 607 return; 608 609 tag = gcov_read_unsigned (); 610 if (tag) 611 { 612 /* Merge data from file. */ 613 if (tag != GCOV_DATA_MAGIC) 614 { 615 gcov_error (GCOV_PROF_PREFIX "Not a gcov data file\n", 616 gf->filename); 617 goto read_fatal; 618 } 619 error = merge_one_data (gf->filename, gi_ptr, &summary); 620 if (error == -1) 621 goto read_fatal; 622 } 623 624 gcov_rewrite (); 625 626#if !IN_GCOV_TOOL 627 if (!run_counted) 628 { 629 summary.runs++; 630 summary.sum_max += run_max; 631 } 632#else 633 summary = gi_ptr->summary; 634#endif 635 636 write_one_data (gi_ptr, &summary, gcov_dump_handler, gcov_allocate_handler, 637 NULL); 638 /* fall through */ 639 640read_fatal:; 641 while (fn_buffer) 642 fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS); 643 644 if ((error = gcov_close ())) 645 gcov_error ((error < 0 ? GCOV_PROF_PREFIX "Overflow writing\n" 646 : GCOV_PROF_PREFIX "Error writing\n"), gf->filename); 647} 648 649 650/* Dump all the coverage counts for the program. It first computes program 651 summary and then traverses gcov_list list and dumps the gcov_info 652 objects one by one. */ 653 654#if !IN_GCOV_TOOL 655static 656#endif 657void 658gcov_do_dump (struct gcov_info *list, int run_counted) 659{ 660 struct gcov_info *gi_ptr; 661 struct gcov_filename gf; 662 663 /* Compute run_max of this program run. */ 664 gcov_type run_max = 0; 665 for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next) 666 for (unsigned f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++) 667 { 668 const struct gcov_ctr_info *cinfo 669 = &gi_ptr->functions[f_ix]->ctrs[GCOV_COUNTER_ARCS]; 670 671 for (unsigned i = 0; i < cinfo->num; i++) 672 if (run_max < cinfo->values[i]) 673 run_max = cinfo->values[i]; 674 } 675 676 allocate_filename_struct (&gf); 677 678 /* Now merge each file. */ 679 for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next) 680 { 681 dump_one_gcov (gi_ptr, &gf, run_counted, run_max); 682 free (gf.filename); 683 } 684 685 free (gf.prefix); 686} 687 688#if IN_GCOV_TOOL 689const char * 690__attribute__ ((unused)) 691gcov_get_filename (struct gcov_info *list) 692{ 693 return list->filename; 694} 695#endif 696 697#if !IN_GCOV_TOOL 698void 699__gcov_dump_one (struct gcov_root *root) 700{ 701 if (root->dumped) 702 return; 703 704 gcov_do_dump (root->list, root->run_counted); 705 706 root->dumped = 1; 707 root->run_counted = 1; 708} 709 710/* Per-dynamic-object gcov state. */ 711struct gcov_root __gcov_root; 712 713/* Exactly one of these will be live in the process image. */ 714struct gcov_master __gcov_master = 715 {GCOV_VERSION, 0}; 716 717/* Dynamic pool for gcov_kvp structures. */ 718struct gcov_kvp *__gcov_kvp_dynamic_pool; 719 720/* Index into __gcov_kvp_dynamic_pool array. */ 721unsigned __gcov_kvp_dynamic_pool_index; 722 723/* Size of _gcov_kvp_dynamic_pool array. */ 724unsigned __gcov_kvp_dynamic_pool_size; 725 726void 727__gcov_exit (void) 728{ 729 __gcov_dump_one (&__gcov_root); 730 if (__gcov_root.next) 731 __gcov_root.next->prev = __gcov_root.prev; 732 if (__gcov_root.prev) 733 __gcov_root.prev->next = __gcov_root.next; 734 else 735 __gcov_master.root = __gcov_root.next; 736 737 gcov_error_exit (); 738} 739 740/* Add a new object file onto the bb chain. Invoked automatically 741 when running an object file's global ctors. */ 742 743void 744__gcov_init (struct gcov_info *info) 745{ 746 if (!info->version || !info->n_functions) 747 return; 748 if (gcov_version (info, info->version, 0)) 749 { 750 if (!__gcov_root.list) 751 { 752 /* Add to master list and at exit function. */ 753 if (gcov_version (NULL, __gcov_master.version, "<master>")) 754 { 755 __gcov_root.next = __gcov_master.root; 756 if (__gcov_master.root) 757 __gcov_master.root->prev = &__gcov_root; 758 __gcov_master.root = &__gcov_root; 759 } 760 } 761 762 info->next = __gcov_root.list; 763 __gcov_root.list = info; 764 } 765} 766#endif /* !IN_GCOV_TOOL */ 767#endif /* NEED_L_GCOV */ 768 769#ifdef NEED_L_GCOV_INFO_TO_GCDA 770/* Convert the gcov info to a gcda data stream. It is intended for 771 free-standing environments which do not support the C library file I/O. */ 772 773void 774__gcov_info_to_gcda (const struct gcov_info *gi_ptr, 775 void (*filename_fn) (const char *, void *), 776 void (*dump_fn) (const void *, unsigned, void *), 777 void *(*allocate_fn) (unsigned, void *), 778 void *arg) 779{ 780 (*filename_fn) (gi_ptr->filename, arg); 781 write_one_data (gi_ptr, NULL, dump_fn, allocate_fn, arg); 782} 783#endif /* NEED_L_GCOV_INFO_TO_GCDA */ 784