RISC-V: Add RISC-V into vect_cmdline_needed
[official-gcc.git] / gcc / gcov-tool.cc
blob76173fdaef76ff0da9e332df125f116f11df560b
1 /* Gcc offline profile processing tool support. */
2 /* Copyright (C) 2014-2023 Free Software Foundation, Inc.
3 Contributed by Rong Xu <xur@google.com>.
5 This file is part of GCC.
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.
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.
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.
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/>. */
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>
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);
54 /* Set to verbose output mode. */
55 static bool verbose;
57 #if HAVE_FTW_H
59 /* Remove file NAME if it has a gcda suffix. */
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)
67 int ret = 0;
68 int len = strlen (name);
69 int len1 = strlen (GCOV_DATA_SUFFIX);
71 if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1))
72 ret = remove (name);
74 if (ret)
75 fatal_error (input_location, "error in removing %s", name);
77 return ret;
79 #endif
81 /* Remove the gcda files in PATH recursively. */
83 static int
84 unlink_profile_dir (const char *path ATTRIBUTE_UNUSED)
86 #if HAVE_FTW_H
87 return nftw(path, unlink_gcda_file, 64, FTW_DEPTH | FTW_PHYS);
88 #else
89 return -1;
90 #endif
93 /* Output GCOV_INFO lists PROFILE to directory OUT. Note that
94 we will remove all the gcda files in OUT. */
96 static void
97 gcov_output_files (const char *out, struct gcov_info *profile)
99 char *pwd;
100 int ret;
102 /* Try to make directory if it doesn't already exist. */
103 if (access (out, F_OK) == -1)
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);
110 /* Output new profile. */
111 pwd = getcwd (NULL, 0);
113 if (pwd == NULL)
114 fatal_error (input_location, "Cannot get current directory name");
116 ret = chdir (out);
117 if (ret)
118 fatal_error (input_location, "Cannot change directory to %s", out);
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);
124 if (access (filename, F_OK) != -1)
125 fatal_error (input_location, "output file %s already exists in folder %s",
126 filename, out);
128 gcov_do_dump (profile, 0, 0);
130 ret = chdir (pwd);
131 if (ret)
132 fatal_error (input_location, "Cannot change directory to %s", pwd);
134 free (pwd);
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. */
141 static int
142 profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2)
144 struct gcov_info *d1_profile;
145 struct gcov_info *d2_profile;
146 struct gcov_info *merged_profile;
148 d1_profile = gcov_read_profile_dir (d1, 0);
149 d2_profile = gcov_read_profile_dir (d2, 0);
151 /* The actual merge: we overwrite to d1_profile. */
152 merged_profile = gcov_profile_merge (d1_profile, d2_profile, w1, w2);
154 if (merged_profile)
155 gcov_output_files (out, merged_profile);
156 else if (verbose)
157 fnotice (stdout, "no profile files were merged\n");
159 return 0;
162 /* Usage message for profile merge. */
164 static void
165 print_merge_usage_message (int error_p)
167 FILE *file = error_p ? stderr : stdout;
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");
175 static const struct option merge_options[] =
177 { "verbose", no_argument, NULL, 'v' },
178 { "output", required_argument, NULL, 'o' },
179 { "weight", required_argument, NULL, 'w' },
180 { 0, 0, 0, 0 }
183 /* Print merge usage and exit. */
185 static void ATTRIBUTE_NORETURN
186 merge_usage (void)
188 fnotice (stderr, "Merge subcommand usage:");
189 print_merge_usage_message (true);
190 exit (FATAL_EXIT_CODE);
193 /* Driver for profile merge subcommand. */
195 static int
196 do_merge (int argc, char **argv)
198 int opt;
199 const char *output_dir = 0;
200 int w1 = 1, w2 = 1;
202 optind = 0;
203 while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1)
205 switch (opt)
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 ();
224 if (output_dir == NULL)
225 output_dir = "merged_profile";
227 if (argc - optind != 2)
228 merge_usage ();
230 return profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2);
233 /* Usage message for profile merge-stream. */
235 static void
236 print_merge_stream_usage_message (int error_p)
238 FILE *file = error_p ? stderr : stdout;
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");
246 static const struct option merge_stream_options[] =
248 { "verbose", no_argument, NULL, 'v' },
249 { "weight", required_argument, NULL, 'w' },
250 { 0, 0, 0, 0 }
253 /* Print merge-stream usage and exit. */
255 static void ATTRIBUTE_NORETURN
256 merge_stream_usage (void)
258 fnotice (stderr, "Merge-stream subcommand usage:");
259 print_merge_stream_usage_message (true);
260 exit (FATAL_EXIT_CODE);
263 /* Driver for profile merge-stream subcommand. */
265 static int
266 do_merge_stream (int argc, char **argv)
268 int opt;
269 int w1 = 1, w2 = 1;
270 struct gcov_info *merged_profile;
272 optind = 0;
273 while ((opt = getopt_long (argc, argv, "vw:",
274 merge_stream_options, NULL)) != -1)
276 switch (opt)
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 ();
292 if (argc - optind > 1)
293 merge_stream_usage ();
295 merged_profile = gcov_profile_merge_stream (argv[optind], w1, w2);
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");
302 return 0;
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. */
309 static int
310 profile_rewrite (const char *d1, const char *out, int64_t n_val,
311 float scale, int n, int d)
313 struct gcov_info * d1_profile;
315 d1_profile = gcov_read_profile_dir (d1, 0);
316 if (!d1_profile)
317 return 1;
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);
324 gcov_output_files (out, d1_profile);
325 return 0;
328 /* Usage function for profile rewrite. */
330 static void
331 print_rewrite_usage_message (int error_p)
333 FILE *file = error_p ? stderr : stdout;
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");
342 static const struct option rewrite_options[] =
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 }
351 /* Print profile rewrite usage and exit. */
353 static void ATTRIBUTE_NORETURN
354 rewrite_usage (void)
356 fnotice (stderr, "Rewrite subcommand usage:");
357 print_rewrite_usage_message (true);
358 exit (FATAL_EXIT_CODE);
361 /* Driver for profile rewrite subcommand. */
363 static int
364 do_rewrite (int argc, char **argv)
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;
375 optind = 0;
376 while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1)
378 switch (opt)
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, "/"))
403 ret = sscanf (optarg, "%d/%d", &numerator, &denominator);
404 if (ret == 2)
406 if (numerator < 0 || denominator <= 0)
408 fnotice (stderr, "incorrect format in scaling, using 1/1\n");
409 denominator = 1;
410 numerator = 1;
414 if (ret != 2)
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;
423 if (scale < 0.0)
424 fatal_error (input_location, "scale needs to be non-negative");
426 if (normalize_val != 0)
428 fnotice (stderr, "normalization cannot co-exist with scaling\n");
429 normalize_val = 0;
431 break;
432 default:
433 rewrite_usage ();
437 if (output_dir == NULL)
438 output_dir = "rewrite_profile";
440 if (argc - optind == 1)
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);
447 else
448 rewrite_usage ();
450 return ret;
453 /* Driver function to computer the overlap score b/w profile D1 and D2.
454 Return 1 on error and 0 if OK. */
456 static int
457 profile_overlap (const char *d1, const char *d2)
459 struct gcov_info *d1_profile;
460 struct gcov_info *d2_profile;
462 d1_profile = gcov_read_profile_dir (d1, 0);
463 if (!d1_profile)
464 return 1;
466 if (d2)
468 d2_profile = gcov_read_profile_dir (d2, 0);
469 if (!d2_profile)
470 return 1;
472 return gcov_profile_overlap (d1_profile, d2_profile);
475 return 1;
478 /* Usage message for profile overlap. */
480 static void
481 print_overlap_usage_message (int error_p)
483 FILE *file = error_p ? stderr : stdout;
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");
494 static const struct option overlap_options[] =
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 }
505 /* Print overlap usage and exit. */
507 static void ATTRIBUTE_NORETURN
508 overlap_usage (void)
510 fnotice (stderr, "Overlap subcommand usage:");
511 print_overlap_usage_message (true);
512 exit (FATAL_EXIT_CODE);
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;
521 /* Driver for profile overlap subcommand. */
523 static int
524 do_overlap (int argc, char **argv)
526 int opt;
527 int ret;
529 optind = 0;
530 while ((opt = getopt_long (argc, argv, "vfFoht:", overlap_options, NULL)) != -1)
532 switch (opt)
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 ();
558 if (argc - optind == 2)
559 ret = profile_overlap (argv[optind], argv[optind+1]);
560 else
561 overlap_usage ();
563 return ret;
567 /* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
568 otherwise the output of --help. */
570 static void
571 print_usage (int error_p)
573 FILE *file = error_p ? stderr : stdout;
574 int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
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);
589 /* Print version information and exit. */
591 static void
592 print_version (void)
594 fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
595 fnotice (stdout, "Copyright %s 2023 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);
603 static const struct option options[] =
605 { "help", no_argument, NULL, 'h' },
606 { "version", no_argument, NULL, 'v' },
607 { 0, 0, 0, 0 }
610 /* Process args, return index to first non-arg. */
612 static int
613 process_args (int argc, char **argv)
615 int opt;
617 while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1)
619 switch (opt)
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. */
635 return optind;
638 /* Main function for gcov-tool. */
641 main (int argc, char **argv)
643 const char *p;
644 const char *sub_command;
646 p = argv[0] + strlen (argv[0]);
647 while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
648 --p;
649 progname = p;
651 xmalloc_set_program_name (progname);
653 /* Unlock the stdio streams. */
654 unlock_std_streams ();
656 gcc_init_libintl ();
658 diagnostic_initialize (global_dc, 0);
660 /* Handle response files. */
661 expandargv (&argc, &argv);
663 process_args (argc, argv);
664 if (optind >= argc)
665 print_usage (true);
667 sub_command = argv[optind];
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);
678 print_usage (true);