]>
Commit | Line | Data |
---|---|---|
805e22b2 | 1 | /* Dump a gcov file, for debugging use. |
5a371c77 | 2 | Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 |
e46a4556 | 3 | Free Software Foundation, Inc. |
805e22b2 | 4 | Contributed by Nathan Sidwell <nathan@codesourcery.com> |
5 | ||
6 | Gcov is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8c4c00c1 | 8 | the Free Software Foundation; either version 3, or (at your option) |
805e22b2 | 9 | any later version. |
10 | ||
11 | Gcov is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
8c4c00c1 | 17 | along with Gcov; see the file COPYING3. If not see |
18 | <http://www.gnu.org/licenses/>. */ | |
805e22b2 | 19 | |
20 | #include "config.h" | |
21 | #include "system.h" | |
22 | #include "coretypes.h" | |
23 | #include "tm.h" | |
24 | #include "version.h" | |
25 | #include <getopt.h> | |
9219dd0a | 26 | #define IN_GCOV (-1) |
805e22b2 | 27 | #include "gcov-io.h" |
44359ced | 28 | #include "gcov-io.c" |
805e22b2 | 29 | |
952f0048 | 30 | static void dump_file (const char *); |
31 | static void print_prefix (const char *, unsigned, gcov_position_t); | |
32 | static void print_usage (void); | |
33 | static void print_version (void); | |
34 | static void tag_function (const char *, unsigned, unsigned); | |
35 | static void tag_blocks (const char *, unsigned, unsigned); | |
36 | static void tag_arcs (const char *, unsigned, unsigned); | |
37 | static void tag_lines (const char *, unsigned, unsigned); | |
38 | static void tag_counters (const char *, unsigned, unsigned); | |
39 | static void tag_summary (const char *, unsigned, unsigned); | |
40 | extern int main (int, char **); | |
805e22b2 | 41 | |
42 | typedef struct tag_format | |
43 | { | |
44 | unsigned tag; | |
45 | char const *name; | |
f187042d | 46 | void (*proc) (const char *, unsigned, unsigned); |
805e22b2 | 47 | } tag_format_t; |
48 | ||
49 | static int flag_dump_contents = 0; | |
80abd9e4 | 50 | static int flag_dump_positions = 0; |
805e22b2 | 51 | |
52 | static const struct option options[] = | |
53 | { | |
54 | { "help", no_argument, NULL, 'h' }, | |
55 | { "version", no_argument, NULL, 'v' }, | |
56 | { "long", no_argument, NULL, 'l' }, | |
80abd9e4 | 57 | { "positions", no_argument, NULL, 'o' }, |
4eb7b61f | 58 | { 0, 0, 0, 0 } |
805e22b2 | 59 | }; |
60 | ||
fd956ee4 | 61 | static const tag_format_t tag_table[] = |
805e22b2 | 62 | { |
63 | {0, "NOP", NULL}, | |
64 | {0, "UNKNOWN", NULL}, | |
ab6a34f2 | 65 | {0, "COUNTERS", tag_counters}, |
805e22b2 | 66 | {GCOV_TAG_FUNCTION, "FUNCTION", tag_function}, |
67 | {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks}, | |
68 | {GCOV_TAG_ARCS, "ARCS", tag_arcs}, | |
69 | {GCOV_TAG_LINES, "LINES", tag_lines}, | |
805e22b2 | 70 | {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary}, |
71 | {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary}, | |
805e22b2 | 72 | {0, NULL, NULL} |
73 | }; | |
74 | ||
952f0048 | 75 | int |
76 | main (int argc ATTRIBUTE_UNUSED, char **argv) | |
805e22b2 | 77 | { |
78 | int opt; | |
79 | ||
4367c81f | 80 | /* Unlock the stdio streams. */ |
9c8f076b | 81 | unlock_std_streams (); |
4367c81f | 82 | |
80abd9e4 | 83 | while ((opt = getopt_long (argc, argv, "hlpv", options, NULL)) != -1) |
805e22b2 | 84 | { |
85 | switch (opt) | |
86 | { | |
87 | case 'h': | |
88 | print_usage (); | |
89 | break; | |
90 | case 'v': | |
91 | print_version (); | |
92 | break; | |
93 | case 'l': | |
94 | flag_dump_contents = 1; | |
95 | break; | |
80abd9e4 | 96 | case 'p': |
97 | flag_dump_positions = 1; | |
98 | break; | |
805e22b2 | 99 | default: |
100 | fprintf (stderr, "unknown flag `%c'\n", opt); | |
101 | } | |
102 | } | |
952f0048 | 103 | |
805e22b2 | 104 | while (argv[optind]) |
105 | dump_file (argv[optind++]); | |
106 | return 0; | |
107 | } | |
108 | ||
109 | static void | |
952f0048 | 110 | print_usage (void) |
805e22b2 | 111 | { |
112 | printf ("Usage: gcov-dump [OPTION] ... gcovfiles\n"); | |
113 | printf ("Print coverage file contents\n"); | |
114 | printf (" -h, --help Print this help\n"); | |
115 | printf (" -v, --version Print version number\n"); | |
116 | printf (" -l, --long Dump record contents too\n"); | |
80abd9e4 | 117 | printf (" -p, --positions Dump record positions\n"); |
805e22b2 | 118 | } |
119 | ||
120 | static void | |
952f0048 | 121 | print_version (void) |
805e22b2 | 122 | { |
d25dc80e | 123 | printf ("gcov-dump %s%s\n", pkgversion_string, version_string); |
5a371c77 | 124 | printf ("Copyright (C) 2009 Free Software Foundation, Inc.\n"); |
9e0943d4 | 125 | printf ("This is free software; see the source for copying conditions.\n" |
126 | "There is NO warranty; not even for MERCHANTABILITY or \n" | |
127 | "FITNESS FOR A PARTICULAR PURPOSE.\n\n"); | |
805e22b2 | 128 | } |
129 | ||
130 | static void | |
952f0048 | 131 | print_prefix (const char *filename, unsigned depth, gcov_position_t position) |
805e22b2 | 132 | { |
133 | static const char prefix[] = " "; | |
952f0048 | 134 | |
80abd9e4 | 135 | printf ("%s:", filename); |
136 | if (flag_dump_positions) | |
137 | printf ("%lu:", (unsigned long) position); | |
138 | printf ("%.*s", (int) depth, prefix); | |
805e22b2 | 139 | } |
140 | ||
141 | static void | |
952f0048 | 142 | dump_file (const char *filename) |
805e22b2 | 143 | { |
805e22b2 | 144 | unsigned tags[4]; |
145 | unsigned depth = 0; | |
952f0048 | 146 | |
9219dd0a | 147 | if (!gcov_open (filename, 1)) |
805e22b2 | 148 | { |
149 | fprintf (stderr, "%s:cannot open\n", filename); | |
150 | return; | |
151 | } | |
952f0048 | 152 | |
805e22b2 | 153 | /* magic */ |
154 | { | |
f187042d | 155 | unsigned magic = gcov_read_unsigned (); |
b4d48d67 | 156 | unsigned version; |
805e22b2 | 157 | const char *type = NULL; |
b4d48d67 | 158 | int endianness = 0; |
9e0943d4 | 159 | char m[4], v[4]; |
b4d48d67 | 160 | |
161 | if ((endianness = gcov_magic (magic, GCOV_DATA_MAGIC))) | |
805e22b2 | 162 | type = "data"; |
b4d48d67 | 163 | else if ((endianness = gcov_magic (magic, GCOV_NOTE_MAGIC))) |
164 | type = "note"; | |
805e22b2 | 165 | else |
166 | { | |
167 | printf ("%s:not a gcov file\n", filename); | |
9219dd0a | 168 | gcov_close (); |
805e22b2 | 169 | return; |
170 | } | |
b4d48d67 | 171 | version = gcov_read_unsigned (); |
9e0943d4 | 172 | GCOV_UNSIGNED2STRING (v, version); |
173 | GCOV_UNSIGNED2STRING (m, magic); | |
b4d48d67 | 174 | |
175 | printf ("%s:%s:magic `%.4s':version `%.4s'%s\n", filename, type, | |
9e0943d4 | 176 | m, v, endianness < 0 ? " (swapped endianness)" : ""); |
b4d48d67 | 177 | if (version != GCOV_VERSION) |
805e22b2 | 178 | { |
9e0943d4 | 179 | char e[4]; |
b4d48d67 | 180 | |
9e0943d4 | 181 | GCOV_UNSIGNED2STRING (e, GCOV_VERSION); |
182 | printf ("%s:warning:current version is `%.4s'\n", filename, e); | |
805e22b2 | 183 | } |
805e22b2 | 184 | } |
185 | ||
42fa384f | 186 | /* stamp */ |
187 | { | |
188 | unsigned stamp = gcov_read_unsigned (); | |
189 | ||
190 | printf ("%s:stamp %lu\n", filename, (unsigned long)stamp); | |
191 | } | |
192 | ||
80abd9e4 | 193 | while (1) |
805e22b2 | 194 | { |
80abd9e4 | 195 | gcov_position_t base, position = gcov_position (); |
196 | unsigned tag, length; | |
805e22b2 | 197 | tag_format_t const *format; |
198 | unsigned tag_depth; | |
f187042d | 199 | int error; |
80abd9e4 | 200 | unsigned mask; |
201 | ||
202 | tag = gcov_read_unsigned (); | |
805e22b2 | 203 | if (!tag) |
80abd9e4 | 204 | break; |
205 | length = gcov_read_unsigned (); | |
206 | base = gcov_position (); | |
207 | mask = GCOV_TAG_MASK (tag) >> 1; | |
208 | for (tag_depth = 4; mask; mask >>= 8) | |
805e22b2 | 209 | { |
80abd9e4 | 210 | if ((mask & 0xff) != 0xff) |
805e22b2 | 211 | { |
80abd9e4 | 212 | printf ("%s:tag `%08x' is invalid\n", filename, tag); |
213 | break; | |
805e22b2 | 214 | } |
80abd9e4 | 215 | tag_depth--; |
805e22b2 | 216 | } |
217 | for (format = tag_table; format->name; format++) | |
218 | if (format->tag == tag) | |
219 | goto found; | |
ab6a34f2 | 220 | format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1]; |
805e22b2 | 221 | found:; |
222 | if (tag) | |
223 | { | |
224 | if (depth && depth < tag_depth) | |
225 | { | |
226 | if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag)) | |
227 | printf ("%s:tag `%08x' is incorrectly nested\n", | |
228 | filename, tag); | |
229 | } | |
230 | depth = tag_depth; | |
231 | tags[depth - 1] = tag; | |
232 | } | |
952f0048 | 233 | |
80abd9e4 | 234 | print_prefix (filename, tag_depth, position); |
805e22b2 | 235 | printf ("%08x:%4u:%s", tag, length, format->name); |
236 | if (format->proc) | |
f187042d | 237 | (*format->proc) (filename, tag, length); |
952f0048 | 238 | |
805e22b2 | 239 | printf ("\n"); |
f187042d | 240 | if (flag_dump_contents && format->proc) |
805e22b2 | 241 | { |
f187042d | 242 | unsigned long actual_length = gcov_position () - base; |
952f0048 | 243 | |
f187042d | 244 | if (actual_length > length) |
805e22b2 | 245 | printf ("%s:record size mismatch %lu bytes overread\n", |
f187042d | 246 | filename, actual_length - length); |
247 | else if (length > actual_length) | |
805e22b2 | 248 | printf ("%s:record size mismatch %lu bytes unread\n", |
f187042d | 249 | filename, length - actual_length); |
250 | } | |
3ddf7676 | 251 | gcov_sync (base, length); |
f187042d | 252 | if ((error = gcov_is_error ())) |
253 | { | |
254 | printf (error < 0 ? "%s:counter overflow at %lu\n" : | |
834f169c | 255 | "%s:read error at %lu\n", filename, |
256 | (long unsigned) gcov_position ()); | |
f187042d | 257 | break; |
805e22b2 | 258 | } |
259 | } | |
9219dd0a | 260 | gcov_close (); |
805e22b2 | 261 | } |
262 | ||
f187042d | 263 | static void |
952f0048 | 264 | tag_function (const char *filename ATTRIBUTE_UNUSED, |
265 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) | |
805e22b2 | 266 | { |
f187042d | 267 | unsigned long pos = gcov_position (); |
952f0048 | 268 | |
3f2c2dd8 | 269 | printf (" ident=%u", gcov_read_unsigned ()); |
270 | printf (", checksum=0x%08x", gcov_read_unsigned ()); | |
805e22b2 | 271 | |
f187042d | 272 | if (gcov_position () - pos < length) |
273 | { | |
3f2c2dd8 | 274 | const char *name; |
952f0048 | 275 | |
3f2c2dd8 | 276 | name = gcov_read_string (); |
277 | printf (", `%s'", name ? name : "NULL"); | |
f187042d | 278 | name = gcov_read_string (); |
279 | printf (" %s", name ? name : "NULL"); | |
280 | printf (":%u", gcov_read_unsigned ()); | |
281 | } | |
805e22b2 | 282 | } |
283 | ||
f187042d | 284 | static void |
952f0048 | 285 | tag_blocks (const char *filename ATTRIBUTE_UNUSED, |
286 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) | |
805e22b2 | 287 | { |
9e0943d4 | 288 | unsigned n_blocks = GCOV_TAG_BLOCKS_NUM (length); |
952f0048 | 289 | |
805e22b2 | 290 | printf (" %u blocks", n_blocks); |
291 | ||
292 | if (flag_dump_contents) | |
293 | { | |
294 | unsigned ix; | |
295 | ||
296 | for (ix = 0; ix != n_blocks; ix++) | |
297 | { | |
805e22b2 | 298 | if (!(ix & 7)) |
80abd9e4 | 299 | { |
300 | printf ("\n"); | |
301 | print_prefix (filename, 0, gcov_position ()); | |
302 | printf ("\t\t%u", ix); | |
303 | } | |
f187042d | 304 | printf (" %04x", gcov_read_unsigned ()); |
805e22b2 | 305 | } |
805e22b2 | 306 | } |
805e22b2 | 307 | } |
308 | ||
f187042d | 309 | static void |
952f0048 | 310 | tag_arcs (const char *filename ATTRIBUTE_UNUSED, |
311 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) | |
805e22b2 | 312 | { |
9e0943d4 | 313 | unsigned n_arcs = GCOV_TAG_ARCS_NUM (length); |
805e22b2 | 314 | |
315 | printf (" %u arcs", n_arcs); | |
316 | if (flag_dump_contents) | |
317 | { | |
318 | unsigned ix; | |
f187042d | 319 | unsigned blockno = gcov_read_unsigned (); |
805e22b2 | 320 | |
321 | for (ix = 0; ix != n_arcs; ix++) | |
322 | { | |
80abd9e4 | 323 | unsigned dst, flags; |
952f0048 | 324 | |
805e22b2 | 325 | if (!(ix & 3)) |
80abd9e4 | 326 | { |
327 | printf ("\n"); | |
328 | print_prefix (filename, 0, gcov_position ()); | |
329 | printf ("\tblock %u:", blockno); | |
330 | } | |
331 | dst = gcov_read_unsigned (); | |
332 | flags = gcov_read_unsigned (); | |
805e22b2 | 333 | printf (" %u:%04x", dst, flags); |
334 | } | |
335 | } | |
805e22b2 | 336 | } |
337 | ||
f187042d | 338 | static void |
952f0048 | 339 | tag_lines (const char *filename ATTRIBUTE_UNUSED, |
340 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) | |
805e22b2 | 341 | { |
342 | if (flag_dump_contents) | |
343 | { | |
f187042d | 344 | unsigned blockno = gcov_read_unsigned (); |
805e22b2 | 345 | char const *sep = NULL; |
346 | ||
805e22b2 | 347 | while (1) |
348 | { | |
80abd9e4 | 349 | gcov_position_t position = gcov_position (); |
f187042d | 350 | const char *source = NULL; |
351 | unsigned lineno = gcov_read_unsigned (); | |
952f0048 | 352 | |
805e22b2 | 353 | if (!lineno) |
354 | { | |
f187042d | 355 | source = gcov_read_string (); |
805e22b2 | 356 | if (!source) |
357 | break; | |
358 | sep = NULL; | |
359 | } | |
952f0048 | 360 | |
805e22b2 | 361 | if (!sep) |
362 | { | |
80abd9e4 | 363 | printf ("\n"); |
364 | print_prefix (filename, 0, position); | |
365 | printf ("\tblock %u:", blockno); | |
805e22b2 | 366 | sep = ""; |
367 | } | |
368 | if (lineno) | |
369 | { | |
370 | printf ("%s%u", sep, lineno); | |
371 | sep = ", "; | |
372 | } | |
373 | else | |
374 | { | |
375 | printf ("%s`%s'", sep, source); | |
376 | sep = ":"; | |
377 | } | |
378 | } | |
379 | } | |
805e22b2 | 380 | } |
381 | ||
f187042d | 382 | static void |
952f0048 | 383 | tag_counters (const char *filename ATTRIBUTE_UNUSED, |
384 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) | |
805e22b2 | 385 | { |
ab6a34f2 | 386 | static const char *const counter_names[] = GCOV_COUNTER_NAMES; |
9e0943d4 | 387 | unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); |
952f0048 | 388 | |
ab6a34f2 | 389 | printf (" %s %u counts", |
390 | counter_names[GCOV_COUNTER_FOR_TAG (tag)], n_counts); | |
805e22b2 | 391 | if (flag_dump_contents) |
392 | { | |
393 | unsigned ix; | |
394 | ||
395 | for (ix = 0; ix != n_counts; ix++) | |
396 | { | |
80abd9e4 | 397 | gcov_type count; |
952f0048 | 398 | |
805e22b2 | 399 | if (!(ix & 7)) |
80abd9e4 | 400 | { |
401 | printf ("\n"); | |
402 | print_prefix (filename, 0, gcov_position ()); | |
403 | printf ("\t\t%u", ix); | |
404 | } | |
952f0048 | 405 | |
80abd9e4 | 406 | count = gcov_read_counter (); |
805e22b2 | 407 | printf (" "); |
408 | printf (HOST_WIDEST_INT_PRINT_DEC, count); | |
409 | } | |
410 | } | |
805e22b2 | 411 | } |
412 | ||
f187042d | 413 | static void |
952f0048 | 414 | tag_summary (const char *filename ATTRIBUTE_UNUSED, |
415 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) | |
805e22b2 | 416 | { |
417 | struct gcov_summary summary; | |
ab6a34f2 | 418 | unsigned ix; |
952f0048 | 419 | |
ab6a34f2 | 420 | gcov_read_summary (&summary); |
805e22b2 | 421 | printf (" checksum=0x%08x", summary.checksum); |
952f0048 | 422 | |
44d32dba | 423 | for (ix = 0; ix != GCOV_COUNTERS_SUMMABLE; ix++) |
ab6a34f2 | 424 | { |
80abd9e4 | 425 | printf ("\n"); |
426 | print_prefix (filename, 0, 0); | |
427 | printf ("\t\tcounts=%u, runs=%u", | |
ab6a34f2 | 428 | summary.ctrs[ix].num, summary.ctrs[ix].runs); |
952f0048 | 429 | |
ab6a34f2 | 430 | printf (", sum_all=" HOST_WIDEST_INT_PRINT_DEC, |
431 | (HOST_WIDEST_INT)summary.ctrs[ix].sum_all); | |
432 | printf (", run_max=" HOST_WIDEST_INT_PRINT_DEC, | |
433 | (HOST_WIDEST_INT)summary.ctrs[ix].run_max); | |
434 | printf (", sum_max=" HOST_WIDEST_INT_PRINT_DEC, | |
435 | (HOST_WIDEST_INT)summary.ctrs[ix].sum_max); | |
436 | } | |
805e22b2 | 437 | } |