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