//===- CoverageMappingReader.cpp - Code coverage mapping reader -----------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file contains support for reading coverage mapping data for // instrumentation based coverage. // //===----------------------------------------------------------------------===// #include "llvm/ProfileData/Coverage/CoverageMappingReader.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/Object/Binary.h" #include "llvm/Object/Error.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/COFF.h" #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" #include using namespace llvm; using namespace coverage; using namespace object; #define DEBUG_TYPE "coverage-mapping" void CoverageMappingIterator::increment() { if (ReadErr != coveragemap_error::success) return; // Check if all the records were read or if an error occurred while reading // the next record. if (auto E = Reader->readNextRecord(Record)) handleAllErrors(std::move(E), [&](const CoverageMapError &CME) { if (CME.get() == coveragemap_error::eof) *this = CoverageMappingIterator(); else ReadErr = CME.get(); }); } Error RawCoverageReader::readULEB128(uint64_t &Result) { if (Data.empty()) return make_error(coveragemap_error::truncated); unsigned N = 0; Result = decodeULEB128(Data.bytes_begin(), &N); if (N > Data.size()) return make_error(coveragemap_error::malformed); Data = Data.substr(N); return Error::success(); } Error RawCoverageReader::readIntMax(uint64_t &Result, uint64_t MaxPlus1) { if (auto Err = readULEB128(Result)) return Err; if (Result >= MaxPlus1) return make_error(coveragemap_error::malformed); return Error::success(); } Error RawCoverageReader::readSize(uint64_t &Result) { if (auto Err = readULEB128(Result)) return Err; // Sanity check the number. if (Result > Data.size()) return make_error(coveragemap_error::malformed); return Error::success(); } Error RawCoverageReader::readString(StringRef &Result) { uint64_t Length; if (auto Err = readSize(Length)) return Err; Result = Data.substr(0, Length); Data = Data.substr(Length); return Error::success(); } Error RawCoverageFilenamesReader::read() { uint64_t NumFilenames; if (auto Err = readSize(NumFilenames)) return Err; for (size_t I = 0; I < NumFilenames; ++I) { StringRef Filename; if (auto Err = readString(Filename)) return Err; Filenames.push_back(Filename); } return Error::success(); } Error RawCoverageMappingReader::decodeCounter(unsigned Value, Counter &C) { auto Tag = Value & Counter::EncodingTagMask; switch (Tag) { case Counter::Zero: C = Counter::getZero(); return Error::success(); case Counter::CounterValueReference: C = Counter::getCounter(Value >> Counter::EncodingTagBits); return Error::success(); default: break; } Tag -= Counter::Expression; switch (Tag) { case CounterExpression::Subtract: case CounterExpression::Add: { auto ID = Value >> Counter::EncodingTagBits; if (ID >= Expressions.size()) return make_error(coveragemap_error::malformed); Expressions[ID].Kind = CounterExpression::ExprKind(Tag); C = Counter::getExpression(ID); break; } default: return make_error(coveragemap_error::malformed); } return Error::success(); } Error RawCoverageMappingReader::readCounter(Counter &C) { uint64_t EncodedCounter; if (auto Err = readIntMax(EncodedCounter, std::numeric_limits::max())) return Err; if (auto Err = decodeCounter(EncodedCounter, C)) return Err; return Error::success(); } static const unsigned EncodingExpansionRegionBit = 1 << Counter::EncodingTagBits; /// Read the sub-array of regions for the given inferred file id. /// \param NumFileIDs the number of file ids that are defined for this /// function. Error RawCoverageMappingReader::readMappingRegionsSubArray( std::vector &MappingRegions, unsigned InferredFileID, size_t NumFileIDs) { uint64_t NumRegions; if (auto Err = readSize(NumRegions)) return Err; unsigned LineStart = 0; for (size_t I = 0; I < NumRegions; ++I) { Counter C; CounterMappingRegion::RegionKind Kind = CounterMappingRegion::CodeRegion; // Read the combined counter + region kind. uint64_t EncodedCounterAndRegion; if (auto Err = readIntMax(EncodedCounterAndRegion, std::numeric_limits::max())) return Err; unsigned Tag = EncodedCounterAndRegion & Counter::EncodingTagMask; uint64_t ExpandedFileID = 0; if (Tag != Counter::Zero) { if (auto Err = decodeCounter(EncodedCounterAndRegion, C)) return Err; } else { // Is it an expansion region? if (EncodedCounterAndRegion & EncodingExpansionRegionBit) { Kind = CounterMappingRegion::ExpansionRegion; ExpandedFileID = EncodedCounterAndRegion >> Counter::EncodingCounterTagAndExpansionRegionTagBits; if (ExpandedFileID >= NumFileIDs) return make_error(coveragemap_error::malformed); } else { switch (EncodedCounterAndRegion >> Counter::EncodingCounterTagAndExpansionRegionTagBits) { case CounterMappingRegion::CodeRegion: // Don't do anything when we have a code region with a zero counter. break; case CounterMappingRegion::SkippedRegion: Kind = CounterMappingRegion::SkippedRegion; break; default: return make_error(coveragemap_error::malformed); } } } // Read the source range. uint64_t LineStartDelta, ColumnStart, NumLines, ColumnEnd; if (auto Err = readIntMax(LineStartDelta, std::numeric_limits::max())) return Err; if (auto Err = readULEB128(ColumnStart)) return Err; if (ColumnStart > std::numeric_limits::max()) return make_error(coveragemap_error::malformed); if (auto Err = readIntMax(NumLines, std::numeric_limits::max())) return Err; if (auto Err = readIntMax(ColumnEnd, std::numeric_limits::max())) return Err; LineStart += LineStartDelta; // If the high bit of ColumnEnd is set, this is a gap region. if (ColumnEnd & (1U << 31)) { Kind = CounterMappingRegion::GapRegion; ColumnEnd &= ~(1U << 31); } // Adjust the column locations for the empty regions that are supposed to // cover whole lines. Those regions should be encoded with the // column range (1 -> std::numeric_limits::max()), but because // the encoded std::numeric_limits::max() is several bytes long, // we set the column range to (0 -> 0) to ensure that the column start and // column end take up one byte each. // The std::numeric_limits::max() is used to represent a column // position at the end of the line without knowing the length of that line. if (ColumnStart == 0 && ColumnEnd == 0) { ColumnStart = 1; ColumnEnd = std::numeric_limits::max(); } LLVM_DEBUG({ dbgs() << "Counter in file " << InferredFileID << " " << LineStart << ":" << ColumnStart << " -> " << (LineStart + NumLines) << ":" << ColumnEnd << ", "; if (Kind == CounterMappingRegion::ExpansionRegion) dbgs() << "Expands to file " << ExpandedFileID; else CounterMappingContext(Expressions).dump(C, dbgs()); dbgs() << "\n"; }); auto CMR = CounterMappingRegion(C, InferredFileID, ExpandedFileID, LineStart, ColumnStart, LineStart + NumLines, ColumnEnd, Kind); if (CMR.startLoc() > CMR.endLoc()) return make_error(coveragemap_error::malformed); MappingRegions.push_back(CMR); } return Error::success(); } Error RawCoverageMappingReader::read() { // Read the virtual file mapping. SmallVector VirtualFileMapping; uint64_t NumFileMappings; if (auto Err = readSize(NumFileMappings)) return Err; for (size_t I = 0; I < NumFileMappings; ++I) { uint64_t FilenameIndex; if (auto Err = readIntMax(FilenameIndex, TranslationUnitFilenames.size())) return Err; VirtualFileMapping.push_back(FilenameIndex); } // Construct the files using unique filenames and virtual file mapping. for (auto I : VirtualFileMapping) { Filenames.push_back(TranslationUnitFilenames[I]); } // Read the expressions. uint64_t NumExpressions; if (auto Err = readSize(NumExpressions)) return Err; // Create an array of dummy expressions that get the proper counters // when the expressions are read, and the proper kinds when the counters // are decoded. Expressions.resize( NumExpressions, CounterExpression(CounterExpression::Subtract, Counter(), Counter())); for (size_t I = 0; I < NumExpressions; ++I) { if (auto Err = readCounter(Expressions[I].LHS)) return Err; if (auto Err = readCounter(Expressions[I].RHS)) return Err; } // Read the mapping regions sub-arrays. for (unsigned InferredFileID = 0, S = VirtualFileMapping.size(); InferredFileID < S; ++InferredFileID) { if (auto Err = readMappingRegionsSubArray(MappingRegions, InferredFileID, VirtualFileMapping.size())) return Err; } // Set the counters for the expansion regions. // i.e. Counter of expansion region = counter of the first region // from the expanded file. // Perform multiple passes to correctly propagate the counters through // all the nested expansion regions. SmallVector FileIDExpansionRegionMapping; FileIDExpansionRegionMapping.resize(VirtualFileMapping.size(), nullptr); for (unsigned Pass = 1, S = VirtualFileMapping.size(); Pass < S; ++Pass) { for (auto &R : MappingRegions) { if (R.Kind != CounterMappingRegion::ExpansionRegion) continue; assert(!FileIDExpansionRegionMapping[R.ExpandedFileID]); FileIDExpansionRegionMapping[R.ExpandedFileID] = &R; } for (auto &R : MappingRegions) { if (FileIDExpansionRegionMapping[R.FileID]) { FileIDExpansionRegionMapping[R.FileID]->Count = R.Count; FileIDExpansionRegionMapping[R.FileID] = nullptr; } } } return Error::success(); } Expected RawCoverageMappingDummyChecker::isDummy() { // A dummy coverage mapping data consists of just one region with zero count. uint64_t NumFileMappings; if (Error Err = readSize(NumFileMappings)) return std::move(Err); if (NumFileMappings != 1) return false; // We don't expect any specific value for the filename index, just skip it. uint64_t FilenameIndex; if (Error Err = readIntMax(FilenameIndex, std::numeric_limits::max())) return std::move(Err); uint64_t NumExpressions; if (Error Err = readSize(NumExpressions)) return std::move(Err); if (NumExpressions != 0) return false; uint64_t NumRegions; if (Error Err = readSize(NumRegions)) return std::move(Err); if (NumRegions != 1) return false; uint64_t EncodedCounterAndRegion; if (Error Err = readIntMax(EncodedCounterAndRegion, std::numeric_limits::max())) return std::move(Err); unsigned Tag = EncodedCounterAndRegion & Counter::EncodingTagMask; return Tag == Counter::Zero; } Error InstrProfSymtab::create(SectionRef &Section) { Expected DataOrErr = Section.getContents(); if (!DataOrErr) return DataOrErr.takeError(); Data = *DataOrErr; Address = Section.getAddress(); // If this is a linked PE/COFF file, then we have to skip over the null byte // that is allocated in the .lprfn$A section in the LLVM profiling runtime. const ObjectFile *Obj = Section.getObject(); if (isa(Obj) && !Obj->isRelocatableObject()) Data = Data.drop_front(1); return Error::success(); } StringRef InstrProfSymtab::getFuncName(uint64_t Pointer, size_t Size) { if (Pointer < Address) return StringRef(); auto Offset = Pointer - Address; if (Offset + Size > Data.size()) return StringRef(); return Data.substr(Pointer - Address, Size); } // Check if the mapping data is a dummy, i.e. is emitted for an unused function. static Expected isCoverageMappingDummy(uint64_t Hash, StringRef Mapping) { // The hash value of dummy mapping records is always zero. if (Hash) return false; return RawCoverageMappingDummyChecker(Mapping).isDummy(); } namespace { struct CovMapFuncRecordReader { virtual ~CovMapFuncRecordReader() = default; // The interface to read coverage mapping function records for a module. // // \p Buf points to the buffer containing the \c CovHeader of the coverage // mapping data associated with the module. // // Returns a pointer to the next \c CovHeader if it exists, or a pointer // greater than \p End if not. virtual Expected readFunctionRecords(const char *Buf, const char *End) = 0; template static Expected> get(CovMapVersion Version, InstrProfSymtab &P, std::vector &R, std::vector &F); }; // A class for reading coverage mapping function records for a module. template class VersionedCovMapFuncRecordReader : public CovMapFuncRecordReader { using FuncRecordType = typename CovMapTraits::CovMapFuncRecordType; using NameRefType = typename CovMapTraits::NameRefType; // Maps function's name references to the indexes of their records // in \c Records. DenseMap FunctionRecords; InstrProfSymtab &ProfileNames; std::vector &Filenames; std::vector &Records; // Add the record to the collection if we don't already have a record that // points to the same function name. This is useful to ignore the redundant // records for the functions with ODR linkage. // In addition, prefer records with real coverage mapping data to dummy // records, which were emitted for inline functions which were seen but // not used in the corresponding translation unit. Error insertFunctionRecordIfNeeded(const FuncRecordType *CFR, StringRef Mapping, size_t FilenamesBegin) { uint64_t FuncHash = CFR->template getFuncHash(); NameRefType NameRef = CFR->template getFuncNameRef(); auto InsertResult = FunctionRecords.insert(std::make_pair(NameRef, Records.size())); if (InsertResult.second) { StringRef FuncName; if (Error Err = CFR->template getFuncName(ProfileNames, FuncName)) return Err; if (FuncName.empty()) return make_error(instrprof_error::malformed); Records.emplace_back(Version, FuncName, FuncHash, Mapping, FilenamesBegin, Filenames.size() - FilenamesBegin); return Error::success(); } // Update the existing record if it's a dummy and the new record is real. size_t OldRecordIndex = InsertResult.first->second; BinaryCoverageReader::ProfileMappingRecord &OldRecord = Records[OldRecordIndex]; Expected OldIsDummyExpected = isCoverageMappingDummy( OldRecord.FunctionHash, OldRecord.CoverageMapping); if (Error Err = OldIsDummyExpected.takeError()) return Err; if (!*OldIsDummyExpected) return Error::success(); Expected NewIsDummyExpected = isCoverageMappingDummy(FuncHash, Mapping); if (Error Err = NewIsDummyExpected.takeError()) return Err; if (*NewIsDummyExpected) return Error::success(); OldRecord.FunctionHash = FuncHash; OldRecord.CoverageMapping = Mapping; OldRecord.FilenamesBegin = FilenamesBegin; OldRecord.FilenamesSize = Filenames.size() - FilenamesBegin; return Error::success(); } public: VersionedCovMapFuncRecordReader( InstrProfSymtab &P, std::vector &R, std::vector &F) : ProfileNames(P), Filenames(F), Records(R) {} ~VersionedCovMapFuncRecordReader() override = default; Expected readFunctionRecords(const char *Buf, const char *End) override { using namespace support; if (Buf + sizeof(CovMapHeader) > End) return make_error(coveragemap_error::malformed); auto CovHeader = reinterpret_cast(Buf); uint32_t NRecords = CovHeader->getNRecords(); uint32_t FilenamesSize = CovHeader->getFilenamesSize(); uint32_t CoverageSize = CovHeader->getCoverageSize(); assert((CovMapVersion)CovHeader->getVersion() == Version); Buf = reinterpret_cast(CovHeader + 1); // Skip past the function records, saving the start and end for later. const char *FunBuf = Buf; Buf += NRecords * sizeof(FuncRecordType); const char *FunEnd = Buf; // Get the filenames. if (Buf + FilenamesSize > End) return make_error(coveragemap_error::malformed); size_t FilenamesBegin = Filenames.size(); RawCoverageFilenamesReader Reader(StringRef(Buf, FilenamesSize), Filenames); if (auto Err = Reader.read()) return std::move(Err); Buf += FilenamesSize; // We'll read the coverage mapping records in the loop below. const char *CovBuf = Buf; Buf += CoverageSize; const char *CovEnd = Buf; if (Buf > End) return make_error(coveragemap_error::malformed); // Each coverage map has an alignment of 8, so we need to adjust alignment // before reading the next map. Buf += offsetToAlignedAddr(Buf, Align(8)); auto CFR = reinterpret_cast(FunBuf); while ((const char *)CFR < FunEnd) { // Read the function information uint32_t DataSize = CFR->template getDataSize(); // Now use that to read the coverage data. if (CovBuf + DataSize > CovEnd) return make_error(coveragemap_error::malformed); auto Mapping = StringRef(CovBuf, DataSize); CovBuf += DataSize; if (Error Err = insertFunctionRecordIfNeeded(CFR, Mapping, FilenamesBegin)) return std::move(Err); CFR++; } return Buf; } }; } // end anonymous namespace template Expected> CovMapFuncRecordReader::get( CovMapVersion Version, InstrProfSymtab &P, std::vector &R, std::vector &F) { using namespace coverage; switch (Version) { case CovMapVersion::Version1: return std::make_unique>(P, R, F); case CovMapVersion::Version2: case CovMapVersion::Version3: // Decompress the name data. if (Error E = P.create(P.getNameData())) return std::move(E); if (Version == CovMapVersion::Version2) return std::make_unique>(P, R, F); else return std::make_unique>(P, R, F); } llvm_unreachable("Unsupported version"); } template static Error readCoverageMappingData( InstrProfSymtab &ProfileNames, StringRef Data, std::vector &Records, std::vector &Filenames) { using namespace coverage; // Read the records in the coverage data section. auto CovHeader = reinterpret_cast(Data.data()); CovMapVersion Version = (CovMapVersion)CovHeader->getVersion(); if (Version > CovMapVersion::CurrentVersion) return make_error(coveragemap_error::unsupported_version); Expected> ReaderExpected = CovMapFuncRecordReader::get(Version, ProfileNames, Records, Filenames); if (Error E = ReaderExpected.takeError()) return E; auto Reader = std::move(ReaderExpected.get()); for (const char *Buf = Data.data(), *End = Buf + Data.size(); Buf < End;) { auto NextHeaderOrErr = Reader->readFunctionRecords(Buf, End); if (auto E = NextHeaderOrErr.takeError()) return E; Buf = NextHeaderOrErr.get(); } return Error::success(); } static const char *TestingFormatMagic = "llvmcovmtestdata"; Expected> BinaryCoverageReader::createCoverageReaderFromBuffer( StringRef Coverage, InstrProfSymtab &&ProfileNames, uint8_t BytesInAddress, support::endianness Endian) { std::unique_ptr Reader(new BinaryCoverageReader()); Reader->ProfileNames = std::move(ProfileNames); if (BytesInAddress == 4 && Endian == support::endianness::little) { if (Error E = readCoverageMappingData( Reader->ProfileNames, Coverage, Reader->MappingRecords, Reader->Filenames)) return std::move(E); } else if (BytesInAddress == 4 && Endian == support::endianness::big) { if (Error E = readCoverageMappingData( Reader->ProfileNames, Coverage, Reader->MappingRecords, Reader->Filenames)) return std::move(E); } else if (BytesInAddress == 8 && Endian == support::endianness::little) { if (Error E = readCoverageMappingData( Reader->ProfileNames, Coverage, Reader->MappingRecords, Reader->Filenames)) return std::move(E); } else if (BytesInAddress == 8 && Endian == support::endianness::big) { if (Error E = readCoverageMappingData( Reader->ProfileNames, Coverage, Reader->MappingRecords, Reader->Filenames)) return std::move(E); } else return make_error(coveragemap_error::malformed); return std::move(Reader); } static Expected> loadTestingFormat(StringRef Data) { uint8_t BytesInAddress = 8; support::endianness Endian = support::endianness::little; Data = Data.substr(StringRef(TestingFormatMagic).size()); if (Data.empty()) return make_error(coveragemap_error::truncated); unsigned N = 0; uint64_t ProfileNamesSize = decodeULEB128(Data.bytes_begin(), &N); if (N > Data.size()) return make_error(coveragemap_error::malformed); Data = Data.substr(N); if (Data.empty()) return make_error(coveragemap_error::truncated); N = 0; uint64_t Address = decodeULEB128(Data.bytes_begin(), &N); if (N > Data.size()) return make_error(coveragemap_error::malformed); Data = Data.substr(N); if (Data.size() < ProfileNamesSize) return make_error(coveragemap_error::malformed); InstrProfSymtab ProfileNames; if (Error E = ProfileNames.create(Data.substr(0, ProfileNamesSize), Address)) return std::move(E); StringRef CoverageMapping = Data.substr(ProfileNamesSize); // Skip the padding bytes because coverage map data has an alignment of 8. if (CoverageMapping.empty()) return make_error(coveragemap_error::truncated); size_t Pad = offsetToAlignedAddr(CoverageMapping.data(), Align(8)); if (CoverageMapping.size() < Pad) return make_error(coveragemap_error::malformed); CoverageMapping = CoverageMapping.substr(Pad); return BinaryCoverageReader::createCoverageReaderFromBuffer( CoverageMapping, std::move(ProfileNames), BytesInAddress, Endian); } static Expected lookupSection(ObjectFile &OF, StringRef Name) { // On COFF, the object file section name may end in "$M". This tells the // linker to sort these sections between "$A" and "$Z". The linker removes the // dollar and everything after it in the final binary. Do the same to match. bool IsCOFF = isa(OF); auto stripSuffix = [IsCOFF](StringRef N) { return IsCOFF ? N.split('$').first : N; }; Name = stripSuffix(Name); for (const auto &Section : OF.sections()) { Expected NameOrErr = Section.getName(); if (!NameOrErr) return NameOrErr.takeError(); if (stripSuffix(*NameOrErr) == Name) return Section; } return make_error(coveragemap_error::no_data_found); } static Expected> loadBinaryFormat(std::unique_ptr Bin, StringRef Arch) { std::unique_ptr OF; if (auto *Universal = dyn_cast(Bin.get())) { // If we have a universal binary, try to look up the object for the // appropriate architecture. auto ObjectFileOrErr = Universal->getMachOObjectForArch(Arch); if (!ObjectFileOrErr) return ObjectFileOrErr.takeError(); OF = std::move(ObjectFileOrErr.get()); } else if (isa(Bin.get())) { // For any other object file, upcast and take ownership. OF.reset(cast(Bin.release())); // If we've asked for a particular arch, make sure they match. if (!Arch.empty() && OF->getArch() != Triple(Arch).getArch()) return errorCodeToError(object_error::arch_not_found); } else // We can only handle object files. return make_error(coveragemap_error::malformed); // The coverage uses native pointer sizes for the object it's written in. uint8_t BytesInAddress = OF->getBytesInAddress(); support::endianness Endian = OF->isLittleEndian() ? support::endianness::little : support::endianness::big; // Look for the sections that we are interested in. auto ObjFormat = OF->getTripleObjectFormat(); auto NamesSection = lookupSection(*OF, getInstrProfSectionName(IPSK_name, ObjFormat, /*AddSegmentInfo=*/false)); if (auto E = NamesSection.takeError()) return std::move(E); auto CoverageSection = lookupSection(*OF, getInstrProfSectionName(IPSK_covmap, ObjFormat, /*AddSegmentInfo=*/false)); if (auto E = CoverageSection.takeError()) return std::move(E); // Get the contents of the given sections. auto CoverageMappingOrErr = CoverageSection->getContents(); if (!CoverageMappingOrErr) return CoverageMappingOrErr.takeError(); InstrProfSymtab ProfileNames; if (Error E = ProfileNames.create(*NamesSection)) return std::move(E); return BinaryCoverageReader::createCoverageReaderFromBuffer( CoverageMappingOrErr.get(), std::move(ProfileNames), BytesInAddress, Endian); } Expected>> BinaryCoverageReader::create( MemoryBufferRef ObjectBuffer, StringRef Arch, SmallVectorImpl> &ObjectFileBuffers) { std::vector> Readers; if (ObjectBuffer.getBuffer().startswith(TestingFormatMagic)) { // This is a special format used for testing. auto ReaderOrErr = loadTestingFormat(ObjectBuffer.getBuffer()); if (!ReaderOrErr) return ReaderOrErr.takeError(); Readers.push_back(std::move(ReaderOrErr.get())); return std::move(Readers); } auto BinOrErr = createBinary(ObjectBuffer); if (!BinOrErr) return BinOrErr.takeError(); std::unique_ptr Bin = std::move(BinOrErr.get()); // MachO universal binaries which contain archives need to be treated as // archives, not as regular binaries. if (auto *Universal = dyn_cast(Bin.get())) { for (auto &ObjForArch : Universal->objects()) { // Skip slices within the universal binary which target the wrong arch. std::string ObjArch = ObjForArch.getArchFlagName(); if (Arch != ObjArch) continue; auto ArchiveOrErr = ObjForArch.getAsArchive(); if (!ArchiveOrErr) { // If this is not an archive, try treating it as a regular object. consumeError(ArchiveOrErr.takeError()); break; } return BinaryCoverageReader::create( ArchiveOrErr.get()->getMemoryBufferRef(), Arch, ObjectFileBuffers); } } // Load coverage out of archive members. if (auto *Ar = dyn_cast(Bin.get())) { Error Err = Error::success(); for (auto &Child : Ar->children(Err)) { Expected ChildBufOrErr = Child.getMemoryBufferRef(); if (!ChildBufOrErr) return ChildBufOrErr.takeError(); auto ChildReadersOrErr = BinaryCoverageReader::create( ChildBufOrErr.get(), Arch, ObjectFileBuffers); if (!ChildReadersOrErr) return ChildReadersOrErr.takeError(); for (auto &Reader : ChildReadersOrErr.get()) Readers.push_back(std::move(Reader)); } if (Err) return std::move(Err); // Thin archives reference object files outside of the archive file, i.e. // files which reside in memory not owned by the caller. Transfer ownership // to the caller. if (Ar->isThin()) for (auto &Buffer : Ar->takeThinBuffers()) ObjectFileBuffers.push_back(std::move(Buffer)); return std::move(Readers); } auto ReaderOrErr = loadBinaryFormat(std::move(Bin), Arch); if (!ReaderOrErr) return ReaderOrErr.takeError(); Readers.push_back(std::move(ReaderOrErr.get())); return std::move(Readers); } Error BinaryCoverageReader::readNextRecord(CoverageMappingRecord &Record) { if (CurrentRecord >= MappingRecords.size()) return make_error(coveragemap_error::eof); FunctionsFilenames.clear(); Expressions.clear(); MappingRegions.clear(); auto &R = MappingRecords[CurrentRecord]; RawCoverageMappingReader Reader( R.CoverageMapping, makeArrayRef(Filenames).slice(R.FilenamesBegin, R.FilenamesSize), FunctionsFilenames, Expressions, MappingRegions); if (auto Err = Reader.read()) return Err; Record.FunctionName = R.FunctionName; Record.FunctionHash = R.FunctionHash; Record.Filenames = FunctionsFilenames; Record.Expressions = Expressions; Record.MappingRegions = MappingRegions; ++CurrentRecord; return Error::success(); }