1 /* Gcc offline profile processing tool support. */
2 /* Copyright (C) 2014-2022 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 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 const char *gcov_get_filename (struct gcov_info
*list
);
50 extern void gcov_set_verbose (void);
52 /* Set to verbose output mode. */
57 /* Remove file NAME if it has a gcda suffix. */
60 unlink_gcda_file (const char *name
,
61 const struct stat
*status ATTRIBUTE_UNUSED
,
62 int type ATTRIBUTE_UNUSED
,
63 struct FTW
*ftwbuf ATTRIBUTE_UNUSED
)
66 int len
= strlen (name
);
67 int len1
= strlen (GCOV_DATA_SUFFIX
);
69 if (len
> len1
&& !strncmp (len
-len1
+ name
, GCOV_DATA_SUFFIX
, len1
))
73 fatal_error (input_location
, "error in removing %s", name
);
79 /* Remove the gcda files in PATH recursively. */
82 unlink_profile_dir (const char *path ATTRIBUTE_UNUSED
)
85 return nftw(path
, unlink_gcda_file
, 64, FTW_DEPTH
| FTW_PHYS
);
91 /* Output GCOV_INFO lists PROFILE to directory OUT. Note that
92 we will remove all the gcda files in OUT. */
95 gcov_output_files (const char *out
, struct gcov_info
*profile
)
100 /* Try to make directory if it doesn't already exist. */
101 if (access (out
, F_OK
) == -1)
103 if (mkdir (out
, S_IRWXU
| S_IRWXG
| S_IRWXO
) == -1 && errno
!= EEXIST
)
104 fatal_error (input_location
, "Cannot make directory %s", out
);
106 unlink_profile_dir (out
);
108 /* Output new profile. */
109 pwd
= getcwd (NULL
, 0);
112 fatal_error (input_location
, "Cannot get current directory name");
116 fatal_error (input_location
, "Cannot change directory to %s", out
);
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
);
122 if (access (filename
, F_OK
) != -1)
123 fatal_error (input_location
, "output file %s already exists in folder %s",
126 gcov_do_dump (profile
, 0);
130 fatal_error (input_location
, "Cannot change directory to %s", pwd
);
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. */
140 profile_merge (const char *d1
, const char *d2
, const char *out
, int w1
, int w2
)
142 struct gcov_info
*d1_profile
;
143 struct gcov_info
*d2_profile
;
146 d1_profile
= gcov_read_profile_dir (d1
, 0);
152 d2_profile
= gcov_read_profile_dir (d2
, 0);
156 /* The actual merge: we overwrite to d1_profile. */
157 ret
= gcov_profile_merge (d1_profile
, d2_profile
, w1
, w2
);
163 gcov_output_files (out
, d1_profile
);
168 /* Usage message for profile merge. */
171 print_merge_usage_message (int error_p
)
173 FILE *file
= error_p
? stderr
: stdout
;
175 fnotice (file
, " merge [options] <dir1> <dir2> Merge coverage file contents\n");
176 fnotice (file
, " -o, --output <dir> Output directory\n");
177 fnotice (file
, " -v, --verbose Verbose mode\n");
178 fnotice (file
, " -w, --weight <w1,w2> Set weights (float point values)\n");
181 static const struct option merge_options
[] =
183 { "verbose", no_argument
, NULL
, 'v' },
184 { "output", required_argument
, NULL
, 'o' },
185 { "weight", required_argument
, NULL
, 'w' },
189 /* Print merge usage and exit. */
191 static void ATTRIBUTE_NORETURN
194 fnotice (stderr
, "Merge subcomand usage:");
195 print_merge_usage_message (true);
196 exit (FATAL_EXIT_CODE
);
199 /* Driver for profile merge sub-command. */
202 do_merge (int argc
, char **argv
)
205 const char *output_dir
= 0;
209 while ((opt
= getopt_long (argc
, argv
, "vo:w:", merge_options
, NULL
)) != -1)
221 sscanf (optarg
, "%d,%d", &w1
, &w2
);
222 if (w1
< 0 || w2
< 0)
223 fatal_error (input_location
, "weights need to be non-negative");
230 if (output_dir
== NULL
)
231 output_dir
= "merged_profile";
233 if (argc
- optind
!= 2)
236 return profile_merge (argv
[optind
], argv
[optind
+1], output_dir
, w1
, w2
);
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. */
244 profile_rewrite (const char *d1
, const char *out
, int64_t n_val
,
245 float scale
, int n
, int d
)
247 struct gcov_info
* d1_profile
;
249 d1_profile
= gcov_read_profile_dir (d1
, 0);
254 gcov_profile_normalize (d1_profile
, (gcov_type
) n_val
);
256 gcov_profile_scale (d1_profile
, scale
, n
, d
);
258 gcov_output_files (out
, d1_profile
);
262 /* Usage function for profile rewrite. */
265 print_rewrite_usage_message (int error_p
)
267 FILE *file
= error_p
? stderr
: stdout
;
269 fnotice (file
, " rewrite [options] <dir> Rewrite coverage file contents\n");
270 fnotice (file
, " -n, --normalize <int64_t> Normalize the profile\n");
271 fnotice (file
, " -o, --output <dir> Output directory\n");
272 fnotice (file
, " -s, --scale <float or simple-frac> Scale the profile counters\n");
273 fnotice (file
, " -v, --verbose Verbose mode\n");
276 static const struct option rewrite_options
[] =
278 { "verbose", no_argument
, NULL
, 'v' },
279 { "output", required_argument
, NULL
, 'o' },
280 { "scale", required_argument
, NULL
, 's' },
281 { "normalize", required_argument
, NULL
, 'n' },
285 /* Print profile rewrite usage and exit. */
287 static void ATTRIBUTE_NORETURN
290 fnotice (stderr
, "Rewrite subcommand usage:");
291 print_rewrite_usage_message (true);
292 exit (FATAL_EXIT_CODE
);
295 /* Driver for profile rewrite sub-command. */
298 do_rewrite (int argc
, char **argv
)
302 const char *output_dir
= 0;
303 int64_t normalize_val
= 0;
310 while ((opt
= getopt_long (argc
, argv
, "vo:s:n:", rewrite_options
, NULL
)) != -1)
323 #if defined(INT64_T_IS_LONG)
324 normalize_val
= strtol (optarg
, (char **)NULL
, 10);
326 normalize_val
= strtoll (optarg
, (char **)NULL
, 10);
329 fnotice (stderr
, "scaling cannot co-exist with normalization,"
335 if (strstr (optarg
, "/"))
337 ret
= sscanf (optarg
, "%d/%d", &numerator
, &denominator
);
340 if (numerator
< 0 || denominator
<= 0)
342 fnotice (stderr
, "incorrect format in scaling, using 1/1\n");
350 ret
= sscanf (optarg
, "%f", &scale
);
352 fnotice (stderr
, "incorrect format in scaling, using 1/1\n");
358 fatal_error (input_location
, "scale needs to be non-negative");
360 if (normalize_val
!= 0)
362 fnotice (stderr
, "normalization cannot co-exist with scaling\n");
371 if (output_dir
== NULL
)
372 output_dir
= "rewrite_profile";
374 if (argc
- optind
== 1)
377 ret
= profile_rewrite (argv
[optind
], output_dir
, 0, 0.0, numerator
, denominator
);
379 ret
= profile_rewrite (argv
[optind
], output_dir
, normalize_val
, scale
, 0, 0);
387 /* Driver function to computer the overlap score b/w profile D1 and D2.
388 Return 1 on error and 0 if OK. */
391 profile_overlap (const char *d1
, const char *d2
)
393 struct gcov_info
*d1_profile
;
394 struct gcov_info
*d2_profile
;
396 d1_profile
= gcov_read_profile_dir (d1
, 0);
402 d2_profile
= gcov_read_profile_dir (d2
, 0);
406 return gcov_profile_overlap (d1_profile
, d2_profile
);
412 /* Usage message for profile overlap. */
415 print_overlap_usage_message (int error_p
)
417 FILE *file
= error_p
? stderr
: stdout
;
419 fnotice (file
, " overlap [options] <dir1> <dir2> Compute the overlap of two profiles\n");
420 fnotice (file
, " -f, --function Print function level info\n");
421 fnotice (file
, " -F, --fullname Print full filename\n");
422 fnotice (file
, " -h, --hotonly Only print info for hot objects/functions\n");
423 fnotice (file
, " -o, --object Print object level info\n");
424 fnotice (file
, " -t <float>, --hot_threshold <float> Set the threshold for hotness\n");
425 fnotice (file
, " -v, --verbose Verbose mode\n");
428 static const struct option overlap_options
[] =
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' },
439 /* Print overlap usage and exit. */
441 static void ATTRIBUTE_NORETURN
444 fnotice (stderr
, "Overlap subcomand usage:");
445 print_overlap_usage_message (true);
446 exit (FATAL_EXIT_CODE
);
449 int overlap_func_level
;
450 int overlap_obj_level
;
451 int overlap_hot_only
;
452 int overlap_use_fullname
;
453 double overlap_hot_threshold
= 0.005;
455 /* Driver for profile overlap sub-command. */
458 do_overlap (int argc
, char **argv
)
464 while ((opt
= getopt_long (argc
, argv
, "vfFoht:", overlap_options
, NULL
)) != -1)
473 overlap_func_level
= 1;
476 overlap_use_fullname
= 1;
479 overlap_obj_level
= 1;
482 overlap_hot_only
= 1;
485 overlap_hot_threshold
= atof (optarg
);
492 if (argc
- optind
== 2)
493 ret
= profile_overlap (argv
[optind
], argv
[optind
+1]);
501 /* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
502 otherwise the output of --help. */
505 print_usage (int error_p
)
507 FILE *file
= error_p
? stderr
: stdout
;
508 int status
= error_p
? FATAL_EXIT_CODE
: SUCCESS_EXIT_CODE
;
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
);
516 print_overlap_usage_message (error_p
);
517 fnotice (file
, "\nFor bug reporting instructions, please see:\n%s.\n",
522 /* Print version information and exit. */
527 fnotice (stdout
, "%s %s%s\n", progname
, pkgversion_string
, version_string
);
528 fnotice (stdout
, "Copyright %s 2022 Free Software Foundation, Inc.\n",
531 _("This is free software; see the source for copying conditions. There is NO\n\
532 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
533 exit (SUCCESS_EXIT_CODE
);
536 static const struct option options
[] =
538 { "help", no_argument
, NULL
, 'h' },
539 { "version", no_argument
, NULL
, 'v' },
543 /* Process args, return index to first non-arg. */
546 process_args (int argc
, char **argv
)
550 while ((opt
= getopt_long (argc
, argv
, "+hv", options
, NULL
)) != -1)
556 /* Print_usage will exit. */
560 /* Print_version will exit. */
564 /* Print_usage will exit. */
571 /* Main function for gcov-tool. */
574 main (int argc
, char **argv
)
577 const char *sub_command
;
579 p
= argv
[0] + strlen (argv
[0]);
580 while (p
!= argv
[0] && !IS_DIR_SEPARATOR (p
[-1]))
584 xmalloc_set_program_name (progname
);
586 /* Unlock the stdio streams. */
587 unlock_std_streams ();
591 diagnostic_initialize (global_dc
, 0);
593 /* Handle response files. */
594 expandargv (&argc
, &argv
);
596 process_args (argc
, argv
);
600 sub_command
= argv
[optind
];
602 if (!strcmp (sub_command
, "merge"))
603 return do_merge (argc
- optind
, argv
+ optind
);
604 else if (!strcmp (sub_command
, "rewrite"))
605 return do_rewrite (argc
- optind
, argv
+ optind
);
606 else if (!strcmp (sub_command
, "overlap"))
607 return do_overlap (argc
- optind
, argv
+ optind
);