[gcc/]
[official-gcc.git] / gcc / gcov-tool.c
blob07b7bfa451df4edb4272d5752a47f89b67894f5b
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 struct gcov_info *gcov_list;
43 extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int);
44 extern int gcov_profile_normalize (struct gcov_info*, gcov_type);
45 extern int gcov_profile_scale (struct gcov_info*, float, int, int);
46 extern struct gcov_info* gcov_read_profile_dir (const char*, int);
47 extern void gcov_exit (void);
48 extern void gcov_set_verbose (void);
50 /* Set to verbose output mode. */
51 static bool verbose;
53 /* Remove file NAME if it has a gcda suffix. */
55 static int
56 unlink_gcda_file (const char *name,
57 const struct stat *status ATTRIBUTE_UNUSED,
58 int type ATTRIBUTE_UNUSED,
59 struct FTW *ftwbuf ATTRIBUTE_UNUSED)
61 int ret = 0;
62 int len = strlen (name);
63 int len1 = strlen (GCOV_DATA_SUFFIX);
65 if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1))
66 ret = remove (name);
68 if (ret)
69 fatal_error ("error in removing %s\n", name);
71 return ret;
74 /* Remove the gcda files in PATH recursively. */
76 static int
77 unlink_profile_dir (const char *path)
79 return nftw(path, unlink_gcda_file, 64, FTW_DEPTH | FTW_PHYS);
82 /* Output GCOV_INFO lists PROFILE to directory OUT. Note that
83 we will remove all the gcda files in OUT. */
85 static void
86 gcov_output_files (const char *out, struct gcov_info *profile)
88 char *pwd;
89 int ret;
91 /* Try to make directory if it doesn't already exist. */
92 if (access (out, F_OK) == -1)
94 #if !defined(_WIN32)
95 if (mkdir (out, S_IRWXU | S_IRWXG | S_IRWXO) == -1 && errno != EEXIST)
96 #else
97 if (mkdir (out) == -1 && errno != EEXIST)
98 #endif
99 fatal_error ("Cannot make directory %s", out);
100 } else
101 unlink_profile_dir (out);
103 /* Output new profile. */
104 pwd = getcwd (NULL, 0);
106 if (pwd == NULL)
107 fatal_error ("Cannot get current directory name");
109 ret = chdir (out);
110 if (ret)
111 fatal_error ("Cannot change directory to %s", out);
113 gcov_list = profile;
114 gcov_exit ();
116 ret = chdir (pwd);
117 if (ret)
118 fatal_error ("Cannot change directory to %s", pwd);
120 free (pwd);
123 /* Merging profile D1 and D2 with weight as W1 and W2, respectively.
124 The result profile is written to directory OUT.
125 Return 0 on success. */
127 static int
128 profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2)
130 struct gcov_info *d1_profile;
131 struct gcov_info *d2_profile;
132 int ret;
134 d1_profile = gcov_read_profile_dir (d1, 0);
135 if (!d1_profile)
136 return 1;
138 if (d2)
140 d2_profile = gcov_read_profile_dir (d2, 0);
141 if (!d2_profile)
142 return 1;
144 /* The actual merge: we overwrite to d1_profile. */
145 ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2);
147 if (ret)
148 return ret;
151 gcov_output_files (out, d1_profile);
153 return 0;
156 /* Usage message for profile merge. */
158 static void
159 print_merge_usage_message (int error_p)
161 FILE *file = error_p ? stderr : stdout;
163 fnotice (file, " merge [options] <dir1> <dir2> Merge coverage file contents\n");
164 fnotice (file, " -v, --verbose Verbose mode\n");
165 fnotice (file, " -o, --output <dir> Output directory\n");
166 fnotice (file, " -w, --weight <w1,w2> Set weights (float point values)\n");
169 static const struct option merge_options[] =
171 { "verbose", no_argument, NULL, 'v' },
172 { "output", required_argument, NULL, 'o' },
173 { "weight", required_argument, NULL, 'w' },
174 { 0, 0, 0, 0 }
177 /* Print merge usage and exit. */
179 static void
180 merge_usage (void)
182 fnotice (stderr, "Merge subcomand usage:");
183 print_merge_usage_message (true);
184 exit (FATAL_EXIT_CODE);
187 /* Driver for profile merge sub-command. */
189 static int
190 do_merge (int argc, char **argv)
192 int opt;
193 int ret;
194 const char *output_dir = 0;
195 int w1 = 1, w2 = 1;
197 optind = 0;
198 while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1)
200 switch (opt)
202 case 'v':
203 verbose = true;
204 gcov_set_verbose ();
205 break;
206 case 'o':
207 output_dir = optarg;
208 break;
209 case 'w':
210 sscanf (optarg, "%d,%d", &w1, &w2);
211 if (w1 < 0 || w2 < 0)
212 fatal_error ("weights need to be non-negative\n");
213 break;
214 default:
215 merge_usage ();
219 if (output_dir == NULL)
220 output_dir = "merged_profile";
222 if (argc - optind == 2)
223 ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2);
224 else
225 merge_usage ();
227 return ret;
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, long long 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 <long long> 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 long long 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 normalize_val = atoll (optarg);
315 else
316 fnotice (stderr, "scaling cannot co-exist with normalization,"
317 " skipping\n");
318 break;
319 case 's':
320 ret = 0;
321 do_scaling = 1;
322 if (strstr (optarg, "/"))
324 ret = sscanf (optarg, "%d/%d", &numerator, &denominator);
325 if (ret == 2)
327 if (numerator < 0 || denominator <= 0)
329 fnotice (stderr, "incorrect format in scaling, using 1/1\n");
330 denominator = 1;
331 numerator = 1;
335 if (ret != 2)
337 ret = sscanf (optarg, "%f", &scale);
338 if (ret != 1)
339 fnotice (stderr, "incorrect format in scaling, using 1/1\n");
340 else
341 denominator = 0;
344 if (scale < 0.0)
345 fatal_error ("scale needs to be non-negative\n");
347 if (normalize_val != 0)
349 fnotice (stderr, "normalization cannot co-exist with scaling\n");
350 normalize_val = 0;
352 break;
353 default:
354 rewrite_usage ();
358 if (output_dir == NULL)
359 output_dir = "rewrite_profile";
361 if (argc - optind == 1)
363 if (denominator > 0)
364 ret = profile_rewrite (argv[optind], output_dir, 0, 0.0, numerator, denominator);
365 else
366 ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale, 0, 0);
368 else
369 rewrite_usage ();
371 return ret;
374 /* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
375 otherwise the output of --help. */
377 static void
378 print_usage (int error_p)
380 FILE *file = error_p ? stderr : stdout;
381 int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
383 fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname);
384 fnotice (file, "Offline tool to handle gcda counts\n\n");
385 fnotice (file, " -h, --help Print this help, then exit\n");
386 fnotice (file, " -v, --version Print version number, then exit\n");
387 print_merge_usage_message (error_p);
388 print_rewrite_usage_message (error_p);
389 fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
390 bug_report_url);
391 exit (status);
394 /* Print version information and exit. */
396 static void
397 print_version (void)
399 fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
400 fnotice (stdout, "Copyright %s 2014 Free Software Foundation, Inc.\n",
401 _("(C)"));
402 fnotice (stdout,
403 _("This is free software; see the source for copying conditions.\n"
404 "There is NO warranty; not even for MERCHANTABILITY or \n"
405 "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
406 exit (SUCCESS_EXIT_CODE);
409 static const struct option options[] =
411 { "help", no_argument, NULL, 'h' },
412 { "version", no_argument, NULL, 'v' },
413 { 0, 0, 0, 0 }
416 /* Process args, return index to first non-arg. */
418 static int
419 process_args (int argc, char **argv)
421 int opt;
423 while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1)
425 switch (opt)
427 case 'h':
428 print_usage (false);
429 /* Print_usage will exit. */
430 case 'v':
431 print_version ();
432 /* Print_version will exit. */
433 default:
434 print_usage (true);
435 /* Print_usage will exit. */
439 return optind;
442 /* Main function for gcov-tool. */
445 main (int argc, char **argv)
447 const char *p;
448 const char *sub_command;
450 p = argv[0] + strlen (argv[0]);
451 while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
452 --p;
453 progname = p;
455 xmalloc_set_program_name (progname);
457 /* Unlock the stdio streams. */
458 unlock_std_streams ();
460 gcc_init_libintl ();
462 diagnostic_initialize (global_dc, 0);
464 /* Handle response files. */
465 expandargv (&argc, &argv);
467 process_args (argc, argv);
468 if (optind >= argc)
469 print_usage (true);
471 sub_command = argv[optind];
473 if (!strcmp (sub_command, "merge"))
474 return do_merge (argc - optind, argv + optind);
475 else if (!strcmp (sub_command, "rewrite"))
476 return do_rewrite (argc - optind, argv + optind);
478 print_usage (true);