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 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 int ret; 206 const char *output_dir = 0; 207 int w1 = 1, w2 = 1; 208 209 optind = 0; 210 while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1) 211 { 212 switch (opt) 213 { 214 case 'v': 215 verbose = true; 216 gcov_set_verbose (); 217 break; 218 case 'o': 219 output_dir = optarg; 220 break; 221 case 'w': 222 sscanf (optarg, "%d,%d", &w1, &w2); 223 if (w1 < 0 || w2 < 0) 224 fatal_error (input_location, "weights need to be non-negative\n"); 225 break; 226 default: 227 merge_usage (); 228 } 229 } 230 231 if (output_dir == NULL) 232 output_dir = "merged_profile"; 233 234 if (argc - optind == 2) 235 ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2); 236 else 237 merge_usage (); 238 239 return ret; 240 } 241 242 /* If N_VAL is no-zero, normalize the profile by setting the largest counter 243 counter value to N_VAL and scale others counters proportionally. 244 Otherwise, multiply the all counters by SCALE. */ 245 246 static int 247 profile_rewrite (const char *d1, const char *out, long long n_val, 248 float scale, int n, int d) 249 { 250 struct gcov_info * d1_profile; 251 252 d1_profile = gcov_read_profile_dir (d1, 0); 253 if (!d1_profile) 254 return 1; 255 256 if (n_val) 257 gcov_profile_normalize (d1_profile, (gcov_type) n_val); 258 else 259 gcov_profile_scale (d1_profile, scale, n, d); 260 261 gcov_output_files (out, d1_profile); 262 return 0; 263 } 264 265 /* Usage function for profile rewrite. */ 266 267 static void 268 print_rewrite_usage_message (int error_p) 269 { 270 FILE *file = error_p ? stderr : stdout; 271 272 fnotice (file, " rewrite [options] <dir> Rewrite coverage file contents\n"); 273 fnotice (file, " -n, --normalize <int64_t> Normalize the profile\n"); 274 fnotice (file, " -o, --output <dir> Output directory\n"); 275 fnotice (file, " -s, --scale <float or simple-frac> Scale the profile counters\n"); 276 fnotice (file, " -n, --normalize <long long> Normalize the profile\n"); 277 fnotice (file, " -v, --verbose Verbose mode\n"); 278 } 279 280 static const struct option rewrite_options[] = 281 { 282 { "verbose", no_argument, NULL, 'v' }, 283 { "output", required_argument, NULL, 'o' }, 284 { "scale", required_argument, NULL, 's' }, 285 { "normalize", required_argument, NULL, 'n' }, 286 { 0, 0, 0, 0 } 287 }; 288 289 /* Print profile rewrite usage and exit. */ 290 291 static void 292 rewrite_usage (void) 293 { 294 fnotice (stderr, "Rewrite subcommand usage:"); 295 print_rewrite_usage_message (true); 296 exit (FATAL_EXIT_CODE); 297 } 298 299 /* Driver for profile rewrite sub-command. */ 300 301 static int 302 do_rewrite (int argc, char **argv) 303 { 304 int opt; 305 int ret; 306 const char *output_dir = 0; 307 #ifdef HAVE_LONG_LONG 308 long long normalize_val = 0; 309 #else 310 int64_t normalize_val = 0; 311 #endif 312 float scale = 0.0; 313 int numerator = 1; 314 int denominator = 1; 315 int do_scaling = 0; 316 317 optind = 0; 318 while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1) 319 { 320 switch (opt) 321 { 322 case 'v': 323 verbose = true; 324 gcov_set_verbose (); 325 break; 326 case 'o': 327 output_dir = optarg; 328 break; 329 case 'n': 330 if (!do_scaling) 331 #if defined(HAVE_LONG_LONG) 332 normalize_val = strtoll (optarg, (char **)NULL, 10); 333 #elif defined(INT64_T_IS_LONG) 334 normalize_val = strtol (optarg, (char **)NULL, 10); 335 #else 336 sscanf (optarg, "%" SCNd64, &normalize_val); 337 #endif 338 else 339 fnotice (stderr, "scaling cannot co-exist with normalization," 340 " skipping\n"); 341 break; 342 case 's': 343 ret = 0; 344 do_scaling = 1; 345 if (strstr (optarg, "/")) 346 { 347 ret = sscanf (optarg, "%d/%d", &numerator, &denominator); 348 if (ret == 2) 349 { 350 if (numerator < 0 || denominator <= 0) 351 { 352 fnotice (stderr, "incorrect format in scaling, using 1/1\n"); 353 denominator = 1; 354 numerator = 1; 355 } 356 } 357 } 358 if (ret != 2) 359 { 360 ret = sscanf (optarg, "%f", &scale); 361 if (ret != 1) 362 fnotice (stderr, "incorrect format in scaling, using 1/1\n"); 363 else 364 denominator = 0; 365 } 366 367 if (scale < 0.0) 368 fatal_error (input_location, "scale needs to be non-negative\n"); 369 370 if (normalize_val != 0) 371 { 372 fnotice (stderr, "normalization cannot co-exist with scaling\n"); 373 normalize_val = 0; 374 } 375 break; 376 default: 377 rewrite_usage (); 378 } 379 } 380 381 if (output_dir == NULL) 382 output_dir = "rewrite_profile"; 383 384 if (argc - optind == 1) 385 { 386 if (denominator > 0) 387 ret = profile_rewrite (argv[optind], output_dir, 0, 0.0, numerator, denominator); 388 else 389 ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale, 0, 0); 390 } 391 else 392 rewrite_usage (); 393 394 return ret; 395 } 396 397 /* Driver function to computer the overlap score b/w profile D1 and D2. 398 Return 1 on error and 0 if OK. */ 399 400 static int 401 profile_overlap (const char *d1, const char *d2) 402 { 403 struct gcov_info *d1_profile; 404 struct gcov_info *d2_profile; 405 406 d1_profile = gcov_read_profile_dir (d1, 0); 407 if (!d1_profile) 408 return 1; 409 410 if (d2) 411 { 412 d2_profile = gcov_read_profile_dir (d2, 0); 413 if (!d2_profile) 414 return 1; 415 416 return gcov_profile_overlap (d1_profile, d2_profile); 417 } 418 419 return 1; 420 } 421 422 /* Usage message for profile overlap. */ 423 424 static void 425 print_overlap_usage_message (int error_p) 426 { 427 FILE *file = error_p ? stderr : stdout; 428 429 fnotice (file, " overlap [options] <dir1> <dir2> Compute the overlap of two profiles\n"); 430 fnotice (file, " -f, --function Print function level info\n"); 431 fnotice (file, " -F, --fullname Print full filename\n"); 432 fnotice (file, " -h, --hotonly Only print info for hot objects/functions\n"); 433 fnotice (file, " -o, --object Print object level info\n"); 434 fnotice (file, " -t <float>, --hot_threshold <float> Set the threshold for hotness\n"); 435 fnotice (file, " -v, --verbose Verbose mode\n"); 436 437 } 438 439 static const struct option overlap_options[] = 440 { 441 { "verbose", no_argument, NULL, 'v' }, 442 { "function", no_argument, NULL, 'f' }, 443 { "fullname", no_argument, NULL, 'F' }, 444 { "object", no_argument, NULL, 'o' }, 445 { "hotonly", no_argument, NULL, 'h' }, 446 { "hot_threshold", required_argument, NULL, 't' }, 447 { 0, 0, 0, 0 } 448 }; 449 450 /* Print overlap usage and exit. */ 451 452 static void 453 overlap_usage (void) 454 { 455 fnotice (stderr, "Overlap subcomand usage:"); 456 print_overlap_usage_message (true); 457 exit (FATAL_EXIT_CODE); 458 } 459 460 int overlap_func_level; 461 int overlap_obj_level; 462 int overlap_hot_only; 463 int overlap_use_fullname; 464 double overlap_hot_threshold = 0.005; 465 466 /* Driver for profile overlap sub-command. */ 467 468 static int 469 do_overlap (int argc, char **argv) 470 { 471 int opt; 472 int ret; 473 474 optind = 0; 475 while ((opt = getopt_long (argc, argv, "vfFoht:", overlap_options, NULL)) != -1) 476 { 477 switch (opt) 478 { 479 case 'v': 480 verbose = true; 481 gcov_set_verbose (); 482 break; 483 case 'f': 484 overlap_func_level = 1; 485 break; 486 case 'F': 487 overlap_use_fullname = 1; 488 break; 489 case 'o': 490 overlap_obj_level = 1; 491 break; 492 case 'h': 493 overlap_hot_only = 1; 494 break; 495 case 't': 496 overlap_hot_threshold = atof (optarg); 497 break; 498 default: 499 overlap_usage (); 500 } 501 } 502 503 if (argc - optind == 2) 504 ret = profile_overlap (argv[optind], argv[optind+1]); 505 else 506 overlap_usage (); 507 508 return ret; 509 } 510 511 512 /* Print a usage message and exit. If ERROR_P is nonzero, this is an error, 513 otherwise the output of --help. */ 514 515 static void 516 print_usage (int error_p) 517 { 518 FILE *file = error_p ? stderr : stdout; 519 int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; 520 521 fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname); 522 fnotice (file, "Offline tool to handle gcda counts\n\n"); 523 fnotice (file, " -h, --help Print this help, then exit\n"); 524 fnotice (file, " -v, --version Print version number, then exit\n"); 525 print_merge_usage_message (error_p); 526 print_rewrite_usage_message (error_p); 527 print_overlap_usage_message (error_p); 528 fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", 529 bug_report_url); 530 exit (status); 531 } 532 533 /* Print version information and exit. */ 534 535 static void 536 print_version (void) 537 { 538 fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string); 539 fnotice (stdout, "Copyright %s 2014-2015 Free Software Foundation, Inc.\n", 540 _("(C)")); 541 fnotice (stdout, 542 _("This is free software; see the source for copying conditions.\n" 543 "There is NO warranty; not even for MERCHANTABILITY or \n" 544 "FITNESS FOR A PARTICULAR PURPOSE.\n\n")); 545 exit (SUCCESS_EXIT_CODE); 546 } 547 548 static const struct option options[] = 549 { 550 { "help", no_argument, NULL, 'h' }, 551 { "version", no_argument, NULL, 'v' }, 552 { 0, 0, 0, 0 } 553 }; 554 555 /* Process args, return index to first non-arg. */ 556 557 static int 558 process_args (int argc, char **argv) 559 { 560 int opt; 561 562 while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1) 563 { 564 switch (opt) 565 { 566 case 'h': 567 print_usage (false); 568 /* Print_usage will exit. */ 569 case 'v': 570 print_version (); 571 /* Print_version will exit. */ 572 default: 573 print_usage (true); 574 /* Print_usage will exit. */ 575 } 576 } 577 578 return optind; 579 } 580 581 /* Main function for gcov-tool. */ 582 583 int 584 main (int argc, char **argv) 585 { 586 const char *p; 587 const char *sub_command; 588 589 p = argv[0] + strlen (argv[0]); 590 while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1])) 591 --p; 592 progname = p; 593 594 xmalloc_set_program_name (progname); 595 596 /* Unlock the stdio streams. */ 597 unlock_std_streams (); 598 599 gcc_init_libintl (); 600 601 diagnostic_initialize (global_dc, 0); 602 603 /* Handle response files. */ 604 expandargv (&argc, &argv); 605 606 process_args (argc, argv); 607 if (optind >= argc) 608 print_usage (true); 609 610 sub_command = argv[optind]; 611 612 if (!strcmp (sub_command, "merge")) 613 return do_merge (argc - optind, argv + optind); 614 else if (!strcmp (sub_command, "rewrite")) 615 return do_rewrite (argc - optind, argv + optind); 616 else if (!strcmp (sub_command, "overlap")) 617 return do_overlap (argc - optind, argv + optind); 618 619 print_usage (true); 620 } 621