# [PATCH] llvm-cov: Added -b option for branch probabilities.

Justin Bogner mail at justinbogner.com
Thu Dec 12 11:19:58 PST 2013

```Yuchen Wu <yuchenericwu at hotmail.com> writes:
>>> +// It is unlikely--but possible--for multiple functions to be on
>>> the same line.
>>> +// But optimize for the common case.
>>
>> What is this comment talking about?
>
> The reason FunctionVector exists is because LineData needs to be able
> to store all of the functions that are on a single line. The comment
> is to explain that it's unlikely two functions will be on the same
> line, so optimize for the common case by setting the SmallVector
> typedef to contain a single element.

I may have been subtly implying you should improve the comment :)

>>> +typedef SmallVector<const GCOVFunction *, 1> FunctionVector;
>>> +typedef DenseMap<uint32_t, FunctionVector> FunctionLines;
>>> typedef SmallVector<const GCOVBlock *, 4> BlockVector;
>>> -typedef DenseMap<uint32_t, BlockVector> LineData;
>>> +typedef DenseMap<uint32_t, BlockVector> BlockLines;
>
>>> +// Safe integer division, returns 0 if divisor is 0.
>>> +static uint32_t safeDiv(uint64_t Numerator, uint64_t Divisor) {
>>> + if (Divisor == 0)
>>> + return 0;
>>> + return Numerator/Divisor;
>>> +}
>>
>> Why do you need this?
>>
>>> +
>>> +// 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) {
>>> + uint8_t Res = safeDiv(Numerator*100+Divisor/2, Divisor);
>>> + if (Res == 0 && Numerator)
>>> + return 1;
>>
>> This seems like pretty strange behaviour if Divisor == 0.
>
> The safeDiv and branchDiv functions need to return 0 if it's asked to
> divide 0 by 0, which actually happens quite often.

Firstl, branchDiv returns 1%, not zero - that seems arbitrary and
insane. Second, you still haven't answered why this happens / is
accepted. This feels like it's papering over a problem somewhere else to
me.

> I've implemented all the other suggestions you made into this new
> patch. Enjoy!
>
> From 536dc2a15462a4e97186c9b99a87f0cc45cad1c5 Mon Sep 17 00:00:00 2001
> From: Yuchen Wu <yuchen_wu at apple.com>
> Date: Tue, 3 Dec 2013 14:27:17 -0800
> Subject: [PATCH 2/7] llvm-cov: Added -b option for branch probabilities.
>
> This option tells llvm-cov to print out branch probabilities when
> a basic block contains multiple branches. It also prints out some
> function summary info including the number of times the function enters,
> the percent of time it returns, and how many blocks were executed.
>
> Also updated tests.
> ---
>  include/llvm/Support/GCOV.h                    |  74 ++++++++++--
>  lib/IR/GCOV.cpp                                | 153 +++++++++++++++++++++----
>  test/tools/llvm-cov/Inputs/test_-a_-b.cpp.gcov | 134 ++++++++++++++++++++++
>  test/tools/llvm-cov/Inputs/test_-a_-b.h.gcov   |  12 ++
>  test/tools/llvm-cov/llvm-cov.test              |   4 +
>  tools/llvm-cov/llvm-cov.cpp                    |   8 +-
>  6 files changed, 349 insertions(+), 36 deletions(-)
>  create mode 100644 test/tools/llvm-cov/Inputs/test_-a_-b.cpp.gcov
>  create mode 100644 test/tools/llvm-cov/Inputs/test_-a_-b.h.gcov
>
> diff --git a/include/llvm/Support/GCOV.h b/include/llvm/Support/GCOV.h
> index 852c0be..fd816c7 100644
> --- a/include/llvm/Support/GCOV.h
> +++ b/include/llvm/Support/GCOV.h
> @@ -36,9 +36,9 @@ namespace GCOV {
>
>  /// GCOVOptions - A struct for passing gcov options between functions.
>  struct GCOVOptions {
> -  GCOVOptions(bool A): AllBlocks(A) {}
> -
> +  GCOVOptions(bool A, bool B): AllBlocks(A), BranchProb(B) {}
>    bool AllBlocks;
> +  bool BranchProb;
>  };
>
>  /// GCOVBuffer - A wrapper around MemoryBuffer to provide GCOV specific
> @@ -236,6 +236,7 @@ private:
>    uint32_t ProgramCount;
>  };
>
> +/// GCOVEdge - Collects edge information.
>  struct GCOVEdge {
>    GCOVEdge(GCOVBlock *S, GCOVBlock *D): Src(S), Dst(D), Count(0) {}
>
> @@ -247,12 +248,21 @@ struct GCOVEdge {
>  /// GCOVFunction - Collects function information.
>  class GCOVFunction {
>  public:
> +  typedef SmallVectorImpl<GCOVBlock *>::const_iterator BlockIterator;
> +
>    GCOVFunction(GCOVFile &P) : Parent(P), Ident(0), LineNumber(0) {}
>    ~GCOVFunction();
>    bool readGCNO(GCOVBuffer &Buffer, GCOV::GCOVVersion Version);
>    bool readGCDA(GCOVBuffer &Buffer, GCOV::GCOVVersion Version);
> +  StringRef getName() const { return Name; }
>    StringRef getFilename() const { return Filename; }
>    size_t getNumBlocks() const { return Blocks.size(); }
> +  uint64_t getEntryCount() const;
> +  uint64_t getExitCount() const;
> +
> +  BlockIterator block_begin() const { return Blocks.begin(); }
> +  BlockIterator block_end() const { return Blocks.end(); }
> +
>    void dump() const;
>    void collectLineCounts(FileInfo &FI);
>  private:
> @@ -268,26 +278,43 @@ private:
>
>  /// GCOVBlock - Collects block information.
>  class GCOVBlock {
> +  struct EdgeWeight {
> +    EdgeWeight(GCOVBlock *D): Dst(D), Count(0) {}
> +
> +    GCOVBlock *Dst;
> +    uint64_t Count;
> +  };
> +
> +  struct SortDstEdgesFunctor {
> +    bool operator()(const GCOVEdge *E1, const GCOVEdge *E2) {
> +      return E1->Dst->Number < E2->Dst->Number;
> +    }
> +  };
>  public:
>    typedef SmallVectorImpl<GCOVEdge *>::const_iterator EdgeIterator;
>
> -  GCOVBlock(GCOVFunction &P, uint32_t N) :
> -    Parent(P), Number(N), Counter(0), SrcEdges(), DstEdges(), Lines() {}
> +  GCOVBlock(GCOVFunction &P, uint32_t N) : Parent(P), Number(N), Counter(0),
> +    DstEdgesAreSorted(true), SrcEdges(), DstEdges(), Lines() {}
>    ~GCOVBlock();
> +  void addLine(uint32_t N) { Lines.push_back(N); }
> +  uint32_t getLastLine() const { return Lines.back(); }
> +  void addCount(size_t DstEdgeNo, uint64_t N);
> +  uint64_t getCount() const { return Counter; }
> +
>      assert(Edge->Dst == this); // up to caller to ensure edge is valid
>      SrcEdges.push_back(Edge);
>    }
>      assert(Edge->Src == this); // up to caller to ensure edge is valid
> +    // Check if adding this edge causes list to become unsorted.
> +    if (DstEdges.size() && DstEdges.back()->Dst->Number > Edge->Dst->Number)
> +      DstEdgesAreSorted = false;
>      DstEdges.push_back(Edge);
>    }
> -  void addLine(uint32_t N) { Lines.push_back(N); }
> -  uint32_t getLastLine() const { return Lines.back(); }
> -  void addCount(size_t DstEdgeNo, uint64_t N);
> -  uint64_t getCount() const { return Counter; }
>    size_t getNumSrcEdges() const { return SrcEdges.size(); }
>    size_t getNumDstEdges() const { return DstEdges.size(); }
> +  void sortDstEdges();
>
>    EdgeIterator src_begin() const { return SrcEdges.begin(); }
>    EdgeIterator src_end() const { return SrcEdges.end(); }
> @@ -300,23 +327,46 @@ private:
>    GCOVFunction &Parent;
>    uint32_t Number;
>    uint64_t Counter;
> +  bool DstEdgesAreSorted;
>    SmallVector<GCOVEdge *, 16> SrcEdges;
>    SmallVector<GCOVEdge *, 16> DstEdges;
>    SmallVector<uint32_t, 16> Lines;
>  };
>
> +// It is unlikely--but possible--for multiple functions to be on the same line.
> +// But optimize for the common case.
> +typedef SmallVector<const GCOVFunction *, 1> FunctionVector;
> +typedef DenseMap<uint32_t, FunctionVector> FunctionLines;
>  typedef SmallVector<const GCOVBlock *, 4> BlockVector;
> -typedef DenseMap<uint32_t, BlockVector> LineData;
> +typedef DenseMap<uint32_t, BlockVector> BlockLines;
> +
>  class FileInfo {
> +  struct LineData {
> +    BlockLines Blocks;
> +    FunctionLines Functions;
> +  };
>  public:
> +  FileInfo(const GCOVOptions &Options) :
> +    Options(Options), LineInfo(), RunCount(0), ProgramCount(0) {}
> +
>    void addBlockLine(StringRef Filename, uint32_t Line, const GCOVBlock *Block) {
> -    LineInfo[Filename][Line-1].push_back(Block);
> +    LineInfo[Filename].Blocks[Line-1].push_back(Block);
> +  }
> +  void addFunctionLine(StringRef Filename, uint32_t Line,
> +                       const GCOVFunction *Function) {
> +    LineInfo[Filename].Functions[Line-1].push_back(Function);
>    }
>    void setRunCount(uint32_t Runs) { RunCount = Runs; }
>    void setProgramCount(uint32_t Programs) { ProgramCount = Programs; }
> -  void print(StringRef GCNOFile, StringRef GCDAFile,
> -             const GCOVOptions &Options) const;
> +  void print(StringRef GCNOFile, StringRef GCDAFile) const;
> +  void printFunctionSummary(raw_fd_ostream &OS, const LineData &Line,
> +                            uint32_t LineIndex) const;
> +  void printBlockInfo(raw_fd_ostream &OS, const GCOVBlock &Block,
> +                      uint32_t LineIndex, uint32_t &BlockNo) const;
> +  void printBranchInfo(raw_fd_ostream &OS, const GCOVBlock &Block,
> +                       uint32_t LineIndex, uint32_t &EdgeNo) const;
>  private:
> +  const GCOVOptions &Options;
>    StringMap<LineData> LineInfo;
>    uint32_t RunCount;
>    uint32_t ProgramCount;
> diff --git a/lib/IR/GCOV.cpp b/lib/IR/GCOV.cpp
> index fb73700..662113d 100644
> --- a/lib/IR/GCOV.cpp
> +++ b/lib/IR/GCOV.cpp
> @@ -19,6 +19,7 @@
>  #include "llvm/Support/Format.h"
>  #include "llvm/Support/MemoryObject.h"
>  #include "llvm/Support/system_error.h"
> +#include <algorithm>
>  using namespace llvm;
>
>  //===----------------------------------------------------------------------===//
> @@ -278,10 +279,23 @@ bool GCOVFunction::readGCDA(GCOVBuffer &Buff, GCOV::GCOVVersion Version) {
>        --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();
> +}
> +
>  /// dump - Dump GCOVFunction content to dbgs() for debugging purposes.
>  void GCOVFunction::dump() const {
>    dbgs() <<  "===== " << Name << " @ " << Filename << ":" << LineNumber << "\n";
> @@ -296,6 +310,7 @@ void GCOVFunction::collectLineCounts(FileInfo &FI) {
>    for (SmallVectorImpl<GCOVBlock *>::iterator I = Blocks.begin(),
>           E = Blocks.end(); I != E; ++I)
>      (*I)->collectLineCounts(FI);
>  }
>
>  //===----------------------------------------------------------------------===//
> @@ -318,6 +333,15 @@ void GCOVBlock::addCount(size_t DstEdgeNo, uint64_t N) {
>      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) {
> @@ -357,9 +381,27 @@ void GCOVBlock::dump() const {
>  //===----------------------------------------------------------------------===//
>  // FileInfo implementation.
>
> +// Safe integer division, returns 0 if divisor is 0.
> +static uint32_t safeDiv(uint64_t Numerator, uint64_t Divisor) {
> +  if (Divisor == 0)
> +    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) {
> +  uint8_t Res = safeDiv(Numerator*100+Divisor/2, Divisor);
> +  if (Res == 0 && Numerator)
> +    return 1;
> +  if (Res == 100 && Numerator != Divisor)
> +    return 99;
> +  return Res;
> +}
> +
>  /// print -  Print source files with collected line count information.
> -void FileInfo::print(StringRef GCNOFile, StringRef GCDAFile,
> -                     const GCOVOptions &Options) const {
> +void FileInfo::print(StringRef GCNOFile, StringRef GCDAFile) const {
>    for (StringMap<LineData>::const_iterator I = LineInfo.begin(),
>           E = LineInfo.end(); I != E; ++I) {
>      StringRef Filename = I->first();
> @@ -383,12 +425,21 @@ void FileInfo::print(StringRef GCNOFile, StringRef GCDAFile,
>      OS << "        -:    0:Programs:" << ProgramCount << "\n";
>
>      const LineData &Line = I->second;
> -    for (uint32_t i = 0; !AllLines.empty(); ++i) {
> -      LineData::const_iterator BlocksIt = Line.find(i);
> +    for (uint32_t LineIndex = 0; !AllLines.empty(); ++LineIndex) {
> +      if (Options.BranchProb)
> +        printFunctionSummary(OS, Line, LineIndex);
>
> -      if (BlocksIt != Line.end()) {
> -        // Add up the block counts to form line counts.
> +      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.
> +        OS << "        -:";
> +        std::pair<StringRef, StringRef> P = AllLines.split('\n');
> +        OS << format("%5u:", LineIndex+1) << P.first << "\n";
> +        AllLines = P.second;
> +      } else {
>          const BlockVector &Blocks = BlocksIt->second;
> +
> +        // Add up the block counts to form line counts.
>          uint64_t LineCount = 0;
>          for (BlockVector::const_iterator I = Blocks.begin(), E = Blocks.end();
>                 I != E; ++I) {
> @@ -406,30 +457,88 @@ void FileInfo::print(StringRef GCNOFile, StringRef GCDAFile,
>            OS << "    #####:";
>          else
>            OS << format("%9" PRIu64 ":", LineCount);
> -      } else {
> -        OS << "        -:";
> -      }
> -      std::pair<StringRef, StringRef> P = AllLines.split('\n');
> -      OS << format("%5u:", i+1) << P.first << "\n";
> -      AllLines = P.second;
>
> -      if (Options.AllBlocks && BlocksIt != Line.end()) {
> -        // Output the counts for each block at the last line of the block.
> +        std::pair<StringRef, StringRef> P = AllLines.split('\n');
> +        OS << format("%5u:", LineIndex+1) << P.first << "\n";
> +        AllLines = P.second;
> +
>          uint32_t BlockNo = 0;
> -        const BlockVector &Blocks = BlocksIt->second;
> +        uint32_t EdgeNo = 0;
>          for (BlockVector::const_iterator I = Blocks.begin(), E = Blocks.end();
>                 I != E; ++I) {
>            const GCOVBlock *Block = *I;
> -          if (Block->getLastLine() != i+1)
> -            continue;
>
> -          if (Block->getCount() == 0)
> -            OS << "    \$\$\$\$\$:";
> -          else
> -            OS << format("%9lu:", Block->getCount());
> -          OS << format("%5u-block  %u\n", i+1, BlockNo++);
> +          // Only print block and branch information at the end of the block.
> +          if (Block->getLastLine() != LineIndex+1)
> +            continue;
> +          if (Options.AllBlocks)
> +            printBlockInfo(OS, *Block, LineIndex, BlockNo);
> +          if (Options.BranchProb)
> +            printBranchInfo(OS, *Block, LineIndex, EdgeNo);
>          }
>        }
>      }
>    }
>  }
> +
> +/// printFunctionSummary - Print function and block summary.
> +void FileInfo::printFunctionSummary(raw_fd_ostream &OS, const LineData &Line,
> +                                    uint32_t LineIndex) const {
> +  FunctionLines::const_iterator FuncsIt = Line.Functions.find(LineIndex);
> +  if (FuncsIt != Line.Functions.end()) {

Better to return early here and save a level of indentation.

> +    const SmallVectorImpl<const GCOVFunction *> &Funcs = FuncsIt->second;
> +    for (SmallVectorImpl<const GCOVFunction*>::const_iterator I =
> +           Funcs.begin(), E = Funcs.end(); I != E; ++I) {
> +      const GCOVFunction *Func = *I;
> +      uint64_t EntryCount = Func->getEntryCount();
> +      uint32_t BlocksExecuted = 0;
> +      for (GCOVFunction::BlockIterator I = Func->block_begin(),
> +             E = Func->block_end(); I != E; ++I) {
> +        const GCOVBlock *Block = *I;
> +        if (Block->getNumDstEdges() && Block->getCount())
> +            ++BlocksExecuted;
> +      }
> +
> +      OS << "function " << Func->getName() << " called " << EntryCount
> +         << " returned " << safeDiv(Func->getExitCount()*100, EntryCount)
> +         << "% blocks executed "
> +         << safeDiv(BlocksExecuted*100, Func->getNumBlocks()-1) << "%\n";
> +    }
> +  }
> +}
> +
> +/// printBlockInfo - Output counts for each block.
> +void FileInfo::printBlockInfo(raw_fd_ostream &OS, const GCOVBlock &Block,
> +                              uint32_t LineIndex, uint32_t &BlockNo) const {
> +  if (Block.getCount() == 0)
> +    OS << "    \$\$\$\$\$:";
> +  else
> +    OS << format("%9lu:", Block.getCount());
> +  OS << format("%5u-block %2u\n", LineIndex+1, BlockNo++);
> +}
> +
> +/// printBranchInfo - Print branch probabilities for blocks that have
> +/// conditional branches.
> +void FileInfo::printBranchInfo(raw_fd_ostream &OS, const GCOVBlock &Block,
> +                               uint32_t LineIndex, uint32_t &EdgeNo) const {
> +  if (Block.getNumDstEdges() < 2)
> +    return;
> +
> +  SmallVector<uint64_t, 16> BranchCounts;
> +  uint64_t TotalCounts = 0;
> +  for (GCOVBlock::EdgeIterator I = Block.dst_begin(), E = Block.dst_end();
> +         I != E; ++I) {
> +    const GCOVEdge *Edge = *I;
> +    BranchCounts.push_back(Edge->Count);
> +    TotalCounts += Edge->Count;
> +  }
> +
> +  for (SmallVectorImpl<uint64_t>::const_iterator I = BranchCounts.begin(),
> +         E = BranchCounts.end(); I != E; ++I) {
> +    if (TotalCounts)
> +      OS << format("branch %2u taken %u%%\n", EdgeNo++,
> +                   branchDiv(*I, TotalCounts));
> +    else
> +      OS << format("branch %2u never executed\n", EdgeNo++);
> +  }
> +}
> diff --git a/test/tools/llvm-cov/Inputs/test_-a_-b.cpp.gcov b/test/tools/llvm-cov/Inputs/test_-a_-b.cpp.gcov
> new file mode 100644
> index 0000000..ae21037
> --- /dev/null
> +++ b/test/tools/llvm-cov/Inputs/test_-a_-b.cpp.gcov
> @@ -0,0 +1,134 @@
> +        -:    0:Source:test.cpp
> +        -:    0:Graph:test.gcno
> +        -:    0:Data:test.gcda
> +        -:    0:Runs:2
> +        -:    0:Programs:1
> +        -:    1:#include "test.h"
> +        -:    2:#include <cstdlib>
> +        -:    3:
> +        -:    4:bool on = false;
> +        -:    5:int len = 42;
> +        -:    6:double grid[10][10] = {0};
> +        -:    7:const char * hello = "world";
> +        -:    8:const char * world = "hello";
> +        -:    9:
> +function _ZN1A1BEv called 8589934592 returned 100% blocks executed 100%
> +8589934592:   10:void A::B() {}
> +8589934592:   10-block  0
> +        -:   11:
> +function _Z7uselessv called 0 returned 0% blocks executed 0%
> +    #####:   12:void useless() {}
> +    \$\$\$\$\$:   12-block  0
> +        -:   13:
> +function _Z12more_uselessv called 0 returned 0% blocks executed 0%
> +        -:   14:double more_useless() {
> +    #####:   15:  return 0;
> +    \$\$\$\$\$:   15-block  0
> +        -:   16:}
> +        -:   17:
> +function _Z3foov called 2 returned 100% blocks executed 100%
> +        -:   18:int foo() {
> +        2:   19:  on = true;
> +        2:   20:  return 3;
> +        2:   20-block  0
> +        -:   21:}
> +        -:   22:
> +function _Z3barv called 0 returned 0% blocks executed 0%
> +        -:   23:int bar() {
> +    #####:   24:  len--;
> +    #####:   25:  return foo() + 45;
> +    \$\$\$\$\$:   25-block  0
> +        -:   26:}
> +        -:   27:
> +function _Z6assignii called 8 returned 100% blocks executed 100%
> +        8:   28:void assign(int ii, int jj) {
> +        8:   29:  grid[ii][jj] = (ii+1) * (jj+1);
> +        8:   30:}
> +        8:   30-block  0
> +        -:   31:
> +function _Z15initialize_gridv called 2 returned 100% blocks executed 100%
> +        -:   32:void initialize_grid() {
> +        6:   33:  for (int ii = 0; ii < 2; ii++)
> +        2:   33-block  0
> +        6:   33-block  1
> +branch  0 taken 67%
> +branch  1 taken 33%
> +        4:   33-block  2
> +       12:   34:    for (int jj = 0; jj < 2; jj++)
> +        4:   34-block  0
> +       12:   34-block  1
> +branch  0 taken 67%
> +branch  1 taken 33%
> +        8:   34-block  2
> +        8:   35:      assign(ii, jj);
> +        8:   35-block  0
> +        4:   35-block  1
> +        2:   36:}
> +        2:   36-block  0
> +        -:   37:
> +function main called 2 returned 100% blocks executed 94%
> +        -:   38:int main() {
> +        2:   39:  initialize_grid();
> +        -:   40:
> +        2:   41:  int a = 2;
> +        2:   42:  on = rand() % 2;
> +        2:   43:  if (on) {
> +        2:   43-block  0
> +branch  0 taken 100%
> +branch  1 taken 0%
> +        2:   44:    foo();
> +        2:   45:    ++a;
> +        2:   46:  } else {
> +        2:   46-block  0
> +    #####:   47:    bar();
> +    #####:   48:    a += rand();
> +    \$\$\$\$\$:   48-block  0
> +        -:   49:  }
> +        -:   50:
> +       22:   51:  for (int ii = 0; ii < 10; ++ii) {
> +        2:   51-block  0
> +       22:   51-block  1
> +branch  0 taken 91%
> +branch  1 taken 9%
> +       20:   51-block  2
> +       20:   52:    switch (rand() % 5) {
> +       20:   52-block  0
> +branch  0 taken 20%
> +branch  1 taken 0%
> +branch  2 taken 10%
> +branch  3 taken 30%
> +branch  4 taken 40%
> +        -:   53:      case 0:
> +        4:   54:        a += rand();
> +        4:   55:        break;
> +        4:   55-block  0
> +        -:   56:      case 1:
> +        -:   57:      case 2:
> +        2:   58:        a += rand() / rand();
> +        2:   59:        break;
> +        2:   59-block  0
> +        -:   60:      case 3:
> +        6:   61:        a -= rand();
> +        6:   62:        break;
> +        6:   62-block  0
> +        -:   63:      default:
> +        8:   64:        a = -1;
> +        8:   65:    }
> +        8:   65-block  0
> +       20:   66:  }
> +       20:   66-block  0
> +        -:   67:
> +        2:   68:  A thing;
> +8589934594:   69:  for (uint64_t ii = 0; ii < 4294967296; ++ii)
> +        2:   69-block  0
> +8589934594:   69-block  1
> +branch  0 taken 99%
> +branch  1 taken 1%
> +8589934592:   69-block  2
> +8589934592:   70:    thing.B();
> +8589934592:   70-block  0
> +        -:   71:
> +        2:   72:  return a + 8 + grid[2][3] + len;
> +        2:   72-block  0
> +        -:   73:  return more_useless();
> +        -:   74:}
> diff --git a/test/tools/llvm-cov/Inputs/test_-a_-b.h.gcov b/test/tools/llvm-cov/Inputs/test_-a_-b.h.gcov
> new file mode 100644
> index 0000000..f3dabcb
> --- /dev/null
> +++ b/test/tools/llvm-cov/Inputs/test_-a_-b.h.gcov
> @@ -0,0 +1,12 @@
> +        -:    0:Source:./test.h
> +        -:    0:Graph:test.gcno
> +        -:    0:Data:test.gcda
> +        -:    0:Runs:2
> +        -:    0:Programs:1
> +function _ZN1AC1Ev called 2 returned 100% blocks executed 100%
> +function _ZN1AC2Ev called 2 returned 100% blocks executed 100%
> +        2:    1:struct A {
> +        2:    1-block  0
> +        2:    1-block  1
> +        -:    2:  virtual void B();
> +        -:    3:};
> diff --git a/test/tools/llvm-cov/llvm-cov.test b/test/tools/llvm-cov/llvm-cov.test
> index 43725a3..bf0fb07 100644
> --- a/test/tools/llvm-cov/llvm-cov.test
> +++ b/test/tools/llvm-cov/llvm-cov.test
> @@ -13,6 +13,10 @@ RUN: llvm-cov -gcno=test.gcno -gcda=test.gcda -a
>  RUN: diff -aub test_-a.cpp.gcov test.cpp.gcov
>  RUN: diff -aub test_-a.h.gcov test.h.gcov
>
> +RUN: llvm-cov -gcno=test.gcno -gcda=test.gcda -a -b
> +RUN: diff -aub test_-a_-b.cpp.gcov test.cpp.gcov
> +RUN: diff -aub test_-a_-b.h.gcov test.h.gcov
> +
>  RUN: not llvm-cov -gcno=test_read_fail.gcno -gcda=test.gcda
>
>  RUN: not llvm-cov -gcno=test.gcno -gcda=test_file_checksum_fail.gcda
> diff --git a/tools/llvm-cov/llvm-cov.cpp b/tools/llvm-cov/llvm-cov.cpp
> index 235670b..fd4fa24 100644
> --- a/tools/llvm-cov/llvm-cov.cpp
> +++ b/tools/llvm-cov/llvm-cov.cpp
> @@ -33,6 +33,9 @@ InputGCDA("gcda", cl::desc("<input gcda file>"), cl::init(""));
>  static cl::opt<bool>
>  AllBlocks("a", cl::init(false), cl::desc("display all block info"));
>
> +static cl::opt<bool>
> +BranchProb("b", cl::init(false), cl::desc("display branch info"));
> +
>  //===----------------------------------------------------------------------===//
>  int main(int argc, char **argv) {
>    // Print a stack trace if we signal out.
> @@ -73,8 +76,9 @@ int main(int argc, char **argv) {
>    if (DumpGCOV)
>      GF.dump();
>
> -  FileInfo FI;
> +  GCOVOptions Options(AllBlocks, BranchProb);
> +  FileInfo FI(Options);
>    GF.collectLineCounts(FI);
> -  FI.print(InputGCNO, InputGCDA, GCOVOptions(AllBlocks));
> +  FI.print(InputGCNO, InputGCDA);
>    return 0;
>  }

```