]> git.ipfire.org Git - thirdparty/gcc.git/blob - libgcc/libgcov-driver.c
Reset PHI base0 flag if it's clear in any argument [PR101977, ...]
[thirdparty/gcc.git] / libgcc / libgcov-driver.c
1 /* Routines required for instrumenting a program. */
2 /* Compile this one with gcc. */
3 /* Copyright (C) 1989-2021 Free Software Foundation, Inc.
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
26 #include "libgcov.h"
27 #include "gcov-io.h"
28
29 /* Return 1, if all counter values are zero, otherwise 0. */
30
31 static inline int
32 are_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)
45 void __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. */
76 static int gcov_error (const char *, ...);
77
78 #if !IN_GCOV_TOOL
79 static void gcov_error_exit (void);
80 #endif
81
82 #include "gcov-io.c"
83
84 #define GCOV_PROF_PREFIX "libgcov profiling error:%s:"
85
86 struct 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
94 struct 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
103 struct gcov_filename
104 {
105 char *filename; /* filename buffer */
106 int strip; /* leading chars to strip from filename */
107 char *prefix; /* prefix string */
108 };
109
110 static struct gcov_fn_buffer *
111 free_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
128 static struct gcov_fn_buffer **
129 buffer_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
183 fail:
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
194 static char *
195 gcov_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
216 static int
217 gcov_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. */
239 static 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
248 static int
249 merge_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 length = gcov_read_unsigned ();
264 if (length != gi_ptr->stamp)
265 {
266 /* Read from a different compilation. Overwrite the file. */
267 gcov_error (GCOV_PROF_PREFIX "overwriting an existing profile data "
268 "with a different timestamp\n", filename);
269 return 0;
270 }
271
272 tag = gcov_read_unsigned ();
273 if (tag != GCOV_TAG_OBJECT_SUMMARY)
274 goto read_mismatch;
275 length = gcov_read_unsigned ();
276 gcc_assert (length > 0);
277 gcov_read_summary (summary);
278
279 tag = gcov_read_unsigned ();
280 /* Merge execution counts for each function. */
281 for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions;
282 f_ix++, tag = gcov_read_unsigned ())
283 {
284 const struct gcov_ctr_info *ci_ptr;
285 const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
286
287 if (tag != GCOV_TAG_FUNCTION)
288 goto read_mismatch;
289
290 length = gcov_read_unsigned ();
291 if (!length)
292 /* This function did not appear in the other program.
293 We have nothing to merge. */
294 continue;
295
296 if (length != GCOV_TAG_FUNCTION_LENGTH)
297 goto read_mismatch;
298
299 if (!gfi_ptr || gfi_ptr->key != gi_ptr)
300 {
301 /* This function appears in the other program. We
302 need to buffer the information in order to write
303 it back out -- we'll be inserting data before
304 this point, so cannot simply keep the data in the
305 file. */
306 fn_tail = buffer_fn_data (filename, gi_ptr, fn_tail, f_ix);
307 if (!fn_tail)
308 goto read_mismatch;
309 continue;
310 }
311
312 length = gcov_read_unsigned ();
313 if (length != gfi_ptr->ident)
314 goto read_mismatch;
315
316 length = gcov_read_unsigned ();
317 if (length != gfi_ptr->lineno_checksum)
318 goto read_mismatch;
319
320 length = gcov_read_unsigned ();
321 if (length != gfi_ptr->cfg_checksum)
322 goto read_mismatch;
323
324 ci_ptr = gfi_ptr->ctrs;
325 for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
326 {
327 gcov_merge_fn merge = gi_ptr->merge[t_ix];
328
329 if (!merge)
330 continue;
331
332 tag = gcov_read_unsigned ();
333 int read_length = (int)gcov_read_unsigned ();
334 length = abs (read_length);
335 if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
336 || (length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num)
337 && t_ix != GCOV_COUNTER_V_TOPN
338 && t_ix != GCOV_COUNTER_V_INDIR))
339 goto read_mismatch;
340 /* Merging with all zero counters does not make sense. */
341 if (read_length > 0)
342 (*merge) (ci_ptr->values, ci_ptr->num);
343 ci_ptr++;
344 }
345 if ((error = gcov_is_error ()))
346 goto read_error;
347 }
348
349 if (tag)
350 {
351 read_mismatch:;
352 gcov_error (GCOV_PROF_PREFIX "Merge mismatch for %s %u\n",
353 filename, f_ix >= 0 ? "function" : "summary",
354 f_ix < 0 ? -1 - f_ix : f_ix);
355 return -1;
356 }
357 return 0;
358
359 read_error:
360 gcov_error (GCOV_PROF_PREFIX "%s merging\n", filename,
361 error < 0 ? "Overflow": "Error");
362 return -1;
363 }
364
365 /* Write the DATA of LENGTH characters to the gcov file. */
366
367 static void
368 gcov_dump_handler (const void *data,
369 unsigned length,
370 void *arg ATTRIBUTE_UNUSED)
371 {
372 gcov_write (data, length);
373 }
374
375 /* Allocate SIZE characters and return the address of the allocated memory. */
376
377 static void *
378 gcov_allocate_handler (unsigned size, void *arg ATTRIBUTE_UNUSED)
379 {
380 return xmalloc (size);
381 }
382 #endif /* NEED_L_GCOV */
383
384 #if defined(NEED_L_GCOV) || defined(NEED_L_GCOV_INFO_TO_GCDA)
385 /* Dump the WORD using the DUMP handler called with ARG. */
386
387 static inline void
388 dump_unsigned (gcov_unsigned_t word,
389 void (*dump_fn) (const void *, unsigned, void *),
390 void *arg)
391 {
392 (*dump_fn) (&word, sizeof (word), arg);
393 }
394
395 /* Dump the COUNTER using the DUMP handler called with ARG. */
396
397 static inline void
398 dump_counter (gcov_type counter,
399 void (*dump_fn) (const void *, unsigned, void *),
400 void *arg)
401 {
402 dump_unsigned ((gcov_unsigned_t)counter, dump_fn, arg);
403
404 if (sizeof (counter) > sizeof (gcov_unsigned_t))
405 dump_unsigned ((gcov_unsigned_t)(counter >> 32), dump_fn, arg);
406 else
407 dump_unsigned (0, dump_fn, arg);
408 }
409
410 #define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
411
412 /* Store all TOP N counters where each has a dynamic length. */
413
414 static void
415 write_topn_counters (const struct gcov_ctr_info *ci_ptr,
416 unsigned t_ix,
417 gcov_unsigned_t n_counts,
418 void (*dump_fn) (const void *, unsigned, void *),
419 void *(*allocate_fn)(unsigned, void *),
420 void *arg)
421 {
422 unsigned counters = n_counts / GCOV_TOPN_MEM_COUNTERS;
423 gcc_assert (n_counts % GCOV_TOPN_MEM_COUNTERS == 0);
424
425 /* It can happen in a multi-threaded environment that number of counters is
426 different from the size of the corresponding linked lists. */
427 #define LIST_SIZE_MIN_LENGTH 4 * 1024
428
429 static unsigned *list_sizes = NULL;
430 static unsigned list_size_length = 0;
431
432 if (list_sizes == NULL || counters > list_size_length)
433 {
434 list_size_length = MAX (LIST_SIZE_MIN_LENGTH, 2 * counters);
435 #if !defined(inhibit_libc) && HAVE_SYS_MMAN_H
436 list_sizes
437 = (unsigned *)malloc_mmap (list_size_length * sizeof (unsigned));
438 #endif
439
440 /* Malloc fallback. */
441 if (list_sizes == NULL)
442 list_sizes =
443 (unsigned *)(*allocate_fn) (list_size_length * sizeof (unsigned),
444 arg);
445 }
446
447 unsigned pair_total = 0;
448
449 for (unsigned i = 0; i < counters; i++)
450 {
451 gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
452 unsigned sizes = 0;
453
454 for (struct gcov_kvp *node = (struct gcov_kvp *)(__INTPTR_TYPE__)start;
455 node != NULL; node = node->next)
456 ++sizes;
457
458 pair_total += sizes;
459 list_sizes[i] = sizes;
460 }
461
462 unsigned disk_size = GCOV_TOPN_DISK_COUNTERS * counters + 2 * pair_total;
463 dump_unsigned (GCOV_TAG_FOR_COUNTER (t_ix), dump_fn, arg),
464 dump_unsigned (GCOV_TAG_COUNTER_LENGTH (disk_size), dump_fn, arg);
465
466 for (unsigned i = 0; i < counters; i++)
467 {
468 dump_counter (ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i], dump_fn, arg);
469 dump_counter (list_sizes[i], dump_fn, arg);
470 gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
471
472 unsigned j = 0;
473 for (struct gcov_kvp *node = (struct gcov_kvp *)(__INTPTR_TYPE__)start;
474 j < list_sizes[i]; node = node->next, j++)
475 {
476 dump_counter (node->value, dump_fn, arg);
477 dump_counter (node->count, dump_fn, arg);
478 }
479 }
480 }
481
482 /* Write counters in GI_PTR and the summary in PRG to a gcda file. In
483 the case of appending to an existing file, SUMMARY_POS will be non-zero.
484 We will write the file starting from SUMMAY_POS. */
485
486 static void
487 write_one_data (const struct gcov_info *gi_ptr,
488 const struct gcov_summary *prg_p ATTRIBUTE_UNUSED,
489 void (*dump_fn) (const void *, unsigned, void *),
490 void *(*allocate_fn) (unsigned, void *),
491 void *arg)
492 {
493 unsigned f_ix;
494
495 dump_unsigned (GCOV_DATA_MAGIC, dump_fn, arg);
496 dump_unsigned (GCOV_VERSION, dump_fn, arg);
497 dump_unsigned (gi_ptr->stamp, dump_fn, arg);
498
499 #ifdef NEED_L_GCOV
500 /* Generate whole program statistics. */
501 gcov_write_summary (GCOV_TAG_OBJECT_SUMMARY, prg_p);
502 #endif
503
504 /* Write execution counts for each function. */
505 for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
506 {
507 #ifdef NEED_L_GCOV
508 unsigned buffered = 0;
509 #endif
510 const struct gcov_fn_info *gfi_ptr;
511 const struct gcov_ctr_info *ci_ptr;
512 gcov_unsigned_t length;
513 unsigned t_ix;
514
515 #ifdef NEED_L_GCOV
516 if (fn_buffer && fn_buffer->fn_ix == f_ix)
517 {
518 /* Buffered data from another program. */
519 buffered = 1;
520 gfi_ptr = &fn_buffer->info;
521 length = GCOV_TAG_FUNCTION_LENGTH;
522 }
523 else
524 #endif
525 {
526 gfi_ptr = gi_ptr->functions[f_ix];
527 if (gfi_ptr && gfi_ptr->key == gi_ptr)
528 length = GCOV_TAG_FUNCTION_LENGTH;
529 else
530 length = 0;
531 }
532
533 dump_unsigned (GCOV_TAG_FUNCTION, dump_fn, arg);
534 dump_unsigned (length, dump_fn, arg);
535 if (!length)
536 continue;
537
538 dump_unsigned (gfi_ptr->ident, dump_fn, arg);
539 dump_unsigned (gfi_ptr->lineno_checksum, dump_fn, arg);
540 dump_unsigned (gfi_ptr->cfg_checksum, dump_fn, arg);
541
542 ci_ptr = gfi_ptr->ctrs;
543 for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
544 {
545 gcov_position_t n_counts;
546
547 if (!gi_ptr->merge[t_ix])
548 continue;
549
550 n_counts = ci_ptr->num;
551
552 if (t_ix == GCOV_COUNTER_V_TOPN || t_ix == GCOV_COUNTER_V_INDIR)
553 write_topn_counters (ci_ptr, t_ix, n_counts, dump_fn, allocate_fn,
554 arg);
555 else
556 {
557 dump_unsigned (GCOV_TAG_FOR_COUNTER (t_ix), dump_fn, arg);
558 if (are_all_counters_zero (ci_ptr))
559 /* Do not stream when all counters are zero. */
560 dump_unsigned (GCOV_TAG_COUNTER_LENGTH (-n_counts),
561 dump_fn, arg);
562 else
563 {
564 dump_unsigned (GCOV_TAG_COUNTER_LENGTH (n_counts),
565 dump_fn, arg);
566 for (unsigned i = 0; i < n_counts; i++)
567 dump_counter (ci_ptr->values[i], dump_fn, arg);
568 }
569 }
570
571 ci_ptr++;
572 }
573 #ifdef NEED_L_GCOV
574 if (buffered)
575 fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
576 #endif
577 }
578
579 dump_unsigned (0, dump_fn, arg);
580 }
581 #endif /* NEED_L_GCOV || NEED_L_GCOV_INFO_TO_GCDA */
582
583 #ifdef NEED_L_GCOV
584 /* Dump the coverage counts for one gcov_info object. We merge with existing
585 counts when possible, to avoid growing the .da files ad infinitum. We use
586 this program's checksum to make sure we only accumulate whole program
587 statistics to the correct summary. An object file might be embedded
588 in two separate programs, and we must keep the two program
589 summaries separate. */
590
591 static void
592 dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf,
593 unsigned run_counted ATTRIBUTE_UNUSED,
594 gcov_type run_max ATTRIBUTE_UNUSED)
595 {
596 struct gcov_summary summary = {};
597 int error;
598 gcov_unsigned_t tag;
599 fn_buffer = 0;
600
601 error = gcov_exit_open_gcda_file (gi_ptr, gf);
602 if (error == -1)
603 return;
604
605 tag = gcov_read_unsigned ();
606 if (tag)
607 {
608 /* Merge data from file. */
609 if (tag != GCOV_DATA_MAGIC)
610 {
611 gcov_error (GCOV_PROF_PREFIX "Not a gcov data file\n",
612 gf->filename);
613 goto read_fatal;
614 }
615 error = merge_one_data (gf->filename, gi_ptr, &summary);
616 if (error == -1)
617 goto read_fatal;
618 }
619
620 gcov_rewrite ();
621
622 #if !IN_GCOV_TOOL
623 if (!run_counted)
624 {
625 summary.runs++;
626 summary.sum_max += run_max;
627 }
628 #else
629 summary = gi_ptr->summary;
630 #endif
631
632 write_one_data (gi_ptr, &summary, gcov_dump_handler, gcov_allocate_handler,
633 NULL);
634 /* fall through */
635
636 read_fatal:;
637 while (fn_buffer)
638 fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
639
640 if ((error = gcov_close ()))
641 gcov_error ((error < 0 ? GCOV_PROF_PREFIX "Overflow writing\n"
642 : GCOV_PROF_PREFIX "Error writing\n"), gf->filename);
643 }
644
645
646 /* Dump all the coverage counts for the program. It first computes program
647 summary and then traverses gcov_list list and dumps the gcov_info
648 objects one by one. */
649
650 #if !IN_GCOV_TOOL
651 static
652 #endif
653 void
654 gcov_do_dump (struct gcov_info *list, int run_counted)
655 {
656 struct gcov_info *gi_ptr;
657 struct gcov_filename gf;
658
659 /* Compute run_max of this program run. */
660 gcov_type run_max = 0;
661 for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next)
662 for (unsigned f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
663 {
664 const struct gcov_ctr_info *cinfo
665 = &gi_ptr->functions[f_ix]->ctrs[GCOV_COUNTER_ARCS];
666
667 for (unsigned i = 0; i < cinfo->num; i++)
668 if (run_max < cinfo->values[i])
669 run_max = cinfo->values[i];
670 }
671
672 allocate_filename_struct (&gf);
673
674 /* Now merge each file. */
675 for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next)
676 {
677 dump_one_gcov (gi_ptr, &gf, run_counted, run_max);
678 free (gf.filename);
679 }
680
681 free (gf.prefix);
682 }
683
684 #if IN_GCOV_TOOL
685 const char *
686 __attribute__ ((unused))
687 gcov_get_filename (struct gcov_info *list)
688 {
689 return list->filename;
690 }
691 #endif
692
693 #if !IN_GCOV_TOOL
694 void
695 __gcov_dump_one (struct gcov_root *root)
696 {
697 if (root->dumped)
698 return;
699
700 gcov_do_dump (root->list, root->run_counted);
701
702 root->dumped = 1;
703 root->run_counted = 1;
704 }
705
706 /* Per-dynamic-object gcov state. */
707 struct gcov_root __gcov_root;
708
709 /* Exactly one of these will be live in the process image. */
710 struct gcov_master __gcov_master =
711 {GCOV_VERSION, 0};
712
713 /* Dynamic pool for gcov_kvp structures. */
714 struct gcov_kvp *__gcov_kvp_dynamic_pool;
715
716 /* Index into __gcov_kvp_dynamic_pool array. */
717 unsigned __gcov_kvp_dynamic_pool_index;
718
719 /* Size of _gcov_kvp_dynamic_pool array. */
720 unsigned __gcov_kvp_dynamic_pool_size;
721
722 void
723 __gcov_exit (void)
724 {
725 __gcov_dump_one (&__gcov_root);
726 if (__gcov_root.next)
727 __gcov_root.next->prev = __gcov_root.prev;
728 if (__gcov_root.prev)
729 __gcov_root.prev->next = __gcov_root.next;
730 else
731 __gcov_master.root = __gcov_root.next;
732
733 gcov_error_exit ();
734 }
735
736 /* Add a new object file onto the bb chain. Invoked automatically
737 when running an object file's global ctors. */
738
739 void
740 __gcov_init (struct gcov_info *info)
741 {
742 if (!info->version || !info->n_functions)
743 return;
744 if (gcov_version (info, info->version, 0))
745 {
746 if (!__gcov_root.list)
747 {
748 /* Add to master list and at exit function. */
749 if (gcov_version (NULL, __gcov_master.version, "<master>"))
750 {
751 __gcov_root.next = __gcov_master.root;
752 if (__gcov_master.root)
753 __gcov_master.root->prev = &__gcov_root;
754 __gcov_master.root = &__gcov_root;
755 }
756 }
757
758 info->next = __gcov_root.list;
759 __gcov_root.list = info;
760 }
761 }
762 #endif /* !IN_GCOV_TOOL */
763 #endif /* NEED_L_GCOV */
764
765 #ifdef NEED_L_GCOV_INFO_TO_GCDA
766 /* Convert the gcov info to a gcda data stream. It is intended for
767 free-standing environments which do not support the C library file I/O. */
768
769 void
770 __gcov_info_to_gcda (const struct gcov_info *gi_ptr,
771 void (*filename_fn) (const char *, void *),
772 void (*dump_fn) (const void *, unsigned, void *),
773 void *(*allocate_fn) (unsigned, void *),
774 void *arg)
775 {
776 (*filename_fn) (gi_ptr->filename, arg);
777 write_one_data (gi_ptr, NULL, dump_fn, allocate_fn, arg);
778 }
779 #endif /* NEED_L_GCOV_INFO_TO_GCDA */