PR c++/79896
[official-gcc.git] / gcc / gcov-tool.c
blobcadf09377ddb56997c92fca8c2efc818ed895e3a
1 /* Gcc offline profile processing tool support. */
2 /* Copyright (C) 2014-2017 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 int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int);
44 extern int gcov_profile_overlap (struct gcov_info*, struct gcov_info*);
45 extern int gcov_profile_normalize (struct gcov_info*, gcov_type);
46 extern int gcov_profile_scale (struct gcov_info*, float, int, int);
47 extern struct gcov_info* gcov_read_profile_dir (const char*, int);
48 extern void gcov_do_dump (struct gcov_info *, int);
49 extern void gcov_set_verbose (void);
51 /* Set to verbose output mode. */
52 static bool verbose;
54 #if HAVE_FTW_H
56 /* Remove file NAME if it has a gcda suffix. */
58 static int
59 unlink_gcda_file (const char *name,
60 const struct stat *status ATTRIBUTE_UNUSED,
61 int type ATTRIBUTE_UNUSED,
62 struct FTW *ftwbuf ATTRIBUTE_UNUSED)
64 int ret = 0;
65 int len = strlen (name);
66 int len1 = strlen (GCOV_DATA_SUFFIX);
68 if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1))
69 ret = remove (name);
71 if (ret)
72 fatal_error (input_location, "error in removing %s\n", name);
74 return ret;
76 #endif
78 /* Remove the gcda files in PATH recursively. */
80 static int
81 unlink_profile_dir (const char *path ATTRIBUTE_UNUSED)
83 #if HAVE_FTW_H
84 return nftw(path, unlink_gcda_file, 64, FTW_DEPTH | FTW_PHYS);
85 #else
86 return -1;
87 #endif
90 /* Output GCOV_INFO lists PROFILE to directory OUT. Note that
91 we will remove all the gcda files in OUT. */
93 static void
94 gcov_output_files (const char *out, struct gcov_info *profile)
96 char *pwd;
97 int ret;
99 /* Try to make directory if it doesn't already exist. */
100 if (access (out, F_OK) == -1)
102 if (mkdir (out, S_IRWXU | S_IRWXG | S_IRWXO) == -1 && errno != EEXIST)
103 fatal_error (input_location, "Cannot make directory %s", out);
104 } else
105 unlink_profile_dir (out);
107 /* Output new profile. */
108 pwd = getcwd (NULL, 0);
110 if (pwd == NULL)
111 fatal_error (input_location, "Cannot get current directory name");
113 ret = chdir (out);
114 if (ret)
115 fatal_error (input_location, "Cannot change directory to %s", out);
117 gcov_do_dump (profile, 0);
119 ret = chdir (pwd);
120 if (ret)
121 fatal_error (input_location, "Cannot change directory to %s", pwd);
123 free (pwd);
126 /* Merging profile D1 and D2 with weight as W1 and W2, respectively.
127 The result profile is written to directory OUT.
128 Return 0 on success. */
130 static int
131 profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2)
133 struct gcov_info *d1_profile;
134 struct gcov_info *d2_profile;
135 int ret;
137 d1_profile = gcov_read_profile_dir (d1, 0);
138 if (!d1_profile)
139 return 1;
141 if (d2)
143 d2_profile = gcov_read_profile_dir (d2, 0);
144 if (!d2_profile)
145 return 1;
147 /* The actual merge: we overwrite to d1_profile. */
148 ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2);
150 if (ret)
151 return ret;
154 gcov_output_files (out, d1_profile);
156 return 0;
159 /* Usage message for profile merge. */
161 static void
162 print_merge_usage_message (int error_p)
164 FILE *file = error_p ? stderr : stdout;
166 fnotice (file, " merge [options] <dir1> <dir2> Merge coverage file contents\n");
167 fnotice (file, " -v, --verbose Verbose mode\n");
168 fnotice (file, " -o, --output <dir> Output directory\n");
169 fnotice (file, " -w, --weight <w1,w2> Set weights (float point values)\n");
172 static const struct option merge_options[] =
174 { "verbose", no_argument, NULL, 'v' },
175 { "output", required_argument, NULL, 'o' },
176 { "weight", required_argument, NULL, 'w' },
177 { 0, 0, 0, 0 }
180 /* Print merge usage and exit. */
182 static void
183 merge_usage (void)
185 fnotice (stderr, "Merge subcomand usage:");
186 print_merge_usage_message (true);
187 exit (FATAL_EXIT_CODE);
190 /* Driver for profile merge sub-command. */
192 static int
193 do_merge (int argc, char **argv)
195 int opt;
196 const char *output_dir = 0;
197 int w1 = 1, w2 = 1;
199 optind = 0;
200 while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1)
202 switch (opt)
204 case 'v':
205 verbose = true;
206 gcov_set_verbose ();
207 break;
208 case 'o':
209 output_dir = optarg;
210 break;
211 case 'w':
212 sscanf (optarg, "%d,%d", &w1, &w2);
213 if (w1 < 0 || w2 < 0)
214 fatal_error (input_location, "weights need to be non-negative\n");
215 break;
216 default:
217 merge_usage ();
221 if (output_dir == NULL)
222 output_dir = "merged_profile";
224 if (argc - optind != 2)
225 merge_usage ();
227 return profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2);
230 /* If N_VAL is no-zero, normalize the profile by setting the largest counter
231 counter value to N_VAL and scale others counters proportionally.
232 Otherwise, multiply the all counters by SCALE. */
234 static int
235 profile_rewrite (const char *d1, const char *out, int64_t n_val,
236 float scale, int n, int d)
238 struct gcov_info * d1_profile;
240 d1_profile = gcov_read_profile_dir (d1, 0);
241 if (!d1_profile)
242 return 1;
244 if (n_val)
245 gcov_profile_normalize (d1_profile, (gcov_type) n_val);
246 else
247 gcov_profile_scale (d1_profile, scale, n, d);
249 gcov_output_files (out, d1_profile);
250 return 0;
253 /* Usage function for profile rewrite. */
255 static void
256 print_rewrite_usage_message (int error_p)
258 FILE *file = error_p ? stderr : stdout;
260 fnotice (file, " rewrite [options] <dir> Rewrite coverage file contents\n");
261 fnotice (file, " -v, --verbose Verbose mode\n");
262 fnotice (file, " -o, --output <dir> Output directory\n");
263 fnotice (file, " -s, --scale <float or simple-frac> Scale the profile counters\n");
264 fnotice (file, " -n, --normalize <int64_t> Normalize the profile\n");
267 static const struct option rewrite_options[] =
269 { "verbose", no_argument, NULL, 'v' },
270 { "output", required_argument, NULL, 'o' },
271 { "scale", required_argument, NULL, 's' },
272 { "normalize", required_argument, NULL, 'n' },
273 { 0, 0, 0, 0 }
276 /* Print profile rewrite usage and exit. */
278 static void
279 rewrite_usage (void)
281 fnotice (stderr, "Rewrite subcommand usage:");
282 print_rewrite_usage_message (true);
283 exit (FATAL_EXIT_CODE);
286 /* Driver for profile rewrite sub-command. */
288 static int
289 do_rewrite (int argc, char **argv)
291 int opt;
292 int ret;
293 const char *output_dir = 0;
294 int64_t normalize_val = 0;
295 float scale = 0.0;
296 int numerator = 1;
297 int denominator = 1;
298 int do_scaling = 0;
300 optind = 0;
301 while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1)
303 switch (opt)
305 case 'v':
306 verbose = true;
307 gcov_set_verbose ();
308 break;
309 case 'o':
310 output_dir = optarg;
311 break;
312 case 'n':
313 if (!do_scaling)
314 #if defined(INT64_T_IS_LONG)
315 normalize_val = strtol (optarg, (char **)NULL, 10);
316 #else
317 normalize_val = strtoll (optarg, (char **)NULL, 10);
318 #endif
319 else
320 fnotice (stderr, "scaling cannot co-exist with normalization,"
321 " skipping\n");
322 break;
323 case 's':
324 ret = 0;
325 do_scaling = 1;
326 if (strstr (optarg, "/"))
328 ret = sscanf (optarg, "%d/%d", &numerator, &denominator);
329 if (ret == 2)
331 if (numerator < 0 || denominator <= 0)
333 fnotice (stderr, "incorrect format in scaling, using 1/1\n");
334 denominator = 1;
335 numerator = 1;
339 if (ret != 2)
341 ret = sscanf (optarg, "%f", &scale);
342 if (ret != 1)
343 fnotice (stderr, "incorrect format in scaling, using 1/1\n");
344 else
345 denominator = 0;
348 if (scale < 0.0)
349 fatal_error (input_location, "scale needs to be non-negative\n");
351 if (normalize_val != 0)
353 fnotice (stderr, "normalization cannot co-exist with scaling\n");
354 normalize_val = 0;
356 break;
357 default:
358 rewrite_usage ();
362 if (output_dir == NULL)
363 output_dir = "rewrite_profile";
365 if (argc - optind == 1)
367 if (denominator > 0)
368 ret = profile_rewrite (argv[optind], output_dir, 0, 0.0, numerator, denominator);
369 else
370 ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale, 0, 0);
372 else
373 rewrite_usage ();
375 return ret;
378 /* Driver function to computer the overlap score b/w profile D1 and D2.
379 Return 1 on error and 0 if OK. */
381 static int
382 profile_overlap (const char *d1, const char *d2)
384 struct gcov_info *d1_profile;
385 struct gcov_info *d2_profile;
387 d1_profile = gcov_read_profile_dir (d1, 0);
388 if (!d1_profile)
389 return 1;
391 if (d2)
393 d2_profile = gcov_read_profile_dir (d2, 0);
394 if (!d2_profile)
395 return 1;
397 return gcov_profile_overlap (d1_profile, d2_profile);
400 return 1;
403 /* Usage message for profile overlap. */
405 static void
406 print_overlap_usage_message (int error_p)
408 FILE *file = error_p ? stderr : stdout;
410 fnotice (file, " overlap [options] <dir1> <dir2> Compute the overlap of two profiles\n");
411 fnotice (file, " -v, --verbose Verbose mode\n");
412 fnotice (file, " -h, --hotonly Only print info for hot objects/functions\n");
413 fnotice (file, " -f, --function Print function level info\n");
414 fnotice (file, " -F, --fullname Print full filename\n");
415 fnotice (file, " -o, --object Print object level info\n");
416 fnotice (file, " -t <float>, --hot_threshold <float> Set the threshold for hotness\n");
420 static const struct option overlap_options[] =
422 { "verbose", no_argument, NULL, 'v' },
423 { "function", no_argument, NULL, 'f' },
424 { "fullname", no_argument, NULL, 'F' },
425 { "object", no_argument, NULL, 'o' },
426 { "hotonly", no_argument, NULL, 'h' },
427 { "hot_threshold", required_argument, NULL, 't' },
428 { 0, 0, 0, 0 }
431 /* Print overlap usage and exit. */
433 static void
434 overlap_usage (void)
436 fnotice (stderr, "Overlap subcomand usage:");
437 print_overlap_usage_message (true);
438 exit (FATAL_EXIT_CODE);
441 int overlap_func_level;
442 int overlap_obj_level;
443 int overlap_hot_only;
444 int overlap_use_fullname;
445 double overlap_hot_threshold = 0.005;
447 /* Driver for profile overlap sub-command. */
449 static int
450 do_overlap (int argc, char **argv)
452 int opt;
453 int ret;
455 optind = 0;
456 while ((opt = getopt_long (argc, argv, "vfFoht:", overlap_options, NULL)) != -1)
458 switch (opt)
460 case 'v':
461 verbose = true;
462 gcov_set_verbose ();
463 break;
464 case 'f':
465 overlap_func_level = 1;
466 break;
467 case 'F':
468 overlap_use_fullname = 1;
469 break;
470 case 'o':
471 overlap_obj_level = 1;
472 break;
473 case 'h':
474 overlap_hot_only = 1;
475 break;
476 case 't':
477 overlap_hot_threshold = atof (optarg);
478 break;
479 default:
480 overlap_usage ();
484 if (argc - optind == 2)
485 ret = profile_overlap (argv[optind], argv[optind+1]);
486 else
487 overlap_usage ();
489 return ret;
493 /* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
494 otherwise the output of --help. */
496 static void
497 print_usage (int error_p)
499 FILE *file = error_p ? stderr : stdout;
500 int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
502 fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname);
503 fnotice (file, "Offline tool to handle gcda counts\n\n");
504 fnotice (file, " -h, --help Print this help, then exit\n");
505 fnotice (file, " -v, --version Print version number, then exit\n");
506 print_merge_usage_message (error_p);
507 print_rewrite_usage_message (error_p);
508 print_overlap_usage_message (error_p);
509 fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
510 bug_report_url);
511 exit (status);
514 /* Print version information and exit. */
516 static void
517 print_version (void)
519 fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
520 fnotice (stdout, "Copyright %s 2014-2017 Free Software Foundation, Inc.\n",
521 _("(C)"));
522 fnotice (stdout,
523 _("This is free software; see the source for copying conditions.\n"
524 "There is NO warranty; not even for MERCHANTABILITY or \n"
525 "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
526 exit (SUCCESS_EXIT_CODE);
529 static const struct option options[] =
531 { "help", no_argument, NULL, 'h' },
532 { "version", no_argument, NULL, 'v' },
533 { 0, 0, 0, 0 }
536 /* Process args, return index to first non-arg. */
538 static int
539 process_args (int argc, char **argv)
541 int opt;
543 while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1)
545 switch (opt)
547 case 'h':
548 print_usage (false);
549 /* Print_usage will exit. */
550 /* FALLTHRU */
551 case 'v':
552 print_version ();
553 /* Print_version will exit. */
554 /* FALLTHRU */
555 default:
556 print_usage (true);
557 /* Print_usage will exit. */
561 return optind;
564 /* Main function for gcov-tool. */
567 main (int argc, char **argv)
569 const char *p;
570 const char *sub_command;
572 p = argv[0] + strlen (argv[0]);
573 while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
574 --p;
575 progname = p;
577 xmalloc_set_program_name (progname);
579 /* Unlock the stdio streams. */
580 unlock_std_streams ();
582 gcc_init_libintl ();
584 diagnostic_initialize (global_dc, 0);
586 /* Handle response files. */
587 expandargv (&argc, &argv);
589 process_args (argc, argv);
590 if (optind >= argc)
591 print_usage (true);
593 sub_command = argv[optind];
595 if (!strcmp (sub_command, "merge"))
596 return do_merge (argc - optind, argv + optind);
597 else if (!strcmp (sub_command, "rewrite"))
598 return do_rewrite (argc - optind, argv + optind);
599 else if (!strcmp (sub_command, "overlap"))
600 return do_overlap (argc - optind, argv + optind);
602 print_usage (true);