1/* Gcc offline profile processing tool support. */ 2/* Copyright (C) 2014-2015 Free Software Foundation, Inc. 3 Contributed by Rong Xu <xur@google.com>. 4 5This file is part of GCC. 6 7GCC is free software; you can redistribute it and/or modify it under 8the terms of the GNU General Public License as published by the Free 9Software Foundation; either version 3, or (at your option) any later 10version. 11 12GCC is distributed in the hope that it will be useful, but WITHOUT ANY 13WARRANTY; without even the implied warranty of MERCHANTABILITY or 14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15for more details. 16 17Under Section 7 of GPL version 3, you are granted additional 18permissions described in the GCC Runtime Library Exception, version 193.1, as published by the Free Software Foundation. 20 21You should have received a copy of the GNU General Public License and 22a copy of the GCC Runtime Library Exception along with this program; 23see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 24<http://www.gnu.org/licenses/>. */ 25 26#include "config.h" 27#include "system.h" 28#include "coretypes.h" 29#include "tm.h" 30#include "intl.h" 31#include "diagnostic.h" 32#include "version.h" 33#include "gcov-io.h" 34#include <stdlib.h> 35#include <stdio.h> 36#include <sys/stat.h> 37#include <unistd.h> 38#if HAVE_FTW_H 39#include <ftw.h> 40#endif 41#include <getopt.h> 42 43extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int); 44extern int gcov_profile_overlap (struct gcov_info*, struct gcov_info*); 45extern int gcov_profile_normalize (struct gcov_info*, gcov_type); 46extern int gcov_profile_scale (struct gcov_info*, float, int, int); 47extern struct gcov_info* gcov_read_profile_dir (const char*, int); 48extern void gcov_do_dump (struct gcov_info *, int); 49extern void gcov_set_verbose (void); 50 51/* Set to verbose output mode. */ 52static bool verbose; 53 54#if HAVE_FTW_H 55 56/* Remove file NAME if it has a gcda suffix. */ 57 58static int 59unlink_gcda_file (const char *name, 60 const struct stat *status ATTRIBUTE_UNUSED, 61 int type ATTRIBUTE_UNUSED, 62 struct FTW *ftwbuf ATTRIBUTE_UNUSED) 63{ 64 int ret = 0; 65 int len = strlen (name); 66 int len1 = strlen (GCOV_DATA_SUFFIX); 67 68 if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1)) 69 ret = remove (name); 70 71 if (ret) 72 fatal_error (input_location, "error in removing %s\n", name); 73 74 return ret; 75} 76#endif 77 78/* Remove the gcda files in PATH recursively. */ 79 80static int 81unlink_profile_dir (const char *path ATTRIBUTE_UNUSED) 82{ 83#if HAVE_FTW_H 84 return nftw(path, unlink_gcda_file, 64, FTW_DEPTH | FTW_PHYS); 85#else 86 return -1; 87#endif 88} 89 90/* Output GCOV_INFO lists PROFILE to directory OUT. Note that 91 we will remove all the gcda files in OUT. */ 92 93static void 94gcov_output_files (const char *out, struct gcov_info *profile) 95{ 96 char *pwd; 97 int ret; 98 99 /* Try to make directory if it doesn't already exist. */ 100 if (access (out, F_OK) == -1) 101 { 102 if (mkdir (out, S_IRWXU | S_IRWXG | S_IRWXO) == -1 && errno != EEXIST) 103 fatal_error (input_location, "Cannot make directory %s", out); 104 } else 105 unlink_profile_dir (out); 106 107 /* Output new profile. */ 108 pwd = getcwd (NULL, 0); 109 110 if (pwd == NULL) 111 fatal_error (input_location, "Cannot get current directory name"); 112 113 ret = chdir (out); 114 if (ret) 115 fatal_error (input_location, "Cannot change directory to %s", out); 116 117 gcov_do_dump (profile, 0); 118 119 ret = chdir (pwd); 120 if (ret) 121 fatal_error (input_location, "Cannot change directory to %s", pwd); 122 123 free (pwd); 124} 125 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. */ 129 130static int 131profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2) 132{ 133 struct gcov_info *d1_profile; 134 struct gcov_info *d2_profile; 135 int ret; 136 137 d1_profile = gcov_read_profile_dir (d1, 0); 138 if (!d1_profile) 139 return 1; 140 141 if (d2) 142 { 143 d2_profile = gcov_read_profile_dir (d2, 0); 144 if (!d2_profile) 145 return 1; 146 147 /* The actual merge: we overwrite to d1_profile. */ 148 ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2); 149 150 if (ret) 151 return ret; 152 } 153 154 gcov_output_files (out, d1_profile); 155 156 return 0; 157} 158 159/* Usage message for profile merge. */ 160 161static void 162print_merge_usage_message (int error_p) 163{ 164 FILE *file = error_p ? stderr : stdout; 165 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"); 170} 171 172static const struct option merge_options[] = 173{ 174 { "verbose", no_argument, NULL, 'v' }, 175 { "output", required_argument, NULL, 'o' }, 176 { "weight", required_argument, NULL, 'w' }, 177 { 0, 0, 0, 0 } 178}; 179 180/* Print merge usage and exit. */ 181 182static void 183merge_usage (void) 184{ 185 fnotice (stderr, "Merge subcomand usage:"); 186 print_merge_usage_message (true); 187 exit (FATAL_EXIT_CODE); 188} 189 190/* Driver for profile merge sub-command. */ 191 192static int 193do_merge (int argc, char **argv) 194{ 195 int opt; 196 int ret; 197 const char *output_dir = 0; 198 int w1 = 1, w2 = 1; 199 200 optind = 0; 201 while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1) 202 { 203 switch (opt) 204 { 205 case 'v': 206 verbose = true; 207 gcov_set_verbose (); 208 break; 209 case 'o': 210 output_dir = optarg; 211 break; 212 case 'w': 213 sscanf (optarg, "%d,%d", &w1, &w2); 214 if (w1 < 0 || w2 < 0) 215 fatal_error (input_location, "weights need to be non-negative\n"); 216 break; 217 default: 218 merge_usage (); 219 } 220 } 221 222 if (output_dir == NULL) 223 output_dir = "merged_profile"; 224 225 if (argc - optind == 2) 226 ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2); 227 else 228 merge_usage (); 229 230 return ret; 231} 232 233/* If N_VAL is no-zero, normalize the profile by setting the largest counter 234 counter value to N_VAL and scale others counters proportionally. 235 Otherwise, multiply the all counters by SCALE. */ 236 237static int 238profile_rewrite (const char *d1, const char *out, long long n_val, 239 float scale, int n, int d) 240{ 241 struct gcov_info * d1_profile; 242 243 d1_profile = gcov_read_profile_dir (d1, 0); 244 if (!d1_profile) 245 return 1; 246 247 if (n_val) 248 gcov_profile_normalize (d1_profile, (gcov_type) n_val); 249 else 250 gcov_profile_scale (d1_profile, scale, n, d); 251 252 gcov_output_files (out, d1_profile); 253 return 0; 254} 255 256/* Usage function for profile rewrite. */ 257 258static void 259print_rewrite_usage_message (int error_p) 260{ 261 FILE *file = error_p ? stderr : stdout; 262 263 fnotice (file, " rewrite [options] <dir> Rewrite coverage file contents\n"); 264 fnotice (file, " -v, --verbose Verbose mode\n"); 265 fnotice (file, " -o, --output <dir> Output directory\n"); 266 fnotice (file, " -s, --scale <float or simple-frac> Scale the profile counters\n"); 267 fnotice (file, " -n, --normalize <long long> Normalize the profile\n"); 268} 269 270static const struct option rewrite_options[] = 271{ 272 { "verbose", no_argument, NULL, 'v' }, 273 { "output", required_argument, NULL, 'o' }, 274 { "scale", required_argument, NULL, 's' }, 275 { "normalize", required_argument, NULL, 'n' }, 276 { 0, 0, 0, 0 } 277}; 278 279/* Print profile rewrite usage and exit. */ 280 281static void 282rewrite_usage (void) 283{ 284 fnotice (stderr, "Rewrite subcommand usage:"); 285 print_rewrite_usage_message (true); 286 exit (FATAL_EXIT_CODE); 287} 288 289/* Driver for profile rewrite sub-command. */ 290 291static int 292do_rewrite (int argc, char **argv) 293{ 294 int opt; 295 int ret; 296 const char *output_dir = 0; 297#ifdef HAVE_LONG_LONG 298 long long normalize_val = 0; 299#else 300 int64_t normalize_val = 0; 301#endif 302 float scale = 0.0; 303 int numerator = 1; 304 int denominator = 1; 305 int do_scaling = 0; 306 307 optind = 0; 308 while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1) 309 { 310 switch (opt) 311 { 312 case 'v': 313 verbose = true; 314 gcov_set_verbose (); 315 break; 316 case 'o': 317 output_dir = optarg; 318 break; 319 case 'n': 320 if (!do_scaling) 321#if defined(HAVE_LONG_LONG) 322 normalize_val = strtoll (optarg, (char **)NULL, 10); 323#elif defined(INT64_T_IS_LONG) 324 normalize_val = strtol (optarg, (char **)NULL, 10); 325#else 326 sscanf (optarg, "%" SCNd64, &normalize_val); 327#endif 328 else 329 fnotice (stderr, "scaling cannot co-exist with normalization," 330 " skipping\n"); 331 break; 332 case 's': 333 ret = 0; 334 do_scaling = 1; 335 if (strstr (optarg, "/")) 336 { 337 ret = sscanf (optarg, "%d/%d", &numerator, &denominator); 338 if (ret == 2) 339 { 340 if (numerator < 0 || denominator <= 0) 341 { 342 fnotice (stderr, "incorrect format in scaling, using 1/1\n"); 343 denominator = 1; 344 numerator = 1; 345 } 346 } 347 } 348 if (ret != 2) 349 { 350 ret = sscanf (optarg, "%f", &scale); 351 if (ret != 1) 352 fnotice (stderr, "incorrect format in scaling, using 1/1\n"); 353 else 354 denominator = 0; 355 } 356 357 if (scale < 0.0) 358 fatal_error (input_location, "scale needs to be non-negative\n"); 359 360 if (normalize_val != 0) 361 { 362 fnotice (stderr, "normalization cannot co-exist with scaling\n"); 363 normalize_val = 0; 364 } 365 break; 366 default: 367 rewrite_usage (); 368 } 369 } 370 371 if (output_dir == NULL) 372 output_dir = "rewrite_profile"; 373 374 if (argc - optind == 1) 375 { 376 if (denominator > 0) 377 ret = profile_rewrite (argv[optind], output_dir, 0, 0.0, numerator, denominator); 378 else 379 ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale, 0, 0); 380 } 381 else 382 rewrite_usage (); 383 384 return ret; 385} 386 387/* Driver function to computer the overlap score b/w profile D1 and D2. 388 Return 1 on error and 0 if OK. */ 389 390static int 391profile_overlap (const char *d1, const char *d2) 392{ 393 struct gcov_info *d1_profile; 394 struct gcov_info *d2_profile; 395 396 d1_profile = gcov_read_profile_dir (d1, 0); 397 if (!d1_profile) 398 return 1; 399 400 if (d2) 401 { 402 d2_profile = gcov_read_profile_dir (d2, 0); 403 if (!d2_profile) 404 return 1; 405 406 return gcov_profile_overlap (d1_profile, d2_profile); 407 } 408 409 return 1; 410} 411 412/* Usage message for profile overlap. */ 413 414static void 415print_overlap_usage_message (int error_p) 416{ 417 FILE *file = error_p ? stderr : stdout; 418 419 fnotice (file, " overlap [options] <dir1> <dir2> Compute the overlap of two profiles\n"); 420 fnotice (file, " -v, --verbose Verbose mode\n"); 421 fnotice (file, " -h, --hotonly Only print info for hot objects/functions\n"); 422 fnotice (file, " -f, --function Print function level info\n"); 423 fnotice (file, " -F, --fullname Print full filename\n"); 424 fnotice (file, " -o, --object Print object level info\n"); 425 fnotice (file, " -t <float>, --hot_threshold <float> Set the threshold for hotness\n"); 426 427} 428 429static const struct option overlap_options[] = 430{ 431 { "verbose", no_argument, NULL, 'v' }, 432 { "function", no_argument, NULL, 'f' }, 433 { "fullname", no_argument, NULL, 'F' }, 434 { "object", no_argument, NULL, 'o' }, 435 { "hotonly", no_argument, NULL, 'h' }, 436 { "hot_threshold", required_argument, NULL, 't' }, 437 { 0, 0, 0, 0 } 438}; 439 440/* Print overlap usage and exit. */ 441 442static void 443overlap_usage (void) 444{ 445 fnotice (stderr, "Overlap subcomand usage:"); 446 print_overlap_usage_message (true); 447 exit (FATAL_EXIT_CODE); 448} 449 450int overlap_func_level; 451int overlap_obj_level; 452int overlap_hot_only; 453int overlap_use_fullname; 454double overlap_hot_threshold = 0.005; 455 456/* Driver for profile overlap sub-command. */ 457 458static int 459do_overlap (int argc, char **argv) 460{ 461 int opt; 462 int ret; 463 464 optind = 0; 465 while ((opt = getopt_long (argc, argv, "vfFoht:", overlap_options, NULL)) != -1) 466 { 467 switch (opt) 468 { 469 case 'v': 470 verbose = true; 471 gcov_set_verbose (); 472 break; 473 case 'f': 474 overlap_func_level = 1; 475 break; 476 case 'F': 477 overlap_use_fullname = 1; 478 break; 479 case 'o': 480 overlap_obj_level = 1; 481 break; 482 case 'h': 483 overlap_hot_only = 1; 484 break; 485 case 't': 486 overlap_hot_threshold = atof (optarg); 487 break; 488 default: 489 overlap_usage (); 490 } 491 } 492 493 if (argc - optind == 2) 494 ret = profile_overlap (argv[optind], argv[optind+1]); 495 else 496 overlap_usage (); 497 498 return ret; 499} 500 501 502/* Print a usage message and exit. If ERROR_P is nonzero, this is an error, 503 otherwise the output of --help. */ 504 505static void 506print_usage (int error_p) 507{ 508 FILE *file = error_p ? stderr : stdout; 509 int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; 510 511 fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname); 512 fnotice (file, "Offline tool to handle gcda counts\n\n"); 513 fnotice (file, " -h, --help Print this help, then exit\n"); 514 fnotice (file, " -v, --version Print version number, then exit\n"); 515 print_merge_usage_message (error_p); 516 print_rewrite_usage_message (error_p); 517 print_overlap_usage_message (error_p); 518 fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", 519 bug_report_url); 520 exit (status); 521} 522 523/* Print version information and exit. */ 524 525static void 526print_version (void) 527{ 528 fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string); 529 fnotice (stdout, "Copyright %s 2014-2015 Free Software Foundation, Inc.\n", 530 _("(C)")); 531 fnotice (stdout, 532 _("This is free software; see the source for copying conditions.\n" 533 "There is NO warranty; not even for MERCHANTABILITY or \n" 534 "FITNESS FOR A PARTICULAR PURPOSE.\n\n")); 535 exit (SUCCESS_EXIT_CODE); 536} 537 538static const struct option options[] = 539{ 540 { "help", no_argument, NULL, 'h' }, 541 { "version", no_argument, NULL, 'v' }, 542 { 0, 0, 0, 0 } 543}; 544 545/* Process args, return index to first non-arg. */ 546 547static int 548process_args (int argc, char **argv) 549{ 550 int opt; 551 552 while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1) 553 { 554 switch (opt) 555 { 556 case 'h': 557 print_usage (false); 558 /* Print_usage will exit. */ 559 case 'v': 560 print_version (); 561 /* Print_version will exit. */ 562 default: 563 print_usage (true); 564 /* Print_usage will exit. */ 565 } 566 } 567 568 return optind; 569} 570 571/* Main function for gcov-tool. */ 572 573int 574main (int argc, char **argv) 575{ 576 const char *p; 577 const char *sub_command; 578 579 p = argv[0] + strlen (argv[0]); 580 while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1])) 581 --p; 582 progname = p; 583 584 xmalloc_set_program_name (progname); 585 586 /* Unlock the stdio streams. */ 587 unlock_std_streams (); 588 589 gcc_init_libintl (); 590 591 diagnostic_initialize (global_dc, 0); 592 593 /* Handle response files. */ 594 expandargv (&argc, &argv); 595 596 process_args (argc, argv); 597 if (optind >= argc) 598 print_usage (true); 599 600 sub_command = argv[optind]; 601 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); 608 609 print_usage (true); 610} 611