AVX-512. Branch to hold overall changes introduced by 20140717 EAS.
[official-gcc.git] / gcc / gcov-tool.c
blob71331cab9d1b230e77c5683ad2b9abb3142cb65b
1 /* Gcc offline profile processing tool support. */
2 /* Copyright (C) 2014 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 #include <ftw.h>
39 #include <getopt.h>
41 extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int);
42 extern int gcov_profile_normalize (struct gcov_info*, gcov_type);
43 extern int gcov_profile_scale (struct gcov_info*, float, int, int);
44 extern struct gcov_info* gcov_read_profile_dir (const char*, int);
45 extern void gcov_exit (void);
46 extern void set_gcov_list (struct gcov_info *);
47 extern void gcov_set_verbose (void);
49 /* Set to verbose output mode. */
50 static bool verbose;
52 /* Remove file NAME if it has a gcda suffix. */
54 static int
55 unlink_gcda_file (const char *name,
56 const struct stat *status ATTRIBUTE_UNUSED,
57 int type ATTRIBUTE_UNUSED,
58 struct FTW *ftwbuf ATTRIBUTE_UNUSED)
60 int ret = 0;
61 int len = strlen (name);
62 int len1 = strlen (GCOV_DATA_SUFFIX);
64 if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1))
65 ret = remove (name);
67 if (ret)
68 fatal_error ("error in removing %s\n", name);
70 return ret;
73 /* Remove the gcda files in PATH recursively. */
75 static int
76 unlink_profile_dir (const char *path)
78 return nftw(path, unlink_gcda_file, 64, FTW_DEPTH | FTW_PHYS);
81 /* Output GCOV_INFO lists PROFILE to directory OUT. Note that
82 we will remove all the gcda files in OUT. */
84 static void
85 gcov_output_files (const char *out, struct gcov_info *profile)
87 char *pwd;
88 int ret;
90 /* Try to make directory if it doesn't already exist. */
91 if (access (out, F_OK) == -1)
93 #if !defined(_WIN32)
94 if (mkdir (out, S_IRWXU | S_IRWXG | S_IRWXO) == -1 && errno != EEXIST)
95 #else
96 if (mkdir (out) == -1 && errno != EEXIST)
97 #endif
98 fatal_error ("Cannot make directory %s", out);
99 } else
100 unlink_profile_dir (out);
102 /* Output new profile. */
103 pwd = getcwd (NULL, 0);
105 if (pwd == NULL)
106 fatal_error ("Cannot get current directory name");
108 ret = chdir (out);
109 if (ret)
110 fatal_error ("Cannot change directory to %s", out);
112 set_gcov_list (profile);
113 gcov_exit ();
115 ret = chdir (pwd);
116 if (ret)
117 fatal_error ("Cannot change directory to %s", pwd);
119 free (pwd);
122 /* Merging profile D1 and D2 with weight as W1 and W2, respectively.
123 The result profile is written to directory OUT.
124 Return 0 on success. */
126 static int
127 profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2)
129 struct gcov_info *d1_profile;
130 struct gcov_info *d2_profile;
131 int ret;
133 d1_profile = gcov_read_profile_dir (d1, 0);
134 if (!d1_profile)
135 return 1;
137 if (d2)
139 d2_profile = gcov_read_profile_dir (d2, 0);
140 if (!d2_profile)
141 return 1;
143 /* The actual merge: we overwrite to d1_profile. */
144 ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2);
146 if (ret)
147 return ret;
150 gcov_output_files (out, d1_profile);
152 return 0;
155 /* Usage message for profile merge. */
157 static void
158 print_merge_usage_message (int error_p)
160 FILE *file = error_p ? stderr : stdout;
162 fnotice (file, " merge [options] <dir1> <dir2> Merge coverage file contents\n");
163 fnotice (file, " -v, --verbose Verbose mode\n");
164 fnotice (file, " -o, --output <dir> Output directory\n");
165 fnotice (file, " -w, --weight <w1,w2> Set weights (float point values)\n");
168 static const struct option merge_options[] =
170 { "verbose", no_argument, NULL, 'v' },
171 { "output", required_argument, NULL, 'o' },
172 { "weight", required_argument, NULL, 'w' },
173 { 0, 0, 0, 0 }
176 /* Print merge usage and exit. */
178 static void
179 merge_usage (void)
181 fnotice (stderr, "Merge subcomand usage:");
182 print_merge_usage_message (true);
183 exit (FATAL_EXIT_CODE);
186 /* Driver for profile merge sub-command. */
188 static int
189 do_merge (int argc, char **argv)
191 int opt;
192 int ret;
193 const char *output_dir = 0;
194 int w1 = 1, w2 = 1;
196 optind = 0;
197 while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1)
199 switch (opt)
201 case 'v':
202 verbose = true;
203 gcov_set_verbose ();
204 break;
205 case 'o':
206 output_dir = optarg;
207 break;
208 case 'w':
209 sscanf (optarg, "%d,%d", &w1, &w2);
210 if (w1 < 0 || w2 < 0)
211 fatal_error ("weights need to be non-negative\n");
212 break;
213 default:
214 merge_usage ();
218 if (output_dir == NULL)
219 output_dir = "merged_profile";
221 if (argc - optind == 2)
222 ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2);
223 else
224 merge_usage ();
226 return ret;
229 /* If N_VAL is no-zero, normalize the profile by setting the largest counter
230 counter value to N_VAL and scale others counters proportionally.
231 Otherwise, multiply the all counters by SCALE. */
233 static int
234 profile_rewrite (const char *d1, const char *out, long long n_val,
235 float scale, int n, int d)
237 struct gcov_info * d1_profile;
239 d1_profile = gcov_read_profile_dir (d1, 0);
240 if (!d1_profile)
241 return 1;
243 if (n_val)
244 gcov_profile_normalize (d1_profile, (gcov_type) n_val);
245 else
246 gcov_profile_scale (d1_profile, scale, n, d);
248 gcov_output_files (out, d1_profile);
249 return 0;
252 /* Usage function for profile rewrite. */
254 static void
255 print_rewrite_usage_message (int error_p)
257 FILE *file = error_p ? stderr : stdout;
259 fnotice (file, " rewrite [options] <dir> Rewrite coverage file contents\n");
260 fnotice (file, " -v, --verbose Verbose mode\n");
261 fnotice (file, " -o, --output <dir> Output directory\n");
262 fnotice (file, " -s, --scale <float or simple-frac> Scale the profile counters\n");
263 fnotice (file, " -n, --normalize <long long> Normalize the profile\n");
266 static const struct option rewrite_options[] =
268 { "verbose", no_argument, NULL, 'v' },
269 { "output", required_argument, NULL, 'o' },
270 { "scale", required_argument, NULL, 's' },
271 { "normalize", required_argument, NULL, 'n' },
272 { 0, 0, 0, 0 }
275 /* Print profile rewrite usage and exit. */
277 static void
278 rewrite_usage (void)
280 fnotice (stderr, "Rewrite subcommand usage:");
281 print_rewrite_usage_message (true);
282 exit (FATAL_EXIT_CODE);
285 /* Driver for profile rewrite sub-command. */
287 static int
288 do_rewrite (int argc, char **argv)
290 int opt;
291 int ret;
292 const char *output_dir = 0;
293 long long normalize_val = 0;
294 float scale = 0.0;
295 int numerator = 1;
296 int denominator = 1;
297 int do_scaling = 0;
299 optind = 0;
300 while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1)
302 switch (opt)
304 case 'v':
305 verbose = true;
306 gcov_set_verbose ();
307 break;
308 case 'o':
309 output_dir = optarg;
310 break;
311 case 'n':
312 if (!do_scaling)
313 normalize_val = atoll (optarg);
314 else
315 fnotice (stderr, "scaling cannot co-exist with normalization,"
316 " skipping\n");
317 break;
318 case 's':
319 ret = 0;
320 do_scaling = 1;
321 if (strstr (optarg, "/"))
323 ret = sscanf (optarg, "%d/%d", &numerator, &denominator);
324 if (ret == 2)
326 if (numerator < 0 || denominator <= 0)
328 fnotice (stderr, "incorrect format in scaling, using 1/1\n");
329 denominator = 1;
330 numerator = 1;
334 if (ret != 2)
336 ret = sscanf (optarg, "%f", &scale);
337 if (ret != 1)
338 fnotice (stderr, "incorrect format in scaling, using 1/1\n");
339 else
340 denominator = 0;
343 if (scale < 0.0)
344 fatal_error ("scale needs to be non-negative\n");
346 if (normalize_val != 0)
348 fnotice (stderr, "normalization cannot co-exist with scaling\n");
349 normalize_val = 0;
351 break;
352 default:
353 rewrite_usage ();
357 if (output_dir == NULL)
358 output_dir = "rewrite_profile";
360 if (argc - optind == 1)
362 if (denominator > 0)
363 ret = profile_rewrite (argv[optind], output_dir, 0, 0.0, numerator, denominator);
364 else
365 ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale, 0, 0);
367 else
368 rewrite_usage ();
370 return ret;
373 /* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
374 otherwise the output of --help. */
376 static void
377 print_usage (int error_p)
379 FILE *file = error_p ? stderr : stdout;
380 int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
382 fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname);
383 fnotice (file, "Offline tool to handle gcda counts\n\n");
384 fnotice (file, " -h, --help Print this help, then exit\n");
385 fnotice (file, " -v, --version Print version number, then exit\n");
386 print_merge_usage_message (error_p);
387 print_rewrite_usage_message (error_p);
388 fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
389 bug_report_url);
390 exit (status);
393 /* Print version information and exit. */
395 static void
396 print_version (void)
398 fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
399 fnotice (stdout, "Copyright %s 2014 Free Software Foundation, Inc.\n",
400 _("(C)"));
401 fnotice (stdout,
402 _("This is free software; see the source for copying conditions.\n"
403 "There is NO warranty; not even for MERCHANTABILITY or \n"
404 "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
405 exit (SUCCESS_EXIT_CODE);
408 static const struct option options[] =
410 { "help", no_argument, NULL, 'h' },
411 { "version", no_argument, NULL, 'v' },
412 { 0, 0, 0, 0 }
415 /* Process args, return index to first non-arg. */
417 static int
418 process_args (int argc, char **argv)
420 int opt;
422 while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1)
424 switch (opt)
426 case 'h':
427 print_usage (false);
428 /* Print_usage will exit. */
429 case 'v':
430 print_version ();
431 /* Print_version will exit. */
432 default:
433 print_usage (true);
434 /* Print_usage will exit. */
438 return optind;
441 /* Main function for gcov-tool. */
444 main (int argc, char **argv)
446 const char *p;
447 const char *sub_command;
449 p = argv[0] + strlen (argv[0]);
450 while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
451 --p;
452 progname = p;
454 xmalloc_set_program_name (progname);
456 /* Unlock the stdio streams. */
457 unlock_std_streams ();
459 gcc_init_libintl ();
461 diagnostic_initialize (global_dc, 0);
463 /* Handle response files. */
464 expandargv (&argc, &argv);
466 process_args (argc, argv);
467 if (optind >= argc)
468 print_usage (true);
470 sub_command = argv[optind];
472 if (!strcmp (sub_command, "merge"))
473 return do_merge (argc - optind, argv + optind);
474 else if (!strcmp (sub_command, "rewrite"))
475 return do_rewrite (argc - optind, argv + optind);
477 print_usage (true);