]>
Commit | Line | Data |
---|---|---|
4977bab6 | 1 | /* Dump a gcov file, for debugging use. |
1d088dee | 2 | Copyright (C) 2002, 2003 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 | |
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> | |
546d2adb | 26 | #define IN_GCOV (-1) |
4977bab6 | 27 | #include "gcov-io.h" |
ca29da43 | 28 | #include "gcov-io.c" |
4977bab6 | 29 | |
1d088dee AJ |
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 **); | |
4977bab6 ZW |
41 | |
42 | typedef struct tag_format | |
43 | { | |
44 | unsigned tag; | |
45 | char const *name; | |
94de45d9 | 46 | void (*proc) (const char *, unsigned, unsigned); |
4977bab6 ZW |
47 | } tag_format_t; |
48 | ||
49 | static int flag_dump_contents = 0; | |
7d63a2fa | 50 | static int flag_dump_positions = 0; |
4977bab6 ZW |
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' }, | |
7d63a2fa | 57 | { "positions", no_argument, NULL, 'o' }, |
d90f9882 | 58 | { 0, 0, 0, 0 } |
4977bab6 ZW |
59 | }; |
60 | ||
83fa8d7a | 61 | static const tag_format_t tag_table[] = |
4977bab6 ZW |
62 | { |
63 | {0, "NOP", NULL}, | |
64 | {0, "UNKNOWN", NULL}, | |
cdb23767 | 65 | {0, "COUNTERS", tag_counters}, |
4977bab6 ZW |
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}, | |
4977bab6 ZW |
70 | {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary}, |
71 | {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary}, | |
4977bab6 ZW |
72 | {0, NULL, NULL} |
73 | }; | |
74 | ||
1d088dee AJ |
75 | int |
76 | main (int argc ATTRIBUTE_UNUSED, char **argv) | |
4977bab6 ZW |
77 | { |
78 | int opt; | |
79 | ||
7d63a2fa | 80 | while ((opt = getopt_long (argc, argv, "hlpv", options, NULL)) != -1) |
4977bab6 ZW |
81 | { |
82 | switch (opt) | |
83 | { | |
84 | case 'h': | |
85 | print_usage (); | |
86 | break; | |
87 | case 'v': | |
88 | print_version (); | |
89 | break; | |
90 | case 'l': | |
91 | flag_dump_contents = 1; | |
92 | break; | |
7d63a2fa NS |
93 | case 'p': |
94 | flag_dump_positions = 1; | |
95 | break; | |
4977bab6 ZW |
96 | default: |
97 | fprintf (stderr, "unknown flag `%c'\n", opt); | |
98 | } | |
99 | } | |
1d088dee | 100 | |
4977bab6 ZW |
101 | while (argv[optind]) |
102 | dump_file (argv[optind++]); | |
103 | return 0; | |
104 | } | |
105 | ||
106 | static void | |
1d088dee | 107 | print_usage (void) |
4977bab6 ZW |
108 | { |
109 | printf ("Usage: gcov-dump [OPTION] ... gcovfiles\n"); | |
110 | printf ("Print coverage file contents\n"); | |
111 | printf (" -h, --help Print this help\n"); | |
112 | printf (" -v, --version Print version number\n"); | |
113 | printf (" -l, --long Dump record contents too\n"); | |
7d63a2fa | 114 | printf (" -p, --positions Dump record positions\n"); |
4977bab6 ZW |
115 | } |
116 | ||
117 | static void | |
1d088dee | 118 | print_version (void) |
4977bab6 | 119 | { |
330d2e2a NS |
120 | printf ("gcov-dump (GCC) %s\n", version_string); |
121 | printf ("Copyright (C) 2003 Free Software Foundation, Inc.\n"); | |
122 | printf ("This is free software; see the source for copying conditions.\n" | |
123 | "There is NO warranty; not even for MERCHANTABILITY or \n" | |
124 | "FITNESS FOR A PARTICULAR PURPOSE.\n\n"); | |
4977bab6 ZW |
125 | } |
126 | ||
127 | static void | |
1d088dee | 128 | print_prefix (const char *filename, unsigned depth, gcov_position_t position) |
4977bab6 ZW |
129 | { |
130 | static const char prefix[] = " "; | |
1d088dee | 131 | |
7d63a2fa NS |
132 | printf ("%s:", filename); |
133 | if (flag_dump_positions) | |
134 | printf ("%lu:", (unsigned long) position); | |
135 | printf ("%.*s", (int) depth, prefix); | |
4977bab6 ZW |
136 | } |
137 | ||
138 | static void | |
1d088dee | 139 | dump_file (const char *filename) |
4977bab6 | 140 | { |
4977bab6 ZW |
141 | unsigned tags[4]; |
142 | unsigned depth = 0; | |
1d088dee | 143 | |
546d2adb | 144 | if (!gcov_open (filename, 1)) |
4977bab6 ZW |
145 | { |
146 | fprintf (stderr, "%s:cannot open\n", filename); | |
147 | return; | |
148 | } | |
1d088dee | 149 | |
4977bab6 ZW |
150 | /* magic */ |
151 | { | |
94de45d9 | 152 | unsigned magic = gcov_read_unsigned (); |
160e2e4f | 153 | unsigned version; |
4977bab6 | 154 | const char *type = NULL; |
160e2e4f | 155 | int endianness = 0; |
330d2e2a | 156 | char m[4], v[4]; |
160e2e4f NS |
157 | |
158 | if ((endianness = gcov_magic (magic, GCOV_DATA_MAGIC))) | |
4977bab6 | 159 | type = "data"; |
160e2e4f NS |
160 | else if ((endianness = gcov_magic (magic, GCOV_NOTE_MAGIC))) |
161 | type = "note"; | |
4977bab6 ZW |
162 | else |
163 | { | |
164 | printf ("%s:not a gcov file\n", filename); | |
546d2adb | 165 | gcov_close (); |
4977bab6 ZW |
166 | return; |
167 | } | |
160e2e4f | 168 | version = gcov_read_unsigned (); |
330d2e2a NS |
169 | GCOV_UNSIGNED2STRING (v, version); |
170 | GCOV_UNSIGNED2STRING (m, magic); | |
160e2e4f NS |
171 | |
172 | printf ("%s:%s:magic `%.4s':version `%.4s'%s\n", filename, type, | |
330d2e2a | 173 | m, v, endianness < 0 ? " (swapped endianness)" : ""); |
160e2e4f | 174 | if (version != GCOV_VERSION) |
4977bab6 | 175 | { |
330d2e2a | 176 | char e[4]; |
160e2e4f | 177 | |
330d2e2a NS |
178 | GCOV_UNSIGNED2STRING (e, GCOV_VERSION); |
179 | printf ("%s:warning:current version is `%.4s'\n", filename, e); | |
4977bab6 | 180 | } |
4977bab6 ZW |
181 | } |
182 | ||
dd486eb2 NS |
183 | /* stamp */ |
184 | { | |
185 | unsigned stamp = gcov_read_unsigned (); | |
186 | ||
187 | printf ("%s:stamp %lu\n", filename, (unsigned long)stamp); | |
188 | } | |
189 | ||
7d63a2fa | 190 | while (1) |
4977bab6 | 191 | { |
7d63a2fa NS |
192 | gcov_position_t base, position = gcov_position (); |
193 | unsigned tag, length; | |
4977bab6 ZW |
194 | tag_format_t const *format; |
195 | unsigned tag_depth; | |
94de45d9 | 196 | int error; |
7d63a2fa NS |
197 | unsigned mask; |
198 | ||
199 | tag = gcov_read_unsigned (); | |
4977bab6 | 200 | if (!tag) |
7d63a2fa NS |
201 | break; |
202 | length = gcov_read_unsigned (); | |
203 | base = gcov_position (); | |
204 | mask = GCOV_TAG_MASK (tag) >> 1; | |
205 | for (tag_depth = 4; mask; mask >>= 8) | |
4977bab6 | 206 | { |
7d63a2fa | 207 | if ((mask & 0xff) != 0xff) |
4977bab6 | 208 | { |
7d63a2fa NS |
209 | printf ("%s:tag `%08x' is invalid\n", filename, tag); |
210 | break; | |
4977bab6 | 211 | } |
7d63a2fa | 212 | tag_depth--; |
4977bab6 ZW |
213 | } |
214 | for (format = tag_table; format->name; format++) | |
215 | if (format->tag == tag) | |
216 | goto found; | |
cdb23767 | 217 | format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1]; |
4977bab6 ZW |
218 | found:; |
219 | if (tag) | |
220 | { | |
221 | if (depth && depth < tag_depth) | |
222 | { | |
223 | if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag)) | |
224 | printf ("%s:tag `%08x' is incorrectly nested\n", | |
225 | filename, tag); | |
226 | } | |
227 | depth = tag_depth; | |
228 | tags[depth - 1] = tag; | |
229 | } | |
1d088dee | 230 | |
7d63a2fa | 231 | print_prefix (filename, tag_depth, position); |
4977bab6 ZW |
232 | printf ("%08x:%4u:%s", tag, length, format->name); |
233 | if (format->proc) | |
94de45d9 | 234 | (*format->proc) (filename, tag, length); |
1d088dee | 235 | |
4977bab6 | 236 | printf ("\n"); |
94de45d9 | 237 | if (flag_dump_contents && format->proc) |
4977bab6 | 238 | { |
94de45d9 | 239 | unsigned long actual_length = gcov_position () - base; |
1d088dee | 240 | |
94de45d9 | 241 | if (actual_length > length) |
4977bab6 | 242 | printf ("%s:record size mismatch %lu bytes overread\n", |
94de45d9 NS |
243 | filename, actual_length - length); |
244 | else if (length > actual_length) | |
4977bab6 | 245 | printf ("%s:record size mismatch %lu bytes unread\n", |
94de45d9 NS |
246 | filename, length - actual_length); |
247 | } | |
474f141e | 248 | gcov_sync (base, length); |
94de45d9 NS |
249 | if ((error = gcov_is_error ())) |
250 | { | |
251 | printf (error < 0 ? "%s:counter overflow at %lu\n" : | |
9b514d25 NS |
252 | "%s:read error at %lu\n", filename, |
253 | (long unsigned) gcov_position ()); | |
94de45d9 | 254 | break; |
4977bab6 ZW |
255 | } |
256 | } | |
7d63a2fa NS |
257 | if (!gcov_is_eof ()) |
258 | printf ("%s:early end of file\n", filename); | |
546d2adb | 259 | gcov_close (); |
4977bab6 ZW |
260 | } |
261 | ||
94de45d9 | 262 | static void |
1d088dee AJ |
263 | tag_function (const char *filename ATTRIBUTE_UNUSED, |
264 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) | |
4977bab6 | 265 | { |
94de45d9 | 266 | unsigned long pos = gcov_position (); |
1d088dee | 267 | |
796621e8 NS |
268 | printf (" ident=%u", gcov_read_unsigned ()); |
269 | printf (", checksum=0x%08x", gcov_read_unsigned ()); | |
4977bab6 | 270 | |
94de45d9 NS |
271 | if (gcov_position () - pos < length) |
272 | { | |
796621e8 | 273 | const char *name; |
1d088dee | 274 | |
796621e8 NS |
275 | name = gcov_read_string (); |
276 | printf (", `%s'", name ? name : "NULL"); | |
94de45d9 NS |
277 | name = gcov_read_string (); |
278 | printf (" %s", name ? name : "NULL"); | |
279 | printf (":%u", gcov_read_unsigned ()); | |
280 | } | |
4977bab6 ZW |
281 | } |
282 | ||
94de45d9 | 283 | static void |
1d088dee AJ |
284 | tag_blocks (const char *filename ATTRIBUTE_UNUSED, |
285 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) | |
4977bab6 | 286 | { |
330d2e2a | 287 | unsigned n_blocks = GCOV_TAG_BLOCKS_NUM (length); |
1d088dee | 288 | |
4977bab6 ZW |
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 | { | |
4977bab6 | 297 | if (!(ix & 7)) |
7d63a2fa NS |
298 | { |
299 | printf ("\n"); | |
300 | print_prefix (filename, 0, gcov_position ()); | |
301 | printf ("\t\t%u", ix); | |
302 | } | |
94de45d9 | 303 | printf (" %04x", gcov_read_unsigned ()); |
4977bab6 | 304 | } |
4977bab6 | 305 | } |
4977bab6 ZW |
306 | } |
307 | ||
94de45d9 | 308 | static void |
1d088dee AJ |
309 | tag_arcs (const char *filename ATTRIBUTE_UNUSED, |
310 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) | |
4977bab6 | 311 | { |
330d2e2a | 312 | unsigned n_arcs = GCOV_TAG_ARCS_NUM (length); |
4977bab6 ZW |
313 | |
314 | printf (" %u arcs", n_arcs); | |
315 | if (flag_dump_contents) | |
316 | { | |
317 | unsigned ix; | |
94de45d9 | 318 | unsigned blockno = gcov_read_unsigned (); |
4977bab6 ZW |
319 | |
320 | for (ix = 0; ix != n_arcs; ix++) | |
321 | { | |
7d63a2fa | 322 | unsigned dst, flags; |
1d088dee | 323 | |
4977bab6 | 324 | if (!(ix & 3)) |
7d63a2fa NS |
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 (); | |
4977bab6 ZW |
332 | printf (" %u:%04x", dst, flags); |
333 | } | |
334 | } | |
4977bab6 ZW |
335 | } |
336 | ||
94de45d9 | 337 | static void |
1d088dee AJ |
338 | tag_lines (const char *filename ATTRIBUTE_UNUSED, |
339 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) | |
4977bab6 ZW |
340 | { |
341 | if (flag_dump_contents) | |
342 | { | |
94de45d9 | 343 | unsigned blockno = gcov_read_unsigned (); |
4977bab6 ZW |
344 | char const *sep = NULL; |
345 | ||
4977bab6 ZW |
346 | while (1) |
347 | { | |
7d63a2fa | 348 | gcov_position_t position = gcov_position (); |
94de45d9 NS |
349 | const char *source = NULL; |
350 | unsigned lineno = gcov_read_unsigned (); | |
1d088dee | 351 | |
4977bab6 ZW |
352 | if (!lineno) |
353 | { | |
94de45d9 | 354 | source = gcov_read_string (); |
4977bab6 ZW |
355 | if (!source) |
356 | break; | |
357 | sep = NULL; | |
358 | } | |
1d088dee | 359 | |
4977bab6 ZW |
360 | if (!sep) |
361 | { | |
7d63a2fa NS |
362 | printf ("\n"); |
363 | print_prefix (filename, 0, position); | |
364 | printf ("\tblock %u:", blockno); | |
4977bab6 ZW |
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 | } | |
4977bab6 ZW |
379 | } |
380 | ||
94de45d9 | 381 | static void |
1d088dee AJ |
382 | tag_counters (const char *filename ATTRIBUTE_UNUSED, |
383 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) | |
4977bab6 | 384 | { |
cdb23767 | 385 | static const char *const counter_names[] = GCOV_COUNTER_NAMES; |
330d2e2a | 386 | unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); |
1d088dee | 387 | |
cdb23767 NS |
388 | printf (" %s %u counts", |
389 | counter_names[GCOV_COUNTER_FOR_TAG (tag)], n_counts); | |
4977bab6 ZW |
390 | if (flag_dump_contents) |
391 | { | |
392 | unsigned ix; | |
393 | ||
394 | for (ix = 0; ix != n_counts; ix++) | |
395 | { | |
7d63a2fa | 396 | gcov_type count; |
1d088dee | 397 | |
4977bab6 | 398 | if (!(ix & 7)) |
7d63a2fa NS |
399 | { |
400 | printf ("\n"); | |
401 | print_prefix (filename, 0, gcov_position ()); | |
402 | printf ("\t\t%u", ix); | |
403 | } | |
1d088dee | 404 | |
7d63a2fa | 405 | count = gcov_read_counter (); |
4977bab6 ZW |
406 | printf (" "); |
407 | printf (HOST_WIDEST_INT_PRINT_DEC, count); | |
408 | } | |
409 | } | |
4977bab6 ZW |
410 | } |
411 | ||
94de45d9 | 412 | static void |
1d088dee AJ |
413 | tag_summary (const char *filename ATTRIBUTE_UNUSED, |
414 | unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) | |
4977bab6 ZW |
415 | { |
416 | struct gcov_summary summary; | |
cdb23767 | 417 | unsigned ix; |
1d088dee | 418 | |
cdb23767 | 419 | gcov_read_summary (&summary); |
4977bab6 | 420 | printf (" checksum=0x%08x", summary.checksum); |
1d088dee | 421 | |
cdb23767 NS |
422 | for (ix = 0; ix != GCOV_COUNTERS; ix++) |
423 | { | |
7d63a2fa NS |
424 | printf ("\n"); |
425 | print_prefix (filename, 0, 0); | |
426 | printf ("\t\tcounts=%u, runs=%u", | |
cdb23767 | 427 | summary.ctrs[ix].num, summary.ctrs[ix].runs); |
1d088dee | 428 | |
cdb23767 NS |
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 | } | |
4977bab6 | 436 | } |