//=-- 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(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::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 &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 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::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::max()) return error(instrprof_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; // 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(); } 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 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; } } } 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 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 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 std::error_code readCoverageMappingData( SectionData &ProfileNames, StringRef Data, std::vector &Records, std::vector &Filenames) { llvm::DenseSet UniqueFunctionMappingData; // Read the records in the coverage data section. while (!Data.empty()) { if (Data.size() < sizeof(CoverageMappingTURecord)) return instrprof_error::malformed; auto TU = reinterpret_cast *>(Data.data()); Data = Data.substr(sizeof(CoverageMappingTURecord)); 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 *>(Data.data()); if (Data.size() < sizeof(CoverageMappingFunctionRecord) * TU->FunctionRecordsSize) return instrprof_error::malformed; Data = Data.substr(sizeof(CoverageMappingFunctionRecord) * 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(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(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 &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(ProfileNames, CoverageMapping, MappingRecords, Filenames)); Object = OwningBinary(std::unique_ptr(), std::move(ObjectBuffer)); return; } auto File = object::ObjectFile::createObjectFile( ObjectBuffer->getMemBufferRef(), Type); if (!File) error(File.getError()); else Object = OwningBinary(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(ProfileNamesData, Data, MappingRecords, Filenames); else Err = readCoverageMappingData(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(); }