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