Mercurial > hg > CbC > CbC_llvm
diff tools/llvm-cov/SourceCoverageView.cpp @ 77:54457678186b LLVM3.6
LLVM 3.6
author | Kaito Tokumori <e105711@ie.u-ryukyu.ac.jp> |
---|---|
date | Mon, 08 Sep 2014 22:06:00 +0900 |
parents | |
children | 60c9769439b8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/llvm-cov/SourceCoverageView.cpp Mon Sep 08 22:06:00 2014 +0900 @@ -0,0 +1,411 @@ +//===- 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); +}