1//===- CoverageMapping.cpp - Code coverage mapping support ----------------===// 3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4// See https://llvm.org/LICENSE.txt for license information. 5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7//===----------------------------------------------------------------------===// 9// This file contains support for clang's and llvm's instrumentation based 12//===----------------------------------------------------------------------===// 42#include <system_error> 47using namespacecoverage;
49#define DEBUG_TYPE "coverage-mapping" 52auto [It, Inserted] = ExpressionIndices.try_emplace(E, Expressions.size());
54 Expressions.push_back(E);
58void CounterExpressionBuilder::extractTerms(
CounterC,
int Factor,
67constauto &E = Expressions[
C.getExpressionID()];
68 extractTerms(E.LHS, Factor, Terms);
75Counter CounterExpressionBuilder::simplify(
Counter ExpressionTree) {
76// Gather constant terms. 78 extractTerms(ExpressionTree, +1, Terms);
80// If there are no terms, this is just a zero. The algorithm below assumes at 85// Group the terms by counter ID. 86llvm::sort(Terms, [](
const Term &LHS,
const Term &RHS) {
87returnLHS.CounterID <
RHS.CounterID;
90// Combine terms by counter ID to eliminate counters that sum to zero. 91auto Prev = Terms.
begin();
92for (
autoI = Prev + 1, E = Terms.
end();
I != E; ++
I) {
93if (
I->CounterID == Prev->CounterID) {
94 Prev->Factor +=
I->Factor;
103// Create additions. We do this before subtractions to avoid constructs like 104// ((0 - X) + Y), as opposed to (Y - X). 108for (
intI = 0;
I <
T.Factor; ++
I)
116// Create subtractions. 120for (
intI = 0;
I < -
T.Factor; ++
I)
129return Simplify ?
simplify(Cnt) : Cnt;
135return Simplify ?
simplify(Cnt) : Cnt;
139// Replace C with the value found in Map even if C is Expression. 140if (
autoI = Map.find(
C);
I != Map.end())
143if (!
C.isExpression())
146auto CE = Expressions[
C.getExpressionID()];
147auto NewLHS =
subst(CE.LHS, Map);
148auto NewRHS =
subst(CE.RHS, Map);
150// Reconstruct Expression with induced subexpressions. 153C =
add(NewLHS, NewRHS);
164switch (
C.getKind()) {
169OS <<
'#' <<
C.getCounterID();
172if (
C.getExpressionID() >= Expressions.size())
174constauto &E = Expressions[
C.getExpressionID()];
183if (CounterValues.
empty())
186if (
auto E =
Value.takeError()) {
201 } VisitCount = KNeverVisited;
204 std::stack<StackElem> CounterStack;
205 CounterStack.push({
C});
207 int64_t LastPoppedValue;
209while (!CounterStack.empty()) {
210 StackElem &Current = CounterStack.top();
212switch (Current.ICounter.getKind()) {
218if (Current.ICounter.getCounterID() >= CounterValues.
size())
220 LastPoppedValue = CounterValues[Current.ICounter.getCounterID()];
224if (Current.ICounter.getExpressionID() >= Expressions.size())
226constauto &E = Expressions[Current.ICounter.getExpressionID()];
227if (Current.VisitCount == StackElem::KNeverVisited) {
228 CounterStack.push(StackElem{E.LHS});
229 Current.VisitCount = StackElem::KVisitedOnce;
230 }
elseif (Current.VisitCount == StackElem::KVisitedOnce) {
231 Current.LHS = LastPoppedValue;
232 CounterStack.push(StackElem{E.RHS});
233 Current.VisitCount = StackElem::KVisitedTwice;
235 int64_t
LHS = Current.LHS;
236 int64_t
RHS = LastPoppedValue;
246return LastPoppedValue;
249// Find an independence pair for each condition: 250// - The condition is true in one test and false in the other. 251// - The decision outcome is true one test and false in the other. 252// - All other conditions' values must be equal or marked as "don't care". 254if (IndependencePairs)
257 IndependencePairs.emplace();
259unsigned NumTVs = TV.
size();
260// Will be replaced to shorter expr. 261unsigned TVTrueIdx = std::distance(
264 [&](
autoI) { return (I.second == MCDCRecord::MCDC_True); })
267for (
unsignedI = TVTrueIdx;
I < NumTVs; ++
I) {
268constauto &[
A, ACond] = TV[
I];
270for (
unsigned J = 0; J < TVTrueIdx; ++J) {
271constauto &[
B, BCond] = TV[J];
273// If the two vectors differ in exactly one condition, ignoring DontCare 274// conditions, we have found an independence pair. 275auto AB =
A.getDifferences(
B);
277 IndependencePairs->insert(
278 {AB.find_first(), std::make_pair(J + 1,
I + 1)});
285 : Indices(NextIDs.
size()) {
286// Construct Nodes and set up each InCount 287autoN = NextIDs.
size();
289for (
unsignedID = 0;
ID <
N; ++
ID) {
290for (
unsignedC = 0;
C < 2; ++
C) {
294auto NextID = NextIDs[
ID][
C];
295 Nodes[
ID].NextIDs[
C] = NextID;
297 ++Nodes[NextID].InCount;
301// Sort key ordered by <-Width, Ord> 305unsigned/// Cond (0 or 1) 309// Traverse Nodes to assign Idx 311assert(Nodes[0].InCount == 0);
320auto &Node = Nodes[
ID];
323for (
unsignedI = 0;
I < 2; ++
I) {
324auto NextID = Node.NextIDs[
I];
325assert(NextID != 0 &&
"NextID should not point to the top");
328 Decisions.emplace_back(-Node.Width, Ord++,
ID,
I);
329assert(Ord == Decisions.size());
334auto &NextNode = Nodes[NextID];
335assert(NextNode.InCount > 0);
340auto NextWidth = int64_t(NextNode.Width) + Node.Width;
345 NextNode.Width = NextWidth;
347// Ready if all incomings are processed. 348// Or NextNode.Width hasn't been confirmed yet. 349if (--NextNode.InCount == 0)
356// Assign TestVector Indices in Decision Nodes 358for (
auto [NegWidth, Ord,
ID,
C] : Decisions) {
359int Width = -NegWidth;
384/// Construct this->NextIDs with Branches for TVIdxBuilder to use it 385/// before MCDCRecordProcessor(). 392 : NextIDs(Branches.
size()) {
396for (
constauto *Branch : Branches) {
397constauto &BranchParams = Branch->getBranchParams();
398assert(SeenIDs.
insert(BranchParams.ID).second &&
"Duplicate CondID");
399 NextIDs[BranchParams.ID] = BranchParams.Conds;
406 /// A bitmap representing the executed test vectors for a boolean expression. 407 /// Each index of the bitmap corresponds to a possible test vector. An index 408 /// with a bit value of '1' indicates that the corresponding Test Vector 409 /// identified by that index was executed. 412 /// Decision Region to which the ExecutedTestVectorBitmap applies. 416 /// Array of branch regions corresponding each conditions in the boolean 420 /// Total number of conditions in the boolean expression. 421unsigned NumConditions;
423 /// Vector used to track whether a condition is constant folded. 426 /// Mapping of calculated MC/DC Independence Pairs for each condition. 429 /// Storage for ExecVectors 430 /// ExecVectors is the alias of its 0th element. 431 std::array<MCDCRecord::TestVectors, 2> ExecVectorsByCond;
433 /// Actual executed Test Vectors for the boolean expression, based on 434 /// ExecutedTestVectorBitmap. 444 MCDCRecordProcessor(
constBitVector &Bitmap,
448 : NextIDsBuilder(Branches), TVIdxBuilder(this->NextIDs), Bitmap(Bitmap),
450 Branches(Branches), NumConditions(DecisionParams.NumConditions),
452 IndependencePairs(NumConditions), ExecVectors(ExecVectorsByCond[
false]),
453 IsVersion11(IsVersion11) {}
456// Walk the binary decision diagram and try assigning both false and true to 457// each node. When a terminal node (ID == 0) is reached, fill in the value in 464 TV.
set(
ID, MCDCCond);
465auto NextID = NextIDs[
ID][MCDCCond];
466auto NextTVIdx = TVIdx + Indices[
ID][MCDCCond];
467assert(NextID == SavedNodes[
ID].NextIDs[MCDCCond]);
469 buildTestVector(TV, NextID, NextTVIdx);
473assert(TVIdx < SavedNodes[
ID].Width);
474assert(TVIdxs.insert(NextTVIdx).second &&
"Duplicate TVIdx");
476if (!Bitmap[IsVersion11
477 ? DecisionParams.BitmapIdx * CHAR_BIT + TV.
getIndex()
478 : DecisionParams.BitmapIdx - NumTestVectors + NextTVIdx])
481// Copy the completed test vector to the vector of testvectors. 482// The final value (T,F) is equal to the last non-dontcare state on the 483// path (in a short-circuiting system). 484 ExecVectorsByCond[MCDCCond].push_back({TV, MCDCCond});
487// Reset back to DontCare. 491 /// Walk the bits in the bitmap. A bit set to '1' indicates that the test 492 /// vector at the corresponding index was executed during a test run. 493void findExecutedTestVectors() {
494// Walk the binary decision diagram to enumerate all possible test vectors. 495// We start at the root node (ID == 0) with all values being DontCare. 496// `TVIdx` starts with 0 and is in the traversal. 497// `Index` encodes the bitmask of true values and is initially 0. 499 buildTestVector(TV, 0, 0);
500assert(TVIdxs.size() ==
unsigned(NumTestVectors) &&
501"TVIdxs wasn't fulfilled");
503// Fill ExecVectors order by False items and True items. 504// ExecVectors is the alias of ExecVectorsByCond[false], so 505// Append ExecVectorsByCond[true] on it. 506auto &ExecVectorsT = ExecVectorsByCond[
true];
507 ExecVectors.append(std::make_move_iterator(ExecVectorsT.begin()),
508 std::make_move_iterator(ExecVectorsT.end()));
512 /// Process the MC/DC Record in order to produce a result for a boolean 513 /// expression. This process includes tracking the conditions that comprise 514 /// the decision region, calculating the list of all possible test vectors, 515 /// marking the executed test vectors, and then finding an Independence Pair 516 /// out of the executed test vectors for each condition in the boolean 517 /// expression. A condition is tracked to ensure that its ID can be mapped to 518 /// its ordinal position in the boolean expression. The condition's source 519 /// location is also tracked, as well as whether it is constant folded (in 520 /// which case it is excuded from the metric). 525// Walk the Record's BranchRegions (representing Conditions) in order to: 526// - Hash the condition based on its corresponding ID. This will be used to 527// calculate the test vectors. 528// - Keep a map of the condition's ordinal position (1, 2, 3, 4) to its 529// actual ID. This will be used to visualize the conditions in the 531// - Keep track of the condition source location. This will be used to 532// visualize where the condition is. 533// - Record whether the condition is constant folded so that we exclude it 534// from being measured. 536constauto &BranchParams =
B->getBranchParams();
537 PosToID[
I] = BranchParams.ID;
538 CondLoc[
I] =
B->startLoc();
539 Folded[
false][
I] =
B->FalseCount.isZero();
540 Folded[
true][
I] =
B->Count.isZero();
543// Using Profile Bitmap from runtime, mark the executed test vectors. 544 findExecutedTestVectors();
546// Record Test vectors, executed vectors, and independence pairs. 548 std::move(PosToID), std::move(CondLoc));
558 MCDCRecordProcessor MCDCProcessor(Bitmap,
Region, Branches, IsVersion11);
559return MCDCProcessor.processMCDCRecord();
570 } VisitCount = KNeverVisited;
573 std::stack<StackElem> CounterStack;
574 CounterStack.push({
C});
576 int64_t LastPoppedValue;
578while (!CounterStack.empty()) {
579 StackElem &Current = CounterStack.top();
581switch (Current.ICounter.getKind()) {
587 LastPoppedValue = Current.ICounter.getCounterID();
591if (Current.ICounter.getExpressionID() >= Expressions.size()) {
595constauto &E = Expressions[Current.ICounter.getExpressionID()];
596if (Current.VisitCount == StackElem::KNeverVisited) {
597 CounterStack.push(StackElem{E.LHS});
598 Current.VisitCount = StackElem::KVisitedOnce;
599 }
elseif (Current.VisitCount == StackElem::KVisitedOnce) {
600 Current.LHS = LastPoppedValue;
601 CounterStack.push(StackElem{E.RHS});
602 Current.VisitCount = StackElem::KVisitedTwice;
604 int64_t
LHS = Current.LHS;
605 int64_t
RHS = LastPoppedValue;
606 LastPoppedValue = std::max(
LHS,
RHS);
615return LastPoppedValue;
618void FunctionRecordIterator::skipOtherFiles() {
619while (Current != Records.end() && !Filename.empty() &&
620 Filename != Current->Filenames[0])
622if (Current == Records.end())
629auto RecordIt = FilenameHash2RecordIndices.find(FilenameHash);
630if (RecordIt == FilenameHash2RecordIndices.end())
632return RecordIt->second;
637unsigned MaxCounterID = 0;
644/// Returns the bit count 647unsigned MaxBitmapIdx = 0;
648unsigned NumConditions = 0;
649// Scan max(BitmapIdx). 650// Note that `<=` is used insted of `<`, because `BitmapIdx == 0` is valid 651// and `MaxBitmapIdx is `unsigned`. `BitmapIdx` is unique in the record. 655constauto &DecisionParams =
Region.getDecisionParams();
656if (MaxBitmapIdx <= DecisionParams.BitmapIdx) {
657 MaxBitmapIdx = DecisionParams.BitmapIdx;
658 NumConditions = DecisionParams.NumConditions;
663 MaxBitmapIdx = MaxBitmapIdx * CHAR_BIT +
671/// Collect Decisions, Branchs, and Expansions and associate them. 672classMCDCDecisionRecorder {
674 /// This holds the DecisionRegion and MCDCBranches under it. 675 /// Also traverses Expansion(s). 676 /// The Decision has the number of MCDCBranches and will complete 677 /// when it is filled with unique ConditionID of MCDCBranches. 678structDecisionRecord {
681 /// They are reflected from DecisionRegion for convenience. 686 /// This is passed to `MCDCRecordProcessor`, so this should be compatible 687 /// to`ArrayRef<const CounterMappingRegion *>`. 690 /// IDs that are stored in MCDCBranches 691 /// Complete when all IDs (1 to NumConditions) are met. 694 /// Set of IDs of Expansion(s) that are relevant to DecisionRegion 695 /// and its children (via expansions). 696 /// FileID pointed by ExpandedFileID is dedicated to the expansion, so 697 /// the location in the expansion doesn't matter. 701 : DecisionRegion(&Decision),
702 DecisionParams(Decision.getDecisionParams()),
703 DecisionStartLoc(Decision.startLoc()),
704 DecisionEndLoc(Decision.endLoc()) {
708 /// Determine whether DecisionRecord dominates `R`. 710// Determine whether `R` is included in `DecisionRegion`. 711if (
R.FileID == DecisionRegion->
FileID &&
712R.startLoc() >= DecisionStartLoc &&
R.endLoc() <= DecisionEndLoc)
715// Determine whether `R` is pointed by any of Expansions. 716return ExpandedFileIDs.
contains(
R.FileID);
720 NotProcessed = 0,
/// Irrelevant to this Decision 721 Processed,
/// Added to this Decision 722 Completed,
/// Added and filled this Decision 725 /// Add Branch into the Decision 726 /// \param Branch expects MCDCBranchRegion 727 /// \returns NotProcessed/Processed/Completed 742// Put `ID=0` in front of `MCDCBranches` for convenience 743// even if `MCDCBranches` is not topological. 745 MCDCBranches.
insert(MCDCBranches.
begin(), &Branch);
749// Mark `ID` as `assigned`. 752// `Completed` when `MCDCBranches` is full 757 /// Record Expansion if it is relevant to this Decision. 758 /// Each `Expansion` may nest. 759 /// \returns true if recorded. 770 /// Decisions in progress 771 /// DecisionRecord is added for each MCDCDecisionRegion. 772 /// DecisionRecord is removed when Decision is completed. 776 ~MCDCDecisionRecorder() {
777assert(Decisions.
empty() &&
"All Decisions have not been resolved");
780 /// Register Region and start recording. 787return Decision.recordExpansion(
Expansion);
791usingDecisionAndBranches =
796 /// Add MCDCBranchRegion to DecisionRecord. 797 /// \param Branch to be processed 798 /// \returns DecisionsAndBranches if DecisionRecord completed. 799 /// Or returns nullopt. 800 std::optional<DecisionAndBranches>
802// Seek each Decision and apply Region to it. 803for (
auto DecisionIter = Decisions.
begin(), DecisionEnd = Decisions.
end();
804 DecisionIter != DecisionEnd; ++DecisionIter)
805switch (DecisionIter->addBranch(Branch)) {
806case DecisionRecord::NotProcessed:
808case DecisionRecord::Processed:
810case DecisionRecord::Completed:
811 DecisionAndBranches
Result =
812 std::make_pair(DecisionIter->DecisionRegion,
813 std::move(DecisionIter->MCDCBranches));
814 Decisions.
erase(DecisionIter);
// No longer used. 824Error CoverageMapping::loadFunctionRecord(
828if (OrigFuncName.
empty())
830"record function name is empty");
832if (
Record.Filenames.empty())
839 std::vector<uint64_t> Counts;
841Record.FunctionHash, Counts)) {
844 FuncHashMismatches.emplace_back(std::string(
Record.FunctionName),
849return make_error<InstrProfError>(IPE);
852 Ctx.setCounts(Counts);
859Record.FunctionHash, Bitmap)) {
862 FuncHashMismatches.emplace_back(std::string(
Record.FunctionName),
867return make_error<InstrProfError>(IPE);
870 Ctx.setBitmap(std::move(Bitmap));
872assert(!
Record.MappingRegions.empty() &&
"Function has no regions");
874// This coverage record is a zero region for a function that's unused in 875// some TU, but used in a different TU. Ignore it. The coverage maps from the 876// the other TU will either be loaded (providing full region counts) or they 877// won't (in which case we don't unintuitively report functions as uncovered 878// when they have non-zero counts in the profile). 879if (
Record.MappingRegions.size() == 1 &&
880Record.MappingRegions[0].Count.isZero() && Counts[0] > 0)
883 MCDCDecisionRecorder MCDCDecisions;
886// MCDCDecisionRegion should be handled first since it overlaps with 889 MCDCDecisions.registerDecision(
Region);
893if (
auto E = ExecutionCount.
takeError()) {
898if (
auto E = AltExecutionCount.
takeError()) {
904// Record ExpansionRegion. 906 MCDCDecisions.recordExpansion(
Region);
910// Do nothing unless MCDCBranchRegion. 915if (!Result)
// Any Decision doesn't complete. 918auto MCDCDecision =
Result->first;
919auto &MCDCBranches =
Result->second;
921// Since the bitmap identifies the executed test vectors for an MC/DC 922// DecisionRegion, all of the information is now available to process. 923// This is where the bulk of the MC/DC progressing takes place. 925 Ctx.evaluateMCDCRegion(*MCDCDecision, MCDCBranches, IsVersion11);
926if (
auto E =
Record.takeError()) {
931// Save the MC/DC Record so that it can be visualized later. 935// Don't create records for (filenames, function) pairs we've already seen. 938if (!RecordProvenance[FilenamesHash].insert(
hash_value(OrigFuncName)).second)
941 Functions.push_back(std::move(
Function));
943// Performance optimization: keep track of the indices of the function records 944// which correspond to each filename. This can be used to substantially speed 945// up queries for coverage info in a file. 946unsigned RecordIndex = Functions.size() - 1;
948auto &RecordIndices = FilenameHash2RecordIndices[
hash_value(Filename)];
949// Note that there may be duplicates in the filename set for a function 950// record, because of e.g. macro expansions in the function in which both 951// the macro and the function are defined in the same file. 952if (RecordIndices.empty() || RecordIndices.back() != RecordIndex)
953 RecordIndices.push_back(RecordIndex);
959// This function is for memory optimization by shortening the lifetimes 960// of CoverageMappingReader instances. 961Error CoverageMapping::loadFromReaders(
962ArrayRef<std::unique_ptr<CoverageMappingReader>> CoverageReaders,
967for (
constauto &CoverageReader : CoverageReaders) {
968for (
auto RecordOrErr : *CoverageReader) {
969if (
Error E = RecordOrErr.takeError())
971constauto &
Record = *RecordOrErr;
980ArrayRef<std::unique_ptr<CoverageMappingReader>> CoverageReaders,
983if (
Error E = loadFromReaders(CoverageReaders, ProfileReader, *Coverage))
985return std::move(Coverage);
988// If E is a no_data_found error, returns success. Otherwise returns E. 994return make_error<CoverageMapError>(CME.
get(), CME.
getMessage());
998Error CoverageMapping::loadFromFile(
1003 Filename,
/*IsText=*/false,
/*RequiresNullTerminator=*/false);
1004if (std::error_code EC = CovMappingBufOrErr.getError())
1007 CovMappingBufOrErr.get()->getMemBufferRef();
1012 CovMappingBufRef, Arch, Buffers, CompilationDir,
1013 FoundBinaryIDs ? &BinaryIDs :
nullptr);
1014if (
Error E = CoverageReadersOrErr.takeError()) {
1022for (
auto &Reader : CoverageReadersOrErr.get())
1024if (FoundBinaryIDs && !Readers.
empty()) {
1030 DataFound |= !Readers.
empty();
1031if (
Error E = loadFromReaders(Readers, ProfileReader, Coverage))
1041if (
Error E = ProfileReaderOrErr.takeError())
1043auto ProfileReader = std::move(ProfileReaderOrErr.get());
1044auto Coverage = std::unique_ptr<CoverageMapping>(
newCoverageMapping());
1045bool DataFound =
false;
1047auto GetArch = [&](
size_tIdx) {
1050if (Arches.
size() == 1)
1051return Arches.
front();
1058 loadFromFile(File.value(), GetArch(File.index()), CompilationDir,
1059 *ProfileReader, *Coverage, DataFound, &FoundBinaryIDs))
1064 std::vector<object::BuildID> ProfileBinaryIDs;
1069if (!ProfileBinaryIDs.empty()) {
1071return std::lexicographical_compare(
A.begin(),
A.end(),
B.begin(),
1075 std::set_difference(
1076 ProfileBinaryIDs.begin(), ProfileBinaryIDs.end(),
1077 FoundBinaryIDs.
begin(), FoundBinaryIDs.
end(),
1078 std::inserter(BinaryIDsToFetch, BinaryIDsToFetch.
end()), Compare);
1082 std::optional<std::string> PathOpt = BIDFetcher->
fetch(BinaryID);
1084 std::string Path = std::move(*PathOpt);
1086if (
Error E = loadFromFile(Path, Arch, CompilationDir, *ProfileReader,
1087 *Coverage, DataFound))
1089 }
elseif (CheckBinaryIDs) {
1093"Missing binary ID: " +
1094 llvm::toHex(BinaryID,
/*LowerCase=*/true)));
1101 join(ObjectFilenames.
begin(), ObjectFilenames.
end(),
", "),
1103return std::move(Coverage);
1108/// Distributes functions into instantiation sets. 1110/// An instantiation set is a collection of functions that have the same source 1111/// code, ie, template functions specializations. 1112classFunctionInstantiationSetCollector {
1113usingMapT = std::map<LineColPair, std::vector<const FunctionRecord *>>;
1114MapT InstantiatedFunctions;
1119while (
I !=
E &&
I->FileID != FileID)
1121assert(
I !=
E &&
"function does not cover the given file");
1122auto &Functions = InstantiatedFunctions[
I->startLoc()];
1126 MapT::iterator begin() {
return InstantiatedFunctions.begin(); }
1127 MapT::iterator end() {
return InstantiatedFunctions.end(); }
1130classSegmentBuilder {
1131 std::vector<CoverageSegment> &
Segments;
1134 SegmentBuilder(std::vector<CoverageSegment> &Segments) :
Segments(
Segments) {}
1136 /// Emit a segment with the count from \p Region starting at \p StartLoc. 1138 /// \p IsRegionEntry: The segment is at the start of a new non-gap region. 1139 /// \p EmitSkippedRegion: The segment must be emitted as a skipped region. 1141bool IsRegionEntry,
bool EmitSkippedRegion =
false) {
1142bool HasCount = !EmitSkippedRegion &&
1145// If the new segment wouldn't affect coverage rendering, skip it. 1146if (!
Segments.empty() && !IsRegionEntry && !EmitSkippedRegion) {
1148if (
Last.HasCount == HasCount &&
Last.Count ==
Region.ExecutionCount &&
1149 !
Last.IsRegionEntry)
1154Segments.emplace_back(StartLoc.first, StartLoc.second,
1155Region.ExecutionCount, IsRegionEntry,
1158Segments.emplace_back(StartLoc.first, StartLoc.second, IsRegionEntry);
1163 <<
" (count = " <<
Last.Count <<
")" 1164 << (
Last.IsRegionEntry ?
", RegionEntry" :
"")
1165 << (!
Last.HasCount ?
", Skipped" :
"")
1166 << (
Last.IsGapRegion ?
", Gap" :
"") <<
"\n";
1170 /// Emit segments for active regions which end before \p Loc. 1172 /// \p Loc: The start location of the next region. If std::nullopt, all active 1173 /// regions are completed. 1174 /// \p FirstCompletedRegion: Index of the first completed region. 1175void completeRegionsUntil(std::optional<LineColPair> Loc,
1176unsigned FirstCompletedRegion) {
1177// Sort the completed regions by end location. This makes it simple to 1178// emit closing segments in sorted order. 1179auto CompletedRegionsIt = ActiveRegions.
begin() + FirstCompletedRegion;
1180 std::stable_sort(CompletedRegionsIt, ActiveRegions.
end(),
1182 return L->endLoc() < R->endLoc();
1185// Emit segments for all completed regions. 1186for (
unsignedI = FirstCompletedRegion + 1,
E = ActiveRegions.
size();
I <
E;
1188constauto *CompletedRegion = ActiveRegions[
I];
1189assert((!Loc || CompletedRegion->endLoc() <= *Loc) &&
1190"Completed region ends after start of new region");
1192constauto *PrevCompletedRegion = ActiveRegions[
I - 1];
1193auto CompletedSegmentLoc = PrevCompletedRegion->endLoc();
1195// Don't emit any more segments if they start where the new region begins. 1196if (Loc && CompletedSegmentLoc == *Loc)
1199// Don't emit a segment if the next completed region ends at the same 1200// location as this one. 1201if (CompletedSegmentLoc == CompletedRegion->endLoc())
1204// Use the count from the last completed region which ends at this loc. 1205for (
unsigned J =
I + 1; J <
E; ++J)
1206if (CompletedRegion->endLoc() == ActiveRegions[J]->endLoc())
1207 CompletedRegion = ActiveRegions[J];
1209 startSegment(*CompletedRegion, CompletedSegmentLoc,
false);
1213if (FirstCompletedRegion &&
Last->endLoc() != *Loc) {
1214// If there's a gap after the end of the last completed region and the 1215// start of the new region, use the last active region to fill the gap. 1216 startSegment(*ActiveRegions[FirstCompletedRegion - 1],
Last->endLoc(),
1218 }
elseif (!FirstCompletedRegion && (!Loc || *Loc !=
Last->endLoc())) {
1219// Emit a skipped segment if there are no more active regions. This 1220// ensures that gaps between functions are marked correctly. 1221 startSegment(*
Last,
Last->endLoc(),
false,
true);
1224// Pop the completed regions. 1225 ActiveRegions.
erase(CompletedRegionsIt, ActiveRegions.
end());
1229for (
constauto &CR :
enumerate(Regions)) {
1230auto CurStartLoc = CR.value().startLoc();
1232// Active regions which end before the current region need to be popped. 1233auto CompletedRegions =
1234 std::stable_partition(ActiveRegions.
begin(), ActiveRegions.
end(),
1236 return !(Region->endLoc() <= CurStartLoc);
1238if (CompletedRegions != ActiveRegions.
end()) {
1239unsigned FirstCompletedRegion =
1240 std::distance(ActiveRegions.
begin(), CompletedRegions);
1241 completeRegionsUntil(CurStartLoc, FirstCompletedRegion);
1246// Try to emit a segment for the current region. 1247if (CurStartLoc == CR.value().endLoc()) {
1248// Avoid making zero-length regions active. If it's the last region, 1249// emit a skipped segment. Otherwise use its predecessor's count. 1251 (CR.index() + 1) == Regions.
size() ||
1253 startSegment(ActiveRegions.
empty() ? CR.value() : *ActiveRegions.
back(),
1254 CurStartLoc, !GapRegion, Skipped);
1255// If it is skipped segment, create a segment with last pushed 1256// regions's count at CurStartLoc. 1257if (Skipped && !ActiveRegions.
empty())
1258 startSegment(*ActiveRegions.
back(), CurStartLoc,
false);
1261if (CR.index() + 1 == Regions.
size() ||
1262 CurStartLoc != Regions[CR.index() + 1].startLoc()) {
1263// Emit a segment if the next region doesn't start at the same location 1265 startSegment(CR.value(), CurStartLoc, !GapRegion);
1268// This region is active (i.e not completed). 1272// Complete any remaining active regions. 1273if (!ActiveRegions.
empty())
1274 completeRegionsUntil(std::nullopt, 0);
1277 /// Sort a nested sequence of regions from a single file. 1280if (
LHS.startLoc() !=
RHS.startLoc())
1281returnLHS.startLoc() <
RHS.startLoc();
1283// When LHS completely contains RHS, we sort LHS first. 1284returnRHS.endLoc() <
LHS.endLoc();
1285// If LHS and RHS cover the same area, we need to sort them according 1286// to their kinds so that the most suitable region will become "active" 1287// in combineRegions(). Because we accumulate counter values only from 1288// regions of the same kind as the first region of the area, prefer 1289// CodeRegion to ExpansionRegion and ExpansionRegion to SkippedRegion. 1294"Unexpected order of region kind values");
1299 /// Combine counts of regions which cover the same area. 1307if (
Active->startLoc() !=
I->startLoc() ||
1308Active->endLoc() !=
I->endLoc()) {
1309// Shift to the next region. 1315// Merge duplicate region. 1316// If CodeRegions and ExpansionRegions cover the same area, it's probably 1317// a macro which is fully expanded to another macro. In that case, we need 1318// to accumulate counts only from CodeRegions, or else the area will be 1320// On the other hand, a macro may have a nested macro in its body. If the 1321// outer macro is used several times, the ExpansionRegion for the nested 1322// macro will also be added several times. These ExpansionRegions cover 1323// the same source locations and have to be combined to reach the correct 1324// value for that area. 1325// We add counts of the regions of the same kind as the active region 1326// to handle the both situations. 1328Active->ExecutionCount +=
I->ExecutionCount;
1334 /// Build a sorted list of CoverageSegments from a list of Regions. 1335static std::vector<CoverageSegment>
1337 std::vector<CoverageSegment>
Segments;
1338 SegmentBuilder Builder(Segments);
1340 sortNestedRegions(Regions);
1344dbgs() <<
"Combined regions:\n";
1345for (
constauto &CR : CombinedRegions)
1346dbgs() <<
" " << CR.LineStart <<
":" << CR.ColumnStart <<
" -> " 1347 << CR.LineEnd <<
":" << CR.ColumnEnd
1348 <<
" (count=" << CR.ExecutionCount <<
")\n";
1351 Builder.buildSegmentsImpl(CombinedRegions);
1357if (!(
L.Line <
R.Line) && !(
L.Line ==
R.Line &&
L.Col <
R.Col)) {
1358if (
L.Line ==
R.Line &&
L.Col ==
R.Col && !
L.HasCount)
1361 <<
" followed by " <<
R.Line <<
":" <<
R.Col <<
"\n");
1362assert(
false &&
"Coverage segments not unique or sorted");
1371}
// end anonymous namespace 1374 std::vector<StringRef> Filenames;
1375for (
constauto &
Function : getCoveredFunctions())
1379 Filenames.erase(
Last, Filenames.end());
1388 FilenameEquivalence[
I] =
true;
1389return FilenameEquivalence;
1392/// Return the ID of the file where the definition of the function is located. 1393static std::optional<unsigned>
1396for (
constauto &CR :
Function.CountedRegions)
1398 IsNotExpandedFile[CR.ExpandedFileID] =
false;
1405/// Check if SourceFile is the file that contains the definition of 1406/// the Function. Return the ID of the file in that case or std::nullopt 1408static std::optional<unsigned>
1423 std::vector<CountedRegion> Regions;
1425// Look up the function records in the given file. Due to hash collisions on 1426// the filename, we may get back some records that are not in the file. 1428 getImpreciseRecordIndicesForFilename(Filename);
1429for (
unsigned RecordIndex : RecordIndices) {
1433for (
constauto &CR :
Function.CountedRegions)
1434if (FileIDs.test(CR.FileID)) {
1435 Regions.push_back(CR);
1437 FileCoverage.Expansions.emplace_back(CR,
Function);
1439// Capture branch regions specific to the function (excluding expansions). 1440for (
constauto &CR :
Function.CountedBranchRegions)
1441if (FileIDs.test(CR.FileID))
1442 FileCoverage.BranchRegions.push_back(CR);
1443// Capture MCDC records specific to the function. 1444for (
constauto &MR :
Function.MCDCRecords)
1445if (FileIDs.test(MR.getDecisionRegion().FileID))
1446 FileCoverage.MCDCRecords.push_back(MR);
1449LLVM_DEBUG(
dbgs() <<
"Emitting segments for file: " << Filename <<
"\n");
1450 FileCoverage.Segments = SegmentBuilder::buildSegments(Regions);
1455std::vector<InstantiationGroup>
1457 FunctionInstantiationSetCollector InstantiationSetCollector;
1458// Look up the function records in the given file. Due to hash collisions on 1459// the filename, we may get back some records that are not in the file. 1461 getImpreciseRecordIndicesForFilename(Filename);
1462for (
unsigned RecordIndex : RecordIndices) {
1467 InstantiationSetCollector.insert(
Function, *MainFileID);
1470 std::vector<InstantiationGroup> Result;
1471for (
auto &InstantiationSet : InstantiationSetCollector) {
1473 InstantiationSet.first.second,
1474 std::move(InstantiationSet.second)};
1475 Result.emplace_back(std::move(IG));
1489 std::vector<CountedRegion> Regions;
1490for (
constauto &CR :
Function.CountedRegions)
1491if (CR.FileID == *MainFileID) {
1492 Regions.push_back(CR);
1494 FunctionCoverage.Expansions.emplace_back(CR,
Function);
1496// Capture branch regions specific to the function (excluding expansions). 1497for (
constauto &CR :
Function.CountedBranchRegions)
1498if (CR.FileID == *MainFileID)
1499 FunctionCoverage.BranchRegions.push_back(CR);
1501// Capture MCDC records specific to the function. 1502for (
constauto &MR :
Function.MCDCRecords)
1503if (MR.getDecisionRegion().FileID == *MainFileID)
1504 FunctionCoverage.MCDCRecords.push_back(MR);
1508 FunctionCoverage.Segments = SegmentBuilder::buildSegments(Regions);
1510return FunctionCoverage;
1518 std::vector<CountedRegion> Regions;
1519for (
constauto &CR :
Expansion.Function.CountedRegions)
1521 Regions.push_back(CR);
1523 ExpansionCoverage.Expansions.emplace_back(CR,
Expansion.Function);
1525for (
constauto &CR :
Expansion.Function.CountedBranchRegions)
1526// Capture branch regions that only pertain to the corresponding expansion. 1528 ExpansionCoverage.BranchRegions.push_back(CR);
1532 ExpansionCoverage.Segments = SegmentBuilder::buildSegments(Regions);
1534return ExpansionCoverage;
1537LineCoverageStats::LineCoverageStats(
1540 : ExecutionCount(0), HasMultipleRegions(
false), Mapped(
false), Line(Line),
1541 LineSegments(LineSegments), WrappedSegment(WrappedSegment) {
1542// Find the minimum number of regions which start in this line. 1543unsigned MinRegionCount = 0;
1545return !S->
IsGapRegion && S->HasCount && S->IsRegionEntry;
1547for (
unsignedI = 0;
I < LineSegments.
size() && MinRegionCount < 2; ++
I)
1548if (isStartOfRegion(LineSegments[
I]))
1551bool StartOfSkippedRegion = !LineSegments.
empty() &&
1552 !LineSegments.
front()->HasCount &&
1553 LineSegments.
front()->IsRegionEntry;
1555 HasMultipleRegions = MinRegionCount > 1;
1557 !StartOfSkippedRegion &&
1558 ((WrappedSegment && WrappedSegment->
HasCount) || (MinRegionCount > 0));
1560// if there is any starting segment at this line with a counter, it must be 1562 Mapped |=
any_of(LineSegments, [](
constauto *Seq) {
1563return Seq->IsRegionEntry && Seq->HasCount;
1570// Pick the max count from the non-gap, region entry segments and the 1573 ExecutionCount = WrappedSegment->
Count;
1576for (
constauto *LS : LineSegments)
1577if (isStartOfRegion(LS))
1578 ExecutionCount = std::max(ExecutionCount, LS->Count);
1582if (Next == CD.
end()) {
1588 WrappedSegment = Segments.back();
1590while (Next != CD.
end() && Next->Line == Line)
1591 Segments.push_back(&*Next++);
1598const std::string &ErrMsg =
"") {
1610OS <<
"no coverage data found";
1613OS <<
"unsupported coverage format version";
1616OS <<
"truncated coverage data";
1619OS <<
"malformed coverage data";
1622OS <<
"failed to decompress coverage data (zlib)";
1625OS <<
"`-arch` specifier is invalid or missing for universal binary";
1629// If optional error message is not empty, append it to the message. 1638// FIXME: This class is only here to support the transition to llvm::Error. It 1639// will be removed once this transition is complete. Clients should prefer to 1640// deal with the Error value directly, rather than converting to error_code. 1641classCoverageMappingErrorCategoryType :
public std::error_category {
1642constchar *
name()
const noexcept
override{
return"llvm.coveragemap"; }
1643 std::string message(
int IE)
const override{
1648}
// end anonymous namespace 1655static CoverageMappingErrorCategoryType ErrorCategory;
1656return 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 unsigned getMaxBitmapSize(const CoverageMappingRecord &Record, bool IsVersion11)
Returns the bit count.
static std::string getCoverageMapErrString(coveragemap_error Err, const std::string &ErrMsg="")
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 contains some templates that are useful if you are working with the STL at all.
This file implements the SmallBitVector class.
This file defines the SmallVector class.
This file contains some functions that are useful when dealing with strings.
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.
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.
uint64_t getVersion() const override
Return the profile version.
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)
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.
Counter subst(Counter C, const SubstMap &Map)
std::map< Counter, Counter > SubstMap
K to V map.
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, bool IsVersion11)
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.
@ C
The default llvm calling convention, compatible with C.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
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 tuples (A, B,...
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.
auto unique(Range &&R, Predicate P)
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.
void findIndependencePairs()
std::array< BitVector, 2 > BoolVector
uint16_t NumConditions
Number of Conditions used for a Decision Region.