diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2023-12-18 20:30:12 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2024-04-19 21:12:03 +0000 |
| commit | c9157d925c489f07ba9c0b2ce47e5149b75969a5 (patch) | |
| tree | 08bc4a3d9cad3f9ebffa558ddf140b9d9257b219 /contrib/llvm-project/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp | |
| parent | 2a66844f606a35d68ad8a8061f4bea204274b3bc (diff) | |
Diffstat (limited to 'contrib/llvm-project/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp')
| -rw-r--r-- | contrib/llvm-project/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp | 533 |
1 files changed, 487 insertions, 46 deletions
diff --git a/contrib/llvm-project/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/contrib/llvm-project/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp index 849ee80bfaa3..eece6a2cc717 100644 --- a/contrib/llvm-project/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp +++ b/contrib/llvm-project/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp @@ -15,7 +15,6 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallBitVector.h" -#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" @@ -31,6 +30,7 @@ #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cassert> +#include <cmath> #include <cstdint> #include <iterator> #include <map> @@ -167,43 +167,373 @@ void CounterMappingContext::dump(const Counter &C, raw_ostream &OS) const { } Expected<int64_t> CounterMappingContext::evaluate(const Counter &C) const { - switch (C.getKind()) { - case Counter::Zero: - return 0; - case Counter::CounterValueReference: - if (C.getCounterID() >= CounterValues.size()) - return errorCodeToError(errc::argument_out_of_domain); - return CounterValues[C.getCounterID()]; - case Counter::Expression: { - if (C.getExpressionID() >= Expressions.size()) - return errorCodeToError(errc::argument_out_of_domain); - const auto &E = Expressions[C.getExpressionID()]; - Expected<int64_t> LHS = evaluate(E.LHS); - if (!LHS) - return LHS; - Expected<int64_t> RHS = evaluate(E.RHS); - if (!RHS) - return RHS; - return E.Kind == CounterExpression::Subtract ? *LHS - *RHS : *LHS + *RHS; + struct StackElem { + Counter ICounter; + int64_t LHS = 0; + enum { + KNeverVisited = 0, + KVisitedOnce = 1, + KVisitedTwice = 2, + } VisitCount = KNeverVisited; + }; + + std::stack<StackElem> CounterStack; + CounterStack.push({C}); + + int64_t LastPoppedValue; + + while (!CounterStack.empty()) { + StackElem &Current = CounterStack.top(); + + switch (Current.ICounter.getKind()) { + case Counter::Zero: + LastPoppedValue = 0; + CounterStack.pop(); + break; + case Counter::CounterValueReference: + if (Current.ICounter.getCounterID() >= CounterValues.size()) + return errorCodeToError(errc::argument_out_of_domain); + LastPoppedValue = CounterValues[Current.ICounter.getCounterID()]; + CounterStack.pop(); + break; + case Counter::Expression: { + if (Current.ICounter.getExpressionID() >= Expressions.size()) + return errorCodeToError(errc::argument_out_of_domain); + const auto &E = Expressions[Current.ICounter.getExpressionID()]; + if (Current.VisitCount == StackElem::KNeverVisited) { + CounterStack.push(StackElem{E.LHS}); + Current.VisitCount = StackElem::KVisitedOnce; + } else if (Current.VisitCount == StackElem::KVisitedOnce) { + Current.LHS = LastPoppedValue; + CounterStack.push(StackElem{E.RHS}); + Current.VisitCount = StackElem::KVisitedTwice; + } else { + int64_t LHS = Current.LHS; + int64_t RHS = LastPoppedValue; + LastPoppedValue = + E.Kind == CounterExpression::Subtract ? LHS - RHS : LHS + RHS; + CounterStack.pop(); + } + break; + } + } } + + return LastPoppedValue; +} + +Expected<BitVector> CounterMappingContext::evaluateBitmap( + const CounterMappingRegion *MCDCDecision) const { + unsigned ID = MCDCDecision->MCDCParams.BitmapIdx; + unsigned NC = MCDCDecision->MCDCParams.NumConditions; + unsigned SizeInBits = llvm::alignTo(uint64_t(1) << NC, CHAR_BIT); + unsigned SizeInBytes = SizeInBits / CHAR_BIT; + + ArrayRef<uint8_t> Bytes(&BitmapBytes[ID], SizeInBytes); + + // Mask each bitmap byte into the BitVector. Go in reverse so that the + // bitvector can just be shifted over by one byte on each iteration. + BitVector Result(SizeInBits, false); + for (auto Byte = std::rbegin(Bytes); Byte != std::rend(Bytes); ++Byte) { + uint32_t Data = *Byte; + Result <<= CHAR_BIT; + Result.setBitsInMask(&Data, 1); } - llvm_unreachable("Unhandled CounterKind"); + return Result; } -unsigned CounterMappingContext::getMaxCounterID(const Counter &C) const { - switch (C.getKind()) { - case Counter::Zero: - return 0; - case Counter::CounterValueReference: - return C.getCounterID(); - case Counter::Expression: { - if (C.getExpressionID() >= Expressions.size()) - return 0; - const auto &E = Expressions[C.getExpressionID()]; - return std::max(getMaxCounterID(E.LHS), getMaxCounterID(E.RHS)); +class MCDCRecordProcessor { + /// A bitmap representing the executed test vectors for a boolean expression. + /// Each index of the bitmap corresponds to a possible test vector. An index + /// with a bit value of '1' indicates that the corresponding Test Vector + /// identified by that index was executed. + BitVector &ExecutedTestVectorBitmap; + + /// Decision Region to which the ExecutedTestVectorBitmap applies. + CounterMappingRegion &Region; + + /// Array of branch regions corresponding each conditions in the boolean + /// expression. + ArrayRef<CounterMappingRegion> Branches; + + /// Total number of conditions in the boolean expression. + unsigned NumConditions; + + /// Mapping of a condition ID to its corresponding branch region. + llvm::DenseMap<unsigned, const CounterMappingRegion *> Map; + + /// Vector used to track whether a condition is constant folded. + MCDCRecord::BoolVector Folded; + + /// Mapping of calculated MC/DC Independence Pairs for each condition. + MCDCRecord::TVPairMap IndependencePairs; + + /// Total number of possible Test Vectors for the boolean expression. + MCDCRecord::TestVectors TestVectors; + + /// Actual executed Test Vectors for the boolean expression, based on + /// ExecutedTestVectorBitmap. + MCDCRecord::TestVectors ExecVectors; + +public: + MCDCRecordProcessor(BitVector &Bitmap, CounterMappingRegion &Region, + ArrayRef<CounterMappingRegion> Branches) + : ExecutedTestVectorBitmap(Bitmap), Region(Region), Branches(Branches), + NumConditions(Region.MCDCParams.NumConditions), + Folded(NumConditions, false), IndependencePairs(NumConditions), + TestVectors((size_t)1 << NumConditions) {} + +private: + void recordTestVector(MCDCRecord::TestVector &TV, + MCDCRecord::CondState Result) { + // Calculate an index that is used to identify the test vector in a vector + // of test vectors. This index also corresponds to the index values of an + // MCDC Region's bitmap (see findExecutedTestVectors()). + unsigned Index = 0; + for (auto Cond = std::rbegin(TV); Cond != std::rend(TV); ++Cond) { + Index <<= 1; + Index |= (*Cond == MCDCRecord::MCDC_True) ? 0x1 : 0x0; + } + + // Copy the completed test vector to the vector of testvectors. + TestVectors[Index] = TV; + + // The final value (T,F) is equal to the last non-dontcare state on the + // path (in a short-circuiting system). + TestVectors[Index].push_back(Result); + } + + void shouldCopyOffTestVectorForTruePath(MCDCRecord::TestVector &TV, + unsigned ID) { + // Branch regions are hashed based on an ID. + const CounterMappingRegion *Branch = Map[ID]; + + TV[ID - 1] = MCDCRecord::MCDC_True; + if (Branch->MCDCParams.TrueID > 0) + buildTestVector(TV, Branch->MCDCParams.TrueID); + else + recordTestVector(TV, MCDCRecord::MCDC_True); + } + + void shouldCopyOffTestVectorForFalsePath(MCDCRecord::TestVector &TV, + unsigned ID) { + // Branch regions are hashed based on an ID. + const CounterMappingRegion *Branch = Map[ID]; + + TV[ID - 1] = MCDCRecord::MCDC_False; + if (Branch->MCDCParams.FalseID > 0) + buildTestVector(TV, Branch->MCDCParams.FalseID); + else + recordTestVector(TV, MCDCRecord::MCDC_False); + } + + /// Starting with the base test vector, build a comprehensive list of + /// possible test vectors by recursively walking the branch condition IDs + /// provided. Once an end node is reached, record the test vector in a vector + /// of test vectors that can be matched against during MC/DC analysis, and + /// then reset the positions to 'DontCare'. + void buildTestVector(MCDCRecord::TestVector &TV, unsigned ID = 1) { + shouldCopyOffTestVectorForTruePath(TV, ID); + shouldCopyOffTestVectorForFalsePath(TV, ID); + + // Reset back to DontCare. + TV[ID - 1] = MCDCRecord::MCDC_DontCare; + } + + /// Walk the bits in the bitmap. A bit set to '1' indicates that the test + /// vector at the corresponding index was executed during a test run. + void findExecutedTestVectors(BitVector &ExecutedTestVectorBitmap) { + for (unsigned Idx = 0; Idx < ExecutedTestVectorBitmap.size(); ++Idx) { + if (ExecutedTestVectorBitmap[Idx] == 0) + continue; + assert(!TestVectors[Idx].empty() && "Test Vector doesn't exist."); + ExecVectors.push_back(TestVectors[Idx]); + } + } + + /// For a given condition and two executed Test Vectors, A and B, see if the + /// two test vectors match forming an Independence Pair for the condition. + /// For two test vectors to match, the following must be satisfied: + /// - The condition's value in each test vector must be opposite. + /// - The result's value in each test vector must be opposite. + /// - All other conditions' values must be equal or marked as "don't care". + bool matchTestVectors(unsigned Aidx, unsigned Bidx, unsigned ConditionIdx) { + const MCDCRecord::TestVector &A = ExecVectors[Aidx]; + const MCDCRecord::TestVector &B = ExecVectors[Bidx]; + + // If condition values in both A and B aren't opposites, no match. + // Because a value can be 0 (false), 1 (true), or -1 (DontCare), a check + // that "XOR != 1" will ensure that the values are opposites and that + // neither of them is a DontCare. + // 1 XOR 0 == 1 | 0 XOR 0 == 0 | -1 XOR 0 == -1 + // 1 XOR 1 == 0 | 0 XOR 1 == 1 | -1 XOR 1 == -2 + // 1 XOR -1 == -2 | 0 XOR -1 == -1 | -1 XOR -1 == 0 + if ((A[ConditionIdx] ^ B[ConditionIdx]) != 1) + return false; + + // If the results of both A and B aren't opposites, no match. + if ((A[NumConditions] ^ B[NumConditions]) != 1) + return false; + + for (unsigned Idx = 0; Idx < NumConditions; ++Idx) { + // Look for other conditions that don't match. Skip over the given + // Condition as well as any conditions marked as "don't care". + const auto ARecordTyForCond = A[Idx]; + const auto BRecordTyForCond = B[Idx]; + if (Idx == ConditionIdx || + ARecordTyForCond == MCDCRecord::MCDC_DontCare || + BRecordTyForCond == MCDCRecord::MCDC_DontCare) + continue; + + // If there is a condition mismatch with any of the other conditions, + // there is no match for the test vectors. + if (ARecordTyForCond != BRecordTyForCond) + return false; + } + + // Otherwise, match. + return true; + } + + /// Find all possible Independence Pairs for a boolean expression given its + /// executed Test Vectors. This process involves looking at each condition + /// and attempting to find two Test Vectors that "match", giving us a pair. + void findIndependencePairs() { + unsigned NumTVs = ExecVectors.size(); + + // For each condition. + for (unsigned C = 0; C < NumConditions; ++C) { + bool PairFound = false; + + // For each executed test vector. + for (unsigned I = 0; !PairFound && I < NumTVs; ++I) { + // Compared to every other executed test vector. + for (unsigned J = 0; !PairFound && J < NumTVs; ++J) { + if (I == J) + continue; + + // If a matching pair of vectors is found, record them. + if ((PairFound = matchTestVectors(I, J, C))) + IndependencePairs[C] = std::make_pair(I + 1, J + 1); + } + } + } + } + +public: + /// Process the MC/DC Record in order to produce a result for a boolean + /// expression. This process includes tracking the conditions that comprise + /// the decision region, calculating the list of all possible test vectors, + /// marking the executed test vectors, and then finding an Independence Pair + /// out of the executed test vectors for each condition in the boolean + /// expression. A condition is tracked to ensure that its ID can be mapped to + /// its ordinal position in the boolean expression. The condition's source + /// location is also tracked, as well as whether it is constant folded (in + /// which case it is excuded from the metric). + MCDCRecord processMCDCRecord() { + unsigned I = 0; + MCDCRecord::CondIDMap PosToID; + MCDCRecord::LineColPairMap CondLoc; + + // Walk the Record's BranchRegions (representing Conditions) in order to: + // - Hash the condition based on its corresponding ID. This will be used to + // calculate the test vectors. + // - Keep a map of the condition's ordinal position (1, 2, 3, 4) to its + // actual ID. This will be used to visualize the conditions in the + // correct order. + // - Keep track of the condition source location. This will be used to + // visualize where the condition is. + // - Record whether the condition is constant folded so that we exclude it + // from being measured. + for (const auto &B : Branches) { + Map[B.MCDCParams.ID] = &B; + PosToID[I] = B.MCDCParams.ID - 1; + CondLoc[I] = B.startLoc(); + Folded[I++] = (B.Count.isZero() && B.FalseCount.isZero()); + } + + // Initialize a base test vector as 'DontCare'. + MCDCRecord::TestVector TV(NumConditions, MCDCRecord::MCDC_DontCare); + + // Use the base test vector to build the list of all possible test vectors. + buildTestVector(TV); + + // Using Profile Bitmap from runtime, mark the executed test vectors. + findExecutedTestVectors(ExecutedTestVectorBitmap); + + // Compare executed test vectors against each other to find an independence + // pairs for each condition. This processing takes the most time. + findIndependencePairs(); + + // Record Test vectors, executed vectors, and independence pairs. + MCDCRecord Res(Region, ExecVectors, IndependencePairs, Folded, PosToID, + CondLoc); + return Res; } +}; + +Expected<MCDCRecord> CounterMappingContext::evaluateMCDCRegion( + CounterMappingRegion Region, BitVector ExecutedTestVectorBitmap, + ArrayRef<CounterMappingRegion> Branches) { + + MCDCRecordProcessor MCDCProcessor(ExecutedTestVectorBitmap, Region, Branches); + return MCDCProcessor.processMCDCRecord(); +} + +unsigned CounterMappingContext::getMaxCounterID(const Counter &C) const { + struct StackElem { + Counter ICounter; + int64_t LHS = 0; + enum { + KNeverVisited = 0, + KVisitedOnce = 1, + KVisitedTwice = 2, + } VisitCount = KNeverVisited; + }; + + std::stack<StackElem> CounterStack; + CounterStack.push({C}); + + int64_t LastPoppedValue; + + while (!CounterStack.empty()) { + StackElem &Current = CounterStack.top(); + + switch (Current.ICounter.getKind()) { + case Counter::Zero: + LastPoppedValue = 0; + CounterStack.pop(); + break; + case Counter::CounterValueReference: + LastPoppedValue = Current.ICounter.getCounterID(); + CounterStack.pop(); + break; + case Counter::Expression: { + if (Current.ICounter.getExpressionID() >= Expressions.size()) { + LastPoppedValue = 0; + CounterStack.pop(); + } else { + const auto &E = Expressions[Current.ICounter.getExpressionID()]; + if (Current.VisitCount == StackElem::KNeverVisited) { + CounterStack.push(StackElem{E.LHS}); + Current.VisitCount = StackElem::KVisitedOnce; + } else if (Current.VisitCount == StackElem::KVisitedOnce) { + Current.LHS = LastPoppedValue; + CounterStack.push(StackElem{E.RHS}); + Current.VisitCount = StackElem::KVisitedTwice; + } else { + int64_t LHS = Current.LHS; + int64_t RHS = LastPoppedValue; + LastPoppedValue = std::max(LHS, RHS); + CounterStack.pop(); + } + } + break; + } + } } - llvm_unreachable("Unhandled CounterKind"); + + return LastPoppedValue; } void FunctionRecordIterator::skipOtherFiles() { @@ -232,12 +562,31 @@ static unsigned getMaxCounterID(const CounterMappingContext &Ctx, return MaxCounterID; } +static unsigned getMaxBitmapSize(const CounterMappingContext &Ctx, + const CoverageMappingRecord &Record) { + unsigned MaxBitmapID = 0; + unsigned NumConditions = 0; + // The last DecisionRegion has the highest bitmap byte index used in the + // function, which when combined with its number of conditions, yields the + // full bitmap size. + for (const auto &Region : reverse(Record.MappingRegions)) { + if (Region.Kind == CounterMappingRegion::MCDCDecisionRegion) { + MaxBitmapID = Region.MCDCParams.BitmapIdx; + NumConditions = Region.MCDCParams.NumConditions; + break; + } + } + unsigned SizeInBits = llvm::alignTo(uint64_t(1) << NumConditions, CHAR_BIT); + return MaxBitmapID + (SizeInBits / CHAR_BIT); +} + Error CoverageMapping::loadFunctionRecord( const CoverageMappingRecord &Record, IndexedInstrProfReader &ProfileReader) { StringRef OrigFuncName = Record.FunctionName; if (OrigFuncName.empty()) - return make_error<CoverageMapError>(coveragemap_error::malformed); + return make_error<CoverageMapError>(coveragemap_error::malformed, + "record function name is empty"); if (Record.Filenames.empty()) OrigFuncName = getFuncNameWithoutPrefix(OrigFuncName); @@ -254,12 +603,28 @@ Error CoverageMapping::loadFunctionRecord( FuncHashMismatches.emplace_back(std::string(Record.FunctionName), Record.FunctionHash); return Error::success(); - } else if (IPE != instrprof_error::unknown_function) + } + if (IPE != instrprof_error::unknown_function) return make_error<InstrProfError>(IPE); Counts.assign(getMaxCounterID(Ctx, Record) + 1, 0); } Ctx.setCounts(Counts); + std::vector<uint8_t> BitmapBytes; + if (Error E = ProfileReader.getFunctionBitmapBytes( + Record.FunctionName, Record.FunctionHash, BitmapBytes)) { + instrprof_error IPE = std::get<0>(InstrProfError::take(std::move(E))); + if (IPE == instrprof_error::hash_mismatch) { + FuncHashMismatches.emplace_back(std::string(Record.FunctionName), + Record.FunctionHash); + return Error::success(); + } + if (IPE != instrprof_error::unknown_function) + return make_error<InstrProfError>(IPE); + BitmapBytes.assign(getMaxBitmapSize(Ctx, Record) + 1, 0); + } + Ctx.setBitmapBytes(BitmapBytes); + assert(!Record.MappingRegions.empty() && "Function has no regions"); // This coverage record is a zero region for a function that's unused in @@ -271,8 +636,20 @@ Error CoverageMapping::loadFunctionRecord( Record.MappingRegions[0].Count.isZero() && Counts[0] > 0) return Error::success(); + unsigned NumConds = 0; + const CounterMappingRegion *MCDCDecision; + std::vector<CounterMappingRegion> MCDCBranches; + FunctionRecord Function(OrigFuncName, Record.Filenames); for (const auto &Region : Record.MappingRegions) { + // If an MCDCDecisionRegion is seen, track the BranchRegions that follow + // it according to Region.NumConditions. + if (Region.Kind == CounterMappingRegion::MCDCDecisionRegion) { + assert(NumConds == 0); + MCDCDecision = &Region; + NumConds = Region.MCDCParams.NumConditions; + continue; + } Expected<int64_t> ExecutionCount = Ctx.evaluate(Region.Count); if (auto E = ExecutionCount.takeError()) { consumeError(std::move(E)); @@ -284,6 +661,44 @@ Error CoverageMapping::loadFunctionRecord( return Error::success(); } Function.pushRegion(Region, *ExecutionCount, *AltExecutionCount); + + // If a MCDCDecisionRegion was seen, store the BranchRegions that + // correspond to it in a vector, according to the number of conditions + // recorded for the region (tracked by NumConds). + if (NumConds > 0 && Region.Kind == CounterMappingRegion::MCDCBranchRegion) { + MCDCBranches.push_back(Region); + + // As we move through all of the MCDCBranchRegions that follow the + // MCDCDecisionRegion, decrement NumConds to make sure we account for + // them all before we calculate the bitmap of executed test vectors. + if (--NumConds == 0) { + // Evaluating the test vector bitmap for the decision region entails + // calculating precisely what bits are pertinent to this region alone. + // This is calculated based on the recorded offset into the global + // profile bitmap; the length is calculated based on the recorded + // number of conditions. + Expected<BitVector> ExecutedTestVectorBitmap = + Ctx.evaluateBitmap(MCDCDecision); + if (auto E = ExecutedTestVectorBitmap.takeError()) { + consumeError(std::move(E)); + return Error::success(); + } + + // Since the bitmap identifies the executed test vectors for an MC/DC + // DecisionRegion, all of the information is now available to process. + // This is where the bulk of the MC/DC progressing takes place. + Expected<MCDCRecord> Record = Ctx.evaluateMCDCRegion( + *MCDCDecision, *ExecutedTestVectorBitmap, MCDCBranches); + if (auto E = Record.takeError()) { + consumeError(std::move(E)); + return Error::success(); + } + + // Save the MC/DC Record so that it can be visualized later. + Function.pushMCDCRecord(*Record); + MCDCBranches.clear(); + } + } } // Don't create records for (filenames, function) pairs we've already seen. @@ -342,7 +757,7 @@ static Error handleMaybeNoDataFoundError(Error E) { std::move(E), [](const CoverageMapError &CME) { if (CME.get() == coveragemap_error::no_data_found) return static_cast<Error>(Error::success()); - return make_error<CoverageMapError>(CME.get()); + return make_error<CoverageMapError>(CME.get(), CME.getMessage()); }); } @@ -790,6 +1205,10 @@ CoverageData CoverageMapping::getCoverageForFile(StringRef Filename) const { for (const auto &CR : Function.CountedBranchRegions) if (FileIDs.test(CR.FileID) && (CR.FileID == CR.ExpandedFileID)) FileCoverage.BranchRegions.push_back(CR); + // Capture MCDC records specific to the function. + for (const auto &MR : Function.MCDCRecords) + if (FileIDs.test(MR.getDecisionRegion().FileID)) + FileCoverage.MCDCRecords.push_back(MR); } LLVM_DEBUG(dbgs() << "Emitting segments for file: " << Filename << "\n"); @@ -842,6 +1261,11 @@ CoverageMapping::getCoverageForFunction(const FunctionRecord &Function) const { if (CR.FileID == *MainFileID) FunctionCoverage.BranchRegions.push_back(CR); + // Capture MCDC records specific to the function. + for (const auto &MR : Function.MCDCRecords) + if (MR.getDecisionRegion().FileID == *MainFileID) + FunctionCoverage.MCDCRecords.push_back(MR); + LLVM_DEBUG(dbgs() << "Emitting segments for function: " << Function.Name << "\n"); FunctionCoverage.Segments = SegmentBuilder::buildSegments(Regions); @@ -925,26 +1349,43 @@ LineCoverageIterator &LineCoverageIterator::operator++() { return *this; } -static std::string getCoverageMapErrString(coveragemap_error Err) { +static std::string getCoverageMapErrString(coveragemap_error Err, + const std::string &ErrMsg = "") { + std::string Msg; + raw_string_ostream OS(Msg); + switch (Err) { case coveragemap_error::success: - return "Success"; + OS << "success"; + break; case coveragemap_error::eof: - return "End of File"; + OS << "end of File"; + break; case coveragemap_error::no_data_found: - return "No coverage data found"; + OS << "no coverage data found"; + break; case coveragemap_error::unsupported_version: - return "Unsupported coverage format version"; + OS << "unsupported coverage format version"; + break; case coveragemap_error::truncated: - return "Truncated coverage data"; + OS << "truncated coverage data"; + break; case coveragemap_error::malformed: - return "Malformed coverage data"; + OS << "malformed coverage data"; + break; case coveragemap_error::decompression_failed: - return "Failed to decompress coverage data (zlib)"; + OS << "failed to decompress coverage data (zlib)"; + break; case coveragemap_error::invalid_or_missing_arch_specifier: - return "`-arch` specifier is invalid or missing for universal binary"; + OS << "`-arch` specifier is invalid or missing for universal binary"; + break; } - llvm_unreachable("A value of coveragemap_error has no message."); + + // If optional error message is not empty, append it to the message. + if (!ErrMsg.empty()) + OS << ": " << ErrMsg; + + return Msg; } namespace { @@ -962,7 +1403,7 @@ class CoverageMappingErrorCategoryType : public std::error_category { } // end anonymous namespace std::string CoverageMapError::message() const { - return getCoverageMapErrString(Err); + return getCoverageMapErrString(Err, Msg); } const std::error_category &llvm::coverage::coveragemap_category() { |
