text: //===- GCOV.cpp - LLVM coverage tool --------------------------------------===//
  //
  //                     The LLVM Compiler Infrastructure
  //
  // This file is distributed under the University of Illinois Open Source
  // License. See LICENSE.TXT for details.
  //
  //===----------------------------------------------------------------------===//
  //
  // GCOV implements the interface to read and write coverage files that use
  // 'gcov' format.
  //
  //===----------------------------------------------------------------------===//
  
  #include "llvm/Support/GCOV.h"
  #include "llvm/ADT/STLExtras.h"
  #include "llvm/Support/Debug.h"
  #include "llvm/Support/FileSystem.h"
  #include "llvm/Support/Format.h"
  #include "llvm/Support/Path.h"
  #include "llvm/Support/raw_ostream.h"
  #include <algorithm>
  #include <system_error>
  
  using namespace llvm;
  
  //===----------------------------------------------------------------------===//
  // GCOVFile implementation.
  
  /// readGCNO - Read GCNO buffer.
  bool GCOVFile::readGCNO(GCOVBuffer &Buffer) {
    if (!Buffer.readGCNOFormat())
      return false;
    if (!Buffer.readGCOVVersion(Version))
      return false;
  
    if (!Buffer.readInt(Checksum))
      return false;
    while (true) {
      if (!Buffer.readFunctionTag())
        break;
      auto GFun = make_unique<GCOVFunction>(*this);
      if (!GFun->readGCNO(Buffer, Version))
        return false;
      Functions.push_back(std::move(GFun));
    }
  
    GCNOInitialized = true;
    return true;
  }
  
  /// readGCDA - Read GCDA buffer. It is required that readGCDA() can only be
  /// called after readGCNO().
  bool GCOVFile::readGCDA(GCOVBuffer &Buffer) {
    assert(GCNOInitialized && "readGCDA() can only be called after readGCNO()");
    if (!Buffer.readGCDAFormat())
      return false;
    GCOV::GCOVVersion GCDAVersion;
    if (!Buffer.readGCOVVersion(GCDAVersion))
      return false;
    if (Version != GCDAVersion) {
      errs() << "GCOV versions do not match.\n";
      return false;
    }
  
    uint32_t GCDAChecksum;
    if (!Buffer.readInt(GCDAChecksum))
      return false;
    if (Checksum != GCDAChecksum) {
      errs() << "File checksums do not match: " << Checksum
             << " != " << GCDAChecksum << ".\n";
      return false;
    }
    for (size_t i = 0, e = Functions.size(); i < e; ++i) {
      if (!Buffer.readFunctionTag()) {
        errs() << "Unexpected number of functions.\n";
        return false;
      }
      if (!Functions[i]->readGCDA(Buffer, Version))
        return false;
    }
    if (Buffer.readObjectTag()) {
      uint32_t Length;
      uint32_t Dummy;
      if (!Buffer.readInt(Length))
        return false;
      if (!Buffer.readInt(Dummy))
        return false; // checksum
      if (!Buffer.readInt(Dummy))
        return false; // num
      if (!Buffer.readInt(RunCount))
        return false;
      Buffer.advanceCursor(Length - 3);
    }
    while (Buffer.readProgramTag()) {
      uint32_t Length;
      if (!Buffer.readInt(Length))
        return false;
      Buffer.advanceCursor(Length);
      ++ProgramCount;
    }
  
    return true;
  }
  
  void GCOVFile::print(raw_ostream &OS) const {
    for (const auto &FPtr : Functions)
      FPtr->print(OS);
  }
  
  #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
  /// dump - Dump GCOVFile content to dbgs() for debugging purposes.
  LLVM_DUMP_METHOD void GCOVFile::dump() const {
    print(dbgs());
  }
  #endif
  
  /// collectLineCounts - Collect line counts. This must be used after
  /// reading .gcno and .gcda files.
  void GCOVFile::collectLineCounts(FileInfo &FI) {
    for (const auto &FPtr : Functions)
      FPtr->collectLineCounts(FI);
    FI.setRunCount(RunCount);
    FI.setProgramCount(ProgramCount);
  }
  
  //===----------------------------------------------------------------------===//
  // GCOVFunction implementation.
  
  /// readGCNO - Read a function from the GCNO buffer. Return false if an error
  /// occurs.
  bool GCOVFunction::readGCNO(GCOVBuffer &Buff, GCOV::GCOVVersion Version) {
    uint32_t Dummy;
    if (!Buff.readInt(Dummy))
      return false; // Function header length
    if (!Buff.readInt(Ident))
      return false;
    if (!Buff.readInt(Checksum))
      return false;
    if (Version != GCOV::V402) {
      uint32_t CfgChecksum;
      if (!Buff.readInt(CfgChecksum))
        return false;
      if (Parent.getChecksum() != CfgChecksum) {
        errs() << "File checksums do not match: " << Parent.getChecksum()
               << " != " << CfgChecksum << " in (" << Name << ").\n";
        return false;
      }
    }
    if (!Buff.readString(Name))
      return false;
    if (!Buff.readString(Filename))
      return false;
    if (!Buff.readInt(LineNumber))
      return false;
  
    // read blocks.
    if (!Buff.readBlockTag()) {
      errs() << "Block tag not found.\n";
      return false;
    }
    uint32_t BlockCount;
    if (!Buff.readInt(BlockCount))
      return false;
    for (uint32_t i = 0, e = BlockCount; i != e; ++i) {
      if (!Buff.readInt(Dummy))
        return false; // Block flags;
      Blocks.push_back(make_unique<GCOVBlock>(*this, i));
    }
  
    // read edges.
    while (Buff.readEdgeTag()) {
      uint32_t EdgeCount;
      if (!Buff.readInt(EdgeCount))
        return false;
      EdgeCount = (EdgeCount - 1) / 2;
      uint32_t BlockNo;
      if (!Buff.readInt(BlockNo))
        return false;
      if (BlockNo >= BlockCount) {
        errs() << "Unexpected block number: " << BlockNo << " (in " << Name
               << ").\n";
        return false;
      }
      for (uint32_t i = 0, e = EdgeCount; i != e; ++i) {
        uint32_t Dst;
        if (!Buff.readInt(Dst))
          return false;
        Edges.push_back(make_unique<GCOVEdge>(*Blocks[BlockNo], *Blocks[Dst]));
        GCOVEdge *Edge = Edges.back().get();
        Blocks[BlockNo]->addDstEdge(Edge);
        Blocks[Dst]->addSrcEdge(Edge);
        if (!Buff.readInt(Dummy))
          return false; // Edge flag
      }
    }
  
    // read line table.
    while (Buff.readLineTag()) {
      uint32_t LineTableLength;
      // Read the length of this line table.
      if (!Buff.readInt(LineTableLength))
        return false;
      uint32_t EndPos = Buff.getCursor() + LineTableLength * 4;
      uint32_t BlockNo;
      // Read the block number this table is associated with.
      if (!Buff.readInt(BlockNo))
        return false;
      if (BlockNo >= BlockCount) {
        errs() << "Unexpected block number: " << BlockNo << " (in " << Name
               << ").\n";
        return false;
      }
      GCOVBlock &Block = *Blocks[BlockNo];
      // Read the word that pads the beginning of the line table. This may be a
      // flag of some sort, but seems to always be zero.
      if (!Buff.readInt(Dummy))
        return false;
  
      // Line information starts here and continues up until the last word.
      if (Buff.getCursor() != (EndPos - sizeof(uint32_t))) {
        StringRef F;
        // Read the source file name.
        if (!Buff.readString(F))
          return false;
        if (Filename != F) {
          errs() << "Multiple sources for a single basic block: " << Filename
                 << " != " << F << " (in " << Name << ").\n";
          return false;
        }
        // Read lines up to, but not including, the null terminator.
        while (Buff.getCursor() < (EndPos - 2 * sizeof(uint32_t))) {
          uint32_t Line;
          if (!Buff.readInt(Line))
            return false;
          // Line 0 means this instruction was injected by the compiler. Skip it.
          if (!Line)
            continue;
          Block.addLine(Line);
        }
        // Read the null terminator.
        if (!Buff.readInt(Dummy))
          return false;
      }
      // The last word is either a flag or padding, it isn't clear which. Skip
      // over it.
      if (!Buff.readInt(Dummy))
        return false;
    }
    return true;
  }
  
  /// readGCDA - Read a function from the GCDA buffer. Return false if an error
  /// occurs.
  bool GCOVFunction::readGCDA(GCOVBuffer &Buff, GCOV::GCOVVersion Version) {
    uint32_t HeaderLength;
    if (!Buff.readInt(HeaderLength))
      return false; // Function header length
  
    uint64_t EndPos = Buff.getCursor() + HeaderLength * sizeof(uint32_t);
  
    uint32_t GCDAIdent;
    if (!Buff.readInt(GCDAIdent))
      return false;
    if (Ident != GCDAIdent) {
      errs() << "Function identifiers do not match: " << Ident
             << " != " << GCDAIdent << " (in " << Name << ").\n";
      return false;
    }
  
    uint32_t GCDAChecksum;
    if (!Buff.readInt(GCDAChecksum))
      return false;
    if (Checksum != GCDAChecksum) {
      errs() << "Function checksums do not match: " << Checksum
             << " != " << GCDAChecksum << " (in " << Name << ").\n";
      return false;
    }
  
    uint32_t CfgChecksum;
    if (Version != GCOV::V402) {
      if (!Buff.readInt(CfgChecksum))
        return false;
      if (Parent.getChecksum() != CfgChecksum) {
        errs() << "File checksums do not match: " << Parent.getChecksum()
               << " != " << CfgChecksum << " (in " << Name << ").\n";
        return false;
      }
    }
  
    if (Buff.getCursor() < EndPos) {
      StringRef GCDAName;
      if (!Buff.readString(GCDAName))
        return false;
      if (Name != GCDAName) {
        errs() << "Function names do not match: " << Name << " != " << GCDAName
               << ".\n";
        return false;
      }
    }
  
    if (!Buff.readArcTag()) {
      errs() << "Arc tag not found (in " << Name << ").\n";
      return false;
    }
  
    uint32_t Count;
    if (!Buff.readInt(Count))
      return false;
    Count /= 2;
  
    // This for loop adds the counts for each block. A second nested loop is
    // required to combine the edge counts that are contained in the GCDA file.
    for (uint32_t BlockNo = 0; Count > 0; ++BlockNo) {
      // The last block is always reserved for exit block
      if (BlockNo >= Blocks.size()) {
        errs() << "Unexpected number of edges (in " << Name << ").\n";
        return false;
      }
      if (BlockNo == Blocks.size() - 1)
        errs() << "(" << Name << ") has arcs from exit block.\n";
      GCOVBlock &Block = *Blocks[BlockNo];
      for (size_t EdgeNo = 0, End = Block.getNumDstEdges(); EdgeNo < End;
           ++EdgeNo) {
        if (Count == 0) {
          errs() << "Unexpected number of edges (in " << Name << ").\n";
          return false;
        }
        uint64_t ArcCount;
        if (!Buff.readInt64(ArcCount))
          return false;
        Block.addCount(EdgeNo, ArcCount);
        --Count;
      }
      Block.sortDstEdges();
    }
    return true;
  }
  
  /// getEntryCount - Get the number of times the function was called by
  /// retrieving the entry block's count.
  uint64_t GCOVFunction::getEntryCount() const {
    return Blocks.front()->getCount();
  }
  
  /// getExitCount - Get the number of times the function returned by retrieving
  /// the exit block's count.
  uint64_t GCOVFunction::getExitCount() const {
    return Blocks.back()->getCount();
  }
  
  void GCOVFunction::print(raw_ostream &OS) const {
    OS << "===== " << Name << " (" << Ident << ") @ " << Filename << ":"
       << LineNumber << "\n";
    for (const auto &Block : Blocks)
      Block->print(OS);
  }
  
  #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
  /// dump - Dump GCOVFunction content to dbgs() for debugging purposes.
  LLVM_DUMP_METHOD void GCOVFunction::dump() const {
    print(dbgs());
  }
  #endif
  
  /// collectLineCounts - Collect line counts. This must be used after
  /// reading .gcno and .gcda files.
  void GCOVFunction::collectLineCounts(FileInfo &FI) {
    // If the line number is zero, this is a function that doesn't actually appear
    // in the source file, so there isn't anything we can do with it.
    if (LineNumber == 0)
      return;
  
    for (const auto &Block : Blocks)
      Block->collectLineCounts(FI);
    FI.addFunctionLine(Filename, LineNumber, this);
  }
  
  //===----------------------------------------------------------------------===//
  // GCOVBlock implementation.
  
  /// ~GCOVBlock - Delete GCOVBlock and its content.
  GCOVBlock::~GCOVBlock() {
    SrcEdges.clear();
    DstEdges.clear();
    Lines.clear();
  }
  
  /// addCount - Add to block counter while storing the edge count. If the
  /// destination has no outgoing edges, also update that block's count too.
  void GCOVBlock::addCount(size_t DstEdgeNo, uint64_t N) {
    assert(DstEdgeNo < DstEdges.size()); // up to caller to ensure EdgeNo is valid
    DstEdges[DstEdgeNo]->Count = N;
    Counter += N;
    if (!DstEdges[DstEdgeNo]->Dst.getNumDstEdges())
      DstEdges[DstEdgeNo]->Dst.Counter += N;
  }
  
  /// sortDstEdges - Sort destination edges by block number, nop if already
  /// sorted. This is required for printing branch info in the correct order.
  void GCOVBlock::sortDstEdges() {
    if (!DstEdgesAreSorted) {
      SortDstEdgesFunctor SortEdges;
      std::stable_sort(DstEdges.begin(), DstEdges.end(), SortEdges);
    }
  }
  
  /// collectLineCounts - Collect line counts. This must be used after
  /// reading .gcno and .gcda files.
  void GCOVBlock::collectLineCounts(FileInfo &FI) {
    for (uint32_t N : Lines)
      FI.addBlockLine(Parent.getFilename(), N, this);
  }
  
  void GCOVBlock::print(raw_ostream &OS) const {
    OS << "Block : " << Number << " Counter : " << Counter << "\n";
    if (!SrcEdges.empty()) {
      OS << "\tSource Edges : ";
      for (const GCOVEdge *Edge : SrcEdges)
        OS << Edge->Src.Number << " (" << Edge->Count << "), ";
      OS << "\n";
    }
    if (!DstEdges.empty()) {
      OS << "\tDestination Edges : ";
      for (const GCOVEdge *Edge : DstEdges)
        OS << Edge->Dst.Number << " (" << Edge->Count << "), ";
      OS << "\n";
    }
    if (!Lines.empty()) {
      OS << "\tLines : ";
      for (uint32_t N : Lines)
        OS << (N) << ",";
      OS << "\n";
    }
  }
  
  #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
  /// dump - Dump GCOVBlock content to dbgs() for debugging purposes.
  LLVM_DUMP_METHOD void GCOVBlock::dump() const {
    print(dbgs());
  }
  #endif
  
  //===----------------------------------------------------------------------===//
  // FileInfo implementation.
  
  // Safe integer division, returns 0 if numerator is 0.
  static uint32_t safeDiv(uint64_t Numerator, uint64_t Divisor) {
    if (!Numerator)
      return 0;
    return Numerator / Divisor;
  }
  
  // This custom division function mimics gcov's branch ouputs:
  //   - Round to closest whole number
  //   - Only output 0% or 100% if it's exactly that value
  static uint32_t branchDiv(uint64_t Numerator, uint64_t Divisor) {
    if (!Numerator)
      return 0;
    if (Numerator == Divisor)
      return 100;
  
    uint8_t Res = (Numerator * 100 + Divisor / 2) / Divisor;
    if (Res == 0)
      return 1;
    if (Res == 100)
      return 99;
    return Res;
  }
  
  namespace {
  struct formatBranchInfo {
    formatBranchInfo(const GCOV::Options &Options, uint64_t Count, uint64_t Total)
        : Options(Options), Count(Count), Total(Total) {}
  
    void print(raw_ostream &OS) const {
      if (!Total)
        OS << "never executed";
      else if (Options.BranchCount)
        OS << "taken " << Count;
      else
        OS << "taken " << branchDiv(Count, Total) << "%";
    }
  
    const GCOV::Options &Options;
    uint64_t Count;
    uint64_t Total;
  };
  
  static raw_ostream &operator<<(raw_ostream &OS, const formatBranchInfo &FBI) {
    FBI.print(OS);
    return OS;
  }
  
  class LineConsumer {
    std::unique_ptr<MemoryBuffer> Buffer;
    StringRef Remaining;
  
  public:
    LineConsumer(StringRef Filename) {
      ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
          MemoryBuffer::getFileOrSTDIN(Filename);
      if (std::error_code EC = BufferOrErr.getError()) {
        errs() << Filename << ": " << EC.message() << "\n";
        Remaining = "";
      } else {
        Buffer = std::move(BufferOrErr.get());
        Remaining = Buffer->getBuffer();
      }
    }
    bool empty() { return Remaining.empty(); }
    void printNext(raw_ostream &OS, uint32_t LineNum) {
      StringRef Line;
      if (empty())
        Line = "/*EOF*/";
      else
        std::tie(Line, Remaining) = Remaining.split("\n");
      OS << format("%5u:", LineNum) << Line << "\n";
    }
  };
  } // end anonymous namespace
  
  /// Convert a path to a gcov filename. If PreservePaths is true, this
  /// translates "/" to "#", ".." to "^", and drops ".", to match gcov.
  static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths) {
    if (!PreservePaths)
      return sys::path::filename(Filename).str();
  
    // This behaviour is defined by gcov in terms of text replacements, so it's
    // not likely to do anything useful on filesystems with different textual
    // conventions.
    llvm::SmallString<256> Result("");
    StringRef::iterator I, S, E;
    for (I = S = Filename.begin(), E = Filename.end(); I != E; ++I) {
      if (*I != '/')
        continue;
  
      if (I - S == 1 && *S == '.') {
        // ".", the current directory, is skipped.
      } else if (I - S == 2 && *S == '.' && *(S + 1) == '.') {
        // "..", the parent directory, is replaced with "^".
        Result.append("^#");
      } else {
        if (S < I)
          // Leave other components intact,
          Result.append(S, I);
        // And separate with "#".
        Result.push_back('#');
      }
      S = I + 1;
    }
  
    if (S < I)
      Result.append(S, I);
    return Result.str();
  }
  
  std::string FileInfo::getCoveragePath(StringRef Filename,
                                        StringRef MainFilename) {
    if (Options.NoOutput)
      // This is probably a bug in gcov, but when -n is specified, paths aren't
      // mangled at all, and the -l and -p options are ignored. Here, we do the
      // same.
      return Filename;
  
    std::string CoveragePath;
    if (Options.LongFileNames && !Filename.equals(MainFilename))
      CoveragePath =
          mangleCoveragePath(MainFilename, Options.PreservePaths) + "##";
    CoveragePath += mangleCoveragePath(Filename, Options.PreservePaths) + ".gcov";
    return CoveragePath;
  }
  
  std::unique_ptr<raw_ostream>
  FileInfo::openCoveragePath(StringRef CoveragePath) {
    if (Options.NoOutput)
      return llvm::make_unique<raw_null_ostream>();
  
    std::error_code EC;
    auto OS = llvm::make_unique<raw_fd_ostream>(CoveragePath, EC,
                                                sys::fs::F_Text);
    if (EC) {
      errs() << EC.message() << "\n";
      return llvm::make_unique<raw_null_ostream>();
    }
    return std::move(OS);
  }
  
  /// print -  Print source files with collected line count information.
  void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename,
                       StringRef GCNOFile, StringRef GCDAFile) {
    SmallVector<StringRef, 4> Filenames;
    for (const auto &LI : LineInfo)
      Filenames.push_back(LI.first());
    std::sort(Filenames.begin(), Filenames.end());
  
    for (StringRef Filename : Filenames) {
      auto AllLines = LineConsumer(Filename);
  
      std::string CoveragePath = getCoveragePath(Filename, MainFilename);
      std::unique_ptr<raw_ostream> CovStream = openCoveragePath(CoveragePath);
      raw_ostream &CovOS = *CovStream;
  
      CovOS << "        -:    0:Source:" << Filename << "\n";
      CovOS << "        -:    0:Graph:" << GCNOFile << "\n";
      CovOS << "        -:    0:Data:" << GCDAFile << "\n";
      CovOS << "        -:    0:Runs:" << RunCount << "\n";
      CovOS << "        -:    0:Programs:" << ProgramCount << "\n";
  
      const LineData &Line = LineInfo[Filename];
      GCOVCoverage FileCoverage(Filename);
      for (uint32_t LineIndex = 0; LineIndex < Line.LastLine || !AllLines.empty();
           ++LineIndex) {
        if (Options.BranchInfo) {
          FunctionLines::const_iterator FuncsIt = Line.Functions.find(LineIndex);
          if (FuncsIt != Line.Functions.end())
            printFunctionSummary(CovOS, FuncsIt->second);
        }
  
        BlockLines::const_iterator BlocksIt = Line.Blocks.find(LineIndex);
        if (BlocksIt == Line.Blocks.end()) {
          // No basic blocks are on this line. Not an executable line of code.
          CovOS << "        -:";
          AllLines.printNext(CovOS, LineIndex + 1);
        } else {
          const BlockVector &Blocks = BlocksIt->second;
  
          // Add up the block counts to form line counts.
          DenseMap<const GCOVFunction *, bool> LineExecs;
          uint64_t LineCount = 0;
          for (const GCOVBlock *Block : Blocks) {
            if (Options.AllBlocks) {
              // Only take the highest block count for that line.
              uint64_t BlockCount = Block->getCount();
              LineCount = LineCount > BlockCount ? LineCount : BlockCount;
            } else {
              // Sum up all of the block counts.
              LineCount += Block->getCount();
            }
  
            if (Options.FuncCoverage) {
              // This is a slightly convoluted way to most accurately gather line
              // statistics for functions. Basically what is happening is that we
              // don't want to count a single line with multiple blocks more than
              // once. However, we also don't simply want to give the total line
              // count to every function that starts on the line. Thus, what is
              // happening here are two things:
              // 1) Ensure that the number of logical lines is only incremented
              //    once per function.
              // 2) If there are multiple blocks on the same line, ensure that the
              //    number of lines executed is incremented as long as at least
              //    one of the blocks are executed.
              const GCOVFunction *Function = &Block->getParent();
              if (FuncCoverages.find(Function) == FuncCoverages.end()) {
                std::pair<const GCOVFunction *, GCOVCoverage> KeyValue(
                    Function, GCOVCoverage(Function->getName()));
                FuncCoverages.insert(KeyValue);
              }
              GCOVCoverage &FuncCoverage = FuncCoverages.find(Function)->second;
  
              if (LineExecs.find(Function) == LineExecs.end()) {
                if (Block->getCount()) {
                  ++FuncCoverage.LinesExec;
                  LineExecs[Function] = true;
                } else {
                  LineExecs[Function] = false;
                }
                ++FuncCoverage.LogicalLines;
              } else if (!LineExecs[Function] && Block->getCount()) {
                ++FuncCoverage.LinesExec;
                LineExecs[Function] = true;
              }
            }
          }
  
          if (LineCount == 0)
            CovOS << "    #####:";
          else {
            CovOS << format("%9" PRIu64 ":", LineCount);
            ++FileCoverage.LinesExec;
          }
          ++FileCoverage.LogicalLines;
  
          AllLines.printNext(CovOS, LineIndex + 1);
  
          uint32_t BlockNo = 0;
          uint32_t EdgeNo = 0;
          for (const GCOVBlock *Block : Blocks) {
            // Only print block and branch information at the end of the block.
            if (Block->getLastLine() != LineIndex + 1)
              continue;
            if (Options.AllBlocks)
              printBlockInfo(CovOS, *Block, LineIndex, BlockNo);
            if (Options.BranchInfo) {
              size_t NumEdges = Block->getNumDstEdges();
              if (NumEdges > 1)
                printBranchInfo(CovOS, *Block, FileCoverage, EdgeNo);
              else if (Options.UncondBranch && NumEdges == 1)
                printUncondBranchInfo(CovOS, EdgeNo,
                                      (*Block->dst_begin())->Count);
            }
          }
        }
      }
      FileCoverages.push_back(std::make_pair(CoveragePath, FileCoverage));
    }
  
    // FIXME: There is no way to detect calls given current instrumentation.
    if (Options.FuncCoverage)
      printFuncCoverage(InfoOS);
    printFileCoverage(InfoOS);
  }
  
  /// printFunctionSummary - Print function and block summary.
  void FileInfo::printFunctionSummary(raw_ostream &OS,
                                      const FunctionVector &Funcs) const {
    for (const GCOVFunction *Func : Funcs) {
      uint64_t EntryCount = Func->getEntryCount();
      uint32_t BlocksExec = 0;
      for (const GCOVBlock &Block : Func->blocks())
        if (Block.getNumDstEdges() && Block.getCount())
          ++BlocksExec;
  
      OS << "function " << Func->getName() << " called " << EntryCount
         << " returned " << safeDiv(Func->getExitCount() * 100, EntryCount)
         << "% blocks executed "
         << safeDiv(BlocksExec * 100, Func->getNumBlocks() - 1) << "%\n";
    }
  }
  
  /// printBlockInfo - Output counts for each block.
  void FileInfo::printBlockInfo(raw_ostream &OS, const GCOVBlock &Block,
                                uint32_t LineIndex, uint32_t &BlockNo) const {
    if (Block.getCount() == 0)
      OS << "    $$$$$:";
    else
      OS << format("%9" PRIu64 ":", Block.getCount());
    OS << format("%5u-block %2u\n", LineIndex + 1, BlockNo++);
  }
  
  /// printBranchInfo - Print conditional branch probabilities.
  void FileInfo::printBranchInfo(raw_ostream &OS, const GCOVBlock &Block,
                                 GCOVCoverage &Coverage, uint32_t &EdgeNo) {
    SmallVector<uint64_t, 16> BranchCounts;
    uint64_t TotalCounts = 0;
    for (const GCOVEdge *Edge : Block.dsts()) {
      BranchCounts.push_back(Edge->Count);
      TotalCounts += Edge->Count;
      if (Block.getCount())
        ++Coverage.BranchesExec;
      if (Edge->Count)
        ++Coverage.BranchesTaken;
      ++Coverage.Branches;
  
      if (Options.FuncCoverage) {
        const GCOVFunction *Function = &Block.getParent();
        GCOVCoverage &FuncCoverage = FuncCoverages.find(Function)->second;
        if (Block.getCount())
          ++FuncCoverage.BranchesExec;
        if (Edge->Count)
          ++FuncCoverage.BranchesTaken;
        ++FuncCoverage.Branches;
      }
    }
  
    for (uint64_t N : BranchCounts)
      OS << format("branch %2u ", EdgeNo++)
         << formatBranchInfo(Options, N, TotalCounts) << "\n";
  }
  
  /// printUncondBranchInfo - Print unconditional branch probabilities.
  void FileInfo::printUncondBranchInfo(raw_ostream &OS, uint32_t &EdgeNo,
                                       uint64_t Count) const {
    OS << format("unconditional %2u ", EdgeNo++)
       << formatBranchInfo(Options, Count, Count) << "\n";
  }
  
  // printCoverage - Print generic coverage info used by both printFuncCoverage
  // and printFileCoverage.
  void FileInfo::printCoverage(raw_ostream &OS,
                               const GCOVCoverage &Coverage) const {
    OS << format("Lines executed:%.2f%% of %u\n",
                 double(Coverage.LinesExec) * 100 / Coverage.LogicalLines,
                 Coverage.LogicalLines);
    if (Options.BranchInfo) {
      if (Coverage.Branches) {
        OS << format("Branches executed:%.2f%% of %u\n",
                     double(Coverage.BranchesExec) * 100 / Coverage.Branches,
                     Coverage.Branches);
        OS << format("Taken at least once:%.2f%% of %u\n",
                     double(Coverage.BranchesTaken) * 100 / Coverage.Branches,
                     Coverage.Branches);
      } else {
        OS << "No branches\n";
      }
      OS << "No calls\n"; // to be consistent with gcov
    }
  }
  
  // printFuncCoverage - Print per-function coverage info.
  void FileInfo::printFuncCoverage(raw_ostream &OS) const {
    for (const auto &FC : FuncCoverages) {
      const GCOVCoverage &Coverage = FC.second;
      OS << "Function '" << Coverage.Name << "'\n";
      printCoverage(OS, Coverage);
      OS << "\n";
    }
  }
  
  // printFileCoverage - Print per-file coverage info.
  void FileInfo::printFileCoverage(raw_ostream &OS) const {
    for (const auto &FC : FileCoverages) {
      const std::string &Filename = FC.first;
      const GCOVCoverage &Coverage = FC.second;
      OS << "File '" << Coverage.Name << "'\n";
      printCoverage(OS, Coverage);
      if (!Options.NoOutput)
        OS << Coverage.Name << ":creating '" << Filename << "'\n";
      OS << "\n";
    }
  }
  