xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/gcov-tool.c (revision f3cfa6f6ce31685c6c4a758bc430e69eb99f50a4)
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