aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/ProfileData
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/ProfileData')
-rw-r--r--llvm/lib/ProfileData/Coverage/CoverageMapping.cpp5
-rw-r--r--llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp377
-rw-r--r--llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp34
-rw-r--r--llvm/lib/ProfileData/GCOV.cpp592
-rw-r--r--llvm/lib/ProfileData/InstrProf.cpp8
-rw-r--r--llvm/lib/ProfileData/InstrProfReader.cpp7
-rw-r--r--llvm/lib/ProfileData/ProfileSummaryBuilder.cpp13
-rw-r--r--llvm/lib/ProfileData/SampleProf.cpp1
-rw-r--r--llvm/lib/ProfileData/SampleProfReader.cpp117
-rw-r--r--llvm/lib/ProfileData/SampleProfWriter.cpp32
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() {