New syntax for -fsanitize-recover.
[gcc.git] / gcc / gcov-tool.c
1 /* Gcc offline profile processing tool support. */
2 /* Copyright (C) 2014 Free Software Foundation, Inc.
3 Contributed by Rong Xu <xur@google.com>.
4
5 This file is part of GCC.
6
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.
11
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.
16
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.
20
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/>. */
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
41 extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int);
42 extern int gcov_profile_overlap (struct gcov_info*, struct gcov_info*);
43 extern int gcov_profile_normalize (struct gcov_info*, gcov_type);
44 extern int gcov_profile_scale (struct gcov_info*, float, int, int);
45 extern struct gcov_info* gcov_read_profile_dir (const char*, int);
46 extern void gcov_do_dump (struct gcov_info *, int);
47 extern void gcov_set_verbose (void);
48
49 /* Set to verbose output mode. */
50 static bool verbose;
51
52 /* Remove file NAME if it has a gcda suffix. */
53
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)
59 {
60 int ret = 0;
61 int len = strlen (name);
62 int len1 = strlen (GCOV_DATA_SUFFIX);
63
64 if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1))
65 ret = remove (name);
66
67 if (ret)
68 fatal_error ("error in removing %s\n", name);
69
70 return ret;
71 }
72
73 /* Remove the gcda files in PATH recursively. */
74
75 static int
76 unlink_profile_dir (const char *path)
77 {
78 return nftw(path, unlink_gcda_file, 64, FTW_DEPTH | FTW_PHYS);
79 }
80
81 /* Output GCOV_INFO lists PROFILE to directory OUT. Note that
82 we will remove all the gcda files in OUT. */
83
84 static void
85 gcov_output_files (const char *out, struct gcov_info *profile)
86 {
87 char *pwd;
88 int ret;
89
90 /* Try to make directory if it doesn't already exist. */
91 if (access (out, F_OK) == -1)
92 {
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);
101
102 /* Output new profile. */
103 pwd = getcwd (NULL, 0);
104
105 if (pwd == NULL)
106 fatal_error ("Cannot get current directory name");
107
108 ret = chdir (out);
109 if (ret)
110 fatal_error ("Cannot change directory to %s", out);
111
112 gcov_do_dump (profile, 0);
113
114 ret = chdir (pwd);
115 if (ret)
116 fatal_error ("Cannot change directory to %s", pwd);
117
118 free (pwd);
119 }
120
121 /* Merging profile D1 and D2 with weight as W1 and W2, respectively.
122 The result profile is written to directory OUT.
123 Return 0 on success. */
124
125 static int
126 profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2)
127 {
128 struct gcov_info *d1_profile;
129 struct gcov_info *d2_profile;
130 int ret;
131
132 d1_profile = gcov_read_profile_dir (d1, 0);
133 if (!d1_profile)
134 return 1;
135
136 if (d2)
137 {
138 d2_profile = gcov_read_profile_dir (d2, 0);
139 if (!d2_profile)
140 return 1;
141
142 /* The actual merge: we overwrite to d1_profile. */
143 ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2);
144
145 if (ret)
146 return ret;
147 }
148
149 gcov_output_files (out, d1_profile);
150
151 return 0;
152 }
153
154 /* Usage message for profile merge. */
155
156 static void
157 print_merge_usage_message (int error_p)
158 {
159 FILE *file = error_p ? stderr : stdout;
160
161 fnotice (file, " merge [options] <dir1> <dir2> Merge coverage file contents\n");
162 fnotice (file, " -v, --verbose Verbose mode\n");
163 fnotice (file, " -o, --output <dir> Output directory\n");
164 fnotice (file, " -w, --weight <w1,w2> Set weights (float point values)\n");
165 }
166
167 static const struct option merge_options[] =
168 {
169 { "verbose", no_argument, NULL, 'v' },
170 { "output", required_argument, NULL, 'o' },
171 { "weight", required_argument, NULL, 'w' },
172 { 0, 0, 0, 0 }
173 };
174
175 /* Print merge usage and exit. */
176
177 static void
178 merge_usage (void)
179 {
180 fnotice (stderr, "Merge subcomand usage:");
181 print_merge_usage_message (true);
182 exit (FATAL_EXIT_CODE);
183 }
184
185 /* Driver for profile merge sub-command. */
186
187 static int
188 do_merge (int argc, char **argv)
189 {
190 int opt;
191 int ret;
192 const char *output_dir = 0;
193 int w1 = 1, w2 = 1;
194
195 optind = 0;
196 while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1)
197 {
198 switch (opt)
199 {
200 case 'v':
201 verbose = true;
202 gcov_set_verbose ();
203 break;
204 case 'o':
205 output_dir = optarg;
206 break;
207 case 'w':
208 sscanf (optarg, "%d,%d", &w1, &w2);
209 if (w1 < 0 || w2 < 0)
210 fatal_error ("weights need to be non-negative\n");
211 break;
212 default:
213 merge_usage ();
214 }
215 }
216
217 if (output_dir == NULL)
218 output_dir = "merged_profile";
219
220 if (argc - optind == 2)
221 ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2);
222 else
223 merge_usage ();
224
225 return ret;
226 }
227
228 /* If N_VAL is no-zero, normalize the profile by setting the largest counter
229 counter value to N_VAL and scale others counters proportionally.
230 Otherwise, multiply the all counters by SCALE. */
231
232 static int
233 profile_rewrite (const char *d1, const char *out, long long n_val,
234 float scale, int n, int d)
235 {
236 struct gcov_info * d1_profile;
237
238 d1_profile = gcov_read_profile_dir (d1, 0);
239 if (!d1_profile)
240 return 1;
241
242 if (n_val)
243 gcov_profile_normalize (d1_profile, (gcov_type) n_val);
244 else
245 gcov_profile_scale (d1_profile, scale, n, d);
246
247 gcov_output_files (out, d1_profile);
248 return 0;
249 }
250
251 /* Usage function for profile rewrite. */
252
253 static void
254 print_rewrite_usage_message (int error_p)
255 {
256 FILE *file = error_p ? stderr : stdout;
257
258 fnotice (file, " rewrite [options] <dir> Rewrite coverage file contents\n");
259 fnotice (file, " -v, --verbose Verbose mode\n");
260 fnotice (file, " -o, --output <dir> Output directory\n");
261 fnotice (file, " -s, --scale <float or simple-frac> Scale the profile counters\n");
262 fnotice (file, " -n, --normalize <long long> Normalize the profile\n");
263 }
264
265 static const struct option rewrite_options[] =
266 {
267 { "verbose", no_argument, NULL, 'v' },
268 { "output", required_argument, NULL, 'o' },
269 { "scale", required_argument, NULL, 's' },
270 { "normalize", required_argument, NULL, 'n' },
271 { 0, 0, 0, 0 }
272 };
273
274 /* Print profile rewrite usage and exit. */
275
276 static void
277 rewrite_usage (void)
278 {
279 fnotice (stderr, "Rewrite subcommand usage:");
280 print_rewrite_usage_message (true);
281 exit (FATAL_EXIT_CODE);
282 }
283
284 /* Driver for profile rewrite sub-command. */
285
286 static int
287 do_rewrite (int argc, char **argv)
288 {
289 int opt;
290 int ret;
291 const char *output_dir = 0;
292 long long normalize_val = 0;
293 float scale = 0.0;
294 int numerator = 1;
295 int denominator = 1;
296 int do_scaling = 0;
297
298 optind = 0;
299 while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1)
300 {
301 switch (opt)
302 {
303 case 'v':
304 verbose = true;
305 gcov_set_verbose ();
306 break;
307 case 'o':
308 output_dir = optarg;
309 break;
310 case 'n':
311 if (!do_scaling)
312 normalize_val = atoll (optarg);
313 else
314 fnotice (stderr, "scaling cannot co-exist with normalization,"
315 " skipping\n");
316 break;
317 case 's':
318 ret = 0;
319 do_scaling = 1;
320 if (strstr (optarg, "/"))
321 {
322 ret = sscanf (optarg, "%d/%d", &numerator, &denominator);
323 if (ret == 2)
324 {
325 if (numerator < 0 || denominator <= 0)
326 {
327 fnotice (stderr, "incorrect format in scaling, using 1/1\n");
328 denominator = 1;
329 numerator = 1;
330 }
331 }
332 }
333 if (ret != 2)
334 {
335 ret = sscanf (optarg, "%f", &scale);
336 if (ret != 1)
337 fnotice (stderr, "incorrect format in scaling, using 1/1\n");
338 else
339 denominator = 0;
340 }
341
342 if (scale < 0.0)
343 fatal_error ("scale needs to be non-negative\n");
344
345 if (normalize_val != 0)
346 {
347 fnotice (stderr, "normalization cannot co-exist with scaling\n");
348 normalize_val = 0;
349 }
350 break;
351 default:
352 rewrite_usage ();
353 }
354 }
355
356 if (output_dir == NULL)
357 output_dir = "rewrite_profile";
358
359 if (argc - optind == 1)
360 {
361 if (denominator > 0)
362 ret = profile_rewrite (argv[optind], output_dir, 0, 0.0, numerator, denominator);
363 else
364 ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale, 0, 0);
365 }
366 else
367 rewrite_usage ();
368
369 return ret;
370 }
371
372 /* Driver function to computer the overlap score b/w profile D1 and D2.
373 Return 1 on error and 0 if OK. */
374
375 static int
376 profile_overlap (const char *d1, const char *d2)
377 {
378 struct gcov_info *d1_profile;
379 struct gcov_info *d2_profile;
380
381 d1_profile = gcov_read_profile_dir (d1, 0);
382 if (!d1_profile)
383 return 1;
384
385 if (d2)
386 {
387 d2_profile = gcov_read_profile_dir (d2, 0);
388 if (!d2_profile)
389 return 1;
390
391 return gcov_profile_overlap (d1_profile, d2_profile);
392 }
393
394 return 1;
395 }
396
397 /* Usage message for profile overlap. */
398
399 static void
400 print_overlap_usage_message (int error_p)
401 {
402 FILE *file = error_p ? stderr : stdout;
403
404 fnotice (file, " overlap [options] <dir1> <dir2> Compute the overlap of two profiles\n");
405 fnotice (file, " -v, --verbose Verbose mode\n");
406 fnotice (file, " -h, --hotonly Only print info for hot objects/functions\n");
407 fnotice (file, " -f, --function Print function level info\n");
408 fnotice (file, " -F, --fullname Print full filename\n");
409 fnotice (file, " -o, --object Print object level info\n");
410 fnotice (file, " -t <float>, --hot_threshold <float> Set the threshold for hotness\n");
411
412 }
413
414 static const struct option overlap_options[] =
415 {
416 { "verbose", no_argument, NULL, 'v' },
417 { "function", no_argument, NULL, 'f' },
418 { "fullname", no_argument, NULL, 'F' },
419 { "object", no_argument, NULL, 'o' },
420 { "hotonly", no_argument, NULL, 'h' },
421 { "hot_threshold", required_argument, NULL, 't' },
422 { 0, 0, 0, 0 }
423 };
424
425 /* Print overlap usage and exit. */
426
427 static void
428 overlap_usage (void)
429 {
430 fnotice (stderr, "Overlap subcomand usage:");
431 print_overlap_usage_message (true);
432 exit (FATAL_EXIT_CODE);
433 }
434
435 int overlap_func_level;
436 int overlap_obj_level;
437 int overlap_hot_only;
438 int overlap_use_fullname;
439 double overlap_hot_threshold = 0.005;
440
441 /* Driver for profile overlap sub-command. */
442
443 static int
444 do_overlap (int argc, char **argv)
445 {
446 int opt;
447 int ret;
448
449 optind = 0;
450 while ((opt = getopt_long (argc, argv, "vfFoht:", overlap_options, NULL)) != -1)
451 {
452 switch (opt)
453 {
454 case 'v':
455 verbose = true;
456 gcov_set_verbose ();
457 break;
458 case 'f':
459 overlap_func_level = 1;
460 break;
461 case 'F':
462 overlap_use_fullname = 1;
463 break;
464 case 'o':
465 overlap_obj_level = 1;
466 break;
467 case 'h':
468 overlap_hot_only = 1;
469 break;
470 case 't':
471 overlap_hot_threshold = atof (optarg);
472 break;
473 default:
474 overlap_usage ();
475 }
476 }
477
478 if (argc - optind == 2)
479 ret = profile_overlap (argv[optind], argv[optind+1]);
480 else
481 overlap_usage ();
482
483 return ret;
484 }
485
486
487 /* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
488 otherwise the output of --help. */
489
490 static void
491 print_usage (int error_p)
492 {
493 FILE *file = error_p ? stderr : stdout;
494 int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
495
496 fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname);
497 fnotice (file, "Offline tool to handle gcda counts\n\n");
498 fnotice (file, " -h, --help Print this help, then exit\n");
499 fnotice (file, " -v, --version Print version number, then exit\n");
500 print_merge_usage_message (error_p);
501 print_rewrite_usage_message (error_p);
502 print_overlap_usage_message (error_p);
503 fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
504 bug_report_url);
505 exit (status);
506 }
507
508 /* Print version information and exit. */
509
510 static void
511 print_version (void)
512 {
513 fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
514 fnotice (stdout, "Copyright %s 2014 Free Software Foundation, Inc.\n",
515 _("(C)"));
516 fnotice (stdout,
517 _("This is free software; see the source for copying conditions.\n"
518 "There is NO warranty; not even for MERCHANTABILITY or \n"
519 "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
520 exit (SUCCESS_EXIT_CODE);
521 }
522
523 static const struct option options[] =
524 {
525 { "help", no_argument, NULL, 'h' },
526 { "version", no_argument, NULL, 'v' },
527 { 0, 0, 0, 0 }
528 };
529
530 /* Process args, return index to first non-arg. */
531
532 static int
533 process_args (int argc, char **argv)
534 {
535 int opt;
536
537 while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1)
538 {
539 switch (opt)
540 {
541 case 'h':
542 print_usage (false);
543 /* Print_usage will exit. */
544 case 'v':
545 print_version ();
546 /* Print_version will exit. */
547 default:
548 print_usage (true);
549 /* Print_usage will exit. */
550 }
551 }
552
553 return optind;
554 }
555
556 /* Main function for gcov-tool. */
557
558 int
559 main (int argc, char **argv)
560 {
561 const char *p;
562 const char *sub_command;
563
564 p = argv[0] + strlen (argv[0]);
565 while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
566 --p;
567 progname = p;
568
569 xmalloc_set_program_name (progname);
570
571 /* Unlock the stdio streams. */
572 unlock_std_streams ();
573
574 gcc_init_libintl ();
575
576 diagnostic_initialize (global_dc, 0);
577
578 /* Handle response files. */
579 expandargv (&argc, &argv);
580
581 process_args (argc, argv);
582 if (optind >= argc)
583 print_usage (true);
584
585 sub_command = argv[optind];
586
587 if (!strcmp (sub_command, "merge"))
588 return do_merge (argc - optind, argv + optind);
589 else if (!strcmp (sub_command, "rewrite"))
590 return do_rewrite (argc - optind, argv + optind);
591 else if (!strcmp (sub_command, "overlap"))
592 return do_overlap (argc - optind, argv + optind);
593
594 print_usage (true);
595 }