123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611 |
- /* Gcc offline profile processing tool support. */
- /* Copyright (C) 2014-2015 Free Software Foundation, Inc.
- Contributed by Rong Xu <xur@google.com>.
- This file is part of GCC.
- GCC is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 3, or (at your option) any later
- version.
- GCC is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for more details.
- Under Section 7 of GPL version 3, you are granted additional
- permissions described in the GCC Runtime Library Exception, version
- 3.1, as published by the Free Software Foundation.
- You should have received a copy of the GNU General Public License and
- a copy of the GCC Runtime Library Exception along with this program;
- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
- <http://www.gnu.org/licenses/>. */
- #include "config.h"
- #include "system.h"
- #include "coretypes.h"
- #include "tm.h"
- #include "intl.h"
- #include "diagnostic.h"
- #include "version.h"
- #include "gcov-io.h"
- #include <stdlib.h>
- #include <stdio.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #if HAVE_FTW_H
- #include <ftw.h>
- #endif
- #include <getopt.h>
- extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int);
- extern int gcov_profile_overlap (struct gcov_info*, struct gcov_info*);
- extern int gcov_profile_normalize (struct gcov_info*, gcov_type);
- extern int gcov_profile_scale (struct gcov_info*, float, int, int);
- extern struct gcov_info* gcov_read_profile_dir (const char*, int);
- extern void gcov_do_dump (struct gcov_info *, int);
- extern void gcov_set_verbose (void);
- /* Set to verbose output mode. */
- static bool verbose;
- #if HAVE_FTW_H
- /* Remove file NAME if it has a gcda suffix. */
- static int
- unlink_gcda_file (const char *name,
- const struct stat *status ATTRIBUTE_UNUSED,
- int type ATTRIBUTE_UNUSED,
- struct FTW *ftwbuf ATTRIBUTE_UNUSED)
- {
- int ret = 0;
- int len = strlen (name);
- int len1 = strlen (GCOV_DATA_SUFFIX);
- if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1))
- ret = remove (name);
- if (ret)
- fatal_error (input_location, "error in removing %s\n", name);
- return ret;
- }
- #endif
- /* Remove the gcda files in PATH recursively. */
- static int
- unlink_profile_dir (const char *path ATTRIBUTE_UNUSED)
- {
- #if HAVE_FTW_H
- return nftw(path, unlink_gcda_file, 64, FTW_DEPTH | FTW_PHYS);
- #else
- return -1;
- #endif
- }
- /* Output GCOV_INFO lists PROFILE to directory OUT. Note that
- we will remove all the gcda files in OUT. */
- static void
- gcov_output_files (const char *out, struct gcov_info *profile)
- {
- char *pwd;
- int ret;
- /* Try to make directory if it doesn't already exist. */
- if (access (out, F_OK) == -1)
- {
- if (mkdir (out, S_IRWXU | S_IRWXG | S_IRWXO) == -1 && errno != EEXIST)
- fatal_error (input_location, "Cannot make directory %s", out);
- } else
- unlink_profile_dir (out);
- /* Output new profile. */
- pwd = getcwd (NULL, 0);
- if (pwd == NULL)
- fatal_error (input_location, "Cannot get current directory name");
- ret = chdir (out);
- if (ret)
- fatal_error (input_location, "Cannot change directory to %s", out);
- gcov_do_dump (profile, 0);
- ret = chdir (pwd);
- if (ret)
- fatal_error (input_location, "Cannot change directory to %s", pwd);
- free (pwd);
- }
- /* Merging profile D1 and D2 with weight as W1 and W2, respectively.
- The result profile is written to directory OUT.
- Return 0 on success. */
- static int
- profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2)
- {
- struct gcov_info *d1_profile;
- struct gcov_info *d2_profile;
- int ret;
- d1_profile = gcov_read_profile_dir (d1, 0);
- if (!d1_profile)
- return 1;
- if (d2)
- {
- d2_profile = gcov_read_profile_dir (d2, 0);
- if (!d2_profile)
- return 1;
- /* The actual merge: we overwrite to d1_profile. */
- ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2);
- if (ret)
- return ret;
- }
- gcov_output_files (out, d1_profile);
- return 0;
- }
- /* Usage message for profile merge. */
- static void
- print_merge_usage_message (int error_p)
- {
- FILE *file = error_p ? stderr : stdout;
- fnotice (file, " merge [options] <dir1> <dir2> Merge coverage file contents\n");
- fnotice (file, " -v, --verbose Verbose mode\n");
- fnotice (file, " -o, --output <dir> Output directory\n");
- fnotice (file, " -w, --weight <w1,w2> Set weights (float point values)\n");
- }
- static const struct option merge_options[] =
- {
- { "verbose", no_argument, NULL, 'v' },
- { "output", required_argument, NULL, 'o' },
- { "weight", required_argument, NULL, 'w' },
- { 0, 0, 0, 0 }
- };
- /* Print merge usage and exit. */
- static void
- merge_usage (void)
- {
- fnotice (stderr, "Merge subcomand usage:");
- print_merge_usage_message (true);
- exit (FATAL_EXIT_CODE);
- }
- /* Driver for profile merge sub-command. */
- static int
- do_merge (int argc, char **argv)
- {
- int opt;
- int ret;
- const char *output_dir = 0;
- int w1 = 1, w2 = 1;
- optind = 0;
- while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1)
- {
- switch (opt)
- {
- case 'v':
- verbose = true;
- gcov_set_verbose ();
- break;
- case 'o':
- output_dir = optarg;
- break;
- case 'w':
- sscanf (optarg, "%d,%d", &w1, &w2);
- if (w1 < 0 || w2 < 0)
- fatal_error (input_location, "weights need to be non-negative\n");
- break;
- default:
- merge_usage ();
- }
- }
- if (output_dir == NULL)
- output_dir = "merged_profile";
- if (argc - optind == 2)
- ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2);
- else
- merge_usage ();
- return ret;
- }
- /* If N_VAL is no-zero, normalize the profile by setting the largest counter
- counter value to N_VAL and scale others counters proportionally.
- Otherwise, multiply the all counters by SCALE. */
- static int
- profile_rewrite (const char *d1, const char *out, long long n_val,
- float scale, int n, int d)
- {
- struct gcov_info * d1_profile;
- d1_profile = gcov_read_profile_dir (d1, 0);
- if (!d1_profile)
- return 1;
- if (n_val)
- gcov_profile_normalize (d1_profile, (gcov_type) n_val);
- else
- gcov_profile_scale (d1_profile, scale, n, d);
- gcov_output_files (out, d1_profile);
- return 0;
- }
- /* Usage function for profile rewrite. */
- static void
- print_rewrite_usage_message (int error_p)
- {
- FILE *file = error_p ? stderr : stdout;
- fnotice (file, " rewrite [options] <dir> Rewrite coverage file contents\n");
- fnotice (file, " -v, --verbose Verbose mode\n");
- fnotice (file, " -o, --output <dir> Output directory\n");
- fnotice (file, " -s, --scale <float or simple-frac> Scale the profile counters\n");
- fnotice (file, " -n, --normalize <long long> Normalize the profile\n");
- }
- static const struct option rewrite_options[] =
- {
- { "verbose", no_argument, NULL, 'v' },
- { "output", required_argument, NULL, 'o' },
- { "scale", required_argument, NULL, 's' },
- { "normalize", required_argument, NULL, 'n' },
- { 0, 0, 0, 0 }
- };
- /* Print profile rewrite usage and exit. */
- static void
- rewrite_usage (void)
- {
- fnotice (stderr, "Rewrite subcommand usage:");
- print_rewrite_usage_message (true);
- exit (FATAL_EXIT_CODE);
- }
- /* Driver for profile rewrite sub-command. */
- static int
- do_rewrite (int argc, char **argv)
- {
- int opt;
- int ret;
- const char *output_dir = 0;
- #ifdef HAVE_LONG_LONG
- long long normalize_val = 0;
- #else
- int64_t normalize_val = 0;
- #endif
- float scale = 0.0;
- int numerator = 1;
- int denominator = 1;
- int do_scaling = 0;
- optind = 0;
- while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1)
- {
- switch (opt)
- {
- case 'v':
- verbose = true;
- gcov_set_verbose ();
- break;
- case 'o':
- output_dir = optarg;
- break;
- case 'n':
- if (!do_scaling)
- #if defined(HAVE_LONG_LONG)
- normalize_val = strtoll (optarg, (char **)NULL, 10);
- #elif defined(INT64_T_IS_LONG)
- normalize_val = strtol (optarg, (char **)NULL, 10);
- #else
- sscanf (optarg, "%" SCNd64, &normalize_val);
- #endif
- else
- fnotice (stderr, "scaling cannot co-exist with normalization,"
- " skipping\n");
- break;
- case 's':
- ret = 0;
- do_scaling = 1;
- if (strstr (optarg, "/"))
- {
- ret = sscanf (optarg, "%d/%d", &numerator, &denominator);
- if (ret == 2)
- {
- if (numerator < 0 || denominator <= 0)
- {
- fnotice (stderr, "incorrect format in scaling, using 1/1\n");
- denominator = 1;
- numerator = 1;
- }
- }
- }
- if (ret != 2)
- {
- ret = sscanf (optarg, "%f", &scale);
- if (ret != 1)
- fnotice (stderr, "incorrect format in scaling, using 1/1\n");
- else
- denominator = 0;
- }
- if (scale < 0.0)
- fatal_error (input_location, "scale needs to be non-negative\n");
- if (normalize_val != 0)
- {
- fnotice (stderr, "normalization cannot co-exist with scaling\n");
- normalize_val = 0;
- }
- break;
- default:
- rewrite_usage ();
- }
- }
- if (output_dir == NULL)
- output_dir = "rewrite_profile";
- if (argc - optind == 1)
- {
- if (denominator > 0)
- ret = profile_rewrite (argv[optind], output_dir, 0, 0.0, numerator, denominator);
- else
- ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale, 0, 0);
- }
- else
- rewrite_usage ();
- return ret;
- }
- /* Driver function to computer the overlap score b/w profile D1 and D2.
- Return 1 on error and 0 if OK. */
- static int
- profile_overlap (const char *d1, const char *d2)
- {
- struct gcov_info *d1_profile;
- struct gcov_info *d2_profile;
- d1_profile = gcov_read_profile_dir (d1, 0);
- if (!d1_profile)
- return 1;
- if (d2)
- {
- d2_profile = gcov_read_profile_dir (d2, 0);
- if (!d2_profile)
- return 1;
- return gcov_profile_overlap (d1_profile, d2_profile);
- }
- return 1;
- }
- /* Usage message for profile overlap. */
- static void
- print_overlap_usage_message (int error_p)
- {
- FILE *file = error_p ? stderr : stdout;
- fnotice (file, " overlap [options] <dir1> <dir2> Compute the overlap of two profiles\n");
- fnotice (file, " -v, --verbose Verbose mode\n");
- fnotice (file, " -h, --hotonly Only print info for hot objects/functions\n");
- fnotice (file, " -f, --function Print function level info\n");
- fnotice (file, " -F, --fullname Print full filename\n");
- fnotice (file, " -o, --object Print object level info\n");
- fnotice (file, " -t <float>, --hot_threshold <float> Set the threshold for hotness\n");
- }
- static const struct option overlap_options[] =
- {
- { "verbose", no_argument, NULL, 'v' },
- { "function", no_argument, NULL, 'f' },
- { "fullname", no_argument, NULL, 'F' },
- { "object", no_argument, NULL, 'o' },
- { "hotonly", no_argument, NULL, 'h' },
- { "hot_threshold", required_argument, NULL, 't' },
- { 0, 0, 0, 0 }
- };
- /* Print overlap usage and exit. */
- static void
- overlap_usage (void)
- {
- fnotice (stderr, "Overlap subcomand usage:");
- print_overlap_usage_message (true);
- exit (FATAL_EXIT_CODE);
- }
- int overlap_func_level;
- int overlap_obj_level;
- int overlap_hot_only;
- int overlap_use_fullname;
- double overlap_hot_threshold = 0.005;
- /* Driver for profile overlap sub-command. */
- static int
- do_overlap (int argc, char **argv)
- {
- int opt;
- int ret;
- optind = 0;
- while ((opt = getopt_long (argc, argv, "vfFoht:", overlap_options, NULL)) != -1)
- {
- switch (opt)
- {
- case 'v':
- verbose = true;
- gcov_set_verbose ();
- break;
- case 'f':
- overlap_func_level = 1;
- break;
- case 'F':
- overlap_use_fullname = 1;
- break;
- case 'o':
- overlap_obj_level = 1;
- break;
- case 'h':
- overlap_hot_only = 1;
- break;
- case 't':
- overlap_hot_threshold = atof (optarg);
- break;
- default:
- overlap_usage ();
- }
- }
- if (argc - optind == 2)
- ret = profile_overlap (argv[optind], argv[optind+1]);
- else
- overlap_usage ();
- return ret;
- }
- /* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
- otherwise the output of --help. */
- static void
- print_usage (int error_p)
- {
- FILE *file = error_p ? stderr : stdout;
- int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
- fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname);
- fnotice (file, "Offline tool to handle gcda counts\n\n");
- fnotice (file, " -h, --help Print this help, then exit\n");
- fnotice (file, " -v, --version Print version number, then exit\n");
- print_merge_usage_message (error_p);
- print_rewrite_usage_message (error_p);
- print_overlap_usage_message (error_p);
- fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
- bug_report_url);
- exit (status);
- }
- /* Print version information and exit. */
- static void
- print_version (void)
- {
- fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
- fnotice (stdout, "Copyright %s 2014-2015 Free Software Foundation, Inc.\n",
- _("(C)"));
- fnotice (stdout,
- _("This is free software; see the source for copying conditions.\n"
- "There is NO warranty; not even for MERCHANTABILITY or \n"
- "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
- exit (SUCCESS_EXIT_CODE);
- }
- static const struct option options[] =
- {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'v' },
- { 0, 0, 0, 0 }
- };
- /* Process args, return index to first non-arg. */
- static int
- process_args (int argc, char **argv)
- {
- int opt;
- while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1)
- {
- switch (opt)
- {
- case 'h':
- print_usage (false);
- /* Print_usage will exit. */
- case 'v':
- print_version ();
- /* Print_version will exit. */
- default:
- print_usage (true);
- /* Print_usage will exit. */
- }
- }
- return optind;
- }
- /* Main function for gcov-tool. */
- int
- main (int argc, char **argv)
- {
- const char *p;
- const char *sub_command;
- p = argv[0] + strlen (argv[0]);
- while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
- --p;
- progname = p;
- xmalloc_set_program_name (progname);
- /* Unlock the stdio streams. */
- unlock_std_streams ();
- gcc_init_libintl ();
- diagnostic_initialize (global_dc, 0);
- /* Handle response files. */
- expandargv (&argc, &argv);
- process_args (argc, argv);
- if (optind >= argc)
- print_usage (true);
- sub_command = argv[optind];
- if (!strcmp (sub_command, "merge"))
- return do_merge (argc - optind, argv + optind);
- else if (!strcmp (sub_command, "rewrite"))
- return do_rewrite (argc - optind, argv + optind);
- else if (!strcmp (sub_command, "overlap"))
- return do_overlap (argc - optind, argv + optind);
- print_usage (true);
- }
|