Path: blob/main/contrib/llvm-project/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
35271 views
//===- CoverageMapping.cpp - Code coverage mapping support ----------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file contains support for clang's and llvm's instrumentation based9// code coverage.10//11//===----------------------------------------------------------------------===//1213#include "llvm/ProfileData/Coverage/CoverageMapping.h"14#include "llvm/ADT/ArrayRef.h"15#include "llvm/ADT/DenseMap.h"16#include "llvm/ADT/STLExtras.h"17#include "llvm/ADT/SmallBitVector.h"18#include "llvm/ADT/SmallVector.h"19#include "llvm/ADT/StringExtras.h"20#include "llvm/ADT/StringRef.h"21#include "llvm/Object/BuildID.h"22#include "llvm/ProfileData/Coverage/CoverageMappingReader.h"23#include "llvm/ProfileData/InstrProfReader.h"24#include "llvm/Support/Debug.h"25#include "llvm/Support/Errc.h"26#include "llvm/Support/Error.h"27#include "llvm/Support/ErrorHandling.h"28#include "llvm/Support/MemoryBuffer.h"29#include "llvm/Support/VirtualFileSystem.h"30#include "llvm/Support/raw_ostream.h"31#include <algorithm>32#include <cassert>33#include <cmath>34#include <cstdint>35#include <iterator>36#include <map>37#include <memory>38#include <optional>39#include <stack>40#include <string>41#include <system_error>42#include <utility>43#include <vector>4445using namespace llvm;46using namespace coverage;4748#define DEBUG_TYPE "coverage-mapping"4950Counter CounterExpressionBuilder::get(const CounterExpression &E) {51auto It = ExpressionIndices.find(E);52if (It != ExpressionIndices.end())53return Counter::getExpression(It->second);54unsigned I = Expressions.size();55Expressions.push_back(E);56ExpressionIndices[E] = I;57return Counter::getExpression(I);58}5960void CounterExpressionBuilder::extractTerms(Counter C, int Factor,61SmallVectorImpl<Term> &Terms) {62switch (C.getKind()) {63case Counter::Zero:64break;65case Counter::CounterValueReference:66Terms.emplace_back(C.getCounterID(), Factor);67break;68case Counter::Expression:69const auto &E = Expressions[C.getExpressionID()];70extractTerms(E.LHS, Factor, Terms);71extractTerms(72E.RHS, E.Kind == CounterExpression::Subtract ? -Factor : Factor, Terms);73break;74}75}7677Counter CounterExpressionBuilder::simplify(Counter ExpressionTree) {78// Gather constant terms.79SmallVector<Term, 32> Terms;80extractTerms(ExpressionTree, +1, Terms);8182// If there are no terms, this is just a zero. The algorithm below assumes at83// least one term.84if (Terms.size() == 0)85return Counter::getZero();8687// Group the terms by counter ID.88llvm::sort(Terms, [](const Term &LHS, const Term &RHS) {89return LHS.CounterID < RHS.CounterID;90});9192// Combine terms by counter ID to eliminate counters that sum to zero.93auto Prev = Terms.begin();94for (auto I = Prev + 1, E = Terms.end(); I != E; ++I) {95if (I->CounterID == Prev->CounterID) {96Prev->Factor += I->Factor;97continue;98}99++Prev;100*Prev = *I;101}102Terms.erase(++Prev, Terms.end());103104Counter C;105// Create additions. We do this before subtractions to avoid constructs like106// ((0 - X) + Y), as opposed to (Y - X).107for (auto T : Terms) {108if (T.Factor <= 0)109continue;110for (int I = 0; I < T.Factor; ++I)111if (C.isZero())112C = Counter::getCounter(T.CounterID);113else114C = get(CounterExpression(CounterExpression::Add, C,115Counter::getCounter(T.CounterID)));116}117118// Create subtractions.119for (auto T : Terms) {120if (T.Factor >= 0)121continue;122for (int I = 0; I < -T.Factor; ++I)123C = get(CounterExpression(CounterExpression::Subtract, C,124Counter::getCounter(T.CounterID)));125}126return C;127}128129Counter CounterExpressionBuilder::add(Counter LHS, Counter RHS, bool Simplify) {130auto Cnt = get(CounterExpression(CounterExpression::Add, LHS, RHS));131return Simplify ? simplify(Cnt) : Cnt;132}133134Counter CounterExpressionBuilder::subtract(Counter LHS, Counter RHS,135bool Simplify) {136auto Cnt = get(CounterExpression(CounterExpression::Subtract, LHS, RHS));137return Simplify ? simplify(Cnt) : Cnt;138}139140void CounterMappingContext::dump(const Counter &C, raw_ostream &OS) const {141switch (C.getKind()) {142case Counter::Zero:143OS << '0';144return;145case Counter::CounterValueReference:146OS << '#' << C.getCounterID();147break;148case Counter::Expression: {149if (C.getExpressionID() >= Expressions.size())150return;151const auto &E = Expressions[C.getExpressionID()];152OS << '(';153dump(E.LHS, OS);154OS << (E.Kind == CounterExpression::Subtract ? " - " : " + ");155dump(E.RHS, OS);156OS << ')';157break;158}159}160if (CounterValues.empty())161return;162Expected<int64_t> Value = evaluate(C);163if (auto E = Value.takeError()) {164consumeError(std::move(E));165return;166}167OS << '[' << *Value << ']';168}169170Expected<int64_t> CounterMappingContext::evaluate(const Counter &C) const {171struct StackElem {172Counter ICounter;173int64_t LHS = 0;174enum {175KNeverVisited = 0,176KVisitedOnce = 1,177KVisitedTwice = 2,178} VisitCount = KNeverVisited;179};180181std::stack<StackElem> CounterStack;182CounterStack.push({C});183184int64_t LastPoppedValue;185186while (!CounterStack.empty()) {187StackElem &Current = CounterStack.top();188189switch (Current.ICounter.getKind()) {190case Counter::Zero:191LastPoppedValue = 0;192CounterStack.pop();193break;194case Counter::CounterValueReference:195if (Current.ICounter.getCounterID() >= CounterValues.size())196return errorCodeToError(errc::argument_out_of_domain);197LastPoppedValue = CounterValues[Current.ICounter.getCounterID()];198CounterStack.pop();199break;200case Counter::Expression: {201if (Current.ICounter.getExpressionID() >= Expressions.size())202return errorCodeToError(errc::argument_out_of_domain);203const auto &E = Expressions[Current.ICounter.getExpressionID()];204if (Current.VisitCount == StackElem::KNeverVisited) {205CounterStack.push(StackElem{E.LHS});206Current.VisitCount = StackElem::KVisitedOnce;207} else if (Current.VisitCount == StackElem::KVisitedOnce) {208Current.LHS = LastPoppedValue;209CounterStack.push(StackElem{E.RHS});210Current.VisitCount = StackElem::KVisitedTwice;211} else {212int64_t LHS = Current.LHS;213int64_t RHS = LastPoppedValue;214LastPoppedValue =215E.Kind == CounterExpression::Subtract ? LHS - RHS : LHS + RHS;216CounterStack.pop();217}218break;219}220}221}222223return LastPoppedValue;224}225226mcdc::TVIdxBuilder::TVIdxBuilder(const SmallVectorImpl<ConditionIDs> &NextIDs,227int Offset)228: Indices(NextIDs.size()) {229// Construct Nodes and set up each InCount230auto N = NextIDs.size();231SmallVector<MCDCNode> Nodes(N);232for (unsigned ID = 0; ID < N; ++ID) {233for (unsigned C = 0; C < 2; ++C) {234#ifndef NDEBUG235Indices[ID][C] = INT_MIN;236#endif237auto NextID = NextIDs[ID][C];238Nodes[ID].NextIDs[C] = NextID;239if (NextID >= 0)240++Nodes[NextID].InCount;241}242}243244// Sort key ordered by <-Width, Ord>245SmallVector<std::tuple<int, /// -Width246unsigned, /// Ord247int, /// ID248unsigned /// Cond (0 or 1)249>>250Decisions;251252// Traverse Nodes to assign Idx253SmallVector<int> Q;254assert(Nodes[0].InCount == 0);255Nodes[0].Width = 1;256Q.push_back(0);257258unsigned Ord = 0;259while (!Q.empty()) {260auto IID = Q.begin();261int ID = *IID;262Q.erase(IID);263auto &Node = Nodes[ID];264assert(Node.Width > 0);265266for (unsigned I = 0; I < 2; ++I) {267auto NextID = Node.NextIDs[I];268assert(NextID != 0 && "NextID should not point to the top");269if (NextID < 0) {270// Decision271Decisions.emplace_back(-Node.Width, Ord++, ID, I);272assert(Ord == Decisions.size());273continue;274}275276// Inter Node277auto &NextNode = Nodes[NextID];278assert(NextNode.InCount > 0);279280// Assign Idx281assert(Indices[ID][I] == INT_MIN);282Indices[ID][I] = NextNode.Width;283auto NextWidth = int64_t(NextNode.Width) + Node.Width;284if (NextWidth > HardMaxTVs) {285NumTestVectors = HardMaxTVs; // Overflow286return;287}288NextNode.Width = NextWidth;289290// Ready if all incomings are processed.291// Or NextNode.Width hasn't been confirmed yet.292if (--NextNode.InCount == 0)293Q.push_back(NextID);294}295}296297llvm::sort(Decisions);298299// Assign TestVector Indices in Decision Nodes300int64_t CurIdx = 0;301for (auto [NegWidth, Ord, ID, C] : Decisions) {302int Width = -NegWidth;303assert(Nodes[ID].Width == Width);304assert(Nodes[ID].NextIDs[C] < 0);305assert(Indices[ID][C] == INT_MIN);306Indices[ID][C] = Offset + CurIdx;307CurIdx += Width;308if (CurIdx > HardMaxTVs) {309NumTestVectors = HardMaxTVs; // Overflow310return;311}312}313314assert(CurIdx < HardMaxTVs);315NumTestVectors = CurIdx;316317#ifndef NDEBUG318for (const auto &Idxs : Indices)319for (auto Idx : Idxs)320assert(Idx != INT_MIN);321SavedNodes = std::move(Nodes);322#endif323}324325namespace {326327/// Construct this->NextIDs with Branches for TVIdxBuilder to use it328/// before MCDCRecordProcessor().329class NextIDsBuilder {330protected:331SmallVector<mcdc::ConditionIDs> NextIDs;332333public:334NextIDsBuilder(const ArrayRef<const CounterMappingRegion *> Branches)335: NextIDs(Branches.size()) {336#ifndef NDEBUG337DenseSet<mcdc::ConditionID> SeenIDs;338#endif339for (const auto *Branch : Branches) {340const auto &BranchParams = Branch->getBranchParams();341assert(SeenIDs.insert(BranchParams.ID).second && "Duplicate CondID");342NextIDs[BranchParams.ID] = BranchParams.Conds;343}344assert(SeenIDs.size() == Branches.size());345}346};347348class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder {349/// A bitmap representing the executed test vectors for a boolean expression.350/// Each index of the bitmap corresponds to a possible test vector. An index351/// with a bit value of '1' indicates that the corresponding Test Vector352/// identified by that index was executed.353const BitVector &Bitmap;354355/// Decision Region to which the ExecutedTestVectorBitmap applies.356const CounterMappingRegion &Region;357const mcdc::DecisionParameters &DecisionParams;358359/// Array of branch regions corresponding each conditions in the boolean360/// expression.361ArrayRef<const CounterMappingRegion *> Branches;362363/// Total number of conditions in the boolean expression.364unsigned NumConditions;365366/// Vector used to track whether a condition is constant folded.367MCDCRecord::BoolVector Folded;368369/// Mapping of calculated MC/DC Independence Pairs for each condition.370MCDCRecord::TVPairMap IndependencePairs;371372/// Storage for ExecVectors373/// ExecVectors is the alias of its 0th element.374std::array<MCDCRecord::TestVectors, 2> ExecVectorsByCond;375376/// Actual executed Test Vectors for the boolean expression, based on377/// ExecutedTestVectorBitmap.378MCDCRecord::TestVectors &ExecVectors;379380/// Number of False items in ExecVectors381unsigned NumExecVectorsF;382383#ifndef NDEBUG384DenseSet<unsigned> TVIdxs;385#endif386387bool IsVersion11;388389public:390MCDCRecordProcessor(const BitVector &Bitmap,391const CounterMappingRegion &Region,392ArrayRef<const CounterMappingRegion *> Branches,393bool IsVersion11)394: NextIDsBuilder(Branches), TVIdxBuilder(this->NextIDs), Bitmap(Bitmap),395Region(Region), DecisionParams(Region.getDecisionParams()),396Branches(Branches), NumConditions(DecisionParams.NumConditions),397Folded(NumConditions, false), IndependencePairs(NumConditions),398ExecVectors(ExecVectorsByCond[false]), IsVersion11(IsVersion11) {}399400private:401// Walk the binary decision diagram and try assigning both false and true to402// each node. When a terminal node (ID == 0) is reached, fill in the value in403// the truth table.404void buildTestVector(MCDCRecord::TestVector &TV, mcdc::ConditionID ID,405int TVIdx) {406for (auto MCDCCond : {MCDCRecord::MCDC_False, MCDCRecord::MCDC_True}) {407static_assert(MCDCRecord::MCDC_False == 0);408static_assert(MCDCRecord::MCDC_True == 1);409TV.set(ID, MCDCCond);410auto NextID = NextIDs[ID][MCDCCond];411auto NextTVIdx = TVIdx + Indices[ID][MCDCCond];412assert(NextID == SavedNodes[ID].NextIDs[MCDCCond]);413if (NextID >= 0) {414buildTestVector(TV, NextID, NextTVIdx);415continue;416}417418assert(TVIdx < SavedNodes[ID].Width);419assert(TVIdxs.insert(NextTVIdx).second && "Duplicate TVIdx");420421if (!Bitmap[IsVersion11422? DecisionParams.BitmapIdx * CHAR_BIT + TV.getIndex()423: DecisionParams.BitmapIdx - NumTestVectors + NextTVIdx])424continue;425426// Copy the completed test vector to the vector of testvectors.427// The final value (T,F) is equal to the last non-dontcare state on the428// path (in a short-circuiting system).429ExecVectorsByCond[MCDCCond].push_back({TV, MCDCCond});430}431432// Reset back to DontCare.433TV.set(ID, MCDCRecord::MCDC_DontCare);434}435436/// Walk the bits in the bitmap. A bit set to '1' indicates that the test437/// vector at the corresponding index was executed during a test run.438void findExecutedTestVectors() {439// Walk the binary decision diagram to enumerate all possible test vectors.440// We start at the root node (ID == 0) with all values being DontCare.441// `TVIdx` starts with 0 and is in the traversal.442// `Index` encodes the bitmask of true values and is initially 0.443MCDCRecord::TestVector TV(NumConditions);444buildTestVector(TV, 0, 0);445assert(TVIdxs.size() == unsigned(NumTestVectors) &&446"TVIdxs wasn't fulfilled");447448// Fill ExecVectors order by False items and True items.449// ExecVectors is the alias of ExecVectorsByCond[false], so450// Append ExecVectorsByCond[true] on it.451NumExecVectorsF = ExecVectors.size();452auto &ExecVectorsT = ExecVectorsByCond[true];453ExecVectors.append(std::make_move_iterator(ExecVectorsT.begin()),454std::make_move_iterator(ExecVectorsT.end()));455}456457// Find an independence pair for each condition:458// - The condition is true in one test and false in the other.459// - The decision outcome is true one test and false in the other.460// - All other conditions' values must be equal or marked as "don't care".461void findIndependencePairs() {462unsigned NumTVs = ExecVectors.size();463for (unsigned I = NumExecVectorsF; I < NumTVs; ++I) {464const auto &[A, ACond] = ExecVectors[I];465assert(ACond == MCDCRecord::MCDC_True);466for (unsigned J = 0; J < NumExecVectorsF; ++J) {467const auto &[B, BCond] = ExecVectors[J];468assert(BCond == MCDCRecord::MCDC_False);469// If the two vectors differ in exactly one condition, ignoring DontCare470// conditions, we have found an independence pair.471auto AB = A.getDifferences(B);472if (AB.count() == 1)473IndependencePairs.insert(474{AB.find_first(), std::make_pair(J + 1, I + 1)});475}476}477}478479public:480/// Process the MC/DC Record in order to produce a result for a boolean481/// expression. This process includes tracking the conditions that comprise482/// the decision region, calculating the list of all possible test vectors,483/// marking the executed test vectors, and then finding an Independence Pair484/// out of the executed test vectors for each condition in the boolean485/// expression. A condition is tracked to ensure that its ID can be mapped to486/// its ordinal position in the boolean expression. The condition's source487/// location is also tracked, as well as whether it is constant folded (in488/// which case it is excuded from the metric).489MCDCRecord processMCDCRecord() {490unsigned I = 0;491MCDCRecord::CondIDMap PosToID;492MCDCRecord::LineColPairMap CondLoc;493494// Walk the Record's BranchRegions (representing Conditions) in order to:495// - Hash the condition based on its corresponding ID. This will be used to496// calculate the test vectors.497// - Keep a map of the condition's ordinal position (1, 2, 3, 4) to its498// actual ID. This will be used to visualize the conditions in the499// correct order.500// - Keep track of the condition source location. This will be used to501// visualize where the condition is.502// - Record whether the condition is constant folded so that we exclude it503// from being measured.504for (const auto *B : Branches) {505const auto &BranchParams = B->getBranchParams();506PosToID[I] = BranchParams.ID;507CondLoc[I] = B->startLoc();508Folded[I++] = (B->Count.isZero() && B->FalseCount.isZero());509}510511// Using Profile Bitmap from runtime, mark the executed test vectors.512findExecutedTestVectors();513514// Compare executed test vectors against each other to find an independence515// pairs for each condition. This processing takes the most time.516findIndependencePairs();517518// Record Test vectors, executed vectors, and independence pairs.519return MCDCRecord(Region, std::move(ExecVectors),520std::move(IndependencePairs), std::move(Folded),521std::move(PosToID), std::move(CondLoc));522}523};524525} // namespace526527Expected<MCDCRecord> CounterMappingContext::evaluateMCDCRegion(528const CounterMappingRegion &Region,529ArrayRef<const CounterMappingRegion *> Branches, bool IsVersion11) {530531MCDCRecordProcessor MCDCProcessor(Bitmap, Region, Branches, IsVersion11);532return MCDCProcessor.processMCDCRecord();533}534535unsigned CounterMappingContext::getMaxCounterID(const Counter &C) const {536struct StackElem {537Counter ICounter;538int64_t LHS = 0;539enum {540KNeverVisited = 0,541KVisitedOnce = 1,542KVisitedTwice = 2,543} VisitCount = KNeverVisited;544};545546std::stack<StackElem> CounterStack;547CounterStack.push({C});548549int64_t LastPoppedValue;550551while (!CounterStack.empty()) {552StackElem &Current = CounterStack.top();553554switch (Current.ICounter.getKind()) {555case Counter::Zero:556LastPoppedValue = 0;557CounterStack.pop();558break;559case Counter::CounterValueReference:560LastPoppedValue = Current.ICounter.getCounterID();561CounterStack.pop();562break;563case Counter::Expression: {564if (Current.ICounter.getExpressionID() >= Expressions.size()) {565LastPoppedValue = 0;566CounterStack.pop();567} else {568const auto &E = Expressions[Current.ICounter.getExpressionID()];569if (Current.VisitCount == StackElem::KNeverVisited) {570CounterStack.push(StackElem{E.LHS});571Current.VisitCount = StackElem::KVisitedOnce;572} else if (Current.VisitCount == StackElem::KVisitedOnce) {573Current.LHS = LastPoppedValue;574CounterStack.push(StackElem{E.RHS});575Current.VisitCount = StackElem::KVisitedTwice;576} else {577int64_t LHS = Current.LHS;578int64_t RHS = LastPoppedValue;579LastPoppedValue = std::max(LHS, RHS);580CounterStack.pop();581}582}583break;584}585}586}587588return LastPoppedValue;589}590591void FunctionRecordIterator::skipOtherFiles() {592while (Current != Records.end() && !Filename.empty() &&593Filename != Current->Filenames[0])594++Current;595if (Current == Records.end())596*this = FunctionRecordIterator();597}598599ArrayRef<unsigned> CoverageMapping::getImpreciseRecordIndicesForFilename(600StringRef Filename) const {601size_t FilenameHash = hash_value(Filename);602auto RecordIt = FilenameHash2RecordIndices.find(FilenameHash);603if (RecordIt == FilenameHash2RecordIndices.end())604return {};605return RecordIt->second;606}607608static unsigned getMaxCounterID(const CounterMappingContext &Ctx,609const CoverageMappingRecord &Record) {610unsigned MaxCounterID = 0;611for (const auto &Region : Record.MappingRegions) {612MaxCounterID = std::max(MaxCounterID, Ctx.getMaxCounterID(Region.Count));613}614return MaxCounterID;615}616617/// Returns the bit count618static unsigned getMaxBitmapSize(const CoverageMappingRecord &Record,619bool IsVersion11) {620unsigned MaxBitmapIdx = 0;621unsigned NumConditions = 0;622// Scan max(BitmapIdx).623// Note that `<=` is used insted of `<`, because `BitmapIdx == 0` is valid624// and `MaxBitmapIdx is `unsigned`. `BitmapIdx` is unique in the record.625for (const auto &Region : reverse(Record.MappingRegions)) {626if (Region.Kind != CounterMappingRegion::MCDCDecisionRegion)627continue;628const auto &DecisionParams = Region.getDecisionParams();629if (MaxBitmapIdx <= DecisionParams.BitmapIdx) {630MaxBitmapIdx = DecisionParams.BitmapIdx;631NumConditions = DecisionParams.NumConditions;632}633}634635if (IsVersion11)636MaxBitmapIdx = MaxBitmapIdx * CHAR_BIT +637llvm::alignTo(uint64_t(1) << NumConditions, CHAR_BIT);638639return MaxBitmapIdx;640}641642namespace {643644/// Collect Decisions, Branchs, and Expansions and associate them.645class MCDCDecisionRecorder {646private:647/// This holds the DecisionRegion and MCDCBranches under it.648/// Also traverses Expansion(s).649/// The Decision has the number of MCDCBranches and will complete650/// when it is filled with unique ConditionID of MCDCBranches.651struct DecisionRecord {652const CounterMappingRegion *DecisionRegion;653654/// They are reflected from DecisionRegion for convenience.655mcdc::DecisionParameters DecisionParams;656LineColPair DecisionStartLoc;657LineColPair DecisionEndLoc;658659/// This is passed to `MCDCRecordProcessor`, so this should be compatible660/// to`ArrayRef<const CounterMappingRegion *>`.661SmallVector<const CounterMappingRegion *> MCDCBranches;662663/// IDs that are stored in MCDCBranches664/// Complete when all IDs (1 to NumConditions) are met.665DenseSet<mcdc::ConditionID> ConditionIDs;666667/// Set of IDs of Expansion(s) that are relevant to DecisionRegion668/// and its children (via expansions).669/// FileID pointed by ExpandedFileID is dedicated to the expansion, so670/// the location in the expansion doesn't matter.671DenseSet<unsigned> ExpandedFileIDs;672673DecisionRecord(const CounterMappingRegion &Decision)674: DecisionRegion(&Decision),675DecisionParams(Decision.getDecisionParams()),676DecisionStartLoc(Decision.startLoc()),677DecisionEndLoc(Decision.endLoc()) {678assert(Decision.Kind == CounterMappingRegion::MCDCDecisionRegion);679}680681/// Determine whether DecisionRecord dominates `R`.682bool dominates(const CounterMappingRegion &R) const {683// Determine whether `R` is included in `DecisionRegion`.684if (R.FileID == DecisionRegion->FileID &&685R.startLoc() >= DecisionStartLoc && R.endLoc() <= DecisionEndLoc)686return true;687688// Determine whether `R` is pointed by any of Expansions.689return ExpandedFileIDs.contains(R.FileID);690}691692enum Result {693NotProcessed = 0, /// Irrelevant to this Decision694Processed, /// Added to this Decision695Completed, /// Added and filled this Decision696};697698/// Add Branch into the Decision699/// \param Branch expects MCDCBranchRegion700/// \returns NotProcessed/Processed/Completed701Result addBranch(const CounterMappingRegion &Branch) {702assert(Branch.Kind == CounterMappingRegion::MCDCBranchRegion);703704auto ConditionID = Branch.getBranchParams().ID;705706if (ConditionIDs.contains(ConditionID) ||707ConditionID >= DecisionParams.NumConditions)708return NotProcessed;709710if (!this->dominates(Branch))711return NotProcessed;712713assert(MCDCBranches.size() < DecisionParams.NumConditions);714715// Put `ID=0` in front of `MCDCBranches` for convenience716// even if `MCDCBranches` is not topological.717if (ConditionID == 0)718MCDCBranches.insert(MCDCBranches.begin(), &Branch);719else720MCDCBranches.push_back(&Branch);721722// Mark `ID` as `assigned`.723ConditionIDs.insert(ConditionID);724725// `Completed` when `MCDCBranches` is full726return (MCDCBranches.size() == DecisionParams.NumConditions ? Completed727: Processed);728}729730/// Record Expansion if it is relevant to this Decision.731/// Each `Expansion` may nest.732/// \returns true if recorded.733bool recordExpansion(const CounterMappingRegion &Expansion) {734if (!this->dominates(Expansion))735return false;736737ExpandedFileIDs.insert(Expansion.ExpandedFileID);738return true;739}740};741742private:743/// Decisions in progress744/// DecisionRecord is added for each MCDCDecisionRegion.745/// DecisionRecord is removed when Decision is completed.746SmallVector<DecisionRecord> Decisions;747748public:749~MCDCDecisionRecorder() {750assert(Decisions.empty() && "All Decisions have not been resolved");751}752753/// Register Region and start recording.754void registerDecision(const CounterMappingRegion &Decision) {755Decisions.emplace_back(Decision);756}757758void recordExpansion(const CounterMappingRegion &Expansion) {759any_of(Decisions, [&Expansion](auto &Decision) {760return Decision.recordExpansion(Expansion);761});762}763764using DecisionAndBranches =765std::pair<const CounterMappingRegion *, /// Decision766SmallVector<const CounterMappingRegion *> /// Branches767>;768769/// Add MCDCBranchRegion to DecisionRecord.770/// \param Branch to be processed771/// \returns DecisionsAndBranches if DecisionRecord completed.772/// Or returns nullopt.773std::optional<DecisionAndBranches>774processBranch(const CounterMappingRegion &Branch) {775// Seek each Decision and apply Region to it.776for (auto DecisionIter = Decisions.begin(), DecisionEnd = Decisions.end();777DecisionIter != DecisionEnd; ++DecisionIter)778switch (DecisionIter->addBranch(Branch)) {779case DecisionRecord::NotProcessed:780continue;781case DecisionRecord::Processed:782return std::nullopt;783case DecisionRecord::Completed:784DecisionAndBranches Result =785std::make_pair(DecisionIter->DecisionRegion,786std::move(DecisionIter->MCDCBranches));787Decisions.erase(DecisionIter); // No longer used.788return Result;789}790791llvm_unreachable("Branch not found in Decisions");792}793};794795} // namespace796797Error CoverageMapping::loadFunctionRecord(798const CoverageMappingRecord &Record,799IndexedInstrProfReader &ProfileReader) {800StringRef OrigFuncName = Record.FunctionName;801if (OrigFuncName.empty())802return make_error<CoverageMapError>(coveragemap_error::malformed,803"record function name is empty");804805if (Record.Filenames.empty())806OrigFuncName = getFuncNameWithoutPrefix(OrigFuncName);807else808OrigFuncName = getFuncNameWithoutPrefix(OrigFuncName, Record.Filenames[0]);809810CounterMappingContext Ctx(Record.Expressions);811812std::vector<uint64_t> Counts;813if (Error E = ProfileReader.getFunctionCounts(Record.FunctionName,814Record.FunctionHash, Counts)) {815instrprof_error IPE = std::get<0>(InstrProfError::take(std::move(E)));816if (IPE == instrprof_error::hash_mismatch) {817FuncHashMismatches.emplace_back(std::string(Record.FunctionName),818Record.FunctionHash);819return Error::success();820}821if (IPE != instrprof_error::unknown_function)822return make_error<InstrProfError>(IPE);823Counts.assign(getMaxCounterID(Ctx, Record) + 1, 0);824}825Ctx.setCounts(Counts);826827bool IsVersion11 =828ProfileReader.getVersion() < IndexedInstrProf::ProfVersion::Version12;829830BitVector Bitmap;831if (Error E = ProfileReader.getFunctionBitmap(Record.FunctionName,832Record.FunctionHash, Bitmap)) {833instrprof_error IPE = std::get<0>(InstrProfError::take(std::move(E)));834if (IPE == instrprof_error::hash_mismatch) {835FuncHashMismatches.emplace_back(std::string(Record.FunctionName),836Record.FunctionHash);837return Error::success();838}839if (IPE != instrprof_error::unknown_function)840return make_error<InstrProfError>(IPE);841Bitmap = BitVector(getMaxBitmapSize(Record, IsVersion11));842}843Ctx.setBitmap(std::move(Bitmap));844845assert(!Record.MappingRegions.empty() && "Function has no regions");846847// This coverage record is a zero region for a function that's unused in848// some TU, but used in a different TU. Ignore it. The coverage maps from the849// the other TU will either be loaded (providing full region counts) or they850// won't (in which case we don't unintuitively report functions as uncovered851// when they have non-zero counts in the profile).852if (Record.MappingRegions.size() == 1 &&853Record.MappingRegions[0].Count.isZero() && Counts[0] > 0)854return Error::success();855856MCDCDecisionRecorder MCDCDecisions;857FunctionRecord Function(OrigFuncName, Record.Filenames);858for (const auto &Region : Record.MappingRegions) {859// MCDCDecisionRegion should be handled first since it overlaps with860// others inside.861if (Region.Kind == CounterMappingRegion::MCDCDecisionRegion) {862MCDCDecisions.registerDecision(Region);863continue;864}865Expected<int64_t> ExecutionCount = Ctx.evaluate(Region.Count);866if (auto E = ExecutionCount.takeError()) {867consumeError(std::move(E));868return Error::success();869}870Expected<int64_t> AltExecutionCount = Ctx.evaluate(Region.FalseCount);871if (auto E = AltExecutionCount.takeError()) {872consumeError(std::move(E));873return Error::success();874}875Function.pushRegion(Region, *ExecutionCount, *AltExecutionCount,876ProfileReader.hasSingleByteCoverage());877878// Record ExpansionRegion.879if (Region.Kind == CounterMappingRegion::ExpansionRegion) {880MCDCDecisions.recordExpansion(Region);881continue;882}883884// Do nothing unless MCDCBranchRegion.885if (Region.Kind != CounterMappingRegion::MCDCBranchRegion)886continue;887888auto Result = MCDCDecisions.processBranch(Region);889if (!Result) // Any Decision doesn't complete.890continue;891892auto MCDCDecision = Result->first;893auto &MCDCBranches = Result->second;894895// Since the bitmap identifies the executed test vectors for an MC/DC896// DecisionRegion, all of the information is now available to process.897// This is where the bulk of the MC/DC progressing takes place.898Expected<MCDCRecord> Record =899Ctx.evaluateMCDCRegion(*MCDCDecision, MCDCBranches, IsVersion11);900if (auto E = Record.takeError()) {901consumeError(std::move(E));902return Error::success();903}904905// Save the MC/DC Record so that it can be visualized later.906Function.pushMCDCRecord(std::move(*Record));907}908909// Don't create records for (filenames, function) pairs we've already seen.910auto FilenamesHash = hash_combine_range(Record.Filenames.begin(),911Record.Filenames.end());912if (!RecordProvenance[FilenamesHash].insert(hash_value(OrigFuncName)).second)913return Error::success();914915Functions.push_back(std::move(Function));916917// Performance optimization: keep track of the indices of the function records918// which correspond to each filename. This can be used to substantially speed919// up queries for coverage info in a file.920unsigned RecordIndex = Functions.size() - 1;921for (StringRef Filename : Record.Filenames) {922auto &RecordIndices = FilenameHash2RecordIndices[hash_value(Filename)];923// Note that there may be duplicates in the filename set for a function924// record, because of e.g. macro expansions in the function in which both925// the macro and the function are defined in the same file.926if (RecordIndices.empty() || RecordIndices.back() != RecordIndex)927RecordIndices.push_back(RecordIndex);928}929930return Error::success();931}932933// This function is for memory optimization by shortening the lifetimes934// of CoverageMappingReader instances.935Error CoverageMapping::loadFromReaders(936ArrayRef<std::unique_ptr<CoverageMappingReader>> CoverageReaders,937IndexedInstrProfReader &ProfileReader, CoverageMapping &Coverage) {938for (const auto &CoverageReader : CoverageReaders) {939for (auto RecordOrErr : *CoverageReader) {940if (Error E = RecordOrErr.takeError())941return E;942const auto &Record = *RecordOrErr;943if (Error E = Coverage.loadFunctionRecord(Record, ProfileReader))944return E;945}946}947return Error::success();948}949950Expected<std::unique_ptr<CoverageMapping>> CoverageMapping::load(951ArrayRef<std::unique_ptr<CoverageMappingReader>> CoverageReaders,952IndexedInstrProfReader &ProfileReader) {953auto Coverage = std::unique_ptr<CoverageMapping>(new CoverageMapping());954if (Error E = loadFromReaders(CoverageReaders, ProfileReader, *Coverage))955return std::move(E);956return std::move(Coverage);957}958959// If E is a no_data_found error, returns success. Otherwise returns E.960static Error handleMaybeNoDataFoundError(Error E) {961return handleErrors(962std::move(E), [](const CoverageMapError &CME) {963if (CME.get() == coveragemap_error::no_data_found)964return static_cast<Error>(Error::success());965return make_error<CoverageMapError>(CME.get(), CME.getMessage());966});967}968969Error CoverageMapping::loadFromFile(970StringRef Filename, StringRef Arch, StringRef CompilationDir,971IndexedInstrProfReader &ProfileReader, CoverageMapping &Coverage,972bool &DataFound, SmallVectorImpl<object::BuildID> *FoundBinaryIDs) {973auto CovMappingBufOrErr = MemoryBuffer::getFileOrSTDIN(974Filename, /*IsText=*/false, /*RequiresNullTerminator=*/false);975if (std::error_code EC = CovMappingBufOrErr.getError())976return createFileError(Filename, errorCodeToError(EC));977MemoryBufferRef CovMappingBufRef =978CovMappingBufOrErr.get()->getMemBufferRef();979SmallVector<std::unique_ptr<MemoryBuffer>, 4> Buffers;980981SmallVector<object::BuildIDRef> BinaryIDs;982auto CoverageReadersOrErr = BinaryCoverageReader::create(983CovMappingBufRef, Arch, Buffers, CompilationDir,984FoundBinaryIDs ? &BinaryIDs : nullptr);985if (Error E = CoverageReadersOrErr.takeError()) {986E = handleMaybeNoDataFoundError(std::move(E));987if (E)988return createFileError(Filename, std::move(E));989return E;990}991992SmallVector<std::unique_ptr<CoverageMappingReader>, 4> Readers;993for (auto &Reader : CoverageReadersOrErr.get())994Readers.push_back(std::move(Reader));995if (FoundBinaryIDs && !Readers.empty()) {996llvm::append_range(*FoundBinaryIDs,997llvm::map_range(BinaryIDs, [](object::BuildIDRef BID) {998return object::BuildID(BID);999}));1000}1001DataFound |= !Readers.empty();1002if (Error E = loadFromReaders(Readers, ProfileReader, Coverage))1003return createFileError(Filename, std::move(E));1004return Error::success();1005}10061007Expected<std::unique_ptr<CoverageMapping>> CoverageMapping::load(1008ArrayRef<StringRef> ObjectFilenames, StringRef ProfileFilename,1009vfs::FileSystem &FS, ArrayRef<StringRef> Arches, StringRef CompilationDir,1010const object::BuildIDFetcher *BIDFetcher, bool CheckBinaryIDs) {1011auto ProfileReaderOrErr = IndexedInstrProfReader::create(ProfileFilename, FS);1012if (Error E = ProfileReaderOrErr.takeError())1013return createFileError(ProfileFilename, std::move(E));1014auto ProfileReader = std::move(ProfileReaderOrErr.get());1015auto Coverage = std::unique_ptr<CoverageMapping>(new CoverageMapping());1016bool DataFound = false;10171018auto GetArch = [&](size_t Idx) {1019if (Arches.empty())1020return StringRef();1021if (Arches.size() == 1)1022return Arches.front();1023return Arches[Idx];1024};10251026SmallVector<object::BuildID> FoundBinaryIDs;1027for (const auto &File : llvm::enumerate(ObjectFilenames)) {1028if (Error E =1029loadFromFile(File.value(), GetArch(File.index()), CompilationDir,1030*ProfileReader, *Coverage, DataFound, &FoundBinaryIDs))1031return std::move(E);1032}10331034if (BIDFetcher) {1035std::vector<object::BuildID> ProfileBinaryIDs;1036if (Error E = ProfileReader->readBinaryIds(ProfileBinaryIDs))1037return createFileError(ProfileFilename, std::move(E));10381039SmallVector<object::BuildIDRef> BinaryIDsToFetch;1040if (!ProfileBinaryIDs.empty()) {1041const auto &Compare = [](object::BuildIDRef A, object::BuildIDRef B) {1042return std::lexicographical_compare(A.begin(), A.end(), B.begin(),1043B.end());1044};1045llvm::sort(FoundBinaryIDs, Compare);1046std::set_difference(1047ProfileBinaryIDs.begin(), ProfileBinaryIDs.end(),1048FoundBinaryIDs.begin(), FoundBinaryIDs.end(),1049std::inserter(BinaryIDsToFetch, BinaryIDsToFetch.end()), Compare);1050}10511052for (object::BuildIDRef BinaryID : BinaryIDsToFetch) {1053std::optional<std::string> PathOpt = BIDFetcher->fetch(BinaryID);1054if (PathOpt) {1055std::string Path = std::move(*PathOpt);1056StringRef Arch = Arches.size() == 1 ? Arches.front() : StringRef();1057if (Error E = loadFromFile(Path, Arch, CompilationDir, *ProfileReader,1058*Coverage, DataFound))1059return std::move(E);1060} else if (CheckBinaryIDs) {1061return createFileError(1062ProfileFilename,1063createStringError(errc::no_such_file_or_directory,1064"Missing binary ID: " +1065llvm::toHex(BinaryID, /*LowerCase=*/true)));1066}1067}1068}10691070if (!DataFound)1071return createFileError(1072join(ObjectFilenames.begin(), ObjectFilenames.end(), ", "),1073make_error<CoverageMapError>(coveragemap_error::no_data_found));1074return std::move(Coverage);1075}10761077namespace {10781079/// Distributes functions into instantiation sets.1080///1081/// An instantiation set is a collection of functions that have the same source1082/// code, ie, template functions specializations.1083class FunctionInstantiationSetCollector {1084using MapT = std::map<LineColPair, std::vector<const FunctionRecord *>>;1085MapT InstantiatedFunctions;10861087public:1088void insert(const FunctionRecord &Function, unsigned FileID) {1089auto I = Function.CountedRegions.begin(), E = Function.CountedRegions.end();1090while (I != E && I->FileID != FileID)1091++I;1092assert(I != E && "function does not cover the given file");1093auto &Functions = InstantiatedFunctions[I->startLoc()];1094Functions.push_back(&Function);1095}10961097MapT::iterator begin() { return InstantiatedFunctions.begin(); }1098MapT::iterator end() { return InstantiatedFunctions.end(); }1099};11001101class SegmentBuilder {1102std::vector<CoverageSegment> &Segments;1103SmallVector<const CountedRegion *, 8> ActiveRegions;11041105SegmentBuilder(std::vector<CoverageSegment> &Segments) : Segments(Segments) {}11061107/// Emit a segment with the count from \p Region starting at \p StartLoc.1108//1109/// \p IsRegionEntry: The segment is at the start of a new non-gap region.1110/// \p EmitSkippedRegion: The segment must be emitted as a skipped region.1111void startSegment(const CountedRegion &Region, LineColPair StartLoc,1112bool IsRegionEntry, bool EmitSkippedRegion = false) {1113bool HasCount = !EmitSkippedRegion &&1114(Region.Kind != CounterMappingRegion::SkippedRegion);11151116// If the new segment wouldn't affect coverage rendering, skip it.1117if (!Segments.empty() && !IsRegionEntry && !EmitSkippedRegion) {1118const auto &Last = Segments.back();1119if (Last.HasCount == HasCount && Last.Count == Region.ExecutionCount &&1120!Last.IsRegionEntry)1121return;1122}11231124if (HasCount)1125Segments.emplace_back(StartLoc.first, StartLoc.second,1126Region.ExecutionCount, IsRegionEntry,1127Region.Kind == CounterMappingRegion::GapRegion);1128else1129Segments.emplace_back(StartLoc.first, StartLoc.second, IsRegionEntry);11301131LLVM_DEBUG({1132const auto &Last = Segments.back();1133dbgs() << "Segment at " << Last.Line << ":" << Last.Col1134<< " (count = " << Last.Count << ")"1135<< (Last.IsRegionEntry ? ", RegionEntry" : "")1136<< (!Last.HasCount ? ", Skipped" : "")1137<< (Last.IsGapRegion ? ", Gap" : "") << "\n";1138});1139}11401141/// Emit segments for active regions which end before \p Loc.1142///1143/// \p Loc: The start location of the next region. If std::nullopt, all active1144/// regions are completed.1145/// \p FirstCompletedRegion: Index of the first completed region.1146void completeRegionsUntil(std::optional<LineColPair> Loc,1147unsigned FirstCompletedRegion) {1148// Sort the completed regions by end location. This makes it simple to1149// emit closing segments in sorted order.1150auto CompletedRegionsIt = ActiveRegions.begin() + FirstCompletedRegion;1151std::stable_sort(CompletedRegionsIt, ActiveRegions.end(),1152[](const CountedRegion *L, const CountedRegion *R) {1153return L->endLoc() < R->endLoc();1154});11551156// Emit segments for all completed regions.1157for (unsigned I = FirstCompletedRegion + 1, E = ActiveRegions.size(); I < E;1158++I) {1159const auto *CompletedRegion = ActiveRegions[I];1160assert((!Loc || CompletedRegion->endLoc() <= *Loc) &&1161"Completed region ends after start of new region");11621163const auto *PrevCompletedRegion = ActiveRegions[I - 1];1164auto CompletedSegmentLoc = PrevCompletedRegion->endLoc();11651166// Don't emit any more segments if they start where the new region begins.1167if (Loc && CompletedSegmentLoc == *Loc)1168break;11691170// Don't emit a segment if the next completed region ends at the same1171// location as this one.1172if (CompletedSegmentLoc == CompletedRegion->endLoc())1173continue;11741175// Use the count from the last completed region which ends at this loc.1176for (unsigned J = I + 1; J < E; ++J)1177if (CompletedRegion->endLoc() == ActiveRegions[J]->endLoc())1178CompletedRegion = ActiveRegions[J];11791180startSegment(*CompletedRegion, CompletedSegmentLoc, false);1181}11821183auto Last = ActiveRegions.back();1184if (FirstCompletedRegion && Last->endLoc() != *Loc) {1185// If there's a gap after the end of the last completed region and the1186// start of the new region, use the last active region to fill the gap.1187startSegment(*ActiveRegions[FirstCompletedRegion - 1], Last->endLoc(),1188false);1189} else if (!FirstCompletedRegion && (!Loc || *Loc != Last->endLoc())) {1190// Emit a skipped segment if there are no more active regions. This1191// ensures that gaps between functions are marked correctly.1192startSegment(*Last, Last->endLoc(), false, true);1193}11941195// Pop the completed regions.1196ActiveRegions.erase(CompletedRegionsIt, ActiveRegions.end());1197}11981199void buildSegmentsImpl(ArrayRef<CountedRegion> Regions) {1200for (const auto &CR : enumerate(Regions)) {1201auto CurStartLoc = CR.value().startLoc();12021203// Active regions which end before the current region need to be popped.1204auto CompletedRegions =1205std::stable_partition(ActiveRegions.begin(), ActiveRegions.end(),1206[&](const CountedRegion *Region) {1207return !(Region->endLoc() <= CurStartLoc);1208});1209if (CompletedRegions != ActiveRegions.end()) {1210unsigned FirstCompletedRegion =1211std::distance(ActiveRegions.begin(), CompletedRegions);1212completeRegionsUntil(CurStartLoc, FirstCompletedRegion);1213}12141215bool GapRegion = CR.value().Kind == CounterMappingRegion::GapRegion;12161217// Try to emit a segment for the current region.1218if (CurStartLoc == CR.value().endLoc()) {1219// Avoid making zero-length regions active. If it's the last region,1220// emit a skipped segment. Otherwise use its predecessor's count.1221const bool Skipped =1222(CR.index() + 1) == Regions.size() ||1223CR.value().Kind == CounterMappingRegion::SkippedRegion;1224startSegment(ActiveRegions.empty() ? CR.value() : *ActiveRegions.back(),1225CurStartLoc, !GapRegion, Skipped);1226// If it is skipped segment, create a segment with last pushed1227// regions's count at CurStartLoc.1228if (Skipped && !ActiveRegions.empty())1229startSegment(*ActiveRegions.back(), CurStartLoc, false);1230continue;1231}1232if (CR.index() + 1 == Regions.size() ||1233CurStartLoc != Regions[CR.index() + 1].startLoc()) {1234// Emit a segment if the next region doesn't start at the same location1235// as this one.1236startSegment(CR.value(), CurStartLoc, !GapRegion);1237}12381239// This region is active (i.e not completed).1240ActiveRegions.push_back(&CR.value());1241}12421243// Complete any remaining active regions.1244if (!ActiveRegions.empty())1245completeRegionsUntil(std::nullopt, 0);1246}12471248/// Sort a nested sequence of regions from a single file.1249static void sortNestedRegions(MutableArrayRef<CountedRegion> Regions) {1250llvm::sort(Regions, [](const CountedRegion &LHS, const CountedRegion &RHS) {1251if (LHS.startLoc() != RHS.startLoc())1252return LHS.startLoc() < RHS.startLoc();1253if (LHS.endLoc() != RHS.endLoc())1254// When LHS completely contains RHS, we sort LHS first.1255return RHS.endLoc() < LHS.endLoc();1256// If LHS and RHS cover the same area, we need to sort them according1257// to their kinds so that the most suitable region will become "active"1258// in combineRegions(). Because we accumulate counter values only from1259// regions of the same kind as the first region of the area, prefer1260// CodeRegion to ExpansionRegion and ExpansionRegion to SkippedRegion.1261static_assert(CounterMappingRegion::CodeRegion <1262CounterMappingRegion::ExpansionRegion &&1263CounterMappingRegion::ExpansionRegion <1264CounterMappingRegion::SkippedRegion,1265"Unexpected order of region kind values");1266return LHS.Kind < RHS.Kind;1267});1268}12691270/// Combine counts of regions which cover the same area.1271static ArrayRef<CountedRegion>1272combineRegions(MutableArrayRef<CountedRegion> Regions) {1273if (Regions.empty())1274return Regions;1275auto Active = Regions.begin();1276auto End = Regions.end();1277for (auto I = Regions.begin() + 1; I != End; ++I) {1278if (Active->startLoc() != I->startLoc() ||1279Active->endLoc() != I->endLoc()) {1280// Shift to the next region.1281++Active;1282if (Active != I)1283*Active = *I;1284continue;1285}1286// Merge duplicate region.1287// If CodeRegions and ExpansionRegions cover the same area, it's probably1288// a macro which is fully expanded to another macro. In that case, we need1289// to accumulate counts only from CodeRegions, or else the area will be1290// counted twice.1291// On the other hand, a macro may have a nested macro in its body. If the1292// outer macro is used several times, the ExpansionRegion for the nested1293// macro will also be added several times. These ExpansionRegions cover1294// the same source locations and have to be combined to reach the correct1295// value for that area.1296// We add counts of the regions of the same kind as the active region1297// to handle the both situations.1298if (I->Kind == Active->Kind) {1299assert(I->HasSingleByteCoverage == Active->HasSingleByteCoverage &&1300"Regions are generated in different coverage modes");1301if (I->HasSingleByteCoverage)1302Active->ExecutionCount = Active->ExecutionCount || I->ExecutionCount;1303else1304Active->ExecutionCount += I->ExecutionCount;1305}1306}1307return Regions.drop_back(std::distance(++Active, End));1308}13091310public:1311/// Build a sorted list of CoverageSegments from a list of Regions.1312static std::vector<CoverageSegment>1313buildSegments(MutableArrayRef<CountedRegion> Regions) {1314std::vector<CoverageSegment> Segments;1315SegmentBuilder Builder(Segments);13161317sortNestedRegions(Regions);1318ArrayRef<CountedRegion> CombinedRegions = combineRegions(Regions);13191320LLVM_DEBUG({1321dbgs() << "Combined regions:\n";1322for (const auto &CR : CombinedRegions)1323dbgs() << " " << CR.LineStart << ":" << CR.ColumnStart << " -> "1324<< CR.LineEnd << ":" << CR.ColumnEnd1325<< " (count=" << CR.ExecutionCount << ")\n";1326});13271328Builder.buildSegmentsImpl(CombinedRegions);13291330#ifndef NDEBUG1331for (unsigned I = 1, E = Segments.size(); I < E; ++I) {1332const auto &L = Segments[I - 1];1333const auto &R = Segments[I];1334if (!(L.Line < R.Line) && !(L.Line == R.Line && L.Col < R.Col)) {1335if (L.Line == R.Line && L.Col == R.Col && !L.HasCount)1336continue;1337LLVM_DEBUG(dbgs() << " ! Segment " << L.Line << ":" << L.Col1338<< " followed by " << R.Line << ":" << R.Col << "\n");1339assert(false && "Coverage segments not unique or sorted");1340}1341}1342#endif13431344return Segments;1345}1346};13471348} // end anonymous namespace13491350std::vector<StringRef> CoverageMapping::getUniqueSourceFiles() const {1351std::vector<StringRef> Filenames;1352for (const auto &Function : getCoveredFunctions())1353llvm::append_range(Filenames, Function.Filenames);1354llvm::sort(Filenames);1355auto Last = llvm::unique(Filenames);1356Filenames.erase(Last, Filenames.end());1357return Filenames;1358}13591360static SmallBitVector gatherFileIDs(StringRef SourceFile,1361const FunctionRecord &Function) {1362SmallBitVector FilenameEquivalence(Function.Filenames.size(), false);1363for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I)1364if (SourceFile == Function.Filenames[I])1365FilenameEquivalence[I] = true;1366return FilenameEquivalence;1367}13681369/// Return the ID of the file where the definition of the function is located.1370static std::optional<unsigned>1371findMainViewFileID(const FunctionRecord &Function) {1372SmallBitVector IsNotExpandedFile(Function.Filenames.size(), true);1373for (const auto &CR : Function.CountedRegions)1374if (CR.Kind == CounterMappingRegion::ExpansionRegion)1375IsNotExpandedFile[CR.ExpandedFileID] = false;1376int I = IsNotExpandedFile.find_first();1377if (I == -1)1378return std::nullopt;1379return I;1380}13811382/// Check if SourceFile is the file that contains the definition of1383/// the Function. Return the ID of the file in that case or std::nullopt1384/// otherwise.1385static std::optional<unsigned>1386findMainViewFileID(StringRef SourceFile, const FunctionRecord &Function) {1387std::optional<unsigned> I = findMainViewFileID(Function);1388if (I && SourceFile == Function.Filenames[*I])1389return I;1390return std::nullopt;1391}13921393static bool isExpansion(const CountedRegion &R, unsigned FileID) {1394return R.Kind == CounterMappingRegion::ExpansionRegion && R.FileID == FileID;1395}13961397CoverageData CoverageMapping::getCoverageForFile(StringRef Filename) const {1398CoverageData FileCoverage(Filename);1399std::vector<CountedRegion> Regions;14001401// Look up the function records in the given file. Due to hash collisions on1402// the filename, we may get back some records that are not in the file.1403ArrayRef<unsigned> RecordIndices =1404getImpreciseRecordIndicesForFilename(Filename);1405for (unsigned RecordIndex : RecordIndices) {1406const FunctionRecord &Function = Functions[RecordIndex];1407auto MainFileID = findMainViewFileID(Filename, Function);1408auto FileIDs = gatherFileIDs(Filename, Function);1409for (const auto &CR : Function.CountedRegions)1410if (FileIDs.test(CR.FileID)) {1411Regions.push_back(CR);1412if (MainFileID && isExpansion(CR, *MainFileID))1413FileCoverage.Expansions.emplace_back(CR, Function);1414}1415// Capture branch regions specific to the function (excluding expansions).1416for (const auto &CR : Function.CountedBranchRegions)1417if (FileIDs.test(CR.FileID) && (CR.FileID == CR.ExpandedFileID))1418FileCoverage.BranchRegions.push_back(CR);1419// Capture MCDC records specific to the function.1420for (const auto &MR : Function.MCDCRecords)1421if (FileIDs.test(MR.getDecisionRegion().FileID))1422FileCoverage.MCDCRecords.push_back(MR);1423}14241425LLVM_DEBUG(dbgs() << "Emitting segments for file: " << Filename << "\n");1426FileCoverage.Segments = SegmentBuilder::buildSegments(Regions);14271428return FileCoverage;1429}14301431std::vector<InstantiationGroup>1432CoverageMapping::getInstantiationGroups(StringRef Filename) const {1433FunctionInstantiationSetCollector InstantiationSetCollector;1434// Look up the function records in the given file. Due to hash collisions on1435// the filename, we may get back some records that are not in the file.1436ArrayRef<unsigned> RecordIndices =1437getImpreciseRecordIndicesForFilename(Filename);1438for (unsigned RecordIndex : RecordIndices) {1439const FunctionRecord &Function = Functions[RecordIndex];1440auto MainFileID = findMainViewFileID(Filename, Function);1441if (!MainFileID)1442continue;1443InstantiationSetCollector.insert(Function, *MainFileID);1444}14451446std::vector<InstantiationGroup> Result;1447for (auto &InstantiationSet : InstantiationSetCollector) {1448InstantiationGroup IG{InstantiationSet.first.first,1449InstantiationSet.first.second,1450std::move(InstantiationSet.second)};1451Result.emplace_back(std::move(IG));1452}1453return Result;1454}14551456CoverageData1457CoverageMapping::getCoverageForFunction(const FunctionRecord &Function) const {1458auto MainFileID = findMainViewFileID(Function);1459if (!MainFileID)1460return CoverageData();14611462CoverageData FunctionCoverage(Function.Filenames[*MainFileID]);1463std::vector<CountedRegion> Regions;1464for (const auto &CR : Function.CountedRegions)1465if (CR.FileID == *MainFileID) {1466Regions.push_back(CR);1467if (isExpansion(CR, *MainFileID))1468FunctionCoverage.Expansions.emplace_back(CR, Function);1469}1470// Capture branch regions specific to the function (excluding expansions).1471for (const auto &CR : Function.CountedBranchRegions)1472if (CR.FileID == *MainFileID)1473FunctionCoverage.BranchRegions.push_back(CR);14741475// Capture MCDC records specific to the function.1476for (const auto &MR : Function.MCDCRecords)1477if (MR.getDecisionRegion().FileID == *MainFileID)1478FunctionCoverage.MCDCRecords.push_back(MR);14791480LLVM_DEBUG(dbgs() << "Emitting segments for function: " << Function.Name1481<< "\n");1482FunctionCoverage.Segments = SegmentBuilder::buildSegments(Regions);14831484return FunctionCoverage;1485}14861487CoverageData CoverageMapping::getCoverageForExpansion(1488const ExpansionRecord &Expansion) const {1489CoverageData ExpansionCoverage(1490Expansion.Function.Filenames[Expansion.FileID]);1491std::vector<CountedRegion> Regions;1492for (const auto &CR : Expansion.Function.CountedRegions)1493if (CR.FileID == Expansion.FileID) {1494Regions.push_back(CR);1495if (isExpansion(CR, Expansion.FileID))1496ExpansionCoverage.Expansions.emplace_back(CR, Expansion.Function);1497}1498for (const auto &CR : Expansion.Function.CountedBranchRegions)1499// Capture branch regions that only pertain to the corresponding expansion.1500if (CR.FileID == Expansion.FileID)1501ExpansionCoverage.BranchRegions.push_back(CR);15021503LLVM_DEBUG(dbgs() << "Emitting segments for expansion of file "1504<< Expansion.FileID << "\n");1505ExpansionCoverage.Segments = SegmentBuilder::buildSegments(Regions);15061507return ExpansionCoverage;1508}15091510LineCoverageStats::LineCoverageStats(1511ArrayRef<const CoverageSegment *> LineSegments,1512const CoverageSegment *WrappedSegment, unsigned Line)1513: ExecutionCount(0), HasMultipleRegions(false), Mapped(false), Line(Line),1514LineSegments(LineSegments), WrappedSegment(WrappedSegment) {1515// Find the minimum number of regions which start in this line.1516unsigned MinRegionCount = 0;1517auto isStartOfRegion = [](const CoverageSegment *S) {1518return !S->IsGapRegion && S->HasCount && S->IsRegionEntry;1519};1520for (unsigned I = 0; I < LineSegments.size() && MinRegionCount < 2; ++I)1521if (isStartOfRegion(LineSegments[I]))1522++MinRegionCount;15231524bool StartOfSkippedRegion = !LineSegments.empty() &&1525!LineSegments.front()->HasCount &&1526LineSegments.front()->IsRegionEntry;15271528HasMultipleRegions = MinRegionCount > 1;1529Mapped =1530!StartOfSkippedRegion &&1531((WrappedSegment && WrappedSegment->HasCount) || (MinRegionCount > 0));15321533// if there is any starting segment at this line with a counter, it must be1534// mapped1535Mapped |= std::any_of(1536LineSegments.begin(), LineSegments.end(),1537[](const auto *Seq) { return Seq->IsRegionEntry && Seq->HasCount; });15381539if (!Mapped) {1540return;1541}15421543// Pick the max count from the non-gap, region entry segments and the1544// wrapped count.1545if (WrappedSegment)1546ExecutionCount = WrappedSegment->Count;1547if (!MinRegionCount)1548return;1549for (const auto *LS : LineSegments)1550if (isStartOfRegion(LS))1551ExecutionCount = std::max(ExecutionCount, LS->Count);1552}15531554LineCoverageIterator &LineCoverageIterator::operator++() {1555if (Next == CD.end()) {1556Stats = LineCoverageStats();1557Ended = true;1558return *this;1559}1560if (Segments.size())1561WrappedSegment = Segments.back();1562Segments.clear();1563while (Next != CD.end() && Next->Line == Line)1564Segments.push_back(&*Next++);1565Stats = LineCoverageStats(Segments, WrappedSegment, Line);1566++Line;1567return *this;1568}15691570static std::string getCoverageMapErrString(coveragemap_error Err,1571const std::string &ErrMsg = "") {1572std::string Msg;1573raw_string_ostream OS(Msg);15741575switch (Err) {1576case coveragemap_error::success:1577OS << "success";1578break;1579case coveragemap_error::eof:1580OS << "end of File";1581break;1582case coveragemap_error::no_data_found:1583OS << "no coverage data found";1584break;1585case coveragemap_error::unsupported_version:1586OS << "unsupported coverage format version";1587break;1588case coveragemap_error::truncated:1589OS << "truncated coverage data";1590break;1591case coveragemap_error::malformed:1592OS << "malformed coverage data";1593break;1594case coveragemap_error::decompression_failed:1595OS << "failed to decompress coverage data (zlib)";1596break;1597case coveragemap_error::invalid_or_missing_arch_specifier:1598OS << "`-arch` specifier is invalid or missing for universal binary";1599break;1600}16011602// If optional error message is not empty, append it to the message.1603if (!ErrMsg.empty())1604OS << ": " << ErrMsg;16051606return Msg;1607}16081609namespace {16101611// FIXME: This class is only here to support the transition to llvm::Error. It1612// will be removed once this transition is complete. Clients should prefer to1613// deal with the Error value directly, rather than converting to error_code.1614class CoverageMappingErrorCategoryType : public std::error_category {1615const char *name() const noexcept override { return "llvm.coveragemap"; }1616std::string message(int IE) const override {1617return getCoverageMapErrString(static_cast<coveragemap_error>(IE));1618}1619};16201621} // end anonymous namespace16221623std::string CoverageMapError::message() const {1624return getCoverageMapErrString(Err, Msg);1625}16261627const std::error_category &llvm::coverage::coveragemap_category() {1628static CoverageMappingErrorCategoryType ErrorCategory;1629return ErrorCategory;1630}16311632char CoverageMapError::ID = 0;163316341635