]>
Commit | Line | Data |
---|---|---|
4977bab6 | 1 | /* Dump a gcov file, for debugging use. |
8d9254fc | 2 | Copyright (C) 2002-2020 Free Software Foundation, Inc. |
4977bab6 ZW |
3 | Contributed by Nathan Sidwell <nathan@codesourcery.com> |
4 | ||
5 | Gcov is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
9dcd6f09 | 7 | the Free Software Foundation; either version 3, or (at your option) |
4977bab6 ZW |
8 | any later version. |
9 | ||
10 | Gcov is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
9dcd6f09 NC |
16 | along with Gcov; see the file COPYING3. If not see |
17 | <http://www.gnu.org/licenses/>. */ | |
4977bab6 ZW |
18 | |
19 | #include "config.h" | |
20 | #include "system.h" | |
21 | #include "coretypes.h" | |
22 | #include "tm.h" | |
23 | #include "version.h" | |
2691e6d7 JM |
24 | #include "intl.h" |
25 | #include "diagnostic.h" | |
4977bab6 | 26 | #include <getopt.h> |
546d2adb | 27 | #define IN_GCOV (-1) |
4977bab6 | 28 | #include "gcov-io.h" |
ca29da43 | 29 | #include "gcov-io.c" |
4977bab6 | 30 | |
be7a421e | 31 | static void dump_gcov_file (const char *); |
1d088dee AJ |
32 | static void print_prefix (const char *, unsigned, gcov_position_t); |
33 | static void print_usage (void); | |
34 | static void print_version (void); | |
5c5059bc ML |
35 | static void tag_function (const char *, unsigned, unsigned, unsigned); |
36 | static void tag_blocks (const char *, unsigned, unsigned, unsigned); | |
37 | static void tag_arcs (const char *, unsigned, unsigned, unsigned); | |
38 | static void tag_lines (const char *, unsigned, unsigned, unsigned); | |
39 | static void tag_counters (const char *, unsigned, unsigned, unsigned); | |
40 | static void tag_summary (const char *, unsigned, unsigned, unsigned); | |
1d088dee | 41 | extern int main (int, char **); |
4977bab6 ZW |
42 | |
43 | typedef struct tag_format | |
44 | { | |
45 | unsigned tag; | |
46 | char const *name; | |
5c5059bc | 47 | void (*proc) (const char *, unsigned, unsigned, unsigned); |
4977bab6 ZW |
48 | } tag_format_t; |
49 | ||
50 | static int flag_dump_contents = 0; | |
7d63a2fa | 51 | static int flag_dump_positions = 0; |
4977bab6 ZW |
52 | |
53 | static const struct option options[] = | |
54 | { | |
55 | { "help", no_argument, NULL, 'h' }, | |
56 | { "version", no_argument, NULL, 'v' }, | |
57 | { "long", no_argument, NULL, 'l' }, | |
7d63a2fa | 58 | { "positions", no_argument, NULL, 'o' }, |
d90f9882 | 59 | { 0, 0, 0, 0 } |
4977bab6 ZW |
60 | }; |
61 | ||
5c5059bc ML |
62 | #define VALUE_PADDING_PREFIX " " |
63 | #define VALUE_PREFIX "%2d: " | |
64 | ||
83fa8d7a | 65 | static const tag_format_t tag_table[] = |
4977bab6 ZW |
66 | { |
67 | {0, "NOP", NULL}, | |
68 | {0, "UNKNOWN", NULL}, | |
cdb23767 | 69 | {0, "COUNTERS", tag_counters}, |
4977bab6 ZW |
70 | {GCOV_TAG_FUNCTION, "FUNCTION", tag_function}, |
71 | {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks}, | |
72 | {GCOV_TAG_ARCS, "ARCS", tag_arcs}, | |
73 | {GCOV_TAG_LINES, "LINES", tag_lines}, | |
4977bab6 | 74 | {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary}, |
4977bab6 ZW |
75 | {0, NULL, NULL} |
76 | }; | |
77 | ||
1d088dee AJ |
78 | int |
79 | main (int argc ATTRIBUTE_UNUSED, char **argv) | |
4977bab6 ZW |
80 | { |
81 | int opt; | |
2691e6d7 JM |
82 | const char *p; |
83 | ||
84 | p = argv[0] + strlen (argv[0]); | |
85 | while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1])) | |
86 | --p; | |
87 | progname = p; | |
88 | ||
89 | xmalloc_set_program_name (progname); | |
4977bab6 | 90 | |
98a3dad4 | 91 | /* Unlock the stdio streams. */ |
2653bb0c | 92 | unlock_std_streams (); |
98a3dad4 | 93 | |
2691e6d7 JM |
94 | gcc_init_libintl (); |
95 | ||
96 | diagnostic_initialize (global_dc, 0); | |
97 | ||
f57ddb5b | 98 | while ((opt = getopt_long (argc, argv, "hlpvw", options, NULL)) != -1) |
4977bab6 ZW |
99 | { |
100 | switch (opt) | |
101 | { | |
102 | case 'h': | |
103 | print_usage (); | |
104 | break; | |
105 | case 'v': | |
106 | print_version (); | |
107 | break; | |
108 | case 'l': | |
109 | flag_dump_contents = 1; | |
110 | break; | |
7d63a2fa NS |
111 | case 'p': |
112 | flag_dump_positions = 1; | |
113 | break; | |
4977bab6 ZW |
114 | default: |
115 | fprintf (stderr, "unknown flag `%c'\n", opt); | |
116 | } | |
117 | } | |
1d088dee | 118 | |
4977bab6 | 119 | while (argv[optind]) |
be7a421e | 120 | dump_gcov_file (argv[optind++]); |
4977bab6 ZW |
121 | return 0; |
122 | } | |
123 | ||
124 | static void | |
1d088dee | 125 | print_usage (void) |
4977bab6 ZW |
126 | { |
127 | printf ("Usage: gcov-dump [OPTION] ... gcovfiles\n"); | |
128 | printf ("Print coverage file contents\n"); | |
129 | printf (" -h, --help Print this help\n"); | |
4977bab6 | 130 | printf (" -l, --long Dump record contents too\n"); |
7d63a2fa | 131 | printf (" -p, --positions Dump record positions\n"); |
2f360676 | 132 | printf (" -v, --version Print version number\n"); |
39653b82 TV |
133 | printf ("\nFor bug reporting instructions, please see:\n%s.\n", |
134 | bug_report_url); | |
4977bab6 ZW |
135 | } |
136 | ||
137 | static void | |
1d088dee | 138 | print_version (void) |
4977bab6 | 139 | { |
2f41c1d6 | 140 | printf ("gcov-dump %s%s\n", pkgversion_string, version_string); |
7e7065b9 | 141 | printf ("Copyright (C) 2020 Free Software Foundation, Inc.\n"); |
330d2e2a NS |
142 | printf ("This is free software; see the source for copying conditions.\n" |
143 | "There is NO warranty; not even for MERCHANTABILITY or \n" | |
144 | "FITNESS FOR A PARTICULAR PURPOSE.\n\n"); | |
4977bab6 ZW |
145 | } |
146 | ||
147 | static void | |
1d088dee | 148 | print_prefix (const char *filename, unsigned depth, gcov_position_t position) |
4977bab6 ZW |
149 | { |
150 | static const char prefix[] = " "; | |
1d088dee | 151 | |
7d63a2fa NS |
152 | printf ("%s:", filename); |
153 | if (flag_dump_positions) | |
5c5059bc ML |
154 | printf ("%5lu:", (unsigned long) position); |
155 | printf ("%.*s", (int) 2 * depth, prefix); | |
4977bab6 ZW |
156 | } |
157 | ||
158 | static void | |
be7a421e | 159 | dump_gcov_file (const char *filename) |
4977bab6 | 160 | { |
4977bab6 ZW |
161 | unsigned tags[4]; |
162 | unsigned depth = 0; | |
bb5307a6 | 163 | bool is_data_type; |
1d088dee | 164 | |
546d2adb | 165 | if (!gcov_open (filename, 1)) |
4977bab6 ZW |
166 | { |
167 | fprintf (stderr, "%s:cannot open\n", filename); | |
168 | return; | |
169 | } | |
1d088dee | 170 | |
4977bab6 ZW |
171 | /* magic */ |
172 | { | |
94de45d9 | 173 | unsigned magic = gcov_read_unsigned (); |
160e2e4f | 174 | unsigned version; |
160e2e4f | 175 | int endianness = 0; |
330d2e2a | 176 | char m[4], v[4]; |
b8698a0f | 177 | |
160e2e4f | 178 | if ((endianness = gcov_magic (magic, GCOV_DATA_MAGIC))) |
bb5307a6 | 179 | is_data_type = true; |
160e2e4f | 180 | else if ((endianness = gcov_magic (magic, GCOV_NOTE_MAGIC))) |
bb5307a6 | 181 | is_data_type = false; |
4977bab6 ZW |
182 | else |
183 | { | |
184 | printf ("%s:not a gcov file\n", filename); | |
546d2adb | 185 | gcov_close (); |
4977bab6 ZW |
186 | return; |
187 | } | |
160e2e4f | 188 | version = gcov_read_unsigned (); |
330d2e2a NS |
189 | GCOV_UNSIGNED2STRING (v, version); |
190 | GCOV_UNSIGNED2STRING (m, magic); | |
b8698a0f | 191 | |
bb5307a6 ML |
192 | printf ("%s:%s:magic `%.4s':version `%.4s'%s\n", filename, |
193 | is_data_type ? "data" : "note", | |
330d2e2a | 194 | m, v, endianness < 0 ? " (swapped endianness)" : ""); |
160e2e4f | 195 | if (version != GCOV_VERSION) |
4977bab6 | 196 | { |
330d2e2a | 197 | char e[4]; |
b8698a0f | 198 | |
330d2e2a NS |
199 | GCOV_UNSIGNED2STRING (e, GCOV_VERSION); |
200 | printf ("%s:warning:current version is `%.4s'\n", filename, e); | |
4977bab6 | 201 | } |
4977bab6 ZW |
202 | } |
203 | ||
dd486eb2 NS |
204 | /* stamp */ |
205 | { | |
206 | unsigned stamp = gcov_read_unsigned (); | |
207 | ||
208 | printf ("%s:stamp %lu\n", filename, (unsigned long)stamp); | |
209 | } | |
b8698a0f | 210 | |
bb5307a6 ML |
211 | if (!is_data_type) |
212 | { | |
c74bd3fb ML |
213 | printf ("%s:cwd: %s\n", filename, gcov_read_string ()); |
214 | ||
bb5307a6 ML |
215 | /* Support for unexecuted basic blocks. */ |
216 | unsigned support_unexecuted_blocks = gcov_read_unsigned (); | |
217 | if (!support_unexecuted_blocks) | |
218 | printf ("%s: has_unexecuted_block is not supported\n", filename); | |
219 | } | |
93814e2d | 220 | |
7d63a2fa | 221 | while (1) |
4977bab6 | 222 | { |
7d63a2fa NS |
223 | gcov_position_t base, position = gcov_position (); |
224 | unsigned tag, length; | |
4977bab6 ZW |
225 | tag_format_t const *format; |
226 | unsigned tag_depth; | |
94de45d9 | 227 | int error; |
7d63a2fa NS |
228 | unsigned mask; |
229 | ||
230 | tag = gcov_read_unsigned (); | |
4977bab6 | 231 | if (!tag) |
7d63a2fa NS |
232 | break; |
233 | length = gcov_read_unsigned (); | |
234 | base = gcov_position (); | |
235 | mask = GCOV_TAG_MASK (tag) >> 1; | |
236 | for (tag_depth = 4; mask; mask >>= 8) | |
4977bab6 | 237 | { |
7d63a2fa | 238 | if ((mask & 0xff) != 0xff) |
4977bab6 | 239 | { |
7d63a2fa NS |
240 | printf ("%s:tag `%08x' is invalid\n", filename, tag); |
241 | break; | |
4977bab6 | 242 | } |
7d63a2fa | 243 | tag_depth--; |
4977bab6 ZW |
244 | } |
245 | for (format = tag_table; format->name; format++) | |
246 | if (format->tag == tag) | |
247 | goto found; | |
cdb23767 | 248 | format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1]; |
4977bab6 ZW |
249 | found:; |
250 | if (tag) | |
251 | { | |
252 | if (depth && depth < tag_depth) | |
253 | { | |
254 | if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag)) | |
255 | printf ("%s:tag `%08x' is incorrectly nested\n", | |
256 | filename, tag); | |
257 | } | |
258 | depth = tag_depth; | |
259 | tags[depth - 1] = tag; | |
260 | } | |
1d088dee | 261 | |
7d63a2fa | 262 | print_prefix (filename, tag_depth, position); |
4977bab6 ZW |
263 | printf ("%08x:%4u:%s", tag, length, format->name); |
264 | if (format->proc) | |
5c5059bc | 265 | (*format->proc) (filename, tag, length, depth); |
1d088dee | 266 | |
4977bab6 | 267 | printf ("\n"); |
94de45d9 | 268 | if (flag_dump_contents && format->proc) |
4977bab6 | 269 | { |
94de45d9 | 270 | unsigned long actual_length = gcov_position () - base; |
1d088dee | 271 | |
94de45d9 | 272 | if (actual_length > length) |
4977bab6 | 273 | printf ("%s:record size mismatch %lu bytes overread\n", |
94de45d9 NS |
274 | filename, actual_length - length); |
275 | else if (length > actual_length) | |
4977bab6 | 276 | printf ("%s:record size mismatch %lu bytes unread\n", |
94de45d9 NS |
277 | filename, length - actual_length); |
278 | } | |
474f141e | 279 | gcov_sync (base, length); |
94de45d9 NS |
280 | if ((error = gcov_is_error ())) |
281 | { | |
282 | printf (error < 0 ? "%s:counter overflow at %lu\n" : | |
9b514d25 NS |
283 | "%s:read error at %lu\n", filename, |
284 | (long unsigned) gcov_position ()); | |
94de45d9 | 285 | break; |
4977bab6 ZW |
286 | } |
287 | } | |
546d2adb | 288 | gcov_close (); |
4977bab6 ZW |
289 | } |
290 | ||
94de45d9 | 291 | static void |
1d088dee | 292 | tag_function (const char *filename ATTRIBUTE_UNUSED, |
5c5059bc ML |
293 | unsigned tag ATTRIBUTE_UNUSED, unsigned length, |
294 | unsigned depth ATTRIBUTE_UNUSED) | |
4977bab6 | 295 | { |
94de45d9 | 296 | unsigned long pos = gcov_position (); |
1d088dee | 297 | |
5366b186 NS |
298 | if (!length) |
299 | printf (" placeholder"); | |
300 | else | |
94de45d9 | 301 | { |
5366b186 NS |
302 | printf (" ident=%u", gcov_read_unsigned ()); |
303 | printf (", lineno_checksum=0x%08x", gcov_read_unsigned ()); | |
bdbdc4e1 | 304 | printf (", cfg_checksum=0x%08x", gcov_read_unsigned ()); |
1d088dee | 305 | |
5366b186 NS |
306 | if (gcov_position () - pos < length) |
307 | { | |
308 | const char *name; | |
309 | ||
310 | name = gcov_read_string (); | |
311 | printf (", `%s'", name ? name : "NULL"); | |
136ca74e | 312 | unsigned artificial = gcov_read_unsigned (); |
5366b186 NS |
313 | name = gcov_read_string (); |
314 | printf (" %s", name ? name : "NULL"); | |
136ca74e ML |
315 | unsigned line_start = gcov_read_unsigned (); |
316 | unsigned column_start = gcov_read_unsigned (); | |
317 | unsigned line_end = gcov_read_unsigned (); | |
b8154717 ML |
318 | unsigned column_end = gcov_read_unsigned (); |
319 | printf (":%u:%u-%u:%u", line_start, column_start, | |
320 | line_end, column_end); | |
136ca74e ML |
321 | if (artificial) |
322 | printf (", artificial"); | |
5366b186 | 323 | } |
94de45d9 | 324 | } |
4977bab6 ZW |
325 | } |
326 | ||
94de45d9 | 327 | static void |
1d088dee | 328 | tag_blocks (const char *filename ATTRIBUTE_UNUSED, |
5c5059bc | 329 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED, |
34769baf | 330 | unsigned depth ATTRIBUTE_UNUSED) |
4977bab6 | 331 | { |
34769baf | 332 | printf (" %u blocks", gcov_read_unsigned ()); |
4977bab6 ZW |
333 | } |
334 | ||
94de45d9 | 335 | static void |
1d088dee | 336 | tag_arcs (const char *filename ATTRIBUTE_UNUSED, |
5c5059bc ML |
337 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED, |
338 | unsigned depth) | |
4977bab6 | 339 | { |
330d2e2a | 340 | unsigned n_arcs = GCOV_TAG_ARCS_NUM (length); |
4977bab6 ZW |
341 | |
342 | printf (" %u arcs", n_arcs); | |
343 | if (flag_dump_contents) | |
344 | { | |
345 | unsigned ix; | |
94de45d9 | 346 | unsigned blockno = gcov_read_unsigned (); |
4977bab6 ZW |
347 | |
348 | for (ix = 0; ix != n_arcs; ix++) | |
349 | { | |
7d63a2fa | 350 | unsigned dst, flags; |
1d088dee | 351 | |
4977bab6 | 352 | if (!(ix & 3)) |
7d63a2fa NS |
353 | { |
354 | printf ("\n"); | |
5c5059bc ML |
355 | print_prefix (filename, depth, gcov_position ()); |
356 | printf (VALUE_PADDING_PREFIX "block %u:", blockno); | |
7d63a2fa NS |
357 | } |
358 | dst = gcov_read_unsigned (); | |
359 | flags = gcov_read_unsigned (); | |
4977bab6 | 360 | printf (" %u:%04x", dst, flags); |
8919c0d9 NS |
361 | if (flags) |
362 | { | |
363 | char c = '('; | |
364 | ||
365 | if (flags & GCOV_ARC_ON_TREE) | |
366 | printf ("%ctree", c), c = ','; | |
367 | if (flags & GCOV_ARC_FAKE) | |
368 | printf ("%cfake", c), c = ','; | |
369 | if (flags & GCOV_ARC_FALLTHROUGH) | |
370 | printf ("%cfall", c), c = ','; | |
371 | printf (")"); | |
372 | } | |
4977bab6 ZW |
373 | } |
374 | } | |
4977bab6 ZW |
375 | } |
376 | ||
94de45d9 | 377 | static void |
1d088dee | 378 | tag_lines (const char *filename ATTRIBUTE_UNUSED, |
5c5059bc ML |
379 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED, |
380 | unsigned depth) | |
4977bab6 ZW |
381 | { |
382 | if (flag_dump_contents) | |
383 | { | |
94de45d9 | 384 | unsigned blockno = gcov_read_unsigned (); |
4977bab6 ZW |
385 | char const *sep = NULL; |
386 | ||
4977bab6 ZW |
387 | while (1) |
388 | { | |
7d63a2fa | 389 | gcov_position_t position = gcov_position (); |
94de45d9 NS |
390 | const char *source = NULL; |
391 | unsigned lineno = gcov_read_unsigned (); | |
1d088dee | 392 | |
4977bab6 ZW |
393 | if (!lineno) |
394 | { | |
94de45d9 | 395 | source = gcov_read_string (); |
4977bab6 ZW |
396 | if (!source) |
397 | break; | |
398 | sep = NULL; | |
399 | } | |
1d088dee | 400 | |
4977bab6 ZW |
401 | if (!sep) |
402 | { | |
7d63a2fa | 403 | printf ("\n"); |
5c5059bc ML |
404 | print_prefix (filename, depth, position); |
405 | printf (VALUE_PADDING_PREFIX "block %u:", blockno); | |
4977bab6 ZW |
406 | sep = ""; |
407 | } | |
408 | if (lineno) | |
409 | { | |
410 | printf ("%s%u", sep, lineno); | |
411 | sep = ", "; | |
412 | } | |
413 | else | |
414 | { | |
415 | printf ("%s`%s'", sep, source); | |
416 | sep = ":"; | |
417 | } | |
418 | } | |
419 | } | |
4977bab6 ZW |
420 | } |
421 | ||
94de45d9 | 422 | static void |
1d088dee | 423 | tag_counters (const char *filename ATTRIBUTE_UNUSED, |
5c5059bc ML |
424 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED, |
425 | unsigned depth) | |
4977bab6 | 426 | { |
c77556a5 RX |
427 | #define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) NAME, |
428 | static const char *const counter_names[] = { | |
429 | #include "gcov-counter.def" | |
430 | }; | |
431 | #undef DEF_GCOV_COUNTER | |
330d2e2a | 432 | unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); |
1d088dee | 433 | |
cdb23767 NS |
434 | printf (" %s %u counts", |
435 | counter_names[GCOV_COUNTER_FOR_TAG (tag)], n_counts); | |
4977bab6 ZW |
436 | if (flag_dump_contents) |
437 | { | |
438 | unsigned ix; | |
439 | ||
440 | for (ix = 0; ix != n_counts; ix++) | |
441 | { | |
7d63a2fa | 442 | gcov_type count; |
1d088dee | 443 | |
4977bab6 | 444 | if (!(ix & 7)) |
7d63a2fa NS |
445 | { |
446 | printf ("\n"); | |
5c5059bc ML |
447 | print_prefix (filename, depth, gcov_position ()); |
448 | printf (VALUE_PADDING_PREFIX VALUE_PREFIX, ix); | |
7d63a2fa | 449 | } |
1d088dee | 450 | |
7d63a2fa | 451 | count = gcov_read_counter (); |
5c5059bc | 452 | printf ("%" PRId64 " ", count); |
4977bab6 ZW |
453 | } |
454 | } | |
4977bab6 ZW |
455 | } |
456 | ||
94de45d9 | 457 | static void |
1d088dee | 458 | tag_summary (const char *filename ATTRIBUTE_UNUSED, |
5c5059bc | 459 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED, |
512cc015 | 460 | unsigned depth ATTRIBUTE_UNUSED) |
4977bab6 | 461 | { |
7f3577f5 | 462 | gcov_summary summary; |
cdb23767 | 463 | gcov_read_summary (&summary); |
512cc015 ML |
464 | printf (" runs=%d, sum_max=%" PRId64, |
465 | summary.runs, summary.sum_max); | |
4977bab6 | 466 | } |