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