File indexing completed on 2026-05-10 08:44:23
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #ifndef LLVM_PROFILEDATA_COVERAGE_COVERAGEMAPPING_H
0015 #define LLVM_PROFILEDATA_COVERAGE_COVERAGEMAPPING_H
0016
0017 #include "llvm/ADT/ArrayRef.h"
0018 #include "llvm/ADT/BitVector.h"
0019 #include "llvm/ADT/DenseMap.h"
0020 #include "llvm/ADT/DenseSet.h"
0021 #include "llvm/ADT/Hashing.h"
0022 #include "llvm/ADT/StringRef.h"
0023 #include "llvm/ADT/iterator.h"
0024 #include "llvm/ADT/iterator_range.h"
0025 #include "llvm/Object/BuildID.h"
0026 #include "llvm/ProfileData/Coverage/MCDCTypes.h"
0027 #include "llvm/ProfileData/InstrProf.h"
0028 #include "llvm/Support/Alignment.h"
0029 #include "llvm/Support/Compiler.h"
0030 #include "llvm/Support/Debug.h"
0031 #include "llvm/Support/Endian.h"
0032 #include "llvm/Support/Error.h"
0033 #include "llvm/Support/raw_ostream.h"
0034 #include <cassert>
0035 #include <cstdint>
0036 #include <iterator>
0037 #include <map>
0038 #include <memory>
0039 #include <optional>
0040 #include <sstream>
0041 #include <string>
0042 #include <system_error>
0043 #include <utility>
0044 #include <vector>
0045
0046 namespace llvm {
0047
0048 class IndexedInstrProfReader;
0049
0050 namespace object {
0051 class BuildIDFetcher;
0052 }
0053
0054 namespace vfs {
0055 class FileSystem;
0056 }
0057
0058 namespace coverage {
0059
0060 class CoverageMappingReader;
0061 struct CoverageMappingRecord;
0062
0063 enum class coveragemap_error {
0064 success = 0,
0065 eof,
0066 no_data_found,
0067 unsupported_version,
0068 truncated,
0069 malformed,
0070 decompression_failed,
0071 invalid_or_missing_arch_specifier
0072 };
0073
0074 const std::error_category &coveragemap_category();
0075
0076 inline std::error_code make_error_code(coveragemap_error E) {
0077 return std::error_code(static_cast<int>(E), coveragemap_category());
0078 }
0079
0080 class CoverageMapError : public ErrorInfo<CoverageMapError> {
0081 public:
0082 CoverageMapError(coveragemap_error Err, const Twine &ErrStr = Twine())
0083 : Err(Err), Msg(ErrStr.str()) {
0084 assert(Err != coveragemap_error::success && "Not an error");
0085 }
0086
0087 std::string message() const override;
0088
0089 void log(raw_ostream &OS) const override { OS << message(); }
0090
0091 std::error_code convertToErrorCode() const override {
0092 return make_error_code(Err);
0093 }
0094
0095 coveragemap_error get() const { return Err; }
0096 const std::string &getMessage() const { return Msg; }
0097
0098 static char ID;
0099
0100 private:
0101 coveragemap_error Err;
0102 std::string Msg;
0103 };
0104
0105
0106
0107 struct Counter {
0108
0109
0110 enum CounterKind { Zero, CounterValueReference, Expression };
0111 static const unsigned EncodingTagBits = 2;
0112 static const unsigned EncodingTagMask = 0x3;
0113 static const unsigned EncodingCounterTagAndExpansionRegionTagBits =
0114 EncodingTagBits + 1;
0115
0116 private:
0117 CounterKind Kind = Zero;
0118 unsigned ID = 0;
0119
0120 Counter(CounterKind Kind, unsigned ID) : Kind(Kind), ID(ID) {}
0121
0122 public:
0123 Counter() = default;
0124
0125 CounterKind getKind() const { return Kind; }
0126
0127 bool isZero() const { return Kind == Zero; }
0128
0129 bool isExpression() const { return Kind == Expression; }
0130
0131 unsigned getCounterID() const { return ID; }
0132
0133 unsigned getExpressionID() const { return ID; }
0134
0135 friend bool operator==(const Counter &LHS, const Counter &RHS) {
0136 return LHS.Kind == RHS.Kind && LHS.ID == RHS.ID;
0137 }
0138
0139 friend bool operator!=(const Counter &LHS, const Counter &RHS) {
0140 return !(LHS == RHS);
0141 }
0142
0143 friend bool operator<(const Counter &LHS, const Counter &RHS) {
0144 return std::tie(LHS.Kind, LHS.ID) < std::tie(RHS.Kind, RHS.ID);
0145 }
0146
0147
0148 static Counter getZero() { return Counter(); }
0149
0150
0151 static Counter getCounter(unsigned CounterId) {
0152 return Counter(CounterValueReference, CounterId);
0153 }
0154
0155
0156
0157 static Counter getExpression(unsigned ExpressionId) {
0158 return Counter(Expression, ExpressionId);
0159 }
0160 };
0161
0162
0163
0164 struct CounterExpression {
0165 enum ExprKind { Subtract, Add };
0166 ExprKind Kind;
0167 Counter LHS, RHS;
0168
0169 CounterExpression(ExprKind Kind, Counter LHS, Counter RHS)
0170 : Kind(Kind), LHS(LHS), RHS(RHS) {}
0171 };
0172
0173
0174
0175 class CounterExpressionBuilder {
0176
0177 std::vector<CounterExpression> Expressions;
0178
0179
0180 DenseMap<CounterExpression, unsigned> ExpressionIndices;
0181
0182
0183
0184
0185
0186
0187 Counter get(const CounterExpression &E);
0188
0189
0190 struct Term {
0191 unsigned CounterID;
0192 int Factor;
0193
0194 Term(unsigned CounterID, int Factor)
0195 : CounterID(CounterID), Factor(Factor) {}
0196 };
0197
0198
0199
0200
0201
0202
0203 void extractTerms(Counter C, int Sign, SmallVectorImpl<Term> &Terms);
0204
0205
0206
0207 Counter simplify(Counter ExpressionTree);
0208
0209 public:
0210 ArrayRef<CounterExpression> getExpressions() const { return Expressions; }
0211
0212
0213 Counter add(Counter LHS, Counter RHS, bool Simplify = true);
0214
0215
0216
0217 Counter subtract(Counter LHS, Counter RHS, bool Simplify = true);
0218
0219
0220
0221 using SubstMap = std::map<Counter, Counter>;
0222
0223
0224
0225 Counter subst(Counter C, const SubstMap &Map);
0226 };
0227
0228 using LineColPair = std::pair<unsigned, unsigned>;
0229
0230
0231 struct CounterMappingRegion {
0232 enum RegionKind {
0233
0234 CodeRegion,
0235
0236
0237
0238
0239 ExpansionRegion,
0240
0241
0242
0243 SkippedRegion,
0244
0245
0246
0247 GapRegion,
0248
0249
0250
0251
0252 BranchRegion,
0253
0254
0255
0256 MCDCDecisionRegion,
0257
0258
0259 MCDCBranchRegion
0260 };
0261
0262
0263 Counter Count;
0264
0265
0266 Counter FalseCount;
0267
0268
0269 mcdc::Parameters MCDCParams;
0270
0271 const auto &getDecisionParams() const {
0272 return mcdc::getParams<const mcdc::DecisionParameters>(MCDCParams);
0273 }
0274
0275 const auto &getBranchParams() const {
0276 return mcdc::getParams<const mcdc::BranchParameters>(MCDCParams);
0277 }
0278
0279 unsigned FileID = 0;
0280 unsigned ExpandedFileID = 0;
0281 unsigned LineStart, ColumnStart, LineEnd, ColumnEnd;
0282
0283 RegionKind Kind;
0284
0285 bool isBranch() const {
0286 return (Kind == BranchRegion || Kind == MCDCBranchRegion);
0287 }
0288
0289 CounterMappingRegion(Counter Count, unsigned FileID, unsigned ExpandedFileID,
0290 unsigned LineStart, unsigned ColumnStart,
0291 unsigned LineEnd, unsigned ColumnEnd, RegionKind Kind)
0292 : Count(Count), FileID(FileID), ExpandedFileID(ExpandedFileID),
0293 LineStart(LineStart), ColumnStart(ColumnStart), LineEnd(LineEnd),
0294 ColumnEnd(ColumnEnd), Kind(Kind) {}
0295
0296 CounterMappingRegion(Counter Count, Counter FalseCount, unsigned FileID,
0297 unsigned ExpandedFileID, unsigned LineStart,
0298 unsigned ColumnStart, unsigned LineEnd,
0299 unsigned ColumnEnd, RegionKind Kind,
0300 const mcdc::Parameters &MCDCParams = std::monostate())
0301 : Count(Count), FalseCount(FalseCount), MCDCParams(MCDCParams),
0302 FileID(FileID), ExpandedFileID(ExpandedFileID), LineStart(LineStart),
0303 ColumnStart(ColumnStart), LineEnd(LineEnd), ColumnEnd(ColumnEnd),
0304 Kind(Kind) {}
0305
0306 CounterMappingRegion(const mcdc::DecisionParameters &MCDCParams,
0307 unsigned FileID, unsigned LineStart,
0308 unsigned ColumnStart, unsigned LineEnd,
0309 unsigned ColumnEnd, RegionKind Kind)
0310 : MCDCParams(MCDCParams), FileID(FileID), LineStart(LineStart),
0311 ColumnStart(ColumnStart), LineEnd(LineEnd), ColumnEnd(ColumnEnd),
0312 Kind(Kind) {}
0313
0314 static CounterMappingRegion
0315 makeRegion(Counter Count, unsigned FileID, unsigned LineStart,
0316 unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) {
0317 return CounterMappingRegion(Count, FileID, 0, LineStart, ColumnStart,
0318 LineEnd, ColumnEnd, CodeRegion);
0319 }
0320
0321 static CounterMappingRegion
0322 makeExpansion(unsigned FileID, unsigned ExpandedFileID, unsigned LineStart,
0323 unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) {
0324 return CounterMappingRegion(Counter(), FileID, ExpandedFileID, LineStart,
0325 ColumnStart, LineEnd, ColumnEnd,
0326 ExpansionRegion);
0327 }
0328
0329 static CounterMappingRegion
0330 makeSkipped(unsigned FileID, unsigned LineStart, unsigned ColumnStart,
0331 unsigned LineEnd, unsigned ColumnEnd) {
0332 return CounterMappingRegion(Counter(), FileID, 0, LineStart, ColumnStart,
0333 LineEnd, ColumnEnd, SkippedRegion);
0334 }
0335
0336 static CounterMappingRegion
0337 makeGapRegion(Counter Count, unsigned FileID, unsigned LineStart,
0338 unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) {
0339 return CounterMappingRegion(Count, FileID, 0, LineStart, ColumnStart,
0340 LineEnd, (1U << 31) | ColumnEnd, GapRegion);
0341 }
0342
0343 static CounterMappingRegion
0344 makeBranchRegion(Counter Count, Counter FalseCount, unsigned FileID,
0345 unsigned LineStart, unsigned ColumnStart, unsigned LineEnd,
0346 unsigned ColumnEnd,
0347 const mcdc::Parameters &MCDCParams = std::monostate()) {
0348 return CounterMappingRegion(
0349 Count, FalseCount, FileID, 0, LineStart, ColumnStart, LineEnd,
0350 ColumnEnd,
0351 (std::get_if<mcdc::BranchParameters>(&MCDCParams) ? MCDCBranchRegion
0352 : BranchRegion),
0353 MCDCParams);
0354 }
0355
0356 static CounterMappingRegion
0357 makeDecisionRegion(const mcdc::DecisionParameters &MCDCParams,
0358 unsigned FileID, unsigned LineStart, unsigned ColumnStart,
0359 unsigned LineEnd, unsigned ColumnEnd) {
0360 return CounterMappingRegion(MCDCParams, FileID, LineStart, ColumnStart,
0361 LineEnd, ColumnEnd, MCDCDecisionRegion);
0362 }
0363
0364 inline LineColPair startLoc() const {
0365 return LineColPair(LineStart, ColumnStart);
0366 }
0367
0368 inline LineColPair endLoc() const { return LineColPair(LineEnd, ColumnEnd); }
0369 };
0370
0371
0372 struct CountedRegion : public CounterMappingRegion {
0373 uint64_t ExecutionCount;
0374 uint64_t FalseExecutionCount;
0375 bool TrueFolded;
0376 bool FalseFolded;
0377
0378 CountedRegion(const CounterMappingRegion &R, uint64_t ExecutionCount)
0379 : CounterMappingRegion(R), ExecutionCount(ExecutionCount),
0380 FalseExecutionCount(0), TrueFolded(false), FalseFolded(true) {}
0381
0382 CountedRegion(const CounterMappingRegion &R, uint64_t ExecutionCount,
0383 uint64_t FalseExecutionCount)
0384 : CounterMappingRegion(R), ExecutionCount(ExecutionCount),
0385 FalseExecutionCount(FalseExecutionCount), TrueFolded(false),
0386 FalseFolded(false) {}
0387 };
0388
0389
0390 struct MCDCRecord {
0391
0392
0393
0394
0395
0396
0397 enum CondState { MCDC_DontCare = -1, MCDC_False = 0, MCDC_True = 1 };
0398
0399
0400
0401
0402
0403
0404 class TestVector {
0405 BitVector Values;
0406 BitVector Visited;
0407
0408 public:
0409
0410 TestVector(unsigned N) : Values(N), Visited(N) {}
0411
0412
0413 CondState operator[](int I) const {
0414 return (Visited[I] ? (Values[I] ? MCDC_True : MCDC_False)
0415 : MCDC_DontCare);
0416 }
0417
0418
0419 auto getIndex() const { return Values.getData()[0]; }
0420
0421
0422
0423 void set(int I, CondState Val) {
0424 Visited[I] = (Val != MCDC_DontCare);
0425 Values[I] = (Val == MCDC_True);
0426 }
0427
0428
0429 void push_back(CondState Val) {
0430 Visited.push_back(Val != MCDC_DontCare);
0431 Values.push_back(Val == MCDC_True);
0432 assert(Values.size() == Visited.size());
0433 }
0434
0435
0436
0437
0438
0439
0440
0441 auto getDifferences(const TestVector &B) const {
0442 const auto &A = *this;
0443 BitVector AB = A.Values;
0444 AB ^= B.Values;
0445 AB &= A.Visited;
0446 AB &= B.Visited;
0447 return AB;
0448 }
0449 };
0450
0451 using TestVectors = llvm::SmallVector<std::pair<TestVector, CondState>>;
0452 using BoolVector = std::array<BitVector, 2>;
0453 using TVRowPair = std::pair<unsigned, unsigned>;
0454 using TVPairMap = llvm::DenseMap<unsigned, TVRowPair>;
0455 using CondIDMap = llvm::DenseMap<unsigned, unsigned>;
0456 using LineColPairMap = llvm::DenseMap<unsigned, LineColPair>;
0457
0458 private:
0459 CounterMappingRegion Region;
0460 TestVectors TV;
0461 std::optional<TVPairMap> IndependencePairs;
0462 BoolVector Folded;
0463 CondIDMap PosToID;
0464 LineColPairMap CondLoc;
0465
0466 public:
0467 MCDCRecord(const CounterMappingRegion &Region, TestVectors &&TV,
0468 BoolVector &&Folded, CondIDMap &&PosToID, LineColPairMap &&CondLoc)
0469 : Region(Region), TV(std::move(TV)), Folded(std::move(Folded)),
0470 PosToID(std::move(PosToID)), CondLoc(std::move(CondLoc)) {
0471 findIndependencePairs();
0472 }
0473
0474
0475
0476 void findIndependencePairs();
0477
0478 const CounterMappingRegion &getDecisionRegion() const { return Region; }
0479 unsigned getNumConditions() const {
0480 return Region.getDecisionParams().NumConditions;
0481 }
0482 unsigned getNumTestVectors() const { return TV.size(); }
0483 bool isCondFolded(unsigned Condition) const {
0484 return Folded[false][Condition] || Folded[true][Condition];
0485 }
0486
0487
0488
0489
0490
0491
0492
0493 CondState getTVCondition(unsigned TestVectorIndex, unsigned Condition) {
0494 return TV[TestVectorIndex].first[PosToID[Condition]];
0495 }
0496
0497
0498
0499 CondState getTVResult(unsigned TestVectorIndex) {
0500 return TV[TestVectorIndex].second;
0501 }
0502
0503
0504
0505
0506
0507
0508 bool isConditionIndependencePairCovered(unsigned Condition) const {
0509 assert(IndependencePairs);
0510 auto It = PosToID.find(Condition);
0511 assert(It != PosToID.end() && "Condition ID without an Ordinal mapping");
0512 return IndependencePairs->contains(It->second);
0513 }
0514
0515
0516
0517
0518
0519
0520 TVRowPair getConditionIndependencePair(unsigned Condition) {
0521 assert(isConditionIndependencePairCovered(Condition));
0522 assert(IndependencePairs);
0523 return (*IndependencePairs)[PosToID[Condition]];
0524 }
0525
0526 float getPercentCovered() const {
0527 unsigned Folded = 0;
0528 unsigned Covered = 0;
0529 for (unsigned C = 0; C < getNumConditions(); C++) {
0530 if (isCondFolded(C))
0531 Folded++;
0532 else if (isConditionIndependencePairCovered(C))
0533 Covered++;
0534 }
0535
0536 unsigned Total = getNumConditions() - Folded;
0537 if (Total == 0)
0538 return 0.0;
0539 return (static_cast<double>(Covered) / static_cast<double>(Total)) * 100.0;
0540 }
0541
0542 std::string getConditionHeaderString(unsigned Condition) {
0543 std::ostringstream OS;
0544 OS << "Condition C" << Condition + 1 << " --> (";
0545 OS << CondLoc[Condition].first << ":" << CondLoc[Condition].second;
0546 OS << ")\n";
0547 return OS.str();
0548 }
0549
0550 std::string getTestVectorHeaderString() const {
0551 std::ostringstream OS;
0552 if (getNumTestVectors() == 0) {
0553 OS << "None.\n";
0554 return OS.str();
0555 }
0556 const auto NumConditions = getNumConditions();
0557 for (unsigned I = 0; I < NumConditions; I++) {
0558 OS << "C" << I + 1;
0559 if (I != NumConditions - 1)
0560 OS << ", ";
0561 }
0562 OS << " Result\n";
0563 return OS.str();
0564 }
0565
0566 std::string getTestVectorString(unsigned TestVectorIndex) {
0567 assert(TestVectorIndex < getNumTestVectors() &&
0568 "TestVector index out of bounds!");
0569 std::ostringstream OS;
0570 const auto NumConditions = getNumConditions();
0571
0572 OS << " " << TestVectorIndex + 1 << " { ";
0573 for (unsigned Condition = 0; Condition < NumConditions; Condition++) {
0574 if (isCondFolded(Condition))
0575 OS << "C";
0576 else {
0577 switch (getTVCondition(TestVectorIndex, Condition)) {
0578 case MCDCRecord::MCDC_DontCare:
0579 OS << "-";
0580 break;
0581 case MCDCRecord::MCDC_True:
0582 OS << "T";
0583 break;
0584 case MCDCRecord::MCDC_False:
0585 OS << "F";
0586 break;
0587 }
0588 }
0589 if (Condition != NumConditions - 1)
0590 OS << ", ";
0591 }
0592
0593
0594 OS << " = ";
0595 if (getTVResult(TestVectorIndex) == MCDC_True)
0596 OS << "T";
0597 else
0598 OS << "F";
0599 OS << " }\n";
0600
0601 return OS.str();
0602 }
0603
0604 std::string getConditionCoverageString(unsigned Condition) {
0605 assert(Condition < getNumConditions() &&
0606 "Condition index is out of bounds!");
0607 std::ostringstream OS;
0608
0609 OS << " C" << Condition + 1 << "-Pair: ";
0610 if (isCondFolded(Condition)) {
0611 OS << "constant folded\n";
0612 } else if (isConditionIndependencePairCovered(Condition)) {
0613 TVRowPair rows = getConditionIndependencePair(Condition);
0614 OS << "covered: (" << rows.first << ",";
0615 OS << rows.second << ")\n";
0616 } else
0617 OS << "not covered\n";
0618
0619 return OS.str();
0620 }
0621 };
0622
0623 namespace mcdc {
0624
0625
0626
0627
0628
0629
0630
0631
0632
0633
0634
0635
0636
0637
0638 class TVIdxBuilder {
0639 public:
0640 struct MCDCNode {
0641 int InCount = 0;
0642 int Width;
0643 ConditionIDs NextIDs;
0644 };
0645
0646 #ifndef NDEBUG
0647
0648
0649 SmallVector<MCDCNode> SavedNodes;
0650 #endif
0651
0652
0653 SmallVector<std::array<int, 2>> Indices;
0654
0655
0656
0657 int NumTestVectors;
0658
0659
0660 static constexpr auto HardMaxTVs =
0661 std::numeric_limits<decltype(NumTestVectors)>::max();
0662
0663 public:
0664
0665
0666
0667
0668 TVIdxBuilder(const SmallVectorImpl<ConditionIDs> &NextIDs, int Offset = 0);
0669 };
0670 }
0671
0672
0673
0674 class CounterMappingContext {
0675 ArrayRef<CounterExpression> Expressions;
0676 ArrayRef<uint64_t> CounterValues;
0677 BitVector Bitmap;
0678
0679 public:
0680 CounterMappingContext(ArrayRef<CounterExpression> Expressions,
0681 ArrayRef<uint64_t> CounterValues = {})
0682 : Expressions(Expressions), CounterValues(CounterValues) {}
0683
0684 void setCounts(ArrayRef<uint64_t> Counts) { CounterValues = Counts; }
0685 void setBitmap(BitVector &&Bitmap_) { Bitmap = std::move(Bitmap_); }
0686
0687 void dump(const Counter &C, raw_ostream &OS) const;
0688 void dump(const Counter &C) const { dump(C, dbgs()); }
0689
0690
0691
0692 Expected<int64_t> evaluate(const Counter &C) const;
0693
0694
0695
0696 Expected<MCDCRecord>
0697 evaluateMCDCRegion(const CounterMappingRegion &Region,
0698 ArrayRef<const CounterMappingRegion *> Branches,
0699 bool IsVersion11);
0700
0701 unsigned getMaxCounterID(const Counter &C) const;
0702 };
0703
0704
0705 struct FunctionRecord {
0706
0707 std::string Name;
0708
0709
0710
0711
0712
0713
0714 std::vector<std::string> Filenames;
0715
0716 std::vector<CountedRegion> CountedRegions;
0717
0718 std::vector<CountedRegion> CountedBranchRegions;
0719
0720 std::vector<MCDCRecord> MCDCRecords;
0721
0722 uint64_t ExecutionCount = 0;
0723
0724 FunctionRecord(StringRef Name, ArrayRef<StringRef> Filenames)
0725 : Name(Name), Filenames(Filenames.begin(), Filenames.end()) {}
0726
0727 FunctionRecord(FunctionRecord &&FR) = default;
0728 FunctionRecord &operator=(FunctionRecord &&) = default;
0729
0730 void pushMCDCRecord(MCDCRecord &&Record) {
0731 MCDCRecords.push_back(std::move(Record));
0732 }
0733
0734 void pushRegion(CounterMappingRegion Region, uint64_t Count,
0735 uint64_t FalseCount) {
0736 if (Region.isBranch()) {
0737 CountedBranchRegions.emplace_back(Region, Count, FalseCount);
0738
0739
0740 CountedBranchRegions.back().TrueFolded = Region.Count.isZero();
0741 CountedBranchRegions.back().FalseFolded = Region.FalseCount.isZero();
0742 return;
0743 }
0744 if (CountedRegions.empty())
0745 ExecutionCount = Count;
0746 CountedRegions.emplace_back(Region, Count, FalseCount);
0747 }
0748 };
0749
0750
0751
0752
0753
0754 class FunctionRecordIterator
0755 : public iterator_facade_base<FunctionRecordIterator,
0756 std::forward_iterator_tag, FunctionRecord> {
0757 ArrayRef<FunctionRecord> Records;
0758 ArrayRef<unsigned> RecordIndices;
0759 ArrayRef<unsigned>::iterator CurrentIndex;
0760 ArrayRef<FunctionRecord>::iterator Current;
0761 StringRef Filename;
0762
0763
0764 void skipOtherFiles();
0765
0766 public:
0767 FunctionRecordIterator(ArrayRef<FunctionRecord> Records_,
0768 StringRef Filename = "",
0769 ArrayRef<unsigned> RecordIndices_ = {})
0770 : Records(Records_), RecordIndices(RecordIndices_),
0771 CurrentIndex(RecordIndices.begin()),
0772
0773
0774 Current(CurrentIndex == RecordIndices.end() ? Records.begin()
0775 : &Records[*CurrentIndex]),
0776 Filename(Filename) {
0777 assert(Filename.empty() == RecordIndices_.empty() &&
0778 "If `Filename` is specified, `RecordIndices` must also be provided");
0779 skipOtherFiles();
0780 }
0781
0782 FunctionRecordIterator() : Current(Records.begin()) {}
0783
0784 bool operator==(const FunctionRecordIterator &RHS) const {
0785 return Current == RHS.Current && Filename == RHS.Filename;
0786 }
0787
0788 const FunctionRecord &operator*() const { return *Current; }
0789
0790 FunctionRecordIterator &operator++() {
0791 advanceOne();
0792 skipOtherFiles();
0793 return *this;
0794 }
0795
0796 private:
0797 void advanceOne() {
0798 if (RecordIndices.empty()) {
0799
0800 assert(Current != Records.end() && "incremented past end");
0801 ++Current;
0802 } else {
0803
0804
0805 assert(CurrentIndex != RecordIndices.end() && "incremented past end");
0806 ++CurrentIndex;
0807 if (CurrentIndex == RecordIndices.end()) {
0808 Current = Records.end();
0809 } else {
0810 Current = &Records[*CurrentIndex];
0811 }
0812 }
0813 }
0814 };
0815
0816
0817
0818
0819
0820
0821 struct ExpansionRecord {
0822
0823 unsigned FileID;
0824
0825 const CountedRegion &Region;
0826
0827 const FunctionRecord &Function;
0828
0829 ExpansionRecord(const CountedRegion &Region,
0830 const FunctionRecord &Function)
0831 : FileID(Region.ExpandedFileID), Region(Region), Function(Function) {}
0832 };
0833
0834
0835
0836
0837
0838 struct CoverageSegment {
0839
0840 unsigned Line;
0841
0842 unsigned Col;
0843
0844 uint64_t Count;
0845
0846 bool HasCount;
0847
0848 bool IsRegionEntry;
0849
0850 bool IsGapRegion;
0851
0852 CoverageSegment(unsigned Line, unsigned Col, bool IsRegionEntry)
0853 : Line(Line), Col(Col), Count(0), HasCount(false),
0854 IsRegionEntry(IsRegionEntry), IsGapRegion(false) {}
0855
0856 CoverageSegment(unsigned Line, unsigned Col, uint64_t Count,
0857 bool IsRegionEntry, bool IsGapRegion = false,
0858 bool IsBranchRegion = false)
0859 : Line(Line), Col(Col), Count(Count), HasCount(true),
0860 IsRegionEntry(IsRegionEntry), IsGapRegion(IsGapRegion) {}
0861
0862 friend bool operator==(const CoverageSegment &L, const CoverageSegment &R) {
0863 return std::tie(L.Line, L.Col, L.Count, L.HasCount, L.IsRegionEntry,
0864 L.IsGapRegion) == std::tie(R.Line, R.Col, R.Count,
0865 R.HasCount, R.IsRegionEntry,
0866 R.IsGapRegion);
0867 }
0868 };
0869
0870
0871
0872
0873
0874
0875 class InstantiationGroup {
0876 friend class CoverageMapping;
0877
0878 unsigned Line;
0879 unsigned Col;
0880 std::vector<const FunctionRecord *> Instantiations;
0881
0882 InstantiationGroup(unsigned Line, unsigned Col,
0883 std::vector<const FunctionRecord *> Instantiations)
0884 : Line(Line), Col(Col), Instantiations(std::move(Instantiations)) {}
0885
0886 public:
0887 InstantiationGroup(const InstantiationGroup &) = delete;
0888 InstantiationGroup(InstantiationGroup &&) = default;
0889
0890
0891 size_t size() const { return Instantiations.size(); }
0892
0893
0894 unsigned getLine() const { return Line; }
0895
0896
0897 unsigned getColumn() const { return Col; }
0898
0899
0900 bool hasName() const {
0901 for (unsigned I = 1, E = Instantiations.size(); I < E; ++I)
0902 if (Instantiations[I]->Name != Instantiations[0]->Name)
0903 return false;
0904 return true;
0905 }
0906
0907
0908 StringRef getName() const {
0909 assert(hasName() && "Instantiations don't have a shared name");
0910 return Instantiations[0]->Name;
0911 }
0912
0913
0914 uint64_t getTotalExecutionCount() const {
0915 uint64_t Count = 0;
0916 for (const FunctionRecord *F : Instantiations)
0917 Count += F->ExecutionCount;
0918 return Count;
0919 }
0920
0921
0922 ArrayRef<const FunctionRecord *> getInstantiations() const {
0923 return Instantiations;
0924 }
0925 };
0926
0927
0928
0929
0930
0931
0932 class CoverageData {
0933 friend class CoverageMapping;
0934
0935 std::string Filename;
0936 std::vector<CoverageSegment> Segments;
0937 std::vector<ExpansionRecord> Expansions;
0938 std::vector<CountedRegion> BranchRegions;
0939 std::vector<MCDCRecord> MCDCRecords;
0940
0941 bool SingleByteCoverage = false;
0942
0943 public:
0944 CoverageData() = default;
0945
0946 CoverageData(bool Single, StringRef Filename)
0947 : Filename(Filename), SingleByteCoverage(Single) {}
0948
0949
0950 StringRef getFilename() const { return Filename; }
0951
0952 bool getSingleByteCoverage() const { return SingleByteCoverage; }
0953
0954
0955
0956 std::vector<CoverageSegment>::const_iterator begin() const {
0957 return Segments.begin();
0958 }
0959
0960 std::vector<CoverageSegment>::const_iterator end() const {
0961 return Segments.end();
0962 }
0963
0964 bool empty() const { return Segments.empty(); }
0965
0966
0967 ArrayRef<ExpansionRecord> getExpansions() const { return Expansions; }
0968
0969
0970 ArrayRef<CountedRegion> getBranches() const { return BranchRegions; }
0971
0972
0973 ArrayRef<MCDCRecord> getMCDCRecords() const { return MCDCRecords; }
0974 };
0975
0976
0977
0978
0979
0980 class CoverageMapping {
0981 DenseMap<size_t, DenseSet<size_t>> RecordProvenance;
0982 std::vector<FunctionRecord> Functions;
0983 DenseMap<size_t, SmallVector<unsigned, 0>> FilenameHash2RecordIndices;
0984 std::vector<std::pair<std::string, uint64_t>> FuncHashMismatches;
0985
0986 std::optional<bool> SingleByteCoverage;
0987
0988 CoverageMapping() = default;
0989
0990
0991 static Error loadFromReaders(
0992 ArrayRef<std::unique_ptr<CoverageMappingReader>> CoverageReaders,
0993 IndexedInstrProfReader &ProfileReader, CoverageMapping &Coverage);
0994
0995
0996 static Error
0997 loadFromFile(StringRef Filename, StringRef Arch, StringRef CompilationDir,
0998 IndexedInstrProfReader &ProfileReader, CoverageMapping &Coverage,
0999 bool &DataFound,
1000 SmallVectorImpl<object::BuildID> *FoundBinaryIDs = nullptr);
1001
1002
1003 Error loadFunctionRecord(const CoverageMappingRecord &Record,
1004 IndexedInstrProfReader &ProfileReader);
1005
1006
1007
1008
1009
1010 ArrayRef<unsigned>
1011 getImpreciseRecordIndicesForFilename(StringRef Filename) const;
1012
1013 public:
1014 CoverageMapping(const CoverageMapping &) = delete;
1015 CoverageMapping &operator=(const CoverageMapping &) = delete;
1016
1017
1018 static Expected<std::unique_ptr<CoverageMapping>>
1019 load(ArrayRef<std::unique_ptr<CoverageMappingReader>> CoverageReaders,
1020 IndexedInstrProfReader &ProfileReader);
1021
1022
1023
1024
1025 static Expected<std::unique_ptr<CoverageMapping>>
1026 load(ArrayRef<StringRef> ObjectFilenames, StringRef ProfileFilename,
1027 vfs::FileSystem &FS, ArrayRef<StringRef> Arches = {},
1028 StringRef CompilationDir = "",
1029 const object::BuildIDFetcher *BIDFetcher = nullptr,
1030 bool CheckBinaryIDs = false);
1031
1032
1033
1034
1035
1036 unsigned getMismatchedCount() const { return FuncHashMismatches.size(); }
1037
1038
1039
1040
1041
1042 ArrayRef<std::pair<std::string, uint64_t>> getHashMismatches() const {
1043 return FuncHashMismatches;
1044 }
1045
1046
1047
1048 std::vector<StringRef> getUniqueSourceFiles() const;
1049
1050
1051
1052
1053
1054
1055 CoverageData getCoverageForFile(StringRef Filename) const;
1056
1057
1058 CoverageData getCoverageForFunction(const FunctionRecord &Function) const;
1059
1060
1061 CoverageData getCoverageForExpansion(const ExpansionRecord &Expansion) const;
1062
1063
1064 iterator_range<FunctionRecordIterator> getCoveredFunctions() const {
1065 return make_range(FunctionRecordIterator(Functions),
1066 FunctionRecordIterator());
1067 }
1068
1069
1070 iterator_range<FunctionRecordIterator>
1071 getCoveredFunctions(StringRef Filename) const {
1072 return make_range(
1073 FunctionRecordIterator(Functions, Filename,
1074 getImpreciseRecordIndicesForFilename(Filename)),
1075 FunctionRecordIterator());
1076 }
1077
1078
1079
1080
1081
1082 std::vector<InstantiationGroup>
1083 getInstantiationGroups(StringRef Filename) const;
1084 };
1085
1086
1087 class LineCoverageStats {
1088 uint64_t ExecutionCount;
1089 bool HasMultipleRegions;
1090 bool Mapped;
1091 unsigned Line;
1092 ArrayRef<const CoverageSegment *> LineSegments;
1093 const CoverageSegment *WrappedSegment;
1094
1095 friend class LineCoverageIterator;
1096 LineCoverageStats() = default;
1097
1098 public:
1099 LineCoverageStats(ArrayRef<const CoverageSegment *> LineSegments,
1100 const CoverageSegment *WrappedSegment, unsigned Line);
1101
1102 uint64_t getExecutionCount() const { return ExecutionCount; }
1103
1104 bool hasMultipleRegions() const { return HasMultipleRegions; }
1105
1106 bool isMapped() const { return Mapped; }
1107
1108 unsigned getLine() const { return Line; }
1109
1110 ArrayRef<const CoverageSegment *> getLineSegments() const {
1111 return LineSegments;
1112 }
1113
1114 const CoverageSegment *getWrappedSegment() const { return WrappedSegment; }
1115 };
1116
1117
1118
1119 class LineCoverageIterator
1120 : public iterator_facade_base<LineCoverageIterator,
1121 std::forward_iterator_tag,
1122 const LineCoverageStats> {
1123 public:
1124 LineCoverageIterator(const CoverageData &CD)
1125 : LineCoverageIterator(CD, CD.begin()->Line) {}
1126
1127 LineCoverageIterator(const CoverageData &CD, unsigned Line)
1128 : CD(CD), WrappedSegment(nullptr), Next(CD.begin()), Ended(false),
1129 Line(Line) {
1130 this->operator++();
1131 }
1132
1133 bool operator==(const LineCoverageIterator &R) const {
1134 return &CD == &R.CD && Next == R.Next && Ended == R.Ended;
1135 }
1136
1137 const LineCoverageStats &operator*() const { return Stats; }
1138
1139 LineCoverageIterator &operator++();
1140
1141 LineCoverageIterator getEnd() const {
1142 auto EndIt = *this;
1143 EndIt.Next = CD.end();
1144 EndIt.Ended = true;
1145 return EndIt;
1146 }
1147
1148 private:
1149 const CoverageData &CD;
1150 const CoverageSegment *WrappedSegment;
1151 std::vector<CoverageSegment>::const_iterator Next;
1152 bool Ended;
1153 unsigned Line;
1154 SmallVector<const CoverageSegment *, 4> Segments;
1155 LineCoverageStats Stats;
1156 };
1157
1158
1159 static inline iterator_range<LineCoverageIterator>
1160 getLineCoverageStats(const coverage::CoverageData &CD) {
1161 auto Begin = LineCoverageIterator(CD);
1162 auto End = Begin.getEnd();
1163 return make_range(Begin, End);
1164 }
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193 namespace accessors {
1194
1195
1196 template <class FuncRecordTy, llvm::endianness Endian>
1197 uint64_t getFuncHash(const FuncRecordTy *Record) {
1198 return support::endian::byte_swap<uint64_t, Endian>(Record->FuncHash);
1199 }
1200
1201
1202 template <class FuncRecordTy, llvm::endianness Endian>
1203 uint64_t getDataSize(const FuncRecordTy *Record) {
1204 return support::endian::byte_swap<uint32_t, Endian>(Record->DataSize);
1205 }
1206
1207
1208 template <class FuncRecordTy, llvm::endianness Endian>
1209 uint64_t getFuncNameRef(const FuncRecordTy *Record) {
1210 return support::endian::byte_swap<uint64_t, Endian>(Record->NameRef);
1211 }
1212
1213
1214
1215 template <class FuncRecordTy, llvm::endianness Endian>
1216 Error getFuncNameViaRef(const FuncRecordTy *Record,
1217 InstrProfSymtab &ProfileNames, StringRef &FuncName) {
1218 uint64_t NameRef = getFuncNameRef<FuncRecordTy, Endian>(Record);
1219 FuncName = ProfileNames.getFuncOrVarName(NameRef);
1220 return Error::success();
1221 }
1222
1223
1224
1225
1226 template <class FuncRecordTy, llvm::endianness Endian>
1227 StringRef getCoverageMappingOutOfLine(const FuncRecordTy *Record,
1228 const char *MappingBuf) {
1229 return {MappingBuf, size_t(getDataSize<FuncRecordTy, Endian>(Record))};
1230 }
1231
1232
1233
1234 template <class FuncRecordTy, llvm::endianness Endian>
1235 std::pair<const char *, const FuncRecordTy *>
1236 advanceByOneOutOfLine(const FuncRecordTy *Record, const char *MappingBuf) {
1237 return {MappingBuf + getDataSize<FuncRecordTy, Endian>(Record), Record + 1};
1238 }
1239
1240 }
1241
1242 LLVM_PACKED_START
1243 template <class IntPtrT>
1244 struct CovMapFunctionRecordV1 {
1245 using ThisT = CovMapFunctionRecordV1<IntPtrT>;
1246
1247 #define COVMAP_V1
1248 #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name;
1249 #include "llvm/ProfileData/InstrProfData.inc"
1250 #undef COVMAP_V1
1251 CovMapFunctionRecordV1() = delete;
1252
1253 template <llvm::endianness Endian> uint64_t getFuncHash() const {
1254 return accessors::getFuncHash<ThisT, Endian>(this);
1255 }
1256
1257 template <llvm::endianness Endian> uint64_t getDataSize() const {
1258 return accessors::getDataSize<ThisT, Endian>(this);
1259 }
1260
1261
1262 template <llvm::endianness Endian> IntPtrT getFuncNameRef() const {
1263 return support::endian::byte_swap<IntPtrT, Endian>(NamePtr);
1264 }
1265
1266
1267 template <llvm::endianness Endian>
1268 Error getFuncName(InstrProfSymtab &ProfileNames, StringRef &FuncName) const {
1269 IntPtrT NameRef = getFuncNameRef<Endian>();
1270 uint32_t NameS = support::endian::byte_swap<uint32_t, Endian>(NameSize);
1271 FuncName = ProfileNames.getFuncName(NameRef, NameS);
1272 if (NameS && FuncName.empty())
1273 return make_error<CoverageMapError>(coveragemap_error::malformed,
1274 "function name is empty");
1275 return Error::success();
1276 }
1277
1278 template <llvm::endianness Endian>
1279 std::pair<const char *, const ThisT *>
1280 advanceByOne(const char *MappingBuf) const {
1281 return accessors::advanceByOneOutOfLine<ThisT, Endian>(this, MappingBuf);
1282 }
1283
1284 template <llvm::endianness Endian> uint64_t getFilenamesRef() const {
1285 llvm_unreachable("V1 function format does not contain a filenames ref");
1286 }
1287
1288 template <llvm::endianness Endian>
1289 StringRef getCoverageMapping(const char *MappingBuf) const {
1290 return accessors::getCoverageMappingOutOfLine<ThisT, Endian>(this,
1291 MappingBuf);
1292 }
1293 };
1294
1295 struct CovMapFunctionRecordV2 {
1296 using ThisT = CovMapFunctionRecordV2;
1297
1298 #define COVMAP_V2
1299 #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name;
1300 #include "llvm/ProfileData/InstrProfData.inc"
1301 #undef COVMAP_V2
1302 CovMapFunctionRecordV2() = delete;
1303
1304 template <llvm::endianness Endian> uint64_t getFuncHash() const {
1305 return accessors::getFuncHash<ThisT, Endian>(this);
1306 }
1307
1308 template <llvm::endianness Endian> uint64_t getDataSize() const {
1309 return accessors::getDataSize<ThisT, Endian>(this);
1310 }
1311
1312 template <llvm::endianness Endian> uint64_t getFuncNameRef() const {
1313 return accessors::getFuncNameRef<ThisT, Endian>(this);
1314 }
1315
1316 template <llvm::endianness Endian>
1317 Error getFuncName(InstrProfSymtab &ProfileNames, StringRef &FuncName) const {
1318 return accessors::getFuncNameViaRef<ThisT, Endian>(this, ProfileNames,
1319 FuncName);
1320 }
1321
1322 template <llvm::endianness Endian>
1323 std::pair<const char *, const ThisT *>
1324 advanceByOne(const char *MappingBuf) const {
1325 return accessors::advanceByOneOutOfLine<ThisT, Endian>(this, MappingBuf);
1326 }
1327
1328 template <llvm::endianness Endian> uint64_t getFilenamesRef() const {
1329 llvm_unreachable("V2 function format does not contain a filenames ref");
1330 }
1331
1332 template <llvm::endianness Endian>
1333 StringRef getCoverageMapping(const char *MappingBuf) const {
1334 return accessors::getCoverageMappingOutOfLine<ThisT, Endian>(this,
1335 MappingBuf);
1336 }
1337 };
1338
1339 struct CovMapFunctionRecordV3 {
1340 using ThisT = CovMapFunctionRecordV3;
1341
1342 #define COVMAP_V3
1343 #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name;
1344 #include "llvm/ProfileData/InstrProfData.inc"
1345 #undef COVMAP_V3
1346 CovMapFunctionRecordV3() = delete;
1347
1348 template <llvm::endianness Endian> uint64_t getFuncHash() const {
1349 return accessors::getFuncHash<ThisT, Endian>(this);
1350 }
1351
1352 template <llvm::endianness Endian> uint64_t getDataSize() const {
1353 return accessors::getDataSize<ThisT, Endian>(this);
1354 }
1355
1356 template <llvm::endianness Endian> uint64_t getFuncNameRef() const {
1357 return accessors::getFuncNameRef<ThisT, Endian>(this);
1358 }
1359
1360 template <llvm::endianness Endian>
1361 Error getFuncName(InstrProfSymtab &ProfileNames, StringRef &FuncName) const {
1362 return accessors::getFuncNameViaRef<ThisT, Endian>(this, ProfileNames,
1363 FuncName);
1364 }
1365
1366
1367 template <llvm::endianness Endian> uint64_t getFilenamesRef() const {
1368 return support::endian::byte_swap<uint64_t, Endian>(FilenamesRef);
1369 }
1370
1371
1372
1373 template <llvm::endianness Endian>
1374 StringRef getCoverageMapping(const char *) const {
1375 return StringRef(&CoverageMapping, getDataSize<Endian>());
1376 }
1377
1378
1379
1380 template <llvm::endianness Endian>
1381 std::pair<const char *, const CovMapFunctionRecordV3 *>
1382 advanceByOne(const char *) const {
1383 assert(isAddrAligned(Align(8), this) && "Function record not aligned");
1384 const char *Next = ((const char *)this) + sizeof(CovMapFunctionRecordV3) -
1385 sizeof(char) + getDataSize<Endian>();
1386
1387
1388 Next += offsetToAlignedAddr(Next, Align(8));
1389 return {nullptr, reinterpret_cast<const CovMapFunctionRecordV3 *>(Next)};
1390 }
1391 };
1392
1393
1394
1395 struct CovMapHeader {
1396 #define COVMAP_HEADER(Type, LLVMType, Name, Init) Type Name;
1397 #include "llvm/ProfileData/InstrProfData.inc"
1398 template <llvm::endianness Endian> uint32_t getNRecords() const {
1399 return support::endian::byte_swap<uint32_t, Endian>(NRecords);
1400 }
1401
1402 template <llvm::endianness Endian> uint32_t getFilenamesSize() const {
1403 return support::endian::byte_swap<uint32_t, Endian>(FilenamesSize);
1404 }
1405
1406 template <llvm::endianness Endian> uint32_t getCoverageSize() const {
1407 return support::endian::byte_swap<uint32_t, Endian>(CoverageSize);
1408 }
1409
1410 template <llvm::endianness Endian> uint32_t getVersion() const {
1411 return support::endian::byte_swap<uint32_t, Endian>(Version);
1412 }
1413 };
1414
1415 LLVM_PACKED_END
1416
1417 enum CovMapVersion {
1418 Version1 = 0,
1419
1420
1421
1422 Version2 = 1,
1423
1424
1425 Version3 = 2,
1426
1427 Version4 = 3,
1428
1429 Version5 = 4,
1430
1431
1432 Version6 = 5,
1433
1434 Version7 = 6,
1435
1436 CurrentVersion = INSTR_PROF_COVMAP_VERSION
1437 };
1438
1439
1440 constexpr uint64_t TestingFormatMagic = 0x6d766f636d766c6c;
1441
1442 enum class TestingFormatVersion : uint64_t {
1443
1444
1445 Version1 = 0x6174616474736574,
1446
1447
1448 Version2 = 1,
1449
1450 CurrentVersion = Version2
1451 };
1452
1453 template <int CovMapVersion, class IntPtrT> struct CovMapTraits {
1454 using CovMapFuncRecordType = CovMapFunctionRecordV3;
1455 using NameRefType = uint64_t;
1456 };
1457
1458 template <class IntPtrT> struct CovMapTraits<CovMapVersion::Version3, IntPtrT> {
1459 using CovMapFuncRecordType = CovMapFunctionRecordV2;
1460 using NameRefType = uint64_t;
1461 };
1462
1463 template <class IntPtrT> struct CovMapTraits<CovMapVersion::Version2, IntPtrT> {
1464 using CovMapFuncRecordType = CovMapFunctionRecordV2;
1465 using NameRefType = uint64_t;
1466 };
1467
1468 template <class IntPtrT> struct CovMapTraits<CovMapVersion::Version1, IntPtrT> {
1469 using CovMapFuncRecordType = CovMapFunctionRecordV1<IntPtrT>;
1470 using NameRefType = IntPtrT;
1471 };
1472
1473 }
1474
1475
1476 template<> struct DenseMapInfo<coverage::CounterExpression> {
1477 static inline coverage::CounterExpression getEmptyKey() {
1478 using namespace coverage;
1479
1480 return CounterExpression(CounterExpression::ExprKind::Subtract,
1481 Counter::getCounter(~0U),
1482 Counter::getCounter(~0U));
1483 }
1484
1485 static inline coverage::CounterExpression getTombstoneKey() {
1486 using namespace coverage;
1487
1488 return CounterExpression(CounterExpression::ExprKind::Add,
1489 Counter::getCounter(~0U),
1490 Counter::getCounter(~0U));
1491 }
1492
1493 static unsigned getHashValue(const coverage::CounterExpression &V) {
1494 return static_cast<unsigned>(
1495 hash_combine(V.Kind, V.LHS.getKind(), V.LHS.getCounterID(),
1496 V.RHS.getKind(), V.RHS.getCounterID()));
1497 }
1498
1499 static bool isEqual(const coverage::CounterExpression &LHS,
1500 const coverage::CounterExpression &RHS) {
1501 return LHS.Kind == RHS.Kind && LHS.LHS == RHS.LHS && LHS.RHS == RHS.RHS;
1502 }
1503 };
1504
1505 }
1506
1507 #endif