1 /* Gcc offline profile processing tool support. */
2 /* Copyright (C) 2014-2016 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)
102 if (mkdir (out
, S_IRWXU
| S_IRWXG
| S_IRWXO
) == -1 && errno
!= EEXIST
)
103 fatal_error (input_location
, "Cannot make directory %s", out
);
105 unlink_profile_dir (out
);
107 /* Output new profile. */
108 pwd
= getcwd (NULL
, 0);
111 fatal_error (input_location
, "Cannot get current directory name");
115 fatal_error (input_location
, "Cannot change directory to %s", out
);
117 gcov_do_dump (profile
, 0);
121 fatal_error (input_location
, "Cannot change directory to %s", pwd
);
126 /* Merging profile D1 and D2 with weight as W1 and W2, respectively.
127 The result profile is written to directory OUT.
128 Return 0 on success. */
131 profile_merge (const char *d1
, const char *d2
, const char *out
, int w1
, int w2
)
133 struct gcov_info
*d1_profile
;
134 struct gcov_info
*d2_profile
;
137 d1_profile
= gcov_read_profile_dir (d1
, 0);
143 d2_profile
= gcov_read_profile_dir (d2
, 0);
147 /* The actual merge: we overwrite to d1_profile. */
148 ret
= gcov_profile_merge (d1_profile
, d2_profile
, w1
, w2
);
154 gcov_output_files (out
, d1_profile
);
159 /* Usage message for profile merge. */
162 print_merge_usage_message (int error_p
)
164 FILE *file
= error_p
? stderr
: stdout
;
166 fnotice (file
, " merge [options] <dir1> <dir2> Merge coverage file contents\n");
167 fnotice (file
, " -v, --verbose Verbose mode\n");
168 fnotice (file
, " -o, --output <dir> Output directory\n");
169 fnotice (file
, " -w, --weight <w1,w2> Set weights (float point values)\n");
172 static const struct option merge_options
[] =
174 { "verbose", no_argument
, NULL
, 'v' },
175 { "output", required_argument
, NULL
, 'o' },
176 { "weight", required_argument
, NULL
, 'w' },
180 /* Print merge usage and exit. */
185 fnotice (stderr
, "Merge subcomand usage:");
186 print_merge_usage_message (true);
187 exit (FATAL_EXIT_CODE
);
190 /* Driver for profile merge sub-command. */
193 do_merge (int argc
, char **argv
)
196 const char *output_dir
= 0;
200 while ((opt
= getopt_long (argc
, argv
, "vo:w:", merge_options
, NULL
)) != -1)
212 sscanf (optarg
, "%d,%d", &w1
, &w2
);
213 if (w1
< 0 || w2
< 0)
214 fatal_error (input_location
, "weights need to be non-negative\n");
221 if (output_dir
== NULL
)
222 output_dir
= "merged_profile";
224 if (argc
- optind
!= 2)
227 return profile_merge (argv
[optind
], argv
[optind
+1], output_dir
, w1
, w2
);
230 /* If N_VAL is no-zero, normalize the profile by setting the largest counter
231 counter value to N_VAL and scale others counters proportionally.
232 Otherwise, multiply the all counters by SCALE. */
235 profile_rewrite (const char *d1
, const char *out
, long long n_val
,
236 float scale
, int n
, int d
)
238 struct gcov_info
* d1_profile
;
240 d1_profile
= gcov_read_profile_dir (d1
, 0);
245 gcov_profile_normalize (d1_profile
, (gcov_type
) n_val
);
247 gcov_profile_scale (d1_profile
, scale
, n
, d
);
249 gcov_output_files (out
, d1_profile
);
253 /* Usage function for profile rewrite. */
256 print_rewrite_usage_message (int error_p
)
258 FILE *file
= error_p
? stderr
: stdout
;
260 fnotice (file
, " rewrite [options] <dir> Rewrite coverage file contents\n");
261 fnotice (file
, " -v, --verbose Verbose mode\n");
262 fnotice (file
, " -o, --output <dir> Output directory\n");
263 fnotice (file
, " -s, --scale <float or simple-frac> Scale the profile counters\n");
264 fnotice (file
, " -n, --normalize <long long> Normalize the profile\n");
267 static const struct option rewrite_options
[] =
269 { "verbose", no_argument
, NULL
, 'v' },
270 { "output", required_argument
, NULL
, 'o' },
271 { "scale", required_argument
, NULL
, 's' },
272 { "normalize", required_argument
, NULL
, 'n' },
276 /* Print profile rewrite usage and exit. */
281 fnotice (stderr
, "Rewrite subcommand usage:");
282 print_rewrite_usage_message (true);
283 exit (FATAL_EXIT_CODE
);
286 /* Driver for profile rewrite sub-command. */
289 do_rewrite (int argc
, char **argv
)
293 const char *output_dir
= 0;
294 #ifdef HAVE_LONG_LONG
295 long long normalize_val
= 0;
297 int64_t normalize_val
= 0;
305 while ((opt
= getopt_long (argc
, argv
, "vo:s:n:", rewrite_options
, NULL
)) != -1)
318 #if defined(HAVE_LONG_LONG)
319 normalize_val
= strtoll (optarg
, (char **)NULL
, 10);
320 #elif defined(INT64_T_IS_LONG)
321 normalize_val
= strtol (optarg
, (char **)NULL
, 10);
323 sscanf (optarg
, "%" SCNd64
, &normalize_val
);
326 fnotice (stderr
, "scaling cannot co-exist with normalization,"
332 if (strstr (optarg
, "/"))
334 ret
= sscanf (optarg
, "%d/%d", &numerator
, &denominator
);
337 if (numerator
< 0 || denominator
<= 0)
339 fnotice (stderr
, "incorrect format in scaling, using 1/1\n");
347 ret
= sscanf (optarg
, "%f", &scale
);
349 fnotice (stderr
, "incorrect format in scaling, using 1/1\n");
355 fatal_error (input_location
, "scale needs to be non-negative\n");
357 if (normalize_val
!= 0)
359 fnotice (stderr
, "normalization cannot co-exist with scaling\n");
368 if (output_dir
== NULL
)
369 output_dir
= "rewrite_profile";
371 if (argc
- optind
== 1)
374 ret
= profile_rewrite (argv
[optind
], output_dir
, 0, 0.0, numerator
, denominator
);
376 ret
= profile_rewrite (argv
[optind
], output_dir
, normalize_val
, scale
, 0, 0);
384 /* Driver function to computer the overlap score b/w profile D1 and D2.
385 Return 1 on error and 0 if OK. */
388 profile_overlap (const char *d1
, const char *d2
)
390 struct gcov_info
*d1_profile
;
391 struct gcov_info
*d2_profile
;
393 d1_profile
= gcov_read_profile_dir (d1
, 0);
399 d2_profile
= gcov_read_profile_dir (d2
, 0);
403 return gcov_profile_overlap (d1_profile
, d2_profile
);
409 /* Usage message for profile overlap. */
412 print_overlap_usage_message (int error_p
)
414 FILE *file
= error_p
? stderr
: stdout
;
416 fnotice (file
, " overlap [options] <dir1> <dir2> Compute the overlap of two profiles\n");
417 fnotice (file
, " -v, --verbose Verbose mode\n");
418 fnotice (file
, " -h, --hotonly Only print info for hot objects/functions\n");
419 fnotice (file
, " -f, --function Print function level info\n");
420 fnotice (file
, " -F, --fullname Print full filename\n");
421 fnotice (file
, " -o, --object Print object level info\n");
422 fnotice (file
, " -t <float>, --hot_threshold <float> Set the threshold for hotness\n");
426 static const struct option overlap_options
[] =
428 { "verbose", no_argument
, NULL
, 'v' },
429 { "function", no_argument
, NULL
, 'f' },
430 { "fullname", no_argument
, NULL
, 'F' },
431 { "object", no_argument
, NULL
, 'o' },
432 { "hotonly", no_argument
, NULL
, 'h' },
433 { "hot_threshold", required_argument
, NULL
, 't' },
437 /* Print overlap usage and exit. */
442 fnotice (stderr
, "Overlap subcomand usage:");
443 print_overlap_usage_message (true);
444 exit (FATAL_EXIT_CODE
);
447 int overlap_func_level
;
448 int overlap_obj_level
;
449 int overlap_hot_only
;
450 int overlap_use_fullname
;
451 double overlap_hot_threshold
= 0.005;
453 /* Driver for profile overlap sub-command. */
456 do_overlap (int argc
, char **argv
)
462 while ((opt
= getopt_long (argc
, argv
, "vfFoht:", overlap_options
, NULL
)) != -1)
471 overlap_func_level
= 1;
474 overlap_use_fullname
= 1;
477 overlap_obj_level
= 1;
480 overlap_hot_only
= 1;
483 overlap_hot_threshold
= atof (optarg
);
490 if (argc
- optind
== 2)
491 ret
= profile_overlap (argv
[optind
], argv
[optind
+1]);
499 /* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
500 otherwise the output of --help. */
503 print_usage (int error_p
)
505 FILE *file
= error_p
? stderr
: stdout
;
506 int status
= error_p
? FATAL_EXIT_CODE
: SUCCESS_EXIT_CODE
;
508 fnotice (file
, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname
);
509 fnotice (file
, "Offline tool to handle gcda counts\n\n");
510 fnotice (file
, " -h, --help Print this help, then exit\n");
511 fnotice (file
, " -v, --version Print version number, then exit\n");
512 print_merge_usage_message (error_p
);
513 print_rewrite_usage_message (error_p
);
514 print_overlap_usage_message (error_p
);
515 fnotice (file
, "\nFor bug reporting instructions, please see:\n%s.\n",
520 /* Print version information and exit. */
525 fnotice (stdout
, "%s %s%s\n", progname
, pkgversion_string
, version_string
);
526 fnotice (stdout
, "Copyright %s 2014-2016 Free Software Foundation, Inc.\n",
529 _("This is free software; see the source for copying conditions.\n"
530 "There is NO warranty; not even for MERCHANTABILITY or \n"
531 "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
532 exit (SUCCESS_EXIT_CODE
);
535 static const struct option options
[] =
537 { "help", no_argument
, NULL
, 'h' },
538 { "version", no_argument
, NULL
, 'v' },
542 /* Process args, return index to first non-arg. */
545 process_args (int argc
, char **argv
)
549 while ((opt
= getopt_long (argc
, argv
, "+hv", options
, NULL
)) != -1)
555 /* Print_usage will exit. */
558 /* Print_version will exit. */
561 /* Print_usage will exit. */
568 /* Main function for gcov-tool. */
571 main (int argc
, char **argv
)
574 const char *sub_command
;
576 p
= argv
[0] + strlen (argv
[0]);
577 while (p
!= argv
[0] && !IS_DIR_SEPARATOR (p
[-1]))
581 xmalloc_set_program_name (progname
);
583 /* Unlock the stdio streams. */
584 unlock_std_streams ();
588 diagnostic_initialize (global_dc
, 0);
590 /* Handle response files. */
591 expandargv (&argc
, &argv
);
593 process_args (argc
, argv
);
597 sub_command
= argv
[optind
];
599 if (!strcmp (sub_command
, "merge"))
600 return do_merge (argc
- optind
, argv
+ optind
);
601 else if (!strcmp (sub_command
, "rewrite"))
602 return do_rewrite (argc
- optind
, argv
+ optind
);
603 else if (!strcmp (sub_command
, "overlap"))
604 return do_overlap (argc
- optind
, argv
+ optind
);