2015-03-24 Ed Schonberg <schonberg@adacore.com>
[official-gcc.git] / gcc / gcov-tool.c
blobfd27d7c11ad0d56c1da5fcfa5c65a45023709427
1 /* Gcc offline profile processing tool support. */
2 /* Copyright (C) 2014-2015 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 int ret;
197 const char *output_dir = 0;
198 int w1 = 1, w2 = 1;
200 optind = 0;
201 while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1)
203 switch (opt)
205 case 'v':
206 verbose = true;
207 gcov_set_verbose ();
208 break;
209 case 'o':
210 output_dir = optarg;
211 break;
212 case 'w':
213 sscanf (optarg, "%d,%d", &w1, &w2);
214 if (w1 < 0 || w2 < 0)
215 fatal_error (input_location, "weights need to be non-negative\n");
216 break;
217 default:
218 merge_usage ();
222 if (output_dir == NULL)
223 output_dir = "merged_profile";
225 if (argc - optind == 2)
226 ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2);
227 else
228 merge_usage ();
230 return ret;
233 /* If N_VAL is no-zero, normalize the profile by setting the largest counter
234 counter value to N_VAL and scale others counters proportionally.
235 Otherwise, multiply the all counters by SCALE. */
237 static int
238 profile_rewrite (const char *d1, const char *out, long long n_val,
239 float scale, int n, int d)
241 struct gcov_info * d1_profile;
243 d1_profile = gcov_read_profile_dir (d1, 0);
244 if (!d1_profile)
245 return 1;
247 if (n_val)
248 gcov_profile_normalize (d1_profile, (gcov_type) n_val);
249 else
250 gcov_profile_scale (d1_profile, scale, n, d);
252 gcov_output_files (out, d1_profile);
253 return 0;
256 /* Usage function for profile rewrite. */
258 static void
259 print_rewrite_usage_message (int error_p)
261 FILE *file = error_p ? stderr : stdout;
263 fnotice (file, " rewrite [options] <dir> Rewrite coverage file contents\n");
264 fnotice (file, " -v, --verbose Verbose mode\n");
265 fnotice (file, " -o, --output <dir> Output directory\n");
266 fnotice (file, " -s, --scale <float or simple-frac> Scale the profile counters\n");
267 fnotice (file, " -n, --normalize <long long> Normalize the profile\n");
270 static const struct option rewrite_options[] =
272 { "verbose", no_argument, NULL, 'v' },
273 { "output", required_argument, NULL, 'o' },
274 { "scale", required_argument, NULL, 's' },
275 { "normalize", required_argument, NULL, 'n' },
276 { 0, 0, 0, 0 }
279 /* Print profile rewrite usage and exit. */
281 static void
282 rewrite_usage (void)
284 fnotice (stderr, "Rewrite subcommand usage:");
285 print_rewrite_usage_message (true);
286 exit (FATAL_EXIT_CODE);
289 /* Driver for profile rewrite sub-command. */
291 static int
292 do_rewrite (int argc, char **argv)
294 int opt;
295 int ret;
296 const char *output_dir = 0;
297 #ifdef HAVE_LONG_LONG
298 long long normalize_val = 0;
299 #else
300 int64_t normalize_val = 0;
301 #endif
302 float scale = 0.0;
303 int numerator = 1;
304 int denominator = 1;
305 int do_scaling = 0;
307 optind = 0;
308 while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1)
310 switch (opt)
312 case 'v':
313 verbose = true;
314 gcov_set_verbose ();
315 break;
316 case 'o':
317 output_dir = optarg;
318 break;
319 case 'n':
320 if (!do_scaling)
321 #if defined(HAVE_LONG_LONG)
322 normalize_val = strtoll (optarg, (char **)NULL, 10);
323 #elif defined(INT64_T_IS_LONG)
324 normalize_val = strtol (optarg, (char **)NULL, 10);
325 #else
326 sscanf (optarg, "%" SCNd64, &normalize_val);
327 #endif
328 else
329 fnotice (stderr, "scaling cannot co-exist with normalization,"
330 " skipping\n");
331 break;
332 case 's':
333 ret = 0;
334 do_scaling = 1;
335 if (strstr (optarg, "/"))
337 ret = sscanf (optarg, "%d/%d", &numerator, &denominator);
338 if (ret == 2)
340 if (numerator < 0 || denominator <= 0)
342 fnotice (stderr, "incorrect format in scaling, using 1/1\n");
343 denominator = 1;
344 numerator = 1;
348 if (ret != 2)
350 ret = sscanf (optarg, "%f", &scale);
351 if (ret != 1)
352 fnotice (stderr, "incorrect format in scaling, using 1/1\n");
353 else
354 denominator = 0;
357 if (scale < 0.0)
358 fatal_error (input_location, "scale needs to be non-negative\n");
360 if (normalize_val != 0)
362 fnotice (stderr, "normalization cannot co-exist with scaling\n");
363 normalize_val = 0;
365 break;
366 default:
367 rewrite_usage ();
371 if (output_dir == NULL)
372 output_dir = "rewrite_profile";
374 if (argc - optind == 1)
376 if (denominator > 0)
377 ret = profile_rewrite (argv[optind], output_dir, 0, 0.0, numerator, denominator);
378 else
379 ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale, 0, 0);
381 else
382 rewrite_usage ();
384 return ret;
387 /* Driver function to computer the overlap score b/w profile D1 and D2.
388 Return 1 on error and 0 if OK. */
390 static int
391 profile_overlap (const char *d1, const char *d2)
393 struct gcov_info *d1_profile;
394 struct gcov_info *d2_profile;
396 d1_profile = gcov_read_profile_dir (d1, 0);
397 if (!d1_profile)
398 return 1;
400 if (d2)
402 d2_profile = gcov_read_profile_dir (d2, 0);
403 if (!d2_profile)
404 return 1;
406 return gcov_profile_overlap (d1_profile, d2_profile);
409 return 1;
412 /* Usage message for profile overlap. */
414 static void
415 print_overlap_usage_message (int error_p)
417 FILE *file = error_p ? stderr : stdout;
419 fnotice (file, " overlap [options] <dir1> <dir2> Compute the overlap of two profiles\n");
420 fnotice (file, " -v, --verbose Verbose mode\n");
421 fnotice (file, " -h, --hotonly Only print info for hot objects/functions\n");
422 fnotice (file, " -f, --function Print function level info\n");
423 fnotice (file, " -F, --fullname Print full filename\n");
424 fnotice (file, " -o, --object Print object level info\n");
425 fnotice (file, " -t <float>, --hot_threshold <float> Set the threshold for hotness\n");
429 static const struct option overlap_options[] =
431 { "verbose", no_argument, NULL, 'v' },
432 { "function", no_argument, NULL, 'f' },
433 { "fullname", no_argument, NULL, 'F' },
434 { "object", no_argument, NULL, 'o' },
435 { "hotonly", no_argument, NULL, 'h' },
436 { "hot_threshold", required_argument, NULL, 't' },
437 { 0, 0, 0, 0 }
440 /* Print overlap usage and exit. */
442 static void
443 overlap_usage (void)
445 fnotice (stderr, "Overlap subcomand usage:");
446 print_overlap_usage_message (true);
447 exit (FATAL_EXIT_CODE);
450 int overlap_func_level;
451 int overlap_obj_level;
452 int overlap_hot_only;
453 int overlap_use_fullname;
454 double overlap_hot_threshold = 0.005;
456 /* Driver for profile overlap sub-command. */
458 static int
459 do_overlap (int argc, char **argv)
461 int opt;
462 int ret;
464 optind = 0;
465 while ((opt = getopt_long (argc, argv, "vfFoht:", overlap_options, NULL)) != -1)
467 switch (opt)
469 case 'v':
470 verbose = true;
471 gcov_set_verbose ();
472 break;
473 case 'f':
474 overlap_func_level = 1;
475 break;
476 case 'F':
477 overlap_use_fullname = 1;
478 break;
479 case 'o':
480 overlap_obj_level = 1;
481 break;
482 case 'h':
483 overlap_hot_only = 1;
484 break;
485 case 't':
486 overlap_hot_threshold = atof (optarg);
487 break;
488 default:
489 overlap_usage ();
493 if (argc - optind == 2)
494 ret = profile_overlap (argv[optind], argv[optind+1]);
495 else
496 overlap_usage ();
498 return ret;
502 /* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
503 otherwise the output of --help. */
505 static void
506 print_usage (int error_p)
508 FILE *file = error_p ? stderr : stdout;
509 int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
511 fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname);
512 fnotice (file, "Offline tool to handle gcda counts\n\n");
513 fnotice (file, " -h, --help Print this help, then exit\n");
514 fnotice (file, " -v, --version Print version number, then exit\n");
515 print_merge_usage_message (error_p);
516 print_rewrite_usage_message (error_p);
517 print_overlap_usage_message (error_p);
518 fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
519 bug_report_url);
520 exit (status);
523 /* Print version information and exit. */
525 static void
526 print_version (void)
528 fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
529 fnotice (stdout, "Copyright %s 2014-2015 Free Software Foundation, Inc.\n",
530 _("(C)"));
531 fnotice (stdout,
532 _("This is free software; see the source for copying conditions.\n"
533 "There is NO warranty; not even for MERCHANTABILITY or \n"
534 "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
535 exit (SUCCESS_EXIT_CODE);
538 static const struct option options[] =
540 { "help", no_argument, NULL, 'h' },
541 { "version", no_argument, NULL, 'v' },
542 { 0, 0, 0, 0 }
545 /* Process args, return index to first non-arg. */
547 static int
548 process_args (int argc, char **argv)
550 int opt;
552 while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1)
554 switch (opt)
556 case 'h':
557 print_usage (false);
558 /* Print_usage will exit. */
559 case 'v':
560 print_version ();
561 /* Print_version will exit. */
562 default:
563 print_usage (true);
564 /* Print_usage will exit. */
568 return optind;
571 /* Main function for gcov-tool. */
574 main (int argc, char **argv)
576 const char *p;
577 const char *sub_command;
579 p = argv[0] + strlen (argv[0]);
580 while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
581 --p;
582 progname = p;
584 xmalloc_set_program_name (progname);
586 /* Unlock the stdio streams. */
587 unlock_std_streams ();
589 gcc_init_libintl ();
591 diagnostic_initialize (global_dc, 0);
593 /* Handle response files. */
594 expandargv (&argc, &argv);
596 process_args (argc, argv);
597 if (optind >= argc)
598 print_usage (true);
600 sub_command = argv[optind];
602 if (!strcmp (sub_command, "merge"))
603 return do_merge (argc - optind, argv + optind);
604 else if (!strcmp (sub_command, "rewrite"))
605 return do_rewrite (argc - optind, argv + optind);
606 else if (!strcmp (sub_command, "overlap"))
607 return do_overlap (argc - optind, argv + optind);
609 print_usage (true);