gcov.c 64.9 KB
Newer Older
Doug Evans committed
1 2
/* Gcov.c: prepend line execution counts and branch probabilities to a
   source file.
3
   Copyright (C) 1990-2015 Free Software Foundation, Inc.
Doug Evans committed
4
   Contributed by James E. Wilson of Cygnus Support.
5
   Mangled by Bob Manson of Cygnus Support.
6
   Mangled further by Nathan Sidwell <nathan@codesourcery.com>
Doug Evans committed
7 8 9

Gcov is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
10
the Free Software Foundation; either version 3, or (at your option)
Doug Evans committed
11 12 13 14 15 16 17 18
any later version.

Gcov is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
19 20
along with Gcov; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */
Doug Evans committed
21 22 23 24 25 26 27 28 29

/* ??? Print a list of the ten blocks with the highest execution counts,
   and list the line numbers corresponding to those blocks.  Also, perhaps
   list the line numbers with the highest execution counts, only printing
   the first if there are several which are all listed in the same block.  */

/* ??? Should have an option to print the number of basic blocks, and the
   percent of them that are covered.  */

30 31
/* Need an option to show individual block counts, and show
   probabilities of fall through arcs.  */
Doug Evans committed
32

33
#include "config.h"
34
#include "system.h"
35 36
#include "coretypes.h"
#include "tm.h"
37
#include "intl.h"
38
#include "diagnostic.h"
39
#include "version.h"
40
#include "demangle.h"
Doug Evans committed
41

42 43
#include <getopt.h>

44
#define IN_GCOV 1
Doug Evans committed
45
#include "gcov-io.h"
46
#include "gcov-io.c"
Doug Evans committed
47

48
/* The gcno file is generated by -ftest-coverage option. The gcda file is
49 50
   generated by a program compiled with -fprofile-arcs. Their formats
   are documented in gcov-io.h.  */
Doug Evans committed
51 52

/* The functions in this file for creating and solution program flow graphs
53 54 55
   are very similar to functions in the gcc source file profile.c.  In
   some places we make use of the knowledge of how profile.c works to
   select particular algorithms here.  */
Doug Evans committed
56

57 58
/* The code validates that the profile information read in corresponds
   to the code currently being compiled.  Rather than checking for
59
   identical files, the code below compares a checksum on the CFG
60
   (based on the order of basic blocks and the arcs in the CFG).  If
61 62
   the CFG checksum in the gcda file match the CFG checksum in the
   gcno file, the profile data will be used.  */
63

Doug Evans committed
64 65
/* This is the size of the buffer used to read in source file lines.  */

66 67
struct function_info;
struct block_info;
68
struct source_info;
Doug Evans committed
69

70
/* Describes an arc between two basic blocks.  */
Doug Evans committed
71

72 73
typedef struct arc_info
{
74
  /* source and destination blocks.  */
75 76
  struct block_info *src;
  struct block_info *dst;
Doug Evans committed
77

78 79
  /* transition counts.  */
  gcov_type count;
80 81
  /* used in cycle search, so that we do not clobber original counts.  */
  gcov_type cs_count;
Doug Evans committed
82 83 84 85 86 87

  unsigned int count_valid : 1;
  unsigned int on_tree : 1;
  unsigned int fake : 1;
  unsigned int fall_through : 1;

88 89 90
  /* Arc to a catch handler.  */
  unsigned int is_throw : 1;

91 92 93
  /* Arc is for a function that abnormally returns.  */
  unsigned int is_call_non_return : 1;

94
  /* Arc is for catch/setjmp.  */
95 96 97 98 99
  unsigned int is_nonlocal_return : 1;

  /* Is an unconditional branch.  */
  unsigned int is_unconditional : 1;

100 101 102
  /* Loop making arc.  */
  unsigned int cycle : 1;

103 104
  /* Next branch on line.  */
  struct arc_info *line_next;
105

106 107 108 109
  /* Links to next arc on src and dst lists.  */
  struct arc_info *succ_next;
  struct arc_info *pred_next;
} arc_t;
Doug Evans committed
110

111 112
/* Describes a basic block. Contains lists of arcs to successor and
   predecessor blocks.  */
Doug Evans committed
113

114
typedef struct block_info
Doug Evans committed
115
{
116 117 118 119
  /* Chain of exit and entry arcs.  */
  arc_t *succ;
  arc_t *pred;

120
  /* Number of unprocessed exit and entry arcs.  */
121 122 123
  gcov_type num_succ;
  gcov_type num_pred;

124
  /* Block execution count.  */
125
  gcov_type count;
126
  unsigned flags : 12;
127 128 129
  unsigned count_valid : 1;
  unsigned valid_chain : 1;
  unsigned invalid_chain : 1;
130
  unsigned exceptional : 1;
131

132
  /* Block is a call instrumenting site.  */
133 134
  unsigned is_call_site : 1; /* Does the call.  */
  unsigned is_call_return : 1; /* Is the return.  */
135

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
  /* Block is a landing pad for longjmp or throw.  */
  unsigned is_nonlocal_return : 1;

  union
  {
    struct
    {
     /* Array of line numbers and source files. source files are
        introduced by a linenumber of zero, the next 'line number' is
        the number of the source file.  Always starts with a source
        file.  */
      unsigned *encoding;
      unsigned num;
    } line; /* Valid until blocks are linked onto lines */
    struct
    {
152
      /* Single line graph cycle workspace.  Used for all-blocks
153
	 mode.  */
154 155 156
      arc_t *arc;
      unsigned ident;
    } cycle; /* Used in all-blocks mode, after blocks are linked onto
157
	       lines.  */
158 159 160 161
  } u;

  /* Temporary chain for solving graph, and for chaining blocks on one
     line.  */
162
  struct block_info *chain;
163

164
} block_t;
Doug Evans committed
165

166
/* Describes a single function. Contains an array of basic blocks.  */
Doug Evans committed
167

168
typedef struct function_info
Nathan Sidwell committed
169
{
170 171
  /* Name of function.  */
  char *name;
172
  char *demangled_name;
173
  unsigned ident;
174 175
  unsigned lineno_checksum;
  unsigned cfg_checksum;
Doug Evans committed
176

177 178 179
  /* The graph contains at least one fake incoming edge.  */
  unsigned has_catch : 1;

180 181 182 183
  /* Array of basic blocks.  Like in GCC, the entry block is
     at blocks[0] and the exit block is at blocks[1].  */
#define ENTRY_BLOCK (0)
#define EXIT_BLOCK (1)
184 185
  block_t *blocks;
  unsigned num_blocks;
186
  unsigned blocks_executed;
187 188 189 190

  /* Raw arc coverage counts.  */
  gcov_type *counts;
  unsigned num_counts;
191

192
  /* First line number & file.  */
193
  unsigned line;
194
  unsigned src;
195 196 197

  /* Next function in same source file.  */
  struct function_info *line_next;
198

199 200 201 202 203 204 205
  /* Next function.  */
  struct function_info *next;
} function_t;

/* Describes coverage of a file or function.  */

typedef struct coverage_info
Nathan Sidwell committed
206 207 208
{
  int lines;
  int lines_executed;
209

Nathan Sidwell committed
210 211 212
  int branches;
  int branches_executed;
  int branches_taken;
213

Nathan Sidwell committed
214 215
  int calls;
  int calls_executed;
216

Nathan Sidwell committed
217
  char *name;
218
} coverage_t;
Nathan Sidwell committed
219

220 221
/* Describes a single line of source. Contains a chain of basic blocks
   with code on it.  */
Doug Evans committed
222

223 224 225
typedef struct line_info
{
  gcov_type count;	   /* execution count */
226 227
  union
  {
228
    arc_t *branches;	   /* branches from blocks that end on this
229
			      line. Used for branch-counts when not
230
			      all-blocks mode.  */
231
    block_t *blocks;       /* blocks which start on this line.  Used
232
			      in all-blocks mode.  */
233
  } u;
234
  unsigned exists : 1;
235
  unsigned unexceptional : 1;
236
} line_t;
Doug Evans committed
237

238 239
/* Describes a file mentioned in the block graph.  Contains an array
   of line info.  */
240

241 242
typedef struct source_info
{
243
  /* Canonical name of source file.  */
244
  char *name;
245
  time_t file_time;
246

247
  /* Array of line information.  */
248 249
  line_t *lines;
  unsigned num_lines;
Doug Evans committed
250

251
  coverage_t coverage;
252 253 254 255

  /* Functions in this source file.  These are in ascending line
     number order.  */
  function_t *functions;
256
} source_t;
Doug Evans committed
257

258 259 260 261 262 263
typedef struct name_map
{
  char *name;  /* Source file name */
  unsigned src;  /* Source file */
} name_map_t;

264
/* Holds a list of function basic block graphs.  */
Doug Evans committed
265

266
static function_t *functions;
267
static function_t **fn_end = &functions;
Doug Evans committed
268

269 270 271
static source_t *sources;   /* Array of source files  */
static unsigned n_sources;  /* Number of sources */
static unsigned a_sources;  /* Allocated sources */
272

273 274 275 276
static name_map_t *names;   /* Mapping of file names to sources */
static unsigned n_names;    /* Number of names */
static unsigned a_names;    /* Allocated names */

277 278
/* This holds data summary information.  */

279
static unsigned object_runs;
280 281
static unsigned program_count;

282 283 284
static unsigned total_lines;
static unsigned total_executed;

285
/* Modification time of graph file.  */
Doug Evans committed
286

287
static time_t bbg_file_time;
Doug Evans committed
288

289 290 291
/* Name of the notes (gcno) output file.  The "bbg" prefix is for
   historical reasons, when the notes file contained only the
   basic block graph notes.  */
Doug Evans committed
292

293
static char *bbg_file_name;
Doug Evans committed
294

295 296 297
/* Stamp of the bbg file */
static unsigned bbg_stamp;

298
/* Name and file pointer of the input file for the count data (gcda).  */
Doug Evans committed
299

300
static char *da_file_name;
Doug Evans committed
301

302 303 304 305
/* Data file is missing.  */

static int no_data_file;

306 307 308 309 310 311 312
/* If there is several input files, compute and display results after
   reading all data files.  This way if two or more gcda file refer to
   the same source file (eg inline subprograms in a .h file), the
   counts are added.  */

static int multiple_files = 0;

313
/* Output branch probabilities.  */
Doug Evans committed
314

315
static int flag_branches = 0;
Doug Evans committed
316

317
/* Show unconditional branches too.  */
318 319
static int flag_unconditional = 0;

Doug Evans committed
320 321 322
/* Output a gcov file if this is true.  This is on by default, and can
   be turned off by the -n option.  */

323
static int flag_gcov_file = 1;
Doug Evans committed
324

325 326 327 328 329
/* Output progress indication if this is true.  This is off by default
   and can be turned on by the -d option.  */

static int flag_display_progress = 0;

330 331 332 333 334 335 336 337
/* Output *.gcov file in intermediate format used by 'lcov'.  */

static int flag_intermediate_format = 0;

/* Output demangled function names.  */

static int flag_demangled_names = 0;

338 339 340
/* For included files, make the gcov output file name include the name
   of the input source file.  For example, if x.h is included in a.c,
   then the output file name is a.c##x.h.gcov instead of x.h.gcov.  */
Doug Evans committed
341

342
static int flag_long_names = 0;
Doug Evans committed
343

344 345 346 347 348
/* Output count information for every basic block, not merely those
   that contain line number information.  */

static int flag_all_blocks = 0;

Doug Evans committed
349 350
/* Output summary info for each function.  */

351
static int flag_function_summary = 0;
Doug Evans committed
352

353 354
/* Object directory file prefix.  This is the directory/file where the
   graph and data files are looked for, if nonzero.  */
Doug Evans committed
355 356 357

static char *object_directory = 0;

358 359 360 361 362 363 364 365 366 367 368
/* Source directory prefix.  This is removed from source pathnames
   that match, when generating the output file name.  */

static char *source_prefix = 0;
static size_t source_length = 0;

/* Only show data for sources with relative pathnames.  Absolute ones
   usually indicate a system header file, which although it may
   contain inline functions, is usually uninteresting.  */
static int flag_relative_only = 0;

369
/* Preserve all pathname components. Needed when object files and
370 371 372 373
   source files are in subdirectories. '/' is mangled as '#', '.' is
   elided and '..' mangled to '^'.  */

static int flag_preserve_paths = 0;
374

375
/* Output the number of times a branch was taken as opposed to the percentage
376
   of times it was taken.  */
377

378
static int flag_counts = 0;
379

Doug Evans committed
380
/* Forward declarations.  */
381 382 383 384
static int process_args (int, char **);
static void print_usage (int) ATTRIBUTE_NORETURN;
static void print_version (void) ATTRIBUTE_NORETURN;
static void process_file (const char *);
385
static void generate_results (const char *);
386
static void create_file_names (const char *);
387 388 389
static int name_search (const void *, const void *);
static int name_sort (const void *, const void *);
static char *canonicalize_name (const char *);
390 391 392
static unsigned find_source (const char *);
static function_t *read_graph_file (void);
static int read_count_file (function_t *);
393
static void solve_flow_graph (function_t *);
394
static void find_exception_blocks (function_t *);
395 396
static void add_branch_counts (coverage_t *, const arc_t *);
static void add_line_counts (coverage_t *, function_t *);
397
static void executed_summary (unsigned, unsigned);
398 399 400
static void function_summary (const coverage_t *, const char *);
static const char *format_gcov (gcov_type, gcov_type, int);
static void accumulate_line_counts (source_t *);
401
static void output_gcov_file (const char *, source_t *);
402 403 404
static int output_branch_count (FILE *, int, const arc_t *);
static void output_lines (FILE *, const source_t *);
static char *make_gcov_file_name (const char *, const char *);
405
static char *mangle_name (const char *, char *);
406
static void release_structures (void);
407
static void release_function (function_t *);
408
extern int main (int, char **);
Doug Evans committed
409 410

int
411
main (int argc, char **argv)
Doug Evans committed
412
{
413
  int argno;
414
  int first_arg;
415 416 417 418 419 420 421 422
  const char *p;

  p = argv[0] + strlen (argv[0]);
  while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
    --p;
  progname = p;

  xmalloc_set_program_name (progname);
423

424
  /* Unlock the stdio streams.  */
425
  unlock_std_streams ();
426

427
  gcc_init_libintl ();
428

429 430
  diagnostic_initialize (global_dc, 0);

431 432 433
  /* Handle response files.  */
  expandargv (&argc, &argv);

434 435 436 437 438
  a_names = 10;
  names = XNEWVEC (name_map_t, a_names);
  a_sources = 10;
  sources = XNEWVEC (source_t, a_sources);
  
439 440 441
  argno = process_args (argc, argv);
  if (optind == argc)
    print_usage (true);
Doug Evans committed
442

443 444 445
  if (argc - argno > 1)
    multiple_files = 1;

446 447
  first_arg = argno;
  
448
  for (; argno != argc; argno++)
449 450
    {
      if (flag_display_progress)
451 452
        printf ("Processing file %d out of %d\n",
		argno - first_arg + 1, argc - first_arg);
453 454
      process_file (argv[argno]);
    }
455

456 457 458
  generate_results (multiple_files ? NULL : argv[argc - 1]);

  release_structures ();
459

Doug Evans committed
460 461 462
  return 0;
}

463 464
/* Print a usage message and exit.  If ERROR_P is nonzero, this is an error,
   otherwise the output of --help.  */
Doug Evans committed
465 466

static void
467
print_usage (int error_p)
Doug Evans committed
468
{
469 470
  FILE *file = error_p ? stderr : stdout;
  int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
471

472
  fnotice (file, "Usage: gcov [OPTION]... SOURCE|OBJ...\n\n");
473 474
  fnotice (file, "Print code coverage information.\n\n");
  fnotice (file, "  -h, --help                      Print this help, then exit\n");
475
  fnotice (file, "  -a, --all-blocks                Show information for every basic block\n");
476
  fnotice (file, "  -b, --branch-probabilities      Include branch probabilities in output\n");
477
  fnotice (file, "  -c, --branch-counts             Output counts of branches taken\n\
478
                                    rather than percentages\n");
479 480 481
  fnotice (file, "  -d, --display-progress          Display progress information\n");
  fnotice (file, "  -f, --function-summaries        Output summaries for each function\n");
  fnotice (file, "  -i, --intermediate-format       Output .gcov file in intermediate text format\n");
482 483
  fnotice (file, "  -l, --long-file-names           Use long output file names for included\n\
                                    source files\n");
484 485
  fnotice (file, "  -m, --demangled-names           Output demangled function names\n");
  fnotice (file, "  -n, --no-output                 Do not create an output file\n");
486 487
  fnotice (file, "  -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n");
  fnotice (file, "  -p, --preserve-paths            Preserve all pathname components\n");
488 489
  fnotice (file, "  -r, --relative-only             Only show data for relative sources\n");
  fnotice (file, "  -s, --source-prefix DIR         Source prefix to elide\n");
490
  fnotice (file, "  -u, --unconditional-branches    Show unconditional branch counts too\n");
491
  fnotice (file, "  -v, --version                   Print version number, then exit\n");
492
  fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
493
	   bug_report_url);
494 495 496 497 498 499
  exit (status);
}

/* Print version information and exit.  */

static void
500
print_version (void)
501
{
502
  fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string);
503
  fprintf (stdout, "Copyright %s 2015 Free Software Foundation, Inc.\n",
504
	   _("(C)"));
505
  fnotice (stdout,
506 507 508
	   _("This is free software; see the source for copying conditions.\n"
	     "There is NO warranty; not even for MERCHANTABILITY or \n"
	     "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
509
  exit (SUCCESS_EXIT_CODE);
Doug Evans committed
510 511
}

512 513 514 515
static const struct option options[] =
{
  { "help",                 no_argument,       NULL, 'h' },
  { "version",              no_argument,       NULL, 'v' },
516
  { "all-blocks",           no_argument,       NULL, 'a' },
517 518
  { "branch-probabilities", no_argument,       NULL, 'b' },
  { "branch-counts",        no_argument,       NULL, 'c' },
519
  { "intermediate-format",  no_argument,       NULL, 'i' },
520 521 522
  { "no-output",            no_argument,       NULL, 'n' },
  { "long-file-names",      no_argument,       NULL, 'l' },
  { "function-summaries",   no_argument,       NULL, 'f' },
523
  { "demangled-names",      no_argument,       NULL, 'm' },
524
  { "preserve-paths",       no_argument,       NULL, 'p' },
525
  { "relative-only",        no_argument,       NULL, 'r' },
526 527
  { "object-directory",     required_argument, NULL, 'o' },
  { "object-file",          required_argument, NULL, 'o' },
528
  { "source-prefix",        required_argument, NULL, 's' },
529
  { "unconditional-branches", no_argument,     NULL, 'u' },
530
  { "display-progress",     no_argument,       NULL, 'd' },
531
  { 0, 0, 0, 0 }
532 533
};

534
/* Process args, return index to first non-arg.  */
Doug Evans committed
535

536
static int
537
process_args (int argc, char **argv)
Doug Evans committed
538
{
539
  int opt;
Doug Evans committed
540

541 542
  while ((opt = getopt_long (argc, argv, "abcdfhilmno:s:pruv", options, NULL)) !=
         -1)
Doug Evans committed
543
    {
544
      switch (opt)
Doug Evans committed
545
	{
546 547 548
	case 'a':
	  flag_all_blocks = 1;
	  break;
549
	case 'b':
550
	  flag_branches = 1;
551 552
	  break;
	case 'c':
553
	  flag_counts = 1;
554
	  break;
555 556
	case 'f':
	  flag_function_summary = 1;
557
	  break;
558 559 560
	case 'h':
	  print_usage (false);
	  /* print_usage will exit.  */
561
	case 'l':
562
	  flag_long_names = 1;
563
	  break;
564 565 566
	case 'm':
	  flag_demangled_names = 1;
	  break;
567 568
	case 'n':
	  flag_gcov_file = 0;
569 570 571 572
	  break;
	case 'o':
	  object_directory = optarg;
	  break;
573 574 575 576 577 578 579
	case 's':
	  source_prefix = optarg;
	  source_length = strlen (source_prefix);
	  break;
	case 'r':
	  flag_relative_only = 1;
	  break;
580
	case 'p':
581
	  flag_preserve_paths = 1;
582
	  break;
583 584 585
	case 'u':
	  flag_unconditional = 1;
	  break;
586 587 588 589
	case 'i':
          flag_intermediate_format = 1;
          flag_gcov_file = 1;
          break;
590 591 592
        case 'd':
          flag_display_progress = 1;
          break;
593 594 595
	case 'v':
	  print_version ();
	  /* print_version will exit.  */
596 597 598
	default:
	  print_usage (true);
	  /* print_usage will exit.  */
Doug Evans committed
599 600 601
	}
    }

602 603 604
  return optind;
}

605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
/* Get the name of the gcov file.  The return value must be free'd.

   It appends the '.gcov' extension to the *basename* of the file.
   The resulting file name will be in PWD.

   e.g.,
   input: foo.da,       output: foo.da.gcov
   input: a/b/foo.cc,   output: foo.cc.gcov  */

static char *
get_gcov_intermediate_filename (const char *file_name)
{
  const char *gcov = ".gcov";
  char *result;
  const char *cptr;

  /* Find the 'basename'.  */
  cptr = lbasename (file_name);

624
  result = XNEWVEC (char, strlen (cptr) + strlen (gcov) + 1);
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
  sprintf (result, "%s%s", cptr, gcov);

  return result;
}

/* Output the result in intermediate format used by 'lcov'.

The intermediate format contains a single file named 'foo.cc.gcov',
with no source code included. A sample output is

file:foo.cc
function:5,1,_Z3foov
function:13,1,main
function:19,1,_GLOBAL__sub_I__Z3foov
function:19,1,_Z41__static_initialization_and_destruction_0ii
lcount:5,1
lcount:7,9
lcount:9,8
lcount:11,1
file:/.../iostream
lcount:74,1
file:/.../basic_ios.h
file:/.../ostream
file:/.../ios_base.h
function:157,0,_ZStorSt12_Ios_IostateS_
lcount:157,0
file:/.../char_traits.h
function:258,0,_ZNSt11char_traitsIcE6lengthEPKc
lcount:258,0
...

The default gcov outputs multiple files: 'foo.cc.gcov',
'iostream.gcov', 'ios_base.h.gcov', etc. with source code
included. Instead the intermediate format here outputs only a single
file 'foo.cc.gcov' similar to the above example. */

static void
output_intermediate_file (FILE *gcov_file, source_t *src)
{
  unsigned line_num;    /* current line number.  */
  const line_t *line;   /* current line info ptr.  */
  function_t *fn;       /* current function info ptr. */

  fprintf (gcov_file, "file:%s\n", src->name);    /* source file name */

  for (fn = src->functions; fn; fn = fn->line_next)
    {
      /* function:<name>,<line_number>,<execution_count> */
      fprintf (gcov_file, "function:%d,%s,%s\n", fn->line,
               format_gcov (fn->blocks[0].count, 0, -1),
               flag_demangled_names ? fn->demangled_name : fn->name);
    }

  for (line_num = 1, line = &src->lines[line_num];
       line_num < src->num_lines;
       line_num++, line++)
    {
      arc_t *arc;
      if (line->exists)
        fprintf (gcov_file, "lcount:%u,%s\n", line_num,
                 format_gcov (line->count, 0, -1));
      if (flag_branches)
        for (arc = line->u.branches; arc; arc = arc->line_next)
          {
            if (!arc->is_unconditional && !arc->is_call_non_return)
              {
                const char *branch_type;
                /* branch:<line_num>,<branch_coverage_type>
                   branch_coverage_type
                     : notexec (Branch not executed)
                     : taken (Branch executed and taken)
                     : nottaken (Branch executed, but not taken)
                */
                if (arc->src->count)
                  branch_type = (arc->count > 0) ? "taken" : "nottaken";
                else
                  branch_type = "notexec";
702
                fprintf (gcov_file, "branch:%d,%s\n", line_num, branch_type);
703 704 705 706 707 708
              }
          }
    }
}


709
/* Process a single input file.  */
710

711
static void
712
process_file (const char *file_name)
713
{
714
  function_t *fns;
715

716
  create_file_names (file_name);
717 718
  fns = read_graph_file ();
  if (!fns)
719
    return;
720 721 722
  
  read_count_file (fns);
  while (fns)
723
    {
724
      function_t *fn = fns;
725

726 727
      fns = fn->next;
      fn->next = NULL;
728 729
      if (fn->counts)
	{
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
	  unsigned src = fn->src;
	  unsigned line = fn->line;
	  unsigned block_no;
	  function_t *probe, **prev;
	  
	  /* Now insert it into the source file's list of
	     functions. Normally functions will be encountered in
	     ascending order, so a simple scan is quick.  Note we're
	     building this list in reverse order.  */
	  for (prev = &sources[src].functions;
	       (probe = *prev); prev = &probe->line_next)
	    if (probe->line <= line)
	      break;
	  fn->line_next = probe;
	  *prev = fn;

	  /* Mark last line in files touched by function.  */
	  for (block_no = 0; block_no != fn->num_blocks; block_no++)
	    {
	      unsigned *enc = fn->blocks[block_no].u.line.encoding;
	      unsigned num = fn->blocks[block_no].u.line.num;

	      for (; num--; enc++)
		if (!*enc)
		  {
		    if (enc[1] != src)
		      {
			if (line >= sources[src].num_lines)
			  sources[src].num_lines = line + 1;
			line = 0;
			src = enc[1];
		      }
		    enc++;
		    num--;
		  }
		else if (*enc > line)
		  line = *enc;
	    }
	  if (line >= sources[src].num_lines)
	    sources[src].num_lines = line + 1;
	  
771
	  solve_flow_graph (fn);
772 773
	  if (fn->has_catch)
	    find_exception_blocks (fn);
774 775
	  *fn_end = fn;
	  fn_end = &fn->next;
776 777
	}
      else
778 779 780
	/* The function was not in the executable -- some other
	   instance must have been selected.  */
	release_function (fn);
781
    }
782 783 784
}

static void
785
output_gcov_file (const char *file_name, source_t *src)
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
{
  char *gcov_file_name = make_gcov_file_name (file_name, src->coverage.name);

  if (src->coverage.lines)
    {
      FILE *gcov_file = fopen (gcov_file_name, "w");
      if (gcov_file)
        {
          fnotice (stdout, "Creating '%s'\n", gcov_file_name);
          output_lines (gcov_file, src);
          if (ferror (gcov_file))
            fnotice (stderr, "Error writing output file '%s'\n", gcov_file_name);
          fclose (gcov_file);
        }
      else
        fnotice (stderr, "Could not open output file '%s'\n", gcov_file_name);
    }
  else
    {
      unlink (gcov_file_name);
      fnotice (stdout, "Removing '%s'\n", gcov_file_name);
    }
  free (gcov_file_name);
}

static void
812 813
generate_results (const char *file_name)
{
814
  unsigned ix;
815 816
  source_t *src;
  function_t *fn;
817 818
  FILE *gcov_intermediate_file = NULL;
  char *gcov_intermediate_filename = NULL;
819

820 821 822 823
  for (ix = n_sources, src = sources; ix--; src++)
    if (src->num_lines)
      src->lines = XCNEWVEC (line_t, src->num_lines);

824 825 826
  for (fn = functions; fn; fn = fn->next)
    {
      coverage_t coverage;
827

828
      memset (&coverage, 0, sizeof (coverage));
829
      coverage.name = flag_demangled_names ? fn->demangled_name : fn->name;
830 831 832 833 834 835 836
      add_line_counts (flag_function_summary ? &coverage : NULL, fn);
      if (flag_function_summary)
	{
	  function_summary (&coverage, "Function");
	  fnotice (stdout, "\n");
	}
    }
837

838 839 840 841 842
  if (file_name)
    {
      name_map_t *name_map = (name_map_t *)bsearch
	(file_name, names, n_names, sizeof (*names), name_search);
      if (name_map)
843
	file_name = sources[name_map->src].coverage.name;
844 845 846
      else
	file_name = canonicalize_name (file_name);
    }
847 848 849 850 851 852 853 854 855 856 857 858 859 860 861

  if (flag_gcov_file && flag_intermediate_format)
    {
      /* Open the intermediate file.  */
      gcov_intermediate_filename =
        get_gcov_intermediate_filename (file_name);
      gcov_intermediate_file = fopen (gcov_intermediate_filename, "w");
      if (!gcov_intermediate_file)
        {
          fnotice (stderr, "Cannot open intermediate output file %s\n",
                   gcov_intermediate_filename);
          return;
        }
    }

862
  for (ix = n_sources, src = sources; ix--; src++)
863
    {
864 865 866 867 868 869 870 871
      if (flag_relative_only)
	{
	  /* Ignore this source, if it is an absolute path (after
	     source prefix removal).  */
	  char first = src->coverage.name[0];
      
#if HAVE_DOS_BASED_FILE_SYSTEM
	  if (first && src->coverage.name[1] == ':')
872
	    first = src->coverage.name[2];
873
#endif
874 875
	  if (IS_DIR_SEPARATOR (first))
	    continue;
876 877
	}
      
878 879
      accumulate_line_counts (src);
      function_summary (&src->coverage, "File");
880 881
      total_lines += src->coverage.lines;
      total_executed += src->coverage.lines_executed;
882
      if (flag_gcov_file)
883
	{
884 885 886 887 888 889 890 891 892
          if (flag_intermediate_format)
            /* Output the intermediate format without requiring source
               files.  This outputs a section to a *single* file.  */
            output_intermediate_file (gcov_intermediate_file, src);
          else
            output_gcov_file (file_name, src);
          fnotice (stdout, "\n");
        }
    }
893

894 895 896 897 898
  if (flag_gcov_file && flag_intermediate_format)
    {
      /* Now we've finished writing the intermediate file.  */
      fclose (gcov_intermediate_file);
      XDELETEVEC (gcov_intermediate_filename);
899
    }
900 901 902

  if (!file_name)
    executed_summary (total_lines, total_executed);
Doug Evans committed
903 904
}

905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924
/* Release a function structure */

static void
release_function (function_t *fn)
{
  unsigned ix;
  block_t *block;

  for (ix = fn->num_blocks, block = fn->blocks; ix--; block++)
    {
      arc_t *arc, *arc_n;

      for (arc = block->succ; arc; arc = arc_n)
	{
	  arc_n = arc->succ_next;
	  free (arc);
	}
    }
  free (fn->blocks);
  free (fn->counts);
925 926 927
  if (flag_demangled_names && fn->demangled_name != fn->name)
    free (fn->demangled_name);
  free (fn->name);
928 929
}

930
/* Release all memory used.  */
Doug Evans committed
931

932
static void
933
release_structures (void)
934
{
935
  unsigned ix;
936
  function_t *fn;
937

938
  for (ix = n_sources; ix--;)
939 940 941 942 943 944
    free (sources[ix].lines);
  free (sources);
  
  for (ix = n_names; ix--;)
    free (names[ix].name);
  free (names);
945

946 947 948
  while ((fn = functions))
    {
      functions = fn->next;
949
      release_function (fn);
950 951 952
    }
}

953 954 955 956 957 958
/* Generate the names of the graph and data files.  If OBJECT_DIRECTORY
   is not specified, these are named from FILE_NAME sans extension.  If
   OBJECT_DIRECTORY is specified and is a directory, the files are in that
   directory, but named from the basename of the FILE_NAME, sans extension.
   Otherwise OBJECT_DIRECTORY is taken to be the name of the object *file*
   and the data files are named from that.  */
Doug Evans committed
959 960

static void
961
create_file_names (const char *file_name)
Doug Evans committed
962 963
{
  char *cptr;
964
  char *name;
965
  int length = strlen (file_name);
966
  int base;
967

968
  /* Free previous file names.  */
969 970
  free (bbg_file_name);
  free (da_file_name);
971 972 973 974
  da_file_name = bbg_file_name = NULL;
  bbg_file_time = 0;
  bbg_stamp = 0;

975
  if (object_directory && object_directory[0])
Doug Evans committed
976
    {
977 978 979
      struct stat status;

      length += strlen (object_directory) + 2;
980
      name = XNEWVEC (char, length);
981
      name[0] = 0;
982

983 984
      base = !stat (object_directory, &status) && S_ISDIR (status.st_mode);
      strcat (name, object_directory);
985
      if (base && (! IS_DIR_SEPARATOR (name[strlen (name) - 1])))
986
	strcat (name, "/");
Doug Evans committed
987 988 989
    }
  else
    {
990
      name = XNEWVEC (char, length + 1);
991 992
      strcpy (name, file_name);
      base = 0;
Doug Evans committed
993
    }
994

995 996
  if (base)
    {
997
      /* Append source file name.  */
998 999
      const char *cptr = lbasename (file_name);
      strcat (name, cptr ? cptr : file_name);
1000
    }
1001

1002
  /* Remove the extension.  */
1003
  cptr = strrchr (CONST_CAST (char *, lbasename (name)), '.');
Doug Evans committed
1004
  if (cptr)
1005
    *cptr = 0;
1006

1007
  length = strlen (name);
H.J. Lu committed
1008

1009
  bbg_file_name = XNEWVEC (char, length + strlen (GCOV_NOTE_SUFFIX) + 1);
1010
  strcpy (bbg_file_name, name);
1011
  strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX);
1012

1013
  da_file_name = XNEWVEC (char, length + strlen (GCOV_DATA_SUFFIX) + 1);
1014 1015
  strcpy (da_file_name, name);
  strcpy (da_file_name + length, GCOV_DATA_SUFFIX);
1016

1017
  free (name);
1018
  return;
Doug Evans committed
1019 1020
}

1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
/* A is a string and B is a pointer to name_map_t.  Compare for file
   name orderability.  */

static int
name_search (const void *a_, const void *b_)
{
  const char *a = (const char *)a_;
  const name_map_t *b = (const name_map_t *)b_;

#if HAVE_DOS_BASED_FILE_SYSTEM
  return strcasecmp (a, b->name);
#else
  return strcmp (a, b->name);
#endif
}

/* A and B are a pointer to name_map_t.  Compare for file name
   orderability.  */

static int
name_sort (const void *a_, const void *b_)
{
  const name_map_t *a = (const name_map_t *)a_;
  return name_search (a->name, b_);
}

1047 1048
/* Find or create a source file structure for FILE_NAME. Copies
   FILE_NAME on creation */
1049

1050
static unsigned
1051
find_source (const char *file_name)
1052
{
1053 1054 1055
  name_map_t *name_map;
  char *canon;
  unsigned idx;
1056
  struct stat status;
1057 1058 1059

  if (!file_name)
    file_name = "<unknown>";
1060 1061 1062 1063 1064 1065 1066
  name_map = (name_map_t *)bsearch
    (file_name, names, n_names, sizeof (*names), name_search);
  if (name_map)
    {
      idx = name_map->src;
      goto check_date;
    }
1067

1068
  if (n_names + 2 > a_names)
1069
    {
1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087
      /* Extend the name map array -- we'll be inserting one or two
	 entries.  */
      a_names *= 2;
      name_map = XNEWVEC (name_map_t, a_names);
      memcpy (name_map, names, n_names * sizeof (*names));
      free (names);
      names = name_map;
    }
  
  /* Not found, try the canonical name. */
  canon = canonicalize_name (file_name);
  name_map = (name_map_t *)bsearch
    (canon, names, n_names, sizeof (*names), name_search);
  if (!name_map)
    {
      /* Not found with canonical name, create a new source.  */
      source_t *src;
      
1088 1089 1090 1091 1092 1093 1094 1095
      if (n_sources == a_sources)
	{
	  a_sources *= 2;
	  src = XNEWVEC (source_t, a_sources);
	  memcpy (src, sources, n_sources * sizeof (*sources));
	  free (sources);
	  sources = src;
	}
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105

      idx = n_sources;

      name_map = &names[n_names++];
      name_map->name = canon;
      name_map->src = idx;

      src = &sources[n_sources++];
      memset (src, 0, sizeof (*src));
      src->name = canon;
1106
      src->coverage.name = src->name;
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116
      if (source_length
#if HAVE_DOS_BASED_FILE_SYSTEM
	  /* You lose if separators don't match exactly in the
	     prefix.  */
	  && !strncasecmp (source_prefix, src->coverage.name, source_length)
#else
	  && !strncmp (source_prefix, src->coverage.name, source_length)
#endif
	  && IS_DIR_SEPARATOR (src->coverage.name[source_length]))
	src->coverage.name += source_length + 1;
1117
      if (!stat (src->name, &status))
1118 1119
	src->file_time = status.st_mtime;
    }
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129
  else
    idx = name_map->src;

  if (name_search (file_name, name_map))
    {
      /* Append the non-canonical name.  */
      name_map = &names[n_names++];
      name_map->name = xstrdup (file_name);
      name_map->src = idx;
    }
1130

1131 1132 1133 1134 1135
  /* Resort the name map.  */
  qsort (names, n_names, sizeof (*names), name_sort);
  
 check_date:
  if (sources[idx].file_time > bbg_file_time)
1136 1137 1138
    {
      static int info_emitted;

1139
      fnotice (stderr, "%s:source file is newer than notes file '%s'\n",
1140
	       file_name, bbg_file_name);
1141 1142 1143 1144 1145 1146
      if (!info_emitted)
	{
	  fnotice (stderr,
		   "(the message is only displayed one per source file)\n");
	  info_emitted = 1;
	}
1147
      sources[idx].file_time = 0;
1148
    }
1149

1150
  return idx;
1151 1152
}

1153
/* Read the notes file.  Return list of functions read -- in reverse order.  */
Doug Evans committed
1154

1155
static function_t *
1156
read_graph_file (void)
Doug Evans committed
1157
{
1158
  unsigned version;
1159
  unsigned current_tag = 0;
1160 1161 1162 1163
  function_t *fn = NULL;
  function_t *fns = NULL;
  function_t **fns_end = &fns;
  unsigned src_idx = 0;
1164
  unsigned ix;
1165
  unsigned tag;
1166

1167
  if (!gcov_open (bbg_file_name, 1))
Doug Evans committed
1168
    {
1169
      fnotice (stderr, "%s:cannot open notes file\n", bbg_file_name);
1170
      return fns;
Doug Evans committed
1171
    }
1172
  bbg_file_time = gcov_time ();
1173
  if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC))
1174
    {
1175
      fnotice (stderr, "%s:not a gcov notes file\n", bbg_file_name);
1176
      gcov_close ();
1177
      return fns;
1178
    }
1179

1180 1181
  version = gcov_read_unsigned ();
  if (version != GCOV_VERSION)
1182 1183
    {
      char v[4], e[4];
1184

1185 1186 1187
      GCOV_UNSIGNED2STRING (v, version);
      GCOV_UNSIGNED2STRING (e, GCOV_VERSION);

1188
      fnotice (stderr, "%s:version '%.4s', prefer '%.4s'\n",
1189 1190
	       bbg_file_name, v, e);
    }
1191 1192
  bbg_stamp = gcov_read_unsigned ();

1193
  while ((tag = gcov_read_unsigned ()))
1194
    {
1195
      unsigned length = gcov_read_unsigned ();
1196
      gcov_position_t base = gcov_position ();
1197

1198
      if (tag == GCOV_TAG_FUNCTION)
1199
	{
1200
	  char *function_name;
1201 1202
	  unsigned ident, lineno;
	  unsigned lineno_checksum, cfg_checksum;
1203

1204
	  ident = gcov_read_unsigned ();
1205 1206
	  lineno_checksum = gcov_read_unsigned ();
	  cfg_checksum = gcov_read_unsigned ();
1207
	  function_name = xstrdup (gcov_read_string ());
1208
	  src_idx = find_source (gcov_read_string ());
1209
	  lineno = gcov_read_unsigned ();
1210

1211
	  fn = XCNEW (function_t);
1212
	  fn->name = function_name;
1213 1214 1215 1216 1217 1218
          if (flag_demangled_names)
            {
              fn->demangled_name = cplus_demangle (fn->name, DMGL_PARAMS);
              if (!fn->demangled_name)
                fn->demangled_name = fn->name;
            }
1219
	  fn->ident = ident;
1220 1221
	  fn->lineno_checksum = lineno_checksum;
	  fn->cfg_checksum = cfg_checksum;
1222
	  fn->src = src_idx;
1223
	  fn->line = lineno;
1224

1225 1226 1227 1228
	  fn->line_next = NULL;
	  fn->next = NULL;
	  *fns_end = fn;
	  fns_end = &fn->next;
1229
	  current_tag = tag;
1230
	}
1231
      else if (fn && tag == GCOV_TAG_BLOCKS)
1232
	{
1233
	  if (fn->blocks)
1234
	    fnotice (stderr, "%s:already seen blocks for '%s'\n",
1235 1236
		     bbg_file_name, fn->name);
	  else
1237
	    {
1238
	      unsigned ix, num_blocks = GCOV_TAG_BLOCKS_NUM (length);
1239
	      fn->num_blocks = num_blocks;
1240

1241
	      fn->blocks = XCNEWVEC (block_t, fn->num_blocks);
1242
	      for (ix = 0; ix != num_blocks; ix++)
1243
		fn->blocks[ix].flags = gcov_read_unsigned ();
1244
	    }
1245 1246 1247
	}
      else if (fn && tag == GCOV_TAG_ARCS)
	{
1248
	  unsigned src = gcov_read_unsigned ();
1249
	  unsigned num_dests = GCOV_TAG_ARCS_NUM (length);
1250 1251 1252
	  block_t *src_blk = &fn->blocks[src];
	  unsigned mark_catches = 0;
	  struct arc_info *arc;
1253

1254
	  if (src >= fn->num_blocks || fn->blocks[src].succ)
1255
	    goto corrupt;
1256

1257
	  while (num_dests--)
1258
	    {
1259 1260
	      unsigned dest = gcov_read_unsigned ();
	      unsigned flags = gcov_read_unsigned ();
1261

1262
	      if (dest >= fn->num_blocks)
1263
		goto corrupt;
1264
	      arc = XCNEW (arc_t);
1265

1266
	      arc->dst = &fn->blocks[dest];
1267
	      arc->src = src_blk;
1268

1269 1270 1271 1272 1273
	      arc->count = 0;
	      arc->count_valid = 0;
	      arc->on_tree = !!(flags & GCOV_ARC_ON_TREE);
	      arc->fake = !!(flags & GCOV_ARC_FAKE);
	      arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH);
1274

1275 1276 1277
	      arc->succ_next = src_blk->succ;
	      src_blk->succ = arc;
	      src_blk->num_succ++;
1278

1279 1280 1281 1282
	      arc->pred_next = fn->blocks[dest].pred;
	      fn->blocks[dest].pred = arc;
	      fn->blocks[dest].num_pred++;

1283 1284 1285 1286 1287 1288 1289 1290
	      if (arc->fake)
		{
		  if (src)
		    {
		      /* Exceptional exit from this function, the
			 source block must be a call.  */
		      fn->blocks[src].is_call_site = 1;
		      arc->is_call_non_return = 1;
1291
		      mark_catches = 1;
1292 1293 1294 1295
		    }
		  else
		    {
		      /* Non-local return from a callee of this
1296
		         function. The destination block is a setjmp.  */
1297 1298 1299 1300
		      arc->is_nonlocal_return = 1;
		      fn->blocks[dest].is_nonlocal_return = 1;
		    }
		}
1301

1302 1303
	      if (!arc->on_tree)
		fn->num_counts++;
1304
	    }
1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318
	  
	  if (mark_catches)
	    {
	      /* We have a fake exit from this block.  The other
		 non-fall through exits must be to catch handlers.
		 Mark them as catch arcs.  */

	      for (arc = src_blk->succ; arc; arc = arc->succ_next)
		if (!arc->fake && !arc->fall_through)
		  {
		    arc->is_throw = 1;
		    fn->has_catch = 1;
		  }
	    }
1319 1320 1321
	}
      else if (fn && tag == GCOV_TAG_LINES)
	{
1322
	  unsigned blockno = gcov_read_unsigned ();
1323
	  unsigned *line_nos = XCNEWVEC (unsigned, length - 1);
1324

1325
	  if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding)
1326
	    goto corrupt;
1327

1328
	  for (ix = 0; ;  )
1329
	    {
1330
	      unsigned lineno = gcov_read_unsigned ();
1331

1332
	      if (lineno)
1333
		{
1334 1335 1336
		  if (!ix)
		    {
		      line_nos[ix++] = 0;
1337
		      line_nos[ix++] = src_idx;
1338 1339
		    }
		  line_nos[ix++] = lineno;
1340
		}
1341 1342
	      else
		{
1343
		  const char *file_name = gcov_read_string ();
1344

1345
		  if (!file_name)
1346
		    break;
1347
		  src_idx = find_source (file_name);
1348
		  line_nos[ix++] = 0;
1349
		  line_nos[ix++] = src_idx;
1350
		}
1351
	    }
1352

1353 1354
	  fn->blocks[blockno].u.line.encoding = line_nos;
	  fn->blocks[blockno].u.line.num = ix;
1355 1356 1357 1358 1359 1360
	}
      else if (current_tag && !GCOV_TAG_IS_SUBTAG (current_tag, tag))
	{
	  fn = NULL;
	  current_tag = 0;
	}
1361
      gcov_sync (base, length);
1362
      if (gcov_is_error ())
1363 1364 1365
	{
	corrupt:;
	  fnotice (stderr, "%s:corrupted\n", bbg_file_name);
1366
	  break;
1367
	}
1368
    }
1369
  gcov_close ();
1370

1371 1372
  if (!fns)
    fnotice (stderr, "%s:no functions found\n", bbg_file_name);
1373

1374
  return fns;
1375
}
Doug Evans committed
1376

1377
/* Reads profiles from the count file and attach to each
1378
   function. Return nonzero if fatal error.  */
Doug Evans committed
1379

1380
static int
1381
read_count_file (function_t *fns)
Doug Evans committed
1382
{
1383
  unsigned ix;
1384
  unsigned version;
1385
  unsigned tag;
1386
  function_t *fn = NULL;
1387
  int error = 0;
1388

1389
  if (!gcov_open (da_file_name, 1))
Doug Evans committed
1390
    {
1391 1392 1393 1394
      fnotice (stderr, "%s:cannot open data file, assuming not executed\n",
	       da_file_name);
      no_data_file = 1;
      return 0;
1395
    }
1396
  if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
1397 1398 1399
    {
      fnotice (stderr, "%s:not a gcov data file\n", da_file_name);
    cleanup:;
1400
      gcov_close ();
1401 1402
      return 1;
    }
1403 1404
  version = gcov_read_unsigned ();
  if (version != GCOV_VERSION)
1405
    {
1406 1407 1408 1409
      char v[4], e[4];

      GCOV_UNSIGNED2STRING (v, version);
      GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
H.J. Lu committed
1410

1411
      fnotice (stderr, "%s:version '%.4s', prefer version '%.4s'\n",
1412
	       da_file_name, v, e);
Doug Evans committed
1413
    }
1414 1415 1416
  tag = gcov_read_unsigned ();
  if (tag != bbg_stamp)
    {
1417
      fnotice (stderr, "%s:stamp mismatch with notes file\n", da_file_name);
1418 1419
      goto cleanup;
    }
1420

1421
  while ((tag = gcov_read_unsigned ()))
1422
    {
1423 1424 1425
      unsigned length = gcov_read_unsigned ();
      unsigned long base = gcov_position ();

1426
      if (tag == GCOV_TAG_PROGRAM_SUMMARY)
Doug Evans committed
1427
	{
1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438
	  struct gcov_summary summary;
	  gcov_read_summary (&summary);
	  object_runs += summary.ctrs[GCOV_COUNTER_ARCS].runs;
	  program_count++;
	}
      else if (tag == GCOV_TAG_FUNCTION && !length)
	; /* placeholder  */
      else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH)
	{
	  unsigned ident;
	  struct function_info *fn_n;
1439

1440 1441 1442
	  /* Try to find the function in the list.  To speed up the
	     search, first start from the last function found.  */
	  ident = gcov_read_unsigned ();
1443
	  fn_n = fns;
1444 1445 1446 1447 1448 1449 1450 1451 1452 1453
	  for (fn = fn ? fn->next : NULL; ; fn = fn->next)
	    {
	      if (fn)
		;
	      else if ((fn = fn_n))
		fn_n = NULL;
	      else
		{
		  fnotice (stderr, "%s:unknown function '%u'\n",
			   da_file_name, ident);
1454
		  break;
1455 1456 1457 1458
		}
	      if (fn->ident == ident)
		break;
	    }
1459 1460 1461

	  if (!fn)
	    ;
1462 1463
	  else if (gcov_read_unsigned () != fn->lineno_checksum
		   || gcov_read_unsigned () != fn->cfg_checksum)
Doug Evans committed
1464
	    {
1465
	    mismatch:;
1466
	      fnotice (stderr, "%s:profile mismatch for '%s'\n",
1467
		       da_file_name, fn->name);
1468 1469 1470
	      goto cleanup;
	    }
	}
1471
      else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
1472
	{
1473
	  if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts))
1474
	    goto mismatch;
1475

1476
	  if (!fn->counts)
1477
	    fn->counts = XCNEWVEC (gcov_type, fn->num_counts);
1478

1479
	  for (ix = 0; ix != fn->num_counts; ix++)
1480 1481
	    fn->counts[ix] += gcov_read_counter ();
	}
1482
      gcov_sync (base, length);
1483
      if ((error = gcov_is_error ()))
1484 1485 1486 1487 1488
	{
	  fnotice (stderr, error < 0 ? "%s:overflowed\n" : "%s:corrupted\n",
		   da_file_name);
	  goto cleanup;
	}
1489
    }
1490

1491
  gcov_close ();
1492
  return 0;
Doug Evans committed
1493 1494
}

1495 1496
/* Solve the flow graph. Propagate counts from the instrumented arcs
   to the blocks and the uninstrumented arcs.  */
Doug Evans committed
1497 1498

static void
1499
solve_flow_graph (function_t *fn)
Doug Evans committed
1500
{
1501 1502 1503
  unsigned ix;
  arc_t *arc;
  gcov_type *count_ptr = fn->counts;
1504
  block_t *blk;
1505 1506
  block_t *valid_blocks = NULL;    /* valid, but unpropagated blocks.  */
  block_t *invalid_blocks = NULL;  /* invalid, but inferable blocks.  */
1507

1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529
  /* The arcs were built in reverse order.  Fix that now.  */
  for (ix = fn->num_blocks; ix--;)
    {
      arc_t *arc_p, *arc_n;

      for (arc_p = NULL, arc = fn->blocks[ix].succ; arc;
	   arc_p = arc, arc = arc_n)
	{
	  arc_n = arc->succ_next;
	  arc->succ_next = arc_p;
	}
      fn->blocks[ix].succ = arc_p;

      for (arc_p = NULL, arc = fn->blocks[ix].pred; arc;
	   arc_p = arc, arc = arc_n)
	{
	  arc_n = arc->pred_next;
	  arc->pred_next = arc_p;
	}
      fn->blocks[ix].pred = arc_p;
    }

1530
  if (fn->num_blocks < 2)
1531
    fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n",
1532 1533
	     bbg_file_name, fn->name);
  else
Doug Evans committed
1534
    {
1535
      if (fn->blocks[ENTRY_BLOCK].num_pred)
1536
	fnotice (stderr, "%s:'%s' has arcs to entry block\n",
1537
		 bbg_file_name, fn->name);
Doug Evans committed
1538
      else
1539 1540
	/* We can't deduce the entry block counts from the lack of
	   predecessors.  */
1541
	fn->blocks[ENTRY_BLOCK].num_pred = ~(unsigned)0;
1542

1543
      if (fn->blocks[EXIT_BLOCK].num_succ)
1544
	fnotice (stderr, "%s:'%s' has arcs from exit block\n",
1545 1546 1547 1548
		 bbg_file_name, fn->name);
      else
	/* Likewise, we can't deduce exit block counts from the lack
	   of its successors.  */
1549
	fn->blocks[EXIT_BLOCK].num_succ = ~(unsigned)0;
Doug Evans committed
1550 1551
    }

1552 1553
  /* Propagate the measured counts, this must be done in the same
     order as the code in profile.c  */
1554
  for (ix = 0, blk = fn->blocks; ix != fn->num_blocks; ix++, blk++)
Doug Evans committed
1555
    {
1556 1557
      block_t const *prev_dst = NULL;
      int out_of_order = 0;
1558
      int non_fake_succ = 0;
1559

1560
      for (arc = blk->succ; arc; arc = arc->succ_next)
Doug Evans committed
1561
	{
1562 1563
	  if (!arc->fake)
	    non_fake_succ++;
1564

1565
	  if (!arc->on_tree)
Doug Evans committed
1566
	    {
1567 1568 1569
	      if (count_ptr)
		arc->count = *count_ptr++;
	      arc->count_valid = 1;
1570
	      blk->num_succ--;
1571
	      arc->dst->num_pred--;
Doug Evans committed
1572
	    }
1573 1574 1575
	  if (prev_dst && prev_dst > arc->dst)
	    out_of_order = 1;
	  prev_dst = arc->dst;
Doug Evans committed
1576
	}
1577 1578 1579 1580 1581 1582 1583 1584 1585
      if (non_fake_succ == 1)
	{
	  /* If there is only one non-fake exit, it is an
	     unconditional branch.  */
	  for (arc = blk->succ; arc; arc = arc->succ_next)
	    if (!arc->fake)
	      {
		arc->is_unconditional = 1;
		/* If this block is instrumenting a call, it might be
1586
		   an artificial block. It is not artificial if it has
1587 1588 1589 1590 1591 1592 1593
		   a non-fallthrough exit, or the destination of this
		   arc has more than one entry.  Mark the destination
		   block as a return site, if none of those conditions
		   hold.  */
		if (blk->is_call_site && arc->fall_through
		    && arc->dst->pred == arc && !arc->pred_next)
		  arc->dst->is_call_return = 1;
1594 1595
	      }
	}
1596

1597 1598 1599
      /* Sort the successor arcs into ascending dst order. profile.c
	 normally produces arcs in the right order, but sometimes with
	 one or two out of order.  We're not using a particularly
1600
	 smart sort.  */
1601
      if (out_of_order)
Doug Evans committed
1602
	{
1603
	  arc_t *start = blk->succ;
1604
	  unsigned changes = 1;
1605

1606 1607 1608
	  while (changes)
	    {
	      arc_t *arc, *arc_p, *arc_n;
1609

1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630
	      changes = 0;
	      for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);)
		{
		  if (arc->dst > arc_n->dst)
		    {
		      changes = 1;
		      if (arc_p)
			arc_p->succ_next = arc_n;
		      else
			start = arc_n;
		      arc->succ_next = arc_n->succ_next;
		      arc_n->succ_next = arc;
		      arc_p = arc_n;
		    }
		  else
		    {
		      arc_p = arc;
		      arc = arc_n;
		    }
		}
	    }
1631
	  blk->succ = start;
Doug Evans committed
1632
	}
1633

1634 1635
      /* Place it on the invalid chain, it will be ignored if that's
	 wrong.  */
1636 1637 1638
      blk->invalid_chain = 1;
      blk->chain = invalid_blocks;
      invalid_blocks = blk;
1639 1640 1641 1642 1643
    }

  while (invalid_blocks || valid_blocks)
    {
      while ((blk = invalid_blocks))
Doug Evans committed
1644
	{
1645 1646
	  gcov_type total = 0;
	  const arc_t *arc;
1647

1648 1649 1650 1651 1652 1653 1654 1655 1656 1657
	  invalid_blocks = blk->chain;
	  blk->invalid_chain = 0;
	  if (!blk->num_succ)
	    for (arc = blk->succ; arc; arc = arc->succ_next)
	      total += arc->count;
	  else if (!blk->num_pred)
	    for (arc = blk->pred; arc; arc = arc->pred_next)
	      total += arc->count;
	  else
	    continue;
1658

1659 1660 1661 1662 1663
	  blk->count = total;
	  blk->count_valid = 1;
	  blk->chain = valid_blocks;
	  blk->valid_chain = 1;
	  valid_blocks = blk;
Doug Evans committed
1664
	}
1665
      while ((blk = valid_blocks))
Doug Evans committed
1666
	{
1667 1668 1669 1670 1671 1672 1673 1674
	  gcov_type total;
	  arc_t *arc, *inv_arc;

	  valid_blocks = blk->chain;
	  blk->valid_chain = 0;
	  if (blk->num_succ == 1)
	    {
	      block_t *dst;
1675

1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710
	      total = blk->count;
	      inv_arc = NULL;
	      for (arc = blk->succ; arc; arc = arc->succ_next)
		{
		  total -= arc->count;
		  if (!arc->count_valid)
		    inv_arc = arc;
		}
	      dst = inv_arc->dst;
	      inv_arc->count_valid = 1;
	      inv_arc->count = total;
	      blk->num_succ--;
	      dst->num_pred--;
	      if (dst->count_valid)
		{
		  if (dst->num_pred == 1 && !dst->valid_chain)
		    {
		      dst->chain = valid_blocks;
		      dst->valid_chain = 1;
		      valid_blocks = dst;
		    }
		}
	      else
		{
		  if (!dst->num_pred && !dst->invalid_chain)
		    {
		      dst->chain = invalid_blocks;
		      dst->invalid_chain = 1;
		      invalid_blocks = dst;
		    }
		}
	    }
	  if (blk->num_pred == 1)
	    {
	      block_t *src;
1711

1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743
	      total = blk->count;
	      inv_arc = NULL;
	      for (arc = blk->pred; arc; arc = arc->pred_next)
		{
		  total -= arc->count;
		  if (!arc->count_valid)
		    inv_arc = arc;
		}
	      src = inv_arc->src;
	      inv_arc->count_valid = 1;
	      inv_arc->count = total;
	      blk->num_pred--;
	      src->num_succ--;
	      if (src->count_valid)
		{
		  if (src->num_succ == 1 && !src->valid_chain)
		    {
		      src->chain = valid_blocks;
		      src->valid_chain = 1;
		      valid_blocks = src;
		    }
		}
	      else
		{
		  if (!src->num_succ && !src->invalid_chain)
		    {
		      src->chain = invalid_blocks;
		      src->invalid_chain = 1;
		      invalid_blocks = src;
		    }
		}
	    }
Doug Evans committed
1744 1745
	}
    }
1746

1747 1748 1749 1750 1751
  /* If the graph has been correctly solved, every block will have a
     valid count.  */
  for (ix = 0; ix < fn->num_blocks; ix++)
    if (!fn->blocks[ix].count_valid)
      {
1752
	fnotice (stderr, "%s:graph is unsolvable for '%s'\n",
1753 1754 1755
		 bbg_file_name, fn->name);
	break;
      }
Doug Evans committed
1756
}
1757

1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785
/* Mark all the blocks only reachable via an incoming catch.  */

static void
find_exception_blocks (function_t *fn)
{
  unsigned ix;
  block_t **queue = XALLOCAVEC (block_t *, fn->num_blocks);

  /* First mark all blocks as exceptional.  */
  for (ix = fn->num_blocks; ix--;)
    fn->blocks[ix].exceptional = 1;

  /* Now mark all the blocks reachable via non-fake edges */
  queue[0] = fn->blocks;
  queue[0]->exceptional = 0;
  for (ix = 1; ix;)
    {
      block_t *block = queue[--ix];
      const arc_t *arc;
      
      for (arc = block->succ; arc; arc = arc->succ_next)
	if (!arc->fake && !arc->is_throw && arc->dst->exceptional)
	  {
	    arc->dst->exceptional = 0;
	    queue[ix++] = arc->dst;
	  }
    }
}
Doug Evans committed
1786 1787


1788
/* Increment totals in COVERAGE according to arc ARC.  */
Nathan Sidwell committed
1789 1790

static void
1791
add_branch_counts (coverage_t *coverage, const arc_t *arc)
Nathan Sidwell committed
1792
{
1793
  if (arc->is_call_non_return)
Nathan Sidwell committed
1794
    {
1795 1796 1797
      coverage->calls++;
      if (arc->src->count)
	coverage->calls_executed++;
Nathan Sidwell committed
1798
    }
1799
  else if (!arc->is_unconditional)
Nathan Sidwell committed
1800
    {
1801 1802 1803 1804 1805
      coverage->branches++;
      if (arc->src->count)
	coverage->branches_executed++;
      if (arc->count)
	coverage->branches_taken++;
Doug Evans committed
1806 1807 1808
    }
}

1809
/* Format a GCOV_TYPE integer as either a percent ratio, or absolute
1810 1811 1812 1813 1814 1815
   count.  If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places.
   If DP is zero, no decimal point is printed. Only print 100% when
   TOP==BOTTOM and only print 0% when TOP=0.  If dp < 0, then simply
   format TOP.  Return pointer to a static string.  */

static char const *
1816
format_gcov (gcov_type top, gcov_type bottom, int dp)
1817 1818
{
  static char buffer[20];
1819

1820 1821 1822 1823 1824 1825
  if (dp >= 0)
    {
      float ratio = bottom ? (float)top / bottom : 0;
      int ix;
      unsigned limit = 100;
      unsigned percent;
1826

1827 1828
      for (ix = dp; ix--; )
	limit *= 10;
1829

1830 1831
      percent = (unsigned) (ratio * limit + (float)0.5);
      if (percent <= 0 && top)
1832
	percent = 1;
1833
      else if (percent >= limit && top != bottom)
1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848
	percent = limit - 1;
      ix = sprintf (buffer, "%.*u%%", dp + 1, percent);
      if (dp)
	{
	  dp++;
	  do
	    {
	      buffer[ix+1] = buffer[ix];
	      ix--;
	    }
	  while (dp--);
	  buffer[ix + 1] = '.';
	}
    }
  else
1849
    sprintf (buffer, "%"PRId64, (int64_t)top);
1850

1851 1852 1853
  return buffer;
}

1854
/* Summary of execution */
Doug Evans committed
1855 1856

static void
1857
executed_summary (unsigned lines, unsigned executed)
Doug Evans committed
1858
{
1859
  if (lines)
1860
    fnotice (stdout, "Lines executed:%s of %d\n",
1861
	     format_gcov (executed, lines, 2), lines);
Doug Evans committed
1862
  else
1863
    fnotice (stdout, "No executable lines\n");
1864 1865 1866 1867 1868 1869 1870 1871 1872
}
  
/* Output summary info for a function or file.  */

static void
function_summary (const coverage_t *coverage, const char *title)
{
  fnotice (stdout, "%s '%s'\n", title, coverage->name);
  executed_summary (coverage->lines, coverage->lines_executed);
Doug Evans committed
1873

1874
  if (flag_branches)
Doug Evans committed
1875
    {
1876
      if (coverage->branches)
Doug Evans committed
1877
	{
1878 1879 1880 1881 1882 1883 1884 1885
	  fnotice (stdout, "Branches executed:%s of %d\n",
		   format_gcov (coverage->branches_executed,
				coverage->branches, 2),
		   coverage->branches);
	  fnotice (stdout, "Taken at least once:%s of %d\n",
		   format_gcov (coverage->branches_taken,
				coverage->branches, 2),
		   coverage->branches);
Doug Evans committed
1886 1887
	}
      else
1888 1889 1890 1891 1892
	fnotice (stdout, "No branches\n");
      if (coverage->calls)
	fnotice (stdout, "Calls executed:%s of %d\n",
		 format_gcov (coverage->calls_executed, coverage->calls, 2),
		 coverage->calls);
Doug Evans committed
1893
      else
1894
	fnotice (stdout, "No calls\n");
Nathan Sidwell committed
1895 1896 1897
    }
}

1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936
/* Canonicalize the filename NAME by canonicalizing directory
   separators, eliding . components and resolving .. components
   appropriately.  Always returns a unique string.  */

static char *
canonicalize_name (const char *name)
{
  /* The canonical name cannot be longer than the incoming name.  */
  char *result = XNEWVEC (char, strlen (name) + 1);
  const char *base = name, *probe;
  char *ptr = result;
  char *dd_base;
  int slash = 0;

#if HAVE_DOS_BASED_FILE_SYSTEM
  if (base[0] && base[1] == ':')
    {
      result[0] = base[0];
      result[1] = ':';
      base += 2;
      ptr += 2;
    }
#endif
  for (dd_base = ptr; *base; base = probe)
    {
      size_t len;
      
      for (probe = base; *probe; probe++)
	if (IS_DIR_SEPARATOR (*probe))
	  break;

      len = probe - base;
      if (len == 1 && base[0] == '.')
	/* Elide a '.' directory */
	;
      else if (len == 2 && base[0] == '.' && base[1] == '.')
	{
	  /* '..', we can only elide it and the previous directory, if
	     we're not a symlink.  */
1937 1938
	  struct stat ATTRIBUTE_UNUSED buf;

1939
	  *ptr = 0;
1940 1941 1942 1943 1944 1945
	  if (dd_base == ptr
#if defined (S_ISLNK)
	      /* S_ISLNK is not POSIX.1-1996.  */
	      || stat (result, &buf) || S_ISLNK (buf.st_mode)
#endif
	      )
1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976
	    {
	      /* Cannot elide, or unreadable or a symlink.  */
	      dd_base = ptr + 2 + slash;
	      goto regular;
	    }
	  while (ptr != dd_base && *ptr != '/')
	    ptr--;
	  slash = ptr != result;
	}
      else
	{
	regular:
	  /* Regular pathname component.  */
	  if (slash)
	    *ptr++ = '/';
	  memcpy (ptr, base, len);
	  ptr += len;
	  slash = 1;
	}

      for (; IS_DIR_SEPARATOR (*probe); probe++)
	continue;
    }
  *ptr = 0;

  return result;
}

/* Generate an output file name. INPUT_NAME is the canonicalized main
   input file and SRC_NAME is the canonicalized file name.
   LONG_OUTPUT_NAMES and PRESERVE_PATHS affect name generation.  With
Nathan Sidwell committed
1977 1978 1979
   long_output_names we prepend the processed name of the input file
   to each output name (except when the current source file is the
   input file, so you don't get a double concatenation). The two
1980 1981 1982 1983 1984
   components are separated by '##'.  With preserve_paths we create a
   filename from all path components of the source file, replacing '/'
   with '#', and .. with '^', without it we simply take the basename
   component.  (Remember, the canonicalized name will already have
   elided '.' components and converted \\ separators.)  */
Nathan Sidwell committed
1985 1986

static char *
1987
make_gcov_file_name (const char *input_name, const char *src_name)
Nathan Sidwell committed
1988
{
1989 1990
  char *ptr;
  char *result;
1991

1992
  if (flag_long_names && input_name && strcmp (src_name, input_name))
Nathan Sidwell committed
1993 1994
    {
      /* Generate the input filename part.  */
1995 1996 1997 1998 1999 2000
      result = XNEWVEC (char, strlen (input_name) + strlen (src_name) + 10);
  
      ptr = result;
      ptr = mangle_name (input_name, ptr);
      ptr[0] = ptr[1] = '#';
      ptr += 2;
Nathan Sidwell committed
2001
    }
2002 2003
  else
    {
2004 2005
      result = XNEWVEC (char, strlen (src_name) + 10);
      ptr = result;
2006
    }
2007

2008 2009 2010 2011 2012
  ptr = mangle_name (src_name, ptr);
  strcpy (ptr, ".gcov");
  
  return result;
}
2013

2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027
static char *
mangle_name (char const *base, char *ptr)
{
  size_t len;
  
  /* Generate the source filename part.  */
  if (!flag_preserve_paths)
    {
      base = lbasename (base);
      len = strlen (base);
      memcpy (ptr, base, len);
      ptr += len;
    }
  else
Nathan Sidwell committed
2028
    {
2029
      /* Convert '/' to '#', convert '..' to '^',
2030
	 convert ':' to '~' on DOS based file system.  */
2031
      const char *probe;
2032

2033 2034
#if HAVE_DOS_BASED_FILE_SYSTEM
      if (base[0] && base[1] == ':')
2035
	{
2036 2037 2038 2039 2040
	  ptr[0] = base[0];
	  ptr[1] = '~';
	  ptr += 2;
	  base += 2;
	}
2041
#endif
2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052
      for (; *base; base = probe)
	{
	  size_t len;

	  for (probe = base; *probe; probe++)
	    if (*probe == '/')
	      break;
	  len = probe - base;
	  if (len == 2 && base[0] == '.' && base[1] == '.')
	    *ptr++ = '^';
	  else
2053
	    {
2054 2055
	      memcpy (ptr, base, len);
	      ptr += len;
2056
	    }
2057
	  if (*probe)
Kai Tietz committed
2058
	    {
2059 2060
	      *ptr++ = '#';
	      probe++;
Kai Tietz committed
2061
	    }
2062
	}
Doug Evans committed
2063
    }
2064 2065
  
  return ptr;
Doug Evans committed
2066 2067
}

2068
/* Scan through the bb_data for each line in the block, increment
Nathan Sidwell committed
2069 2070
   the line number execution count indicated by the execution count of
   the appropriate basic block.  */
Doug Evans committed
2071 2072

static void
2073
add_line_counts (coverage_t *coverage, function_t *fn)
Doug Evans committed
2074
{
2075
  unsigned ix;
2076
  line_t *line = NULL; /* This is propagated from one iteration to the
2077 2078
			  next.  */

2079
  /* Scan each basic block.  */
2080
  for (ix = 0; ix != fn->num_blocks; ix++)
Doug Evans committed
2081
    {
2082
      block_t *block = &fn->blocks[ix];
2083 2084 2085 2086
      unsigned *encoding;
      const source_t *src = NULL;
      unsigned jx;

2087 2088 2089 2090
      if (block->count && ix && ix + 1 != fn->num_blocks)
	fn->blocks_executed++;
      for (jx = 0, encoding = block->u.line.encoding;
	   jx != block->u.line.num; jx++, encoding++)
2091 2092
	if (!*encoding)
	  {
2093
	    src = &sources[*++encoding];
2094 2095 2096 2097 2098 2099 2100 2101 2102 2103
	    jx++;
	  }
	else
	  {
	    line = &src->lines[*encoding];

	    if (coverage)
	      {
		if (!line->exists)
		  coverage->lines++;
2104
		if (!line->count && block->count)
2105 2106 2107
		  coverage->lines_executed++;
	      }
	    line->exists = 1;
2108 2109
	    if (!block->exceptional)
	      line->unexceptional = 1;
2110 2111
	    line->count += block->count;
	  }
2112
      free (block->u.line.encoding);
2113 2114
      block->u.cycle.arc = NULL;
      block->u.cycle.ident = ~0U;
2115

2116 2117 2118
      if (!ix || ix + 1 == fn->num_blocks)
	/* Entry or exit block */;
      else if (flag_all_blocks)
Doug Evans committed
2119
	{
2120 2121 2122 2123
	  line_t *block_line = line;

	  if (!block_line)
	    block_line = &sources[fn->src].lines[fn->line];
2124

2125 2126
	  block->chain = block_line->u.blocks;
	  block_line->u.blocks = block;
2127 2128 2129 2130 2131
	}
      else if (flag_branches)
	{
	  arc_t *arc;

2132
	  for (arc = block->succ; arc; arc = arc->succ_next)
Doug Evans committed
2133
	    {
2134 2135 2136
	      arc->line_next = line->u.branches;
	      line->u.branches = arc;
	      if (coverage && !arc->is_unconditional)
2137
		add_branch_counts (coverage, arc);
Doug Evans committed
2138
	    }
Nathan Sidwell committed
2139 2140
	}
    }
2141
  if (!line)
2142
    fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name);
2143 2144
}

2145
/* Accumulate the line counts of a file.  */
2146 2147

static void
2148
accumulate_line_counts (source_t *src)
2149 2150
{
  line_t *line;
2151
  function_t *fn, *fn_p, *fn_n;
2152
  unsigned ix;
2153 2154 2155 2156 2157 2158 2159 2160 2161

  /* Reverse the function order.  */
  for (fn = src->functions, fn_p = NULL; fn;
       fn_p = fn, fn = fn_n)
    {
      fn_n = fn->line_next;
      fn->line_next = fn_p;
    }
  src->functions = fn_p;
2162

2163
  for (ix = src->num_lines, line = src->lines; ix--; line++)
Nathan Sidwell committed
2164
    {
2165
      if (!flag_all_blocks)
Nathan Sidwell committed
2166
	{
2167
	  arc_t *arc, *arc_p, *arc_n;
2168

2169 2170 2171 2172 2173 2174
	  /* Total and reverse the branch information.  */
	  for (arc = line->u.branches, arc_p = NULL; arc;
	       arc_p = arc, arc = arc_n)
	    {
	      arc_n = arc->line_next;
	      arc->line_next = arc_p;
2175

2176 2177 2178
	      add_branch_counts (&src->coverage, arc);
	    }
	  line->u.branches = arc_p;
Nathan Sidwell committed
2179
	}
2180 2181 2182 2183 2184
      else if (line->u.blocks)
	{
	  /* The user expects the line count to be the number of times
	     a line has been executed. Simply summing the block count
	     will give an artificially high number.  The Right Thing
2185 2186 2187
	     is to sum the entry counts to the graph of blocks on this
	     line, then find the elementary cycles of the local graph
	     and add the transition counts of those cycles.  */
2188 2189
	  block_t *block, *block_p, *block_n;
	  gcov_type count = 0;
2190

2191
	  /* Reverse the block information.  */
2192 2193 2194 2195 2196
	  for (block = line->u.blocks, block_p = NULL; block;
	       block_p = block, block = block_n)
	    {
	      block_n = block->chain;
	      block->chain = block_p;
2197
	      block->u.cycle.ident = ix;
2198 2199
	    }
	  line->u.blocks = block_p;
2200

2201 2202 2203 2204
	  /* Sum the entry arcs.  */
	  for (block = line->u.blocks; block; block = block->chain)
	    {
	      arc_t *arc;
Doug Evans committed
2205

2206 2207 2208 2209 2210 2211 2212
	      for (arc = block->pred; arc; arc = arc->pred_next)
		{
		  if (arc->src->u.cycle.ident != ix)
		    count += arc->count;
		  if (flag_branches)
		    add_branch_counts (&src->coverage, arc);
		}
2213 2214 2215 2216

	      /* Initialize the cs_count.  */
	      for (arc = block->succ; arc; arc = arc->succ_next)
		arc->cs_count = arc->count;
2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232
	    }

	  /* Find the loops. This uses the algorithm described in
	     Tiernan 'An Efficient Search Algorithm to Find the
	     Elementary Circuits of a Graph', CACM Dec 1970. We hold
	     the P array by having each block point to the arc that
	     connects to the previous block. The H array is implicitly
	     held because of the arc ordering, and the block's
	     previous arc pointer.

	     Although the algorithm is O(N^3) for highly connected
	     graphs, at worst we'll have O(N^2), as most blocks have
	     only one or two exits. Most graphs will be small.

	     For each loop we find, locate the arc with the smallest
	     transition count, and add that to the cumulative
2233 2234
	     count.  Decrease flow over the cycle and remove the arc
	     from consideration.  */
2235
	  for (block = line->u.blocks; block; block = block->chain)
2236
	    {
2237 2238
	      block_t *head = block;
	      arc_t *arc;
2239

2240 2241 2242 2243
	    next_vertex:;
	      arc = head->succ;
	    current_vertex:;
	      while (arc)
2244
		{
2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255
		  block_t *dst = arc->dst;
		  if (/* Already used that arc.  */
		      arc->cycle
		      /* Not to same graph, or before first vertex.  */
		      || dst->u.cycle.ident != ix
		      /* Already in path.  */
		      || dst->u.cycle.arc)
		    {
		      arc = arc->succ_next;
		      continue;
		    }
2256

2257
		  if (dst == block)
2258
		    {
2259
		      /* Found a closing arc.  */
2260
		      gcov_type cycle_count = arc->cs_count;
2261 2262
		      arc_t *cycle_arc = arc;
		      arc_t *probe_arc;
2263

2264
		      /* Locate the smallest arc count of the loop.  */
2265 2266
		      for (dst = head; (probe_arc = dst->u.cycle.arc);
			   dst = probe_arc->src)
2267
			if (cycle_count > probe_arc->cs_count)
2268
			  {
2269
			    cycle_count = probe_arc->cs_count;
2270 2271
			    cycle_arc = probe_arc;
			  }
2272

2273 2274
		      count += cycle_count;
		      cycle_arc->cycle = 1;
2275 2276 2277 2278 2279 2280 2281

		      /* Remove the flow from the cycle.  */
		      arc->cs_count -= cycle_count;
		      for (dst = head; (probe_arc = dst->u.cycle.arc);
			   dst = probe_arc->src)
			probe_arc->cs_count -= cycle_count;

2282 2283
		      /* Unwind to the cyclic arc.  */
		      while (head != cycle_arc->src)
2284
			{
2285
			  arc = head->u.cycle.arc;
2286
			  head->u.cycle.arc = NULL;
2287
			  head = arc->src;
2288
			}
2289 2290 2291
		      /* Move on.  */
		      arc = arc->succ_next;
		      continue;
2292
		    }
2293

2294 2295 2296 2297
		  /* Add new block to chain.  */
		  dst->u.cycle.arc = arc;
		  head = dst;
		  goto next_vertex;
2298
		}
2299 2300 2301 2302
	      /* We could not add another vertex to the path. Remove
		 the last vertex from the list.  */
	      arc = head->u.cycle.arc;
	      if (arc)
2303
		{
2304
		  /* It was not the first vertex. Move onto next arc.  */
2305 2306 2307 2308
		  head->u.cycle.arc = NULL;
		  head = arc->src;
		  arc = arc->succ_next;
		  goto current_vertex;
2309
		}
2310 2311
	      /* Mark this block as unusable.  */
	      block->u.cycle.ident = ~0U;
2312
	    }
2313

2314 2315
	  line->count = count;
	}
2316

2317
      if (line->exists)
Nathan Sidwell committed
2318
	{
2319 2320 2321
	  src->coverage.lines++;
	  if (line->count)
	    src->coverage.lines_executed++;
Nathan Sidwell committed
2322 2323 2324
	}
    }
}
Doug Evans committed
2325

2326
/* Output information about ARC number IX.  Returns nonzero if
2327 2328 2329
   anything is output.  */

static int
2330
output_branch_count (FILE *gcov_file, int ix, const arc_t *arc)
2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347
{
  if (arc->is_call_non_return)
    {
      if (arc->src->count)
	{
	  fnotice (gcov_file, "call   %2d returned %s\n", ix,
		   format_gcov (arc->src->count - arc->count,
				arc->src->count, -flag_counts));
	}
      else
	fnotice (gcov_file, "call   %2d never executed\n", ix);
    }
  else if (!arc->is_unconditional)
    {
      if (arc->src->count)
	fnotice (gcov_file, "branch %2d taken %s%s\n", ix,
		 format_gcov (arc->count, arc->src->count, -flag_counts),
2348 2349
		 arc->fall_through ? " (fallthrough)"
		 : arc->is_throw ? " (throw)" : "");
2350 2351 2352
      else
	fnotice (gcov_file, "branch %2d never executed\n", ix);
    }
2353
  else if (flag_unconditional && !arc->dst->is_call_return)
2354 2355 2356 2357 2358 2359 2360 2361 2362 2363
    {
      if (arc->src->count)
	fnotice (gcov_file, "unconditional %2d taken %s\n", ix,
		 format_gcov (arc->count, arc->src->count, -flag_counts));
      else
	fnotice (gcov_file, "unconditional %2d never executed\n", ix);
    }
  else
    return 0;
  return 1;
2364

2365 2366
}

2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390
static const char *
read_line (FILE *file)
{
  static char *string;
  static size_t string_len;
  size_t pos = 0;
  char *ptr;

  if (!string_len)
    {
      string_len = 200;
      string = XNEWVEC (char, string_len);
    }

  while ((ptr = fgets (string + pos, string_len - pos, file)))
    {
      size_t len = strlen (string + pos);

      if (string[pos + len - 1] == '\n')
	{
	  string[pos + len - 1] = 0;
	  return string;
	}
      pos += len;
2391 2392
      string = XRESIZEVEC (char, string, string_len * 2);
      string_len *= 2;
2393 2394 2395 2396 2397
    }
      
  return pos ? string : NULL;
}

Nathan Sidwell committed
2398 2399 2400
/* Read in the source file one line at a time, and output that line to
   the gcov file preceded by its execution count and other
   information.  */
Doug Evans committed
2401

Nathan Sidwell committed
2402
static void
2403
output_lines (FILE *gcov_file, const source_t *src)
Nathan Sidwell committed
2404 2405
{
  FILE *source_file;
2406
  unsigned line_num;	/* current line number.  */
2407
  const line_t *line;           /* current line info ptr.  */
2408
  const char *retval = "";	/* status of source file reading.  */
2409
  function_t *fn = NULL;
2410

2411
  fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->coverage.name);
2412 2413 2414 2415 2416
  if (!multiple_files)
    {
      fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name);
      fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0,
	       no_data_file ? "-" : da_file_name);
2417
      fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0, object_runs);
2418
    }
2419
  fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count);
2420

2421
  source_file = fopen (src->name, "r");
Nathan Sidwell committed
2422 2423
  if (!source_file)
    {
2424
      fnotice (stderr, "Cannot open source file %s\n", src->name);
Nathan Sidwell committed
2425 2426
      retval = NULL;
    }
2427 2428
  else if (src->file_time == 0)
    fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n", "-", 0);
Nathan Sidwell committed
2429

2430 2431 2432
  if (flag_branches)
    fn = src->functions;

2433 2434
  for (line_num = 1, line = &src->lines[line_num];
       line_num < src->num_lines; line_num++, line++)
Nathan Sidwell committed
2435
    {
2436 2437
      for (; fn && fn->line == line_num; fn = fn->line_next)
	{
2438 2439 2440
	  arc_t *arc = fn->blocks[EXIT_BLOCK].pred;
	  gcov_type return_count = fn->blocks[EXIT_BLOCK].count;
	  gcov_type called_count = fn->blocks[ENTRY_BLOCK].count;
H.J. Lu committed
2441

2442 2443 2444
	  for (; arc; arc = arc->pred_next)
	    if (arc->fake)
	      return_count -= arc->count;
H.J. Lu committed
2445

2446 2447
	  fprintf (gcov_file, "function %s", flag_demangled_names ?
                   fn->demangled_name : fn->name);
2448
	  fprintf (gcov_file, " called %s",
2449
		   format_gcov (called_count, 0, -1));
2450
	  fprintf (gcov_file, " returned %s",
2451
		   format_gcov (return_count, called_count, 0));
2452 2453 2454 2455
	  fprintf (gcov_file, " blocks executed %s",
		   format_gcov (fn->blocks_executed, fn->num_blocks - 2, 0));
	  fprintf (gcov_file, "\n");
	}
2456

2457 2458 2459
      if (retval)
	retval = read_line (source_file);

Nathan Sidwell committed
2460
      /* For lines which don't exist in the .bb file, print '-' before
2461
	 the source line.  For lines which exist but were never
2462 2463 2464 2465 2466
	 executed, print '#####' or '=====' before the source line.
	 Otherwise, print the execution count before the source line.
	 There are 16 spaces of indentation added before the source
	 line so that tabs won't be messed up.  */
      fprintf (gcov_file, "%9s:%5u:%s\n",
2467 2468
	       !line->exists ? "-" : line->count
	       ? format_gcov (line->count, 0, -1)
2469 2470
	       : line->unexceptional ? "#####" : "=====", line_num,
	       retval ? retval : "/*EOF*/");
2471 2472

      if (flag_all_blocks)
Nathan Sidwell committed
2473
	{
2474
	  block_t *block;
2475
	  arc_t *arc;
2476
	  int ix, jx;
2477

2478 2479
	  for (ix = jx = 0, block = line->u.blocks; block;
	       block = block->chain)
2480
	    {
2481
	      if (!block->is_call_return)
2482
		fprintf (gcov_file, "%9s:%5u-block %2d\n",
2483 2484 2485
			 !line->exists ? "-" : block->count
			 ? format_gcov (block->count, 0, -1)
			 : block->exceptional ? "%%%%%" : "$$$$$",
2486
			 line_num, ix++);
2487 2488 2489
	      if (flag_branches)
		for (arc = block->succ; arc; arc = arc->succ_next)
		  jx += output_branch_count (gcov_file, jx, arc);
Doug Evans committed
2490
	    }
Nathan Sidwell committed
2491
	}
2492 2493 2494 2495
      else if (flag_branches)
	{
	  int ix;
	  arc_t *arc;
2496

2497 2498 2499
	  for (ix = 0, arc = line->u.branches; arc; arc = arc->line_next)
	    ix += output_branch_count (gcov_file, ix, arc);
	}
Nathan Sidwell committed
2500
    }
2501

Nathan Sidwell committed
2502 2503 2504 2505
  /* Handle all remaining source lines.  There may be lines after the
     last line of code.  */
  if (retval)
    {
2506 2507
      for (; (retval = read_line (source_file)); line_num++)
	fprintf (gcov_file, "%9s:%5u:%s\n", "-", line_num, retval);
Nathan Sidwell committed
2508
    }
2509

Nathan Sidwell committed
2510 2511 2512
  if (source_file)
    fclose (source_file);
}