]> git.ipfire.org Git - thirdparty/gcc.git/blame - libgcc/libgcov-driver.c
i386: Cleanup ix86_expand_{unary|binary}_operator issues
[thirdparty/gcc.git] / libgcc / libgcov-driver.c
CommitLineData
d6d3f033
RX
1/* Routines required for instrumenting a program. */
2/* Compile this one with gcc. */
83ffe9cd 3/* Copyright (C) 1989-2023 Free Software Foundation, Inc.
d6d3f033
RX
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
40d6b753 26#include "libgcov.h"
512cc015 27#include "gcov-io.h"
d6d3f033 28
9124bbe1
SH
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
d6d3f033
RX
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
d6d3f033
RX
50#if GCOV_LOCKED
51#include <fcntl.h>
52#include <errno.h>
53#include <sys/stat.h>
9ec469f5
EB
54#elif GCOV_LOCKED_WITH_LOCKING
55#include <fcntl.h>
56#include <sys/locking.h>
57#include <sys/stat.h>
d6d3f033
RX
58#endif
59
6a8fc0c3
ML
60#if HAVE_SYS_MMAN_H
61#include <sys/mman.h>
62#endif
63
9124bbe1
SH
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
e3f0315f 73
9124bbe1 74#ifdef NEED_L_GCOV
8aa5bdd6 75/* A utility function for outputting errors. */
e3f0315f
TJ
76static int gcov_error (const char *, ...);
77
8aa5bdd6
AC
78#if !IN_GCOV_TOOL
79static void gcov_error_exit (void);
80#endif
81
cd9912b5 82#include "gcov-io.cc"
d6d3f033 83
d273c40a
ML
84#define GCOV_PROF_PREFIX "libgcov profiling error:%s:"
85
d6d3f033
RX
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
6dc33097
NS
100/* A struct that bundles all the related information about the
101 gcda filename. */
102
103struct gcov_filename
104{
105 char *filename; /* filename buffer */
6dc33097 106 int strip; /* leading chars to strip from filename */
6c086e8c 107 char *prefix; /* prefix string */
6dc33097
NS
108};
109
d6d3f033
RX
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;
40d6b753 141 fn_buffer = (struct gcov_fn_buffer *) xmalloc (len);
d6d3f033
RX
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);
40d6b753 168 values = (gcov_type *) xmalloc (len);
d6d3f033
RX
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:
d273c40a 184 gcov_error (GCOV_PROF_PREFIX "Function %u %s %u \n", filename, fn_ix,
d6d3f033
RX
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
be9d9fdb
ML
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
d6d3f033
RX
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];
e3a682f4 223 char ver_string[128], expected_string[128];
d6d3f033
RX
224
225 GCOV_UNSIGNED2STRING (v, version);
226 GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
227
d273c40a 228 gcov_error (GCOV_PROF_PREFIX "Version mismatch - expected %s (%.4s) "
be9d9fdb
ML
229 "got %s (%.4s)\n",
230 filename? filename : ptr->filename,
231 gcov_version_string (expected_string, e), e,
e3a682f4 232 gcov_version_string (ver_string, v), v);
d6d3f033
RX
233 return 0;
234 }
235 return 1;
236}
237
d6d3f033
RX
238/* buffer for the fn_data from another program. */
239static struct gcov_fn_buffer *fn_buffer;
d6d3f033 240
d6d3f033
RX
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
19926161
NS
249merge_one_data (const char *filename,
250 struct gcov_info *gi_ptr,
512cc015 251 struct gcov_summary *summary)
d6d3f033
RX
252{
253 gcov_unsigned_t tag, length;
254 unsigned t_ix;
512cc015 255 int f_ix = -1;
d6d3f033
RX
256 int error = 0;
257 struct gcov_fn_buffer **fn_tail = &fn_buffer;
d6d3f033
RX
258
259 length = gcov_read_unsigned ();
6dc33097 260 if (!gcov_version (gi_ptr, length, filename))
d6d3f033
RX
261 return -1;
262
72e0c742
ML
263 /* Skip timestamp. */
264 gcov_read_unsigned ();
265
d6d3f033 266 length = gcov_read_unsigned ();
72e0c742 267 if (length != gi_ptr->checksum)
0e8f29da
ML
268 {
269 /* Read from a different compilation. Overwrite the file. */
d273c40a 270 gcov_error (GCOV_PROF_PREFIX "overwriting an existing profile data "
72e0c742 271 "with a different checksum\n", filename);
0e8f29da
ML
272 return 0;
273 }
d6d3f033 274
512cc015
ML
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);
d6d3f033 281
512cc015 282 tag = gcov_read_unsigned ();
d6d3f033
RX
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. */
6dc33097 309 fn_tail = buffer_fn_data (filename, gi_ptr, fn_tail, f_ix);
d6d3f033
RX
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
871e5ada 335 tag = gcov_read_unsigned ();
ece21ff6
ML
336 int read_length = (int)gcov_read_unsigned ();
337 length = abs (read_length);
871e5ada
ML
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;
ece21ff6
ML
343 /* Merging with all zero counters does not make sense. */
344 if (read_length > 0)
345 (*merge) (ci_ptr->values, ci_ptr->num);
871e5ada
ML
346 ci_ptr++;
347 }
d6d3f033 348 if ((error = gcov_is_error ()))
871e5ada 349 goto read_error;
d6d3f033
RX
350 }
351
352 if (tag)
353 {
354 read_mismatch:;
d273c40a 355 gcov_error (GCOV_PROF_PREFIX "Merge mismatch for %s %u\n",
6dc33097 356 filename, f_ix >= 0 ? "function" : "summary",
d6d3f033
RX
357 f_ix < 0 ? -1 - f_ix : f_ix);
358 return -1;
359 }
360 return 0;
361
362read_error:
d273c40a 363 gcov_error (GCOV_PROF_PREFIX "%s merging\n", filename,
d6d3f033
RX
364 error < 0 ? "Overflow": "Error");
365 return -1;
366}
367
9124bbe1
SH
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
39d80300
SH
413/* Dump the STRING using the DUMP handler called with ARG. */
414
415static inline void
28cfea98 416ATTRIBUTE_UNUSED
39d80300
SH
417dump_string (const char *string,
418 void (*dump_fn) (const void *, unsigned, void *),
419 void *arg)
420{
421 unsigned length = 0;
422
423 if (string)
424 length = strlen (string) + 1;
425
426 dump_unsigned (length, dump_fn, arg);
427 if (string)
428 (*dump_fn) (string, length, arg);
429}
430
6a8fc0c3
ML
431#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
432
871e5ada
ML
433/* Store all TOP N counters where each has a dynamic length. */
434
435static void
6a8fc0c3
ML
436write_topn_counters (const struct gcov_ctr_info *ci_ptr,
437 unsigned t_ix,
9124bbe1
SH
438 gcov_unsigned_t n_counts,
439 void (*dump_fn) (const void *, unsigned, void *),
440 void *(*allocate_fn)(unsigned, void *),
441 void *arg)
871e5ada
ML
442{
443 unsigned counters = n_counts / GCOV_TOPN_MEM_COUNTERS;
444 gcc_assert (n_counts % GCOV_TOPN_MEM_COUNTERS == 0);
6a8fc0c3
ML
445
446 /* It can happen in a multi-threaded environment that number of counters is
447 different from the size of the corresponding linked lists. */
448#define LIST_SIZE_MIN_LENGTH 4 * 1024
449
450 static unsigned *list_sizes = NULL;
451 static unsigned list_size_length = 0;
452
453 if (list_sizes == NULL || counters > list_size_length)
454 {
455 list_size_length = MAX (LIST_SIZE_MIN_LENGTH, 2 * counters);
9124bbe1 456#if !defined(inhibit_libc) && HAVE_SYS_MMAN_H
6a8fc0c3
ML
457 list_sizes
458 = (unsigned *)malloc_mmap (list_size_length * sizeof (unsigned));
459#endif
460
461 /* Malloc fallback. */
462 if (list_sizes == NULL)
9124bbe1
SH
463 list_sizes =
464 (unsigned *)(*allocate_fn) (list_size_length * sizeof (unsigned),
465 arg);
6a8fc0c3
ML
466 }
467
871e5ada 468 unsigned pair_total = 0;
6a8fc0c3 469
871e5ada 470 for (unsigned i = 0; i < counters; i++)
6a8fc0c3
ML
471 {
472 gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
9124bbe1
SH
473 unsigned sizes = 0;
474
3c94db20 475 for (struct gcov_kvp *node = (struct gcov_kvp *)(__INTPTR_TYPE__)start;
6a8fc0c3 476 node != NULL; node = node->next)
9124bbe1
SH
477 ++sizes;
478
479 pair_total += sizes;
480 list_sizes[i] = sizes;
6a8fc0c3
ML
481 }
482
871e5ada 483 unsigned disk_size = GCOV_TOPN_DISK_COUNTERS * counters + 2 * pair_total;
9124bbe1
SH
484 dump_unsigned (GCOV_TAG_FOR_COUNTER (t_ix), dump_fn, arg),
485 dump_unsigned (GCOV_TAG_COUNTER_LENGTH (disk_size), dump_fn, arg);
871e5ada
ML
486
487 for (unsigned i = 0; i < counters; i++)
488 {
9124bbe1
SH
489 dump_counter (ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i], dump_fn, arg);
490 dump_counter (list_sizes[i], dump_fn, arg);
862b9b22 491 gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
6a8fc0c3
ML
492
493 unsigned j = 0;
3c94db20 494 for (struct gcov_kvp *node = (struct gcov_kvp *)(__INTPTR_TYPE__)start;
6a8fc0c3 495 j < list_sizes[i]; node = node->next, j++)
871e5ada 496 {
9124bbe1
SH
497 dump_counter (node->value, dump_fn, arg);
498 dump_counter (node->count, dump_fn, arg);
871e5ada
ML
499 }
500 }
501}
502
d6d3f033
RX
503/* Write counters in GI_PTR and the summary in PRG to a gcda file. In
504 the case of appending to an existing file, SUMMARY_POS will be non-zero.
505 We will write the file starting from SUMMAY_POS. */
506
507static void
19926161 508write_one_data (const struct gcov_info *gi_ptr,
9124bbe1
SH
509 const struct gcov_summary *prg_p ATTRIBUTE_UNUSED,
510 void (*dump_fn) (const void *, unsigned, void *),
511 void *(*allocate_fn) (unsigned, void *),
512 void *arg)
d6d3f033
RX
513{
514 unsigned f_ix;
d6d3f033 515
9124bbe1
SH
516 dump_unsigned (GCOV_DATA_MAGIC, dump_fn, arg);
517 dump_unsigned (GCOV_VERSION, dump_fn, arg);
518 dump_unsigned (gi_ptr->stamp, dump_fn, arg);
72e0c742 519 dump_unsigned (gi_ptr->checksum, dump_fn, arg);
d6d3f033 520
9124bbe1 521#ifdef NEED_L_GCOV
d6d3f033 522 /* Generate whole program statistics. */
a9c83fb7 523 gcov_write_object_summary (prg_p);
9124bbe1 524#endif
d6d3f033
RX
525
526 /* Write execution counts for each function. */
527 for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
528 {
9124bbe1 529#ifdef NEED_L_GCOV
d6d3f033 530 unsigned buffered = 0;
9124bbe1 531#endif
d6d3f033
RX
532 const struct gcov_fn_info *gfi_ptr;
533 const struct gcov_ctr_info *ci_ptr;
534 gcov_unsigned_t length;
535 unsigned t_ix;
536
9124bbe1 537#ifdef NEED_L_GCOV
d6d3f033
RX
538 if (fn_buffer && fn_buffer->fn_ix == f_ix)
539 {
540 /* Buffered data from another program. */
541 buffered = 1;
542 gfi_ptr = &fn_buffer->info;
543 length = GCOV_TAG_FUNCTION_LENGTH;
544 }
545 else
9124bbe1 546#endif
d6d3f033
RX
547 {
548 gfi_ptr = gi_ptr->functions[f_ix];
549 if (gfi_ptr && gfi_ptr->key == gi_ptr)
550 length = GCOV_TAG_FUNCTION_LENGTH;
551 else
552 length = 0;
553 }
554
9124bbe1
SH
555 dump_unsigned (GCOV_TAG_FUNCTION, dump_fn, arg);
556 dump_unsigned (length, dump_fn, arg);
d6d3f033
RX
557 if (!length)
558 continue;
559
9124bbe1
SH
560 dump_unsigned (gfi_ptr->ident, dump_fn, arg);
561 dump_unsigned (gfi_ptr->lineno_checksum, dump_fn, arg);
562 dump_unsigned (gfi_ptr->cfg_checksum, dump_fn, arg);
d6d3f033
RX
563
564 ci_ptr = gfi_ptr->ctrs;
565 for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
566 {
ece21ff6 567 gcov_position_t n_counts;
d6d3f033 568
ece21ff6
ML
569 if (!gi_ptr->merge[t_ix])
570 continue;
d6d3f033 571
ece21ff6 572 n_counts = ci_ptr->num;
871e5ada 573
4ecf368f 574 if (t_ix == GCOV_COUNTER_V_TOPN || t_ix == GCOV_COUNTER_V_INDIR)
9124bbe1
SH
575 write_topn_counters (ci_ptr, t_ix, n_counts, dump_fn, allocate_fn,
576 arg);
871e5ada
ML
577 else
578 {
9124bbe1
SH
579 dump_unsigned (GCOV_TAG_FOR_COUNTER (t_ix), dump_fn, arg);
580 if (are_all_counters_zero (ci_ptr))
581 /* Do not stream when all counters are zero. */
582 dump_unsigned (GCOV_TAG_COUNTER_LENGTH (-n_counts),
583 dump_fn, arg);
ece21ff6
ML
584 else
585 {
9124bbe1
SH
586 dump_unsigned (GCOV_TAG_COUNTER_LENGTH (n_counts),
587 dump_fn, arg);
ece21ff6 588 for (unsigned i = 0; i < n_counts; i++)
9124bbe1 589 dump_counter (ci_ptr->values[i], dump_fn, arg);
ece21ff6 590 }
871e5ada
ML
591 }
592
ece21ff6
ML
593 ci_ptr++;
594 }
9124bbe1 595#ifdef NEED_L_GCOV
d6d3f033
RX
596 if (buffered)
597 fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
9124bbe1 598#endif
d6d3f033
RX
599 }
600
9124bbe1 601 dump_unsigned (0, dump_fn, arg);
d6d3f033 602}
9124bbe1 603#endif /* NEED_L_GCOV || NEED_L_GCOV_INFO_TO_GCDA */
d6d3f033 604
9124bbe1 605#ifdef NEED_L_GCOV
d6d3f033
RX
606/* Dump the coverage counts for one gcov_info object. We merge with existing
607 counts when possible, to avoid growing the .da files ad infinitum. We use
608 this program's checksum to make sure we only accumulate whole program
609 statistics to the correct summary. An object file might be embedded
610 in two separate programs, and we must keep the two program
611 summaries separate. */
612
613static void
19926161 614dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf,
88891c5f 615 unsigned run_counted ATTRIBUTE_UNUSED,
1a6314b0 616 gcov_type run_max ATTRIBUTE_UNUSED, int mode)
d6d3f033 617{
512cc015 618 struct gcov_summary summary = {};
d6d3f033
RX
619 int error;
620 gcov_unsigned_t tag;
d6d3f033 621 fn_buffer = 0;
d6d3f033 622
1a6314b0 623 error = gcov_exit_open_gcda_file (gi_ptr, gf, mode);
d6d3f033
RX
624 if (error == -1)
625 return;
626
627 tag = gcov_read_unsigned ();
628 if (tag)
629 {
630 /* Merge data from file. */
631 if (tag != GCOV_DATA_MAGIC)
632 {
d273c40a
ML
633 gcov_error (GCOV_PROF_PREFIX "Not a gcov data file\n",
634 gf->filename);
d6d3f033
RX
635 goto read_fatal;
636 }
512cc015 637 error = merge_one_data (gf->filename, gi_ptr, &summary);
d6d3f033
RX
638 if (error == -1)
639 goto read_fatal;
640 }
641
642 gcov_rewrite ();
643
88891c5f
ML
644#if !IN_GCOV_TOOL
645 if (!run_counted)
646 {
647 summary.runs++;
648 summary.sum_max += run_max;
649 }
650#else
651 summary = gi_ptr->summary;
652#endif
d6d3f033 653
9124bbe1
SH
654 write_one_data (gi_ptr, &summary, gcov_dump_handler, gcov_allocate_handler,
655 NULL);
d6d3f033
RX
656 /* fall through */
657
658read_fatal:;
659 while (fn_buffer)
660 fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
661
662 if ((error = gcov_close ()))
23eb66d1 663 gcov_error ((error < 0 ? GCOV_PROF_PREFIX "Overflow writing\n"
664 : GCOV_PROF_PREFIX "Error writing\n"), gf->filename);
d6d3f033
RX
665}
666
667
668/* Dump all the coverage counts for the program. It first computes program
669 summary and then traverses gcov_list list and dumps the gcov_info
1a6314b0 670 objects one by one. Use MODE to open files. */
d6d3f033 671
4303c581
NS
672#if !IN_GCOV_TOOL
673static
674#endif
675void
1a6314b0 676gcov_do_dump (struct gcov_info *list, int run_counted, int mode)
d6d3f033
RX
677{
678 struct gcov_info *gi_ptr;
6dc33097 679 struct gcov_filename gf;
d6d3f033 680
512cc015
ML
681 /* Compute run_max of this program run. */
682 gcov_type run_max = 0;
683 for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next)
684 for (unsigned f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
685 {
686 const struct gcov_ctr_info *cinfo
687 = &gi_ptr->functions[f_ix]->ctrs[GCOV_COUNTER_ARCS];
688
689 for (unsigned i = 0; i < cinfo->num; i++)
690 if (run_max < cinfo->values[i])
691 run_max = cinfo->values[i];
692 }
d6d3f033
RX
693
694 allocate_filename_struct (&gf);
695
696 /* Now merge each file. */
19926161 697 for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next)
6c086e8c 698 {
1a6314b0 699 dump_one_gcov (gi_ptr, &gf, run_counted, run_max, mode);
6c086e8c
ML
700 free (gf.filename);
701 }
d6d3f033 702
6c086e8c 703 free (gf.prefix);
d6d3f033
RX
704}
705
63971184
ML
706#if IN_GCOV_TOOL
707const char *
708__attribute__ ((unused))
709gcov_get_filename (struct gcov_info *list)
710{
711 return list->filename;
712}
713#endif
714
b98a872b 715#if !IN_GCOV_TOOL
19926161 716void
4303c581 717__gcov_dump_one (struct gcov_root *root)
19926161 718{
4303c581 719 if (root->dumped)
19926161
NS
720 return;
721
1a6314b0 722 gcov_do_dump (root->list, root->run_counted, 0);
19926161 723
4303c581
NS
724 root->dumped = 1;
725 root->run_counted = 1;
19926161
NS
726}
727
cadb2b96 728/* Per-dynamic-object gcov state. */
4303c581 729struct gcov_root __gcov_root;
d6d3f033 730
cadb2b96
NS
731/* Exactly one of these will be live in the process image. */
732struct gcov_master __gcov_master =
733 {GCOV_VERSION, 0};
bc2b1a23 734
00d79dc4
ML
735/* Dynamic pool for gcov_kvp structures. */
736struct gcov_kvp *__gcov_kvp_dynamic_pool;
bc2b1a23 737
00d79dc4
ML
738/* Index into __gcov_kvp_dynamic_pool array. */
739unsigned __gcov_kvp_dynamic_pool_index;
740
741/* Size of _gcov_kvp_dynamic_pool array. */
742unsigned __gcov_kvp_dynamic_pool_size;
cadb2b96 743
8c9434c2
ML
744void
745__gcov_exit (void)
d6d3f033 746{
4303c581 747 __gcov_dump_one (&__gcov_root);
cadb2b96
NS
748 if (__gcov_root.next)
749 __gcov_root.next->prev = __gcov_root.prev;
750 if (__gcov_root.prev)
751 __gcov_root.prev->next = __gcov_root.next;
752 else
753 __gcov_master.root = __gcov_root.next;
8aa5bdd6
AC
754
755 gcov_error_exit ();
d6d3f033
RX
756}
757
758/* Add a new object file onto the bb chain. Invoked automatically
759 when running an object file's global ctors. */
760
761void
762__gcov_init (struct gcov_info *info)
763{
764 if (!info->version || !info->n_functions)
765 return;
766 if (gcov_version (info, info->version, 0))
767 {
4303c581 768 if (!__gcov_root.list)
cadb2b96
NS
769 {
770 /* Add to master list and at exit function. */
771 if (gcov_version (NULL, __gcov_master.version, "<master>"))
772 {
773 __gcov_root.next = __gcov_master.root;
774 if (__gcov_master.root)
775 __gcov_master.root->prev = &__gcov_root;
776 __gcov_master.root = &__gcov_root;
777 }
cadb2b96 778 }
d6d3f033 779
4303c581
NS
780 info->next = __gcov_root.list;
781 __gcov_root.list = info;
d6d3f033 782 }
d6d3f033 783}
b98a872b 784#endif /* !IN_GCOV_TOOL */
9124bbe1
SH
785#endif /* NEED_L_GCOV */
786
787#ifdef NEED_L_GCOV_INFO_TO_GCDA
788/* Convert the gcov info to a gcda data stream. It is intended for
39d80300 789 freestanding environments which do not support the C library file I/O. */
9124bbe1
SH
790
791void
792__gcov_info_to_gcda (const struct gcov_info *gi_ptr,
793 void (*filename_fn) (const char *, void *),
794 void (*dump_fn) (const void *, unsigned, void *),
795 void *(*allocate_fn) (unsigned, void *),
796 void *arg)
797{
798 (*filename_fn) (gi_ptr->filename, arg);
799 write_one_data (gi_ptr, NULL, dump_fn, allocate_fn, arg);
800}
39d80300
SH
801
802/* Convert the filename to a gcfn data stream. It is intended for
803 freestanding environments which do not support the C library file I/O. */
804
805void
806__gcov_filename_to_gcfn (const char *filename,
807 void (*dump_fn) (const void *, unsigned, void *),
808 void *arg)
809{
810 dump_unsigned (GCOV_FILENAME_MAGIC, dump_fn, arg);
811 dump_unsigned (GCOV_VERSION, dump_fn, arg);
812 dump_string (filename, dump_fn, arg);
813}
9124bbe1 814#endif /* NEED_L_GCOV_INFO_TO_GCDA */