1 /* Gcc offline profile processing tool support. */
2 /* Copyright (C) 2014-2015 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 void gcov_set_verbose (void);
51 /* Set to verbose output mode. */
56 /* Remove file NAME if it has a gcda suffix. */
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
)
65 int len
= strlen (name
);
66 int len1
= strlen (GCOV_DATA_SUFFIX
);
68 if (len
> len1
&& !strncmp (len
-len1
+ name
, GCOV_DATA_SUFFIX
, len1
))
72 fatal_error (input_location
, "error in removing %s\n", name
);
78 /* Remove the gcda files in PATH recursively. */
81 unlink_profile_dir (const char *path ATTRIBUTE_UNUSED
)
84 return nftw(path
, unlink_gcda_file
, 64, FTW_DEPTH
| FTW_PHYS
);
90 /* Output GCOV_INFO lists PROFILE to directory OUT. Note that
91 we will remove all the gcda files in OUT. */
94 gcov_output_files (const char *out
, struct gcov_info
*profile
)
99 /* Try to make directory if it doesn't already exist. */
100 if (access (out
, F_OK
) == -1)
103 if (mkdir (out
, S_IRWXU
| S_IRWXG
| S_IRWXO
) == -1 && errno
!= EEXIST
)
105 if (mkdir (out
) == -1 && errno
!= EEXIST
)
107 fatal_error (input_location
, "Cannot make directory %s", out
);
109 unlink_profile_dir (out
);
111 /* Output new profile. */
112 pwd
= getcwd (NULL
, 0);
115 fatal_error (input_location
, "Cannot get current directory name");
119 fatal_error (input_location
, "Cannot change directory to %s", out
);
121 gcov_do_dump (profile
, 0);
125 fatal_error (input_location
, "Cannot change directory to %s", pwd
);
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. */
135 profile_merge (const char *d1
, const char *d2
, const char *out
, int w1
, int w2
)
137 struct gcov_info
*d1_profile
;
138 struct gcov_info
*d2_profile
;
141 d1_profile
= gcov_read_profile_dir (d1
, 0);
147 d2_profile
= gcov_read_profile_dir (d2
, 0);
151 /* The actual merge: we overwrite to d1_profile. */
152 ret
= gcov_profile_merge (d1_profile
, d2_profile
, w1
, w2
);
158 gcov_output_files (out
, d1_profile
);
163 /* Usage message for profile merge. */
166 print_merge_usage_message (int error_p
)
168 FILE *file
= error_p
? stderr
: stdout
;
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");
176 static const struct option merge_options
[] =
178 { "verbose", no_argument
, NULL
, 'v' },
179 { "output", required_argument
, NULL
, 'o' },
180 { "weight", required_argument
, NULL
, 'w' },
184 /* Print merge usage and exit. */
189 fnotice (stderr
, "Merge subcomand usage:");
190 print_merge_usage_message (true);
191 exit (FATAL_EXIT_CODE
);
194 /* Driver for profile merge sub-command. */
197 do_merge (int argc
, char **argv
)
201 const char *output_dir
= 0;
205 while ((opt
= getopt_long (argc
, argv
, "vo:w:", merge_options
, NULL
)) != -1)
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");
226 if (output_dir
== NULL
)
227 output_dir
= "merged_profile";
229 if (argc
- optind
== 2)
230 ret
= profile_merge (argv
[optind
], argv
[optind
+1], output_dir
, w1
, w2
);
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. */
242 profile_rewrite (const char *d1
, const char *out
, long long n_val
,
243 float scale
, int n
, int d
)
245 struct gcov_info
* d1_profile
;
247 d1_profile
= gcov_read_profile_dir (d1
, 0);
252 gcov_profile_normalize (d1_profile
, (gcov_type
) n_val
);
254 gcov_profile_scale (d1_profile
, scale
, n
, d
);
256 gcov_output_files (out
, d1_profile
);
260 /* Usage function for profile rewrite. */
263 print_rewrite_usage_message (int error_p
)
265 FILE *file
= error_p
? stderr
: stdout
;
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");
274 static const struct option rewrite_options
[] =
276 { "verbose", no_argument
, NULL
, 'v' },
277 { "output", required_argument
, NULL
, 'o' },
278 { "scale", required_argument
, NULL
, 's' },
279 { "normalize", required_argument
, NULL
, 'n' },
283 /* Print profile rewrite usage and exit. */
288 fnotice (stderr
, "Rewrite subcommand usage:");
289 print_rewrite_usage_message (true);
290 exit (FATAL_EXIT_CODE
);
293 /* Driver for profile rewrite sub-command. */
296 do_rewrite (int argc
, char **argv
)
300 const char *output_dir
= 0;
301 #ifdef HAVE_LONG_LONG
302 long long normalize_val
= 0;
304 int64_t normalize_val
= 0;
312 while ((opt
= getopt_long (argc
, argv
, "vo:s:n:", rewrite_options
, NULL
)) != -1)
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);
330 sscanf (optarg
, "%" SCNd64
, &normalize_val
);
333 fnotice (stderr
, "scaling cannot co-exist with normalization,"
339 if (strstr (optarg
, "/"))
341 ret
= sscanf (optarg
, "%d/%d", &numerator
, &denominator
);
344 if (numerator
< 0 || denominator
<= 0)
346 fnotice (stderr
, "incorrect format in scaling, using 1/1\n");
354 ret
= sscanf (optarg
, "%f", &scale
);
356 fnotice (stderr
, "incorrect format in scaling, using 1/1\n");
362 fatal_error (input_location
, "scale needs to be non-negative\n");
364 if (normalize_val
!= 0)
366 fnotice (stderr
, "normalization cannot co-exist with scaling\n");
375 if (output_dir
== NULL
)
376 output_dir
= "rewrite_profile";
378 if (argc
- optind
== 1)
381 ret
= profile_rewrite (argv
[optind
], output_dir
, 0, 0.0, numerator
, denominator
);
383 ret
= profile_rewrite (argv
[optind
], output_dir
, normalize_val
, scale
, 0, 0);
391 /* Driver function to computer the overlap score b/w profile D1 and D2.
392 Return 1 on error and 0 if OK. */
395 profile_overlap (const char *d1
, const char *d2
)
397 struct gcov_info
*d1_profile
;
398 struct gcov_info
*d2_profile
;
400 d1_profile
= gcov_read_profile_dir (d1
, 0);
406 d2_profile
= gcov_read_profile_dir (d2
, 0);
410 return gcov_profile_overlap (d1_profile
, d2_profile
);
416 /* Usage message for profile overlap. */
419 print_overlap_usage_message (int error_p
)
421 FILE *file
= error_p
? stderr
: stdout
;
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");
433 static const struct option overlap_options
[] =
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' },
444 /* Print overlap usage and exit. */
449 fnotice (stderr
, "Overlap subcomand usage:");
450 print_overlap_usage_message (true);
451 exit (FATAL_EXIT_CODE
);
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;
460 /* Driver for profile overlap sub-command. */
463 do_overlap (int argc
, char **argv
)
469 while ((opt
= getopt_long (argc
, argv
, "vfFoht:", overlap_options
, NULL
)) != -1)
478 overlap_func_level
= 1;
481 overlap_use_fullname
= 1;
484 overlap_obj_level
= 1;
487 overlap_hot_only
= 1;
490 overlap_hot_threshold
= atof (optarg
);
497 if (argc
- optind
== 2)
498 ret
= profile_overlap (argv
[optind
], argv
[optind
+1]);
506 /* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
507 otherwise the output of --help. */
510 print_usage (int error_p
)
512 FILE *file
= error_p
? stderr
: stdout
;
513 int status
= error_p
? FATAL_EXIT_CODE
: SUCCESS_EXIT_CODE
;
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",
527 /* Print version information and exit. */
532 fnotice (stdout
, "%s %s%s\n", progname
, pkgversion_string
, version_string
);
533 fnotice (stdout
, "Copyright %s 2014-2015 Free Software Foundation, Inc.\n",
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
);
542 static const struct option options
[] =
544 { "help", no_argument
, NULL
, 'h' },
545 { "version", no_argument
, NULL
, 'v' },
549 /* Process args, return index to first non-arg. */
552 process_args (int argc
, char **argv
)
556 while ((opt
= getopt_long (argc
, argv
, "+hv", options
, NULL
)) != -1)
562 /* Print_usage will exit. */
565 /* Print_version will exit. */
568 /* Print_usage will exit. */
575 /* Main function for gcov-tool. */
578 main (int argc
, char **argv
)
581 const char *sub_command
;
583 p
= argv
[0] + strlen (argv
[0]);
584 while (p
!= argv
[0] && !IS_DIR_SEPARATOR (p
[-1]))
588 xmalloc_set_program_name (progname
);
590 /* Unlock the stdio streams. */
591 unlock_std_streams ();
595 diagnostic_initialize (global_dc
, 0);
597 /* Handle response files. */
598 expandargv (&argc
, &argv
);
600 process_args (argc
, argv
);
604 sub_command
= argv
[optind
];
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
);