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