diff --git a/tools/llvm-cov/CodeCoverage.cpp b/tools/llvm-cov/CodeCoverage.cpp index fcc1c6b7d0b..eef4185522e 100644 --- a/tools/llvm-cov/CodeCoverage.cpp +++ b/tools/llvm-cov/CodeCoverage.cpp @@ -284,12 +284,12 @@ void CodeCoverageTool::createExpansionSubView( return; auto SubView = llvm::make_unique(SourceBuffer.get(), Parent.getOptions()); - SourceCoverageDataManager RegionManager; + auto RegionManager = llvm::make_unique(); for (const auto &CR : Function.CountedRegions) { if (CR.FileID == ExpandedRegion.ExpandedFileID) - RegionManager.insert(CR); + RegionManager->insert(CR); } - SubView->load(RegionManager); + SubView->load(std::move(RegionManager)); createExpansionSubViews(*SubView, ExpandedRegion.ExpandedFileID, Function); Parent.addExpansion(ExpandedRegion, std::move(SubView)); } @@ -311,16 +311,16 @@ void CodeCoverageTool::createExpansionSubViews( void CodeCoverageTool::createInstantiationSubView( StringRef SourceFile, const FunctionCoverageMapping &Function, SourceCoverageView &View) { - SourceCoverageDataManager RegionManager; + auto RegionManager = llvm::make_unique(); SmallSet InterestingFileIDs; if (!gatherInterestingFileIDs(SourceFile, Function, InterestingFileIDs)) return; // Get the interesting regions for (const auto &CR : Function.CountedRegions) { if (InterestingFileIDs.count(CR.FileID)) - RegionManager.insert(CR); + RegionManager->insert(CR); } - View.load(RegionManager); + View.load(std::move(RegionManager)); unsigned MainFileID; if (findMainViewFileID(SourceFile, Function, MainFileID)) return; @@ -331,7 +331,7 @@ bool CodeCoverageTool::createSourceFileView( StringRef SourceFile, SourceCoverageView &View, ArrayRef FunctionMappingRecords, bool UseOnlyRegionsInMainFile) { - SourceCoverageDataManager RegionManager; + auto RegionManager = llvm::make_unique(); FunctionInstantiationSetCollector InstantiationSetCollector; for (const auto &Function : FunctionMappingRecords) { @@ -347,14 +347,14 @@ bool CodeCoverageTool::createSourceFileView( // Get the interesting regions for (const auto &CR : Function.CountedRegions) { if (InterestingFileIDs.count(CR.FileID)) - RegionManager.insert(CR); + RegionManager->insert(CR); } InstantiationSetCollector.insert(Function, MainFileID); createExpansionSubViews(View, MainFileID, Function); } - if (RegionManager.getSourceRegions().empty()) + if (RegionManager->getCoverageSegments().empty()) return true; - View.load(RegionManager); + View.load(std::move(RegionManager)); // Show instantiations if (!ViewOpts.ShowFunctionInstantiations) return false; @@ -626,7 +626,7 @@ int CodeCoverageTool::show(int argc, const char **argv, ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << Function.Name << " from " << SourceFile << ":"; outs() << "\n"; - mainView.render(outs()); + mainView.render(outs(), /*WholeFile=*/false); if (FunctionMappingRecords.size() > 1) outs() << "\n"; } @@ -663,7 +663,7 @@ int CodeCoverageTool::show(int argc, const char **argv, ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile << ":"; outs() << "\n"; } - mainView.render(outs()); + mainView.render(outs(), /*Wholefile=*/true); if (SourceFiles.size() > 1) outs() << "\n"; } diff --git a/tools/llvm-cov/RenderingSupport.h b/tools/llvm-cov/RenderingSupport.h index 4778501305f..0271329997d 100644 --- a/tools/llvm-cov/RenderingSupport.h +++ b/tools/llvm-cov/RenderingSupport.h @@ -49,9 +49,10 @@ inline raw_ostream &operator<<(const ColoredRawOstream &OS, T &&Value) { /// is true. Returns an object that resets the color when destroyed. inline ColoredRawOstream colored_ostream(raw_ostream &OS, raw_ostream::Colors Color, - bool IsColorUsed = true) { + bool IsColorUsed = true, + bool Bold = false, bool BG = false) { if (IsColorUsed) - OS.changeColor(Color); + OS.changeColor(Color, Bold, BG); return ColoredRawOstream(OS, IsColorUsed); } } diff --git a/tools/llvm-cov/SourceCoverageDataManager.cpp b/tools/llvm-cov/SourceCoverageDataManager.cpp index 6f7c8e5d985..a60ea6e2586 100644 --- a/tools/llvm-cov/SourceCoverageDataManager.cpp +++ b/tools/llvm-cov/SourceCoverageDataManager.cpp @@ -19,32 +19,86 @@ using namespace coverage; void SourceCoverageDataManager::insert(const CountedRegion &CR) { Regions.push_back(CR); - Uniqued = false; + Segments.clear(); } -ArrayRef SourceCoverageDataManager::getSourceRegions() { - if (Uniqued || Regions.size() <= 1) - return Regions; +namespace { +class SegmentBuilder { + std::vector Segments; + SmallVector ActiveRegions; - // Sort the regions given that they're all in the same file at this point. - std::sort(Regions.begin(), Regions.end(), - [](const CountedRegion &LHS, const CountedRegion &RHS) { - return LHS.startLoc() < RHS.startLoc(); - }); - - // Merge duplicate source ranges and sum their execution counts. - auto Prev = Regions.begin(); - for (auto I = Prev + 1, E = Regions.end(); I != E; ++I) { - if (I->startLoc() == Prev->startLoc() && I->endLoc() == Prev->endLoc()) { - Prev->ExecutionCount += I->ExecutionCount; - continue; - } - ++Prev; - *Prev = *I; + /// Start a segment with no count specified. + void startSegment(unsigned Line, unsigned Col, bool IsRegionEntry) { + Segments.emplace_back(Line, Col, IsRegionEntry); } - ++Prev; - Regions.erase(Prev, Regions.end()); - Uniqued = true; - return Regions; + /// Start a segment with the given Region's count. + void startSegment(unsigned Line, unsigned Col, bool IsRegionEntry, + const CountedRegion &Region) { + if (Segments.empty()) + Segments.emplace_back(Line, Col, IsRegionEntry); + CoverageSegment S = Segments.back(); + // Avoid creating empty regions. + if (S.Line != Line || S.Col != Col) { + Segments.emplace_back(Line, Col, IsRegionEntry); + S = Segments.back(); + } + // Set this region's count. + if (Region.Kind != coverage::CounterMappingRegion::SkippedRegion) + Segments.back().setCount(Region.ExecutionCount); + } + + /// Start a segment for the given region. + void startSegment(const CountedRegion &Region) { + startSegment(Region.LineStart, Region.ColumnStart, true, Region); + } + + /// Pop the top region off of the active stack, starting a new segment with + /// the containing Region's count. + void popRegion() { + const CountedRegion *Active = ActiveRegions.back(); + unsigned Line = Active->LineEnd, Col = Active->ColumnEnd; + ActiveRegions.pop_back(); + if (ActiveRegions.empty()) + startSegment(Line, Col, /*IsRegionEntry=*/false); + else + startSegment(Line, Col, /*IsRegionEntry=*/false, *ActiveRegions.back()); + } + +public: + /// Build a list of CoverageSegments from a sorted list of Regions. + std::vector + buildSegments(ArrayRef Regions) { + for (const auto &Region : Regions) { + // Pop any regions that end before this one starts. + while (!ActiveRegions.empty() && + ActiveRegions.back()->endLoc() <= Region.startLoc()) + popRegion(); + // Add this region to the stack. + ActiveRegions.push_back(&Region); + startSegment(Region); + } + // Pop any regions that are left in the stack. + while (!ActiveRegions.empty()) + popRegion(); + return Segments; + } +}; +} + +ArrayRef SourceCoverageDataManager::getCoverageSegments() { + if (Segments.empty()) { + // Sort the regions given that they're all in the same file at this point. + std::sort(Regions.begin(), Regions.end(), + [](const CountedRegion &LHS, const CountedRegion &RHS) { + if (LHS.startLoc() == RHS.startLoc()) + // When LHS completely contains RHS, we sort LHS first. + return RHS.endLoc() < LHS.endLoc(); + return LHS.startLoc() < RHS.startLoc(); + }); + + Segments = SegmentBuilder().buildSegments(Regions); + } + + return Segments; } diff --git a/tools/llvm-cov/SourceCoverageDataManager.h b/tools/llvm-cov/SourceCoverageDataManager.h index abeca192e64..eec175222b4 100644 --- a/tools/llvm-cov/SourceCoverageDataManager.h +++ b/tools/llvm-cov/SourceCoverageDataManager.h @@ -19,19 +19,33 @@ namespace llvm { +struct CoverageSegment { + unsigned Line; + unsigned Col; + bool IsRegionEntry; + uint64_t Count; + bool HasCount; + + CoverageSegment(unsigned Line, unsigned Col, bool IsRegionEntry) + : Line(Line), Col(Col), IsRegionEntry(IsRegionEntry), + Count(0), HasCount(false) {} + void setCount(uint64_t NewCount) { + Count = NewCount; + HasCount = true; + } +}; + /// \brief Partions mapping regions by their kind and sums /// the execution counts of the regions that start at the same location. class SourceCoverageDataManager { std::vector Regions; - bool Uniqued; + std::vector Segments; public: - SourceCoverageDataManager() : Uniqued(false) {} - void insert(const coverage::CountedRegion &CR); - /// \brief Return the source regions in order of first to last occurring. - ArrayRef getSourceRegions(); + /// \brief Return a sequence of non-overlapping coverage segments. + ArrayRef getCoverageSegments(); }; } // namespace llvm diff --git a/tools/llvm-cov/SourceCoverageView.cpp b/tools/llvm-cov/SourceCoverageView.cpp index a83bd54e15e..2a212e5173d 100644 --- a/tools/llvm-cov/SourceCoverageView.cpp +++ b/tools/llvm-cov/SourceCoverageView.cpp @@ -12,54 +12,54 @@ //===----------------------------------------------------------------------===// #include "SourceCoverageView.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/LineIterator.h" using namespace llvm; void SourceCoverageView::renderLine(raw_ostream &OS, StringRef Line, - ArrayRef Ranges) { - if (Ranges.empty()) { - OS << Line << "\n"; - return; - } - if (Line.empty()) - Line = " "; + int64_t LineNumber, + const CoverageSegment *WrappedSegment, + ArrayRef Segments, + unsigned ExpansionCol) { + Optional Highlight; + SmallVector, 2> HighlightedRanges; - unsigned PrevColumnStart = 0; - unsigned Start = 1; - for (const auto &Range : Ranges) { - if (PrevColumnStart == Range.ColumnStart) - continue; + // The first segment overlaps from a previous line, so we treat it specially. + if (WrappedSegment && WrappedSegment->HasCount && WrappedSegment->Count == 0) + Highlight = raw_ostream::RED; - // 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(); + // 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, + Options.Colors && Highlight, /*Bold=*/false, /*BG=*/true) + << Line.substr(Col - 1, End - Col); + if (Options.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 - OS << Line.substr(Start - 1, Line.size() - Start + 1); + colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR, + Options.Colors && Highlight, /*Bold=*/false, /*BG=*/true) + << Line.substr(Col - 1, Line.size() - Col + 1); OS << "\n"; if (Options.Debug) { - for (const auto &Range : Ranges) { - errs() << "Highlighted line " << Range.Line << ", " << Range.ColumnStart - << " -> "; - if (Range.ColumnEnd == std::numeric_limits::max()) { - errs() << "?\n"; - } else { - errs() << Range.ColumnEnd << "\n"; - } - } + for (const auto &Range : HighlightedRanges) + errs() << "Highlighted line " << LineNumber << ", " << Range.first + << " -> " << Range.second << "\n"; + if (Highlight) + errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n"; } } @@ -109,18 +109,20 @@ void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS, OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|'; } -void SourceCoverageView::renderRegionMarkers(raw_ostream &OS, - ArrayRef Regions) { +void SourceCoverageView::renderRegionMarkers( + raw_ostream &OS, ArrayRef Segments) { SmallString<32> Buffer; raw_svector_ostream BufferOS(Buffer); unsigned PrevColumn = 1; - for (const auto &Region : Regions) { + for (const auto *S : Segments) { + if (!S->IsRegionEntry) + continue; // Skip to the new region - if (Region.Column > PrevColumn) - OS.indent(Region.Column - PrevColumn); - PrevColumn = Region.Column + 1; - BufferOS << Region.ExecutionCount; + if (S->Col > PrevColumn) + OS.indent(S->Col - PrevColumn); + PrevColumn = S->Col + 1; + BufferOS << S->Count; StringRef Str = BufferOS.str(); // Trim the execution count Str = Str.substr(0, std::min(Str.size(), (size_t)7)); @@ -130,87 +132,14 @@ void SourceCoverageView::renderRegionMarkers(raw_ostream &OS, } OS << "\n"; - if (Options.Debug) { - for (const auto &Region : Regions) { - errs() << "Marker at " << Region.Line << ":" << Region.Column << " = " - << Region.ExecutionCount << "\n"; - } - } + if (Options.Debug) + for (const auto *S : Segments) + errs() << "Marker at " << S->Line << ":" << S->Col << " = " << S->Count + << (S->IsRegionEntry ? "\n" : " (pop)\n"); } -/// \brief Insert a new highlighting range into the line's highlighting ranges -/// Return line's new highlighting ranges in result. -static void insertExpansionHighlightRange( - ArrayRef Ranges, - unsigned Line, unsigned StartCol, unsigned EndCol, - SmallVectorImpl &Result) { - auto RangeToInsert = SourceCoverageView::HighlightRange( - Line, StartCol, EndCol, SourceCoverageView::HighlightRange::Expanded); - 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]); -} - -template -ArrayRef gatherLineItems(size_t &CurrentIdx, const std::vector &Items, - unsigned LineNo) { - auto PrevIdx = CurrentIdx; - auto E = Items.size(); - while (CurrentIdx < E && Items[CurrentIdx].Line == LineNo) - ++CurrentIdx; - return ArrayRef(Items.data() + PrevIdx, CurrentIdx - PrevIdx); -} - -void SourceCoverageView::render(raw_ostream &OS, unsigned IndentLevel) { - SmallVector AdjustedLineHighlightRanges; - size_t CurrentHighlightRange = 0; - size_t CurrentRegionMarker = 0; - - line_iterator Lines(File, /*SkipBlanks=*/false); - // Advance the line iterator to the first line. - while (Lines.line_number() < LineOffset) - ++Lines; - +void SourceCoverageView::render(raw_ostream &OS, bool WholeFile, + unsigned IndentLevel) { // The width of the leading columns unsigned CombinedColumnWidth = (Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) + @@ -228,66 +157,88 @@ void SourceCoverageView::render(raw_ostream &OS, unsigned IndentLevel) { auto NextISV = InstantiationSubViews.begin(); auto EndISV = InstantiationSubViews.end(); - for (size_t I = 0, E = LineStats.size(); I < E && !Lines.is_at_eof(); - ++I, ++Lines) { - unsigned LineNo = Lines.line_number(); + // Get the coverage information for the file. + auto CoverageSegments = RegionManager->getCoverageSegments(); + assert(CoverageSegments.size() && "View with no coverage?"); + auto NextSegment = CoverageSegments.begin(); + auto EndSegment = CoverageSegments.end(); - 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 (NextESV != EndESV && NextESV->getLine() == LineNo && Options.Colors) { - insertExpansionHighlightRange( - LineHighlightRanges, NextESV->getLine(), NextESV->getStartCol(), - NextESV->getEndCol(), AdjustedLineHighlightRanges); - LineRanges = AdjustedLineHighlightRanges; + const CoverageSegment *WrappedSegment = nullptr; + SmallVector LineSegments; + for (line_iterator LI(File, /*SkipBlanks=*/false); !LI.is_at_eof(); ++LI) { + // If we aren't rendering the whole file, we need to filter out the prologue + // and epilogue. + if (!WholeFile) { + if (NextSegment == EndSegment) + break; + else if (LI.line_number() < NextSegment->Line) + continue; } + // Collect the coverage information relevant to this line. + if (LineSegments.size()) + WrappedSegment = LineSegments.back(); + LineSegments.clear(); + while (NextSegment != EndSegment && NextSegment->Line == LI.line_number()) + LineSegments.push_back(&*NextSegment++); + + // Calculate a count to be for the line as a whole. + LineCoverageInfo LineCount; + if (WrappedSegment && WrappedSegment->HasCount) + LineCount.addRegionCount(WrappedSegment->Count); + for (const auto *S : LineSegments) + if (S->HasCount && S->IsRegionEntry) + LineCount.addRegionStartCount(S->Count); + + // Render the line prefix. + renderIndent(OS, IndentLevel); + if (Options.ShowLineStats) + renderLineCoverageColumn(OS, LineCount); + if (Options.ShowLineNumbers) + renderLineNumberColumn(OS, LI.line_number()); + + // If there are expansion subviews, we want to highlight the first one. + unsigned ExpansionColumn = 0; + if (NextESV != EndESV && NextESV->getLine() == LI.line_number() && + Options.Colors) + ExpansionColumn = NextESV->getStartCol(); + // Display the source code for the current line. - StringRef Line = *Lines; - renderLine(OS, Line, LineRanges); + renderLine(OS, *LI, LI.line_number(), WrappedSegment, LineSegments, + ExpansionColumn); // Show the region markers. - bool ShowMarkers = !Options.ShowLineStatsOrRegionMarkers || - LineStats[I].hasMultipleRegions(); - auto LineMarkers = gatherLineItems(CurrentRegionMarker, Markers, LineNo); - if (ShowMarkers && !LineMarkers.empty()) { + if (Options.ShowRegionMarkers && (!Options.ShowLineStatsOrRegionMarkers || + LineCount.hasMultipleRegions()) && + !LineSegments.empty()) { renderIndent(OS, IndentLevel); OS.indent(CombinedColumnWidth); - renderRegionMarkers(OS, LineMarkers); + renderRegionMarkers(OS, LineSegments); } // Show the expansions and instantiations for this line. unsigned NestedIndent = IndentLevel + 1; bool RenderedSubView = false; - for (; NextESV != EndESV && NextESV->getLine() == LineNo; ++NextESV) { + 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. - insertExpansionHighlightRange( - LineHighlightRanges, NextESV->getLine(), NextESV->getStartCol(), - NextESV->getEndCol(), AdjustedLineHighlightRanges); + ExpansionColumn = NextESV->getStartCol(); renderIndent(OS, IndentLevel); OS.indent(CombinedColumnWidth + (IndentLevel == 0 ? 0 : 1)); - renderLine(OS, Line, AdjustedLineHighlightRanges); + renderLine(OS, *LI, LI.line_number(), WrappedSegment, LineSegments, + ExpansionColumn); renderViewDivider(NestedIndent, DividerWidth, OS); OS << "\n"; } // Render the child subview - NextESV->View->render(OS, NestedIndent); + NextESV->View->render(OS, false, NestedIndent); RenderedSubView = true; } - for (; NextISV != EndISV && NextISV->Line == LineNo; ++NextISV) { + for (; NextISV != EndISV && NextISV->Line == LI.line_number(); ++NextISV) { renderViewDivider(NestedIndent, DividerWidth, OS); OS << "\n"; renderIndent(OS, NestedIndent); @@ -295,7 +246,7 @@ void SourceCoverageView::render(raw_ostream &OS, unsigned IndentLevel) { Options.colored_ostream(OS, raw_ostream::CYAN) << NextISV->FunctionName << ":"; OS << "\n"; - NextISV->View->render(OS, NestedIndent); + NextISV->View->render(OS, false, NestedIndent); RenderedSubView = true; } if (RenderedSubView) { @@ -304,89 +255,3 @@ void SourceCoverageView::render(raw_ostream &OS, unsigned IndentLevel) { } } } - -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 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::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::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); -} diff --git a/tools/llvm-cov/SourceCoverageView.h b/tools/llvm-cov/SourceCoverageView.h index 80669d37f76..54a11d8c51c 100644 --- a/tools/llvm-cov/SourceCoverageView.h +++ b/tools/llvm-cov/SourceCoverageView.h @@ -77,7 +77,7 @@ struct InstantiationView { /// \brief A code coverage view of a specific source file. /// It can have embedded coverage views. class SourceCoverageView { -public: +private: /// \brief Coverage information for a single line. struct LineCoverageInfo { uint64_t ExecutionCount; @@ -98,90 +98,22 @@ public: void addRegionCount(uint64_t Count) { Mapped = true; - ExecutionCount = Count; + if (!RegionCount) + ExecutionCount = Count; } }; - /// \brief A marker that points at the start - /// of a specific mapping region. - struct RegionMarker { - unsigned Line, Column; - uint64_t ExecutionCount; - - RegionMarker(unsigned Line, unsigned Column, uint64_t Value) - : Line(Line), Column(Column), ExecutionCount(Value) {} - }; - - /// \brief A single line source range used to - /// render highlighted text. - struct HighlightRange { - enum HighlightKind { - /// The code that wasn't executed. - NotCovered, - - /// The region of code that was expanded. - Expanded - }; - HighlightKind Kind; - unsigned Line; - unsigned ColumnStart; - unsigned ColumnEnd; - - HighlightRange(unsigned Line, unsigned ColumnStart, unsigned ColumnEnd, - HighlightKind Kind = NotCovered) - : Kind(Kind), Line(Line), ColumnStart(ColumnStart), - ColumnEnd(ColumnEnd) {} - - bool operator<(const HighlightRange &Other) const { - if (Line == Other.Line) - return ColumnStart < Other.ColumnStart; - return Line < Other.Line; - } - - bool columnStartOverlaps(const HighlightRange &Other) const { - return ColumnStart <= Other.ColumnStart && ColumnEnd > Other.ColumnStart; - } - bool columnEndOverlaps(const HighlightRange &Other) const { - return ColumnEnd >= Other.ColumnEnd && ColumnStart < Other.ColumnEnd; - } - bool contains(const HighlightRange &Other) const { - if (Line != Other.Line) - return false; - return ColumnStart <= Other.ColumnStart && ColumnEnd >= Other.ColumnEnd; - } - - bool overlaps(const HighlightRange &Other) const { - if (Line != Other.Line) - return false; - return columnStartOverlaps(Other) || columnEndOverlaps(Other); - } - }; - -private: const MemoryBuffer &File; const CoverageViewOptions &Options; - unsigned LineOffset; + std::unique_ptr RegionManager; std::vector ExpansionSubViews; std::vector InstantiationSubViews; - std::vector LineStats; - std::vector HighlightRanges; - std::vector Markers; - - /// \brief Initialize the visible source range for this view. - void setUpVisibleRange(SourceCoverageDataManager &Data); - - /// \brief Create the line coverage information using the coverage data. - void createLineCoverageInfo(SourceCoverageDataManager &Data); - - /// \brief Create the line highlighting ranges using the coverage data. - void createHighlightRanges(SourceCoverageDataManager &Data); - - /// \brief Create the region markers using the coverage data. - void createRegionMarkers(SourceCoverageDataManager &Data); /// \brief Render a source line with highlighting. - void renderLine(raw_ostream &OS, StringRef Line, - ArrayRef Ranges); + void renderLine(raw_ostream &OS, StringRef Line, int64_t LineNumber, + const CoverageSegment *WrappedSegment, + ArrayRef Segments, + unsigned ExpansionCol); void renderIndent(raw_ostream &OS, unsigned Level); @@ -194,7 +126,8 @@ private: void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo); /// \brief Render all the region's execution counts on a line. - void renderRegionMarkers(raw_ostream &OS, ArrayRef Regions); + void renderRegionMarkers(raw_ostream &OS, + ArrayRef Segments); static const unsigned LineCoverageColumnWidth = 7; static const unsigned LineNumberColumnWidth = 5; @@ -202,7 +135,7 @@ private: public: SourceCoverageView(const MemoryBuffer &File, const CoverageViewOptions &Options) - : File(File), Options(Options), LineOffset(0) {} + : File(File), Options(Options) {} const CoverageViewOptions &getOptions() const { return Options; } @@ -220,11 +153,13 @@ public: /// \brief Print the code coverage information for a specific /// portion of a source file to the output stream. - void render(raw_ostream &OS, unsigned IndentLevel = 0); + void render(raw_ostream &OS, bool WholeFile, unsigned IndentLevel = 0); /// \brief Load the coverage information required for rendering /// from the mapping regions in the data manager. - void load(SourceCoverageDataManager &Data); + void load(std::unique_ptr Data) { + RegionManager = std::move(Data); + } }; } // namespace llvm