llvm.org GIT mirror llvm / 55c8c00
[llvm-cov] Add support for creating html reports Based on a patch by Harlan Haskins! Differential Revision: http://reviews.llvm.org/D18278 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@274688 91177308-0d34-0410-b5e6-96231b3b80d8 Vedant Kumar 3 years ago
9 changed file(s) with 615 addition(s) and 2 deletion(s). Raw diff Collapse all Expand all
237237
238238 .. option:: -format=
239239
240 Use the specified output format. The supported formats are: "text".
240 Use the specified output format. The supported formats are: "text", "html".
241241
242242 .. option:: -output-dir=PATH
243243
3737 // Test index creation.
3838 // RUN: FileCheck -check-prefix=INDEX -input-file %t.dir/index.txt %s
3939 // INDEX: showLineExecutionCounts.cpp.txt
40 //
41 // Test html output.
42 // RUN: llvm-cov show %S/Inputs/lineExecutionCounts.covmapping -format html -o %t.html.dir -instr-profile %t.profdata -filename-equivalence %s
43 // RUN: llvm-cov show %S/Inputs/lineExecutionCounts.covmapping -format html -o %t.html.dir -instr-profile %t.profdata -filename-equivalence -name=main %s
44 // RUN: FileCheck -check-prefixes=HTML,HTML-WHOLE-FILE -input-file %t.html.dir/coverage/tmp/showLineExecutionCounts.cpp.html %s
45 // RUN: FileCheck -check-prefixes=HTML,HTML-FILTER -input-file %t.html.dir/functions.html %s
46 //
47 // HTML-WHOLE-FILE:
[[@LINE-44]]
// before

                  
                
48 // HTML-FILTER-NOT:
[[@LINE-45]]
// before

                  
                
49 // HTML:
161
[[@LINE-44]]
int main() {

                  
                
50 // HTML:
161
[[@LINE-44]]
  int x = 0

                  
                
51 // HTML:
161
[[@LINE-44]]

                  
                
52 // HTML:
0
[[@LINE-44]]
  if (x) {

                  
                
53 // HTML:
0
[[@LINE-44]]

                  
                
54 // HTML:
161
[[@LINE-44]]
  }

                  
                
55 // HTML:
161
[[@LINE-44]]
    x = 1;

                  
                
56 // HTML:
161
[[@LINE-44]]
  }

                  
                
57 // HTML:
161
[[@LINE-44]]

                  
                
58 // HTML:
16.2k
[[@LINE-44]]
  for (int i = 0; i < 100; ++i)

                  
                
59 // HTML:
16.1k
[[@LINE-44]]
    x = 1;

                  
                
60 // HTML:
16.1k
[[@LINE-44]]
  }

                  
                
61 // HTML:
161
[[@LINE-44]]

                  
                
62 // HTML:
161
[[@LINE-44]]
  x = x < 10

                  
                
63 // HTML:
161
[[@LINE-44]]
  x = x > 10

                  
                
64 // HTML:
0
[[@LINE-44]]
        x - 1:

                  
                
65 // HTML:
161
[[@LINE-44]]
        x + 1;

                  
                
66 // HTML:
161
[[@LINE-44]]

                  
                
67 // HTML:
161
[[@LINE-44]]
  return 0;

                  
                
68 // HTML:
161
[[@LINE-44]]
}

                  
                
69 // HTML-WHOLE-FILE:
[[@LINE-44]]
// after

                  
                
70 // HTML-FILTER-NOT:
[[@LINE-45]]
// after

                  
                
3737 } // ALL-NEXT: 1| [[@LINE]]|}
3838 // after coverage // ALL-NEXT: | [[@LINE]]|// after
3939 // FILTER-NOT: | [[@LINE-1]]|// after
40
41 // Test html output.
42 // RUN: llvm-cov show %S/Inputs/templateInstantiations.covmapping -instr-profile %S/Inputs/templateInstantiations.profdata -filename-equivalence %s -format html -o %t.html.dir
43 // RUN: llvm-cov show %S/Inputs/templateInstantiations.covmapping -instr-profile %S/Inputs/templateInstantiations.profdata -filename-equivalence -name=_Z4funcIbEiT_ %s -format html -o %t.html.dir
44 // RUN: FileCheck -check-prefixes=HTML-SHARED,HTML-ALL -input-file=%t.html.dir/coverage/tmp/showTemplateInstantiations.cpp.html %s
45 // RUN: FileCheck -check-prefixes=HTML-SHARED,HTML-FILTER -input-file=%t.html.dir/functions.html %s
46
47 // HTML-ALL:
[[@LINE-44]]
// before

                  
                
48 // HTML-FILTER-NOT:
[[@LINE-45]]
// before

                  
                
49 // HTML-ALL:
[[@LINE-44]]
template<typename T>

                  
                
50 // HTML-ALL:
2
[[@LINE-44]]
int func(T x) {

                  
                
51 // HTML-ALL:
2
[[@LINE-44]]
  if(x)

                  
                
52 // HTML-ALL:
1
[[@LINE-44]]
    ret

                  
                
53 // HTML-ALL:
2
[[@LINE-44]]
  else

                  
                
54 // HTML-ALL:
1
[[@LINE-44]]
    ret

                  
                
55 // HTML-ALL:
0
[[@LINE-44]]

                  
                
56 // HTML-ALL:
2
[[@LINE-44]]
}

                  
                
57
58 // HTML-SHARED:
_Z4funcIbEiT_
59 // HTML-SHARED:
1
[[@LINE-53]]
int func(T x) {

                  
                
60 // HTML-SHARED:
1
[[@LINE-53]]
  if(x)

                  
                
61 // HTML-SHARED:
1
[[@LINE-53]]
    ret

                  
                
62 // HTML-SHARED:
1
[[@LINE-53]]
  else

                  
                
63 // HTML-SHARED:
0
[[@LINE-53]]

                  
                
64 // HTML-SHARED:
0
[[@LINE-53]]

                  
                
65 // HTML-SHARED:
1
[[@LINE-53]]
}

                  
                
66
67 // HTML-ALL:
_Z4funcIiEiT_
68 // HTML-FILTER-NOT:
_Z4funcIiEiT_
69 // HTML-ALL:
1
[[@LINE-63]]
int func(T x) {

                  
                
70 // HTML-ALL:
1
[[@LINE-63]]
  if(x)

                  
                
71 // HTML-ALL:
0
[[@LINE-63]]

                  
                
72 // HTML-ALL:
1
[[@LINE-63]]
  else

                  
                
73 // HTML-ALL:
1
[[@LINE-63]]
    ret

                  
                
74 // HTML-ALL:
0
[[@LINE-63]]

                  
                
75 // HTML-ALL:
1
[[@LINE-63]]
}

                  
                
76
77 // HTML-ALL: td class='covered-line'>
1
[[@LINE-44]]
int main() {

                  
                
78 // HTML-ALL:
1
[[@LINE-44]]
  func<int>(0);

                  
                
79 // HTML-ALL:
1
[[@LINE-44]]
  func<bool>(true);

                  
                
80 // HTML-ALL:
1
[[@LINE-44]]
  return 0;

                  
                
81 // HTML-ALL:
1
[[@LINE-44]]
}

                  
                
82
83 // HTML-ALL:
[[@LINE-45]]
// after

                  
                
84 // HTML-FILTER-NOT:
[[@LINE-46]]
// after

                  
                
77 CoverageReport.cpp
88 CoverageSummaryInfo.cpp
99 SourceCoverageView.cpp
10 SourceCoverageViewHTML.cpp
1011 SourceCoverageViewText.cpp
1112 TestingSupport.cpp
1213 )
271271 "format", cl::desc("Output format for line-based coverage reports"),
272272 cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text",
273273 "Text output"),
274 clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html",
275 "HTML output"),
274276 clEnumValEnd),
275277 cl::init(CoverageViewOptions::OutputFormat::Text));
276278
331333 ViewOpts.Colors = UseColor == cl::BOU_UNSET
332334 ? sys::Process::StandardOutHasColors()
333335 : UseColor == cl::BOU_TRUE;
336 break;
337 case CoverageViewOptions::OutputFormat::HTML:
338 if (UseColor == cl::BOU_FALSE)
339 error("Color output cannot be disabled when generating html.");
340 ViewOpts.Colors = true;
334341 break;
335342 }
336343
526533 if (Err)
527534 return Err;
528535
536 if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML)
537 error("HTML output for summary reports is not yet supported.");
538
529539 auto Coverage = load();
530540 if (!Coverage)
531541 return 1;
1616 /// \brief The options for displaying the code coverage information.
1717 struct CoverageViewOptions {
1818 enum class OutputFormat {
19 Text
19 Text,
20 HTML
2021 };
2122
2223 bool Debug;
1111 //===----------------------------------------------------------------------===//
1212
1313 #include "SourceCoverageView.h"
14 #include "SourceCoverageViewHTML.h"
1415 #include "SourceCoverageViewText.h"
1516 #include "llvm/ADT/SmallString.h"
1617 #include "llvm/ADT/StringExtras.h"
7374 switch (Opts.Format) {
7475 case CoverageViewOptions::OutputFormat::Text:
7576 return llvm::make_unique(Opts);
77 case CoverageViewOptions::OutputFormat::HTML:
78 return llvm::make_unique(Opts);
7679 }
7780 llvm_unreachable("Unknown coverage output format!");
7881 }
109112 switch (Options.Format) {
110113 case CoverageViewOptions::OutputFormat::Text:
111114 return llvm::make_unique(SourceName, File, Options,
115 std::move(CoverageInfo));
116 case CoverageViewOptions::OutputFormat::HTML:
117 return llvm::make_unique(SourceName, File, Options,
112118 std::move(CoverageInfo));
113119 }
114120 llvm_unreachable("Unknown coverage output format!");
"; "; ";
0 //===- SourceCoverageViewHTML.cpp - A html code coverage view -------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file This file implements the html coverage renderer.
10 ///
11 //===----------------------------------------------------------------------===//
12
13 #include "SourceCoverageViewHTML.h"
14 #include "llvm/ADT/Optional.h"
15 #include "llvm/ADT/SmallString.h"
16 #include "llvm/ADT/StringExtras.h"
17 #include "llvm/Support/Path.h"
18
19 using namespace llvm;
20
21 namespace {
22
23 const char *BeginHeader =
24 ""
25 ""
26 "";
27
28 const char *CSSForCoverage =
29 "";
145
146 const char *EndHeader = "";
147
148 const char *BeginCenteredDiv = "
";
149
150 const char *EndCenteredDiv = "";
151
152 const char *BeginSourceNameDiv = "
";
153
154 const char *EndSourceNameDiv = "";
155
156 const char *BeginCodeTD = "";
157
158 const char *EndCodeTD = "
159
160 const char *BeginPre = "
";

                  
                
161
162 const char *EndPre = "";
163
164 const char *BeginExpansionDiv = "
";
165
166 const char *EndExpansionDiv = "";
167
168 const char *BeginTable = "";
169
170 const char *EndTable = "
";
171
172 void emitPrelude(raw_ostream &OS) {
173 OS << ""
174 ""
175 << BeginHeader << CSSForCoverage << EndHeader << ""
176 << BeginCenteredDiv;
177 }
178
179 void emitEpilog(raw_ostream &OS) {
180 OS << EndCenteredDiv << ""
181 "";
182 }
183
184 // Return a string with the special characters in \p Str escaped.
185 std::string escape(StringRef Str) {
186 std::string Result;
187 for (char C : Str) {
188 if (C == '&')
189 Result += "&";
190 else if (C == '<')
191 Result += "<";
192 else if (C == '>')
193 Result += ">";
194 else if (C == '\"')
195 Result += """;
196 else
197 Result += C;
198 }
199 return Result;
200 }
201
202 // Create a \p Name tag around \p Str, and optionally set its \p ClassName.
203 std::string tag(const std::string &Name, const std::string &Str,
204 const std::string &ClassName = "") {
205 std::string Tag = "<" + Name;
206 if (ClassName != "")
207 Tag += " class='" + ClassName + "'";
208 return Tag + ">" + Str + "";
209 }
210
211 // Create an anchor to \p Link with the label \p Str.
212 std::string a(const std::string &Link, const std::string &Str) {
213 return "" + Str + "";
214 }
215
216 } // anonymous namespace
217
218 Expected
219 CoveragePrinterHTML::createViewFile(StringRef Path, bool InToplevel) {
220 auto OSOrErr = createOutputStream(Path, "html", InToplevel);
221 if (!OSOrErr)
222 return OSOrErr;
223
224 OwnedStream OS = std::move(OSOrErr.get());
225 emitPrelude(*OS.get());
226 return std::move(OS);
227 }
228
229 void CoveragePrinterHTML::closeViewFile(OwnedStream OS) {
230 emitEpilog(*OS.get());
231 }
232
233 Error CoveragePrinterHTML::createIndexFile(ArrayRef SourceFiles) {
234 auto OSOrErr = createOutputStream("index", "html", /*InToplevel=*/true);
235 if (Error E = OSOrErr.takeError())
236 return E;
237 auto OS = std::move(OSOrErr.get());
238 raw_ostream &OSRef = *OS.get();
239
240 // Emit a table containing links to reports for each file in the covmapping.
241 emitPrelude(OSRef);
242 OSRef << BeginSourceNameDiv << "Index" << EndSourceNameDiv;
243 OSRef << BeginTable;
244 for (StringRef SF : SourceFiles) {
245 std::string LinkText = escape(sys::path::relative_path(SF));
246 std::string LinkTarget =
247 escape(getOutputPath(SF, "html", /*InToplevel=*/false));
248 OSRef << tag("tr", tag("td", tag("pre", a(LinkTarget, LinkText), "code")));
249 }
250 OSRef << EndTable;
251 emitEpilog(OSRef);
252
253 return Error::success();
254 }
255
256 void SourceCoverageViewHTML::renderViewHeader(raw_ostream &OS) {
257 OS << BeginTable;
258 }
259
260 void SourceCoverageViewHTML::renderViewFooter(raw_ostream &OS) {
261 OS << EndTable;
262 }
263
264 void SourceCoverageViewHTML::renderSourceName(raw_ostream &OS) {
265 OS << BeginSourceNameDiv << tag("pre", escape(getSourceName()))
266 << EndSourceNameDiv;
267 }
268
269 void SourceCoverageViewHTML::renderLinePrefix(raw_ostream &OS, unsigned) {
270 OS << "
271 }
272
273 void SourceCoverageViewHTML::renderLineSuffix(raw_ostream &OS, unsigned) {
274 // If this view has sub-views, renderLine() cannot close the view's cell.
275 // Take care of it here, after all sub-views have been rendered.
276 if (hasSubViews())
277 OS << EndCodeTD;
278 OS << "
279 }
280
281 void SourceCoverageViewHTML::renderViewDivider(raw_ostream &, unsigned) {
282 // The table-based output makes view dividers unnecessary.
283 }
284
285 void SourceCoverageViewHTML::renderLine(
286 raw_ostream &OS, LineRef L, const coverage::CoverageSegment *WrappedSegment,
287 CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned) {
288 StringRef Line = L.Line;
289
290 // Steps for handling text-escaping, highlighting, and tooltip creation:
291 //
292 // 1. Split the line into N+1 snippets, where N = |Segments|. The first
293 // snippet starts from Col=1 and ends at the start of the first segment.
294 // The last snippet starts at the last mapped column in the line and ends
295 // at the end of the line. Both are required but may be empty.
296
297 SmallVector Snippets;
298
299 unsigned LCol = 1;
300 auto Snip = [&](unsigned Start, unsigned Len) {
301 assert(Start + Len <= Line.size() && "Snippet extends past the EOL");
302 Snippets.push_back(Line.substr(Start, Len));
303 LCol += Len;
304 };
305
306 Snip(LCol - 1, Segments.empty() ? 0 : (Segments.front()->Col - 1));
307
308 for (unsigned I = 1, E = Segments.size(); I < E; ++I) {
309 assert(LCol == Segments[I - 1]->Col && "Snippet start position is wrong");
310 Snip(LCol - 1, Segments[I]->Col - LCol);
311 }
312
313 // |Line| + 1 is needed to avoid underflow when, e.g |Line| = 0 and LCol = 1.
314 Snip(LCol - 1, Line.size() + 1 - LCol);
315 assert(LCol == Line.size() + 1 && "Final snippet doesn't reach the EOL");
316
317 // 2. Escape all of the snippets.
318
319 for (unsigned I = 0, E = Snippets.size(); I < E; ++I)
320 Snippets[I] = escape(Snippets[I]);
321
322 // 3. Use \p WrappedSegment to set the highlight for snippets 0 and 1. Use
323 // segment 1 to set the highlight for snippet 2, segment 2 to set the
324 // highlight for snippet 3, and so on.
325
326 Optional Color;
327 auto Highlight = [&](const std::string &Snippet) {
328 return tag("span", Snippet, Color.getValue());
329 };
330
331 auto CheckIfUncovered = [](const coverage::CoverageSegment *S) {
332 return S && S->HasCount && S->Count == 0;
333 };
334
335 if (CheckIfUncovered(WrappedSegment) ||
336 CheckIfUncovered(Segments.empty() ? nullptr : Segments.front())) {
337 Color = "red";
338 Snippets[0] = Highlight(Snippets[0]);
339 Snippets[1] = Highlight(Snippets[1]);
340 }
341
342 for (unsigned I = 1, E = Segments.size(); I < E; ++I) {
343 const auto *CurSeg = Segments[I];
344 if (CurSeg->Col == ExpansionCol)
345 Color = "cyan";
346 else if (CheckIfUncovered(CurSeg))
347 Color = "red";
348 else
349 Color = None;
350
351 if (Color.hasValue())
352 Snippets[I + 1] = Highlight(Snippets[I + 1]);
353 }
354
355 // 4. Snippets[1:N+1] correspond to \p Segments[0:N]: use these to generate
356 // sub-line region count tooltips if needed.
357
358 bool HasMultipleRegions = [&] {
359 unsigned RegionCount = 0;
360 for (const auto *S : Segments)
361 if (S->HasCount && S->IsRegionEntry)
362 if (++RegionCount > 1)
363 return true;
364 return false;
365 }();
366
367 if (shouldRenderRegionMarkers(HasMultipleRegions)) {
368 for (unsigned I = 0, E = Segments.size(); I < E; ++I) {
369 const auto *CurSeg = Segments[I];
370 if (!CurSeg->IsRegionEntry || !CurSeg->HasCount)
371 continue;
372
373 Snippets[I + 1] =
374 tag("div", Snippets[I + 1] + tag("span", formatCount(CurSeg->Count),
375 "tooltip-content"),
376 "tooltip");
377 }
378 }
379
380 OS << BeginCodeTD;
381 OS << BeginPre;
382 for (const auto &Snippet : Snippets)
383 OS << Snippet;
384 OS << EndPre;
385
386 // If there are no sub-views left to attach to this cell, end the cell.
387 // Otherwise, end it after the sub-views are rendered (renderLineSuffix()).
388 if (!hasSubViews())
389 OS << EndCodeTD;
390 }
391
392 void SourceCoverageViewHTML::renderLineCoverageColumn(
393 raw_ostream &OS, const LineCoverageStats &Line) {
394 std::string Count = "";
395 if (Line.isMapped())
396 Count = tag("pre", formatCount(Line.ExecutionCount));
397 std::string CoverageClass =
398 (Line.ExecutionCount > 0) ? "covered-line" : "uncovered-line";
399 OS << tag("td", Count, CoverageClass);
400 }
401
402 void SourceCoverageViewHTML::renderLineNumberColumn(raw_ostream &OS,
403 unsigned LineNo) {
404 OS << tag("td", tag("pre", utostr(uint64_t(LineNo))), "line-number");
405 }
406
407 void SourceCoverageViewHTML::renderRegionMarkers(raw_ostream &,
408 CoverageSegmentArray,
409 unsigned) {
410 // Region markers are rendered in-line using tooltips.
411 }
412
413 void SourceCoverageViewHTML::renderExpansionSite(
414 raw_ostream &OS, LineRef L, const coverage::CoverageSegment *WrappedSegment,
415 CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned ViewDepth) {
416 // Render the line containing the expansion site. No extra formatting needed.
417 renderLine(OS, L, WrappedSegment, Segments, ExpansionCol, ViewDepth);
418 }
419
420 void SourceCoverageViewHTML::renderExpansionView(raw_ostream &OS,
421 ExpansionView &ESV,
422 unsigned ViewDepth) {
423 OS << BeginExpansionDiv;
424 ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,
425 ViewDepth + 1);
426 OS << EndExpansionDiv;
427 }
428
429 void SourceCoverageViewHTML::renderInstantiationView(raw_ostream &OS,
430 InstantiationView &ISV,
431 unsigned ViewDepth) {
432 OS << BeginExpansionDiv;
433 ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true, ViewDepth);
434 OS << EndExpansionDiv;
435 }
0 //===- SourceCoverageViewHTML.h - A html code coverage view ---------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file This file defines the interface to the html coverage renderer.
10 ///
11 //===----------------------------------------------------------------------===//
12
13 #ifndef LLVM_COV_SOURCECOVERAGEVIEWHTML_H
14 #define LLVM_COV_SOURCECOVERAGEVIEWHTML_H
15
16 #include "SourceCoverageView.h"
17
18 namespace llvm {
19
20 /// \brief A coverage printer for html output.
21 class CoveragePrinterHTML : public CoveragePrinter {
22 public:
23 Expected createViewFile(StringRef Path,
24 bool InToplevel) override;
25
26 void closeViewFile(OwnedStream OS) override;
27
28 Error createIndexFile(ArrayRef SourceFiles) override;
29
30 CoveragePrinterHTML(const CoverageViewOptions &Opts)
31 : CoveragePrinter(Opts) {}
32 };
33
34 /// \brief A code coverage view which supports html-based rendering.
35 class SourceCoverageViewHTML : public SourceCoverageView {
36 void renderViewHeader(raw_ostream &OS) override;
37
38 void renderViewFooter(raw_ostream &OS) override;
39
40 void renderSourceName(raw_ostream &OS) override;
41
42 void renderLinePrefix(raw_ostream &OS, unsigned ViewDepth) override;
43
44 void renderLineSuffix(raw_ostream &OS, unsigned ViewDepth) override;
45
46 void renderViewDivider(raw_ostream &OS, unsigned ViewDepth) override;
47
48 void renderLine(raw_ostream &OS, LineRef L,
49 const coverage::CoverageSegment *WrappedSegment,
50 CoverageSegmentArray Segments, unsigned ExpansionCol,
51 unsigned ViewDepth) override;
52
53 void renderExpansionSite(raw_ostream &OS, LineRef L,
54 const coverage::CoverageSegment *WrappedSegment,
55 CoverageSegmentArray Segments, unsigned ExpansionCol,
56 unsigned ViewDepth) override;
57
58 void renderExpansionView(raw_ostream &OS, ExpansionView &ESV,
59 unsigned ViewDepth) override;
60
61 void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
62 unsigned ViewDepth) override;
63
64 void renderLineCoverageColumn(raw_ostream &OS,
65 const LineCoverageStats &Line) override;
66
67 void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo) override;
68
69 void renderRegionMarkers(raw_ostream &OS, CoverageSegmentArray Segments,
70 unsigned ViewDepth) override;
71
72 public:
73 SourceCoverageViewHTML(StringRef SourceName, const MemoryBuffer &File,
74 const CoverageViewOptions &Options,
75 coverage::CoverageData &&CoverageInfo)
76 : SourceCoverageView(SourceName, File, Options, std::move(CoverageInfo)) {
77 }
78 };
79
80 } // namespace llvm
81
82 #endif // LLVM_COV_SOURCECOVERAGEVIEWHTML_H