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