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