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