<div dir="ltr"><div>Hey Alex,</div><div><br></div><div>Overall this looks pretty good. I have quite a few nitpicky style</div><div>comments and one or two bigger things below.</div><div><br></div><div>-- Justin</div><div><br>
</div><div>Alex Lorenz <<a href="mailto:arphaman@gmail.com">arphaman@gmail.com</a>> writes:</div><div>> Index: tools/llvm-cov/CMakeLists.txt</div><div>> ===================================================================</div>
<div>> --- tools/llvm-cov/CMakeLists.txt</div><div>> +++ tools/llvm-cov/CMakeLists.txt</div><div>> @@ -1,6 +1,14 @@</div><div>> -set(LLVM_LINK_COMPONENTS core support )</div><div>> +set(LLVM_LINK_COMPONENTS core support object)</div>
<div>></div><div>> add_llvm_tool(llvm-cov</div><div>> llvm-cov.cpp</div><div>> gcov.cpp</div><div>> + CodeCoverage.cpp</div><div>> + CoverageFilters.cpp</div><div>> + CoverageReport.cpp</div>
<div>> + CoverageSummary.cpp</div><div>> + CoverageSummaryInfo.cpp</div><div>> + SourceCoverageDataManager.cpp</div><div>> + SourceCoverageView.cpp</div><div>> + SourceLanguages.cpp</div><div>> )</div>
<div>> Index: tools/llvm-cov/CodeCoverage.cpp</div><div>> ===================================================================</div><div>> --- /dev/null</div><div>> +++ tools/llvm-cov/CodeCoverage.cpp</div><div>
> @@ -0,0 +1,707 @@</div><div>> +//===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===//</div><div>> +//</div><div>> +// The LLVM Compiler Infrastructure</div><div>
> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +//</div><div>> +// This class implements a command line tool to analyze and report coverage</div><div>> +// information using the profiling instrumentation and code coverage mapping.</div><div><br></div><div>
What is "This class"? This is a file. Maybe mention CodeCoverageTool</div><div>here.</div><div><br></div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +</div><div>> +#include "FunctionCoverageMapping.h"</div><div>> +#include "CoverageViewOptions.h"</div><div>> +#include "CoverageFilters.h"</div><div>> +#include "SourceLanguages.h"</div>
<div>> +#include "SourceCoverageDataManager.h"</div><div>> +#include "SourceCoverageView.h"</div><div>> +#include "CoverageSummary.h"</div><div>> +#include "CoverageReport.h"</div>
<div>> +#include "llvm/ADT/StringRef.h"</div><div>> +#include "llvm/ADT/SmallString.h"</div><div>> +#include "llvm/ADT/SmallSet.h"</div><div>> +#include "llvm/ADT/DenseSet.h"</div>
<div>> +#include "llvm/ProfileData/InstrProfReader.h"</div><div>> +#include "llvm/ProfileData/CoverageMapping.h"</div><div>> +#include "llvm/ProfileData/CoverageMappingReader.h"</div>
<div>> +#include "llvm/Support/CommandLine.h"</div><div>> +#include "llvm/Support/FileSystem.h"</div><div>> +#include "llvm/Support/ManagedStatic.h"</div><div>> +#include "llvm/Support/MemoryObject.h"</div>
<div>> +#include "llvm/Support/Format.h"</div><div>> +#include "llvm/Support/Path.h"</div><div>> +#include "llvm/Support/Signals.h"</div><div>> +#include "llvm/Support/PrettyStackTrace.h"</div>
<div>> +#include <system_error></div><div>> +#include <functional></div><div>> +</div><div>> +using namespace llvm;</div><div>> +using namespace coverage;</div><div>> +</div><div>> +namespace {</div>
<div>> +/// \brief Distribute the functions into instantiation sets.</div><div>> +/// An instantiation set is a collection of functions</div><div>> +/// that have the same source code, e.g.</div><div>> +/// template functions specializations.</div>
<div>> +class FunctionInstantiationSetCollector {</div><div>> + ArrayRef<FunctionCoverageMapping> FunctionMappings;</div><div>> + typedef uint64_t KeyType;</div><div>> + typedef std::vector<const FunctionCoverageMapping *> SetType;</div>
<div>> + std::unordered_map<uint64_t, SetType> InstantiatedFunctions;</div><div>> +</div><div>> + static KeyType getKey(const MappingRegion &R) {</div><div>> + return uint64_t(R.LineStart) | uint64_t(R.ColumnStart) << 32;</div>
<div>> + }</div><div>> +</div><div>> +public:</div><div>> + void insert(const FunctionCoverageMapping &Function, unsigned FileID) {</div><div>> + KeyType Key = 0;</div><div>> + for (const auto &R : Function.MappingRegions) {</div>
<div>> + if (R.FileID == FileID) {</div><div>> + Key = getKey(R);</div><div>> + break;</div><div>> + }</div><div>> + }</div><div>> + auto I = InstantiatedFunctions.find(Key);</div>
<div>> + if (I == InstantiatedFunctions.end()) {</div><div>> + SetType Set;</div><div>> + Set.push_back(&Function);</div><div>> + InstantiatedFunctions.insert(std::make_pair(Key, Set));</div>
<div>> + } else</div><div>> + I->second.push_back(&Function);</div><div>> + }</div><div>> +</div><div>> + std::unordered_map<KeyType, SetType>::iterator begin() {</div><div>> + return InstantiatedFunctions.begin();</div>
<div>> + }</div><div>> +</div><div>> + std::unordered_map<KeyType, SetType>::iterator end() {</div><div>> + return InstantiatedFunctions.end();</div><div>> + }</div><div>> +};</div><div>> +</div>
<div>> +/// \brief The implementation of the coverage tool.</div><div>> +class CodeCoverageTool {</div><div>> +public:</div><div>> + enum Command {</div><div>> + /// \brief The show command.</div><div>> + Show,</div>
<div>> + /// \brief The report command.</div><div>> + Report</div><div>> + };</div><div>> +</div><div>> + /// \brief Print the error message to the error output stream.</div><div>> + void error(const Twine &Message, StringRef Whence = "");</div>
<div>> +</div><div>> + /// \brief Return a memory buffer for the given source file.</div><div>> + ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);</div><div>> +</div><div>> + /// \brief Collect a set of function's file ids which correspond to the</div>
<div>> + /// given source file. Return false if the set is empty.</div><div>> + bool gatherInterestingFileIDs(StringRef SourceFile,</div><div>> + const FunctionCoverageMapping &Function,</div>
<div>> + SmallSet<unsigned, 8> &InterestingFileIDs);</div><div>> +</div><div>> + /// \brief Find the file id which is not an expanded file id.</div><div>> + bool findMainViewFileID(StringRef SourceFile,</div>
<div>> + const FunctionCoverageMapping &Function,</div><div>> + unsigned &MainViewFileID);</div><div>> +</div><div>> + bool findMainViewFileID(const FunctionCoverageMapping &Function,</div>
<div>> + unsigned &MainViewFileID);</div><div>> +</div><div>> + /// \brief Create a source view which shows coverage for an expansion</div><div>> + /// of a file.</div><div>> + void createExpansionSubView(const MappingRegion &ExpandedRegion,</div>
<div>> + const FunctionCoverageMapping &Function,</div><div>> + SourceCoverageView &Parent);</div><div>> +</div><div>> + void createExpansionSubViews(SourceCoverageView &View, unsigned ViewFileID,</div>
<div>> + const FunctionCoverageMapping &Function);</div><div>> +</div><div>> + /// \brief Create a source view which shows coverage for an instantiation</div><div>> + /// of a funciton.</div>
<div>> + void createInstantiationSubView(StringRef SourceFile,</div><div>> + const FunctionCoverageMapping &Function,</div><div>> + SourceCoverageView &View);</div>
<div>> +</div><div>> + /// \brief Create the main source view of a particular source file.</div><div>> + /// Return true if this particular source file is not covered.</div><div>> + bool</div><div>> + createSourceFileView(StringRef SourceFile, SourceCoverageView &View,</div>
<div>> + ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,</div><div>> + bool UseOnlyRegionsInMainFile = false);</div><div>> +</div><div>> + /// \brief Return true if an error occured during loading</div>
<div>> + /// of the coverage mapping data.</div><div>> + bool load();</div><div><br></div><div>This wording doesn't make it clear that this, you know, loads the</div><div>coverage mapping data. :)</div><div><br>
</div><div>> +</div><div>> + int run(Command Cmd, int argc, const char **argv);</div><div>> +</div><div>> + typedef std::function<int(int, const char **)> CommandLineParserType;</div><div>> +</div><div>
> + int show(int argc, const char **argv,</div><div>> + CommandLineParserType commandLineParser);</div><div>> +</div><div>> + int report(int argc, const char **argv,</div><div>> + CommandLineParserType commandLineParser);</div>
<div>> +</div><div>> + StringRef ObjectFilename;</div><div>> + CoverageViewOptions ViewOpts;</div><div>> + std::unique_ptr<IndexedInstrProfReader> PGOReader;</div><div>> + CoverageFiltersMatchAll Filters;</div>
<div>> + SourceLanguages Languages;</div><div>> + std::vector<std::string> SourceFiles;</div><div>> + std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>></div><div>> + LoadedSourceFiles;</div>
<div>> + std::vector<FunctionCoverageMapping> FunctionMappingRecords;</div><div>> +};</div><div>> +}</div><div>> +</div><div>> +void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {</div>
<div>> + errs() << "error: ";</div><div>> + if (!Whence.empty())</div><div>> + errs() << Whence << ": ";</div><div>> + errs() << Message << "\n";</div>
<div>> +}</div><div>> +</div><div>> +ErrorOr<const MemoryBuffer &></div><div>> +CodeCoverageTool::getSourceFile(StringRef SourceFile) {</div><div>> + SmallString<256> Path(SourceFile);</div>
<div>> + sys::fs::make_absolute(Path);</div><div>> + for (const auto &Files : LoadedSourceFiles) {</div><div>> + if (sys::fs::equivalent(Path.str(), Files.first)) {</div><div>> + return *Files.second;</div>
<div>> + }</div><div>> + }</div><div>> + auto Buffer = MemoryBuffer::getFile(SourceFile);</div><div>> + if (auto EC = Buffer.getError()) {</div><div>> + error(EC.message(), SourceFile);</div><div>> + return EC;</div>
<div>> + }</div><div>> + LoadedSourceFiles.push_back(std::make_pair(</div><div>> + std::string(Path.begin(), Path.end()), std::move(Buffer.get())));</div><div>> + return *LoadedSourceFiles.back().second;</div>
<div>> +}</div><div>> +</div><div>> +/// \brief Return a line start - line end range which contains</div><div>> +/// all the mapping regions of a given function with a particular file id.</div><div>> +std::pair<unsigned, unsigned></div>
<div>> +findExpandedFileInterestingLineRange(unsigned FileID,</div><div>> + const FunctionCoverageMapping &Function) {</div><div>> + unsigned LineStart = std::numeric_limits<unsigned>::max();</div>
<div>> + unsigned LineEnd = 0;</div><div>> + for (const auto &I : Function.MappingRegions) {</div><div><br></div><div>I isn't a great name. Better to use something descriptive like Region.</div><div><br></div>
<div>> + if (I.FileID != FileID)</div><div>> + continue;</div><div>> + LineStart = std::min(I.LineStart, LineStart);</div><div>> + LineEnd = std::max(I.LineEnd, LineEnd);</div><div>> + }</div>
<div>> + return std::make_pair(LineStart, LineEnd);</div><div>> +}</div><div>> +</div><div>> +bool CodeCoverageTool::gatherInterestingFileIDs(</div><div>> + StringRef SourceFile, const FunctionCoverageMapping &Function,</div>
<div>> + SmallSet<unsigned, 8> &InterestingFileIDs) {</div><div>> + bool Interesting = false;</div><div>> + for (unsigned I = 0, S = Function.Filenames.size(); I < S; ++I) {</div><div><br></div>
<div>We usually use E (for end) in these loops. You use S (for size?) a few</div><div>times in this patch. Please change them to E to match the usual LLVM</div><div>convention.</div><div><br></div><div>> + if (llvm::sys::fs::equivalent(SourceFile, Function.Filenames[I])) {</div>
<div>> + InterestingFileIDs.insert(I);</div><div>> + Interesting = true;</div><div>> + }</div><div>> + }</div><div>> + return Interesting;</div><div>> +}</div><div>> +</div><div>> +bool</div>
<div>> +CodeCoverageTool::findMainViewFileID(StringRef SourceFile,</div><div>> + const FunctionCoverageMapping &Function,</div><div>> + unsigned &MainViewFileID) {</div>
<div>> + llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false);</div><div>> + llvm::SmallVector<bool, 8> FilenameEquivalence(Function.Filenames.size(),</div><div>> + false);</div>
<div>> + for (unsigned I = 0, S = Function.Filenames.size(); I < S; ++I) {</div><div>> + if (llvm::sys::fs::equivalent(SourceFile, Function.Filenames[I]))</div><div>> + FilenameEquivalence[I] = true;</div>
<div>> + }</div><div>> + for (const auto &R : Function.MappingRegions) {</div><div>> + if (R.Kind == MappingRegion::ExpansionRegion &&</div><div>> + FilenameEquivalence[R.FileID])</div>
<div>> + IsExpandedFile[R.ExpandedFileID] = true;</div><div>> + }</div><div>> + for (unsigned I = 0; I < Function.Filenames.size(); ++I) {</div><div><br></div><div>Please change this to "I = 0, E = size(); I < E; ..." like the above.</div>
<div><br></div><div>> + if (!FilenameEquivalence[I] || IsExpandedFile[I])</div><div>> + continue;</div><div>> + MainViewFileID = I;</div><div>> + return false;</div><div>> + }</div><div>> + return true;</div>
<div>> +}</div><div>> +</div><div>> +bool</div><div>> +CodeCoverageTool::findMainViewFileID(const FunctionCoverageMapping &Function,</div><div>> + unsigned &MainViewFileID) {</div>
<div>> + llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false);</div><div>> + for (const auto &R : Function.MappingRegions) {</div><div>> + if (R.Kind == MappingRegion::ExpansionRegion)</div>
<div>> + IsExpandedFile[R.ExpandedFileID] = true;</div><div>> + }</div><div>> + for (unsigned I = 0; I < Function.Filenames.size(); ++I) {</div><div><br></div><div>Ditto.</div><div><br></div><div>> + if (IsExpandedFile[I])</div>
<div>> + continue;</div><div>> + MainViewFileID = I;</div><div>> + return false;</div><div>> + }</div><div>> + return true;</div><div>> +}</div><div>> +</div><div>> +void CodeCoverageTool::createExpansionSubView(</div>
<div>> + const MappingRegion &ExpandedRegion,</div><div>> + const FunctionCoverageMapping &Function, SourceCoverageView &Parent) {</div><div>> + auto ExpandedLines = findExpandedFileInterestingLineRange(</div>
<div>> + ExpandedRegion.ExpandedFileID, Function);</div><div>> + if (ViewOpts.Debug)</div><div>> + llvm::outs() << "Expansion of " << ExpandedRegion.ExpandedFileID << ":"</div>
<div>> + << ExpandedLines.first << " -> " << ExpandedLines.second</div><div>> + << " @ " << ExpandedRegion.FileID << ", "</div>
<div>> + << ExpandedRegion.LineStart << ":"</div><div>> + << ExpandedRegion.ColumnStart << "\n";</div><div>> + auto SourceBuffer =</div>
<div>> + getSourceFile(Function.Filenames[ExpandedRegion.ExpandedFileID]);</div><div>> + if (!SourceBuffer)</div><div>> + return;</div><div>> + auto SubView = new SourceCoverageView(SourceBuffer.get(), Parent.getOptions(),</div>
<div>> + ExpandedLines.first,</div><div>> + ExpandedLines.second, ExpandedRegion);</div><div><br></div><div>I see later Parent.addChild wraps this in a unique pointer - better to</div>
<div>pass a unique pointer in so that it's obvious who owns this.</div><div><br></div><div>> + Parent.addChild(SubView);</div><div>> + SourceCoverageDataManager RegionManager;</div><div>> + for (const auto &I : Function.MappingRegions) {</div>
<div>> + if (I.FileID == ExpandedRegion.ExpandedFileID)</div><div>> + RegionManager.insert(I);</div><div>> + }</div><div>> + SubView->load(RegionManager);</div><div>> + createExpansionSubViews(*SubView, ExpandedRegion.ExpandedFileID, Function);</div>
<div>> +}</div><div>> +</div><div>> +void CodeCoverageTool::createExpansionSubViews(</div><div>> + SourceCoverageView &View, unsigned ViewFileID,</div><div>> + const FunctionCoverageMapping &Function) {</div>
<div>> + if (!ViewOpts.ShowExpandedRegions)</div><div>> + return;</div><div>> + for (const auto &Region : Function.MappingRegions) {</div><div>> + if (Region.Kind != CounterMappingRegion::ExpansionRegion)</div>
<div>> + continue;</div><div>> + if (Region.FileID != ViewFileID)</div><div>> + continue;</div><div>> + createExpansionSubView(Region, Function, View);</div><div>> + }</div><div>> +}</div>
<div>> +</div><div>> +void CodeCoverageTool::createInstantiationSubView(</div><div>> + StringRef SourceFile, const FunctionCoverageMapping &Function,</div><div>> + SourceCoverageView &View) {</div>
<div>> + SourceCoverageDataManager RegionManager;</div><div>> + SmallSet<unsigned, 8> InterestingFileIDs;</div><div>> + if (!gatherInterestingFileIDs(SourceFile, Function, InterestingFileIDs))</div><div>
> + return;</div><div>> + // Get the interesting regions</div><div>> + for (const auto &I : Function.MappingRegions) {</div><div>> + if (InterestingFileIDs.count(I.FileID))</div><div>> + RegionManager.insert(I);</div>
<div>> + }</div><div>> + View.load(RegionManager);</div><div>> + unsigned MainFileID;</div><div>> + if (findMainViewFileID(SourceFile, Function, MainFileID))</div><div>> + return;</div><div>> + createExpansionSubViews(View, MainFileID, Function);</div>
<div>> +}</div><div>> +</div><div>> +bool CodeCoverageTool::createSourceFileView(</div><div>> + StringRef SourceFile, SourceCoverageView &View,</div><div>> + ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,</div>
<div>> + bool UseOnlyRegionsInMainFile) {</div><div>> + SourceCoverageDataManager RegionManager;</div><div>> + FunctionInstantiationSetCollector InstantiationSetCollector;</div><div>> +</div><div>> + for (const auto &Function : FunctionMappingRecords) {</div>
<div>> + unsigned MainFileID;</div><div>> + if (findMainViewFileID(SourceFile, Function, MainFileID))</div><div>> + continue;</div><div>> + SmallSet<unsigned, 8> InterestingFileIDs;</div><div>
> + if (UseOnlyRegionsInMainFile) {</div><div>> + InterestingFileIDs.insert(MainFileID);</div><div>> + } else {</div><div>> + if (!gatherInterestingFileIDs(SourceFile, Function, InterestingFileIDs))</div>
<div>> + continue;</div><div>> + }</div><div><br></div><div>Isn't this just "else if"?</div><div><br></div><div>> + // Get the interesting regions</div><div>> + for (const auto &Region : Function.MappingRegions) {</div>
<div>> + if (InterestingFileIDs.count(Region.FileID))</div><div>> + RegionManager.insert(Region);</div><div>> + }</div><div>> + InstantiationSetCollector.insert(Function, MainFileID);</div><div>
> + createExpansionSubViews(View, MainFileID, Function);</div><div>> + }</div><div>> + if (RegionManager.getSourceRegions().empty())</div><div>> + return true;</div><div>> + View.load(RegionManager);</div>
<div>> + // Show instantiations</div><div>> + if (!ViewOpts.ShowFunctionInstantiations)</div><div>> + return false;</div><div>> + for (const auto &InstantiationSet : InstantiationSetCollector) {</div>
<div>> + if (InstantiationSet.second.size() < 2)</div><div>> + continue;</div><div>> + auto InterestingRange = findExpandedFileInterestingLineRange(</div><div>> + InstantiationSet.second.front()->MappingRegions.front().FileID,</div>
<div>> + *InstantiationSet.second.front());</div><div>> + for (auto Function : InstantiationSet.second) {</div><div>> + auto SubView =</div><div>> + new SourceCoverageView(View, InterestingRange.first,</div>
<div>> + InterestingRange.second, Function->PrettyName);</div><div>> + View.addChild(SubView);</div><div><br></div><div>> + createInstantiationSubView(SourceFile, *Function, *SubView);</div>
<div>> + }</div><div>> + }</div><div>> + return false;</div><div>> +}</div><div>> +</div><div>> +bool CodeCoverageTool::load() {</div><div>> + auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename);</div>
<div>> + if (auto EC = CounterMappingBuff.getError()) {</div><div>> + error(EC.message(), ObjectFilename);</div><div>> + return true;</div><div>> + }</div><div>> + ObjectFileCoverageMappingReader MappingReader(CounterMappingBuff.get());</div>
<div>> + if (auto EC = MappingReader.readHeader()) {</div><div>> + error(EC.message(), ObjectFilename);</div><div>> + return true;</div><div>> + }</div><div>> +</div><div>> + std::vector<uint64_t> Counts;</div>
<div>> + for (const auto &I : MappingReader) {</div><div>> + FunctionCoverageMapping Function(I.FunctionName, I.Filenames);</div><div>> +</div><div>> + // Demangle</div><div>> + Languages.get(Function)->demangle(Function.PrettyName);</div>
<div>> +</div><div>> + // Create the mapping regions with evaluated execution counts</div><div>> + Counts.clear();</div><div>> + uint64_t FuncHash;</div><div>> + PGOReader->getFunctionCounts(Function.Name, FuncHash, Counts);</div>
<div>> +</div><div>> + // Get the biggest referenced counters</div><div>> + bool RegionError = false;</div><div>> + CounterMappingContext Ctx(I.Expressions, Counts);</div><div>> + for (const auto &R : I.MappingRegions) {</div>
<div>> + // Compute the values of mapped regions</div><div>> + if (ViewOpts.Debug) {</div><div>> + outs() << "File " << R.FileID << "| " << R.LineStart << ":"</div>
<div>> + << R.ColumnStart << " -> " << R.LineEnd << ":" << R.ColumnEnd</div><div>> + << " = ";</div><div>> + Ctx.dump(R.Count);</div>
<div>> + if (R.Kind == CounterMappingRegion::ExpansionRegion) {</div><div>> + outs() << " (Expanded file id = " << R.ExpandedFileID << ") ";</div><div>> + }</div>
<div>> + outs() << "\n";</div><div>> + }</div><div>> + std::error_code Error;</div><div>> + Function.MappingRegions.push_back(</div><div>> + MappingRegion(R, Ctx.evaluate(R.Count, Error)));</div>
<div>> + if (Error && !RegionError) {</div><div>> + errs().changeColor(raw_ostream::RED);</div><div>> + errs() << "error: Regions and counters don't match in a function '"</div>
<div>> + << Function.PrettyName << "' (re-run the instrumented binary).";</div><div>> + errs().resetColor();</div><div>> + errs() << "\n";</div>
<div>> + RegionError = true;</div><div>> + }</div><div>> + }</div><div>> +</div><div>> + if (RegionError || !Filters.matches(Function))</div><div>> + continue;</div><div>> +</div>
<div>> + FunctionMappingRecords.push_back(Function);</div><div>> + }</div><div>> + return false;</div><div>> +}</div><div>> +</div><div>> +int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {</div>
<div>> + // Print a stack trace if we signal out.</div><div>> + sys::PrintStackTraceOnErrorSignal();</div><div>> + PrettyStackTraceProgram X(argc, argv);</div><div>> + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.</div>
<div>> +</div><div>> + cl::list<std::string> InputSourceFiles(</div><div>> + cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);</div><div>> +</div><div>> + cl::opt<std::string> PGOFilename(</div>
<div>> + "instr-profile", cl::Required,</div><div>> + cl::desc(</div><div>> + "File with the profile data obtained after an instrumented run"));</div><div>> +</div><div>> + cl::opt<bool> DebugDump("dump", cl::Optional,</div>
<div>> + cl::desc("Show internal debug dump"));</div><div>> +</div><div>> + cl::OptionCategory FilteringCategory("Function filtering options");</div><div>> +</div>
<div>> + cl::list<std::string> NameFilters(</div><div>> + "name", cl::Optional,</div><div>> + cl::desc("Show code coverage only for functions with the given name"),</div><div>
> + cl::ZeroOrMore, cl::cat(FilteringCategory));</div><div>> +</div><div>> + cl::list<std::string> NameRegexFilters(</div><div>> + "name-regex", cl::Optional,</div><div>> + cl::desc("Show code coverage only for functions that match the given "</div>
<div>> + "regular expression"),</div><div>> + cl::ZeroOrMore, cl::cat(FilteringCategory));</div><div>> +</div><div>> + cl::opt<double> RegionCoverageLtFilter(</div><div>> + "region-coverage-lt", cl::Optional,</div>
<div>> + cl::desc("Show code coverage only for functions with region coverage "</div><div>> + "less than the given threshold"),</div><div>> + cl::cat(FilteringCategory));</div>
<div>> +</div><div>> + cl::opt<double> RegionCoverageGtFilter(</div><div>> + "region-coverage-gt", cl::Optional,</div><div>> + cl::desc("Show code coverage only for functions with region coverage "</div>
<div>> + "greater than the given threshold"),</div><div>> + cl::cat(FilteringCategory));</div><div>> +</div><div>> + cl::opt<double> LineCoverageLtFilter(</div><div>> + "line-coverage-lt", cl::Optional,</div>
<div>> + cl::desc("Show code coverage only for functions with line coverage less "</div><div>> + "than the given threshold"),</div><div>> + cl::cat(FilteringCategory));</div>
<div>> +</div><div>> + cl::opt<double> LineCoverageGtFilter(</div><div>> + "line-coverage-gt", cl::Optional,</div><div>> + cl::desc("Show code coverage only for functions with line coverage "</div>
<div>> + "greater than the given threshold"),</div><div>> + cl::cat(FilteringCategory));</div><div>> +</div><div>> + auto commandLineParser = [&, this](int argc, const char **argv) -> int {</div>
<div>> + cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");</div><div>> + ViewOpts.Debug = DebugDump;</div><div>> +</div><div>> + if (auto EC = IndexedInstrProfReader::create(PGOFilename, PGOReader)) {</div>
<div>> + error(EC.message(), PGOFilename);</div><div>> + return 1;</div><div>> + }</div><div>> +</div><div>> + // Create the function filters</div><div>> + if (!NameFilters.empty() || !NameRegexFilters.empty()) {</div>
<div>> + auto NameFilterer = new CoverageFilters;</div><div>> + for (const auto &Name : NameFilters)</div><div>> + NameFilterer->push_back(new NameCoverageFilter(Name));</div><div>> + for (const auto &Regex : NameRegexFilters)</div>
<div>> + NameFilterer->push_back(new NameRegexCoverageFilter(Regex));</div><div>> + Filters.push_back(NameFilterer);</div><div>> + }</div><div>> + if (RegionCoverageLtFilter.getNumOccurrences() ||</div>
<div>> + RegionCoverageGtFilter.getNumOccurrences() ||</div><div>> + LineCoverageLtFilter.getNumOccurrences() ||</div><div>> + LineCoverageGtFilter.getNumOccurrences()) {</div><div>> + auto StatFilterer = new CoverageFilters;</div>
<div>> + if (RegionCoverageLtFilter.getNumOccurrences())</div><div>> + StatFilterer->push_back(new RegionCoverageFilter(</div><div>> + RegionCoverageFilter::LessThan, RegionCoverageLtFilter));</div>
<div>> + if (RegionCoverageGtFilter.getNumOccurrences())</div><div>> + StatFilterer->push_back(new RegionCoverageFilter(</div><div>> + RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));</div>
<div>> + if (LineCoverageLtFilter.getNumOccurrences())</div><div>> + StatFilterer->push_back(new LineCoverageFilter(</div><div>> + LineCoverageFilter::LessThan, LineCoverageLtFilter));</div>
<div>> + if (LineCoverageGtFilter.getNumOccurrences())</div><div>> + StatFilterer->push_back(new LineCoverageFilter(</div><div>> + RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));</div>
<div>> + Filters.push_back(StatFilterer);</div><div>> + }</div><div>> +</div><div>> + SourceFiles = InputSourceFiles;</div><div>> + return 0;</div><div>> + };</div><div>> +</div><div>> + // Parse the object filename</div>
<div>> + if (argc > 1) {</div><div>> + StringRef Arg(argv[1]);</div><div>> + if (Arg.equals_lower("-help") || Arg.equals_lower("-version")) {</div><div>> + cl::ParseCommandLineOptions(2, argv, "LLVM code coverage tool\n");</div>
<div>> + return 0;</div><div>> + }</div><div>> + ObjectFilename = Arg;</div><div>> +</div><div>> + argv[1] = argv[0];</div><div>> + --argc;</div><div>> + ++argv;</div><div>> + } else {</div>
<div>> + errs() << sys::path::filename(argv[0]) << ": No executable file given!\n";</div><div>> + return 1;</div><div>> + }</div><div>> +</div><div>> + switch (Cmd) {</div><div>
> + case Show:</div><div>> + return show(argc, argv, commandLineParser);</div><div>> + case Report:</div><div>> + return report(argc, argv, commandLineParser);</div><div>> + }</div><div>> + return 0;</div>
<div>> +}</div><div>> +</div><div>> +int CodeCoverageTool::show(int argc, const char **argv,</div><div>> + CommandLineParserType commandLineParser) {</div><div>> +</div><div>> + cl::OptionCategory ViewCategory("Viewing options");</div>
<div>> +</div><div>> + cl::opt<bool> ShowLineExecutionCounts(</div><div>> + "show-line-counts", cl::Optional,</div><div>> + cl::desc("Show the execution counts for each line"), cl::init(true),</div>
<div>> + cl::cat(ViewCategory));</div><div>> +</div><div>> + cl::opt<bool> ShowRegions(</div><div>> + "show-regions", cl::Optional,</div><div>> + cl::desc("Show the execution counts for each region"),</div>
<div>> + cl::cat(ViewCategory));</div><div>> +</div><div>> + cl::opt<bool> ShowBestLineRegionsCounts(</div><div>> + "show-line-counts-or-regions", cl::Optional,</div><div>> + cl::desc("Show the execution counts for each line, or the execution "</div>
<div>> + "counts for each region on lines that have multiple regions"),</div><div>> + cl::cat(ViewCategory));</div><div>> +</div><div>> + cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,</div>
<div>> + cl::desc("Show expanded source regions"),</div><div>> + cl::cat(ViewCategory));</div><div>> +</div><div>> + cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,</div>
<div>> + cl::desc("Show function instantiations"),</div><div>> + cl::cat(ViewCategory));</div><div>> +</div><div>> + cl::opt<bool> NoColors("no-colors", cl::Optional,</div>
<div>> + cl::desc("Don't show text colors"), cl::init(false),</div><div>> + cl::cat(ViewCategory));</div><div>> +</div><div>> + auto Err = commandLineParser(argc, argv);</div>
<div>> + if (Err)</div><div>> + return Err;</div><div>> +</div><div>> + ViewOpts.Colors = !NoColors;</div><div>> + ViewOpts.ShowLineNumbers = true;</div><div>> + ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||</div>
<div>> + !ShowRegions || ShowBestLineRegionsCounts;</div><div>> + ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;</div><div>> + ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts;</div>
<div>> + ViewOpts.ShowExpandedRegions = ShowExpansions;</div><div>> + ViewOpts.ShowFunctionInstantiations = ShowInstantiations;</div><div>> +</div><div>> + if (load())</div><div>> + return 1;</div><div>
> +</div><div>> + if (!Filters.empty()) {</div><div>> + // Show functions</div><div>> + for (const auto &Function : FunctionMappingRecords) {</div><div>> + unsigned MainFileID;</div><div>> + if (findMainViewFileID(Function, MainFileID))</div>
<div>> + continue;</div><div>> + StringRef SourceFile = Function.Filenames[MainFileID];</div><div>> + std::unique_ptr<SourceCoverageView> mainView;</div><div>> + auto SourceBuffer = getSourceFile(SourceFile);</div>
<div>> + if (!SourceBuffer)</div><div>> + return 1;</div><div>> + auto Range = findExpandedFileInterestingLineRange(MainFileID, Function);</div><div>> + mainView.reset(new SourceCoverageView(SourceBuffer.get(), ViewOpts,</div>
<div>> + Range.first, Range.second));</div><div>> + createSourceFileView(SourceFile, *mainView, Function, true);</div><div>> + if (ViewOpts.Colors)</div><div>> + outs().changeColor(raw_ostream::CYAN);</div>
<div>> + outs() << Function.PrettyName << " from " << SourceFile << ":";</div><div>> + outs().resetColor();</div><div><br></div><div>Does resetColor do the right thing if we didn't change the color?</div>
<div><br></div><div>> + outs() << "\n";</div><div>> + mainView->render(outs());</div><div>> + if (FunctionMappingRecords.size() > 1)</div><div>> + outs() << "\n";</div>
<div>> + }</div><div>> + return 0;</div><div>> + }</div><div>> +</div><div>> + // Show files</div><div>> + bool ShowFilenames = SourceFiles.size() != 1;</div><div>> +</div><div>> + if (SourceFiles.empty()) {</div>
<div>> + // Get the source files from the function coverage mapping</div><div>> + std::set<StringRef> UniqueFilenames;</div><div>> + for (const auto &Function : FunctionMappingRecords) {</div><div>
> + for (const auto &Filename : Function.Filenames)</div><div>> + UniqueFilenames.insert(Filename);</div><div>> + }</div><div>> + for (const auto &Filename : UniqueFilenames)</div><div>
> + SourceFiles.push_back(Filename);</div><div>> + }</div><div>> +</div><div>> + for (const auto &SourceFile : SourceFiles) {</div><div>> + std::unique_ptr<SourceCoverageView> mainView;</div>
<div>> + auto SourceBuffer = getSourceFile(SourceFile);</div><div>> + if (!SourceBuffer)</div><div>> + return 1;</div><div>> + mainView.reset(new SourceCoverageView(SourceBuffer.get(), ViewOpts));</div>
<div>> + if (createSourceFileView(SourceFile, *mainView, FunctionMappingRecords)) {</div><div>> + if (ViewOpts.Colors)</div><div>> + outs().changeColor(raw_ostream::RED);</div><div>> + outs() << "warning: The file '" << SourceFile << "' isn't covered.";</div>
<div>> + outs().resetColor();</div><div>> + outs() << "\n";</div><div>> + continue;</div><div>> + }</div><div>> +</div><div>> + if (ShowFilenames) {</div><div>> + if (ViewOpts.Colors)</div>
<div>> + outs().changeColor(raw_ostream::CYAN);</div><div>> + outs() << SourceFile << ":";</div><div>> + outs().resetColor();</div><div>> + outs() << "\n";</div>
<div>> + }</div><div>> + mainView->render(outs());</div><div>> + if (SourceFiles.size() > 1)</div><div>> + outs() << "\n";</div><div>> + }</div><div>> +</div><div>> + return 0;</div>
<div>> +}</div><div>> +</div><div>> +int CodeCoverageTool::report(int argc, const char **argv,</div><div>> + CommandLineParserType commandLineParser) {</div><div>> + cl::opt<bool> NoColors("no-colors", cl::Optional,</div>
<div>> + cl::desc("Don't show text colors"), cl::init(false));</div><div>> +</div><div>> + auto Err = commandLineParser(argc, argv);</div><div>> + if (Err)</div><div>> + return Err;</div>
<div>> +</div><div>> + ViewOpts.Colors = !NoColors;</div><div>> +</div><div>> + if (load())</div><div>> + return 1;</div><div>> +</div><div>> + CoverageSummary Summarizer;</div><div>> + Summarizer.createSummaries(FunctionMappingRecords);</div>
<div>> + CoverageReport Report(ViewOpts, Summarizer);</div><div>> + if (SourceFiles.empty() && Filters.empty()) {</div><div>> + Report.renderFileReports(llvm::outs());</div><div>> + return 0;</div>
<div>> + }</div><div>> +</div><div>> + Report.renderFunctionReports(llvm::outs());</div><div>> + return 0;</div><div>> +}</div><div>> +</div><div>> +int show_main(int argc, const char **argv) {</div>
<div>> + CodeCoverageTool Tool;</div><div>> + return Tool.run(CodeCoverageTool::Show, argc, argv);</div><div>> +}</div><div>> +</div><div>> +int report_main(int argc, const char **argv) {</div><div>> + CodeCoverageTool Tool;</div>
<div>> + return Tool.run(CodeCoverageTool::Report, argc, argv);</div><div>> +}</div><div>> Index: tools/llvm-cov/CoverageFilters.cpp</div><div>> ===================================================================</div>
<div>> --- /dev/null</div><div>> +++ tools/llvm-cov/CoverageFilters.cpp</div><div>> @@ -0,0 +1,57 @@</div><div>> +//===- CoverageFilters.cpp - Function coverage mapping filters ------------===//</div><div>> +//</div>
<div>> +// The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// These classes provide filtering for function coverage mapping records.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#include "CoverageFilters.h"</div><div>> +#include "CoverageSummaryInfo.h"</div>
<div>> +#include "llvm/Support/Regex.h"</div><div>> +</div><div>> +using namespace llvm;</div><div>> +</div><div>> +bool NameCoverageFilter::matches(const FunctionCoverageMapping &Function) {</div>
<div>> + StringRef FuncName = Function.PrettyName;</div><div>> + return FuncName.find(Name) != StringRef::npos;</div><div>> +}</div><div>> +</div><div>> +bool NameRegexCoverageFilter::matches(const FunctionCoverageMapping &Function) {</div>
<div>> + return llvm::Regex(Regex).match(Function.PrettyName);</div><div>> +}</div><div>> +</div><div>> +bool RegionCoverageFilter::matches(const FunctionCoverageMapping &Function) {</div><div>> + return PassesThreshold(FunctionCoverageSummary::get(Function)</div>
<div>> + .RegionCoverage.getPercentCovered());</div><div>> +}</div><div>> +</div><div>> +bool LineCoverageFilter::matches(const FunctionCoverageMapping &Function) {</div><div>> + return PassesThreshold(</div>
<div>> + FunctionCoverageSummary::get(Function).LineCoverage.getPercentCovered());</div><div>> +}</div><div>> +</div><div>> +void CoverageFilters::push_back(CoverageFilter *filter) {</div><div>> + Filters.push_back(std::unique_ptr<CoverageFilter>(filter));</div>
<div>> +}</div><div><br></div><div>Making the argument a unique_ptr is clearer about ownership.</div><div><br></div><div>> +</div><div>> +bool CoverageFilters::matches(const FunctionCoverageMapping &Function) {</div>
<div>> + for (const auto &Filter : Filters) {</div><div>> + if (Filter->matches(Function))</div><div>> + return true;</div><div>> + }</div><div>> + return false;</div><div>> +}</div><div>
> +</div><div>> +bool CoverageFiltersMatchAll::matches(const FunctionCoverageMapping &Function) {</div><div>> + for (const auto &Filter : Filters) {</div><div>> + if (!Filter->matches(Function))</div>
<div>> + return false;</div><div>> + }</div><div>> + return true;</div><div>> +}</div><div>> Index: tools/llvm-cov/CoverageFilters.h</div><div>> ===================================================================</div>
<div>> --- /dev/null</div><div>> +++ tools/llvm-cov/CoverageFilters.h</div><div>> @@ -0,0 +1,123 @@</div><div>> +//===- CoverageFilters.h - Function coverage mapping filters --------------===//</div><div>> +//</div>
<div>> +// The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// These classes provide filtering for function coverage mapping records.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#ifndef COVERAGEFILTERS_H</div><div>> +#define COVERAGEFILTERS_H</div><div>
<br></div><div>Header guards should be more specific as to avoid collisions in case</div><div>things move around. How about LLVM_COV_COVERAGEFILTERS_H? This applies</div><div>to all of the other headers as well.</div><div>
<br></div><div>> +</div><div>> +#include "FunctionCoverageMapping.h"</div><div>> +#include <vector></div><div>> +#include <memory></div><div>> +</div><div>> +namespace llvm {</div><div>
> +</div><div>> +/// \brief Matches specific functions that pass the requirement of this filter.</div><div>> +class CoverageFilter {</div><div>> +public:</div><div>> + /// \brief Return true if the function passes the requirements of this filter.</div>
<div>> + virtual bool matches(const FunctionCoverageMapping &Function) { return true; }</div><div>> +};</div><div>> +</div><div>> +/// \brief Matches functions that contain a specific string in their name.</div>
<div>> +class NameCoverageFilter : public CoverageFilter {</div><div>> + StringRef Name;</div><div>> +</div><div>> +public:</div><div>> + NameCoverageFilter(StringRef Name) : Name(Name) {}</div><div>> +</div>
<div>> + bool matches(const FunctionCoverageMapping &Function) override;</div><div>> +};</div><div>> +</div><div>> +/// \brief Matches functions whose name matches a certain regular expression.</div><div>
> +class NameRegexCoverageFilter : public CoverageFilter {</div><div>> + StringRef Regex;</div><div>> +</div><div>> +public:</div><div>> + NameRegexCoverageFilter(StringRef Regex) : Regex(Regex) {}</div><div>
> +</div><div>> + bool matches(const FunctionCoverageMapping &Function) override;</div><div>> +};</div><div>> +</div><div>> +/// \brief Matches numbers that pass a certain threshold.</div><div>> +template <typename T> class StatisticThresholdFilter {</div>
<div>> +public:</div><div>> + enum Operation { LessThan, GreaterThan };</div><div>> +</div><div>> +protected:</div><div>> + Operation Op;</div><div>> + T Threshold;</div><div>> +</div><div>> + StatisticThresholdFilter(Operation Op, T Threshold)</div>
<div>> + : Op(Op), Threshold(Threshold) {}</div><div>> +</div><div>> + /// \brief Return true if the given number is less than</div><div>> + /// or greater than the certain threshold.</div><div>> + bool PassesThreshold(T Value) const {</div>
<div>> + switch (Op) {</div><div>> + case LessThan:</div><div>> + return Value < Threshold;</div><div>> + case GreaterThan:</div><div>> + return Value > Threshold;</div><div>> + }</div>
<div>> + return false;</div><div>> + }</div><div>> +};</div><div>> +</div><div>> +/// \brief Matches functions whose region coverage percentage</div><div>> +/// is above/below a certain percentage.</div>
<div>> +class RegionCoverageFilter : public CoverageFilter,</div><div>> + public StatisticThresholdFilter<double> {</div><div>> +public:</div><div>> + RegionCoverageFilter(Operation Op, double Threshold)</div>
<div>> + : StatisticThresholdFilter(Op, Threshold) {}</div><div>> +</div><div>> + bool matches(const FunctionCoverageMapping &Function) override;</div><div>> +};</div><div>> +</div><div>> +/// \brief Matches functions whose line coverage percentage</div>
<div>> +/// is above/below a certain percentage.</div><div>> +class LineCoverageFilter : public CoverageFilter,</div><div>> + public StatisticThresholdFilter<double> {</div><div>> +public:</div>
<div>> + LineCoverageFilter(Operation Op, double Threshold)</div><div>> + : StatisticThresholdFilter(Op, Threshold) {}</div><div>> +</div><div>> + bool matches(const FunctionCoverageMapping &Function) override;</div>
<div>> +};</div><div>> +</div><div>> +/// \brief A collection of filters.</div><div>> +/// Matches functions that match any filters contained</div><div>> +/// in an instance of this class.</div><div>> +class CoverageFilters : public CoverageFilter {</div>
<div>> +protected:</div><div>> + std::vector<std::unique_ptr<CoverageFilter>> Filters;</div><div>> +</div><div>> +public:</div><div>> + /// \brief Append a filter to this collection.</div><div>
> + void push_back(CoverageFilter *filter);</div><div>> +</div><div>> + bool empty() const { return Filters.empty(); }</div><div>> +</div><div>> + bool matches(const FunctionCoverageMapping &Function) override;</div>
<div>> +};</div><div>> +</div><div>> +/// \brief A collection of filters.</div><div>> +/// Matches functions that match all of the filters contained</div><div>> +/// in an instance of this class.</div><div>
> +class CoverageFiltersMatchAll : public CoverageFilters {</div><div>> +public:</div><div>> + bool matches(const FunctionCoverageMapping &Function) override;</div><div>> +};</div><div>> +</div><div>> +} // namespace llvm</div>
<div>> +</div><div>> +#endif // COVERAGEFILTERS_H</div><div>> Index: tools/llvm-cov/CoverageReport.cpp</div><div>> ===================================================================</div><div>> --- /dev/null</div>
<div>> +++ tools/llvm-cov/CoverageReport.cpp</div><div>> @@ -0,0 +1,204 @@</div><div>> +//===- CoverageReport.cpp - Code coverage report -------------------------===//</div><div>> +//</div><div>> +// The LLVM Compiler Infrastructure</div>
<div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +//</div><div>> +// This class implements rendering of a code coverage report.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div>
<div>> +#include "CoverageReport.h"</div><div>> +#include "CoverageSummary.h"</div><div>> +#include "llvm/Support/Format.h"</div><div>> +#include "llvm/Support/FileSystem.h"</div>
<div>> +</div><div>> +using namespace llvm;</div><div>> +namespace {</div><div>> +/// \brief Helper struct which prints trimmed and aligned columns.</div><div>> +struct Column {</div><div>> + enum TrimKind { NoTrim, LeftTrim, RightTrim };</div>
<div>> +</div><div>> + enum AlignmentKind { LeftAlignment, RightAlignment };</div><div>> +</div><div>> + StringRef Str;</div><div>> + unsigned Width;</div><div>> + TrimKind Trim;</div><div>> + AlignmentKind Alignment;</div>
<div>> +</div><div>> + Column(StringRef Str, unsigned Width)</div><div>> + : Str(Str), Width(Width), Trim(NoTrim), Alignment(LeftAlignment) {}</div><div>> +</div><div>> + Column &set(TrimKind Value) {</div>
<div>> + Trim = Value;</div><div>> + return *this;</div><div>> + }</div><div>> +</div><div>> + Column &set(AlignmentKind Value) {</div><div>> + Alignment = Value;</div><div>> + return *this;</div>
<div>> + }</div><div>> +</div><div>> + void render(raw_ostream &OS) const;</div><div>> +};</div><div>> +raw_ostream &operator<<(raw_ostream &OS, const Column &Value) {</div><div>> + Value.render(OS);</div>
<div>> + return OS;</div><div>> +}</div><div>> +}</div><div>> +</div><div>> +void Column::render(raw_ostream &OS) const {</div><div>> + if (Str.size() <= Width) {</div><div>> + if (Alignment == RightAlignment) {</div>
<div>> + OS.indent(Width - Str.size());</div><div>> + OS << Str;</div><div>> + return;</div><div>> + }</div><div>> + OS << Str;</div><div>> + OS.indent(Width - Str.size());</div>
<div>> + return;</div><div>> + }</div><div>> +</div><div>> + switch (Trim) {</div><div>> + case NoTrim:</div><div>> + OS << Str.substr(0, Width);</div><div>> + break;</div><div>> + case LeftTrim:</div>
<div>> + OS << "..." << Str.substr(Str.size() - Width + 3);</div><div>> + break;</div><div>> + case RightTrim:</div><div>> + OS << Str.substr(0, Width - 3) << "...";</div>
<div>> + break;</div><div>> + }</div><div>> +}</div><div>> +</div><div>> +static Column column(StringRef Str, unsigned Width) {</div><div>> + return Column(Str, Width);</div><div>> +}</div><div>> +</div>
<div>> +template <typename T></div><div>> +static Column column(StringRef Str, unsigned Width, const T &Value) {</div><div>> + return Column(Str, Width).set(Value);</div><div>> +}</div><div>> +</div>
<div>> +static const unsigned FileReportColumns[] = {25, 10, 8, 8, 10, 8};</div><div>> +static const unsigned FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8};</div><div>> +</div><div>> +/// \brief Prints a horizontal divider which spans across the given columns.</div>
<div>> +template <typename T, size_t N></div><div>> +static void renderDivider(T (&Columns)[N], raw_ostream &OS) {</div><div>> + unsigned Length = 0;</div><div>> + for (unsigned I = 0; I < N; ++I)</div>
<div>> + Length += Columns[I];</div><div>> + for (unsigned I = 0; I < Length; ++I)</div><div>> + OS << '-';</div><div>> +}</div><div>> +</div><div>> +/// \brief Return the color which correponds to the coverage</div>
<div>> +/// percentage of a certain metric.</div><div>> +template <typename T></div><div>> +static raw_ostream::Colors determineCoveragePercentageColor(const T &Info) {</div><div>> + if (Info.isFullyCovered())</div>
<div>> + return raw_ostream::GREEN;</div><div>> + return Info.getPercentCovered() >= 80.0 ? raw_ostream::YELLOW</div><div>> + : raw_ostream::RED;</div><div>> +}</div>
<div>> +</div><div>> +void CoverageReport::render(const FileCoverageSummary &File, raw_ostream &OS) {</div><div>> + OS << column(File.Name, FileReportColumns[0], Column::LeftTrim)</div><div>> + << format("%*zd", FileReportColumns[1], File.RegionCoverage.NumRegions);</div>
<div>> + if (Options.Colors)</div><div>> + OS.changeColor(File.RegionCoverage.isFullyCovered() ? raw_ostream::GREEN</div><div>> + : raw_ostream::RED);</div>
<div>> + OS << format("%*zd", FileReportColumns[2], File.RegionCoverage.NotCovered);</div><div>> + if (Options.Colors)</div><div>> + OS.changeColor(determineCoveragePercentageColor(File.RegionCoverage));</div>
<div>> + OS << format("%*.2f", FileReportColumns[3] - 1,</div><div>> + File.RegionCoverage.getPercentCovered()) << '%';</div><div>> + OS.resetColor();</div><div>> + OS << format("%*zd", FileReportColumns[4],</div>
<div>> + File.FunctionCoverage.NumFunctions);</div><div>> + if (Options.Colors)</div><div>> + OS.changeColor(determineCoveragePercentageColor(File.FunctionCoverage));</div><div>> + OS << format("%*.2f", FileReportColumns[5] - 1,</div>
<div>> + File.FunctionCoverage.getPercentCovered()) << '%';</div><div>> + OS.resetColor();</div><div>> + OS << "\n";</div><div>> +}</div><div><br></div><div>Might be nicer to have an "unsigned Col" and increment it instead of</div>
<div>writing out the column index by hand. I don't feel strongly though.</div><div><br></div><div>> +</div><div>> +void CoverageReport::render(const FunctionCoverageSummary &Function,</div><div>> + raw_ostream &OS) {</div>
<div>> + OS << column(Function.Name, FunctionReportColumns[0], Column::RightTrim)</div><div>> + << format("%*zd", FunctionReportColumns[1],</div><div>> + Function.RegionCoverage.NumRegions);</div>
<div>> + if (Options.Colors)</div><div>> + OS.changeColor(Function.RegionCoverage.isFullyCovered() ? raw_ostream::GREEN</div><div>> + : raw_ostream::RED);</div>
<div>> + OS << format("%*zd", FunctionReportColumns[2],</div><div>> + Function.RegionCoverage.NotCovered);</div><div>> + if (Options.Colors)</div><div>> + OS.changeColor(determineCoveragePercentageColor(Function.RegionCoverage));</div>
<div>> + OS << format("%*.2f", FunctionReportColumns[3] - 1,</div><div>> + Function.RegionCoverage.getPercentCovered()) << '%';</div><div>> + OS.resetColor();</div><div>
> + OS << format("%*zd", FunctionReportColumns[4],</div><div>> + Function.LineCoverage.NumLines);</div><div>> + if (Options.Colors)</div><div>> + OS.changeColor(Function.LineCoverage.isFullyCovered() ? raw_ostream::GREEN</div>
<div>> + : raw_ostream::RED);</div><div>> + OS << format("%*zd", FunctionReportColumns[5],</div><div>> + Function.LineCoverage.NotCovered);</div>
<div>> + if (Options.Colors)</div><div>> + OS.changeColor(determineCoveragePercentageColor(Function.LineCoverage));</div><div>> + OS << format("%*.2f", FunctionReportColumns[6] - 1,</div><div>
> + Function.LineCoverage.getPercentCovered()) << '%';</div><div>> + OS.resetColor();</div><div>> + OS << "\n";</div><div>> +}</div><div>> +</div><div>> +void CoverageReport::renderFunctionReports(raw_ostream &OS) {</div>
<div>> + bool isFirst = true;</div><div>> + for (const auto &File : Summary.getFileSummaries()) {</div><div>> + if (isFirst)</div><div>> + isFirst = false;</div><div>> + else</div><div>> + OS << "\n";</div>
<div>> + OS << "File '" << File.Name << "':\n";</div><div>> + OS << column("Name", FunctionReportColumns[0])</div><div>> + << column("Regions", FunctionReportColumns[1], Column::RightAlignment)</div>
<div>> + << column("Miss", FunctionReportColumns[2], Column::RightAlignment)</div><div>> + << column("Cover", FunctionReportColumns[3], Column::RightAlignment)</div><div>> + << column("Lines", FunctionReportColumns[4], Column::RightAlignment)</div>
<div>> + << column("Miss", FunctionReportColumns[5], Column::RightAlignment)</div><div>> + << column("Cover", FunctionReportColumns[6], Column::RightAlignment);</div><div>
> + OS << "\n";</div><div>> + renderDivider(FunctionReportColumns, OS);</div><div>> + OS << "\n";</div><div>> + for (const auto &Function : File.FunctionSummaries)</div>
<div>> + render(Function, OS);</div><div>> + renderDivider(FunctionReportColumns, OS);</div><div>> + OS << "\n";</div><div>> + render(FunctionCoverageSummary("TOTAL", File.RegionCoverage,</div>
<div>> + File.LineCoverage),</div><div>> + OS);</div><div>> + }</div><div>> +}</div><div>> +</div><div>> +void CoverageReport::renderFileReports(raw_ostream &OS) {</div>
<div>> + OS << column("Filename", FileReportColumns[0])</div><div>> + << column("Regions", FileReportColumns[1], Column::RightAlignment)</div><div>> + << column("Miss", FileReportColumns[2], Column::RightAlignment)</div>
<div>> + << column("Cover", FileReportColumns[3], Column::RightAlignment)</div><div>> + << column("Functions", FileReportColumns[4], Column::RightAlignment)</div><div>> + << column("Cover", FileReportColumns[5], Column::RightAlignment) << "\n";</div>
<div>> + renderDivider(FileReportColumns, OS);</div><div>> + OS << "\n";</div><div>> + for (const auto &File : Summary.getFileSummaries())</div><div>> + render(File, OS);</div><div>> + renderDivider(FileReportColumns, OS);</div>
<div>> + OS << "\n";</div><div>> + render(Summary.getCombinedFileSummaries(), OS);</div><div>> +}</div><div>> Index: tools/llvm-cov/CoverageReport.h</div><div>> ===================================================================</div>
<div>> --- /dev/null</div><div>> +++ tools/llvm-cov/CoverageReport.h</div><div>> @@ -0,0 +1,40 @@</div><div>> +//===- CoverageReport.h - Code coverage report ---------------------------===//</div><div>> +//</div>
<div>> +// The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// This class implements rendering of a code coverage report.</div><div>> +//</div>
<div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#ifndef COVERAGEREPORT_H</div><div>> +#define COVERAGEREPORT_H</div><div>> +</div><div>> +#include "CoverageViewOptions.h"</div>
<div>> +#include "CoverageSummary.h"</div><div>> +</div><div>> +namespace llvm {</div><div>> +</div><div>> +/// \brief Displays the code coverage report</div><div>> +class CoverageReport {</div>
<div>> + const CoverageViewOptions &Options;</div><div>> + CoverageSummary &Summary;</div><div>> +</div><div>> + void render(const FileCoverageSummary &File, raw_ostream &OS);</div><div>> + void render(const FunctionCoverageSummary &Function, raw_ostream &OS);</div>
<div>> +</div><div>> +public:</div><div>> + CoverageReport(const CoverageViewOptions &Options, CoverageSummary &Summary)</div><div>> + : Options(Options), Summary(Summary) {}</div><div>> +</div>
<div>> + void renderFunctionReports(raw_ostream &OS);</div><div>> +</div><div>> + void renderFileReports(raw_ostream &OS);</div><div>> +};</div><div>> +}</div><div>> +</div><div>> +#endif // COVERAGEREPORT_H</div>
<div>> Index: tools/llvm-cov/CoverageSummary.cpp</div><div>> ===================================================================</div><div>> --- /dev/null</div><div>> +++ tools/llvm-cov/CoverageSummary.cpp</div>
<div>> @@ -0,0 +1,93 @@</div><div>> +//===- CoverageSummary.cpp - Code coverage summary ------------------------===//</div><div>> +//</div><div>> +// The LLVM Compiler Infrastructure</div><div>
> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +//</div><div>> +// This class implements data management and rendering for the code coverage</div><div>> +// summaries of all files and functions.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +</div><div>> +#include "CoverageSummary.h"</div><div>> +#include "llvm/Support/FileSystem.h"</div><div>> +#include "llvm/Support/Format.h"</div><div>> +</div><div>> +using namespace llvm;</div>
<div>> +</div><div>> +unsigned CoverageSummary::getFileID(StringRef Filename) {</div><div>> + for (unsigned I = 0, S = Filenames.size(); I < S; ++I) {</div><div>> + if (sys::fs::equivalent(Filenames[I], Filename))</div>
<div>> + return I;</div><div>> + }</div><div>> + Filenames.push_back(Filename);</div><div>> + return Filenames.size() - 1;</div><div>> +}</div><div>> +</div><div>> +void</div><div>> +CoverageSummary::createSummaries(ArrayRef<FunctionCoverageMapping> Functions) {</div>
<div>> + std::vector<std::pair<unsigned, size_t>> FunctionFileIDs;</div><div>> +</div><div>> + FunctionFileIDs.resize(Functions.size());</div><div>> + for (size_t I = 0, S = Functions.size(); I < S; ++I) {</div>
<div>> + StringRef Filename = Functions[I].Filenames[0];</div><div>> + FunctionFileIDs[I] = std::make_pair(getFileID(Filename), I);</div><div>> + }</div><div>> +</div><div>> + // Sort the function records by file ids</div>
<div>> + std::sort(FunctionFileIDs.begin(), FunctionFileIDs.end(),</div><div>> + [](const std::pair<unsigned, size_t> &lhs,</div><div>> + const std::pair<unsigned, size_t> &rhs) {</div>
<div>> + return lhs.first < rhs.first;</div><div>> + });</div><div>> +</div><div>> + // Create function summaries in a sorted order (by file ids)</div><div>> + FunctionSummaries.reserve(Functions.size());</div>
<div>> + for (size_t I = 0, S = Functions.size(); I < S; ++I)</div><div>> + FunctionSummaries.push_back(</div><div>> + FunctionCoverageSummary::get(Functions[FunctionFileIDs[I].second]));</div><div>
> +</div><div>> + // Create file summaries</div><div>> + size_t CurrentSummary = 0;</div><div>> + for (unsigned FileID = 0; FileID < Filenames.size(); ++FileID) {</div><div>> + // Gather the relevant functions summaries</div>
<div>> + auto PrevSummary = CurrentSummary;</div><div>> + for (; CurrentSummary < FunctionSummaries.size() &&</div><div>> + FunctionFileIDs[CurrentSummary].first == FileID;</div><div>
> + ++CurrentSummary)</div><div>> + ;</div><div><br></div><div>It's clearer (and shorter!) to use a while loop here:</div><div><br></div><div> while (CurrentSummary < FunctionSummaries.size() &&</div>
<div> FunctionFileIDs[CurrentSummary].first == FileID)</div><div> ++CurrentSummary;</div><div><br></div><div>> + ArrayRef<FunctionCoverageSummary> LocalSummaries(</div><div>> + FunctionSummaries.data() + PrevSummary,</div>
<div>> + FunctionSummaries.data() + CurrentSummary);</div><div>> + if (LocalSummaries.empty())</div><div>> + continue;</div><div>> +</div><div>> + FileSummaries.push_back(</div><div>> + FileCoverageSummary::get(Filenames[FileID], LocalSummaries));</div>
<div>> + }</div><div>> +}</div><div>> +</div><div>> +FileCoverageSummary CoverageSummary::getCombinedFileSummaries() {</div><div>> + size_t NumRegions = 0, CoveredRegions = 0;</div><div>> + size_t NumLines = 0, NumNonCodeLines = 0, CoveredLines = 0;</div>
<div>> + size_t NumFunctionsCovered = 0, NumFunctions = 0;</div><div>> + for (const auto &File : FileSummaries) {</div><div>> + NumRegions += File.RegionCoverage.NumRegions;</div><div>> + CoveredRegions += File.RegionCoverage.Covered;</div>
<div>> +</div><div>> + NumLines += File.LineCoverage.NumLines;</div><div>> + NumNonCodeLines += File.LineCoverage.NumNonCodeLines;</div><div>> + CoveredLines += File.LineCoverage.Covered;</div><div>> +</div>
<div>> + NumFunctionsCovered += File.FunctionCoverage.FullyCovered;</div><div>> + NumFunctions += File.FunctionCoverage.NumFunctions;</div><div>> + }</div><div>> + return FileCoverageSummary(</div><div>
> + "TOTAL", RegionCoverageInfo(CoveredRegions, NumRegions),</div><div>> + LineCoverageInfo(CoveredLines, NumNonCodeLines, NumLines),</div><div>> + FunctionCoverageInfo(NumFunctionsCovered, NumFunctions),</div>
<div>> + ArrayRef<FunctionCoverageSummary>());</div><div>> +}</div><div>> Index: tools/llvm-cov/CoverageSummary.h</div><div>> ===================================================================</div>
<div>> --- /dev/null</div><div>> +++ tools/llvm-cov/CoverageSummary.h</div><div>> @@ -0,0 +1,45 @@</div><div>> +//===- CoverageSummary.h - Code coverage summary --------------------------===//</div><div>> +//</div>
<div>> +// The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// This class implements data management and rendering for the code coverage</div>
<div>> +// summaries of all files and functions.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#ifndef COVERAGESUMMARY_H</div>
<div>> +#define COVERAGESUMMARY_H</div><div>> +</div><div>> +#include "CoverageSummaryInfo.h"</div><div>> +#include <vector></div><div>> +</div><div>> +namespace llvm {</div><div>> +</div>
<div>> +/// \brief Manager for the function and file code coverage summaries.</div><div>> +class CoverageSummary {</div><div>> + std::vector<StringRef> Filenames;</div><div>> + std::vector<FunctionCoverageSummary> FunctionSummaries;</div>
<div>> + std::vector<std::pair<unsigned, unsigned>> FunctionSummariesFileIDs;</div><div>> + std::vector<FileCoverageSummary> FileSummaries;</div><div>> +</div><div>> + unsigned getFileID(StringRef Filename);</div>
<div>> +</div><div>> +public:</div><div>> + void createSummaries(ArrayRef<FunctionCoverageMapping> Functions);</div><div>> +</div><div>> + ArrayRef<FileCoverageSummary> getFileSummaries() { return FileSummaries; }</div>
<div>> +</div><div>> + FileCoverageSummary getCombinedFileSummaries();</div><div>> +</div><div>> + void render(const FunctionCoverageSummary &Summary, raw_ostream &OS);</div><div>> +</div><div>> + void render(raw_ostream &OS);</div>
<div>> +};</div><div>> +}</div><div>> +</div><div>> +#endif // COVERAGESUMMARY_H</div><div>> Index: tools/llvm-cov/CoverageSummaryInfo.cpp</div><div>> ===================================================================</div>
<div>> --- /dev/null</div><div>> +++ tools/llvm-cov/CoverageSummaryInfo.cpp</div><div>> @@ -0,0 +1,95 @@</div><div>> +//===- CoverageSummaryInfo.cpp - Coverage summary for function/file -------===//</div><div>
> +//</div><div>> +// The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// These structures are used to represent code coverage metrics</div><div>> +// for functions/files.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#include "CoverageSummaryInfo.h"</div><div>> +</div><div>> +using namespace llvm;</div>
<div>> +using namespace coverage;</div><div>> +</div><div>> +FunctionCoverageSummary</div><div>> +FunctionCoverageSummary::get(const FunctionCoverageMapping &Function) {</div><div>> + // Compute the region coverage</div>
<div>> + size_t NumCodeRegions = 0, CoveredRegions = 0;</div><div>> + for (auto &Region : Function.MappingRegions) {</div><div>> + if (Region.Kind != CounterMappingRegion::CodeRegion)</div><div>> + continue;</div>
<div>> + ++NumCodeRegions;</div><div>> + if (Region.ExecutionCount != 0)</div><div>> + ++CoveredRegions;</div><div>> + }</div><div>> +</div><div>> + // Compute the line coverage</div><div>> + size_t NumLines = 0, CoveredLines = 0;</div>
<div>> + for (unsigned FileID = 0, S = Function.Filenames.size(); FileID < S;</div><div>> + ++FileID) {</div><div>> + // Find the line start and end of the function's source code</div><div>> + // in that particular file</div>
<div>> + unsigned LineStart = std::numeric_limits<unsigned>::max();</div><div>> + unsigned LineEnd = 0;</div><div>> + for (auto &Region : Function.MappingRegions) {</div><div>> + if (Region.FileID != FileID)</div>
<div>> + continue;</div><div>> + LineStart = std::min(LineStart, Region.LineStart);</div><div>> + LineEnd = std::max(LineEnd, Region.LineEnd);</div><div>> + }</div><div>> + unsigned LineCount = LineEnd - LineStart + 1;</div>
<div>> +</div><div>> + // Get counters</div><div>> + llvm::SmallVector<uint64_t, 16> ExecutionCounts;</div><div>> + ExecutionCounts.resize(LineCount, 0);</div><div>> + for (auto &Region : Function.MappingRegions) {</div>
<div>> + if (Region.FileID != FileID)</div><div>> + continue;</div><div>> + // Ignore the lines that were skipped by the preprocessor.</div><div>> + auto ExecutionCount = Region.ExecutionCount;</div>
<div>> + if (Region.Kind == MappingRegion::SkippedRegion) {</div><div>> + LineCount -= Region.LineEnd - Region.LineStart + 1;</div><div>> + ExecutionCount = 1;</div><div>> + }</div><div>
> + for (unsigned I = Region.LineStart; I <= Region.LineEnd; ++I)</div><div>> + ExecutionCounts[I - LineStart] = ExecutionCount;</div><div>> + }</div><div>> + CoveredLines += LineCount - std::count(ExecutionCounts.begin(),</div>
<div>> + ExecutionCounts.end(), 0);</div><div>> + NumLines += LineCount;</div><div>> + }</div><div>> + return FunctionCoverageSummary(</div><div>> + Function.PrettyName, RegionCoverageInfo(CoveredRegions, NumCodeRegions),</div>
<div>> + LineCoverageInfo(CoveredLines, 0, NumLines));</div><div>> +}</div><div>> +</div><div>> +FileCoverageSummary</div><div>> +FileCoverageSummary::get(StringRef Name,</div><div>> + ArrayRef<FunctionCoverageSummary> FunctionSummaries) {</div>
<div>> + size_t NumRegions = 0, CoveredRegions = 0;</div><div>> + size_t NumLines = 0, NumNonCodeLines = 0, CoveredLines = 0;</div><div>> + size_t NumFunctionsCovered = 0;</div><div>> + for (const auto &Func : FunctionSummaries) {</div>
<div>> + CoveredRegions += Func.RegionCoverage.Covered;</div><div>> + NumRegions += Func.RegionCoverage.NumRegions;</div><div>> +</div><div>> + CoveredLines += Func.LineCoverage.Covered;</div><div>> + NumNonCodeLines += Func.LineCoverage.NumNonCodeLines;</div>
<div>> + NumLines += Func.LineCoverage.NumLines;</div><div>> +</div><div>> + if (Func.RegionCoverage.isFullyCovered())</div><div>> + ++NumFunctionsCovered;</div><div>> + }</div><div>> +</div>
<div>> + return FileCoverageSummary(</div><div>> + Name, RegionCoverageInfo(CoveredRegions, NumRegions),</div><div>> + LineCoverageInfo(CoveredLines, NumNonCodeLines, NumLines),</div><div>> + FunctionCoverageInfo(NumFunctionsCovered, FunctionSummaries.size()),</div>
<div>> + FunctionSummaries);</div><div>> +}</div><div>> Index: tools/llvm-cov/CoverageSummaryInfo.h</div><div>> ===================================================================</div><div>> --- /dev/null</div>
<div>> +++ tools/llvm-cov/CoverageSummaryInfo.h</div><div>> @@ -0,0 +1,133 @@</div><div>> +//===- CoverageSummaryInfo.h - Coverage summary for function/file ---------===//</div><div>> +//</div><div>> +// The LLVM Compiler Infrastructure</div>
<div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +//</div><div>> +// These structures are used to represent code coverage metrics</div><div>> +// for functions/files.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +</div><div>> +#ifndef COVERAGESUMMARYINFO_H</div><div>> +#define COVERAGESUMMARYINFO_H</div><div>> +</div><div>> +#include "FunctionCoverageMapping.h"</div><div>> +#include "llvm/Support/raw_ostream.h"</div>
<div>> +</div><div>> +namespace llvm {</div><div>> +</div><div>> +/// \brief Provides information about region coverage for a function/file</div><div><br></div><div>Comments should end in a period. You seem to have been consistent about</div>
<div>full sentences everywhere except for this file ;)</div><div><br></div><div>> +struct RegionCoverageInfo {</div><div>> + /// \brief The number of regions that were executed at least once</div><div>> + size_t Covered;</div>
<div>> +</div><div>> + /// \brief The number of regions that weren't executed</div><div>> + size_t NotCovered;</div><div>> +</div><div>> + /// \brief The total number of regions in a function/file</div>
<div>> + size_t NumRegions;</div><div>> +</div><div>> + RegionCoverageInfo(size_t Covered, size_t NumRegions)</div><div>> + : Covered(Covered), NotCovered(NumRegions - Covered),</div><div>> + NumRegions(NumRegions) {}</div>
<div>> +</div><div>> + bool isFullyCovered() const { return Covered == NumRegions; }</div><div>> +</div><div>> + double getPercentCovered() const {</div><div>> + return double(Covered) / double(NumRegions) * 100.0;</div>
<div>> + }</div><div>> +};</div><div>> +</div><div>> +/// \brief Provides information about line coverage for a function/file</div><div>> +struct LineCoverageInfo {</div><div>> + /// \brief The number of lines that were executed at least once</div>
<div>> + size_t Covered;</div><div>> +</div><div>> + /// \brief The number of lines that weren't executed</div><div>> + size_t NotCovered;</div><div>> +</div><div>> + /// \brief The number of lines that aren't code</div>
<div>> + size_t NumNonCodeLines;</div><div><br></div><div>NumNon is a bit awkward to say. It's fairly obvious it's a number, so</div><div>how about just NonCodeLines?</div><div><br></div><div>> +</div><div>
> + /// \brief The total number of lines in a function/file</div><div>> + size_t NumLines;</div><div>> +</div><div>> + LineCoverageInfo(size_t Covered, size_t NumNonCodeLines, size_t NumLines)</div><div>> + : Covered(Covered), NotCovered(NumLines - NumNonCodeLines - Covered),</div>
<div>> + NumNonCodeLines(NumNonCodeLines), NumLines(NumLines) {}</div><div>> +</div><div>> + bool isFullyCovered() const {</div><div>> + return Covered == (NumLines - NumNonCodeLines);</div><div>> + }</div>
<div>> +</div><div>> + double getPercentCovered() const {</div><div>> + return double(Covered) / double(NumLines - NumNonCodeLines) * 100.0;</div><div>> + }</div><div>> +};</div><div>> +</div><div>> +/// \brief Provides information about function coverage for a file</div>
<div>> +struct FunctionCoverageInfo {</div><div>> + /// \brief The number of functions that have full</div><div>> + /// region coverage</div><div>> + size_t FullyCovered;</div><div>> +</div><div>> + /// \brief The total number of functions in this file</div>
<div>> + size_t NumFunctions;</div><div>> +</div><div>> + FunctionCoverageInfo(size_t FullyCovered, size_t NumFunctions)</div><div>> + : FullyCovered(FullyCovered), NumFunctions(NumFunctions) {}</div><div>
> +</div><div>> + bool isFullyCovered() const { return FullyCovered == NumFunctions; }</div><div>> +</div><div>> + double getPercentCovered() const {</div><div>> + return double(FullyCovered) / double(NumFunctions) * 100.0;</div>
<div>> + }</div><div>> +};</div><div>> +</div><div>> +/// \brief A summary of function's code coverage</div><div>> +struct FunctionCoverageSummary {</div><div>> + StringRef Name;</div><div>> + RegionCoverageInfo RegionCoverage;</div>
<div>> + LineCoverageInfo LineCoverage;</div><div>> +</div><div>> + FunctionCoverageSummary(StringRef Name,</div><div>> + const RegionCoverageInfo &RegionCoverage,</div><div>> + const LineCoverageInfo &LineCoverage)</div>
<div>> + : Name(Name), RegionCoverage(RegionCoverage), LineCoverage(LineCoverage) {</div><div>> + }</div><div>> +</div><div>> + /// \brief Compute the code coverage summary for the given function coverage</div>
<div>> + /// mapping record.</div><div>> + static FunctionCoverageSummary get(const FunctionCoverageMapping &Function);</div><div>> +};</div><div>> +</div><div>> +/// \brief A summary of file's code coverage</div>
<div>> +struct FileCoverageSummary {</div><div>> + StringRef Name;</div><div>> + RegionCoverageInfo RegionCoverage;</div><div>> + LineCoverageInfo LineCoverage;</div><div>> + FunctionCoverageInfo FunctionCoverage;</div>
<div>> + /// \brief The summary of every function</div><div>> + /// in this file.</div><div>> + ArrayRef<FunctionCoverageSummary> FunctionSummaries;</div><div>> +</div><div>> + FileCoverageSummary(StringRef Name, const RegionCoverageInfo &RegionCoverage,</div>
<div>> + const LineCoverageInfo &LineCoverage,</div><div>> + const FunctionCoverageInfo &FunctionCoverage,</div><div>> + ArrayRef<FunctionCoverageSummary> FunctionSummaries)</div>
<div>> + : Name(Name), RegionCoverage(RegionCoverage), LineCoverage(LineCoverage),</div><div>> + FunctionCoverage(FunctionCoverage),</div><div>> + FunctionSummaries(FunctionSummaries) {}</div><div>
> +</div><div>> + /// \brief Compute the code coverage summary for a file</div><div>> + static FileCoverageSummary</div><div>> + get(StringRef Name, ArrayRef<FunctionCoverageSummary> FunctionSummaries);</div>
<div>> +};</div><div>> +</div><div>> +} // namespace llvm</div><div>> +</div><div>> +#endif // COVERAGESUMMARYINFO_H</div><div>> Index: tools/llvm-cov/CoverageViewOptions.h</div><div>> ===================================================================</div>
<div>> --- /dev/null</div><div>> +++ tools/llvm-cov/CoverageViewOptions.h</div><div>> @@ -0,0 +1,28 @@</div><div>> +//===- CoverageViewOptions.h - Code coverage display options -------------===//</div><div>> +//</div>
<div>> +// The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#ifndef COVERAGEVIEWOPTIONS_H</div><div>> +#define COVERAGEVIEWOPTIONS_H</div>
<div>> +</div><div>> +namespace llvm {</div><div>> +</div><div>> +/// \brief The options for displaying the code coverage information.</div><div>> +struct CoverageViewOptions {</div><div>> + bool Debug;</div>
<div>> + bool Colors;</div><div>> + bool ShowLineNumbers;</div><div>> + bool ShowLineStats;</div><div>> + bool ShowRegionMarkers;</div><div>> + bool ShowLineStatsOrRegionMarkers;</div><div>> + bool ShowExpandedRegions;</div>
<div>> + bool ShowFunctionInstantiations;</div><div>> +};</div><div>> +}</div><div>> +</div><div>> +#endif // COVERAGEVIEWOPTIONS_H</div><div>> Index: tools/llvm-cov/FunctionCoverageMapping.h</div><div>
> ===================================================================</div><div>> --- /dev/null</div><div>> +++ tools/llvm-cov/FunctionCoverageMapping.h</div><div>> @@ -0,0 +1,50 @@</div><div>> +//===- FunctionCoverageMapping.h - Function coverage mapping record -------===//</div>
<div>> +//</div><div>> +// The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// A structure that stores the coverage mapping record for a single function.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#ifndef FUNCTIONCOVERAGEMAPPING_H</div><div>> +#define FUNCTIONCOVERAGEMAPPING_H</div>
<div>> +</div><div>> +#include <string></div><div>> +#include <vector></div><div>> +#include "llvm/ADT/ArrayRef.h"</div><div>> +#include "llvm/ADT/StringRef.h"</div><div>> +#include "llvm/ProfileData/CoverageMapping.h"</div>
<div>> +</div><div>> +namespace llvm {</div><div>> +</div><div>> +/// \brief Associates a source range with an execution count.</div><div>> +struct MappingRegion : public coverage::CounterMappingRegion {</div>
<div>> + uint64_t ExecutionCount;</div><div>> +</div><div>> + MappingRegion(const CounterMappingRegion &R, uint64_t ExecutionCount)</div><div>> + : CounterMappingRegion(R), ExecutionCount(ExecutionCount) {}</div>
<div>> +};</div><div>> +</div><div>> +/// \brief Stores all the required information</div><div>> +/// about code coverage for a single function.</div><div>> +struct FunctionCoverageMapping {</div><div>> + /// \brief Raw function name</div>
<div>> + std::string Name;</div><div>> + /// \brief Demangled function name</div><div>> + std::string PrettyName;</div><div>> + std::vector<std::string> Filenames;</div><div>> + std::vector<MappingRegion> MappingRegions;</div>
<div>> +</div><div>> + FunctionCoverageMapping(StringRef Name, ArrayRef<StringRef> Filenames)</div><div>> + : Name(Name), PrettyName(Name),</div><div>> + Filenames(Filenames.begin(), Filenames.end()) {}</div>
<div>> +};</div><div>> +</div><div>> +} // namespace llvm</div><div>> +</div><div>> +#endif // FUNCTIONCOVERAGEMAPPING_H</div><div>> Index: tools/llvm-cov/LLVMBuild.txt</div><div>> ===================================================================</div>
<div>> --- tools/llvm-cov/LLVMBuild.txt</div><div>> +++ tools/llvm-cov/LLVMBuild.txt</div><div>> @@ -19,4 +19,4 @@</div><div>> type = Tool</div><div>> name = llvm-cov</div><div>> parent = Tools</div><div>
> -required_libraries = Instrumentation</div><div>> +required_libraries = ProfileData Support Instrumentation</div><div>> Index: tools/llvm-cov/Makefile</div><div>> ===================================================================</div>
<div>> --- tools/llvm-cov/Makefile</div><div>> +++ tools/llvm-cov/Makefile</div><div>> @@ -9,7 +9,7 @@</div><div>></div><div>> LEVEL := ../..</div><div>> TOOLNAME := llvm-cov</div><div>> -LINK_COMPONENTS := core support</div>
<div>> +LINK_COMPONENTS := core support profiledata object</div><div>></div><div>> # This tool has no plugins, optimize startup time.</div><div>> TOOL_NO_EXPORTS := 1</div><div>> Index: tools/llvm-cov/SourceCoverageDataManager.cpp</div>
<div>> ===================================================================</div><div>> --- /dev/null</div><div>> +++ tools/llvm-cov/SourceCoverageDataManager.cpp</div><div>> @@ -0,0 +1,55 @@</div><div>> +//===- SourceCoverageDataManager.cpp - Manager for source file coverage</div>
<div>> +// data-===//</div><div>> +//</div><div>> +// The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div>
<div>> +// License. See LICENSE.TXT for details.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// This class separates and merges mapping regions for a specific source file.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#include "SourceCoverageDataManager.h"</div><div>> +</div><div>> +using namespace llvm;</div>
<div>> +using namespace coverage;</div><div>> +</div><div>> +void SourceCoverageDataManager::insert(const MappingRegion &Region) {</div><div>> + SourceRange Range(Region.LineStart, Region.ColumnStart, Region.LineEnd,</div>
<div>> + Region.ColumnEnd);</div><div>> + if (Region.Kind == CounterMappingRegion::SkippedRegion) {</div><div>> + SkippedRegions.push_back(Range);</div><div>> + return;</div><div>> + }</div>
<div>> + auto It = Regions.find(Range);</div><div>> + if (It == Regions.end()) {</div><div>> + Regions.insert(std::make_pair(Range, Region.ExecutionCount));</div><div>> + return;</div><div>> + }</div>
<div>> + It->second += Region.ExecutionCount;</div><div>> +}</div><div>> +</div><div>> +ArrayRef<std::pair<SourceCoverageDataManager::SourceRange, uint64_t>></div><div>> +SourceCoverageDataManager::getSourceRegions() {</div>
<div>> + if (!SortedRegions.empty())</div><div>> + return SortedRegions;</div><div>> +</div><div>> + SortedRegions.reserve(Regions.size());</div><div>> + for (const auto &Region : Regions)</div><div>
> + SortedRegions.push_back(std::make_pair(Region.first, Region.second));</div><div>> +</div><div>> + // Get rid of the map</div><div>> + Regions.clear();</div><div>> +</div><div>> + // Sort the final source regions</div>
<div>> + std::sort(SortedRegions.begin(), SortedRegions.end(),</div><div>> + [](const std::pair<SourceRange, uint64_t> &LHS,</div><div>> + const std::pair<SourceRange, uint64_t> &RHS) {</div>
<div>> + return LHS.first < RHS.first;</div><div>> + });</div><div>> +</div><div>> + return SortedRegions;</div><div>> +}</div><div>> Index: tools/llvm-cov/SourceCoverageDataManager.h</div><div>> ===================================================================</div>
<div>> --- /dev/null</div><div>> +++ tools/llvm-cov/SourceCoverageDataManager.h</div><div>> @@ -0,0 +1,83 @@</div><div>> +//===- SourceCoverageDataManager.h - Manager for source file coverage data-===//</div><div>
> +//</div><div>> +// The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// This class separates and merges mapping regions for a specific source file.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#ifndef SOURCECOVERAGEDATAMANAGER_H</div><div>> +#define SOURCECOVERAGEDATAMANAGER_H</div>
<div>> +</div><div>> +#include "FunctionCoverageMapping.h"</div><div>> +#include "llvm/ProfileData/CoverageMapping.h"</div><div>> +#include <vector></div><div>> +#include <unordered_map></div>
<div>> +</div><div>> +namespace llvm {</div><div>> +</div><div>> +/// \brief Partions mapping regions by their kind and sums</div><div>> +/// the execution counts of the regions that start at the same location.</div>
<div>> +class SourceCoverageDataManager {</div><div>> +public:</div><div>> + struct SourceRange {</div><div>> + unsigned LineStart, ColumnStart, LineEnd, ColumnEnd;</div><div>> +</div><div>> + SourceRange(unsigned LineStart, unsigned ColumnStart, unsigned LineEnd,</div>
<div>> + unsigned ColumnEnd)</div><div>> + : LineStart(LineStart), ColumnStart(ColumnStart), LineEnd(LineEnd),</div><div>> + ColumnEnd(ColumnEnd) {}</div><div>> +</div><div>> + bool operator==(const SourceRange &Other) const {</div>
<div>> + return LineStart == Other.LineStart && ColumnStart == Other.ColumnStart &&</div><div>> + LineEnd == Other.LineEnd && ColumnEnd == Other.ColumnEnd;</div><div>> + }</div>
<div>> +</div><div>> + bool operator<(const SourceRange &Other) const {</div><div>> + if (LineStart == Other.LineStart)</div><div>> + return ColumnStart < Other.ColumnStart;</div><div>
> + return LineStart < Other.LineStart;</div><div>> + }</div><div>> +</div><div>> + bool contains(const SourceRange &Other) {</div><div>> + if (LineStart > Other.LineStart ||</div>
<div>> + (LineStart == Other.LineStart && ColumnStart > Other.ColumnStart))</div><div>> + return false;</div><div>> + if (LineEnd < Other.LineEnd ||</div><div>> + (LineEnd == Other.LineEnd && ColumnEnd < Other.ColumnEnd))</div>
<div>> + return false;</div><div>> + return true;</div><div>> + }</div><div>> +</div><div>> + struct Hash {</div><div>> + size_t operator()(const SourceRange &Range) const {</div>
<div>> + return Range.LineStart + (Range.ColumnStart << 9) +</div><div>> + (Range.LineEnd << 16) + (Range.ColumnEnd << 25);</div><div><br></div><div>Where do these numbers come from? This needs some comments at the very</div>
<div>least.</div><div><br></div><div>> + }</div><div>> + };</div><div>> + };</div><div>> +</div><div>> +protected:</div><div>> + std::unordered_map<SourceRange, uint64_t, SourceRange::Hash> Regions;</div>
<div>> + std::vector<std::pair<SourceRange, uint64_t>> SortedRegions;</div><div>> + std::vector<SourceRange> SkippedRegions;</div><div>> +</div><div>> +public:</div><div>> + void insert(const MappingRegion &Region);</div>
<div>> +</div><div>> + /// \brief Return the source ranges and execution counts</div><div>> + /// obtained from the non-skipped mapping regions.</div><div>> + ArrayRef<std::pair<SourceRange, uint64_t>> getSourceRegions();</div>
<div>> +</div><div>> + /// \brief Return the source ranges obtained from the skipped mapping regions.</div><div>> + ArrayRef<SourceRange> getSkippedRegions() const { return SkippedRegions; }</div><div>> +};</div>
<div>> +</div><div>> +} // namespace llvm</div><div>> +</div><div>> +#endif // SOURCECOVERAGEDATAMANAGER_H</div><div>> Index: tools/llvm-cov/SourceCoverageView.cpp</div><div>> ===================================================================</div>
<div>> --- /dev/null</div><div>> +++ tools/llvm-cov/SourceCoverageView.cpp</div><div>> @@ -0,0 +1,390 @@</div><div>> +//===- SourceCoverageView.cpp - Code coverage view for source code --------===//</div><div>
> +//</div><div>> +// The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// This class implements rendering for code coverage of source code.</div><div>
> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#include "SourceCoverageView.h"</div><div>> +#include "llvm/ADT/SmallString.h"</div>
<div>> +</div><div>> +using namespace llvm;</div><div>> +</div><div>> +void SourceCoverageView::skipToLine(StringRef &Source, unsigned LineToFind) {</div><div>> + if (LineToFind < 2)</div><div>> + return;</div>
<div>> +</div><div>> + unsigned Line = 1;</div><div>> + for (size_t I = 0; I < Source.size();) {</div><div>> + if (Source[I] != '\n') {</div><div>> + ++I;</div><div>> + continue;</div>
<div>> + }</div><div>> + ++Line, ++I;</div><div>> + if (Line == LineToFind) {</div><div>> + Source = Source.substr(I);</div><div>> + return;</div><div>> + }</div><div>> + }</div>
<div>> + Source = StringRef();</div><div>> +}</div><div>> +</div><div>> +StringRef SourceCoverageView::getNextLine(StringRef &Source) {</div><div>> + for (size_t I = 0; I < Source.size(); ++I) {</div>
<div>> + if (Source[I] == '\n') {</div><div>> + auto Line = Source.substr(0, I);</div><div>> + Source = Source.substr(I + 1);</div><div>> + return Line;</div><div>> + }</div><div>
> + }</div><div>> + // This must be the last line</div><div>> + return Source;</div><div>> +}</div><div><br></div><div>These functions look pretty similar to llvm/Support/LineIterator's</div><div>behaviour. Can we avoid some code duplication by using that interface</div>
<div>instead?</div><div><br></div><div>> +</div><div>> +void SourceCoverageView::renderLine(raw_ostream &OS, StringRef Line,</div><div>> + ArrayRef<HighlightRange> Ranges) {</div>
<div>> + if (Ranges.empty()) {</div><div>> + OS << Line << "\n";</div><div>> + return;</div><div>> + }</div><div>> + if (Line.empty())</div><div>> + Line = " ";</div>
<div>> +</div><div>> + unsigned PrevColumnStart = 0;</div><div>> + unsigned Start = 1;</div><div>> + for (const auto &Range : Ranges) {</div><div>> + if (PrevColumnStart == Range.ColumnStart)</div>
<div>> + continue;</div><div>> +</div><div>> + // Show the unhighlighted part</div><div>> + unsigned ColumnStart = PrevColumnStart = Range.ColumnStart;</div><div>> + OS << Line.substr(Start - 1, ColumnStart - Start);</div>
<div>> +</div><div>> + // Show the highlighted part</div><div>> + auto Color = Range.Kind == HighlightRange::NotCovered ? raw_ostream::RED</div><div>> + : raw_ostream::CYAN;</div>
<div>> + OS.changeColor(Color, false, true);</div><div>> + unsigned ColumnEnd = std::min(Range.ColumnEnd, (unsigned)Line.size() + 1);</div><div>> + OS << Line.substr(ColumnStart - 1, ColumnEnd - ColumnStart);</div>
<div>> + Start = ColumnEnd;</div><div>> + OS.resetColor();</div><div>> + }</div><div>> +</div><div>> + // Show the rest of the line</div><div>> + OS << Line.substr(Start - 1, Line.size() - Start + 1);</div>
<div>> + OS << "\n";</div><div>> +}</div><div>> +</div><div>> +void SourceCoverageView::renderOffset(raw_ostream &OS, unsigned I) {</div><div>> + for (unsigned J = 0; J < I; ++J)</div>
<div>> + OS << " |";</div><div>> +}</div><div>> +</div><div>> +void SourceCoverageView::renderViewDivider(unsigned Offset, unsigned Length,</div><div>> + raw_ostream &OS) {</div>
<div>> + for (unsigned J = 1; J < Offset; ++J)</div><div>> + OS << " |";</div><div>> + if (Offset != 0)</div><div>> + OS.indent(2);</div><div>> + for (unsigned I = 0; I < Length; ++I)</div>
<div>> + OS << "-";</div><div>> +}</div><div>> +</div><div>> +void</div><div>> +SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS,</div><div>> + const LineCoverageInfo &Line) {</div>
<div>> + if (!Line.isMapped()) {</div><div>> + OS.indent(LineCoverageColumnWidth) << '|';</div><div>> + return;</div><div>> + }</div><div>> + SmallString<32> Buffer;</div><div>> + raw_svector_ostream BufferOS(Buffer);</div>
<div>> + BufferOS << Line.ExecutionCount;</div><div>> + auto Str = BufferOS.str();</div><div>> + if (Line.hasMultipleRegions() && Options.Colors)</div><div>> + OS.changeColor(raw_ostream::MAGENTA, false, false);</div>
<div>> + // Trim</div><div>> + Str = Str.substr(0, std::min(Str.size(), (size_t)LineCoverageColumnWidth));</div><div>> + // Align to the right</div><div>> + OS.indent(LineCoverageColumnWidth - Str.size()) << Str;</div>
<div>> + OS.resetColor();</div><div>> + OS << '|';</div><div>> +}</div><div>> +</div><div>> +void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS,</div><div>> + unsigned LineNo) {</div>
<div>> + SmallString<32> Buffer;</div><div>> + raw_svector_ostream BufferOS(Buffer);</div><div>> + BufferOS << LineNo;</div><div>> + auto Str = BufferOS.str();</div><div>> + // Trim and align to the right</div>
<div>> + Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));</div><div>> + OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';</div><div>> +}</div><div>> +</div>
<div>> +void SourceCoverageView::renderRegionMarkers(raw_ostream &OS,</div><div>> + ArrayRef<RegionMarker> Regions) {</div><div>> + SmallString<32> Buffer;</div>
<div>> + raw_svector_ostream BufferOS(Buffer);</div><div>> +</div><div>> + unsigned PrevColumn = 1;</div><div>> + for (const auto &Region : Regions) {</div><div>> + // Skip to the new region</div>
<div>> + if (Region.Column > PrevColumn)</div><div>> + OS.indent(Region.Column - PrevColumn);</div><div>> + PrevColumn = Region.Column + 1;</div><div>> + BufferOS << Region.ExecutionCount;</div>
<div>> + StringRef Str = BufferOS.str();</div><div>> + // Trim the execution count</div><div>> + Str = Str.substr(0, std::min(Str.size(), (size_t)7));</div><div>> + PrevColumn += Str.size();</div><div>
> + OS << '^' << Str;</div><div>> + Buffer.clear();</div><div>> + }</div><div>> + OS << "\n";</div><div>> +}</div><div>> +</div><div>> +/// \brief Insert a new highlighting range into the line's highlighting ranges</div>
<div>> +/// Return line's new highlighting ranges in result.</div><div>> +static void InsertHighlightRange(</div><div><br></div><div>Functions start with lower case letters, ie, insertHighlightRange.</div><div><br>
</div><div>> + ArrayRef<SourceCoverageView::HighlightRange> Ranges,</div><div>> + SourceCoverageView::HighlightRange RangeToInsert,</div><div>> + SmallVectorImpl<SourceCoverageView::HighlightRange> &Result) {</div>
<div>> + Result.clear();</div><div>> + for (const auto &Range : Ranges) {</div><div>> + if (!Range.overlaps(RangeToInsert)) {</div><div>> + Result.push_back(Range);</div><div>> + continue;</div>
<div>> + }</div><div>> +</div><div>> + if (RangeToInsert.contains(Range))</div><div>> + continue;</div><div>> + if (Range.contains(RangeToInsert)) {</div><div>> + // Split into 0 - 2 ranges</div>
<div>> + if (RangeToInsert.ColumnStart != Range.ColumnStart)</div><div>> + Result.push_back(SourceCoverageView::HighlightRange(</div><div>> + Range.Line, Range.ColumnStart, RangeToInsert.ColumnStart,</div>
<div>> + Range.Kind));</div><div>> + if (RangeToInsert.ColumnEnd != Range.ColumnEnd)</div><div>> + Result.push_back(SourceCoverageView::HighlightRange(</div><div>> + Range.Line, RangeToInsert.ColumnEnd, Range.ColumnEnd, Range.Kind));</div>
<div>> + } else if (Range.columnStartOverlaps(RangeToInsert)) {</div><div>> + if (RangeToInsert.ColumnStart != Range.ColumnStart)</div><div>> + Result.push_back(SourceCoverageView::HighlightRange(</div>
<div>> + Range.Line, Range.ColumnStart, RangeToInsert.ColumnStart,</div><div>> + Range.Kind));</div><div>> + } else {</div><div>> + if (RangeToInsert.ColumnEnd != Range.ColumnEnd)</div>
<div>> + Result.push_back(SourceCoverageView::HighlightRange(</div><div>> + Range.Line, RangeToInsert.ColumnEnd, Range.ColumnEnd, Range.Kind));</div><div>> + }</div><div>> + }</div><div>> + Result.push_back(RangeToInsert);</div>
<div>> + std::sort(Result.begin(), Result.end());</div><div><br></div><div>Do we really need to sort here? AFAICT createHighlightRanges sorted</div><div>these already, so we should just be able to insert our range at the</div>
<div>right point above, no?</div><div><br></div><div>Similarly, I think this logic can be simplified if the input is</div><div>guaranteed to already be sorted.</div><div><br></div><div>> +}</div><div>> +</div><div>> +void SourceCoverageView::sortChildren() {</div>
<div>> + for (auto &I : Children)</div><div>> + I->sortChildren();</div><div>> + std::sort(Children.begin(), Children.end(),</div><div>> + [](const std::unique_ptr<SourceCoverageView> &LHS,</div>
<div>> + const std::unique_ptr<SourceCoverageView> &RHS) {</div><div>> + return LHS->ExpansionRegion < RHS->ExpansionRegion;</div><div>> + });</div><div>> +}</div><div>> +</div>
<div>> +SourceCoverageView::HighlightRange</div><div>> +SourceCoverageView::getExpansionHighlightRange() const {</div><div>> + return HighlightRange(ExpansionRegion.LineStart, ExpansionRegion.ColumnStart,</div><div>
> + ExpansionRegion.ColumnEnd, HighlightRange::Expanded);</div><div>> +}</div><div>> +</div><div>> +template <typename T></div><div>> +ArrayRef<T> gatherLineItems(size_t &CurrentIdx, const std::vector<T> &Items,</div>
<div>> + unsigned LineNo) {</div><div>> + auto PrevIdx = CurrentIdx;</div><div>> + for (auto S = Items.size();</div><div>> + CurrentIdx < S && Items[CurrentIdx].Line == LineNo; ++CurrentIdx)</div>
<div>> + ;</div><div><br></div><div>This is simpler using "while".</div><div><br></div><div>> + return ArrayRef<T>(Items.data() + PrevIdx, CurrentIdx - PrevIdx);</div><div>> +}</div><div>> +</div>
<div>> +void SourceCoverageView::render(raw_ostream &OS, unsigned Offset) {</div><div><br></div><div>This whole function is confusing. Notably, what is going on with</div><div>ChildIterator? You iterator over all of the children at the end of the</div>
<div>first iteration of the outer (LineCount) loop, and you check if that's</div><div>already happened for some of the intermediate steps.</div><div><br></div><div>At the very least, this needs some comments explaining what and why</div>
<div>different parts of the function are doing what they're doing. I suspect</div><div>it might be possible to reorganize it so it's clearer in the first place</div><div>though.</div><div><br></div><div>> + sortChildren();</div>
<div>> +</div><div>> + SmallVector<HighlightRange, 8> AdjustedLineHighlightRanges;</div><div>> + auto ChildIterator = Children.begin();</div><div>> + size_t CurrentHighlightRange = 0;</div><div>> + size_t CurrentRegionMarker = 0;</div>
<div>> +</div><div>> + StringRef Source = File.getBuffer();</div><div>> + skipToLine(Source, LineStart);</div><div>> + unsigned CombinedColumnWidth =</div><div>> + (Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +</div>
<div>> + (Options.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);</div><div>> + unsigned DividerWidth = CombinedColumnWidth + 4;</div><div>> +</div><div>> + for (size_t I = 0; I < LineCount; ++I) {</div>
<div>> + unsigned LineNo = I + LineStart;</div><div>> +</div><div>> + renderOffset(OS, Offset);</div><div>> + if (Options.ShowLineStats)</div><div>> + renderLineCoverageColumn(OS, LineStats[I]);</div>
<div>> + if (Options.ShowLineNumbers)</div><div>> + renderLineNumberColumn(OS, LineNo);</div><div>> +</div><div>> + // Gather highlighting ranges</div><div>> + auto LineHighlightRanges =</div>
<div>> + gatherLineItems(CurrentHighlightRange, HighlightRanges, LineNo);</div><div>> + auto LineRanges = LineHighlightRanges;</div><div>> + if (ChildIterator != Children.end() &&</div><div>> + (*ChildIterator)->ExpansionRegion.LineStart == LineNo &&</div>
<div>> + Options.Colors) {</div><div>> + InsertHighlightRange(LineHighlightRanges,</div><div>> + (*ChildIterator)->getExpansionHighlightRange(),</div><div>> + AdjustedLineHighlightRanges);</div>
<div>> + LineRanges = AdjustedLineHighlightRanges;</div><div>> + }</div><div>> +</div><div>> + // Display the source code for the current line</div><div>> + StringRef Line = getNextLine(Source);</div>
<div>> + renderLine(OS, Line, LineRanges);</div><div>> +</div><div>> + // Show the region markers</div><div>> + bool ShowMarkers = !Options.ShowLineStatsOrRegionMarkers ||</div><div>> + LineStats[I].hasMultipleRegions();</div>
<div>> + auto LineMarkers = gatherLineItems(CurrentRegionMarker, Markers, LineNo);</div><div>> + if (ShowMarkers && !LineMarkers.empty()) {</div><div>> + renderOffset(OS, Offset);</div><div>> + OS.indent(CombinedColumnWidth);</div>
<div>> + renderRegionMarkers(OS, LineMarkers);</div><div>> + }</div><div>> +</div><div>> + // Show the embedded expanded child views</div><div>> + for (bool FirstChildExpansion = true;</div><div>
> + ChildIterator != Children.end() &&</div><div>> + (*ChildIterator)->ExpansionRegion.LineStart == LineNo;</div><div>> + ++ChildIterator) {</div><div>> + const auto &Child = *ChildIterator;</div>
<div>> + unsigned NewOffset = Offset + 1;</div><div>> +</div><div>> + if (Child->isInstantiationSubView()) {</div><div>> + if (FirstChildExpansion) {</div><div>> + renderViewDivider(NewOffset, CombinedColumnWidth + 4, OS);</div>
<div>> + OS << "\n";</div><div>> + }</div><div>> + renderOffset(OS, NewOffset);</div><div>> + OS << ' ';</div><div>> + if (Options.Colors)</div>
<div>> + OS.changeColor(raw_ostream::CYAN, false, false);</div><div>> + OS << Child->FunctionName << ":";</div><div>> + OS.resetColor();</div><div>> + OS << "\n";</div>
<div>> + }</div><div>> +</div><div>> + if (FirstChildExpansion)</div><div>> + FirstChildExpansion = false;</div><div>> + else if (Child->isExpansionSubView()) {</div><div>> + // Re-render the current line</div>
<div>> + InsertHighlightRange(LineHighlightRanges,</div><div>> + Child->getExpansionHighlightRange(),</div><div>> + AdjustedLineHighlightRanges);</div>
<div>> + renderOffset(OS, Offset);</div><div>> + OS.indent(CombinedColumnWidth + (Offset == 0 ? 0 : 1));</div><div>> + renderLine(OS, Line, AdjustedLineHighlightRanges);</div><div>> + }</div>
<div>> +</div><div>> + if (Child->isExpansionSubView()) {</div><div>> + renderViewDivider(NewOffset, DividerWidth, OS);</div><div>> + OS << "\n";</div><div>> + }</div>
<div>> + Child->render(OS, NewOffset);</div><div>> + renderViewDivider(NewOffset, DividerWidth, OS);</div><div>> + OS << "\n";</div><div>> + }</div><div>> + }</div><div>
> +}</div><div>> +</div><div>> +void</div><div>> +SourceCoverageView::createLineCoverageInfo(SourceCoverageDataManager &Data) {</div><div>> + LineStats.resize(LineCount);</div><div>> + for (const auto &Region : Data.getSourceRegions()) {</div>
<div>> + auto Value = Region.second;</div><div>> + LineStats[Region.first.LineStart - LineStart].addRegionStartCount(Value);</div><div>> + for (unsigned Line = Region.first.LineStart + 1;</div><div>> + Line <= Region.first.LineEnd; ++Line)</div>
<div>> + LineStats[Line - LineStart].addRegionCount(Value);</div><div>> + }</div><div>> +</div><div>> + // Reset the line stats for skipped regions</div><div>> + for (const auto &Region : Data.getSkippedRegions()) {</div>
<div>> + for (unsigned Line = Region.LineStart; Line <= Region.LineEnd; ++Line)</div><div>> + LineStats[Line - LineStart] = LineCoverageInfo();</div><div>> + }</div><div>> +}</div><div>> +</div>
<div>> +void</div><div>> +SourceCoverageView::createHighlightRanges(SourceCoverageDataManager &Data) {</div><div>> + auto Regions = Data.getSourceRegions();</div><div>> + std::vector<bool> AlreadyHighlighted;</div>
<div>> + AlreadyHighlighted.resize(Regions.size(), false);</div><div>> +</div><div>> + for (size_t I = 0, S = Regions.size(); I < S; ++I) {</div><div>> + const auto &Region = Regions[I];</div><div>
> + auto Value = Region.second;</div><div>> + auto SrcRange = Region.first;</div><div>> + if (Value != 0)</div><div>> + continue;</div><div>> + if (AlreadyHighlighted[I])</div><div>> + continue;</div>
<div>> + for (size_t J = 0; J < S; ++J) {</div><div>> + if (SrcRange.contains(Regions[J].first)) {</div><div>> + AlreadyHighlighted[J] = true;</div><div>> + }</div><div>> + }</div>
<div>> + if (Options.Debug)</div><div>> + outs() << "Highlighted range " << Value << ", "</div><div>> + << " " << SrcRange.LineStart << ":" << SrcRange.ColumnStart</div>
<div>> + << " -> " << SrcRange.LineEnd << ":" << SrcRange.ColumnEnd << "\n";</div><div>> + if (SrcRange.LineStart == SrcRange.LineEnd) {</div>
<div>> + HighlightRanges.push_back(HighlightRange(</div><div>> + SrcRange.LineStart, SrcRange.ColumnStart, SrcRange.ColumnEnd));</div><div>> + continue;</div><div>> + }</div><div>> + HighlightRanges.push_back(</div>
<div>> + HighlightRange(SrcRange.LineStart, SrcRange.ColumnStart,</div><div>> + std::numeric_limits<unsigned>::max()));</div><div>> + HighlightRanges.push_back(</div><div>> + HighlightRange(SrcRange.LineEnd, 1, SrcRange.ColumnEnd));</div>
<div>> + for (unsigned Line = SrcRange.LineStart + 1; Line < SrcRange.LineEnd;</div><div>> + ++Line) {</div><div>> + HighlightRanges.push_back(</div><div>> + HighlightRange(Line, 1, std::numeric_limits<unsigned>::max()));</div>
<div>> + }</div><div>> + }</div><div>> +</div><div>> + std::sort(HighlightRanges.begin(), HighlightRanges.end());</div><div>> +}</div><div>> +</div><div>> +void SourceCoverageView::createRegionMarkers(SourceCoverageDataManager &Data) {</div>
<div>> + for (const auto &Region : Data.getSourceRegions()) {</div><div>> + if (Region.first.LineStart >= LineStart)</div><div>> + Markers.push_back(RegionMarker(Region.first.LineStart,</div><div>
> + Region.first.ColumnStart, Region.second));</div><div>> + }</div><div>> +}</div><div>> +</div><div>> +void SourceCoverageView::load(SourceCoverageDataManager &Data) {</div>
<div>> + if (Options.ShowLineStats)</div><div>> + createLineCoverageInfo(Data);</div><div>> + if (Options.Colors)</div><div>> + createHighlightRanges(Data);</div><div>> + if (Options.ShowRegionMarkers)</div>
<div>> + createRegionMarkers(Data);</div><div>> +}</div><div>> Index: tools/llvm-cov/SourceCoverageView.h</div><div>> ===================================================================</div><div>> --- /dev/null</div>
<div>> +++ tools/llvm-cov/SourceCoverageView.h</div><div>> @@ -0,0 +1,215 @@</div><div>> +//===- SourceCoverageView.h - Code coverage view for source code ----------===//</div><div>> +//</div><div>> +// The LLVM Compiler Infrastructure</div>
<div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +//</div><div>> +// This class implements rendering for code coverage of source code.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>
> +</div><div>> +#ifndef SOURCECOVERAGEVIEW_H</div><div>> +#define SOURCECOVERAGEVIEW_H</div><div>> +</div><div>> +#include "CoverageViewOptions.h"</div><div>> +#include "SourceCoverageDataManager.h"</div>
<div>> +#include "llvm/ProfileData/CoverageMapping.h"</div><div>> +#include "llvm/Support/MemoryBuffer.h"</div><div>> +#include <vector></div><div>> +</div><div>> +namespace llvm {</div>
<div>> +</div><div>> +/// \brief A code coverage view of a specific source file.</div><div>> +/// It can have embedded coverage views.</div><div>> +class SourceCoverageView {</div><div>> +public:</div><div>
> + enum SubViewKind { View, ExpansionView, InstantiationView };</div><div>> +</div><div>> + /// \brief Coverage information for a single line</div><div>> + struct LineCoverageInfo {</div><div>> + uint64_t ExecutionCount;</div>
<div>> + unsigned RegionCount;</div><div>> + bool mapped;</div><div><br></div><div>Why not capitalize mapped?</div><div><br></div><div>> +</div><div>> + LineCoverageInfo() : ExecutionCount(0), RegionCount(0), mapped(false) {}</div>
<div>> +</div><div>> + bool isMapped() const { return mapped; }</div><div>> +</div><div>> + bool hasMultipleRegions() const { return RegionCount > 1; }</div><div>> +</div><div>> + void addRegionStartCount(uint64_t Count) {</div>
<div>> + mapped = true;</div><div>> + ExecutionCount = Count;</div><div>> + ++RegionCount;</div><div>> + }</div><div>> +</div><div>> + void addRegionCount(uint64_t Count) {</div><div>
> + mapped = true;</div><div>> + ExecutionCount = Count;</div><div>> + }</div><div>> + };</div><div>> +</div><div>> + /// \brief A marker that points at the start</div><div>> + /// of a specific mapping region.</div>
<div>> + struct RegionMarker {</div><div>> + unsigned Line, Column;</div><div>> + uint64_t ExecutionCount;</div><div>> +</div><div>> + RegionMarker(unsigned Line, unsigned Column, uint64_t Value)</div>
<div>> + : Line(Line), Column(Column), ExecutionCount(Value) {}</div><div>> + };</div><div>> +</div><div>> + /// \brief A single line source range used to</div><div>> + /// render highlighted text.</div>
<div>> + struct HighlightRange {</div><div>> + enum HighlightKind {</div><div>> + /// The code that wasn't executed</div><div>> + NotCovered,</div><div>> +</div><div>> + /// The region of code that was expanded</div>
<div>> + Expanded</div><div>> + };</div><div>> + HighlightKind Kind;</div><div>> + unsigned Line;</div><div>> + unsigned ColumnStart;</div><div>> + unsigned ColumnEnd;</div><div>> +</div>
<div>> + HighlightRange(unsigned Line, unsigned ColumnStart, unsigned ColumnEnd,</div><div>> + HighlightKind Kind = NotCovered)</div><div>> + : Kind(Kind), Line(Line), ColumnStart(ColumnStart),</div>
<div>> + ColumnEnd(ColumnEnd) {}</div><div>> +</div><div>> + bool operator<(const HighlightRange &Other) const {</div><div>> + if (Line == Other.Line)</div><div>> + return ColumnStart < Other.ColumnStart;</div>
<div>> + return Line < Other.Line;</div><div>> + }</div><div>> +</div><div>> + bool columnStartOverlaps(const HighlightRange &Other) const {</div><div>> + return ColumnStart <= Other.ColumnStart && ColumnEnd > Other.ColumnStart;</div>
<div>> + }</div><div>> + bool columnEndOverlaps(const HighlightRange &Other) const {</div><div>> + return ColumnEnd >= Other.ColumnEnd && ColumnStart < Other.ColumnEnd;</div><div>> + }</div>
<div>> + bool contains(const HighlightRange &Other) const {</div><div>> + if (Line != Other.Line)</div><div>> + return false;</div><div>> + return ColumnStart <= Other.ColumnStart && ColumnEnd >= Other.ColumnEnd;</div>
<div>> + }</div><div>> +</div><div>> + bool overlaps(const HighlightRange &Other) const {</div><div>> + if (Line != Other.Line)</div><div>> + return false;</div><div>> + return columnStartOverlaps(Other) || columnEndOverlaps(Other);</div>
<div>> + }</div><div>> + };</div><div>> +</div><div>> +private:</div><div>> + const MemoryBuffer &File;</div><div>> + const CoverageViewOptions &Options;</div><div>> + unsigned LineStart, LineCount;</div>
<div>> + SubViewKind Kind;</div><div>> + coverage::CounterMappingRegion ExpansionRegion;</div><div>> + std::vector<std::unique_ptr<SourceCoverageView>> Children;</div><div>> + std::vector<LineCoverageInfo> LineStats;</div>
<div>> + std::vector<HighlightRange> HighlightRanges;</div><div>> + std::vector<RegionMarker> Markers;</div><div>> + StringRef FunctionName;</div><div>> +</div><div>> + /// \brief Create the line coverage information using the coverage data</div>
<div>> + void createLineCoverageInfo(SourceCoverageDataManager &Data);</div><div>> +</div><div>> + /// \brief Create the line highlighting ranges using the coverage data</div><div>> + void createHighlightRanges(SourceCoverageDataManager &Data);</div>
<div>> +</div><div>> + /// \brief Create the region markers using the coverage data</div><div>> + void createRegionMarkers(SourceCoverageDataManager &Data);</div><div>> +</div><div>> + /// \brief Sort children by the starting location.</div>
<div>> + void sortChildren();</div><div>> +</div><div>> + /// \brief Update the given source string to start at the given line</div><div>> + void skipToLine(StringRef &Source, unsigned LineToFind);</div>
<div>> +</div><div>> + /// \brief Return the current source line and update the source</div><div>> + /// string to start from the next line</div><div>> + StringRef getNextLine(StringRef &Source);</div><div>
> +</div><div>> + /// \brief Return a highlight range for the expansion region of this view.</div><div>> + HighlightRange getExpansionHighlightRange() const;</div><div>> +</div><div>> + /// \brief Render a source line with highlighting.</div>
<div>> + void renderLine(raw_ostream &OS, StringRef Line,</div><div>> + ArrayRef<HighlightRange> Ranges);</div><div>> +</div><div>> + void renderOffset(raw_ostream &OS, unsigned I);</div>
<div>> +</div><div>> + void renderViewDivider(unsigned Offset, unsigned Length, raw_ostream &OS);</div><div>> +</div><div>> + /// \brief Render the line's execution count column</div><div>> + void renderLineCoverageColumn(raw_ostream &OS, const LineCoverageInfo &Line);</div>
<div>> +</div><div>> + /// \brief Render the line number column</div><div>> + void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo);</div><div>> +</div><div>> + /// \brief Render all the region's execution counts on a line</div>
<div>> + void renderRegionMarkers(raw_ostream &OS, ArrayRef<RegionMarker> Regions);</div><div>> +</div><div>> + static const unsigned LineCoverageColumnWidth = 7;</div><div>> + static const unsigned LineNumberColumnWidth = 5;</div>
<div>> +</div><div>> +public:</div><div>> + SourceCoverageView(const MemoryBuffer &File,</div><div>> + const CoverageViewOptions &Options)</div><div>> + : File(File), Options(Options), LineStart(1), Kind(View),</div>
<div>> + ExpansionRegion(coverage::Counter(), 0, 0, 0, 0, 0) {</div><div>> + LineCount = File.getBuffer().count('\n') + 1;</div><div>> + }</div><div>> +</div><div>> + SourceCoverageView(const MemoryBuffer &File,</div>
<div>> + const CoverageViewOptions &Options, unsigned LineStart,</div><div>> + unsigned LineEnd)</div><div>> + : File(File), Options(Options), LineStart(LineStart),</div>
<div>> + LineCount(LineEnd - LineStart + 1), Kind(View),</div><div>> + ExpansionRegion(coverage::Counter(), 0, 0, 0, 0, 0) {}</div><div>> +</div><div>> + SourceCoverageView(SourceCoverageView &Parent, unsigned LineStart,</div>
<div>> + unsigned LineEnd, StringRef FunctionName)</div><div>> + : File(Parent.File), Options(Parent.Options), LineStart(LineStart),</div><div>> + LineCount(LineEnd - LineStart + 1), Kind(InstantiationView),</div>
<div>> + ExpansionRegion(coverage::Counter(), 0, LineEnd, 0, LineEnd, 0),</div><div>> + FunctionName(FunctionName) {}</div><div>> +</div><div>> + SourceCoverageView(const MemoryBuffer &File,</div>
<div>> + const CoverageViewOptions &Options, unsigned LineStart,</div><div>> + unsigned LineEnd,</div><div>> + const coverage::CounterMappingRegion &ExpansionRegion)</div>
<div>> + : File(File), Options(Options), LineStart(LineStart),</div><div>> + LineCount(LineEnd - LineStart + 1), Kind(ExpansionView),</div><div>> + ExpansionRegion(ExpansionRegion) {}</div><div>
> +</div><div>> + const CoverageViewOptions &getOptions() const { return Options; }</div><div>> +</div><div>> + bool isExpansionSubView() const { return Kind == ExpansionView; }</div><div>> +</div><div>
> + bool isInstantiationSubView() const { return Kind == InstantiationView; }</div><div>> +</div><div>> + void addChild(SourceCoverageView *view) {</div><div>> + Children.push_back(std::unique_ptr<SourceCoverageView>(view));</div>
<div>> + }</div><div><br></div><div>As I mentioned above, better to take a unique_ptr argument so that it's</div><div>obvious that ownership is passed.</div><div><br></div><div>> +</div><div>> + /// \brief Print the code coverage information for a specific</div>
<div>> + /// portion of a source file to the output stream.</div><div>> + void render(raw_ostream &OS, unsigned Offset = 0);</div><div>> +</div><div>> + /// \brief Load the coverage information required for rendering</div>
<div>> + /// from the mapping regions in the data manager.</div><div>> + void load(SourceCoverageDataManager &Data);</div><div>> +};</div><div>> +</div><div>> +} // namespace llvm</div><div>> +</div>
<div>> +#endif // SOURCECOVERAGEVIEW_H</div><div>> Index: tools/llvm-cov/SourceLanguages.cpp</div><div>> ===================================================================</div><div>> --- /dev/null</div><div>
> +++ tools/llvm-cov/SourceLanguages.cpp</div><div>> @@ -0,0 +1,93 @@</div><div>> +//===- SourceLanguages.cpp - Language specific operations ---------------===//</div><div>> +//</div><div>> +// The LLVM Compiler Infrastructure</div>
<div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +//</div><div>> +// These classes provide various language specific operations.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div><br>
</div><div>As far as I can tell, this really only provides "demangle", and I'm not</div><div>entirely convinced this is even a good idea. This is *very* brittle and</div><div>pretty awkward to extend, and really difficult to make general enough to</div>
<div>be valuable. How much is it really gaining us?</div><div><br></div><div>> +</div><div>> +#include "SourceLanguages.h"</div><div>> +#include "llvm/ADT/StringRef.h"</div><div>> +#include "llvm/Support/Path.h"</div>
<div>> +</div><div>> +using namespace llvm;</div><div>> +</div><div>> +SourceLanguages::SourceLanguages() {</div><div>> + Languages[SourceLanguage::Unknown].reset(new SourceLanguage);</div><div>> + Languages[SourceLanguage::CXX].reset(new CXXSourceLanguage);</div>
<div>> + Languages[SourceLanguage::ObjectiveC].reset(new ObjectiveCSourceLanguage);</div><div>> +}</div><div>> +</div><div>> +SourceLanguage *SourceLanguages::get(const FunctionCoverageMapping &Function) {</div>
<div>> + unsigned Scores[SourceLanguage::NumLanguages];</div><div>> + for (unsigned I = 0; I < SourceLanguage::NumLanguages; ++I)</div><div>> + Scores[I] = 0;</div><div>> + for (unsigned I = 0; I < SourceLanguage::NumLanguages; ++I) {</div>
<div>> + if (Languages[I]->matchMangledName(Function.Name))</div><div>> + ++Scores[I];</div><div>> + for (const auto &File : Function.Filenames) {</div><div>> + if (Languages[I]->matchFileExtension(sys::path::extension(File)))</div>
<div>> + ++Scores[I];</div><div>> + }</div><div>> + }</div><div>> + unsigned BestScore = 0;</div><div>> + SourceLanguage *Match = Languages[SourceLanguage::Unknown].get();</div><div>> + for (unsigned I = 0; I < SourceLanguage::NumLanguages; ++I) {</div>
<div>> + if (Scores[I] > BestScore) {</div><div>> + BestScore = Scores[I];</div><div>> + Match = Languages[I].get();</div><div>> + }</div><div>> + }</div><div>> + return Match;</div>
<div>> +}</div><div>> +</div><div>> +#if !defined(_MSC_VER)</div><div>> +// Assume that __cxa_demangle is provided by libcxxabi (except for Windows).</div><div>> +extern "C" char *__cxa_demangle(const char *mangled_name, char *output_buffer,</div>
<div>> + size_t *length, int *status);</div><div>> +#endif</div><div>> +</div><div>> +void CXXSourceLanguage::demangle(std::string &Name) {</div><div>> + StringRef NameRef(Name);</div>
<div>> + std::string FileName;</div><div>> + size_t ColonPos = NameRef.find_first_of(':');</div><div>> + if (ColonPos != StringRef::npos) {</div><div>> + FileName = std::string(Name.begin(), Name.begin() + ColonPos);</div>
<div>> + Name = std::string(Name.begin() + ColonPos + 1, Name.end());</div><div>> + }</div><div>> +#if !defined(_MSC_VER)</div><div>> + int status = 0;</div><div>> + char *DemangledName = __cxa_demangle(Name.c_str(), 0, 0, &status);</div>
<div>> + if (status != 0)</div><div>> + return;</div><div>> + Name = DemangledName;</div><div>> + free(DemangledName);</div><div>> +#endif</div><div>> + if (FileName.empty())</div><div>> + return;</div>
<div>> + Name = FileName + ": " + Name;</div><div>> +}</div><div>> +</div><div>> +bool CXXSourceLanguage::matchMangledName(StringRef Name) {</div><div>> + return Name.startswith("_Z");</div>
<div>> +}</div><div>> +</div><div>> +bool CXXSourceLanguage::matchFileExtension(StringRef Ext) {</div><div>> + return Ext.equals_lower(".h") || Ext.equals_lower(".cpp") ||</div><div>> + Ext.equals_lower(".cxx") || Ext.equals_lower(".hpp") ||</div>
<div>> + Ext.equals_lower(".cc") || Ext.equals_lower("hxx");</div><div>> +}</div><div>> +</div><div>> +bool ObjectiveCSourceLanguage::matchMangledName(StringRef Name) {</div><div>> + return Name.startswith("_c") || Name.startswith("_i");</div>
<div>> +}</div><div>> +</div><div>> +bool ObjectiveCSourceLanguage::matchFileExtension(StringRef Ext) {</div><div>> + return Ext.equals_lower(".m") || Ext.equals_lower(".h") ||</div><div>> + Ext.equals_lower(".mm");</div>
<div>> +}</div><div>> Index: tools/llvm-cov/SourceLanguages.h</div><div>> ===================================================================</div><div>> --- /dev/null</div><div>> +++ tools/llvm-cov/SourceLanguages.h</div>
<div>> @@ -0,0 +1,69 @@</div><div>> +//===- SourceLanguages.h - Language specific operations -----------------===//</div><div>> +//</div><div>> +// The LLVM Compiler Infrastructure</div><div>
> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +//</div><div>> +// These classes provide various language specific operations.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div>
<div>> +#ifndef SOURCELANGUAGES_H</div><div>> +#define SOURCELANGUAGES_H</div><div>> +</div><div>> +#include "FunctionCoverageMapping.h"</div><div>> +#include "llvm/ADT/StringRef.h"</div>
<div>> +#include <string></div><div>> +</div><div>> +namespace llvm {</div><div>> +</div><div>> +/// \brief Interface for language specific operations.</div><div>> +class SourceLanguage {</div><div>
> +public:</div><div>> + enum LanguageKind { Unknown, CXX, ObjectiveC, NumLanguages };</div><div>> +</div><div>> + virtual void demangle(std::string &Name) {}</div><div>> +</div><div>> + /// \brief Return true if the given name has this language's mangling.</div>
<div>> + virtual bool matchMangledName(StringRef Name) { return false; }</div><div>> +</div><div>> + /// \brief Return true if the given file extension</div><div>> + /// is the file extension used by the source files of this language.</div>
<div>> + virtual bool matchFileExtension(StringRef Ext) { return false; }</div><div>> +};</div><div>> +</div><div>> +/// \brief Manages all of the known source languages.</div><div>> +class SourceLanguages {</div>
<div>> + std::unique_ptr<SourceLanguage> Languages[SourceLanguage::NumLanguages];</div><div>> +</div><div>> +public:</div><div>> + SourceLanguages();</div><div>> +</div><div>> + /// \brief Return the source language object for the given function.</div>
<div>> + SourceLanguage *get(const FunctionCoverageMapping &Function);</div><div>> +};</div><div>> +</div><div>> +/// \brief Implementation of C++ language specific operations.</div><div>> +class CXXSourceLanguage : public SourceLanguage {</div>
<div>> +public:</div><div>> + void demangle(std::string &Name) override;</div><div>> +</div><div>> + bool matchMangledName(StringRef Name) override;</div><div>> +</div><div>> + bool matchFileExtension(StringRef Ext) override;</div>
<div>> +};</div><div>> +</div><div>> +/// \brief Implementation of Objective C language specific operations.</div><div>> +class ObjectiveCSourceLanguage : public SourceLanguage {</div><div>> +public:</div><div>
> + bool matchMangledName(StringRef Name) override;</div><div>> +</div><div>> + bool matchFileExtension(StringRef Ext) override;</div><div>> +};</div><div>> +</div><div>> +} // namespace llvm</div><div>
> +</div><div>> +#endif // SOURCELANGUAGES_H</div><div>> Index: tools/llvm-cov/llvm-cov.cpp</div><div>> ===================================================================</div><div>> --- tools/llvm-cov/llvm-cov.cpp</div>
<div>> +++ tools/llvm-cov/llvm-cov.cpp</div><div>> @@ -11,9 +11,59 @@</div><div>> //</div><div>> //===----------------------------------------------------------------------===//</div><div>></div><div>> +#include "llvm/ADT/StringRef.h"</div>
<div>> +#include "llvm/Support/raw_ostream.h"</div><div>> +#include "llvm/Support/Path.h"</div><div>> +#include <string></div><div>> +</div><div>> +using namespace llvm;</div><div>
> +</div><div>> +/// \brief The main function for the invocation of the tool with a 'show'</div><div>> +/// command</div><div>> +int show_main(int argc, const char **argv);</div><div>> +</div><div>> +/// \brief The main function for the invocation of the tool with a 'report'</div>
<div>> +/// command</div><div>> +int report_main(int argc, const char **argv);</div><div><br></div><div>These comments are a bit confusing. I'd prefer "The main entry point for</div><div>the 'show' subcommand" and similarly for 'report'.</div>
<div><br></div><div>> +</div><div>> /// \brief The main function for the gcov compatible coverage tool</div><div>> int gcov_main(int argc, const char **argv);</div><div>></div><div>> int main(int argc, const char **argv) {</div>
<div>> + // If argv[0] is or ends with 'gcov', always be gcov compatible</div><div>> + if (sys::path::stem(argv[0]).endswith_lower("gcov"))</div><div>> + return gcov_main(argc, argv);</div>
<div>> +</div><div>> + // Check if we are invoking a specific tool command.</div><div>> + if (argc > 1) {</div><div>> + int (*func)(int, const char **) = nullptr;</div><div>> +</div><div>> + StringRef command = argv[1];</div>
<div>> + if (command.equals_lower("show"))</div><div>> + func = show_main;</div><div>> + else if (command.equals_lower("report"))</div><div>> + func = report_main;</div><div>
> + else if (command.equals_lower("gcov"))</div><div>> + func = gcov_main;</div><div>> +</div><div>> + if (func) {</div><div>> + std::string Invocation(std::string(argv[0]) + " " + argv[1]);</div>
<div>> + argv[1] = Invocation.c_str();</div><div>> + return func(argc - 1, argv + 1);</div><div>> + }</div><div>> + }</div><div>> +</div><div>> + // Give a warning and fall back to gcov</div>
<div>> + errs().changeColor(raw_ostream::RED);</div><div>> + errs() << "warning: ";</div><div>> + if (argc > 1)</div><div>> + errs() << "Unrecognized command '" << argv[1] << "'.";</div>
<div>> + else</div><div>> + errs() << "No command given.";</div><div><br></div><div>If argc is 0, calling gcov_main isn't going to accomplish much (it needs</div><div>a filename at the very least) - maybe just spit out usage for that.</div>
<div><br></div><div>It would also be nice to avoid the "unrecognized command" prompt if the</div><div>argument starts with - or if it's a filename. There's diminishing</div><div>returns on how much effort to put in, but maybe only say unrecognized</div>
<div>command if the arg doesn't start with - and doesn't have a . in it?</div><div><br></div><div>> + errs() << " Using the gcov compatible mode "</div><div>> + "(this behaviour may be dropped in the future).";</div>
<div>> + errs().resetColor();</div><div>> + errs() << "\n";</div><div>> +</div><div>> return gcov_main(argc, argv);</div><div>> }</div><div><br></div></div><div class="gmail_extra"><br>
<br><div class="gmail_quote">On Fri, Jul 25, 2014 at 11:31 AM, Alex Lorenz <span dir="ltr"><<a href="mailto:arphaman@gmail.com" target="_blank">arphaman@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Split the patch and updated it to be based on <a href="http://reviews.llvm.org/D4673" target="_blank">http://reviews.llvm.org/D4673</a>.<br>
Added some minor fixes.<br>
<div class=""><br>
<a href="http://reviews.llvm.org/D4445" target="_blank">http://reviews.llvm.org/D4445</a><br>
<br>
Files:<br>
tools/llvm-cov/CMakeLists.txt<br>
tools/llvm-cov/CodeCoverage.cpp<br>
tools/llvm-cov/CoverageFilters.cpp<br>
tools/llvm-cov/CoverageFilters.h<br>
tools/llvm-cov/CoverageReport.cpp<br>
tools/llvm-cov/CoverageReport.h<br>
tools/llvm-cov/CoverageSummary.cpp<br>
tools/llvm-cov/CoverageSummary.h<br>
tools/llvm-cov/CoverageSummaryInfo.cpp<br>
tools/llvm-cov/CoverageSummaryInfo.h<br>
tools/llvm-cov/CoverageViewOptions.h<br>
tools/llvm-cov/FunctionCoverageMapping.h<br>
tools/llvm-cov/LLVMBuild.txt<br>
tools/llvm-cov/Makefile<br>
tools/llvm-cov/SourceCoverageDataManager.cpp<br>
tools/llvm-cov/SourceCoverageDataManager.h<br>
tools/llvm-cov/SourceCoverageView.cpp<br>
tools/llvm-cov/SourceCoverageView.h<br>
tools/llvm-cov/SourceLanguages.cpp<br>
tools/llvm-cov/SourceLanguages.h<br>
</div> tools/llvm-cov/llvm-cov.cpp<br>
</blockquote></div><br></div>