1 //=-- CoverageMapping.cpp - Code coverage mapping support ---------*- C++ -*-=//
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 file contains support for clang's and llvm's instrumentation based
11 // code coverage.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "llvm/ProfileData/CoverageMapping.h"
16 #include "llvm/ADT/DenseMap.h"
17 #include "llvm/ADT/Optional.h"
18 #include "llvm/ADT/SmallBitVector.h"
19 #include "llvm/ProfileData/CoverageMappingReader.h"
20 #include "llvm/ProfileData/InstrProfReader.h"
21 #include "llvm/Support/Debug.h"
22 #include "llvm/Support/ErrorHandling.h"
23 #include "llvm/Support/raw_ostream.h"
24 
25 using namespace llvm;
26 using namespace coverage;
27 
28 #define DEBUG_TYPE "coverage-mapping"
29 
get(const CounterExpression & E)30 Counter CounterExpressionBuilder::get(const CounterExpression &E) {
31   auto It = ExpressionIndices.find(E);
32   if (It != ExpressionIndices.end())
33     return Counter::getExpression(It->second);
34   unsigned I = Expressions.size();
35   Expressions.push_back(E);
36   ExpressionIndices[E] = I;
37   return Counter::getExpression(I);
38 }
39 
extractTerms(Counter C,int Sign,SmallVectorImpl<std::pair<unsigned,int>> & Terms)40 void CounterExpressionBuilder::extractTerms(
41     Counter C, int Sign, SmallVectorImpl<std::pair<unsigned, int>> &Terms) {
42   switch (C.getKind()) {
43   case Counter::Zero:
44     break;
45   case Counter::CounterValueReference:
46     Terms.push_back(std::make_pair(C.getCounterID(), Sign));
47     break;
48   case Counter::Expression:
49     const auto &E = Expressions[C.getExpressionID()];
50     extractTerms(E.LHS, Sign, Terms);
51     extractTerms(E.RHS, E.Kind == CounterExpression::Subtract ? -Sign : Sign,
52                  Terms);
53     break;
54   }
55 }
56 
simplify(Counter ExpressionTree)57 Counter CounterExpressionBuilder::simplify(Counter ExpressionTree) {
58   // Gather constant terms.
59   llvm::SmallVector<std::pair<unsigned, int>, 32> Terms;
60   extractTerms(ExpressionTree, +1, Terms);
61 
62   // If there are no terms, this is just a zero. The algorithm below assumes at
63   // least one term.
64   if (Terms.size() == 0)
65     return Counter::getZero();
66 
67   // Group the terms by counter ID.
68   std::sort(Terms.begin(), Terms.end(),
69             [](const std::pair<unsigned, int> &LHS,
70                const std::pair<unsigned, int> &RHS) {
71     return LHS.first < RHS.first;
72   });
73 
74   // Combine terms by counter ID to eliminate counters that sum to zero.
75   auto Prev = Terms.begin();
76   for (auto I = Prev + 1, E = Terms.end(); I != E; ++I) {
77     if (I->first == Prev->first) {
78       Prev->second += I->second;
79       continue;
80     }
81     ++Prev;
82     *Prev = *I;
83   }
84   Terms.erase(++Prev, Terms.end());
85 
86   Counter C;
87   // Create additions. We do this before subtractions to avoid constructs like
88   // ((0 - X) + Y), as opposed to (Y - X).
89   for (auto Term : Terms) {
90     if (Term.second <= 0)
91       continue;
92     for (int I = 0; I < Term.second; ++I)
93       if (C.isZero())
94         C = Counter::getCounter(Term.first);
95       else
96         C = get(CounterExpression(CounterExpression::Add, C,
97                                   Counter::getCounter(Term.first)));
98   }
99 
100   // Create subtractions.
101   for (auto Term : Terms) {
102     if (Term.second >= 0)
103       continue;
104     for (int I = 0; I < -Term.second; ++I)
105       C = get(CounterExpression(CounterExpression::Subtract, C,
106                                 Counter::getCounter(Term.first)));
107   }
108   return C;
109 }
110 
add(Counter LHS,Counter RHS)111 Counter CounterExpressionBuilder::add(Counter LHS, Counter RHS) {
112   return simplify(get(CounterExpression(CounterExpression::Add, LHS, RHS)));
113 }
114 
subtract(Counter LHS,Counter RHS)115 Counter CounterExpressionBuilder::subtract(Counter LHS, Counter RHS) {
116   return simplify(
117       get(CounterExpression(CounterExpression::Subtract, LHS, RHS)));
118 }
119 
dump(const Counter & C,llvm::raw_ostream & OS) const120 void CounterMappingContext::dump(const Counter &C,
121                                  llvm::raw_ostream &OS) const {
122   switch (C.getKind()) {
123   case Counter::Zero:
124     OS << '0';
125     return;
126   case Counter::CounterValueReference:
127     OS << '#' << C.getCounterID();
128     break;
129   case Counter::Expression: {
130     if (C.getExpressionID() >= Expressions.size())
131       return;
132     const auto &E = Expressions[C.getExpressionID()];
133     OS << '(';
134     dump(E.LHS, OS);
135     OS << (E.Kind == CounterExpression::Subtract ? " - " : " + ");
136     dump(E.RHS, OS);
137     OS << ')';
138     break;
139   }
140   }
141   if (CounterValues.empty())
142     return;
143   ErrorOr<int64_t> Value = evaluate(C);
144   if (!Value)
145     return;
146   OS << '[' << *Value << ']';
147 }
148 
evaluate(const Counter & C) const149 ErrorOr<int64_t> CounterMappingContext::evaluate(const Counter &C) const {
150   switch (C.getKind()) {
151   case Counter::Zero:
152     return 0;
153   case Counter::CounterValueReference:
154     if (C.getCounterID() >= CounterValues.size())
155       return std::make_error_code(std::errc::argument_out_of_domain);
156     return CounterValues[C.getCounterID()];
157   case Counter::Expression: {
158     if (C.getExpressionID() >= Expressions.size())
159       return std::make_error_code(std::errc::argument_out_of_domain);
160     const auto &E = Expressions[C.getExpressionID()];
161     ErrorOr<int64_t> LHS = evaluate(E.LHS);
162     if (!LHS)
163       return LHS;
164     ErrorOr<int64_t> RHS = evaluate(E.RHS);
165     if (!RHS)
166       return RHS;
167     return E.Kind == CounterExpression::Subtract ? *LHS - *RHS : *LHS + *RHS;
168   }
169   }
170   llvm_unreachable("Unhandled CounterKind");
171 }
172 
skipOtherFiles()173 void FunctionRecordIterator::skipOtherFiles() {
174   while (Current != Records.end() && !Filename.empty() &&
175          Filename != Current->Filenames[0])
176     ++Current;
177   if (Current == Records.end())
178     *this = FunctionRecordIterator();
179 }
180 
181 ErrorOr<std::unique_ptr<CoverageMapping>>
load(CoverageMappingReader & CoverageReader,IndexedInstrProfReader & ProfileReader)182 CoverageMapping::load(CoverageMappingReader &CoverageReader,
183                       IndexedInstrProfReader &ProfileReader) {
184   auto Coverage = std::unique_ptr<CoverageMapping>(new CoverageMapping());
185 
186   std::vector<uint64_t> Counts;
187   for (const auto &Record : CoverageReader) {
188     CounterMappingContext Ctx(Record.Expressions);
189 
190     Counts.clear();
191     if (std::error_code EC = ProfileReader.getFunctionCounts(
192             Record.FunctionName, Record.FunctionHash, Counts)) {
193       if (EC == instrprof_error::hash_mismatch) {
194         Coverage->MismatchedFunctionCount++;
195         continue;
196       } else if (EC != instrprof_error::unknown_function)
197         return EC;
198     } else
199       Ctx.setCounts(Counts);
200 
201     assert(!Record.MappingRegions.empty() && "Function has no regions");
202     FunctionRecord Function(Record.FunctionName, Record.Filenames);
203     for (const auto &Region : Record.MappingRegions) {
204       ErrorOr<int64_t> ExecutionCount = Ctx.evaluate(Region.Count);
205       if (!ExecutionCount)
206         break;
207       Function.pushRegion(Region, *ExecutionCount);
208     }
209     if (Function.CountedRegions.size() != Record.MappingRegions.size()) {
210       Coverage->MismatchedFunctionCount++;
211       continue;
212     }
213 
214     Coverage->Functions.push_back(std::move(Function));
215   }
216 
217   return std::move(Coverage);
218 }
219 
220 ErrorOr<std::unique_ptr<CoverageMapping>>
load(StringRef ObjectFilename,StringRef ProfileFilename,Triple::ArchType Arch)221 CoverageMapping::load(StringRef ObjectFilename, StringRef ProfileFilename,
222                       Triple::ArchType Arch) {
223   auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename);
224   if (std::error_code EC = CounterMappingBuff.getError())
225     return EC;
226   auto CoverageReaderOrErr =
227       BinaryCoverageReader::create(CounterMappingBuff.get(), Arch);
228   if (std::error_code EC = CoverageReaderOrErr.getError())
229     return EC;
230   auto CoverageReader = std::move(CoverageReaderOrErr.get());
231   auto ProfileReaderOrErr = IndexedInstrProfReader::create(ProfileFilename);
232   if (auto EC = ProfileReaderOrErr.getError())
233     return EC;
234   auto ProfileReader = std::move(ProfileReaderOrErr.get());
235   return load(*CoverageReader, *ProfileReader);
236 }
237 
238 namespace {
239 /// \brief Distributes functions into instantiation sets.
240 ///
241 /// An instantiation set is a collection of functions that have the same source
242 /// code, ie, template functions specializations.
243 class FunctionInstantiationSetCollector {
244   typedef DenseMap<std::pair<unsigned, unsigned>,
245                    std::vector<const FunctionRecord *>> MapT;
246   MapT InstantiatedFunctions;
247 
248 public:
insert(const FunctionRecord & Function,unsigned FileID)249   void insert(const FunctionRecord &Function, unsigned FileID) {
250     auto I = Function.CountedRegions.begin(), E = Function.CountedRegions.end();
251     while (I != E && I->FileID != FileID)
252       ++I;
253     assert(I != E && "function does not cover the given file");
254     auto &Functions = InstantiatedFunctions[I->startLoc()];
255     Functions.push_back(&Function);
256   }
257 
begin()258   MapT::iterator begin() { return InstantiatedFunctions.begin(); }
259 
end()260   MapT::iterator end() { return InstantiatedFunctions.end(); }
261 };
262 
263 class SegmentBuilder {
264   std::vector<CoverageSegment> Segments;
265   SmallVector<const CountedRegion *, 8> ActiveRegions;
266 
267   /// Start a segment with no count specified.
startSegment(unsigned Line,unsigned Col)268   void startSegment(unsigned Line, unsigned Col) {
269     DEBUG(dbgs() << "Top level segment at " << Line << ":" << Col << "\n");
270     Segments.emplace_back(Line, Col, /*IsRegionEntry=*/false);
271   }
272 
273   /// Start a segment with the given Region's count.
startSegment(unsigned Line,unsigned Col,bool IsRegionEntry,const CountedRegion & Region)274   void startSegment(unsigned Line, unsigned Col, bool IsRegionEntry,
275                     const CountedRegion &Region) {
276     if (Segments.empty())
277       Segments.emplace_back(Line, Col, IsRegionEntry);
278     CoverageSegment S = Segments.back();
279     // Avoid creating empty regions.
280     if (S.Line != Line || S.Col != Col) {
281       Segments.emplace_back(Line, Col, IsRegionEntry);
282       S = Segments.back();
283     }
284     DEBUG(dbgs() << "Segment at " << Line << ":" << Col);
285     // Set this region's count.
286     if (Region.Kind != coverage::CounterMappingRegion::SkippedRegion) {
287       DEBUG(dbgs() << " with count " << Region.ExecutionCount);
288       Segments.back().setCount(Region.ExecutionCount);
289     }
290     DEBUG(dbgs() << "\n");
291   }
292 
293   /// Start a segment for the given region.
startSegment(const CountedRegion & Region)294   void startSegment(const CountedRegion &Region) {
295     startSegment(Region.LineStart, Region.ColumnStart, true, Region);
296   }
297 
298   /// Pop the top region off of the active stack, starting a new segment with
299   /// the containing Region's count.
popRegion()300   void popRegion() {
301     const CountedRegion *Active = ActiveRegions.back();
302     unsigned Line = Active->LineEnd, Col = Active->ColumnEnd;
303     ActiveRegions.pop_back();
304     if (ActiveRegions.empty())
305       startSegment(Line, Col);
306     else
307       startSegment(Line, Col, false, *ActiveRegions.back());
308   }
309 
310 public:
311   /// Build a list of CoverageSegments from a sorted list of Regions.
buildSegments(ArrayRef<CountedRegion> Regions)312   std::vector<CoverageSegment> buildSegments(ArrayRef<CountedRegion> Regions) {
313     const CountedRegion *PrevRegion = nullptr;
314     for (const auto &Region : Regions) {
315       // Pop any regions that end before this one starts.
316       while (!ActiveRegions.empty() &&
317              ActiveRegions.back()->endLoc() <= Region.startLoc())
318         popRegion();
319       if (PrevRegion && PrevRegion->startLoc() == Region.startLoc() &&
320           PrevRegion->endLoc() == Region.endLoc()) {
321         if (Region.Kind == coverage::CounterMappingRegion::CodeRegion)
322           Segments.back().addCount(Region.ExecutionCount);
323       } else {
324         // Add this region to the stack.
325         ActiveRegions.push_back(&Region);
326         startSegment(Region);
327       }
328       PrevRegion = &Region;
329     }
330     // Pop any regions that are left in the stack.
331     while (!ActiveRegions.empty())
332       popRegion();
333     return Segments;
334   }
335 };
336 }
337 
getUniqueSourceFiles() const338 std::vector<StringRef> CoverageMapping::getUniqueSourceFiles() const {
339   std::vector<StringRef> Filenames;
340   for (const auto &Function : getCoveredFunctions())
341     Filenames.insert(Filenames.end(), Function.Filenames.begin(),
342                      Function.Filenames.end());
343   std::sort(Filenames.begin(), Filenames.end());
344   auto Last = std::unique(Filenames.begin(), Filenames.end());
345   Filenames.erase(Last, Filenames.end());
346   return Filenames;
347 }
348 
gatherFileIDs(StringRef SourceFile,const FunctionRecord & Function)349 static SmallBitVector gatherFileIDs(StringRef SourceFile,
350                                     const FunctionRecord &Function) {
351   SmallBitVector FilenameEquivalence(Function.Filenames.size(), false);
352   for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I)
353     if (SourceFile == Function.Filenames[I])
354       FilenameEquivalence[I] = true;
355   return FilenameEquivalence;
356 }
357 
findMainViewFileID(StringRef SourceFile,const FunctionRecord & Function)358 static Optional<unsigned> findMainViewFileID(StringRef SourceFile,
359                                              const FunctionRecord &Function) {
360   SmallBitVector IsNotExpandedFile(Function.Filenames.size(), true);
361   SmallBitVector FilenameEquivalence = gatherFileIDs(SourceFile, Function);
362   for (const auto &CR : Function.CountedRegions)
363     if (CR.Kind == CounterMappingRegion::ExpansionRegion &&
364         FilenameEquivalence[CR.FileID])
365       IsNotExpandedFile[CR.ExpandedFileID] = false;
366   IsNotExpandedFile &= FilenameEquivalence;
367   int I = IsNotExpandedFile.find_first();
368   if (I == -1)
369     return None;
370   return I;
371 }
372 
findMainViewFileID(const FunctionRecord & Function)373 static Optional<unsigned> findMainViewFileID(const FunctionRecord &Function) {
374   SmallBitVector IsNotExpandedFile(Function.Filenames.size(), true);
375   for (const auto &CR : Function.CountedRegions)
376     if (CR.Kind == CounterMappingRegion::ExpansionRegion)
377       IsNotExpandedFile[CR.ExpandedFileID] = false;
378   int I = IsNotExpandedFile.find_first();
379   if (I == -1)
380     return None;
381   return I;
382 }
383 
384 /// Sort a nested sequence of regions from a single file.
sortNestedRegions(It First,It Last)385 template <class It> static void sortNestedRegions(It First, It Last) {
386   std::sort(First, Last,
387             [](const CountedRegion &LHS, const CountedRegion &RHS) {
388     if (LHS.startLoc() == RHS.startLoc())
389       // When LHS completely contains RHS, we sort LHS first.
390       return RHS.endLoc() < LHS.endLoc();
391     return LHS.startLoc() < RHS.startLoc();
392   });
393 }
394 
isExpansion(const CountedRegion & R,unsigned FileID)395 static bool isExpansion(const CountedRegion &R, unsigned FileID) {
396   return R.Kind == CounterMappingRegion::ExpansionRegion && R.FileID == FileID;
397 }
398 
getCoverageForFile(StringRef Filename)399 CoverageData CoverageMapping::getCoverageForFile(StringRef Filename) {
400   CoverageData FileCoverage(Filename);
401   std::vector<coverage::CountedRegion> Regions;
402 
403   for (const auto &Function : Functions) {
404     auto MainFileID = findMainViewFileID(Filename, Function);
405     if (!MainFileID)
406       continue;
407     auto FileIDs = gatherFileIDs(Filename, Function);
408     for (const auto &CR : Function.CountedRegions)
409       if (FileIDs.test(CR.FileID)) {
410         Regions.push_back(CR);
411         if (isExpansion(CR, *MainFileID))
412           FileCoverage.Expansions.emplace_back(CR, Function);
413       }
414   }
415 
416   sortNestedRegions(Regions.begin(), Regions.end());
417   DEBUG(dbgs() << "Emitting segments for file: " << Filename << "\n");
418   FileCoverage.Segments = SegmentBuilder().buildSegments(Regions);
419 
420   return FileCoverage;
421 }
422 
423 std::vector<const FunctionRecord *>
getInstantiations(StringRef Filename)424 CoverageMapping::getInstantiations(StringRef Filename) {
425   FunctionInstantiationSetCollector InstantiationSetCollector;
426   for (const auto &Function : Functions) {
427     auto MainFileID = findMainViewFileID(Filename, Function);
428     if (!MainFileID)
429       continue;
430     InstantiationSetCollector.insert(Function, *MainFileID);
431   }
432 
433   std::vector<const FunctionRecord *> Result;
434   for (const auto &InstantiationSet : InstantiationSetCollector) {
435     if (InstantiationSet.second.size() < 2)
436       continue;
437     Result.insert(Result.end(), InstantiationSet.second.begin(),
438                   InstantiationSet.second.end());
439   }
440   return Result;
441 }
442 
443 CoverageData
getCoverageForFunction(const FunctionRecord & Function)444 CoverageMapping::getCoverageForFunction(const FunctionRecord &Function) {
445   auto MainFileID = findMainViewFileID(Function);
446   if (!MainFileID)
447     return CoverageData();
448 
449   CoverageData FunctionCoverage(Function.Filenames[*MainFileID]);
450   std::vector<coverage::CountedRegion> Regions;
451   for (const auto &CR : Function.CountedRegions)
452     if (CR.FileID == *MainFileID) {
453       Regions.push_back(CR);
454       if (isExpansion(CR, *MainFileID))
455         FunctionCoverage.Expansions.emplace_back(CR, Function);
456     }
457 
458   sortNestedRegions(Regions.begin(), Regions.end());
459   DEBUG(dbgs() << "Emitting segments for function: " << Function.Name << "\n");
460   FunctionCoverage.Segments = SegmentBuilder().buildSegments(Regions);
461 
462   return FunctionCoverage;
463 }
464 
465 CoverageData
getCoverageForExpansion(const ExpansionRecord & Expansion)466 CoverageMapping::getCoverageForExpansion(const ExpansionRecord &Expansion) {
467   CoverageData ExpansionCoverage(
468       Expansion.Function.Filenames[Expansion.FileID]);
469   std::vector<coverage::CountedRegion> Regions;
470   for (const auto &CR : Expansion.Function.CountedRegions)
471     if (CR.FileID == Expansion.FileID) {
472       Regions.push_back(CR);
473       if (isExpansion(CR, Expansion.FileID))
474         ExpansionCoverage.Expansions.emplace_back(CR, Expansion.Function);
475     }
476 
477   sortNestedRegions(Regions.begin(), Regions.end());
478   DEBUG(dbgs() << "Emitting segments for expansion of file " << Expansion.FileID
479                << "\n");
480   ExpansionCoverage.Segments = SegmentBuilder().buildSegments(Regions);
481 
482   return ExpansionCoverage;
483 }
484