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