1/* Dump a gcov file, for debugging use. 2 Copyright (C) 2002-2015 Free Software Foundation, Inc. 3 Contributed by Nathan Sidwell <nathan@codesourcery.com> 4 5Gcov is free software; you can redistribute it and/or modify 6it under the terms of the GNU General Public License as published by 7the Free Software Foundation; either version 3, or (at your option) 8any later version. 9 10Gcov is distributed in the hope that it will be useful, 11but WITHOUT ANY WARRANTY; without even the implied warranty of 12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with Gcov; see the file COPYING3. If not see 17<http://www.gnu.org/licenses/>. */ 18 19#include "config.h" 20#include "system.h" 21#include "coretypes.h" 22#include "tm.h" 23#include "version.h" 24#include "intl.h" 25#include "diagnostic.h" 26#include <getopt.h> 27#define IN_GCOV (-1) 28#include "gcov-io.h" 29#include "gcov-io.c" 30 31static void dump_gcov_file (const char *); 32static void print_prefix (const char *, unsigned, gcov_position_t); 33static void print_usage (void); 34static void print_version (void); 35static void tag_function (const char *, unsigned, unsigned); 36static void tag_blocks (const char *, unsigned, unsigned); 37static void tag_arcs (const char *, unsigned, unsigned); 38static void tag_lines (const char *, unsigned, unsigned); 39static void tag_counters (const char *, unsigned, unsigned); 40static void tag_summary (const char *, unsigned, unsigned); 41static void dump_working_sets (const char *filename ATTRIBUTE_UNUSED, 42 const struct gcov_ctr_summary *summary); 43extern int main (int, char **); 44 45typedef struct tag_format 46{ 47 unsigned tag; 48 char const *name; 49 void (*proc) (const char *, unsigned, unsigned); 50} tag_format_t; 51 52static int flag_dump_contents = 0; 53static int flag_dump_positions = 0; 54static int flag_dump_working_sets = 0; 55 56static const struct option options[] = 57{ 58 { "help", no_argument, NULL, 'h' }, 59 { "version", no_argument, NULL, 'v' }, 60 { "long", no_argument, NULL, 'l' }, 61 { "positions", no_argument, NULL, 'o' }, 62 { "working-sets", no_argument, NULL, 'w' }, 63 { 0, 0, 0, 0 } 64}; 65 66static const tag_format_t tag_table[] = 67{ 68 {0, "NOP", NULL}, 69 {0, "UNKNOWN", NULL}, 70 {0, "COUNTERS", tag_counters}, 71 {GCOV_TAG_FUNCTION, "FUNCTION", tag_function}, 72 {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks}, 73 {GCOV_TAG_ARCS, "ARCS", tag_arcs}, 74 {GCOV_TAG_LINES, "LINES", tag_lines}, 75 {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary}, 76 {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary}, 77 {0, NULL, NULL} 78}; 79 80int 81main (int argc ATTRIBUTE_UNUSED, char **argv) 82{ 83 int opt; 84 const char *p; 85 86 p = argv[0] + strlen (argv[0]); 87 while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1])) 88 --p; 89 progname = p; 90 91 xmalloc_set_program_name (progname); 92 93 /* Unlock the stdio streams. */ 94 unlock_std_streams (); 95 96 gcc_init_libintl (); 97 98 diagnostic_initialize (global_dc, 0); 99 100 while ((opt = getopt_long (argc, argv, "hlpvw", options, NULL)) != -1) 101 { 102 switch (opt) 103 { 104 case 'h': 105 print_usage (); 106 break; 107 case 'v': 108 print_version (); 109 break; 110 case 'l': 111 flag_dump_contents = 1; 112 break; 113 case 'p': 114 flag_dump_positions = 1; 115 break; 116 case 'w': 117 flag_dump_working_sets = 1; 118 break; 119 default: 120 fprintf (stderr, "unknown flag `%c'\n", opt); 121 } 122 } 123 124 while (argv[optind]) 125 dump_gcov_file (argv[optind++]); 126 return 0; 127} 128 129static void 130print_usage (void) 131{ 132 printf ("Usage: gcov-dump [OPTION] ... gcovfiles\n"); 133 printf ("Print coverage file contents\n"); 134 printf (" -h, --help Print this help\n"); 135 printf (" -v, --version Print version number\n"); 136 printf (" -l, --long Dump record contents too\n"); 137 printf (" -p, --positions Dump record positions\n"); 138 printf (" -w, --working-sets Dump working set computed from summary\n"); 139} 140 141static void 142print_version (void) 143{ 144 printf ("gcov-dump %s%s\n", pkgversion_string, version_string); 145 printf ("Copyright (C) 2015 Free Software Foundation, Inc.\n"); 146 printf ("This is free software; see the source for copying conditions.\n" 147 "There is NO warranty; not even for MERCHANTABILITY or \n" 148 "FITNESS FOR A PARTICULAR PURPOSE.\n\n"); 149} 150 151static void 152print_prefix (const char *filename, unsigned depth, gcov_position_t position) 153{ 154 static const char prefix[] = " "; 155 156 printf ("%s:", filename); 157 if (flag_dump_positions) 158 printf ("%lu:", (unsigned long) position); 159 printf ("%.*s", (int) depth, prefix); 160} 161 162static void 163dump_gcov_file (const char *filename) 164{ 165 unsigned tags[4]; 166 unsigned depth = 0; 167 168 if (!gcov_open (filename, 1)) 169 { 170 fprintf (stderr, "%s:cannot open\n", filename); 171 return; 172 } 173 174 /* magic */ 175 { 176 unsigned magic = gcov_read_unsigned (); 177 unsigned version; 178 const char *type = NULL; 179 int endianness = 0; 180 char m[4], v[4]; 181 182 if ((endianness = gcov_magic (magic, GCOV_DATA_MAGIC))) 183 type = "data"; 184 else if ((endianness = gcov_magic (magic, GCOV_NOTE_MAGIC))) 185 type = "note"; 186 else 187 { 188 printf ("%s:not a gcov file\n", filename); 189 gcov_close (); 190 return; 191 } 192 version = gcov_read_unsigned (); 193 GCOV_UNSIGNED2STRING (v, version); 194 GCOV_UNSIGNED2STRING (m, magic); 195 196 printf ("%s:%s:magic `%.4s':version `%.4s'%s\n", filename, type, 197 m, v, endianness < 0 ? " (swapped endianness)" : ""); 198 if (version != GCOV_VERSION) 199 { 200 char e[4]; 201 202 GCOV_UNSIGNED2STRING (e, GCOV_VERSION); 203 printf ("%s:warning:current version is `%.4s'\n", filename, e); 204 } 205 } 206 207 /* stamp */ 208 { 209 unsigned stamp = gcov_read_unsigned (); 210 211 printf ("%s:stamp %lu\n", filename, (unsigned long)stamp); 212 } 213 214 while (1) 215 { 216 gcov_position_t base, position = gcov_position (); 217 unsigned tag, length; 218 tag_format_t const *format; 219 unsigned tag_depth; 220 int error; 221 unsigned mask; 222 223 tag = gcov_read_unsigned (); 224 if (!tag) 225 break; 226 length = gcov_read_unsigned (); 227 base = gcov_position (); 228 mask = GCOV_TAG_MASK (tag) >> 1; 229 for (tag_depth = 4; mask; mask >>= 8) 230 { 231 if ((mask & 0xff) != 0xff) 232 { 233 printf ("%s:tag `%08x' is invalid\n", filename, tag); 234 break; 235 } 236 tag_depth--; 237 } 238 for (format = tag_table; format->name; format++) 239 if (format->tag == tag) 240 goto found; 241 format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1]; 242 found:; 243 if (tag) 244 { 245 if (depth && depth < tag_depth) 246 { 247 if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag)) 248 printf ("%s:tag `%08x' is incorrectly nested\n", 249 filename, tag); 250 } 251 depth = tag_depth; 252 tags[depth - 1] = tag; 253 } 254 255 print_prefix (filename, tag_depth, position); 256 printf ("%08x:%4u:%s", tag, length, format->name); 257 if (format->proc) 258 (*format->proc) (filename, tag, length); 259 260 printf ("\n"); 261 if (flag_dump_contents && format->proc) 262 { 263 unsigned long actual_length = gcov_position () - base; 264 265 if (actual_length > length) 266 printf ("%s:record size mismatch %lu bytes overread\n", 267 filename, actual_length - length); 268 else if (length > actual_length) 269 printf ("%s:record size mismatch %lu bytes unread\n", 270 filename, length - actual_length); 271 } 272 gcov_sync (base, length); 273 if ((error = gcov_is_error ())) 274 { 275 printf (error < 0 ? "%s:counter overflow at %lu\n" : 276 "%s:read error at %lu\n", filename, 277 (long unsigned) gcov_position ()); 278 break; 279 } 280 } 281 gcov_close (); 282} 283 284static void 285tag_function (const char *filename ATTRIBUTE_UNUSED, 286 unsigned tag ATTRIBUTE_UNUSED, unsigned length) 287{ 288 unsigned long pos = gcov_position (); 289 290 if (!length) 291 printf (" placeholder"); 292 else 293 { 294 printf (" ident=%u", gcov_read_unsigned ()); 295 printf (", lineno_checksum=0x%08x", gcov_read_unsigned ()); 296 printf (", cfg_checksum=0x%08x", gcov_read_unsigned ()); 297 298 if (gcov_position () - pos < length) 299 { 300 const char *name; 301 302 name = gcov_read_string (); 303 printf (", `%s'", name ? name : "NULL"); 304 name = gcov_read_string (); 305 printf (" %s", name ? name : "NULL"); 306 printf (":%u", gcov_read_unsigned ()); 307 } 308 } 309} 310 311static void 312tag_blocks (const char *filename ATTRIBUTE_UNUSED, 313 unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) 314{ 315 unsigned n_blocks = GCOV_TAG_BLOCKS_NUM (length); 316 317 printf (" %u blocks", n_blocks); 318 319 if (flag_dump_contents) 320 { 321 unsigned ix; 322 323 for (ix = 0; ix != n_blocks; ix++) 324 { 325 if (!(ix & 7)) 326 { 327 printf ("\n"); 328 print_prefix (filename, 0, gcov_position ()); 329 printf ("\t\t%u", ix); 330 } 331 printf (" %04x", gcov_read_unsigned ()); 332 } 333 } 334} 335 336static void 337tag_arcs (const char *filename ATTRIBUTE_UNUSED, 338 unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) 339{ 340 unsigned n_arcs = GCOV_TAG_ARCS_NUM (length); 341 342 printf (" %u arcs", n_arcs); 343 if (flag_dump_contents) 344 { 345 unsigned ix; 346 unsigned blockno = gcov_read_unsigned (); 347 348 for (ix = 0; ix != n_arcs; ix++) 349 { 350 unsigned dst, flags; 351 352 if (!(ix & 3)) 353 { 354 printf ("\n"); 355 print_prefix (filename, 0, gcov_position ()); 356 printf ("\tblock %u:", blockno); 357 } 358 dst = gcov_read_unsigned (); 359 flags = gcov_read_unsigned (); 360 printf (" %u:%04x", dst, flags); 361 if (flags) 362 { 363 char c = '('; 364 365 if (flags & GCOV_ARC_ON_TREE) 366 printf ("%ctree", c), c = ','; 367 if (flags & GCOV_ARC_FAKE) 368 printf ("%cfake", c), c = ','; 369 if (flags & GCOV_ARC_FALLTHROUGH) 370 printf ("%cfall", c), c = ','; 371 printf (")"); 372 } 373 } 374 } 375} 376 377static void 378tag_lines (const char *filename ATTRIBUTE_UNUSED, 379 unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) 380{ 381 if (flag_dump_contents) 382 { 383 unsigned blockno = gcov_read_unsigned (); 384 char const *sep = NULL; 385 386 while (1) 387 { 388 gcov_position_t position = gcov_position (); 389 const char *source = NULL; 390 unsigned lineno = gcov_read_unsigned (); 391 392 if (!lineno) 393 { 394 source = gcov_read_string (); 395 if (!source) 396 break; 397 sep = NULL; 398 } 399 400 if (!sep) 401 { 402 printf ("\n"); 403 print_prefix (filename, 0, position); 404 printf ("\tblock %u:", blockno); 405 sep = ""; 406 } 407 if (lineno) 408 { 409 printf ("%s%u", sep, lineno); 410 sep = ", "; 411 } 412 else 413 { 414 printf ("%s`%s'", sep, source); 415 sep = ":"; 416 } 417 } 418 } 419} 420 421static void 422tag_counters (const char *filename ATTRIBUTE_UNUSED, 423 unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) 424{ 425#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) NAME, 426 static const char *const counter_names[] = { 427#include "gcov-counter.def" 428}; 429#undef DEF_GCOV_COUNTER 430 unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); 431 432 printf (" %s %u counts", 433 counter_names[GCOV_COUNTER_FOR_TAG (tag)], n_counts); 434 if (flag_dump_contents) 435 { 436 unsigned ix; 437 438 for (ix = 0; ix != n_counts; ix++) 439 { 440 gcov_type count; 441 442 if (!(ix & 7)) 443 { 444 printf ("\n"); 445 print_prefix (filename, 0, gcov_position ()); 446 printf ("\t\t%u", ix); 447 } 448 449 count = gcov_read_counter (); 450 printf (" "); 451 printf ("%"PRId64, count); 452 } 453 } 454} 455 456static void 457tag_summary (const char *filename ATTRIBUTE_UNUSED, 458 unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) 459{ 460 struct gcov_summary summary; 461 unsigned ix, h_ix; 462 gcov_bucket_type *histo_bucket; 463 464 gcov_read_summary (&summary); 465 printf (" checksum=0x%08x", summary.checksum); 466 467 for (ix = 0; ix != GCOV_COUNTERS_SUMMABLE; ix++) 468 { 469 printf ("\n"); 470 print_prefix (filename, 0, 0); 471 printf ("\t\tcounts=%u, runs=%u", 472 summary.ctrs[ix].num, summary.ctrs[ix].runs); 473 474 printf (", sum_all=%"PRId64, 475 (int64_t)summary.ctrs[ix].sum_all); 476 printf (", run_max=%"PRId64, 477 (int64_t)summary.ctrs[ix].run_max); 478 printf (", sum_max=%"PRId64, 479 (int64_t)summary.ctrs[ix].sum_max); 480 if (ix != GCOV_COUNTER_ARCS) 481 continue; 482 printf ("\n"); 483 print_prefix (filename, 0, 0); 484 printf ("\t\tcounter histogram:"); 485 for (h_ix = 0; h_ix < GCOV_HISTOGRAM_SIZE; h_ix++) 486 { 487 histo_bucket = &summary.ctrs[ix].histogram[h_ix]; 488 if (!histo_bucket->num_counters) 489 continue; 490 printf ("\n"); 491 print_prefix (filename, 0, 0); 492 printf ("\t\t%d: num counts=%u, min counter=" 493 "%"PRId64 ", cum_counter=" 494 "%"PRId64, 495 h_ix, histo_bucket->num_counters, 496 (int64_t)histo_bucket->min_value, 497 (int64_t)histo_bucket->cum_value); 498 } 499 if (flag_dump_working_sets) 500 dump_working_sets (filename, &summary.ctrs[ix]); 501 } 502} 503 504static void 505dump_working_sets (const char *filename ATTRIBUTE_UNUSED, 506 const struct gcov_ctr_summary *summary) 507{ 508 gcov_working_set_t gcov_working_sets[NUM_GCOV_WORKING_SETS]; 509 unsigned ws_ix, pctinc, pct; 510 gcov_working_set_t *ws_info; 511 512 compute_working_sets (summary, gcov_working_sets); 513 514 printf ("\n"); 515 print_prefix (filename, 0, 0); 516 printf ("\t\tcounter working sets:"); 517 /* Multiply the percentage by 100 to avoid float. */ 518 pctinc = 100 * 100 / NUM_GCOV_WORKING_SETS; 519 for (ws_ix = 0, pct = pctinc; ws_ix < NUM_GCOV_WORKING_SETS; 520 ws_ix++, pct += pctinc) 521 { 522 if (ws_ix == NUM_GCOV_WORKING_SETS - 1) 523 pct = 9990; 524 ws_info = &gcov_working_sets[ws_ix]; 525 /* Print out the percentage using int arithmatic to avoid float. */ 526 printf ("\n"); 527 print_prefix (filename, 0, 0); 528 printf ("\t\t%u.%02u%%: num counts=%u, min counter=" 529 "%"PRId64, 530 pct / 100, pct - (pct / 100 * 100), 531 ws_info->num_counters, 532 (int64_t)ws_info->min_counter); 533 } 534} 535