10b57cec5SDimitry Andric //===- SourceCoverageViewHTML.cpp - A html code coverage view -------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric /// 90b57cec5SDimitry Andric /// \file This file implements the html coverage renderer. 100b57cec5SDimitry Andric /// 110b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 120b57cec5SDimitry Andric 130b57cec5SDimitry Andric #include "CoverageReport.h" 140b57cec5SDimitry Andric #include "SourceCoverageViewHTML.h" 150b57cec5SDimitry Andric #include "llvm/ADT/Optional.h" 160b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 170b57cec5SDimitry Andric #include "llvm/ADT/StringExtras.h" 180b57cec5SDimitry Andric #include "llvm/Support/Format.h" 190b57cec5SDimitry Andric #include "llvm/Support/Path.h" 200b57cec5SDimitry Andric 210b57cec5SDimitry Andric using namespace llvm; 220b57cec5SDimitry Andric 230b57cec5SDimitry Andric namespace { 240b57cec5SDimitry Andric 250b57cec5SDimitry Andric // Return a string with the special characters in \p Str escaped. 260b57cec5SDimitry Andric std::string escape(StringRef Str, const CoverageViewOptions &Opts) { 270b57cec5SDimitry Andric std::string TabExpandedResult; 280b57cec5SDimitry Andric unsigned ColNum = 0; // Record the column number. 290b57cec5SDimitry Andric for (char C : Str) { 300b57cec5SDimitry Andric if (C == '\t') { 310b57cec5SDimitry Andric // Replace '\t' with up to TabSize spaces. 320b57cec5SDimitry Andric unsigned NumSpaces = Opts.TabSize - (ColNum % Opts.TabSize); 335ffd83dbSDimitry Andric TabExpandedResult.append(NumSpaces, ' '); 340b57cec5SDimitry Andric ColNum += NumSpaces; 350b57cec5SDimitry Andric } else { 360b57cec5SDimitry Andric TabExpandedResult += C; 370b57cec5SDimitry Andric if (C == '\n' || C == '\r') 380b57cec5SDimitry Andric ColNum = 0; 390b57cec5SDimitry Andric else 400b57cec5SDimitry Andric ++ColNum; 410b57cec5SDimitry Andric } 420b57cec5SDimitry Andric } 430b57cec5SDimitry Andric std::string EscapedHTML; 440b57cec5SDimitry Andric { 450b57cec5SDimitry Andric raw_string_ostream OS{EscapedHTML}; 460b57cec5SDimitry Andric printHTMLEscaped(TabExpandedResult, OS); 470b57cec5SDimitry Andric } 480b57cec5SDimitry Andric return EscapedHTML; 490b57cec5SDimitry Andric } 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric // Create a \p Name tag around \p Str, and optionally set its \p ClassName. 520b57cec5SDimitry Andric std::string tag(const std::string &Name, const std::string &Str, 530b57cec5SDimitry Andric const std::string &ClassName = "") { 540b57cec5SDimitry Andric std::string Tag = "<" + Name; 550b57cec5SDimitry Andric if (!ClassName.empty()) 560b57cec5SDimitry Andric Tag += " class='" + ClassName + "'"; 570b57cec5SDimitry Andric return Tag + ">" + Str + "</" + Name + ">"; 580b57cec5SDimitry Andric } 590b57cec5SDimitry Andric 600b57cec5SDimitry Andric // Create an anchor to \p Link with the label \p Str. 610b57cec5SDimitry Andric std::string a(const std::string &Link, const std::string &Str, 620b57cec5SDimitry Andric const std::string &TargetName = "") { 630b57cec5SDimitry Andric std::string Name = TargetName.empty() ? "" : ("name='" + TargetName + "' "); 640b57cec5SDimitry Andric return "<a " + Name + "href='" + Link + "'>" + Str + "</a>"; 650b57cec5SDimitry Andric } 660b57cec5SDimitry Andric 670b57cec5SDimitry Andric const char *BeginHeader = 680b57cec5SDimitry Andric "<head>" 690b57cec5SDimitry Andric "<meta name='viewport' content='width=device-width,initial-scale=1'>" 700b57cec5SDimitry Andric "<meta charset='UTF-8'>"; 710b57cec5SDimitry Andric 720b57cec5SDimitry Andric const char *CSSForCoverage = 730b57cec5SDimitry Andric R"(.red { 740b57cec5SDimitry Andric background-color: #ffd0d0; 750b57cec5SDimitry Andric } 760b57cec5SDimitry Andric .cyan { 770b57cec5SDimitry Andric background-color: cyan; 780b57cec5SDimitry Andric } 790b57cec5SDimitry Andric body { 800b57cec5SDimitry Andric font-family: -apple-system, sans-serif; 810b57cec5SDimitry Andric } 820b57cec5SDimitry Andric pre { 830b57cec5SDimitry Andric margin-top: 0px !important; 840b57cec5SDimitry Andric margin-bottom: 0px !important; 850b57cec5SDimitry Andric } 860b57cec5SDimitry Andric .source-name-title { 870b57cec5SDimitry Andric padding: 5px 10px; 880b57cec5SDimitry Andric border-bottom: 1px solid #dbdbdb; 890b57cec5SDimitry Andric background-color: #eee; 900b57cec5SDimitry Andric line-height: 35px; 910b57cec5SDimitry Andric } 920b57cec5SDimitry Andric .centered { 930b57cec5SDimitry Andric display: table; 940b57cec5SDimitry Andric margin-left: left; 950b57cec5SDimitry Andric margin-right: auto; 960b57cec5SDimitry Andric border: 1px solid #dbdbdb; 970b57cec5SDimitry Andric border-radius: 3px; 980b57cec5SDimitry Andric } 990b57cec5SDimitry Andric .expansion-view { 1000b57cec5SDimitry Andric background-color: rgba(0, 0, 0, 0); 1010b57cec5SDimitry Andric margin-left: 0px; 1020b57cec5SDimitry Andric margin-top: 5px; 1030b57cec5SDimitry Andric margin-right: 5px; 1040b57cec5SDimitry Andric margin-bottom: 5px; 1050b57cec5SDimitry Andric border: 1px solid #dbdbdb; 1060b57cec5SDimitry Andric border-radius: 3px; 1070b57cec5SDimitry Andric } 1080b57cec5SDimitry Andric table { 1090b57cec5SDimitry Andric border-collapse: collapse; 1100b57cec5SDimitry Andric } 1110b57cec5SDimitry Andric .light-row { 1120b57cec5SDimitry Andric background: #ffffff; 1130b57cec5SDimitry Andric border: 1px solid #dbdbdb; 1140b57cec5SDimitry Andric } 1150b57cec5SDimitry Andric .light-row-bold { 1160b57cec5SDimitry Andric background: #ffffff; 1170b57cec5SDimitry Andric border: 1px solid #dbdbdb; 1180b57cec5SDimitry Andric font-weight: bold; 1190b57cec5SDimitry Andric } 1200b57cec5SDimitry Andric .column-entry { 1210b57cec5SDimitry Andric text-align: left; 1220b57cec5SDimitry Andric } 1230b57cec5SDimitry Andric .column-entry-bold { 1240b57cec5SDimitry Andric font-weight: bold; 1250b57cec5SDimitry Andric text-align: left; 1260b57cec5SDimitry Andric } 1270b57cec5SDimitry Andric .column-entry-yellow { 1280b57cec5SDimitry Andric text-align: left; 1290b57cec5SDimitry Andric background-color: #ffffd0; 1300b57cec5SDimitry Andric } 1310b57cec5SDimitry Andric .column-entry-yellow:hover { 1320b57cec5SDimitry Andric background-color: #fffff0; 1330b57cec5SDimitry Andric } 1340b57cec5SDimitry Andric .column-entry-red { 1350b57cec5SDimitry Andric text-align: left; 1360b57cec5SDimitry Andric background-color: #ffd0d0; 1370b57cec5SDimitry Andric } 1380b57cec5SDimitry Andric .column-entry-red:hover { 1390b57cec5SDimitry Andric background-color: #fff0f0; 1400b57cec5SDimitry Andric } 1410b57cec5SDimitry Andric .column-entry-green { 1420b57cec5SDimitry Andric text-align: left; 1430b57cec5SDimitry Andric background-color: #d0ffd0; 1440b57cec5SDimitry Andric } 1450b57cec5SDimitry Andric .column-entry-green:hover { 1460b57cec5SDimitry Andric background-color: #f0fff0; 1470b57cec5SDimitry Andric } 1480b57cec5SDimitry Andric .line-number { 1490b57cec5SDimitry Andric text-align: right; 1500b57cec5SDimitry Andric color: #aaa; 1510b57cec5SDimitry Andric } 1520b57cec5SDimitry Andric .covered-line { 1530b57cec5SDimitry Andric text-align: right; 1540b57cec5SDimitry Andric color: #0080ff; 1550b57cec5SDimitry Andric } 1560b57cec5SDimitry Andric .uncovered-line { 1570b57cec5SDimitry Andric text-align: right; 1580b57cec5SDimitry Andric color: #ff3300; 1590b57cec5SDimitry Andric } 1600b57cec5SDimitry Andric .tooltip { 1610b57cec5SDimitry Andric position: relative; 1620b57cec5SDimitry Andric display: inline; 1630b57cec5SDimitry Andric background-color: #b3e6ff; 1640b57cec5SDimitry Andric text-decoration: none; 1650b57cec5SDimitry Andric } 1660b57cec5SDimitry Andric .tooltip span.tooltip-content { 1670b57cec5SDimitry Andric position: absolute; 1680b57cec5SDimitry Andric width: 100px; 1690b57cec5SDimitry Andric margin-left: -50px; 1700b57cec5SDimitry Andric color: #FFFFFF; 1710b57cec5SDimitry Andric background: #000000; 1720b57cec5SDimitry Andric height: 30px; 1730b57cec5SDimitry Andric line-height: 30px; 1740b57cec5SDimitry Andric text-align: center; 1750b57cec5SDimitry Andric visibility: hidden; 1760b57cec5SDimitry Andric border-radius: 6px; 1770b57cec5SDimitry Andric } 1780b57cec5SDimitry Andric .tooltip span.tooltip-content:after { 1790b57cec5SDimitry Andric content: ''; 1800b57cec5SDimitry Andric position: absolute; 1810b57cec5SDimitry Andric top: 100%; 1820b57cec5SDimitry Andric left: 50%; 1830b57cec5SDimitry Andric margin-left: -8px; 1840b57cec5SDimitry Andric width: 0; height: 0; 1850b57cec5SDimitry Andric border-top: 8px solid #000000; 1860b57cec5SDimitry Andric border-right: 8px solid transparent; 1870b57cec5SDimitry Andric border-left: 8px solid transparent; 1880b57cec5SDimitry Andric } 1890b57cec5SDimitry Andric :hover.tooltip span.tooltip-content { 1900b57cec5SDimitry Andric visibility: visible; 1910b57cec5SDimitry Andric opacity: 0.8; 1920b57cec5SDimitry Andric bottom: 30px; 1930b57cec5SDimitry Andric left: 50%; 1940b57cec5SDimitry Andric z-index: 999; 1950b57cec5SDimitry Andric } 1960b57cec5SDimitry Andric th, td { 1970b57cec5SDimitry Andric vertical-align: top; 1980b57cec5SDimitry Andric padding: 2px 8px; 1990b57cec5SDimitry Andric border-collapse: collapse; 2000b57cec5SDimitry Andric border-right: solid 1px #eee; 2010b57cec5SDimitry Andric border-left: solid 1px #eee; 2020b57cec5SDimitry Andric text-align: left; 2030b57cec5SDimitry Andric } 2040b57cec5SDimitry Andric td pre { 2050b57cec5SDimitry Andric display: inline-block; 2060b57cec5SDimitry Andric } 2070b57cec5SDimitry Andric td:first-child { 2080b57cec5SDimitry Andric border-left: none; 2090b57cec5SDimitry Andric } 2100b57cec5SDimitry Andric td:last-child { 2110b57cec5SDimitry Andric border-right: none; 2120b57cec5SDimitry Andric } 2130b57cec5SDimitry Andric tr:hover { 2140b57cec5SDimitry Andric background-color: #f0f0f0; 2150b57cec5SDimitry Andric } 2160b57cec5SDimitry Andric )"; 2170b57cec5SDimitry Andric 2180b57cec5SDimitry Andric const char *EndHeader = "</head>"; 2190b57cec5SDimitry Andric 2200b57cec5SDimitry Andric const char *BeginCenteredDiv = "<div class='centered'>"; 2210b57cec5SDimitry Andric 2220b57cec5SDimitry Andric const char *EndCenteredDiv = "</div>"; 2230b57cec5SDimitry Andric 2240b57cec5SDimitry Andric const char *BeginSourceNameDiv = "<div class='source-name-title'>"; 2250b57cec5SDimitry Andric 2260b57cec5SDimitry Andric const char *EndSourceNameDiv = "</div>"; 2270b57cec5SDimitry Andric 2280b57cec5SDimitry Andric const char *BeginCodeTD = "<td class='code'>"; 2290b57cec5SDimitry Andric 2300b57cec5SDimitry Andric const char *EndCodeTD = "</td>"; 2310b57cec5SDimitry Andric 2320b57cec5SDimitry Andric const char *BeginPre = "<pre>"; 2330b57cec5SDimitry Andric 2340b57cec5SDimitry Andric const char *EndPre = "</pre>"; 2350b57cec5SDimitry Andric 2360b57cec5SDimitry Andric const char *BeginExpansionDiv = "<div class='expansion-view'>"; 2370b57cec5SDimitry Andric 2380b57cec5SDimitry Andric const char *EndExpansionDiv = "</div>"; 2390b57cec5SDimitry Andric 2400b57cec5SDimitry Andric const char *BeginTable = "<table>"; 2410b57cec5SDimitry Andric 2420b57cec5SDimitry Andric const char *EndTable = "</table>"; 2430b57cec5SDimitry Andric 2440b57cec5SDimitry Andric const char *ProjectTitleTag = "h1"; 2450b57cec5SDimitry Andric 2460b57cec5SDimitry Andric const char *ReportTitleTag = "h2"; 2470b57cec5SDimitry Andric 2480b57cec5SDimitry Andric const char *CreatedTimeTag = "h4"; 2490b57cec5SDimitry Andric 2500b57cec5SDimitry Andric std::string getPathToStyle(StringRef ViewPath) { 251e8d8bef9SDimitry Andric std::string PathToStyle; 2525ffd83dbSDimitry Andric std::string PathSep = std::string(sys::path::get_separator()); 2530b57cec5SDimitry Andric unsigned NumSeps = ViewPath.count(PathSep); 2540b57cec5SDimitry Andric for (unsigned I = 0, E = NumSeps; I < E; ++I) 2550b57cec5SDimitry Andric PathToStyle += ".." + PathSep; 2560b57cec5SDimitry Andric return PathToStyle + "style.css"; 2570b57cec5SDimitry Andric } 2580b57cec5SDimitry Andric 2590b57cec5SDimitry Andric void emitPrelude(raw_ostream &OS, const CoverageViewOptions &Opts, 2600b57cec5SDimitry Andric const std::string &PathToStyle = "") { 2610b57cec5SDimitry Andric OS << "<!doctype html>" 2620b57cec5SDimitry Andric "<html>" 2630b57cec5SDimitry Andric << BeginHeader; 2640b57cec5SDimitry Andric 2650b57cec5SDimitry Andric // Link to a stylesheet if one is available. Otherwise, use the default style. 2660b57cec5SDimitry Andric if (PathToStyle.empty()) 2670b57cec5SDimitry Andric OS << "<style>" << CSSForCoverage << "</style>"; 2680b57cec5SDimitry Andric else 2690b57cec5SDimitry Andric OS << "<link rel='stylesheet' type='text/css' href='" 2700b57cec5SDimitry Andric << escape(PathToStyle, Opts) << "'>"; 2710b57cec5SDimitry Andric 2720b57cec5SDimitry Andric OS << EndHeader << "<body>"; 2730b57cec5SDimitry Andric } 2740b57cec5SDimitry Andric 2750b57cec5SDimitry Andric void emitEpilog(raw_ostream &OS) { 2760b57cec5SDimitry Andric OS << "</body>" 2770b57cec5SDimitry Andric << "</html>"; 2780b57cec5SDimitry Andric } 2790b57cec5SDimitry Andric 2800b57cec5SDimitry Andric } // anonymous namespace 2810b57cec5SDimitry Andric 2820b57cec5SDimitry Andric Expected<CoveragePrinter::OwnedStream> 2830b57cec5SDimitry Andric CoveragePrinterHTML::createViewFile(StringRef Path, bool InToplevel) { 2840b57cec5SDimitry Andric auto OSOrErr = createOutputStream(Path, "html", InToplevel); 2850b57cec5SDimitry Andric if (!OSOrErr) 2860b57cec5SDimitry Andric return OSOrErr; 2870b57cec5SDimitry Andric 2880b57cec5SDimitry Andric OwnedStream OS = std::move(OSOrErr.get()); 2890b57cec5SDimitry Andric 2900b57cec5SDimitry Andric if (!Opts.hasOutputDirectory()) { 2910b57cec5SDimitry Andric emitPrelude(*OS.get(), Opts); 2920b57cec5SDimitry Andric } else { 2930b57cec5SDimitry Andric std::string ViewPath = getOutputPath(Path, "html", InToplevel); 2940b57cec5SDimitry Andric emitPrelude(*OS.get(), Opts, getPathToStyle(ViewPath)); 2950b57cec5SDimitry Andric } 2960b57cec5SDimitry Andric 2970b57cec5SDimitry Andric return std::move(OS); 2980b57cec5SDimitry Andric } 2990b57cec5SDimitry Andric 3000b57cec5SDimitry Andric void CoveragePrinterHTML::closeViewFile(OwnedStream OS) { 3010b57cec5SDimitry Andric emitEpilog(*OS.get()); 3020b57cec5SDimitry Andric } 3030b57cec5SDimitry Andric 3040b57cec5SDimitry Andric /// Emit column labels for the table in the index. 3050b57cec5SDimitry Andric static void emitColumnLabelsForIndex(raw_ostream &OS, 3060b57cec5SDimitry Andric const CoverageViewOptions &Opts) { 3070b57cec5SDimitry Andric SmallVector<std::string, 4> Columns; 3080b57cec5SDimitry Andric Columns.emplace_back(tag("td", "Filename", "column-entry-bold")); 3090b57cec5SDimitry Andric Columns.emplace_back(tag("td", "Function Coverage", "column-entry-bold")); 3100b57cec5SDimitry Andric if (Opts.ShowInstantiationSummary) 3110b57cec5SDimitry Andric Columns.emplace_back( 3120b57cec5SDimitry Andric tag("td", "Instantiation Coverage", "column-entry-bold")); 3130b57cec5SDimitry Andric Columns.emplace_back(tag("td", "Line Coverage", "column-entry-bold")); 3140b57cec5SDimitry Andric if (Opts.ShowRegionSummary) 3150b57cec5SDimitry Andric Columns.emplace_back(tag("td", "Region Coverage", "column-entry-bold")); 316e8d8bef9SDimitry Andric if (Opts.ShowBranchSummary) 317e8d8bef9SDimitry Andric Columns.emplace_back(tag("td", "Branch Coverage", "column-entry-bold")); 3180b57cec5SDimitry Andric OS << tag("tr", join(Columns.begin(), Columns.end(), "")); 3190b57cec5SDimitry Andric } 3200b57cec5SDimitry Andric 3210b57cec5SDimitry Andric std::string 3220b57cec5SDimitry Andric CoveragePrinterHTML::buildLinkToFile(StringRef SF, 3230b57cec5SDimitry Andric const FileCoverageSummary &FCS) const { 3240b57cec5SDimitry Andric SmallString<128> LinkTextStr(sys::path::relative_path(FCS.Name)); 32504eeddc0SDimitry Andric sys::path::remove_dots(LinkTextStr, /*remove_dot_dot=*/true); 3260b57cec5SDimitry Andric sys::path::native(LinkTextStr); 3270b57cec5SDimitry Andric std::string LinkText = escape(LinkTextStr, Opts); 3280b57cec5SDimitry Andric std::string LinkTarget = 3290b57cec5SDimitry Andric escape(getOutputPath(SF, "html", /*InToplevel=*/false), Opts); 3300b57cec5SDimitry Andric return a(LinkTarget, LinkText); 3310b57cec5SDimitry Andric } 3320b57cec5SDimitry Andric 3330b57cec5SDimitry Andric /// Render a file coverage summary (\p FCS) in a table row. If \p IsTotals is 3340b57cec5SDimitry Andric /// false, link the summary to \p SF. 3350b57cec5SDimitry Andric void CoveragePrinterHTML::emitFileSummary(raw_ostream &OS, StringRef SF, 3360b57cec5SDimitry Andric const FileCoverageSummary &FCS, 3370b57cec5SDimitry Andric bool IsTotals) const { 3380b57cec5SDimitry Andric SmallVector<std::string, 8> Columns; 3390b57cec5SDimitry Andric 3400b57cec5SDimitry Andric // Format a coverage triple and add the result to the list of columns. 341*81ad6265SDimitry Andric auto AddCoverageTripleToColumn = 342*81ad6265SDimitry Andric [&Columns, this](unsigned Hit, unsigned Total, float Pctg) { 3430b57cec5SDimitry Andric std::string S; 3440b57cec5SDimitry Andric { 3450b57cec5SDimitry Andric raw_string_ostream RSO{S}; 3460b57cec5SDimitry Andric if (Total) 3470b57cec5SDimitry Andric RSO << format("%*.2f", 7, Pctg) << "% "; 3480b57cec5SDimitry Andric else 3490b57cec5SDimitry Andric RSO << "- "; 3500b57cec5SDimitry Andric RSO << '(' << Hit << '/' << Total << ')'; 3510b57cec5SDimitry Andric } 3520b57cec5SDimitry Andric const char *CellClass = "column-entry-yellow"; 353*81ad6265SDimitry Andric if (Pctg >= Opts.HighCovWatermark) 3540b57cec5SDimitry Andric CellClass = "column-entry-green"; 355*81ad6265SDimitry Andric else if (Pctg < Opts.LowCovWatermark) 3560b57cec5SDimitry Andric CellClass = "column-entry-red"; 3570b57cec5SDimitry Andric Columns.emplace_back(tag("td", tag("pre", S), CellClass)); 3580b57cec5SDimitry Andric }; 3590b57cec5SDimitry Andric 3600b57cec5SDimitry Andric // Simplify the display file path, and wrap it in a link if requested. 3610b57cec5SDimitry Andric std::string Filename; 3620b57cec5SDimitry Andric if (IsTotals) { 3635ffd83dbSDimitry Andric Filename = std::string(SF); 3640b57cec5SDimitry Andric } else { 3650b57cec5SDimitry Andric Filename = buildLinkToFile(SF, FCS); 3660b57cec5SDimitry Andric } 3670b57cec5SDimitry Andric 3680b57cec5SDimitry Andric Columns.emplace_back(tag("td", tag("pre", Filename))); 3690b57cec5SDimitry Andric AddCoverageTripleToColumn(FCS.FunctionCoverage.getExecuted(), 3700b57cec5SDimitry Andric FCS.FunctionCoverage.getNumFunctions(), 3710b57cec5SDimitry Andric FCS.FunctionCoverage.getPercentCovered()); 3720b57cec5SDimitry Andric if (Opts.ShowInstantiationSummary) 3730b57cec5SDimitry Andric AddCoverageTripleToColumn(FCS.InstantiationCoverage.getExecuted(), 3740b57cec5SDimitry Andric FCS.InstantiationCoverage.getNumFunctions(), 3750b57cec5SDimitry Andric FCS.InstantiationCoverage.getPercentCovered()); 3760b57cec5SDimitry Andric AddCoverageTripleToColumn(FCS.LineCoverage.getCovered(), 3770b57cec5SDimitry Andric FCS.LineCoverage.getNumLines(), 3780b57cec5SDimitry Andric FCS.LineCoverage.getPercentCovered()); 3790b57cec5SDimitry Andric if (Opts.ShowRegionSummary) 3800b57cec5SDimitry Andric AddCoverageTripleToColumn(FCS.RegionCoverage.getCovered(), 3810b57cec5SDimitry Andric FCS.RegionCoverage.getNumRegions(), 3820b57cec5SDimitry Andric FCS.RegionCoverage.getPercentCovered()); 383e8d8bef9SDimitry Andric if (Opts.ShowBranchSummary) 384e8d8bef9SDimitry Andric AddCoverageTripleToColumn(FCS.BranchCoverage.getCovered(), 385e8d8bef9SDimitry Andric FCS.BranchCoverage.getNumBranches(), 386e8d8bef9SDimitry Andric FCS.BranchCoverage.getPercentCovered()); 3870b57cec5SDimitry Andric 3880b57cec5SDimitry Andric if (IsTotals) 3890b57cec5SDimitry Andric OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row-bold"); 3900b57cec5SDimitry Andric else 3910b57cec5SDimitry Andric OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row"); 3920b57cec5SDimitry Andric } 3930b57cec5SDimitry Andric 3940b57cec5SDimitry Andric Error CoveragePrinterHTML::createIndexFile( 3950b57cec5SDimitry Andric ArrayRef<std::string> SourceFiles, const CoverageMapping &Coverage, 3960b57cec5SDimitry Andric const CoverageFiltersMatchAll &Filters) { 3970b57cec5SDimitry Andric // Emit the default stylesheet. 3980b57cec5SDimitry Andric auto CSSOrErr = createOutputStream("style", "css", /*InToplevel=*/true); 3990b57cec5SDimitry Andric if (Error E = CSSOrErr.takeError()) 4000b57cec5SDimitry Andric return E; 4010b57cec5SDimitry Andric 4020b57cec5SDimitry Andric OwnedStream CSS = std::move(CSSOrErr.get()); 4030b57cec5SDimitry Andric CSS->operator<<(CSSForCoverage); 4040b57cec5SDimitry Andric 4050b57cec5SDimitry Andric // Emit a file index along with some coverage statistics. 4060b57cec5SDimitry Andric auto OSOrErr = createOutputStream("index", "html", /*InToplevel=*/true); 4070b57cec5SDimitry Andric if (Error E = OSOrErr.takeError()) 4080b57cec5SDimitry Andric return E; 4090b57cec5SDimitry Andric auto OS = std::move(OSOrErr.get()); 4100b57cec5SDimitry Andric raw_ostream &OSRef = *OS.get(); 4110b57cec5SDimitry Andric 4120b57cec5SDimitry Andric assert(Opts.hasOutputDirectory() && "No output directory for index file"); 4130b57cec5SDimitry Andric emitPrelude(OSRef, Opts, getPathToStyle("")); 4140b57cec5SDimitry Andric 4150b57cec5SDimitry Andric // Emit some basic information about the coverage report. 4160b57cec5SDimitry Andric if (Opts.hasProjectTitle()) 4170b57cec5SDimitry Andric OSRef << tag(ProjectTitleTag, escape(Opts.ProjectTitle, Opts)); 4180b57cec5SDimitry Andric OSRef << tag(ReportTitleTag, "Coverage Report"); 4190b57cec5SDimitry Andric if (Opts.hasCreatedTime()) 4200b57cec5SDimitry Andric OSRef << tag(CreatedTimeTag, escape(Opts.CreatedTimeStr, Opts)); 4210b57cec5SDimitry Andric 4220b57cec5SDimitry Andric // Emit a link to some documentation. 4230b57cec5SDimitry Andric OSRef << tag("p", "Click " + 4240b57cec5SDimitry Andric a("http://clang.llvm.org/docs/" 4250b57cec5SDimitry Andric "SourceBasedCodeCoverage.html#interpreting-reports", 4260b57cec5SDimitry Andric "here") + 4270b57cec5SDimitry Andric " for information about interpreting this report."); 4280b57cec5SDimitry Andric 4290b57cec5SDimitry Andric // Emit a table containing links to reports for each file in the covmapping. 4300b57cec5SDimitry Andric // Exclude files which don't contain any regions. 4310b57cec5SDimitry Andric OSRef << BeginCenteredDiv << BeginTable; 4320b57cec5SDimitry Andric emitColumnLabelsForIndex(OSRef, Opts); 4330b57cec5SDimitry Andric FileCoverageSummary Totals("TOTALS"); 4340b57cec5SDimitry Andric auto FileReports = CoverageReport::prepareFileReports( 4350b57cec5SDimitry Andric Coverage, Totals, SourceFiles, Opts, Filters); 4360b57cec5SDimitry Andric bool EmptyFiles = false; 4370b57cec5SDimitry Andric for (unsigned I = 0, E = FileReports.size(); I < E; ++I) { 4380b57cec5SDimitry Andric if (FileReports[I].FunctionCoverage.getNumFunctions()) 4390b57cec5SDimitry Andric emitFileSummary(OSRef, SourceFiles[I], FileReports[I]); 4400b57cec5SDimitry Andric else 4410b57cec5SDimitry Andric EmptyFiles = true; 4420b57cec5SDimitry Andric } 4430b57cec5SDimitry Andric emitFileSummary(OSRef, "Totals", Totals, /*IsTotals=*/true); 4440b57cec5SDimitry Andric OSRef << EndTable << EndCenteredDiv; 4450b57cec5SDimitry Andric 4460b57cec5SDimitry Andric // Emit links to files which don't contain any functions. These are normally 4470b57cec5SDimitry Andric // not very useful, but could be relevant for code which abuses the 4480b57cec5SDimitry Andric // preprocessor. 4490b57cec5SDimitry Andric if (EmptyFiles && Filters.empty()) { 4500b57cec5SDimitry Andric OSRef << tag("p", "Files which contain no functions. (These " 4510b57cec5SDimitry Andric "files contain code pulled into other files " 4520b57cec5SDimitry Andric "by the preprocessor.)\n"); 4530b57cec5SDimitry Andric OSRef << BeginCenteredDiv << BeginTable; 4540b57cec5SDimitry Andric for (unsigned I = 0, E = FileReports.size(); I < E; ++I) 4550b57cec5SDimitry Andric if (!FileReports[I].FunctionCoverage.getNumFunctions()) { 4560b57cec5SDimitry Andric std::string Link = buildLinkToFile(SourceFiles[I], FileReports[I]); 4570b57cec5SDimitry Andric OSRef << tag("tr", tag("td", tag("pre", Link)), "light-row") << '\n'; 4580b57cec5SDimitry Andric } 4590b57cec5SDimitry Andric OSRef << EndTable << EndCenteredDiv; 4600b57cec5SDimitry Andric } 4610b57cec5SDimitry Andric 4620b57cec5SDimitry Andric OSRef << tag("h5", escape(Opts.getLLVMVersionString(), Opts)); 4630b57cec5SDimitry Andric emitEpilog(OSRef); 4640b57cec5SDimitry Andric 4650b57cec5SDimitry Andric return Error::success(); 4660b57cec5SDimitry Andric } 4670b57cec5SDimitry Andric 4680b57cec5SDimitry Andric void SourceCoverageViewHTML::renderViewHeader(raw_ostream &OS) { 4690b57cec5SDimitry Andric OS << BeginCenteredDiv << BeginTable; 4700b57cec5SDimitry Andric } 4710b57cec5SDimitry Andric 4720b57cec5SDimitry Andric void SourceCoverageViewHTML::renderViewFooter(raw_ostream &OS) { 4730b57cec5SDimitry Andric OS << EndTable << EndCenteredDiv; 4740b57cec5SDimitry Andric } 4750b57cec5SDimitry Andric 4760b57cec5SDimitry Andric void SourceCoverageViewHTML::renderSourceName(raw_ostream &OS, bool WholeFile) { 4770b57cec5SDimitry Andric OS << BeginSourceNameDiv << tag("pre", escape(getSourceName(), getOptions())) 4780b57cec5SDimitry Andric << EndSourceNameDiv; 4790b57cec5SDimitry Andric } 4800b57cec5SDimitry Andric 4810b57cec5SDimitry Andric void SourceCoverageViewHTML::renderLinePrefix(raw_ostream &OS, unsigned) { 4820b57cec5SDimitry Andric OS << "<tr>"; 4830b57cec5SDimitry Andric } 4840b57cec5SDimitry Andric 4850b57cec5SDimitry Andric void SourceCoverageViewHTML::renderLineSuffix(raw_ostream &OS, unsigned) { 4860b57cec5SDimitry Andric // If this view has sub-views, renderLine() cannot close the view's cell. 4870b57cec5SDimitry Andric // Take care of it here, after all sub-views have been rendered. 4880b57cec5SDimitry Andric if (hasSubViews()) 4890b57cec5SDimitry Andric OS << EndCodeTD; 4900b57cec5SDimitry Andric OS << "</tr>"; 4910b57cec5SDimitry Andric } 4920b57cec5SDimitry Andric 4930b57cec5SDimitry Andric void SourceCoverageViewHTML::renderViewDivider(raw_ostream &, unsigned) { 4940b57cec5SDimitry Andric // The table-based output makes view dividers unnecessary. 4950b57cec5SDimitry Andric } 4960b57cec5SDimitry Andric 4970b57cec5SDimitry Andric void SourceCoverageViewHTML::renderLine(raw_ostream &OS, LineRef L, 4980b57cec5SDimitry Andric const LineCoverageStats &LCS, 4990b57cec5SDimitry Andric unsigned ExpansionCol, unsigned) { 5000b57cec5SDimitry Andric StringRef Line = L.Line; 5010b57cec5SDimitry Andric unsigned LineNo = L.LineNo; 5020b57cec5SDimitry Andric 5030b57cec5SDimitry Andric // Steps for handling text-escaping, highlighting, and tooltip creation: 5040b57cec5SDimitry Andric // 5050b57cec5SDimitry Andric // 1. Split the line into N+1 snippets, where N = |Segments|. The first 5060b57cec5SDimitry Andric // snippet starts from Col=1 and ends at the start of the first segment. 5070b57cec5SDimitry Andric // The last snippet starts at the last mapped column in the line and ends 5080b57cec5SDimitry Andric // at the end of the line. Both are required but may be empty. 5090b57cec5SDimitry Andric 5100b57cec5SDimitry Andric SmallVector<std::string, 8> Snippets; 5110b57cec5SDimitry Andric CoverageSegmentArray Segments = LCS.getLineSegments(); 5120b57cec5SDimitry Andric 5130b57cec5SDimitry Andric unsigned LCol = 1; 5140b57cec5SDimitry Andric auto Snip = [&](unsigned Start, unsigned Len) { 5155ffd83dbSDimitry Andric Snippets.push_back(std::string(Line.substr(Start, Len))); 5160b57cec5SDimitry Andric LCol += Len; 5170b57cec5SDimitry Andric }; 5180b57cec5SDimitry Andric 5190b57cec5SDimitry Andric Snip(LCol - 1, Segments.empty() ? 0 : (Segments.front()->Col - 1)); 5200b57cec5SDimitry Andric 5210b57cec5SDimitry Andric for (unsigned I = 1, E = Segments.size(); I < E; ++I) 5220b57cec5SDimitry Andric Snip(LCol - 1, Segments[I]->Col - LCol); 5230b57cec5SDimitry Andric 5240b57cec5SDimitry Andric // |Line| + 1 is needed to avoid underflow when, e.g |Line| = 0 and LCol = 1. 5250b57cec5SDimitry Andric Snip(LCol - 1, Line.size() + 1 - LCol); 5260b57cec5SDimitry Andric 5270b57cec5SDimitry Andric // 2. Escape all of the snippets. 5280b57cec5SDimitry Andric 5290b57cec5SDimitry Andric for (unsigned I = 0, E = Snippets.size(); I < E; ++I) 5300b57cec5SDimitry Andric Snippets[I] = escape(Snippets[I], getOptions()); 5310b57cec5SDimitry Andric 5320b57cec5SDimitry Andric // 3. Use \p WrappedSegment to set the highlight for snippet 0. Use segment 5330b57cec5SDimitry Andric // 1 to set the highlight for snippet 2, segment 2 to set the highlight for 5340b57cec5SDimitry Andric // snippet 3, and so on. 5350b57cec5SDimitry Andric 5360b57cec5SDimitry Andric Optional<StringRef> Color; 5370b57cec5SDimitry Andric SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges; 5380b57cec5SDimitry Andric auto Highlight = [&](const std::string &Snippet, unsigned LC, unsigned RC) { 5390b57cec5SDimitry Andric if (getOptions().Debug) 5400b57cec5SDimitry Andric HighlightedRanges.emplace_back(LC, RC); 541*81ad6265SDimitry Andric return tag("span", Snippet, std::string(*Color)); 5420b57cec5SDimitry Andric }; 5430b57cec5SDimitry Andric 5440b57cec5SDimitry Andric auto CheckIfUncovered = [&](const CoverageSegment *S) { 5450b57cec5SDimitry Andric return S && (!S->IsGapRegion || (Color && *Color == "red")) && 5460b57cec5SDimitry Andric S->HasCount && S->Count == 0; 5470b57cec5SDimitry Andric }; 5480b57cec5SDimitry Andric 5490b57cec5SDimitry Andric if (CheckIfUncovered(LCS.getWrappedSegment())) { 5500b57cec5SDimitry Andric Color = "red"; 5510b57cec5SDimitry Andric if (!Snippets[0].empty()) 5520b57cec5SDimitry Andric Snippets[0] = Highlight(Snippets[0], 1, 1 + Snippets[0].size()); 5530b57cec5SDimitry Andric } 5540b57cec5SDimitry Andric 5550b57cec5SDimitry Andric for (unsigned I = 0, E = Segments.size(); I < E; ++I) { 5560b57cec5SDimitry Andric const auto *CurSeg = Segments[I]; 5570b57cec5SDimitry Andric if (CheckIfUncovered(CurSeg)) 5580b57cec5SDimitry Andric Color = "red"; 5590b57cec5SDimitry Andric else if (CurSeg->Col == ExpansionCol) 5600b57cec5SDimitry Andric Color = "cyan"; 5610b57cec5SDimitry Andric else 5620b57cec5SDimitry Andric Color = None; 5630b57cec5SDimitry Andric 564*81ad6265SDimitry Andric if (Color) 5650b57cec5SDimitry Andric Snippets[I + 1] = Highlight(Snippets[I + 1], CurSeg->Col, 5660b57cec5SDimitry Andric CurSeg->Col + Snippets[I + 1].size()); 5670b57cec5SDimitry Andric } 5680b57cec5SDimitry Andric 569*81ad6265SDimitry Andric if (Color && Segments.empty()) 5700b57cec5SDimitry Andric Snippets.back() = Highlight(Snippets.back(), 1, 1 + Snippets.back().size()); 5710b57cec5SDimitry Andric 5720b57cec5SDimitry Andric if (getOptions().Debug) { 5730b57cec5SDimitry Andric for (const auto &Range : HighlightedRanges) { 5740b57cec5SDimitry Andric errs() << "Highlighted line " << LineNo << ", " << Range.first << " -> "; 5750b57cec5SDimitry Andric if (Range.second == 0) 5760b57cec5SDimitry Andric errs() << "?"; 5770b57cec5SDimitry Andric else 5780b57cec5SDimitry Andric errs() << Range.second; 5790b57cec5SDimitry Andric errs() << "\n"; 5800b57cec5SDimitry Andric } 5810b57cec5SDimitry Andric } 5820b57cec5SDimitry Andric 5830b57cec5SDimitry Andric // 4. Snippets[1:N+1] correspond to \p Segments[0:N]: use these to generate 5840b57cec5SDimitry Andric // sub-line region count tooltips if needed. 5850b57cec5SDimitry Andric 5860b57cec5SDimitry Andric if (shouldRenderRegionMarkers(LCS)) { 5870b57cec5SDimitry Andric // Just consider the segments which start *and* end on this line. 5880b57cec5SDimitry Andric for (unsigned I = 0, E = Segments.size() - 1; I < E; ++I) { 5890b57cec5SDimitry Andric const auto *CurSeg = Segments[I]; 5900b57cec5SDimitry Andric if (!CurSeg->IsRegionEntry) 5910b57cec5SDimitry Andric continue; 5920b57cec5SDimitry Andric if (CurSeg->Count == LCS.getExecutionCount()) 5930b57cec5SDimitry Andric continue; 5940b57cec5SDimitry Andric 5950b57cec5SDimitry Andric Snippets[I + 1] = 5960b57cec5SDimitry Andric tag("div", Snippets[I + 1] + tag("span", formatCount(CurSeg->Count), 5970b57cec5SDimitry Andric "tooltip-content"), 5980b57cec5SDimitry Andric "tooltip"); 5990b57cec5SDimitry Andric 6000b57cec5SDimitry Andric if (getOptions().Debug) 6010b57cec5SDimitry Andric errs() << "Marker at " << CurSeg->Line << ":" << CurSeg->Col << " = " 6020b57cec5SDimitry Andric << formatCount(CurSeg->Count) << "\n"; 6030b57cec5SDimitry Andric } 6040b57cec5SDimitry Andric } 6050b57cec5SDimitry Andric 6060b57cec5SDimitry Andric OS << BeginCodeTD; 6070b57cec5SDimitry Andric OS << BeginPre; 6080b57cec5SDimitry Andric for (const auto &Snippet : Snippets) 6090b57cec5SDimitry Andric OS << Snippet; 6100b57cec5SDimitry Andric OS << EndPre; 6110b57cec5SDimitry Andric 6120b57cec5SDimitry Andric // If there are no sub-views left to attach to this cell, end the cell. 6130b57cec5SDimitry Andric // Otherwise, end it after the sub-views are rendered (renderLineSuffix()). 6140b57cec5SDimitry Andric if (!hasSubViews()) 6150b57cec5SDimitry Andric OS << EndCodeTD; 6160b57cec5SDimitry Andric } 6170b57cec5SDimitry Andric 6180b57cec5SDimitry Andric void SourceCoverageViewHTML::renderLineCoverageColumn( 6190b57cec5SDimitry Andric raw_ostream &OS, const LineCoverageStats &Line) { 620e8d8bef9SDimitry Andric std::string Count; 6210b57cec5SDimitry Andric if (Line.isMapped()) 6220b57cec5SDimitry Andric Count = tag("pre", formatCount(Line.getExecutionCount())); 6230b57cec5SDimitry Andric std::string CoverageClass = 6240b57cec5SDimitry Andric (Line.getExecutionCount() > 0) ? "covered-line" : "uncovered-line"; 6250b57cec5SDimitry Andric OS << tag("td", Count, CoverageClass); 6260b57cec5SDimitry Andric } 6270b57cec5SDimitry Andric 6280b57cec5SDimitry Andric void SourceCoverageViewHTML::renderLineNumberColumn(raw_ostream &OS, 6290b57cec5SDimitry Andric unsigned LineNo) { 6300b57cec5SDimitry Andric std::string LineNoStr = utostr(uint64_t(LineNo)); 6310b57cec5SDimitry Andric std::string TargetName = "L" + LineNoStr; 6320b57cec5SDimitry Andric OS << tag("td", a("#" + TargetName, tag("pre", LineNoStr), TargetName), 6330b57cec5SDimitry Andric "line-number"); 6340b57cec5SDimitry Andric } 6350b57cec5SDimitry Andric 6360b57cec5SDimitry Andric void SourceCoverageViewHTML::renderRegionMarkers(raw_ostream &, 6370b57cec5SDimitry Andric const LineCoverageStats &Line, 6380b57cec5SDimitry Andric unsigned) { 6390b57cec5SDimitry Andric // Region markers are rendered in-line using tooltips. 6400b57cec5SDimitry Andric } 6410b57cec5SDimitry Andric 6420b57cec5SDimitry Andric void SourceCoverageViewHTML::renderExpansionSite(raw_ostream &OS, LineRef L, 6430b57cec5SDimitry Andric const LineCoverageStats &LCS, 6440b57cec5SDimitry Andric unsigned ExpansionCol, 6450b57cec5SDimitry Andric unsigned ViewDepth) { 6460b57cec5SDimitry Andric // Render the line containing the expansion site. No extra formatting needed. 6470b57cec5SDimitry Andric renderLine(OS, L, LCS, ExpansionCol, ViewDepth); 6480b57cec5SDimitry Andric } 6490b57cec5SDimitry Andric 6500b57cec5SDimitry Andric void SourceCoverageViewHTML::renderExpansionView(raw_ostream &OS, 6510b57cec5SDimitry Andric ExpansionView &ESV, 6520b57cec5SDimitry Andric unsigned ViewDepth) { 6530b57cec5SDimitry Andric OS << BeginExpansionDiv; 6540b57cec5SDimitry Andric ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false, 6550b57cec5SDimitry Andric /*ShowTitle=*/false, ViewDepth + 1); 6560b57cec5SDimitry Andric OS << EndExpansionDiv; 6570b57cec5SDimitry Andric } 6580b57cec5SDimitry Andric 659e8d8bef9SDimitry Andric void SourceCoverageViewHTML::renderBranchView(raw_ostream &OS, BranchView &BRV, 660e8d8bef9SDimitry Andric unsigned ViewDepth) { 661e8d8bef9SDimitry Andric // Render the child subview. 662e8d8bef9SDimitry Andric if (getOptions().Debug) 663e8d8bef9SDimitry Andric errs() << "Branch at line " << BRV.getLine() << '\n'; 664e8d8bef9SDimitry Andric 665e8d8bef9SDimitry Andric OS << BeginExpansionDiv; 666e8d8bef9SDimitry Andric OS << BeginPre; 667e8d8bef9SDimitry Andric for (const auto &R : BRV.Regions) { 668e8d8bef9SDimitry Andric // Calculate TruePercent and False Percent. 669e8d8bef9SDimitry Andric double TruePercent = 0.0; 670e8d8bef9SDimitry Andric double FalsePercent = 0.0; 671e8d8bef9SDimitry Andric unsigned Total = R.ExecutionCount + R.FalseExecutionCount; 672e8d8bef9SDimitry Andric 673e8d8bef9SDimitry Andric if (!getOptions().ShowBranchCounts && Total != 0) { 674e8d8bef9SDimitry Andric TruePercent = ((double)(R.ExecutionCount) / (double)Total) * 100.0; 675e8d8bef9SDimitry Andric FalsePercent = ((double)(R.FalseExecutionCount) / (double)Total) * 100.0; 676e8d8bef9SDimitry Andric } 677e8d8bef9SDimitry Andric 678e8d8bef9SDimitry Andric // Display Line + Column. 679e8d8bef9SDimitry Andric std::string LineNoStr = utostr(uint64_t(R.LineStart)); 680e8d8bef9SDimitry Andric std::string ColNoStr = utostr(uint64_t(R.ColumnStart)); 681e8d8bef9SDimitry Andric std::string TargetName = "L" + LineNoStr; 682e8d8bef9SDimitry Andric 683e8d8bef9SDimitry Andric OS << " Branch ("; 684e8d8bef9SDimitry Andric OS << tag("span", 685e8d8bef9SDimitry Andric a("#" + TargetName, tag("span", LineNoStr + ":" + ColNoStr), 686e8d8bef9SDimitry Andric TargetName), 687e8d8bef9SDimitry Andric "line-number") + 688e8d8bef9SDimitry Andric "): ["; 689e8d8bef9SDimitry Andric 690e8d8bef9SDimitry Andric if (R.Folded) { 691e8d8bef9SDimitry Andric OS << "Folded - Ignored]\n"; 692e8d8bef9SDimitry Andric continue; 693e8d8bef9SDimitry Andric } 694e8d8bef9SDimitry Andric 695e8d8bef9SDimitry Andric // Display TrueCount or TruePercent. 696e8d8bef9SDimitry Andric std::string TrueColor = R.ExecutionCount ? "None" : "red"; 697e8d8bef9SDimitry Andric std::string TrueCovClass = 698e8d8bef9SDimitry Andric (R.ExecutionCount > 0) ? "covered-line" : "uncovered-line"; 699e8d8bef9SDimitry Andric 700e8d8bef9SDimitry Andric OS << tag("span", "True", TrueColor); 701e8d8bef9SDimitry Andric OS << ": "; 702e8d8bef9SDimitry Andric if (getOptions().ShowBranchCounts) 703e8d8bef9SDimitry Andric OS << tag("span", formatCount(R.ExecutionCount), TrueCovClass) << ", "; 704e8d8bef9SDimitry Andric else 705e8d8bef9SDimitry Andric OS << format("%0.2f", TruePercent) << "%, "; 706e8d8bef9SDimitry Andric 707e8d8bef9SDimitry Andric // Display FalseCount or FalsePercent. 708e8d8bef9SDimitry Andric std::string FalseColor = R.FalseExecutionCount ? "None" : "red"; 709e8d8bef9SDimitry Andric std::string FalseCovClass = 710e8d8bef9SDimitry Andric (R.FalseExecutionCount > 0) ? "covered-line" : "uncovered-line"; 711e8d8bef9SDimitry Andric 712e8d8bef9SDimitry Andric OS << tag("span", "False", FalseColor); 713e8d8bef9SDimitry Andric OS << ": "; 714e8d8bef9SDimitry Andric if (getOptions().ShowBranchCounts) 715e8d8bef9SDimitry Andric OS << tag("span", formatCount(R.FalseExecutionCount), FalseCovClass); 716e8d8bef9SDimitry Andric else 717e8d8bef9SDimitry Andric OS << format("%0.2f", FalsePercent) << "%"; 718e8d8bef9SDimitry Andric 719e8d8bef9SDimitry Andric OS << "]\n"; 720e8d8bef9SDimitry Andric } 721e8d8bef9SDimitry Andric OS << EndPre; 722e8d8bef9SDimitry Andric OS << EndExpansionDiv; 723e8d8bef9SDimitry Andric } 724e8d8bef9SDimitry Andric 7250b57cec5SDimitry Andric void SourceCoverageViewHTML::renderInstantiationView(raw_ostream &OS, 7260b57cec5SDimitry Andric InstantiationView &ISV, 7270b57cec5SDimitry Andric unsigned ViewDepth) { 7280b57cec5SDimitry Andric OS << BeginExpansionDiv; 7290b57cec5SDimitry Andric if (!ISV.View) 7300b57cec5SDimitry Andric OS << BeginSourceNameDiv 7310b57cec5SDimitry Andric << tag("pre", 7320b57cec5SDimitry Andric escape("Unexecuted instantiation: " + ISV.FunctionName.str(), 7330b57cec5SDimitry Andric getOptions())) 7340b57cec5SDimitry Andric << EndSourceNameDiv; 7350b57cec5SDimitry Andric else 7360b57cec5SDimitry Andric ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true, 7370b57cec5SDimitry Andric /*ShowTitle=*/false, ViewDepth); 7380b57cec5SDimitry Andric OS << EndExpansionDiv; 7390b57cec5SDimitry Andric } 7400b57cec5SDimitry Andric 7410b57cec5SDimitry Andric void SourceCoverageViewHTML::renderTitle(raw_ostream &OS, StringRef Title) { 7420b57cec5SDimitry Andric if (getOptions().hasProjectTitle()) 7430b57cec5SDimitry Andric OS << tag(ProjectTitleTag, escape(getOptions().ProjectTitle, getOptions())); 7440b57cec5SDimitry Andric OS << tag(ReportTitleTag, escape(Title, getOptions())); 7450b57cec5SDimitry Andric if (getOptions().hasCreatedTime()) 7460b57cec5SDimitry Andric OS << tag(CreatedTimeTag, 7470b57cec5SDimitry Andric escape(getOptions().CreatedTimeStr, getOptions())); 7480b57cec5SDimitry Andric } 7490b57cec5SDimitry Andric 7500b57cec5SDimitry Andric void SourceCoverageViewHTML::renderTableHeader(raw_ostream &OS, 7510b57cec5SDimitry Andric unsigned FirstUncoveredLineNo, 7520b57cec5SDimitry Andric unsigned ViewDepth) { 7530b57cec5SDimitry Andric std::string SourceLabel; 7540b57cec5SDimitry Andric if (FirstUncoveredLineNo == 0) { 7550b57cec5SDimitry Andric SourceLabel = tag("td", tag("pre", "Source")); 7560b57cec5SDimitry Andric } else { 7570b57cec5SDimitry Andric std::string LinkTarget = "#L" + utostr(uint64_t(FirstUncoveredLineNo)); 7580b57cec5SDimitry Andric SourceLabel = 7590b57cec5SDimitry Andric tag("td", tag("pre", "Source (" + 7600b57cec5SDimitry Andric a(LinkTarget, "jump to first uncovered line") + 7610b57cec5SDimitry Andric ")")); 7620b57cec5SDimitry Andric } 7630b57cec5SDimitry Andric 7640b57cec5SDimitry Andric renderLinePrefix(OS, ViewDepth); 7650b57cec5SDimitry Andric OS << tag("td", tag("pre", "Line")) << tag("td", tag("pre", "Count")) 7660b57cec5SDimitry Andric << SourceLabel; 7670b57cec5SDimitry Andric renderLineSuffix(OS, ViewDepth); 7680b57cec5SDimitry Andric } 769