1 /* Gcc offline profile processing tool support. */
2 /* Copyright (C) 2014-2024 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
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
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/>. */
28 #include "coretypes.h"
31 #include "diagnostic.h"
43 extern struct gcov_info
*gcov_profile_merge (struct gcov_info
*,
44 struct gcov_info
*, int, int);
45 extern struct gcov_info
*gcov_profile_merge_stream (const char *, int, int);
46 extern int gcov_profile_overlap (struct gcov_info
*, struct gcov_info
*);
47 extern int gcov_profile_normalize (struct gcov_info
*, gcov_type
);
48 extern int gcov_profile_scale (struct gcov_info
*, float, int, int);
49 extern struct gcov_info
* gcov_read_profile_dir (const char*, int);
50 extern void gcov_do_dump (struct gcov_info
*, int, int);
51 extern const char *gcov_get_filename (struct gcov_info
*list
);
52 extern void gcov_set_verbose (void);
54 /* Set to verbose output mode. */
59 /* Remove file NAME if it has a gcda suffix. */
62 unlink_gcda_file (const char *name
,
63 const struct stat
*status ATTRIBUTE_UNUSED
,
64 int type ATTRIBUTE_UNUSED
,
65 struct FTW
*ftwbuf ATTRIBUTE_UNUSED
)
68 int len
= strlen (name
);
69 int len1
= strlen (GCOV_DATA_SUFFIX
);
71 if (len
> len1
&& !strncmp (len
-len1
+ name
, GCOV_DATA_SUFFIX
, len1
))
75 fatal_error (input_location
, "error in removing %s", name
);
81 /* Remove the gcda files in PATH recursively. */
84 unlink_profile_dir (const char *path ATTRIBUTE_UNUSED
)
87 return nftw(path
, unlink_gcda_file
, 64, FTW_DEPTH
| FTW_PHYS
);
93 /* Output GCOV_INFO lists PROFILE to directory OUT. Note that
94 we will remove all the gcda files in OUT. */
97 gcov_output_files (const char *out
, struct gcov_info
*profile
)
102 /* Try to make directory if it doesn't already exist. */
103 if (access (out
, F_OK
) == -1)
105 if (mkdir (out
, S_IRWXU
| S_IRWXG
| S_IRWXO
) == -1 && errno
!= EEXIST
)
106 fatal_error (input_location
, "Cannot make directory %s", out
);
108 unlink_profile_dir (out
);
110 /* Output new profile. */
111 pwd
= getcwd (NULL
, 0);
114 fatal_error (input_location
, "Cannot get current directory name");
118 fatal_error (input_location
, "Cannot change directory to %s", out
);
120 /* Verify that output file does not exist (either was removed by
121 unlink_profile_data or removed by user). */
122 const char *filename
= gcov_get_filename (profile
);
124 if (access (filename
, F_OK
) != -1)
125 fatal_error (input_location
, "output file %s already exists in folder %s",
128 gcov_do_dump (profile
, 0, 0);
132 fatal_error (input_location
, "Cannot change directory to %s", pwd
);
137 /* Merging profile D1 and D2 with weight as W1 and W2, respectively.
138 The result profile is written to directory OUT.
139 Return 0 on success. */
142 profile_merge (const char *d1
, const char *d2
, const char *out
, int w1
, int w2
)
144 struct gcov_info
*d1_profile
;
145 struct gcov_info
*d2_profile
;
146 struct gcov_info
*merged_profile
;
148 d1_profile
= gcov_read_profile_dir (d1
, 0);
149 d2_profile
= gcov_read_profile_dir (d2
, 0);
151 /* The actual merge: we overwrite to d1_profile. */
152 merged_profile
= gcov_profile_merge (d1_profile
, d2_profile
, w1
, w2
);
155 gcov_output_files (out
, merged_profile
);
157 fnotice (stdout
, "no profile files were merged\n");
162 /* Usage message for profile merge. */
165 print_merge_usage_message (int error_p
)
167 FILE *file
= error_p
? stderr
: stdout
;
169 fnotice (file
, " merge [options] <dir1> <dir2> Merge coverage file contents\n");
170 fnotice (file
, " -o, --output <dir> Output directory\n");
171 fnotice (file
, " -v, --verbose Verbose mode\n");
172 fnotice (file
, " -w, --weight <w1,w2> Set weights (float point values)\n");
175 static const struct option merge_options
[] =
177 { "verbose", no_argument
, NULL
, 'v' },
178 { "output", required_argument
, NULL
, 'o' },
179 { "weight", required_argument
, NULL
, 'w' },
183 /* Print merge usage and exit. */
185 static void ATTRIBUTE_NORETURN
188 fnotice (stderr
, "Merge subcommand usage:");
189 print_merge_usage_message (true);
190 exit (FATAL_EXIT_CODE
);
193 /* Driver for profile merge subcommand. */
196 do_merge (int argc
, char **argv
)
199 const char *output_dir
= 0;
203 while ((opt
= getopt_long (argc
, argv
, "vo:w:", merge_options
, NULL
)) != -1)
215 sscanf (optarg
, "%d,%d", &w1
, &w2
);
216 if (w1
< 0 || w2
< 0)
217 fatal_error (input_location
, "weights need to be non-negative");
224 if (output_dir
== NULL
)
225 output_dir
= "merged_profile";
227 if (argc
- optind
!= 2)
230 return profile_merge (argv
[optind
], argv
[optind
+1], output_dir
, w1
, w2
);
233 /* Usage message for profile merge-stream. */
236 print_merge_stream_usage_message (int error_p
)
238 FILE *file
= error_p
? stderr
: stdout
;
240 fnotice (file
, " merge-stream [options] [<file>] Merge coverage stream file (or stdin)\n"
241 " and coverage file contents\n");
242 fnotice (file
, " -v, --verbose Verbose mode\n");
243 fnotice (file
, " -w, --weight <w1,w2> Set weights (float point values)\n");
246 static const struct option merge_stream_options
[] =
248 { "verbose", no_argument
, NULL
, 'v' },
249 { "weight", required_argument
, NULL
, 'w' },
253 /* Print merge-stream usage and exit. */
255 static void ATTRIBUTE_NORETURN
256 merge_stream_usage (void)
258 fnotice (stderr
, "Merge-stream subcommand usage:");
259 print_merge_stream_usage_message (true);
260 exit (FATAL_EXIT_CODE
);
263 /* Driver for profile merge-stream subcommand. */
266 do_merge_stream (int argc
, char **argv
)
270 struct gcov_info
*merged_profile
;
273 while ((opt
= getopt_long (argc
, argv
, "vw:",
274 merge_stream_options
, NULL
)) != -1)
283 sscanf (optarg
, "%d,%d", &w1
, &w2
);
284 if (w1
< 0 || w2
< 0)
285 fatal_error (input_location
, "weights need to be non-negative");
288 merge_stream_usage ();
292 if (argc
- optind
> 1)
293 merge_stream_usage ();
295 merged_profile
= gcov_profile_merge_stream (argv
[optind
], w1
, w2
);
298 gcov_do_dump (merged_profile
, 0, -1);
300 fnotice (stdout
, "no profile files were merged\n");
305 /* If N_VAL is no-zero, normalize the profile by setting the largest counter
306 counter value to N_VAL and scale others counters proportionally.
307 Otherwise, multiply the all counters by SCALE. */
310 profile_rewrite (const char *d1
, const char *out
, int64_t n_val
,
311 float scale
, int n
, int d
)
313 struct gcov_info
* d1_profile
;
315 d1_profile
= gcov_read_profile_dir (d1
, 0);
320 gcov_profile_normalize (d1_profile
, (gcov_type
) n_val
);
322 gcov_profile_scale (d1_profile
, scale
, n
, d
);
324 gcov_output_files (out
, d1_profile
);
328 /* Usage function for profile rewrite. */
331 print_rewrite_usage_message (int error_p
)
333 FILE *file
= error_p
? stderr
: stdout
;
335 fnotice (file
, " rewrite [options] <dir> Rewrite coverage file contents\n");
336 fnotice (file
, " -n, --normalize <int64_t> Normalize the profile\n");
337 fnotice (file
, " -o, --output <dir> Output directory\n");
338 fnotice (file
, " -s, --scale <float or simple-frac> Scale the profile counters\n");
339 fnotice (file
, " -v, --verbose Verbose mode\n");
342 static const struct option rewrite_options
[] =
344 { "verbose", no_argument
, NULL
, 'v' },
345 { "output", required_argument
, NULL
, 'o' },
346 { "scale", required_argument
, NULL
, 's' },
347 { "normalize", required_argument
, NULL
, 'n' },
351 /* Print profile rewrite usage and exit. */
353 static void ATTRIBUTE_NORETURN
356 fnotice (stderr
, "Rewrite subcommand usage:");
357 print_rewrite_usage_message (true);
358 exit (FATAL_EXIT_CODE
);
361 /* Driver for profile rewrite subcommand. */
364 do_rewrite (int argc
, char **argv
)
368 const char *output_dir
= 0;
369 int64_t normalize_val
= 0;
376 while ((opt
= getopt_long (argc
, argv
, "vo:s:n:", rewrite_options
, NULL
)) != -1)
389 #if defined(INT64_T_IS_LONG)
390 normalize_val
= strtol (optarg
, (char **)NULL
, 10);
392 normalize_val
= strtoll (optarg
, (char **)NULL
, 10);
395 fnotice (stderr
, "scaling cannot co-exist with normalization,"
401 if (strstr (optarg
, "/"))
403 ret
= sscanf (optarg
, "%d/%d", &numerator
, &denominator
);
406 if (numerator
< 0 || denominator
<= 0)
408 fnotice (stderr
, "incorrect format in scaling, using 1/1\n");
416 ret
= sscanf (optarg
, "%f", &scale
);
418 fnotice (stderr
, "incorrect format in scaling, using 1/1\n");
424 fatal_error (input_location
, "scale needs to be non-negative");
426 if (normalize_val
!= 0)
428 fnotice (stderr
, "normalization cannot co-exist with scaling\n");
437 if (output_dir
== NULL
)
438 output_dir
= "rewrite_profile";
440 if (argc
- optind
== 1)
443 ret
= profile_rewrite (argv
[optind
], output_dir
, 0, 0.0, numerator
, denominator
);
445 ret
= profile_rewrite (argv
[optind
], output_dir
, normalize_val
, scale
, 0, 0);
453 /* Driver function to computer the overlap score b/w profile D1 and D2.
454 Return 1 on error and 0 if OK. */
457 profile_overlap (const char *d1
, const char *d2
)
459 struct gcov_info
*d1_profile
;
460 struct gcov_info
*d2_profile
;
462 d1_profile
= gcov_read_profile_dir (d1
, 0);
468 d2_profile
= gcov_read_profile_dir (d2
, 0);
472 return gcov_profile_overlap (d1_profile
, d2_profile
);
478 /* Usage message for profile overlap. */
481 print_overlap_usage_message (int error_p
)
483 FILE *file
= error_p
? stderr
: stdout
;
485 fnotice (file
, " overlap [options] <dir1> <dir2> Compute the overlap of two profiles\n");
486 fnotice (file
, " -f, --function Print function level info\n");
487 fnotice (file
, " -F, --fullname Print full filename\n");
488 fnotice (file
, " -h, --hotonly Only print info for hot objects/functions\n");
489 fnotice (file
, " -o, --object Print object level info\n");
490 fnotice (file
, " -t <float>, --hot_threshold <float> Set the threshold for hotness\n");
491 fnotice (file
, " -v, --verbose Verbose mode\n");
494 static const struct option overlap_options
[] =
496 { "verbose", no_argument
, NULL
, 'v' },
497 { "function", no_argument
, NULL
, 'f' },
498 { "fullname", no_argument
, NULL
, 'F' },
499 { "object", no_argument
, NULL
, 'o' },
500 { "hotonly", no_argument
, NULL
, 'h' },
501 { "hot_threshold", required_argument
, NULL
, 't' },
505 /* Print overlap usage and exit. */
507 static void ATTRIBUTE_NORETURN
510 fnotice (stderr
, "Overlap subcommand usage:");
511 print_overlap_usage_message (true);
512 exit (FATAL_EXIT_CODE
);
515 int overlap_func_level
;
516 int overlap_obj_level
;
517 int overlap_hot_only
;
518 int overlap_use_fullname
;
519 double overlap_hot_threshold
= 0.005;
521 /* Driver for profile overlap subcommand. */
524 do_overlap (int argc
, char **argv
)
530 while ((opt
= getopt_long (argc
, argv
, "vfFoht:", overlap_options
, NULL
)) != -1)
539 overlap_func_level
= 1;
542 overlap_use_fullname
= 1;
545 overlap_obj_level
= 1;
548 overlap_hot_only
= 1;
551 overlap_hot_threshold
= atof (optarg
);
558 if (argc
- optind
== 2)
559 ret
= profile_overlap (argv
[optind
], argv
[optind
+1]);
567 /* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
568 otherwise the output of --help. */
571 print_usage (int error_p
)
573 FILE *file
= error_p
? stderr
: stdout
;
574 int status
= error_p
? FATAL_EXIT_CODE
: SUCCESS_EXIT_CODE
;
576 fnotice (file
, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname
);
577 fnotice (file
, "Offline tool to handle gcda counts\n\n");
578 fnotice (file
, " -h, --help Print this help, then exit\n");
579 fnotice (file
, " -v, --version Print version number, then exit\n");
580 print_merge_usage_message (error_p
);
581 print_merge_stream_usage_message (error_p
);
582 print_rewrite_usage_message (error_p
);
583 print_overlap_usage_message (error_p
);
584 fnotice (file
, "\nFor bug reporting instructions, please see:\n%s.\n",
589 /* Print version information and exit. */
594 fnotice (stdout
, "%s %s%s\n", progname
, pkgversion_string
, version_string
);
595 fnotice (stdout
, "Copyright %s 2024 Free Software Foundation, Inc.\n",
598 _("This is free software; see the source for copying conditions. There is NO\n\
599 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
600 exit (SUCCESS_EXIT_CODE
);
603 static const struct option options
[] =
605 { "help", no_argument
, NULL
, 'h' },
606 { "version", no_argument
, NULL
, 'v' },
610 /* Process args, return index to first non-arg. */
613 process_args (int argc
, char **argv
)
617 while ((opt
= getopt_long (argc
, argv
, "+hv", options
, NULL
)) != -1)
623 /* Print_usage will exit. */
627 /* Print_version will exit. */
631 /* Print_usage will exit. */
638 /* Main function for gcov-tool. */
641 main (int argc
, char **argv
)
644 const char *sub_command
;
646 p
= argv
[0] + strlen (argv
[0]);
647 while (p
!= argv
[0] && !IS_DIR_SEPARATOR (p
[-1]))
651 xmalloc_set_program_name (progname
);
653 /* Unlock the stdio streams. */
654 unlock_std_streams ();
658 diagnostic_initialize (global_dc
, 0);
660 /* Handle response files. */
661 expandargv (&argc
, &argv
);
663 process_args (argc
, argv
);
667 sub_command
= argv
[optind
];
669 if (!strcmp (sub_command
, "merge"))
670 return do_merge (argc
- optind
, argv
+ optind
);
671 else if (!strcmp (sub_command
, "merge-stream"))
672 return do_merge_stream (argc
- optind
, argv
+ optind
);
673 else if (!strcmp (sub_command
, "rewrite"))
674 return do_rewrite (argc
- optind
, argv
+ optind
);
675 else if (!strcmp (sub_command
, "overlap"))
676 return do_overlap (argc
- optind
, argv
+ optind
);