diff options
Diffstat (limited to 'llvm/lib/ProfileData/GCOV.cpp')
| -rw-r--r-- | llvm/lib/ProfileData/GCOV.cpp | 940 | 
1 files changed, 940 insertions, 0 deletions
| diff --git a/llvm/lib/ProfileData/GCOV.cpp b/llvm/lib/ProfileData/GCOV.cpp new file mode 100644 index 000000000000..00e6294c57a6 --- /dev/null +++ b/llvm/lib/ProfileData/GCOV.cpp @@ -0,0 +1,940 @@ +//===- GCOV.cpp - LLVM coverage tool --------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// GCOV implements the interface to read and write coverage files that use +// 'gcov' format. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ProfileData/GCOV.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/MD5.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 = std::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(std::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(std::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) +    llvm::stable_sort(DstEdges, [](const GCOVEdge *E1, const GCOVEdge *E2) { +      return E1->Dst.Number < E2->Dst.Number; +    }); +} + +/// 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 + +//===----------------------------------------------------------------------===// +// Cycles detection +// +// The algorithm in GCC is based on the algorihtm by Hawick & James: +//   "Enumerating Circuits and Loops in Graphs with Self-Arcs and Multiple-Arcs" +//   http://complexity.massey.ac.nz/cstn/013/cstn-013.pdf. + +/// Get the count for the detected cycle. +uint64_t GCOVBlock::getCycleCount(const Edges &Path) { +  uint64_t CycleCount = std::numeric_limits<uint64_t>::max(); +  for (auto E : Path) { +    CycleCount = std::min(E->CyclesCount, CycleCount); +  } +  for (auto E : Path) { +    E->CyclesCount -= CycleCount; +  } +  return CycleCount; +} + +/// Unblock a vertex previously marked as blocked. +void GCOVBlock::unblock(const GCOVBlock *U, BlockVector &Blocked, +                        BlockVectorLists &BlockLists) { +  auto it = find(Blocked, U); +  if (it == Blocked.end()) { +    return; +  } + +  const size_t index = it - Blocked.begin(); +  Blocked.erase(it); + +  const BlockVector ToUnblock(BlockLists[index]); +  BlockLists.erase(BlockLists.begin() + index); +  for (auto GB : ToUnblock) { +    GCOVBlock::unblock(GB, Blocked, BlockLists); +  } +} + +bool GCOVBlock::lookForCircuit(const GCOVBlock *V, const GCOVBlock *Start, +                               Edges &Path, BlockVector &Blocked, +                               BlockVectorLists &BlockLists, +                               const BlockVector &Blocks, uint64_t &Count) { +  Blocked.push_back(V); +  BlockLists.emplace_back(BlockVector()); +  bool FoundCircuit = false; + +  for (auto E : V->dsts()) { +    const GCOVBlock *W = &E->Dst; +    if (W < Start || find(Blocks, W) == Blocks.end()) { +      continue; +    } + +    Path.push_back(E); + +    if (W == Start) { +      // We've a cycle. +      Count += GCOVBlock::getCycleCount(Path); +      FoundCircuit = true; +    } else if (find(Blocked, W) == Blocked.end() && // W is not blocked. +               GCOVBlock::lookForCircuit(W, Start, Path, Blocked, BlockLists, +                                         Blocks, Count)) { +      FoundCircuit = true; +    } + +    Path.pop_back(); +  } + +  if (FoundCircuit) { +    GCOVBlock::unblock(V, Blocked, BlockLists); +  } else { +    for (auto E : V->dsts()) { +      const GCOVBlock *W = &E->Dst; +      if (W < Start || find(Blocks, W) == Blocks.end()) { +        continue; +      } +      const size_t index = find(Blocked, W) - Blocked.begin(); +      BlockVector &List = BlockLists[index]; +      if (find(List, V) == List.end()) { +        List.push_back(V); +      } +    } +  } + +  return FoundCircuit; +} + +/// Get the count for the list of blocks which lie on the same line. +void GCOVBlock::getCyclesCount(const BlockVector &Blocks, uint64_t &Count) { +  for (auto Block : Blocks) { +    Edges Path; +    BlockVector Blocked; +    BlockVectorLists BlockLists; + +    GCOVBlock::lookForCircuit(Block, Block, Path, Blocked, BlockLists, Blocks, +                              Count); +  } +} + +/// Get the count for the list of blocks which lie on the same line. +uint64_t GCOVBlock::getLineCount(const BlockVector &Blocks) { +  uint64_t Count = 0; + +  for (auto Block : Blocks) { +    if (Block->getNumSrcEdges() == 0) { +      // The block has no predecessors and a non-null counter +      // (can be the case with entry block in functions). +      Count += Block->getCount(); +    } else { +      // Add counts from predecessors that are not on the same line. +      for (auto E : Block->srcs()) { +        const GCOVBlock *W = &E->Src; +        if (find(Blocks, W) == Blocks.end()) { +          Count += E->Count; +        } +      } +    } +    for (auto E : Block->dsts()) { +      E->CyclesCount = E->Count; +    } +  } + +  GCOVBlock::getCyclesCount(Blocks, Count); + +  return Count; +} + +//===----------------------------------------------------------------------===// +// 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); +  if (Options.HashFilenames) { +    MD5 Hasher; +    MD5::MD5Result Result; +    Hasher.update(Filename.str()); +    Hasher.final(Result); +    CoveragePath += "##" + Result.digest().str().str(); +  } +  CoveragePath += ".gcov"; +  return CoveragePath; +} + +std::unique_ptr<raw_ostream> +FileInfo::openCoveragePath(StringRef CoveragePath) { +  if (Options.NoOutput) +    return std::make_unique<raw_null_ostream>(); + +  std::error_code EC; +  auto OS = +      std::make_unique<raw_fd_ostream>(CoveragePath, EC, sys::fs::OF_Text); +  if (EC) { +    errs() << EC.message() << "\n"; +    return std::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()); +  llvm::sort(Filenames); + +  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; +        for (const GCOVBlock *Block : Blocks) { +          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; +            } +          } +        } + +        const uint64_t LineCount = GCOVBlock::getLineCount(Blocks); +        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"; +  } +} | 
