1/* Dump a gcov file, for debugging use. 2 Copyright (C) 2002-2020 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, unsigned); 36static void tag_blocks (const char *, unsigned, unsigned, unsigned); 37static void tag_arcs (const char *, unsigned, unsigned, unsigned); 38static void tag_lines (const char *, unsigned, unsigned, unsigned); 39static void tag_counters (const char *, unsigned, unsigned, unsigned); 40static void tag_summary (const char *, unsigned, unsigned, unsigned); 41extern int main (int, char **); 42 43typedef struct tag_format 44{ 45 unsigned tag; 46 char const *name; 47 void (*proc) (const char *, unsigned, unsigned, unsigned); 48} tag_format_t; 49 50static int flag_dump_contents = 0; 51static int flag_dump_positions = 0; 52 53static const struct option options[] = 54{ 55 { "help", no_argument, NULL, 'h' }, 56 { "version", no_argument, NULL, 'v' }, 57 { "long", no_argument, NULL, 'l' }, 58 { "positions", no_argument, NULL, 'o' }, 59 { 0, 0, 0, 0 } 60}; 61 62#define VALUE_PADDING_PREFIX " " 63#define VALUE_PREFIX "%2d: " 64 65static const tag_format_t tag_table[] = 66{ 67 {0, "NOP", NULL}, 68 {0, "UNKNOWN", NULL}, 69 {0, "COUNTERS", tag_counters}, 70 {GCOV_TAG_FUNCTION, "FUNCTION", tag_function}, 71 {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks}, 72 {GCOV_TAG_ARCS, "ARCS", tag_arcs}, 73 {GCOV_TAG_LINES, "LINES", tag_lines}, 74 {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary}, 75 {0, NULL, NULL} 76}; 77 78int 79main (int argc ATTRIBUTE_UNUSED, char **argv) 80{ 81 int opt; 82 const char *p; 83 84 p = argv[0] + strlen (argv[0]); 85 while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1])) 86 --p; 87 progname = p; 88 89 xmalloc_set_program_name (progname); 90 91 /* Unlock the stdio streams. */ 92 unlock_std_streams (); 93 94 gcc_init_libintl (); 95 96 diagnostic_initialize (global_dc, 0); 97 98 while ((opt = getopt_long (argc, argv, "hlpvw", options, NULL)) != -1) 99 { 100 switch (opt) 101 { 102 case 'h': 103 print_usage (); 104 break; 105 case 'v': 106 print_version (); 107 break; 108 case 'l': 109 flag_dump_contents = 1; 110 break; 111 case 'p': 112 flag_dump_positions = 1; 113 break; 114 default: 115 fprintf (stderr, "unknown flag `%c'\n", opt); 116 } 117 } 118 119 while (argv[optind]) 120 dump_gcov_file (argv[optind++]); 121 return 0; 122} 123 124static void 125print_usage (void) 126{ 127 printf ("Usage: gcov-dump [OPTION] ... gcovfiles\n"); 128 printf ("Print coverage file contents\n"); 129 printf (" -h, --help Print this help\n"); 130 printf (" -l, --long Dump record contents too\n"); 131 printf (" -p, --positions Dump record positions\n"); 132 printf (" -v, --version Print version number\n"); 133 printf ("\nFor bug reporting instructions, please see:\n%s.\n", 134 bug_report_url); 135} 136 137static void 138print_version (void) 139{ 140 printf ("gcov-dump %s%s\n", pkgversion_string, version_string); 141 printf ("Copyright (C) 2020 Free Software Foundation, Inc.\n"); 142 printf ("This is free software; see the source for copying conditions.\n" 143 "There is NO warranty; not even for MERCHANTABILITY or \n" 144 "FITNESS FOR A PARTICULAR PURPOSE.\n\n"); 145} 146 147static void 148print_prefix (const char *filename, unsigned depth, gcov_position_t position) 149{ 150 static const char prefix[] = " "; 151 152 printf ("%s:", filename); 153 if (flag_dump_positions) 154 printf ("%5lu:", (unsigned long) position); 155 printf ("%.*s", (int) 2 * depth, prefix); 156} 157 158static void 159dump_gcov_file (const char *filename) 160{ 161 unsigned tags[4]; 162 unsigned depth = 0; 163 bool is_data_type; 164 165 if (!gcov_open (filename, 1)) 166 { 167 fprintf (stderr, "%s:cannot open\n", filename); 168 return; 169 } 170 171 /* magic */ 172 { 173 unsigned magic = gcov_read_unsigned (); 174 unsigned version; 175 int endianness = 0; 176 char m[4], v[4]; 177 178 if ((endianness = gcov_magic (magic, GCOV_DATA_MAGIC))) 179 is_data_type = true; 180 else if ((endianness = gcov_magic (magic, GCOV_NOTE_MAGIC))) 181 is_data_type = false; 182 else 183 { 184 printf ("%s:not a gcov file\n", filename); 185 gcov_close (); 186 return; 187 } 188 version = gcov_read_unsigned (); 189 GCOV_UNSIGNED2STRING (v, version); 190 GCOV_UNSIGNED2STRING (m, magic); 191 192 printf ("%s:%s:magic `%.4s':version `%.4s'%s\n", filename, 193 is_data_type ? "data" : "note", 194 m, v, endianness < 0 ? " (swapped endianness)" : ""); 195 if (version != GCOV_VERSION) 196 { 197 char e[4]; 198 199 GCOV_UNSIGNED2STRING (e, GCOV_VERSION); 200 printf ("%s:warning:current version is `%.4s'\n", filename, e); 201 } 202 } 203 204 /* stamp */ 205 { 206 unsigned stamp = gcov_read_unsigned (); 207 208 printf ("%s:stamp %lu\n", filename, (unsigned long)stamp); 209 } 210 211 if (!is_data_type) 212 { 213 printf ("%s:cwd: %s\n", filename, gcov_read_string ()); 214 215 /* Support for unexecuted basic blocks. */ 216 unsigned support_unexecuted_blocks = gcov_read_unsigned (); 217 if (!support_unexecuted_blocks) 218 printf ("%s: has_unexecuted_block is not supported\n", filename); 219 } 220 221 while (1) 222 { 223 gcov_position_t base, position = gcov_position (); 224 unsigned tag, length; 225 tag_format_t const *format; 226 unsigned tag_depth; 227 int error; 228 unsigned mask; 229 230 tag = gcov_read_unsigned (); 231 if (!tag) 232 break; 233 length = gcov_read_unsigned (); 234 base = gcov_position (); 235 mask = GCOV_TAG_MASK (tag) >> 1; 236 for (tag_depth = 4; mask; mask >>= 8) 237 { 238 if ((mask & 0xff) != 0xff) 239 { 240 printf ("%s:tag `%08x' is invalid\n", filename, tag); 241 break; 242 } 243 tag_depth--; 244 } 245 for (format = tag_table; format->name; format++) 246 if (format->tag == tag) 247 goto found; 248 format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1]; 249 found:; 250 if (tag) 251 { 252 if (depth && depth < tag_depth) 253 { 254 if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag)) 255 printf ("%s:tag `%08x' is incorrectly nested\n", 256 filename, tag); 257 } 258 depth = tag_depth; 259 tags[depth - 1] = tag; 260 } 261 262 print_prefix (filename, tag_depth, position); 263 printf ("%08x:%4u:%s", tag, length, format->name); 264 if (format->proc) 265 (*format->proc) (filename, tag, length, depth); 266 267 printf ("\n"); 268 if (flag_dump_contents && format->proc) 269 { 270 unsigned long actual_length = gcov_position () - base; 271 272 if (actual_length > length) 273 printf ("%s:record size mismatch %lu bytes overread\n", 274 filename, actual_length - length); 275 else if (length > actual_length) 276 printf ("%s:record size mismatch %lu bytes unread\n", 277 filename, length - actual_length); 278 } 279 gcov_sync (base, length); 280 if ((error = gcov_is_error ())) 281 { 282 printf (error < 0 ? "%s:counter overflow at %lu\n" : 283 "%s:read error at %lu\n", filename, 284 (long unsigned) gcov_position ()); 285 break; 286 } 287 } 288 gcov_close (); 289} 290 291static void 292tag_function (const char *filename ATTRIBUTE_UNUSED, 293 unsigned tag ATTRIBUTE_UNUSED, unsigned length, 294 unsigned depth ATTRIBUTE_UNUSED) 295{ 296 unsigned long pos = gcov_position (); 297 298 if (!length) 299 printf (" placeholder"); 300 else 301 { 302 printf (" ident=%u", gcov_read_unsigned ()); 303 printf (", lineno_checksum=0x%08x", gcov_read_unsigned ()); 304 printf (", cfg_checksum=0x%08x", gcov_read_unsigned ()); 305 306 if (gcov_position () - pos < length) 307 { 308 const char *name; 309 310 name = gcov_read_string (); 311 printf (", `%s'", name ? name : "NULL"); 312 unsigned artificial = gcov_read_unsigned (); 313 name = gcov_read_string (); 314 printf (" %s", name ? name : "NULL"); 315 unsigned line_start = gcov_read_unsigned (); 316 unsigned column_start = gcov_read_unsigned (); 317 unsigned line_end = gcov_read_unsigned (); 318 unsigned column_end = gcov_read_unsigned (); 319 printf (":%u:%u-%u:%u", line_start, column_start, 320 line_end, column_end); 321 if (artificial) 322 printf (", artificial"); 323 } 324 } 325} 326 327static void 328tag_blocks (const char *filename ATTRIBUTE_UNUSED, 329 unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED, 330 unsigned depth ATTRIBUTE_UNUSED) 331{ 332 printf (" %u blocks", gcov_read_unsigned ()); 333} 334 335static void 336tag_arcs (const char *filename ATTRIBUTE_UNUSED, 337 unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED, 338 unsigned depth) 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, depth, gcov_position ()); 356 printf (VALUE_PADDING_PREFIX "block %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 unsigned depth) 381{ 382 if (flag_dump_contents) 383 { 384 unsigned blockno = gcov_read_unsigned (); 385 char const *sep = NULL; 386 387 while (1) 388 { 389 gcov_position_t position = gcov_position (); 390 const char *source = NULL; 391 unsigned lineno = gcov_read_unsigned (); 392 393 if (!lineno) 394 { 395 source = gcov_read_string (); 396 if (!source) 397 break; 398 sep = NULL; 399 } 400 401 if (!sep) 402 { 403 printf ("\n"); 404 print_prefix (filename, depth, position); 405 printf (VALUE_PADDING_PREFIX "block %u:", blockno); 406 sep = ""; 407 } 408 if (lineno) 409 { 410 printf ("%s%u", sep, lineno); 411 sep = ", "; 412 } 413 else 414 { 415 printf ("%s`%s'", sep, source); 416 sep = ":"; 417 } 418 } 419 } 420} 421 422static void 423tag_counters (const char *filename ATTRIBUTE_UNUSED, 424 unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED, 425 unsigned depth) 426{ 427#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) NAME, 428 static const char *const counter_names[] = { 429#include "gcov-counter.def" 430}; 431#undef DEF_GCOV_COUNTER 432 unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); 433 434 printf (" %s %u counts", 435 counter_names[GCOV_COUNTER_FOR_TAG (tag)], n_counts); 436 if (flag_dump_contents) 437 { 438 unsigned ix; 439 440 for (ix = 0; ix != n_counts; ix++) 441 { 442 gcov_type count; 443 444 if (!(ix & 7)) 445 { 446 printf ("\n"); 447 print_prefix (filename, depth, gcov_position ()); 448 printf (VALUE_PADDING_PREFIX VALUE_PREFIX, ix); 449 } 450 451 count = gcov_read_counter (); 452 printf ("%" PRId64 " ", count); 453 } 454 } 455} 456 457static void 458tag_summary (const char *filename ATTRIBUTE_UNUSED, 459 unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED, 460 unsigned depth ATTRIBUTE_UNUSED) 461{ 462 gcov_summary summary; 463 gcov_read_summary (&summary); 464 printf (" runs=%d, sum_max=%" PRId64, 465 summary.runs, summary.sum_max); 466} 467