| //===- 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 <vector> |
| |
| 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<CoverageMapError>(coveragemap_error::truncated); |
| unsigned N = 0; |
| Result = decodeULEB128(Data.bytes_begin(), &N); |
| if (N > Data.size()) |
| return make_error<CoverageMapError>(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<CoverageMapError>(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<CoverageMapError>(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<CoverageMapError>(coveragemap_error::malformed); |
| Expressions[ID].Kind = CounterExpression::ExprKind(Tag); |
| C = Counter::getExpression(ID); |
| break; |
| } |
| default: |
| return make_error<CoverageMapError>(coveragemap_error::malformed); |
| } |
| return Error::success(); |
| } |
| |
| Error RawCoverageMappingReader::readCounter(Counter &C) { |
| uint64_t EncodedCounter; |
| if (auto Err = |
| readIntMax(EncodedCounter, std::numeric_limits<unsigned>::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<CounterMappingRegion> &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<unsigned>::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<CoverageMapError>(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<CoverageMapError>(coveragemap_error::malformed); |
| } |
| } |
| } |
| |
| // Read the source range. |
| uint64_t LineStartDelta, ColumnStart, NumLines, ColumnEnd; |
| if (auto Err = |
| readIntMax(LineStartDelta, std::numeric_limits<unsigned>::max())) |
| return Err; |
| if (auto Err = readULEB128(ColumnStart)) |
| return Err; |
| if (ColumnStart > std::numeric_limits<unsigned>::max()) |
| return make_error<CoverageMapError>(coveragemap_error::malformed); |
| if (auto Err = readIntMax(NumLines, std::numeric_limits<unsigned>::max())) |
| return Err; |
| if (auto Err = readIntMax(ColumnEnd, std::numeric_limits<unsigned>::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<unsigned>::max()), but because |
| // the encoded std::numeric_limits<unsigned>::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<unsigned>::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<unsigned>::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<CoverageMapError>(coveragemap_error::malformed); |
| MappingRegions.push_back(CMR); |
| } |
| return Error::success(); |
| } |
| |
| Error RawCoverageMappingReader::read() { |
| // Read the virtual file mapping. |
| SmallVector<unsigned, 8> 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<CounterMappingRegion *, 8> 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<bool> 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<unsigned>::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<unsigned>::max())) |
| return std::move(Err); |
| unsigned Tag = EncodedCounterAndRegion & Counter::EncodingTagMask; |
| return Tag == Counter::Zero; |
| } |
| |
| Error InstrProfSymtab::create(SectionRef &Section) { |
| Expected<StringRef> 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<COFFObjectFile>(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<bool> 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<const char *> readFunctionRecords(const char *Buf, |
| const char *End) = 0; |
| |
| template <class IntPtrT, support::endianness Endian> |
| static Expected<std::unique_ptr<CovMapFuncRecordReader>> |
| get(CovMapVersion Version, InstrProfSymtab &P, |
| std::vector<BinaryCoverageReader::ProfileMappingRecord> &R, |
| std::vector<StringRef> &F); |
| }; |
| |
| // A class for reading coverage mapping function records for a module. |
| template <CovMapVersion Version, class IntPtrT, support::endianness Endian> |
| class VersionedCovMapFuncRecordReader : public CovMapFuncRecordReader { |
| using FuncRecordType = |
| typename CovMapTraits<Version, IntPtrT>::CovMapFuncRecordType; |
| using NameRefType = typename CovMapTraits<Version, IntPtrT>::NameRefType; |
| |
| // Maps function's name references to the indexes of their records |
| // in \c Records. |
| DenseMap<NameRefType, size_t> FunctionRecords; |
| InstrProfSymtab &ProfileNames; |
| std::vector<StringRef> &Filenames; |
| std::vector<BinaryCoverageReader::ProfileMappingRecord> &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<Endian>(); |
| NameRefType NameRef = CFR->template getFuncNameRef<Endian>(); |
| auto InsertResult = |
| FunctionRecords.insert(std::make_pair(NameRef, Records.size())); |
| if (InsertResult.second) { |
| StringRef FuncName; |
| if (Error Err = CFR->template getFuncName<Endian>(ProfileNames, FuncName)) |
| return Err; |
| if (FuncName.empty()) |
| return make_error<InstrProfError>(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<bool> OldIsDummyExpected = isCoverageMappingDummy( |
| OldRecord.FunctionHash, OldRecord.CoverageMapping); |
| if (Error Err = OldIsDummyExpected.takeError()) |
| return Err; |
| if (!*OldIsDummyExpected) |
| return Error::success(); |
| Expected<bool> 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<BinaryCoverageReader::ProfileMappingRecord> &R, |
| std::vector<StringRef> &F) |
| : ProfileNames(P), Filenames(F), Records(R) {} |
| |
| ~VersionedCovMapFuncRecordReader() override = default; |
| |
| Expected<const char *> readFunctionRecords(const char *Buf, |
| const char *End) override { |
| using namespace support; |
| |
| if (Buf + sizeof(CovMapHeader) > End) |
| return make_error<CoverageMapError>(coveragemap_error::malformed); |
| auto CovHeader = reinterpret_cast<const CovMapHeader *>(Buf); |
| uint32_t NRecords = CovHeader->getNRecords<Endian>(); |
| uint32_t FilenamesSize = CovHeader->getFilenamesSize<Endian>(); |
| uint32_t CoverageSize = CovHeader->getCoverageSize<Endian>(); |
| assert((CovMapVersion)CovHeader->getVersion<Endian>() == Version); |
| Buf = reinterpret_cast<const char *>(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<CoverageMapError>(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<CoverageMapError>(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<const FuncRecordType *>(FunBuf); |
| while ((const char *)CFR < FunEnd) { |
| // Read the function information |
| uint32_t DataSize = CFR->template getDataSize<Endian>(); |
| |
| // Now use that to read the coverage data. |
| if (CovBuf + DataSize > CovEnd) |
| return make_error<CoverageMapError>(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 <class IntPtrT, support::endianness Endian> |
| Expected<std::unique_ptr<CovMapFuncRecordReader>> CovMapFuncRecordReader::get( |
| CovMapVersion Version, InstrProfSymtab &P, |
| std::vector<BinaryCoverageReader::ProfileMappingRecord> &R, |
| std::vector<StringRef> &F) { |
| using namespace coverage; |
| |
| switch (Version) { |
| case CovMapVersion::Version1: |
| return std::make_unique<VersionedCovMapFuncRecordReader< |
| CovMapVersion::Version1, IntPtrT, Endian>>(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<VersionedCovMapFuncRecordReader< |
| CovMapVersion::Version2, IntPtrT, Endian>>(P, R, F); |
| else |
| return std::make_unique<VersionedCovMapFuncRecordReader< |
| CovMapVersion::Version3, IntPtrT, Endian>>(P, R, F); |
| } |
| llvm_unreachable("Unsupported version"); |
| } |
| |
| template <typename T, support::endianness Endian> |
| static Error readCoverageMappingData( |
| InstrProfSymtab &ProfileNames, StringRef Data, |
| std::vector<BinaryCoverageReader::ProfileMappingRecord> &Records, |
| std::vector<StringRef> &Filenames) { |
| using namespace coverage; |
| |
| // Read the records in the coverage data section. |
| auto CovHeader = |
| reinterpret_cast<const CovMapHeader *>(Data.data()); |
| CovMapVersion Version = (CovMapVersion)CovHeader->getVersion<Endian>(); |
| if (Version > CovMapVersion::CurrentVersion) |
| return make_error<CoverageMapError>(coveragemap_error::unsupported_version); |
| Expected<std::unique_ptr<CovMapFuncRecordReader>> ReaderExpected = |
| CovMapFuncRecordReader::get<T, Endian>(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<std::unique_ptr<BinaryCoverageReader>> |
| BinaryCoverageReader::createCoverageReaderFromBuffer( |
| StringRef Coverage, InstrProfSymtab &&ProfileNames, uint8_t BytesInAddress, |
| support::endianness Endian) { |
| std::unique_ptr<BinaryCoverageReader> Reader(new BinaryCoverageReader()); |
| Reader->ProfileNames = std::move(ProfileNames); |
| if (BytesInAddress == 4 && Endian == support::endianness::little) { |
| if (Error E = |
| readCoverageMappingData<uint32_t, support::endianness::little>( |
| Reader->ProfileNames, Coverage, Reader->MappingRecords, |
| Reader->Filenames)) |
| return std::move(E); |
| } else if (BytesInAddress == 4 && Endian == support::endianness::big) { |
| if (Error E = readCoverageMappingData<uint32_t, support::endianness::big>( |
| Reader->ProfileNames, Coverage, Reader->MappingRecords, |
| Reader->Filenames)) |
| return std::move(E); |
| } else if (BytesInAddress == 8 && Endian == support::endianness::little) { |
| if (Error E = |
| readCoverageMappingData<uint64_t, support::endianness::little>( |
| Reader->ProfileNames, Coverage, Reader->MappingRecords, |
| Reader->Filenames)) |
| return std::move(E); |
| } else if (BytesInAddress == 8 && Endian == support::endianness::big) { |
| if (Error E = readCoverageMappingData<uint64_t, support::endianness::big>( |
| Reader->ProfileNames, Coverage, Reader->MappingRecords, |
| Reader->Filenames)) |
| return std::move(E); |
| } else |
| return make_error<CoverageMapError>(coveragemap_error::malformed); |
| return std::move(Reader); |
| } |
| |
| static Expected<std::unique_ptr<BinaryCoverageReader>> |
| 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<CoverageMapError>(coveragemap_error::truncated); |
| unsigned N = 0; |
| uint64_t ProfileNamesSize = decodeULEB128(Data.bytes_begin(), &N); |
| if (N > Data.size()) |
| return make_error<CoverageMapError>(coveragemap_error::malformed); |
| Data = Data.substr(N); |
| if (Data.empty()) |
| return make_error<CoverageMapError>(coveragemap_error::truncated); |
| N = 0; |
| uint64_t Address = decodeULEB128(Data.bytes_begin(), &N); |
| if (N > Data.size()) |
| return make_error<CoverageMapError>(coveragemap_error::malformed); |
| Data = Data.substr(N); |
| if (Data.size() < ProfileNamesSize) |
| return make_error<CoverageMapError>(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<CoverageMapError>(coveragemap_error::truncated); |
| size_t Pad = offsetToAlignedAddr(CoverageMapping.data(), Align(8)); |
| if (CoverageMapping.size() < Pad) |
| return make_error<CoverageMapError>(coveragemap_error::malformed); |
| CoverageMapping = CoverageMapping.substr(Pad); |
| return BinaryCoverageReader::createCoverageReaderFromBuffer( |
| CoverageMapping, std::move(ProfileNames), BytesInAddress, Endian); |
| } |
| |
| static Expected<SectionRef> 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<COFFObjectFile>(OF); |
| auto stripSuffix = [IsCOFF](StringRef N) { |
| return IsCOFF ? N.split('$').first : N; |
| }; |
| Name = stripSuffix(Name); |
| |
| for (const auto &Section : OF.sections()) { |
| Expected<StringRef> NameOrErr = Section.getName(); |
| if (!NameOrErr) |
| return NameOrErr.takeError(); |
| if (stripSuffix(*NameOrErr) == Name) |
| return Section; |
| } |
| return make_error<CoverageMapError>(coveragemap_error::no_data_found); |
| } |
| |
| static Expected<std::unique_ptr<BinaryCoverageReader>> |
| loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch) { |
| std::unique_ptr<ObjectFile> OF; |
| if (auto *Universal = dyn_cast<MachOUniversalBinary>(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<ObjectFile>(Bin.get())) { |
| // For any other object file, upcast and take ownership. |
| OF.reset(cast<ObjectFile>(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<CoverageMapError>(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<std::vector<std::unique_ptr<BinaryCoverageReader>>> |
| BinaryCoverageReader::create( |
| MemoryBufferRef ObjectBuffer, StringRef Arch, |
| SmallVectorImpl<std::unique_ptr<MemoryBuffer>> &ObjectFileBuffers) { |
| std::vector<std::unique_ptr<BinaryCoverageReader>> 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<Binary> 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<MachOUniversalBinary>(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<Archive>(Bin.get())) { |
| Error Err = Error::success(); |
| for (auto &Child : Ar->children(Err)) { |
| Expected<MemoryBufferRef> 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<CoverageMapError>(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(); |
| } |