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