diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:01:25 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:01:25 +0000 |
| commit | d8e91e46262bc44006913e6796843909f1ac7bcd (patch) | |
| tree | 7d0c143d9b38190e0fa0180805389da22cd834c5 /lib/ProfileData | |
| parent | b7eb8e35e481a74962664b63dfb09483b200209a (diff) | |
Notes
Diffstat (limited to 'lib/ProfileData')
| -rw-r--r-- | lib/ProfileData/Coverage/CoverageMapping.cpp | 31 | ||||
| -rw-r--r-- | lib/ProfileData/GCOV.cpp | 154 | ||||
| -rw-r--r-- | lib/ProfileData/InstrProf.cpp | 11 | ||||
| -rw-r--r-- | lib/ProfileData/InstrProfReader.cpp | 164 | ||||
| -rw-r--r-- | lib/ProfileData/ProfileSummaryBuilder.cpp | 2 | ||||
| -rw-r--r-- | lib/ProfileData/SampleProf.cpp | 10 | ||||
| -rw-r--r-- | lib/ProfileData/SampleProfReader.cpp | 167 | ||||
| -rw-r--r-- | lib/ProfileData/SampleProfWriter.cpp | 57 |
8 files changed, 532 insertions, 64 deletions
diff --git a/lib/ProfileData/Coverage/CoverageMapping.cpp b/lib/ProfileData/Coverage/CoverageMapping.cpp index b3c2b182e76c..b2dde3406a63 100644 --- a/lib/ProfileData/Coverage/CoverageMapping.cpp +++ b/lib/ProfileData/Coverage/CoverageMapping.cpp @@ -83,7 +83,7 @@ Counter CounterExpressionBuilder::simplify(Counter ExpressionTree) { return Counter::getZero(); // Group the terms by counter ID. - llvm::sort(Terms.begin(), Terms.end(), [](const Term &LHS, const Term &RHS) { + llvm::sort(Terms, [](const Term &LHS, const Term &RHS) { return LHS.CounterID < RHS.CounterID; }); @@ -207,12 +207,6 @@ Error CoverageMapping::loadFunctionRecord( else OrigFuncName = getFuncNameWithoutPrefix(OrigFuncName, Record.Filenames[0]); - // Don't load records for (filenames, function) pairs we've already seen. - auto FilenamesHash = hash_combine_range(Record.Filenames.begin(), - Record.Filenames.end()); - if (!RecordProvenance[FilenamesHash].insert(hash_value(OrigFuncName)).second) - return Error::success(); - CounterMappingContext Ctx(Record.Expressions); std::vector<uint64_t> Counts; @@ -230,6 +224,15 @@ Error CoverageMapping::loadFunctionRecord( assert(!Record.MappingRegions.empty() && "Function has no regions"); + // This coverage record is a zero region for a function that's unused in + // some TU, but used in a different TU. Ignore it. The coverage maps from the + // the other TU will either be loaded (providing full region counts) or they + // won't (in which case we don't unintuitively report functions as uncovered + // when they have non-zero counts in the profile). + if (Record.MappingRegions.size() == 1 && + Record.MappingRegions[0].Count.isZero() && Counts[0] > 0) + return Error::success(); + FunctionRecord Function(OrigFuncName, Record.Filenames); for (const auto &Region : Record.MappingRegions) { Expected<int64_t> ExecutionCount = Ctx.evaluate(Region.Count); @@ -239,11 +242,12 @@ Error CoverageMapping::loadFunctionRecord( } Function.pushRegion(Region, *ExecutionCount); } - if (Function.CountedRegions.size() != Record.MappingRegions.size()) { - FuncCounterMismatches.emplace_back(Record.FunctionName, - Function.CountedRegions.size()); + + // Don't create records for (filenames, function) pairs we've already seen. + auto FilenamesHash = hash_combine_range(Record.Filenames.begin(), + Record.Filenames.end()); + if (!RecordProvenance[FilenamesHash].insert(hash_value(OrigFuncName)).second) return Error::success(); - } Functions.push_back(std::move(Function)); return Error::success(); @@ -459,8 +463,7 @@ class SegmentBuilder { /// Sort a nested sequence of regions from a single file. static void sortNestedRegions(MutableArrayRef<CountedRegion> Regions) { - llvm::sort(Regions.begin(), Regions.end(), [](const CountedRegion &LHS, - const CountedRegion &RHS) { + llvm::sort(Regions, [](const CountedRegion &LHS, const CountedRegion &RHS) { if (LHS.startLoc() != RHS.startLoc()) return LHS.startLoc() < RHS.startLoc(); if (LHS.endLoc() != RHS.endLoc()) @@ -557,7 +560,7 @@ std::vector<StringRef> CoverageMapping::getUniqueSourceFiles() const { for (const auto &Function : getCoveredFunctions()) Filenames.insert(Filenames.end(), Function.Filenames.begin(), Function.Filenames.end()); - llvm::sort(Filenames.begin(), Filenames.end()); + llvm::sort(Filenames); auto Last = std::unique(Filenames.begin(), Filenames.end()); Filenames.erase(Last, Filenames.end()); return Filenames; diff --git a/lib/ProfileData/GCOV.cpp b/lib/ProfileData/GCOV.cpp index c9155439ec46..b687346a2c05 100644 --- a/lib/ProfileData/GCOV.cpp +++ b/lib/ProfileData/GCOV.cpp @@ -111,9 +111,7 @@ void GCOVFile::print(raw_ostream &OS) const { #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) /// dump - Dump GCOVFile content to dbgs() for debugging purposes. -LLVM_DUMP_METHOD void GCOVFile::dump() const { - print(dbgs()); -} +LLVM_DUMP_METHOD void GCOVFile::dump() const { print(dbgs()); } #endif /// collectLineCounts - Collect line counts. This must be used after @@ -359,9 +357,7 @@ void GCOVFunction::print(raw_ostream &OS) const { #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) /// dump - Dump GCOVFunction content to dbgs() for debugging purposes. -LLVM_DUMP_METHOD void GCOVFunction::dump() const { - print(dbgs()); -} +LLVM_DUMP_METHOD void GCOVFunction::dump() const { print(dbgs()); } #endif /// collectLineCounts - Collect line counts. This must be used after @@ -437,12 +433,135 @@ void GCOVBlock::print(raw_ostream &OS) const { #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) /// dump - Dump GCOVBlock content to dbgs() for debugging purposes. -LLVM_DUMP_METHOD void GCOVBlock::dump() const { - print(dbgs()); -} +LLVM_DUMP_METHOD void GCOVBlock::dump() const { print(dbgs()); } #endif //===----------------------------------------------------------------------===// +// Cycles detection +// +// The algorithm in GCC is based on the algorihtm by Hawick & James: +// "Enumerating Circuits and Loops in Graphs with Self-Arcs and Multiple-Arcs" +// http://complexity.massey.ac.nz/cstn/013/cstn-013.pdf. + +/// Get the count for the detected cycle. +uint64_t GCOVBlock::getCycleCount(const Edges &Path) { + uint64_t CycleCount = std::numeric_limits<uint64_t>::max(); + for (auto E : Path) { + CycleCount = std::min(E->CyclesCount, CycleCount); + } + for (auto E : Path) { + E->CyclesCount -= CycleCount; + } + return CycleCount; +} + +/// Unblock a vertex previously marked as blocked. +void GCOVBlock::unblock(const GCOVBlock *U, BlockVector &Blocked, + BlockVectorLists &BlockLists) { + auto it = find(Blocked, U); + if (it == Blocked.end()) { + return; + } + + const size_t index = it - Blocked.begin(); + Blocked.erase(it); + + const BlockVector ToUnblock(BlockLists[index]); + BlockLists.erase(BlockLists.begin() + index); + for (auto GB : ToUnblock) { + GCOVBlock::unblock(GB, Blocked, BlockLists); + } +} + +bool GCOVBlock::lookForCircuit(const GCOVBlock *V, const GCOVBlock *Start, + Edges &Path, BlockVector &Blocked, + BlockVectorLists &BlockLists, + const BlockVector &Blocks, uint64_t &Count) { + Blocked.push_back(V); + BlockLists.emplace_back(BlockVector()); + bool FoundCircuit = false; + + for (auto E : V->dsts()) { + const GCOVBlock *W = &E->Dst; + if (W < Start || find(Blocks, W) == Blocks.end()) { + continue; + } + + Path.push_back(E); + + if (W == Start) { + // We've a cycle. + Count += GCOVBlock::getCycleCount(Path); + FoundCircuit = true; + } else if (find(Blocked, W) == Blocked.end() && // W is not blocked. + GCOVBlock::lookForCircuit(W, Start, Path, Blocked, BlockLists, + Blocks, Count)) { + FoundCircuit = true; + } + + Path.pop_back(); + } + + if (FoundCircuit) { + GCOVBlock::unblock(V, Blocked, BlockLists); + } else { + for (auto E : V->dsts()) { + const GCOVBlock *W = &E->Dst; + if (W < Start || find(Blocks, W) == Blocks.end()) { + continue; + } + const size_t index = find(Blocked, W) - Blocked.begin(); + BlockVector &List = BlockLists[index]; + if (find(List, V) == List.end()) { + List.push_back(V); + } + } + } + + return FoundCircuit; +} + +/// Get the count for the list of blocks which lie on the same line. +void GCOVBlock::getCyclesCount(const BlockVector &Blocks, uint64_t &Count) { + for (auto Block : Blocks) { + Edges Path; + BlockVector Blocked; + BlockVectorLists BlockLists; + + GCOVBlock::lookForCircuit(Block, Block, Path, Blocked, BlockLists, Blocks, + Count); + } +} + +/// Get the count for the list of blocks which lie on the same line. +uint64_t GCOVBlock::getLineCount(const BlockVector &Blocks) { + uint64_t Count = 0; + + for (auto Block : Blocks) { + if (Block->getNumSrcEdges() == 0) { + // The block has no predecessors and a non-null counter + // (can be the case with entry block in functions). + Count += Block->getCount(); + } else { + // Add counts from predecessors that are not on the same line. + for (auto E : Block->srcs()) { + const GCOVBlock *W = &E->Src; + if (find(Blocks, W) == Blocks.end()) { + Count += E->Count; + } + } + } + for (auto E : Block->dsts()) { + E->CyclesCount = E->Count; + } + } + + GCOVBlock::getCyclesCount(Blocks, Count); + + return Count; +} + +//===----------------------------------------------------------------------===// // FileInfo implementation. // Safe integer division, returns 0 if numerator is 0. @@ -578,8 +697,8 @@ FileInfo::openCoveragePath(StringRef CoveragePath) { return llvm::make_unique<raw_null_ostream>(); std::error_code EC; - auto OS = llvm::make_unique<raw_fd_ostream>(CoveragePath, EC, - sys::fs::F_Text); + auto OS = + llvm::make_unique<raw_fd_ostream>(CoveragePath, EC, sys::fs::F_Text); if (EC) { errs() << EC.message() << "\n"; return llvm::make_unique<raw_null_ostream>(); @@ -593,7 +712,7 @@ void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename, SmallVector<StringRef, 4> Filenames; for (const auto &LI : LineInfo) Filenames.push_back(LI.first()); - llvm::sort(Filenames.begin(), Filenames.end()); + llvm::sort(Filenames); for (StringRef Filename : Filenames) { auto AllLines = LineConsumer(Filename); @@ -628,17 +747,7 @@ void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename, // Add up the block counts to form line counts. DenseMap<const GCOVFunction *, bool> LineExecs; - uint64_t LineCount = 0; for (const GCOVBlock *Block : Blocks) { - if (Options.AllBlocks) { - // Only take the highest block count for that line. - uint64_t BlockCount = Block->getCount(); - LineCount = LineCount > BlockCount ? LineCount : BlockCount; - } else { - // Sum up all of the block counts. - LineCount += Block->getCount(); - } - if (Options.FuncCoverage) { // This is a slightly convoluted way to most accurately gather line // statistics for functions. Basically what is happening is that we @@ -674,6 +783,7 @@ void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename, } } + const uint64_t LineCount = GCOVBlock::getLineCount(Blocks); if (LineCount == 0) CovOS << " #####:"; else { diff --git a/lib/ProfileData/InstrProf.cpp b/lib/ProfileData/InstrProf.cpp index 544a77ec20a5..aaa8000ff2f9 100644 --- a/lib/ProfileData/InstrProf.cpp +++ b/lib/ProfileData/InstrProf.cpp @@ -252,11 +252,12 @@ static StringRef stripDirPrefix(StringRef PathNameStr, uint32_t NumPrefix) { // data, its original linkage must be non-internal. std::string getPGOFuncName(const Function &F, bool InLTO, uint64_t Version) { if (!InLTO) { - StringRef FileName = (StaticFuncFullModulePrefix - ? F.getParent()->getName() - : sys::path::filename(F.getParent()->getName())); - if (StaticFuncFullModulePrefix && StaticFuncStripDirNamePrefix != 0) - FileName = stripDirPrefix(FileName, StaticFuncStripDirNamePrefix); + StringRef FileName(F.getParent()->getSourceFileName()); + uint32_t StripLevel = StaticFuncFullModulePrefix ? 0 : (uint32_t)-1; + if (StripLevel < StaticFuncStripDirNamePrefix) + StripLevel = StaticFuncStripDirNamePrefix; + if (StripLevel) + FileName = stripDirPrefix(FileName, StripLevel); return getPGOFuncName(F.getName(), F.getLinkage(), FileName, Version); } diff --git a/lib/ProfileData/InstrProfReader.cpp b/lib/ProfileData/InstrProfReader.cpp index 3b704158a5c5..eaf0eb04bfbf 100644 --- a/lib/ProfileData/InstrProfReader.cpp +++ b/lib/ProfileData/InstrProfReader.cpp @@ -14,6 +14,7 @@ #include "llvm/ProfileData/InstrProfReader.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/IR/ProfileSummary.h" @@ -23,6 +24,7 @@ #include "llvm/Support/Error.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SymbolRemappingReader.h" #include "llvm/Support/SwapByteOrder.h" #include <algorithm> #include <cctype> @@ -88,16 +90,29 @@ InstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer) { } Expected<std::unique_ptr<IndexedInstrProfReader>> -IndexedInstrProfReader::create(const Twine &Path) { +IndexedInstrProfReader::create(const Twine &Path, const Twine &RemappingPath) { // Set up the buffer to read. auto BufferOrError = setupMemoryBuffer(Path); if (Error E = BufferOrError.takeError()) return std::move(E); - return IndexedInstrProfReader::create(std::move(BufferOrError.get())); + + // Set up the remapping buffer if requested. + std::unique_ptr<MemoryBuffer> RemappingBuffer; + std::string RemappingPathStr = RemappingPath.str(); + if (!RemappingPathStr.empty()) { + auto RemappingBufferOrError = setupMemoryBuffer(RemappingPathStr); + if (Error E = RemappingBufferOrError.takeError()) + return std::move(E); + RemappingBuffer = std::move(RemappingBufferOrError.get()); + } + + return IndexedInstrProfReader::create(std::move(BufferOrError.get()), + std::move(RemappingBuffer)); } Expected<std::unique_ptr<IndexedInstrProfReader>> -IndexedInstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer) { +IndexedInstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer, + std::unique_ptr<MemoryBuffer> RemappingBuffer) { // Sanity check the buffer. if (uint64_t(Buffer->getBufferSize()) > std::numeric_limits<unsigned>::max()) return make_error<InstrProfError>(instrprof_error::too_large); @@ -105,7 +120,8 @@ IndexedInstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer) { // Create the reader. if (!IndexedInstrProfReader::hasFormat(*Buffer)) return make_error<InstrProfError>(instrprof_error::bad_magic); - auto Result = llvm::make_unique<IndexedInstrProfReader>(std::move(Buffer)); + auto Result = llvm::make_unique<IndexedInstrProfReader>( + std::move(Buffer), std::move(RemappingBuffer)); // Initialize the reader and return the result. if (Error E = initializeReader(*Result)) @@ -587,6 +603,124 @@ InstrProfReaderIndex<HashTableImpl>::InstrProfReaderIndex( RecordIterator = HashTable->data_begin(); } +namespace { +/// A remapper that does not apply any remappings. +class InstrProfReaderNullRemapper : public InstrProfReaderRemapper { + InstrProfReaderIndexBase &Underlying; + +public: + InstrProfReaderNullRemapper(InstrProfReaderIndexBase &Underlying) + : Underlying(Underlying) {} + + Error getRecords(StringRef FuncName, + ArrayRef<NamedInstrProfRecord> &Data) override { + return Underlying.getRecords(FuncName, Data); + } +}; +} + +/// A remapper that applies remappings based on a symbol remapping file. +template <typename HashTableImpl> +class llvm::InstrProfReaderItaniumRemapper + : public InstrProfReaderRemapper { +public: + InstrProfReaderItaniumRemapper( + std::unique_ptr<MemoryBuffer> RemapBuffer, + InstrProfReaderIndex<HashTableImpl> &Underlying) + : RemapBuffer(std::move(RemapBuffer)), Underlying(Underlying) { + } + + /// Extract the original function name from a PGO function name. + static StringRef extractName(StringRef Name) { + // We can have multiple :-separated pieces; there can be pieces both + // before and after the mangled name. Find the first part that starts + // with '_Z'; we'll assume that's the mangled name we want. + std::pair<StringRef, StringRef> Parts = {StringRef(), Name}; + while (true) { + Parts = Parts.second.split(':'); + if (Parts.first.startswith("_Z")) + return Parts.first; + if (Parts.second.empty()) + return Name; + } + } + + /// Given a mangled name extracted from a PGO function name, and a new + /// form for that mangled name, reconstitute the name. + static void reconstituteName(StringRef OrigName, StringRef ExtractedName, + StringRef Replacement, + SmallVectorImpl<char> &Out) { + Out.reserve(OrigName.size() + Replacement.size() - ExtractedName.size()); + Out.insert(Out.end(), OrigName.begin(), ExtractedName.begin()); + Out.insert(Out.end(), Replacement.begin(), Replacement.end()); + Out.insert(Out.end(), ExtractedName.end(), OrigName.end()); + } + + Error populateRemappings() override { + if (Error E = Remappings.read(*RemapBuffer)) + return E; + for (StringRef Name : Underlying.HashTable->keys()) { + StringRef RealName = extractName(Name); + if (auto Key = Remappings.insert(RealName)) { + // FIXME: We could theoretically map the same equivalence class to + // multiple names in the profile data. If that happens, we should + // return NamedInstrProfRecords from all of them. + MappedNames.insert({Key, RealName}); + } + } + return Error::success(); + } + + Error getRecords(StringRef FuncName, + ArrayRef<NamedInstrProfRecord> &Data) override { + StringRef RealName = extractName(FuncName); + if (auto Key = Remappings.lookup(RealName)) { + StringRef Remapped = MappedNames.lookup(Key); + if (!Remapped.empty()) { + if (RealName.begin() == FuncName.begin() && + RealName.end() == FuncName.end()) + FuncName = Remapped; + else { + // Try rebuilding the name from the given remapping. + SmallString<256> Reconstituted; + reconstituteName(FuncName, RealName, Remapped, Reconstituted); + Error E = Underlying.getRecords(Reconstituted, Data); + if (!E) + return E; + + // If we failed because the name doesn't exist, fall back to asking + // about the original name. + if (Error Unhandled = handleErrors( + std::move(E), [](std::unique_ptr<InstrProfError> Err) { + return Err->get() == instrprof_error::unknown_function + ? Error::success() + : Error(std::move(Err)); + })) + return Unhandled; + } + } + } + return Underlying.getRecords(FuncName, Data); + } + +private: + /// The memory buffer containing the remapping configuration. Remappings + /// holds pointers into this buffer. + std::unique_ptr<MemoryBuffer> RemapBuffer; + + /// The mangling remapper. + SymbolRemappingReader Remappings; + + /// Mapping from mangled name keys to the name used for the key in the + /// profile data. + /// FIXME: Can we store a location within the on-disk hash table instead of + /// redoing lookup? + DenseMap<SymbolRemappingReader::Key, StringRef> MappedNames; + + /// The real profile data reader. + InstrProfReaderIndex<HashTableImpl> &Underlying; +}; + bool IndexedInstrProfReader::hasFormat(const MemoryBuffer &DataBuffer) { using namespace support; @@ -683,10 +817,22 @@ Error IndexedInstrProfReader::readHeader() { uint64_t HashOffset = endian::byte_swap<uint64_t, little>(Header->HashOffset); // The rest of the file is an on disk hash table. - InstrProfReaderIndexBase *IndexPtr = nullptr; - IndexPtr = new InstrProfReaderIndex<OnDiskHashTableImplV3>( - Start + HashOffset, Cur, Start, HashType, FormatVersion); - Index.reset(IndexPtr); + auto IndexPtr = + llvm::make_unique<InstrProfReaderIndex<OnDiskHashTableImplV3>>( + Start + HashOffset, Cur, Start, HashType, FormatVersion); + + // Load the remapping table now if requested. + if (RemappingBuffer) { + Remapper = llvm::make_unique< + InstrProfReaderItaniumRemapper<OnDiskHashTableImplV3>>( + std::move(RemappingBuffer), *IndexPtr); + if (Error E = Remapper->populateRemappings()) + return E; + } else { + Remapper = llvm::make_unique<InstrProfReaderNullRemapper>(*IndexPtr); + } + Index = std::move(IndexPtr); + return success(); } @@ -707,7 +853,7 @@ Expected<InstrProfRecord> IndexedInstrProfReader::getInstrProfRecord(StringRef FuncName, uint64_t FuncHash) { ArrayRef<NamedInstrProfRecord> Data; - Error Err = Index->getRecords(FuncName, Data); + Error Err = Remapper->getRecords(FuncName, Data); if (Err) return std::move(Err); // Found it. Look for counters with the right hash. diff --git a/lib/ProfileData/ProfileSummaryBuilder.cpp b/lib/ProfileData/ProfileSummaryBuilder.cpp index 62f00d693c68..3a8462fd9b0d 100644 --- a/lib/ProfileData/ProfileSummaryBuilder.cpp +++ b/lib/ProfileData/ProfileSummaryBuilder.cpp @@ -58,7 +58,7 @@ void SampleProfileSummaryBuilder::addRecord( void ProfileSummaryBuilder::computeDetailedSummary() { if (DetailedSummaryCutoffs.empty()) return; - llvm::sort(DetailedSummaryCutoffs.begin(), DetailedSummaryCutoffs.end()); + llvm::sort(DetailedSummaryCutoffs); auto Iter = CountFrequencies.begin(); const auto End = CountFrequencies.end(); diff --git a/lib/ProfileData/SampleProf.cpp b/lib/ProfileData/SampleProf.cpp index 30438ba7962a..1a124415f179 100644 --- a/lib/ProfileData/SampleProf.cpp +++ b/lib/ProfileData/SampleProf.cpp @@ -26,6 +26,14 @@ using namespace llvm; using namespace sampleprof; +namespace llvm { +namespace sampleprof { +SampleProfileFormat FunctionSamples::Format; +DenseMap<uint64_t, StringRef> FunctionSamples::GUIDToFuncNameMap; +Module *FunctionSamples::CurrentModule; +} // namespace sampleprof +} // namespace llvm + namespace { // FIXME: This class is only here to support the transition to llvm::Error. It @@ -59,6 +67,8 @@ class SampleProfErrorCategoryType : public std::error_category { return "Unimplemented feature"; case sampleprof_error::counter_overflow: return "Counter overflow"; + case sampleprof_error::ostream_seek_unsupported: + return "Ostream does not support seek"; } llvm_unreachable("A value of sampleprof_error has no message."); } diff --git a/lib/ProfileData/SampleProfReader.cpp b/lib/ProfileData/SampleProfReader.cpp index 79335e67cd98..a68d1e9d3ab0 100644 --- a/lib/ProfileData/SampleProfReader.cpp +++ b/lib/ProfileData/SampleProfReader.cpp @@ -30,6 +30,7 @@ #include "llvm/Support/ErrorOr.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/LineIterator.h" +#include "llvm/Support/MD5.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -320,6 +321,21 @@ ErrorOr<StringRef> SampleProfileReaderBinary::readString() { } template <typename T> +ErrorOr<T> SampleProfileReaderBinary::readUnencodedNumber() { + std::error_code EC; + + if (Data + sizeof(T) > End) { + EC = sampleprof_error::truncated; + reportError(0, EC.message()); + return EC; + } + + using namespace support; + T Val = endian::readNext<T, little, unaligned>(Data); + return Val; +} + +template <typename T> inline ErrorOr<uint32_t> SampleProfileReaderBinary::readStringIndex(T &Table) { std::error_code EC; auto Idx = readNumber<uint32_t>(); @@ -423,29 +439,51 @@ SampleProfileReaderBinary::readProfile(FunctionSamples &FProfile) { return sampleprof_error::success; } -std::error_code SampleProfileReaderBinary::read() { - while (!at_eof()) { - auto NumHeadSamples = readNumber<uint64_t>(); - if (std::error_code EC = NumHeadSamples.getError()) - return EC; +std::error_code SampleProfileReaderBinary::readFuncProfile() { + auto NumHeadSamples = readNumber<uint64_t>(); + if (std::error_code EC = NumHeadSamples.getError()) + return EC; - auto FName(readStringFromTable()); - if (std::error_code EC = FName.getError()) - return EC; + auto FName(readStringFromTable()); + if (std::error_code EC = FName.getError()) + return EC; - Profiles[*FName] = FunctionSamples(); - FunctionSamples &FProfile = Profiles[*FName]; - FProfile.setName(*FName); + Profiles[*FName] = FunctionSamples(); + FunctionSamples &FProfile = Profiles[*FName]; + FProfile.setName(*FName); - FProfile.addHeadSamples(*NumHeadSamples); + FProfile.addHeadSamples(*NumHeadSamples); + + if (std::error_code EC = readProfile(FProfile)) + return EC; + return sampleprof_error::success; +} - if (std::error_code EC = readProfile(FProfile)) +std::error_code SampleProfileReaderBinary::read() { + while (!at_eof()) { + if (std::error_code EC = readFuncProfile()) return EC; } return sampleprof_error::success; } +std::error_code SampleProfileReaderCompactBinary::read() { + 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 *SavedData = Data; + Data = reinterpret_cast<const uint8_t *>(Buffer->getBufferStart()) + + iter->second; + if (std::error_code EC = readFuncProfile()) + return EC; + Data = SavedData; + } + return sampleprof_error::success; +} + std::error_code SampleProfileReaderRawBinary::verifySPMagic(uint64_t Magic) { if (Magic == SPMagic()) return sampleprof_error::success; @@ -514,6 +552,53 @@ std::error_code SampleProfileReaderBinary::readHeader() { return sampleprof_error::success; } +std::error_code SampleProfileReaderCompactBinary::readHeader() { + SampleProfileReaderBinary::readHeader(); + if (std::error_code EC = readFuncOffsetTable()) + return EC; + return sampleprof_error::success; +} + +std::error_code SampleProfileReaderCompactBinary::readFuncOffsetTable() { + auto TableOffset = readUnencodedNumber<uint64_t>(); + if (std::error_code EC = TableOffset.getError()) + return EC; + + const uint8_t *SavedData = Data; + const uint8_t *TableStart = + reinterpret_cast<const uint8_t *>(Buffer->getBufferStart()) + + *TableOffset; + Data = TableStart; + + auto Size = readNumber<uint64_t>(); + if (std::error_code EC = Size.getError()) + return EC; + + FuncOffsetTable.reserve(*Size); + for (uint32_t I = 0; I < *Size; ++I) { + auto FName(readStringFromTable()); + if (std::error_code EC = FName.getError()) + return EC; + + auto Offset = readNumber<uint64_t>(); + if (std::error_code EC = Offset.getError()) + return EC; + + FuncOffsetTable[*FName] = *Offset; + } + End = TableStart; + Data = SavedData; + return sampleprof_error::success; +} + +void SampleProfileReaderCompactBinary::collectFuncsToUse(const Module &M) { + FuncsToUse.clear(); + for (auto &F : M) { + StringRef Fname = F.getName().split('.').first; + FuncsToUse.insert(Fname); + } +} + std::error_code SampleProfileReaderBinary::readSummaryEntry( std::vector<ProfileSummaryEntry> &Entries) { auto Cutoff = readNumber<uint64_t>(); @@ -827,6 +912,40 @@ bool SampleProfileReaderGCC::hasFormat(const MemoryBuffer &Buffer) { return Magic == "adcg*704"; } +std::error_code SampleProfileReaderItaniumRemapper::read() { + // If the underlying data is in compact format, we can't remap it because + // we don't know what the original function names were. + if (getFormat() == SPF_Compact_Binary) { + Ctx.diagnose(DiagnosticInfoSampleProfile( + Buffer->getBufferIdentifier(), + "Profile data remapping cannot be applied to profile data " + "in compact format (original mangled names are not available).", + DS_Warning)); + return sampleprof_error::success; + } + + if (Error E = Remappings.read(*Buffer)) { + handleAllErrors( + std::move(E), [&](const SymbolRemappingParseError &ParseError) { + reportError(ParseError.getLineNum(), ParseError.getMessage()); + }); + return sampleprof_error::malformed; + } + + for (auto &Sample : getProfiles()) + if (auto Key = Remappings.insert(Sample.first())) + SampleMap.insert({Key, &Sample.second}); + + return sampleprof_error::success; +} + +FunctionSamples * +SampleProfileReaderItaniumRemapper::getSamplesFor(StringRef Fname) { + if (auto Key = Remappings.lookup(Fname)) + return SampleMap.lookup(Key); + return SampleProfileReader::getSamplesFor(Fname); +} + /// Prepare a memory buffer for the contents of \p Filename. /// /// \returns an error code indicating the status of the buffer. @@ -859,6 +978,27 @@ SampleProfileReader::create(const Twine &Filename, LLVMContext &C) { return create(BufferOrError.get(), C); } +/// Create a sample profile remapper from the given input, to remap the +/// function names in the given profile data. +/// +/// \param Filename The file to open. +/// +/// \param C The LLVM context to use to emit diagnostics. +/// +/// \param Underlying The underlying profile data reader to remap. +/// +/// \returns an error code indicating the status of the created reader. +ErrorOr<std::unique_ptr<SampleProfileReader>> +SampleProfileReaderItaniumRemapper::create( + const Twine &Filename, LLVMContext &C, + std::unique_ptr<SampleProfileReader> Underlying) { + auto BufferOrError = setupMemoryBuffer(Filename); + if (std::error_code EC = BufferOrError.getError()) + return EC; + return llvm::make_unique<SampleProfileReaderItaniumRemapper>( + std::move(BufferOrError.get()), C, std::move(Underlying)); +} + /// Create a sample profile reader based on the format of the input data. /// /// \param B The memory buffer to create the reader from (assumes ownership). @@ -880,6 +1020,7 @@ SampleProfileReader::create(std::unique_ptr<MemoryBuffer> &B, LLVMContext &C) { else return sampleprof_error::unrecognized_format; + FunctionSamples::Format = Reader->getFormat(); if (std::error_code EC = Reader->readHeader()) return EC; diff --git a/lib/ProfileData/SampleProfWriter.cpp b/lib/ProfileData/SampleProfWriter.cpp index b4de30118b8b..b1c669ec31c4 100644 --- a/lib/ProfileData/SampleProfWriter.cpp +++ b/lib/ProfileData/SampleProfWriter.cpp @@ -22,6 +22,8 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ProfileData/ProfileCommon.h" #include "llvm/ProfileData/SampleProf.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/EndianStream.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/LEB128.h" @@ -64,6 +66,15 @@ SampleProfileWriter::write(const StringMap<FunctionSamples> &ProfileMap) { return sampleprof_error::success; } +std::error_code SampleProfileWriterCompactBinary::write( + const StringMap<FunctionSamples> &ProfileMap) { + if (std::error_code EC = SampleProfileWriter::write(ProfileMap)) + return EC; + if (std::error_code EC = writeFuncOffsetTable()) + return EC; + return sampleprof_error::success; +} + /// Write samples to a text file. /// /// Note: it may be tempting to implement this in terms of @@ -168,6 +179,30 @@ std::error_code SampleProfileWriterRawBinary::writeNameTable() { return sampleprof_error::success; } +std::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() { + auto &OS = *OutputStream; + + // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable. + auto &OFS = static_cast<raw_fd_ostream &>(OS); + uint64_t FuncOffsetTableStart = OS.tell(); + if (OFS.seek(TableOffset) == (uint64_t)-1) + return sampleprof_error::ostream_seek_unsupported; + support::endian::Writer Writer(*OutputStream, support::little); + Writer.write(FuncOffsetTableStart); + if (OFS.seek(FuncOffsetTableStart) == (uint64_t)-1) + return sampleprof_error::ostream_seek_unsupported; + + // Write out the table size. + encodeULEB128(FuncOffsetTable.size(), OS); + + // Write out FuncOffsetTable. + for (auto entry : FuncOffsetTable) { + writeNameIdx(entry.first); + encodeULEB128(entry.second, OS); + } + return sampleprof_error::success; +} + std::error_code SampleProfileWriterCompactBinary::writeNameTable() { auto &OS = *OutputStream; std::set<StringRef> V; @@ -215,6 +250,19 @@ std::error_code SampleProfileWriterBinary::writeHeader( return sampleprof_error::success; } +std::error_code SampleProfileWriterCompactBinary::writeHeader( + const StringMap<FunctionSamples> &ProfileMap) { + support::endian::Writer Writer(*OutputStream, support::little); + if (auto EC = SampleProfileWriterBinary::writeHeader(ProfileMap)) + return EC; + + // Reserve a slot for the offset of function offset table. The slot will + // be populated with the offset of FuncOffsetTable later. + TableOffset = OutputStream->tell(); + Writer.write(static_cast<uint64_t>(-2)); + return sampleprof_error::success; +} + std::error_code SampleProfileWriterBinary::writeSummary() { auto &OS = *OutputStream; encodeULEB128(Summary->getTotalCount(), OS); @@ -283,6 +331,15 @@ std::error_code SampleProfileWriterBinary::write(const FunctionSamples &S) { return writeBody(S); } +std::error_code +SampleProfileWriterCompactBinary::write(const FunctionSamples &S) { + uint64_t Offset = OutputStream->tell(); + StringRef Name = S.getName(); + FuncOffsetTable[Name] = Offset; + encodeULEB128(S.getHeadSamples(), *OutputStream); + return writeBody(S); +} + /// Create a sample profile file writer based on the specified format. /// /// \param Filename The file to create. |
