llvm-6502/tools/llvm-cov/SourceCoverageView.cpp
Alex Lorenz 6c7a6a1ba2 llvm-cov: add code coverage tool that's based on coverage mapping format and clang's pgo.
This commit expands llvm-cov's functionality by adding support for a new code coverage
tool that uses LLVM's coverage mapping format and clang's instrumentation based profiling.
The gcov compatible tool can be invoked by supplying the 'gcov' command as the first argument,
or by modifying the tool's name to end with 'gcov'.

Differential Revision: http://reviews.llvm.org/D4445


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@216300 91177308-0d34-0410-b5e6-96231b3b80d8
2014-08-22 22:56:03 +00:00

412 lines
14 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";
}
void SourceCoverageView::renderOffset(raw_ostream &OS, unsigned I) {
for (unsigned J = 0; J < I; ++J)
OS << " |";
}
void SourceCoverageView::renderViewDivider(unsigned Offset, unsigned Length,
raw_ostream &OS) {
for (unsigned J = 1; J < Offset; ++J)
OS << " |";
if (Offset != 0)
OS.indent(2);
for (unsigned I = 0; I < Length; ++I)
OS << "-";
}
void
SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS,
const LineCoverageInfo &Line) {
if (!Line.isMapped()) {
OS.indent(LineCoverageColumnWidth) << '|';
return;
}
SmallString<32> Buffer;
raw_svector_ostream BufferOS(Buffer);
BufferOS << Line.ExecutionCount;
auto Str = BufferOS.str();
// Trim
Str = Str.substr(0, std::min(Str.size(), (size_t)LineCoverageColumnWidth));
// Align to the right
OS.indent(LineCoverageColumnWidth - Str.size());
colored_ostream(OS, raw_ostream::MAGENTA,
Line.hasMultipleRegions() && Options.Colors)
<< Str;
OS << '|';
}
void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS,
unsigned LineNo) {
SmallString<32> Buffer;
raw_svector_ostream BufferOS(Buffer);
BufferOS << LineNo;
auto Str = BufferOS.str();
// Trim and align to the right
Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
}
void SourceCoverageView::renderRegionMarkers(raw_ostream &OS,
ArrayRef<RegionMarker> Regions) {
SmallString<32> Buffer;
raw_svector_ostream BufferOS(Buffer);
unsigned PrevColumn = 1;
for (const auto &Region : Regions) {
// Skip to the new region
if (Region.Column > PrevColumn)
OS.indent(Region.Column - PrevColumn);
PrevColumn = Region.Column + 1;
BufferOS << Region.ExecutionCount;
StringRef Str = BufferOS.str();
// Trim the execution count
Str = Str.substr(0, std::min(Str.size(), (size_t)7));
PrevColumn += Str.size();
OS << '^' << Str;
Buffer.clear();
}
OS << "\n";
}
/// \brief Insert a new highlighting range into the line's highlighting ranges
/// Return line's new highlighting ranges in result.
static void insertHighlightRange(
ArrayRef<SourceCoverageView::HighlightRange> Ranges,
SourceCoverageView::HighlightRange RangeToInsert,
SmallVectorImpl<SourceCoverageView::HighlightRange> &Result) {
Result.clear();
size_t I = 0;
auto E = Ranges.size();
for (; I < E; ++I) {
if (RangeToInsert.ColumnStart < Ranges[I].ColumnEnd) {
const auto &Range = Ranges[I];
bool NextRangeContainsInserted = false;
// If the next range starts before the inserted range, move the end of the
// next range to the start of the inserted range.
if (Range.ColumnStart < RangeToInsert.ColumnStart) {
if (RangeToInsert.ColumnStart != Range.ColumnStart)
Result.push_back(SourceCoverageView::HighlightRange(
Range.Line, Range.ColumnStart, RangeToInsert.ColumnStart,
Range.Kind));
// If the next range also ends after the inserted range, keep this range
// and create a new range that starts at the inserted range and ends
// at the next range later.
if (Range.ColumnEnd > RangeToInsert.ColumnEnd)
NextRangeContainsInserted = true;
}
if (!NextRangeContainsInserted) {
++I;
// Ignore ranges that are contained in inserted range
while (I < E && RangeToInsert.contains(Ranges[I]))
++I;
}
break;
}
Result.push_back(Ranges[I]);
}
Result.push_back(RangeToInsert);
// If the next range starts before the inserted range end, move the start
// of the next range to the end of the inserted range.
if (I < E && Ranges[I].ColumnStart < RangeToInsert.ColumnEnd) {
const auto &Range = Ranges[I];
if (RangeToInsert.ColumnEnd != Range.ColumnEnd)
Result.push_back(SourceCoverageView::HighlightRange(
Range.Line, RangeToInsert.ColumnEnd, Range.ColumnEnd, Range.Kind));
++I;
}
// Add the remaining ranges that are located after the inserted range
for (; I < E; ++I)
Result.push_back(Ranges[I]);
}
void SourceCoverageView::sortChildren() {
for (auto &I : Children)
I->sortChildren();
std::sort(Children.begin(), Children.end(),
[](const std::unique_ptr<SourceCoverageView> &LHS,
const std::unique_ptr<SourceCoverageView> &RHS) {
return LHS->ExpansionRegion < RHS->ExpansionRegion;
});
}
SourceCoverageView::HighlightRange
SourceCoverageView::getExpansionHighlightRange() const {
return HighlightRange(ExpansionRegion.LineStart, ExpansionRegion.ColumnStart,
ExpansionRegion.ColumnEnd, HighlightRange::Expanded);
}
template <typename T>
ArrayRef<T> gatherLineItems(size_t &CurrentIdx, const std::vector<T> &Items,
unsigned LineNo) {
auto PrevIdx = CurrentIdx;
auto E = Items.size();
while (CurrentIdx < E && Items[CurrentIdx].Line == LineNo)
++CurrentIdx;
return ArrayRef<T>(Items.data() + PrevIdx, CurrentIdx - PrevIdx);
}
ArrayRef<std::unique_ptr<SourceCoverageView>>
gatherLineSubViews(size_t &CurrentIdx,
ArrayRef<std::unique_ptr<SourceCoverageView>> Items,
unsigned LineNo) {
auto PrevIdx = CurrentIdx;
auto E = Items.size();
while (CurrentIdx < E &&
Items[CurrentIdx]->getSubViewsExpansionLine() == LineNo)
++CurrentIdx;
return ArrayRef<std::unique_ptr<SourceCoverageView>>(Items.data() + PrevIdx,
CurrentIdx - PrevIdx);
}
void SourceCoverageView::render(raw_ostream &OS, unsigned Offset) {
// Make sure that the children are in sorted order.
sortChildren();
SmallVector<HighlightRange, 8> AdjustedLineHighlightRanges;
size_t CurrentChild = 0;
size_t CurrentHighlightRange = 0;
size_t CurrentRegionMarker = 0;
line_iterator Lines(File);
// Advance the line iterator to the first line.
while (Lines.line_number() < LineStart)
++Lines;
// The width of the leading columns
unsigned CombinedColumnWidth =
(Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
(Options.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
// The width of the line that is used to divide between the view and the
// subviews.
unsigned DividerWidth = CombinedColumnWidth + 4;
for (size_t I = 0; I < LineCount; ++I) {
unsigned LineNo = I + LineStart;
// Gather the child subviews that are visible on this line.
auto LineSubViews = gatherLineSubViews(CurrentChild, Children, LineNo);
renderOffset(OS, Offset);
if (Options.ShowLineStats)
renderLineCoverageColumn(OS, LineStats[I]);
if (Options.ShowLineNumbers)
renderLineNumberColumn(OS, LineNo);
// Gather highlighting ranges.
auto LineHighlightRanges =
gatherLineItems(CurrentHighlightRange, HighlightRanges, LineNo);
auto LineRanges = LineHighlightRanges;
// Highlight the expansion range if there is an expansion subview on this
// line.
if (!LineSubViews.empty() && LineSubViews.front()->isExpansionSubView() &&
Options.Colors) {
insertHighlightRange(LineHighlightRanges,
LineSubViews.front()->getExpansionHighlightRange(),
AdjustedLineHighlightRanges);
LineRanges = AdjustedLineHighlightRanges;
}
// Display the source code for the current line.
StringRef Line = *Lines;
// Check if the line is empty, as line_iterator skips blank lines.
if (LineNo < Lines.line_number())
Line = "";
else if (!Lines.is_at_eof())
++Lines;
renderLine(OS, Line, LineRanges);
// Show the region markers.
bool ShowMarkers = !Options.ShowLineStatsOrRegionMarkers ||
LineStats[I].hasMultipleRegions();
auto LineMarkers = gatherLineItems(CurrentRegionMarker, Markers, LineNo);
if (ShowMarkers && !LineMarkers.empty()) {
renderOffset(OS, Offset);
OS.indent(CombinedColumnWidth);
renderRegionMarkers(OS, LineMarkers);
}
// Show the line's expanded child subviews.
bool FirstChildExpansion = true;
if (LineSubViews.empty())
continue;
unsigned NewOffset = Offset + 1;
renderViewDivider(NewOffset, DividerWidth, OS);
OS << "\n";
for (const auto &Child : LineSubViews) {
// If this subview shows a function instantiation, render the function's
// name.
if (Child->isInstantiationSubView()) {
renderOffset(OS, NewOffset);
OS << ' ';
Options.colored_ostream(OS, raw_ostream::CYAN) << Child->FunctionName
<< ":";
OS << "\n";
} else {
if (!FirstChildExpansion) {
// Re-render the current line and highlight the expansion range for
// this
// subview.
insertHighlightRange(LineHighlightRanges,
Child->getExpansionHighlightRange(),
AdjustedLineHighlightRanges);
renderOffset(OS, Offset);
OS.indent(CombinedColumnWidth + (Offset == 0 ? 0 : 1));
renderLine(OS, Line, AdjustedLineHighlightRanges);
renderViewDivider(NewOffset, DividerWidth, OS);
OS << "\n";
} else
FirstChildExpansion = false;
}
// Render the child subview
Child->render(OS, NewOffset);
renderViewDivider(NewOffset, DividerWidth, OS);
OS << "\n";
}
}
}
void
SourceCoverageView::createLineCoverageInfo(SourceCoverageDataManager &Data) {
LineStats.resize(LineCount);
for (const auto &Region : Data.getSourceRegions()) {
auto Value = Region.second;
LineStats[Region.first.LineStart - LineStart].addRegionStartCount(Value);
for (unsigned Line = Region.first.LineStart + 1;
Line <= Region.first.LineEnd; ++Line)
LineStats[Line - LineStart].addRegionCount(Value);
}
// Reset the line stats for skipped regions.
for (const auto &Region : Data.getSkippedRegions()) {
for (unsigned Line = Region.LineStart; Line <= Region.LineEnd; ++Line)
LineStats[Line - LineStart] = LineCoverageInfo();
}
}
void
SourceCoverageView::createHighlightRanges(SourceCoverageDataManager &Data) {
auto Regions = Data.getSourceRegions();
std::vector<bool> AlreadyHighlighted;
AlreadyHighlighted.resize(Regions.size(), false);
for (size_t I = 0, S = Regions.size(); I < S; ++I) {
const auto &Region = Regions[I];
auto Value = Region.second;
auto SrcRange = Region.first;
if (Value != 0)
continue;
if (AlreadyHighlighted[I])
continue;
for (size_t J = 0; J < S; ++J) {
if (SrcRange.contains(Regions[J].first)) {
AlreadyHighlighted[J] = true;
}
}
if (SrcRange.LineStart == SrcRange.LineEnd) {
HighlightRanges.push_back(HighlightRange(
SrcRange.LineStart, SrcRange.ColumnStart, SrcRange.ColumnEnd));
continue;
}
HighlightRanges.push_back(
HighlightRange(SrcRange.LineStart, SrcRange.ColumnStart,
std::numeric_limits<unsigned>::max()));
HighlightRanges.push_back(
HighlightRange(SrcRange.LineEnd, 1, SrcRange.ColumnEnd));
for (unsigned Line = SrcRange.LineStart + 1; Line < SrcRange.LineEnd;
++Line) {
HighlightRanges.push_back(
HighlightRange(Line, 1, std::numeric_limits<unsigned>::max()));
}
}
std::sort(HighlightRanges.begin(), HighlightRanges.end());
if (Options.Debug) {
for (const auto &Range : HighlightRanges) {
outs() << "Highlighted line " << Range.Line << ", " << Range.ColumnStart
<< " -> ";
if (Range.ColumnEnd == std::numeric_limits<unsigned>::max()) {
outs() << "?\n";
} else {
outs() << Range.ColumnEnd << "\n";
}
}
}
}
void SourceCoverageView::createRegionMarkers(SourceCoverageDataManager &Data) {
for (const auto &Region : Data.getSourceRegions()) {
if (Region.first.LineStart >= LineStart)
Markers.push_back(RegionMarker(Region.first.LineStart,
Region.first.ColumnStart, Region.second));
}
if (Options.Debug) {
for (const auto &Marker : Markers) {
outs() << "Marker at " << Marker.Line << ":" << Marker.Column << " = "
<< Marker.ExecutionCount << "\n";
}
}
}
void SourceCoverageView::load(SourceCoverageDataManager &Data) {
if (Options.ShowLineStats)
createLineCoverageInfo(Data);
if (Options.Colors)
createHighlightRanges(Data);
if (Options.ShowRegionMarkers)
createRegionMarkers(Data);
}