forked from OSchip/llvm-project
[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. llvm-svn: 273767
This commit is contained in:
parent
cc676c47a3
commit
f9151b9372
|
@ -8,5 +8,6 @@ add_llvm_tool(llvm-cov
|
|||
CoverageReport.cpp
|
||||
CoverageSummaryInfo.cpp
|
||||
SourceCoverageView.cpp
|
||||
SourceCoverageViewText.cpp
|
||||
TestingSupport.cpp
|
||||
)
|
||||
|
|
|
@ -132,9 +132,9 @@ CodeCoverageTool::attachExpansionSubViews(SourceCoverageView &View,
|
|||
continue;
|
||||
|
||||
auto SubViewExpansions = ExpansionCoverage.getExpansions();
|
||||
auto SubView = llvm::make_unique<SourceCoverageView>(
|
||||
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<SourceCoverageView>(
|
||||
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<SourceCoverageView>(
|
||||
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<SourceCoverageView>(
|
||||
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";
|
||||
}
|
||||
|
|
|
@ -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<const coverage::CoverageSegment *> Segments,
|
||||
unsigned ExpansionCol) {
|
||||
Optional<raw_ostream::Colors> Highlight;
|
||||
SmallVector<std::pair<unsigned, unsigned>, 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<unsigned>(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<SourceCoverageView> 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<SourceCoverageView> View) {
|
||||
InstantiationSubViews.emplace_back(FunctionName, Line, std::move(View));
|
||||
}
|
||||
|
||||
void SourceCoverageView::renderRegionMarkers(
|
||||
raw_ostream &OS, ArrayRef<const coverage::CoverageSegment *> 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>
|
||||
SourceCoverageView::create(StringRef SourceName, const MemoryBuffer &File,
|
||||
const CoverageViewOptions &Options,
|
||||
coverage::CoverageData &&CoverageInfo) {
|
||||
return llvm::make_unique<SourceCoverageViewText>(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<LineRef>({*LI, LI.line_number()})
|
||||
: Optional<LineRef>(),
|
||||
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";
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "CoverageViewOptions.h"
|
||||
#include "llvm/ProfileData/Coverage/CoverageMapping.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include <vector>
|
||||
|
||||
|
@ -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<InstantiationView> InstantiationSubViews;
|
||||
|
||||
protected:
|
||||
struct LineRef {
|
||||
StringRef Line;
|
||||
int64_t LineNo;
|
||||
|
||||
LineRef(StringRef Line, int64_t LineNo) : Line(Line), LineNo(LineNo) {}
|
||||
};
|
||||
|
||||
using CoverageSegmentArray = ArrayRef<const coverage::CoverageSegment *>;
|
||||
|
||||
/// @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<const coverage::CoverageSegment *> 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<const coverage::CoverageSegment *> 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<LineRef> 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<SourceCoverageView>
|
||||
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<SourceCoverageView> View) {
|
||||
ExpansionSubViews.emplace_back(Region, std::move(View));
|
||||
}
|
||||
std::unique_ptr<SourceCoverageView> View);
|
||||
|
||||
/// \brief Add a function instantiation subview to this view.
|
||||
void addInstantiation(StringRef FunctionName, unsigned Line,
|
||||
std::unique_ptr<SourceCoverageView> View) {
|
||||
InstantiationSubViews.emplace_back(FunctionName, Line, std::move(View));
|
||||
}
|
||||
std::unique_ptr<SourceCoverageView> 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
|
||||
|
|
|
@ -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<raw_ostream::Colors> Highlight;
|
||||
SmallVector<std::pair<unsigned, unsigned>, 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<unsigned>(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<LineRef> 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);
|
||||
}
|
|
@ -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<LineRef> 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
|
Loading…
Reference in New Issue