[llvm] r216300 - llvm-cov: add code coverage tool that's based on coverage mapping format and clang's pgo.

Sean Silva chisophugis at gmail.com
Fri Aug 22 18:56:36 PDT 2014


On Fri, Aug 22, 2014 at 5:54 PM, Alex L <arphaman at gmail.com> wrote:

> The big-endian buildbots are failing, I will commit an XFAIL for
> big-endian for now.
>

In the future, consider just reverting instead. The benefit of doing that
is that then there isn't an XFAIL that can be forgotten.

-- Sean Silva


>
>
> 2014-08-22 15:56 GMT-07:00 Alex Lorenz <arphaman at gmail.com>:
>
>> Author: arphaman
>> Date: Fri Aug 22 17:56:03 2014
>> New Revision: 216300
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=216300&view=rev
>> Log:
>> llvm-cov: add code coverage tool that's based on coverage mapping format
>> and clang's pgo.
>>
>> This commit expands llvm-cov's functionality by adding support for a new
>> code coverage
>> tool that uses LLVM's coverage mapping format and clang's instrumentation
>> based profiling.
>> The gcov compatible tool can be invoked by supplying the 'gcov' command
>> as the first argument,
>> or by modifying the tool's name to end with 'gcov'.
>>
>> Differential Revision: http://reviews.llvm.org/D4445
>>
>> Added:
>>     llvm/trunk/test/tools/llvm-cov/Inputs/highlightedRanges.covmapping
>>  (with props)
>>     llvm/trunk/test/tools/llvm-cov/Inputs/highlightedRanges.profdata
>>  (with props)
>>     llvm/trunk/test/tools/llvm-cov/Inputs/lineExecutionCounts.covmapping
>>  (with props)
>>     llvm/trunk/test/tools/llvm-cov/Inputs/lineExecutionCounts.profdata
>>  (with props)
>>     llvm/trunk/test/tools/llvm-cov/Inputs/regionMarkers.covmapping
>>  (with props)
>>     llvm/trunk/test/tools/llvm-cov/Inputs/regionMarkers.profdata   (with
>> props)
>>     llvm/trunk/test/tools/llvm-cov/showHighlightedRanges.cpp
>>     llvm/trunk/test/tools/llvm-cov/showLineExecutionCounts.cpp
>>     llvm/trunk/test/tools/llvm-cov/showRegionMarkers.cpp
>>     llvm/trunk/tools/llvm-cov/CodeCoverage.cpp
>>     llvm/trunk/tools/llvm-cov/CoverageFilters.cpp
>>     llvm/trunk/tools/llvm-cov/CoverageFilters.h
>>     llvm/trunk/tools/llvm-cov/CoverageReport.cpp
>>     llvm/trunk/tools/llvm-cov/CoverageReport.h
>>     llvm/trunk/tools/llvm-cov/CoverageSummary.cpp
>>     llvm/trunk/tools/llvm-cov/CoverageSummary.h
>>     llvm/trunk/tools/llvm-cov/CoverageSummaryInfo.cpp
>>     llvm/trunk/tools/llvm-cov/CoverageSummaryInfo.h
>>     llvm/trunk/tools/llvm-cov/CoverageViewOptions.h
>>     llvm/trunk/tools/llvm-cov/FunctionCoverageMapping.h
>>     llvm/trunk/tools/llvm-cov/RenderingSupport.h
>>     llvm/trunk/tools/llvm-cov/SourceCoverageDataManager.cpp
>>     llvm/trunk/tools/llvm-cov/SourceCoverageDataManager.h
>>     llvm/trunk/tools/llvm-cov/SourceCoverageView.cpp
>>     llvm/trunk/tools/llvm-cov/SourceCoverageView.h
>>     llvm/trunk/tools/llvm-cov/TestingSupport.cpp
>> Modified:
>>     llvm/trunk/lib/ProfileData/CoverageMappingReader.cpp
>>     llvm/trunk/test/tools/llvm-cov/Inputs/README
>>     llvm/trunk/tools/llvm-cov/CMakeLists.txt
>>     llvm/trunk/tools/llvm-cov/LLVMBuild.txt
>>     llvm/trunk/tools/llvm-cov/Makefile
>>     llvm/trunk/tools/llvm-cov/llvm-cov.cpp
>>
>> Modified: llvm/trunk/lib/ProfileData/CoverageMappingReader.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ProfileData/CoverageMappingReader.cpp?rev=216300&r1=216299&r2=216300&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/lib/ProfileData/CoverageMappingReader.cpp (original)
>> +++ llvm/trunk/lib/ProfileData/CoverageMappingReader.cpp Fri Aug 22
>> 17:56:03 2014
>> @@ -289,18 +289,6 @@ ObjectFileCoverageMappingReader::ObjectF
>>      Object = std::move(File.get());
>>  }
>>
>> -ObjectFileCoverageMappingReader::ObjectFileCoverageMappingReader(
>> -    std::unique_ptr<MemoryBuffer> &ObjectBuffer, sys::fs::file_magic
>> Type)
>> -    : CurrentRecord(0) {
>> -  auto File = object::ObjectFile::createObjectFile(
>> -      ObjectBuffer->getMemBufferRef(), Type);
>> -  if (!File)
>> -    error(File.getError());
>> -  else
>> -    Object = OwningBinary<ObjectFile>(std::move(File.get()),
>> -                                      std::move(ObjectBuffer));
>> -}
>> -
>>  namespace {
>>  /// \brief The coverage mapping data for a single function.
>>  /// It points to the function's name.
>> @@ -347,19 +335,11 @@ struct SectionData {
>>
>>  template <typename T>
>>  std::error_code readCoverageMappingData(
>> -    SectionRef &ProfileNames, SectionRef &CoverageMapping,
>> +    SectionData &ProfileNames, StringRef Data,
>>      std::vector<ObjectFileCoverageMappingReader::ProfileMappingRecord>
>> &Records,
>>      std::vector<StringRef> &Filenames) {
>>    llvm::DenseSet<T> UniqueFunctionMappingData;
>>
>> -  // Get the contents of the given sections.
>> -  StringRef Data;
>> -  if (auto Err = CoverageMapping.getContents(Data))
>> -    return Err;
>> -  SectionData ProfileNamesData;
>> -  if (auto Err = ProfileNamesData.load(ProfileNames))
>> -    return Err;
>> -
>>    // Read the records in the coverage data section.
>>    while (!Data.empty()) {
>>      if (Data.size() < sizeof(CoverageMappingTURecord<T>))
>> @@ -418,9 +398,9 @@ std::error_code readCoverageMappingData(
>>          continue;
>>        UniqueFunctionMappingData.insert(MappingRecord.FunctionNamePtr);
>>        StringRef FunctionName;
>> -      if (auto Err = ProfileNamesData.get(MappingRecord.FunctionNamePtr,
>> -                                          MappingRecord.FunctionNameSize,
>> -                                          FunctionName))
>> +      if (auto Err =
>> +              ProfileNames.get(MappingRecord.FunctionNamePtr,
>> +                               MappingRecord.FunctionNameSize,
>> FunctionName))
>>          return Err;
>>
>>  Records.push_back(ObjectFileCoverageMappingReader::ProfileMappingRecord(
>>            Version, FunctionName, MappingRecord.FunctionHash, Mapping,
>> @@ -431,6 +411,63 @@ std::error_code readCoverageMappingData(
>>    return instrprof_error::success;
>>  }
>>
>> +static const char *TestingFormatMagic = "llvmcovmtestdata";
>> +
>> +static std::error_code decodeTestingFormat(StringRef Data,
>> +                                           SectionData &ProfileNames,
>> +                                           StringRef &CoverageMapping) {
>> +  Data = Data.substr(StringRef(TestingFormatMagic).size());
>> +  if (Data.size() < 1)
>> +    return instrprof_error::truncated;
>> +  unsigned N = 0;
>> +  auto ProfileNamesSize =
>> +      decodeULEB128(reinterpret_cast<const uint8_t *>(Data.data()), &N);
>> +  if (N > Data.size())
>> +    return instrprof_error::malformed;
>> +  Data = Data.substr(N);
>> +  if (Data.size() < 1)
>> +    return instrprof_error::truncated;
>> +  N = 0;
>> +  ProfileNames.Address =
>> +      decodeULEB128(reinterpret_cast<const uint8_t *>(Data.data()), &N);
>> +  if (N > Data.size())
>> +    return instrprof_error::malformed;
>> +  Data = Data.substr(N);
>> +  if (Data.size() < ProfileNamesSize)
>> +    return instrprof_error::malformed;
>> +  ProfileNames.Data = Data.substr(0, ProfileNamesSize);
>> +  CoverageMapping = Data.substr(ProfileNamesSize);
>> +  return instrprof_error::success;
>> +}
>> +
>> +ObjectFileCoverageMappingReader::ObjectFileCoverageMappingReader(
>> +    std::unique_ptr<MemoryBuffer> &ObjectBuffer, sys::fs::file_magic
>> Type)
>> +    : CurrentRecord(0) {
>> +  if (ObjectBuffer->getBuffer().startswith(TestingFormatMagic)) {
>> +    // This is a special format used for testing.
>> +    SectionData ProfileNames;
>> +    StringRef CoverageMapping;
>> +    if (auto Err = decodeTestingFormat(ObjectBuffer->getBuffer(),
>> ProfileNames,
>> +                                       CoverageMapping)) {
>> +      error(Err);
>> +      return;
>> +    }
>> +    error(readCoverageMappingData<uint64_t>(ProfileNames,
>> CoverageMapping,
>> +                                            MappingRecords, Filenames));
>> +    Object = OwningBinary<ObjectFile>(std::unique_ptr<ObjectFile>(),
>> +                                      std::move(ObjectBuffer));
>> +    return;
>> +  }
>> +
>> +  auto File = object::ObjectFile::createObjectFile(
>> +      ObjectBuffer->getMemBufferRef(), Type);
>> +  if (!File)
>> +    error(File.getError());
>> +  else
>> +    Object = OwningBinary<ObjectFile>(std::move(File.get()),
>> +                                      std::move(ObjectBuffer));
>> +}
>> +
>>  std::error_code ObjectFileCoverageMappingReader::readHeader() {
>>    ObjectFile *OF = Object.getBinary().get();
>>    if (!OF)
>> @@ -457,13 +494,21 @@ std::error_code ObjectFileCoverageMappin
>>    if (FoundSectionCount != 2)
>>      return error(instrprof_error::bad_header);
>>
>> +  // Get the contents of the given sections.
>> +  StringRef Data;
>> +  if (auto Err = CoverageMapping.getContents(Data))
>> +    return Err;
>> +  SectionData ProfileNamesData;
>> +  if (auto Err = ProfileNamesData.load(ProfileNames))
>> +    return Err;
>> +
>>    // Load the data from the found sections.
>>    std::error_code Err;
>>    if (BytesInAddress == 4)
>> -    Err = readCoverageMappingData<uint32_t>(ProfileNames,
>> CoverageMapping,
>> +    Err = readCoverageMappingData<uint32_t>(ProfileNamesData, Data,
>>                                              MappingRecords, Filenames);
>>    else
>> -    Err = readCoverageMappingData<uint64_t>(ProfileNames,
>> CoverageMapping,
>> +    Err = readCoverageMappingData<uint64_t>(ProfileNamesData, Data,
>>                                              MappingRecords, Filenames);
>>    if (Err)
>>      return error(Err);
>>
>> Modified: llvm/trunk/test/tools/llvm-cov/Inputs/README
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-cov/Inputs/README?rev=216300&r1=216299&r2=216300&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/test/tools/llvm-cov/Inputs/README (original)
>> +++ llvm/trunk/test/tools/llvm-cov/Inputs/README Fri Aug 22 17:56:03 2014
>> @@ -1,7 +1,21 @@
>>  These inputs were pre-generated to allow for easier testing of llvm-cov.
>>
>> -test.gcno and test.gcda were create by running clang:
>> -  clang++ -g -ftest-coverage -fprofile-arcs test.cpp
>> +The files used to test the gcov compatible code coverage tool were
>> generated
>> +using the following method:
>>
>> -test.cpp.gcov was created by running gcov 4.2.1:
>> -  gcov test.cpp
>> +  test.gcno and test.gcda were create by running clang:
>> +    clang++ -g -ftest-coverage -fprofile-arcs test.cpp
>> +
>> +  test.cpp.gcov was created by running gcov 4.2.1:
>> +    gcov test.cpp
>> +
>> +The 'covmapping' files that are used to test llvm-cov contain raw
>> sections
>> +with the coverage mapping data generated by the compiler and linker.
>> They are
>> +created by running clang and llvm-cov:
>> +  clang++ -fprofile-instr-generate -fcoverage-mapping -o test test.cpp
>> +  llvm-cov convert-for-testing -o test.covmapping test
>> +
>> +The 'profdata' files were generated by running an instrumented version
>> of the
>> +program and merging the raw profile data using llvm-profdata.
>> +  ./test
>> +  llvm-profdata merge -o test.profdata default.profraw
>>
>> Added: llvm/trunk/test/tools/llvm-cov/Inputs/highlightedRanges.covmapping
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-cov/Inputs/highlightedRanges.covmapping?rev=216300&view=auto
>>
>> ==============================================================================
>> Binary file - no diff available.
>>
>> Propchange:
>> llvm/trunk/test/tools/llvm-cov/Inputs/highlightedRanges.covmapping
>>
>> ------------------------------------------------------------------------------
>>     svn:mime-type = application/octet-stream
>>
>> Added: llvm/trunk/test/tools/llvm-cov/Inputs/highlightedRanges.profdata
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-cov/Inputs/highlightedRanges.profdata?rev=216300&view=auto
>>
>> ==============================================================================
>> Binary file - no diff available.
>>
>> Propchange:
>> llvm/trunk/test/tools/llvm-cov/Inputs/highlightedRanges.profdata
>>
>> ------------------------------------------------------------------------------
>>     svn:mime-type = application/octet-stream
>>
>> Added:
>> llvm/trunk/test/tools/llvm-cov/Inputs/lineExecutionCounts.covmapping
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-cov/Inputs/lineExecutionCounts.covmapping?rev=216300&view=auto
>>
>> ==============================================================================
>> Binary file - no diff available.
>>
>> Propchange:
>> llvm/trunk/test/tools/llvm-cov/Inputs/lineExecutionCounts.covmapping
>>
>> ------------------------------------------------------------------------------
>>     svn:mime-type = application/octet-stream
>>
>> Added: llvm/trunk/test/tools/llvm-cov/Inputs/lineExecutionCounts.profdata
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-cov/Inputs/lineExecutionCounts.profdata?rev=216300&view=auto
>>
>> ==============================================================================
>> Binary file - no diff available.
>>
>> Propchange:
>> llvm/trunk/test/tools/llvm-cov/Inputs/lineExecutionCounts.profdata
>>
>> ------------------------------------------------------------------------------
>>     svn:mime-type = application/octet-stream
>>
>> Added: llvm/trunk/test/tools/llvm-cov/Inputs/regionMarkers.covmapping
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-cov/Inputs/regionMarkers.covmapping?rev=216300&view=auto
>>
>> ==============================================================================
>> Binary file - no diff available.
>>
>> Propchange: llvm/trunk/test/tools/llvm-cov/Inputs/regionMarkers.covmapping
>>
>> ------------------------------------------------------------------------------
>>     svn:mime-type = application/octet-stream
>>
>> Added: llvm/trunk/test/tools/llvm-cov/Inputs/regionMarkers.profdata
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-cov/Inputs/regionMarkers.profdata?rev=216300&view=auto
>>
>> ==============================================================================
>> Binary file - no diff available.
>>
>> Propchange: llvm/trunk/test/tools/llvm-cov/Inputs/regionMarkers.profdata
>>
>> ------------------------------------------------------------------------------
>>     svn:mime-type = application/octet-stream
>>
>> Added: llvm/trunk/test/tools/llvm-cov/showHighlightedRanges.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-cov/showHighlightedRanges.cpp?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/test/tools/llvm-cov/showHighlightedRanges.cpp (added)
>> +++ llvm/trunk/test/tools/llvm-cov/showHighlightedRanges.cpp Fri Aug 22
>> 17:56:03 2014
>> @@ -0,0 +1,45 @@
>> +// RUN: llvm-cov show %S/Inputs/highlightedRanges.covmapping
>> -instr-profile %S/Inputs/highlightedRanges.profdata -dump
>> -filename-equivalence %s | FileCheck %s
>> +
>> +void func() {
>> +  return;
>> +  int i = 0;                     // CHECK: Highlighted line [[@LINE]], 3
>> -> 12
>> +}
>> +
>> +void func2(int x) {
>> +  if(x > 5) {
>> +    while(x >= 9) {
>> +      return;
>> +      --x;                       // CHECK: Highlighted line [[@LINE]], 7
>> -> 10
>> +    }
>> +    int i = 0;                   // CHECK: Highlighted line [[@LINE]], 5
>> -> 14
>> +  }
>> +}
>> +
>> +void test() {
>> +  int x = 0;
>> +
>> +  if (x) {                       // CHECK: Highlighted line [[@LINE]],
>> 10 -> ?
>> +    x = 0;                       // CHECK: Highlighted line [[@LINE]], 1
>> -> ?
>> +  } else {                       // CHECK: Highlighted line [[@LINE]], 1
>> -> 4
>> +    x = 1;
>> +  }
>> +
>> +                                  // CHECK: Highlighted line
>> [[@LINE+1]], 26 -> 29
>> +  for (int i = 0; i < 0; ++i) {   // CHECK: Highlighted line [[@LINE]],
>> 31 -> ?
>> +    x = 1;                        // CHECK: Highlighted line [[@LINE]],
>> 1 -> ?
>> +  }                               // CHECK: Highlighted line [[@LINE]],
>> 1 -> 4
>> +
>> +  x = x < 10 ? x +
>> +               1
>> +             : x - 1;             // CHECK: Highlighted line [[@LINE]],
>> 16 -> 21
>> +  x = x > 10 ? x +                // CHECK: Highlighted line [[@LINE]],
>> 16 -> ?
>> +               1                  // CHECK: Highlighted line [[@LINE]],
>> 1 -> 17
>> +             : x - 1;
>> +}
>> +
>> +int main() {
>> +  test();
>> +  func();
>> +  func2(9);
>> +  return 0;
>> +}
>>
>> Added: llvm/trunk/test/tools/llvm-cov/showLineExecutionCounts.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-cov/showLineExecutionCounts.cpp?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/test/tools/llvm-cov/showLineExecutionCounts.cpp (added)
>> +++ llvm/trunk/test/tools/llvm-cov/showLineExecutionCounts.cpp Fri Aug 22
>> 17:56:03 2014
>> @@ -0,0 +1,22 @@
>> +// RUN: llvm-cov show %S/Inputs/lineExecutionCounts.covmapping
>> -instr-profile %S/Inputs/lineExecutionCounts.profdata -no-colors
>> -filename-equivalence %s | FileCheck %s
>> +
>> +int main() {                             // CHECK:   1| [[@LINE]]|int
>> main(
>> +  int x = 0;                             // CHECK:   1| [[@LINE]]|  int x
>> +                                         // CHECK:   1| [[@LINE]]|
>> +  if (x) {                               // CHECK:   0| [[@LINE]]|  if
>> (x)
>> +    x = 0;                               // CHECK:   0| [[@LINE]]|    x
>> = 0
>> +  } else {                               // CHECK:   1| [[@LINE]]|  }
>> else
>> +    x = 1;                               // CHECK:   1| [[@LINE]]|    x
>> = 1
>> +  }                                      // CHECK:   1| [[@LINE]]|  }
>> +                                         // CHECK:   1| [[@LINE]]|
>> +  for (int i = 0; i < 100; ++i) {        // CHECK: 100| [[@LINE]]|  for (
>> +    x = 1;                               // CHECK: 100| [[@LINE]]|    x
>> = 1
>> +  }                                      // CHECK: 100| [[@LINE]]|  }
>> +                                         // CHECK:   1| [[@LINE]]|
>> +  x = x < 10 ? x + 1 : x - 1;            // CHECK:   0| [[@LINE]]|  x =
>> +  x = x > 10 ?                           // CHECK:   1| [[@LINE]]|  x =
>> +        x - 1:                           // CHECK:   0| [[@LINE]]|
>>   x
>> +        x + 1;                           // CHECK:   1| [[@LINE]]|
>>   x
>> +                                         // CHECK:   1| [[@LINE]]|
>> +  return 0;                              // CHECK:   1| [[@LINE]]|
>> return
>> +}                                        // CHECK:   1| [[@LINE]]|}
>>
>> Added: llvm/trunk/test/tools/llvm-cov/showRegionMarkers.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-cov/showRegionMarkers.cpp?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/test/tools/llvm-cov/showRegionMarkers.cpp (added)
>> +++ llvm/trunk/test/tools/llvm-cov/showRegionMarkers.cpp Fri Aug 22
>> 17:56:03 2014
>> @@ -0,0 +1,23 @@
>> +// RUN: llvm-cov show %S/Inputs/regionMarkers.covmapping -instr-profile
>> %S/Inputs/regionMarkers.profdata -show-regions -dump -filename-equivalence
>> %s | FileCheck %s
>> +
>> +int main() {                      // CHECK: Marker at [[@LINE]]:12 = 1
>> +  int x = 0;
>> +
>> +  if (x) {                        // CHECK: Marker at [[@LINE]]:10 = 0
>> +    x = 0;
>> +  } else {                        // CHECK: Marker at [[@LINE]]:10 = 1
>> +    x = 1;
>> +  }
>> +                                  // CHECK: Marker at [[@LINE+2]]:19 =
>> 101
>> +                                  // CHECK: Marker at [[@LINE+1]]:28 =
>> 100
>> +  for (int i = 0; i < 100; ++i) { // CHECK: Marker at [[@LINE]]:33 = 100
>> +    x = 1;
>> +  }
>> +                                  // CHECK: Marker at [[@LINE+1]]:16 = 1
>> +  x = x < 10 ? x + 1 : x - 1;     // CHECK: Marker at [[@LINE]]:24 = 0
>> +  x = x > 10 ?
>> +        x - 1:                    // CHECK: Marker at [[@LINE]]:9 = 0
>> +        x + 1;                    // CHECK: Marker at [[@LINE]]:9 = 1
>> +
>> +  return 0;
>> +}
>>
>> Modified: llvm/trunk/tools/llvm-cov/CMakeLists.txt
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/CMakeLists.txt?rev=216300&r1=216299&r2=216300&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/CMakeLists.txt (original)
>> +++ llvm/trunk/tools/llvm-cov/CMakeLists.txt Fri Aug 22 17:56:03 2014
>> @@ -1,6 +1,14 @@
>> -set(LLVM_LINK_COMPONENTS core support )
>> +set(LLVM_LINK_COMPONENTS core support object profiledata)
>>
>>  add_llvm_tool(llvm-cov
>>    llvm-cov.cpp
>>    gcov.cpp
>> +  CodeCoverage.cpp
>> +  CoverageFilters.cpp
>> +  CoverageReport.cpp
>> +  CoverageSummary.cpp
>> +  CoverageSummaryInfo.cpp
>> +  SourceCoverageDataManager.cpp
>> +  SourceCoverageView.cpp
>> +  TestingSupport.cpp
>>    )
>>
>> Added: llvm/trunk/tools/llvm-cov/CodeCoverage.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/CodeCoverage.cpp?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/CodeCoverage.cpp (added)
>> +++ llvm/trunk/tools/llvm-cov/CodeCoverage.cpp Fri Aug 22 17:56:03 2014
>> @@ -0,0 +1,709 @@
>> +//===- CodeCoverage.cpp - Coverage tool based on profiling
>> instrumentation-===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// The 'CodeCoverageTool' class implements a command line tool to
>> analyze and
>> +// report coverage information using the profiling instrumentation and
>> code
>> +// coverage mapping.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#include "FunctionCoverageMapping.h"
>> +#include "RenderingSupport.h"
>> +#include "CoverageViewOptions.h"
>> +#include "CoverageFilters.h"
>> +#include "SourceCoverageDataManager.h"
>> +#include "SourceCoverageView.h"
>> +#include "CoverageSummary.h"
>> +#include "CoverageReport.h"
>> +#include "llvm/ADT/StringRef.h"
>> +#include "llvm/ADT/SmallString.h"
>> +#include "llvm/ADT/SmallSet.h"
>> +#include "llvm/ADT/DenseSet.h"
>> +#include "llvm/ProfileData/InstrProfReader.h"
>> +#include "llvm/ProfileData/CoverageMapping.h"
>> +#include "llvm/ProfileData/CoverageMappingReader.h"
>> +#include "llvm/Support/CommandLine.h"
>> +#include "llvm/Support/FileSystem.h"
>> +#include "llvm/Support/ManagedStatic.h"
>> +#include "llvm/Support/MemoryObject.h"
>> +#include "llvm/Support/Format.h"
>> +#include "llvm/Support/Path.h"
>> +#include "llvm/Support/Signals.h"
>> +#include "llvm/Support/PrettyStackTrace.h"
>> +#include <system_error>
>> +#include <functional>
>> +
>> +using namespace llvm;
>> +using namespace coverage;
>> +
>> +namespace {
>> +/// \brief Distribute the functions into instantiation sets.
>> +/// An instantiation set is a collection of functions
>> +/// that have the same source code, e.g.
>> +/// template functions specializations.
>> +class FunctionInstantiationSetCollector {
>> +  ArrayRef<FunctionCoverageMapping> FunctionMappings;
>> +  typedef uint64_t KeyType;
>> +  typedef std::vector<const FunctionCoverageMapping *> SetType;
>> +  std::unordered_map<uint64_t, SetType> InstantiatedFunctions;
>> +
>> +  static KeyType getKey(const MappingRegion &R) {
>> +    return uint64_t(R.LineStart) | uint64_t(R.ColumnStart) << 32;
>> +  }
>> +
>> +public:
>> +  void insert(const FunctionCoverageMapping &Function, unsigned FileID) {
>> +    KeyType Key = 0;
>> +    for (const auto &R : Function.MappingRegions) {
>> +      if (R.FileID == FileID) {
>> +        Key = getKey(R);
>> +        break;
>> +      }
>> +    }
>> +    auto I = InstantiatedFunctions.find(Key);
>> +    if (I == InstantiatedFunctions.end()) {
>> +      SetType Set;
>> +      Set.push_back(&Function);
>> +      InstantiatedFunctions.insert(std::make_pair(Key, Set));
>> +    } else
>> +      I->second.push_back(&Function);
>> +  }
>> +
>> +  std::unordered_map<KeyType, SetType>::iterator begin() {
>> +    return InstantiatedFunctions.begin();
>> +  }
>> +
>> +  std::unordered_map<KeyType, SetType>::iterator end() {
>> +    return InstantiatedFunctions.end();
>> +  }
>> +};
>> +
>> +/// \brief The implementation of the coverage tool.
>> +class CodeCoverageTool {
>> +public:
>> +  enum Command {
>> +    /// \brief The show command.
>> +    Show,
>> +    /// \brief The report command.
>> +    Report
>> +  };
>> +
>> +  /// \brief Print the error message to the error output stream.
>> +  void error(const Twine &Message, StringRef Whence = "");
>> +
>> +  /// \brief Return a memory buffer for the given source file.
>> +  ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
>> +
>> +  /// \brief Return true if two filepaths refer to the same file.
>> +  bool equivalentFiles(StringRef A, StringRef B);
>> +
>> +  /// \brief Collect a set of function's file ids which correspond to the
>> +  /// given source file. Return false if the set is empty.
>> +  bool gatherInterestingFileIDs(StringRef SourceFile,
>> +                                const FunctionCoverageMapping &Function,
>> +                                SmallSet<unsigned, 8>
>> &InterestingFileIDs);
>> +
>> +  /// \brief Find the file id which is not an expanded file id.
>> +  bool findMainViewFileID(StringRef SourceFile,
>> +                          const FunctionCoverageMapping &Function,
>> +                          unsigned &MainViewFileID);
>> +
>> +  bool findMainViewFileID(const FunctionCoverageMapping &Function,
>> +                          unsigned &MainViewFileID);
>> +
>> +  /// \brief Create a source view which shows coverage for an expansion
>> +  /// of a file.
>> +  void createExpansionSubView(const MappingRegion &ExpandedRegion,
>> +                              const FunctionCoverageMapping &Function,
>> +                              SourceCoverageView &Parent);
>> +
>> +  void createExpansionSubViews(SourceCoverageView &View, unsigned
>> ViewFileID,
>> +                               const FunctionCoverageMapping &Function);
>> +
>> +  /// \brief Create a source view which shows coverage for an
>> instantiation
>> +  /// of a funciton.
>> +  void createInstantiationSubView(StringRef SourceFile,
>> +                                  const FunctionCoverageMapping
>> &Function,
>> +                                  SourceCoverageView &View);
>> +
>> +  /// \brief Create the main source view of a particular source file.
>> +  /// Return true if this particular source file is not covered.
>> +  bool
>> +  createSourceFileView(StringRef SourceFile, SourceCoverageView &View,
>> +                       ArrayRef<FunctionCoverageMapping>
>> FunctionMappingRecords,
>> +                       bool UseOnlyRegionsInMainFile = false);
>> +
>> +  /// \brief Load the coverage mapping data. Return true if an error
>> occured.
>> +  bool load();
>> +
>> +  int run(Command Cmd, int argc, const char **argv);
>> +
>> +  typedef std::function<int(int, const char **)> CommandLineParserType;
>> +
>> +  int show(int argc, const char **argv,
>> +           CommandLineParserType commandLineParser);
>> +
>> +  int report(int argc, const char **argv,
>> +             CommandLineParserType commandLineParser);
>> +
>> +  StringRef ObjectFilename;
>> +  CoverageViewOptions ViewOpts;
>> +  std::unique_ptr<IndexedInstrProfReader> PGOReader;
>> +  CoverageFiltersMatchAll Filters;
>> +  std::vector<std::string> SourceFiles;
>> +  std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
>> +      LoadedSourceFiles;
>> +  std::vector<FunctionCoverageMapping> FunctionMappingRecords;
>> +  bool CompareFilenamesOnly;
>> +};
>> +}
>> +
>> +void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
>> +  errs() << "error: ";
>> +  if (!Whence.empty())
>> +    errs() << Whence << ": ";
>> +  errs() << Message << "\n";
>> +}
>> +
>> +ErrorOr<const MemoryBuffer &>
>> +CodeCoverageTool::getSourceFile(StringRef SourceFile) {
>> +  SmallString<256> Path(SourceFile);
>> +  sys::fs::make_absolute(Path);
>> +  for (const auto &Files : LoadedSourceFiles) {
>> +    if (sys::fs::equivalent(Path.str(), Files.first)) {
>> +      return *Files.second;
>> +    }
>> +  }
>> +  auto Buffer = MemoryBuffer::getFile(SourceFile);
>> +  if (auto EC = Buffer.getError()) {
>> +    error(EC.message(), SourceFile);
>> +    return EC;
>> +  }
>> +  LoadedSourceFiles.push_back(std::make_pair(
>> +      std::string(Path.begin(), Path.end()), std::move(Buffer.get())));
>> +  return *LoadedSourceFiles.back().second;
>> +}
>> +
>> +/// \brief Return a line start - line end range which contains
>> +/// all the mapping regions of a given function with a particular file
>> id.
>> +std::pair<unsigned, unsigned>
>> +findExpandedFileInterestingLineRange(unsigned FileID,
>> +                                     const FunctionCoverageMapping
>> &Function) {
>> +  unsigned LineStart = std::numeric_limits<unsigned>::max();
>> +  unsigned LineEnd = 0;
>> +  for (const auto &Region : Function.MappingRegions) {
>> +    if (Region.FileID != FileID)
>> +      continue;
>> +    LineStart = std::min(Region.LineStart, LineStart);
>> +    LineEnd = std::max(Region.LineEnd, LineEnd);
>> +  }
>> +  return std::make_pair(LineStart, LineEnd);
>> +}
>> +
>> +bool CodeCoverageTool::equivalentFiles(StringRef A, StringRef B) {
>> +  if (CompareFilenamesOnly)
>> +    return sys::path::filename(A).equals_lower(sys::path::filename(B));
>> +  return sys::fs::equivalent(A, B);
>> +}
>> +
>> +bool CodeCoverageTool::gatherInterestingFileIDs(
>> +    StringRef SourceFile, const FunctionCoverageMapping &Function,
>> +    SmallSet<unsigned, 8> &InterestingFileIDs) {
>> +  bool Interesting = false;
>> +  for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
>> +    if (equivalentFiles(SourceFile, Function.Filenames[I])) {
>> +      InterestingFileIDs.insert(I);
>> +      Interesting = true;
>> +    }
>> +  }
>> +  return Interesting;
>> +}
>> +
>> +bool
>> +CodeCoverageTool::findMainViewFileID(StringRef SourceFile,
>> +                                     const FunctionCoverageMapping
>> &Function,
>> +                                     unsigned &MainViewFileID) {
>> +  llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(),
>> false);
>> +  llvm::SmallVector<bool, 8>
>> FilenameEquivalence(Function.Filenames.size(),
>> +                                                 false);
>> +  for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
>> +    if (equivalentFiles(SourceFile, Function.Filenames[I]))
>> +      FilenameEquivalence[I] = true;
>> +  }
>> +  for (const auto &Region : Function.MappingRegions) {
>> +    if (Region.Kind == MappingRegion::ExpansionRegion &&
>> +        FilenameEquivalence[Region.FileID])
>> +      IsExpandedFile[Region.ExpandedFileID] = true;
>> +  }
>> +  for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
>> +    if (!FilenameEquivalence[I] || IsExpandedFile[I])
>> +      continue;
>> +    MainViewFileID = I;
>> +    return false;
>> +  }
>> +  return true;
>> +}
>> +
>> +bool
>> +CodeCoverageTool::findMainViewFileID(const FunctionCoverageMapping
>> &Function,
>> +                                     unsigned &MainViewFileID) {
>> +  llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(),
>> false);
>> +  for (const auto &Region : Function.MappingRegions) {
>> +    if (Region.Kind == MappingRegion::ExpansionRegion)
>> +      IsExpandedFile[Region.ExpandedFileID] = true;
>> +  }
>> +  for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
>> +    if (IsExpandedFile[I])
>> +      continue;
>> +    MainViewFileID = I;
>> +    return false;
>> +  }
>> +  return true;
>> +}
>> +
>> +void CodeCoverageTool::createExpansionSubView(
>> +    const MappingRegion &ExpandedRegion,
>> +    const FunctionCoverageMapping &Function, SourceCoverageView &Parent)
>> {
>> +  auto ExpandedLines = findExpandedFileInterestingLineRange(
>> +      ExpandedRegion.ExpandedFileID, Function);
>> +  if (ViewOpts.Debug)
>> +    llvm::outs() << "Expansion of " << ExpandedRegion.ExpandedFileID <<
>> ":"
>> +                 << ExpandedLines.first << " -> " << ExpandedLines.second
>> +                 << " @ " << ExpandedRegion.FileID << ", "
>> +                 << ExpandedRegion.LineStart << ":"
>> +                 << ExpandedRegion.ColumnStart << "\n";
>> +  auto SourceBuffer =
>> +      getSourceFile(Function.Filenames[ExpandedRegion.ExpandedFileID]);
>> +  if (!SourceBuffer)
>> +    return;
>> +  auto SubView = llvm::make_unique<SourceCoverageView>(
>> +      SourceBuffer.get(), Parent.getOptions(), ExpandedLines.first,
>> +      ExpandedLines.second, ExpandedRegion);
>> +  SourceCoverageDataManager RegionManager;
>> +  for (const auto &Region : Function.MappingRegions) {
>> +    if (Region.FileID == ExpandedRegion.ExpandedFileID)
>> +      RegionManager.insert(Region);
>> +  }
>> +  SubView->load(RegionManager);
>> +  createExpansionSubViews(*SubView, ExpandedRegion.ExpandedFileID,
>> Function);
>> +  Parent.addChild(std::move(SubView));
>> +}
>> +
>> +void CodeCoverageTool::createExpansionSubViews(
>> +    SourceCoverageView &View, unsigned ViewFileID,
>> +    const FunctionCoverageMapping &Function) {
>> +  if (!ViewOpts.ShowExpandedRegions)
>> +    return;
>> +  for (const auto &Region : Function.MappingRegions) {
>> +    if (Region.Kind != CounterMappingRegion::ExpansionRegion)
>> +      continue;
>> +    if (Region.FileID != ViewFileID)
>> +      continue;
>> +    createExpansionSubView(Region, Function, View);
>> +  }
>> +}
>> +
>> +void CodeCoverageTool::createInstantiationSubView(
>> +    StringRef SourceFile, const FunctionCoverageMapping &Function,
>> +    SourceCoverageView &View) {
>> +  SourceCoverageDataManager RegionManager;
>> +  SmallSet<unsigned, 8> InterestingFileIDs;
>> +  if (!gatherInterestingFileIDs(SourceFile, Function,
>> InterestingFileIDs))
>> +    return;
>> +  // Get the interesting regions
>> +  for (const auto &Region : Function.MappingRegions) {
>> +    if (InterestingFileIDs.count(Region.FileID))
>> +      RegionManager.insert(Region);
>> +  }
>> +  View.load(RegionManager);
>> +  unsigned MainFileID;
>> +  if (findMainViewFileID(SourceFile, Function, MainFileID))
>> +    return;
>> +  createExpansionSubViews(View, MainFileID, Function);
>> +}
>> +
>> +bool CodeCoverageTool::createSourceFileView(
>> +    StringRef SourceFile, SourceCoverageView &View,
>> +    ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,
>> +    bool UseOnlyRegionsInMainFile) {
>> +  SourceCoverageDataManager RegionManager;
>> +  FunctionInstantiationSetCollector InstantiationSetCollector;
>> +
>> +  for (const auto &Function : FunctionMappingRecords) {
>> +    unsigned MainFileID;
>> +    if (findMainViewFileID(SourceFile, Function, MainFileID))
>> +      continue;
>> +    SmallSet<unsigned, 8> InterestingFileIDs;
>> +    if (UseOnlyRegionsInMainFile) {
>> +      InterestingFileIDs.insert(MainFileID);
>> +    } else if (!gatherInterestingFileIDs(SourceFile, Function,
>> +                                         InterestingFileIDs))
>> +      continue;
>> +    // Get the interesting regions
>> +    for (const auto &Region : Function.MappingRegions) {
>> +      if (InterestingFileIDs.count(Region.FileID))
>> +        RegionManager.insert(Region);
>> +    }
>> +    InstantiationSetCollector.insert(Function, MainFileID);
>> +    createExpansionSubViews(View, MainFileID, Function);
>> +  }
>> +  if (RegionManager.getSourceRegions().empty())
>> +    return true;
>> +  View.load(RegionManager);
>> +  // Show instantiations
>> +  if (!ViewOpts.ShowFunctionInstantiations)
>> +    return false;
>> +  for (const auto &InstantiationSet : InstantiationSetCollector) {
>> +    if (InstantiationSet.second.size() < 2)
>> +      continue;
>> +    auto InterestingRange = findExpandedFileInterestingLineRange(
>> +        InstantiationSet.second.front()->MappingRegions.front().FileID,
>> +        *InstantiationSet.second.front());
>> +    for (auto Function : InstantiationSet.second) {
>> +      auto SubView = llvm::make_unique<SourceCoverageView>(
>> +          View, InterestingRange.first, InterestingRange.second,
>> +          Function->PrettyName);
>> +      createInstantiationSubView(SourceFile, *Function, *SubView);
>> +      View.addChild(std::move(SubView));
>> +    }
>> +  }
>> +  return false;
>> +}
>> +
>> +bool CodeCoverageTool::load() {
>> +  auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename);
>> +  if (auto EC = CounterMappingBuff.getError()) {
>> +    error(EC.message(), ObjectFilename);
>> +    return true;
>> +  }
>> +  ObjectFileCoverageMappingReader
>> MappingReader(CounterMappingBuff.get());
>> +  if (auto EC = MappingReader.readHeader()) {
>> +    error(EC.message(), ObjectFilename);
>> +    return true;
>> +  }
>> +
>> +  std::vector<uint64_t> Counts;
>> +  for (const auto &I : MappingReader) {
>> +    FunctionCoverageMapping Function(I.FunctionName, I.Filenames);
>> +
>> +    // Create the mapping regions with evaluated execution counts
>> +    Counts.clear();
>> +    PGOReader->getFunctionCounts(Function.Name, I.FunctionHash, Counts);
>> +
>> +    // Get the biggest referenced counters
>> +    bool RegionError = false;
>> +    CounterMappingContext Ctx(I.Expressions, Counts);
>> +    for (const auto &R : I.MappingRegions) {
>> +      // Compute the values of mapped regions
>> +      if (ViewOpts.Debug) {
>> +        outs() << "File " << R.FileID << "| " << R.LineStart << ":"
>> +               << R.ColumnStart << " -> " << R.LineEnd << ":" <<
>> R.ColumnEnd
>> +               << " = ";
>> +        Ctx.dump(R.Count);
>> +        if (R.Kind == CounterMappingRegion::ExpansionRegion) {
>> +          outs() << " (Expanded file id = " << R.ExpandedFileID << ") ";
>> +        }
>> +        outs() << "\n";
>> +      }
>> +      std::error_code Error;
>> +      Function.MappingRegions.push_back(
>> +          MappingRegion(R, Ctx.evaluate(R.Count, Error)));
>> +      if (Error && !RegionError) {
>> +        colored_ostream(errs(), raw_ostream::RED)
>> +            << "error: Regions and counters don't match in a function '"
>> +            << Function.PrettyName << "' (re-run the instrumented
>> binary).";
>> +        errs() << "\n";
>> +        RegionError = true;
>> +      }
>> +    }
>> +
>> +    if (RegionError || !Filters.matches(Function))
>> +      continue;
>> +
>> +    FunctionMappingRecords.push_back(Function);
>> +  }
>> +  return false;
>> +}
>> +
>> +int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
>> +  // Print a stack trace if we signal out.
>> +  sys::PrintStackTraceOnErrorSignal();
>> +  PrettyStackTraceProgram X(argc, argv);
>> +  llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
>> +
>> +  cl::list<std::string> InputSourceFiles(
>> +      cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);
>> +
>> +  cl::opt<std::string> PGOFilename(
>> +      "instr-profile", cl::Required,
>> +      cl::desc(
>> +          "File with the profile data obtained after an instrumented
>> run"));
>> +
>> +  cl::opt<bool> DebugDump("dump", cl::Optional,
>> +                          cl::desc("Show internal debug dump"));
>> +
>> +  cl::opt<bool> FilenameEquivalence(
>> +      "filename-equivalence", cl::Optional,
>> +      cl::desc("Compare the filenames instead of full filepaths"));
>> +
>> +  cl::OptionCategory FilteringCategory("Function filtering options");
>> +
>> +  cl::list<std::string> NameFilters(
>> +      "name", cl::Optional,
>> +      cl::desc("Show code coverage only for functions with the given
>> name"),
>> +      cl::ZeroOrMore, cl::cat(FilteringCategory));
>> +
>> +  cl::list<std::string> NameRegexFilters(
>> +      "name-regex", cl::Optional,
>> +      cl::desc("Show code coverage only for functions that match the
>> given "
>> +               "regular expression"),
>> +      cl::ZeroOrMore, cl::cat(FilteringCategory));
>> +
>> +  cl::opt<double> RegionCoverageLtFilter(
>> +      "region-coverage-lt", cl::Optional,
>> +      cl::desc("Show code coverage only for functions with region
>> coverage "
>> +               "less than the given threshold"),
>> +      cl::cat(FilteringCategory));
>> +
>> +  cl::opt<double> RegionCoverageGtFilter(
>> +      "region-coverage-gt", cl::Optional,
>> +      cl::desc("Show code coverage only for functions with region
>> coverage "
>> +               "greater than the given threshold"),
>> +      cl::cat(FilteringCategory));
>> +
>> +  cl::opt<double> LineCoverageLtFilter(
>> +      "line-coverage-lt", cl::Optional,
>> +      cl::desc("Show code coverage only for functions with line coverage
>> less "
>> +               "than the given threshold"),
>> +      cl::cat(FilteringCategory));
>> +
>> +  cl::opt<double> LineCoverageGtFilter(
>> +      "line-coverage-gt", cl::Optional,
>> +      cl::desc("Show code coverage only for functions with line coverage
>> "
>> +               "greater than the given threshold"),
>> +      cl::cat(FilteringCategory));
>> +
>> +  auto commandLineParser = [&, this](int argc, const char **argv) -> int
>> {
>> +    cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
>> +    ViewOpts.Debug = DebugDump;
>> +    CompareFilenamesOnly = FilenameEquivalence;
>> +
>> +    if (auto EC = IndexedInstrProfReader::create(PGOFilename,
>> PGOReader)) {
>> +      error(EC.message(), PGOFilename);
>> +      return 1;
>> +    }
>> +
>> +    // Create the function filters
>> +    if (!NameFilters.empty() || !NameRegexFilters.empty()) {
>> +      auto NameFilterer = new CoverageFilters;
>> +      for (const auto &Name : NameFilters)
>> +
>> NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name));
>> +      for (const auto &Regex : NameRegexFilters)
>> +        NameFilterer->push_back(
>> +            llvm::make_unique<NameRegexCoverageFilter>(Regex));
>> +      Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer));
>> +    }
>> +    if (RegionCoverageLtFilter.getNumOccurrences() ||
>> +        RegionCoverageGtFilter.getNumOccurrences() ||
>> +        LineCoverageLtFilter.getNumOccurrences() ||
>> +        LineCoverageGtFilter.getNumOccurrences()) {
>> +      auto StatFilterer = new CoverageFilters;
>> +      if (RegionCoverageLtFilter.getNumOccurrences())
>> +        StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
>> +            RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
>> +      if (RegionCoverageGtFilter.getNumOccurrences())
>> +        StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
>> +            RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
>> +      if (LineCoverageLtFilter.getNumOccurrences())
>> +        StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
>> +            LineCoverageFilter::LessThan, LineCoverageLtFilter));
>> +      if (LineCoverageGtFilter.getNumOccurrences())
>> +        StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
>> +            RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
>> +      Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer));
>> +    }
>> +
>> +    SourceFiles = InputSourceFiles;
>> +    return 0;
>> +  };
>> +
>> +  // Parse the object filename
>> +  if (argc > 1) {
>> +    StringRef Arg(argv[1]);
>> +    if (Arg.equals_lower("-help") || Arg.equals_lower("-version")) {
>> +      cl::ParseCommandLineOptions(2, argv, "LLVM code coverage tool\n");
>> +      return 0;
>> +    }
>> +    ObjectFilename = Arg;
>> +
>> +    argv[1] = argv[0];
>> +    --argc;
>> +    ++argv;
>> +  } else {
>> +    errs() << sys::path::filename(argv[0]) << ": No executable file
>> given!\n";
>> +    return 1;
>> +  }
>> +
>> +  switch (Cmd) {
>> +  case Show:
>> +    return show(argc, argv, commandLineParser);
>> +  case Report:
>> +    return report(argc, argv, commandLineParser);
>> +  }
>> +  return 0;
>> +}
>> +
>> +int CodeCoverageTool::show(int argc, const char **argv,
>> +                           CommandLineParserType commandLineParser) {
>> +
>> +  cl::OptionCategory ViewCategory("Viewing options");
>> +
>> +  cl::opt<bool> ShowLineExecutionCounts(
>> +      "show-line-counts", cl::Optional,
>> +      cl::desc("Show the execution counts for each line"),
>> cl::init(true),
>> +      cl::cat(ViewCategory));
>> +
>> +  cl::opt<bool> ShowRegions(
>> +      "show-regions", cl::Optional,
>> +      cl::desc("Show the execution counts for each region"),
>> +      cl::cat(ViewCategory));
>> +
>> +  cl::opt<bool> ShowBestLineRegionsCounts(
>> +      "show-line-counts-or-regions", cl::Optional,
>> +      cl::desc("Show the execution counts for each line, or the
>> execution "
>> +               "counts for each region on lines that have multiple
>> regions"),
>> +      cl::cat(ViewCategory));
>> +
>> +  cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,
>> +                               cl::desc("Show expanded source regions"),
>> +                               cl::cat(ViewCategory));
>> +
>> +  cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
>> +                                   cl::desc("Show function
>> instantiations"),
>> +                                   cl::cat(ViewCategory));
>> +
>> +  cl::opt<bool> NoColors("no-colors", cl::Optional,
>> +                         cl::desc("Don't show text colors"),
>> cl::init(false),
>> +                         cl::cat(ViewCategory));
>> +
>> +  auto Err = commandLineParser(argc, argv);
>> +  if (Err)
>> +    return Err;
>> +
>> +  ViewOpts.Colors = !NoColors;
>> +  ViewOpts.ShowLineNumbers = true;
>> +  ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences()
>> != 0 ||
>> +                           !ShowRegions || ShowBestLineRegionsCounts;
>> +  ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
>> +  ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts;
>> +  ViewOpts.ShowExpandedRegions = ShowExpansions;
>> +  ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
>> +
>> +  if (load())
>> +    return 1;
>> +
>> +  if (!Filters.empty()) {
>> +    // Show functions
>> +    for (const auto &Function : FunctionMappingRecords) {
>> +      unsigned MainFileID;
>> +      if (findMainViewFileID(Function, MainFileID))
>> +        continue;
>> +      StringRef SourceFile = Function.Filenames[MainFileID];
>> +      std::unique_ptr<SourceCoverageView> mainView;
>> +      auto SourceBuffer = getSourceFile(SourceFile);
>> +      if (!SourceBuffer)
>> +        return 1;
>> +      auto Range = findExpandedFileInterestingLineRange(MainFileID,
>> Function);
>> +      mainView.reset(new SourceCoverageView(SourceBuffer.get(), ViewOpts,
>> +                                            Range.first, Range.second));
>> +      createSourceFileView(SourceFile, *mainView, Function, true);
>> +      ViewOpts.colored_ostream(outs(), raw_ostream::CYAN)
>> +          << Function.PrettyName << " from " << SourceFile << ":";
>> +      outs() << "\n";
>> +      mainView->render(outs());
>> +      if (FunctionMappingRecords.size() > 1)
>> +        outs() << "\n";
>> +    }
>> +    return 0;
>> +  }
>> +
>> +  // Show files
>> +  bool ShowFilenames = SourceFiles.size() != 1;
>> +
>> +  if (SourceFiles.empty()) {
>> +    // Get the source files from the function coverage mapping
>> +    std::set<StringRef> UniqueFilenames;
>> +    for (const auto &Function : FunctionMappingRecords) {
>> +      for (const auto &Filename : Function.Filenames)
>> +        UniqueFilenames.insert(Filename);
>> +    }
>> +    for (const auto &Filename : UniqueFilenames)
>> +      SourceFiles.push_back(Filename);
>> +  }
>> +
>> +  for (const auto &SourceFile : SourceFiles) {
>> +    std::unique_ptr<SourceCoverageView> mainView;
>> +    auto SourceBuffer = getSourceFile(SourceFile);
>> +    if (!SourceBuffer)
>> +      return 1;
>> +    mainView.reset(new SourceCoverageView(SourceBuffer.get(), ViewOpts));
>> +    if (createSourceFileView(SourceFile, *mainView,
>> FunctionMappingRecords)) {
>> +      ViewOpts.colored_ostream(outs(), raw_ostream::RED)
>> +          << "warning: The file '" << SourceFile << "' isn't covered.";
>> +      outs() << "\n";
>> +      continue;
>> +    }
>> +
>> +    if (ShowFilenames) {
>> +      ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile
>> << ":";
>> +      outs() << "\n";
>> +    }
>> +    mainView->render(outs());
>> +    if (SourceFiles.size() > 1)
>> +      outs() << "\n";
>> +  }
>> +
>> +  return 0;
>> +}
>> +
>> +int CodeCoverageTool::report(int argc, const char **argv,
>> +                             CommandLineParserType commandLineParser) {
>> +  cl::opt<bool> NoColors("no-colors", cl::Optional,
>> +                         cl::desc("Don't show text colors"),
>> cl::init(false));
>> +
>> +  auto Err = commandLineParser(argc, argv);
>> +  if (Err)
>> +    return Err;
>> +
>> +  ViewOpts.Colors = !NoColors;
>> +
>> +  if (load())
>> +    return 1;
>> +
>> +  CoverageSummary Summarizer;
>> +  Summarizer.createSummaries(FunctionMappingRecords);
>> +  CoverageReport Report(ViewOpts, Summarizer);
>> +  if (SourceFiles.empty() && Filters.empty()) {
>> +    Report.renderFileReports(llvm::outs());
>> +    return 0;
>> +  }
>> +
>> +  Report.renderFunctionReports(llvm::outs());
>> +  return 0;
>> +}
>> +
>> +int show_main(int argc, const char **argv) {
>> +  CodeCoverageTool Tool;
>> +  return Tool.run(CodeCoverageTool::Show, argc, argv);
>> +}
>> +
>> +int report_main(int argc, const char **argv) {
>> +  CodeCoverageTool Tool;
>> +  return Tool.run(CodeCoverageTool::Report, argc, argv);
>> +}
>>
>> Added: llvm/trunk/tools/llvm-cov/CoverageFilters.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/CoverageFilters.cpp?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/CoverageFilters.cpp (added)
>> +++ llvm/trunk/tools/llvm-cov/CoverageFilters.cpp Fri Aug 22 17:56:03 2014
>> @@ -0,0 +1,57 @@
>> +//===- CoverageFilters.cpp - Function coverage mapping filters
>> ------------===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// These classes provide filtering for function coverage mapping records.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#include "CoverageFilters.h"
>> +#include "CoverageSummaryInfo.h"
>> +#include "llvm/Support/Regex.h"
>> +
>> +using namespace llvm;
>> +
>> +bool NameCoverageFilter::matches(const FunctionCoverageMapping
>> &Function) {
>> +  StringRef FuncName = Function.PrettyName;
>> +  return FuncName.find(Name) != StringRef::npos;
>> +}
>> +
>> +bool NameRegexCoverageFilter::matches(const FunctionCoverageMapping
>> &Function) {
>> +  return llvm::Regex(Regex).match(Function.PrettyName);
>> +}
>> +
>> +bool RegionCoverageFilter::matches(const FunctionCoverageMapping
>> &Function) {
>> +  return PassesThreshold(FunctionCoverageSummary::get(Function)
>> +                             .RegionCoverage.getPercentCovered());
>> +}
>> +
>> +bool LineCoverageFilter::matches(const FunctionCoverageMapping
>> &Function) {
>> +  return PassesThreshold(
>> +
>> FunctionCoverageSummary::get(Function).LineCoverage.getPercentCovered());
>> +}
>> +
>> +void CoverageFilters::push_back(std::unique_ptr<CoverageFilter> Filter) {
>> +  Filters.push_back(std::move(Filter));
>> +}
>> +
>> +bool CoverageFilters::matches(const FunctionCoverageMapping &Function) {
>> +  for (const auto &Filter : Filters) {
>> +    if (Filter->matches(Function))
>> +      return true;
>> +  }
>> +  return false;
>> +}
>> +
>> +bool CoverageFiltersMatchAll::matches(const FunctionCoverageMapping
>> &Function) {
>> +  for (const auto &Filter : Filters) {
>> +    if (!Filter->matches(Function))
>> +      return false;
>> +  }
>> +  return true;
>> +}
>>
>> Added: llvm/trunk/tools/llvm-cov/CoverageFilters.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/CoverageFilters.h?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/CoverageFilters.h (added)
>> +++ llvm/trunk/tools/llvm-cov/CoverageFilters.h Fri Aug 22 17:56:03 2014
>> @@ -0,0 +1,125 @@
>> +//===- CoverageFilters.h - Function coverage mapping filters
>> --------------===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// These classes provide filtering for function coverage mapping records.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#ifndef LLVM_COV_COVERAGEFILTERS_H
>> +#define LLVM_COV_COVERAGEFILTERS_H
>> +
>> +#include "FunctionCoverageMapping.h"
>> +#include <vector>
>> +#include <memory>
>> +
>> +namespace llvm {
>> +
>> +/// \brief Matches specific functions that pass the requirement of this
>> filter.
>> +class CoverageFilter {
>> +public:
>> +  virtual ~CoverageFilter() {}
>> +
>> +  /// \brief Return true if the function passes the requirements of this
>> filter.
>> +  virtual bool matches(const FunctionCoverageMapping &Function) { return
>> true; }
>> +};
>> +
>> +/// \brief Matches functions that contain a specific string in their
>> name.
>> +class NameCoverageFilter : public CoverageFilter {
>> +  StringRef Name;
>> +
>> +public:
>> +  NameCoverageFilter(StringRef Name) : Name(Name) {}
>> +
>> +  bool matches(const FunctionCoverageMapping &Function) override;
>> +};
>> +
>> +/// \brief Matches functions whose name matches a certain regular
>> expression.
>> +class NameRegexCoverageFilter : public CoverageFilter {
>> +  StringRef Regex;
>> +
>> +public:
>> +  NameRegexCoverageFilter(StringRef Regex) : Regex(Regex) {}
>> +
>> +  bool matches(const FunctionCoverageMapping &Function) override;
>> +};
>> +
>> +/// \brief Matches numbers that pass a certain threshold.
>> +template <typename T> class StatisticThresholdFilter {
>> +public:
>> +  enum Operation { LessThan, GreaterThan };
>> +
>> +protected:
>> +  Operation Op;
>> +  T Threshold;
>> +
>> +  StatisticThresholdFilter(Operation Op, T Threshold)
>> +      : Op(Op), Threshold(Threshold) {}
>> +
>> +  /// \brief Return true if the given number is less than
>> +  /// or greater than the certain threshold.
>> +  bool PassesThreshold(T Value) const {
>> +    switch (Op) {
>> +    case LessThan:
>> +      return Value < Threshold;
>> +    case GreaterThan:
>> +      return Value > Threshold;
>> +    }
>> +    return false;
>> +  }
>> +};
>> +
>> +/// \brief Matches functions whose region coverage percentage
>> +/// is above/below a certain percentage.
>> +class RegionCoverageFilter : public CoverageFilter,
>> +                             public StatisticThresholdFilter<double> {
>> +public:
>> +  RegionCoverageFilter(Operation Op, double Threshold)
>> +      : StatisticThresholdFilter(Op, Threshold) {}
>> +
>> +  bool matches(const FunctionCoverageMapping &Function) override;
>> +};
>> +
>> +/// \brief Matches functions whose line coverage percentage
>> +/// is above/below a certain percentage.
>> +class LineCoverageFilter : public CoverageFilter,
>> +                           public StatisticThresholdFilter<double> {
>> +public:
>> +  LineCoverageFilter(Operation Op, double Threshold)
>> +      : StatisticThresholdFilter(Op, Threshold) {}
>> +
>> +  bool matches(const FunctionCoverageMapping &Function) override;
>> +};
>> +
>> +/// \brief A collection of filters.
>> +/// Matches functions that match any filters contained
>> +/// in an instance of this class.
>> +class CoverageFilters : public CoverageFilter {
>> +protected:
>> +  std::vector<std::unique_ptr<CoverageFilter>> Filters;
>> +
>> +public:
>> +  /// \brief Append a filter to this collection.
>> +  void push_back(std::unique_ptr<CoverageFilter> Filter);
>> +
>> +  bool empty() const { return Filters.empty(); }
>> +
>> +  bool matches(const FunctionCoverageMapping &Function) override;
>> +};
>> +
>> +/// \brief A collection of filters.
>> +/// Matches functions that match all of the filters contained
>> +/// in an instance of this class.
>> +class CoverageFiltersMatchAll : public CoverageFilters {
>> +public:
>> +  bool matches(const FunctionCoverageMapping &Function) override;
>> +};
>> +
>> +} // namespace llvm
>> +
>> +#endif // LLVM_COV_COVERAGEFILTERS_H
>>
>> Added: llvm/trunk/tools/llvm-cov/CoverageReport.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/CoverageReport.cpp?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/CoverageReport.cpp (added)
>> +++ llvm/trunk/tools/llvm-cov/CoverageReport.cpp Fri Aug 22 17:56:03 2014
>> @@ -0,0 +1,201 @@
>> +//===- CoverageReport.cpp - Code coverage report
>> -------------------------===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// This class implements rendering of a code coverage report.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#include "CoverageReport.h"
>> +#include "CoverageSummary.h"
>> +#include "RenderingSupport.h"
>> +#include "llvm/Support/Format.h"
>> +#include "llvm/Support/FileSystem.h"
>> +
>> +using namespace llvm;
>> +namespace {
>> +/// \brief Helper struct which prints trimmed and aligned columns.
>> +struct Column {
>> +  enum TrimKind { NoTrim, LeftTrim, RightTrim };
>> +
>> +  enum AlignmentKind { LeftAlignment, RightAlignment };
>> +
>> +  StringRef Str;
>> +  unsigned Width;
>> +  TrimKind Trim;
>> +  AlignmentKind Alignment;
>> +
>> +  Column(StringRef Str, unsigned Width)
>> +      : Str(Str), Width(Width), Trim(NoTrim), Alignment(LeftAlignment) {}
>> +
>> +  Column &set(TrimKind Value) {
>> +    Trim = Value;
>> +    return *this;
>> +  }
>> +
>> +  Column &set(AlignmentKind Value) {
>> +    Alignment = Value;
>> +    return *this;
>> +  }
>> +
>> +  void render(raw_ostream &OS) const;
>> +};
>> +raw_ostream &operator<<(raw_ostream &OS, const Column &Value) {
>> +  Value.render(OS);
>> +  return OS;
>> +}
>> +}
>> +
>> +void Column::render(raw_ostream &OS) const {
>> +  if (Str.size() <= Width) {
>> +    if (Alignment == RightAlignment) {
>> +      OS.indent(Width - Str.size());
>> +      OS << Str;
>> +      return;
>> +    }
>> +    OS << Str;
>> +    OS.indent(Width - Str.size());
>> +    return;
>> +  }
>> +
>> +  switch (Trim) {
>> +  case NoTrim:
>> +    OS << Str.substr(0, Width);
>> +    break;
>> +  case LeftTrim:
>> +    OS << "..." << Str.substr(Str.size() - Width + 3);
>> +    break;
>> +  case RightTrim:
>> +    OS << Str.substr(0, Width - 3) << "...";
>> +    break;
>> +  }
>> +}
>> +
>> +static Column column(StringRef Str, unsigned Width) {
>> +  return Column(Str, Width);
>> +}
>> +
>> +template <typename T>
>> +static Column column(StringRef Str, unsigned Width, const T &Value) {
>> +  return Column(Str, Width).set(Value);
>> +}
>> +
>> +static const unsigned FileReportColumns[] = {25, 10, 8, 8, 10, 8};
>> +static const unsigned FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8};
>> +
>> +/// \brief Prints a horizontal divider which spans across the given
>> columns.
>> +template <typename T, size_t N>
>> +static void renderDivider(T (&Columns)[N], raw_ostream &OS) {
>> +  unsigned Length = 0;
>> +  for (unsigned I = 0; I < N; ++I)
>> +    Length += Columns[I];
>> +  for (unsigned I = 0; I < Length; ++I)
>> +    OS << '-';
>> +}
>> +
>> +/// \brief Return the color which correponds to the coverage
>> +/// percentage of a certain metric.
>> +template <typename T>
>> +static raw_ostream::Colors determineCoveragePercentageColor(const T
>> &Info) {
>> +  if (Info.isFullyCovered())
>> +    return raw_ostream::GREEN;
>> +  return Info.getPercentCovered() >= 80.0 ? raw_ostream::YELLOW
>> +                                          : raw_ostream::RED;
>> +}
>> +
>> +void CoverageReport::render(const FileCoverageSummary &File, raw_ostream
>> &OS) {
>> +  OS << column(File.Name, FileReportColumns[0], Column::LeftTrim)
>> +     << format("%*zd", FileReportColumns[1],
>> File.RegionCoverage.NumRegions);
>> +  Options.colored_ostream(OS, File.RegionCoverage.isFullyCovered()
>> +                                  ? raw_ostream::GREEN
>> +                                  : raw_ostream::RED)
>> +      << format("%*zd", FileReportColumns[2],
>> File.RegionCoverage.NotCovered);
>> +  Options.colored_ostream(OS,
>> +
>> determineCoveragePercentageColor(File.RegionCoverage))
>> +      << format("%*.2f", FileReportColumns[3] - 1,
>> +                File.RegionCoverage.getPercentCovered()) << '%';
>> +  OS << format("%*zd", FileReportColumns[4],
>> +               File.FunctionCoverage.NumFunctions);
>> +  Options.colored_ostream(
>> +      OS, determineCoveragePercentageColor(File.FunctionCoverage))
>> +      << format("%*.2f", FileReportColumns[5] - 1,
>> +                File.FunctionCoverage.getPercentCovered()) << '%';
>> +  OS << "\n";
>> +}
>> +
>> +void CoverageReport::render(const FunctionCoverageSummary &Function,
>> +                            raw_ostream &OS) {
>> +  OS << column(Function.Name, FunctionReportColumns[0],
>> Column::RightTrim)
>> +     << format("%*zd", FunctionReportColumns[1],
>> +               Function.RegionCoverage.NumRegions);
>> +  Options.colored_ostream(OS, Function.RegionCoverage.isFullyCovered()
>> +                                  ? raw_ostream::GREEN
>> +                                  : raw_ostream::RED)
>> +      << format("%*zd", FunctionReportColumns[2],
>> +                Function.RegionCoverage.NotCovered);
>> +  Options.colored_ostream(
>> +      OS, determineCoveragePercentageColor(Function.RegionCoverage))
>> +      << format("%*.2f", FunctionReportColumns[3] - 1,
>> +                Function.RegionCoverage.getPercentCovered()) << '%';
>> +  OS << format("%*zd", FunctionReportColumns[4],
>> +               Function.LineCoverage.NumLines);
>> +  Options.colored_ostream(OS, Function.LineCoverage.isFullyCovered()
>> +                                  ? raw_ostream::GREEN
>> +                                  : raw_ostream::RED)
>> +      << format("%*zd", FunctionReportColumns[5],
>> +                Function.LineCoverage.NotCovered);
>> +  Options.colored_ostream(
>> +      OS, determineCoveragePercentageColor(Function.LineCoverage))
>> +      << format("%*.2f", FunctionReportColumns[6] - 1,
>> +                Function.LineCoverage.getPercentCovered()) << '%';
>> +  OS << "\n";
>> +}
>> +
>> +void CoverageReport::renderFunctionReports(raw_ostream &OS) {
>> +  bool isFirst = true;
>> +  for (const auto &File : Summary.getFileSummaries()) {
>> +    if (isFirst)
>> +      isFirst = false;
>> +    else
>> +      OS << "\n";
>> +    OS << "File '" << File.Name << "':\n";
>> +    OS << column("Name", FunctionReportColumns[0])
>> +       << column("Regions", FunctionReportColumns[1],
>> Column::RightAlignment)
>> +       << column("Miss", FunctionReportColumns[2],
>> Column::RightAlignment)
>> +       << column("Cover", FunctionReportColumns[3],
>> Column::RightAlignment)
>> +       << column("Lines", FunctionReportColumns[4],
>> Column::RightAlignment)
>> +       << column("Miss", FunctionReportColumns[5],
>> Column::RightAlignment)
>> +       << column("Cover", FunctionReportColumns[6],
>> Column::RightAlignment);
>> +    OS << "\n";
>> +    renderDivider(FunctionReportColumns, OS);
>> +    OS << "\n";
>> +    for (const auto &Function : File.FunctionSummaries)
>> +      render(Function, OS);
>> +    renderDivider(FunctionReportColumns, OS);
>> +    OS << "\n";
>> +    render(FunctionCoverageSummary("TOTAL", File.RegionCoverage,
>> +                                   File.LineCoverage),
>> +           OS);
>> +  }
>> +}
>> +
>> +void CoverageReport::renderFileReports(raw_ostream &OS) {
>> +  OS << column("Filename", FileReportColumns[0])
>> +     << column("Regions", FileReportColumns[1], Column::RightAlignment)
>> +     << column("Miss", FileReportColumns[2], Column::RightAlignment)
>> +     << column("Cover", FileReportColumns[3], Column::RightAlignment)
>> +     << column("Functions", FileReportColumns[4], Column::RightAlignment)
>> +     << column("Cover", FileReportColumns[5], Column::RightAlignment) <<
>> "\n";
>> +  renderDivider(FileReportColumns, OS);
>> +  OS << "\n";
>> +  for (const auto &File : Summary.getFileSummaries())
>> +    render(File, OS);
>> +  renderDivider(FileReportColumns, OS);
>> +  OS << "\n";
>> +  render(Summary.getCombinedFileSummaries(), OS);
>> +}
>>
>> Added: llvm/trunk/tools/llvm-cov/CoverageReport.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/CoverageReport.h?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/CoverageReport.h (added)
>> +++ llvm/trunk/tools/llvm-cov/CoverageReport.h Fri Aug 22 17:56:03 2014
>> @@ -0,0 +1,40 @@
>> +//===- CoverageReport.h - Code coverage report
>> ---------------------------===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// This class implements rendering of a code coverage report.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#ifndef LLVM_COV_COVERAGEREPORT_H
>> +#define LLVM_COV_COVERAGEREPORT_H
>> +
>> +#include "CoverageViewOptions.h"
>> +#include "CoverageSummary.h"
>> +
>> +namespace llvm {
>> +
>> +/// \brief Displays the code coverage report.
>> +class CoverageReport {
>> +  const CoverageViewOptions &Options;
>> +  CoverageSummary &Summary;
>> +
>> +  void render(const FileCoverageSummary &File, raw_ostream &OS);
>> +  void render(const FunctionCoverageSummary &Function, raw_ostream &OS);
>> +
>> +public:
>> +  CoverageReport(const CoverageViewOptions &Options, CoverageSummary
>> &Summary)
>> +      : Options(Options), Summary(Summary) {}
>> +
>> +  void renderFunctionReports(raw_ostream &OS);
>> +
>> +  void renderFileReports(raw_ostream &OS);
>> +};
>> +}
>> +
>> +#endif // LLVM_COV_COVERAGEREPORT_H
>>
>> Added: llvm/trunk/tools/llvm-cov/CoverageSummary.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/CoverageSummary.cpp?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/CoverageSummary.cpp (added)
>> +++ llvm/trunk/tools/llvm-cov/CoverageSummary.cpp Fri Aug 22 17:56:03 2014
>> @@ -0,0 +1,92 @@
>> +//===- CoverageSummary.cpp - Code coverage summary
>> ------------------------===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// This class implements data management and rendering for the code
>> coverage
>> +// summaries of all files and functions.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#include "CoverageSummary.h"
>> +#include "llvm/Support/FileSystem.h"
>> +#include "llvm/Support/Format.h"
>> +
>> +using namespace llvm;
>> +
>> +unsigned CoverageSummary::getFileID(StringRef Filename) {
>> +  for (unsigned I = 0, E = Filenames.size(); I < E; ++I) {
>> +    if (sys::fs::equivalent(Filenames[I], Filename))
>> +      return I;
>> +  }
>> +  Filenames.push_back(Filename);
>> +  return Filenames.size() - 1;
>> +}
>> +
>> +void
>> +CoverageSummary::createSummaries(ArrayRef<FunctionCoverageMapping>
>> Functions) {
>> +  std::vector<std::pair<unsigned, size_t>> FunctionFileIDs;
>> +
>> +  FunctionFileIDs.resize(Functions.size());
>> +  for (size_t I = 0, E = Functions.size(); I < E; ++I) {
>> +    StringRef Filename = Functions[I].Filenames[0];
>> +    FunctionFileIDs[I] = std::make_pair(getFileID(Filename), I);
>> +  }
>> +
>> +  // Sort the function records by file ids
>> +  std::sort(FunctionFileIDs.begin(), FunctionFileIDs.end(),
>> +            [](const std::pair<unsigned, size_t> &lhs,
>> +               const std::pair<unsigned, size_t> &rhs) {
>> +    return lhs.first < rhs.first;
>> +  });
>> +
>> +  // Create function summaries in a sorted order (by file ids)
>> +  FunctionSummaries.reserve(Functions.size());
>> +  for (size_t I = 0, E = Functions.size(); I < E; ++I)
>> +    FunctionSummaries.push_back(
>> +
>> FunctionCoverageSummary::get(Functions[FunctionFileIDs[I].second]));
>> +
>> +  // Create file summaries
>> +  size_t CurrentSummary = 0;
>> +  for (unsigned FileID = 0; FileID < Filenames.size(); ++FileID) {
>> +    // Gather the relevant functions summaries
>> +    auto PrevSummary = CurrentSummary;
>> +    while (CurrentSummary < FunctionSummaries.size() &&
>> +           FunctionFileIDs[CurrentSummary].first == FileID)
>> +      ++CurrentSummary;
>> +    ArrayRef<FunctionCoverageSummary> LocalSummaries(
>> +        FunctionSummaries.data() + PrevSummary,
>> +        FunctionSummaries.data() + CurrentSummary);
>> +    if (LocalSummaries.empty())
>> +      continue;
>> +
>> +    FileSummaries.push_back(
>> +        FileCoverageSummary::get(Filenames[FileID], LocalSummaries));
>> +  }
>> +}
>> +
>> +FileCoverageSummary CoverageSummary::getCombinedFileSummaries() {
>> +  size_t NumRegions = 0, CoveredRegions = 0;
>> +  size_t NumLines = 0, NonCodeLines = 0, CoveredLines = 0;
>> +  size_t NumFunctionsCovered = 0, NumFunctions = 0;
>> +  for (const auto &File : FileSummaries) {
>> +    NumRegions += File.RegionCoverage.NumRegions;
>> +    CoveredRegions += File.RegionCoverage.Covered;
>> +
>> +    NumLines += File.LineCoverage.NumLines;
>> +    NonCodeLines += File.LineCoverage.NonCodeLines;
>> +    CoveredLines += File.LineCoverage.Covered;
>> +
>> +    NumFunctionsCovered += File.FunctionCoverage.FullyCovered;
>> +    NumFunctions += File.FunctionCoverage.NumFunctions;
>> +  }
>> +  return FileCoverageSummary(
>> +      "TOTAL", RegionCoverageInfo(CoveredRegions, NumRegions),
>> +      LineCoverageInfo(CoveredLines, NonCodeLines, NumLines),
>> +      FunctionCoverageInfo(NumFunctionsCovered, NumFunctions),
>> +      ArrayRef<FunctionCoverageSummary>());
>> +}
>>
>> Added: llvm/trunk/tools/llvm-cov/CoverageSummary.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/CoverageSummary.h?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/CoverageSummary.h (added)
>> +++ llvm/trunk/tools/llvm-cov/CoverageSummary.h Fri Aug 22 17:56:03 2014
>> @@ -0,0 +1,45 @@
>> +//===- CoverageSummary.h - Code coverage summary
>> --------------------------===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// This class implements data management and rendering for the code
>> coverage
>> +// summaries of all files and functions.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#ifndef LLVM_COV_COVERAGESUMMARY_H
>> +#define LLVM_COV_COVERAGESUMMARY_H
>> +
>> +#include "CoverageSummaryInfo.h"
>> +#include <vector>
>> +
>> +namespace llvm {
>> +
>> +/// \brief Manager for the function and file code coverage summaries.
>> +class CoverageSummary {
>> +  std::vector<StringRef> Filenames;
>> +  std::vector<FunctionCoverageSummary> FunctionSummaries;
>> +  std::vector<std::pair<unsigned, unsigned>> FunctionSummariesFileIDs;
>> +  std::vector<FileCoverageSummary> FileSummaries;
>> +
>> +  unsigned getFileID(StringRef Filename);
>> +
>> +public:
>> +  void createSummaries(ArrayRef<FunctionCoverageMapping> Functions);
>> +
>> +  ArrayRef<FileCoverageSummary> getFileSummaries() { return
>> FileSummaries; }
>> +
>> +  FileCoverageSummary getCombinedFileSummaries();
>> +
>> +  void render(const FunctionCoverageSummary &Summary, raw_ostream &OS);
>> +
>> +  void render(raw_ostream &OS);
>> +};
>> +}
>> +
>> +#endif // LLVM_COV_COVERAGESUMMARY_H
>>
>> Added: llvm/trunk/tools/llvm-cov/CoverageSummaryInfo.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/CoverageSummaryInfo.cpp?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/CoverageSummaryInfo.cpp (added)
>> +++ llvm/trunk/tools/llvm-cov/CoverageSummaryInfo.cpp Fri Aug 22 17:56:03
>> 2014
>> @@ -0,0 +1,95 @@
>> +//===- CoverageSummaryInfo.cpp - Coverage summary for function/file
>> -------===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// These structures are used to represent code coverage metrics
>> +// for functions/files.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#include "CoverageSummaryInfo.h"
>> +
>> +using namespace llvm;
>> +using namespace coverage;
>> +
>> +FunctionCoverageSummary
>> +FunctionCoverageSummary::get(const FunctionCoverageMapping &Function) {
>> +  // Compute the region coverage
>> +  size_t NumCodeRegions = 0, CoveredRegions = 0;
>> +  for (auto &Region : Function.MappingRegions) {
>> +    if (Region.Kind != CounterMappingRegion::CodeRegion)
>> +      continue;
>> +    ++NumCodeRegions;
>> +    if (Region.ExecutionCount != 0)
>> +      ++CoveredRegions;
>> +  }
>> +
>> +  // Compute the line coverage
>> +  size_t NumLines = 0, CoveredLines = 0;
>> +  for (unsigned FileID = 0, E = Function.Filenames.size(); FileID < E;
>> +       ++FileID) {
>> +    // Find the line start and end of the function's source code
>> +    // in that particular file
>> +    unsigned LineStart = std::numeric_limits<unsigned>::max();
>> +    unsigned LineEnd = 0;
>> +    for (auto &Region : Function.MappingRegions) {
>> +      if (Region.FileID != FileID)
>> +        continue;
>> +      LineStart = std::min(LineStart, Region.LineStart);
>> +      LineEnd = std::max(LineEnd, Region.LineEnd);
>> +    }
>> +    unsigned LineCount = LineEnd - LineStart + 1;
>> +
>> +    // Get counters
>> +    llvm::SmallVector<uint64_t, 16> ExecutionCounts;
>> +    ExecutionCounts.resize(LineCount, 0);
>> +    for (auto &Region : Function.MappingRegions) {
>> +      if (Region.FileID != FileID)
>> +        continue;
>> +      // Ignore the lines that were skipped by the preprocessor.
>> +      auto ExecutionCount = Region.ExecutionCount;
>> +      if (Region.Kind == MappingRegion::SkippedRegion) {
>> +        LineCount -= Region.LineEnd - Region.LineStart + 1;
>> +        ExecutionCount = 1;
>> +      }
>> +      for (unsigned I = Region.LineStart; I <= Region.LineEnd; ++I)
>> +        ExecutionCounts[I - LineStart] = ExecutionCount;
>> +    }
>> +    CoveredLines += LineCount - std::count(ExecutionCounts.begin(),
>> +                                           ExecutionCounts.end(), 0);
>> +    NumLines += LineCount;
>> +  }
>> +  return FunctionCoverageSummary(
>> +      Function.PrettyName, RegionCoverageInfo(CoveredRegions,
>> NumCodeRegions),
>> +      LineCoverageInfo(CoveredLines, 0, NumLines));
>> +}
>> +
>> +FileCoverageSummary
>> +FileCoverageSummary::get(StringRef Name,
>> +                         ArrayRef<FunctionCoverageSummary>
>> FunctionSummaries) {
>> +  size_t NumRegions = 0, CoveredRegions = 0;
>> +  size_t NumLines = 0, NonCodeLines = 0, CoveredLines = 0;
>> +  size_t NumFunctionsCovered = 0;
>> +  for (const auto &Func : FunctionSummaries) {
>> +    CoveredRegions += Func.RegionCoverage.Covered;
>> +    NumRegions += Func.RegionCoverage.NumRegions;
>> +
>> +    CoveredLines += Func.LineCoverage.Covered;
>> +    NonCodeLines += Func.LineCoverage.NonCodeLines;
>> +    NumLines += Func.LineCoverage.NumLines;
>> +
>> +    if (Func.RegionCoverage.isFullyCovered())
>> +      ++NumFunctionsCovered;
>> +  }
>> +
>> +  return FileCoverageSummary(
>> +      Name, RegionCoverageInfo(CoveredRegions, NumRegions),
>> +      LineCoverageInfo(CoveredLines, NonCodeLines, NumLines),
>> +      FunctionCoverageInfo(NumFunctionsCovered,
>> FunctionSummaries.size()),
>> +      FunctionSummaries);
>> +}
>>
>> Added: llvm/trunk/tools/llvm-cov/CoverageSummaryInfo.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/CoverageSummaryInfo.h?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/CoverageSummaryInfo.h (added)
>> +++ llvm/trunk/tools/llvm-cov/CoverageSummaryInfo.h Fri Aug 22 17:56:03
>> 2014
>> @@ -0,0 +1,131 @@
>> +//===- CoverageSummaryInfo.h - Coverage summary for function/file
>> ---------===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// These structures are used to represent code coverage metrics
>> +// for functions/files.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#ifndef LLVM_COV_COVERAGESUMMARYINFO_H
>> +#define LLVM_COV_COVERAGESUMMARYINFO_H
>> +
>> +#include "FunctionCoverageMapping.h"
>> +#include "llvm/Support/raw_ostream.h"
>> +
>> +namespace llvm {
>> +
>> +/// \brief Provides information about region coverage for a
>> function/file.
>> +struct RegionCoverageInfo {
>> +  /// \brief The number of regions that were executed at least once.
>> +  size_t Covered;
>> +
>> +  /// \brief The number of regions that weren't executed.
>> +  size_t NotCovered;
>> +
>> +  /// \brief The total number of regions in a function/file.
>> +  size_t NumRegions;
>> +
>> +  RegionCoverageInfo(size_t Covered, size_t NumRegions)
>> +      : Covered(Covered), NotCovered(NumRegions - Covered),
>> +        NumRegions(NumRegions) {}
>> +
>> +  bool isFullyCovered() const { return Covered == NumRegions; }
>> +
>> +  double getPercentCovered() const {
>> +    return double(Covered) / double(NumRegions) * 100.0;
>> +  }
>> +};
>> +
>> +/// \brief Provides information about line coverage for a function/file.
>> +struct LineCoverageInfo {
>> +  /// \brief The number of lines that were executed at least once.
>> +  size_t Covered;
>> +
>> +  /// \brief The number of lines that weren't executed.
>> +  size_t NotCovered;
>> +
>> +  /// \brief The number of lines that aren't code.
>> +  size_t NonCodeLines;
>> +
>> +  /// \brief The total number of lines in a function/file.
>> +  size_t NumLines;
>> +
>> +  LineCoverageInfo(size_t Covered, size_t NumNonCodeLines, size_t
>> NumLines)
>> +      : Covered(Covered), NotCovered(NumLines - NumNonCodeLines -
>> Covered),
>> +        NonCodeLines(NumNonCodeLines), NumLines(NumLines) {}
>> +
>> +  bool isFullyCovered() const { return Covered == (NumLines -
>> NonCodeLines); }
>> +
>> +  double getPercentCovered() const {
>> +    return double(Covered) / double(NumLines - NonCodeLines) * 100.0;
>> +  }
>> +};
>> +
>> +/// \brief Provides information about function coverage for a file.
>> +struct FunctionCoverageInfo {
>> +  /// \brief The number of functions that have full
>> +  /// region coverage.
>> +  size_t FullyCovered;
>> +
>> +  /// \brief The total number of functions in this file.
>> +  size_t NumFunctions;
>> +
>> +  FunctionCoverageInfo(size_t FullyCovered, size_t NumFunctions)
>> +      : FullyCovered(FullyCovered), NumFunctions(NumFunctions) {}
>> +
>> +  bool isFullyCovered() const { return FullyCovered == NumFunctions; }
>> +
>> +  double getPercentCovered() const {
>> +    return double(FullyCovered) / double(NumFunctions) * 100.0;
>> +  }
>> +};
>> +
>> +/// \brief A summary of function's code coverage.
>> +struct FunctionCoverageSummary {
>> +  StringRef Name;
>> +  RegionCoverageInfo RegionCoverage;
>> +  LineCoverageInfo LineCoverage;
>> +
>> +  FunctionCoverageSummary(StringRef Name,
>> +                          const RegionCoverageInfo &RegionCoverage,
>> +                          const LineCoverageInfo &LineCoverage)
>> +      : Name(Name), RegionCoverage(RegionCoverage),
>> LineCoverage(LineCoverage) {
>> +  }
>> +
>> +  /// \brief Compute the code coverage summary for the given function
>> coverage
>> +  /// mapping record.
>> +  static FunctionCoverageSummary get(const FunctionCoverageMapping
>> &Function);
>> +};
>> +
>> +/// \brief A summary of file's code coverage.
>> +struct FileCoverageSummary {
>> +  StringRef Name;
>> +  RegionCoverageInfo RegionCoverage;
>> +  LineCoverageInfo LineCoverage;
>> +  FunctionCoverageInfo FunctionCoverage;
>> +  /// \brief The summary of every function
>> +  /// in this file.
>> +  ArrayRef<FunctionCoverageSummary> FunctionSummaries;
>> +
>> +  FileCoverageSummary(StringRef Name, const RegionCoverageInfo
>> &RegionCoverage,
>> +                      const LineCoverageInfo &LineCoverage,
>> +                      const FunctionCoverageInfo &FunctionCoverage,
>> +                      ArrayRef<FunctionCoverageSummary>
>> FunctionSummaries)
>> +      : Name(Name), RegionCoverage(RegionCoverage),
>> LineCoverage(LineCoverage),
>> +        FunctionCoverage(FunctionCoverage),
>> +        FunctionSummaries(FunctionSummaries) {}
>> +
>> +  /// \brief Compute the code coverage summary for a file.
>> +  static FileCoverageSummary
>> +  get(StringRef Name, ArrayRef<FunctionCoverageSummary>
>> FunctionSummaries);
>> +};
>> +
>> +} // namespace llvm
>> +
>> +#endif // LLVM_COV_COVERAGESUMMARYINFO_H
>>
>> Added: llvm/trunk/tools/llvm-cov/CoverageViewOptions.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/CoverageViewOptions.h?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/CoverageViewOptions.h (added)
>> +++ llvm/trunk/tools/llvm-cov/CoverageViewOptions.h Fri Aug 22 17:56:03
>> 2014
>> @@ -0,0 +1,36 @@
>> +//===- CoverageViewOptions.h - Code coverage display options
>> -------------===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#ifndef LLVM_COV_COVERAGEVIEWOPTIONS_H
>> +#define LLVM_COV_COVERAGEVIEWOPTIONS_H
>> +
>> +#include "RenderingSupport.h"
>> +
>> +namespace llvm {
>> +
>> +/// \brief The options for displaying the code coverage information.
>> +struct CoverageViewOptions {
>> +  bool Debug;
>> +  bool Colors;
>> +  bool ShowLineNumbers;
>> +  bool ShowLineStats;
>> +  bool ShowRegionMarkers;
>> +  bool ShowLineStatsOrRegionMarkers;
>> +  bool ShowExpandedRegions;
>> +  bool ShowFunctionInstantiations;
>> +
>> +  /// \brief Change the output's stream color if the colors are enabled.
>> +  ColoredRawOstream colored_ostream(raw_ostream &OS,
>> +                                    raw_ostream::Colors Color) const {
>> +    return llvm::colored_ostream(OS, Color, Colors);
>> +  }
>> +};
>> +}
>> +
>> +#endif // LLVM_COV_COVERAGEVIEWOPTIONS_H
>>
>> Added: llvm/trunk/tools/llvm-cov/FunctionCoverageMapping.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/FunctionCoverageMapping.h?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/FunctionCoverageMapping.h (added)
>> +++ llvm/trunk/tools/llvm-cov/FunctionCoverageMapping.h Fri Aug 22
>> 17:56:03 2014
>> @@ -0,0 +1,50 @@
>> +//===- FunctionCoverageMapping.h - Function coverage mapping record
>> -------===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// A structure that stores the coverage mapping record for a single
>> function.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#ifndef LLVM_COV_FUNCTIONCOVERAGEMAPPING_H
>> +#define LLVM_COV_FUNCTIONCOVERAGEMAPPING_H
>> +
>> +#include <string>
>> +#include <vector>
>> +#include "llvm/ADT/ArrayRef.h"
>> +#include "llvm/ADT/StringRef.h"
>> +#include "llvm/ProfileData/CoverageMapping.h"
>> +
>> +namespace llvm {
>> +
>> +/// \brief Associates a source range with an execution count.
>> +struct MappingRegion : public coverage::CounterMappingRegion {
>> +  uint64_t ExecutionCount;
>> +
>> +  MappingRegion(const CounterMappingRegion &R, uint64_t ExecutionCount)
>> +      : CounterMappingRegion(R), ExecutionCount(ExecutionCount) {}
>> +};
>> +
>> +/// \brief Stores all the required information
>> +/// about code coverage for a single function.
>> +struct FunctionCoverageMapping {
>> +  /// \brief Raw function name.
>> +  std::string Name;
>> +  /// \brief Demangled function name.
>> +  std::string PrettyName;
>> +  std::vector<std::string> Filenames;
>> +  std::vector<MappingRegion> MappingRegions;
>> +
>> +  FunctionCoverageMapping(StringRef Name, ArrayRef<StringRef> Filenames)
>> +      : Name(Name), PrettyName(Name),
>> +        Filenames(Filenames.begin(), Filenames.end()) {}
>> +};
>> +
>> +} // namespace llvm
>> +
>> +#endif // LLVM_COV_FUNCTIONCOVERAGEMAPPING_H
>>
>> Modified: llvm/trunk/tools/llvm-cov/LLVMBuild.txt
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/LLVMBuild.txt?rev=216300&r1=216299&r2=216300&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/LLVMBuild.txt (original)
>> +++ llvm/trunk/tools/llvm-cov/LLVMBuild.txt Fri Aug 22 17:56:03 2014
>> @@ -19,4 +19,4 @@
>>  type = Tool
>>  name = llvm-cov
>>  parent = Tools
>> -required_libraries = Instrumentation
>> +required_libraries = ProfileData Support Instrumentation
>>
>> Modified: llvm/trunk/tools/llvm-cov/Makefile
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/Makefile?rev=216300&r1=216299&r2=216300&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/Makefile (original)
>> +++ llvm/trunk/tools/llvm-cov/Makefile Fri Aug 22 17:56:03 2014
>> @@ -9,7 +9,7 @@
>>
>>  LEVEL := ../..
>>  TOOLNAME := llvm-cov
>> -LINK_COMPONENTS := core support
>> +LINK_COMPONENTS := core support profiledata object
>>
>>  # This tool has no plugins, optimize startup time.
>>  TOOL_NO_EXPORTS := 1
>>
>> Added: llvm/trunk/tools/llvm-cov/RenderingSupport.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/RenderingSupport.h?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/RenderingSupport.h (added)
>> +++ llvm/trunk/tools/llvm-cov/RenderingSupport.h Fri Aug 22 17:56:03 2014
>> @@ -0,0 +1,59 @@
>> +//===- RenderingSupport.h - output stream rendering support functions
>> ----===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#ifndef LLVM_COV_RENDERINGSUPPORT_H
>> +#define LLVM_COV_RENDERINGSUPPORT_H
>> +
>> +#include "llvm/Support/raw_ostream.h"
>> +#include <utility>
>> +
>> +namespace llvm {
>> +
>> +/// \brief A helper class that resets the output stream's color if needed
>> +/// when destroyed.
>> +class ColoredRawOstream {
>> +  ColoredRawOstream(const ColoredRawOstream &OS) LLVM_DELETED_FUNCTION;
>> +
>> +public:
>> +  raw_ostream &OS;
>> +  bool IsColorUsed;
>> +
>> +  ColoredRawOstream(raw_ostream &OS, bool IsColorUsed)
>> +      : OS(OS), IsColorUsed(IsColorUsed) {}
>> +
>> +  ColoredRawOstream(ColoredRawOstream &&Other)
>> +      : OS(Other.OS), IsColorUsed(Other.IsColorUsed) {
>> +    // Reset the other IsColorUsed so that the other object won't reset
>> the
>> +    // color when destroyed.
>> +    Other.IsColorUsed = false;
>> +  }
>> +
>> +  ~ColoredRawOstream() {
>> +    if (IsColorUsed)
>> +      OS.resetColor();
>> +  }
>> +};
>> +
>> +template <typename T>
>> +inline raw_ostream &operator<<(const ColoredRawOstream &OS, T &&Value) {
>> +  return OS.OS << std::forward<T>(Value);
>> +}
>> +
>> +/// \brief Change the color of the output stream if the `IsColorUsed`
>> flag
>> +/// is true. Returns an object that resets the color when destroyed.
>> +inline ColoredRawOstream colored_ostream(raw_ostream &OS,
>> +                                         raw_ostream::Colors Color,
>> +                                         bool IsColorUsed = false) {
>> +  if (IsColorUsed)
>> +    OS.changeColor(Color);
>> +  return ColoredRawOstream(OS, IsColorUsed);
>> +}
>> +}
>> +
>> +#endif // LLVM_COV_RENDERINGSUPPORT_H
>>
>> Added: llvm/trunk/tools/llvm-cov/SourceCoverageDataManager.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/SourceCoverageDataManager.cpp?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/SourceCoverageDataManager.cpp (added)
>> +++ llvm/trunk/tools/llvm-cov/SourceCoverageDataManager.cpp Fri Aug 22
>> 17:56:03 2014
>> @@ -0,0 +1,57 @@
>> +//===- SourceCoverageDataManager.cpp - Manager for source file coverage
>> +// data-===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// This class separates and merges mapping regions for a specific source
>> file.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#include "SourceCoverageDataManager.h"
>> +
>> +using namespace llvm;
>> +using namespace coverage;
>> +
>> +void SourceCoverageDataManager::insert(const MappingRegion &Region) {
>> +  SourceRange Range(Region.LineStart, Region.ColumnStart, Region.LineEnd,
>> +                    Region.ColumnEnd);
>> +  if (Region.Kind == CounterMappingRegion::SkippedRegion) {
>> +    SkippedRegions.push_back(Range);
>> +    return;
>> +  }
>> +  Regions.push_back(std::make_pair(Range, Region.ExecutionCount));
>> +}
>> +
>> +ArrayRef<std::pair<SourceCoverageDataManager::SourceRange, uint64_t>>
>> +SourceCoverageDataManager::getSourceRegions() {
>> +  if (Uniqued || Regions.size() <= 1)
>> +    return Regions;
>> +
>> +  // Sort.
>> +  std::sort(Regions.begin(), Regions.end(),
>> +            [](const std::pair<SourceRange, uint64_t> &LHS,
>> +               const std::pair<SourceRange, uint64_t> &RHS) {
>> +    return LHS.first < RHS.first;
>> +  });
>> +
>> +  // Merge duplicate source ranges and sum their execution counts.
>> +  auto Prev = Regions.begin();
>> +  for (auto I = Prev + 1, E = Regions.end(); I != E; ++I) {
>> +    if (I->first == Prev->first) {
>> +      Prev->second += I->second;
>> +      continue;
>> +    }
>> +    ++Prev;
>> +    *Prev = *I;
>> +  }
>> +  ++Prev;
>> +  Regions.erase(Prev, Regions.end());
>> +
>> +  Uniqued = true;
>> +  return Regions;
>> +}
>>
>> Added: llvm/trunk/tools/llvm-cov/SourceCoverageDataManager.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/SourceCoverageDataManager.h?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/SourceCoverageDataManager.h (added)
>> +++ llvm/trunk/tools/llvm-cov/SourceCoverageDataManager.h Fri Aug 22
>> 17:56:03 2014
>> @@ -0,0 +1,79 @@
>> +//===- SourceCoverageDataManager.h - Manager for source file coverage
>> data-===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// This class separates and merges mapping regions for a specific source
>> file.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#ifndef LLVM_COV_SOURCECOVERAGEDATAMANAGER_H
>> +#define LLVM_COV_SOURCECOVERAGEDATAMANAGER_H
>> +
>> +#include "FunctionCoverageMapping.h"
>> +#include "llvm/ProfileData/CoverageMapping.h"
>> +#include "llvm/ADT/Hashing.h"
>> +#include <vector>
>> +#include <unordered_map>
>> +
>> +namespace llvm {
>> +
>> +/// \brief Partions mapping regions by their kind and sums
>> +/// the execution counts of the regions that start at the same location.
>> +class SourceCoverageDataManager {
>> +public:
>> +  struct SourceRange {
>> +    unsigned LineStart, ColumnStart, LineEnd, ColumnEnd;
>> +
>> +    SourceRange(unsigned LineStart, unsigned ColumnStart, unsigned
>> LineEnd,
>> +                unsigned ColumnEnd)
>> +        : LineStart(LineStart), ColumnStart(ColumnStart),
>> LineEnd(LineEnd),
>> +          ColumnEnd(ColumnEnd) {}
>> +
>> +    bool operator==(const SourceRange &Other) const {
>> +      return LineStart == Other.LineStart && ColumnStart ==
>> Other.ColumnStart &&
>> +             LineEnd == Other.LineEnd && ColumnEnd == Other.ColumnEnd;
>> +    }
>> +
>> +    bool operator<(const SourceRange &Other) const {
>> +      if (LineStart == Other.LineStart)
>> +        return ColumnStart < Other.ColumnStart;
>> +      return LineStart < Other.LineStart;
>> +    }
>> +
>> +    bool contains(const SourceRange &Other) {
>> +      if (LineStart > Other.LineStart ||
>> +          (LineStart == Other.LineStart && ColumnStart >
>> Other.ColumnStart))
>> +        return false;
>> +      if (LineEnd < Other.LineEnd ||
>> +          (LineEnd == Other.LineEnd && ColumnEnd < Other.ColumnEnd))
>> +        return false;
>> +      return true;
>> +    }
>> +  };
>> +
>> +protected:
>> +  std::vector<std::pair<SourceRange, uint64_t>> Regions;
>> +  std::vector<SourceRange> SkippedRegions;
>> +  bool Uniqued;
>> +
>> +public:
>> +  SourceCoverageDataManager() : Uniqued(false) {}
>> +
>> +  void insert(const MappingRegion &Region);
>> +
>> +  /// \brief Return the source ranges and execution counts
>> +  /// obtained from the non-skipped mapping regions.
>> +  ArrayRef<std::pair<SourceRange, uint64_t>> getSourceRegions();
>> +
>> +  /// \brief Return the source ranges obtained from the skipped mapping
>> regions.
>> +  ArrayRef<SourceRange> getSkippedRegions() const { return
>> SkippedRegions; }
>> +};
>> +
>> +} // namespace llvm
>> +
>> +#endif // LLVM_COV_SOURCECOVERAGEDATAMANAGER_H
>>
>> Added: llvm/trunk/tools/llvm-cov/SourceCoverageView.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/SourceCoverageView.cpp?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/SourceCoverageView.cpp (added)
>> +++ llvm/trunk/tools/llvm-cov/SourceCoverageView.cpp Fri Aug 22 17:56:03
>> 2014
>> @@ -0,0 +1,411 @@
>> +//===- SourceCoverageView.cpp - Code coverage view for source code
>> --------===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// This class implements rendering for code coverage of source code.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#include "SourceCoverageView.h"
>> +#include "llvm/ADT/SmallString.h"
>> +#include "llvm/Support/LineIterator.h"
>> +
>> +using namespace llvm;
>> +
>> +void SourceCoverageView::renderLine(raw_ostream &OS, StringRef Line,
>> +                                    ArrayRef<HighlightRange> Ranges) {
>> +  if (Ranges.empty()) {
>> +    OS << Line << "\n";
>> +    return;
>> +  }
>> +  if (Line.empty())
>> +    Line = " ";
>> +
>> +  unsigned PrevColumnStart = 0;
>> +  unsigned Start = 1;
>> +  for (const auto &Range : Ranges) {
>> +    if (PrevColumnStart == Range.ColumnStart)
>> +      continue;
>> +
>> +    // Show the unhighlighted part
>> +    unsigned ColumnStart = PrevColumnStart = Range.ColumnStart;
>> +    OS << Line.substr(Start - 1, ColumnStart - Start);
>> +
>> +    // Show the highlighted part
>> +    auto Color = Range.Kind == HighlightRange::NotCovered ?
>> raw_ostream::RED
>> +                                                          :
>> raw_ostream::CYAN;
>> +    OS.changeColor(Color, false, true);
>> +    unsigned ColumnEnd = std::min(Range.ColumnEnd, (unsigned)Line.size()
>> + 1);
>> +    OS << Line.substr(ColumnStart - 1, ColumnEnd - ColumnStart);
>> +    Start = ColumnEnd;
>> +    OS.resetColor();
>> +  }
>> +
>> +  // Show the rest of the line
>> +  OS << Line.substr(Start - 1, Line.size() - Start + 1);
>> +  OS << "\n";
>> +}
>> +
>> +void SourceCoverageView::renderOffset(raw_ostream &OS, unsigned I) {
>> +  for (unsigned J = 0; J < I; ++J)
>> +    OS << "  |";
>> +}
>> +
>> +void SourceCoverageView::renderViewDivider(unsigned Offset, unsigned
>> Length,
>> +                                           raw_ostream &OS) {
>> +  for (unsigned J = 1; J < Offset; ++J)
>> +    OS << "  |";
>> +  if (Offset != 0)
>> +    OS.indent(2);
>> +  for (unsigned I = 0; I < Length; ++I)
>> +    OS << "-";
>> +}
>> +
>> +void
>> +SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS,
>> +                                             const LineCoverageInfo
>> &Line) {
>> +  if (!Line.isMapped()) {
>> +    OS.indent(LineCoverageColumnWidth) << '|';
>> +    return;
>> +  }
>> +  SmallString<32> Buffer;
>> +  raw_svector_ostream BufferOS(Buffer);
>> +  BufferOS << Line.ExecutionCount;
>> +  auto Str = BufferOS.str();
>> +  // Trim
>> +  Str = Str.substr(0, std::min(Str.size(),
>> (size_t)LineCoverageColumnWidth));
>> +  // Align to the right
>> +  OS.indent(LineCoverageColumnWidth - Str.size());
>> +  colored_ostream(OS, raw_ostream::MAGENTA,
>> +                  Line.hasMultipleRegions() && Options.Colors)
>> +      << Str;
>> +  OS << '|';
>> +}
>> +
>> +void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS,
>> +                                                unsigned LineNo) {
>> +  SmallString<32> Buffer;
>> +  raw_svector_ostream BufferOS(Buffer);
>> +  BufferOS << LineNo;
>> +  auto Str = BufferOS.str();
>> +  // Trim and align to the right
>> +  Str = Str.substr(0, std::min(Str.size(),
>> (size_t)LineNumberColumnWidth));
>> +  OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
>> +}
>> +
>> +void SourceCoverageView::renderRegionMarkers(raw_ostream &OS,
>> +                                             ArrayRef<RegionMarker>
>> Regions) {
>> +  SmallString<32> Buffer;
>> +  raw_svector_ostream BufferOS(Buffer);
>> +
>> +  unsigned PrevColumn = 1;
>> +  for (const auto &Region : Regions) {
>> +    // Skip to the new region
>> +    if (Region.Column > PrevColumn)
>> +      OS.indent(Region.Column - PrevColumn);
>> +    PrevColumn = Region.Column + 1;
>> +    BufferOS << Region.ExecutionCount;
>> +    StringRef Str = BufferOS.str();
>> +    // Trim the execution count
>> +    Str = Str.substr(0, std::min(Str.size(), (size_t)7));
>> +    PrevColumn += Str.size();
>> +    OS << '^' << Str;
>> +    Buffer.clear();
>> +  }
>> +  OS << "\n";
>> +}
>> +
>> +/// \brief Insert a new highlighting range into the line's highlighting
>> ranges
>> +/// Return line's new highlighting ranges in result.
>> +static void insertHighlightRange(
>> +    ArrayRef<SourceCoverageView::HighlightRange> Ranges,
>> +    SourceCoverageView::HighlightRange RangeToInsert,
>> +    SmallVectorImpl<SourceCoverageView::HighlightRange> &Result) {
>> +  Result.clear();
>> +  size_t I = 0;
>> +  auto E = Ranges.size();
>> +  for (; I < E; ++I) {
>> +    if (RangeToInsert.ColumnStart < Ranges[I].ColumnEnd) {
>> +      const auto &Range = Ranges[I];
>> +      bool NextRangeContainsInserted = false;
>> +      // If the next range starts before the inserted range, move the
>> end of the
>> +      // next range to the start of the inserted range.
>> +      if (Range.ColumnStart < RangeToInsert.ColumnStart) {
>> +        if (RangeToInsert.ColumnStart != Range.ColumnStart)
>> +          Result.push_back(SourceCoverageView::HighlightRange(
>> +              Range.Line, Range.ColumnStart, RangeToInsert.ColumnStart,
>> +              Range.Kind));
>> +        // If the next range also ends after the inserted range, keep
>> this range
>> +        // and create a new range that starts at the inserted range and
>> ends
>> +        // at the next range later.
>> +        if (Range.ColumnEnd > RangeToInsert.ColumnEnd)
>> +          NextRangeContainsInserted = true;
>> +      }
>> +      if (!NextRangeContainsInserted) {
>> +        ++I;
>> +        // Ignore ranges that are contained in inserted range
>> +        while (I < E && RangeToInsert.contains(Ranges[I]))
>> +          ++I;
>> +      }
>> +      break;
>> +    }
>> +    Result.push_back(Ranges[I]);
>> +  }
>> +  Result.push_back(RangeToInsert);
>> +  // If the next range starts before the inserted range end, move the
>> start
>> +  // of the next range to the end of the inserted range.
>> +  if (I < E && Ranges[I].ColumnStart < RangeToInsert.ColumnEnd) {
>> +    const auto &Range = Ranges[I];
>> +    if (RangeToInsert.ColumnEnd != Range.ColumnEnd)
>> +      Result.push_back(SourceCoverageView::HighlightRange(
>> +          Range.Line, RangeToInsert.ColumnEnd, Range.ColumnEnd,
>> Range.Kind));
>> +    ++I;
>> +  }
>> +  // Add the remaining ranges that are located after the inserted range
>> +  for (; I < E; ++I)
>> +    Result.push_back(Ranges[I]);
>> +}
>> +
>> +void SourceCoverageView::sortChildren() {
>> +  for (auto &I : Children)
>> +    I->sortChildren();
>> +  std::sort(Children.begin(), Children.end(),
>> +            [](const std::unique_ptr<SourceCoverageView> &LHS,
>> +               const std::unique_ptr<SourceCoverageView> &RHS) {
>> +    return LHS->ExpansionRegion < RHS->ExpansionRegion;
>> +  });
>> +}
>> +
>> +SourceCoverageView::HighlightRange
>> +SourceCoverageView::getExpansionHighlightRange() const {
>> +  return HighlightRange(ExpansionRegion.LineStart,
>> ExpansionRegion.ColumnStart,
>> +                        ExpansionRegion.ColumnEnd,
>> HighlightRange::Expanded);
>> +}
>> +
>> +template <typename T>
>> +ArrayRef<T> gatherLineItems(size_t &CurrentIdx, const std::vector<T>
>> &Items,
>> +                            unsigned LineNo) {
>> +  auto PrevIdx = CurrentIdx;
>> +  auto E = Items.size();
>> +  while (CurrentIdx < E && Items[CurrentIdx].Line == LineNo)
>> +    ++CurrentIdx;
>> +  return ArrayRef<T>(Items.data() + PrevIdx, CurrentIdx - PrevIdx);
>> +}
>> +
>> +ArrayRef<std::unique_ptr<SourceCoverageView>>
>> +gatherLineSubViews(size_t &CurrentIdx,
>> +                   ArrayRef<std::unique_ptr<SourceCoverageView>> Items,
>> +                   unsigned LineNo) {
>> +  auto PrevIdx = CurrentIdx;
>> +  auto E = Items.size();
>> +  while (CurrentIdx < E &&
>> +         Items[CurrentIdx]->getSubViewsExpansionLine() == LineNo)
>> +    ++CurrentIdx;
>> +  return ArrayRef<std::unique_ptr<SourceCoverageView>>(Items.data() +
>> PrevIdx,
>> +                                                       CurrentIdx -
>> PrevIdx);
>> +}
>> +
>> +void SourceCoverageView::render(raw_ostream &OS, unsigned Offset) {
>> +  // Make sure that the children are in sorted order.
>> +  sortChildren();
>> +
>> +  SmallVector<HighlightRange, 8> AdjustedLineHighlightRanges;
>> +  size_t CurrentChild = 0;
>> +  size_t CurrentHighlightRange = 0;
>> +  size_t CurrentRegionMarker = 0;
>> +
>> +  line_iterator Lines(File);
>> +  // Advance the line iterator to the first line.
>> +  while (Lines.line_number() < LineStart)
>> +    ++Lines;
>> +
>> +  // The width of the leading columns
>> +  unsigned CombinedColumnWidth =
>> +      (Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
>> +      (Options.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
>> +  // The width of the line that is used to divide between the view and
>> the
>> +  // subviews.
>> +  unsigned DividerWidth = CombinedColumnWidth + 4;
>> +
>> +  for (size_t I = 0; I < LineCount; ++I) {
>> +    unsigned LineNo = I + LineStart;
>> +
>> +    // Gather the child subviews that are visible on this line.
>> +    auto LineSubViews = gatherLineSubViews(CurrentChild, Children,
>> LineNo);
>> +
>> +    renderOffset(OS, Offset);
>> +    if (Options.ShowLineStats)
>> +      renderLineCoverageColumn(OS, LineStats[I]);
>> +    if (Options.ShowLineNumbers)
>> +      renderLineNumberColumn(OS, LineNo);
>> +
>> +    // Gather highlighting ranges.
>> +    auto LineHighlightRanges =
>> +        gatherLineItems(CurrentHighlightRange, HighlightRanges, LineNo);
>> +    auto LineRanges = LineHighlightRanges;
>> +    // Highlight the expansion range if there is an expansion subview on
>> this
>> +    // line.
>> +    if (!LineSubViews.empty() &&
>> LineSubViews.front()->isExpansionSubView() &&
>> +        Options.Colors) {
>> +      insertHighlightRange(LineHighlightRanges,
>> +
>>  LineSubViews.front()->getExpansionHighlightRange(),
>> +                           AdjustedLineHighlightRanges);
>> +      LineRanges = AdjustedLineHighlightRanges;
>> +    }
>> +
>> +    // Display the source code for the current line.
>> +    StringRef Line = *Lines;
>> +    // Check if the line is empty, as line_iterator skips blank lines.
>> +    if (LineNo < Lines.line_number())
>> +      Line = "";
>> +    else if (!Lines.is_at_eof())
>> +      ++Lines;
>> +    renderLine(OS, Line, LineRanges);
>> +
>> +    // Show the region markers.
>> +    bool ShowMarkers = !Options.ShowLineStatsOrRegionMarkers ||
>> +                       LineStats[I].hasMultipleRegions();
>> +    auto LineMarkers = gatherLineItems(CurrentRegionMarker, Markers,
>> LineNo);
>> +    if (ShowMarkers && !LineMarkers.empty()) {
>> +      renderOffset(OS, Offset);
>> +      OS.indent(CombinedColumnWidth);
>> +      renderRegionMarkers(OS, LineMarkers);
>> +    }
>> +
>> +    // Show the line's expanded child subviews.
>> +    bool FirstChildExpansion = true;
>> +    if (LineSubViews.empty())
>> +      continue;
>> +    unsigned NewOffset = Offset + 1;
>> +    renderViewDivider(NewOffset, DividerWidth, OS);
>> +    OS << "\n";
>> +    for (const auto &Child : LineSubViews) {
>> +      // If this subview shows a function instantiation, render the
>> function's
>> +      // name.
>> +      if (Child->isInstantiationSubView()) {
>> +        renderOffset(OS, NewOffset);
>> +        OS << ' ';
>> +        Options.colored_ostream(OS, raw_ostream::CYAN) <<
>> Child->FunctionName
>> +                                                       << ":";
>> +        OS << "\n";
>> +      } else {
>> +        if (!FirstChildExpansion) {
>> +          // Re-render the current line and highlight the expansion
>> range for
>> +          // this
>> +          // subview.
>> +          insertHighlightRange(LineHighlightRanges,
>> +                               Child->getExpansionHighlightRange(),
>> +                               AdjustedLineHighlightRanges);
>> +          renderOffset(OS, Offset);
>> +          OS.indent(CombinedColumnWidth + (Offset == 0 ? 0 : 1));
>> +          renderLine(OS, Line, AdjustedLineHighlightRanges);
>> +          renderViewDivider(NewOffset, DividerWidth, OS);
>> +          OS << "\n";
>> +        } else
>> +          FirstChildExpansion = false;
>> +      }
>> +      // Render the child subview
>> +      Child->render(OS, NewOffset);
>> +      renderViewDivider(NewOffset, DividerWidth, OS);
>> +      OS << "\n";
>> +    }
>> +  }
>> +}
>> +
>> +void
>> +SourceCoverageView::createLineCoverageInfo(SourceCoverageDataManager
>> &Data) {
>> +  LineStats.resize(LineCount);
>> +  for (const auto &Region : Data.getSourceRegions()) {
>> +    auto Value = Region.second;
>> +    LineStats[Region.first.LineStart -
>> LineStart].addRegionStartCount(Value);
>> +    for (unsigned Line = Region.first.LineStart + 1;
>> +         Line <= Region.first.LineEnd; ++Line)
>> +      LineStats[Line - LineStart].addRegionCount(Value);
>> +  }
>> +
>> +  // Reset the line stats for skipped regions.
>> +  for (const auto &Region : Data.getSkippedRegions()) {
>> +    for (unsigned Line = Region.LineStart; Line <= Region.LineEnd;
>> ++Line)
>> +      LineStats[Line - LineStart] = LineCoverageInfo();
>> +  }
>> +}
>> +
>> +void
>> +SourceCoverageView::createHighlightRanges(SourceCoverageDataManager
>> &Data) {
>> +  auto Regions = Data.getSourceRegions();
>> +  std::vector<bool> AlreadyHighlighted;
>> +  AlreadyHighlighted.resize(Regions.size(), false);
>> +
>> +  for (size_t I = 0, S = Regions.size(); I < S; ++I) {
>> +    const auto &Region = Regions[I];
>> +    auto Value = Region.second;
>> +    auto SrcRange = Region.first;
>> +    if (Value != 0)
>> +      continue;
>> +    if (AlreadyHighlighted[I])
>> +      continue;
>> +    for (size_t J = 0; J < S; ++J) {
>> +      if (SrcRange.contains(Regions[J].first)) {
>> +        AlreadyHighlighted[J] = true;
>> +      }
>> +    }
>> +    if (SrcRange.LineStart == SrcRange.LineEnd) {
>> +      HighlightRanges.push_back(HighlightRange(
>> +          SrcRange.LineStart, SrcRange.ColumnStart, SrcRange.ColumnEnd));
>> +      continue;
>> +    }
>> +    HighlightRanges.push_back(
>> +        HighlightRange(SrcRange.LineStart, SrcRange.ColumnStart,
>> +                       std::numeric_limits<unsigned>::max()));
>> +    HighlightRanges.push_back(
>> +        HighlightRange(SrcRange.LineEnd, 1, SrcRange.ColumnEnd));
>> +    for (unsigned Line = SrcRange.LineStart + 1; Line < SrcRange.LineEnd;
>> +         ++Line) {
>> +      HighlightRanges.push_back(
>> +          HighlightRange(Line, 1, std::numeric_limits<unsigned>::max()));
>> +    }
>> +  }
>> +
>> +  std::sort(HighlightRanges.begin(), HighlightRanges.end());
>> +
>> +  if (Options.Debug) {
>> +    for (const auto &Range : HighlightRanges) {
>> +      outs() << "Highlighted line " << Range.Line << ", " <<
>> Range.ColumnStart
>> +             << " -> ";
>> +      if (Range.ColumnEnd == std::numeric_limits<unsigned>::max()) {
>> +        outs() << "?\n";
>> +      } else {
>> +        outs() << Range.ColumnEnd << "\n";
>> +      }
>> +    }
>> +  }
>> +}
>> +
>> +void SourceCoverageView::createRegionMarkers(SourceCoverageDataManager
>> &Data) {
>> +  for (const auto &Region : Data.getSourceRegions()) {
>> +    if (Region.first.LineStart >= LineStart)
>> +      Markers.push_back(RegionMarker(Region.first.LineStart,
>> +                                     Region.first.ColumnStart,
>> Region.second));
>> +  }
>> +
>> +  if (Options.Debug) {
>> +    for (const auto &Marker : Markers) {
>> +      outs() << "Marker at " << Marker.Line << ":" << Marker.Column << "
>> = "
>> +             << Marker.ExecutionCount << "\n";
>> +    }
>> +  }
>> +}
>> +
>> +void SourceCoverageView::load(SourceCoverageDataManager &Data) {
>> +  if (Options.ShowLineStats)
>> +    createLineCoverageInfo(Data);
>> +  if (Options.Colors)
>> +    createHighlightRanges(Data);
>> +  if (Options.ShowRegionMarkers)
>> +    createRegionMarkers(Data);
>> +}
>>
>> Added: llvm/trunk/tools/llvm-cov/SourceCoverageView.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/SourceCoverageView.h?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/SourceCoverageView.h (added)
>> +++ llvm/trunk/tools/llvm-cov/SourceCoverageView.h Fri Aug 22 17:56:03
>> 2014
>> @@ -0,0 +1,213 @@
>> +//===- SourceCoverageView.h - Code coverage view for source code
>> ----------===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// This class implements rendering for code coverage of source code.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#ifndef LLVM_COV_SOURCECOVERAGEVIEW_H
>> +#define LLVM_COV_SOURCECOVERAGEVIEW_H
>> +
>> +#include "CoverageViewOptions.h"
>> +#include "SourceCoverageDataManager.h"
>> +#include "llvm/ProfileData/CoverageMapping.h"
>> +#include "llvm/Support/MemoryBuffer.h"
>> +#include <vector>
>> +
>> +namespace llvm {
>> +
>> +/// \brief A code coverage view of a specific source file.
>> +/// It can have embedded coverage views.
>> +class SourceCoverageView {
>> +public:
>> +  enum SubViewKind { View, ExpansionView, InstantiationView };
>> +
>> +  /// \brief Coverage information for a single line.
>> +  struct LineCoverageInfo {
>> +    uint64_t ExecutionCount;
>> +    unsigned RegionCount;
>> +    bool Mapped;
>> +
>> +    LineCoverageInfo() : ExecutionCount(0), RegionCount(0),
>> Mapped(false) {}
>> +
>> +    bool isMapped() const { return Mapped; }
>> +
>> +    bool hasMultipleRegions() const { return RegionCount > 1; }
>> +
>> +    void addRegionStartCount(uint64_t Count) {
>> +      Mapped = true;
>> +      ExecutionCount = Count;
>> +      ++RegionCount;
>> +    }
>> +
>> +    void addRegionCount(uint64_t Count) {
>> +      Mapped = true;
>> +      ExecutionCount = Count;
>> +    }
>> +  };
>> +
>> +  /// \brief A marker that points at the start
>> +  /// of a specific mapping region.
>> +  struct RegionMarker {
>> +    unsigned Line, Column;
>> +    uint64_t ExecutionCount;
>> +
>> +    RegionMarker(unsigned Line, unsigned Column, uint64_t Value)
>> +        : Line(Line), Column(Column), ExecutionCount(Value) {}
>> +  };
>> +
>> +  /// \brief A single line source range used to
>> +  /// render highlighted text.
>> +  struct HighlightRange {
>> +    enum HighlightKind {
>> +      /// The code that wasn't executed.
>> +      NotCovered,
>> +
>> +      /// The region of code that was expanded.
>> +      Expanded
>> +    };
>> +    HighlightKind Kind;
>> +    unsigned Line;
>> +    unsigned ColumnStart;
>> +    unsigned ColumnEnd;
>> +
>> +    HighlightRange(unsigned Line, unsigned ColumnStart, unsigned
>> ColumnEnd,
>> +                   HighlightKind Kind = NotCovered)
>> +        : Kind(Kind), Line(Line), ColumnStart(ColumnStart),
>> +          ColumnEnd(ColumnEnd) {}
>> +
>> +    bool operator<(const HighlightRange &Other) const {
>> +      if (Line == Other.Line)
>> +        return ColumnStart < Other.ColumnStart;
>> +      return Line < Other.Line;
>> +    }
>> +
>> +    bool columnStartOverlaps(const HighlightRange &Other) const {
>> +      return ColumnStart <= Other.ColumnStart && ColumnEnd >
>> Other.ColumnStart;
>> +    }
>> +    bool columnEndOverlaps(const HighlightRange &Other) const {
>> +      return ColumnEnd >= Other.ColumnEnd && ColumnStart <
>> Other.ColumnEnd;
>> +    }
>> +    bool contains(const HighlightRange &Other) const {
>> +      if (Line != Other.Line)
>> +        return false;
>> +      return ColumnStart <= Other.ColumnStart && ColumnEnd >=
>> Other.ColumnEnd;
>> +    }
>> +
>> +    bool overlaps(const HighlightRange &Other) const {
>> +      if (Line != Other.Line)
>> +        return false;
>> +      return columnStartOverlaps(Other) || columnEndOverlaps(Other);
>> +    }
>> +  };
>> +
>> +private:
>> +  const MemoryBuffer &File;
>> +  const CoverageViewOptions &Options;
>> +  unsigned LineStart, LineCount;
>> +  SubViewKind Kind;
>> +  coverage::CounterMappingRegion ExpansionRegion;
>> +  std::vector<std::unique_ptr<SourceCoverageView>> Children;
>> +  std::vector<LineCoverageInfo> LineStats;
>> +  std::vector<HighlightRange> HighlightRanges;
>> +  std::vector<RegionMarker> Markers;
>> +  StringRef FunctionName;
>> +
>> +  /// \brief Create the line coverage information using the coverage
>> data.
>> +  void createLineCoverageInfo(SourceCoverageDataManager &Data);
>> +
>> +  /// \brief Create the line highlighting ranges using the coverage data.
>> +  void createHighlightRanges(SourceCoverageDataManager &Data);
>> +
>> +  /// \brief Create the region markers using the coverage data.
>> +  void createRegionMarkers(SourceCoverageDataManager &Data);
>> +
>> +  /// \brief Sort children by the starting location.
>> +  void sortChildren();
>> +
>> +  /// \brief Return a highlight range for the expansion region of this
>> view.
>> +  HighlightRange getExpansionHighlightRange() const;
>> +
>> +  /// \brief Render a source line with highlighting.
>> +  void renderLine(raw_ostream &OS, StringRef Line,
>> +                  ArrayRef<HighlightRange> Ranges);
>> +
>> +  void renderOffset(raw_ostream &OS, unsigned I);
>> +
>> +  void renderViewDivider(unsigned Offset, unsigned Length, raw_ostream
>> &OS);
>> +
>> +  /// \brief Render the line's execution count column.
>> +  void renderLineCoverageColumn(raw_ostream &OS, const LineCoverageInfo
>> &Line);
>> +
>> +  /// \brief Render the line number column.
>> +  void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo);
>> +
>> +  /// \brief Render all the region's execution counts on a line.
>> +  void renderRegionMarkers(raw_ostream &OS, ArrayRef<RegionMarker>
>> Regions);
>> +
>> +  static const unsigned LineCoverageColumnWidth = 7;
>> +  static const unsigned LineNumberColumnWidth = 5;
>> +
>> +public:
>> +  SourceCoverageView(const MemoryBuffer &File,
>> +                     const CoverageViewOptions &Options)
>> +      : File(File), Options(Options), LineStart(1), Kind(View),
>> +        ExpansionRegion(coverage::Counter(), 0, 0, 0, 0, 0) {
>> +    LineCount = File.getBuffer().count('\n') + 1;
>> +  }
>> +
>> +  SourceCoverageView(const MemoryBuffer &File,
>> +                     const CoverageViewOptions &Options, unsigned
>> LineStart,
>> +                     unsigned LineEnd)
>> +      : File(File), Options(Options), LineStart(LineStart),
>> +        LineCount(LineEnd - LineStart + 1), Kind(View),
>> +        ExpansionRegion(coverage::Counter(), 0, 0, 0, 0, 0) {}
>> +
>> +  SourceCoverageView(SourceCoverageView &Parent, unsigned LineStart,
>> +                     unsigned LineEnd, StringRef FunctionName)
>> +      : File(Parent.File), Options(Parent.Options), LineStart(LineStart),
>> +        LineCount(LineEnd - LineStart + 1), Kind(InstantiationView),
>> +        ExpansionRegion(coverage::Counter(), 0, LineEnd, 0, LineEnd, 0),
>> +        FunctionName(FunctionName) {}
>> +
>> +  SourceCoverageView(const MemoryBuffer &File,
>> +                     const CoverageViewOptions &Options, unsigned
>> LineStart,
>> +                     unsigned LineEnd,
>> +                     const coverage::CounterMappingRegion
>> &ExpansionRegion)
>> +      : File(File), Options(Options), LineStart(LineStart),
>> +        LineCount(LineEnd - LineStart + 1), Kind(ExpansionView),
>> +        ExpansionRegion(ExpansionRegion) {}
>> +
>> +  const CoverageViewOptions &getOptions() const { return Options; }
>> +
>> +  bool isExpansionSubView() const { return Kind == ExpansionView; }
>> +
>> +  bool isInstantiationSubView() const { return Kind ==
>> InstantiationView; }
>> +
>> +  /// \brief Return the line number after which the subview expansion is
>> shown.
>> +  unsigned getSubViewsExpansionLine() const {
>> +    return ExpansionRegion.LineStart;
>> +  }
>> +
>> +  void addChild(std::unique_ptr<SourceCoverageView> View) {
>> +    Children.push_back(std::move(View));
>> +  }
>> +
>> +  /// \brief Print the code coverage information for a specific
>> +  /// portion of a source file to the output stream.
>> +  void render(raw_ostream &OS, unsigned Offset = 0);
>> +
>> +  /// \brief Load the coverage information required for rendering
>> +  /// from the mapping regions in the data manager.
>> +  void load(SourceCoverageDataManager &Data);
>> +};
>> +
>> +} // namespace llvm
>> +
>> +#endif // LLVM_COV_SOURCECOVERAGEVIEW_H
>>
>> Added: llvm/trunk/tools/llvm-cov/TestingSupport.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/TestingSupport.cpp?rev=216300&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/TestingSupport.cpp (added)
>> +++ llvm/trunk/tools/llvm-cov/TestingSupport.cpp Fri Aug 22 17:56:03 2014
>> @@ -0,0 +1,92 @@
>> +//===- TestingSupport.cpp - Convert objects files into test files
>> --------===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#include "llvm/Object/ObjectFile.h"
>> +#include "llvm/Support/raw_ostream.h"
>> +#include "llvm/Support/LEB128.h"
>> +#include "llvm/Support/CommandLine.h"
>> +#include "llvm/Support/ManagedStatic.h"
>> +#include "llvm/Support/MemoryObject.h"
>> +#include "llvm/Support/Signals.h"
>> +#include "llvm/Support/PrettyStackTrace.h"
>> +#include <system_error>
>> +#include <functional>
>> +
>> +using namespace llvm;
>> +using namespace object;
>> +
>> +int convert_for_testing_main(int argc, const char **argv) {
>> +  sys::PrintStackTraceOnErrorSignal();
>> +  PrettyStackTraceProgram X(argc, argv);
>> +  llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
>> +
>> +  cl::opt<std::string> InputSourceFile(cl::Positional, cl::Required,
>> +                                       cl::desc("<Source file>"));
>> +
>> +  cl::opt<std::string> OutputFilename(
>> +      "o", cl::Required,
>> +      cl::desc(
>> +          "File with the profile data obtained after an instrumented
>> run"));
>> +
>> +  cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
>> +
>> +  auto ObjErr =
>> llvm::object::ObjectFile::createObjectFile(InputSourceFile);
>> +  if (auto Err = ObjErr.getError()) {
>> +    errs() << "error: " << Err.message() << "\n";
>> +    return 1;
>> +  }
>> +  ObjectFile *OF = ObjErr.get().getBinary().get();
>> +  auto BytesInAddress = OF->getBytesInAddress();
>> +  if (BytesInAddress != 8) {
>> +    errs() << "error: 64 bit binary expected\n";
>> +    return 1;
>> +  }
>> +
>> +  // Look for the sections that we are interested in.
>> +  int FoundSectionCount = 0;
>> +  SectionRef ProfileNames, CoverageMapping;
>> +  for (const auto &Section : OF->sections()) {
>> +    StringRef Name;
>> +    if (Section.getName(Name))
>> +      return 1;
>> +    if (Name == "__llvm_prf_names") {
>> +      ProfileNames = Section;
>> +    } else if (Name == "__llvm_covmap") {
>> +      CoverageMapping = Section;
>> +    } else
>> +      continue;
>> +    ++FoundSectionCount;
>> +  }
>> +  if (FoundSectionCount != 2)
>> +    return 1;
>> +
>> +  // Get the contents of the given sections.
>> +  StringRef CoverageMappingData;
>> +  uint64_t ProfileNamesAddress;
>> +  StringRef ProfileNamesData;
>> +  if (CoverageMapping.getContents(CoverageMappingData) ||
>> +      ProfileNames.getAddress(ProfileNamesAddress) ||
>> +      ProfileNames.getContents(ProfileNamesData))
>> +    return 1;
>> +
>> +  int FD;
>> +  if (auto Err =
>> +          sys::fs::openFileForWrite(OutputFilename, FD,
>> sys::fs::F_None)) {
>> +    errs() << "error: " << Err.message() << "\n";
>> +    return 1;
>> +  }
>> +
>> +  raw_fd_ostream OS(FD, true);
>> +  OS << "llvmcovmtestdata";
>> +  encodeULEB128(ProfileNamesData.size(), OS);
>> +  encodeULEB128(ProfileNamesAddress, OS);
>> +  OS << ProfileNamesData << CoverageMappingData;
>> +
>> +  return 0;
>> +}
>>
>> Modified: llvm/trunk/tools/llvm-cov/llvm-cov.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cov/llvm-cov.cpp?rev=216300&r1=216299&r2=216300&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-cov/llvm-cov.cpp (original)
>> +++ llvm/trunk/tools/llvm-cov/llvm-cov.cpp Fri Aug 22 17:56:03 2014
>> @@ -11,9 +11,63 @@
>>  //
>>
>>  //===----------------------------------------------------------------------===//
>>
>> -/// \brief The main function for the gcov compatible coverage tool
>> +#include "llvm/ADT/StringRef.h"
>> +#include "llvm/Support/raw_ostream.h"
>> +#include "llvm/Support/Path.h"
>> +#include <string>
>> +
>> +using namespace llvm;
>> +
>> +/// \brief The main entry point for the 'show' subcommand.
>> +int show_main(int argc, const char **argv);
>> +
>> +/// \brief The main entry point for the 'report' subcommand.
>> +int report_main(int argc, const char **argv);
>> +
>> +/// \brief The main entry point for the 'convert-for-testing' subcommand.
>> +int convert_for_testing_main(int argc, const char **argv);
>> +
>> +/// \brief The main entry point for the gcov compatible coverage tool.
>>  int gcov_main(int argc, const char **argv);
>>
>>  int main(int argc, const char **argv) {
>> +  // If argv[0] is or ends with 'gcov', always be gcov compatible
>> +  if (sys::path::stem(argv[0]).endswith_lower("gcov"))
>> +    return gcov_main(argc, argv);
>> +
>> +  // Check if we are invoking a specific tool command.
>> +  if (argc > 1) {
>> +    int (*func)(int, const char **) = nullptr;
>> +
>> +    StringRef command = argv[1];
>> +    if (command.equals_lower("show"))
>> +      func = show_main;
>> +    else if (command.equals_lower("report"))
>> +      func = report_main;
>> +    else if (command.equals_lower("convert-for-testing"))
>> +      func = convert_for_testing_main;
>> +    else if (command.equals_lower("gcov"))
>> +      func = gcov_main;
>> +
>> +    if (func) {
>> +      std::string Invocation(std::string(argv[0]) + " " + argv[1]);
>> +      argv[1] = Invocation.c_str();
>> +      return func(argc - 1, argv + 1);
>> +    }
>> +  }
>> +
>> +  // Give a warning and fall back to gcov
>> +  errs().changeColor(raw_ostream::RED);
>> +  errs() << "warning:";
>> +  // Assume that argv[1] wasn't a command when it stats with a '-' or is
>> a
>> +  // filename (i.e. contains a '.')
>> +  if (argc > 1 && !StringRef(argv[1]).startswith("-") &&
>> +      StringRef(argv[1]).find(".") == StringRef::npos)
>> +    errs() << " Unrecognized command '" << argv[1] << "'.";
>> +  errs() << " Using the gcov compatible mode "
>> +            "(this behaviour may be dropped in the future).";
>> +  errs().resetColor();
>> +  errs() << "\n";
>> +
>>    return gcov_main(argc, argv);
>>  }
>>
>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>>
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20140822/95b7c087/attachment.html>


More information about the llvm-commits mailing list