diff options
-rw-r--r-- | llvm/test/tools/llvm-cov/showLineExecutionCounts.cpp | 31 | ||||
-rw-r--r-- | llvm/test/tools/llvm-cov/style.test | 5 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/CodeCoverage.cpp | 2 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/SourceCoverageView.h | 3 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp | 126 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/SourceCoverageViewHTML.h | 10 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/SourceCoverageViewText.cpp | 9 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/SourceCoverageViewText.h | 3 |
8 files changed, 154 insertions, 35 deletions
diff --git a/llvm/test/tools/llvm-cov/showLineExecutionCounts.cpp b/llvm/test/tools/llvm-cov/showLineExecutionCounts.cpp index f544832..8d4397a 100644 --- a/llvm/test/tools/llvm-cov/showLineExecutionCounts.cpp +++ b/llvm/test/tools/llvm-cov/showLineExecutionCounts.cpp @@ -35,9 +35,9 @@ int main() { // TEXT: [[@LINE]]| 161|int main( // RUN: FileCheck -check-prefixes=TEXT,WHOLE-FILE -input-file %t.dir/coverage/tmp/showLineExecutionCounts.cpp.txt %s // RUN: FileCheck -check-prefixes=TEXT,FILTER -input-file %t.dir/functions.txt %s // -// Test index creation. -// RUN: FileCheck -check-prefix=INDEX -input-file %t.dir/index.txt %s -// INDEX: showLineExecutionCounts.cpp.txt +// RUN: llvm-cov export %S/Inputs/lineExecutionCounts.covmapping -instr-profile %t.profdata -name=main 2>/dev/null > %t.export.json +// RUN: FileCheck -input-file %t.export.json %S/Inputs/lineExecutionCounts.json +// RUN: cat %t.export.json | %python -c "import json, sys; json.loads(sys.stdin.read())" // // Test html output. // RUN: llvm-cov show %S/Inputs/lineExecutionCounts.covmapping -format html -o %t.html.dir -instr-profile %t.profdata -filename-equivalence %s @@ -69,7 +69,24 @@ int main() { // TEXT: [[@LINE]]| 161|int main( // HTML: <td class='line-number'><a name='L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre>} // HTML-WHOLE-FILE: <td class='line-number'><a name='L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='uncovered-line'></td><td class='code'><pre>// after // HTML-FILTER-NOT: <td class='line-number'><a name='L[[@LINE-45]]'><pre>[[@LINE-45]]</pre></a></td><td class='uncovered-line'></td><td class='code'><pre>// after - -// RUN: llvm-cov export %S/Inputs/lineExecutionCounts.covmapping -instr-profile %t.profdata -name=main 2>/dev/null > %t.export.json -// RUN: FileCheck -input-file %t.export.json %S/Inputs/lineExecutionCounts.json -// RUN: cat %t.export.json | %python -c "import json, sys; json.loads(sys.stdin.read())" +// +// Test index creation. +// RUN: FileCheck -check-prefix=TEXT-INDEX -input-file %t.dir/index.txt %s +// TEXT-INDEX: Filename +// TEXT-INDEX-NEXT: --- +// TEXT-INDEX-NEXT: {{.*}}showLineExecutionCounts.cpp +// +// RUN: FileCheck -check-prefix HTML-INDEX -input-file %t.html.dir/index.html %s +// HTML-INDEX-LABEL: <table> +// HTML-INDEX: <td class='column-entry'>Filename</td> +// HTML-INDEX: <td class='column-entry'>Region Coverage</td> +// HTML-INDEX: <td class='column-entry'>Function Coverage</td> +// HTML-INDEX: <td class='column-entry'>Line Coverage</td> +// HTML-INDEX: <a href='coverage{{.*}}showLineExecutionCounts.cpp.html'{{.*}}showLineExecutionCounts.cpp</a> +// HTML-INDEX: <td class='column-entry-red'> +// HTML-INDEX: 70.00% (7/10) +// HTML-INDEX: <td class='column-entry-green'> +// HTML-INDEX: 100.00% (1/1) +// HTML-INDEX: <td class='column-entry-yellow'> +// HTML-INDEX: 80.00% (16/20) +// HTML-INDEX: TOTALS diff --git a/llvm/test/tools/llvm-cov/style.test b/llvm/test/tools/llvm-cov/style.test index 543d508..55ebe85 100644 --- a/llvm/test/tools/llvm-cov/style.test +++ b/llvm/test/tools/llvm-cov/style.test @@ -15,6 +15,11 @@ STYLE-DAG: .source-name-title STYLE-DAG: .centered STYLE-DAG: .expansion-view STYLE-DAG: .line-number +STYLE-DAG: .light-row +STYLE-DAG: .column-entry +STYLE-DAG: .column-entry-yellow +STYLE-DAG: .column-entry-red +STYLE-DAG: .column-entry-green STYLE-DAG: .covered-line STYLE-DAG: .uncovered-line STYLE-DAG: .tooltip diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp index e5b9b69..6edca9f 100644 --- a/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -679,7 +679,7 @@ int CodeCoverageTool::show(int argc, const char **argv, // Create an index out of the source files. if (ViewOpts.hasOutputDirectory()) { - if (Error E = Printer->createIndexFile(SourceFiles)) { + if (Error E = Printer->createIndexFile(SourceFiles, *Coverage)) { error("Could not create index file!", toString(std::move(E))); return 1; } diff --git a/llvm/tools/llvm-cov/SourceCoverageView.h b/llvm/tools/llvm-cov/SourceCoverageView.h index 3f58937..b1bbff8 100644 --- a/llvm/tools/llvm-cov/SourceCoverageView.h +++ b/llvm/tools/llvm-cov/SourceCoverageView.h @@ -142,7 +142,8 @@ public: virtual void closeViewFile(OwnedStream OS) = 0; /// \brief Create an index which lists reports for the given source files. - virtual Error createIndexFile(ArrayRef<StringRef> SourceFiles) = 0; + virtual Error createIndexFile(ArrayRef<StringRef> SourceFiles, + const coverage::CoverageMapping &Coverage) = 0; /// @} }; diff --git a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp index 17353b5..1c078ee9 100644 --- a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp @@ -11,11 +11,13 @@ /// //===----------------------------------------------------------------------===// +#include "CoverageReport.h" #include "SourceCoverageViewHTML.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" #include "llvm/Support/Path.h" using namespace llvm; @@ -73,7 +75,7 @@ const char *BeginHeader = const char *CSSForCoverage = R"(.red { - background-color: #FFD0D0; + background-color: #ffd0d0; } .cyan { background-color: cyan; @@ -110,6 +112,25 @@ pre { table { border-collapse: collapse; } +.light-row { + background: #ffffff; + border: 1px solid #dbdbdb; +} +.column-entry { + text-align: right; +} +.column-entry-yellow { + text-align: right; + background-color: #ffffd0; +} +.column-entry-red { + text-align: right; + background-color: #ffd0d0; +} +.column-entry-green { + text-align: right; + background-color: #d0ffd0; +} .line-number { text-align: right; color: #aaa; @@ -284,16 +305,88 @@ void CoveragePrinterHTML::closeViewFile(OwnedStream OS) { emitEpilog(*OS.get()); } -Error CoveragePrinterHTML::createIndexFile(ArrayRef<StringRef> SourceFiles) { +/// Emit column labels for the table in the index. +static void emitColumnLabelsForIndex(raw_ostream &OS) { + SmallVector<std::string, 4> Columns; + for (const char *Label : + {"Filename", "Region Coverage", "Function Coverage", "Line Coverage"}) + Columns.emplace_back(tag("td", Label, "column-entry")); + OS << tag("tr", join(Columns.begin(), Columns.end(), "")); +} + +/// Render a file coverage summary (\p FCS) in a table row. If \p IsTotals is +/// false, link the summary to \p SF. +void CoveragePrinterHTML::emitFileSummary(raw_ostream &OS, StringRef SF, + const FileCoverageSummary &FCS, + bool IsTotals) const { + SmallVector<std::string, 4> Columns; + + // Format a coverage triple and add the result to the list of columns. + auto AddCoverageTripleToColumn = [&Columns](unsigned Hit, unsigned Total, + float Pctg) { + std::string S; + { + raw_string_ostream RSO{S}; + RSO << format("%*.2f", 7, Pctg) << "% (" << Hit << '/' << Total << ')'; + } + const char *CellClass = "column-entry-yellow"; + if (Pctg < 80.0) + CellClass = "column-entry-red"; + else if (Hit == Total) + CellClass = "column-entry-green"; + Columns.emplace_back(tag("td", tag("pre", S, "code"), CellClass)); + }; + + // Simplify the display file path, and wrap it in a link if requested. + std::string Filename; + SmallString<128> LinkTextStr(sys::path::relative_path(FCS.Name)); + sys::path::remove_dots(LinkTextStr, /*remove_dot_dots=*/true); + sys::path::native(LinkTextStr); + std::string LinkText = escape(LinkTextStr, Opts); + if (IsTotals) { + Filename = LinkText; + } else { + std::string LinkTarget = + escape(getOutputPath(SF, "html", /*InToplevel=*/false), Opts); + Filename = a(LinkTarget, LinkText); + } + + Columns.emplace_back(tag("td", tag("pre", Filename, "code"))); + AddCoverageTripleToColumn( + FCS.RegionCoverage.NumRegions - FCS.RegionCoverage.NotCovered, + FCS.RegionCoverage.NumRegions, FCS.RegionCoverage.getPercentCovered()); + AddCoverageTripleToColumn(FCS.FunctionCoverage.Executed, + FCS.FunctionCoverage.NumFunctions, + FCS.FunctionCoverage.getPercentCovered()); + AddCoverageTripleToColumn( + FCS.LineCoverage.NumLines - FCS.LineCoverage.NotCovered, + FCS.LineCoverage.NumLines, FCS.LineCoverage.getPercentCovered()); + + OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row"); +} + +Error CoveragePrinterHTML::createIndexFile( + ArrayRef<StringRef> SourceFiles, + const coverage::CoverageMapping &Coverage) { + // Emit the default stylesheet. + auto CSSOrErr = createOutputStream("style", "css", /*InToplevel=*/true); + if (Error E = CSSOrErr.takeError()) + return E; + + OwnedStream CSS = std::move(CSSOrErr.get()); + CSS->operator<<(CSSForCoverage); + + // Emit a file index along with some coverage statistics. auto OSOrErr = createOutputStream("index", "html", /*InToplevel=*/true); if (Error E = OSOrErr.takeError()) return E; auto OS = std::move(OSOrErr.get()); raw_ostream &OSRef = *OS.get(); - // Emit a table containing links to reports for each file in the covmapping. assert(Opts.hasOutputDirectory() && "No output directory for index file"); emitPrelude(OSRef, Opts, getPathToStyle("")); + + // Emit some basic information about the coverage report. if (Opts.hasProjectTitle()) OSRef << BeginProjectTitleDiv << tag("span", escape(Opts.ProjectTitle, Opts)) << EndProjectTitleDiv; @@ -305,28 +398,19 @@ Error CoveragePrinterHTML::createIndexFile(ArrayRef<StringRef> SourceFiles) { << tag("span", escape(Opts.CreatedTimeStr, Opts)) << EndCreatedTimeDiv; OSRef << LineBreak; + + // Emit a table containing links to reports for each file in the covmapping. + CoverageReport Report(Opts, Coverage); OSRef << BeginCenteredDiv << BeginTable; - OSRef << BeginSourceNameDiv << "Index" << EndSourceNameDiv; - for (StringRef SF : SourceFiles) { - SmallString<128> LinkTextStr(sys::path::relative_path(SF)); - sys::path::remove_dots(LinkTextStr, /*remove_dot_dots=*/true); - sys::path::native(LinkTextStr); - std::string LinkText = escape(sys::path::relative_path(LinkTextStr), Opts); - std::string LinkTarget = - escape(getOutputPath(SF, "html", /*InToplevel=*/false), Opts); - OSRef << tag("tr", tag("td", tag("pre", a(LinkTarget, LinkText), "code"))); - } + emitColumnLabelsForIndex(OSRef); + FileCoverageSummary Totals("TOTALS"); + auto FileReports = Report.prepareFileReports(Totals, SourceFiles); + for (unsigned I = 0, E = FileReports.size(); I < E; ++I) + emitFileSummary(OSRef, SourceFiles[I], FileReports[I]); + emitFileSummary(OSRef, "Totals", Totals, /*IsTotals=*/true); OSRef << EndTable << EndCenteredDiv; emitEpilog(OSRef); - // Emit the default stylesheet. - auto CSSOrErr = createOutputStream("style", "css", /*InToplevel=*/true); - if (Error E = CSSOrErr.takeError()) - return E; - - OwnedStream CSS = std::move(CSSOrErr.get()); - CSS->operator<<(CSSForCoverage); - return Error::success(); } diff --git a/llvm/tools/llvm-cov/SourceCoverageViewHTML.h b/llvm/tools/llvm-cov/SourceCoverageViewHTML.h index edbe7c4..bcf2793 100644 --- a/llvm/tools/llvm-cov/SourceCoverageViewHTML.h +++ b/llvm/tools/llvm-cov/SourceCoverageViewHTML.h @@ -18,6 +18,8 @@ namespace llvm { +struct FileCoverageSummary; + /// \brief A coverage printer for html output. class CoveragePrinterHTML : public CoveragePrinter { public: @@ -26,10 +28,16 @@ public: void closeViewFile(OwnedStream OS) override; - Error createIndexFile(ArrayRef<StringRef> SourceFiles) override; + Error createIndexFile(ArrayRef<StringRef> SourceFiles, + const coverage::CoverageMapping &Coverage) override; CoveragePrinterHTML(const CoverageViewOptions &Opts) : CoveragePrinter(Opts) {} + +private: + void emitFileSummary(raw_ostream &OS, StringRef SF, + const FileCoverageSummary &FCS, + bool IsTotals = false) const; }; /// \brief A code coverage view which supports html-based rendering. diff --git a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp index 3d2c067..b0676ca 100644 --- a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp @@ -11,6 +11,7 @@ /// //===----------------------------------------------------------------------===// +#include "CoverageReport.h" #include "SourceCoverageViewText.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" @@ -27,15 +28,17 @@ void CoveragePrinterText::closeViewFile(OwnedStream OS) { OS->operator<<('\n'); } -Error CoveragePrinterText::createIndexFile(ArrayRef<StringRef> SourceFiles) { +Error CoveragePrinterText::createIndexFile( + ArrayRef<StringRef> SourceFiles, + const coverage::CoverageMapping &Coverage) { 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(); - for (StringRef SF : SourceFiles) - OSRef << getOutputPath(SF, "txt", /*InToplevel=*/false) << '\n'; + CoverageReport Report(Opts, Coverage); + Report.renderFileReports(OSRef); return Error::success(); } diff --git a/llvm/tools/llvm-cov/SourceCoverageViewText.h b/llvm/tools/llvm-cov/SourceCoverageViewText.h index fe2642a..6fd35f5 100644 --- a/llvm/tools/llvm-cov/SourceCoverageViewText.h +++ b/llvm/tools/llvm-cov/SourceCoverageViewText.h @@ -26,7 +26,8 @@ public: void closeViewFile(OwnedStream OS) override; - Error createIndexFile(ArrayRef<StringRef> SourceFiles) override; + Error createIndexFile(ArrayRef<StringRef> SourceFiles, + const coverage::CoverageMapping &Coverage) override; CoveragePrinterText(const CoverageViewOptions &Opts) : CoveragePrinter(Opts) {} |