xref: /netbsd-src/external/gpl3/gcc/dist/libgcc/libgcov-driver.c (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /* Routines required for instrumenting a program.  */
2 /* Compile this one with gcc.  */
3 /* Copyright (C) 1989-2022 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
are_all_counters_zero(const struct gcov_ctr_info * ci_ptr)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)
__gcov_init(struct gcov_info * p)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.cc"
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 *
free_fn_data(const struct gcov_info * gi_ptr,struct gcov_fn_buffer * buffer,unsigned limit)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 **
buffer_fn_data(const char * filename,const struct gcov_info * gi_ptr,struct gcov_fn_buffer ** end_ptr,unsigned fn_ix)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 *
gcov_version_string(char * buffer,char version[4])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
gcov_version(struct gcov_info * ptr,gcov_unsigned_t version,const char * filename)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
merge_one_data(const char * filename,struct gcov_info * gi_ptr,struct gcov_summary * summary)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   /* 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 
362 read_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 
370 static void
gcov_dump_handler(const void * data,unsigned length,void * arg ATTRIBUTE_UNUSED)371 gcov_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 
380 static void *
gcov_allocate_handler(unsigned size,void * arg ATTRIBUTE_UNUSED)381 gcov_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 
390 static inline void
dump_unsigned(gcov_unsigned_t word,void (* dump_fn)(const void *,unsigned,void *),void * arg)391 dump_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 
400 static inline void
dump_counter(gcov_type counter,void (* dump_fn)(const void *,unsigned,void *),void * arg)401 dump_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 
417 static void
write_topn_counters(const struct gcov_ctr_info * ci_ptr,unsigned t_ix,gcov_unsigned_t n_counts,void (* dump_fn)(const void *,unsigned,void *),void * (* allocate_fn)(unsigned,void *),void * arg)418 write_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 
489 static void
write_one_data(const struct gcov_info * gi_ptr,const struct gcov_summary * prg_p ATTRIBUTE_UNUSED,void (* dump_fn)(const void *,unsigned,void *),void * (* allocate_fn)(unsigned,void *),void * arg)490 write_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 
595 static void
dump_one_gcov(struct gcov_info * gi_ptr,struct gcov_filename * gf,unsigned run_counted ATTRIBUTE_UNUSED,gcov_type run_max ATTRIBUTE_UNUSED)596 dump_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 
640 read_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
655 static
656 #endif
657 void
gcov_do_dump(struct gcov_info * list,int run_counted)658 gcov_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
689 const char *
690 __attribute__ ((unused))
gcov_get_filename(struct gcov_info * list)691 gcov_get_filename (struct gcov_info *list)
692 {
693   return list->filename;
694 }
695 #endif
696 
697 #if !IN_GCOV_TOOL
698 void
__gcov_dump_one(struct gcov_root * root)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.  */
711 struct gcov_root __gcov_root;
712 
713 /* Exactly one of these will be live in the process image.  */
714 struct gcov_master __gcov_master =
715   {GCOV_VERSION, 0};
716 
717 /* Dynamic pool for gcov_kvp structures.  */
718 struct gcov_kvp *__gcov_kvp_dynamic_pool;
719 
720 /* Index into __gcov_kvp_dynamic_pool array.  */
721 unsigned __gcov_kvp_dynamic_pool_index;
722 
723 /* Size of _gcov_kvp_dynamic_pool array.  */
724 unsigned __gcov_kvp_dynamic_pool_size;
725 
726 void
__gcov_exit(void)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 
743 void
__gcov_init(struct gcov_info * info)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 
773 void
__gcov_info_to_gcda(const struct gcov_info * gi_ptr,void (* filename_fn)(const char *,void *),void (* dump_fn)(const void *,unsigned,void *),void * (* allocate_fn)(unsigned,void *),void * arg)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