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