41#include <system_error>
46using namespace coverage;
48#define DEBUG_TYPE "coverage-mapping"
51 auto It = ExpressionIndices.find(E);
52 if (It != ExpressionIndices.end())
54 unsigned I = Expressions.size();
55 Expressions.push_back(E);
56 ExpressionIndices[E] =
I;
60void CounterExpressionBuilder::extractTerms(
Counter C,
int Factor,
62 switch (
C.getKind()) {
69 const auto &E = Expressions[
C.getExpressionID()];
70 extractTerms(E.LHS, Factor, Terms);
77Counter CounterExpressionBuilder::simplify(
Counter ExpressionTree) {
80 extractTerms(ExpressionTree, +1, Terms);
84 if (Terms.
size() == 0)
88 llvm::sort(Terms, [](
const Term &LHS,
const Term &RHS) {
89 return LHS.CounterID <
RHS.CounterID;
93 auto Prev = Terms.
begin();
94 for (
auto I = Prev + 1, E = Terms.
end();
I != E; ++
I) {
95 if (
I->CounterID == Prev->CounterID) {
96 Prev->Factor +=
I->Factor;
107 for (
auto T : Terms) {
110 for (
int I = 0;
I <
T.Factor; ++
I)
119 for (
auto T : Terms) {
122 for (
int I = 0;
I < -
T.Factor; ++
I)
131 return Simplify ?
simplify(Cnt) : Cnt;
137 return Simplify ?
simplify(Cnt) : Cnt;
141 switch (
C.getKind()) {
146 OS <<
'#' <<
C.getCounterID();
149 if (
C.getExpressionID() >= Expressions.size())
151 const auto &E = Expressions[
C.getExpressionID()];
160 if (CounterValues.
empty())
163 if (
auto E =
Value.takeError()) {
178 } VisitCount = KNeverVisited;
181 std::stack<StackElem> CounterStack;
182 CounterStack.push({
C});
184 int64_t LastPoppedValue;
186 while (!CounterStack.empty()) {
187 StackElem &Current = CounterStack.top();
189 switch (Current.ICounter.getKind()) {
195 if (Current.ICounter.getCounterID() >= CounterValues.
size())
197 LastPoppedValue = CounterValues[Current.ICounter.getCounterID()];
201 if (Current.ICounter.getExpressionID() >= Expressions.size())
203 const auto &E = Expressions[Current.ICounter.getExpressionID()];
204 if (Current.VisitCount == StackElem::KNeverVisited) {
205 CounterStack.push(StackElem{E.LHS});
206 Current.VisitCount = StackElem::KVisitedOnce;
207 }
else if (Current.VisitCount == StackElem::KVisitedOnce) {
208 Current.LHS = LastPoppedValue;
209 CounterStack.push(StackElem{E.RHS});
210 Current.VisitCount = StackElem::KVisitedTwice;
212 int64_t
LHS = Current.LHS;
213 int64_t
RHS = LastPoppedValue;
223 return LastPoppedValue;
228 : Indices(NextIDs.
size()) {
230 auto N = NextIDs.
size();
232 for (
unsigned ID = 0;
ID <
N; ++
ID) {
233 for (
unsigned C = 0;
C < 2; ++
C) {
237 auto NextID = NextIDs[
ID][
C];
238 Nodes[
ID].NextIDs[
C] = NextID;
240 ++Nodes[NextID].InCount;
254 assert(Nodes[0].InCount == 0);
260 auto IID = Q.
begin();
263 auto &Node = Nodes[
ID];
266 for (
unsigned I = 0;
I < 2; ++
I) {
267 auto NextID = Node.NextIDs[
I];
268 assert(NextID != 0 &&
"NextID should not point to the top");
271 Decisions.emplace_back(-Node.Width, Ord++,
ID,
I);
272 assert(Ord == Decisions.size());
277 auto &NextNode = Nodes[NextID];
278 assert(NextNode.InCount > 0);
283 auto NextWidth = int64_t(NextNode.Width) + Node.Width;
288 NextNode.Width = NextWidth;
292 if (--NextNode.InCount == 0)
301 for (
auto [NegWidth, Ord,
ID,
C] : Decisions) {
302 int Width = -NegWidth;
318 for (
const auto &Idxs :
Indices)
319 for (
auto Idx : Idxs)
329class NextIDsBuilder {
335 : NextIDs(Branches.
size()) {
339 for (
const auto *Branch : Branches) {
340 const auto &BranchParams = Branch->getBranchParams();
341 assert(SeenIDs.
insert(BranchParams.ID).second &&
"Duplicate CondID");
342 NextIDs[BranchParams.ID] = BranchParams.Conds;
364 unsigned NumConditions;
374 std::array<MCDCRecord::TestVectors, 2> ExecVectorsByCond;
381 unsigned NumExecVectorsF;
388 MCDCRecordProcessor(
const BitVector &Bitmap,
391 : NextIDsBuilder(Branches), TVIdxBuilder(this->NextIDs), Bitmap(Bitmap),
393 Branches(Branches), NumConditions(DecisionParams.NumConditions),
394 Folded(NumConditions,
false), IndependencePairs(NumConditions),
395 ExecVectors(ExecVectorsByCond[
false]) {}
406 TV.
set(
ID, MCDCCond);
407 auto NextID = NextIDs[
ID][MCDCCond];
408 auto NextTVIdx = TVIdx + Indices[
ID][MCDCCond];
409 assert(NextID == SavedNodes[
ID].NextIDs[MCDCCond]);
411 buildTestVector(TV, NextID, NextTVIdx);
415 assert(TVIdx < SavedNodes[
ID].Width);
416 assert(TVIdxs.
insert(NextTVIdx).second &&
"Duplicate TVIdx");
424 ExecVectorsByCond[MCDCCond].push_back({TV, MCDCCond});
433 void findExecutedTestVectors() {
439 buildTestVector(TV, 0, 0);
440 assert(TVIdxs.
size() ==
unsigned(NumTestVectors) &&
441 "TVIdxs wasn't fulfilled");
446 NumExecVectorsF = ExecVectors.
size();
447 auto &ExecVectorsT = ExecVectorsByCond[
true];
448 ExecVectors.
append(std::make_move_iterator(ExecVectorsT.begin()),
449 std::make_move_iterator(ExecVectorsT.end()));
456 void findIndependencePairs() {
457 unsigned NumTVs = ExecVectors.
size();
458 for (
unsigned I = NumExecVectorsF;
I < NumTVs; ++
I) {
459 const auto &[
A, ACond] = ExecVectors[
I];
461 for (
unsigned J = 0; J < NumExecVectorsF; ++J) {
462 const auto &[
B, BCond] = ExecVectors[J];
466 auto AB =
A.getDifferences(
B);
469 {
AB.find_first(), std::make_pair(J + 1,
I + 1)});
499 for (
const auto *
B : Branches) {
500 const auto &BranchParams =
B->getBranchParams();
501 PosToID[
I] = BranchParams.ID;
502 CondLoc[
I] =
B->startLoc();
503 Folded[
I++] = (
B->Count.isZero() &&
B->FalseCount.isZero());
507 findExecutedTestVectors();
511 findIndependencePairs();
515 std::move(IndependencePairs), std::move(Folded),
516 std::move(PosToID), std::move(CondLoc));
526 MCDCRecordProcessor MCDCProcessor(Bitmap,
Region, Branches);
527 return MCDCProcessor.processMCDCRecord();
538 } VisitCount = KNeverVisited;
541 std::stack<StackElem> CounterStack;
542 CounterStack.push({
C});
544 int64_t LastPoppedValue;
546 while (!CounterStack.empty()) {
547 StackElem &Current = CounterStack.top();
549 switch (Current.ICounter.getKind()) {
555 LastPoppedValue = Current.ICounter.getCounterID();
559 if (Current.ICounter.getExpressionID() >= Expressions.size()) {
563 const auto &E = Expressions[Current.ICounter.getExpressionID()];
564 if (Current.VisitCount == StackElem::KNeverVisited) {
565 CounterStack.push(StackElem{E.LHS});
566 Current.VisitCount = StackElem::KVisitedOnce;
567 }
else if (Current.VisitCount == StackElem::KVisitedOnce) {
568 Current.LHS = LastPoppedValue;
569 CounterStack.push(StackElem{E.RHS});
570 Current.VisitCount = StackElem::KVisitedTwice;
572 int64_t
LHS = Current.LHS;
573 int64_t
RHS = LastPoppedValue;
574 LastPoppedValue = std::max(
LHS,
RHS);
583 return LastPoppedValue;
586void FunctionRecordIterator::skipOtherFiles() {
587 while (Current != Records.end() && !Filename.empty() &&
588 Filename != Current->Filenames[0])
590 if (Current == Records.end())
597 auto RecordIt = FilenameHash2RecordIndices.find(FilenameHash);
598 if (RecordIt == FilenameHash2RecordIndices.end())
600 return RecordIt->second;
605 unsigned MaxCounterID = 0;
615 unsigned MaxBitmapIdx = 0;
616 unsigned NumConditions = 0;
623 const auto &DecisionParams =
Region.getDecisionParams();
624 if (MaxBitmapIdx <= DecisionParams.
BitmapIdx) {
630 return MaxBitmapIdx * CHAR_BIT + SizeInBits;
636class MCDCDecisionRecorder {
642 struct DecisionRecord {
665 : DecisionRegion(&Decision),
666 DecisionParams(Decision.getDecisionParams()),
667 DecisionStartLoc(Decision.startLoc()),
668 DecisionEndLoc(Decision.endLoc()) {
675 if (
R.FileID == DecisionRegion->
FileID &&
676 R.startLoc() >= DecisionStartLoc &&
R.endLoc() <= DecisionEndLoc)
680 return ExpandedFileIDs.
contains(
R.FileID);
708 if (ConditionID == 0)
709 MCDCBranches.
insert(MCDCBranches.
begin(), &Branch);
740 ~MCDCDecisionRecorder() {
741 assert(Decisions.
empty() &&
"All Decisions have not been resolved");
751 return Decision.recordExpansion(
Expansion);
755 using DecisionAndBranches =
764 std::optional<DecisionAndBranches>
767 for (
auto DecisionIter = Decisions.
begin(), DecisionEnd = Decisions.
end();
768 DecisionIter != DecisionEnd; ++DecisionIter)
769 switch (DecisionIter->addBranch(Branch)) {
770 case DecisionRecord::NotProcessed:
772 case DecisionRecord::Processed:
774 case DecisionRecord::Completed:
775 DecisionAndBranches
Result =
776 std::make_pair(DecisionIter->DecisionRegion,
777 std::move(DecisionIter->MCDCBranches));
778 Decisions.
erase(DecisionIter);
788Error CoverageMapping::loadFunctionRecord(
792 if (OrigFuncName.
empty())
794 "record function name is empty");
796 if (
Record.Filenames.empty())
803 std::vector<uint64_t> Counts;
805 Record.FunctionHash, Counts)) {
808 FuncHashMismatches.emplace_back(std::string(
Record.FunctionName),
813 return make_error<InstrProfError>(IPE);
816 Ctx.setCounts(Counts);
820 Record.FunctionHash, Bitmap)) {
823 FuncHashMismatches.emplace_back(std::string(
Record.FunctionName),
828 return make_error<InstrProfError>(IPE);
831 Ctx.setBitmap(std::move(Bitmap));
833 assert(!
Record.MappingRegions.empty() &&
"Function has no regions");
840 if (
Record.MappingRegions.size() == 1 &&
841 Record.MappingRegions[0].Count.isZero() && Counts[0] > 0)
844 MCDCDecisionRecorder MCDCDecisions;
850 MCDCDecisions.registerDecision(
Region);
854 if (
auto E = ExecutionCount.
takeError()) {
859 if (
auto E = AltExecutionCount.
takeError()) {
868 MCDCDecisions.recordExpansion(
Region);
880 auto MCDCDecision =
Result->first;
881 auto &MCDCBranches =
Result->second;
887 Ctx.evaluateMCDCRegion(*MCDCDecision, MCDCBranches);
888 if (
auto E =
Record.takeError()) {
900 if (!RecordProvenance[FilenamesHash].insert(
hash_value(OrigFuncName)).second)
903 Functions.push_back(std::move(
Function));
908 unsigned RecordIndex = Functions.size() - 1;
910 auto &RecordIndices = FilenameHash2RecordIndices[
hash_value(Filename)];
914 if (RecordIndices.empty() || RecordIndices.back() != RecordIndex)
915 RecordIndices.push_back(RecordIndex);
923Error CoverageMapping::loadFromReaders(
924 ArrayRef<std::unique_ptr<CoverageMappingReader>> CoverageReaders,
926 for (
const auto &CoverageReader : CoverageReaders) {
927 for (
auto RecordOrErr : *CoverageReader) {
928 if (
Error E = RecordOrErr.takeError())
930 const auto &
Record = *RecordOrErr;
939 ArrayRef<std::unique_ptr<CoverageMappingReader>> CoverageReaders,
941 auto Coverage = std::unique_ptr<CoverageMapping>(
new CoverageMapping());
942 if (
Error E = loadFromReaders(CoverageReaders, ProfileReader, *Coverage))
944 return std::move(Coverage);
953 return make_error<CoverageMapError>(CME.
get(), CME.
getMessage());
957Error CoverageMapping::loadFromFile(
962 Filename,
false,
false);
963 if (std::error_code EC = CovMappingBufOrErr.getError())
966 CovMappingBufOrErr.get()->getMemBufferRef();
971 CovMappingBufRef, Arch, Buffers, CompilationDir,
972 FoundBinaryIDs ? &BinaryIDs :
nullptr);
973 if (
Error E = CoverageReadersOrErr.takeError()) {
981 for (
auto &Reader : CoverageReadersOrErr.get())
983 if (FoundBinaryIDs && !Readers.
empty()) {
989 DataFound |= !Readers.
empty();
990 if (
Error E = loadFromReaders(Readers, ProfileReader, Coverage))
1000 if (
Error E = ProfileReaderOrErr.takeError())
1002 auto ProfileReader = std::move(ProfileReaderOrErr.get());
1003 auto Coverage = std::unique_ptr<CoverageMapping>(
new CoverageMapping());
1004 bool DataFound =
false;
1006 auto GetArch = [&](
size_t Idx) {
1009 if (Arches.
size() == 1)
1010 return Arches.
front();
1017 loadFromFile(File.value(), GetArch(File.index()), CompilationDir,
1018 *ProfileReader, *Coverage, DataFound, &FoundBinaryIDs))
1019 return std::move(E);
1023 std::vector<object::BuildID> ProfileBinaryIDs;
1028 if (!ProfileBinaryIDs.empty()) {
1030 return std::lexicographical_compare(
A.begin(),
A.end(),
B.begin(),
1034 std::set_difference(
1035 ProfileBinaryIDs.begin(), ProfileBinaryIDs.end(),
1036 FoundBinaryIDs.
begin(), FoundBinaryIDs.
end(),
1037 std::inserter(BinaryIDsToFetch, BinaryIDsToFetch.
end()), Compare);
1041 std::optional<std::string> PathOpt = BIDFetcher->
fetch(BinaryID);
1043 std::string Path = std::move(*PathOpt);
1045 if (
Error E = loadFromFile(Path, Arch, CompilationDir, *ProfileReader,
1046 *Coverage, DataFound))
1047 return std::move(E);
1048 }
else if (CheckBinaryIDs) {
1052 "Missing binary ID: " +
1053 llvm::toHex(BinaryID,
true)));
1060 join(ObjectFilenames.
begin(), ObjectFilenames.
end(),
", "),
1062 return std::move(Coverage);
1071class FunctionInstantiationSetCollector {
1072 using MapT = std::map<LineColPair, std::vector<const FunctionRecord *>>;
1073 MapT InstantiatedFunctions;
1078 while (
I !=
E &&
I->FileID != FileID)
1080 assert(
I !=
E &&
"function does not cover the given file");
1081 auto &Functions = InstantiatedFunctions[
I->startLoc()];
1085 MapT::iterator begin() {
return InstantiatedFunctions.begin(); }
1086 MapT::iterator end() {
return InstantiatedFunctions.end(); }
1089class SegmentBuilder {
1090 std::vector<CoverageSegment> &
Segments;
1093 SegmentBuilder(std::vector<CoverageSegment> &Segments) :
Segments(
Segments) {}
1100 bool IsRegionEntry,
bool EmitSkippedRegion =
false) {
1101 bool HasCount = !EmitSkippedRegion &&
1105 if (!
Segments.empty() && !IsRegionEntry && !EmitSkippedRegion) {
1107 if (
Last.HasCount == HasCount &&
Last.Count ==
Region.ExecutionCount &&
1108 !
Last.IsRegionEntry)
1113 Segments.emplace_back(StartLoc.first, StartLoc.second,
1114 Region.ExecutionCount, IsRegionEntry,
1117 Segments.emplace_back(StartLoc.first, StartLoc.second, IsRegionEntry);
1121 dbgs() <<
"Segment at " <<
Last.Line <<
":" <<
Last.Col
1122 <<
" (count = " <<
Last.Count <<
")"
1123 << (
Last.IsRegionEntry ?
", RegionEntry" :
"")
1124 << (!
Last.HasCount ?
", Skipped" :
"")
1125 << (
Last.IsGapRegion ?
", Gap" :
"") <<
"\n";
1134 void completeRegionsUntil(std::optional<LineColPair> Loc,
1135 unsigned FirstCompletedRegion) {
1138 auto CompletedRegionsIt = ActiveRegions.
begin() + FirstCompletedRegion;
1139 std::stable_sort(CompletedRegionsIt, ActiveRegions.
end(),
1141 return L->endLoc() < R->endLoc();
1145 for (
unsigned I = FirstCompletedRegion + 1,
E = ActiveRegions.
size();
I <
E;
1147 const auto *CompletedRegion = ActiveRegions[
I];
1148 assert((!Loc || CompletedRegion->endLoc() <= *Loc) &&
1149 "Completed region ends after start of new region");
1151 const auto *PrevCompletedRegion = ActiveRegions[
I - 1];
1152 auto CompletedSegmentLoc = PrevCompletedRegion->endLoc();
1155 if (Loc && CompletedSegmentLoc == *Loc)
1160 if (CompletedSegmentLoc == CompletedRegion->endLoc())
1164 for (
unsigned J =
I + 1; J <
E; ++J)
1165 if (CompletedRegion->endLoc() == ActiveRegions[J]->endLoc())
1166 CompletedRegion = ActiveRegions[J];
1168 startSegment(*CompletedRegion, CompletedSegmentLoc,
false);
1172 if (FirstCompletedRegion &&
Last->endLoc() != *Loc) {
1175 startSegment(*ActiveRegions[FirstCompletedRegion - 1],
Last->endLoc(),
1177 }
else if (!FirstCompletedRegion && (!Loc || *Loc !=
Last->endLoc())) {
1180 startSegment(*
Last,
Last->endLoc(),
false,
true);
1184 ActiveRegions.
erase(CompletedRegionsIt, ActiveRegions.
end());
1188 for (
const auto &CR :
enumerate(Regions)) {
1189 auto CurStartLoc = CR.value().startLoc();
1192 auto CompletedRegions =
1193 std::stable_partition(ActiveRegions.
begin(), ActiveRegions.
end(),
1195 return !(Region->endLoc() <= CurStartLoc);
1197 if (CompletedRegions != ActiveRegions.
end()) {
1198 unsigned FirstCompletedRegion =
1199 std::distance(ActiveRegions.
begin(), CompletedRegions);
1200 completeRegionsUntil(CurStartLoc, FirstCompletedRegion);
1206 if (CurStartLoc == CR.value().endLoc()) {
1209 const bool Skipped =
1210 (CR.index() + 1) == Regions.
size() ||
1212 startSegment(ActiveRegions.
empty() ? CR.value() : *ActiveRegions.
back(),
1213 CurStartLoc, !GapRegion, Skipped);
1216 if (Skipped && !ActiveRegions.
empty())
1217 startSegment(*ActiveRegions.
back(), CurStartLoc,
false);
1220 if (CR.index() + 1 == Regions.
size() ||
1221 CurStartLoc != Regions[CR.index() + 1].startLoc()) {
1224 startSegment(CR.value(), CurStartLoc, !GapRegion);
1232 if (!ActiveRegions.
empty())
1233 completeRegionsUntil(std::nullopt, 0);
1239 if (
LHS.startLoc() !=
RHS.startLoc())
1240 return LHS.startLoc() <
RHS.startLoc();
1243 return RHS.endLoc() <
LHS.endLoc();
1253 "Unexpected order of region kind values");
1254 return LHS.Kind <
RHS.Kind;
1261 if (Regions.
empty())
1263 auto Active = Regions.
begin();
1264 auto End = Regions.
end();
1265 for (
auto I = Regions.
begin() + 1;
I !=
End; ++
I) {
1266 if (Active->startLoc() !=
I->startLoc() ||
1267 Active->endLoc() !=
I->endLoc()) {
1286 if (
I->Kind == Active->Kind) {
1287 assert(
I->HasSingleByteCoverage == Active->HasSingleByteCoverage &&
1288 "Regions are generated in different coverage modes");
1289 if (
I->HasSingleByteCoverage)
1290 Active->ExecutionCount = Active->ExecutionCount ||
I->ExecutionCount;
1292 Active->ExecutionCount +=
I->ExecutionCount;
1295 return Regions.
drop_back(std::distance(++Active,
End));
1300 static std::vector<CoverageSegment>
1302 std::vector<CoverageSegment>
Segments;
1303 SegmentBuilder Builder(Segments);
1305 sortNestedRegions(Regions);
1309 dbgs() <<
"Combined regions:\n";
1310 for (
const auto &CR : CombinedRegions)
1311 dbgs() <<
" " << CR.LineStart <<
":" << CR.ColumnStart <<
" -> "
1312 << CR.LineEnd <<
":" << CR.ColumnEnd
1313 <<
" (count=" << CR.ExecutionCount <<
")\n";
1316 Builder.buildSegmentsImpl(CombinedRegions);
1322 if (!(
L.Line <
R.Line) && !(
L.Line ==
R.Line &&
L.Col <
R.Col)) {
1323 if (
L.Line ==
R.Line &&
L.Col ==
R.Col && !
L.HasCount)
1326 <<
" followed by " <<
R.Line <<
":" <<
R.Col <<
"\n");
1327 assert(
false &&
"Coverage segments not unique or sorted");
1339 std::vector<StringRef> Filenames;
1340 for (
const auto &
Function : getCoveredFunctions())
1343 auto Last = std::unique(Filenames.begin(), Filenames.end());
1344 Filenames.erase(
Last, Filenames.end());
1352 if (SourceFile ==
Function.Filenames[
I])
1353 FilenameEquivalence[
I] =
true;
1354 return FilenameEquivalence;
1358static std::optional<unsigned>
1361 for (
const auto &CR :
Function.CountedRegions)
1363 IsNotExpandedFile[CR.ExpandedFileID] =
false;
1366 return std::nullopt;
1373static std::optional<unsigned>
1376 if (
I && SourceFile ==
Function.Filenames[*
I])
1378 return std::nullopt;
1387 std::vector<CountedRegion> Regions;
1392 getImpreciseRecordIndicesForFilename(Filename);
1393 for (
unsigned RecordIndex : RecordIndices) {
1397 for (
const auto &CR :
Function.CountedRegions)
1398 if (FileIDs.test(CR.FileID)) {
1399 Regions.push_back(CR);
1401 FileCoverage.Expansions.emplace_back(CR,
Function);
1404 for (
const auto &CR :
Function.CountedBranchRegions)
1405 if (FileIDs.test(CR.FileID) && (CR.FileID == CR.ExpandedFileID))
1406 FileCoverage.BranchRegions.push_back(CR);
1408 for (
const auto &MR :
Function.MCDCRecords)
1409 if (FileIDs.test(MR.getDecisionRegion().FileID))
1410 FileCoverage.MCDCRecords.push_back(MR);
1413 LLVM_DEBUG(
dbgs() <<
"Emitting segments for file: " << Filename <<
"\n");
1414 FileCoverage.Segments = SegmentBuilder::buildSegments(Regions);
1416 return FileCoverage;
1419std::vector<InstantiationGroup>
1421 FunctionInstantiationSetCollector InstantiationSetCollector;
1425 getImpreciseRecordIndicesForFilename(Filename);
1426 for (
unsigned RecordIndex : RecordIndices) {
1431 InstantiationSetCollector.insert(
Function, *MainFileID);
1434 std::vector<InstantiationGroup> Result;
1435 for (
auto &InstantiationSet : InstantiationSetCollector) {
1437 InstantiationSet.first.second,
1438 std::move(InstantiationSet.second)};
1439 Result.emplace_back(std::move(IG));
1451 std::vector<CountedRegion> Regions;
1452 for (
const auto &CR :
Function.CountedRegions)
1453 if (CR.FileID == *MainFileID) {
1454 Regions.push_back(CR);
1456 FunctionCoverage.Expansions.emplace_back(CR,
Function);
1459 for (
const auto &CR :
Function.CountedBranchRegions)
1460 if (CR.FileID == *MainFileID)
1461 FunctionCoverage.BranchRegions.push_back(CR);
1464 for (
const auto &MR :
Function.MCDCRecords)
1465 if (MR.getDecisionRegion().FileID == *MainFileID)
1466 FunctionCoverage.MCDCRecords.push_back(MR);
1470 FunctionCoverage.Segments = SegmentBuilder::buildSegments(Regions);
1472 return FunctionCoverage;
1479 std::vector<CountedRegion> Regions;
1480 for (
const auto &CR :
Expansion.Function.CountedRegions)
1482 Regions.push_back(CR);
1484 ExpansionCoverage.Expansions.emplace_back(CR,
Expansion.Function);
1486 for (
const auto &CR :
Expansion.Function.CountedBranchRegions)
1489 ExpansionCoverage.BranchRegions.push_back(CR);
1493 ExpansionCoverage.Segments = SegmentBuilder::buildSegments(Regions);
1495 return ExpansionCoverage;
1498LineCoverageStats::LineCoverageStats(
1501 : ExecutionCount(0), HasMultipleRegions(
false), Mapped(
false), Line(Line),
1502 LineSegments(LineSegments), WrappedSegment(WrappedSegment) {
1504 unsigned MinRegionCount = 0;
1506 return !S->
IsGapRegion && S->HasCount && S->IsRegionEntry;
1508 for (
unsigned I = 0;
I < LineSegments.
size() && MinRegionCount < 2; ++
I)
1509 if (isStartOfRegion(LineSegments[
I]))
1512 bool StartOfSkippedRegion = !LineSegments.
empty() &&
1513 !LineSegments.
front()->HasCount &&
1514 LineSegments.
front()->IsRegionEntry;
1516 HasMultipleRegions = MinRegionCount > 1;
1518 !StartOfSkippedRegion &&
1519 ((WrappedSegment && WrappedSegment->
HasCount) || (MinRegionCount > 0));
1523 Mapped |= std::any_of(
1524 LineSegments.
begin(), LineSegments.
end(),
1525 [](
const auto *Seq) { return Seq->IsRegionEntry && Seq->HasCount; });
1534 ExecutionCount = WrappedSegment->
Count;
1535 if (!MinRegionCount)
1537 for (
const auto *LS : LineSegments)
1538 if (isStartOfRegion(LS))
1539 ExecutionCount = std::max(ExecutionCount, LS->Count);
1543 if (Next == CD.
end()) {
1548 if (Segments.size())
1549 WrappedSegment = Segments.back();
1551 while (Next != CD.
end() && Next->Line == Line)
1552 Segments.push_back(&*Next++);
1559 const std::string &ErrMsg =
"") {
1568 OS <<
"end of File";
1571 OS <<
"no coverage data found";
1574 OS <<
"unsupported coverage format version";
1577 OS <<
"truncated coverage data";
1580 OS <<
"malformed coverage data";
1583 OS <<
"failed to decompress coverage data (zlib)";
1586 OS <<
"`-arch` specifier is invalid or missing for universal binary";
1591 if (!ErrMsg.empty())
1592 OS <<
": " << ErrMsg;
1602class CoverageMappingErrorCategoryType :
public std::error_category {
1603 const char *
name()
const noexcept
override {
return "llvm.coveragemap"; }
1604 std::string message(
int IE)
const override {
1616 static CoverageMappingErrorCategoryType ErrorCategory;
1617 return ErrorCategory;
This file declares a library for handling Build IDs and using them to find debug info.
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static SmallBitVector gatherFileIDs(StringRef SourceFile, const FunctionRecord &Function)
static std::optional< unsigned > findMainViewFileID(const FunctionRecord &Function)
Return the ID of the file where the definition of the function is located.
static bool isExpansion(const CountedRegion &R, unsigned FileID)
static Error handleMaybeNoDataFoundError(Error E)
static std::string getCoverageMapErrString(coveragemap_error Err, const std::string &ErrMsg="")
static unsigned getMaxBitmapSize(const CounterMappingContext &Ctx, const CoverageMappingRecord &Record)
Returns the bit count.
static unsigned getMaxCounterID(const CounterMappingContext &Ctx, const CoverageMappingRecord &Record)
Returns the sub type a function will return at a given Idx Should correspond to the result type of an ExtractValue instruction executed with just that one unsigned Idx
This file defines the DenseMap class.
static bool dominates(InstrPosIndexes &PosIndexes, const MachineInstr &A, const MachineInstr &B)
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
This file implements the SmallBitVector class.
This file defines the SmallVector class.
Defines the virtual file system interface vfs::FileSystem.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
const T & front() const
front - Get the first element.
size_t size() const
size - Get the array size.
bool empty() const
empty - Check if the array is empty.
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Implements a dense probed hash-table based set.
Lightweight error class with error context and mandatory checking.
static ErrorSuccess success()
Create a success value.
Tagged union holding either a T or a Error.
Error takeError()
Take ownership of the stored error.
Reader for the indexed binary instrprof format.
static Expected< std::unique_ptr< IndexedInstrProfReader > > create(const Twine &Path, vfs::FileSystem &FS, const Twine &RemappingPath="")
Factory method to create an indexed reader.
Error getFunctionBitmap(StringRef FuncName, uint64_t FuncHash, BitVector &Bitmap)
Fill Bitmap with the profile data for the given function name.
bool hasSingleByteCoverage() const override
Return true if the profile has single byte counters representing coverage.
Error getFunctionCounts(StringRef FuncName, uint64_t FuncHash, std::vector< uint64_t > &Counts)
Fill Counts with the profile data for the given function name.
Error readBinaryIds(std::vector< llvm::object::BuildID > &BinaryIds) override
Read a list of binary ids.
static std::pair< instrprof_error, std::string > take(Error E)
Consume an Error and return the raw enum value contained within it, and the optional error message.
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFileOrSTDIN(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, or open stdin if the Filename is "-".
MutableArrayRef - Represent a mutable reference to an array (0 or more elements consecutively in memo...
MutableArrayRef< T > drop_back(size_t N=1) const
This is a 'bitvector' (really, a variable-sized bit array), optimized for the case when the array is ...
int find_first() const
Returns the index of the first set bit, -1 if none of the bits are set.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
reference emplace_back(ArgTypes &&... Args)
iterator erase(const_iterator CI)
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
iterator insert(iterator I, T &&Elt)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
StringRef - Represent a constant reference to a string, i.e.
constexpr bool empty() const
empty - Check if the string is empty.
LLVM Value Representation.
static Expected< std::vector< std::unique_ptr< BinaryCoverageReader > > > create(MemoryBufferRef ObjectBuffer, StringRef Arch, SmallVectorImpl< std::unique_ptr< MemoryBuffer > > &ObjectFileBuffers, StringRef CompilationDir="", SmallVectorImpl< object::BuildIDRef > *BinaryIDs=nullptr)
Counter subtract(Counter LHS, Counter RHS, bool Simplify=true)
Return a counter that represents the expression that subtracts RHS from LHS.
Counter add(Counter LHS, Counter RHS, bool Simplify=true)
Return a counter that represents the expression that adds LHS and RHS.
A Counter mapping context is used to connect the counters, expressions and the obtained counter value...
Expected< MCDCRecord > evaluateMCDCRegion(const CounterMappingRegion &Region, ArrayRef< const CounterMappingRegion * > Branches)
Return an MCDC record that indicates executed test vectors and condition pairs.
Expected< int64_t > evaluate(const Counter &C) const
Return the number of times that a region of code associated with this counter was executed.
unsigned getMaxCounterID(const Counter &C) const
void dump(const Counter &C, raw_ostream &OS) const
Coverage information to be processed or displayed.
std::vector< CoverageSegment >::const_iterator end() const
std::string message() const override
Return the error message as a string.
coveragemap_error get() const
const std::string & getMessage() const
The mapping of profile information to coverage data.
std::vector< StringRef > getUniqueSourceFiles() const
Returns a lexicographically sorted, unique list of files that are covered.
CoverageData getCoverageForExpansion(const ExpansionRecord &Expansion) const
Get the coverage for an expansion within a coverage set.
CoverageData getCoverageForFunction(const FunctionRecord &Function) const
Get the coverage for a particular function.
std::vector< InstantiationGroup > getInstantiationGroups(StringRef Filename) const
Get the list of function instantiation groups in a particular file.
CoverageData getCoverageForFile(StringRef Filename) const
Get the coverage for a particular file.
static Expected< std::unique_ptr< CoverageMapping > > load(ArrayRef< std::unique_ptr< CoverageMappingReader > > CoverageReaders, IndexedInstrProfReader &ProfileReader)
Load the coverage mapping using the given readers.
Iterator over Functions, optionally filtered to a single file.
An instantiation group contains a FunctionRecord list, such that each record corresponds to a distinc...
An iterator over the LineCoverageStats objects for lines described by a CoverageData instance.
LineCoverageIterator & operator++()
Coverage statistics for a single line.
Emulate SmallVector<CondState> with a pair of BitVector.
auto getIndex() const
Equivalent to buildTestVector's Index.
void set(int I, CondState Val)
Set the condition Val at position I.
Compute TestVector Indices "TVIdx" from the Conds graph.
static constexpr auto HardMaxTVs
Hard limit of test vectors.
TVIdxBuilder(const SmallVectorImpl< ConditionIDs > &NextIDs, int Offset=0)
Calculate and assign Indices.
SmallVector< std::array< int, 2 > > Indices
Output: Index for TestVectors bitmap (These are not CondIDs)
int NumTestVectors
Output: The number of test vectors.
SmallVector< MCDCNode > SavedNodes
This is no longer needed after the assignment.
std::pair< iterator, bool > insert(const ValueT &V)
bool contains(const_arg_type_t< ValueT > V) const
Check if the set contains the given element.
BuildIDFetcher searches local cache directories for debug info.
virtual std::optional< std::string > fetch(BuildIDRef BuildID) const
Returns the path to the debug file with the given build ID.
This class implements an extremely fast bulk output stream that can only output to a stream.
A raw_ostream that writes to an std::string.
The virtual file system interface.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
@ C
The default llvm calling convention, compatible with C.
int16_t ConditionID
The ID for MCDCBranch.
std::array< ConditionID, 2 > ConditionIDs
const std::error_category & coveragemap_category()
@ invalid_or_missing_arch_specifier
std::pair< unsigned, unsigned > LineColPair
SmallVector< uint8_t, 10 > BuildID
A build ID in binary form.
This is an optimization pass for GlobalISel generic memory operations.
hash_code hash_value(const FixedPointSemantics &Val)
Error createFileError(const Twine &F, Error E)
Concatenate a source file path and/or name with an Error.
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
auto enumerate(FirstRange &&First, RestRanges &&...Rest)
Given two or more input ranges, returns a new range whose values are are tuples (A,...
StringRef getFuncNameWithoutPrefix(StringRef PGOFuncName, StringRef FileName="<unknown>")
Given a PGO function name, remove the filename prefix and return the original (static) function name.
Error handleErrors(Error E, HandlerTs &&... Hs)
Pass the ErrorInfo(s) contained in E to their respective handlers.
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
auto map_range(ContainerTy &&C, FuncTy F)
@ no_such_file_or_directory
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
auto reverse(ContainerTy &&C)
void sort(IteratorTy Start, IteratorTy End)
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
uint64_t alignTo(uint64_t Size, Align A)
Returns a multiple of A needed to store Size bytes.
Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
void consumeError(Error Err)
Consume a Error without doing anything.
hash_code hash_combine_range(InputIteratorT first, InputIteratorT last)
Compute a hash_code for a sequence of values.
Associates a source range with an execution count.
A Counter expression is a value that represents an arithmetic operation with two counters.
A Counter mapping region associates a source range with a specific counter.
@ ExpansionRegion
An ExpansionRegion represents a file expansion region that associates a source range with the expansi...
@ MCDCDecisionRegion
A DecisionRegion represents a top-level boolean expression and is associated with a variable length b...
@ MCDCBranchRegion
A Branch Region can be extended to include IDs to facilitate MC/DC.
@ SkippedRegion
A SkippedRegion represents a source range with code that was skipped by a preprocessor or similar mea...
@ GapRegion
A GapRegion is like a CodeRegion, but its count is only set as the line execution count when its the ...
@ CodeRegion
A CodeRegion associates some code with a counter.
A Counter is an abstract value that describes how to compute the execution count for a region of code...
static Counter getZero()
Return the counter that represents the number zero.
static Counter getCounter(unsigned CounterId)
Return the counter that corresponds to a specific profile counter.
static Counter getExpression(unsigned ExpressionId)
Return the counter that corresponds to a specific addition counter expression.
Coverage mapping information for a single function.
The execution count information starting at a point in a file.
bool HasCount
When false, the segment was uninstrumented or skipped.
uint64_t Count
The execution count, or zero if no count was recorded.
bool IsGapRegion
Whether this enters a gap region.
Coverage information for a macro expansion or #included file.
Code coverage information for a single function.
MCDC Record grouping all information together.
unsigned BitmapIdx
Byte Index of Bitmap Coverage Object for a Decision Region.
uint16_t NumConditions
Number of Conditions used for a Decision Region.