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