arm-builtins.c (enum arm_type_qualifiers): Add qualifier_lane_pair_index.
[gcc.git] / gcc / gcov.c
index 846a232619647defb78bde94e2ce546e47f0d474..b8ce1ee0e096eb90d1a43dc0a5d5bf24c8be86f3 100644 (file)
@@ -1,6 +1,6 @@
 /* Gcov.c: prepend line execution counts and branch probabilities to a
    source file.
-   Copyright (C) 1990-2017 Free Software Foundation, Inc.
+   Copyright (C) 1990-2019 Free Software Foundation, Inc.
    Contributed by James E. Wilson of Cygnus Support.
    Mangled by Bob Manson of Cygnus Support.
    Mangled further by Nathan Sidwell <nathan@codesourcery.com>
@@ -44,7 +44,10 @@ along with Gcov; see the file COPYING3.  If not see
 #include "version.h"
 #include "demangle.h"
 #include "color-macros.h"
+#include "pretty-print.h"
+#include "json.h"
 
+#include <zlib.h>
 #include <getopt.h>
 
 #include "md5.h"
@@ -79,7 +82,7 @@ struct source_info;
 
 /* Describes an arc between two basic blocks.  */
 
-typedef struct arc_info
+struct arc_info
 {
   /* source and destination blocks.  */
   struct block_info *src;
@@ -113,7 +116,7 @@ typedef struct arc_info
   /* Links to next arc on src and dst lists.  */
   struct arc_info *succ_next;
   struct arc_info *pred_next;
-} arc_t;
+};
 
 /* Describes which locations (lines and files) are associated with
    a basic block.  */
@@ -131,14 +134,14 @@ struct block_location_info
 /* Describes a basic block. Contains lists of arcs to successor and
    predecessor blocks.  */
 
-typedef struct block_info
+struct block_info
 {
   /* Constructor.  */
   block_info ();
 
   /* Chain of exit and entry arcs.  */
-  arc_t *succ;
-  arc_t *pred;
+  arc_info *succ;
+  arc_info *pred;
 
   /* Number of unprocessed exit and entry arcs.  */
   gcov_type num_succ;
@@ -166,7 +169,7 @@ typedef struct block_info
   {
     /* Single line graph cycle workspace.  Used for all-blocks
        mode.  */
-    arc_t *arc;
+    arc_info *arc;
     unsigned ident;
   } cycle; /* Used in all-blocks mode, after blocks are linked onto
             lines.  */
@@ -175,7 +178,7 @@ typedef struct block_info
      line.  */
   struct block_info *chain;
 
-} block_t;
+};
 
 block_info::block_info (): succ (NULL), pred (NULL), num_succ (0), num_pred (0),
   id (0), count (0), count_valid (0), valid_chain (0), invalid_chain (0),
@@ -194,16 +197,16 @@ struct line_info
   line_info ();
 
   /* Return true when NEEDLE is one of basic blocks the line belongs to.  */
-  bool has_block (block_t *needle);
+  bool has_block (block_info *needle);
 
   /* Execution count.  */
   gcov_type count;
 
   /* Branches from blocks that end on this line.  */
-  vector<arc_t *> branches;
+  vector<arc_info *> branches;
 
   /* blocks which start on this line.  Used in all-blocks mode.  */
-  vector<block_t *> blocks;
+  vector<block_info *> blocks;
 
   unsigned exists : 1;
   unsigned unexceptional : 1;
@@ -216,14 +219,18 @@ line_info::line_info (): count (0), branches (), blocks (), exists (false),
 }
 
 bool
-line_info::has_block (block_t *needle)
+line_info::has_block (block_info *needle)
 {
   return std::find (blocks.begin (), blocks.end (), needle) != blocks.end ();
 }
 
+/* Output demangled function names.  */
+
+static int flag_demangled_names = 0;
+
 /* Describes a single function. Contains an array of basic blocks.  */
 
-typedef struct function_info
+struct function_info
 {
   function_info ();
   ~function_info ();
@@ -232,9 +239,17 @@ typedef struct function_info
      The line must be defined in body of the function, can't be inlined.  */
   bool group_line_p (unsigned n, unsigned src_idx);
 
+  /* Function filter based on function_info::artificial variable.  */
+
+  static inline bool
+  is_artificial (function_info *fn)
+  {
+    return fn->artificial;
+  }
+
   /* Name of function.  */
-  char *name;
-  char *demangled_name;
+  char *m_name;
+  char *m_demangled_name;
   unsigned ident;
   unsigned lineno_checksum;
   unsigned cfg_checksum;
@@ -253,12 +268,11 @@ typedef struct function_info
      at blocks[0] and the exit block is at blocks[1].  */
 #define ENTRY_BLOCK (0)
 #define EXIT_BLOCK (1)
-  vector<block_t> blocks;
+  vector<block_info> blocks;
   unsigned blocks_executed;
 
   /* Raw arc coverage counts.  */
-  gcov_type *counts;
-  unsigned num_counts;
+  vector<gcov_type> counts;
 
   /* First line number.  */
   unsigned start_line;
@@ -277,7 +291,33 @@ typedef struct function_info
 
   /* Next function.  */
   struct function_info *next;
-} function_t;
+
+  /*  Get demangled name of a function.  The demangled name
+      is converted when it is used for the first time.  */
+  char *get_demangled_name ()
+  {
+    if (m_demangled_name == NULL)
+      {
+       m_demangled_name = cplus_demangle (m_name, DMGL_PARAMS);
+       if (!m_demangled_name)
+         m_demangled_name = m_name;
+      }
+
+    return m_demangled_name;
+  }
+
+  /* Get name of the function based on flag_demangled_names.  */
+  char *get_name ()
+  {
+    return flag_demangled_names ? get_demangled_name () : m_name;
+  }
+
+  /* Return number of basic blocks (without entry and exit block).  */
+  unsigned get_block_count ()
+  {
+    return blocks.size () - 2;
+  }
+};
 
 /* Function info comparer that will sort functions according to starting
    line.  */
@@ -295,7 +335,7 @@ struct function_line_start_cmp
 
 /* Describes coverage of a file or function.  */
 
-typedef struct coverage_info
+struct coverage_info
 {
   int lines;
   int lines_executed;
@@ -308,7 +348,7 @@ typedef struct coverage_info
   int calls_executed;
 
   char *name;
-} coverage_t;
+};
 
 /* Describes a file mentioned in the block graph.  Contains an array
    of line info.  */
@@ -318,7 +358,10 @@ struct source_info
   /* Default constructor.  */
   source_info ();
 
-  vector<function_t *> get_functions_at_location (unsigned line_num) const;
+  vector<function_info *> *get_functions_at_location (unsigned line_num) const;
+
+  /* Register a new function.  */
+  void add_function (function_info *fn);
 
   /* Index of the source_info in sources vector.  */
   unsigned index;
@@ -330,33 +373,51 @@ struct source_info
   /* Vector of line information.  */
   vector<line_info> lines;
 
-  coverage_t coverage;
+  coverage_info coverage;
+
+  /* Maximum line count in the source file.  */
+  unsigned int maximum_count;
 
   /* Functions in this source file.  These are in ascending line
      number order.  */
-  vector <function_t *> functions;
+  vector<function_info *> functions;
+
+  /* Line number to functions map.  */
+  vector<vector<function_info *> *> line_to_function_map;
 };
 
 source_info::source_info (): index (0), name (NULL), file_time (),
-  lines (), coverage (), functions ()
+  lines (), coverage (), maximum_count (0), functions ()
 {
 }
 
-vector<function_t *>
-source_info::get_functions_at_location (unsigned line_num) const
+/* Register a new function.  */
+void
+source_info::add_function (function_info *fn)
 {
-  vector<function_t *> r;
+  functions.push_back (fn);
 
-  for (vector<function_t *>::const_iterator it = functions.begin ();
-       it != functions.end (); it++)
-    {
-      if ((*it)->start_line == line_num && (*it)->src == index)
-       r.push_back (*it);
-    }
+  if (fn->start_line >= line_to_function_map.size ())
+    line_to_function_map.resize (fn->start_line + 1);
 
-  std::sort (r.begin (), r.end (), function_line_start_cmp ());
+  vector<function_info *> **slot = &line_to_function_map[fn->start_line];
+  if (*slot == NULL)
+    *slot = new vector<function_info *> ();
 
-  return r;
+  (*slot)->push_back (fn);
+}
+
+vector<function_info *> *
+source_info::get_functions_at_location (unsigned line_num) const
+{
+  if (line_num >= line_to_function_map.size ())
+    return NULL;
+
+  vector<function_info *> *slot = line_to_function_map[line_num];
+  if (slot != NULL)
+    std::sort (slot->begin (), slot->end (), function_line_start_cmp ());
+
+  return slot;
 }
 
 class name_map
@@ -392,10 +453,11 @@ public:
   unsigned src;  /* Source file */
 };
 
-/* Holds a list of function basic block graphs.  */
+/* Vector of all functions.  */
+static vector<function_info *> functions;
 
-static function_t *functions;
-static function_t **fn_end = &functions;
+/* Function ident to function_info * map.  */
+static map<unsigned, function_info *> ident_to_fn;
 
 /* Vector of source files.  */
 static vector<source_info> sources;
@@ -403,10 +465,13 @@ static vector<source_info> sources;
 /* Mapping of file names to sources */
 static vector<name_map> names;
 
+/* Record all processed files in order to warn about
+   a file being read multiple times.  */
+static vector<char *> processed_files;
+
 /* This holds data summary information.  */
 
 static unsigned object_runs;
-static unsigned program_count;
 
 static unsigned total_lines;
 static unsigned total_executed;
@@ -424,6 +489,12 @@ static char *bbg_file_name;
 /* Stamp of the bbg file */
 static unsigned bbg_stamp;
 
+/* Supports has_unexecuted_blocks functionality.  */
+static unsigned bbg_supports_has_unexecuted_blocks;
+
+/* Working directory in which a TU was compiled.  */
+static const char *bbg_cwd;
+
 /* Name and file pointer of the input file for the count data (gcda).  */
 
 static char *da_file_name;
@@ -451,18 +522,18 @@ static int flag_unconditional = 0;
 
 static int flag_gcov_file = 1;
 
+/* Output to stdout instead to a gcov file.  */
+
+static int flag_use_stdout = 0;
+
 /* 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;
 
-/* Output *.gcov file in intermediate format used by 'lcov'.  */
-
-static int flag_intermediate_format = 0;
+/* Output *.gcov file in JSON intermediate format used by consumers.  */
 
-/* Output demangled function names.  */
-
-static int flag_demangled_names = 0;
+static int flag_json_format = 0;
 
 /* 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,
@@ -483,6 +554,10 @@ static int flag_verbose = 0;
 
 static int flag_use_colors = 0;
 
+/* Use perf-like colors to indicate hot lines.  */
+
+static int flag_use_hotness_colors = 0;
+
 /* Output count information for every basic block, not merely those
    that contain line number information.  */
 
@@ -528,33 +603,34 @@ 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 *);
+static void process_all_functions (void);
 static void generate_results (const char *);
 static void create_file_names (const char *);
 static char *canonicalize_name (const char *);
 static unsigned find_source (const char *);
-static function_t *read_graph_file (void);
-static int read_count_file (function_t *);
-static void solve_flow_graph (function_t *);
-static void find_exception_blocks (function_t *);
-static void add_branch_counts (coverage_t *, const arc_t *);
-static void add_line_counts (coverage_t *, function_t *);
+static void read_graph_file (void);
+static int read_count_file (void);
+static void solve_flow_graph (function_info *);
+static void find_exception_blocks (function_info *);
+static void add_branch_counts (coverage_info *, const arc_info *);
+static void add_line_counts (coverage_info *, function_info *);
 static void executed_summary (unsigned, unsigned);
-static void function_summary (const coverage_t *, const char *);
+static void function_summary (const coverage_info *, const char *);
 static const char *format_gcov (gcov_type, gcov_type, int);
 static void accumulate_line_counts (source_info *);
 static void output_gcov_file (const char *, source_info *);
-static int output_branch_count (FILE *, int, const arc_t *);
+static int output_branch_count (FILE *, int, const arc_info *);
 static void output_lines (FILE *, const source_info *);
 static char *make_gcov_file_name (const char *, const char *);
 static char *mangle_name (const char *, char *);
 static void release_structures (void);
 extern int main (int, char **);
 
-function_info::function_info (): name (NULL), demangled_name (NULL),
+function_info::function_info (): m_name (NULL), m_demangled_name (NULL),
   ident (0), lineno_checksum (0), cfg_checksum (0), has_catch (0),
   artificial (0), is_group (0),
-  blocks (), blocks_executed (0), counts (NULL), num_counts (0),
-  start_line (0), start_column (0), end_line (0), src (0), lines (), next (NULL)
+  blocks (), blocks_executed (0), counts (),
+  start_line (0), start_column (), end_line (0), src (0), lines (), next (NULL)
 {
 }
 
@@ -562,7 +638,7 @@ function_info::~function_info ()
 {
   for (int i = blocks.size () - 1; i >= 0; i--)
     {
-      arc_t *arc, *arc_n;
+      arc_info *arc, *arc_n;
 
       for (arc = blocks[i].succ; arc; arc = arc_n)
        {
@@ -570,10 +646,9 @@ function_info::~function_info ()
          free (arc);
        }
     }
-  free (counts);
-  if (flag_demangled_names && demangled_name != name)
-    free (demangled_name);
-  free (name);
+  if (m_demangled_name != m_name)
+    free (m_demangled_name);
+  free (m_name);
 }
 
 bool function_info::group_line_p (unsigned n, unsigned src_idx)
@@ -593,8 +668,8 @@ bool function_info::group_line_p (unsigned n, unsigned src_idx)
    simple paths)--the node is unblocked only when it participates in a cycle.
    */
 
-typedef vector<arc_t *> arc_vector_t;
-typedef vector<const block_t *> block_vector_t;
+typedef vector<arc_info *> arc_vector_t;
+typedef vector<const block_info *> block_vector_t;
 
 /* Enum with types of loop in CFG.  */
 
@@ -639,7 +714,7 @@ handle_cycle (const arc_vector_t &edges, int64_t &count)
    blocked by U in BLOCK_LISTS.  */
 
 static void
-unblock (const block_t *u, block_vector_t &blocked,
+unblock (const block_info *u, block_vector_t &blocked,
         vector<block_vector_t > &block_lists)
 {
   block_vector_t::iterator it = find (blocked.begin (), blocked.end (), u);
@@ -664,7 +739,7 @@ unblock (const block_t *u, block_vector_t &blocked,
    Returns what type of loop it contains.  */
 
 static loop_type
-circuit (block_t *v, arc_vector_t &path, block_t *start,
+circuit (block_info *v, arc_vector_t &path, block_info *start,
         block_vector_t &blocked, vector<block_vector_t> &block_lists,
         line_info &linfo, int64_t &count)
 {
@@ -675,9 +750,9 @@ circuit (block_t *v, arc_vector_t &path, block_t *start,
   blocked.push_back (v);
   block_lists.push_back (block_vector_t ());
 
-  for (arc_t *arc = v->succ; arc; arc = arc->succ_next)
+  for (arc_info *arc = v->succ; arc; arc = arc->succ_next)
     {
-      block_t *w = arc->dst;
+      block_info *w = arc->dst;
       if (w < start || !linfo.has_block (w))
        continue;
 
@@ -694,9 +769,9 @@ circuit (block_t *v, arc_vector_t &path, block_t *start,
   if (result != NO_LOOP)
     unblock (v, blocked, block_lists);
   else
-    for (arc_t *arc = v->succ; arc; arc = arc->succ_next)
+    for (arc_info *arc = v->succ; arc; arc = arc->succ_next)
       {
-       block_t *w = arc->dst;
+       block_info *w = arc->dst;
        if (w < start || !linfo.has_block (w))
          continue;
 
@@ -725,7 +800,7 @@ get_cycles_count (line_info &linfo, bool handle_negative_cycles = true)
 
   loop_type result = NO_LOOP;
   gcov_type count = 0;
-  for (vector<block_t *>::iterator it = linfo.blocks.begin ();
+  for (vector<block_info *>::iterator it = linfo.blocks.begin ();
        it != linfo.blocks.end (); it++)
     {
       arc_vector_t path;
@@ -781,11 +856,14 @@ main (int argc, char **argv)
        printf ("Processing file %d out of %d\n", argno - first_arg + 1,
                argc - first_arg);
       process_file (argv[argno]);
-    }
-
-  generate_results (multiple_files ? NULL : argv[argc - 1]);
 
-  release_structures ();
+      if (flag_json_format || argno == argc - 1)
+       {
+         process_all_functions ();
+         generate_results (argv[argno]);
+         release_structures ();
+       }
+    }
 
   return 0;
 }
@@ -808,7 +886,7 @@ print_usage (int error_p)
   fnotice (file, "  -d, --display-progress          Display progress information\n");
   fnotice (file, "  -f, --function-summaries        Output summaries for each function\n");
   fnotice (file, "  -h, --help                      Print this help, then exit\n");
-  fnotice (file, "  -i, --intermediate-format       Output .gcov file in intermediate text format\n");
+  fnotice (file, "  -i, --json-format              Output JSON intermediate format into .gcov.json.gz file\n");
   fnotice (file, "  -j, --human-readable            Output human readable numbers\n");
   fnotice (file, "  -k, --use-colors                Emit colored output\n");
   fnotice (file, "  -l, --long-file-names           Use long output file names for included\n\
@@ -817,8 +895,10 @@ print_usage (int error_p)
   fnotice (file, "  -n, --no-output                 Do not create an output file\n");
   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");
+  fnotice (file, "  -q, --use-hotness-colors        Emit perf-like colored output for hot lines\n");
   fnotice (file, "  -r, --relative-only             Only show data for relative sources\n");
   fnotice (file, "  -s, --source-prefix DIR         Source prefix to elide\n");
+  fnotice (file, "  -t, --stdout                    Output to stdout instead of a file\n");
   fnotice (file, "  -u, --unconditional-branches    Show unconditional branch counts too\n");
   fnotice (file, "  -v, --version                   Print version number, then exit\n");
   fnotice (file, "  -w, --verbose                   Print verbose informations\n");
@@ -834,7 +914,7 @@ static void
 print_version (void)
 {
   fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string);
-  fprintf (stdout, "Copyright %s 2017 Free Software Foundation, Inc.\n",
+  fprintf (stdout, "Copyright %s 2019 Free Software Foundation, Inc.\n",
           _("(C)"));
   fnotice (stdout,
           _("This is free software; see the source for copying conditions.\n"
@@ -851,7 +931,7 @@ static const struct option options[] =
   { "all-blocks",           no_argument,       NULL, 'a' },
   { "branch-probabilities", no_argument,       NULL, 'b' },
   { "branch-counts",        no_argument,       NULL, 'c' },
-  { "intermediate-format",  no_argument,       NULL, 'i' },
+  { "json-format",         no_argument,       NULL, 'i' },
   { "human-readable",      no_argument,       NULL, 'j' },
   { "no-output",            no_argument,       NULL, 'n' },
   { "long-file-names",      no_argument,       NULL, 'l' },
@@ -862,10 +942,12 @@ static const struct option options[] =
   { "object-directory",     required_argument, NULL, 'o' },
   { "object-file",          required_argument, NULL, 'o' },
   { "source-prefix",        required_argument, NULL, 's' },
+  { "stdout",              no_argument,       NULL, 't' },
   { "unconditional-branches", no_argument,     NULL, 'u' },
   { "display-progress",     no_argument,       NULL, 'd' },
   { "hash-filenames",      no_argument,       NULL, 'x' },
   { "use-colors",          no_argument,       NULL, 'k' },
+  { "use-hotness-colors",   no_argument,       NULL, 'q' },
   { 0, 0, 0, 0 }
 };
 
@@ -876,7 +958,7 @@ process_args (int argc, char **argv)
 {
   int opt;
 
-  const char *opts = "abcdfhijklmno:prs:uvwx";
+  const char *opts = "abcdfhijklmno:pqrs:tuvwx";
   while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1)
     {
       switch (opt)
@@ -905,6 +987,9 @@ process_args (int argc, char **argv)
        case 'k':
          flag_use_colors = 1;
          break;
+       case 'q':
+         flag_use_hotness_colors = 1;
+         break;
        case 'm':
          flag_demangled_names = 1;
          break;
@@ -928,18 +1013,21 @@ process_args (int argc, char **argv)
          flag_unconditional = 1;
          break;
        case 'i':
-          flag_intermediate_format = 1;
-          flag_gcov_file = 1;
-          break;
-        case 'd':
-          flag_display_progress = 1;
-          break;
+         flag_json_format = 1;
+         flag_gcov_file = 1;
+         break;
+       case 'd':
+         flag_display_progress = 1;
+         break;
        case 'x':
          flag_hash_filenames = 1;
          break;
        case 'w':
          flag_verbose = 1;
          break;
+       case 't':
+         flag_use_stdout = 1;
+         break;
        case 'v':
          print_version ();
          /* print_version will exit.  */
@@ -952,86 +1040,126 @@ process_args (int argc, char **argv)
   return optind;
 }
 
-/* Output intermediate LINE sitting on LINE_NUM to output file F.  */
+/* Output intermediate LINE sitting on LINE_NUM to JSON OBJECT.  */
 
 static void
-output_intermediate_line (FILE *f, line_info *line, unsigned line_num)
+output_intermediate_json_line (json::array *object,
+                              line_info *line, unsigned line_num)
 {
   if (!line->exists)
     return;
 
-  fprintf (f, "lcount:%u,%s,%d\n", line_num,
-          format_gcov (line->count, 0, -1),
-          line->has_unexecuted_block);
+  json::object *lineo = new json::object ();
+  lineo->set ("line_number", new json::number (line_num));
+  lineo->set ("count", new json::number (line->count));
+  lineo->set ("unexecuted_block",
+             new json::literal (line->has_unexecuted_block));
 
-  vector<arc_t *>::const_iterator it;
+  json::array *branches = new json::array ();
+  lineo->set ("branches", branches);
+
+  vector<arc_info *>::const_iterator it;
   if (flag_branches)
     for (it = line->branches.begin (); it != line->branches.end ();
         it++)
       {
        if (!(*it)->is_unconditional && !(*it)->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 ((*it)->src->count)
-                branch_type
-                       = ((*it)->count > 0) ? "taken" : "nottaken";
-           else
-             branch_type = "notexec";
-           fprintf (f, "branch:%d,%s\n", line_num, branch_type);
+           json::object *branch = new json::object ();
+           branch->set ("count", new json::number ((*it)->count));
+           branch->set ("throw", new json::literal ((*it)->is_throw));
+           branch->set ("fallthrough",
+                        new json::literal ((*it)->fall_through));
+           branches->append (branch);
          }
       }
+
+  object->append (lineo);
 }
 
-/* Output the result in intermediate format used by 'lcov'.
+/* 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.
 
-The intermediate format contains a single file named 'foo.cc.gcov',
-with no source code included.
+   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.json.gz";
+  char *result;
+  const char *cptr;
+
+  /* Find the 'basename'.  */
+  cptr = lbasename (file_name);
+
+  result = XNEWVEC (char, strlen (cptr) + strlen (gcov) + 1);
+  sprintf (result, "%s%s", cptr, gcov);
+
+  return result;
+}
 
-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. */
+/* Output the result in JSON intermediate format.
+   Source info SRC is dumped into JSON_FILES which is JSON array.  */
 
 static void
-output_intermediate_file (FILE *gcov_file, source_info *src)
+output_json_intermediate_file (json::array *json_files, source_info *src)
 {
-  fprintf (gcov_file, "file:%s\n", src->name);    /* source file name */
+  json::object *root = new json::object ();
+  json_files->append (root);
+
+  root->set ("file", new json::string (src->name));
+
+  json::array *functions = new json::array ();
+  root->set ("functions", functions);
 
   std::sort (src->functions.begin (), src->functions.end (),
             function_line_start_cmp ());
-  for (vector<function_t *>::iterator it = src->functions.begin ();
+  for (vector<function_info *>::iterator it = src->functions.begin ();
        it != src->functions.end (); it++)
     {
-      /* function:<name>,<line_number>,<execution_count> */
-      fprintf (gcov_file, "function:%d,%d,%s,%s\n", (*it)->start_line,
-              (*it)->end_line, format_gcov ((*it)->blocks[0].count, 0, -1),
-              flag_demangled_names ? (*it)->demangled_name : (*it)->name);
+      json::object *function = new json::object ();
+      function->set ("name", new json::string ((*it)->m_name));
+      function->set ("demangled_name",
+                    new json::string ((*it)->get_demangled_name ()));
+      function->set ("start_line", new json::number ((*it)->start_line));
+      function->set ("end_line", new json::number ((*it)->end_line));
+      function->set ("blocks",
+                    new json::number ((*it)->get_block_count ()));
+      function->set ("blocks_executed",
+                    new json::number ((*it)->blocks_executed));
+      function->set ("execution_count",
+                    new json::number ((*it)->blocks[0].count));
+
+      functions->append (function);
     }
 
-  for (unsigned line_num = 0; line_num <= src->lines.size (); line_num++)
+  json::array *lineso = new json::array ();
+  root->set ("lines", lineso);
+
+  for (unsigned line_num = 1; line_num <= src->lines.size (); line_num++)
     {
-      vector<function_t *> fns = src->get_functions_at_location (line_num);
+      vector<function_info *> *fns = src->get_functions_at_location (line_num);
 
-      /* Print first group functions that begin on the line.  */
-      for (vector<function_t *>::iterator it2 = fns.begin ();
-          it2 != fns.end (); it2++)
-       {
-         vector<line_info> &lines = (*it2)->lines;
-         for (unsigned i = 0; i < lines.size (); i++)
-           {
-             line_info *line = &lines[i];
-             output_intermediate_line (gcov_file, line, line_num + i);
-           }
-       }
+      if (fns != NULL)
+       /* Print first group functions that begin on the line.  */
+       for (vector<function_info *>::iterator it2 = fns->begin ();
+            it2 != fns->end (); it2++)
+         {
+           vector<line_info> &lines = (*it2)->lines;
+           for (unsigned i = 0; i < lines.size (); i++)
+             {
+               line_info *line = &lines[i];
+               output_intermediate_json_line (lineso, line, line_num + i);
+             }
+         }
 
       /* Follow with lines associated with the source file.  */
-      output_intermediate_line (gcov_file, &src->lines[line_num], line_num);
+      if (line_num < src->lines.size ())
+       output_intermediate_json_line (lineso, &src->lines[line_num], line_num);
     }
 }
 
@@ -1095,64 +1223,82 @@ struct function_start_pair_hash : typed_noop_remove <function_start>
 static void
 process_file (const char *file_name)
 {
-  function_t *fns;
-
   create_file_names (file_name);
-  fns = read_graph_file ();
-  if (!fns)
-    return;
 
-  read_count_file (fns);
+  for (unsigned i = 0; i < processed_files.size (); i++)
+    if (strcmp (da_file_name, processed_files[i]) == 0)
+      {
+       fnotice (stderr, "'%s' file is already processed\n",
+                file_name);
+       return;
+      }
+
+  processed_files.push_back (xstrdup (da_file_name));
 
-  hash_map<function_start_pair_hash, function_t *> fn_map;
+  read_graph_file ();
+  read_count_file ();
+}
+
+/* Process all functions in all files.  */
+
+static void
+process_all_functions (void)
+{
+  hash_map<function_start_pair_hash, function_info *> fn_map;
 
   /* Identify group functions.  */
-  for (function_t *f = fns; f; f = f->next)
-    if (!f->artificial)
+  for (vector<function_info *>::iterator it = functions.begin ();
+       it != functions.end (); it++)
+    if (!(*it)->artificial)
       {
        function_start needle;
-       needle.source_file_idx = f->src;
-       needle.start_line = f->start_line;
+       needle.source_file_idx = (*it)->src;
+       needle.start_line = (*it)->start_line;
 
-       function_t **slot = fn_map.get (needle);
+       function_info **slot = fn_map.get (needle);
        if (slot)
          {
-           gcc_assert ((*slot)->end_line == f->end_line);
            (*slot)->is_group = 1;
-           f->is_group = 1;
+           (*it)->is_group = 1;
          }
        else
-         fn_map.put (needle, f);
+         fn_map.put (needle, *it);
       }
 
-  while (fns)
+  /* Remove all artificial function.  */
+  functions.erase (remove_if (functions.begin (), functions.end (),
+                             function_info::is_artificial), functions.end ());
+
+  for (vector<function_info *>::iterator it = functions.begin ();
+       it != functions.end (); it++)
     {
-      function_t *fn = fns;
+      function_info *fn = *it;
+      unsigned src = fn->src;
 
-      fns = fn->next;
-      fn->next = NULL;
-      if (fn->counts || no_data_file)
+      if (!fn->counts.empty () || no_data_file)
        {
-         unsigned src = fn->src;
-         unsigned block_no;
+         source_info *s = &sources[src];
+         s->add_function (fn);
 
-         /* Process only non-artificial functions.  */
-         if (!fn->artificial)
+         /* Mark last line in files touched by function.  */
+         for (unsigned block_no = 0; block_no != fn->blocks.size ();
+              block_no++)
            {
-             source_info *s = &sources[src];
-             s->functions.push_back (fn);
-
-             /* Mark last line in files touched by function.  */
-             for (block_no = 0; block_no != fn->blocks.size (); block_no++)
+             block_info *block = &fn->blocks[block_no];
+             for (unsigned i = 0; i < block->locations.size (); i++)
                {
-                 block_t *block = &fn->blocks[block_no];
-                 for (unsigned i = 0; i < block->locations.size (); i++)
+                 /* Sort lines of locations.  */
+                 sort (block->locations[i].lines.begin (),
+                       block->locations[i].lines.end ());
+
+                 if (!block->locations[i].lines.empty ())
                    {
-                     /* Sort lines of locations.  */
-                     sort (block->locations[i].lines.begin (),
-                           block->locations[i].lines.end ());
+                     s = &sources[block->locations[i].source_file_idx];
+                     unsigned last_line
+                       = block->locations[i].lines.back ();
 
-                     if (!block->locations[i].lines.empty ())
+                     /* Record new lines for the function.  */
+                     if (last_line >= s->lines.size ())
                        {
                          s = &sources[block->locations[i].source_file_idx];
                          unsigned last_line
@@ -1166,25 +1312,23 @@ process_file (const char *file_name)
                            }
                        }
                    }
-
-                 /* Allocate lines for group function, following start_line
-                    and end_line information of the function.  */
-                 if (fn->is_group)
-                   fn->lines.resize (fn->end_line - fn->start_line + 1);
                }
-
-             solve_flow_graph (fn);
-             if (fn->has_catch)
-               find_exception_blocks (fn);
            }
 
-         *fn_end = fn;
-         fn_end = &fn->next;
+         /* Allocate lines for group function, following start_line
+            and end_line information of the function.  */
+         if (fn->is_group)
+           fn->lines.resize (fn->end_line - fn->start_line + 1);
+
+         solve_flow_graph (fn);
+         if (fn->has_catch)
+           find_exception_blocks (fn);
        }
       else
-       /* The function was not in the executable -- some other
-          instance must have been selected.  */
-       delete fn;
+       {
+         /* The function was not in the executable -- some other
+            instance must have been selected.  */
+       }
     }
 }
 
@@ -1197,19 +1341,16 @@ output_gcov_file (const char *file_name, source_info *src)
     {
       FILE *gcov_file = fopen (gcov_file_name, "w");
       if (gcov_file)
-        {
-          fnotice (stdout, "Creating '%s'\n", gcov_file_name);
-
-         if (flag_intermediate_format)
-           output_intermediate_file (gcov_file, src);
-         else
-           output_lines (gcov_file, src);
-          if (ferror (gcov_file))
-            fnotice (stderr, "Error writing output file '%s'\n", gcov_file_name);
-          fclose (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);
+       fnotice (stderr, "Could not open output file '%s'\n", gcov_file_name);
     }
   else
     {
@@ -1222,16 +1363,16 @@ output_gcov_file (const char *file_name, source_info *src)
 static void
 generate_results (const char *file_name)
 {
-  function_t *fn;
+  char *gcov_intermediate_filename;
 
-  for (fn = functions; fn; fn = fn->next)
+  for (vector<function_info *>::iterator it = functions.begin ();
+       it != functions.end (); it++)
     {
-      coverage_t coverage;
-      if (fn->artificial)
-       continue;
+      function_info *fn = *it;
+      coverage_info coverage;
 
       memset (&coverage, 0, sizeof (coverage));
-      coverage.name = flag_demangled_names ? fn->demangled_name : fn->name;
+      coverage.name = fn->get_name ();
       add_line_counts (flag_function_summary ? &coverage : NULL, fn);
       if (flag_function_summary)
        {
@@ -1253,6 +1394,18 @@ generate_results (const char *file_name)
        file_name = canonicalize_name (file_name);
     }
 
+  gcov_intermediate_filename = get_gcov_intermediate_filename (file_name);
+
+  json::object *root = new json::object ();
+  root->set ("format_version", new json::string ("1"));
+  root->set ("gcc_version", new json::string (version_string));
+
+  if (bbg_cwd != NULL)
+    root->set ("current_working_directory", new json::string (bbg_cwd));
+
+  json::array *json_files = new json::array ();
+  root->set ("files", json_files);
+
   for (vector<source_info>::iterator it = sources.begin ();
        it != sources.end (); it++)
     {
@@ -1272,14 +1425,60 @@ generate_results (const char *file_name)
        }
 
       accumulate_line_counts (src);
-      function_summary (&src->coverage, "File");
+
+      if (!flag_use_stdout)
+       function_summary (&src->coverage, "File");
       total_lines += src->coverage.lines;
       total_executed += src->coverage.lines_executed;
       if (flag_gcov_file)
        {
-         output_gcov_file (file_name, src);
-          fnotice (stdout, "\n");
-        }
+         if (flag_json_format)
+           output_json_intermediate_file (json_files, src);
+         else
+           {
+             if (flag_use_stdout)
+               {
+                 if (src->coverage.lines)
+                   output_lines (stdout, src);
+               }
+             else
+               {
+                 output_gcov_file (file_name, src);
+                 fnotice (stdout, "\n");
+               }
+           }
+       }
+    }
+
+  if (flag_gcov_file && flag_json_format)
+    {
+      if (flag_use_stdout)
+       {
+         root->dump (stdout);
+         printf ("\n");
+       }
+      else
+       {
+         pretty_printer pp;
+         root->print (&pp);
+         pp_formatted_text (&pp);
+
+         gzFile output = gzopen (gcov_intermediate_filename, "w");
+         if (output == NULL)
+           {
+             fnotice (stderr, "Cannot open JSON output file %s\n",
+                      gcov_intermediate_filename);
+             return;
+           }
+
+         if (gzputs (output, pp_formatted_text (&pp)) == EOF
+             || gzclose (output))
+           {
+             fnotice (stderr, "Error writing JSON output file %s\n",
+                      gcov_intermediate_filename);
+             return;
+           }
+       }
     }
 
   if (!file_name)
@@ -1291,13 +1490,14 @@ generate_results (const char *file_name)
 static void
 release_structures (void)
 {
-  function_t *fn;
+  for (vector<function_info *>::iterator it = functions.begin ();
+       it != functions.end (); it++)
+    delete (*it);
 
-  while ((fn = functions))
-    {
-      functions = fn->next;
-      delete fn;
-    }
+  sources.resize (0);
+  names.resize (0);
+  functions.resize (0);
+  ident_to_fn.clear ();
 }
 
 /* Generate the names of the graph and data files.  If OBJECT_DIRECTORY
@@ -1455,29 +1655,26 @@ find_source (const char *file_name)
   return idx;
 }
 
-/* Read the notes file.  Return list of functions read -- in reverse order.  */
+/* Read the notes file.  Save functions to FUNCTIONS global vector.  */
 
-static function_t *
+static void
 read_graph_file (void)
 {
   unsigned version;
   unsigned current_tag = 0;
-  function_t *fn = NULL;
-  function_t *fns = NULL;
-  function_t **fns_end = &fns;
   unsigned tag;
 
   if (!gcov_open (bbg_file_name, 1))
     {
       fnotice (stderr, "%s:cannot open notes file\n", bbg_file_name);
-      return fns;
+      return;
     }
   bbg_file_time = gcov_time ();
   if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC))
     {
       fnotice (stderr, "%s:not a gcov notes file\n", bbg_file_name);
       gcov_close ();
-      return fns;
+      return;
     }
 
   version = gcov_read_unsigned ();
@@ -1492,7 +1689,10 @@ read_graph_file (void)
               bbg_file_name, v, e);
     }
   bbg_stamp = gcov_read_unsigned ();
+  bbg_cwd = xstrdup (gcov_read_string ());
+  bbg_supports_has_unexecuted_blocks = gcov_read_unsigned ();
 
+  function_info *fn = NULL;
   while ((tag = gcov_read_unsigned ()))
     {
       unsigned length = gcov_read_unsigned ();
@@ -1514,14 +1714,11 @@ read_graph_file (void)
          unsigned start_column = gcov_read_unsigned ();
          unsigned end_line = gcov_read_unsigned ();
 
-         fn = new function_t;
-         fn->name = function_name;
-         if (flag_demangled_names)
-           {
-             fn->demangled_name = cplus_demangle (fn->name, DMGL_PARAMS);
-             if (!fn->demangled_name)
-               fn->demangled_name = fn->name;
-           }
+         fn = new function_info ();
+         functions.push_back (fn);
+         ident_to_fn[ident] = fn;
+
+         fn->m_name = function_name;
          fn->ident = ident;
          fn->lineno_checksum = lineno_checksum;
          fn->cfg_checksum = cfg_checksum;
@@ -1531,16 +1728,13 @@ read_graph_file (void)
          fn->end_line = end_line;
          fn->artificial = artificial;
 
-         fn->next = NULL;
-         *fns_end = fn;
-         fns_end = &fn->next;
          current_tag = tag;
        }
       else if (fn && tag == GCOV_TAG_BLOCKS)
        {
          if (!fn->blocks.empty ())
            fnotice (stderr, "%s:already seen blocks for '%s'\n",
-                    bbg_file_name, fn->name);
+                    bbg_file_name, fn->get_name ());
          else
            fn->blocks.resize (gcov_read_unsigned ());
        }
@@ -1549,7 +1743,7 @@ read_graph_file (void)
          unsigned src = gcov_read_unsigned ();
          fn->blocks[src].id = src;
          unsigned num_dests = GCOV_TAG_ARCS_NUM (length);
-         block_t *src_blk = &fn->blocks[src];
+         block_info *src_blk = &fn->blocks[src];
          unsigned mark_catches = 0;
          struct arc_info *arc;
 
@@ -1563,7 +1757,7 @@ read_graph_file (void)
 
              if (dest >= fn->blocks.size ())
                goto corrupt;
-             arc = XCNEW (arc_t);
+             arc = XCNEW (arc_info);
 
              arc->dst = &fn->blocks[dest];
              arc->src = src_blk;
@@ -1602,7 +1796,7 @@ read_graph_file (void)
                }
 
              if (!arc->on_tree)
-               fn->num_counts++;
+               fn->counts.push_back (0);
            }
 
          if (mark_catches)
@@ -1622,7 +1816,7 @@ read_graph_file (void)
       else if (fn && tag == GCOV_TAG_LINES)
        {
          unsigned blockno = gcov_read_unsigned ();
-         block_t *block = &fn->blocks[blockno];
+         block_info *block = &fn->blocks[blockno];
 
          if (blockno >= fn->blocks.size ())
            goto corrupt;
@@ -1659,23 +1853,22 @@ read_graph_file (void)
     }
   gcov_close ();
 
-  if (!fns)
+  if (functions.empty ())
     fnotice (stderr, "%s:no functions found\n", bbg_file_name);
-
-  return fns;
 }
 
 /* Reads profiles from the count file and attach to each
    function. Return nonzero if fatal error.  */
 
 static int
-read_count_file (function_t *fns)
+read_count_file (void)
 {
   unsigned ix;
   unsigned version;
   unsigned tag;
-  function_t *fn = NULL;
+  function_info *fn = NULL;
   int error = 0;
+  map<unsigned, function_info *>::iterator it;
 
   if (!gcov_open (da_file_name, 1))
     {
@@ -1714,39 +1907,22 @@ read_count_file (function_t *fns)
       unsigned length = gcov_read_unsigned ();
       unsigned long base = gcov_position ();
 
-      if (tag == GCOV_TAG_PROGRAM_SUMMARY)
+      if (tag == GCOV_TAG_OBJECT_SUMMARY)
        {
          struct gcov_summary summary;
          gcov_read_summary (&summary);
-         object_runs += summary.ctrs[GCOV_COUNTER_ARCS].runs;
-         program_count++;
+         object_runs = summary.runs;
        }
       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;
-
-         /* Try to find the function in the list.  To speed up the
-            search, first start from the last function found.  */
          ident = gcov_read_unsigned ();
-         fn_n = fns;
-         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);
-                 break;
-               }
-             if (fn->ident == ident)
-               break;
-           }
+         fn = NULL;
+         it = ident_to_fn.find (ident);
+         if (it != ident_to_fn.end ())
+           fn = it->second;
 
          if (!fn)
            ;
@@ -1755,19 +1931,16 @@ read_count_file (function_t *fns)
            {
            mismatch:;
              fnotice (stderr, "%s:profile mismatch for '%s'\n",
-                      da_file_name, fn->name);
+                      da_file_name, fn->get_name ());
              goto cleanup;
            }
        }
       else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
        {
-         if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts))
+         if (length != GCOV_TAG_COUNTER_LENGTH (fn->counts.size ()))
            goto mismatch;
 
-         if (!fn->counts)
-           fn->counts = XCNEWVEC (gcov_type, fn->num_counts);
-
-         for (ix = 0; ix != fn->num_counts; ix++)
+         for (ix = 0; ix != fn->counts.size (); ix++)
            fn->counts[ix] += gcov_read_counter ();
        }
       gcov_sync (base, length);
@@ -1790,19 +1963,19 @@ read_count_file (function_t *fns)
    to the blocks and the uninstrumented arcs.  */
 
 static void
-solve_flow_graph (function_t *fn)
+solve_flow_graph (function_info *fn)
 {
   unsigned ix;
-  arc_t *arc;
-  gcov_type *count_ptr = fn->counts;
-  block_t *blk;
-  block_t *valid_blocks = NULL;    /* valid, but unpropagated blocks.  */
-  block_t *invalid_blocks = NULL;  /* invalid, but inferable blocks.  */
+  arc_info *arc;
+  gcov_type *count_ptr = &fn->counts.front ();
+  block_info *blk;
+  block_info *valid_blocks = NULL;    /* valid, but unpropagated blocks.  */
+  block_info *invalid_blocks = NULL;  /* invalid, but inferable blocks.  */
 
   /* The arcs were built in reverse order.  Fix that now.  */
   for (ix = fn->blocks.size (); ix--;)
     {
-      arc_t *arc_p, *arc_n;
+      arc_info *arc_p, *arc_n;
 
       for (arc_p = NULL, arc = fn->blocks[ix].succ; arc;
           arc_p = arc, arc = arc_n)
@@ -1823,12 +1996,12 @@ solve_flow_graph (function_t *fn)
 
   if (fn->blocks.size () < 2)
     fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n",
-            bbg_file_name, fn->name);
+            bbg_file_name, fn->get_name ());
   else
     {
       if (fn->blocks[ENTRY_BLOCK].num_pred)
        fnotice (stderr, "%s:'%s' has arcs to entry block\n",
-                bbg_file_name, fn->name);
+                bbg_file_name, fn->get_name ());
       else
        /* We can't deduce the entry block counts from the lack of
           predecessors.  */
@@ -1836,7 +2009,7 @@ solve_flow_graph (function_t *fn)
 
       if (fn->blocks[EXIT_BLOCK].num_succ)
        fnotice (stderr, "%s:'%s' has arcs from exit block\n",
-                bbg_file_name, fn->name);
+                bbg_file_name, fn->get_name ());
       else
        /* Likewise, we can't deduce exit block counts from the lack
           of its successors.  */
@@ -1848,7 +2021,7 @@ solve_flow_graph (function_t *fn)
   for (unsigned i = 0; i < fn->blocks.size (); i++)
     {
       blk = &fn->blocks[i];
-      block_t const *prev_dst = NULL;
+      block_info const *prev_dst = NULL;
       int out_of_order = 0;
       int non_fake_succ = 0;
 
@@ -1895,12 +2068,12 @@ solve_flow_graph (function_t *fn)
         smart sort.  */
       if (out_of_order)
        {
-         arc_t *start = blk->succ;
+         arc_info *start = blk->succ;
          unsigned changes = 1;
 
          while (changes)
            {
-             arc_t *arc, *arc_p, *arc_n;
+             arc_info *arc, *arc_p, *arc_n;
 
              changes = 0;
              for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);)
@@ -1938,7 +2111,7 @@ solve_flow_graph (function_t *fn)
       while ((blk = invalid_blocks))
        {
          gcov_type total = 0;
-         const arc_t *arc;
+         const arc_info *arc;
 
          invalid_blocks = blk->chain;
          blk->invalid_chain = 0;
@@ -1960,13 +2133,13 @@ solve_flow_graph (function_t *fn)
       while ((blk = valid_blocks))
        {
          gcov_type total;
-         arc_t *arc, *inv_arc;
+         arc_info *arc, *inv_arc;
 
          valid_blocks = blk->chain;
          blk->valid_chain = 0;
          if (blk->num_succ == 1)
            {
-             block_t *dst;
+             block_info *dst;
 
              total = blk->count;
              inv_arc = NULL;
@@ -2002,7 +2175,7 @@ solve_flow_graph (function_t *fn)
            }
          if (blk->num_pred == 1)
            {
-             block_t *src;
+             block_info *src;
 
              total = blk->count;
              inv_arc = NULL;
@@ -2045,7 +2218,7 @@ solve_flow_graph (function_t *fn)
     if (!fn->blocks[i].count_valid)
       {
        fnotice (stderr, "%s:graph is unsolvable for '%s'\n",
-                bbg_file_name, fn->name);
+                bbg_file_name, fn->get_name ());
        break;
       }
 }
@@ -2053,10 +2226,10 @@ solve_flow_graph (function_t *fn)
 /* Mark all the blocks only reachable via an incoming catch.  */
 
 static void
-find_exception_blocks (function_t *fn)
+find_exception_blocks (function_info *fn)
 {
   unsigned ix;
-  block_t **queue = XALLOCAVEC (block_t *, fn->blocks.size ());
+  block_info **queue = XALLOCAVEC (block_info *, fn->blocks.size ());
 
   /* First mark all blocks as exceptional.  */
   for (ix = fn->blocks.size (); ix--;)
@@ -2067,8 +2240,8 @@ find_exception_blocks (function_t *fn)
   queue[0]->exceptional = 0;
   for (ix = 1; ix;)
     {
-      block_t *block = queue[--ix];
-      const arc_t *arc;
+      block_info *block = queue[--ix];
+      const arc_info *arc;
 
       for (arc = block->succ; arc; arc = arc->succ_next)
        if (!arc->fake && !arc->is_throw && arc->dst->exceptional)
@@ -2083,7 +2256,7 @@ find_exception_blocks (function_t *fn)
 /* Increment totals in COVERAGE according to arc ARC.  */
 
 static void
-add_branch_counts (coverage_t *coverage, const arc_t *arc)
+add_branch_counts (coverage_info *coverage, const arc_info *arc)
 {
   if (arc->is_call_non_return)
     {
@@ -2123,56 +2296,30 @@ format_count (gcov_type count)
       if (count + divisor / 2 < 1000 * divisor)
        break;
     }
-  gcov_type r  = (count + divisor / 2) / divisor;
-  sprintf (buffer, "%" PRId64 "%c", r, units[i]);
+  float r = 1.0f * count / divisor;
+  sprintf (buffer, "%.1f%c", r, units[i]);
   return buffer;
 }
 
 /* Format a GCOV_TYPE integer as either a percent ratio, or absolute
-   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
+   count.  If DECIMAL_PLACES >= 0, format TOP/BOTTOM * 100 to DECIMAL_PLACES.
+   If DECIMAL_PLACES is zero, no decimal point is printed. Only print 100% when
+   TOP==BOTTOM and only print 0% when TOP=0.  If DECIMAL_PLACES < 0, then simply
    format TOP.  Return pointer to a static string.  */
 
 static char const *
-format_gcov (gcov_type top, gcov_type bottom, int dp)
+format_gcov (gcov_type top, gcov_type bottom, int decimal_places)
 {
   static char buffer[20];
 
-  /* Handle invalid values that would result in a misleading value.  */
-  if (bottom != 0 && top > bottom && dp >= 0)
+  if (decimal_places >= 0)
     {
-      sprintf (buffer, "NAN %%");
-      return buffer;
-    }
+      float ratio = bottom ? 100.0f * top / bottom: 0;
 
-  if (dp >= 0)
-    {
-      float ratio = bottom ? (float)top / bottom : 0;
-      int ix;
-      unsigned limit = 100;
-      unsigned percent;
-
-      for (ix = dp; ix--; )
-       limit *= 10;
-
-      percent = (unsigned) (ratio * limit + (float)0.5);
-      if (percent <= 0 && top)
-       percent = 1;
-      else if (percent >= limit && top != bottom)
-       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] = '.';
-       }
+      /* Round up to 1% if there's a small non-zero value.  */
+      if (ratio > 0.0f && ratio < 0.5f && decimal_places == 0)
+       ratio = 1.0f;
+      sprintf (buffer, "%.*f%%", decimal_places, ratio);
     }
   else
     return format_count (top);
@@ -2195,7 +2342,7 @@ executed_summary (unsigned lines, unsigned executed)
 /* Output summary info for a function or file.  */
 
 static void
-function_summary (const coverage_t *coverage, const char *title)
+function_summary (const coverage_info *coverage, const char *title)
 {
   fnotice (stdout, "%s '%s'\n", title, coverage->name);
   executed_summary (coverage->lines, coverage->lines_executed);
@@ -2387,42 +2534,7 @@ mangle_name (char const *base, char *ptr)
       ptr += len;
     }
   else
-    {
-      /* Convert '/' to '#', convert '..' to '^',
-        convert ':' to '~' on DOS based file system.  */
-      const char *probe;
-
-#if HAVE_DOS_BASED_FILE_SYSTEM
-      if (base[0] && base[1] == ':')
-       {
-         ptr[0] = base[0];
-         ptr[1] = '~';
-         ptr += 2;
-         base += 2;
-       }
-#endif
-      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
-           {
-             memcpy (ptr, base, len);
-             ptr += len;
-           }
-         if (*probe)
-           {
-             *ptr++ = '#';
-             probe++;
-           }
-       }
-    }
+    ptr = mangle_path (base);
 
   return ptr;
 }
@@ -2432,14 +2544,14 @@ mangle_name (char const *base, char *ptr)
    the appropriate basic block.  */
 
 static void
-add_line_counts (coverage_t *coverage, function_t *fn)
+add_line_counts (coverage_info *coverage, function_info *fn)
 {
   bool has_any_line = false;
   /* Scan each basic block.  */
   for (unsigned ix = 0; ix != fn->blocks.size (); ix++)
     {
       line_info *line = NULL;
-      block_t *block = &fn->blocks[ix];
+      block_info *block = &fn->blocks[ix];
       if (block->count && ix && ix + 1 != fn->blocks.size ())
        fn->blocks_executed++;
       for (unsigned i = 0; i < block->locations.size (); i++)
@@ -2500,7 +2612,7 @@ add_line_counts (coverage_t *coverage, function_t *fn)
 
              if (flag_branches)
                {
-                 arc_t *arc;
+                 arc_info *arc;
 
                  for (arc = block->succ; arc; arc = arc->succ_next)
                    line->branches.push_back (arc);
@@ -2510,7 +2622,8 @@ add_line_counts (coverage_t *coverage, function_t *fn)
     }
 
   if (!has_any_line)
-    fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name);
+    fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name,
+            fn->get_name ());
 }
 
 /* Accumulate info for LINE that belongs to SRC source file.  If ADD_COVERAGE
@@ -2535,19 +2648,22 @@ static void accumulate_line_info (line_info *line, source_info *src,
       gcov_type count = 0;
 
       /* Cycle detection.  */
-      for (vector<block_t *>::iterator it = line->blocks.begin ();
+      for (vector<block_info *>::iterator it = line->blocks.begin ();
           it != line->blocks.end (); it++)
        {
-         for (arc_t *arc = (*it)->pred; arc; arc = arc->pred_next)
+         for (arc_info *arc = (*it)->pred; arc; arc = arc->pred_next)
            if (!line->has_block (arc->src))
              count += arc->count;
-         for (arc_t *arc = (*it)->succ; arc; arc = arc->succ_next)
+         for (arc_info *arc = (*it)->succ; arc; arc = arc->succ_next)
            arc->cs_count = arc->count;
        }
 
       /* Now, add the count of loops entirely on this line.  */
       count += get_cycles_count (*line);
       line->count = count;
+
+      if (line->count > src->maximum_count)
+       src->maximum_count = line->count;
     }
 
   if (line->exists && add_coverage)
@@ -2564,7 +2680,7 @@ static void
 accumulate_line_counts (source_info *src)
 {
   /* First work on group functions.  */
-  for (vector<function_t *>::iterator it = src->functions.begin ();
+  for (vector<function_info *>::iterator it = src->functions.begin ();
        it != src->functions.end (); it++)
     {
       function_info *fn = *it;
@@ -2587,8 +2703,8 @@ accumulate_line_counts (source_info *src)
 
   /* If not using intermediate mode, sum lines of group functions and
      add them to lines that live in a source file.  */
-  if (!flag_intermediate_format)
-    for (vector<function_t *>::iterator it = src->functions.begin ();
+  if (!flag_json_format)
+    for (vector<function_info *>::iterator it = src->functions.begin ();
         it != src->functions.end (); it++)
       {
        function_info *fn = *it;
@@ -2626,7 +2742,7 @@ accumulate_line_counts (source_info *src)
    anything is output.  */
 
 static int
-output_branch_count (FILE *gcov_file, int ix, const arc_t *arc)
+output_branch_count (FILE *gcov_file, int ix, const arc_info *arc)
 {
   if (arc->is_call_non_return)
     {
@@ -2724,7 +2840,8 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional,
                       bool has_unexecuted_block,
                       gcov_type count, unsigned line_num,
                       const char *exceptional_string,
-                      const char *unexceptional_string)
+                      const char *unexceptional_string,
+                      unsigned int maximum_count)
 {
   string s;
   if (exists)
@@ -2732,7 +2849,8 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional,
       if (count > 0)
        {
          s = format_gcov (count, 0, -1);
-         if (has_unexecuted_block)
+         if (has_unexecuted_block
+             && bbg_supports_has_unexecuted_blocks)
            {
              if (flag_use_colors)
                {
@@ -2773,7 +2891,23 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional,
       pad_count_string (s);
     }
 
-  fprintf (f, "%s:%5u", s.c_str (), line_num);
+  /* Format line number in output.  */
+  char buffer[16];
+  sprintf (buffer, "%5u", line_num);
+  string linestr (buffer);
+
+  if (flag_use_hotness_colors && maximum_count)
+    {
+      if (count * 2 > maximum_count) /* > 50%.  */
+       linestr.insert (0, SGR_SEQ (COLOR_BG_RED));
+      else if (count * 5 > maximum_count) /* > 20%.  */
+       linestr.insert (0, SGR_SEQ (COLOR_BG_YELLOW));
+      else if (count * 10 > maximum_count) /* > 10%.  */
+       linestr.insert (0, SGR_SEQ (COLOR_BG_GREEN));
+      linestr += SGR_RESET;
+    }
+
+  fprintf (f, "%s:%s", s.c_str (), linestr.c_str ());
 }
 
 static void
@@ -2794,11 +2928,11 @@ output_line_details (FILE *f, const line_info *line, unsigned line_num)
 {
   if (flag_all_blocks)
     {
-      arc_t *arc;
+      arc_info *arc;
       int ix, jx;
 
       ix = jx = 0;
-      for (vector<block_t *>::const_iterator it = line->blocks.begin ();
+      for (vector<block_info *>::const_iterator it = line->blocks.begin ();
           it != line->blocks.end (); it++)
        {
          if (!(*it)->is_call_return)
@@ -2806,7 +2940,7 @@ output_line_details (FILE *f, const line_info *line, unsigned line_num)
              output_line_beginning (f, line->exists,
                                     (*it)->exceptional, false,
                                     (*it)->count, line_num,
-                                    "%%%%%", "$$$$$");
+                                    "%%%%%", "$$$$$", 0);
              fprintf (f, "-block %2d", ix++);
              if (flag_verbose)
                fprintf (f, " (BB %u)", (*it)->id);
@@ -2822,7 +2956,7 @@ output_line_details (FILE *f, const line_info *line, unsigned line_num)
       int ix;
 
       ix = 0;
-      for (vector<arc_t *>::const_iterator it = line->branches.begin ();
+      for (vector<arc_info *>::const_iterator it = line->branches.begin ();
           it != line->branches.end (); it++)
        ix += output_branch_count (f, ix, (*it));
     }
@@ -2831,12 +2965,12 @@ output_line_details (FILE *f, const line_info *line, unsigned line_num)
 /* Output detail statistics about function FN to file F.  */
 
 static void
-output_function_details (FILE *f, const function_info *fn)
+output_function_details (FILE *f, function_info *fn)
 {
   if (!flag_branches)
     return;
 
-  arc_t *arc = fn->blocks[EXIT_BLOCK].pred;
+  arc_info *arc = fn->blocks[EXIT_BLOCK].pred;
   gcov_type return_count = fn->blocks[EXIT_BLOCK].count;
   gcov_type called_count = fn->blocks[ENTRY_BLOCK].count;
 
@@ -2844,15 +2978,13 @@ output_function_details (FILE *f, const function_info *fn)
     if (arc->fake)
       return_count -= arc->count;
 
-  fprintf (f, "function %s",
-          flag_demangled_names ? fn->demangled_name : fn->name);
+  fprintf (f, "function %s", fn->get_name ());
   fprintf (f, " called %s",
           format_gcov (called_count, 0, -1));
   fprintf (f, " returned %s",
           format_gcov (return_count, called_count, 0));
   fprintf (f, " blocks executed %s",
-          format_gcov (fn->blocks_executed, fn->blocks.size () - 2,
-                       0));
+          format_gcov (fn->blocks_executed, fn->get_block_count (), 0));
   fprintf (f, "\n");
 }
 
@@ -2869,6 +3001,23 @@ output_lines (FILE *gcov_file, const source_info *src)
   FILE *source_file;
   const char *retval;
 
+  /* Print colorization legend.  */
+  if (flag_use_colors)
+    fprintf (gcov_file, "%s",
+            DEFAULT_LINE_START "Colorization: profile count: " \
+            SGR_SEQ (COLOR_BG_CYAN) "zero coverage (exceptional)" SGR_RESET \
+            " " \
+            SGR_SEQ (COLOR_BG_RED) "zero coverage (unexceptional)" SGR_RESET \
+            " " \
+            SGR_SEQ (COLOR_BG_MAGENTA) "unexecuted block" SGR_RESET "\n");
+
+  if (flag_use_hotness_colors)
+    fprintf (gcov_file, "%s",
+            DEFAULT_LINE_START "Colorization: line numbers: hotness: " \
+            SGR_SEQ (COLOR_BG_RED) "> 50%" SGR_RESET " " \
+            SGR_SEQ (COLOR_BG_YELLOW) "> 20%" SGR_RESET " " \
+            SGR_SEQ (COLOR_BG_GREEN) "> 10%" SGR_RESET "\n");
+
   fprintf (gcov_file, DEFAULT_LINE_START "Source:%s\n", src->coverage.name);
   if (!multiple_files)
     {
@@ -2877,7 +3026,6 @@ output_lines (FILE *gcov_file, const source_info *src)
               no_data_file ? "-" : da_file_name);
       fprintf (gcov_file, DEFAULT_LINE_START "Runs:%u\n", object_runs);
     }
-  fprintf (gcov_file, DEFAULT_LINE_START "Programs:%u\n", program_count);
 
   source_file = fopen (src->name, "r");
   if (!source_file)
@@ -2891,7 +3039,7 @@ output_lines (FILE *gcov_file, const source_info *src)
       source_lines.push_back (xstrdup (retval));
 
   unsigned line_start_group = 0;
-  vector<function_t *> fns;
+  vector<function_info *> *fns;
 
   for (unsigned line_num = 1; line_num <= source_lines.size (); line_num++)
     {
@@ -2907,11 +3055,18 @@ output_lines (FILE *gcov_file, const source_info *src)
       if (line_start_group == 0)
        {
          fns = src->get_functions_at_location (line_num);
-         if (fns.size () > 1)
-           line_start_group = fns[0]->end_line;
-         else if (fns.size () == 1)
+         if (fns != NULL && fns->size () > 1)
            {
-             function_t *fn = fns[0];
+             /* It's possible to have functions that partially overlap,
+                thus take the maximum end_line of functions starting
+                at LINE_NUM.  */
+             for (unsigned i = 0; i < fns->size (); i++)
+               if ((*fns)[i]->end_line > line_start_group)
+                 line_start_group = (*fns)[i]->end_line;
+           }
+         else if (fns != NULL && fns->size () == 1)
+           {
+             function_info *fn = (*fns)[0];
              output_function_details (gcov_file, fn);
            }
        }
@@ -2924,24 +3079,22 @@ output_lines (FILE *gcov_file, const source_info *src)
         line so that tabs won't be messed up.  */
       output_line_beginning (gcov_file, line->exists, line->unexceptional,
                             line->has_unexecuted_block, line->count,
-                            line_num, "=====", "#####");
+                            line_num, "=====", "#####", src->maximum_count);
 
       print_source_line (gcov_file, source_lines, line_num);
       output_line_details (gcov_file, line, line_num);
 
       if (line_start_group == line_num)
        {
-         for (vector<function_t *>::iterator it = fns.begin ();
-              it != fns.end (); it++)
+         for (vector<function_info *>::iterator it = fns->begin ();
+              it != fns->end (); it++)
            {
              function_info *fn = *it;
              vector<line_info> &lines = fn->lines;
 
              fprintf (gcov_file, FN_SEPARATOR);
 
-             string fn_name
-               = flag_demangled_names ? fn->demangled_name : fn->name;
-
+             string fn_name = fn->get_name ();
              if (flag_use_colors)
                {
                  fn_name.insert (0, SGR_SEQ (COLOR_FG_CYAN));
@@ -2969,7 +3122,8 @@ output_lines (FILE *gcov_file, const source_info *src)
                                         line->unexceptional,
                                         line->has_unexecuted_block,
                                         line->count,
-                                        l, "=====", "#####");
+                                        l, "=====", "#####",
+                                        src->maximum_count);
 
                  print_source_line (gcov_file, source_lines, l);
                  output_line_details (gcov_file, line, l);