1132718Skan/* Dump a gcov file, for debugging use. 2169689Skan Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. 3132718Skan Contributed by Nathan Sidwell <nathan@codesourcery.com> 4132718Skan 5132718SkanGcov is free software; you can redistribute it and/or modify 6132718Skanit under the terms of the GNU General Public License as published by 7132718Skanthe Free Software Foundation; either version 2, or (at your option) 8132718Skanany later version. 9132718Skan 10132718SkanGcov is distributed in the hope that it will be useful, 11132718Skanbut WITHOUT ANY WARRANTY; without even the implied warranty of 12132718SkanMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13132718SkanGNU General Public License for more details. 14132718Skan 15132718SkanYou should have received a copy of the GNU General Public License 16132718Skanalong with Gcov; see the file COPYING. If not, write to 17169689Skanthe Free Software Foundation, 51 Franklin Street, Fifth Floor, 18169689SkanBoston, MA 02110-1301, USA. */ 19132718Skan 20132718Skan#include "config.h" 21132718Skan#include "system.h" 22132718Skan#include "coretypes.h" 23132718Skan#include "tm.h" 24132718Skan#include "version.h" 25132718Skan#include <getopt.h> 26132718Skan#define IN_GCOV (-1) 27132718Skan#include "gcov-io.h" 28132718Skan#include "gcov-io.c" 29132718Skan 30132718Skanstatic void dump_file (const char *); 31132718Skanstatic void print_prefix (const char *, unsigned, gcov_position_t); 32132718Skanstatic void print_usage (void); 33132718Skanstatic void print_version (void); 34132718Skanstatic void tag_function (const char *, unsigned, unsigned); 35132718Skanstatic void tag_blocks (const char *, unsigned, unsigned); 36132718Skanstatic void tag_arcs (const char *, unsigned, unsigned); 37132718Skanstatic void tag_lines (const char *, unsigned, unsigned); 38132718Skanstatic void tag_counters (const char *, unsigned, unsigned); 39132718Skanstatic void tag_summary (const char *, unsigned, unsigned); 40132718Skanextern int main (int, char **); 41132718Skan 42132718Skantypedef struct tag_format 43132718Skan{ 44132718Skan unsigned tag; 45132718Skan char const *name; 46132718Skan void (*proc) (const char *, unsigned, unsigned); 47132718Skan} tag_format_t; 48132718Skan 49132718Skanstatic int flag_dump_contents = 0; 50132718Skanstatic int flag_dump_positions = 0; 51132718Skan 52132718Skanstatic const struct option options[] = 53132718Skan{ 54132718Skan { "help", no_argument, NULL, 'h' }, 55132718Skan { "version", no_argument, NULL, 'v' }, 56132718Skan { "long", no_argument, NULL, 'l' }, 57132718Skan { "positions", no_argument, NULL, 'o' }, 58132718Skan { 0, 0, 0, 0 } 59132718Skan}; 60132718Skan 61132718Skanstatic const tag_format_t tag_table[] = 62132718Skan{ 63132718Skan {0, "NOP", NULL}, 64132718Skan {0, "UNKNOWN", NULL}, 65132718Skan {0, "COUNTERS", tag_counters}, 66132718Skan {GCOV_TAG_FUNCTION, "FUNCTION", tag_function}, 67132718Skan {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks}, 68132718Skan {GCOV_TAG_ARCS, "ARCS", tag_arcs}, 69132718Skan {GCOV_TAG_LINES, "LINES", tag_lines}, 70132718Skan {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary}, 71132718Skan {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary}, 72132718Skan {0, NULL, NULL} 73132718Skan}; 74132718Skan 75132718Skanint 76132718Skanmain (int argc ATTRIBUTE_UNUSED, char **argv) 77132718Skan{ 78132718Skan int opt; 79132718Skan 80169689Skan /* Unlock the stdio streams. */ 81169689Skan unlock_std_streams (); 82169689Skan 83132718Skan while ((opt = getopt_long (argc, argv, "hlpv", options, NULL)) != -1) 84132718Skan { 85132718Skan switch (opt) 86132718Skan { 87132718Skan case 'h': 88132718Skan print_usage (); 89132718Skan break; 90132718Skan case 'v': 91132718Skan print_version (); 92132718Skan break; 93132718Skan case 'l': 94132718Skan flag_dump_contents = 1; 95132718Skan break; 96132718Skan case 'p': 97132718Skan flag_dump_positions = 1; 98132718Skan break; 99132718Skan default: 100132718Skan fprintf (stderr, "unknown flag `%c'\n", opt); 101132718Skan } 102132718Skan } 103132718Skan 104132718Skan while (argv[optind]) 105132718Skan dump_file (argv[optind++]); 106132718Skan return 0; 107132718Skan} 108132718Skan 109132718Skanstatic void 110132718Skanprint_usage (void) 111132718Skan{ 112132718Skan printf ("Usage: gcov-dump [OPTION] ... gcovfiles\n"); 113132718Skan printf ("Print coverage file contents\n"); 114132718Skan printf (" -h, --help Print this help\n"); 115132718Skan printf (" -v, --version Print version number\n"); 116132718Skan printf (" -l, --long Dump record contents too\n"); 117132718Skan printf (" -p, --positions Dump record positions\n"); 118132718Skan} 119132718Skan 120132718Skanstatic void 121132718Skanprint_version (void) 122132718Skan{ 123132718Skan printf ("gcov-dump (GCC) %s\n", version_string); 124161651Skan printf ("Copyright (C) 2006 Free Software Foundation, Inc.\n"); 125132718Skan printf ("This is free software; see the source for copying conditions.\n" 126132718Skan "There is NO warranty; not even for MERCHANTABILITY or \n" 127132718Skan "FITNESS FOR A PARTICULAR PURPOSE.\n\n"); 128132718Skan} 129132718Skan 130132718Skanstatic void 131132718Skanprint_prefix (const char *filename, unsigned depth, gcov_position_t position) 132132718Skan{ 133132718Skan static const char prefix[] = " "; 134132718Skan 135132718Skan printf ("%s:", filename); 136132718Skan if (flag_dump_positions) 137132718Skan printf ("%lu:", (unsigned long) position); 138132718Skan printf ("%.*s", (int) depth, prefix); 139132718Skan} 140132718Skan 141132718Skanstatic void 142132718Skandump_file (const char *filename) 143132718Skan{ 144132718Skan unsigned tags[4]; 145132718Skan unsigned depth = 0; 146132718Skan 147132718Skan if (!gcov_open (filename, 1)) 148132718Skan { 149132718Skan fprintf (stderr, "%s:cannot open\n", filename); 150132718Skan return; 151132718Skan } 152132718Skan 153132718Skan /* magic */ 154132718Skan { 155132718Skan unsigned magic = gcov_read_unsigned (); 156132718Skan unsigned version; 157132718Skan const char *type = NULL; 158132718Skan int endianness = 0; 159132718Skan char m[4], v[4]; 160132718Skan 161132718Skan if ((endianness = gcov_magic (magic, GCOV_DATA_MAGIC))) 162132718Skan type = "data"; 163132718Skan else if ((endianness = gcov_magic (magic, GCOV_NOTE_MAGIC))) 164132718Skan type = "note"; 165132718Skan else 166132718Skan { 167132718Skan printf ("%s:not a gcov file\n", filename); 168132718Skan gcov_close (); 169132718Skan return; 170132718Skan } 171132718Skan version = gcov_read_unsigned (); 172132718Skan GCOV_UNSIGNED2STRING (v, version); 173132718Skan GCOV_UNSIGNED2STRING (m, magic); 174132718Skan 175132718Skan printf ("%s:%s:magic `%.4s':version `%.4s'%s\n", filename, type, 176132718Skan m, v, endianness < 0 ? " (swapped endianness)" : ""); 177132718Skan if (version != GCOV_VERSION) 178132718Skan { 179132718Skan char e[4]; 180132718Skan 181132718Skan GCOV_UNSIGNED2STRING (e, GCOV_VERSION); 182132718Skan printf ("%s:warning:current version is `%.4s'\n", filename, e); 183132718Skan } 184132718Skan } 185132718Skan 186132718Skan /* stamp */ 187132718Skan { 188132718Skan unsigned stamp = gcov_read_unsigned (); 189132718Skan 190132718Skan printf ("%s:stamp %lu\n", filename, (unsigned long)stamp); 191132718Skan } 192132718Skan 193132718Skan while (1) 194132718Skan { 195132718Skan gcov_position_t base, position = gcov_position (); 196132718Skan unsigned tag, length; 197132718Skan tag_format_t const *format; 198132718Skan unsigned tag_depth; 199132718Skan int error; 200132718Skan unsigned mask; 201132718Skan 202132718Skan tag = gcov_read_unsigned (); 203132718Skan if (!tag) 204132718Skan break; 205132718Skan length = gcov_read_unsigned (); 206132718Skan base = gcov_position (); 207132718Skan mask = GCOV_TAG_MASK (tag) >> 1; 208132718Skan for (tag_depth = 4; mask; mask >>= 8) 209132718Skan { 210132718Skan if ((mask & 0xff) != 0xff) 211132718Skan { 212132718Skan printf ("%s:tag `%08x' is invalid\n", filename, tag); 213132718Skan break; 214132718Skan } 215132718Skan tag_depth--; 216132718Skan } 217132718Skan for (format = tag_table; format->name; format++) 218132718Skan if (format->tag == tag) 219132718Skan goto found; 220132718Skan format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1]; 221132718Skan found:; 222132718Skan if (tag) 223132718Skan { 224132718Skan if (depth && depth < tag_depth) 225132718Skan { 226132718Skan if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag)) 227132718Skan printf ("%s:tag `%08x' is incorrectly nested\n", 228132718Skan filename, tag); 229132718Skan } 230132718Skan depth = tag_depth; 231132718Skan tags[depth - 1] = tag; 232132718Skan } 233132718Skan 234132718Skan print_prefix (filename, tag_depth, position); 235132718Skan printf ("%08x:%4u:%s", tag, length, format->name); 236132718Skan if (format->proc) 237132718Skan (*format->proc) (filename, tag, length); 238132718Skan 239132718Skan printf ("\n"); 240132718Skan if (flag_dump_contents && format->proc) 241132718Skan { 242132718Skan unsigned long actual_length = gcov_position () - base; 243132718Skan 244132718Skan if (actual_length > length) 245132718Skan printf ("%s:record size mismatch %lu bytes overread\n", 246132718Skan filename, actual_length - length); 247132718Skan else if (length > actual_length) 248132718Skan printf ("%s:record size mismatch %lu bytes unread\n", 249132718Skan filename, length - actual_length); 250132718Skan } 251132718Skan gcov_sync (base, length); 252132718Skan if ((error = gcov_is_error ())) 253132718Skan { 254132718Skan printf (error < 0 ? "%s:counter overflow at %lu\n" : 255132718Skan "%s:read error at %lu\n", filename, 256132718Skan (long unsigned) gcov_position ()); 257132718Skan break; 258132718Skan } 259132718Skan } 260132718Skan gcov_close (); 261132718Skan} 262132718Skan 263132718Skanstatic void 264132718Skantag_function (const char *filename ATTRIBUTE_UNUSED, 265132718Skan unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) 266132718Skan{ 267132718Skan unsigned long pos = gcov_position (); 268132718Skan 269132718Skan printf (" ident=%u", gcov_read_unsigned ()); 270132718Skan printf (", checksum=0x%08x", gcov_read_unsigned ()); 271132718Skan 272132718Skan if (gcov_position () - pos < length) 273132718Skan { 274132718Skan const char *name; 275132718Skan 276132718Skan name = gcov_read_string (); 277132718Skan printf (", `%s'", name ? name : "NULL"); 278132718Skan name = gcov_read_string (); 279132718Skan printf (" %s", name ? name : "NULL"); 280132718Skan printf (":%u", gcov_read_unsigned ()); 281132718Skan } 282132718Skan} 283132718Skan 284132718Skanstatic void 285132718Skantag_blocks (const char *filename ATTRIBUTE_UNUSED, 286132718Skan unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) 287132718Skan{ 288132718Skan unsigned n_blocks = GCOV_TAG_BLOCKS_NUM (length); 289132718Skan 290132718Skan printf (" %u blocks", n_blocks); 291132718Skan 292132718Skan if (flag_dump_contents) 293132718Skan { 294132718Skan unsigned ix; 295132718Skan 296132718Skan for (ix = 0; ix != n_blocks; ix++) 297132718Skan { 298132718Skan if (!(ix & 7)) 299132718Skan { 300132718Skan printf ("\n"); 301132718Skan print_prefix (filename, 0, gcov_position ()); 302132718Skan printf ("\t\t%u", ix); 303132718Skan } 304132718Skan printf (" %04x", gcov_read_unsigned ()); 305132718Skan } 306132718Skan } 307132718Skan} 308132718Skan 309132718Skanstatic void 310132718Skantag_arcs (const char *filename ATTRIBUTE_UNUSED, 311132718Skan unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) 312132718Skan{ 313132718Skan unsigned n_arcs = GCOV_TAG_ARCS_NUM (length); 314132718Skan 315132718Skan printf (" %u arcs", n_arcs); 316132718Skan if (flag_dump_contents) 317132718Skan { 318132718Skan unsigned ix; 319132718Skan unsigned blockno = gcov_read_unsigned (); 320132718Skan 321132718Skan for (ix = 0; ix != n_arcs; ix++) 322132718Skan { 323132718Skan unsigned dst, flags; 324132718Skan 325132718Skan if (!(ix & 3)) 326132718Skan { 327132718Skan printf ("\n"); 328132718Skan print_prefix (filename, 0, gcov_position ()); 329132718Skan printf ("\tblock %u:", blockno); 330132718Skan } 331132718Skan dst = gcov_read_unsigned (); 332132718Skan flags = gcov_read_unsigned (); 333132718Skan printf (" %u:%04x", dst, flags); 334132718Skan } 335132718Skan } 336132718Skan} 337132718Skan 338132718Skanstatic void 339132718Skantag_lines (const char *filename ATTRIBUTE_UNUSED, 340132718Skan unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) 341132718Skan{ 342132718Skan if (flag_dump_contents) 343132718Skan { 344132718Skan unsigned blockno = gcov_read_unsigned (); 345132718Skan char const *sep = NULL; 346132718Skan 347132718Skan while (1) 348132718Skan { 349132718Skan gcov_position_t position = gcov_position (); 350132718Skan const char *source = NULL; 351132718Skan unsigned lineno = gcov_read_unsigned (); 352132718Skan 353132718Skan if (!lineno) 354132718Skan { 355132718Skan source = gcov_read_string (); 356132718Skan if (!source) 357132718Skan break; 358132718Skan sep = NULL; 359132718Skan } 360132718Skan 361132718Skan if (!sep) 362132718Skan { 363132718Skan printf ("\n"); 364132718Skan print_prefix (filename, 0, position); 365132718Skan printf ("\tblock %u:", blockno); 366132718Skan sep = ""; 367132718Skan } 368132718Skan if (lineno) 369132718Skan { 370132718Skan printf ("%s%u", sep, lineno); 371132718Skan sep = ", "; 372132718Skan } 373132718Skan else 374132718Skan { 375132718Skan printf ("%s`%s'", sep, source); 376132718Skan sep = ":"; 377132718Skan } 378132718Skan } 379132718Skan } 380132718Skan} 381132718Skan 382132718Skanstatic void 383132718Skantag_counters (const char *filename ATTRIBUTE_UNUSED, 384132718Skan unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) 385132718Skan{ 386132718Skan static const char *const counter_names[] = GCOV_COUNTER_NAMES; 387132718Skan unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); 388132718Skan 389132718Skan printf (" %s %u counts", 390132718Skan counter_names[GCOV_COUNTER_FOR_TAG (tag)], n_counts); 391132718Skan if (flag_dump_contents) 392132718Skan { 393132718Skan unsigned ix; 394132718Skan 395132718Skan for (ix = 0; ix != n_counts; ix++) 396132718Skan { 397132718Skan gcov_type count; 398132718Skan 399132718Skan if (!(ix & 7)) 400132718Skan { 401132718Skan printf ("\n"); 402132718Skan print_prefix (filename, 0, gcov_position ()); 403132718Skan printf ("\t\t%u", ix); 404132718Skan } 405132718Skan 406132718Skan count = gcov_read_counter (); 407132718Skan printf (" "); 408132718Skan printf (HOST_WIDEST_INT_PRINT_DEC, count); 409132718Skan } 410132718Skan } 411132718Skan} 412132718Skan 413132718Skanstatic void 414132718Skantag_summary (const char *filename ATTRIBUTE_UNUSED, 415132718Skan unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) 416132718Skan{ 417132718Skan struct gcov_summary summary; 418132718Skan unsigned ix; 419132718Skan 420132718Skan gcov_read_summary (&summary); 421132718Skan printf (" checksum=0x%08x", summary.checksum); 422132718Skan 423132718Skan for (ix = 0; ix != GCOV_COUNTERS; ix++) 424132718Skan { 425132718Skan printf ("\n"); 426132718Skan print_prefix (filename, 0, 0); 427132718Skan printf ("\t\tcounts=%u, runs=%u", 428132718Skan summary.ctrs[ix].num, summary.ctrs[ix].runs); 429132718Skan 430132718Skan printf (", sum_all=" HOST_WIDEST_INT_PRINT_DEC, 431132718Skan (HOST_WIDEST_INT)summary.ctrs[ix].sum_all); 432132718Skan printf (", run_max=" HOST_WIDEST_INT_PRINT_DEC, 433132718Skan (HOST_WIDEST_INT)summary.ctrs[ix].run_max); 434132718Skan printf (", sum_max=" HOST_WIDEST_INT_PRINT_DEC, 435132718Skan (HOST_WIDEST_INT)summary.ctrs[ix].sum_max); 436132718Skan } 437132718Skan} 438