summaryrefslogtreecommitdiff
path: root/llvm/tools/llvm-cov/SourceCoverageViewText.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/tools/llvm-cov/SourceCoverageViewText.cpp')
-rw-r--r--llvm/tools/llvm-cov/SourceCoverageViewText.cpp251
1 files changed, 251 insertions, 0 deletions
diff --git a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp
new file mode 100644
index 000000000000..fcabee2ee69d
--- /dev/null
+++ b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp
@@ -0,0 +1,251 @@
+//===- SourceCoverageViewText.cpp - A text-based code coverage view -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file implements the text-based coverage renderer.
+///
+//===----------------------------------------------------------------------===//
+
+#include "CoverageReport.h"
+#include "SourceCoverageViewText.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+
+using namespace llvm;
+
+Expected<CoveragePrinter::OwnedStream>
+CoveragePrinterText::createViewFile(StringRef Path, bool InToplevel) {
+ return createOutputStream(Path, "txt", InToplevel);
+}
+
+void CoveragePrinterText::closeViewFile(OwnedStream OS) {
+ OS->operator<<('\n');
+}
+
+Error CoveragePrinterText::createIndexFile(
+ ArrayRef<std::string> SourceFiles, const CoverageMapping &Coverage,
+ const CoverageFiltersMatchAll &Filters) {
+ auto OSOrErr = createOutputStream("index", "txt", /*InToplevel=*/true);
+ if (Error E = OSOrErr.takeError())
+ return E;
+ auto OS = std::move(OSOrErr.get());
+ raw_ostream &OSRef = *OS.get();
+
+ CoverageReport Report(Opts, Coverage);
+ Report.renderFileReports(OSRef, SourceFiles, Filters);
+
+ Opts.colored_ostream(OSRef, raw_ostream::CYAN) << "\n"
+ << Opts.getLLVMVersionString();
+
+ return Error::success();
+}
+
+namespace {
+
+static const unsigned LineCoverageColumnWidth = 7;
+static const unsigned LineNumberColumnWidth = 5;
+
+/// Get the width of the leading columns.
+unsigned getCombinedColumnWidth(const CoverageViewOptions &Opts) {
+ return (Opts.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
+ (Opts.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
+}
+
+/// 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::renderViewHeader(raw_ostream &) {}
+
+void SourceCoverageViewText::renderViewFooter(raw_ostream &) {}
+
+void SourceCoverageViewText::renderSourceName(raw_ostream &OS, bool WholeFile) {
+ 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::renderLineSuffix(raw_ostream &, unsigned) {}
+
+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 LineCoverageStats &LCS,
+ unsigned ExpansionCol,
+ unsigned ViewDepth) {
+ StringRef Line = L.Line;
+ unsigned LineNumber = L.LineNo;
+ auto *WrappedSegment = LCS.getWrappedSegment();
+ CoverageSegmentArray Segments = LCS.getLineSegments();
+
+ 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->IsGapRegion &&
+ 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 ((!S->IsGapRegion || (Highlight && *Highlight == raw_ostream::RED)) &&
+ S->HasCount && S->Count == 0)
+ Highlight = raw_ostream::RED;
+ else if (Col == ExpansionCol)
+ Highlight = raw_ostream::CYAN;
+ 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.getExecutionCount());
+ 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,
+ const LineCoverageStats &Line,
+ unsigned ViewDepth) {
+ renderLinePrefix(OS, ViewDepth);
+ OS.indent(getCombinedColumnWidth(getOptions()));
+
+ CoverageSegmentArray Segments = Line.getLineSegments();
+
+ // Just consider the segments which start *and* end on this line.
+ if (Segments.size() > 1)
+ Segments = Segments.drop_back();
+
+ unsigned PrevColumn = 1;
+ for (const auto *S : Segments) {
+ if (!S->IsRegionEntry)
+ continue;
+ if (S->Count == Line.getExecutionCount())
+ 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;
+
+ if (getOptions().Debug)
+ errs() << "Marker at " << S->Line << ":" << S->Col << " = "
+ << formatCount(S->Count) << "\n";
+ }
+ OS << '\n';
+}
+
+void SourceCoverageViewText::renderExpansionSite(raw_ostream &OS, LineRef L,
+ const LineCoverageStats &LCS,
+ unsigned ExpansionCol,
+ unsigned ViewDepth) {
+ renderLinePrefix(OS, ViewDepth);
+ OS.indent(getCombinedColumnWidth(getOptions()) + (ViewDepth == 0 ? 0 : 1));
+ renderLine(OS, L, LCS, ExpansionCol, ViewDepth);
+}
+
+void SourceCoverageViewText::renderExpansionView(raw_ostream &OS,
+ ExpansionView &ESV,
+ unsigned ViewDepth) {
+ // 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,
+ /*ShowTitle=*/false, ViewDepth + 1);
+}
+
+void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS,
+ InstantiationView &ISV,
+ unsigned ViewDepth) {
+ renderLinePrefix(OS, ViewDepth);
+ OS << ' ';
+ if (!ISV.View)
+ getOptions().colored_ostream(OS, raw_ostream::RED)
+ << "Unexecuted instantiation: " << ISV.FunctionName << "\n";
+ else
+ ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true,
+ /*ShowTitle=*/false, ViewDepth);
+}
+
+void SourceCoverageViewText::renderTitle(raw_ostream &OS, StringRef Title) {
+ if (getOptions().hasProjectTitle())
+ getOptions().colored_ostream(OS, raw_ostream::CYAN)
+ << getOptions().ProjectTitle << "\n";
+
+ getOptions().colored_ostream(OS, raw_ostream::CYAN) << Title << "\n";
+
+ if (getOptions().hasCreatedTime())
+ getOptions().colored_ostream(OS, raw_ostream::CYAN)
+ << getOptions().CreatedTimeStr << "\n";
+}
+
+void SourceCoverageViewText::renderTableHeader(raw_ostream &, unsigned,
+ unsigned) {}