mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-11-01 15:11:24 +00:00
d41bba158c
Offset is a terrible name for an indentation / nesting level, and it confuses me every time I look at this code. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@217861 91177308-0d34-0410-b5e6-96231b3b80d8
422 lines
15 KiB
C++
422 lines
15 KiB
C++
//===- SourceCoverageView.cpp - Code coverage view for source code --------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This class implements rendering for code coverage of source code.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "SourceCoverageView.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/Support/LineIterator.h"
|
|
|
|
using namespace llvm;
|
|
|
|
void SourceCoverageView::renderLine(raw_ostream &OS, StringRef Line,
|
|
ArrayRef<HighlightRange> Ranges) {
|
|
if (Ranges.empty()) {
|
|
OS << Line << "\n";
|
|
return;
|
|
}
|
|
if (Line.empty())
|
|
Line = " ";
|
|
|
|
unsigned PrevColumnStart = 0;
|
|
unsigned Start = 1;
|
|
for (const auto &Range : Ranges) {
|
|
if (PrevColumnStart == Range.ColumnStart)
|
|
continue;
|
|
|
|
// Show the unhighlighted part
|
|
unsigned ColumnStart = PrevColumnStart = Range.ColumnStart;
|
|
OS << Line.substr(Start - 1, ColumnStart - Start);
|
|
|
|
// Show the highlighted part
|
|
auto Color = Range.Kind == HighlightRange::NotCovered ? raw_ostream::RED
|
|
: raw_ostream::CYAN;
|
|
OS.changeColor(Color, false, true);
|
|
unsigned ColumnEnd = std::min(Range.ColumnEnd, (unsigned)Line.size() + 1);
|
|
OS << Line.substr(ColumnStart - 1, ColumnEnd - ColumnStart);
|
|
Start = ColumnEnd;
|
|
OS.resetColor();
|
|
}
|
|
|
|
// Show the rest of the line
|
|
OS << Line.substr(Start - 1, Line.size() - Start + 1);
|
|
OS << "\n";
|
|
|
|
if (Options.Debug) {
|
|
for (const auto &Range : Ranges) {
|
|
errs() << "Highlighted line " << Range.Line << ", " << Range.ColumnStart
|
|
<< " -> ";
|
|
if (Range.ColumnEnd == std::numeric_limits<unsigned>::max()) {
|
|
errs() << "?\n";
|
|
} else {
|
|
errs() << Range.ColumnEnd << "\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 << "-";
|
|
}
|
|
|
|
void
|
|
SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS,
|
|
const LineCoverageInfo &Line) {
|
|
if (!Line.isMapped()) {
|
|
OS.indent(LineCoverageColumnWidth) << '|';
|
|
return;
|
|
}
|
|
SmallString<32> Buffer;
|
|
raw_svector_ostream BufferOS(Buffer);
|
|
BufferOS << Line.ExecutionCount;
|
|
auto Str = BufferOS.str();
|
|
// Trim
|
|
Str = Str.substr(0, std::min(Str.size(), (size_t)LineCoverageColumnWidth));
|
|
// Align to the right
|
|
OS.indent(LineCoverageColumnWidth - Str.size());
|
|
colored_ostream(OS, raw_ostream::MAGENTA,
|
|
Line.hasMultipleRegions() && Options.Colors)
|
|
<< Str;
|
|
OS << '|';
|
|
}
|
|
|
|
void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS,
|
|
unsigned LineNo) {
|
|
SmallString<32> Buffer;
|
|
raw_svector_ostream BufferOS(Buffer);
|
|
BufferOS << LineNo;
|
|
auto Str = BufferOS.str();
|
|
// Trim and align to the right
|
|
Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
|
|
OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
|
|
}
|
|
|
|
void SourceCoverageView::renderRegionMarkers(raw_ostream &OS,
|
|
ArrayRef<RegionMarker> Regions) {
|
|
SmallString<32> Buffer;
|
|
raw_svector_ostream BufferOS(Buffer);
|
|
|
|
unsigned PrevColumn = 1;
|
|
for (const auto &Region : Regions) {
|
|
// Skip to the new region
|
|
if (Region.Column > PrevColumn)
|
|
OS.indent(Region.Column - PrevColumn);
|
|
PrevColumn = Region.Column + 1;
|
|
BufferOS << Region.ExecutionCount;
|
|
StringRef Str = BufferOS.str();
|
|
// Trim the execution count
|
|
Str = Str.substr(0, std::min(Str.size(), (size_t)7));
|
|
PrevColumn += Str.size();
|
|
OS << '^' << Str;
|
|
Buffer.clear();
|
|
}
|
|
OS << "\n";
|
|
|
|
if (Options.Debug) {
|
|
for (const auto &Region : Regions) {
|
|
errs() << "Marker at " << Region.Line << ":" << Region.Column << " = "
|
|
<< Region.ExecutionCount << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
/// \brief Insert a new highlighting range into the line's highlighting ranges
|
|
/// Return line's new highlighting ranges in result.
|
|
static void insertHighlightRange(
|
|
ArrayRef<SourceCoverageView::HighlightRange> Ranges,
|
|
SourceCoverageView::HighlightRange RangeToInsert,
|
|
SmallVectorImpl<SourceCoverageView::HighlightRange> &Result) {
|
|
Result.clear();
|
|
size_t I = 0;
|
|
auto E = Ranges.size();
|
|
for (; I < E; ++I) {
|
|
if (RangeToInsert.ColumnStart < Ranges[I].ColumnEnd) {
|
|
const auto &Range = Ranges[I];
|
|
bool NextRangeContainsInserted = false;
|
|
// If the next range starts before the inserted range, move the end of the
|
|
// next range to the start of the inserted range.
|
|
if (Range.ColumnStart < RangeToInsert.ColumnStart) {
|
|
if (RangeToInsert.ColumnStart != Range.ColumnStart)
|
|
Result.push_back(SourceCoverageView::HighlightRange(
|
|
Range.Line, Range.ColumnStart, RangeToInsert.ColumnStart,
|
|
Range.Kind));
|
|
// If the next range also ends after the inserted range, keep this range
|
|
// and create a new range that starts at the inserted range and ends
|
|
// at the next range later.
|
|
if (Range.ColumnEnd > RangeToInsert.ColumnEnd)
|
|
NextRangeContainsInserted = true;
|
|
}
|
|
if (!NextRangeContainsInserted) {
|
|
++I;
|
|
// Ignore ranges that are contained in inserted range
|
|
while (I < E && RangeToInsert.contains(Ranges[I]))
|
|
++I;
|
|
}
|
|
break;
|
|
}
|
|
Result.push_back(Ranges[I]);
|
|
}
|
|
Result.push_back(RangeToInsert);
|
|
// If the next range starts before the inserted range end, move the start
|
|
// of the next range to the end of the inserted range.
|
|
if (I < E && Ranges[I].ColumnStart < RangeToInsert.ColumnEnd) {
|
|
const auto &Range = Ranges[I];
|
|
if (RangeToInsert.ColumnEnd != Range.ColumnEnd)
|
|
Result.push_back(SourceCoverageView::HighlightRange(
|
|
Range.Line, RangeToInsert.ColumnEnd, Range.ColumnEnd, Range.Kind));
|
|
++I;
|
|
}
|
|
// Add the remaining ranges that are located after the inserted range
|
|
for (; I < E; ++I)
|
|
Result.push_back(Ranges[I]);
|
|
}
|
|
|
|
void SourceCoverageView::sortChildren() {
|
|
for (auto &I : Children)
|
|
I->sortChildren();
|
|
std::sort(Children.begin(), Children.end(),
|
|
[](const std::unique_ptr<SourceCoverageView> &LHS,
|
|
const std::unique_ptr<SourceCoverageView> &RHS) {
|
|
return LHS->ExpansionRegion < RHS->ExpansionRegion;
|
|
});
|
|
}
|
|
|
|
SourceCoverageView::HighlightRange
|
|
SourceCoverageView::getExpansionHighlightRange() const {
|
|
return HighlightRange(ExpansionRegion.LineStart, ExpansionRegion.ColumnStart,
|
|
ExpansionRegion.ColumnEnd, HighlightRange::Expanded);
|
|
}
|
|
|
|
template <typename T>
|
|
ArrayRef<T> gatherLineItems(size_t &CurrentIdx, const std::vector<T> &Items,
|
|
unsigned LineNo) {
|
|
auto PrevIdx = CurrentIdx;
|
|
auto E = Items.size();
|
|
while (CurrentIdx < E && Items[CurrentIdx].Line == LineNo)
|
|
++CurrentIdx;
|
|
return ArrayRef<T>(Items.data() + PrevIdx, CurrentIdx - PrevIdx);
|
|
}
|
|
|
|
ArrayRef<std::unique_ptr<SourceCoverageView>>
|
|
gatherLineSubViews(size_t &CurrentIdx,
|
|
ArrayRef<std::unique_ptr<SourceCoverageView>> Items,
|
|
unsigned LineNo) {
|
|
auto PrevIdx = CurrentIdx;
|
|
auto E = Items.size();
|
|
while (CurrentIdx < E &&
|
|
Items[CurrentIdx]->getSubViewsExpansionLine() == LineNo)
|
|
++CurrentIdx;
|
|
return Items.slice(PrevIdx, CurrentIdx - PrevIdx);
|
|
}
|
|
|
|
void SourceCoverageView::render(raw_ostream &OS, unsigned IndentLevel) {
|
|
// Make sure that the children are in sorted order.
|
|
sortChildren();
|
|
|
|
SmallVector<HighlightRange, 8> AdjustedLineHighlightRanges;
|
|
size_t CurrentChild = 0;
|
|
size_t CurrentHighlightRange = 0;
|
|
size_t CurrentRegionMarker = 0;
|
|
|
|
line_iterator Lines(File);
|
|
// Advance the line iterator to the first line.
|
|
while (Lines.line_number() < LineOffset)
|
|
++Lines;
|
|
|
|
// The width of the leading columns
|
|
unsigned CombinedColumnWidth =
|
|
(Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
|
|
(Options.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
|
|
// The width of the line that is used to divide between the view and the
|
|
// subviews.
|
|
unsigned DividerWidth = CombinedColumnWidth + 4;
|
|
|
|
for (size_t I = 0, E = LineStats.size(); I < E; ++I) {
|
|
unsigned LineNo = I + LineOffset;
|
|
|
|
// Gather the child subviews that are visible on this line.
|
|
auto LineSubViews = gatherLineSubViews(CurrentChild, Children, LineNo);
|
|
|
|
renderIndent(OS, IndentLevel);
|
|
if (Options.ShowLineStats)
|
|
renderLineCoverageColumn(OS, LineStats[I]);
|
|
if (Options.ShowLineNumbers)
|
|
renderLineNumberColumn(OS, LineNo);
|
|
|
|
// Gather highlighting ranges.
|
|
auto LineHighlightRanges =
|
|
gatherLineItems(CurrentHighlightRange, HighlightRanges, LineNo);
|
|
auto LineRanges = LineHighlightRanges;
|
|
// Highlight the expansion range if there is an expansion subview on this
|
|
// line.
|
|
if (!LineSubViews.empty() && LineSubViews.front()->isExpansionSubView() &&
|
|
Options.Colors) {
|
|
insertHighlightRange(LineHighlightRanges,
|
|
LineSubViews.front()->getExpansionHighlightRange(),
|
|
AdjustedLineHighlightRanges);
|
|
LineRanges = AdjustedLineHighlightRanges;
|
|
}
|
|
|
|
// Display the source code for the current line.
|
|
StringRef Line = *Lines;
|
|
// Check if the line is empty, as line_iterator skips blank lines.
|
|
if (LineNo < Lines.line_number())
|
|
Line = "";
|
|
else if (!Lines.is_at_eof())
|
|
++Lines;
|
|
renderLine(OS, Line, LineRanges);
|
|
|
|
// Show the region markers.
|
|
bool ShowMarkers = !Options.ShowLineStatsOrRegionMarkers ||
|
|
LineStats[I].hasMultipleRegions();
|
|
auto LineMarkers = gatherLineItems(CurrentRegionMarker, Markers, LineNo);
|
|
if (ShowMarkers && !LineMarkers.empty()) {
|
|
renderIndent(OS, IndentLevel);
|
|
OS.indent(CombinedColumnWidth);
|
|
renderRegionMarkers(OS, LineMarkers);
|
|
}
|
|
|
|
// Show the line's expanded child subviews.
|
|
bool FirstChildExpansion = true;
|
|
if (LineSubViews.empty())
|
|
continue;
|
|
unsigned NestedIndent = IndentLevel + 1;
|
|
renderViewDivider(NestedIndent, DividerWidth, OS);
|
|
OS << "\n";
|
|
for (const auto &Child : LineSubViews) {
|
|
// If this subview shows a function instantiation, render the function's
|
|
// name.
|
|
if (Child->isInstantiationSubView()) {
|
|
renderIndent(OS, NestedIndent);
|
|
OS << ' ';
|
|
Options.colored_ostream(OS, raw_ostream::CYAN) << Child->FunctionName
|
|
<< ":";
|
|
OS << "\n";
|
|
} else {
|
|
if (!FirstChildExpansion) {
|
|
// Re-render the current line and highlight the expansion range for
|
|
// this
|
|
// subview.
|
|
insertHighlightRange(LineHighlightRanges,
|
|
Child->getExpansionHighlightRange(),
|
|
AdjustedLineHighlightRanges);
|
|
renderIndent(OS, IndentLevel);
|
|
OS.indent(CombinedColumnWidth + (IndentLevel == 0 ? 0 : 1));
|
|
renderLine(OS, Line, AdjustedLineHighlightRanges);
|
|
renderViewDivider(NestedIndent, DividerWidth, OS);
|
|
OS << "\n";
|
|
} else
|
|
FirstChildExpansion = false;
|
|
}
|
|
// Render the child subview
|
|
Child->render(OS, NestedIndent);
|
|
renderViewDivider(NestedIndent, DividerWidth, OS);
|
|
OS << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void SourceCoverageView::setUpVisibleRange(SourceCoverageDataManager &Data) {
|
|
auto CountedRegions = Data.getSourceRegions();
|
|
if (!CountedRegions.size())
|
|
return;
|
|
|
|
unsigned Start = CountedRegions.front().LineStart, End = 0;
|
|
for (const auto &CR : CountedRegions) {
|
|
Start = std::min(Start, CR.LineStart);
|
|
End = std::max(End, CR.LineEnd);
|
|
}
|
|
LineOffset = Start;
|
|
LineStats.resize(End - Start + 1);
|
|
}
|
|
|
|
void
|
|
SourceCoverageView::createLineCoverageInfo(SourceCoverageDataManager &Data) {
|
|
auto CountedRegions = Data.getSourceRegions();
|
|
for (const auto &CR : CountedRegions) {
|
|
if (CR.Kind == coverage::CounterMappingRegion::SkippedRegion) {
|
|
// Reset the line stats for skipped regions.
|
|
for (unsigned Line = CR.LineStart; Line <= CR.LineEnd;
|
|
++Line)
|
|
LineStats[Line - LineOffset] = LineCoverageInfo();
|
|
continue;
|
|
}
|
|
LineStats[CR.LineStart - LineOffset].addRegionStartCount(CR.ExecutionCount);
|
|
for (unsigned Line = CR.LineStart + 1; Line <= CR.LineEnd; ++Line)
|
|
LineStats[Line - LineOffset].addRegionCount(CR.ExecutionCount);
|
|
}
|
|
}
|
|
|
|
void
|
|
SourceCoverageView::createHighlightRanges(SourceCoverageDataManager &Data) {
|
|
auto CountedRegions = Data.getSourceRegions();
|
|
std::vector<bool> AlreadyHighlighted;
|
|
AlreadyHighlighted.resize(CountedRegions.size(), false);
|
|
|
|
for (size_t I = 0, S = CountedRegions.size(); I < S; ++I) {
|
|
const auto &CR = CountedRegions[I];
|
|
if (CR.Kind == coverage::CounterMappingRegion::SkippedRegion ||
|
|
CR.ExecutionCount != 0)
|
|
continue;
|
|
if (AlreadyHighlighted[I])
|
|
continue;
|
|
for (size_t J = 0; J < S; ++J) {
|
|
if (CR.contains(CountedRegions[J])) {
|
|
AlreadyHighlighted[J] = true;
|
|
}
|
|
}
|
|
if (CR.LineStart == CR.LineEnd) {
|
|
HighlightRanges.push_back(HighlightRange(
|
|
CR.LineStart, CR.ColumnStart, CR.ColumnEnd));
|
|
continue;
|
|
}
|
|
HighlightRanges.push_back(
|
|
HighlightRange(CR.LineStart, CR.ColumnStart,
|
|
std::numeric_limits<unsigned>::max()));
|
|
HighlightRanges.push_back(
|
|
HighlightRange(CR.LineEnd, 1, CR.ColumnEnd));
|
|
for (unsigned Line = CR.LineStart + 1; Line < CR.LineEnd;
|
|
++Line) {
|
|
HighlightRanges.push_back(
|
|
HighlightRange(Line, 1, std::numeric_limits<unsigned>::max()));
|
|
}
|
|
}
|
|
|
|
std::sort(HighlightRanges.begin(), HighlightRanges.end());
|
|
}
|
|
|
|
void SourceCoverageView::createRegionMarkers(SourceCoverageDataManager &Data) {
|
|
for (const auto &CR : Data.getSourceRegions())
|
|
if (CR.Kind != coverage::CounterMappingRegion::SkippedRegion)
|
|
Markers.push_back(
|
|
RegionMarker(CR.LineStart, CR.ColumnStart, CR.ExecutionCount));
|
|
}
|
|
|
|
void SourceCoverageView::load(SourceCoverageDataManager &Data) {
|
|
setUpVisibleRange(Data);
|
|
if (Options.ShowLineStats)
|
|
createLineCoverageInfo(Data);
|
|
if (Options.Colors)
|
|
createHighlightRanges(Data);
|
|
if (Options.ShowRegionMarkers)
|
|
createRegionMarkers(Data);
|
|
}
|