| //=-- CoverageMappingReader.cpp - Code coverage mapping reader ----*- C++ -*-=// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file contains support for reading coverage mapping data for |
| // instrumentation based coverage. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/ProfileData/CoverageMappingReader.h" |
| #include "llvm/ADT/DenseSet.h" |
| #include "llvm/Object/ObjectFile.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/LEB128.h" |
| |
| using namespace llvm; |
| using namespace coverage; |
| using namespace object; |
| |
| #define DEBUG_TYPE "coverage-mapping" |
| |
| void CoverageMappingIterator::increment() { |
| // Check if all the records were read or if an error occurred while reading |
| // the next record. |
| if (Reader->readNextRecord(Record)) |
| *this = CoverageMappingIterator(); |
| } |
| |
| std::error_code RawCoverageReader::readULEB128(uint64_t &Result) { |
| if (Data.size() < 1) |
| return error(instrprof_error::truncated); |
| unsigned N = 0; |
| Result = decodeULEB128(reinterpret_cast<const uint8_t *>(Data.data()), &N); |
| if (N > Data.size()) |
| return error(instrprof_error::malformed); |
| Data = Data.substr(N); |
| return success(); |
| } |
| |
| std::error_code RawCoverageReader::readIntMax(uint64_t &Result, |
| uint64_t MaxPlus1) { |
| if (auto Err = readULEB128(Result)) |
| return Err; |
| if (Result >= MaxPlus1) |
| return error(instrprof_error::malformed); |
| return success(); |
| } |
| |
| std::error_code RawCoverageReader::readSize(uint64_t &Result) { |
| if (auto Err = readULEB128(Result)) |
| return Err; |
| // Sanity check the number. |
| if (Result > Data.size()) |
| return error(instrprof_error::malformed); |
| return success(); |
| } |
| |
| std::error_code RawCoverageReader::readString(StringRef &Result) { |
| uint64_t Length; |
| if (auto Err = readSize(Length)) |
| return Err; |
| Result = Data.substr(0, Length); |
| Data = Data.substr(Length); |
| return success(); |
| } |
| |
| std::error_code 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 success(); |
| } |
| |
| std::error_code RawCoverageMappingReader::decodeCounter(unsigned Value, |
| Counter &C) { |
| auto Tag = Value & Counter::EncodingTagMask; |
| switch (Tag) { |
| case Counter::Zero: |
| C = Counter::getZero(); |
| return success(); |
| case Counter::CounterValueReference: |
| C = Counter::getCounter(Value >> Counter::EncodingTagBits); |
| return success(); |
| default: |
| break; |
| } |
| Tag -= Counter::Expression; |
| switch (Tag) { |
| case CounterExpression::Subtract: |
| case CounterExpression::Add: { |
| auto ID = Value >> Counter::EncodingTagBits; |
| if (ID >= Expressions.size()) |
| return error(instrprof_error::malformed); |
| Expressions[ID].Kind = CounterExpression::ExprKind(Tag); |
| C = Counter::getExpression(ID); |
| break; |
| } |
| default: |
| return error(instrprof_error::malformed); |
| } |
| return success(); |
| } |
| |
| std::error_code 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 success(); |
| } |
| |
| static const unsigned EncodingExpansionRegionBit = 1 |
| << Counter::EncodingTagBits; |
| |
| /// \brief 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. |
| std::error_code 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 error(instrprof_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 error(instrprof_error::malformed); |
| } |
| } |
| } |
| |
| // Read the source range. |
| uint64_t LineStartDelta, CodeBeforeColumnStart, NumLines, ColumnEnd; |
| if (auto Err = |
| readIntMax(LineStartDelta, std::numeric_limits<unsigned>::max())) |
| return Err; |
| if (auto Err = readULEB128(CodeBeforeColumnStart)) |
| return Err; |
| bool HasCodeBefore = CodeBeforeColumnStart & 1; |
| uint64_t ColumnStart = CodeBeforeColumnStart >> |
| CounterMappingRegion::EncodingHasCodeBeforeBits; |
| if (ColumnStart > std::numeric_limits<unsigned>::max()) |
| return error(instrprof_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; |
| // 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(); |
| } |
| |
| 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"; |
| }); |
| |
| MappingRegions.push_back(CounterMappingRegion( |
| C, InferredFileID, LineStart, ColumnStart, LineStart + NumLines, |
| ColumnEnd, HasCodeBefore, Kind)); |
| MappingRegions.back().ExpandedFileID = ExpandedFileID; |
| } |
| return success(); |
| } |
| |
| std::error_code RawCoverageMappingReader::read(CoverageMappingRecord &Record) { |
| |
| // Read the virtual file mapping. |
| llvm::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; |
| } |
| } |
| } |
| |
| Record.FunctionName = FunctionName; |
| Record.Filenames = Filenames; |
| Record.Expressions = Expressions; |
| Record.MappingRegions = MappingRegions; |
| return success(); |
| } |
| |
| ObjectFileCoverageMappingReader::ObjectFileCoverageMappingReader( |
| StringRef FileName) |
| : CurrentRecord(0) { |
| auto File = llvm::object::ObjectFile::createObjectFile(FileName); |
| if (!File) |
| error(File.getError()); |
| else |
| Object = std::move(File.get()); |
| } |
| |
| namespace { |
| /// \brief The coverage mapping data for a single function. |
| /// It points to the function's name. |
| template <typename IntPtrT> struct CoverageMappingFunctionRecord { |
| IntPtrT FunctionNamePtr; |
| uint32_t FunctionNameSize; |
| uint32_t CoverageMappingSize; |
| uint64_t FunctionHash; |
| }; |
| |
| /// \brief The coverage mapping data for a single translation unit. |
| /// It points to the array of function coverage mapping records and the encoded |
| /// filenames array. |
| template <typename IntPtrT> struct CoverageMappingTURecord { |
| uint32_t FunctionRecordsSize; |
| uint32_t FilenamesSize; |
| uint32_t CoverageMappingsSize; |
| uint32_t Version; |
| }; |
| |
| /// \brief A helper structure to access the data from a section |
| /// in an object file. |
| struct SectionData { |
| StringRef Data; |
| uint64_t Address; |
| |
| std::error_code load(SectionRef &Section) { |
| if (auto Err = Section.getContents(Data)) |
| return Err; |
| Address = Section.getAddress(); |
| return instrprof_error::success; |
| } |
| |
| std::error_code get(uint64_t Pointer, size_t Size, StringRef &Result) { |
| if (Pointer < Address) |
| return instrprof_error::malformed; |
| auto Offset = Pointer - Address; |
| if (Offset + Size > Data.size()) |
| return instrprof_error::malformed; |
| Result = Data.substr(Pointer - Address, Size); |
| return instrprof_error::success; |
| } |
| }; |
| } |
| |
| template <typename T> |
| std::error_code readCoverageMappingData( |
| SectionData &ProfileNames, StringRef Data, |
| std::vector<ObjectFileCoverageMappingReader::ProfileMappingRecord> &Records, |
| std::vector<StringRef> &Filenames) { |
| llvm::DenseSet<T> UniqueFunctionMappingData; |
| |
| // Read the records in the coverage data section. |
| while (!Data.empty()) { |
| if (Data.size() < sizeof(CoverageMappingTURecord<T>)) |
| return instrprof_error::malformed; |
| auto TU = reinterpret_cast<const CoverageMappingTURecord<T> *>(Data.data()); |
| Data = Data.substr(sizeof(CoverageMappingTURecord<T>)); |
| switch (TU->Version) { |
| case CoverageMappingVersion1: |
| break; |
| default: |
| return instrprof_error::unsupported_version; |
| } |
| auto Version = CoverageMappingVersion(TU->Version); |
| |
| // Get the function records. |
| auto FunctionRecords = |
| reinterpret_cast<const CoverageMappingFunctionRecord<T> *>(Data.data()); |
| if (Data.size() < |
| sizeof(CoverageMappingFunctionRecord<T>) * TU->FunctionRecordsSize) |
| return instrprof_error::malformed; |
| Data = Data.substr(sizeof(CoverageMappingFunctionRecord<T>) * |
| TU->FunctionRecordsSize); |
| |
| // Get the filenames. |
| if (Data.size() < TU->FilenamesSize) |
| return instrprof_error::malformed; |
| auto RawFilenames = Data.substr(0, TU->FilenamesSize); |
| Data = Data.substr(TU->FilenamesSize); |
| size_t FilenamesBegin = Filenames.size(); |
| RawCoverageFilenamesReader Reader(RawFilenames, Filenames); |
| if (auto Err = Reader.read()) |
| return Err; |
| |
| // Get the coverage mappings. |
| if (Data.size() < TU->CoverageMappingsSize) |
| return instrprof_error::malformed; |
| auto CoverageMappings = Data.substr(0, TU->CoverageMappingsSize); |
| Data = Data.substr(TU->CoverageMappingsSize); |
| |
| for (unsigned I = 0; I < TU->FunctionRecordsSize; ++I) { |
| auto &MappingRecord = FunctionRecords[I]; |
| |
| // Get the coverage mapping. |
| if (CoverageMappings.size() < MappingRecord.CoverageMappingSize) |
| return instrprof_error::malformed; |
| auto Mapping = |
| CoverageMappings.substr(0, MappingRecord.CoverageMappingSize); |
| CoverageMappings = |
| CoverageMappings.substr(MappingRecord.CoverageMappingSize); |
| |
| // Ignore this record if we 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. |
| if (!UniqueFunctionMappingData.insert(MappingRecord.FunctionNamePtr) |
| .second) |
| continue; |
| StringRef FunctionName; |
| if (auto Err = |
| ProfileNames.get(MappingRecord.FunctionNamePtr, |
| MappingRecord.FunctionNameSize, FunctionName)) |
| return Err; |
| Records.push_back(ObjectFileCoverageMappingReader::ProfileMappingRecord( |
| Version, FunctionName, MappingRecord.FunctionHash, Mapping, |
| FilenamesBegin, Filenames.size() - FilenamesBegin)); |
| } |
| } |
| |
| return instrprof_error::success; |
| } |
| |
| static const char *TestingFormatMagic = "llvmcovmtestdata"; |
| |
| static std::error_code decodeTestingFormat(StringRef Data, |
| SectionData &ProfileNames, |
| StringRef &CoverageMapping) { |
| Data = Data.substr(StringRef(TestingFormatMagic).size()); |
| if (Data.size() < 1) |
| return instrprof_error::truncated; |
| unsigned N = 0; |
| auto ProfileNamesSize = |
| decodeULEB128(reinterpret_cast<const uint8_t *>(Data.data()), &N); |
| if (N > Data.size()) |
| return instrprof_error::malformed; |
| Data = Data.substr(N); |
| if (Data.size() < 1) |
| return instrprof_error::truncated; |
| N = 0; |
| ProfileNames.Address = |
| decodeULEB128(reinterpret_cast<const uint8_t *>(Data.data()), &N); |
| if (N > Data.size()) |
| return instrprof_error::malformed; |
| Data = Data.substr(N); |
| if (Data.size() < ProfileNamesSize) |
| return instrprof_error::malformed; |
| ProfileNames.Data = Data.substr(0, ProfileNamesSize); |
| CoverageMapping = Data.substr(ProfileNamesSize); |
| return instrprof_error::success; |
| } |
| |
| ObjectFileCoverageMappingReader::ObjectFileCoverageMappingReader( |
| std::unique_ptr<MemoryBuffer> &ObjectBuffer, sys::fs::file_magic Type) |
| : CurrentRecord(0) { |
| if (ObjectBuffer->getBuffer().startswith(TestingFormatMagic)) { |
| // This is a special format used for testing. |
| SectionData ProfileNames; |
| StringRef CoverageMapping; |
| if (auto Err = decodeTestingFormat(ObjectBuffer->getBuffer(), ProfileNames, |
| CoverageMapping)) { |
| error(Err); |
| return; |
| } |
| error(readCoverageMappingData<uint64_t>(ProfileNames, CoverageMapping, |
| MappingRecords, Filenames)); |
| Object = OwningBinary<ObjectFile>(std::unique_ptr<ObjectFile>(), |
| std::move(ObjectBuffer)); |
| return; |
| } |
| |
| auto File = object::ObjectFile::createObjectFile( |
| ObjectBuffer->getMemBufferRef(), Type); |
| if (!File) |
| error(File.getError()); |
| else |
| Object = OwningBinary<ObjectFile>(std::move(File.get()), |
| std::move(ObjectBuffer)); |
| } |
| |
| std::error_code ObjectFileCoverageMappingReader::readHeader() { |
| const ObjectFile *OF = Object.getBinary(); |
| if (!OF) |
| return getError(); |
| auto BytesInAddress = OF->getBytesInAddress(); |
| if (BytesInAddress != 4 && BytesInAddress != 8) |
| return error(instrprof_error::malformed); |
| |
| // Look for the sections that we are interested in. |
| int FoundSectionCount = 0; |
| SectionRef ProfileNames, CoverageMapping; |
| for (const auto &Section : OF->sections()) { |
| StringRef Name; |
| if (auto Err = Section.getName(Name)) |
| return Err; |
| if (Name == "__llvm_prf_names") { |
| ProfileNames = Section; |
| } else if (Name == "__llvm_covmap") { |
| CoverageMapping = Section; |
| } else |
| continue; |
| ++FoundSectionCount; |
| } |
| if (FoundSectionCount != 2) |
| return error(instrprof_error::bad_header); |
| |
| // Get the contents of the given sections. |
| StringRef Data; |
| if (auto Err = CoverageMapping.getContents(Data)) |
| return Err; |
| SectionData ProfileNamesData; |
| if (auto Err = ProfileNamesData.load(ProfileNames)) |
| return Err; |
| |
| // Load the data from the found sections. |
| std::error_code Err; |
| if (BytesInAddress == 4) |
| Err = readCoverageMappingData<uint32_t>(ProfileNamesData, Data, |
| MappingRecords, Filenames); |
| else |
| Err = readCoverageMappingData<uint64_t>(ProfileNamesData, Data, |
| MappingRecords, Filenames); |
| if (Err) |
| return error(Err); |
| |
| return success(); |
| } |
| |
| std::error_code |
| ObjectFileCoverageMappingReader::readNextRecord(CoverageMappingRecord &Record) { |
| if (CurrentRecord >= MappingRecords.size()) |
| return error(instrprof_error::eof); |
| |
| FunctionsFilenames.clear(); |
| Expressions.clear(); |
| MappingRegions.clear(); |
| auto &R = MappingRecords[CurrentRecord]; |
| RawCoverageMappingReader Reader( |
| R.FunctionName, R.CoverageMapping, |
| makeArrayRef(Filenames.data() + R.FilenamesBegin, R.FilenamesSize), |
| FunctionsFilenames, Expressions, MappingRegions); |
| if (auto Err = Reader.read(Record)) |
| return Err; |
| Record.FunctionHash = R.FunctionHash; |
| ++CurrentRecord; |
| return success(); |
| } |