]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/gcov-tool.cc
Don't build readline/libreadline.a, when --with-system-readline is supplied
[thirdparty/gcc.git] / gcc / gcov-tool.cc
1 /* Gcc offline profile processing tool support. */
2 /* Copyright (C) 2014-2022 Free Software Foundation, Inc.
3 Contributed by Rong Xu <xur@google.com>.
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 "config.h"
27 #include "system.h"
28 #include "coretypes.h"
29 #include "tm.h"
30 #include "intl.h"
31 #include "diagnostic.h"
32 #include "version.h"
33 #include "gcov-io.h"
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38 #if HAVE_FTW_H
39 #include <ftw.h>
40 #endif
41 #include <getopt.h>
42
43 extern struct gcov_info *gcov_profile_merge (struct gcov_info*,
44 struct gcov_info*, int, int);
45 extern struct gcov_info *gcov_profile_merge_stream (const char *, int, int);
46 extern int gcov_profile_overlap (struct gcov_info*, struct gcov_info*);
47 extern int gcov_profile_normalize (struct gcov_info*, gcov_type);
48 extern int gcov_profile_scale (struct gcov_info*, float, int, int);
49 extern struct gcov_info* gcov_read_profile_dir (const char*, int);
50 extern void gcov_do_dump (struct gcov_info *, int, int);
51 extern const char *gcov_get_filename (struct gcov_info *list);
52 extern void gcov_set_verbose (void);
53
54 /* Set to verbose output mode. */
55 static bool verbose;
56
57 #if HAVE_FTW_H
58
59 /* Remove file NAME if it has a gcda suffix. */
60
61 static int
62 unlink_gcda_file (const char *name,
63 const struct stat *status ATTRIBUTE_UNUSED,
64 int type ATTRIBUTE_UNUSED,
65 struct FTW *ftwbuf ATTRIBUTE_UNUSED)
66 {
67 int ret = 0;
68 int len = strlen (name);
69 int len1 = strlen (GCOV_DATA_SUFFIX);
70
71 if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1))
72 ret = remove (name);
73
74 if (ret)
75 fatal_error (input_location, "error in removing %s", name);
76
77 return ret;
78 }
79 #endif
80
81 /* Remove the gcda files in PATH recursively. */
82
83 static int
84 unlink_profile_dir (const char *path ATTRIBUTE_UNUSED)
85 {
86 #if HAVE_FTW_H
87 return nftw(path, unlink_gcda_file, 64, FTW_DEPTH | FTW_PHYS);
88 #else
89 return -1;
90 #endif
91 }
92
93 /* Output GCOV_INFO lists PROFILE to directory OUT. Note that
94 we will remove all the gcda files in OUT. */
95
96 static void
97 gcov_output_files (const char *out, struct gcov_info *profile)
98 {
99 char *pwd;
100 int ret;
101
102 /* Try to make directory if it doesn't already exist. */
103 if (access (out, F_OK) == -1)
104 {
105 if (mkdir (out, S_IRWXU | S_IRWXG | S_IRWXO) == -1 && errno != EEXIST)
106 fatal_error (input_location, "Cannot make directory %s", out);
107 } else
108 unlink_profile_dir (out);
109
110 /* Output new profile. */
111 pwd = getcwd (NULL, 0);
112
113 if (pwd == NULL)
114 fatal_error (input_location, "Cannot get current directory name");
115
116 ret = chdir (out);
117 if (ret)
118 fatal_error (input_location, "Cannot change directory to %s", out);
119
120 /* Verify that output file does not exist (either was removed by
121 unlink_profile_data or removed by user). */
122 const char *filename = gcov_get_filename (profile);
123
124 if (access (filename, F_OK) != -1)
125 fatal_error (input_location, "output file %s already exists in folder %s",
126 filename, out);
127
128 gcov_do_dump (profile, 0, 0);
129
130 ret = chdir (pwd);
131 if (ret)
132 fatal_error (input_location, "Cannot change directory to %s", pwd);
133
134 free (pwd);
135 }
136
137 /* Merging profile D1 and D2 with weight as W1 and W2, respectively.
138 The result profile is written to directory OUT.
139 Return 0 on success. */
140
141 static int
142 profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2)
143 {
144 struct gcov_info *d1_profile;
145 struct gcov_info *d2_profile;
146 struct gcov_info *merged_profile;
147
148 d1_profile = gcov_read_profile_dir (d1, 0);
149 d2_profile = gcov_read_profile_dir (d2, 0);
150
151 /* The actual merge: we overwrite to d1_profile. */
152 merged_profile = gcov_profile_merge (d1_profile, d2_profile, w1, w2);
153
154 if (merged_profile)
155 gcov_output_files (out, merged_profile);
156 else if (verbose)
157 fnotice (stdout, "no profile files were merged\n");
158
159 return 0;
160 }
161
162 /* Usage message for profile merge. */
163
164 static void
165 print_merge_usage_message (int error_p)
166 {
167 FILE *file = error_p ? stderr : stdout;
168
169 fnotice (file, " merge [options] <dir1> <dir2> Merge coverage file contents\n");
170 fnotice (file, " -o, --output <dir> Output directory\n");
171 fnotice (file, " -v, --verbose Verbose mode\n");
172 fnotice (file, " -w, --weight <w1,w2> Set weights (float point values)\n");
173 }
174
175 static const struct option merge_options[] =
176 {
177 { "verbose", no_argument, NULL, 'v' },
178 { "output", required_argument, NULL, 'o' },
179 { "weight", required_argument, NULL, 'w' },
180 { 0, 0, 0, 0 }
181 };
182
183 /* Print merge usage and exit. */
184
185 static void ATTRIBUTE_NORETURN
186 merge_usage (void)
187 {
188 fnotice (stderr, "Merge subcomand usage:");
189 print_merge_usage_message (true);
190 exit (FATAL_EXIT_CODE);
191 }
192
193 /* Driver for profile merge sub-command. */
194
195 static int
196 do_merge (int argc, char **argv)
197 {
198 int opt;
199 const char *output_dir = 0;
200 int w1 = 1, w2 = 1;
201
202 optind = 0;
203 while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1)
204 {
205 switch (opt)
206 {
207 case 'v':
208 verbose = true;
209 gcov_set_verbose ();
210 break;
211 case 'o':
212 output_dir = optarg;
213 break;
214 case 'w':
215 sscanf (optarg, "%d,%d", &w1, &w2);
216 if (w1 < 0 || w2 < 0)
217 fatal_error (input_location, "weights need to be non-negative");
218 break;
219 default:
220 merge_usage ();
221 }
222 }
223
224 if (output_dir == NULL)
225 output_dir = "merged_profile";
226
227 if (argc - optind != 2)
228 merge_usage ();
229
230 return profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2);
231 }
232
233 /* Usage message for profile merge-stream. */
234
235 static void
236 print_merge_stream_usage_message (int error_p)
237 {
238 FILE *file = error_p ? stderr : stdout;
239
240 fnotice (file, " merge-stream [options] [<file>] Merge coverage stream file (or stdin)\n"
241 " and coverage file contents\n");
242 fnotice (file, " -v, --verbose Verbose mode\n");
243 fnotice (file, " -w, --weight <w1,w2> Set weights (float point values)\n");
244 }
245
246 static const struct option merge_stream_options[] =
247 {
248 { "verbose", no_argument, NULL, 'v' },
249 { "weight", required_argument, NULL, 'w' },
250 { 0, 0, 0, 0 }
251 };
252
253 /* Print merge-stream usage and exit. */
254
255 static void ATTRIBUTE_NORETURN
256 merge_stream_usage (void)
257 {
258 fnotice (stderr, "Merge-stream subcomand usage:");
259 print_merge_stream_usage_message (true);
260 exit (FATAL_EXIT_CODE);
261 }
262
263 /* Driver for profile merge-stream sub-command. */
264
265 static int
266 do_merge_stream (int argc, char **argv)
267 {
268 int opt;
269 int w1 = 1, w2 = 1;
270 struct gcov_info *merged_profile;
271
272 optind = 0;
273 while ((opt = getopt_long (argc, argv, "vw:",
274 merge_stream_options, NULL)) != -1)
275 {
276 switch (opt)
277 {
278 case 'v':
279 verbose = true;
280 gcov_set_verbose ();
281 break;
282 case 'w':
283 sscanf (optarg, "%d,%d", &w1, &w2);
284 if (w1 < 0 || w2 < 0)
285 fatal_error (input_location, "weights need to be non-negative");
286 break;
287 default:
288 merge_stream_usage ();
289 }
290 }
291
292 if (argc - optind > 1)
293 merge_stream_usage ();
294
295 merged_profile = gcov_profile_merge_stream (argv[optind], w1, w2);
296
297 if (merged_profile)
298 gcov_do_dump (merged_profile, 0, -1);
299 else if (verbose)
300 fnotice (stdout, "no profile files were merged\n");
301
302 return 0;
303 }
304
305 /* If N_VAL is no-zero, normalize the profile by setting the largest counter
306 counter value to N_VAL and scale others counters proportionally.
307 Otherwise, multiply the all counters by SCALE. */
308
309 static int
310 profile_rewrite (const char *d1, const char *out, int64_t n_val,
311 float scale, int n, int d)
312 {
313 struct gcov_info * d1_profile;
314
315 d1_profile = gcov_read_profile_dir (d1, 0);
316 if (!d1_profile)
317 return 1;
318
319 if (n_val)
320 gcov_profile_normalize (d1_profile, (gcov_type) n_val);
321 else
322 gcov_profile_scale (d1_profile, scale, n, d);
323
324 gcov_output_files (out, d1_profile);
325 return 0;
326 }
327
328 /* Usage function for profile rewrite. */
329
330 static void
331 print_rewrite_usage_message (int error_p)
332 {
333 FILE *file = error_p ? stderr : stdout;
334
335 fnotice (file, " rewrite [options] <dir> Rewrite coverage file contents\n");
336 fnotice (file, " -n, --normalize <int64_t> Normalize the profile\n");
337 fnotice (file, " -o, --output <dir> Output directory\n");
338 fnotice (file, " -s, --scale <float or simple-frac> Scale the profile counters\n");
339 fnotice (file, " -v, --verbose Verbose mode\n");
340 }
341
342 static const struct option rewrite_options[] =
343 {
344 { "verbose", no_argument, NULL, 'v' },
345 { "output", required_argument, NULL, 'o' },
346 { "scale", required_argument, NULL, 's' },
347 { "normalize", required_argument, NULL, 'n' },
348 { 0, 0, 0, 0 }
349 };
350
351 /* Print profile rewrite usage and exit. */
352
353 static void ATTRIBUTE_NORETURN
354 rewrite_usage (void)
355 {
356 fnotice (stderr, "Rewrite subcommand usage:");
357 print_rewrite_usage_message (true);
358 exit (FATAL_EXIT_CODE);
359 }
360
361 /* Driver for profile rewrite sub-command. */
362
363 static int
364 do_rewrite (int argc, char **argv)
365 {
366 int opt;
367 int ret;
368 const char *output_dir = 0;
369 int64_t normalize_val = 0;
370 float scale = 0.0;
371 int numerator = 1;
372 int denominator = 1;
373 int do_scaling = 0;
374
375 optind = 0;
376 while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1)
377 {
378 switch (opt)
379 {
380 case 'v':
381 verbose = true;
382 gcov_set_verbose ();
383 break;
384 case 'o':
385 output_dir = optarg;
386 break;
387 case 'n':
388 if (!do_scaling)
389 #if defined(INT64_T_IS_LONG)
390 normalize_val = strtol (optarg, (char **)NULL, 10);
391 #else
392 normalize_val = strtoll (optarg, (char **)NULL, 10);
393 #endif
394 else
395 fnotice (stderr, "scaling cannot co-exist with normalization,"
396 " skipping\n");
397 break;
398 case 's':
399 ret = 0;
400 do_scaling = 1;
401 if (strstr (optarg, "/"))
402 {
403 ret = sscanf (optarg, "%d/%d", &numerator, &denominator);
404 if (ret == 2)
405 {
406 if (numerator < 0 || denominator <= 0)
407 {
408 fnotice (stderr, "incorrect format in scaling, using 1/1\n");
409 denominator = 1;
410 numerator = 1;
411 }
412 }
413 }
414 if (ret != 2)
415 {
416 ret = sscanf (optarg, "%f", &scale);
417 if (ret != 1)
418 fnotice (stderr, "incorrect format in scaling, using 1/1\n");
419 else
420 denominator = 0;
421 }
422
423 if (scale < 0.0)
424 fatal_error (input_location, "scale needs to be non-negative");
425
426 if (normalize_val != 0)
427 {
428 fnotice (stderr, "normalization cannot co-exist with scaling\n");
429 normalize_val = 0;
430 }
431 break;
432 default:
433 rewrite_usage ();
434 }
435 }
436
437 if (output_dir == NULL)
438 output_dir = "rewrite_profile";
439
440 if (argc - optind == 1)
441 {
442 if (denominator > 0)
443 ret = profile_rewrite (argv[optind], output_dir, 0, 0.0, numerator, denominator);
444 else
445 ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale, 0, 0);
446 }
447 else
448 rewrite_usage ();
449
450 return ret;
451 }
452
453 /* Driver function to computer the overlap score b/w profile D1 and D2.
454 Return 1 on error and 0 if OK. */
455
456 static int
457 profile_overlap (const char *d1, const char *d2)
458 {
459 struct gcov_info *d1_profile;
460 struct gcov_info *d2_profile;
461
462 d1_profile = gcov_read_profile_dir (d1, 0);
463 if (!d1_profile)
464 return 1;
465
466 if (d2)
467 {
468 d2_profile = gcov_read_profile_dir (d2, 0);
469 if (!d2_profile)
470 return 1;
471
472 return gcov_profile_overlap (d1_profile, d2_profile);
473 }
474
475 return 1;
476 }
477
478 /* Usage message for profile overlap. */
479
480 static void
481 print_overlap_usage_message (int error_p)
482 {
483 FILE *file = error_p ? stderr : stdout;
484
485 fnotice (file, " overlap [options] <dir1> <dir2> Compute the overlap of two profiles\n");
486 fnotice (file, " -f, --function Print function level info\n");
487 fnotice (file, " -F, --fullname Print full filename\n");
488 fnotice (file, " -h, --hotonly Only print info for hot objects/functions\n");
489 fnotice (file, " -o, --object Print object level info\n");
490 fnotice (file, " -t <float>, --hot_threshold <float> Set the threshold for hotness\n");
491 fnotice (file, " -v, --verbose Verbose mode\n");
492 }
493
494 static const struct option overlap_options[] =
495 {
496 { "verbose", no_argument, NULL, 'v' },
497 { "function", no_argument, NULL, 'f' },
498 { "fullname", no_argument, NULL, 'F' },
499 { "object", no_argument, NULL, 'o' },
500 { "hotonly", no_argument, NULL, 'h' },
501 { "hot_threshold", required_argument, NULL, 't' },
502 { 0, 0, 0, 0 }
503 };
504
505 /* Print overlap usage and exit. */
506
507 static void ATTRIBUTE_NORETURN
508 overlap_usage (void)
509 {
510 fnotice (stderr, "Overlap subcomand usage:");
511 print_overlap_usage_message (true);
512 exit (FATAL_EXIT_CODE);
513 }
514
515 int overlap_func_level;
516 int overlap_obj_level;
517 int overlap_hot_only;
518 int overlap_use_fullname;
519 double overlap_hot_threshold = 0.005;
520
521 /* Driver for profile overlap sub-command. */
522
523 static int
524 do_overlap (int argc, char **argv)
525 {
526 int opt;
527 int ret;
528
529 optind = 0;
530 while ((opt = getopt_long (argc, argv, "vfFoht:", overlap_options, NULL)) != -1)
531 {
532 switch (opt)
533 {
534 case 'v':
535 verbose = true;
536 gcov_set_verbose ();
537 break;
538 case 'f':
539 overlap_func_level = 1;
540 break;
541 case 'F':
542 overlap_use_fullname = 1;
543 break;
544 case 'o':
545 overlap_obj_level = 1;
546 break;
547 case 'h':
548 overlap_hot_only = 1;
549 break;
550 case 't':
551 overlap_hot_threshold = atof (optarg);
552 break;
553 default:
554 overlap_usage ();
555 }
556 }
557
558 if (argc - optind == 2)
559 ret = profile_overlap (argv[optind], argv[optind+1]);
560 else
561 overlap_usage ();
562
563 return ret;
564 }
565
566
567 /* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
568 otherwise the output of --help. */
569
570 static void
571 print_usage (int error_p)
572 {
573 FILE *file = error_p ? stderr : stdout;
574 int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
575
576 fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname);
577 fnotice (file, "Offline tool to handle gcda counts\n\n");
578 fnotice (file, " -h, --help Print this help, then exit\n");
579 fnotice (file, " -v, --version Print version number, then exit\n");
580 print_merge_usage_message (error_p);
581 print_merge_stream_usage_message (error_p);
582 print_rewrite_usage_message (error_p);
583 print_overlap_usage_message (error_p);
584 fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
585 bug_report_url);
586 exit (status);
587 }
588
589 /* Print version information and exit. */
590
591 static void
592 print_version (void)
593 {
594 fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
595 fnotice (stdout, "Copyright %s 2022 Free Software Foundation, Inc.\n",
596 _("(C)"));
597 fnotice (stdout,
598 _("This is free software; see the source for copying conditions. There is NO\n\
599 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
600 exit (SUCCESS_EXIT_CODE);
601 }
602
603 static const struct option options[] =
604 {
605 { "help", no_argument, NULL, 'h' },
606 { "version", no_argument, NULL, 'v' },
607 { 0, 0, 0, 0 }
608 };
609
610 /* Process args, return index to first non-arg. */
611
612 static int
613 process_args (int argc, char **argv)
614 {
615 int opt;
616
617 while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1)
618 {
619 switch (opt)
620 {
621 case 'h':
622 print_usage (false);
623 /* Print_usage will exit. */
624 /* FALLTHRU */
625 case 'v':
626 print_version ();
627 /* Print_version will exit. */
628 /* FALLTHRU */
629 default:
630 print_usage (true);
631 /* Print_usage will exit. */
632 }
633 }
634
635 return optind;
636 }
637
638 /* Main function for gcov-tool. */
639
640 int
641 main (int argc, char **argv)
642 {
643 const char *p;
644 const char *sub_command;
645
646 p = argv[0] + strlen (argv[0]);
647 while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
648 --p;
649 progname = p;
650
651 xmalloc_set_program_name (progname);
652
653 /* Unlock the stdio streams. */
654 unlock_std_streams ();
655
656 gcc_init_libintl ();
657
658 diagnostic_initialize (global_dc, 0);
659
660 /* Handle response files. */
661 expandargv (&argc, &argv);
662
663 process_args (argc, argv);
664 if (optind >= argc)
665 print_usage (true);
666
667 sub_command = argv[optind];
668
669 if (!strcmp (sub_command, "merge"))
670 return do_merge (argc - optind, argv + optind);
671 else if (!strcmp (sub_command, "merge-stream"))
672 return do_merge_stream (argc - optind, argv + optind);
673 else if (!strcmp (sub_command, "rewrite"))
674 return do_rewrite (argc - optind, argv + optind);
675 else if (!strcmp (sub_command, "overlap"))
676 return do_overlap (argc - optind, argv + optind);
677
678 print_usage (true);
679 }