llvm.org GIT mirror llvm / 6c7a6a1
llvm-cov: add code coverage tool that's based on coverage mapping format and clang's pgo. This commit expands llvm-cov's functionality by adding support for a new code coverage tool that uses LLVM's coverage mapping format and clang's instrumentation based profiling. The gcov compatible tool can be invoked by supplying the 'gcov' command as the first argument, or by modifying the tool's name to end with 'gcov'. Differential Revision: http://reviews.llvm.org/D4445 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@216300 91177308-0d34-0410-b5e6-96231b3b80d8 Alex Lorenz 6 years ago
32 changed file(s) with 2737 addition(s) and 34 deletion(s). Raw diff Collapse all Expand all
288288 Object = std::move(File.get());
289289 }
290290
291 ObjectFileCoverageMappingReader::ObjectFileCoverageMappingReader(
292 std::unique_ptr &ObjectBuffer, sys::fs::file_magic Type)
293 : CurrentRecord(0) {
294 auto File = object::ObjectFile::createObjectFile(
295 ObjectBuffer->getMemBufferRef(), Type);
296 if (!File)
297 error(File.getError());
298 else
299 Object = OwningBinary(std::move(File.get()),
300 std::move(ObjectBuffer));
301 }
302
303291 namespace {
304292 /// \brief The coverage mapping data for a single function.
305293 /// It points to the function's name.
346334
347335 template
348336 std::error_code readCoverageMappingData(
349 SectionRef &ProfileNames, SectionRef &CoverageMapping,
337 SectionData &ProfileNames, StringRef Data,
350338 std::vector &Records,
351339 std::vector &Filenames) {
352340 llvm::DenseSet UniqueFunctionMappingData;
353
354 // Get the contents of the given sections.
355 StringRef Data;
356 if (auto Err = CoverageMapping.getContents(Data))
357 return Err;
358 SectionData ProfileNamesData;
359 if (auto Err = ProfileNamesData.load(ProfileNames))
360 return Err;
361341
362342 // Read the records in the coverage data section.
363343 while (!Data.empty()) {
417397 continue;
418398 UniqueFunctionMappingData.insert(MappingRecord.FunctionNamePtr);
419399 StringRef FunctionName;
420 if (auto Err = ProfileNamesData.get(MappingRecord.FunctionNamePtr,
421 MappingRecord.FunctionNameSize,
422 FunctionName))
400 if (auto Err =
401 ProfileNames.get(MappingRecord.FunctionNamePtr,
402 MappingRecord.FunctionNameSize, FunctionName))
423403 return Err;
424404 Records.push_back(ObjectFileCoverageMappingReader::ProfileMappingRecord(
425405 Version, FunctionName, MappingRecord.FunctionHash, Mapping,
428408 }
429409
430410 return instrprof_error::success;
411 }
412
413 static const char *TestingFormatMagic = "llvmcovmtestdata";
414
415 static std::error_code decodeTestingFormat(StringRef Data,
416 SectionData &ProfileNames,
417 StringRef &CoverageMapping) {
418 Data = Data.substr(StringRef(TestingFormatMagic).size());
419 if (Data.size() < 1)
420 return instrprof_error::truncated;
421 unsigned N = 0;
422 auto ProfileNamesSize =
423 decodeULEB128(reinterpret_cast(Data.data()), &N);
424 if (N > Data.size())
425 return instrprof_error::malformed;
426 Data = Data.substr(N);
427 if (Data.size() < 1)
428 return instrprof_error::truncated;
429 N = 0;
430 ProfileNames.Address =
431 decodeULEB128(reinterpret_cast(Data.data()), &N);
432 if (N > Data.size())
433 return instrprof_error::malformed;
434 Data = Data.substr(N);
435 if (Data.size() < ProfileNamesSize)
436 return instrprof_error::malformed;
437 ProfileNames.Data = Data.substr(0, ProfileNamesSize);
438 CoverageMapping = Data.substr(ProfileNamesSize);
439 return instrprof_error::success;
440 }
441
442 ObjectFileCoverageMappingReader::ObjectFileCoverageMappingReader(
443 std::unique_ptr &ObjectBuffer, sys::fs::file_magic Type)
444 : CurrentRecord(0) {
445 if (ObjectBuffer->getBuffer().startswith(TestingFormatMagic)) {
446 // This is a special format used for testing.
447 SectionData ProfileNames;
448 StringRef CoverageMapping;
449 if (auto Err = decodeTestingFormat(ObjectBuffer->getBuffer(), ProfileNames,
450 CoverageMapping)) {
451 error(Err);
452 return;
453 }
454 error(readCoverageMappingData(ProfileNames, CoverageMapping,
455 MappingRecords, Filenames));
456 Object = OwningBinary(std::unique_ptr(),
457 std::move(ObjectBuffer));
458 return;
459 }
460
461 auto File = object::ObjectFile::createObjectFile(
462 ObjectBuffer->getMemBufferRef(), Type);
463 if (!File)
464 error(File.getError());
465 else
466 Object = OwningBinary(std::move(File.get()),
467 std::move(ObjectBuffer));
431468 }
432469
433470 std::error_code ObjectFileCoverageMappingReader::readHeader() {
456493 if (FoundSectionCount != 2)
457494 return error(instrprof_error::bad_header);
458495
496 // Get the contents of the given sections.
497 StringRef Data;
498 if (auto Err = CoverageMapping.getContents(Data))
499 return Err;
500 SectionData ProfileNamesData;
501 if (auto Err = ProfileNamesData.load(ProfileNames))
502 return Err;
503
459504 // Load the data from the found sections.
460505 std::error_code Err;
461506 if (BytesInAddress == 4)
462 Err = readCoverageMappingData(ProfileNames, CoverageMapping,
507 Err = readCoverageMappingData(ProfileNamesData, Data,
463508 MappingRecords, Filenames);
464509 else
465 Err = readCoverageMappingData(ProfileNames, CoverageMapping,
510 Err = readCoverageMappingData(ProfileNamesData, Data,
466511 MappingRecords, Filenames);
467512 if (Err)
468513 return error(Err);
0 These inputs were pre-generated to allow for easier testing of llvm-cov.
11
2 test.gcno and test.gcda were create by running clang:
3 clang++ -g -ftest-coverage -fprofile-arcs test.cpp
2 The files used to test the gcov compatible code coverage tool were generated
3 using the following method:
44
5 test.cpp.gcov was created by running gcov 4.2.1:
6 gcov test.cpp
5 test.gcno and test.gcda were create by running clang:
6 clang++ -g -ftest-coverage -fprofile-arcs test.cpp
7
8 test.cpp.gcov was created by running gcov 4.2.1:
9 gcov test.cpp
10
11 The 'covmapping' files that are used to test llvm-cov contain raw sections
12 with the coverage mapping data generated by the compiler and linker. They are
13 created by running clang and llvm-cov:
14 clang++ -fprofile-instr-generate -fcoverage-mapping -o test test.cpp
15 llvm-cov convert-for-testing -o test.covmapping test
16
17 The 'profdata' files were generated by running an instrumented version of the
18 program and merging the raw profile data using llvm-profdata.
19 ./test
20 llvm-profdata merge -o test.profdata default.profraw
0 // RUN: llvm-cov show %S/Inputs/highlightedRanges.covmapping -instr-profile %S/Inputs/highlightedRanges.profdata -dump -filename-equivalence %s | FileCheck %s
1
2 void func() {
3 return;
4 int i = 0; // CHECK: Highlighted line [[@LINE]], 3 -> 12
5 }
6
7 void func2(int x) {
8 if(x > 5) {
9 while(x >= 9) {
10 return;
11 --x; // CHECK: Highlighted line [[@LINE]], 7 -> 10
12 }
13 int i = 0; // CHECK: Highlighted line [[@LINE]], 5 -> 14
14 }
15 }
16
17 void test() {
18 int x = 0;
19
20 if (x) { // CHECK: Highlighted line [[@LINE]], 10 -> ?
21 x = 0; // CHECK: Highlighted line [[@LINE]], 1 -> ?
22 } else { // CHECK: Highlighted line [[@LINE]], 1 -> 4
23 x = 1;
24 }
25
26 // CHECK: Highlighted line [[@LINE+1]], 26 -> 29
27 for (int i = 0; i < 0; ++i) { // CHECK: Highlighted line [[@LINE]], 31 -> ?
28 x = 1; // CHECK: Highlighted line [[@LINE]], 1 -> ?
29 } // CHECK: Highlighted line [[@LINE]], 1 -> 4
30
31 x = x < 10 ? x +
32 1
33 : x - 1; // CHECK: Highlighted line [[@LINE]], 16 -> 21
34 x = x > 10 ? x + // CHECK: Highlighted line [[@LINE]], 16 -> ?
35 1 // CHECK: Highlighted line [[@LINE]], 1 -> 17
36 : x - 1;
37 }
38
39 int main() {
40 test();
41 func();
42 func2(9);
43 return 0;
44 }
0 // RUN: llvm-cov show %S/Inputs/lineExecutionCounts.covmapping -instr-profile %S/Inputs/lineExecutionCounts.profdata -no-colors -filename-equivalence %s | FileCheck %s
1
2 int main() { // CHECK: 1| [[@LINE]]|int main(
3 int x = 0; // CHECK: 1| [[@LINE]]| int x
4 // CHECK: 1| [[@LINE]]|
5 if (x) { // CHECK: 0| [[@LINE]]| if (x)
6 x = 0; // CHECK: 0| [[@LINE]]| x = 0
7 } else { // CHECK: 1| [[@LINE]]| } else
8 x = 1; // CHECK: 1| [[@LINE]]| x = 1
9 } // CHECK: 1| [[@LINE]]| }
10 // CHECK: 1| [[@LINE]]|
11 for (int i = 0; i < 100; ++i) { // CHECK: 100| [[@LINE]]| for (
12 x = 1; // CHECK: 100| [[@LINE]]| x = 1
13 } // CHECK: 100| [[@LINE]]| }
14 // CHECK: 1| [[@LINE]]|
15 x = x < 10 ? x + 1 : x - 1; // CHECK: 0| [[@LINE]]| x =
16 x = x > 10 ? // CHECK: 1| [[@LINE]]| x =
17 x - 1: // CHECK: 0| [[@LINE]]| x
18 x + 1; // CHECK: 1| [[@LINE]]| x
19 // CHECK: 1| [[@LINE]]|
20 return 0; // CHECK: 1| [[@LINE]]| return
21 } // CHECK: 1| [[@LINE]]|}
0 // RUN: llvm-cov show %S/Inputs/regionMarkers.covmapping -instr-profile %S/Inputs/regionMarkers.profdata -show-regions -dump -filename-equivalence %s | FileCheck %s
1
2 int main() { // CHECK: Marker at [[@LINE]]:12 = 1
3 int x = 0;
4
5 if (x) { // CHECK: Marker at [[@LINE]]:10 = 0
6 x = 0;
7 } else { // CHECK: Marker at [[@LINE]]:10 = 1
8 x = 1;
9 }
10 // CHECK: Marker at [[@LINE+2]]:19 = 101
11 // CHECK: Marker at [[@LINE+1]]:28 = 100
12 for (int i = 0; i < 100; ++i) { // CHECK: Marker at [[@LINE]]:33 = 100
13 x = 1;
14 }
15 // CHECK: Marker at [[@LINE+1]]:16 = 1
16 x = x < 10 ? x + 1 : x - 1; // CHECK: Marker at [[@LINE]]:24 = 0
17 x = x > 10 ?
18 x - 1: // CHECK: Marker at [[@LINE]]:9 = 0
19 x + 1; // CHECK: Marker at [[@LINE]]:9 = 1
20
21 return 0;
22 }
None set(LLVM_LINK_COMPONENTS core support )
0 set(LLVM_LINK_COMPONENTS core support object profiledata)
11
22 add_llvm_tool(llvm-cov
33 llvm-cov.cpp
44 gcov.cpp
5 CodeCoverage.cpp
6 CoverageFilters.cpp
7 CoverageReport.cpp
8 CoverageSummary.cpp
9 CoverageSummaryInfo.cpp
10 SourceCoverageDataManager.cpp
11 SourceCoverageView.cpp
12 TestingSupport.cpp
513 )
0 //===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===//
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 // The 'CodeCoverageTool' class implements a command line tool to analyze and
10 // report coverage information using the profiling instrumentation and code
11 // coverage mapping.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #include "FunctionCoverageMapping.h"
16 #include "RenderingSupport.h"
17 #include "CoverageViewOptions.h"
18 #include "CoverageFilters.h"
19 #include "SourceCoverageDataManager.h"
20 #include "SourceCoverageView.h"
21 #include "CoverageSummary.h"
22 #include "CoverageReport.h"
23 #include "llvm/ADT/StringRef.h"
24 #include "llvm/ADT/SmallString.h"
25 #include "llvm/ADT/SmallSet.h"
26 #include "llvm/ADT/DenseSet.h"
27 #include "llvm/ProfileData/InstrProfReader.h"
28 #include "llvm/ProfileData/CoverageMapping.h"
29 #include "llvm/ProfileData/CoverageMappingReader.h"
30 #include "llvm/Support/CommandLine.h"
31 #include "llvm/Support/FileSystem.h"
32 #include "llvm/Support/ManagedStatic.h"
33 #include "llvm/Support/MemoryObject.h"
34 #include "llvm/Support/Format.h"
35 #include "llvm/Support/Path.h"
36 #include "llvm/Support/Signals.h"
37 #include "llvm/Support/PrettyStackTrace.h"
38 #include
39 #include
40
41 using namespace llvm;
42 using namespace coverage;
43
44 namespace {
45 /// \brief Distribute the functions into instantiation sets.
46 /// An instantiation set is a collection of functions
47 /// that have the same source code, e.g.
48 /// template functions specializations.
49 class FunctionInstantiationSetCollector {
50 ArrayRef FunctionMappings;
51 typedef uint64_t KeyType;
52 typedef std::vector SetType;
53 std::unordered_map InstantiatedFunctions;
54
55 static KeyType getKey(const MappingRegion &R) {
56 return uint64_t(R.LineStart) | uint64_t(R.ColumnStart) << 32;
57 }
58
59 public:
60 void insert(const FunctionCoverageMapping &Function, unsigned FileID) {
61 KeyType Key = 0;
62 for (const auto &R : Function.MappingRegions) {
63 if (R.FileID == FileID) {
64 Key = getKey(R);
65 break;
66 }
67 }
68 auto I = InstantiatedFunctions.find(Key);
69 if (I == InstantiatedFunctions.end()) {
70 SetType Set;
71 Set.push_back(&Function);
72 InstantiatedFunctions.insert(std::make_pair(Key, Set));
73 } else
74 I->second.push_back(&Function);
75 }
76
77 std::unordered_map::iterator begin() {
78 return InstantiatedFunctions.begin();
79 }
80
81 std::unordered_map::iterator end() {
82 return InstantiatedFunctions.end();
83 }
84 };
85
86 /// \brief The implementation of the coverage tool.
87 class CodeCoverageTool {
88 public:
89 enum Command {
90 /// \brief The show command.
91 Show,
92 /// \brief The report command.
93 Report
94 };
95
96 /// \brief Print the error message to the error output stream.
97 void error(const Twine &Message, StringRef Whence = "");
98
99 /// \brief Return a memory buffer for the given source file.
100 ErrorOr getSourceFile(StringRef SourceFile);
101
102 /// \brief Return true if two filepaths refer to the same file.
103 bool equivalentFiles(StringRef A, StringRef B);
104
105 /// \brief Collect a set of function's file ids which correspond to the
106 /// given source file. Return false if the set is empty.
107 bool gatherInterestingFileIDs(StringRef SourceFile,
108 const FunctionCoverageMapping &Function,
109 SmallSet &InterestingFileIDs);
110
111 /// \brief Find the file id which is not an expanded file id.
112 bool findMainViewFileID(StringRef SourceFile,
113 const FunctionCoverageMapping &Function,
114 unsigned &MainViewFileID);
115
116 bool findMainViewFileID(const FunctionCoverageMapping &Function,
117 unsigned &MainViewFileID);
118
119 /// \brief Create a source view which shows coverage for an expansion
120 /// of a file.
121 void createExpansionSubView(const MappingRegion &ExpandedRegion,
122 const FunctionCoverageMapping &Function,
123 SourceCoverageView &Parent);
124
125 void createExpansionSubViews(SourceCoverageView &View, unsigned ViewFileID,
126 const FunctionCoverageMapping &Function);
127
128 /// \brief Create a source view which shows coverage for an instantiation
129 /// of a funciton.
130 void createInstantiationSubView(StringRef SourceFile,
131 const FunctionCoverageMapping &Function,
132 SourceCoverageView &View);
133
134 /// \brief Create the main source view of a particular source file.
135 /// Return true if this particular source file is not covered.
136 bool
137 createSourceFileView(StringRef SourceFile, SourceCoverageView &View,
138 ArrayRef FunctionMappingRecords,
139 bool UseOnlyRegionsInMainFile = false);
140
141 /// \brief Load the coverage mapping data. Return true if an error occured.
142 bool load();
143
144 int run(Command Cmd, int argc, const char **argv);
145
146 typedef std::function CommandLineParserType;
147
148 int show(int argc, const char **argv,
149 CommandLineParserType commandLineParser);
150
151 int report(int argc, const char **argv,
152 CommandLineParserType commandLineParser);
153
154 StringRef ObjectFilename;
155 CoverageViewOptions ViewOpts;
156 std::unique_ptr PGOReader;
157 CoverageFiltersMatchAll Filters;
158 std::vector SourceFiles;
159 std::vector>>
160 LoadedSourceFiles;
161 std::vector FunctionMappingRecords;
162 bool CompareFilenamesOnly;
163 };
164 }
165
166 void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
167 errs() << "error: ";
168 if (!Whence.empty())
169 errs() << Whence << ": ";
170 errs() << Message << "\n";
171 }
172
173 ErrorOr
174 CodeCoverageTool::getSourceFile(StringRef SourceFile) {
175 SmallString<256> Path(SourceFile);
176 sys::fs::make_absolute(Path);
177 for (const auto &Files : LoadedSourceFiles) {
178 if (sys::fs::equivalent(Path.str(), Files.first)) {
179 return *Files.second;
180 }
181 }
182 auto Buffer = MemoryBuffer::getFile(SourceFile);
183 if (auto EC = Buffer.getError()) {
184 error(EC.message(), SourceFile);
185 return EC;
186 }
187 LoadedSourceFiles.push_back(std::make_pair(
188 std::string(Path.begin(), Path.end()), std::move(Buffer.get())));
189 return *LoadedSourceFiles.back().second;
190 }
191
192 /// \brief Return a line start - line end range which contains
193 /// all the mapping regions of a given function with a particular file id.
194 std::pair
195 findExpandedFileInterestingLineRange(unsigned FileID,
196 const FunctionCoverageMapping &Function) {
197 unsigned LineStart = std::numeric_limits::max();
198 unsigned LineEnd = 0;
199 for (const auto &Region : Function.MappingRegions) {
200 if (Region.FileID != FileID)
201 continue;
202 LineStart = std::min(Region.LineStart, LineStart);
203 LineEnd = std::max(Region.LineEnd, LineEnd);
204 }
205 return std::make_pair(LineStart, LineEnd);
206 }
207
208 bool CodeCoverageTool::equivalentFiles(StringRef A, StringRef B) {
209 if (CompareFilenamesOnly)
210 return sys::path::filename(A).equals_lower(sys::path::filename(B));
211 return sys::fs::equivalent(A, B);
212 }
213
214 bool CodeCoverageTool::gatherInterestingFileIDs(
215 StringRef SourceFile, const FunctionCoverageMapping &Function,
216 SmallSet &InterestingFileIDs) {
217 bool Interesting = false;
218 for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
219 if (equivalentFiles(SourceFile, Function.Filenames[I])) {
220 InterestingFileIDs.insert(I);
221 Interesting = true;
222 }
223 }
224 return Interesting;
225 }
226
227 bool
228 CodeCoverageTool::findMainViewFileID(StringRef SourceFile,
229 const FunctionCoverageMapping &Function,
230 unsigned &MainViewFileID) {
231 llvm::SmallVector IsExpandedFile(Function.Filenames.size(), false);
232 llvm::SmallVector FilenameEquivalence(Function.Filenames.size(),
233 false);
234 for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
235 if (equivalentFiles(SourceFile, Function.Filenames[I]))
236 FilenameEquivalence[I] = true;
237 }
238 for (const auto &Region : Function.MappingRegions) {
239 if (Region.Kind == MappingRegion::ExpansionRegion &&
240 FilenameEquivalence[Region.FileID])
241 IsExpandedFile[Region.ExpandedFileID] = true;
242 }
243 for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
244 if (!FilenameEquivalence[I] || IsExpandedFile[I])
245 continue;
246 MainViewFileID = I;
247 return false;
248 }
249 return true;
250 }
251
252 bool
253 CodeCoverageTool::findMainViewFileID(const FunctionCoverageMapping &Function,
254 unsigned &MainViewFileID) {
255 llvm::SmallVector IsExpandedFile(Function.Filenames.size(), false);
256 for (const auto &Region : Function.MappingRegions) {
257 if (Region.Kind == MappingRegion::ExpansionRegion)
258 IsExpandedFile[Region.ExpandedFileID] = true;
259 }
260 for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
261 if (IsExpandedFile[I])
262 continue;
263 MainViewFileID = I;
264 return false;
265 }
266 return true;
267 }
268
269 void CodeCoverageTool::createExpansionSubView(
270 const MappingRegion &ExpandedRegion,
271 const FunctionCoverageMapping &Function, SourceCoverageView &Parent) {
272 auto ExpandedLines = findExpandedFileInterestingLineRange(
273 ExpandedRegion.ExpandedFileID, Function);
274 if (ViewOpts.Debug)
275 llvm::outs() << "Expansion of " << ExpandedRegion.ExpandedFileID << ":"
276 << ExpandedLines.first << " -> " << ExpandedLines.second
277 << " @ " << ExpandedRegion.FileID << ", "
278 << ExpandedRegion.LineStart << ":"
279 << ExpandedRegion.ColumnStart << "\n";
280 auto SourceBuffer =
281 getSourceFile(Function.Filenames[ExpandedRegion.ExpandedFileID]);
282 if (!SourceBuffer)
283 return;
284 auto SubView = llvm::make_unique(
285 SourceBuffer.get(), Parent.getOptions(), ExpandedLines.first,
286 ExpandedLines.second, ExpandedRegion);
287 SourceCoverageDataManager RegionManager;
288 for (const auto &Region : Function.MappingRegions) {
289 if (Region.FileID == ExpandedRegion.ExpandedFileID)
290 RegionManager.insert(Region);
291 }
292 SubView->load(RegionManager);
293 createExpansionSubViews(*SubView, ExpandedRegion.ExpandedFileID, Function);
294 Parent.addChild(std::move(SubView));
295 }
296
297 void CodeCoverageTool::createExpansionSubViews(
298 SourceCoverageView &View, unsigned ViewFileID,
299 const FunctionCoverageMapping &Function) {
300 if (!ViewOpts.ShowExpandedRegions)
301 return;
302 for (const auto &Region : Function.MappingRegions) {
303 if (Region.Kind != CounterMappingRegion::ExpansionRegion)
304 continue;
305 if (Region.FileID != ViewFileID)
306 continue;
307 createExpansionSubView(Region, Function, View);
308 }
309 }
310
311 void CodeCoverageTool::createInstantiationSubView(
312 StringRef SourceFile, const FunctionCoverageMapping &Function,
313 SourceCoverageView &View) {
314 SourceCoverageDataManager RegionManager;
315 SmallSet InterestingFileIDs;
316 if (!gatherInterestingFileIDs(SourceFile, Function, InterestingFileIDs))
317 return;
318 // Get the interesting regions
319 for (const auto &Region : Function.MappingRegions) {
320 if (InterestingFileIDs.count(Region.FileID))
321 RegionManager.insert(Region);
322 }
323 View.load(RegionManager);
324 unsigned MainFileID;
325 if (findMainViewFileID(SourceFile, Function, MainFileID))
326 return;
327 createExpansionSubViews(View, MainFileID, Function);
328 }
329
330 bool CodeCoverageTool::createSourceFileView(
331 StringRef SourceFile, SourceCoverageView &View,
332 ArrayRef FunctionMappingRecords,
333 bool UseOnlyRegionsInMainFile) {
334 SourceCoverageDataManager RegionManager;
335 FunctionInstantiationSetCollector InstantiationSetCollector;
336
337 for (const auto &Function : FunctionMappingRecords) {
338 unsigned MainFileID;
339 if (findMainViewFileID(SourceFile, Function, MainFileID))
340 continue;
341 SmallSet InterestingFileIDs;
342 if (UseOnlyRegionsInMainFile) {
343 InterestingFileIDs.insert(MainFileID);
344 } else if (!gatherInterestingFileIDs(SourceFile, Function,
345 InterestingFileIDs))
346 continue;
347 // Get the interesting regions
348 for (const auto &Region : Function.MappingRegions) {
349 if (InterestingFileIDs.count(Region.FileID))
350 RegionManager.insert(Region);
351 }
352 InstantiationSetCollector.insert(Function, MainFileID);
353 createExpansionSubViews(View, MainFileID, Function);
354 }
355 if (RegionManager.getSourceRegions().empty())
356 return true;
357 View.load(RegionManager);
358 // Show instantiations
359 if (!ViewOpts.ShowFunctionInstantiations)
360 return false;
361 for (const auto &InstantiationSet : InstantiationSetCollector) {
362 if (InstantiationSet.second.size() < 2)
363 continue;
364 auto InterestingRange = findExpandedFileInterestingLineRange(
365 InstantiationSet.second.front()->MappingRegions.front().FileID,
366 *InstantiationSet.second.front());
367 for (auto Function : InstantiationSet.second) {
368 auto SubView = llvm::make_unique(
369 View, InterestingRange.first, InterestingRange.second,
370 Function->PrettyName);
371 createInstantiationSubView(SourceFile, *Function, *SubView);
372 View.addChild(std::move(SubView));
373 }
374 }
375 return false;
376 }
377
378 bool CodeCoverageTool::load() {
379 auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename);
380 if (auto EC = CounterMappingBuff.getError()) {
381 error(EC.message(), ObjectFilename);
382 return true;
383 }
384 ObjectFileCoverageMappingReader MappingReader(CounterMappingBuff.get());
385 if (auto EC = MappingReader.readHeader()) {
386 error(EC.message(), ObjectFilename);
387 return true;
388 }
389
390 std::vector Counts;
391 for (const auto &I : MappingReader) {
392 FunctionCoverageMapping Function(I.FunctionName, I.Filenames);
393
394 // Create the mapping regions with evaluated execution counts
395 Counts.clear();
396 PGOReader->getFunctionCounts(Function.Name, I.FunctionHash, Counts);
397
398 // Get the biggest referenced counters
399 bool RegionError = false;
400 CounterMappingContext Ctx(I.Expressions, Counts);
401 for (const auto &R : I.MappingRegions) {
402 // Compute the values of mapped regions
403 if (ViewOpts.Debug) {
404 outs() << "File " << R.FileID << "| " << R.LineStart << ":"
405 << R.ColumnStart << " -> " << R.LineEnd << ":" << R.ColumnEnd
406 << " = ";
407 Ctx.dump(R.Count);
408 if (R.Kind == CounterMappingRegion::ExpansionRegion) {
409 outs() << " (Expanded file id = " << R.ExpandedFileID << ") ";
410 }
411 outs() << "\n";
412 }
413 std::error_code Error;
414 Function.MappingRegions.push_back(
415 MappingRegion(R, Ctx.evaluate(R.Count, Error)));
416 if (Error && !RegionError) {
417 colored_ostream(errs(), raw_ostream::RED)
418 << "error: Regions and counters don't match in a function '"
419 << Function.PrettyName << "' (re-run the instrumented binary).";
420 errs() << "\n";
421 RegionError = true;
422 }
423 }
424
425 if (RegionError || !Filters.matches(Function))
426 continue;
427
428 FunctionMappingRecords.push_back(Function);
429 }
430 return false;
431 }
432
433 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
434 // Print a stack trace if we signal out.
435 sys::PrintStackTraceOnErrorSignal();
436 PrettyStackTraceProgram X(argc, argv);
437 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
438
439 cl::list InputSourceFiles(
440 cl::Positional, cl::desc(""), cl::ZeroOrMore);
441
442 cl::opt PGOFilename(
443 "instr-profile", cl::Required,
444 cl::desc(
445 "File with the profile data obtained after an instrumented run"));
446
447 cl::opt DebugDump("dump", cl::Optional,
448 cl::desc("Show internal debug dump"));
449
450 cl::opt FilenameEquivalence(
451 "filename-equivalence", cl::Optional,
452 cl::desc("Compare the filenames instead of full filepaths"));
453
454 cl::OptionCategory FilteringCategory("Function filtering options");
455
456 cl::list NameFilters(
457 "name", cl::Optional,
458 cl::desc("Show code coverage only for functions with the given name"),
459 cl::ZeroOrMore, cl::cat(FilteringCategory));
460
461 cl::list NameRegexFilters(
462 "name-regex", cl::Optional,
463 cl::desc("Show code coverage only for functions that match the given "
464 "regular expression"),
465 cl::ZeroOrMore, cl::cat(FilteringCategory));
466
467 cl::opt RegionCoverageLtFilter(
468 "region-coverage-lt", cl::Optional,
469 cl::desc("Show code coverage only for functions with region coverage "
470 "less than the given threshold"),
471 cl::cat(FilteringCategory));
472
473 cl::opt RegionCoverageGtFilter(
474 "region-coverage-gt", cl::Optional,
475 cl::desc("Show code coverage only for functions with region coverage "
476 "greater than the given threshold"),
477 cl::cat(FilteringCategory));
478
479 cl::opt LineCoverageLtFilter(
480 "line-coverage-lt", cl::Optional,
481 cl::desc("Show code coverage only for functions with line coverage less "
482 "than the given threshold"),
483 cl::cat(FilteringCategory));
484
485 cl::opt LineCoverageGtFilter(
486 "line-coverage-gt", cl::Optional,
487 cl::desc("Show code coverage only for functions with line coverage "
488 "greater than the given threshold"),
489 cl::cat(FilteringCategory));
490
491 auto commandLineParser = [&, this](int argc, const char **argv) -> int {
492 cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
493 ViewOpts.Debug = DebugDump;
494 CompareFilenamesOnly = FilenameEquivalence;
495
496 if (auto EC = IndexedInstrProfReader::create(PGOFilename, PGOReader)) {
497 error(EC.message(), PGOFilename);
498 return 1;
499 }
500
501 // Create the function filters
502 if (!NameFilters.empty() || !NameRegexFilters.empty()) {
503 auto NameFilterer = new CoverageFilters;
504 for (const auto &Name : NameFilters)
505 NameFilterer->push_back(llvm::make_unique(Name));
506 for (const auto &Regex : NameRegexFilters)
507 NameFilterer->push_back(
508 llvm::make_unique(Regex));
509 Filters.push_back(std::unique_ptr(NameFilterer));
510 }
511 if (RegionCoverageLtFilter.getNumOccurrences() ||
512 RegionCoverageGtFilter.getNumOccurrences() ||
513 LineCoverageLtFilter.getNumOccurrences() ||
514 LineCoverageGtFilter.getNumOccurrences()) {
515 auto StatFilterer = new CoverageFilters;
516 if (RegionCoverageLtFilter.getNumOccurrences())
517 StatFilterer->push_back(llvm::make_unique(
518 RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
519 if (RegionCoverageGtFilter.getNumOccurrences())
520 StatFilterer->push_back(llvm::make_unique(
521 RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
522 if (LineCoverageLtFilter.getNumOccurrences())
523 StatFilterer->push_back(llvm::make_unique(
524 LineCoverageFilter::LessThan, LineCoverageLtFilter));
525 if (LineCoverageGtFilter.getNumOccurrences())
526 StatFilterer->push_back(llvm::make_unique(
527 RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
528 Filters.push_back(std::unique_ptr(StatFilterer));
529 }
530
531 SourceFiles = InputSourceFiles;
532 return 0;
533 };
534
535 // Parse the object filename
536 if (argc > 1) {
537 StringRef Arg(argv[1]);
538 if (Arg.equals_lower("-help") || Arg.equals_lower("-version")) {
539 cl::ParseCommandLineOptions(2, argv, "LLVM code coverage tool\n");
540 return 0;
541 }
542 ObjectFilename = Arg;
543
544 argv[1] = argv[0];
545 --argc;
546 ++argv;
547 } else {
548 errs() << sys::path::filename(argv[0]) << ": No executable file given!\n";
549 return 1;
550 }
551
552 switch (Cmd) {
553 case Show:
554 return show(argc, argv, commandLineParser);
555 case Report:
556 return report(argc, argv, commandLineParser);
557 }
558 return 0;
559 }
560
561 int CodeCoverageTool::show(int argc, const char **argv,
562 CommandLineParserType commandLineParser) {
563
564 cl::OptionCategory ViewCategory("Viewing options");
565
566 cl::opt ShowLineExecutionCounts(
567 "show-line-counts", cl::Optional,
568 cl::desc("Show the execution counts for each line"), cl::init(true),
569 cl::cat(ViewCategory));
570
571 cl::opt ShowRegions(
572 "show-regions", cl::Optional,
573 cl::desc("Show the execution counts for each region"),
574 cl::cat(ViewCategory));
575
576 cl::opt ShowBestLineRegionsCounts(
577 "show-line-counts-or-regions", cl::Optional,
578 cl::desc("Show the execution counts for each line, or the execution "
579 "counts for each region on lines that have multiple regions"),
580 cl::cat(ViewCategory));
581
582 cl::opt ShowExpansions("show-expansions", cl::Optional,
583 cl::desc("Show expanded source regions"),
584 cl::cat(ViewCategory));
585
586 cl::opt ShowInstantiations("show-instantiations", cl::Optional,
587 cl::desc("Show function instantiations"),
588 cl::cat(ViewCategory));
589
590 cl::opt NoColors("no-colors", cl::Optional,
591 cl::desc("Don't show text colors"), cl::init(false),
592 cl::cat(ViewCategory));
593
594 auto Err = commandLineParser(argc, argv);
595 if (Err)
596 return Err;
597
598 ViewOpts.Colors = !NoColors;
599 ViewOpts.ShowLineNumbers = true;
600 ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
601 !ShowRegions || ShowBestLineRegionsCounts;
602 ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
603 ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts;
604 ViewOpts.ShowExpandedRegions = ShowExpansions;
605 ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
606
607 if (load())
608 return 1;
609
610 if (!Filters.empty()) {
611 // Show functions
612 for (const auto &Function : FunctionMappingRecords) {
613 unsigned MainFileID;
614 if (findMainViewFileID(Function, MainFileID))
615 continue;
616 StringRef SourceFile = Function.Filenames[MainFileID];
617 std::unique_ptr mainView;
618 auto SourceBuffer = getSourceFile(SourceFile);
619 if (!SourceBuffer)
620 return 1;
621 auto Range = findExpandedFileInterestingLineRange(MainFileID, Function);
622 mainView.reset(new SourceCoverageView(SourceBuffer.get(), ViewOpts,
623 Range.first, Range.second));
624 createSourceFileView(SourceFile, *mainView, Function, true);
625 ViewOpts.colored_ostream(outs(), raw_ostream::CYAN)
626 << Function.PrettyName << " from " << SourceFile << ":";
627 outs() << "\n";
628 mainView->render(outs());
629 if (FunctionMappingRecords.size() > 1)
630 outs() << "\n";
631 }
632 return 0;
633 }
634
635 // Show files
636 bool ShowFilenames = SourceFiles.size() != 1;
637
638 if (SourceFiles.empty()) {
639 // Get the source files from the function coverage mapping
640 std::set UniqueFilenames;
641 for (const auto &Function : FunctionMappingRecords) {
642 for (const auto &Filename : Function.Filenames)
643 UniqueFilenames.insert(Filename);
644 }
645 for (const auto &Filename : UniqueFilenames)
646 SourceFiles.push_back(Filename);
647 }
648
649 for (const auto &SourceFile : SourceFiles) {
650 std::unique_ptr mainView;
651 auto SourceBuffer = getSourceFile(SourceFile);
652 if (!SourceBuffer)
653 return 1;
654 mainView.reset(new SourceCoverageView(SourceBuffer.get(), ViewOpts));
655 if (createSourceFileView(SourceFile, *mainView, FunctionMappingRecords)) {
656 ViewOpts.colored_ostream(outs(), raw_ostream::RED)
657 << "warning: The file '" << SourceFile << "' isn't covered.";
658 outs() << "\n";
659 continue;
660 }
661
662 if (ShowFilenames) {
663 ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile << ":";
664 outs() << "\n";
665 }
666 mainView->render(outs());
667 if (SourceFiles.size() > 1)
668 outs() << "\n";
669 }
670
671 return 0;
672 }
673
674 int CodeCoverageTool::report(int argc, const char **argv,
675 CommandLineParserType commandLineParser) {
676 cl::opt NoColors("no-colors", cl::Optional,
677 cl::desc("Don't show text colors"), cl::init(false));
678
679 auto Err = commandLineParser(argc, argv);
680 if (Err)
681 return Err;
682
683 ViewOpts.Colors = !NoColors;
684
685 if (load())
686 return 1;
687
688 CoverageSummary Summarizer;
689 Summarizer.createSummaries(FunctionMappingRecords);
690 CoverageReport Report(ViewOpts, Summarizer);
691 if (SourceFiles.empty() && Filters.empty()) {
692 Report.renderFileReports(llvm::outs());
693 return 0;
694 }
695
696 Report.renderFunctionReports(llvm::outs());
697 return 0;
698 }
699
700 int show_main(int argc, const char **argv) {
701 CodeCoverageTool Tool;
702 return Tool.run(CodeCoverageTool::Show, argc, argv);
703 }
704
705 int report_main(int argc, const char **argv) {
706 CodeCoverageTool Tool;
707 return Tool.run(CodeCoverageTool::Report, argc, argv);
708 }
0 //===- CoverageFilters.cpp - Function coverage mapping filters ------------===//
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 // These classes provide filtering for function coverage mapping records.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "CoverageFilters.h"
14 #include "CoverageSummaryInfo.h"
15 #include "llvm/Support/Regex.h"
16
17 using namespace llvm;
18
19 bool NameCoverageFilter::matches(const FunctionCoverageMapping &Function) {
20 StringRef FuncName = Function.PrettyName;
21 return FuncName.find(Name) != StringRef::npos;
22 }
23
24 bool NameRegexCoverageFilter::matches(const FunctionCoverageMapping &Function) {
25 return llvm::Regex(Regex).match(Function.PrettyName);
26 }
27
28 bool RegionCoverageFilter::matches(const FunctionCoverageMapping &Function) {
29 return PassesThreshold(FunctionCoverageSummary::get(Function)
30 .RegionCoverage.getPercentCovered());
31 }
32
33 bool LineCoverageFilter::matches(const FunctionCoverageMapping &Function) {
34 return PassesThreshold(
35 FunctionCoverageSummary::get(Function).LineCoverage.getPercentCovered());
36 }
37
38 void CoverageFilters::push_back(std::unique_ptr Filter) {
39 Filters.push_back(std::move(Filter));
40 }
41
42 bool CoverageFilters::matches(const FunctionCoverageMapping &Function) {
43 for (const auto &Filter : Filters) {
44 if (Filter->matches(Function))
45 return true;
46 }
47 return false;
48 }
49
50 bool CoverageFiltersMatchAll::matches(const FunctionCoverageMapping &Function) {
51 for (const auto &Filter : Filters) {
52 if (!Filter->matches(Function))
53 return false;
54 }
55 return true;
56 }
0 //===- CoverageFilters.h - Function coverage mapping filters --------------===//
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 // These classes provide filtering for function coverage mapping records.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #ifndef LLVM_COV_COVERAGEFILTERS_H
14 #define LLVM_COV_COVERAGEFILTERS_H
15
16 #include "FunctionCoverageMapping.h"
17 #include
18 #include
19
20 namespace llvm {
21
22 /// \brief Matches specific functions that pass the requirement of this filter.
23 class CoverageFilter {
24 public:
25 virtual ~CoverageFilter() {}
26
27 /// \brief Return true if the function passes the requirements of this filter.
28 virtual bool matches(const FunctionCoverageMapping &Function) { return true; }
29 };
30
31 /// \brief Matches functions that contain a specific string in their name.
32 class NameCoverageFilter : public CoverageFilter {
33 StringRef Name;
34
35 public:
36 NameCoverageFilter(StringRef Name) : Name(Name) {}
37
38 bool matches(const FunctionCoverageMapping &Function) override;
39 };
40
41 /// \brief Matches functions whose name matches a certain regular expression.
42 class NameRegexCoverageFilter : public CoverageFilter {
43 StringRef Regex;
44
45 public:
46 NameRegexCoverageFilter(StringRef Regex) : Regex(Regex) {}
47
48 bool matches(const FunctionCoverageMapping &Function) override;
49 };
50
51 /// \brief Matches numbers that pass a certain threshold.
52 template class StatisticThresholdFilter {
53 public:
54 enum Operation { LessThan, GreaterThan };
55
56 protected:
57 Operation Op;
58 T Threshold;
59
60 StatisticThresholdFilter(Operation Op, T Threshold)
61 : Op(Op), Threshold(Threshold) {}
62
63 /// \brief Return true if the given number is less than
64 /// or greater than the certain threshold.
65 bool PassesThreshold(T Value) const {
66 switch (Op) {
67 case LessThan:
68 return Value < Threshold;
69 case GreaterThan:
70 return Value > Threshold;
71 }
72 return false;
73 }
74 };
75
76 /// \brief Matches functions whose region coverage percentage
77 /// is above/below a certain percentage.
78 class RegionCoverageFilter : public CoverageFilter,
79 public StatisticThresholdFilter {
80 public:
81 RegionCoverageFilter(Operation Op, double Threshold)
82 : StatisticThresholdFilter(Op, Threshold) {}
83
84 bool matches(const FunctionCoverageMapping &Function) override;
85 };
86
87 /// \brief Matches functions whose line coverage percentage
88 /// is above/below a certain percentage.
89 class LineCoverageFilter : public CoverageFilter,
90 public StatisticThresholdFilter {
91 public:
92 LineCoverageFilter(Operation Op, double Threshold)
93 : StatisticThresholdFilter(Op, Threshold) {}
94
95 bool matches(const FunctionCoverageMapping &Function) override;
96 };
97
98 /// \brief A collection of filters.
99 /// Matches functions that match any filters contained
100 /// in an instance of this class.
101 class CoverageFilters : public CoverageFilter {
102 protected:
103 std::vector> Filters;
104
105 public:
106 /// \brief Append a filter to this collection.
107 void push_back(std::unique_ptr Filter);
108
109 bool empty() const { return Filters.empty(); }
110
111 bool matches(const FunctionCoverageMapping &Function) override;
112 };
113
114 /// \brief A collection of filters.
115 /// Matches functions that match all of the filters contained
116 /// in an instance of this class.
117 class CoverageFiltersMatchAll : public CoverageFilters {
118 public:
119 bool matches(const FunctionCoverageMapping &Function) override;
120 };
121
122 } // namespace llvm
123
124 #endif // LLVM_COV_COVERAGEFILTERS_H
0 //===- CoverageReport.cpp - Code coverage report -------------------------===//
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 // This class implements rendering of a code coverage report.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "CoverageReport.h"
14 #include "CoverageSummary.h"
15 #include "RenderingSupport.h"
16 #include "llvm/Support/Format.h"
17 #include "llvm/Support/FileSystem.h"
18
19 using namespace llvm;
20 namespace {
21 /// \brief Helper struct which prints trimmed and aligned columns.
22 struct Column {
23 enum TrimKind { NoTrim, LeftTrim, RightTrim };
24
25 enum AlignmentKind { LeftAlignment, RightAlignment };
26
27 StringRef Str;
28 unsigned Width;
29 TrimKind Trim;
30 AlignmentKind Alignment;
31
32 Column(StringRef Str, unsigned Width)
33 : Str(Str), Width(Width), Trim(NoTrim), Alignment(LeftAlignment) {}
34
35 Column &set(TrimKind Value) {
36 Trim = Value;
37 return *this;
38 }
39
40 Column &set(AlignmentKind Value) {
41 Alignment = Value;
42 return *this;
43 }
44
45 void render(raw_ostream &OS) const;
46 };
47 raw_ostream &operator<<(raw_ostream &OS, const Column &Value) {
48 Value.render(OS);
49 return OS;
50 }
51 }
52
53 void Column::render(raw_ostream &OS) const {
54 if (Str.size() <= Width) {
55 if (Alignment == RightAlignment) {
56 OS.indent(Width - Str.size());
57 OS << Str;
58 return;
59 }
60 OS << Str;
61 OS.indent(Width - Str.size());
62 return;
63 }
64
65 switch (Trim) {
66 case NoTrim:
67 OS << Str.substr(0, Width);
68 break;
69 case LeftTrim:
70 OS << "..." << Str.substr(Str.size() - Width + 3);
71 break;
72 case RightTrim:
73 OS << Str.substr(0, Width - 3) << "...";
74 break;
75 }
76 }
77
78 static Column column(StringRef Str, unsigned Width) {
79 return Column(Str, Width);
80 }
81
82 template
83 static Column column(StringRef Str, unsigned Width, const T &Value) {
84 return Column(Str, Width).set(Value);
85 }
86
87 static const unsigned FileReportColumns[] = {25, 10, 8, 8, 10, 8};
88 static const unsigned FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8};
89
90 /// \brief Prints a horizontal divider which spans across the given columns.
91 template
92 static void renderDivider(T (&Columns)[N], raw_ostream &OS) {
93 unsigned Length = 0;
94 for (unsigned I = 0; I < N; ++I)
95 Length += Columns[I];
96 for (unsigned I = 0; I < Length; ++I)
97 OS << '-';
98 }
99
100 /// \brief Return the color which correponds to the coverage
101 /// percentage of a certain metric.
102 template
103 static raw_ostream::Colors determineCoveragePercentageColor(const T &Info) {
104 if (Info.isFullyCovered())
105 return raw_ostream::GREEN;
106 return Info.getPercentCovered() >= 80.0 ? raw_ostream::YELLOW
107 : raw_ostream::RED;
108 }
109
110 void CoverageReport::render(const FileCoverageSummary &File, raw_ostream &OS) {
111 OS << column(File.Name, FileReportColumns[0], Column::LeftTrim)
112 << format("%*zd", FileReportColumns[1], File.RegionCoverage.NumRegions);
113 Options.colored_ostream(OS, File.RegionCoverage.isFullyCovered()
114 ? raw_ostream::GREEN
115 : raw_ostream::RED)
116 << format("%*zd", FileReportColumns[2], File.RegionCoverage.NotCovered);
117 Options.colored_ostream(OS,
118 determineCoveragePercentageColor(File.RegionCoverage))
119 << format("%*.2f", FileReportColumns[3] - 1,
120 File.RegionCoverage.getPercentCovered()) << '%';
121 OS << format("%*zd", FileReportColumns[4],
122 File.FunctionCoverage.NumFunctions);
123 Options.colored_ostream(
124 OS, determineCoveragePercentageColor(File.FunctionCoverage))
125 << format("%*.2f", FileReportColumns[5] - 1,
126 File.FunctionCoverage.getPercentCovered()) << '%';
127 OS << "\n";
128 }
129
130 void CoverageReport::render(const FunctionCoverageSummary &Function,
131 raw_ostream &OS) {
132 OS << column(Function.Name, FunctionReportColumns[0], Column::RightTrim)
133 << format("%*zd", FunctionReportColumns[1],
134 Function.RegionCoverage.NumRegions);
135 Options.colored_ostream(OS, Function.RegionCoverage.isFullyCovered()
136 ? raw_ostream::GREEN
137 : raw_ostream::RED)
138 << format("%*zd", FunctionReportColumns[2],
139 Function.RegionCoverage.NotCovered);
140 Options.colored_ostream(
141 OS, determineCoveragePercentageColor(Function.RegionCoverage))
142 << format("%*.2f", FunctionReportColumns[3] - 1,
143 Function.RegionCoverage.getPercentCovered()) << '%';
144 OS << format("%*zd", FunctionReportColumns[4],
145 Function.LineCoverage.NumLines);
146 Options.colored_ostream(OS, Function.LineCoverage.isFullyCovered()
147 ? raw_ostream::GREEN
148 : raw_ostream::RED)
149 << format("%*zd", FunctionReportColumns[5],
150 Function.LineCoverage.NotCovered);
151 Options.colored_ostream(
152 OS, determineCoveragePercentageColor(Function.LineCoverage))
153 << format("%*.2f", FunctionReportColumns[6] - 1,
154 Function.LineCoverage.getPercentCovered()) << '%';
155 OS << "\n";
156 }
157
158 void CoverageReport::renderFunctionReports(raw_ostream &OS) {
159 bool isFirst = true;
160 for (const auto &File : Summary.getFileSummaries()) {
161 if (isFirst)
162 isFirst = false;
163 else
164 OS << "\n";
165 OS << "File '" << File.Name << "':\n";
166 OS << column("Name", FunctionReportColumns[0])
167 << column("Regions", FunctionReportColumns[1], Column::RightAlignment)
168 << column("Miss", FunctionReportColumns[2], Column::RightAlignment)
169 << column("Cover", FunctionReportColumns[3], Column::RightAlignment)
170 << column("Lines", FunctionReportColumns[4], Column::RightAlignment)
171 << column("Miss", FunctionReportColumns[5], Column::RightAlignment)
172 << column("Cover", FunctionReportColumns[6], Column::RightAlignment);
173 OS << "\n";
174 renderDivider(FunctionReportColumns, OS);
175 OS << "\n";
176 for (const auto &Function : File.FunctionSummaries)
177 render(Function, OS);
178 renderDivider(FunctionReportColumns, OS);
179 OS << "\n";
180 render(FunctionCoverageSummary("TOTAL", File.RegionCoverage,
181 File.LineCoverage),
182 OS);
183 }
184 }
185
186 void CoverageReport::renderFileReports(raw_ostream &OS) {
187 OS << column("Filename", FileReportColumns[0])
188 << column("Regions", FileReportColumns[1], Column::RightAlignment)
189 << column("Miss", FileReportColumns[2], Column::RightAlignment)
190 << column("Cover", FileReportColumns[3], Column::RightAlignment)
191 << column("Functions", FileReportColumns[4], Column::RightAlignment)
192 << column("Cover", FileReportColumns[5], Column::RightAlignment) << "\n";
193 renderDivider(FileReportColumns, OS);
194 OS << "\n";
195 for (const auto &File : Summary.getFileSummaries())
196 render(File, OS);
197 renderDivider(FileReportColumns, OS);
198 OS << "\n";
199 render(Summary.getCombinedFileSummaries(), OS);
200 }
0 //===- CoverageReport.h - Code coverage report ---------------------------===//
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 // This class implements rendering of a code coverage report.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #ifndef LLVM_COV_COVERAGEREPORT_H
14 #define LLVM_COV_COVERAGEREPORT_H
15
16 #include "CoverageViewOptions.h"
17 #include "CoverageSummary.h"
18
19 namespace llvm {
20
21 /// \brief Displays the code coverage report.
22 class CoverageReport {
23 const CoverageViewOptions &Options;
24 CoverageSummary &Summary;
25
26 void render(const FileCoverageSummary &File, raw_ostream &OS);
27 void render(const FunctionCoverageSummary &Function, raw_ostream &OS);
28
29 public:
30 CoverageReport(const CoverageViewOptions &Options, CoverageSummary &Summary)
31 : Options(Options), Summary(Summary) {}
32
33 void renderFunctionReports(raw_ostream &OS);
34
35 void renderFileReports(raw_ostream &OS);
36 };
37 }
38
39 #endif // LLVM_COV_COVERAGEREPORT_H
0 //===- CoverageSummary.cpp - Code coverage summary ------------------------===//
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 // This class implements data management and rendering for the code coverage
10 // summaries of all files and functions.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "CoverageSummary.h"
15 #include "llvm/Support/FileSystem.h"
16 #include "llvm/Support/Format.h"
17
18 using namespace llvm;
19
20 unsigned CoverageSummary::getFileID(StringRef Filename) {
21 for (unsigned I = 0, E = Filenames.size(); I < E; ++I) {
22 if (sys::fs::equivalent(Filenames[I], Filename))
23 return I;
24 }
25 Filenames.push_back(Filename);
26 return Filenames.size() - 1;
27 }
28
29 void
30 CoverageSummary::createSummaries(ArrayRef Functions) {
31 std::vector> FunctionFileIDs;
32
33 FunctionFileIDs.resize(Functions.size());
34 for (size_t I = 0, E = Functions.size(); I < E; ++I) {
35 StringRef Filename = Functions[I].Filenames[0];
36 FunctionFileIDs[I] = std::make_pair(getFileID(Filename), I);
37 }
38
39 // Sort the function records by file ids
40 std::sort(FunctionFileIDs.begin(), FunctionFileIDs.end(),
41 [](const std::pair &lhs,
42 const std::pair &rhs) {
43 return lhs.first < rhs.first;
44 });
45
46 // Create function summaries in a sorted order (by file ids)
47 FunctionSummaries.reserve(Functions.size());
48 for (size_t I = 0, E = Functions.size(); I < E; ++I)
49 FunctionSummaries.push_back(
50 FunctionCoverageSummary::get(Functions[FunctionFileIDs[I].second]));
51
52 // Create file summaries
53 size_t CurrentSummary = 0;
54 for (unsigned FileID = 0; FileID < Filenames.size(); ++FileID) {
55 // Gather the relevant functions summaries
56 auto PrevSummary = CurrentSummary;
57 while (CurrentSummary < FunctionSummaries.size() &&
58 FunctionFileIDs[CurrentSummary].first == FileID)
59 ++CurrentSummary;
60 ArrayRef LocalSummaries(
61 FunctionSummaries.data() + PrevSummary,
62 FunctionSummaries.data() + CurrentSummary);
63 if (LocalSummaries.empty())
64 continue;
65
66 FileSummaries.push_back(
67 FileCoverageSummary::get(Filenames[FileID], LocalSummaries));
68 }
69 }
70
71 FileCoverageSummary CoverageSummary::getCombinedFileSummaries() {
72 size_t NumRegions = 0, CoveredRegions = 0;
73 size_t NumLines = 0, NonCodeLines = 0, CoveredLines = 0;
74 size_t NumFunctionsCovered = 0, NumFunctions = 0;
75 for (const auto &File : FileSummaries) {
76 NumRegions += File.RegionCoverage.NumRegions;
77 CoveredRegions += File.RegionCoverage.Covered;
78
79 NumLines += File.LineCoverage.NumLines;
80 NonCodeLines += File.LineCoverage.NonCodeLines;
81 CoveredLines += File.LineCoverage.Covered;
82
83 NumFunctionsCovered += File.FunctionCoverage.FullyCovered;
84 NumFunctions += File.FunctionCoverage.NumFunctions;
85 }
86 return FileCoverageSummary(
87 "TOTAL", RegionCoverageInfo(CoveredRegions, NumRegions),
88 LineCoverageInfo(CoveredLines, NonCodeLines, NumLines),
89 FunctionCoverageInfo(NumFunctionsCovered, NumFunctions),
90 ArrayRef());
91 }
0 //===- CoverageSummary.h - Code coverage summary --------------------------===//
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 // This class implements data management and rendering for the code coverage
10 // summaries of all files and functions.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #ifndef LLVM_COV_COVERAGESUMMARY_H
15 #define LLVM_COV_COVERAGESUMMARY_H
16
17 #include "CoverageSummaryInfo.h"
18 #include
19
20 namespace llvm {
21
22 /// \brief Manager for the function and file code coverage summaries.
23 class CoverageSummary {
24 std::vector Filenames;
25 std::vector FunctionSummaries;
26 std::vector> FunctionSummariesFileIDs;
27 std::vector FileSummaries;
28
29 unsigned getFileID(StringRef Filename);
30
31 public:
32 void createSummaries(ArrayRef Functions);
33
34 ArrayRef getFileSummaries() { return FileSummaries; }
35
36 FileCoverageSummary getCombinedFileSummaries();
37
38 void render(const FunctionCoverageSummary &Summary, raw_ostream &OS);
39
40 void render(raw_ostream &OS);
41 };
42 }
43
44 #endif // LLVM_COV_COVERAGESUMMARY_H
0 //===- CoverageSummaryInfo.cpp - Coverage summary for function/file -------===//
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 // These structures are used to represent code coverage metrics
10 // for functions/files.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "CoverageSummaryInfo.h"
15
16 using namespace llvm;
17 using namespace coverage;
18
19 FunctionCoverageSummary
20 FunctionCoverageSummary::get(const FunctionCoverageMapping &Function) {
21 // Compute the region coverage
22 size_t NumCodeRegions = 0, CoveredRegions = 0;
23 for (auto &Region : Function.MappingRegions) {
24 if (Region.Kind != CounterMappingRegion::CodeRegion)
25 continue;
26 ++NumCodeRegions;
27 if (Region.ExecutionCount != 0)
28 ++CoveredRegions;
29 }
30
31 // Compute the line coverage
32 size_t NumLines = 0, CoveredLines = 0;
33 for (unsigned FileID = 0, E = Function.Filenames.size(); FileID < E;
34 ++FileID) {
35 // Find the line start and end of the function's source code
36 // in that particular file
37 unsigned LineStart = std::numeric_limits::max();
38 unsigned LineEnd = 0;
39 for (auto &Region : Function.MappingRegions) {
40 if (Region.FileID != FileID)
41 continue;
42 LineStart = std::min(LineStart, Region.LineStart);
43 LineEnd = std::max(LineEnd, Region.LineEnd);
44 }
45 unsigned LineCount = LineEnd - LineStart + 1;
46
47 // Get counters
48 llvm::SmallVector ExecutionCounts;
49 ExecutionCounts.resize(LineCount, 0);
50 for (auto &Region : Function.MappingRegions) {
51 if (Region.FileID != FileID)
52 continue;
53 // Ignore the lines that were skipped by the preprocessor.
54 auto ExecutionCount = Region.ExecutionCount;
55 if (Region.Kind == MappingRegion::SkippedRegion) {
56 LineCount -= Region.LineEnd - Region.LineStart + 1;
57 ExecutionCount = 1;
58 }
59 for (unsigned I = Region.LineStart; I <= Region.LineEnd; ++I)
60 ExecutionCounts[I - LineStart] = ExecutionCount;
61 }
62 CoveredLines += LineCount - std::count(ExecutionCounts.begin(),
63 ExecutionCounts.end(), 0);
64 NumLines += LineCount;
65 }
66 return FunctionCoverageSummary(
67 Function.PrettyName, RegionCoverageInfo(CoveredRegions, NumCodeRegions),
68 LineCoverageInfo(CoveredLines, 0, NumLines));
69 }
70
71 FileCoverageSummary
72 FileCoverageSummary::get(StringRef Name,
73 ArrayRef FunctionSummaries) {
74 size_t NumRegions = 0, CoveredRegions = 0;
75 size_t NumLines = 0, NonCodeLines = 0, CoveredLines = 0;
76 size_t NumFunctionsCovered = 0;
77 for (const auto &Func : FunctionSummaries) {
78 CoveredRegions += Func.RegionCoverage.Covered;
79 NumRegions += Func.RegionCoverage.NumRegions;
80
81 CoveredLines += Func.LineCoverage.Covered;
82 NonCodeLines += Func.LineCoverage.NonCodeLines;
83 NumLines += Func.LineCoverage.NumLines;
84
85 if (Func.RegionCoverage.isFullyCovered())
86 ++NumFunctionsCovered;
87 }
88
89 return FileCoverageSummary(
90 Name, RegionCoverageInfo(CoveredRegions, NumRegions),
91 LineCoverageInfo(CoveredLines, NonCodeLines, NumLines),
92 FunctionCoverageInfo(NumFunctionsCovered, FunctionSummaries.size()),
93 FunctionSummaries);
94 }
0 //===- CoverageSummaryInfo.h - Coverage summary for function/file ---------===//
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 // These structures are used to represent code coverage metrics
10 // for functions/files.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #ifndef LLVM_COV_COVERAGESUMMARYINFO_H
15 #define LLVM_COV_COVERAGESUMMARYINFO_H
16
17 #include "FunctionCoverageMapping.h"
18 #include "llvm/Support/raw_ostream.h"
19
20 namespace llvm {
21
22 /// \brief Provides information about region coverage for a function/file.
23 struct RegionCoverageInfo {
24 /// \brief The number of regions that were executed at least once.
25 size_t Covered;
26
27 /// \brief The number of regions that weren't executed.
28 size_t NotCovered;
29
30 /// \brief The total number of regions in a function/file.
31 size_t NumRegions;
32
33 RegionCoverageInfo(size_t Covered, size_t NumRegions)
34 : Covered(Covered), NotCovered(NumRegions - Covered),
35 NumRegions(NumRegions) {}
36
37 bool isFullyCovered() const { return Covered == NumRegions; }
38
39 double getPercentCovered() const {
40 return double(Covered) / double(NumRegions) * 100.0;
41 }
42 };
43
44 /// \brief Provides information about line coverage for a function/file.
45 struct LineCoverageInfo {
46 /// \brief The number of lines that were executed at least once.
47 size_t Covered;
48
49 /// \brief The number of lines that weren't executed.
50 size_t NotCovered;
51
52 /// \brief The number of lines that aren't code.
53 size_t NonCodeLines;
54
55 /// \brief The total number of lines in a function/file.
56 size_t NumLines;
57
58 LineCoverageInfo(size_t Covered, size_t NumNonCodeLines, size_t NumLines)
59 : Covered(Covered), NotCovered(NumLines - NumNonCodeLines - Covered),
60 NonCodeLines(NumNonCodeLines), NumLines(NumLines) {}
61
62 bool isFullyCovered() const { return Covered == (NumLines - NonCodeLines); }
63
64 double getPercentCovered() const {
65 return double(Covered) / double(NumLines - NonCodeLines) * 100.0;
66 }
67 };
68
69 /// \brief Provides information about function coverage for a file.
70 struct FunctionCoverageInfo {
71 /// \brief The number of functions that have full
72 /// region coverage.
73 size_t FullyCovered;
74
75 /// \brief The total number of functions in this file.
76 size_t NumFunctions;
77
78 FunctionCoverageInfo(size_t FullyCovered, size_t NumFunctions)
79 : FullyCovered(FullyCovered), NumFunctions(NumFunctions) {}
80
81 bool isFullyCovered() const { return FullyCovered == NumFunctions; }
82
83 double getPercentCovered() const {
84 return double(FullyCovered) / double(NumFunctions) * 100.0;
85 }
86 };
87
88 /// \brief A summary of function's code coverage.
89 struct FunctionCoverageSummary {
90 StringRef Name;
91 RegionCoverageInfo RegionCoverage;
92 LineCoverageInfo LineCoverage;
93
94 FunctionCoverageSummary(StringRef Name,
95 const RegionCoverageInfo &RegionCoverage,
96 const LineCoverageInfo &LineCoverage)
97 : Name(Name), RegionCoverage(RegionCoverage), LineCoverage(LineCoverage) {
98 }
99
100 /// \brief Compute the code coverage summary for the given function coverage
101 /// mapping record.
102 static FunctionCoverageSummary get(const FunctionCoverageMapping &Function);
103 };
104
105 /// \brief A summary of file's code coverage.
106 struct FileCoverageSummary {
107 StringRef Name;
108 RegionCoverageInfo RegionCoverage;
109 LineCoverageInfo LineCoverage;
110 FunctionCoverageInfo FunctionCoverage;
111 /// \brief The summary of every function
112 /// in this file.
113 ArrayRef FunctionSummaries;
114
115 FileCoverageSummary(StringRef Name, const RegionCoverageInfo &RegionCoverage,
116 const LineCoverageInfo &LineCoverage,
117 const FunctionCoverageInfo &FunctionCoverage,
118 ArrayRef FunctionSummaries)
119 : Name(Name), RegionCoverage(RegionCoverage), LineCoverage(LineCoverage),
120 FunctionCoverage(FunctionCoverage),
121 FunctionSummaries(FunctionSummaries) {}
122
123 /// \brief Compute the code coverage summary for a file.
124 static FileCoverageSummary
125 get(StringRef Name, ArrayRef FunctionSummaries);
126 };
127
128 } // namespace llvm
129
130 #endif // LLVM_COV_COVERAGESUMMARYINFO_H
0 //===- CoverageViewOptions.h - Code coverage display options -------------===//
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 #ifndef LLVM_COV_COVERAGEVIEWOPTIONS_H
10 #define LLVM_COV_COVERAGEVIEWOPTIONS_H
11
12 #include "RenderingSupport.h"
13
14 namespace llvm {
15
16 /// \brief The options for displaying the code coverage information.
17 struct CoverageViewOptions {
18 bool Debug;
19 bool Colors;
20 bool ShowLineNumbers;
21 bool ShowLineStats;
22 bool ShowRegionMarkers;
23 bool ShowLineStatsOrRegionMarkers;
24 bool ShowExpandedRegions;
25 bool ShowFunctionInstantiations;
26
27 /// \brief Change the output's stream color if the colors are enabled.
28 ColoredRawOstream colored_ostream(raw_ostream &OS,
29 raw_ostream::Colors Color) const {
30 return llvm::colored_ostream(OS, Color, Colors);
31 }
32 };
33 }
34
35 #endif // LLVM_COV_COVERAGEVIEWOPTIONS_H
0 //===- FunctionCoverageMapping.h - Function coverage mapping record -------===//
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 // A structure that stores the coverage mapping record for a single function.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #ifndef LLVM_COV_FUNCTIONCOVERAGEMAPPING_H
14 #define LLVM_COV_FUNCTIONCOVERAGEMAPPING_H
15
16 #include
17 #include
18 #include "llvm/ADT/ArrayRef.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/ProfileData/CoverageMapping.h"
21
22 namespace llvm {
23
24 /// \brief Associates a source range with an execution count.
25 struct MappingRegion : public coverage::CounterMappingRegion {
26 uint64_t ExecutionCount;
27
28 MappingRegion(const CounterMappingRegion &R, uint64_t ExecutionCount)
29 : CounterMappingRegion(R), ExecutionCount(ExecutionCount) {}
30 };
31
32 /// \brief Stores all the required information
33 /// about code coverage for a single function.
34 struct FunctionCoverageMapping {
35 /// \brief Raw function name.
36 std::string Name;
37 /// \brief Demangled function name.
38 std::string PrettyName;
39 std::vector Filenames;
40 std::vector MappingRegions;
41
42 FunctionCoverageMapping(StringRef Name, ArrayRef Filenames)
43 : Name(Name), PrettyName(Name),
44 Filenames(Filenames.begin(), Filenames.end()) {}
45 };
46
47 } // namespace llvm
48
49 #endif // LLVM_COV_FUNCTIONCOVERAGEMAPPING_H
1818 type = Tool
1919 name = llvm-cov
2020 parent = Tools
21 required_libraries = Instrumentation
21 required_libraries = ProfileData Support Instrumentation
88
99 LEVEL := ../..
1010 TOOLNAME := llvm-cov
11 LINK_COMPONENTS := core support
11 LINK_COMPONENTS := core support profiledata object
1212
1313 # This tool has no plugins, optimize startup time.
1414 TOOL_NO_EXPORTS := 1
0 //===- RenderingSupport.h - output stream rendering support functions ----===//
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 #ifndef LLVM_COV_RENDERINGSUPPORT_H
10 #define LLVM_COV_RENDERINGSUPPORT_H
11
12 #include "llvm/Support/raw_ostream.h"
13 #include
14
15 namespace llvm {
16
17 /// \brief A helper class that resets the output stream's color if needed
18 /// when destroyed.
19 class ColoredRawOstream {
20 ColoredRawOstream(const ColoredRawOstream &OS) LLVM_DELETED_FUNCTION;
21
22 public:
23 raw_ostream &OS;
24 bool IsColorUsed;
25
26 ColoredRawOstream(raw_ostream &OS, bool IsColorUsed)
27 : OS(OS), IsColorUsed(IsColorUsed) {}
28
29 ColoredRawOstream(ColoredRawOstream &&Other)
30 : OS(Other.OS), IsColorUsed(Other.IsColorUsed) {
31 // Reset the other IsColorUsed so that the other object won't reset the
32 // color when destroyed.
33 Other.IsColorUsed = false;
34 }
35
36 ~ColoredRawOstream() {
37 if (IsColorUsed)
38 OS.resetColor();
39 }
40 };
41
42 template
43 inline raw_ostream &operator<<(const ColoredRawOstream &OS, T &&Value) {
44 return OS.OS << std::forward(Value);
45 }
46
47 /// \brief Change the color of the output stream if the `IsColorUsed` flag
48 /// is true. Returns an object that resets the color when destroyed.
49 inline ColoredRawOstream colored_ostream(raw_ostream &OS,
50 raw_ostream::Colors Color,
51 bool IsColorUsed = false) {
52 if (IsColorUsed)
53 OS.changeColor(Color);
54 return ColoredRawOstream(OS, IsColorUsed);
55 }
56 }
57
58 #endif // LLVM_COV_RENDERINGSUPPORT_H
0 //===- SourceCoverageDataManager.cpp - Manager for source file coverage
1 // data-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This class separates and merges mapping regions for a specific source file.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "SourceCoverageDataManager.h"
15
16 using namespace llvm;
17 using namespace coverage;
18
19 void SourceCoverageDataManager::insert(const MappingRegion &Region) {
20 SourceRange Range(Region.LineStart, Region.ColumnStart, Region.LineEnd,
21 Region.ColumnEnd);
22 if (Region.Kind == CounterMappingRegion::SkippedRegion) {
23 SkippedRegions.push_back(Range);
24 return;
25 }
26 Regions.push_back(std::make_pair(Range, Region.ExecutionCount));
27 }
28
29 ArrayRef>
30 SourceCoverageDataManager::getSourceRegions() {
31 if (Uniqued || Regions.size() <= 1)
32 return Regions;
33
34 // Sort.
35 std::sort(Regions.begin(), Regions.end(),
36 [](const std::pair &LHS,
37 const std::pair &RHS) {
38 return LHS.first < RHS.first;
39 });
40
41 // Merge duplicate source ranges and sum their execution counts.
42 auto Prev = Regions.begin();
43 for (auto I = Prev + 1, E = Regions.end(); I != E; ++I) {
44 if (I->first == Prev->first) {
45 Prev->second += I->second;
46 continue;
47 }
48 ++Prev;
49 *Prev = *I;
50 }
51 ++Prev;
52 Regions.erase(Prev, Regions.end());
53
54 Uniqued = true;
55 return Regions;
56 }
0 //===- SourceCoverageDataManager.h - Manager for source file coverage data-===//
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 // This class separates and merges mapping regions for a specific source file.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #ifndef LLVM_COV_SOURCECOVERAGEDATAMANAGER_H
14 #define LLVM_COV_SOURCECOVERAGEDATAMANAGER_H
15
16 #include "FunctionCoverageMapping.h"
17 #include "llvm/ProfileData/CoverageMapping.h"
18 #include "llvm/ADT/Hashing.h"
19 #include
20 #include
21
22 namespace llvm {
23
24 /// \brief Partions mapping regions by their kind and sums
25 /// the execution counts of the regions that start at the same location.
26 class SourceCoverageDataManager {
27 public:
28 struct SourceRange {
29 unsigned LineStart, ColumnStart, LineEnd, ColumnEnd;
30
31 SourceRange(unsigned LineStart, unsigned ColumnStart, unsigned LineEnd,
32 unsigned ColumnEnd)
33 : LineStart(LineStart), ColumnStart(ColumnStart), LineEnd(LineEnd),
34 ColumnEnd(ColumnEnd) {}
35
36 bool operator==(const SourceRange &Other) const {
37 return LineStart == Other.LineStart && ColumnStart == Other.ColumnStart &&
38 LineEnd == Other.LineEnd && ColumnEnd == Other.ColumnEnd;
39 }
40
41 bool operator<(const SourceRange &Other) const {
42 if (LineStart == Other.LineStart)
43 return ColumnStart < Other.ColumnStart;
44 return LineStart < Other.LineStart;
45 }
46
47 bool contains(const SourceRange &Other) {
48 if (LineStart > Other.LineStart ||
49 (LineStart == Other.LineStart && ColumnStart > Other.ColumnStart))
50 return false;
51 if (LineEnd < Other.LineEnd ||
52 (LineEnd == Other.LineEnd && ColumnEnd < Other.ColumnEnd))
53 return false;
54 return true;
55 }
56 };
57
58 protected:
59 std::vector> Regions;
60 std::vector SkippedRegions;
61 bool Uniqued;
62
63 public:
64 SourceCoverageDataManager() : Uniqued(false) {}
65
66 void insert(const MappingRegion &Region);
67
68 /// \brief Return the source ranges and execution counts
69 /// obtained from the non-skipped mapping regions.
70 ArrayRef> getSourceRegions();
71
72 /// \brief Return the source ranges obtained from the skipped mapping regions.
73 ArrayRef getSkippedRegions() const { return SkippedRegions; }
74 };
75
76 } // namespace llvm
77
78 #endif // LLVM_COV_SOURCECOVERAGEDATAMANAGER_H
0 //===- SourceCoverageView.cpp - Code coverage view for source code --------===//
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 // This class implements rendering for code coverage of source code.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "SourceCoverageView.h"
14 #include "llvm/ADT/SmallString.h"
15 #include "llvm/Support/LineIterator.h"
16
17 using namespace llvm;
18
19 void SourceCoverageView::renderLine(raw_ostream &OS, StringRef Line,
20 ArrayRef Ranges) {
21 if (Ranges.empty()) {
22 OS << Line << "\n";
23 return;
24 }
25 if (Line.empty())
26 Line = " ";
27
28 unsigned PrevColumnStart = 0;
29 unsigned Start = 1;
30 for (const auto &Range : Ranges) {
31 if (PrevColumnStart == Range.ColumnStart)
32 continue;
33
34 // Show the unhighlighted part
35 unsigned ColumnStart = PrevColumnStart = Range.ColumnStart;
36 OS << Line.substr(Start - 1, ColumnStart - Start);
37
38 // Show the highlighted part
39 auto Color = Range.Kind == HighlightRange::NotCovered ? raw_ostream::RED
40 : raw_ostream::CYAN;
41 OS.changeColor(Color, false, true);
42 unsigned ColumnEnd = std::min(Range.ColumnEnd, (unsigned)Line.size() + 1);
43 OS << Line.substr(ColumnStart - 1, ColumnEnd - ColumnStart);
44 Start = ColumnEnd;
45 OS.resetColor();
46 }
47
48 // Show the rest of the line
49 OS << Line.substr(Start - 1, Line.size() - Start + 1);
50 OS << "\n";
51 }
52
53 void SourceCoverageView::renderOffset(raw_ostream &OS, unsigned I) {
54 for (unsigned J = 0; J < I; ++J)
55 OS << " |";
56 }
57
58 void SourceCoverageView::renderViewDivider(unsigned Offset, unsigned Length,
59 raw_ostream &OS) {
60 for (unsigned J = 1; J < Offset; ++J)
61 OS << " |";
62 if (Offset != 0)
63 OS.indent(2);
64 for (unsigned I = 0; I < Length; ++I)
65 OS << "-";
66 }
67
68 void
69 SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS,
70 const LineCoverageInfo &Line) {
71 if (!Line.isMapped()) {
72 OS.indent(LineCoverageColumnWidth) << '|';
73 return;
74 }
75 SmallString<32> Buffer;
76 raw_svector_ostream BufferOS(Buffer);
77 BufferOS << Line.ExecutionCount;
78 auto Str = BufferOS.str();
79 // Trim
80 Str = Str.substr(0, std::min(Str.size(), (size_t)LineCoverageColumnWidth));
81 // Align to the right
82 OS.indent(LineCoverageColumnWidth - Str.size());
83 colored_ostream(OS, raw_ostream::MAGENTA,
84 Line.hasMultipleRegions() && Options.Colors)
85 << Str;
86 OS << '|';
87 }
88
89 void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS,
90 unsigned LineNo) {
91 SmallString<32> Buffer;
92 raw_svector_ostream BufferOS(Buffer);
93 BufferOS << LineNo;
94 auto Str = BufferOS.str();
95 // Trim and align to the right
96 Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
97 OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
98 }
99
100 void SourceCoverageView::renderRegionMarkers(raw_ostream &OS,
101 ArrayRef Regions) {
102 SmallString<32> Buffer;
103 raw_svector_ostream BufferOS(Buffer);
104
105 unsigned PrevColumn = 1;
106 for (const auto &Region : Regions) {
107 // Skip to the new region
108 if (Region.Column > PrevColumn)
109 OS.indent(Region.Column - PrevColumn);
110 PrevColumn = Region.Column + 1;
111 BufferOS << Region.ExecutionCount;
112 StringRef Str = BufferOS.str();
113 // Trim the execution count
114 Str = Str.substr(0, std::min(Str.size(), (size_t)7));
115 PrevColumn += Str.size();
116 OS << '^' << Str;
117 Buffer.clear();
118 }
119 OS << "\n";
120 }
121
122 /// \brief Insert a new highlighting range into the line's highlighting ranges
123 /// Return line's new highlighting ranges in result.
124 static void insertHighlightRange(
125 ArrayRef Ranges,
126 SourceCoverageView::HighlightRange RangeToInsert,
127 SmallVectorImpl &Result) {
128 Result.clear();
129 size_t I = 0;
130 auto E = Ranges.size();
131 for (; I < E; ++I) {
132 if (RangeToInsert.ColumnStart < Ranges[I].ColumnEnd) {
133 const auto &Range = Ranges[I];
134 bool NextRangeContainsInserted = false;
135 // If the next range starts before the inserted range, move the end of the
136 // next range to the start of the inserted range.
137 if (Range.ColumnStart < RangeToInsert.ColumnStart) {
138 if (RangeToInsert.ColumnStart != Range.ColumnStart)
139 Result.push_back(SourceCoverageView::HighlightRange(
140 Range.Line, Range.ColumnStart, RangeToInsert.ColumnStart,
141 Range.Kind));
142 // If the next range also ends after the inserted range, keep this range
143 // and create a new range that starts at the inserted range and ends
144 // at the next range later.
145 if (Range.ColumnEnd > RangeToInsert.ColumnEnd)
146 NextRangeContainsInserted = true;
147 }
148 if (!NextRangeContainsInserted) {
149 ++I;
150 // Ignore ranges that are contained in inserted range
151 while (I < E && RangeToInsert.contains(Ranges[I]))
152 ++I;
153 }
154 break;
155 }
156 Result.push_back(Ranges[I]);
157 }
158 Result.push_back(RangeToInsert);
159 // If the next range starts before the inserted range end, move the start
160 // of the next range to the end of the inserted range.
161 if (I < E && Ranges[I].ColumnStart < RangeToInsert.ColumnEnd) {
162 const auto &Range = Ranges[I];
163 if (RangeToInsert.ColumnEnd != Range.ColumnEnd)
164 Result.push_back(SourceCoverageView::HighlightRange(
165 Range.Line, RangeToInsert.ColumnEnd, Range.ColumnEnd, Range.Kind));
166 ++I;
167 }
168 // Add the remaining ranges that are located after the inserted range
169 for (; I < E; ++I)
170 Result.push_back(Ranges[I]);
171 }
172
173 void SourceCoverageView::sortChildren() {
174 for (auto &I : Children)
175 I->sortChildren();
176 std::sort(Children.begin(), Children.end(),
177 [](const std::unique_ptr &LHS,
178 const std::unique_ptr &RHS) {
179 return LHS->ExpansionRegion < RHS->ExpansionRegion;
180 });
181 }
182
183 SourceCoverageView::HighlightRange
184 SourceCoverageView::getExpansionHighlightRange() const {
185 return HighlightRange(ExpansionRegion.LineStart, ExpansionRegion.ColumnStart,
186 ExpansionRegion.ColumnEnd, HighlightRange::Expanded);
187 }
188
189 template
190 ArrayRef gatherLineItems(size_t &CurrentIdx, const std::vector &Items,
191 unsigned LineNo) {
192 auto PrevIdx = CurrentIdx;
193 auto E = Items.size();
194 while (CurrentIdx < E && Items[CurrentIdx].Line == LineNo)
195 ++CurrentIdx;
196 return ArrayRef(Items.data() + PrevIdx, CurrentIdx - PrevIdx);
197 }
198
199 ArrayRef>
200 gatherLineSubViews(size_t &CurrentIdx,
201 ArrayRef> Items,
202 unsigned LineNo) {
203 auto PrevIdx = CurrentIdx;
204 auto E = Items.size();
205 while (CurrentIdx < E &&
206 Items[CurrentIdx]->getSubViewsExpansionLine() == LineNo)
207 ++CurrentIdx;
208 return ArrayRef>(Items.data() + PrevIdx,
209 CurrentIdx - PrevIdx);
210 }
211
212 void SourceCoverageView::render(raw_ostream &OS, unsigned Offset) {
213 // Make sure that the children are in sorted order.
214 sortChildren();
215
216 SmallVector AdjustedLineHighlightRanges;
217 size_t CurrentChild = 0;
218 size_t CurrentHighlightRange = 0;
219 size_t CurrentRegionMarker = 0;
220
221 line_iterator Lines(File);
222 // Advance the line iterator to the first line.
223 while (Lines.line_number() < LineStart)
224 ++Lines;
225
226 // The width of the leading columns
227 unsigned CombinedColumnWidth =
228 (Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
229 (Options.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
230 // The width of the line that is used to divide between the view and the
231 // subviews.
232 unsigned DividerWidth = CombinedColumnWidth + 4;
233
234 for (size_t I = 0; I < LineCount; ++I) {
235 unsigned LineNo = I + LineStart;
236
237 // Gather the child subviews that are visible on this line.
238 auto LineSubViews = gatherLineSubViews(CurrentChild, Children, LineNo);
239
240 renderOffset(OS, Offset);
241 if (Options.ShowLineStats)
242 renderLineCoverageColumn(OS, LineStats[I]);
243 if (Options.ShowLineNumbers)
244 renderLineNumberColumn(OS, LineNo);
245
246 // Gather highlighting ranges.
247 auto LineHighlightRanges =
248 gatherLineItems(CurrentHighlightRange, HighlightRanges, LineNo);
249 auto LineRanges = LineHighlightRanges;
250 // Highlight the expansion range if there is an expansion subview on this
251 // line.
252 if (!LineSubViews.empty() && LineSubViews.front()->isExpansionSubView() &&
253 Options.Colors) {
254 insertHighlightRange(LineHighlightRanges,
255 LineSubViews.front()->getExpansionHighlightRange(),
256 AdjustedLineHighlightRanges);
257 LineRanges = AdjustedLineHighlightRanges;
258 }
259
260 // Display the source code for the current line.
261 StringRef Line = *Lines;
262 // Check if the line is empty, as line_iterator skips blank lines.
263 if (LineNo < Lines.line_number())
264 Line = "";
265 else if (!Lines.is_at_eof())
266 ++Lines;
267 renderLine(OS, Line, LineRanges);
268
269 // Show the region markers.
270 bool ShowMarkers = !Options.ShowLineStatsOrRegionMarkers ||
271 LineStats[I].hasMultipleRegions();
272 auto LineMarkers = gatherLineItems(CurrentRegionMarker, Markers, LineNo);
273 if (ShowMarkers && !LineMarkers.empty()) {
274 renderOffset(OS, Offset);
275 OS.indent(CombinedColumnWidth);
276 renderRegionMarkers(OS, LineMarkers);
277 }
278
279 // Show the line's expanded child subviews.
280 bool FirstChildExpansion = true;
281 if (LineSubViews.empty())
282 continue;
283 unsigned NewOffset = Offset + 1;
284 renderViewDivider(NewOffset, DividerWidth, OS);
285 OS << "\n";
286 for (const auto &Child : LineSubViews) {
287 // If this subview shows a function instantiation, render the function's
288 // name.
289 if (Child->isInstantiationSubView()) {
290 renderOffset(OS, NewOffset);
291 OS << ' ';
292 Options.colored_ostream(OS, raw_ostream::CYAN) << Child->FunctionName
293 << ":";
294 OS << "\n";
295 } else {
296 if (!FirstChildExpansion) {
297 // Re-render the current line and highlight the expansion range for
298 // this
299 // subview.
300 insertHighlightRange(LineHighlightRanges,
301 Child->getExpansionHighlightRange(),
302 AdjustedLineHighlightRanges);
303 renderOffset(OS, Offset);
304 OS.indent(CombinedColumnWidth + (Offset == 0 ? 0 : 1));
305 renderLine(OS, Line, AdjustedLineHighlightRanges);
306 renderViewDivider(NewOffset, DividerWidth, OS);
307 OS << "\n";
308 } else
309 FirstChildExpansion = false;
310 }
311 // Render the child subview
312 Child->render(OS, NewOffset);
313 renderViewDivider(NewOffset, DividerWidth, OS);
314 OS << "\n";
315 }
316 }
317 }
318
319 void
320 SourceCoverageView::createLineCoverageInfo(SourceCoverageDataManager &Data) {
321 LineStats.resize(LineCount);
322 for (const auto &Region : Data.getSourceRegions()) {
323 auto Value = Region.second;
324 LineStats[Region.first.LineStart - LineStart].addRegionStartCount(Value);
325 for (unsigned Line = Region.first.LineStart + 1;
326 Line <= Region.first.LineEnd; ++Line)
327 LineStats[Line - LineStart].addRegionCount(Value);
328 }
329
330 // Reset the line stats for skipped regions.
331 for (const auto &Region : Data.getSkippedRegions()) {
332 for (unsigned Line = Region.LineStart; Line <= Region.LineEnd; ++Line)
333 LineStats[Line - LineStart] = LineCoverageInfo();
334 }
335 }
336
337 void
338 SourceCoverageView::createHighlightRanges(SourceCoverageDataManager &Data) {
339 auto Regions = Data.getSourceRegions();
340 std::vector AlreadyHighlighted;
341 AlreadyHighlighted.resize(Regions.size(), false);
342
343 for (size_t I = 0, S = Regions.size(); I < S; ++I) {
344 const auto &Region = Regions[I];
345 auto Value = Region.second;
346 auto SrcRange = Region.first;
347 if (Value != 0)
348 continue;
349 if (AlreadyHighlighted[I])
350 continue;
351 for (size_t J = 0; J < S; ++J) {
352 if (SrcRange.contains(Regions[J].first)) {
353 AlreadyHighlighted[J] = true;
354 }
355 }
356 if (SrcRange.LineStart == SrcRange.LineEnd) {
357 HighlightRanges.push_back(HighlightRange(
358 SrcRange.LineStart, SrcRange.ColumnStart, SrcRange.ColumnEnd));
359 continue;
360 }
361 HighlightRanges.push_back(
362 HighlightRange(SrcRange.LineStart, SrcRange.ColumnStart,
363 std::numeric_limits::max()));
364 HighlightRanges.push_back(
365 HighlightRange(SrcRange.LineEnd, 1, SrcRange.ColumnEnd));
366 for (unsigned Line = SrcRange.LineStart + 1; Line < SrcRange.LineEnd;
367 ++Line) {
368 HighlightRanges.push_back(
369 HighlightRange(Line, 1, std::numeric_limits::max()));
370 }
371 }
372
373 std::sort(HighlightRanges.begin(), HighlightRanges.end());
374
375 if (Options.Debug) {
376 for (const auto &Range : HighlightRanges) {
377 outs() << "Highlighted line " << Range.Line << ", " << Range.ColumnStart
378 << " -> ";
379 if (Range.ColumnEnd == std::numeric_limits::max()) {
380 outs() << "?\n";
381 } else {
382 outs() << Range.ColumnEnd << "\n";
383 }
384 }
385 }
386 }
387
388 void SourceCoverageView::createRegionMarkers(SourceCoverageDataManager &Data) {
389 for (const auto &Region : Data.getSourceRegions()) {
390 if (Region.first.LineStart >= LineStart)
391 Markers.push_back(RegionMarker(Region.first.LineStart,
392 Region.first.ColumnStart, Region.second));
393 }
394
395 if (Options.Debug) {
396 for (const auto &Marker : Markers) {
397 outs() << "Marker at " << Marker.Line << ":" << Marker.Column << " = "
398 << Marker.ExecutionCount << "\n";
399 }
400 }
401 }
402
403 void SourceCoverageView::load(SourceCoverageDataManager &Data) {
404 if (Options.ShowLineStats)
405 createLineCoverageInfo(Data);
406 if (Options.Colors)
407 createHighlightRanges(Data);
408 if (Options.ShowRegionMarkers)
409 createRegionMarkers(Data);
410 }
0 //===- SourceCoverageView.h - Code coverage view for source code ----------===//
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 // This class implements rendering for code coverage of source code.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #ifndef LLVM_COV_SOURCECOVERAGEVIEW_H
14 #define LLVM_COV_SOURCECOVERAGEVIEW_H
15
16 #include "CoverageViewOptions.h"
17 #include "SourceCoverageDataManager.h"
18 #include "llvm/ProfileData/CoverageMapping.h"
19 #include "llvm/Support/MemoryBuffer.h"
20 #include
21
22 namespace llvm {
23
24 /// \brief A code coverage view of a specific source file.
25 /// It can have embedded coverage views.
26 class SourceCoverageView {
27 public:
28 enum SubViewKind { View, ExpansionView, InstantiationView };
29
30 /// \brief Coverage information for a single line.
31 struct LineCoverageInfo {
32 uint64_t ExecutionCount;
33 unsigned RegionCount;
34 bool Mapped;
35
36 LineCoverageInfo() : ExecutionCount(0), RegionCount(0), Mapped(false) {}
37
38 bool isMapped() const { return Mapped; }
39
40 bool hasMultipleRegions() const { return RegionCount > 1; }
41
42 void addRegionStartCount(uint64_t Count) {
43 Mapped = true;
44 ExecutionCount = Count;
45 ++RegionCount;
46 }
47
48 void addRegionCount(uint64_t Count) {
49 Mapped = true;
50 ExecutionCount = Count;
51 }
52 };
53
54 /// \brief A marker that points at the start
55 /// of a specific mapping region.
56 struct RegionMarker {
57 unsigned Line, Column;
58 uint64_t ExecutionCount;
59
60 RegionMarker(unsigned Line, unsigned Column, uint64_t Value)
61 : Line(Line), Column(Column), ExecutionCount(Value) {}
62 };
63
64 /// \brief A single line source range used to
65 /// render highlighted text.
66 struct HighlightRange {
67 enum HighlightKind {
68 /// The code that wasn't executed.
69 NotCovered,
70
71 /// The region of code that was expanded.
72 Expanded
73 };
74 HighlightKind Kind;
75 unsigned Line;
76 unsigned ColumnStart;
77 unsigned ColumnEnd;
78
79 HighlightRange(unsigned Line, unsigned ColumnStart, unsigned ColumnEnd,
80 HighlightKind Kind = NotCovered)
81 : Kind(Kind), Line(Line), ColumnStart(ColumnStart),
82 ColumnEnd(ColumnEnd) {}
83
84 bool operator<(const HighlightRange &Other) const {
85 if (Line == Other.Line)
86 return ColumnStart < Other.ColumnStart;
87 return Line < Other.Line;
88 }
89
90 bool columnStartOverlaps(const HighlightRange &Other) const {
91 return ColumnStart <= Other.ColumnStart && ColumnEnd > Other.ColumnStart;
92 }
93 bool columnEndOverlaps(const HighlightRange &Other) const {
94 return ColumnEnd >= Other.ColumnEnd && ColumnStart < Other.ColumnEnd;
95 }
96 bool contains(const HighlightRange &Other) const {
97 if (Line != Other.Line)
98 return false;
99 return ColumnStart <= Other.ColumnStart && ColumnEnd >= Other.ColumnEnd;
100 }
101
102 bool overlaps(const HighlightRange &Other) const {
103 if (Line != Other.Line)
104 return false;
105 return columnStartOverlaps(Other) || columnEndOverlaps(Other);
106 }
107 };
108
109 private:
110 const MemoryBuffer &File;
111 const CoverageViewOptions &Options;
112 unsigned LineStart, LineCount;
113 SubViewKind Kind;
114 coverage::CounterMappingRegion ExpansionRegion;
115 std::vector> Children;
116 std::vector LineStats;
117 std::vector HighlightRanges;
118 std::vector Markers;
119 StringRef FunctionName;
120
121 /// \brief Create the line coverage information using the coverage data.
122 void createLineCoverageInfo(SourceCoverageDataManager &Data);
123
124 /// \brief Create the line highlighting ranges using the coverage data.
125 void createHighlightRanges(SourceCoverageDataManager &Data);
126
127 /// \brief Create the region markers using the coverage data.
128 void createRegionMarkers(SourceCoverageDataManager &Data);
129
130 /// \brief Sort children by the starting location.
131 void sortChildren();
132
133 /// \brief Return a highlight range for the expansion region of this view.
134 HighlightRange getExpansionHighlightRange() const;
135
136 /// \brief Render a source line with highlighting.
137 void renderLine(raw_ostream &OS, StringRef Line,
138 ArrayRef Ranges);
139
140 void renderOffset(raw_ostream &OS, unsigned I);
141
142 void renderViewDivider(unsigned Offset, unsigned Length, raw_ostream &OS);
143
144 /// \brief Render the line's execution count column.
145 void renderLineCoverageColumn(raw_ostream &OS, const LineCoverageInfo &Line);
146
147 /// \brief Render the line number column.
148 void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo);
149
150 /// \brief Render all the region's execution counts on a line.
151 void renderRegionMarkers(raw_ostream &OS, ArrayRef Regions);
152
153 static const unsigned LineCoverageColumnWidth = 7;
154 static const unsigned LineNumberColumnWidth = 5;
155
156 public:
157 SourceCoverageView(const MemoryBuffer &File,
158 const CoverageViewOptions &Options)
159 : File(File), Options(Options), LineStart(1), Kind(View),
160 ExpansionRegion(coverage::Counter(), 0, 0, 0, 0, 0) {
161 LineCount = File.getBuffer().count('\n') + 1;
162 }
163
164 SourceCoverageView(const MemoryBuffer &File,
165 const CoverageViewOptions &Options, unsigned LineStart,
166 unsigned LineEnd)
167 : File(File), Options(Options), LineStart(LineStart),
168 LineCount(LineEnd - LineStart + 1), Kind(View),
169 ExpansionRegion(coverage::Counter(), 0, 0, 0, 0, 0) {}
170
171 SourceCoverageView(SourceCoverageView &Parent, unsigned LineStart,
172 unsigned LineEnd, StringRef FunctionName)
173 : File(Parent.File), Options(Parent.Options), LineStart(LineStart),
174 LineCount(LineEnd - LineStart + 1), Kind(InstantiationView),
175 ExpansionRegion(coverage::Counter(), 0, LineEnd, 0, LineEnd, 0),
176 FunctionName(FunctionName) {}
177
178 SourceCoverageView(const MemoryBuffer &File,
179 const CoverageViewOptions &Options, unsigned LineStart,
180 unsigned LineEnd,
181 const coverage::CounterMappingRegion &ExpansionRegion)
182 : File(File), Options(Options), LineStart(LineStart),
183 LineCount(LineEnd - LineStart + 1), Kind(ExpansionView),
184 ExpansionRegion(ExpansionRegion) {}
185
186 const CoverageViewOptions &getOptions() const { return Options; }
187
188 bool isExpansionSubView() const { return Kind == ExpansionView; }
189
190 bool isInstantiationSubView() const { return Kind == InstantiationView; }
191
192 /// \brief Return the line number after which the subview expansion is shown.
193 unsigned getSubViewsExpansionLine() const {
194 return ExpansionRegion.LineStart;
195 }
196
197 void addChild(std::unique_ptr View) {
198 Children.push_back(std::move(View));
199 }
200
201 /// \brief Print the code coverage information for a specific
202 /// portion of a source file to the output stream.
203 void render(raw_ostream &OS, unsigned Offset = 0);
204
205 /// \brief Load the coverage information required for rendering
206 /// from the mapping regions in the data manager.
207 void load(SourceCoverageDataManager &Data);
208 };
209
210 } // namespace llvm
211
212 #endif // LLVM_COV_SOURCECOVERAGEVIEW_H
0 //===- TestingSupport.cpp - Convert objects files into test files --------===//
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 #include "llvm/Object/ObjectFile.h"
10 #include "llvm/Support/raw_ostream.h"
11 #include "llvm/Support/LEB128.h"
12 #include "llvm/Support/CommandLine.h"
13 #include "llvm/Support/ManagedStatic.h"
14 #include "llvm/Support/MemoryObject.h"
15 #include "llvm/Support/Signals.h"
16 #include "llvm/Support/PrettyStackTrace.h"
17 #include
18 #include
19
20 using namespace llvm;
21 using namespace object;
22
23 int convert_for_testing_main(int argc, const char **argv) {
24 sys::PrintStackTraceOnErrorSignal();
25 PrettyStackTraceProgram X(argc, argv);
26 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
27
28 cl::opt InputSourceFile(cl::Positional, cl::Required,
29 cl::desc(""));
30
31 cl::opt OutputFilename(
32 "o", cl::Required,
33 cl::desc(
34 "File with the profile data obtained after an instrumented run"));
35
36 cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
37
38 auto ObjErr = llvm::object::ObjectFile::createObjectFile(InputSourceFile);
39 if (auto Err = ObjErr.getError()) {
40 errs() << "error: " << Err.message() << "\n";
41 return 1;
42 }
43 ObjectFile *OF = ObjErr.get().getBinary().get();
44 auto BytesInAddress = OF->getBytesInAddress();
45 if (BytesInAddress != 8) {
46 errs() << "error: 64 bit binary expected\n";
47 return 1;
48 }
49
50 // Look for the sections that we are interested in.
51 int FoundSectionCount = 0;
52 SectionRef ProfileNames, CoverageMapping;
53 for (const auto &Section : OF->sections()) {
54 StringRef Name;
55 if (Section.getName(Name))
56 return 1;
57 if (Name == "__llvm_prf_names") {
58 ProfileNames = Section;
59 } else if (Name == "__llvm_covmap") {
60 CoverageMapping = Section;
61 } else
62 continue;
63 ++FoundSectionCount;
64 }
65 if (FoundSectionCount != 2)
66 return 1;
67
68 // Get the contents of the given sections.
69 StringRef CoverageMappingData;
70 uint64_t ProfileNamesAddress;
71 StringRef ProfileNamesData;
72 if (CoverageMapping.getContents(CoverageMappingData) ||
73 ProfileNames.getAddress(ProfileNamesAddress) ||
74 ProfileNames.getContents(ProfileNamesData))
75 return 1;
76
77 int FD;
78 if (auto Err =
79 sys::fs::openFileForWrite(OutputFilename, FD, sys::fs::F_None)) {
80 errs() << "error: " << Err.message() << "\n";
81 return 1;
82 }
83
84 raw_fd_ostream OS(FD, true);
85 OS << "llvmcovmtestdata";
86 encodeULEB128(ProfileNamesData.size(), OS);
87 encodeULEB128(ProfileNamesAddress, OS);
88 OS << ProfileNamesData << CoverageMappingData;
89
90 return 0;
91 }
1010 //
1111 //===----------------------------------------------------------------------===//
1212
13 /// \brief The main function for the gcov compatible coverage tool
13 #include "llvm/ADT/StringRef.h"
14 #include "llvm/Support/raw_ostream.h"
15 #include "llvm/Support/Path.h"
16 #include
17
18 using namespace llvm;
19
20 /// \brief The main entry point for the 'show' subcommand.
21 int show_main(int argc, const char **argv);
22
23 /// \brief The main entry point for the 'report' subcommand.
24 int report_main(int argc, const char **argv);
25
26 /// \brief The main entry point for the 'convert-for-testing' subcommand.
27 int convert_for_testing_main(int argc, const char **argv);
28
29 /// \brief The main entry point for the gcov compatible coverage tool.
1430 int gcov_main(int argc, const char **argv);
1531
1632 int main(int argc, const char **argv) {
33 // If argv[0] is or ends with 'gcov', always be gcov compatible
34 if (sys::path::stem(argv[0]).endswith_lower("gcov"))
35 return gcov_main(argc, argv);
36
37 // Check if we are invoking a specific tool command.
38 if (argc > 1) {
39 int (*func)(int, const char **) = nullptr;
40
41 StringRef command = argv[1];
42 if (command.equals_lower("show"))
43 func = show_main;
44 else if (command.equals_lower("report"))
45 func = report_main;
46 else if (command.equals_lower("convert-for-testing"))
47 func = convert_for_testing_main;
48 else if (command.equals_lower("gcov"))
49 func = gcov_main;
50
51 if (func) {
52 std::string Invocation(std::string(argv[0]) + " " + argv[1]);
53 argv[1] = Invocation.c_str();
54 return func(argc - 1, argv + 1);
55 }
56 }
57
58 // Give a warning and fall back to gcov
59 errs().changeColor(raw_ostream::RED);
60 errs() << "warning:";
61 // Assume that argv[1] wasn't a command when it stats with a '-' or is a
62 // filename (i.e. contains a '.')
63 if (argc > 1 && !StringRef(argv[1]).startswith("-") &&
64 StringRef(argv[1]).find(".") == StringRef::npos)
65 errs() << " Unrecognized command '" << argv[1] << "'.";
66 errs() << " Using the gcov compatible mode "
67 "(this behaviour may be dropped in the future).";
68 errs().resetColor();
69 errs() << "\n";
70
1771 return gcov_main(argc, argv);
1872 }