gcov-tool.c 16 KB

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