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