]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/gcov-tool.c
re PR c++/63405 (ICE in cp_perform_integral_promotions, at cp/typeck.c:2084)
[thirdparty/gcc.git] / gcc / gcov-tool.c
CommitLineData
c77556a5
RX
1/* Gcc offline profile processing tool support. */
2/* Copyright (C) 2014 Free Software Foundation, Inc.
3 Contributed by Rong Xu <xur@google.com>.
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17Under Section 7 of GPL version 3, you are granted additional
18permissions described in the GCC Runtime Library Exception, version
193.1, as published by the Free Software Foundation.
20
21You should have received a copy of the GNU General Public License and
22a copy of the GCC Runtime Library Exception along with this program;
23see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
24<http://www.gnu.org/licenses/>. */
25
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>
40
41extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int);
42extern int gcov_profile_normalize (struct gcov_info*, gcov_type);
43extern int gcov_profile_scale (struct gcov_info*, float, int, int);
44extern struct gcov_info* gcov_read_profile_dir (const char*, int);
19926161 45extern void gcov_do_dump (struct gcov_info *, int);
c77556a5
RX
46extern void gcov_set_verbose (void);
47
48/* Set to verbose output mode. */
49static bool verbose;
50
51/* Remove file NAME if it has a gcda suffix. */
52
53static int
54unlink_gcda_file (const char *name,
55 const struct stat *status ATTRIBUTE_UNUSED,
56 int type ATTRIBUTE_UNUSED,
57 struct FTW *ftwbuf ATTRIBUTE_UNUSED)
58{
59 int ret = 0;
60 int len = strlen (name);
61 int len1 = strlen (GCOV_DATA_SUFFIX);
62
63 if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1))
64 ret = remove (name);
65
66 if (ret)
67 fatal_error ("error in removing %s\n", name);
68
69 return ret;
70}
71
72/* Remove the gcda files in PATH recursively. */
73
74static int
75unlink_profile_dir (const char *path)
76{
77 return nftw(path, unlink_gcda_file, 64, FTW_DEPTH | FTW_PHYS);
78}
79
80/* Output GCOV_INFO lists PROFILE to directory OUT. Note that
81 we will remove all the gcda files in OUT. */
82
83static void
84gcov_output_files (const char *out, struct gcov_info *profile)
85{
86 char *pwd;
87 int ret;
88
89 /* Try to make directory if it doesn't already exist. */
90 if (access (out, F_OK) == -1)
91 {
68f68004
RX
92#if !defined(_WIN32)
93 if (mkdir (out, S_IRWXU | S_IRWXG | S_IRWXO) == -1 && errno != EEXIST)
c77556a5
RX
94#else
95 if (mkdir (out) == -1 && errno != EEXIST)
96#endif
97 fatal_error ("Cannot make directory %s", out);
98 } else
99 unlink_profile_dir (out);
100
101 /* Output new profile. */
102 pwd = getcwd (NULL, 0);
103
104 if (pwd == NULL)
105 fatal_error ("Cannot get current directory name");
106
107 ret = chdir (out);
108 if (ret)
109 fatal_error ("Cannot change directory to %s", out);
110
19926161 111 gcov_do_dump (profile, 0);
c77556a5
RX
112
113 ret = chdir (pwd);
114 if (ret)
115 fatal_error ("Cannot change directory to %s", pwd);
116
117 free (pwd);
118}
119
120/* Merging profile D1 and D2 with weight as W1 and W2, respectively.
121 The result profile is written to directory OUT.
122 Return 0 on success. */
123
124static int
125profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2)
126{
127 struct gcov_info *d1_profile;
128 struct gcov_info *d2_profile;
129 int ret;
130
131 d1_profile = gcov_read_profile_dir (d1, 0);
132 if (!d1_profile)
133 return 1;
134
135 if (d2)
136 {
137 d2_profile = gcov_read_profile_dir (d2, 0);
138 if (!d2_profile)
139 return 1;
140
141 /* The actual merge: we overwrite to d1_profile. */
142 ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2);
143
144 if (ret)
145 return ret;
146 }
147
148 gcov_output_files (out, d1_profile);
149
150 return 0;
151}
152
153/* Usage message for profile merge. */
154
155static void
156print_merge_usage_message (int error_p)
157{
158 FILE *file = error_p ? stderr : stdout;
159
160 fnotice (file, " merge [options] <dir1> <dir2> Merge coverage file contents\n");
161 fnotice (file, " -v, --verbose Verbose mode\n");
162 fnotice (file, " -o, --output <dir> Output directory\n");
163 fnotice (file, " -w, --weight <w1,w2> Set weights (float point values)\n");
164}
165
166static const struct option merge_options[] =
167{
168 { "verbose", no_argument, NULL, 'v' },
169 { "output", required_argument, NULL, 'o' },
170 { "weight", required_argument, NULL, 'w' },
171 { 0, 0, 0, 0 }
172};
173
174/* Print merge usage and exit. */
175
176static void
177merge_usage (void)
178{
179 fnotice (stderr, "Merge subcomand usage:");
180 print_merge_usage_message (true);
181 exit (FATAL_EXIT_CODE);
182}
183
184/* Driver for profile merge sub-command. */
185
186static int
187do_merge (int argc, char **argv)
188{
189 int opt;
190 int ret;
191 const char *output_dir = 0;
192 int w1 = 1, w2 = 1;
193
194 optind = 0;
195 while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1)
196 {
197 switch (opt)
198 {
199 case 'v':
200 verbose = true;
201 gcov_set_verbose ();
202 break;
203 case 'o':
204 output_dir = optarg;
205 break;
206 case 'w':
207 sscanf (optarg, "%d,%d", &w1, &w2);
208 if (w1 < 0 || w2 < 0)
209 fatal_error ("weights need to be non-negative\n");
210 break;
211 default:
212 merge_usage ();
213 }
214 }
215
216 if (output_dir == NULL)
217 output_dir = "merged_profile";
218
219 if (argc - optind == 2)
220 ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2);
221 else
222 merge_usage ();
223
224 return ret;
225}
226
227/* If N_VAL is no-zero, normalize the profile by setting the largest counter
228 counter value to N_VAL and scale others counters proportionally.
229 Otherwise, multiply the all counters by SCALE. */
230
231static int
232profile_rewrite (const char *d1, const char *out, long long n_val,
233 float scale, int n, int d)
234{
235 struct gcov_info * d1_profile;
236
237 d1_profile = gcov_read_profile_dir (d1, 0);
238 if (!d1_profile)
239 return 1;
240
241 if (n_val)
242 gcov_profile_normalize (d1_profile, (gcov_type) n_val);
243 else
244 gcov_profile_scale (d1_profile, scale, n, d);
245
246 gcov_output_files (out, d1_profile);
247 return 0;
248}
249
250/* Usage function for profile rewrite. */
251
252static void
253print_rewrite_usage_message (int error_p)
254{
255 FILE *file = error_p ? stderr : stdout;
256
257 fnotice (file, " rewrite [options] <dir> Rewrite coverage file contents\n");
258 fnotice (file, " -v, --verbose Verbose mode\n");
259 fnotice (file, " -o, --output <dir> Output directory\n");
260 fnotice (file, " -s, --scale <float or simple-frac> Scale the profile counters\n");
261 fnotice (file, " -n, --normalize <long long> Normalize the profile\n");
262}
263
264static const struct option rewrite_options[] =
265{
266 { "verbose", no_argument, NULL, 'v' },
267 { "output", required_argument, NULL, 'o' },
268 { "scale", required_argument, NULL, 's' },
269 { "normalize", required_argument, NULL, 'n' },
270 { 0, 0, 0, 0 }
271};
272
273/* Print profile rewrite usage and exit. */
274
275static void
276rewrite_usage (void)
277{
278 fnotice (stderr, "Rewrite subcommand usage:");
279 print_rewrite_usage_message (true);
280 exit (FATAL_EXIT_CODE);
281}
282
283/* Driver for profile rewrite sub-command. */
284
285static int
286do_rewrite (int argc, char **argv)
287{
288 int opt;
289 int ret;
290 const char *output_dir = 0;
291 long long normalize_val = 0;
292 float scale = 0.0;
293 int numerator = 1;
294 int denominator = 1;
295 int do_scaling = 0;
296
297 optind = 0;
298 while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1)
299 {
300 switch (opt)
301 {
302 case 'v':
303 verbose = true;
304 gcov_set_verbose ();
305 break;
306 case 'o':
307 output_dir = optarg;
308 break;
309 case 'n':
310 if (!do_scaling)
311 normalize_val = atoll (optarg);
312 else
313 fnotice (stderr, "scaling cannot co-exist with normalization,"
314 " skipping\n");
315 break;
316 case 's':
317 ret = 0;
318 do_scaling = 1;
319 if (strstr (optarg, "/"))
320 {
321 ret = sscanf (optarg, "%d/%d", &numerator, &denominator);
322 if (ret == 2)
323 {
324 if (numerator < 0 || denominator <= 0)
325 {
326 fnotice (stderr, "incorrect format in scaling, using 1/1\n");
327 denominator = 1;
328 numerator = 1;
329 }
330 }
331 }
332 if (ret != 2)
333 {
334 ret = sscanf (optarg, "%f", &scale);
335 if (ret != 1)
336 fnotice (stderr, "incorrect format in scaling, using 1/1\n");
337 else
338 denominator = 0;
339 }
340
341 if (scale < 0.0)
342 fatal_error ("scale needs to be non-negative\n");
343
344 if (normalize_val != 0)
345 {
346 fnotice (stderr, "normalization cannot co-exist with scaling\n");
347 normalize_val = 0;
348 }
349 break;
350 default:
351 rewrite_usage ();
352 }
353 }
354
355 if (output_dir == NULL)
356 output_dir = "rewrite_profile";
357
358 if (argc - optind == 1)
359 {
360 if (denominator > 0)
361 ret = profile_rewrite (argv[optind], output_dir, 0, 0.0, numerator, denominator);
362 else
363 ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale, 0, 0);
364 }
365 else
366 rewrite_usage ();
367
368 return ret;
369}
370
371/* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
372 otherwise the output of --help. */
373
374static void
375print_usage (int error_p)
376{
377 FILE *file = error_p ? stderr : stdout;
378 int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
379
380 fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname);
381 fnotice (file, "Offline tool to handle gcda counts\n\n");
382 fnotice (file, " -h, --help Print this help, then exit\n");
383 fnotice (file, " -v, --version Print version number, then exit\n");
384 print_merge_usage_message (error_p);
385 print_rewrite_usage_message (error_p);
386 fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
387 bug_report_url);
388 exit (status);
389}
390
391/* Print version information and exit. */
392
393static void
394print_version (void)
395{
396 fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
397 fnotice (stdout, "Copyright %s 2014 Free Software Foundation, Inc.\n",
398 _("(C)"));
399 fnotice (stdout,
400 _("This is free software; see the source for copying conditions.\n"
401 "There is NO warranty; not even for MERCHANTABILITY or \n"
402 "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
403 exit (SUCCESS_EXIT_CODE);
404}
405
406static const struct option options[] =
407{
408 { "help", no_argument, NULL, 'h' },
409 { "version", no_argument, NULL, 'v' },
410 { 0, 0, 0, 0 }
411};
412
413/* Process args, return index to first non-arg. */
414
415static int
416process_args (int argc, char **argv)
417{
418 int opt;
419
420 while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1)
421 {
422 switch (opt)
423 {
424 case 'h':
425 print_usage (false);
426 /* Print_usage will exit. */
427 case 'v':
428 print_version ();
429 /* Print_version will exit. */
430 default:
431 print_usage (true);
432 /* Print_usage will exit. */
433 }
434 }
435
436 return optind;
437}
438
439/* Main function for gcov-tool. */
440
441int
442main (int argc, char **argv)
443{
444 const char *p;
445 const char *sub_command;
446
447 p = argv[0] + strlen (argv[0]);
448 while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
449 --p;
450 progname = p;
451
452 xmalloc_set_program_name (progname);
453
454 /* Unlock the stdio streams. */
455 unlock_std_streams ();
456
457 gcc_init_libintl ();
458
459 diagnostic_initialize (global_dc, 0);
460
461 /* Handle response files. */
462 expandargv (&argc, &argv);
463
464 process_args (argc, argv);
465 if (optind >= argc)
466 print_usage (true);
467
468 sub_command = argv[optind];
469
470 if (!strcmp (sub_command, "merge"))
471 return do_merge (argc - optind, argv + optind);
472 else if (!strcmp (sub_command, "rewrite"))
473 return do_rewrite (argc - optind, argv + optind);
474
475 print_usage (true);
476}