From a9bf312be4c22287bc30af0c730f4c2544cfb018 Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Sat, 25 Jun 2016 02:58:30 +0000 Subject: [PATCH] [llvm-cov] Separate presentation logic from formatting logic, NFC This makes it easier to add renderers for new kinds of output formats. - Define and document a pure-virtual coverage rendering interface. - Move the text-based rendering logic into its a new file. - Re-work the API to better reflect the presentation/formatting split. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@273767 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/llvm-cov/CMakeLists.txt | 1 + tools/llvm-cov/CodeCoverage.cpp | 28 ++-- tools/llvm-cov/SourceCoverageView.cpp | 191 ++++----------------- tools/llvm-cov/SourceCoverageView.h | 92 +++++++---- tools/llvm-cov/SourceCoverageViewText.cpp | 193 ++++++++++++++++++++++ tools/llvm-cov/SourceCoverageViewText.h | 61 +++++++ 6 files changed, 364 insertions(+), 202 deletions(-) create mode 100644 tools/llvm-cov/SourceCoverageViewText.cpp create mode 100644 tools/llvm-cov/SourceCoverageViewText.h diff --git a/tools/llvm-cov/CMakeLists.txt b/tools/llvm-cov/CMakeLists.txt index f8b7fa666ea..c8d64c08512 100644 --- a/tools/llvm-cov/CMakeLists.txt +++ b/tools/llvm-cov/CMakeLists.txt @@ -8,5 +8,6 @@ add_llvm_tool(llvm-cov CoverageReport.cpp CoverageSummaryInfo.cpp SourceCoverageView.cpp + SourceCoverageViewText.cpp TestingSupport.cpp ) diff --git a/tools/llvm-cov/CodeCoverage.cpp b/tools/llvm-cov/CodeCoverage.cpp index a59860b957a..6ca2141508a 100644 --- a/tools/llvm-cov/CodeCoverage.cpp +++ b/tools/llvm-cov/CodeCoverage.cpp @@ -132,9 +132,9 @@ CodeCoverageTool::attachExpansionSubViews(SourceCoverageView &View, continue; auto SubViewExpansions = ExpansionCoverage.getExpansions(); - auto SubView = llvm::make_unique( - Expansion.Function.Name, SourceBuffer.get(), ViewOpts, - std::move(ExpansionCoverage)); + auto SubView = + SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(), + ViewOpts, std::move(ExpansionCoverage)); attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); View.addExpansion(Expansion.Region, std::move(SubView)); } @@ -151,8 +151,8 @@ CodeCoverageTool::createFunctionView(const FunctionRecord &Function, return nullptr; auto Expansions = FunctionCoverage.getExpansions(); - auto View = llvm::make_unique( - Function.Name, SourceBuffer.get(), ViewOpts, std::move(FunctionCoverage)); + auto View = SourceCoverageView::create(Function.Name, SourceBuffer.get(), + ViewOpts, std::move(FunctionCoverage)); attachExpansionSubViews(*View, Expansions, Coverage); return View; @@ -169,16 +169,16 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile, return nullptr; auto Expansions = FileCoverage.getExpansions(); - auto View = llvm::make_unique( - SourceFile, SourceBuffer.get(), ViewOpts, std::move(FileCoverage)); + auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(), + ViewOpts, std::move(FileCoverage)); attachExpansionSubViews(*View, Expansions, Coverage); for (auto Function : Coverage.getInstantiations(SourceFile)) { auto SubViewCoverage = Coverage.getCoverageForFunction(*Function); auto SubViewExpansions = SubViewCoverage.getExpansions(); - auto SubView = llvm::make_unique( - Function->Name, SourceBuffer.get(), ViewOpts, - std::move(SubViewCoverage)); + auto SubView = + SourceCoverageView::create(Function->Name, SourceBuffer.get(), ViewOpts, + std::move(SubViewCoverage)); attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); if (SubView) { @@ -428,8 +428,7 @@ int CodeCoverageTool::show(int argc, const char **argv, << "\n"; continue; } - mainView->renderSourceName(outs()); - mainView->render(outs(), /*WholeFile=*/false); + mainView->print(outs(), /*WholeFile=*/false, /*ShowSourceName=*/true); outs() << "\n"; } return 0; @@ -452,10 +451,7 @@ int CodeCoverageTool::show(int argc, const char **argv, continue; } - if (ShowFilenames) - mainView->renderSourceName(outs()); - - mainView->render(outs(), /*Wholefile=*/true); + mainView->print(outs(), /*Wholefile=*/true, /*ShowSourceName=*/ShowFilenames); if (SourceFiles.size() > 1) outs() << "\n"; } diff --git a/tools/llvm-cov/SourceCoverageView.cpp b/tools/llvm-cov/SourceCoverageView.cpp index 8aa99295a0f..c393ad42f4c 100644 --- a/tools/llvm-cov/SourceCoverageView.cpp +++ b/tools/llvm-cov/SourceCoverageView.cpp @@ -12,75 +12,14 @@ //===----------------------------------------------------------------------===// #include "SourceCoverageView.h" -#include "llvm/ADT/Optional.h" +#include "SourceCoverageViewText.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/LineIterator.h" using namespace llvm; -void SourceCoverageView::renderLine( - raw_ostream &OS, StringRef Line, int64_t LineNumber, - const coverage::CoverageSegment *WrappedSegment, - ArrayRef Segments, - unsigned ExpansionCol) { - Optional Highlight; - SmallVector, 2> HighlightedRanges; - - // The first segment overlaps from a previous line, so we treat it specially. - if (WrappedSegment && WrappedSegment->HasCount && WrappedSegment->Count == 0) - Highlight = raw_ostream::RED; - - // Output each segment of the line, possibly highlighted. - unsigned Col = 1; - for (const auto *S : Segments) { - unsigned End = std::min(S->Col, static_cast(Line.size()) + 1); - colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR, - getOptions().Colors && Highlight, /*Bold=*/false, - /*BG=*/true) - << Line.substr(Col - 1, End - Col); - if (getOptions().Debug && Highlight) - HighlightedRanges.push_back(std::make_pair(Col, End)); - Col = End; - if (Col == ExpansionCol) - Highlight = raw_ostream::CYAN; - else if (S->HasCount && S->Count == 0) - Highlight = raw_ostream::RED; - else - Highlight = None; - } - - // Show the rest of the line - colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR, - getOptions().Colors && Highlight, /*Bold=*/false, /*BG=*/true) - << Line.substr(Col - 1, Line.size() - Col + 1); - OS << "\n"; - - if (getOptions().Debug) { - for (const auto &Range : HighlightedRanges) - errs() << "Highlighted line " << LineNumber << ", " << Range.first - << " -> " << Range.second << "\n"; - if (Highlight) - errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n"; - } -} - -void SourceCoverageView::renderIndent(raw_ostream &OS, unsigned Level) { - for (unsigned I = 0; I < Level; ++I) - OS << " |"; -} - -void SourceCoverageView::renderViewDivider(unsigned Level, unsigned Length, - raw_ostream &OS) { - assert(Level != 0 && "Cannot render divider at top level"); - renderIndent(OS, Level - 1); - OS.indent(2); - for (unsigned I = 0; I < Length; ++I) - OS << "-"; -} - -/// Format a count using engineering notation with 3 significant digits. -static std::string formatCount(uint64_t N) { +std::string SourceCoverageView::formatCount(uint64_t N) { std::string Number = utostr(N); int Len = Number.size(); if (Len <= 3) @@ -95,63 +34,30 @@ static std::string formatCount(uint64_t N) { return Result; } -void -SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS, - const LineCoverageStats &Line) { - if (!Line.isMapped()) { - OS.indent(LineCoverageColumnWidth) << '|'; - return; - } - std::string C = formatCount(Line.ExecutionCount); - OS.indent(LineCoverageColumnWidth - C.size()); - colored_ostream(OS, raw_ostream::MAGENTA, - Line.hasMultipleRegions() && getOptions().Colors) - << C; - OS << '|'; +void SourceCoverageView::addExpansion( + const coverage::CounterMappingRegion &Region, + std::unique_ptr View) { + ExpansionSubViews.emplace_back(Region, std::move(View)); } -void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS, - unsigned LineNo) { - SmallString<32> Buffer; - raw_svector_ostream BufferOS(Buffer); - BufferOS << LineNo; - auto Str = BufferOS.str(); - // Trim and align to the right - Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth)); - OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|'; +void SourceCoverageView::addInstantiation( + StringRef FunctionName, unsigned Line, + std::unique_ptr View) { + InstantiationSubViews.emplace_back(FunctionName, Line, std::move(View)); } -void SourceCoverageView::renderRegionMarkers( - raw_ostream &OS, ArrayRef Segments) { - unsigned PrevColumn = 1; - for (const auto *S : Segments) { - if (!S->IsRegionEntry) - continue; - // Skip to the new region - if (S->Col > PrevColumn) - OS.indent(S->Col - PrevColumn); - PrevColumn = S->Col + 1; - std::string C = formatCount(S->Count); - PrevColumn += C.size(); - OS << '^' << C; - } - OS << "\n"; - - if (getOptions().Debug) - for (const auto *S : Segments) - errs() << "Marker at " << S->Line << ":" << S->Col << " = " - << formatCount(S->Count) << (S->IsRegionEntry ? "\n" : " (pop)\n"); +std::unique_ptr +SourceCoverageView::create(StringRef SourceName, const MemoryBuffer &File, + const CoverageViewOptions &Options, + coverage::CoverageData &&CoverageInfo) { + return llvm::make_unique(SourceName, File, Options, + std::move(CoverageInfo)); } -void SourceCoverageView::render(raw_ostream &OS, bool WholeFile, - unsigned IndentLevel) { - // The width of the leading columns - unsigned CombinedColumnWidth = - (getOptions().ShowLineStats ? LineCoverageColumnWidth + 1 : 0) + - (getOptions().ShowLineNumbers ? LineNumberColumnWidth + 1 : 0); - // The width of the line that is used to divide between the view and the - // subviews. - unsigned DividerWidth = CombinedColumnWidth + 4; +void SourceCoverageView::print(raw_ostream &OS, bool WholeFile, + bool ShowSourceName, unsigned ViewDepth) { + if (ShowSourceName) + renderSourceName(OS); // We need the expansions and instantiations sorted so we can go through them // while we iterate lines. @@ -192,10 +98,9 @@ void SourceCoverageView::render(raw_ostream &OS, bool WholeFile, LineCount.addRegionCount(WrappedSegment->Count); for (const auto *S : LineSegments) if (S->HasCount && S->IsRegionEntry) - LineCount.addRegionStartCount(S->Count); + LineCount.addRegionStartCount(S->Count); - // Render the line prefix. - renderIndent(OS, IndentLevel); + renderLinePrefix(OS, ViewDepth); if (getOptions().ShowLineStats) renderLineCoverageColumn(OS, LineCount); if (getOptions().ShowLineNumbers) @@ -208,62 +113,34 @@ void SourceCoverageView::render(raw_ostream &OS, bool WholeFile, ExpansionColumn = NextESV->getStartCol(); // Display the source code for the current line. - renderLine(OS, *LI, LI.line_number(), WrappedSegment, LineSegments, - ExpansionColumn); + renderLine(OS, {*LI, LI.line_number()}, WrappedSegment, LineSegments, + ExpansionColumn, ViewDepth); // Show the region markers. if (getOptions().ShowRegionMarkers && (!getOptions().ShowLineStatsOrRegionMarkers || LineCount.hasMultipleRegions()) && !LineSegments.empty()) { - renderIndent(OS, IndentLevel); - OS.indent(CombinedColumnWidth); - renderRegionMarkers(OS, LineSegments); + renderRegionMarkers(OS, LineSegments, ViewDepth); } // Show the expansions and instantiations for this line. - unsigned NestedIndent = IndentLevel + 1; bool RenderedSubView = false; for (; NextESV != EndESV && NextESV->getLine() == LI.line_number(); ++NextESV) { - renderViewDivider(NestedIndent, DividerWidth, OS); - OS << "\n"; - if (RenderedSubView) { - // Re-render the current line and highlight the expansion range for - // this subview. - ExpansionColumn = NextESV->getStartCol(); - renderIndent(OS, IndentLevel); - OS.indent(CombinedColumnWidth + (IndentLevel == 0 ? 0 : 1)); - renderLine(OS, *LI, LI.line_number(), WrappedSegment, LineSegments, - ExpansionColumn); - renderViewDivider(NestedIndent, DividerWidth, OS); - OS << "\n"; - } - // Render the child subview - if (getOptions().Debug) - errs() << "Expansion at line " << NextESV->getLine() << ", " - << NextESV->getStartCol() << " -> " << NextESV->getEndCol() - << "\n"; - NextESV->View->render(OS, false, NestedIndent); + renderViewDivider(OS, ViewDepth + 1); + ExpansionColumn = renderExpansionView( + OS, *NextESV, + RenderedSubView ? Optional({*LI, LI.line_number()}) + : Optional(), + ExpansionColumn, WrappedSegment, LineSegments, ViewDepth); RenderedSubView = true; } for (; NextISV != EndISV && NextISV->Line == LI.line_number(); ++NextISV) { - renderViewDivider(NestedIndent, DividerWidth, OS); - OS << "\n"; - renderIndent(OS, NestedIndent); - OS << ' '; - NextISV->View->renderSourceName(OS); - NextISV->View->render(OS, false, NestedIndent); + renderInstantiationView(OS, *NextISV, ViewDepth + 1); RenderedSubView = true; } - if (RenderedSubView) { - renderViewDivider(NestedIndent, DividerWidth, OS); - OS << "\n"; - } + if (RenderedSubView) + renderViewDivider(OS, ViewDepth + 1); } } - -void SourceCoverageView::renderSourceName(raw_ostream &OS) { - getOptions().colored_ostream(OS, raw_ostream::CYAN) << getSourceName() - << ":\n"; -} diff --git a/tools/llvm-cov/SourceCoverageView.h b/tools/llvm-cov/SourceCoverageView.h index 2e98151a70e..ef237d8e885 100644 --- a/tools/llvm-cov/SourceCoverageView.h +++ b/tools/llvm-cov/SourceCoverageView.h @@ -16,6 +16,7 @@ #include "CoverageViewOptions.h" #include "llvm/ProfileData/Coverage/CoverageMapping.h" +#include "llvm/ADT/Optional.h" #include "llvm/Support/MemoryBuffer.h" #include @@ -100,7 +101,6 @@ struct LineCoverageStats { /// \brief A code coverage view of a specific source file. /// It can have embedded coverage views. class SourceCoverageView { -private: /// A function or file name. StringRef SourceName; @@ -120,59 +120,93 @@ private: /// on display. std::vector InstantiationSubViews; +protected: + struct LineRef { + StringRef Line; + int64_t LineNo; + + LineRef(StringRef Line, int64_t LineNo) : Line(Line), LineNo(LineNo) {} + }; + + using CoverageSegmentArray = ArrayRef; + + /// @name Rendering Interface + /// @{ + + /// \brief Render the source name for the view. + virtual void renderSourceName(raw_ostream &OS) = 0; + + /// \brief Render the line prefix at the given \p ViewDepth. + virtual void renderLinePrefix(raw_ostream &OS, unsigned ViewDepth) = 0; + + /// \brief Render a view divider at the given \p ViewDepth. + virtual void renderViewDivider(raw_ostream &OS, unsigned ViewDepth) = 0; + /// \brief Render a source line with highlighting. - void renderLine(raw_ostream &OS, StringRef Line, int64_t LineNumber, - const coverage::CoverageSegment *WrappedSegment, - ArrayRef Segments, - unsigned ExpansionCol); - - void renderIndent(raw_ostream &OS, unsigned Level); - - void renderViewDivider(unsigned Offset, unsigned Length, raw_ostream &OS); + virtual void renderLine(raw_ostream &OS, LineRef L, + const coverage::CoverageSegment *WrappedSegment, + CoverageSegmentArray Segments, unsigned ExpansionCol, + unsigned ViewDepth) = 0; /// \brief Render the line's execution count column. - void renderLineCoverageColumn(raw_ostream &OS, const LineCoverageStats &Line); + virtual void renderLineCoverageColumn(raw_ostream &OS, + const LineCoverageStats &Line) = 0; /// \brief Render the line number column. - void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo); + virtual void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo) = 0; /// \brief Render all the region's execution counts on a line. - void - renderRegionMarkers(raw_ostream &OS, - ArrayRef Segments); + virtual void renderRegionMarkers(raw_ostream &OS, + CoverageSegmentArray Segments, + unsigned ViewDepth) = 0; - static const unsigned LineCoverageColumnWidth = 7; - static const unsigned LineNumberColumnWidth = 5; + /// \brief Render an expansion view. If \p FirstLine is provided, it points + /// to the expansion site, which must be re-rendered for clarity. + virtual unsigned renderExpansionView( + raw_ostream &OS, ExpansionView &ESV, Optional FirstLine, + unsigned ExpansionCol, const coverage::CoverageSegment *WrappedSegment, + CoverageSegmentArray LineSegments, unsigned ViewDepth) = 0; + + /// \brief Render an instantiation view. + virtual void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV, + unsigned ViewDepth) = 0; + + /// @} + + /// \brief Format a count using engineering notation with 3 significant + /// digits. + static std::string formatCount(uint64_t N); -public: SourceCoverageView(StringRef SourceName, const MemoryBuffer &File, const CoverageViewOptions &Options, coverage::CoverageData &&CoverageInfo) : SourceName(SourceName), File(File), Options(Options), CoverageInfo(std::move(CoverageInfo)) {} +public: + static std::unique_ptr + create(StringRef SourceName, const MemoryBuffer &File, + const CoverageViewOptions &Options, + coverage::CoverageData &&CoverageInfo); + + virtual ~SourceCoverageView() {} + StringRef getSourceName() const { return SourceName; } const CoverageViewOptions &getOptions() const { return Options; } /// \brief Add an expansion subview to this view. void addExpansion(const coverage::CounterMappingRegion &Region, - std::unique_ptr View) { - ExpansionSubViews.emplace_back(Region, std::move(View)); - } + std::unique_ptr View); /// \brief Add a function instantiation subview to this view. void addInstantiation(StringRef FunctionName, unsigned Line, - std::unique_ptr View) { - InstantiationSubViews.emplace_back(FunctionName, Line, std::move(View)); - } + std::unique_ptr View); - /// \brief Print the code coverage information for a specific - /// portion of a source file to the output stream. - void render(raw_ostream &OS, bool WholeFile, unsigned IndentLevel = 0); - - /// \brief Print the source name corresponding to this view. - void renderSourceName(raw_ostream &OS); + /// \brief Print the code coverage information for a specific portion of a + /// source file to the output stream. + void print(raw_ostream &OS, bool WholeFile, bool ShowSourceName, + unsigned ViewDepth = 0); }; } // namespace llvm diff --git a/tools/llvm-cov/SourceCoverageViewText.cpp b/tools/llvm-cov/SourceCoverageViewText.cpp new file mode 100644 index 00000000000..a7cb5075583 --- /dev/null +++ b/tools/llvm-cov/SourceCoverageViewText.cpp @@ -0,0 +1,193 @@ +//===- SourceCoverageViewText.cpp - A text-based code coverage view -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the text-based coverage renderer. +// +//===----------------------------------------------------------------------===// + +#include "SourceCoverageViewText.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" + +using namespace llvm; + +namespace { + +constexpr unsigned LineCoverageColumnWidth = 7; +constexpr unsigned LineNumberColumnWidth = 5; + +/// \brief Get the width of the leading columns. +unsigned getCombinedColumnWidth(const CoverageViewOptions &Opts) { + return (Opts.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) + + (Opts.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0); +} + +/// \brief The width of the line that is used to divide between the view and +/// the subviews. +unsigned getDividerWidth(const CoverageViewOptions &Opts) { + return getCombinedColumnWidth(Opts) + 4; +} + +} // anonymous namespace + +void SourceCoverageViewText::renderSourceName(raw_ostream &OS) { + getOptions().colored_ostream(OS, raw_ostream::CYAN) << getSourceName() + << ":\n"; +} + +void SourceCoverageViewText::renderLinePrefix(raw_ostream &OS, + unsigned ViewDepth) { + for (unsigned I = 0; I < ViewDepth; ++I) + OS << " |"; +} + +void SourceCoverageViewText::renderViewDivider(raw_ostream &OS, + unsigned ViewDepth) { + assert(ViewDepth != 0 && "Cannot render divider at top level"); + renderLinePrefix(OS, ViewDepth - 1); + OS.indent(2); + unsigned Length = getDividerWidth(getOptions()); + for (unsigned I = 0; I < Length; ++I) + OS << '-'; + OS << '\n'; +} + +void SourceCoverageViewText::renderLine( + raw_ostream &OS, LineRef L, + const coverage::CoverageSegment *WrappedSegment, + CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned ViewDepth) { + StringRef Line = L.Line; + unsigned LineNumber = L.LineNo; + + Optional Highlight; + SmallVector, 2> HighlightedRanges; + + // The first segment overlaps from a previous line, so we treat it specially. + if (WrappedSegment && WrappedSegment->HasCount && WrappedSegment->Count == 0) + Highlight = raw_ostream::RED; + + // Output each segment of the line, possibly highlighted. + unsigned Col = 1; + for (const auto *S : Segments) { + unsigned End = std::min(S->Col, static_cast(Line.size()) + 1); + colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR, + getOptions().Colors && Highlight, /*Bold=*/false, + /*BG=*/true) + << Line.substr(Col - 1, End - Col); + if (getOptions().Debug && Highlight) + HighlightedRanges.push_back(std::make_pair(Col, End)); + Col = End; + if (Col == ExpansionCol) + Highlight = raw_ostream::CYAN; + else if (S->HasCount && S->Count == 0) + Highlight = raw_ostream::RED; + else + Highlight = None; + } + + // Show the rest of the line. + colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR, + getOptions().Colors && Highlight, /*Bold=*/false, /*BG=*/true) + << Line.substr(Col - 1, Line.size() - Col + 1); + OS << '\n'; + + if (getOptions().Debug) { + for (const auto &Range : HighlightedRanges) + errs() << "Highlighted line " << LineNumber << ", " << Range.first + << " -> " << Range.second << '\n'; + if (Highlight) + errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n"; + } +} + +void SourceCoverageViewText::renderLineCoverageColumn( + raw_ostream &OS, const LineCoverageStats &Line) { + if (!Line.isMapped()) { + OS.indent(LineCoverageColumnWidth) << '|'; + return; + } + std::string C = formatCount(Line.ExecutionCount); + OS.indent(LineCoverageColumnWidth - C.size()); + colored_ostream(OS, raw_ostream::MAGENTA, + Line.hasMultipleRegions() && getOptions().Colors) + << C; + OS << '|'; +} + +void SourceCoverageViewText::renderLineNumberColumn(raw_ostream &OS, + unsigned LineNo) { + SmallString<32> Buffer; + raw_svector_ostream BufferOS(Buffer); + BufferOS << LineNo; + auto Str = BufferOS.str(); + // Trim and align to the right. + Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth)); + OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|'; +} + +void SourceCoverageViewText::renderRegionMarkers( + raw_ostream &OS, CoverageSegmentArray Segments, unsigned ViewDepth) { + renderLinePrefix(OS, ViewDepth); + OS.indent(getCombinedColumnWidth(getOptions())); + + unsigned PrevColumn = 1; + for (const auto *S : Segments) { + if (!S->IsRegionEntry) + continue; + // Skip to the new region. + if (S->Col > PrevColumn) + OS.indent(S->Col - PrevColumn); + PrevColumn = S->Col + 1; + std::string C = formatCount(S->Count); + PrevColumn += C.size(); + OS << '^' << C; + } + OS << '\n'; + + if (getOptions().Debug) + for (const auto *S : Segments) + errs() << "Marker at " << S->Line << ":" << S->Col << " = " + << formatCount(S->Count) << (S->IsRegionEntry ? "\n" : " (pop)\n"); +} + +unsigned SourceCoverageViewText::renderExpansionView( + raw_ostream &OS, ExpansionView &ESV, Optional FirstLine, + unsigned ExpansionCol, const coverage::CoverageSegment *WrappedSegment, + CoverageSegmentArray LineSegments, unsigned ViewDepth) { + unsigned NextExpansionCol = ExpansionCol; + + if (FirstLine.hasValue()) { + // Re-render the current line and highlight the expansion range for + // this subview. + NextExpansionCol = ESV.getStartCol(); + renderLinePrefix(OS, ViewDepth); + OS.indent(getCombinedColumnWidth(getOptions()) + (ViewDepth == 0 ? 0 : 1)); + renderLine(OS, *FirstLine, WrappedSegment, LineSegments, ExpansionCol, + ViewDepth); + renderViewDivider(OS, ViewDepth + 1); + } + + // Render the child subview. + if (getOptions().Debug) + errs() << "Expansion at line " << ESV.getLine() << ", " << ESV.getStartCol() + << " -> " << ESV.getEndCol() << '\n'; + ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false, + ViewDepth + 1); + + return NextExpansionCol; +} + +void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS, + InstantiationView &ISV, + unsigned ViewDepth) { + renderViewDivider(OS, ViewDepth); + renderLinePrefix(OS, ViewDepth); + OS << ' '; + ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true, ViewDepth); +} diff --git a/tools/llvm-cov/SourceCoverageViewText.h b/tools/llvm-cov/SourceCoverageViewText.h new file mode 100644 index 00000000000..f383afcd5a7 --- /dev/null +++ b/tools/llvm-cov/SourceCoverageViewText.h @@ -0,0 +1,61 @@ +//===- SourceCoverageViewText.h - A text-based code coverage view ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interface to the text-based coverage renderer. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_SOURCECOVERAGEVIEWTEXT_H +#define LLVM_COV_SOURCECOVERAGEVIEWTEXT_H + +#include "SourceCoverageView.h" + +namespace llvm { + +class SourceCoverageViewText : public SourceCoverageView { + void renderSourceName(raw_ostream &OS) override; + + void renderLinePrefix(raw_ostream &OS, unsigned ViewDepth) override; + + void renderViewDivider(raw_ostream &OS, unsigned ViewDepth) override; + + void renderLine(raw_ostream &OS, LineRef L, + const coverage::CoverageSegment *WrappedSegment, + CoverageSegmentArray Segments, unsigned ExpansionCol, + unsigned ViewDepth) override; + + unsigned renderExpansionView(raw_ostream &OS, ExpansionView &ESV, + Optional FirstLine, + unsigned ExpansionCol, + const coverage::CoverageSegment *WrappedSegment, + CoverageSegmentArray LineSegments, + unsigned ViewDepth) override; + + void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV, + unsigned ViewDepth) override; + + void renderLineCoverageColumn(raw_ostream &OS, + const LineCoverageStats &Line) override; + + void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo) override; + + void renderRegionMarkers(raw_ostream &OS, CoverageSegmentArray Segments, + unsigned ViewDepth) override; + +public: + SourceCoverageViewText(StringRef SourceName, const MemoryBuffer &File, + const CoverageViewOptions &Options, + coverage::CoverageData &&CoverageInfo) + : SourceCoverageView(SourceName, File, Options, std::move(CoverageInfo)) { + } +}; + +} // namespace llvm + +#endif // LLVM_COV_SOURCECOVERAGEVIEWTEXT_H