diff options
Diffstat (limited to 'llvm/lib/ProfileData')
-rw-r--r-- | llvm/lib/ProfileData/Coverage/CoverageMapping.cpp | 5 | ||||
-rw-r--r-- | llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp | 377 | ||||
-rw-r--r-- | llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp | 34 | ||||
-rw-r--r-- | llvm/lib/ProfileData/GCOV.cpp | 592 | ||||
-rw-r--r-- | llvm/lib/ProfileData/InstrProf.cpp | 8 | ||||
-rw-r--r-- | llvm/lib/ProfileData/InstrProfReader.cpp | 7 | ||||
-rw-r--r-- | llvm/lib/ProfileData/ProfileSummaryBuilder.cpp | 13 | ||||
-rw-r--r-- | llvm/lib/ProfileData/SampleProf.cpp | 1 | ||||
-rw-r--r-- | llvm/lib/ProfileData/SampleProfReader.cpp | 117 | ||||
-rw-r--r-- | llvm/lib/ProfileData/SampleProfWriter.cpp | 32 |
10 files changed, 742 insertions, 444 deletions
diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp index 8d5e56e26c0f..70f00d333db1 100644 --- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp @@ -222,7 +222,8 @@ Error CoverageMapping::loadFunctionRecord( Record.FunctionHash, Counts)) { instrprof_error IPE = InstrProfError::take(std::move(E)); if (IPE == instrprof_error::hash_mismatch) { - FuncHashMismatches.emplace_back(Record.FunctionName, Record.FunctionHash); + FuncHashMismatches.emplace_back(std::string(Record.FunctionName), + Record.FunctionHash); return Error::success(); } else if (IPE != instrprof_error::unknown_function) return make_error<InstrProfError>(IPE); @@ -804,6 +805,8 @@ static std::string getCoverageMapErrString(coveragemap_error Err) { return "Truncated coverage data"; case coveragemap_error::malformed: return "Malformed coverage data"; + case coveragemap_error::decompression_failed: + return "Failed to decompress coverage data (zlib)"; } llvm_unreachable("A value of coveragemap_error has no message."); } diff --git a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp index 679ff3525eeb..b75738bc360c 100644 --- a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/Object/Binary.h" @@ -25,6 +26,7 @@ #include "llvm/Object/COFF.h" #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" @@ -40,6 +42,9 @@ using namespace object; #define DEBUG_TYPE "coverage-mapping" +STATISTIC(CovMapNumRecords, "The # of coverage function records"); +STATISTIC(CovMapNumUsedRecords, "The # of used coverage function records"); + void CoverageMappingIterator::increment() { if (ReadErr != coveragemap_error::success) return; @@ -92,10 +97,60 @@ Error RawCoverageReader::readString(StringRef &Result) { return Error::success(); } -Error RawCoverageFilenamesReader::read() { +Error RawCoverageFilenamesReader::read( + CovMapVersion Version, + BinaryCoverageReader::DecompressedData &Decompressed) { uint64_t NumFilenames; if (auto Err = readSize(NumFilenames)) return Err; + if (!NumFilenames) + return make_error<CoverageMapError>(coveragemap_error::malformed); + + if (Version < CovMapVersion::Version4) + return readUncompressed(NumFilenames); + + // The uncompressed length may exceed the size of the encoded filenames. + // Skip size validation. + uint64_t UncompressedLen; + if (auto Err = readULEB128(UncompressedLen)) + return Err; + + uint64_t CompressedLen; + if (auto Err = readSize(CompressedLen)) + return Err; + + if (CompressedLen > 0) { + if (!zlib::isAvailable()) + return make_error<CoverageMapError>( + coveragemap_error::decompression_failed); + + // Allocate memory for the decompressed filenames. Transfer ownership of + // the memory to BinaryCoverageReader. + auto DecompressedStorage = std::make_unique<SmallVector<char, 0>>(); + SmallVectorImpl<char> &StorageBuf = *DecompressedStorage.get(); + Decompressed.push_back(std::move(DecompressedStorage)); + + // Read compressed filenames. + StringRef CompressedFilenames = Data.substr(0, CompressedLen); + Data = Data.substr(CompressedLen); + auto Err = + zlib::uncompress(CompressedFilenames, StorageBuf, UncompressedLen); + if (Err) { + consumeError(std::move(Err)); + return make_error<CoverageMapError>( + coveragemap_error::decompression_failed); + } + + StringRef UncompressedFilenames(StorageBuf.data(), StorageBuf.size()); + RawCoverageFilenamesReader Delegate(UncompressedFilenames, Filenames); + return Delegate.readUncompressed(NumFilenames); + } + + return readUncompressed(NumFilenames); +} + +Error RawCoverageFilenamesReader::readUncompressed(uint64_t NumFilenames) { + // Read uncompressed filenames. for (size_t I = 0; I < NumFilenames; ++I) { StringRef Filename; if (auto Err = readString(Filename)) @@ -380,20 +435,51 @@ static Expected<bool> isCoverageMappingDummy(uint64_t Hash, StringRef Mapping) { return RawCoverageMappingDummyChecker(Mapping).isDummy(); } +/// A range of filename indices. Used to specify the location of a batch of +/// filenames in a vector-like container. +struct FilenameRange { + unsigned StartingIndex; + unsigned Length; + + FilenameRange(unsigned StartingIndex, unsigned Length) + : StartingIndex(StartingIndex), Length(Length) {} + + void markInvalid() { Length = 0; } + bool isInvalid() const { return Length == 0; } +}; + namespace { +/// The interface to read coverage mapping function records for a module. struct CovMapFuncRecordReader { virtual ~CovMapFuncRecordReader() = default; - // The interface to read coverage mapping function records for a module. + // Read a coverage header. // - // \p Buf points to the buffer containing the \c CovHeader of the coverage + // \p CovBuf 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; + // Returns a pointer to the next \c CovHeader if it exists, or to an address + // greater than \p CovEnd if not. + virtual Expected<const char *> + readCoverageHeader(const char *CovBuf, const char *CovBufEnd, + BinaryCoverageReader::DecompressedData &Decompressed) = 0; + + // Read function records. + // + // \p FuncRecBuf points to the buffer containing a batch of function records. + // \p FuncRecBufEnd points past the end of the batch of records. + // + // Prior to Version4, \p OutOfLineFileRange points to a sequence of filenames + // associated with the function records. It is unused in Version4. + // + // Prior to Version4, \p OutOfLineMappingBuf points to a sequence of coverage + // mappings associated with the function records. It is unused in Version4. + virtual Error readFunctionRecords(const char *FuncRecBuf, + const char *FuncRecBufEnd, + Optional<FilenameRange> OutOfLineFileRange, + const char *OutOfLineMappingBuf, + const char *OutOfLineMappingBufEnd) = 0; template <class IntPtrT, support::endianness Endian> static Expected<std::unique_ptr<CovMapFuncRecordReader>> @@ -416,6 +502,10 @@ class VersionedCovMapFuncRecordReader : public CovMapFuncRecordReader { std::vector<StringRef> &Filenames; std::vector<BinaryCoverageReader::ProfileMappingRecord> &Records; + // Maps a hash of the filenames in a TU to a \c FileRange. The range + // specifies the location of the hashed filenames in \c Filenames. + DenseMap<uint64_t, FilenameRange> FileRangeMap; + // 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. @@ -423,7 +513,9 @@ class VersionedCovMapFuncRecordReader : public CovMapFuncRecordReader { // 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) { + StringRef Mapping, + FilenameRange FileRange) { + ++CovMapNumRecords; uint64_t FuncHash = CFR->template getFuncHash<Endian>(); NameRefType NameRef = CFR->template getFuncNameRef<Endian>(); auto InsertResult = @@ -434,8 +526,9 @@ class VersionedCovMapFuncRecordReader : public CovMapFuncRecordReader { return Err; if (FuncName.empty()) return make_error<InstrProfError>(instrprof_error::malformed); - Records.emplace_back(Version, FuncName, FuncHash, Mapping, FilenamesBegin, - Filenames.size() - FilenamesBegin); + ++CovMapNumUsedRecords; + Records.emplace_back(Version, FuncName, FuncHash, Mapping, + FileRange.StartingIndex, FileRange.Length); return Error::success(); } // Update the existing record if it's a dummy and the new record is real. @@ -454,10 +547,11 @@ class VersionedCovMapFuncRecordReader : public CovMapFuncRecordReader { return Err; if (*NewIsDummyExpected) return Error::success(); + ++CovMapNumUsedRecords; OldRecord.FunctionHash = FuncHash; OldRecord.CoverageMapping = Mapping; - OldRecord.FilenamesBegin = FilenamesBegin; - OldRecord.FilenamesSize = Filenames.size() - FilenamesBegin; + OldRecord.FilenamesBegin = FileRange.StartingIndex; + OldRecord.FilenamesSize = FileRange.Length; return Error::success(); } @@ -470,61 +564,134 @@ public: ~VersionedCovMapFuncRecordReader() override = default; - Expected<const char *> readFunctionRecords(const char *Buf, - const char *End) override { + Expected<const char *> readCoverageHeader( + const char *CovBuf, const char *CovBufEnd, + BinaryCoverageReader::DecompressedData &Decompressed) override { using namespace support; - if (Buf + sizeof(CovMapHeader) > End) + if (CovBuf + sizeof(CovMapHeader) > CovBufEnd) return make_error<CoverageMapError>(coveragemap_error::malformed); - auto CovHeader = reinterpret_cast<const CovMapHeader *>(Buf); + auto CovHeader = reinterpret_cast<const CovMapHeader *>(CovBuf); 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); + CovBuf = 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; + // This is a no-op in Version4 (function records are read after all headers + // are read). + const char *FuncRecBuf = nullptr; + const char *FuncRecBufEnd = nullptr; + if (Version < CovMapVersion::Version4) + FuncRecBuf = CovBuf; + CovBuf += NRecords * sizeof(FuncRecordType); + if (Version < CovMapVersion::Version4) + FuncRecBufEnd = CovBuf; // Get the filenames. - if (Buf + FilenamesSize > End) + if (CovBuf + FilenamesSize > CovBufEnd) return make_error<CoverageMapError>(coveragemap_error::malformed); size_t FilenamesBegin = Filenames.size(); - RawCoverageFilenamesReader Reader(StringRef(Buf, FilenamesSize), Filenames); - if (auto Err = Reader.read()) + StringRef FilenameRegion(CovBuf, FilenamesSize); + RawCoverageFilenamesReader Reader(FilenameRegion, Filenames); + if (auto Err = Reader.read(Version, Decompressed)) return std::move(Err); - Buf += FilenamesSize; + CovBuf += FilenamesSize; + FilenameRange FileRange(FilenamesBegin, Filenames.size() - FilenamesBegin); + + if (Version == CovMapVersion::Version4) { + // Map a hash of the filenames region to the filename range associated + // with this coverage header. + int64_t FilenamesRef = + llvm::IndexedInstrProf::ComputeHash(FilenameRegion); + auto Insert = + FileRangeMap.insert(std::make_pair(FilenamesRef, FileRange)); + if (!Insert.second) { + // The same filenames ref was encountered twice. It's possible that + // the associated filenames are the same. + auto It = Filenames.begin(); + FilenameRange &OrigRange = Insert.first->getSecond(); + if (std::equal(It + OrigRange.StartingIndex, + It + OrigRange.StartingIndex + OrigRange.Length, + It + FileRange.StartingIndex, + It + FileRange.StartingIndex + FileRange.Length)) + // Map the new range to the original one. + FileRange = OrigRange; + else + // This is a hash collision. Mark the filenames ref invalid. + OrigRange.markInvalid(); + } + } // We'll read the coverage mapping records in the loop below. - const char *CovBuf = Buf; - Buf += CoverageSize; - const char *CovEnd = Buf; + // This is a no-op in Version4 (coverage mappings are not affixed to the + // coverage header). + const char *MappingBuf = CovBuf; + if (Version == CovMapVersion::Version4 && CoverageSize != 0) + return make_error<CoverageMapError>(coveragemap_error::malformed); + CovBuf += CoverageSize; + const char *MappingEnd = CovBuf; - if (Buf > End) + if (CovBuf > CovBufEnd) return make_error<CoverageMapError>(coveragemap_error::malformed); + + if (Version < CovMapVersion::Version4) { + // Read each function record. + if (Error E = readFunctionRecords(FuncRecBuf, FuncRecBufEnd, FileRange, + MappingBuf, MappingEnd)) + return std::move(E); + } + // 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++; + CovBuf += offsetToAlignedAddr(CovBuf, Align(8)); + + return CovBuf; + } + + Error readFunctionRecords(const char *FuncRecBuf, const char *FuncRecBufEnd, + Optional<FilenameRange> OutOfLineFileRange, + const char *OutOfLineMappingBuf, + const char *OutOfLineMappingBufEnd) override { + auto CFR = reinterpret_cast<const FuncRecordType *>(FuncRecBuf); + while ((const char *)CFR < FuncRecBufEnd) { + // Validate the length of the coverage mapping for this function. + const char *NextMappingBuf; + const FuncRecordType *NextCFR; + std::tie(NextMappingBuf, NextCFR) = + CFR->template advanceByOne<Endian>(OutOfLineMappingBuf); + if (Version < CovMapVersion::Version4) + if (NextMappingBuf > OutOfLineMappingBufEnd) + return make_error<CoverageMapError>(coveragemap_error::malformed); + + // Look up the set of filenames associated with this function record. + Optional<FilenameRange> FileRange; + if (Version < CovMapVersion::Version4) { + FileRange = OutOfLineFileRange; + } else { + uint64_t FilenamesRef = CFR->template getFilenamesRef<Endian>(); + auto It = FileRangeMap.find(FilenamesRef); + if (It == FileRangeMap.end()) + return make_error<CoverageMapError>(coveragemap_error::malformed); + else + FileRange = It->getSecond(); + } + + // Now, read the coverage data. + if (FileRange && !FileRange->isInvalid()) { + StringRef Mapping = + CFR->template getCoverageMapping<Endian>(OutOfLineMappingBuf); + if (Version == CovMapVersion::Version4 && + Mapping.data() + Mapping.size() > FuncRecBufEnd) + return make_error<CoverageMapError>(coveragemap_error::malformed); + if (Error Err = insertFunctionRecordIfNeeded(CFR, Mapping, *FileRange)) + return Err; + } + + std::tie(OutOfLineMappingBuf, CFR) = std::tie(NextMappingBuf, NextCFR); } - return Buf; + return Error::success(); } }; @@ -543,29 +710,34 @@ Expected<std::unique_ptr<CovMapFuncRecordReader>> CovMapFuncRecordReader::get( CovMapVersion::Version1, IntPtrT, Endian>>(P, R, F); case CovMapVersion::Version2: case CovMapVersion::Version3: + case CovMapVersion::Version4: // 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 + else if (Version == CovMapVersion::Version3) return std::make_unique<VersionedCovMapFuncRecordReader< CovMapVersion::Version3, IntPtrT, Endian>>(P, R, F); + else if (Version == CovMapVersion::Version4) + return std::make_unique<VersionedCovMapFuncRecordReader< + CovMapVersion::Version4, IntPtrT, Endian>>(P, R, F); } llvm_unreachable("Unsupported version"); } template <typename T, support::endianness Endian> static Error readCoverageMappingData( - InstrProfSymtab &ProfileNames, StringRef Data, + InstrProfSymtab &ProfileNames, StringRef CovMap, StringRef FuncRecords, std::vector<BinaryCoverageReader::ProfileMappingRecord> &Records, - std::vector<StringRef> &Filenames) { + std::vector<StringRef> &Filenames, + BinaryCoverageReader::DecompressedData &Decompressed) { using namespace coverage; // Read the records in the coverage data section. auto CovHeader = - reinterpret_cast<const CovMapHeader *>(Data.data()); + reinterpret_cast<const CovMapHeader *>(CovMap.data()); CovMapVersion Version = (CovMapVersion)CovHeader->getVersion<Endian>(); if (Version > CovMapVersion::CurrentVersion) return make_error<CoverageMapError>(coveragemap_error::unsupported_version); @@ -575,12 +747,28 @@ static Error readCoverageMappingData( 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()) + const char *CovBuf = CovMap.data(); + const char *CovBufEnd = CovBuf + CovMap.size(); + const char *FuncRecBuf = FuncRecords.data(); + const char *FuncRecBufEnd = FuncRecords.data() + FuncRecords.size(); + while (CovBuf < CovBufEnd) { + // Read the current coverage header & filename data. + // + // Prior to Version4, this also reads all function records affixed to the + // header. + // + // Return a pointer to the next coverage header. + auto NextOrErr = + Reader->readCoverageHeader(CovBuf, CovBufEnd, Decompressed); + if (auto E = NextOrErr.takeError()) return E; - Buf = NextHeaderOrErr.get(); + CovBuf = NextOrErr.get(); } + // In Version4, function records are not affixed to coverage headers. Read + // the records from their dedicated section. + if (Version == CovMapVersion::Version4) + return Reader->readFunctionRecords(FuncRecBuf, FuncRecBufEnd, None, nullptr, + nullptr); return Error::success(); } @@ -588,31 +776,35 @@ 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()); + StringRef Coverage, std::string &&FuncRecords, InstrProfSymtab &&ProfileNames, + uint8_t BytesInAddress, support::endianness Endian) { + std::unique_ptr<BinaryCoverageReader> Reader( + new BinaryCoverageReader(std::move(FuncRecords))); Reader->ProfileNames = std::move(ProfileNames); + StringRef FuncRecordsRef = Reader->FuncRecords; if (BytesInAddress == 4 && Endian == support::endianness::little) { if (Error E = readCoverageMappingData<uint32_t, support::endianness::little>( - Reader->ProfileNames, Coverage, Reader->MappingRecords, - Reader->Filenames)) + Reader->ProfileNames, Coverage, FuncRecordsRef, + Reader->MappingRecords, Reader->Filenames, + Reader->Decompressed)) 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)) + Reader->ProfileNames, Coverage, FuncRecordsRef, + Reader->MappingRecords, Reader->Filenames, Reader->Decompressed)) 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)) + Reader->ProfileNames, Coverage, FuncRecordsRef, + Reader->MappingRecords, Reader->Filenames, + Reader->Decompressed)) 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)) + Reader->ProfileNames, Coverage, FuncRecordsRef, + Reader->MappingRecords, Reader->Filenames, Reader->Decompressed)) return std::move(E); } else return make_error<CoverageMapError>(coveragemap_error::malformed); @@ -653,10 +845,13 @@ loadTestingFormat(StringRef Data) { return make_error<CoverageMapError>(coveragemap_error::malformed); CoverageMapping = CoverageMapping.substr(Pad); return BinaryCoverageReader::createCoverageReaderFromBuffer( - CoverageMapping, std::move(ProfileNames), BytesInAddress, Endian); + CoverageMapping, "", std::move(ProfileNames), BytesInAddress, Endian); } -static Expected<SectionRef> lookupSection(ObjectFile &OF, StringRef Name) { +/// Find all sections that match \p Name. There may be more than one if comdats +/// are in use, e.g. for the __llvm_covfun section on ELF. +static Expected<std::vector<SectionRef>> lookupSections(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. @@ -666,14 +861,17 @@ static Expected<SectionRef> lookupSection(ObjectFile &OF, StringRef Name) { }; Name = stripSuffix(Name); + std::vector<SectionRef> Sections; for (const auto &Section : OF.sections()) { Expected<StringRef> NameOrErr = Section.getName(); if (!NameOrErr) return NameOrErr.takeError(); if (stripSuffix(*NameOrErr) == Name) - return Section; + Sections.push_back(Section); } - return make_error<CoverageMapError>(coveragemap_error::no_data_found); + if (Sections.empty()) + return make_error<CoverageMapError>(coveragemap_error::no_data_found); + return Sections; } static Expected<std::unique_ptr<BinaryCoverageReader>> @@ -705,28 +903,51 @@ loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch) { // Look for the sections that we are interested in. auto ObjFormat = OF->getTripleObjectFormat(); auto NamesSection = - lookupSection(*OF, getInstrProfSectionName(IPSK_name, ObjFormat, + lookupSections(*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)); + lookupSections(*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(); + std::vector<SectionRef> CoverageSectionRefs = *CoverageSection; + if (CoverageSectionRefs.size() != 1) + return make_error<CoverageMapError>(coveragemap_error::malformed); + auto CoverageMappingOrErr = CoverageSectionRefs.back().getContents(); if (!CoverageMappingOrErr) return CoverageMappingOrErr.takeError(); + StringRef CoverageMapping = CoverageMappingOrErr.get(); InstrProfSymtab ProfileNames; - if (Error E = ProfileNames.create(*NamesSection)) + std::vector<SectionRef> NamesSectionRefs = *NamesSection; + if (NamesSectionRefs.size() != 1) + return make_error<CoverageMapError>(coveragemap_error::malformed); + if (Error E = ProfileNames.create(NamesSectionRefs.back())) return std::move(E); + // Look for the coverage records section (Version4 only). + std::string FuncRecords; + auto CoverageRecordsSections = + lookupSections(*OF, getInstrProfSectionName(IPSK_covfun, ObjFormat, + /*AddSegmentInfo=*/false)); + if (auto E = CoverageRecordsSections.takeError()) + consumeError(std::move(E)); + else { + for (SectionRef Section : *CoverageRecordsSections) { + auto CoverageRecordsOrErr = Section.getContents(); + if (!CoverageRecordsOrErr) + return CoverageRecordsOrErr.takeError(); + FuncRecords += CoverageRecordsOrErr.get(); + while (FuncRecords.size() % 8 != 0) + FuncRecords += '\0'; + } + } + return BinaryCoverageReader::createCoverageReaderFromBuffer( - CoverageMappingOrErr.get(), std::move(ProfileNames), BytesInAddress, - Endian); + CoverageMapping, std::move(FuncRecords), std::move(ProfileNames), + BytesInAddress, Endian); } Expected<std::vector<std::unique_ptr<BinaryCoverageReader>>> diff --git a/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp index d75854a60d1e..8d3c429c4484 100644 --- a/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp @@ -11,9 +11,11 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ProfileData/InstrProf.h" #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -34,12 +36,34 @@ CoverageFilenamesSectionWriter::CoverageFilenamesSectionWriter( #endif } -void CoverageFilenamesSectionWriter::write(raw_ostream &OS) { - encodeULEB128(Filenames.size(), OS); - for (const auto &Filename : Filenames) { - encodeULEB128(Filename.size(), OS); - OS << Filename; +void CoverageFilenamesSectionWriter::write(raw_ostream &OS, bool Compress) { + std::string FilenamesStr; + { + raw_string_ostream FilenamesOS{FilenamesStr}; + for (const auto &Filename : Filenames) { + encodeULEB128(Filename.size(), FilenamesOS); + FilenamesOS << Filename; + } + } + + SmallString<128> CompressedStr; + bool doCompression = + Compress && zlib::isAvailable() && DoInstrProfNameCompression; + if (doCompression) { + auto E = + zlib::compress(FilenamesStr, CompressedStr, zlib::BestSizeCompression); + if (E) + report_bad_alloc_error("Failed to zlib compress coverage data"); } + + // ::= <num-filenames> + // <uncompressed-len> + // <compressed-len-or-zero> + // (<compressed-filenames> | <uncompressed-filenames>) + encodeULEB128(Filenames.size(), OS); + encodeULEB128(FilenamesStr.size(), OS); + encodeULEB128(doCompression ? CompressedStr.size() : 0U, OS); + OS << (doCompression ? StringRef(CompressedStr) : StringRef(FilenamesStr)); } namespace { diff --git a/llvm/lib/ProfileData/GCOV.cpp b/llvm/lib/ProfileData/GCOV.cpp index 228c1b3b442a..71ea44a1a722 100644 --- a/llvm/lib/ProfileData/GCOV.cpp +++ b/llvm/lib/ProfileData/GCOV.cpp @@ -25,25 +25,118 @@ using namespace llvm; +enum : uint32_t { + GCOV_ARC_ON_TREE = 1 << 0, + GCOV_ARC_FALLTHROUGH = 1 << 2, + + GCOV_TAG_FUNCTION = 0x01000000, + GCOV_TAG_BLOCKS = 0x01410000, + GCOV_TAG_ARCS = 0x01430000, + GCOV_TAG_LINES = 0x01450000, + GCOV_TAG_COUNTER_ARCS = 0x01a10000, + // GCOV_TAG_OBJECT_SUMMARY superseded GCOV_TAG_PROGRAM_SUMMARY in GCC 9. + GCOV_TAG_OBJECT_SUMMARY = 0xa1000000, + GCOV_TAG_PROGRAM_SUMMARY = 0xa3000000, +}; + //===----------------------------------------------------------------------===// // GCOVFile implementation. /// readGCNO - Read GCNO buffer. -bool GCOVFile::readGCNO(GCOVBuffer &Buffer) { - if (!Buffer.readGCNOFormat()) +bool GCOVFile::readGCNO(GCOVBuffer &buf) { + if (!buf.readGCNOFormat()) return false; - if (!Buffer.readGCOVVersion(Version)) + if (!buf.readGCOVVersion(Version)) return false; - if (!Buffer.readInt(Checksum)) - return false; - while (true) { - if (!Buffer.readFunctionTag()) - break; - auto GFun = std::make_unique<GCOVFunction>(*this); - if (!GFun->readGCNO(Buffer, Version)) + Checksum = buf.getWord(); + if (Version >= GCOV::V900) + cwd = buf.getString(); + if (Version >= GCOV::V800) + buf.getWord(); // hasUnexecutedBlocks + + uint32_t tag, length; + GCOVFunction *fn; + while ((tag = buf.getWord())) { + if (!buf.readInt(length)) return false; - Functions.push_back(std::move(GFun)); + if (tag == GCOV_TAG_FUNCTION) { + Functions.push_back(std::make_unique<GCOVFunction>(*this)); + fn = Functions.back().get(); + fn->ident = buf.getWord(); + fn->linenoChecksum = buf.getWord(); + if (Version >= GCOV::V407) + fn->cfgChecksum = buf.getWord(); + buf.readString(fn->Name); + StringRef filename; + if (Version < GCOV::V800) { + filename = buf.getString(); + fn->startLine = buf.getWord(); + } else { + fn->artificial = buf.getWord(); + filename = buf.getString(); + fn->startLine = buf.getWord(); + fn->startColumn = buf.getWord(); + fn->endLine = buf.getWord(); + if (Version >= GCOV::V900) + fn->endColumn = buf.getWord(); + } + auto r = filenameToIdx.try_emplace(filename, filenameToIdx.size()); + if (r.second) + filenames.emplace_back(filename); + fn->srcIdx = r.first->second; + IdentToFunction[fn->ident] = fn; + } else if (tag == GCOV_TAG_BLOCKS && fn) { + if (Version < GCOV::V800) { + for (uint32_t i = 0; i != length; ++i) { + buf.getWord(); // Ignored block flags + fn->Blocks.push_back(std::make_unique<GCOVBlock>(*fn, i)); + } + } else { + uint32_t num = buf.getWord(); + for (uint32_t i = 0; i != num; ++i) + fn->Blocks.push_back(std::make_unique<GCOVBlock>(*fn, i)); + } + } else if (tag == GCOV_TAG_ARCS && fn) { + uint32_t srcNo = buf.getWord(); + if (srcNo >= fn->Blocks.size()) { + errs() << "unexpected block number: " << srcNo << " (in " + << fn->Blocks.size() << ")\n"; + return false; + } + GCOVBlock *src = fn->Blocks[srcNo].get(); + for (uint32_t i = 0, e = (length - 1) / 2; i != e; ++i) { + uint32_t dstNo = buf.getWord(), flags = buf.getWord(); + GCOVBlock *dst = fn->Blocks[dstNo].get(); + auto arc = + std::make_unique<GCOVArc>(*src, *dst, flags & GCOV_ARC_FALLTHROUGH); + src->addDstEdge(arc.get()); + dst->addSrcEdge(arc.get()); + if (flags & GCOV_ARC_ON_TREE) + fn->treeArcs.push_back(std::move(arc)); + else + fn->arcs.push_back(std::move(arc)); + } + } else if (tag == GCOV_TAG_LINES && fn) { + uint32_t srcNo = buf.getWord(); + if (srcNo >= fn->Blocks.size()) { + errs() << "unexpected block number: " << srcNo << " (in " + << fn->Blocks.size() << ")\n"; + return false; + } + GCOVBlock &Block = *fn->Blocks[srcNo]; + for (;;) { + uint32_t line = buf.getWord(); + if (line) + Block.addLine(line); + else { + StringRef filename = buf.getString(); + if (filename.empty()) + break; + // TODO Unhandled + } + } + } } GCNOInitialized = true; @@ -52,12 +145,12 @@ bool GCOVFile::readGCNO(GCOVBuffer &Buffer) { /// readGCDA - Read GCDA buffer. It is required that readGCDA() can only be /// called after readGCNO(). -bool GCOVFile::readGCDA(GCOVBuffer &Buffer) { +bool GCOVFile::readGCDA(GCOVBuffer &buf) { assert(GCNOInitialized && "readGCDA() can only be called after readGCNO()"); - if (!Buffer.readGCDAFormat()) + if (!buf.readGCDAFormat()) return false; GCOV::GCOVVersion GCDAVersion; - if (!Buffer.readGCOVVersion(GCDAVersion)) + if (!buf.readGCOVVersion(GCDAVersion)) return false; if (Version != GCDAVersion) { errs() << "GCOV versions do not match.\n"; @@ -65,48 +158,87 @@ bool GCOVFile::readGCDA(GCOVBuffer &Buffer) { } uint32_t GCDAChecksum; - if (!Buffer.readInt(GCDAChecksum)) + if (!buf.readInt(GCDAChecksum)) return false; if (Checksum != GCDAChecksum) { errs() << "File checksums do not match: " << Checksum << " != " << GCDAChecksum << ".\n"; return false; } - for (size_t i = 0, e = Functions.size(); i < e; ++i) { - if (!Buffer.readFunctionTag()) { - errs() << "Unexpected number of functions.\n"; + uint32_t dummy, tag, length; + uint32_t ident; + GCOVFunction *fn = nullptr; + while ((tag = buf.getWord())) { + if (!buf.readInt(length)) return false; + uint32_t pos = buf.cursor.tell(); + if (tag == GCOV_TAG_OBJECT_SUMMARY) { + buf.readInt(RunCount); + buf.readInt(dummy); + // clang<11 uses a fake 4.2 format which sets length to 9. + if (length == 9) + buf.readInt(RunCount); + } else if (tag == GCOV_TAG_PROGRAM_SUMMARY) { + // clang<11 uses a fake 4.2 format which sets length to 0. + if (length > 0) { + buf.readInt(dummy); + buf.readInt(dummy); + buf.readInt(RunCount); + } + ++ProgramCount; + } else if (tag == GCOV_TAG_FUNCTION) { + if (length == 0) // Placeholder + continue; + // As of GCC 10, GCOV_TAG_FUNCTION_LENGTH has never been larger than 3. + // However, clang<11 uses a fake 4.2 format which may set length larger + // than 3. + if (length < 2 || !buf.readInt(ident)) + return false; + auto It = IdentToFunction.find(ident); + uint32_t linenoChecksum, cfgChecksum = 0; + buf.readInt(linenoChecksum); + if (Version >= GCOV::V407) + buf.readInt(cfgChecksum); + if (It != IdentToFunction.end()) { + fn = It->second; + if (linenoChecksum != fn->linenoChecksum || + cfgChecksum != fn->cfgChecksum) { + errs() << fn->Name + << format(": checksum mismatch, (%u, %u) != (%u, %u)\n", + linenoChecksum, cfgChecksum, fn->linenoChecksum, + fn->cfgChecksum); + return false; + } + } + } else if (tag == GCOV_TAG_COUNTER_ARCS && fn) { + if (length != 2 * fn->arcs.size()) { + errs() << fn->Name + << format( + ": GCOV_TAG_COUNTER_ARCS mismatch, got %u, expected %u\n", + length, unsigned(2 * fn->arcs.size())); + return false; + } + for (std::unique_ptr<GCOVArc> &arc : fn->arcs) { + if (!buf.readInt64(arc->Count)) + return false; + // FIXME Fix counters + arc->src.Counter += arc->Count; + if (arc->dst.succ.empty()) + arc->dst.Counter += arc->Count; + } } - if (!Functions[i]->readGCDA(Buffer, Version)) - return false; - } - if (Buffer.readObjectTag()) { - uint32_t Length; - uint32_t Dummy; - if (!Buffer.readInt(Length)) - return false; - if (!Buffer.readInt(Dummy)) - return false; // checksum - if (!Buffer.readInt(Dummy)) - return false; // num - if (!Buffer.readInt(RunCount)) - return false; - Buffer.advanceCursor(Length - 3); - } - while (Buffer.readProgramTag()) { - uint32_t Length; - if (!Buffer.readInt(Length)) + pos += 4 * length; + if (pos < buf.cursor.tell()) return false; - Buffer.advanceCursor(Length); - ++ProgramCount; + buf.de.skip(buf.cursor, pos - buf.cursor.tell()); } return true; } void GCOVFile::print(raw_ostream &OS) const { - for (const auto &FPtr : Functions) - FPtr->print(OS); + for (const GCOVFunction &f : *this) + f.print(OS); } #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) @@ -116,225 +248,22 @@ LLVM_DUMP_METHOD void GCOVFile::dump() const { print(dbgs()); } /// collectLineCounts - Collect line counts. This must be used after /// reading .gcno and .gcda files. -void GCOVFile::collectLineCounts(FileInfo &FI) { - for (const auto &FPtr : Functions) - FPtr->collectLineCounts(FI); - FI.setRunCount(RunCount); - FI.setProgramCount(ProgramCount); +void GCOVFile::collectLineCounts(FileInfo &fi) { + assert(fi.sources.empty()); + for (StringRef filename : filenames) + fi.sources.emplace_back(filename); + for (GCOVFunction &f : *this) { + f.collectLineCounts(fi); + fi.sources[f.srcIdx].functions.push_back(&f); + } + fi.setRunCount(RunCount); + fi.setProgramCount(ProgramCount); } //===----------------------------------------------------------------------===// // GCOVFunction implementation. -/// readGCNO - Read a function from the GCNO buffer. Return false if an error -/// occurs. -bool GCOVFunction::readGCNO(GCOVBuffer &Buff, GCOV::GCOVVersion Version) { - uint32_t Dummy; - if (!Buff.readInt(Dummy)) - return false; // Function header length - if (!Buff.readInt(Ident)) - return false; - if (!Buff.readInt(Checksum)) - return false; - if (Version != GCOV::V402) { - uint32_t CfgChecksum; - if (!Buff.readInt(CfgChecksum)) - return false; - if (Parent.getChecksum() != CfgChecksum) { - errs() << "File checksums do not match: " << Parent.getChecksum() - << " != " << CfgChecksum << " in (" << Name << ").\n"; - return false; - } - } - if (!Buff.readString(Name)) - return false; - if (!Buff.readString(Filename)) - return false; - if (!Buff.readInt(LineNumber)) - return false; - - // read blocks. - if (!Buff.readBlockTag()) { - errs() << "Block tag not found.\n"; - return false; - } - uint32_t BlockCount; - if (!Buff.readInt(BlockCount)) - return false; - for (uint32_t i = 0, e = BlockCount; i != e; ++i) { - if (!Buff.readInt(Dummy)) - return false; // Block flags; - Blocks.push_back(std::make_unique<GCOVBlock>(*this, i)); - } - - // read edges. - while (Buff.readEdgeTag()) { - uint32_t EdgeCount; - if (!Buff.readInt(EdgeCount)) - return false; - EdgeCount = (EdgeCount - 1) / 2; - uint32_t BlockNo; - if (!Buff.readInt(BlockNo)) - return false; - if (BlockNo >= BlockCount) { - errs() << "Unexpected block number: " << BlockNo << " (in " << Name - << ").\n"; - return false; - } - for (uint32_t i = 0, e = EdgeCount; i != e; ++i) { - uint32_t Dst; - if (!Buff.readInt(Dst)) - return false; - Edges.push_back(std::make_unique<GCOVEdge>(*Blocks[BlockNo], *Blocks[Dst])); - GCOVEdge *Edge = Edges.back().get(); - Blocks[BlockNo]->addDstEdge(Edge); - Blocks[Dst]->addSrcEdge(Edge); - if (!Buff.readInt(Dummy)) - return false; // Edge flag - } - } - - // read line table. - while (Buff.readLineTag()) { - uint32_t LineTableLength; - // Read the length of this line table. - if (!Buff.readInt(LineTableLength)) - return false; - uint32_t EndPos = Buff.getCursor() + LineTableLength * 4; - uint32_t BlockNo; - // Read the block number this table is associated with. - if (!Buff.readInt(BlockNo)) - return false; - if (BlockNo >= BlockCount) { - errs() << "Unexpected block number: " << BlockNo << " (in " << Name - << ").\n"; - return false; - } - GCOVBlock &Block = *Blocks[BlockNo]; - // Read the word that pads the beginning of the line table. This may be a - // flag of some sort, but seems to always be zero. - if (!Buff.readInt(Dummy)) - return false; - - // Line information starts here and continues up until the last word. - if (Buff.getCursor() != (EndPos - sizeof(uint32_t))) { - StringRef F; - // Read the source file name. - if (!Buff.readString(F)) - return false; - if (Filename != F) { - errs() << "Multiple sources for a single basic block: " << Filename - << " != " << F << " (in " << Name << ").\n"; - return false; - } - // Read lines up to, but not including, the null terminator. - while (Buff.getCursor() < (EndPos - 2 * sizeof(uint32_t))) { - uint32_t Line; - if (!Buff.readInt(Line)) - return false; - // Line 0 means this instruction was injected by the compiler. Skip it. - if (!Line) - continue; - Block.addLine(Line); - } - // Read the null terminator. - if (!Buff.readInt(Dummy)) - return false; - } - // The last word is either a flag or padding, it isn't clear which. Skip - // over it. - if (!Buff.readInt(Dummy)) - return false; - } - return true; -} - -/// readGCDA - Read a function from the GCDA buffer. Return false if an error -/// occurs. -bool GCOVFunction::readGCDA(GCOVBuffer &Buff, GCOV::GCOVVersion Version) { - uint32_t HeaderLength; - if (!Buff.readInt(HeaderLength)) - return false; // Function header length - - uint64_t EndPos = Buff.getCursor() + HeaderLength * sizeof(uint32_t); - - uint32_t GCDAIdent; - if (!Buff.readInt(GCDAIdent)) - return false; - if (Ident != GCDAIdent) { - errs() << "Function identifiers do not match: " << Ident - << " != " << GCDAIdent << " (in " << Name << ").\n"; - return false; - } - - uint32_t GCDAChecksum; - if (!Buff.readInt(GCDAChecksum)) - return false; - if (Checksum != GCDAChecksum) { - errs() << "Function checksums do not match: " << Checksum - << " != " << GCDAChecksum << " (in " << Name << ").\n"; - return false; - } - - uint32_t CfgChecksum; - if (Version != GCOV::V402) { - if (!Buff.readInt(CfgChecksum)) - return false; - if (Parent.getChecksum() != CfgChecksum) { - errs() << "File checksums do not match: " << Parent.getChecksum() - << " != " << CfgChecksum << " (in " << Name << ").\n"; - return false; - } - } - - if (Buff.getCursor() < EndPos) { - StringRef GCDAName; - if (!Buff.readString(GCDAName)) - return false; - if (Name != GCDAName) { - errs() << "Function names do not match: " << Name << " != " << GCDAName - << ".\n"; - return false; - } - } - - if (!Buff.readArcTag()) { - errs() << "Arc tag not found (in " << Name << ").\n"; - return false; - } - - uint32_t Count; - if (!Buff.readInt(Count)) - return false; - Count /= 2; - - // This for loop adds the counts for each block. A second nested loop is - // required to combine the edge counts that are contained in the GCDA file. - for (uint32_t BlockNo = 0; Count > 0; ++BlockNo) { - // The last block is always reserved for exit block - if (BlockNo >= Blocks.size()) { - errs() << "Unexpected number of edges (in " << Name << ").\n"; - return false; - } - if (BlockNo == Blocks.size() - 1) - errs() << "(" << Name << ") has arcs from exit block.\n"; - GCOVBlock &Block = *Blocks[BlockNo]; - for (size_t EdgeNo = 0, End = Block.getNumDstEdges(); EdgeNo < End; - ++EdgeNo) { - if (Count == 0) { - errs() << "Unexpected number of edges (in " << Name << ").\n"; - return false; - } - uint64_t ArcCount; - if (!Buff.readInt64(ArcCount)) - return false; - Block.addCount(EdgeNo, ArcCount); - --Count; - } - Block.sortDstEdges(); - } - return true; -} +StringRef GCOVFunction::getFilename() const { return file.filenames[srcIdx]; } /// getEntryCount - Get the number of times the function was called by /// retrieving the entry block's count. @@ -349,8 +278,8 @@ uint64_t GCOVFunction::getExitCount() const { } void GCOVFunction::print(raw_ostream &OS) const { - OS << "===== " << Name << " (" << Ident << ") @ " << Filename << ":" - << LineNumber << "\n"; + OS << "===== " << Name << " (" << ident << ") @ " << getFilename() << ":" + << startLine << "\n"; for (const auto &Block : Blocks) Block->print(OS); } @@ -365,43 +294,17 @@ LLVM_DUMP_METHOD void GCOVFunction::dump() const { print(dbgs()); } void GCOVFunction::collectLineCounts(FileInfo &FI) { // If the line number is zero, this is a function that doesn't actually appear // in the source file, so there isn't anything we can do with it. - if (LineNumber == 0) + if (startLine == 0) return; for (const auto &Block : Blocks) Block->collectLineCounts(FI); - FI.addFunctionLine(Filename, LineNumber, this); + FI.addFunctionLine(getFilename(), startLine, this); } //===----------------------------------------------------------------------===// // GCOVBlock implementation. -/// ~GCOVBlock - Delete GCOVBlock and its content. -GCOVBlock::~GCOVBlock() { - SrcEdges.clear(); - DstEdges.clear(); - Lines.clear(); -} - -/// addCount - Add to block counter while storing the edge count. If the -/// destination has no outgoing edges, also update that block's count too. -void GCOVBlock::addCount(size_t DstEdgeNo, uint64_t N) { - assert(DstEdgeNo < DstEdges.size()); // up to caller to ensure EdgeNo is valid - DstEdges[DstEdgeNo]->Count = N; - Counter += N; - if (!DstEdges[DstEdgeNo]->Dst.getNumDstEdges()) - DstEdges[DstEdgeNo]->Dst.Counter += N; -} - -/// sortDstEdges - Sort destination edges by block number, nop if already -/// sorted. This is required for printing branch info in the correct order. -void GCOVBlock::sortDstEdges() { - if (!DstEdgesAreSorted) - llvm::stable_sort(DstEdges, [](const GCOVEdge *E1, const GCOVEdge *E2) { - return E1->Dst.Number < E2->Dst.Number; - }); -} - /// collectLineCounts - Collect line counts. This must be used after /// reading .gcno and .gcda files. void GCOVBlock::collectLineCounts(FileInfo &FI) { @@ -411,16 +314,16 @@ void GCOVBlock::collectLineCounts(FileInfo &FI) { void GCOVBlock::print(raw_ostream &OS) const { OS << "Block : " << Number << " Counter : " << Counter << "\n"; - if (!SrcEdges.empty()) { + if (!pred.empty()) { OS << "\tSource Edges : "; - for (const GCOVEdge *Edge : SrcEdges) - OS << Edge->Src.Number << " (" << Edge->Count << "), "; + for (const GCOVArc *Edge : pred) + OS << Edge->src.Number << " (" << Edge->Count << "), "; OS << "\n"; } - if (!DstEdges.empty()) { + if (!succ.empty()) { OS << "\tDestination Edges : "; - for (const GCOVEdge *Edge : DstEdges) - OS << Edge->Dst.Number << " (" << Edge->Count << "), "; + for (const GCOVArc *Edge : succ) + OS << Edge->dst.Number << " (" << Edge->Count << "), "; OS << "\n"; } if (!Lines.empty()) { @@ -482,7 +385,7 @@ bool GCOVBlock::lookForCircuit(const GCOVBlock *V, const GCOVBlock *Start, bool FoundCircuit = false; for (auto E : V->dsts()) { - const GCOVBlock *W = &E->Dst; + const GCOVBlock *W = &E->dst; if (W < Start || find(Blocks, W) == Blocks.end()) { continue; } @@ -506,7 +409,7 @@ bool GCOVBlock::lookForCircuit(const GCOVBlock *V, const GCOVBlock *Start, GCOVBlock::unblock(V, Blocked, BlockLists); } else { for (auto E : V->dsts()) { - const GCOVBlock *W = &E->Dst; + const GCOVBlock *W = &E->dst; if (W < Start || find(Blocks, W) == Blocks.end()) { continue; } @@ -545,7 +448,7 @@ uint64_t GCOVBlock::getLineCount(const BlockVector &Blocks) { } else { // Add counts from predecessors that are not on the same line. for (auto E : Block->srcs()) { - const GCOVBlock *W = &E->Src; + const GCOVBlock *W = &E->src; if (find(Blocks, W) == Blocks.end()) { Count += E->Count; } @@ -617,6 +520,7 @@ class LineConsumer { StringRef Remaining; public: + LineConsumer() = default; LineConsumer(StringRef Filename) { ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = MemoryBuffer::getFileOrSTDIN(Filename); @@ -672,7 +576,7 @@ static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths) { if (S < I) Result.append(S, I); - return Result.str(); + return std::string(Result.str()); } std::string FileInfo::getCoveragePath(StringRef Filename, @@ -681,7 +585,7 @@ std::string FileInfo::getCoveragePath(StringRef Filename, // This is probably a bug in gcov, but when -n is specified, paths aren't // mangled at all, and the -l and -p options are ignored. Here, we do the // same. - return Filename; + return std::string(Filename); std::string CoveragePath; if (Options.LongFileNames && !Filename.equals(MainFilename)) @@ -693,7 +597,7 @@ std::string FileInfo::getCoveragePath(StringRef Filename, MD5::MD5Result Result; Hasher.update(Filename.str()); Hasher.final(Result); - CoveragePath += "##" + Result.digest().str().str(); + CoveragePath += "##" + std::string(Result.digest()); } CoveragePath += ".gcov"; return CoveragePath; @@ -701,9 +605,6 @@ std::string FileInfo::getCoveragePath(StringRef Filename, std::unique_ptr<raw_ostream> FileInfo::openCoveragePath(StringRef CoveragePath) { - if (Options.NoOutput) - return std::make_unique<raw_null_ostream>(); - std::error_code EC; auto OS = std::make_unique<raw_fd_ostream>(CoveragePath, EC, sys::fs::OF_Text); @@ -716,24 +617,30 @@ FileInfo::openCoveragePath(StringRef CoveragePath) { /// print - Print source files with collected line count information. void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename, - StringRef GCNOFile, StringRef GCDAFile) { + StringRef GCNOFile, StringRef GCDAFile, GCOVFile &file) { SmallVector<StringRef, 4> Filenames; for (const auto &LI : LineInfo) Filenames.push_back(LI.first()); llvm::sort(Filenames); for (StringRef Filename : Filenames) { - auto AllLines = LineConsumer(Filename); - + auto AllLines = + Options.Intermediate ? LineConsumer() : LineConsumer(Filename); std::string CoveragePath = getCoveragePath(Filename, MainFilename); - std::unique_ptr<raw_ostream> CovStream = openCoveragePath(CoveragePath); - raw_ostream &CovOS = *CovStream; + std::unique_ptr<raw_ostream> CovStream; + if (Options.NoOutput || Options.Intermediate) + CovStream = std::make_unique<raw_null_ostream>(); + else if (!Options.UseStdout) + CovStream = openCoveragePath(CoveragePath); + raw_ostream &CovOS = + !Options.NoOutput && Options.UseStdout ? llvm::outs() : *CovStream; CovOS << " -: 0:Source:" << Filename << "\n"; CovOS << " -: 0:Graph:" << GCNOFile << "\n"; CovOS << " -: 0:Data:" << GCDAFile << "\n"; CovOS << " -: 0:Runs:" << RunCount << "\n"; - CovOS << " -: 0:Programs:" << ProgramCount << "\n"; + if (file.getVersion() < GCOV::V900) + CovOS << " -: 0:Programs:" << ProgramCount << "\n"; const LineData &Line = LineInfo[Filename]; GCOVCoverage FileCoverage(Filename); @@ -815,19 +722,67 @@ void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename, if (NumEdges > 1) printBranchInfo(CovOS, *Block, FileCoverage, EdgeNo); else if (Options.UncondBranch && NumEdges == 1) - printUncondBranchInfo(CovOS, EdgeNo, - (*Block->dst_begin())->Count); + printUncondBranchInfo(CovOS, EdgeNo, Block->succ[0]->Count); + } + } + } + } + SourceInfo &source = sources[file.filenameToIdx.find(Filename)->second]; + source.name = CoveragePath; + source.coverage = FileCoverage; + } + + if (Options.Intermediate && !Options.NoOutput) { + // gcov 7.* unexpectedly create multiple .gcov files, which was fixed in 8.0 + // (PR GCC/82702). We create just one file. + std::string outputPath(sys::path::filename(MainFilename)); + std::error_code ec; + raw_fd_ostream os(outputPath + ".gcov", ec, sys::fs::OF_Text); + if (ec) { + errs() << ec.message() << "\n"; + return; + } + + for (const SourceInfo &source : sources) { + os << "file:" << source.filename << '\n'; + for (const GCOVFunction *f : source.functions) + os << "function:" << f->startLine << ',' << f->getEntryCount() << ',' + << f->Name << '\n'; + const LineData &line = LineInfo[source.filename]; + for (uint32_t lineNum = 0; lineNum != line.LastLine; ++lineNum) { + BlockLines::const_iterator BlocksIt = line.Blocks.find(lineNum); + if (BlocksIt == line.Blocks.end()) + continue; + const BlockVector &blocks = BlocksIt->second; + // GCC 8 (r254259) added third third field for Ada: + // lcount:<line>,<count>,<has_unexecuted_blocks> + // We don't need the third field. + os << "lcount:" << (lineNum + 1) << ',' + << GCOVBlock::getLineCount(blocks) << '\n'; + + if (!Options.BranchInfo) + continue; + for (const GCOVBlock *block : blocks) { + if (block->getLastLine() != lineNum + 1 || + block->getNumDstEdges() < 2) + continue; + for (const GCOVArc *arc : block->dsts()) { + const char *type = block->getCount() + ? arc->Count ? "taken" : "nottaken" + : "notexec"; + os << "branch:" << (lineNum + 1) << ',' << type << '\n'; } } } } - FileCoverages.push_back(std::make_pair(CoveragePath, FileCoverage)); } - // FIXME: There is no way to detect calls given current instrumentation. - if (Options.FuncCoverage) - printFuncCoverage(InfoOS); - printFileCoverage(InfoOS); + if (!Options.UseStdout) { + // FIXME: There is no way to detect calls given current instrumentation. + if (Options.FuncCoverage) + printFuncCoverage(InfoOS); + printFileCoverage(InfoOS); + } } /// printFunctionSummary - Print function and block summary. @@ -862,7 +817,7 @@ void FileInfo::printBranchInfo(raw_ostream &OS, const GCOVBlock &Block, GCOVCoverage &Coverage, uint32_t &EdgeNo) { SmallVector<uint64_t, 16> BranchCounts; uint64_t TotalCounts = 0; - for (const GCOVEdge *Edge : Block.dsts()) { + for (const GCOVArc *Edge : Block.dsts()) { BranchCounts.push_back(Edge->Count); TotalCounts += Edge->Count; if (Block.getCount()) @@ -928,13 +883,12 @@ void FileInfo::printFuncCoverage(raw_ostream &OS) const { // printFileCoverage - Print per-file coverage info. void FileInfo::printFileCoverage(raw_ostream &OS) const { - for (const auto &FC : FileCoverages) { - const std::string &Filename = FC.first; - const GCOVCoverage &Coverage = FC.second; + for (const SourceInfo &source : sources) { + const GCOVCoverage &Coverage = source.coverage; OS << "File '" << Coverage.Name << "'\n"; printCoverage(OS, Coverage); - if (!Options.NoOutput) - OS << Coverage.Name << ":creating '" << Filename << "'\n"; + if (!Options.NoOutput && !Options.Intermediate) + OS << "Creating '" << source.name << "'\n"; OS << "\n"; } } diff --git a/llvm/lib/ProfileData/InstrProf.cpp b/llvm/lib/ProfileData/InstrProf.cpp index 57d4fbc59f83..b9d8ae9ba60d 100644 --- a/llvm/lib/ProfileData/InstrProf.cpp +++ b/llvm/lib/ProfileData/InstrProf.cpp @@ -162,6 +162,10 @@ const char *InstrProfSectNamePrefix[] = { namespace llvm { +cl::opt<bool> DoInstrProfNameCompression( + "enable-name-compression", + cl::desc("Enable name/filename string compression"), cl::init(true)); + std::string getInstrProfSectionName(InstrProfSectKind IPSK, Triple::ObjectFormatType OF, bool AddSegmentInfo) { @@ -286,7 +290,7 @@ StringRef getFuncNameWithoutPrefix(StringRef PGOFuncName, StringRef FileName) { // symbol is created to hold the name. Return the legalized symbol name. std::string getPGOFuncNameVarName(StringRef FuncName, GlobalValue::LinkageTypes Linkage) { - std::string VarName = getInstrProfNameVarPrefix(); + std::string VarName = std::string(getInstrProfNameVarPrefix()); VarName += FuncName; if (!GlobalValue::isLocalLinkage(Linkage)) @@ -427,7 +431,7 @@ Error collectPGOFuncNameStrings(ArrayRef<GlobalVariable *> NameVars, std::string &Result, bool doCompression) { std::vector<std::string> NameStrs; for (auto *NameVar : NameVars) { - NameStrs.push_back(getPGOFuncNameVarInitializer(NameVar)); + NameStrs.push_back(std::string(getPGOFuncNameVarInitializer(NameVar))); } return collectPGOFuncNameStrings( NameStrs, zlib::isAvailable() && doCompression, Result); diff --git a/llvm/lib/ProfileData/InstrProfReader.cpp b/llvm/lib/ProfileData/InstrProfReader.cpp index b904f983dceb..16a69cb5457b 100644 --- a/llvm/lib/ProfileData/InstrProfReader.cpp +++ b/llvm/lib/ProfileData/InstrProfReader.cpp @@ -15,6 +15,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/IR/ProfileSummary.h" #include "llvm/ProfileData/InstrProf.h" @@ -144,7 +145,7 @@ bool TextInstrProfReader::hasFormat(const MemoryBuffer &Buffer) { StringRef buffer = Buffer.getBufferStart(); return count == 0 || std::all_of(buffer.begin(), buffer.begin() + count, - [](char c) { return isPrint(c) || ::isspace(c); }); + [](char c) { return isPrint(c) || isSpace(c); }); } // Read the profile variant flag from the header: ":FE" means this is a FE @@ -422,11 +423,11 @@ Error RawInstrProfReader<IntPtrT>::readRawCounts( // Check bounds. Note that the counter pointer embedded in the data record // may itself be corrupt. - if (NumCounters > MaxNumCounters) + if (MaxNumCounters < 0 || NumCounters > (uint32_t)MaxNumCounters) return error(instrprof_error::malformed); ptrdiff_t CounterOffset = getCounterOffset(CounterPtr); if (CounterOffset < 0 || CounterOffset > MaxNumCounters || - (CounterOffset + NumCounters) > MaxNumCounters) + ((uint32_t)CounterOffset + NumCounters) > (uint32_t)MaxNumCounters) return error(instrprof_error::malformed); auto RawCounts = makeArrayRef(getCounter(CounterOffset), NumCounters); diff --git a/llvm/lib/ProfileData/ProfileSummaryBuilder.cpp b/llvm/lib/ProfileData/ProfileSummaryBuilder.cpp index 3299b5f92069..5d3a07640942 100644 --- a/llvm/lib/ProfileData/ProfileSummaryBuilder.cpp +++ b/llvm/lib/ProfileData/ProfileSummaryBuilder.cpp @@ -31,6 +31,19 @@ static const uint32_t DefaultCutoffsData[] = { const ArrayRef<uint32_t> ProfileSummaryBuilder::DefaultCutoffs = DefaultCutoffsData; +const ProfileSummaryEntry & +ProfileSummaryBuilder::getEntryForPercentile(SummaryEntryVector &DS, + uint64_t Percentile) { + auto It = partition_point(DS, [=](const ProfileSummaryEntry &Entry) { + return Entry.Cutoff < Percentile; + }); + // The required percentile has to be <= one of the percentiles in the + // detailed summary. + if (It == DS.end()) + report_fatal_error("Desired percentile exceeds the maximum cutoff"); + return *It; +} + void InstrProfSummaryBuilder::addRecord(const InstrProfRecord &R) { // The first counter is not necessarily an entry count for IR // instrumentation profiles. diff --git a/llvm/lib/ProfileData/SampleProf.cpp b/llvm/lib/ProfileData/SampleProf.cpp index 003e8d4d4296..e5d0fdba5fc4 100644 --- a/llvm/lib/ProfileData/SampleProf.cpp +++ b/llvm/lib/ProfileData/SampleProf.cpp @@ -30,6 +30,7 @@ using namespace sampleprof; namespace llvm { namespace sampleprof { SampleProfileFormat FunctionSamples::Format; +bool FunctionSamples::UseMD5; } // namespace sampleprof } // namespace llvm diff --git a/llvm/lib/ProfileData/SampleProfReader.cpp b/llvm/lib/ProfileData/SampleProfReader.cpp index 001aafce7bfd..03f1ac190b91 100644 --- a/llvm/lib/ProfileData/SampleProfReader.cpp +++ b/llvm/lib/ProfileData/SampleProfReader.cpp @@ -245,7 +245,7 @@ std::error_code SampleProfileReaderText::readImpl() { InlineStack.pop_back(); } FunctionSamples &FSamples = InlineStack.back()->functionSamplesAt( - LineLocation(LineOffset, Discriminator))[FName]; + LineLocation(LineOffset, Discriminator))[std::string(FName)]; FSamples.setName(FName); MergeResult(Result, FSamples.addTotalSamples(NumSamples)); InlineStack.push_back(&FSamples); @@ -430,7 +430,7 @@ SampleProfileReaderBinary::readProfile(FunctionSamples &FProfile) { return EC; FunctionSamples &CalleeProfile = FProfile.functionSamplesAt( - LineLocation(*LineOffset, *Discriminator))[*FName]; + LineLocation(*LineOffset, *Discriminator))[std::string(*FName)]; CalleeProfile.setName(*FName); if (std::error_code EC = readProfile(CalleeProfile)) return EC; @@ -470,18 +470,20 @@ std::error_code SampleProfileReaderBinary::readImpl() { return sampleprof_error::success; } -std::error_code -SampleProfileReaderExtBinary::readOneSection(const uint8_t *Start, - uint64_t Size, SecType Type) { +std::error_code SampleProfileReaderExtBinary::readOneSection( + const uint8_t *Start, uint64_t Size, const SecHdrTableEntry &Entry) { Data = Start; End = Start + Size; - switch (Type) { + switch (Entry.Type) { case SecProfSummary: if (std::error_code EC = readSummary()) return EC; + if (hasSecFlag(Entry, SecProfSummaryFlags::SecFlagPartial)) + Summary->setPartialProfile(true); break; case SecNameTable: - if (std::error_code EC = readNameTable()) + if (std::error_code EC = readNameTableSec( + hasSecFlag(Entry, SecNameTableFlags::SecFlagMD5Name))) return EC; break; case SecLBRProfile: @@ -546,15 +548,28 @@ std::error_code SampleProfileReaderExtBinary::readFuncProfiles() { } } - for (auto NameOffset : FuncOffsetTable) { - auto FuncName = NameOffset.first; - if (!FuncsToUse.count(FuncName) && - (!Remapper || !Remapper->exist(FuncName))) - continue; - const uint8_t *FuncProfileAddr = Start + NameOffset.second; - assert(FuncProfileAddr < End && "out of LBRProfile section"); - if (std::error_code EC = readFuncProfile(FuncProfileAddr)) - return EC; + if (useMD5()) { + for (auto Name : FuncsToUse) { + auto GUID = std::to_string(MD5Hash(Name)); + auto iter = FuncOffsetTable.find(StringRef(GUID)); + if (iter == FuncOffsetTable.end()) + continue; + const uint8_t *FuncProfileAddr = Start + iter->second; + assert(FuncProfileAddr < End && "out of LBRProfile section"); + if (std::error_code EC = readFuncProfile(FuncProfileAddr)) + return EC; + } + } else { + for (auto NameOffset : FuncOffsetTable) { + auto FuncName = NameOffset.first; + if (!FuncsToUse.count(FuncName) && + (!Remapper || !Remapper->exist(FuncName))) + continue; + const uint8_t *FuncProfileAddr = Start + NameOffset.second; + assert(FuncProfileAddr < End && "out of LBRProfile section"); + if (std::error_code EC = readFuncProfile(FuncProfileAddr)) + return EC; + } } Data = End; @@ -617,7 +632,7 @@ std::error_code SampleProfileReaderExtBinaryBase::readImpl() { // DecompressBuf before reading the actual data. The pointee of // 'Data' will be changed to buffer hold by DecompressBuf // temporarily when reading the actual data. - bool isCompressed = hasSecFlag(Entry, SecFlagCompress); + bool isCompressed = hasSecFlag(Entry, SecCommonFlags::SecFlagCompress); if (isCompressed) { const uint8_t *DecompressBuf; uint64_t DecompressBufSize; @@ -628,7 +643,7 @@ std::error_code SampleProfileReaderExtBinaryBase::readImpl() { SecSize = DecompressBufSize; } - if (std::error_code EC = readOneSection(SecStart, SecSize, Entry.Type)) + if (std::error_code EC = readOneSection(SecStart, SecSize, Entry)) return EC; if (Data != SecStart + SecSize) return sampleprof_error::malformed; @@ -705,6 +720,31 @@ std::error_code SampleProfileReaderBinary::readNameTable() { return sampleprof_error::success; } +std::error_code SampleProfileReaderExtBinary::readMD5NameTable() { + auto Size = readNumber<uint64_t>(); + if (std::error_code EC = Size.getError()) + return EC; + NameTable.reserve(*Size); + MD5StringBuf = std::make_unique<std::vector<std::string>>(); + MD5StringBuf->reserve(*Size); + for (uint32_t I = 0; I < *Size; ++I) { + auto FID = readNumber<uint64_t>(); + if (std::error_code EC = FID.getError()) + return EC; + MD5StringBuf->push_back(std::to_string(*FID)); + // NameTable is a vector of StringRef. Here it is pushing back a + // StringRef initialized with the last string in MD5stringBuf. + NameTable.push_back(MD5StringBuf->back()); + } + return sampleprof_error::success; +} + +std::error_code SampleProfileReaderExtBinary::readNameTableSec(bool IsMD5) { + if (IsMD5) + return readMD5NameTable(); + return SampleProfileReaderBinary::readNameTable(); +} + std::error_code SampleProfileReaderCompactBinary::readNameTable() { auto Size = readNumber<uint64_t>(); if (std::error_code EC = Size.getError()) @@ -793,11 +833,40 @@ uint64_t SampleProfileReaderExtBinaryBase::getFileSize() { return FileSize; } +static std::string getSecFlagsStr(const SecHdrTableEntry &Entry) { + std::string Flags; + if (hasSecFlag(Entry, SecCommonFlags::SecFlagCompress)) + Flags.append("{compressed,"); + else + Flags.append("{"); + + switch (Entry.Type) { + case SecNameTable: + if (hasSecFlag(Entry, SecNameTableFlags::SecFlagMD5Name)) + Flags.append("md5,"); + break; + case SecProfSummary: + if (hasSecFlag(Entry, SecProfSummaryFlags::SecFlagPartial)) + Flags.append("partial,"); + break; + default: + break; + } + char &last = Flags.back(); + if (last == ',') + last = '}'; + else + Flags.append("}"); + return Flags; +} + bool SampleProfileReaderExtBinaryBase::dumpSectionInfo(raw_ostream &OS) { uint64_t TotalSecsSize = 0; for (auto &Entry : SecHdrTable) { OS << getSecName(Entry.Type) << " - Offset: " << Entry.Offset - << ", Size: " << Entry.Size << "\n"; + << ", Size: " << Entry.Size << ", Flags: " << getSecFlagsStr(Entry) + << "\n"; + ; TotalSecsSize += getSectionSize(Entry.Type); } uint64_t HeaderSize = SecHdrTable.front().Offset; @@ -1007,7 +1076,7 @@ std::error_code SampleProfileReaderGCC::readHeader() { if (!GcovBuffer.readGCOVVersion(version)) return sampleprof_error::unrecognized_format; - if (version != GCOV::V704) + if (version != GCOV::V407) return sampleprof_error::unsupported_version; // Skip the empty integer. @@ -1043,7 +1112,7 @@ std::error_code SampleProfileReaderGCC::readNameTable() { StringRef Str; if (!GcovBuffer.readString(Str)) return sampleprof_error::truncated; - Names.push_back(Str); + Names.push_back(std::string(Str)); } return sampleprof_error::success; @@ -1107,7 +1176,7 @@ std::error_code SampleProfileReaderGCC::readOneFunctionProfile( uint32_t LineOffset = Offset >> 16; uint32_t Discriminator = Offset & 0xffff; FProfile = &CallerProfile->functionSamplesAt( - LineLocation(LineOffset, Discriminator))[Name]; + LineLocation(LineOffset, Discriminator))[std::string(Name)]; } FProfile->setName(Name); @@ -1210,9 +1279,9 @@ bool SampleProfileReaderGCC::hasFormat(const MemoryBuffer &Buffer) { } void SampleProfileReaderItaniumRemapper::applyRemapping(LLVMContext &Ctx) { - // If the reader is in compact format, we can't remap it because + // If the reader uses MD5 to represent string, we can't remap it because // we don't know what the original function names were. - if (Reader.getFormat() == SPF_Compact_Binary) { + if (Reader.useMD5()) { Ctx.diagnose(DiagnosticInfoSampleProfile( Reader.getBuffer()->getBufferIdentifier(), "Profile data remapping cannot be applied to profile data " diff --git a/llvm/lib/ProfileData/SampleProfWriter.cpp b/llvm/lib/ProfileData/SampleProfWriter.cpp index 8d09af31f94b..48d3faa6cd2f 100644 --- a/llvm/lib/ProfileData/SampleProfWriter.cpp +++ b/llvm/lib/ProfileData/SampleProfWriter.cpp @@ -87,7 +87,7 @@ uint64_t SampleProfileWriterExtBinaryBase::markSectionStart(SecType Type) { uint64_t SectionStart = OutputStream->tell(); auto &Entry = getEntryInLayout(Type); // Use LocalBuf as a temporary output for writting data. - if (hasSecFlag(Entry, SecFlagCompress)) + if (hasSecFlag(Entry, SecCommonFlags::SecFlagCompress)) LocalBufStream.swap(OutputStream); return SectionStart; } @@ -117,7 +117,7 @@ std::error_code SampleProfileWriterExtBinaryBase::addNewSection(SecType Type, uint64_t SectionStart) { auto Entry = getEntryInLayout(Type); - if (hasSecFlag(Entry, SecFlagCompress)) { + if (hasSecFlag(Entry, SecCommonFlags::SecFlagCompress)) { LocalBufStream.swap(OutputStream); if (std::error_code EC = compressAndOutput()) return EC; @@ -166,6 +166,22 @@ std::error_code SampleProfileWriterExtBinary::writeFuncOffsetTable() { return sampleprof_error::success; } +std::error_code SampleProfileWriterExtBinary::writeNameTable() { + if (!UseMD5) + return SampleProfileWriterBinary::writeNameTable(); + + auto &OS = *OutputStream; + std::set<StringRef> V; + stablizeNameTable(V); + + // Write out the name table. + encodeULEB128(NameTable.size(), OS); + for (auto N : V) { + encodeULEB128(MD5Hash(N), OS); + } + return sampleprof_error::success; +} + std::error_code SampleProfileWriterExtBinary::writeSections( const StringMap<FunctionSamples> &ProfileMap) { uint64_t SectionStart = markSectionStart(SecProfSummary); @@ -390,19 +406,11 @@ std::error_code SampleProfileWriterBinary::writeHeader( void SampleProfileWriterExtBinaryBase::setToCompressAllSections() { for (auto &Entry : SectionHdrLayout) - addSecFlags(Entry, SecFlagCompress); + addSecFlag(Entry, SecCommonFlags::SecFlagCompress); } void SampleProfileWriterExtBinaryBase::setToCompressSection(SecType Type) { - addSectionFlags(Type, SecFlagCompress); -} - -void SampleProfileWriterExtBinaryBase::addSectionFlags(SecType Type, - SecFlags Flags) { - for (auto &Entry : SectionHdrLayout) { - if (Entry.Type == Type) - addSecFlags(Entry, Flags); - } + addSectionFlag(Type, SecCommonFlags::SecFlagCompress); } void SampleProfileWriterExtBinaryBase::allocSecHdrTable() { |